linux-ftpd-0.17/ 40700 144 144 0 7141141771 12733 5ustar dhollandpeoplelinux-ftpd-0.17/ftpd/ 40700 144 144 0 7141141771 13670 5ustar dhollandpeoplelinux-ftpd-0.17/ftpd/.cvsignore100644 144 144 16 6774151645 15750 0ustar dhollandpeopleftpcmd.c ftpd linux-ftpd-0.17/ftpd/Makefile100644 144 144 1627 7024761712 15450 0ustar dhollandpeopleinclude ../MCONFIG CFLAGS+=-I../support LIBS+=-L../support -lsupport OBJS=ftpd.o ftpcmd.o logutmp.o logwtmp.o popen.o ## our internal version of ls. # LSDIR = ${.CURDIR}/../../bin/ls # .PATH: ${LSDIR} # SRCS += ls.c cmp.c print.c stat_flags.c util.c # CFLAGS += -I${LSDIR} ## (if tcp_wrappers) # CFLAGS+=-DTCPWRAPPERS # LDADD+= -lwrap all: ftpd %.o: %.c $(CC) $(CFLAGS) -DHASSETPROCTITLE $< -c ftpcmd.c: %.c: %.y $(YACC) $< -mv -f y.tab.c $@ ftpd: $(OBJS) $(CC) $(LDFLAGS) $^ $(LIBS) -o $@ install: ftpd install -s -m$(DAEMONMODE) ftpd $(INSTALLROOT)$(SBINDIR)/in.ftpd install -m$(MANMODE) ftpusers.5 $(INSTALLROOT)$(MANDIR)/man5/ftpusers.5 install -m$(MANMODE) ftpd.8 $(INSTALLROOT)$(MANDIR)/man8/in.ftpd.8 ln -sf in.ftpd.8 $(INSTALLROOT)$(MANDIR)/man8/ftpd.8 clean: rm -f *.o ftpcmd.c y.tab.h ftpd ftpd.o: pathnames.h ../version.h $(OBJS): extern.h ../support/daemon.h ../support/setproctitle.h linux-ftpd-0.17/ftpd/README100644 144 144 2706 6743503426 14672 0ustar dhollandpeopleThis source was retrieved from ftp.openbsd.org on Thu Jul 15 19:38:16 EDT 1999 ---- To do a new import: 1. go to ftp.openbsd.org and get sources from libexec/ftpd. The files are: Makefile ftpcmd.y ftpd.c logwtmp.c popen.c extern.h ftpd.8 logutmp.c pathnames.h If OpenBSD has added more files, be sure to get them and update this list. Put the files in a temp directory. 2. Update this file as necessary (including particularly the date at the top) and copy it into the temp directory under the name README. 3. Make sure CVSROOT is set correctly. 4. Do the following: cvs import -m "Import from OpenBSD " linux-ftpd/ftpd \ openbsd openbsd- where should represent the OpenBSD version, either a release number or the date of a -current in the form 19990630, and should hold the same information in a human-readable format. Note that you can't use . in cvs tags; use _ instead. E.g.: cvs import -m "Import from OpenBSD 1.4" linux-ftpd/ftpd openbsd openbsd-1_4 or cvs import -m "Import from OpenBSD June 30, 1999" linux-ftpd/ftpd \ openbsd openbsd-19990630 5. If there are merge conflicts cvs will tell you to do a cvs checkout with some -j flags. Do that in a scratch tree, clean up the conflicts, and commit the results. Then update your main working tree, build, and test. You may want to manually check any OpenBSD changes to see if they have added anything that won't work in Linux. linux-ftpd-0.17/ftpd/extern.h100644 144 144 6200 6743503426 15461 0ustar dhollandpeople/*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)extern.h 8.2 (Berkeley) 4/4/94 * NetBSD: extern.h,v 1.2 1995/04/11 02:44:49 cgd Exp * $Id: extern.h,v 1.5 1999/07/16 01:12:54 dholland Exp $ */ void blkfree __P((char **)); char **copyblk __P((char **)); void cwd __P((const char *)); void delete __P((char *)); void dologout __P((int)); void fatal __P((const char *)); int ftpd_pclose __P((FILE *)); FILE *ftpd_popen __P((char *, const char *)); char *ftpd_getline __P((char *, int, FILE *)); void ftpdlogwtmp __P((const char *, const char *, const char *)); void lreply __P((int, const char *, ...)); void makedir __P((char *)); void nack __P((const char *)); void pass __P((char *)); void passive __P((void)); void perror_reply __P((int, const char *)); void pwd __P((void)); void removedir __P((char *)); void renamecmd __P((char *, char *)); char *renamefrom __P((char *)); void reply __P((int, const char *, ...)); void retrieve __P((const char *, const char *)); void send_file_list __P((const char *)); void statcmd __P((void)); void statfilecmd __P((char *)); void store __P((const char *, const char *, int)); void upper __P((char *)); void user __P((char *)); void yyerror __P((char *)); void toolong __P((int)); int yyparse __P((void)); struct utmp; void login(const struct utmp *); int logout(const char *line); #ifdef __linux__ #include "daemon.h" #include "setproctitle.h" #endif linux-ftpd-0.17/ftpd/ftpcmd.y100644 144 144 65105 6777524454 15516 0ustar dhollandpeople/* * Copyright (c) 1985, 1988, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94 * NetBSD: ftpcmd.y,v 1.7 1996/04/08 19:03:11 jtc Exp * OpenBSD: ftpcmd.y,v 1.16 1998/05/22 06:46:09 deraadt Exp */ /* * Grammar for FTP commands. * See RFC 959. */ %{ char ftpcmd_rcsid[] = "$Id: ftpcmd.y,v 1.11 1999/10/09 02:32:12 dholland Exp $"; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef __linux__ #include #else #define TM_YEAR_BASE 1900 #endif #include "extern.h" extern struct sockaddr_in data_dest; extern int logged_in; extern struct passwd *pw; extern int guest; extern int logging; extern int type; extern int form; extern int debug; extern int timeout; extern int maxtimeout; extern int pdata; extern char hostname[], remotehost[]; extern char proctitle[]; extern int usedefault; extern int transflag; extern char tmpline[]; extern int portcheck; extern struct sockaddr_in his_addr; off_t restart_point; static int cmd_type; static int cmd_form; static int cmd_bytesz; char cbuf[512]; char *fromname; struct tab; static int yylex __P((void)); static void sizecmd __P((char *)); static void help __P((struct tab *, char *)); extern struct tab cmdtab[]; extern struct tab sitetab[]; %} %union { int i; char *s; } %token A B C E F I L N P R S T SP CRLF COMMA USER PASS ACCT REIN QUIT PORT PASV TYPE STRU MODE RETR STOR APPE MLFL MAIL MSND MSOM MSAM MRSQ MRCP ALLO REST RNFR RNTO ABOR DELE CWD LIST NLST SITE STAT HELP NOOP MKD RMD PWD CDUP STOU SMNT SYST SIZE MDTM UMASK IDLE CHMOD LEXERR %token STRING %token NUMBER %type check_login octal_number byte_size %type struct_code mode_code type_code form_code %type pathstring pathname password username %type host_port %start cmd_list %% cmd_list : /* empty */ | cmd_list cmd { fromname = (char *) 0; restart_point = (off_t) 0; } | cmd_list rcmd ; cmd : USER SP username CRLF { user($3); free($3); } | PASS SP password CRLF { pass($3); memset($3, 0, strlen($3)); free($3); } | PORT check_login SP host_port CRLF { if ($2) { if ($4) { usedefault = 1; reply(500, "Illegal PORT rejected (range errors)."); } else if (portcheck && ntohs(data_dest.sin_port) < IPPORT_RESERVED) { usedefault = 1; reply(500, "Illegal PORT rejected (reserved port)."); } else if (portcheck && memcmp(&data_dest.sin_addr, &his_addr.sin_addr, sizeof data_dest.sin_addr)) { usedefault = 1; reply(500, "Illegal PORT rejected (address wrong)."); } else { usedefault = 0; if (pdata >= 0) { (void) close(pdata); pdata = -1; } reply(200, "PORT command successful."); } } } | PASV check_login CRLF { if ($2) { passive(); } } | TYPE check_login SP type_code CRLF { if ($2) { switch (cmd_type) { case TYPE_A: if (cmd_form == FORM_N) { reply(200, "Type set to A."); type = cmd_type; form = cmd_form; } else reply(504, "Form must be N."); break; case TYPE_E: reply(504, "Type E not implemented."); break; case TYPE_I: reply(200, "Type set to I."); type = cmd_type; break; case TYPE_L: if (cmd_bytesz == 8) { reply(200, "Type set to L (byte size 8)."); type = cmd_type; } else reply(504, "Byte size must be 8."); } } } | STRU check_login SP struct_code CRLF { if ($2) { switch ($4) { case STRU_F: reply(200, "STRU F ok."); break; default: reply(504, "Unimplemented STRU type."); } } } | MODE check_login SP mode_code CRLF { if ($2) { switch ($4) { case MODE_S: reply(200, "MODE S ok."); break; default: reply(502, "Unimplemented MODE type."); } } } | ALLO check_login SP NUMBER CRLF { if ($2) { reply(202, "ALLO command ignored."); } } | ALLO check_login SP NUMBER SP R SP NUMBER CRLF { if ($2) { reply(202, "ALLO command ignored."); } } | RETR check_login SP pathname CRLF { if ($2 && $4 != NULL) retrieve((char *) 0, $4); if ($4 != NULL) free($4); } | STOR check_login SP pathname CRLF { if ($2 && $4 != NULL) store($4, "w", 0); if ($4 != NULL) free($4); } | APPE check_login SP pathname CRLF { if ($2 && $4 != NULL) store($4, "a", 0); if ($4 != NULL) free($4); } | NLST check_login CRLF { if ($2) send_file_list("."); } | NLST check_login SP STRING CRLF { if ($2 && $4 != NULL) send_file_list($4); if ($4 != NULL) free($4); } | LIST check_login CRLF { if ($2) retrieve("/bin/ls -lgA", ""); } | LIST check_login SP pathname CRLF { if ($2 && $4 != NULL) retrieve("/bin/ls -lgA %s", $4); if ($4 != NULL) free($4); } | STAT check_login SP pathname CRLF { if ($2 && $4 != NULL) statfilecmd($4); if ($4 != NULL) free($4); } | STAT check_login CRLF { if ($2) statcmd(); } | DELE check_login SP pathname CRLF { if ($2 && $4 != NULL) delete($4); if ($4 != NULL) free($4); } | RNTO check_login SP pathname CRLF { if ($2) { if (fromname) { renamecmd(fromname, $4); free(fromname); fromname = (char *) 0; } else { reply(503, "Bad sequence of commands."); } } free($4); } | ABOR check_login CRLF { if ($2) reply(225, "ABOR command successful."); } | CWD check_login CRLF { if ($2) cwd(pw->pw_dir); } | CWD check_login SP pathname CRLF { if ($2 && $4 != NULL) cwd($4); if ($4 != NULL) free($4); } | HELP CRLF { help(cmdtab, (char *) 0); } | HELP SP STRING CRLF { char *cp = $3; if (strncasecmp(cp, "SITE", 4) == 0) { cp = $3 + 4; if (*cp == ' ') cp++; if (*cp) help(sitetab, cp); else help(sitetab, (char *) 0); } else help(cmdtab, $3); if ($3 != NULL) free ($3); } | NOOP CRLF { reply(200, "NOOP command successful."); } | MKD check_login SP pathname CRLF { if ($2 && $4 != NULL) makedir($4); if ($4 != NULL) free($4); } | RMD check_login SP pathname CRLF { if ($2 && $4 != NULL) removedir($4); if ($4 != NULL) free($4); } | PWD check_login CRLF { if ($2) pwd(); } | CDUP check_login CRLF { if ($2) cwd(".."); } | SITE SP HELP CRLF { help(sitetab, (char *) 0); } | SITE SP HELP SP STRING CRLF { help(sitetab, $5); if ($5 != NULL) free ($5); } | SITE SP UMASK check_login CRLF { int oldmask; if ($4) { oldmask = umask(0); (void) umask(oldmask); reply(200, "Current UMASK is %03o", oldmask); } } | SITE SP UMASK check_login SP octal_number CRLF { int oldmask; if ($4) { if (($6 == -1) || ($6 > 0777)) { reply(501, "Bad UMASK value"); } else { oldmask = umask($6); reply(200, "UMASK set to %03o (was %03o)", $6, oldmask); } } } | SITE SP CHMOD check_login SP octal_number SP pathname CRLF { if ($4 && ($8 != NULL)) { if ($6 > 0777) reply(501, "CHMOD: Mode value must be between 0 and 0777"); else if (chmod($8, $6) < 0) perror_reply(550, $8); else reply(200, "CHMOD command successful."); } if ($8 != NULL) free($8); } | SITE SP check_login IDLE CRLF { if ($3) reply(200, "Current IDLE time limit is %d seconds; max %d", timeout, maxtimeout); } | SITE SP check_login IDLE SP NUMBER CRLF { if ($3) { if ($6 < 30 || $6 > maxtimeout) { reply(501, "Maximum IDLE time must be between 30 and %d seconds", maxtimeout); } else { timeout = $6; (void) alarm((unsigned) timeout); reply(200, "Maximum IDLE time set to %d seconds", timeout); } } } | STOU check_login SP pathname CRLF { if ($2 && $4 != NULL) store($4, "w", 1); if ($4 != NULL) free($4); } | SYST check_login CRLF { if ($2) #ifdef __linux__ reply(215, "UNIX Type: L%d (Linux)", CHAR_BIT); #else #ifdef unix #ifdef BSD reply(215, "UNIX Type: L%d Version: BSD-%d", CHAR_BIT, BSD); #else /* BSD */ reply(215, "UNIX Type: L%d", CHAR_BIT); #endif /* BSD */ #else /* unix */ reply(215, "UNKNOWN Type: L%d", CHAR_BIT); #endif /* unix */ #endif /* __linux__ */ } /* * SIZE is not in RFC959, but Postel has blessed it and * it will be in the updated RFC. * * Return size of file in a format suitable for * using with RESTART (we just count bytes). */ | SIZE check_login SP pathname CRLF { if ($2 && $4 != NULL) sizecmd($4); if ($4 != NULL) free($4); } /* * MDTM is not in RFC959, but Postel has blessed it and * it will be in the updated RFC. * * Return modification time of file as an ISO 3307 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx * where xxx is the fractional second (of any precision, * not necessarily 3 digits) */ | MDTM check_login SP pathname CRLF { if ($2 && $4 != NULL) { struct stat stbuf; if (stat($4, &stbuf) < 0) reply(550, "%s: %s", $4, strerror(errno)); else if (!S_ISREG(stbuf.st_mode)) { reply(550, "%s: not a plain file.", $4); } else { struct tm *t; t = gmtime(&stbuf.st_mtime); reply(213, "%04d%02d%02d%02d%02d%02d", TM_YEAR_BASE + t->tm_year, t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); } } if ($4 != NULL) free($4); } | QUIT CRLF { reply(221, "Goodbye."); dologout(0); } | error CRLF { yyerrok; } ; rcmd : RNFR check_login SP pathname CRLF { restart_point = (off_t) 0; if ($2 && $4) { fromname = renamefrom($4); if (fromname == (char *) 0 && $4) { free($4); } } else { if ($4) free ($4); } } | REST check_login SP byte_size CRLF { if ($2) { fromname = (char *) 0; restart_point = $4; /* XXX $4 is only "int" */ reply(350, "Restarting at %qd. %s", (quad_t) restart_point, "Send STORE or RETRIEVE to initiate transfer."); } } ; username : STRING ; password : /* empty */ { $$ = (char *)calloc(1, sizeof(char)); } | STRING ; byte_size : NUMBER ; host_port : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER { char *a, *p; if ($1 < 0 || $1 > 255 || $3 < 0 || $3 > 255 || $5 < 0 || $5 > 255 || $7 < 0 || $7 > 255 || $9 < 0 || $9 > 255 || $11 < 0 || $11 > 255) { $$ = 1; } else { #ifndef __linux__ data_dest.sin_len = sizeof(struct sockaddr_in); #endif data_dest.sin_family = AF_INET; p = (char *)&data_dest.sin_port; p[0] = $9; p[1] = $11; a = (char *)&data_dest.sin_addr; a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; $$ = 0; } } ; form_code : N { $$ = FORM_N; } | T { $$ = FORM_T; } | C { $$ = FORM_C; } ; type_code : A { cmd_type = TYPE_A; cmd_form = FORM_N; } | A SP form_code { cmd_type = TYPE_A; cmd_form = $3; } | E { cmd_type = TYPE_E; cmd_form = FORM_N; } | E SP form_code { cmd_type = TYPE_E; cmd_form = $3; } | I { cmd_type = TYPE_I; } | L { cmd_type = TYPE_L; cmd_bytesz = CHAR_BIT; } | L SP byte_size { cmd_type = TYPE_L; cmd_bytesz = $3; } /* this is for a bug in the BBN ftp */ | L byte_size { cmd_type = TYPE_L; cmd_bytesz = $2; } ; struct_code : F { $$ = STRU_F; } | R { $$ = STRU_R; } | P { $$ = STRU_P; } ; mode_code : S { $$ = MODE_S; } | B { $$ = MODE_B; } | C { $$ = MODE_C; } ; pathname : pathstring { /* * Problem: this production is used for all pathname * processing, but only gives a 550 error reply. * This is a valid reply in some cases but not in others. */ if (logged_in && $1 && strchr($1, '~') != NULL) { glob_t gl; #ifdef __linux__ /* see popen.c */ int flags = GLOB_NOCHECK; #else int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; #endif char *pptr = $1; /* * glob() will only find a leading ~, but * Netscape kindly puts a slash in front of * it for publish URLs. There needs to be * a flag for glob() that expands tildes * anywhere in the string. */ if ((pptr[0] == '/') && (pptr[1] == '~')) pptr++; memset(&gl, 0, sizeof(gl)); if (glob(pptr, flags, NULL, &gl) || gl.gl_pathc == 0) { reply(550, "not found"); $$ = NULL; } else { $$ = strdup(gl.gl_pathv[0]); } globfree(&gl); free($1); } else $$ = $1; } ; pathstring : STRING ; octal_number : NUMBER { int ret, dec, multby, digit; /* * Convert a number that was read as decimal number * to what it would be if it had been read as octal. */ dec = $1; multby = 1; ret = 0; while (dec) { digit = dec%10; if (digit > 7) { ret = -1; break; } ret += digit * multby; multby *= 8; dec /= 10; } $$ = ret; } ; check_login : /* empty */ { if (logged_in) $$ = 1; else { reply(530, "Please login with USER and PASS."); $$ = 0; } } ; %% extern jmp_buf errcatch; #define CMD 0 /* beginning of command */ #define ARGS 1 /* expect miscellaneous arguments */ #define STR1 2 /* expect SP followed by STRING */ #define STR2 3 /* expect STRING */ #define OSTR 4 /* optional SP then STRING */ #define ZSTR1 5 /* SP then optional STRING */ #define ZSTR2 6 /* optional STRING after SP */ #define SITECMD 7 /* SITE command */ #define NSTR 8 /* Number followed by a string */ struct tab { const char *name; short token; short state; short implemented; /* 1 if command is implemented */ const char *help; }; struct tab cmdtab[] = { /* In order defined in RFC 765 */ { "USER", USER, STR1, 1, " username" }, { "PASS", PASS, ZSTR1, 1, " password" }, { "ACCT", ACCT, STR1, 0, "(specify account)" }, { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, { "PORT", PORT, ARGS, 1, " b0, b1, b2, b3, b4" }, { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, { "TYPE", TYPE, ARGS, 1, " [ A | E | I | L ]" }, { "STRU", STRU, ARGS, 1, "(specify file structure)" }, { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, { "RETR", RETR, STR1, 1, " file-name" }, { "STOR", STOR, STR1, 1, " file-name" }, { "APPE", APPE, STR1, 1, " file-name" }, { "MLFL", MLFL, OSTR, 0, "(mail file)" }, { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, { "REST", REST, ARGS, 1, " offset (restart command)" }, { "RNFR", RNFR, STR1, 1, " file-name" }, { "RNTO", RNTO, STR1, 1, " file-name" }, { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, { "DELE", DELE, STR1, 1, " file-name" }, { "CWD", CWD, OSTR, 1, "[ directory-name ]" }, { "XCWD", CWD, OSTR, 1, "[ directory-name ]" }, { "LIST", LIST, OSTR, 1, "[ path-name ]" }, { "NLST", NLST, OSTR, 1, "[ path-name ]" }, { "SITE", SITE, SITECMD, 1, "site-cmd [ arguments ]" }, { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, { "STAT", STAT, OSTR, 1, "[ path-name ]" }, { "HELP", HELP, OSTR, 1, "[ ]" }, { "NOOP", NOOP, ARGS, 1, "" }, { "MKD", MKD, STR1, 1, " path-name" }, { "XMKD", MKD, STR1, 1, " path-name" }, { "RMD", RMD, STR1, 1, " path-name" }, { "XRMD", RMD, STR1, 1, " path-name" }, { "PWD", PWD, ARGS, 1, "(return current directory)" }, { "XPWD", PWD, ARGS, 1, "(return current directory)" }, { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, { "STOU", STOU, STR1, 1, " file-name" }, { "SIZE", SIZE, OSTR, 1, " path-name" }, { "MDTM", MDTM, OSTR, 1, " path-name" }, { NULL, 0, 0, 0, 0 } }; struct tab sitetab[] = { { "UMASK", UMASK, ARGS, 1, "[ umask ]" }, { "IDLE", IDLE, ARGS, 1, "[ maximum-idle-time ]" }, { "CHMOD", CHMOD, NSTR, 1, " mode file-name" }, { "HELP", HELP, OSTR, 1, "[ ]" }, { NULL, 0, 0, 0, 0 } }; static void help __P((struct tab *, char *)); static struct tab * lookup __P((struct tab *, char *)); static void sizecmd __P((char *)); static int yylex __P((void)); static struct tab *lookup(struct tab *p, char *cmd) { for (; p->name != NULL; p++) if (strcmp(cmd, p->name) == 0) return (p); return (0); } #include /* * getline - a hacked up version of fgets to ignore TELNET escape codes. */ char * ftpd_getline(char *s, int n, FILE *iop) { int c; register char *cs; cs = s; /* tmpline may contain saved command from urgent mode interruption */ for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { *cs++ = tmpline[c]; if (tmpline[c] == '\n') { *cs++ = '\0'; if (debug) syslog(LOG_DEBUG, "command: %s", s); tmpline[0] = '\0'; return(s); } if (c == 0) tmpline[0] = '\0'; } while ((c = getc(iop)) != EOF) { c &= 0377; if (c == IAC) { if ((c = getc(iop)) != EOF) { c &= 0377; switch (c) { case WILL: case WONT: c = getc(iop); printf("%c%c%c", IAC, DONT, 0377&c); (void) fflush(stdout); continue; case DO: case DONT: c = getc(iop); printf("%c%c%c", IAC, WONT, 0377&c); (void) fflush(stdout); continue; case IAC: break; default: continue; /* ignore command */ } } } *cs++ = c; if (--n <= 0 || c == '\n') break; } if (c == EOF && cs == s) return (NULL); *cs++ = '\0'; if (debug) { if (!guest && strncasecmp("pass ", s, 5) == 0) { /* Don't syslog passwords */ syslog(LOG_DEBUG, "command: %.5s ???", s); } else { register char *cp; register int len; /* Don't syslog trailing CR-LF */ len = strlen(s); cp = s + len - 1; while (cp >= s && (*cp == '\n' || *cp == '\r')) { --cp; --len; } syslog(LOG_DEBUG, "command: %.*s", len, s); } } return (s); } void toolong(int signo) { (void)signo; reply(421, "Timeout (%d seconds): closing control connection.", timeout); if (logging) syslog(LOG_INFO, "User %s timed out after %d seconds", (pw ? pw -> pw_name : "unknown"), timeout); dologout(1); } static int yylex(void) { static int cpos, state; char *cp, *cp2; struct tab *p; int n, value; char c; for (;;) { switch (state) { case CMD: (void) signal(SIGALRM, toolong); (void) alarm((unsigned) timeout); if (ftpd_getline(cbuf, sizeof(cbuf)-1, stdin)==NULL) { reply(221, "You could at least say goodbye."); dologout(0); } (void) alarm(0); if ((cp = strchr(cbuf, '\r'))) { *cp++ = '\n'; *cp = '\0'; } #ifdef HASSETPROCTITLE if (strncasecmp(cbuf, "PASS", 4) != 0) { if ((cp = strpbrk(cbuf, "\n"))) { c = *cp; *cp = '\0'; setproctitle("%s: %s", proctitle, cbuf); *cp = c; } } #endif /* HASSETPROCTITLE */ if ((cp = strpbrk(cbuf, " \n"))) cpos = cp - cbuf; if (cpos == 0) cpos = 4; c = cbuf[cpos]; cbuf[cpos] = '\0'; upper(cbuf); p = lookup(cmdtab, cbuf); cbuf[cpos] = c; if (p != 0) { if (p->implemented == 0) { nack(p->name); longjmp(errcatch,0); /* NOTREACHED */ } state = p->state; yylval.s = (char *)p->name; /* XXX */ return (p->token); } break; case SITECMD: if (cbuf[cpos] == ' ') { cpos++; return (SP); } cp = &cbuf[cpos]; if ((cp2 = strpbrk(cp, " \n"))) cpos = cp2 - cbuf; c = cbuf[cpos]; cbuf[cpos] = '\0'; upper(cp); p = lookup(sitetab, cp); cbuf[cpos] = c; if (p != 0) { if (p->implemented == 0) { state = CMD; nack(p->name); longjmp(errcatch,0); /* NOTREACHED */ } state = p->state; yylval.s = (char *) p->name; /* XXX */ return (p->token); } state = CMD; break; case OSTR: if (cbuf[cpos] == '\n') { state = CMD; return (CRLF); } /* FALLTHROUGH */ case STR1: case ZSTR1: dostr1: if (cbuf[cpos] == ' ') { cpos++; /* DOH!!! who wrote this? * state = ++state; is undefined in C! * state = state == OSTR ? STR2 : ++state; * looks elegant but not correct, adding 'value' */ value = state == OSTR ? STR2 : ++state; state = value; return (SP); } break; case ZSTR2: if (cbuf[cpos] == '\n') { state = CMD; return (CRLF); } /* FALLTHROUGH */ case STR2: cp = &cbuf[cpos]; n = strlen(cp); cpos += n - 1; /* * Make sure the string is nonempty and \n terminated. */ if (n > 1 && cbuf[cpos] == '\n') { cbuf[cpos] = '\0'; yylval.s = strdup(cp); if (yylval.s == NULL) fatal("Ran out of memory."); cbuf[cpos] = '\n'; state = ARGS; return (STRING); } break; case NSTR: if (cbuf[cpos] == ' ') { cpos++; return (SP); } if (isdigit(cbuf[cpos])) { cp = &cbuf[cpos]; while (isdigit(cbuf[++cpos])) ; c = cbuf[cpos]; cbuf[cpos] = '\0'; yylval.i = atoi(cp); cbuf[cpos] = c; state = STR1; return (NUMBER); } state = STR1; goto dostr1; case ARGS: if (isdigit(cbuf[cpos])) { cp = &cbuf[cpos]; while (isdigit(cbuf[++cpos])) ; c = cbuf[cpos]; cbuf[cpos] = '\0'; yylval.i = atoi(cp); cbuf[cpos] = c; return (NUMBER); } switch (cbuf[cpos++]) { case '\n': state = CMD; return (CRLF); case ' ': return (SP); case ',': return (COMMA); case 'A': case 'a': return (A); case 'B': case 'b': return (B); case 'C': case 'c': return (C); case 'E': case 'e': return (E); case 'F': case 'f': return (F); case 'I': case 'i': return (I); case 'L': case 'l': return (L); case 'N': case 'n': return (N); case 'P': case 'p': return (P); case 'R': case 'r': return (R); case 'S': case 's': return (S); case 'T': case 't': return (T); } break; default: fatal("Unknown state in scanner."); } yyerror((char *) 0); state = CMD; longjmp(errcatch,0); } } void upper(char *s) { while (*s != '\0') { if (islower(*s)) *s = toupper(*s); s++; } } static void help(struct tab *ctab, char *s) { struct tab *c; int width, NCMDS; const char *type; if (ctab == sitetab) type = "SITE "; else type = ""; width = 0, NCMDS = 0; for (c = ctab; c->name != NULL; c++) { int len = strlen(c->name); if (len > width) width = len; NCMDS++; } width = (width + 8) &~ 7; if (s == 0) { int i, j, w; int columns, lines; lreply(214, "The following %scommands are recognized %s.", type, "(* =>'s unimplemented)"); columns = 76 / width; if (columns == 0) columns = 1; lines = (NCMDS + columns - 1) / columns; for (i = 0; i < lines; i++) { printf(" "); for (j = 0; j < columns; j++) { c = ctab + j * lines + i; printf("%s%c", c->name, c->implemented ? ' ' : '*'); if (c + lines >= &ctab[NCMDS]) break; w = strlen(c->name) + 1; while (w < width) { putchar(' '); w++; } } printf("\r\n"); } (void) fflush(stdout); reply(214, "Direct comments to ftp-bugs@%s.", hostname); return; } upper(s); c = lookup(ctab, s); if (c == (struct tab *)0) { reply(502, "Unknown command %s.", s); return; } if (c->implemented) reply(214, "Syntax: %s%s %s", type, c->name, c->help); else reply(214, "%s%-*s\t%s; unimplemented.", type, width, c->name, c->help); } static void sizecmd(char *filename) { switch (type) { case TYPE_L: case TYPE_I: { struct stat stbuf; if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) reply(550, "%s: not a plain file.", filename); else reply(213, "%qu", (quad_t) stbuf.st_size); break; } case TYPE_A: { FILE *fin; int c; off_t count; struct stat stbuf; fin = fopen(filename, "r"); if (fin == NULL) { perror_reply(550, filename); return; } if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) { reply(550, "%s: not a plain file.", filename); (void) fclose(fin); return; } count = 0; while((c=getc(fin)) != EOF) { if (c == '\n') /* will get expanded to \r\n */ count++; count++; } (void) fclose(fin); reply(213, "%qd", (quad_t) count); break; } default: reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); } } linux-ftpd-0.17/ftpd/ftpd.8100644 144 144 30345 7141140311 15037 0ustar dhollandpeople.\" .\" Copyright (c) 1985, 1988, 1991, 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. All advertising materials mentioning features or use of this software .\" must display the following acknowledgement: .\" This product includes software developed by the University of .\" California, Berkeley and its contributors. .\" 4. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" @(#)ftpd.8 8.2 (Berkeley) 4/19/94 .\" NetBSD: ftpd.8,v 1.8 1996/01/14 20:55:23 thorpej Exp .\" OpenBSD: ftpd.8,v 1.9 1996/12/03 03:07:16 deraadt Exp .\" OpenBSD: ftpd.8,v 1.12 1997/05/01 14:45:36 deraadt Exp .\" OpenBSD: ftpd.8,v 1.22 1999/07/09 13:35:50 aaron Exp .\" $Id: ftpd.8,v 1.17 2000/07/30 23:56:57 dholland Exp $ .\" .Dd September 14, 1999 .Dt FTPD 8 .Os "Linux NetKit (0.17)" .Sh NAME .Nm ftpd .Nd Internet File Transfer Protocol server .Sh SYNOPSIS .Nm ftpd .Op Fl AdDhlMPSU .Op Fl T Ar maxtimeout .Op Fl t Ar timeout .Op Fl u Ar mask .Sh DESCRIPTION .Nm Ftpd is the Internet File Transfer Protocol server process. The server uses the .Tn TCP protocol and listens at the port specified in the .Dq ftp service specification; see .Xr services 5 . .Pp Available options: .Bl -tag -width Ds .It Fl A Permit only anonymous ftp connections or accounts listed in .Pa /etc/ftpchroot. Other connection attempts are refused. .It Fl d Debugging information is written to the syslog using LOG_FTP. .It Fl D With this option set, .Nm ftpd will detach and become a daemon, accepting connections on the FTP port and forking child processes to handle them. This has lower overhead than starting .Nm ftpd from .Xr inetd 8 and is thus useful on busy servers to reduce load. .It Fl h The server will use data ports in the high port range for passive connections. This range is defined by the .Ev IPPORT_HIFIRSTAUTO and .Ev IPPORT_HILASTAUTO defines in . In .Ox they are set to 49152 and 65535 respectively. .It Fl l Each successful and failed .Xr ftp 1 session is logged using syslog with a facility of LOG_FTP. If this option is specified twice, the retrieve (get), store (put), append, delete, make directory, remove directory and rename operations and their filename arguments are also logged. .It Fl M Enables multihomed mode. Instead of simply using .Pa ~ftp for anonymous transfers, a directory matching the fully qualified name of the IP number the client connected to, and located inside .Pa ~ftp is used instead. .It Fl p Disable passive mode ftp connections. This is useful if you are behind a firewall that refuses connections to arbitrary high numbered ports. Many ftp clients try passive mode first and do not always react gracefully to a server that refuses connections to the port it asked the client to connect to. .It Fl P Permit illegal port numbers or addresses for PORT command initiated connects. By default .Xr ftpd 8 violates the RFC and thus constrains the PORT command to non-reserved ports and requires it use the same source address as the connection came from. This prevents the "FTP bounce attack" against services on both the local machine and other local machines. .It Fl S With this option set, .Nm ftpd logs all anonymous transfers to the file .Pa /var/log/ftpd when this file exists. .It Fl U Each concurrent .Xr ftp 1 session is logged to the file .Pa /var/run/utmp , making them visible to commands such as .Xr who 1 . This option at present is unsupporte and will always silently fail. .It Fl T A client may also request a different timeout period; the maximum period allowed may be set to .Ar timeout seconds with the .Fl T option. The default limit is 2 hours. .It Fl t The inactivity timeout period is set to .Ar timeout seconds (the default is 15 minutes). .It Fl u Change the default umask from 027 to .Ar mask . .El .Pp The file .Pa /etc/nologin can be used to disable ftp access. If the file exists, .Nm displays it and exits. If the file .Pa /etc/ftpwelcome exists, .Nm prints it before issuing the .Dq ready message. If the file .Pa /etc/motd exists, .Nm prints it after a successful login. If the file .Pa .message exists in a directory, .Nm prints it when that directory is entered. .Pp The ftp server currently supports the following ftp requests. The case of the requests is ignored. .Bl -column "Request" -offset indent .It Request Ta "Description" .It ABOR Ta "abort previous command" .It ACCT Ta "specify account (ignored)" .It ALLO Ta "allocate storage (vacuously)" .It APPE Ta "append to a file" .It CDUP Ta "change to parent of current working directory" .It CWD Ta "change working directory" .It DELE Ta "delete a file" .It HELP Ta "give help information" .It LIST Ta "give list files in a directory" Pq Dq Li "ls -lgA" .It MKD Ta "make a directory" .It MDTM Ta "show last modification time of file" .It MODE Ta "specify data transfer" Em mode .It NLST Ta "give name list of files in directory" .It NOOP Ta "do nothing" .It PASS Ta "specify password" .It PASV Ta "prepare for server-to-server transfer" .It PORT Ta "specify data connection port" .It PWD Ta "print the current working directory" .It QUIT Ta "terminate session" .It REST Ta "restart incomplete transfer" .It RETR Ta "retrieve a file" .It RMD Ta "remove a directory" .It RNFR Ta "specify rename-from file name" .It RNTO Ta "specify rename-to file name" .It SITE Ta "non-standard commands (see next section)" .It SIZE Ta "return size of file" .It STAT Ta "return status of server" .It STOR Ta "store a file" .It STOU Ta "store a file with a unique name" .It STRU Ta "specify data transfer" Em structure .It SYST Ta "show operating system type of server system" .It TYPE Ta "specify data transfer" Em type .It USER Ta "specify user name" .It XCUP Ta "change to parent of current working directory (deprecated)" .It XCWD Ta "change working directory (deprecated)" .It XMKD Ta "make a directory (deprecated)" .It XPWD Ta "print the current working directory (deprecated)" .It XRMD Ta "remove a directory (deprecated)" .El .Pp The following non-standard or .Tn UNIX specific commands are supported by the SITE request. .Pp .Bl -column Request -offset indent .It Sy Request Ta Sy Description .It UMASK Ta change umask, e.g. ``SITE UMASK 002'' .It IDLE Ta set idle-timer, e.g. ``SITE IDLE 60'' .It CHMOD Ta change mode of a file, e.g. ``SITE CHMOD 755 filename'' .It HELP Ta give help information. .El .Pp The remaining ftp requests specified in Internet RFC 959 are recognized, but not implemented. MDTM and SIZE are not specified in RFC 959, but will appear in the next updated FTP RFC. .Pp The ftp server will abort an active file transfer only when the ABOR command is preceded by a Telnet "Interrupt Process" (IP) signal and a Telnet "Synch" signal in the command Telnet stream, as described in Internet RFC 959. If a STAT command is received during a data transfer, preceded by a Telnet IP and Synch, transfer status will be returned. .Pp .Nm Ftpd interprets file names according to the .Dq globbing conventions used by .Xr csh 1 . This allows users to utilize the metacharacters .Dq Li \&*?[]{}~ . .Pp .Nm Ftpd authenticates users according to five rules. .Pp .Bl -enum -offset indent .It The login name must be in the password data base, .\" .Pa /etc/pwd.db , .Pa /etc/passwd , and not have a null password. In this case a password must be provided by the client before any file operations may be performed. If the user has an S/Key key, the response from a successful USER command will include an S/Key challenge. The client may choose to respond with a PASS command giving either a standard password or an S/Key one-time password. The server will automatically determine which type of password it has been given and attempt to authenticate accordingly. See .Xr skey 1 for more information on S/Key authentication. S/Key is a Trademark of Bellcore. .It The login name must not appear in the file .Pa /etc/ftpusers . .It The user must have a standard shell returned by .Xr getusershell 3 . .It If the user name appears in the file .Pa /etc/ftpchroot the session's root will be changed to the user's login directory by .Xr chroot 2 as for an .Dq anonymous or .Dq ftp account (see next item). However, the user must still supply a password. This feature is intended as a compromise between a fully anonymous account and a fully privileged account. The account should also be set up as for an anonymous account. .It If the user name is .Dq anonymous or .Dq ftp , an anonymous ftp account must be present in the password file (user .Dq ftp ) . In this case the user is allowed to log in by specifying any password (by convention an email address for the user should be used as the password). .El .Pp In the last case, .Nm ftpd takes special measures to restrict the client's access privileges. The server performs a .Xr chroot 2 to the home directory of the .Dq ftp user. In order that system security is not breached, it is recommended that the .Dq ftp subtree be constructed with care, following these rules: .Bl -tag -width "~ftp/pub" -offset indent .It Pa ~ftp Make the home directory owned by .Dq root and unwritable by anyone (mode 555). .It Pa ~ftp/bin Make this directory owned by .Dq root and unwritable by anyone (mode 511). .\"This directory is optional unless you have commands you wish .\"the anonymous ftp user to be able to run (the .\".Xr ls 1 .\"command exists as a builtin). .\" -- not in the Linux port (yet?) This directory is required, and should contain at least a statically linked copy of .Xr ls 1. Any programs in this directory should be mode 111 (executable only). .It Pa ~ftp/etc Make this directory owned by .Dq root and unwritable by anyone (mode 511). The files .\" pwd.db (see .\" .Xr pwd_mkdb 8 ) .Xr passwd 5 and .Xr group 5 must be present for the .Xr ls command to be able to produce owner names rather than numbers. The password field in .\" .Xr pwd.db .Pa passwd is not used, and should not contain real passwords. The file .Pa motd , if present, will be printed after a successful login. These files should be mode 444. .It Pa ~ftp/pub Make this directory mode 555 and owned by .Dq root . This is traditionally where publically accessible files are stored for download. .El .Sh FILES .Bl -tag -width /etc/ftpwelcome -compact .It Pa /etc/ftpusers List of unwelcome/restricted users. .It Pa /etc/ftpchroot List of normal users who should be chroot'd. .It Pa /etc/ftpwelcome Welcome notice. .It Pa /etc/motd Welcome notice after login. .It Pa /etc/nologin Displayed and access refused. .It Pa /var/run/utmp List of users on the system. .It Pa /var/log/ftpd Log file for anonymous transfers. .El .Sh SEE ALSO .Xr ftp 1 , .Xr skey 1 , .Xr who 1 , .Xr getusershell 3 , .Xr ftpusers 5 , .Xr syslogd 8 .Sh BUGS The server must run as the super-user to create sockets with privileged port numbers. It maintains an effective user ID of the logged in user, reverting to the super-user only when binding addresses to sockets. The possible security holes have been extensively scrutinized, but are possibly incomplete. .Sh HISTORY The .Nm command appeared in .Bx 4.2 . linux-ftpd-0.17/ftpd/ftpd.c100644 144 144 145205 7136463740 15156 0ustar dhollandpeople/* * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * From: @(#)ftpd.c 8.4 (Berkeley) 4/16/94 * From: NetBSD: ftpd.c,v 1.15 1995/06/03 22:46:47 mycroft Exp * From: OpenBSD: ftpd.c,v 1.26 1996/12/07 09:00:22 bitblt Exp * From: OpenBSD: ftpd.c,v 1.35 1997/05/01 14:45:37 deraadt Exp * From: OpenBSD: ftpd.c,v 1.54 1999/04/29 21:38:43 downsj Exp */ char ftpd_rcsid[] = "$Id: ftpd.c,v 1.20 2000/07/23 03:34:56 dholland Exp $"; char copyright[] = "@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\n" " The Regents of the University of California. All rights reserved.\n"; /* * FTP server. */ #ifdef __linux__ #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #define FTP_NAMES #include #include #include #include #include #include #include #include #include /* for CHAR_BIT */ #include #include #include #include #include #include #include #include #include #include #include #include #ifndef __linux__ #include #include #else #include /* for initgroups() */ /* #include * for L_SET et al. * <--- not used? */ /*typedef int64_t quad_t;*/ typedef unsigned int useconds_t; #endif #include "../version.h" /* glibc 2.[01] does not have TCP_CORK, so define it here */ #if __GLIBC__ >= 2 && defined(__linux__) && !defined(TCP_CORK) #define TCP_CORK 3 #endif #ifndef MAP_FAILED #define MAP_FAILED ((void *)-1) #endif #ifdef USE_SHADOW #include #include "isexpired.h" #endif #if defined(TCPWRAPPERS) #include #endif /* TCPWRAPPERS */ #if defined(SKEY) #include #endif #include "pathnames.h" #include "extern.h" #ifdef __STDC__ #include #else #include #endif static char versionpre[] = "Version 6.4/OpenBSD/Linux"; static char version[sizeof(versionpre)+sizeof(pkg)]; extern off_t restart_point; extern char cbuf[]; struct sockaddr_in server_addr; struct sockaddr_in ctrl_addr; struct sockaddr_in data_source; struct sockaddr_in data_dest; struct sockaddr_in his_addr; struct sockaddr_in pasv_addr; int daemon_mode = 0; int data; jmp_buf errcatch, urgcatch; int logged_in; struct passwd *pw; #ifdef USE_SHADOW struct spwd *spw = NULL; #endif int debug = 0; 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 high_data_ports = 0; int anon_only = 0; int multihome = 0; int guest; int stats; int statfd = -1; int portcheck = 1; int dochroot; int type; int form; int stru; /* avoid C keyword */ int mode; int doutmp = 0; /* update utmp file */ int usedefault = 1; /* for data transfers */ int pdata = -1; /* for passive mode */ sig_atomic_t transflag; off_t file_size; off_t byte_count; #if !defined(CMASK) || CMASK == 0 #undef CMASK #define CMASK 027 #endif int defumask = CMASK; /* default umask value */ char tmpline[7]; char hostname[MAXHOSTNAMELEN]; char remotehost[MAXHOSTNAMELEN]; char dhostname[MAXHOSTNAMELEN]; char *guestpw; static char ttyline[20]; char *tty = ttyline; /* for klogin */ static struct utmp utmp; /* for utmp */ #if defined(TCPWRAPPERS) int allow_severity = LOG_INFO; int deny_severity = LOG_NOTICE; #endif /* TCPWRAPPERS */ #if defined(KERBEROS) int notickets = 1; char *krbtkfile_env = NULL; #endif char *ident = NULL; /* * Timeout intervals for retrying connections * to hosts that don't accept PORT cmds. This * is a kludge, but given the problems with TCP... */ #define SWAITMAX 90 /* wait at most 90 seconds */ #define SWAITINT 5 /* interval between retries */ int swaitmax = SWAITMAX; int swaitint = SWAITINT; #ifdef HASSETPROCTITLE char proctitle[BUFSIZ]; /* initial part of title */ #endif /* HASSETPROCTITLE */ #define LOGCMD(cmd, file) \ if (logging > 1) \ syslog(LOG_INFO,"%s %s%s", cmd, \ *(file) == '/' ? "" : curdir(), file); #define LOGCMD2(cmd, file1, file2) \ if (logging > 1) \ syslog(LOG_INFO,"%s %s%s %s%s", cmd, \ *(file1) == '/' ? "" : curdir(), file1, \ *(file2) == '/' ? "" : curdir(), file2); #define LOGBYTES(cmd, file, cnt) \ if (logging > 1) { \ if (cnt == (off_t)-1) \ syslog(LOG_INFO,"%s %s%s", cmd, \ *(file) == '/' ? "" : curdir(), file); \ else \ syslog(LOG_INFO, "%s %s%s = %qd bytes", cmd, \ *(file) == '/' ? "" : curdir(), file, (quad_t)(cnt)); \ } static void ack __P((const char *)); static void myoob __P((int)); static int checkuser __P((const char *, const char *)); static FILE *dataconn __P((const char *, off_t, const char *)); static void dolog __P((struct sockaddr_in *)); static const char *curdir __P((void)); static void end_login __P((void)); static FILE *getdatasock __P((const char *)); static int guniquefd __P((const char *, char **)); static void lostconn __P((int)); static void sigquit __P((int)); static int receive_data __P((FILE *, FILE *)); static void replydirname __P((const char *, const char *)); static void send_data __P((FILE *, FILE *, off_t, off_t, int)); static struct passwd * sgetpwnam __P((const char *)); static char *sgetsave __P((char *)); static void reapchild __P((int)); #if defined(TCPWRAPPERS) static int check_host __P((struct sockaddr_in *)); #endif void logxfer __P((const char *, off_t, time_t)); #ifdef __linux__ static void warnx(const char *format, ...) { va_list ap; va_start(ap, format); fprintf(stderr, "ftpd: "); vfprintf(stderr, format, ap); fprintf(stderr, "\n"); va_end(ap); } #endif /* __linux__ */ static const char * curdir(void) { static char path[MAXPATHLEN+1]; /* path + '/' */ if (getcwd(path, sizeof(path)-1) == NULL) return (""); if (path[1] != '\0') /* special case for root dir. */ strcat(path, "/"); /* For guest account, skip / since it's chrooted */ return (guest ? path+1 : path); } int main(int argc, char *argv[], char **envp) { int ch, on = 1, tos; socklen_t addrlen; char *cp, line[LINE_MAX]; FILE *fd; const char *argstr = "AdDhlMSt:T:u:UvP"; struct hostent *hp; #ifdef __linux__ initsetproctitle(argc, argv, envp); srandom(time(NULL)^(getpid()<<8)); /* * Get the version number from pkg[] and put it in version[] * (we do this like this because pkg[] gets updated by the build * environment) */ { char *tmp, *tmp2, tbuf[sizeof(pkg)+1]; strcpy(tbuf, pkg); strcpy(version, versionpre); tmp = strchr(tbuf, '-'); if (tmp) { tmp2 = strchr(tmp, ' '); if (tmp2) *tmp2=0; strcat(version, tmp); } } #endif tzset(); /* in case no timezone database in ~ftp */ /* set this here so klogin can use it... */ (void)snprintf(ttyline, sizeof(ttyline), "ftp%d", getpid()); while ((ch = getopt(argc, argv, argstr)) != -1) { switch (ch) { case 'A': anon_only = 1; break; case 'd': debug = 1; break; case 'D': daemon_mode = 1; break; case 'P': portcheck = 0; break; case 'h': high_data_ports = 1; break; case 'l': logging++; /* > 1 == extra logging */ break; case 'M': multihome = 1; break; case 'S': stats = 1; break; case 't': timeout = atoi(optarg); if (maxtimeout < timeout) maxtimeout = timeout; break; case 'T': maxtimeout = atoi(optarg); if (timeout > maxtimeout) timeout = maxtimeout; break; case 'u': { long val = 0; val = strtol(optarg, &optarg, 8); if (*optarg != '\0' || val < 0 || (val & ~ACCESSPERMS)) warnx("bad value for -u"); else defumask = val; break; } case 'U': doutmp = 1; break; case 'v': debug = 1; break; default: warnx("unknown flag -%c ignored", optopt); break; } } (void) freopen(_PATH_DEVNULL, "w", stderr); /* * LOG_NDELAY sets up the logging connection immediately, * necessary for anonymous ftp's that chroot and can't do it later. */ #ifndef LOG_FTP #define LOG_FTP LOG_DAEMON #endif openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP); if (daemon_mode) { int ctl_sock, fd2; struct servent *sv; /* * Detach from parent. */ if (daemon(1, 1) < 0) { syslog(LOG_ERR, "failed to become a daemon"); exit(1); } (void) signal(SIGCHLD, reapchild); /* * Get port number for ftp/tcp. */ sv = getservbyname("ftp", "tcp"); if (sv == NULL) { syslog(LOG_ERR, "getservbyname for ftp failed"); exit(1); } /* * Open a socket, bind it to the FTP port, and start * listening. */ ctl_sock = socket(AF_INET, SOCK_STREAM, 0); if (ctl_sock < 0) { syslog(LOG_ERR, "control socket: %m"); exit(1); } if (setsockopt(ctl_sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) syslog(LOG_ERR, "control setsockopt: %m");; server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; server_addr.sin_port = sv->s_port; if (bind(ctl_sock, (struct sockaddr *)&server_addr, sizeof(server_addr))) { syslog(LOG_ERR, "control bind: %m"); exit(1); } if (listen(ctl_sock, 32) < 0) { syslog(LOG_ERR, "control listen: %m"); exit(1); } /* * Loop forever accepting connection requests and forking off * children to handle them. */ while (1) { addrlen = sizeof(his_addr); fd2 = accept(ctl_sock, (struct sockaddr *)&his_addr, &addrlen); if (fork() == 0) { /* child */ (void) dup2(fd2, 0); (void) dup2(fd2, 1); close(ctl_sock); break; } close(fd2); } #if defined(TCPWRAPPERS) /* ..in the child. */ if (!check_host(&his_addr)) exit(1); #endif /* TCPWRAPPERS */ } else { addrlen = sizeof(his_addr); if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { syslog(LOG_ERR, "getpeername (%s): %m", argv[0]); exit(1); } } (void) signal(SIGHUP, sigquit); (void) signal(SIGINT, sigquit); (void) signal(SIGQUIT, sigquit); (void) signal(SIGTERM, sigquit); (void) signal(SIGPIPE, lostconn); (void) signal(SIGCHLD, SIG_IGN); if (signal(SIGURG, myoob) == SIG_ERR) syslog(LOG_ERR, "signal: %m"); addrlen = sizeof(ctrl_addr); if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { syslog(LOG_ERR, "getsockname (%s): %m", argv[0]); exit(1); } #ifdef IP_TOS tos = IPTOS_LOWDELAY; if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); #endif data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); /* Try to handle urgent data inline */ #ifdef SO_OOBINLINE if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) syslog(LOG_ERR, "setsockopt: %m"); #endif #ifdef F_SETOWN if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) syslog(LOG_ERR, "fcntl F_SETOWN: %m"); #endif dolog(&his_addr); /* * Set up default state */ data = -1; type = TYPE_A; form = FORM_N; stru = STRU_F; mode = MODE_S; tmpline[0] = '\0'; /* If logins are disabled, print out the message. */ if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) { while (fgets(line, sizeof(line), fd) != NULL) { if ((cp = strchr(line, '\n')) != NULL) *cp = '\0'; lreply(530, "%s", line); } (void) fflush(stdout); (void) fclose(fd); reply(530, "System not available."); exit(0); } if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) { while (fgets(line, sizeof(line), fd) != NULL) { if ((cp = strchr(line, '\n')) != NULL) *cp = '\0'; lreply(220, "%s", line); } (void) fflush(stdout); (void) fclose(fd); /* reply(220,) must follow */ } (void) gethostname(hostname, sizeof(hostname)); /* Make sure hostname is fully qualified. */ hp = gethostbyname(hostname); if (hp != NULL) strcpy(hostname, hp->h_name); if (multihome) { hp = gethostbyaddr((char *) &ctrl_addr.sin_addr, sizeof (struct in_addr), AF_INET); if (hp != NULL) { strcpy(dhostname, hp->h_name); } else { /* Default. */ strcpy(dhostname, inet_ntoa(ctrl_addr.sin_addr)); } } reply(220, "%s FTP server (%s) ready.", (multihome ? dhostname : hostname), version); (void) setjmp(errcatch); for (;;) (void) yyparse(); /* NOTREACHED */ } /* * Signal handlers. */ static void lostconn(int signo) { (void)signo; if (debug) syslog(LOG_DEBUG, "lost connection"); dologout(-1); } static void sigquit(int signo) { syslog(LOG_ERR, "got signal %s", strsignal(signo)); dologout(-1); } /* * Helper function for sgetpwnam(). */ static char * sgetsave(char *s) { char *new = malloc((unsigned) strlen(s) + 1); if (new == NULL) { perror_reply(421, "Local resource failure: malloc"); dologout(1); /* NOTREACHED */ } (void) strcpy(new, s); return (new); } /* * Save the result of a getpwnam. Used for USER command, since * the data returned must not be clobbered by any other command * (e.g., globbing). */ static struct passwd *sgetpwnam(const char *name) { static struct passwd save; struct passwd *p; if ((p = getpwnam(name)) == NULL) return (p); #ifdef USE_SHADOW if ((spw = getspnam(name)) != NULL) p->pw_passwd = spw->sp_pwdp; endspent(); /* ? */ #endif if (save.pw_name) { free(save.pw_name); memset(save.pw_passwd, 0, strlen(save.pw_passwd)); free(save.pw_passwd); free(save.pw_gecos); free(save.pw_dir); free(save.pw_shell); } save = *p; save.pw_name = sgetsave(p->pw_name); save.pw_passwd = sgetsave(p->pw_passwd); save.pw_gecos = sgetsave(p->pw_gecos); save.pw_dir = sgetsave(p->pw_dir); save.pw_shell = sgetsave(p->pw_shell); return (&save); } static int login_attempts; /* number of failed login attempts */ static int askpasswd; /* had user command, ask for passwd */ static char curname[16]; /* current USER name */ /* * USER command. * Sets global passwd pointer pw if named account exists and is acceptable; * sets askpasswd if a PASS command is expected. If logged in previously, * need to reset state. If name is "ftp" or "anonymous", the name is not in * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return. * If account doesn't exist, ask for passwd anyway. Otherwise, check user * requesting login privileges. Disallow anyone who does not have a standard * shell as returned by getusershell(). Disallow anyone mentioned in the file * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. */ void user(char *name) { const char *cp, *shell; if (logged_in) { if (guest) { reply(530, "Can't change user from guest login."); return; } else if (dochroot) { reply(530, "Can't change user from chroot user."); return; } end_login(); } guest = 0; if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { if (checkuser(_PATH_FTPUSERS, "ftp") || checkuser(_PATH_FTPUSERS, "anonymous")) reply(530, "User %s access denied.", name); else if ((pw = sgetpwnam("ftp")) != NULL) { guest = 1; askpasswd = 1; reply(331, "Guest login ok, type your name as password."); } else reply(530, "User %s unknown.", name); if (!askpasswd && logging) syslog(LOG_NOTICE, "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost); return; } if (anon_only && !checkuser(_PATH_FTPCHROOT, name)) { reply(530, "Sorry, only anonymous ftp allowed."); return; } if ((pw = sgetpwnam(name))!=NULL) { if ((shell = pw->pw_shell) == NULL || *shell == 0) shell = _PATH_BSHELL; while ((cp = getusershell()) != NULL) if (strcmp(cp, shell) == 0) break; endusershell(); if (cp == NULL || checkuser(_PATH_FTPUSERS, name)) { 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 (logging) { strncpy(curname, name, sizeof(curname)-1); curname[sizeof(curname)-1] = '\0'; } #ifdef SKEY if (!skey_haskey(name)) { char *myskey, *skey_keyinfo __P((char *name)); myskey = skey_keyinfo(name); reply(331, "Password [ %s ] for %s required.", myskey ? myskey : "error getting challenge", name); } else #endif reply(331, "Password required for %s.", name); askpasswd = 1; /* * Delay before reading passwd after first failed * attempt to slow down passwd-guessing programs. */ if (login_attempts) sleep((unsigned) login_attempts); } /* * Check if a user is in the file "fname" */ static int checkuser(const char *fname, const char *name) { FILE *fd; int found = 0; char *p, line[BUFSIZ]; if ((fd = fopen(fname, "r")) != NULL) { while (fgets(line, sizeof(line), fd) != NULL) if ((p = strchr(line, '\n')) != NULL) { *p = '\0'; if (line[0] == '#') continue; if (strcmp(line, name) == 0) { found = 1; break; } } (void) fclose(fd); } return (found); } /* * Terminate login as previous user, if any, resetting state; * used when USER command is given or login fails. */ static void end_login(void) { sigset_t allsigs; sigfillset (&allsigs); sigprocmask (SIG_BLOCK, &allsigs, NULL); (void) seteuid((uid_t)0); if (logged_in) { ftpdlogwtmp(ttyline, "", ""); if (doutmp) logout(utmp.ut_line); } pw = NULL; logged_in = 0; guest = 0; dochroot = 0; } void pass(char *passwd) { int rval; FILE *fd; static char homedir[MAXPATHLEN]; char rootdir[MAXPATHLEN]; sigset_t allsigs; if (logged_in || askpasswd == 0) { reply(503, "Login with USER first."); return; } askpasswd = 0; if (!guest) { /* "ftp" is only account allowed no password */ if (pw == NULL) { useconds_t us; /* Sleep between 1 and 3 seconds to emulate a crypt. */ #ifndef __linux__ us = arc4random() % 3000000; #else us = random() % 3000000; #endif usleep(us); rval = 1; /* failure below */ goto skip; } #if defined(KERBEROS) rval = klogin(pw, "", hostname, passwd); if (rval == 0) goto skip; #endif #ifdef SKEY if (skey_haskey(pw->pw_name) == 0 && (skey_passcheck(pw->pw_name, passwd) != -1)) { rval = 0; goto skip; } #endif /* the strcmp does not catch null passwords! */ if (strcmp(crypt(passwd, pw->pw_passwd), pw->pw_passwd) || *pw->pw_passwd == '\0') { rval = 1; /* failure */ goto skip; } rval = 0; skip: /* * If rval == 1, the user failed the authentication check * above. If rval == 0, either Kerberos or local authentication * succeeded. */ if (rval) { reply(530, "Login incorrect."); if (logging) syslog(LOG_NOTICE, "FTP LOGIN FAILED FROM %s, %s", remotehost, curname); pw = NULL; if (login_attempts++ >= 5) { syslog(LOG_NOTICE, "repeated login failures from %s", remotehost); exit(0); } return; } } else { /* Save anonymous' password. */ guestpw = strdup(passwd); if (guestpw == (char *)NULL) fatal("Out of memory"); } login_attempts = 0; /* this time successful */ #ifdef USE_SHADOW switch (isexpired(spw)) { case 0: /* success */ break; case 1: syslog(LOG_NOTICE, "expired password from %s, %s", remotehost, pw->pw_name); reply(530, "Please change your password and try again."); return; case 2: syslog(LOG_NOTICE, "inactive login from %s, %s", remotehost, pw->pw_name); reply(530, "Login inactive -- contact administrator."); return; case 3: syslog(LOG_NOTICE, "expired login from %s, %s", remotehost, pw->pw_name); reply(530, "Account expired -- contact administrator."); return; } #endif if (setegid((gid_t)pw->pw_gid) < 0) { reply(550, "Can't set gid."); return; } (void) initgroups(pw->pw_name, pw->pw_gid); /* open wtmp before chroot */ ftpdlogwtmp(ttyline, pw->pw_name, remotehost); /* open utmp before chroot */ if (doutmp) { memset((void *)&utmp, 0, sizeof(utmp)); (void)time(&utmp.ut_time); (void)strncpy(utmp.ut_name, pw->pw_name, sizeof(utmp.ut_name)); (void)strncpy(utmp.ut_host, remotehost, sizeof(utmp.ut_host)); (void)strncpy(utmp.ut_line, ttyline, sizeof(utmp.ut_line)); login(&utmp); } /* open stats file before chroot */ if (guest && (stats == 1) && (statfd < 0)) if ((statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND)) < 0) stats = 0; logged_in = 1; dochroot = checkuser(_PATH_FTPCHROOT, pw->pw_name); if (guest || dochroot) { if (multihome && guest) { struct stat ts; /* Compute root directory. */ snprintf(rootdir, sizeof(rootdir), "%s/%s", pw->pw_dir, dhostname); if (stat(rootdir, &ts) < 0) { snprintf(rootdir, sizeof(rootdir), "%s/%s", pw->pw_dir, hostname); } } 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 ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) { char *cp, line[LINE_MAX]; while (fgets(line, sizeof(line), fd) != NULL) { if ((cp = strchr(line, '\n')) != NULL) *cp = '\0'; lreply(230, "%s", line); } (void) fflush(stdout); (void) fclose(fd); } 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); } (void) umask(defumask); return; bad: /* Forget all about it... */ end_login(); } void retrieve(const char *cmd, const char *name) { FILE *fin, *dout; struct stat st; int (*closefunc) __P((FILE *)); time_t start; if (cmd == 0) { fin = fopen(name, "r"), closefunc = fclose; st.st_size = 0; } else { char line[BUFSIZ]; (void) snprintf(line, sizeof(line), cmd, name); name = line; fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; st.st_size = -1; st.st_blksize = BUFSIZ; } if (fin == NULL) { if (errno != 0) { perror_reply(550, name); if (cmd == 0) { LOGCMD("get", name); } } return; } byte_count = -1; if (cmd == 0 && (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode))) { reply(550, "%s: not a plain file.", name); goto done; } if (restart_point) { if (type == TYPE_A) { off_t i, n; int c; n = restart_point; i = 0; while (i++ < n) { if ((c=getc(fin)) == EOF) { perror_reply(550, name); goto done; } if (c == '\n') i++; } } else if (lseek(fileno(fin), restart_point, SEEK_SET) < 0) { perror_reply(550, name); goto done; } } dout = dataconn(name, st.st_size, "w"); if (dout == NULL) goto done; time(&start); send_data(fin, dout, st.st_blksize, st.st_size, (restart_point == 0 && cmd == 0 && S_ISREG(st.st_mode))); if ((cmd == 0) && stats) logxfer(name, st.st_size, start); (void) fclose(dout); data = -1; pdata = -1; done: if (cmd == 0) LOGBYTES("get", name, byte_count); (*closefunc)(fin); } void store(const char *name, const char *mode, int unique) { FILE *fout, *din; int (*closefunc) __P((FILE *)); struct stat st; int fd; if (unique && stat(name, &st) == 0) { char *nam; fd = guniquefd(name, &nam); if (fd == -1) { LOGCMD(*mode == 'w' ? "put" : "append", name); return; } name = nam; if (restart_point) mode = "r+"; fout = fdopen(fd, mode); } else fout = fopen(name, mode); closefunc = fclose; if (fout == NULL) { perror_reply(553, name); LOGCMD(*mode == 'w' ? "put" : "append", name); return; } byte_count = -1; if (restart_point) { if (type == TYPE_A) { off_t i, n; int c; n = restart_point; i = 0; while (i++ < n) { if ((c=getc(fout)) == EOF) { perror_reply(550, name); goto done; } if (c == '\n') i++; } /* * We must do this seek to "current" position * because we are changing from reading to * writing. */ if (fseek(fout, 0L, SEEK_CUR) < 0) { perror_reply(550, name); goto done; } } else if (lseek(fileno(fout), restart_point, SEEK_SET) < 0) { perror_reply(550, name); goto done; } } din = dataconn(name, (off_t)-1, "r"); if (din == NULL) goto done; if (receive_data(din, fout) == 0) { if (unique) reply(226, "Transfer complete (unique file name:%s).", name); else reply(226, "Transfer complete."); } (void) fclose(din); data = -1; pdata = -1; done: LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count); (*closefunc)(fout); } static FILE * getdatasock(const char *mode) { int on = 1, s, t, tries; sigset_t allsigs; if (data >= 0) return (fdopen(data, mode)); sigfillset(&allsigs); sigprocmask (SIG_BLOCK, &allsigs, NULL); (void) seteuid((uid_t)0); s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) goto bad; if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0) goto bad; /* anchor socket to avoid multi-homing problems */ #ifndef __linux__ data_source.sin_len = sizeof(struct sockaddr_in); #endif data_source.sin_family = AF_INET; data_source.sin_addr = ctrl_addr.sin_addr; for (tries = 1; ; tries++) { if (bind(s, (struct sockaddr *)&data_source, sizeof(data_source)) >= 0) break; if (errno != EADDRINUSE || tries > 10) goto bad; sleep(tries); } (void) seteuid((uid_t)pw->pw_uid); sigfillset(&allsigs); sigprocmask (SIG_UNBLOCK, &allsigs, NULL); #ifdef IP_TOS on = IPTOS_THROUGHPUT; if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0) syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); #endif #ifdef TCP_NOPUSH /* * Turn off push flag to keep sender TCP from sending short packets * at the boundaries of each write(). Should probably do a SO_SNDBUF * to set the send buffer size as well, but that may not be desirable * in heavy-load situations. */ on = 1; if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, (char *)&on, sizeof on) < 0) syslog(LOG_WARNING, "setsockopt (TCP_NOPUSH): %m"); #endif #if 0 /* Respect the user's settings */ #ifdef SO_SNDBUF on = 65536; if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&on, sizeof on) < 0) syslog(LOG_WARNING, "setsockopt (SO_SNDBUF): %m"); #endif #endif return (fdopen(s, mode)); 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); errno = t; return (NULL); } static FILE * dataconn(const char *name, off_t size, const char *mode) { char sizebuf[32]; FILE *file; int retry = 0, tos; file_size = size; byte_count = 0; if (size != (off_t) -1) { (void) snprintf(sizebuf, sizeof(sizebuf), " (%lld bytes)", (quad_t) size); } else sizebuf[0] = '\0'; if (pdata >= 0) { struct sockaddr_in from; int s; socklen_t fromlen = sizeof(from); signal (SIGALRM, toolong); (void) alarm ((unsigned) timeout); s = accept(pdata, (struct sockaddr *)&from, &fromlen); (void) alarm (0); if (s < 0) { reply(425, "Can't open data connection."); (void) close(pdata); pdata = -1; return (NULL); } if (ntohs(from.sin_port) < IPPORT_RESERVED) { perror_reply(425, "Can't build data connection"); (void) close(pdata); (void) close(s); pdata = -1; return (NULL); } if (from.sin_addr.s_addr != his_addr.sin_addr.s_addr) { perror_reply(435, "Can't build data connection"); (void) close(pdata); (void) close(s); pdata = -1; return (NULL); } (void) close(pdata); pdata = s; #ifdef IP_TOS tos = IPTOS_THROUGHPUT; (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)); #endif reply(150, "Opening %s mode data connection for '%s'%s.", type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); return (fdopen(pdata, mode)); } if (data >= 0) { reply(125, "Using existing data connection for '%s'%s.", name, sizebuf); usedefault = 1; return (fdopen(data, mode)); } if (usedefault) data_dest = his_addr; usedefault = 1; file = getdatasock(mode); if (file == NULL) { reply(425, "Can't create data socket (%s,%d): %s.", inet_ntoa(data_source.sin_addr), ntohs(data_source.sin_port), strerror(errno)); return (NULL); } data = fileno(file); /* * attempt to connect to reserved port on client machine; * this looks like an attack */ if (ntohs(data_dest.sin_port) < IPPORT_RESERVED || ntohs(data_dest.sin_port) == 2049) { /* XXX */ perror_reply(425, "Can't build data connection"); (void) fclose(file); data = -1; return NULL; } if (data_dest.sin_addr.s_addr != his_addr.sin_addr.s_addr) { perror_reply(435, "Can't build data connection"); (void) fclose(file); data = -1; return NULL; } while (connect(data, (struct sockaddr *)&data_dest, sizeof(data_dest)) < 0) { if (errno == EADDRINUSE && retry < swaitmax) { sleep((unsigned) swaitint); retry += swaitint; continue; } perror_reply(425, "Can't build data connection"); (void) fclose(file); data = -1; return (NULL); } reply(150, "Opening %s mode data connection for '%s'%s.", type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); return (file); } /* * Tranfer the contents of "instr" to "outstr" peer using the appropriate * encapsulation of the data subject to Mode, Structure, and Type. * * NB: Form isn't handled. */ static void send_data(FILE *instr, FILE *outstr, off_t blksize, off_t filesize, int isreg) { int c, cnt, filefd, netfd; char *buf, *bp; size_t len,size; transflag++; if (setjmp(urgcatch)) { transflag = 0; return; } switch (type) { case TYPE_A: while ((c = getc(instr)) != EOF) { byte_count++; if (c == '\n') { if (ferror(outstr)) goto data_err; (void) putc('\r', outstr); } (void) putc(c, outstr); } fflush(outstr); transflag = 0; if (ferror(instr)) goto file_err; if (ferror(outstr)) goto data_err; reply(226, "Transfer complete."); return; case TYPE_I: case TYPE_L: /* * isreg is only set if we are not doing restart and we * are sending a regular file */ netfd = fileno(outstr); filefd = fileno(instr); if (isreg && filesize < (off_t)16 * 1024 * 1024) { buf = mmap(0, filesize, PROT_READ, MAP_SHARED, filefd, (off_t)0); if (buf==MAP_FAILED || buf==NULL) { syslog(LOG_WARNING, "mmap(%lu): %m", (unsigned long)filesize); goto oldway; } bp = buf; len = filesize; do { cnt = write(netfd, bp, len); len -= cnt; bp += cnt; if (cnt > 0) byte_count += cnt; } while(cnt > 0 && len > 0); transflag = 0; munmap(buf, (size_t)filesize); if (cnt < 0) goto data_err; reply(226, "Transfer complete."); return; } oldway: size = blksize * 16; if ((buf = malloc(size)) == NULL) { transflag = 0; perror_reply(451, "Local resource failure: malloc"); return; } #ifdef TCP_CORK { int on = 1; setsockopt(netfd, SOL_TCP, TCP_CORK, &on, sizeof on); /* failure is harmless */ } #endif while ((cnt = read(filefd, buf, size)) > 0 && write(netfd, buf, cnt) == cnt) byte_count += cnt; #ifdef TCP_CORK { int off = 0; setsockopt(netfd, SOL_TCP, TCP_CORK, &off, sizeof off); } #endif transflag = 0; (void)free(buf); if (cnt != 0) { if (cnt < 0) goto file_err; goto data_err; } reply(226, "Transfer complete."); return; default: transflag = 0; reply(550, "Unimplemented TYPE %d in send_data", type); return; } data_err: transflag = 0; perror_reply(426, "Data connection"); return; file_err: transflag = 0; perror_reply(551, "Error on input file"); } /* * Transfer data from peer to "outstr" using the appropriate encapulation of * the data subject to Mode, Structure, and Type. * * N.B.: Form isn't handled. */ static int receive_data(FILE *instr, FILE *outstr) { int c; int cnt; volatile int bare_lfs = 0; char buf[BUFSIZ]; transflag++; if (setjmp(urgcatch)) { transflag = 0; return (-1); } switch (type) { case TYPE_I: case TYPE_L: signal (SIGALRM, lostconn); do { (void) alarm ((unsigned) timeout); cnt = read(fileno(instr), buf, sizeof(buf)); (void) alarm (0); if (cnt > 0) { if (write(fileno(outstr), buf, cnt) != cnt) goto file_err; byte_count += cnt; } } while (cnt > 0); if (cnt < 0) goto data_err; transflag = 0; return (0); case TYPE_E: reply(553, "TYPE E not implemented."); transflag = 0; return (-1); case TYPE_A: while ((c = getc(instr)) != EOF) { byte_count++; if (c == '\n') bare_lfs++; while (c == '\r') { if (ferror(outstr)) goto data_err; if ((c = getc(instr)) != '\n') { (void) putc ('\r', outstr); if (c == '\0' || c == EOF) goto contin2; } } (void) putc(c, outstr); contin2: ; } fflush(outstr); if (ferror(instr)) goto data_err; if (ferror(outstr)) goto file_err; transflag = 0; if (bare_lfs) { lreply(226, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs); (void)printf(" File may not have transferred correctly.\r\n"); } return (0); default: reply(550, "Unimplemented TYPE %d in receive_data", type); transflag = 0; return (-1); } data_err: transflag = 0; perror_reply(426, "Data Connection"); return (-1); file_err: transflag = 0; perror_reply(452, "Error writing file"); return (-1); } void statfilecmd(char *filename) { FILE *fin; int c; char line[LINE_MAX]; (void)snprintf(line, sizeof(line), "/bin/ls -lgA %s", filename); fin = ftpd_popen(line, "r"); lreply(211, "status of %s:", filename); while ((c = getc(fin)) != EOF) { if (c == '\n') { if (ferror(stdout)){ perror_reply(421, "control connection"); (void) ftpd_pclose(fin); dologout(1); /* NOTREACHED */ } if (ferror(fin)) { perror_reply(551, filename); (void) ftpd_pclose(fin); return; } (void) putc('\r', stdout); } (void) putc(c, stdout); } (void) ftpd_pclose(fin); reply(211, "End of Status"); } void statcmd(void) { struct sockaddr_in *sn; u_char *a, *p; lreply(211, "%s FTP server status:", hostname, version); printf(" %s\r\n", version); printf(" Connected to %s", remotehost); if (!isdigit(remotehost[0])) printf(" (%s)", inet_ntoa(his_addr.sin_addr)); printf("\r\n"); if (logged_in) { if (guest) printf(" Logged in anonymously\r\n"); else printf(" Logged in as %s\r\n", pw->pw_name); } else if (askpasswd) printf(" Waiting for password\r\n"); else printf(" Waiting for user name\r\n"); printf(" TYPE: %s", typenames[type]); if (type == TYPE_A || type == TYPE_E) printf(", FORM: %s", formnames[form]); if (type == TYPE_L) #if CHAR_BIT == 8 printf(" %d", CHAR_BIT); #else printf(" %d", bytesize); /* need definition! */ #endif printf("; STRUcture: %s; transfer MODE: %s\r\n", strunames[stru], modenames[mode]); if (data != -1) printf(" Data connection open\r\n"); else if (pdata != -1) { printf(" in Passive mode"); sn = &pasv_addr; goto printaddr; } else if (usedefault == 0) { printf(" PORT"); sn = &data_dest; printaddr: a = (u_char *) &sn->sin_addr; p = (u_char *) &sn->sin_port; #define UC(b) (((int) b) & 0xff) printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); #undef UC } else printf(" No data connection\r\n"); reply(211, "End of status"); } void fatal(const char *s) { reply(451, "Error in server: %s\n", s); reply(221, "Closing connection due to server error."); dologout(0); /* NOTREACHED */ } void #ifdef __STDC__ reply(int n, const char *fmt, ...) #else reply(int n, char *fmt, va_dcl va_alist) #endif { va_list ap; #ifdef __STDC__ va_start(ap, fmt); #else va_start(ap); #endif (void)printf("%d ", n); (void)vprintf(fmt, ap); (void)printf("\r\n"); (void)fflush(stdout); if (debug) { syslog(LOG_DEBUG, "<--- %d ", n); vsyslog(LOG_DEBUG, fmt, ap); } } void #ifdef __STDC__ lreply(int n, const char *fmt, ...) #else lreply(n, fmt, va_alist) int n; char *fmt; va_dcl #endif { va_list ap; #ifdef __STDC__ va_start(ap, fmt); #else va_start(ap); #endif (void)printf("%d- ", n); (void)vprintf(fmt, ap); (void)printf("\r\n"); (void)fflush(stdout); if (debug) { syslog(LOG_DEBUG, "<--- %d- ", n); vsyslog(LOG_DEBUG, fmt, ap); } } static void ack(const char *s) { reply(250, "%s command successful.", s); } void nack(const char *s) { reply(502, "%s command not implemented.", s); } /* ARGSUSED */ void yyerror(char *s) { char *cp; (void)s; /* ignore argument */ if ((cp = strchr(cbuf,'\n'))!=NULL) *cp = '\0'; reply(500, "'%s': command not understood.", cbuf); } void delete(char *name) { struct stat st; LOGCMD("delete", name); if (stat(name, &st) < 0) { perror_reply(550, name); return; } if ((st.st_mode&S_IFMT) == S_IFDIR) { if (rmdir(name) < 0) { perror_reply(550, name); return; } goto done; } if (unlink(name) < 0) { perror_reply(550, name); return; } done: ack("DELE"); } void cwd(const char *path) { FILE *message; if (chdir(path) < 0) perror_reply(550, path); else { if ((message = fopen(_PATH_CWDMESG, "r")) != NULL) { char *cp, line[LINE_MAX]; while (fgets(line, sizeof(line), message) != NULL) { if ((cp = strchr(line, '\n')) != NULL) *cp = '\0'; lreply(250, "%s", line); } (void) fflush(stdout); (void) fclose(message); } ack("CWD"); } } void replydirname(const char *name, const char *message) { char npath[MAXPATHLEN]; int i; for (i = 0; *name != '\0' && i < (int)sizeof(npath) - 1; i++, name++) { npath[i] = *name; if (*name == '"') npath[++i] = '"'; } npath[i] = '\0'; reply(257, "\"%s\" %s", npath, message); } void makedir(char *name) { LOGCMD("mkdir", name); if (mkdir(name, 0777) < 0) perror_reply(550, name); else replydirname(name, "directory created."); } void removedir(char *name) { LOGCMD("rmdir", name); if (rmdir(name) < 0) perror_reply(550, name); else ack("RMD"); } void pwd(void) { char path[MAXPATHLEN]; if (getcwd(path, sizeof path) == (char *)NULL) reply(550, "%s.", path); else replydirname(path, "is current directory."); } char * renamefrom(char *name) { struct stat st; if (stat(name, &st) < 0) { perror_reply(550, name); return ((char *)0); } reply(350, "File exists, ready for destination name"); return (name); } void renamecmd(char *from, char *to) { LOGCMD2("rename", from, to); if (rename(from, to) < 0) perror_reply(550, "rename"); else ack("RNTO"); } static void dolog(struct sockaddr_in *sn) { struct hostent *hp = gethostbyaddr((char *)&sn->sin_addr, sizeof(struct in_addr), AF_INET); if (hp) (void) strncpy(remotehost, hp->h_name, sizeof(remotehost)-1); else (void) strncpy(remotehost, inet_ntoa(sn->sin_addr), sizeof(remotehost)-1); remotehost[sizeof(remotehost)-1] = '\0'; #ifdef HASSETPROCTITLE snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost); setproctitle("%s", proctitle); #endif /* HASSETPROCTITLE */ if (logging) syslog(LOG_INFO, "connection from %s", remotehost); } /* * Record logout in wtmp file * and exit with supplied status. */ void dologout(int status) { sigset_t allsigs; transflag = 0; if (logged_in) { sigfillset(&allsigs); sigprocmask(SIG_BLOCK, &allsigs, NULL); (void) seteuid((uid_t)0); ftpdlogwtmp(ttyline, "", ""); if (doutmp) logout(utmp.ut_line); #if defined(KERBEROS) if (!notickets && krbtkfile_env) unlink(krbtkfile_env); #endif } /* beware of flushing buffers after a SIGPIPE */ _exit(status); } static void myoob(int signo) { char *cp; int save_errno = errno; (void)signo; /* only process if transfer occurring */ if (!transflag) return; cp = tmpline; if (ftpd_getline(cp, 7, stdin) == NULL) { reply(221, "You could at least say goodbye."); dologout(0); } upper(cp); if (strcmp(cp, "ABOR\r\n") == 0) { tmpline[0] = '\0'; reply(426, "Transfer aborted. Data connection closed."); reply(226, "Abort successful"); longjmp(urgcatch, 1); } if (strcmp(cp, "STAT\r\n") == 0) { if (file_size != (off_t) -1) reply(213, "Status: %qd of %qd bytes transferred", (quad_t) byte_count, (quad_t) file_size); else reply(213, "Status: %qd bytes transferred", (quad_t)byte_count); } errno = save_errno; } /* * Note: a response of 425 is not mentioned as a possible response to * the PASV command in RFC959. However, it has been blessed as * a legitimate response by Jon Postel in a telephone conversation * with Rick Adams on 25 Jan 89. */ void passive(void) { socklen_t len; #ifdef IP_PORTRANGE int on; #else u_short port; #endif char *p, *a; if (pw == NULL) { reply(530, "Please login with USER and PASS"); return; } if (pdata >= 0) close(pdata); pdata = socket(AF_INET, SOCK_STREAM, 0); if (pdata < 0) { perror_reply(425, "Can't open passive connection"); return; } #ifdef IP_PORTRANGE on = high_data_ports ? IP_PORTRANGE_HIGH : IP_PORTRANGE_DEFAULT; if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE, (char *)&on, sizeof(on)) < 0) goto pasv_error; #else #define FTP_DATA_BOTTOM 40000 #define FTP_DATA_TOP 44999 if (high_data_ports) { for (port = FTP_DATA_BOTTOM; port <= FTP_DATA_TOP; port++) { pasv_addr = ctrl_addr; pasv_addr.sin_port = htons(port); if (bind(pdata, (struct sockaddr *) &pasv_addr, sizeof(pasv_addr)) == 0) break; if (errno != EADDRINUSE) goto pasv_error; } if (port > FTP_DATA_TOP) goto pasv_error; } else #endif { pasv_addr = ctrl_addr; pasv_addr.sin_port = 0; if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) goto pasv_error; } len = sizeof(pasv_addr); if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) goto pasv_error; if (listen(pdata, 1) < 0) goto pasv_error; a = (char *) &pasv_addr.sin_addr; p = (char *) &pasv_addr.sin_port; #define UC(b) (((int) b) & 0xff) reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); return; pasv_error: (void) close(pdata); pdata = -1; perror_reply(425, "Can't open passive connection"); return; } /* * Generate unique name for file with basename "local". * The file named "local" is already known to exist. * Generates failure reply on error. */ static int guniquefd(const char *local, char **nam) { static char new[MAXPATHLEN]; struct stat st; int count, len, fd; char *cp; cp = strrchr(local, '/'); if (cp) *cp = '\0'; if (stat(cp ? local : ".", &st) < 0) { perror_reply(553, cp ? local : "."); return (-1); } if (cp) *cp = '/'; (void) strncpy(new, local, sizeof(new)-1); new[sizeof(new)-1] = '\0'; len = strlen(new); if (len+2+1 >= (int)sizeof(new)-1) return (-1); cp = new + len; *cp++ = '.'; for (count = 1; count < 100; count++) { (void)snprintf(cp, sizeof(new) - (cp - new), "%d", count); fd = open(new, O_RDWR|O_CREAT|O_EXCL, 0666); if (fd == -1) continue; if (nam) *nam = new; return (fd); } reply(452, "Unique file name cannot be created."); return (-1); } /* * Format and send reply containing system error number. */ void perror_reply(int code, const char *string) { reply(code, "%s: %s.", string, strerror(errno)); } static const char *onefile[] = { "", 0 }; void send_file_list(const char *whichf) { struct stat st; DIR *dirp = NULL; struct dirent *dir; FILE *volatile dout = NULL; char const *const *volatile dirlist; const char *dirname; volatile int simple = 0; volatile int freeglob = 0; glob_t gl; /* XXX: should the { go away if __linux__? */ if (strpbrk(whichf, "~{[*?") != NULL) { #ifdef __linux__ /* see popen.c */ int flags = GLOB_NOCHECK; #else int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; #endif memset(&gl, 0, sizeof(gl)); freeglob = 1; if (glob(whichf, flags, 0, &gl)) { reply(550, "not found"); goto out; } else if (gl.gl_pathc == 0) { errno = ENOENT; perror_reply(550, whichf); goto out; } /* The cast is necessary because of bugs in C's type system */ dirlist = (char const *const *) gl.gl_pathv; } else { onefile[0] = whichf; dirlist = onefile; simple = 1; } if (setjmp(urgcatch)) { transflag = 0; goto out; } while ((dirname = *dirlist++)!=NULL) { if (stat(dirname, &st) < 0) { /* * If user typed "ls -l", etc, and the client * used NLST, do what the user meant. */ if (dirname[0] == '-' && *dirlist == NULL && transflag == 0) { retrieve("/bin/ls %s", dirname); goto out; } perror_reply(550, whichf); if (dout != NULL) { (void) fclose(dout); transflag = 0; data = -1; pdata = -1; } goto out; } if (S_ISREG(st.st_mode)) { if (dout == NULL) { dout = dataconn("file list", (off_t)-1, "w"); if (dout == NULL) goto out; transflag++; } fprintf(dout, "%s%s\n", dirname, type == TYPE_A ? "\r" : ""); byte_count += strlen(dirname) + 1; continue; } else if (!S_ISDIR(st.st_mode)) continue; if ((dirp = opendir(dirname)) == NULL) continue; while ((dir = readdir(dirp)) != NULL) { char nbuf[MAXPATHLEN]; #ifdef __linux__ if (!strcmp(dir->d_name, ".")) continue; if (!strcmp(dir->d_name, "..")) continue; #else if (dir->d_name[0] == '.' && dir->d_namlen == 1) continue; if (dir->d_name[0] == '.' && dir->d_name[1] == '.' && dir->d_namlen == 2) continue; #endif snprintf(nbuf, sizeof(nbuf), "%s/%s", dirname, dir->d_name); /* * We have to do a stat to insure it's * not a directory or special file. */ if (simple || (stat(nbuf, &st) == 0 && S_ISREG(st.st_mode))) { if (dout == NULL) { dout = dataconn("file list", (off_t)-1, "w"); if (dout == NULL) goto out; transflag++; } if (nbuf[0] == '.' && nbuf[1] == '/') fprintf(dout, "%s%s\n", &nbuf[2], type == TYPE_A ? "\r" : ""); else fprintf(dout, "%s%s\n", nbuf, type == TYPE_A ? "\r" : ""); byte_count += strlen(nbuf) + 1; } } (void) closedir(dirp); } if (dout == NULL) 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); data = -1; pdata = -1; out: if (freeglob) { freeglob = 0; globfree(&gl); } } static void reapchild(int signo) { int save_errno = errno; (void)signo; while (wait3(NULL, WNOHANG, NULL) > 0) ; errno = save_errno; } void logxfer(const char *name, off_t size, time_t start) { char buf[400 + MAXHOSTNAMELEN*4 + MAXPATHLEN*4]; char dir[MAXPATHLEN], path[MAXPATHLEN], rpath[MAXPATHLEN]; char vremotehost[MAXHOSTNAMELEN*4], vpath[MAXPATHLEN*4]; char *vpw; time_t now; if ((statfd >= 0) && (getcwd(dir, sizeof(dir)) != NULL)) { time(&now); vpw = (char *)malloc(strlen((guest) ? guestpw : pw->pw_name)*4+1); if (vpw == NULL) return; snprintf(path, sizeof path, "%s/%s", dir, name); if (realpath(path, rpath) == NULL) { strncpy(rpath, path, sizeof rpath-1); rpath[sizeof rpath-1] = '\0'; } strvis(vpath, rpath, VIS_SAFE|VIS_NOSLASH); strvis(vremotehost, remotehost, VIS_SAFE|VIS_NOSLASH); strvis(vpw, (guest) ? guestpw : pw->pw_name, VIS_SAFE|VIS_NOSLASH); snprintf(buf, sizeof(buf), "%.24s %ld %s %qd %s %c %s %c %c %s ftp %d %s %s\n", ctime(&now), (long)(now - start + (now == start)), vremotehost, (long long) size, vpath, ((type == TYPE_A) ? 'a' : 'b'), "*" /* none yet */, 'o', ((guest) ? 'a' : 'r'), vpw, 0 /* none yet */, ((guest) ? "*" : pw->pw_name), dhostname); write(statfd, buf, strlen(buf)); free(vpw); } } #if defined(TCPWRAPPERS) static int check_host(struct sockaddr_in *sin) { struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr, sizeof(struct in_addr), AF_INET); char *addr = inet_ntoa(sin->sin_addr); if (hp) { if (!hosts_ctl("ftpd", hp->h_name, addr, STRING_UNKNOWN)) { syslog(LOG_NOTICE, "tcpwrappers rejected: %s [%s]", hp->h_name, addr); return (0); } } else { if (!hosts_ctl("ftpd", STRING_UNKNOWN, addr, STRING_UNKNOWN)) { syslog(LOG_NOTICE, "tcpwrappers rejected: [%s]", addr); return (0); } } return (1); } #endif /* TCPWRAPPERS */ linux-ftpd-0.17/ftpd/ftpusers.5100644 144 144 1757 7141140311 15737 0ustar dhollandpeople.\" Copyright (c) 1994 Peter Tobias (tobias@server.et-inf.fho-emden.de), .\" This file may be distributed under the GNU General Public License. .\" .\" $Id: ftpusers.5,v 1.11 2000/07/30 23:56:57 dholland Exp $ .\" .Dd April 22, 1994 .Dt FTPUSERS 5 .Os "Linux NetKit (0.17)" .Sh NAME .Nm ftpusers .Nd file which lists users who are not allowed to use ftp .Sh DESCRIPTION .Pa /etc/ftpusers is used by .Xr ftpd 8 ; the file contains a list of users who are not allowed to use the ftp command. For security reasons at least users like ``root'', ``bin'', ``uucp'' and ``news'' should be listed in this file. Blank lines and lines beginning with `#' are ignored. .Pp Note: a lines with `#' in the .Em middle is .Em not a comment. Don't put `#' after a name to comment it; use another line, or things will silently fail on you. .Sh EXAMPLES .Pa /etc/ftpusers might contain the following entries: .Bd -literal # # /etc/ftpusers # root uucp news .Ed .Sh FILES .Pa /etc/ftpusers .Sh SEE ALSO .Xr ftp 1 , .Xr ftpd 8 linux-ftpd-0.17/ftpd/logutmp.c100644 144 144 7511 6743503426 15644 0ustar dhollandpeople/* * Portions Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * Portions Copyright (c) 1996, Jason Downs. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * From: OpenBSD: logutmp.c,v 1.1 1996/06/18 10:09:23 downsj Exp * From: OpenBSD: logutmp.c,v 1.2 1998/07/13 02:11:17 millert Exp */ char logutmp_rcsid[] = "$Id: logutmp.c,v 1.4 1999/07/16 01:12:54 dholland Exp $"; #include #include #include #include #include #include #include #ifndef __linux__ #include #endif #include "extern.h" typedef struct utmp UTMP; #ifndef __linux__ static int fd = -1; static int topslot = -1; #endif /* * Special versions of login()/logout() which hold the utmp file open, * for use with ftpd. */ void login(const UTMP *ut) { #ifndef __linux__ UTMP ubuf; /* * First, loop through /etc/ttys, if needed, to initialize the * top of the tty slots, since ftpd has no tty. */ if (topslot < 0) { topslot = 0; while (getttyent() != (struct ttyent *)NULL) topslot++; } if ((topslot < 0) || ((fd < 0) && (fd = open(_PATH_UTMP, O_RDWR|O_CREAT, 0644)) < 0)) return; /* * Now find a slot that's not in use... */ (void)lseek(fd, (off_t)(topslot * sizeof(UTMP)), SEEK_SET); while (1) { if (read(fd, &ubuf, sizeof(UTMP)) == sizeof(UTMP)) { if (!ubuf.ut_name[0]) { (void)lseek(fd, -(off_t)sizeof(UTMP), SEEK_CUR); break; } topslot++; } else { (void)lseek(fd, (off_t)(topslot * sizeof(UTMP)), SEEK_SET); break; } } (void)write(fd, ut, sizeof(UTMP)); #else (void)ut; #endif } int logout(register const char *line) { #ifndef __linux__ UTMP ut; int rval; rval = 0; if (fd < 0) return(rval); (void)lseek(fd, 0, SEEK_SET); while (read(fd, &ut, sizeof(UTMP)) == sizeof(UTMP)) { if (!ut.ut_name[0] || strncmp(ut.ut_line, line, UT_LINESIZE)) continue; bzero(ut.ut_name, UT_NAMESIZE); bzero(ut.ut_host, UT_HOSTSIZE); (void)time(&ut.ut_time); (void)lseek(fd, -(off_t)sizeof(UTMP), SEEK_CUR); (void)write(fd, &ut, sizeof(UTMP)); rval = 1; } return(rval); #else (void)line; return 1; #endif } linux-ftpd-0.17/ftpd/logwtmp.c100644 144 144 6006 6743477025 15651 0ustar dhollandpeople/* * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /* * From: @(#)logwtmp.c 8.1 (Berkeley) 6/4/93 * From: NetBSD: logwtmp.c,v 1.4 1995/04/11 02:44:58 cgd Exp * From: OpenBSD: ? */ char logwtmp_rcsid[] = "$Id: logwtmp.c,v 1.5 1999/07/16 00:34:29 dholland Exp $"; #include #include #include #include #include #include #include #include #include "extern.h" static int fd = -1; /* * Modified version of logwtmp that holds wtmp file open * after first call, for use with ftp (which may chroot * after login, but before logout). */ void ftpdlogwtmp(const char *line, const char *name, const char *host) { struct utmp ut; struct stat buf; if (fd < 0 && (fd = open(_PATH_WTMP, O_WRONLY|O_APPEND, 0)) < 0) return; if (fstat(fd, &buf) == 0) { memset(&ut, 0, sizeof(ut)); #ifdef USER_PROCESS ut.ut_type = *name ? USER_PROCESS : DEAD_PROCESS; ut.ut_pid = getpid(); #endif (void)strncpy(ut.ut_line, line, sizeof(ut.ut_line)); (void)strncpy(ut.ut_name, name, sizeof(ut.ut_name)); (void)strncpy(ut.ut_host, host, sizeof(ut.ut_host)); (void)time(&ut.ut_time); if (write(fd, (char *)&ut, sizeof(struct utmp)) != sizeof(struct utmp)) (void)ftruncate(fd, buf.st_size); } } linux-ftpd-0.17/ftpd/pathnames.h100644 144 144 4517 6743461246 16150 0ustar dhollandpeople/* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)pathnames.h 8.1 (Berkeley) 6/4/93 * NetBSD: pathnames.h,v 1.5 1995/04/11 02:44:59 cgd Exp * OpenBSD: pathnames.h,v 1.2 1996/07/28 22:42:46 downsj Exp * OpenBSD: pathnames.h,v 1.3 1997/03/25 22:47:08 millert Exp * $Id: pathnames.h,v 1.3 1999/07/15 22:37:26 dholland Exp $ */ #include #define _PATH_FTPUSERS "/etc/ftpusers" #define _PATH_FTPCHROOT "/etc/ftpchroot" #define _PATH_FTPWELCOME "/etc/ftpwelcome" #define _PATH_FTPLOGINMESG "/etc/motd" #define _PATH_FTPDSTATFILE "/var/log/ftpd" #define _PATH_CWDMESG ".message" linux-ftpd-0.17/ftpd/popen.c100644 144 144 14375 6743503426 15324 0ustar dhollandpeople/* * Copyright (c) 1988, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software written by Ken Arnold and * published in UNIX Review, Vol. 6, No. 8. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /* * From: @(#)popen.c 8.3 (Berkeley) 4/6/94 * From: NetBSD: popen.c,v 1.5 1995/04/11 02:45:00 cgd Exp * From: OpenBSD: popen.c,v 1.8 1996/12/07 10:52:06 bitblt Exp * From: OpenBSD: popen.c,v 1.10 1999/02/26 00:15:54 art Exp */ char popen_rcsid[] = "$Id: popen.c,v 1.6 1999/07/16 01:12:54 dholland Exp $"; #include #include #include #include #include #include #include #include #include #include #include "extern.h" /* * Special version of popen which avoids call to shell. This ensures noone * may create a pipe to a hidden program as a side effect of a list or dir * command. * * Note: destroys "program". */ static int *pids; static int fds; #define MAX_ARGV 100 #define MAX_GARGV 1000 FILE * ftpd_popen(char *program, const char *type) { char *cp; FILE *volatile iop; int argc, gargc, pdes[2], pid, i; char **pop, *argv[MAX_ARGV], *gargv[MAX_GARGV]; if ((*type != 'r' && *type != 'w') || type[1]) return (NULL); if (!pids) { if ((fds = getdtablesize()) <= 0) return (NULL); if ((pids = (int *)malloc((u_int)(fds * sizeof(int)))) == NULL) return (NULL); memset(pids, 0, fds * sizeof(int)); } if (pipe(pdes) < 0) return (NULL); /* break up string into pieces */ for (argc = 0, cp = program;argc < MAX_ARGV-1; cp = NULL) if (!(argv[argc++] = strtok(cp, " \t\n"))) break; argv[MAX_ARGV-1] = NULL; /* glob each piece */ gargv[0] = argv[0]; for (gargc = argc = 1; argv[argc]; argc++) { glob_t gl; #ifdef __linux__ /* * GLOB_QUOTE is default behavior. GLOB_BRACE and GLOB_TILDE * aren't available... */ int flags = GLOB_NOCHECK; #else int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; #endif memset(&gl, 0, sizeof(gl)); if (glob(argv[argc], flags, NULL, &gl)) { if (gargc < MAX_GARGV-1) { gargv[gargc++] = strdup(argv[argc]); if (gargv[gargc -1] == NULL) fatal ("Out of memory"); } } else for (pop = gl.gl_pathv; *pop && gargc < MAX_GARGV-1; pop++) { gargv[gargc++] = strdup(*pop); if (gargv[gargc - 1] == NULL) fatal ("Out of memory"); } globfree(&gl); } gargv[gargc] = NULL; iop = NULL; switch(pid = fork()) { case -1: /* error */ (void)close(pdes[0]); (void)close(pdes[1]); goto pfree; /* NOTREACHED */ case 0: /* child */ if (*type == 'r') { if (pdes[1] != STDOUT_FILENO) { dup2(pdes[1], STDOUT_FILENO); (void)close(pdes[1]); } dup2(STDOUT_FILENO, STDERR_FILENO); /* stderr too! */ (void)close(pdes[0]); } else { if (pdes[0] != STDIN_FILENO) { dup2(pdes[0], STDIN_FILENO); (void)close(pdes[0]); } (void)close(pdes[1]); } closelog(); /* * Close all unnecessary file descriptors and drop any privs * before running external programs. */ for (i=3; i nor bison output pass this) # -Wshadow (seems to produce only bogus warnings) # -Wredundant-decls (there are a number of such in Linux's /usr/include) ALLWARNINGS CC CFLAGS LDFLAGS LIBS YACC # Features BSDSIGNAL SHADOW FN(crypt) FN(snprintf) TYPE(socklen_t) linux-ftpd-0.17/Makefile100644 144 144 747 7024720304 14465 0ustar dhollandpeople# You can do "make SUB=blah" to make only a few, or edit here, or both # You can also run make directly in the subdirs you want. SUB = support ftpd %.build: (cd $(patsubst %.build, %, $@) && $(MAKE)) %.install: (cd $(patsubst %.install, %, $@) && $(MAKE) install) %.clean: (cd $(patsubst %.clean, %, $@) && $(MAKE) clean) all: $(patsubst %, %.build, $(SUB)) install: $(patsubst %, %.install, $(SUB)) clean: $(patsubst %, %.clean, $(SUB)) distclean: clean rm -f MCONFIG linux-ftpd-0.17/README100644 144 144 10533 7141137753 13751 0ustar dhollandpeopleThis is linux-ftpd-0.17 for Linux, a port of the OpenBSD ftp daemon. This package updates linux-ftpd-0.16. If you're reading this off a CD, go right away and check the net archives for later versions and security fixes. As of this writing the home site for NetKit is ftp://ftp.uk.linux.org/pub/linux/Networking/netkit Contents: ftpd Daemon for the ftp protocol. Requires: Working compiler, libc, and kernel. Supports shadow passwords. Does not (yet) support PAM. Security: This version contains vital fixes for a remotely exploitable hole relating to printf format string processing. linux-ftpd-0.16 and earlier should not be used. Restrictions: This version does not put ftp users in utmp; the utmp code is #if'd out. This means the -U option does not work. Keep in mind that the man page has not been completely edited for Linux and may refer to a few BSDisms. Port information: The source was retrieved from ftp.openbsd.org on Thu Jul 15 19:38:16 EDT 1999 including the files extern.h ftpcmd.y ftpd.8 ftpd.c logutmp.c logwtmp.c pathnames.h popen.c These came from src/libexec/ftpd. The support files came from various sources, including OpenBSD's libc, NetKit 0.09, and sendmail 8.7.4. (8.7.4 because it was handy, not for any particular reason.) Others were written for this port. Installation: Do "./configure --help" and decide what options you want. The defaults should be suitable for most Linux systems. Then run the configure script. Do "make" to compile. Then (as root) do "make install". Save a backup copy of any mission-critical program in case the new one doesn't work, and so forth. We warned you. If you get gcc warnings from files in /usr/include, they are due to problems in your libc, not netkit. (You may only see them when compiling netkit because netkit turns on a lot of compiler warnings.) DEC CC: The DEC compiler for the Alpha is now freely available. This is a much better compiler with gcc, that is, it generates much better code. If you have the DEC compiler, you can explicitly use the DEC compiler instead of gcc by configuring like this: ./configure --with-c-compiler=ccc It is known to generate spurious warnings on some files. Also, some headers from some versions of glibc confuse it; that may prevent netkit from working. Other problems should be reported as bugs. Bugs: Send bug reports and whatnot to the netkit bug address, not to OpenBSD, as the OpenBSD organization doesn't have anything official to do with this port. Things will be forwarded if necessary. Please make sure the header files in /usr/include match the libc version installed in /lib and /usr/lib. If you have weird problems this is the most likely culprit. Also, before reporting a bug, be sure you're working with the latest version. If something doesn't compile for you, fix it and send diffs. If you can't, send the compiler's error output. If it compiles but doesn't work, send as complete a bug report as you can. Patches and fixes are welcome, as long as you describe adequately what they're supposed to fix. Please, one patch per distinct fix. Please do NOT send the whole archive back or reindent the source. Be sure to send all correspondence in e-mail to the netkit address. Postings to netnews or mailing lists will not be seen due to the enormous volume. Also, anything that doesn't get filed in the bug database is quite likely to end up forgotten. Please don't report known bugs (see the BUGS file(s)) unless you are including fixes. :-) Mail should be sent to: netbug@ftp.uk.linux.org Early in April 2000, a hacker broke into the machine that was hosting the netkit bug database for me and trashed it. Unfortunately, it seems backups hadn't gotten done for a while, so three months of mail (since mid-January) was lost. So, if you sent something and didn't hear back, or you sent something, heard back, but the changes failed to appear in this release (unlikely but possible) - please resend. Please see http://www.hcs.harvard.edu/~dholland/computers/netkit.html if you are curious why it was so long between the 0.10 and 0.16 releases. Future plans for netkit maintenance are still up in the air, but in the meantime new releases will still appear from time to time. I don't have a whole lot of cycles to spare to work on netkit, so things are likely to continue to be fairly slow. David A. Holland 23 July 2000 linux-ftpd-0.17/configure100755 144 144 24151 7140615674 15003 0ustar dhollandpeople#!/bin/sh # # This file was generated by confgen version 2. # Do not edit. # PREFIX='/usr' #EXECPREFIX='$PREFIX' INSTALLROOT='' BINMODE='755' #DAEMONMODE='$BINMODE' MANMODE='644' while [ x$1 != x ]; do case $1 in --help) cat < __conftest.c int main() { int class=0; return class; } EOF if [ x"$CC" = x ]; then echo -n 'Looking for a C compiler... ' for TRY in egcs gcc g++ CC c++ cc; do ( $TRY __conftest.c -o __conftest || exit 1; ./__conftest || exit 1; ) >/dev/null 2>&1 || continue; CC=$TRY break; done if [ x"$CC" = x ]; then echo 'failed.' echo 'Cannot find a C compiler. Run configure with --with-c-compiler.' rm -f __conftest* exit fi echo "$CC" else echo -n 'Checking if C compiler works... ' if ( $CC __conftest.c -o __conftest || exit 1 ./__conftest || exit 1 ) >/dev/null 2>&1; then echo 'yes' else echo 'no' echo 'Compiler '"$CC"' does not exist or cannot compile C; try another.' rm -f __conftest* exit fi fi echo -n "Checking if $CC accepts gcc warnings... " if ( $CC $WARNINGS __conftest.c -o __conftest || exit 1 ) >/dev/null 2>&1; then echo 'yes' CC_WARNINGS=1 else echo 'no' fi if [ x$DEBUG = x ]; then echo -n "Checking if $CC accepts -O2... " if ( $CC -O2 __conftest.c -o __conftest ) >/dev/null 2>&1; then echo 'yes' CFLAGS="$CFLAGS -O2" else echo 'no' echo -n "Checking if $CC accepts -O... " if ( $CC -O __conftest.c -o __conftest ) >/dev/null 2>&1; then echo 'yes' CFLAGS="$CFLAGS -O" else echo 'no' fi fi else echo -n "Checking if $CC accepts -g... " if ( $CC -g __conftest.c -o __conftest ) >/dev/null 2>&1; then echo 'yes' CFLAGS="$CFLAGS -g" else echo 'no' fi fi LDFLAGS= LIBS= rm -f __conftest* ################################################## echo -n 'Checking for yacc... ' mkdir __conftest cat <__conftest/test.y %token X %start ALL %% ALL: X; %% EOF for TRY in 'bison -y' yacc; do ( cd __conftest $TRY test.y || exit 1 test -f y.tab.c || exit 1 ) >/dev/null 2>&1 || continue; echo "$TRY" YACC="$TRY" break; done rm -rf __conftest if [ x"$YACC" = x ]; then echo 'not found' echo 'This package requires yacc or bison to be available.' exit fi ################################################## echo -n 'Checking for BSD signal semantics... ' cat <__conftest.c #include #include int count=0; void handle(int foo) { count++; } int main() { int pid=getpid(); signal(SIGINT, handle); kill(pid,SIGINT); kill(pid,SIGINT); kill(pid,SIGINT); if (count!=3) return 1; return 0; } EOF if ( $CC $CFLAGS __conftest.c -o __conftest || exit 1 ./__conftest || exit 1 ) >/dev/null 2>&1; then echo 'yes' else if ( $CC $CFLAGS -D__USE_BSD_SIGNAL __conftest.c -o __conftest || exit 1 ./__conftest || exit 1 ) >/dev/null 2>&1; then echo '-D__USE_BSD_SIGNAL' CFLAGS="$CFLAGS -D__USE_BSD_SIGNAL" else echo 'no' echo 'This package needs BSD signal semantics to run.' rm -f __conftest* exit fi fi rm -f __conftest* ################################################## if [ x$USE_PAM = x ]; then echo -n 'Checking for shadow... ' if [ x$WITHOUT_SHADOW != x ]; then echo disabled else cat <__conftest.c #include #include int main() { getspnam("nobody"); return 0; } EOF if ( $CC $CFLAGS __conftest.c -o __conftest || exit 1 ) >/dev/null 2>&1; then echo 'yes' USE_SHADOW=1 LIBSHADOW= else if ( $CC $CFLAGS __conftest.c -lshadow -o __conftest || exit 1 ) >/dev/null 2>&1; then echo '-lshadow' USE_SHADOW=1 LIBSHADOW=-lshadow else echo 'no' fi fi fi fi rm -f __conftest* ################################################## echo -n 'Checking for crypt... ' cat <__conftest.c int main() { crypt("aa", "bb"); } EOF if ( $CC $CFLAGS __conftest.c -o __conftest || exit 1 ) >/dev/null 2>&1; then echo 'yes' else if ( $CC $CFLAGS __conftest.c -lcrypt -o __conftest || exit 1 ) >/dev/null 2>&1; then echo '-lcrypt' LIBS="$LIBS -lcrypt" else echo 'no' echo 'This package requires crypt.' rm -f __conftest* exit fi fi rm -f __conftest* ################################################## echo -n 'Checking for socklen_t... ' cat <__conftest.c #include #include #include int main() { struct sockaddr_in sn; socklen_t len = sizeof(sn); getpeername(0, (struct sockaddr *)&sn, &len); return 0; } EOF if ( $CC $CFLAGS __conftest.c -o __conftest || exit 1 ) >/dev/null 2>&1; then echo 'yes' else if ( $CC $CFLAGS -Dsocklen_t=int __conftest.c -o __conftest || exit 1 ) >/dev/null 2>&1; then echo 'int' CFLAGS="$CFLAGS -Dsocklen_t=int" else if ( $CC $CFLAGS -Dsocklen_t=size_t __conftest.c -o __conftest || exit 1 ) >/dev/null 2>&1; then echo 'size_t' CFLAGS="$CFLAGS -Dsocklen_t=size_t" else echo 'no' echo 'Cannot work out what to use for socklen_t. Help...' rm -f __conftest* exit fi fi fi rm -f __conftest* ################################################## echo -n 'Checking for snprintf declaration... ' cat <__conftest.c #include int main() { void *x = (void *)snprintf; printf("%lx", (long)x); return 0; } EOF if ( $CC $CFLAGS __conftest.c -o __conftest || exit 1 ) >/dev/null 2>&1; then echo 'ok' else if ( $CC $CFLAGS -D_GNU_SOURCE __conftest.c -o __conftest || exit 1 ./__conftest || exit 1 ) >/dev/null 2>&1; then echo '-D_GNU_SOURCE' CFLAGS="$CFLAGS -D_GNU_SOURCE" else echo 'manual' CFLAGS="$CFLAGS -DDECLARE_SNPRINTF" fi fi rm -f __conftest* echo -n 'Checking for snprintf implementation... ' cat <__conftest.c #include #include #ifdef DECLARE_SNPRINTF #ifdef __cplusplus extern "C" #endif /*__cplusplus*/ int snprintf(char *, int, const char *, ...); #endif /*DECLARE_SNPRINTF*/ int main() { char buf[32]; snprintf(buf, 8, "%s", "1234567890"); if (strlen(buf)!=7) return 1; return 0; } EOF if ( $CC $CFLAGS __conftest.c $LIBBSD -o __conftest || exit 1 ./__conftest || exit 1 ) >/dev/null 2>&1; then echo 'ok' else if ( $CC $CFLAGS __conftest.c -lsnprintf $LIBBSD -o __conftest || exit 1 ./__conftest || exit 1 ) >/dev/null 2>&1; then echo '-lsnprintf' LIBS="$LIBS -lsnprintf" else if ( $CC $CFLAGS __conftest.c -ldb $LIBBSD -o __conftest || exit 1 ./__conftest || exit 1 ) >/dev/null 2>&1; then echo '-ldb' LIBS="$LIBS -ldb" else echo 'missing' echo 'This package requires snprintf.' rm -f __conftest* exit fi fi fi rm -f __conftest* ################################################## ## libbsd should go last in case it's broken if [ "x$LIBBSD" != x ]; then LIBS="$LIBS $LIBBSD" fi echo 'Generating MCONFIG...' ( echo -n '# Generated by configure (confgen version 2) on ' date echo '#' echo echo "SBINDIR=$SBINDIR" echo "MANDIR=$MANDIR" echo "BINMODE=$BINMODE" echo "DAEMONMODE=$DAEMONMODE" echo "MANMODE=$MANMODE" echo "PREFIX=$PREFIX" echo "EXECPREFIX=$EXECPREFIX" echo "INSTALLROOT=$INSTALLROOT" echo "CC=$CC" if [ x$CC_WARNINGS != x ]; then CFLAGS="$CFLAGS $WARNINGS" fi echo "CFLAGS=$CFLAGS" | sed 's/= */=/' echo "LDFLAGS=$LDFLAGS" | sed 's/= */=/' echo "LIBS=$LIBS" | sed 's/= */=/' echo "YACC=$YACC" | sed 's/= */=/' echo "USE_SHADOW=$USE_SHADOW" echo "LIBSHADOW=$LIBSHADOW" ) > MCONFIG linux-ftpd-0.17/version.h100644 144 144 144 7141140311 14644 0ustar dhollandpeople/* * String to embed in binaries to identify package */ char pkg[]="$NetKit: linux-ftpd-0.17 $"; linux-ftpd-0.17/support/ 40700 144 144 0 7141141771 14447 5ustar dhollandpeoplelinux-ftpd-0.17/support/Makefile100644 144 144 431 6750671500 16177 0ustar dhollandpeopleinclude ../MCONFIG OBJS=daemon.o setproctitle.o isexpired.o vis.o all: libsupport.a %.o: %.c $(CC) $(CFLAGS) -DHASSETPROCTITLE $< -c libsupport.a: $(OBJS) ar -cruv $@ $^ ranlib $@ install: ; clean: rm -f *.o libsupport.a setproctitle.o: setproctitle.h daemon.o: daemon.h linux-ftpd-0.17/support/daemon.3100644 144 144 5461 7141140312 16101 0ustar dhollandpeople.\" From: NetBSD: daemon.3,v 1.3 1995/02/25 13:41:12 cgd Exp .\" $Id: daemon.3,v 1.12 2000/07/30 23:56:58 dholland Exp $ .\" .\" Copyright (c) 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. All advertising materials mentioning features or use of this software .\" must display the following acknowledgement: .\" This product includes software developed by the University of .\" California, Berkeley and its contributors. .\" 4. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" @(#)daemon.3 8.1 (Berkeley) 6/9/93 .\" .Dd June 9, 1993 .Dt DAEMON 3 .Os "Linux NetKit (0.17)" .Sh NAME .Nm daemon .Nd run in the background .Sh SYNOPSIS .Fd #include .Fn daemon "int nochdir" "int noclose" .Sh DESCRIPTION .Pp The .Fn daemon function is for programs wishing to detach themselves from the controlling terminal and run in the background as system daemons. .Pp Unless the argument .Fa nochdir is non-zero, .Fn daemon changes the current working directory to the root (``/''). .Pp Unless the argument .Fa noclose is non-zero, .Fn daemon will redirect standard input, standard output and standard error to ``/dev/null''. .Sh ERRORS The function .Fn daemon may fail and set .Va errno for any of the errors specified for the library functions .Xr fork 2 and .Xr setsid 2 . .Sh SEE ALSO .Xr setsid 2 .Sh HISTORY The .Fn daemon function first appeared in .Bx 4.4 . linux-ftpd-0.17/support/daemon.c100644 144 144 4721 6743454530 16200 0ustar dhollandpeople/*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * From: @(#)daemon.c 8.1 (Berkeley) 6/4/93 * NetBSD: daemon.c,v 1.4 1995/02/25 13:41:16 cgd Exp */ char daemon_rcsid[] = "$Id: daemon.c,v 1.1.1.1 1999/07/15 21:57:44 dholland Exp $"; #include #include #include #include "daemon.h" int daemon(int nochdir, int noclose) { int fd; switch (fork()) { case -1: return -1; case 0: break; default: _exit(0); } if (setsid() == -1) return -1; if (!nochdir) chdir("/"); if (noclose) return 0; fd = open(_PATH_DEVNULL, O_RDWR, 0); if (fd != -1) { dup2(fd, STDIN_FILENO); dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); if (fd > 2) close(fd); } return 0; } linux-ftpd-0.17/support/daemon.h100644 144 144 26 6743454530 16137 0ustar dhollandpeopleint daemon(int, int); linux-ftpd-0.17/support/isexpired.c100644 144 144 1544 6751163032 16722 0ustar dhollandpeople#ifdef USE_SHADOW #include #include #include "isexpired.h" /* * Check password aging info when using shadow passwords. Returns: * 3 - after account expiration date * 2 - expired password not changed for too long * 1 - password expired, must be changed * 0 - success, or no shadow password information at all * * Written by Marek Michalkiewicz , * public domain. */ int isexpired(const struct spwd *sp) { long now, change; if (!sp) return 0; now = (time(NULL) / (24L * 3600L)); if (sp->sp_expire > 0 && now >= sp->sp_expire) return 3; if (sp->sp_lstchg > 0 && sp->sp_max > 0) { change = sp->sp_lstchg + sp->sp_max; if (sp->sp_inact >= 0 && now >= change + sp->sp_inact) return 2; if (now >= change) return 1; } if (sp->sp_lstchg == 0) return 1; return 0; } #endif linux-ftpd-0.17/support/isexpired.h100644 144 144 61 6340047132 16654 0ustar dhollandpeoplestruct spwd; int isexpired(const struct spwd *); linux-ftpd-0.17/support/setproctitle.3100644 144 144 5425 7141140312 17357 0ustar dhollandpeople.\" OpenBSD: setproctitle.3,v 1.4 1996/10/08 01:20:08 michaels Exp .\" $Id: setproctitle.3,v 1.13 2000/07/30 23:56:58 dholland Exp $ .\" .\" Copyright (c) 1994, 1995 Christopher G. Demetriou .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. All advertising materials mentioning features or use of this software .\" must display the following acknowledgement: .\" This product includes software developed by Christopher G. Demetriou .\" for the NetBSD Project. .\" 3. The name of the author may not be used to endorse or promote products .\" derived from this software without specific prior written permission .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES .\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. .\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, .\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT .\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" .Dd April 13, 1994 .Dt SETPROCTITLE 3 .Os "Linux NetKit (0.17)" .Sh NAME .Nm setproctitle .Nd set process title .Sh SYNOPSIS .Fd #include .Ft void .Fn setproctitle "const char *fmt" "..." .Sh DESCRIPTION The .Fn setproctitle function sets the invoking process's title. The process title is set to the last component of the program name, followed by a colon and the formatted string specified by .Va fmt . If .Va fmt is NULL, the colon and formatted string are omitted. The length of a process title is limited to 2048 bytes. .Sh EXAMPLES Set the process title to the program name, with no further information: .Bd -literal -offset indent setproctitle(NULL); .Ed .Pp Set the process title to the program name, an informational string, and the process id: .Bd -literal -offset indent setproctitle("foo! (%d)", getpid()); .Ed .Sh SEE ALSO .Xr ps 1 , .Xr w 1 , .Xr printf 3 .Sh HISTORY The .Fn setproctitle function first appeared in NetBSD 0.9a. linux-ftpd-0.17/support/setproctitle.c100644 144 144 10072 6774265447 17505 0ustar dhollandpeople/* * setproctitle implementation for linux. * Stolen from sendmail 8.7.4 and bashed around by David A. Holland */ /* * Copyright (c) 1983, 1995 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * From: @(#)conf.c 8.243 (Berkeley) 11/20/95 */ char setproctitle_rcsid[] = "$Id: setproctitle.c,v 1.6 1999/09/29 01:28:07 netbug Exp $"; #include #include #include #include #include #include "setproctitle.h" /* ** SETPROCTITLE -- set process title for ps ** ** Parameters: ** fmt -- a printf style format string. ** a, b, c -- possible parameters to fmt. ** ** Returns: ** none. ** ** Side Effects: ** Clobbers argv of our main procedure so ps(1) will ** display the title. */ /* ** Pointers for setproctitle. ** This allows "ps" listings to give more useful information. */ static char **Argv = NULL; /* pointer to argument vector */ static char *LastArgv = NULL; /* end of argv */ static char Argv0[128]; /* program name */ void initsetproctitle(int argc, char **argv, char **envp) { register int i; char *tmp; /* ** Move the environment so setproctitle can use the space at ** the top of memory. */ for (i = 0; envp[i] != NULL; i++) continue; __environ = (char **) malloc(sizeof (char *) * (i + 1)); for (i = 0; envp[i] != NULL; i++) __environ[i] = strdup(envp[i]); __environ[i] = NULL; /* ** Save start and extent of argv for setproctitle. */ Argv = argv; if (i > 0) LastArgv = envp[i - 1] + strlen(envp[i - 1]); else LastArgv = argv[argc - 1] + strlen(argv[argc - 1]); tmp = strrchr(argv[0], '/'); if (!tmp) tmp = argv[0]; else tmp++; strncpy(Argv0, tmp, sizeof(Argv0)); Argv0[sizeof(Argv0)-1] = 0; } void setproctitle(const char *fmt, ...) { register char *p; register int i=0; static char buf[2048]; va_list ap; p = buf; /* print progname: heading for grep */ /* This can't overflow buf due to the relative size of Argv0. */ (void) strcpy(p, Argv0); (void) strcat(p, ": "); p += strlen(p); /* print the argument string */ va_start(ap, fmt); (void) vsnprintf(p, sizeof(buf) - (p - buf), fmt, ap); va_end(ap); i = strlen(buf); if (i > LastArgv - Argv[0] - 2) { i = LastArgv - Argv[0] - 2; buf[i] = '\0'; } (void) strcpy(Argv[0], buf); p = &Argv[0][i]; while (p < LastArgv) *p++ = ' '; Argv[1] = NULL; } linux-ftpd-0.17/support/setproctitle.h100644 144 144 200 6261721145 17421 0ustar dhollandpeople/* Call this from main. */ void initsetproctitle(int argc, char **argv, char **envp); void setproctitle(const char *fmt, ...); linux-ftpd-0.17/support/vis.3100644 144 144 14774 7141140312 15466 0ustar dhollandpeople.\" $OpenBSD: vis.3,v 1.3 1996/08/19 08:27:34 tholo Exp $ .\" .\" Copyright (c) 1989, 1991, 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. All advertising materials mentioning features or use of this software .\" must display the following acknowledgement: .\" This product includes software developed by the University of .\" California, Berkeley and its contributors. .\" 4. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .Dd June 9, 1993 .Dt VIS 3 .Os "Linux NetKit (0.17)" .Sh NAME .Nm vis .Nd visually encode characters .Sh SYNOPSIS .Fd #include .Ft char * .Fn vis "char *dst" "char c" "int flag" "char nextc" .Ft int .Fn strvis "char *dst" "char *src" "int flag" .Ft int .Fn strvisx "char *dst" "char *src" "int len" "int flag" .Sh DESCRIPTION The .Fn vis function copies into .Fa dst a string which represents the character .Fa c . If .Fa c needs no encoding, it is copied in unaltered. The string is null terminated, and a pointer to the end of the string is returned. The maximum length of any encoding is four characters (not including the trailing .Dv NUL ) ; thus, when encoding a set of characters into a buffer, the size of the buffer should be four times the number of characters encoded, plus one for the trailing .Dv NUL . The flag parameter is used for altering the default range of characters considered for encoding and for altering the visual representation. The additional character, .Fa nextc , is only used when selecting the .Dv VIS_CSTYLE encoding format (explained below). .Pp The .Fn strvis and .Fn strvisx functions copy into .Fa dst a visual representation of the string .Fa src . The .Fn strvis function encodes characters from .Fa src up to the first .Dv NUL . The .Fn strvisx function encodes exactly .Fa len characters from .Fa src (this is useful for encoding a block of data that may contain .Dv NUL Ns 's). Both forms .Dv NUL terminate .Fa dst . The size of .Fa dst must be four times the number of characters encoded from .Fa src (plus one for the .Dv NUL ) . Both forms return the number of characters in dst (not including the trailing .Dv NUL ) . .Pp The encoding is a unique, invertible representation composed entirely of graphic characters; it can be decoded back into the original form using the .Xr unvis 3 or .Xr strunvis 3 functions. .Pp There are two parameters that can be controlled: the range of characters that are encoded, and the type of representation used. By default, all non-graphic characters. except space, tab, and newline are encoded. (See .Xr isgraph 3 . ) The following flags alter this: .Bl -tag -width VIS_WHITEX .It Dv VIS_SP Also encode space. .It Dv VIS_TAB Also encode tab. .It Dv VIS_NL Also encode newline. .It Dv VIS_WHITE Synonym for .Dv VIS_SP \&| .Dv VIS_TAB \&| .Dv VIS_NL . .It Dv VIS_SAFE Only encode "unsafe" characters. Unsafe means control characters which may cause common terminals to perform unexpected functions. Currently this form allows space, tab, newline, backspace, bell, and return - in addition to all graphic characters - unencoded. .El .Pp There are three forms of encoding. All forms use the backslash character .Ql \e to introduce a special sequence; two backslashes are used to represent a real backslash. These are the visual formats: .Bl -tag -width VIS_CSTYLE .It (default) Use an .Ql M to represent meta characters (characters with the 8th bit set), and use caret .Ql ^ to represent control characters see .Pf ( Xr iscntrl 3 ) . The following formats are used: .Bl -tag -width xxxxx .It Dv \e^C Represents the control character .Ql C . Spans characters .Ql \e000 through .Ql \e037 , and .Ql \e177 (as .Ql \e^? ) . .It Dv \eM-C Represents character .Ql C with the 8th bit set. Spans characters .Ql \e241 through .Ql \e376 . .It Dv \eM^C Represents control character .Ql C with the 8th bit set. Spans characters .Ql \e200 through .Ql \e237 , and .Ql \e377 (as .Ql \eM^? ) . .It Dv \e040 Represents .Tn ASCII space. .It Dv \e240 Represents Meta-space. .El .Pp .It Dv VIS_CSTYLE Use C-style backslash sequences to represent standard non-printable characters. The following sequences are used to represent the indicated characters: .Bd -unfilled -offset indent .Li \ea Tn - BEL No (007) .Li \eb Tn - BS No (010) .Li \ef Tn - NP No (014) .Li \en Tn - NL No (012) .Li \er Tn - CR No (015) .Li \et Tn - HT No (011) .Li \ev Tn - VT No (013) .Li \e0 Tn - NUL No (000) .Ed .Pp When using this format, the nextc parameter is looked at to determine if a .Dv NUL character can be encoded as .Ql \e0 instead of .Ql \e000 . If .Fa nextc is an octal digit, the latter representation is used to avoid ambiguity. .It Dv VIS_OCTAL Use a three digit octal sequence. The form is .Ql \eddd where .Em d represents an octal digit. .El .Pp There is one additional flag, .Dv VIS_NOSLASH , which inhibits the doubling of backslashes and the backslash before the default format (that is, control characters are represented by .Ql ^C and meta characters as .Ql M-C ) . With this flag set, the encoding is ambiguous and non-invertible. .Sh SEE ALSO .Xr vis 1 , .Xr unvis 1 , .Xr unvis 3 .Sh HISTORY These functions first appeared in 4.4BSD. linux-ftpd-0.17/support/vis.c100644 144 144 11111 6774265447 15560 0ustar dhollandpeople/*- * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * From: OpenBSD: vis.c,v 1.2 1996/08/19 08:27:36 tholo Exp */ char vis_rcsid[] = \ "$Id: vis.c,v 1.2 1999/09/29 01:28:07 netbug Exp $"; #include #include #include #ifndef __linux__ #include #else #include "vis.h" #endif #define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7') /* * vis - visually encode characters */ char * vis(register char *dst, int c, int nextc, register int flag) { if (((u_int)c <= UCHAR_MAX && isascii(c) && isgraph(c)) || ((flag & VIS_SP) == 0 && c == ' ') || ((flag & VIS_TAB) == 0 && c == '\t') || ((flag & VIS_NL) == 0 && c == '\n') || ((flag & VIS_SAFE) && (c == '\b' || c == '\007' || c == '\r'))) { *dst++ = c; if (c == '\\' && (flag & VIS_NOSLASH) == 0) *dst++ = '\\'; *dst = '\0'; return (dst); } if (flag & VIS_CSTYLE) { switch(c) { case '\n': *dst++ = '\\'; *dst++ = 'n'; goto done; case '\r': *dst++ = '\\'; *dst++ = 'r'; goto done; case '\b': *dst++ = '\\'; *dst++ = 'b'; goto done; #if __STDC__ case '\a': #else case '\007': #endif *dst++ = '\\'; *dst++ = 'a'; goto done; case '\v': *dst++ = '\\'; *dst++ = 'v'; goto done; case '\t': *dst++ = '\\'; *dst++ = 't'; goto done; case '\f': *dst++ = '\\'; *dst++ = 'f'; goto done; case ' ': *dst++ = '\\'; *dst++ = 's'; goto done; case '\0': *dst++ = '\\'; *dst++ = '0'; if (isoctal(nextc)) { *dst++ = '0'; *dst++ = '0'; } goto done; } } if (((c & 0177) == ' ') || (flag & VIS_OCTAL)) { *dst++ = '\\'; *dst++ = ((u_char)c >> 6 & 07) + '0'; *dst++ = ((u_char)c >> 3 & 07) + '0'; *dst++ = ((u_char)c & 07) + '0'; goto done; } if ((flag & VIS_NOSLASH) == 0) *dst++ = '\\'; if (c & 0200) { c &= 0177; *dst++ = 'M'; } if (iscntrl(c)) { *dst++ = '^'; if (c == 0177) *dst++ = '?'; else *dst++ = c + '@'; } else { *dst++ = '-'; *dst++ = c; } done: *dst = '\0'; return (dst); } /* * strvis, strvisx - visually encode characters from src into dst * * Dst must be 4 times the size of src to account for possible * expansion. The length of dst, not including the trailing NULL, * is returned. * * Strvisx encodes exactly len bytes from src into dst. * This is useful for encoding a block of data. */ int strvis(register char *dst, register const char *src, int flag) { register char c; char *start; for (start = dst; (c = *src); ) dst = vis(dst, c, flag, *++src); *dst = '\0'; return (dst - start); } int strvisx(register char *dst, register const char *src, register size_t len, int flag) { register char c; char *start; for (start = dst; len > 1; len--) { c = *src; dst = vis(dst, c, flag, *++src); } if (len) dst = vis(dst, *src, flag, '\0'); *dst = '\0'; return (dst - start); } linux-ftpd-0.17/support/vis.h100644 144 144 6364 7136450467 15552 0ustar dhollandpeople/* $OpenBSD: vis.h,v 1.3 1999/05/14 20:31:56 aaron Exp $ */ /* $NetBSD: vis.h,v 1.4 1994/10/26 00:56:41 cgd Exp $ */ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)vis.h 5.9 (Berkeley) 4/3/91 */ #ifndef _VIS_H_ #define _VIS_H_ /* * to select alternate encoding format */ #define VIS_OCTAL 0x01 /* use octal \ddd format */ #define VIS_CSTYLE 0x02 /* use \[nrft0..] where appropriate */ /* * to alter set of characters encoded (default is to encode all * non-graphic except space, tab, and newline). */ #define VIS_SP 0x04 /* also encode space */ #define VIS_TAB 0x08 /* also encode tab */ #define VIS_NL 0x10 /* also encode newline */ #define VIS_WHITE (VIS_SP | VIS_TAB | VIS_NL) #define VIS_SAFE 0x20 /* only encode "unsafe" characters */ /* * other */ #define VIS_NOSLASH 0x40 /* inhibit printing '\' */ /* * unvis return codes */ #define UNVIS_VALID 1 /* character valid */ #define UNVIS_VALIDPUSH 2 /* character valid, push back passed char */ #define UNVIS_NOCHAR 3 /* valid sequence, no character produced */ #define UNVIS_SYNBAD -1 /* unrecognized escape sequence */ #define UNVIS_ERROR -2 /* decoder in unknown state (unrecoverable) */ /* * unvis flags */ #define UNVIS_END 1 /* no more characters */ #include __BEGIN_DECLS char *vis __P((char *, int, int, int)); int strvis __P((char *, const char *, int)); int strvisx __P((char *, const char *, size_t, int)); int strunvis __P((char *, const char *)); int unvis __P((char *, char, int *, int)); __END_DECLS #endif /* !_VIS_H_ */