summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimo Sorce <simo@redhat.com>2015-11-30 17:53:42 -0500
committerSimo Sorce <simo@redhat.com>2015-12-03 13:30:09 -0500
commit7f11db955b8440668fc806b4203f584bb44f58c1 (patch)
tree0abe923f6c0e00f5ef838df35ede4589fb2356c2
parent472d605d916f7ad63cd8bbffa100997eca700da4 (diff)
downloadmod_auth_gssapi-name_attrs.tar.gz
mod_auth_gssapi-name_attrs.tar.xz
mod_auth_gssapi-name_attrs.zip
Add code to set attribute names in the environmentname_attrs
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
-rw-r--r--README27
-rw-r--r--src/environ.c243
-rw-r--r--src/environ.h5
-rw-r--r--src/mod_auth_gssapi.c70
-rw-r--r--src/mod_auth_gssapi.h20
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);