Page MenuHomec4science

mod_tequila.c
No OneTemporary

File Metadata

Created
Tue, May 14, 15:20

mod_tequila.c

//
//
// Apache module for Tequila authentication tool.
// Version 2.0.2
//
//
#include <httpd.h>
#include <http_log.h>
#include <http_core.h>
#include <http_config.h>
#include <http_protocol.h>
#ifdef APACHE2
# include <apr_strings.h>
# include <ap_config.h>
# include <apr_lib.h>
#endif
#ifdef USESSL
# include <string.h>
# include "openssl/bio.h"
# include "openssl/ssl.h"
# include "openssl/err.h"
# include "openssl/x509v3.h"
#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 char *unescape_url (AP_POOL *p, const char *url);
static int setenvvars (request_rec *r);
static void error (request_rec *r, char *msg);
static void writeline (AP_FILE_T fp, char *string);
#ifdef APACHE2
#include <sys/stat.h>
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( "TequilaServer", cmd_server, NULL, RSRC_CONF, "Tequila server"),
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" },
{"TequilaServer", cmd_server, NULL, RSRC_CONF, TAKE1, "Local Tequila server" },
{"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) {
char *left = skip_match (r->uri, dconf->path);
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) {
strfilt = AP_STRCAT (r->pool, strfilt, filt->nam, "=", filt->val, NULL);
} else {
strfilt = AP_STRCAT (r->pool, filt->nam, "=", filt->val, 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, "%7c", NULL);
first = 1;
while (allo) {
if (!first) strallows = AP_STRCAT (r->pool, strallows, "%26", NULL); else first = 0;
if (strallows != NULL) {
strallows = AP_STRCAT (r->pool, strallows, allo->nam, "%3d", allo->val, NULL);
} else {
strallows = AP_STRCAT (r->pool, allo->nam, "%3d", allo->val, 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;
unsigned int i;
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", fname);
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_SIZE_T nbytes;
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);
}
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_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_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;
while (*s) {
char *n, *v;
n = s;
while (*s && *s != '=') s++;
if (!*s) break;
*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 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) {
fprintf (stderr, " (%s=%s) ", filter->nam, filter->val);
} else {
teqlog (r, 3, " (%s=%s) ", filter->nam, filter->val);
}
filter = filter->next;
}
if (r == NULL) fprintf (stderr, "\n");
}
static int uri_match (const char *uri1, const char *uri2) {
const char *end_uri2 = uri2 + strlen (uri2);
const char *u1 = uri1, *u2 = uri2;
while (u2 < end_uri2) {
if (*u2 == '/') {
if (*u1 != '/') return 0;
while (*u2 == '/') ++u2;
while (*u1 == '/') ++u1;
} else {
if (*u1++ != *u2++) return 0;
}
}
if (u2 [-1] != '/' && *u1 != '\0' && *u1 != '/') return 0;
return u1 - uri1;
}
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->servname = NULL;
dconf->session = NULL;
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 *baseconf = (tequila_perdir_conf *) base_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->servname = overconf->servname;
mergeconf->session = overconf->session;
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->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->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;
int i;
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;
void *data;
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;
char *uripath;
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;
int nbytes;
char *keydeb;
char *args = NULL;
const char *cookie;
keyfromcookie = 0;
//
// Try URL first
//
args = r->args;
teqlog (r, 4, "getkey: args : %s", args);
if (args && (keydeb = strstr (args, "key="))) {
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; }
teqlog (r, 5, "getkey: No key found");
return NULL;
}
static char *getkeyfromcookie (request_rec *r, const char *cookie) {
tequila_perdir_conf *dconf;
char *s, *c, *h, *value, *cook, *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";
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 int createserverrequest (request_rec *r) {
tequila_perdir_conf *dconf;
tequila_global_conf *gconf;
char *teqserver, *proto, *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
teqserver = gconf->teqserver;
if (teqserver == NULL) teqserver = defaultteqserver;
tequrl = AP_STRCAT (r->pool, "https://", teqserver, "/cgi-bin/tequila/auth", NULL);
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);
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);
}
}
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);
close (sock);
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_PERMANENTLY;
}
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) {
close (sock);
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 (!org || !user || !host || !path) {
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;
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;
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);
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
"<html><head>\n<title>", "Tequila error",
"</title>\n</head><body>\n<h1>", "Tequila error", "</h1>\n",
NULL);
ap_rvputs (r, msg, NULL);
ap_rvputs (r, ap_psignature ("<hr>\n", r), NULL);
ap_rvputs (r, "</body></html>\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);
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;
GENERAL_NAME *name;
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 */

Event Timeline