lsu/ 2755 1625 12 0 5463052333 4671 lsu/Ed/ 2755 1625 12 0 5106523460 5217 lsu/Ed/Ed.SGI3_4 644 1625 12 526 5106523460 6447 #! /bin/sh n=0 for m in SUPERM LSUPERM LOG do n=`expr $n + 1` echo '/^\/\* begin' $m '\*\/\\\\$/,/^\/\* end' $m '\*\/\\\\$/c' echo '/* begin' $m '*/\\' j=0 for i in `sed -n ${n}p $1` do echo "buf[$j] = '$i';\\" j=`expr "$j" + 1` done echo "buf[$j] = '\\\\0';\\" echo '/* end' $m '*/\\' echo '.' done echo '1,$w' echo 'q' exit 0 lsu/Ed/Ed.UNICOS 644 1625 12 526 5106523460 6517 #! /bin/sh n=0 for m in SUPERM LSUPERM LOG do n=`expr $n + 1` echo '/^\/\* begin' $m '\*\/\\\\$/,/^\/\* end' $m '\*\/\\\\$/c' echo '/* begin' $m '*/\\' j=0 for i in `sed -n ${n}p $1` do echo "buf[$j] = '$i';\\" j=`expr "$j" + 1` done echo "buf[$j] = '\\\\0';\\" echo '/* end' $m '*/\\' echo '.' done echo '1,$w' echo 'q' exit 0 lsu/Ed/Ed.BSD4_3 644 1625 12 516 5106523460 6434 #! /bin/sh n=0 for m in SUPERM LSUPERM LOG do n=`expr $n + 1` echo '/^\/\* begin' $m '\*\/\\$/,/^\/\* end' $m '\*\/\\$/c' echo '/* begin' $m '*/\' j=0 for i in `sed -n ${n}p $1` do echo "buf[$j] = '$i';\\" j=`expr "$j" + 1` done echo "buf[$j] = '\\0';\\" echo '/* end' $m '*/\' echo '.' done echo '1,$w' echo 'q' exit 0 exit 0 lsu/Ed/Ed.UTS 644 1625 12 526 5106523460 6172 #! /bin/sh n=0 for m in SUPERM LSUPERM LOG do n=`expr $n + 1` echo '/^\/\* begin' $m '\*\/\\\\$/,/^\/\* end' $m '\*\/\\\\$/c' echo '/* begin' $m '*/\\' j=0 for i in `sed -n ${n}p $1` do echo "buf[$j] = '$i';\\" j=`expr "$j" + 1` done echo "buf[$j] = '\\\\0';\\" echo '/* end' $m '*/\\' echo '.' done echo '1,$w' echo 'q' exit 0 lsu/Ed/Ed.SGI3_5 644 1625 12 526 5106523460 6450 #! /bin/sh n=0 for m in SUPERM LSUPERM LOG do n=`expr $n + 1` echo '/^\/\* begin' $m '\*\/\\\\$/,/^\/\* end' $m '\*\/\\\\$/c' echo '/* begin' $m '*/\\' j=0 for i in `sed -n ${n}p $1` do echo "buf[$j] = '$i';\\" j=`expr "$j" + 1` done echo "buf[$j] = '\\\\0';\\" echo '/* end' $m '*/\\' echo '.' done echo '1,$w' echo 'q' exit 0 lsu/Ed/Ed.ROS3_3 644 1625 12 526 5106523460 6467 #! /bin/sh n=0 for m in SUPERM LSUPERM LOG do n=`expr $n + 1` echo '/^\/\* begin' $m '\*\/\\\\$/,/^\/\* end' $m '\*\/\\\\$/c' echo '/* begin' $m '*/\\' j=0 for i in `sed -n ${n}p $1` do echo "buf[$j] = '$i';\\" j=`expr "$j" + 1` done echo "buf[$j] = '\\\\0';\\" echo '/* end' $m '*/\\' echo '.' done echo '1,$w' echo 'q' exit 0 lsu/Ed/Ed.SGI2_3 644 1625 12 526 5106523460 6445 #! /bin/sh n=0 for m in SUPERM LSUPERM LOG do n=`expr $n + 1` echo '/^\/\* begin' $m '\*\/\\\\$/,/^\/\* end' $m '\*\/\\\\$/c' echo '/* begin' $m '*/\\' j=0 for i in `sed -n ${n}p $1` do echo "buf[$j] = '$i';\\" j=`expr "$j" + 1` done echo "buf[$j] = '\\\\0';\\" echo '/* end' $m '*/\\' echo '.' done echo '1,$w' echo 'q' exit 0 lsu/Ed/Ed.SYSV 644 1625 12 526 5106523460 6323 #! /bin/sh n=0 for m in SUPERM LSUPERM LOG do n=`expr $n + 1` echo '/^\/\* begin' $m '\*\/\\\\$/,/^\/\* end' $m '\*\/\\\\$/c' echo '/* begin' $m '*/\\' j=0 for i in `sed -n ${n}p $1` do echo "buf[$j] = '$i';\\" j=`expr "$j" + 1` done echo "buf[$j] = '\\\\0';\\" echo '/* end' $m '*/\\' echo '.' done echo '1,$w' echo 'q' exit 0 lsu/Ed/Ed.BSD4_2 644 1625 12 516 5106523460 6433 #! /bin/sh n=0 for m in SUPERM LSUPERM LOG do n=`expr $n + 1` echo '/^\/\* begin' $m '\*\/\\$/,/^\/\* end' $m '\*\/\\$/c' echo '/* begin' $m '*/\' j=0 for i in `sed -n ${n}p $1` do echo "buf[$j] = '$i';\\" j=`expr "$j" + 1` done echo "buf[$j] = '\\0';\\" echo '/* end' $m '*/\' echo '.' done echo '1,$w' echo 'q' exit 0 exit 0 lsu/Ed/Ed.DYNIX2_0 644 1625 12 516 5106523460 6712 #! /bin/sh n=0 for m in SUPERM LSUPERM LOG do n=`expr $n + 1` echo '/^\/\* begin' $m '\*\/\\$/,/^\/\* end' $m '\*\/\\$/c' echo '/* begin' $m '*/\' j=0 for i in `sed -n ${n}p $1` do echo "buf[$j] = '$i';\\" j=`expr "$j" + 1` done echo "buf[$j] = '\\0';\\" echo '/* end' $m '*/\' echo '.' done echo '1,$w' echo 'q' exit 0 exit 0 lsu/Ed/Ed.NPSN3 644 1625 12 526 5106523460 6360 #! /bin/sh n=0 for m in SUPERM LSUPERM LOG do n=`expr $n + 1` echo '/^\/\* begin' $m '\*\/\\\\$/,/^\/\* end' $m '\*\/\\\\$/c' echo '/* begin' $m '*/\\' j=0 for i in `sed -n ${n}p $1` do echo "buf[$j] = '$i';\\" j=`expr "$j" + 1` done echo "buf[$j] = '\\\\0';\\" echo '/* end' $m '*/\\' echo '.' done echo '1,$w' echo 'q' exit 0 lsu/Make/ 2755 1625 12 0 5463055411 5546 lsu/Make/Make.NPSN3 644 1625 12 3271 5106523461 7253 # # lsu -- local super user # # variables that tailor the program SUPERM="./.su" LSUPERM = "./.lsu" LOG="./.log" # this should not be changed; it names the system (see sysdep.h # for the meaning and other abbreviations) SYS = NPSN3 # this is a temp file used to edit the source E = Ed/Ed.$(SYS) # usual make stuff CFLAGS = -O -D$(SYS) DESTDIR = /usr/local/bin/ EXEC = su lsu nsu csu LIBS = RM = rm -f OBJECTS = lsu.o log.o pat.o perm.o syntax.o time.o tty.o util.o SOURCES = lsu.c log.c pat.c perm.c syntax.c time.y tty.y util.c all: $(EXEC) su: $(OBJECTS) $(CC) $(CFLAGS) -o su $(OBJECTS) $(LIBS) lsu: $(OBJECTS) $(CC) $(CFLAGS) -o lsu $(OBJECTS) $(LIBS) nsu: $(OBJECTS) $(CC) $(CFLAGS) -o nsu $(OBJECTS) $(LIBS) csu: $(OBJECTS) $(CC) $(CFLAGS) -o csu $(OBJECTS) $(LIBS) dtime: time.c $(CC) -o dtime $(DEBUG) $(CFLAGS) time.c dtty: tty.c lsu.h $(CC) -o dtty $(DEBUG) $(CFLAGS) tty.c pat.c $(OBJECTS): sysdep.h lsu.h # all this does is edit in the new definitions of SUPERM, LSUPERM, and LOG lsu.h: lsu.H fixup cp lsu.H lsu.h sh $E fixup | ed - lsu.h time.c: time.y yacc time.y; sed 's/yy/y1/g' y.tab.c > time.c tty.c: tty.y yacc tty.y; sed 's/yy/y2/g' y.tab.c > tty.c fixup: Makefile echo $(SUPERM) | sed 's/./& /g' > fixup echo $(LSUPERM) | sed 's/./& /g' >> fixup echo $(LOG) | sed 's/./& /g' >> fixup install: $(EXEC) -cp $(EXEC) $(DESTDIR) chown root $(DESTDIR)lsu $(DESTDIR)su $(DESTDIR)nsu $(DESTDIR)csu chmod 4755 $(DESTDIR)lsu $(DESTDIR)su $(DESTDIR)nsu $(DESTDIR)csu lint: time.c tty.c lsu.h lint -p -D$(SYS) lsu.c log.c pat.c perm.c syntax.c time.c tty.c util.c clean: $(RM) $(OBJECTS) y.tab.c clobber: $(RM) $(EXEC) $(OBJECTS) lsu.h time.c tty.c y.tab.c fixup dtime dtty lsu/Make/Make.ROS3_3 644 1625 12 3275 5106523461 7366 # # lsu -- local super user # # variables that tailor the program SUPERM="./.su" LSUPERM = "./.lsu" LOG="./.log" # this should not be changed; it names the system (see sysdep.h # for the meaning and other abbreviations) SYS = ROS3_3 # this is a temp file used to edit the source E = Ed/Ed.$(SYS) # usual make stuff CFLAGS = -O -D$(SYS) DESTDIR = /usr/local/bin/ EXEC = su lsu nsu csu LIBS = RM = rm -f OBJECTS = lsu.o log.o pat.o perm.o syntax.o time.o tty.o util.o SOURCES = lsu.c log.c pat.c perm.c syntax.c time.y tty.y util.c all: $(EXEC) su: $(OBJECTS) $(CC) $(CFLAGS) -o su $(OBJECTS) $(LIBS) lsu: $(OBJECTS) $(CC) $(CFLAGS) -o lsu $(OBJECTS) $(LIBS) nsu: $(OBJECTS) $(CC) $(CFLAGS) -o nsu $(OBJECTS) $(LIBS) csu: $(OBJECTS) $(CC) $(CFLAGS) -o csu $(OBJECTS) $(LIBS) dtime: time.c $(CC) -o dtime $(DEBUG) $(CFLAGS) time.c dtty: tty.c lsu.h $(CC) -o dtty $(DEBUG) $(CFLAGS) tty.c pat.c $(OBJECTS): sysdep.h lsu.h # all this does is edit in the new definitions of SUPERM, LSUPERM, and LOG lsu.h: lsu.H fixup cp lsu.H lsu.h sh $E fixup | ed - lsu.h time.c: time.y yacc time.y; sed 's/yy/y1/g' y.tab.c > time.c tty.c: tty.y yacc tty.y; sed 's/yy/y2/g' y.tab.c > tty.c fixup: Makefile echo $(SUPERM) | sed 's/./& /g' > fixup echo $(LSUPERM) | sed 's/./& /g' >> fixup echo $(LOG) | sed 's/./& /g' >> fixup install: $(EXEC) -cp $(EXEC) $(DESTDIR) chown root $(DESTDIR)lsu $(DESTDIR)su $(DESTDIR)nsu $(DESTDIR)csu chmod 4755 $(DESTDIR)lsu $(DESTDIR)su $(DESTDIR)nsu $(DESTDIR)csu lint: time.c tty.c lsu.h lint -phbac -D$(SYS) lsu.c log.c pat.c perm.c syntax.c time.c tty.c util.c clean: $(RM) $(OBJECTS) y.tab.c clobber: $(RM) $(EXEC) $(OBJECTS) lsu.h time.c tty.c y.tab.c fixup dtime dtty o $(SUPERM) | sed 's/./& /g' > fixup echo $(LSUPERM) | sed 's/./& /g' >> fixup echo $(LOG) | sed 's/./& /g' >> fixup install: $(EXEC) -cp $(EXEC) $(DESTDIR) chown root $(DESTDIR)lsu $(DESTDIR)su $(DESTDIR)nsu $(DESTDIR)csu chmod 4755 $(DESTDIR)lsu $(DESTDIR)su $(DESTDIR)nsu $(DESTDIR)csu lint: time.c tty.c lsu.h llsu/Make/Make.SGI2_3 644 1625 12 3301 5106523461 7332 # # lsu -- local super user # # variables that tailor the program SUPERM="./.su" LSUPERM = "./.lsu" LOG="./.log" # this should not be changed; it names the system (see sysdep.h # for the meaning and other abbreviations) SYS = SGI2_3 # this is a temp file used to edit the source E = Ed/Ed.$(SYS) # usual make stuff CFLAGS = -O -D$(SYS) DESTDIR = /usr/local/bin/ EXEC = su lsu nsu csu LIBS = -lcrypt RM = rm -f OBJECTS = lsu.o log.o pat.o perm.o syntax.o time.o tty.o util.o SOURCES = lsu.c log.c pat.c perm.c syntax.c time.y tty.y util.c all: $(EXEC) su: $(OBJECTS) $(CC) $(CFLAGS) -o su $(OBJECTS) $(LIBS) lsu: $(OBJECTS) $(CC) $(CFLAGS) -o lsu $(OBJECTS) $(LIBS) nsu: $(OBJECTS) $(CC) $(CFLAGS) -o nsu $(OBJECTS) $(LIBS) csu: $(OBJECTS) $(CC) $(CFLAGS) -o csu $(OBJECTS) $(LIBS) dtime: time.c $(CC) -o dtime $(DEBUG) $(CFLAGS) time.c dtty: tty.c lsu.h $(CC) -o dtty $(DEBUG) $(CFLAGS) tty.c pat.c $(OBJECTS): sysdep.h lsu.h # all this does is edit in the new definitions of SUPERM, LSUPERM, and LOG lsu.h: lsu.H fixup cp lsu.H lsu.h sh $E fixup | ed - lsu.h time.c: time.y yacc time.y; sed 's/yy/y1/g' y.tab.c > time.c tty.c: tty.y yacc tty.y; sed 's/yy/y2/g' y.tab.c > tty.c fixup: Makefile echo $(SUPERM) | sed 's/./& /g' > fixup echo $(LSUPERM) | sed 's/./& /g' >> fixup echo $(LOG) | sed 's/./& /g' >> fixup install: $(EXEC) -cp $(EXEC) $(DESTDIR) chown root $(DESTDIR)lsu $(DESTDIR)su $(DESTDIR)nsu $(DESTDIR)csu chmod 4755 $(DESTDIR)lsu $(DESTDIR)su $(DESTDIR)nsu $(DESTDIR)csu lint: time.c tty.c lsu.h lint -p -D$(SYS) lsu.c log.c pat.c perm.c syntax.c time.c tty.c util.c clean: $(RM) $(OBJECTS) y.tab.c clobber: $(RM) $(EXEC) $(OBJECTS) lsu.h time.c tty.c y.tab.c fixup dtime dtty lsu/Make/Make.SGI3_5 644 1625 12 3301 5106523461 7335 # # lsu -- local super user # # variables that tailor the program SUPERM="./.su" LSUPERM = "./.lsu" LOG="./.log" # this should not be changed; it names the system (see sysdep.h # for the meaning and other abbreviations) SYS = SGI3_4 # this is a temp file used to edit the source E = Ed/Ed.$(SYS) # usual make stuff CFLAGS = -O -D$(SYS) DESTDIR = /usr/local/bin/ EXEC = su lsu nsu csu LIBS = -lcrypt RM = rm -f OBJECTS = lsu.o log.o pat.o perm.o syntax.o time.o tty.o util.o SOURCES = lsu.c log.c pat.c perm.c syntax.c time.y tty.y util.c all: $(EXEC) su: $(OBJECTS) $(CC) $(CFLAGS) -o su $(OBJECTS) $(LIBS) lsu: $(OBJECTS) $(CC) $(CFLAGS) -o lsu $(OBJECTS) $(LIBS) nsu: $(OBJECTS) $(CC) $(CFLAGS) -o nsu $(OBJECTS) $(LIBS) csu: $(OBJECTS) $(CC) $(CFLAGS) -o csu $(OBJECTS) $(LIBS) dtime: time.c $(CC) -o dtime $(DEBUG) $(CFLAGS) time.c dtty: tty.c lsu.h $(CC) -o dtty $(DEBUG) $(CFLAGS) tty.c pat.c $(OBJECTS): sysdep.h lsu.h # all this does is edit in the new definitions of SUPERM, LSUPERM, and LOG lsu.h: lsu.H fixup cp lsu.H lsu.h sh $E fixup | ed - lsu.h time.c: time.y yacc time.y; sed 's/yy/y1/g' y.tab.c > time.c tty.c: tty.y yacc tty.y; sed 's/yy/y2/g' y.tab.c > tty.c fixup: Makefile echo $(SUPERM) | sed 's/./& /g' > fixup echo $(LSUPERM) | sed 's/./& /g' >> fixup echo $(LOG) | sed 's/./& /g' >> fixup install: $(EXEC) -cp $(EXEC) $(DESTDIR) chown root $(DESTDIR)lsu $(DESTDIR)su $(DESTDIR)nsu $(DESTDIR)csu chmod 4755 $(DESTDIR)lsu $(DESTDIR)su $(DESTDIR)nsu $(DESTDIR)csu lint: time.c tty.c lsu.h lint -p -D$(SYS) lsu.c log.c pat.c perm.c syntax.c time.c tty.c util.c clean: $(RM) $(OBJECTS) y.tab.c clobber: $(RM) $(EXEC) $(OBJECTS) lsu.h time.c tty.c y.tab.c fixup dtime dtty lsu/Make/Make.SYSV 644 1625 12 3267 5106523461 7223 # # lsu -- local super user # # variables that tailor the program SUPERM="./.su" LSUPERM = "./.lsu" LOG="./.log" # this should not be changed; it names the system (see sysdep.h # for the meaning and other abbreviations) SYS = SYSV # this is a temp file used to edit the source E = Ed/Ed.$(SYS) # usual make stuff CFLAGS = -O -D$(SYS) DESTDIR = /usr/local/bin/ EXEC = su lsu nsu csu LIBS = RM = rm -f OBJECTS = lsu.o log.o pat.o perm.o syntax.o time.o tty.o util.o SOURCES = lsu.c log.c pat.c perm.c syntax.c time.y tty.y util.c all: $(EXEC) su: $(OBJECTS) $(CC) $(CFLAGS) -o su $(OBJECTS) $(LIBS) lsu: $(OBJECTS) $(CC) $(CFLAGS) -o lsu $(OBJECTS) $(LIBS) nsu: $(OBJECTS) $(CC) $(CFLAGS) -o nsu $(OBJECTS) $(LIBS) csu: $(OBJECTS) $(CC) $(CFLAGS) -o csu $(OBJECTS) $(LIBS) dtime: time.c $(CC) -o dtime $(DEBUG) $(CFLAGS) time.c dtty: tty.c lsu.h $(CC) -o dtty $(DEBUG) $(CFLAGS) tty.c pat.c $(OBJECTS): sysdep.h lsu.h # all this does is edit in the new definitions of SUPERM, LSUPERM, and LOG lsu.h: lsu.H fixup cp lsu.H lsu.h sh $E fixup | ed - lsu.h time.c: time.y yacc time.y; sed 's/yy/y1/g' y.tab.c > time.c tty.c: tty.y yacc tty.y; sed 's/yy/y2/g' y.tab.c > tty.c fixup: Makefile echo $(SUPERM) | sed 's/./& /g' > fixup echo $(LSUPERM) | sed 's/./& /g' >> fixup echo $(LOG) | sed 's/./& /g' >> fixup install: $(EXEC) -cp $(EXEC) $(DESTDIR) chown root $(DESTDIR)lsu $(DESTDIR)su $(DESTDIR)nsu $(DESTDIR)csu chmod 4755 $(DESTDIR)lsu $(DESTDIR)su $(DESTDIR)nsu $(DESTDIR)csu lint: time.c tty.c lsu.h lint -p -D$(SYS) lsu.c log.c pat.c perm.c syntax.c time.c tty.c util.c clean: $(RM) $(OBJECTS) y.tab.c clobber: $(RM) $(EXEC) $(OBJECTS) lsu.h time.c tty.c y.tab.c fixup dtime dtty o nsu $(OBJECTS) $(LIBS) csu: $(OBJECTS) $(CC) $(CFLAGS) -o csu $(OBJECTS) $(LIBS) dtime: time.c $(CC) -o dtime $(DEBUG) $(CFLAGS) time.c dtty: tty.c lsu.h $(CC) -o dtty $(DEBUG) $(CFLAGS) tty.c pat.c $(OBJECTS): sysdep.h lsu.h # all this does is edit in the new definitions of SUPERM, LSUPERM, and LOG lsu.h: lsu.H fixulsu/Make/Make.UNICOS 644 1625 12 3271 5106523461 7412 # # lsu -- local super user # # variables that tailor the program SUPERM="./.su" LSUPERM = "./.lsu" LOG="./.log" # this should not be changed; it names the system (see sysdep.h # for the meaning and other abbreviations) SYS = UNICOS # this is a temp file used to edit the source E = Ed/Ed.$(SYS) # usual make stuff CFLAGS = -O -D$(SYS) DESTDIR = /usr/local/bin/ EXEC = su lsu nsu csu LIBS = RM = rm -f OBJECTS = lsu.o log.o pat.o perm.o syntax.o time.o tty.o util.o SOURCES = lsu.c log.c pat.c perm.c syntax.c time.y tty.y util.c all: $(EXEC) su: $(OBJECTS) $(CC) $(CFLAGS) -o su $(OBJECTS) $(LIBS) lsu: $(OBJECTS) $(CC) $(CFLAGS) -o lsu $(OBJECTS) $(LIBS) nsu: $(OBJECTS) $(CC) $(CFLAGS) -o nsu $(OBJECTS) $(LIBS) csu: $(OBJECTS) $(CC) $(CFLAGS) -o csu $(OBJECTS) $(LIBS) dtime: time.c $(CC) -o dtime $(DEBUG) $(CFLAGS) time.c dtty: tty.c lsu.h $(CC) -o dtty $(DEBUG) $(CFLAGS) tty.c pat.c $(OBJECTS): sysdep.h lsu.h # all this does is edit in the new definitions of SUPERM, LSUPERM, and LOG lsu.h: lsu.H fixup cp lsu.H lsu.h sh $E fixup | ed - lsu.h time.c: time.y yacc time.y; sed 's/yy/y1/g' y.tab.c > time.c tty.c: tty.y yacc tty.y; sed 's/yy/y2/g' y.tab.c > tty.c fixup: Makefile echo $(SUPERM) | sed 's/./& /g' > fixup echo $(LSUPERM) | sed 's/./& /g' >> fixup echo $(LOG) | sed 's/./& /g' >> fixup install: $(EXEC) -cp $(EXEC) $(DESTDIR) chown root $(DESTDIR)lsu $(DESTDIR)su $(DESTDIR)nsu $(DESTDIR)csu chmod 4755 $(DESTDIR)lsu $(DESTDIR)su $(DESTDIR)nsu $(DESTDIR)csu lint: time.c tty.c lsu.h lint -p -D$(SYS) lsu.c log.c pat.c perm.c syntax.c time.c tty.c util.c clean: $(RM) $(OBJECTS) y.tab.c clobber: $(RM) $(EXEC) $(OBJECTS) lsu.h time.c tty.c y.tab.c fixup dtime dtty e dtty lsu/Make/Make.UTS 644 1625 12 3266 5106523461 7071 # # lsu -- local super user # # variables that tailor the program SUPERM="./.su" LSUPERM = "./.lsu" LOG="./.log" # this should not be changed; it names the system (see sysdep.h # for the meaning and other abbreviations) SYS = UTS # this is a temp file used to edit the source E = Ed/Ed.$(SYS) # usual make stuff CFLAGS = -O -D$(SYS) DESTDIR = /usr/local/bin/ EXEC = su lsu nsu csu LIBS = RM = rm -f OBJECTS = lsu.o log.o pat.o perm.o syntax.o time.o tty.o util.o SOURCES = lsu.c log.c pat.c perm.c syntax.c time.y tty.y util.c all: $(EXEC) su: $(OBJECTS) $(CC) $(CFLAGS) -o su $(OBJECTS) $(LIBS) lsu: $(OBJECTS) $(CC) $(CFLAGS) -o lsu $(OBJECTS) $(LIBS) nsu: $(OBJECTS) $(CC) $(CFLAGS) -o nsu $(OBJECTS) $(LIBS) csu: $(OBJECTS) $(CC) $(CFLAGS) -o csu $(OBJECTS) $(LIBS) dtime: time.c $(CC) -o dtime $(DEBUG) $(CFLAGS) time.c dtty: tty.c lsu.h $(CC) -o dtty $(DEBUG) $(CFLAGS) tty.c pat.c $(OBJECTS): sysdep.h lsu.h # all this does is edit in the new definitions of SUPERM, LSUPERM, and LOG lsu.h: lsu.H fixup cp lsu.H lsu.h sh $E fixup | ed - lsu.h time.c: time.y yacc time.y; sed 's/yy/y1/g' y.tab.c > time.c tty.c: tty.y yacc tty.y; sed 's/yy/y2/g' y.tab.c > tty.c fixup: Makefile echo $(SUPERM) | sed 's/./& /g' > fixup echo $(LSUPERM) | sed 's/./& /g' >> fixup echo $(LOG) | sed 's/./& /g' >> fixup install: $(EXEC) -cp $(EXEC) $(DESTDIR) chown root $(DESTDIR)lsu $(DESTDIR)su $(DESTDIR)nsu $(DESTDIR)csu chmod 4755 $(DESTDIR)lsu $(DESTDIR)su $(DESTDIR)nsu $(DESTDIR)csu lint: time.c tty.c lsu.h lint -p -D$(SYS) lsu.c log.c pat.c perm.c syntax.c time.c tty.c util.c clean: $(RM) $(OBJECTS) y.tab.c clobber: $(RM) $(EXEC) $(OBJECTS) lsu.h time.c tty.c y.tab.c fixup dtime dtty y e dtty lsu/Make/Make.SGI3_4 644 1625 12 3301 5106523461 7334 # # lsu -- local super user # # variables that tailor the program SUPERM="./.su" LSUPERM = "./.lsu" LOG="./.log" # this should not be changed; it names the system (see sysdep.h # for the meaning and other abbreviations) SYS = SGI3_4 # this is a temp file used to edit the source E = Ed/Ed.$(SYS) # usual make stuff CFLAGS = -O -D$(SYS) DESTDIR = /usr/local/bin/ EXEC = su lsu nsu csu LIBS = -lcrypt RM = rm -f OBJECTS = lsu.o log.o pat.o perm.o syntax.o time.o tty.o util.o SOURCES = lsu.c log.c pat.c perm.c syntax.c time.y tty.y util.c all: $(EXEC) su: $(OBJECTS) $(CC) $(CFLAGS) -o su $(OBJECTS) $(LIBS) lsu: $(OBJECTS) $(CC) $(CFLAGS) -o lsu $(OBJECTS) $(LIBS) nsu: $(OBJECTS) $(CC) $(CFLAGS) -o nsu $(OBJECTS) $(LIBS) csu: $(OBJECTS) $(CC) $(CFLAGS) -o csu $(OBJECTS) $(LIBS) dtime: time.c $(CC) -o dtime $(DEBUG) $(CFLAGS) time.c dtty: tty.c lsu.h $(CC) -o dtty $(DEBUG) $(CFLAGS) tty.c pat.c $(OBJECTS): sysdep.h lsu.h # all this does is edit in the new definitions of SUPERM, LSUPERM, and LOG lsu.h: lsu.H fixup cp lsu.H lsu.h sh $E fixup | ed - lsu.h time.c: time.y yacc time.y; sed 's/yy/y1/g' y.tab.c > time.c tty.c: tty.y yacc tty.y; sed 's/yy/y2/g' y.tab.c > tty.c fixup: Makefile echo $(SUPERM) | sed 's/./& /g' > fixup echo $(LSUPERM) | sed 's/./& /g' >> fixup echo $(LOG) | sed 's/./& /g' >> fixup install: $(EXEC) -cp $(EXEC) $(DESTDIR) chown root $(DESTDIR)lsu $(DESTDIR)su $(DESTDIR)nsu $(DESTDIR)csu chmod 4755 $(DESTDIR)lsu $(DESTDIR)su $(DESTDIR)nsu $(DESTDIR)csu lint: time.c tty.c lsu.h lint -p -D$(SYS) lsu.c log.c pat.c perm.c syntax.c time.c tty.c util.c clean: $(RM) $(OBJECTS) y.tab.c clobber: $(RM) $(EXEC) $(OBJECTS) lsu.h time.c tty.c y.tab.c fixup dtime dtty g and other abbreviations) SYS = SGI3_4 # this is a temp file used to edit the source E = Ed/Ed.$(SYS) # usual make stuff CFLAGS = -O -D$(SYS) DESTDIR = /usr/local/bin/ EXEC = su lsu nsu csu LIBS = -lcrypt RM = rm -f OBJECTS = lsu.o log.o pat.o perm.o syntax.o time.o tty.o util.o SOURCES = lsu.c log.c pat.c perm.c synlsu/Make/Make.BSD4_3 644 1625 12 3275 5106523461 7334 # # lsu -- local super user # # variables that tailor the program SUPERM="./.su" LSUPERM = "./.lsu" LOG="./.log" # this should not be changed; it names the system (see sysdep.h # for the meaning and other abbreviations) SYS = BSD4_3 # this is a temp file used to edit the source E = Ed/Ed.$(SYS) # usual make stuff CFLAGS = -O -D$(SYS) DESTDIR = /usr/local/bin/ EXEC = su lsu nsu csu LIBS = RM = rm -f OBJECTS = lsu.o log.o pat.o perm.o syntax.o time.o tty.o util.o SOURCES = lsu.c log.c pat.c perm.c syntax.c time.y tty.y util.c all: $(EXEC) su: $(OBJECTS) $(CC) $(CFLAGS) -o su $(OBJECTS) $(LIBS) lsu: $(OBJECTS) $(CC) $(CFLAGS) -o lsu $(OBJECTS) $(LIBS) nsu: $(OBJECTS) $(CC) $(CFLAGS) -o nsu $(OBJECTS) $(LIBS) csu: $(OBJECTS) $(CC) $(CFLAGS) -o csu $(OBJECTS) $(LIBS) dtime: time.c $(CC) -o dtime $(DEBUG) $(CFLAGS) time.c dtty: tty.c lsu.h $(CC) -o dtty $(DEBUG) $(CFLAGS) tty.c pat.c $(OBJECTS): sysdep.h lsu.h # all this does is edit in the new definitions of SUPERM, LSUPERM, and LOG lsu.h: lsu.H fixup cp lsu.H lsu.h sh $E fixup | ed - lsu.h time.c: time.y yacc time.y; sed 's/yy/y1/g' y.tab.c > time.c tty.c: tty.y yacc tty.y; sed 's/yy/y2/g' y.tab.c > tty.c fixup: Makefile echo $(SUPERM) | sed 's/./& /g' > fixup echo $(LSUPERM) | sed 's/./& /g' >> fixup echo $(LOG) | sed 's/./& /g' >> fixup install: $(EXEC) -cp $(EXEC) $(DESTDIR) chown root $(DESTDIR)lsu $(DESTDIR)su $(DESTDIR)nsu $(DESTDIR)csu chmod 4755 $(DESTDIR)lsu $(DESTDIR)su $(DESTDIR)nsu $(DESTDIR)csu lint: time.c tty.c lsu.h lint -phbac -D$(SYS) lsu.c log.c pat.c perm.c syntax.c time.c tty.c util.c clean: $(RM) $(OBJECTS) y.tab.c clobber: $(RM) $(EXEC) $(OBJECTS) lsu.h time.c tty.c y.tab.c fixup dtime dtty ty lsu/Make/Make.DYNIX2_0 644 1625 12 3357 5106523461 7613 # # lsu -- local super user # # variables that tailor the program SUPERM="./.su" LSUPERM = "./.lsu" LOG="./.log" # this should not be changed; it names the system (see sysdep.h # for the meaning and other abbreviations) SYS = DYNIX2_0 # this is a temp file used to edit the source E = Ed/Ed.$(SYS) # usual make stuff CFLAGS = -O -D$(SYS) DESTDIR = /usr/local/bin/ EXEC = su lsu nsu csu LIBS = RM = rm -f OBJECTS = lsu.o log.o pat.o perm.o syntax.o time.o tty.o util.o pat.o SOURCES = lsu.c log.c pat.c perm.c syntax.c time.y tty.y util.c pat.c all: $(EXEC) su: $(OBJECTS) $(CC) $(CFLAGS) -o su $(OBJECTS) $(LIBS) csu: $(OBJECTS) $(CC) $(CFLAGS) -o csu $(OBJECTS) $(LIBS) lsu: $(OBJECTS) $(CC) $(CFLAGS) -o lsu $(OBJECTS) $(LIBS) nsu: $(OBJECTS) $(CC) $(CFLAGS) -o nsu $(OBJECTS) $(LIBS) dtime: time.c $(CC) -o dtime $(DEBUG) $(CFLAGS) time.c dtty: tty.c lsu.h $(CC) -o dtty $(DEBUG) $(CFLAGS) tty.c pat.c $(OBJECTS): sysdep.h lsu.h # all this does is edit in the new definitions of SUPERM, LSUPERM, and LOG lsu.h: lsu.H fixup cp lsu.H lsu.h sh $E fixup | ed - lsu.h time.c: time.y $(YACC) time.y; sed 's/yy/y1/g' y.tab.c > time.c; $(RM) y.tab.c tty.c: tty.y $(YACC) tty.y; sed 's/yy/y2/g' y.tab.c > tty.c; $(RM) y.tab.c fixup: Makefile echo $(SUPERM) | sed 's/./& /g' > fixup echo $(LSUPERM) | sed 's/./& /g' >> fixup echo $(LOG) | sed 's/./& /g' >> fixup install: $(EXEC) -cp $(EXEC) $(DESTDIR) chown root $(DESTDIR)lsu $(DESTDIR)su $(DESTDIR)nsu $(DESTDIR)csu chmod 4755 $(DESTDIR)lsu $(DESTDIR)su $(DESTDIR)nsu $(DESTDIR)csu lint: time.c tty.c lsu.h lint -phbac -D$(SYS) lsu.c log.c pat.c perm.c syntax.c time.c tty.c util.c clean: $(RM) $(OBJECTS) y.tab.c clobber: $(RM) $(EXEC) $(OBJECTS) lsu.h time.c tty.c y.tab.c fixup dtime dtty lsu/Make/Make.BSD4_2 644 1625 12 3274 5106523461 7332 # # lsu -- local super user # # variables that tailor the program SUPERM="./.su" LSUPERM = "./.lsu" LOG="./.log" # this should not be changed; it names the system (see sysdep.h # for the meaning and other abbreviations) SYS = BSD4_2 # this is a temp file used to edit the source E = Ed/Ed.$(SYS) # usual make stuff CFLAGS = -O -D$(SYS) DESTDIR = /usr/local/bin/ EXEC = su lsu nsu csu LIBS = RM = rm -f OBJECTS = lsu.o log.o pat.o perm.o syntax.o time.o tty.o util.o SOURCES = lsu.c log.c pat.c perm.c syntax.c time.y tty.y util.c all: $(EXEC) su: $(OBJECTS) $(CC) $(CFLAGS) -o su $(OBJECTS) $(LIBS) lsu: $(OBJECTS) $(CC) $(CFLAGS) -o lsu $(OBJECTS) $(LIBS) nsu: $(OBJECTS) $(CC) $(CFLAGS) -o nsu $(OBJECTS) $(LIBS) csu: $(OBJECTS) $(CC) $(CFLAGS) -o csu $(OBJECTS) $(LIBS) dtime: time.c $(CC) -o dtime $(DEBUG) $(CFLAGS) time.c dtty: tty.c lsu.h $(CC) -o dtty $(DEBUG) $(CFLAGS) tty.c pat.c $(OBJECTS): sysdep.h lsu.h # all this does is edit in the new definitions of SUPERM, LSUPERM, and LOG lsu.h: lsu.H fixup cp lsu.H lsu.h sh $E fixup | ed - lsu.h time.c: time.y yacc time.y; sed 's/yy/y1/g' y.tab.c > time.c tty.c: tty.y yacc tty.y; sed 's/yy/y2/g' y.tab.c > tty.c fixup: Makefile echo $(SUPERM) | sed 's/./& /g' > fixup echo $(LSUPERM) | sed 's/./& /g' >> fixup echo $(LOG) | sed 's/./& /g' >> fixup install: $(EXEC) -cp $(EXEC) $(DESTDIR) chown root $(DESTDIR)lsu $(DESTDIR)su $(DESTDIR)nsu $(DESTDIR)csu chmod 4755 $(DESTDIR)lsu $(DESTDIR)su $(DESTDIR)nsu $(DESTDIR)csu lint: time.c tty.c lsu.h lint -phbac -D$(SYS) lsu.c log.c pat.c perm.c syntax.c time.c tty.c util.c clean: $(RM) $(OBJECTS) y.tab.c clobber: $(RM) $(EXEC) $(OBJECTS) lsu.h time.c tty.c y.tab.c fixup dtime dtty ECTS) lsu.h time.c tty.c y.tab.c fixup dtime dtty lsu/LOG 644 1625 12 5142 5106523461 5301 # ========== # |LOG FILE| # ========== # # This file describes the format of the log file. # # SAMPLE # ====== # # This is a sample log file; at the end of the sample is a description of # the format # SU 02/20 13:16 + ttyi9 mab-spool [28670] - became spool (UID 2, GID 1) SU 02/20 13:16 i ttyi9 spool- [28671] - bad tty (line 19) SU 02/20 13:16 i ttyi9 spool-root [28671] - invalid password SU 02/20 13:16 - ttyi9 spool-root [28671] - permission denied SU 02/20 13:16 i ttyi9 mab-nancy [28672] - nancy not newuser (line 17) SU 02/20 13:16 - ttyi9 mab-nancy [28672] - permission denied nu 02/20 13:18 i ttyi9 mab- [28678] - root not newuser (line 17) nu 02/20 13:18 + ttyi9 mab-root [28678] - became root (UID 0, GID 0) su 02/20 13:18 i ttyi9 mab- [28681] - root not newuser (line 17) su 02/20 13:18 i ttyi9 mab-root [28681] - invalid password su 02/20 13:18 - ttyi9 mab-root [28681] - permission denied su 02/20 13:18 i ttyi9 mab- [28682] - root not newuser (line 17) su 02/20 13:18 + ttyi9 mab-root [28682] - became root (UID 0, GID 0) cu 02/20 13:19 v ttyi9 mab- [28686] - permission denied (not root) cu 02/20 13:19 v ttyi9 root- [28687] - checking syntax of PERM.lsu # # FORMAT DESCRIPTION # ====== =========== # # Consider the following line: # # su 02/20 13:18 - ttyi9 mab-root [28681] - permission denied # A B C D E F G H I J K # # A -- which program was run; values are "SU" for lsu, "su" for su, "nu" # for nsu, and "cu" for csu (2 characters) # B -- month; 1 for January, 12 for December (2 digits) # C -- day of month (2 digits) # D -- hour using a 24-hour clock (2 digits) # E -- minute (2 digits) # F -- what this message is; values are "+" for success, "-" for failure, # "i" for information, and "v" for syntax check (1 character) # G -- tty from which the program was run (at least 7 characters) # H -- who is running the program (up to 8 characters) # I -- who ihe is trying to change to (up to 8 characters) # J -- message number; all log entries associated with one run of a program # will have the same message number (5 digits) # K -- message (to end of log line) # # The format is: # %2s %02d/%02d %02d:%02d %1c %7s %17s [%05d] - %s # A B C D E F G H,I J K # Notes: # G -- may be more than 7 characters; if less than 7, the trailing characters # are blanks # H,I this has the format "%s-%s"; length of the two fields never exceeds # 17 characters. # K -- runs to end of line # lsu/lsu.1 644 1625 12 12431 5106523462 5642 .if n .ds LQ " .if n .ds RQ " .if t .ds LQ `` .if t .ds RQ '' .TH LSU 1 ARC .SH NAME su, lsu, nsu \- substitute user id temporarily .SH SYNOPSIS .B lsu [ .B \- ] [ .I userid | .B \-\- ] [ .I command ] .LP .B nsu [ .B \- ] [ .I userid | .B \-\- ] [ .I command ] .LP .B su [ .B \- ] [ .I userid | .B \-\- ] [ .I command_as_shell_argument ] .LP .B csu [ .I file " ..." ] .SH DESCRIPTION .IR Lsu , .IR nsu , and .I su allow a user to become another user without logging off. These programs, collectively called \*(LQsu programs\*(RQ, will execute a shell with real and effective UIDs and GIDs set to those of the specified user. The new shell will be the program named in the shell field of the specified user's password file entry, or .IR sh (1) if none. The new user ID stays in force until the shell exits. .PP Any additional arguments given on the command line are passed to the program invoked as the shell. Notice that with .I sh and .I csh (1), this means commands must be preceded by the option .B \-c and must be quoted (see the respective manual pages or the examples below.) In this case, the default user ID may be used by replacing .I userid with .BR \-\- . This must be done if .I userid is to be defaulted, since otherwise .I lsu will think the first word of the command is the login name of a user. .PP The user's environment variables are changed as follows: .I lsu and .I nsu set .B USER to the login name corresponding to .IR userid (the two are usually the same); all su programs set .B HOME to the home directory of .IR userid ; .BR SHELL , to the full path name of the shell being executed; and if the .I userid has a UID of 0, .B PATH will be set to a specific set of directories. (If there is no .B PATH environment variable and .I userid has a UID of 0, a .B PATH environment variable will be added.) However, argument 0 of the shell being executed is set to the name of the su program being executed except when the first argument is .BR \- , in which case the environment is changed further to what would be expected if the user had logged in as .IR userid . This is done by invoking the shell with the first character of argument 0 being `\-' (that is, as .IR \-lsu , .IR \-nsu , or .IR \-su ); this convention causes shells to read their startup files. .PP To use .IR nsu or .IR su , the user must know the password of the .I userid to which he wishes to change. The system administrator may allow only certain users to use these programs to change to a given .IR userid ; in such a case no other user may use these programs to change to that .I userid whether or not he knows .IR userid 's password. In these cases the system administrator may also limit the devices from which these two programs may be run and the times during which it may be run. With all users, the default .I userid is .IR root . .PP To use .IR lsu , the user need not know the password of the .I userid to which he wishes to .IR lsu . However, the system administrator must explicitly grant the user permission to use this program. The system administrator may constrain the use of this program by limiting the devices from which it may be run, the times during which it may be run, and/or the .IR userid s that may be substituted. The system administrator also controls the .I userid that will be assumed should no .I userid be supplied on the command line. .PP If the current user has a UID of 0, access control files are not checked and no passwords need be supplied. .PP .I Csu takes as arguments one or more access files, and checks the syntax of the entries in these files. If no arguments are supplied, .I csu reads from its standard input. .I Csu may only be run by the superuser. .SH EXAMPLES .PP To become user .B spool while retaining your nonlocal environment, type .sp 0.5v .ce lsu spool .sp 0.5v To become user .B spool but change the environment to that which .B spool would have had it logged in, type .sp 0.5v .ce lsu \- spool .sp 0.5v To execute .I command with the login environment and permissions of user .BR spool , type .sp 0.5v .ce lsu \- spool \-c "\f2command\fP" .sp 0.5v Note the arguments following .B spool are passed directly to .BR spool 's shell. To execute .I command as the default user ID, type .sp 0.5v .ce lsu \-\- \-c "\f2command\fP" .sp 0.5v .LP In all cases, .I su or .I nsu may be used rather than .IR lsu . .SH WARNINGS .LP The shell supplied by .I lsu and .I nsu runs the startup files of the user you are .IR lsu 'ing or .IR nsu 'ing to. This is in the spirit of what these programs should do. If you really want the old behavior, use .IR su ; it runs the startup files of the user you are .IR su 'ing to, and is provided for backwards compatibility with an older version of .IR su (1). .LP The default search path varies from system to system. If .B PATH is defined, the directories which will be in the search path when a user .IR lsu s, .IR nsu s or .IR su s to superuser also vary from machine to machine. In an ideal world, the resulting search paths would always be the same, regardless of the machine on which this program were run, but in our environment, this is not likely soon. .SH FILES /etc/passwd password file \& various access files \& log file .SH "SEE ALSO" csh(1), login(1), sh(1) .SH AUTHOR Matt Bishop (\f2mab@riacs.edu\fP) .SH VERSION This describes version 3.0 of .IR lsu , .IR nsu , and .I su . .\"(distributed on December 1, 1986). user. .PP The user's environment variables are changed as follows: .I lsu and .I nsu set .B USER to the login name corresponding to .IR userid (the two are usually the same); all su programs set .B HOME to the home directory of .Ilsu/fixup 644 1625 12 45 5307731047 5754 . / . s u . / . l s u . / . l o g p#Manual#README#log.c#lsu.c#pat.c#perm.c#syntax.c#sysdep.h#time.y(#tty.y8#util.c(H#ACCESS8X# CoverHh#!SetupX#"Makefile#$lsu.H#" nsu 'ing to. This is in the spirit of what these programs should do. If you really want the old behavior, use .IR su ; it runs the startup lsu/lsu.h 644 1625 12 15526 5307731053 5741 /* * LSU -- local superuser header file * * Author: * Matt Bishop * Research Institute for Advanced Computer Science * NASA Ames Research Center * Moffett Field, CA 94035 * * mab@riacs.edu ARPA * ...!decvax!decwrl!riacs!mab UUCP * ...!ihnp4!ames!riacs!mab UUCP * * Copyright (c) 1986. */ #include #include #include #include #include /* * this should run on several different versions of UNIX(tm) * the dependencies are localized into this file */ #include "sysdep.h" /* * the important file names (ie, the permission file and the log file * names) are constructed in memory just before use and cleared imme- * diately after; this prevents people from snooping through the exe- * cutable file to find the log or permission file name. To change * these, change them in the Makefile. Changing * LSUPERM changes the permission file for "lsu" * SUPERM changes the permission file for "su" * LOG changes the log file * DO NOT CHANGE THEM HERE as when you change anything else in the * file, the Makefile will clobber the changes you make here. Also, * don't delete anything -- you may screw up the way the Makefile works. */ #define MAKESUPERM(buf) { /* build the permission file name */ \ /* begin SUPERM */\ buf[0] = '.';\ buf[1] = '/';\ buf[2] = '.';\ buf[3] = 's';\ buf[4] = 'u';\ buf[5] = '\0';\ /* end SUPERM */\ } #define MAKELSUPERM(buf) { /* build the permission file name */ \ /* begin LSUPERM */\ buf[0] = '.';\ buf[1] = '/';\ buf[2] = '.';\ buf[3] = 'l';\ buf[4] = 's';\ buf[5] = 'u';\ buf[6] = '\0';\ /* end LSUPERM */\ } #define MAKELOG(buf) { /* build the log file name */ \ /* begin LOG */\ buf[0] = '.';\ buf[1] = '/';\ buf[2] = '.';\ buf[3] = 'l';\ buf[4] = 'o';\ buf[5] = 'g';\ buf[6] = '\0';\ /* end LOG */\ } #define CLEARNAME(buf) { /* clear the buffer */ \ register char *bc = buf; \ while(*bc) \ *bc++ = '\0'; \ } /* * internal definitions */ /* some universal UNIX goodies */ /* lower case version of c */ #define lowcase(c) (isupper((c))?tolower((c)):(c)) #define SZPASSWORD 8 /* max password length (see getpass(3)) */ #define BOGUSPWD "\001\001\001" /* illegal output from crypt(3) */ #define bitset(w,b) (((w)&(b))==(b)) /* defaults for lsu */ #define LSUCOMM '#' /* begins comment lines in LSUPERM */ #define LSUSHELL "/bin/sh" /* default shell */ #define LSUUSER "root" /* default user to su/lsu to */ #define SUPERUID 0 /* superuser UID */ /* logging options */ #define L_MASK 0x000f /* mask for logging options */ #define L_INFO 0x0000 /* no action */ #define L_NO 0x0001 /* don't allow user to lsu */ #define L_YES 0x0002 /* allow user to lsu */ #define L_CHK 0x0004 /* check syntax */ /* error options */ #define F_MASK 0x0ff0 /* mask for error options */ #define F_NONE 0x0000 /* no errors */ #define F_FAIL 0x0010 /* ?su is to fail */ #define F_COND 0x0020 /* fail UNLESS is (n)su to root */ #define F_PERM 0x0040 /* mail perm file message to LSUMAINT */ #define F_LOG 0x0080 /* mail log file message to LSUMAINT */ #define F_SYS 0x0100 /* system error */ /* errors in perm file lines */ #define E_MASK 0xf000 /* mask for error in perm file options */ #define E_NONE 0x0000 /* no error */ #define E_USER 0x1000 /* user not listed */ #define E_NEWU 0x2000 /* user can't lsu/su to that new user */ #define E_TTY 0x4000 /* bad tty */ #define E_TIME 0x8000 /* bad time */ /* abbreviations for legal program names (see runas[]) */ #define LSU (runas[0].pname)/* as local super user */ #define SU (runas[1].pname)/* as super user */ #define NSU (runas[2].pname)/* as super user */ #define CSU (runas[3].pname)/* as syntax checker */ /* used to determine what shell variables to reset */ #define V_NONE 0x0001 /* never reset this */ #define V_ALWAYS 0x0002 /* always reset this */ #define V_LSU 0x0004 /* reset this for lsu */ #define V_SU 0x0008 /* reset this for su */ #define V_NSU 0x0010 /* reset this for nsu */ #define V_CSU 0x0020 /* reset this for csu */ #define V_ORLOGIN 0x0040 /* reset if login OR another test met */ #define V_ANDLOGIN 0x0080 /* reset if login AND another test met */ /* useful versions of NULL */ #define CNULL ((char *) NULL) #define PNULL ((struct passwd *) NULL) /* * forward definitions */ char *isinlist(); /* determine if a name is in a list */ char *strsave(); /* make a copy of a string in reserved memory */ FILE *chkperm(); /* check and open the permission file */ /* * system variables */ extern int errno; /* system error number */ extern int sys_nerr; /* number of elements in sys_errlist[] */ extern char *sys_errlist[]; /* list of system error messages */ /* * system library functions */ char *crypt(); /* encrypt password */ char *ctime(); /* get time in ASCII format */ char *getpass(); /* get password from user */ struct passwd *getpwnam(); /* get data in password file for name */ struct passwd *getpwuid(); /* get data in password file for uid */ char *malloc(); /* allocate some memory */ char *rindex(); /* get the index of the rightmost char */ char *strcat(); /* catenate a string */ char *strcpy(); /* copy a string */ long time(); /* get time in internal format */ char *ttyname(); /* return the name of associated tty */ /* * structure to control permissions and such */ struct perms { char *lname; /* log name */ char *pname; /* program name */ }; /* * structure to control resetting of shell environment variables */ struct sv { char *splate; /* template to be reset */ int len; /* length of the template to be compared */ char **cval; /* if a string required, what it is */ unsigned int vused; /* when used */ }; /* * global variables */ extern char *progname; /* program name */ extern struct perms *progis; /* what the program runs as */ extern struct perms runas[]; /* legal program names -- MUST be one of these */ extern FILE *logfp; /* log file pointer */ extern char *date; /* date of run */ extern char *tty; /* terminal name or background */ extern char *towho; /* command line argument */ extern struct passwd curpw; /* information about who's running the program */ extern struct passwd newpw; /* information about who you're lsu'ing to */ extern char *shell; /* shell to be exec'ed */ extern char *exclude[]; /* exclude directories from path if new UID is 0 */ extern struct sv shvar[]; /* list of shell variables to change */ extern int stamp; /* PID stamp of this process (for logging) */ extern int dash; /* 1 if a login shell to be used */ extern int mailmesg; /* 1 if file problem message to be mailed */ extern unsigned int success; /* 1 if l/n/csu is to succeed */ #ifndef lint /* * these are used to keep track * of how and when the binaries * were made */ extern char *lsu_system; /* what operating system this was made for */ extern char *lsu_version; /* what version this is */ #endif \ register char *bc = buf; \ while(*bc) \ *bc++ = '\0'; \ } /* * internal definitions */ /* some universal UNIX goodies */ /* lower case vlsu/Manual 644 1625 12 131221 5106523462 6134 .if t .ds Lq ` .if t .ds Rq ' .if t .ds lQ `` .if t .ds rQ '' .if t .ds - \- .if t .ds dg \(dg .if t .ds co \(co .if n .ds Lq ' .if n .ds Rq ' .if n .ds lQ " .if n .ds rQ " .if n .ds - -- .if n .ds dg + .if n .ds co Copyright (c) .de cD .br .ls 1 .ps -2 .vs -2 .ne \\$1v .cs 1 25 .ss 20 .nf .di dX .. .de dC .di .nr iX (\\n(LLu-\\n(dlu)/2u .if \\n(iX>0 .in +\\n(iXu .dX .if \\n(iX>0 .in -\\n(iXu .fi .ss .cs 1 .vs +2 .ps +2 .sp .ls 2 .LP .. .lg 0 .bd S 3 3 .nr PS 12 .nr VS 14 .RP .TL A Mechanism for Sharing Accounts .AU Matt Bishop .AI Research Institute for Advanced Computer Science NASA Ames Research Center Moffett Field, CA 94035 .AB A serious problem in account management is how to determine accountability when several people must share access to one account. Further, password management for such an account is extremely complex. This paper describes a mechanism to overcome these problems, and an implementation of the mechanism under .UX \&. .AE .LP .ls 2 .FS This work was supported by grant NCC 2-398 from the National Aeronautics and Space Administration to the Universitied Space Research Association. .sp 0.5v This paper has been submitted for publication to the as-yet-unnamed USENIX technical journal. .sp 0.5v An extended abstract of this paper was presented at the Large Installation System Administrators Workshop in Philadelphia, PA on April 9-10, 1987 (see p. 36 of the \f2Proceedings\fP.) .FE .SH Overview .PP The Numerical Aerodynamic Simulator project runs a variety of .UX based operating system, on its computers (a Cray 2, 2 Amdahl 5840s, 4 VAX-11/780s and 25 IRIS 3500 workstations.) Users work on and off site, using a variety of networks not all of which are under NASA's control. Off site installations can be as close as a different building on the base or as distant as ICASE (on the East Coast.) In this environment, sharing accounts is very common; it provides a very quick and easy way to enable many people to work together, and allows many people to share responsibility for a particular task. For example, the account .I nasops is used to maintain a database of users (among other functions.) So, many people need access to that account. Unfortunately, this poses some problems. .PP First is the question of accountability. If someone logs in on the account .I nasops and compromises the database, how can the offending user be traced? Password management is the second problem; how can the site administrator force 40 or so people to keep the password secret, particularly since these users need at least two passwords (one for their own account and one for the .I nasops account)? When someone changes .I nasops ' password, how does he or she communicate that change to the other users in a timely manner? .PP A \*(lQgroup account\*(rQ is an account meant to be shared. No-one can log into a group account, but a program called .I lsu overlays the login identity with the group identity (just as the .UX program .I su overlays the login identity with a new user's identity.) The user must type his own password when switching to the group identity. .I Lsu checks an access file to ensure that the user can access the group account at the given time and from the given terminal, and then checks the password. If access is allowed and the user types his own password correctly, the group identity is pushed over the user's identity; if access is denied or the password is incorrect, .I lsu simply informs the user permission is denied. .PP Because of the sensitive nature of the program, several steps are taken to prevent compromise. While it seems redundant to require the user to type his password (didn't he type it to log in?), experience shows that people do leave their terminals unattended, so checking the password provides some assurance the user is the person running .I lsu . The password must always be typed, even when .I lsu has already determined the used has no right to .I lsu to the group account. The location of the access and log files are constructed in memory when .I lsu runs, and are erased immediately after being used. Both files must be owned by the most privileged account and must be unreadable and unwritable by anyone else; if not, the .I lsu fails and mail is sent to the .I lsu administrators. Of course, when access is denied, the user is not told why access is denied. .PP Another version of this program provides the same control for overlaying user accounts. The program .I nsu functions like .I lsu but requires the new account's password as well as meeting any conditions in the access file. (If the account is not listed in the access file, anyone can .I nsu to it.) In the event the access file is corrupt, .I nsu can only be used to access an account from which the access file can be fixed. .PP .I Lsu and .I nsu use the startup file of the new (group or user) identity, not that of the user running the program; this provides uniformity among users who may have wildly different environments in their private accounts. A third program, called .I su , uses the startup file of the user running the program, but uses the .I nsu access file to check permission. This provides compatibility with older versions of .I su . .SH Detailed Statement of the Problem .PP One of the nastiest problems in account management arises when a number of people must share a single account. This is actually quite common; for example, at a .SM TOPS-\c .NL 20 site, several users must have access to the .I wheel account to manage the system, or at a .UX site, there are a number of such administrative accounts that more than one user accesses on a daily basis. This poses a number of management problems. .PP The most obvious one is accountability. Who is using the account? If the account has problems such as being over disk quota, or being used in an insecure manner, it can be very difficult to track the individual responsible for the problems. Worse, if the account should be used at an unusual time, or to do something the users of the account do not normally do, the system administrator will have to contact each user of the account in order to determine whether or not another party has learned the password and thus been able to penetrate the system. .PP The second problem is the computer system's version of a key management problem. Suppose 40 people have the password to the account .I source , which is used to edit and compile system programs. As any system administrator knows, this means \*(lQat least\*(rQ 40 people, because experience dictates they will share the source account password with their colleagues who want it, whether or not those colleagues are authorized to get the password. So, to prevent this, the system administrator decides to change the password every so often. Now 40 people must be notified of the new password; their work will be delayed while they are told of the new password; and undoubtedly some of them will tell their colleagues about the new password. All this gains is pain for the users as well as the administrators. .PP One way around these administrative problems is to create .I "group accounts" . Access control lists\*(dg .FS \*(dg See [DENN82], section 4.4, for a discussion of access control lists. .FE determine whether or not a specific user can gain access to such an account. However, the accounts are not accessible by just logging in; so, the user must log in as himself, and then run another program to \*(lQpush\*(rQ the group identity on top of his own. .PP This paper discusses the implementation of such a scheme under .UX . The next section describes the environment in which the solution was developed; the section after that describes the use of the relevant program, and the section after that discusses the access control list used by the program. The final section discusses some remaining problems and offers guidance on future work. .SH The .UX Environment .PP .UX provides a mechanism, called .I setuid , that allows one user to execute a program with the privileges of another user. The program may be a command interpreter called a .I shell (see [BOUR84] and [JOY84]), in which case any commands issued from within that command interpreter run with the privileges of the second user. This will be the basis for the notion of \*(lQpushing\*(rQ the group identity. Pushing corresponds to executing such a command interpreter; popping correspondingly means exiting the command interpreter and thereby returning to the original environment. .PP .UX does not ever store cleartext passwords; rather, the passwords are encrypted using a variant of the DES, and the thirteen character result is saved in a file called the .I password file. (See [MORR79] for a detailed discussion of how and why this is done.) So, a program need not be privileged to compare a password to that of a specific user; it simply encrypts the typed password, and compares the result to the one in the password file. (This provides an elegant mechanism for preventing someone from logging into a group account; simply use a string not in the range of the encryption function as the encrypted password, and no one will be able to log in to that account. This is useful for system maintenance accounts, such as the line printer spooler's account.) In addition, the password file stores other information about each user; this information is used to set up the environment for the user. .PP We discussed command interpreters called shells earlier. A .UX shell is actually more than a command interpreter; users can define variables within the shell command language, and some of these variables are special. For example, the shell variable .B USER contains the name of the user executing the shell; the variable .B HOME contains the directory in which the user began his session; the variable .B SHELL contains the name of the command interpreter; and the variable .B PATH contains a list of the places to search for each command to be executed by the shell (and the value of this variable is often referred to as the \*(lQsearch path\*(rQ). The last variable is vital, since if two programs have the same name, the shell will decide which one to execute based on the value of the .B PATH variable. Because of their effect on the way the shell works, these shell variables are more commonly called .I "environment variables" . .PP A shell is invoked by passing the shell's name, followed by a list of arguments and preset environment variables. Shells sometimes execute the commands in a command file (called a .I "startup file" ) when they begin executing. There are two kinds of startup files, those executed when the shell is started, and those executed when the user logs in. The shell decides if it was started when the user logged in by checking the name with which it was invoked; if that argument begins with a hyphen (\*(lQ\-\*(rQ) the shell is a login shell. .PP This is a rough description of points of .UX that must be taken into consideration in the design of the group account access program. The next section describes how that program works. .SH The Program .PP The program used to push a new user identity onto the current one exists in two forms. The first, .I lsu , is a very restrictive program that is used to access group accounts. The second, .I nsu , may be used to access any account with a password.\(dg .FS .IP \(dg \w'\(dg'u There are two other versions of this program: .I su , which exists for compatibility reasons (it uses all protection mechanisms of .I nsu but duplicates the environment of the .UX command .I su , hence the name) and .I csu , which checks the syntax and format of the access file. .FE The way these commands are used is basically the same; we shall use .I lsu for demonstration, pointing out the difference between the two when relevant. .PP To push the identity of the user .I grpact (for \*(lQgroup account\*(rQ), use the command .DS C lsu grpact .DE .I Lsu will prompt the user for his password. At this point, the user types a password. If the program is .I nsu , the password is that of .I grpact ; if the program is .I lsu , the password is that of the user's account and .B not that of the account .I grpact (which may not even have a password.) After verifying the password is correct, and after checking that the user is allowed to push the identity of account .I grpact over his, .I lsu will respond by providing a shell with the privileges of the account .I grpact . To return to his original environment, the user can pop the .I grpact environment by exiting this shell. Should the password be incorrect or permission not be given, .I lsu responds with .DS C lsu: permission denied .DE and does nothing. .PP If the user wants to execute only one command using the group account, he may give additional arguments to the command; all after the group account name are passed as command arguments to the shell. For example, suppose there is a group account \*(lQsource\*(rQ that is used to recompile system sources, and a user with access to that account wants to copy the file \*(lQaux.c\*(rQ to \*(lQaux.c.old\*(rQ. He can do this in one of two ways (in the following, what the computer types is in \fBboldface\fP and what the user types is in normal type): .DS B \fB$\fP lsu source \fBPassword:\fP abx34yz \fB%\fP cp aux.c aux.c.old \fB%\fP exit \fB$\fP .DE or .DS B \fB$\fP lsu source -c "cp aux.c aux.c.old" \fBPassword:\fP abx34yz \fB%\fP .DE The \*(lQ-c\*(rQ and double quotes around the copy command are necessary, because the \*(lQsource\*(rQ account uses the C shell, and that requires commands which are given on the command line to be surrounded by quotes. The actual command .I lsu will execute is .DS C /bin/csh -c "cp aux.c aux.c.old" .DE (refer to .I csh (1) in [UPM84] for more information.) .PP Normally, when a new environment is pushed onto an existing one, certain parts of the old environment are not changed. These attributes are the ones associated with logging in; remember, some environment variables are set when you log in and others whenever the shell is invoked. Sometimes a user switching to the identity of a group account will want the shell to set the environment variables associated with logging in as well as those associated with the invocation of the shell. To have .I lsu do this, give the first argument as \*(lQ\-\*(rQ. So, in the first example above, if you wanted to set the environment as though you had logged in as \*(lQsource\*(rQ, say .DS C lsu \- source .DE rather than .DS C lsu source .DE .PP In the subshell started by .I lsu , some variables in the environment are changed. .B USER is set to the login name of the new identity, .B SHELL is set to the pathname of the shell being used, and .B HOME is set to the home directory of the new account. If the new account is that of the most privileged user, .I root (often called the .I superuser ), the value of the environment variable .B PATH is changed to exclude all but a small number of specific system directories. This is done because many systems have directories containing programs not necessarily written and installed by system programmer; allowing .I root to execute programs in them would create interesting possibilities for Trojan horse attacks. .PP The use of these commands is controlled by an access file set up by the superuser .I root . Basically, this access file controls who can push what identity, when, and from where. If .I lsu is being used, the user must have permission explicitly granted by this file. If .I nsu is being used, any new account listed in the access file becomes restricted in the sense that no user can .I nsu to such an account unless the access file explicitly allows him to. However, anyone knowing the proper password can .I nsu to any account that is not restricted in this sense. In the next section, we will discuss the format of this access file. .SH Access File .PP The access file is set up by the superuser and controls who may push new identities. It is readable and writable by only the superuser. Each entry is one line, and consists of four fields separated by one or more tab characters (\s-2ASCII HT\s0, octal 011, hex 0x09): .KS .TS center; l l l l. \f2user\fP \f2identities-he-can-push\fP \f2terminals\fP \f2time\fP .TE .KE .PP The first field names the user attempting the push. The second is a comma-separated list of user names the identities of which the user named in the first field may push. The third field lists constraints on the devices the user may .I lsu or .I nsu from; the last field constrains the times during which the user may push a new identity. Some sample entries are: .cD .ta 10n 20n 35n 45n 55n nancy bin !"ttyp.*" weekdays 9am-5pm mab Any console March 1985 rlb staff Any Any gba news >=9600 June 21 - Sept 21 .dC .PP An entry is said to be .I satisfied if that entry allows the current user to push the desired identity from the current terminal and at the current time; that is, if the entry alone would allow access for the user to substitute the new identity (assuming the correct password were supplied.) A user may be entered in the file more than once. The file is searched in order; if any entry for that user/identity pair is satisfied, the access permission is granted. Note that if there are two enbtries for the same user/identity pair, access permission is granted if either entry is satisfied. .PP The .I terminals field may constrain the device from which .I lsu or .I nsu is run by name or by terminal speed. To constrain by name, simply type the name of the terminal; for example, if the field were .DS C /dev/console .DE then the user could substitute any of the identities on that line only if he executed .I lsu or .I nsu from the system console. (If the file name is not a full path name, the prefix \*(lQ/dev/\*(rQ is assumed.) If the program should only be run from the console and some other terminal, say \*(lQttyi9\*(rQ, the above line could read .DS C console,ttyi9 .DE Sometimes it is more convenient to specify terminal names by patterns, as (for example) to prevent use of .I lsu and .I nsu from pseudo-ttys, the names of which begin with \*(lQttyp\*(rQ or \*(lQttyq\*(rQ, one could give the line .DS C !"ttyp.*","ttyq.*" .DE where the \*(lQ!\*(rQ means \*(lQnot\*(rQ and the double quotes mean that the string they surround is to be used as a pattern. The pattern matcher used is the same as for the editor .I ed (1) (see [UPM84]) so the precise syntax differs between Berkeley .UX and System V .UX . Finally, there are times when systems need to constrain users based on terminal speed. For example, a site somewhat conscious of security might not want users to overlay certain identities from terminals not located within the building; for these sites, giving a relationship followed by a baud rate will allow the user to push an identity only if the speed of the terminal satisfies the relationship with the given baud rate. Thus, if the third field were .DS C >=9600 .DE the user would not be permitted to push any identities on that line unless he were working on a terminal which ran at at least 9600 baud. Allowed relationships are \*(lQ@\*(rQ and \*(lQ=\*(rQ (both meaning equal), \&\*(lQ<\*(rQ (less than), \&\*(lQ<=\*(rQ (less than or equal to), \&\*(lQ>\*(rQ (greater than), \&\*(lQ>=\*(rQ (greater than or equal to), and \&\*(lQ!=\*(rQ, \*(lQ<>\*(rQ, and \*(lQ><\*(rQ (all meaning not equal to.) Note that if permission is conditioned on speed and the program is run in the background or with the standard input, output, and error all associated with files, permission will always be denied. A complete syntax is given in Appendix I. .PP As examples, go back to the sample access control file at the beginning of this section. The first line says that the user \*(lQnancy\*(rQ can .I lsu to the group account .I bin only when she does so from a terminal the name of which does not begin with ``ttyp'' (on many implementations of .UX , this prevents her from doing so when she is logged in over a network.) The next line says that user \*(lQmab\*(rQ can .I lsu to any other account on the system from the console, but from no other terminal. The third line means that the user \*(lQrlb\*(rQ can .I lsu to the group account .I staff from any terminal, and the last line says that user \*(lQgba\*(rQ can push the identity of the group account .I news from any terminal running at 9600 baud or greater; in practise, this means that he cannot do so when logged in over a telephone line. .PP The .I time field constrains when the user can push the new identity. Times may be constrained by days of the week, times of day, or days of the year. For example, if a contractor is doing kernel work, he might be supposed to access the appropriate group account only during working hours. Times are expressed as a day of the year, followed by a day of the week, followed by a time of day; any of these three parts may be omitted, but if more than one is present, all must be true for .I lsu or .I nsu to allow the pushing. .PP The format for each of these is quite liberal; for the day of the year, any of the following forms will be recognized: .DS B July 4, 1986 July 4 July, 1986 July 7/4/86 7/4 .DE The first and fifth give permission only for July 4, 1986; the second and sixth, for July 4 of any year; the third, for any day within July, 1986; and the fourth, for any day in July of any year. Days of the week may be expressed by naming the day; only as much to identify one day is needed (for example, any of \*(lQSunday\*(rQ, \*(lQSun\*(rQ, and \*(lQSu\*(rQ will be read as \*(lQSunday\*(rQ; but \*(lQS\*(rQ will be rejected as ambiguous, since it could refer to \*(lQSaturday\*(rQ.) Times of day may be given in any of these forms: .DS B 8:12:16 PM 8:12:16 8:12 PM 8:12 8 PM 8 noon midnight .DE If an \*(lQAM\*(rQ or \*(lQPM\*(rQ is given, time is based on a 12-hour clock. Otherwise, it is based on a 24-hour clock. So, the first time would give permission at 8:12:16 in the evening, the second at 8:12:16 in the morning, and so forth. Note that saying \*(lQ8\*(rQ gives permission for the entire hour of 8 o'clock in the morning; thus, 8 and 8:00:00 are .B not the same. .PP Times may also be expressed as an interval. For example, to prevent someone from running .I lsu on weekends, the time field could be set to \*(lQMon-Fri\*(rQ. To restrict use of .I lsu to working hours during the summer months, the field \*(lQJune-Sept Mon-Fri 8am-5pm\*(rQ suffices. .PP A complete syntax is given in Appendix II. .PP Refer back to the sample file at the beginning of this section. The first line means user \*(lQnancy\*(rQ can .I lsu to the group account .I bin only during the weekdays from 9 AM to 5 PM; the second line says that user \*(lQmab\*(rQ can .I lsu to any account on the system during March 1985. The third line indicates that user \*(lQrlb\*(rQ can substitute the identity of the group account .I staff at any time, and the final line allows \*(lQgba\*(rQ to .I lsu to .I news only during summers. .PP Fields are separated by one or more tab characters. If either of the last two fields are omitted, they are held to be satisfied. It is recommended this not be done (and this feature may disappear in the next version.) Note the terminal field must be present if the time field is present. .PP The program .I csu will check for, and report, syntax errors in the access file. Since it will print the syntactically incorrect lines, it can only be run by the superuser. .PP .I Lsu and .I nsu use the permission file a bit differently. With .I lsu , if a user is not explicitly listed in the file as being able to .I lsu to the new identity, permission is denied; and the default new identity is the first name in the .I identities-he-can-push field of the permission file that is satisfied. With .I nsu , if a user attempts to .I nsu to some other account, and provides a proper password, the .I nsu succeeds unless the account being pushed is named in .B any .I identities-he-can-push field of the access file. If so, the access file must allow the user running .I nsu to do the pushing. The default new identity for .I nsu is .I root . .SH Log File .PP Every time .I lsu or .I nsu is executed, an entry is made in a log file indicating the success or failure of the attempt. This log is readable and writable only by the superuser. Some sample entries: .cD 17 SU 02/18 11:20 + ttyi9 mab-spool [09941] \- became spool (UID 2, GID 1) SU 02/18 11:21 i ttyi9 spool- [09942] \- bad tty (line 11) SU 02/18 11:21 i ttyi9 spool-root [09942] \- invalid password SU 02/18 11:21 \- ttyi9 spool-root [09942] \- permission denied SU 02/18 11:21 i ttyi9 mab-nancy [09944] \- nancy not newuser (line 9) SU 02/18 11:21 \- ttyi9 mab-nancy [09944] \- permission denied nu 02/18 11:26 + ttyi9 mab-root [09980] \- became root (UID 0, GID 0) nu 02/18 11:42 i ttyi9 mab-root [10088] \- invalid password nu 02/18 11:42 \- ttyi9 mab-root [10088] \- permission denied su 02/18 11:55 i ttyi9 mab- [10236] \- root not newuser (line 1) su 02/18 11:55 i ttyi9 mab- [10236] \- bad tty (line 3) su 02/18 11:55 \- ttyi9 mab-root [10236] \- permission denied su 02/18 11:56 i ttyi9 mab- [10243] \- root not newuser (line 1) su 02/18 11:56 + ttyi9 mab-root [10243] \- became root (UID 0, GID 0) cu 02/18 11:59 v ttyi9 root- [10270] \- checking syntax of XXX cu 02/18 12:04 v ttyi9 mab- [10317] \- permission denied (not root) .dC Each line has the same format. Fields are separated by blanks, except that the last field (everything after the rightmost hyphen, in the display above) may contain blanks. The first item is a two-character code for the version of .I lsu being run; they are .KS .TS center, doublebox; c | c l | c. \f2code\fP \f2program\fP = su su _ SU lsu _ nu nsu _ cu csu .TE .KE The next two fields give the month, day, and time; they are always in the form of a two-digit number for the month followed by a slash followed by a two-digit day of the month, and a two-digit hour followed by a colon followed by a two-digit minute. The month is represented as a number from 1 (January) to 12 (December), and the day of the month follows the slash. Times are given using a 24-hour (European) clock. The fourth field is one of \*(lQ+\*(rQ, \*(lQ\-\*(rQ, \*(lQv\*(rQ, or \*(lQi\*(rQ; the first indicates the substitution was successful, the second that it failed, the third that a syntax check was attempted, and the fourth an informational message. (Note that one run of any of these programs may produce many informational messages, but only one message indicating success or failure.) The fifth field is the name of the terminal from which .I lsu or .I nsu were run; it is at least seven characters long (if the terminal name is longer, this field is extended appropriately). The sixth field is seventeen characters or less; it consists of the user's name followed by a hyphen followed by the name of the new identity. If there is a problem with the access file, the name of the new identity may be omitted. If for some reason no login name can be found, the user identification number is printed in parentheses. The seventh field is a stamp which can be used to associate messages from the same run with one another; it is always five digits enclosed in square brackets and separated from the sixth and eighth field by one blank. Following this field, there is a hyphen, followed by a message field. .PP The message field indicates what happened and why. If the attempted substitution was successful, the message gives the login name, user identification number, and group identification number of the new identity. If the attempted substitution failed due to a system error, such as the access file having incorrect permissions or not existing, or because the user did not know the correct password, the message indicates this. If the substitution failed because the user did not have permission to make the desired access, many messages may appear, each indicating one reason for denial of access; but all have the same stamp. Note that under certain conditions error messages may be given but the access will succeed; this mainly happens when a user listed in the .I nsu permission file tries to .I nsu to an account not listed in any newuser's field of the .I nsu permission file. .SH Security Considerations .PP .I Lsu and .I nsu are very sensitive programs; security was a major consideration while writing them. We used a number of techniques to increase the difficulty of ``breaking'' the programs. .PP The first weak point is the access file. Obviously, it must be owned by .I root and not writable by any other user. Less obviously, it should not even be readable by any other user (if it were, an attacker would know which users can gain access to privileged accounts, and thus that he or she should concentrate their efforts on obtaining those user's password.) So, one step within the program verifies that these conditions hold. If not, the program terminates, the access file is made unreadable, mail is sent to an administrator, and the .I lsu fails. (Making that file unreadable prevents future .I lsu 's or .I nsu 's from using it but allows a superuser to look at it later.) .PP There is one problem with this scheme. If the identity to be pushed is that of the superuser, and the access file is corrupt or does not exist, it would not be possible for anyone to become superuser, thereby forcing the system to be reloaded (or some such drastic action.) To eliminate this problem, when the access file is corrupt or nonexistant and the identity to be pushed has UID 0, .I nsu will ignore the access file. This way, the user need only supply the superuser password. Of course, the first thing that user should do is fix the access file problem! .PP In fact, the name of the access file itself is hidden, on the principle that not advertising the location of the file will discourage attackers from using other means of breaching security to alter the file. This is done by constructing the file name in memory, and clearing the file name immediately after use. Thus, the file name exists during only two systems calls: the first, to get status information about the file; and the second, to open the file. .PP .I Lsu and .I nsu also conceal the reasons for denial of access from the user. When a user runs either of these programs, and for some reason access is to be denied, the only message the user will get is: .DS C \f2program name\fP: permission denied .DE regardless of the reason for denial. This discourages attempts to figure out what a password is, or when the action is allowed, by trial and error. Of course, the specific reason for denial is placed in the log file; this log, however, is as stringently controlled as is the access file, and if it is not readable and writable by root and no-one else, .I lsu and .I nsu will refuse to push any new identity. Moreover, the user must at all times supply a password, whether or not the password is relevant to the denial of access. For example, if user \*(lQmab\*(rQ tried to .I lsu to the superuser, and he did not have permission in the access file, there is no need to request a password; even if \*(lQmab\*(rQ knows the password, access will be denied. But \*(lQmab\*(rQ will still have to supply the password; this is to prevent him from knowing that he was denied access due to the access file. Otherwise, if an attacker were impersonating \*(lQmab\*(rQ, the attacker would know to try another account because he would not be able to use .I lsu or .I nsu to access the superuser account. .PP Normally .UX programs may be invoked by more than one name using a mechanism called .I linking . .I Lsu requires that the program be invoked with a known name, because the name is used to determine how to verify the access rights of the user. If .I lsu is invoked by an unknown name, it denies access. .PP Normally the value of the user's .B PATH environment variable is not changed (unless, of course, he instructs .I lsu or .I nsu to read a startup file which does change that.) However, if the new identity is that of the superuser, a new default value is supplied. This is done because some directories are writable by users other than the superuser; a prime example is the current working directory, known as \*(lQ.\*(rQ, which is usually in nonprivileged user's values of .B PATH but should .B never be in that variable's value for the superuser. The danger is it makes accidentally invoking Trojan horses very likely. .SH Backward Compatibility .PP The programs .I lsu and .I nsu are based on an older program called .I su . However, .I lsu and .I nsu differ in several important respects. .PP First, under most .UX systems, .I su does not do any access list checking; if the user knows the password of the user whose identity he wishes to push, the push is allowed. This prevents the use of group accounts as described in the first section, because if the group account has a password someone can log into that account. The one system which provides an access list (4.3 BSD .UX ) does so only for the superuser and requires that the user be a member of the group .I wheel . Unfortunately, the file defining the members of that group is world-readable, and so an attacker knows which accounts he should try to penetrate in order to use .I su to get superuser privileges. Worse, no constraints on time or terminal are possible, and any account for which the user knows (or can guess) the password is vulnerable. .PP The version of .I su provided with .I lsu and .I nsu provides the security of those two programs but the environment of the original .I su . It uses only the access file, not the group file, to determine if a user can access the superuser, unless the access file is corrupt or nonexistant; in this case it acts just like .I nsu . This is very confusing for people who are used to using the original .I su and do not know it has been replaced! .PP Functionally, .I su is similar to .I nsu in that it requires the password of the account the identity of which is being pushed; however, the environment variable .B USER retains its original value, and on System V-based .UX es, .B HOME also retains its original value. This causes certain programs such as mail-reading programs to work with the original user rather than the new identity that was substituted. .SH Further Directions .PP There are two areas where security needs to be tightened; unfortunately, these are under kernel control. The first is that some .UX systems have a bug in the paging algorithm that does not check for text pages which have been written on; in this case, it is possible to alter one page of a setuid program to execute a shell, and then rerun the setuid program. The effect is to provide a setuid shell without needing a password. The only fix is to fix the kernel. .PP The second is that the access file is not encrypted. Were it encrypted, concealing its location would not be so critical, and in fact this is planned for the next version of these programs. .PP Currently these programs run on many different systems (see .B "Appendix III" for a list.) .I Lsu has been used for several months for administrative accounts and its users are quite pleased with the result. Not only do they not have to remember a second password, but .I lsu provides a consistent environment for all users of any particular account. This makes working together quite simple, and allows one procedure to be drawn up, rather than a different one for each user. All in all, it appears to be a success. .LP \kx\f2Acknowledgements:\fP\h'|\nxu+2u'\f2Acknowledgements:\fP Thanks to Barry Leiner for his review of, and comments on, the first draft, and Bob Van Cleef for his on the second. .SH References .nr rW \w'[BOUR84]\ 'u .nr tW \w'[DENN82]\ 'u .if \n(rW<\n(tW .nr rW \n(tW .nr tW \w'[JOY84]\ 'u .if \n(rW<\n(tW .nr rW \n(tW .nr tW \w'[MORR79]\ 'u .if \n(rW<\n(tW .nr rW \n(tW .nr tW \w'[UPM84]\ 'u .if \n(rW<\n(tW .nr rW \n(tW .de rP .IP \\$1 \\n(rWu .. .LP .ls 1 .rP [BOUR84] Bourne, S., \&\*(lQAn Introduction to the .UX Shell\*(rQ, in .I "\s-2UNIX\s0 User's Manual, Supplementary Documents" , .I "4.2 Berkeley Software Distribution" , .I "Virtual \s-2VAX\s0\*(dg-11 Version" , .FS \*(dg \s-2VAX\s0 is a Trademark of Digital Equipment Corporation. .FE Computer Science Division, Department of Electrical Engineering and Computer Science, University of California, Berkeley, CA (March 1984), as reprinted by the USENIX association. .rP [DENN82] Denning, Dorothy, .I "Cryptography and Data Security" , Addison-Wesley Publishing Company, Reading, MA \*(co 1984. .rP [JOY84] Joy, William, \&\*(lQAn Introduction to the C Shell\*(rQ, in .I "\s-2UNIX\s0 User's Manual, Supplementary Documents" , .I "4.2 Berkeley Software Distribution" , .I "Virtual \s-2VAX\s0-11 Version" , Computer Science Division, Department of Electrical Engineering and Computer Science, University of California, Berkeley, CA (March 1984), as reprinted by the USENIX association. .rP [MORR79] Morris, Robert and Thompson, Ken, \&\*(lQPassword Security: A Case History\*(rQ, .I CACM .B 22 (11), pp. 594 - 597. .rP [UPM84] \*-, .I "\s-2UNIX\s0 Programmer's Manual Reference Guide" , .I "4.2 Berkeley Software Distribution" , .I "Virtual \s-2VAX\s0-11 Version" , Computer Science Division, Department of Electrical Engineering snd Computer Science, University of California, Berkeley, CA (March 1984), as reprinted by the USENIX association. .LP .ls 2 .SH Appendix I. Syntax of the Terminal Field in the Access File .PP This appendix describes the semantics of the terminal field in the access file. In the grammar below, words in CAPITAL ROMAN TYPE are terminals and are defined after the grammar; words in \f2small italics\fP are nonterminals and are defined within the grammar itself. .DS I .nr vB (\w'::='u-\w'|'u)/2u .ds vB \h'\n(vBu'|\h'\n(vBu' .ta 4n 8n 12n 16n 20n \f2stat\fP ::= \f2expr\fP \f2expr\fP ::= \*(Lq(\*(Rq \f2expr\fP \*(Lq)\*(Rq \*(vB \*(Lq!\*(Rq \f2expr\fP \*(vB \f2expr\fP \*(Lq&\*(Rq \f2expr\fP \*(vB \f2expr\fP \*(Lq|\*(Rq \f2expr\fP \*(vB \f2ttyname\fP \*(vB \f2ttyspeed\fP \*(vB \*(Lqany\*(Rq \*(vB \*(Lqnone\*(Rq \f2ttyname\fP ::= \f2name\fP \*(vB \*(Lq+\*(Rq \f2name\fP \*(vB \*(Lq-\*(Rq \f2name\fP \*(vB \*(Lq*\*(Rq \f2name\fP \*(vB \f2pattern\fP \*(vB \*(Lq+\*(Rq \f2pattern\fP \*(vB \*(Lq-\*(Rq \f2pattern\fP \*(vB \*(Lq*\*(Rq \f2pattern\fP \f2ttyspeed\fP ::= \f2rate\fP \*(vB \*(Lq+\*(Rq \f2rate\fP \*(vB \*(Lq-\*(Rq \f2rate\fP \*(vB \*(Lq*\*(Rq \f2rate\fP \f2rate\fP ::= RELATION NUMBER \f2name\fP ::= STRING \f2pattern\fP ::= \*(Lq"\*(Rq STRING \*(Lq"\*(Rq .DE where .I RELATION is any of: .KS .TS center; c l c l c l. @ (equal to) <> (not equal to) > (greater than) \&= (equal to) >< (not equal to) >= (greater than or equal to) < (less than) != (not equal to) <= (less than or equal to) .TE .KE and .I NUMBER is any non-negative integer and .I STRING is any list of characters. In a string, the following characters must be preceded by a \*(lQ\e\*(rQ or they will terminate the string and produce unexpected results: .KS .TS center; c l c l c l. \&, (comma) \&= (equal sign) > (greater than sign) \&< (less than sign) ! (exclamation point) ( (left parenthesis) \&) (right parenthesis) \&| (vertical bar) & (ampersand) \&+ (plus sign) \&- (hyphen) * (asterisk) \e (backslash) .TE .KE .PP The symbol \*(Lq+\*(Rq means the characteristic applies to the standard input device only, \&\*(Lq-\*(Rq means the characteristic applies to the standard output device only, and \*(Lq*\*(Rq means the characteristic applies to the standard error device only. .SH Appendix II. Syntax of the Time Field in the Access File .PP This appendix describes the semantics of the time field in the access file. In the grammar below, words in CAPITAL ROMAN TYPE are terminals and are defined after the grammar; words in \f2small italics\fP are nonterminals and are defined within the grammar itself. .DS I .nr vB (\w\*(Rq::=\*(Rqu-\w\*(Rq|\*(Rqu)/2u .ds vB \h\*(Rq\n(vBu\*(Rq|\h\*(Rq\n(vBu\*(Rq .ta 4n 8n 12n 16n 20n \f2stat\fP ::= \f2expr\fP \f2expr\fP ::= \*(Lq(\*(Rq \f2expr\fP \*(Lq)\*(Rq \*(vB \*(Lq!\*(Rq \f2expr\fP \*(vB \f2expr\fP \*(Lq&\*(Rq \f2expr\fP \*(vB \f2expr\fP \*(Lq|\*(Rq \f2expr\fP \*(vB \f2day_of_year\fP \f2day_of_week\fP \f2time_of_day\fP \*(vB \f2day_of_week\fP \f2day_of_year\fP \f2time_of_day\fP \*(vB \f2day_of_year\fP \f2time_of_day\fP \*(vB \f2day_of_year\fP \f2day_of_week\fP \*(vB \f2day_of_week\fP \f2time_of_day\fP \*(vB \f2day_of_week\fP \f2day_of_year\fP \*(vB \f2day_of_year\fP \*(vB \f2day_of_week\fP \*(vB \f2time_of_day\fP \*(vB \*(Lqany\*(Rq \*(vB \*(Lqnone\*(Rq \f2day_of_week\fP ::= DAY_OF_WEEK \*(vB DAY_OF_WEEK \*(Lq-\*(Rq DAY_OF_WEEK \*(vB \f2day_of_week\fP \*(Lq,\*(Rq \f2day_of_week\fP \f2time_of_day\fP ::= \f2time_of_day\fP \*(Lq-\*(Rq \f2time_of_day\fP \*(vB \f2time_of_day\fP \*(Lq,\*(Rq \f2time_of_day\fP \*(vB NUMBER \*(Lq:\*(Rq NUMBER \*(Lq:\*(Rq NUMBER MERIDIAN \*(vB NUMBER \*(Lq:\*(Rq NUMBER MERIDIAN \*(vB NUMBER MERIDIAN \*(vB NUMBER \*(Lq:\*(Rq NUMBER \*(Lq:\*(Rq NUMBER \*(vB NUMBER \*(Lq:\*(Rq NUMBER \*(vB NUMBER \*(vB SPECIAL_TIME \f2day_of_year\fP ::= MONTH NUMBER \*(Lq,\*(Rq NUMBER \*(vB MONTH NUMBER \*(vB MONTH \*(Lq,\*(Rq NUMBER \*(vB MONTH \*(vB NUMBER \*(Lq/\*(Rq NUMBER \*(Lq/\*(Rq NUMBER \*(vB NUMBER \*(Lq/\*(Rq NUMBER .DE where .I NUMBER is a positive integer (if not valid, an error message is printed), .I MERIDIAN is either \*(lQam\*(rQ or \*(lQpm\*(rQ, .I SPECIAL_TIME is either \*(lQnoon\*(rQ or \*(lQmidnight\*(rQ, .I DAY_OF_WEEK is any of: .KS .TS center; l l l l. Sunday Tuesday Thursday Saturday Monday Wednesday Friday weekdays .TE .KE and .I MONTH is any of: .KS .TS center; l l l l. January April July October February May August November March June September December .TE .KE With any of these keywords, only enough of the name to identify a unique name must be given, and case is ignored. (Note that \*(Lqa\*(Rq is not a unique abbreviation for \*(lQam\*(rQ, because it is also the first letter in \*(lQapril\*(rQ and \*(lQaugust\*(rQ.) .SH Appendix III. Compilation and Installation .PP When you get the .I lsu package, type .DS C sh Setup .DE This program asks you to enter your system type as defined below: .KS .TS center, doublebox; c | c l | l. \f2enter\fP \f2if your system is\fP = BSD4_2 Berkeley Software Distribution Release 4.2 _ BSD4_3 Berkeley Software Distribution Release 4.3 _ DYNIX2_0 Sequent Balance Dynix Release 2.0 _ NPSN3 NAS Processing System Network Build 3 _ ROS3_3 Ridge Operating System Release 3.3 _ SGI2_3 Silicon Graphics IRIS Graphics Library 2 - Workstation 2.3 _ SGI3_4 Silicon Graphics IRIS Graphics Library 3 - Workstation 3.4 _ SGI3_5 Silicon Graphics IRIS Graphics Library 3 - Workstation 3.5 _ SUN4_2 Sun Microsystems UNIX Release 4.2 _ SYSV AT&T System V _ UNICOS Cray UNIX Operating System _ UTS Amdahl UTS .TE .KE (You can supply the type as a command-line argument, too.) This will link the command file to make the programs for the system to \&\*(lQMakefile\*(rQ. Once this is done, replace the lines .DS B LSUPERM="./.lsu" SUPERM="./.su" LOG="./.log" DESTDIR="/usr/local/bin/" .DE with the full path names of the access files for .I lsu , both .I nsu and .I su , the log file, and the directory where the executables are to be placed, respectively. Then type .DS C make .DE to compile the programs and .DS C make install .DE to install them. .PP If your system is not one of the ones named above, look in the directory \*(lQMake\*(rQ for a file that will work, or build one using them as a model. The command files all are named \*(lQMake.\f2sys\fP\*(rQ, where .I sys is the system type entered to .I Setup (see above.) Do this for your system, too, and add the new abbreviation to .I Setup . Then look in the directory \*(lQEd\*(rQ \*- these files edit part of \*(lQlsu.H\*(rQ to change the names of the log file and the permission files. The output will look something like this (the lines that begin with \*(lQbuf[\*(rQ will be different; on a System V-based version of .UX , the .I ed commands will have some extra backslashes. This is due to the difference between BSD and System V pattern matchers.) .DS B /^\e/\e* begin SUPERM \e*\e/\e\e$/,/^\e/\e* end SUPERM \e*\e/\e\e$/c /* begin SUPERM */\e buf[0] = '.';\e buf[1] = '/';\e buf[2] = '.';\e buf[3] = 's';\e buf[4] = 'u';\e buf[5] = '\e0';\e /* end SUPERM */\e . /^\e/\e* begin LSUPERM \e*\e/\e\e$/,/^\e/\e* end LSUPERM \e*\e/\e\e$/c /* begin LSUPERM */\e buf[0] = '.';\e buf[1] = '/';\e buf[2] = '.';\e buf[3] = 'l';\e buf[4] = 's';\e buf[5] = 'u';\e buf[6] = '\e0';\e /* end LSUPERM */\e . /^\e/\e* begin LOG \e*\e/\e\e$/,/^\e/\e* end LOG \e*\e/\e\e$/c /* begin LOG */\e buf[0] = '.';\e buf[1] = '/';\e buf[2] = '.';\e buf[3] = 'l';\e buf[4] = 'o';\e buf[5] = 'g';\e buf[6] = '\e0';\e /* end LOG */\e . 1,$w q .DE This is used as input to .I ed to set up the names of the log file and the permission files without putting the strings into a form that can be read by looking for \s-2ASCII\s0 strings in the executable.) Figure out which one works. Finally, look in \*(lQsysdep.h\*(rQ and add the appropriate definitions. .PP Messages indicating log and access file problems are sent to the user \*(lQlsumaint\*(rQ. Either create a mail alias by this name to reroute the messages to the appropriate people, or change the value of the constant .B LSUMAINT in \*(lQsysdep.h\*(rQ. .SH Appendix IV. Manual Pages .PP The following pages are the manual pages for .I lsu , .I nsu , and .I su , version 3.0. 2 .SH Appendix I. Syntax of the Terminal Field in the Access File .PP This appendix describes the semantics of the terminal field in the access file. In the grammar below, words in CAPITAL ROMAN TYPE are terminals and are defined after the grammar; words in \f2small italics\fP are nonterminals and are defined within the grammar itself. .DS I .nr vB (\w'::='u-\w'|lsu/README 644 1625 12 40342 5224607510 5635 SUBSTITUTE USER ========== ==== lsu: This program is designed to give specific individuals the ability to get a setuid-to-user shell without needing the user password. That way, you need not change the user password after giving some people temporary access to that user. Using this program requires specific permission. nsu: This program is designed to give specific individuals the ability to get a setuid-to-user shell. You need the user password. Using this program does not require specific permission unless the user being su'ed to is listed in the permission file; then it does. su: This program does the same as nsu, except the USER and (on System V based UNIXes) HOME environment variables are not reset. Calls are: lsu [ - ] [ name ] [ -c args ] nsu [ - ] [ name ] [ -c args ] su [ - ] [ name ] [ -c args ] where: - means to start the shell as though it were a login shell name means to start a shell with the ID of name; -- is same as omitting this argument -c args all args are passed as argument 1, ... to the shell For example, to read /usr/lib/uucp/L.sys (normally readable only by root), type lsu root -c "cat /usr/lib/uucp/L.sys" (both the Bourne shell and C shell require the command be one argument.) The user environment is unchanged EXCEPT for the following environment variables: USER set to the login name of the user with the new UID ** not reset by su HOME set to the home directory of USER ** not reset by su on System V based systems SHELL set to the shell of USER PATH set to eliminate specific directories if new UID is 0 The UID and GID are reset to that of the new USER, and argument #0 is "lsu" ("nsu", "su") or "-lsu" ("-nsu", "-su") depending on whether a '-' option is given. ------------------ FILES ------------------ Relevant files are: log log file .lsu lsu permission file .su nsu, su permission file This describes what the entries in each file are. Log File: The log file has lines of the form PR MM/dd hh:mm ? ttyxx users - message where: PR code for program executed "SU" is the code for "lsu" "su" is the code for "su" MM 2 digit number of month (01=January, 12=December) dd 2 digit number of day of month (01-31) hh 2 digit number of hour (24-hr clock, 00-23) mm 2 digit number of minute (00-59) ? code for result of running the program "+" for success "-" for failure ttyxx name of device from which program was run users indicates both who runs the program and who is being lsu'ed (su'ed) to; format is oldid-newid where oldid user name of user executing program newid user name of user su'ing or lsu'ing to if user name for either not available, the UID is printed in parentheses. message informational message: on success, has form "became newid (UID n, GID m)" on failure, is error message The log file line uses (an equivalent to) the following printf format string to print the log entry lines %2s %02d/%02d %02d:%02d %c %-6s %-17s - %s\n ^^^ ^^^^ ^^^^ ^^^^ ^^^^ ^^ ^^^^ ^^^^^ ^^ | | | | | | | | +------ message | | | | | | | +------------- users (no blanks) | | | | | | +------------------ device | | | | | +---------------------- ? | | | | +-------------------------- minutes | | | +------------------------------- hours | | +------------------------------------ day of month | +----------------------------------------- number of month +----------------------------------------------code for program The su programs are designed so that a user is never told why an attempt to run them fails (this is intended to prevent someone from figuring out what the password is, or what times the action is allowed, etc., by trial and error.) Since the log file does contain error messages, it must be and writable only by the superuser. Permission File: The permission files for all incantations have the same format, so this is described here. Differences are described below. Comments are allowed; lines with '#' in column 1 are ignored. Each line has the format userid<\t>userlist<\t>ttys<\t>time where <\t> is an ASCII horizontal tab (octal 011) and: userid is a login name userlist is a (list of comma-separated) login name(s), or the word "any" ttys is a (list of comma-separated) terminal devices, or the word "any" time is (a list of comma-separated) time intervals, or the word "any" The syntax of the "ttys" field is: ttys == '(' ttys ')' /* grouping */ '!' ttys /* not */ ttys '&' ttys /* and */ ttys '|' ttys /* or */ ttys ',' ttys /* or */ ttyname /* terminal name */ ttyspeed /* terminal speed */ ttyname == NAME (ie, ttyi9) the input, output, and error devices all have this name '+' NAME (ie, +ttyi9) the input device has this name '-' NAME (ie, -ttyi9) the output device has this name '*' NAME (ie, *ttyi9) the error device has this name '"' PATTERN '"' (ie, "tty.*") the input, output, and error devices all have names matching this pattern '+' '"' PATTERN '"' (ie, +"tty.*") the input, output, and error devices all have names matching this pattern the input device has this name '-' '"' PATTERN '"' (ie, -"tty.*") the input, output, and error devices all have names matching this pattern the output device has this name '*' '"' PATTERN '"' (ie, *"tty.*") the input, output, and error devices all have names matching this pattern the error device has this name ttyspeed == rate (ie, <9600) the input, output, and error device speeds all meet this condition '+' rate (ie, +<9600) the input device speed meets this condition '-' rate (ie, -<9600) the output device speed meets this condition '*' rate (ie, *<9600) the error device speed meets this condition rate == '=' SPEED | /* equal to SPEED */ '@' SPEED | /* equal to SPEED */ '<' SPEED | /* less than SPEED */ '<>' SPEED | /* not equal to SPEED */ '<=' SPEED | /* less than or equal to SPEED */ '>' SPEED | /* greater than SPEED */ '>=' SPEED | /* greater than or equal to SPEED */ '><' SPEED | /* not equal to SPEED */ '!=' SPEED ; /* not equal to SPEED */ where NAME is the file name of the device, PATTERN is a pattern ad described in ed(1), and SPEED is a numeric baud rate. When the su programs start up, they determine the file name and speed of the input, output, and error devices (ie, the ones associated with stdin, stderr, stderr.) These are then compared as indicated above. If the final "ttys" expression is satisfied, the tty is valid; if not, the tty is invalid. Some examples of "ttys" field entries are: (ttyi9|console)&>4800 means "the input, output, and error file descriptors of this program are either from ttyi9 or the console and the speed of all devices is greater than 4800 baud". +>2400&->2400&*>120 means "both the input and output device speeds are greater than 2400 baud and the error device speed is greater than 120 baud." The syntax of the "time" field is: time == '(' time ')' | /* grouping */ '!' time | /* not */ time '&' time | /* and */ time '|' time | /* or */ time ',' time | /* or */ day_of_year day_of_week time_of_day ; where: day_of_year == month number ',' number (ie, Aug 29, 1986) month number (ie, Aug 29) month ',' number (ie, Aug, 1986) month (ie, Aug) number '/' number '/' number (ie, 8/29/86) number '/' number (ie, 8/29) day_of_year '-' day_of_year (ie, 8/29-8/31) means from the first to the second day_of_year ',' day_of_year (ie, 8/29,8/31) means the first or the second day_of_week == special_day Any of "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", or "Saturday" day_of_week '-' day_of_week (ie, Monday-Tuesday) means from the first to the second day_of_week ',' day_of_week (ie, Monday,Friday) means the first or the second time_of_day == number ':' number ':' number mer (ie, 8:12:56 pm) number ':' number ':' number (ie, 20:12:56) number ':' number meridian (ie, 8:12 pm) number ':' number (ie, 20:12) number meridian (ie, 8 pm) number (ie, 20) special_time Any of "noon", "midnight" time_of_day '-' time_of_day (ie, 8am-4pm) means from the first to the second time_of_day ',' time_of_day (ie, 8am,4pm) means the first or the second "meridian" is "am" or "pm". Enough of "special_day" and "special_time" words to identify the word uniquely must be given; for example, any of "Au", "Aug", "Augu", "Augus", or "August" will match "August"; but "A" will not, since it might also be "Am" or "Any". Note also case is irrelevant. Examples of legal times are: August 10, 1986 - August 20, 1986 Monday - Friday 9AM - 5PM This describes the working week between August 10 and August 20 in 1986 Tuesday noon - midnight This describes times on Tuesdays from noon until midnight 8/10/86 - 8/20/86 Mon-Fri 9-17 This is the same as the first description An entry in the permission file is "satisfied" if the terminal satisfies the "ttys" field, the date satisfies the "time" field, and the user running the program and the user being substituted satisfy the first two fields (the interpretation of the first two fields varies.) lsu Permission File Interpretation: Lsu interprets the "userid" field to be the name of the user doing the lsu, and "userlist" the list of user names to whom he/she can lsu. The "userid" can only lsu to those users named in the list; however, one user may have multiple entries, and in that case if any entry is satisfied the lsu is permitted. Sample lsu permission file entries and their interpretation: mab Any ttyi9,console Mon-Fri 9am-5pm "mab" can lsu to any other user from ttyi9 or the console during any day of the working week from 9am to 5pm rlb root,bin,staff >=9600 Aug 10 - Aug 20 Mon "rlb" can lsu to "root", "bin", or "staff" from any terminal that operates at 9600 baud or greater on any Monday from August 10 to August 20 of the current year su Permission File Interpretation: Su interprets the "userid" field to be the name of the user who is being su'ed to, and "userlist" the list of names of users who can su to the userid. The user can su to any unlisted "userid", or any listed "userid" if he/she is named in the associated "userlist". He/she cannot su to a "userid" if he/she is not named in the "userlist", even though the user may know the "userid"'s password. If there are multiple lines for one "userid", and any of the entries is satisfied, the su is permitted. Sample su permission file entries and their interpretation: mab Any ttyi9,console Mon-Fri 9am-5pm Any user may su to "mab" from ttyi9 or the console during the working week from 9am to 5pm. rlb bin,staff >=9600 Aug 10 - Aug 20 Mon "bin" or "staff" can su to "rlb" from any terminal terminal that operates at 9600 baud or greater on any Monday from August 10 to August 20 of the current year ------------------ COMPILATION ------------------ When you get this, type "sh Setup". This program asks you to enter your system type as defined below: enter if your system is ----- ----------------- BSD4_2 Berkeley Software Distribution Release 4.2 BSD4_3 Berkeley Software Distribution Release 4.3 DYNIX2_0 Sequent Balance Dynix Release 2.0 NPSN3 NAS Processing System Network Build 3 ROS3_3 Ridge Operating System Release 3.3 SGI2_3 Silicon Graphics IRIS Graphics Library 2 - Workstation 2.3 SGI3_4 Silicon Graphics IRIS Graphics Library 3 - Workstation 3.4 SUN4_2 Sun Microsystems UNIX Release 4.2 SYSV AT&T System V UNICOS Cray UNIX Operating System UTS Amdahl UTS (You can supply the type as a command-line argument, too.) This will link the appropriate Makefile. Once this is done, type "make" to compile and "make install" to install. Don't forget to check the names of the log file (LOG) and the permission files (SUPERM, LSUPERM) in Makefile to be sure they are correct. If your system is not one of the ones named above, look in the directory Make for a file that will work, or build one using them as a model. The makefiles all are named "Make.", where "" is the system type entered to Setup (see above.) Do this for your system, too, and add the new abbreviation to Setup. Then look in the directory Ed -- these files edit part of lsu.c.src to change the names of the log file and the per- mission file. The output will look something like this (the lines that begin "buf[" will be different; on a System V-based version of UNIX, the ed commands will have some extra backslashes. This is due to the difference between BSD and System V pattern matchers.) /^\/\* begin SUPERM \*\/\\$/,/^\/\* end SUPERM \*\/\\$/c /* begin SUPERM */\ buf[0] = '.';\ buf[1] = '/';\ buf[2] = '.';\ buf[3] = 's';\ buf[4] = 'u';\ buf[5] = '\0';\ /* end SUPERM */\ . /^\/\* begin LSUPERM \*\/\\$/,/^\/\* end LSUPERM \*\/\\$/c /* begin LSUPERM */\ buf[0] = '.';\ buf[1] = '/';\ buf[2] = '.';\ buf[3] = 'l';\ buf[4] = 's';\ buf[5] = 'u';\ buf[6] = '\0';\ /* end LSUPERM */\ . /^\/\* begin LOG \*\/\\$/,/^\/\* end LOG \*\/\\$/c /* begin LOG */\ buf[0] = '.';\ buf[1] = '/';\ buf[2] = '.';\ buf[3] = 'l';\ buf[4] = 'o';\ buf[5] = 'g';\ buf[6] = '\0';\ /* end LOG */\ . 1,$w q (It is used as input to "ed" to set up the names of the log file and the permission files without putting the strings into a form that can be read by looking for ASCII strings in the executable.) Figure out which one works. Finally, look in "sysdep.h" and add the appropriate definitions. ------------------ VERSIONS ------------------ 0.0 August 22, 1986 (not numbered in distributed version) Programmer: mab@riacs.ARPA Unofficial alpha release; used to test portability within the NPSN. 0.1 September 2, 1986 Programmer: mab@riacs.ARPA First official alpha version; added code to delete specified directories from PATH environment variable when lsu'ing to a user with a UID of 0, or (if no PATH environment variable) to supply a default root path. The default root path, and the list of directories to be deleted, vary from system to system; they are set in "sysdep.h". Note that the null directory "::" is always deleted. 1.0 October 23, 1986 Programmer: mab@riacs.ARPA Added support for several other versions of UNIX, and cleaned up the code. 2.0 December 1, 1986 Programmer: mab@riacs.ARPA First production version; expanded syntax of ttys field, merged lsu and su, 2.1 January 13, 1988 Programmer: matt.bishop@dartmouth.edu, crabb@nas.nasa.gov Miscellaneous changes detailed below: * If you have root nasops,vancleef any any in the access control file and crabb su's to root, it should fail. It succeeds. The bug is we check to see if you're in towholist, and if so reject the su. We should check to see if you're NOT in towholist ... perm.c, 165-169: changed "&& towholist" to "&& !towholist" in the "if" statement * if root is not listed in the list of authorized users in the access control file, it cannot su to another account. Also, unless authorized, no-one can su to their own account. The bug is that we don't special-case these situations ... perm.c, 131: change condition in "else if" from "isinlist(who, okuser) == NULL" to "isinlist(who, okuser) == NULL && curpw.pw_uid != 0 && strcmp(curpw.pw_name,who) != 0". * on a BSD based system you get no search path. sysdep.h, line 241: change the "#ifdef" to "#ifndef" * if you log to a file or a program you get a core dump. The first logging message is printed before everything is set up. lsu.c, lines 72-75: exchange the "init" and "openlog" lines. * if an entry is given in the su access control file, the su still fails with "user not listed"; but if that file specified "any" could switch to that user, everything worked. This is because the meaning of the lines is reversed for su and lsu, but the test does not reflect this ... perm.c, line 131: change the first argument of "isinlist" from "who" to "progname == SU ? curpw.pw_name : who" 8:12 pm) number ':' number (ie, 20:12) number meridian (ie, 8 pm) number (ie, 20) special_time Any of "noon", "midnight" time_of_day '-' time_of_day (ie, 8am-4pm) means from the first to the second time_of_daylsu/log.c 644 1625 12 4057 5106523462 5667 /* * LSU -- local superuser (logging routines) * * Author: * Matt Bishop * Research Institute for Advanced Computer Science * NASA Ames Research Center * Moffett Field, CA 94035 * * mab@riacs.edu ARPA * ...!decvax!decwrl!riacs!mab UUCP * ...!ihnp4!ames!riacs!mab UUCP * * Copyright (c) 1986. */ #include "lsu.h" /* * open the log file */ openlog() { char logfile[BUFSIZ]; /* log file name */ int logfd; /* log file descriptor */ int lerr = 0; /* 1 if error opening log file */ /* * open the log file, creating it if need be */ MAKELOG(logfile); if ((logfd = open(logfile, O_WRONLY|O_APPEND|O_CREAT, 0600)) < 0) lerr = 1; CLEARNAME(logfile); /* * get the file pointer */ if (logfd >= 0) logfp = fdopen(logfd, "a"); else logfp = NULL; /* * if anything went wrong, log the error if possible */ if (lerr) err(L_INFO|F_COND|F_LOG|F_SYS, "log file"); } /* * write a message to the log */ logmessage(flag, logmsg) unsigned int flag; /* L_YES if successful */ char *logmsg; /* message to be logged */ { char buf[BUFSIZ]; /* used to format log file entry */ /* * if the log file is closed, * what can you do? */ if (logfp == NULL) return; /* * date it and identify the program */ (void) fprintf(logfp, "%s %s ", progis->lname, date); /* * success or failure? */ switch(flag&L_MASK){ case L_INFO: (void) putc('i', logfp); break; case L_YES: (void) putc('+', logfp); break; case L_NO: (void) putc('-', logfp); break; case L_CHK: (void) putc('v', logfp); break; default: (void) putc('?', logfp); break; } /* * tty name */ (void) fprintf(logfp, " %-7s ", tty); /* * identify the user */ if (curpw.pw_name == NULL || curpw.pw_name[0] == '\0') (void) sprintf(buf, "(%d)-", curpw.pw_uid); else (void) sprintf(buf, "%s-", curpw.pw_name); if (towho != NULL) (void) strcat(buf, towho); else if (newpw.pw_name != NULL) (void) strcat(buf, newpw.pw_name); (void) fprintf(logfp, "%-17.17s [%05d] - ", buf, stamp); /* * give the message */ (void) fprintf(logfp, "%s", logmsg); } on "rlb" can lsu to "root", "bin", or "staff" from any terminal that operates at 9600 baud or greater on any Monday from August 10 to August 20 of the current year su Permission File Interpretation: Su interprets the "userid" field to be the name of the user who is being su'ed to, and "userlist" the list of names of users who can su to the userid. The user can su to any unlisted "userid", or any listed "userid" if he/she is named in the associatedlsu/lsu.c 644 1625 12 20565 5106525322 5730 /* * LSU -- local superuser * * Author: * Matt Bishop * Research Institute for Advanced Computer Science * NASA Ames Research Center * Moffett Field, CA 94035 * * mab@riacs.edu ARPA * ...!decvax!decwrl!riacs!mab UUCP * ...!ihnp4!ames!riacs!mab UUCP * * Copyright (c) 1986. */ #include "lsu.h" /* * global variables */ char *progname = NULL; /* program name */ struct perms *progis; /* what the program runs as */ struct perms runas[] = { /* legal program names -- MUST be one of these */ { "SU", "lsu", }, /* local super user */ { "su", "su", }, /* super user */ { "nu", "nsu", }, /* new super user */ { "cu", "csu", }, /* check access file syntax */ { "??", NULL, }, /* end of list marker */ }; FILE *logfp = NULL; /* log file pointer */ char *date; /* date of run */ char *tty; /* terminal name or background */ char *towho = NULL; /* command line argument */ struct passwd curpw; /* information about who's running the program */ struct passwd newpw; /* information about who you're lsu'ing to */ char *shell = LSUSHELL; /* shell to be exec'ed */ int stamp = -1; /* PID stamp of this process (for logging) */ int dash = 0; /* 1 if a login shell to be used */ struct sv shvar[] = LSUVARS; /* list of shell variables to change */ unsigned int success = F_NONE; /* F_NONE if l/n/csu is to succeed */ #ifdef lint /* * these are here to shut lint up; they are * used for system errors */ int errno; /* system error number */ int sys_nerr; /* number of elements in sys_errlist[] */ char *sys_errlist[1]; /* list of system error messages */ #else /* * these are used to keep track * of how and when the binaries * were made */ char *lsu_system = SYSTEM; /* what operating system this was made for */ char *lsu_version = VERSION; /* what version this is */ #endif main(argc, argv, envp) int argc; /* number of arguments */ char **argv; /* argument list */ char **envp; /* environment list */ { char buf[BUFSIZ]; /* buffer for error and other messages */ /* * set up the password fields (used for error messages * if no password yet demanded), open the error log, * set up the program's name, and initialize the * structures this goodie needs */ init(); setname(argv[0]); openlog(); /* * if checking syntax only, do so and stop */ if (progname == CSU) exit(permsyntax(++argv)); /* * process any arguments */ if (argc > 1 && strcmp(argv[1], "-") == 0){ dash = 1; argc--; argv++; } if (argc > 1){ if (strncmp(argv[1], "--", 2) != 0) towho = strsave(argv[1]); argc--; argv++; } /* * figure out who to become */ if (towho != NULL) (void) strcpy(buf, towho); else if (progname == SU || progname == NSU) (void) strcpy(buf, LSUUSER); else buf[0] = '\0'; /* * see if the user has permission to change to * whoever he/she wants */ perms(buf); /* * at this point buf[] contains the new user * gather new user data and check the password */ getnewuser(buf); chkpasswd(); /* * get the right shell * if that fails, assume the default shell * also reset the argument list */ getshell(argv, envp); /* * reset the path environment variable if need be */ if (newpw.pw_uid == 0) chkpath(&envp); /* * reset the UID and GID of this process and log it */ if (setgid(newpw.pw_gid) < 0) err(F_COND|F_SYS, buf); if (setuid(newpw.pw_uid) < 0) err(F_COND|F_SYS, buf); /* * check for success or failure here */ switch(success){ case F_COND: if ((progname == NSU || progname == SU) && (newpw.pw_uid == SUPERUID)) break; /* FALLS THROUGH */ case F_FAIL: logmessage(L_NO, "permission denied\n"); fprintf(stderr, "%s: permission denied\n", progname); exit(1); default: break; } (void) sprintf(buf, "became %s (UID %d, GID %d)\n", newpw.pw_name, newpw.pw_uid, newpw.pw_gid); logmessage(L_YES, buf); if (logfp != NULL) (void) fclose(logfp); /* * now overlay the shell */ execve(shell, argv, envp); /* * if you get here the execve failed, so ... */ openlog(); err(F_COND|F_SYS, "execve"); /* NOTREACHED */ } /* * initialize -- get the user information and the date * and open the log file for appending */ init() { long clock; /* internal representation of current time */ char *t; /* used to locate terminal */ char buf[BUFSIZ]; /* error message buffer */ struct passwd *pw; /* password file entry */ struct tm *tick; /* local time */ /* * get the stamp (for errors and logging) */ stamp = getpid(); /* * get the date */ clock = time((long *) 0); tick = localtime(&clock); (void) sprintf(buf, "%02d/%02d %02d:%02d", tick->tm_mon+1, tick->tm_mday, tick->tm_hour + 1, tick->tm_min); date = strsave(buf); /* * get the terminal number * if none available, it's being run from background */ if ((t = ttyname(0)) == NULL && (t = ttyname(1)) == NULL && (t = ttyname(2)) == NULL) t = "background"; if (strncmp(t, "/dev/", strlen("/dev/")) == 0) t += strlen("/dev/"); tty = strsave(t); /* * get the password file information for this user * if none, log the error using the current UID and die */ curpw.pw_name = NULL; curpw.pw_uid = getuid(); curpw.pw_gid = getgid(); if ((pw = getpwuid(curpw.pw_uid)) == NULL){ (void) sprintf(buf, "no user with UID %d\n", curpw.pw_uid); err(F_COND, buf); } curpw.pw_name = strsave(pw->pw_name); curpw.pw_passwd = strsave(pw->pw_passwd); curpw.pw_dir = strsave(pw->pw_dir); curpw.pw_shell = strsave(pw->pw_shell); } /* * get shell -- set up argv0 and shell */ getshell(argv, envp) char **argv; /* argument list */ char **envp; /* environment list */ { register int i, j; /* counter in for loops */ char *p; /* used to set up arg 0 to shell */ char *argv0; /* argument 0 */ char tmpbuf[BUFSIZ]; /* temporary buffer */ /* * set up the shell */ if (newpw.pw_shell[0] != '\0') shell = strsave(newpw.pw_shell); else{ (void) free(newpw.pw_shell); newpw.pw_shell = strsave(LSUSHELL); shell = strsave(LSUSHELL); } /* * get the last part of the program name */ argv0 = strsave(progname); if ((p = rindex(argv0, '/')) == NULL){ (void) sprintf(tmpbuf, "/%s", progname); p = tmpbuf; } /* * if this is to be a login shell, arg 0 goes to '-' */ if (dash) *p = '-'; else p++; /* * now stuff this in argument 0 */ argv[0] = strsave(p); /* * set up the environment */ for(j = 0; envp[j] != NULL; j++){ for(i = 0; shvar[i].splate != NULL; i++){ if (strncmp(envp[j], shvar[i].splate, shvar[i].len) == 0 && envdoit(shvar[i].vused)){ (void) sprintf(tmpbuf, shvar[i].splate, *shvar[i].cval); envp[j] = strsave(tmpbuf); } } } } /* * determine if a shell variable should be replaced */ int envdoit(when) unsigned int when; { /* * if always done, succeed */ if (bitset(when, V_ALWAYS)) return(1); /* * if never done, fail */ if (bitset(when, V_NONE)) return(0); /* * if done only for a login shell and this isn't one, fail */ if (bitset(when, V_ANDLOGIN) && !dash) return(0); /* * if done for a login shell or anything else, * and this is a login shell, succeed */ if (bitset(when, V_ORLOGIN) && dash) return(1); /* * on a per-program basis */ if (bitset(when, V_CSU) && progname == CSU) return(1); if (bitset(when, V_LSU) && progname == LSU) return(1); if (bitset(when, V_NSU) && progname == NSU) return(1); if (bitset(when, V_SU) && progname == SU) return(1); /* * failure */ return(0); } /* * reset the PATH variable to a default, safe one */ chkpath(envp) char ***envp; { register int i, j; /* counter in a for loop */ union { /* used to allocate space for new environment */ char **cpp; /* char ** space */ char *cp; /* char * space */ } u; /* the union */ /* * see if there's a PATH environment variable */ for(i = 0; (*envp)[i] != NULL; i++) if (strncmp((*envp)[i], "PATH=", 5) == 0){ /* * there is; put in the new path and return */ (*envp)[i] = strsave(LSUPATH); return; } /* * allocate space for a new environment */ if ((u.cp = malloc((unsigned) (sizeof(char **) * (i+2)))) == NULL) err(F_SYS|F_COND, "environment"); /* * copy the environment over */ for(j = 0, i = 0; (*envp)[i] != NULL; i++) u.cpp[j++] = (*envp)[i]; /* * insert the new path */ u.cpp[j++] = strsave(LSUPATH); /* * end the new environment * and save it where the old one is */ u.cpp[j] = NULL; *envp = u.cpp; } p (see above.) Do this for your system, too, and add the new abbreviation to Setup. Then look in the directory Ed -- these files edit parlsu/pat.c 644 1625 12 6051 5106523462 5666 /* * LSU -- local superuser * * Author: * Matt Bishop * Research Institute for Advanced Computer Science * NASA Ames Research Center * Moffett Field, CA 94035 * * mab@riacs.edu ARPA * ...!decvax!decwrl!riacs!mab UUCP * ...!ihnp4!ames!riacs!mab UUCP * * Copyright (c) 1986. */ #include "lsu.h" #ifdef SYSV_TYPE # define INIT register char *sp = instring; # define GETC() (*sp++) # define PEEKC() (*sp) # define UNGETC(c) (--sp) # define RETURN(c) return; # define ERROR(c) regerr(c) # include #endif #ifdef BSD4_TYPE char *re_comp(); /* compile regular expression */ #endif /* * global variables */ static char *pattern; /* points to typed-in pattern */ #ifdef SYSV_TYPE static char patcomp[BUFSIZ]; /* buffer for compiled pattern */ #endif /* * smatch -- set up the pattern to be matched; bomb on error */ int smatch(pat) char *pat; /* pattern to be matched */ { #ifdef BSD4_TYPE char buf[BUFSIZ]; /* buffer for error message */ register char *p; /* points to any error message */ /* * compile the pattern */ if ((p = re_comp(pat)) != NULL){ (void) sprintf(buf, "%s: %s\n", pat, p); err(L_INFO|F_COND|F_PERM, buf); } #endif #ifdef SYSV_TYPE pattern = pat; (void) compile(pat, patcomp, &patcomp[BUFSIZ], '\0'); #endif } /* * match -- compare a string to the compiled pattern */ match(str) char *str; /* string to be compared */ { #ifdef BSD4_TYPE char buf[BUFSIZ]; /* buffer for error message */ /* * compare appropriately */ switch(re_exec(str)){ case 1: /* success */ return(1); case 0: /* failure */ return(0); default: (void) sprintf(buf, "Internal error comparing %s to %s\n", str, pattern); err(L_INFO|F_COND, buf); } return(0); #endif #ifdef SYSV_TYPE /* * compare appropriately */ return(step(str, patcomp)); #endif } #ifdef SYSV_TYPE /* * regerr -- pattern matching error handler */ regerr(n) int n; /* number of error */ { char buf[BUFSIZ]; /* buffer for error message */ switch(n){ case 11: (void) sprintf(buf, "%s: range endpoint too large", pattern); break; case 16: (void) sprintf(buf, "%s: bad number", pattern); break; case 25: (void) sprintf(buf, "%s: \"\\digit\" out of range", pattern); break; case 36: (void) sprintf(buf, "%s: illegal or missing delimiter", pattern); break; case 41: (void) sprintf(buf, "%s: no remembered search string", pattern); break; case 42: (void) sprintf(buf, "%s: \\( \\) imbalance", pattern); break; case 43: (void) sprintf(buf, "%s: too many \\(", pattern); break; case 44: (void) sprintf(buf, "%s: more than 2 numbers given in \\{ \\}", pattern); break; case 45: (void) sprintf(buf, "%s: } expected after \\", pattern); break; case 46: (void) sprintf(buf, "%s: first number exceeds second in \\{ \\}", pattern); break; case 49: (void) sprintf(buf, "%s: { } imbalance", pattern); break; case 50: (void) sprintf(buf, "%s: regular expression overflow", pattern); break; default: (void) sprintf(buf, "%s: unknown regexp error %d", pattern, n); break; } err(L_INFO|F_COND|F_PERM, buf); } #endif : * Matt Bishop * Research Institute for Advanced Computer Science * NASA Ames Research Center * Moffett Field, CA 94035 * * mab@riacs.edu ARPA * ...!decvax!decwrl!riacs!mab UUCP * ...!ihnp4!ames!riacs!mab UUCP * * Copyright (c) 1986. */ #include "lsu.h" #ifdef SYSV_TYPE # define INIT register char *sp = instring; # define GETC() (*sp++) # define PEEKC() (*sp) # define UNGETC(c) (--sp) # define RETURN(c) return; # define ERROR(c) regerr(c) # includelsu/perm.c 644 1625 12 16342 5106525642 6073 /* * LSU -- local superuser header file * * Author: * Matt Bishop * Research Institute for Advanced Computer Science * NASA Ames Research Center * Moffett Field, CA 94035 * * mab@riacs.edu ARPA * ...!decvax!decwrl!riacs!mab UUCP * ...!ihnp4!ames!riacs!mab UUCP * * Copyright (c) 1986. */ #include "lsu.h" /* * line information; used to print errors */ int linect; /* line number */ /* * perms -- verify the user is listed in a consistent log file * if not, log the attempt and end */ perms(who) char *who; /* login name of new user */ { char buf[BUFSIZ]; /* used as a buffer */ register char *b, *t; /* used to scan buffer buf[] */ int nope; /* > 0 if no permission */ int valid = 0; /* 1 if a valid line found */ int towholist = 0; /* 1 if ANYBODY lists who */ char okuser[BUFSIZ]; /* holds list of legal users */ int ercode = E_USER; /* error code */ int notuser = 0; /* 0 if line's for another user */ char ebuf[BUFSIZ]; /* error buffer */ FILE *permfp; /* lsuperm file pointer */ /* * check that the log file is root owned and * readable/writable ONLY by the owner (root) * open it for reading and search for the user name */ if ((permfp = chkperm()) == NULL){ if (*who == '\0'){ if (progname == SU || progname == NSU) (void) strcpy(who, LSUUSER); else (void) strcpy(who, curpw.pw_name); } return; } /* * now scan the file for the current user (curpw.pw_name) name * note we parse the line even if this isn't the right user, * since we need to see if the person "newpw.pw_name" is in * the list of people to whom any user can lsu/su */ for(linect = 1; fgets(buf, BUFSIZ, permfp) != NULL; linect++){ /* * assume there's nothing wrong yet */ notuser = 0; nope = 0; /* * eat comment lines (all begin with LSUCOMM) */ if (buf[0] == LSUCOMM) continue; /* * skip leading blanks and see if the name * is that of the current user */ for(b = buf; *b && !isspace(*b); b++); if (isspace(*b)) *b++ = '\0'; if (strcmp(buf, curpw.pw_name) != 0){ notuser++; nope++; } else ercode = E_NONE; /* * got him * save the list of who he can become */ if (*b != '\0'){ while(*b && isspace(*b)) b++; t = okuser; while(*b && *b != '\t') *t++ = *b++; *t = '\0'; } /* * now see if he can do it from this terminal */ while(*b == '\t') b++; if (*b != '\0' && !isintty(b) && !notuser){ (void) sprintf(ebuf, "bad tty (line %d)\n", linect); err(L_INFO|F_NONE, ebuf); nope++; } while(*b && *b != '\t') b++; if (*b == '\t') b++; /* * see if it is the right time */ if (!isintime(b) && !notuser){ (void) sprintf(ebuf, "bad time (line %d)\n", linect); err(L_INFO|F_NONE, ebuf); nope++; } /* * now check users -- if the first char in who[] is * '\0', it becomes the first one in the list */ if (who[0] == '\0' && !notuser){ for(t = who, b = okuser; *b && *b != ','; t++, b++) *t = lowcase(*b); *t = '\0'; if (strcmp("any", who) == 0) (void) strcpy(who, LSUUSER); } else if (isinlist(progname == SU ? curpw.pw_name : who, okuser) == NULL && curpw.pw_uid != 0 && strcmp(curpw.pw_name, who) != 0){ if (!notuser){ (void) sprintf(ebuf, "%s not newuser (line %d)\n", who, linect); err(L_INFO|F_NONE, ebuf); } nope++; } else towholist++; /* * see if it's valid */ if (nope == 0) valid++; } /* * close the permission file */ (void) fclose(permfp); /* * okay, now get the data to actually do the identity substitution */ if (who[0] == '\0') (void) strcpy(who, LSUUSER); /* * is he or is he not legitimate? * YES: if root * NO: if this is lsu and there's not a valid line in the perm file * NO: if this is su, there's not a valid line in the perm file, AND * if any line in the permission file controls access to the user * in "newpw.pw_name". */ if (curpw.pw_uid != 0 && strcmp(curpw.pw_name, who) != 0 && (progname == LSU && !valid) || ((progname == SU || progname == NSU) && !valid && !towholist)){ if (ercode == E_USER) err(L_INFO|F_FAIL, "user not listed in permission file\n"); success = F_FAIL; } } getnewuser(who) char *who; { struct passwd *pw; char buf[BUFSIZ]; if ((pw = getpwnam(who)) == NULL){ (void) sprintf(buf, "%s not listed in password file\n", who); err(L_INFO|F_COND, buf); pw = &curpw; } newpw.pw_name = strsave(pw->pw_name); newpw.pw_passwd = strsave(pw->pw_passwd); newpw.pw_uid = pw->pw_uid; newpw.pw_gid = pw->pw_gid; newpw.pw_dir = strsave(pw->pw_dir); newpw.pw_shell = strsave(pw->pw_shell); } chkpasswd() { if (progname == LSU) vfypwd(curpw.pw_passwd); else if (progname == SU || progname == NSU) vfypwd(newpw.pw_passwd); } /* * verify the permissions file is owned by UID 0 * and readable/writable only by that user */ FILE *chkperm() { char perm[BUFSIZ]; /* permission file name */ char buf[BUFSIZ]; /* buffer for error messages */ struct stat stbuf; /* used to check owner, mode of perm file */ struct passwd *tpw; /* for errors in ownership */ FILE *permfp = NULL; /* pointer to permission file */ /* * stat the file; note we clobber it * as soon as the call is done */ if (progname == LSU){ MAKELSUPERM(perm); } else if (progname == SU || progname == NSU){ MAKESUPERM(perm); } if (stat(perm, &stbuf) < 0){ CLEARNAME(perm); err(L_INFO|F_PERM|F_SYS|F_COND, "permission file"); CLEARNAME(perm); return(NULL); } CLEARNAME(perm); /* * if the UID is not 0, * log who owns the file and quit */ if (stbuf.st_uid != 0){ /* * get the UID and password file information for this UID * if none, log the error using the UID and die */ if ((tpw = getpwuid((int) stbuf.st_uid)) == NULL || tpw->pw_name == NULL) (void) sprintf(buf, "permission file owned by UID %d\n", stbuf.st_uid); else (void) sprintf(buf, "permission file owned by %s\n", tpw->pw_name); /* * protect the file */ protfile(); /* * log the error */ err(L_INFO|F_PERM|F_COND, buf); return(NULL); } /* * check the permissions */ if ((stbuf.st_mode&0777) != 0600){ /* * say what mode the file is */ (void) sprintf(buf, "permission file mode is bad (%04o)\n", stbuf.st_mode&07777); /* * protect the file */ protfile(); /* * log the error */ err(L_INFO|F_PERM|F_COND, buf); return(NULL); } /* * open the permission file */ if (progname == LSU){ MAKELSUPERM(perm); } else if (progname == SU || progname == NSU){ MAKESUPERM(perm); } if ((permfp = fopen(perm, "r")) == NULL){ CLEARNAME(perm); err(L_INFO|F_PERM|F_SYS|F_COND, "permission file"); } CLEARNAME(perm); /* * return the file pointer */ return(permfp); } /* * if there is a problem, change the ownership of the access file to * UID 0 GID 0 and the mode to 0000 -- that forces a superuser to look * at it */ protfile() { char perm[BUFSIZ]; /* buffer for protection file name */ /* * get the file name */ if (progname == LSU){ MAKELSUPERM(perm); } else if (progname == SU || progname == NSU){ MAKESUPERM(perm); } /* * change ownership and mode */ (void) chown(perm, 0, 0); (void) chmod(perm, 0000); /* * clobber the name at once */ CLEARNAME(perm); } !riacs!mab UUCP * ...!ihnp4!ames!riacs!mab UUCP * * Copyright (c) 1986. */ #include "lsu.h" /* * line information; used to print errors */ int linect; /* line number */ /* * perms -- verify the user is listed in a consistent log file * if not, log the attempt and end */ pelsu/syntax.c 644 1625 12 11210 5106523462 6441 /* * LSU -- local superuser header file * * Author: * Matt Bishop * Research Institute for Advanced Computer Science * NASA Ames Research Center * Moffett Field, CA 94035 * * mab@riacs.edu ARPA * ...!decvax!decwrl!riacs!mab UUCP * ...!ihnp4!ames!riacs!mab UUCP * * Copyright (c) 1986. */ #include "lsu.h" /* * line information; used to print errors */ extern int linect; /* line number (see "perm.c") */ /* * this is the driver that checks the access file syntax * it calls a checker for each file in the argument list */ permsyntax(argv) char **argv; /* argument list (argv[1]->...) */ { register int i; /* counter in a for loop */ register int res = 0; /* return value */ register FILE *fp; /* file to be checked */ char buf[BUFSIZ]; /* buffer for messages */ /* * if not root, usual "permission denied" stuff */ if (curpw.pw_uid != 0){ logmessage(L_CHK, "permission denied (not root)\n"); err(L_CHK|F_FAIL, "permission denied\n"); return(1); } /* * no file named -- use standard input */ if (*argv == NULL) return(psyn(stdin)); /* * walk the list */ for(i = 0; argv[i] != NULL; i++){ /* * log the check */ (void) sprintf(buf, "checking syntax of %s\n", argv[i]); logmessage(L_CHK, buf); /* * open the file; on failure, give error message */ if ((fp = fopen(argv[i], "r")) == NULL){ err(L_INFO|F_SYS, argv[i]); res = 1; } else{ /* * print the name of the file being scanned * and scan it */ fprintf(stderr, "%s:\n", argv[i]); psyn(fp); } } /* * return the result of the scan(s) */ return(res); } /* * check the syntax of one file */ psyn(fp) FILE *fp; /* file pointer */ { char buf[BUFSIZ]; /* buffer for line */ char ebuf[BUFSIZ]; /* buffer for error messages */ register char *b, *f; /* used to get fields */ char c; /* character ending a name */ int exstat = 0; /* exit status */ /* * process the file a line at a time */ for(linect = 1; fgets(buf, BUFSIZ, fp) != NULL; linect++){ /* * comment lines are correct */ if (buf[0] == LSUCOMM) continue; /* * eat leading spaces; if nothing else, * the line is empty */ for(b = buf; isspace(*b); b++); if (*b == '\0'){ (void) sprintf(ebuf, "(%2d): empty line\n", linect); err(L_INFO|F_NONE, ebuf); exstat = 1; continue; } /* * begin user field; f points to user name, * b will point to just beyond end of this field */ f = b; while(*b && !isspace(*b)) b++; if (isspace(*b)) *b++ = '\0'; /* * if no such user, warn -- may be problem */ if (getpwnam(f) == PNULL){ (void) sprintf(ebuf, "(%2d): %s -- no such user\n", linect, f); err(L_INFO|F_NONE, ebuf); exstat = 1; } /* * skip to next field; if nothing else, * there are three fields missing */ while(*b != '\t') b++; while(isspace(*b)) b++; if (*b == '\0'){ (void) sprintf(ebuf, "(%2d): missing newuser, tty, time fields\n", linect); err(L_INFO|F_NONE, ebuf); exstat = 1; continue; } /* * begin newuser user field; f points to a newuser name, * b will point to just beyond end of that name */ f = b; do{ /* * skip over the name; if it ends in comma, * another follows */ while(*b && *b != ',' && !isspace(*b)) b++; c = *b; *b++ = '\0'; /* * is it a legal user */ if (!isany(f) && getpwnam(f) == PNULL){ (void) sprintf(ebuf, "(%2d): %s -- no such user\n", linect, f); exstat = 1; err(L_INFO|F_NONE, ebuf); } /* * now look at beginning of next one */ f = b; } while(c == ','); /* * skip to next field; if nothing else, * there are two fields missing */ if (c != '\t') while(*b && *b != '\t') b++; while(isspace(*b)) b++; if (*b == '\0'){ (void) sprintf(ebuf, "(%2d): missing tty, time fields\n", linect); err(L_INFO|F_NONE, ebuf); exstat = 1; continue; } /* * now check the terminal specification syntax */ f = b; (void) isintty(f); /* * skip to next field; if nothing else, * there are two fields missing */ while(*b && *b != '\t') b++; while(isspace(*b)) b++; if (*b == '\0'){ (void) sprintf(ebuf, "(%2d): missing time field\n", linect); err(L_INFO|F_NONE, ebuf); continue; } /* * now check the time specification format */ f = b; (void) isintime(f); } return(exstat); } /* * this returns 1 if the word f is "ANY" in any case */ int isany(f) char *f; { /* * you maybe expected a table lookup? */ return (f[0] && tolower(f[0]) == 'a' && f[1] && tolower(f[1]) == 'n' && f[2] && tolower(f[2]) == 'y' && f[3] == '\0'); } ypwd(curpw.pw_passwd); else if (progname == SU || progname == NSU) vfypwd(newpw.pw_passwd); } /* * verify the permissions file is owned by UID 0 * and readable/writable only by that user */ FILE *chkperm() { char perm[BUFSIZ]; /* permission file name */ char buf[BUFSIZ]; /* buffer for error messages */ struct stat stbuf; /* used to check owner, mode of perm fillsu/sysdep.h 644 1625 12 16201 5106524761 6437 /* * this should run on the following versions of UNIX(tm): * Amdahl UTS (UTS) * AT&T System V (SYSV) * Berkeley Software Distribution Release 4.2 (BSD4_2) * Berkeley Software Distribution Release 4.3 (BSD4_3) * Cray UNIX Operating System (UNICOS) * NAS Processing System Network Build 3 (NPSN3) * Ridge Operating System Release 3.3 (ROS3_3) * Sequent Balance Dynix Release 2.0 (DYNIX2_0) * Silicon Graphics Graphics Library 2, Workstation 2.3 (SGI2_3) * Silicon Graphics Graphics Library 3, Workstation 3.4 (SGI3_4) * Silicon Graphics Graphics Library 3, Workstation 3.5 (SGI3_5) * Sun Microsystems UNIX Release 4.2 (SUN4_2) * here are the defines to make them look the same so far * as the code is concerned * * defines specific to lsu: * SYSTEM complete identification of the system * LSUMAIL shell command to mail a letter; must take the user name * as the last argument, ie. "/bin/mail root" -- BE SURE * A FULL PATH NAME IS USED OR ELSE THERE IS A SECURITY * HOLE (namely, Trojan horses) * LSUMAINT who to mail error messages to (typically, an alias like * "lsumaint") * LSUPATH if there is no PATH environment variable for the user * and he tries to lsu to someone with UID 0, this gets * put in his environment; it is a colon-separated list * of directories * * these handle the specific AT&T and BSD differences: * BSD4_TYPE assume this is like BSD 4 * SYSV_TYPE assume this is like AT&T System V * * Author: * Matt Bishop * Research Institute for Advanced Computer Science * NASA Ames Research Center * Moffett Field, CA 94035 * * mab@riacs.arpa ARPA * ...!decvax!decwrl!riacs!mab UUCP * ...!ihnp4!ames!riacs!mab UUCP * * Copyright (c) 1986. */ #define VERSION "version 2.2, April 21, 1987 mab@riacs.edu" /* * Amdahl UTS (UTS) */ #ifdef UTS # define SYSTEM "Amdahl UTS" # define LSUPATH "PATH=/usr/local/bin:/bin:/usr/bin:/usr/amdahl:/usr/ucb/bin:/etc:/usr/local/etc:/usr/hosts" # define SYSV_TYPE #endif /* * AT&T System V (SYSV) */ #ifdef SYSV # define SYSTEM "AT&T Unix System V" # define SYSV_TYPE #endif /* * Berkeley Software Distribution Release 4.2 (BSD4_2) */ #ifdef BSD4_2 # define SYSTEM "Berkeley Software Distribution Release 4.2" # define BSD4_TYPE #endif /* * Berkeley Software Distribution Release 4.3 (BSD4_3) */ #ifdef BSD4_3 # define SYSTEM "Berkeley Software Distribution Release 4.3" # define BSD4_TYPE #endif /* * Cray UNIX Operating System (UNICOS) */ #ifdef UNICOS # define SYSTEM "Cray Unix Operating System" # define SYSV_TYPE /* * UNICOS does not support getting speed * from the terminal structure, so CBAUD * is not defined. Fake it here. */ # define CBAUD 037 #endif /* * NAS Processing System Network Build 3 (NPSN3) */ #ifdef NPSN3 # define SYSTEM "NAS Processing System Network, Build 3" # define SYSV_TYPE #endif /* * Ridge Operating System Release 3.3 (ROS3_3) */ #ifdef ROS3_3 # define SYSTEM "Ridge Computer Systems ROS 3.3" # define LSUPATH "PATH=/usr/ucb:/etc:/bin:/usr/bin" # define SYSV_TYPE #endif /* * Sequent Balance Dynix Release 2.0 (DYNIX2_0) */ #ifdef DYNIX2_0 # define SYSTEM "Sequent Computer Systems DYNIX 2.0" # define LSUMAINT "mab" # define BSD4_TYPE # define LOCAL_VARS { "LOGNAME=%s", 8, &(newpw.pw_name), V_ORLOGIN, }, #endif /* * Silicon Graphics Graphics Library 2, Workstation 2.3 (SGI2_3) */ #ifdef SGI2_3 # define SYSTEM "Silicon Graphics IRIS GL2.4" # define SYSV_TYPE #endif /* * Silicon Graphics Graphics Library 3, Workstation 3.4 (SGI3_4) */ #ifdef SGI3_4 # define SYSTEM "Silicon Graphics IRIS GL3.4" # define SYSV_TYPE #endif /* * Silicon Graphics Graphics Library 3, Workstation 3.5 (SGI3_5) */ #ifdef SGI3_5 # define SYSTEM "Silicon Graphics IRIS GL3.5" # define SYSV_TYPE #endif /* * Sun Microsystems UNIX Release 4.2 (SUN4_2) */ #ifdef SUN4_2 # define SYSTEM "Sun Microsystems UNIX Release 4.2" # define BSD4_TYPE #endif /* * these encode generic differences between AT&T System ... and * Berkeley ... UNIX */ #ifdef SYSV_TYPE /* * string library functions */ # define index strchr /* find first occurrance of char in string */ # define rindex strrchr /* find last occurrance of char in string */ /* * file definitions */ # include /* file opening modes */ /* * time definitions */ # include /* time structures */ /* * these are mainly for lint */ void exit(); /* exit declared as a void, not int */ void free(); /* free declared as a void, not int */ /* * tty structures are COMPLETELY different! */ # include /* terminal description info */ typedef struct termio T_TTY; /* terminal control structure */ # define IO_GTTY TCGETA /* get the tty structure via an ioctl */ # define TTYISPEED(x) (((x).c_cflag)&CBAUD) /* baud rate as index */ # define TTYOSPEED(x) (((x).c_cflag)&CBAUD) /* baud rate as index */ /* allowed terminal speeds */ # define SPEEDS 0L,50L,75L,110L,135L,150L,200L,300L,600L,\ 1200L,1800L,2400L,4800L,9600L,19200L,56000L,\ -1L, /* * reset these variables */ # define SYSTEM_VARS \ { "HOME=%s", 5, &(newpw.pw_dir), V_NSU|V_LSU, },\ { "LOGNAME=%s", 8, &(newpw.pw_name), V_ORLOGIN, },\ { "MAIL=/usr/mail/%s", 5, &(newpw.pw_name), V_ORLOGIN, }, /* * some miscellaneous stuff; used only if not previously defined */ # ifndef MAILPLATE # define MAILPLATE "MAIL=/usr/mail/%s" # endif # ifndef LSUPATH # define LSUPATH "PATH=/etc:/bin:/usr/bin" # endif #endif #ifdef BSD4_TYPE /* * file definitions */ # include /* file opening modes */ /* * time definitions */ # include /* time structures */ /* * tty structures are COMPLETELY different! */ # include /* terminal description info */ typedef struct sgttyb T_TTY; /* terminal control structure */ # define IO_GTTY TIOCGETP /* get the tty structure via an ioctl */ # define TTYISPEED(x) ((x).sg_ispeed) /* baud rate as index */ # define TTYOSPEED(x) ((x).sg_ospeed) /* baud rate as index */ /* allowed terminal speeds */ # define SPEEDS 0L,50L,75L,110L,135L,150L,200L,300L,600L,\ 1200L,1800L,2400L,4800L,9600L,19200L,56000L,\ -1L, /* * reset these variables */ # define SYSTEM_VARS \ { "HOME=%s", 5, &(newpw.pw_dir), V_ALWAYS, },\ { "MAIL=/usr/spool/mail/%s", 5, &(newpw.pw_name), V_ORLOGIN, }, /* * some miscellaneous stuff; used only if not previously defined */ # ifndef MAILPLATE # define MAILPLATE "MAIL=/usr/spool/mail/%s" # endif # ifndef LSUPATH # define LSUPATH "PATH=/usr/ucb:/etc:/bin:/usr/bin" # endif #endif /* * this sets up environment variables that are to be reset * note SYSTEM_VARS are defined in the system_TYPE section * above, and LOCAL_VARS are defined for each machine */ #ifndef SYSTEM_VARS # define SYSTEM_VARS #endif #ifndef LOCAL_VARS # define LOCAL_VARS #endif #define LSUVARS {\ { "SHELL=%s", 6, &(newpw.pw_shell), V_ALWAYS, }, \ { "USER=%s", 5, &(newpw.pw_name), V_NSU|V_LSU, }, \ SYSTEM_VARS \ LOCAL_VARS \ { NULL, 0, &(newpw.pw_name), V_NONE, }, \ } /* * generic defines; used only if not previously defined */ #ifndef LSUMAIL # define LSUMAIL "/bin/mail" #endif #ifndef LSUMAINT # define LSUMAINT "lsumaint" #endif #ifndef LSUPATH # define LSUPATH "PATH=/etc:/bin:/usr/bin" #endif log the error using the UID and die */ if ((tpw = getpwuid((int) stbuf.st_uid)) == NULL || tpw->pw_name == NULL) (void) sprintf(buf, "permission file owned by UID %d\n", stbuf.st_uid); else (void) sprintf(buf, "permission file owned by %s\n", tpw->pw_name); /* * protect the file */ protfile(); /* * log the error */ err(L_INFO|F_PElsu/time.y 644 1625 12 56540 5106523463 6117 %{ /* * this file contains the parser for the time of day * the function "isintime(t)" returns 1 if the current time * is within the time given by the string t * grammar: * day_of_year day_of_week time_of_day * where: * day_of_year == month number ',' number (ie, Aug 29, 1986) * month number (ie, Aug 29) * month ',' number (ie, Aug, 1986) * month (ie, Aug) * number '/' number '/' number (ie, 8/29/86) * number '/' number (ie, 8/29) * day_of_week == special_day * Any of "Sunday", "Monday", "Tuesday", * "Wednesday", "Thursday", "Friday", * or "Saturday" * time_of_day == number ':' number ':' number mer (ie, 8:12:56 pm) * number ':' number ':' number (ie, 20:12:56) * number ':' number meridian (ie, 8:12 pm) * number ':' number (ie, 20:12) * number meridian (ie, 8 pm) * number (ie, 20) * special_time * Any of "noon", "midnight" * "meridian" is "am" or "pm". Enough of "special_day" and "special_time" words * to identify the word uniquely must be given. * * if you want to change the syntax, you can debug the new grammar by defining * "DEBUG" -- this allows the file to be compiled as a separate program, and * will display the result. * * Author: * Matt Bishop * Research Institute for Advanced Computer Science * NASA Ames Research Center * Moffett Field, CA 94035 * * mab@riacs.arpa ARPA * ...!decvax!decwrl!riacs!mab UUCP * ...!ihnp4!ames!riacs!mab UUCP * * Copyright (c) 1986. * */ #include #include #ifdef DEBUG # define F_COND 0 /* dummy value; not used here */ # define L_INFO 0 /* dummy value; not used here */ # include "sysdep.h" #else # include "lsu.h" #endif /* * useful macros */ /* 1 if current time is between x and y */ #define intime(x,y) (timecmp((x), &cur) != 1 && timecmp(&cur, (y)) != 1) /* if c is upper case, make it lower case */ #define lowcase(c) (isupper((c))?tolower((c)):(c)) #define LK_AMBIG -1 /* ambiguous result from table lookup */ #define LK_NOSUCH -2 /* no such result from table lookup */ /* * times are represented as intervals; lo < hi */ typedef struct { struct tm lo; /* lower bound of time */ struct tm hi; /* upper bound of time */ } TIME; #define NULL_TIME ((TIME *) NULL) %} /* * for reasons due entirely to the way YACC processes input, * this has to go here since TIME is used as a type in the * header file to declare yylval */ %union { int ival; /* as an integer */ TIME vval; /* as a time interval */ } /* * tokens returned by the lexical analyzer yylex() */ %token AND /* integer: ampersand */ %token ANY /* integer: matches any date */ %token COMMA /* integer: comma */ %token COLON /* integer: colon */ %token DASH /* integer: hyphen */ %token DAY_OF_WEEK /* interval: day of the week */ %token EOL /* integer: no more input */ %token LPAR /* integer: left parenthesis */ %token MERIDIAN /* interval: am or pm */ %token MONTH /* interval: month of the year */ %token NONE /* integer: matches no date */ %token NOT /* integer: exclamation point */ %token NUMBER /* integer: number token */ %token OR /* integer: vertical bar */ %token RPAR /* integer: right parenthesis */ %token SLASH /* integer: slash */ %token TIME_OF_DAY /* interval: time of the day */ %token UNK /* integer: unknown */ /* * productions analyzed by the parser * in what follows, "interval" refers to the (given) time interval */ %type day_of_week_ok /* integer: 1 if in day of week interval */ %type day_of_year /* interval: bounds day of the year */ %type day_of_year_ok /* integer: 1 if in day of year interval */ %type expr /* integer: 1 if time so far in interval */ %type stat /* integer: expr with an EOL token tacked on */ %type time_of_day /* interval: bounds time of the day */ %type time_of_day_ok /* integer: 1 if in time of day interval */ /* * expression operators * these must be on the same line since they are of equal precedence */ %left OR AND %right NOT %{ /* * time fields which we don't care about in comparisons are * set to -1 to warn the comparison function "timecmp" that * they are not to be considered; here's the prototype */ TIME nulltime = { {-1,-1,-1,-1,-1,-1,-1,-1,-1 }, /* lower bound */ {-1,-1,-1,-1,-1,-1,-1,-1,-1 }, /* upper bound */ }; /* * lookup table -- all special words (keywords to compiler types) go here * the time limits go into the time table * (this should be one table, but some compilers -- like Amdahl's UTS * C compiler -- complain about too many initializers ...) */ struct looktbl { char *string; /* the key word */ int refnum; /* number referring to keyword of this rettype */ int rettype; /* MONTH, MERIDIAN, TIME_OF_DAY, DAY_OF_WEEK */ } wordlist[] = { { "any", -1, ANY }, /* 0 */ { "am", 0, MERIDIAN, }, /* 1 */ { "april", 4, MONTH, }, /* 2 */ { "august", 8, MONTH, }, /* 3 */ { "december", 12, MONTH, }, /* 4 */ { "february", 2, MONTH, }, /* 5 */ { "friday", 5, DAY_OF_WEEK, }, /* 6 */ { "january", 1, MONTH, }, /* 7 */ { "july", 7, MONTH, }, /* 8 */ { "june", 6, MONTH, }, /* 9 */ { "march", 3, MONTH, }, /* 10 */ { "may", 5, MONTH, }, /* 11 */ { "midnight", 0, TIME_OF_DAY, }, /* 12 */ { "monday", 1, DAY_OF_WEEK, }, /* 13 */ { "none", -1, NONE }, /* 14 */ { "noon", 12, TIME_OF_DAY, }, /* 15 */ { "november", 11, MONTH, }, /* 16 */ { "october", 10, MONTH, }, /* 17 */ { "pm", 12, MERIDIAN, }, /* 18 */ { "saturday", 6, DAY_OF_WEEK, }, /* 19 */ { "september", 9, MONTH, }, /* 20 */ { "sunday", 0, DAY_OF_WEEK, }, /* 21 */ { "thursday", 4, DAY_OF_WEEK, }, /* 22 */ { "tuesday", 2, DAY_OF_WEEK, }, /* 23 */ { "wednesday", 3, DAY_OF_WEEK, }, /* 24 */ { "weekdays", -1, DAY_OF_WEEK, }, /* 25 */ { NULL, 0, 0, }, /* -1 */ }; TIME timelist[] ={ /* lower and upper bounds of time period */ { {-1,-1,-1,-1,-1,-1,-1,-1,-1}, {-1,-1,-1,-1,-1,-1,-1,-1,-1} }, /* 0 */ { {-1,-1, 0,-1,-1,-1,-1,-1,-1}, {-1,-1, 0,-1,-1,-1,-1,-1,-1} }, /* 1 */ { { 0, 0, 0, 0, 3,-1,-1,-1,-1}, {60,60,24,30, 3,-1,-1,-1,-1} }, /* 2 */ { { 0, 0, 0, 0, 7,-1,-1,-1,-1}, {60,60,24,31, 7,-1,-1,-1,-1} }, /* 3 */ { { 0, 0, 0, 0,11,-1,-1,-1,-1}, {60,60,24,31,11,-1,-1,-1,-1} }, /* 4 */ { { 0, 0, 0, 0, 1,-1,-1,-1,-1}, {60,60,24,29, 1,-1,-1,-1,-1} }, /* 5 */ { { 0, 0, 0,-1,-1,-1, 5,-1,-1}, {60,60,24,-1,-1,-1, 5,-1,-1} }, /* 6 */ { { 0, 0, 0, 0, 0,-1,-1,-1,-1}, {60,60,24,31, 0,-1,-1,-1,-1} }, /* 7 */ { { 0, 0, 0, 0, 6,-1,-1,-1,-1}, {60,60,24,31, 6,-1,-1,-1,-1} }, /* 8 */ { { 0, 0, 0, 0, 5,-1,-1,-1,-1}, {60,60,24,30, 5,-1,-1,-1,-1} }, /* 9 */ { { 0, 0, 0, 0, 2,-1,-1,-1,-1}, {60,60,24,31, 2,-1,-1,-1,-1} }, /* 10 */ { { 0, 0, 0, 0, 4,-1,-1,-1,-1}, {60,60,24,31, 4,-1,-1,-1,-1} }, /* 11 */ { { 0, 0, 0,-1,-1,-1,-1,-1,-1}, { 0, 0, 0,-1,-1,-1,-1,-1,-1} }, /* 12 */ { { 0, 0, 0,-1,-1,-1, 1,-1,-1}, {60,60,24,-1,-1,-1, 1,-1,-1} }, /* 13 */ { {-1,-1,-1,-1,-1,-1,-1,-1,-1}, {-1,-1,-1,-1,-1,-1,-1,-1,-1} }, /* 14 */ { { 0, 0,12,-1,-1,-1,-1,-1,-1}, { 0, 0,12,-1,-1,-1,-1,-1,-1} }, /* 15 */ { { 0, 0, 0, 0,10,-1,-1,-1,-1}, {60,60,24,30,10,-1,-1,-1,-1} }, /* 16 */ { { 0, 0, 0, 0, 9,-1,-1,-1,-1}, {60,60,24,31, 9,-1,-1,-1,-1} }, /* 17 */ { {-1,-1,12,-1,-1,-1,-1,-1,-1}, {-1,-1,12,-1,-1,-1,-1,-1,-1} }, /* 18 */ { { 0, 0, 0,-1,-1,-1, 6,-1,-1}, {60,60,24,-1,-1,-1, 6,-1,-1} }, /* 19 */ { { 0, 0, 0, 0, 8,-1,-1,-1,-1}, {60,60,24,30, 8,-1,-1,-1,-1} }, /* 20 */ { { 0, 0, 0,-1,-1,-1, 0,-1,-1}, {60,60,24,-1,-1,-1, 0,-1,-1} }, /* 21 */ { { 0, 0, 0,-1,-1,-1, 4,-1,-1}, {60,60,24,-1,-1,-1, 4,-1,-1} }, /* 22 */ { { 0, 0, 0,-1,-1,-1, 2,-1,-1}, {60,60,24,-1,-1,-1, 2,-1,-1} }, /* 23 */ { { 0, 0, 0,-1,-1,-1, 3,-1,-1}, {60,60,24,-1,-1,-1, 3,-1,-1} }, /* 24 */ { { 0, 0, 0,-1,-1,-1, 1,-1,-1}, {60,60,24,-1,-1,-1, 5,-1,-1} }, /* 25 */ { {-1,-1,-1,-1,-1,-1,-1,-1,-1}, {-1,-1,-1,-1,-1,-1,-1,-1,-1} }, /* -1 */ }; /* * variables */ static int ateol = 0; /* 1 when you hit the end of string */ static struct tm cur; /* current time as structure */ static int timing; /* 1 if time is okay, 0 if not */ static char *lptr; /* used to walk input string */ static long clock; /* internal time */ /* * library functions */ char *index(); /* find last character in a string */ struct tm *localtime(); /* convert internal time to a more useable structure */ long time(); /* return internal time */ %} /* * start analysis at state stat */ %start stat %% stat : expr EOL { timing = $1; } ; expr : LPAR expr RPAR { $$ = $2; } | NOT expr { $$ = !$2; } | expr OR expr { $$ = $1 || $3; } | expr AND expr { $$ = $1 && $3; } | day_of_year_ok day_of_week_ok time_of_day_ok { $$ = $1 && $2 && $3; } | day_of_week_ok day_of_year_ok time_of_day_ok { $$ = $1 && $2 && $3; } | day_of_year_ok time_of_day_ok { $$ = $1 && $2; } | day_of_week_ok time_of_day_ok { $$ = $1 && $2; } | day_of_year_ok day_of_week_ok { $$ = $1 && $2; } | day_of_week_ok day_of_year_ok { $$ = $1 && $2; } | day_of_year_ok { $$ = $1; } | day_of_week_ok { $$ = $1; } | time_of_day_ok { $$ = $1; } | ANY { $$ = 1; } | NONE { $$ = 0; } | /* EMPTY */ { $$ = 1; } ; day_of_week_ok : DAY_OF_WEEK { $$ = intime(&$1.lo, &$1.hi); } | DAY_OF_WEEK DASH DAY_OF_WEEK { $$ = intime(&$1.lo, &$3.hi); } | day_of_week_ok COMMA day_of_week_ok { $$ = $1 || $3; } ; time_of_day_ok : time_of_day { $$ = intime(&$1.lo, &$1.hi); } | time_of_day DASH time_of_day { $$ = intime(&$1.lo, &$3.hi); } | time_of_day_ok COMMA time_of_day_ok { $$ = $1 || $3; } ; day_of_year_ok : day_of_year { $$ = intime(&$1.lo, &$1.hi); } | day_of_year DASH day_of_year { $$ = intime(&$1.lo, &$3.hi); } | day_of_year_ok COMMA day_of_year_ok { $$ = $1 || $3; } ; time_of_day : TIME_OF_DAY { $$ = $1; } | NUMBER COLON NUMBER COLON NUMBER MERIDIAN { /* * set up the hour, minute, and second * and verify that the time is legal */ $$ = nulltime; $$.lo.tm_hour = $1 + $6.lo.tm_hour; $$.hi.tm_hour = $1 + $6.hi.tm_hour; $$.lo.tm_min = $$.hi.tm_min = $3; $$.lo.tm_sec = $$.hi.tm_sec = $5; if (badtime(&$$.hi) || badtime(&$$.lo)) YYERROR; } | NUMBER COLON NUMBER COLON NUMBER { /* * set up the hour, minute, and second * and verify that the time is legal */ $$ = nulltime; $$.lo.tm_hour = $$.hi.tm_hour = $1; $$.lo.tm_min = $$.hi.tm_min = $3; $$.lo.tm_sec = $$.hi.tm_sec = $5; if (badtime(&$$.hi) || badtime(&$$.lo)) YYERROR; } | NUMBER COLON NUMBER MERIDIAN { /* * set up the hour and minute and verify * that the time is legal */ $$ = nulltime; $$.lo.tm_hour = $1 + $4.lo.tm_hour; $$.hi.tm_hour = $1 + $4.hi.tm_hour; $$.lo.tm_min = $$.hi.tm_min = $3; if (badtime(&$$.hi) || badtime(&$$.lo)) YYERROR; } | NUMBER COLON NUMBER { /* * set up the hour and minute and verify * that the time is legal */ $$ = nulltime; $$.lo.tm_hour = $$.hi.tm_hour = $1; $$.lo.tm_min = $$.hi.tm_min = $3; if (badtime(&$$.hi) || badtime(&$$.lo)) YYERROR; } | NUMBER MERIDIAN { /* * set up the hour and verify that the time is legal */ $$ = nulltime; $$.lo.tm_hour = $1 + $2.lo.tm_hour; $$.hi.tm_hour = $1 + $2.hi.tm_hour; if (badtime(&$$.hi) || badtime(&$$.lo)) YYERROR; } | NUMBER { /* * set up the hour and verify that the time is legal */ $$ = nulltime; $$.hi.tm_hour = $$.lo.tm_hour = $1; if (badtime(&$$.hi) || badtime(&$$.lo)) YYERROR; } ; day_of_year : MONTH NUMBER COMMA NUMBER { int yr; /* normalized year */ /* * get the month and set the day * of the month and the year */ if (badyear($4, &yr)) YYERROR; $$.lo.tm_mday = $$.hi.tm_mday = $2; $$.lo.tm_year = $$.hi.tm_year = yr; } | MONTH NUMBER { /* * get the month and set the day * of the month */ $$ = $1; $$.lo.tm_mday = $$.hi.tm_mday = $2; } | MONTH COMMA NUMBER { int yr; /* normalized year */ /* * get the month and set the year */ if (badyear($3, &yr)) YYERROR; $$ = $1; $$.lo.tm_year = $$.hi.tm_year = yr; } | MONTH { $$ = $1; } | NUMBER SLASH NUMBER SLASH NUMBER { int yr; /* normalized year */ /* * get the month and set the day * of the month and the year */ if (badmonth($1, &$$) || badyear($5, &yr)) YYERROR; $$.lo.tm_mday = $$.hi.tm_mday = $3; $$.lo.tm_year = $$.hi.tm_year = yr; } | NUMBER SLASH NUMBER { /* * get the month and set the day of the month */ if (badmonth($1, &$$)) YYERROR; $$.lo.tm_mday = $$.hi.tm_mday = $3; } ; %% /* * this is the lexer -- it's pretty dumb */ yylex() { register int i; /* used for table lookups if need be */ /* * this is hit at the end of string * we need to do it this way because the '\0' (EOL) * token must be returned, so we have to return * another "end of input" token -- in other words, * the end of input character is NOT the same as * the end of string (EOL) character */ if (ateol){ ateol = 0; return(-1); /* YACC's end of file character */ } /* * eat leading white spaces */ while(*lptr && isspace(*lptr)) lptr++; /* * hit end of string character * indicate there's nothing more with ateol * (so next time we return YACC's end of file) * and return the EOL token */ if (*lptr == '\0'){ ateol = 1; return(EOL); } /* * word -- look in reserved word area */ if (isalpha(*lptr)){ if ((i = lookup(&lptr)) != LK_NOSUCH && i != LK_AMBIG){ yylval.vval.lo = timelist[i].lo; yylval.vval.hi = timelist[i].hi; return(wordlist[i].rettype); } } /* * number -- just return it */ if (isdigit(*lptr)){ for(i = 0; isdigit(*lptr); lptr++) i = i * 10 + *lptr - '0'; yylval.ival = i; return(NUMBER); } /* * something else -- analyze it */ switch(*lptr++){ case '(': /* begin grouping */ return(LPAR); case ')': /* end grouping */ return(RPAR); case '!': /* negation */ return(NOT); case '|': /* conjunction */ return(OR); case '&': /* disjunction */ return(AND); case '-': /* interval indicator */ return(DASH); case ',': /* list separator, etc. */ return(COMMA); case ':': /* time separator */ return(COLON); case '/': /* date separator */ return(SLASH); } /* * unknown */ return(UNK); } /*==================== S U P P O R T F U N C T I O N S =================*/ /* * special word lookup * the argument pointer is advanced to beyond the end of the match * note case doesn't matter * also note it can be any prefix that's unique; so, * "T" is no good ("Tuesday" and "Thursday") but "Th" is fine */ int lookup(s) char **s; /* string to be looked up */ { register int i; /* counter in a for loop */ register int uniq = LK_NOSUCH; /* number of matches in table */ register char *suniq = NULL; /* number of matches in table */ char *s1, *p; /* used to do the comparisons */ /* * since we need to save s, we use s1 for comparisons */ s1 = *s; /* * walk the list of the word */ for(i = 0; wordlist[i].string != NULL; i++){ /* * compare the current table entry and the string * map the input case to lower case * stop at first mismatch */ p = wordlist[i].string; while(*s1 && *p && lowcase(*s1) == *p){ s1++; p++; } /* * if no match, just loop * if a match, next input char must NOT be a letter * or there's no match */ if (s1 == *s) continue; if (*p == '\0' || *s1 == '\0' || !isalpha(*s1)){ /* * match -- if unmatched so far, remember it * otherwise mark this as ambiguous and * return */ if (uniq == LK_NOSUCH){ uniq = i; suniq = s1; } else return(LK_AMBIG); } s1 = *s; } /* * got something unambiguous */ if (uniq != LK_NOSUCH){ *s = suniq; return(uniq); } /* * nothing */ return(LK_NOSUCH); } /* * looks up something based on rettype and refnum * useful when you know the number of a month but not the name (for example) * this pair is always unique so we don't worry about ambiguities */ int nlookup(num, what) int num; /* number of rettype */ int what; /* the rettype */ { register int i; /* counter in a for loop */ /* * walk the lookup table */ for(i = 0; wordlist[i].string != NULL; i++) if (wordlist[i].rettype == what && wordlist[i].refnum == num){ /* * found it */ return(i); } /* * nothing there ... */ return(LK_NOSUCH); } /* * map the number to a month * return 1 if mapping fails, 0 otherwise */ badmonth(n, ts) int n; /* number of month */ TIME *ts; /* pointer to return structure */ { register int i; /* holds value of month lookup */ char buf[BUFSIZ]; /* for any error messages */ /* * see if the month is there * if not, give error message */ if (n < 1 || n > 12 || (i = nlookup(n, MONTH)) == LK_NOSUCH){ (void) sprintf(buf, "bad month %d", n); yyerror(buf); return(1); } /* * return the associated structure */ *ts = timelist[i]; return(0); } /* * normalize the year number * return 1 if the number is not valid * note any non-negative years are errors! */ badyear(year, norm) int year; /* year number */ int *norm; /* where the normalized year number goes */ { char buf[BUFSIZ]; /* for any error messages */ /* * if year is negative, * it's an error */ if (year < 0){ (void) sprintf(buf, "bad year %d", year); yyerror(buf); return(1); } /* * anything else is okay -- if over 99, normalize */ if (year < 100){ *norm = year; return(0); } *norm = year - 1900; return(0); } /* * look for bad hour, minute, second */ badtime(ts) struct tm *ts; /* time to be checked */ { char buf[BUFSIZ]; /* for any error messages */ /* * check hours */ if (ts->tm_hour != -1 && ts->tm_hour > 23){ (void) sprintf(buf, "bad hour %d", ts->tm_hour); yyerror(buf); return(1); } /* * check minutes */ if (ts->tm_min != -1 && ts->tm_min > 59){ (void) sprintf(buf, "bad minute %d", ts->tm_min); yyerror(buf); return(1); } /* * check seconds */ if (ts->tm_sec != -1 && ts->tm_sec > 59){ (void) sprintf(buf, "bad second %d", ts->tm_sec); yyerror(buf); return(1); } /* * it's a good time */ return(0); } /* * time comparison function; return * -1 if a < b * 0 if a = b * +1 if a > b * just like strcmp * comparison is on year, then month, then day of the month, * then hour, then minute, then second -- ignoring any fields with * -1 in either a or b */ int timecmp(a, b) struct tm *a, *b; /* times to be compared */ { /* * compare days of the week */ if (a->tm_wday != -1 && b->tm_wday != -1){ if (a->tm_wday > b->tm_wday) return(1); else if (a->tm_wday < b->tm_wday) return(-1); } /* * compare years */ if (a->tm_year != -1 && b->tm_year != -1){ if (a->tm_year > b->tm_year) return(1); else if (a->tm_year < b->tm_year) return(-1); } /* * compare months */ if (a->tm_mon != -1 && b->tm_mon != -1){ if (a->tm_mon > b->tm_mon) return(1); else if (a->tm_mon < b->tm_mon) return(-1); } /* * compare days of the month */ if (a->tm_mday != -1 && b->tm_mday != -1){ if (a->tm_mday > b->tm_mday) return(1); else if (a->tm_mday < b->tm_mday) return(-1); } /* * compare hours */ if (a->tm_hour != -1 && b->tm_hour != -1){ if (a->tm_hour > b->tm_hour) return(1); else if (a->tm_hour < b->tm_hour) return(-1); } /* * compare minutes */ if (a->tm_min != -1 && b->tm_min != -1){ if (a->tm_min > b->tm_min) return(1); else if (a->tm_min < b->tm_min) return(-1); } /* * compare seconds */ if (a->tm_sec != -1 && b->tm_sec != -1){ if (a->tm_sec > b->tm_sec) return(1); else if (a->tm_sec < b->tm_sec) return(-1); } /* * equality */ return(0); } /*= I N I T I A L I Z A T I O N A N D C H E C K I N G R O U T I N E S =*/ /* * initialize the current time */ inittime() { /* * get the current time */ clock = time((long *)0); cur = *localtime(&clock); #ifdef DEBUG /* * print it if need be */ prtime(&cur, "CURRENT TIME"); #endif } /* * this routine sets up the string for parsing, calls the parser, * and handles the result */ isintime(t) char *t; /* buffer for time */ { /* * get the current time */ inittime(); /* * set up the pointer to the input * for yylex, the lexical analyzer */ lptr = &t[strlen(t)-1]; if (*lptr == '\n') *lptr = '\0'; lptr = t; /* * parse the date and process the result */ if (yyparse()){ #ifdef DEBUG printf("illegal time description\n"); #endif return(0); } #ifdef DEBUG printf("time %s this description\n", timing ? "matches" : "does not match"); #endif return(timing); } /* * error in parse * we just log it; since we do NOT do error recovery, * the attempt is rejected */ yyerror(s) char *s; /* error message (supplied by YACC) */ { char buf[BUFSIZ]; /* error message */ register char *p; /* used to format error message */ extern int linect; /* line number */ /* * shoot the rest of the line */ if ((p = index(s, '\t')) != NULL) *p = '\0'; /* * dump the error message */ (void) sprintf(buf, "(%2d): %s -- bad time (at \"%s\")\n", linect, s, --lptr); err(L_INFO|F_COND, buf); } /*===================== M A I N C A L L I N G R O U T I N E S ==============*/ /* * two sets of main routines * if "DEBUG" is defined, you get a main routine that reads in one date, * parses it, and prints the result * if "DEBUG" is not defined, you get a routine that returns 1 if the current * time is within the time range of the argument string, 0 if not * * "DEBUG" san be set to two levels; "1" gives you just the result (1 or 0), * but you can use the print function "prtime()" to print out key times in * the parse. "2" gives you complete debugging info from YACC as well */ #ifdef DEBUG /* * set the flag YYDEBUG */ #if DEBUG > 1 #define YYDEBUG /* flag so YACC will generate debugging info */ extern int yydebug; /* constant to tell YACC to generate debugging info */ #endif int linect = 0; /* number of expression being tested */ /* * main routine -- set current time, read the date, parse it, * and print the result */ main() { char buf[BUFSIZ]; /* input buffer */ #if DEBUG > 1 yydebug = 1; /* YACC is to give full debugging output */ #endif /* * get the input; if EOF, quit */ while(fgets(buf, BUFSIZ, stdin) != NULL){ /* * new expression */ linect++; /* * clear the end of line flag */ ateol = 0; /* * clobber any trailing newline */ lptr = &buf[strlen(buf)-1]; if (*lptr == '\n') *lptr = '\0'; /* * print the buffer */ printf("buf is <%s>\n", buf); /* * set up the pointer to the input * for yylex, the lexical analyzer */ lptr = buf; /* * parse the date and process the result */ (void) isintime(buf); } /* * no problem */ exit(0); } /* * print the given time, preceeded by the given string, * in a readable format */ prtime(s, lb) struct tm *s; /* time */ char *lb; /* label string */ { printf("%s %d/%d/%d %d:%02d:%02d (%d)\n", lb, s->tm_mon, s->tm_mday, s->tm_year, s->tm_hour, s->tm_min, s->tm_sec, s->tm_wday); } /* * error in parse * this mimics a function in lsu */ err(f, s) unsigned int f; /* flag */ char *s; /* error message (supplied by YACC) */ { fprintf(stderr, "%s\n", s); } #endif rst mismatch */ p = wordlist[i].string; while(*s1 && *p && lowcase(*s1) == *p){ s1++; p++; } /* * if no match, just loop * if a match, nelsu/tty.y 644 1625 12 45500 5106523465 5775 %{ /* * this file contains the parser fot the terminal * the function "isintty(t) returns 1 id the current tty * matches the description given by the string t * grammar: * ttyname speed * where: * ttyname == filename (ie, "/dev/ttyi9") * speed == ( '=' | '@' ) number (ie, "@9600") * '<=' number (ie, "<=9600") * '>=' number (ie, ">=9600") * ( '!=' | '<>' | '><' ) number (ie, "!=9600") * '<' number (ie, ">9600") * '>' number (ie, ">9600") * * if you want to change the syntax, you can debug the new grammar by defining * "DEBUG" -- this allows the file to be compiled as a separate program, and * will display the result. * * Author: * Matt Bishop * Research Institute for Advanced Computer Science * NASA Ames Research Center * Moffett Field, CA 94035 * * mab@riacs.arpa ARPA * ...!decvax!decwrl!riacs!mab UUCP * ...!ihnp4!ames!riacs!mab UUCP * * Copyright (c) 1986. */ #include #include #ifdef DEBUG # define F_COND 0 /* dummy value; not used here */ # define L_INFO 0 /* dummy value; not used here */ # include "sysdep.h" #else # include "lsu.h" #endif /* * useful macros */ #define DEFTTY "/dev/null" /* if you can't get a tty description use this */ /* if c is upper case, make it lower case */ #define lowcase(c) (isupper((c))?tolower((c)):(c)) /* * test for equality in name of device * for input, output, error, and all * devices */ #define inname(val) checkname(&(thistty.input), val) #define outname(val) checkname(&(thistty.output), val) #define errname(val) checkname(&(thistty.errput), val) #define allname(val) (inname(val) && outname(val) && errname(val)) /* * test for pattern match in name of device * for input, output, error, and all * devices */ #define inpat(val) checkpat(&(thistty.input), val) #define outpat(val) checkpat(&(thistty.output), val) #define errpat(val) checkpat(&(thistty.errput), val) #define allpat(val) (inpat(val) && outpat(val) && errpat(val)) /* * test for a relationship to a speed * for input, output, error, and all * devices */ #define inspeed(val) checkspeed(thistty.input.speed, val) #define outspeed(val) checkspeed(thistty.output.speed, val) #define errspeed(val) checkspeed(thistty.errput.speed, val) #define allspeed(val) (inspeed(val) && outspeed(val) && errspeed(val)) /* * these are the relations for SPEED */ #define GR 1 /* (tty speed) > (number) */ #define LT 2 /* (tty speed) < (number) */ #define EQ 3 /* (tty speed) = (number) */ #define GE 4 /* (tty speed) >= (number) */ #define LE 5 /* (tty speed) <= (number) */ #define NE 6 /* (tty speed) != (number) */ /* * the terminal description information is a bit bulky -- basically, * names of the devices and speeds are obtained for each of the terminals * connected to stdin, stdout, and stderr; all this information is saved * in a TTY structure */ struct ttydesc { char *path; /* full device name of tty */ char *devname; /* common name of this device */ long speed; /* speed */ }; typedef struct ttyinfo { struct ttydesc input; /* input tty description */ struct ttydesc output; /* output tty description */ struct ttydesc errput; /* error tty description */ } TTY; /* * speeds in the language are represented by a relation and a number * anything satisfying the relation with the number is acceptable; * relations are the usual =, <, >, !=, <=, >=, and the number goes * on the right, so (i.e.) a speed of less than 9600 baud is saved as * { 9600, < } */ typedef struct speedinfo { long rate; /* requisite speed */ int state; /* relation (EQ, NE, ...) */ } SPEED; %} /* * for reasons due entirely to the way YACC processes input, * this has to go here since SPEED is used as a type in the * header file to declare yylval */ %union { char *fval; /* as a character string */ int ival; /* as an integer */ SPEED sval; /* as a speed */ } /* * tokens returned by the lexical analyzer yylex() */ %token AND /* integer: ampersand */ %token ANY /* integer: matches any tty */ %token EOL /* integer: no more input */ %token ERRPUT /* integer: look only at tty on stderr */ %token INPUT /* integer: look only at tty on stdin */ %token LPAR /* integer: left parenthesis */ %token NAME /* char *: name of tty device */ %token NONE /* integer: matches no tty */ %token NOT /* integer: exclamation point */ %token OR /* integer: vertical bar */ %token OUTPUT /* integer: look only at tty on stdout */ %token PATTERN /* char *: pattern for tty device */ %token RATE /* SPEED: speed of tty device */ %token RPAR /* integer: right parenthesis */ /* * productions analyzed by the parser */ %type stat /* integer: expr with an EOL token tacked on */ %type expr /* integer: 1 if tty matches description so far */ %type ttyname /* integer: 1 if tty name matches device name */ %type ttyspeed /* integer: 2 if tty speed matches device speed */ /* * expression operators * these are arranged so NOT has highest precedence, * followed by OR and AND (which must be on the same line since * they are of equal precedence) */ %left OR AND %right NOT %{ /* * table of speeds * UNIX systems store speeds as indices into a table; * the elements stored in the table are the speed in baud rate; * since this varies from UNIX to UNIX, it is defined in "sysdep.h" * (fractional baud rates get rounded to the nearest long) */ long speedtab[] = { SPEEDS }; /* table of tty speeds */ #define SZSPEEDTAB (sizeof(speedtab)/sizeof(long)) /* number of elements */ /* * unless escaped, any of the following characters terminate a file name */ char *eofile = ",=>'){ /* * not equal to */ lptr++; yylval.sval.state = NE; } else{ /* * just less than */ yylval.sval.state = LT; } /* * get the speed */ yylval.sval.rate = getnum(); return(RATE); case '>': /* greater than/not equal to a speed */ if (*lptr == '='){ /* * greater than or equal to */ lptr++; yylval.sval.state = GE; } else if (*lptr == '<'){ /* * not equal to */ lptr++; yylval.sval.state = NE; } else{ /* * just less than */ yylval.sval.state = GR; } /* * get the speed */ yylval.sval.rate = getnum(); return(RATE); case '!': /* not equal to a speed/negation */ if (*lptr == '='){ /* * not equal to */ lptr++; yylval.sval.state = NE; /* * get the speed */ yylval.sval.rate = getnum(); return(RATE); } /* * more general negation */ return(NOT); case '"': /* pattern match */ /* * set up the pointer and put the character * at the beginning of the array */ f = fname; /* * go until unescaped end-of-file char or tab */ while(*lptr && *lptr != '\t' && *lptr != '"'){ if (*lptr == '\\' && lptr[1]) lptr++; *f++ = *lptr++; } *f = '\0'; if (*lptr == '"') lptr++; /* * save it somewhere (needed in case of lookahead) */ yylval.fval = strsave(fname); return(PATTERN); default: /* anything else is a filename */ /* * set up the pointer and put the character * at the beginning of the array */ f = fname; *f++ = c; /* * go until unescaped end-of-file char or space */ while(*lptr && !isspace(*lptr) && index(eofile, *lptr) == NULL){ if (*lptr == '\\' && lptr[1]) lptr++; *f++ = *lptr++; } *f = '\0'; /* * see if it is "any" */ if (strlen(fname) == 3 && lowcase(fname[0]) == 'a' && lowcase(fname[1]) == 'n' && lowcase(fname[2]) == 'y') return(ANY); /* * see if it is "none" */ if (strlen(fname) == 4 && lowcase(fname[0]) == 'n' && lowcase(fname[1]) == 'o' && lowcase(fname[2]) == 'n' && lowcase(fname[3]) == 'e') return(NONE); /* * save it somewhere (needed in case of lookahead) */ yylval.fval = strsave(fname); return(NAME); } /* NOTREACHED */ } /*==================== S U P P O R T F U N C T I O N S ==================*/ /* * compare the name of a terminal device to the real device's name */ checkname(real, t) struct ttydesc *real; /* description of the current tty */ char *t; /* filename of the device */ { /* * see if it is a full path name (ie, any '/' in it?) * compare appropriately */ if (index(t, '/') == NULL) return(strcmp(t, real->devname) == 0); return(strcmp(t, real->path) == 0); } /* * pattern match a terminal device to the real device's name */ checkpat(real, t) struct ttydesc *real; /* description of the current tty */ char *t; /* filename pattern of the device */ { /* * compile the pattern */ (void) smatch(t); /* * compare appropriately */ return(match(real->devname) || match(real->path)); } /* * compare the current speed to the real device's speed */ checkspeed(tst, this) long tst; /* the current speed */ SPEED *this; /* the speed and expected relationship */ { /* * check for a bogus speed */ if (tst == -1) return(-1); /* * now compare */ switch(this->state){ case EQ: return(tst == this->rate); case LE: return(tst >= this->rate); case GE: return(tst <= this->rate); case NE: return(tst != this->rate); case LT: return(tst < this->rate); case GR: return(tst > this->rate); } /* * bad relationship -- fail! */ return(-1); } /* * read in a number */ long getnum() { register long l; /* used to gather number */ /* * be sure there's something there */ while(isspace(*lptr)) lptr++; if (!isdigit(*lptr)) return(-1); /* * loop through the digits */ for(l = 0; isdigit(*lptr); lptr++){ l *= 10; switch(*lptr){ case '0': l += 0; break; case '1': l += 1; break; case '2': l += 2; break; case '3': l += 3; break; case '4': l += 4; break; case '5': l += 5; break; case '6': l += 6; break; case '7': l += 7; break; case '8': l += 8; break; case '9': l += 9; break; } } /* * return the number */ return(l); } /*= I N I T I A L I Z A T I O N A N D C H E C K I N G R O U T I N E S =*/ /* * initialize the terminal status function */ inittty() { T_TTY ttybuf; /* tty structure */ char *p; /* used to get name */ int indx; /* index into tty speed array */ /* * get the current input tty state */ if (ioctl(fileno(stdin), IO_GTTY, &ttybuf) < 0){ thistty.input.speed = -1; thistty.input.path = strsave(DEFTTY); } else{ if ((indx = TTYISPEED(ttybuf)) < 0 || indx > SZSPEEDTAB) indx = -1; thistty.input.speed = speedtab[indx]; thistty.input.path = strsave(ttyname(fileno(stdin))); } if ((p = rindex(thistty.input.path, '/')) == NULL) thistty.input.devname = thistty.input.path; else thistty.input.devname = p + 1; /* * get the current output tty state */ if (ioctl(fileno(stdout), IO_GTTY, &ttybuf) < 0){ thistty.output.speed = -1; thistty.output.path = strsave(DEFTTY); } else{ if ((indx = TTYOSPEED(ttybuf)) < 0 || indx > SZSPEEDTAB) indx = -1; thistty.output.speed = speedtab[indx]; thistty.output.path = strsave(ttyname(fileno(stdout))); } if ((p = rindex(thistty.output.path, '/')) == NULL) thistty.output.devname = thistty.output.path; else thistty.output.devname = p + 1; /* * get the current error tty state */ if (ioctl(fileno(stderr), IO_GTTY, &ttybuf) < 0){ thistty.errput.speed = -1; thistty.errput.path = strsave(DEFTTY); } else{ if ((indx = TTYOSPEED(ttybuf)) < 0 || indx > SZSPEEDTAB) indx = -1; thistty.errput.speed = speedtab[indx]; thistty.errput.path = strsave(ttyname(fileno(stderr))); } if ((p = rindex(thistty.errput.path, '/')) == NULL) thistty.errput.devname = thistty.errput.path; else thistty.errput.devname = p + 1; #ifdef DEBUG /* * print it if need be */ prtty(&thistty, "CURRENT TTY"); #endif } /* * this routine sets up the string for parsing, calls the parser, * and handles the results */ isintty(t) char *t; /* buffer for string */ { /* * get the current tty description */ inittty(); /* * set up the pointer to the input * for yylex, the lexical analyzer */ lptr = &t[strlen(t)-1]; if (*lptr == '\n') *lptr = '\0'; lptr = t; /* * parse the date and process the result */ if (yyparse()){ #ifdef DEBUG printf("illegal terminal description\n"); #endif return(0); } #ifdef DEBUG printf("terminal %s this description\n", ttyval ? "matches" : "does not match"); #endif return(ttyval); } /* * error in parse * we just log it; since we do NOT do error recovery, * the attempt is rejected */ yyerror(s) char *s; /* error message (supplied by YACC) */ { char buf[BUFSIZ]; /* error message */ register char *p; /* used to format error message */ extern int linect; /* line number */ /* * truncate message at the end of field */ if ((p = index(lptr, '\t')) != NULL) *p = '\0'; /* * print the error message */ (void) sprintf(buf, "(%2d): %s -- bad tty (at \"%s\")\n", linect, s, --lptr); err(L_INFO|F_COND, buf); } /*===================== M A I N C A L L I N G R O U T I N E S ==============*/ /* * two sets of main routines * if "DEBUG" is defined, you get a main routine that reads in one date, * parses it, and prints the result * if "DEBUG" is not defined, you get a routine that returns 1 if the current * tty matches the description of the argument string, 0 if not * * "DEBUG" san be set to two levels; "1" gives you just the result (1 or 0), * but you can use the print function "prtty()" to print out key tty * descriptions in the parse. "2" gives you complete debugging info from * YACC as well */ #ifdef DEBUG /* * set the flag YYDEBUG */ #if DEBUG > 1 #define YYDEBUG /* flag so YACC will generate debugging info */ extern int yydebug; /* constant to tell YACC to generate debugging info */ #endif int linect = 0; /* number of expressions so far */ /* * main routine -- set current tty, read the description, parse it, * and print the result */ main() { char buf[BUFSIZ]; /* input buffer */ #if DEBUG > 1 yydebug = 1; /* YACC is to give full debugging output */ #endif /* * get the input; if EOF, quit */ while(fgets(buf, BUFSIZ, stdin) != NULL){ /* * another expression */ linect++; /* * reset lexer state */ attab = 0; /* * clobber any trailing newline */ lptr = &buf[strlen(buf)-1]; if (*lptr == '\n') *lptr = '\0'; /* * print the buffer */ printf("buf is <%s>\n", buf); /* * set up the pointer to the input * for yylex, the lexical analyzer */ lptr = buf; /* * parse the terminal and process the result */ (void) isintty(buf); } } /* * print the given tty information, preceeded by the given string, * in a readable format */ prtty(s, lb) TTY *s; /* tty */ char *lb; /* label string */ { printf("%s (input) %s (%s) %d baud\n", lb, s->input.path, s->input.devname, s->input.speed); printf("%s (output) %s (%s) %d baud\n", lb, s->output.path, s->output.devname, s->output.speed); printf("%s (error) %s (%s) %d baud\n", lb, s->errput.path, s->errput.devname, s->errput.speed); } /* * error in parse * this mimics a function in lsu */ err(f, s) unsigned int f; /* flag */ char *s; /* error message (supplied by YACC) */ { fprintf(stderr, "%s\n", s); } /* * allocate and copy a string into its own space * this mimics a function in lsu */ char *strsave(s) char *s; /* string to be saved */ { register char *p; /* used to point to new space */ char *malloc(); /* storage allocator */ char *strcpy(); /* used to copy string */ /* * handle a NULL pointer properly */ if (s == NULL) s = ""; /* * allocate the space */ if ((p = malloc((unsigned)(strlen(s)+1))) == NULL) return(NULL); /* * copy the string and return the new string */ (void) strcpy(p, s); return(s); } #endif */ if (strlen(fname) == 4 && lowcase(fname[0]) == 'n' && lowcase(fname[1]) == 'o' && lowcase(fname[2]) == 'n' && lowcase(fname[3]) == 'e') return(NONE); /* * salsu/util.c 644 1625 12 13521 5106523465 6102 /* * LSU -- local superuser * * Author: * Matt Bishop * Research Institute for Advanced Computer Science * NASA Ames Research Center * Moffett Field, CA 94035 * * mab@riacs.edu ARPA * ...!decvax!decwrl!riacs!mab UUCP * ...!ihnp4!ames!riacs!mab UUCP * * Copyright (c) 1986. */ #include "lsu.h" /* * setname -- get the tail of the path name; if legal, set progname to * the name in the "runas" array */ setname(path) char *path; /* full path name of program */ { register char *p, *q, *r; /* used to find tail */ register int i; /* counter in a for loop */ char buf[BUFSIZ]; /* buffer for error message */ /* * get the tail part of the name */ for(q = p = path; *p != '\0'; p++){ if (*p == '/'){ for(r = p; *p == '/'; p++); if (*p == '\0'){ *r = '\0'; break; } q = p; } } /* * pick the element of runas[] that this program * is being run as */ for(i = 0; runas[i].pname != NULL; i++) if (strcmp(q, runas[i].pname) == 0){ progname = runas[i].pname; progis = &runas[i]; return; } progname = q; progis = &runas[i]; /* * the program can only be called as SU or LSU * die on anything else */ (void) sprintf(buf, "bad program name (%s)\n", path); err(L_INFO|F_FAIL, buf); } /* * isinlist -- see if arg 1 is in comma-separated list for arg2 * if arg 1 is NULL, return the first element of the list in arg 1 * if first element of list is "any", match */ char *isinlist(name, buf) char *name; /* name of person to check */ char *buf; /* list to be checked */ { register char *b; /* used to split comma-separated parts */ register char *eob; /* points to end of name list */ /* * find the end of the name list */ while(isspace(*buf)) buf++; for(eob = buf; *eob && *eob != '\t'; eob++); if (*eob) *eob++ = '\0'; /* * if the list is "ANY", match */ if (lowcase(buf[0]) == 'a' && lowcase(buf[1]) == 'n' && lowcase(buf[2]) == 'y' && (buf[3] == '\0' || buf[3] == '\t' || buf[3] == '\n')) return(eob); /* * loop until you run out of list */ do{ /* * split the next name off from the comma-separated list */ for(b = buf; *b && *b != ','; b++); if (*b == ',') *b++ = '\0'; /* * compare -- see if this is it */ if (strcmp(name, buf) == 0) return(eob); } while(*(buf = b) != '\0'); /* * not found -- return nothing */ return(NULL); } /* * vfypwd -- check the password */ vfypwd(encpw) char *encpw; /* encrypted password */ { register int i; /* used to blank out passwords */ register char *p; /* used to load passwords */ /* * demand a password if the stored password field is not null; */ if (curpw.pw_uid != 0 && encpw != NULL && *encpw != '\0'){ /* * we need a password */ p = getpass("Password: "); if (success != F_FAIL && (p == NULL || (strcmp(encpw, crypt(p, encpw)) != 0 && strcmp(encpw, BOGUSPWD) != 0))) err(L_INFO|F_FAIL, "invalid password\n"); /* * blank out the password in core (just in case) */ for(i = 0; i < SZPASSWORD; i++) p[i] = '\001'; } } /* * strsave -- saves a string in reserved space */ char *strsave(s) char *s; /* string to be copied */ { char *p; /* pointer to new space */ /* * if there is no string, make an empty one */ if (s == NULL) s = ""; /* * allocate space for the string */ if ((p = malloc((unsigned) (strlen(s)+1))) == NULL) err(L_INFO|F_COND|F_SYS, "malloc"); /* * copy the string into the reserved memory * and return the reserved memory space */ (void) strcpy(p, s); return(p); } /* * err -- print an error message (like perror) */ err(flag, what) unsigned int flag; /* error flags */ char *what; /* what to print */ { char message[BUFSIZ]; /* message to print */ /* * if a system error, get the message */ if (bitset(flag, F_SYS)){ if (errno < sys_nerr) (void) sprintf(message, "%s: %s\n", what, sys_errlist[errno]); else (void) sprintf(message, "%s: unknown error #%d\n", what, errno); } else (void) strcpy(message, what); /* * if checking syntax, just report syntax errors */ if (progname == CSU){ (void) fprintf(stderr, "%s: %s", progname, message); return; } /* * log the error message */ logmessage(flag&L_MASK, message); /* * mail a message */ if (bitset(flag, F_PERM)) mailperm(message); if (bitset(flag, F_LOG)) maillog(message); /* * how to allow access */ if (bitset(flag, F_FAIL)) success = F_FAIL; if (bitset(flag, F_COND) && (success != F_FAIL)) success = F_COND; } /* * this mails a message to a list of users * who maintain the access file */ static char errperm[BUFSIZ] = "%s %s << xxEOFxx\n\ From: root\n\ To: %s\n\ Subject: \"su\" access file problems\n\ \n\ When %s tried to run \"%s\", it encountered a problem with the\n\ access file:\n\ %s\ (The stamp for the log messages is [%05d].) This must be fixed at once\n\ or the \"su\" programs will fail consistently. As of now, only\n\ \"su root\" or \"nsu root\" can work.\n\ xxEOFxx\n"; mailperm(mesg) char *mesg; /* message */ { char buf[BUFSIZ]; /* buffer for mail message */ (void) sprintf(buf, errperm, LSUMAIL, LSUMAINT, LSUMAINT, curpw.pw_name, progname, mesg, stamp); (void) system(buf); } /* * this mails a message to a list of users * who maintain the log file */ static char errlog[BUFSIZ] = "%s %s << xxEOFxx\n\ From: root\n\ To: %s\n\ Subject: \"su\" log file problems\n\ \n\ When %s tried to run \"%s\", it encountered a problem with the\n\ log file:\n\ %s\ As a result, none of the \"su\" programs can log messages. This\n\ must be fixed at once or the \"su\" programs will fail consistently.\n\ As of now, only \"su root\" or \"nsu root\" can work.\n\ xxEOFxx\n"; maillog(mesg) char *mesg; /* message */ { char buf[BUFSIZ]; /* buffer for mail message */ (void) sprintf(buf, errlog, LSUMAIL, LSUMAINT, LSUMAINT, curpw.pw_name, progname, mesg); (void) system(buf); } inal description\n"); #endif return(0); } #ifdef DEBUG printf("terminal %s this description\n", ttyval ? "matches" : "does not match"); #endif return(ttyval); } /* lsu/ACCESS 644 1625 12 13032 5106523465 5642 # ================== # |PERMISSION FILES| # ================== # # All permission files are the same; this is one for "lsu". Just substitute # "nsu" or "su" for "lsu" to get the permission file for those programs. # # SAMPLE # ====== # # This is a sample file; at the end of the sample list is a description of # how to make new entries # # user who he can ttys on which when he can # name become he can change change # ---- ---------- ------------- ----------- mab spool,rlb ttyi9 ANY rlb mab ANY Monday spool root "ttyp.*" ANY # # HOW TO MAKE ENTRIES IN THIS FILE # === == ==== ======= == ==== ==== # # Lines have the following form: # userid newuser ttys time # where: # userid is the login name of the person allowed to run lsu # newuser is a (list of comma-separated) login name(s) to which # userid may lsu; case is not ignored, but the word # "any" matches any user (and this word may be in any # case) # ttys is a (list of comma-separated) terminal devices from # which userid may run lsu to become newuser; see the # detailed description below. The word "any" matches # any device. # time describes the time interval during which userid may # lsu to newuser; see the detailed description below. # The word "any" matches any time. # There may be multiple lines for one userid; lsu scans the file until # a matching line is found, or (if none) until the end of file is reached. # # Within both the following syntaxes, the following may also be used, # where "expr" is either a legal tty or time. Note time and tty exprs # CANNOT be mixed in the same field: # # stat == expr # expr == '(' expr ')' (grouping) # '!' expr (negation) # expr '&' expr (disjunction) # expr '|' expr (conjunction) # 'any' (matches any time or tty) # 'none' (matches no time or tty) # # For example, to give a time as the weekend nights, you can say: # ( Saturday | Sunday ) & 8pm - 8am # (for "Saturday or Sunday and between 8pm and 8am".) # # TTY SYNTAX # === ====== # The syntax of tty is: # ttyname | ttyspeed # where: # ttyname == name (ie, /dev/ttyi9) # '+' name (ie, +/dev/ttyi9) # '-' name (ie, -/dev/ttyi9) # '*' name (ie, */dev/ttyi9) # pattern (ie, "/dev/ttyp.") # '+' pattern (ie, +"/dev/ttyp.") # '-' pattern (ie, -"/dev/ttyp.") # '*' pattern (ie, *"/dev/ttyp.") # ttyspeed == rate (ie, >=9600) # '+' rate (ie, +>=9600) # '-' rate (ie, ->=9600) # '*' rate (ie, *>=9600) # rate == relation number # name == string # pattern == '"' string '"' # "relation" is any of "@", "=", "!=", "<>", "><", "<", ">", "<=", ">=", # all with the obvious meanings; "number" is a baud rate. "string" is a # string; if put in double quotes (a "pattern"), pattern matching rather # than character comparison is done using the pattern syntax of ed(1). # If preceded by "+", apply only to standard input; by "-", apply only to # standard output; by "*", apply only to standard error. # # # TIME SYNTAX # ==== ====== # The syntax of time is: # day_of_year day_of_week time_of_day # where: # day_of_year == month number ',' number (ie, Aug 29, 1986) # month number (ie, Aug 29) # month ',' number (ie, Aug, 1986) # month (ie, Aug) # number '/' number '/' number (ie, 8/29/86) # number '/' number (ie, 8/29) # day_of_year '-' day_of_year (ie, 8/29-8/31) # means from the first to the second # day_of_year ',' day_of_year (ie, 8/29,8/31) # means the first or the second # day_of_week == special_day # Any of "Sunday", "Monday", "Tuesday", # "Wednesday", "Thursday", "Friday", # or "Saturday" # day_of_week '-' day_of_week (ie, Monday-Tuesday) # means from the first to the second # day_of_week ',' day_of_week (ie, Monday,Friday) # means the first or the second # time_of_day == number ':' number ':' number mer (ie, 8:12:56 pm) # number ':' number ':' number (ie, 20:12:56) # number ':' number meridian (ie, 8:12 pm) # number ':' number (ie, 20:12) # number meridian (ie, 8 pm) # number (ie, 20) # special_time # Any of "noon", "midnight" # time_of_day '-' time_of_day (ie, 8am-4pm) # means from the first to the second # time_of_day ',' time_of_day (ie, 8am,4pm) # means the first or the second # "meridian" is "am" or "pm". Enough of "special_day" and "special_time" words # to identify the word uniquely must be given; for example, any of "Au", "Aug", # "Augu", "Augus", or "August" will match "August"; but "A" will not, since it # might also be "Am" or "Any". Note also case is irrelevant. Examples of # legal times are: # August 10, 1986 - August 20, 1986 Monday - Friday 9AM - 5PM # This describes the working week between August 10 # and August 20 in 1986 # Tuesday noon - midnight # This describes times on Tuesdays from noon until # midnight # 8/10/86 - 8/20/86 Mon-Fri 9-17 # This is the same as the first description # Saturday-Sunday 8pm-8am # This is the same as the example given before the TTY SYNTAX # section. # # LSU PERMISSION FILE ENTRIES # === ========== ==== ======= # Example of lsu permission file entries: # mab Any ttyi9,console Mon-Fri 9am-5pm # "mab" can lsu to any other user from ttyi9 or the console # during any day of the working week from 9am to 5pm # rlb root,bin,staff ttyh3 Aug 10 - Aug 20 Mon # "rlb" can lsu to "root", "bin", or "staff" from ttyh3 # on any Monday from August 10 to August 20 of the current # year # will fail consistently.\n\ As of now, only \"su root\" or \"nsu root\" can work.\n\ xxEOFxx\n"; maillog(mesg) char *mesg; /* message */ { char buf[BUFSIZ]; /* buffer for mail message */ (void) sprintf(buf, errlog, LSUMAIL, LSUMAINT, LSUMAINT, curpw.pw_name, progname, mesg); (void) system(buf); } inal description\n"); #endif return(0); } #ifdef DEBUG printf("terminal %s this description\n", ttyval ? "matches" : "does not match"); #endif return(ttyval); } /* lsu/Cover 644 1625 12 2517 5106523465 5745 .t troff ..q -F tr -t 2 .hy 0 .po 1.25i .ps 12 .vs 14 .nh .na .de pp .sp 1 .ti 5n .. .rs .de us \" underlined string \\$1\l'|0\(ul' .. .wh -1i pb .de pb 'bp 'sp 1i .. .sp 2.i .tl '''June 25, 1987' .sp 2 .in 1i .ti 0 .nf TO:\h'|1i'Alan Fernquist, Bill Wall .ti 0 \v'1v'FROM:\h'|1i'Matt Bishop .ti 0 \v'1v'CC:\h'|1i'Peter Denning .ti 0 \v'1v'SUBJECT:\h'|1i'Final report on action items #9, #31, and #36 .fi .in 0 .sp .pp The program relevant to these items has been completed and a development test has been completed. It is now ready for operational testing. .pp The development test consisted of replacing the distributed ``su'' with the ``su'' version of this program on orville and running it for 24 hours. No serious problems were noted (a very minor one was fixed within one minute of my receiving it) and all people and programs that used it continued to work. .pp Attached is a paper describing the concerns that led to the development of the programs, as well as a manual page for them. The installation guide is in the back of the paper. .pp Please note that an extended abstract of this paper was presented at the Large System Administrators Workshop in Philadelphia on April 9-10, 1987, and that the complete paper has been submitted to a technical journal dealing with \s-2UNIX\s0 that the USENIX Association will begin publishing early next year. v'1v'SUBJECT:\h'|1i'Final report on action items #9, #31, and #36 .fi .in 0 .sp .pp The program relevant to these items has been completed and a development test has been complelsu/Setup 755 1625 12 2751 5106523465 5772 #! /bin/sh if test "$1" then SYS="$1" else echo 'Type the name of your system ' echo 'Here are the legal entries: ' echo ' Type this if your system is this' echo ' BSD4_2 Berkeley Software Distribution Release 4.2' echo ' BSD4_3 Berkeley Software Distribution Release 4.3' echo ' DYNIX2_0 Sequent Balance Dynix Release 2.0' echo ' NPSN3 NAS Processing System Network Build 3' echo ' ROS3_3 Ridge Operating System Release 3.3' echo ' SGI2_3 Silicon Graphics Graphics Library 2, Workstation 2.3' echo ' SGI3_4 Silicon Graphics Graphics Library 3, Workstation 3.4' echo ' SGI3_5 Silicon Graphics Graphics Library 3, Workstation 3.5' echo ' SUN4_2 Sun Microsystems UNIX Release 4.2' echo ' SYSV AT&T System V' echo ' UNICOS Cray UNIX Operating System' echo ' UTS Amdahl UTS' read SYS GARBAGE fi case "$SYS" in BSD4_3) break;; BSD4_2) break;; DYNIX2_0) break;; NPSN3) break;; ROS3_3) break;; SGI2_3) break;; SGI3_4) break;; SGI3_5) break;; SUN4_2) break;; SYSV) break;; UNICOS) break;; UTS) break;; *) echo "$0: bad system name $1 -- just type $0 for help" 1>&2; exit 1;; esac echo 'Working ...' if test -r Makefile then rm -f Makefile fi ln Make/Make.$SYS Makefile WARN=yes for i in /lib/libc.a /usr/lib/libcrypt.a do if test -s $i then if test "`nm $i | grep crypt`" then WARN=no break fi fi done echo 'To make and install lsu, type "make install"' if test "$WARN" = "yes" then echo 'You will have to find and install the "crypt()" library first.' fi exit 0 be used, # where "exprlsu/Makefile 644 1625 12 3355 5307730702 6402 # # lsu -- local super user # # variables that tailor the program SUPERM="./.su" LSUPERM = "./.lsu" LOG="./.log" # this should not be changed; it names the system (see sysdep.h # for the meaning and other abbreviations) SYS = BSD4_3 # this is a temp file used to edit the source E = Ed/Ed.$(SYS) # usual make stuff CFLAGS = -O -D$(SYS) DESTDIR = /usr/local/bin/ EXEC = su lsu nsu csu LIBS = RM = rm -f OBJECTS = lsu.o log.o pat.o perm.o syntax.o time.o tty.o util.o pat.o SOURCES = lsu.c log.c pat.c perm.c syntax.c time.y tty.y util.c pat.c all: $(EXEC) su: $(OBJECTS) $(CC) $(CFLAGS) -o su $(OBJECTS) $(LIBS) csu: $(OBJECTS) $(CC) $(CFLAGS) -o csu $(OBJECTS) $(LIBS) lsu: $(OBJECTS) $(CC) $(CFLAGS) -o lsu $(OBJECTS) $(LIBS) nsu: $(OBJECTS) $(CC) $(CFLAGS) -o nsu $(OBJECTS) $(LIBS) dtime: time.c $(CC) -o dtime $(DEBUG) $(CFLAGS) time.c dtty: tty.c lsu.h $(CC) -o dtty $(DEBUG) $(CFLAGS) tty.c pat.c $(OBJECTS): sysdep.h lsu.h # all this does is edit in the new definitions of SUPERM, LSUPERM, and LOG lsu.h: lsu.H fixup cp lsu.H lsu.h sh $E fixup | ed - lsu.h time.c: time.y $(YACC) time.y; sed 's/yy/y1/g' y.tab.c > time.c; $(RM) y.tab.c tty.c: tty.y $(YACC) tty.y; sed 's/yy/y2/g' y.tab.c > tty.c; $(RM) y.tab.c fixup: Makefile echo $(SUPERM) | sed 's/./& /g' > fixup echo $(LSUPERM) | sed 's/./& /g' >> fixup echo $(LOG) | sed 's/./& /g' >> fixup install: $(EXEC) -cp $(EXEC) $(DESTDIR) chown root $(DESTDIR)lsu $(DESTDIR)su $(DESTDIR)nsu $(DESTDIR)csu chmod 4755 $(DESTDIR)lsu $(DESTDIR)su $(DESTDIR)nsu $(DESTDIR)csu lint: time.c tty.c lsu.h lint -phbac -D$(SYS) lsu.c log.c pat.c perm.c syntax.c time.c tty.c util.c clean: $(RM) $(OBJECTS) y.tab.c clobber: $(RM) $(EXEC) $(OBJECTS) lsu.h time.c tty.c y.tab.c fixup dtime dtty 'any' (matches any time or tty) # 'none' (matches no time or tty) # # For example, to give a time as the weekend nights, you can say: # ( Saturday | Sunday ) & 8pm - 8am # (for "Saturday or Sunday and between 8pm and 8am".) # # TTY SYNTAX # === ====== # The syntax oflsu/lsu.H 644 1625 12 15526 5106523462 5701 /* * LSU -- local superuser header file * * Author: * Matt Bishop * Research Institute for Advanced Computer Science * NASA Ames Research Center * Moffett Field, CA 94035 * * mab@riacs.edu ARPA * ...!decvax!decwrl!riacs!mab UUCP * ...!ihnp4!ames!riacs!mab UUCP * * Copyright (c) 1986. */ #include #include #include #include #include /* * this should run on several different versions of UNIX(tm) * the dependencies are localized into this file */ #include "sysdep.h" /* * the important file names (ie, the permission file and the log file * names) are constructed in memory just before use and cleared imme- * diately after; this prevents people from snooping through the exe- * cutable file to find the log or permission file name. To change * these, change them in the Makefile. Changing * LSUPERM changes the permission file for "lsu" * SUPERM changes the permission file for "su" * LOG changes the log file * DO NOT CHANGE THEM HERE as when you change anything else in the * file, the Makefile will clobber the changes you make here. Also, * don't delete anything -- you may screw up the way the Makefile works. */ #define MAKESUPERM(buf) { /* build the permission file name */ \ /* begin SUPERM */\ buf[0] = '.';\ buf[1] = '/';\ buf[2] = '.';\ buf[3] = 's';\ buf[4] = 'u';\ buf[5] = '\0';\ /* end SUPERM */\ } #define MAKELSUPERM(buf) { /* build the permission file name */ \ /* begin LSUPERM */\ buf[0] = '.';\ buf[1] = '/';\ buf[2] = '.';\ buf[3] = 'l';\ buf[4] = 's';\ buf[5] = 'u';\ buf[6] = '\0';\ /* end LSUPERM */\ } #define MAKELOG(buf) { /* build the log file name */ \ /* begin LOG */\ buf[0] = '.';\ buf[1] = '/';\ buf[2] = '.';\ buf[3] = 'l';\ buf[4] = 'o';\ buf[5] = 'g';\ buf[6] = '\0';\ /* end LOG */\ } #define CLEARNAME(buf) { /* clear the buffer */ \ register char *bc = buf; \ while(*bc) \ *bc++ = '\0'; \ } /* * internal definitions */ /* some universal UNIX goodies */ /* lower case version of c */ #define lowcase(c) (isupper((c))?tolower((c)):(c)) #define SZPASSWORD 8 /* max password length (see getpass(3)) */ #define BOGUSPWD "\001\001\001" /* illegal output from crypt(3) */ #define bitset(w,b) (((w)&(b))==(b)) /* defaults for lsu */ #define LSUCOMM '#' /* begins comment lines in LSUPERM */ #define LSUSHELL "/bin/sh" /* default shell */ #define LSUUSER "root" /* default user to su/lsu to */ #define SUPERUID 0 /* superuser UID */ /* logging options */ #define L_MASK 0x000f /* mask for logging options */ #define L_INFO 0x0000 /* no action */ #define L_NO 0x0001 /* don't allow user to lsu */ #define L_YES 0x0002 /* allow user to lsu */ #define L_CHK 0x0004 /* check syntax */ /* error options */ #define F_MASK 0x0ff0 /* mask for error options */ #define F_NONE 0x0000 /* no errors */ #define F_FAIL 0x0010 /* ?su is to fail */ #define F_COND 0x0020 /* fail UNLESS is (n)su to root */ #define F_PERM 0x0040 /* mail perm file message to LSUMAINT */ #define F_LOG 0x0080 /* mail log file message to LSUMAINT */ #define F_SYS 0x0100 /* system error */ /* errors in perm file lines */ #define E_MASK 0xf000 /* mask for error in perm file options */ #define E_NONE 0x0000 /* no error */ #define E_USER 0x1000 /* user not listed */ #define E_NEWU 0x2000 /* user can't lsu/su to that new user */ #define E_TTY 0x4000 /* bad tty */ #define E_TIME 0x8000 /* bad time */ /* abbreviations for legal program names (see runas[]) */ #define LSU (runas[0].pname)/* as local super user */ #define SU (runas[1].pname)/* as super user */ #define NSU (runas[2].pname)/* as super user */ #define CSU (runas[3].pname)/* as syntax checker */ /* used to determine what shell variables to reset */ #define V_NONE 0x0001 /* never reset this */ #define V_ALWAYS 0x0002 /* always reset this */ #define V_LSU 0x0004 /* reset this for lsu */ #define V_SU 0x0008 /* reset this for su */ #define V_NSU 0x0010 /* reset this for nsu */ #define V_CSU 0x0020 /* reset this for csu */ #define V_ORLOGIN 0x0040 /* reset if login OR another test met */ #define V_ANDLOGIN 0x0080 /* reset if login AND another test met */ /* useful versions of NULL */ #define CNULL ((char *) NULL) #define PNULL ((struct passwd *) NULL) /* * forward definitions */ char *isinlist(); /* determine if a name is in a list */ char *strsave(); /* make a copy of a string in reserved memory */ FILE *chkperm(); /* check and open the permission file */ /* * system variables */ extern int errno; /* system error number */ extern int sys_nerr; /* number of elements in sys_errlist[] */ extern char *sys_errlist[]; /* list of system error messages */ /* * system library functions */ char *crypt(); /* encrypt password */ char *ctime(); /* get time in ASCII format */ char *getpass(); /* get password from user */ struct passwd *getpwnam(); /* get data in password file for name */ struct passwd *getpwuid(); /* get data in password file for uid */ char *malloc(); /* allocate some memory */ char *rindex(); /* get the index of the rightmost char */ char *strcat(); /* catenate a string */ char *strcpy(); /* copy a string */ long time(); /* get time in internal format */ char *ttyname(); /* return the name of associated tty */ /* * structure to control permissions and such */ struct perms { char *lname; /* log name */ char *pname; /* program name */ }; /* * structure to control resetting of shell environment variables */ struct sv { char *splate; /* template to be reset */ int len; /* length of the template to be compared */ char **cval; /* if a string required, what it is */ unsigned int vused; /* when used */ }; /* * global variables */ extern char *progname; /* program name */ extern struct perms *progis; /* what the program runs as */ extern struct perms runas[]; /* legal program names -- MUST be one of these */ extern FILE *logfp; /* log file pointer */ extern char *date; /* date of run */ extern char *tty; /* terminal name or background */ extern char *towho; /* command line argument */ extern struct passwd curpw; /* information about who's running the program */ extern struct passwd newpw; /* information about who you're lsu'ing to */ extern char *shell; /* shell to be exec'ed */ extern char *exclude[]; /* exclude directories from path if new UID is 0 */ extern struct sv shvar[]; /* list of shell variables to change */ extern int stamp; /* PID stamp of this process (for logging) */ extern int dash; /* 1 if a login shell to be used */ extern int mailmesg; /* 1 if file problem message to be mailed */ extern unsigned int success; /* 1 if l/n/csu is to succeed */ #ifndef lint /* * these are used to keep track * of how and when the binaries * were made */ extern char *lsu_system; /* what operating system this was made for */ extern char *lsu_version; /* what version this is */ #endif s the first or the second # "meridian" is "am" or "pm". Enough of "special_day" and "special_time" words # to identify the word uniquely must be given; for example, any /* * LSU -- local superuser header file * * Author: * Matt Bishop * Research Institute for Advanced Computer Science * NASA Ames Research Center * Moffett Field, CA 94035 * * mab@riacs.edu ARPA * ...!decvax!decwrl!riacs!mab UUCP * ...!ihnp4!ames!riacs!mab UUCP * * Copyright (c) 1986. */ #include #include #include #include #include /* * this should run on several different versions of UNIX(tm) * the dependencies are localized into this file */ #include "sysdep.h" /* * the important file names (ie, the permission file and the log file * names) are constructed in memory just before use and cleared imme- * diately after; this prevents people from snooping through the exe- * cutable file to find the log or permission file name. To change * these, change them in the Makefile. Changing * LSUPERM changes the permission file for "lsu" * SUPERM changes the permission file for "su" * LOG changes the log file * DO NOT CHANGE THEM HERE as when you change anything else in the * file, the Makefile will clobber the changes you make here. Also, * don't delete anything -- you may screw up the way the Makefile works. */ #define MAKESUPERM(buf) { /* build the permission file name */ \ /* begin SUPERM */\ buf[0] = '.';\ buf[1] = '/';\ buf[2] = '.';\ buf[3] = 's';\ buf[4] = 'u';\ buf[5] = '\0';\ /* end SUPERM */\ } #define MAKELSUPERM(buf) { /* build the permission file name */ \ /* begin LSUPERM */\ buf[0] = '.';\ buf[1] = '/';\ buf[2] = '.';\ buf[3] = 'l';\ buf[4] = 's';\ buf[5] = 'u';\ buf[6] = '\0';\ /* end LSUPERM */\ } #define MAKELOG(buf) { /* build the log file name */ \ /* begin LOG */\ buf[0] = '.';\ buf[1] = '/';\ buf[2] = '.';\ buf[3] = 'l';\ buf[4] = 'o';\ buf[5] = 'g';\ buf[6] = '\0';\ /* end LOG */\ } #define CLEARNAME(buf) { /* clear the buffer */ \ register char *bc = buf; \ while(*bc) \ *bc++ = '\0'; \ } /* * internal definitions */ /* some universal UNIX goodies */ /* lower case version of c */ #define lowcase(c) (isupper((c))?tolower((c)):(c)) #define SZPASSWORD 8 /* max password length (see getpass(3)) */ #define BOGUSPWD "\001\001\001" /* illegal output from crypt(3) */ #define bitset(w,b) (((w)&(b))==(b)) /* defaults for lsu */ #define LSUCOMM '#' /* begins comment lines in LSUPERM */ #define LSUSHELL "/bin/sh" /* default shell */ #define LSUUSER "root" /* default user to su/lsu to */ #define SUPERUID 0 /* superuser UID */ /* logging options */ #define L_MASK 0x000f