4 Copyright (C) 2014 Simo Sorce <simo@redhat.com>
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:
13 The above copyright notice and this permission notice shall be included in
14 all copies or substantial portions of the Software.
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.
25 #include "mod_auth_gssapi.h"
27 #define MOD_AUTH_GSSAPI_VERSION PACKAGE_NAME "/" PACKAGE_VERSION
29 module AP_MODULE_DECLARE_DATA auth_gssapi_module;
31 APLOG_USE_MODULE(auth_gssapi);
33 static char *mag_status(request_rec *req, int type, uint32_t err)
35 uint32_t maj_ret, min_ret;
44 maj_ret = gss_display_status(&min_ret, err, type,
45 GSS_C_NO_OID, &msg_ctx, &text);
46 if (maj_ret != GSS_S_COMPLETE) {
52 msg_ret = apr_psprintf(req->pool, "%s, %*s",
53 msg_ret, len, (char *)text.value);
55 msg_ret = apr_psprintf(req->pool, "%*s", len, (char *)text.value);
57 gss_release_buffer(&min_ret, &text);
58 } while (msg_ctx != 0);
63 static char *mag_error(request_rec *req, const char *msg,
64 uint32_t maj, uint32_t min)
69 msg_maj = mag_status(req, GSS_C_GSS_CODE, maj);
70 msg_min = mag_status(req, GSS_C_MECH_CODE, min);
71 return apr_psprintf(req->pool, "%s: [%s (%s)]", msg, msg_maj, msg_min);
74 static APR_OPTIONAL_FN_TYPE(ssl_is_https) *mag_is_https = NULL;
76 static int mag_post_config(apr_pool_t *cfgpool, apr_pool_t *log,
77 apr_pool_t *temp, server_rec *s)
79 /* FIXME: create mutex to deal with connections and contexts ? */
80 mag_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
81 mag_post_config_session();
82 ap_add_version_component(cfgpool, MOD_AUTH_GSSAPI_VERSION);
87 static int mag_pre_connection(conn_rec *c, void *csd)
91 mc = apr_pcalloc(c->pool, sizeof(struct mag_conn));
92 if (!mc) return DECLINED;
95 ap_set_module_config(c->conn_config, &auth_gssapi_module, (void*)mc);
99 static apr_status_t mag_conn_destroy(void *ptr)
101 struct mag_conn *mc = (struct mag_conn *)ptr;
105 (void)gss_delete_sec_context(&min, &mc->ctx, GSS_C_NO_BUFFER);
106 mc->established = false;
111 static bool mag_conn_is_https(conn_rec *c)
114 if (mag_is_https(c)) return true;
120 static char *escape(apr_pool_t *pool, const char *name,
121 char find, const char *replace)
123 char *escaped = NULL;
128 namecopy = apr_pstrdup(pool, name);
129 if (!namecopy) goto done;
131 p = strchr(namecopy, find);
132 if (!p) return namecopy;
137 /* terminate previous segment */
140 escaped = apr_pstrcat(pool, escaped, n, replace, NULL);
142 escaped = apr_pstrcat(pool, n, replace, NULL);
144 if (!escaped) goto done;
145 /* move to next segment */
149 /* append last segment if any */
151 escaped = apr_pstrcat(pool, escaped, n, NULL);
156 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, NULL,
157 "OOM escaping name");
162 static void mag_store_deleg_creds(request_rec *req,
163 char *dir, char *clientname,
164 gss_cred_id_t delegated_cred,
167 gss_key_value_element_desc element;
168 gss_key_value_set_desc store;
173 /* We need to escape away '/', we can't have path separators in
174 * a ccache file name */
175 /* first double escape the esacping char (~) if any */
176 escaped = escape(req->pool, clientname, '~', "~~");
177 if (!escaped) return;
178 /* then escape away the separator (/) if any */
179 escaped = escape(req->pool, escaped, '/', "~");
180 if (!escaped) return;
182 value = apr_psprintf(req->pool, "FILE:%s/%s", dir, escaped);
184 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, NULL,
185 "OOM storing delegated credentials");
189 element.key = "ccache";
190 element.value = value;
191 store.elements = &element;
194 maj = gss_store_cred_into(&min, delegated_cred, GSS_C_INITIATE,
195 GSS_C_NULL_OID, 1, 1, &store, NULL, NULL);
196 if (GSS_ERROR(maj)) {
197 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
198 mag_error(req, "failed to store delegated creds",
205 static int mag_auth(request_rec *req)
208 const char *auth_type;
209 struct mag_config *cfg;
210 const char *auth_header;
211 char *auth_header_type;
212 char *auth_header_value;
213 int ret = HTTP_UNAUTHORIZED;
214 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
216 gss_buffer_desc input = GSS_C_EMPTY_BUFFER;
217 gss_buffer_desc output = GSS_C_EMPTY_BUFFER;
218 gss_buffer_desc name = GSS_C_EMPTY_BUFFER;
219 gss_name_t client = GSS_C_NO_NAME;
220 gss_cred_id_t user_cred = GSS_C_NO_CREDENTIAL;
221 gss_cred_id_t acquired_cred = GSS_C_NO_CREDENTIAL;
222 gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
223 gss_cred_usage_t cred_usage = GSS_C_ACCEPT;
230 gss_OID mech_type = GSS_C_NO_OID;
231 gss_buffer_desc lname = GSS_C_EMPTY_BUFFER;
232 struct mag_conn *mc = NULL;
233 bool is_basic = false;
234 gss_ctx_id_t user_ctx = GSS_C_NO_CONTEXT;
235 gss_name_t server = GSS_C_NO_NAME;
236 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
237 const char *user_ccache = NULL;
238 const char *orig_ccache = NULL;
240 uint32_t init_flags = 0;
243 type = ap_auth_type(req);
244 if ((type == NULL) || (strcasecmp(type, "GSSAPI") != 0)) {
248 cfg = ap_get_module_config(req->per_dir_config, &auth_gssapi_module);
250 /* implicit auth for subrequests if main auth already happened */
251 if (!ap_is_initial_req(req)) {
252 type = ap_auth_type(req->main);
253 if ((type != NULL) && (strcasecmp(type, "GSSAPI") == 0)) {
254 /* warn if the subrequest location and the main request
255 * location have different configs */
256 if (cfg != ap_get_module_config(req->main->per_dir_config,
257 &auth_gssapi_module)) {
258 ap_log_rerror(APLOG_MARK, APLOG_WARNING||APLOG_NOERRNO, 0,
259 req, "Subrequest authentication bypass on "
260 "location with different configuration!");
262 if (req->main->user) {
263 req->user = apr_pstrdup(req->pool, req->main->user);
266 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
267 "The main request is tasked to establish the "
268 "security context, can't proceed!");
269 return HTTP_UNAUTHORIZED;
272 ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, req,
273 "Subrequest GSSAPI auth with no auth on the main "
274 "request. This operation may fail if other "
275 "subrequests already established a context or the "
276 "mechanism requires multiple roundtrips.");
281 if (!mag_conn_is_https(req->connection)) {
282 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
283 "Not a TLS connection, refusing to authenticate!");
288 if (cfg->gss_conn_ctx) {
289 mc = (struct mag_conn *)ap_get_module_config(
290 req->connection->conn_config,
291 &auth_gssapi_module);
293 ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, req,
294 "Failed to retrieve connection context!");
299 /* if available, session always supersedes connection bound data */
300 if (cfg->use_sessions) {
301 mag_check_session(req, cfg, &mc);
305 /* register the context in the memory pool, so it can be freed
306 * when the connection/request is terminated */
307 apr_pool_userdata_set(mc, "mag_conn_ptr",
308 mag_conn_destroy, mc->parent);
310 if (mc->established) {
311 ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, req,
312 "Already established context found!");
313 apr_table_set(req->subprocess_env, "GSS_NAME", mc->gss_name);
314 apr_table_set(req->subprocess_env, "GSS_SESSION_EXPIRATION",
315 apr_psprintf(req->pool,
316 "%ld", (long)mc->expiration));
317 req->ap_auth_type = apr_pstrdup(req->pool, mc->auth_type);
318 req->user = apr_pstrdup(req->pool, mc->user_name);
327 auth_header = apr_table_get(req->headers_in, "Authorization");
328 if (!auth_header) goto done;
330 auth_header_type = ap_getword_white(req->pool, &auth_header);
331 if (!auth_header_type) goto done;
333 if (strcasecmp(auth_header_type, "Negotiate") == 0) {
334 auth_type = "Negotiate";
336 auth_header_value = ap_getword_white(req->pool, &auth_header);
337 if (!auth_header_value) goto done;
338 input.length = apr_base64_decode_len(auth_header_value) + 1;
339 input.value = apr_pcalloc(req->pool, input.length);
340 if (!input.value) goto done;
341 input.length = apr_base64_decode(input.value, auth_header_value);
342 } else if ((strcasecmp(auth_header_type, "Basic") == 0) &&
343 (cfg->use_basic_auth == true)) {
347 gss_buffer_desc ba_user;
348 gss_buffer_desc ba_pwd;
350 ba_pwd.value = ap_pbase64decode(req->pool, auth_header);
351 if (!ba_pwd.value) goto done;
352 ba_user.value = ap_getword_nulls_nc(req->pool,
353 (char **)&ba_pwd.value, ':');
354 if (!ba_user.value) goto done;
355 if (((char *)ba_user.value)[0] == '\0' ||
356 ((char *)ba_pwd.value)[0] == '\0') {
357 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
358 "Invalid empty user or password for Basic Auth");
361 ba_user.length = strlen(ba_user.value);
362 ba_pwd.length = strlen(ba_pwd.value);
363 maj = gss_import_name(&min, &ba_user, GSS_C_NT_USER_NAME, &client);
364 if (GSS_ERROR(maj)) {
365 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
367 mag_error(req, "gss_import_name() failed",
371 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
372 /* Set a per-thread ccache in case we are using kerberos,
373 * it is not elegant but avoids interference between threads */
374 long long unsigned int rndname;
376 rs = apr_generate_random_bytes((unsigned char *)(&rndname),
377 sizeof(long long unsigned int));
378 if (rs != APR_SUCCESS) {
379 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
380 "Failed to generate random ccache name");
383 user_ccache = apr_psprintf(req->pool, "MEMORY:user_%qu", rndname);
384 maj = gss_krb5_ccache_name(&min, user_ccache, &orig_ccache);
385 if (GSS_ERROR(maj)) {
386 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
388 mag_error(req, "gss_krb5_ccache_name() "
389 "failed", maj, min));
393 maj = gss_acquire_cred_with_password(&min, client, &ba_pwd,
397 &user_cred, NULL, NULL);
398 if (GSS_ERROR(maj)) {
399 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
401 mag_error(req, "gss_acquire_cred_with_password() "
402 "failed", maj, min));
405 gss_release_name(&min, &client);
410 req->ap_auth_type = apr_pstrdup(req->pool, auth_type);
412 #ifdef HAVE_GSS_ACQUIRE_CRED_FROM
413 if (cfg->use_s4u2proxy) {
414 cred_usage = GSS_C_BOTH;
416 if (cfg->cred_store) {
417 maj = gss_acquire_cred_from(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
418 GSS_C_NO_OID_SET, cred_usage,
419 cfg->cred_store, &acquired_cred,
421 if (GSS_ERROR(maj)) {
422 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
423 mag_error(req, "gss_acquire_cred_from() failed",
431 if (!acquired_cred) {
432 /* Try to acquire default creds */
433 maj = gss_acquire_cred(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
434 GSS_C_NO_OID_SET, cred_usage,
435 &acquired_cred, NULL, NULL);
436 if (GSS_ERROR(maj)) {
437 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
438 "%s", mag_error(req, "gss_acquire_cred_from()"
439 " failed", maj, min));
443 maj = gss_inquire_cred(&min, acquired_cred, &server,
445 if (GSS_ERROR(maj)) {
446 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
447 "%s", mag_error(req, "gss_inquired_cred_() "
448 "failed", maj, min));
452 if (cfg->deleg_ccache_dir) {
453 /* delegate ourselves credentials so we store them as requested */
454 init_flags |= GSS_C_DELEG_FLAG;
457 /* output and input are inverted here, this is intentional */
458 maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
459 GSS_C_NO_OID, init_flags, 300,
460 GSS_C_NO_CHANNEL_BINDINGS, &output,
461 NULL, &input, NULL, NULL);
462 if (GSS_ERROR(maj)) {
463 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
464 "%s", mag_error(req, "gss_init_sec_context() "
465 "failed", maj, min));
470 maj = gss_accept_sec_context(&min, pctx, acquired_cred,
471 &input, GSS_C_NO_CHANNEL_BINDINGS,
472 &client, &mech_type, &output, &flags, &vtime,
474 if (GSS_ERROR(maj)) {
475 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
476 mag_error(req, "gss_accept_sec_context() failed",
481 while (maj == GSS_S_CONTINUE_NEEDED) {
482 gss_release_buffer(&min, &input);
483 /* output and input are inverted here, this is intentional */
484 maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
485 GSS_C_NO_OID, init_flags, 300,
486 GSS_C_NO_CHANNEL_BINDINGS, &output,
487 NULL, &input, NULL, NULL);
488 if (GSS_ERROR(maj)) {
489 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
490 "%s", mag_error(req, "gss_init_sec_context() "
491 "failed", maj, min));
494 gss_release_buffer(&min, &output);
495 maj = gss_accept_sec_context(&min, pctx, acquired_cred,
496 &input, GSS_C_NO_CHANNEL_BINDINGS,
497 &client, &mech_type, &output, &flags,
498 &vtime, &delegated_cred);
499 if (GSS_ERROR(maj)) {
500 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
501 "%s", mag_error(req, "gss_accept_sec_context()"
502 " failed", maj, min));
506 } else if (maj == GSS_S_CONTINUE_NEEDED) {
508 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
509 "Mechanism needs continuation but neither "
510 "GssapiConnectionBound nor "
511 "GssapiUseSessions are available");
512 gss_delete_sec_context(&min, pctx, GSS_C_NO_BUFFER);
513 gss_release_buffer(&min, &output);
516 /* auth not complete send token and wait next packet */
520 /* Always set the GSS name in an env var */
521 maj = gss_display_name(&min, client, &name, NULL);
522 if (GSS_ERROR(maj)) {
523 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
524 mag_error(req, "gss_display_name() failed",
528 clientname = apr_pstrndup(req->pool, name.value, name.length);
529 apr_table_set(req->subprocess_env, "GSS_NAME", clientname);
530 expiration = time(NULL) + vtime;
531 apr_table_set(req->subprocess_env, "GSS_SESSION_EXPIRATION",
532 apr_psprintf(req->pool, "%ld", (long)expiration));
534 #ifdef HAVE_GSS_STORE_CRED_INTO
535 if (cfg->deleg_ccache_dir && delegated_cred != GSS_C_NO_CREDENTIAL) {
536 char *ccachefile = NULL;
538 mag_store_deleg_creds(req, cfg->deleg_ccache_dir, clientname,
539 delegated_cred, &ccachefile);
542 apr_table_set(req->subprocess_env, "KRB5CCNAME", ccachefile);
547 if (cfg->map_to_local) {
548 maj = gss_localname(&min, client, mech_type, &lname);
549 if (maj != GSS_S_COMPLETE) {
550 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
551 mag_error(req, "gss_localname() failed", maj, min));
554 req->user = apr_pstrndup(req->pool, lname.value, lname.length);
556 req->user = clientname;
560 mc->user_name = apr_pstrdup(mc->parent, req->user);
561 mc->gss_name = apr_pstrdup(mc->parent, clientname);
562 mc->established = true;
563 if (vtime == GSS_C_INDEFINITE || vtime < MIN_SESS_EXP_TIME) {
564 vtime = MIN_SESS_EXP_TIME;
566 mc->expiration = expiration;
567 if (cfg->use_sessions) {
568 mag_attempt_session(req, cfg, mc);
570 mc->auth_type = auth_type;
573 if (cfg->send_persist)
574 apr_table_set(req->headers_out, "Persistent-Auth",
575 cfg->gss_conn_ctx ? "true" : "false");
580 if ((!is_basic) && (output.length != 0)) {
581 replen = apr_base64_encode_len(output.length) + 1;
582 reply = apr_pcalloc(req->pool, 10 + replen);
584 memcpy(reply, "Negotiate ", 10);
585 apr_base64_encode(&reply[10], output.value, output.length);
586 apr_table_add(req->err_headers_out,
587 "WWW-Authenticate", reply);
589 } else if (ret == HTTP_UNAUTHORIZED) {
590 apr_table_add(req->err_headers_out,
591 "WWW-Authenticate", "Negotiate");
592 if (cfg->use_basic_auth) {
593 apr_table_add(req->err_headers_out,
595 apr_psprintf(req->pool, "Basic realm=\"%s\"",
599 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
600 if (user_ccache != NULL) {
601 maj = gss_krb5_ccache_name(&min, orig_ccache, NULL);
602 if (maj != GSS_S_COMPLETE) {
603 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
604 "Failed to restore per-thread ccache, %s",
605 mag_error(req, "gss_krb5_ccache_name() "
606 "failed", maj, min));
610 gss_delete_sec_context(&min, &user_ctx, &output);
611 gss_release_cred(&min, &user_cred);
612 gss_release_cred(&min, &acquired_cred);
613 gss_release_cred(&min, &delegated_cred);
614 gss_release_buffer(&min, &output);
615 gss_release_name(&min, &client);
616 gss_release_name(&min, &server);
617 gss_release_buffer(&min, &name);
618 gss_release_buffer(&min, &lname);
623 static void *mag_create_dir_config(apr_pool_t *p, char *dir)
625 struct mag_config *cfg;
627 cfg = (struct mag_config *)apr_pcalloc(p, sizeof(struct mag_config));
628 if (!cfg) return NULL;
634 static const char *mag_ssl_only(cmd_parms *parms, void *mconfig, int on)
636 struct mag_config *cfg = (struct mag_config *)mconfig;
637 cfg->ssl_only = on ? true : false;
641 static const char *mag_map_to_local(cmd_parms *parms, void *mconfig, int on)
643 struct mag_config *cfg = (struct mag_config *)mconfig;
644 cfg->map_to_local = on ? true : false;
648 static const char *mag_conn_ctx(cmd_parms *parms, void *mconfig, int on)
650 struct mag_config *cfg = (struct mag_config *)mconfig;
651 cfg->gss_conn_ctx = on ? true : false;
655 static const char *mag_send_persist(cmd_parms *parms, void *mconfig, int on)
657 struct mag_config *cfg = (struct mag_config *)mconfig;
658 cfg->send_persist = on ? true : false;
662 static const char *mag_use_sess(cmd_parms *parms, void *mconfig, int on)
664 struct mag_config *cfg = (struct mag_config *)mconfig;
665 cfg->use_sessions = on ? true : false;
669 static const char *mag_use_s4u2p(cmd_parms *parms, void *mconfig, int on)
671 struct mag_config *cfg = (struct mag_config *)mconfig;
672 cfg->use_s4u2proxy = on ? true : false;
674 if (cfg->deleg_ccache_dir == NULL) {
675 cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, "/tmp");
676 if (!cfg->deleg_ccache_dir) {
677 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0,
678 parms->server, "%s", "OOM setting deleg_ccache_dir.");
684 static const char *mag_sess_key(cmd_parms *parms, void *mconfig, const char *w)
686 struct mag_config *cfg = (struct mag_config *)mconfig;
693 if (strncmp(w, "key:", 4) != 0) {
694 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
695 "Invalid key format, expected prefix 'key:'");
700 l = apr_base64_decode_len(k);
701 val = apr_palloc(parms->temp_pool, l);
703 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
704 "Failed to get memory to decode key");
708 keys.length = (int)apr_base64_decode_binary(val, k);
709 keys.value = (unsigned char *)val;
711 if (keys.length != 32) {
712 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
713 "Invalid key lenght, expected 32 got %d", keys.length);
717 rc = SEAL_KEY_CREATE(cfg->pool, &cfg->mag_skey, &keys);
719 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
720 "Failed to import sealing key!");
725 #define MAX_CRED_OPTIONS 10
727 static const char *mag_cred_store(cmd_parms *parms, void *mconfig,
730 struct mag_config *cfg = (struct mag_config *)mconfig;
731 gss_key_value_element_desc *elements;
740 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
741 "%s [%s]", "Invalid syntax for GssapiCredStore option", w);
745 key = apr_pstrndup(parms->pool, w, (p-w));
746 value = apr_pstrdup(parms->pool, p + 1);
747 if (!key || !value) {
748 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
749 "%s", "OOM handling GssapiCredStore option");
753 if (!cfg->cred_store) {
754 cfg->cred_store = apr_pcalloc(parms->pool,
755 sizeof(gss_key_value_set_desc));
756 if (!cfg->cred_store) {
757 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
758 "%s", "OOM handling GssapiCredStore option");
761 size = sizeof(gss_key_value_element_desc) * MAX_CRED_OPTIONS;
762 cfg->cred_store->elements = apr_palloc(parms->pool, size);
763 if (!cfg->cred_store->elements) {
764 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
765 "%s", "OOM handling GssapiCredStore option");
769 elements = cfg->cred_store->elements;
770 count = cfg->cred_store->count;
772 if (count >= MAX_CRED_OPTIONS) {
773 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
774 "Too many GssapiCredStore options (MAX: %d)",
778 cfg->cred_store->count++;
780 elements[count].key = key;
781 elements[count].value = value;
786 static const char *mag_deleg_ccache_dir(cmd_parms *parms, void *mconfig,
789 struct mag_config *cfg = (struct mag_config *)mconfig;
791 cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, value);
792 if (!cfg->deleg_ccache_dir) {
793 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
794 "%s", "OOM handling GssapiDelegCcacheDir option");
800 static const char *mag_use_basic_auth(cmd_parms *parms, void *mconfig, int on)
802 struct mag_config *cfg = (struct mag_config *)mconfig;
804 cfg->use_basic_auth = on ? true : false;
808 static const command_rec mag_commands[] = {
809 AP_INIT_FLAG("GssapiSSLonly", mag_ssl_only, NULL, OR_AUTHCFG,
810 "Work only if connection is SSL Secured"),
811 AP_INIT_FLAG("GssapiLocalName", mag_map_to_local, NULL, OR_AUTHCFG,
812 "Translate principals to local names"),
813 AP_INIT_FLAG("GssapiConnectionBound", mag_conn_ctx, NULL, OR_AUTHCFG,
814 "Authentication is bound to the TCP connection"),
815 AP_INIT_FLAG("GssapiSignalPersistentAuth", mag_send_persist, NULL, OR_AUTHCFG,
816 "Send Persitent-Auth header according to connection bound"),
817 AP_INIT_FLAG("GssapiUseSessions", mag_use_sess, NULL, OR_AUTHCFG,
818 "Authentication uses mod_sessions to hold status"),
819 AP_INIT_RAW_ARGS("GssapiSessionKey", mag_sess_key, NULL, OR_AUTHCFG,
820 "Key Used to seal session data."),
821 #ifdef HAVE_GSS_ACQUIRE_CRED_FROM
822 AP_INIT_FLAG("GssapiUseS4U2Proxy", mag_use_s4u2p, NULL, OR_AUTHCFG,
823 "Initializes credentials for s4u2proxy usage"),
825 #ifdef HAVE_GSS_STORE_CRED_INTO
826 AP_INIT_ITERATE("GssapiCredStore", mag_cred_store, NULL, OR_AUTHCFG,
828 AP_INIT_RAW_ARGS("GssapiDelegCcacheDir", mag_deleg_ccache_dir, NULL,
829 OR_AUTHCFG, "Directory to store delegated credentials"),
831 #ifdef HAVE_GSS_ACQUIRE_CRED_WITH_PASSWORD
832 AP_INIT_FLAG("GssapiBasicAuth", mag_use_basic_auth, NULL, OR_AUTHCFG,
833 "Allows use of Basic Auth for authentication"),
839 mag_register_hooks(apr_pool_t *p)
841 ap_hook_check_user_id(mag_auth, NULL, NULL, APR_HOOK_MIDDLE);
842 ap_hook_post_config(mag_post_config, NULL, NULL, APR_HOOK_MIDDLE);
843 ap_hook_pre_connection(mag_pre_connection, NULL, NULL, APR_HOOK_MIDDLE);
846 module AP_MODULE_DECLARE_DATA auth_gssapi_module =
848 STANDARD20_MODULE_STUFF,
849 mag_create_dir_config,