Merge sourceforge mod_waklog
[hcoop/debian/libapache-mod-waklog.git] / mod_waklog.c
index 0a0fae5..eca89ad 100644 (file)
@@ -10,6 +10,8 @@
 
 #ifdef sun
 #include <synch.h>
+#include <stropts.h>
+#include <sys/ioccom.h>
 #elif linux
 #define use_pthreads
 #include <features.h>
@@ -31,9 +33,6 @@
 /********************* APACHE1 ******************************************************************************/
 #ifndef APACHE2
 #include "ap_config.h"
-#if defined(sun)
-#include <sys/ioccom.h>
-#endif /* sun */
 #include <http_conf_globals.h>
 #define MK_POOL pool
 #define MK_TABLE_GET ap_table_get
@@ -46,19 +45,16 @@ module waklog_module;
 
 /********************* APACHE2 ******************************************************************************/
 #else
+#include "http_connection.h"
 #include <apr_strings.h>
 #include <apr_base64.h>
+#include <apr_time.h>
 #define ap_pcalloc apr_pcalloc
 #define ap_pdupstr apr_pdupstr
 #define ap_pstrdup apr_pstrdup
 #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,     \
@@ -67,23 +63,30 @@ module AP_MODULE_DECLARE_DATA waklog_module;
 typedef struct { int dummy; } child_info;
 const char *userdata_key = "waklog_init"; 
 
+/* Apache 2.4 */
+#ifdef APLOG_USE_MODULE
+APLOG_USE_MODULE(waklog);
+#endif
+
 #endif /* APACHE2 */
 /**************************************************************************************************/
 
 #include <krb5.h>
-#include <stropts.h>
+#include <kopenafs.h>
+
+#include <afs/param.h>
+
 #include <afs/venus.h>
 #include <afs/auth.h>
 #include <afs/dirpath.h>
 #include <afs/ptuser.h>
+#include <afs/com_err.h>
 #include <rx/rxkad.h>
 
 #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
+#define WAKLOG_UNSET -1
 
 #ifdef WAKLOG_DEBUG
 #undef APLOG_DEBUG
@@ -106,6 +109,7 @@ typedef struct
   char *default_principal;
   char *default_keytab;
   char *afs_cell;
+  char *afs_cell_realm;
   char *path;
   MK_POOL *p;
 }
@@ -162,22 +166,16 @@ int renewcount = 0;
 
 #define getModConfig(P, X) P = (waklog_config *) ap_get_module_config( (X)->module_config, &waklog_module );
 
-#include <krb5.h>
-
-#if defined(sun)
-#include <sys/ioccom.h>
-#endif /* sun */
-#include <stropts.h>
-#include <afs/venus.h>
-#include <afs/auth.h>
-#include <afs/dirpath.h>
-#include <afs/ptuser.h>
-#include <rx/rxkad.h>
-
 
+#ifdef APLOG_USE_MODULE
+static void
+log_error (const char *file, int line, int module_index, int level, int status,
+           const server_rec * s, const char *fmt, ...)
+#else
 static void
 log_error (const char *file, int line, int level, int status,
            const server_rec * s, const char *fmt, ...)
+#endif
 {
   char errstr[4096];
   va_list ap;
@@ -187,7 +185,12 @@ log_error (const char *file, int line, int level, int status,
   va_end (ap);
 
 #ifdef APACHE2
+ #ifdef APLOG_USE_MODULE
+  /* Apache 2.4 */
+  ap_log_error (file, line, module_index, level | APLOG_NOERRNO, status, s, "%s", errstr);
+ #else
   ap_log_error (file, line, level | APLOG_NOERRNO, status, s, "(%d) %s", getpid(), errstr);
+ #endif
 #else
   ap_log_error (file, line, level | APLOG_NOERRNO, s, "(%d) %s", getpid(), errstr);
 #endif
@@ -231,6 +234,7 @@ set_auth ( server_rec *s, request_rec *r, int self, char *principal, char *keyta
   krb5_get_init_creds_opt kopts;
   krb5_creds v5creds;
   krb5_creds increds;
+  krb5_ccache clientccache;
   struct ktc_principal server = { "afs", "", "" };
   struct ktc_principal client;
   struct ktc_token token;
@@ -241,31 +245,50 @@ set_auth ( server_rec *s, request_rec *r, int self, char *principal, char *keyta
   int rc = 0;
   int buflen = 0;
   time_t oldest_time = 0;
-  int oldest;
+  int oldest = 0;
   int stored = -1;
   time_t mytime;
   int indentical;
   int cell_in_principal;
   int attempt;
+  int use_client_credentials = 0;
   
-  char k5user[MAXNAMELEN];
-  char *k5secret;
+  char k5user[MAXNAMELEN] = "";
+  char *k5secret = NULL;
+
+  char *k5path = NULL;
   
   memset((char *) &increds, 0, sizeof(increds));
-  
   /* init some stuff if it ain't already */
-  
+  /* XXX - In what situation will these not be initialized? */
+
   if ( ! child.kcontext ) {
-    kerror = krb5_init_context(&child.kcontext);
+    if ((kerror = krb5_init_context(&child.kcontext))) {
+      log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: can't initialize Kerberos context err=%d", 
+               kerror);
+      return(-1);
+    }
   }
-  
-  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 );
+
+  if ( !child.ccache) {
+    if ((kerror = krb5_cc_resolve(child.kcontext, "MEMORY:tmpcache", &child.ccache))) {
+      log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: can't initialize credentials cache %s err=%d",
+                k5path, kerror );
+      return(-1);
+    }
   }
   
-  
-  log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: set_auth: %d, %s, %s, %d", self, principal ? principal : "NULL", 
-    keytab ? keytab : "NULL", storeonly);
+  log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: set_auth: %d, %s, %s, %d, KRB5CC=%s user=%s",
+            self, principal ? principal : "NULL", 
+            keytab ? keytab : "NULL",
+            storeonly,
+            k5path ? k5path : "NULL",
+#ifdef APACHE2
+            (r && r->user) ? r->user : "NULL"
+#else
+            (r && r->connection && r->connection->user) ? r->connection->user : "NULL"
+#endif
+            );
   
   /* pull the server config record that we care about... */
   
@@ -280,34 +303,51 @@ set_auth ( server_rec *s, request_rec *r, int self, char *principal, char *keyta
   if ( ! cfg ) {
     log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: cfg is %d", cfg );
   }
-  
-#ifdef APACHE2
+
+
   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 */
-       
+       through. */
+
+#ifdef APACHE2       
        if ( ! ( r && r->connection && r->user )) {
          log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: self authentication selected, but no data available");
+         log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: r->user=%s", (r->user==NULL ? "null" : r->user));
          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");
+#else
+       if ( ! (r && r->connection && r->connection->user)) {
+        log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: self authentication selected, but no username available");
          return -1;
-       }     
+       }
+
+       strncpy(k5user, r->connection->user, sizeof(k5user));
+#endif
+       /* if they've supplied a credentials cache */
+       k5path = (char *) MK_TABLE_GET( r->subprocess_env, "KRB5CCNAME" );
+
+       /* the other thing we need is someone's password */       
+       k5secret = (char *) MK_TABLE_GET( r->notes, "ATTR_PASSWORD" );
        
        /* we'll pick this up later after we've checked the cache and current state */
              
   } else
-#endif
   if ( principal ) {
-    strncpy( k5user, principal, sizeof(k5user));
+    strncpy(k5user, principal, sizeof(k5user));
+  } else
+#ifdef APACHE2
+  if (r && r->user) {
+    strncpy(k5user, r->user, sizeof(k5user));
+  }
+#else
+  if (r && r->connection && r->connection->user) {
+    strncpy(k5user, r->connection->user, sizeof(k5user));
   }
+#endif
   
+  log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: set_auth: k5user=%s", k5user);
   mytime = time(0);
   
   /* see if we should just go ahead and ignore this call, since we already should be set to these
@@ -376,18 +416,17 @@ set_auth ( server_rec *s, request_rec *r, int self, char *principal, char *keyta
     }
   
   }
-  
+
   /* if 'usecached' isn't set, we've got to get our tokens from somewhere... */
-  
-  if (( ! usecached ) && ( k5user )) {
+  if ( ! usecached ) {
 
     /* 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) );
+    if ( ( kerror = krb5_parse_name (child.kcontext, k5user, &kprinc ) ) ) {
+      log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: krb5_parse_name %s", (char *) afs_error_message(kerror) );
       goto cleanup;
     }
     
@@ -399,100 +438,108 @@ set_auth ( server_rec *s, request_rec *r, int self, char *principal, char *keyta
     krb5_get_init_creds_opt_set_forwardable ( &kopts, 0 );
     krb5_get_init_creds_opt_set_proxiable ( &kopts, 0 );
     
-    if ( keytab ) {
+    if ( keytab || k5secret ) {
     
-      /* if we've been passed a keytab, we're going to be getting our credentials from it */
+      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);
+        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_kt_resolve(child.kcontext, keytab, &krb5kt ) ) ) {
+          log_error( APLOG_MARK, APLOG_ERR, 0, s,
+            "mod_waklog: krb5_kt_resolve %s", afs_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;
-      }
+        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",
+                  afs_error_message(kerror) );
+                goto cleanup;
+        }
+      } else if (k5secret) {
       
-    } else if ( self ) {
+        /* If the WebSSO is lame enough to provide a secret, then try and use that to get a token */
       
-      /* 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",
+                  afs_error_message(kerror) );
+                  /* nuke the password so it doesn't end up in core files */
+                  memset(k5secret, 0, strlen(k5secret));
+                goto cleanup;
+        }
       
-      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, strlen(k5secret));
       }
-      
-      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", 
+                  afs_error_message(kerror));
+        goto cleanup;
+      }
       
-    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;
-    }
+      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", 
+                  afs_error_message(kerror));
+        goto cleanup;
+      }
     
-    krb5_free_cred_contents(child.kcontext, &v5creds);
+      krb5_free_cred_contents(child.kcontext, &v5creds);
+      if ( kerror ) {
+        log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: store cred %s", afs_error_message(kerror));
+        goto cleanup;
+      }
+      
+      log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: kinit ok for %s", k5user );
+
+    } else if (k5path) {
+      /* If we've got a path to a credentials cache, then try and use that. We can't just
+       * replace child.creds, because we want to ensure that only this request gets access to
+       * that cache */
+
+      if ( ( kerror = krb5_cc_resolve(child.kcontext, k5path, &clientccache ) ) ) {
+        log_error(APLOG_MARK, APLOG_ERR, 0, s,
+                 "mod_waklog: can't open provided credentials cache %s err=%d",
+                 k5path, kerror );
+        goto cleanup;
+      }
+
+      use_client_credentials = 1;
+    } 
     
-    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 );
-
     /** we make two attempts here, one for afs@REALM and one for afs/cell@REALM */
     for(attempt = 0; attempt <= 1; attempt++) {
+      strncpy( buf, "afs", sizeof(buf) - 1 );
       cell_in_principal = (cfg->cell_in_principal + attempt) % 2;
 
       log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: cell_in_principal=%d", cell_in_principal );
       if (cell_in_principal) {
-        strncat(buf, "/", sizeof(buf) - strlen(buf) - 1);
+        strncat(buf, "/",           sizeof(buf) - strlen(buf) - 1);
         strncat(buf, cfg->afs_cell, sizeof(buf) - strlen(buf) - 1);
       }
+      if (cfg->afs_cell_realm != NULL) {
+        strncat(buf, "@",                 sizeof(buf) - strlen(buf) - 1);
+        strncat(buf, cfg->afs_cell_realm, 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));
+        log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: krb5_parse name %s", afs_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));
+
+      if (!use_client_credentials) {
+        clientccache = child.ccache;
+      }
+
+      if ((kerror = krb5_cc_get_principal(child.kcontext, clientccache, &increds.client))) {
+        log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: krb5_cc_get_princ %s %p", afs_error_message(kerror), clientccache);
         goto cleanup;
       }
       
@@ -500,13 +547,11 @@ set_auth ( server_rec *s, request_rec *r, int self, char *principal, char *keyta
       
       increds.times.endtime = 0;
       
-      increds.keyblock.enctype = ENCTYPE_DES_CBC_CRC;
-      
-      if (kerror = krb5_get_credentials (child.kcontext, 0, child.ccache, &increds, &v5credsp )) {
+      if ( ( kerror = krb5_get_credentials (child.kcontext, 0, clientccache, &increds, &v5credsp ) ) ) {
         /* only complain once we've tried both afs@REALM and afs/cell@REALM */
         if (attempt>=1) {
           log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: krb5_get_credentials: %s",
-                    error_message(kerror));
+                    afs_error_message(kerror));
           goto cleanup;
         } else {
           continue;
@@ -529,7 +574,12 @@ set_auth ( server_rec *s, request_rec *r, int self, char *principal, char *keyta
     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);
+    if (tkt_DeriveDesKey(v5credsp->keyblock.enctype, v5credsp->keyblock.contents,
+                         v5credsp->keyblock.length, &token.sessionKey) != 0) {
+        log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: tkt_DeriveDesKey failure (enctype: %d)",
+                  v5credsp->keyblock.enctype);
+        goto cleanup;
+    }
     token.kvno = RXKAD_TKT_TYPE_KERBEROS_V5;
     token.ticketLen = v5credsp->ticket.length;
     memmove( token.ticket, v5credsp->ticket.data, token.ticketLen);
@@ -664,15 +714,17 @@ set_auth ( server_rec *s, request_rec *r, int self, char *principal, char *keyta
   
   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);
+      afs_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);
+        afs_error_message(rc), k5user);
       goto cleanup;
     }
   }
   
   cleanup:
+  if (use_client_credentials)
+    krb5_cc_close(child.kcontext, clientccache);
   if ( v5credsp )
     krb5_free_cred_contents(child.kcontext, v5credsp);
   if ( increds.client )
@@ -687,7 +739,7 @@ set_auth ( server_rec *s, request_rec *r, int self, char *principal, char *keyta
   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));
+    log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: set_auth ending with krb5 error %d, %s", kerror, afs_error_message(kerror));
   } else {
     log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: set_auth ending ok");
   }
@@ -697,6 +749,27 @@ set_auth ( server_rec *s, request_rec *r, int self, char *principal, char *keyta
 }
 
 
+int get_cfg_usertokens(waklog_config *cfg)
+{
+  if (cfg->usertokens==WAKLOG_UNSET)
+    return 0; /* default */
+  return cfg->usertokens;
+}
+
+int get_cfg_protect(waklog_config *cfg)
+{
+  if (cfg->protect==WAKLOG_UNSET)
+    return 0; /* default */
+  return cfg->protect;
+}
+
+int get_cfg_disable_token_cache(waklog_config *cfg)
+{
+  if (cfg->disable_token_cache==WAKLOG_UNSET)
+    return 0; /* default */
+  return cfg->disable_token_cache;
+}
+
 
 static void *
 waklog_create_server_config (MK_POOL * p, server_rec * s)
@@ -710,11 +783,12 @@ waklog_create_server_config (MK_POOL * p, server_rec * s)
   cfg->protect = WAKLOG_UNSET;
   cfg->usertokens = WAKLOG_UNSET;
   cfg->disable_token_cache = 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->keytab = NULL;
+  cfg->principal = NULL;
+  cfg->default_principal = NULL;
+  cfg->default_keytab = NULL;
+  cfg->afs_cell = NULL;
+  cfg->afs_cell_realm = NULL;
   cfg->forked = 0;
   cfg->configured = 0;
 
@@ -738,11 +812,12 @@ waklog_create_dir_config (MK_POOL * p, char *dir)
   cfg->protect = WAKLOG_UNSET;
   cfg->usertokens = WAKLOG_UNSET;
   cfg->disable_token_cache = 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->keytab = NULL;
+  cfg->principal = NULL;
+  cfg->default_principal = NULL;
+  cfg->default_keytab = NULL;
+  cfg->afs_cell = NULL;
+  cfg->afs_cell_realm = NULL;
   cfg->forked = 0;
   cfg->configured = 0;
 
@@ -757,21 +832,23 @@ static void *waklog_merge_dir_config(MK_POOL *p, void *parent_conf, void *newloc
   
   merged->protect = child->protect != WAKLOG_UNSET ? child->protect : parent->protect;
   
-  merged->path = child->path != WAKLOG_UNSET ? child->path : parent->path;
+  merged->path = child->path != NULL ? child->path : parent->path;
   
   merged->usertokens = child->usertokens != WAKLOG_UNSET ? child->usertokens : parent->usertokens;
 
   merged->disable_token_cache = child->disable_token_cache != WAKLOG_UNSET ? child->disable_token_cache : parent->disable_token_cache;
   
-  merged->principal = child->principal != WAKLOG_UNSET ? child->principal : parent->principal;
+  merged->principal = child->principal != NULL ? child->principal : parent->principal;
   
-  merged->keytab = child->keytab != WAKLOG_UNSET ? child->keytab : parent->keytab;
+  merged->keytab = child->keytab != NULL ? child->keytab : parent->keytab;
   
-  merged->default_keytab = child->default_keytab != WAKLOG_UNSET ? child->default_keytab : parent->default_keytab;
+  merged->default_keytab = child->default_keytab != NULL ? child->default_keytab : parent->default_keytab;
   
-  merged->default_principal = child->default_principal != WAKLOG_UNSET ? child->default_principal : parent->default_principal;
+  merged->default_principal = child->default_principal != NULL ? child->default_principal : parent->default_principal;
   
-  merged->afs_cell = child->afs_cell != WAKLOG_UNSET ? child->afs_cell : parent->afs_cell;
+  merged->afs_cell = child->afs_cell != NULL ? child->afs_cell : parent->afs_cell;
+
+  merged->afs_cell_realm = child->afs_cell_realm != NULL ? child->afs_cell_realm : parent->afs_cell_realm;
   
   return (void *) merged;
   
@@ -789,20 +866,23 @@ static void *waklog_merge_server_config(MK_POOL *p, void *parent_conf, void *new
 
   merged->disable_token_cache = nconf->disable_token_cache == WAKLOG_UNSET ? pconf->disable_token_cache : nconf->disable_token_cache;
 
-  merged->keytab = nconf->keytab ==  WAKLOG_UNSET ? ap_pstrdup(p, pconf->keytab) : 
-    ( nconf->keytab == WAKLOG_UNSET ? WAKLOG_UNSET : ap_pstrdup(p, pconf->keytab) );
+  merged->keytab = nconf->keytab == NULL ? ap_pstrdup(p, pconf->keytab) : 
+    ( nconf->keytab == NULL ? NULL : ap_pstrdup(p, nconf->keytab) );
     
-  merged->principal = nconf->principal == WAKLOG_UNSET ? ap_pstrdup(p, pconf->principal) : 
-      ( nconf->principal == WAKLOG_UNSET ? WAKLOG_UNSET : ap_pstrdup(p, pconf->principal) );
+  merged->principal = nconf->principal == NULL ? ap_pstrdup(p, pconf->principal) : 
+      ( nconf->principal == NULL ? NULL : ap_pstrdup(p, nconf->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->afs_cell = nconf->afs_cell == NULL ? ap_pstrdup(p, pconf->afs_cell) : 
+      ( nconf->afs_cell == NULL ? NULL : ap_pstrdup(p, nconf->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->afs_cell_realm = nconf->afs_cell_realm  == NULL ? ap_pstrdup(p, pconf->afs_cell_realm) : 
+      ( nconf->afs_cell_realm == NULL ? NULL : ap_pstrdup(p, nconf->afs_cell_realm) );    
+  
+  merged->default_keytab = nconf->default_keytab == NULL ? ap_pstrdup(p, pconf->default_keytab) : 
+        ( nconf->default_keytab == NULL ? NULL : ap_pstrdup(p, nconf->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) );
+  merged->default_principal = nconf->default_principal == NULL ? ap_pstrdup(p, pconf->default_principal) : 
+        ( nconf->default_principal == NULL ? NULL : ap_pstrdup(p, nconf->default_principal) );
   
   
   return (void *) merged;
@@ -880,7 +960,9 @@ set_waklog_afs_cell (cmd_parms * params, void *mconfig, char *file)
   log_error (APLOG_MARK, APLOG_INFO, 0, params->server,
              "mod_waklog: will use afs_cell: %s", file);
 
-  waklog_srvconfig->cell_in_principal = 0;
+  // Prefer afs/cell@REALM over afs@REALM, just like the OpenAFS tools
+  waklog_srvconfig->cell_in_principal = 1;
+
   waklog_srvconfig->afs_cell = ap_pstrdup (params->pool, file);
   waklog_srvconfig->configured = 1;
 
@@ -892,6 +974,24 @@ set_waklog_afs_cell (cmd_parms * params, void *mconfig, char *file)
   return (NULL);
 }
 
+static const char *
+set_waklog_afs_cell_realm (cmd_parms * params, void *mconfig, char *file)
+{
+  waklog_config *waklog_mconfig = ( waklog_config * ) mconfig;
+  waklog_config *waklog_srvconfig =
+    ( waklog_config * ) ap_get_module_config(params->server->module_config, &waklog_module );
+
+  log_error (APLOG_MARK, APLOG_INFO, 0, params->server,
+             "mod_waklog: will use afs_cell_realm: %s", file);
+
+  waklog_srvconfig->afs_cell_realm = ap_pstrdup (params->pool, file);
+
+  if (waklog_mconfig != NULL) {
+    waklog_mconfig->afs_cell_realm = ap_pstrdup (params->pool, file);
+  }
+  return (NULL);
+}
+
 static const char *
 set_waklog_default_principal (cmd_parms * params, void *mconfig, char *principal, char *keytab)
 {
@@ -1007,21 +1107,21 @@ waklog_child_init (server_rec * s, MK_POOL * p)
 
   memset (&child, 0, sizeof(child));
   
-  if ( code = krb5_init_context(&child.kcontext) ) {
+  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) ) {
+  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 ();
+    k_setpag ();
   }
 
   getModConfig (cfg, s);
 
-  if ( cfg->default_principal != WAKLOG_UNSET ) {
+  if ( cfg->default_principal != NULL ) {
     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);
   }
@@ -1044,6 +1144,9 @@ command_rec waklog_cmds[] = {
   command ("WaklogAFSCell", set_waklog_afs_cell, 0, TAKE1,
            "Use the supplied AFS cell (required)"),
 
+  command ("WaklogAFSCellRealm", set_waklog_afs_cell_realm, 0, TAKE1,
+           "Assume that the AFS cell belongs to the specified Kerberos realm (optional)"),
+
   command ("WaklogEnabled", set_waklog_enabled, 0, FLAG,
            "enable waklog on a server, location, or directory basis"),
 
@@ -1083,7 +1186,15 @@ token_cleanup (void *data)
   return 0;
 }
 
+/* This function doesn't return anything but is passed to ap_bspawn_child on
+ * Apache 1 which expects it to return a pid as an int. For want of better
+ * understanding, err on the side of not changing Apache 1 code while fixing
+ * the compile warning on Apache 2. */
+#ifdef APACHE2
+static void
+#else
 static int
+#endif
 waklog_child_routine (void *data, child_info * pinfo)
 {
   int i;
@@ -1101,18 +1212,22 @@ waklog_child_routine (void *data, child_info * pinfo)
   
   memset (&child, 0, sizeof(child));
   
-  if ( code = krb5_init_context(&child.kcontext) ) {
+  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) ) {
+  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 );
   }
   
+  log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: about to pr_Initialize");
+
   /* need to do this so we can make PTS calls */
   cell = strdup(cfg->afs_cell); /* stupid */
   pr_Initialize(  0, AFSDIR_CLIENT_ETC_DIR, cell );
-  
+  log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: still here");
+
   while(1) {
   
     for ( i = 0; i < renewcount; ++i ) {
@@ -1220,15 +1335,24 @@ waklog_init_handler (apr_pool_t * p, apr_pool_t * plog,
           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));
+          if ( write(fd, &bob, sizeof(struct sharedspace_s)) != sizeof(struct sharedspace_s) ) {
+            log_error(APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: failed to write to our cache file %s (%d)", cache_file, errno );
+            exit(errno);
+          }
           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 ) {
+          int err = 0;
           log_error( APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: shared mmap region ok %d", sharedspace );
-          close(fd);
+          err = unlink(cache_file);
+          if (err) {
+            log_error( APLOG_MARK, APLOG_ERR, 0, s, "mod_waklog: unable to delete %s due to %d", cache_file, errno);
+          } else {
+            log_error( APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: shared cache unlinked (will be deleted when Apache quits)");
+          }
         } else {
            log_error( APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: mmap failed %d", errno );
            exit(errno);
@@ -1241,7 +1365,7 @@ waklog_init_handler (apr_pool_t * p, apr_pool_t * plog,
 #define locktype rwlock_t
 #endif
 
-      if ( sharedlock = ( locktype * ) mmap ( NULL, sizeof(locktype), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0 ) ) {
+      if ( ( sharedlock = ( locktype * ) mmap ( NULL, sizeof(locktype), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0 ) ) != NULL ) {
 #ifndef use_pthreads
         rwlock_init(sharedlock, USYNC_PROCESS, NULL );
 #else
@@ -1281,7 +1405,7 @@ waklog_init_handler (apr_pool_t * p, apr_pool_t * plog,
         /* 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);
+          apr_sleep(150000);
         }
       }
 
@@ -1298,7 +1422,6 @@ 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;
@@ -1342,7 +1465,7 @@ waklog_init (server_rec * s, MK_POOL * p)
 
     /* mmap the region */
     
-    if ( ( sharedspace = (struct sharedspace_s *) mmap ( NULL, sizeof(struct sharedspace_s), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0 ) ) != -1 ) {
+    if ( ( sharedspace = (struct sharedspace_s *) mmap ( NULL, sizeof(struct sharedspace_s), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0 ) ) != (void *) -1 ) {
       log_error( APLOG_MARK, APLOG_DEBUG, 0, s, "mod_waklog: shared mmap region ok %d", sharedspace );
       close(fd);
     } else {
@@ -1358,7 +1481,7 @@ waklog_init (server_rec * s, MK_POOL * p)
 #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 ) ) {
+  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
@@ -1413,13 +1536,19 @@ waklog_phase0 (request_rec * r)
 
   cfg = retrieve_config(r);
 
-  if ( cfg->protect && cfg->principal ) {
+  if ( get_cfg_protect(cfg) && 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 {
+
+    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: phase0 not doing nothin.");
   }
 
@@ -1436,7 +1565,7 @@ waklog_phase1 (request_rec * r)
 
   cfg = retrieve_config(r);
 
-  if ( cfg->protect && cfg->principal ) {
+  if ( get_cfg_protect(cfg) && 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 ) {
@@ -1459,7 +1588,7 @@ waklog_phase3 (request_rec * r)
 
   cfg = retrieve_config(r);
   
-  if ( cfg->protect && cfg->principal ) {
+  if ( get_cfg_protect(cfg) && 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 ) {
@@ -1482,7 +1611,7 @@ waklog_phase6 (request_rec * r)
 
   cfg = retrieve_config(r);
   
-  if ( cfg->protect && cfg->principal ) {
+  if ( get_cfg_protect(cfg) && 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 ) {
@@ -1506,15 +1635,21 @@ waklog_phase7 (request_rec * r)
 
   cfg = retrieve_config (r);
 
-  if ( cfg->protect && cfg->usertokens ) {
+  if ( get_cfg_protect(cfg) && get_cfg_usertokens(cfg) ) {
     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 ) {
+  } else if ( get_cfg_protect(cfg) && 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);
+  } else {
+    log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mod_waklog: no tokens");
+    if (child.token.ticketLen) {
+       memset(&child.token, 0, sizeof(struct ktc_token));
+       ktc_ForgetAllTokens();
+    }
   }
   
   if ( rc ) {
@@ -1628,7 +1763,7 @@ module MODULE_VAR_EXPORT waklog_module = {
   waklog_phase6,                      /* [#6] determine MIME type            */
   waklog_phase7,                      /* [#7] pre-run fixups                 */
   waklog_phase9,                      /* [#9] log a transaction              */
-  NULL,                               /* [#2] header parser                  */
+  waklog_phase2,                      /* [#2] header parser                  */
   waklog_child_init,                  /* child_init                          */
   waklog_child_exit,                  /* child_exit                          */
   waklog_phase0                       /* [#0] post read-request              */