/*
 * mod_owner
 *
 * Apache 2 module to restrict access to objects based on ownership.
 *
 * My first ever Apache 2 module! Its anticipated use is in a shared
 * hosting environment where users have access to PHP, CGI, etc. which run
 * as Apache; this module can then be used to prevent execution of
 * malicious scripts that have appeared in an insecure upload area (as
 * such scripts will be owned by the UID of the webserver rather than the
 * user who runs that particular site).
 *
 * Whilst in theory PHP programmers would check what gets put in an upload
 * area and protect the contents from execution using .htaccess or by
 * locating the area outside of the document root, on a shared environment
 * with hundreds of users it's rather hard to ensure that these simple
 * precautions have been taken by everybody...
 *
 * To compile and install: (as root; substitute apxs2 for apxs on Debian)
 *
 * $ apxs -c -i mod_owner.c
 *
 * To activate in the Apache 2 configuration:
 *
 * LoadModule owner_module /path/to/apache2/modules/mod_owner.so
 *
 * <Directory /some/directory/>
 *     RequireOwner 1000
 * </Directory>
 *
 * The above example will result in a 500 "Forbidden" error if a client
 * requests an object in /some/directory/ that isn't owned by UID 1000.
 * More fine grained control can be obtained by using <Files> directives
 * with regular expressions such as \.(php|cgi|pl)$)
 *
 * Please let me know if you have any comments or criticisms (or even just
 * if you tried the module!).
 *
 * v0.01 - Initial release
 *         by simon@grimsworld.org.uk on 08/06/2005
 *
 * This code may be used under the BSD license, see:
 *   http://www.grimsworld.org.uk/licenses/bsd.txt
 *
 */

#include <stdlib.h>
#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "http_log.h"
#include "http_request.h"
#include "http_core.h"
#include "ap_config.h"
#include "apr_strings.h"

module AP_MODULE_DECLARE_DATA owner_module;

typedef struct owner_config_struct {
    int require_owner; /* -1 when no ownership requirement */
} owner_config_rec;

static void *create_owner_config(apr_pool_t *p, char *dummy) {
    owner_config_rec *new = apr_pcalloc(p, sizeof(owner_config_rec));

    new->require_owner = -1;
    return (void *)new;
}

static const char *set_require_owner(cmd_parms *parms, void *mconfig,
    const char *arg) {

    owner_config_rec *cfg = (owner_config_rec *)mconfig;
    cfg->require_owner = atoi(arg);

    return NULL;
}

static const command_rec owner_cmds[] = {
    AP_INIT_TAKE1("RequireOwner", set_require_owner, NULL, ACCESS_CONF,
        "require_owner (string) username objects must be owned by"),
    {NULL}
};

static int owner_access_checker(request_rec *r) {
    owner_config_rec *cfg = ap_get_module_config(r->per_dir_config,
        &owner_module);

    apr_status_t status = 0;
    apr_finfo_t finfo;

    char *reason = NULL;

    if(cfg->require_owner<0) {
        return DECLINED;
    }

    if(!r->filename) {
        reason = "no filename available";
        goto exit;
    }
    
    status = apr_stat(&finfo, r->filename, APR_FINFO_USER, r->pool);

    if(status != APR_SUCCESS) {
        /*reason = apr_pstrcat(r->pool, "could not stat file ",
            r->filename, NULL);
        goto exit;*/

        return DECLINED;
    }

    if(!(finfo.valid & APR_FINFO_USER)) {
        reason = "no file owner information available";
        goto exit;
    }

    if(cfg->require_owner == finfo.user) {
        return OK;
    }

    ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r,
        "Owner of %s [%d] != RequireOwner [%d]", r->filename, finfo.user,
        cfg->require_owner);
    
    return HTTP_FORBIDDEN;
    
exit:
   
    ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r,
        "Owner check for %s failed, reason: %s", r->filename, reason);
    
    return HTTP_INTERNAL_SERVER_ERROR;
}

static void owner_register_hooks(apr_pool_t *p) {
    ap_hook_access_checker(owner_access_checker, NULL, NULL, APR_HOOK_MIDDLE);
}

/* Dispatch list for API hooks */
module AP_MODULE_DECLARE_DATA owner_module = {
    STANDARD20_MODULE_STUFF, 
    create_owner_config,   /* create per-dir    config structures */
    NULL,                  /* merge  per-dir    config structures */
    NULL,                  /* create per-server config structures */
    NULL,                  /* merge  per-server config structures */
    owner_cmds,            /* table of config file commands       */
    owner_register_hooks   /* register hooks                      */
};

