Export error environment variables.
[mod_auth_kerb.git] / gss.c
1 /*
2  * Copyright (c) 2010 CESNET
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  *    this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * 3. Neither the name of CESNET nor the names of its contributors may
16  *    be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include "mod_auth_gssapi.h"
33
34 void
35 gss_log(const char *file,
36         int line,
37 #if AP_SERVER_MAJORVERSION_NUMBER == 2 && AP_SERVER_MINORVERSION_NUMBER == 4
38         int module_index,
39 #endif
40         int level,
41         int status,
42         const request_rec *r,
43         const char *fmt, ...)
44 {
45     char errstr[1024];
46     va_list ap;
47
48     va_start(ap, fmt);
49     vsnprintf(errstr, sizeof(errstr), fmt, ap);
50     va_end(ap);
51
52     ap_log_rerror(file,
53                   line,
54 #if AP_SERVER_MAJORVERSION_NUMBER == 2 && AP_SERVER_MINORVERSION_NUMBER == 4
55                   module_index,
56 #endif
57                   level | APLOG_NOERRNO,
58                   status,
59                   r,
60                   "%s",
61                   errstr);
62 }
63
64 apr_status_t
65 gss_cleanup_conn_ctx(void *data)
66 {
67     gss_conn_ctx ctx = (gss_conn_ctx) data;
68     OM_uint32 minor_status;
69
70     if (ctx && ctx->context != GSS_C_NO_CONTEXT)
71         gss_delete_sec_context(&minor_status, &ctx->context, GSS_C_NO_BUFFER);
72
73     if (ctx && ctx->server_creds != GSS_C_NO_CREDENTIAL)
74       gss_release_cred(&minor_status, &ctx->server_creds);
75
76     return APR_SUCCESS;
77 }
78
79 gss_conn_ctx
80 gss_create_conn_ctx(request_rec *r, gss_auth_config *conf)
81 {
82   char key[1024];
83   gss_conn_ctx ctx = NULL;
84
85   snprintf(key, sizeof(key), "mod_auth_gssweb:conn_ctx");
86
87   if (NULL == (ctx = (gss_conn_ctx) apr_pcalloc(r->connection->pool, sizeof(*ctx)))) {
88     gss_log(APLOG_MARK, APLOG_ERR, 0, r, "gss_create_conn_ctx: Can't allocate GSS context");
89     return NULL;
90   }
91   ctx->context = GSS_C_NO_CONTEXT;
92   ctx->state = GSS_CTX_EMPTY;
93   ctx->filter_stat = GSS_FILT_NEW;
94   ctx->user = NULL;
95   ctx->name_attributes = NULL;
96   apr_pool_create(&ctx->pool, r->connection->pool);
97   /* register the context in the memory pool, so it can be freed
98    * when the connection/request is terminated */
99   apr_pool_cleanup_register(ctx->pool, (void *)ctx,
100                               gss_cleanup_conn_ctx, apr_pool_cleanup_null);
101
102   /* Acquire and store server credentials */
103   if (0 == get_gss_creds(r, conf, &(ctx->server_creds))) {
104     gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, "gss_create_conn_ctx: Server credentials acquired");
105   } else {
106     gss_log(APLOG_MARK, APLOG_ERR, 0, r, "gss_create_conn_ctx: Error: Server credentials NOT acquired");
107     return NULL;
108   }
109
110   apr_pool_userdata_set(ctx, key, gss_cleanup_conn_ctx, r->connection->pool);
111
112   return ctx;
113 }
114
115 gss_conn_ctx
116 gss_retrieve_conn_ctx(request_rec *r)
117 {
118   char key[1024];
119   gss_conn_ctx ctx = NULL;
120
121   snprintf(key, sizeof(key), "mod_auth_gssweb:conn_ctx");
122   apr_pool_userdata_get((void **)&ctx, key, r->connection->pool);
123
124   if (NULL == ctx)
125     gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, "gss_retrieve_conn_ctx: No GSS context found");
126
127   return ctx;
128 }
129
130 void *
131 gss_config_dir_create(apr_pool_t *p, char *d)
132 {
133     gss_auth_config *conf;
134
135     conf = (gss_auth_config *) apr_pcalloc(p, sizeof(*conf));
136     conf->pool = p;
137
138     return conf;
139 }
140
141
142 const char *
143 get_gss_error(request_rec *r, OM_uint32 err_maj, OM_uint32 err_min, char *prefix)
144 {
145    OM_uint32 maj_stat, min_stat;
146    OM_uint32 msg_ctx = 0;
147    gss_buffer_desc status_string;
148    char *err_msg;
149    int first_pass;
150
151    gss_log(APLOG_MARK, APLOG_DEBUG, 0, r,
152            "GSS-API major_status:%8.8x, minor_status:%8.8x",
153            err_maj, err_min);
154
155    err_msg = apr_pstrdup(r->pool, prefix);
156    do {
157       maj_stat = gss_display_status (&min_stat,
158                                      err_maj,
159                                      GSS_C_GSS_CODE,
160                                      GSS_C_NO_OID,
161                                      &msg_ctx,
162                                      &status_string);
163       if (!GSS_ERROR(maj_stat)) {
164          err_msg = apr_pstrcat(r->pool, err_msg,
165                                ": ", (char*) status_string.value, NULL);
166          gss_release_buffer(&min_stat, &status_string);
167          first_pass = 0;
168       }
169    } while (!GSS_ERROR(maj_stat) && msg_ctx != 0);
170
171    msg_ctx = 0;
172    err_msg = apr_pstrcat(r->pool, err_msg, " (", NULL);
173    first_pass = 1;
174    do {
175       maj_stat = gss_display_status (&min_stat,
176                                      err_min,
177                                      GSS_C_MECH_CODE,
178                                      GSS_C_NULL_OID,
179                                      &msg_ctx,
180                                      &status_string);
181       if (!GSS_ERROR(maj_stat)) {
182          err_msg = apr_pstrcat(r->pool, err_msg,
183                                (first_pass) ? "" : ", ",
184                                (char *) status_string.value,
185                                NULL);
186          gss_release_buffer(&min_stat, &status_string);
187          first_pass = 0;
188       }
189    } while (!GSS_ERROR(maj_stat) && msg_ctx != 0);
190    err_msg = apr_pstrcat(r->pool, err_msg, ")", NULL);
191
192    return err_msg;
193 }
194
195 int
196 get_gss_creds(request_rec *r,
197               gss_auth_config *conf,
198               gss_cred_id_t *server_creds)
199 {
200    gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
201    OM_uint32 major_status, minor_status, minor_status2;
202    gss_name_t server_name = GSS_C_NO_NAME;
203    char buf[1024];
204    int have_server_princ;
205
206    if (conf->service_name && strcmp(conf->service_name, "Any") == 0) {
207        *server_creds = GSS_C_NO_CREDENTIAL;
208        return 0;
209    }
210
211    have_server_princ = conf->service_name && strchr(conf->service_name, '/') != NULL;
212    if (have_server_princ)
213        strncpy(buf, conf->service_name, sizeof(buf));
214    else
215        snprintf(buf, sizeof(buf), "%s@%s",
216                (conf->service_name) ? conf->service_name : SERVICE_NAME,
217                ap_get_server_name(r));
218
219    token.value = buf;
220    token.length = strlen(buf) + 1;
221
222    major_status = gss_import_name(&minor_status, &token,
223                                   (have_server_princ) ? (gss_OID) GSS_KRB5_NT_PRINCIPAL_NAME : (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
224                                   &server_name);
225    memset(&token, 0, sizeof(token));
226    if (GSS_ERROR(major_status)) {
227       gss_log(APLOG_MARK, APLOG_ERR, 0, r,
228               "%s", get_gss_error(r, major_status, minor_status,
229               "gss_import_name() failed"));
230       return HTTP_INTERNAL_SERVER_ERROR;
231    }
232
233    major_status = gss_display_name(&minor_status, server_name, &token, NULL);
234    if (GSS_ERROR(major_status)) {
235       /* Perhaps we could just ignore this error but it's safer to give up now,
236          I think */
237       gss_log(APLOG_MARK, APLOG_ERR, 0, r,
238               "%s", get_gss_error(r, major_status, minor_status,
239                                   "gss_display_name() failed"));
240       return HTTP_INTERNAL_SERVER_ERROR;
241    }
242
243    gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, "Acquiring creds for %s", token.value);
244    gss_release_buffer(&minor_status, &token);
245
246    major_status = gss_acquire_cred(&minor_status, server_name, GSS_C_INDEFINITE,
247                                    GSS_C_NO_OID_SET, GSS_C_ACCEPT,
248                                    server_creds, NULL, NULL);
249    gss_release_name(&minor_status2, &server_name);
250    if (GSS_ERROR(major_status)) {
251       gss_log(APLOG_MARK, APLOG_ERR, 0, r,
252               "%s", get_gss_error(r, major_status, minor_status,
253                                   "Failed to load GSS-API credentials"));
254       return HTTP_INTERNAL_SERVER_ERROR;
255    }
256
257    return 0;
258 }
259
260 int
261 cmp_gss_type(gss_buffer_t token, gss_OID oid)
262 {
263    unsigned char *p;
264    size_t len;
265
266    if (token->length == 0)
267       return GSS_S_DEFECTIVE_TOKEN;
268
269    p = token->value;
270    if (*p++ != 0x60)
271       return GSS_S_DEFECTIVE_TOKEN;
272    len = *p++;
273    if (len & 0x80) {
274       if ((len & 0x7f) > 4)
275          return GSS_S_DEFECTIVE_TOKEN;
276       p += len & 0x7f;
277    }
278    if (*p++ != 0x06)
279       return GSS_S_DEFECTIVE_TOKEN;
280
281    if (((OM_uint32) *p++) != oid->length)
282       return GSS_S_DEFECTIVE_TOKEN;
283
284    return memcmp(p, oid->elements, oid->length);
285 }
286
287 /*
288  * Name attributes to environment variables code
289  * This code is strongly based on the code from https://github.com/modauthgssapi/mod_auth_gssapi
290  */
291
292 static char *mag_status(request_rec *req, int type, uint32_t err)
293 {
294     uint32_t maj_ret, min_ret;
295     gss_buffer_desc text;
296     uint32_t msg_ctx;
297     char *msg_ret;
298     int len;
299
300     msg_ret = NULL;
301     msg_ctx = 0;
302     do {
303         maj_ret = gss_display_status(&min_ret, err, type,
304                                      GSS_C_NO_OID, &msg_ctx, &text);
305         if (maj_ret != GSS_S_COMPLETE) {
306             return msg_ret;
307         }
308
309         len = text.length;
310         if (msg_ret) {
311             msg_ret = apr_psprintf(req->pool, "%s, %*s",
312                                    msg_ret, len, (char *)text.value);
313         } else {
314             msg_ret = apr_psprintf(req->pool, "%*s", len, (char *)text.value);
315         }
316         gss_release_buffer(&min_ret, &text);
317     } while (msg_ctx != 0);
318
319     return msg_ret;
320 }
321
322 char *mag_error(request_rec *req, const char *msg, uint32_t maj, uint32_t min)
323 {
324     char *msg_maj;
325     char *msg_min;
326
327     msg_maj = mag_status(req, GSS_C_GSS_CODE, maj);
328     msg_min = mag_status(req, GSS_C_MECH_CODE, min);
329     return apr_psprintf(req->pool, "%s: [%s (%s)]", msg, msg_maj, msg_min);
330 }
331
332
333 static char mag_get_name_attr(request_rec *req,
334                               gss_name_t name, name_attr *attr)
335 {
336     uint32_t maj, min;
337
338     maj = gss_get_name_attribute(&min, name, &attr->name,
339                                  &attr->authenticated,
340                                  &attr->complete,
341                                  &attr->value,
342                                  &attr->display_value,
343                                  &attr->more);
344     if (GSS_ERROR(maj)) {
345         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
346                       "gss_get_name_attribute() failed on %.*s%s",
347                       (int)attr->name.length, (char *)attr->name.value,
348                       mag_error(req, "", maj, min));
349         return 0;
350     }
351
352     return 1;
353 }
354
355 #define GSS_NAME_ATTR_USERDATA "GSS Name Attributes Userdata"
356
357 static apr_status_t mag_gss_name_attrs_cleanup(void *data)
358 {
359     gss_conn_ctx_t *gss_ctx = (gss_conn_ctx_t *) data;
360     free(gss_ctx->name_attributes);
361     gss_ctx->name_attributes = NULL;
362     return 0;
363 }
364
365 static void mc_add_name_attribute(gss_conn_ctx_t *gss_ctx,
366                                   const char *name, const char *value)
367 {
368     size_t size;
369
370     if (gss_ctx->na_count % 16 == 0) {
371         size = sizeof(mag_attr) * (gss_ctx->na_count + 16);
372         gss_ctx->name_attributes = realloc(gss_ctx->name_attributes, size);
373         if (!gss_ctx->name_attributes) apr_pool_abort_get(gss_ctx->pool)(ENOMEM);
374         apr_pool_userdata_setn(gss_ctx, GSS_NAME_ATTR_USERDATA,
375                                mag_gss_name_attrs_cleanup, gss_ctx->pool);
376     }
377
378     gss_ctx->name_attributes[gss_ctx->na_count].name = apr_pstrdup(gss_ctx->pool, name);
379     gss_ctx->name_attributes[gss_ctx->na_count].value = apr_pstrdup(gss_ctx->pool, value);
380     gss_ctx->na_count++;
381 }
382
383 static void mag_set_env_name_attr(request_rec *req, gss_conn_ctx_t *gss_ctx,
384                                   name_attr *attr)
385 {
386     char *value = "";
387     int len = 0;
388
389     /* Prefer a display_value, otherwise fallback to value */
390     if (attr->display_value.length != 0) {
391         len = attr->display_value.length;
392         value = (char *)attr->display_value.value;
393     } else if (attr->value.length != 0) {
394         len = apr_base64_encode_len(attr->value.length);
395         value = apr_pcalloc(req->pool, len);
396         len = apr_base64_encode(value,
397                                 (char *)attr->value.value,
398                                 attr->value.length);
399     }
400
401     if (attr->number == 1) {
402         mc_add_name_attribute(gss_ctx,
403                               attr->env_name,
404                               apr_psprintf(req->pool, "%.*s", len, value));
405     }
406     if (attr->more != 0 || attr->number > 1) {
407         mc_add_name_attribute(gss_ctx,
408                               apr_psprintf(req->pool, "%s_%d",
409                                            attr->env_name, attr->number),
410                               apr_psprintf(req->pool, "%.*s", len, value));
411     }
412     if (attr->more == 0 && attr->number > 1) {
413         mc_add_name_attribute(gss_ctx,
414                               apr_psprintf(req->pool, "%s_N", attr->env_name),
415                               apr_psprintf(req->pool, "%d", attr->number - 1));
416     }
417 }
418
419 static char *mag_escape_display_value(request_rec *req,
420                                       gss_buffer_desc disp_value)
421 {
422     /* This function returns a copy (in the pool) of the given gss_buffer_t
423      * where some characters are escaped as required by RFC4627. The string is
424      * NULL terminated */
425     char *value = disp_value.value;
426     char *escaped_value = NULL;
427     char *p = NULL;
428     size_t i = 0;
429
430     /* gss_buffer_t are not \0 terminated, but our result will be. Hence,
431      * escaped length will be original length * 6 + 1 in the worst case */
432     p = escaped_value = apr_palloc(req->pool, disp_value.length * 6 + 1);
433     for (i = 0; i < disp_value.length; i++) {
434         switch (value[i]) {
435         case '"':
436             memcpy(p, "\\\"", 2);
437             p += 2;
438             break;
439         case '\\':
440             memcpy(p, "\\\\", 2);
441             p += 2;
442             break;
443         case '\b':
444             memcpy(p, "\\b", 2);
445             p += 2;
446             break;
447         case '\t':
448             memcpy(p, "\\t", 2);
449             p += 2;
450             break;
451         case '\r':
452             memcpy(p, "\\r", 2);
453             p += 2;
454             break;
455         case '\f':
456             memcpy(p, "\\f", 2);
457             p += 2;
458             break;
459         case '\n':
460             memcpy(p, "\\n", 2);
461             p += 2;
462             break;
463         default:
464             if (value[i] <= 0x1F) {
465                 apr_snprintf(p, 7, "\\u%04d", (int)value[i]);
466                 p += 6;
467             } else {
468                 *p = value[i];
469                 p += 1;
470             }
471             break;
472         }
473     }
474     /* make the string NULL terminated */
475     *p = '\0';
476     return escaped_value;
477 }
478
479 static void mag_add_json_name_attr(request_rec *req, char first,
480                                    name_attr *attr, char **json)
481 {
482     const char *value = "";
483     int len = 0;
484     char *b64value = NULL;
485     int b64len = 0;
486     const char *vstart = "";
487     const char *vend = "";
488     const char *vformat;
489
490     if (attr->value.length != 0) {
491         b64len = apr_base64_encode_len(attr->value.length);
492         b64value = apr_pcalloc(req->pool, b64len);
493         b64len = apr_base64_encode(b64value,
494                                    (char *)attr->value.value,
495                                    attr->value.length);
496     }
497     if (attr->display_value.length != 0) {
498         value = mag_escape_display_value(req, attr->display_value);
499         len = strlen(value);
500     }
501     if (attr->number == 1) {
502         *json = apr_psprintf(req->pool,
503                             "%s%s\"%.*s\":{\"authenticated\":%s,"
504                                           "\"complete\":%s,"
505                                           "\"values\":[",
506                             *json, (first ? "" : ","),
507                             (int)attr->name.length, (char *)attr->name.value,
508                             attr->authenticated ? "true" : "false",
509                             attr->complete ? "true" : "false");
510     } else {
511         vstart = ",";
512     }
513
514     if (b64value) {
515         if (len) {
516             vformat = "%s%s{\"raw\":\"%s\",\"display\":\"%.*s\"}%s";
517         } else {
518             vformat = "%s%s{\"raw\":\"%s\",\"display\":%.*s}%s";
519         }
520     } else {
521         if (len) {
522             vformat = "%s%s{\"raw\":%s,\"display\":\"%.*s\"}%s";
523         } else {
524             vformat = "%s%s{\"raw\":%s,\"display\":%.*s}%s";
525         }
526     }
527
528     if (attr->more == 0) {
529         vend = "]}";
530     }
531
532     *json = apr_psprintf(req->pool, vformat, *json,
533                         vstart,
534                         b64value ? b64value : "null",
535                         len ? len : 4, len ? value : "null",
536                         vend);
537 }
538
539 gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
540
541 void mag_get_name_attributes(request_rec *req, gss_auth_config *cfg,
542                              gss_name_t name, gss_conn_ctx_t *gss_ctx)
543 {
544     uint32_t maj, min;
545     gss_buffer_set_t attrs = GSS_C_NO_BUFFER_SET;
546     name_attr attr;
547     char *json = NULL;
548     char *error;
549     int count = 0;
550     int i, j;
551
552     maj = gss_inquire_name(&min, name, NULL, NULL, &attrs);
553     if (GSS_ERROR(maj)) {
554         error = mag_error(req, "gss_inquire_name() failed", maj, min);
555         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s", error);
556         apr_table_set(req->subprocess_env, "GSS_NAME_ATTR_ERROR", error);
557         return;
558     }
559
560     if (!attrs || attrs->count == 0) {
561         mc_add_name_attribute(gss_ctx, "GSS_NAME_ATTR_ERROR", "0 attributes found");
562     }
563
564     if (cfg->name_attributes->output_json) {
565
566         if (attrs) count = attrs->count;
567
568         json = apr_psprintf(req->pool,
569                             "{\"name\":\"%s\",\"attributes\":{",
570                             gss_ctx->user);
571     } else {
572         count = cfg->name_attributes->map_count;
573     }
574
575     for (i = 0; i < count; i++) {
576         memset(&attr, 0, sizeof(name_attr));
577
578         if (cfg->name_attributes->output_json) {
579             attr.name = attrs->elements[i];
580             for (j = 0; j < cfg->name_attributes->map_count; j++) {
581                 if (strncmp(cfg->name_attributes->map[j].attr_name,
582                             attrs->elements[i].value,
583                             attrs->elements[i].length) == 0) {
584                     attr.env_name = cfg->name_attributes->map[j].env_name;
585                     break;
586                 }
587             }
588         } else {
589             attr.name.length = strlen(cfg->name_attributes->map[i].attr_name);
590             attr.name.value = cfg->name_attributes->map[i].attr_name;
591             attr.env_name = cfg->name_attributes->map[i].env_name;
592         }
593
594         attr.number = 0;
595         attr.more = -1;
596         do {
597             attr.number++;
598             attr.value = empty_buffer;
599             attr.display_value = empty_buffer;
600             if (!mag_get_name_attr(req, name, &attr)) break;
601
602             if (cfg->name_attributes->output_json) {
603                 mag_add_json_name_attr(req, i == 0, &attr, &json);
604             }
605             if (attr.env_name) {
606                 mag_set_env_name_attr(req, gss_ctx, &attr);
607             }
608
609             gss_release_buffer(&min, &attr.value);
610             gss_release_buffer(&min, &attr.display_value);
611         } while (attr.more != 0);
612     }
613
614     if (cfg->name_attributes->output_json) {
615         json = apr_psprintf(req->pool, "%s}}", json);
616         mc_add_name_attribute(gss_ctx, "GSS_NAME_ATTRS_JSON", json);
617     }
618 }
619
620 static void mag_set_name_attributes(request_rec *req, gss_conn_ctx_t *gss_ctx)
621 {
622     int i = 0;
623     for (i = 0; i < gss_ctx->na_count; i++) {
624         apr_table_set(req->subprocess_env,
625                       gss_ctx->name_attributes[i].name,
626                       gss_ctx->name_attributes[i].value);
627     }
628 }
629
630 void mag_set_req_data(request_rec *req,
631                       gss_auth_config *cfg,
632                       gss_conn_ctx_t *gss_ctx)
633 {
634     if (gss_ctx->name_attributes) {
635         mag_set_name_attributes(req, gss_ctx);
636     }
637 }
638
639 static apr_status_t mag_name_attrs_cleanup(void *data)
640 {
641     gss_auth_config *cfg = (gss_auth_config *)data;
642     free(cfg->name_attributes);
643     cfg->name_attributes = NULL;
644     return 0;
645 }
646
647 const char *mag_name_attrs(cmd_parms *parms, void *mconfig,
648                                   const char *w)
649 {
650     gss_auth_config *cfg = (gss_auth_config *)mconfig;
651     void *tmp_na;
652     size_t size = 0;
653     char *p;
654     int c;
655
656     if (!cfg->name_attributes) {
657         size = sizeof(mag_name_attributes)
658                 + (sizeof(mag_na_map) * 16);
659     } else if (cfg->name_attributes->map_count % 16 == 0) {
660         size = sizeof(mag_name_attributes)
661                 + (sizeof(mag_na_map)
662                     * (cfg->name_attributes->map_count + 16));
663     }
664     if (size) {
665         tmp_na = realloc(cfg->name_attributes, size);
666         if (!tmp_na) apr_pool_abort_get(cfg->pool)(ENOMEM);
667
668         if (cfg->name_attributes) {
669             size_t empty = (sizeof(mag_na_map) * 16);
670             memset(tmp_na + size - empty, 0, empty);
671         } else {
672             memset(tmp_na, 0, size);
673         }
674         cfg->name_attributes = (mag_name_attributes *)tmp_na;
675         apr_pool_userdata_setn(cfg, GSS_NAME_ATTR_USERDATA,
676                                mag_name_attrs_cleanup, cfg->pool);
677     }
678
679
680     p = strchr(w, ' ');
681     if (p == NULL) {
682         if (strcmp(w, "json") == 0) {
683             cfg->name_attributes->output_json = 1;
684         } else {
685             ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
686                          "Invalid Name Attributes value [%s].", w);
687         }
688         return NULL;
689     }
690
691     c = cfg->name_attributes->map_count;
692     cfg->name_attributes->map[c].env_name = apr_pstrndup(cfg->pool, w, p-w);
693     p++;
694     cfg->name_attributes->map[c].attr_name = apr_pstrdup(cfg->pool, p);
695     cfg->name_attributes->map_count += 1;
696
697     return NULL;
698 }