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 void mag_store_deleg_creds(request_rec *req,
123 char *dir, char *clientname,
124 gss_cred_id_t delegated_cred,
127 gss_key_value_element_desc element;
128 gss_key_value_set_desc store;
132 value = apr_psprintf(req->pool, "FILE:%s/%s", dir, clientname);
134 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, NULL,
135 "OOM storing delegated credentials");
139 element.key = "ccache";
140 element.value = value;
141 store.elements = &element;
144 maj = gss_store_cred_into(&min, delegated_cred, GSS_C_INITIATE,
145 GSS_C_NULL_OID, 1, 1, &store, NULL, NULL);
146 if (GSS_ERROR(maj)) {
147 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
148 mag_error(req, "failed to store delegated creds",
155 static int mag_auth(request_rec *req)
158 const char *auth_type;
159 struct mag_config *cfg;
160 const char *auth_header;
161 char *auth_header_type;
162 char *auth_header_value;
163 int ret = HTTP_UNAUTHORIZED;
164 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
166 gss_buffer_desc input = GSS_C_EMPTY_BUFFER;
167 gss_buffer_desc output = GSS_C_EMPTY_BUFFER;
168 gss_buffer_desc name = GSS_C_EMPTY_BUFFER;
169 gss_name_t client = GSS_C_NO_NAME;
170 gss_cred_id_t user_cred = GSS_C_NO_CREDENTIAL;
171 gss_cred_id_t acquired_cred = GSS_C_NO_CREDENTIAL;
172 gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
173 gss_cred_usage_t cred_usage = GSS_C_ACCEPT;
180 gss_OID mech_type = GSS_C_NO_OID;
181 gss_buffer_desc lname = GSS_C_EMPTY_BUFFER;
182 struct mag_conn *mc = NULL;
183 bool is_basic = false;
184 gss_ctx_id_t user_ctx = GSS_C_NO_CONTEXT;
185 gss_name_t server = GSS_C_NO_NAME;
187 type = ap_auth_type(req);
188 if ((type == NULL) || (strcasecmp(type, "GSSAPI") != 0)) {
192 /* ignore auth for subrequests */
193 if (!ap_is_initial_req(req)) {
197 cfg = ap_get_module_config(req->per_dir_config, &auth_gssapi_module);
200 if (!mag_conn_is_https(req->connection)) {
201 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
202 "Not a TLS connection, refusing to authenticate!");
207 if (cfg->gss_conn_ctx) {
208 mc = (struct mag_conn *)ap_get_module_config(
209 req->connection->conn_config,
210 &auth_gssapi_module);
212 ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, req,
213 "Failed to retrieve connection context!");
218 /* if available, session always supersedes connection bound data */
219 if (cfg->use_sessions) {
220 mag_check_session(req, cfg, &mc);
224 /* register the context in the memory pool, so it can be freed
225 * when the connection/request is terminated */
226 apr_pool_userdata_set(mc, "mag_conn_ptr",
227 mag_conn_destroy, mc->parent);
229 if (mc->established) {
230 ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, req,
231 "Already established context found!");
232 apr_table_set(req->subprocess_env, "GSS_NAME", mc->gss_name);
233 req->ap_auth_type = apr_pstrdup(req->pool, mc->auth_type);
234 req->user = apr_pstrdup(req->pool, mc->user_name);
243 auth_header = apr_table_get(req->headers_in, "Authorization");
244 if (!auth_header) goto done;
246 auth_header_type = ap_getword_white(req->pool, &auth_header);
247 if (!auth_header_type) goto done;
249 if (strcasecmp(auth_header_type, "Negotiate") == 0) {
250 auth_type = "Negotiate";
252 auth_header_value = ap_getword_white(req->pool, &auth_header);
253 if (!auth_header_value) goto done;
254 input.length = apr_base64_decode_len(auth_header_value) + 1;
255 input.value = apr_pcalloc(req->pool, input.length);
256 if (!input.value) goto done;
257 input.length = apr_base64_decode(input.value, auth_header_value);
258 } else if (strcasecmp(auth_header_type, "Basic") == 0) {
262 gss_buffer_desc ba_user;
263 gss_buffer_desc ba_pwd;
265 switch (cfg->basic_auth) {
267 /* handle directly */
270 /* decline to handle ourselves, let other modules do it */
278 ba_pwd.value = ap_pbase64decode(req->pool, auth_header);
279 if (!ba_pwd.value) goto done;
280 ba_user.value = ap_getword_nulls_nc(req->pool,
281 (char **)&ba_pwd.value, ':');
282 if (!ba_user.value) goto done;
283 if (((char *)ba_user.value)[0] == '\0' ||
284 ((char *)ba_pwd.value)[0] == '\0') {
285 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
286 "Invalid empty user or password for Basic Auth");
289 ba_user.length = strlen(ba_user.value);
290 ba_pwd.length = strlen(ba_pwd.value);
291 maj = gss_import_name(&min, &ba_user, GSS_C_NT_USER_NAME, &client);
292 if (GSS_ERROR(maj)) {
293 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
295 mag_error(req, "gss_import_name() failed",
299 maj = gss_acquire_cred_with_password(&min, client, &ba_pwd,
303 &user_cred, NULL, NULL);
304 if (GSS_ERROR(maj)) {
305 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
307 mag_error(req, "gss_acquire_cred_with_password() "
308 "failed", maj, min));
311 gss_release_name(&min, &client);
316 req->ap_auth_type = apr_pstrdup(req->pool, auth_type);
318 #ifdef HAVE_GSS_ACQUIRE_CRED_FROM
319 if (cfg->use_s4u2proxy) {
320 cred_usage = GSS_C_BOTH;
322 if (cfg->cred_store) {
323 maj = gss_acquire_cred_from(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
324 GSS_C_NO_OID_SET, cred_usage,
325 cfg->cred_store, &acquired_cred,
327 if (GSS_ERROR(maj)) {
328 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
329 mag_error(req, "gss_acquire_cred_from() failed",
337 if (!acquired_cred) {
338 /* Try to acquire default creds */
339 maj = gss_acquire_cred(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
340 GSS_C_NO_OID_SET, cred_usage,
341 &acquired_cred, NULL, NULL);
342 if (GSS_ERROR(maj)) {
343 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
344 "%s", mag_error(req, "gss_acquire_cred_from()"
345 " failed", maj, min));
349 maj = gss_inquire_cred(&min, acquired_cred, &server,
351 if (GSS_ERROR(maj)) {
352 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
353 "%s", mag_error(req, "gss_inquired_cred_() "
354 "failed", maj, min));
357 /* output and input are inverted here, this is intentional */
358 maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
359 GSS_C_NO_OID, 0, 300,
360 GSS_C_NO_CHANNEL_BINDINGS, &output,
361 NULL, &input, NULL, NULL);
362 if (GSS_ERROR(maj)) {
363 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
364 "%s", mag_error(req, "gss_init_sec_context() "
365 "failed", maj, min));
370 maj = gss_accept_sec_context(&min, pctx, acquired_cred,
371 &input, GSS_C_NO_CHANNEL_BINDINGS,
372 &client, &mech_type, &output, &flags, &vtime,
374 if (GSS_ERROR(maj)) {
375 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
376 mag_error(req, "gss_accept_sec_context() failed",
381 while (maj == GSS_S_CONTINUE_NEEDED) {
382 gss_release_buffer(&min, &input);
383 /* output and input are inverted here, this is intentional */
384 maj = gss_init_sec_context(&min, user_cred, &user_ctx, server,
385 GSS_C_NO_OID, 0, 300,
386 GSS_C_NO_CHANNEL_BINDINGS, &output,
387 NULL, &input, NULL, NULL);
388 if (GSS_ERROR(maj)) {
389 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
390 "%s", mag_error(req, "gss_init_sec_context() "
391 "failed", maj, min));
394 gss_release_buffer(&min, &output);
395 maj = gss_accept_sec_context(&min, pctx, acquired_cred,
396 &input, GSS_C_NO_CHANNEL_BINDINGS,
397 &client, &mech_type, &output, &flags,
398 &vtime, &delegated_cred);
399 if (GSS_ERROR(maj)) {
400 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
401 "%s", mag_error(req, "gss_accept_sec_context()"
402 " failed", maj, min));
406 } else if (maj == GSS_S_CONTINUE_NEEDED) {
408 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
409 "Mechanism needs continuation but neither "
410 "GssapiConnectionBound nor "
411 "GssapiUseSessions are available");
412 gss_delete_sec_context(&min, pctx, GSS_C_NO_BUFFER);
413 gss_release_buffer(&min, &output);
416 /* auth not complete send token and wait next packet */
420 /* Always set the GSS name in an env var */
421 maj = gss_display_name(&min, client, &name, NULL);
422 if (GSS_ERROR(maj)) {
423 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
424 mag_error(req, "gss_display_name() failed",
428 clientname = apr_pstrndup(req->pool, name.value, name.length);
429 apr_table_set(req->subprocess_env, "GSS_NAME", clientname);
431 #ifdef HAVE_GSS_STORE_CRED_INTO
432 if (cfg->deleg_ccache_dir && delegated_cred != GSS_C_NO_CREDENTIAL) {
433 char *ccachefile = NULL;
435 mag_store_deleg_creds(req, cfg->deleg_ccache_dir, clientname,
436 delegated_cred, &ccachefile);
439 apr_table_set(req->subprocess_env, "KRB5CCNAME", ccachefile);
444 if (cfg->map_to_local) {
445 maj = gss_localname(&min, client, mech_type, &lname);
446 if (maj != GSS_S_COMPLETE) {
447 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s",
448 mag_error(req, "gss_localname() failed", maj, min));
451 req->user = apr_pstrndup(req->pool, lname.value, lname.length);
453 req->user = clientname;
457 mc->user_name = apr_pstrdup(mc->parent, req->user);
458 mc->gss_name = apr_pstrdup(mc->parent, clientname);
459 mc->established = true;
460 if (vtime == GSS_C_INDEFINITE || vtime < MIN_SESS_EXP_TIME) {
461 vtime = MIN_SESS_EXP_TIME;
463 mc->expiration = time(NULL) + vtime;
464 if (cfg->use_sessions) {
465 mag_attempt_session(req, cfg, mc);
467 mc->auth_type = auth_type;
473 if (ret == HTTP_UNAUTHORIZED) {
474 if (output.length != 0) {
475 replen = apr_base64_encode_len(output.length) + 1;
476 reply = apr_pcalloc(req->pool, 10 + replen);
478 memcpy(reply, "Negotiate ", 10);
479 apr_base64_encode(&reply[10], output.value, output.length);
480 apr_table_add(req->err_headers_out,
481 "WWW-Authenticate", reply);
484 apr_table_add(req->err_headers_out,
485 "WWW-Authenticate", "Negotiate");
486 if (cfg->basic_auth != BA_OFF) {
487 apr_table_add(req->err_headers_out,
489 apr_psprintf(req->pool, "Basic realm=\"%s\"",
494 gss_delete_sec_context(&min, &user_ctx, &output);
495 gss_release_cred(&min, &user_cred);
496 gss_release_cred(&min, &acquired_cred);
497 gss_release_cred(&min, &delegated_cred);
498 gss_release_buffer(&min, &output);
499 gss_release_name(&min, &client);
500 gss_release_name(&min, &server);
501 gss_release_buffer(&min, &name);
502 gss_release_buffer(&min, &lname);
507 static void *mag_create_dir_config(apr_pool_t *p, char *dir)
509 struct mag_config *cfg;
511 cfg = (struct mag_config *)apr_pcalloc(p, sizeof(struct mag_config));
512 if (!cfg) return NULL;
518 static const char *mag_ssl_only(cmd_parms *parms, void *mconfig, int on)
520 struct mag_config *cfg = (struct mag_config *)mconfig;
521 cfg->ssl_only = on ? true : false;
525 static const char *mag_map_to_local(cmd_parms *parms, void *mconfig, int on)
527 struct mag_config *cfg = (struct mag_config *)mconfig;
528 cfg->map_to_local = on ? true : false;
532 static const char *mag_conn_ctx(cmd_parms *parms, void *mconfig, int on)
534 struct mag_config *cfg = (struct mag_config *)mconfig;
535 cfg->gss_conn_ctx = on ? true : false;
539 static const char *mag_use_sess(cmd_parms *parms, void *mconfig, int on)
541 struct mag_config *cfg = (struct mag_config *)mconfig;
542 cfg->use_sessions = on ? true : false;
546 static const char *mag_use_s4u2p(cmd_parms *parms, void *mconfig, int on)
548 struct mag_config *cfg = (struct mag_config *)mconfig;
549 cfg->use_s4u2proxy = on ? true : false;
551 if (cfg->deleg_ccache_dir == NULL) {
552 cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, "/tmp");
553 if (!cfg->deleg_ccache_dir) {
554 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0,
555 parms->server, "%s", "OOM setting deleg_ccache_dir.");
561 static const char *mag_sess_key(cmd_parms *parms, void *mconfig, const char *w)
563 struct mag_config *cfg = (struct mag_config *)mconfig;
570 if (strncmp(w, "key:", 4) != 0) {
571 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
572 "Invalid key format, expected prefix 'key:'");
577 l = apr_base64_decode_len(k);
578 val = apr_palloc(parms->temp_pool, l);
580 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
581 "Failed to get memory to decode key");
585 keys.length = (int)apr_base64_decode_binary(val, k);
586 keys.value = (unsigned char *)val;
588 if (keys.length != 32) {
589 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
590 "Invalid key lenght, expected 32 got %d", keys.length);
594 rc = SEAL_KEY_CREATE(cfg->pool, &cfg->mag_skey, &keys);
596 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
597 "Failed to import sealing key!");
602 #define MAX_CRED_OPTIONS 10
604 static const char *mag_cred_store(cmd_parms *parms, void *mconfig,
607 struct mag_config *cfg = (struct mag_config *)mconfig;
608 gss_key_value_element_desc *elements;
617 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
618 "%s [%s]", "Invalid syntax for GssapiCredStore option", w);
622 key = apr_pstrndup(parms->pool, w, (p-w));
623 value = apr_pstrdup(parms->pool, p + 1);
624 if (!key || !value) {
625 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
626 "%s", "OOM handling GssapiCredStore option");
630 if (!cfg->cred_store) {
631 cfg->cred_store = apr_pcalloc(parms->pool,
632 sizeof(gss_key_value_set_desc));
633 if (!cfg->cred_store) {
634 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
635 "%s", "OOM handling GssapiCredStore option");
638 size = sizeof(gss_key_value_element_desc) * MAX_CRED_OPTIONS;
639 cfg->cred_store->elements = apr_palloc(parms->pool, size);
640 if (!cfg->cred_store->elements) {
641 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
642 "%s", "OOM handling GssapiCredStore option");
646 elements = cfg->cred_store->elements;
647 count = cfg->cred_store->count;
649 if (count >= MAX_CRED_OPTIONS) {
650 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
651 "Too many GssapiCredStore options (MAX: %d)",
655 cfg->cred_store->count++;
657 elements[count].key = key;
658 elements[count].value = value;
663 static const char *mag_deleg_ccache_dir(cmd_parms *parms, void *mconfig,
666 struct mag_config *cfg = (struct mag_config *)mconfig;
668 cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, value);
669 if (!cfg->deleg_ccache_dir) {
670 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, parms->server,
671 "%s", "OOM handling GssapiDelegCcacheDir option");
677 static const char *mag_use_basic_auth(cmd_parms *parms, void *mconfig,
680 struct mag_config *cfg = (struct mag_config *)mconfig;
682 if (strcasecmp(value, "on") == 0) {
683 cfg->basic_auth = BA_ON;
684 } else if (strcasecmp(value, "forward") == 0) {
685 cfg->basic_auth = BA_FORWARD;
687 cfg->basic_auth = BA_OFF;
693 static const command_rec mag_commands[] = {
694 AP_INIT_FLAG("GssapiSSLonly", mag_ssl_only, NULL, OR_AUTHCFG,
695 "Work only if connection is SSL Secured"),
696 AP_INIT_FLAG("GssapiLocalName", mag_map_to_local, NULL, OR_AUTHCFG,
697 "Translate principals to local names"),
698 AP_INIT_FLAG("GssapiConnectionBound", mag_conn_ctx, NULL, OR_AUTHCFG,
699 "Authentication is bound to the TCP connection"),
700 AP_INIT_FLAG("GssapiUseSessions", mag_use_sess, NULL, OR_AUTHCFG,
701 "Authentication uses mod_sessions to hold status"),
702 AP_INIT_RAW_ARGS("GssapiSessionKey", mag_sess_key, NULL, OR_AUTHCFG,
703 "Key Used to seal session data."),
704 #ifdef HAVE_GSS_ACQUIRE_CRED_FROM
705 AP_INIT_FLAG("GssapiUseS4U2Proxy", mag_use_s4u2p, NULL, OR_AUTHCFG,
706 "Initializes credentials for s4u2proxy usage"),
708 #ifdef HAVE_GSS_STORE_CRED_INTO
709 AP_INIT_ITERATE("GssapiCredStore", mag_cred_store, NULL, OR_AUTHCFG,
711 AP_INIT_RAW_ARGS("GssapiDelegCcacheDir", mag_deleg_ccache_dir, NULL,
712 OR_AUTHCFG, "Directory to store delegated credentials"),
714 #ifdef HAVE_GSS_ACQUIRE_CRED_WITH_PASSWORD
715 AP_INIT_TAKE1("GssapiBasicAuth", mag_use_basic_auth, NULL, OR_AUTHCFG,
716 "Allows use of Basic Auth for authentication"),
722 mag_register_hooks(apr_pool_t *p)
724 ap_hook_check_user_id(mag_auth, NULL, NULL, APR_HOOK_MIDDLE);
725 ap_hook_post_config(mag_post_config, NULL, NULL, APR_HOOK_MIDDLE);
726 ap_hook_pre_connection(mag_pre_connection, NULL, NULL, APR_HOOK_MIDDLE);
729 module AP_MODULE_DECLARE_DATA auth_gssapi_module =
731 STANDARD20_MODULE_STUFF,
732 mag_create_dir_config,