75a40bdca17d762aabc213753c28ef7604d019f0
[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 = (struct 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, gss_buffer_desc disp_value)
420 {
421     /* This function returns a copy (in the pool) of the given gss_buffer_t where every
422      * occurrence of " has been replaced by \". This string is NULL terminated */
423     int i = 0, j = 0, n_quotes = 0;
424     char *escaped_value = NULL;
425     char *value = (char*) disp_value.value;
426
427     // count number of quotes in the input string
428     for (i = 0, j = 0; i < disp_value.length; i++)
429         if (value[i] == '"')
430             n_quotes++;
431
432     // if there are no quotes, just return a copy of the string
433     if (n_quotes == 0)
434         return apr_pstrndup(req->pool, value, disp_value.length);
435
436     // gss_buffer_t are not \0 terminated, but our result will be
437     escaped_value = apr_palloc(req->pool, disp_value.length + n_quotes + 1);
438     for (i = 0,j = 0; i < disp_value.length; i++, j++) {
439         if (value[i] == '"') {
440             escaped_value[j] = '\\';
441             j++;
442         }
443         escaped_value[j] = value[i];
444     }
445     // make the string NULL terminated
446     escaped_value[j] = '\0';
447     return escaped_value;
448 }
449
450 static void mag_add_json_name_attr(request_rec *req, char first,
451                                    name_attr *attr, char **json)
452 {
453     const char *value = "";
454     int len = 0;
455     char *b64value = NULL;
456     int b64len = 0;
457     const char *vstart = "";
458     const char *vend = "";
459     const char *vformat;
460
461     if (attr->value.length != 0) {
462         b64len = apr_base64_encode_len(attr->value.length);
463         b64value = apr_pcalloc(req->pool, b64len);
464         b64len = apr_base64_encode(b64value,
465                                    (char *)attr->value.value,
466                                    attr->value.length);
467     }
468     if (attr->display_value.length != 0) {
469         value = mag_escape_display_value(req, attr->display_value);
470         len = strlen(value);
471     }
472     if (attr->number == 1) {
473         *json = apr_psprintf(req->pool,
474                             "%s%s\"%.*s\":{\"authenticated\":%s,"
475                                           "\"complete\":%s,"
476                                           "\"values\":[",
477                             *json, (first ? "" : ","),
478                             (int)attr->name.length, (char *)attr->name.value,
479                             attr->authenticated ? "true" : "false",
480                             attr->complete ? "true" : "false");
481     } else {
482         vstart = ",";
483     }
484
485     if (b64value) {
486         if (len) {
487             vformat = "%s%s{\"raw\":\"%s\",\"display\":\"%.*s\"}%s";
488         } else {
489             vformat = "%s%s{\"raw\":\"%s\",\"display\":%.*s}%s";
490         }
491     } else {
492         if (len) {
493             vformat = "%s%s{\"raw\":%s,\"display\":\"%.*s\"}%s";
494         } else {
495             vformat = "%s%s{\"raw\":%s,\"display\":%.*s}%s";
496         }
497     }
498
499     if (attr->more == 0) {
500         vend = "]}";
501     }
502
503     *json = apr_psprintf(req->pool, vformat, *json,
504                         vstart,
505                         b64value ? b64value : "null",
506                         len ? len : 4, len ? value : "null",
507                         vend);
508 }
509
510 gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
511
512 void mag_get_name_attributes(request_rec *req, gss_auth_config *cfg,
513                              gss_name_t name, gss_conn_ctx_t *gss_ctx)
514 {
515     uint32_t maj, min;
516     gss_buffer_set_t attrs = GSS_C_NO_BUFFER_SET;
517     name_attr attr;
518     char *json = NULL;
519     char *error;
520     int count = 0;
521     int i, j;
522
523     maj = gss_inquire_name(&min, name, NULL, NULL, &attrs);
524     if (GSS_ERROR(maj)) {
525         error = mag_error(req, "gss_inquire_name() failed", maj, min);
526         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s", error);
527         apr_table_set(req->subprocess_env, "GSS_NAME_ATTR_ERROR", error);
528         return;
529     }
530
531     if (!attrs || attrs->count == 0) {
532         mc_add_name_attribute(gss_ctx, "GSS_NAME_ATTR_ERROR", "0 attributes found");
533     }
534
535     if (cfg->name_attributes->output_json) {
536
537         if (attrs) count = attrs->count;
538
539         json = apr_psprintf(req->pool,
540                             "{\"name\":\"%s\",\"attributes\":{",
541                             gss_ctx->user);
542     } else {
543         count = cfg->name_attributes->map_count;
544     }
545
546     for (i = 0; i < count; i++) {
547         memset(&attr, 0, sizeof(name_attr));
548
549         if (cfg->name_attributes->output_json) {
550             attr.name = attrs->elements[i];
551             for (j = 0; j < cfg->name_attributes->map_count; j++) {
552                 if (strncmp(cfg->name_attributes->map[j].attr_name,
553                             attrs->elements[i].value,
554                             attrs->elements[i].length) == 0) {
555                     attr.env_name = cfg->name_attributes->map[j].env_name;
556                     break;
557                 }
558             }
559         } else {
560             attr.name.length = strlen(cfg->name_attributes->map[i].attr_name);
561             attr.name.value = cfg->name_attributes->map[i].attr_name;
562             attr.env_name = cfg->name_attributes->map[i].env_name;
563         }
564
565         attr.number = 0;
566         attr.more = -1;
567         do {
568             attr.number++;
569             attr.value = empty_buffer;
570             attr.display_value = empty_buffer;
571             if (!mag_get_name_attr(req, name, &attr)) break;
572
573             if (cfg->name_attributes->output_json) {
574                 mag_add_json_name_attr(req, i == 0, &attr, &json);
575             }
576             if (attr.env_name) {
577                 mag_set_env_name_attr(req, gss_ctx, &attr);
578             }
579
580             gss_release_buffer(&min, &attr.value);
581             gss_release_buffer(&min, &attr.display_value);
582         } while (attr.more != 0);
583     }
584
585     if (cfg->name_attributes->output_json) {
586         json = apr_psprintf(req->pool, "%s}}", json);
587         mc_add_name_attribute(gss_ctx, "GSS_NAME_ATTRS_JSON", json);
588     }
589 }
590
591 static void mag_set_name_attributes(request_rec *req, gss_conn_ctx_t *gss_ctx)
592 {
593     int i = 0;
594     for (i = 0; i < gss_ctx->na_count; i++) {
595         apr_table_set(req->subprocess_env,
596                       gss_ctx->name_attributes[i].name,
597                       gss_ctx->name_attributes[i].value);
598     }
599 }
600
601 void mag_set_req_data(request_rec *req,
602                       gss_auth_config *cfg,
603                       gss_conn_ctx_t *gss_ctx)
604 {
605     if (gss_ctx->name_attributes) {
606         mag_set_name_attributes(req, gss_ctx);
607     }
608 }
609
610 static apr_status_t mag_name_attrs_cleanup(void *data)
611 {
612     gss_auth_config *cfg = (gss_auth_config *)data;
613     free(cfg->name_attributes);
614     cfg->name_attributes = NULL;
615     return 0;
616 }
617
618 const char *mag_name_attrs(cmd_parms *parms, void *mconfig,
619                                   const char *w)
620 {
621     gss_auth_config *cfg = (gss_auth_config *)mconfig;
622     void *tmp_na;
623     size_t size = 0;
624     char *p;
625     int c;
626
627     if (!cfg->name_attributes) {
628         size = sizeof(mag_name_attributes)
629                 + (sizeof(mag_na_map) * 16);
630     } else if (cfg->name_attributes->map_count % 16 == 0) {
631         size = sizeof(mag_name_attributes)
632                 + (sizeof(mag_na_map)
633                     * (cfg->name_attributes->map_count + 16));
634     }
635     if (size) {
636         tmp_na = realloc(cfg->name_attributes, size);
637         if (!tmp_na) apr_pool_abort_get(cfg->pool)(ENOMEM);
638
639         if (cfg->name_attributes) {
640             size_t empty = (sizeof(mag_na_map) * 16);
641             memset(tmp_na + size - empty, 0, empty);
642         } else {
643             memset(tmp_na, 0, size);
644         }
645         cfg->name_attributes = (mag_name_attributes *)tmp_na;
646         apr_pool_userdata_setn(cfg, GSS_NAME_ATTR_USERDATA,
647                                mag_name_attrs_cleanup, cfg->pool);
648     }
649
650
651     p = strchr(w, ' ');
652     if (p == NULL) {
653         if (strcmp(w, "json") == 0) {
654             cfg->name_attributes->output_json = 1;
655         } else {
656             ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
657                          "Invalid Name Attributes value [%s].", w);
658         }
659         return NULL;
660     }
661
662     c = cfg->name_attributes->map_count;
663     cfg->name_attributes->map[c].env_name = apr_pstrndup(cfg->pool, w, p-w);
664     p++;
665     cfg->name_attributes->map[c].attr_name = apr_pstrdup(cfg->pool, p);
666     cfg->name_attributes->map_count += 1;
667
668     return NULL;
669 }