From: <ton...@us...> - 2003-12-14 10:51:37
|
Update of /cvsroot/serverfilters/script In directory sc8-pr-cvs1:/tmp/cvs-serv31087 Modified Files: filtercmd.c Added Files: Makefile filtercmd.opts test.sh Log Message: Progress on filtercmd credential checking. filtercmd now reads a username and password from stdin; uses the username to drop priviledges; reads the IMAP server from a file; but does not yet actually try log in to the server. Path checking is in place. More file owner checking needs to be done. --- NEW FILE: Makefile --- all: filtercmd filtercmd: filtercmd.c filtercmd.opts gcc -o filtercmd filtercmd.c `cat filtercmd.opts` chmod 4750 filtercmd chown root:apache filtercmd test: test.sh filtercmd sh test.sh --- NEW FILE: filtercmd.opts --- -DRCCHECK1="/home/*/.procmailrc" -DTMPCHECK1="/tmp/*" --- NEW FILE: test.sh --- (echo $USER; echo a_password) | ./filtercmd getrc /home/$USER/.procmailrc /tmp/test || echo "RESULT:" $? Index: filtercmd.c =================================================================== RCS file: /cvsroot/serverfilters/script/filtercmd.c,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** filtercmd.c 9 Dec 2003 19:01:28 -0000 1.2 --- filtercmd.c 13 Dec 2003 19:47:39 -0000 1.3 *************** *** 1,10 **** /* ! how-to compile: ! # gcc -o filtercmd filtercmd.c; chmod 4750 filtercmd; chown root:apache filtercmd ! */ #define STR_MAX 1024 #define MAXLEN 1024 #include <stdio.h> #include <unistd.h> #include <sys/types.h> --- 1,13 ---- /* ! * filtercmd -- installs/reads filter files (.procmailrc, etc) for squirrelmail ! * ! */ #define STR_MAX 1024 #define MAXLEN 1024 + + #include <stdio.h> + #include <string.h> #include <unistd.h> #include <sys/types.h> *************** *** 17,21 **** --- 20,77 ---- int putrc(char*, char*, char*); int copy_file(char*, char*); + int validrcpath(char*); + int validtmppath(char*); + int checkpath(char*, char*); + int validpath(char*, char**); + + /* error definitions */ + #define ERR_NO_RC_FILE 1 + #define ERR_NO_CMD 2 + #define ERR_USAGE 3 + #define ERR_INVALID_COMMAND 4 + #define ERR_USER_IS_ROOT 5 + #define ERR_BAD_RC_PATH 6 + #define ERR_BAD_TEMP_PATH 7 + #define ERR_COPY_CANT_OPEN_SRC 8 + #define ERR_COPY_CANT_OPEN_DEST 9 + #define ERR_NEED_CREDENTIALS 10 + #define ERR_CANT_READ_IMAP_SERVER 11 + #define ERR_BAD_UID_GID 12 + #define ERR_BAD_CREDENTIALS 13 + static char *err_strings[] = { + /* non error (error index 0) */ + "", + + /* silent error -- rcexists false result */ + "", + + "You forgot to provide a command.\n", + + /* placeholder - usage errors are expected to be printed directly */ + "Usage error - consult filtercmd.c\n", + + "Invalid command specified.\n", + + /* this command will refuse to handles root's filter files */ + "The root user cannot be edited for security reasons\n", + + "rc file path fails checks\n", + + "temp file path fails checks\n", + + "Can't open source file\n", + + "Can't open destination file\n", + + "Credentials not passed correctly\n", + + "Can't read imap server from configfile\n", + + "Can't find uid/gid for user\n", + + "Bad credentials\n", + + }; int main(int argc, char *argv[]){ *************** *** 29,83 **** char cmd[STR_MAX]; char user[STR_MAX]; char filter_file[STR_MAX]; char temp_file[STR_MAX]; uid_t UID; gid_t GID; ! UID = getuid(); ! GID = getgid(); ! ! if((setuid(0)) < 0) eperror("setuid"); ! if((setgid(3)) < 0) eperror("setgid"); ! sprintf(cmd,"%s",argv[1]); if(!strlen(cmd)){ ! printf("You forgot to provide a command.\n"); ! return 1; } else { if (!strcmp("getrc",cmd)) { if (argc < 4) { printf("Usage: filtercmd getrc filter_file temp_file\n"); ! return 2; } ! sprintf(filter_file, "%s", argv[2]); ! sprintf(temp_file, "%s", argv[3]); return getrc(filter_file, temp_file, UID, GID); } else if (!strcmp("putrc", cmd)) { if (argc < 5) { printf("Usage: filtercmd putrc owner temp_file filter_file\n"); ! return 3; } ! sprintf(user, "%s", argv[2]); if(!strcmp(user,"root") && !strcmp(user, "0")){ ! printf("The file cannot have root ownership for security reasons.\n"); ! return 6; //the root user cannot be edited for security reasons } ! sprintf(temp_file, "%s", argv[3]); ! sprintf(filter_file, "%s", argv[4]); return putrc(user, temp_file, filter_file); } else if (!strcmp("rcexists",cmd)) { if (argc < 3) { printf("Usage: filtercmd rcexists filter_file\n"); ! return 4; } ! sprintf(filter_file, "%s", argv[2]); return rcexists(filter_file); } else if (argc > 1) { ! printf("Invalid command specified.\n"); ! return 5; } else { printf("Usage: filtercmd [getrc|putrc|rcexists]\n"); ! return 7; } } --- 85,151 ---- char cmd[STR_MAX]; char user[STR_MAX]; + char passwd[STR_MAX]; char filter_file[STR_MAX]; char temp_file[STR_MAX]; + char imap_server[STR_MAX]; uid_t UID; gid_t GID; ! if (!readcredentials(user, passwd, STR_MAX)) { ! return inerror(ERR_NEED_CREDENTIALS); ! } ! if (!readimapserver(imap_server, STR_MAX)) { ! return inerror(ERR_CANT_READ_IMAP_SERVER); ! } ! ! if (!finduidgid(user, &UID, &GID)) { ! return inerror(ERR_BAD_UID_GID); ! } ! ! if((setgid(GID)) < 0) eperror("setgid"); ! if((setuid(UID)) < 0) eperror("setuid"); ! ! if (!checkcredentials(imap_server, user, passwd)) { ! return inerror(ERR_BAD_CREDENTIALS); ! } ! ! strncpy(cmd,argv[1],STR_MAX); if(!strlen(cmd)){ ! return inerror(ERR_NO_CMD); } else { if (!strcmp("getrc",cmd)) { if (argc < 4) { printf("Usage: filtercmd getrc filter_file temp_file\n"); ! return ERR_USAGE; } ! strncpy(filter_file, argv[2], STR_MAX); ! strncpy(temp_file, argv[3], STR_MAX); return getrc(filter_file, temp_file, UID, GID); } else if (!strcmp("putrc", cmd)) { if (argc < 5) { printf("Usage: filtercmd putrc owner temp_file filter_file\n"); ! return ERR_USAGE; } ! strncpy(user, argv[2], STR_MAX); if(!strcmp(user,"root") && !strcmp(user, "0")){ ! return inerror(ERR_USER_IS_ROOT); } ! strncpy(temp_file, argv[3], STR_MAX); ! strncpy(filter_file, argv[4], STR_MAX); return putrc(user, temp_file, filter_file); } else if (!strcmp("rcexists",cmd)) { if (argc < 3) { printf("Usage: filtercmd rcexists filter_file\n"); ! return ERR_USAGE; } ! strncpy(filter_file, argv[2], STR_MAX); return rcexists(filter_file); } else if (argc > 1) { ! return inerror(ERR_INVALID_COMMAND); } else { printf("Usage: filtercmd [getrc|putrc|rcexists]\n"); ! return ERR_USAGE; } } *************** *** 94,109 **** Thiago Melo de Paula - th...@fa... */ ! char str[50]; ! sprintf(str,"filtercmd - %s",s); perror(str); exit(1); } int rcexists(char* filter_file) { FILE *filter; if(!(filter=fopen(filter_file,"r"))){ ! return 7; } else { fclose(filter); --- 162,271 ---- Thiago Melo de Paula - th...@fa... */ ! char str[STR_MAX]; ! snprintf(str,STR_MAX,"filtercmd - %s",s); perror(str); exit(1); } + int inerror(errnum) + int errnum; + { + fprintf(stderr, "%s", err_strings[errnum]); + return errnum; + } + + /* Read credentials from stdin; user and passwd should each be on a line + * user may not include spaces. FIXME: password may not include + * spaces either, currently. + */ + int readcredentials(user, passwd, n) + char *user; + char *passwd; + int n; + { + int result; + /* we limit credentials to 100 characters in the scanf string. + make sure we were passed big enough buffers (including room for NUL) */ + if (n <= 100) return 0; + result = scanf("%100s%100s", user, passwd); + if (result == EOF || result < 2) return 0; + return 1; + } + + #define CONFIGFILE "/etc/squirrelmail/config.php" + int readimapserver(server, serverlen) + char *server; + int serverlen; + { + FILE *f; + char line[STR_MAX]; + int foundserver = 0; + + f = fopen(CONFIGFILE, "r"); + if (!f) return 0; + while (fgets(line, STR_MAX, f)) { + if (!foundserver) + foundserver = parsephpstring("$imapServerAddress", line, server, serverlen); + } + return foundserver; + } + + int finduidgid(user, uid, gid) + char *user; + uid_t *uid; + gid_t *gid; + { + struct passwd *p; + + p = getpwnam(user); + if (!p) return 0; + *uid = p->pw_uid; + *gid = p->pw_gid; + return 1; + } + + int checkcredentials(imapserver, user, passwd) + char *imapserver; + char *user; + char *passwd; + { + /*DEBUG*/ + printf("checkcredentials: imap://%s:%s@%s\n", user, passwd, imapserver); + + /*FIXME*/ + printf("WARNING: credentials not checked\n"); + return 1; + } + + int parsephpstring(varname, line, dest, destlen) + char *varname; + char *line; + char *dest; + int destlen; + { + char *s; + int n; + + s = strstr(line, varname); + if (s == NULL) return 0; + + /* skip ahead to either ' or " */ + s += strcspn(line, "\"'\0\n") + 1; + n = strcspn(s, "\"'\0\n"); + if (n > destlen) n = destlen; + strncpy(dest, s, n); + return 1; + } + int rcexists(char* filter_file) { FILE *filter; + if (!validrcpath(filter_file)) { + return inerror(ERR_BAD_RC_PATH); + } + if(!(filter=fopen(filter_file,"r"))){ ! return ERR_NO_RC_FILE; /* silent */ } else { fclose(filter); *************** *** 115,119 **** int getrc(char* filter_file, char* temp_file, uid_t UID, gid_t GID) { ! copy_file(filter_file, temp_file); chown(temp_file, UID, GID); return 0; --- 277,290 ---- int getrc(char* filter_file, char* temp_file, uid_t UID, gid_t GID) { ! int result; ! ! if (!validrcpath(filter_file)) { ! return inerror(ERR_BAD_RC_PATH); ! } ! if (!validtmppath(temp_file)) { ! return inerror(ERR_BAD_TEMP_PATH); ! } ! result = copy_file(filter_file, temp_file); ! if (result) return result; chown(temp_file, UID, GID); return 0; *************** *** 123,130 **** { struct passwd* user_pass; ! user_pass = getpwnam(user); ! if (copy_file(temp_file, filter_file)) { ! return 10; } chown(filter_file, user_pass->pw_uid, user_pass->pw_gid); --- 294,310 ---- { struct passwd* user_pass; ! int result; ! if (!validrcpath(filter_file)) { ! return inerror(ERR_BAD_RC_PATH); ! } ! if (!validtmppath(temp_file)) { ! return inerror(ERR_BAD_TEMP_PATH); ! } ! ! user_pass = getpwnam(user); ! ! if (result = copy_file(temp_file, filter_file)) { ! return result; } chown(filter_file, user_pass->pw_uid, user_pass->pw_gid); *************** *** 140,149 **** if (!(infile=fopen(old_file,"r"))) { ! printf("Could not open %s", old_file); ! return 8; } if (!(outfile=fopen(new_file,"w"))) { ! printf("Could not open %s", new_file); ! return 9; } --- 320,329 ---- if (!(infile=fopen(old_file,"r"))) { ! fprintf(stderr, "Could not open %s\n", old_file); ! return ERR_COPY_CANT_OPEN_SRC; } if (!(outfile=fopen(new_file,"w"))) { ! fprintf(stderr, "Could not open %s\n", new_file); ! return ERR_COPY_CANT_OPEN_DEST; } *************** *** 155,156 **** --- 335,413 ---- return 0; } + + /* Path checking code. To limit the files that this command will touch, + * define RCHECK1,2,3 to be a path with a single '*', + * e.g. "/home/ * /.procmailrc" (take spaces out) + * + * At least one check must pass for a given file to be touched. + */ + + #ifndef RCCHECK1 + #define RCCHECK1 "*" + #endif + #ifndef RCCHECK2 + #define RCCHECK2 NULL + #endif + #ifndef RCCHECK3 + #define RCCHECK3 NULL + #endif + + static char *rcpathchecks[] = { + RCCHECK1, + RCCHECK2, + RCCHECK3, + NULL, + }; + + #ifndef TMPCHECK1 + #define TMPCHECK1 "*" + #endif + #ifndef TMPCHECK2 + #define TMPCHECK2 NULL + #endif + #ifndef TMPCHECK3 + #define TMPCHECK3 NULL + #endif + + static char *tmppathchecks[] = { + TMPCHECK1, + TMPCHECK2, + TMPCHECK3, + NULL, + }; + + int validrcpath(char* file) { + return validpath(file, rcpathchecks); + } + + int validtmppath(char* file) { + return validpath(file, tmppathchecks); + } + + int validpath(char *file, char **p) { + for (; *p; p++) { + if (checkpath(file, *p)) + return 1; + } + return 0; + } + + int checkpath(char* file, char* check) { + char *starpos, *suffix; + int filelen, starlen, minlen, suffixlen; + + starpos = strchr(check, '*'); + if (starpos == NULL) + starpos = strchr(check, '\0'); + starlen = starpos - check; + filelen = strlen(file); + minlen = starlen < filelen ? starlen : filelen; + suffix = starpos + 1; + suffixlen = strlen(suffix); + + return filelen >= suffixlen \ + && strncmp(file, check, minlen) == 0 \ + && strcmp(file+(filelen-suffixlen), suffix) == 0; + } + + |