[alt.sources] login permissions library

deraadt@cpsc.ucalgary.ca (Theo de Raadt) (05/24/91)

This archive contains the permissions library I wrote to control who could
login to which machines on our network. It uses a YP map which specifies
the permissions.

sources to BSD versions of login, in.ftpd, and in.rshd are supplied which
allready use the permissions lib.

Share and Enjoy. Please clear all changes you make through me, as I don't
want 10 incompatible versions on the net.
 <tdr.

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  examples in.ftpd in.rshd login perms permtest Makefile
#   README examples/cpsc.ucalgary.ca in.ftpd/Makefile in.ftpd/ftpcmd.y
#   in.ftpd/ftpd.c in.ftpd/glob.c in.ftpd/logwtmp.c in.ftpd/popen.c
#   in.ftpd/vers.c in.rshd/Makefile in.rshd/in.rshd.c login/Makefile
#   login/login.c perms/Makefile perms/glob_match.c perms/perms.y
#   permtest/Makefile permtest/permtest.c
# Wrapped by deraadt@cpsc.ucalgary.ca on Thu May 23 22:54:53 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test ! -d 'examples' ; then
    echo shar: Creating directory \"'examples'\"
    mkdir 'examples'
fi
if test ! -d 'in.ftpd' ; then
    echo shar: Creating directory \"'in.ftpd'\"
    mkdir 'in.ftpd'
fi
if test ! -d 'in.rshd' ; then
    echo shar: Creating directory \"'in.rshd'\"
    mkdir 'in.rshd'
fi
if test ! -d 'login' ; then
    echo shar: Creating directory \"'login'\"
    mkdir 'login'
fi
if test ! -d 'perms' ; then
    echo shar: Creating directory \"'perms'\"
    mkdir 'perms'
fi
if test ! -d 'permtest' ; then
    echo shar: Creating directory \"'permtest'\"
    mkdir 'permtest'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(247 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
Xall:
X	cd perms; $(MAKE)
X	cd permtest; $(MAKE)
X	cd login; $(MAKE)
X	cd in.rshd; $(MAKE)
X	cd in.ftpd; $(MAKE)
X
Xclean:
X	cd perms; $(MAKE) clean
X	cd permtest; $(MAKE) clean
X	cd login; $(MAKE) clean
X	cd in.rshd; $(MAKE) clean
X	cd in.ftpd; $(MAKE) clean
END_OF_FILE
if test 247 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(2484 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XDESCRIPTION:
XIn a basic BSD environemt only three utilities let people onto a machine:
X	login, rshd, and ftpd.
XThese three programs are modified to check a YP map called 'permissions'
Xwhich determines whether a person is allowed to login.
XControl over login is given based on four parameters: hostname, ttyname,
Xlogin, and groups.
X
XThe permissions library routines have been tested on suns, iris, and mips
Xboxes. The problem is not so much that permissions might be nonportable,
Xbut rather that source for login is unavailable on the other machines I
Xhave access to. Writing a workalike login for a system V box is nontrivial.
Xpermtest, rshd and ftpd have been tested to work though, with minor hacks.
XOn a BSD-like box, permissions should be trivial to install.
X
XPerhaps someone who has written a free system V login could send it to
Xme. I'd love to support more architectures/operating systems.
X
XThe permissions library can be used for other purposes too. We also use it
Xfor printer access. Someone suggested doing device access like dialin/dailout
Xand tape drives through it.
X
XINSTALLATION:
X1. Building permissions:
X	# make
X2. Build a permissions map for your network and install it into YP.
X   See your systems manuals for the correct way to install a YP map
X   in your system. Here's what we use in /var/yp/Makefile,
X
Xpermissions.time: $(DIR)/permissions
X	@(sed -e "/^#/d" -e s/#.*$$// $(DIR)/permissions $(CHKPIPE)) | \
X	$(MAKEDBM) - $(YPDBDIR)/$(DOM)/permissions; 
X	@touch permissions.time; 
X	@echo "updated permissions"; 
X	@if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOM) permissions; fi
X	@if [ ! $(NOPUSH) ]; then echo "pushed permissions"; fi
X
X   To install the map, on sunos4.1, I would use the following:
X	# touch /etc/permissions
X	# ypmake NOPUSH=1 permissions
X	# foreach i ( `ypcat ypservers` )
X	> rsh $i /usr/etc/yp/ypxfr -h `hostname` permissions
X	> end
X	#
X
X5. Test the permissions database with permtest. For example,
X	# permtest -v deraadt ttyh0 fsa
X	8 groups: staff wheel daemon kmem bin oldstaff telnet cdrom
X	user deraadt permitted on fsa:ttyh0
X
X4. Now install the three remaining parts.
X	login/login -> /bin/login
X	in.ftpd/in.ftpd -> /usr/etc/in.ftpd
X	in.rshd/in.rshd -> /usr/etc/in.rshd
X   Be sure to save copies of your old utilities.
X   Remember, on most systems, login is setuid root.
X
XOne request. Please clear all changes to this through me. I would be very
Xunhappy to see five different incompatible versions of this in use.
X
XEnjoy.
Xderaadt@cpsc.ucalgary.ca
END_OF_FILE
if test 2484 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'examples/cpsc.ucalgary.ca' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'examples/cpsc.ucalgary.ca'\"
else
echo shar: Extracting \"'examples/cpsc.ucalgary.ca'\" \(4493 characters\)
sed "s/^X//" >'examples/cpsc.ucalgary.ca' <<'END_OF_FILE'
X# ----------------------------------------------------------------------
X# NIS map = permissions
X# groked by: login, in.rshd, permtest, in.ftpd
X# rsh and ftp generate tty field values of 'rsh' and 'ftp'
X#
X# SYNTAX:
X# entry		: hostname '\t' permlist
X# permlist	: permission `|` permlist
X#		| permission
X#		| null
X# permission	: ttylist ':' authlist
X#		| '$' entry		-> #include an entry
X# ttylist	: tty ',' ttylist
X#		| tty			-> tty regexp
X#		| null
X# authlist	: auth ',' authlist
X#		| auth
X# auth		: + spec		-> add
X#		: - spec		-> delete
X# 		| null
X# spec		: user			-> username to change
X#		| '.' group		-> group regexp to change
X#
X# NOTES:
X# Watch how we deny anonomous ftp on every machine with the $admin
X# macro, then allow it on fsa again. fsa is our admin only machine, and
X# does not actually run the ftpd included, but rather a logging ftpd.
X# 
X# Two example users will explain the groups
X# aycock is in groups: c510/L01, c461/L01, and c401/L01. (His account is
X#	actually in /home/c510/L01/aycock)
X# deraadt is in groups: staff wheel daemonkmem bin telnet cdrom
X# 
X# The macros at the top are very important. They groups machines into sets
X# making their management easier.
X# 
X# In some places you will see references to tty's. These are our fast modem
X# racks. We restrict certain groups to using them. Others can go through the
X# slower campus terminal servers.
X# ----------------------------------------------------------------------
X
X# macros
Xadmin		*:+.utils,+.staff,+.wheel,+operator | ftp:-ftp
Xgrad_ws		$admin | *:+.grads,+.profs,+.research,+.summer,+.gl,+.vlsi,+.srdg,+.c502/L01,+.c599/L01,+.c651/L01
Xprof_ws		$admin | *:+.profs,+.research,+.srdg,+.offstaff
Xvlsi_ws		$admin | *:+.vlsi,+graham,+olthof,+jevans,+milligan
Xoff_ws		$admin | *:+.offstaff,+.profs
Xug_ws		$admin | $grad_ws | *:+.c[456]*
X
X# admin machines
Xfsa		$admin | ttyb,ttyh?:-.*,+.staff,+.uucp | *:+frangos,+elsie | ftp:+ftp
Xaa		$admin
Xrat		$admin | *:+aycock
Xsev		$admin
Xatlas		$admin | *:+test,-jamesm
Xdudes		$admin
Xglags		$admin
X
X# profs machines
Xfsc		$admin | *:+.grads,+.profs,+.research,+.srdg,+.vlsi,+.offstaff,+.c491/L01,+.banff,+.visitors | ttyj[0-6]:-.*,+.staff,+.profs,+.offstaff,+gl
Xinterval	$prof_ws
Xca		$prof_ws
Xcb		$prof_ws
Xcc		$prof_ws | *:+.gl
Xcd		$prof_ws | *:+.grads,+.research,+.srdg,+.vlsi,+joan
Xce		$prof_ws
Xcf		$prof_ws
Xcg		$prof_ws
Xch		$prof_ws
Xic		$prof_ws
Xgolf		$prof_ws
Xalbert		$prof_ws
X
X# grads machines
Xfsd		$admin | *:+.grads,+.profs,+.research,+.summer,+.vlsi,+gl,+.srdg,+.c502/L01,+.c599/L01,+.banff,+.arc,+conway | ttyh[0-6]:-.*,+.staff,+.profs,+.grads,+.vlsi,+.offstaff,+.research
Xab		$grad_ws | *:+publisher
Xda		$grad_ws
Xdb		$grad_ws
Xdc		$grad_ws | *:+joan
Xdd		$grad_ws
Xde		$grad_ws
Xdf		$grad_ws
Xdg		$grad_ws
Xdh		$grad_ws
Xdi		$grad_ws
Xdj		$grad_ws
Xdk		$grad_ws
Xij		$grad_ws
X
X# vlsi machines
Xfsg		$vlsi_ws
Xga		$vlsi_ws
Xgb		$vlsi_ws
Xgc		$vlsi_ws
Xgd		$vlsi_ws
Xge		$vlsi_ws
Xgf		$vlsi_ws
Xgg		$vlsi_ws
Xgh		$vlsi_ws
X
X# office staff machines
Xia		$off_ws
Xih		$off_ws
X
X# undergraduate workstations
Xea		$ug_ws
Xeb		$ug_ws
Xec		$ug_ws
Xed		$ug_ws
Xee		$ug_ws
Xef		$ug_ws
Xeg		$ug_ws
Xeh		$ug_ws
Xei		$ug_ws
Xej		$ug_ws
Xha		$ug_ws
Xhb		$ug_ws
Xhc		$ug_ws
Xhd		$ug_ws
Xhe		$ug_ws
Xhf		$ug_ws
Xhg		$ug_ws
Xhh		$ug_ws
Xhi		$ug_ws
Xib		$ug_ws
Xid		$ug_ws
Xie		$ug_ws
Xif		$ug_ws
X
X# graphicsland fileserver
Xgfx		$admin | *:+.gl,+thorne
X
X# myths. these machines do not run the right login yet.
Xbfly		$grad_ws
Xirisa		$admin | *:+.gl,+.c55[13]*
Xirisb		$admin | *:+.gl,+.c55[13]*
Xirisc		$admin | *:+.gl
Xirisd		$admin | *:+.gl
Xirise		$admin | *:+.gl
Xirisf		$admin | *:+.gl,+.c55[13]*
X
X# remaining machines. Anyone may use an undergrad machine.
X# be careful - the $admin is at the end to turn off anon ftp
X*		*:+.*,-.uucp | $admin
X
X# PRINTERS
X# Our printer permissions are done through permissions as well. Sorry,
X# this distribution of permissions does not include our lpr hacks.
X# I left this in here simply as an example.
X
Xlp1		*:+.*,-.nlp
Xlp2		*:+.*,-.nlp
X
Xcs1		$admin | *:+.grads,+.research,+.profs,+.offstaff,+.srdg,+.submit | *:+.cs1,-.ncs1
Xcs2		$admin | *:+.grads,+.research,+.profs,+.offstaff,+.srdg,+.vlsi | *:+.cs2,-.ncs2
X
Xalw1		$admin | *:+.profs,+.offstaff,+.grads | *:+.alw1,-.nalw1
Xalw2		$admin | *:+.profs,+.offstaff,+.vlsi | *:+.alw2,-.nalw2
Xalw3		$admin | *:+.grads,+.research,+.profs,+.srdg,+.vlsi | *:+.alw3,-.nalw3
Xalw4		$admin | *:+.grads,+.research,+.profs,+.offstaff,+.srdg,+.vlsi | *:+.alw4,-.nalw4
X
Xbp		$admin | *:+.bp,-.nbp
Xip		$admin | *:+.grads,+.research,+.profs,+.srdg,+.c481* | *:+.ip,-.nip
END_OF_FILE
if test 4493 -ne `wc -c <'examples/cpsc.ucalgary.ca'`; then
    echo shar: \"'examples/cpsc.ucalgary.ca'\" unpacked with wrong size!
fi
# end of 'examples/cpsc.ucalgary.ca'
fi
if test -f 'in.ftpd/Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'in.ftpd/Makefile'\"
else
echo shar: Extracting \"'in.ftpd/Makefile'\" \(203 characters\)
sed "s/^X//" >'in.ftpd/Makefile' <<'END_OF_FILE'
XOBJ = ftpd.o ftpcmd.o glob.o popen.o logwtmp.o vers.o\
X	../perms/perms.o ../perms/glob_match.o
XCFLAGS = -g -DPERMS
X
Xin.ftpd: $(OBJ)
X	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJ)
X
Xclean:
X	$(RM) *.o *~ in.ftpd
END_OF_FILE
if test 203 -ne `wc -c <'in.ftpd/Makefile'`; then
    echo shar: \"'in.ftpd/Makefile'\" unpacked with wrong size!
fi
# end of 'in.ftpd/Makefile'
fi
if test -f 'in.ftpd/ftpcmd.y' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'in.ftpd/ftpcmd.y'\"
else
echo shar: Extracting \"'in.ftpd/ftpcmd.y'\" \(16052 characters\)
sed "s/^X//" >'in.ftpd/ftpcmd.y' <<'END_OF_FILE'
X/*
X * Copyright (c) 1985, 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 */
X
X/*
X * Grammar for FTP commands.
X * See RFC 765.
X */
X
X%{
X
X#ifndef lint
Xstatic	char sccsid[] = "@(#)ftpcmd.y 1.1 90/03/23 SMI"; /* from UCB 5.16 12/8/88 */
X#endif
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 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			user((char *) $3);
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				pdata = -1;
X			}
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", 0);
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", 0);
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				store((char *) $4, "w", 1);
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:	/* empty */
X		= {
X			$$ = (int) "";
X		}
X	|	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 (logged_in && $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 SP then STRING */
X#define	ZSTR1	5	/* SP then optional STRING */
X#define	ZSTR2	6	/* optional STRING after SP */
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, ZSTR1, 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, "command: %s", s);
X			tmpline[0] = '\0';
X			return(s);
X		}
X		if (c == 0)
X			tmpline[0] = '\0';
X	}
X	while ((c = getc(iop)) != EOF) {
X		c &= 0377;
X		if (c == IAC) {
X		    if ((c = getc(iop)) != EOF) {
X			c &= 0377;
X			switch (c) {
X			case WILL:
X			case WONT:
X				c = getc(iop);
X				printf("%c%c%c", IAC, DONT, 0377&c);
X				(void) fflush(stdout);
X				continue;
X			case DO:
X			case DONT:
X				c = getc(iop);
X				printf("%c%c%c", IAC, WONT, 0377&c);
X				(void) fflush(stdout);
X				continue;
X			case IAC:
X				break;
X			default:
X				continue;	/* ignore command */
X			}
X		    }
X		}
X		*cs++ = c;
X		if (--n <= 0 || c == '\n')
X			break;
X	}
X	if (c == EOF && cs == s)
X		return (NULL);
X	*cs++ = '\0';
X	if (debug)
X		syslog(LOG_DEBUG, "command: %s", s);
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			"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, *strpbrk();
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 ((cp = index(cbuf, '\r'))) {
X				*cp++ = '\n';
X				*cp = '\0';
X			}
X			if ((cp = strpbrk(cbuf, " \n")))
X				cpos = cp - cbuf;
X			if (cpos == 0)
X				cpos = 4;
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			/* FALLTHROUGH */
X
X		case STR1:
X		case ZSTR1:
X			if (cbuf[cpos] == ' ') {
X				cpos++;
X				state = state == OSTR ? STR2 : ++state;
X				return (SP);
X			}
X			break;
X
X		case ZSTR2:
X			if (cbuf[cpos] == '\n') {
X				state = CMD;
X				return (CRLF);
X			}
X			/* FALL THRU */
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	register 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 bugs@Sun.COM.");
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_FILE
if test 16052 -ne `wc -c <'in.ftpd/ftpcmd.y'`; then
    echo shar: \"'in.ftpd/ftpcmd.y'\" unpacked with wrong size!
fi
# end of 'in.ftpd/ftpcmd.y'
fi
if test -f 'in.ftpd/ftpd.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'in.ftpd/ftpd.c'\"
else
echo shar: Extracting \"'in.ftpd/ftpd.c'\" \(24225 characters\)
sed "s/^X//" >'in.ftpd/ftpd.c' <<'END_OF_FILE'
X/*
X * Copyright (c) 1985, 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 */
X
X#ifndef lint
Xchar copyright[] =
X"@(#) Copyright (c) 1985, 1988 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.2 (Berkeley) 12/7/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#ifdef PERMS
X#include <string.h>
X#include <grp.h>
X#endif
X#include <setjmp.h>
X#include <netdb.h>
X#include <errno.h>
X#include <strings.h>
X#include <syslog.h>
X#include <varargs.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	int sys_nerr;
Xextern	char *crypt();
Xextern	char version[];
Xextern	char *home;		/* pointer to home directory for glob */
Xextern	FILE *ftpd_popen(), *fopen(), *freopen();
Xextern	int  ftpd_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	type;
Xint	form;
Xint	stru;			/* avoid C keyword */
Xint	mode;
Xint	usedefault = 1;		/* for data transfers */
Xint	pdata = -1;		/* for passive mode */
Xint	transflag;
Xint	socksize = 24 * 1024;	/* larger socket window size for data */
Xchar	tmpline[7];
Xchar	hostname[MAXHOSTNAMELEN];
Xchar	remotehost[MAXHOSTNAMELEN];
Xchar	buf[BUFSIZ*8];		/* larger buffer to speed up binary xfers */
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	char *cp;
X#ifdef PERMS
X	char domainname[MAXHOSTNAMELEN];
X#endif
X	openlog("ftpd", LOG_PID, LOG_DAEMON);
X	addrlen = sizeof (his_addr);
X	if (getpeername(0, (struct sockaddr *)&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, (struct sockaddr *)&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	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 (signal(SIGURG, myoob) == BADSIG)
X		syslog(LOG_ERR, "signal: %m");
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#endif
X	if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
X		syslog(LOG_ERR, "fcntl F_SETOWN: %m");
X	dolog(&his_addr);
X	/* do telnet option negotiation here */
X	/*
X	 * Set up default state
X	 */
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#ifdef PERMS
X	if( Getdomainname(domainname, sizeof(domainname)) ) {
X		reply(220, "%s.%s FTP server (%s) ready.",
X			hostname, domainname, version);
X	} else {
X		reply(220, "%s FTP server (%s) ready.",
X			hostname, version);
X	}
X#else
X	reply(220, "%s FTP server (%s) ready.", hostname, version);
X#endif
X	(void) setjmp(errcatch);
X	for (;;)
X		(void) yyparse();
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	char *malloc();
X	char *new = malloc((unsigned) strlen(s) + 1);
X	
X	if (new == NULL) {
X		reply(553, "Local resource failure: malloc");
X		dologout(1);
X	}
X	(void) strcpy(new, s);
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
Xint login_attempts;		/* number of failed login attempts */
Xint askpasswd;			/* had user command, ask for passwd */
X
X/*
X * USER command.
X * Sets global passwd pointer pw if named account exists
X * and is acceptable; sets askpasswd if a PASS command is
X * expected. If logged in previously, need to reset state.
X * If name is "ftp" or "anonymous" and ftp account exists,
X * set guest and pw, then just return.
X * If account doesn't exist, ask for passwd anyway.
X * Otherwise, check user requesting login privileges.
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 root and uucp to be avoided.
X */
Xuser(name)
X	char *name;
X{
X	register char *cp;
X	FILE *fd;
X	char *shell;
X	char line[BUFSIZ], *index(), *getusershell();
X
X	if (logged_in) {
X		if (guest) {
X			reply(530, "Can't change user from guest login.");
X			return;
X		}
X		end_login();
X	}
X
X	guest = 0;
X	if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
X		if ((pw = sgetpwnam("ftp")) != NULL) {
X#ifdef PERMS
X			if( checkuser(pw) ) {
X				reply(530, "Anon FTP not permitted here.");
X				pw = (struct passwd *) NULL;
X				return;
X			}
X#endif
X			guest = 1;
X			askpasswd = 1;
X			reply(331, "Guest login ok, send ident as password.");
X		} else
X			reply(530, "User %s unknown.", name);
X		return;
X	}
X	if (pw = sgetpwnam(name)) {
X#ifdef PERMS
X		if( checkuser(pw)) {
X			reply(530, "User %s access denied.", name);
X			pw = (struct passwd *) NULL;
X			return;
X		}
X#endif
X		if ((shell = pw->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			reply(530, "User %s access denied.", name);
X			pw = (struct passwd *) NULL;
X			return;
X		}
X		if ((fd = fopen(FTPUSERS, "r")) != NULL) {
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				reply(530, "User %s access denied.", name);
X				pw = (struct passwd *) NULL;
X				return;
X			}
X		    }
X		}
X		(void) fclose(fd);
X	}
X	reply(331, "Password required for %s.", name);
X	askpasswd = 1;
X	/*
X	 * Delay before reading passwd after first failed
X	 * attempt to slow down passwd-guessing programs.
X	 */
X	if (login_attempts)
X		sleep((unsigned) login_attempts);
X}
X
X/*
X * Terminate login as previous user, if any, resetting state;
X * used when USER command is given or login fails.
X */
Xend_login()
X{
X
X	(void) seteuid((uid_t)0);
X	if (logged_in)
X		logwtmp(ttyline, "", "");
X	pw = NULL;
X	logged_in = 0;
X	guest = 0;
X}
X
Xpass(passwd)
X	char *passwd;
X{
X	char *xpasswd, *salt;
X
X	if (logged_in || askpasswd == 0) {
X		reply(503, "Login with USER first.");
X		return;
X	}
X	askpasswd = 0;
X	if (!guest) {		/* "ftp" is only account allowed no password */
X		if (pw == NULL)
X			salt = "xx";
X		else
X			salt = pw->pw_passwd;
X		xpasswd = crypt(passwd, salt);
X		/* The strcmp does not catch null passwords! */
X		if (pw == NULL || *pw->pw_passwd == '\0' ||
X		    strcmp(xpasswd, pw->pw_passwd)) {
X			reply(530, "Login incorrect.");
X			pw = NULL;
X			if (login_attempts++ >= 5) {
X				syslog(LOG_ERR,
X				    "repeated login failures from %s",
X				    remotehost);
X				exit(0);
X			}
X			return;
X		}
X	}
X	login_attempts = 0;		/* this time successful */
X	(void) setegid((gid_t)pw->pw_gid);
X	(void) initgroups(pw->pw_name, pw->pw_gid);
X	if (chdir(pw->pw_dir)) {
X		if (chdir("/")) {
X			reply(550, "User %s: can't change directory to %s.",
X				pw->pw_name, pw->pw_dir);
X			goto bad;
X		}
X	}
X
X	/* open wtmp before chroot */
X	(void)sprintf(ttyline, "ftp%d", getpid());
X	logwtmp(ttyline, pw->pw_name, remotehost);
X	logged_in = 1;
X
X	if (guest && chroot(pw->pw_dir) < 0) {
X		reply(550, "Can't set guest privileges.");
X		goto bad;
X	}
X	if (seteuid((uid_t)pw->pw_uid) < 0) {
X		reply(550, "Can't set uid.");
X		goto bad;
X	}
X	if (guest)
X		reply(230, "Guest login ok, access restrictions apply.");
X	else
X		reply(230, "User %s logged in.", pw->pw_name);
X	home = pw->pw_dir;		/* home dir for globbing */
X	return;
Xbad:
X	/* Forget all about it... */
X	end_login();
X}
X
X/*
X * return a printable type string
X */
Xchar *print_type(t)
X{
X	switch (t)
X	{
X	  case TYPE_A:	return("ASCII ");
X	  case TYPE_L:
X	  case TYPE_I:	return("Binary ");
X	}
X	return("");
X}
X
Xretrieve(cmd, name)
X	char *cmd, *name;
X{
X	FILE *fin, *dout;
X	struct stat st;
X	int (*closefunc)(), tmp;
X	void (*oldpipe)();	/* Hold value of SIGPIPE during close */
X
X	if (cmd == 0) {
X		fin = fopen(name, "r"), closefunc = fclose;
X	} else {
X		char line[BUFSIZ];
X
X		(void) sprintf(line, cmd, name), name = line;
X		fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
X	}
X	if (fin == NULL) {
X		if (errno != 0)
X			perror_reply(550, name);
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		perror_reply(550, name);
X	}
X	else if (tmp == 0) {
X		reply(226, "%sTransfer complete.", print_type(type));
X	}
X	/*
X	 * If the transfer failed because the data connection got aborted,
X	 * then the fclose may cause a SIGPIPE trying to flush the buffers
X	 * and abort the whole session.  Ignore SIGPIPEs during the fclose.
X	 */
X	oldpipe = signal(SIGPIPE, SIG_IGN);
X	(void) fclose(dout);
X	data = -1;
X	pdata = -1;
X	signal(SIGPIPE, oldpipe);
Xdone:
X	(*closefunc)(fin);
X}
X
Xstore(name, mode, unique)
X	char *name, *mode;
X	int unique;
X{
X	FILE *fout, *din;
X	int (*closefunc)(), tmp;
X	char *gunique();
X
X	{
X		struct stat st;
X
X		if (unique && stat(name, &st) == 0 &&
X		    (name = gunique(name)) == NULL)
X			return;
X		fout = fopen(name, mode), closefunc = fclose;
X	}
X	if (fout == NULL) {
X		perror_reply(553, name);
X		return;
X	}
X	din = dataconn(name, (off_t)-1, "r");
X	if (din == NULL)
X		goto done;
X	if ((tmp = receive_data(din, fout)) > 0 || ferror(fout) > 0)
X		perror_reply(552, name);
X	else if (tmp == 0) {
X		if (ferror(fout) > 0)
X			perror_reply(552, name);
X		else if (unique)
X			reply(226, "Transfer complete (unique file name:%s).",
X			    name);
X		else
X			reply(226, "%sTransfer complete.", print_type(type));
X	}
X	(void) fclose(din);
X	data = -1;
X	pdata = -1;
Xdone:
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	(void) seteuid((uid_t)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, (struct sockaddr *)&data_source, sizeof (data_source)) < 0)
X		goto bad;
X	(void) seteuid((uid_t)pw->pw_uid);
X	return (fdopen(s, mode));
Xbad:
X	(void) seteuid((uid_t)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 != (off_t) -1)
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, (struct sockaddr *)&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, "%sdata connection for %s (%s,%d)%s.",
X		     print_type(type),
X		     name, inet_ntoa(from.sin_addr),
X		     ntohs(from.sin_port), sizebuf);
X		return(fdopen(pdata, mode));
X	}
X	if (data >= 0) {
X		reply(125, "Using existing %sdata connection for %s%s.",
X		    print_type(type),
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		    errno < sys_nerr ? sys_errlist[errno] : "unknown error");
X		return (NULL);
X	}
X	data = fileno(file);
X	(void) setsockopt(data, SOL_SOCKET, SO_SNDBUF, (char *)&socksize, 
X				sizeof (socksize));
X	(void) setsockopt(data, SOL_SOCKET, SO_RCVBUF, (char *)&socksize, 
X				sizeof (socksize));
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		perror_reply(425, "Can't build data connection");
X		(void) fclose(file);
X		data = -1;
X		return (NULL);
X	}
X	reply(150, "%sdata connection for %s (%s,%d)%s.",
X	    print_type(type),
X	    name, inet_ntoa(data_dest.sin_addr),
X	    ntohs(data_dest.sin_port), sizebuf);
X	return (file);
X}
X
X/*
X * Envelope for 'send_data_body'.  Allow data connections to fail without
X * terminating the daemon, but SIGPIPE is set to be ignored so that if
X * one occurs on the data channel we'll just catch the error return on
X * the write rather than causing the whole session to abort.
X */
X
Xsend_data(instr, outstr)
X	FILE *instr;		/* Data being sent */
X	FILE *outstr;		/* Connection being transmitted upon */
X{
X	int value;		/* Return value from send_data_body */
X	void (*oldpipe)();	/* Old handler for SIGPIPE */
X
X	oldpipe = signal(SIGPIPE, SIG_IGN);
X	value = send_data_body(instr, outstr);
X	signal(SIGPIPE, oldpipe);
X	return (value);	
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_body(instr, outstr)
X	FILE *instr, *outstr;
X{
X	register int c;
X	int netfd, filefd, cnt;
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		}
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
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			}
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
X/*VARARGS2*/
Xreply(n, s, va_alist)
X	int n;
X	char *s;
X	va_dcl
X{
X	va_list ap;
X
X	va_start(ap);
X	printf("%d ", n);
X	_doprnt(s, ap, stdout);
X	printf("\r\n");
X	(void) fflush(stdout);
X	if (debug) {
X		syslog(LOG_DEBUG, "<--- %d ", n);
X		vsyslog(LOG_DEBUG, s, ap);
X	}
X	va_end(ap);
X}
X
X/*VARARGS2*/
Xlreply(n, s, va_alist)
X	int n;
X	char *s;
X	va_dcl
X{
X	va_list ap;
X
X	va_start(ap);
X	printf("%d-", n);
X	_doprnt(s, ap, stdout);
X	printf("\r\n");
X	(void) fflush(stdout);
X	if (debug) {
X		syslog(LOG_DEBUG, "<--- %d- ", n);
X		vsyslog(LOG_DEBUG, s, ap);
X	}
X	va_end(ap);
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
X/* ARGSUSED */
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		perror_reply(550, name);
X		return;
X	}
X	if ((st.st_mode&S_IFMT) == S_IFDIR) {
X		if (rmdir(name) < 0) {
X			perror_reply(550, name);
X			return;
X		}
X		goto done;
X	}
X	if (unlink(name) < 0) {
X		perror_reply(550, name);
X		return;
X	}
Xdone:
X	ack("DELE");
X}
X
Xcwd(path)
X	char *path;
X{
X
X	if (chdir(path) < 0) {
X		perror_reply(550, path);
X		return;
X	}
X	ack("CWD");
X}
X
Xmakedir(name)
X	char *name;
X{
X	if (mkdir(name, 0777) < 0)
X		perror_reply(550, name);
X	else
X		reply(257, "MKD command successful.");
X}
X
Xremovedir(name)
X	char *name;
X{
X
X	if (rmdir(name) < 0) {
X		perror_reply(550, name);
X		return;
X	}
X	ack("RMD");
X}
X
Xpwd()
X{
X	char path[MAXPATHLEN + 1];
X	extern char *getwd();
X
X	if (getwd(path) == (char *)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		perror_reply(550, name);
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		perror_reply(550, "rename");
X		return;
X	}
X	ack("RNTO");
X}
X
Xdolog(sin)
X	struct sockaddr_in *sin;
X{
X	struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr,
X		sizeof (struct in_addr), AF_INET);
X	time_t t, time();
X	extern char *ctime();
X
X	if (hp)
X		(void) strncpy(remotehost, hp->h_name, sizeof (remotehost));
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, "connection from %s at %s",
X	    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((uid_t)0);
X		logwtmp(ttyline, "", "");
X	}
X	/* beware of flushing buffers after a SIGPIPE */
X	_exit(status);
X}
X
Xmyoob()
X{
X	char *cp;
X
X	/* only process if transfer occurring */
X	if (!transflag)
X		return;
X	cp = tmpline;
X	if (getline(cp, 7, stdin) == NULL) {
X		reply(221, "You could at least say goodbye.");
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	(void) seteuid((uid_t)0);
X	if (bind(pdata, (struct sockaddr *) &tmp, sizeof(tmp)) < 0) {
X		(void) seteuid((uid_t)pw->pw_uid);
X		(void) close(pdata);
X		pdata = -1;
X		reply(530, "Can't open passive connection");
X		return;
X	}
X	(void) seteuid((uid_t)pw->pw_uid);
X	len = sizeof(tmp);
X	if (getsockname(pdata, (struct sockaddr *) &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
X/*
X * Generate unique name for file with basename "local".
X * The file named "local" is already known to exist.
X * Generates failure reply on error.
X */
Xchar *
Xgunique(local)
X	char *local;
X{
X	static char new[MAXPATHLEN];
X	struct stat st;
X	char *cp = rindex(local, '/');
X	int d, count=0;
X
X	if (cp)
X		*cp = '\0';
X	d = stat(cp ? local : ".", &st);
X	if (cp)
X		*cp = '/';
X	if (d < 0) {
X		perror_reply(553, local);
X		return((char *) 0);
X	}
X	(void) strcpy(new, local);
X	cp = new + strlen(new);
X	*cp++ = '.';
X	for (count = 1; count < 100; count++) {
X		(void) sprintf(cp, "%d", count);
X		if (stat(new, &st) < 0)
X			return(new);
X	}
X	reply(452, "Unique file name cannot be created.");
X	return((char *) 0);
X}
X
X/*
X * Format and send reply containing system error number.
X */
Xperror_reply(code, string)
X	int code;
X	char *string;
X{
X
X	if (errno < sys_nerr)
X		reply(code, "%s: %s.", string, sys_errlist[errno]);
X	else
X		reply(code, "%s: unknown error %d.", string, errno);
X}
X
X#ifdef PERMS
Xint
XGetdomainname(s, n)
Xchar *s;
X{
X	FILE *f;
X	char buf[200], *p;
X	int i = 0;
X
X	f = fopen("/etc/resolv.conf", "r");
X	if(!f)
X		return 0;
X	while( fgets(buf, sizeof buf, f) ) {
X		p = buf;
X		while(*p==' ' || *p=='\t')
X			p++;
X		if( strncmp(p, "domain", 6))
X			continue;
X		p+= 6;
X		while(*p==' ' || *p=='\t')
X			p++;
X		while(*p!=' ' && *p!='\t' && *p!='\n' && *p!='\0' ) {
X			s[i++] = *p++;
X			if(i==n)
X				return 0;
X		}
X		s[i] = '\0';
X		return 1;
X	}
X}
X
Xcheckuser(pwd)
Xstruct passwd *pwd;
X{
X	char *grpnames[NGROUPS+1];
X	int ngrps, lp;
X	struct group *grp;
X	extern int permcheck();
X
X	setgrent();
X	ngrps = 0;
X	if(!(grp=getgrgid(pwd->pw_gid))) {
X		syslog(LOG_CRIT,
X			"cannot find group name for %d\n", pwd->pw_gid);
X		goto broken_groups;
X	}
X	grpnames[ngrps++] = strdup(grp->gr_name);
X	while( grp=getgrent() ) {
X		if(pwd->pw_gid == grp->gr_gid)
X			continue;
X		while(*grp->gr_mem) {
X			if( !strcmp(pwd->pw_name, *grp->gr_mem)) {
X				grpnames[ngrps++] = strdup(grp->gr_name);
X			}
X			grp->gr_mem++;
X		}
X	}
X	endgrent();
X	grpnames[ngrps] = NULL;
X	if( !permcheck(pw->pw_name, "ftp", grpnames, NULL) ) {
X		syslog(LOG_CRIT,
X			"%s:%s not permitted", "ftp", pw->pw_name);
X		return 1;
X	}
Xbroken_groups:
X	return 0;
X}
X#endif
END_OF_FILE
if test 24225 -ne `wc -c <'in.ftpd/ftpd.c'`; then
    echo shar: \"'in.ftpd/ftpd.c'\" unpacked with wrong size!
fi
# end of 'in.ftpd/ftpd.c'
fi
if test -f 'in.ftpd/glob.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'in.ftpd/glob.c'\"
else
echo shar: Extracting \"'in.ftpd/glob.c'\" \(9587 characters\)
sed "s/^X//" >'in.ftpd/glob.c' <<'END_OF_FILE'
X/*
X * Copyright (c) 1980 Regents of the University of California.
X * All rights reserved.  The Berkeley software License Agreement
X * specifies the terms and conditions for redistribution.
X */
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)glob.c	5.2 (Berkeley) 3/7/86";
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_FILE
if test 9587 -ne `wc -c <'in.ftpd/glob.c'`; then
    echo shar: \"'in.ftpd/glob.c'\" unpacked with wrong size!
fi
# end of 'in.ftpd/glob.c'
fi
if test -f 'in.ftpd/logwtmp.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'in.ftpd/logwtmp.c'\"
else
echo shar: Extracting \"'in.ftpd/logwtmp.c'\" \(1604 characters\)
sed "s/^X//" >'in.ftpd/logwtmp.c' <<'END_OF_FILE'
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 */
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)logwtmp.c 1.1 90/03/23 SMI"; /* from UCB 5.3 12/7/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	"/var/adm/wtmp"
X
Xstatic int fd = -1;
X
X/*
X * Modified version of logwtmp that holds wtmp file open
X * after first call, for use with ftp (which may chroot
X * after login, but before logout).
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 < 0 && (fd = open(WTMPFILE, O_WRONLY|O_APPEND, 0)) < 0)
X		return;
X	if (fstat(fd, &buf) == 0) {
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_FILE
if test 1604 -ne `wc -c <'in.ftpd/logwtmp.c'`; then
    echo shar: \"'in.ftpd/logwtmp.c'\" unpacked with wrong size!
fi
# end of 'in.ftpd/logwtmp.c'
fi
if test -f 'in.ftpd/popen.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'in.ftpd/popen.c'\"
else
echo shar: Extracting \"'in.ftpd/popen.c'\" \(3504 characters\)
sed "s/^X//" >'in.ftpd/popen.c' <<'END_OF_FILE'
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 */
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)popen.c 1.1 90/03/23 SMI"; /* from UCB 5.4 12/7/88 */
X#endif /* not lint */
X
X#include <sys/types.h>
X#include <sys/signal.h>
X#include <sys/wait.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 int *pids;
Xstatic int fds;
X
XFILE *
Xftpd_popen(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(), *malloc();
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 = (int *)malloc((u_int)(fds * sizeof(int)))) == NULL)
X			return(NULL);
X		bzero((char *)pids, fds * sizeof(int));
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			/*
X			 * Need to grab stderr too for new ls
X			 */
X				dup2(pdes[1], 2);
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
Xftpd_pclose(iop)
X	FILE *iop;
X{
X	register int fdes;
X	int omask;
X	union wait stat_loc;
X	int pid;
X
X	/*
X	 * pclose returns -1 if stream is not associated with a
X	 * `popened' command, or, if already `pclosed'.
X	 */
X	if (pids == 0 || 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(pid == -1 ? -1 : stat_loc.w_status);
X}
END_OF_FILE
if test 3504 -ne `wc -c <'in.ftpd/popen.c'`; then
    echo shar: \"'in.ftpd/popen.c'\" unpacked with wrong size!
fi
# end of 'in.ftpd/popen.c'
fi
if test -f 'in.ftpd/vers.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'in.ftpd/vers.c'\"
else
echo shar: Extracting \"'in.ftpd/vers.c'\" \(64 characters\)
sed "s/^X//" >'in.ftpd/vers.c' <<'END_OF_FILE'
X/*	@(#)vers.c 1.1 90/03/23 SMI	*/
Xchar version[] = "SunOS 4.1";
END_OF_FILE
if test 64 -ne `wc -c <'in.ftpd/vers.c'`; then
    echo shar: \"'in.ftpd/vers.c'\" unpacked with wrong size!
fi
# end of 'in.ftpd/vers.c'
fi
if test -f 'in.rshd/Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'in.rshd/Makefile'\"
else
echo shar: Extracting \"'in.rshd/Makefile'\" \(160 characters\)
sed "s/^X//" >'in.rshd/Makefile' <<'END_OF_FILE'
XOBJ = in.rshd.o ../perms/perms.o ../perms/glob_match.o
XCFLAGS = -g -DPERMS
X
Xin.rshd:	$(OBJ)
X		$(CC) $(LDFLAGS) $(OBJ) -o in.rshd
X
Xclean:
X		$(RM) *~ *.o in.rshd
END_OF_FILE
if test 160 -ne `wc -c <'in.rshd/Makefile'`; then
    echo shar: \"'in.rshd/Makefile'\" unpacked with wrong size!
fi
# end of 'in.rshd/Makefile'
fi
if test -f 'in.rshd/in.rshd.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'in.rshd/in.rshd.c'\"
else
echo shar: Extracting \"'in.rshd/in.rshd.c'\" \(6522 characters\)
sed "s/^X//" >'in.rshd/in.rshd.c' <<'END_OF_FILE'
X/*
X * Copyright (c) 1983 Regents of the University of California.
X * All rights reserved.  The Berkeley software License Agreement
X * specifies the terms and conditions for redistribution.
X */
X
X#ifndef lint
Xchar copyright[] =
X"@(#) Copyright (c) 1983 Regents of the University of California.\n\
X All rights reserved.\n";
X#endif not lint
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)rshd.c	5.7 (Berkeley) 5/9/86";
X#endif not lint
X
X/*
X * remote shell server:
X *	remuser\0
X *	locuser\0
X *	command\0
X *	data
X */
X#include <sys/ioctl.h>
X#include <sys/param.h>
X#include <sys/socket.h>
X#include <sys/time.h>
X
X#include <netinet/in.h>
X
X#include <arpa/inet.h>
X
X#include <stdio.h>
X#include <errno.h>
X#include <pwd.h>
X#include <signal.h>
X#include <netdb.h>
X#include <syslog.h>
X
X
X#ifdef PERMS
X#include <grp.h>
Xchar *strdup(), *grpnames[NGROUPS+1];
Xint ngrps, lp;
Xstruct group *grp;
Xextern int permcheck();
X#endif
X
Xint	errno;
Xchar	*index(), *rindex(), *strncat();
X/*VARARGS1*/
Xint	error();
X
X/*ARGSUSED*/
Xmain(argc, argv)
X	int argc;
X	char **argv;
X{
X	struct linger linger;
X	int on = 1, fromlen;
X	struct sockaddr_in from;
X
X	openlog("rsh", LOG_PID | LOG_ODELAY, LOG_DAEMON);
X	fromlen = sizeof (from);
X	if (getpeername(0, &from, &fromlen) < 0) {
X		fprintf(stderr, "%s: ", argv[0]);
X		perror("getpeername");
X		_exit(1);
X	}
X	if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
X	    sizeof (on)) < 0)
X		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
X	linger.l_onoff = 1;
X	linger.l_linger = 60;			/* XXX */
X	if (setsockopt(0, SOL_SOCKET, SO_LINGER, (char *)&linger,
X	    sizeof (linger)) < 0)
X		syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m");
X	doit(dup(0), &from);
X}
X
Xchar	username[20] = "USER=";
Xchar	homedir[64] = "HOME=";
Xchar	shell[64] = "SHELL=";
Xchar	*envinit[] =
X	    {homedir, shell, "PATH=:/usr/ucb:/bin:/usr/bin", username, 0};
Xchar	**environ;
X
Xdoit(f, fromp)
X	int f;
X	struct sockaddr_in *fromp;
X{
X	char cmdbuf[NCARGS+1], *cp;
X	char locuser[16], remuser[16];
X	struct passwd *pwd;
X	int s;
X	struct hostent *hp;
X	char *hostname;
X	short port;
X	int pv[2], pid, ready, readfrom, cc;
X	char buf[BUFSIZ], sig;
X	int one = 1;
X
X	(void) signal(SIGINT, SIG_DFL);
X	(void) signal(SIGQUIT, SIG_DFL);
X	(void) signal(SIGTERM, SIG_DFL);
X#ifdef DEBUG
X	{ int t = open("/dev/tty", 2);
X	  if (t >= 0) {
X		ioctl(t, TIOCNOTTY, (char *)0);
X		(void) close(t);
X	  }
X	}
X#endif
X	fromp->sin_port = ntohs((u_short)fromp->sin_port);
X	if (fromp->sin_family != AF_INET ||
X	    fromp->sin_port >= IPPORT_RESERVED) {
X		syslog(LOG_ERR, "malformed from address\n");
X		exit(1);
X	}
X	(void) alarm(60);
X	port = 0;
X	for (;;) {
X		char c;
X		if (read(f, &c, 1) != 1) {
X			syslog(LOG_ERR, "read: %m");
X			shutdown(f, 1+1);
X			exit(1);
X		}
X		if (c == 0)
X			break;
X		port = port * 10 + c - '0';
X	}
X	(void) alarm(0);
X	if (port != 0) {
X		int lport = IPPORT_RESERVED - 1;
X		s = rresvport(&lport);
X		if (s < 0) {
X			syslog(LOG_ERR, "can't get stderr port: %m");
X			exit(1);
X		}
X		if (port >= IPPORT_RESERVED) {
X			syslog(LOG_ERR, "2nd port not reserved\n");
X			exit(1);
X		}
X		fromp->sin_port = htons((u_short)port);
X		if (connect(s, fromp, sizeof (*fromp)) < 0) {
X			syslog(LOG_INFO, "connect second port: %m");
X			exit(1);
X		}
X	}
X	dup2(f, 0);
X	dup2(f, 1);
X	dup2(f, 2);
X	hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof (struct in_addr),
X		fromp->sin_family);
X	if (hp)
X		hostname = hp->h_name;
X	else
X		hostname = inet_ntoa(fromp->sin_addr);
X	getstr(remuser, sizeof(remuser), "remuser");
X	getstr(locuser, sizeof(locuser), "locuser");
X	getstr(cmdbuf, sizeof(cmdbuf), "command");
X	setpwent();
X	pwd = getpwnam(locuser);
X	if (pwd == NULL) {
X		error("Login incorrect.\n");
X		exit(1);
X	}
X	endpwent();
X	if (chdir(pwd->pw_dir) < 0) {
X		(void) chdir("/");
X#ifdef notdef
X		error("No remote directory.\n");
X		exit(1);
X#endif
X	}
X	if (pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0' &&
X	    ruserok(hostname, pwd->pw_uid == 0, remuser, locuser) < 0) {
X		error("Permission denied.\n");
X		exit(1);
X	}
X
X#ifdef PERMS
X	/* build groups so we can look them up in 'permissions'
X	 * this check is not run for uid 0
X	 */
X	if(pwd->pw_uid != 0) {
X		setgrent();
X		ngrps = 0;
X		grp=getgrgid(pwd->pw_gid);
X		grpnames[ngrps++] = strdup(grp->gr_name);
X		while( grp=getgrent() ) {
X			if(pwd->pw_gid == grp->gr_gid)
X				continue;
X			while(*grp->gr_mem) {
X				if( !strcmp(locuser, *grp->gr_mem)) {
X					grpnames[ngrps++] = strdup(grp->gr_name);
X				}
X				grp->gr_mem++;
X			}
X		}
X		endgrent();
X		grpnames[ngrps] = NULL;
X
X		lp = permcheck(locuser, "rsh", grpnames, NULL);
X		if(!lp) {
X			syslog(LOG_CRIT,
X				"rsh:%s not permitted", locuser);
X			error("Permission denied.\n");
X			exit(1);
X		}		
X	}
X#endif
X
X	(void) write(2, "\0", 1);
X	if (port) {
X		if (pipe(pv) < 0) {
X			error("Can't make pipe.\n");
X			exit(1);
X		}
X		pid = fork();
X		if (pid == -1)  {
X			error("Try again.\n");
X			exit(1);
X		}
X		if (pid) {
X			(void) close(0); (void) close(1); (void) close(2);
X			(void) close(f); (void) close(pv[1]);
X			readfrom = (1<<s) | (1<<pv[0]);
X			ioctl(pv[0], FIONBIO, (char *)&one);
X			/* should set s nbio! */
X			do {
X				ready = readfrom;
X				if (select(16, &ready, (fd_set *)0,
X				    (fd_set *)0, (struct timeval *)0) < 0)
X					break;
X				if (ready & (1<<s)) {
X					if (read(s, &sig, 1) <= 0)
X						readfrom &= ~(1<<s);
X					else
X						killpg(pid, sig);
X				}
X				if (ready & (1<<pv[0])) {
X					errno = 0;
X					cc = read(pv[0], buf, sizeof (buf));
X					if (cc <= 0) {
X						shutdown(s, 1+1);
X						readfrom &= ~(1<<pv[0]);
X					} else
X						(void) write(s, buf, cc);
X				}
X			} while (readfrom);
X			exit(0);
X		}
X		setpgrp(0, getpid());
X		(void) close(s); (void) close(pv[0]);
X		dup2(pv[1], 2);
X	}
X	if (*pwd->pw_shell == '\0')
X		pwd->pw_shell = "/bin/sh";
X	(void) close(f);
X	(void) setgid((gid_t)pwd->pw_gid);
X	initgroups(pwd->pw_name, pwd->pw_gid);
X	(void) setuid((uid_t)pwd->pw_uid);
X	environ = envinit;
X	strncat(homedir, pwd->pw_dir, sizeof(homedir)-6);
X	strncat(shell, pwd->pw_shell, sizeof(shell)-7);
X	strncat(username, pwd->pw_name, sizeof(username)-6);
X	cp = rindex(pwd->pw_shell, '/');
X	if (cp)
X		cp++;
X	else
X		cp = pwd->pw_shell;
X	execl(pwd->pw_shell, cp, "-c", cmdbuf, 0);
X	perror(pwd->pw_shell);
X	exit(1);
X}
X
X/*VARARGS1*/
Xerror(fmt, a1, a2, a3)
X	char *fmt;
X	int a1, a2, a3;
X{
X	char buf[BUFSIZ];
X
X	buf[0] = 1;
X	(void) sprintf(buf+1, fmt, a1, a2, a3);
X	(void) write(2, buf, strlen(buf));
X}
X
Xgetstr(buf, cnt, err)
X	char *buf;
X	int cnt;
X	char *err;
X{
X	char c;
X
X	do {
X		if (read(0, &c, 1) != 1)
X			exit(1);
X		*buf++ = c;
X		if (--cnt == 0) {
X			error("%s too long\n", err);
X			exit(1);
X		}
X	} while (c != 0);
X}
END_OF_FILE
if test 6522 -ne `wc -c <'in.rshd/in.rshd.c'`; then
    echo shar: \"'in.rshd/in.rshd.c'\" unpacked with wrong size!
fi
# end of 'in.rshd/in.rshd.c'
fi
if test -f 'login/Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'login/Makefile'\"
else
echo shar: Extracting \"'login/Makefile'\" \(164 characters\)
sed "s/^X//" >'login/Makefile' <<'END_OF_FILE'
XOBJ = login.o ../perms/perms.o ../perms/glob_match.o
XCFLAGS = -g -DPERMS -DMAILPING
X
Xlogin:		$(OBJ)
X		$(CC) $(LDFLAGS) $(OBJ) -o login
X
Xclean:
X		$(RM) *~ *.o login
END_OF_FILE
if test 164 -ne `wc -c <'login/Makefile'`; then
    echo shar: \"'login/Makefile'\" unpacked with wrong size!
fi
# end of 'login/Makefile'
fi
if test -f 'login/login.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'login/login.c'\"
else
echo shar: Extracting \"'login/login.c'\" \(15072 characters\)
sed "s/^X//" >'login/login.c' <<'END_OF_FILE'
X/*
X * Copyright (c) 1980 Regents of the University of California.
X * All rights reserved.  The Berkeley software License Agreement
X * specifies the terms and conditions for redistribution.
X */
X
X#ifndef lint
Xchar copyright[] =
X"@(#) Copyright (c) 1980 Regents of the University of California.\n\
X All rights reserved.\n";
X#endif not lint
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)login.c	5.15 (Berkeley) 4/12/86";
X#endif not lint
X
X/*
X * login [ name ]
X * login -r hostname (for rlogind)
X * login -h hostname (for telnetd, etc.)
X */
X
X#include <sys/param.h>
X#include <sys/stat.h>
X#include <sys/time.h>
X#include <sys/resource.h>
X#include <sys/file.h>
X
X#include <sgtty.h>
X#include <utmp.h>
X#include <signal.h>
X#include <pwd.h>
X#include <stdio.h>
X#include <lastlog.h>
X#include <errno.h>
X#include <ttyent.h>
X#include <syslog.h>
X#include <grp.h>
X
X#ifdef MAILPING
X#include <sys/socket.h>
X#include <netinet/in.h>
X#include <netdb.h>
Xchar mailhost[] = "mailhost";
X#endif
X
X#ifdef PERMS
Xchar *strdup(), *grpnames[NGROUPS+1];
Xint ngrps, lp;
Xstruct group *grp;
Xextern int permcheck();
X#endif
X
X#define TTYGRPNAME	"tty"		/* name of group to own ttys */
X#define TTYGID(gid)	tty_gid(gid)	/* gid that owns all ttys */
X
X#define	SCMPN(a, b)	strncmp(a, b, sizeof(a))
X#define	SCPYN(a, b)	strncpy(a, b, sizeof(a))
X
X#define NMAX	sizeof(utmp.ut_name)
X#define HMAX	sizeof(utmp.ut_host)
X
X#define	FALSE	0
X#define	TRUE	-1
X
X#define QUOTAWARN       "/usr/ucb/quota -q"        /* warn user about quotas */
X
Xchar	nolog[] =	"/etc/nologin";
Xchar	qlog[]  =	".hushlogin";
Xchar	maildir[30] =	"/usr/spool/mail/";
Xchar	lastlog[] =	"/usr/adm/lastlog";
Xstruct	passwd nouser = {"", "nope", -1, -1, -1, "", "", "", "" };
Xstruct	sgttyb ttyb;
Xstruct	utmp utmp;
Xchar	minusnam[16] = "-";
Xchar	*envinit[] = { 0 };		/* now set by setenv calls */
X/*
X * This bounds the time given to login.  We initialize it here
X * so it can be patched on machines where it's too small.
X */
Xint	timeout = 60;
X
Xchar	term[64];
X
Xstruct	passwd *pwd;
Xchar	*strcat(), *rindex(), *index(), *malloc(), *realloc();
Xint	timedout();
Xchar	*ttyname();
Xchar	*crypt();
Xchar	*getpass();
Xchar	*stypeof();
Xextern	char **environ;
Xextern	int errno;
X
Xstruct	tchars tc = {
X	CINTR, CQUIT, CSTART, CSTOP, CEOT, CBRK
X};
Xstruct	ltchars ltc = {
X	CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT
X};
X
Xstruct winsize win = { 0, 0, 0, 0 };
X
Xint	rflag;
Xint	usererr = -1;
Xchar	rusername[NMAX+1], lusername[NMAX+1];
Xchar	rpassword[NMAX+1];
Xchar	name[NMAX+1];
Xchar	*rhost;
X
Xmain(argc, argv)
X	char *argv[];
X{
X	register char *namep;
X	int pflag = 0, hflag = 0, t, f, c;
X	int invalid, quietlog;
X	FILE *nlfd;
X	char *ttyn, *tty;
X	int ldisc = 0, zero = 0, i;
X	char **envnew;
X
X	signal(SIGALRM, timedout);
X	alarm(timeout);
X	signal(SIGQUIT, SIG_IGN);
X	signal(SIGINT, SIG_IGN);
X	setpriority(PRIO_PROCESS, 0, 0);
X	/*
X	 * -p is used by getty to tell login not to destroy the environment
X	 * -r is used by rlogind to cause the autologin protocol;
X	 * -h is used by other servers to pass the name of the
X	 * remote host to login so that it may be placed in utmp and wtmp
X	 */
X	while (argc > 1) {
X		if (strcmp(argv[1], "-r") == 0) {
X			if (rflag || hflag) {
X				printf("Only one of -r and -h allowed\n");
X				exit(1);
X			}
X			rflag = 1;
X			usererr = doremotelogin(argv[2]);
X			SCPYN(utmp.ut_host, argv[2]);
X			argc -= 2;
X			argv += 2;
X			continue;
X		}
X		if (strcmp(argv[1], "-h") == 0 && getuid() == 0) {
X			if (rflag || hflag) {
X				printf("Only one of -r and -h allowed\n");
X				exit(1);
X			}
X			hflag = 1;
X			SCPYN(utmp.ut_host, argv[2]);
X			argc -= 2;
X			argv += 2;
X			continue;
X		}
X		if (strcmp(argv[1], "-p") == 0) {
X			argc--;
X			argv++;
X			pflag = 1;
X			continue;
X		}
X		break;
X	}
X	ioctl(0, TIOCLSET, &zero);
X	ioctl(0, TIOCNXCL, 0);
X	ioctl(0, FIONBIO, &zero);
X	ioctl(0, FIOASYNC, &zero);
X	ioctl(0, TIOCGETP, &ttyb);
X	/*
X	 * If talking to an rlogin process,
X	 * propagate the terminal type and
X	 * baud rate across the network.
X	 */
X	if (rflag)
X		doremoteterm(term, &ttyb);
X	ttyb.sg_erase = CERASE;
X	ttyb.sg_kill = CKILL;
X	ioctl(0, TIOCSLTC, &ltc);
X	ioctl(0, TIOCSETC, &tc);
X	ioctl(0, TIOCSETP, &ttyb);
X	for (t = getdtablesize(); t > 2; t--)
X		close(t);
X	ttyn = ttyname(0);
X	if (ttyn == (char *)0 || *ttyn == '\0')
X		ttyn = "/dev/tty??";
X	tty = rindex(ttyn, '/');
X	if (tty == NULL)
X		tty = ttyn;
X	else
X		tty++;
X	openlog("login", LOG_ODELAY, LOG_AUTH);
X	t = 0;
X	invalid = FALSE;
X	do {
X		ldisc = 0;
X		ioctl(0, TIOCSETD, &ldisc);
X		SCPYN(utmp.ut_name, "");
X		/*
X		 * Name specified, take it.
X		 */
X		if (argc > 1) {
X			SCPYN(utmp.ut_name, argv[1]);
X			argc = 0;
X		}
X		/*
X		 * If remote login take given name,
X		 * otherwise prompt user for something.
X		 */
X		if (rflag && !invalid)
X			SCPYN(utmp.ut_name, lusername);
X		else
X			getloginname(&utmp);
X		invalid = FALSE;
X		if (!strcmp(pwd->pw_shell, "/bin/csh")) {
X			ldisc = NTTYDISC;
X			ioctl(0, TIOCSETD, &ldisc);
X		}
X		/*
X		 * If no remote login authentication and
X		 * a password exists for this user, prompt
X		 * for one and verify it.
X		 */
X		if (usererr == -1 && *pwd->pw_passwd != '\0') {
X			char *pp;
X
X			setpriority(PRIO_PROCESS, 0, -4);
X			pp = getpass("Password:");
X			namep = crypt(pp, pwd->pw_passwd);
X			setpriority(PRIO_PROCESS, 0, 0);
X			if (strcmp(namep, pwd->pw_passwd))
X				invalid = TRUE;
X		}
X		/*
X		 * If user not super-user, check for logins disabled.
X		 */
X		if (pwd->pw_uid != 0 && (nlfd = fopen(nolog, "r")) > 0) {
X			while ((c = getc(nlfd)) != EOF)
X				putchar(c);
X			fflush(stdout);
X			sleep(5);
X			exit(0);
X		}
X		/*
X		 * If valid so far and root is logging in,
X		 * see if root logins on this terminal are permitted.
X		 */
X		if (!invalid && pwd->pw_uid == 0 && !rootterm(tty)) {
X			if (utmp.ut_host[0])
X				syslog(LOG_CRIT,
X				    "ROOT LOGIN REFUSED ON %s FROM %.*s",
X				    tty, HMAX, utmp.ut_host);
X			else
X				syslog(LOG_CRIT,
X				    "ROOT LOGIN REFUSED ON %s", tty);
X			invalid = TRUE;
X		}
X#ifdef PERMS
X		/* build groups so we can look them up in 'permissions'
X		 * this check is not run for uid 0
X		 */
X		if(!invalid && pwd->pw_uid != 0) {
X			setgrent();
X			ngrps = 0;
X			if(!(grp=getgrgid(pwd->pw_gid))) {
X				syslog(LOG_CRIT,
X					"cannot find group name for %d\n", pwd->pw_gid);
X				goto broken_groups;
X			}
X			grpnames[ngrps++] = strdup(grp->gr_name);
X			while( grp=getgrent() ) {
X				if(pwd->pw_gid == grp->gr_gid)
X					continue;
X				while(*grp->gr_mem) {
X					if( !strcmp(lusername, *grp->gr_mem)) {
X						grpnames[ngrps++] = strdup(grp->gr_name);
X					}
X					grp->gr_mem++;
X				}
X			}
X			endgrent();
X			grpnames[ngrps] = NULL;
X			lp = permcheck(lusername, tty, grpnames, NULL);
X			if(!lp) {
X				syslog(LOG_CRIT,
X					"%s:%s not permitted", tty, lusername);
X				printf("Permission denied.\n");
X				exit(1);
X			}
X		}
Xbroken_groups:
X#endif
X		if (invalid) {
X			printf("Login incorrect\n");
X			if (++t >= 5) {
X				if (utmp.ut_host[0])
X					syslog(LOG_CRIT,
X					    "REPEATED LOGIN FAILURES ON %s FROM %.*s, %.*s",
X					    tty, HMAX, utmp.ut_host,
X					    NMAX, utmp.ut_name);
X				else
X					syslog(LOG_CRIT,
X					    "REPEATED LOGIN FAILURES ON %s, %.*s",
X						tty, NMAX, utmp.ut_name);
X				ioctl(0, TIOCHPCL, (struct sgttyb *) 0);
X				close(0), close(1), close(2);
X				sleep(10);
X				exit(1);
X			}
X		}
X		if (*pwd->pw_shell == '\0')
X			pwd->pw_shell = "/bin/sh";
X		if (chdir(pwd->pw_dir) < 0 && !invalid ) {
X			if (chdir("/") < 0) {
X				printf("No directory!\n");
X				invalid = TRUE;
X			} else {
X				printf("No directory! %s\n",
X				   "Logging in with home=/");
X				pwd->pw_dir = "/";
X			}
X		}
X		/*
X		 * Remote login invalid must have been because
X		 * of a restriction of some sort, no extra chances.
X		 */
X		if (!usererr && invalid)
X			exit(1);
X	} while (invalid);
X/* committed to login turn off timeout */
X	alarm(0);
X
X	time(&utmp.ut_time);
X	t = ttyslot();
X	if (t > 0 && (f = open("/etc/utmp", O_WRONLY)) >= 0) {
X		lseek(f, (long)(t*sizeof(utmp)), 0);
X		SCPYN(utmp.ut_line, tty);
X		write(f, (char *)&utmp, sizeof(utmp));
X		close(f);
X	}
X	if ((f = open("/usr/adm/wtmp", O_WRONLY|O_APPEND)) >= 0) {
X		write(f, (char *)&utmp, sizeof(utmp));
X		close(f);
X	}
X	quietlog = access(qlog, F_OK) == 0;
X	if ((f = open(lastlog, O_RDWR)) >= 0) {
X		struct lastlog ll;
X
X		lseek(f, (long)pwd->pw_uid * sizeof (struct lastlog), 0);
X		if (read(f, (char *) &ll, sizeof ll) == sizeof ll &&
X		    ll.ll_time != 0 && !quietlog) {
X			printf("Last login: %.*s ",
X			    24-5, (char *)ctime(&ll.ll_time));
X			if (*ll.ll_host != '\0')
X				printf("from %.*s\n",
X				    sizeof (ll.ll_host), ll.ll_host);
X			else
X				printf("on %.*s\n",
X				    sizeof (ll.ll_line), ll.ll_line);
X		}
X		lseek(f, (long)pwd->pw_uid * sizeof (struct lastlog), 0);
X		time(&ll.ll_time);
X		SCPYN(ll.ll_line, tty);
X		SCPYN(ll.ll_host, utmp.ut_host);
X		write(f, (char *) &ll, sizeof ll);
X		close(f);
X	}
X	chown(ttyn, pwd->pw_uid, TTYGID(pwd->pw_gid));
X	if (!hflag && !rflag)					/* XXX */
X		ioctl(0, TIOCSWINSZ, &win);
X	chmod(ttyn, 0620);
X	setgid(pwd->pw_gid);
X	strncpy(name, utmp.ut_name, NMAX);
X	name[NMAX] = '\0';
X	initgroups(name, pwd->pw_gid);
X	setuid(pwd->pw_uid);
X	/* destroy environment unless user has asked to preserve it */
X	if (!pflag)
X		environ = envinit;
X
X	/* set up environment, this time without destruction */
X	/* copy the environment before setenving */
X	i = 0;
X	while (environ[i] != NULL)
X		i++;
X	envnew = (char **) malloc(sizeof (char *) * (i + 1));
X	for (; i >= 0; i--)
X		envnew[i] = environ[i];
X	environ = envnew;
X
X	setenv("HOME=", pwd->pw_dir, 1);
X	setenv("SHELL=", pwd->pw_shell, 1);
X	if (term[0] == '\0')
X		strncpy(term, stypeof(tty), sizeof(term));
X	setenv("TERM=", term, 0);
X	setenv("USER=", pwd->pw_name, 1);
X	setenv("PATH=", ":/usr/ucb:/bin:/usr/bin", 0);
X
X	if ((namep = rindex(pwd->pw_shell, '/')) == NULL)
X		namep = pwd->pw_shell;
X	else
X		namep++;
X	strcat(minusnam, namep);
X	if (tty[sizeof("tty")-1] == 'd')
X		syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
X	if (pwd->pw_uid == 0)
X		if (utmp.ut_host[0])
X			syslog(LOG_NOTICE, "ROOT LOGIN %s FROM %.*s",
X			    tty, HMAX, utmp.ut_host);
X		else
X			syslog(LOG_NOTICE, "ROOT LOGIN %s", tty);
X	if (!quietlog) {
X		struct stat st;
X
X		showmotd();
X		strcat(maildir, pwd->pw_name);
X#ifdef MAILPING
X		if(!serverup())
X			printf("No idea if you have mail.\n");
X		else
X#endif
X		if (stat(maildir, &st) == 0 && st.st_size != 0)
X			printf("You have %smail.\n",
X				(st.st_mtime > st.st_atime) ? "new " : "");
X		system(QUOTAWARN);
X	}
X	signal(SIGALRM, SIG_DFL);
X	signal(SIGQUIT, SIG_DFL);
X	signal(SIGINT, SIG_DFL);
X	signal(SIGTSTP, SIG_IGN);
X	execlp(pwd->pw_shell, minusnam, 0);
X	perror(pwd->pw_shell);
X	printf("No shell\n");
X	exit(0);
X}
X
Xgetloginname(up)
X	register struct utmp *up;
X{
X	register char *namep;
X	char c;
X
X	while (up->ut_name[0] == '\0') {
X		namep = up->ut_name;
X		printf("login: ");
X		while ((c = getchar()) != '\n') {
X			if (c == ' ')
X				c = '_';
X			if (c == EOF)
X				exit(0);
X			if (namep < up->ut_name+NMAX)
X				*namep++ = c;
X		}
X	}
X	strncpy(lusername, up->ut_name, NMAX);
X	lusername[NMAX] = 0;
X	if ((pwd = getpwnam(lusername)) == NULL)
X		pwd = &nouser;
X}
X
Xtimedout()
X{
X
X	printf("Login timed out after %d seconds\n", timeout);
X	exit(0);
X}
X
Xint	stopmotd;
Xcatch()
X{
X
X	signal(SIGINT, SIG_IGN);
X	stopmotd++;
X}
X
Xrootterm(tty)
X	char *tty;
X{
X	register struct ttyent *t;
X
X	if ((t = getttynam(tty)) != NULL) {
X		if (t->ty_status & TTY_SECURE)
X			return (1);
X	}
X	return (0);
X}
X
Xshowmotd()
X{
X	FILE *mf;
X	register c;
X
X	signal(SIGINT, catch);
X	if ((mf = fopen("/etc/motd", "r")) != NULL) {
X		while ((c = getc(mf)) != EOF && stopmotd == 0)
X			putchar(c);
X		fclose(mf);
X	}
X	signal(SIGINT, SIG_IGN);
X}
X
X#undef	UNKNOWN
X#define UNKNOWN "su"
X
Xchar *
Xstypeof(ttyid)
X	char *ttyid;
X{
X	register struct ttyent *t;
X
X	if (ttyid == NULL || (t = getttynam(ttyid)) == NULL)
X		return (UNKNOWN);
X	return (t->ty_type);
X}
X
Xdoremotelogin(host)
X	char *host;
X{
X	getstr(rusername, sizeof (rusername), "remuser");
X	getstr(lusername, sizeof (lusername), "locuser");
X	getstr(term, sizeof(term), "Terminal type");
X	if (getuid()) {
X		pwd = &nouser;
X		return(-1);
X	}
X	pwd = getpwnam(lusername);
X	if (pwd == NULL) {
X		pwd = &nouser;
X		return(-1);
X	}
X	return(ruserok(host, (pwd->pw_uid == 0), rusername, lusername));
X}
X
Xgetstr(buf, cnt, err)
X	char *buf;
X	int cnt;
X	char *err;
X{
X	char c;
X
X	do {
X		if (read(0, &c, 1) != 1)
X			exit(1);
X		if (--cnt < 0) {
X			printf("%s too long\r\n", err);
X			exit(1);
X		}
X		*buf++ = c;
X	} while (c != 0);
X}
X
Xchar	*speeds[] =
X    { "0", "50", "75", "110", "134", "150", "200", "300",
X      "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400" };
X#define	NSPEEDS	(sizeof (speeds) / sizeof (speeds[0]))
X
Xdoremoteterm(term, tp)
X	char *term;
X	struct sgttyb *tp;
X{
X	register char *cp = index(term, '/'), **cpp;
X	char *speed;
X
X	if (cp) {
X		*cp++ = '\0';
X		speed = cp;
X		cp = index(speed, '/');
X		if (cp)
X			*cp++ = '\0';
X		for (cpp = speeds; cpp < &speeds[NSPEEDS]; cpp++)
X			if (strcmp(*cpp, speed) == 0) {
X				tp->sg_ispeed = tp->sg_ospeed = cpp-speeds;
X				break;
X			}
X	}
X	tp->sg_flags = ECHO|CRMOD|ANYP|XTABS;
X}
X
X/*
X * Set the value of var to be arg in the Unix 4.2 BSD environment env.
X * Var should end with '='.
X * (bindings are of the form "var=value")
X * This procedure assumes the memory for the first level of environ
X * was allocated using malloc.
X */
Xsetenv(var, value, clobber)
X	char *var, *value;
X{
X	extern char **environ;
X	int index = 0;
X	int varlen = strlen(var);
X	int vallen = strlen(value);
X
X	for (index = 0; environ[index] != NULL; index++) {
X		if (strncmp(environ[index], var, varlen) == 0) {
X			/* found it */
X			if (!clobber)
X				return;
X			environ[index] = malloc(varlen + vallen + 1);
X			strcpy(environ[index], var);
X			strcat(environ[index], value);
X			return;
X		}
X	}
X	environ = (char **) realloc(environ, sizeof (char *) * (index + 2));
X	if (environ == NULL) {
X		fprintf(stderr, "login: malloc out of memory\n");
X		exit(1);
X	}
X	environ[index] = malloc(varlen + vallen + 1);
X	strcpy(environ[index], var);
X	strcat(environ[index], value);
X	environ[++index] = NULL;
X}
X
Xtty_gid(default_gid)
X	int default_gid;
X{
X	struct group *getgrnam(), *gr;
X	int gid = default_gid;
X
X	gr = getgrnam(TTYGRPNAME);
X	if (gr != (struct group *) 0)
X		gid = gr->gr_gid;
X
X	endgrent();
X
X	return (gid);
X}
X
X#ifdef MAILPING
X/* ping mailhost, at port echo/udp
X * determine if server is up=1
X * if unable, default to down=0
X */
Xserverup()
X{
X	struct sockaddr_in sin;
X	struct hostent *hent;
X	struct servent *sent;
X	struct timeval tv;
X	int s, count, tsize;
X	fd_set fdin;
X
X	if( !(hent=gethostbyname(mailhost)))
X		return 0;
X	if( !(sent=getservbyname("echo", "udp")))
X		return 0;
X
X	bzero(&sin, sizeof sin);
X	sin.sin_family = hent->h_addrtype;
X	bcopy((char *)hent->h_addr, (char *)&sin.sin_addr, hent->h_length);
X	sin.sin_port = sent->s_port;
X
X	if( (s=socket(AF_INET, SOCK_DGRAM, 0)) <0)
X		return 0;
X
X	tsize = getdtablesize();
X	tv.tv_sec = 1;
X	tv.tv_usec = 0;
X	for(count=0; count<10; count++) {
X		FD_ZERO(&fdin);
X		FD_SET(s, &fdin);
X		switch( select(tsize, &fdin, NULL, NULL, &tv) ) {
X		case -1:
X			perror("select");
X			break;
X		case 0:
X			if( sendto(s, "UP?", 3, 0, &sin, sizeof sin) < 0)
X				perror("sendto");
X			break;
X		case 1:
X			close(s);
X			return 1;
X		}
X	}
X
X	close(s);
X	return 0;
X}
X#endif
END_OF_FILE
if test 15072 -ne `wc -c <'login/login.c'`; then
    echo shar: \"'login/login.c'\" unpacked with wrong size!
fi
# end of 'login/login.c'
fi
if test -f 'perms/Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'perms/Makefile'\"
else
echo shar: Extracting \"'perms/Makefile'\" \(293 characters\)
sed "s/^X//" >'perms/Makefile' <<'END_OF_FILE'
XYACCFLAGS = -d
XOBJ = perms.o glob_match.o
XCFLAGS = -g -DPERMS
X
Xall:		glob_match.o perms.o
X
Xperms.o:	perms.y
X		yacc $(YACCFLAGS) perms.y
X		sed -e "s/yy/xx/g" -e "s/YY/XX/g" y.tab.c > x.tab.c
X		$(CC) $(CFLAGS) -c x.tab.c -o perms.o
X		$(RM) y.tab.c x.tab.c
X
Xclean:
X		$(RM) *~ *.o y.tab.h y.tab.c
END_OF_FILE
if test 293 -ne `wc -c <'perms/Makefile'`; then
    echo shar: \"'perms/Makefile'\" unpacked with wrong size!
fi
# end of 'perms/Makefile'
fi
if test -f 'perms/glob_match.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'perms/glob_match.c'\"
else
echo shar: Extracting \"'perms/glob_match.c'\" \(7660 characters\)
sed "s/^X//" >'perms/glob_match.c' <<'END_OF_FILE'
X/* File-name wildcard pattern matching for GNU.
X   Copyright (C) 1985, 1988 Free Software Foundation, Inc.
X
X               NO WARRANTY
X
X  BECAUSE THIS PROGRAM IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY
XNO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW.  EXCEPT
XWHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC,
XRICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE THIS PROGRAM "AS IS"
XWITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
XBUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
XFITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY
XAND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE PROGRAM PROVE
XDEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
XCORRECTION.
X
X IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M.
XSTALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY
XWHO MAY MODIFY AND REDISTRIBUTE THIS PROGRAM AS PERMITTED BELOW, BE
XLIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR
XOTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
XUSE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR
XDATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR
XA FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) THIS
XPROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
XDAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY.
X
X        GENERAL PUBLIC LICENSE TO COPY
X
X  1. You may copy and distribute verbatim copies of this source file
Xas you receive it, in any medium, provided that you conspicuously and
Xappropriately publish on each copy a valid copyright notice "Copyright
X(C) 1988 Free Software Foundation, Inc."; and include following the
Xcopyright notice a verbatim copy of the above disclaimer of warranty
Xand of this License.
X
X  2. You may modify your copy or copies of this source file or
Xany portion of it, and copy and distribute such modifications under
Xthe terms of Paragraph 1 above, provided that you also do the following:
X
X    a) cause the modified files to carry prominent notices stating
X    that you changed the files and the date of any change; and
X
X    b) cause the whole of any work that you distribute or publish,
X    that in whole or in part contains or is a derivative of this
X    program or any part thereof, to be licensed at no charge to all
X    third parties on terms identical to those contained in this
X    License Agreement (except that you may choose to grant more extensive
X    warranty protection to some or all third parties, at your option).
X
X    c) You may charge a distribution fee for the physical act of
X    transferring a copy, and you may at your option offer warranty
X    protection in exchange for a fee.
X
XMere aggregation of another unrelated program with this program (or its
Xderivative) on a volume of a storage or distribution medium does not bring
Xthe other program under the scope of these terms.
X
X  3. You may copy and distribute this program (or a portion or derivative
Xof it, under Paragraph 2) in object code or executable form under the terms
Xof Paragraphs 1 and 2 above provided that you also do one of the following:
X
X    a) accompany it with the complete corresponding machine-readable
X    source code, which must be distributed under the terms of
X    Paragraphs 1 and 2 above; or,
X
X    b) accompany it with a written offer, valid for at least three
X    years, to give any third party free (except for a nominal
X    shipping charge) a complete machine-readable copy of the
X    corresponding source code, to be distributed under the terms of
X    Paragraphs 1 and 2 above; or,
X
X    c) accompany it with the information you received as to where the
X    corresponding source code may be obtained.  (This alternative is
X    allowed only for noncommercial distribution and only if you
X    received the program in object code or executable form alone.)
X
XFor an executable file, complete source code means all the source code for
Xall modules it contains; but, as a special exception, it need not include
Xsource code for modules which are standard libraries that accompany the
Xoperating system on which the executable file runs.
X
X  4. You may not copy, sublicense, distribute or transfer this program
Xexcept as expressly provided under this License Agreement.  Any attempt
Xotherwise to copy, sublicense, distribute or transfer this program is void and
Xyour rights to use the program under this License agreement shall be
Xautomatically terminated.  However, parties who have received computer
Xsoftware programs from you with this License Agreement will not have
Xtheir licenses terminated so long as such parties remain in full compliance.
X
X
XIn other words, you are welcome to use, share and improve this program.
XYou are forbidden to forbid anyone else to use, share and improve
Xwhat you give them.   Help stamp out software-hoarding!  */
X
X/* Match the pattern PATTERN against the string TEXT;
X   return 1 if it matches, 0 otherwise.
X
X   A match means the entire string TEXT is used up in matching.
X
X   In the pattern string, `*' matches any sequence of characters,
X   `?' matches any character, [SET] matches any character in the specified set,
X   [^SET] matches any character not in the specified set.
X
X   A set is composed of characters or ranges; a range looks like
X   character hyphen character (as in 0-9 or A-Z).
X   [0-9a-zA-Z_] is the set of characters allowed in C identifiers.
X   Any other character in the pattern must be matched exactly.
X
X   To suppress the special syntactic significance of any of `[]*?^-\',
X   and match the character exactly, precede it with a `\'.
X
X   If DOT_SPECIAL is nonzero,
X   `*' and `?' do not match `.' at the beginning of TEXT.  */
X
Xint
Xglob_match (pattern, text, dot_special)
X     char *pattern, *text;
X     int dot_special;
X{
X  register char *p = pattern, *t = text;
X  register char c;
X
X  while ((c = *p++))
X    {
X      switch (c)
X    {
X    case '?':
X      if (*t == 0 || (dot_special && t == text && *t == '.')) return 0;
X      else ++t;
X      break;
X
X    case '\\':
X      if (*p++ != *t++) return 0;
X      break;
X
X    case '*':
X      if (dot_special && t == text && *t == '.')
X        return 0;
X      return star_glob_match (p, t);
X
X    case '[':
X      {
X        register char c1 = *t++;
X        register int invert = (*p == '^');
X
X        if (invert) p++;
X
X        c = *p++;
X        while (1)
X          {
X        register char cstart = c, cend = c;
X
X        if (c == '\\')
X          {
X            cstart = *p++; cend = cstart;
X          }
X        c = *p++;
X        if (c == '-')
X          { cend = *p++; if (cend == '\\') cend = *p++; c = *p++; }
X        if (c1 >= cstart && c1 <= cend) goto match;
X        if (c == ']')
X          break;
X          }
X        if (!invert) return 0;
X        break;
X
X      match:
X        /* Skip the rest of the [...] construct that already matched.  */
X        while (c != ']')
X          {
X            c = *p++;
X        if (c == '\\') p++;
X          }
X        if (invert) return 0;
X        break;
X      }
X
X    default:
X      if (c != *t++) return 0;
X    }
X    }
X
X  if (*t) return 0;
X  return 1;
X}
X
X/* Like glob_match, but match PATTERN against any final segment of TEXT.  */
X
Xstatic int
Xstar_glob_match (pattern, text)
X     char *pattern, *text;
X{
X  register char *p = pattern, *t = text;
X  register char c, c1;
X
X  while ((c = *p++) == '?' || c == '*')
X    {
X      if (c == '?' && *t++ == 0)
X    return 0;
X    }
X
X  if (c == 0)
X    return 1;
X
X  if (c == '\\') c1 = *p;
X  else c1 = c;
X
X  for (;;)
X    {
X      if ((c == '[' || *t == c1)
X          && glob_match (p - 1, t, 0))
X    return 1;
X      if (*t++ == 0) return 0;
X    }
X}
END_OF_FILE
if test 7660 -ne `wc -c <'perms/glob_match.c'`; then
    echo shar: \"'perms/glob_match.c'\" unpacked with wrong size!
fi
# end of 'perms/glob_match.c'
fi
if test -f 'perms/perms.y' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'perms/perms.y'\"
else
echo shar: Extracting \"'perms/perms.y'\" \(7791 characters\)
sed "s/^X//" >'perms/perms.y' <<'END_OF_FILE'
X/* Copyright (c) 1990 Theo de Raadt deraadt@cpsc.ucalgary.ca
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 Theo de Raadt.
X */
X%{
X#ifndef lint
Xchar Copyright[] =
X"@(#) Copyright (c) 1990 Theo de Raadt.\nAll rights reserved.\n";
X#endif not lint
X
X#ifndef lint
Xstatic	char Sccsid[] = "@(#)perms.y 1.0 90/11/01 TDR";
X#endif not lint
X
X#include <sys/param.h>
X#include <sys/types.h>
X#include <sys/socket.h>
X#include <sys/file.h>
X#include <netinet/in.h>
X#include <stdio.h>
X#include <ctype.h>
X#include <string.h>
X#include <pwd.h>
X#include <grp.h>
X#include <rpc/types.h>
X#include <rpc/rpc.h>
X#include <rpcsvc/ypclnt.h>
X#include <rpcsvc/yp_prot.h>
X#include <syslog.h>
X#include "y.tab.h"
X
X#undef DEBUG
X
X/* define SECURE if you want logins disallowed if errors show up
X * during a map lookup. If this is not set, YP failures, incorrect
X * maps, automount failure, etc.,will let anymore login until the
X * problem is fixed. In general, you never want to define this!
X */
X#undef	SECURE
X
X#define SEARCHMAP "permissions"
X
X#define LX_SP	0x00
X#define LX_EOF	0x01
X#define LX_C	0x02
X#define LX_X	0x04
X#define LX_SC	0x08
X#define LX_S1	0x10
Xstatic char lookup[] = {
X	0x01, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 	/* 00-07 */
X	0x04, 0x00, 0x01, 0x04, 0x04, 0x01, 0x04, 0x04, 	/* 08-0f */
X	0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 	/* 10-17 */
X	0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 	/* 18-1f */
X	0x00, 0x02, 0x04, 0x04, 0x02, 0x04, 0x02, 0x04, 	/* 20-27 */
X	0x18, 0x18, 0x18, 0x02, 0x02, 0x0a, 0x18, 0x18, 	/* 28-2f */
X	0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 	/* 30-37 */
X	0x18, 0x18, 0x02, 0x04, 0x04, 0x04, 0x04, 0x18, 	/* 38-3f */
X	0x02, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 	/* 40-47 */
X	0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 	/* 48-4f */
X	0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 	/* 50-57 */
X	0x18, 0x18, 0x18, 0x18, 0x04, 0x18, 0x18, 0x18, 	/* 58-5f */
X	0x04, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 	/* 60-67 */
X	0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 	/* 68-6f */
X	0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 	/* 70-77 */
X	0x18, 0x18, 0x18, 0x04, 0x02, 0x04, 0x04, 0x04, 	/* 78-7f */
X};
X
X#define YYSTYPE caddr_t
Xextern YYSTYPE yylval;
X
Xstatic char *grplst[NGROUPS], **grplist;
Xchar domain[MAXHOSTNAMELEN], key[MAXHOSTNAMELEN];
Xstatic char *usern, *ttyn, *line;
Xstatic int ttymatch, grpusrmatch;
X
Xstatic int allow, err_allow;
Xstatic char *stack[20];
Xstatic int stackp;
X
Xstatic
Xyyerror() {
X	syslog(LOG_CRIT,
X		"permissions: [key %s] parsing error\n", key);
X	err_allow = 1;
X}
X
X/*
X * noisy, specifies to be noisy on failure or not
X */
Xstatic char *
Xgetypent(s, noisy)
Xchar *s;
X{
X	char *entry;
X	int entrylen, err;
X
X	err = yp_match(domain, SEARCHMAP, s, strlen(s), &entry, &entrylen);
X	if(err) {
X		if(noisy) syslog(LOG_CRIT,
X			"yperr: [key %s] %s\n", s, yperr_string(err));
X		return NULL;
X	}
X	return entry;
X}
X
X/* usr:  user name. Absolutely has to be the login name.
X * tty:  tty name. if NULL, any tty will match.
X * grps: NULL terminated array of group names. If grps==NULL, perms
X *	 will fetch the groups using getpwent() and getgrent(). These
X *	 have static data -- beware.
X * lkey: base lookup key. If NULL, hostname will be used.
X *
X * return values need cleaning up.
X *  1 - allowed
X *  0 - not allowed
X * -1 - error
X */
Xint
Xpermcheck(usr, tty, grps, lkey)
Xchar *usr, *tty, **grps, *lkey;
X{
X	struct passwd *pwd;
X	struct group *grp;
X	int ngrps = 0;
X
X	if(!usr)
X		return -1;
X	if(!grps) {
X		grps = grplst;
X#ifdef DEBUG
X		printf("user: %s\n", usr);
X#endif
X		setpwent();
X		if( !(pwd=getpwnam(usr)))
X			return -1;
X#ifdef DEBUG
X		printf("user: (%s)\n", pwd->pw_name);
X#endif
X
X		setgrent();
X		if( !(grp=getgrgid(pwd->pw_gid)) )
X			return -1;
X		*grps++ = strdup(grp->gr_name);
X#ifdef DEBUG
X		printf("group: %s\n", grp->gr_name);
X#endif
X		ngrps++;
X		while( grp=getgrent() ) {
X			if(pwd->pw_gid == grp->gr_gid)
X				continue;
X			while(*grp->gr_mem) {
X				if(!strcmp(*grp->gr_mem, usr)) {
X					if(++ngrps>NGROUPS)
X						break;
X					*grps++ = strdup(grp->gr_name);
X#ifdef DEBUG
X					printf("group: %s\n", grp->gr_name);
X#endif
X				}
X				grp->gr_mem++;
X			}
X		}
X		endgrent();
X		endpwent();
X		*grps = NULL;
X		grps = grplst;
X	}
X	if(lkey)
X		strcpy(key, lkey);
X	else
X		gethostname(key, sizeof key);
X
X	getdomainname(domain, sizeof domain);
X	if( !(line=getypent(key, 0)) ) {
X		line = getypent("*", 1);
X	}
X	allow = 0;
X	err_allow = !line;
X
X	if(line) {
X		usern = usr;
X		ttyn = tty;
X		grplist = grps;
X		ttymatch = grpusrmatch = -1;
X		yyparse();
X	}
X
X#ifdef SECURE
X	return allow;
X#else
X	return allow | err_allow;
X#endif
X}
X
Xstatic
Xnewmap(s)
Xchar *s;
X{
X#ifdef DEBUG
X	printf("include %s, pushing %s", s, line);
X#endif
X	stack[stackp++] = line;
X	if(stackp==sizeof(stack)/sizeof(char *)) {
X		syslog(LOG_CRIT,
X			"permissions: stack overflow on [key %s]\n", s);
X		line = stack[--stackp];
X		err_allow = 1;
X		return;
X	}
X	line = getypent(s,1);
X	if(!line) {
X		syslog(LOG_CRIT,
X			"permissions: [key %s] not found\n", s);
X		line = stack[--stackp];
X		err_allow = 1;
X		return;
X	}
X#ifdef DEBUG
X	printf("result %s", line);
X#endif
X}
X
Xstatic
Xyylex()
X{
X	static pos = 0;
X	static char savebuf[1024], *p;
X	char c;
X
Xrestartlex:
X	p = savebuf;
X	while(1)
X		switch( lookup[(c= *line++)] ) {
X		case LX_SP:
X			break;
X		case LX_EOF:
X			if(!stackp)
X				return -1;
X			line = stack[--stackp];
X#ifdef DEBUG
X			printf("popping %s", line);
X#endif
X			goto restartlex;
X		case LX_C:
X		case LX_C|LX_SC:
X#ifdef DEBUG
X			printf("lex: char '%c'\n", c);
X#endif
X			return c;
X		case LX_X:
X			syslog(LOG_CRIT,
X				"permissions: [key %s] illegal char 0%3o\n",
X				key, c);
X			err_allow = 1;
X			return -1;
X		case LX_S1:
X		case LX_S1|LX_SC:
X			do {
X				*p++ = c;
X				c = *line++;
X			} while( lookup[c]&LX_SC );
X			*p = '\0';
X			line--;
X			yylval = savebuf;
X#ifdef DEBUG
X			printf("lex: symbol %s\n", savebuf);
X#endif
X			return SYMBOL;
X		}
X}
X
Xstatic void
Xgrpcheck(s, add)
Xchar *s;
X{
X	FILE *fin;
X	char **grp, *p, buf[80];
X	int i;
X
X	switch(*s) {
X	case '(':
X		if( strchr(s+1, '(')) {
X			syslog(LOG_CRIT,
X				"permissions: [key %s] can't nest brackets\n", s);
X			return;
X		}
X
X		p = strchr(s, ')');
X		if(p)
X			*p = '\0';
X		else {
X			syslog(LOG_CRIT,
X				"permissions: [key %s] include syntax error\n", s);
X			return;
X		}
X		if( strchr(s, ')')) {
X			syslog(LOG_CRIT,
X				"permissions: [key %s] can't nest brackets\n", s);
X			return;
X		}
X
X		s++;
X		fin = fopen(s, "r");
X		if(!fin) {
X			syslog(LOG_CRIT,
X				"permissions: [key %s] can't open include\n", s);
X			return;
X		}
X		while( fgets(buf, sizeof buf, fin) ) {
X			for(p=buf; *p; p++)
X				if( *p==' ' || *p=='\n' || *p=='#' || *p=='\t' ) {
X					*p = '\0';
X					break;
X				}
X			if(!*buf)
X				continue;
X#ifdef DEBUG
X			printf("file: %s '%s'\n", s, buf);
X#endif
X			grpcheck(buf, add);
X		}
X		fclose(fin);
X		break;
X	case '.':
X		grp = grplist;
X		while(*grp) {
X			if( glob_match(s+1, *grp, 0) )
X				grpusrmatch = add;
X			grp++;
X		}
X		break;
X	default:
X		if( !strcmp(s, usern) )
X			grpusrmatch = add;
X		break;
X	}
X}
X
X%}
X
X%token SYMBOL
X%start permlist
X
X%%
Xpermlist	: perm
X		| permlist '|' perm
X		;
X
Xperm		: ttylist ':' grouplist {
X			if(ttymatch!=-1 && grpusrmatch!=-1)
X				allow = ttymatch && grpusrmatch;
X			ttymatch = grpusrmatch = -1;
X		}
X		| '$' SYMBOL { newmap($2); } permlist
X		|
X		;
X
Xttylist		: tty
X		| ttylist ',' tty
X		;
Xgrouplist	: group
X		| grouplist ',' group
X		;
X
Xtty		: SYMBOL {
X			if(!ttyn)
X				ttymatch = 1;
X			else if( glob_match($1, ttyn, 0) )
X				ttymatch = 1;
X		}
X		;
X
Xgroup		: '+' SYMBOL	{ grpcheck($2, 1); }
X		| '-' SYMBOL	{ grpcheck($2, 0); }
X		;
X%%
END_OF_FILE
if test 7791 -ne `wc -c <'perms/perms.y'`; then
    echo shar: \"'perms/perms.y'\" unpacked with wrong size!
fi
# end of 'perms/perms.y'
fi
if test -f 'permtest/Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'permtest/Makefile'\"
else
echo shar: Extracting \"'permtest/Makefile'\" \(164 characters\)
sed "s/^X//" >'permtest/Makefile' <<'END_OF_FILE'
XOBJ = permtest.o ../perms/perms.o ../perms/glob_match.o
XCFLAGS = -g -DPERMS
X
Xpermtest:	$(OBJ)
X		$(CC) $(LDFLAGS) $(OBJ) -o permtest
X
Xclean:
X		$(RM) *~ *.o permtest
END_OF_FILE
if test 164 -ne `wc -c <'permtest/Makefile'`; then
    echo shar: \"'permtest/Makefile'\" unpacked with wrong size!
fi
chmod +x 'permtest/Makefile'
# end of 'permtest/Makefile'
fi
if test -f 'permtest/permtest.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'permtest/permtest.c'\"
else
echo shar: Extracting \"'permtest/permtest.c'\" \(1614 characters\)
sed "s/^X//" >'permtest/permtest.c' <<'END_OF_FILE'
X#include <sys/param.h>
X#include <sys/types.h>
X#include <sys/socket.h>
X#include <string.h>
X#include <stdio.h>
X#include <ctype.h>
X#include <netdb.h>
X#include <grp.h>
X#include <pwd.h>
X
Xchar *grpnames[NGROUPS+1];
Xint ngrps;
Xstruct passwd *pwd;
Xstruct group *grp;
Xint verbose;
X
Xextern int permcheck();
X
Xmain(argc, argv)
Xchar **argv;
X{
X	char *logn, *ttyn, *hostn;
X	struct hostent *hent;
X	int lp, i;
X
X	if(argc<4) {
X		fprintf(stderr, "usage: %s [-v] username ttyname hostname\n",
X			*argv);
X		exit(0);
X	}
X	if(!strcmp(argv[1], "-v")) {
X		argv++;
X		verbose=1;
X	}
X
X	logn = argv[1];
X	ttyn = argv[2];
X
X	setpwent();
X	pwd = getpwnam(logn);
X	if(!pwd) {
X		fprintf(stderr, "%s: unknown user\n", logn);
X		exit(0);
X	}
X
X	setgrent();
X	ngrps = 0;
X	if(!(grp=getgrgid(pwd->pw_gid))) {
X		fprintf(stderr, 
X			"%d: group id has no name\n", pwd->pw_gid);
X		exit(0);
X	}
X	grpnames[ngrps++] = strdup(grp->gr_name);
X	while( grp=getgrent() ) {
X		if(pwd->pw_gid == grp->gr_gid)
X			continue;
X		while(*grp->gr_mem) {
X			if( !strcmp(logn, *grp->gr_mem)) {
X				grpnames[ngrps++] = strdup(grp->gr_name);
X			}
X			grp->gr_mem++;
X		}
X	}
X	endgrent();
X	grpnames[ngrps] = NULL;
X
X	if(verbose) {
X		printf("%d group%s:", ngrps, (ngrps>1)?"s":"");
X		for(i=0; i<ngrps; i++)
X			printf(" %s", grpnames[i]);
X		printf("\n");
X	}
X
X	for( argv= &argv[3]; *argv; argv++) {
X		if((hent = gethostbyname(*argv)))
X			hostn = hent->h_name;
X		else
X			hostn = *argv;
X		lp = permcheck(logn, ttyn, grpnames, hostn);
X		if(verbose)
X			printf("user %s %spermitted on %s:%s\n",
X				logn, lp ? "":"not ", hostn, ttyn);
X		else
X			printf("%s: %s\n", hostn, lp ? "":"not");
X	}
X	exit(0);
X}
X
X
X
END_OF_FILE
if test 1614 -ne `wc -c <'permtest/permtest.c'`; then
    echo shar: \"'permtest/permtest.c'\" unpacked with wrong size!
fi
# end of 'permtest/permtest.c'
fi
echo shar: End of shell archive.
exit 0
--

SunOS 4.0.3: /usr/include/vm/as.h, Line 44      | Theo de Raadt
SunOS 4.1.1: /usr/include/vm/as.h, Line 49      | deraadt@cpsc.ucalgary.ca
Is it a typo? Should the '_'  be an 's'?? :-)   | deraadt@cpsc.ucalgary.ca