#include "mod_auth_gssapi.h"
-static const char *
+void
+gss_log(const char *file,
+ int line,
+#if AP_SERVER_MAJORVERSION_NUMBER == 2 && AP_SERVER_MINORVERSION_NUMBER == 4
+ int module_index,
+#endif
+ int level,
+ int status,
+ const request_rec *r,
+ const char *fmt, ...)
+{
+ char errstr[1024];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(errstr, sizeof(errstr), fmt, ap);
+ va_end(ap);
+
+ ap_log_rerror(file,
+ line,
+#if AP_SERVER_MAJORVERSION_NUMBER == 2 && AP_SERVER_MINORVERSION_NUMBER == 4
+ module_index,
+#endif
+ level | APLOG_NOERRNO,
+ status,
+ r,
+ "%s",
+ errstr);
+}
+
+apr_status_t
+gss_cleanup_conn_ctx(void *data)
+{
+ gss_conn_ctx ctx = (gss_conn_ctx) data;
+ OM_uint32 minor_status;
+
+ if (ctx && ctx->context != GSS_C_NO_CONTEXT)
+ gss_delete_sec_context(&minor_status, &ctx->context, GSS_C_NO_BUFFER);
+
+ if (ctx && ctx->server_creds != GSS_C_NO_CREDENTIAL)
+ gss_release_cred(&minor_status, &ctx->server_creds);
+
+ return APR_SUCCESS;
+}
+
+gss_conn_ctx
+gss_create_conn_ctx(request_rec *r, gss_auth_config *conf)
+{
+ char key[1024];
+ gss_conn_ctx ctx = NULL;
+
+ snprintf(key, sizeof(key), "mod_auth_gssweb:conn_ctx");
+
+ if (NULL == (ctx = (gss_conn_ctx) apr_pcalloc(r->connection->pool, sizeof(*ctx)))) {
+ gss_log(APLOG_MARK, APLOG_ERR, 0, r, "gss_create_conn_ctx: Can't allocate GSS context");
+ return NULL;
+ }
+ ctx->context = GSS_C_NO_CONTEXT;
+ ctx->state = GSS_CTX_EMPTY;
+ ctx->filter_stat = GSS_FILT_NEW;
+ ctx->user = NULL;
+ ctx->name_attributes = NULL;
+ apr_pool_create(&ctx->pool, r->connection->pool);
+ /* register the context in the memory pool, so it can be freed
+ * when the connection/request is terminated */
+ apr_pool_cleanup_register(ctx->pool, (void *)ctx,
+ gss_cleanup_conn_ctx, apr_pool_cleanup_null);
+
+ /* Acquire and store server credentials */
+ if (0 == get_gss_creds(r, conf, &(ctx->server_creds))) {
+ gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, "gss_create_conn_ctx: Server credentials acquired");
+ } else {
+ gss_log(APLOG_MARK, APLOG_ERR, 0, r, "gss_create_conn_ctx: Error: Server credentials NOT acquired");
+ return NULL;
+ }
+
+ apr_pool_userdata_set(ctx, key, gss_cleanup_conn_ctx, r->connection->pool);
+
+ return ctx;
+}
+
+gss_conn_ctx
+gss_retrieve_conn_ctx(request_rec *r)
+{
+ char key[1024];
+ gss_conn_ctx ctx = NULL;
+
+ snprintf(key, sizeof(key), "mod_auth_gssweb:conn_ctx");
+ apr_pool_userdata_get((void **)&ctx, key, r->connection->pool);
+
+ if (NULL == ctx)
+ gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, "gss_retrieve_conn_ctx: No GSS context found");
+
+ return ctx;
+}
+
+void *
+gss_config_dir_create(apr_pool_t *p, char *d)
+{
+ gss_auth_config *conf;
+
+ conf = (gss_auth_config *) apr_pcalloc(p, sizeof(*conf));
+ conf->pool = p;
+
+ return conf;
+}
+
+
+const char *
get_gss_error(request_rec *r, OM_uint32 err_maj, OM_uint32 err_min, char *prefix)
{
- OM_uint32 maj_stat, min_stat;
+ OM_uint32 maj_stat, min_stat;
OM_uint32 msg_ctx = 0;
gss_buffer_desc status_string;
char *err_msg;
return err_msg;
}
-static int
+int
get_gss_creds(request_rec *r,
gss_auth_config *conf,
gss_cred_id_t *server_creds)
gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, "Acquiring creds for %s", token.value);
gss_release_buffer(&minor_status, &token);
-
+
major_status = gss_acquire_cred(&minor_status, server_name, GSS_C_INDEFINITE,
GSS_C_NO_OID_SET, GSS_C_ACCEPT,
server_creds, NULL, NULL);
return 0;
}
-static int
+int
cmp_gss_type(gss_buffer_t token, gss_OID oid)
{
unsigned char *p;
return memcmp(p, oid->elements, oid->length);
}
-int
-gss_authenticate(request_rec *r, gss_auth_config *conf, gss_conn_ctx ctx,
- const char *auth_line, char **negotiate_ret_value)
+/*
+ * Name attributes to environment variables code
+ * This code is strongly based on the code from https://github.com/modauthgssapi/mod_auth_gssapi
+ */
+
+static char *mag_status(request_rec *req, int type, uint32_t err)
{
- OM_uint32 major_status, minor_status, minor_status2;
- gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
- gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
- const char *auth_param = NULL;
- int ret;
- gss_name_t client_name = GSS_C_NO_NAME;
- gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
- gss_cred_id_t server_creds = GSS_C_NO_CREDENTIAL;
- OM_uint32 ret_flags = 0;
- gss_OID_desc spnego_oid;
- OM_uint32 (*accept_sec_context)
- (OM_uint32 *, gss_ctx_id_t *, const gss_cred_id_t,
- const gss_buffer_t, const gss_channel_bindings_t,
- gss_name_t *, gss_OID *, gss_buffer_t, OM_uint32 *,
- OM_uint32 *, gss_cred_id_t *);
-
- *negotiate_ret_value = "\0";
-
- spnego_oid.length = 6;
- spnego_oid.elements = (void *)"\x2b\x06\x01\x05\x05\x02";
-
- if (conf->krb5_keytab) {
- char *ktname;
- /* we don't use the ap_* calls here, since the string passed to putenv()
- * will become part of the enviroment and shouldn't be free()ed by apache
- */
- ktname = malloc(strlen("KRB5_KTNAME=") + strlen(conf->krb5_keytab) + 1);
- if (ktname == NULL) {
- gss_log(APLOG_MARK, APLOG_ERR, 0, r, "malloc() failed: not enough memory");
- ret = HTTP_INTERNAL_SERVER_ERROR;
- goto end;
- }
- sprintf(ktname, "KRB5_KTNAME=%s", conf->krb5_keytab);
- putenv(ktname);
-#ifdef HEIMDAL
- /* Seems to be also supported by latest MIT */
- gsskrb5_register_acceptor_identity(conf->krb_5_keytab);
-#endif
- }
+ uint32_t maj_ret, min_ret;
+ gss_buffer_desc text;
+ uint32_t msg_ctx;
+ char *msg_ret;
+ int len;
+
+ msg_ret = NULL;
+ msg_ctx = 0;
+ do {
+ maj_ret = gss_display_status(&min_ret, err, type,
+ GSS_C_NO_OID, &msg_ctx, &text);
+ if (maj_ret != GSS_S_COMPLETE) {
+ return msg_ret;
+ }
+
+ len = text.length;
+ if (msg_ret) {
+ msg_ret = apr_psprintf(req->pool, "%s, %*s",
+ msg_ret, len, (char *)text.value);
+ } else {
+ msg_ret = apr_psprintf(req->pool, "%*s", len, (char *)text.value);
+ }
+ gss_release_buffer(&min_ret, &text);
+ } while (msg_ctx != 0);
+
+ return msg_ret;
+}
- ret = get_gss_creds(r, conf, &server_creds);
- if (ret)
- goto end;
-
- /* ap_getword() shifts parameter */
- auth_param = ap_getword_white(r->pool, &auth_line);
- if (auth_param == NULL) {
- gss_log(APLOG_MARK, APLOG_ERR, 0, r,
- "No Authorization parameter in request from client");
- ret = HTTP_UNAUTHORIZED;
- goto end;
- }
+char *mag_error(request_rec *req, const char *msg, uint32_t maj, uint32_t min)
+{
+ char *msg_maj;
+ char *msg_min;
- if (ctx->state == GSS_CTX_ESTABLISHED) {
- gss_delete_sec_context(&minor_status, &ctx->context, GSS_C_NO_BUFFER);
- ctx->context = GSS_C_NO_CONTEXT;
- ctx->state = GSS_CTX_EMPTY;
- }
+ msg_maj = mag_status(req, GSS_C_GSS_CODE, maj);
+ msg_min = mag_status(req, GSS_C_MECH_CODE, min);
+ return apr_psprintf(req->pool, "%s: [%s (%s)]", msg, msg_maj, msg_min);
+}
- input_token.length = apr_base64_decode_len(auth_param) + 1;
- input_token.value = apr_pcalloc(r->connection->pool, input_token.length);
- if (input_token.value == NULL) {
- gss_log(APLOG_MARK, APLOG_ERR, 0, r,
- "ap_pcalloc() failed (not enough memory)");
- ret = HTTP_INTERNAL_SERVER_ERROR;
- goto end;
- }
- input_token.length = apr_base64_decode(input_token.value, auth_param);
-
- /* LOG length, type */
-
-#ifdef GSSAPI_SUPPORTS_SPNEGO
- accept_sec_context = gss_accept_sec_context;
-#else
- accept_sec_context = (cmp_gss_type(&input_token, &spnego_oid) == 0) ?
- gss_accept_sec_context_spnego : gss_accept_sec_context;
-#endif
-
- major_status = accept_sec_context(&minor_status,
- &ctx->context,
- server_creds,
- &input_token,
- GSS_C_NO_CHANNEL_BINDINGS,
- NULL,
- NULL,
- &output_token,
- &ret_flags,
- NULL,
- &delegated_cred);
- gss_log(APLOG_MARK, APLOG_DEBUG, 0, r,
- "Client %s us their credential",
- (ret_flags & GSS_C_DELEG_FLAG) ? "delegated" : "didn't delegate");
- if (output_token.length) {
- char *token = NULL;
- size_t len;
-
- len = apr_base64_encode_len(output_token.length) + 1;
- token = apr_pcalloc(r->connection->pool, len + 1);
- if (token == NULL) {
- gss_log(APLOG_MARK, APLOG_ERR, 0, r,
- "ap_pcalloc() failed (not enough memory)");
- ret = HTTP_INTERNAL_SERVER_ERROR;
- gss_release_buffer(&minor_status2, &output_token);
- goto end;
- }
- apr_base64_encode(token, output_token.value, output_token.length);
- token[len] = '\0';
- *negotiate_ret_value = token;
- gss_log(APLOG_MARK, APLOG_DEBUG, 0, r,
- "GSS-API token of length %d bytes will be sent back",
- output_token.length);
- gss_release_buffer(&minor_status2, &output_token);
- }
- if (GSS_ERROR(major_status)) {
- gss_log(APLOG_MARK, APLOG_ERR, 0, r,
- "%s", get_gss_error(r, major_status, minor_status,
- "Failed to establish authentication"));
-#if 0
- /* Don't offer the Negotiate method again if call to GSS layer failed */
- /* XXX ... which means we don't return the "error" output */
- *negotiate_ret_value = NULL;
-#endif
- gss_delete_sec_context(&minor_status, &ctx->context, GSS_C_NO_BUFFER);
- ctx->context = GSS_C_NO_CONTEXT;
- ctx->state = GSS_CTX_EMPTY;
- ret = HTTP_UNAUTHORIZED;
- goto end;
- }
+static char mag_get_name_attr(request_rec *req,
+ gss_name_t name, 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 0;
+ }
+
+ return 1;
+}
- if (major_status & GSS_S_CONTINUE_NEEDED) {
- ctx->state = GSS_CTX_IN_PROGRESS;
- ret = HTTP_UNAUTHORIZED;
- goto end;
- }
+#define GSS_NAME_ATTR_USERDATA "GSS Name Attributes Userdata"
- major_status = gss_inquire_context(&minor_status, ctx->context, &client_name,
- NULL, NULL, NULL, NULL, NULL, NULL);
- if (GSS_ERROR(major_status)) {
- gss_log(APLOG_MARK, APLOG_ERR, 0, r,
- "%s", get_gss_error(r, major_status, minor_status, "gss_inquire_context() failed"));
- ret = HTTP_INTERNAL_SERVER_ERROR;
- goto end;
- }
+static apr_status_t mag_gss_name_attrs_cleanup(void *data)
+{
+ gss_conn_ctx_t *gss_ctx = (gss_conn_ctx_t *) data;
+ free(gss_ctx->name_attributes);
+ gss_ctx->name_attributes = NULL;
+ return 0;
+}
- major_status = gss_display_name(&minor_status, client_name, &output_token, NULL);
- gss_release_name(&minor_status, &client_name);
- if (GSS_ERROR(major_status)) {
- gss_log(APLOG_MARK, APLOG_ERR, 0, r,
- "%s", get_gss_error(r, major_status, minor_status,
- "gss_display_name() failed"));
- ret = HTTP_INTERNAL_SERVER_ERROR;
- goto end;
- }
+static void mc_add_name_attribute(gss_conn_ctx_t *gss_ctx,
+ const char *name, const char *value)
+{
+ size_t size;
+
+ if (gss_ctx->na_count % 16 == 0) {
+ size = sizeof(mag_attr) * (gss_ctx->na_count + 16);
+ gss_ctx->name_attributes = realloc(gss_ctx->name_attributes, size);
+ if (!gss_ctx->name_attributes) apr_pool_abort_get(gss_ctx->pool)(ENOMEM);
+ apr_pool_userdata_setn(gss_ctx, GSS_NAME_ATTR_USERDATA,
+ mag_gss_name_attrs_cleanup, gss_ctx->pool);
+ }
+
+ gss_ctx->name_attributes[gss_ctx->na_count].name = apr_pstrdup(gss_ctx->pool, name);
+ gss_ctx->name_attributes[gss_ctx->na_count].value = apr_pstrdup(gss_ctx->pool, value);
+ gss_ctx->na_count++;
+}
- ctx->state = GSS_CTX_ESTABLISHED;
- ctx->user = apr_pstrdup(r->pool, output_token.value);
- gss_release_buffer(&minor_status, &output_token);
+static void mag_set_env_name_attr(request_rec *req, gss_conn_ctx_t *gss_ctx,
+ 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(gss_ctx,
+ attr->env_name,
+ apr_psprintf(req->pool, "%.*s", len, value));
+ }
+ if (attr->more != 0 || attr->number > 1) {
+ mc_add_name_attribute(gss_ctx,
+ 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(gss_ctx,
+ apr_psprintf(req->pool, "%s_N", attr->env_name),
+ apr_psprintf(req->pool, "%d", attr->number - 1));
+ }
+}
- ret = OK;
+static char *mag_escape_display_value(request_rec *req,
+ gss_buffer_desc disp_value)
+{
+ /* This function returns a copy (in the pool) of the given gss_buffer_t
+ * where some characters are escaped as required by RFC4627. The string is
+ * NULL terminated */
+ char *value = disp_value.value;
+ char *escaped_value = NULL;
+ char *p = NULL;
+ size_t i = 0;
+
+ /* gss_buffer_t are not \0 terminated, but our result will be. Hence,
+ * escaped length will be original length * 6 + 1 in the worst case */
+ p = escaped_value = apr_palloc(req->pool, disp_value.length * 6 + 1);
+ for (i = 0; i < disp_value.length; i++) {
+ switch (value[i]) {
+ case '"':
+ memcpy(p, "\\\"", 2);
+ p += 2;
+ break;
+ case '\\':
+ memcpy(p, "\\\\", 2);
+ p += 2;
+ break;
+ case '\b':
+ memcpy(p, "\\b", 2);
+ p += 2;
+ break;
+ case '\t':
+ memcpy(p, "\\t", 2);
+ p += 2;
+ break;
+ case '\r':
+ memcpy(p, "\\r", 2);
+ p += 2;
+ break;
+ case '\f':
+ memcpy(p, "\\f", 2);
+ p += 2;
+ break;
+ case '\n':
+ memcpy(p, "\\n", 2);
+ p += 2;
+ break;
+ default:
+ if (value[i] <= 0x1F) {
+ apr_snprintf(p, 7, "\\u%04d", (int)value[i]);
+ p += 6;
+ } else {
+ *p = value[i];
+ p += 1;
+ }
+ break;
+ }
+ }
+ /* make the string NULL terminated */
+ *p = '\0';
+ return escaped_value;
+}
-end:
- if (delegated_cred)
- gss_release_cred(&minor_status, &delegated_cred);
+static void mag_add_json_name_attr(request_rec *req, char first,
+ 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) {
+ value = mag_escape_display_value(req, attr->display_value);
+ len = strlen(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);
+}
- if (output_token.length)
- gss_release_buffer(&minor_status, &output_token);
+gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
- if (client_name != GSS_C_NO_NAME)
- gss_release_name(&minor_status, &client_name);
+void mag_get_name_attributes(request_rec *req, gss_auth_config *cfg,
+ gss_name_t name, gss_conn_ctx_t *gss_ctx)
+{
+ uint32_t maj, min;
+ gss_buffer_set_t attrs = GSS_C_NO_BUFFER_SET;
+ 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(gss_ctx, "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\":{",
+ gss_ctx->user);
+ } else {
+ count = cfg->name_attributes->map_count;
+ }
+
+ for (i = 0; i < count; i++) {
+ memset(&attr, 0, sizeof(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)) break;
+
+ 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, gss_ctx, &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(gss_ctx, "GSS_NAME_ATTRS_JSON", json);
+ }
+}
- if (server_creds != GSS_C_NO_CREDENTIAL)
- gss_release_cred(&minor_status, &server_creds);
+static void mag_set_name_attributes(request_rec *req, gss_conn_ctx_t *gss_ctx)
+{
+ int i = 0;
+ for (i = 0; i < gss_ctx->na_count; i++) {
+ apr_table_set(req->subprocess_env,
+ gss_ctx->name_attributes[i].name,
+ gss_ctx->name_attributes[i].value);
+ }
+}
+
+void mag_set_req_data(request_rec *req,
+ gss_auth_config *cfg,
+ gss_conn_ctx_t *gss_ctx)
+{
+ if (gss_ctx->name_attributes) {
+ mag_set_name_attributes(req, gss_ctx);
+ }
+}
- return ret;
+static apr_status_t mag_name_attrs_cleanup(void *data)
+{
+ gss_auth_config *cfg = (gss_auth_config *)data;
+ free(cfg->name_attributes);
+ cfg->name_attributes = NULL;
+ return 0;
+}
+
+const char *mag_name_attrs(cmd_parms *parms, void *mconfig,
+ const char *w)
+{
+ gss_auth_config *cfg = (gss_auth_config *)mconfig;
+ void *tmp_na;
+ size_t size = 0;
+ char *p;
+ int c;
+
+ if (!cfg->name_attributes) {
+ size = sizeof(mag_name_attributes)
+ + (sizeof(mag_na_map) * 16);
+ } else if (cfg->name_attributes->map_count % 16 == 0) {
+ size = sizeof(mag_name_attributes)
+ + (sizeof(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(mag_na_map) * 16);
+ memset(tmp_na + size - empty, 0, empty);
+ } else {
+ memset(tmp_na, 0, size);
+ }
+ cfg->name_attributes = (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 = 1;
+ } 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;
}