Search Mailing List Archives


Limit search to: Subject & Body Subject Author
Sort by: Reverse Sort
Limit to: All This Week Last Week This Month Last Month
Select Date Range     through    

[PATCH] Support keyring credential caches

Benjamin Coddington bcodding at uvm.edu
Thu May 10 12:25:07 PDT 2012


MIT Kerberos defines the keyring ccache type which can protect
credentials from sibling processes.  On systems with libkeyutils,
override WebAuthCredCacheDir to enable protected session-linked
keyring credential caches.
---
 config-mod.h.in          |    3 +
 configure.ac             |    2 +
 include/webauth.h        |   10 +++
 lib/krb5.c               |   42 ++++++++++++
 lib/libwebauth.map       |    1 +
 lib/libwebauth.sym       |    1 +
 modules/webauth/config.c |    6 ++
 modules/webauth/krb5.c   |  158 +++++++++++++++++++++++++++++++++++++++-------
 8 files changed, 200 insertions(+), 23 deletions(-)

diff --git a/config-mod.h.in b/config-mod.h.in
index e46aade..36e5f7c 100644
--- a/config-mod.h.in
+++ b/config-mod.h.in
@@ -59,6 +59,9 @@
 /* Define to 1 if you have the `krb5_xfree' function. */
 #undef HAVE_KRB5_XFREE
 
+/* Define to 1 if you have the `keyutils' library (-lkeyutils). */
+#undef HAVE_LIBKEYUTILS
+
 /* Define to 1 if `useragent_ip' is a member of `request_rec'. */
 #undef HAVE_REQUEST_REC_USERAGENT_IP
 
diff --git a/configure.ac b/configure.ac
index 9bfd20c..8a711fc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -137,6 +137,8 @@ AS_IF([test x"$rra_reduced_depends" = xtrue],
      DEPEND_LIBS="$DEPEND_LIBS $CRYPTO_LDFLAGS $CRYPTO_LIBS"])
 AC_SUBST([DEPEND_LIBS])
 
+AC_CHECK_LIB([keyutils], [add_key, keyctl_search, keyctl_setperm])
+
 AC_CONFIG_FILES([Makefile tests/data/conf-webkdc])
 AC_CONFIG_HEADERS([config.h config-mod.h])
 AS_IF([test x"$build_perl" = xtrue], [AC_CONFIG_FILES([perl/Makefile.PL])])
diff --git a/include/webauth.h b/include/webauth.h
index 4275b13..10857cd 100644
--- a/include/webauth.h
+++ b/include/webauth.h
@@ -699,6 +699,16 @@ int webauth_krb5_init_via_cred(WEBAUTH_KRB5_CTXT *, const void *cred,
                                size_t cred_len, const char *cache_name);
 
 /*
+ * Initialize a context from a credential that was created via
+ * webauth_krb5_export_tgt or webauth_krb5_export_ticket, but do not import
+ * the credential.
+ *
+ * Returns WA_ERR_NONE or WA_ERR_KRB5.
+ */
+int webauth_krb5_prepare_via_cred(WEBAUTH_KRB5_CTXT *, const void *cred,
+                                  size_t cred_len, const char *cache_name);
+
+/*
  * Export the TGT from the context and also store the expiration time.  This
  * is used to construct a proxy-token after a call to
  * webauth_krb5_init_via_password or webauth_krb5_init_via_tgt.  Memory
diff --git a/lib/krb5.c b/lib/krb5.c
index 89b6124..d8c3ada 100644
--- a/lib/krb5.c
+++ b/lib/krb5.c
@@ -807,6 +807,48 @@ webauth_krb5_init_via_cred(WEBAUTH_KRB5_CTXT *context, const void *cred,
         return WA_ERR_NONE;
 }
 
+/*
+ * Initialize a context from a passed, delegated credential, but do
+ * not import the credential.
+ */
+
+int
+webauth_krb5_prepare_via_cred(WEBAUTH_KRB5_CTXT *context, const void *cred,
+                              size_t cred_len, const char *cache_name)
+{
+    WEBAUTH_KRB5_CTXTP *c = (WEBAUTH_KRB5_CTXTP *) context;
+    krb5_creds creds;
+    char ccname[128];
+    int s;
+
+    assert(c != NULL);
+    assert(cred != NULL);
+
+    if (cache_name == NULL) {
+        snprintf(ccname, sizeof(ccname), "MEMORY:%p", c);
+        cache_name = ccname;
+    }
+
+    s = cred_from_attr_encoding(c, cred, cred_len, &creds);
+
+    if (s != WA_ERR_NONE)
+        return s;
+
+    c->code = krb5_cc_resolve(c->ctx, cache_name, &c->cc);
+    if (c->code != 0)
+        return WA_ERR_KRB5;
+
+    c->code = krb5_copy_principal(c->ctx, creds.client, &c->princ);
+    if (c->code != 0)
+        return WA_ERR_KRB5;
+
+    c->code = krb5_cc_initialize(c->ctx, c->cc, c->princ);
+    if (c->code != 0)
+        return WA_ERR_KRB5;
+
+    return WA_ERR_NONE;
+}
+
 
 /*
  * Import a credential into an existing ticket cache.
diff --git a/lib/libwebauth.map b/lib/libwebauth.map
index c1ed4ec..83afe18 100644
--- a/lib/libwebauth.map
+++ b/lib/libwebauth.map
@@ -47,6 +47,7 @@ WEBAUTH_3 {
         webauth_krb5_import_cred;
         webauth_krb5_init_via_cache;
         webauth_krb5_init_via_cred;
+        webauth_krb5_prepare_via_cred;
         webauth_krb5_init_via_keytab;
         webauth_krb5_init_via_password;
         webauth_krb5_keep_cred_cache;
diff --git a/lib/libwebauth.sym b/lib/libwebauth.sym
index 58600b9..76d95db 100644
--- a/lib/libwebauth.sym
+++ b/lib/libwebauth.sym
@@ -56,6 +56,7 @@ webauth_krb5_get_realm
 webauth_krb5_import_cred
 webauth_krb5_init_via_cache
 webauth_krb5_init_via_cred
+webauth_krb5_prepare_via_cred
 webauth_krb5_init_via_keytab
 webauth_krb5_init_via_password
 webauth_krb5_keep_cred_cache
diff --git a/modules/webauth/config.c b/modules/webauth/config.c
index fc288c5..23d1910 100644
--- a/modules/webauth/config.c
+++ b/modules/webauth/config.c
@@ -463,6 +463,12 @@ cfg_str(cmd_parms *cmd, void *mconf, const char *arg)
         sconf->auth_type = apr_pstrdup(cmd->pool, arg);
         break;
     case E_CredCacheDir:
+#ifdef HAVE_LIBKEYUTILS
+        if (strncmp(arg, "KEYRING:", 8) == 0) {
+            sconf->cred_cache_dir = apr_pstrdup(cmd->pool, arg);
+            break;
+        }
+#endif
         sconf->cred_cache_dir = ap_server_root_relative(cmd->pool, arg);
         break;
     case E_Keyring:
diff --git a/modules/webauth/krb5.c b/modules/webauth/krb5.c
index a79694d..d69c07b 100644
--- a/modules/webauth/krb5.c
+++ b/modules/webauth/krb5.c
@@ -20,6 +20,10 @@
 #include <webauth/basic.h>
 #include <webauth/tokens.h>
 
+#ifdef HAVE_LIBKEYUTILS
+#include <keyutils.h>
+#endif
+
 
 static void
 log_webauth_error(server_rec *s, int status, WEBAUTH_KRB5_CTXT *ctxt,
@@ -106,29 +110,21 @@ krb5_validate_sad(MWA_REQ_CTXT *rc, const void *sad, size_t sad_len)
  * called when the request pool gets cleaned up
  */
 static apr_status_t
-cred_cache_destroy(void *data)
+krb5_cleanup_context(void *data)
 {
-    char *path = (char*)data;
+    WEBAUTH_KRB5_CTXT *ctxt = (WEBAUTH_KRB5_CTXT *)data;
     /*
     ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
-                 "mod_webauth: cleanup cred: %s", path);
+                 "mod_webauth: cleanup ctxt: %p", ctxt);
     */
-    if (unlink(path) == -1) {
-        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
-                     "mod_webauth: cleanup cred: unlink(%s) errno(%d)",
-                     path, errno);
-    }
+    webauth_krb5_free(ctxt);
     return APR_SUCCESS;
 }
 
-
-/*
- * prepare any krb5 creds
- */
 static int
-krb5_prepare_creds(MWA_REQ_CTXT *rc, apr_array_header_t *creds)
+krb5_prepare_file_creds(MWA_REQ_CTXT *rc, apr_array_header_t *creds)
 {
-    const char *mwa_func="krb5_prepare_creds";
+    const char *mwa_func="krb5_prepare_file_creds";
     WEBAUTH_KRB5_CTXT *ctxt;
     size_t i;
     int status;
@@ -136,12 +132,6 @@ krb5_prepare_creds(MWA_REQ_CTXT *rc, apr_array_header_t *creds)
     apr_file_t *fp;
     apr_status_t astatus;
 
-    if (rc->sconf->cred_cache_dir == NULL) {
-        ap_log_error(APLOG_MARK, APLOG_ERR, 0, rc->r->server,
-                     "mod_webauth: WebAuthCredCacheDir is not set");
-        return 0;
-    }
-
     astatus = apr_filepath_merge(&temp_cred_file,
                                  rc->sconf->cred_cache_dir,
                                  "temp.krb5.XXXXXX",
@@ -166,7 +156,7 @@ krb5_prepare_creds(MWA_REQ_CTXT *rc, apr_array_header_t *creds)
     }
 
     apr_pool_cleanup_register(rc->r->pool, temp_cred_file,
-                              cred_cache_destroy,
+                              krb5_cleanup_context,
                               apr_pool_cleanup_null);
 
     if (rc->sconf->debug)
@@ -178,8 +168,6 @@ krb5_prepare_creds(MWA_REQ_CTXT *rc, apr_array_header_t *creds)
     if (ctxt == NULL)
         return 0;
 
-    webauth_krb5_keep_cred_cache(ctxt);
-
     for (i = 0; i < (size_t) creds->nelts; i++) {
         struct webauth_token_cred *cred;
 
@@ -213,6 +201,130 @@ krb5_prepare_creds(MWA_REQ_CTXT *rc, apr_array_header_t *creds)
     return 1;
 }
 
+#ifdef HAVE_LIBKEYUTILS
+static int
+krb5_prepare_keyring_creds(MWA_REQ_CTXT *rc, apr_array_header_t *creds)
+{
+    const char *mwa_func="krb5_prepare_keyring_creds";
+    WEBAUTH_KRB5_CTXT *ctxt;
+    size_t i;
+    int status;
+    const char *kr_ccache_name;
+    key_serial_t kr_ccache = 0;
+    key_serial_t key;
+
+    kr_ccache_name = rc->sconf->cred_cache_dir;
+
+    if (rc->sconf->debug)
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, rc->r->server,
+                     "mod_webauth: %s: krb5 keyring cache %s)",
+                     mwa_func, kr_ccache_name);
+
+    ctxt = get_webauth_krb5_ctxt(rc->r->server, mwa_func);
+    if (ctxt == NULL)
+        return 0;
+
+    /* register a pool cleanup handler */
+    apr_pool_cleanup_register(rc->r->pool, ctxt,
+                              krb5_cleanup_context,
+                              apr_pool_cleanup_null);
+
+    for (i = 0; i < (size_t) creds->nelts; i++) {
+        struct webauth_token_cred *cred;
+
+        cred = APR_ARRAY_IDX(creds, i, struct webauth_token_cred *);
+
+        // sanity
+        if (strcmp(cred->type, "krb5") != 0)
+            continue;
+
+        /* In order to enforce possessor only permissions on our keyring
+         * ccache, create the keys and set permissions before krb5 fills the
+         * keys.  There is still a window between add_key and setperm where
+         * other procs can link the key to become a "possessor"..
+         */
+
+        if (kr_ccache == 0) {
+            if (rc->sconf->debug)
+                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, rc->r->server,
+                             "mod_webauth: %s: prepare (%s) for (%s)",
+                             mwa_func, cred->service, cred->subject);
+
+            kr_ccache = add_key("keyring", (const char *)kr_ccache_name + 8,
+                                NULL, 0, KEY_SPEC_SESSION_KEYRING);
+            if (kr_ccache < 0) {
+                status = errno;
+                ap_log_error(APLOG_MARK, APLOG_ERR, 0, rc->r->server,
+                      "mod_webauth: %s: failed to create ccache keyring %s: %s",
+                       mwa_func, kr_ccache_name, strerror(status));
+                return 0;
+            }
+
+            keyctl_setperm(kr_ccache, KEY_POS_ALL);
+
+            status = webauth_krb5_prepare_via_cred(ctxt,
+                                                   (void *) cred->data,
+                                                   cred->data_len,
+                                                   kr_ccache_name);
+            if (status != WA_ERR_NONE) {
+                log_webauth_error(rc->r->server, status, ctxt, mwa_func,
+                                  "webauth_krb5_prepare_via_cred", NULL);
+                return 0;
+            }
+
+            key = keyctl_search(kr_ccache, "user", "__krb5_princ__", 0);
+            if (key < 0) {
+                status = errno;
+                ap_log_error(APLOG_MARK, APLOG_ERR, 0, rc->r->server,
+                      "mod_webauth: %s: failed to find princ in keyring %d: %s",
+                       mwa_func, kr_ccache, strerror(status));
+                return 0;
+            }
+
+            keyctl_setperm(key, KEY_POS_ALL);
+         }
+
+        key = add_key("user", cred->service, "null", 4, kr_ccache);
+        if (key < 0) {
+            status = errno;
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, rc->r->server,
+                  "mod_webauth: %s: add_key failed for %s, keyring %d: %s",
+                   mwa_func, cred->service, kr_ccache, strerror(status));
+            continue;
+        }
+
+        keyctl_setperm(key, KEY_POS_ALL);
+
+        status = webauth_krb5_import_cred(ctxt, (void *) cred->data,
+                                          cred->data_len);
+        if (status != WA_ERR_NONE)
+            log_webauth_error(rc->r->server, status, ctxt, mwa_func,
+                              "webauth_krb5_import_cred", NULL);
+    }
+
+    /* set environment variable */
+    apr_table_setn(rc->r->subprocess_env, ENV_KRB5CCNAME, kr_ccache_name);
+    return 1;
+}
+#endif
+
+/*
+ * prepare any krb5 creds
+ */
+static int
+krb5_prepare_creds(MWA_REQ_CTXT *rc, apr_array_header_t *creds)
+{
+    if (rc->sconf->cred_cache_dir == NULL) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, rc->r->server,
+                     "mod_webauth: WebAuthCredCacheDir is not set");
+        return 0;
+    }
+#ifdef HAVE_LIBKEYUTILS
+    if (strncmp(rc->sconf->cred_cache_dir, "KEYRING:", 8) == 0)
+        return krb5_prepare_keyring_creds(rc, creds);
+#endif
+    return krb5_prepare_file_creds(rc, creds);
+}
 
 static const char *
 krb5_webkdc_credential(server_rec *server,
-- 
1.7.5.4



More information about the webauth-info mailing list