Newsgroups: comp.sources.unix From: kinch@julian.uwo.ca (Dave Kinchlea) Subject: v29i053: suadi - (SU AUDIT) /bin/su replacement (V2), Part01/01 Message-id: <2.817522150.24012@gw.home.vix.com> Sender: unix-sources-moderator@gw.home.vix.com Approved: vixie@gw.home.vix.com Submitted-By: kinch@julian.uwo.ca (Dave Kinchlea) Posting-Number: Volume 29, Issue 53 Archive-Name: suadi/part01 From the supplied man page: suadi [user [-c command]] or suadi -p DESCRIPTION Suadi is a program that combines the functionality of su(1) and script(1). The user who invokes this program is checked against a database (stored in /etc/passwd format), authenti- cated via passwd(1) semantics using crypt(3) (unless the envoking user is root) and then given a root shell (as determined in the suadi.passwd file) or a shell for the user on the command line (as specified in /etc/passwd ). Anything returned to the terminal is logged via syslog(3). The results, of course, should be monitored. OPTIONS -c command Rather than giving an interactive shell, this will run the equivalent of sh -c command for the user specified. -p This option allows an existing suadi user to change their password. The new password is checked using the libpass(2) pro-active password checker. #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # README # BEWARE # Makefile # README.UWO # VERSIONS # suadi.c # suadi.h # suadi.man # suadi.passwd # This archive created: Mon Nov 27 14:59:10 1995 export PATH; PATH=/bin:$PATH echo shar: extracting "'README'" '(1721 characters)' if test -f 'README' then echo shar: will not over-write existing file "'README'" else sed 's/^ X//' << \SHAR_EOF > 'README' X $Author: kinch $ X $Date: 1995/11/27 19:45:31 $ X $Id: README,v 1.2 1995/11/27 19:45:31 kinch Exp $ X $Source: /usr/src/usr.local/security/suadi/RCS/README,v $ X $Locker: $ X XOriginal work August 1994 by Dave Kinchlea Kudos Xto Reg Quinton for the idea and his help in Ximplementing this on the various platforms. X XMajor update: November 1995 by kinch X X Copyright (c) 1994 The University of Western Ontario. All rights X reserved. Redistribution is permitted provided that this notice is X preserved and that due credit is given to the authors as well as The X University of Western Ontario. X X Please send any modifications/bug fixes/requests/kudos or Xcomplaints to: kinch@julian.uwo.ca X XFrom the supplied man page: X X suadi [user [-c command]] X or X suadi -p X XDESCRIPTION X Suadi is a program that combines the functionality of su(1) X and script(1). The user who invokes this program is checked X against a database (stored in /etc/passwd format), authenti- X cated via passwd(1) semantics using crypt(3) (unless the X envoking user is root) and then given a root shell (as X determined in the suadi.passwd file) or a shell for the user X on the command line (as specified in /etc/passwd ). Anything X returned to the terminal is logged via syslog(3). The X results, of course, should be monitored. X XOPTIONS X -c command X Rather than giving an interactive shell, this will run X the equivalent of sh -c command for the user specified. X X -p This option allows an existing suadi user to change X their password. The new password is checked using the X libpass(2) pro-active password checker. X X X SHAR_EOF if test 1721 -ne "`wc -c < 'README'`" then echo shar: error transmitting "'README'" '(should have been 1721 characters)' fi fi # end of overwriting check echo shar: extracting "'BEWARE'" '(4281 characters)' if test -f 'BEWARE' then echo shar: will not over-write existing file "'BEWARE'" else sed 's/^ X//' << \SHAR_EOF > 'BEWARE' X $Author: kinch $ X $Id: BEWARE,v 1.9 1995/11/27 19:21:31 kinch Exp $ X $Source: /usr/src/usr.local/security/suadi/RCS/BEWARE,v $ X $Locker: $ X XOriginal work August 1994 by Dave Kinchlea XMajor additions: November 1995 -- kinch X X Copyright (c) 1994,1995 The University of Western Ontario. All rights X reserved. Redistribution is permitted provided that this notice is X preserved and that due credit is given to the authors as well as The X University of Western Ontario. X X X SU AUDIT: BEWARE X XThere are a few things to be concerned about with respect to Xsuadi: X X1) By necessity, suadi is a setuid program. This, in itself, if Xnot too much of a concern as the intention of the program is to Xprovide root access ;-( X X2) SYSLOGD must be given `records' to log, this means that suadi X*must* do some internal buffering of input to ensure proper records Xare logged. One MUST be sure that the buffer is large enough. Most Xshells read input one character at a time (CBREAK mode) and so there Xis not always a predefined line length limit to use and there is very Xlittle control with respect to the output arbitrary programs can Xgenerate. Experience has shown that syslog lines of large lengths can Xconfuse SYSLOGD, as such all records should be limited to a size of XMAX_SYSLOG_LEN (see suadi.h) and multiple records should be Xused when appropriate (each `record' is a newline terminated string) X X3) As with script, absolutely NO filtering of output is done. As such, Xmany of the lines written to SYSLOG will appear to contain garbage Xwhen in fact they contain terminal control characters (wrt prompts) Xand line editing control characters (typically backspace/delete Xcharacters). There is no known way to remove these characters while at Xthe same time guaranteeing that the important information is logged. XThis behaviour is identical to the way that script(1) works. X X4) This code works on BSD and SYSV systems. IT IS IMPERATIVE that one Xof BSD or SYSV be defined for compilation. There is a check in suadi.h Xto insure this, it uses the #error pre-processing macro. Not all Xcompilers can deal with this (specifically, pre-ansi compilers). X X5) The pseudo tty interface is different for each environment. The Xrelevant code is in getmaster() and getslave(). X X6) Controlling terminals are handled differently for SYSV and BSD. X X7) Termios is used over termio when possible. You CAN'T tell from Xsuadi.c which code is being used, look at suadi.h X X8) the MIPS compiler (umips 1.4.3) is confused with this code. We Xspeculate that there is a library mismatch but have not been able to Xtrack it down as yet. The long and the short of this is that the Xmaster process will *not* stop cannonical processing. This can be Xconfusing to the user but otherwise does not seriously effect the use Xof suadi. We have decided that it is not worth the extra effort to get Xthis to work with the mips machine. X X 8a) This has been fixed, in EP/IX 1.4.3 the OPOST mode bit in termio X does not seem to be honoured, it is necessary to completely clear X rtt.cc_oflags. X X9) NOTE: suadi should not be thought of as a `secure' su alternative. XThe auditing being done can be defeated -- easily if X is Xavailable. If you do not trust the people using suadi, they should not Xhave access to it! X X10) AIX 3.2.5 seems to have a race condition in the code where the Xprocess doing the logging tries to read from the shell's pipe before Xthe shell is ready to write. The logging process would then die but Xthe shell wouldn't realize it. A kludge around this has been put Xin. Just BEFORE the call to dooutput() a sleep(SLEEPTIME) is called X(SLEEPTIME is defined in suadi.h). I suspect that this time Xcould/should be tweaked depending on how busy your system is. X X11) suadi utilizes routines from libpass(3), a pro-active password Xchecker written by Reg Quinton. If you do not intend to install this Xlibrary, then you must define two functions: X X int configure(char *dummy) {return(0);} X int crack(char *dum1, char *dum2, char *dum3, int dum4) {return(0);} X XHowever, I believe that libpass.a is a usefull library, I suggest you Xget it (from ftp://ftp.uwo.ca/pub/unix/security/libpasswd.tar.Z) X X12) suadi updates the password field of /etc/suadi.passwd `on the fly' Xand uses lockf() to ensure locked access. X SHAR_EOF if test 4281 -ne "`wc -c < 'BEWARE'`" then echo shar: error transmitting "'BEWARE'" '(should have been 4281 characters)' fi fi # end of overwriting check echo shar: extracting "'Makefile'" '(4589 characters)' if test -f 'Makefile' then echo shar: will not over-write existing file "'Makefile'" else sed 's/^ X//' << \SHAR_EOF > 'Makefile' X# Makefile for suadi X# X# $Author: kinch $ X# $Id: Makefile,v 1.21 1995/11/01 14:44:56 kinch Exp $ X# $Source: /usr/src/usr.local/security/suadi/RCS/Makefile,v $ X# $Locker: $ X# X# Build and install things in the right places. X# X# Original work August 1994 by Dave Kinchlea X# X# Copyright (c) 1994 The University of Western Ontario. All rights X# reserved. Redistribution is permitted provided that this notice is X# preserved and that due credit is given to the authors as well as The X# University of Western Ontario. X# X# Start of Configuration Section X XSHELL= /bin/sh XENVIRONMENT= SYSV XOLD= /uwo/ccs/old XBIN= /uwo/ccs/bin XMANEXT= 8 XMAN= /uwo/ccs/man/man${MANEXT} XDIRS= $(MAN) $(BIN) XPROG= suadi XSRCS= $(PROG).c $(PROG).passwd $(PROG).h XHDRS= $(PROG).h XOBJS= $(PROG).o XLIB= -L/uwo/ccs/lib -lpass $(LIBS) XGET= co X X# When testing I often do 'make FUNCTION= OPTIMIZE=-g' to skip my X# setuid code and build something I can trace with dbx X XFUNCTION= -DSETUID XOPTIMIZE= -O ${FUNCTION} X#OPTIMIZE= -g ${FUNCTION} Xdebug= 0 X X# This assumes a BSD version of install, use 'make INSTALL=... install' to X# use your version of BSD install. If you don't know how to do that you X XINSTALL= ucbinstall X X# End of Global Configuration section. X Xall: X @echo "You must pick a platform, there is NO default" X @echo "Choose from: $(SYSTEMS)" X @echo "Or create your own!" X X# To get things out of RCS X X$(SRCS):; $(GET) $@ X X# To make an object from source (it is assumed that there is a X# corresponding header file for each source ) X X$(OBJS): $(PROG).c $(PROG).h X $(CC) $(CFLAGS) $(OPTIMIZE) -Ddebug=$(debug) -c $(PROG).c -D$(ENVIRONMENT) X X# Make the image(s) X X$(PROG): $(OBJS) Makefile X $(CC) $(CFLAGS) $(OPTIMIZE) -o $(PROG) -D$(ENVIRONMENT) \ X $(LDFLAGS) $(OBJS) $(LOCAL_OBJS) $(LIB) X X# Make some directories X X$(DIRS): X umask 022; mkdir -p $@ X X# Install things in the right places X# the program, setuid (remove setuid bit on old executable) X# the passwd database, readable only by root X# the man page X# this assumes a ucb compatible install X Xinstall: $(DIRS) $(SRCS) X -mv $(BIN)/$(PROG) $(OLD) X -chmod 0755 $(OLD)/$(PROG) X $(INSTALL) -s -c -m 4755 -o root $(PROG) $(BIN) X -mv $(MAN)/$(PROG).$(MANEXT) $(OLD) X $(INSTALL) -c -m 644 $(PROG).man $(MAN)/$(PROG).$(MANEXT) X @echo You need a /etc/suadi.passwd file. X X# And clean up after yourself. Assume *~ are emacs backup files and X# #*# files are emacs automatic backups. X Xclean: X $(RM) *.o $(PROG) *~ \#*\# X X# The following are supported enviornments XSYSTEMS= sunos, solaris, aix, irix, epix X# This seems to work: X# [2:05pm suncon] uname -a X# SunOS suncon.cc 4.1.3_U1 1 sun4c X# [2:05pm suncon] date X# Thu Sep 22 14:05:45 EDT 1994 X Xsunos: X make ENVIRONMENT=BSD \ X CC=gcc \ X CFLAGS="-ansi -fwritable-strings \ X -DNO_PWD_PROTO -DNO_GETPASS_PROTO \ X -DNO_STRERROR ${CFLAGS}" \ X OPTIMIZE="$(OPTIMIZE)" debug=$(debug) \ X LIBS="" \ X LDFLAGS=\ X $(PROG) X X# This seems to work: X# [2:21pm falcon] uname -a X# SunOS falcon.ccs.uwo.ca 5.3 Generic_Patch sun4m sparc X# [2:21pm falcon] date X# Thu Sep 22 14:21:53 EDT 1994 X Xsolaris: X make ENVIRONMENT=SYSV \ X CC=gcc \ X CFLAGS="-ansi -fwritable-strings\ X -DHAS_CRYPT_H -DNO_GETPASS_PROTO ${CFLAGS}" \ X OPTIMIZE="$(OPTIMIZE)" debug=$(debug) \ X LIBS="" \ X LDFLAGS="" $(PROG) X X# This one works now: X# [1:54pm julian] uname -a X# julian.uwo.ca julian.uwo.ca 1.4.3 UMIPS mips X# [1:54pm julian] date X# Mon Oct 3 13:54:37 EDT 1994 X# X# NOTE: There is still a problem with ICANNON being X# mistakenly on with the EPIX environment, but X# this works acceptably well for now. X Xepix: X make ENVIRONMENT=BSD \ X CC=/usr/bin/cc2.20 \ X CFLAGS="-systype bsd43 \ X -D__mips__ \ X -DNO_PWD_PROTO \ X -DNO_STRERROR \ X -DNO_TCGETATTR \ X -DNO_WAITPID \ X -DNO_GETPASS_PROTO ${CFLAGS}" \ X OPTIMIZE="$(OPTIMIZE)" debug=$(debug) \ X LIBS=""\ X LDFLAGS="" \ X $(PROG) X# X# This seems to work: X# [12:11pm xia] uname -a X# AIX xia 2 3 000065074100 X# [12:11pm xia] date X# Tue Oct 4 12:12:01 EDT 1994 X Xaix: X make ENVIRONMENT=BSD \ X CC=xlc langlvl=extended \ X CFLAGS="${CFLAGS} -DNO_PWD_QUOTA \ X -D__aix__ -D_ALL_SOURCE \ X -DNO_FGETPWENT" \ X OPTIMIZE="$(OPTIMIZE)" debug=$(debug) \ X LIBS="-lbsd" \ X LDFLAGS="" \ X $(PROG) Xirix: X make ENVIRONMENT=SYSV \ X CC=cc \ X CFLAGS="-xansi -fullwarn ${CFLAGS}" \ X OPTIMIZE="$(OPTIMIZE)" debug=$(debug) \ X LIBS="" \ X LDFLAGS="-delay_load" \ X $(PROG) SHAR_EOF if test 4589 -ne "`wc -c < 'Makefile'`" then echo shar: error transmitting "'Makefile'" '(should have been 4589 characters)' fi fi # end of overwriting check echo shar: extracting "'README.UWO'" '(5192 characters)' if test -f 'README.UWO' then echo shar: will not over-write existing file "'README.UWO'" else sed 's/^ X//' << \SHAR_EOF > 'README.UWO' X $Author: kinch $ X $Date: 1995/11/27 19:45:31 $ X $Id: README.UWO,v 1.10 1995/11/27 19:45:31 kinch Exp $ X $Source: /usr/src/usr.local/security/suadi/RCS/README.UWO,v $ X $Locker: $ X XOriginal work August 1994 by Dave Kinchlea Kudos Xto Reg Quinton for the idea and his help in Ximplementing this on the various platforms. X XMajor update: November 1995 by kinch X X Copyright (c) 1994 The University of Western Ontario. All rights X reserved. Redistribution is permitted provided that this notice is X preserved and that due credit is given to the authors as well as The X University of Western Ontario. X X Please send any modifications/bug fixes/requests/kudos or Xcomplaints to: kinch@julian.uwo.ca X X SU AUDIT: README X X As more and more systems get added to a computer network, more Xand more people are needed to administer the network. From time to Xtime, each of these people will need some form of root access to Xperform their duties. The standard command to do this, /bin/su, has Xthree flaws that this program (suadi) addresses: X X 1) /bin/su requires that the user invoking it know the `root' Xpassword; It is felt that knowledge of the `root' password should be Xlimited to as few people as possible (for justification of this Xstatement, refer to the document:"UNIX Policies and Procedures Manual" Xavailable on-line (on ITS systems) from: X ftp.uwo.ca:~ftp/doc/unix-policy.[ps|text]). X X 2) Many flavours of UNIX only allow members of the `wheel' Xgroup to su to root, unfortunately this cannot be relied upon on a Xtruly heterogeneous network (like the one ITS administers) nor is that X`policy' right for all environments; It also doesn't scale well if you Xwant to use NIS, or something similar. X X 3) /bin/su has no facility to audit its use, beyond a single Xentry in a log file (usually SYSLOG). It is desirable to monitor X(especially new and/or vendor supplied) users who are working with Xroot privileges. The desire to monitor does not necessarily stem from Xan inherent distrust of users, rather out of a desire to ensure that Xusers are performing tasks that require root access in as safe and Xsecure an environment as possible as well as for as short a time as Xpossible. X X X There does not exist such a tool as of this writing (at least, Xone has not been heard of locally) and so suadi has been designed for Xthe task. The intention is to provide a database of users who are Xallowed to gain root access (using the standard passwd file syntax and Xsemantics) each with their own password. If the user who invokes suadi Xis in the database, they will be prompted for a password and if the Xauthentication process passes, the user will be given a *standard* Xroot shell and *all actions performed as root will be logged to XSYSLOG*. X X X suadi is simply script(1) modified to perform authentication Xas well as to do logging to the SYSLOGD rather than to a file. Some Xcode from su(1) has also been borrowed (specifically the code to Ximplement the `su -' command --- this provides a complete login Xenvironment as opposed to a modified user environment; ie: $HOME == X~root). X X The modifications to log to SYSLOGD are simple and portable Xacross all systems (via the syslog() system function) as is the modifications Xto provide authentication. In fact, the only BSD vs SYSV problems are Xwith pseudo ttys and controlling terminals. X X *NOTE*: suadi should not be thought of as a `secure' su Xalternative. The auditing being done can *very* easily be defeated. XIf you do not trust the people using suadi, they should not have Xaccess to it! X X X suadi.h contains many customizable values (default file names, Xbuffer sizes etc) and MUST BE edited (at least reviewed) before Xcompiling. X X XFiles in this distribution: XBEWARE: This file describes problems/concerns both past and present XMakefile: A makefile for many machines XREADME: This file XVERSIONS: (slightly) edited output from rlog suadi.c suadi.h Xsuadi.c: C source code for suadi, if I did things right you X shouldn't need to make any modifications to this file (unless you are X porting to a new architecture, of course). Xsuadi.h: This should probably be suadi.config.h as there more X options in here than ls itself ;-( Xsuadi.man: Standard nroff man page Xsuadi.passwd: passwd stub, this file will not allow any access. X X XTO DO: X It might be a good idea to check to see that both the invoking Xuser AND the -u user are allowed access, currently (94/09/01) only the X-u user (which IS the invoking user by default) is checked. --- -u Xuser option removed as just a bad idea. (94/09/30) X X X Get controlling terminal to work on SYSV. --- DONE. (94/09/02 -kinch) X X Find/fix problem with EPIX 1.4.3 and apparent ICANNON Xbehaviour --- DONE (94/11/14 -kinch) X X Find/fix problem with AIX and lost processes. --- DONE (I Xthink ;-() (94/11/15) X X Add ability to su user (instead of su root) X Suadi now allows: suadi [user [-c command]] syntax, the Xauthentication is still done using /etc/suadi.passwd. Otherwise the Xsemantics are identical to /bin/su as mentioned in Solaris man Xpages. (95/11/01) X X Suadi now allows an existing suadi user to change their Xpassword: suadi -p (95/11/01) X X Get people to use it, hmmmmmmmmm. X X SHAR_EOF if test 5192 -ne "`wc -c < 'README.UWO'`" then echo shar: error transmitting "'README.UWO'" '(should have been 5192 characters)' fi fi # end of overwriting check echo shar: extracting "'VERSIONS'" '(8201 characters)' if test -f 'VERSIONS' then echo shar: will not over-write existing file "'VERSIONS'" else sed 's/^ X//' << \SHAR_EOF > 'VERSIONS' X XRCS file: RCS/suadi.c,v XWorking file: suadi.c Xhead: 1.27 Xbranch: Xlocks: strict Xaccess list: X kinch X reggers Xsymbolic names: Xcomment leader: " * " Xkeyword substitution: kv Xtotal revisions: 27; selected revisions: 27 Xdescription: X---------------------------- Xrevision 1.27 Xdate: 1995/11/27 18:46:49; author: kinch; state: Exp; lines: +5 -5 XWe really do want those messages from libpasswd. X---------------------------- Xrevision 1.26 Xdate: 1995/11/02 20:03:15; author: kinch; state: Exp; lines: +77 -50 XImplemented NoLogging option for when being called by root with a X-c command option in use. X---------------------------- Xrevision 1.25 Xdate: 1995/11/02 19:49:25; author: kinch; state: Exp; lines: +12 -14 XThis removes all the time and user stuff from syslog reports X---------------------------- Xrevision 1.24 Xdate: 1995/11/02 15:33:04; author: kinch; state: Exp; lines: +19 -25 XLots of changes to the way things are logged, a little less noisy is all. XAlso cleaned up some formatting on error strings and removed -q option Xfrom getopts() call. X XRemoved existence of time_t tvec var, as time is no longer in the syslog Xmessage this is an unnecessary var. X---------------------------- Xrevision 1.23 Xdate: 1995/11/02 14:55:44; author: kinch; state: Exp; lines: +46 -28 XHad some problems under aix, some changes are debug attempts X(ie: renaming rootshell to rotshell to help my confusion) while Xthe copying of static info into global variables is to help aix Xwhich loses its static area somehow (though I couldn't determine how, probably Xin the fork()) X---------------------------- Xrevision 1.22 Xdate: 1995/11/01 14:46:09; author: kinch; state: Exp; lines: +5 -5 XFixed spelling error in setgid error message Xchanged default logging of ttyoutput to LOG_DEBUG X---------------------------- Xrevision 1.21 Xdate: 1995/10/30 22:50:30; author: kinch; state: Exp; lines: +15 -11 XCleaned up unexpected close procedure; Xused /uwo/ccs/share/etc/libpass.rc rather than /etc/libpass.rc; X XHandle case where no shell found in /etc/passwd: this means use X/bin/sh X---------------------------- Xrevision 1.20 Xdate: 1995/10/30 16:11:35; author: kinch; state: Exp; lines: +3 -7 XRemoved -q option, I don't think it should be there at all. X---------------------------- Xrevision 1.19 Xdate: 1995/10/27 14:20:15; author: kinch; state: Exp; lines: +84 -28 XAdded support for suadi user, suadi -c user command and suadi user -c command. XTested under solaris and epix only. X` Xthe suadi -c user command will be undocumented but it will exec(command) Xas user rather than passing command to the shell. This can have strange Xconsequences (ie: suadi -c kinch ls will fail as ls will not be a command Xthat is found, no $PATH. instead: suadi -c kinch /bin/ls but even that Xwill not produce the expected results as the buffering will be lost) X---------------------------- Xrevision 1.18 Xdate: 1995/10/24 22:34:58; author: kinch; state: Exp; lines: +239 -11 XTwo things: cleaned up the global variables, moved them local to main X(or whereever) when that was possible, moved the others into this file Xso that they are visible. X XAdded -p change_password support. This will edit the /etc/suadi.passwd Xfile on-the-fly XThis now uses libpass.a's configure and crack routines so that the Xresulting passwords will be pro-actively strong. X Xlibpass could be better, it would be nice to know WHY something Xfailed crack -- more work to do. X---------------------------- Xrevision 1.17 Xdate: 1995/04/20 17:29:17; author: kinch; state: Exp; lines: +13 -7 XEPIX seems to have problems with blocking I/O from stdin. It doesn't block ;-( XIn order to stop runaway suadi processes on julian I have implemented Xselect calls, seems to work well on julian and solaris, I have not Xtried it on sunos4/sunos3/irix or aix X---------------------------- Xrevision 1.16 Xdate: 1994/11/14 18:33:24; author: kinch; state: Exp; lines: +11 -8 XOn AIX there is an apparent race-condition in which the reader of the Xnew shell tries too quickly to read and fails, the new shell process has Xno way of knowing this occured and seems lost (but is still active and most Xlikely with setuid(0) privs!). I put a sleep(SLEEPTIME) just before the call Xto dooutput() hoping to allow enough time for doshell() to complete. Seems Xto work (SLEEPTIME is defined in suadi.h) X---------------------------- Xrevision 1.15 Xdate: 1994/11/14 15:21:39; author: kinch; state: Exp; lines: +6 -6 XChanged to sections to get mips RAW mode to work. fixtty(): added Xline to remove all Output processing (shouldn't be necessary if not OPOST Xbut apparently it is on mips X XChanged logic on doinput routine to stop on >= 0 instead of > 0 X Xthis fixes mips problems, haven't tested it on other platforms yet X Xkinch X---------------------------- Xrevision 1.14 Xdate: 1994/10/04 15:40:45; author: kinch; state: Exp; lines: +12 -4 XAdded NO_FGETPWENT CPP macro to deal with machines that dont Xhave fgetpwent(FILE *) and, instead, need to use setpwfile(char *). XCurrently, this is the case solely for AIX X---------------------------- Xrevision 1.13 Xdate: 1994/09/27 15:30:59; author: kinch; state: Exp; lines: +12 -8 XAdded Copyright notice X---------------------------- Xrevision 1.12 Xdate: 1994/09/27 15:07:21; author: kinch; state: Exp; lines: +15 -15 XWrapped all syslog(LOG_DEBUG) statements around if (debug) conditionals Xwhich will be optimized away at compile time if debug=0 XThis was done at Reg's request. X---------------------------- Xrevision 1.11 Xdate: 1994/09/27 13:55:02; author: kinch; state: Exp; lines: +10 -20 XCleaned up rootpwd initialization, I just didn't like the looks of Xwhat I had done. Moved initialization to suadi.h, compile time initialization XThe code is slightly easier to follow. X---------------------------- Xrevision 1.10 Xdate: 1994/09/26 20:46:29; author: kinch; state: Exp; lines: +9 -9 XJust changed some pointer magic, solaris is much pickyer than sunos X---------------------------- Xrevision 1.9 Xdate: 1994/09/26 19:38:12; author: kinch; state: Exp; lines: +55 -12 XFixes to make the setuid stuff work properly. Added new static variables Xin doshell() for putenv() calls, added proper checking for root information Xin authorize_user() (information now found in authfilenm, or use XROOT* defines. X---------------------------- Xrevision 1.8 Xdate: 1994/09/22 17:57:01; author: reggers; state: Exp; lines: +7 -7 Xgod damn mips X---------------------------- Xrevision 1.7 Xdate: 1994/09/22 17:52:09; author: reggers; state: Exp; lines: +34 -24 XBSD has setenv, SYSV has putenv. Pulled out fancy stuff where we pass Xa user name. Will add some of that back but use it to go to that user Xnot to authenticate by that user. X---------------------------- Xrevision 1.6 Xdate: 1994/09/22 14:23:05; author: kinch; state: Exp; lines: +14 -13 XCleaned up comments XReady for release? X---------------------------- Xrevision 1.5 Xdate: 1994/09/21 13:34:33; author: kinch; state: Exp; lines: +63 -29 XJust added the rcs constants (Author, Id and Locked) X---------------------------- Xrevision 1.4 Xdate: 1994/09/02 18:47:55; author: kinch; state: Exp; lines: +1 -1 XMoved setpgrp() call to BEFORE open(slavepty) call in getslave(), Xthis allows for a controlling terminal. X---------------------------- Xrevision 1.3 Xdate: 1994/09/02 18:00:30; author: kinch; state: Exp; lines: +66 -39 XAdded putenv() call to set $HOME, XAdded syslog support: if debug LOG_LOCAL3|LOG_PID|LOG_DEBUG Xelse LOG_AUTH|LOG_PID|LOG_INFO configurable using the XSYSLOG_FACILITY SYSLOG_OPTIONS and SYSLOG_PRIORITY defines X XAdded strcpy(username,Getlogin()) to save static user Xinformation returned (Irix 5.2 share static area amongst Xgetpwuid and fgetpwent functions) X XDEBUG statements now send to syslog(LOG_DEBUG), no Xfiles now opened. X---------------------------- Xrevision 1.2 Xdate: 1994/09/01 16:41:52; author: kinch; state: Exp; lines: +32 -11 XSet ISIG flag on master (ignore signals), prevents user from killing Xthe process accidentally. X XAdded setuid code (in doshell) X---------------------------- Xrevision 1.1 Xdate: 1994/08/31 19:48:20; author: kinch; state: Exp; XInitial revision X============================================================================= SHAR_EOF if test 8201 -ne "`wc -c < 'VERSIONS'`" then echo shar: error transmitting "'VERSIONS'" '(should have been 8201 characters)' fi fi # end of overwriting check echo shar: extracting "'suadi.c'" '(22689 characters)' if test -f 'suadi.c' then echo shar: will not over-write existing file "'suadi.c'" else sed 's/^ X//' << \SHAR_EOF > 'suadi.c' X/* X $Author: kinch $ X $Date: 1995/11/27 18:46:49 $ X $Id: suadi.c,v 1.27 1995/11/27 18:46:49 kinch Exp $ X $Source: /usr/src/usr.local/security/suadi/RCS/suadi.c,v $ X $Locker: $ X X Original work August 1994 by Dave Kinchlea X X Copyright (c) 1994 The University of Western Ontario. All rights X reserved. Redistribution is permitted provided that this notice is X preserved and that due credit is given to the authors as well as The X University of Western Ontario. X X It should be no surprise that much of this code is stolen from: 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 X static char copyright[] = X "@(#) Copyright (c) 1980 Regents of the University of California.\n\ X All rights reserved.\n"; X static char sccsid[] = "@(#)script.c 5.4 (Berkeley) 11/13/85"; X/* script 5..4 is the base for suadi */ X static char rcsid[] = "$Id: suadi.c,v 1.27 1995/11/27 18:46:49 kinch Exp $"; X#endif /* not lint */ X X#include "suadi.h" X/* GLOBAL variable section -- ugg but that's what happens when you steal code */ X X/* This is the default root password entry, in case root is not found X in the authfilenm. X { name, pass, uid, gid, quota, comment, gecos, dir, shell } */ X Xstruct passwd *supass, rootpwd = { X ROOTNAME, "", ROOTUID, ROOTGID, X#ifndef NO_PWD_QUOTA X 0, "", X#endif X"", ROOTDIR, ROOTSHELL X }; X X/* We need the following in case a root entry IS found in authfilenm. */ X Xchar rootdir[NAME_MAX]=ROOTDIR, rootshell[NAME_MAX]=ROOTSHELL, rootname[NAME_MAX]=ROOTNAME; X Xchar *user=NULL; Xchar progname[NAME_MAX]; Xchar username[NAME_MAX]; /* Needed by some to store getpwuid() information */ Xchar line[12] = "/dev/ptyXX"; /* Old ugly way of getting a pty */ Xint NoLogging=0; /* If true, then no logging of tty output will be done */ Xint master=0; Xint slave=0; Xint child=0; Xint subchild=0; Xstruct termios tt; Xstruct winsize win; X X#ifdef SYSV X char *slavename; X#endif X Xmain(int argc, char *argv[]) X{ X extern char *optarg; X extern int optind; X int ch; X int cmdflg=0; /* A command is to be executed */ X int logflg=0; /* Override the nologging feature */ X int passflg=0; /* password option? */ X X X strcpy(progname,argv[0]); X while ((ch = getopt(argc, argv, "lpc")) != EOF) X switch((char)ch) { X case 'c': X cmdflg++; X break; X case 'l': X logflg++; X break; X case 'p': X passflg++; X break; X case '?': X default: X USAGE; X exit(1); X } X argc -= optind; X argv += optind; X X /* Initialize syslog */ X X openlog(progname,SYSLOG_OPTIONS,SYSLOG_FACILITY); X setlogmask(LOG_UPTO(SYSLOG_PRIORITY)); X X /* Who is this? (needed for authentication) */ X X strcpy(username,Getlogin()); /* getuid() */ X user = username; X X if (user == NULL) { X syslog(LOG_NOTICE,"Unknown user attempting access"); X fprintf(stderr,"I can't figure out who you are, quitting\n"); X exit(BAD_USER); X } X X /* Before we do anything else, authenticate the user -- unless we are already root*/ X X if ((ch=authenticate_user((const char*)user))!= SUCCESS) { X fprintf(stderr,"Oops .. cannot authenticate!\n"); X exit(ch); X } X /* Ok, This person IS authenticated, let syslog know. */ X syslog(LOG_INFO,"Successful attempt, %s on %s", X user, ttyname(2)); X X if (passflg) { X /* This user would like to change their password */ X exit(change_passwd((const char*)user)); X X } X X /* This is the normal case. */ X X if (argc >= 1) { X /* we have a username, fill in suuser (UID) */ X /* We will have to setpwfile for those that need it. */ X#ifdef NO_FGETPWENT X setpwfile("/etc/passwd"); X#endif X setpwent(); X if ((supass = getpwnam(argv[0])) == NULL) { X syslog(LOG_ERR,"Can't su to %s: no such user",argv[0]); X fprintf(stderr,"Can't su to %s: no such user\n",argv[0]); X exit(127); X } X rootpwd.pw_uid = supass->pw_uid; X rootpwd.pw_gid = supass->pw_gid; X strcpy(rootname,supass->pw_name); X rootpwd.pw_name = rootname; X strcpy(rootdir, supass->pw_dir); X rootpwd.pw_dir = rootdir; X strcpy(rootshell,(*supass->pw_shell == '\0' ? "/bin/sh":supass->pw_shell)); X rootpwd.pw_shell = rootshell; X if (debug) fprintf(stderr,"rootpwd= %s:%d:%d:%s:%s:%s\n", rootpwd.pw_name, rootpwd.pw_uid, rootpwd.pw_gid, rootpwd.pw_dir, rootpwd.pw_shell); X argc--; X argv++; X } X X if (argc >= 1) { X { X char command[MAX_SYSLOG_LEN-sizeof("Running command: %s as user %s")]; X char *ptr; X int i; X X if (cmdflg) { X fprintf(stderr,"ah ha!\n"); X strcpy(rootpwd.pw_shell,argv[0]); X } X ptr=command; X for (i=0; i= (int) getuid()) && (!logflg)) { X syslog(LOG_INFO, "Logging turned off for this run"); X NoLogging++; X } X syslog(LOG_INFO,"Running command: %s as user %s",command,rootpwd.pw_name); X } X } X if (!NoLogging) { X getmaster(); X fixtty(); X } X X (void) signal(SIGCHLD, finish); X (void) signal(SIGBUS, finish); X (void) signal(SIGSEGV, finish); X X if (!NoLogging) { X child = fork(); X if (child < 0) { X syslog(LOG_ERR,"fork: %s",strerror(errno)); X perror("fork"); X fail(); X } X if (child == 0) { X subchild = child = fork(); X if (child < 0) { X syslog(LOG_ERR,"fork: %s",strerror(errno)); X perror("fork"); X fail(); X } X if (child) { X /* This is the second process, it reads X input from master pipe (fed by starting process), outputs it X to */ X#ifdef __aix__ X sleep(SLEEPTIME); X#endif X dooutput(); X } else { X doshell(argc,argv); X } X } X /* If here, we are the starting process. All our job is IPC for the X other two processes.*/ X doinput(); X } else { X doshell(argc,argv); X } X} X Xvoid Xdoinput(void) X{ X char ibuf[BUFSIZE]; X int cc=0; X fd_set readfd; X X FD_SET(0, &readfd); X (void) close(1); /* probably unnecessary, but no harm done */ X while (cc >= 0) { X select(1,&readfd,NULL,NULL,NULL); X (cc = read(0, ibuf, BUFSIZE)) != 0; X (void) write(master, ibuf, cc); X } X done(); X} X Xvoid Xfinish(int dummy) X{ X int status; /* We can get away with this, because we dont care about status */ X X#ifdef NO_WAITPID X int pid,die=0; X while ((pid = wait(&status, WNOHANG, 0)) > 0) X if (pid == child) X die = 1; X X if (die) X done(); X#else X if (waitpid(child,(int *)&status, WNOHANG) == child) X done(); X#endif X if (debug) syslog(LOG_DEBUG,"Caught Signal\n"); X} X Xvoid Xdooutput(void) X{ X char obuf[BUFSIZE+1]; X int cc; X X (void) close(0); X X if (debug) syslog(LOG_DEBUG,"In dooutput"); X for (;;) { X cc = read(master, obuf,sizeof(obuf)); X if (cc < 0) { X if (debug) syslog(LOG_DEBUG,"EOF read on master, bye bye"); X break; X } else if (cc > 0) { X (void) write(1, obuf, cc); X logit(obuf,cc); X } /* else (cc > 0) */ X } /* for (;;) */ X done(); X} X Xvoid X doshell(int argc, char *argv[]) X{ X /* The following MUST be static (according to putenv, probably not really X a concern as this function is never left, still ...) */ X static char rothome[NAME_MAX]; X static char rotuser[NAME_MAX]; X static char rotlog[NAME_MAX]; X static char rotshell[NAME_MAX]; X X if (debug) syslog(LOG_DEBUG,"In doshell\n"); X X if (!NoLogging) { X (void) close(master); X getslave(); X if (dup2(slave, 0)<0){ X syslog(LOG_ERR,"Dup2, stdin: %s",strerror(errno)); X perror("Dup2, stdin"); X fail(); X } X if (dup2(slave, 1)<0){ X syslog(LOG_ERR,"Dup2, stdout: %s",strerror(errno)); X perror("Dup2, stdout"); X fail(); X } X if (dup2(slave, 2)<0){ X syslog(LOG_ERR,"Dup2, stderr: %s",strerror(errno)); X perror("Dup2, stderr"); X fail(); X } X (void) close(slave); X } X if (debug) syslog(LOG_DEBUG,"About to change identity"); X#ifdef SETUID X if (rootpwd.pw_uid != 0) { X if (setuid(0) < 0) { X syslog(LOG_ERR,"setuid failed: %s",strerror(errno)); X perror("suadi: setuid"); X fail(); X } X } X if (setgid(rootpwd.pw_gid) < 0) { X syslog(LOG_ERR,"setgid failed: %s",strerror(errno)); X perror("suadi: setgid fail"); X fail(); X } X if (initgroups(user,rootpwd.pw_gid) < 0) { X fprintf(stderr,"suadi: initgroups failed\n"); X syslog(LOG_ERR,"initgroups failed"); X fail(); X } X if (setuid(rootpwd.pw_uid) < 0) { X syslog(LOG_ERR,"setuid failed: %s",strerror(errno)); X perror("suadi: setuid"); X fail(); X } X#endif X X /* Ensure all users have same environment */ X#ifdef __mips__ X setenv("HOME",rootpwd.pw_dir,1); X setenv("USER",rootpwd.pw_name,1); X setenv("LOGNAME",rootpwd.pw_name,1); X setenv("SHELL",rootpwd.pw_shell,1); X#else X sprintf(rothome,"HOME=%s",rootpwd.pw_dir); X putenv(rothome); X sprintf(rotuser,"USER=%s",rootpwd.pw_name); X putenv(rotuser); X sprintf(rotlog,"LOGNAME=%s",rootpwd.pw_name); X putenv(rotlog); X sprintf(rotshell,"SHELL=%s",rootpwd.pw_shell); X putenv(rotshell); X if (debug) { X syslog(LOG_DEBUG,"%s,%s,%s,%s",rothome,rotuser,rotlog,rotshell); X fprintf(stderr,"%s,%s,%s,%s\n",rothome,rotuser,rotlog,rotshell); X } X#endif X closelog(); /* finally, close the syslog file so that the shell doesn't accidentally use it */ X if (argc >= 1) { X argv--; X execv(rootpwd.pw_shell, argv); X } else { X if (NoLogging) { X fprintf(stderr, "This shouldn\'t happen,\n\ XNoLogging should only be possible with the -c command option\n\ XQuitting\n"); X syslog(LOG_ERR | SYSLOG_FACILITY, "This shouldn\'t happen,\n\ XNoLogging should only be possible with the -c command option\n\ XQuitting\n"); X fail(); X } X execl(rootpwd.pw_shell, rootpwd.pw_shell, "-i", 0); X } X /* If here, execl failed. We have to be verbose because the initialization of syslog has been removed. */ X syslog(LOG_ERR|SYSLOG_FACILITY,"%s: exec of shell failed (%s): %s",progname,rootpwd.pw_shell,strerror(errno)); X perror(rootpwd.pw_shell); X fail(); X} X Xvoid Xfixtty(void) X{ X struct termios rtt; X X if (debug) syslog(LOG_DEBUG,"In fixtty"); X rtt = tt; /* tt holds Master attributes */ X rtt.c_lflag = LFLAG; X rtt.c_oflag = LFLAG; X rtt.c_cc[VMIN] = MIN_CHAR_NUM; X rtt.c_cc[VTIME] = MIN_CHAR_TIME; X#ifdef NO_TCGETATTR X (void) ioctl(0,TCSETA,&rtt); X#else X (void) tcsetattr(0,TCSAFLUSH,&rtt); X#endif X if (debug) syslog(LOG_DEBUG,"Out fixtty"); X} X Xvoid Xfail(void) X{ X syslog(LOG_ERR,"Failure"); X (void) kill(0, SIGTERM); X done(); X} X Xvoid Xdone(void) X{ X X if (subchild) { X syslog(LOG_INFO,"\nfinished"); X (void) close(master); X (void) fflush(stderr); X } else { X#ifdef NO_TCGETATTR X (void) ioctl(0,TCSETA,(char *) &tt); X#else X (void) tcsetattr(0, TCSAFLUSH, &tt); X#endif X } X closelog(); X exit(0); X} X Xvoid Xgetmaster(void) X{ X X#ifdef SYSV X extern char *ptsname(int); X X if ((master = open("/dev/ptmx", O_RDWR)) == -1) { /* open master */ X syslog(LOG_ERR,"Can't open master pty"); X perror("Can't open master pty"); X fail(); X } X if (grantpt(master)) { /* change permission ofslave */ X syslog(LOG_ERR,"Can't grant pty"); X perror("Can't grant pty"); X fail(); X } X if (unlockpt(master)) { /* unlock slave */ X syslog(LOG_ERR,"Can't unlock pty"); X perror("Can't unlock pty"); X fail(); X } X if ((slavename = ptsname(master)) == NULL) { /* get name of slave */ X syslog(LOG_ERR,"ptsname fail, (this shouldn't happen)"); X perror("ptsname fail (this shouldn't happen)"); X fail(); X } X (void)tcgetattr(0,&tt); X (void)ioctl(0,TIOCGWINSZ,(char *)&win); X return; X#else /* The BSD way */ X char *pty, *bank, *cp; X struct stat stb; X X pty = &line[strlen("/dev/ptyp")]; X for (bank = "pqrstuvwxyz"; *bank; bank++) { X line[strlen("/dev/pty")] = *bank; X *pty = '0'; X if (stat(line, &stb) < 0) X break; X for (cp = "0123456789abcdef"; *cp; cp++) { X *pty = *cp; X master = open(line, O_RDWR); X if (master >= 0) { X char *tp = &line[strlen("/dev/")]; X int ok; X X /* verify slave side is usable */ X *tp = 't'; X ok = access(line, R_OK|W_OK) == 0; X *tp = 'p'; X if (ok) { X#ifdef NO_TCGETATTR X (void) ioctl(0,TCGETA,(char *)&tt); X#else X (void) tcgetattr(0,&tt); X#endif X (void) ioctl(0, TIOCGWINSZ, (char *)&win); X if (debug) syslog(LOG_DEBUG,"Successful Out getmaster"); X return; X } X (void) close(master); X } else { X if (debug) syslog(LOG_DEBUG,"Open of pty fail, %s: %s",line,strerror(errno)); X if (debug) perror(line); X } X } X } X syslog(LOG_ERR,"Out of pty's\n"); X fprintf(stderr, "Out of pty's\n"); X fail(); X#endif X} X Xvoid Xgetslave(void) X{ X int t; Xif (debug) syslog(LOG_DEBUG,"In getslave\n"); X#ifdef SYSV X (void) setpgrp(); X if ((slave = open(slavename, O_RDWR)) == -1) { /* open slave */ X syslog(LOG_ERR,"%s: %s",slavename,strerror(errno)); X perror(slavename); X fail(); X } X (void)ioctl(slave, I_PUSH, "ptem"); /* push ptem */ X (void)ioctl(slave, I_PUSH, "ldterm"); /* push ldterm */ X#else X t = open("/dev/tty", O_RDWR); X if (t >= 0) { X if (ioctl(t, TIOCNOTTY, (char *)0) == -1) { X syslog(LOG_ERR,"IOCTL, No controlling terminal: %s",strerror(errno)); X perror("IOCTL, No controlling terminal"); X } X (void) close(t); X if (debug) syslog(LOG_DEBUG,"Closed /dev/tty\n"); X } X X line[strlen("/dev/")] = 't'; X slave = open(line, O_RDWR); X if (slave < 0) { X syslog(LOG_ERR,"%s: %s",line,strerror(errno)); X perror(line); X fail(); X } X#endif X X#ifdef NO_TCGETATTR X (void) ioctl(slave,TCSETA,(char *)&tt); X#else X (void) tcsetattr(slave,TCSAFLUSH,&tt); X#endif X (void) ioctl(slave, TIOCSWINSZ, (char *)&win); X#ifdef TIOCSCTTY X (void) ioctl(slave, TIOCSCTTY, 0); X X X#endif /*TIOCSCTTY */ X X if (debug) syslog(LOG_DEBUG,"Done getslave"); X} X Xint Xauthenticate_user(const char *user) X{ X/* X The authentication is not based on /etc/passwd, rather on a specific file X for this program. The filename is: AUTHFILE (suadi.h). X X X As the passwd file is not /bin/passwd, we cannot use getpwent* family, Xinstead we use the fgetpwent() function. X*/ X X FILE *authfileptr; X char *password; X struct passwd *authpwd; X int fd; X char *authfilenm = AUTHFILE; X X X#ifdef NO_FGETPWENT X#define fgetpwent(x) getpwent() X setpwfile(authfilenm); X#else X if ((authfileptr = fopen(authfilenm,"r")) == NULL) { X syslog(LOG_ERR,"Authentication file open failed:%s",strerror(errno)); X perror(authfilenm); X return(SYSFAIL); X } X#endif X /* We assume that the root entry in authfilenm will be the first entry, X and so if the user entry is found first, there IS not root entry. X The test (below) will be if rootpwd.pw_name != NULL), so we ensure X that this is the default. */ X X rootpwd.pw_name = NULL; X X X while ((authpwd = fgetpwent(authfileptr)) != NULL) { X if (!strcmp(authpwd->pw_name,user)) break; X if (!strcmp(authpwd->pw_name,ROOTNAME)) { X /* We must make explicit copies, the info gets overwritten X the next call through. */ X strcpy(rootdir,authpwd->pw_dir); X strcpy(rootshell,authpwd->pw_shell); X rootpwd.pw_name = ROOTNAME; X rootpwd.pw_dir = rootdir; X rootpwd.pw_shell = rootshell; X rootpwd.pw_uid = authpwd->pw_uid; X rootpwd.pw_gid = authpwd->pw_gid; X } X } X X#ifndef NO_FGETPWENT X (void)fclose(authfileptr); X#endif X X if (authpwd) { X if (0!=getuid()) { X#ifdef __mips__ X fd = open("/dev/tty",O_RDWR); X if (fd < 0) { X perror("Can't open /dev/tty"); X syslog(LOG_ERR,"Can't open /dev/tty:%s",strerror(errno)); X return(SYSFAIL); X } X#endif X X password = getpass("Password:"); X if (strcmp(authpwd->pw_passwd,crypt(password,authpwd->pw_passwd)) != 0) { X syslog(LOG_INFO,"Bad Password, %s on %s", X user,ttyname(2)); X return(BAD_PASS); X } X } else { X strcpy(rootdir,authpwd->pw_dir); X strcpy(rootshell,authpwd->pw_shell); X rootpwd.pw_name = ROOTNAME; X rootpwd.pw_dir = rootdir; X rootpwd.pw_shell = rootshell; X rootpwd.pw_uid = authpwd->pw_uid; X rootpwd.pw_gid = authpwd->pw_gid; X } X } else { X fprintf(stderr,"%s is not authorized to use %s\n",user,progname); X syslog(LOG_ALERT,"Unauthorized attempt: %s on %s ", X user,ttyname(2)); X return(BAD_USER); X } X /* If we got here, we have a authenticated user. If rootpwd.pw_name is NULL X then a root entry was not found in the authfilenm. */ X if (! (rootpwd.pw_name)) { X rootpwd.pw_name = ROOTNAME; X } X X return(SUCCESS); X} X Xvoid Xlogit(char *inbuf, int incnt) X{ X/* Log all IO to syslog, this must be buffered to: X a) prevent long lines, X b) ensure reasonable formating X X This function has no return value as there does not seem X any point in telling the process that something failed X (it may well be a good idea to send mail to root that syslog X appears down, later ...) X*/ X X static char buffer[MAX_SYSLOG_LEN]; X static int cnt=0; X X for (;incnt>0;incnt--,inbuf++,cnt++) { X buffer[cnt] = *inbuf; X if (*inbuf == '\n' || cnt==MAX_SYSLOG_LEN) { X buffer[cnt+1] = '\0'; X syslog(LOG_DEBUG,"%s",buffer); X cnt = -1; /* This insures that cnt will be zero for start of next loop */ X } X } X} X X Xint Xchange_passwd(const char *user) X{ X /* X The authentication is not based on /etc/passwd, rather on a specific file X for this program. The filename is: AUTHFILE (suadi.h). X X X As the we wish to write to the password file, we cannot use the getpwent() X family, will have to roll my own. X X This routine will: flock(authfilenm), open(authfilenm,rw), find the entry X (we KNOW it is there or this function wouldn't get called), prompt for a new X password and replace the entry in authfilenm (we should be able to do this X without rewriting the entire file). X X */ X X X FILE *authfileptr; X char *password, *getsalt(void), authentry[1024], *ptr, *ptr2; X char name[10],pass[15],uid[10],gid[10],gecos[128],dir[128],shell[128]; X int fd,fd2, retval; X char *authfilenm = AUTHFILE; X X X if ((fd = open(authfilenm,O_RDWR)) < 0) { X syslog(LOG_ERR,"Authentication file open failed:%s",strerror(errno)); X perror(authfilenm); X return(SYSFAIL); X } X X /* Now we lockf() the file */ X X if ((retval = lockf(fd, F_TLOCK, 0)) != 0) { X fprintf(stderr,"%s locked by another user, try again later\n", authfilenm); X perror("lockf"); X close(fd); X return(LOCKED); X } X X /* but we really want a file pointer not a file descriptor */ X X if ((authfileptr = fdopen(fd,"r+")) == NULL) { X syslog(LOG_ERR,"Failed to fdopen(), strange:%s",strerror(errno)); X perror("Failed to fdopen(), strange"); X lockf(fd, F_ULOCK, 0); X close(fd); X return(SYSFAIL); X } X X while (fgets(authentry,sizeof(authentry),authfileptr) != NULL) { X if (!strncmp(authentry,user,strlen(user))) break; X } X X/* we need to strip out the passwd and gecos fields, might just as well X strip them all out, it might be usefull for something later on and X its reasonably cheap X*/ X X ptr = strchr(authentry,':'); X *ptr++ = '\0'; strcpy(name,authentry); X ptr2 = strchr(ptr,':'); X *ptr2++ = '\0'; strcpy(pass,ptr); ptr=ptr2; X ptr2 = strchr(ptr,':'); X *ptr2++ = '\0'; strcpy(uid,ptr); ptr=ptr2; X ptr2 = strchr(ptr,':'); X *ptr2++ = '\0'; strcpy(gid,ptr); ptr=ptr2; X ptr2 = strchr(ptr,':'); X *ptr2++ = '\0'; strcpy(gecos,ptr); ptr=ptr2; X ptr2 = strchr(ptr,':'); X *ptr2++ = '\0'; strcpy(dir,ptr); ptr=ptr2; X strcpy(shell,ptr2); X X#ifdef __mips__ X fd2 = open("/dev/tty",O_RDWR); X if (fd2 < 0) { X perror("Can't open /dev/tty"); X syslog(LOG_ERR,"Can't open /dev/tty:%s",strerror(errno)); X lockf(fd, F_ULOCK, 0); X if ((fclose(authfileptr) == EOF)) { X perror("Closing trouble"); X } X return(SYSFAIL); X } X#endif X if (configure("/uwo/ccs/share/etc/libpass.rc") != SUCCESS) { X syslog(LOG_ERR,"Couldn't configure libpass: %s",strerror(errno)); X fprintf(stderr,"Couldn't configure libpass: %s\n",strerror(errno)); X lockf(fd, F_ULOCK, 0); X if ((fclose(authfileptr) == EOF)) { X perror("Closing trouble"); X } X return(SYSFAIL); X } X Xgetpasswd: X strcpy(pass,getpass("New Password: ")); X password = getpass("Type again to confirm: "); X X if (strcmp(pass, password)) { X fprintf(stderr, "Oops ... Password Mismatch\n"); X lockf(fd, F_ULOCK, 0); X if ((fclose(authfileptr) == EOF)) { X perror("Closing trouble"); X } X return(SYSFAIL); X } X X if ((retval = crack(password,name,gecos,1)) != SUCCESS) { X fprintf(stderr,"Please try again (SIGQUIT to quit)\n"); X goto getpasswd; X } X strcpy(pass,crypt(password,getsalt())); X X /* If we got here, we have a authenticated user. If rootpwd.pw_name is NULL X then a root entry was not found in the authfilenm. */ X X sprintf(authentry,"%s:%s:%s:%s:%s:%s:%s", X name,pass,uid,gid,gecos,dir,shell); X X X if (fseek(authfileptr,-(strlen(authentry)),1) != 0) { X syslog(LOG_ERR,"Couldn't fseek backwards, password not changed"); X fprintf(stderr,"Couldn't fseek backwards, password not changed\n"); X lockf(fd, F_ULOCK, 0); X if ((fclose(authfileptr) == EOF)) { X perror("Closing trouble"); X } X return(SYSFAIL); X } X if (fprintf(authfileptr,"%s",authentry) < 0) { X syslog(LOG_ERR,"Couldn't write new password for %s: %s",user,strerror(errno)); X fprintf(stderr,"Couldn't write new password for %s: %s",user,strerror(errno)); X lockf(fd, F_ULOCK, 0); X if ((fclose(authfileptr) == EOF)) { X perror("Closing trouble"); X } X return(SYSFAIL); X } X syslog(LOG_INFO,"Changing Password for %s on %s ", X user,ttyname(2)); X X if ((lockf(fd, F_ULOCK, 0)!=0) || (fclose(authfileptr) == EOF)) { X perror("Closing trouble, AND otherwise done."); X } X return(SUCCESS); X} X X Xchar * Xgetsalt(void) X{ X /* Returns a random salt for crypt, rand() will do nicely as the X salt is not that important */ X X char salt[3]; X int num; X X srand((int)time(NULL)); X X for (;;) { X num = (rand() % 'z'); X if (num < '.') num += '0'; X if ((num > '9' && num < 'A') || X (num > 'Z' && num < 'a')) X continue; /* While this POTENIALLY could go forever, it isn't likely to */ X break; X } X salt[0] = num; X X for (;;) { X num = (rand() % 'z'); X if (num < '.') num += '0'; X if ((num > '9' && num < 'A') || X (num > 'Z' && num < 'a')) X continue; /* While this POTENIALLY could go forever, it isn't likely to */ X break; X } X salt[1] = num; X salt[2] = '\0'; X if (debug) fprintf(stderr,"The salt is: %s\n",salt); X return(salt); X} SHAR_EOF if test 22689 -ne "`wc -c < 'suadi.c'`" then echo shar: error transmitting "'suadi.c'" '(should have been 22689 characters)' fi fi # end of overwriting check echo shar: extracting "'suadi.h'" '(6281 characters)' if test -f 'suadi.h' then echo shar: will not over-write existing file "'suadi.h'" else sed 's/^ X//' << \SHAR_EOF > 'suadi.h' X/* X $Author: kinch $ X $Id: suadi.h,v 1.22 1995/11/02 20:18:52 kinch Exp $ X $Source: /usr/src/usr.local/security/suadi/RCS/suadi.h,v $ X $Locker: $ X X Original work August 1994 by Dave Kinchlea X with Kudos to Reg Quinton for the original X idea and much help along the way. X X Copyright (c) 1994 The University of Western Ontario. All rights X reserved. Redistribution is permitted provided that this notice is X preserved and that due credit is given to the authors as well as The X University of Western Ontario. X X This is the suadi header file. Any and all architectural modifications X should be made in this file (if at all possible) or better yet X with 'make CFLAGS=-DVAR=value'. X X*/ X X#ifndef SUADI_H X#define SUADI_H X X#define USAGE fprintf(stderr, "usage: %s [user [-c command]]\nor\n%s -p\n\ X\t %s: alone provides a standard root shell\n\ X\t %s user: provides a shell under user\'s uid\n\ X\t %s user -c command: will pass the \"-c command\" to the user\'s shell\n\ X\t %s -p: allows an existing %s user to change their password\n",\ Xprogname,progname,progname,progname,progname,progname,progname) X X X/* All too often, there is a necessity to differentiate between system v and X bsd environments. There is no standard way to determine this, we EXPECT X one of -DSYSV or -DBSD to be included, we will barf otherwise. X XNOTE: Not all compilers can deal with the #error preprocessor symbol */ X X#ifndef SYSV X#ifndef BSD X#error "One of SYSV or BSD must be defined" X#endif /* SYSV */ X#endif /* BSD */ X X/* GCC defines __STRICT_ANSI__, __STDC__ is the proper way to do this */ X X#ifdef __STRICT_ANSI__ X#ifndef __STDC__ X#define __STDC__ X#endif X#endif X X/* Necessary Include files */ X X#include X/* The following useful extension is used. */ Xextern FILE *fdopen(int, const char *); X#ifndef __mips__ X#include X#endif X#include X#include X#include X#include X#include X#ifdef __mips__ X#include X#else X#include X#endif X#include X#include X#include X#include X#include X#include X#include X X#ifdef SYSV X#include /* This is the streams stuff for SYSV pty */ X#endif X X#ifdef __aix__ X#include X#include X#define SLEEPTIME 3 /* time in seconds to sleep: This is necessary to subvert X a bad timing bug shown (so far) only in AIX 3.2.5 systems X The suspicion is that the code in dooutput() which is X simply a while read >= 0 returns -1 because the other X end of the pipe is not setup yet. You may have to adjust X SLEEPTIME depending on how busy your system is (how many X failed pseudo tty lookups). */ X X#endif X X#ifdef HAS_CRYPT_H X#include X#else X#define NO_CRYPT_PROTO X#endif X X/* Helpful macros for output */ X X#ifndef debug X#define debug 0 /* (non-zero is on) */ X#endif X X/* Useful CONSTANTS */ X X/* UIDs below MAGIUID will, when enoking suadi with a -c command option, X not use logging UNLESS the -l flag is given */ X#define MAGICUID 10 X X#define MAX_SYSLOG_LEN 256 /* Allow room for syslog formating */ X X/* Allow for a different AUTHFILE to be given at compile time, else Xuse one in current directory if being compiled with debug (its easier Xthat way) */ X X#ifndef AUTHFILE X#if debug == 0 X#define AUTHFILE "/etc/suadi.passwd" X#else X#define AUTHFILE "suadi.passwd" X#endif /* ifdef debug */ X#endif /* ifndef AUTHFILE */ X X/* BUFSIZ is a system constant, we use BUFSIZE so you can freely change it */ X X#define BUFSIZE BUFSIZ X X/* TTY modes */ X X#ifndef LFLAG X#define LFLAG 0 /* We want RAW mode */ X#endif X#ifndef MIN_CHAR_NUM X#define MIN_CHAR_NUM 1 /* minimum number of characters to read (stdin) */ X#endif X#ifndef MIN_CHAR_TIME X#define MIN_CHAR_TIME 10 /* Intercharacter timeout value */ X#endif X X/* The following are for syslog calls. If debug is defined, the X default syslog values are LOG_LOCAL3:LOG_DEBUG:(LOG_PID|LOG_NOWAIT) if X debug is not defined, the default values are: X LOG_AUTH:LOG_INFO:(LOG_PID|LOG_NOWAIT). These can, of course, be X changed at compile time via appropriate -D statements. */ X X#ifndef SYSLOG_FACILITY X#if debug == 0 X#define SYSLOG_FACILITY LOG_AUTH X#else X#define SYSLOG_FACILITY LOG_LOCAL3 X#endif /* ifdef debug */ X#endif /* ifndef SYSLOG_FACILITY */ X X#ifndef SYSLOG_OPTIONS X#define SYSLOG_OPTIONS LOG_PID|LOG_NOWAIT X#endif X X#ifndef SYSLOG_PRIORITY X#define SYSLOG_PRIORITY LOG_DEBUG X#endif /* ifndef SYSLOG_PRIORITY */ X X X/* Some older systems don't have strerror but usually provide sys_errlist */ X X#ifdef NO_STRERROR Xextern char *sys_errlist[]; Xextern int sys_nerr; X#define strerror(x) (sys_nerr >= errno? sys_errlist[x] : "Unknown error number") X#endif X X/* Return Values */ X X#define TRUE 1 X#define FALSE 0 X#define SUCCESS 0 X#define BAD_PASS 1 X#define BAD_USER 2 X#define SYSFAIL 3 X#define LOCKED 4 X#ifndef NAME_MAX X#define NAME_MAX 256 /* Max number of characters in a program name */ X#endif X X/* default root uid, gid, shell and directory, X overrriden by entry in AUTHFILE */ X X#define ROOTUID 0 X#define ROOTGID 0 X#define ROOTNAME "root" X#define ROOTSHELL "/bin/sh" X#define ROOTDIR "/" X X/* Function Prototype declaration section */ X X X#if !defined __STDC__ && !defined __mips__ X#define void X#else Xvoid doinput(void); Xvoid finish(int); Xvoid dooutput(void); Xvoid doshell(int, char **); Xvoid fixtty(void); Xvoid fail(void); Xvoid done(void); Xvoid getmaster(void); Xvoid getslave(void); Xint authenticate_user(const char *); Xvoid logit(char *,int); Xint change_passwd(const char *); X X/* libpass does not yet have a header file, so: */ Xint configure(char *); Xint crack(char *, char *, char *, int); X X#ifdef NO_PWD_PROTO Xstruct passwd *fgetpwent(FILE *); X#endif X X#ifdef NO_GETPASS_PROTO Xchar *getpass(const char *); X#endif X X#ifdef NO_CRYPT_PROTO Xchar *crypt(const char *, const char *); X#endif X X#endif /*__STDC__ && __mips__*/ X X/* Dont get too confused here, getpwuid returns a struct passwd *, X I am simply using that pointer to get at the login name. */ X X#define Getlogin() (getpwuid(getuid()))->pw_name X X#ifdef __mips__ Xstruct passwd *getpwuid(struct passwd *); Xstruct passwd *getuid(void); X#endif X X#endif /* SUADI_H */ SHAR_EOF if test 6281 -ne "`wc -c < 'suadi.h'`" then echo shar: error transmitting "'suadi.h'" '(should have been 6281 characters)' fi fi # end of overwriting check echo shar: extracting "'suadi.man'" '(2162 characters)' if test -f 'suadi.man' then echo shar: will not over-write existing file "'suadi.man'" else sed 's/^ X//' << \SHAR_EOF > 'suadi.man' X.\" $Author: reggers $ X.\" $Date: 1994/09/22 15:23:12 $ X.\" $Id: suadi.man,v 1.4 1994/09/22 15:23:12 reggers Exp kinch $ X.\" $Source: /usr/src/usr.local/security/suadi/RCS/suadi.man,v $ X.\" $Locker: kinch $ X.\" X.\" Original work August 1994 by Dave Kinchlea X.\" X.TH SUADI 8 "1 November 1995" X.SH NAME Xsuadi \- Allow and audit root access X.SH SYNOPSIS X.B suadi \%[user [-c command]] X.br Xor X.br X.B suadi -p X.SH DESCRIPTION X.PP X.I Suadi Xis a program that combines the functionality of X.I su(1) Xand X.I script(1). XThe user who invokes this program is checked against a database X(stored in X.B /etc/passwd Xformat), authenticated via X.I passwd(1) Xsemantics using X.I crypt(3) X(unless the envoking user is X.B root) Xand then given a root shell (as determined in the X.B suadi.passwd Xfile) or a shell for the user on the command line (as specified in X.B /etc/passwd X). Anything returned to the terminal is logged via X.IR syslog(3) . XThe results, of course, should be monitored. X.SH OPTIONS X.TP X.BR \-c\ command XRather than giving an interactive shell, this will run the equivalent Xof X.B sh -c command Xfor the X.B user Xspecified. X.TP X.BR \-p XThis option allows an existing X.B suadi Xuser to change their password. The new password is checked using the X.I libpass(2) Xpro-active password checker. X.SH FILES X.TP X.B /etc/suadi.passwd XA X.I passwd(5) Xstyle file containing all users who are allowed to use suadi. Each should Xhave a unique and non trivial password. X.SH BUGS X.PP XThe authentication used is X.B always Xthat of the envoking user as stored in X.I /etc/suadi.passwd Xthis can be confusing to historic users of X.I /bin/su user. XUnder X.B suadi Xsemantics, only those allowed to X.I su root Xare allowed to X.I su user Xand so knowledge of the X.B user's Xpassword is unnecessary. X X.PP XUnexpected results occur if no X.B user Xis specified when using the X.B -c command Xoption. X.SH SEE ALSO X.IR script(1) , X.IR su(1) X(particularly the X.B Solaris(tm) Xversion), X.IR passwd(1) , X.IR passwd(5) , X.IR libpass(2) . X.SH AUTHOR X.PP XDave Kinchlea . Information Technology Services, XThe University of Western Ontario. November 1995 X X SHAR_EOF if test 2162 -ne "`wc -c < 'suadi.man'`" then echo shar: error transmitting "'suadi.man'" '(should have been 2162 characters)' fi fi # end of overwriting check echo shar: extracting "'suadi.passwd'" '(54 characters)' if test -f 'suadi.passwd' then echo shar: will not over-write existing file "'suadi.passwd'" else sed 's/^ X//' << \SHAR_EOF > 'suadi.passwd' Xroot:*NOPWD*:0:1:Julian System Account,,,:/:/bin/tcsh SHAR_EOF if test 54 -ne "`wc -c < 'suadi.passwd'`" then echo shar: error transmitting "'suadi.passwd'" '(should have been 54 characters)' fi fi # end of overwriting check # End of shell archive exit 0 -- kinch for PGP key(s) Unix PGP Key fingerprint = 6F 36 6F 9D 79 16 DF 40 2B EC 18 5B 5C 6D 03 6F Home PGP Key fingerprint = 28 20 0E DF 27 17 80 C5 5D 04 8E 1B D0 9A A6 4C