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 const gss_OID_desc gss_mech_ntlmssp = {
28 GSS_NTLMSSP_OID_LENGTH, GSS_NTLMSSP_OID_STRING
31 #define MOD_AUTH_GSSAPI_VERSION PACKAGE_NAME "/" PACKAGE_VERSION
33 module AP_MODULE_DECLARE_DATA auth_gssapi_module;
35 APLOG_USE_MODULE(auth_gssapi);
37 static char *mag_status(request_rec *req, int type, uint32_t err)
39 uint32_t maj_ret, min_ret;
48 maj_ret = gss_display_status(&min_ret, err, type,
49 GSS_C_NO_OID, &msg_ctx, &text);
50 if (maj_ret != GSS_S_COMPLETE) {
56 msg_ret = apr_psprintf(req->pool, "%s, %*s",
57 msg_ret, len, (char *)text.value);
59 msg_ret = apr_psprintf(req->pool, "%*s", len, (char *)text.value);
61 gss_release_buffer(&min_ret, &text);
62 } while (msg_ctx != 0);
67 static char *mag_error(request_rec *req, const char *msg,
68 uint32_t maj, uint32_t min)
73 msg_maj = mag_status(req, GSS_C_GSS_CODE, maj);
74 msg_min = mag_status(req, GSS_C_MECH_CODE, min);
75 return apr_psprintf(req->pool, "%s: [%s (%s)]", msg, msg_maj, msg_min);
78 static APR_OPTIONAL_FN_TYPE(ssl_is_https) *mag_is_https = NULL;
80 static int mag_post_config(apr_pool_t *cfgpool, apr_pool_t *log,
81 apr_pool_t *temp, server_rec *s)
83 /* FIXME: create mutex to deal with connections and contexts ? */
84 mag_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
85 mag_post_config_session();
86 ap_add_version_component(cfgpool, MOD_AUTH_GSSAPI_VERSION);
91 static int mag_pre_connection(conn_rec *c, void *csd)
95 mc = apr_pcalloc(c->pool, sizeof(struct mag_conn));
98 ap_set_module_config(c->conn_config, &auth_gssapi_module, (void*)mc);
102 static apr_status_t mag_conn_destroy(void *ptr)
104 struct mag_conn *mc = (struct mag_conn *)ptr;
108 (void)gss_delete_sec_context(&min, &mc->ctx, GSS_C_NO_BUFFER);
109 mc->established = false;
114 static bool mag_conn_is_https(conn_rec *c)
117 if (mag_is_https(c)) return true;
123 static bool mag_acquire_creds(request_rec *req,
124 struct mag_config *cfg,
125 gss_OID_set desired_mechs,
126 gss_cred_usage_t cred_usage,
127 gss_cred_id_t *creds)
130 #ifdef HAVE_CRED_STORE
131 gss_const_key_value_set_t store = cfg->cred_store;
133 maj = gss_acquire_cred_from(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
134 desired_mechs, cred_usage, store, creds,
137 maj = gss_acquire_cred(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
138 desired_mechs, cred_usage, creds, NULL, NULL);
141 if (GSS_ERROR(maj)) {
142 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
143 mag_error(req, "gss_acquire_cred[_from]() "
144 "failed to get server creds",
152 #ifdef HAVE_CRED_STORE
153 static char *escape(apr_pool_t *pool, const char *name,
154 char find, const char *replace)
156 char *escaped = NULL;
161 namecopy = apr_pstrdup(pool, name);
163 p = strchr(namecopy, find);
164 if (!p) return namecopy;
169 /* terminate previous segment */
172 escaped = apr_pstrcat(pool, escaped, n, replace, NULL);
174 escaped = apr_pstrcat(pool, n, replace, NULL);
176 /* move to next segment */
180 /* append last segment if any */
182 escaped = apr_pstrcat(pool, escaped, n, NULL);
188 static void mag_store_deleg_creds(request_rec *req,
189 char *dir, char *clientname,
190 gss_cred_id_t delegated_cred,
193 gss_key_value_element_desc element;
194 gss_key_value_set_desc store;
199 /* We need to escape away '/', we can't have path separators in
200 * a ccache file name */
201 /* first double escape the esacping char (~) if any */
202 escaped = escape(req->pool, clientname, '~', "~~");
203 if (!escaped) return;
204 /* then escape away the separator (/) if any */
205 escaped = escape(req->pool, escaped, '/', "~");
206 if (!escaped) return;
208 value = apr_psprintf(req->pool, "FILE:%s/%s", dir, escaped);
210 element.key = "ccache";
211 element.value = value;
212 store.elements = &element;
215 maj = gss_store_cred_into(&min, delegated_cred, GSS_C_INITIATE,
216 GSS_C_NULL_OID, 1, 1, &store, NULL, NULL);
217 if (GSS_ERROR(maj)) {
218 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
219 mag_error(req, "failed to store delegated creds",
227 static int mag_auth(request_rec *req)
230 const char *auth_type;
231 struct mag_config *cfg;
232 const char *auth_header;
233 char *auth_header_type;
234 char *auth_header_value;
235 int ret = HTTP_UNAUTHORIZED;
236 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
238 gss_buffer_desc input = GSS_C_EMPTY_BUFFER;
239 gss_buffer_desc output = GSS_C_EMPTY_BUFFER;
240 gss_buffer_desc name = GSS_C_EMPTY_BUFFER;
241 gss_name_t client = GSS_C_NO_NAME;
242 gss_cred_id_t user_cred = GSS_C_NO_CREDENTIAL;
243 gss_cred_id_t acquired_cred = GSS_C_NO_CREDENTIAL;
244 gss_cred_id_t server_cred = GSS_C_NO_CREDENTIAL;
245 gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
246 gss_cred_usage_t cred_usage = GSS_C_ACCEPT;
253 gss_OID mech_type = GSS_C_NO_OID;
254 gss_buffer_desc lname = GSS_C_EMPTY_BUFFER;
255 struct mag_conn *mc = NULL;
256 bool is_basic = false;
257 gss_ctx_id_t user_ctx = GSS_C_NO_CONTEXT;
258 gss_name_t server = GSS_C_NO_NAME;
259 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
260 const char *user_ccache = NULL;
261 const char *orig_ccache = NULL;
263 uint32_t init_flags = 0;
266 type = ap_auth_type(req);
267 if ((type == NULL) || (strcasecmp(type, "GSSAPI") != 0)) {
271 cfg = ap_get_module_config(req->per_dir_config, &auth_gssapi_module);
273 /* implicit auth for subrequests if main auth already happened */
274 if (!ap_is_initial_req(req)) {
275 type = ap_auth_type(req->main);
276 if ((type != NULL) && (strcasecmp(type, "GSSAPI") == 0)) {
277 /* warn if the subrequest location and the main request
278 * location have different configs */
279 if (cfg != ap_get_module_config(req->main->per_dir_config,
280 &auth_gssapi_module)) {
281 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0,
282 req, "Subrequest authentication bypass on "
283 "location with different configuration!");
285 if (req->main->user) {
286 req->user = apr_pstrdup(req->pool, req->main->user);
289 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
290 "The main request is tasked to establish the "
291 "security context, can't proceed!");
292 return HTTP_UNAUTHORIZED;
295 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
296 "Subrequest GSSAPI auth with no auth on the main "
297 "request. This operation may fail if other "
298 "subrequests already established a context or the "
299 "mechanism requires multiple roundtrips.");
304 if (!mag_conn_is_https(req->connection)) {
305 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
306 "Not a TLS connection, refusing to authenticate!");
311 if (cfg->gss_conn_ctx) {
312 mc = (struct mag_conn *)ap_get_module_config(
313 req->connection->conn_config,
314 &auth_gssapi_module);
316 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
317 "Failed to retrieve connection context!");
322 /* if available, session always supersedes connection bound data */
323 if (cfg->use_sessions) {
324 mag_check_session(req, cfg, &mc);
328 /* register the context in the memory pool, so it can be freed
329 * when the connection/request is terminated */
330 apr_pool_userdata_set(mc, "mag_conn_ptr",
331 mag_conn_destroy, mc->parent);
333 if (mc->established) {
334 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
335 "Already established context found!");
336 apr_table_set(req->subprocess_env, "GSS_NAME", mc->gss_name);
337 apr_table_set(req->subprocess_env, "GSS_SESSION_EXPIRATION",
338 apr_psprintf(req->pool,
339 "%ld", (long)mc->expiration));
340 req->ap_auth_type = apr_pstrdup(req->pool, mc->auth_type);
341 req->user = apr_pstrdup(req->pool, mc->user_name);
350 auth_header = apr_table_get(req->headers_in, "Authorization");
351 if (!auth_header) goto done;
353 auth_header_type = ap_getword_white(req->pool, &auth_header);
354 if (!auth_header_type) goto done;
356 if (strcasecmp(auth_header_type, "Negotiate") == 0) {
357 auth_type = "Negotiate";
359 auth_header_value = ap_getword_white(req->pool, &auth_header);
360 if (!auth_header_value) goto done;
361 input.length = apr_base64_decode_len(auth_header_value) + 1;
362 input.value = apr_pcalloc(req->pool, input.length);
363 if (!input.value) goto done;
364 input.length = apr_base64_decode(input.value, auth_header_value);
365 } else if ((strcasecmp(auth_header_type, "Basic") == 0) &&
366 (cfg->use_basic_auth == true)) {
370 gss_buffer_desc ba_user;
371 gss_buffer_desc ba_pwd;
373 ba_pwd.value = ap_pbase64decode(req->pool, auth_header);
374 if (!ba_pwd.value) goto done;
375 ba_user.value = ap_getword_nulls_nc(req->pool,
376 (char **)&ba_pwd.value, ':');
377 if (!ba_user.value) goto done;
378 if (((char *)ba_user.value)[0] == '\0' ||
379 ((char *)ba_pwd.value)[0] == '\0') {
380 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
381 "Invalid empty user or password for Basic Auth");
384 ba_user.length = strlen(ba_user.value);
385 ba_pwd.length = strlen(ba_pwd.value);
386 maj = gss_import_name(&min, &ba_user, GSS_C_NT_USER_NAME, &client);
387 if (GSS_ERROR(maj)) {
388 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
390 mag_error(req, "gss_import_name() failed",
394 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
395 /* Set a per-thread ccache in case we are using kerberos,
396 * it is not elegant but avoids interference between threads */
397 long long unsigned int rndname;
399 rs = apr_generate_random_bytes((unsigned char *)(&rndname),
400 sizeof(long long unsigned int));
401 if (rs != APR_SUCCESS) {
402 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
403 "Failed to generate random ccache name");
406 user_ccache = apr_psprintf(req->pool, "MEMORY:user_%qu", rndname);
407 maj = gss_krb5_ccache_name(&min, user_ccache, &orig_ccache);
408 if (GSS_ERROR(maj)) {
409 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
411 mag_error(req, "gss_krb5_ccache_name() "
412 "failed", maj, min));
416 maj = gss_acquire_cred_with_password(&min, client, &ba_pwd,
420 &user_cred, NULL, NULL);
421 if (GSS_ERROR(maj)) {
422 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
424 mag_error(req, "gss_acquire_cred_with_password() "
425 "failed", maj, min));
428 gss_release_name(&min, &client);
433 req->ap_auth_type = apr_pstrdup(req->pool, auth_type);
435 #ifdef HAVE_CRED_STORE
436 if (cfg->use_s4u2proxy) {
437 cred_usage = GSS_C_BOTH;
440 if (!mag_acquire_creds(req, cfg, GSS_C_NO_OID_SET,
441 cred_usage, &acquired_cred)) {
446 if (cred_usage == GSS_C_BOTH) {
447 /* If GSS_C_BOTH is used then inquire_cred will return the client
448 * name instead of the SPN of the server credentials. Therefore we
449 * need to acquire a different set of credential setting
450 * GSS_C_ACCEPT explicitly */
451 if (!mag_acquire_creds(req, cfg, GSS_C_NO_OID_SET,
452 GSS_C_ACCEPT, &server_cred)) {
456 server_cred = acquired_cred;
458 maj = gss_inquire_cred(&min, server_cred, &server,
460 if (GSS_ERROR(maj)) {
461 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
462 "%s", mag_error(req, "gss_inquired_cred_() "
463 "failed", maj, min));
466 if (server_cred != acquired_cred) {
467 gss_release_cred(&min, &server_cred);
470 #ifdef HAVE_CRED_STORE
471 if (cfg->deleg_ccache_dir) {
472 /* delegate ourselves credentials so we store them as requested */
473 init_flags |= GSS_C_DELEG_FLAG;
477 /* output and input are inverted here, this is intentional */
478 maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
479 GSS_C_NO_OID, init_flags, 300,
480 GSS_C_NO_CHANNEL_BINDINGS, &output,
481 NULL, &input, NULL, NULL);
482 if (GSS_ERROR(maj)) {
483 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
484 "%s", mag_error(req, "gss_init_sec_context() "
485 "failed", maj, min));
490 if (!is_basic && cfg->allowed_mechs != GSS_C_NO_OID_SET) {
491 maj = gss_set_neg_mechs(&min, acquired_cred, cfg->allowed_mechs);
492 if (GSS_ERROR(maj)) {
493 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
494 mag_error(req, "gss_set_neg_mechs() failed",
500 maj = gss_accept_sec_context(&min, pctx, acquired_cred,
501 &input, GSS_C_NO_CHANNEL_BINDINGS,
502 &client, &mech_type, &output, &flags, &vtime,
504 if (GSS_ERROR(maj)) {
505 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
506 mag_error(req, "gss_accept_sec_context() failed",
511 while (maj == GSS_S_CONTINUE_NEEDED) {
512 gss_release_buffer(&min, &input);
513 /* output and input are inverted here, this is intentional */
514 maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
515 GSS_C_NO_OID, init_flags, 300,
516 GSS_C_NO_CHANNEL_BINDINGS, &output,
517 NULL, &input, NULL, NULL);
518 if (GSS_ERROR(maj)) {
519 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
520 "%s", mag_error(req, "gss_init_sec_context() "
521 "failed", maj, min));
524 gss_release_buffer(&min, &output);
525 maj = gss_accept_sec_context(&min, pctx, acquired_cred,
526 &input, GSS_C_NO_CHANNEL_BINDINGS,
527 &client, &mech_type, &output, &flags,
528 &vtime, &delegated_cred);
529 if (GSS_ERROR(maj)) {
530 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
531 "%s", mag_error(req, "gss_accept_sec_context()"
532 " failed", maj, min));
536 } else if (maj == GSS_S_CONTINUE_NEEDED) {
538 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
539 "Mechanism needs continuation but neither "
540 "GssapiConnectionBound nor "
541 "GssapiUseSessions are available");
542 gss_delete_sec_context(&min, pctx, GSS_C_NO_BUFFER);
543 gss_release_buffer(&min, &output);
546 /* auth not complete send token and wait next packet */
550 /* Always set the GSS name in an env var */
551 maj = gss_display_name(&min, client, &name, NULL);
552 if (GSS_ERROR(maj)) {
553 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
554 mag_error(req, "gss_display_name() failed",
558 clientname = apr_pstrndup(req->pool, name.value, name.length);
559 apr_table_set(req->subprocess_env, "GSS_NAME", clientname);
560 expiration = time(NULL) + vtime;
561 apr_table_set(req->subprocess_env, "GSS_SESSION_EXPIRATION",
562 apr_psprintf(req->pool, "%ld", (long)expiration));
564 #ifdef HAVE_CRED_STORE
565 if (cfg->deleg_ccache_dir && delegated_cred != GSS_C_NO_CREDENTIAL) {
566 char *ccachefile = NULL;
568 mag_store_deleg_creds(req, cfg->deleg_ccache_dir, clientname,
569 delegated_cred, &ccachefile);
572 apr_table_set(req->subprocess_env, "KRB5CCNAME", ccachefile);
577 if (cfg->map_to_local) {
578 maj = gss_localname(&min, client, mech_type, &lname);
579 if (maj != GSS_S_COMPLETE) {
580 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
581 mag_error(req, "gss_localname() failed", maj, min));
584 req->user = apr_pstrndup(req->pool, lname.value, lname.length);
586 req->user = clientname;
590 mc->user_name = apr_pstrdup(mc->parent, req->user);
591 mc->gss_name = apr_pstrdup(mc->parent, clientname);
592 mc->established = true;
593 if (vtime == GSS_C_INDEFINITE || vtime < MIN_SESS_EXP_TIME) {
594 vtime = MIN_SESS_EXP_TIME;
596 mc->expiration = expiration;
597 if (cfg->use_sessions) {
598 mag_attempt_session(req, cfg, mc);
600 mc->auth_type = auth_type;
603 if (cfg->send_persist)
604 apr_table_set(req->headers_out, "Persistent-Auth",
605 cfg->gss_conn_ctx ? "true" : "false");
610 if ((!is_basic) && (output.length != 0)) {
611 replen = apr_base64_encode_len(output.length) + 1;
612 reply = apr_pcalloc(req->pool, 10 + replen);
614 memcpy(reply, "Negotiate ", 10);
615 apr_base64_encode(&reply[10], output.value, output.length);
616 apr_table_add(req->err_headers_out,
617 "WWW-Authenticate", reply);
619 } else if (ret == HTTP_UNAUTHORIZED) {
620 apr_table_add(req->err_headers_out,
621 "WWW-Authenticate", "Negotiate");
622 if (cfg->use_basic_auth) {
623 apr_table_add(req->err_headers_out,
625 apr_psprintf(req->pool, "Basic realm=\"%s\"",
629 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
630 if (user_ccache != NULL) {
631 maj = gss_krb5_ccache_name(&min, orig_ccache, NULL);
632 if (maj != GSS_S_COMPLETE) {
633 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
634 "Failed to restore per-thread ccache, %s",
635 mag_error(req, "gss_krb5_ccache_name() "
636 "failed", maj, min));
640 gss_delete_sec_context(&min, &user_ctx, &output);
641 gss_release_cred(&min, &user_cred);
642 gss_release_cred(&min, &acquired_cred);
643 gss_release_cred(&min, &delegated_cred);
644 gss_release_buffer(&min, &output);
645 gss_release_name(&min, &client);
646 gss_release_name(&min, &server);
647 gss_release_buffer(&min, &name);
648 gss_release_buffer(&min, &lname);
653 static void *mag_create_dir_config(apr_pool_t *p, char *dir)
655 struct mag_config *cfg;
657 cfg = (struct mag_config *)apr_pcalloc(p, sizeof(struct mag_config));
663 static const char *mag_ssl_only(cmd_parms *parms, void *mconfig, int on)
665 struct mag_config *cfg = (struct mag_config *)mconfig;
666 cfg->ssl_only = on ? true : false;
670 static const char *mag_map_to_local(cmd_parms *parms, void *mconfig, int on)
672 struct mag_config *cfg = (struct mag_config *)mconfig;
673 cfg->map_to_local = on ? true : false;
677 static const char *mag_conn_ctx(cmd_parms *parms, void *mconfig, int on)
679 struct mag_config *cfg = (struct mag_config *)mconfig;
680 cfg->gss_conn_ctx = on ? true : false;
684 static const char *mag_send_persist(cmd_parms *parms, void *mconfig, int on)
686 struct mag_config *cfg = (struct mag_config *)mconfig;
687 cfg->send_persist = on ? true : false;
691 static const char *mag_use_sess(cmd_parms *parms, void *mconfig, int on)
693 struct mag_config *cfg = (struct mag_config *)mconfig;
694 cfg->use_sessions = on ? true : false;
698 #ifdef HAVE_CRED_STORE
699 static const char *mag_use_s4u2p(cmd_parms *parms, void *mconfig, int on)
701 struct mag_config *cfg = (struct mag_config *)mconfig;
702 cfg->use_s4u2proxy = on ? true : false;
704 if (cfg->deleg_ccache_dir == NULL) {
705 cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, "/tmp");
711 static const char *mag_sess_key(cmd_parms *parms, void *mconfig, const char *w)
713 struct mag_config *cfg = (struct mag_config *)mconfig;
720 if (strncmp(w, "key:", 4) != 0) {
721 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
722 "Invalid key format, expected prefix 'key:'");
727 l = apr_base64_decode_len(k);
728 val = apr_palloc(parms->temp_pool, l);
730 keys.length = (int)apr_base64_decode_binary(val, k);
731 keys.value = (unsigned char *)val;
733 if (keys.length != 32) {
734 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
735 "Invalid key length, expected 32 got %d", keys.length);
739 rc = SEAL_KEY_CREATE(cfg->pool, &cfg->mag_skey, &keys);
741 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
742 "Failed to import sealing key!");
747 #ifdef HAVE_CRED_STORE
749 #define MAX_CRED_OPTIONS 10
751 static const char *mag_cred_store(cmd_parms *parms, void *mconfig,
754 struct mag_config *cfg = (struct mag_config *)mconfig;
755 gss_key_value_element_desc *elements;
764 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
765 "%s [%s]", "Invalid syntax for GssapiCredStore option", w);
769 key = apr_pstrndup(parms->pool, w, (p-w));
770 value = apr_pstrdup(parms->pool, p + 1);
772 if (!cfg->cred_store) {
773 cfg->cred_store = apr_pcalloc(parms->pool,
774 sizeof(gss_key_value_set_desc));
775 size = sizeof(gss_key_value_element_desc) * MAX_CRED_OPTIONS;
776 cfg->cred_store->elements = apr_palloc(parms->pool, size);
779 elements = cfg->cred_store->elements;
780 count = cfg->cred_store->count;
782 if (count >= MAX_CRED_OPTIONS) {
783 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
784 "Too many GssapiCredStore options (MAX: %d)",
788 cfg->cred_store->count++;
790 elements[count].key = key;
791 elements[count].value = value;
796 static const char *mag_deleg_ccache_dir(cmd_parms *parms, void *mconfig,
799 struct mag_config *cfg = (struct mag_config *)mconfig;
801 cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, value);
807 static const char *mag_use_basic_auth(cmd_parms *parms, void *mconfig, int on)
809 struct mag_config *cfg = (struct mag_config *)mconfig;
811 cfg->use_basic_auth = on ? true : false;
815 #define MAX_ALLOWED_MECHS 10
817 static const char *mag_allow_mech(cmd_parms *parms, void *mconfig,
820 struct mag_config *cfg = (struct mag_config *)mconfig;
824 if (!cfg->allowed_mechs) {
825 cfg->allowed_mechs = apr_pcalloc(parms->pool,
826 sizeof(gss_OID_set_desc));
827 size = sizeof(gss_OID) * MAX_ALLOWED_MECHS;
828 cfg->allowed_mechs->elements = apr_palloc(parms->pool, size);
831 if (strcmp(w, "krb5") == 0) {
833 } else if (strcmp(w, "iakerb") == 0) {
834 oid = gss_mech_iakerb;
835 } else if (strcmp(w, "ntlmssp") == 0) {
836 oid = &gss_mech_ntlmssp;
838 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
839 "Unrecognized GSSAPI Mechanism: %s", w);
843 if (cfg->allowed_mechs->count >= MAX_ALLOWED_MECHS) {
844 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
845 "Too many GssapiAllowedMech options (MAX: %d)",
849 cfg->allowed_mechs->elements[cfg->allowed_mechs->count] = *oid;
850 cfg->allowed_mechs->count++;
855 static const command_rec mag_commands[] = {
856 AP_INIT_FLAG("GssapiSSLonly", mag_ssl_only, NULL, OR_AUTHCFG,
857 "Work only if connection is SSL Secured"),
858 AP_INIT_FLAG("GssapiLocalName", mag_map_to_local, NULL, OR_AUTHCFG,
859 "Translate principals to local names"),
860 AP_INIT_FLAG("GssapiConnectionBound", mag_conn_ctx, NULL, OR_AUTHCFG,
861 "Authentication is bound to the TCP connection"),
862 AP_INIT_FLAG("GssapiSignalPersistentAuth", mag_send_persist, NULL, OR_AUTHCFG,
863 "Send Persitent-Auth header according to connection bound"),
864 AP_INIT_FLAG("GssapiUseSessions", mag_use_sess, NULL, OR_AUTHCFG,
865 "Authentication uses mod_sessions to hold status"),
866 AP_INIT_RAW_ARGS("GssapiSessionKey", mag_sess_key, NULL, OR_AUTHCFG,
867 "Key Used to seal session data."),
868 #ifdef HAVE_CRED_STORE
869 AP_INIT_FLAG("GssapiUseS4U2Proxy", mag_use_s4u2p, NULL, OR_AUTHCFG,
870 "Initializes credentials for s4u2proxy usage"),
871 AP_INIT_ITERATE("GssapiCredStore", mag_cred_store, NULL, OR_AUTHCFG,
873 AP_INIT_RAW_ARGS("GssapiDelegCcacheDir", mag_deleg_ccache_dir, NULL,
874 OR_AUTHCFG, "Directory to store delegated credentials"),
876 #ifdef HAVE_GSS_ACQUIRE_CRED_WITH_PASSWORD
877 AP_INIT_FLAG("GssapiBasicAuth", mag_use_basic_auth, NULL, OR_AUTHCFG,
878 "Allows use of Basic Auth for authentication"),
880 AP_INIT_ITERATE("GssapiAllowedMech", mag_allow_mech, NULL, OR_AUTHCFG,
881 "Allowed Mechanisms"),
886 mag_register_hooks(apr_pool_t *p)
888 ap_hook_check_user_id(mag_auth, NULL, NULL, APR_HOOK_MIDDLE);
889 ap_hook_post_config(mag_post_config, NULL, NULL, APR_HOOK_MIDDLE);
890 ap_hook_pre_connection(mag_pre_connection, NULL, NULL, APR_HOOK_MIDDLE);
893 module AP_MODULE_DECLARE_DATA auth_gssapi_module =
895 STANDARD20_MODULE_STUFF,
896 mag_create_dir_config,