From 7f11db955b8440668fc806b4203f584bb44f58c1 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Mon, 30 Nov 2015 17:53:42 -0500 Subject: [PATCH] Add code to set attribute names in the environment This code allows to specify which attributes in a name are interesting to the application and set them as named environemnt variables. Optionally the whole set of attributes can be exported in a json formatted structure. Signed-off-by: Simo Sorce Close #62 Close #63 --- README | 27 ++++++ src/environ.c | 243 +++++++++++++++++++++++++++++++++++++++++++++++++- src/environ.h | 5 ++ src/mod_auth_gssapi.c | 70 ++++++++++++++- src/mod_auth_gssapi.h | 20 +++++ 5 files changed, 362 insertions(+), 3 deletions(-) diff --git a/README b/README index 9d25fb3..3851f92 100644 --- a/README +++ b/README @@ -237,3 +237,30 @@ The recognized mechanism names are: krb5, iakerb, ntlmssp #### Example GssapiBasicAuthMech krb5 + + +#### GssapiNameAttributes + +Enables the module to source Name Attributes from the client name +(authorization data associated with the established context) and exposes them +as environment variables. + +Value format: ENV_VAR_NAME ATTRIBUTE_NAME + +This option can be specified multiple times, once for each attribute to expose. +The Special value "json" is used to expose all attributes in a json formatted +string via the special environment variable GSS_NAME_ATTRS_JSON +The environment variable GSS_NAME_ATTR_ERROR is set with the Gssapi returned +error string in case the inquire name function fails to retrieve attributes, +and with the string "0 attributes found", if no attributes are set. + +Note: These variables are NOT saved in the session data stored in the cookie so they +are available only on the first authenticated request when GssapiUseSessions is +used. + +Note: It is recommended but not required to use only capital letters and underscores +for environment variable names. + +#### Example + GssapiNameAttributes json + GssapiNameAttributes RADIUS_NAME urn:ietf:params:gss:radius-attribute_1 diff --git a/src/environ.c b/src/environ.c index 0ff7cf0..9ef010b 100644 --- a/src/environ.c +++ b/src/environ.c @@ -1,7 +1,243 @@ /* Copyright (C) 2015 mod_auth_gssapi authors - See COPYING for (C) terms */ #include "mod_auth_gssapi.h" -#include "environ.h" + +struct name_attr { + gss_buffer_desc name; + int authenticated; + int complete; + gss_buffer_desc value; + gss_buffer_desc display_value; + const char *env_name; + int number; + int more; +}; + +static bool mag_get_name_attr(request_rec *req, + gss_name_t name, struct name_attr *attr) +{ + uint32_t maj, min; + + maj = gss_get_name_attribute(&min, name, &attr->name, + &attr->authenticated, + &attr->complete, + &attr->value, + &attr->display_value, + &attr->more); + if (GSS_ERROR(maj)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, + "gss_get_name_attribute() failed on %.*s%s", + (int)attr->name.length, (char *)attr->name.value, + mag_error(req, "", maj, min)); + return false; + } + + return true; +} + +static void mc_add_name_attribute(struct mag_conn *mc, + const char *name, const char *value) +{ + size_t size; + + if (mc->na_count % 16 == 0) { + size = sizeof(struct mag_attr) * (mc->na_count + 16); + mc->name_attributes = realloc(mc->name_attributes, size); + if (!mc->name_attributes) apr_pool_abort_get(mc->pool)(ENOMEM); + } + + mc->name_attributes[mc->na_count].name = apr_pstrdup(mc->pool, name); + mc->name_attributes[mc->na_count].value = apr_pstrdup(mc->pool, value); + mc->na_count++; +} + +static void mag_set_env_name_attr(request_rec *req, struct mag_conn *mc, + struct name_attr *attr) +{ + char *value = ""; + int len = 0; + + /* Prefer a display_value, otherwise fallback to value */ + if (attr->display_value.length != 0) { + len = attr->display_value.length; + value = (char *)attr->display_value.value; + } else if (attr->value.length != 0) { + len = apr_base64_encode_len(attr->value.length); + value = apr_pcalloc(req->pool, len); + len = apr_base64_encode(value, + (char *)attr->value.value, + attr->value.length); + } + + if (attr->number == 1) { + mc_add_name_attribute(mc, + attr->env_name, + apr_psprintf(req->pool, "%.*s", len, value)); + } + if (attr->more != 0 || attr->number > 1) { + mc_add_name_attribute(mc, + apr_psprintf(req->pool, "%s_%d", + attr->env_name, attr->number), + apr_psprintf(req->pool, "%.*s", len, value)); + } + if (attr->more == 0 && attr->number > 1) { + mc_add_name_attribute(mc, + apr_psprintf(req->pool, "%s_N", attr->env_name), + apr_psprintf(req->pool, "%d", attr->number - 1)); + } +} + +static void mag_add_json_name_attr(request_rec *req, bool first, + struct name_attr *attr, char **json) +{ + const char *value = ""; + int len = 0; + char *b64value = NULL; + int b64len = 0; + const char *vstart = ""; + const char *vend = ""; + const char *vformat; + + if (attr->value.length != 0) { + b64len = apr_base64_encode_len(attr->value.length); + b64value = apr_pcalloc(req->pool, b64len); + b64len = apr_base64_encode(b64value, + (char *)attr->value.value, + attr->value.length); + } + if (attr->display_value.length != 0) { + len = attr->display_value.length; + value = (const char *)attr->display_value.value; + } + if (attr->number == 1) { + *json = apr_psprintf(req->pool, + "%s%s\"%.*s\":{\"authenticated\":%s," + "\"complete\":%s," + "\"values\":[", + *json, (first ? "" : ","), + (int)attr->name.length, (char *)attr->name.value, + attr->authenticated ? "true" : "false", + attr->complete ? "true" : "false"); + } else { + vstart = ","; + } + + if (b64value) { + if (len) { + vformat = "%s%s{\"raw\":\"%s\",\"display\":\"%.*s\"}%s"; + } else { + vformat = "%s%s{\"raw\":\"%s\",\"display\":%.*s}%s"; + } + } else { + if (len) { + vformat = "%s%s{\"raw\":%s,\"display\":\"%.*s\"}%s"; + } else { + vformat = "%s%s{\"raw\":%s,\"display\":%.*s}%s"; + } + } + + if (attr->more == 0) { + vend = "]}"; + } + + *json = apr_psprintf(req->pool, vformat, *json, + vstart, + b64value ? b64value : "null", + len ? len : 4, len ? value : "null", + vend); +} + +gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER; + +void mag_get_name_attributes(request_rec *req, struct mag_config *cfg, + gss_name_t name, struct mag_conn *mc) +{ + uint32_t maj, min; + gss_buffer_set_t attrs = GSS_C_NO_BUFFER_SET; + struct name_attr attr; + char *json = NULL; + char *error; + int count = 0; + int i, j; + + maj = gss_inquire_name(&min, name, NULL, NULL, &attrs); + if (GSS_ERROR(maj)) { + error = mag_error(req, "gss_inquire_name() failed", maj, min); + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s", error); + apr_table_set(req->subprocess_env, "GSS_NAME_ATTR_ERROR", error); + return; + } + + if (!attrs || attrs->count == 0) { + mc_add_name_attribute(mc, "GSS_NAME_ATTR_ERROR", "0 attributes found"); + } + + if (cfg->name_attributes->output_json) { + + if (attrs) count = attrs->count; + + json = apr_psprintf(req->pool, + "{\"name\":\"%s\",\"attributes\":{", + mc->gss_name); + } else { + count = cfg->name_attributes->map_count; + } + + for (i = 0; i < count; i++) { + + memset(&attr, 0, sizeof(struct name_attr)); + + if (cfg->name_attributes->output_json) { + attr.name = attrs->elements[i]; + for (j = 0; j < cfg->name_attributes->map_count; j++) { + if (strncmp(cfg->name_attributes->map[j].attr_name, + attrs->elements[i].value, + attrs->elements[i].length) == 0) { + attr.env_name = cfg->name_attributes->map[j].env_name; + break; + } + } + } else { + attr.name.length = strlen(cfg->name_attributes->map[i].attr_name); + attr.name.value = cfg->name_attributes->map[i].attr_name; + attr.env_name = cfg->name_attributes->map[i].env_name; + } + + attr.number = 0; + attr.more = -1; + do { + attr.number++; + attr.value = empty_buffer; + attr.display_value = empty_buffer; + + if (!mag_get_name_attr(req, name, &attr)) continue; + + if (cfg->name_attributes->output_json) { + mag_add_json_name_attr(req, i == 0, &attr, &json); + } + if (attr.env_name) { + mag_set_env_name_attr(req, mc, &attr); + } + + gss_release_buffer(&min, &attr.value); + gss_release_buffer(&min, &attr.display_value); + } while (attr.more != 0); + } + + if (cfg->name_attributes->output_json) { + json = apr_psprintf(req->pool, "%s}}", json); + mc_add_name_attribute(mc, "GSS_NAME_ATTRS_JSON", json); + } +} + +static void mag_set_name_attributes(request_rec *req, struct mag_conn *mc) +{ + for (int i = 0; i < mc->na_count; i++) { + apr_table_set(req->subprocess_env, + mc->name_attributes[i].name, + mc->name_attributes[i].value); + } +} static void mag_set_KRB5CCANME(request_rec *req, char *ccname) { @@ -31,6 +267,11 @@ void mag_set_req_data(request_rec *req, req->ap_auth_type = apr_pstrdup(req->pool, mag_str_auth_type(mc->auth_type)); req->user = apr_pstrdup(req->pool, mc->user_name); + + if (mc->name_attributes) { + mag_set_name_attributes(req, mc); + } + if (cfg->deleg_ccache_dir && mc->delegated) { char *ccname; ccname = mag_gss_name_to_ccache_name(req, diff --git a/src/environ.h b/src/environ.h index e20ed64..3895665 100644 --- a/src/environ.h +++ b/src/environ.h @@ -3,6 +3,11 @@ struct mag_config; struct mag_conn; +void mag_get_name_attributes(request_rec *req, + struct mag_config *cfg, + gss_name_t name, + struct mag_conn *mc); + void mag_set_req_data(request_rec *req, struct mag_config *cfg, struct mag_conn *mc); diff --git a/src/mod_auth_gssapi.c b/src/mod_auth_gssapi.c index 42284f9..6d12036 100644 --- a/src/mod_auth_gssapi.c +++ b/src/mod_auth_gssapi.c @@ -80,8 +80,7 @@ static char *mag_status(request_rec *req, int type, uint32_t err) return msg_ret; } -static char *mag_error(request_rec *req, const char *msg, - uint32_t maj, uint32_t min) +char *mag_error(request_rec *req, const char *msg, uint32_t maj, uint32_t min) { char *msg_maj; char *msg_min; @@ -897,12 +896,15 @@ complete: maj, min)); goto done; } + mc->gss_name = apr_pstrndup(req->pool, name.value, name.length); if (vtime == GSS_C_INDEFINITE || vtime < MIN_SESS_EXP_TIME) { vtime = MIN_SESS_EXP_TIME; } mc->expiration = time(NULL) + vtime; + mag_get_name_attributes(req, cfg, client, mc); + #ifdef HAVE_CRED_STORE if (cfg->deleg_ccache_dir && delegated_cred != GSS_C_NO_CREDENTIAL) { mag_store_deleg_creds(req, cfg->deleg_ccache_dir, mc->gss_name, @@ -1225,6 +1227,68 @@ static const char *mag_allow_mech(cmd_parms *parms, void *mconfig, return NULL; } +#define GSS_NAME_ATTR_USERDATA "GSS Name Attributes Userdata" + +static apr_status_t mag_name_attrs_cleanup(void *data) +{ + struct mag_config *cfg = (struct mag_config *)data; + free(cfg->name_attributes); + cfg->name_attributes = NULL; + return 0; +} + +static const char *mag_name_attrs(cmd_parms *parms, void *mconfig, + const char *w) +{ + struct mag_config *cfg = (struct mag_config *)mconfig; + void *tmp_na; + size_t size = 0; + char *p; + int c; + + if (!cfg->name_attributes) { + size = sizeof(struct mag_name_attributes) + + (sizeof(struct mag_na_map) * 16); + } else if (cfg->name_attributes->map_count % 16 == 0) { + size = sizeof(struct mag_name_attributes) + + (sizeof(struct mag_na_map) + * (cfg->name_attributes->map_count + 16)); + } + if (size) { + tmp_na = realloc(cfg->name_attributes, size); + if (!tmp_na) apr_pool_abort_get(cfg->pool)(ENOMEM); + + if (cfg->name_attributes) { + size_t empty = (sizeof(struct mag_na_map) * 16); + memset(tmp_na + size - empty, 0, empty); + } else { + memset(tmp_na, 0, size); + } + cfg->name_attributes = (struct mag_name_attributes *)tmp_na; + apr_pool_userdata_setn(cfg, GSS_NAME_ATTR_USERDATA, + mag_name_attrs_cleanup, cfg->pool); + } + + p = strchr(w, ' '); + if (p == NULL) { + if (strcmp(w, "json") == 0) { + cfg->name_attributes->output_json = true; + } else { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server, + "Invalid Name Attributes value [%s].", w); + } + return NULL; + } + + c = cfg->name_attributes->map_count; + cfg->name_attributes->map[c].env_name = apr_pstrndup(cfg->pool, w, p-w); + p++; + cfg->name_attributes->map[c].attr_name = apr_pstrdup(cfg->pool, p); + cfg->name_attributes->map_count += 1; + + return NULL; +} + #ifdef HAVE_GSS_ACQUIRE_CRED_WITH_PASSWORD static const char *mag_basic_auth_mechs(cmd_parms *parms, void *mconfig, const char *w) @@ -1294,6 +1358,8 @@ static const command_rec mag_commands[] = { #endif AP_INIT_ITERATE("GssapiAllowedMech", mag_allow_mech, NULL, OR_AUTHCFG, "Allowed Mechanisms"), + AP_INIT_RAW_ARGS("GssapiNameAttributes", mag_name_attrs, NULL, OR_AUTHCFG, + "Name Attributes to be exported as environ variables"), { NULL } }; diff --git a/src/mod_auth_gssapi.h b/src/mod_auth_gssapi.h index 5662add..4e9fdf3 100644 --- a/src/mod_auth_gssapi.h +++ b/src/mod_auth_gssapi.h @@ -46,6 +46,17 @@ # endif #endif +struct mag_na_map { + char *env_name; + char *attr_name; +}; + +struct mag_name_attributes { + bool output_json; + int map_count; + struct mag_na_map map[]; +}; + struct mag_config { apr_pool_t *pool; bool ssl_only; @@ -63,6 +74,7 @@ struct mag_config { bool use_basic_auth; gss_OID_set_desc *allowed_mechs; gss_OID_set_desc *basic_mechs; + struct mag_name_attributes *name_attributes; }; struct mag_server_config { @@ -81,6 +93,11 @@ struct mag_req_cfg { struct seal_key *mag_skey; }; +struct mag_attr { + const char *name; + const char *value; +}; + struct mag_conn { apr_pool_t *pool; gss_ctx_id_t ctx; @@ -92,6 +109,8 @@ struct mag_conn { bool delegated; struct databuf basic_hash; bool is_preserved; + int na_count; + struct mag_attr *name_attributes; }; #define discard_const(ptr) ((void *)((uintptr_t)(ptr))) @@ -100,3 +119,4 @@ struct mag_conn *mag_new_conn_ctx(apr_pool_t *pool); const char *mag_str_auth_type(int auth_type); char *mag_gss_name_to_ccache_name(request_rec *req, char *dir, const char *gss_name); +char *mag_error(request_rec *req, const char *msg, uint32_t maj, uint32_t min); -- 2.1.4