? .ftpd.c.swp ? bsdftpd-patch Index: Makefile =================================================================== RCS file: /cvs/privman/privman/clients/ftpd-bsd/Makefile,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- Makefile 2002/08/26 13:48:11 1.1 +++ Makefile 2002/08/27 19:59:20 1.2 @@ -6,7 +6,7 @@ YACC = bison -y CFLAGS = $(OPT_CFLAGS) $(EXTRA_CFLAGS) -DTCPWRAPPERS -DUSE_PAM -DAUTO_UNCOMPRESS -DINTERNAL_LS -LIBS = $(EXTRA_LIBS) -lutil -lwrap -lnsl -lcrypt -lpam -ldl +LIBS = $(EXTRA_LIBS) -lutil -lwrap -lnsl -lcrypt -lprivman -lpam -ldl -lpam_misc ## If you prefer shadow password support, try this: #CFLAGS = $(OPT_CFLAGS) $(EXTRA_CFLAGS) -DTCPWRAPPERS -DUSE_SHADOW -DAUTO_UNCOMPRESS -DINTERNAL_LS #LIBS = $(EXTRA_LIBS) -lutil -lwrap -lnsl -lcrypt Index: ftpd.c =================================================================== RCS file: /cvs/privman/privman/clients/ftpd-bsd/ftpd.c,v retrieving revision 1.1 retrieving revision 1.3 diff -u -r1.1 -r1.3 --- ftpd.c 2002/08/26 13:48:11 1.1 +++ ftpd.c 2002/08/27 19:59:20 1.3 @@ -131,6 +131,8 @@ #include #endif /* not ORIGINAL_SOURCE */ +#include + #if defined(TCPWRAPPERS) #include #endif /* TCPWRAPPERS */ @@ -182,7 +184,7 @@ int daemon_mode = 0; int data; jmp_buf errcatch, urgcatch; -int logged_in; +int logged_in = 0; struct passwd *pw; int debug = 0; int timeout = 900; /* timeout after 15 minutes of inactivity */ @@ -320,6 +322,59 @@ void logxfer __P((char *, off_t, time_t)); +void run_as_user(const char *arg) +{ + static char homedir[MAXPATHLEN]; + const char *s_name, *s_uid, *s_gid, *s_dir, *s_shell; + int uid; + + logged_in = 1; + /* Arg of format "name:*:uid:gid::dir:/bin/false" + * Extract the uid. If I can look that up, do so. + * Else, create a fake pw entry instead. + */ + + s_name = arg; + s_uid = index(s_name, ':') + 3; /* skip ":*:" */ + + uid = atoi(s_uid); + pw = getpwuid(uid); + + if (pw == NULL) { + size_t len; + + syslog(LOG_INFO, "making up pwent info."); + + s_gid = index(s_uid, ':') + 1; + s_dir = index(s_gid, ':') + 2; + pw = (struct passwd *)malloc(sizeof(struct passwd)); + + len = s_uid - s_name - 3; + pw->pw_name = (char *)malloc(len + 1); + memcpy(pw->pw_name, s_name, len); + pw->pw_name[len] = '\0'; + + pw->pw_passwd = 0; + pw->pw_uid = uid; + pw->pw_gid = atoi(s_gid); + pw->pw_gecos = 0; + + len = s_shell - s_dir - 1; + pw->pw_dir = (char *)malloc(len + 1); + memcpy(pw->pw_dir, s_dir, len); + pw->pw_dir[len] = '\0'; + + pw->pw_shell = "/bin/false"; + } + + if (chdir(pw->pw_dir) < 0) { + syslog(LOG_ERR, "chdir(%s) failed. Using '/'", pw->pw_dir); + chdir("/"); + } + if (getcwd(homedir, MAXPATHLEN) != NULL) + setenv("HOME", homedir, 1); +} + static char * curdir() { @@ -363,6 +418,8 @@ FILE *fp; struct hostent *hp; + priv_init("ftpd"); + tzset(); /* in case no timezone database in ~ftp */ while ((ch = getopt(argc, argv, argstr)) != -1) { @@ -471,10 +528,12 @@ /* * Detach from parent. */ - if (daemon(1, 1) < 0) { +#if 0 + if (priv_daemon(1, 1) < 0) { syslog(LOG_ERR, "failed to become a daemon"); exit(1); } +#endif (void) signal(SIGCHLD, reapchild); /* * Get port number for ftp/tcp. @@ -513,12 +572,13 @@ break; } #ifdef ORIGINAL_SOURCE - if (bind(ctl_sock, (struct sockaddr *)&server_addr, - server_addr.su_len)) { + if (priv_bind(ctl_sock, (struct sockaddr *)&server_addr, + server_addr.su_len)) #else - if (bind(ctl_sock, (struct sockaddr *)&server_addr, - SA_LEN((struct sockaddr *)&server_addr))) { + if (priv_bind(ctl_sock, (struct sockaddr *)&server_addr, + sizeof(server_addr))) #endif /* ORIGINAL_SOURCE */ + { syslog(LOG_ERR, "control bind: %m"); exit(1); } @@ -527,7 +587,7 @@ exit(1); } /* Stash pid in pidfile */ - if ((fp = fopen(_PATH_FTPDPID, "w")) == NULL) + if ((fp = priv_fopen(_PATH_FTPDPID, "w")) == NULL) syslog(LOG_ERR, "can't open %s: %m", _PATH_FTPDPID); else { fprintf(fp, "%d\n", getpid()); @@ -692,24 +752,25 @@ getnameinfo((struct sockaddr *)&ctrl_addr, ctrl_addr.su_len, dhostname, sizeof(dhostname), NULL, 0, 0); #else - getnameinfo((struct sockaddr *)&ctrl_addr, - SA_LEN((struct sockaddr *)&ctrl_addr), + getnameinfo((struct sockaddr *)&ctrl_addr, sizeof(ctrl_addr), dhostname, sizeof(dhostname), NULL, 0, 0); #endif /* ORIGINAL_SOURCE */ } + if (!logged_in) { #ifdef ORIGINAL_SOURCE - reply(220, "%s FTP server (%s) ready.", - (multihome ? dhostname : hostname), version); + reply(220, "%s FTP server (%s) ready.", + (multihome ? dhostname : hostname), version); #else - if (!quiet) { - reply(220, "%s FTP server (%s) ready.", - (multihome ? dhostname : hostname), version); - } else { - reply(220, "%s FTP server ready.", - (multihome ? dhostname : hostname)); - } + if (!quiet) { + reply(220, "%s FTP server (%s) ready.", + (multihome ? dhostname : hostname), version); + } else { + reply(220, "%s FTP server ready.", + (multihome ? dhostname : hostname)); + } #endif /* ORIGINAL_SOURCE */ + } (void) setjmp(errcatch); for (;;) (void) yyparse(); @@ -940,7 +1001,6 @@ sigset_t allsigs; sigfillset (&allsigs); sigprocmask (SIG_BLOCK, &allsigs, NULL); - (void) seteuid((uid_t)0); if (logged_in) { ftpdlogwtmp(ttyline, "", ""); if (doutmp) @@ -1008,8 +1068,8 @@ { int rval; FILE *fp; - static char homedir[MAXPATHLEN]; char rootdir[MAXPATHLEN]; + char *new_root = 0; sigset_t allsigs; #ifndef ORIGINAL_SOURCE #ifdef USE_PAM @@ -1054,24 +1114,24 @@ rval = 0; #else conv.appdata_ptr = passwd; - if (pam_start ("ftp", curname, &conv, &pamh) != PAM_SUCCESS) + if (priv_pam_start("ftp", curname, &conv, &pamh) != PAM_SUCCESS) goto pam_fail; - pam_set_item (pamh, PAM_RHOST, remotehost); - pam_fail_delay (pamh, 2000000); - if (pam_authenticate (pamh, 0) != PAM_SUCCESS) + priv_pam_set_item (pamh, PAM_RHOST, remotehost); + priv_pam_fail_delay (pamh, 2000000); + if (priv_pam_authenticate (pamh, 0) != PAM_SUCCESS) goto pam_fail; - if (pam_acct_mgmt (pamh, 0) != PAM_SUCCESS) + if (priv_pam_acct_mgmt (pamh, 0) != PAM_SUCCESS) goto pam_fail; - if (pam_setcred (pamh, PAM_ESTABLISH_CRED) != PAM_SUCCESS) + if (priv_pam_setcred (pamh, PAM_ESTABLISH_CRED) != PAM_SUCCESS) goto pam_fail; /* User is authenticated */ rval = 0; - pam_end (pamh, 0); + priv_pam_end (pamh, 0); goto skip; pam_fail: rval = 1; - pam_end (pamh, 0); + priv_pam_end (pamh, 0); goto skip; #endif /* defined(ORIGINAL_SOURCE) || !defined (USE_PAM) */ @@ -1103,11 +1163,13 @@ fatal("Out of memory"); } login_attempts = 0; /* this time successful */ +#if 0 if (setegid((gid_t)pw->pw_gid) < 0) { reply(550, "Can't set gid."); return; } (void) initgroups(pw->pw_name, pw->pw_gid); +#endif /* open wtmp before chroot */ ftpdlogwtmp(ttyline, pw->pw_name, remotehost); @@ -1137,12 +1199,14 @@ /* open stats file before chroot */ if (guest && (stats == 1) && (statfd < 0)) - if ((statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND)) < 0) + if ((statfd = priv_open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND)) < 0) stats = 0; logged_in = 1; + /* Drop to here on rerunas. */ dochroot = checkuser(_PATH_FTPCHROOT, pw->pw_name); + if (guest || dochroot) { if (multihome && guest) { struct stat ts; @@ -1157,89 +1221,57 @@ } else strcpy(rootdir, pw->pw_dir); } - if (guest) { - /* - * We MUST do a chdir() after the chroot. Otherwise - * the old current directory will be accessible as "." - * outside the new root! - */ - if (chroot(rootdir) < 0 || chdir("/") < 0) { - reply(550, "Can't set guest privileges."); - goto bad; - } - strcpy(pw->pw_dir, "/"); - setenv("HOME", "/", 1); - } else if (dochroot) { - if (chroot(rootdir) < 0 || chdir("/") < 0) { - reply(550, "Can't change root."); - goto bad; - } - strcpy(pw->pw_dir, "/"); - setenv("HOME", "/", 1); - } else if (chdir(pw->pw_dir) < 0) { - if (chdir("/") < 0) { - reply(530, "User %s: can't change directory to %s.", - pw->pw_name, pw->pw_dir); - goto bad; - } else - lreply(230, "No directory! Logging in with home=/"); - } - if (seteuid((uid_t)pw->pw_uid) < 0) { - reply(550, "Can't set uid."); - goto bad; - } - sigfillset(&allsigs); - sigprocmask(SIG_UNBLOCK,&allsigs,NULL); - - /* - * Set home directory so that use of ~ (tilde) works correctly. - */ - if (getcwd(homedir, MAXPATHLEN) != NULL) - setenv("HOME", homedir, 1); - /* - * Display a login message, if it exists. - * N.B. reply(230,) must follow the message. - */ - if ((fp = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) { - char *cp, line[LINE_MAX]; - - while (fgets(line, sizeof(line), fp) != NULL) { - if ((cp = strchr(line, '\n')) != NULL) - *cp = '\0'; - lreply(230, "%s", line); - } - (void) fflush(stdout); - (void) fclose(fp); - } - if (guest) { - if (ident != NULL) - free(ident); - ident = strdup(passwd); - if (ident == (char *)NULL) - fatal("Ran out of memory."); - reply(230, "Guest login ok, access restrictions apply."); -#ifdef HASSETPROCTITLE - snprintf(proctitle, sizeof(proctitle), - "%s: anonymous/%.*s", remotehost, - (int)(sizeof(proctitle) - sizeof(remotehost) - - sizeof(": anonymous/")), passwd); - setproctitle("%s", proctitle); -#endif /* HASSETPROCTITLE */ - if (logging) - syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", - remotehost, passwd); - } else { - reply(230, "User %s logged in.", pw->pw_name); -#ifdef HASSETPROCTITLE - snprintf(proctitle, sizeof(proctitle), - "%s: %s", remotehost, pw->pw_name); - setproctitle("%s", proctitle); -#endif /* HASSETPROCTITLE */ - if (logging) - syslog(LOG_INFO, "FTP LOGIN FROM %s as %s", - remotehost, pw->pw_name); - } + if (guest && logging) { + syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", + remotehost, passwd); + } else if (logging) { + syslog(LOG_INFO, "FTP LOGIN FROM %s as %s", + remotehost, pw->pw_name); + } + /* + * Display a login message, if it exists. + * N.B. reply(230,) must follow the message. + */ + if ((fp = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) { + char *cp, line[LINE_MAX]; + + while (fgets(line, sizeof(line), fp) != NULL) { + if ((cp = strchr(line, '\n')) != NULL) + *cp = '\0'; + lreply(230, "%s", line); + } + (void) fflush(stdout); + (void) fclose(fp); + } + + if (guest) { + reply(230, "Guest login ok, access restrictions apply."); + if (logging) + syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", + remotehost, passwd); + } else { + reply(230, "User %s logged in.", pw->pw_name); + } + + { + char args[1024]; + /* Hack: + * restart execution from "priv_init". The function listed + * below will run first, allowing us to muck with the global + * environment. + */ + syslog(LOG_INFO, "rerunning. user = %s, root = %s", + pw->pw_name, new_root); + + snprintf(args, sizeof(args)-1, "%s:*:%d:%d::%s:/bin/false", + pw->pw_name, pw->pw_uid, pw->pw_gid, pw->pw_dir); + + priv_rerunas(run_as_user, args, pw->pw_name, new_root); + syslog(LOG_ERR, "call to \"priv_rerunas\" failed."); + reply(530, "Can't set privileges."); + goto bad; + } (void) umask(defumask); return; bad: @@ -1436,7 +1468,6 @@ return (fdopen(data, mode)); sigfillset(&allsigs); sigprocmask (SIG_BLOCK, &allsigs, NULL); - (void) seteuid((uid_t)0); s = socket(ctrl_addr.su_family, SOCK_STREAM, 0); if (s < 0) goto bad; @@ -1448,19 +1479,18 @@ data_source.su_port = htons(20); /* ftp-data port */ for (tries = 1; ; tries++) { #ifdef ORIGINAL_SOURCE - if (bind(s, (struct sockaddr *)&data_source, + if (priv_bind(s, (struct sockaddr *)&data_source, data_source.su_len) >= 0) break; #else - if (bind(s, (struct sockaddr *)&data_source, - SA_LEN((struct sockaddr *)&data_source)) >= 0) + if (priv_bind(s, (struct sockaddr *)&data_source, + sizeof(data_source)) >= 0) break; #endif /* ORIGINAL_SOURCE */ if (errno != EADDRINUSE || tries > 10) goto bad; sleep(tries); } - (void) seteuid((uid_t)pw->pw_uid); sigfillset(&allsigs); sigprocmask (SIG_UNBLOCK, &allsigs, NULL); @@ -1493,7 +1523,6 @@ bad: /* Return the real value of errno (close may change it) */ t = errno; - (void) seteuid((uid_t)pw->pw_uid); sigfillset (&allsigs); sigprocmask (SIG_UNBLOCK, &allsigs, NULL); (void) close(s); @@ -1599,8 +1628,7 @@ hbuf, sizeof(hbuf), pbuf, sizeof(pbuf), NI_NUMERICHOST | NI_NUMERICSERV); #else - getnameinfo((struct sockaddr *)&data_source, - SA_LEN((struct sockaddr *)&data_source), + getnameinfo((struct sockaddr *)&data_source,sizeof(data_source), hbuf, sizeof(hbuf), pbuf, sizeof(pbuf), NI_NUMERICHOST | NI_NUMERICSERV); #endif /* ORIGINAL_SOURCE */ @@ -1651,7 +1679,7 @@ data_dest.su_len) < 0) { #else while (connect(data, (struct sockaddr *)&data_dest, - SA_LEN((struct sockaddr *)&data_dest)) < 0) { + sizeof(data_dest)) < 0) { #endif /* ORIGINAL_SOURCE */ if (errno == EADDRINUSE && retry < swaitmax) { sleep((unsigned) swaitint); @@ -1955,8 +1983,7 @@ getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST); #else - getnameinfo((struct sockaddr *)&his_addr, - SA_LEN((struct sockaddr *)&his_addr), + getnameinfo((struct sockaddr *)&his_addr, sizeof(his_addr), hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST); #endif /* ORIGINAL_SOURCE */ printf(" Connected to %s", remotehost); @@ -2065,7 +2092,7 @@ NI_NUMERICHOST) == 0) { #else if (getnameinfo((struct sockaddr *)su, - SA_LEN((struct sockaddr *)su), + sizeof(struct sockaddr_in), hbuf, sizeof(hbuf), pbuf, sizeof(pbuf), NI_NUMERICHOST) == 0) { #endif /* ORIGINAL_SOURCE */ @@ -2316,7 +2343,7 @@ #ifdef ORIGINAL_SOURCE getnameinfo(sa, sa->sa_len, hbuf, sizeof(hbuf), NULL, 0, 0); #else - getnameinfo(sa, SA_LEN(sa), hbuf, sizeof(hbuf), NULL, 0, 0); + getnameinfo(sa, sizeof(struct sockaddr_in), hbuf, sizeof(hbuf), NULL, 0, 0); #endif /* ORIGINAL_SOURCE */ (void) strncpy(remotehost, hbuf, sizeof(remotehost)-1); @@ -2345,7 +2372,6 @@ if (logged_in) { sigfillset(&allsigs); sigprocmask(SIG_BLOCK, &allsigs, NULL); - (void) seteuid((uid_t)0); ftpdlogwtmp(ttyline, "", ""); if (doutmp) logout(utmp.ut_line); @@ -2451,7 +2477,7 @@ pasv_addr.su_len) < 0) #else if (bind(pdata, (struct sockaddr *)&pasv_addr, - SA_LEN((struct sockaddr *)&pasv_addr)) < 0) + sizeof(pasv_addr)) < 0) #endif /* ORIGINAL_SOURCE */ goto pasv_error; @@ -2468,7 +2494,6 @@ return; pasv_error: - (void) seteuid((uid_t)pw->pw_uid); (void) close(pdata); pdata = -1; perror_reply(425, "Can't open passive connection"); @@ -2557,21 +2582,18 @@ pasv_addr = ctrl_addr; pasv_addr.su_port = 0; - (void) seteuid((uid_t) 0); #ifdef ORIGINAL_SOURCE - if (bind(pdata, (struct sockaddr *) &pasv_addr, pasv_addr.su_len) < 0) { + if (bind(pdata, (struct sockaddr *) &pasv_addr, pasv_addr.su_len) < 0) #else - if (bind(pdata, (struct sockaddr *) &pasv_addr, - SA_LEN((struct sockaddr *) &pasv_addr)) < 0) { + if (bind(pdata, (struct sockaddr *) &pasv_addr,sizeof(pasv_addr)) < 0) #endif /* ORIGINAL_SOURCE */ - (void) seteuid((uid_t) pw->pw_uid); + { goto pasv_error; } - (void) seteuid((uid_t) pw->pw_uid); #ifdef ORIGINAL_SOURCE len = pasv_addr.su_len; #else - len = SA_LEN((struct sockaddr *)&pasv_addr); + len = sizeof(pasv_addr); #endif /* ORIGINAL_SOURCE */ if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) goto pasv_error; Index: logutmp.c =================================================================== RCS file: /cvs/privman/privman/clients/ftpd-bsd/logutmp.c,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- logutmp.c 2002/08/26 13:48:11 1.1 +++ logutmp.c 2002/08/26 13:59:20 1.2 @@ -42,6 +42,7 @@ #include #include #include +#include typedef struct utmp UTMP; Index: logwtmp.c =================================================================== RCS file: /cvs/privman/privman/clients/ftpd-bsd/logwtmp.c,v retrieving revision 1.1 retrieving revision 1.3 diff -u -r1.1 -r1.3 --- logwtmp.c 2002/08/26 13:48:11 1.1 +++ logwtmp.c 2002/08/27 19:59:20 1.3 @@ -51,6 +51,8 @@ #include #include #include +#include +#include #include #include "extern.h" @@ -69,7 +71,7 @@ struct utmp ut; struct stat buf; - if (fd < 0 && (fd = open(_PATH_WTMP, O_WRONLY|O_APPEND, 0)) < 0) + if (fd < 0 && (fd = priv_open(_PATH_WTMP, O_WRONLY|O_APPEND, 0)) < 0) return; if (fstat(fd, &buf) == 0) { #ifndef ORIGINAL_SOURCE