/*
 * Copyright (c) 1996 Lou Langholtz and the University of Utah.
 * All rights reserved.
 *
 * Permission to use, copy, modify and distribute this software is hereby
 * granted provided that these copyright, permission, and disclaimer notices
 * are preserved.   THIS SOFTWARE IS PROVIDED WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

/*
 * mod_disallow_id.c
 * 
 * By Lou Langholtz <http://www.cs.utah.edu/~ldl/>
 * 
 * $Id: mod_disallow_id.c,v 1.4 1996/12/20 01:13:12 ldl Exp $
 *
 * Module to disallow serving of web pages based on the user or group ID
 * owning the web page. This is useful for sites where any following of
 * symlinks must be allowed, so as to prevent files like /etc/passwd from
 * being served accidentally or otherwise through some misguided symlink.
 *
 * For instance, to prevent any super-user owned files from being served,
 * add the following line to your server resource configuration file:
 *
 * DisallowUid 0
 *
 * Group 0 is ussually privaledged enough to warrant not serving any files
 * owned by this group ID either. To stop these files from being served:
 *
 * DisallowGid 0
 */

#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_log.h"

/*
 * need to keep set of id's per (virtual) server
 */

typedef struct disallow_id_config_struct {
    array_header *disallow_uid;
    array_header *disallow_gid;
} disallow_id_config_rec;

static void *create_disallow_id_config(pool *p, server_rec *s)
{
    disallow_id_config_rec *vid =
    (disallow_id_config_rec *) ap_palloc(p, sizeof(disallow_id_config_rec));

    vid->disallow_uid = ap_make_array(p, 5, sizeof(int));
    vid->disallow_gid = ap_make_array(p, 5, sizeof(int));

    return (void *) vid;
}

module disallow_id_module;

static const char *add_disallow_uid(cmd_parms *cmd, void *dummy, char *arg)
{
    disallow_id_config_rec *vid = ap_get_module_config(
                           cmd->server->module_config, &disallow_id_module);

    *((int *) ap_push_array(vid->disallow_uid)) = atoi(arg);
    return NULL;                /* non-null return val means error */
}

static const char *add_disallow_gid(cmd_parms *cmd, void *dummy, char *arg)
{
    disallow_id_config_rec *vid = ap_get_module_config(
                           cmd->server->module_config, &disallow_id_module);

    *((int *) ap_push_array(vid->disallow_gid)) = atoi(arg);
    return NULL;                /* non-null return val means error */
}

static command_rec disallow_id_cmds[] =
{
    {
        "DisallowUid", add_disallow_uid, NULL,
        RSRC_CONF, ITERATE,
        "one or more UIDs to disallow"
    },
    {
        "DisallowGid", add_disallow_gid, NULL,
        RSRC_CONF, ITERATE,
        "one or more GIDs to disallow"
    },
    {
        NULL
    }
};

static int ap_check_access(request_rec *orig)
{
    request_rec *r;
    int i, id, *elts, ret = OK;
    disallow_id_config_rec *vid;
    static char reason_fmt[] = "file owned by disallowed %s %d";
    char buf[80];

    /*
     * Get actual file, if locally redirected.
     * Is this really what we need to do here?
     */
    for (r = orig; r->next; r = r->next)
        continue;

    if (r->finfo.st_mode == 0)
        return ret;

    vid = ap_get_module_config(orig->server->module_config, &disallow_id_module);

    if (ret == OK) {
        elts = (int *) vid->disallow_gid->elts;
        id = r->finfo.st_gid;
        for (i = 0; i < vid->disallow_gid->nelts; i++)
            if (elts[i] == id) {
                ret = FORBIDDEN;
                sprintf(buf, reason_fmt, "gid", id);
                ap_log_reason(buf, r->filename, r);
                break;
            }
    }
    if (ret == OK) {
        elts = (int *) vid->disallow_uid->elts;
        id = r->finfo.st_uid;
        for (i = 0; i < vid->disallow_uid->nelts; i++)
            if (elts[i] == id) {
                ret = FORBIDDEN;
                sprintf(buf, reason_fmt, "uid", id);
                ap_log_reason(buf, r->filename, r);
                break;
            }
    }
    return ret;
}

module disallow_id_module =
{
    STANDARD_MODULE_STUFF,
    NULL,                       /* initializer (none needed for us) */
    NULL,                       /* dir config creater */
    NULL,                       /* dir merger --- default is to override */
    create_disallow_id_config,  /* server config */
    NULL,                       /* merge server config */
    disallow_id_cmds,           /* command table */
    NULL,                       /* handlers */
    NULL,                       /* filename translation */
    NULL,                       /* check_user_id */
    NULL,                       /* check auth */
    ap_check_access,               /* check access */
    NULL,                       /* type_checker */
    NULL,                       /* fixups */
    NULL,                       /* logger */
    NULL,                       /* header parser */
    NULL,                       /* child_init */
    NULL,                       /* child_exit */
    NULL                        /* post read-request */
};
