/************************************************************************/
/* 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.4.3						*/
/* 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.    */
/*									*/
/************************************************************************/

/****************/
/* Includes	*/
/****************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h> 
#include <fcntl.h>
#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, "\t-v\t\tverbose\n");
    fprintf(stderr, "\t-o<filename>\t\tOutput 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 	*/

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

    if ((fileval = open("/tmp/templist.txt", O_RDWR | O_CREAT, 0600)) < 0)
    {
        perror("Could not make file w/perms 0600...\n");
        perror("Possible link attack while creating/opening file!\n");
        perror("Bailing in lsatmain...\n");
        exit(-1);
    }

    /* get the uname, redirect to tempfile	*/
    if ( (system("uname -a >> /tmp/templist")) < 0)
    {
        /* rhut-rho...something bad happened.                   */
        perror("Problem executing pwd...\n");
	perror("Error was in lsatmain.\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");
	perror("Error was in lsatmain.\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");
		perror("Error was in lsatmain.\n");
                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");
	perror("Error was in lsatmain.\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   */
    int i;		  /* gen purpose counter variable	*/
    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 line[128];	  /* temp line buffer			*/
    const char *out_file = "lsat.out";  /* output filename var	*/
    /* note if no filename given, default = lsat.out	*/
    FILE *tempptr;    /* a temp file pointer		*/
    FILE *tempptr2;   /* a temp file pointer		*/

    /* we used to set the umask of the user here... */
    /* note that does not work all fo the time.     */
    /* instead use open with options below. Also,   */
    /* warn user at bottom about setting umask to   */
    /* sensible value before modifying files.       */


    /* process command line args. so far only two options. */
    i = 0;
    while (i < argc)
    {
        if (argv[i][0] == '-')
        {
            switch (argv[i][1])
            {
            case 'v': verbose = 1; break;
            case 'o': out_file = argv[i+1]; i++; break;
            default: usage(); break;
            }
        }
        i++;
    }

    if (!out_file) /* if no filename after -o */
    {
        usage();
    }

    /* check if output exists, if it does, make a 	*/
    /* backup and rm the orig. named one.		*/
    tempptr = fopen(out_file, "r");
    if (tempptr != 0)
    {
        /* output file exists */
        /* make a backup copy */
        char newname[30];
        fclose(tempptr);
        sprintf(newname, "%s.old", out_file);

        /* gnu, but should be ok... */
        /* failure? eh, its ok...   */
        rename(out_file, newname);
    }
    
    /* now that we have a backup, remove the outfile */
    /* don't care about failure here...              */
    remove(out_file);

    /* noted that mkstemp, even with mode set, does  */
    /* not seem to work 100% of the time on my system*/
    /* for making files w/chmod 0600...              */
    /* reverting to open with options...             */
    retval = 0;
    if ((retval = open(out_file, O_RDWR | O_CREAT, 0600)) < 0) 
    {
	perror("Could not make file w/perms 0600...\n");
	perror("Possible link attack while creating/opening file!\n");
	perror("Bailing in lsatmain...\n");
        exit(-1);
    }


    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;
    }



    /* 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);
    }

    retval = 0;
    retval = checkkbd(release, kernel, out_file, verbose);
    if (retval < 0)
    {
        /* something went wrong...                      */
        perror("Problem in checkkbd 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);
    printf("Don't forget to check your umask or file perms\n");
    printf("when modifying files on the system.\n");
    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
**********************************************************************/
