v21i023: System ecurity analysis tool, Part01/05

Rich Salz rsalz at bbn.com
Fri Mar 23 04:55:59 AEST 1990


Submitted-by: Dan Farmer <df at sei.cmu.edu>
Posting-number: Volume 21, Issue 23
Archive-name: cops/part01

COPS is a security tool that is useful to system administrators, system
programmers, or for anyone who would like to learn about UNIX security.
It does not restrict a system's environment by placing constraints on
activity; it is a purely diagnostic tool that checks and reports on the
current status of a given UNIX machine.

Written in Bourne shell, generic commands (awk, sed, etc.) and some C, the
system is basically a shell script that runs several small security
programs.  Theoretically (at least), it attempts to find the following
problems (among others) on a generic UNIX system, and then mails or saves
the results, if indeed any problems do exist:
  --Checks /dev/*mem and all devs listed in "/etc/fstab" for world
    read/writability.
  --Checks special/important directories and files for "bad" (world
    writable, whatever) modes.  (/etc/passwd, /bin, etc.)
  --Checks against /etc/passwd for crummy passwords (user selectable, it
    can be as vigorous or as lax as you wish.)
  --Checks /etc/passwd for non-unique uids, invalid fields, non-numeric
    user ids, etc.  Also includes a password checker.
  --Checks /etc/group for non-unique groups, invalid fields, non-numeric
    group ids, etc.
  --Checks all users' home directories and their
    .login/.cshrc/.rhosts/.profile/etc. files
  --Checks all commands and paths listed in /etc/rc* and crontabs for
    world writability.
  --Checks for bad root paths, world exportable NFS systems, some other
    misc stuff.
  --Includes the Kuang expert system.  Written by Robert Baldwin, this
    basically checks to see if a given user (by default root) is
    compromisible, given that certain rules are true.  Kind of hard to
    explain in a sentence, but worth the price of admission.
  --Checks the system for _changes_ in SUID status.  This is the one (the
    only) program that should be run as superuser, because it runs a
    "find" on all SUID programs from the / directory, and then uses that as
    a reference file for future runs.

The "README" file tells how to install, run, and interpret any results
found by COPS.


#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
mkdir cops 2>/dev/null
mkdir cops/docs 2>/dev/null
mkdir cops/src 2>/dev/null
mkdir cops/extensions 2>/dev/null
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	cops/MANIFEST
#	cops/README
#	cops/XTRA_CREDIT
#	cops/chk_strings
#	cops/cops
#	cops/cron.chk
#	cops/dev.chk
#	cops/dir.chk
#	cops/dir.chklst
#	cops/disclaimer
#	cops/file.chk
#	cops/file.chklst
#	cops/group.chk
#	cops/init_kuang
#	cops/kuang
#	cops/makefile
#	cops/pass.words
#	cops/passwd.chk
# This archive created: Tue Jan 30 23:35:07 1990
# By:	dan (Purdue University)
echo shar: extracting cops/MANIFEST '(763 characters)'
cat << \SHAR_EOF > cops/MANIFEST
File Name
==================
MANIFEST
README
XTRA_CREDIT
chk_strings
cops
cron.chk
dev.chk
dir.chk
dir.chklst
docs
file.chk
file.chklst
group.chk
init_kuang
kuang
makefile
pass.words
passwd.chk
rc.chk
reconfig
root.chk
stop.sample
suid.chk

docs/COPS.report
docs/KUANG.README
docs/SUID.README
docs/cops
docs/cron
docs/dev
docs/dir
docs/file
docs/group
docs/home
docs/is_able
docs/kuang.1
docs/kuang.man
docs/pass
docs/passwd
docs/rc
docs/release.notes
docs/root
docs/suid.man
docs/tilde
docs/user
docs/warnings

extensions/THINGS_2_DO
extensions/YAR
extensions/netstuff
extensions/passwords
extensions/questions

src/addto.c
src/clearfiles.c
src/filewriters.c
src/home.chk.c
src/is_readable.c
src/is_writable.c
src/members.c
src/pass.c
src/tilde.c
src/user.chk.c
SHAR_EOF
echo shar: extracting cops/README '(14146 characters)'
cat << \SHAR_EOF > cops/README

   Welcome!  You now hold in your hands (terminal?) a collection
of security tools that are designed specifically to aid the typical
UNIX systems administrator, programmer, operator, or consultant in
the oft neglected area of computer security.
   The package, which will be henceforth be referred to as COPS
(Computer Oracle and Password System), can be broken down into three
key parts.  The first is the actual set of programs that attempt
to automate security checks that are often performed manually (or
perhaps with self written short shell scripts or programs) by a systems
administrator.  The second part is the documentation, which details
how to set up, operate, and to interpret any results given by the
programs.  Finally, COPS is an evolving beast.  It includes a list
of possible extensions that might appear in future releases, as well
as pointers to other works in UNIX security that could not be included
at this time, due to space or other restrictions.
   This document contains six sections:

      1) What is COPS?
      2) What COPS is _not_
      3) How to Configure/Install COPS
      4) Running COPS for the 1st Time
      5) Continued Use of COPS
      6) Disclaimer and End Notes


1) What is COPS?
-----------------

   COPS is a collection of about a dozen programs that each attempt
to tackle a different problem area of UNIX security.  Among the areas
checked are file, directory, and device permissions/modes, passwords,
contents of password and group files, the contents of /etc/rc && cron
files, changes in SUID status, the writability of users home directories
and startup files (.profile, .cshrc, etc), and a few others as well.
It also includes the Kuang expert system, written by Bob Baldwin, that
takes a set of rules and tries to determine if your system can be
compromised.  For a more complete list of all of the checks, look at the
file "release.notes" or "cops.report."
   All of the programs merely warn the user of a potential problem --
COPS DOES NOT ATTEMPT TO CORRECT OR EXPLOIT ANY OF THE POTENTIAL PROBLEMS
IT FINDS!  COPS either mails or creates a file (user selectable) of any
of the problems it finds while running on your system.  And because COPS
does not correct potential hazards it finds, it does _not_ have to be
run by a privileged account (i.e. root or whomever.)  The only security
check that should be run by root to get maximum results is the SUID checker;
although it can be run as an unprivileged user, to find all the SUID files
in a system, it should be run as root.
   The programs are mostly written in Bourne shell (using awk, sed, grep,
etc. as well) for (hopefully) maximum portability.  A few are written
in C for speed (most notably the Kuang expert system and for implementing
fast user home directory searching), but the entire system should run on
most BSD and System V machines with a minimum of tweaking.

2) What COPS is _not_
----------------------

   COPS merely provides a method of checking for common procedural errors.
It is not meant to be used as a replacement for common sense or user/
operator/administrative alertness!  Think of it as an aid, a first line
of defense -- not as an impenetrable shield against security woes.  An
experienced wrong-doer could easily circumnavigate _any_ protection that
COPS can give.  However, COPS _can_ aid a system in protecting its users
from (their own?) ignorance, carelessness, and the occasional malcontent
user.
   Once again, COPS does not correct any errors found.  There are several
reasons for this; first and foremost, computer security is a slippery
beast.  What is a major breach in security at one site may be a standard
policy of openness at another site.  Additionally, in order to correct all
problems it finds, it would have to be run as a privileged user; and I'm
not going to go into the myriad problems of running SUID shell scripts
(See the bibliography at the end of the technical report "cops.report"
for pointer to a good paper on this subject by Matt Bishop.)
   At this time, COPS does not attempt to detect bugs or features (such
as infamous ftpd, fingerd, etc) that may cause security problems.  Although
this may change in future versions, the current line of reasoning to avoid
general publication of programs such as these is that all the problems that
COPS detects can be repaired on any system it runs on.  However, many bugs
can be readily repaired only be having source code (and possibly a good
vendor to repair it), and many sites would have serious troubles if they
suddenly discovered unrepairable problems that could compromise their
livelihood.  It is possible that a more controlled release may come out
in the future to address such problems (but don't mail to me about getting
them -- unless you want to help write them! :-))

3) How to Configure/Install COPS
---------------------------------

  System V users, other Non-BSD systems, or sites with commands in
strange places -- you may have to run a shell script called "reconfig"
to change the pathnames of the executable programs called when using
COPS.  If your system does not use the paths listed in the shell
scripts, try running "reconfig".  This will reconfigure the pathnames
used by COPS to your system; COPS should run fine then, if it
can find all of the commands (reconfig should tell you if it
cannot.)  If trouble persists, you will have to change the paths
to your executable files (awk, sed, etc) by hand.  A drag, I know.
This all may change without notice, anyway.

4) Running COPS for the 1st Time
---------------------------------

   Since most of COPS was written and tested mostly on just a few machines
(at least compared to the total number out there!), you may have significant
differences that were not anticipated -- unfortunately, or fortunately,
UNIX is not quite standardized yet.
   COPS is run by simply typing "cops".  "cops" is a Bourne shell script
that runs each of the smaller programs, accumulates the output, and then
mails or stores any results.  "suid.chk", since it can take a long, long time
to run, is the only "standalone" program in the COPS package; look at
suid.man for more information.

   To run COPS for the first time, I suggest doing the following:

   -- Look at the disclaimer, file "disclaimer".  Don't sue me.
      Actually, this holds for all the times you use COPS (1/4 :-))
   -- Type "make" to create the formatted manual pages, to compile the
      C programs,  and to make the shell programs executable.
   -- Read the technical report to understand what COPS is doing and
      what is going on -- "cops.report".  This gives a look at the
      philosophies, design notes, and finally a general outlay of the
      COPS system and UNIX security.
   -- Next, change lines 36 and 37 in the "cops" shell file from:
        SECURE=/usr/foo/bar
        SECURE_USERS="foo at bar.edu"
      SECURE should be the same directory as the directory that contains
      the cops programs, and SECURE_USERS should be your own login id, or
      to whomever you designate as the recipient of the output (your enemy?)
   -- Set "MAIL=NO" in the "cops" shell file (line 22).  This will prevent
      a large mail file from choking the mailer.  All of the output will be
      put into a file called "report.$$", where $$ is the process
      number that cops had while running.
   -- Look at the directory and file configuration files, "dir.chklst"
      and "file.chklst".  They contain critical files that COPS checks
      for world writability.  Add or delete whatever files/directories
      you wish; if a file doesn't exist, COPS will effectively ignore it.
      (If you don't know or are uncertain what files/directories are
      important, what is given there is a good set to start with on most
      systems.
   -- You may wish to comment out the password checker (line 72 in the
      "cops" shell file).  Although this is not necessary, it will speed
      up the package if you wish for immediate gratification.

  You should be ready to roll.  COPS is run by simply typing "cops" (you
may wish to put in the background....)  If you followed my advice and
set "MAIL=NO" in the "cops" shell file, after COPS is finished, there
will be a report file created "report.$$" that lists the time and machine
it was created on.  Otherwise, COPS mails the report to the user listed
on the line 'SECURE_USERS="foo at bar.edu"'.  There is a file "warnings", which
contains most of the warning messages COPS uses, as well as a brief
explanation of how the message might pertain to your system as well as how
to "fix" any problem.

   NOTE: Change the shell script "cops" to reflect who you want the output
sent to and where the location of the program is BEFORE running the program.


5) Continued Use of COPS
-------------------------

   Once you are satisfied that COPS indeed does something useful
(hopefully this will occur :-)), a good way to use it is to run it
on at least a semi-regular basis.  Even if it doesn't find any problems
immediately, the types of problems and holes it can detect are of the
sort that can pop up at any given time.  One way of running COPS
might be to run it as an "at" job or by cron.
   I highly advise that whatever directory COPS is placed in is to be
readable, writable, and executable only by the owner (typing 
"chmod 700 /usr/foo/bar" or whatever the name is will do this) of the
directory.  This is to prevent prying eyes from seeing any security
problems your site may have.  Even if you don't think of them as
important, someone else might come around and change your mind.  Since
COPS is fairly configurable, an intruder could easily change the paths
and files that COPS checks for, hence making it fairly worthless.  Again,
this comes back to the point that COPS is only a tool -- don't put down
your defensive shields merely because COPS says "all clear".  If this
sounds paranoid, it is!  Security people are traditionally paranoid,
for a reason....  In any case, it is probably not a good idea to advertise
any (even) potential weaknesses.

   After running COPS, if any warnings are given that compromise any
individual users accounts (such as world writable .profiles, home
directories, guessed passwords, etc.), and the warnings are not corrected
immediately (or you are not sure whether or not it is worth hassling
the user to change it), try this:
   Edit the file "init_kuang", and add the compromised user(s) uids and
groups in their respective target lines (below lines 21 and 27,
respectively), and run kuang again to see if the users can compromise
the entire system.  You may change your mind about not thinking
they are a problem!  In addition, kuang does not have to have "root" 
as a target (the last line).  Try putting in system administrators or
other powerful figures to see if they are in danger as well.

6) Disclaimer and End Notes
----------------------------

   COPS is meant to be a tool to aid in the tightening of security, not
as a weapon to be used by an enemy to find security flaws in a system.
It may be argued that allowing anyone to have access to such a tool may
be dangerous.  But hopefully the overall benefit for systems that use
this package will outweigh any negative impact.  To me it is akin to a
law enforcement problem -- that although telling the public how to break
into a house may foster a slight rise in break-in attempts, the overall
rise in public awareness on how to defend themselves would actually result
in a drop in break-ins.  The crackers with black hats already know how
to crush system defenses and have similar tools, I'm sure.  It's time
we fought back.

  COPS is not the final answer to anyone's security woes.  You can use
the system as long as you realize that COPS has no warranty, implied
or otherwise, and that any problems that you may have with it are
not my or any of the other authors fault.  I will certainly attempt to
help you solve them, if I am able, but please don't try to sue me or
anything...  Let's all make COPS a collective effort that helps people, ok?
If you have ideas for additional programs, or a better implementation of
any of the programs here, I would be very interested in seeing them.
COPS was the work of a LOT of people, both in writing code and in the
testing phase (thanks beta testers!).  For a complete list of contributors,
look at the file "XTRA_CREDIT".

   So good luck, and I hope you find COPS useful as we plunge into UNIX
of the 1990's.

   dan farmer
   January 31, 1989


# include "disclaimer.h"

-------------------- Cut here for disclaimer -------------------------

/***********************************************************************
* Copyright 1989, 1990 by Purdue University and Dan Farmer.  All rights
* reserved.  Some individual files may be covered by other copyrights.
* 
* This material was written and compiled by Dan Farmer while at Purdue
* University in 1989 and 1990, under the direction and sponsorship of
* Professor Gene Spafford.  Other material was contributed as noted
* elsewhere.
* Redistribution and use in source and binary forms are permitted
* provided that this entire copyright notice is duplicated in all such
* copies, and that any documentation, announcements, and other
* materials related to such distribution and use acknowledge that the
* software was developed at Purdue University, W. Lafayette, IN.  No
* charge, other than an "at-cost" distribution fee, may be charged for
* copies, derivations, or distributions of this material without the
* express written consent of the copyright holders.  Neither the
* name of the University, the name of the author, nor the name of this
* project's sponsor may  be used to endorse or promote products
* derived from this material without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTIBILITY AND FITNESS FOR ANY PARTICULAR PURPOSE.
************************************************************************/
---------------------- End disclaimer -------------------------------
SHAR_EOF
echo shar: extracting cops/XTRA_CREDIT '(2063 characters)'
cat << \SHAR_EOF > cops/XTRA_CREDIT

  Code credits are where code credits are due.  If I miss anyone, please
forgive (and notify) me!

Gene Spafford -- overall design help.

Robert Baldwin -- the kuang package.

Craig Leres, Seth Alford, Roger Southwick, Steve Dum, and Rick Lindsley
all get credit for the password guessing program.

Prentiss Riddle -- the suid checker.

  And of course lots of credit goes to my great Beta-release sweatshop team;
especially Adri Verhoef for tightening up lots of my crummy code (cops,
group.chk, root.chk, is_writable, dev.chk, dir.chk & file.chk among others),
Steve Romig for good ideas _and_ letting me use a system V machine to test
on (how many people do you know that would let you test a security
system on their system with no strings attached!) Jason Levitt, Jim
Kimble, Jim Rowan, Stefan Vorkoetter, Judy Scheltema, Pete Troxell (all
the Sun C2 stuff....), Dennis Conley, and of course John Sechrest.
Tony Petrost pointed out some of my incorrect assumptions and helped
fix cron.chk.  Kudos also to Bruce Spence for giving me some good
implementation ideas at LISA II.

  If strings is not available to you, a version is available on uunet;
also a nifty install program written by Kevin Braunsdorf that can be used
as a super directory/file mode checker/security device should be
available soon in comp.sources.unix (these programs large sizes preculudes
their inclusion in COPS, but I recommend looking into them.)
Both can be gotten via anonymous ftp.  Strings is in comp.unix.sources
directory, install should be in the same.
  Everything else not explicitely mentioned in the COPS.report.ms paper
or here was written by me.  Not mentioned execpt in the source code are
some small changes made by myself to make everything fit in as a cohesive
whole; I tried to make comments in the source code if I changed it (never
to drastic in any case.)

  For a good story on the subject, you might want to read _The Cuckoo's
Egg_, by Clifford Stoll.  This is a true tale of a sysadmin's fight 
against beaurocracy and a hacker (the bad kind.)  Good stuff.

SHAR_EOF
echo shar: extracting cops/chk_strings '(1559 characters)'
cat << \SHAR_EOF > cops/chk_strings
#!/bin/sh
#
#  Usage: chk_strings filename
#
#  This will check pathnames inside executable files for writability,
# using the "strings" command and egrep.
#
#  I have identified three basic types of strings containing paths to files:
# 1)
#    /path1/path2/file			/* standard */
# 2) 
#    '/path1/path2/file'		/* standard, in single quotes */
# 3)
#    :/path1/file1:/path2/file2		/* a path for searching */
#
#  For the first two, I simply test the writability; for the last, I
# parse it into seperate paths and check each one in turn.
#
AWK=/bin/awk
EGREP=/usr/bin/egrep
TEST=/bin/test
ECHO=/bin/echo
SORT=/usr/bin/sort
STRINGS=/usr/ucb/strings

if test ! -s $STRINGS
	then
	exit 0
fi

if test $# -eq 0
	then
	$ECHO "Usage: $0 file"
	exit 2
fi

while test 0 -ne $#
	do
	# $ECHO Checking $1...
	# get the first two types:
	test_files=`$STRINGS $1 | $EGREP "/.*/" | $AWK '{for (i=1;i<=NF;i++) 
	if ((res=substr($i,1,1))=="/") 
		printf("%s\n",$i)
	else if ((res!=":") && (res=substr($i,2,1))=="/")
		printf("%s\n",substr($i,2,length($i)-2))}'| $SORT -u`

	# and type number three, parse into separate paths as well:
	paths=`$STRINGS $1|$EGREP "/.*/" |$AWK '{for (i=1;i<=NF;i++) 
		if ((substr($i,1,1)==":") && (substr($i,2,1))=="/")
			printf("%s",$i)}'`
	paths=`$ECHO $paths | $AWK -F: '{for (i=1;i<=NF;i++) printf("%s\n",$i)}'| $SORT -u`


	all_files=$test_files$paths

	for i in $all_files
		do
		if ./is_writable $i
			then
			$ECHO "      Warning!  File $i (inside root executed file $1) is _World_ writable!"
		fi
		done
	shift
done

# end of script
SHAR_EOF
echo shar: extracting cops/cops '(2872 characters)'
cat << \SHAR_EOF > cops/cops
#!/bin/sh
#
#  Usage: cops
#
#  This will change into the $SECURE directory, ensure all the security
# programs (listed below) indeed do exist, and run all of the security
# programs.  If any of the programs find any security problems, they
# send mail to everyone in the $SECURE_USERS list.  It then destroys all
# temporary files, and exits the program.  Programs that are run
# (besides this one):
#
#	root.chk	dev.chk		dir.chk
#	file.chk	group.chk	home.chk
#	rc.chk		passwd.chk	pass.chk
#	user.chk	cron.chk
# The U-kuang system runs these additional programs:
#	init_kuang	kuang		addto
#	clearfiles	filewriters	members
#
#  If this is changed to "NO", the report that cops creates
# will not be deleted and the results will not be mailed to anyone.
MMAIL="YES"

# Where is everyone?
ECHO=/bin/echo
TEST=/bin/test
RM=/bin/rm
CAT=/bin/cat
MAIL=/bin/mail
DATE=/bin/date
CHMOD=/bin/chmod

######################
#  Change these lines!
######################
SECURE=/usr/foo/bar
SECURE_USERS="foo at bar.edu"
######################

SECURE_PROGRAMS="root.chk dev.chk dir.chk file.chk group.chk \
                 home.chk rc.chk passwd.chk pass.chk \
		 cron.chk user.chk init_kuang kuang addto \
		 clearfiles filewriters members"

if $TEST ! -d "$SECURE"
	then
	$ECHO "Error -- Security directory $SECURE doesn't exist"
	exit 1
fi

$CHMOD 700 $SECURE
cd $SECURE

for i in $SECURE_PROGRAMS
	do
	if $TEST ! -s "$i"
		then
		$ECHO "Error -- Security program $i doesn't exist"
		exit 1
	fi
done

$SECURE/root.chk		>	$SECURE/result.$$ 2> /dev/null
$SECURE/dev.chk			>>	$SECURE/result.$$ 2> /dev/null
$SECURE/dir.chk			>>	$SECURE/result.$$ 2> /dev/null
$SECURE/file.chk		>>	$SECURE/result.$$ 2> /dev/null
$SECURE/rc.chk			>>	$SECURE/result.$$ 2> /dev/null
$SECURE/cron.chk		>>	$SECURE/result.$$ 2> /dev/null
$SECURE/group.chk		>>	$SECURE/result.$$ 2> /dev/null
$SECURE/home.chk		>>	$SECURE/result.$$ 2> /dev/null
$SECURE/passwd.chk		>>	$SECURE/result.$$ 2> /dev/null
$SECURE/pass.chk 		>>	$SECURE/result.$$ 2> /dev/null
$SECURE/user.chk		>>	$SECURE/result.$$ 2> /dev/null
$SECURE/kuang			>	/dev/null 2> /dev/null
if $TEST -s "$SECURE/Success"
	then
	$CAT $SECURE/Success >> $SECURE/result.$$
fi
$RM -f $SECURE/Success


#
#   Mail the final report to $SECURE_USERS and remove the evidence
if $TEST -s "$SECURE/result.$$"
	then
	$ECHO ATTENTION:				>  $SECURE/report.$$
	$ECHO "Security Report for "`$DATE`	>> $SECURE/report.$$

	#
	#  Thanks to arbitron for this idea...
	HOSTNAME=`/bin/sh -c "/bin/uname -n || /usr/bin/uuname -l || /bin/hostname" 2>&-`
	$ECHO "from host $HOSTNAME"			>> $SECURE/report.$$
	$ECHO					>> $SECURE/report.$$
	$ECHO					>> $SECURE/report.$$
	$CAT $SECURE/result.$$			>> $SECURE/report.$$

	if $TEST "$MMAIL" = "YES"
		then
		$MAIL $SECURE_USERS < $SECURE/report.$$
		$RM -f $SECURE/report.$$
	fi

fi
$RM -f $SECURE/result.$$

#  end it all....
exit 0
SHAR_EOF
echo shar: extracting cops/cron.chk '(2248 characters)'
cat << \SHAR_EOF > cops/cron.chk
#!/bin/sh
#
#  Usage: cron.chk
#
#  This checks pathnames and files inside the cron files /usr/lib/crontab
# for writability.
#
#  Mechanism:  The commands inside the file /usr/lib/crontab are executed
# by root.  This shell script greps for commands/paths that begins with
# "/" and takes each potential problem-string and uses the program
# "is_writable" to determine if it is world writable.  All results are
# echoed to standard output.
#  In addition, it throws away everything that has a /tmp, /dev/null, or
# tty in the writable string, and everything after a ">"; e.g. if crontab
# is writing to a file it doesn't care.
#
#  Cron.chk will try to find a file in /usr/lib/crontab first (bsd),
# and then if it isn't there, it will look in the any alternate
# possible locations next -- right now, /usr/spool/cron/crontab -- to
# see if a directory exists, and, if it does, it checks all the cron
# files in turn.
#
#  WARNING!
#
#  Spurious messages can occur; a more stringent method (if perhaps less
# careful of a check) would be to test just the 6th field, instead of
# all the fields after the fifth.  Also throwing away /tmp, etc. could
# be a mistake.
#

#  Location of stuff:
AWK=/bin/awk
SED=/bin/sed
ECHO=/bin/echo
EGREP=/usr/bin/egrep
TEST=/bin/test
CAT=/bin/cat

#  Possible location of crontab file:
cron=/usr/lib/crontab
#  alternate reality locations of crontab file:
alt_cron="/usr/spool/cron/crontabs"

if $TEST ! -s $cron
	then
	cron=""
	for i in "$alt_cron"
		do
		if $TEST -d $i
			then
			cron=`$ECHO $alt_cron/*`
			fi
		done

	if $TEST  -z "$cron"
		then
		exit
		fi
	fi

# finally, do the checking -- maybe for one, maybe for lots of
# cron-ites:

for cron_kid in $cron
	do
	# A typical crontab entry might look something like this:
	#
	#   0,15,30,45 * * * * /bin/sh /usr/adm/newsyslog
	#
	risky_stuff=`$AWK '{for (i=6;i<NF;i++) printf("%s ", $i);
		if (NF!=6) printf("%s\n",$NF)}' $cron_kid | $SED -e 's/>.*//' |
		$AWK '{for (i=1;i<=NF;i++) if (substr($i,1,1)=="/") print $i}'`

	for i in $risky_stuff
		do
		if $TEST `echo $i | $EGREP "/tmp|/dev/null|tty"`
			then
			continue
			fi
		if ./is_writable $i
			then
			$ECHO "Warning!  $i (in $cron_kid) is World writable!"
			fi
		done
	done	# for all the cron-kids
SHAR_EOF
echo shar: extracting cops/dev.chk '(2691 characters)'
cat << \SHAR_EOF > cops/dev.chk
#!/bin/sh
#
#  dev.chk [-g]
#
#   This shell script checks the permissions of /dev/mem, /dev/kmem, and
# all devs listed in the file /etc/fstab (the "mount" command would be
# a preferable way of getting the file system name, but the syntax of the
# output is variable from machine to machine), and flags them if they are
# readable by using the "is_readable" command.  It also checks for
# unrestricted NFS mountings.  By default, dev_check will flag devs only
# if world readable or writable.  The -g option tells it to print out devs
# that are also group readable/writable.
#
AWK=/bin/awk
LS=/bin/ls
ECHO=/bin/echo
TEST=/bin/test

# locations of vital stuff...
mtab=/etc/fstab
exports=/etc/exports

#   Optional List of assorted files that shouldn't be
# readable (mix 'n match; add to the list as desired):
opt_files='/usr/adm/sulog /etc/btmp /.netrc'

group=no

if $TEST $# -gt 1
	then
	$ECHO "Usage: $0 [-g]"
	exit 2
fi

if $TEST $# -eq 1
	then
	if $TEST "X$1" = "X-g"
		then
		group=yes
	else
		$ECHO "Usage: $0 [-g]"
		exit 2
	fi
fi

#  Testing filesystems and devices for improper read/write permissions...

#  NEVER want these readable!
always_crit_files="/dev/kmem /dev/mem"

# grab devices from "/etc/fstab"....
#
#  Format of /etc/fstab:	/dev/zd0e   +junk(:-)
#
#  Or NFS mounted:		uther:/usr/spaf   +junk(:-)
#
#  Not sure what to do with NFS stuff, so we'll ignore it.  Seems that
# this doesn't tell us anything about what we want anyway....
crit_devs=$always_crit_files" "`$AWK 'index($1, "/")==1 {print $1}' $mtab`

# Alternate way; grab devices from "mount [-p]"....
#   Format of output from mount (some machines use -p option, some
# don't.  Check your local man page... :
# crit_devs=$always_crit_files" "`/etc/mount -p|$AWK 'index($1, "/")==1
#					{print $1} \
#				}'`

#
# However, do check for single line entries in /etc/exports:
if $TEST -s $exports
	then
	$AWK '{while(getline >0) if ($0 !~ /^#/ && NF == 1) \
		printf("Warning!  NFS file system %s exported with no restrictions.\n",$0)}' $exports
	fi

for i in $crit_devs
	do
	if ./is_readable $i
		then
		$ECHO Warning!  $i is _World_ readable!
	fi
	if ./is_writable $i
		then
		$ECHO Warning!  $i is _World_ writable!
	fi
	if $TEST "$group" = "yes"
		then
		if ./is_readable -g $i
			then
			$ECHO Warning!  $i is group readable!
		fi
		if ./is_writable -g $i
			then
			$ECHO Warning!  $i is group readable!
		fi
	fi
done

# Do the mix 'n match assorted no-read files:
for i in $opt_files
	do
	if ./is_readable $i
		then
		$ECHO Warning!  $i is _World_ readable!
	fi
	if $TEST "$group" = "yes"
		then
		if ./is_readable -g $i
			then
			$ECHO Warning!  $i is group readable!
		fi
	fi
done

# end of script
SHAR_EOF
echo shar: extracting cops/dir.chk '(1459 characters)'
cat << \SHAR_EOF > cops/dir.chk
#!/bin/sh
#
#  dir.chk [-g]
#
#   This shell script checks the permissions of all directories listed 
# in the configuration file "dirs.755.dirlist",  and flags them if they
# are world-writable.  The -g option tells it to print out directories
# that are also group writable.  See the config file for the format of
# the configuration file.
#
#   Mechanism:  This shell script simply takes each line from the
# configure file and uses the "is_writable" program to check if any of
# the directories in question are writable by world/group.  All results
# are written to standard output.
#
AWK=/bin/awk
TEST=/bin/test
ECHO=/bin/echo

dir_list=dir.chklst
group=no

if $TEST $# -gt 1
	then
	$ECHO "Usage: $0 [-g]"
	exit 2
fi

if $TEST $# -eq 1
	then
	if $TEST "X$1" = "X-g"
		then
		group=yes
	else
		$ECHO "Usage: $0 [-g]"
		exit 2
	fi
fi

#  Testing directories in file $dir_list for potential write mode problems

#  Read from $dir_list (e.g. "dirs.755.dirlist") what dirs to check.
#
# Comments are lines starting with a "#".
#
while read i
do
	case $i in
	 "#"* | "" )
		continue;;
	esac

	# exit code 0 is writable, 1 is not
	dirs=`$ECHO $i`
	for d in $dirs
		do
		if ./is_writable $d
			then
			echo "Warning!  Directory $d is _World_ writable!"
		fi
	done

	if $TEST "$group" = "yes"
		then
		for d in $dirs
			do
			if ./is_writable -g $d
				then
				echo "Warning!  Directory $d is group writable!"
			fi
		done
	fi

done < $dir_list

# end of script
SHAR_EOF
echo shar: extracting cops/dir.chklst '(377 characters)'
cat << \SHAR_EOF > cops/dir.chklst
#
#  This lists any/all sensitive files the administration wants to ensure
# non-writability of.  Comments are lines starting with a "#".
#
#   Lines are of the format:
#
#	/path/to/file
#
/
# /*
/etc
/usr
/bin

/usr/spool
/usr/adm
/usr/etc
/usr/lib
/usr/local
/usr/local/bin
/usr/local/lib
/usr/bin
/usr/etc
/usr/spool/mail
/usr/spool/news
/usr/spool/uucp
/usr/spool/at
/Mail
SHAR_EOF
echo shar: extracting cops/disclaimer '(1468 characters)'
cat << \SHAR_EOF > cops/disclaimer
/***********************************************************************
* Copyright 1989, 1990 by Purdue University and Dan Farmer.  All rights
* reserved.  Some individual files may be covered by other copyrights.
* 
* This material was written and compiled by Dan Farmer while at Purdue
* University in 1989 and 1990, under the direction and sponsorship of
* Professor Gene Spafford.  Other material was contributed as noted
* elsewhere.
* Redistribution and use in source and binary forms are permitted
* provided that this entire copyright notice is duplicated in all such
* copies, and that any documentation, announcements, and other
* materials related to such distribution and use acknowledge that the
* software was developed at Purdue University, W. Lafayette, IN.  No
* charge, other than an "at-cost" distribution fee, may be charged for
* copies, derivations, or distributions of this material without the
* express written consent of the copyright holders.  Neither the
* name of the University, the name of the author, nor the name of this
* project's sponsor may  be used to endorse or promote products
* derived from this material without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTIBILITY AND FITNESS FOR ANY PARTICULAR PURPOSE.
************************************************************************/
SHAR_EOF
echo shar: extracting cops/file.chk '(1423 characters)'
cat << \SHAR_EOF > cops/file.chk
#!/bin/sh
#
#  file.chk [-g]
#
#   This shell script checks the permissions of all files listed 
# in the configuration file "files.chklst",  and flags them if they
# are world-writable.  The -g option tells it to print out files
# that are also group writable.  See the config file the format of
# the configuration file.
#
#   Mechanism:  This shell script simply takes each line from the
# configure file and uses the "is_writable" program to check if any of
# the files in question are writable by world/group.  All results
# are written to standard output.
#
AWK=/bin/awk
TEST=/bin/test
ECHO=/bin/echo

file_list=file.chklst
group=no

if $TEST $# -gt 1
	then
	$ECHO "Usage: $0 [-g]"
	exit 2
fi

if $TEST $# -eq 1
	then
	if $TEST "X$1" = "X-g"
		then
		group=yes
	else
		$ECHO "Usage: $0 [-g]"
		exit 2
	fi
fi

# Checking files in file $file_list for potential write mode problems

#  Read from $file_list (e.g. "files.chklst") what files to check.
#
# Comments are lines starting with a "#".
#
while read i
do
	case $i in
	 "#"* | "" )
		continue;;
	esac

	# exit code 0 is writable, 1 is not
	files=`$ECHO $i`
	for f in $files
		do
		if ./is_writable $f
			then
			echo "Warning!  File $f is _World_ writable!"
		fi
	done

	if $TEST "$group" = "yes"
		then
		for f in $files
			do
			if ./is_writable -g $f
				then
				echo "Warning!  File $f is group writable!"
			fi
		done
	fi

done < $file_list

# end of script
SHAR_EOF
echo shar: extracting cops/file.chklst '(523 characters)'
cat << \SHAR_EOF > cops/file.chklst
#
#  This lists any/all sensitive files the administration wants to ensure
# non-writability of.  Comments are lines starting with a "#".
#
#   Lines are of the format:
#
#	/path/to/file
#
/.*
#	/.login
#	/.profile
#	/.cshrc
#	/.crontab
#	/.rhost
/etc/*
#	/etc/passwd
#	/etc/group
#	/etc/inittab
#	/etc/rc
#	/etc/rc.local
#	/etc/rc.boot
#	/etc/hosts.equiv
#	/etc/profile
#	/etc/syslog.conf
#	/etc/export
/usr/etc/yp*
/usr/lib/crontab
/usr/lib/aliases
/usr/lib/sendmail
# /usr/spool/L.sys
# /usr/adm/sulog
/usr/adm/*
/bin/*
SHAR_EOF
echo shar: extracting cops/group.chk '(5099 characters)'
cat << \SHAR_EOF > cops/group.chk
#!/bin/sh
#
#   group.chk
#
#  Check group file -- /etc/group -- for incorrect number of fields,
# duplicate groups, non-alphanumeric group names, and non-numeric group
# id's.
#
# Awk part based on _passwd_ from _The AWK Programming Language_, page 78
#
#   Mechanism:  Group.check uses awk to ensure that each line of the group
# has 4 fields, as well as examining each line for any duplicate groups or
# any duplicate user id's in a given group by using "sort -u" to ferret
# out any duplications.  It also checks to make sure that the password
# field (the second one) is a "*", meaning the group has no password (a
# group password is usually not necessary because each member listed on 
# the line has all the privilages that the group has.)  All results are
# echoed to standard output.  Finally it ensures that the group names
# are alphanumeric, that the group id's are numeric, and that there are
# no blank lines.  For yellow pages groups, it does the same checking,
# but in order to get a listing of all members of the groups, it does a
# "ypcat group > ./$$" and uses that temporary file for a groupfile.
# It removes the tmp file after using it, of course.
#   The /etc/group file has a very specific format, making the task
# fairly simple.  Normally it has lines with 4 fields, each field
# separated by a colon (:).  The first field is the group name, the second
# field is the encrypted password (an asterix (*) means the group has no
# password, otherwise the first two characters are the salt), the third
# field is the group id number, and the fourth field is a list of user
# ids in the group.  If a line begins with a plus sign (+), it is a yellow
# pages entry.  See group(5) for more information.
#
#
AWK=/bin/awk
SED=/bin/sed
ECHO=/bin/echo
TEST=/bin/test
SORT=/usr/bin/sort
UNIQ=/usr/bin/uniq
YPCAT=/usr/bin/ypcat
RM=/bin/rm

#   Used for Sun C2 security group file.  FALSE (default) will flag
# valid C2 group syntax as an error, TRUE attempts to validate it.
# Thanks to Pete Troxell for pointing this out.
C2=FALSE

etc_group=/etc/group
yp_group=./$$
yp=false

if $TEST -f $YPCAT
	then
if $TEST -s $YPCAT
	then
	$YPCAT group > $yp_group
	if $TEST $? -eq 0
		then
		yp=true
		fi
	fi
fi

# Testing $etc_group for potential problems....

#   First line is for a yellow pages entry in the group file.
# It really should check for correct yellow pages syntax....
$AWK 'BEGIN {FS = ":" } {
	if (substr($1,1,1) != "+") { \
	if ($0 ~ /^[ 	]*$/) { printf("Warning!  Group file, line %d, is blank\n", NR) } else {
	if (NF != 4) { printf("Warning!  Group file, line %d, does not have 4 fields: %s\n", NR, $0) } \
	if ($1 !~ /[A-Za-z0-9]/) {
		printf("Warning!  Group file, line %d, nonalphanumeric user id: %s\n", NR, $0) } \
	if ($2 != "" && $2 != "*") {
		if ("'$C2'" != "TRUE")
			printf("Warning!  Group file, line %d, group has password: %s\n", NR, $0)
		else {
			if ("#$"$1 != $2)
				printf("Warning!  Group file, line %d, group has invalid field for C2:\n%s\n", NR, $0) } \
		} \
	if ($3 !~ /[0-9]/) {
		printf("Warning!  Group file, line %d, nonnumeric group id: %s\n", NR, $0) }}}} ' $etc_group

#
#  Look for duplications in groups in $etc_group
#
result=`$AWK -F: '{print $1}' $etc_group | $SORT |$UNIQ -d`
if $TEST "$result"
	then
	$ECHO "Warning!  Duplicate Group(s) found in $etc_group:"
	$ECHO $result
fi

#
#   Next, check for duplicate users in a group in /etc/group.  Let
# awk do all the work (thanks, adri!)
#

# Ignore all groups with less than two members.
#
awk -F: '
	split($4, users, ",") > 1 {
		ct = 0
		for (i in users) {
			curuser = users[i]
			for (j in users) {
				if (j > i && curuser == users[j]) {
					if (ct++ == 0) print "Warning!  Group "$1" has duplicate user(s):"
					print curuser
				}
			}
		}
	}
	' $etc_group


#
# Test yellow pages groups as well
if $TEST "$yp" = "true"
	then
$AWK 'BEGIN {FS = ":" } {
	if ($0 ~ /^[ 	]*$/) { printf("Warning!  YGroup file, line %d, is blank\n", NR) } else {
	if (NF != 4) { printf("Warning!  YGroup file, line %d, does not have 4 fields: %s\n", NR, $0) } \
	if ($1 !~ /[A-Za-z0-9]/) {
		printf("Warning!  YGroup file, line %d, nonalphanumeric user id: %s\n", NR, $0) } \
	if ($2 != "" && $2 != "*") {
		printf("Warning!  YGroup file, line %d, group has password: %s\n", NR, $0) } \
	if ($3 !~ /[0-9]/) {
		printf("Warning!  YGroup file, line %d, nonnumeric group id: %s\n", NR, $0) }}} ' $yp_group

#
#  Look for duplications in groups in yellow pages groups
#
	yresult=`$AWK -F: '{print $1}' $yp_group | $SORT |$UNIQ -d`
	if $TEST "$yresult"
		then
		$ECHO "Warning!  Duplicate Group(s) found in yellow pages group:"
		$ECHO $result
	fi
#
#   Next, check for duplicate users in a group in yellow groups.  Let
# awk do all the work (thanks, adri!) 

# ignore all groups with one member.
#
	awk -F: '
	split($4, users, ",") > 1 {
		ct = 0
		for (i in users) {
			curuser = users[i]
			for (j in users) {
				if (j > i && curuser == users[j]) {
					if (ct++ == 0) print "Warning!  YGroup "$1" has duplicate user(s):"
					print curuser
				}
			}
		}
	}
	' $yp_group

fi

$RM -f $yp_group

# end
SHAR_EOF
echo shar: extracting cops/init_kuang '(773 characters)'
cat << \SHAR_EOF > cops/init_kuang
# /* Copyright 1985 Robert W. Baldwin */
# /* Copyright 1986 Robert W. Baldwin */
###############################################
# Kuang: Rule based computer security checker.
###############################################

CAT=/bin/cat
ECHO=/bin/echo

#
# Initialization.
#
./clearfiles
#
# First setup what we have access to.
# The uids.k file must include the user 'OTHER' meaning the world access bits.
# Add any other UIDs accessible to the attacker (e.g., ftp, daemon).
#
# Directly accessible user IDs.
$CAT >uids.k <<END
OTHER
END
#
# Directly accessible group IDs.
# This usually includes a group like 'users', which most users are in.
#
$CAT >gids.k <<END
END
#
# Setup the primary goal(s).
#
$ECHO Setting up goal						#>/dev/tty
./addto uids root DO ANYTHING
SHAR_EOF
echo shar: extracting cops/kuang '(5967 characters)'
cat << \SHAR_EOF > cops/kuang
# /* Copyright 1985 Robert W. Baldwin */
# /* Copyright 1986 Robert W. Baldwin */
#
# Jan 1990, Ported to bourne shell from Csh.  Dan Farmer
#
#   Took out some comments, combined four of Bob's shell
# scripts into one (the target script remains separate for
# easy editing of targets.)  More or less a straight line
# for line translation; a rewrite that goes for speed will
# come later.  Maybe just rewrite it in C.  Yeah, that's it....

###############################################
# Kuang: Rule based computer security checker.
###############################################

# commands used....
SH=/bin/sh
MV=/bin/mv
TEST=/bin/test
ECHO=/bin/echo
AWK=/bin/awk
RM=/bin/rm

# Initialization.
$SH ./init_kuang

# Main loop
#
$ECHO Starting main loop                        #>/dev/tty
while $TEST -f uids.n -o -f gids.n -o -f files.n
    do
    if $TEST -f uids.n ; then
        $MV uids.n uids.x

# Process a list of uids from stdin.
# Usage: douids username comments
    $ECHO Called douids                        #>/dev/tty
    i=1
    while $TEST "1"
        do
        nextuid=`$AWK '{if (NR=="'$i'") print $0}' uids.x`
        i=`expr $i + 1`

        if $TEST -z "$nextuid"  ; then
            break;
	    fi

            user=`$ECHO $nextuid | $AWK '{print $1}'`

        $ECHO "   " User $user                    #>/dev/tty

# Rules mapping uids to files.
#
        next=`$ECHO $nextuid | $AWK '{for (i=2;i<=NF;i++) printf("%s ", $i)}'`
        ./addto files /etc/passwd replace grant $user $next
        ./addto files /usr/lib/aliases replace trojan $user $next

#   hsh = home sweet home = home directory of $user
        hsh=`./tilde $user`

        if $TEST -f $hsh/.rhosts ;  then
            ./addto files $hsh/.rhosts write grant $user $next
        fi

        if $TEST -f $hsh/.login ;  then
            ./addto files $hsh/.login replace trojan $user $next
        fi

        if $TEST -f $hsh/.cshrc ;  then
            ./addto files $hsh/.cshrc replace trojan $user $next
        fi

        if $TEST -f $hsh/.profile ;  then
            ./addto files $hsh/.profile replace trojan $user $next
        fi

        if $TEST "$user" = "root" ;  then
	    if $TEST -f /usr/lib/crontab ; then
               ./addto files /usr/lib/crontab replace create supershell $next
	    else
               ./addto files /usr/spool/cron/crontabs replace create supershell $next
	    fi
            ./addto files /etc/rc replace trojan $user $next
            ./addto files /etc/rc.local replace trojan $user $next
        fi

        if $TEST "$user" != "root" ;  then
            ./addto files /etc/hosts.equiv replace allow rlogin $next
        fi

        if $TEST "$user" != "root" -a -f /etc/hosts.equiv -a -s /etc/hosts.equiv 
            then
            ./addto files /etc/hosts replace fake HostAddress $next
        fi

    done
fi

    if $TEST -f gids.n ; then
       $MV gids.n gids.x

    $ECHO Called dogids                        #>/dev/tty
    i=1
    while $TEST "1"
        do
        nextgid=`$AWK '{if (NR=="'$i'") print $0}' gids.x`
        i=`expr $i + 1`

        if $TEST -z "$nextgid" ; then
            break;
	    fi

        group=`$ECHO $nextgid | $AWK '{print $1}'`
        $ECHO "   " Group $group                    #>/dev/tty

# Rules mapping gids to uids.
#
        next=`$ECHO $nextgid | $AWK '{for (i=2;i<=NF;i++) printf("%s ", $i)}'`
        use=`./members $group`
        for user in $use
            do
            ./addto uids $user grant $group $next
            done

# Rules mapping gids to files.
#
        ./addto files /etc/group replace grant $group $next
        done
    fi

    if $TEST -f files.n ; then
       $MV files.n files.x

# A list of file names is read from successive lines of stdin.
# Each file is examined for ways to access it.
# The input format is:
#    <filename> <whitespace> <mode> <comments>
# The <mode> is either "write" or "replace".
#
    $ECHO Called dofiles.                        #>/dev/tty
    i=1
    while $TEST "1"
        do
        nextfile=`$AWK '{if (NR=='"$i"') print $0}' files.x`
        i=`expr $i + 1`
        if $TEST -z "$nextfile" ; then
            break;
	    fi

        file=`$ECHO $nextfile | $AWK '{print $1}'`
        mode=`$ECHO $nextfile | $AWK '{print $2}'`

        $ECHO "    File $file, mode $mode"            #>/dev/tty

# Rules converting filename goals into UserName or GroupName goals.
#
        next=`$ECHO $nextfile | $AWK '{for (i=3;i<=NF;i++) printf("%s ", $i)}'`

        writers=`./filewriters $file`
        numwriters=`$ECHO $writers | $AWK '{print NF}'`
        if $TEST "$numwriters" = "3" ; then
            owner=`$ECHO $writers | $AWK '{print $1}'`
            group=`$ECHO $writers | $AWK '{print $2}'`
            other=`$ECHO $writers | $AWK '{print $3}'`

            $ECHO "        Writers are $owner $group $other"    #>/dev/tty
                ./addto uids $owner $mode $file $next
            if $TEST "$group" != "NONE" ; then
                ./addto gids $group $mode $file $next
            fi
            if $TEST "$other" != "NONE" ; then
                ./addto uids $other $mode $file $next
            fi
        else
            $ECHO "        $file does not exist"        #>/dev/tty
            continue
        fi

# Rules converting filename goals into other filename goals.
#
        if $TEST "$mode" != "replace" ; then
            continue
        fi

    parent=`$ECHO $file | $AWK -F/ '{if (NF == 2) {
		printf("/%s", $1)}
		else if (NF>2) {for (i=2;i<NF;i++) printf("/%s", $i)} 
		else printf("")'}`

    basename=`$ECHO $file | $AWK -F/ '{print $NF}'`

    $ECHO -n "       " Parent directory is $parent        #>/dev/tty
    $ECHO ", " basename is $basename                #>/dev/tty
    if $TEST -n "$parent" ; then
       ./addto files $parent write replace $basename $next
        fi
    done

    fi
done

# destroy the evidence.... Need "Success" file for report, though.
$RM files.? gids.? uids.?
SHAR_EOF
echo shar: extracting cops/makefile '(2721 characters)'
cat << \SHAR_EOF > cops/makefile
#  Simple Makefile for the COPS system; compiles, and chmods 
# the programs.
#
#	make all	    -- makes everything
#	make <program_name> -- make a given program
EXECUTABLE = home.chk user.chk is_readable is_writable pass.chk \
	     addto clearfiles filewriters members tilde
C_SRC      = home.chk.c user.chk.c is_readable.c is_writable.c pass.c \
	     addto.c clearfiles.c filewriters.c members.c tilde.c
SHELL_PROGS= chk_strings root.chk dev.chk dir.chk cron.chk \
	     file.chk cops group.chk rc.chk passwd.chk \
	     suid.chk kuang init_kuang reconfig
SUPPORT    = dir.chklst file.chklst makefile stop.sample \
	     COPS.READ.1ST Beta.info SUID.README MANIFEST
DOCS	   = COPS.report.ms suid.man.ms kuang.man.ms
MAN        = cops.1 cron.1 dev.1 dir.1 file.1 group.1 passwd.1 \
	     is_able.1 home.1 user.1 pass.1
CFLAGS     = -O
ROFFLAGS   = -ms

#
# Where the programs are....
#
CHMOD=/bin/chmod
MKDIR=/bin/mkdir
CP=/bin/cp
CC=/bin/cc
NROFF=/usr/bin/nroff

# make all
all:	$(EXECUTABLE) $(DOCS) $(MAN)
	$(CHMOD) 700 $(SHELL_PROGS)

# make the programs
addto: src/addto.c
	$(CC) $(CFLAGS) -o addto src/addto.c

clearfiles: src/clearfiles.c
	$(CC) $(CFLAGS) -o clearfiles src/clearfiles.c

filewriters: src/filewriters.c
	$(CC) $(CFLAGS) -o filewriters src/filewriters.c

members: src/members.c
	$(CC) $(CFLAGS) -o members src/members.c

home.chk: src/home.chk.c
	$(CC) $(CFLAGS) -o home.chk src/home.chk.c

user.chk: src/user.chk.c
	$(CC) $(CFLAGS) -o user.chk src/user.chk.c

is_readable: src/is_readable.c
	$(CC) $(CFLAGS) -o is_readable src/is_readable.c

is_writable: src/is_writable.c
	$(CC) $(CFLAGS) -o is_writable src/is_writable.c

pass.chk: src/pass.c
	$(CC) $(CFLAGS) -o pass.chk src/pass.c

tilde: src/tilde.c
	$(CC) $(CFLAGS) -o tilde src/tilde.c

# 'roff out those docs
COPS.report.ms: docs/COPS.report
	$(NROFF) $(ROFFLAGS) docs/COPS.report > docs/COPS.report.ms

kuang.man.ms: docs/kuang.man
	$(NROFF) $(ROFFLAGS) docs/kuang.man > docs/kuang.man.ms

suid.man.ms: docs/suid.man
	$(NROFF) $(ROFFLAGS) docs/suid.man > docs/suid.man.ms

cops.1: docs/cops
	$(NROFF) -man docs/cops > docs/cops.1

cron.1: docs/cron
	$(NROFF) -man docs/cron > docs/cron.1

dev.1: docs/dev
	$(NROFF) -man docs/dev > docs/dev.1

dir.1: docs/dir
	$(NROFF) -man docs/dir > docs/dir.1

file.1: docs/file
	$(NROFF) -man docs/file > docs/file.1

group.1: docs/group
	$(NROFF) -man docs/group > docs/group.1

passwd.1: docs/passwd
	$(NROFF) -man docs/passwd > docs/passwd.1

pass.1: docs/pass
	$(NROFF) -man docs/pass > docs/pass.1

is_able.1: docs/is_able
	$(NROFF) -man docs/is_able > docs/is_able.1

home.1: docs/home
	$(NROFF) -man docs/home > docs/home.1

user.1: docs/user
	$(NROFF) -man docs/user > docs/user.1

# the end
SHAR_EOF
echo shar: extracting cops/pass.words '(3278 characters)'
cat << \SHAR_EOF > cops/pass.words
aaa
academia
aerobics
airplane
albany
albatross
albert
alex
alexander
algebra
aliases
alphabet
ama
amorphous
analog
anchor
andromache
animals
answer
anthropogenic
anvils
anything
aria
ariadne
arrow
arthur
athena
atmosphere
aztecs
azure
bacchus
bailey
banana
bananas
bandit
banks
barber
baritone
bass
bassoon
batman
beater
beauty
beethoven
beloved
benz
beowulf
berkeley
berliner
beryl
beverly
bicameral
bob
brenda
brian
bridget
broadway
bumbling
burgess
campanile
cantor
cardinal
carmen
carolina
caroline
cascades
castle
cat
cayuga
celtics
cerulean
change
charles
charming
charon
chester
cigar
classic
clusters
coffee
coke
collins
commrades
computer
condo
cookie
cooper
cornelius
couscous
creation
creosote
cretin
daemon
dancer
daniel
danny
dave
december
defoe
deluge
desperate
develop
dieter
digital
discovery
disney
dog
drought
duncan
eager
easier
edges
edinburgh
edwin
edwina
egghead
eiderdown
eileen
einstein
elephant
elizabeth
ellen
emerald
engine
engineer
enterprise
enzyme
ersatz
establish
estate
euclid
evelyn
extension
fairway
felicia
fender
fermat
fidelity
finite
fishers
flakes
float
flower
flowers
foolproof
football
foresight
format
forsythe
fourier
fred
friend
frighten
fun
fungible
gabriel
gardner
garfield
gauss
george
gertrude
ginger
glacier
gnu
golfer
gorgeous
gorges
gosling
gouge
graham
gryphon
guest
guitar
gumption
guntis
hacker
hamlet
handily
happening
harmony
harold
harvey
hebrides
heinlein
hello
help
herbert
hiawatha
hibernia
honey
horse
horus
hutchins
imbroglio
imperial
include
ingres
inna
innocuous
irishman
isis
japan
jessica
jester
jixian
johnny
joseph
joshua
judith
juggle
julia
kathleen
kermit
kernel
kirkland
knight
ladle
lambda
lamination
larkin
larry
lazarus
lebesgue
lee
leland
leroy
lewis
light
lisa
louis
lynne
macintosh
mack
maggot
magic
malcolm
mark
markus
marty
marvin
master
maurice
mellon
merlin
mets
michael
michelle
mike
minimum
minsky
moguls
moose
morley
mozart
nancy
napoleon
nepenthe
ness
network
newton
next
noxious
nutrition
nyquist
oceanography
ocelot
olivetti
olivia
oracle
orca
orwell
osiris
outlaw
oxford
pacific
painless
pakistan
pam
papers
password
patricia
penguin
peoria
percolate
persimmon
persona
pete
peter
philip
phoenix
pierre
pizza
plover
plymouth
polynomial
pondering
pork
poster
praise
precious
prelude
prince
princeton
protect
protozoa
pumpkin
puneet
puppet
rabbit
rachmaninoff
rainbow
raindrop
raleigh
random
rascal
really
rebecca
remote
rick
ripple
robotics
rochester
rolex
romano
ronald
rosebud
rosemary
roses
ruben
rules
ruth
sal
saxon
scamper
scheme
scott
scotty
secret
sensor
serenity
sharks
sharon
sheffield
sheldon
shiva
shivers
shuttle
signature
simon
simple
singer
single
smile
smiles
smooch
smother
snatch
snoopy
soap
socrates
sossina
sparrows
spit
spring
springer
squires
strangle
stratford
stuttgart
subway
success
summer
super
superstage
support
supported
surfer
suzanne
swearer
symmetry
tangerine
tape
target
tarragon
taylor
telephone
temptation
thailand
tiger
toggle
tomato
topography
tortoise
toyota
trails
trivial
trombone
tubas
tuttle
umesh
unhappy
unicorn
unknown
urchin
utility
vasant
vertigo
vicky
village
virginia
warren
water
weenie
whatnot
whiting
whitney
will
william
williamsburg
willie
winston
wisconsin
wizard
wombat
woodwind
wormwood
yacov
yang
yellowstone
yosemite
zap
zimmerman
SHAR_EOF
echo shar: extracting cops/passwd.chk '(5023 characters)'
cat << \SHAR_EOF > cops/passwd.chk
#!/bin/sh
#
#   passswd.chk
#
#  Check passsword file -- /etc/passswd -- for incorrect number of fields,
# duplicate uid's, non-alphanumeric uids, and non-numeric group id's.
#
# Awk part from _The AWK Programming Language_, page 78
#
#  Mechanism:  Passwd.check uses awk to ensure that each line of the file
# has 7 fields, as well as examining the file for any duplicate users
# by using "sort -u".  It also checks to make sure that the password
# field (the second one) is either a "*", meaning the group has no password,
# or a non-null field (which would mean that the account has a null
# password.)  It then checks to ensure that all uids are alphanumeric,
# and that all user id numbers are indeed numeric.  For yellow pages
# passwords, it does the same checking, but in order to get a listing of
# all members of the password file, it does a "ypcat passwd > ./$$" and
# uses that temporary file for a passfile.  It removes the tmp file after
# using it, of course.
#   The /etc/passwd file has a very specific format, making the task
# fairly simple.  Normally it has lines with 7 fields, each field
# separated by a colon (:).  The first field is the user id, the second
# field is the encrypted password (an asterix (*) means the group has no
# password, otherwise the first two characters are the salt), the third
# field is the user id number, the fourth field is the group id number,
# the fifth field is the GECOS field (basically holds miscellaneous
# information, varying from site to site), the sixth field is the home
# directory of the user, and lastly the seventh field is the login shell
# of the user.  No blank lines should be present.
#   If a line begins with a plus sign (+), it is a yellow pages entry.
# See passwd(5) for more information, if this applies to your site.
#
AWK=/bin/awk
TEST=/bin/test
ECHO=/bin/echo
SORT=/usr/bin/sort
UNIQ=/usr/bin/uniq
RM=/bin/rm
YPCAT=/usr/bin/ypcat

#   Used for Sun C2 security group file.  FALSE (default) will flag
# valid C2 passwd syntax as an error, TRUE attempts to validate it.
# Thanks to Pete Troxell for pointing this out.
C2=FALSE

#
# Important files:
etc_passwd=/etc/passwd
yp_passwd=./$$

yp=false

# Testing $etc_passwd for potential problems....
if $TEST -f $YPCAT
	then
if $TEST -s $YPCAT
	then
	$YPCAT passwd > $yp_passwd
	if $TEST $? -eq 0
		then
		yp=true
	fi
fi
fi

result=`$AWK -F: '{print $1}' $etc_passwd | $SORT |$UNIQ -d`
if $TEST "$result"
	then
	$ECHO "Warning!  Duplicate uid(s) found in $etc_passwd:"
	$ECHO $result
fi


#   First line is for a yellow pages entry in the password file.
# It really should check for correct yellow pages syntax....
$AWK 'BEGIN {FS = ":" } \
	{if (substr($1,1,1) != "+") { \
	if ($0 ~ /^[ 	]*$/) { printf("Warning!  Password file, line %d, is blank\n", NR) } else {
	if (NF != 7) {
		printf("Warning!  Password file, line %d, does not have 7 fields: \n\t%s\n", NR, $0) } \
	if ($1 !~ /[A-Za-z0-9]/) {
		printf("Warning!  Password file, line %d, nonalphanumeric login: \n\t%s\n", NR, $0) } \
	if ($2 == "") {
		printf("Warning!  Password file, line %d, no password: \n\t%s\n", NR, $0) } \
 	if ("'$C2'" == "TRUE" && $2 ~ /^##/ && "##"$1 != $2) {
		printf("Warning!  Password file, line %d, invalid password field for C2: \n\t%s\n", NR, $0) } \
	if ($3 !~ /[0-9]/) {
		printf("Warning!  Password file, line %d, nonnumeric user id: \n\t%s\n", NR, $0) } \
	if ($3 == "0" && $1 != "root") {
		printf("Warning!  Password file, line %d, user %s has uid = 0 and is not root\n\t%s\n", NR, $1, $0) } \
	if ($4 !~ /[0-9]/) {
		printf("Warning!  Password file, line %d, nonnumeric group id: \n\t%s\n", NR, $0) } \
	if ($6 !~ /^\//) {
		printf("Warning!  Password file, line %d, invalid login directory: \n\t%s\n", NR, $0) } \
	}}}' $etc_passwd

#
# Test yellow pages passwords as well
if $TEST "$yp" = "true"
	then
	yresult=`$AWK -F: '{print $1}' $yp_passwd | $SORT |$UNIQ -d`
	if $TEST "$yresult"
		then
		$ECHO "Warning!  Duplicate uid(s) found in yellow page passwords:"
		$ECHO $yresult
	fi

	$AWK 'BEGIN {FS = ":" } \
		{ if ($0 ~ /^[ 	]*$/) { printf("Warning!  YPassword file, line %d, is blank\n", NR) } else {
		if (NF != 7) {
			printf("Warning!  YPassword file, line %d, does not have 7 fields: \n\t%s\n", NR, $0) } \
		if ($1 !~ /[A-Za-z0-9]/) {
			printf("Warning!  YPassword file, line %d, nonalphanumeric login: \n\t%s\n", NR, $0) } \
		if ($2 == "") {
			printf("Warning!  YPassword file, line %d, no password: \n\t%s\n", NR, $0) } \
		if ($3 !~ /[0-9]/ && $3 != "-2") {
			printf("Warning!  YPassword file, line %d, nonnumeric user id: \n\t%s\n", NR, $0) } \
		if ($3 == "0" && $1 != "root") {
			printf("Warning!  YPassword file, line %d, user %s has uid = 0 and is not root\n\t%s\n", NR, $1, $0) } \
		if ($4 !~ /[0-9]/ && $4 != "-2") {
			printf("Warning!  YPassword file, line %d, nonnumeric group id: \n\t%s\n", NR, $0) } \
		if ($6 !~ /^\//) {
			printf("Warning!  YPassword file, line %d, invalid login directory: \n\t%s\n", NR, $0) } \
		}}' $yp_passwd
	
fi

$RM -f $yp_passwd

# end
SHAR_EOF
#	End of shell archive
exit 0

-- 
Please send comp.sources.unix-related mail to rsalz at uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.



More information about the Comp.sources.unix mailing list