diff --git a/mod_tequila.c b/mod_tequila.c index fde964f..6356abb 100644 --- a/mod_tequila.c +++ b/mod_tequila.c @@ -1,1946 +1,1960 @@ // // // Apache module for Tequila authentication tool. // Version 2.0.7 // // // -// 2.0.5 -> 2.0.6 : fix crash in parseFilter(). -// : Different cosmetic things. -// 2.0.6 -> 2.0.7 : Add support for : identities, confirmuser and customarg, tequilaserverurl. -// 2.0.7 -> 2.0.8 : Fix redirect status from .HTTP_MOVED_PERMANENTLY to HTTP_MOVED_TEMPORARILY. -// Add call to SSL_library_init (); -// 2.0.8 -> 2.0.9 : Add env var HTTP_TEQUILA_SESSION_COOKIE_NAME. Applications protected -// by the module will be able to logout properly by destroying this cookie. +// 2.0.5 -> 2.0.6 : fix crash in parseFilter(). +// : Different cosmetic things. +// 2.0.6 -> 2.0.7 : Add support for : identities, confirmuser and customarg, tequilaserverurl. +// 2.0.7 -> 2.0.8 : Fix redirect status from .HTTP_MOVED_PERMANENTLY to HTTP_MOVED_TEMPORARILY. +// Add call to SSL_library_init (); +// 2.0.8 -> 2.0.9 : Add env var HTTP_TEQUILA_SESSION_COOKIE_NAME. Applications protected +// by the module will be able to logout properly by destroying this cookie. +// 2.0.9 -> 2.0.10 : Fix getkey to accept only 'key=smth' and not 'smthkey=smth'. // // #include #include #include #include #include #include #ifdef APACHE2 # include # include # include # include # include #endif #ifdef USESSL # include # include "openssl/bio.h" # include "openssl/ssl.h" # include "openssl/err.h" # include "openssl/x509v3.h" #endif #include #if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE) #include "unixd.h" #define MOD_REWRITE_SET_MUTEX_PERMS /* XXX Apache should define something */ #endif #include "mod_tequila.h" static int createserverrequest (request_rec *r); static Session *fetchattributes (request_rec *r, const char *requestkey); static void substsepar (char *string, char oldsepar, char newsepar); static int setenvvars (request_rec *r); static void error (request_rec *r, char *msg); static void writeline (AP_FILE_T fp, char *string); #ifdef DEBUG static char *escape_url (AP_POOL *p, const char *url); static char *unescape_url (AP_POOL *p, const char *url); static void printfilter (request_rec *r, Filter *filter); #endif /* DEBUG */ #ifdef APACHE2 #include static apr_global_mutex_t *tequila_log_lock = NULL; static const command_rec command_table [] = { AP_INIT_TAKE1( "TequilaRewrite", cmd_rewrite, NULL, OR_FILEINFO, "Rewrite"), AP_INIT_RAW_ARGS( "TequilaAllowIf", cmd_allowif, NULL, OR_AUTHCFG, "Access condition"), AP_INIT_RAW_ARGS( "TequilaRequest", cmd_request, NULL, OR_AUTHCFG, "Request"), AP_INIT_RAW_ARGS( "TequilaAllowNet", cmd_allownet, NULL, OR_AUTHCFG, "Allowed Net"), AP_INIT_RAW_ARGS( "TequilaAllows", cmd_allows, NULL, OR_AUTHCFG, "Allows attribute"), AP_INIT_TAKE1( "TequilaService", cmd_service , NULL, OR_AUTHCFG, "Service name"), AP_INIT_TAKE1( "TequilaResource", cmd_resource, NULL, OR_AUTHCFG, "Tequila resource"), AP_INIT_TAKE1( "TequilaKeyFile", cmd_sslkeyfile, NULL, OR_AUTHCFG, "SSL Key file"), AP_INIT_TAKE1( "TequilaCertFile", cmd_sslcertfile, NULL, OR_AUTHCFG, "SSL Cert file"), AP_INIT_TAKE1( "TequilaIdentities", cmd_identities, NULL, OR_AUTHCFG, "Identities"), AP_INIT_TAKE1( "TequilaConfirmUser", cmd_confirmuser, NULL, OR_AUTHCFG, "Force login"), AP_INIT_TAKE1( "TequilaCustomArg", cmd_customarg, NULL, OR_AUTHCFG, "Custom argument"), AP_INIT_TAKE1( "TequilaServer", cmd_server, NULL, RSRC_CONF, "Tequila server"), AP_INIT_TAKE1( "TequilaServerURL", cmd_serverurl, NULL, RSRC_CONF, "Tequila server URL"), AP_INIT_TAKE1( "TequilaSessionDir", cmd_sessdir, NULL, RSRC_CONF, "Session directory"), AP_INIT_TAKE1( "TequilaSessionMax", cmd_sessmax, NULL, RSRC_CONF, "Session duration"), AP_INIT_TAKE1( "TequilaLog", cmd_log, NULL, RSRC_CONF, "Logfile"), AP_INIT_TAKE1( "TequilaLogLevel", cmd_loglevel, NULL, RSRC_CONF, "Log level"), AP_INIT_NO_ARGS( "TequilaNoSSL", cmd_nossl, NULL, RSRC_CONF, "No SSL support"), AP_INIT_NO_ARGS( "TequilaHasSSL", cmd_usessl, NULL, RSRC_CONF, "SSL support"), AP_INIT_NO_ARGS( "TequilaUseSSL", cmd_usessl, NULL, RSRC_CONF, "SSL support"), AP_INIT_TAKE1( "TequilaCAFile", cmd_cafile, NULL, RSRC_CONF, "CA file"), AP_INIT_NO_ARGS( "TequilaCheckServerName", cmd_checkservername, NULL, RSRC_CONF, "Check Server Name"), { NULL } }; module AP_MODULE_DECLARE_DATA tequila_module = { STANDARD20_MODULE_STUFF, create_perdir_config, /* create per-dir config structures */ merge_perdir_config, /* merge per-dir config structures */ create_global_config, /* create per-server config structures */ merge_global_config, /* merge per-server config structures */ command_table, /* table of config file commands */ register_hooks /* register hooks */ }; static void register_hooks (AP_POOL *p) { ap_hook_post_config (post_config, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_header_parser (check_access, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_translate_name (translate, NULL, NULL, APR_HOOK_FIRST); ap_hook_fixups (setenvvars, NULL, NULL, APR_HOOK_MIDDLE); } APR_DECLARE_OPTIONAL_FN(int, ssl_is_https, (conn_rec *)); static APR_OPTIONAL_FN_TYPE(ssl_is_https) *url_is_https = NULL; #else /* APACHE2 */ static const command_rec tequila_cmds[] = { {"TequilaRewrite", cmd_rewrite, NULL, OR_FILEINFO, TAKE1, "Rewrite" }, {"TequilaAllowIf", cmd_allowif, NULL, OR_AUTHCFG, RAW_ARGS, "Access condition" }, {"TequilaRequest", cmd_request, NULL, OR_AUTHCFG, RAW_ARGS, "Request" }, {"TequilaAllowNet", cmd_allownet, NULL, OR_AUTHCFG, RAW_ARGS, "Allowed Net." }, {"TequilaAllows", cmd_allows, NULL, OR_AUTHCFG, RAW_ARGS, "Allows attribute" }, {"TequilaService", cmd_service, NULL, OR_AUTHCFG, TAKE1, "Service name" }, {"TequilaResource", cmd_resource, NULL, OR_AUTHCFG, TAKE1, "Tequila resource" }, {"TequilaKeyFile", cmd_sslkeyfile, NULL, OR_AUTHCFG, TAKE1, "SSL Key file" }, {"TequilaCertFile", cmd_sslcertfile, NULL, OR_AUTHCFG, TAKE1, "SSL Cert file" }, {"TequilaIdentities", cmd_identities, NULL, OR_AUTHCFG, TAKE1, "Identities" }, {"TequilaConfirmUser", cmd_confirmuser, NULL, OR_AUTHCFG, TAKE1, "Force login" }, {"TequilaCustomArg", cmd_customarg, NULL, OR_AUTHCFG, TAKE1, "Custom argument" }, {"TequilaServer", cmd_server, NULL, RSRC_CONF, TAKE1, "Local Tequila server" }, {"TequilaServerURL", cmd_serverurl, NULL, RSRC_CONF, TAKE1, "Tequila server URL" }, {"TequilaSessionDir", cmd_sessdir, NULL, RSRC_CONF, TAKE1, "Session directory." }, {"TequilaSessionMax", cmd_sessmax, NULL, RSRC_CONF, TAKE1, "Session duration." }, {"TequilaLog", cmd_log, NULL, RSRC_CONF, TAKE1, "Filename of the logfile" }, {"TequilaLogLevel", cmd_loglevel, NULL, RSRC_CONF, TAKE1, "Log level" }, {"TequilaNoSSL", cmd_nossl, NULL, RSRC_CONF, NO_ARGS, "No SSL support" }, {"TequilaHasSSL", cmd_usessl, NULL, RSRC_CONF, NO_ARGS, "SSL support" }, {"TequilaUseSSL", cmd_usessl, NULL, RSRC_CONF, NO_ARGS, "SSL support" }, {"TequilaCAFile", cmd_cafile, NULL, RSRC_CONF, TAKE1, "CA file" }, {"TequilaCheckServerName", cmd_checkservername, NULL, RSRC_CONF, NO_ARGS, "Check Server Name" }, {NULL} }; module MODULE_VAR_EXPORT tequila_module = { STANDARD_MODULE_STUFF, init_module, /* initializer */ create_perdir_config, /* create per-directory config structure */ merge_perdir_config, /* merge per-directory config structures */ create_global_config, /* create per-server config structure */ merge_global_config, /* server config merger */ tequila_cmds, /* table of config file commands */ NULL, /* [#8] MIME-typed-dispatched handlers */ translate, /* [#1] URI to filename translation */ NULL, /* [#4] validate user id from request */ NULL, /* [#5] check if the user is ok _here_ */ NULL, /* [#3] check access by host address */ NULL, /* [#6] determine MIME type */ setenvvars, /* [#7] pre-run fixups */ NULL, /* [#9] logger */ check_access, /* [#2] header parser */ NULL, /* child_init */ NULL, /* child_exit */ NULL /* [#0] post read-request */ }; #endif /* APACHE2 */ static int keyfromcookie = 0; static char *defaultsessdir = "/var/www/Tequila/Sessions"; static char *defaultteqserver = "tequila"; static int defaultsessmax = 12 * 3600; #ifdef USESSL typedef struct { SSL_CTX *ctx; BIO *bio; } SSLenv; static SSLenv *opensslsocket (request_rec *r, char *host, int port); static void closesslsocket (SSLenv *sslenv); static int checkcertificate (request_rec *r, X509 *cert, char *host); #endif static int translate (request_rec *r) { tequila_perdir_conf *dconf; char *left; teqlog (r, 99, "translate: uri = %s, filename = %s", r->uri, r->filename); dconf = (tequila_perdir_conf*) ap_get_module_config (r->per_dir_config, &tequila_module); if (!dconf || !dconf->path || !dconf->rewrite) return DECLINED; left = skip_match (r->uri, dconf->path); if (!strncmp (dconf->rewrite, "http:", 5) || !strncmp (dconf->rewrite, "https:", 6)) { r->proxyreq = PROXYREQ; r->handler = "proxy-server"; r->filename = AP_STRCAT (r->pool, "proxy:", dconf->rewrite, left, NULL); if (r->path_info != NULL && r->uri == r->unparsed_uri) { r->filename = AP_STRCAT (r->pool, r->filename, r->path_info, NULL); } if (r->args != NULL) { r->filename = AP_STRCAT (r->pool, r->filename, "?", r->args, NULL); } } else { r->filename = AP_STRCAT (r->pool, dconf->rewrite, left, NULL); } /* if (r->path_info != NULL && r->uri == r->unparsed_uri) { r->filename = AP_STRCAT (r->pool, r->filename, r->path_info, NULL); } if (r->args != NULL) { r->filename = AP_STRCAT (r->pool, r->filename, "?", r->args, NULL); } */ if (r->main && r->main->args) { r->args = AP_STRDUP (r->pool, r->main->args); } teqlog (r, 2, "translate: url %s rewritten to %s", r->uri, r->filename); return OK; } static int check_access (request_rec *r) { tequila_perdir_conf *dconf; char *key; int status; teqlog (r, 99, "check_access: uri = %s, filename = %s", r->uri, r->filename); dconf = (tequila_perdir_conf*) ap_get_module_config (r->per_dir_config, &tequila_module); if (dconf == NULL || (dconf->allowif == NULL && dconf->allownet == NULL)) return DECLINED; if (dconf->allownet && check_net (r)) return OK; key = getkey (r); if (key) { Session *session; teqlog (r, 5, "check_access: key = %s", key); session = readsession (r, key); if (session) { dconf->session = session; if (!r->main && !keyfromcookie) set_cookie (r, key); teqlog (r, 2, "check_access: session OK"); return OK; } teqlog (r, 3, "check_access: readsession failed"); session = fetchattributes (r, key); if (session) { dconf->session = session; if (!r->main && !keyfromcookie) set_cookie (r, key); teqlog (r, 2, "check_access: fetchattributes OK"); return OK; } teqlog (r, 3, "check_access: fetchattributes failed"); // return HTTP_NOT_MODIFIED; } status = createserverrequest (r); teqlog (r, 2, "check_access: redirect to %s\n", r->filename); return status; } static int check_net (request_rec *r) { conn_rec *connection; tequila_perdir_conf *dconf = (tequila_perdir_conf*) ap_get_module_config (r->per_dir_config, &tequila_module); if (!dconf || !dconf->allownet || !dconf->allownet->nelts) return 0; connection = r->connection; if (connection && connection->remote_ip) { int i; char *ip = connection->remote_ip; char **netp = (char**) dconf->allownet->elts; for (i = 0; i < dconf->allownet->nelts; i++) { char *net = netp [i]; teqlog (r, 4, "check_net: comparing %s to %s", ip, net); if (strstr (ip, net) == ip) { teqlog (r, 4, "check_net: %s matches %s, access allowed.\n", ip, net); teqlog (r, 2, "check_net: Acces authorised for ip address %s (%s)", ip, net); return 1; } teqlog (r, 2, "check_net: %s doesn't match %s", ip, net); } } return 0; } static char *getRequest (request_rec *r) { int i; char **reqp, *request; tequila_perdir_conf *dconf = (tequila_perdir_conf*) ap_get_module_config (r->per_dir_config, &tequila_module); if (!dconf || !dconf->request || !dconf->request->nelts) return NULL; reqp = (char**) dconf->request->elts; request = AP_STRDUP (r->pool, reqp [0]); for (i = 1; i < dconf->request->nelts; i++) { request = AP_STRCAT (r->pool, request, "+", reqp [i], NULL); } teqlog (r, 1, "getRequest: request = %s", request); return request; } static char *getFilter (request_rec *r) { tequila_perdir_conf *dconf; int i; char *strfilt = NULL; Filter *filter; dconf = (tequila_perdir_conf*) ap_get_module_config (r->per_dir_config, &tequila_module); if (dconf == NULL || dconf->allowif == NULL) return NULL; filter = (Filter*) dconf->allowif->elts; for (i = 0; i < dconf->allowif->nelts; i++) { int first; Filter *filt = (Filter*) (filter + i); if (strfilt != NULL) strfilt = AP_STRCAT (r->pool, strfilt, "|", NULL); first = 1; while (filt) { if (!first) strfilt = AP_STRCAT (r->pool, strfilt, "&", NULL); else first = 0; if (strfilt != NULL) { if (filt->val) { strfilt = AP_STRCAT (r->pool, strfilt, filt->nam, "=", filt->val, NULL); } else { strfilt = AP_STRCAT (r->pool, strfilt, filt->nam, NULL); } } else { if (filt->val) { strfilt = AP_STRCAT (r->pool, filt->nam, "=", filt->val, NULL); } else { strfilt = AP_STRCAT (r->pool, filt->nam, NULL); } } filt = filt->next; } } return strfilt; } static char *getAllows (request_rec *r) { tequila_perdir_conf *dconf; int i; char *strallows = NULL; Filter *allows; dconf = (tequila_perdir_conf*) ap_get_module_config (r->per_dir_config, &tequila_module); if (dconf == NULL || dconf->allows == NULL) return NULL; allows = (Filter*) dconf->allows->elts; for (i = 0; i < dconf->allows->nelts; i++) { int first; Filter *allo = (Filter*) (allows + i); if (strallows != NULL) strallows = AP_STRCAT (r->pool, strallows, "|", NULL); first = 1; while (allo) { if (!first) strallows = AP_STRCAT (r->pool, strallows, "&", NULL); else first = 0; if (strallows != NULL) { if (allo->val) { strallows = AP_STRCAT (r->pool, strallows, allo->nam, "=", allo->val, NULL); } else { strallows = AP_STRCAT (r->pool, strallows, allo->nam, NULL); } } else { if (allo->val) { strallows = AP_STRCAT (r->pool, allo->nam, "=", allo->val, NULL); } else { strallows = AP_STRCAT (r->pool, allo->nam, NULL); } } allo = allo->next; } } return strallows; } static Session *readsession (request_rec *r, char *key) { tequila_global_conf *gconf; tequila_perdir_conf *dconf; void *sconf = r->server->module_config; AP_PERM_T flags = (AP_READ); AP_MODE_T mode = (AP_UREAD); AP_FILE_T fp; AP_STATUS rc; AP_SIZE_T nbytes; struct stat buf; time_t now, mtime; char buffer [1024]; char *sessionstring = NULL, *s; char *requiredfilter; char *sessdir; time_t sessmax; char *fname; Session *session; teqlog (r, 3, "readsession: key = %s", key); gconf = (tequila_global_conf*) ap_get_module_config (sconf, &tequila_module); sessdir = gconf->sessdir; if (sessdir == NULL) sessdir = defaultsessdir; sessmax = gconf->sessmax; if (sessmax == 0) sessmax = defaultsessmax; fname = AP_STRCAT (r->pool, sessdir, "/", key, NULL); dconf = (tequila_perdir_conf*) ap_get_module_config (r->per_dir_config, &tequila_module); if ((dconf == NULL) || (dconf->allowif == NULL)) { teqlog (r, 2, "readsession: internal error."); return NULL; } if (stat (fname, &buf) == -1) { /* does not exist */ teqlog (r, 2, "readsession: could not open session file %s (errno = %d)", fname, errno); return NULL; } now = time (NULL); mtime = buf.st_mtime; if (now > mtime + sessmax) { unlink (fname); teqlog (r, 2, "readsession: session %s expired.", key); return NULL; } if (utime (fname, NULL) == -1) { teqlog (r, 2, "readsession: cannot reset usage time on session %s.", key); return NULL; } sessionstring = ""; #ifdef APACHE2 if ((rc = AP_FILEOPEN (&fp, fname, flags, mode, r->pool)) != AP_SUCCESS) { teqlog (r, 2, "readsession: cannot open session file : %s", fname); return NULL; } nbytes = 1023; while (AP_FILEREAD (fp, buffer, &nbytes) == AP_SUCCESS) { buffer [nbytes] = '\0'; sessionstring = AP_STRCAT (r->pool, sessionstring, buffer, NULL); nbytes = 1023; } AP_FILECLOSE (fp); #else if ((fp = AP_FILEOPEN (r->pool, fname, flags, mode)) < 0) { teqlog (r, 2, "readsession: cannot open session file : %s", fname); return NULL; } while (nbytes = AP_FILEREAD (fp, buffer, 1023)) { if (nbytes < 0) break; buffer [nbytes] = '\0'; sessionstring = AP_STRCAT (r->pool, sessionstring, buffer, NULL); } ap_pclosef (r->pool, fp); #endif session = (Session*) AP_ALLOC (r->pool, sizeof (Session)); session->attrs = AP_TABLE_MAKE (r->pool, 16); s = sessionstring; while (*s) { char *attr, *value, u; char *t = s; while (*t && (*t != '=')) t++; if (!*t) { teqlog (r, 1, "readsession: malformed session file : %s", s); *s = '\0'; break; } *t = '\0'; attr = AP_STRDUP (r->pool, s); s = ++t; while (*t && (*t != '\n') && (*t != '\r')) t++; u = *t; *t = '\0'; value = AP_STRDUP (r->pool, s); *t = u; while ((*t == '\n') || (*t == '\r')) t++; s = t; if (!strcmp (attr, "org")) { session->org = value; continue; } if (!strcmp (attr, "user")) { session->user = value; continue; } if (!strcmp (attr, "host")) { session->host = value; continue; } if (!strcmp (attr, "hash")) { session->hash = value; continue; } if (!strcmp (attr, "path")) { session->path = value; continue; } if (!strcmp (attr, "require")) { session->filter = value; continue; } AP_TABLESET (session->attrs, attr, value); } if (!session->org) { teqlog (r, 2, "readsession: no org found"); return NULL; } if (!session->user) { teqlog (r, 2, "readsession: no user found"); return NULL; } if (!session->host) { teqlog (r, 2, "readsession: no host found"); return NULL; } if (!session->hash) { teqlog (r, 2, "readsession: no hash found"); return NULL; } if (!session->path) { teqlog (r, 2, "readsession: no path found"); return NULL; } requiredfilter = getFilter (r); if (requiredfilter) { if (session->filter) { teqlog (r, 3, "readsession: requiredfilter = %s, session->filter = %s.", requiredfilter, session->filter); } else { teqlog (r, 3, "readsession: requiredfilter = %s, session->filter = (null).", requiredfilter); } if (!session->filter || strcmp (requiredfilter, session->filter)) { teqlog (r, 2, "readsession: requiredfilter doesn't match session->filter."); return NULL; } } return (session); } static int writesession (request_rec *r, char *sessdir, Session *session) { char *pathhash, *fname; int i; AP_FILE_T fp; AP_STATUS rc; AP_MODE_T flags = (AP_WRITE | AP_CREATE); AP_PERM_T mode = (AP_UREAD | AP_UWRITE); const AP_ARRAY_HEADER_T *hdrs_attrs; const AP_TABLE_ENTRY_T *hdrs; if (sessdir == NULL) sessdir = defaultsessdir; pathhash = AP_SPRINTF (r->pool, "%u", hash (session->path)); fname = AP_STRCAT (r->pool, sessdir, "/", session->key, NULL); #ifdef APACHE2 if ((rc = AP_FILEOPEN (&fp, fname, flags, mode, r->pool)) != AP_SUCCESS) { #else if ((fp = AP_FILEOPEN (r->pool, fname, flags, mode)) < 0) { #endif teqlog (r, 2, "writesession: could not create session file %s, status = %d", fname, rc); r->status = 450; r->status_line = AP_SPRINTF (r->pool, "450 Unable to create session : file = %s", fname); return 0; } writeline (fp, AP_STRCAT (r->pool, "org=", session->org, NULL)); writeline (fp, AP_STRCAT (r->pool, "user=", session->user, NULL)); writeline (fp, AP_STRCAT (r->pool, "host=", session->host, NULL)); writeline (fp, AP_STRCAT (r->pool, "hash=", pathhash, NULL)); writeline (fp, AP_STRCAT (r->pool, "path=", session->path, NULL)); writeline (fp, AP_STRCAT (r->pool, "require=", session->filter, NULL)); hdrs_attrs = AP_TABLE_ELTS (session->attrs); hdrs = (const AP_TABLE_ENTRY_T*) hdrs_attrs->elts; for (i = 0; i < hdrs_attrs->nelts; i++) { if (!hdrs [i].key) continue; writeline (fp, AP_STRCAT (r->pool, hdrs [i].key, "=", hdrs[i].val, NULL)); } #ifdef APACHE2 AP_FILECLOSE (fp); #else ap_pclosef (r->pool, fp); #endif teqlog (r, 2, "writesession: session %s created", session->key); return 1; } static void writeline (AP_FILE_T fp, char *string) { AP_SIZE_T nbytes = strlen (string); #ifdef APACHE2 AP_FILEWRITE (fp, string, &nbytes); nbytes = 1; AP_FILEWRITE (fp, "\n", &nbytes); #else write (fp, string, nbytes); write (fp, "\n", 1); #endif } static void substsepar (char *string, char oldsepar, char newsepar) { char *s = string; while (*s) { if (*s == oldsepar) *s = newsepar; s++; } } static const char *cmd_rewrite (cmd_parms *cmd, cmd_conf *vconf, cmd_arg *path) { tequila_perdir_conf *dconf = (tequila_perdir_conf*) vconf; dconf->rewrite = AP_STRDUP (cmd->pool, path); return NULL; } static const char *cmd_allowif (cmd_parms *cmd, cmd_conf *vconf, cmd_arg *cond) { tequila_perdir_conf *dconf = (tequila_perdir_conf*) vconf; Filter *filter, *new; filter = parsefilter (cmd, cond); if (filter == NULL) return NULL; new = AP_ARRAYPUSH (dconf->allowif); new->nam = filter->nam; new->val = filter->val; new->next = filter->next; return NULL; } static const char *cmd_allownet (cmd_parms *cmd, cmd_conf *vconf, cmd_arg *net) { tequila_perdir_conf *dconf = (tequila_perdir_conf*) vconf; const char **netp; if (!net) return NULL; netp = (const char **) AP_ARRAYPUSH (dconf->allownet); *netp = AP_STRDUP (cmd->pool, net); return NULL; } static const char *cmd_request (cmd_parms *cmd, cmd_conf *vconf, cmd_arg *request) { char *r; tequila_perdir_conf *dconf = (tequila_perdir_conf*) vconf; const char **reqp; if (!request) return NULL; r = (char*) request; while ((*r == ' ') || (*r == ',') || (*r == '\r') || (*r == '\n')) r++; while (*r) { char *s = r; char t; while (*s && (*s != ' ') && (*s != ',') && (*s != '\r') && (*s != '\n')) s++; t = *s; *s = '\0'; reqp = (const char **) AP_ARRAYPUSH (dconf->request); *reqp = AP_STRDUP (cmd->pool, r); *s = t; while ((*s == ' ') || (*s == ',') || (*s == '\r') || (*s == '\n')) s++; r = s; } return NULL; } static const char *cmd_service (cmd_parms *cmd, cmd_conf *vconf, cmd_arg *servname) { tequila_perdir_conf *dconf = (tequila_perdir_conf*) vconf; dconf->servname = AP_STRDUP (cmd->pool, servname); return NULL; } static const char *cmd_allows (cmd_parms *cmd, cmd_conf *vconf, cmd_arg *cond) { tequila_perdir_conf *dconf = (tequila_perdir_conf*) vconf; Filter *filter, *new; filter = parsefilter (cmd, cond); if (filter == NULL) return NULL; new = AP_ARRAYPUSH (dconf->allows); new->nam = filter->nam; new->val = filter->val; new->next = filter->next; return NULL; } static const char *cmd_server (cmd_parms *cmd, void *dummy, cmd_arg *server) { server_rec *s = cmd->server; tequila_global_conf *gconf; gconf = (tequila_global_conf*) ap_get_module_config (s->module_config, &tequila_module); gconf->teqserver = AP_STRDUP (cmd->pool, server); return NULL; } static const char *cmd_serverurl (cmd_parms *cmd, void *dummy, cmd_arg *serverurl) { server_rec *s = cmd->server; tequila_global_conf *gconf; gconf = (tequila_global_conf*) ap_get_module_config (s->module_config, &tequila_module); gconf->teqserverurl = AP_STRDUP (cmd->pool, serverurl); return NULL; } static const char *cmd_resource (cmd_parms *cmd, cmd_conf *vconf, cmd_arg *resname) { tequila_perdir_conf *dconf = (tequila_perdir_conf*) vconf; dconf->resource = AP_STRDUP (cmd->pool, resname); return NULL; } static const char *cmd_sslkeyfile (cmd_parms *cmd, cmd_conf *vconf, cmd_arg *keyfile) { tequila_perdir_conf *dconf = (tequila_perdir_conf*) vconf; dconf->keyfile = AP_STRDUP (cmd->pool, keyfile); return NULL; } static const char *cmd_sslcertfile (cmd_parms *cmd, cmd_conf *vconf, cmd_arg *certfile) { tequila_perdir_conf *dconf = (tequila_perdir_conf*) vconf; dconf->certfile = AP_STRDUP (cmd->pool, certfile); return NULL; } static const char *cmd_identities (cmd_parms *cmd, cmd_conf *vconf, cmd_arg *identities) { tequila_perdir_conf *dconf = (tequila_perdir_conf*) vconf; dconf->identities = AP_STRDUP (cmd->pool, identities); return NULL; } static const char *cmd_confirmuser (cmd_parms *cmd, cmd_conf *vconf, cmd_arg *confirmuser) { tequila_perdir_conf *dconf = (tequila_perdir_conf*) vconf; dconf->confirmuser = AP_STRDUP (cmd->pool, confirmuser); return NULL; } static const char *cmd_customarg (cmd_parms *cmd, cmd_conf *vconf, cmd_arg *customarg) { tequila_perdir_conf *dconf = (tequila_perdir_conf*) vconf; const char **argp; if (!customarg) return NULL; argp = (const char **) AP_ARRAYPUSH (dconf->customargs); *argp = AP_STRDUP (cmd->pool, customarg); return NULL; } static const char *cmd_sessdir (cmd_parms *cmd, void *dummy, cmd_arg *sessdir) { server_rec *s = cmd->server; tequila_global_conf *gconf; gconf = (tequila_global_conf*) ap_get_module_config (s->module_config, &tequila_module); gconf->sessdir = AP_STRDUP (cmd->pool, sessdir); return NULL; } static const char *cmd_sessmax (cmd_parms *cmd, void *dummy, cmd_arg *sessmax) { server_rec *s = cmd->server; tequila_global_conf *gconf; gconf = (tequila_global_conf*) ap_get_module_config (s->module_config, &tequila_module); gconf->sessmax = atoi (sessmax); return NULL; } static const char *cmd_log (cmd_parms *cmd, void *dummy, cmd_arg *logfile) { server_rec *s = cmd->server; tequila_global_conf *gconf; gconf = (tequila_global_conf*) ap_get_module_config (s->module_config, &tequila_module); gconf->logfile = AP_STRDUP (cmd->pool, logfile); return NULL; } static const char *cmd_loglevel (cmd_parms *cmd, void *dummy, cmd_arg *loglevel) { server_rec *s = cmd->server; tequila_global_conf *gconf; gconf = (tequila_global_conf*) ap_get_module_config (s->module_config, &tequila_module); gconf->loglevel = atoi (loglevel); return NULL; } static const char *cmd_nossl (cmd_parms *cmd, void *dummy) { server_rec *s = cmd->server; tequila_global_conf *gconf; gconf = (tequila_global_conf*) ap_get_module_config (s->module_config, &tequila_module); gconf->usessl = 0; return NULL; } static const char *cmd_usessl (cmd_parms *cmd, void *dummy) { server_rec *s = cmd->server; tequila_global_conf *gconf; gconf = (tequila_global_conf*) ap_get_module_config (s->module_config, &tequila_module); gconf->usessl = 1; return NULL; } static const char *cmd_cafile (cmd_parms *cmd, void *dummy, cmd_arg *cafile) { server_rec *s = cmd->server; tequila_global_conf *gconf; gconf = (tequila_global_conf*) ap_get_module_config (s->module_config, &tequila_module); gconf->cafile = AP_STRDUP (cmd->pool, cafile); return NULL; } static const char *cmd_checkservername (cmd_parms *cmd, void *dummy) { server_rec *s = cmd->server; tequila_global_conf *gconf; gconf = (tequila_global_conf*) ap_get_module_config (s->module_config, &tequila_module); gconf->checkservername = 1; return NULL; } static Filter *parsefilter (cmd_parms *cmd, const char *str) { char *cstr = AP_STRDUP (cmd->pool, str); char *s = cstr; Filter *filter = NULL, *filt = NULL; while (*s) { char *n, *v = NULL; n = s; while (*s && *s != '=' && *s != '&') s++; if (*s == '=') { *s = '\0'; s++; v = s; while (*s && *s != '&') s++; } if (*s) { *s = '\0'; s++; } filt = (Filter*) AP_ALLOC (cmd->pool, sizeof (Filter)); filt->nam = AP_STRDUP (cmd->pool, n); filt->val = AP_STRDUP (cmd->pool, v); filt->next = filter; filter = filt; } return filt; } static char *skip_match (char *uri, char *prefix) { char *x = prefix + strlen (prefix); char *p = prefix, *urip = uri; while (p < x) { if (*p == '/') { while (*p == '/') ++p; while (*urip == '/') ++urip; } else { urip++; p++; } } return urip; } static void *create_perdir_config (AP_POOL *p, char *path) { tequila_perdir_conf *dconf; dconf = (tequila_perdir_conf*) AP_ALLOC (p, sizeof (tequila_perdir_conf)); if (path == NULL) return NULL; dconf->path = AP_STRDUP (p, path); dconf->rewrite = NULL; dconf->resource = NULL; dconf->keyfile = NULL; dconf->certfile = NULL; dconf->identities = NULL; dconf->confirmuser = NULL; dconf->servname = NULL; dconf->session = NULL; dconf->customargs = AP_ARRAYMAKE (p, 2, sizeof (char*)); dconf->allowif = AP_ARRAYMAKE (p, 2, sizeof (Filter)); dconf->allownet = AP_ARRAYMAKE (p, 2, sizeof (char*)); dconf->allows = AP_ARRAYMAKE (p, 2, sizeof (Filter)); dconf->request = AP_ARRAYMAKE (p, 2, sizeof (char*)); return (void*) dconf; } static void *merge_perdir_config (AP_POOL *p, void *base_conf, void *over_conf) { tequila_perdir_conf *mergeconf = AP_ALLOC (p, sizeof (tequila_perdir_conf)); tequila_perdir_conf *overconf = (tequila_perdir_conf *) over_conf; mergeconf->path = overconf->path; mergeconf->rewrite = overconf->rewrite; mergeconf->resource = overconf->resource; mergeconf->keyfile = overconf->keyfile; mergeconf->certfile = overconf->certfile; mergeconf->identities = overconf->identities; mergeconf->confirmuser = overconf->confirmuser; mergeconf->servname = overconf->servname; mergeconf->session = overconf->session; mergeconf->customargs = overconf->customargs; mergeconf->allowif = overconf->allowif; mergeconf->allownet = overconf->allownet; mergeconf->allows = overconf->allows; mergeconf->request = overconf->request; return (void *) mergeconf; } static void *create_global_config (AP_POOL *p, server_rec *s) { tequila_global_conf *gconf = AP_ALLOC (p, sizeof (tequila_global_conf)); gconf->logfile = NULL; gconf->teqserver = NULL; gconf->teqserverurl = NULL; gconf->sessdir = NULL; gconf->sessmax = 0; gconf->logfp = 0; gconf->loglevel = -1; gconf->usessl = 1; gconf->cafile = NULL; gconf->checkservername = 0; return (void*) gconf; } #define max(a,b) ((a > b) ? a : b) static void *merge_global_config (AP_POOL *p, void *base_conf, void *over_conf) { tequila_global_conf *mergeconf = AP_ALLOC (p, sizeof (tequila_global_conf)); tequila_global_conf *baseconf = (tequila_global_conf *) base_conf; tequila_global_conf *overconf = (tequila_global_conf *) over_conf; mergeconf->logfile = overconf->logfile ? AP_STRDUP (p, overconf->logfile) : AP_STRDUP (p, baseconf->logfile); mergeconf->teqserver = overconf->teqserver ? AP_STRDUP (p, overconf->teqserver) : AP_STRDUP (p, baseconf->teqserver); mergeconf->teqserverurl = overconf->teqserverurl ? AP_STRDUP (p, overconf->teqserverurl) : AP_STRDUP (p, baseconf->teqserverurl); mergeconf->sessdir = overconf->sessdir ? AP_STRDUP (p, overconf->sessdir) : AP_STRDUP (p, baseconf->sessdir); mergeconf->cafile = overconf->cafile ? AP_STRDUP (p, overconf->cafile) : AP_STRDUP (p, baseconf->cafile); mergeconf->sessmax = overconf->sessmax ? overconf->sessmax : baseconf->sessmax; mergeconf->logfp = 0; if (overconf->loglevel == -1) { mergeconf->loglevel = baseconf->loglevel; } else { mergeconf->loglevel = overconf->loglevel; } mergeconf->usessl = baseconf->usessl && overconf->usessl; mergeconf->checkservername = baseconf->checkservername && overconf->checkservername; return (void *) mergeconf; } static void openteqlog (server_rec *s, AP_POOL *p) { tequila_global_conf *gconf; char *fname; piped_log *pl; AP_STATUS rc; AP_MODE_T flags = (AP_WRITE | AP_APPEND | AP_CREATE); AP_PERM_T mode = (AP_UREAD | AP_UWRITE | AP_GREAD | AP_WREAD); gconf = ap_get_module_config (s->module_config, &tequila_module); if (gconf->logfile == NULL) return; if (*(gconf->logfile) == '\0') return; if (gconf->logfp) return; /* virtual log shared w/ main server */ if (*gconf->logfile == '|') { if ((pl = ap_open_piped_log (p, gconf->logfile + 1)) == NULL) { #ifdef APACHE2 ap_log_error (APLOG_MARK, APLOG_ERR, 0, s, #else ap_log_error (APLOG_MARK, APLOG_ERR, s, #endif "mod_tequila: could not open reliable pipe " "to TequilaLog filter %s", gconf->logfile + 1); exit (1); } gconf->logfp = ap_piped_log_write_fd (pl); } else if (*gconf->logfile != '\0') { fname = ap_server_root_relative (p, gconf->logfile); #ifdef APACHE2 if ((rc = AP_FILEOPEN (&gconf->logfp, fname, flags, mode, p)) != AP_SUCCESS) { gconf->logfp = NULL; ap_log_error (APLOG_MARK, APLOG_ERR, 0, s, #else if ((gconf->logfp = AP_FILEOPEN (p, fname, flags, mode)) < 0) { gconf->logfp = 0; ap_log_error (APLOG_MARK, APLOG_ERR, s, #endif "mod_tequila: could not open Tequila log file : %s, %s", fname, gconf->logfile); exit (1); } } return; } static void teqlog (request_rec *r, int level, const char *text, ...) { tequila_global_conf *gconf; conn_rec *conn = r->connection; char *str1; char str2 [2048]; char str3 [2100]; va_list ap; ssize_t wsize; char *now; const char *rhost; #ifdef APACHE2 AP_STATUS rv; AP_SIZE_T nbytes; #endif va_start (ap, text); if (r == NULL) { if (level > default_loglevel) return; AP_VSNPRINTF (str2, sizeof (str2), text, ap); wsize = write (2, str2, strlen (str2)); return; } gconf = ap_get_module_config (r->server->module_config, &tequila_module); if (!gconf->logfp) return; if (gconf->logfile == NULL) return; if (*(gconf->logfile) == '\0') return; if (gconf->loglevel == -1) gconf->loglevel = default_loglevel; if (level > gconf->loglevel) return; #ifdef APACHE2 rhost = ap_get_remote_host (conn, r->server->module_config, REMOTE_NOLOOKUP, NULL); #else rhost = ap_get_remote_host (conn, r->server->module_config, REMOTE_NOLOOKUP); #endif if (rhost == NULL) rhost = "UNKNOWN-HOST"; str1 = AP_STRCAT (r->pool, rhost, NULL); AP_VSNPRINTF (str2, sizeof(str2), text, ap); va_end (ap); now = current_logtime (r); AP_SNPRINTF (str3, sizeof(str3), "%s %s (%d) %s\n", str1, now, level, str2); #ifdef APACHE2 rv = apr_global_mutex_lock (tequila_log_lock); if (rv != AP_SUCCESS) { ap_log_rerror (APLOG_MARK, APLOG_ERR, rv, r, "apr_global_mutex_lock (tequila_log_lock) failed"); } nbytes = strlen (str3); apr_file_write (gconf->logfp, str3, &nbytes); rv = apr_global_mutex_unlock (tequila_log_lock); if (rv != AP_SUCCESS) { ap_log_rerror (APLOG_MARK, APLOG_ERR, rv, r, "apr_global_mutex_unlock (tequila_log_lock) failed"); } #else fd_lock (r, gconf->logfp); write (gconf->logfp, str3, strlen (str3)); fd_unlock (r, gconf->logfp); #endif return; } static char *current_logtime (request_rec *r) { int timz; char tstr [80]; char sign; #ifdef APACHE2 apr_time_exp_t xt; AP_SIZE_T rv; ap_explode_recent_localtime (&xt, r->request_time); timz = xt.tm_gmtoff; sign = (timz < 0 ? '-' : '+'); if (timz < 0) timz = -timz; timz = timz / 60; apr_strftime (tstr, &rv, 80, "[%d/%b/%Y:%H:%M:%S ", &xt); #else struct tm *t; t = ap_get_gmtoff (&timz); sign = (timz < 0 ? '-' : '+'); if (timz < 0) timz = -timz; timz = timz / 60; strftime(tstr, 80, "[%d/%b/%Y:%H:%M:%S ", t); #endif AP_SNPRINTF (tstr + strlen (tstr), 80 - strlen (tstr), "%c%.2d%.2d]", sign, timz / 60, timz % 60); return AP_STRDUP (r->pool, tstr); } #ifdef APACHE2 static int post_config (AP_POOL *p, AP_POOL *plog, AP_POOL *ptemp, server_rec *s) { AP_STATUS rv; proxy_available = (ap_find_linked_module("mod_proxy.c") != NULL); if ((rv = apr_global_mutex_create (&tequila_log_lock, NULL, APR_LOCK_DEFAULT, p)) != AP_SUCCESS) { ap_log_error (APLOG_MARK, APLOG_CRIT, rv, s, "mod_tequila : could not create tequila_log_lock"); return HTTP_INTERNAL_SERVER_ERROR; } url_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https); #if APR_USE_SYSVSEM_SERIALIZE rv = unixd_set_global_mutex_perms (tequila_log_lock); if (rv != AP_SUCCESS) { ap_log_error (APLOG_MARK, APLOG_CRIT, rv, s, "mod_tequila : Could not set permissions on " "tequila_log_lock; check User and Group directives"); return HTTP_INTERNAL_SERVER_ERROR; } #endif for (; s; s = s->next) { openteqlog (s, p); } return OK; } #else static void fd_lock (request_rec *r, int fd) { int rc = -1; #ifdef USE_FCNTL lock_it.l_whence = SEEK_SET; /* from current point */ lock_it.l_start = 0; /* -"- */ lock_it.l_len = 0; /* until end of file */ lock_it.l_type = F_WRLCK; /* set exclusive/write lock */ lock_it.l_pid = 0; /* pid not actually interesting */ while (((rc = fcntl(fd, F_SETLKW, &lock_it)) < 0) && (errno == EINTR)) continue; #endif #ifdef USE_FLOCK while (((rc = flock(fd, LOCK_EX)) < 0) && (errno == EINTR)) continue; #endif #ifdef USE_LOCKING /* Lock the first byte, always, assume we want to append and seek to the end afterwards */ lseek (fd, 0, SEEK_SET); rc = _locking (fd, _LK_LOCK, 1); lseek (fd, 0, SEEK_END); #endif #ifdef NETWARE if ((locking_sem != 0) && (TimedWaitOnLocalSemaphore (locking_sem, 10000) != 0)) rc = -1; else rc = 1; #endif if (rc < 0) { fprintf (stderr, "mod_tequila: failed to lock file descriptor\n"); exit (1); } return; } static void fd_unlock (request_rec *r, int fd) { int rc; #ifdef USE_FCNTL unlock_it.l_whence = SEEK_SET; /* from current point */ unlock_it.l_start = 0; /* -"- */ unlock_it.l_len = 0; /* until end of file */ unlock_it.l_type = F_UNLCK; /* unlock */ unlock_it.l_pid = 0; /* pid not actually interesting */ rc = fcntl (fd, F_SETLKW, &unlock_it); #endif #ifdef USE_FLOCK rc = flock (fd, LOCK_UN); #endif #ifdef USE_LOCKING lseek (fd, 0, SEEK_SET); rc = _locking (fd, _LK_UNLCK, 1); lseek (fd, 0, SEEK_END); #endif #ifdef NETWARE if (locking_sem) SignalLocalSemaphore (locking_sem); rc = 1; #endif if (rc < 0) return; } static void init_module (server_rec *s, AP_POOL *p) { proxy_available = (ap_find_linked_module ("mod_proxy.c") != NULL); for (; s; s = s->next) { openteqlog (s, p); } } #endif static void set_cookie (request_rec *r, char *key) { tequila_perdir_conf *dconf; char *cookie; unsigned pathhash; dconf = (tequila_perdir_conf*) ap_get_module_config (r->per_dir_config, &tequila_module); if ((dconf == NULL) || (dconf->path == NULL)) return; teqlog (r, 4, "set_cookie: uri = %s, dconf->path = %s", r->uri, dconf->path); pathhash = hash (dconf->path); cookie = AP_SPRINTF (r->pool, "teqkey:%u=%s; path=%s", pathhash, key, "/"); teqlog (r, 2, "set_cookie: cookie = %s", cookie); AP_TABLESET (r->err_headers_out, "Set-Cookie", cookie); return; } static char *getkey (request_rec *r) { char *key = (char*)0; char *keydeb; char *args = NULL; const char *cookie; + int keyfound = 0; keyfromcookie = 0; // // Try URL first // args = r->args; - if (args && (keydeb = strstr (args, "key="))) { + if (args) { + char *left = args; + keydeb = strstr (left, "key="); + while (keydeb) { + if ((keydeb == left) || (*(keydeb - 1) == '&') || (*(keydeb - 1) == '?')) { + keyfound = 1; + break; + } + left = keydeb + 4; + keydeb = strstr (left, "key="); + } + } + if (keyfound) { char *s = keydeb + 4; while (*s && (*s != '&')) s++; if (*s == '&') { *s = '\0'; key = AP_STRDUP (r->pool, keydeb + 4); *s = '&'; } else { key = AP_STRDUP (r->pool, keydeb + 4); } } if (key) return key; // // No key in URL, try cookies. // if (r->main) { /* subrequest */ const char *cookie = AP_TABLEGET (r->main->err_headers_out, "Set-Cookie"); if (cookie) key = getkeyfromcookie (r, cookie); if (key) { keyfromcookie = 1; return key; } } cookie = AP_TABLEGET (r->headers_in, "Cookie"); if (cookie) key = getkeyfromcookie (r, cookie); if (key) { keyfromcookie = 1; return key; } return NULL; } static char *getkeyfromcookie (request_rec *r, const char *cookie) { tequila_perdir_conf *dconf; char *value, *pathhash; if (!cookie) { teqlog (r, 99, "getkeyfromcookie:no cookie found"); return (char*)0; } dconf = (tequila_perdir_conf*) ap_get_module_config (r->per_dir_config, &tequila_module); if ((dconf == NULL) || (dconf->path == NULL)) return (char*)0; pathhash = AP_SPRINTF (r->pool, "%u", hash (dconf->path)); while (cookie && (value = strstr (cookie, "teqkey:"))) { char *hashbuf, *hashend, *cookbuf, *cookend; value += strlen ("teqkey:"); hashbuf = AP_STRDUP (r->pool, value); hashend = strchr (hashbuf, '='); if (hashend) *hashend = '\0'; cookbuf = hashend + 1; cookend = strchr (cookbuf, ';'); if (cookend) *cookend = '\0'; if (!strcmp (hashbuf, pathhash)) return cookbuf; cookie = cookend ? cookend + 2 : NULL; } return (char*)0; } static unsigned rantable [] = { 747323161, 163537951, 559183603, 535991927, 55228437, 902893215, 702443576, 383259134, 470551003, 409377227, 762435312, 12954216, 630240577, 429738414, 747966939, 524041125, 52534467, 937350059, 721602759, 771156665, 488380559, 4064707, 292023389, 958556516, 108153117, 757587078, 10845887, 667567374, 162364313, 635823247, 605561322, 700796899, 133054934, 174699191, 527643367, 878021462, 336077656, 744595963, 110892239, 578143469, 938117957, 189405922, 310366059, 662568395, 302540930, 415351040, 152178176, 439074741, 546037787, 729518521, 565999420, 94247440, 488856080, 620697805, 704311001, 427860482, 958415115, 443025647, 602304977, 275428698, 305330063, 989115693, 12720429, 762708049, 53701004, 123361206, 755590333, 111032844, 241454650, 222111569, 571301928, 740799682, 978018134, 587842825, 666902019, 771909829, 392389580, 705378773, 802997113, 757532942, 18403645, 535199863, 443011788, 770089618, 354719086, 166649883, 562152615, 207773813, 798374317, 548274697, 191292350, 243399978, 585649237, 746210183, 620179656, 517625624, 139966790, 911997570, 132500521, 449348102, 517478498, 125893297, 767028767, 826990710, 576848567, 935575809, 987216614, 640287812, 350307482, 164030500, 343584660, 778009689, 385756146, 96474262, 828151765, 528953582, 479452185, 387811215, 391990207, 277922478, 812303128, 763943999, 257744410, 582803839, 275887479, 25822136, 915890024, 434924082, }; static unsigned hash (char *string) { unsigned h = 0; while (*string) { unsigned char i = *string; unsigned char l = ((h >> 24) & 0x000000ff); h = ((h << 8) | l) ^ rantable [i]; string++; } return (h); } static const char c2x_table [] = "0123456789ABCDEF"; #ifdef DEBUG static char *escape_url (AP_POOL *p, const char *url) { char *copy; const char *u; char *c; if (url == NULL) return NULL; copy = AP_ALLOC (p, 3 * strlen (url) + 3); u = (const char *) url; c = (char *)copy; while (*u) { if ((*u == ' ') || (*u == '?') || (*u == '&') || (*u == '/')) { *c++ = '%'; *c++ = c2x_table [*u >> 4]; *c++ = c2x_table [*u & 0xf]; } else { *c++ = *u; } ++u; } *c = '\0'; return copy; } static char *unescape_url (AP_POOL *p, const char *url) { char *copy; const char *u; char *c; if (url == NULL) return NULL; copy = AP_ALLOC (p, strlen (url) + 1); u = (const char *) url; c = (char *)copy; while (*u) { if (*u == '%') { int hexcode; u++; if (*u && sscanf (u, "%2x", &hexcode)) { *c++ = hexcode; u++; u++; } else { *c++ = '%'; *c++ = *u++; *c++ = *u++; } } else { *c++ = *u++; } } *c = '\0'; return copy; } static void printfilter (request_rec *r, Filter *filter) { if (r == NULL) { fprintf (stderr, "printfilter: filter = "); } else { teqlog (r, 3, "printfilter: filter = "); } while (filter) { if (r == NULL) { if (filter->val) { fprintf (stderr, " (%s=%s) ", filter->nam, filter->val); } else { fprintf (stderr, " (%s) ", filter->nam); } } else { if (filter->val) { teqlog (r, 3, " (%s=%s) ", filter->nam, filter->val); } else { teqlog (r, 3, " (%s) ", filter->nam); } } filter = filter->next; } if (r == NULL) fprintf (stderr, "\n"); } #endif /* DEBUG */ static int createserverrequest (request_rec *r) { tequila_perdir_conf *dconf; tequila_global_conf *gconf; char *proto, *teqserver, *tequrl, *args, *postline, *hostline, *contlen, *res, *key; int status; int port = 80; int https; int argslen; int maxresult = 1024; char result [1025]; int len; if (!r || !r->server) return DECLINED; gconf = (tequila_global_conf*) ap_get_module_config (r->server->module_config, &tequila_module); #ifdef APACHE2 https = url_is_https && url_is_https (r->connection); teqlog (r, 2, "createserverrequest: url_is_https = %d", https); proto = https ? "https" : "http"; #else proto = "http"; #endif tequrl = gconf->teqserverurl; if (!tequrl) { teqserver = gconf->teqserver; if (!teqserver) teqserver = defaultteqserver; tequrl = AP_STRCAT (r->pool, "https://", teqserver, "/cgi-bin/tequila/auth", NULL); } else { char *u = tequrl; while (*u && *u != '/') u++; while (*u == '/') u++; char *v = u; while (*v && *v != '/') v++; char c = *v; *v = '\0'; teqserver = AP_STRDUP (r->pool, u); *v = c; } dconf = (tequila_perdir_conf*) ap_get_module_config (r->per_dir_config, &tequila_module); if ((dconf == NULL) || (dconf->allowif == NULL)) return HTTP_FORBIDDEN; if (dconf->resource) { args = AP_STRCAT (r->pool, "resource=", dconf->resource, "\n", NULL); } else { char *urlaccess, *filter, *allows, *servname, *request; int port = ap_get_server_port (r); if (port != 80) { char sport [8]; sprintf (sport, "%d", port); urlaccess = AP_STRCAT (r->pool, proto, "://", r->hostname, ":", sport, r->uri, NULL); } else { urlaccess = AP_STRCAT (r->pool, proto, "://", r->hostname, r->uri, NULL); } if (r->args) urlaccess = AP_STRCAT (r->pool, urlaccess, "?", r->args, NULL); filter = getFilter (r); if (filter == NULL) return DECLINED; args = AP_STRCAT (r->pool, "urlaccess=", urlaccess, "\n", "path=", dconf->path, "\n", "require=", filter, "\n", NULL); if (dconf->identities) { args = AP_STRCAT (r->pool, args, "identities=", dconf->identities, "\n", NULL); } if (dconf->confirmuser) { args = AP_STRCAT (r->pool, args, "confirmuser=", dconf->confirmuser, "\n", NULL); } allows = getAllows (r); if (allows) { teqlog (r, 9, "createserverrequest: allows = %s.", allows); args = AP_STRCAT (r->pool, args, "allows=", allows, "\n", NULL); } servname = dconf->servname; if (!servname) { servname = AP_STRCAT (r->pool, "Document ", r->server->server_hostname, ":", dconf->path, NULL); } args = AP_STRCAT (r->pool, args, "service=", servname, "\n", NULL); request = getRequest (r); if (request) { teqlog (r, 9, "createserverrequest: request = %s.", request); args = AP_STRCAT (r->pool, args, "request=", request, "\n", NULL); } if (dconf->customargs) { int i; char **argp = (char**) dconf->customargs->elts; for (i = 0; i < dconf->customargs->nelts; i++) { char *arg = argp [i]; teqlog (r, 9, "createserverrequest: custom arg = %s.", arg); args = AP_STRCAT (r->pool, args, arg, "\n", NULL); } } } teqlog (r, 9, "createserverrequest: args = %s", args); argslen = strlen (args); postline = "POST /cgi-bin/tequila/createrequest HTTP/1.0\r\n"; hostline = AP_STRCAT (r->pool, "Host: ", teqserver, "\r\n", NULL); contlen = AP_SPRINTF (r->pool, "Content-length: %d\r\n", argslen); #ifdef USESSL if (!gconf->usessl) #endif { ssize_t wsize; int sock, tot; struct sockaddr_in sockaddr; struct hostent *hostentry; hostentry = gethostbyname (teqserver); if (!hostentry) { teqlog (r, 2, "createserverrequest: cannot get address of %s.", teqserver); return HTTP_FORBIDDEN; } bzero ((char*) &sockaddr, sizeof (sockaddr)); bcopy (hostentry->h_addr, (char*) &sockaddr.sin_addr, hostentry->h_length); sockaddr.sin_family = hostentry->h_addrtype; sockaddr.sin_port = htons (port); sock = socket (AF_INET, SOCK_STREAM, 0); if (sock < 0) { teqlog (r, 2, "createserverrequest: cannot create socket."); return HTTP_FORBIDDEN; } status = connect (sock, (struct sockaddr*) &sockaddr, sizeof (sockaddr)); if (status < 0) { teqlog (r, 2, "createserverrequest: cannot connect to socket."); return HTTP_FORBIDDEN; } wsize = write (sock, postline, strlen (postline)); wsize = write (sock, hostline, strlen (hostline)); wsize = write (sock, contlen, strlen (contlen)); wsize = write (sock, "\r\n", 2); wsize = write (sock, args, argslen); wsize = write (sock, "\r\n", 2); tot = 0; while ((len = read (sock, result + tot, maxresult - tot))) { if (len < 0) break; tot += len; if (tot > maxresult) { tot = maxresult; break; } result [tot] = '\0'; } close (sock); } #ifdef USESSL else { SSLenv *sslenv = opensslsocket (r, teqserver, 443); int tot = 0; if (!sslenv) { teqlog (r, 2, "createserverrequest: cannot open SSL connection to %s.", teqserver); return HTTP_FORBIDDEN; } BIO_write (sslenv->bio, postline, strlen (postline)); BIO_write (sslenv->bio, hostline, strlen (hostline)); BIO_write (sslenv->bio, contlen, strlen (contlen)); BIO_write (sslenv->bio, "\r\n", 2); BIO_write (sslenv->bio, args, argslen); BIO_write (sslenv->bio, "\r\n", 2); while ((len = BIO_read (sslenv->bio, result + tot, maxresult - tot))) { if (len < 0) break; tot += len; if (tot > maxresult) { tot = maxresult; break; } } result [tot] = '\0'; closesslsocket (sslenv); } #endif teqlog (r, 99, "createserverrequest: result = %s", result); res = result; while (*res && (*res != ' ')) res++; if (!sscanf (res, "%d", &status)) { teqlog (r, 2, "createserverrequest: invalid status line"); return HTTP_FORBIDDEN; } if (status != 200) { teqlog (r, 2, "createserverrequest: invalid status : %d", status); return HTTP_FORBIDDEN; } key = NULL; while (*res) { while (*res && *res != '\n' && *res != '\r') res++; if (!*res) break; while (*res == '\n' || *res == '\r') res++; if (strncmp (res, "key=", 4) == 0) { char *end; res += 4; end = res; while (*end && (*end != '\n')) end++; *end = '\0'; key = AP_STRDUP (r->pool, res); break; } } if (!key) { teqlog (r, 2, "createserverrequest: Bad response from local Tequila server"); return HTTP_FORBIDDEN; } r->args = AP_STRCAT (r->pool, "requestkey=", key, NULL); r->filename = AP_STRCAT (r->pool, tequrl, "?", r->args, NULL); teqlog (r, 3, "createserverrequest: r->filename = %s", r->filename); AP_TABLESET (r->headers_out, "Location", r->filename); AP_TABLESET (r->headers_out, "Pragma", "no-cache"); return HTTP_MOVED_TEMPORARILY; } static Session *fetchattributes (request_rec *r, const char *requestkey) { tequila_perdir_conf *dconf; tequila_global_conf *gconf; char *hostline, *teqserver, *res; int status; int port = 80; Session *session; int maxresult = 8192; char result [8193]; int len; char *org = "", *user = "", *host = "", *path = "", *filter = ""; teqlog (r, 2, "fetchattributes: requestkey = %s", requestkey); if (!r || !r->server) { error (r, "Unable to contact Tequila server"); return NULL; } gconf = (tequila_global_conf*) ap_get_module_config (r->server->module_config, &tequila_module); dconf = (tequila_perdir_conf*) ap_get_module_config (r->per_dir_config, &tequila_module); teqserver = gconf->teqserver; if (teqserver == NULL) teqserver = defaultteqserver; hostline = AP_STRCAT (r->pool, "Host: ", teqserver, "\r\n", NULL); #ifdef USESSL if (!gconf->usessl && (!dconf || !dconf->resource)) #endif { ssize_t wsize; int sock, tot; struct sockaddr_in sockaddr; struct hostent *hostentry; char *getline = AP_STRCAT (r->pool, "GET /cgi-bin/tequila/fetchattributes?key=", requestkey, " HTTP/1.0\r\n", NULL); hostentry = gethostbyname (teqserver); if (hostentry == NULL) { teqlog (r, 2, "fetchattributes: cannot get address of %s.", teqserver); error (r, "Unable to get address of Tequila server"); return NULL; } bzero ((char*) &sockaddr, sizeof (sockaddr)); bcopy (hostentry->h_addr, (char*) &sockaddr.sin_addr, hostentry->h_length); sockaddr.sin_family = hostentry->h_addrtype; sockaddr.sin_port = htons (port); sock = socket (AF_INET, SOCK_STREAM, 0); if (sock < 0) { teqlog (r, 2, "fetchattributes: cannot create socket."); error (r, "Unable to contact Tequila server"); return NULL; } status = connect (sock, (struct sockaddr*) &sockaddr, sizeof (sockaddr)); if (status < 0) { teqlog (r, 2, "fetchattributes: cannot connect to socket."); error (r, "Unable to connect to Tequila server"); return NULL; } wsize = write (sock, getline, strlen (getline)); wsize = write (sock, hostline, strlen (hostline)); wsize = write (sock, "\r\n", 2); tot = 0; while ((len = read (sock, result + tot, maxresult - tot))) { if (len < 0) break; tot += len; if (tot > maxresult) { tot = maxresult; break; } result [tot] = '\0'; } close (sock); } #ifdef USESSL else { int tot = 0; char *scriptname = (dconf && dconf->resource) ? "/cgi-bin/tequilac" : "/cgi-bin/tequila"; char *getline = AP_STRCAT (r->pool, "GET ", scriptname, "/fetchattributes?key=", requestkey, " HTTP/1.0\r\n", NULL); SSLenv *sslenv = opensslsocket (r, teqserver, 443); if (!sslenv) { teqlog (r, 2, "fetchattributes: cannot open SSL connection to %s.", teqserver); error (r, "Unable to open SSL connection to Tequila server"); return NULL; } BIO_write (sslenv->bio, getline, strlen (getline)); BIO_write (sslenv->bio, hostline, strlen (hostline)); BIO_write (sslenv->bio, "\r\n", 2); while ((len = BIO_read (sslenv->bio, result + tot, maxresult - tot))) { if (len < 0) break; tot += len; if (tot > maxresult) { tot = maxresult; break; } } result [tot] = '\0'; closesslsocket (sslenv); } #endif res = result; while (*res && (*res != ' ')) res++; if (!sscanf (res, "%d", &status)) { char *err; teqlog (r, 2, "fetchattributes: invalid status line"); err = AP_STRCAT (r->pool, "Tequila server returns invalid line : ", result, NULL); error (r, err); return NULL; } if (status == 451) { /* Invalid key.*/ teqlog (r, 2, "fetchattributes: key %s unknown to server.", requestkey); return NULL; } if (status != 200) { teqlog (r, 2, "fetchattributes: invalid status : %d", status); while (*res && (*res != '\n')) res++; *res = '\0'; error (r, result); return NULL; } while (*res) { int sep = 0; while (*res && *res != '\n' && *res != '\r') res++; while (*res == '\n' || *res == '\r') { if (*res == '\n') sep++; res++; } if (sep > 1) break; } session = (Session*) AP_ALLOC (r->pool, sizeof (Session)); session->attrs = AP_TABLE_MAKE (r->pool, 16); while (*res) { char *name = res; while (*res && *res != '=' && *res != '\n' && *res != '\r') res++; if (*res == '=') { char *value; *res = '\0'; res++; value = res; while (*res && *res != '\n' && *res != '\r') res++; if (*res) *res++ = '\0'; if (strcmp (name, "org") == 0) { org = value; } else if (strcmp (name , "user") == 0) { user = value; } else if (strcmp (name, "host") == 0) { host = value; } else if (strcmp (name, "path") == 0) { path = value; } else if (strcmp (name, "require") == 0) { filter = value; } else { AP_TABLESET (session->attrs, name, value); } } while (*res == '\n' || *res == '\r') res++; } if ((strlen (org) == 0) || (strlen (user) == 0) || (strlen (host) == 0) || (strlen (path) == 0)) { teqlog (r, 2, "fetchattributes: invalid response from Tequila server."); error (r, "Invalid response from Tequila server."); return NULL; } substsepar (filter, ':', '&'); session->key = AP_STRDUP (r->pool, requestkey); session->org = AP_STRDUP (r->pool, org); session->user = AP_STRDUP (r->pool, user); session->host = AP_STRDUP (r->pool, host); session->path = AP_STRDUP (r->pool, path); session->filter = filter ? AP_STRDUP (r->pool, filter) : NULL; writesession (r, gconf->sessdir, session); return session; } static int setenvvars (request_rec *r) { AP_TABLE_T *env = r->subprocess_env; const AP_ARRAY_HEADER_T *hdrs_attrs; const AP_TABLE_ENTRY_T *hdrs; tequila_perdir_conf *dconf; Session *session; char *cookiename; int i; dconf = (tequila_perdir_conf*) ap_get_module_config (r->per_dir_config, &tequila_module); if (!dconf || !dconf->session) return OK; session = dconf->session; cookiename = AP_SPRINTF (r->pool, "teqkey:%u", hash (session->path)); AP_TABLESET (env, "REMOTE_USER", session->user); AP_TABLESET (env, "HTTP_TEQUILA_KEY", session->key); AP_TABLESET (env, "HTTP_TEQUILA_ORG", session->org); AP_TABLESET (env, "HTTP_TEQUILA_USER", session->user); AP_TABLESET (env, "HTTP_TEQUILA_HOST", session->host); AP_TABLESET (env, "HTTP_TEQUILA_PATH", session->path); AP_TABLESET (env, "HTTP_TEQUILA_FILTER", session->filter); AP_TABLESET (env, "HTTP_TEQUILA_SESSION_COOKIE_NAME", cookiename); hdrs_attrs = AP_TABLE_ELTS (session->attrs); hdrs = (const AP_TABLE_ENTRY_T*) hdrs_attrs->elts; for (i = 0; i < hdrs_attrs->nelts; i++) { char *key, *k, *attrname; if (!hdrs [i].key) continue; key = AP_STRDUP (r->pool, hdrs [i].key); k = key; while (*k) { *k = AP_TOUPPER (*k); k++; } attrname = AP_STRCAT (r->pool, "HTTP_TEQUILA_", key, NULL); AP_TABLESET (env, attrname, hdrs[i].val); } return OK; } static void error (request_rec *r, char *msg) { #ifdef APACHE2 ap_set_content_type (r, "text/html; charset=iso-8859-1"); #else r->content_type = "text/html; charset=iso-8859-1"; #endif ap_rvputs (r, DOCTYPE_HTML_2_0 "\n", "Tequila error", "\n\n

", "Tequila error", "

\n", NULL); ap_rvputs (r, msg, NULL); ap_rvputs (r, ap_psignature ("
\n", r), NULL); ap_rvputs (r, "\n", NULL); ap_finalize_request_protocol (r); } #ifdef USESSL static SSLenv *opensslsocket (request_rec *r, char *host, int port) { char hostport [128]; SSL *ssl; BIO *bio; SSL_CTX *ctx; SSLenv *sslenv; void *sconf = r->server->module_config; tequila_global_conf *gconf; tequila_perdir_conf *dconf; sprintf (hostport, "%s:%d", host, port); teqlog (r, 1, "opensslsocket:Connecting to %s.", hostport); SSL_library_init (); ctx = SSL_CTX_new (SSLv23_client_method ()); if(ctx == NULL) { teqlog (r, 1, "Unable to allocate SSL context."); return NULL; } gconf = (tequila_global_conf*) ap_get_module_config (sconf, &tequila_module); dconf = (tequila_perdir_conf*) ap_get_module_config (r->per_dir_config, &tequila_module); if (gconf->cafile) { int loadverif = SSL_CTX_load_verify_locations (ctx, gconf->cafile, NULL); if(!loadverif) { teqlog (r, 1, "opensslsocket:Error loading trust store."); ERR_print_errors_fp (stderr); SSL_CTX_free (ctx); return NULL; } } bio = BIO_new_ssl_connect (ctx); BIO_get_ssl (bio, &ssl); SSL_set_mode (ssl, SSL_MODE_AUTO_RETRY); if (dconf->resource && dconf->keyfile && dconf->certfile) { teqlog (r, 2, "opensslsocket:resource = %s, keyfile = %s, certfile = %s.", dconf->resource, dconf->keyfile, dconf->certfile); SSL_use_certificate_file (ssl, dconf->certfile, SSL_FILETYPE_PEM); SSL_use_PrivateKey_file (ssl, dconf->keyfile, SSL_FILETYPE_PEM); } BIO_set_conn_hostname (bio, hostport); if (BIO_do_connect (bio) <= 0) { teqlog (r, 1, "opensslsocket:Error attempting to connect."); ERR_print_errors_fp (stderr); BIO_free_all (bio); SSL_CTX_free (ctx); return NULL; } if (gconf->checkservername) { X509 *cert = SSL_get_peer_certificate (ssl); if (!checkcertificate (r, cert, host)) { teqlog (r, 1, "opensslsocket:Bad certificate."); BIO_free_all (bio); SSL_CTX_free (ctx); return NULL; } } if (gconf->cafile) { int verif; teqlog (r, 1, "opensslsocket:Checking server certificate chain."); verif = SSL_get_verify_result (ssl); if (verif != X509_V_OK) { teqlog (r, 1, "opensslsocket:Certificate verification error: %d.", SSL_get_verify_result(ssl)); BIO_free_all (bio); SSL_CTX_free (ctx); return NULL; } } sslenv = (SSLenv*) malloc (sizeof (SSLenv*)); sslenv->ctx = ctx; sslenv->bio = bio; return sslenv; } static void closesslsocket (SSLenv *sslenv) { BIO_free_all (sslenv->bio); SSL_CTX_free (sslenv->ctx); free (sslenv); } static int checkcertificate (request_rec *r, X509 *cert, char *host) { STACK_OF(GENERAL_NAME) *gens; X509_NAME *subject = X509_get_subject_name (cert); char buf [513]; char *c; int rg; int i; teqlog (r, 3, "checkcertificate:host = %s, subject = %s.", host, buf); X509_NAME_oneline (subject, buf, 512); c = buf + 1; while (*c) { if (!strncasecmp (c, "cn=", 3)) { c += 3; if (*c == '(') { char *domain; char *subjects [32]; int is = 0; char *ut; int ns; char tmp [128]; char *t = ++c; while (*t && (*t != ')')) { char v; char *u = t; while ((*u != '|') && (*u != ')')) u++; v = *u; *u = '\0'; subjects [is++] = t; t = u + 1; if (v != '|') break; } if (!*t) break; ut = t; while (*ut && (*ut != '/')) ut++; *ut = '\0'; domain = t; ns = is; for (is = 0; is < ns; is++) { strcpy (tmp, subjects [is]); strcat (tmp, domain); if (!strcmp (tmp, host)) return 1; } c = ut + 1; } else if (*c == '*') { char *s = c + 1; char *t = s; char *h; int n; while (*t && (*t != '/')) t++; *t = '\0'; h = host; n = strlen (h) - strlen (s); if (n >= 0) h += n; if (!strcasecmp (h, s)) return 1; c = t + 1; } else { char *s = c; while (*s && (*s != '/')) s++; *s = '\0'; if (!strcmp (c, host)) return 1; c = s + 1; } } else { while (*c && (*c != '/')) c++; if (*c) c++; } } gens = X509_get_ext_d2i (cert, NID_subject_alt_name, NULL, NULL); if (!gens) return 0; teqlog (r, 99, "Checking alternate names.."); rg = sk_GENERAL_NAME_num(gens); for (i = 0; i < rg; ++i) { const GENERAL_NAME *gn = sk_GENERAL_NAME_value (gens, i); if (gn->type == GEN_DNS) { char *c = (char*) gn->d.ia5->data; char *h = host; if (*c == '*') { int n; ++c; n = strlen (h) - strlen (c); if (n >= 0) h += n; } if (!strcasecmp (c, h)) return 1; } } sk_GENERAL_NAME_free (gens); return 0; } #endif /* USESSL */