WARNING to people installing ftpd: we have no idea what all the security problems with ftp might be, but here's at least one classic you should be sure is plugged before you go on the air. ftp -nv victim.com ftp>quote user anonymous ftp>cd ~root ftp>quote pass guest ftp>get /etc/passwd ftp>quit This file, /netlib/misc/ftpdiff, isn't even listed in the index because we don't want to encouraged novices to play with fire. differences between original ftpd.c taken off the Internet and the version running on research.att.com as of 20 May 1991 The changes were largely made by Bill Cheswick; a few were made by Eric Grosse. Others are free to use this; the same "as is" disclaimer you find in the original applies here. Again, we emphasize that if you aren't fully competent to check for security holes yourself, don't even think of touching this! *** ORIG/ftpd.c Fri Nov 3 17:34:28 1989 --- ftpd.c Thu Apr 4 16:37:17 1991 *************** *** 55,60 **** --- 55,62 ---- #include #include + #include "../logs.h" + /* * File containing login names * NOT to be used on this machine. *************** *** 83,92 **** jmp_buf errcatch, urgcatch; int logged_in; struct passwd *pw; ! int debug; int timeout = 900; /* timeout after 15 minutes of inactivity */ int maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */ - int logging; int guest; int type; int form; --- 85,93 ---- jmp_buf errcatch, urgcatch; int logged_in; struct passwd *pw; ! int debug=1; int timeout = 900; /* timeout after 15 minutes of inactivity */ int maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */ int guest; int type; int form; *************** *** 135,153 **** int addrlen, on = 1; char *cp; addrlen = sizeof (his_addr); if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { ! syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); exit(1); } addrlen = sizeof (ctrl_addr); if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { ! syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); exit(1); } data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); - debug = 0; - openlog("ftpd", LOG_PID, LOG_DAEMON); #ifdef SETPROCTITLE /* * Save start and extent of argv for setproctitle. --- 136,153 ---- int addrlen, on = 1; char *cp; + openlog("ftpd", LOG_PID, LOG_FTPD); addrlen = sizeof (his_addr); if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { ! syslog(LOG_ERR, "getpeername: %m"); exit(1); } addrlen = sizeof (ctrl_addr); if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { ! syslog(LOG_ERR, "getsockname: %m"); exit(1); } data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); #ifdef SETPROCTITLE /* * Save start and extent of argv for setproctitle. *************** *** 170,179 **** debug = 1; break; - case 'l': - logging = 1; - break; - case 't': timeout = atoi(++cp); if (maxtimeout < timeout) --- 170,175 ---- *************** *** 207,213 **** nextopt: argc--, argv++; } ! (void) freopen("/dev/null", "w", stderr); (void) signal(SIGPIPE, lostconn); (void) signal(SIGCHLD, SIG_IGN); if ((int)signal(SIGURG, myoob) < 0) --- 203,209 ---- nextopt: argc--, argv++; } ! (void) freopen("/usr/spool/ftpd.log", "w", stderr); (void) signal(SIGPIPE, lostconn); (void) signal(SIGCHLD, SIG_IGN); if ((int)signal(SIGURG, myoob) < 0) *************** *** 216,221 **** --- 212,218 ---- /* handle urgent data inline */ /* Sequent defines this, but it doesn't work */ #ifdef SO_OOBINLINE + if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) syslog(LOG_ERR, "setsockopt: %m"); #endif *************** *** 236,243 **** (void) gethostname(hostname, sizeof (hostname)); reply(220, "%s FTP server (%s) ready.", hostname, version); (void) setjmp(errcatch); ! for (;;) (void) yyparse(); /* NOTREACHED */ } --- 233,241 ---- (void) gethostname(hostname, sizeof (hostname)); reply(220, "%s FTP server (%s) ready.", hostname, version); (void) setjmp(errcatch); ! for (;;) { (void) yyparse(); + } /* NOTREACHED */ } *************** *** 244,251 **** lostconn() { ! if (debug) ! syslog(LOG_DEBUG, "lost connection"); dologout(-1); } --- 242,248 ---- lostconn() { ! syslog(LOG_INFO, "lost connection"); dologout(-1); } *************** *** 324,330 **** register char *cp; FILE *fd; char *shell; ! char line[BUFSIZ], *getusershell(); if (logged_in) { if (guest) { --- 321,327 ---- register char *cp; FILE *fd; char *shell; ! char line[BUFSIZ]; if (logged_in) { if (guest) { *************** *** 335,383 **** } guest = 0; if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { if ((pw = sgetpwnam("ftp")) != NULL) { guest = 1; - askpasswd = 1; reply(331, "Guest login ok, send ident as password."); ! } else ! reply(530, "User %s unknown.", name); ! return; ! } ! if (pw = sgetpwnam(name)) { ! if ((shell = pw->pw_shell) == NULL || *shell == 0) ! shell = "/bin/sh"; ! while ((cp = getusershell()) != NULL) ! if (strcmp(cp, shell) == 0) ! break; ! endusershell(); ! if (cp == NULL) { reply(530, "User %s access denied.", name); ! if (logging) ! syslog(LOG_NOTICE, ! "FTP LOGIN REFUSED FROM %s, %s", ! remotehost, name); pw = (struct passwd *) NULL; return; } ! if ((fd = fopen(FTPUSERS, "r")) != NULL) { ! while (fgets(line, sizeof (line), fd) != NULL) { ! if ((cp = index(line, '\n')) != NULL) ! *cp = '\0'; ! if (strcmp(line, name) == 0) { ! reply(530, "User %s access denied.", name); ! if (logging) ! syslog(LOG_NOTICE, ! "FTP LOGIN REFUSED FROM %s, %s", ! remotehost, name); ! pw = (struct passwd *) NULL; ! return; ! } ! } } ! (void) fclose(fd); ! } ! reply(331, "Password required for %s.", name); askpasswd = 1; /* * Delay before reading passwd after first failed --- 332,362 ---- } guest = 0; + askpasswd = 1; if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { if ((pw = sgetpwnam("ftp")) != NULL) { guest = 1; reply(331, "Guest login ok, send ident as password."); ! } else { reply(530, "User %s access denied.", name); ! syslog(LOG_NOTICE, "FTP LOGIN REFUSED FROM %s, %s", ! remotehost, name); pw = (struct passwd *) NULL; return; } ! }else if (strcmp(name, "netlib") == 0 ) { ! if ((pw = sgetpwnam("netlib")) != NULL) { ! guest = 1; ! reply(331, "Please send your email address, for bug reports and logs."); ! } else { ! reply(530, "User %s access denied.", name); ! syslog(LOG_NOTICE, "FTP LOGIN REFUSED FROM %s, %s", ! remotehost, name); ! pw = (struct passwd *) NULL; ! return; } ! } else ! reply(331, "Password required for %s.", name); askpasswd = 1; /* * Delay before reading passwd after first failed *************** *** 413,435 **** } askpasswd = 0; if (!guest) { /* "ftp" is only account allowed no password */ ! if (pw == NULL) ! salt = "xx"; ! else ! salt = pw->pw_passwd; ! xpasswd = crypt(passwd, salt); ! /* The strcmp does not catch null passwords! */ ! if (pw == NULL || *pw->pw_passwd == '\0' || ! strcmp(xpasswd, pw->pw_passwd)) { ! reply(530, "Login incorrect."); ! pw = NULL; ! if (login_attempts++ >= 5) { ! syslog(LOG_NOTICE, ! "repeated login failures from %s", remotehost); ! exit(0); ! } ! return; } } login_attempts = 0; /* this time successful */ --- 392,403 ---- } askpasswd = 0; if (!guest) { /* "ftp" is only account allowed no password */ ! reply(530, "Login incorrect."); ! pw = NULL; ! if (login_attempts++ >= 5) { ! syslog(LOG_NOTICE, "repeated login failures from %s", remotehost); ! exit(0); } } login_attempts = 0; /* this time successful */ *************** *** 451,463 **** reply(550, "Can't set guest privileges."); goto bad; } ! } 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."); --- 419,425 ---- reply(550, "Can't set guest privileges."); goto bad; } ! syslog(LOG_INFO, "CHROOT %s", pw->pw_dir); } if (seteuid((uid_t)pw->pw_uid) < 0) { reply(550, "Can't set uid."); *************** *** 471,488 **** sizeof(": anonymous/"), passwd); setproctitle(proctitle); #endif /* SETPROCTITLE */ ! if (logging) ! syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", ! remotehost, passwd); ! } else { ! reply(230, "User %s logged in.", pw->pw_name); ! #ifdef SETPROCTITLE ! sprintf(proctitle, "%s: %s", remotehost, pw->pw_name); ! setproctitle(proctitle); ! #endif /* SETPROCTITLE */ ! if (logging) ! syslog(LOG_INFO, "FTP LOGIN FROM %s, %s", ! remotehost, pw->pw_name); } home = pw->pw_dir; /* home dir for globbing */ (void) umask(defumask); --- 433,440 ---- sizeof(": anonymous/"), passwd); setproctitle(proctitle); #endif /* SETPROCTITLE */ ! syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", ! remotehost, passwd); } home = pw->pw_dir; /* home dir for globbing */ (void) umask(defumask); *************** *** 492,507 **** end_login(); } retrieve(cmd, name) char *cmd, *name; { FILE *fin, *dout; struct stat st; int (*closefunc)(); if (cmd == 0) { ! fin = fopen(name, "r"), closefunc = fclose; ! st.st_size = 0; } else { char line[BUFSIZ]; --- 444,482 ---- end_login(); } + char * + absolutepath(file) + char *file; + { + static char path[MAXPATHLEN]; + extern char *getwd(); + + if (getwd(path) == NULL) + strcpy(path, "??/"); + strcat(path, "/"); + strcat(path, file); + return path; + } + retrieve(cmd, name) char *cmd, *name; { + int n; FILE *fin, *dout; struct stat st; int (*closefunc)(); if (cmd == 0) { ! n = strlen(name); ! if( type == TYPE_A && ! ((n>=2 && !strcmp(name+n-2,".Z")) || ! (n>=4 && !strcmp(name+n-4,".tar")) )){ ! reply(550, "set BINARY mode before retrieving %s", name); ! return; ! }else{ ! fin = fopen(name, "r"), closefunc = fclose; ! st.st_size = 0; ! } } else { char line[BUFSIZ]; *************** *** 524,529 **** --- 499,506 ---- if (dout == NULL) goto done; send_data(fin, dout, st.st_blksize); + if (cmd == 0) + syslog(LOG_INFO, "Sent file %s", absolutepath(name)); (void) fclose(dout); data = -1; pdata = -1; *************** *** 559,564 **** --- 536,543 ---- name); else reply(226, "Transfer complete."); + syslog(LOG_INFO, "Received %d bytes in file %s", + byte_count, absolutepath(name)); } (void) fclose(din); data = -1; *************** *** 572,577 **** --- 551,557 ---- char *mode; { int s, on = 1; + FILE *d; if (data >= 0) return (fdopen(data, mode)); *************** *** 587,593 **** if (bind(s, (struct sockaddr *)&data_source, sizeof (data_source)) < 0) goto bad; (void) seteuid((uid_t)pw->pw_uid); ! return (fdopen(s, mode)); bad: (void) seteuid((uid_t)pw->pw_uid); (void) close(s); --- 567,574 ---- if (bind(s, (struct sockaddr *)&data_source, sizeof (data_source)) < 0) goto bad; (void) seteuid((uid_t)pw->pw_uid); ! d = fdopen(s, mode); ! return (d); bad: (void) seteuid((uid_t)pw->pw_uid); (void) close(s); *************** *** 702,707 **** --- 683,689 ---- if (ferror(outstr)) goto data_err; reply(226, "Transfer complete."); + syslog(LOG_INFO, "Sent %d bytes", byte_count); return; case TYPE_I: *************** *** 724,729 **** --- 706,712 ---- goto data_err; } reply(226, "Transfer complete."); + syslog(LOG_INFO, "Sent %d bytes", byte_count); return; default: transflag = 0; *************** *** 918,932 **** int n; char *fmt; { ! printf("%d ", n); ! printf(fmt, p0, p1, p2, p3, p4, p5); ! printf("\r\n"); ! (void)fflush(stdout); ! if (debug) { ! syslog(LOG_DEBUG, "<--- %d ", n); ! syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5); } - } /* VARARGS2 */ lreply(n, fmt, p0, p1, p2, p3, p4, p5) --- 901,914 ---- int n; char *fmt; { ! char buf1[1000], buf2[1000], buf3[1000]; ! ! sprintf(buf1, fmt, p0, p1, p2, p3, p4, p5); ! sprintf(buf2, "%d %s", n, buf1); ! sprintf(buf3, "%s\r\n", buf2); ! write(1, buf3, strlen(buf3)); ! syslog(LOG_INFO, "<--- %s", buf2); } /* VARARGS2 */ lreply(n, fmt, p0, p1, p2, p3, p4, p5) *************** *** 933,946 **** int n; char *fmt; { ! printf("%d- ", n); ! printf(fmt, p0, p1, p2, p3, p4, p5); ! printf("\r\n"); ! (void)fflush(stdout); ! if (debug) { ! syslog(LOG_DEBUG, "<--- %d- ", n); ! syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5); ! } } ack(s) --- 915,927 ---- int n; char *fmt; { ! char buf1[1000], buf2[1000], buf3[1000]; ! ! sprintf(buf1, fmt, p0, p1, p2, p3, p4, p5); ! sprintf(buf2, "%d- %s", n, buf1); ! sprintf(buf3, "%d %s\r\n", n, buf2); ! write(1, buf2, strlen(buf2)); ! syslog(LOG_INFO, "<--- %s", buf2); } ack(s) *************** *** 1069,1079 **** setproctitle(proctitle); #endif /* SETPROCTITLE */ ! if (logging) { ! t = time((time_t *) 0); ! syslog(LOG_INFO, "connection from %s at %s", ! remotehost, ctime(&t)); ! } } /* --- 1050,1057 ---- setproctitle(proctitle); #endif /* SETPROCTITLE */ ! t = time((time_t *) 0); ! syslog(LOG_INFO, "connection from %s at %s", remotehost, ctime(&t)); } /* *************** *** 1087,1092 **** --- 1065,1071 ---- (void) seteuid((uid_t)0); logwtmp(ttyline, "", ""); } + syslog(LOG_INFO, "Logout, status %d", status); /* beware of flushing buffers after a SIGPIPE */ _exit(status); } *************** *** 1323,1331 **** reply(550, "No files found."); else if (ferror(dout) != 0) perror_reply(550, "Data connection"); ! else reply(226, "Transfer complete."); ! transflag = 0; if (dout != NULL) (void) fclose(dout); --- 1302,1311 ---- reply(550, "No files found."); else if (ferror(dout) != 0) perror_reply(550, "Data connection"); ! else { reply(226, "Transfer complete."); ! syslog(LOG_INFO, "Sent %d bytes", byte_count); ! } transflag = 0; if (dout != NULL) (void) fclose(dout); .