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;
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 req->ap_auth_type = apr_pstrdup(req->pool, mc->auth_type);
315 req->user = apr_pstrdup(req->pool, mc->user_name);
324 auth_header = apr_table_get(req->headers_in, "Authorization");
325 if (!auth_header) goto done;
327 auth_header_type = ap_getword_white(req->pool, &auth_header);
328 if (!auth_header_type) goto done;
330 if (strcasecmp(auth_header_type, "Negotiate") == 0) {
331 auth_type = "Negotiate";
333 auth_header_value = ap_getword_white(req->pool, &auth_header);
334 if (!auth_header_value) goto done;
335 input.length = apr_base64_decode_len(auth_header_value) + 1;
336 input.value = apr_pcalloc(req->pool, input.length);
337 if (!input.value) goto done;
338 input.length = apr_base64_decode(input.value, auth_header_value);
339 } else if ((strcasecmp(auth_header_type, "Basic") == 0) &&
340 (cfg->use_basic_auth == true)) {
344 gss_buffer_desc ba_user;
345 gss_buffer_desc ba_pwd;
347 ba_pwd.value = ap_pbase64decode(req->pool, auth_header);
348 if (!ba_pwd.value) goto done;
349 ba_user.value = ap_getword_nulls_nc(req->pool,
350 (char **)&ba_pwd.value, ':');
351 if (!ba_user.value) goto done;
352 if (((char *)ba_user.value)[0] == '\0' ||
353 ((char *)ba_pwd.value)[0] == '\0') {
354 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
355 "Invalid empty user or password for Basic Auth");
358 ba_user.length = strlen(ba_user.value);
359 ba_pwd.length = strlen(ba_pwd.value);
360 maj = gss_import_name(&min, &ba_user, GSS_C_NT_USER_NAME, &client);
361 if (GSS_ERROR(maj)) {
362 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
364 mag_error(req, "gss_import_name() failed",
368 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
369 /* Set a per-thread ccache in case we are using kerberos,
370 * it is not elegant but avoids interference between threads */
371 long long unsigned int rndname;
373 rs = apr_generate_random_bytes((unsigned char *)(&rndname),
374 sizeof(long long unsigned int));
375 if (rs != APR_SUCCESS) {
376 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
377 "Failed to generate random ccache name");
380 user_ccache = apr_psprintf(req->pool, "MEMORY:user_%qu", rndname);
381 maj = gss_krb5_ccache_name(&min, user_ccache, &orig_ccache);
382 if (GSS_ERROR(maj)) {
383 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
385 mag_error(req, "gss_krb5_ccache_name() "
386 "failed", maj, min));
390 maj = gss_acquire_cred_with_password(&min, client, &ba_pwd,
394 &user_cred, NULL, NULL);
395 if (GSS_ERROR(maj)) {
396 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
398 mag_error(req, "gss_acquire_cred_with_password() "
399 "failed", maj, min));
402 gss_release_name(&min, &client);
407 req->ap_auth_type = apr_pstrdup(req->pool, auth_type);
409 #ifdef HAVE_GSS_ACQUIRE_CRED_FROM
410 if (cfg->use_s4u2proxy) {
411 cred_usage = GSS_C_BOTH;
413 if (cfg->cred_store) {
414 maj = gss_acquire_cred_from(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
415 GSS_C_NO_OID_SET, cred_usage,
416 cfg->cred_store, &acquired_cred,
418 if (GSS_ERROR(maj)) {
419 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
420 mag_error(req, "gss_acquire_cred_from() failed",
428 if (!acquired_cred) {
429 /* Try to acquire default creds */
430 maj = gss_acquire_cred(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
431 GSS_C_NO_OID_SET, cred_usage,
432 &acquired_cred, NULL, NULL);
433 if (GSS_ERROR(maj)) {
434 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
435 "%s", mag_error(req, "gss_acquire_cred_from()"
436 " failed", maj, min));
440 maj = gss_inquire_cred(&min, acquired_cred, &server,
442 if (GSS_ERROR(maj)) {
443 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
444 "%s", mag_error(req, "gss_inquired_cred_() "
445 "failed", maj, min));
448 /* output and input are inverted here, this is intentional */
449 maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
450 GSS_C_NO_OID, 0, 300,
451 GSS_C_NO_CHANNEL_BINDINGS, &output,
452 NULL, &input, NULL, NULL);
453 if (GSS_ERROR(maj)) {
454 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
455 "%s", mag_error(req, "gss_init_sec_context() "
456 "failed", maj, min));
461 maj = gss_accept_sec_context(&min, pctx, acquired_cred,
462 &input, GSS_C_NO_CHANNEL_BINDINGS,
463 &client, &mech_type, &output, &flags, &vtime,
465 if (GSS_ERROR(maj)) {
466 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
467 mag_error(req, "gss_accept_sec_context() failed",
472 while (maj == GSS_S_CONTINUE_NEEDED) {
473 gss_release_buffer(&min, &input);
474 /* output and input are inverted here, this is intentional */
475 maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
476 GSS_C_NO_OID, 0, 300,
477 GSS_C_NO_CHANNEL_BINDINGS, &output,
478 NULL, &input, NULL, NULL);
479 if (GSS_ERROR(maj)) {
480 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
481 "%s", mag_error(req, "gss_init_sec_context() "
482 "failed", maj, min));
485 gss_release_buffer(&min, &output);
486 maj = gss_accept_sec_context(&min, pctx, acquired_cred,
487 &input, GSS_C_NO_CHANNEL_BINDINGS,
488 &client, &mech_type, &output, &flags,
489 &vtime, &delegated_cred);
490 if (GSS_ERROR(maj)) {
491 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
492 "%s", mag_error(req, "gss_accept_sec_context()"
493 " failed", maj, min));
497 } else if (maj == GSS_S_CONTINUE_NEEDED) {
499 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
500 "Mechanism needs continuation but neither "
501 "GssapiConnectionBound nor "
502 "GssapiUseSessions are available");
503 gss_delete_sec_context(&min, pctx, GSS_C_NO_BUFFER);
504 gss_release_buffer(&min, &output);
507 /* auth not complete send token and wait next packet */
511 /* Always set the GSS name in an env var */
512 maj = gss_display_name(&min, client, &name, NULL);
513 if (GSS_ERROR(maj)) {
514 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
515 mag_error(req, "gss_display_name() failed",
519 clientname = apr_pstrndup(req->pool, name.value, name.length);
520 apr_table_set(req->subprocess_env, "GSS_NAME", clientname);
522 #ifdef HAVE_GSS_STORE_CRED_INTO
523 if (cfg->deleg_ccache_dir && delegated_cred != GSS_C_NO_CREDENTIAL) {
524 char *ccachefile = NULL;
526 mag_store_deleg_creds(req, cfg->deleg_ccache_dir, clientname,
527 delegated_cred, &ccachefile);
530 apr_table_set(req->subprocess_env, "KRB5CCNAME", ccachefile);
535 if (cfg->map_to_local) {
536 maj = gss_localname(&min, client, mech_type, &lname);
537 if (maj != GSS_S_COMPLETE) {
538 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
539 mag_error(req, "gss_localname() failed", maj, min));
542 req->user = apr_pstrndup(req->pool, lname.value, lname.length);
544 req->user = clientname;
548 mc->user_name = apr_pstrdup(mc->parent, req->user);
549 mc->gss_name = apr_pstrdup(mc->parent, clientname);
550 mc->established = true;
551 if (vtime == GSS_C_INDEFINITE || vtime < MIN_SESS_EXP_TIME) {
552 vtime = MIN_SESS_EXP_TIME;
554 mc->expiration = time(NULL) + vtime;
555 if (cfg->use_sessions) {
556 mag_attempt_session(req, cfg, mc);
558 mc->auth_type = auth_type;
564 if (ret == HTTP_UNAUTHORIZED) {
565 if (output.length != 0) {
566 replen = apr_base64_encode_len(output.length) + 1;
567 reply = apr_pcalloc(req->pool, 10 + replen);
569 memcpy(reply, "Negotiate ", 10);
570 apr_base64_encode(&reply[10], output.value, output.length);
571 apr_table_add(req->err_headers_out,
572 "WWW-Authenticate", reply);
575 apr_table_add(req->err_headers_out,
576 "WWW-Authenticate", "Negotiate");
577 if (cfg->use_basic_auth) {
578 apr_table_add(req->err_headers_out,
580 apr_psprintf(req->pool, "Basic realm=\"%s\"",
585 #ifdef HAVE_GSS_KRB5_CCACHE_NAME
586 if (user_ccache != NULL) {
587 maj = gss_krb5_ccache_name(&min, orig_ccache, NULL);
588 if (maj != GSS_S_COMPLETE) {
589 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
590 "Failed to restore per-thread ccache, %s",
591 mag_error(req, "gss_krb5_ccache_name() "
592 "failed", maj, min));
596 gss_delete_sec_context(&min, &user_ctx, &output);
597 gss_release_cred(&min, &user_cred);
598 gss_release_cred(&min, &acquired_cred);
599 gss_release_cred(&min, &delegated_cred);
600 gss_release_buffer(&min, &output);
601 gss_release_name(&min, &client);
602 gss_release_name(&min, &server);
603 gss_release_buffer(&min, &name);
604 gss_release_buffer(&min, &lname);
609 static void *mag_create_dir_config(apr_pool_t *p, char *dir)
611 struct mag_config *cfg;
613 cfg = (struct mag_config *)apr_pcalloc(p, sizeof(struct mag_config));
614 if (!cfg) return NULL;
620 static const char *mag_ssl_only(cmd_parms *parms, void *mconfig, int on)
622 struct mag_config *cfg = (struct mag_config *)mconfig;
623 cfg->ssl_only = on ? true : false;
627 static const char *mag_map_to_local(cmd_parms *parms, void *mconfig, int on)
629 struct mag_config *cfg = (struct mag_config *)mconfig;
630 cfg->map_to_local = on ? true : false;
634 static const char *mag_conn_ctx(cmd_parms *parms, void *mconfig, int on)
636 struct mag_config *cfg = (struct mag_config *)mconfig;
637 cfg->gss_conn_ctx = on ? true : false;
641 static const char *mag_use_sess(cmd_parms *parms, void *mconfig, int on)
643 struct mag_config *cfg = (struct mag_config *)mconfig;
644 cfg->use_sessions = on ? true : false;
648 static const char *mag_use_s4u2p(cmd_parms *parms, void *mconfig, int on)
650 struct mag_config *cfg = (struct mag_config *)mconfig;
651 cfg->use_s4u2proxy = on ? true : false;
653 if (cfg->deleg_ccache_dir == NULL) {
654 cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, "/tmp");
655 if (!cfg->deleg_ccache_dir) {
656 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0,
657 parms->server, "%s", "OOM setting deleg_ccache_dir.");
663 static const char *mag_sess_key(cmd_parms *parms, void *mconfig, const char *w)
665 struct mag_config *cfg = (struct mag_config *)mconfig;
672 if (strncmp(w, "key:", 4) != 0) {
673 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
674 "Invalid key format, expected prefix 'key:'");
679 l = apr_base64_decode_len(k);
680 val = apr_palloc(parms->temp_pool, l);
682 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
683 "Failed to get memory to decode key");
687 key.length = (int)apr_base64_decode_binary(val, k);
688 key.value = (unsigned char *)val;
690 if (key.length < 32) {
691 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
692 "Invalid key length, expected >=32 got %d", key.length);
696 rc = SEAL_KEY_CREATE(cfg->pool, &cfg->mag_skey, &key);
698 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
699 "Failed to import sealing key!");
704 #define MAX_CRED_OPTIONS 10
706 static const char *mag_cred_store(cmd_parms *parms, void *mconfig,
709 struct mag_config *cfg = (struct mag_config *)mconfig;
710 gss_key_value_element_desc *elements;
719 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
720 "%s [%s]", "Invalid syntax for GssapiCredStore option", w);
724 key = apr_pstrndup(parms->pool, w, (p-w));
725 value = apr_pstrdup(parms->pool, p + 1);
726 if (!key || !value) {
727 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
728 "%s", "OOM handling GssapiCredStore option");
732 if (!cfg->cred_store) {
733 cfg->cred_store = apr_pcalloc(parms->pool,
734 sizeof(gss_key_value_set_desc));
735 if (!cfg->cred_store) {
736 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
737 "%s", "OOM handling GssapiCredStore option");
740 size = sizeof(gss_key_value_element_desc) * MAX_CRED_OPTIONS;
741 cfg->cred_store->elements = apr_palloc(parms->pool, size);
742 if (!cfg->cred_store->elements) {
743 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
744 "%s", "OOM handling GssapiCredStore option");
748 elements = cfg->cred_store->elements;
749 count = cfg->cred_store->count;
751 if (count >= MAX_CRED_OPTIONS) {
752 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
753 "Too many GssapiCredStore options (MAX: %d)",
757 cfg->cred_store->count++;
759 elements[count].key = key;
760 elements[count].value = value;
765 static const char *mag_deleg_ccache_dir(cmd_parms *parms, void *mconfig,
768 struct mag_config *cfg = (struct mag_config *)mconfig;
770 cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, value);
771 if (!cfg->deleg_ccache_dir) {
772 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
773 "%s", "OOM handling GssapiDelegCcacheDir option");
779 static const char *mag_use_basic_auth(cmd_parms *parms, void *mconfig, int on)
781 struct mag_config *cfg = (struct mag_config *)mconfig;
783 cfg->use_basic_auth = on ? true : false;
787 static const command_rec mag_commands[] = {
788 AP_INIT_FLAG("GssapiSSLonly", mag_ssl_only, NULL, OR_AUTHCFG,
789 "Work only if connection is SSL Secured"),
790 AP_INIT_FLAG("GssapiLocalName", mag_map_to_local, NULL, OR_AUTHCFG,
791 "Translate principals to local names"),
792 AP_INIT_FLAG("GssapiConnectionBound", mag_conn_ctx, NULL, OR_AUTHCFG,
793 "Authentication is bound to the TCP connection"),
794 AP_INIT_FLAG("GssapiUseSessions", mag_use_sess, NULL, OR_AUTHCFG,
795 "Authentication uses mod_sessions to hold status"),
796 AP_INIT_RAW_ARGS("GssapiSessionKey", mag_sess_key, NULL, OR_AUTHCFG,
797 "Key Used to seal session data."),
798 #ifdef HAVE_GSS_ACQUIRE_CRED_FROM
799 AP_INIT_FLAG("GssapiUseS4U2Proxy", mag_use_s4u2p, NULL, OR_AUTHCFG,
800 "Initializes credentials for s4u2proxy usage"),
802 #ifdef HAVE_GSS_STORE_CRED_INTO
803 AP_INIT_ITERATE("GssapiCredStore", mag_cred_store, NULL, OR_AUTHCFG,
805 AP_INIT_RAW_ARGS("GssapiDelegCcacheDir", mag_deleg_ccache_dir, NULL,
806 OR_AUTHCFG, "Directory to store delegated credentials"),
808 #ifdef HAVE_GSS_ACQUIRE_CRED_WITH_PASSWORD
809 AP_INIT_FLAG("GssapiBasicAuth", mag_use_basic_auth, NULL, OR_AUTHCFG,
810 "Allows use of Basic Auth for authentication"),
816 mag_register_hooks(apr_pool_t *p)
818 ap_hook_check_user_id(mag_auth, NULL, NULL, APR_HOOK_MIDDLE);
819 ap_hook_post_config(mag_post_config, NULL, NULL, APR_HOOK_MIDDLE);
820 ap_hook_pre_connection(mag_pre_connection, NULL, NULL, APR_HOOK_MIDDLE);
823 module AP_MODULE_DECLARE_DATA auth_gssapi_module =
825 STANDARD20_MODULE_STUFF,
826 mag_create_dir_config,