[net.sources] integrity -- program to check file permissions against a package

smk@linus.UUCP (Steven M. Kramer) (06/24/83)

Here is man page and source:
------------------------
integrity.8
---------------------
.TH INTEGRITY 8 LinusIII
.SH NAME
integrity \- check files against specification
.SH SYNOPSIS
.B integrity
specifications ...
.SH DESCRIPTION
.I Integrity
checks files against a specification file.
One or more specifications are placed on the command line.
These are files in a certain format discussed below.
Briefly, each line of the specification is a description
about a certain file.
The existence, owner, group, type, and mode of the file are
checked.
Whenever a match does not occur (or the file mode is `greater' than
the one in the specification), an error is printed.
.PP
Users may use
.i integrity
to check their file protection modes.
Additionally, for system files,
.I integrity
should be run
from the initialization
process through the file
/etc/rc
(see
.IR init (8)
and
.IR rc (8)),
or periodically by
.IR cron (8).
.PP
Each specification file
consists of lines of four fields each.
The fields are separated by spaces or tabs.
The first is the (relative or full) pathname of the file
to be checked.
The second field is a string
that is the login name of the owner of the file.
The third field is a string
that is the name of the group of the file.
.PP
The last field contains both the file type and the file mode.
The format for this is as in
.IR ls (1).
The first character specifies the file type.
Directories, block special, character special, multiplexed,
and regular files are denoted by
.BR d ,
.BR b ,
.BR c ,
.BR m ,
and
.BR - ,
respectively.
The remaining nine characters specify the mode.
The read
.RB ( r ),
write
.RB ( w ),
or execute
.RB ( x )
permissions are in the order for owner, group, and others.
Instead of
.B x
for the owner (or group),
.B s
designates a setuid (setgid) program.
Similarly,
.B t
instead of
.B x
in the other mode designates a program with the
.IR sticky (8)
bit on.
.SH "SEE ALSO"
ls(1), stat(2), cron(8), rc(8), sticky(8)
.SH AUTHOR
Steven M. Kramer
----------------------
integrity.c
--------------------
/*
			L I N U S   I I I
			e n o n e
			a t t i c
			d o i x u
			i   c   r
			n   e   i
			g   a   t
			    b	y
			    l
			    e

	Copyright	October, 1982		Steven M. Kramer
						..!linus!smk

		This program is part of the LINUS III Security package
	and is not to be reproduced, copied, or redistributed without
	the expressed consent of the author.  This program may not be
	redistributed for any commercial venture.  The author does not
	guarantee or promise in any way that the software works as
	advertised.  There may be outstanding problems with the software
	and the author is not responsible for any security, functional,
	or performance problems arising from this software.
*/


/*	Integrity Checking Program

	Should be run periodically, either by cron(8) or by rc(8).

	integrity takes integrity specifications as command line
	arguments.  Each line of the specification is in the format:

		<filename> <owner> <group> <mode>

	where mode is in the style presented by ls(1).  Any inconsistencies
	found are printed.
*/



#include "linus.h"



main (argc, argv)
int     argc;
char   *argv[];
{
    register    boolean found_a_problem;
    register int    scan;

    if (argc == 1)
    {
	fprintf (stderr, "Usage: %s files ...\n", argv[0]);
	exit (1);
    }

    fprintf (stdout, "\n\n\t\tFILE SYSTEM INTEGRITY CHECK:\n\n");
    found_a_problem = FALSE;

 /* Perform the checking for each command line specification.	 */
    for (scan = 1; scan < argc; scan++)
    {
	fprintf (stdout, "%s:\n", argv[scan]);
	found_a_problem |= integrity (argv[0], argv[scan]);
	fprintf (stdout, "\n");
    }

 /* 	Provide final status of the check.	 */
    if (found_a_problem)
	fprintf (stdout, "\t\tINTEGRITY CHECK FAILED\n");
    else
	fprintf (stdout, "\t\tINTEGRITY CHECK SUCCESSFUL\n");
}



/*	This is the workhorse routine.  It goes through each
	line of a specification and reports any errors.  Each
	file (one per line) is checked and errors are reported.
*/
boolean integrity (cmdname, specification)
char   *cmdname;
char   *specification;
{
    register int    count;
    register int    line;
    register    FILE * files;
    char    file_buffer[BUFSIZ];
    char    integrity_item[BUFSIZ];
    char    important_file[BUFSIZ];
    char    owner[BUFSIZ];
    char    mode[BUFSIZ];
    char    group[BUFSIZ];
    struct stat buffer;
    struct passwd  *true_owner;
    struct group   *true_group;
    boolean found_a_problem;

    files = fopen (specification, "r");
    if (files == NULL)
    {
	fprintf (stderr, "%s: cannot find %s\n",
		cmdname, specification);
	found_a_problem = TRUE;
    }
    else
    {
	setbuf (files, file_buffer);

	line = 0;

    /* 	This loop is executed once per line of specification.	 */
	while (fgets (integrity_item, sizeof (integrity_item),
		    files) != NULL)
	{
	    line++;

	/* 	Pick off the data from each line.	 */
	    count = sscanf (integrity_item, "%s %s %s %s\n",
		    important_file, owner, group, mode);

	    if (count == 4)
	    /* 	The line format was OK.	 */
		if (stat (important_file, &buffer) == 0)
		{
		/* 	The file was found.  Use the stat(2) information
		   to determine is the file characteristics meet the
		   specification.	 */

		/* 	Check owner of file against specification.	 
		*/
		    true_owner = getpwuid (buffer.st_uid);
		    if (true_owner == NULL)
		    {
			found_a_problem = TRUE;
			fprintf (stdout,
				"\t%s: owner %d (should be %s)\n",
				important_file,
				buffer.st_uid,
				owner);
		    }
		    else
			if (strcmp (true_owner -> pw_name,
				    owner))
			{
			    found_a_problem = TRUE;
			    fprintf (stdout,
				    "\t%s: owner %s (should be %s)\n",
				    important_file,
				    true_owner -> pw_name,
				    owner);
			}

		/* 	Check group of file against specification.	 
		*/
		    true_group = getgrgid (buffer.st_gid);
		    if (true_group == NULL)
		    {
			found_a_problem = TRUE;
			fprintf (stdout,
				"\t%s: group %d (should be %s)\n",
				important_file,
				buffer.st_gid,
				group);
		    }
		    else
			if (strcmp (true_group -> gr_name,
				    group))
			{
			    found_a_problem = TRUE;
			    fprintf (stdout,
				    "\t%s: group %s (should be %s)\n",
				    important_file,
				    true_group -> gr_name,
				    group);
			}

		/* 	Check mode of file against specification.	 
		*/
		    if (compare_mode (important_file, mode,
				buffer.st_mode))
			found_a_problem = TRUE;
		}
		else
		{
		/* 	Couldn't get info for file.	 */
		    found_a_problem = TRUE;
		    fprintf (stderr,
			    "\tcannot access %s\n",
			    important_file);
		}
	    else
	    {
	    /* 	OH! NO!  The user didn't have the correct format in the
	       specification.	 */
		fprintf (stderr,
			"\tbad line format in %s at line %d --\n\t\t%s",
			specification, line, integrity_item);
		found_a_problem = TRUE;
	    }
	}
	(void) fclose (files);
    }
    return (found_a_problem);
}



/*	This routine checks the ls(1) format mode against
	that produced by stat(2), and reports any errors
	in the ls(1) format.  Errors occur when any part
	(file type, owner/group/other mode) do not match.
	Actually, the modes are alright if they are at least
	as strict as the specification.	*/
boolean compare_mode (file, human_desired_mode, file_mode)
char   *file;
char   *human_desired_mode;
unsigned short int  file_mode;
{
    register char  *desired_mode;
    register unsigned short int decoded_mode;
    register int    permission;
    char    human_file_mode[BUFSIZ];
    int     action;

    desired_mode = human_desired_mode;
    decoded_mode = 0;

 /* decode file type in desired_mode */
    switch (*desired_mode)
    {
	case 'd': 
	    decoded_mode |= S_IFDIR;
	    break;
	case 'c': 
	    decoded_mode |= S_IFCHR;
	    break;
	case 'b': 
	    decoded_mode |= S_IFBLK;
	    break;
	case 'm': 
	    decoded_mode |= S_IFMPC;
	    break;
	default: 
	    decoded_mode |= S_IFREG;
	    break;
    }
    while (strlen (desired_mode) > 9)
	desired_mode++;

 /* 	decode file modes	 */
    for (permission = 2; permission >= 0; permission--)
	for (action = 2; action >= 0; action--)
	{
	    switch (*desired_mode)
	    {
		case '-': 
		    break;
		case 's': 
		    if (permission == 2)
			decoded_mode |= S_ISUID |
			    (01 << (permission * 3 +
				    action));
		    else
			decoded_mode |= S_ISGID |
			    (01 << (permission * 3 +
				    action));
		    break;
		case 't': 
		    decoded_mode |= S_ISVTX |
			(01 << (permission * 3 +
				action));
		    break;
		default: 
		    decoded_mode |= (01 << (permission * 3 +
				action));
		    break;
	    }
	    desired_mode++;
	}

 /* 	If there is a discrepency, output in ls(1) format.	 */
    if (file_mode & ~decoded_mode)
    {
	(void) sprintf (human_file_mode, "----------");
	switch (file_mode & S_IFMT)
	{
	    case S_IFDIR: 
		human_file_mode[0] = 'd';
		break;
	    case S_IFCHR: 
		human_file_mode[0] = 'c';
		break;
	    case S_IFBLK: 
		human_file_mode[0] = 'b';
		break;
	    case S_IFMPC: 
	    case S_IFMPB: 
		human_file_mode[0] = 'm';
		break;
	    case S_IFREG: 
		break;
	    default: 
		break;
	}
	for (permission = 0; permission < 3; permission++)
	    for (action = 0; action < 3; action++)
		if (file_mode &
			(01 << (8 - 3 * permission - action)))
		    switch (action)
		    {
			case 0: 
			    human_file_mode[1 + 3 * permission] =
				'r';
			    break;
			case 1: 
			    human_file_mode[2 + 3 * permission] =
				'w';
			    break;
			case 2: 
			    human_file_mode[3 + 3 * permission] =
				'x';
			    break;
			default: 
			    break;
		    }

    /* 	Fill in special mode settings.	 */
	if (file_mode & S_ISUID)
	    human_file_mode[3] = 's';
	if (file_mode & S_ISGID)
	    human_file_mode[6] = 's';
	if (file_mode & S_ISVTX)
	    human_file_mode[9] = 't';
	fprintf (stdout,
		"\t%s: mode %s (should not be more than %s)\n",
		file, human_file_mode, human_desired_mode);
    }
}
-- 
--steve kramer
	{allegra,genrad,ihnp4,utzoo,philabs,uw-beaver}!linus!smk	(UUCP)
	linus!smk@mitre-bedford						(ARPA)