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 APR_DECLARE_OPTIONAL_FN(int, ssl_is_https, (conn_rec *));
35 static char *mag_status(request_rec *req, int type, uint32_t err)
37 uint32_t maj_ret, min_ret;
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) {
54 msg_ret = apr_psprintf(req->pool, "%s, %*s",
55 msg_ret, len, (char *)text.value);
57 msg_ret = apr_psprintf(req->pool, "%*s", len, (char *)text.value);
59 gss_release_buffer(&min_ret, &text);
60 } while (msg_ctx != 0);
65 static char *mag_error(request_rec *req, const char *msg,
66 uint32_t maj, uint32_t min)
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);
76 static APR_OPTIONAL_FN_TYPE(ssl_is_https) *mag_is_https = NULL;
78 static int mag_post_config(apr_pool_t *cfgpool, apr_pool_t *log,
79 apr_pool_t *temp, server_rec *s)
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);
89 static int mag_pre_connection(conn_rec *c, void *csd)
93 mc = apr_pcalloc(c->pool, sizeof(struct mag_conn));
94 if (!mc) return DECLINED;
97 ap_set_module_config(c->conn_config, &auth_gssapi_module, (void*)mc);
101 static apr_status_t mag_conn_destroy(void *ptr)
103 struct mag_conn *mc = (struct mag_conn *)ptr;
107 (void)gss_delete_sec_context(&min, &mc->ctx, GSS_C_NO_BUFFER);
108 mc->established = false;
113 static bool mag_conn_is_https(conn_rec *c)
116 if (mag_is_https(c)) return true;
122 static char *escape(apr_pool_t *pool, const char *name,
123 char find, const char *replace)
125 char *escaped = NULL;
130 namecopy = apr_pstrdup(pool, name);
131 if (!namecopy) goto done;
133 p = strchr(namecopy, find);
134 if (!p) return namecopy;
139 /* terminate previous segment */
142 escaped = apr_pstrcat(pool, escaped, n, replace, NULL);
144 escaped = apr_pstrcat(pool, n, replace, NULL);
146 if (!escaped) goto done;
147 /* move to next segment */
151 /* append last segment if any */
153 escaped = apr_pstrcat(pool, escaped, n, NULL);
158 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, NULL,
159 "OOM escaping name");
164 static void mag_store_deleg_creds(request_rec *req,
165 char *dir, char *clientname,
166 gss_cred_id_t delegated_cred,
169 gss_key_value_element_desc element;
170 gss_key_value_set_desc store;
175 /* We need to escape away '/', we can't have path separators in
176 * a ccache file name */
177 /* first double escape the esacping char (~) if any */
178 escaped = escape(req->pool, clientname, '~', "~~");
179 if (!escaped) return;
180 /* then escape away the separator (/) if any */
181 escaped = escape(req->pool, escaped, '/', "~");
182 if (!escaped) return;
184 value = apr_psprintf(req->pool, "FILE:%s/%s", dir, escaped);
186 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, NULL,
187 "OOM storing delegated credentials");
191 element.key = "ccache";
192 element.value = value;
193 store.elements = &element;
196 maj = gss_store_cred_into(&min, delegated_cred, GSS_C_INITIATE,
197 GSS_C_NULL_OID, 1, 1, &store, NULL, NULL);
198 if (GSS_ERROR(maj)) {
199 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
200 mag_error(req, "failed to store delegated creds",
207 static int mag_auth(request_rec *req)
210 const char *auth_type;
211 struct mag_config *cfg;
212 const char *auth_header;
213 char *auth_header_type;
214 char *auth_header_value;
215 int ret = HTTP_UNAUTHORIZED;
216 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
218 gss_buffer_desc input = GSS_C_EMPTY_BUFFER;
219 gss_buffer_desc output = GSS_C_EMPTY_BUFFER;
220 gss_buffer_desc name = GSS_C_EMPTY_BUFFER;
221 gss_name_t client = GSS_C_NO_NAME;
222 gss_cred_id_t user_cred = GSS_C_NO_CREDENTIAL;
223 gss_cred_id_t acquired_cred = GSS_C_NO_CREDENTIAL;
224 gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
225 gss_cred_usage_t cred_usage = GSS_C_ACCEPT;
232 gss_OID mech_type = GSS_C_NO_OID;
233 gss_buffer_desc lname = GSS_C_EMPTY_BUFFER;
234 struct mag_conn *mc = NULL;
235 bool is_basic = false;
236 gss_ctx_id_t user_ctx = GSS_C_NO_CONTEXT;
237 gss_name_t server = GSS_C_NO_NAME;
238 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
239 const char *user_ccache = NULL;
240 const char *orig_ccache = NULL;
242 uint32_t init_flags = 0;
245 type = ap_auth_type(req);
246 if ((type == NULL) || (strcasecmp(type, "GSSAPI") != 0)) {
250 cfg = ap_get_module_config(req->per_dir_config, &auth_gssapi_module);
252 /* implicit auth for subrequests if main auth already happened */
253 if (!ap_is_initial_req(req)) {
254 type = ap_auth_type(req->main);
255 if ((type != NULL) && (strcasecmp(type, "GSSAPI") == 0)) {
256 /* warn if the subrequest location and the main request
257 * location have different configs */
258 if (cfg != ap_get_module_config(req->main->per_dir_config,
259 &auth_gssapi_module)) {
260 ap_log_rerror(APLOG_MARK, APLOG_WARNING||APLOG_NOERRNO, 0,
261 req, "Subrequest authentication bypass on "
262 "location with different configuration!");
264 if (req->main->user) {
265 req->user = apr_pstrdup(req->pool, req->main->user);
268 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
269 "The main request is tasked to establish the "
270 "security context, can't proceed!");
271 return HTTP_UNAUTHORIZED;
274 ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, req,
275 "Subrequest GSSAPI auth with no auth on the main "
276 "request. This operation may fail if other "
277 "subrequests already established a context or the "
278 "mechanism requires multiple roundtrips.");
283 if (!mag_conn_is_https(req->connection)) {
284 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
285 "Not a TLS connection, refusing to authenticate!");
290 if (cfg->gss_conn_ctx) {
291 mc = (struct mag_conn *)ap_get_module_config(
292 req->connection->conn_config,
293 &auth_gssapi_module);
295 ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, req,
296 "Failed to retrieve connection context!");
301 /* if available, session always supersedes connection bound data */
302 if (cfg->use_sessions) {
303 mag_check_session(req, cfg, &mc);
307 /* register the context in the memory pool, so it can be freed
308 * when the connection/request is terminated */
309 apr_pool_userdata_set(mc, "mag_conn_ptr",
310 mag_conn_destroy, mc->parent);
312 if (mc->established) {
313 ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, req,
314 "Already established context found!");
315 apr_table_set(req->subprocess_env, "GSS_NAME", mc->gss_name);
316 apr_table_set(req->subprocess_env, "GSS_SESSION_EXPIRATION",
317 apr_psprintf(req->pool,
318 "%ld", (long)mc->expiration));
319 req->ap_auth_type = apr_pstrdup(req->pool, mc->auth_type);
320 req->user = apr_pstrdup(req->pool, mc->user_name);
329 auth_header = apr_table_get(req->headers_in, "Authorization");
330 if (!auth_header) goto done;
332 auth_header_type = ap_getword_white(req->pool, &auth_header);
333 if (!auth_header_type) goto done;
335 if (strcasecmp(auth_header_type, "Negotiate") == 0) {
336 auth_type = "Negotiate";
338 auth_header_value = ap_getword_white(req->pool, &auth_header);
339 if (!auth_header_value) goto done;
340 input.length = apr_base64_decode_len(auth_header_value) + 1;
341 input.value = apr_pcalloc(req->pool, input.length);
342 if (!input.value) goto done;
343 input.length = apr_base64_decode(input.value, auth_header_value);
344 } else if ((strcasecmp(auth_header_type, "Basic") == 0) &&
345 (cfg->use_basic_auth == true)) {
349 gss_buffer_desc ba_user;
350 gss_buffer_desc ba_pwd;
352 ba_pwd.value = ap_pbase64decode(req->pool, auth_header);
353 if (!ba_pwd.value) goto done;
354 ba_user.value = ap_getword_nulls_nc(req->pool,
355 (char **)&ba_pwd.value, ':');
356 if (!ba_user.value) goto done;
357 if (((char *)ba_user.value)[0] == '\0' ||
358 ((char *)ba_pwd.value)[0] == '\0') {
359 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
360 "Invalid empty user or password for Basic Auth");
363 ba_user.length = strlen(ba_user.value);
364 ba_pwd.length = strlen(ba_pwd.value);
365 maj = gss_import_name(&min, &ba_user, GSS_C_NT_USER_NAME, &client);
366 if (GSS_ERROR(maj)) {
367 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
369 mag_error(req, "gss_import_name() failed",
373 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
374 /* Set a per-thread ccache in case we are using kerberos,
375 * it is not elegant but avoids interference between threads */
376 long long unsigned int rndname;
378 rs = apr_generate_random_bytes((unsigned char *)(&rndname),
379 sizeof(long long unsigned int));
380 if (rs != APR_SUCCESS) {
381 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
382 "Failed to generate random ccache name");
385 user_ccache = apr_psprintf(req->pool, "MEMORY:user_%qu", rndname);
386 maj = gss_krb5_ccache_name(&min, user_ccache, &orig_ccache);
387 if (GSS_ERROR(maj)) {
388 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
390 mag_error(req, "gss_krb5_ccache_name() "
391 "failed", maj, min));
395 maj = gss_acquire_cred_with_password(&min, client, &ba_pwd,
399 &user_cred, NULL, NULL);
400 if (GSS_ERROR(maj)) {
401 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
403 mag_error(req, "gss_acquire_cred_with_password() "
404 "failed", maj, min));
407 gss_release_name(&min, &client);
412 req->ap_auth_type = apr_pstrdup(req->pool, auth_type);
414 #ifdef HAVE_GSS_ACQUIRE_CRED_FROM
415 if (cfg->use_s4u2proxy) {
416 cred_usage = GSS_C_BOTH;
418 if (cfg->cred_store) {
419 maj = gss_acquire_cred_from(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
420 GSS_C_NO_OID_SET, cred_usage,
421 cfg->cred_store, &acquired_cred,
423 if (GSS_ERROR(maj)) {
424 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
425 mag_error(req, "gss_acquire_cred_from() failed",
433 if (!acquired_cred) {
434 /* Try to acquire default creds */
435 maj = gss_acquire_cred(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
436 GSS_C_NO_OID_SET, cred_usage,
437 &acquired_cred, NULL, NULL);
438 if (GSS_ERROR(maj)) {
439 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
440 "%s", mag_error(req, "gss_acquire_cred_from()"
441 " failed", maj, min));
445 maj = gss_inquire_cred(&min, acquired_cred, &server,
447 if (GSS_ERROR(maj)) {
448 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
449 "%s", mag_error(req, "gss_inquired_cred_() "
450 "failed", maj, min));
454 if (cfg->deleg_ccache_dir) {
455 /* delegate ourselves credentials so we store them as requested */
456 init_flags |= GSS_C_DELEG_FLAG;
459 /* output and input are inverted here, this is intentional */
460 maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
461 GSS_C_NO_OID, init_flags, 300,
462 GSS_C_NO_CHANNEL_BINDINGS, &output,
463 NULL, &input, NULL, NULL);
464 if (GSS_ERROR(maj)) {
465 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
466 "%s", mag_error(req, "gss_init_sec_context() "
467 "failed", maj, min));
472 maj = gss_accept_sec_context(&min, pctx, acquired_cred,
473 &input, GSS_C_NO_CHANNEL_BINDINGS,
474 &client, &mech_type, &output, &flags, &vtime,
476 if (GSS_ERROR(maj)) {
477 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
478 mag_error(req, "gss_accept_sec_context() failed",
483 while (maj == GSS_S_CONTINUE_NEEDED) {
484 gss_release_buffer(&min, &input);
485 /* output and input are inverted here, this is intentional */
486 maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
487 GSS_C_NO_OID, init_flags, 300,
488 GSS_C_NO_CHANNEL_BINDINGS, &output,
489 NULL, &input, NULL, NULL);
490 if (GSS_ERROR(maj)) {
491 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
492 "%s", mag_error(req, "gss_init_sec_context() "
493 "failed", maj, min));
496 gss_release_buffer(&min, &output);
497 maj = gss_accept_sec_context(&min, pctx, acquired_cred,
498 &input, GSS_C_NO_CHANNEL_BINDINGS,
499 &client, &mech_type, &output, &flags,
500 &vtime, &delegated_cred);
501 if (GSS_ERROR(maj)) {
502 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
503 "%s", mag_error(req, "gss_accept_sec_context()"
504 " failed", maj, min));
508 } else if (maj == GSS_S_CONTINUE_NEEDED) {
510 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
511 "Mechanism needs continuation but neither "
512 "GssapiConnectionBound nor "
513 "GssapiUseSessions are available");
514 gss_delete_sec_context(&min, pctx, GSS_C_NO_BUFFER);
515 gss_release_buffer(&min, &output);
518 /* auth not complete send token and wait next packet */
522 /* Always set the GSS name in an env var */
523 maj = gss_display_name(&min, client, &name, NULL);
524 if (GSS_ERROR(maj)) {
525 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
526 mag_error(req, "gss_display_name() failed",
530 clientname = apr_pstrndup(req->pool, name.value, name.length);
531 apr_table_set(req->subprocess_env, "GSS_NAME", clientname);
532 expiration = time(NULL) + vtime;
533 apr_table_set(req->subprocess_env, "GSS_SESSION_EXPIRATION",
534 apr_psprintf(req->pool, "%ld", (long)expiration));
536 #ifdef HAVE_GSS_STORE_CRED_INTO
537 if (cfg->deleg_ccache_dir && delegated_cred != GSS_C_NO_CREDENTIAL) {
538 char *ccachefile = NULL;
540 mag_store_deleg_creds(req, cfg->deleg_ccache_dir, clientname,
541 delegated_cred, &ccachefile);
544 apr_table_set(req->subprocess_env, "KRB5CCNAME", ccachefile);
549 if (cfg->map_to_local) {
550 maj = gss_localname(&min, client, mech_type, &lname);
551 if (maj != GSS_S_COMPLETE) {
552 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
553 mag_error(req, "gss_localname() failed", maj, min));
556 req->user = apr_pstrndup(req->pool, lname.value, lname.length);
558 req->user = clientname;
562 mc->user_name = apr_pstrdup(mc->parent, req->user);
563 mc->gss_name = apr_pstrdup(mc->parent, clientname);
564 mc->established = true;
565 if (vtime == GSS_C_INDEFINITE || vtime < MIN_SESS_EXP_TIME) {
566 vtime = MIN_SESS_EXP_TIME;
568 mc->expiration = expiration;
569 if (cfg->use_sessions) {
570 mag_attempt_session(req, cfg, mc);
572 mc->auth_type = auth_type;
575 if (cfg->send_persist)
576 apr_table_set(req->headers_out, "Persistent-Auth",
577 cfg->gss_conn_ctx ? "true" : "false");
582 if ((!is_basic) && (output.length != 0)) {
583 replen = apr_base64_encode_len(output.length) + 1;
584 reply = apr_pcalloc(req->pool, 10 + replen);
586 memcpy(reply, "Negotiate ", 10);
587 apr_base64_encode(&reply[10], output.value, output.length);
588 apr_table_add(req->err_headers_out,
589 "WWW-Authenticate", reply);
591 } else if (ret == HTTP_UNAUTHORIZED) {
592 apr_table_add(req->err_headers_out,
593 "WWW-Authenticate", "Negotiate");
594 if (cfg->use_basic_auth) {
595 apr_table_add(req->err_headers_out,
597 apr_psprintf(req->pool, "Basic realm=\"%s\"",
601 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
602 if (user_ccache != NULL) {
603 maj = gss_krb5_ccache_name(&min, orig_ccache, NULL);
604 if (maj != GSS_S_COMPLETE) {
605 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
606 "Failed to restore per-thread ccache, %s",
607 mag_error(req, "gss_krb5_ccache_name() "
608 "failed", maj, min));
612 gss_delete_sec_context(&min, &user_ctx, &output);
613 gss_release_cred(&min, &user_cred);
614 gss_release_cred(&min, &acquired_cred);
615 gss_release_cred(&min, &delegated_cred);
616 gss_release_buffer(&min, &output);
617 gss_release_name(&min, &client);
618 gss_release_name(&min, &server);
619 gss_release_buffer(&min, &name);
620 gss_release_buffer(&min, &lname);
625 static void *mag_create_dir_config(apr_pool_t *p, char *dir)
627 struct mag_config *cfg;
629 cfg = (struct mag_config *)apr_pcalloc(p, sizeof(struct mag_config));
630 if (!cfg) return NULL;
636 static const char *mag_ssl_only(cmd_parms *parms, void *mconfig, int on)
638 struct mag_config *cfg = (struct mag_config *)mconfig;
639 cfg->ssl_only = on ? true : false;
643 static const char *mag_map_to_local(cmd_parms *parms, void *mconfig, int on)
645 struct mag_config *cfg = (struct mag_config *)mconfig;
646 cfg->map_to_local = on ? true : false;
650 static const char *mag_conn_ctx(cmd_parms *parms, void *mconfig, int on)
652 struct mag_config *cfg = (struct mag_config *)mconfig;
653 cfg->gss_conn_ctx = on ? true : false;
657 static const char *mag_send_persist(cmd_parms *parms, void *mconfig, int on)
659 struct mag_config *cfg = (struct mag_config *)mconfig;
660 cfg->send_persist = on ? true : false;
664 static const char *mag_use_sess(cmd_parms *parms, void *mconfig, int on)
666 struct mag_config *cfg = (struct mag_config *)mconfig;
667 cfg->use_sessions = on ? true : false;
671 static const char *mag_use_s4u2p(cmd_parms *parms, void *mconfig, int on)
673 struct mag_config *cfg = (struct mag_config *)mconfig;
674 cfg->use_s4u2proxy = on ? true : false;
676 if (cfg->deleg_ccache_dir == NULL) {
677 cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, "/tmp");
678 if (!cfg->deleg_ccache_dir) {
679 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0,
680 parms->server, "%s", "OOM setting deleg_ccache_dir.");
686 static const char *mag_sess_key(cmd_parms *parms, void *mconfig, const char *w)
688 struct mag_config *cfg = (struct mag_config *)mconfig;
695 if (strncmp(w, "key:", 4) != 0) {
696 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
697 "Invalid key format, expected prefix 'key:'");
702 l = apr_base64_decode_len(k);
703 val = apr_palloc(parms->temp_pool, l);
705 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
706 "Failed to get memory to decode key");
710 keys.length = (int)apr_base64_decode_binary(val, k);
711 keys.value = (unsigned char *)val;
713 if (keys.length != 32) {
714 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
715 "Invalid key lenght, expected 32 got %d", keys.length);
719 rc = SEAL_KEY_CREATE(cfg->pool, &cfg->mag_skey, &keys);
721 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
722 "Failed to import sealing key!");
727 #define MAX_CRED_OPTIONS 10
729 static const char *mag_cred_store(cmd_parms *parms, void *mconfig,
732 struct mag_config *cfg = (struct mag_config *)mconfig;
733 gss_key_value_element_desc *elements;
742 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
743 "%s [%s]", "Invalid syntax for GssapiCredStore option", w);
747 key = apr_pstrndup(parms->pool, w, (p-w));
748 value = apr_pstrdup(parms->pool, p + 1);
749 if (!key || !value) {
750 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
751 "%s", "OOM handling GssapiCredStore option");
755 if (!cfg->cred_store) {
756 cfg->cred_store = apr_pcalloc(parms->pool,
757 sizeof(gss_key_value_set_desc));
758 if (!cfg->cred_store) {
759 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
760 "%s", "OOM handling GssapiCredStore option");
763 size = sizeof(gss_key_value_element_desc) * MAX_CRED_OPTIONS;
764 cfg->cred_store->elements = apr_palloc(parms->pool, size);
765 if (!cfg->cred_store->elements) {
766 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
767 "%s", "OOM handling GssapiCredStore option");
771 elements = cfg->cred_store->elements;
772 count = cfg->cred_store->count;
774 if (count >= MAX_CRED_OPTIONS) {
775 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
776 "Too many GssapiCredStore options (MAX: %d)",
780 cfg->cred_store->count++;
782 elements[count].key = key;
783 elements[count].value = value;
788 static const char *mag_deleg_ccache_dir(cmd_parms *parms, void *mconfig,
791 struct mag_config *cfg = (struct mag_config *)mconfig;
793 cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, value);
794 if (!cfg->deleg_ccache_dir) {
795 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
796 "%s", "OOM handling GssapiDelegCcacheDir option");
802 static const char *mag_use_basic_auth(cmd_parms *parms, void *mconfig, int on)
804 struct mag_config *cfg = (struct mag_config *)mconfig;
806 cfg->use_basic_auth = on ? true : false;
810 static const command_rec mag_commands[] = {
811 AP_INIT_FLAG("GssapiSSLonly", mag_ssl_only, NULL, OR_AUTHCFG,
812 "Work only if connection is SSL Secured"),
813 AP_INIT_FLAG("GssapiLocalName", mag_map_to_local, NULL, OR_AUTHCFG,
814 "Translate principals to local names"),
815 AP_INIT_FLAG("GssapiConnectionBound", mag_conn_ctx, NULL, OR_AUTHCFG,
816 "Authentication is bound to the TCP connection"),
817 AP_INIT_FLAG("GssapiSignalPersistentAuth", mag_send_persist, NULL, OR_AUTHCFG,
818 "Send Persitent-Auth header according to connection bound"),
819 AP_INIT_FLAG("GssapiUseSessions", mag_use_sess, NULL, OR_AUTHCFG,
820 "Authentication uses mod_sessions to hold status"),
821 AP_INIT_RAW_ARGS("GssapiSessionKey", mag_sess_key, NULL, OR_AUTHCFG,
822 "Key Used to seal session data."),
823 #ifdef HAVE_GSS_ACQUIRE_CRED_FROM
824 AP_INIT_FLAG("GssapiUseS4U2Proxy", mag_use_s4u2p, NULL, OR_AUTHCFG,
825 "Initializes credentials for s4u2proxy usage"),
827 #ifdef HAVE_GSS_STORE_CRED_INTO
828 AP_INIT_ITERATE("GssapiCredStore", mag_cred_store, NULL, OR_AUTHCFG,
830 AP_INIT_RAW_ARGS("GssapiDelegCcacheDir", mag_deleg_ccache_dir, NULL,
831 OR_AUTHCFG, "Directory to store delegated credentials"),
833 #ifdef HAVE_GSS_ACQUIRE_CRED_WITH_PASSWORD
834 AP_INIT_FLAG("GssapiBasicAuth", mag_use_basic_auth, NULL, OR_AUTHCFG,
835 "Allows use of Basic Auth for authentication"),
841 mag_register_hooks(apr_pool_t *p)
843 ap_hook_check_user_id(mag_auth, NULL, NULL, APR_HOOK_MIDDLE);
844 ap_hook_post_config(mag_post_config, NULL, NULL, APR_HOOK_MIDDLE);
845 ap_hook_pre_connection(mag_pre_connection, NULL, NULL, APR_HOOK_MIDDLE);
848 module AP_MODULE_DECLARE_DATA auth_gssapi_module =
850 STANDARD20_MODULE_STUFF,
851 mag_create_dir_config,