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;
244 type = ap_auth_type(req);
245 if ((type == NULL) || (strcasecmp(type, "GSSAPI") != 0)) {
249 cfg = ap_get_module_config(req->per_dir_config, &auth_gssapi_module);
251 /* implicit auth for subrequests if main auth already happened */
252 if (!ap_is_initial_req(req)) {
253 type = ap_auth_type(req->main);
254 if ((type != NULL) && (strcasecmp(type, "GSSAPI") == 0)) {
255 /* warn if the subrequest location and the main request
256 * location have different configs */
257 if (cfg != ap_get_module_config(req->main->per_dir_config,
258 &auth_gssapi_module)) {
259 ap_log_rerror(APLOG_MARK, APLOG_WARNING||APLOG_NOERRNO, 0,
260 req, "Subrequest authentication bypass on "
261 "location with different configuration!");
263 if (req->main->user) {
264 req->user = apr_pstrdup(req->pool, req->main->user);
267 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
268 "The main request is tasked to establish the "
269 "security context, can't proceed!");
270 return HTTP_UNAUTHORIZED;
273 ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, req,
274 "Subrequest GSSAPI auth with no auth on the main "
275 "request. This operation may fail if other "
276 "subrequests already established a context or the "
277 "mechanism requires multiple roundtrips.");
282 if (!mag_conn_is_https(req->connection)) {
283 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
284 "Not a TLS connection, refusing to authenticate!");
289 if (cfg->gss_conn_ctx) {
290 mc = (struct mag_conn *)ap_get_module_config(
291 req->connection->conn_config,
292 &auth_gssapi_module);
294 ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, req,
295 "Failed to retrieve connection context!");
300 /* if available, session always supersedes connection bound data */
301 if (cfg->use_sessions) {
302 mag_check_session(req, cfg, &mc);
306 /* register the context in the memory pool, so it can be freed
307 * when the connection/request is terminated */
308 apr_pool_userdata_set(mc, "mag_conn_ptr",
309 mag_conn_destroy, mc->parent);
311 if (mc->established) {
312 ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, req,
313 "Already established context found!");
314 apr_table_set(req->subprocess_env, "GSS_NAME", mc->gss_name);
315 req->ap_auth_type = apr_pstrdup(req->pool, mc->auth_type);
316 req->user = apr_pstrdup(req->pool, mc->user_name);
325 auth_header = apr_table_get(req->headers_in, "Authorization");
326 if (!auth_header) goto done;
328 auth_header_type = ap_getword_white(req->pool, &auth_header);
329 if (!auth_header_type) goto done;
331 if (strcasecmp(auth_header_type, "Negotiate") == 0) {
332 auth_type = "Negotiate";
334 auth_header_value = ap_getword_white(req->pool, &auth_header);
335 if (!auth_header_value) goto done;
336 input.length = apr_base64_decode_len(auth_header_value) + 1;
337 input.value = apr_pcalloc(req->pool, input.length);
338 if (!input.value) goto done;
339 input.length = apr_base64_decode(input.value, auth_header_value);
340 } else if ((strcasecmp(auth_header_type, "Basic") == 0) &&
341 (cfg->use_basic_auth == true)) {
345 gss_buffer_desc ba_user;
346 gss_buffer_desc ba_pwd;
348 ba_pwd.value = ap_pbase64decode(req->pool, auth_header);
349 if (!ba_pwd.value) goto done;
350 ba_user.value = ap_getword_nulls_nc(req->pool,
351 (char **)&ba_pwd.value, ':');
352 if (!ba_user.value) goto done;
353 if (((char *)ba_user.value)[0] == '\0' ||
354 ((char *)ba_pwd.value)[0] == '\0') {
355 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
356 "Invalid empty user or password for Basic Auth");
359 ba_user.length = strlen(ba_user.value);
360 ba_pwd.length = strlen(ba_pwd.value);
361 maj = gss_import_name(&min, &ba_user, GSS_C_NT_USER_NAME, &client);
362 if (GSS_ERROR(maj)) {
363 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
365 mag_error(req, "gss_import_name() failed",
369 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
370 /* Set a per-thread ccache in case we are using kerberos,
371 * it is not elegant but avoids interference between threads */
372 long long unsigned int rndname;
374 rs = apr_generate_random_bytes((unsigned char *)(&rndname),
375 sizeof(long long unsigned int));
376 if (rs != APR_SUCCESS) {
377 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
378 "Failed to generate random ccache name");
381 user_ccache = apr_psprintf(req->pool, "MEMORY:user_%qu", rndname);
382 maj = gss_krb5_ccache_name(&min, user_ccache, &orig_ccache);
383 if (GSS_ERROR(maj)) {
384 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
386 mag_error(req, "gss_krb5_ccache_name() "
387 "failed", maj, min));
391 maj = gss_acquire_cred_with_password(&min, client, &ba_pwd,
395 &user_cred, NULL, NULL);
396 if (GSS_ERROR(maj)) {
397 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
399 mag_error(req, "gss_acquire_cred_with_password() "
400 "failed", maj, min));
403 gss_release_name(&min, &client);
408 req->ap_auth_type = apr_pstrdup(req->pool, auth_type);
410 #ifdef HAVE_GSS_ACQUIRE_CRED_FROM
411 if (cfg->use_s4u2proxy) {
412 cred_usage = GSS_C_BOTH;
414 if (cfg->cred_store) {
415 maj = gss_acquire_cred_from(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
416 GSS_C_NO_OID_SET, cred_usage,
417 cfg->cred_store, &acquired_cred,
419 if (GSS_ERROR(maj)) {
420 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
421 mag_error(req, "gss_acquire_cred_from() failed",
429 if (!acquired_cred) {
430 /* Try to acquire default creds */
431 maj = gss_acquire_cred(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
432 GSS_C_NO_OID_SET, cred_usage,
433 &acquired_cred, NULL, NULL);
434 if (GSS_ERROR(maj)) {
435 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
436 "%s", mag_error(req, "gss_acquire_cred_from()"
437 " failed", maj, min));
441 maj = gss_inquire_cred(&min, acquired_cred, &server,
443 if (GSS_ERROR(maj)) {
444 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
445 "%s", mag_error(req, "gss_inquired_cred_() "
446 "failed", maj, min));
450 if (cfg->deleg_ccache_dir) {
451 /* delegate ourselves credentials so we store them as requested */
452 init_flags |= GSS_C_DELEG_FLAG;
455 /* output and input are inverted here, this is intentional */
456 maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
457 GSS_C_NO_OID, init_flags, 300,
458 GSS_C_NO_CHANNEL_BINDINGS, &output,
459 NULL, &input, NULL, NULL);
460 if (GSS_ERROR(maj)) {
461 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
462 "%s", mag_error(req, "gss_init_sec_context() "
463 "failed", maj, min));
468 maj = gss_accept_sec_context(&min, pctx, acquired_cred,
469 &input, GSS_C_NO_CHANNEL_BINDINGS,
470 &client, &mech_type, &output, &flags, &vtime,
472 if (GSS_ERROR(maj)) {
473 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
474 mag_error(req, "gss_accept_sec_context() failed",
479 while (maj == GSS_S_CONTINUE_NEEDED) {
480 gss_release_buffer(&min, &input);
481 /* output and input are inverted here, this is intentional */
482 maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
483 GSS_C_NO_OID, init_flags, 300,
484 GSS_C_NO_CHANNEL_BINDINGS, &output,
485 NULL, &input, NULL, NULL);
486 if (GSS_ERROR(maj)) {
487 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
488 "%s", mag_error(req, "gss_init_sec_context() "
489 "failed", maj, min));
492 gss_release_buffer(&min, &output);
493 maj = gss_accept_sec_context(&min, pctx, acquired_cred,
494 &input, GSS_C_NO_CHANNEL_BINDINGS,
495 &client, &mech_type, &output, &flags,
496 &vtime, &delegated_cred);
497 if (GSS_ERROR(maj)) {
498 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
499 "%s", mag_error(req, "gss_accept_sec_context()"
500 " failed", maj, min));
504 } else if (maj == GSS_S_CONTINUE_NEEDED) {
506 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
507 "Mechanism needs continuation but neither "
508 "GssapiConnectionBound nor "
509 "GssapiUseSessions are available");
510 gss_delete_sec_context(&min, pctx, GSS_C_NO_BUFFER);
511 gss_release_buffer(&min, &output);
514 /* auth not complete send token and wait next packet */
518 /* Always set the GSS name in an env var */
519 maj = gss_display_name(&min, client, &name, NULL);
520 if (GSS_ERROR(maj)) {
521 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
522 mag_error(req, "gss_display_name() failed",
526 clientname = apr_pstrndup(req->pool, name.value, name.length);
527 apr_table_set(req->subprocess_env, "GSS_NAME", clientname);
529 #ifdef HAVE_GSS_STORE_CRED_INTO
530 if (cfg->deleg_ccache_dir && delegated_cred != GSS_C_NO_CREDENTIAL) {
531 char *ccachefile = NULL;
533 mag_store_deleg_creds(req, cfg->deleg_ccache_dir, clientname,
534 delegated_cred, &ccachefile);
537 apr_table_set(req->subprocess_env, "KRB5CCNAME", ccachefile);
542 if (cfg->map_to_local) {
543 maj = gss_localname(&min, client, mech_type, &lname);
544 if (maj != GSS_S_COMPLETE) {
545 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
546 mag_error(req, "gss_localname() failed", maj, min));
549 req->user = apr_pstrndup(req->pool, lname.value, lname.length);
551 req->user = clientname;
555 mc->user_name = apr_pstrdup(mc->parent, req->user);
556 mc->gss_name = apr_pstrdup(mc->parent, clientname);
557 mc->established = true;
558 if (vtime == GSS_C_INDEFINITE || vtime < MIN_SESS_EXP_TIME) {
559 vtime = MIN_SESS_EXP_TIME;
561 mc->expiration = time(NULL) + vtime;
562 if (cfg->use_sessions) {
563 mag_attempt_session(req, cfg, mc);
565 mc->auth_type = auth_type;
571 if ((!is_basic) && (output.length != 0)) {
572 replen = apr_base64_encode_len(output.length) + 1;
573 reply = apr_pcalloc(req->pool, 10 + replen);
575 memcpy(reply, "Negotiate ", 10);
576 apr_base64_encode(&reply[10], output.value, output.length);
577 apr_table_add(req->err_headers_out,
578 "WWW-Authenticate", reply);
580 } else if (ret == HTTP_UNAUTHORIZED) {
581 apr_table_add(req->err_headers_out,
582 "WWW-Authenticate", "Negotiate");
583 if (cfg->use_basic_auth) {
584 apr_table_add(req->err_headers_out,
586 apr_psprintf(req->pool, "Basic realm=\"%s\"",
590 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
591 if (user_ccache != NULL) {
592 maj = gss_krb5_ccache_name(&min, orig_ccache, NULL);
593 if (maj != GSS_S_COMPLETE) {
594 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
595 "Failed to restore per-thread ccache, %s",
596 mag_error(req, "gss_krb5_ccache_name() "
597 "failed", maj, min));
601 gss_delete_sec_context(&min, &user_ctx, &output);
602 gss_release_cred(&min, &user_cred);
603 gss_release_cred(&min, &acquired_cred);
604 gss_release_cred(&min, &delegated_cred);
605 gss_release_buffer(&min, &output);
606 gss_release_name(&min, &client);
607 gss_release_name(&min, &server);
608 gss_release_buffer(&min, &name);
609 gss_release_buffer(&min, &lname);
614 static void *mag_create_dir_config(apr_pool_t *p, char *dir)
616 struct mag_config *cfg;
618 cfg = (struct mag_config *)apr_pcalloc(p, sizeof(struct mag_config));
619 if (!cfg) return NULL;
625 static const char *mag_ssl_only(cmd_parms *parms, void *mconfig, int on)
627 struct mag_config *cfg = (struct mag_config *)mconfig;
628 cfg->ssl_only = on ? true : false;
632 static const char *mag_map_to_local(cmd_parms *parms, void *mconfig, int on)
634 struct mag_config *cfg = (struct mag_config *)mconfig;
635 cfg->map_to_local = on ? true : false;
639 static const char *mag_conn_ctx(cmd_parms *parms, void *mconfig, int on)
641 struct mag_config *cfg = (struct mag_config *)mconfig;
642 cfg->gss_conn_ctx = on ? true : false;
646 static const char *mag_use_sess(cmd_parms *parms, void *mconfig, int on)
648 struct mag_config *cfg = (struct mag_config *)mconfig;
649 cfg->use_sessions = on ? true : false;
653 static const char *mag_use_s4u2p(cmd_parms *parms, void *mconfig, int on)
655 struct mag_config *cfg = (struct mag_config *)mconfig;
656 cfg->use_s4u2proxy = on ? true : false;
658 if (cfg->deleg_ccache_dir == NULL) {
659 cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, "/tmp");
660 if (!cfg->deleg_ccache_dir) {
661 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0,
662 parms->server, "%s", "OOM setting deleg_ccache_dir.");
668 static const char *mag_sess_key(cmd_parms *parms, void *mconfig, const char *w)
670 struct mag_config *cfg = (struct mag_config *)mconfig;
677 if (strncmp(w, "key:", 4) != 0) {
678 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
679 "Invalid key format, expected prefix 'key:'");
684 l = apr_base64_decode_len(k);
685 val = apr_palloc(parms->temp_pool, l);
687 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
688 "Failed to get memory to decode key");
692 keys.length = (int)apr_base64_decode_binary(val, k);
693 keys.value = (unsigned char *)val;
695 if (keys.length != 32) {
696 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
697 "Invalid key lenght, expected 32 got %d", keys.length);
701 rc = SEAL_KEY_CREATE(cfg->pool, &cfg->mag_skey, &keys);
703 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
704 "Failed to import sealing key!");
709 #define MAX_CRED_OPTIONS 10
711 static const char *mag_cred_store(cmd_parms *parms, void *mconfig,
714 struct mag_config *cfg = (struct mag_config *)mconfig;
715 gss_key_value_element_desc *elements;
724 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
725 "%s [%s]", "Invalid syntax for GssapiCredStore option", w);
729 key = apr_pstrndup(parms->pool, w, (p-w));
730 value = apr_pstrdup(parms->pool, p + 1);
731 if (!key || !value) {
732 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
733 "%s", "OOM handling GssapiCredStore option");
737 if (!cfg->cred_store) {
738 cfg->cred_store = apr_pcalloc(parms->pool,
739 sizeof(gss_key_value_set_desc));
740 if (!cfg->cred_store) {
741 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
742 "%s", "OOM handling GssapiCredStore option");
745 size = sizeof(gss_key_value_element_desc) * MAX_CRED_OPTIONS;
746 cfg->cred_store->elements = apr_palloc(parms->pool, size);
747 if (!cfg->cred_store->elements) {
748 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
749 "%s", "OOM handling GssapiCredStore option");
753 elements = cfg->cred_store->elements;
754 count = cfg->cred_store->count;
756 if (count >= MAX_CRED_OPTIONS) {
757 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
758 "Too many GssapiCredStore options (MAX: %d)",
762 cfg->cred_store->count++;
764 elements[count].key = key;
765 elements[count].value = value;
770 static const char *mag_deleg_ccache_dir(cmd_parms *parms, void *mconfig,
773 struct mag_config *cfg = (struct mag_config *)mconfig;
775 cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, value);
776 if (!cfg->deleg_ccache_dir) {
777 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
778 "%s", "OOM handling GssapiDelegCcacheDir option");
784 static const char *mag_use_basic_auth(cmd_parms *parms, void *mconfig, int on)
786 struct mag_config *cfg = (struct mag_config *)mconfig;
788 cfg->use_basic_auth = on ? true : false;
792 static const command_rec mag_commands[] = {
793 AP_INIT_FLAG("GssapiSSLonly", mag_ssl_only, NULL, OR_AUTHCFG,
794 "Work only if connection is SSL Secured"),
795 AP_INIT_FLAG("GssapiLocalName", mag_map_to_local, NULL, OR_AUTHCFG,
796 "Translate principals to local names"),
797 AP_INIT_FLAG("GssapiConnectionBound", mag_conn_ctx, NULL, OR_AUTHCFG,
798 "Authentication is bound to the TCP connection"),
799 AP_INIT_FLAG("GssapiUseSessions", mag_use_sess, NULL, OR_AUTHCFG,
800 "Authentication uses mod_sessions to hold status"),
801 AP_INIT_RAW_ARGS("GssapiSessionKey", mag_sess_key, NULL, OR_AUTHCFG,
802 "Key Used to seal session data."),
803 #ifdef HAVE_GSS_ACQUIRE_CRED_FROM
804 AP_INIT_FLAG("GssapiUseS4U2Proxy", mag_use_s4u2p, NULL, OR_AUTHCFG,
805 "Initializes credentials for s4u2proxy usage"),
807 #ifdef HAVE_GSS_STORE_CRED_INTO
808 AP_INIT_ITERATE("GssapiCredStore", mag_cred_store, NULL, OR_AUTHCFG,
810 AP_INIT_RAW_ARGS("GssapiDelegCcacheDir", mag_deleg_ccache_dir, NULL,
811 OR_AUTHCFG, "Directory to store delegated credentials"),
813 #ifdef HAVE_GSS_ACQUIRE_CRED_WITH_PASSWORD
814 AP_INIT_FLAG("GssapiBasicAuth", mag_use_basic_auth, NULL, OR_AUTHCFG,
815 "Allows use of Basic Auth for authentication"),
821 mag_register_hooks(apr_pool_t *p)
823 ap_hook_check_user_id(mag_auth, NULL, NULL, APR_HOOK_MIDDLE);
824 ap_hook_post_config(mag_post_config, NULL, NULL, APR_HOOK_MIDDLE);
825 ap_hook_pre_connection(mag_pre_connection, NULL, NULL, APR_HOOK_MIDDLE);
828 module AP_MODULE_DECLARE_DATA auth_gssapi_module =
830 STANDARD20_MODULE_STUFF,
831 mag_create_dir_config,