httpd-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Randy Terbush <ra...@zyzzyva.com>
Subject Re: Authentication
Date Tue, 04 Jun 1996 22:48:55 GMT
I have the following sucgi.c working on a test port of my main
server (Solaris). Things seem to be working well. DOC_ROOT now
restricts execution to within this path. The CGI program passed
in cannot contain *any* '/' characters. This makes things secure
enough to put me at ease (but then, I'm easy).

Other checks include:

* file and directory owner must match
* file must not be writable by anyone but the owner
* directory cannot be writable by anyone but the owner
* requests for execution of CGI as root are denied
* request for execution of CGI as group 0 are denied

Todo:

* portability issue with setrlimit() call
* code up a linage test using kvm_getproc() for paranoid souls
* add the ability to set owner execution on per/directory basis
* ??



#define HTTPD_USER "www"
#define LOG_EXEC "/www/var/log/cgi.log"
#define DOC_ROOT "/disks/sd1/www/docs"

#include <sys/param.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdarg.h>
#include <strings.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <sys/stat.h>
#include <sys/resource.h>


static void
err_output (const char *fmt, va_list ap)
{
    time_t timevar;
    struct tm *lt;
    FILE *log;

    if ((log = fopen (LOG_EXEC, "a")) == NULL)
    {
	fprintf (stderr, "failed to open log file\n");
	perror ("fopen");
    }

    time (&timevar);
    lt = localtime (&timevar);
    
    fprintf (log, "[%.2d:%.2d:%.2d %.2d-%.2d-%.2d]: ", lt->tm_hour, lt->tm_min,
	     lt->tm_sec, lt->tm_mday, lt->tm_mon, lt->tm_year);
    
    vfprintf (log, fmt, ap);

    fflush (log);
    fclose (log);
    return;
}

void
log_err (const char *fmt, ...)
{
#ifdef LOG_EXEC
    va_list     ap;
    fprintf (stderr, "%d\n", __LINE__);

    va_start (ap, fmt);
    fprintf (stderr, "%d\n", __LINE__);
    err_output (fmt, ap);
    fprintf (stderr, "%d\n", __LINE__);
    va_end(ap);
#endif
    fprintf (stderr, "%d\n", __LINE__);
    return;
}

int
main(int argc, char *argv[], char **env)
{
    int doclen;
    uid_t uid, euid;
    char *server_uid;
    char *server_gid;
    char *prog;
    char *cmd;
    char *cwd;
    char *buf = NULL;
    struct passwd *pw;
    struct group *gr;
    struct stat dir_info;
    struct stat prg_info;
    struct rlimit limits;

    
    prog = argv[0];
    if (argc < 4)
    {
	log_err ("too few arguments\n");
	exit(101);
    }

    server_uid = argv[1];
    server_gid = argv[2];
    cmd = argv[3];

    getrlimit ( RLIMIT_NOFILE, &limits );
    if (limits.rlim_cur < limits.rlim_max)
    {
      limits.rlim_cur = 256;
      if (setrlimit (RLIMIT_NOFILE, &limits) < 0)
	log_err ("Cannot exceed hard limit for open files\n");
    }

    uid = getuid();
    if ((pw = getpwuid (uid)) == NULL)
    {
	log_err ("invalid uid: (%ld)\n", uid);
	exit (102);
    }
    
    if (strcmp (HTTPD_USER, pw->pw_name))
    {
        log_err ("user mismatch (%s)\n", pw->pw_name);
        exit(103);
    }
    
    if (strchr (cmd, '/') != (char) NULL )
    {
        log_err ("invalid command (%s)\n", cmd);
        exit(104);
    }

    cwd = getcwd (buf, MAXPATHLEN);
    log_err ("%s\n", cwd);

    doclen = strlen (DOC_ROOT);
    if (strncmp (cwd, DOC_ROOT, doclen))
    {
        log_err ("invalid command (%s)\n", cmd);
        exit(105);
    }

    if ((stat (cwd, &dir_info)) && !(S_ISDIR(dir_info.st_mode)))
    {
	log_err ("cannot stat directory: (%s)\n", cwd);
	exit (106);
    }
    
    if ((dir_info.st_mode & S_IWOTH) || (dir_info.st_mode & S_IWGRP))
    {
	log_err ("directory is writable by others: (%s)\n", cwd);
	exit (107);
    }

    if ((stat (cmd, &prg_info)) && !(S_ISLNK(prg_info.st_mode)))
    {
	log_err ("cannot stat program: (%s)\n", cmd);
	exit (106);
    }

    if ((prg_info.st_mode & S_IWOTH) || (prg_info.st_mode & S_IWGRP))
    {
	log_err ("file is writable by others: (%s/%s)\n", cwd, cmd);
	exit (108);
    }

    pw = getpwnam (server_uid);
    gr = getgrnam (server_gid);
    if (pw->pw_uid != dir_info.st_uid && gr->gr_gid != dir_info.st_gid)
    {
	log_err ("server uid/gid (%ld/%ld) and directory uid/gid (%ld/%ld) mismatch\n",
		 pw->pw_uid, gr->gr_gid, dir_info.st_uid, dir_info.st_gid);
	exit (109);
    }
    
    if (pw->pw_uid == 0)
    {
        log_err ("cannot run as uid 0 (%s)\n", cmd);
        exit(110);
    }
	
    if (gr->gr_gid == 0)
    {
        log_err ("cannot run as gid 0 (%s)\n", cmd);
        exit(112);
    }
	
    if ((setuid (pw->pw_uid)) != 0)
    {
        fprintf (stderr, "failed to setuid (%ld: %s)\n", pw->pw_uid, cmd);
        exit(111);
    }

    if ((setgid (gr->gr_gid)) != 0)
    {
        fprintf (stderr, "failed to setgid (%d: %s)\n", gr->gr_gid, cmd);
        exit(113);
    }

    execve (cmd, &argv[4], env);

    fprintf (stderr, "exec failed (%s)\n", cmd);
    exit(255);
}








Mime
View raw message