Set KRB5CCNAME on follow up requests with session
authorSimo Sorce <simo@redhat.com>
Sat, 11 Apr 2015 21:27:21 +0000 (17:27 -0400)
committerSimo Sorce <simo@redhat.com>
Tue, 9 Jun 2015 22:10:22 +0000 (18:10 -0400)
If the original context establishment delegated credentials, set
the KRB5CCNAME variable to the proper file name for follow up
connections that uses the session to validate access.

Closes #18

Signed-off-by: Simo Sorce <simo@redhat.com>
src/asn1c/BOOLEAN.c [new file with mode: 0644]
src/asn1c/BOOLEAN.h [new file with mode: 0644]
src/asn1c/GSSSessionData.c
src/asn1c/GSSSessionData.h
src/asn1c/Makefile.am
src/asn1c/session.asn1
src/mod_auth_gssapi.c
src/mod_auth_gssapi.h
src/sessions.c

diff --git a/src/asn1c/BOOLEAN.c b/src/asn1c/BOOLEAN.c
new file mode 100644 (file)
index 0000000..1b74ea1
--- /dev/null
@@ -0,0 +1,282 @@
+/*-
+ * Copyright (c) 2003, 2005 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#include <asn_internal.h>
+#include <asn_codecs_prim.h>
+#include <BOOLEAN.h>
+
+/*
+ * BOOLEAN basic type description.
+ */
+static ber_tlv_tag_t asn_DEF_BOOLEAN_tags[] = {
+       (ASN_TAG_CLASS_UNIVERSAL | (1 << 2))
+};
+asn_TYPE_descriptor_t asn_DEF_BOOLEAN = {
+       "BOOLEAN",
+       "BOOLEAN",
+       BOOLEAN_free,
+       BOOLEAN_print,
+       asn_generic_no_constraint,
+       BOOLEAN_decode_ber,
+       BOOLEAN_encode_der,
+       BOOLEAN_decode_xer,
+       BOOLEAN_encode_xer,
+       BOOLEAN_decode_uper,    /* Unaligned PER decoder */
+       BOOLEAN_encode_uper,    /* Unaligned PER encoder */
+       0, /* Use generic outmost tag fetcher */
+       asn_DEF_BOOLEAN_tags,
+       sizeof(asn_DEF_BOOLEAN_tags) / sizeof(asn_DEF_BOOLEAN_tags[0]),
+       asn_DEF_BOOLEAN_tags,   /* Same as above */
+       sizeof(asn_DEF_BOOLEAN_tags) / sizeof(asn_DEF_BOOLEAN_tags[0]),
+       0,      /* No PER visible constraints */
+       0, 0,   /* No members */
+       0       /* No specifics */
+};
+
+/*
+ * Decode BOOLEAN type.
+ */
+asn_dec_rval_t
+BOOLEAN_decode_ber(asn_codec_ctx_t *opt_codec_ctx,
+               asn_TYPE_descriptor_t *td,
+               void **bool_value, const void *buf_ptr, size_t size,
+               int tag_mode) {
+       BOOLEAN_t *st = (BOOLEAN_t *)*bool_value;
+       asn_dec_rval_t rval;
+       ber_tlv_len_t length;
+       ber_tlv_len_t lidx;
+
+       if(st == NULL) {
+               st = (BOOLEAN_t *)(*bool_value = CALLOC(1, sizeof(*st)));
+               if(st == NULL) {
+                       rval.code = RC_FAIL;
+                       rval.consumed = 0;
+                       return rval;
+               }
+       }
+
+       ASN_DEBUG("Decoding %s as BOOLEAN (tm=%d)",
+               td->name, tag_mode);
+
+       /*
+        * Check tags.
+        */
+       rval = ber_check_tags(opt_codec_ctx, td, 0, buf_ptr, size,
+               tag_mode, 0, &length, 0);
+       if(rval.code != RC_OK)
+               return rval;
+
+       ASN_DEBUG("Boolean length is %d bytes", (int)length);
+
+       buf_ptr = ((const char *)buf_ptr) + rval.consumed;
+       size -= rval.consumed;
+       if(length > (ber_tlv_len_t)size) {
+               rval.code = RC_WMORE;
+               rval.consumed = 0;
+               return rval;
+       }
+
+       /*
+        * Compute boolean value.
+        */
+       for(*st = 0, lidx = 0;
+               (lidx < length) && *st == 0; lidx++) {
+               /*
+                * Very simple approach: read bytes until the end or
+                * value is already TRUE.
+                * BOOLEAN is not supposed to contain meaningful data anyway.
+                */
+               *st |= ((const uint8_t *)buf_ptr)[lidx];
+       }
+
+       rval.code = RC_OK;
+       rval.consumed += length;
+
+       ASN_DEBUG("Took %ld/%ld bytes to encode %s, value=%d",
+               (long)rval.consumed, (long)length,
+               td->name, *st);
+       
+       return rval;
+}
+
+asn_enc_rval_t
+BOOLEAN_encode_der(asn_TYPE_descriptor_t *td, void *sptr,
+       int tag_mode, ber_tlv_tag_t tag,
+       asn_app_consume_bytes_f *cb, void *app_key) {
+       asn_enc_rval_t erval;
+       BOOLEAN_t *st = (BOOLEAN_t *)sptr;
+
+       erval.encoded = der_write_tags(td, 1, tag_mode, 0, tag, cb, app_key);
+       if(erval.encoded == -1) {
+               erval.failed_type = td;
+               erval.structure_ptr = sptr;
+               return erval;
+       }
+
+       if(cb) {
+               uint8_t bool_value;
+
+               bool_value = *st ? 0xff : 0; /* 0xff mandated by DER */
+
+               if(cb(&bool_value, 1, app_key) < 0) {
+                       erval.encoded = -1;
+                       erval.failed_type = td;
+                       erval.structure_ptr = sptr;
+                       return erval;
+               }
+       }
+
+       erval.encoded += 1;
+
+       _ASN_ENCODED_OK(erval);
+}
+
+
+/*
+ * Decode the chunk of XML text encoding INTEGER.
+ */
+static enum xer_pbd_rval
+BOOLEAN__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, const void *chunk_buf, size_t chunk_size) {
+       BOOLEAN_t *st = (BOOLEAN_t *)sptr;
+       const char *p = (const char *)chunk_buf;
+
+       (void)td;
+
+       if(chunk_size && p[0] == 0x3c /* '<' */) {
+               switch(xer_check_tag(chunk_buf, chunk_size, "false")) {
+               case XCT_BOTH:
+                       /* "<false/>" */
+                       *st = 0;
+                       break;
+               case XCT_UNKNOWN_BO:
+                       if(xer_check_tag(chunk_buf, chunk_size, "true")
+                                       != XCT_BOTH)
+                               return XPBD_BROKEN_ENCODING;
+                       /* "<true/>" */
+                       *st = 1;        /* Or 0xff as in DER?.. */
+                       break;
+               default:
+                       return XPBD_BROKEN_ENCODING;
+               }
+               return XPBD_BODY_CONSUMED;
+       } else {
+               return XPBD_BROKEN_ENCODING;
+       }
+}
+
+
+asn_dec_rval_t
+BOOLEAN_decode_xer(asn_codec_ctx_t *opt_codec_ctx,
+       asn_TYPE_descriptor_t *td, void **sptr, const char *opt_mname,
+               const void *buf_ptr, size_t size) {
+
+       return xer_decode_primitive(opt_codec_ctx, td,
+               sptr, sizeof(BOOLEAN_t), opt_mname, buf_ptr, size,
+               BOOLEAN__xer_body_decode);
+}
+
+asn_enc_rval_t
+BOOLEAN_encode_xer(asn_TYPE_descriptor_t *td, void *sptr,
+       int ilevel, enum xer_encoder_flags_e flags,
+               asn_app_consume_bytes_f *cb, void *app_key) {
+       const BOOLEAN_t *st = (const BOOLEAN_t *)sptr;
+       asn_enc_rval_t er;
+
+       (void)ilevel;
+       (void)flags;
+
+       if(!st) _ASN_ENCODE_FAILED;
+
+       if(*st) {
+               _ASN_CALLBACK("<true/>", 7);
+               er.encoded = 7;
+       } else {
+               _ASN_CALLBACK("<false/>", 8);
+               er.encoded = 8;
+       }
+
+       _ASN_ENCODED_OK(er);
+cb_failed:
+       _ASN_ENCODE_FAILED;
+}
+
+int
+BOOLEAN_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel,
+       asn_app_consume_bytes_f *cb, void *app_key) {
+       const BOOLEAN_t *st = (const BOOLEAN_t *)sptr;
+       const char *buf;
+       size_t buflen;
+
+       (void)td;       /* Unused argument */
+       (void)ilevel;   /* Unused argument */
+
+       if(st) {
+               if(*st) {
+                       buf = "TRUE";
+                       buflen = 4;
+               } else {
+                       buf = "FALSE";
+                       buflen = 5;
+               }
+       } else {
+               buf = "<absent>";
+               buflen = 8;
+       }
+
+       return (cb(buf, buflen, app_key) < 0) ? -1 : 0;
+}
+
+void
+BOOLEAN_free(asn_TYPE_descriptor_t *td, void *ptr, int contents_only) {
+       if(td && ptr && !contents_only) {
+               FREEMEM(ptr);
+       }
+}
+
+asn_dec_rval_t
+BOOLEAN_decode_uper(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td,
+       asn_per_constraints_t *constraints, void **sptr, asn_per_data_t *pd) {
+       asn_dec_rval_t rv;
+       BOOLEAN_t *st = (BOOLEAN_t *)*sptr;
+
+       (void)opt_codec_ctx;
+       (void)constraints;
+
+       if(!st) {
+               st = (BOOLEAN_t *)(*sptr = MALLOC(sizeof(*st)));
+               if(!st) _ASN_DECODE_FAILED;
+       }
+
+       /*
+        * Extract a single bit
+        */
+       switch(per_get_few_bits(pd, 1)) {
+       case 1: *st = 1; break;
+       case 0: *st = 0; break;
+       case -1: default: _ASN_DECODE_STARVED;
+       }
+
+       ASN_DEBUG("%s decoded as %s", td->name, *st ? "TRUE" : "FALSE");
+
+       rv.code = RC_OK;
+       rv.consumed = 1;
+       return rv;
+}
+
+
+asn_enc_rval_t
+BOOLEAN_encode_uper(asn_TYPE_descriptor_t *td,
+       asn_per_constraints_t *constraints, void *sptr, asn_per_outp_t *po) {
+       const BOOLEAN_t *st = (const BOOLEAN_t *)sptr;
+       asn_enc_rval_t er = { 0, 0, 0 };
+
+       (void)constraints;
+
+       if(!st) _ASN_ENCODE_FAILED;
+
+       if(per_put_few_bits(po, *st ? 1 : 0, 1))
+               _ASN_ENCODE_FAILED;
+
+       _ASN_ENCODED_OK(er);
+}
diff --git a/src/asn1c/BOOLEAN.h b/src/asn1c/BOOLEAN.h
new file mode 100644 (file)
index 0000000..217d0f1
--- /dev/null
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#ifndef        _BOOLEAN_H_
+#define        _BOOLEAN_H_
+
+#include <asn_application.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The underlying integer may contain various values, but everything
+ * non-zero is capped to 0xff by the DER encoder. The BER decoder may
+ * yield non-zero values different from 1, beware.
+ */
+typedef int BOOLEAN_t;
+
+extern asn_TYPE_descriptor_t asn_DEF_BOOLEAN;
+
+asn_struct_free_f BOOLEAN_free;
+asn_struct_print_f BOOLEAN_print;
+ber_type_decoder_f BOOLEAN_decode_ber;
+der_type_encoder_f BOOLEAN_encode_der;
+xer_type_decoder_f BOOLEAN_decode_xer;
+xer_type_encoder_f BOOLEAN_encode_xer;
+per_type_decoder_f BOOLEAN_decode_uper;
+per_type_encoder_f BOOLEAN_encode_uper;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _BOOLEAN_H_ */
index 5c8ec17..0f20581 100644 (file)
@@ -8,9 +8,27 @@
 #include "GSSSessionData.h"
 
 static asn_TYPE_member_t asn_MBR_GSSSessionData_1[] = {
-       { ATF_NOFLAGS, 0, offsetof(struct GSSSessionData, expiration),
+       { ATF_NOFLAGS, 0, offsetof(struct GSSSessionData, established),
                (ASN_TAG_CLASS_CONTEXT | (0 << 2)),
                +1,     /* EXPLICIT tag at current level */
+               &asn_DEF_BOOLEAN,
+               0,      /* Defer constraints checking to the member type */
+               0,      /* PER is not compiled, use -gen-PER */
+               0,
+               "established"
+               },
+       { ATF_NOFLAGS, 0, offsetof(struct GSSSessionData, delegated),
+               (ASN_TAG_CLASS_CONTEXT | (1 << 2)),
+               +1,     /* EXPLICIT tag at current level */
+               &asn_DEF_BOOLEAN,
+               0,      /* Defer constraints checking to the member type */
+               0,      /* PER is not compiled, use -gen-PER */
+               0,
+               "delegated"
+               },
+       { ATF_NOFLAGS, 0, offsetof(struct GSSSessionData, expiration),
+               (ASN_TAG_CLASS_CONTEXT | (2 << 2)),
+               +1,     /* EXPLICIT tag at current level */
                &asn_DEF_Uint32,
                0,      /* Defer constraints checking to the member type */
                0,      /* PER is not compiled, use -gen-PER */
@@ -18,7 +36,7 @@ static asn_TYPE_member_t asn_MBR_GSSSessionData_1[] = {
                "expiration"
                },
        { ATF_NOFLAGS, 0, offsetof(struct GSSSessionData, username),
-               (ASN_TAG_CLASS_CONTEXT | (1 << 2)),
+               (ASN_TAG_CLASS_CONTEXT | (3 << 2)),
                +1,     /* EXPLICIT tag at current level */
                &asn_DEF_OCTET_STRING,
                0,      /* Defer constraints checking to the member type */
@@ -27,7 +45,7 @@ static asn_TYPE_member_t asn_MBR_GSSSessionData_1[] = {
                "username"
                },
        { ATF_NOFLAGS, 0, offsetof(struct GSSSessionData, gssname),
-               (ASN_TAG_CLASS_CONTEXT | (2 << 2)),
+               (ASN_TAG_CLASS_CONTEXT | (4 << 2)),
                +1,     /* EXPLICIT tag at current level */
                &asn_DEF_OCTET_STRING,
                0,      /* Defer constraints checking to the member type */
@@ -40,15 +58,17 @@ static ber_tlv_tag_t asn_DEF_GSSSessionData_tags_1[] = {
        (ASN_TAG_CLASS_UNIVERSAL | (16 << 2))
 };
 static asn_TYPE_tag2member_t asn_MAP_GSSSessionData_tag2el_1[] = {
-    { (ASN_TAG_CLASS_CONTEXT | (0 << 2)), 0, 0, 0 }, /* expiration */
-    { (ASN_TAG_CLASS_CONTEXT | (1 << 2)), 1, 0, 0 }, /* username */
-    { (ASN_TAG_CLASS_CONTEXT | (2 << 2)), 2, 0, 0 } /* gssname */
+    { (ASN_TAG_CLASS_CONTEXT | (0 << 2)), 0, 0, 0 }, /* established */
+    { (ASN_TAG_CLASS_CONTEXT | (1 << 2)), 1, 0, 0 }, /* delegated */
+    { (ASN_TAG_CLASS_CONTEXT | (2 << 2)), 2, 0, 0 }, /* expiration */
+    { (ASN_TAG_CLASS_CONTEXT | (3 << 2)), 3, 0, 0 }, /* username */
+    { (ASN_TAG_CLASS_CONTEXT | (4 << 2)), 4, 0, 0 } /* gssname */
 };
 static asn_SEQUENCE_specifics_t asn_SPC_GSSSessionData_specs_1 = {
        sizeof(struct GSSSessionData),
        offsetof(struct GSSSessionData, _asn_ctx),
        asn_MAP_GSSSessionData_tag2el_1,
-       3,      /* Count of tags in the map */
+       5,      /* Count of tags in the map */
        0, 0, 0,        /* Optional elements (not needed) */
        -1,     /* Start extensions */
        -1      /* Stop extensions */
@@ -73,7 +93,7 @@ asn_TYPE_descriptor_t asn_DEF_GSSSessionData = {
                /sizeof(asn_DEF_GSSSessionData_tags_1[0]), /* 1 */
        0,      /* No PER visible constraints */
        asn_MBR_GSSSessionData_1,
-       3,      /* Elements count */
+       5,      /* Elements count */
        &asn_SPC_GSSSessionData_specs_1 /* Additional specs */
 };
 
index b4e2e45..423996f 100644 (file)
@@ -12,6 +12,7 @@
 #include <asn_application.h>
 
 /* Including external dependencies */
+#include <BOOLEAN.h>
 #include "Uint32.h"
 #include <OCTET_STRING.h>
 #include <constr_SEQUENCE.h>
@@ -22,6 +23,8 @@ extern "C" {
 
 /* GSSSessionData */
 typedef struct GSSSessionData {
+       BOOLEAN_t        established;
+       BOOLEAN_t        delegated;
        Uint32_t         expiration;
        OCTET_STRING_t   username;
        OCTET_STRING_t   gssname;
index 8d6c81d..d116e58 100644 (file)
@@ -6,6 +6,7 @@ ASN1C_SOURCES =                 \
        ber_tlv_length.c        \
        ber_tlv_tag.c           \
        BIT_STRING.c            \
+       BOOLEAN.c               \
        constraints.c           \
        constr_SEQUENCE.c       \
        constr_TYPE.c           \
@@ -30,6 +31,7 @@ ASN1C_SOURCES =                       \
        ber_tlv_length.h        \
        ber_tlv_tag.h           \
        BIT_STRING.h            \
+       BOOLEAN.h               \
        constraints.h           \
        constr_SEQUENCE.h       \
        constr_TYPE.h           \
index 8f17e47..8d7b4e5 100644 (file)
@@ -3,8 +3,10 @@ GssapiSessionModule DEFINITIONS ::= BEGIN
     Uint32 ::= INTEGER (0..4294967295)
 
     GSSSessionData ::= SEQUENCE {
-        expiration  [0] Uint32,
-        username    [1] OCTET STRING,
-        gssname     [2] OCTET STRING
+        established [0] BOOLEAN,
+        delegated   [1] BOOLEAN,
+        expiration  [2] Uint32,
+        username    [3] OCTET STRING,
+        gssname     [4] OCTET STRING
     }
 END
index a88b653..79d62cd 100644 (file)
@@ -191,33 +191,54 @@ static char *escape(apr_pool_t *pool, const char *name,
     return escaped;
 }
 
-static void mag_store_deleg_creds(request_rec *req,
-                                  char *dir, char *clientname,
-                                  gss_cred_id_t delegated_cred,
-                                  char **ccachefile)
+static char *mag_gss_name_to_ccache_name(request_rec *req,
+                                         char *dir, const char *gss_name)
 {
-    gss_key_value_element_desc element;
-    gss_key_value_set_desc store;
-    char *value;
-    uint32_t maj, min;
     char *escaped;
 
     /* We need to escape away '/', we can't have path separators in
      * a ccache file name */
     /* first double escape the esacping char (~) if any */
-    escaped = escape(req->pool, clientname, '~', "~~");
-    if (!escaped) return;
+    escaped = escape(req->pool, gss_name, '~', "~~");
     /* then escape away the separator (/) if any */
     escaped = escape(req->pool, escaped, '/', "~");
-    if (!escaped) return;
 
-    value = apr_psprintf(req->pool, "FILE:%s/%s", dir, escaped);
+    return apr_psprintf(req->pool, "%s/%s", dir, escaped);
+}
+
+static void mag_set_KRB5CCANME(request_rec *req, char *ccname)
+{
+    apr_status_t status;
+    apr_finfo_t finfo;
+    char *value;
 
+    status = apr_stat(&finfo, ccname, APR_FINFO_MIN, req->pool);
+    if (status != APR_SUCCESS && status != APR_INCOMPLETE) {
+        /* set the file cache anyway, but warn */
+        ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
+                      "KRB5CCNAME file (%s) lookup failed!", ccname);
+    }
+
+    value = apr_psprintf(req->pool, "FILE:%s", ccname);
+    apr_table_set(req->subprocess_env, "KRB5CCNAME", value);
+}
+
+static void mag_store_deleg_creds(request_rec *req,
+                                  char *dir, char *clientname,
+                                  gss_cred_id_t delegated_cred,
+                                  char **ccachefile)
+{
+    gss_key_value_element_desc element;
+    gss_key_value_set_desc store;
+    char *ccname;
+    uint32_t maj, min;
     element.key = "ccache";
-    element.value = value;
     store.elements = &element;
     store.count = 1;
 
+    ccname = mag_gss_name_to_ccache_name(req, dir, clientname);
+    element.value = apr_psprintf(req->pool, "FILE:%s", ccname);
+
     maj = gss_store_cred_into(&min, delegated_cred, GSS_C_INITIATE,
                               GSS_C_NULL_OID, 1, 1, &store, NULL, NULL);
     if (GSS_ERROR(maj)) {
@@ -226,7 +247,7 @@ static void mag_store_deleg_creds(request_rec *req,
                                 maj, min));
     }
 
-    *ccachefile = value;
+    *ccachefile = ccname;
 }
 #endif
 
@@ -392,6 +413,15 @@ static int mag_auth(request_rec *req)
             req->ap_auth_type = apr_pstrdup(req->pool,
                                             auth_types[mc->auth_type]);
             req->user = apr_pstrdup(req->pool, mc->user_name);
+            if (cfg->deleg_ccache_dir && mc->delegated) {
+                char *ccname;
+                ccname = mag_gss_name_to_ccache_name(req,
+                                                     cfg->deleg_ccache_dir,
+                                                     mc->gss_name);
+                if (ccname) {
+                    mag_set_KRB5CCANME(req, ccname);
+                }
+            }
             ret = OK;
             goto done;
         }
@@ -644,7 +674,11 @@ static int mag_auth(request_rec *req)
                               delegated_cred, &ccachefile);
 
         if (ccachefile) {
-            apr_table_set(req->subprocess_env, "KRB5CCNAME", ccachefile);
+            mag_set_KRB5CCANME(req, ccachefile);
+        }
+
+        if (mc) {
+            mc->delegated = true;
         }
     }
 #endif
index 103ec61..97ba2c8 100644 (file)
@@ -67,6 +67,7 @@ struct mag_conn {
     const char *gss_name;
     time_t expiration;
     int auth_type;
+    bool delegated;
 };
 
 #define discard_const(ptr) ((void *)((uintptr_t)(ptr)))
index e8c79cd..f90857c 100644 (file)
@@ -153,6 +153,10 @@ void mag_check_session(request_rec *req,
         return;
     }
 
+    /* booleans */
+    if (gsessdata->established != 0) mc->established = true;
+    if (gsessdata->delegated != 0) mc->delegated = true;
+
     /* get time */
     expiration = gsessdata->expiration;
     if (expiration < time(NULL)) {
@@ -211,6 +215,8 @@ void mag_attempt_session(request_rec *req,
         }
     }
 
+    gsessdata.established = mc->established?1:0;
+    gsessdata.delegated = mc->delegated?1:0;
     gsessdata.expiration = mc->expiration;
     if (OCTET_STRING_fromString(&gsessdata.username, mc->user_name) != 0)
         goto done;