
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by Lance R. Bailey <zaphod@ctrg> on Tue Nov  2 11:09:16 1993
#
# This archive contains:
#	ProgMailer.c	ProgMailer.h	Makefile	ProgMailer.1M	
#	README		version.h	
#
# Error checking via wc(1) will be performed.

LANG=""; export LANG
PATH=/bin:/usr/bin:$PATH; export PATH

echo x - ProgMailer.c
cat >ProgMailer.c <<'@EOF'
/*
 * ProgMailer.c
 * run a program from under mail, but ONLY what we allow
 */

#include <stdio.h>
#include <syslog.h>
#include <string.h>

#ifndef rindex
#define rindex strrchr
#endif

#ifndef index
#define index strchr
#endif

#include "ProgMailer.h"
#include "version.h"

/*
 * in your sendmail.cf, you will typically find a mailer definition such as:
 *
 * Mprog,		P=/bin/sh,   F=lsDFM,    A=sh -c $u
 *
 * meaning that the program /bin/sh is called with Arguments "sh" "-c" and
 * "foobar" where "foobar" is the command to be executed, including arguments.
 * 
 * what we are doing is replacing the program /bin/sh, which will execute
 * anything you specify, with a piece of code with executes ONLY what you
 * specify.
 * 
 * the preliminary arguments "sh" and "-c" are skipped before the 
 * authorization check is done.  We also skip a preliminary "/bin/sh" or
 * a "ProgMailer" argument.
 * 
 * after the preliminaries are skipped, we check the command requested against
 * a list of legal operations. this list is compiled into the code. a file 
 * lookup is crackable
 * 
 * Logging is done via logger.
 *
 * failed attempts are logged to mail.warn
 * failed execs are logged to mail.alert
 *
 * if compiled with  -DPARANOID then we log start up, what is attempted and
 * what is executed to mail.info
 */


main(argc,argv)
    int argc; char *argv[];
{   int i;
    char syscmd[512];

    openlog("ProgMailer",LOG_NDELAY|LOG_NOWAIT,LOG_MAIL);
#ifdef PARANOID
    syslog(LOG_INFO,"startup");
#endif

/* check the reqest
 */
    if ( validate(argv)  <0)
    {   strcpy(syscmd,"illegal access to"); 
        for (i=0;i<argc;i++) {strcat(syscmd," ");strcat(syscmd,argv[i]);}
        syslog(LOG_WARNING,syscmd);
        closelog();
        exit(0);
    }


/* at this point we have accepted the program to run. we execute the program
 * under the defined SHELL with the arguments  passed to this program.
 */
#ifdef PARANOID
    strcpy(syscmd,"executing \"");
    for (i=0;i<argc;i++) {
        strcat(syscmd," ");
        strcat(syscmd,argv[i]);
    }
    strcat(syscmd,"\"");
    syslog(LOG_INFO,syscmd);
#endif

    execv(SHELL,argv); /* NOT execvp */
    perror("execv");
    openlog("ProgMailer",LOG_NOWAIT,LOG_MAIL);
    syslog(LOG_ALERT,"could not exec %s\n",SHELL);
    closelog();
    exit(0);
}


validate(args)
    char *args[];
{   int i=0;
    char *ptr1,*ptr2;
    char *currententry;

#ifdef USECHECKSUMS
    char *checksum, iline[100];
    int valid;
    FILE *pstream;
#endif

#ifdef ALLOWEDFILES
    char AllowedFile[100];
    FILE *flist;
#endif


    ptr1=args[0]; while (*ptr1 == ' ') ptr1++;
    ptr2=index(ptr1,' '); if (ptr2) *ptr2=0;

    while (SKIPS[i])
    {   if (!strcmp(ptr1,SKIPS[i])) {
            if (ptr2) *ptr2=' ';
            return (1+validate(args+1));
        }
        else i++;
    }

#ifdef PARANOID
    syslog(LOG_INFO,"checking program \"%s\"",ptr1);
    if (*ptr1 != '/')
        {syslog(LOG_WARNING,"no leading '/' on %s",ptr1); return -255;}
#endif

    i=0;

#ifdef ALLOWEDFILES
    flist = fopen(ALLOWEDFILES,"r");
    if (!flist) {
        syslog(LOG_WARNING,"could not open %s",ALLOWEDFILES);
        return -255;
    }

    currententry=AllowedFile;
    fgets(AllowedFile,100,flist);
    while (!feof(flist)) {
        checksum=index(AllowedFile,'\n');
        if (checksum)
            *checksum = 0;
#else
    while (LEGALS[i]) {
        currententry=LEGALS[i];
#endif


#ifdef USECHECKSUMS
        checksum=rindex(currententry,' ');
        if (checksum)
            *(checksum++) = 0;

        if (!strcmp(ptr1,currententry)) {

            sprintf(iline,"%s %s",CHKSUM,currententry);

            pstream= popen(iline,"r");
            if (pstream) {
                fgets(iline,100,pstream);
                pclose(pstream);
                valid = !strncmp(checksum,iline,strlen(checksum));
            }
            else {
                syslog(LOG_ALERT,"could not execute %s",iline);
                valid =0;
            }

            if (!valid) {
                syslog(LOG_ALERT,"checksum error on %s",currententry);
                return -255;
            }
#else
        if (!strcmp(ptr1,currententry)) {
#endif
            if (ptr2)
                   *ptr2=' ';

#ifdef ALLOWEDFILES
            fclose(flist);
#endif

            return 1;
        }
#ifdef ALLOWEDFILES
        fgets(AllowedFile,100,flist);
#else
        i++;
#endif
    }

#ifdef ALLOWEDFILES
    fclose(flist);
#endif


    syslog(LOG_WARNING,"could not find authorization for %s",ptr1);
    return -255;
}

/* 
 * EOF
 */
@EOF
set `wc -lwc <ProgMailer.c`
if test $1$2$3 != 2045014576
then
	echo ERROR: wc results of ProgMailer.c are $* should be 204 501 4576
fi

chmod 600 ProgMailer.c

echo x - ProgMailer.h
cat >ProgMailer.h <<'@EOF'
/*
 * ProgMailer.h
 * run a program from under mail, but ONLY what we allow
 */

/* if you want to log all invokations of ProgMail, and what it executes,
 * define PARAMOID
 */
#define PARANOID

/* if you want to validate the programs with a system checksum routine, then
 * define CHECKSUMS
 */
#define USECHECKSUMS

/* if you want to use a configuration file containing the names of 
 * files you allow to run, then define ALLOWEDFILES as the name of that file
 */
#define ALLOWEDFILES "/usr/local/etc/files"

/*
 * the checksum program to use, cksum is better than sum
 */
#define CHKSUM "/bin/cksum"


/*
 * the shell used by sendmail in the sendmail.cf line for the progmailer
 */
#define SHELL "/bin/sh"


/* preliminary arguments to ignore (ie, we validate the program AFTER these
 * things.
 *
 * add favourite shells on your system
 * add the arguments in the progmail command line from the sendmail.cf
 */

char *SKIPS[] = {"ProgMailer",                    /* my name */
                 "/bin/sh","/bin/ksh","/bin/csh", /* shells on the system */
                 "sh","-c",                       /* args from sendmail.cf */
                 0}; 



/* acceptable programs 
 * DO NOT PUT a legal shell (such as /bin/sh) IN THIS LIST
 * 
 * if using checksums then the format of the entries is 
 *          program<space>checksum
 *
 * the checksum to be used is the FIRST number returned by the checksum
 * program (cksum and sum both return a checksum and a second less
 * meaningful number
 * do not use the checksums provided here, recalculate your own on
 * your own system
 *
 * if not using checksums then the format of the entries is 
 *          program
 */

#ifndef ALLOWEDFILES
#ifdef USECHECKSUMS

char *LEGALS[] = {"/usr/local/bin/mail-list 876286326",
                  "/usr/bin/vacation 896695172",
                  "/usr/local/bin/psprinter 1017544131",
                  "/usr/local/lib/logmessage 2721188103",
                  0}; 

#else

char *LEGALS[] = {"/usr/local/bin/mail-list",
                  "/usr/bin/vacation",
                  "/usr/local/bin/psprinter",
                  "/usr/local/lib/logmessage",
                  0}; 
#endif

#endif


/*
 * EOF
 */
@EOF
set `wc -lwc <ProgMailer.h`
if test $1$2$3 != 863032203
then
	echo ERROR: wc results of ProgMailer.h are $* should be 86 303 2203
fi

chmod 600 ProgMailer.h

echo x - Makefile
cat >Makefile <<'@EOF'
ProgMailer:	ProgMailer.c	ProgMailer.h
	cc -o ProgMailer ProgMailer.c
	rm -f /usr/local/bin/ProgMailer
	mv ProgMailer /usr/local/bin/ProgMailer
	chmod 555  /usr/local/bin/ProgMailer

ProgMailer.shar:	ProgMailer.c ProgMailer.h  Makefile ProgMailer.1M README version.h
	shar -c ProgMailer.c ProgMailer.h  Makefile ProgMailer.1M README > ProgMailer.shar version.h
@EOF
set `wc -lwc <Makefile`
if test $1$2$3 != 833360
then
	echo ERROR: wc results of Makefile are $* should be 8 33 360
fi

chmod 600 Makefile

echo x - ProgMailer.1M
sed 's/^@//' >ProgMailer.1M <<'@EOF'
@.nh
@.TH PROGMAILER 1M
@.SH NAME
ProgMailer \-  Mailer Mprog for sendmail with authorization
@.SH SYNOPSIS
@.B ProgMailer
program arguments
@.SH DESCRIPTION
@.B ProgMailer
is an attempt to tighten up calls to programs via sendmail. Typically all
that needs to be run is vacation and, well... that's about it. 
@.B ProgMailer
is defined for the P value of the Mprog line in the sendmail configuration file.

In your sendmail.cf, you will find a mailer definition such as:

@.nf
Mprog,	 P=/bin/sh,   F=lsDFM,    A=sh -c $u
@.fi

Meaning that the program /bin/sh is called with Arguments "sh" "-c" and
"foobar" where "foobar" is the command to be executed, including arguments.
@.sp 1
What we are doing is replacing the program /bin/sh, which will execute
anything you specify, with a piece of code with executes ONLY what the
system administrator allows.

@.nf
Mprog,   P=/usr/local/bin/ProgMailer, F=lsDFM,    A=sh -c $u
@.fi

The preliminary arguments "sh" and "-c" are skipped before the 
authorization check is done.  We also skip any preliminary arguments of /bin/sh or
@.BR ProgMailer .
@.sp 1
After the preliminary arguments are skipped, we check the command requested against
a list of legal executables. this list is compiled into the code. A file 
could be altered and is thus not supported. To add a new legal program 
you must edit the code and recompile.
@.SH DIAGNOSTICS
Logging is done via
@.IR syslogd (1M)
with failed authorizations logged to MAIL.WARN. Accepted programs are
executed via
@.IR execv (2)
and NOT 
@.IR execvp (2)
failed execs are logged to MAIL.ALERT.
@.PP
If compiled with  PARANOID defined in the ProgMailer.h file then 
@.B ProgMailer
logs when it starts up, what is attempted and
what is executed to MAIL.INFO. Additionally, PARANOID ensures that
the executed program is fully qualified (begins with a '/').
@.PP
@.B ProgMailer
keeps track of checksums on the legal programs, and if a program
has a new checksum, a message to mail.alert is logged. If you compile
with FORCE_CHKSUM defined in the ProgMailer.h file, then a
failed checksum also stops execution.
@.SH RETURN VALUE
-1 on error, 0 otherwise.
@.SH FILES
@.B ProgMailer
uses no files for configuration. All acceptable programs are compiled right
into the code.
@.SH "SEE ALSO"
syslog(3),sendmail(1M)
@.SH BUGS
Since the acceptable programs are strings in the code, 
it is possible to replace these with other strings.
@.SH AUTHOR
Lance R. Bailey, <zaphod@rri.uwo.ca>
@.\" index  "sendmail" "Mprog" "Mailer" "with authorization" PROGMAILER(1M)
@.\" index  "" "sendmail Mprog" "Mailer" "with authorization" PROGMAILER(1M)
@.\" index  "" "" "sendmail Mprog Mailer" "with authorization" PROGMAILER(1M)
@.\" index  "" "" "" "sendmail Mprog Mailer with authorization" PROGMAILER(1M)
@.\" toc PROGMAILER(1M)   sendmail Mprog Mailer with authorization
@.sp 0
@EOF
set `wc -lwc <ProgMailer.1M`
if test $1$2$3 != 794422812
then
	echo ERROR: wc results of ProgMailer.1M are $* should be 79 442 2812
fi

chmod 600 ProgMailer.1M

echo x - README
cat >README <<'@EOF'
Preamble
--------
One of the *ahem* features of sendmail is that John Bull can use it
to mail into a program and create nasty security holes in your system. 

this is bad.

ProgMailer is designed to validate programs before they are
executed and only run those that the system administrator has approved.


Installation
------------
Progmail is designed as a drop in substitue for the shell used by
sendmail to run programs. Typically you will find a mailer in your
sendmail.cf file which looks like this:

Mprog,		P=/bin/sh, F=lsDFM, A=sh -c $u

you will change it to read as follows:


Mprog,		P=/usr/local/bin/ProgMailer, F=lsDFM, A=sh -c $u
                  ^^^^^^^^^^^^^^^^^^^^^^^^

which means "when i have a program foo to run, send it to ProgMailer with
the arguments "sh" "-c" "foo"

ProgMailer will run, skip past the "sh" and "-c" arguments and then validate
"foo" and run it only if the system administrator has permitted it.

Remember to freeze the config file if you have a sendmail.fc file ALREADY
in existance (some vendors core dump on frozen config files, so only 
freeze if you froze before) 

Kill the sendmail daemon and restart.


Compiler Directives
-------------------

PARANOID
    if you want to log all invokations of ProgMail, and what it executes,
    define PARAMOID. This directive also forces the programs run to
    be fully qualified (start with a "/")

CHECKSUMS
    if you want to validate the programs with a system checksum routine,
    then define CHECKSUMS. This is recommended.

ALLOWEDFILES
    if you want to use a configuration file containing the names of 
    files you allow to run, then define ALLOWEDFILES as the name of
    that file

CHKSUM
    The checksum program to use, cksum is better than sum

SHELL
    The shell to use to execute approved programs. use the same one as
    you replaced in the sendmail.cf file. (In the example that would
    be "/bin/sh")


Strings To Define
-----------------

SKIPS

You must define a set of skip strings and the set of allowed programs.
The skip strings are preliminary arguments to ignore (ie, we validate
the program AFTER these things.)  Remember how we saw that Progmailer 
would be run with arguments "sh" "-c" "foo" ? Well we don't want to
pass every invocation because of a match on "sh", nor do we want to fail
every invocation because "sh" is not listed in the acceptable programs.

Thus we define a list of programs we "skip over" before we run.

You should also add favourite shells on your system lest someone tries to
mail to "/bin/sh -c ReallyBadStuff"


LEGALS

The set of legal programs are used to define what is a permitted recipient
of mail (program-wise). This could include the vacation program, the inews
program, or whatever.

DO NOT PUT a legal shell (such as /bin/sh) IN THIS LIST. This would allow 
someone the same hose as if ProgMailer was not being used.


Progmail supports a simple checksumming routine.  This means that after
a program is found to be approved, then we ensure that it has not been
changed.  I make a call to the checksum program define in CHKSUM, and
compare the results with the stored data.  I only use the FIRST number
returned by a program such as sum or cksum, so only put that number in
the defintions.  Do not use the checksums provided in the ProgMailer.h
file, recalculate your own on your own system


If using checksums then the format of the entries is 
         program<space>checksum

if not using checksums then the format of the entries is 
         program


NOTE
You can define the legal programs right in the code (in ProgMailer.h), 
which is the recommended way... or you can  define a file in ALLLOWEDFILES
which contains the entries for you. The format of the file is the same
as the LEGALS string.




Attributes
----------
magi suggested the idea of checksumming
reggers wanted a config file for allowable programs
my father showed me how to be mis-trusting...
--
Lance R. Bailey  System/Network Manager       Robarts Research Institute
     NeXT/email: zaphod@ctrg.rri.uwo.ca  box: Clinical Trials Resources Group
            fax: +1 519 663 3789              P.O. Box 5015, 100 Perth Dr.
            vox: +1 519 663 3787 x4108        London, Canada N6A 5K8


@EOF
set `wc -lwc <README`
if test $1$2$3 != 1296854229
then
	echo ERROR: wc results of README are $* should be 129 685 4229
fi

chmod 600 README

echo x - version.h
cat >version.h <<'@EOF'
char *version = "\r\nProgMailer 2.0 copyright (c) zaphod, Nov. 1993\r\n";
@EOF
set `wc -lwc <version.h`
if test $1$2$3 != 11074
then
	echo ERROR: wc results of version.h are $* should be 1 10 74
fi

chmod 600 version.h

exit 0
