/************************************************************************/
/* This is the main file for lsat					*/
/* It contains the main program and also does two checks of its own.    */
/* It gives the user some usage info (perhaps) and will check the 	*/
/* kernel version and release level of software running on the box.	*/
/*									*/
/* Current release is 0.1.8						*/
/* Project started on 04/01/2002					*/
/* homepage: http://www.dimlight.org/~number9/lsat 			*/
/* changelog: http://www.dimlight.org/~number9/lsat/changelog.html  	*/
/*									*/
/* This software Licensed under the GNU General Public License		*/
/* see the bottom of this lsatmain.c file for details.			*/
/*									*/
/* This program is _supposed_ to be modular in nature. That is to say:  */
/* all security checks are sub-functions called from this main prog.    */
/* As of now (release 0.1.1) the subfunctions can be called with 2	*/
/* parameters, the kernel level, and the release level. However, 	*/
/* the checkinetd.c program returns an integer dictating if inetd.conf  */
/* exists, and if it does another function hosts.c will check the 	*/
/* /etc/hosts.allow|deny files for existance, etc.			*/
/*									*/
/* I should not get in the habit of doing the above as it destroys the  */
/* modularity (sp?) of the program.					*/
/*									*/
/************************************************************************/	

/****************/
/* Includes	*/
/****************/
#include <stdio.h>  /* fprintf,fgets,fputs,fopen,fclose,perror,sscanf,getchar */
#include <stdlib.h> /* the std stuff */
#include <string.h> /* strtok */
#include "lsatheader.h"

/********************************************************/
/* Usage function. Tell user command line options	*/
/********************************************************/
void usage()
{
	fprintf(stderr, "Usage is lsat [options]\n");
	fprintf(stderr, "Options:\n");
	fprintf(stderr, "		-v					verbose\n");
	fprintf(stderr, "		-o<filename>	Output file name\n");
	exit(8);
}

/***************************************************/
/* Check what version and kernel release we are at */
/* Changed: 4/4/2002:				   */
/* we will no longer cat /etc/redhat-release 	   */
/* in main... we will now just grab the 1st field  */
/* from the uname -a output so that it is easier   */
/* (later on too) to figure out what we should do. */
/* We shall still grab the "kernel/release" field  */
/* (field 3) to get the linux kernel or release    */
/* (e.g. Solaris version) that we are on. This (I  */
/* think) will give us greater flexibility in      */
/* porting to other *NIXs like Solaris or AIX.     */
/*						   */
/* Note: release will be field 1, kernel field 3   */
/***************************************************/
int versions(char release[], char kernel[], int verbose)
{
	/* some local vars... */
	FILE *infile;         /* file pointer to input file 	*/
	char *ptr;	      /* char pointer for strtok    	*/
	char line[100];       /* temp array for reading lines 	*/
	/* note make tempstring 100 also, avoid buffer overruns */
	char tempstring[100]; /* temp string for sscanf		*/
	int  linecount=0;     /* line counter			*/
	int  field=0;	      /* field counter for strtok	*/
	int  i=0;	      /* general counter variable	*/
	int  fileval=0;	      /* return value for mkstemp	*/

	/* First off, get the output of uname -a                        */
        /* this is silly, I should know how to do this more easily      */

	/* note we now use mkstemp for more security	*/
	fileval = mkstemp("/tmp/templist");
	if (fileval < -1)
	{
		printf("Unable to create tempfile!?\n");
		return(-1);
	}

	/* get the uname, redirect to tempfile	*/
        if ( (system("uname -a >> /tmp/templist")) < 0)
        {
                /* rhut-rho...something bad happened.                   */
                perror("Problem executing pwd...\n");
                return(0);
        }

	/* open input file for read... */
	infile = fopen("/tmp/templist", "r");
	if (infile == NULL)
	{
		/* rhut-rho */
		perror("Can't open the file we just made!?!\n");
		perror("Giving up, check perms or something...\n");
		return(-1);
	}

	/* get a line from the file... it should contain only 2	*/
	/* 1st line is uname -a, so the third field is the kernel version */
	while (fgets(line, sizeof(line), infile) != NULL)
	{
		/* (re)set field...	*/
		field=0;
		ptr = strtok(line, ", \"\n");
		if (ptr == NULL)
		{
			break;
		}
		/* not blank?? Do some stuff...			*/
		while (ptr != NULL)
		{
			/* if linecount == 0, get 1st field */
			if (sscanf(ptr, "%s", &tempstring) < 1)
			{
				perror("sscanf bit the dust");
				return(-1);
			}
			if ((field == 0) && (linecount == 0))
			{
				for (i = 0; i < 50; i++)
				{
					release[i]=tempstring[i];
				}
			}
			if ((field == 2) && (linecount == 0))
			{
				for (i = 0; i < 50; i++)
				{
					kernel[i]=tempstring[i];
				}
			}
			ptr = strtok(NULL, ", \"\n");
			if (ptr == NULL)
			{
				/* freak out */
				break;
			}
			/* increment counter for the field we are reading */
			field++;
		} /* end while(ptr != NULL) */
		/* increment the line counter */
		linecount++;
	} /* end while (fgets...) */

	if ( (system("rm -f /tmp/templist")) < 0)
        {
                /* rhut-rho...something bad happened.           */
                perror("Problem rm'ing tempfile...\n");
                return(0);
        }

	if (verbose == 1)
	{
		printf("System type is: %s\n", release);
		printf("Kernel/release level is: %s\n", kernel);
	}

	return(0);
}

/****************************************************************/
/* Main <smile> All sub-programs/functions should be called 	*/
/* from here. Note: they should also be listed in lsatheader.h  */
/****************************************************************/
int main(int argc, char *argv[])
{
	int retval=0;     /* a return value indicator		*/
	int noout=0;	  /* is there an output file?		*/
	int verbose=0;    /* for verbosity			*/
	int inetexists=0; /* if inetd.conf exists, this is 1 	*/
	int distribution=1; /* what distro are we on?		*/
	/* 1=redhat, 2=debian */
	/* default = redhat   */
	char release[50]; /* array for release level		*/
	char kernel[50];  /* what kernel user is running	*/
	char choice;	  /* if umask no workie as user y/n	*/
	char *out_file="lsat.out";  /* output filename var	*/
	/* note if no filename given, default = lsat.out	*/
	FILE *tempptr;    /* a temp file pointer		*/


	/* before we do ANYTHING, lets set our umask to */
	/* be 066 so only root could read the files we  */
	/* make during this audit...			*/
	/* note, we also need to keep the old umask and */
	/* restore it when we are done...		*/
	if ((system("umask 066")) < 0)
	{
		printf("Failed to set a good umask for file writes!\n");
		printf("This could be a possible security risk in\n");
		printf("itself, as others may read what we are doing!\n");
		printf("I am going to bail, unless you are sure you\n");
		printf("wish to continue.\n");
		printf("Continue? (y/n): ");
		choice = getchar();
		if ((choice == 'n') || (choice == 'N'))
		{
		 	printf("ok, bailing...\n");
			exit(0);
		}
	}

	/* process command line args. so far only two options. */
	while ((argc > 1) && (argv[1][0]=='-'))
	{
		/* argv[1][1] is 1st option char */
		switch (argv[1][1])
		{
			case 'v':
				verbose = 1;
				break;

			/* outfile 					*/
			/* -o<name> is format 	*/

			case 'o':
				out_file = &argv[1][2];
				break;

			default:
				printf("Bad option %s\n", argv[1]);
				usage();
		}
		++argv;
		--argc;
	}

	if (verbose == 1)
	{
		printf("Starting LSAT...\n");
	}

	/* read that in, check kernel version and "release"	*/
	if (verbose == 1)
	{
		printf("Getting system information...\n");
	}
	retval = versions(release, kernel, verbose);
	if (retval < 0)
	{
		/* something went wrong... 			*/
		perror("Problem in versions function.\n");
		return(0);
	}

	/* note about Mandrake: until I can find vast differences */
	/* between RedHat/Mandrake in terms of configs, we assume */
	/* that it is redhat and don't check /etc/mandrake_rel... */
	/* simple check to see if we are on RedHat or Debian */
	if ((system("cat /etc/redhat-release 1>/dev/null 2>/dev/null >/dev/null")) == 0)
	{
		/* we are on redhat */
		distribution = 1;
	}
	if ((system("cat /etc/debian_version 1>/dev/null 2>/dev/null >/dev/null")) == 0)
	{
		/* we are on debian */
		distribution = 2;
	}


	/* secondly, check to see if lsat.out exists.   */
	/* if it does, call it lsat.old and rm lsat.out	*/
	/* if it does not exist, create it w/mkstemp    */

	tempptr = fopen(out_file, "r");
	if (tempptr == NULL)
	{
		/* the outfile does not exist 		*/	
		noout = 1;
	}
	if (noout == 0)	
	{
		/* the outfile exists. lets not     */
		/* append to it, but back it up     */
		if (verbose == 1)
		{
			printf("Outfile exists...\n");
			printf("Making backup copy...\n");
		}
		if ((system("mv -f lsat.out lsat.old")) < 0)
		{
			/* rhut-rho... problems... */
			perror("Problem backing up lsat.out file.\n");
			perror("Check perms on the cwd please...\n");
			return(-1);
		}
	}
	
	/* make the output file with mkstemp */
        retval = mkstemp(out_file);
        if (retval < -1)
	{
        	/* can't create outfile?? */
               	perror("Can't create outfile\n");
                perror("Uh-oh. Perms problem?? Bailing...\n");
                exit(0);
        }


	/* run checkpkgs */
	retval = 0;
	retval = checkpkgs(release, kernel, distribution, out_file, verbose);
	if (retval < 0)
	{
		/* something went wrong...                      */
                perror("Problem in checkpkgs module.\n");
		perror("You should have seen errors...\n");
                return(0);
	}

	/* run checkinetd */
	/* if hosts file exists run checkhostsfiles */
	inetexists = checkinetd(release, kernel, out_file, verbose);
	/* if inetexists == 1, it does!				*/
	/* if inetexists == 0, it does not...			*/
	/* if inetexists == -1, we had some problems.		*/
	if (inetexists == 1)
	{
		checkhostsfiles(out_file, verbose);
	}
	if (inetexists == 0)
	{
		/* do nothing... */
		;
	}
	if (inetexists == -1)
	{
		/* there was a problem. User should have seen	*/
		/* messages from checkinetd program.		*/
		/* do nothing here and continue...		*/
		;
	}

	/* check for setuid/setgid files */
	retval = 0;
	retval = checkset(out_file, verbose);
	if (retval < 0)
        {
                /* something went wrong...                      */
                perror("Problem in checkset module.\n");
                perror("You should have seen errors...\n");
                return(0);
        }


	/* check for world writable files/directories */
	retval = 0;
	retval = checkwrite(out_file, verbose);
	if (retval < 0)
        {
                /* something went wrong...                      */
                perror("Problem in checkwrite module.\n");
                perror("You should have seen errors...\n");
                return(0);
        }

	/* check for .exrc and .forward files */
	retval = 0;
	retval = checkdotfiles(out_file, verbose);
	if (retval < 0)
	{
		/* something went wrong...                      */
                perror("Problem in checkdotfiles module.\n");
                perror("You should have seen errors...\n");
                return(0);
        }


	/* check /etc/passwd for uid 0 other than */
	/* root and users not needed on the sys.  */
	retval = 0;
	retval = checkpasswd(out_file, verbose);
	if (retval < 0)
        {
                /* something went wrong...                      */
                perror("Problem in checkpasswd module.\n");
                perror("You should have seen errors...\n");
                return(0);
        } 

	/* check to see if sticky bits are set */
	/* on /tmp & /var/tmp. I will add more */
	/* to this later on...			*/
	retval = 0;
	retval = checkfiles(out_file, verbose);
	if (retval < 0)
	{
		/* something went wrong...			*/
		perror("Problem in checkfiles module.\n");
		perror("You should have seen errors...\n");
		return(0);
	}

	retval = 0;
	retval = checkumask(out_file, verbose);
	if (retval < 0)
	{
		/* something went wrong...                      */
                perror("Problem in chumask module.\n");
                perror("You should have seen errors...\n");
                return(0);
        }

	retval = 0;
	retval = checkftpusers(out_file, verbose);
	if (retval < 0)
	{
		/* something went wrong...                      */
                perror("Problem in checkftpusers module.\n");
                perror("You should have seen errors...\n");
                return(0);
        }

	retval = 0;
	retval = checkrc(release, kernel, distribution, out_file, verbose);
	if (retval < 0)
	{
		/* something went wrong...			*/
		perror("Problem in checkrc module.\n");
		perror("You should have seen errors...\n");
		return(0);
	}

	/* by convention I will always have network */
	/* related checks at the bottom...	    */
	/* this will be moved to the TOP, as in 1st */
	/* when more network checks are in place.   */

	retval = 0;
	retval = checknet(out_file, verbose);
	if (retval < 0)
	{
		/* something went wrong...                      */
                perror("Problem in checknet module.\n");
                perror("You should have seen errors...\n");
		return(0);
        }


	/* restore users umask to sensible value */
	/* recall 022 is -rw-------		 */
	if ((system("umask 022")) < 0)
	{
		perror("Can't restore umask!!\n");
		perror("Log out/in or set manually...\n");
	}
	

	/* tell the user we are finished and where the output is */
	printf("Finished.\n");
	printf("Check %s for details.\n", out_file);
	return(0);
}


/**********************************************************************
Copyright (C) 2002 Robert Minvielle (number9)

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
**********************************************************************/
