"Fossies" - the Fresh Open Source Software Archive

Member "scponly-20110526/helper.c" (8 Sep 2010, 14005 Bytes) of package /linux/privat/old/scponly-20110526.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "helper.c": 4.8_vs_20110526.

    1 /*
    2  *  helper functions for scponly
    3  */
    4 #include <stdio.h>  /* io */
    5 #include <string.h> /* for str* */
    6 #include <sys/types.h>  /* for stat, getpwuid */
    7 #include <sys/stat.h>   /* for stat */
    8 #include <unistd.h> /* for exit, access, getpwuid, execve, getopt */
    9 #include <errno.h>  /* for debugging */
   10 #include <pwd.h>    /* to get username for config parsing */
   11 #include <time.h>   /* time */
   12 #include <libgen.h> /* basename */
   13 #include <stdlib.h> /* realloc */
   14 #include <syslog.h>
   15 
   16 #include "config.h"
   17 #include "scponly.h" /* includes getopt */
   18 
   19 #ifdef HAVE_GLOB
   20 #include <glob.h>   /* for glob() */
   21 #else
   22 #ifdef HAVE_WORDEXP
   23 #include <wordexp.h>    /* for wordexp() */
   24 #endif
   25 #endif
   26 
   27 #ifdef RSYNC_COMPAT
   28 #define RSYNC_ARG_SERVER 0x01
   29 #define RSYNC_ARG_EXECUTE 0x02
   30 #endif
   31 
   32 #define MAX(x,y)    ( ( x > y ) ? x : y )
   33 #define MIN(x,y)    ( ( x < y ) ? x : y )
   34 
   35 extern int debuglevel;
   36 extern char username[MAX_USERNAME];
   37 extern char homedir[FILENAME_MAX];
   38 extern cmd_t commands[];
   39 extern cmd_arg_t dangerous_args[];
   40 extern char * allowed_env_vars[];
   41 extern char * safeenv[MAX_ENV];
   42 extern void (*debug)(int priority, const char* format, ...);
   43 extern int (*scponly_getopt_long)( int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex);
   44 
   45 #ifdef HAVE_GETOPT
   46 extern char *optarg;
   47 extern int optind;
   48 #ifdef HAVE_OPTRESET
   49 extern int optreset;
   50 #endif
   51 #endif
   52 
   53 void noop_syslog(int priority, const char* format, ...)
   54 {
   55     /* purposefully does nothing, used when debuglevel <= 0 */
   56 }
   57 
   58 #ifdef UNIX_COMPAT
   59 char* solaris_needs_strsep(char** str, char* delims)
   60 {
   61     char* tmpstr;
   62 
   63     if (*str==NULL) {
   64     return NULL;
   65     }
   66 
   67     tmpstr=*str;
   68     while (**str!='\0') {
   69     if (strchr(delims,**str)!=NULL) {
   70         **str='\0';
   71         (*str)++;
   72         return tmpstr;
   73     }
   74     (*str)++;
   75     }
   76     *str=NULL;
   77     return tmpstr;
   78 }
   79 #endif
   80 
   81 /*
   82  * perform a deep copy of an argument vector, ignoring all vector elements which begin with "--"
   83  */
   84 char **strip_vector(char **sav)
   85 {
   86     char    **tmpptr=sav;  
   87     char    **dav=(char **)malloc(MAX_ARGC * (sizeof(char *)));
   88     int     dac=0;
   89 
   90     bzero(dav,sizeof(dav));
   91     while (*tmpptr!=NULL)
   92     {
   93         if(NULL == strbeg(*tmpptr, "--"))
   94             dav[dac++]=strdup(*tmpptr);
   95         tmpptr++;
   96     }
   97     return dav;
   98 }
   99 
  100 void discard_vector(char **av)
  101 {
  102     discard_child_vectors(av);
  103     free(av);
  104 }
  105 
  106 void discard_child_vectors(char **av)
  107 {
  108     char **tmpptr=av;   
  109     while (*tmpptr!=NULL)
  110         free(*tmpptr++);
  111 }
  112 
  113 char *flatten_vector(char **av)
  114 {
  115     char **tmpptr=av;   
  116     char *temp=NULL;
  117     char *crptr=NULL;
  118     char *outbuf=NULL;
  119     int len=0,newlen=0;
  120 
  121     while (*tmpptr!=NULL)
  122     {
  123         if (NULL != (crptr=strchr(*tmpptr, '\n')))
  124         {
  125             *crptr='\0';
  126         }
  127         if (outbuf!=NULL)
  128         {
  129             len = strlen(outbuf);
  130             newlen=len + strlen(*tmpptr)+1;
  131         }
  132         else
  133         {
  134             len = 0;
  135             newlen=strlen(*tmpptr);
  136         }
  137         if (NULL == (temp = realloc(outbuf, newlen + 1)))
  138         {
  139             perror("realloc");
  140             if (outbuf)
  141                 free(outbuf);
  142             exit(EXIT_FAILURE);
  143         }
  144         outbuf=temp;
  145         temp=NULL;
  146         if (len)
  147         {
  148             outbuf[len]=' ';
  149             strcpy(&outbuf[len+1],*tmpptr); 
  150         }
  151         else
  152             strcpy(outbuf,*tmpptr); 
  153         tmpptr++;
  154     }
  155     return (outbuf);
  156 }
  157 
  158 /*
  159  *  since some programs support invoking other programs for their encryption
  160  *  (dropins to replace ssh), we must refuse to support these arguments
  161  *
  162  *  RETURN: 1 means reject this command, 0 means it is safe.
  163  */
  164 int check_dangerous_args(char **av)
  165 {
  166     cmd_arg_t   *cmdarg=dangerous_args;
  167     char        **tmpptr=av;
  168     int         ch;
  169     int         ac=0;
  170     int         longopt_index = 0;
  171 #ifdef RSYNC_COMPAT
  172     /* 
  173      * bitwise flag: 0x01 = server, 0x02 = -e.
  174      * Thus 0x03 is allowed and 0x01 is allowed, but 0x02 is not allowed
  175      */
  176     int         rsync_flags = 0; 
  177 #endif /* RSYNC_COMPAT */
  178 
  179     while (cmdarg != NULL)
  180     {
  181         if (cmdarg->name == NULL)
  182             return 0;
  183         if (exact_match(cmdarg->name,av[0]))
  184         {
  185             /*
  186              *  the command matches one of our dangerous commands
  187              *  check the rest of the vector for dangerous command
  188              *  line arguments
  189              *
  190              *  if the "getoptflag" is set for this command, use getopt
  191              *  to determine if the flag is present, otherwise
  192              *  to a string match on each element of the argument vector
  193              */
  194             if (1 == cmdarg->getoptflag)
  195             {
  196                 debug(LOG_DEBUG, "Using getopt processing for cmd %s\n (%s)", cmdarg->name, logstamp());
  197                 /*  
  198                  *  first count the arguments in the vector
  199                  */
  200                 tmpptr=av;
  201                 while (*tmpptr!=NULL)
  202                 {   
  203                     tmpptr++;
  204                     ac++;
  205                 }
  206                 /* 
  207                  *  now use getopt to look for our problem option
  208                  */
  209 #ifdef HAVE_GETOPT
  210 #ifdef HAVE_OPTRESET
  211                 /*
  212                  * if we have optreset, use it to reset the global variables
  213                  */
  214                 optreset=1;
  215                 optind=1;
  216 #else
  217                 /*
  218                  *  otherwise, try a glibc-style reset of the global getopt vars
  219                  */
  220                 optind=0;
  221 #endif /* HAVE_OPTRESET */
  222                 /*
  223                  *  tell getopt to only be strict if the 'opts' is well defined
  224                  */
  225                 opterr=cmdarg->strict;
  226                 while ((ch = scponly_getopt_long(ac, av, cmdarg->opts, cmdarg->longopts, &longopt_index)) != -1) {
  227                     
  228                     debug(LOG_DEBUG, "getopt processing returned '%c' (%s)", ch, logstamp());
  229                     
  230 #ifdef RSYNC_COMPAT
  231                     if (exact_match(cmdarg->name, PROG_RSYNC) && (ch == 's' || ch == 'e')) {
  232                         if (ch == 's')
  233                             rsync_flags |= RSYNC_ARG_SERVER;
  234                         else
  235                             /* -e */
  236                             rsync_flags |= RSYNC_ARG_EXECUTE;
  237                         debug(LOG_DEBUG, "rsync_flags are now set to: %0x", rsync_flags);
  238                     }
  239                     else
  240 #endif /* RSYNC_COMPAT */
  241 
  242                     /* if the character is found in badarg, then it's not a permitted option */
  243                     if (cmdarg->badarg != NULL && (strchr(cmdarg->badarg, ch) != NULL))
  244                     {
  245                         syslog(LOG_ERR, "option '%c' or a related long option is not permitted for use with %s (arg was %s) (%s))", 
  246                             ch, cmdarg->name, (optarg!=NULL ? optarg : "<NULL>"), logstamp());
  247                         return 1;
  248                     }
  249                     else if (cmdarg->strict && ch == '?')
  250                     {
  251                         syslog(LOG_ERR, "an unrecognized option was encountered while processing cmd %s (arg was %s) (%s))", 
  252                             cmdarg->name, (optarg!=NULL ? optarg : "<NULL>"), logstamp());
  253                         return 1;
  254                     }
  255                 }
  256 #ifdef RSYNC_COMPAT
  257                 /* it's not safe if the execute flag was set and server was not set */
  258                 if ((rsync_flags & RSYNC_ARG_EXECUTE) != 0 && (rsync_flags & RSYNC_ARG_SERVER) == 0) {
  259                         syslog(LOG_ERR, "option 'e' is not allowed unless '--server' is also set with cmd %s (%s)", 
  260                             PROG_RSYNC, logstamp());
  261                         return 1;
  262                 }
  263 #endif /* RSYNC_COMPAT */
  264 
  265 #else /* HAVE_GETOPT */
  266                 /*
  267                  * make sure that processing doesn't continue if we can't validate a rsync check
  268                  * and if the getopt flag is set.
  269                  */
  270                 syslog(LOG_ERR, "a getopt() argument check could not be performed for %s, recompile scponly without support for %s or rebuild scponly with getopt", av[0], av[0]);
  271                 return 1;
  272 #endif /* HAVE_GETOPT */
  273             }
  274             else
  275             /*
  276              * command does not require getopt processing
  277              */
  278             {
  279                 debug(LOG_DEBUG, "Not using getopt processing on cmd %s (%s)", cmdarg->name, logstamp());
  280 
  281                 tmpptr=av;
  282                 tmpptr++;
  283                 while (*tmpptr!=NULL)
  284                 {
  285                     debug(LOG_DEBUG, "Verifying that %s is an allowed option (%s)", *tmpptr, logstamp());
  286                         
  287                     if(strbeg(*tmpptr, cmdarg->badarg))
  288                     {
  289                         syslog(LOG_ERR, "%s is not permitted for use with %s (%s))", 
  290                             cmdarg->badarg, cmdarg->name, logstamp());
  291                         return 1;
  292                     }
  293                     tmpptr++;
  294                 }
  295             }
  296         }
  297         cmdarg++;
  298     }
  299     return 0;
  300 }
  301 
  302 int valid_arg_vector(char **av)
  303 {
  304     cmd_t   *cmd=commands;
  305 
  306     while (cmd != NULL)
  307     {
  308         if (cmd->name == NULL)
  309             return 0;
  310         if (exact_match(cmd->name,av[0]))
  311         {
  312             if ((cmd->argflag == 0) && (av[1]!=NULL))
  313             {
  314                 return 0;
  315             }
  316             return 1;
  317         }
  318         cmd++;
  319     }
  320     return 0;
  321 }
  322 
  323 char *substitute_known_path(char *request)
  324 {
  325     cmd_t   *cmd=commands;
  326     char *stripped_req=strdup(basename(request));
  327     while (cmd != NULL)
  328     {
  329         if (cmd->name == NULL)
  330             break;
  331         if (exact_match(basename(cmd->name),stripped_req))
  332         {
  333             free(stripped_req); /* discard old pathname */
  334             return (strdup(cmd->name));
  335         }
  336         cmd++;
  337     }
  338     return (stripped_req);
  339 }
  340 
  341 char **build_arg_vector(char *request)
  342 {
  343     /*
  344      * i strdup vector elements so i know they are
  345      * mine to free later.
  346      */
  347     char **ap, *argv[MAX_ARGC], *inputstring, *tmpstring, *freeme;
  348     char **ap2,**av=(char **)malloc(MAX_ARGC * (sizeof(char *)));
  349 
  350     ap=argv;
  351     freeme=inputstring=strdup(request); /* make a local copy  */
  352 
  353     while (ap < &argv[(MAX_ARGC-1)]) 
  354     {
  355         if (inputstring && (*inputstring=='"'))
  356         {
  357             if (NULL != (tmpstring=strchr((inputstring+1),'"')))
  358             {
  359                 *tmpstring++='\0';
  360                 *ap=(inputstring+1);
  361                 
  362 #ifdef UNIX_COMPAT
  363                 if (solaris_needs_strsep(&tmpstring, WHITE) == NULL)
  364 #else
  365                 if (strsep(&tmpstring, WHITE) == NULL)
  366 #endif
  367                     break;
  368                 inputstring=tmpstring;
  369         
  370                     if (**ap != '\0')
  371                     ap++;
  372                 continue;
  373             }
  374         }
  375         
  376 #ifdef UNIX_COMPAT
  377         if ((*ap = solaris_needs_strsep(&inputstring, WHITE)) == NULL)
  378 #else
  379         if ((*ap = strsep(&inputstring, WHITE)) == NULL)
  380 #endif
  381             break;
  382         
  383         if (**ap != '\0')
  384             ap++;
  385     }
  386     *ap = NULL;
  387     ap=argv;
  388     ap2=av;
  389     while (*ap != NULL)
  390     {
  391         *ap2=strdup(*ap);
  392         ap2++;
  393         ap++;
  394     }
  395     *ap2 = NULL;
  396     free(freeme);
  397     
  398     return (av);    
  399 }
  400 
  401 char **expand_wildcards(char **av_old)
  402 #ifdef HAVE_GLOB
  403 {
  404     char        **av_new=(char **)malloc(MAX_ARGC * (sizeof(char *)));
  405     glob_t g;
  406     int c_old,c_new,c;  /* argument counters */
  407 #ifdef UNIX_COMPAT
  408     int flags = GLOB_NOCHECK;
  409 #else
  410     int flags = GLOB_NOCHECK | GLOB_TILDE;
  411 #endif
  412 
  413     g.gl_offs = c_new = c_old = 0;
  414 
  415     while(av_old[c_old] != NULL )
  416     {
  417         if (0 == glob(av_old[c_old++],flags,NULL,&g))
  418         {
  419             c=0;
  420             while((g.gl_pathv[c] != NULL) && (c_new < (MAX_ARGC-1)))
  421                 av_new[c_new++]=strdup(g.gl_pathv[c++]);
  422             globfree(&g);
  423         }
  424     }
  425     av_new[c_new]=NULL;
  426     discard_vector(av_old);
  427     return av_new;
  428 }
  429 
  430 #else 
  431 #ifdef HAVE_WORDEXP
  432 {
  433     return NULL;
  434 }
  435 #endif
  436 #endif
  437 
  438 int cntchr(char *buf, char x)
  439 {
  440     int count=0;
  441     while (*buf!=0)
  442         if (*buf++==x)
  443             count++;
  444     return count;
  445 }
  446 
  447 char *logstamp ()
  448 {
  449     /* Time and pid are handled for us by syslog(3). */
  450     static char ret_buf[255];
  451     static const char bad_ip[10] = "no ip?!";
  452     char *ipstring = NULL;
  453     
  454     ipstring = (char *)getenv("SSH_CLIENT");
  455     if (!ipstring)
  456         ipstring = (char *)getenv("SSH2_CLIENT");
  457     if (!ipstring)
  458         ipstring = (char *)bad_ip;
  459     snprintf(ret_buf, sizeof(ret_buf)-1,
  460          "username: %s(%d), IP/port: %s", username, getuid(), ipstring);
  461     return ret_buf;
  462 }
  463 
  464 /*
  465  *  if big ends with small, return big without
  466  *  small in a new buf, else NULL
  467  */
  468 char *strend (char *big, char *small)
  469 {
  470     int blen,slen;
  471     slen=strlen(small);
  472     blen=strlen(big);
  473     if ((blen==0) || (slen==0) || (blen < slen))
  474     {
  475         return NULL;
  476     }
  477     if (0 == strcmp(&big[(blen-slen)],small))
  478     {
  479         char *tempbuf=NULL;
  480         tempbuf=(char *)malloc(blen-slen+1);
  481         if (tempbuf==NULL)
  482         {
  483             perror("malloc");
  484             exit(EXIT_FAILURE);
  485         }
  486         bzero(tempbuf,(blen-slen+1));
  487         strncpy(tempbuf, big, blen-slen);
  488         return tempbuf;
  489     }
  490     return NULL;
  491 }
  492 
  493 /*
  494  *  if big starts with small, return the char after 
  495  *  the last char in small from big. ahem.
  496  */
  497 char *strbeg(char *big, char *small)
  498 {
  499     if (strlen(big) <= strlen(small))
  500         return NULL;
  501     if (0==strncmp(big,small,strlen(small)))
  502         return (big+strlen(small));
  503     return NULL;
  504 }
  505 
  506 /*
  507  *  if any chars in string dont appear in ALLOWABLE
  508  *  then fail (return 0)
  509  */
  510 int valid_chars(char *string)
  511 {
  512     int count;
  513     if ((count=strspn(string,ALLOWABLE))==strlen(string))
  514         return 1;
  515     else
  516     {
  517         fprintf (stderr, "invalid characters in scp command!\n");
  518         fprintf (stderr, "here:%s\n",string+count);
  519         fprintf (stderr, "try using a wildcard to match this file/directory\n");
  520         return 0;
  521     }
  522 }
  523 
  524 /*
  525  * retrieves username and home directory from passwd database
  526  */
  527 int get_uservar(void)
  528 {
  529     struct passwd *userinfo;
  530 
  531     char *user = (char *)getenv("USER");
  532     if (user) {
  533         if (NULL==(userinfo=getpwnam(user)))
  534         {
  535             syslog(LOG_WARNING, "no knowledge of username %s [%s]", user, logstamp());
  536             return 0;
  537         }
  538         if (userinfo->pw_uid != getuid())
  539         {
  540             syslog(LOG_WARNING, "%s's uid doesn't match getuid(): %d [%s]", 
  541                                               user, getuid(), logstamp());
  542             return 0;
  543         }
  544         debug(LOG_DEBUG, "determined USER is \"%s\" from environment", user);
  545     } else {
  546         if (NULL==(userinfo=getpwuid(getuid())))
  547         {
  548             syslog (LOG_WARNING, "no knowledge of uid %d [%s]", getuid(), logstamp());
  549             return 0;
  550         }
  551     }
  552     debug(
  553         LOG_DEBUG,
  554         "retrieved home directory of \"%s\" for user \"%s\"",
  555         userinfo->pw_dir, userinfo->pw_name
  556         );
  557     strncpy(username,userinfo->pw_name,MAX_USERNAME);
  558     strncpy(homedir,userinfo->pw_dir,FILENAME_MAX);
  559     return 1;
  560 }
  561 
  562 /*
  563  * look through safeenv for the "name" environment variable and replace
  564  * it's value with "value".
  565  */
  566 int replace_env_entry(const char* name, const char* value) {
  567     
  568     char** base = safeenv;
  569     char buf[257]; /* make sure I don't overflow */
  570     snprintf(buf, 255, "%s=", name);
  571     while (*base != NULL) {
  572         debug(LOG_DEBUG, "Looking for '%s' in '%s'", buf, *base);
  573         if (strbeg(*base, buf) != NULL) {
  574             strcpy(*base + strlen(buf), value);
  575             debug(LOG_DEBUG, "'%s' env entry now reads '%s'", name, *base);
  576             return 0;
  577         }
  578         base++;
  579     }
  580     return 1;
  581 }
  582 
  583 int mysetenv(const char *name, const char *value) {
  584     /* from: http://www.onlamp.com/pub/a/onlamp/excerpt/PUIS3_chap16/index3.html */
  585     static char count = 0;
  586     char buff[255];
  587     
  588     if (count == 0)
  589         /* in case nothing ever gets put in here... */
  590         safeenv[0] = NULL;
  591     if (count == MAX_ENV)
  592             return 0;
  593     if (!name || !value)
  594             return 0;
  595     if (snprintf(buff, sizeof(buff), "%s=%s", name, value) < 0)
  596             return 0;
  597     if ((safeenv[count] = strdup(buff))) {
  598         safeenv[++count] = NULL;
  599         return 1;
  600     }
  601     return 0;
  602 }
  603 
  604 void filter_allowed_env_vars() {
  605     
  606     char *p_env;
  607     char **p_valid = allowed_env_vars;
  608     
  609     /* check each allowed variable */
  610     while (NULL != *p_valid) {
  611         
  612         p_env = (char*)getenv(*p_valid);
  613         if (NULL != p_env) {
  614             debug(LOG_DEBUG, "Found \"%s\" and setting it to \"%s\"", *p_valid, p_env);
  615             if (!mysetenv(*p_valid, p_env))
  616                 syslog(LOG_ERR, "Unable to set environment var \"%s\" to \"%s\"", *p_valid, p_env);
  617         } else {
  618             debug(LOG_DEBUG, "Unable to find \"%s\" in the environment", *p_valid);
  619         }
  620         p_valid++;
  621     }
  622 }
  623 
  624 /* vim: set noet sw=4 ts=4: */