/* * scponly.c * * http://sublimation.org/scponly * joe@sublimation.org * * see CONTRIB for additional credits */ #include /* io */ #include /* for str* */ #include /* for fork, wait, stat */ #include /* for stat */ #include /* for wait */ #include /* for exit, fork */ #include /* EXIT_* */ #include #include #include "scponly.h" int debuglevel=0; int winscp_mode=0; int chrooted=0; char username[MAX_USERNAME]; char homedir[FILENAME_MAX]; char chrootdir[FILENAME_MAX]; char *safeenv[MAX_ENV]; cmd_t commands[] = { #ifdef ENABLE_SFTP { PROG_SFTP_SERVER, 0 }, #endif /*ENABLE_SFTP*/ #ifdef ENABLE_SCP2 { PROG_LS, 1 }, { PROG_CHMOD, 1 }, { PROG_CHOWN, 1 }, { PROG_CHGRP, 1 }, { PROG_MKDIR, 1 }, { PROG_RMDIR, 1 }, { PROG_SCP, 1 }, { PROG_LN, 1 }, { PROG_MV, 1 }, { PROG_RM, 1 }, { PROG_CD, 1 }, #endif /*ENABLE_SCP2*/ #ifdef WINSCP_COMPAT { PROG_GROUPS, 0 }, { PROG_PWD, 0 }, { PROG_ECHO, 1 }, #endif /*WINSCP_COMPAT*/ #ifdef UNISON_COMPAT { PROG_UNISON, 1 }, #endif /*ENABLE_UNISON*/ #ifdef RSYNC_COMPAT { PROG_RSYNC, 1 }, #endif /*ENABLE_RSYNC*/ #ifdef PASSWD_COMPAT { PROG_PASSWD, 1 }, #endif /*ENABLE_PASSWD*/ #ifdef QUOTA_COMPAT { PROG_QUOTA, 1 }, #endif /*QUOTA_COMPAT*/ #ifdef SVN_COMPAT { PROG_SVN, 1 }, #endif /*ENABLE_SVN*/ #ifdef SVNSERV_COMPAT { PROG_SVNSERV, 1 }, #endif /*ENABLE_SVNSERV*/ NULL }; /* * several binaries support arbitrary command execution in their arguments * to prevent this we have to check the arguments for these binaries before * invoking them. */ cmd_arg_t dangerous_args[] = { /* * 'oplist' only neccesary where 'use getopt' is 1 * 'strict optlist' only applicable where 'use getopt?' is 1 * * program name use getopt? strict optlist? optname optlist */ #ifdef ENABLE_SCP2 { PROG_SCP, 1, 1, "S", "dfl:prtvBCc:i:P:q1246S:o:F:" }, #endif #ifdef RSYNC_COMPAT { PROG_RSYNC, 1, 0, "e", "e:" }, { PROG_RSYNC, 0, 0, "-rsh", NULL }, #endif #ifdef UNISON_COMPAT { PROG_UNISON, 0, 0, "-rshcmd", NULL }, { PROG_UNISON, 0, 0, "-sshcmd", NULL }, { PROG_UNISON, 0, 0, "-servercmd, NULL" }, #endif NULL }; /* * SFTP logging requires that the following environment variables be * defined in order to work: * * LOG_SFTP * USER * SFTP_UMASK * SFTP_PERMIT_CHMOD * SFTP_PERMIT_CHOWN * SFTP_LOG_LEVEL * SFTP_LOG_FACILITY */ char * allowed_env_vars[] = { #ifdef SFTP_LOGGING "LOG_SFTP", "USER", "SFTP_UMASK", "SFTP_PERMIT_CHMOD", "SFTP_PERMIT_CHOWN", "SFTP_LOG_LEVEL", "SFTP_LOG_FACILITY", #endif #ifdef UNISON_COMPAT "HOME", #endif NULL }; int process_ssh_request(char *request); int winscp_regular_request(char *request); int winscp_transit_request(char *request); int process_winscp_requests(void); int main (int argc, char **argv) { FILE *debugfile; int logopts = LOG_PID|LOG_NDELAY; struct stat homedirstat; /* * set debuglevel. any nonzero number will result in debugging info to log */ if (NULL!=(debugfile=fopen(DEBUGFILE,"r"))) { fscanf(debugfile,"%u",&debuglevel); fclose(debugfile); } #ifndef UNIX_COMPAT if (debuglevel > 1) /* debuglevel 1 will still log to syslog */ logopts |= LOG_PERROR; #endif #ifdef UNIX_COMPAT openlog(PACKAGE_NAME, logopts, LOG_AUTH); #elif IRIX_COMPAT openlog(PACKAGE_NAME, logopts, LOG_AUTH); #else if (debuglevel > 1) /* debuglevel 1 will still log to syslog */ logopts |= LOG_PERROR; openlog(PACKAGE_NAME, logopts, LOG_AUTHPRIV); #endif #ifdef CHROOTED_NAME /* * is this a chroot'ed scponly installation? */ #ifdef WINSCP_COMPAT if ((argc==3 && (0==strncmp(argv[0],CHROOTED_NAME,FILENAME_MAX)) ) || ( argc==1 && (0==strncmp(&argv[0][1],CHROOTED_NAME,FILENAME_MAX )))) #else if (0==strncmp(argv[0],CHROOTED_NAME,FILENAME_MAX)) #endif { if (debuglevel) syslog(LOG_INFO, "chrooted binary in place, will chroot()"); chrooted=1; } #endif /* CHROOTED_NAME */ if (debuglevel) { int i; syslog(LOG_DEBUG, "%d arguments in total.", argc); for (i=0;i FILENAME_MAX) || !mysetenv("HOME",homedir)) { syslog(LOG_ERR, "could not set HOME environment variable (%s)", logstamp()); exit(EXIT_FAILURE); } if (debuglevel) syslog(LOG_DEBUG, "set HOME environment variable to %s (%s)", homedir, logstamp()); } #endif syslog(LOG_INFO, "running: %s (%s)", flat_request, logstamp()); #ifdef WINSCP_COMPAT if (winscp_mode) { int status=0; if (fork() == 0) #ifdef USE_SAFE_ENVIRONMENT retval=execve(av[0],av,safeenv); #else retval=execve(av[0],av,NULL); #endif else { wait(&status); fflush(stdout); fflush(stderr); discard_vector(av); #ifdef USE_SAFE_ENVIRONMENT discard_vector(safeenv); #endif free(flat_request); free(tmprequest); return(WEXITSTATUS(status)); } } else #endif { #ifdef USE_SAFE_ENVIRONMENT retval=execve(av[0],av,safeenv); #else retval=execve(av[0],av,NULL); #endif } syslog(LOG_ERR, "failed: %s with error %s(%u) (%s)", flat_request, strerror(errno), errno, logstamp()); free(flat_request); discard_vector(av); #ifdef USE_SAFE_ENVIRONMENT discard_vector(safeenv); #endif #ifdef WINSCP_COMPAT if (winscp_mode) { free(tmprequest); return(-1); } else #endif exit(errno); } /* * reaching this point in the code means the request isnt one of * our accepted commands */ if (debuglevel) { if (exact_match(flat_request,tmprequest)) syslog (LOG_ERR, "denied request: %s [%s]", tmprequest, logstamp()); else syslog (LOG_ERR, "denied request: %s (resolved to: %s) [%s]", tmprequest, flat_request, logstamp()); } free(flat_request); #ifdef WINSCP_COMPAT if (winscp_mode) { printf ("command not permitted by scponly\n"); free(tmprequest); return(-1); } else #endif exit(EXIT_FAILURE); }