Add permanent session keys support
authorSimo Sorce <simo@redhat.com>
Mon, 7 Jul 2014 15:42:57 +0000 (11:42 -0400)
committerSimo Sorce <simo@redhat.com>
Thu, 10 Jul 2014 10:52:55 +0000 (06:52 -0400)
Keys (encryption+MAC) can now be stored in apache configuration.
The key must be a base64 encoded blob of original length of 32 bytes
(16 bytes for encryption and 16 for the MAC key)

The format is:
key:<base64 blob>

src/crypto.c
src/crypto.h
src/mod_auth_gssapi.c
src/sessions.c

index 9be58e5..584bf16 100644 (file)
@@ -13,56 +13,73 @@ struct seal_key {
     unsigned char *hkey;
 };
 
-apr_status_t SEAL_KEY_CREATE(struct seal_key **skey)
+apr_status_t SEAL_KEY_CREATE(apr_pool_t *p, struct seal_key **skey,
+                             struct databuf *keys)
 {
     struct seal_key *n;
+    int keylen;
     int ret;
 
-    n = calloc(1, sizeof(*n));
+    n = apr_pcalloc(p, sizeof(*n));
     if (!n) return ENOMEM;
 
     n->cipher = EVP_aes_128_cbc();
     if (!n->cipher) {
-        free(n);
-        return EFAULT;
+        ret = EFAULT;
+        goto done;
     }
 
+    keylen = n->cipher->key_len;
+
     n->md = EVP_sha256();
     if (!n->md) {
-        free(n);
-        return EFAULT;
+        ret = EFAULT;
+        goto done;
     }
 
-    n->ekey = malloc(n->cipher->key_len);
+    n->ekey = apr_palloc(p, keylen);
     if (!n->ekey) {
-        free(n);
-        return ENOMEM;
+        ret = ENOMEM;
+        goto done;
     }
 
-    n->hkey = malloc(n->cipher->key_len);
+    n->hkey = apr_palloc(p, keylen);
     if (!n->hkey) {
-        free(n);
-        return ENOMEM;
+        ret = ENOMEM;
+        goto done;
     }
 
-    ret = RAND_bytes(n->ekey, n->cipher->key_len);
-    if (ret == 0) {
-        free(n->ekey);
-        free(n->hkey);
-        free(n);
-        return EFAULT;
+    if (keys) {
+        if (keys->length != (keylen * 2)) {
+            ret = EINVAL;
+            goto done;
+        }
+        memcpy(n->ekey, keys->value, keylen);
+        memcpy(n->hkey, keys->value + keylen, keylen);
+    } else {
+        ret = RAND_bytes(n->ekey, keylen);
+        if (ret == 0) {
+            ret = EFAULT;
+            goto done;
+        }
+
+        ret = RAND_bytes(n->hkey, keylen);
+        if (ret == 0) {
+            ret = EFAULT;
+            goto done;
+        }
     }
 
-    ret = RAND_bytes(n->hkey, n->cipher->key_len);
-    if (ret == 0) {
+    ret = 0;
+done:
+    if (ret) {
         free(n->ekey);
         free(n->hkey);
         free(n);
-        return EFAULT;
+    } else {
+        *skey = n;
     }
-
-    *skey = n;
-    return 0;
+    return ret;
 }
 
 apr_status_t SEAL_BUFFER(apr_pool_t *p, struct seal_key *skey,
index a8b5ca0..cfc83fa 100644 (file)
@@ -10,7 +10,8 @@ struct databuf {
     int length;
 };
 
-apr_status_t SEAL_KEY_CREATE(struct seal_key **skey);
+apr_status_t SEAL_KEY_CREATE(apr_pool_t *p, struct seal_key **skey,
+                             struct databuf *keys);
 apr_status_t SEAL_BUFFER(apr_pool_t *p, struct seal_key *skey,
                          struct databuf *plain, struct databuf *cipher);
 apr_status_t UNSEAL_BUFFER(apr_pool_t *p, struct seal_key *skey,
index 7e8df96..ec78e82 100644 (file)
@@ -346,6 +346,47 @@ static const char *mag_use_sess(cmd_parms *parms, void *mconfig, int on)
     return NULL;
 }
 
+static const char *mag_sess_key(cmd_parms *parms, void *mconfig, const char *w)
+{
+    struct mag_config *cfg = (struct mag_config *)mconfig;
+    struct databuf keys;
+    unsigned char *val;
+    apr_status_t rc;
+    const char *k;
+    int l;
+
+    if (strncmp(w, "key:", 4) != 0) {
+        ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
+                     "Invalid key format, expected prefix 'key:'");
+        return NULL;
+    }
+    k = w + 4;
+
+    l = apr_base64_decode_len(k);
+    val = apr_palloc(parms->temp_pool, l);
+    if (!val) {
+        ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
+                     "Failed to get memory to decode key");
+        return NULL;
+    }
+
+    keys.length = (int)apr_base64_decode_binary(val, k);
+    keys.value = (unsigned char *)val;
+
+    if (keys.length != 32) {
+        ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
+                     "Invalid key lenght, expected 32 got %d", keys.length);
+        return NULL;
+    }
+
+    rc = SEAL_KEY_CREATE(cfg->pool, &cfg->mag_skey, &keys);
+    if (rc != OK) {
+        ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
+                     "Failed to import sealing key!");
+    }
+    return NULL;
+}
+
 static const char *mag_cred_store(cmd_parms *parms, void *mconfig,
                                   const char *w)
 {
@@ -401,6 +442,8 @@ static const command_rec mag_commands[] = {
                   "Authentication is bound to the TCP connection"),
     AP_INIT_FLAG("GssapiUseSessions", mag_use_sess, NULL, OR_AUTHCFG,
                   "Authentication uses mod_sessions to hold status"),
+    AP_INIT_RAW_ARGS("GssapiSessionKey", mag_sess_key, NULL, OR_AUTHCFG,
+                     "Key Used to seal session data."),
     AP_INIT_ITERATE("GssapiCredStore", mag_cred_store, NULL, OR_AUTHCFG,
                     "Credential Store"),
     { NULL }
index 766ca66..1086505 100644 (file)
@@ -147,7 +147,7 @@ void mag_attempt_session(request_rec *req,
     if (!cfg->mag_skey) {
         ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, req,
                       "Session key not available, generating new one.");
-        rc = SEAL_KEY_CREATE(&cfg->mag_skey);
+        rc = SEAL_KEY_CREATE(cfg->pool, &cfg->mag_skey, NULL);
         if (rc != OK) {
             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
                           "Failed to create sealing key!");