Thread View: comp.bugs.4bsd.ucb-fixes
1 messages
1 total messages
Started by Keith Bostic
Mon, 31 Oct 1988 23:17
V1.65 (security problem in ftpd.)
Author: Keith Bostic
Date: Mon, 31 Oct 1988 23:17
Date: Mon, 31 Oct 1988 23:17
3389 lines
75149 bytes
75149 bytes
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
Thread Navigation
This is a paginated view of messages in the thread with full content displayed inline.
Messages are displayed in chronological order, with the original post highlighted in green.
Use pagination controls to navigate through all messages in large threads.
Back to All Threads