Add support for handling Basic Auth
[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         auth_type = "Basic";
260         is_basic = true;
261
262         gss_buffer_desc ba_user;
263         gss_buffer_desc ba_pwd;
264
265         switch (cfg->basic_auth) {
266         case BA_ON:
267             /* handle directly */
268             break;
269         case BA_FORWARD:
270             /* decline to handle ourselves, let other modules do it */
271             ret = DECLINED;
272             goto done;
273         case BA_OFF:
274             goto done;
275         default:
276             goto done;
277         }
278         ba_pwd.value = ap_pbase64decode(req->pool, auth_header);
279         if (!ba_pwd.value) goto done;
280         ba_user.value = ap_getword_nulls_nc(req->pool,
281                                             (char **)&ba_pwd.value, ':');
282         if (!ba_user.value) goto done;
283         if (((char *)ba_user.value)[0] == '\0' ||
284             ((char *)ba_pwd.value)[0] == '\0') {
285             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
286                           "Invalid empty user or password for Basic Auth");
287             goto done;
288         }
289         ba_user.length = strlen(ba_user.value);
290         ba_pwd.length = strlen(ba_pwd.value);
291         maj = gss_import_name(&min, &ba_user, GSS_C_NT_USER_NAME, &client);
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_import_name() failed",
296                                     maj, min));
297             goto done;
298         }
299         maj = gss_acquire_cred_with_password(&min, client, &ba_pwd,
300                                              GSS_C_INDEFINITE,
301                                              GSS_C_NO_OID_SET,
302                                              GSS_C_INITIATE,
303                                              &user_cred, NULL, NULL);
304         if (GSS_ERROR(maj)) {
305             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
306                           "In Basic Auth, %s",
307                           mag_error(req, "gss_acquire_cred_with_password() "
308                                     "failed", maj, min));
309             goto done;
310         }
311         gss_release_name(&min, &client);
312     } else {
313         goto done;
314     }
315
316     req->ap_auth_type = apr_pstrdup(req->pool, auth_type);
317
318 #ifdef HAVE_GSS_ACQUIRE_CRED_FROM
319     if (cfg->use_s4u2proxy) {
320         cred_usage = GSS_C_BOTH;
321     }
322     if (cfg->cred_store) {
323         maj = gss_acquire_cred_from(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
324                                     GSS_C_NO_OID_SET, cred_usage,
325                                     cfg->cred_store, &acquired_cred,
326                                     NULL, NULL);
327         if (GSS_ERROR(maj)) {
328             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
329                           mag_error(req, "gss_acquire_cred_from() failed",
330                                     maj, min));
331             goto done;
332         }
333     }
334 #endif
335
336     if (is_basic) {
337         if (!acquired_cred) {
338             /* Try to acquire default creds */
339             maj = gss_acquire_cred(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
340                                    GSS_C_NO_OID_SET, cred_usage,
341                                    &acquired_cred, NULL, NULL);
342             if (GSS_ERROR(maj)) {
343                 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
344                               "%s", mag_error(req, "gss_acquire_cred_from()"
345                                               " failed", maj, min));
346                 goto done;
347             }
348         }
349         maj = gss_inquire_cred(&min, acquired_cred, &server,
350                                NULL, NULL, NULL);
351         if (GSS_ERROR(maj)) {
352             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
353                           "%s", mag_error(req, "gss_inquired_cred_() "
354                                           "failed", maj, min));
355             goto done;
356         }
357         /* output and input are inverted here, this is intentional */
358         maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
359                                    GSS_C_NO_OID, 0, 300,
360                                    GSS_C_NO_CHANNEL_BINDINGS, &output,
361                                    NULL, &input, NULL, NULL);
362         if (GSS_ERROR(maj)) {
363             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
364                           "%s", mag_error(req, "gss_init_sec_context() "
365                                           "failed", maj, min));
366             goto done;
367         }
368     }
369
370     maj = gss_accept_sec_context(&min, pctx, acquired_cred,
371                                  &input, GSS_C_NO_CHANNEL_BINDINGS,
372                                  &client, &mech_type, &output, &flags, &vtime,
373                                  &delegated_cred);
374     if (GSS_ERROR(maj)) {
375         ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
376                       mag_error(req, "gss_accept_sec_context() failed",
377                                 maj, min));
378         goto done;
379     }
380     if (is_basic) {
381         while (maj == GSS_S_CONTINUE_NEEDED) {
382             gss_release_buffer(&min, &input);
383             /* output and input are inverted here, this is intentional */
384             maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
385                                        GSS_C_NO_OID, 0, 300,
386                                        GSS_C_NO_CHANNEL_BINDINGS, &output,
387                                        NULL, &input, NULL, NULL);
388             if (GSS_ERROR(maj)) {
389                 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
390                               "%s", mag_error(req, "gss_init_sec_context() "
391                                               "failed", maj, min));
392                 goto done;
393             }
394             gss_release_buffer(&min, &output);
395             maj = gss_accept_sec_context(&min, pctx, acquired_cred,
396                                          &input, GSS_C_NO_CHANNEL_BINDINGS,
397                                          &client, &mech_type, &output, &flags,
398                                          &vtime, &delegated_cred);
399             if (GSS_ERROR(maj)) {
400                 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
401                               "%s", mag_error(req, "gss_accept_sec_context()"
402                                               " failed", maj, min));
403                 goto done;
404             }
405         }
406     } else if (maj == GSS_S_CONTINUE_NEEDED) {
407         if (!mc) {
408             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
409                           "Mechanism needs continuation but neither "
410                           "GssapiConnectionBound nor "
411                           "GssapiUseSessions are available");
412             gss_delete_sec_context(&min, pctx, GSS_C_NO_BUFFER);
413             gss_release_buffer(&min, &output);
414             output.length = 0;
415         }
416         /* auth not complete send token and wait next packet */
417         goto done;
418     }
419
420     /* Always set the GSS name in an env var */
421     maj = gss_display_name(&min, client, &name, NULL);
422     if (GSS_ERROR(maj)) {
423         ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
424                       mag_error(req, "gss_display_name() failed",
425                                 maj, min));
426         goto done;
427     }
428     clientname = apr_pstrndup(req->pool, name.value, name.length);
429     apr_table_set(req->subprocess_env, "GSS_NAME", clientname);
430
431 #ifdef HAVE_GSS_STORE_CRED_INTO
432     if (cfg->deleg_ccache_dir && delegated_cred != GSS_C_NO_CREDENTIAL) {
433         char *ccachefile = NULL;
434
435         mag_store_deleg_creds(req, cfg->deleg_ccache_dir, clientname,
436                               delegated_cred, &ccachefile);
437
438         if (ccachefile) {
439             apr_table_set(req->subprocess_env, "KRB5CCNAME", ccachefile);
440         }
441     }
442 #endif
443
444     if (cfg->map_to_local) {
445         maj = gss_localname(&min, client, mech_type, &lname);
446         if (maj != GSS_S_COMPLETE) {
447             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
448                           mag_error(req, "gss_localname() failed", maj, min));
449             goto done;
450         }
451         req->user = apr_pstrndup(req->pool, lname.value, lname.length);
452     } else {
453         req->user = clientname;
454     }
455
456     if (mc) {
457         mc->user_name = apr_pstrdup(mc->parent, req->user);
458         mc->gss_name = apr_pstrdup(mc->parent, clientname);
459         mc->established = true;
460         if (vtime == GSS_C_INDEFINITE || vtime < MIN_SESS_EXP_TIME) {
461             vtime = MIN_SESS_EXP_TIME;
462         }
463         mc->expiration = time(NULL) + vtime;
464         if (cfg->use_sessions) {
465             mag_attempt_session(req, cfg, mc);
466         }
467         mc->auth_type = auth_type;
468     }
469
470     ret = OK;
471
472 done:
473     if (ret == HTTP_UNAUTHORIZED) {
474         if (output.length != 0) {
475             replen = apr_base64_encode_len(output.length) + 1;
476             reply = apr_pcalloc(req->pool, 10 + replen);
477             if (reply) {
478                 memcpy(reply, "Negotiate ", 10);
479                 apr_base64_encode(&reply[10], output.value, output.length);
480                 apr_table_add(req->err_headers_out,
481                               "WWW-Authenticate", reply);
482             }
483         } else {
484             apr_table_add(req->err_headers_out,
485                           "WWW-Authenticate", "Negotiate");
486             if (cfg->basic_auth != BA_OFF) {
487                 apr_table_add(req->err_headers_out,
488                               "WWW-Authenticate",
489                               apr_psprintf(req->pool, "Basic realm=\"%s\"",
490                                            ap_auth_name(req)));
491             }
492         }
493     }
494     gss_delete_sec_context(&min, &user_ctx, &output);
495     gss_release_cred(&min, &user_cred);
496     gss_release_cred(&min, &acquired_cred);
497     gss_release_cred(&min, &delegated_cred);
498     gss_release_buffer(&min, &output);
499     gss_release_name(&min, &client);
500     gss_release_name(&min, &server);
501     gss_release_buffer(&min, &name);
502     gss_release_buffer(&min, &lname);
503     return ret;
504 }
505
506
507 static void *mag_create_dir_config(apr_pool_t *p, char *dir)
508 {
509     struct mag_config *cfg;
510
511     cfg = (struct mag_config *)apr_pcalloc(p, sizeof(struct mag_config));
512     if (!cfg) return NULL;
513     cfg->pool = p;
514
515     return cfg;
516 }
517
518 static const char *mag_ssl_only(cmd_parms *parms, void *mconfig, int on)
519 {
520     struct mag_config *cfg = (struct mag_config *)mconfig;
521     cfg->ssl_only = on ? true : false;
522     return NULL;
523 }
524
525 static const char *mag_map_to_local(cmd_parms *parms, void *mconfig, int on)
526 {
527     struct mag_config *cfg = (struct mag_config *)mconfig;
528     cfg->map_to_local = on ? true : false;
529     return NULL;
530 }
531
532 static const char *mag_conn_ctx(cmd_parms *parms, void *mconfig, int on)
533 {
534     struct mag_config *cfg = (struct mag_config *)mconfig;
535     cfg->gss_conn_ctx = on ? true : false;
536     return NULL;
537 }
538
539 static const char *mag_use_sess(cmd_parms *parms, void *mconfig, int on)
540 {
541     struct mag_config *cfg = (struct mag_config *)mconfig;
542     cfg->use_sessions = on ? true : false;
543     return NULL;
544 }
545
546 static const char *mag_use_s4u2p(cmd_parms *parms, void *mconfig, int on)
547 {
548     struct mag_config *cfg = (struct mag_config *)mconfig;
549     cfg->use_s4u2proxy = on ? true : false;
550
551     if (cfg->deleg_ccache_dir == NULL) {
552         cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, "/tmp");
553         if (!cfg->deleg_ccache_dir) {
554             ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0,
555                          parms->server, "%s", "OOM setting deleg_ccache_dir.");
556         }
557     }
558     return NULL;
559 }
560
561 static const char *mag_sess_key(cmd_parms *parms, void *mconfig, const char *w)
562 {
563     struct mag_config *cfg = (struct mag_config *)mconfig;
564     struct databuf keys;
565     unsigned char *val;
566     apr_status_t rc;
567     const char *k;
568     int l;
569
570     if (strncmp(w, "key:", 4) != 0) {
571         ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
572                      "Invalid key format, expected prefix 'key:'");
573         return NULL;
574     }
575     k = w + 4;
576
577     l = apr_base64_decode_len(k);
578     val = apr_palloc(parms->temp_pool, l);
579     if (!val) {
580         ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
581                      "Failed to get memory to decode key");
582         return NULL;
583     }
584
585     keys.length = (int)apr_base64_decode_binary(val, k);
586     keys.value = (unsigned char *)val;
587
588     if (keys.length != 32) {
589         ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
590                      "Invalid key lenght, expected 32 got %d", keys.length);
591         return NULL;
592     }
593
594     rc = SEAL_KEY_CREATE(cfg->pool, &cfg->mag_skey, &keys);
595     if (rc != OK) {
596         ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
597                      "Failed to import sealing key!");
598     }
599     return NULL;
600 }
601
602 #define MAX_CRED_OPTIONS 10
603
604 static const char *mag_cred_store(cmd_parms *parms, void *mconfig,
605                                   const char *w)
606 {
607     struct mag_config *cfg = (struct mag_config *)mconfig;
608     gss_key_value_element_desc *elements;
609     uint32_t count;
610     size_t size;
611     const char *p;
612     char *value;
613     char *key;
614
615     p = strchr(w, ':');
616     if (!p) {
617         ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
618                      "%s [%s]", "Invalid syntax for GssapiCredStore option", w);
619         return NULL;
620     }
621
622     key = apr_pstrndup(parms->pool, w, (p-w));
623     value = apr_pstrdup(parms->pool, p + 1);
624     if (!key || !value) {
625         ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
626                      "%s", "OOM handling GssapiCredStore option");
627         return NULL;
628     }
629
630     if (!cfg->cred_store) {
631         cfg->cred_store = apr_pcalloc(parms->pool,
632                                       sizeof(gss_key_value_set_desc));
633         if (!cfg->cred_store) {
634             ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
635                          "%s", "OOM handling GssapiCredStore option");
636             return NULL;
637         }
638         size = sizeof(gss_key_value_element_desc) * MAX_CRED_OPTIONS;
639         cfg->cred_store->elements = apr_palloc(parms->pool, size);
640         if (!cfg->cred_store->elements) {
641             ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
642                          "%s", "OOM handling GssapiCredStore option");
643         }
644     }
645
646     elements = cfg->cred_store->elements;
647     count = cfg->cred_store->count;
648
649     if (count >= MAX_CRED_OPTIONS) {
650         ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
651                      "Too many GssapiCredStore options (MAX: %d)",
652                      MAX_CRED_OPTIONS);
653         return NULL;
654     }
655     cfg->cred_store->count++;
656
657     elements[count].key = key;
658     elements[count].value = value;
659
660     return NULL;
661 }
662
663 static const char *mag_deleg_ccache_dir(cmd_parms *parms, void *mconfig,
664                                         const char *value)
665 {
666     struct mag_config *cfg = (struct mag_config *)mconfig;
667
668     cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, value);
669     if (!cfg->deleg_ccache_dir) {
670         ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
671                      "%s", "OOM handling GssapiDelegCcacheDir option");
672     }
673
674     return NULL;
675 }
676
677 static const char *mag_use_basic_auth(cmd_parms *parms, void *mconfig,
678                                       const char *value)
679 {
680     struct mag_config *cfg = (struct mag_config *)mconfig;
681
682     if (strcasecmp(value, "on") == 0) {
683         cfg->basic_auth = BA_ON;
684     } else if (strcasecmp(value, "forward") == 0) {
685         cfg->basic_auth = BA_FORWARD;
686     } else {
687         cfg->basic_auth = BA_OFF;
688     }
689
690     return NULL;
691 }
692
693 static const command_rec mag_commands[] = {
694     AP_INIT_FLAG("GssapiSSLonly", mag_ssl_only, NULL, OR_AUTHCFG,
695                   "Work only if connection is SSL Secured"),
696     AP_INIT_FLAG("GssapiLocalName", mag_map_to_local, NULL, OR_AUTHCFG,
697                   "Translate principals to local names"),
698     AP_INIT_FLAG("GssapiConnectionBound", mag_conn_ctx, NULL, OR_AUTHCFG,
699                   "Authentication is bound to the TCP connection"),
700     AP_INIT_FLAG("GssapiUseSessions", mag_use_sess, NULL, OR_AUTHCFG,
701                   "Authentication uses mod_sessions to hold status"),
702     AP_INIT_RAW_ARGS("GssapiSessionKey", mag_sess_key, NULL, OR_AUTHCFG,
703                      "Key Used to seal session data."),
704 #ifdef HAVE_GSS_ACQUIRE_CRED_FROM
705     AP_INIT_FLAG("GssapiUseS4U2Proxy", mag_use_s4u2p, NULL, OR_AUTHCFG,
706                   "Initializes credentials for s4u2proxy usage"),
707 #endif
708 #ifdef HAVE_GSS_STORE_CRED_INTO
709     AP_INIT_ITERATE("GssapiCredStore", mag_cred_store, NULL, OR_AUTHCFG,
710                     "Credential Store"),
711     AP_INIT_RAW_ARGS("GssapiDelegCcacheDir", mag_deleg_ccache_dir, NULL,
712                      OR_AUTHCFG, "Directory to store delegated credentials"),
713 #endif
714 #ifdef HAVE_GSS_ACQUIRE_CRED_WITH_PASSWORD
715     AP_INIT_TAKE1("GssapiBasicAuth", mag_use_basic_auth, NULL, OR_AUTHCFG,
716                      "Allows use of Basic Auth for authentication"),
717 #endif
718     { NULL }
719 };
720
721 static void
722 mag_register_hooks(apr_pool_t *p)
723 {
724     ap_hook_check_user_id(mag_auth, NULL, NULL, APR_HOOK_MIDDLE);
725     ap_hook_post_config(mag_post_config, NULL, NULL, APR_HOOK_MIDDLE);
726     ap_hook_pre_connection(mag_pre_connection, NULL, NULL, APR_HOOK_MIDDLE);
727 }
728
729 module AP_MODULE_DECLARE_DATA auth_gssapi_module =
730 {
731     STANDARD20_MODULE_STUFF,
732     mag_create_dir_config,
733     NULL,
734     NULL,
735     NULL,
736     mag_commands,
737     mag_register_hooks
738 };