From: megacz Date: Sun, 17 Jun 2007 02:56:04 +0000 (+0000) Subject: massive UMBC code import -- this patch does a lot of stuff X-Git-Url: https://git.hcoop.net/hcoop/zz_old/modwaklog.git/commitdiff_plain/3f4fda20a1e53ffea96022b4e32f25b1e117429e massive UMBC code import -- this patch does a lot of stuff git-svn-id: https://modwaklog.svn.sourceforge.net/svnroot/modwaklog/trunk/modwaklog@47 0d961d1b-a432-0410-8fea-cc29f225fe07 --- diff --git a/mod_waklog.c b/mod_waklog.c dissimilarity index 70% index 0271956..cc18e54 100644 --- a/mod_waklog.c +++ b/mod_waklog.c @@ -1,923 +1,1636 @@ -#define _LARGEFILE64_SOURCE -#define _GNU_SOURCE - -#include "httpd.h" -#include "http_config.h" -#include "http_log.h" -#include "http_protocol.h" -#include "http_request.h" -#include "http_core.h" - -#ifdef sun -#include -#elif linux -#define use_pthreads -#include -#include -#include -#include -#else -#error "make sure you include the right stuff here" -#endif - -#ifndef MAXNAMELEN -#define MAXNAMELEN 1024 -#endif - -/********************* APACHE1 ******************************************************************************/ -#ifndef STANDARD20_MODULE_STUFF -#include "ap_config.h" -#if defined(sun) -#include -#endif /* sun */ -#include -#define MK_POOL pool -#define MK_TABLE_GET ap_table_get -#define MK_TABLE_SET ap_table_set -#define command(name, func, var, type, usage) \ - { name, func, \ - NULL , \ - RSRC_CONF | ACCESS_CONF , type, usage } -#define command(name, func, var, type, usage) \ - { name, func, \ - (void*)XtOffsetOf(waklog_commands, var), \ - OR_AUTHCFG | RSRC_CONF, type, usage } - -/********************* APACHE2 ******************************************************************************/ -#else -#include -#include -//#include -#define ap_pcalloc apr_pcalloc -#define ap_pdupstr apr_pdupstr -#define ap_pstrdup apr_pstrdup - -module AP_MODULE_DECLARE_DATA waklog_module; - -#define MK_POOL apr_pool_t -#define MK_TABLE_GET apr_table_get -#define MK_TABLE_SET apr_table_set -#include "unixd.h" -extern unixd_config_rec unixd_config; -#define ap_user_id unixd_config.user_id -#define ap_group_id unixd_config.group_id -#define ap_user_name unixd_config.user_name -#define command(name, func, var, type, usage) \ - AP_INIT_ ## type (name, (void*) func, \ - NULL, \ - RSRC_CONF | ACCESS_CONF, usage) -typedef struct -{ - int dummy; -} -child_info; - -const char *userdata_key = "waklog_init"; -#endif /* STANDARD20_MODULE_STUFF */ -/**************************************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include - -#define TKT_LIFE ( 12 * 60 * 60 ) -#define SLEEP_TIME ( TKT_LIFE - 5*60 ) - -#define WAKLOG_ON 1 -#define WAKLOG_OFF 2 -#define WAKLOG_UNSET 0 - -#ifdef WAKLOG_DEBUG -#undef APLOG_DEBUG -#define APLOG_DEBUG APLOG_ERR -#endif - -#ifndef CELL_IN_PRINCIPAL -int cell_in_principal = 1; -#else -int cell_in_principal = 0; -#endif - -/* this is used to turn off pag generation for the backround worker child during startup */ -int pag_for_children = 1; - -typedef struct -{ - int forked; - int configured; - int protect; - int usertokens; - char *keytab; - char *principal; - char *default_principal; - char *default_keytab; - char *afs_cell; - char *path; - MK_POOL *p; -} -waklog_config; - -typedef struct -{ - struct ktc_token token; - char clientprincipal[MAXNAMELEN]; - krb5_context kcontext; - krb5_ccache ccache; - struct ktc_principal server; - struct ktc_principal client; - int pr_init; -} waklog_child_config; - -waklog_child_config child; - -struct tokencache_ent { - char clientprincipal[MAXNAMELEN]; - struct ktc_token token; - struct ktc_principal client; - struct ktc_principal server; - time_t lastused; - int persist; -}; - -#define SHARED_TABLE_SIZE 512 - -struct sharedspace_s { - int renewcount; - struct tokencache_ent sharedtokens[SHARED_TABLE_SIZE]; -}; - -struct sharedspace_s *sharedspace = NULL; - -struct renew_ent { - char *keytab; - char *principal; - int lastrenewed; -}; - -#ifdef use_pthreads -pthread_rwlock_t *sharedlock = NULL; -#else -rwlock_t *sharedlock = NULL; -#endif - -struct renew_ent renewtable[SHARED_TABLE_SIZE]; - -int renewcount = 0; - -module waklog_module; - - -#define getModConfig(P, X) P = (waklog_config *) ap_get_module_config( (X)->module_config, &waklog_module ); - -#include - -#if defined(sun) -#include -#endif /* sun */ -#include -#include -#include -#include -#include -#include - -#define KEYTAB "/etc/keytab.wwwserver" -#define PRINCIPAL "someplacewwwserver" -#define AFS_CELL "someplace.edu" - -/* If there's an error, retry more aggressively */ -#define ERR_SLEEP_TIME 5*60 - - -#define K5PATH "FILE:/tmp/waklog.creds.k5" - -static void -log_error(const char *file, int line, int level, int status, - const server_rec *s, const char *fmt, ...) -{ - char errstr[1024]; - va_list ap; - - va_start(ap, fmt); - vsnprintf(errstr, sizeof(errstr), fmt, ap); - va_end(ap); - -#ifdef STANDARD20_MODULE_STUFF - ap_log_error(file, line, level | APLOG_NOERRNO, status, s, "%s", errstr); -#else - ap_log_error(file, line, level | APLOG_NOERRNO, s, "%s", errstr); -#endif - -} - - static void * -waklog_create_server_config( MK_POOL *p, server_rec *s ) -{ - waklog_config *cfg; - - cfg = (waklog_config *)ap_pcalloc( p, sizeof( waklog_config )); - cfg->p = p; - cfg->forked = 0; - cfg->configured = 0; - cfg->protect = 0; - cfg->keytab = KEYTAB; - cfg->principal = PRINCIPAL; - cfg->afs_cell = AFS_CELL; - - log_error( APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: server config created." ); - - return( cfg ); -} - - - static const char * -set_waklog_protect( cmd_parms *params, void *mconfig, int flag ) -{ - waklog_config *cfg; - - getModConfig(cfg, params->server ); - - cfg->protect = flag; - cfg->configured = 1; - log_error( APLOG_MARK, APLOG_DEBUG, 0, params->server, "mod_waklog: waklog_protect set" ); - return( NULL ); -} - - - static const char * -set_waklog_keytab( cmd_parms *params, void *mconfig, char *file ) -{ - waklog_config *cfg; - - getModConfig(cfg, params->server ); - - log_error( APLOG_MARK, APLOG_INFO, 0, params->server, - "mod_waklog: will use keytab: %s", file ); - - cfg->keytab = ap_pstrdup ( params->pool, file ); - cfg->configured = 1; - return( NULL ); -} - - - static const char * -set_waklog_use_principal( cmd_parms *params, void *mconfig, char *file ) -{ - waklog_config *cfg; - - getModConfig(cfg, params->server ); - - log_error( APLOG_MARK, APLOG_INFO, 0, params->server, - "mod_waklog: will use principal: %s", file ); - - cfg->principal = ap_pstrdup ( params->pool, file ); - cfg->configured = 1; - return( NULL ); -} - - - static const char * -set_waklog_use_afs_cell( cmd_parms *params, void *mconfig, char *file ) -{ - waklog_config *cfg; - - getModConfig(cfg, params->server ); - - log_error( APLOG_MARK, APLOG_INFO, 0, params->server, - "mod_waklog: will use afs_cell: %s", file ); - - cfg->afs_cell = ap_pstrdup( params->pool, file ); - cfg->configured = 1; - return( NULL ); -} - - - static void -#ifdef STANDARD20_MODULE_STUFF -waklog_child_init(MK_POOL *p, server_rec *s) -#else -waklog_child_init(server_rec *s, MK_POOL *p) -#endif -{ - - log_error( APLOG_MARK, APLOG_DEBUG, 0, s, - "mod_waklog: child_init called" ); - - memset( &child.token, 0, sizeof( struct ktc_token ) ); - - setpag(); - - log_error( APLOG_MARK, APLOG_DEBUG, 0, s, - "mod_waklog: child_init returned" ); - - return; -} - -typedef struct { - int wak_protect; - char *wak_keytab; - char *wak_ktprinc; - char *wak_afscell; -} waklog_commands; - -command_rec waklog_cmds[ ] = -{ - command("WaklogProtected", set_waklog_protect, wak_protect, FLAG, "enable waklog on a location or directory basis"), - - command("WaklogKeytab", set_waklog_keytab, wak_keytab, TAKE1, "Use the supplied keytab rather than the default"), - - command("WaklogUseKeytabPrincipal", set_waklog_use_principal, wak_ktprinc, TAKE1, "Use the supplied keytab principal rather than the default"), - - command("WaklogUseAFSCell", set_waklog_use_afs_cell, wak_afscell, TAKE1, "Use the supplied AFS cell rather than the default"), - - { NULL } -}; - - - static int -token_cleanup( void *data ) -{ - request_rec *r = (request_rec *)data; - - if ( child.token.ticketLen ) { - memset( &child.token, 0, sizeof( struct ktc_token ) ); - - ktc_ForgetAllTokens(); - - log_error( APLOG_MARK, APLOG_DEBUG, 0, r->server, - "mod_waklog: ktc_ForgetAllTokens succeeded: pid: %d", getpid() ); - } - return 0; -} - - - static int -waklog_kinit( server_rec *s ) -{ - krb5_error_code kerror = 0; - krb5_context kcontext = NULL; - krb5_principal kprinc = NULL; - krb5_get_init_creds_opt kopts; - krb5_creds v5creds; - krb5_ccache kccache = NULL; - krb5_keytab keytab = NULL; - char ktbuf[ MAX_KEYTAB_NAME_LEN + 1 ]; - int i; - waklog_config *cfg; - - log_error( APLOG_MARK, APLOG_DEBUG, 0, s, - "mod_waklog: waklog_kinit called: pid: %d", getpid() ); - - getModConfig(cfg, s); - - if (( kerror = krb5_init_context( &kcontext ))) { - log_error( APLOG_MARK, APLOG_ERR, 0, s, - "mod_waklog: %s", (char *)error_message( kerror )); - - goto cleanup; - } - - /* use the path */ - if (( kerror = krb5_cc_resolve( kcontext, K5PATH, &kccache )) != 0 ) { - log_error( APLOG_MARK, APLOG_ERR, 0, s, - "mod_waklog: %s", (char *)error_message( kerror )); - - goto cleanup; - } - - log_error( APLOG_MARK, APLOG_DEBUG, 0, s, - "mod_waklog: principal: %s", cfg->principal ); - - if (( kerror = krb5_parse_name( kcontext, cfg->principal, &kprinc ))) { - log_error( APLOG_MARK, APLOG_ERR, 0, s, - "mod_waklog: %s", (char *)error_message( kerror )); - - goto cleanup; - } - - krb5_get_init_creds_opt_init( &kopts ); - krb5_get_init_creds_opt_set_tkt_life( &kopts, TKT_LIFE ); - krb5_get_init_creds_opt_set_renew_life( &kopts, 0 ); - krb5_get_init_creds_opt_set_forwardable( &kopts, 1 ); - krb5_get_init_creds_opt_set_proxiable( &kopts, 0 ); - - /* keytab from config */ - strncpy( ktbuf, cfg->keytab, sizeof( ktbuf ) - 1 ); - - log_error( APLOG_MARK, APLOG_DEBUG, 0, s, - "mod_waklog: waklog_kinit using: %s", ktbuf ); - - if (( kerror = krb5_kt_resolve( kcontext, ktbuf, &keytab )) != 0 ) { - log_error( APLOG_MARK, APLOG_ERR, 0, s, - "mod_waklog:krb5_kt_resolve %s", (char *)error_message( kerror )); - - goto cleanup; - } - - memset( (char *)&v5creds, 0, sizeof(v5creds)); - - /* get the krbtgt */ - if (( kerror = krb5_get_init_creds_keytab( kcontext, &v5creds, - kprinc, keytab, 0, NULL, &kopts ))) { - - log_error( APLOG_MARK, APLOG_ERR, 0, s, - "mod_waklog:krb5_get_init_creds_keytab %s", (char *)error_message( kerror )); - - goto cleanup; - } - - if (( kerror = krb5_cc_initialize( kcontext, kccache, kprinc )) != 0 ) { - log_error( APLOG_MARK, APLOG_ERR, 0, s, - "mod_waklog:krb5_cc_initialize %s", (char *)error_message( kerror )); - - goto cleanup; - } - - kerror = krb5_cc_store_cred( kcontext, kccache, &v5creds ); - krb5_free_cred_contents( kcontext, &v5creds ); - if ( kerror != 0 ) { - log_error( APLOG_MARK, APLOG_ERR, 0, s, - "mod_waklog: %s", (char *)error_message( kerror )); - - goto cleanup; - } - - log_error( APLOG_MARK, APLOG_DEBUG, 0, s, - "mod_waklog: waklog_kinit success" ); - -cleanup: - if ( keytab ) - (void)krb5_kt_close( kcontext, keytab ); - if ( kprinc ) - krb5_free_principal( kcontext, kprinc ); - if ( kccache ) - krb5_cc_close( kcontext, kccache ); - if ( kcontext ) - krb5_free_context( kcontext ); - - log_error( APLOG_MARK, APLOG_DEBUG, 0, s, - "mod_waklog: waklog_kinit: exiting" ); - - return( kerror ); -} - - - static void -waklog_aklog( request_rec *r ) -{ - int rc; - char buf[ MAXKTCTICKETLEN ]; - const char *k5path = NULL; - krb5_error_code kerror; - krb5_context kcontext = NULL; - krb5_creds increds; - krb5_creds *v5credsp = NULL; - krb5_ccache kccache = NULL; - struct ktc_principal server = { "afs", "", "" }; - struct ktc_principal client; - struct ktc_token token; - waklog_config *cfg; - int buflen; - - k5path = MK_TABLE_GET( r->subprocess_env, "KRB5CCNAME" ); - - log_error( APLOG_MARK, APLOG_INFO, 0, r->server, - "mod_waklog: waklog_aklog called: k5path: %s", k5path ); - - if ( k5path == NULL ) { - log_error( APLOG_MARK, APLOG_DEBUG, 0, r->server, - "mod_waklog: waklog_aklog giving up" ); - goto cleanup; - } - - /* - ** Get/build creds from file/tgs, then see if we need to SetToken - */ - - if (( kerror = krb5_init_context( &kcontext ))) { - /* Authentication Required ( kerberos error ) */ - log_error( APLOG_MARK, APLOG_ERR, 0, r->server, - (char *)error_message( kerror )); - - goto cleanup; - } - - memset( (char *)&increds, 0, sizeof(increds)); - - getModConfig(cfg, r->server ); - - /* afs/ or afs */ - strncpy( buf, "afs", sizeof( buf ) - 1 ); - if ( strcmp( cfg->afs_cell, AFS_CELL ) ) { - strncat( buf, "/" , sizeof( buf ) - strlen( buf ) - 1 ); - strncat( buf, cfg->afs_cell, sizeof( buf ) - strlen( buf ) - 1 ); - } - - /* set server part */ - if (( kerror = krb5_parse_name( kcontext, buf, &increds.server ))) { - log_error( APLOG_MARK, APLOG_ERR, 0, r->server, - (char *)error_message( kerror )); - - goto cleanup; - } - - if (( kerror = krb5_cc_resolve( kcontext, k5path, &kccache )) != 0 ) { - log_error( APLOG_MARK, APLOG_ERR, 0, r->server, - (char *)error_message( kerror )); - - goto cleanup; - } - - /* set client part */ - krb5_cc_get_principal( kcontext, kccache, &increds.client ); - - increds.times.endtime = 0; - /* Ask for DES since that is what V4 understands */ - increds.keyblock.enctype = ENCTYPE_DES_CBC_CRC; - - /* get the V5 credentials */ - if (( kerror = krb5_get_credentials( kcontext, 0, kccache, - &increds, &v5credsp ) ) ) { - log_error( APLOG_MARK, APLOG_ERR, 0, r->server, - "mod_waklog: krb5_get_credentials: %s", error_message( kerror )); - goto cleanup; - } - - /* don't overflow */ - if ( v5credsp->ticket.length >= MAXKTCTICKETLEN ) { /* from krb524d.c */ - log_error( APLOG_MARK, APLOG_ERR, 0, r->server, - "mod_waklog: ticket size (%d) too big to fake", v5credsp->ticket.length ); - goto cleanup; - } - - /* assemble the token */ - memset( &token, 0, sizeof( struct ktc_token ) ); - - token.startTime = v5credsp->times.starttime ? v5credsp->times.starttime : v5credsp->times.authtime; - token.endTime = v5credsp->times.endtime; - memmove( &token.sessionKey, v5credsp->keyblock.contents, v5credsp->keyblock.length ); - token.kvno = RXKAD_TKT_TYPE_KERBEROS_V5; - token.ticketLen = v5credsp->ticket.length; - memmove( token.ticket, v5credsp->ticket.data, token.ticketLen ); - - /* make sure we have to do this */ - if ( child.token.kvno != token.kvno || - child.token.ticketLen != token.ticketLen || - (memcmp( &child.token.sessionKey, &token.sessionKey, - sizeof( token.sessionKey ) )) || - (memcmp( child.token.ticket, token.ticket, token.ticketLen )) ) { - - log_error( APLOG_MARK, APLOG_DEBUG, 0, r->server, - "mod_waklog: client: %s", buf ); - - /* build the name */ - memmove( buf, v5credsp->client->data[0].data, - min( v5credsp->client->data[0].length, MAXKTCNAMELEN - 1 ) ); - buf[ v5credsp->client->data[0].length ] = '\0'; - if ( v5credsp->client->length > 1 ) { - strncat( buf, ".", sizeof( buf ) - strlen( buf ) - 1 ); - buflen = strlen( buf ); - memmove( buf + buflen, v5credsp->client->data[1].data, - min( v5credsp->client->data[1].length, MAXKTCNAMELEN - strlen( buf ) - 1 ) ); - buf[ buflen + v5credsp->client->data[1].length ] = '\0'; - } - - /* assemble the client */ - strncpy( client.name, buf, sizeof( client.name ) - 1 ); - strncpy( client.instance, "", sizeof( client.instance) - 1 ); - memmove( buf, v5credsp->client->realm.data, - min( v5credsp->client->realm.length, MAXKTCNAMELEN - 1 ) ); - buf[ v5credsp->client->realm.length ] = '\0'; - strncpy( client.cell, buf, sizeof( client.cell ) - 1 ); - - /* assemble the server's cell */ - strncpy( server.cell, cfg->afs_cell , sizeof( server.cell ) - 1 ); - - log_error( APLOG_MARK, APLOG_DEBUG, 0, r->server, - "mod_waklog: server: name=%s, instance=%s, cell=%s", - server.name, server.instance, server.cell ); - - log_error( APLOG_MARK, APLOG_DEBUG, 0, r->server, - "mod_waklog: client: name=%s, instance=%s, cell=%s", - client.name, client.instance, client.cell ); - - /* use the path */ - - /* rumor: we have to do this for AIX 4.1.4 with AFS 3.4+ */ - write( 2, "", 0 ); - - if ( ( rc = ktc_SetToken( &server, &token, &client, 0 ) ) ) { - log_error( APLOG_MARK, APLOG_ERR, 0, r->server, - "mod_waklog: settoken returned %d", rc ); - goto cleanup; - } - - /* save this */ - memmove( &child.token, &token, sizeof( struct ktc_token ) ); - - /* we'll need to unlog when this connection is done. */ -#ifndef STANDARD20_MODULE_STUFF - ap_register_cleanup( r->pool, (void *)r, token_cleanup, ap_null_cleanup ); -#else - /* FIXME!!!! */ -#endif - } - -cleanup: - if ( v5credsp ) - krb5_free_cred_contents( kcontext, v5credsp ); - if ( increds.client ) - krb5_free_principal( kcontext, increds.client ); - if ( increds.server ) - krb5_free_principal( kcontext, increds.server ); - if ( kccache ) - krb5_cc_close( kcontext, kccache ); - if ( kcontext ) - krb5_free_context( kcontext ); - - log_error( APLOG_MARK, APLOG_DEBUG, 0, r->server, - "mod_waklog: finished with waklog_aklog" ); - - return; - -} - - static int -waklog_child_routine( void *s, child_info *pinfo ) -{ - if ( !getuid() ) { - log_error( APLOG_MARK, APLOG_DEBUG, 0, s, - "mod_waklog: waklog_child_routine called as root" ); - - /* this was causing the credential file to get owned by root */ -#ifdef STANDARD20_MODULE_STUFF - setgid(ap_group_id); - setuid(ap_user_id); -#endif - } - - while( 1 ) { - waklog_kinit( s ); - log_error( APLOG_MARK, APLOG_DEBUG, 0, s, - "mod_waklog: child_routine sleeping" ); - sleep( SLEEP_TIME ); - log_error( APLOG_MARK, APLOG_DEBUG, 0, s, - "mod_waklog: slept, calling waklog_kinit" ); - } - -} - -#ifdef STANDARD20_MODULE_STUFF -static int -waklog_init_handler(apr_pool_t *p, apr_pool_t *plog, - apr_pool_t *ptemp, server_rec *s) -{ - int rv; - extern char *version; - apr_proc_t *proc; - waklog_config *cfg; - void *data; - - getModConfig(cfg, s); - - /* initialize_module() will be called twice, and if it's a DSO - * then all static data from the first call will be lost. Only - * set up our static data on the second call. - * see http://issues.apache.org/bugzilla/show_bug.cgi?id=37519 */ - apr_pool_userdata_get(&data, userdata_key, s->process->pool); - - if (!data) { - apr_pool_userdata_set((const void *)1, userdata_key, - apr_pool_cleanup_null, s->process->pool); - } else { - log_error( APLOG_MARK, APLOG_INFO, 0, s, - "mod_waklog: version %s initialized.", version ); - - proc = (apr_proc_t *)ap_pcalloc( s->process->pool, sizeof(apr_proc_t)); - - rv = apr_proc_fork(proc, s->process->pool); - - if (rv == APR_INCHILD) { - waklog_child_routine(s, NULL); - } else { - apr_pool_note_subprocess(s->process->pool, proc, APR_KILL_ALWAYS); - } - /* parent and child */ - cfg->forked = proc->pid; - } - return 0; -} -#else - static void -waklog_init( server_rec *s, MK_POOL *p ) -{ - extern char *version; - int pid; - - log_error( APLOG_MARK, APLOG_INFO, 0, s, - "mod_waklog: version %s initialized.", version ); - - pid = ap_bspawn_child( p, waklog_child_routine, s, kill_always, - NULL, NULL, NULL ); - - log_error( APLOG_MARK, APLOG_DEBUG, 0, s, - "mod_waklog: ap_bspawn_child: %d.", pid ); -} -#endif - - static int -waklog_phase0( request_rec *r ) -{ - waklog_config *cfg; - - log_error( APLOG_MARK, APLOG_DEBUG, 0, r->server, - "mod_waklog: phase0 called" ); - - getModConfig(cfg, r->server ); - - log_error( APLOG_MARK, APLOG_DEBUG, 0, r->server, - "mod_waklog: phase0, checking cfg->protect" ); - if ( !cfg->protect ) { - log_error( APLOG_MARK, APLOG_DEBUG, 0, r->server, - "mod_waklog: phase0 declining" ); - return( DECLINED ); - } - - log_error( APLOG_MARK, APLOG_DEBUG, 0, r->server, - "mod_waklog: phase0, NOT setting environment variable" ); - /* set our environment variable */ - apr_table_set( r->subprocess_env, "KRB5CCNAME", K5PATH ); - - log_error( APLOG_MARK, APLOG_DEBUG, 0, r->server, - "mod_waklog: phase0, checking child.token.ticketLen" ); - /* do this only if we are still unauthenticated */ - if ( !child.token.ticketLen ) { - - log_error( APLOG_MARK, APLOG_DEBUG, 0, r->server, - "mod_waklog: phase0, calling waklog_aklog" ); - /* stuff the credentials into the kernel */ - waklog_aklog( r ); - } - - log_error( APLOG_MARK, APLOG_DEBUG, 0, r->server, - "mod_waklog: phase0 returning" ); - return DECLINED; -} - - - static int -waklog_phase7( request_rec *r ) -{ - waklog_config *cfg; - - log_error( APLOG_MARK, APLOG_DEBUG, 0, r->server, - "mod_waklog: phase7 called" ); - - getModConfig(cfg, r->server ); - - if ( !cfg->protect ) { - return( DECLINED ); - } - - /* stuff the credentials into the kernel */ - - log_error( APLOG_MARK, APLOG_DEBUG, 0, r->server, - "mod_waklog: phase7, calling waklog_aklog" ); - waklog_aklog( r ); - - log_error( APLOG_MARK, APLOG_DEBUG, 0, r->server, - "mod_waklog: phase7 returning" ); - - return DECLINED; -} - - -static -#ifdef STANDARD20_MODULE_STUFF - int -#else - void -#endif -waklog_new_connection (conn_rec * c -#ifdef STANDARD20_MODULE_STUFF - , void *dummy -#endif - ) -{ - - waklog_commands *cfg; - - log_error (APLOG_MARK, APLOG_DEBUG, 0, c->base_server, - "mod_waklog: new_connection called: pid: %d", getpid ()); - /* - getModConfig(cfg, c->base_server); - - if ( cfg->default_principal ) { - log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "mod_waklog: new conn setting default user %s", - cfg->default_principal); - set_auth( c->base_server, NULL, 0, cfg->default_principal, cfg->default_keytab, 0); - } - */ - - return -#ifdef STANDARD20_MODULE_STUFF - 0 -#endif - ; -} - - -/* -** Here's a quick explaination for phase0 and phase2: -** Apache does a stat() on the path between phase0 and -** phase2, and must by ACLed rl to succeed. So, at -** phase0 we acquire credentials for umweb:servers from -** a keytab, and at phase2 we must ensure we remove them. -** -** Failure to "unlog" would be a security risk. -*/ -static int -waklog_phase2 (request_rec * r) -{ - - log_error (APLOG_MARK, APLOG_DEBUG, 0, r->server, - "mod_waklog: phase2 called"); - - if (child.token.ticketLen) - { - memset (&child.token, 0, sizeof (struct ktc_token)); - - ktc_ForgetAllTokens (); - - log_error (APLOG_MARK, APLOG_DEBUG, 0, r->server, - "mod_waklog: ktc_ForgetAllTokens succeeded: pid: %d", - getpid ()); - } - - log_error (APLOG_MARK, APLOG_DEBUG, 0, r->server, - "mod_waklog: phase2 returning"); - - return DECLINED; -} - -#ifndef STANDARD20_MODULE_STUFF -module MODULE_VAR_EXPORT waklog_module = { - STANDARD_MODULE_STUFF, - waklog_init, /* module initializer */ -#if 0 - waklog_create_dir_config, /* create per-dir config structures */ -#else /* 0 */ - NULL, /* create per-dir config structures */ -#endif /* 0 */ - NULL, /* merge per-dir config structures */ - waklog_create_server_config, /* create per-server config structures */ - NULL, /* merge per-server config structures */ - waklog_cmds, /* table of config file commands */ - NULL, /* [#8] MIME-typed-dispatched handlers */ - NULL, /* [#1] URI to filename translation */ - NULL, /* [#4] validate user id from request */ - NULL, /* [#5] check if the user is ok _here_ */ - NULL, /* [#3] check access by host address */ - NULL, /* [#6] determine MIME type */ - waklog_phase7, /* [#7] pre-run fixups */ - NULL, /* [#9] log a transaction */ - waklog_phase2, /* [#2] header parser */ - waklog_child_init, /* child_init */ - NULL, /* child_exit */ - waklog_phase0 /* [#0] post read-request */ -#ifdef EAPI - ,NULL, /* EAPI: add_module */ - NULL, /* EAPI: remove_module */ - NULL, /* EAPI: rewrite_command */ - waklog_new_connection /* EAPI: new_connection */ -#endif -}; -#else -static void -waklog_register_hooks (apr_pool_t * p) -{ - ap_hook_header_parser (waklog_phase2, NULL, NULL, APR_HOOK_FIRST); - ap_hook_fixups (waklog_phase7, NULL, NULL, APR_HOOK_FIRST); - ap_hook_child_init (waklog_child_init, NULL, NULL, APR_HOOK_FIRST); - ap_hook_post_read_request (waklog_phase0, NULL, NULL, APR_HOOK_FIRST); - ap_hook_pre_connection (waklog_new_connection, NULL, NULL, APR_HOOK_FIRST); - ap_hook_post_config (waklog_init_handler, NULL, NULL, APR_HOOK_MIDDLE); -} - - -module AP_MODULE_DECLARE_DATA waklog_module = -{ - STANDARD20_MODULE_STUFF, - NULL, /* create per-dir conf structures */ - NULL, /* merge per-dir conf structures */ - waklog_create_server_config, /* create per-server conf structures */ - NULL, /* merge per-server conf structures */ - waklog_cmds, /* table of configuration directives */ - waklog_register_hooks /* register hooks */ -}; -#endif - +#define _LARGEFILE64_SOURCE +#define _GNU_SOURCE + +#include "httpd.h" +#include "http_config.h" +#include "http_log.h" +#include "http_protocol.h" +#include "http_request.h" +#include "http_core.h" + +#ifdef sun +#include +#elif linux +#define use_pthreads +#include +#include +#include +#include +#else +#error "make sure you include the right stuff here" +#endif + +#ifndef MAXNAMELEN +#define MAXNAMELEN 1024 +#endif + +/********************* APACHE1 ******************************************************************************/ +#ifndef STANDARD20_MODULE_STUFF +#include "ap_config.h" +#if defined(sun) +#include +#endif /* sun */ +#include +#define MK_POOL pool +#define MK_TABLE_GET ap_table_get +#define MK_TABLE_SET ap_table_set +#define command(name, func, var, type, usage) \ + { name, func, \ + NULL , \ + RSRC_CONF | ACCESS_CONF , type, usage } + +/********************* APACHE2 ******************************************************************************/ +#else +#include +#include +//#include +#define ap_pcalloc apr_pcalloc +#define ap_pdupstr apr_pdupstr +#define ap_pstrdup apr_pstrdup + +module AP_MODULE_DECLARE_DATA waklog_module; + +#define MK_POOL apr_pool_t +#define MK_TABLE_GET apr_table_get +#define MK_TABLE_SET apr_table_set +#include "unixd.h" +extern unixd_config_rec unixd_config; +#define ap_user_id unixd_config.user_id +#define ap_group_id unixd_config.group_id +#define ap_user_name unixd_config.user_name +#define command(name, func, var, type, usage) \ + AP_INIT_ ## type (name, (void*) func, \ + NULL, \ + RSRC_CONF | ACCESS_CONF, usage) +typedef struct +{ + int dummy; +} +child_info; + +const char *userdata_key = "waklog_init"; +#endif /* STANDARD20_MODULE_STUFF */ +/**************************************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#define TKT_LIFE ( 12 * 60 * 60 ) +#define SLEEP_TIME ( TKT_LIFE - 5*60 ) + +#define WAKLOG_ON 1 +#define WAKLOG_OFF 2 +#define WAKLOG_UNSET 0 + +#ifdef WAKLOG_DEBUG +#undef APLOG_DEBUG +#define APLOG_DEBUG APLOG_ERR +#endif + +#ifndef CELL_IN_PRINCIPAL +int cell_in_principal = 1; +#else +int cell_in_principal = 0; +#endif + +/* this is used to turn off pag generation for the backround worker child during startup */ +int pag_for_children = 1; + +typedef struct +{ + int forked; + int configured; + int protect; + int usertokens; + char *keytab; + char *principal; + char *default_principal; + char *default_keytab; + char *afs_cell; + char *path; + MK_POOL *p; +} +waklog_config; + +typedef struct +{ + struct ktc_token token; + char clientprincipal[MAXNAMELEN]; + krb5_context kcontext; + krb5_ccache ccache; + struct ktc_principal server; + struct ktc_principal client; + int pr_init; +} waklog_child_config; + +waklog_child_config child; + +struct tokencache_ent { + char clientprincipal[MAXNAMELEN]; + struct ktc_token token; + struct ktc_principal client; + struct ktc_principal server; + time_t lastused; + int persist; +}; + +#define SHARED_TABLE_SIZE 512 + +struct sharedspace_s { + int renewcount; + struct tokencache_ent sharedtokens[SHARED_TABLE_SIZE]; +}; + +struct sharedspace_s *sharedspace = NULL; + +struct renew_ent { + char *keytab; + char *principal; + int lastrenewed; +}; + +#ifdef use_pthreads +pthread_rwlock_t *sharedlock = NULL; +#else +rwlock_t *sharedlock = NULL; +#endif + +struct renew_ent renewtable[SHARED_TABLE_SIZE]; + +int renewcount = 0; + +module waklog_module; + + +#define getModConfig(P, X) P = (waklog_config *) ap_get_module_config( (X)->module_config, &waklog_module ); + +#include + +#if defined(sun) +#include +#endif /* sun */ +#include +#include +#include +#include +#include +#include + +#define KEYTAB "/etc/keytab.wwwserver" +#define PRINCIPAL "someplacewwwserver" +#define AFS_CELL "someplace.edu" + +/* If there's an error, retry more aggressively */ +#define ERR_SLEEP_TIME 5*60 + + +#define K5PATH "FILE:/tmp/waklog.creds.k5" + +static void +log_error (const char *file, int line, int level, int status, + const server_rec * s, const char *fmt, ...) +{ + char errstr[4096]; + va_list ap; + + va_start (ap, fmt); + vsnprintf (errstr, 1024, fmt, ap); + va_end (ap); + +#ifdef STANDARD20_MODULE_STUFF + ap_log_error (file, line, level | APLOG_NOERRNO, status, s, "(%d) %s", getpid(), errstr); +#else + ap_log_error (file, line, level | APLOG_NOERRNO, s, "(%d) %s", getpid(), errstr); +#endif + +} + +waklog_config *retrieve_config(request_rec *r) { + + request_rec *my; + waklog_config *cfg; + + if ( r && r->main ) { + my = r->main; + } else if (r) { + my = r; + } else { + return NULL; + } + + if ( my && ( cfg = (waklog_config *) ap_get_module_config(my->per_dir_config, &waklog_module ) ) ) { + return cfg; + } else { + getModConfig (cfg, r->server); + } + + return cfg; + +} + +/* set_auth -- sets the tokens of the current process to this user. + if "self" is set, it divines the user from the current requests' environment. + otherwise, it's gettng it from principal/keytab */ + +int +set_auth ( server_rec *s, request_rec *r, int self, char *principal, char *keytab, int storeonly ) { + + int i; + int usecached = 0; + krb5_error_code kerror = 0; + krb5_principal kprinc = NULL; + krb5_get_init_creds_opt kopts; + krb5_creds v5creds; + krb5_creds increds; + struct ktc_principal server = { "afs", "", "" }; + struct ktc_principal client; + struct ktc_token token; + krb5_creds *v5credsp = NULL; + krb5_keytab krb5kt = NULL; + char buf[MAXNAMELEN]; + waklog_config *cfg; + int rc = 0; + int buflen = 0; + time_t oldest_time = 0; + int oldest; + int stored = -1; + time_t mytime; + int indentical; + + char k5user[MAXNAMELEN]; + char *k5secret; + + memset((char *) &increds, 0, sizeof(increds)); + + /* init some stuff if it ain't already */ + + if ( ! child.kcontext ) { + kerror = krb5_init_context(&child.kcontext); + } + + if ( ( ! child.ccache ) && ( kerror = krb5_cc_resolve(child.kcontext, "MEMORY:tmpcache", &child.ccache) ) ) { + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: can't initialize in-memory credentials cache %d", kerror ); + } + + + log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: set_auth: %d, %s, %s, %d", self, principal ? principal : "NULL", + keytab ? keytab : "NULL", storeonly); + + /* pull the server config record that we care about... */ + + if ( r ) { + cfg = retrieve_config(r); + } else { + log_error (APLOG_MARK, APLOG_DEBUG, 0, s, + "mod_waklog: set_auth using no config" ); + getModConfig (cfg, s); + } + + if ( ! cfg ) { + log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: cfg is %d", cfg ); + } + +#ifdef STANDARD20_MODULE_STUFF + if ( self ) { + /* pull out our principal name and stuff from the environment -- webauth better have sent + through. This here is also where you'd suck stuff out of KRB5CCNAME if we were + using something like Cosign */ + + if ( ! ( r && r->connection && r->user )) { + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: self authentication selected, but no data available"); + return -1; + } + + strncpy(k5user, r->user, sizeof(k5user)); + + /* the other thing we need is someone's password */ + if ( ! ( k5secret = (char *) MK_TABLE_GET( r->notes, "ATTR_PASSWORD" ) ) ) { + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: cant do self auth without a secret"); + return -1; + } + + /* we'll pick this up later after we've checked the cache and current state */ + + } else +#endif + if ( principal ) { + strncpy( k5user, principal, sizeof(k5user)); + } + + mytime = time(0); + + /* see if we should just go ahead and ignore this call, since we already should be set to these + credentials */ + + if ( ! storeonly ) { + +#ifdef use_pthreads + pthread_rwlock_rdlock( sharedlock ); +#else + rw_rdlock( sharedlock ); +#endif + + for ( i = 0; i < SHARED_TABLE_SIZE; ++i ) { + + /* if it's a token for the principal we're looking for, and it hasn't expired yet */ + + if ( ( !strcmp( k5user, + sharedspace->sharedtokens[i].clientprincipal ) ) && + ( sharedspace->sharedtokens[i].token.endTime > mytime ) ) { + + if ( ! memcmp(&child.token, &sharedspace->sharedtokens[i].token, sizeof(child.token) ) ) { + indentical = 1; + } else { + indentical = 0; + } + + /* copy the token out of the cache and into the child object */ + + strcpy(child.clientprincipal, sharedspace->sharedtokens[i].clientprincipal ); + memcpy(&child.token, &sharedspace->sharedtokens[i].token, sizeof(child.token)); + memcpy(&child.server, &sharedspace->sharedtokens[i].server, sizeof(child.server)); + memcpy(&child.client, &sharedspace->sharedtokens[i].client, sizeof(child.client)); + + /* set our last used time thing */ + sharedspace->sharedtokens[i].lastused = mytime; + + usecached = 1; + + break; + + } + + } + + /* release the lock on the token cache */ +#ifdef use_pthreads + pthread_rwlock_unlock( sharedlock ); +#else + rw_unlock( sharedlock ); +#endif + + if ( usecached ) { + /* release the lock on the token cache */ + log_error(APLOG_MARK, APLOG_DEBUG, 0, s, + "mod_waklog: set_auth using shared token %d for %s", i, k5user ); + + } + + /* if this is something that was in the cache, and it's the same as the token we already have stored, + and we weren't calling this just to renew it... */ + + if ( usecached && indentical ) { + log_error (APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: token is identical for %s", k5user ); + return 0; + } + + } + + /* if 'usecached' isn't set, we've got to get our tokens from somewhere... */ + + if (( ! usecached ) && ( k5user )) { + + /* clear out the creds structure */ + memset((void *) &v5creds, 0, sizeof(v5creds)); + + /* create a principal out of our k5user string */ + + if ( kerror = krb5_parse_name (child.kcontext, k5user, &kprinc ) ) { + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: krb5_parse_name %s", (char *) error_message(kerror) ); + goto cleanup; + } + + /* create the credentials options */ + + krb5_get_init_creds_opt_init ( &kopts ); + krb5_get_init_creds_opt_set_tkt_life ( &kopts, TKT_LIFE ); + krb5_get_init_creds_opt_set_renew_life ( &kopts, 0 ); + krb5_get_init_creds_opt_set_forwardable ( &kopts, 0 ); + krb5_get_init_creds_opt_set_proxiable ( &kopts, 0 ); + + if ( keytab ) { + + /* if we've been passed a keytab, we're going to be getting our credentials from it */ + + log_error( APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: using keytab %s", keytab); + + if ( kerror = krb5_kt_resolve(child.kcontext, keytab, &krb5kt ) ) { + log_error( APLOG_MARK, APLOG_ERR, 0, s, + "mod_waklog: krb5_kt_resolve %s", error_message(kerror) ); + goto cleanup; + } + + if ((kerror = krb5_get_init_creds_keytab (child.kcontext, &v5creds, + kprinc, krb5kt, 0, NULL, &kopts ) ) ) { + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: krb5_get_init_creds_keytab %s", + error_message(kerror) ); + goto cleanup; + } + + } else if ( self ) { + + /* if 'self' is set, we're going to use the credentials provided in the credential information */ + /* if you're using CoSign, you'd actually just set the ccache to the KRB5CCNAME credentials */ + /* and skip ahead... Our WebSSO is lame, but has a way for us to snarf the password out of */ + /* the encrypted token for proxy-authentication stuff. We only hand out keys that allow this */ + /* functionality to VERY special people. */ + + if ((kerror = krb5_get_init_creds_password ( child.kcontext, &v5creds, + kprinc, k5secret, NULL, NULL, 0, NULL, &kopts ) ) ) { + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: krb5_get_init_creds_password %s", + error_message(kerror) ); + /* nuke the password so it doesn't end up in core files */ + memset(k5secret, 0, sizeof(k5secret)); + goto cleanup; + } + + memset(k5secret, 0, sizeof(k5secret)); + + } else { + + /* degenerate case -- we don't have enough information to do anything */ + + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: no keytab, no self?"); + kerror = -1; + goto cleanup; + + } + + /* initialize the credentials cache and store the stuff we just got */ + + if ( kerror = krb5_cc_initialize (child.kcontext, child.ccache, kprinc)) { + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: init credentials cache %s", + error_message(kerror)); + goto cleanup; + } + + if ( kerror = krb5_cc_store_cred(child.kcontext, child.ccache, &v5creds) ) { + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: cannot store credentials %s", + error_message(kerror)); + goto cleanup; + } + + krb5_free_cred_contents(child.kcontext, &v5creds); + + if ( kerror ) { + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: store cred %s", error_message(kerror)); + goto cleanup; + } + + log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: kinit ok for %s", k5user ); + + /* now, to the 'aklog' portion of our program. */ + + strncpy( buf, "afs", sizeof(buf) - 1 ); + + if ( cfg->afs_cell && strcmp( cfg->afs_cell, AFS_CELL ) ) { + strncat(buf, "/", sizeof(buf) - strlen(buf) - 1); + strncat(buf, cfg->afs_cell, sizeof(buf) - strlen(buf) - 1); + } else if ( cell_in_principal ) { + strncat(buf, "/", sizeof(buf) - strlen(buf) - 1); + strncat(buf, AFS_CELL, sizeof(buf) - strlen(buf) - 1); + } + + log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: using AFS principal: %s", buf); + + if ((kerror = krb5_parse_name (child.kcontext, buf, &increds.server))) { + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: krb5_parse name %s", error_message(kerror)); + goto cleanup; + } + + if ((kerror = krb5_cc_get_principal(child.kcontext, child.ccache, &increds.client))) { + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: krb5_cc_get_princ %s", error_message(kerror)); + goto cleanup; + } + + log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: retrieved data from ccache for %s", k5user); + + increds.times.endtime = 0; + + increds.keyblock.enctype = ENCTYPE_DES_CBC_CRC; + + if (kerror = krb5_get_credentials (child.kcontext, 0, child.ccache, &increds, &v5credsp )) { + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: krb5_get_credentials: %s", + error_message(kerror)); + goto cleanup; + } + + log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: get_credentials passed for %s", k5user); + + if ( v5credsp->ticket.length >= MAXKTCTICKETLEN ) { + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: ticket size (%d) too big to fake", + v5credsp->ticket.length); + goto cleanup; + } + + memset(&token, 0, sizeof(struct ktc_token)); + + token.startTime = v5credsp->times.starttime ? v5credsp->times.starttime : v5credsp->times.authtime; + token.endTime = v5credsp->times.endtime; + + memmove( &token.sessionKey, v5credsp->keyblock.contents, v5credsp->keyblock.length); + token.kvno = RXKAD_TKT_TYPE_KERBEROS_V5; + token.ticketLen = v5credsp->ticket.length; + memmove( token.ticket, v5credsp->ticket.data, token.ticketLen); + + /* build the name */ + + memmove( buf, v5credsp->client->data[0].data, min(v5credsp->client->data[0].length, + MAXKTCNAMELEN -1 )); + buf[v5credsp->client->data[0].length] = '\0'; + if ( v5credsp->client->length > 1 ) { + strncat(buf, ".", sizeof(buf) - strlen(buf) - 1); + buflen = strlen(buf); + memmove(buf + buflen, v5credsp->client->data[1].data, + min(v5credsp->client->data[1].length, + MAXKTCNAMELEN - strlen(buf) - 1)); + buf[buflen + v5credsp->client->data[1].length] = '\0'; + } + + /* assemble the client */ + strncpy(client.name, buf, sizeof(client.name) - 1 ); + strncpy(client.instance, "", sizeof(client.instance) - 1 ); + memmove(buf, v5credsp->client->realm.data, min(v5credsp->client->realm.length, + MAXKTCNAMELEN - 1)); + buf[v5credsp->client->realm.length] = '\0'; + strncpy(client.cell, buf, sizeof(client.cell)); + + /* assemble the server's cell */ + if ( cfg->afs_cell ) { + strncpy(server.cell, cfg->afs_cell, sizeof(server.cell) - 1); + } else { + strncpy(server.cell, AFS_CELL, sizeof(server.cell) - 1); + } + + log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: preparing to init PTS connection for %s", server.cell); + + /* fill out the AFS ID in the client name */ + /* we've done a pr_Initialize in the child_init -- once, per process. If you try to do it more + * strange things seem to happen. */ + + { + afs_int32 viceId = 0; + + log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: making PTS call to look up %s", client.name); + + if ( ( rc = pr_SNameToId( client.name, &viceId ) ) == 0 ) { + snprintf( client.name, sizeof(client.name), "AFS ID %d", viceId ); + } else { + log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: PTS call returned error %d ", rc); + } + + log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: PTS call returned %s ", client.name); + + } + + log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: server: name %s, instance %s, cell %s", + server.name, server.instance, server.cell ); + + log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: client: name %s, instance %s, cell %s", + client.name, client.instance, client.cell ); + + /* copy the resulting stuff into the child structure */ + + strncpy(child.clientprincipal, k5user, sizeof(child.clientprincipal)); + memcpy(&child.token, &token, sizeof(child.token)); + memcpy(&child.server, &server, sizeof(child.server)); + memcpy(&child.client, &client, sizeof(child.client)); + + /* stuff the resulting token-related stuff into our shared token cache */ + /* note, that anything that was set from a keytab is "persistant", and is immune + * from LRU-aging. This is because nothing but the process that's running as root + * can update these, and it's running every hour or so and renewing these tokens. + * and we don't want them aged out. + */ + + mytime = oldest_time = time(0); + + log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: waiting for shared space for %s ", k5user); + +#ifdef use_pthreads + pthread_rwlock_wrlock(sharedlock); +#else + rw_wrlock(sharedlock); +#endif + + for( i = ( SHARED_TABLE_SIZE - 1 ); i >= 0; i-- ) { + if ( ( sharedspace->sharedtokens[i].lastused <= oldest_time) && + ( sharedspace->sharedtokens[i].persist == 0 ) ) { + oldest = i; + oldest_time = sharedspace->sharedtokens[i].lastused; + } + if ( ! strcmp ( sharedspace->sharedtokens[i].clientprincipal, + child.clientprincipal ) ) { + memcpy(&sharedspace->sharedtokens[i].token, &child.token, sizeof(child.token) ); + memcpy(&sharedspace->sharedtokens[i].client, &child.client, sizeof(child.client) ); + memcpy(&sharedspace->sharedtokens[i].server, &child.server, sizeof(child.server) ); + sharedspace->sharedtokens[i].lastused = mytime; + sharedspace->sharedtokens[i].persist = keytab ? 1 : 0; + stored = i; + break; + } + } + + if ( stored == -1 ) { + memcpy(&sharedspace->sharedtokens[oldest].token, &child.token, sizeof(child.token) ); + memcpy(&sharedspace->sharedtokens[oldest].client, &child.client, sizeof(child.client) ); + memcpy(&sharedspace->sharedtokens[oldest].server, &child.server, sizeof(child.server) ); + strcpy(sharedspace->sharedtokens[oldest].clientprincipal, child.clientprincipal ); + sharedspace->sharedtokens[oldest].lastused = mytime; + sharedspace->sharedtokens[oldest].persist = keytab ? 1 : 0; + stored = oldest; + } + +#ifdef use_pthreads + pthread_rwlock_unlock(sharedlock); +#else + rw_unlock(sharedlock); +#endif + + log_error( APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: token stored in slot %d for %s", stored, + child.clientprincipal ); + + } else if ( ! usecached ) { + log_error( APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: set_auth divergent case"); + } + + if ( storeonly ) { + goto cleanup; + } + + usecachedtoken: + + /* don't ask. Something about AIX. We're leaving it here.*/ + /* write(2, "", 0); */ + + /* we try twice, because sometimes the first one fails. Dunno why, but it always works the second time */ + + if ((rc = ktc_SetToken(&child.server, &child.token, &child.client, 0))) { + log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: settoken returned %s for %s -- trying again", + error_message(rc), k5user); + if ((rc = ktc_SetToken(&child.server, &child.token, &child.client, 0))) { + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: settoken2 returned %s for %s", + error_message(rc), k5user); + goto cleanup; + } + } + + cleanup: + if ( v5credsp ) + krb5_free_cred_contents(child.kcontext, v5credsp); + if ( increds.client ) + krb5_free_principal (child.kcontext, increds.client); + if ( increds.server ) + krb5_free_principal (child.kcontext, increds.server); + if ( krb5kt ) + (void) krb5_kt_close(child.kcontext, krb5kt); + if ( kprinc ) + krb5_free_principal (child.kcontext, kprinc); + + if ( rc ) { + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: set_auth ending with %d", rc ); + } else if ( kerror ) { + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: set_auth ending with krb5 error %d, %s", kerror, error_message(kerror)); + } else { + log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: set_auth ending ok"); + } + + return kerror ? (int) kerror : (int) rc; + +} + + + +static void * +waklog_create_server_config (MK_POOL * p, server_rec * s) +{ + waklog_config *cfg; + + cfg = (waklog_config *) ap_pcalloc (p, sizeof (waklog_config)); + cfg->p = p; + memset(cfg, 0, sizeof(waklog_config)); + cfg->path = "(server)"; + cfg->protect = WAKLOG_UNSET; + cfg->usertokens = WAKLOG_UNSET; + cfg->keytab = WAKLOG_UNSET; + cfg->principal = WAKLOG_UNSET; + cfg->default_principal = WAKLOG_UNSET; + cfg->default_keytab = WAKLOG_UNSET; + cfg->afs_cell = WAKLOG_UNSET; + cfg->forked = 0; + cfg->configured = 0; + + log_error (APLOG_MARK, APLOG_DEBUG, 0, s, + "mod_waklog: server config created."); + + return (cfg); +} + +/* initialize with host-config information */ + +static void * +waklog_create_dir_config (MK_POOL * p, char *dir) +{ + waklog_config *cfg; + + cfg = (waklog_config *) ap_pcalloc (p, sizeof (waklog_config)); + memset(cfg, 0, sizeof(waklog_config)); + cfg->p = p; + cfg->path = ap_pstrdup(p, dir ); + cfg->protect = WAKLOG_UNSET; + cfg->usertokens = WAKLOG_UNSET; + cfg->keytab = WAKLOG_UNSET; + cfg->principal = WAKLOG_UNSET; + cfg->default_principal = WAKLOG_UNSET; + cfg->default_keytab = WAKLOG_UNSET; + cfg->afs_cell = WAKLOG_UNSET; + cfg->forked = 0; + cfg->configured = 0; + + return (cfg); +} + +static void *waklog_merge_dir_config(MK_POOL *p, void *parent_conf, void *newloc_conf) { + + waklog_config *merged = ( waklog_config * ) ap_pcalloc(p, sizeof(waklog_config ) ); + waklog_config *parent = ( waklog_config * ) parent_conf; + waklog_config *child = ( waklog_config * ) newloc_conf; + + merged->protect = child->protect != WAKLOG_UNSET ? child->protect : parent->protect; + + merged->path = child->path != WAKLOG_UNSET ? child->path : parent->path; + + merged->usertokens = child->usertokens != WAKLOG_UNSET ? child->usertokens : parent->usertokens; + + merged->principal = child->principal != WAKLOG_UNSET ? child->principal : parent->principal; + + merged->keytab = child->keytab != WAKLOG_UNSET ? child->keytab : parent->keytab; + + merged->default_keytab = child->default_keytab != WAKLOG_UNSET ? child->default_keytab : parent->default_keytab; + + merged->default_principal = child->default_principal != WAKLOG_UNSET ? child->default_principal : parent->default_principal; + + merged->afs_cell = child->afs_cell != WAKLOG_UNSET ? child->afs_cell : parent->afs_cell; + + return (void *) merged; + +} + +static void *waklog_merge_server_config(MK_POOL *p, void *parent_conf, void *newloc_conf) { + + waklog_config *merged = ( waklog_config * ) ap_pcalloc(p, sizeof(waklog_config ) ); + waklog_config *pconf = ( waklog_config * ) parent_conf; + waklog_config *nconf = ( waklog_config * ) newloc_conf; + + merged->protect = nconf->protect == WAKLOG_UNSET ? pconf->protect : nconf->protect; + + merged->usertokens = nconf->usertokens == WAKLOG_UNSET ? pconf->usertokens : nconf->usertokens; + + merged->keytab = nconf->keytab == WAKLOG_UNSET ? ap_pstrdup(p, pconf->keytab) : + ( nconf->keytab == WAKLOG_UNSET ? WAKLOG_UNSET : ap_pstrdup(p, pconf->keytab) ); + + merged->principal = nconf->principal == WAKLOG_UNSET ? ap_pstrdup(p, pconf->principal) : + ( nconf->principal == WAKLOG_UNSET ? WAKLOG_UNSET : ap_pstrdup(p, pconf->principal) ); + + merged->afs_cell = nconf->afs_cell == WAKLOG_UNSET ? ap_pstrdup(p, pconf->afs_cell) : + ( nconf->afs_cell == WAKLOG_UNSET ? WAKLOG_UNSET : ap_pstrdup(p, pconf->afs_cell) ); + + merged->default_keytab = nconf->default_keytab == WAKLOG_UNSET ? ap_pstrdup(p, pconf->default_keytab) : + ( nconf->default_keytab == WAKLOG_UNSET ? WAKLOG_UNSET : ap_pstrdup(p, pconf->default_keytab) ); + + merged->default_principal = nconf->default_principal == WAKLOG_UNSET ? ap_pstrdup(p, pconf->default_principal) : + ( nconf->default_principal == WAKLOG_UNSET ? WAKLOG_UNSET : ap_pstrdup(p, pconf->default_principal) ); + + + return (void *) merged; + +} + +static const char * +set_waklog_protect (cmd_parms * params, void *mconfig, int flag) +{ + waklog_config *cfg = mconfig ? ( waklog_config * ) mconfig : + ( waklog_config * ) ap_get_module_config(params->server->module_config, &waklog_module ); + + cfg->protect = flag; + cfg->configured = 1; + log_error (APLOG_MARK, APLOG_DEBUG, 0, params->server, + "mod_waklog: waklog_protect set on %s", cfg->path ? cfg->path : "NULL"); + return (NULL); +} + + +/* this adds a principal/keytab pair to get their tokens renewed by the + child process every few centons. */ + +void add_to_renewtable(MK_POOL *p, char *keytab, char *principal) { + + int i; + + if ( renewcount >= SHARED_TABLE_SIZE ) { + log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "mod_waklog: big problem. Increase the SHARED_TABLE_SIZE or \ + decrease your tokens."); + return; + } + + /* check to see if it's already there */ + + for ( i = 0; i < renewcount; i++ ) { + if ( ! strcmp(renewtable[i].principal, principal ) ) { + return; + } + } + + renewtable[renewcount].keytab = ap_pstrdup(p, keytab); + renewtable[renewcount].principal = ap_pstrdup(p, principal); + renewtable[renewcount].lastrenewed = 0; + ++renewcount; + +} + +static const char * +set_waklog_principal (cmd_parms *params, void *mconfig, char *principal, char *keytab) +{ + waklog_config *cfg = mconfig ? ( waklog_config * ) mconfig : + ( waklog_config * ) ap_get_module_config(params->server->module_config, &waklog_module ); + + log_error (APLOG_MARK, APLOG_DEBUG, 0, params->server, + "mod_waklog: configuring principal: %s, keytab: %s", principal, keytab); + + cfg->principal = ap_pstrdup(params->pool, principal); + cfg->keytab = ap_pstrdup (params->pool, keytab); + + add_to_renewtable(params->pool, keytab, principal); + + cfg->configured = 1; + + return (NULL); +} + +static const char * +set_waklog_use_afs_cell (cmd_parms * params, void *mconfig, char *file) +{ + waklog_config *cfg = mconfig ? ( waklog_config * ) mconfig : + ( waklog_config * ) ap_get_module_config(params->server->module_config, &waklog_module ); + + log_error (APLOG_MARK, APLOG_DEBUG, 0, params->server, + "mod_waklog: will use afs_cell: %s", file); + + cfg->afs_cell = ap_pstrdup (params->pool, file); + cfg->configured = 1; + return (NULL); +} + +static const char * +set_waklog_default_principal (cmd_parms * params, void *mconfig, char *principal, char *keytab) +{ + waklog_config *cfg = mconfig ? ( waklog_config * ) mconfig : + ( waklog_config * ) ap_get_module_config(params->server->module_config, &waklog_module ); + + waklog_config *srvcfg = ( waklog_config * ) ap_get_module_config(params->server->module_config, &waklog_module ); + + log_error (APLOG_MARK, APLOG_DEBUG, 0, params->server, + "mod_waklog: set default princ/keytab: %s, %s for %s", principal, keytab, cfg->path ? cfg->path : "NULL"); + + cfg->default_principal = ap_pstrdup (params->pool, principal); + cfg->default_keytab = ap_pstrdup(params->pool, keytab ); + + /* this also gets set at the server level */ + if ( mconfig && ( ! cfg->path ) ) { + srvcfg->default_principal = ap_pstrdup (params->pool, principal); + srvcfg->default_keytab = ap_pstrdup(params->pool, keytab ); + } else { + log_error(APLOG_MARK, APLOG_ERR, 0, params->server, "only able to set default principal on a global level!"); + return "Unable to set DefaultPrincipal outside of top level config!"; + } + + add_to_renewtable( params->pool, keytab, principal ); + + cfg->configured = 1; + + return (NULL); +} + +static const char * +set_waklog_use_usertokens (cmd_parms * params, void *mconfig, int flag) +{ + waklog_config *cfg = mconfig ? ( waklog_config * ) mconfig : + ( waklog_config * ) ap_get_module_config(params->server->module_config, &waklog_module ); + + cfg->usertokens = flag; + + cfg->configured = 1; + + log_error (APLOG_MARK, APLOG_DEBUG, 0, params->server, + "mod_waklog: waklog_use_user_tokens set"); + return (NULL); +} + + +#ifndef STANDARD20_MODULE_STUFF +static void waklog_child_exit( server_rec *s, MK_POOL *p ) { +#else +apr_status_t waklog_child_exit( void *sr ) { + + server_rec *s = (server_rec *) sr; +#endif + + if ( child.ccache ) { + krb5_cc_close(child.kcontext, child.ccache); + } + + if ( child.kcontext ) { + krb5_free_context(child.kcontext); + } + + /* forget our tokens */ + + ktc_ForgetAllTokens (); + + log_error (APLOG_MARK, APLOG_DEBUG, 0, s, + "mod_waklog: waklog_child_exit complete"); + +#ifdef STANDARD20_MODULE_STUFF + return APR_SUCCESS; +#endif + +} + +static void +#ifdef STANDARD20_MODULE_STUFF +waklog_child_init (MK_POOL * p, server_rec * s) +#else +waklog_child_init (server_rec * s, MK_POOL * p) +#endif +{ + + krb5_error_code code; + waklog_config *cfg; + + char *cell; + + cell = strdup(AFS_CELL); + + log_error (APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: child_init called for pid %d", getpid()); + + if ( !sharedspace ) { + log_error( APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: child_init called without shared space? %d", getpid()); + return; + } + + log_error (APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: child_init called for pid %d", getpid()); + + memset (&child, 0, sizeof(child)); + + if ( code = krb5_init_context(&child.kcontext) ) { + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: can't init kerberos context %d", code ); + } + + if ( code = krb5_cc_resolve(child.kcontext, "MEMORY:tmpcache", &child.ccache) ) { + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: can't initialize in-memory credentials cache %d", code ); + } + + if ( pag_for_children ) { + setpag (); + } + + getModConfig (cfg, s); + + if ( cfg->default_principal != WAKLOG_UNSET ) { + log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: child_init setting default user %s, %s", cfg->default_principal, cfg->default_keytab); + set_auth( s, NULL, 0, cfg->default_principal, cfg->default_keytab, 0); + } + + pr_Initialize( 0, AFSDIR_CLIENT_ETC_DIR, cell ); + +#ifdef STANDARD20_MODULE_STUFF + apr_pool_cleanup_register(p, s, waklog_child_exit, apr_pool_cleanup_null); +#endif + + log_error (APLOG_MARK, APLOG_DEBUG, 0, s, + "mod_waklog: child_init returned"); + + return; +} + +command_rec waklog_cmds[] = { + + command ("WaklogProtected", set_waklog_protect, 0, FLAG, + "enable waklog on a location or directory basis"), + + command ("WaklogPrincipal", set_waklog_principal, 0, TAKE2, + "Use the supplied keytab rather than the default"), + + command ("WaklogUseAFSCell", set_waklog_use_afs_cell, 0, TAKE1, + "Use the supplied AFS cell rather than the default"), + + command ("WaklogUseUserTokens", set_waklog_use_usertokens, 0, FLAG, + "Use the requesting user tokens (from webauth)"), + + command ("WaklogDefaultPrincipal", set_waklog_default_principal, 0, TAKE2, + "Set the default principal that the server runs as"), + + {NULL} +}; + + +/* not currently using this */ + +static int +token_cleanup (void *data) +{ + request_rec *r = (request_rec *) data; + + if (child.token.ticketLen) + { + memset (&child.token, 0, sizeof (struct ktc_token)); + + ktc_ForgetAllTokens (); + + log_error (APLOG_MARK, APLOG_DEBUG, 0, r->server, + "mod_waklog: ktc_ForgetAllTokens succeeded: pid: %d", + getpid ()); + } + return 0; +} + +static int +waklog_child_routine (void *data, child_info * pinfo) +{ + int i; + server_rec *s = (server_rec *) data; + krb5_error_code code; + char *cell; + time_t sleep_time = ( TKT_LIFE / 2 ) ; + time_t when; + time_t left; + waklog_config *cfg; + + getModConfig( cfg, s ); + + log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: waklog_child_routine started, running as %d", getuid()); + + memset (&child, 0, sizeof(child)); + + if ( code = krb5_init_context(&child.kcontext) ) { + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: can't init kerberos context %d", code ); + } + + if ( code = krb5_cc_resolve(child.kcontext, "MEMORY:tmpcache", &child.ccache) ) { + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: can't initialize in-memory credentials cache %d", code ); + } + + /* need to do this so we can make PTS calls */ + cell = strdup(AFS_CELL); /* stupid */ + pr_Initialize( 0, AFSDIR_CLIENT_ETC_DIR, cell ); + + while(1) { + + for ( i = 0; i < renewcount; ++i ) { + renewtable[i].lastrenewed = time(0); + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: (pid %d) renewing %s / %s", getpid(), renewtable[i].principal, + renewtable[i].keytab); + + set_auth( s, NULL, 0, renewtable[i].principal, renewtable[i].keytab, 1 ); + + /* if this is our default token, we want to "stash" it in our current PAG so the parent maintains readability of + things that it needs to read */ + + if ( cfg && cfg->default_principal && ( ! strcmp(cfg->default_principal, renewtable[i].principal ) ) ) { + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: renewing/setting default tokens" ); + set_auth( s, NULL, 0, renewtable[i].principal, renewtable[i].keytab, 0 ); + } + + } + + sharedspace->renewcount++; + + left = sleep_time; + + while( left > 5 ) { + when = time(0); + + sleep(left); + + left -= ( time(0) - when ); + } + + } + + pr_End(); + +} + +#ifdef STANDARD20_MODULE_STUFF +static int +waklog_init_handler (apr_pool_t * p, apr_pool_t * plog, + apr_pool_t * ptemp, server_rec * s) +{ + int rv; + extern char *version; + apr_proc_t *proc; + waklog_config *cfg; + void *data; + int fd = -1; + int use_existing = 1; + int oldrenewcount; + char cache_file[MAXNAMELEN]; +#ifdef use_pthreads + pthread_rwlockattr_t rwlock_attr; +#endif + + + getModConfig (cfg, s); + + /* initialize_module() will be called twice, and if it's a DSO + * then all static data from the first call will be lost. Only + * set up our static data on the second call. + * see http://issues.apache.org/bugzilla/show_bug.cgi?id=37519 */ + apr_pool_userdata_get (&data, userdata_key, s->process->pool); + + if (!data) + { + apr_pool_userdata_set ((const void *) 1, userdata_key, + apr_pool_cleanup_null, s->process->pool); + } + else + { + log_error (APLOG_MARK, APLOG_INFO, 0, s, + "mod_waklog: version %s initialized.", version); + + if ( sharedspace ) { + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: shared memory already allocated." ); + } else { + + snprintf( cache_file, MAXNAMELEN, "/tmp/waklog_cache.%d", getpid() ); + + if ( ( fd = open( cache_file, O_RDWR, 0600 ) ) == -1 ) { + + if ( errno == ENOENT ) { + + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: creating shared token cache file %s", cache_file ); + use_existing = 0; + if ( ( fd = open( cache_file, O_RDWR|O_CREAT|O_TRUNC, 0600 ) ) == -1 ) { + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: cannot create shared token cache file %s (%d)", cache_file, errno ); + exit(errno); + } + } else { + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: cannot open existing shared token cache file %s (%d)", cache_file, errno ); + } + } + + if ( use_existing == 0 ) { + struct sharedspace_s bob; + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: sizing our cache file %d to %d", fd, sizeof(struct sharedspace_s) ); + memset( &bob, 0, sizeof(struct sharedspace_s)); + write(fd, &bob, sizeof(struct sharedspace_s)); + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: done sizing our cache file to %d", sizeof(struct sharedspace_s) ); + } + + /* mmap the region */ + + if ( ( sharedspace = (struct sharedspace_s *) mmap ( NULL, sizeof(struct sharedspace_s), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0 ) ) != MAP_FAILED ) { + log_error( APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: shared mmap region ok %d", sharedspace ); + close(fd); + } else { + log_error( APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: mmap failed %d", errno ); + exit(errno); + } + } + +#ifdef use_pthreads +#define locktype pthread_rwlock_t +#else +#define locktype rwlock_t +#endif + + if ( sharedlock = ( locktype * ) mmap ( NULL, sizeof(locktype), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0 ) ) { +#ifndef use_pthreads + rwlock_init(sharedlock, USYNC_PROCESS, NULL ); +#else + pthread_rwlockattr_init(&rwlock_attr); + pthread_rwlockattr_setpshared(&rwlock_attr, PTHREAD_PROCESS_SHARED); + pthread_rwlock_init(sharedlock, &rwlock_attr ); +#endif + } else { + log_error( APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: rwlock mmap failed %d", errno ); + } + +#undef locktype + + /* set our default tokens */ + + oldrenewcount = sharedspace->renewcount; + + pag_for_children = 0; + + proc = (apr_proc_t *) ap_pcalloc (s->process->pool, sizeof (apr_proc_t)); + + rv = apr_proc_fork (proc, s->process->pool); + + if (rv == APR_INCHILD) + { + waklog_child_routine (s, NULL); + } + else + { + apr_pool_note_subprocess (s->process->pool, proc, APR_KILL_ALWAYS); + } + /* parent and child */ + cfg->forked = proc->pid; + pag_for_children = 1; + + if ( use_existing == 0 ) { + /* wait here until our child process has gone and done it's renewing thing. */ + while( sharedspace->renewcount == oldrenewcount ) { + log_error( APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: waiting for tokens..." ); + sleep(2); + } + } + + if ( cfg->default_principal ) { + set_auth( s, NULL, 0, cfg->default_principal, cfg->default_keytab, 0); + } + } + return 0; +} +#else +static void +waklog_init (server_rec * s, MK_POOL * p) +{ + extern char *version; + int pid; + waklog_config *cfg; + char *cell; + int fd = -1; + int use_existing = 1; + int oldrenewcount; + char cache_file[MAXNAMELEN]; +#ifdef use_pthreads + pthread_rwlockattr_t rwlock_attr; +#endif + + log_error (APLOG_MARK, APLOG_DEBUG, 0, s, + "mod_waklog: version %s initialized.", version); + + if ( sharedspace ) { + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: shared memory already allocated." ); + } else { + + snprintf( cache_file, MAXNAMELEN, "/tmp/waklog_cache.%d", getpid() ); + + if ( ( fd = open( cache_file, O_RDWR, 0600 ) ) == -1 ) { + + if ( errno == ENOENT ) { + + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: creating shared token cache file %s", cache_file ); + use_existing = 0; + if ( ( fd = open( cache_file, O_RDWR|O_CREAT|O_TRUNC, 0600 ) ) == -1 ) { + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: cannot create shared token cache file %s (%d)", cache_file, errno ); + exit(errno); + } + } else { + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: cannot open existing shared token cache file %s (%d)", cache_file, errno ); + } + + } + + if ( use_existing == 0 ) { + struct sharedspace_s bob; + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: sizing our cache file %d to %d", fd, sizeof(struct sharedspace_s) ); + memset( &bob, 0, sizeof(struct sharedspace_s)); + write(fd, &bob, sizeof(struct sharedspace_s)); + log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: done sizing our cache file to %d", sizeof(struct sharedspace_s) ); + } + + /* mmap the region */ + + if ( ( sharedspace = (struct sharedspace_s *) mmap ( NULL, sizeof(struct sharedspace_s), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0 ) ) != -1 ) { + log_error( APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: shared mmap region ok %d", sharedspace ); + close(fd); + } else { + log_error( APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: mmap failed %d", errno ); + exit(errno); + } + } + +#ifdef use_pthreads +#define locktype pthread_rwlock_t +#else +#define locktype rwlock_t +#endif + + /* mmap our shared space for our lock */ + if ( sharedlock = ( locktype * ) mmap ( NULL, sizeof(locktype), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0 ) ) { +#ifndef use_pthreads + rwlock_init(sharedlock, USYNC_PROCESS, NULL ); +#else + pthread_rwlockattr_init(&rwlock_attr); + pthread_rwlockattr_setpshared(&rwlock_attr, PTHREAD_PROCESS_SHARED); + pthread_rwlock_init(sharedlock, &rwlock_attr ); +#endif + } else { + log_error( APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: rwlock mmap failed %d", errno ); + } + +#undef locktype + + /* set our default tokens */ + + getModConfig (cfg, s); + + oldrenewcount = sharedspace->renewcount; + + pag_for_children = 0; + + pid = ap_bspawn_child (p, waklog_child_routine, s, kill_always, + NULL, NULL, NULL); + + pag_for_children = 1; + + log_error (APLOG_MARK, APLOG_DEBUG, 0, s, + "mod_waklog: ap_bspawn_child: %d.", pid); + + if ( use_existing == 0 ) { + /* wait here until our child process has gone and done it's renewing thing. */ + while( sharedspace->renewcount == oldrenewcount ) { + log_error( APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: waiting for tokens..." ); + sleep(2); + } + } + + if ( cfg->default_principal ) { + set_auth( s, NULL, 0, cfg->default_principal, cfg->default_keytab, 0); + } + +} +#endif + +static int +waklog_phase0 (request_rec * r) +{ + waklog_config *cfg; + + log_error (APLOG_MARK, APLOG_DEBUG, 0, r->server, + "mod_waklog: phase0 called"); + + cfg = retrieve_config(r); + + if ( cfg->protect && cfg->principal ) { + log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: phase0 using user %s", cfg->principal); + set_auth(r->server, r, 0, cfg->principal, cfg->keytab, 0); + } else if ( cfg->default_principal ) { + log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: phase0 using default user %s", cfg->default_principal); + set_auth(r->server, r, 0, cfg->default_principal, cfg->default_keytab, 0); + } else { + log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: phase0 not doing nothin."); + } + + return DECLINED; +} + +static int +waklog_phase1 (request_rec * r) +{ + waklog_config *cfg; + + log_error (APLOG_MARK, APLOG_DEBUG, 0, r->server, + "mod_waklog: phase1 called"); + + cfg = retrieve_config(r); + + if ( cfg->protect && cfg->principal ) { + log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: phase1 using user %s", cfg->principal); + set_auth(r->server, r, 0, cfg->principal, cfg->keytab, 0); + } else if ( cfg->default_principal ) { + log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: phase1 using default user %s", cfg->default_principal); + set_auth(r->server, r, 0, cfg->default_principal, cfg->default_keytab, 0); + } else { + log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: phase1 not doing nothin."); + } + + return DECLINED; +} + +static int +waklog_phase3 (request_rec * r) +{ + waklog_config *cfg; + + log_error (APLOG_MARK, APLOG_DEBUG, 0, r->server, + "mod_waklog: phase 3 called"); + + cfg = retrieve_config(r); + + if ( cfg->protect && cfg->principal ) { + log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: phase3 using user %s", cfg->principal); + set_auth(r->server, r, 0, cfg->principal, cfg->keytab, 0); + } else if ( cfg->default_principal ) { + log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: phase3 using default user %s", cfg->default_principal); + set_auth(r->server, r, 0, cfg->default_principal, cfg->default_keytab, 0); + } else { + log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: phase3 not doing nothin."); + } + + return DECLINED; +} + +static int +waklog_phase6 (request_rec * r) +{ + waklog_config *cfg; + + log_error (APLOG_MARK, APLOG_DEBUG, 0, r->server, + "mod_waklog: phase6 called"); + + cfg = retrieve_config(r); + + if ( cfg->protect && cfg->principal ) { + log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: phase6 using user %s", cfg->principal); + set_auth(r->server, r, 0, cfg->principal, cfg->keytab, 0); + } else if ( cfg->default_principal ) { + log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: phase6 using default user %s", cfg->default_principal); + set_auth(r->server, r, 0, cfg->default_principal, cfg->default_keytab, 0); + } else { + log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: phase6 not doing nothin."); + } + + return DECLINED; +} + +static int +waklog_phase7 (request_rec * r) +{ + waklog_config *cfg; + int rc = 0; + + log_error (APLOG_MARK, APLOG_DEBUG, 0, r->server, + "mod_waklog: phase7 called"); + + cfg = retrieve_config (r); + + if ( cfg->protect && cfg->usertokens ) { + log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: phase7 using usertokens"); + rc = set_auth( r->server, r, 1, NULL, NULL, 0); + } else if ( cfg->protect && cfg->principal ) { + log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: phase7 using user %s", cfg->principal); + rc = set_auth( r->server, r, 0, cfg->principal, cfg->keytab, 0); + } else if ( cfg->default_principal ) { + log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: phase7 using default user %s", cfg->default_principal); + rc = set_auth( r->server, r, 0, cfg->default_principal, cfg->default_keytab, 0); + } + + if ( rc ) { + return 400; + } + + return DECLINED; +} + +static int +waklog_phase9 (request_rec * r) +{ + waklog_config *cfg; + + log_error (APLOG_MARK, APLOG_DEBUG, 0, r->server, + "mod_waklog: phase9 called"); + + getModConfig (cfg, r->server); + + if ( cfg->default_principal ) { + log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: phase9 using default user %s", cfg->default_principal); + set_auth( r->server, r, 0, cfg->default_principal, cfg->default_keytab, 0); + } + + return DECLINED; +} + + +static +#ifdef STANDARD20_MODULE_STUFF + int +#else + void +#endif +waklog_new_connection (conn_rec * c +#ifdef STANDARD20_MODULE_STUFF + , void *dummy +#endif + ) +{ + + waklog_config *cfg; + + log_error (APLOG_MARK, APLOG_DEBUG, 0, c->base_server, + "mod_waklog: new_connection called: pid: %d", getpid ()); + + getModConfig(cfg, c->base_server); + + if ( cfg->default_principal ) { + log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, "mod_waklog: new conn setting default user %s", + cfg->default_principal); + set_auth( c->base_server, NULL, 0, cfg->default_principal, cfg->default_keytab, 0); + } + + + return +#ifdef STANDARD20_MODULE_STUFF + 0 +#endif + ; +} + + +/* +** Here's a quick explaination for phase0 and phase2: +** Apache does a stat() on the path between phase0 and +** phase2, and must by ACLed rl to succeed. So, at +** phase0 we acquire credentials for umweb:servers from +** a keytab, and at phase2 we must ensure we remove them. +** +** Failure to "unlog" would be a security risk. +*/ +static int +waklog_phase2 (request_rec * r) +{ + + log_error (APLOG_MARK, APLOG_DEBUG, 0, r->server, + "mod_waklog: phase2 called"); + + if (child.token.ticketLen) + { + memset (&child.token, 0, sizeof (struct ktc_token)); + + ktc_ForgetAllTokens (); + + log_error (APLOG_MARK, APLOG_DEBUG, 0, r->server, + "mod_waklog: ktc_ForgetAllTokens succeeded: pid: %d", + getpid ()); + } + + log_error (APLOG_MARK, APLOG_DEBUG, 0, r->server, + "mod_waklog: phase2 returning"); + + return DECLINED; +} + +#ifndef STANDARD20_MODULE_STUFF +module MODULE_VAR_EXPORT waklog_module = { + STANDARD_MODULE_STUFF, + waklog_init, /* module initializer */ + waklog_create_dir_config, /* create per-dir config structures */ + waklog_merge_dir_config, /* merge per-dir config structures */ + waklog_create_server_config, /* create per-server config structures */ + waklog_merge_dir_config, /* merge per-server config structures */ + waklog_cmds, /* table of config file commands */ + NULL, /* [#8] MIME-typed-dispatched handlers */ + waklog_phase1, /* [#1] URI to filename translation */ + NULL, /* [#4] validate user id from request */ + NULL, /* [#5] check if the user is ok _here_ */ + waklog_phase3, /* [#3] check access by host address */ + waklog_phase6, /* [#6] determine MIME type */ + waklog_phase7, /* [#7] pre-run fixups */ + waklog_phase9, /* [#9] log a transaction */ + NULL, /* [#2] header parser */ + waklog_child_init, /* child_init */ + waklog_child_exit, /* child_exit */ + waklog_phase0 /* [#0] post read-request */ +#ifdef EAPI + , NULL, /* EAPI: add_module */ + NULL, /* EAPI: remove_module */ + NULL, /* EAPI: rewrite_command */ + waklog_new_connection /* EAPI: new_connection */ +#endif +}; +#else +static void +waklog_register_hooks (apr_pool_t * p) +{ + ap_hook_translate_name (waklog_phase1, NULL, NULL, APR_HOOK_FIRST); + ap_hook_header_parser (waklog_phase2, NULL, NULL, APR_HOOK_FIRST); + ap_hook_access_checker (waklog_phase3, NULL, NULL, APR_HOOK_FIRST); + ap_hook_type_checker (waklog_phase6, NULL, NULL, APR_HOOK_FIRST); + ap_hook_fixups (waklog_phase7, NULL, NULL, APR_HOOK_FIRST); + ap_hook_log_transaction (waklog_phase9, NULL, NULL, APR_HOOK_FIRST); + ap_hook_child_init (waklog_child_init, NULL, NULL, APR_HOOK_FIRST); + ap_hook_post_read_request (waklog_phase0, NULL, NULL, APR_HOOK_FIRST); + ap_hook_pre_connection (waklog_new_connection, NULL, NULL, APR_HOOK_FIRST); + ap_hook_post_config (waklog_init_handler, NULL, NULL, APR_HOOK_MIDDLE); +} + + +module AP_MODULE_DECLARE_DATA waklog_module = { + STANDARD20_MODULE_STUFF, + waklog_create_dir_config, /* create per-dir conf structures */ + waklog_merge_dir_config, /* merge per-dir conf structures */ + waklog_create_server_config, /* create per-server conf structures */ + waklog_merge_dir_config, /* merge per-server conf structures */ + waklog_cmds, /* table of configuration directives */ + waklog_register_hooks /* register hooks */ +}; +#endif