/* ** mod_auth_useragent.c -- Simple security by User-Agent */ #include "httpd.h" #include "http_config.h" #include "http_protocol.h" #include "ap_config.h" #include "fnmatch.h" #define AuthUserAgentAllow "Allow a User-Agent that was disallowed" #define AuthUserAgentDeny "Deny a connection by User-Agent." /* Copied this out of mod_layout to reuse some code... I am lazy */ #define unless(a) if(!a) #define WATCHPOINT printf("WATCHPOINT %s %d\n", __FILE__, __LINE__); module MODULE_VAR_EXPORT auth_useragent_module; typedef struct { array_header *allow; array_header *deny; } auth_useragent_conf; int useragent_match(const char *s1, const char *s2) { if(!s1) return 1; if(!s2) return 1; return ap_fnmatch(s1, s2, FNM_CASE_BLIND); } static void *create_dir_mconfig(pool *p, char *dir) { auth_useragent_conf *cfg; cfg = ap_pcalloc(p, sizeof(auth_useragent_conf)); cfg->allow = NULL; cfg->deny = NULL; return (void *) cfg; } static void *merge_dir_mconfig(pool *p, void *origin, void *new) { auth_useragent_conf *cfg; auth_useragent_conf *cfg_new = (auth_useragent_conf *)new; auth_useragent_conf *cfg_origin = (auth_useragent_conf *)origin; cfg = ap_pcalloc(p, sizeof(auth_useragent_conf)); cfg->allow = NULL; cfg->deny = NULL; if (cfg_new->deny && cfg_origin->deny) { cfg->deny = ap_append_arrays(p, cfg_origin->deny, cfg_new->deny); } else if (cfg_new->deny) { cfg->deny = cfg_new->deny; } else { cfg->deny = cfg_origin->deny; } if (cfg_new->allow && cfg_origin->allow) { cfg->allow = ap_append_arrays(p, cfg_origin->allow, cfg_new->allow); } else if (cfg_new->allow) { cfg->allow = cfg_new->allow; } else { cfg->allow = cfg_origin->allow; } return cfg; } static int auth_useragent(request_rec *r) { int true = 0; int x = 0; char **items = NULL; const char *agent = ap_table_get(r->headers_in, "User-Agent"); auth_useragent_conf *cfg = ap_get_module_config (r->per_dir_config, &auth_useragent_module); /* Yes, you read this right, if there is no agent we say ok This is not ever ment to be a perfect/reliable form of security. */ #ifdef DEBUG printf("AUTHUSER: %s\n", agent); #endif unless(agent) return OK; if (cfg->deny) { items = (char **) cfg->deny->elts; for(x = 0; x < cfg->deny->nelts; x++) { #ifdef DEBUG printf("AUTHUSER-Deny: %s\n", items[x]); #endif if(!useragent_match(items[x], agent)) { true = 1; } } } if (cfg->allow) { items = (char **) cfg->allow->elts; for(x = 0; x < cfg->allow->nelts; x++) { #ifdef DEBUG printf("AUTHUSER-Allow: %s\n", items[x]); #endif if(!useragent_match(items[x], agent)) { true = 0; } } } return (true ? FORBIDDEN : OK); } static const char *user_agent_add(cmd_parms * cmd, void *mconfig, char *param) { auth_useragent_conf *cfg = (auth_useragent_conf *) mconfig; struct stat sbuf; const char *temp; unless(strcasecmp(cmd->cmd->name, "AuthUserAgentAllow")) { unless(cfg->allow) { cfg->allow = ap_make_array (cmd->pool, 1, sizeof (char *)); } *(char **) ap_push_array (cfg->allow) = (char *) param; } else unless(strcasecmp(cmd->cmd->name, "AuthUserAgentDeny")) { unless(cfg->deny) { cfg->deny = ap_make_array (cmd->pool, 1, sizeof (char *)); } *(char **) ap_push_array (cfg->deny) = (char *) param; } return NULL; } static const command_rec auth_useragent_cmds[] = { {"AuthUserAgentDeny", user_agent_add, NULL, OR_ALL, TAKE1, AuthUserAgentDeny}, {"AuthUserAgentAllow", user_agent_add, NULL, OR_ALL, TAKE1, AuthUserAgentAllow}, {NULL}, }; /* Dispatch list for API hooks */ module MODULE_VAR_EXPORT auth_useragent_module = { STANDARD_MODULE_STUFF, NULL, /* module initializer */ create_dir_mconfig, /* create per-dir config structures */ merge_dir_mconfig, /* merge per-dir config structures */ NULL, /* create per-server config structures */ NULL, /* merge per-server config structures */ auth_useragent_cmds, /* table of config file commands */ NULL, /* [#8] MIME-typed-dispatched handlers */ NULL, /* [#1] URI to filename translation */ NULL, /* [#4] validate user id from request */ NULL, /* [#5] check if the user is ok _here_ */ auth_useragent, /* [#3] check access by host address */ NULL, /* [#6] determine MIME type */ NULL, /* [#7] pre-run fixups */ NULL, /* [#9] log a transaction */ NULL, /* [#2] header parser */ NULL, /* child_init */ NULL, /* child_exit */ NULL /* [#0] post read-request */ #ifdef EAPI ,NULL, /* EAPI: add_module */ NULL, /* EAPI: remove_module */ NULL, /* EAPI: rewrite_command */ NULL /* EAPI: new_connection */ #endif };