"Fossies" - the Fresh Open Source Software Archive

Member "scponly-20110526/scponly.c" (27 May 2011, 21262 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 "scponly.c": 4.8_vs_20110526.

    1 /*
    2  *  scponly.c
    3  *
    4  *  http://sublimation.org/scponly
    5  *  joe@sublimation.org
    6  *
    7  *  see CONTRIB for additional credits
    8  */
    9  
   10 #include <stdio.h>  /* io */
   11 #include <string.h> /* for str* */
   12 #include <sys/types.h>  /* for fork, wait, stat */
   13 #include <sys/stat.h> /* for stat */
   14 #include <sys/wait.h>   /* for wait */
   15 #include <unistd.h> /* for exit, fork */
   16 #include <stdlib.h> /* EXIT_* */
   17 #include <errno.h>
   18 #include <syslog.h>
   19 #include "scponly.h"
   20 
   21 int debuglevel=0;
   22 int winscp_mode=0;
   23 int chrooted=0;
   24 char username[MAX_USERNAME];
   25 char homedir[FILENAME_MAX];
   26 char chrootdir[FILENAME_MAX];
   27 char *safeenv[MAX_ENV];
   28 
   29 /* will point to syslog or a noop */
   30 void (*debug)(int priority, const char* format, ...);
   31 int (*scponly_getopt_long)(
   32         int argc,
   33         char * const argv[],
   34         const char *optstring,
   35         const struct option *longopts,
   36         int *longindex
   37         );
   38 
   39 cmd_t commands[] =
   40 { 
   41 #ifdef ENABLE_SFTP
   42     { PROG_SFTP_SERVER, 1 },
   43 #endif /*ENABLE_SFTP*/
   44 #ifdef ENABLE_SCP2
   45     { PROG_LS, 1 }, 
   46     { PROG_CHMOD, 1 },
   47     { PROG_CHOWN, 1 },
   48     { PROG_CHGRP, 1 },
   49     { PROG_MKDIR, 1 },
   50     { PROG_RMDIR, 1 },
   51     { PROG_SCP, 1 },
   52     { PROG_LN, 1 },
   53     { PROG_MV, 1 },
   54     { PROG_RM, 1 },
   55     { PROG_CD, 1 },
   56 #endif /*ENABLE_SCP2*/
   57 
   58 #ifdef WINSCP_COMPAT
   59     { PROG_GROUPS, 0 },
   60     { PROG_PWD, 0 },
   61     { PROG_ECHO, 1 },
   62 #endif /*WINSCP_COMPAT*/
   63 
   64 #ifdef UNISON_COMPAT
   65     { PROG_UNISON, 1 },
   66 #endif /*ENABLE_UNISON*/
   67 
   68 #ifdef RSYNC_COMPAT
   69     { PROG_RSYNC, 1 },
   70 #endif /*ENABLE_RSYNC*/
   71 
   72 #ifdef PASSWD_COMPAT
   73     { PROG_PASSWD, 1 },
   74 #endif /*ENABLE_PASSWD*/
   75 
   76 #ifdef QUOTA_COMPAT
   77     { PROG_QUOTA, 1 },
   78 #endif /*QUOTA_COMPAT*/
   79 
   80 #ifdef SVN_COMPAT
   81     { PROG_SVN, 1 },
   82 #endif /*ENABLE_SVN*/
   83 
   84 #ifdef SVNSERV_COMPAT
   85     { PROG_SVNSERV, 1 },
   86 #endif /*ENABLE_SVNSERV*/
   87     
   88     { NULL }
   89 };
   90 
   91 /*
   92  * The array of longopts to be used for validation
   93  * longopts := (name, has_args, *flag, val)
   94  */
   95 struct option empty_longopts[] = {
   96     { NULL,         0,  NULL,   0 },
   97     };
   98 
   99 #ifdef RSYNC_COMPAT
  100 struct option rsync_longopts[] = {
  101     /* options we need to know about that are safe */
  102     {"server",          0,  0,      (int)'s'},
  103     /* the following options have behaviors we don't want to see */
  104     {"rsh",             1,  0,      (int)'r'},
  105     /* the following are disabled because they use daemon mode */
  106     {"daemon",          0,  0,      (int)'d'},
  107     {"rsync-path",      1,  0,      (int)'d'},
  108     {"address",         1,  0,      (int)'d'},
  109     {"port",            1,  0,      (int)'d'},
  110     {"sockopts",        1,  0,      (int)'d'},
  111     {"config",          1,  0,      (int)'d'},
  112     {"no-detach",       0,  0,      (int)'d'},
  113     { NULL,             0,  NULL,   0 },
  114     };
  115 #endif
  116 
  117 #ifdef SVNSERV_COMPAT
  118 struct option svnserv_longopts[] = {
  119     /* bad */
  120     {"daemon",      0,  NULL,   (int)'d' },
  121     {"listen-port", 1,  NULL,   (int)'d' },
  122     {"listen-host", 1,  NULL,   (int)'d' },
  123     {"foreground",  0,  NULL,   (int)'d' },
  124     {"inetd",       0,  NULL,   (int)'i' },
  125     {"threads",     0,  NULL,   (int)'T' },
  126     {"listen-once", 0,  NULL,   (int)'X' },
  127     /* good */
  128     {"read-only",   0,  NULL,   (int)'R' },
  129     {"help",        0,  NULL,   (int)'h' },
  130     {"root",        0,  NULL,   (int)'r' },
  131     {"tunnel",      0,  NULL,   (int)'t' },
  132     {"tunnel-user", 1,  NULL,   0 },
  133     { NULL,         0,  NULL,   0 },
  134     };
  135 #endif
  136 
  137 #ifdef SVN_COMPAT
  138 struct option svn_longopts[] = {
  139     /* bad */
  140     {"editor-cmd",  1,  NULL,   (int)'X' },
  141     {"diff-cmd",    1,  NULL,   (int)'X' },
  142     {"diff3-cmd",   1,  NULL,   (int)'X' },
  143     {"config-dir",  1,  NULL,   (int)'X' },
  144     { NULL,         0,  NULL,   0 },
  145     };
  146 #endif
  147 
  148 /*
  149  *  several binaries support arbitrary command execution in their arguments
  150  *  to prevent this we have to check the arguments for these binaries before
  151  *  invoking them.  
  152  */
  153 cmd_arg_t dangerous_args[] =
  154 {
  155     /*
  156      *  'oplist' only neccesary where 'use getopt' is 1
  157      *  'strict optlist' only applicable where 'use getopt?' is 1
  158      *  'badarg' is a string to look for if not in rsync mode, if in rsync mode a list of invalid options
  159      *
  160      * program name     use getopt?     strict optlist? badarg          optlist         longopts\n
  161      */
  162 #ifdef ENABLE_SFTP
  163     { PROG_SFTP_SERVER, 1,              1,              NULL,           "f:l:u:",       empty_longopts },
  164 #endif
  165 #ifdef ENABLE_SCP2
  166     { PROG_SCP,         1,              1,              "SoF",          "dfl:prtvBCc:i:P:q1246S:o:F:", empty_longopts },
  167 #endif
  168 #ifdef RSYNC_COMPAT
  169     { PROG_RSYNC,       1,              0,              "rde",          "e::",          rsync_longopts },
  170 #endif  
  171 #ifdef UNISON_COMPAT    
  172     { PROG_UNISON,      0,              0,              "-rshcmd",      NULL,           empty_longopts },
  173     { PROG_UNISON,      0,              0,              "-sshcmd",      NULL,           empty_longopts },
  174     { PROG_UNISON,      0,              0,              "-servercmd",   NULL,           empty_longopts },
  175     { PROG_UNISON,      0,              0,              "-addversionno",NULL,           empty_longopts },
  176 #endif
  177 #ifdef SVNSERV_COMPAT
  178     { PROG_SVNSERV,     1,              1,              "diTX",         "dihr:RtTX",    svnserv_longopts },
  179 #endif
  180 #ifdef SVN_COMPAT
  181     { PROG_SVN,         1,              0,              "Xx",           "NvxuRr:qm:F:",     svn_longopts },
  182 #endif
  183 #ifdef QUOTA_COMPAT
  184     { PROG_QUOTA,       1,              1,              NULL,           "-F:guvsilqQ",  empty_longopts },
  185 #endif
  186     { NULL }
  187 };
  188 
  189 /*
  190  *  SFTP logging requires that the following environment variables be
  191  *  defined in order to work:
  192  *
  193  *  LOG_SFTP
  194  *  USER
  195  *  SFTP_UMASK
  196  *  SFTP_PERMIT_CHMOD
  197  *  SFTP_PERMIT_CHOWN
  198  *  SFTP_LOG_LEVEL
  199  *  SFTP_LOG_FACILITY
  200  */
  201 char * allowed_env_vars[] =
  202 {
  203 #ifdef SFTP_LOGGING
  204     "LOG_SFTP",
  205     "USER",
  206     "SFTP_UMASK",
  207     "SFTP_PERMIT_CHMOD",
  208     "SFTP_PERMIT_CHOWN",
  209     "SFTP_LOG_LEVEL",
  210     "SFTP_LOG_FACILITY",
  211 #endif
  212 #ifdef UNISON_COMPAT
  213     "HOME",
  214 #endif
  215     NULL
  216 };
  217 
  218 int process_ssh_request(char *request);
  219 int process_pre_chroot_request(char **av);
  220 int winscp_regular_request(char *request);
  221 int winscp_transit_request(char *request);
  222 int process_winscp_requests(void);
  223 
  224 int main (int argc, char **argv) 
  225 {
  226     FILE *debugfile;
  227     int logopts = LOG_PID|LOG_NDELAY;
  228     int chars_read = 0;
  229 #ifdef CHROOT_CHECKDIR
  230     struct stat homedirstat;
  231 #endif
  232 
  233     /*
  234      * set debuglevel.  any nonzero number will result in debugging info to log
  235      */
  236     if (NULL!=(debugfile=fopen(DEBUGFILE,"r")))
  237     {
  238         chars_read = fscanf(debugfile,"%d",&debuglevel);
  239         if (chars_read < 1)
  240             debuglevel = 0;
  241         fclose(debugfile);
  242     }
  243 #ifndef UNIX_COMPAT
  244     if (debuglevel > 1) /* debuglevel 1 will still log to syslog */
  245         logopts |= LOG_PERROR;
  246 #endif
  247 
  248 #ifdef UNIX_COMPAT 
  249     openlog(PACKAGE_NAME, logopts, LOG_AUTH);
  250 #elif IRIX_COMPAT
  251     openlog(PACKAGE_NAME, logopts, LOG_AUTH);
  252 #else
  253     if (debuglevel > 1) /* debuglevel 1 will still log to syslog */
  254         logopts |= LOG_PERROR;
  255     openlog(PACKAGE_NAME, logopts, LOG_AUTHPRIV);
  256 #endif
  257 
  258     if (debuglevel > 0)
  259         debug = syslog;
  260     else
  261         debug = noop_syslog;
  262 
  263 #ifdef HAVE_GETOPT_H
  264     scponly_getopt_long = getopt_long;
  265 #else
  266     debug(LOG_INFO, "using netbsd's bundled getopt_long");
  267     scponly_getopt_long = netbsd_getopt_long;
  268 #endif
  269 
  270 #ifdef CHROOTED_NAME
  271     /*
  272      *  is this a chroot'ed scponly installation?
  273      */
  274 #ifdef WINSCP_COMPAT
  275     if ((argc==3 && (0==strncmp(argv[0],CHROOTED_NAME,FILENAME_MAX)) ) || 
  276         ( argc==1 && (0==strncmp(&argv[0][1],CHROOTED_NAME,FILENAME_MAX ))))
  277 
  278 #else
  279     if (0==strncmp(argv[0],CHROOTED_NAME,FILENAME_MAX))
  280 #endif
  281     {
  282         debug(LOG_INFO, "chrooted binary in place, will chroot()");
  283         chrooted=1;
  284     }
  285 #endif /* CHROOTED_NAME */
  286 
  287     if (debuglevel)
  288     {
  289         int i;
  290         syslog(LOG_DEBUG, "%d arguments in total.", argc);
  291         for (i=0;i<argc;i++)
  292             syslog(LOG_DEBUG, "\targ %u is %s", i, argv[i]);
  293     }
  294 
  295 #ifdef UNIX_COMPAT
  296     debug(LOG_DEBUG, "opened log at LOG_AUTH, opts 0x%08x", logopts);
  297 #else
  298     debug(LOG_DEBUG, "opened log at LOG_AUTHPRIV, opts 0x%08x", logopts);
  299 #endif
  300 
  301     if (getuid()==0)
  302     {   
  303         syslog(LOG_ERR, "root login denied [%s]", logstamp());
  304         exit(EXIT_FAILURE);
  305     }
  306 
  307 #ifdef WINSCP_COMPAT
  308     if ((argc!=3) && (argc!=1))
  309 #else
  310     if (argc!=3)
  311 #endif
  312     {
  313         debug(LOG_ERR, "incorrect number of args");
  314         exit(EXIT_FAILURE);
  315     }
  316     if (!get_uservar())
  317     {
  318         syslog(LOG_ERR, "%s is misconfigured. contact sysadmin.", argv[0]);
  319         exit (EXIT_FAILURE);
  320     }
  321 
  322 #ifdef CHROOTED_NAME
  323     if (chrooted)
  324     {
  325         char **av = NULL;
  326         char *tmprequest = NULL;
  327         char *root_dir = chrootdir;
  328         char chdir_path[FILENAME_MAX];
  329         
  330 
  331         strcpy(chrootdir, homedir);
  332         strcpy(chdir_path, "/");
  333         while((root_dir = strchr(root_dir, '/')) != NULL) 
  334         {
  335             if (strncmp(root_dir, "//", 2) == 0) 
  336             {
  337                 snprintf(chdir_path, FILENAME_MAX, "%s", root_dir + 1);
  338                 /* make sure HOME will be set to something correct if used*/
  339                 debug(LOG_DEBUG, "Setting homedir to %s", chdir_path);
  340                 strcpy(homedir, chdir_path);
  341                 *root_dir = '\0';
  342                 break;
  343             }
  344             root_dir++;
  345         }
  346 #ifdef CHROOT_CHECKDIR
  347         bzero(&homedirstat, sizeof(struct stat));
  348         if (-1 == stat(chrootdir, &homedirstat))
  349         {
  350             syslog (LOG_ERR, "couldnt stat chroot dir: %s with errno %u", chrootdir, errno);
  351             exit(EXIT_FAILURE);
  352         }
  353         if (0 == (homedirstat.st_mode | S_IFDIR))
  354         {
  355             syslog (LOG_ERR, "chroot dir is not a directory: %s", chrootdir);
  356             exit(EXIT_FAILURE);
  357         }
  358         if (homedirstat.st_uid != 0)
  359         {
  360             syslog (LOG_ERR, "chroot dir not owned by root: %s", chrootdir);
  361             exit(EXIT_FAILURE);
  362         }
  363         if (0 != (homedirstat.st_mode & S_IWOTH))
  364         {
  365             syslog (LOG_ERR, "chroot dir writable by other: %s", chrootdir);
  366             exit(EXIT_FAILURE);
  367         }
  368         if (0 != (homedirstat.st_mode & S_IWGRP))
  369         {
  370             syslog (LOG_ERR, "chroot dir writable by group: %s", chrootdir);
  371             exit(EXIT_FAILURE);
  372         }
  373 #endif
  374 
  375 /* already within CHROOTED_NAME block */
  376 #if defined(PASSWD_COMPAT) || defined(QUOTA_COMPAT)
  377         
  378         /*
  379          * perhaps we need to refactor so we don't have to exit right
  380          * in the middle of the code, but we can't chroot and expect to be
  381          * able to change the password and have it be of any use unless
  382          * there is some additional process that scponly is unaware of
  383          * happening on the back end.
  384          */
  385         tmprequest = strdup(argv[2]);
  386         av = build_arg_vector(tmprequest);
  387         free(tmprequest);
  388         if (
  389 #ifdef PASSWD_COMPAT
  390             (exact_match(av[0],"passwd"))
  391             || (exact_match(av[0],PROG_PASSWD))
  392 #else
  393             0
  394 #endif
  395 #ifdef QUOTA_COMPAT
  396             || (exact_match(av[0],"quota"))
  397             || (exact_match(av[0],PROG_QUOTA))
  398 #endif
  399         ) {
  400             int status = process_pre_chroot_request(av);
  401             discard_vector(av);
  402             
  403             if (status) {
  404                 syslog(LOG_ERR, "process_pre_chroot_request(%s) failed with code %i [%s]",
  405                     argv[2],WEXITSTATUS(status),logstamp());
  406                 exit(EXIT_FAILURE);
  407             }
  408             debug(LOG_DEBUG, "scponly completed");
  409             exit(EXIT_SUCCESS);
  410         } else {
  411             discard_vector(av);
  412         }
  413 
  414 #endif /* passwd or quota */
  415 
  416         debug(LOG_DEBUG, "chrooting to dir: \"%s\"", chrootdir);
  417         if (-1==(chroot(chrootdir)))
  418         {
  419             debug(LOG_ERR, "chroot: %m");
  420             syslog(LOG_ERR, "couldn't chroot to %s [%s]", chrootdir, logstamp());
  421             exit(EXIT_FAILURE);
  422         }
  423         
  424         debug(LOG_DEBUG, "chdiring to dir: \"%s\"", chdir_path);                         
  425         if (-1==(chdir(chdir_path)))                                           
  426         {                                                         
  427             debug(LOG_ERR, "chdir: %m");                                 
  428             syslog (LOG_ERR, "couldn't chdir to %s [%s]", chdir_path, logstamp());                    
  429             exit(EXIT_FAILURE);     
  430         }
  431     }
  432 #endif /* CHROOTED_NAME */
  433 
  434     debug(LOG_DEBUG, "setting uid to %u", getuid());
  435     if (-1==(seteuid(getuid())))
  436     {
  437         syslog(LOG_ERR, "couldn't revert to my real uid. seteuid: %m");
  438         exit(EXIT_FAILURE);
  439     }
  440 
  441 #ifdef WINSCP_COMPAT
  442     if (argc==1)
  443     {
  444         debug(LOG_DEBUG, "entering WinSCP compatibility mode [%s]",logstamp());
  445         if (-1==process_winscp_requests())
  446         {
  447             syslog(LOG_ERR, "failed WinSCP compatibility mode [%s]", logstamp());
  448             exit(EXIT_FAILURE);
  449         }
  450     }
  451 #else
  452     if (0)  {}  /*  placeholder */
  453 #endif
  454     else if (-1==process_ssh_request(argv[2]))
  455     {
  456         syslog(LOG_ERR, "bad request: %s [%s]", argv[2], logstamp());
  457         exit(EXIT_FAILURE);
  458     }
  459     debug(LOG_DEBUG, "scponly completed");
  460     exit(EXIT_SUCCESS);
  461 }
  462 
  463 #ifdef CHROOTED_NAME
  464 #if defined(PASSWD_COMPAT) || defined(QUOTA_COMPAT)
  465 
  466 int process_pre_chroot_request(char ** av) {
  467 
  468     char *flat_request = NULL;
  469     char *env[2] = { NULL, NULL };
  470     int retval = -1;
  471     
  472     /* revert to real user so I'm not changing the passwd as root */
  473     debug(LOG_DEBUG, "handling pre-chroot request");
  474     debug(LOG_DEBUG, "setting uid to %u", getuid());
  475 
  476     if (-1==(seteuid(getuid())))
  477     {
  478         syslog (LOG_ERR, "couldn't revert to my real uid. seteuid: %m");
  479         exit(EXIT_FAILURE);
  480     }
  481 
  482     av[0] = substitute_known_path(av[0]);
  483     flat_request = flatten_vector(av);
  484     
  485     /* 
  486      * sanity check, substitute_known_path should have given the exact path,
  487      * ONLY execute if an exact match was found
  488      */
  489     if (
  490 #ifdef PASSWD_COMPAT
  491         (!exact_match(av[0], PROG_PASSWD))
  492 #else
  493         1
  494 #endif
  495 #ifdef QUOTA_COMPAT
  496         && (!exact_match(av[0], PROG_QUOTA))
  497 #endif
  498     ) {
  499         syslog(LOG_ERR, "Invalid pre-chroot request attempted: '%s' [%s]", av[0], logstamp());
  500         exit(EXIT_FAILURE);
  501     }
  502     
  503     if(check_dangerous_args(av))
  504     {
  505         syslog(LOG_ERR, "requested command (%s) tried to use disallowed argument [%s])", 
  506             flat_request, logstamp());
  507         exit(EXIT_FAILURE);
  508     }
  509 
  510     if (valid_arg_vector(av))
  511     {
  512         int status = 0;
  513         
  514         debug(LOG_DEBUG, "about to fork (%s) [%s]",flat_request,logstamp());
  515         
  516         if (fork() == 0)
  517             retval=execve(av[0],av,env);
  518         else
  519         {
  520             wait(&status);
  521             fflush(stdout);
  522             fflush(stderr);
  523             free(flat_request);
  524             
  525             debug(LOG_DEBUG, "forked child returned... [%s]",logstamp());
  526             
  527             return WEXITSTATUS(status);
  528         }
  529 
  530     } else {
  531         syslog(LOG_ERR, "invalid argument vector (%s) [%s]", 
  532             flat_request,logstamp());
  533         free(flat_request);
  534         
  535         exit(EXIT_FAILURE);
  536     }
  537     return retval;
  538 }
  539 
  540 #endif /* passwd or quota support */
  541 #endif /* chrooted support */
  542 
  543 #ifdef WINSCP_COMPAT
  544 
  545 int winscp_transit_request(char *request)
  546 {
  547     char *new_request=NULL;
  548 
  549     /*
  550      * for scp -t and scp -f commands, winscp preceeds each
  551      * command with a "begin-of-file" marker we need to
  552      * provide, everything after that can be handled by
  553      * winscp_regular_request.
  554      */
  555 
  556     new_request=strbeg(request,WINSCP_BOF_REQ);
  557     if (NULL == new_request)
  558     {
  559         return(-1);  /* improper transit cmd */
  560     }
  561     printf ("%s\n",WINSCP_BOF); /* start transfer */
  562     fflush(stdout);
  563     return(winscp_regular_request(new_request));
  564 }
  565 
  566 int winscp_regular_request(char *request)
  567 {
  568     /*
  569      * winscp uses one of two requests to terminate each command.
  570      * we must determine which (if any) is terminating this request. fun!
  571      */
  572     char *new_request=NULL;
  573     int retval=0;
  574     int retzero=1;
  575     new_request=strend(request, WINSCP_EOF_REQ_ZERO);
  576     if (NULL == new_request)
  577     {
  578         retzero=0;
  579         new_request=strend(request, WINSCP_EOF_REQ_STATUS);
  580         if (NULL == new_request)
  581         {
  582             new_request=strend(request, WINSCP_EOF_REQ_RETVAL);
  583             if (NULL == new_request)
  584             {
  585                 printf ("command wasn't terminated with %s, %s or %s\n",
  586                     WINSCP_EOF_REQ_RETVAL, WINSCP_EOF_REQ_ZERO, WINSCP_EOF_REQ_STATUS);
  587                 return(-1); /* bogus termination */
  588             }
  589         }
  590     }
  591     /*
  592      *  here is where we fool winscp clients into believing we are a real shell
  593      */
  594     if ((exact_match(new_request, "echo \"$status\"")) ||
  595         (exact_match(new_request, "echo \"$?\"")))
  596     {
  597         printf ("0\n");
  598         fflush(stdout);
  599     }
  600     /*
  601      *  ignore unalias and unset commands
  602      */
  603     else if ((NULL!=strbeg(new_request,"unset ")) ||
  604         (NULL!=strbeg(new_request,"unalias ")))
  605         retval=0;
  606     else
  607     {
  608         retval=process_ssh_request(new_request);
  609         if (retzero)        /* ignore actual retval if winscp wants us to */
  610             retval=0;
  611     }
  612     free(new_request);
  613     return(retval);
  614 }
  615 
  616 int process_winscp_requests(void)
  617 {
  618     char    linebuf[MAX_REQUEST];
  619     int count=0;        /* num of semicolons in cmd */
  620     int ack=0;
  621 
  622     winscp_mode=1;
  623 
  624     fflush(stdout);
  625 
  626 #ifdef ENABLE_DEFAULT_CHDIR
  627     syslog(LOG_INFO, "changing initial directory to %s", DEFAULT_CHDIR);
  628     chdir(DEFAULT_CHDIR);
  629 #endif
  630 
  631     /*
  632      *  now process commands interactively
  633      */
  634     while (fgets(&linebuf[0],MAX_REQUEST, stdin) != NULL)
  635     {
  636         ack=0;
  637 
  638         if (strlen(linebuf)==0)
  639             return(-1);
  640 
  641         linebuf[strlen(linebuf)-1]=0;   /* drop the trailing CR */
  642         count=cntchr(linebuf,';');
  643 
  644         if (count==1)   /* regular cmd */
  645         {
  646             ack=winscp_regular_request(linebuf);
  647         }
  648         else if (count==2)   /* transit command */
  649         {
  650             ack=winscp_transit_request(linebuf);
  651         }
  652         else
  653             ack=0;  /* winscp always sends 2 or 3 cmds at once */
  654 
  655         printf ("%s%d\n",WINSCP_EOF,ack); /* respond to client */
  656         fflush(stdout);
  657     }
  658     return 0;
  659 }
  660 
  661 #endif
  662 
  663 int process_ssh_request(char *request)
  664 {
  665     char **av, **tmp_av, **tenv;
  666     char *flat_request,*tmpstring, *tmprequest;
  667     char bad_winscp3str[] = "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server exec sftp-server";
  668     int retval;
  669     int reqlen=strlen(request);
  670     char **env = NULL;
  671 
  672     debug(LOG_DEBUG, "processing request: \"%s\"\n", request);
  673 
  674     tmprequest=strdup(request);
  675 
  676 #ifdef WINSCP_COMPAT            
  677 
  678     bad_winscp3str[57]=10;
  679     bad_winscp3str[127]=10;
  680             
  681     if(strcmp(request,bad_winscp3str)==0)
  682     {
  683         /*
  684          * switch out the command to use, winscp wont know the difference
  685          */
  686         free(tmprequest);
  687         tmprequest=strdup(PROG_SFTP_SERVER);
  688         syslog(LOG_DEBUG, "winscp3 compat correcting to: \"[%s]\"\n", PROG_SFTP_SERVER);
  689     }
  690 #endif
  691 
  692     
  693 #ifdef GFTP_COMPAT 
  694     /*
  695      *  gFTP compatibility hack
  696      */
  697     if (NULL != (tmpstring=strbeg(request, "echo -n xsftp ; ")))
  698     {
  699         free(tmprequest);
  700         tmprequest=strdup(tmpstring);
  701         printf("xsftp");
  702         fflush(stdout);
  703     }
  704 #endif
  705 
  706 #ifdef RESTRICTIVE_FILENAMES
  707     /*
  708      * we flat out reject special chars
  709      */
  710     if (!valid_chars(tmprequest))
  711     {
  712         debug(LOG_DEBUG, "rejected because of invalid chars (%s)", logstamp());
  713         free(tmprequest);
  714         return(-1);
  715     }
  716 #endif
  717 
  718 #ifdef WINSCP_COMPAT
  719     if (strbeg(tmprequest,PROG_CD))
  720     {
  721         char *destdir=(char *)malloc(reqlen);
  722         if (destdir == NULL)
  723         {
  724             perror("malloc");
  725             exit(EXIT_FAILURE);
  726         }
  727 
  728         /*
  729          * well, now that scponly is a persistent shell
  730          * i have to maintain a $PWD.  damn.
  731          * we're going to INSIST upon a double quote
  732          * encapsulated new directory to change to.
  733          */
  734         if ((tmprequest[(reqlen-1)]=='"') && (tmprequest[3]=='"'))
  735         {
  736             bzero(destdir,reqlen);
  737             strncpy(destdir,&tmprequest[4],reqlen-5);
  738             debug(LOG_INFO, "chdir: %s (%s)", tmprequest, logstamp());
  739             retval=chdir(destdir);
  740             free(destdir);
  741             free(tmprequest);
  742             return(retval);
  743         }
  744         syslog(LOG_ERR, "bogus chdir request: %s (%s)", tmprequest, logstamp());
  745         free(tmprequest);
  746         return(-1);
  747     }
  748 #endif
  749 
  750     /*
  751      * convert request string to an arg_vector
  752      */
  753     av = build_arg_vector(tmprequest);
  754 
  755     /*
  756      * clean any path info from request and substitute our known pathnames
  757      */
  758     av[0] = substitute_known_path(av[0]);
  759     
  760     /*
  761      * we only process wildcards for scp commands
  762      */
  763 #ifdef ENABLE_WILDCARDS
  764 #ifdef ENABLE_SCP2
  765     if (exact_match(av[0],PROG_SCP))
  766         av = expand_wildcards(av);
  767 #endif
  768 #endif
  769 
  770 /*
  771  *  check for a compile time chdir configuration
  772  */
  773 #ifdef ENABLE_DEFAULT_CHDIR
  774     if (exact_match(av[0],PROG_SFTP_SERVER))
  775     {
  776         syslog(LOG_INFO, "changing initial directory to %s", DEFAULT_CHDIR);
  777         chdir(DEFAULT_CHDIR);
  778     }
  779 #endif
  780     
  781 
  782     flat_request = flatten_vector(av);
  783 
  784     /* 
  785      * Use a temp arg vector since getopt will permute the command line arguments
  786      * for anything that it does not know about.  If all rsync options are well
  787      * defined this isn't necessary.
  788      */
  789     tmp_av = build_arg_vector(flat_request);
  790     if(check_dangerous_args(tmp_av))
  791     {
  792         syslog(LOG_ERR, "requested command (%s) tried to use disallowed argument (%s))", 
  793             flat_request, logstamp());
  794         exit(EXIT_FAILURE);
  795     }
  796     discard_vector(tmp_av);
  797 
  798     if (valid_arg_vector(av))
  799     {
  800 
  801 /*                                                         
  802  * Unison needs the HOME environment variable be set to the directory                         
  803  * where the .unison directory resides.                                     
  804  */                                                         
  805 #ifdef USE_SAFE_ENVIRONMENT
  806         safeenv[0] = NULL;
  807         filter_allowed_env_vars();
  808         tenv = safeenv;
  809         if (debuglevel) {
  810             while (NULL != *tenv) {
  811                 syslog(LOG_DEBUG, "Environment contains \"%s\"", *tenv++);
  812             }
  813         }
  814         env = safeenv;
  815 #endif
  816 
  817 #ifdef UNISON_COMPAT
  818         /* the HOME environment variable should have been set above, but I need to make sure
  819          * that it's value as read from the environment is replaced with the actual value
  820          * as it exists within the chroot, which is what the applications will expect to see.
  821          */
  822         if (replace_env_entry("HOME",homedir) && (((strlen(homedir) + 6 ) > FILENAME_MAX) || !mysetenv("HOME",homedir)))
  823         {
  824             syslog(LOG_ERR, "could not set HOME environment variable (%s)", logstamp());
  825             exit(EXIT_FAILURE);
  826         }
  827         debug(LOG_DEBUG, "set non-chrooted HOME environment variable to %s (%s)", homedir, logstamp());
  828 #endif 
  829         syslog(LOG_INFO, "running: %s (%s)", flat_request, logstamp());
  830 
  831 #ifdef WINSCP_COMPAT
  832         if (winscp_mode)
  833         {
  834             int status=0;
  835             if (fork() == 0)
  836                 retval=execve(av[0],av,env);
  837             else
  838             {
  839                 wait(&status);
  840                 fflush(stdout);
  841                 fflush(stderr);
  842                 discard_vector(av);
  843 #ifdef USE_SAFE_ENVIRONMENT                                             
  844                 discard_child_vectors(safeenv);
  845 #endif
  846                 free(flat_request);
  847                 free(tmprequest);
  848                 return(WEXITSTATUS(status));
  849             }
  850         }
  851         else
  852 #endif
  853         {
  854             debug(LOG_DEBUG, "about to exec \"%s\" (%s)", av[0], logstamp());
  855             retval=execve(av[0],av,env);
  856         }
  857         syslog(LOG_ERR, "failed: %s with error %s(%u) (%s)", flat_request, strerror(errno), errno, logstamp());
  858         free(flat_request);
  859         discard_vector(av);
  860 #ifdef USE_SAFE_ENVIRONMENT
  861         discard_child_vectors(safeenv);
  862 #endif
  863 #ifdef WINSCP_COMPAT
  864         if (winscp_mode)
  865         {
  866             free(tmprequest);
  867             return(-1);
  868         }
  869         else
  870 #endif 
  871             exit(errno);
  872     }
  873 
  874     /*
  875      *  reaching this point in the code means the request isnt one of
  876      *  our accepted commands
  877      */
  878     if (debuglevel)
  879     {
  880         if (exact_match(flat_request,tmprequest))
  881             syslog (LOG_ERR, "denied request: %s [%s]", tmprequest, logstamp());
  882         else
  883             syslog (LOG_ERR, "denied request: %s (resolved to: %s) [%s]", tmprequest, flat_request, logstamp());
  884     }
  885     free(flat_request); 
  886 #ifdef WINSCP_COMPAT
  887     if (winscp_mode)
  888     {
  889         printf ("command not permitted by scponly\n");
  890         free(tmprequest);
  891         return(-1);
  892     }
  893     else
  894 #endif 
  895         exit(EXIT_FAILURE);
  896 }
  897 
  898 /* vim: set noet sw=4 ts=4: */