bostic@OKEEFFE.BERKELEY.EDU (Keith Bostic) (11/01/88)
Subject: security problem in ftpd. Index: etc/ftpd 4.3BSD,4.3BSD-tahoe Description: There's a security problem associated with anonymous ftp in all systems with Berkeley derived networking. If your site doesn't permit anonymous ftp logins, you're not affected. Fix: The attached shar has three fixes. The file context.diff.4.3 is for 4.3BSD systems, context.diff.4.3-tahoe is for 4.3BSD-tahoe systems. The rest of the files are complete source for the ftpd(8) program, in case you're a binary site. # This is a shell archive. Save it in a file, remove anything before # this line, and then unpack it by entering "sh file". Note, it may # create directories; files and directories will be owned by you and # have default permissions. # # This archive contains: # # Makefile # context.diff.4.3 # context.diff.4.3-tahoe # ftpcmd.y # ftpd.c # glob.c # logwtmp.c # newvers.sh # popen.c # vers.c # version # echo x - Makefile sed 's/^X//' >Makefile << 'END-of-Makefile' X# X# Copyright (c) 1988 Regents of the University of California. X# All rights reserved. X# X# Redistribution and use in source and binary forms are permitted X# provided that the above copyright notice and this paragraph are X# duplicated in all such forms and that any documentation, X# advertising materials, and other materials related to such X# distribution and use acknowledge that the software was developed X# by the University of California, Berkeley. The name of the X# University may not be used to endorse or promote products derived X# from this software without specific prior written permission. X# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR X# IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED X# WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. X# X# @(#)Makefile 5.8 (Berkeley) 9/22/88 X# XCFLAGS= -O XLIBC= /lib/libc.a XSRCS= ftpd.c ftpcmd.c glob.c logwtmp.c popen.c vers.c XOBJS= ftpd.o ftpcmd.o glob.o logwtmp.o popen.o vers.o XMAN= ftpd.0 X Xall: ftpd X Xftpd: ${OBJS} ${LIBC} X ${CC} -o $@ ${OBJS} X Xvers.o: ftpd.c ftpcmd.y X sh newvers.sh X ${CC} ${CFLAGS} -c vers.c X Xclean: X rm -f ${OBJS} ftpd core ftpcmd.c X Xcleandir: clean X rm -f ${MAN} tags .depend X Xdepend: ${SRCS} X mkdep ${CFLAGS} ${SRCS} X Xinstall: X install -s -o bin -g bin -m 755 ftpd ${DESTDIR}/etc/ftpd X Xlint: ${SRCS} X lint ${CFLAGS} ${SRCS} X Xtags: ${SRCS} X ctags ${SRCS} END-of-Makefile echo x - context.diff.4.3 sed 's/^X//' >context.diff.4.3 << 'END-of-context.diff.4.3' XRCS file: RCS/ftpcmd.y,v Xretrieving revision 1.2 Xdiff -c2 -r1.2 ftpcmd.y X*** /tmp/,RCSt1026617 Sat Oct 29 23:52:42 1988 X--- ftpcmd.y Sat Oct 29 23:16:59 1988 X*************** X*** 87,91 **** X cmd: USER SP username CRLF X = { X! extern struct passwd *getpwnam(); X X logged_in = 0; X--- 87,91 ---- X cmd: USER SP username CRLF X = { X! extern struct passwd *sgetpwnam(); X X logged_in = 0; X*************** X*** 92,96 **** X if (strcmp((char *) $3, "ftp") == 0 || X strcmp((char *) $3, "anonymous") == 0) { X! if ((pw = getpwnam("ftp")) != NULL) { X guest = 1; X reply(331, X--- 92,96 ---- X if (strcmp((char *) $3, "ftp") == 0 || X strcmp((char *) $3, "anonymous") == 0) { X! if ((pw = sgetpwnam("ftp")) != NULL) { X guest = 1; X reply(331, X*************** X*** 102,106 **** X } else if (checkuser((char *) $3)) { X guest = 0; X! pw = getpwnam((char *) $3); X if (pw == NULL) { X reply(530, "User %s unknown.", $3); X--- 102,106 ---- X } else if (checkuser((char *) $3)) { X guest = 0; X! pw = sgetpwnam((char *) $3); X if (pw == NULL) { X reply(530, "User %s unknown.", $3); X X*** ftpd.c.orig Mon Oct 31 12:13:20 1988 X--- ftpd.c Mon Oct 31 12:36:14 1988 X*************** X*** 191,201 **** X dologout(-1); X } X X pass(passwd) X char *passwd; X { X! char *xpasswd, *savestr(); X! static struct passwd save; X X if (logged_in || pw == NULL) { X reply(503, "Login with USER first."); X--- 191,257 ---- X dologout(-1); X } X X+ /* X+ * Helper function for sgetpwnam(). X+ */ X+ char * X+ sgetsave(s) X+ char *s; X+ { X+ #ifdef notdef X+ char *new = strdup(s); X+ #else X+ char *malloc(); X+ char *new = malloc((unsigned) strlen(s) + 1); X+ #endif X+ X+ if (new == NULL) { X+ reply(553, "Local resource failure"); /* ??? */ X+ dologout(1); X+ } X+ #ifndef notdef X+ (void) strcpy(new, s); X+ #endif X+ return (new); X+ } X+ X+ /* X+ * Save the result of a getpwnam. Used for USER command, since X+ * the data returned must not be clobbered by any other command X+ * (e.g., globbing). X+ */ X+ struct passwd * X+ sgetpwnam(name) X+ char *name; X+ { X+ static struct passwd save; X+ register struct passwd *p; X+ char *sgetsave(); X+ X+ if ((p = getpwnam(name)) == NULL) X+ return (p); X+ if (save.pw_name) { X+ free(save.pw_name); X+ free(save.pw_passwd); X+ free(save.pw_comment); X+ free(save.pw_gecos); X+ free(save.pw_dir); X+ free(save.pw_shell); X+ } X+ save = *p; X+ save.pw_name = sgetsave(p->pw_name); X+ save.pw_passwd = sgetsave(p->pw_passwd); X+ save.pw_comment = sgetsave(p->pw_comment); X+ save.pw_gecos = sgetsave(p->pw_gecos); X+ save.pw_dir = sgetsave(p->pw_dir); X+ save.pw_shell = sgetsave(p->pw_shell); X+ return (&save); X+ } X+ X pass(passwd) X char *passwd; X { X! char *xpasswd; X X if (logged_in || pw == NULL) { X reply(503, "Login with USER first."); X*************** X*** 235,252 **** X logged_in = 1; X dologin(pw); X seteuid(pw->pw_uid); X- /* X- * Save everything so globbing doesn't X- * clobber the fields. X- */ X- save = *pw; X- save.pw_name = savestr(pw->pw_name); X- save.pw_passwd = savestr(pw->pw_passwd); X- save.pw_comment = savestr(pw->pw_comment); X- save.pw_gecos = savestr(pw->pw_gecos); X- save.pw_dir = savestr(pw->pw_dir); X- save.pw_shell = savestr(pw->pw_shell); X- pw = &save; X home = pw->pw_dir; /* home dir for globbing */ X return; X bad: X--- 291,296 ---- X*************** X*** 254,271 **** X pw = NULL; X } X X- char * X- savestr(s) X- char *s; X- { X- char *malloc(); X- char *new = malloc((unsigned) strlen(s) + 1); X- X- if (new != NULL) X- (void) strcpy(new, s); X- return (new); X- } X- X retrieve(cmd, name) X char *cmd, *name; X { X--- 298,303 ---- X*************** X*** 771,778 **** X /* X * Record login in wtmp file. X */ X! dologin(pw) X! struct passwd *pw; X { X char line[32]; X X--- 803,810 ---- X /* X * Record login in wtmp file. X */ X! dologin(p) X! struct passwd *p; X { X char line[32]; X X*************** X*** 780,786 **** X /* hack, but must be unique and no tty line */ X (void) sprintf(line, "ftp%d", getpid()); X SCPYN(utmp.ut_line, line); X! SCPYN(utmp.ut_name, pw->pw_name); X SCPYN(utmp.ut_host, remotehost); X utmp.ut_time = (long) time((time_t *) 0); X (void) write(wtmp, (char *)&utmp, sizeof (utmp)); X--- 812,818 ---- X /* hack, but must be unique and no tty line */ X (void) sprintf(line, "ftp%d", getpid()); X SCPYN(utmp.ut_line, line); X! SCPYN(utmp.ut_name, p->pw_name); X SCPYN(utmp.ut_host, remotehost); X utmp.ut_time = (long) time((time_t *) 0); X (void) write(wtmp, (char *)&utmp, sizeof (utmp)); X*************** X*** 934,960 **** X register char *name; X { X register char *cp; X- char line[BUFSIZ], *index(), *getusershell(); X FILE *fd; X! struct passwd *pw; X int found = 0; X X! pw = getpwnam(name); X! if (pw == NULL) X return (0); X while ((cp = getusershell()) != NULL) X! if (strcmp(cp, pw->pw_shell) == 0) X break; X endpwent(); X endusershell(); X if (cp == NULL) X return (0); X! fd = fopen(FTPUSERS, "r"); X! if (fd == NULL) X return (1); X while (fgets(line, sizeof (line), fd) != NULL) { X! cp = index(line, '\n'); X! if (cp) X *cp = '\0'; X if (strcmp(line, name) == 0) { X found++; X--- 966,992 ---- X register char *name; X { X register char *cp; X FILE *fd; X! struct passwd *p; X! char *shell; X int found = 0; X+ char line[BUFSIZ], *index(), *getusershell(); X X! if ((p = getpwnam(name)) == NULL) X return (0); X+ if ((shell = p->pw_shell) == NULL || *shell == 0) X+ shell = "/bin/sh"; X while ((cp = getusershell()) != NULL) X! if (strcmp(cp, shell) == 0) X break; X endpwent(); X endusershell(); X if (cp == NULL) X return (0); X! if ((fd = fopen(FTPUSERS, "r")) == NULL) X return (1); X while (fgets(line, sizeof (line), fd) != NULL) { X! if ((cp = index(line, '\n')) != NULL) X *cp = '\0'; X if (strcmp(line, name) == 0) { X found++; END-of-context.diff.4.3 echo x - context.diff.4.3-tahoe sed 's/^X//' >context.diff.4.3-tahoe << 'END-of-context.diff.4.3-tahoe' XRCS file: RCS/ftpcmd.y,v Xretrieving revision 1.2 Xdiff -c2 -r1.2 ftpcmd.y X*** /tmp/,RCSt1026617 Sat Oct 29 23:52:42 1988 X--- ftpcmd.y Sat Oct 29 23:16:59 1988 X*************** X*** 87,91 **** X cmd: USER SP username CRLF X = { X! extern struct passwd *getpwnam(); X X logged_in = 0; X--- 87,91 ---- X cmd: USER SP username CRLF X = { X! extern struct passwd *sgetpwnam(); X X logged_in = 0; X*************** X*** 92,96 **** X if (strcmp((char *) $3, "ftp") == 0 || X strcmp((char *) $3, "anonymous") == 0) { X! if ((pw = getpwnam("ftp")) != NULL) { X guest = 1; X reply(331, X--- 92,96 ---- X if (strcmp((char *) $3, "ftp") == 0 || X strcmp((char *) $3, "anonymous") == 0) { X! if ((pw = sgetpwnam("ftp")) != NULL) { X guest = 1; X reply(331, X*************** X*** 102,106 **** X } else if (checkuser((char *) $3)) { X guest = 0; X! pw = getpwnam((char *) $3); X if (pw == NULL) { X reply(530, "User %s unknown.", $3); X--- 102,106 ---- X } else if (checkuser((char *) $3)) { X guest = 0; X! pw = sgetpwnam((char *) $3); X if (pw == NULL) { X reply(530, "User %s unknown.", $3); X X XRCS file: RCS/ftpd.c,v Xretrieving revision 1.3 Xdiff -c2 -r1.3 ftpd.c X*** /tmp/,RCSt1026617 Sat Oct 29 23:52:48 1988 X--- ftpd.c Sat Oct 29 23:49:01 1988 X*************** X*** 194,202 **** X } X X pass(passwd) X char *passwd; X { X! char *xpasswd, *savestr(); X! static struct passwd save; X X if (logged_in || pw == NULL) { X--- 194,258 ---- X } X X+ /* X+ * Helper function for sgetpwnam(). X+ */ X+ char * X+ sgetsave(s) X+ char *s; X+ { X+ #ifdef notdef X+ char *new = strdup(s); X+ #else X+ char *malloc(); X+ char *new = malloc((unsigned) strlen(s) + 1); X+ #endif X+ X+ if (new == NULL) { X+ reply(553, "Local resource failure"); /* ??? */ X+ dologout(1); X+ } X+ #ifndef notdef X+ (void) strcpy(new, s); X+ #endif X+ return (new); X+ } X+ X+ /* X+ * Save the result of a getpwnam. Used for USER command, since X+ * the data returned must not be clobbered by any other command X+ * (e.g., globbing). X+ */ X+ struct passwd * X+ sgetpwnam(name) X+ char *name; X+ { X+ static struct passwd save; X+ register struct passwd *p; X+ char *sgetsave(); X+ X+ if ((p = getpwnam(name)) == NULL) X+ return (p); X+ if (save.pw_name) { X+ free(save.pw_name); X+ free(save.pw_passwd); X+ free(save.pw_comment); X+ free(save.pw_gecos); X+ free(save.pw_dir); X+ free(save.pw_shell); X+ } X+ save = *p; X+ save.pw_name = sgetsave(p->pw_name); X+ save.pw_passwd = sgetsave(p->pw_passwd); X+ save.pw_comment = sgetsave(p->pw_comment); X+ save.pw_gecos = sgetsave(p->pw_gecos); X+ save.pw_dir = sgetsave(p->pw_dir); X+ save.pw_shell = sgetsave(p->pw_shell); X+ return (&save); X+ } X+ X pass(passwd) X char *passwd; X { X! char *xpasswd; X X if (logged_in || pw == NULL) { X*************** X*** 238,253 **** X dologin(pw); X seteuid(pw->pw_uid); X- /* X- * Save everything so globbing doesn't X- * clobber the fields. X- */ X- save = *pw; X- save.pw_name = savestr(pw->pw_name); X- save.pw_passwd = savestr(pw->pw_passwd); X- save.pw_comment = savestr(pw->pw_comment); X- save.pw_gecos = savestr(pw->pw_gecos); X- save.pw_dir = savestr(pw->pw_dir); X- save.pw_shell = savestr(pw->pw_shell); X- pw = &save; X home = pw->pw_dir; /* home dir for globbing */ X return; X--- 294,297 ---- X*************** X*** 257,272 **** X } X X- char * X- savestr(s) X- char *s; X- { X- char *malloc(); X- char *new = malloc((unsigned) strlen(s) + 1); X- X- if (new != NULL) X- (void) strcpy(new, s); X- return (new); X- } X- X retrieve(cmd, name) X char *cmd, *name; X--- 301,304 ---- X*************** X*** 785,790 **** X * Record login in wtmp file. X */ X! dologin(pw) X! struct passwd *pw; X { X char line[32]; X--- 817,822 ---- X * Record login in wtmp file. X */ X! dologin(p) X! struct passwd *p; X { X char line[32]; X*************** X*** 794,798 **** X (void) sprintf(line, "ftp%d", getpid()); X SCPYN(utmp.ut_line, line); X! SCPYN(utmp.ut_name, pw->pw_name); X SCPYN(utmp.ut_host, remotehost); X utmp.ut_time = (long) time((time_t *) 0); X--- 826,830 ---- X (void) sprintf(line, "ftp%d", getpid()); X SCPYN(utmp.ut_line, line); X! SCPYN(utmp.ut_name, p->pw_name); X SCPYN(utmp.ut_host, remotehost); X utmp.ut_time = (long) time((time_t *) 0); X*************** X*** 948,963 **** X { X register char *cp; X- char line[BUFSIZ], *index(), *getusershell(); X FILE *fd; X! struct passwd *pw; X int found = 0; X X! pw = getpwnam(name); X! if (pw == NULL) X return (0); X! if (pw ->pw_shell == NULL || pw->pw_shell[0] == NULL) X! pw->pw_shell = "/bin/sh"; X while ((cp = getusershell()) != NULL) X! if (strcmp(cp, pw->pw_shell) == 0) X break; X endusershell(); X--- 980,995 ---- X { X register char *cp; X FILE *fd; X! struct passwd *p; X! char *shell; X int found = 0; X+ char line[BUFSIZ], *index(), *getusershell(); X X! if ((p = getpwnam(name)) == NULL) X return (0); X! if ((shell = p->pw_shell) == NULL || *shell == 0) X! shell = "/bin/sh"; X while ((cp = getusershell()) != NULL) X! if (strcmp(cp, shell) == 0) X break; X endusershell(); X*************** X*** 964,973 **** X if (cp == NULL) X return (0); X! fd = fopen(FTPUSERS, "r"); X! if (fd == NULL) X return (1); X while (fgets(line, sizeof (line), fd) != NULL) { X! cp = index(line, '\n'); X! if (cp) X *cp = '\0'; X if (strcmp(line, name) == 0) { X--- 996,1003 ---- X if (cp == NULL) X return (0); X! if ((fd = fopen(FTPUSERS, "r")) == NULL) X return (1); X while (fgets(line, sizeof (line), fd) != NULL) { X! if ((cp = index(line, '\n')) != NULL) X *cp = '\0'; X if (strcmp(line, name) == 0) { X END-of-context.diff.4.3-tahoe echo x - ftpcmd.y sed 's/^X//' >ftpcmd.y << 'END-of-ftpcmd.y' X/* X * Copyright (c) 1985 Regents of the University of California. X * All rights reserved. X * X * Redistribution and use in source and binary forms are permitted X * provided that the above copyright notice and this paragraph are X * duplicated in all such forms and that any documentation, X * advertising materials, and other materials related to such X * distribution and use acknowledge that the software was developed X * by the University of California, Berkeley. The name of the X * University may not be used to endorse or promote products derived X * from this software without specific prior written permission. X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED X * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. X * X * @(#)ftpcmd.y 5.12 (Berkeley) 10/30/88 X */ X X/* X * Grammar for FTP commands. X * See RFC 765. X */ X X%{ X X#ifndef lint Xstatic char sccsid[] = "@(#)ftpcmd.y 5.12 (Berkeley) 10/30/88"; X#endif /* not lint */ X X#include <sys/types.h> X#include <sys/socket.h> X X#include <netinet/in.h> X X#include <arpa/ftp.h> X X#include <stdio.h> X#include <signal.h> X#include <ctype.h> X#include <pwd.h> X#include <setjmp.h> X#include <syslog.h> X Xextern struct sockaddr_in data_dest; Xextern int logged_in; Xextern struct passwd *pw; Xextern int guest; Xextern int logging; Xextern int type; Xextern int form; Xextern int debug; Xextern int timeout; Xextern int pdata; Xextern char hostname[]; Xextern char *globerr; Xextern int usedefault; Xextern int unique; Xextern int transflag; Xextern char tmpline[]; Xchar **glob(); X Xstatic int cmd_type; Xstatic int cmd_form; Xstatic int cmd_bytesz; Xchar cbuf[512]; Xchar *fromname; X Xchar *index(); X%} X X%token X A B C E F I X L N P R S T X X SP CRLF COMMA STRING NUMBER X X USER PASS ACCT REIN QUIT PORT X PASV TYPE STRU MODE RETR STOR X APPE MLFL MAIL MSND MSOM MSAM X MRSQ MRCP ALLO REST RNFR RNTO X ABOR DELE CWD LIST NLST SITE X STAT HELP NOOP XMKD XRMD XPWD X XCUP STOU X X LEXERR X X%start cmd_list X X%% X Xcmd_list: /* empty */ X | cmd_list cmd X = { X fromname = (char *) 0; X } X | cmd_list rcmd X ; X Xcmd: USER SP username CRLF X = { X extern struct passwd *sgetpwnam(); X X logged_in = 0; X if (strcmp((char *) $3, "ftp") == 0 || X strcmp((char *) $3, "anonymous") == 0) { X if ((pw = sgetpwnam("ftp")) != NULL) { X guest = 1; X reply(331, X "Guest login ok, send ident as password."); X } X else { X reply(530, "User %s unknown.", $3); X } X } else if (checkuser((char *) $3)) { X guest = 0; X pw = sgetpwnam((char *) $3); X if (pw == NULL) { X reply(530, "User %s unknown.", $3); X } X else { X reply(331, "Password required for %s.", $3); X } X } else { X reply(530, "User %s access denied.", $3); X } X free((char *) $3); X } X | PASS SP password CRLF X = { X pass((char *) $3); X free((char *) $3); X } X | PORT SP host_port CRLF X = { X usedefault = 0; X if (pdata > 0) { X (void) close(pdata); X } X pdata = -1; X reply(200, "PORT command successful."); X } X | PASV CRLF X = { X passive(); X } X | TYPE SP type_code CRLF X = { X switch (cmd_type) { X X case TYPE_A: X if (cmd_form == FORM_N) { X reply(200, "Type set to A."); X type = cmd_type; X form = cmd_form; X } else X reply(504, "Form must be N."); X break; X X case TYPE_E: X reply(504, "Type E not implemented."); X break; X X case TYPE_I: X reply(200, "Type set to I."); X type = cmd_type; X break; X X case TYPE_L: X if (cmd_bytesz == 8) { X reply(200, X "Type set to L (byte size 8)."); X type = cmd_type; X } else X reply(504, "Byte size must be 8."); X } X } X | STRU SP struct_code CRLF X = { X switch ($3) { X X case STRU_F: X reply(200, "STRU F ok."); X break; X X default: X reply(504, "Unimplemented STRU type."); X } X } X | MODE SP mode_code CRLF X = { X switch ($3) { X X case MODE_S: X reply(200, "MODE S ok."); X break; X X default: X reply(502, "Unimplemented MODE type."); X } X } X | ALLO SP NUMBER CRLF X = { X reply(202, "ALLO command ignored."); X } X | RETR check_login SP pathname CRLF X = { X if ($2 && $4 != NULL) X retrieve((char *) 0, (char *) $4); X if ($4 != NULL) X free((char *) $4); X } X | STOR check_login SP pathname CRLF X = { X if ($2 && $4 != NULL) X store((char *) $4, "w"); X if ($4 != NULL) X free((char *) $4); X } X | APPE check_login SP pathname CRLF X = { X if ($2 && $4 != NULL) X store((char *) $4, "a"); X if ($4 != NULL) X free((char *) $4); X } X | NLST check_login CRLF X = { X if ($2) X retrieve("/bin/ls", ""); X } X | NLST check_login SP pathname CRLF X = { X if ($2 && $4 != NULL) X retrieve("/bin/ls %s", (char *) $4); X if ($4 != NULL) X free((char *) $4); X } X | LIST check_login CRLF X = { X if ($2) X retrieve("/bin/ls -lg", ""); X } X | LIST check_login SP pathname CRLF X = { X if ($2 && $4 != NULL) X retrieve("/bin/ls -lg %s", (char *) $4); X if ($4 != NULL) X free((char *) $4); X } X | DELE check_login SP pathname CRLF X = { X if ($2 && $4 != NULL) X delete((char *) $4); X if ($4 != NULL) X free((char *) $4); X } X | RNTO SP pathname CRLF X = { X if (fromname) { X renamecmd(fromname, (char *) $3); X free(fromname); X fromname = (char *) 0; X } else { X reply(503, "Bad sequence of commands."); X } X free((char *) $3); X } X | ABOR CRLF X = { X reply(225, "ABOR command successful."); X } X | CWD check_login CRLF X = { X if ($2) X cwd(pw->pw_dir); X } X | CWD check_login SP pathname CRLF X = { X if ($2 && $4 != NULL) X cwd((char *) $4); X if ($4 != NULL) X free((char *) $4); X } X | HELP CRLF X = { X help((char *) 0); X } X | HELP SP STRING CRLF X = { X help((char *) $3); X } X | NOOP CRLF X = { X reply(200, "NOOP command successful."); X } X | XMKD check_login SP pathname CRLF X = { X if ($2 && $4 != NULL) X makedir((char *) $4); X if ($4 != NULL) X free((char *) $4); X } X | XRMD check_login SP pathname CRLF X = { X if ($2 && $4 != NULL) X removedir((char *) $4); X if ($4 != NULL) X free((char *) $4); X } X | XPWD check_login CRLF X = { X if ($2) X pwd(); X } X | XCUP check_login CRLF X = { X if ($2) X cwd(".."); X } X | STOU check_login SP pathname CRLF X = { X if ($2 && $4 != NULL) { X unique++; X store((char *) $4, "w"); X unique = 0; X } X if ($4 != NULL) X free((char *) $4); X } X | QUIT CRLF X = { X reply(221, "Goodbye."); X dologout(0); X } X | error CRLF X = { X yyerrok; X } X ; X Xrcmd: RNFR check_login SP pathname CRLF X = { X char *renamefrom(); X X if ($2 && $4) { X fromname = renamefrom((char *) $4); X if (fromname == (char *) 0 && $4) { X free((char *) $4); X } X } X } X ; X Xusername: STRING X ; X Xpassword: STRING X ; X Xbyte_size: NUMBER X ; X Xhost_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA X NUMBER COMMA NUMBER X = { X register char *a, *p; X X a = (char *)&data_dest.sin_addr; X a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; X p = (char *)&data_dest.sin_port; X p[0] = $9; p[1] = $11; X data_dest.sin_family = AF_INET; X } X ; X Xform_code: N X = { X $$ = FORM_N; X } X | T X = { X $$ = FORM_T; X } X | C X = { X $$ = FORM_C; X } X ; X Xtype_code: A X = { X cmd_type = TYPE_A; X cmd_form = FORM_N; X } X | A SP form_code X = { X cmd_type = TYPE_A; X cmd_form = $3; X } X | E X = { X cmd_type = TYPE_E; X cmd_form = FORM_N; X } X | E SP form_code X = { X cmd_type = TYPE_E; X cmd_form = $3; X } X | I X = { X cmd_type = TYPE_I; X } X | L X = { X cmd_type = TYPE_L; X cmd_bytesz = 8; X } X | L SP byte_size X = { X cmd_type = TYPE_L; X cmd_bytesz = $3; X } X /* this is for a bug in the BBN ftp */ X | L byte_size X = { X cmd_type = TYPE_L; X cmd_bytesz = $2; X } X ; X Xstruct_code: F X = { X $$ = STRU_F; X } X | R X = { X $$ = STRU_R; X } X | P X = { X $$ = STRU_P; X } X ; X Xmode_code: S X = { X $$ = MODE_S; X } X | B X = { X $$ = MODE_B; X } X | C X = { X $$ = MODE_C; X } X ; X Xpathname: pathstring X = { X /* X * Problem: this production is used for all pathname X * processing, but only gives a 550 error reply. X * This is a valid reply in some cases but not in others. X */ X if ($1 && strncmp((char *) $1, "~", 1) == 0) { X $$ = (int)*glob((char *) $1); X if (globerr != NULL) { X reply(550, globerr); X $$ = NULL; X } X free((char *) $1); X } else X $$ = $1; X } X ; X Xpathstring: STRING X ; X Xcheck_login: /* empty */ X = { X if (logged_in) X $$ = 1; X else { X reply(530, "Please login with USER and PASS."); X $$ = 0; X } X } X ; X X%% X Xextern jmp_buf errcatch; X X#define CMD 0 /* beginning of command */ X#define ARGS 1 /* expect miscellaneous arguments */ X#define STR1 2 /* expect SP followed by STRING */ X#define STR2 3 /* expect STRING */ X#define OSTR 4 /* optional STRING */ X Xstruct tab { X char *name; X short token; X short state; X short implemented; /* 1 if command is implemented */ X char *help; X}; X Xstruct tab cmdtab[] = { /* In order defined in RFC 765 */ X { "USER", USER, STR1, 1, "<sp> username" }, X { "PASS", PASS, STR1, 1, "<sp> password" }, X { "ACCT", ACCT, STR1, 0, "(specify account)" }, X { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, X { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, X { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, X { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, X { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, X { "STRU", STRU, ARGS, 1, "(specify file structure)" }, X { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, X { "RETR", RETR, STR1, 1, "<sp> file-name" }, X { "STOR", STOR, STR1, 1, "<sp> file-name" }, X { "APPE", APPE, STR1, 1, "<sp> file-name" }, X { "MLFL", MLFL, OSTR, 0, "(mail file)" }, X { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, X { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, X { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, X { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, X { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, X { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, X { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, X { "REST", REST, STR1, 0, "(restart command)" }, X { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, X { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, X { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, X { "DELE", DELE, STR1, 1, "<sp> file-name" }, X { "CWD", CWD, OSTR, 1, "[ <sp> directory-name]" }, X { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, X { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, X { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, X { "SITE", SITE, STR1, 0, "(get site parameters)" }, X { "STAT", STAT, OSTR, 0, "(get server status)" }, X { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, X { "NOOP", NOOP, ARGS, 1, "" }, X { "MKD", XMKD, STR1, 1, "<sp> path-name" }, X { "XMKD", XMKD, STR1, 1, "<sp> path-name" }, X { "RMD", XRMD, STR1, 1, "<sp> path-name" }, X { "XRMD", XRMD, STR1, 1, "<sp> path-name" }, X { "PWD", XPWD, ARGS, 1, "(return current directory)" }, X { "XPWD", XPWD, ARGS, 1, "(return current directory)" }, X { "CDUP", XCUP, ARGS, 1, "(change to parent directory)" }, X { "XCUP", XCUP, ARGS, 1, "(change to parent directory)" }, X { "STOU", STOU, STR1, 1, "<sp> file-name" }, X { NULL, 0, 0, 0, 0 } X}; X Xstruct tab * Xlookup(cmd) X char *cmd; X{ X register struct tab *p; X X for (p = cmdtab; p->name != NULL; p++) X if (strcmp(cmd, p->name) == 0) X return (p); X return (0); X} X X#include <arpa/telnet.h> X X/* X * getline - a hacked up version of fgets to ignore TELNET escape codes. X */ Xchar * Xgetline(s, n, iop) X char *s; X register FILE *iop; X{ X register c; X register char *cs; X X cs = s; X/* tmpline may contain saved command from urgent mode interruption */ X for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { X *cs++ = tmpline[c]; X if (tmpline[c] == '\n') { X *cs++ = '\0'; X if (debug) { X syslog(LOG_DEBUG, "FTPD: command: %s", s); X } X tmpline[0] = '\0'; X return(s); X } X if (c == 0) { X tmpline[0] = '\0'; X } X } X while (--n > 0 && (c = getc(iop)) != EOF) { X c = 0377 & c; X while (c == IAC) { X switch (c = 0377 & getc(iop)) { X case WILL: X case WONT: X c = 0377 & getc(iop); X printf("%c%c%c", IAC, WONT, c); X (void) fflush(stdout); X break; X case DO: X case DONT: X c = 0377 & getc(iop); X printf("%c%c%c", IAC, DONT, c); X (void) fflush(stdout); X break; X default: X break; X } X c = 0377 & getc(iop); /* try next character */ X } X *cs++ = c; X if (c=='\n') X break; X } X if (c == EOF && cs == s) X return (NULL); X *cs++ = '\0'; X if (debug) { X syslog(LOG_DEBUG, "FTPD: command: %s", s); X } X return (s); X} X Xstatic int Xtoolong() X{ X time_t now; X extern char *ctime(); X extern time_t time(); X X reply(421, X "Timeout (%d seconds): closing control connection.", timeout); X (void) time(&now); X if (logging) { X syslog(LOG_INFO, X "FTPD: User %s timed out after %d seconds at %s", X (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now)); X } X dologout(1); X} X Xyylex() X{ X static int cpos, state; X register char *cp; X register struct tab *p; X int n; X char c; X X for (;;) { X switch (state) { X X case CMD: X (void) signal(SIGALRM, toolong); X (void) alarm((unsigned) timeout); X if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { X reply(221, "You could at least say goodbye."); X dologout(0); X } X (void) alarm(0); X if (index(cbuf, '\r')) { X cp = index(cbuf, '\r'); X cp[0] = '\n'; cp[1] = 0; X } X if (index(cbuf, ' ')) X cpos = index(cbuf, ' ') - cbuf; X else X cpos = index(cbuf, '\n') - cbuf; X if (cpos == 0) { X cpos = 4; X } X c = cbuf[cpos]; X cbuf[cpos] = '\0'; X upper(cbuf); X p = lookup(cbuf); X cbuf[cpos] = c; X if (p != 0) { X if (p->implemented == 0) { X nack(p->name); X longjmp(errcatch,0); X /* NOTREACHED */ X } X state = p->state; X yylval = (int) p->name; X return (p->token); X } X break; X X case OSTR: X if (cbuf[cpos] == '\n') { X state = CMD; X return (CRLF); X } X /* FALL THRU */ X X case STR1: X if (cbuf[cpos] == ' ') { X cpos++; X state = STR2; X return (SP); X } X break; X X case STR2: X cp = &cbuf[cpos]; X n = strlen(cp); X cpos += n - 1; X /* X * Make sure the string is nonempty and \n terminated. X */ X if (n > 1 && cbuf[cpos] == '\n') { X cbuf[cpos] = '\0'; X yylval = copy(cp); X cbuf[cpos] = '\n'; X state = ARGS; X return (STRING); X } X break; X X case ARGS: X if (isdigit(cbuf[cpos])) { X cp = &cbuf[cpos]; X while (isdigit(cbuf[++cpos])) X ; X c = cbuf[cpos]; X cbuf[cpos] = '\0'; X yylval = atoi(cp); X cbuf[cpos] = c; X return (NUMBER); X } X switch (cbuf[cpos++]) { X X case '\n': X state = CMD; X return (CRLF); X X case ' ': X return (SP); X X case ',': X return (COMMA); X X case 'A': X case 'a': X return (A); X X case 'B': X case 'b': X return (B); X X case 'C': X case 'c': X return (C); X X case 'E': X case 'e': X return (E); X X case 'F': X case 'f': X return (F); X X case 'I': X case 'i': X return (I); X X case 'L': X case 'l': X return (L); X X case 'N': X case 'n': X return (N); X X case 'P': X case 'p': X return (P); X X case 'R': X case 'r': X return (R); X X case 'S': X case 's': X return (S); X X case 'T': X case 't': X return (T); X X } X break; X X default: X fatal("Unknown state in scanner."); X } X yyerror((char *) 0); X state = CMD; X longjmp(errcatch,0); X } X} X Xupper(s) X char *s; X{ X while (*s != '\0') { X if (islower(*s)) X *s = toupper(*s); X s++; X } X} X Xcopy(s) X char *s; X{ X char *p; X extern char *malloc(), *strcpy(); X X p = malloc((unsigned) strlen(s) + 1); X if (p == NULL) X fatal("Ran out of memory."); X (void) strcpy(p, s); X return ((int)p); X} X Xhelp(s) X char *s; X{ X register struct tab *c; X register int width, NCMDS; X X width = 0, NCMDS = 0; X for (c = cmdtab; c->name != NULL; c++) { X int len = strlen(c->name) + 1; X X if (len > width) X width = len; X NCMDS++; X } X width = (width + 8) &~ 7; X if (s == 0) { X register int i, j, w; X int columns, lines; X X lreply(214, X "The following commands are recognized (* =>'s unimplemented)."); X columns = 76 / width; X if (columns == 0) X columns = 1; X lines = (NCMDS + columns - 1) / columns; X for (i = 0; i < lines; i++) { X printf(" "); X for (j = 0; j < columns; j++) { X c = cmdtab + j * lines + i; X printf("%s%c", c->name, X c->implemented ? ' ' : '*'); X if (c + lines >= &cmdtab[NCMDS]) X break; X w = strlen(c->name) + 1; X while (w < width) { X putchar(' '); X w++; X } X } X printf("\r\n"); X } X (void) fflush(stdout); X reply(214, "Direct comments to ftp-bugs@%s.", hostname); X return; X } X upper(s); X c = lookup(s); X if (c == (struct tab *)0) { X reply(502, "Unknown command %s.", s); X return; X } X if (c->implemented) X reply(214, "Syntax: %s %s", c->name, c->help); X else X reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help); X} END-of-ftpcmd.y echo x - ftpd.c sed 's/^X//' >ftpd.c << 'END-of-ftpd.c' X/* X * Copyright (c) 1985 Regents of the University of California. X * All rights reserved. X * X * Redistribution and use in source and binary forms are permitted X * provided that the above copyright notice and this paragraph are X * duplicated in all such forms and that any documentation, X * advertising materials, and other materials related to such X * distribution and use acknowledge that the software was developed X * by the University of California, Berkeley. The name of the X * University may not be used to endorse or promote products derived X * from this software without specific prior written permission. X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED X * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. X */ X X#ifndef lint Xchar copyright[] = X"@(#) Copyright (c) 1985 Regents of the University of California.\n\ X All rights reserved.\n"; X#endif /* not lint */ X X#ifndef lint Xstatic char sccsid[] = "@(#)ftpd.c 5.16 (Berkeley) 10/30/88"; X#endif /* not lint */ X X/* X * FTP server. X */ X#include <sys/param.h> X#include <sys/stat.h> X#include <sys/ioctl.h> X#include <sys/socket.h> X#include <sys/file.h> X#include <sys/wait.h> X X#include <netinet/in.h> X X#include <arpa/ftp.h> X#include <arpa/inet.h> X#include <arpa/telnet.h> X X#include <stdio.h> X#include <signal.h> X#include <pwd.h> X#include <setjmp.h> X#include <netdb.h> X#include <errno.h> X#include <strings.h> X#include <syslog.h> X X/* X * File containing login names X * NOT to be used on this machine. X * Commonly used to disallow uucp. X */ X#define FTPUSERS "/etc/ftpusers" X Xextern int errno; Xextern char *sys_errlist[]; Xextern char *crypt(); Xextern char version[]; Xextern char *home; /* pointer to home directory for glob */ Xextern FILE *popen(), *fopen(), *freopen(); Xextern int pclose(), fclose(); Xextern char *getline(); Xextern char cbuf[]; X Xstruct sockaddr_in ctrl_addr; Xstruct sockaddr_in data_source; Xstruct sockaddr_in data_dest; Xstruct sockaddr_in his_addr; X Xint data; Xjmp_buf errcatch, urgcatch; Xint logged_in; Xstruct passwd *pw; Xint debug; Xint timeout = 900; /* timeout after 15 minutes of inactivity */ Xint logging; Xint guest; Xint wtmp; Xint type; Xint form; Xint stru; /* avoid C keyword */ Xint mode; Xint usedefault = 1; /* for data transfers */ Xint pdata; /* for passive mode */ Xint unique; Xint transflag; Xchar tmpline[7]; Xchar hostname[32]; Xchar remotehost[32]; X X/* X * Timeout intervals for retrying connections X * to hosts that don't accept PORT cmds. This X * is a kludge, but given the problems with TCP... X */ X#define SWAITMAX 90 /* wait at most 90 seconds */ X#define SWAITINT 5 /* interval between retries */ X Xint swaitmax = SWAITMAX; Xint swaitint = SWAITINT; X Xint lostconn(); Xint myoob(); XFILE *getdatasock(), *dataconn(); X Xmain(argc, argv) X int argc; X char *argv[]; X{ X int addrlen, on = 1; X long pgid; X char *cp; X X addrlen = sizeof (his_addr); X if (getpeername(0, &his_addr, &addrlen) < 0) { X syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); X exit(1); X } X addrlen = sizeof (ctrl_addr); X if (getsockname(0, (char *) &ctrl_addr, &addrlen) < 0) { X syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); X exit(1); X } X data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); X debug = 0; X openlog("ftpd", LOG_PID, LOG_DAEMON); X argc--, argv++; X while (argc > 0 && *argv[0] == '-') { X for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { X X case 'v': X debug = 1; X break; X X case 'd': X debug = 1; X break; X X case 'l': X logging = 1; X break; X X case 't': X timeout = atoi(++cp); X goto nextopt; X break; X X default: X fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n", X *cp); X break; X } Xnextopt: X argc--, argv++; X } X (void) freopen("/dev/null", "w", stderr); X (void) signal(SIGPIPE, lostconn); X (void) signal(SIGCHLD, SIG_IGN); X if ((int)signal(SIGURG, myoob) < 0) X syslog(LOG_ERR, "signal: %m"); X X /* handle urgent data inline */ X#ifdef SO_OOBINLINE X if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) { X syslog(LOG_ERR, "setsockopt: %m"); X } X#endif SO_OOBINLINE X pgid = getpid(); X if (ioctl(fileno(stdin), SIOCSPGRP, (char *) &pgid) < 0) { X syslog(LOG_ERR, "ioctl: %m"); X } X dolog(&his_addr); X /* do telnet option negotiation here */ X /* X * Set up default state X */ X logged_in = 0; X data = -1; X type = TYPE_A; X form = FORM_N; X stru = STRU_F; X mode = MODE_S; X tmpline[0] = '\0'; X (void) gethostname(hostname, sizeof (hostname)); X reply(220, "%s FTP server (%s) ready.", X hostname, version); X for (;;) { X (void) setjmp(errcatch); X (void) yyparse(); X } X} X Xlostconn() X{ X X if (debug) X syslog(LOG_DEBUG, "lost connection"); X dologout(-1); X} X Xstatic char ttyline[20]; X X/* X * Helper function for sgetpwnam(). X */ Xchar * Xsgetsave(s) X char *s; X{ X#ifdef notdef X char *new = strdup(s); X#else X char *malloc(); X char *new = malloc((unsigned) strlen(s) + 1); X#endif X X if (new == NULL) { X reply(553, "Local resource failure"); X dologout(1); X } X#ifndef notdef X (void) strcpy(new, s); X#endif X return (new); X} X X/* X * Save the result of a getpwnam. Used for USER command, since X * the data returned must not be clobbered by any other command X * (e.g., globbing). X */ Xstruct passwd * Xsgetpwnam(name) X char *name; X{ X static struct passwd save; X register struct passwd *p; X char *sgetsave(); X X if ((p = getpwnam(name)) == NULL) X return (p); X if (save.pw_name) { X free(save.pw_name); X free(save.pw_passwd); X free(save.pw_comment); X free(save.pw_gecos); X free(save.pw_dir); X free(save.pw_shell); X } X save = *p; X save.pw_name = sgetsave(p->pw_name); X save.pw_passwd = sgetsave(p->pw_passwd); X save.pw_comment = sgetsave(p->pw_comment); X save.pw_gecos = sgetsave(p->pw_gecos); X save.pw_dir = sgetsave(p->pw_dir); X save.pw_shell = sgetsave(p->pw_shell); X return (&save); X} X Xpass(passwd) X char *passwd; X{ X char *xpasswd; X X if (logged_in || pw == NULL) { X reply(503, "Login with USER first."); X return; X } X if (!guest) { /* "ftp" is only account allowed no password */ X xpasswd = crypt(passwd, pw->pw_passwd); X /* The strcmp does not catch null passwords! */ X if (*pw->pw_passwd == '\0' || strcmp(xpasswd, pw->pw_passwd)) { X reply(530, "Login incorrect."); X pw = NULL; X return; X } X } X setegid(pw->pw_gid); X initgroups(pw->pw_name, pw->pw_gid); X if (chdir(pw->pw_dir)) { X reply(530, "User %s: can't change directory to %s.", X pw->pw_name, pw->pw_dir); X goto bad; X } X X /* grab wtmp before chroot */ X wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND); X if (guest && chroot(pw->pw_dir) < 0) { X reply(550, "Can't set guest privileges."); X if (wtmp >= 0) { X (void) close(wtmp); X wtmp = -1; X } X goto bad; X } X if (!guest) X reply(230, "User %s logged in.", pw->pw_name); X else X reply(230, "Guest login ok, access restrictions apply."); X logged_in = 1; X (void)sprintf(ttyline, "ftp%d", getpid()); X logwtmp(ttyline, pw->pw_name, remotehost); X seteuid(pw->pw_uid); X home = pw->pw_dir; /* home dir for globbing */ X return; Xbad: X seteuid(0); X pw = NULL; X} X Xretrieve(cmd, name) X char *cmd, *name; X{ X FILE *fin, *dout; X struct stat st; X int (*closefunc)(), tmp; X X if (cmd == 0) { X#ifdef notdef X /* no remote command execution -- it's a security hole */ X if (*name == '|') X fin = popen(name + 1, "r"), closefunc = pclose; X else X#endif X fin = fopen(name, "r"), closefunc = fclose; X } else { X char line[BUFSIZ]; X X (void) sprintf(line, cmd, name), name = line; X fin = popen(line, "r"), closefunc = pclose; X } X if (fin == NULL) { X if (errno != 0) X reply(550, "%s: %s.", name, sys_errlist[errno]); X return; X } X st.st_size = 0; X if (cmd == 0 && X (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { X reply(550, "%s: not a plain file.", name); X goto done; X } X dout = dataconn(name, st.st_size, "w"); X if (dout == NULL) X goto done; X if ((tmp = send_data(fin, dout)) > 0 || ferror(dout) > 0) { X reply(550, "%s: %s.", name, sys_errlist[errno]); X } X else if (tmp == 0) { X reply(226, "Transfer complete."); X } X (void) fclose(dout); X data = -1; X pdata = -1; Xdone: X (*closefunc)(fin); X} X Xstore(name, mode) X char *name, *mode; X{ X FILE *fout, *din; X int (*closefunc)(), dochown = 0, tmp; X char *gunique(), *local; X X#ifdef notdef X /* no remote command execution -- it's a security hole */ X if (name[0] == '|') X fout = popen(&name[1], "w"), closefunc = pclose; X else X#endif X { X struct stat st; X X local = name; X if (stat(name, &st) < 0) { X dochown++; X } X else if (unique) { X if ((local = gunique(name)) == NULL) { X return; X } X dochown++; X } X fout = fopen(local, mode), closefunc = fclose; X } X if (fout == NULL) { X reply(553, "%s: %s.", local, sys_errlist[errno]); X return; X } X din = dataconn(local, (off_t)-1, "r"); X if (din == NULL) X goto done; X if ((tmp = receive_data(din, fout)) > 0 || ferror(fout) > 0) { X reply(552, "%s: %s.", local, sys_errlist[errno]); X } X else if (tmp == 0 && !unique) { X reply(226, "Transfer complete."); X } X else if (tmp == 0 && unique) { X reply(226, "Transfer complete (unique file name:%s).", local); X } X (void) fclose(din); X data = -1; X pdata = -1; Xdone: X if (dochown) X (void) chown(local, pw->pw_uid, -1); X (*closefunc)(fout); X} X XFILE * Xgetdatasock(mode) X char *mode; X{ X int s, on = 1; X X if (data >= 0) X return (fdopen(data, mode)); X s = socket(AF_INET, SOCK_STREAM, 0); X if (s < 0) X return (NULL); X seteuid(0); X if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) < 0) X goto bad; X /* anchor socket to avoid multi-homing problems */ X data_source.sin_family = AF_INET; X data_source.sin_addr = ctrl_addr.sin_addr; X if (bind(s, &data_source, sizeof (data_source)) < 0) X goto bad; X seteuid(pw->pw_uid); X return (fdopen(s, mode)); Xbad: X seteuid(pw->pw_uid); X (void) close(s); X return (NULL); X} X XFILE * Xdataconn(name, size, mode) X char *name; X off_t size; X char *mode; X{ X char sizebuf[32]; X FILE *file; X int retry = 0; X X if (size >= 0) X (void) sprintf (sizebuf, " (%ld bytes)", size); X else X (void) strcpy(sizebuf, ""); X if (pdata > 0) { X struct sockaddr_in from; X int s, fromlen = sizeof(from); X X s = accept(pdata, &from, &fromlen); X if (s < 0) { X reply(425, "Can't open data connection."); X (void) close(pdata); X pdata = -1; X return(NULL); X } X (void) close(pdata); X pdata = s; X reply(150, "Opening data connection for %s (%s mode)%s.", X name, type == TYPE_A ? "ascii" : "binary", sizebuf); X return(fdopen(pdata, mode)); X } X if (data >= 0) { X reply(125, "Using existing data connection for %s%s.", X name, sizebuf); X usedefault = 1; X return (fdopen(data, mode)); X } X if (usedefault) X data_dest = his_addr; X usedefault = 1; X file = getdatasock(mode); X if (file == NULL) { X reply(425, "Can't create data socket (%s,%d): %s.", X inet_ntoa(data_source.sin_addr), X ntohs(data_source.sin_port), X sys_errlist[errno]); X return (NULL); X } X data = fileno(file); X while (connect(data, &data_dest, sizeof (data_dest)) < 0) { X if (errno == EADDRINUSE && retry < swaitmax) { X sleep((unsigned) swaitint); X retry += swaitint; X continue; X } X reply(425, "Can't build data connection: %s.", X sys_errlist[errno]); X (void) fclose(file); X data = -1; X return (NULL); X } X reply(150, "Opening data connection for %s (%s mode)%s.", X name, type == TYPE_A ? "ascii" : "binary", sizebuf); X return (file); X} X X/* X * Tranfer the contents of "instr" to X * "outstr" peer using the appropriate X * encapulation of the date subject X * to Mode, Structure, and Type. X * X * NB: Form isn't handled. X */ Xsend_data(instr, outstr) X FILE *instr, *outstr; X{ X register int c; X int netfd, filefd, cnt; X char buf[BUFSIZ]; X X transflag++; X if (setjmp(urgcatch)) { X transflag = 0; X return(-1); X } X switch (type) { X X case TYPE_A: X while ((c = getc(instr)) != EOF) { X if (c == '\n') { X if (ferror (outstr)) { X transflag = 0; X return (1); X } X (void) putc('\r', outstr); X } X (void) putc(c, outstr); X /* if (c == '\r') */ X /* putc ('\0', outstr); */ X } X transflag = 0; X if (ferror (instr) || ferror (outstr)) { X return (1); X } X return (0); X X case TYPE_I: X case TYPE_L: X netfd = fileno(outstr); X filefd = fileno(instr); X X while ((cnt = read(filefd, buf, sizeof (buf))) > 0) { X if (write(netfd, buf, cnt) < 0) { X transflag = 0; X return (1); X } X } X transflag = 0; X return (cnt < 0); X } X reply(550, "Unimplemented TYPE %d in send_data", type); X transflag = 0; X return (-1); X} X X/* X * Transfer data from peer to X * "outstr" using the appropriate X * encapulation of the data subject X * to Mode, Structure, and Type. X * X * N.B.: Form isn't handled. X */ Xreceive_data(instr, outstr) X FILE *instr, *outstr; X{ X register int c; X int cnt; X char buf[BUFSIZ]; X X X transflag++; X if (setjmp(urgcatch)) { X transflag = 0; X return(-1); X } X switch (type) { X X case TYPE_I: X case TYPE_L: X while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) { X if (write(fileno(outstr), buf, cnt) < 0) { X transflag = 0; X return (1); X } X } X transflag = 0; X return (cnt < 0); X X case TYPE_E: X reply(553, "TYPE E not implemented."); X transflag = 0; X return (-1); X X case TYPE_A: X while ((c = getc(instr)) != EOF) { X while (c == '\r') { X if (ferror (outstr)) { X transflag = 0; X return (1); X } X if ((c = getc(instr)) != '\n') X (void) putc ('\r', outstr); X /* if (c == '\0') */ X /* continue; */ X } X (void) putc (c, outstr); X } X transflag = 0; X if (ferror (instr) || ferror (outstr)) X return (1); X return (0); X } X transflag = 0; X fatal("Unknown type in receive_data."); X /*NOTREACHED*/ X} X Xfatal(s) X char *s; X{ X reply(451, "Error in server: %s\n", s); X reply(221, "Closing connection due to server error."); X dologout(0); X} X Xreply(n, s, p0, p1, p2, p3, p4) X int n; X char *s; X{ X X printf("%d ", n); X printf(s, p0, p1, p2, p3, p4); X printf("\r\n"); X (void) fflush(stdout); X if (debug) { X syslog(LOG_DEBUG, "<--- %d ", n); X syslog(LOG_DEBUG, s, p0, p1, p2, p3, p4); X } X} X Xlreply(n, s, p0, p1, p2, p3, p4) X int n; X char *s; X{ X printf("%d-", n); X printf(s, p0, p1, p2, p3, p4); X printf("\r\n"); X (void) fflush(stdout); X if (debug) { X syslog(LOG_DEBUG, "<--- %d- ", n); X syslog(LOG_DEBUG, s, p0, p1, p2, p3, p4); X } X} X Xack(s) X char *s; X{ X reply(250, "%s command successful.", s); X} X Xnack(s) X char *s; X{ X reply(502, "%s command not implemented.", s); X} X Xyyerror(s) X char *s; X{ X char *cp; X X cp = index(cbuf,'\n'); X *cp = '\0'; X reply(500, "'%s': command not understood.",cbuf); X} X Xdelete(name) X char *name; X{ X struct stat st; X X if (stat(name, &st) < 0) { X reply(550, "%s: %s.", name, sys_errlist[errno]); X return; X } X if ((st.st_mode&S_IFMT) == S_IFDIR) { X if (rmdir(name) < 0) { X reply(550, "%s: %s.", name, sys_errlist[errno]); X return; X } X goto done; X } X if (unlink(name) < 0) { X reply(550, "%s: %s.", name, sys_errlist[errno]); X return; X } Xdone: X ack("DELE"); X} X Xcwd(path) X char *path; X{ X X if (chdir(path) < 0) { X reply(550, "%s: %s.", path, sys_errlist[errno]); X return; X } X ack("CWD"); X} X Xmakedir(name) X char *name; X{ X struct stat st; X int dochown = stat(name, &st) < 0; X X if (mkdir(name, 0777) < 0) { X reply(550, "%s: %s.", name, sys_errlist[errno]); X return; X } X if (dochown) X (void) chown(name, pw->pw_uid, -1); X reply(257, "MKD command successful."); X} X Xremovedir(name) X char *name; X{ X X if (rmdir(name) < 0) { X reply(550, "%s: %s.", name, sys_errlist[errno]); X return; X } X ack("RMD"); X} X Xpwd() X{ X char path[MAXPATHLEN + 1]; X X if (getwd(path) == NULL) { X reply(550, "%s.", path); X return; X } X reply(257, "\"%s\" is current directory.", path); X} X Xchar * Xrenamefrom(name) X char *name; X{ X struct stat st; X X if (stat(name, &st) < 0) { X reply(550, "%s: %s.", name, sys_errlist[errno]); X return ((char *)0); X } X reply(350, "File exists, ready for destination name"); X return (name); X} X Xrenamecmd(from, to) X char *from, *to; X{ X X if (rename(from, to) < 0) { X reply(550, "rename: %s.", sys_errlist[errno]); X return; X } X ack("RNTO"); X} X Xdolog(sin) X struct sockaddr_in *sin; X{ X struct hostent *hp = gethostbyaddr(&sin->sin_addr, X sizeof (struct in_addr), AF_INET); X time_t t; X extern char *ctime(); X X if (hp) { X (void) strncpy(remotehost, hp->h_name, sizeof (remotehost)); X endhostent(); X } else X (void) strncpy(remotehost, inet_ntoa(sin->sin_addr), X sizeof (remotehost)); X if (!logging) X return; X t = time((time_t *) 0); X syslog(LOG_INFO,"FTPD: connection from %s at %s", remotehost, ctime(&t)); X} X X/* X * Record logout in wtmp file X * and exit with supplied status. X */ Xdologout(status) X int status; X{ X if (logged_in) { X (void) seteuid(0); X logwtmp(ttyline, "", ""); X } X /* beware of flushing buffers after a SIGPIPE */ X _exit(status); X} X X/* X * Check user requesting login priviledges. X * Disallow anyone who does not have a standard X * shell returned by getusershell() (/etc/shells). X * Disallow anyone mentioned in the file FTPUSERS X * to allow people such as uucp to be avoided. X */ Xcheckuser(name) X register char *name; X{ X register char *cp; X FILE *fd; X struct passwd *p; X char *shell; X int found = 0; X char line[BUFSIZ], *index(), *getusershell(); X X if ((p = getpwnam(name)) == NULL) X return (0); X if ((shell = p->pw_shell) == NULL || *shell == 0) X shell = "/bin/sh"; X while ((cp = getusershell()) != NULL) X if (strcmp(cp, shell) == 0) X break; X endusershell(); X if (cp == NULL) X return (0); X if ((fd = fopen(FTPUSERS, "r")) == NULL) X return (1); X while (fgets(line, sizeof (line), fd) != NULL) { X if ((cp = index(line, '\n')) != NULL) X *cp = '\0'; X if (strcmp(line, name) == 0) { X found++; X break; X } X } X (void) fclose(fd); X return (!found); X} X Xmyoob() X{ X char *cp; X X /* only process if transfer occurring */ X if (!transflag) { X return; X } X cp = tmpline; X if (getline(cp, 7, stdin) == NULL) { X reply(221, "You could at least say goodby."); X dologout(0); X } X upper(cp); X if (strcmp(cp, "ABOR\r\n")) X return; X tmpline[0] = '\0'; X reply(426,"Transfer aborted. Data connection closed."); X reply(226,"Abort successful"); X longjmp(urgcatch, 1); X} X X/* X * Note: The 530 reply codes could be 4xx codes, except nothing is X * given in the state tables except 421 which implies an exit. (RFC959) X */ Xpassive() X{ X int len; X struct sockaddr_in tmp; X register char *p, *a; X X pdata = socket(AF_INET, SOCK_STREAM, 0); X if (pdata < 0) { X reply(530, "Can't open passive connection"); X return; X } X tmp = ctrl_addr; X tmp.sin_port = 0; X seteuid(0); X if (bind(pdata, (struct sockaddr *) &tmp, sizeof(tmp)) < 0) { X seteuid(pw->pw_uid); X (void) close(pdata); X pdata = -1; X reply(530, "Can't open passive connection"); X return; X } X seteuid(pw->pw_uid); X len = sizeof(tmp); X if (getsockname(pdata, (char *) &tmp, &len) < 0) { X (void) close(pdata); X pdata = -1; X reply(530, "Can't open passive connection"); X return; X } X if (listen(pdata, 1) < 0) { X (void) close(pdata); X pdata = -1; X reply(530, "Can't open passive connection"); X return; X } X a = (char *) &tmp.sin_addr; X p = (char *) &tmp.sin_port; X X#define UC(b) (((int) b) & 0xff) X X reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), X UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); X} X Xchar * Xgunique(local) X char *local; X{ X static char new[MAXPATHLEN]; X char *cp = rindex(local, '/'); X int d, count=0; X char ext = '1'; X X if (cp) { X *cp = '\0'; X } X d = access(cp ? local : ".", 2); X if (cp) { X *cp = '/'; X } X if (d < 0) { X syslog(LOG_ERR, "%s: %m", local); X return((char *) 0); X } X (void) strcpy(new, local); X cp = new + strlen(new); X *cp++ = '.'; X while (!d) { X if (++count == 100) { X reply(452, "Unique file name not cannot be created."); X return((char *) 0); X } X *cp++ = ext; X *cp = '\0'; X if (ext == '9') { X ext = '0'; X } X else { X ext++; X } X if ((d = access(new, 0)) < 0) { X break; X } X if (ext != '0') { X cp--; X } X else if (*(cp - 2) == '.') { X *(cp - 1) = '1'; X } X else { X *(cp - 2) = *(cp - 2) + 1; X cp--; X } X } X return(new); X} END-of-ftpd.c echo x - glob.c sed 's/^X//' >glob.c << 'END-of-glob.c' X/* X * Copyright (c) 1980 Regents of the University of California. X * All rights reserved. X * X * Redistribution and use in source and binary forms are permitted X * provided that the above copyright notice and this paragraph are X * duplicated in all such forms and that any documentation, X * advertising materials, and other materials related to such X * distribution and use acknowledge that the software was developed X * by the University of California, Berkeley. The name of the X * University may not be used to endorse or promote products derived X * from this software without specific prior written permission. X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED X * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. X */ X X#ifndef lint Xstatic char sccsid[] = "@(#)glob.c 5.4 (Berkeley) 6/29/88"; X#endif /* not lint */ X X/* X * C-shell glob for random programs. X */ X X#include <sys/param.h> X#include <sys/stat.h> X#include <sys/dir.h> X X#include <stdio.h> X#include <errno.h> X#include <pwd.h> X X#define QUOTE 0200 X#define TRIM 0177 X#define eq(a,b) (strcmp(a, b)==0) X#define GAVSIZ (NCARGS/6) X#define isdir(d) ((d.st_mode & S_IFMT) == S_IFDIR) X Xstatic char **gargv; /* Pointer to the (stack) arglist */ Xstatic short gargc; /* Number args in gargv */ Xstatic short gnleft; Xstatic short gflag; Xstatic int tglob(); Xchar **glob(); Xchar *globerr; Xchar *home; Xstruct passwd *getpwnam(); Xextern int errno; Xstatic char *strspl(), *strend(); Xchar *malloc(), *strcpy(), *strcat(); Xchar **copyblk(); X Xstatic int globcnt; X Xchar *globchars = "`{[*?"; X Xstatic char *gpath, *gpathp, *lastgpathp; Xstatic int globbed; Xstatic char *entp; Xstatic char **sortbas; X Xchar ** Xglob(v) X register char *v; X{ X char agpath[BUFSIZ]; X char *agargv[GAVSIZ]; X char *vv[2]; X vv[0] = v; X vv[1] = 0; X gflag = 0; X rscan(vv, tglob); X if (gflag == 0) X return (copyblk(vv)); X X globerr = 0; X gpath = agpath; gpathp = gpath; *gpathp = 0; X lastgpathp = &gpath[sizeof agpath - 2]; X ginit(agargv); globcnt = 0; X collect(v); X if (globcnt == 0 && (gflag&1)) { X blkfree(gargv), gargv = 0; X return (0); X } else X return (gargv = copyblk(gargv)); X} X Xstatic Xginit(agargv) X char **agargv; X{ X X agargv[0] = 0; gargv = agargv; sortbas = agargv; gargc = 0; X gnleft = NCARGS - 4; X} X Xstatic Xcollect(as) X register char *as; X{ X if (eq(as, "{") || eq(as, "{}")) { X Gcat(as, ""); X sort(); X } else X acollect(as); X} X Xstatic Xacollect(as) X register char *as; X{ X register int ogargc = gargc; X X gpathp = gpath; *gpathp = 0; globbed = 0; X expand(as); X if (gargc != ogargc) X sort(); X} X Xstatic Xsort() X{ X register char **p1, **p2, *c; X char **Gvp = &gargv[gargc]; X X p1 = sortbas; X while (p1 < Gvp-1) { X p2 = p1; X while (++p2 < Gvp) X if (strcmp(*p1, *p2) > 0) X c = *p1, *p1 = *p2, *p2 = c; X p1++; X } X sortbas = Gvp; X} X Xstatic Xexpand(as) X char *as; X{ X register char *cs; X register char *sgpathp, *oldcs; X struct stat stb; X X sgpathp = gpathp; X cs = as; X if (*cs == '~' && gpathp == gpath) { X addpath('~'); X for (cs++; letter(*cs) || digit(*cs) || *cs == '-';) X addpath(*cs++); X if (!*cs || *cs == '/') { X if (gpathp != gpath + 1) { X *gpathp = 0; X if (gethdir(gpath + 1)) X globerr = "Unknown user name after ~"; X (void) strcpy(gpath, gpath + 1); X } else X (void) strcpy(gpath, home); X gpathp = strend(gpath); X } X } X while (!any(*cs, globchars)) { X if (*cs == 0) { X if (!globbed) X Gcat(gpath, ""); X else if (stat(gpath, &stb) >= 0) { X Gcat(gpath, ""); X globcnt++; X } X goto endit; X } X addpath(*cs++); X } X oldcs = cs; X while (cs > as && *cs != '/') X cs--, gpathp--; X if (*cs == '/') X cs++, gpathp++; X *gpathp = 0; X if (*oldcs == '{') { X (void) execbrc(cs, ((char *)0)); X return; X } X matchdir(cs); Xendit: X gpathp = sgpathp; X *gpathp = 0; X} X Xstatic Xmatchdir(pattern) X char *pattern; X{ X struct stat stb; X register struct direct *dp; X DIR *dirp; X X dirp = opendir(gpath); X if (dirp == NULL) { X if (globbed) X return; X goto patherr2; X } X if (fstat(dirp->dd_fd, &stb) < 0) X goto patherr1; X if (!isdir(stb)) { X errno = ENOTDIR; X goto patherr1; X } X while ((dp = readdir(dirp)) != NULL) { X if (dp->d_ino == 0) X continue; X if (match(dp->d_name, pattern)) { X Gcat(gpath, dp->d_name); X globcnt++; X } X } X closedir(dirp); X return; X Xpatherr1: X closedir(dirp); Xpatherr2: X globerr = "Bad directory components"; X} X Xstatic Xexecbrc(p, s) X char *p, *s; X{ X char restbuf[BUFSIZ + 2]; X register char *pe, *pm, *pl; X int brclev = 0; X char *lm, savec, *sgpathp; X X for (lm = restbuf; *p != '{'; *lm++ = *p++) X continue; X for (pe = ++p; *pe; pe++) X switch (*pe) { X X case '{': X brclev++; X continue; X X case '}': X if (brclev == 0) X goto pend; X brclev--; X continue; X X case '[': X for (pe++; *pe && *pe != ']'; pe++) X continue; X continue; X } Xpend: X brclev = 0; X for (pl = pm = p; pm <= pe; pm++) X switch (*pm & (QUOTE|TRIM)) { X X case '{': X brclev++; X continue; X X case '}': X if (brclev) { X brclev--; X continue; X } X goto doit; X X case ','|QUOTE: X case ',': X if (brclev) X continue; Xdoit: X savec = *pm; X *pm = 0; X (void) strcpy(lm, pl); X (void) strcat(restbuf, pe + 1); X *pm = savec; X if (s == 0) { X sgpathp = gpathp; X expand(restbuf); X gpathp = sgpathp; X *gpathp = 0; X } else if (amatch(s, restbuf)) X return (1); X sort(); X pl = pm + 1; X if (brclev) X return (0); X continue; X X case '[': X for (pm++; *pm && *pm != ']'; pm++) X continue; X if (!*pm) X pm--; X continue; X } X if (brclev) X goto doit; X return (0); X} X Xstatic Xmatch(s, p) X char *s, *p; X{ X register int c; X register char *sentp; X char sglobbed = globbed; X X if (*s == '.' && *p != '.') X return (0); X sentp = entp; X entp = s; X c = amatch(s, p); X entp = sentp; X globbed = sglobbed; X return (c); X} X Xstatic Xamatch(s, p) X register char *s, *p; X{ X register int scc; X int ok, lc; X char *sgpathp; X struct stat stb; X int c, cc; X X globbed = 1; X for (;;) { X scc = *s++ & TRIM; X switch (c = *p++) { X X case '{': X return (execbrc(p - 1, s - 1)); X X case '[': X ok = 0; X lc = 077777; X while (cc = *p++) { X if (cc == ']') { X if (ok) X break; X return (0); X } X if (cc == '-') { X if (lc <= scc && scc <= *p++) X ok++; X } else X if (scc == (lc = cc)) X ok++; X } X if (cc == 0) X if (ok) X p--; X else X return 0; X continue; X X case '*': X if (!*p) X return (1); X if (*p == '/') { X p++; X goto slash; X } X s--; X do { X if (amatch(s, p)) X return (1); X } while (*s++); X return (0); X X case 0: X return (scc == 0); X X default: X if (c != scc) X return (0); X continue; X X case '?': X if (scc == 0) X return (0); X continue; X X case '/': X if (scc) X return (0); Xslash: X s = entp; X sgpathp = gpathp; X while (*s) X addpath(*s++); X addpath('/'); X if (stat(gpath, &stb) == 0 && isdir(stb)) X if (*p == 0) { X Gcat(gpath, ""); X globcnt++; X } else X expand(p); X gpathp = sgpathp; X *gpathp = 0; X return (0); X } X } X} X Xstatic XGmatch(s, p) X register char *s, *p; X{ X register int scc; X int ok, lc; X int c, cc; X X for (;;) { X scc = *s++ & TRIM; X switch (c = *p++) { X X case '[': X ok = 0; X lc = 077777; X while (cc = *p++) { X if (cc == ']') { X if (ok) X break; X return (0); X } X if (cc == '-') { X if (lc <= scc && scc <= *p++) X ok++; X } else X if (scc == (lc = cc)) X ok++; X } X if (cc == 0) X if (ok) X p--; X else X return 0; X continue; X X case '*': X if (!*p) X return (1); X for (s--; *s; s++) X if (Gmatch(s, p)) X return (1); X return (0); X X case 0: X return (scc == 0); X X default: X if ((c & TRIM) != scc) X return (0); X continue; X X case '?': X if (scc == 0) X return (0); X continue; X X } X } X} X Xstatic XGcat(s1, s2) X register char *s1, *s2; X{ X register int len = strlen(s1) + strlen(s2) + 1; X X if (len >= gnleft || gargc >= GAVSIZ - 1) X globerr = "Arguments too long"; X else { X gargc++; X gnleft -= len; X gargv[gargc] = 0; X gargv[gargc - 1] = strspl(s1, s2); X } X} X Xstatic Xaddpath(c) X char c; X{ X X if (gpathp >= lastgpathp) X globerr = "Pathname too long"; X else { X *gpathp++ = c; X *gpathp = 0; X } X} X Xstatic Xrscan(t, f) X register char **t; X int (*f)(); X{ X register char *p, c; X X while (p = *t++) { X if (f == tglob) X if (*p == '~') X gflag |= 2; X else if (eq(p, "{") || eq(p, "{}")) X continue; X while (c = *p++) X (*f)(c); X } X} X/* Xstatic Xscan(t, f) X register char **t; X int (*f)(); X{ X register char *p, c; X X while (p = *t++) X while (c = *p) X *p++ = (*f)(c); X} */ X Xstatic Xtglob(c) X register char c; X{ X X if (any(c, globchars)) X gflag |= c == '{' ? 2 : 1; X return (c); X} X/* Xstatic Xtrim(c) X char c; X{ X X return (c & TRIM); X} */ X X Xletter(c) X register char c; X{ X X return (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_'); X} X Xdigit(c) X register char c; X{ X X return (c >= '0' && c <= '9'); X} X Xany(c, s) X register int c; X register char *s; X{ X X while (*s) X if (*s++ == c) X return(1); X return(0); X} Xblklen(av) X register char **av; X{ X register int i = 0; X X while (*av++) X i++; X return (i); X} X Xchar ** Xblkcpy(oav, bv) X char **oav; X register char **bv; X{ X register char **av = oav; X X while (*av++ = *bv++) X continue; X return (oav); X} X Xblkfree(av0) X char **av0; X{ X register char **av = av0; X X while (*av) X free(*av++); X free((char *)av0); X} X Xstatic Xchar * Xstrspl(cp, dp) X register char *cp, *dp; X{ X register char *ep = malloc((unsigned)(strlen(cp) + strlen(dp) + 1)); X X if (ep == (char *)0) X fatal("Out of memory"); X (void) strcpy(ep, cp); X (void) strcat(ep, dp); X return (ep); X} X Xchar ** Xcopyblk(v) X register char **v; X{ X register char **nv = (char **)malloc((unsigned)((blklen(v) + 1) * X sizeof(char **))); X if (nv == (char **)0) X fatal("Out of memory"); X X return (blkcpy(nv, v)); X} X Xstatic Xchar * Xstrend(cp) X register char *cp; X{ X X while (*cp) X cp++; X return (cp); X} X/* X * Extract a home directory from the password file X * The argument points to a buffer where the name of the X * user whose home directory is sought is currently. X * We write the home directory of the user back there. X */ Xgethdir(home) X char *home; X{ X register struct passwd *pp = getpwnam(home); X X if (pp == 0) X return (1); X (void) strcpy(home, pp->pw_dir); X return (0); X} END-of-glob.c echo x - logwtmp.c sed 's/^X//' >logwtmp.c << 'END-of-logwtmp.c' X/* X * Copyright (c) 1988 The Regents of the University of California. X * All rights reserved. X * X * Redistribution and use in source and binary forms are permitted X * provided that the above copyright notice and this paragraph are X * duplicated in all such forms and that any documentation, X * advertising materials, and other materials related to such X * distribution and use acknowledge that the software was developed X * by the University of California, Berkeley. The name of the X * University may not be used to endorse or promote products derived X * from this software without specific prior written permission. X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED X * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. X * X * static char sccsid[] = "@(#)logwtmp.c 5.2 (Berkeley) 9/20/88"; X */ X X#ifndef lint Xstatic char sccsid[] = "@(#)logwtmp.c 5.2 (Berkeley) 9/22/88"; X#endif /* not lint */ X X#include <sys/types.h> X#include <sys/file.h> X#include <sys/time.h> X#include <sys/stat.h> X#include <utmp.h> X X#define WTMPFILE "/usr/adm/wtmp" X Xstatic int fd; X Xlogwtmp(line, name, host) X char *line, *name, *host; X{ X struct utmp ut; X struct stat buf; X time_t time(); X char *strncpy(); X X if (!fd && (fd = open(WTMPFILE, O_WRONLY|O_APPEND, 0)) < 0) X return; X if (!fstat(fd, &buf)) { X (void)strncpy(ut.ut_line, line, sizeof(ut.ut_line)); X (void)strncpy(ut.ut_name, name, sizeof(ut.ut_name)); X (void)strncpy(ut.ut_host, host, sizeof(ut.ut_host)); X (void)time(&ut.ut_time); X if (write(fd, (char *)&ut, sizeof(struct utmp)) != X sizeof(struct utmp)) X (void)ftruncate(fd, buf.st_size); X } X} END-of-logwtmp.c echo x - newvers.sh sed 's/^X//' >newvers.sh << 'END-of-newvers.sh' X#!/bin/sh - X# X# Copyright (c) 1983 The Regents of the University of California. X# All rights reserved. X# X# Redistribution and use in source and binary forms are permitted X# provided that the above copyright notice and this paragraph are X# duplicated in all such forms and that any documentation, X# advertising materials, and other materials related to such X# distribution and use acknowledge that the software was developed X# by the University of California, Berkeley. The name of the X# University may not be used to endorse or promote products derived X# from this software without specific prior written permission. X# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR X# IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED X# WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. X# X# @(#)newvers.sh 5.3 (Berkeley) 10/31/88 X# Xif [ ! -r version ]; then echo 0 > version; fi Xtouch version Xawk ' { version = $1 + 1; }\ XEND { printf "char version[] = \"Version 4.%d ", version > "vers.c";\ X printf "%d\n", version > "version"; }' < version Xecho `date`'";' >> vers.c END-of-newvers.sh echo x - popen.c sed 's/^X//' >popen.c << 'END-of-popen.c' X/* X * Copyright (c) 1988 The Regents of the University of California. X * All rights reserved. X * X * This code is derived from software written by Ken Arnold and X * published in UNIX Review, Vol. 6, No. 8. X * X * Redistribution and use in source and binary forms are permitted X * provided that the above copyright notice and this paragraph are X * duplicated in all such forms and that any documentation, X * advertising materials, and other materials related to such X * distribution and use acknowledge that the software was developed X * by the University of California, Berkeley. The name of the X * University may not be used to endorse or promote products derived X * from this software without specific prior written permission. X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED X * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. X * X * static char sccsid[] = "@(#)popen.c 5.7 (Berkeley) 9/1/88"; X */ X X#ifndef lint Xstatic char sccsid[] = "@(#)popen.c 5.2 (Berkeley) 9/22/88"; X#endif /* not lint */ X X#include <sys/types.h> X#include <sys/signal.h> X#include <stdio.h> X X/* X * Special version of popen which avoids call to shell. This insures noone X * may create a pipe to a hidden program as a side effect of a list or dir X * command. X */ Xstatic uid_t *pids; Xstatic int fds; X XFILE * Xpopen(program, type) X char *program, *type; X{ X register char *cp; X FILE *iop; X int argc, gargc, pdes[2], pid; X char **pop, *argv[100], *gargv[1000], *vv[2]; X extern char **glob(), **copyblk(), *strtok(); X X if (*type != 'r' && *type != 'w' || type[1]) X return(NULL); X X if (!pids) { X if ((fds = getdtablesize()) <= 0) X return(NULL); X if (!(pids = X (uid_t *)malloc((u_int)(fds * sizeof(uid_t))))) X return(NULL); X bzero(pids, fds * sizeof(uid_t)); X } X if (pipe(pdes) < 0) X return(NULL); X X /* break up string into pieces */ X for (argc = 0, cp = program;; cp = NULL) X if (!(argv[argc++] = strtok(cp, " \t\n"))) X break; X X /* glob each piece */ X gargv[0] = argv[0]; X for (gargc = argc = 1; argv[argc]; argc++) { X if (!(pop = glob(argv[argc]))) { /* globbing failed */ X vv[0] = argv[argc]; X vv[1] = NULL; X pop = copyblk(vv); X } X argv[argc] = (char *)pop; /* save to free later */ X while (*pop && gargc < 1000) X gargv[gargc++] = *pop++; X } X gargv[gargc] = NULL; X X iop = NULL; X switch(pid = vfork()) { X case -1: /* error */ X (void)close(pdes[0]); X (void)close(pdes[1]); X goto free; X /* NOTREACHED */ X case 0: /* child */ X if (*type == 'r') { X if (pdes[1] != 1) { X dup2(pdes[1], 1); X (void)close(pdes[1]); X } X (void)close(pdes[0]); X } else { X if (pdes[0] != 0) { X dup2(pdes[0], 0); X (void)close(pdes[0]); X } X (void)close(pdes[1]); X } X execv(gargv[0], gargv); X _exit(1); X } X /* parent; assume fdopen can't fail... */ X if (*type == 'r') { X iop = fdopen(pdes[0], type); X (void)close(pdes[1]); X } else { X iop = fdopen(pdes[1], type); X (void)close(pdes[0]); X } X pids[fileno(iop)] = pid; X Xfree: for (argc = 1; argv[argc] != NULL; argc++) X blkfree((char **)argv[argc]); X return(iop); X} X Xpclose(iop) X FILE *iop; X{ X register int fdes; X long omask; X int pid, stat_loc; X u_int waitpid(); X X /* X * pclose returns -1 if stream is not associated with a X * `popened' command, or, if already `pclosed'. X */ X if (pids[fdes = fileno(iop)] == 0) X return(-1); X (void)fclose(iop); X omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP)); X while ((pid = wait(&stat_loc)) != pids[fdes] && pid != -1); X (void)sigsetmask(omask); X pids[fdes] = 0; X return(stat_loc); X} END-of-popen.c echo x - vers.c sed 's/^X//' >vers.c << 'END-of-vers.c' Xchar version[] = "Version 4.160 Mon Oct 31 11:50:39 PST 1988"; END-of-vers.c echo x - version sed 's/^X//' >version << 'END-of-version' X160 END-of-version exit