Add code to set attribute names in the environment
authorSimo Sorce <simo@redhat.com>
Mon, 30 Nov 2015 22:53:42 +0000 (17:53 -0500)
committerSimo Sorce <simo@redhat.com>
Thu, 3 Dec 2015 18:30:09 +0000 (13:30 -0500)
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 <simo@redhat.com>
Close #62
Close #63

README
src/environ.c
src/environ.h
src/mod_auth_gssapi.c
src/mod_auth_gssapi.h

diff --git a/README b/README
index 9d25fb3..3851f92 100644 (file)
--- 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
index 0ff7cf0..9ef010b 100644 (file)
@@ -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,
index e20ed64..3895665 100644 (file)
@@ -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);
index 42284f9..6d12036 100644 (file)
@@ -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 }
 };
 
index 5662add..4e9fdf3 100644 (file)
 #  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);