Remove forward basic auth and fix docs
[mod_auth_gssapi.git] / src / mod_auth_gssapi.c
1 /*
2    MOD AUTH GSSAPI
3
4    Copyright (C) 2014 Simo Sorce <simo@redhat.com>
5
6    Permission is hereby granted, free of charge, to any person obtaining a
7    copy of this software and associated documentation files (the "Software"),
8    to deal in the Software without restriction, including without limitation
9    the rights to use, copy, modify, merge, publish, distribute, sublicense,
10    and/or sell copies of the Software, and to permit persons to whom the
11    Software is furnished to do so, subject to the following conditions:
12
13    The above copyright notice and this permission notice shall be included in
14    all copies or substantial portions of the Software.
15
16    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22    DEALINGS IN THE SOFTWARE.
23 */
24
25 #include "mod_auth_gssapi.h"
26
27 #define MOD_AUTH_GSSAPI_VERSION PACKAGE_NAME "/" PACKAGE_VERSION
28
29 module AP_MODULE_DECLARE_DATA auth_gssapi_module;
30
31 APLOG_USE_MODULE(auth_gssapi);
32
33 APR_DECLARE_OPTIONAL_FN(int, ssl_is_https, (conn_rec *));
34
35 static char *mag_status(request_rec *req, int type, uint32_t err)
36 {
37     uint32_t maj_ret, min_ret;
38     gss_buffer_desc text;
39     uint32_t msg_ctx;
40     char *msg_ret;
41     int len;
42
43     msg_ret = NULL;
44     msg_ctx = 0;
45     do {
46         maj_ret = gss_display_status(&min_ret, err, type,
47                                      GSS_C_NO_OID, &msg_ctx, &text);
48         if (maj_ret != GSS_S_COMPLETE) {
49             return msg_ret;
50         }
51
52         len = text.length;
53         if (msg_ret) {
54             msg_ret = apr_psprintf(req->pool, "%s, %*s",
55                                    msg_ret, len, (char *)text.value);
56         } else {
57             msg_ret = apr_psprintf(req->pool, "%*s", len, (char *)text.value);
58         }
59         gss_release_buffer(&min_ret, &text);
60     } while (msg_ctx != 0);
61
62     return msg_ret;
63 }
64
65 static char *mag_error(request_rec *req, const char *msg,
66                        uint32_t maj, uint32_t min)
67 {
68     char *msg_maj;
69     char *msg_min;
70
71     msg_maj = mag_status(req, GSS_C_GSS_CODE, maj);
72     msg_min = mag_status(req, GSS_C_MECH_CODE, min);
73     return apr_psprintf(req->pool, "%s: [%s (%s)]", msg, msg_maj, msg_min);
74 }
75
76 static APR_OPTIONAL_FN_TYPE(ssl_is_https) *mag_is_https = NULL;
77
78 static int mag_post_config(apr_pool_t *cfgpool, apr_pool_t *log,
79                            apr_pool_t *temp, server_rec *s)
80 {
81     /* FIXME: create mutex to deal with connections and contexts ? */
82     mag_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
83     mag_post_config_session();
84     ap_add_version_component(cfgpool, MOD_AUTH_GSSAPI_VERSION);
85
86     return OK;
87 }
88
89 static int mag_pre_connection(conn_rec *c, void *csd)
90 {
91     struct mag_conn *mc;
92
93     mc = apr_pcalloc(c->pool, sizeof(struct mag_conn));
94     if (!mc) return DECLINED;
95
96     mc->parent = c->pool;
97     ap_set_module_config(c->conn_config, &auth_gssapi_module, (void*)mc);
98     return OK;
99 }
100
101 static apr_status_t mag_conn_destroy(void *ptr)
102 {
103     struct mag_conn *mc = (struct mag_conn *)ptr;
104     uint32_t min;
105
106     if (mc->ctx) {
107         (void)gss_delete_sec_context(&min, &mc->ctx, GSS_C_NO_BUFFER);
108         mc->established = false;
109     }
110     return APR_SUCCESS;
111 }
112
113 static bool mag_conn_is_https(conn_rec *c)
114 {
115     if (mag_is_https) {
116         if (mag_is_https(c)) return true;
117     }
118
119     return false;
120 }
121
122 static void mag_store_deleg_creds(request_rec *req,
123                                   char *dir, char *clientname,
124                                   gss_cred_id_t delegated_cred,
125                                   char **ccachefile)
126 {
127     gss_key_value_element_desc element;
128     gss_key_value_set_desc store;
129     char *value;
130     uint32_t maj, min;
131
132     value = apr_psprintf(req->pool, "FILE:%s/%s", dir, clientname);
133     if (!value) {
134         ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, NULL,
135                      "OOM storing delegated credentials");
136         return;
137     }
138
139     element.key = "ccache";
140     element.value = value;
141     store.elements = &element;
142     store.count = 1;
143
144     maj = gss_store_cred_into(&min, delegated_cred, GSS_C_INITIATE,
145                               GSS_C_NULL_OID, 1, 1, &store, NULL, NULL);
146     if (GSS_ERROR(maj)) {
147         ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
148                       mag_error(req, "failed to store delegated creds",
149                                 maj, min));
150     }
151
152     *ccachefile = value;
153 }
154
155 static int mag_auth(request_rec *req)
156 {
157     const char *type;
158     const char *auth_type;
159     struct mag_config *cfg;
160     const char *auth_header;
161     char *auth_header_type;
162     char *auth_header_value;
163     int ret = HTTP_UNAUTHORIZED;
164     gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
165     gss_ctx_id_t *pctx;
166     gss_buffer_desc input = GSS_C_EMPTY_BUFFER;
167     gss_buffer_desc output = GSS_C_EMPTY_BUFFER;
168     gss_buffer_desc name = GSS_C_EMPTY_BUFFER;
169     gss_name_t client = GSS_C_NO_NAME;
170     gss_cred_id_t user_cred = GSS_C_NO_CREDENTIAL;
171     gss_cred_id_t acquired_cred = GSS_C_NO_CREDENTIAL;
172     gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
173     gss_cred_usage_t cred_usage = GSS_C_ACCEPT;
174     uint32_t flags;
175     uint32_t vtime;
176     uint32_t maj, min;
177     char *reply;
178     size_t replen;
179     char *clientname;
180     gss_OID mech_type = GSS_C_NO_OID;
181     gss_buffer_desc lname = GSS_C_EMPTY_BUFFER;
182     struct mag_conn *mc = NULL;
183     bool is_basic = false;
184     gss_ctx_id_t user_ctx = GSS_C_NO_CONTEXT;
185     gss_name_t server = GSS_C_NO_NAME;
186
187     type = ap_auth_type(req);
188     if ((type == NULL) || (strcasecmp(type, "GSSAPI") != 0)) {
189         return DECLINED;
190     }
191
192     /* ignore auth for subrequests */
193     if (!ap_is_initial_req(req)) {
194         return OK;
195     }
196
197     cfg = ap_get_module_config(req->per_dir_config, &auth_gssapi_module);
198
199     if (cfg->ssl_only) {
200         if (!mag_conn_is_https(req->connection)) {
201             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
202                           "Not a TLS connection, refusing to authenticate!");
203             goto done;
204         }
205     }
206
207     if (cfg->gss_conn_ctx) {
208         mc = (struct mag_conn *)ap_get_module_config(
209                                                 req->connection->conn_config,
210                                                 &auth_gssapi_module);
211         if (!mc) {
212             ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, req,
213                           "Failed to retrieve connection context!");
214             goto done;
215         }
216     }
217
218     /* if available, session always supersedes connection bound data */
219     if (cfg->use_sessions) {
220         mag_check_session(req, cfg, &mc);
221     }
222
223     if (mc) {
224         /* register the context in the memory pool, so it can be freed
225          * when the connection/request is terminated */
226         apr_pool_userdata_set(mc, "mag_conn_ptr",
227                               mag_conn_destroy, mc->parent);
228
229         if (mc->established) {
230             ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, req,
231                           "Already established context found!");
232             apr_table_set(req->subprocess_env, "GSS_NAME", mc->gss_name);
233             req->ap_auth_type = apr_pstrdup(req->pool, mc->auth_type);
234             req->user = apr_pstrdup(req->pool, mc->user_name);
235             ret = OK;
236             goto done;
237         }
238         pctx = &mc->ctx;
239     } else {
240         pctx = &ctx;
241     }
242
243     auth_header = apr_table_get(req->headers_in, "Authorization");
244     if (!auth_header) goto done;
245
246     auth_header_type = ap_getword_white(req->pool, &auth_header);
247     if (!auth_header_type) goto done;
248
249     if (strcasecmp(auth_header_type, "Negotiate") == 0) {
250         auth_type = "Negotiate";
251
252         auth_header_value = ap_getword_white(req->pool, &auth_header);
253         if (!auth_header_value) goto done;
254         input.length = apr_base64_decode_len(auth_header_value) + 1;
255         input.value = apr_pcalloc(req->pool, input.length);
256         if (!input.value) goto done;
257         input.length = apr_base64_decode(input.value, auth_header_value);
258     } else if ((strcasecmp(auth_header_type, "Basic") == 0) &&
259                (cfg->use_basic_auth == true)) {
260         auth_type = "Basic";
261         is_basic = true;
262
263         gss_buffer_desc ba_user;
264         gss_buffer_desc ba_pwd;
265
266         ba_pwd.value = ap_pbase64decode(req->pool, auth_header);
267         if (!ba_pwd.value) goto done;
268         ba_user.value = ap_getword_nulls_nc(req->pool,
269                                             (char **)&ba_pwd.value, ':');
270         if (!ba_user.value) goto done;
271         if (((char *)ba_user.value)[0] == '\0' ||
272             ((char *)ba_pwd.value)[0] == '\0') {
273             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
274                           "Invalid empty user or password for Basic Auth");
275             goto done;
276         }
277         ba_user.length = strlen(ba_user.value);
278         ba_pwd.length = strlen(ba_pwd.value);
279         maj = gss_import_name(&min, &ba_user, GSS_C_NT_USER_NAME, &client);
280         if (GSS_ERROR(maj)) {
281             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
282                           "In Basic Auth, %s",
283                           mag_error(req, "gss_import_name() failed",
284                                     maj, min));
285             goto done;
286         }
287         maj = gss_acquire_cred_with_password(&min, client, &ba_pwd,
288                                              GSS_C_INDEFINITE,
289                                              GSS_C_NO_OID_SET,
290                                              GSS_C_INITIATE,
291                                              &user_cred, NULL, NULL);
292         if (GSS_ERROR(maj)) {
293             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
294                           "In Basic Auth, %s",
295                           mag_error(req, "gss_acquire_cred_with_password() "
296                                     "failed", maj, min));
297             goto done;
298         }
299         gss_release_name(&min, &client);
300     } else {
301         goto done;
302     }
303
304     req->ap_auth_type = apr_pstrdup(req->pool, auth_type);
305
306 #ifdef HAVE_GSS_ACQUIRE_CRED_FROM
307     if (cfg->use_s4u2proxy) {
308         cred_usage = GSS_C_BOTH;
309     }
310     if (cfg->cred_store) {
311         maj = gss_acquire_cred_from(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
312                                     GSS_C_NO_OID_SET, cred_usage,
313                                     cfg->cred_store, &acquired_cred,
314                                     NULL, NULL);
315         if (GSS_ERROR(maj)) {
316             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
317                           mag_error(req, "gss_acquire_cred_from() failed",
318                                     maj, min));
319             goto done;
320         }
321     }
322 #endif
323
324     if (is_basic) {
325         if (!acquired_cred) {
326             /* Try to acquire default creds */
327             maj = gss_acquire_cred(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
328                                    GSS_C_NO_OID_SET, cred_usage,
329                                    &acquired_cred, NULL, NULL);
330             if (GSS_ERROR(maj)) {
331                 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
332                               "%s", mag_error(req, "gss_acquire_cred_from()"
333                                               " failed", maj, min));
334                 goto done;
335             }
336         }
337         maj = gss_inquire_cred(&min, acquired_cred, &server,
338                                NULL, NULL, NULL);
339         if (GSS_ERROR(maj)) {
340             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
341                           "%s", mag_error(req, "gss_inquired_cred_() "
342                                           "failed", maj, min));
343             goto done;
344         }
345         /* output and input are inverted here, this is intentional */
346         maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
347                                    GSS_C_NO_OID, 0, 300,
348                                    GSS_C_NO_CHANNEL_BINDINGS, &output,
349                                    NULL, &input, NULL, NULL);
350         if (GSS_ERROR(maj)) {
351             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
352                           "%s", mag_error(req, "gss_init_sec_context() "
353                                           "failed", maj, min));
354             goto done;
355         }
356     }
357
358     maj = gss_accept_sec_context(&min, pctx, acquired_cred,
359                                  &input, GSS_C_NO_CHANNEL_BINDINGS,
360                                  &client, &mech_type, &output, &flags, &vtime,
361                                  &delegated_cred);
362     if (GSS_ERROR(maj)) {
363         ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
364                       mag_error(req, "gss_accept_sec_context() failed",
365                                 maj, min));
366         goto done;
367     }
368     if (is_basic) {
369         while (maj == GSS_S_CONTINUE_NEEDED) {
370             gss_release_buffer(&min, &input);
371             /* output and input are inverted here, this is intentional */
372             maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
373                                        GSS_C_NO_OID, 0, 300,
374                                        GSS_C_NO_CHANNEL_BINDINGS, &output,
375                                        NULL, &input, NULL, NULL);
376             if (GSS_ERROR(maj)) {
377                 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
378                               "%s", mag_error(req, "gss_init_sec_context() "
379                                               "failed", maj, min));
380                 goto done;
381             }
382             gss_release_buffer(&min, &output);
383             maj = gss_accept_sec_context(&min, pctx, acquired_cred,
384                                          &input, GSS_C_NO_CHANNEL_BINDINGS,
385                                          &client, &mech_type, &output, &flags,
386                                          &vtime, &delegated_cred);
387             if (GSS_ERROR(maj)) {
388                 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
389                               "%s", mag_error(req, "gss_accept_sec_context()"
390                                               " failed", maj, min));
391                 goto done;
392             }
393         }
394     } else if (maj == GSS_S_CONTINUE_NEEDED) {
395         if (!mc) {
396             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
397                           "Mechanism needs continuation but neither "
398                           "GssapiConnectionBound nor "
399                           "GssapiUseSessions are available");
400             gss_delete_sec_context(&min, pctx, GSS_C_NO_BUFFER);
401             gss_release_buffer(&min, &output);
402             output.length = 0;
403         }
404         /* auth not complete send token and wait next packet */
405         goto done;
406     }
407
408     /* Always set the GSS name in an env var */
409     maj = gss_display_name(&min, client, &name, NULL);
410     if (GSS_ERROR(maj)) {
411         ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
412                       mag_error(req, "gss_display_name() failed",
413                                 maj, min));
414         goto done;
415     }
416     clientname = apr_pstrndup(req->pool, name.value, name.length);
417     apr_table_set(req->subprocess_env, "GSS_NAME", clientname);
418
419 #ifdef HAVE_GSS_STORE_CRED_INTO
420     if (cfg->deleg_ccache_dir && delegated_cred != GSS_C_NO_CREDENTIAL) {
421         char *ccachefile = NULL;
422
423         mag_store_deleg_creds(req, cfg->deleg_ccache_dir, clientname,
424                               delegated_cred, &ccachefile);
425
426         if (ccachefile) {
427             apr_table_set(req->subprocess_env, "KRB5CCNAME", ccachefile);
428         }
429     }
430 #endif
431
432     if (cfg->map_to_local) {
433         maj = gss_localname(&min, client, mech_type, &lname);
434         if (maj != GSS_S_COMPLETE) {
435             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
436                           mag_error(req, "gss_localname() failed", maj, min));
437             goto done;
438         }
439         req->user = apr_pstrndup(req->pool, lname.value, lname.length);
440     } else {
441         req->user = clientname;
442     }
443
444     if (mc) {
445         mc->user_name = apr_pstrdup(mc->parent, req->user);
446         mc->gss_name = apr_pstrdup(mc->parent, clientname);
447         mc->established = true;
448         if (vtime == GSS_C_INDEFINITE || vtime < MIN_SESS_EXP_TIME) {
449             vtime = MIN_SESS_EXP_TIME;
450         }
451         mc->expiration = time(NULL) + vtime;
452         if (cfg->use_sessions) {
453             mag_attempt_session(req, cfg, mc);
454         }
455         mc->auth_type = auth_type;
456     }
457
458     ret = OK;
459
460 done:
461     if (ret == HTTP_UNAUTHORIZED) {
462         if (output.length != 0) {
463             replen = apr_base64_encode_len(output.length) + 1;
464             reply = apr_pcalloc(req->pool, 10 + replen);
465             if (reply) {
466                 memcpy(reply, "Negotiate ", 10);
467                 apr_base64_encode(&reply[10], output.value, output.length);
468                 apr_table_add(req->err_headers_out,
469                               "WWW-Authenticate", reply);
470             }
471         } else {
472             apr_table_add(req->err_headers_out,
473                           "WWW-Authenticate", "Negotiate");
474             if (cfg->use_basic_auth) {
475                 apr_table_add(req->err_headers_out,
476                               "WWW-Authenticate",
477                               apr_psprintf(req->pool, "Basic realm=\"%s\"",
478                                            ap_auth_name(req)));
479             }
480         }
481     }
482     gss_delete_sec_context(&min, &user_ctx, &output);
483     gss_release_cred(&min, &user_cred);
484     gss_release_cred(&min, &acquired_cred);
485     gss_release_cred(&min, &delegated_cred);
486     gss_release_buffer(&min, &output);
487     gss_release_name(&min, &client);
488     gss_release_name(&min, &server);
489     gss_release_buffer(&min, &name);
490     gss_release_buffer(&min, &lname);
491     return ret;
492 }
493
494
495 static void *mag_create_dir_config(apr_pool_t *p, char *dir)
496 {
497     struct mag_config *cfg;
498
499     cfg = (struct mag_config *)apr_pcalloc(p, sizeof(struct mag_config));
500     if (!cfg) return NULL;
501     cfg->pool = p;
502
503     return cfg;
504 }
505
506 static const char *mag_ssl_only(cmd_parms *parms, void *mconfig, int on)
507 {
508     struct mag_config *cfg = (struct mag_config *)mconfig;
509     cfg->ssl_only = on ? true : false;
510     return NULL;
511 }
512
513 static const char *mag_map_to_local(cmd_parms *parms, void *mconfig, int on)
514 {
515     struct mag_config *cfg = (struct mag_config *)mconfig;
516     cfg->map_to_local = on ? true : false;
517     return NULL;
518 }
519
520 static const char *mag_conn_ctx(cmd_parms *parms, void *mconfig, int on)
521 {
522     struct mag_config *cfg = (struct mag_config *)mconfig;
523     cfg->gss_conn_ctx = on ? true : false;
524     return NULL;
525 }
526
527 static const char *mag_use_sess(cmd_parms *parms, void *mconfig, int on)
528 {
529     struct mag_config *cfg = (struct mag_config *)mconfig;
530     cfg->use_sessions = on ? true : false;
531     return NULL;
532 }
533
534 static const char *mag_use_s4u2p(cmd_parms *parms, void *mconfig, int on)
535 {
536     struct mag_config *cfg = (struct mag_config *)mconfig;
537     cfg->use_s4u2proxy = on ? true : false;
538
539     if (cfg->deleg_ccache_dir == NULL) {
540         cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, "/tmp");
541         if (!cfg->deleg_ccache_dir) {
542             ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0,
543                          parms->server, "%s", "OOM setting deleg_ccache_dir.");
544         }
545     }
546     return NULL;
547 }
548
549 static const char *mag_sess_key(cmd_parms *parms, void *mconfig, const char *w)
550 {
551     struct mag_config *cfg = (struct mag_config *)mconfig;
552     struct databuf keys;
553     unsigned char *val;
554     apr_status_t rc;
555     const char *k;
556     int l;
557
558     if (strncmp(w, "key:", 4) != 0) {
559         ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
560                      "Invalid key format, expected prefix 'key:'");
561         return NULL;
562     }
563     k = w + 4;
564
565     l = apr_base64_decode_len(k);
566     val = apr_palloc(parms->temp_pool, l);
567     if (!val) {
568         ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
569                      "Failed to get memory to decode key");
570         return NULL;
571     }
572
573     keys.length = (int)apr_base64_decode_binary(val, k);
574     keys.value = (unsigned char *)val;
575
576     if (keys.length != 32) {
577         ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
578                      "Invalid key lenght, expected 32 got %d", keys.length);
579         return NULL;
580     }
581
582     rc = SEAL_KEY_CREATE(cfg->pool, &cfg->mag_skey, &keys);
583     if (rc != OK) {
584         ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
585                      "Failed to import sealing key!");
586     }
587     return NULL;
588 }
589
590 #define MAX_CRED_OPTIONS 10
591
592 static const char *mag_cred_store(cmd_parms *parms, void *mconfig,
593                                   const char *w)
594 {
595     struct mag_config *cfg = (struct mag_config *)mconfig;
596     gss_key_value_element_desc *elements;
597     uint32_t count;
598     size_t size;
599     const char *p;
600     char *value;
601     char *key;
602
603     p = strchr(w, ':');
604     if (!p) {
605         ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
606                      "%s [%s]", "Invalid syntax for GssapiCredStore option", w);
607         return NULL;
608     }
609
610     key = apr_pstrndup(parms->pool, w, (p-w));
611     value = apr_pstrdup(parms->pool, p + 1);
612     if (!key || !value) {
613         ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
614                      "%s", "OOM handling GssapiCredStore option");
615         return NULL;
616     }
617
618     if (!cfg->cred_store) {
619         cfg->cred_store = apr_pcalloc(parms->pool,
620                                       sizeof(gss_key_value_set_desc));
621         if (!cfg->cred_store) {
622             ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
623                          "%s", "OOM handling GssapiCredStore option");
624             return NULL;
625         }
626         size = sizeof(gss_key_value_element_desc) * MAX_CRED_OPTIONS;
627         cfg->cred_store->elements = apr_palloc(parms->pool, size);
628         if (!cfg->cred_store->elements) {
629             ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
630                          "%s", "OOM handling GssapiCredStore option");
631         }
632     }
633
634     elements = cfg->cred_store->elements;
635     count = cfg->cred_store->count;
636
637     if (count >= MAX_CRED_OPTIONS) {
638         ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
639                      "Too many GssapiCredStore options (MAX: %d)",
640                      MAX_CRED_OPTIONS);
641         return NULL;
642     }
643     cfg->cred_store->count++;
644
645     elements[count].key = key;
646     elements[count].value = value;
647
648     return NULL;
649 }
650
651 static const char *mag_deleg_ccache_dir(cmd_parms *parms, void *mconfig,
652                                         const char *value)
653 {
654     struct mag_config *cfg = (struct mag_config *)mconfig;
655
656     cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, value);
657     if (!cfg->deleg_ccache_dir) {
658         ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
659                      "%s", "OOM handling GssapiDelegCcacheDir option");
660     }
661
662     return NULL;
663 }
664
665 static const char *mag_use_basic_auth(cmd_parms *parms, void *mconfig, int on)
666 {
667     struct mag_config *cfg = (struct mag_config *)mconfig;
668
669     cfg->use_basic_auth = on ? true : false;
670     return NULL;
671 }
672
673 static const command_rec mag_commands[] = {
674     AP_INIT_FLAG("GssapiSSLonly", mag_ssl_only, NULL, OR_AUTHCFG,
675                   "Work only if connection is SSL Secured"),
676     AP_INIT_FLAG("GssapiLocalName", mag_map_to_local, NULL, OR_AUTHCFG,
677                   "Translate principals to local names"),
678     AP_INIT_FLAG("GssapiConnectionBound", mag_conn_ctx, NULL, OR_AUTHCFG,
679                   "Authentication is bound to the TCP connection"),
680     AP_INIT_FLAG("GssapiUseSessions", mag_use_sess, NULL, OR_AUTHCFG,
681                   "Authentication uses mod_sessions to hold status"),
682     AP_INIT_RAW_ARGS("GssapiSessionKey", mag_sess_key, NULL, OR_AUTHCFG,
683                      "Key Used to seal session data."),
684 #ifdef HAVE_GSS_ACQUIRE_CRED_FROM
685     AP_INIT_FLAG("GssapiUseS4U2Proxy", mag_use_s4u2p, NULL, OR_AUTHCFG,
686                   "Initializes credentials for s4u2proxy usage"),
687 #endif
688 #ifdef HAVE_GSS_STORE_CRED_INTO
689     AP_INIT_ITERATE("GssapiCredStore", mag_cred_store, NULL, OR_AUTHCFG,
690                     "Credential Store"),
691     AP_INIT_RAW_ARGS("GssapiDelegCcacheDir", mag_deleg_ccache_dir, NULL,
692                      OR_AUTHCFG, "Directory to store delegated credentials"),
693 #endif
694 #ifdef HAVE_GSS_ACQUIRE_CRED_WITH_PASSWORD
695     AP_INIT_FLAG("GssapiBasicAuth", mag_use_basic_auth, NULL, OR_AUTHCFG,
696                      "Allows use of Basic Auth for authentication"),
697 #endif
698     { NULL }
699 };
700
701 static void
702 mag_register_hooks(apr_pool_t *p)
703 {
704     ap_hook_check_user_id(mag_auth, NULL, NULL, APR_HOOK_MIDDLE);
705     ap_hook_post_config(mag_post_config, NULL, NULL, APR_HOOK_MIDDLE);
706     ap_hook_pre_connection(mag_pre_connection, NULL, NULL, APR_HOOK_MIDDLE);
707 }
708
709 module AP_MODULE_DECLARE_DATA auth_gssapi_module =
710 {
711     STANDARD20_MODULE_STUFF,
712     mag_create_dir_config,
713     NULL,
714     NULL,
715     NULL,
716     mag_commands,
717     mag_register_hooks
718 };