From: Simo Sorce Date: Sat, 11 Apr 2015 21:27:21 +0000 (-0400) Subject: Set KRB5CCNAME on follow up requests with session X-Git-Tag: v1.3.0~29 X-Git-Url: http://www.project-moonshot.org/gitweb/?p=mod_auth_gssapi.git;a=commitdiff_plain;h=8cabd1e6ac6c300f7e603cd61f1a8a7a7da7fb8f Set KRB5CCNAME on follow up requests with session 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 --- diff --git a/src/asn1c/BOOLEAN.c b/src/asn1c/BOOLEAN.c new file mode 100644 index 0000000..1b74ea1 --- /dev/null +++ b/src/asn1c/BOOLEAN.c @@ -0,0 +1,282 @@ +/*- + * Copyright (c) 2003, 2005 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#include +#include +#include + +/* + * 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: + /* "" */ + *st = 0; + break; + case XCT_UNKNOWN_BO: + if(xer_check_tag(chunk_buf, chunk_size, "true") + != XCT_BOTH) + return XPBD_BROKEN_ENCODING; + /* "" */ + *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("", 7); + er.encoded = 7; + } else { + _ASN_CALLBACK("", 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 = ""; + 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 index 0000000..217d0f1 --- /dev/null +++ b/src/asn1c/BOOLEAN.h @@ -0,0 +1,36 @@ +/*- + * Copyright (c) 2003 Lev Walkin . All rights reserved. + * Redistribution and modifications are permitted subject to BSD license. + */ +#ifndef _BOOLEAN_H_ +#define _BOOLEAN_H_ + +#include + +#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_ */ diff --git a/src/asn1c/GSSSessionData.c b/src/asn1c/GSSSessionData.c index 5c8ec17..0f20581 100644 --- a/src/asn1c/GSSSessionData.c +++ b/src/asn1c/GSSSessionData.c @@ -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 */ }; diff --git a/src/asn1c/GSSSessionData.h b/src/asn1c/GSSSessionData.h index b4e2e45..423996f 100644 --- a/src/asn1c/GSSSessionData.h +++ b/src/asn1c/GSSSessionData.h @@ -12,6 +12,7 @@ #include /* Including external dependencies */ +#include #include "Uint32.h" #include #include @@ -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; diff --git a/src/asn1c/Makefile.am b/src/asn1c/Makefile.am index 8d6c81d..d116e58 100644 --- a/src/asn1c/Makefile.am +++ b/src/asn1c/Makefile.am @@ -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 \ diff --git a/src/asn1c/session.asn1 b/src/asn1c/session.asn1 index 8f17e47..8d7b4e5 100644 --- a/src/asn1c/session.asn1 +++ b/src/asn1c/session.asn1 @@ -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 diff --git a/src/mod_auth_gssapi.c b/src/mod_auth_gssapi.c index a88b653..79d62cd 100644 --- a/src/mod_auth_gssapi.c +++ b/src/mod_auth_gssapi.c @@ -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 diff --git a/src/mod_auth_gssapi.h b/src/mod_auth_gssapi.h index 103ec61..97ba2c8 100644 --- a/src/mod_auth_gssapi.h +++ b/src/mod_auth_gssapi.h @@ -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))) diff --git a/src/sessions.c b/src/sessions.c index e8c79cd..f90857c 100644 --- a/src/sessions.c +++ b/src/sessions.c @@ -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;