V1.65 (security problem in ftpd.)
Keith Bostic
bostic at OKEEFFE.BERKELEY.EDU
Tue Nov 1 09:17:05 AEST 1988
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
More information about the Comp.bugs.4bsd.ucb-fixes
mailing list