Use aes-256-gcm rather than aes-128-cbc
[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 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
187     const char *user_ccache = NULL;
188     const char *orig_ccache = NULL;
189 #endif
190
191     type = ap_auth_type(req);
192     if ((type == NULL) || (strcasecmp(type, "GSSAPI") != 0)) {
193         return DECLINED;
194     }
195
196     /* ignore auth for subrequests */
197     if (!ap_is_initial_req(req)) {
198         return OK;
199     }
200
201     cfg = ap_get_module_config(req->per_dir_config, &auth_gssapi_module);
202
203     if (cfg->ssl_only) {
204         if (!mag_conn_is_https(req->connection)) {
205             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
206                           "Not a TLS connection, refusing to authenticate!");
207             goto done;
208         }
209     }
210
211     if (cfg->gss_conn_ctx) {
212         mc = (struct mag_conn *)ap_get_module_config(
213                                                 req->connection->conn_config,
214                                                 &auth_gssapi_module);
215         if (!mc) {
216             ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, req,
217                           "Failed to retrieve connection context!");
218             goto done;
219         }
220     }
221
222     /* if available, session always supersedes connection bound data */
223     if (cfg->use_sessions) {
224         mag_check_session(req, cfg, &mc);
225     }
226
227     if (mc) {
228         /* register the context in the memory pool, so it can be freed
229          * when the connection/request is terminated */
230         apr_pool_userdata_set(mc, "mag_conn_ptr",
231                               mag_conn_destroy, mc->parent);
232
233         if (mc->established) {
234             ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, req,
235                           "Already established context found!");
236             apr_table_set(req->subprocess_env, "GSS_NAME", mc->gss_name);
237             req->ap_auth_type = apr_pstrdup(req->pool, mc->auth_type);
238             req->user = apr_pstrdup(req->pool, mc->user_name);
239             ret = OK;
240             goto done;
241         }
242         pctx = &mc->ctx;
243     } else {
244         pctx = &ctx;
245     }
246
247     auth_header = apr_table_get(req->headers_in, "Authorization");
248     if (!auth_header) goto done;
249
250     auth_header_type = ap_getword_white(req->pool, &auth_header);
251     if (!auth_header_type) goto done;
252
253     if (strcasecmp(auth_header_type, "Negotiate") == 0) {
254         auth_type = "Negotiate";
255
256         auth_header_value = ap_getword_white(req->pool, &auth_header);
257         if (!auth_header_value) goto done;
258         input.length = apr_base64_decode_len(auth_header_value) + 1;
259         input.value = apr_pcalloc(req->pool, input.length);
260         if (!input.value) goto done;
261         input.length = apr_base64_decode(input.value, auth_header_value);
262     } else if ((strcasecmp(auth_header_type, "Basic") == 0) &&
263                (cfg->use_basic_auth == true)) {
264         auth_type = "Basic";
265         is_basic = true;
266
267         gss_buffer_desc ba_user;
268         gss_buffer_desc ba_pwd;
269
270         ba_pwd.value = ap_pbase64decode(req->pool, auth_header);
271         if (!ba_pwd.value) goto done;
272         ba_user.value = ap_getword_nulls_nc(req->pool,
273                                             (char **)&ba_pwd.value, ':');
274         if (!ba_user.value) goto done;
275         if (((char *)ba_user.value)[0] == '\0' ||
276             ((char *)ba_pwd.value)[0] == '\0') {
277             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
278                           "Invalid empty user or password for Basic Auth");
279             goto done;
280         }
281         ba_user.length = strlen(ba_user.value);
282         ba_pwd.length = strlen(ba_pwd.value);
283         maj = gss_import_name(&min, &ba_user, GSS_C_NT_USER_NAME, &client);
284         if (GSS_ERROR(maj)) {
285             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
286                           "In Basic Auth, %s",
287                           mag_error(req, "gss_import_name() failed",
288                                     maj, min));
289             goto done;
290         }
291 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
292         /* Set a per-thread ccache in case we are using kerberos,
293          * it is not elegant but avoids interference between threads */
294         long long unsigned int rndname;
295         apr_status_t rs;
296         rs = apr_generate_random_bytes((unsigned char *)(&rndname),
297                                        sizeof(long long unsigned int));
298         if (rs != APR_SUCCESS) {
299             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
300                           "Failed to generate random ccache name");
301             goto done;
302         }
303         user_ccache = apr_psprintf(req->pool, "MEMORY:user_%qu", rndname);
304         maj = gss_krb5_ccache_name(&min, user_ccache, &orig_ccache);
305         if (GSS_ERROR(maj)) {
306             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
307                           "In Basic Auth, %s",
308                           mag_error(req, "gss_krb5_ccache_name() "
309                                     "failed", maj, min));
310             goto done;
311         }
312 #endif
313         maj = gss_acquire_cred_with_password(&min, client, &ba_pwd,
314                                              GSS_C_INDEFINITE,
315                                              GSS_C_NO_OID_SET,
316                                              GSS_C_INITIATE,
317                                              &user_cred, NULL, NULL);
318         if (GSS_ERROR(maj)) {
319             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
320                           "In Basic Auth, %s",
321                           mag_error(req, "gss_acquire_cred_with_password() "
322                                     "failed", maj, min));
323             goto done;
324         }
325         gss_release_name(&min, &client);
326     } else {
327         goto done;
328     }
329
330     req->ap_auth_type = apr_pstrdup(req->pool, auth_type);
331
332 #ifdef HAVE_GSS_ACQUIRE_CRED_FROM
333     if (cfg->use_s4u2proxy) {
334         cred_usage = GSS_C_BOTH;
335     }
336     if (cfg->cred_store) {
337         maj = gss_acquire_cred_from(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
338                                     GSS_C_NO_OID_SET, cred_usage,
339                                     cfg->cred_store, &acquired_cred,
340                                     NULL, NULL);
341         if (GSS_ERROR(maj)) {
342             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
343                           mag_error(req, "gss_acquire_cred_from() failed",
344                                     maj, min));
345             goto done;
346         }
347     }
348 #endif
349
350     if (is_basic) {
351         if (!acquired_cred) {
352             /* Try to acquire default creds */
353             maj = gss_acquire_cred(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
354                                    GSS_C_NO_OID_SET, cred_usage,
355                                    &acquired_cred, NULL, NULL);
356             if (GSS_ERROR(maj)) {
357                 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
358                               "%s", mag_error(req, "gss_acquire_cred_from()"
359                                               " failed", maj, min));
360                 goto done;
361             }
362         }
363         maj = gss_inquire_cred(&min, acquired_cred, &server,
364                                NULL, NULL, NULL);
365         if (GSS_ERROR(maj)) {
366             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
367                           "%s", mag_error(req, "gss_inquired_cred_() "
368                                           "failed", maj, min));
369             goto done;
370         }
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     }
383
384     maj = gss_accept_sec_context(&min, pctx, acquired_cred,
385                                  &input, GSS_C_NO_CHANNEL_BINDINGS,
386                                  &client, &mech_type, &output, &flags, &vtime,
387                                  &delegated_cred);
388     if (GSS_ERROR(maj)) {
389         ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
390                       mag_error(req, "gss_accept_sec_context() failed",
391                                 maj, min));
392         goto done;
393     }
394     if (is_basic) {
395         while (maj == GSS_S_CONTINUE_NEEDED) {
396             gss_release_buffer(&min, &input);
397             /* output and input are inverted here, this is intentional */
398             maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
399                                        GSS_C_NO_OID, 0, 300,
400                                        GSS_C_NO_CHANNEL_BINDINGS, &output,
401                                        NULL, &input, NULL, NULL);
402             if (GSS_ERROR(maj)) {
403                 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
404                               "%s", mag_error(req, "gss_init_sec_context() "
405                                               "failed", maj, min));
406                 goto done;
407             }
408             gss_release_buffer(&min, &output);
409             maj = gss_accept_sec_context(&min, pctx, acquired_cred,
410                                          &input, GSS_C_NO_CHANNEL_BINDINGS,
411                                          &client, &mech_type, &output, &flags,
412                                          &vtime, &delegated_cred);
413             if (GSS_ERROR(maj)) {
414                 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
415                               "%s", mag_error(req, "gss_accept_sec_context()"
416                                               " failed", maj, min));
417                 goto done;
418             }
419         }
420     } else if (maj == GSS_S_CONTINUE_NEEDED) {
421         if (!mc) {
422             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
423                           "Mechanism needs continuation but neither "
424                           "GssapiConnectionBound nor "
425                           "GssapiUseSessions are available");
426             gss_delete_sec_context(&min, pctx, GSS_C_NO_BUFFER);
427             gss_release_buffer(&min, &output);
428             output.length = 0;
429         }
430         /* auth not complete send token and wait next packet */
431         goto done;
432     }
433
434     /* Always set the GSS name in an env var */
435     maj = gss_display_name(&min, client, &name, NULL);
436     if (GSS_ERROR(maj)) {
437         ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
438                       mag_error(req, "gss_display_name() failed",
439                                 maj, min));
440         goto done;
441     }
442     clientname = apr_pstrndup(req->pool, name.value, name.length);
443     apr_table_set(req->subprocess_env, "GSS_NAME", clientname);
444
445 #ifdef HAVE_GSS_STORE_CRED_INTO
446     if (cfg->deleg_ccache_dir && delegated_cred != GSS_C_NO_CREDENTIAL) {
447         char *ccachefile = NULL;
448
449         mag_store_deleg_creds(req, cfg->deleg_ccache_dir, clientname,
450                               delegated_cred, &ccachefile);
451
452         if (ccachefile) {
453             apr_table_set(req->subprocess_env, "KRB5CCNAME", ccachefile);
454         }
455     }
456 #endif
457
458     if (cfg->map_to_local) {
459         maj = gss_localname(&min, client, mech_type, &lname);
460         if (maj != GSS_S_COMPLETE) {
461             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
462                           mag_error(req, "gss_localname() failed", maj, min));
463             goto done;
464         }
465         req->user = apr_pstrndup(req->pool, lname.value, lname.length);
466     } else {
467         req->user = clientname;
468     }
469
470     if (mc) {
471         mc->user_name = apr_pstrdup(mc->parent, req->user);
472         mc->gss_name = apr_pstrdup(mc->parent, clientname);
473         mc->established = true;
474         if (vtime == GSS_C_INDEFINITE || vtime < MIN_SESS_EXP_TIME) {
475             vtime = MIN_SESS_EXP_TIME;
476         }
477         mc->expiration = time(NULL) + vtime;
478         if (cfg->use_sessions) {
479             mag_attempt_session(req, cfg, mc);
480         }
481         mc->auth_type = auth_type;
482     }
483
484     ret = OK;
485
486 done:
487     if (ret == HTTP_UNAUTHORIZED) {
488         if (output.length != 0) {
489             replen = apr_base64_encode_len(output.length) + 1;
490             reply = apr_pcalloc(req->pool, 10 + replen);
491             if (reply) {
492                 memcpy(reply, "Negotiate ", 10);
493                 apr_base64_encode(&reply[10], output.value, output.length);
494                 apr_table_add(req->err_headers_out,
495                               "WWW-Authenticate", reply);
496             }
497         } else {
498             apr_table_add(req->err_headers_out,
499                           "WWW-Authenticate", "Negotiate");
500             if (cfg->use_basic_auth) {
501                 apr_table_add(req->err_headers_out,
502                               "WWW-Authenticate",
503                               apr_psprintf(req->pool, "Basic realm=\"%s\"",
504                                            ap_auth_name(req)));
505             }
506         }
507     }
508 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
509     if (user_ccache != NULL) {
510         maj = gss_krb5_ccache_name(&min, orig_ccache, NULL);
511         if (maj != GSS_S_COMPLETE) {
512             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
513                           "Failed to restore per-thread ccache, %s",
514                           mag_error(req, "gss_krb5_ccache_name() "
515                                     "failed", maj, min));
516         }
517     }
518 #endif
519     gss_delete_sec_context(&min, &user_ctx, &output);
520     gss_release_cred(&min, &user_cred);
521     gss_release_cred(&min, &acquired_cred);
522     gss_release_cred(&min, &delegated_cred);
523     gss_release_buffer(&min, &output);
524     gss_release_name(&min, &client);
525     gss_release_name(&min, &server);
526     gss_release_buffer(&min, &name);
527     gss_release_buffer(&min, &lname);
528     return ret;
529 }
530
531
532 static void *mag_create_dir_config(apr_pool_t *p, char *dir)
533 {
534     struct mag_config *cfg;
535
536     cfg = (struct mag_config *)apr_pcalloc(p, sizeof(struct mag_config));
537     if (!cfg) return NULL;
538     cfg->pool = p;
539
540     return cfg;
541 }
542
543 static const char *mag_ssl_only(cmd_parms *parms, void *mconfig, int on)
544 {
545     struct mag_config *cfg = (struct mag_config *)mconfig;
546     cfg->ssl_only = on ? true : false;
547     return NULL;
548 }
549
550 static const char *mag_map_to_local(cmd_parms *parms, void *mconfig, int on)
551 {
552     struct mag_config *cfg = (struct mag_config *)mconfig;
553     cfg->map_to_local = on ? true : false;
554     return NULL;
555 }
556
557 static const char *mag_conn_ctx(cmd_parms *parms, void *mconfig, int on)
558 {
559     struct mag_config *cfg = (struct mag_config *)mconfig;
560     cfg->gss_conn_ctx = on ? true : false;
561     return NULL;
562 }
563
564 static const char *mag_use_sess(cmd_parms *parms, void *mconfig, int on)
565 {
566     struct mag_config *cfg = (struct mag_config *)mconfig;
567     cfg->use_sessions = on ? true : false;
568     return NULL;
569 }
570
571 static const char *mag_use_s4u2p(cmd_parms *parms, void *mconfig, int on)
572 {
573     struct mag_config *cfg = (struct mag_config *)mconfig;
574     cfg->use_s4u2proxy = on ? true : false;
575
576     if (cfg->deleg_ccache_dir == NULL) {
577         cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, "/tmp");
578         if (!cfg->deleg_ccache_dir) {
579             ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0,
580                          parms->server, "%s", "OOM setting deleg_ccache_dir.");
581         }
582     }
583     return NULL;
584 }
585
586 static const char *mag_sess_key(cmd_parms *parms, void *mconfig, const char *w)
587 {
588     struct mag_config *cfg = (struct mag_config *)mconfig;
589     struct databuf key;
590     unsigned char *val;
591     apr_status_t rc;
592     const char *k;
593     int l;
594
595     if (strncmp(w, "key:", 4) != 0) {
596         ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
597                      "Invalid key format, expected prefix 'key:'");
598         return NULL;
599     }
600     k = w + 4;
601
602     l = apr_base64_decode_len(k);
603     val = apr_palloc(parms->temp_pool, l);
604     if (!val) {
605         ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
606                      "Failed to get memory to decode key");
607         return NULL;
608     }
609
610     key.length = (int)apr_base64_decode_binary(val, k);
611     key.value = (unsigned char *)val;
612
613     if (key.length < 32) {
614         ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
615                      "Invalid key length, expected >=32 got %d", key.length);
616         return NULL;
617     }
618
619     rc = SEAL_KEY_CREATE(cfg->pool, &cfg->mag_skey, &key);
620     if (rc != OK) {
621         ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
622                      "Failed to import sealing key!");
623     }
624     return NULL;
625 }
626
627 #define MAX_CRED_OPTIONS 10
628
629 static const char *mag_cred_store(cmd_parms *parms, void *mconfig,
630                                   const char *w)
631 {
632     struct mag_config *cfg = (struct mag_config *)mconfig;
633     gss_key_value_element_desc *elements;
634     uint32_t count;
635     size_t size;
636     const char *p;
637     char *value;
638     char *key;
639
640     p = strchr(w, ':');
641     if (!p) {
642         ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
643                      "%s [%s]", "Invalid syntax for GssapiCredStore option", w);
644         return NULL;
645     }
646
647     key = apr_pstrndup(parms->pool, w, (p-w));
648     value = apr_pstrdup(parms->pool, p + 1);
649     if (!key || !value) {
650         ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
651                      "%s", "OOM handling GssapiCredStore option");
652         return NULL;
653     }
654
655     if (!cfg->cred_store) {
656         cfg->cred_store = apr_pcalloc(parms->pool,
657                                       sizeof(gss_key_value_set_desc));
658         if (!cfg->cred_store) {
659             ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
660                          "%s", "OOM handling GssapiCredStore option");
661             return NULL;
662         }
663         size = sizeof(gss_key_value_element_desc) * MAX_CRED_OPTIONS;
664         cfg->cred_store->elements = apr_palloc(parms->pool, size);
665         if (!cfg->cred_store->elements) {
666             ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
667                          "%s", "OOM handling GssapiCredStore option");
668         }
669     }
670
671     elements = cfg->cred_store->elements;
672     count = cfg->cred_store->count;
673
674     if (count >= MAX_CRED_OPTIONS) {
675         ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
676                      "Too many GssapiCredStore options (MAX: %d)",
677                      MAX_CRED_OPTIONS);
678         return NULL;
679     }
680     cfg->cred_store->count++;
681
682     elements[count].key = key;
683     elements[count].value = value;
684
685     return NULL;
686 }
687
688 static const char *mag_deleg_ccache_dir(cmd_parms *parms, void *mconfig,
689                                         const char *value)
690 {
691     struct mag_config *cfg = (struct mag_config *)mconfig;
692
693     cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, value);
694     if (!cfg->deleg_ccache_dir) {
695         ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
696                      "%s", "OOM handling GssapiDelegCcacheDir option");
697     }
698
699     return NULL;
700 }
701
702 static const char *mag_use_basic_auth(cmd_parms *parms, void *mconfig, int on)
703 {
704     struct mag_config *cfg = (struct mag_config *)mconfig;
705
706     cfg->use_basic_auth = on ? true : false;
707     return NULL;
708 }
709
710 static const command_rec mag_commands[] = {
711     AP_INIT_FLAG("GssapiSSLonly", mag_ssl_only, NULL, OR_AUTHCFG,
712                   "Work only if connection is SSL Secured"),
713     AP_INIT_FLAG("GssapiLocalName", mag_map_to_local, NULL, OR_AUTHCFG,
714                   "Translate principals to local names"),
715     AP_INIT_FLAG("GssapiConnectionBound", mag_conn_ctx, NULL, OR_AUTHCFG,
716                   "Authentication is bound to the TCP connection"),
717     AP_INIT_FLAG("GssapiUseSessions", mag_use_sess, NULL, OR_AUTHCFG,
718                   "Authentication uses mod_sessions to hold status"),
719     AP_INIT_RAW_ARGS("GssapiSessionKey", mag_sess_key, NULL, OR_AUTHCFG,
720                      "Key Used to seal session data."),
721 #ifdef HAVE_GSS_ACQUIRE_CRED_FROM
722     AP_INIT_FLAG("GssapiUseS4U2Proxy", mag_use_s4u2p, NULL, OR_AUTHCFG,
723                   "Initializes credentials for s4u2proxy usage"),
724 #endif
725 #ifdef HAVE_GSS_STORE_CRED_INTO
726     AP_INIT_ITERATE("GssapiCredStore", mag_cred_store, NULL, OR_AUTHCFG,
727                     "Credential Store"),
728     AP_INIT_RAW_ARGS("GssapiDelegCcacheDir", mag_deleg_ccache_dir, NULL,
729                      OR_AUTHCFG, "Directory to store delegated credentials"),
730 #endif
731 #ifdef HAVE_GSS_ACQUIRE_CRED_WITH_PASSWORD
732     AP_INIT_FLAG("GssapiBasicAuth", mag_use_basic_auth, NULL, OR_AUTHCFG,
733                      "Allows use of Basic Auth for authentication"),
734 #endif
735     { NULL }
736 };
737
738 static void
739 mag_register_hooks(apr_pool_t *p)
740 {
741     ap_hook_check_user_id(mag_auth, NULL, NULL, APR_HOOK_MIDDLE);
742     ap_hook_post_config(mag_post_config, NULL, NULL, APR_HOOK_MIDDLE);
743     ap_hook_pre_connection(mag_pre_connection, NULL, NULL, APR_HOOK_MIDDLE);
744 }
745
746 module AP_MODULE_DECLARE_DATA auth_gssapi_module =
747 {
748     STANDARD20_MODULE_STUFF,
749     mag_create_dir_config,
750     NULL,
751     NULL,
752     NULL,
753     mag_commands,
754     mag_register_hooks
755 };