"Fossies" - the Fresh Open Source Software Archive

Member "mod_auth_gssapi-1.6.3/src/mod_auth_gssapi.c" (5 Aug 2020, 66440 Bytes) of package /linux/www/apache_httpd_modules/mod_auth_gssapi-1.6.3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "mod_auth_gssapi.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.6.2_vs_1.6.3.

    1 /* Copyright (C) 2014, 2016, 2020 mod_auth_gssapi contributors
    2  * See COPYING for (C) terms */
    3 
    4 #include "mod_auth_gssapi.h"
    5 #include "mag_parse.h"
    6 
    7 #include <sys/stat.h>
    8 #include <sys/types.h>
    9 
   10 const gss_OID_desc gss_mech_spnego = {
   11     6, "\x2b\x06\x01\x05\x05\x02"
   12 };
   13 
   14 #ifdef HAVE_GSSAPI_GSSAPI_NTLMSSP_H
   15 const gss_OID_desc gss_mech_ntlmssp_desc = {
   16     GSS_NTLMSSP_OID_LENGTH, GSS_NTLMSSP_OID_STRING
   17 };
   18 gss_const_OID gss_mech_ntlmssp = &gss_mech_ntlmssp_desc;
   19 
   20 const gss_OID_set_desc gss_mech_set_ntlmssp_desc = {
   21     1, discard_const(&gss_mech_ntlmssp_desc)
   22 };
   23 gss_const_OID_set gss_mech_set_ntlmssp = &gss_mech_set_ntlmssp_desc;
   24 
   25 #else
   26 gss_OID gss_mech_ntlmssp = GSS_C_NO_OID;
   27 gss_OID_set gss_mech_set_ntlmssp = GSS_C_NO_OID_SET;
   28 #endif
   29 
   30 #define MOD_AUTH_GSSAPI_VERSION PACKAGE_NAME "/" PACKAGE_VERSION
   31 
   32 module AP_MODULE_DECLARE_DATA auth_gssapi_module;
   33 
   34 APLOG_USE_MODULE(auth_gssapi);
   35 
   36 static char *mag_status(apr_pool_t *pool, int type, uint32_t err)
   37 {
   38     uint32_t maj_ret, min_ret;
   39     gss_buffer_desc text;
   40     uint32_t msg_ctx;
   41     char *msg_ret;
   42     int len;
   43 
   44     msg_ret = NULL;
   45     msg_ctx = 0;
   46     do {
   47         maj_ret = gss_display_status(&min_ret, err, type,
   48                                      GSS_C_NO_OID, &msg_ctx, &text);
   49         if (maj_ret != GSS_S_COMPLETE) {
   50             return msg_ret;
   51         }
   52 
   53         len = text.length;
   54         if (msg_ret) {
   55             msg_ret = apr_psprintf(pool, "%s, %*s",
   56                                    msg_ret, len, (char *)text.value);
   57         } else {
   58             msg_ret = apr_psprintf(pool, "%*s", len, (char *)text.value);
   59         }
   60         gss_release_buffer(&min_ret, &text);
   61     } while (msg_ctx != 0);
   62 
   63     return msg_ret;
   64 }
   65 
   66 char *mag_error(apr_pool_t *pool, const char *msg, uint32_t maj, uint32_t min)
   67 {
   68     char *msg_maj;
   69     char *msg_min;
   70 
   71     msg_maj = mag_status(pool, GSS_C_GSS_CODE, maj);
   72     msg_min = mag_status(pool, GSS_C_MECH_CODE, min);
   73     return apr_psprintf(pool, "%s: [%s (%s)]", msg, msg_maj, msg_min);
   74 }
   75 
   76 enum mag_err_code {
   77     MAG_NO_AUTH = 1,
   78     MAG_GSS_ERR,
   79     MAG_INTERNAL,
   80     MAG_AUTH_NOT_ALLOWED
   81 };
   82 
   83 static const char *mag_err_text(enum mag_err_code err)
   84 {
   85     switch (err) {
   86     case MAG_NO_AUTH:
   87         return "NO AUTH DATA";
   88     case MAG_GSS_ERR:
   89         return "GSS ERROR";
   90     case MAG_INTERNAL:
   91         return "INTERNAL ERROR";
   92     case MAG_AUTH_NOT_ALLOWED:
   93         return "AUTH NOT ALLOWED";
   94     default:
   95         return "INVALID ERROR CODE";
   96     }
   97 }
   98 
   99 static void mag_post_info(request_rec *req, struct mag_config *cfg,
  100                           enum mag_err_code err, const char *msg)
  101 {
  102     if (cfg->enverrs) {
  103         mag_publish_error(req, 0, 0, msg, mag_err_text(err));
  104     }
  105 
  106     ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, req, "%s %s", mag_err_text(err),
  107                   msg);
  108 }
  109 
  110 static void mag_post_error(request_rec *req, struct mag_config *cfg,
  111                            enum mag_err_code err, uint32_t maj, uint32_t min,
  112                            const char *msg)
  113 {
  114     const char *text = NULL;
  115 
  116     if (maj)
  117         text = mag_error(req->pool, msg, maj, min);
  118 
  119     if (cfg->enverrs)
  120         mag_publish_error(req, maj, min, text ? text : msg, mag_err_text(err));
  121 
  122     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s %s", mag_err_text(err),
  123                   text ? text : msg);
  124 }
  125 
  126 static APR_OPTIONAL_FN_TYPE(ssl_is_https) *mag_is_https = NULL;
  127 
  128 static int mag_post_config(apr_pool_t *cfgpool, apr_pool_t *log,
  129                            apr_pool_t *temp, server_rec *s)
  130 {
  131     /* FIXME: create mutex to deal with connections and contexts ? */
  132     mag_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
  133     mag_post_config_session();
  134     ap_add_version_component(cfgpool, MOD_AUTH_GSSAPI_VERSION);
  135 
  136     return OK;
  137 }
  138 
  139 static int mag_pre_connection(conn_rec *c, void *csd)
  140 {
  141     struct mag_conn *mc;
  142 
  143     mc = mag_new_conn_ctx(c->pool);
  144     mc->is_preserved = true;
  145     ap_set_module_config(c->conn_config, &auth_gssapi_module, (void*)mc);
  146     return OK;
  147 }
  148 
  149 static apr_status_t mag_conn_destroy(void *ptr)
  150 {
  151     struct mag_conn *mc = (struct mag_conn *)ptr;
  152     uint32_t min;
  153 
  154     if (mc->ctx) {
  155         (void)gss_delete_sec_context(&min, &mc->ctx, GSS_C_NO_BUFFER);
  156     }
  157     return APR_SUCCESS;
  158 }
  159 
  160 struct mag_conn *mag_new_conn_ctx(apr_pool_t *pool)
  161 {
  162     struct mag_conn *mc;
  163 
  164     mc = apr_pcalloc(pool, sizeof(struct mag_conn));
  165 
  166     apr_pool_create(&mc->pool, pool);
  167     mc->env = apr_table_make(mc->pool, 1);
  168 
  169     /* register the context in the memory pool, so it can be freed
  170      * when the connection/request is terminated */
  171     apr_pool_cleanup_register(mc->pool, (void *)mc,
  172                               mag_conn_destroy, apr_pool_cleanup_null);
  173     return mc;
  174 }
  175 
  176 static void mag_conn_clear(struct mag_conn *mc)
  177 {
  178     (void)mag_conn_destroy(mc);
  179     apr_pool_t *temp;
  180 
  181     apr_pool_clear(mc->pool);
  182     temp = mc->pool;
  183     memset(mc, 0, sizeof(struct mag_conn));
  184     mc->pool = temp;
  185     mc->env = apr_table_make(mc->pool, 1);
  186 }
  187 
  188 static bool mag_conn_is_https(conn_rec *c)
  189 {
  190     if (mag_is_https) {
  191         if (mag_is_https(c)) return true;
  192     }
  193 
  194     return false;
  195 }
  196 
  197 static bool mag_acquire_creds(request_rec *req,
  198                               struct mag_config *cfg,
  199                               gss_OID_set desired_mechs,
  200                               gss_cred_usage_t cred_usage,
  201                               gss_cred_id_t *creds,
  202                               gss_OID_set *actual_mechs)
  203 {
  204     gss_name_t acceptor_name = GSS_C_NO_NAME;
  205     uint32_t maj, min;
  206     bool ret;
  207 
  208     if (cfg->acceptor_name_from_req) {
  209         gss_buffer_desc bufnam;
  210 
  211         bufnam.value = apr_psprintf(req->pool, "HTTP@%s", req->hostname);
  212         bufnam.length = strlen(bufnam.value);
  213 
  214         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req, "GSS Server Name: %s",
  215                       (char *)bufnam.value);
  216 
  217         maj = gss_import_name(&min, &bufnam, GSS_C_NT_HOSTBASED_SERVICE,
  218                               &acceptor_name);
  219         if (GSS_ERROR(maj)) {
  220             mag_post_error(req, cfg, MAG_GSS_ERR, maj, min,
  221                            "gss_import_name() failed to import hostnname");
  222             return false;
  223         }
  224     } else {
  225         acceptor_name = cfg->acceptor_name;
  226     }
  227 
  228 #ifdef HAVE_CRED_STORE
  229     gss_const_key_value_set_t store = cfg->cred_store;
  230 
  231     maj = gss_acquire_cred_from(&min, acceptor_name, GSS_C_INDEFINITE,
  232                                 desired_mechs, cred_usage, store, creds,
  233                                 actual_mechs, NULL);
  234 #else
  235     maj = gss_acquire_cred(&min, acceptor_name, GSS_C_INDEFINITE,
  236                            desired_mechs, cred_usage, creds,
  237                            actual_mechs, NULL);
  238 #endif
  239 
  240     if (GSS_ERROR(maj)) {
  241         mag_post_error(req, cfg, MAG_GSS_ERR, maj, min,
  242                        "gss_acquire_cred[_from]() failed to get server creds");
  243         ret = false;
  244     } else {
  245         ret = true;
  246     }
  247 
  248     if (cfg->acceptor_name_from_req) {
  249         gss_release_name(&min, &acceptor_name);
  250     }
  251     return ret;
  252 }
  253 
  254 #ifdef HAVE_CRED_STORE
  255 static char *escape(apr_pool_t *pool, const char *name,
  256                     char find, const char *replace)
  257 {
  258     char *escaped = NULL;
  259     char *namecopy;
  260     char *n;
  261     char *p;
  262 
  263     namecopy = apr_pstrdup(pool, name);
  264 
  265     p = strchr(namecopy, find);
  266     if (!p) return namecopy;
  267 
  268     /* first segment */
  269     n = namecopy;
  270     while (p) {
  271         /* terminate previous segment */
  272         *p = '\0';
  273         if (escaped) {
  274             escaped = apr_pstrcat(pool, escaped, n, replace, NULL);
  275         } else {
  276             escaped = apr_pstrcat(pool, n, replace, NULL);
  277         }
  278         /* move to next segment */
  279         n = p + 1;
  280         p = strchr(n, find);
  281     }
  282     /* append last segment if any */
  283     if (*n) {
  284         escaped = apr_pstrcat(pool, escaped, n, NULL);
  285     }
  286 
  287     return escaped;
  288 }
  289 
  290 static char *get_ccache_name(request_rec *req, char *dir, const char *gss_name,
  291                              bool use_unique, struct mag_conn *mc)
  292 {
  293     char *ccname, *escaped;
  294     int ccachefd;
  295     mode_t umask_save;
  296 
  297     /* We need to escape away '/', we can't have path separators in
  298      * a ccache file name */
  299     /* first double escape the esacping char (~) if any */
  300     escaped = escape(req->pool, gss_name, '~', "~~");
  301     /* then escape away the separator (/) if any */
  302     escaped = escape(req->pool, escaped, '/', "~");
  303 
  304     if (use_unique == false) {
  305         return apr_psprintf(mc->pool, "%s/%s", dir, escaped);
  306     }
  307 
  308     ccname = apr_psprintf(mc->pool, "%s/%s-XXXXXX", dir, escaped);
  309 
  310     umask_save = umask(0177);
  311     ccachefd = mkstemp(ccname);
  312     umask(umask_save);
  313 
  314     if (ccachefd == -1) {
  315         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
  316                       "creating unique ccache file %s failed", ccname);
  317         return NULL;
  318     }
  319     close(ccachefd);
  320     return ccname;
  321 }
  322 
  323 static void mag_store_deleg_creds(request_rec *req, const char *ccname,
  324                                   gss_cred_id_t delegated_cred)
  325 {
  326     gss_key_value_element_desc element;
  327     gss_key_value_set_desc store;
  328     uint32_t maj, min;
  329     element.key = "ccache";
  330     store.elements = &element;
  331     store.count = 1;
  332 
  333     element.value = apr_psprintf(req->pool, "FILE:%s", ccname);
  334 
  335     maj = gss_store_cred_into(&min, delegated_cred, GSS_C_INITIATE,
  336                               GSS_C_NULL_OID, 1, 1, &store, NULL, NULL);
  337     if (GSS_ERROR(maj)) {
  338         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s",
  339                       mag_error(req->pool, "failed to store delegated creds",
  340                                 maj, min));
  341     }
  342 }
  343 #endif
  344 
  345 static bool parse_auth_header(apr_pool_t *pool, const char **auth_header,
  346                               gss_buffer_t value)
  347 {
  348     char *auth_header_value;
  349 
  350     auth_header_value = ap_getword_white(pool, auth_header);
  351     if (!auth_header_value) return false;
  352     value->length = apr_base64_decode_len(auth_header_value) + 1;
  353     value->value = apr_pcalloc(pool, value->length);
  354     if (!value->value) return false;
  355     value->length = apr_base64_decode(value->value, auth_header_value);
  356 
  357     return true;
  358 }
  359 
  360 static bool is_mech_allowed(gss_OID_set allowed_mechs, gss_const_OID mech,
  361                             bool multi_step_supported)
  362 {
  363     if (mech == GSS_C_NO_OID) return false;
  364 
  365     if (!multi_step_supported && gss_oid_equal(gss_mech_ntlmssp, mech))
  366         return false;
  367 
  368     if (allowed_mechs == GSS_C_NO_OID_SET) return true;
  369 
  370     for (int i = 0; i < allowed_mechs->count; i++) {
  371         if (gss_oid_equal(&allowed_mechs->elements[i], mech)) {
  372             return true;
  373         }
  374     }
  375     return false;
  376 }
  377 
  378 #define AUTH_TYPE_NEGOTIATE 0
  379 #define AUTH_TYPE_BASIC 1
  380 #define AUTH_TYPE_RAW_NTLM 2
  381 #define AUTH_TYPE_IMPERSONATE 3
  382 const char *auth_types[] = {
  383     "Negotiate",
  384     "Basic",
  385     "NTLM",
  386     "Impersonate",
  387     NULL
  388 };
  389 
  390 const char *mag_str_auth_type(int auth_type)
  391 {
  392     return auth_types[auth_type];
  393 }
  394 
  395 gss_OID_set mag_filter_unwanted_mechs(gss_OID_set src)
  396 {
  397     gss_const_OID unwanted_mechs[] = {
  398         &gss_mech_spnego,
  399         gss_mech_krb5_old,
  400         gss_mech_krb5_wrong,
  401         gss_mech_iakerb,
  402         GSS_C_NO_OID
  403     };
  404     gss_OID_set dst;
  405     uint32_t maj, min;
  406     int present = 0;
  407 
  408     if (src == GSS_C_NO_OID_SET) return GSS_C_NO_OID_SET;
  409 
  410     for (int i = 0; unwanted_mechs[i] != GSS_C_NO_OID; i++) {
  411         maj = gss_test_oid_set_member(&min,
  412                                       discard_const(unwanted_mechs[i]),
  413                                       src, &present);
  414         if (present) break;
  415     }
  416     if (present) {
  417         maj = gss_create_empty_oid_set(&min, &dst);
  418         if (maj != GSS_S_COMPLETE) {
  419             return GSS_C_NO_OID_SET;
  420         }
  421         for (int i = 0; i < src->count; i++) {
  422             present = 0;
  423             for (int j = 0; unwanted_mechs[j] != GSS_C_NO_OID; j++) {
  424                 if (gss_oid_equal(&src->elements[i], unwanted_mechs[j])) {
  425                     present = 1;
  426                     break;
  427                 }
  428             }
  429             if (present) continue;
  430             maj = gss_add_oid_set_member(&min, &src->elements[i], &dst);
  431             if (maj != GSS_S_COMPLETE) {
  432                 gss_release_oid_set(&min, &dst);
  433                 return GSS_C_NO_OID_SET;
  434             }
  435         }
  436         return dst;
  437     }
  438     return src;
  439 }
  440 
  441 static uint32_t mag_context_loop(uint32_t *min,
  442                                  request_rec *req,
  443                                  struct mag_config *cfg,
  444                                  gss_cred_id_t init_cred,
  445                                  gss_cred_id_t accept_cred,
  446                                  gss_OID mech_type,
  447                                  uint32_t req_lifetime,
  448                                  gss_name_t *client,
  449                                  uint32_t *lifetime,
  450                                  gss_cred_id_t *delegated_cred)
  451 {
  452     gss_ctx_id_t init_ctx = GSS_C_NO_CONTEXT;
  453     gss_ctx_id_t accept_ctx = GSS_C_NO_CONTEXT;
  454     gss_buffer_desc init_token = GSS_C_EMPTY_BUFFER;
  455     gss_buffer_desc accept_token = GSS_C_EMPTY_BUFFER;
  456     gss_name_t accept_name = GSS_C_NO_NAME;
  457     uint32_t maj, tmin;
  458 
  459     maj = gss_inquire_cred_by_mech(min, accept_cred, mech_type, &accept_name,
  460                                    NULL, NULL, NULL);
  461     if (GSS_ERROR(maj)) {
  462         mag_post_error(req, cfg, MAG_GSS_ERR, maj, *min,
  463                        "gss_inquired_cred_by_mech() failed");
  464         return maj;
  465     }
  466 
  467     do {
  468         /* output and input are inverted here, this is intentional */
  469         maj = gss_init_sec_context(min, init_cred, &init_ctx,
  470                                    accept_name, mech_type, GSS_C_DELEG_FLAG,
  471                                    req_lifetime, GSS_C_NO_CHANNEL_BINDINGS,
  472                                    &accept_token, NULL, &init_token, NULL,
  473                                    NULL);
  474         if (GSS_ERROR(maj)) {
  475             mag_post_error(req, cfg, MAG_GSS_ERR, maj, *min,
  476                            "gss_init_sec_context()");
  477             goto done;
  478         }
  479         gss_release_buffer(&tmin, &accept_token);
  480 
  481         maj = gss_accept_sec_context(min, &accept_ctx, accept_cred,
  482                                      &init_token, GSS_C_NO_CHANNEL_BINDINGS,
  483                                      client, NULL, &accept_token, NULL,
  484                                      lifetime, delegated_cred);
  485         if (GSS_ERROR(maj)) {
  486             mag_post_error(req, cfg, MAG_GSS_ERR, maj, *min,
  487                            "gss_accept_sec_context()");
  488             goto done;
  489         }
  490         gss_release_buffer(&tmin, &init_token);
  491     } while (maj == GSS_S_CONTINUE_NEEDED);
  492 
  493 done:
  494     gss_release_name(&tmin, &accept_name);
  495     gss_release_buffer(&tmin, &init_token);
  496     gss_release_buffer(&tmin, &accept_token);
  497     gss_delete_sec_context(&tmin, &init_ctx, GSS_C_NO_BUFFER);
  498     gss_delete_sec_context(&tmin, &accept_ctx, GSS_C_NO_BUFFER);
  499     return maj;
  500 }
  501 
  502 static int mag_complete(struct mag_req_cfg *req_cfg, struct mag_conn *mc,
  503                         gss_name_t client, gss_OID mech_type,
  504                         uint32_t vtime, gss_cred_id_t delegated_cred);
  505 
  506 static int mag_auth_basic(struct mag_req_cfg *req_cfg, struct mag_conn *mc,
  507                           gss_buffer_desc ba_user, gss_buffer_desc ba_pwd)
  508 {
  509     struct mag_config *cfg = req_cfg->cfg;
  510     request_rec *req = req_cfg->req;
  511     const char *user_ccache = NULL;
  512     const char *orig_ccache = NULL;
  513     long long unsigned int rndname;
  514     apr_status_t rs;
  515     gss_name_t user = GSS_C_NO_NAME;
  516     gss_cred_id_t user_cred = GSS_C_NO_CREDENTIAL;
  517     gss_cred_id_t server_cred = GSS_C_NO_CREDENTIAL;
  518     gss_OID_set allowed_mechs;
  519     gss_OID_set filtered_mechs;
  520     gss_OID_set actual_mechs = GSS_C_NO_OID_SET;
  521     gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
  522     gss_name_t client = GSS_C_NO_NAME;
  523     uint32_t vtime;
  524     uint32_t maj, min;
  525     int present = 0;
  526     int ret = HTTP_UNAUTHORIZED;
  527 
  528     maj = gss_import_name(&min, &ba_user, GSS_C_NT_USER_NAME, &user);
  529     if (GSS_ERROR(maj)) {
  530         mag_post_error(req, cfg, MAG_GSS_ERR, maj, min,
  531                        "In Basic Auth: gss_import_name() failed");
  532         goto done;
  533     }
  534 
  535     if (cfg->basic_mechs) {
  536         allowed_mechs = cfg->basic_mechs;
  537     } else if (cfg->allowed_mechs) {
  538         allowed_mechs = cfg->allowed_mechs;
  539     } else {
  540         struct mag_server_config *scfg;
  541         /* Try to fetch the default set if not explicitly configured,
  542          * We need to do this because gss_acquire_cred_with_password()
  543          * is currently limited to acquire creds for a single "default"
  544          * mechanism if no desired mechanisms are passed in. This causes
  545          * authentication to fail for secondary mechanisms as no user
  546          * credentials are generated for those. */
  547         scfg = ap_get_module_config(req->server->module_config,
  548                                     &auth_gssapi_module);
  549         /* In the worst case scenario default_mechs equals to GSS_C_NO_OID_SET.
  550          * This generally causes only the krb5 mechanism to be tried due
  551          * to implementation constraints, but may change in future. */
  552         allowed_mechs = scfg->default_mechs;
  553     }
  554 
  555     /* Remove Spnego if present, or we'd repeat failed authentiations
  556      * multiple times, one within Spnego and then again with an explicit
  557      * mechanism. We would normally just force Spnego and use
  558      * gss_set_neg_mechs, but due to the way we source the server name
  559      * and the fact MIT up to 1.14 at least does no handle union names,
  560      * we can't provide spnego with a server name that can be used by
  561      * multiple mechanisms, causing any but the first mechanism to fail.
  562      * Also remove unwanted krb mechs, or AS requests will be repeated
  563      * multiple times uselessly.
  564      */
  565     filtered_mechs = mag_filter_unwanted_mechs(allowed_mechs);
  566     if (filtered_mechs == allowed_mechs) {
  567         /* in case filtered_mechs was not allocated here don't free it */
  568         filtered_mechs = GSS_C_NO_OID_SET;
  569     } else if (filtered_mechs == GSS_C_NO_OID_SET) {
  570         mag_post_error(req, cfg, MAG_INTERNAL, 0, 0,
  571                        "Fatal failure while filtering mechs, aborting");
  572         goto done;
  573     } else {
  574         /* use the filtered list */
  575         allowed_mechs = filtered_mechs;
  576     }
  577 
  578     /* If we are using the krb5 mechanism make sure to set a per thread
  579      * memory ccache so that there can't be interferences between threads.
  580      * Also make sure we have  new cache so no cached results end up being
  581      * used. Some implementations of gss_acquire_cred_with_password() do
  582      * not reacquire creds if cached ones are around, failing to check
  583      * again for the password. */
  584     maj = gss_test_oid_set_member(&min, discard_const(gss_mech_krb5),
  585                                   allowed_mechs, &present);
  586     if (GSS_ERROR(maj)) {
  587         mag_post_error(req, cfg, MAG_GSS_ERR, maj, min,
  588                        "In Basic Auth: gss_test_oid_set_member() failed");
  589         goto done;
  590     }
  591     if (present) {
  592         rs = apr_generate_random_bytes((unsigned char *)(&rndname),
  593                                        sizeof(long long unsigned int));
  594         if (rs != APR_SUCCESS) {
  595             mag_post_error(req, cfg, MAG_INTERNAL, 0, 0,
  596                            "Failed to generate random ccache name");
  597             goto done;
  598         }
  599         user_ccache = apr_psprintf(req->pool, "MEMORY:user_%qu", rndname);
  600         maj = gss_krb5_ccache_name(&min, user_ccache, &orig_ccache);
  601         if (GSS_ERROR(maj)) {
  602             mag_post_error(req, cfg, MAG_GSS_ERR, maj, min,
  603                           "In Basic Auth: gss_krb5_ccache_name() failed");
  604             goto done;
  605         }
  606     }
  607 
  608     maj = gss_acquire_cred_with_password(&min, user, &ba_pwd,
  609                                          cfg->basic_timeout,
  610                                          allowed_mechs,
  611                                          GSS_C_INITIATE,
  612                                          &user_cred, &actual_mechs, NULL);
  613     if (GSS_ERROR(maj)) {
  614             mag_post_error(req, cfg, MAG_GSS_ERR, maj, min,
  615                            "In Basic Auth: gss_acquire_cred_with_password() "
  616                            "failed");
  617         goto done;
  618     }
  619 
  620     /* must acquire creds based on the actual mechs we want to try */
  621     if (!mag_acquire_creds(req, cfg, actual_mechs,
  622                            GSS_C_ACCEPT, &server_cred, NULL)) {
  623         goto done;
  624     }
  625 
  626     for (int i = 0; i < actual_mechs->count; i++) {
  627         maj = mag_context_loop(&min, req, cfg, user_cred, server_cred,
  628                                &actual_mechs->elements[i], cfg->basic_timeout,
  629                                &client, &vtime, &delegated_cred);
  630         if (maj == GSS_S_COMPLETE) {
  631             ret = mag_complete(req_cfg, mc, client, &actual_mechs->elements[i],
  632                                vtime, delegated_cred);
  633             if (ret == OK) {
  634                 mag_basic_cache(req_cfg, mc, ba_user, ba_pwd);
  635             }
  636             break;
  637         }
  638     }
  639 
  640 done:
  641     gss_release_cred(&min, &delegated_cred);
  642     gss_release_name(&min, &client);
  643     gss_release_cred(&min, &server_cred);
  644     gss_release_name(&min, &user);
  645     gss_release_cred(&min, &user_cred);
  646     gss_release_oid_set(&min, &actual_mechs);
  647     gss_release_oid_set(&min, &filtered_mechs);
  648 
  649     if (user_ccache != NULL) {
  650         maj = gss_krb5_ccache_name(&min, orig_ccache, NULL);
  651         if (maj != GSS_S_COMPLETE) {
  652             ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, req,
  653                           "Failed to restore per-thread ccache, %s",
  654                           mag_error(req->pool, "gss_krb5_ccache_name() "
  655                                     "failed", maj, min));
  656         }
  657     }
  658 
  659     return ret;
  660 }
  661 
  662 struct mag_req_cfg *mag_init_cfg(request_rec *req)
  663 {
  664     struct mag_server_config *scfg;
  665     struct mag_req_cfg *req_cfg = apr_pcalloc(req->pool,
  666                                               sizeof(struct mag_req_cfg));
  667     req_cfg->req = req;
  668     req_cfg->cfg = ap_get_module_config(req->per_dir_config,
  669                                         &auth_gssapi_module);
  670 
  671     scfg = ap_get_module_config(req->server->module_config,
  672                                 &auth_gssapi_module);
  673 
  674     if (req_cfg->cfg->allowed_mechs) {
  675         req_cfg->desired_mechs = req_cfg->cfg->allowed_mechs;
  676     } else {
  677         /* Use the default set if not explicitly configured */
  678         req_cfg->desired_mechs = scfg->default_mechs;
  679     }
  680 
  681     if (req_cfg->cfg->mag_skey) {
  682         req_cfg->mag_skey = req_cfg->cfg->mag_skey;
  683     } else {
  684         /* Use server random key if not explicitly configured */
  685         req_cfg->mag_skey = scfg->mag_skey;
  686     }
  687 
  688     if (req->proxyreq == PROXYREQ_PROXY) {
  689         req_cfg->req_proto = "Proxy-Authorization";
  690         req_cfg->rep_proto = "Proxy-Authenticate";
  691     } else {
  692         req_cfg->req_proto = "Authorization";
  693         req_cfg->rep_proto = "WWW-Authenticate";
  694         req_cfg->use_sessions = req_cfg->cfg->use_sessions;
  695         req_cfg->send_persist = req_cfg->cfg->send_persist;
  696     }
  697 
  698     return req_cfg;
  699 }
  700 
  701 #ifdef HAVE_CRED_STORE
  702 static bool use_s4u2proxy(struct mag_req_cfg *req_cfg) {
  703     if (req_cfg->cfg->use_s4u2proxy) {
  704         if (req_cfg->cfg->deleg_ccache_dir != NULL) {
  705             return true;
  706         } else {
  707             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req_cfg->req,
  708                           "S4U2 Proxy requested but GssapiDelegCcacheDir "
  709                           "is not set. Constrained delegation disabled!");
  710         }
  711     }
  712     return false;
  713 }
  714 
  715 static apr_status_t mag_s4u2self(request_rec *req)
  716 {
  717     apr_status_t ret = DECLINED;
  718     const char *type;
  719     struct mag_config *cfg;
  720     struct mag_req_cfg *req_cfg;
  721     gss_OID mech_type = discard_const(gss_mech_krb5);
  722     gss_OID_set_desc gss_mech_krb5_set = { 1, mech_type };
  723     gss_buffer_desc user_name = GSS_C_EMPTY_BUFFER;
  724     gss_cred_id_t user_cred = GSS_C_NO_CREDENTIAL;
  725     gss_name_t user = GSS_C_NO_NAME;
  726     gss_name_t client = GSS_C_NO_NAME;
  727     gss_cred_id_t server_cred = GSS_C_NO_CREDENTIAL;
  728     gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
  729     struct mag_conn *mc = NULL;
  730     uint32_t vtime;
  731     uint32_t maj, min;
  732 
  733     req_cfg = mag_init_cfg(req);
  734     cfg = req_cfg->cfg;
  735 
  736     if (!cfg->s4u2self) {
  737         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
  738                       "GSSapiImpersonate not On, skipping impersonation.");
  739         return DECLINED;
  740     }
  741 
  742     type = ap_auth_type(req);
  743     if (type && (strcasecmp(type, "GSSAPI") == 0)) {
  744         /* do not try to impersonate if GSSAPI is handling real auth */
  745         return DECLINED;
  746     }
  747 
  748     if (!req->user) {
  749         ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, req,
  750                       "Authentication user not found, "
  751                       "skipping impersonation.");
  752         return DECLINED;
  753     }
  754 
  755     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
  756                   "Using user %s for impersonation.", req->user);
  757 
  758     if (!mag_acquire_creds(req, cfg, &gss_mech_krb5_set,
  759                            GSS_C_BOTH, &server_cred, NULL)) {
  760         goto done;
  761     }
  762 
  763     user_name.value = req->user;
  764     user_name.length = strlen(user_name.value);
  765     maj = gss_import_name(&min, &user_name, GSS_C_NT_USER_NAME, &user);
  766     if (GSS_ERROR(maj)) {
  767         mag_post_error(req, cfg, MAG_GSS_ERR, maj, min,
  768                        "In S4U2Self: gss_import_name()");
  769         goto done;
  770     }
  771 
  772     maj = gss_acquire_cred_impersonate_name(&min, server_cred, user,
  773                                             GSS_C_INDEFINITE,
  774                                             &gss_mech_krb5_set,
  775                                             GSS_C_INITIATE, &user_cred,
  776                                             NULL, NULL);
  777     if (GSS_ERROR(maj)) {
  778         mag_post_error(req, cfg, MAG_GSS_ERR, maj, min,
  779                        "In S4U2Self: gss_acquire_cred_impersonate_name()");
  780         goto done;
  781     }
  782 
  783     /* the following exchange is needed to decrypt the ticket and get named
  784      * attributes as well as check if the ticket is forwardable when
  785      * delegated credentials are requested */
  786     maj = mag_context_loop(&min, req, cfg, user_cred, server_cred,
  787                            discard_const(gss_mech_krb5), GSS_C_INDEFINITE,
  788                            &client, &vtime, &delegated_cred);
  789     if (GSS_ERROR(maj))
  790         goto done;
  791 
  792     if (cfg->deleg_ccache_dir && delegated_cred == GSS_C_NO_CREDENTIAL) {
  793         mag_post_error(req, cfg, MAG_INTERNAL, 0, 0,
  794                        "Failed to obtain delegated credentials, "
  795                        "does service have +ok_to_auth_as_delegate?");
  796         goto done;
  797     }
  798 
  799     mc = mag_new_conn_ctx(req->pool);
  800     mc->auth_type = AUTH_TYPE_IMPERSONATE;
  801 
  802     ret = mag_complete(req_cfg, mc, client, mech_type, vtime, delegated_cred);
  803     if (ret != OK) ret = DECLINED;
  804 
  805 done:
  806     gss_release_cred(&min, &user_cred);
  807     gss_release_name(&min, &user);
  808     gss_release_name(&min, &client);
  809     gss_release_cred(&min, &server_cred);
  810     gss_release_cred(&min, &delegated_cred);
  811     return ret;
  812 }
  813 #endif
  814 
  815 static apr_status_t mag_oid_set_destroy(void *ptr)
  816 {
  817     uint32_t min;
  818     gss_OID_set set = (gss_OID_set)ptr;
  819     (void)gss_release_oid_set(&min, &set);
  820     return APR_SUCCESS;
  821 }
  822 
  823 static gss_OID_set mag_get_negotiate_mechs(apr_pool_t *p, gss_OID_set desired)
  824 {
  825     gss_OID spnego_oid = discard_const(&gss_mech_spnego);
  826     uint32_t maj, min;
  827     int present = 0;
  828 
  829     maj = gss_test_oid_set_member(&min, spnego_oid, desired, &present);
  830     if (maj != GSS_S_COMPLETE) {
  831         return GSS_C_NO_OID_SET;
  832     }
  833     if (present) {
  834         return desired;
  835     } else {
  836         gss_OID_set set;
  837         maj = gss_create_empty_oid_set(&min, &set);
  838         if (maj != GSS_S_COMPLETE) {
  839             return GSS_C_NO_OID_SET;
  840         }
  841         apr_pool_cleanup_register(p, (void *)set,
  842                                   mag_oid_set_destroy,
  843                                   apr_pool_cleanup_null);
  844         maj = gss_add_oid_set_member(&min, spnego_oid, &set);
  845         if (maj != GSS_S_COMPLETE) {
  846             return GSS_C_NO_OID_SET;
  847         }
  848         for (int i = 0; i < desired->count; i++) {
  849              maj = gss_add_oid_set_member(&min, &desired->elements[i], &set);
  850             if (maj != GSS_S_COMPLETE) {
  851                 return GSS_C_NO_OID_SET;
  852             }
  853         }
  854         return set;
  855     }
  856 }
  857 
  858 static int mag_auth(request_rec *req)
  859 {
  860     const char *type;
  861     int auth_type = -1;
  862     struct mag_req_cfg *req_cfg;
  863     struct mag_config *cfg;
  864     const char *auth_header;
  865     char *auth_header_type;
  866     int ret = HTTP_UNAUTHORIZED;
  867     gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
  868     gss_ctx_id_t *pctx;
  869     gss_buffer_desc input = GSS_C_EMPTY_BUFFER;
  870     gss_buffer_desc output = GSS_C_EMPTY_BUFFER;
  871     gss_buffer_desc ba_user;
  872     gss_buffer_desc ba_pwd;
  873     gss_name_t client = GSS_C_NO_NAME;
  874     gss_cred_id_t acquired_cred = GSS_C_NO_CREDENTIAL;
  875     gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
  876     gss_cred_usage_t cred_usage = GSS_C_ACCEPT;
  877     uint32_t vtime;
  878     uint32_t maj, min;
  879     char *reply;
  880     size_t replen;
  881     gss_OID mech_type = GSS_C_NO_OID;
  882     gss_OID_set desired_mechs = GSS_C_NO_OID_SET;
  883     struct mag_conn *mc = NULL;
  884     int i;
  885     bool send_nego_header = true;
  886 
  887     type = ap_auth_type(req);
  888     if ((type == NULL) || (strcasecmp(type, "GSSAPI") != 0)) {
  889         return DECLINED;
  890     }
  891 
  892     req_cfg = mag_init_cfg(req);
  893 
  894     cfg = req_cfg->cfg;
  895 
  896     if ((req_cfg->desired_mechs == GSS_C_NO_OID_SET) ||
  897         (req_cfg->desired_mechs->count == 0)) {
  898         mag_post_error(req, cfg, MAG_INTERNAL, 0, 0,
  899                        "List of desired mechs is missing or empty, "
  900                        "can't proceed!");
  901         return HTTP_UNAUTHORIZED;
  902     }
  903 
  904     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
  905                   "URI: %s, %s main, %s prev", req->uri ?: "no-uri",
  906                   req->main ? "with" : "no", req->prev ? "with" : "no");
  907 
  908     /* implicit auth for subrequests if main auth already happened */
  909     if (!ap_is_initial_req(req)) {
  910         request_rec *main_req = req;
  911 
  912         /* Not initial means either a subrequest or an internal redirect */
  913         while (!ap_is_initial_req(main_req))
  914             if (main_req->main)
  915                 main_req = main_req->main;
  916             else
  917                 main_req = main_req->prev;
  918 
  919         type = ap_auth_type(main_req);
  920         if ((type != NULL) && (strcasecmp(type, "GSSAPI") == 0)) {
  921             /* warn if the subrequest location and the main request
  922              * location have different configs */
  923             if (cfg != ap_get_module_config(main_req->per_dir_config,
  924                                             &auth_gssapi_module)) {
  925                 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0,
  926                               req, "Subrequest authentication bypass on "
  927                                    "location with different configuration!");
  928             }
  929             if (main_req->user) {
  930                 apr_table_t *env;
  931 
  932                 req->user = apr_pstrdup(req->pool, main_req->user);
  933                 req->ap_auth_type = main_req->ap_auth_type;
  934 
  935                 env = ap_get_module_config(main_req->request_config,
  936                                            &auth_gssapi_module);
  937                 if (!env) {
  938                     ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, req,
  939                                   "Failed to lookup env table in subrequest");
  940                 } else
  941                     mag_export_req_env(req, env);
  942 
  943                 return OK;
  944             } else {
  945                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
  946                               "The main request is tasked to establish the "
  947                               "security context, can't proceed!");
  948                 return HTTP_UNAUTHORIZED;
  949             }
  950         } else {
  951             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
  952                           "Subrequest GSSAPI auth with no auth on the main "
  953                           "request. This operation may fail if other "
  954                           "subrequests already established a context or the "
  955                           "mechanism requires multiple roundtrips.");
  956         }
  957     }
  958 
  959     /* check if admin wants to disable negotiate with this client */
  960     if (apr_table_get(req->subprocess_env, "gssapi-no-negotiate")) {
  961         send_nego_header = false;
  962     }
  963 
  964     if (cfg->ssl_only) {
  965         if (!mag_conn_is_https(req->connection)) {
  966             mag_post_error(req, cfg, MAG_AUTH_NOT_ALLOWED, 0, 0,
  967                            "Not a TLS connection, refusing to authenticate!");
  968             goto done;
  969         }
  970     }
  971 
  972     if (cfg->gss_conn_ctx) {
  973         mc = (struct mag_conn *)ap_get_module_config(
  974                                                 req->connection->conn_config,
  975                                                 &auth_gssapi_module);
  976         if (!mc) {
  977             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
  978                           "Failed to retrieve connection context!");
  979             goto done;
  980         }
  981     }
  982 
  983     /* if available, session always supersedes connection bound data */
  984     if (req_cfg->use_sessions) {
  985         mag_check_session(req_cfg, &mc);
  986     }
  987 
  988     auth_header = apr_table_get(req->headers_in, req_cfg->req_proto);
  989 
  990     if (mc) {
  991         if (mc->established &&
  992             (auth_header == NULL) &&
  993             (mc->auth_type != AUTH_TYPE_BASIC)) {
  994             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
  995                           "Already established context found!");
  996             mag_set_req_data(req, cfg, mc);
  997             ret = OK;
  998             goto done;
  999         }
 1000         pctx = &mc->ctx;
 1001     } else {
 1002         /* no preserved mc, create one just for this request */
 1003         mc = mag_new_conn_ctx(req->pool);
 1004         pctx = &ctx;
 1005     }
 1006 
 1007     /* We can proceed only if we do have an auth header */
 1008     if (!auth_header) {
 1009         mag_post_info(req, cfg, MAG_NO_AUTH,
 1010                       "Client did not send any authentication headers");
 1011         goto done;
 1012     }
 1013 
 1014     auth_header_type = ap_getword_white(req->pool, &auth_header);
 1015     if (!auth_header_type) {
 1016         mag_post_error(req, cfg, MAG_NO_AUTH, 0, 0,
 1017                        "Client sent malformed authentication headers");
 1018         goto done;
 1019     }
 1020 
 1021     /* We got auth header, sending auth header would mean re-auth */
 1022     if (cfg->negotiate_once) {
 1023             send_nego_header = false;
 1024     }
 1025 
 1026     for (i = 0; auth_types[i] != NULL; i++) {
 1027         if (strcasecmp(auth_header_type, auth_types[i]) == 0) {
 1028             auth_type = i;
 1029             break;
 1030         }
 1031     }
 1032 
 1033     switch (auth_type) {
 1034     case AUTH_TYPE_NEGOTIATE:
 1035         if (!parse_auth_header(req->pool, &auth_header, &input)) {
 1036             goto done;
 1037         }
 1038         desired_mechs = mag_get_negotiate_mechs(req->pool,
 1039                                                 req_cfg->desired_mechs);
 1040         if (desired_mechs == GSS_C_NO_OID_SET) {
 1041             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
 1042                           "Failed to get negotiate_mechs");
 1043             goto done;
 1044         }
 1045         break;
 1046     case AUTH_TYPE_BASIC:
 1047         if (!cfg->use_basic_auth) {
 1048             goto done;
 1049         }
 1050 
 1051         ba_pwd.value = ap_pbase64decode(req->pool, auth_header);
 1052         if (!ba_pwd.value) goto done;
 1053         ba_user.value = ap_getword_nulls_nc(req->pool,
 1054                                             (char **)&ba_pwd.value, ':');
 1055         if (!ba_user.value) goto done;
 1056 
 1057         if (((char *)ba_user.value)[0] == '\0' ||
 1058             ((char *)ba_pwd.value)[0] == '\0') {
 1059             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
 1060                           "Invalid empty user or password for Basic Auth");
 1061             goto done;
 1062         }
 1063         ba_user.length = strlen(ba_user.value);
 1064         ba_pwd.length = strlen(ba_pwd.value);
 1065 
 1066         if (mc->is_preserved && mc->established &&
 1067             mag_basic_check(req_cfg, mc, ba_user, ba_pwd)) {
 1068             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
 1069                           "Already established BASIC AUTH context found!");
 1070             mag_set_req_data(req, cfg, mc);
 1071             ret = OK;
 1072             goto done;
 1073         }
 1074 
 1075         break;
 1076 
 1077     case AUTH_TYPE_RAW_NTLM:
 1078         if (!is_mech_allowed(desired_mechs, gss_mech_ntlmssp,
 1079                              cfg->gss_conn_ctx)) {
 1080             mag_post_error(req, cfg, MAG_AUTH_NOT_ALLOWED, 0, 0,
 1081                            "NTLM Authentication is not allowed!");
 1082             goto done;
 1083         }
 1084 
 1085         if (!parse_auth_header(req->pool, &auth_header, &input)) {
 1086             goto done;
 1087         }
 1088 
 1089         desired_mechs = discard_const(gss_mech_set_ntlmssp);
 1090         if (desired_mechs == GSS_C_NO_OID_SET) {
 1091             mag_post_error(req, cfg, MAG_INTERNAL, 0 ,0,
 1092                            "No support for ntlmssp mech");
 1093             goto done;
 1094         }
 1095         break;
 1096 
 1097     default:
 1098         mag_post_error(req, cfg, MAG_NO_AUTH, 0, 0,
 1099                        "Client sent unknown authentication headers");
 1100         goto done;
 1101     }
 1102 
 1103     if (mc->established) {
 1104         /* if we are re-authenticating make sure the conn context
 1105          * is cleaned up so we do not accidentally reuse an existing
 1106          * established context */
 1107         mag_conn_clear(mc);
 1108     }
 1109 
 1110     mc->auth_type = auth_type;
 1111 
 1112 #ifdef HAVE_CRED_STORE
 1113     if (use_s4u2proxy(req_cfg)) {
 1114         cred_usage = GSS_C_BOTH;
 1115     }
 1116 #endif
 1117 
 1118     if (auth_type == AUTH_TYPE_BASIC) {
 1119         ret = mag_auth_basic(req_cfg, mc, ba_user, ba_pwd);
 1120         goto done;
 1121     }
 1122 
 1123     if (!mag_acquire_creds(req, cfg, desired_mechs,
 1124                            cred_usage, &acquired_cred, NULL)) {
 1125         goto done;
 1126     }
 1127 
 1128     if (auth_type == AUTH_TYPE_NEGOTIATE &&
 1129         cfg->allowed_mechs != GSS_C_NO_OID_SET) {
 1130         maj = gss_set_neg_mechs(&min, acquired_cred, cfg->allowed_mechs);
 1131         if (GSS_ERROR(maj)) {
 1132             mag_post_error(req, cfg, MAG_GSS_ERR, maj, min,
 1133                            "In Negotiate Auth: gss_set_neg_mechs() failed");
 1134             goto done;
 1135         }
 1136     }
 1137 
 1138     maj = gss_accept_sec_context(&min, pctx, acquired_cred,
 1139                                  &input, GSS_C_NO_CHANNEL_BINDINGS,
 1140                                  &client, &mech_type, &output, NULL, &vtime,
 1141                                  &delegated_cred);
 1142     if (GSS_ERROR(maj)) {
 1143         mag_post_error(req, cfg, MAG_GSS_ERR, maj, min,
 1144                        "In Negotiate Auth: gss_accept_sec_context() failed");
 1145         goto done;
 1146     } else if (maj == GSS_S_CONTINUE_NEEDED) {
 1147         if (!mc->is_preserved) {
 1148             mag_post_error(req, cfg, MAG_INTERNAL, 0, 0,
 1149                            "Mechanism needs continuation but neither "
 1150                            "GssapiConnectionBound nor "
 1151                            "GssapiUseSessions are configured");
 1152             gss_release_buffer(&min, &output);
 1153             output.length = 0;
 1154         }
 1155         /* auth not complete send token and wait next packet */
 1156         goto done;
 1157     }
 1158 
 1159     ret = mag_complete(req_cfg, mc, client, mech_type, vtime, delegated_cred);
 1160 
 1161 done:
 1162     if (ret == OK && req_cfg->send_persist)
 1163         apr_table_set(req->err_headers_out, "Persistent-Auth",
 1164             cfg->gss_conn_ctx ? "true" : "false");
 1165 
 1166     if ((auth_type != AUTH_TYPE_BASIC) && (output.length != 0)) {
 1167         int prefixlen = strlen(mag_str_auth_type(auth_type)) + 1;
 1168         replen = apr_base64_encode_len(output.length) + 1;
 1169         reply = apr_pcalloc(req->pool, prefixlen + replen);
 1170         if (reply) {
 1171             memcpy(reply, mag_str_auth_type(auth_type), prefixlen - 1);
 1172             reply[prefixlen - 1] = ' ';
 1173             apr_base64_encode(&reply[prefixlen], output.value, output.length);
 1174             apr_table_add(req->err_headers_out, req_cfg->rep_proto, reply);
 1175         }
 1176     } else if (ret == HTTP_UNAUTHORIZED) {
 1177         if (send_nego_header) {
 1178             apr_table_add(req->err_headers_out,
 1179                           req_cfg->rep_proto, "Negotiate");
 1180             if (is_mech_allowed(desired_mechs, gss_mech_ntlmssp,
 1181                                 cfg->gss_conn_ctx)) {
 1182                 apr_table_add(req->err_headers_out, req_cfg->rep_proto,
 1183                               "NTLM");
 1184             }
 1185         }
 1186         if (cfg->use_basic_auth) {
 1187             apr_table_add(req->err_headers_out, req_cfg->rep_proto,
 1188                           apr_psprintf(req->pool, "Basic realm=\"%s\"",
 1189                                        ap_auth_name(req)));
 1190         }
 1191     }
 1192 
 1193     if (ctx != GSS_C_NO_CONTEXT)
 1194         gss_delete_sec_context(&min, &ctx, GSS_C_NO_BUFFER);
 1195     gss_release_cred(&min, &acquired_cred);
 1196     gss_release_cred(&min, &delegated_cred);
 1197     gss_release_buffer(&min, &output);
 1198     gss_release_name(&min, &client);
 1199     return ret;
 1200 }
 1201 
 1202 static int mag_complete(struct mag_req_cfg *req_cfg, struct mag_conn *mc,
 1203                         gss_name_t client, gss_OID mech_type,
 1204                         uint32_t vtime, gss_cred_id_t delegated_cred)
 1205 {
 1206     gss_buffer_desc lname = GSS_C_EMPTY_BUFFER;
 1207     gss_buffer_desc name = GSS_C_EMPTY_BUFFER;
 1208     struct mag_config *cfg = req_cfg->cfg;
 1209     request_rec *req = req_cfg->req;
 1210     int r, ret = HTTP_UNAUTHORIZED;
 1211     uint32_t maj, min;
 1212 
 1213     maj = gss_display_name(&min, client, &name, NULL);
 1214     if (GSS_ERROR(maj)) {
 1215         mag_post_error(req, cfg, MAG_GSS_ERR, maj, min,
 1216                        "gss_display_name() failed");
 1217         goto done;
 1218     }
 1219 
 1220     mc->gss_name = apr_pstrndup(req->pool, name.value, name.length);
 1221     if (vtime == GSS_C_INDEFINITE || vtime < MIN_SESS_EXP_TIME) {
 1222         vtime = MIN_SESS_EXP_TIME;
 1223     }
 1224     mc->expiration = time(NULL) + vtime;
 1225 
 1226     mag_get_name_attributes(req, cfg, client, mc);
 1227 
 1228     r = mag_verify_name_attributes(cfg->required_na_expr,
 1229                                    mc->required_name_attrs,
 1230                                    mc->required_name_vals);
 1231     if (r == -1) {
 1232         mag_post_error(req, cfg, MAG_INTERNAL, 0, 0,
 1233                        "Error verifying name attributes!");
 1234         goto done;
 1235     } else if (r == 0) {
 1236         ret = HTTP_FORBIDDEN;
 1237         mag_set_req_attr_fail(req, cfg, mc);
 1238         goto done;
 1239     }
 1240 
 1241 #ifdef HAVE_CRED_STORE
 1242     if (cfg->deleg_ccache_dir &&
 1243         delegated_cred != GSS_C_NO_CREDENTIAL) {
 1244         char *ccache_path;
 1245 
 1246         mc->ccname = 0;
 1247         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, req,
 1248                       "requester: %s", mc->gss_name);
 1249 
 1250         ccache_path = get_ccache_name(req, cfg->deleg_ccache_dir, mc->gss_name,
 1251                                       cfg->deleg_ccache_unique, mc);
 1252         if (ccache_path == NULL) {
 1253             goto done;
 1254         }
 1255 
 1256         mag_store_deleg_creds(req, ccache_path, delegated_cred);
 1257         mc->delegated = true;
 1258 
 1259         if (!req_cfg->use_sessions && cfg->deleg_ccache_unique) {
 1260             /* queue removing ccache to avoid littering filesystem */
 1261             apr_pool_cleanup_register(mc->pool, ccache_path,
 1262                                       (int (*)(void *)) unlink,
 1263                                       apr_pool_cleanup_null);
 1264         }
 1265 
 1266         /* extract filename from full path */
 1267         mc->ccname = strrchr(ccache_path, '/') + 1;
 1268     }
 1269 #endif
 1270 
 1271     if (cfg->map_to_local) {
 1272         /* We have to play heuristics here as gss_localname does not work
 1273          * as expected with SPNEGO-wrapped names.
 1274          * http://krbdev.mit.edu/rt/Ticket/Display.html?id=8782
 1275          */
 1276         maj = gss_localname(&min, client, mech_type, &lname);
 1277         if (maj != GSS_S_COMPLETE) {
 1278             uint32_t sub_maj, sub_min;
 1279             /* try fallback with no oid */
 1280             sub_maj = gss_localname(&sub_min, client, GSS_C_NO_OID, &lname);
 1281             if (sub_maj != GSS_S_UNAVAILABLE) {
 1282                 /* use second call errors only if they are meaningful */
 1283                 maj = sub_maj;
 1284                 min = sub_min;
 1285             }
 1286         }
 1287         if (maj != GSS_S_COMPLETE) {
 1288             mag_post_error(req, cfg, MAG_GSS_ERR, maj, min,
 1289                            "gss_localname() failed");
 1290             goto done;
 1291         }
 1292         mc->user_name = apr_pstrndup(mc->pool, lname.value, lname.length);
 1293     } else {
 1294         mc->user_name = apr_pstrdup(mc->pool, mc->gss_name);
 1295     }
 1296 
 1297     if (cfg->pubmech) {
 1298         mag_publish_mech(req, mc, mag_str_auth_type(mc->auth_type), mech_type);
 1299     }
 1300 
 1301     mc->established = true;
 1302     if (req_cfg->use_sessions) {
 1303         mag_attempt_session(req_cfg, mc);
 1304     }
 1305 
 1306     /* Now set request data and env vars */
 1307     mag_set_req_data(req, cfg, mc);
 1308 
 1309     ret = OK;
 1310 
 1311 done:
 1312     gss_release_buffer(&min, &name);
 1313     gss_release_buffer(&min, &lname);
 1314     return ret;
 1315 }
 1316 
 1317 static void *mag_create_dir_config(apr_pool_t *p, char *dir)
 1318 {
 1319     struct mag_config *cfg;
 1320 
 1321     cfg = (struct mag_config *)apr_pcalloc(p, sizeof(struct mag_config));
 1322     cfg->pool = p;
 1323 #ifdef HAVE_CRED_STORE
 1324     cfg->ccname_envvar = "KRB5CCNAME";
 1325 #endif
 1326     cfg->basic_timeout = 300;
 1327 
 1328     return cfg;
 1329 }
 1330 
 1331 static const char *mag_ssl_only(cmd_parms *parms, void *mconfig, int on)
 1332 {
 1333     struct mag_config *cfg = (struct mag_config *)mconfig;
 1334     cfg->ssl_only = on ? true : false;
 1335     return NULL;
 1336 }
 1337 
 1338 static const char *mag_map_to_local(cmd_parms *parms, void *mconfig, int on)
 1339 {
 1340     struct mag_config *cfg = (struct mag_config *)mconfig;
 1341     cfg->map_to_local = on ? true : false;
 1342     return NULL;
 1343 }
 1344 
 1345 static const char *mag_conn_ctx(cmd_parms *parms, void *mconfig, int on)
 1346 {
 1347     struct mag_config *cfg = (struct mag_config *)mconfig;
 1348     cfg->gss_conn_ctx = on ? true : false;
 1349     return NULL;
 1350 }
 1351 
 1352 static const char *mag_send_persist(cmd_parms *parms, void *mconfig, int on)
 1353 {
 1354     struct mag_config *cfg = (struct mag_config *)mconfig;
 1355     cfg->send_persist = on ? true : false;
 1356     return NULL;
 1357 }
 1358 
 1359 static const char *mag_use_sess(cmd_parms *parms, void *mconfig, int on)
 1360 {
 1361     struct mag_config *cfg = (struct mag_config *)mconfig;
 1362     cfg->use_sessions = on ? true : false;
 1363     return NULL;
 1364 }
 1365 
 1366 #ifdef HAVE_CRED_STORE
 1367 static const char *mag_use_s4u2p(cmd_parms *parms, void *mconfig, int on)
 1368 {
 1369     struct mag_config *cfg = (struct mag_config *)mconfig;
 1370     cfg->use_s4u2proxy = on ? true : false;
 1371 
 1372     return NULL;
 1373 }
 1374 
 1375 static const char *mag_deleg_ccache_unique(cmd_parms *parms, void *mconfig,
 1376                                            int on)
 1377 {
 1378     struct mag_config *cfg = (struct mag_config *)mconfig;
 1379     cfg->deleg_ccache_unique = on ? true : false;
 1380     return NULL;
 1381 }
 1382 
 1383 #endif
 1384 
 1385 #define SESS_KEYS_TOT_LEN 32
 1386 
 1387 static void create_sess_key_file(cmd_parms *parms, const char *name)
 1388 {
 1389     apr_status_t ret;
 1390     apr_file_t *fd = NULL;
 1391     unsigned char keys[SESS_KEYS_TOT_LEN];
 1392     apr_size_t bw;
 1393 
 1394     ret = apr_file_open(&fd, name,
 1395                         APR_FOPEN_CREATE | APR_FOPEN_WRITE | APR_FOPEN_EXCL,
 1396                         APR_FPROT_UREAD | APR_FPROT_UWRITE, parms->temp_pool);
 1397     if (ret != APR_SUCCESS) {
 1398         char err[256];
 1399         apr_strerror(ret, err, sizeof(err));
 1400         ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
 1401                      "Failed to create key file %s: %s", name, err);
 1402         return;
 1403     }
 1404     ret = apr_generate_random_bytes(keys, SESS_KEYS_TOT_LEN);
 1405     if (ret != OK) {
 1406         ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
 1407                      "Failed to generate random sealing key!");
 1408         ret = APR_INCOMPLETE;
 1409         goto done;
 1410     }
 1411     ret = apr_file_write_full(fd, keys, SESS_KEYS_TOT_LEN, &bw);
 1412     if ((ret != APR_SUCCESS) || (bw != SESS_KEYS_TOT_LEN)) {
 1413         char err[256];
 1414         apr_strerror(ret, err, sizeof(err));
 1415         ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
 1416                      "Failed to store key in %s: %s", name, err);
 1417         ret = APR_INCOMPLETE;
 1418         goto done;
 1419     }
 1420 done:
 1421     apr_file_close(fd);
 1422     if (ret != APR_SUCCESS) apr_file_remove(name, parms->temp_pool);
 1423 }
 1424 
 1425 static const char *mag_sess_key(cmd_parms *parms, void *mconfig, const char *w)
 1426 {
 1427     struct mag_config *cfg = (struct mag_config *)mconfig;
 1428     struct databuf keys;
 1429     unsigned char *val;
 1430     apr_status_t rc;
 1431     int l;
 1432 
 1433     if (strncmp(w, "key:", 4) == 0) {
 1434         const char *k = w + 4;
 1435 
 1436         l = apr_base64_decode_len(k);
 1437         val = apr_palloc(parms->temp_pool, l);
 1438 
 1439         keys.length = (int)apr_base64_decode_binary(val, k);
 1440         keys.value = (unsigned char *)val;
 1441 
 1442         if (keys.length != SESS_KEYS_TOT_LEN) {
 1443             ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
 1444                          "Invalid key length, expected 32 got %d",
 1445                          keys.length);
 1446             return NULL;
 1447         }
 1448     } else if (strncmp(w, "file:", 5) == 0) {
 1449         apr_status_t ret;
 1450         apr_file_t *fd = NULL;
 1451         apr_int32_t ronly = APR_FOPEN_READ;
 1452         const char *fname;
 1453 
 1454         keys.length = SESS_KEYS_TOT_LEN;
 1455         keys.value = apr_palloc(parms->temp_pool, keys.length);
 1456 
 1457         fname = w + 5;
 1458 
 1459         ret = apr_file_open(&fd, fname, ronly, 0, parms->temp_pool);
 1460         if (APR_STATUS_IS_ENOENT(ret)) {
 1461             create_sess_key_file(parms, fname);
 1462 
 1463             ret = apr_file_open(&fd, fname, ronly, 0, parms->temp_pool);
 1464         }
 1465         if (ret == APR_SUCCESS) {
 1466             apr_size_t br;
 1467             ret = apr_file_read_full(fd, keys.value, keys.length, &br);
 1468             apr_file_close(fd);
 1469             if ((ret != APR_SUCCESS) || (br != keys.length)) {
 1470                 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
 1471                              "Failed to read sealing key from %s!", fname);
 1472                 return NULL;
 1473             }
 1474         } else {
 1475             ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
 1476                          "Failed to open key file %s", fname);
 1477             return NULL;
 1478         }
 1479     } else {
 1480         ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
 1481                      "Invalid key format, unexpected prefix in %s'", w);
 1482         return NULL;
 1483     }
 1484     rc = SEAL_KEY_CREATE(cfg->pool, &cfg->mag_skey, &keys);
 1485     if (rc != OK) {
 1486         ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
 1487                      "Failed to import sealing key!");
 1488     }
 1489     return NULL;
 1490 }
 1491 
 1492 #ifdef HAVE_CRED_STORE
 1493 
 1494 #define MAX_CRED_OPTIONS 10
 1495 
 1496 static const char *mag_cred_store(cmd_parms *parms, void *mconfig,
 1497                                   const char *w)
 1498 {
 1499     struct mag_config *cfg = (struct mag_config *)mconfig;
 1500     gss_key_value_element_desc *elements;
 1501     uint32_t count;
 1502     size_t size;
 1503     const char *p;
 1504     char *value;
 1505     char *key;
 1506 
 1507     p = strchr(w, ':');
 1508     if (!p) {
 1509         ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
 1510                      "%s [%s]", "Invalid syntax for GssapiCredStore option", w);
 1511         return NULL;
 1512     }
 1513 
 1514     key = apr_pstrndup(parms->pool, w, (p-w));
 1515     value = apr_pstrdup(parms->pool, p + 1);
 1516 
 1517     if (!cfg->cred_store) {
 1518         cfg->cred_store = apr_pcalloc(parms->pool,
 1519                                       sizeof(gss_key_value_set_desc));
 1520         size = sizeof(gss_key_value_element_desc) * MAX_CRED_OPTIONS;
 1521         cfg->cred_store->elements = apr_palloc(parms->pool, size);
 1522     }
 1523 
 1524     elements = cfg->cred_store->elements;
 1525     count = cfg->cred_store->count;
 1526 
 1527     if (count >= MAX_CRED_OPTIONS) {
 1528         ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
 1529                      "Too many GssapiCredStore options (MAX: %d)",
 1530                      MAX_CRED_OPTIONS);
 1531         return NULL;
 1532     }
 1533     cfg->cred_store->count++;
 1534 
 1535     elements[count].key = key;
 1536     elements[count].value = value;
 1537 
 1538     return NULL;
 1539 }
 1540 
 1541 static const char *mag_deleg_ccache_dir(cmd_parms *parms, void *mconfig,
 1542                                         const char *value)
 1543 {
 1544     struct mag_config *cfg = (struct mag_config *)mconfig;
 1545 
 1546     cfg->deleg_ccache_dir = apr_pstrdup(parms->pool, value);
 1547 
 1548     return NULL;
 1549 }
 1550 
 1551 #define CCMODE "mode:"
 1552 #define CCUID "uid:"
 1553 #define CCGID "gid:"
 1554 
 1555 static const char *mag_deleg_ccache_perms(cmd_parms *parms, void *mconfig,
 1556                                           const char *w)
 1557 {
 1558     struct mag_config *cfg = (struct mag_config *)mconfig;
 1559 
 1560     if (strncmp(w, CCMODE, sizeof(CCMODE) - 1) == 0) {
 1561         const char *p = w + sizeof(CCMODE) -1;
 1562         errno = 0;
 1563         /* mode is traditionally represented in octal, but the actual
 1564          * permission bit are using the 3 least significant bit of each quartet
 1565          * so effectively if we read an octal number as hex we get the correct
 1566          * mode bits */
 1567         cfg->deleg_ccache_mode = strtol(p, NULL, 16);
 1568         if (errno != 0) {
 1569             ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
 1570                          "Invalid GssapiDelegCcachePerms mode value [%s]", p);
 1571             /* reset to the default */
 1572             cfg->deleg_ccache_mode = 0;
 1573         }
 1574     } else if (strncmp(w, CCUID, sizeof(CCUID) - 1) == 0) {
 1575         const char *p = w + sizeof(CCUID) - 1;
 1576         errno = 0;
 1577         if (isdigit(*p)) {
 1578             char *endptr;
 1579             cfg->deleg_ccache_uid = strtol(p, &endptr, 0);
 1580             if (errno != 0 || (endptr && *endptr != '\0')) {
 1581                 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
 1582                              "Invalid GssapiDelegCcachePerms uid value [%s]",
 1583                              p);
 1584                 /* reset to the default */
 1585                 cfg->deleg_ccache_uid = 0;
 1586             }
 1587         } else {
 1588             int ret = mag_get_user_uid(p, &cfg->deleg_ccache_uid);
 1589             if (ret != 0) {
 1590                 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
 1591                     "Invalid GssapiDelegCcachePerms uid value [%s](%s)",
 1592                     p, strerror(ret));
 1593             }
 1594         }
 1595     } else if (strncmp(w, CCGID, sizeof(CCGID) - 1) == 0) {
 1596         const char *p = w + sizeof(CCGID) - 1;
 1597         errno = 0;
 1598         if (isdigit(*p)) {
 1599             char *endptr;
 1600             cfg->deleg_ccache_gid = strtol(p, &endptr, 0);
 1601             if (errno != 0 || (endptr && *endptr != '\0')) {
 1602                 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
 1603                              "Invalid GssapiDelegCcachePerms gid value [%s]",
 1604                              p);
 1605                 /* reset to the default */
 1606                 cfg->deleg_ccache_gid = 0;
 1607             }
 1608         } else {
 1609             int ret = mag_get_group_gid(p, &cfg->deleg_ccache_gid);
 1610             if (ret != 0) {
 1611                 ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
 1612                     "Invalid GssapiDelegCcachePerms gid value [%s](%s)",
 1613                     p, strerror(ret));
 1614             }
 1615         }
 1616     } else {
 1617         ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
 1618                      "Invalid GssapiDelegCcachePerms directive [%s]", w);
 1619     }
 1620 
 1621     return NULL;
 1622 }
 1623 #endif
 1624 
 1625 static const char *mag_use_basic_auth(cmd_parms *parms, void *mconfig, int on)
 1626 {
 1627     struct mag_config *cfg = (struct mag_config *)mconfig;
 1628 
 1629     cfg->use_basic_auth = on ? true : false;
 1630     return NULL;
 1631 }
 1632 
 1633 static bool mag_list_of_mechs(cmd_parms *parms, gss_OID_set *oidset,
 1634                               const char *w)
 1635 {
 1636     gss_buffer_desc buf = { 0 };
 1637     uint32_t maj, min;
 1638     gss_OID_set set;
 1639     gss_OID oid;
 1640     bool release_oid = false;
 1641 
 1642     if (NULL == *oidset) {
 1643         maj = gss_create_empty_oid_set(&min, &set);
 1644         if (maj != GSS_S_COMPLETE) {
 1645             ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
 1646                          "gss_create_empty_oid_set() failed.");
 1647             *oidset = GSS_C_NO_OID_SET;
 1648             return false;
 1649         }
 1650         /* register in the pool so it can be released once the server
 1651          * winds down */
 1652         apr_pool_cleanup_register(parms->pool, (void *)set,
 1653                                   mag_oid_set_destroy,
 1654                                   apr_pool_cleanup_null);
 1655         *oidset = set;
 1656     } else {
 1657         set = *oidset;
 1658     }
 1659 
 1660     if (strcmp(w, "krb5") == 0) {
 1661         oid = discard_const(gss_mech_krb5);
 1662     } else if (strcmp(w, "iakerb") == 0) {
 1663         oid = discard_const(gss_mech_iakerb);
 1664     } else if (strcmp(w, "ntlmssp") == 0) {
 1665         oid = discard_const(gss_mech_ntlmssp);
 1666     } else {
 1667         buf.value = discard_const(w);
 1668         buf.length = strlen(w);
 1669         maj = gss_str_to_oid(&min, &buf, &oid);
 1670         if (maj != GSS_S_COMPLETE) {
 1671             ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
 1672                          "Unrecognized GSSAPI Mechanism: [%s]", w);
 1673             return false;
 1674         }
 1675         release_oid = true;
 1676     }
 1677     maj = gss_add_oid_set_member(&min, oid, &set);
 1678     if (maj != GSS_S_COMPLETE) {
 1679         ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
 1680                          "gss_add_oid_set_member() failed for [%s].", w);
 1681     }
 1682     if (release_oid) {
 1683         (void)gss_release_oid(&min, &oid);
 1684     }
 1685 
 1686     return true;
 1687 }
 1688 
 1689 static const char *mag_allow_mech(cmd_parms *parms, void *mconfig,
 1690                                   const char *w)
 1691 {
 1692     struct mag_config *cfg = (struct mag_config *)mconfig;
 1693 
 1694     if (!mag_list_of_mechs(parms, &cfg->allowed_mechs, w))
 1695         return "Failed to apply GssapiAllowedMech directive";
 1696 
 1697     return NULL;
 1698 }
 1699 
 1700 static const char *mag_negotiate_once(cmd_parms *parms, void *mconfig, int on)
 1701 {
 1702     struct mag_config *cfg = (struct mag_config *)mconfig;
 1703 
 1704     cfg->negotiate_once = on ? true : false;
 1705     return NULL;
 1706 }
 1707 
 1708 static apr_status_t mag_name_attrs_cleanup(void *data)
 1709 {
 1710     struct mag_config *cfg = (struct mag_config *)data;
 1711     free(cfg->name_attributes);
 1712     cfg->name_attributes = NULL;
 1713     return 0;
 1714 }
 1715 
 1716 static const char *mag_name_attrs(cmd_parms *parms, void *mconfig,
 1717                                   const char *w)
 1718 {
 1719     struct mag_config *cfg = (struct mag_config *)mconfig;
 1720     void *tmp_na;
 1721     size_t size = 0;
 1722     char *p;
 1723     int c;
 1724 
 1725     if (!cfg->name_attributes) {
 1726         size = sizeof(struct mag_name_attributes)
 1727                 + (sizeof(struct mag_na_map) * 16);
 1728     } else if (cfg->name_attributes->map_count % 16 == 0) {
 1729         size = sizeof(struct mag_name_attributes)
 1730                 + (sizeof(struct mag_na_map)
 1731                     * (cfg->name_attributes->map_count + 16));
 1732     }
 1733     if (size) {
 1734         tmp_na = realloc(cfg->name_attributes, size);
 1735         if (!tmp_na) apr_pool_abort_get(cfg->pool)(ENOMEM);
 1736 
 1737         if (cfg->name_attributes) {
 1738             size_t empty = (sizeof(struct mag_na_map) * 16);
 1739             memset(tmp_na + size - empty, 0, empty);
 1740         } else {
 1741             memset(tmp_na, 0, size);
 1742         }
 1743         cfg->name_attributes = (struct mag_name_attributes *)tmp_na;
 1744         apr_pool_userdata_setn(cfg, GSS_NAME_ATTR_USERDATA,
 1745                                mag_name_attrs_cleanup, cfg->pool);
 1746     }
 1747 
 1748     p = strchr(w, ' ');
 1749     if (p == NULL) {
 1750         if (strcmp(w, "json") == 0) {
 1751             cfg->name_attributes->output_json = true;
 1752         } else {
 1753             ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
 1754                          "Invalid Name Attributes value [%s].", w);
 1755         }
 1756         return NULL;
 1757     }
 1758 
 1759     c = cfg->name_attributes->map_count;
 1760     cfg->name_attributes->map[c].env_name = apr_pstrndup(cfg->pool, w, p-w);
 1761     p++;
 1762     cfg->name_attributes->map[c].attr_name = apr_pstrdup(cfg->pool, p);
 1763     cfg->name_attributes->map_count += 1;
 1764 
 1765     return NULL;
 1766 }
 1767 
 1768 static const char *required_name_attrs(cmd_parms *parms, void *mconfig,
 1769                                      const char *w)
 1770 {
 1771     struct mag_config *cfg = (struct mag_config *)mconfig;
 1772 
 1773     if (!mag_check_name_attr_expr(w)) {
 1774         ap_log_error(APLOG_MARK, APLOG_ERR, 0, parms->server,
 1775                      "syntax error in [%s].", w);
 1776         return "Failed to verify required name attribute expression";
 1777     }
 1778     cfg->required_na_expr = apr_pstrdup(cfg->pool, w);
 1779 
 1780     return NULL;
 1781 }
 1782 
 1783 static const char *mag_basic_auth_mechs(cmd_parms *parms, void *mconfig,
 1784                                         const char *w)
 1785 {
 1786     struct mag_config *cfg = (struct mag_config *)mconfig;
 1787 
 1788     if (!mag_list_of_mechs(parms, &cfg->basic_mechs, w))
 1789         return "Failed to apply GssapiBasicAuthMech directive";
 1790 
 1791     return NULL;
 1792 }
 1793 
 1794 static const char *mag_acceptor_name(cmd_parms *parms, void *mconfig,
 1795                                      const char *w)
 1796 {
 1797     struct mag_config *cfg = (struct mag_config *)mconfig;
 1798     gss_buffer_desc bufnam = { strlen(w), (void *)w };
 1799     uint32_t maj, min;
 1800 
 1801     if (strcmp(w, "{HOSTNAME}") == 0) {
 1802         cfg->acceptor_name_from_req = true;
 1803         return NULL;
 1804     }
 1805 
 1806     maj = gss_import_name(&min, &bufnam, GSS_C_NT_HOSTBASED_SERVICE,
 1807                           &cfg->acceptor_name);
 1808     if (GSS_ERROR(maj)) {
 1809         return apr_psprintf(parms->pool, "[%s] Failed to import name '%s' %s",
 1810                             parms->cmd->name, w,
 1811                             mag_error(parms->pool, "", maj, min));
 1812     }
 1813 
 1814     return NULL;
 1815 }
 1816 
 1817 static const char *mag_basic_timeout(cmd_parms *parms, void *mconfig,
 1818                                      const char *w)
 1819 {
 1820     struct mag_config *cfg = (struct mag_config *)mconfig;
 1821     unsigned long int value;
 1822 
 1823     value = strtoul(w, NULL, 10);
 1824     if (value >= UINT32_MAX) {
 1825         cfg->basic_timeout = GSS_C_INDEFINITE;
 1826         return NULL;
 1827     }
 1828     cfg->basic_timeout = value;
 1829     return NULL;
 1830 }
 1831 
 1832 static void *mag_create_server_config(apr_pool_t *p, server_rec *s)
 1833 {
 1834     struct mag_server_config *scfg;
 1835     uint32_t maj, min;
 1836     apr_status_t rc;
 1837 
 1838     scfg = apr_pcalloc(p, sizeof(struct mag_server_config));
 1839 
 1840     maj = gss_indicate_mechs(&min, &scfg->default_mechs);
 1841     if (maj != GSS_S_COMPLETE) {
 1842         ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
 1843                      "gss_indicate_mechs() failed");
 1844     } else {
 1845         /* Register the set in pool */
 1846         apr_pool_cleanup_register(p, (void *)scfg->default_mechs,
 1847                                   mag_oid_set_destroy, apr_pool_cleanup_null);
 1848     }
 1849 
 1850     rc = SEAL_KEY_CREATE(p, &scfg->mag_skey, NULL);
 1851     if (rc != OK) {
 1852         ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
 1853                      "Failed to generate random sealing key!");
 1854     }
 1855 
 1856     return scfg;
 1857 }
 1858 
 1859 static const command_rec mag_commands[] = {
 1860     AP_INIT_FLAG("GssapiSSLonly", mag_ssl_only, NULL, OR_AUTHCFG,
 1861                   "Work only if connection is SSL Secured"),
 1862     AP_INIT_FLAG("GssapiLocalName", mag_map_to_local, NULL, OR_AUTHCFG,
 1863                   "Translate principals to local names"),
 1864     AP_INIT_FLAG("GssapiConnectionBound", mag_conn_ctx, NULL, OR_AUTHCFG,
 1865                   "Authentication is bound to the TCP connection"),
 1866     AP_INIT_FLAG("GssapiSignalPersistentAuth", mag_send_persist, NULL, OR_AUTHCFG,
 1867                   "Send Persitent-Auth header according to connection bound"),
 1868     AP_INIT_FLAG("GssapiUseSessions", mag_use_sess, NULL, OR_AUTHCFG,
 1869                   "Authentication uses mod_sessions to hold status"),
 1870     AP_INIT_RAW_ARGS("GssapiSessionKey", mag_sess_key, NULL, OR_AUTHCFG,
 1871                      "Key Used to seal session data."),
 1872 #ifdef HAVE_CRED_STORE
 1873     AP_INIT_FLAG("GssapiUseS4U2Proxy", mag_use_s4u2p, NULL, OR_AUTHCFG,
 1874                   "Initializes credentials for s4u2proxy usage"),
 1875     AP_INIT_ITERATE("GssapiCredStore", mag_cred_store, NULL, OR_AUTHCFG,
 1876                     "Credential Store"),
 1877     AP_INIT_RAW_ARGS("GssapiDelegCcacheDir", mag_deleg_ccache_dir, NULL,
 1878                      OR_AUTHCFG, "Directory to store delegated credentials"),
 1879     AP_INIT_ITERATE("GssapiDelegCcachePerms", mag_deleg_ccache_perms, NULL,
 1880                      OR_AUTHCFG, "Permissions to assign to Ccache files"),
 1881     AP_INIT_TAKE1("GssapiDelegCcacheEnvVar", ap_set_string_slot,
 1882                     (void *)APR_OFFSETOF(struct mag_config, ccname_envvar),
 1883                     OR_AUTHCFG, "Environment variable to receive ccache name"),
 1884     AP_INIT_FLAG("GssapiDelegCcacheUnique", mag_deleg_ccache_unique, NULL,
 1885                  OR_AUTHCFG, "Use unique ccaches for delgation"),
 1886     AP_INIT_FLAG("GssapiImpersonate", ap_set_flag_slot,
 1887           (void *)APR_OFFSETOF(struct mag_config, s4u2self), OR_AUTHCFG,
 1888                "Do impersonation call (S4U2Self) "
 1889                "based on already authentication username"),
 1890 #endif
 1891     AP_INIT_FLAG("GssapiBasicAuth", mag_use_basic_auth, NULL, OR_AUTHCFG,
 1892                      "Allows use of Basic Auth for authentication"),
 1893     AP_INIT_ITERATE("GssapiBasicAuthMech", mag_basic_auth_mechs, NULL,
 1894                     OR_AUTHCFG, "Mechanisms to use for basic auth"),
 1895     AP_INIT_ITERATE("GssapiAllowedMech", mag_allow_mech, NULL, OR_AUTHCFG,
 1896                     "Allowed Mechanisms"),
 1897     AP_INIT_FLAG("GssapiNegotiateOnce", mag_negotiate_once, NULL, OR_AUTHCFG,
 1898                     "Don't resend negotiate header on negotiate failure"),
 1899     AP_INIT_RAW_ARGS("GssapiNameAttributes", mag_name_attrs, NULL, OR_AUTHCFG,
 1900                      "Name Attributes to be exported as environ variables"),
 1901     AP_INIT_RAW_ARGS("GssapiRequiredNameAttributes", required_name_attrs, NULL,
 1902                      OR_AUTHCFG, "Name Attributes required to be present"),
 1903     AP_INIT_FLAG("GssapiPublishErrors", ap_set_flag_slot,
 1904                  (void *)APR_OFFSETOF(struct mag_config, enverrs), OR_AUTHCFG,
 1905                  "Publish GSSAPI Errors in Envionment Variables"),
 1906     AP_INIT_FLAG("GssapiPublishMech", ap_set_flag_slot,
 1907                  (void *)APR_OFFSETOF(struct mag_config, pubmech), OR_AUTHCFG,
 1908                  "Publish GSSAPI Mech Name in Envionment Variables"),
 1909     AP_INIT_RAW_ARGS("GssapiAcceptorName", mag_acceptor_name, NULL, OR_AUTHCFG,
 1910                      "Name of the acceptor credentials."),
 1911     AP_INIT_TAKE1("GssapiBasicTicketTimeout", mag_basic_timeout, NULL,
 1912                   OR_AUTHCFG, "Ticket Validity Timeout with Basic Auth."),
 1913     { NULL }
 1914 };
 1915 
 1916 static void
 1917 mag_register_hooks(apr_pool_t *p)
 1918 {
 1919 #ifdef AP_AUTH_INTERNAL_PER_CONF
 1920     ap_hook_check_authn(mag_auth, NULL, NULL, APR_HOOK_MIDDLE,
 1921                                                 AP_AUTH_INTERNAL_PER_CONF);
 1922 #else
 1923     ap_hook_check_user_id(mag_auth, NULL, NULL, APR_HOOK_MIDDLE);
 1924 #endif
 1925     ap_hook_post_config(mag_post_config, NULL, NULL, APR_HOOK_MIDDLE);
 1926     ap_hook_pre_connection(mag_pre_connection, NULL, NULL, APR_HOOK_MIDDLE);
 1927 #ifdef HAVE_CRED_STORE
 1928     ap_hook_fixups(mag_s4u2self, NULL, NULL, APR_HOOK_MIDDLE);
 1929 #endif
 1930 }
 1931 
 1932 module AP_MODULE_DECLARE_DATA auth_gssapi_module =
 1933 {
 1934     STANDARD20_MODULE_STUFF,
 1935     mag_create_dir_config,
 1936     NULL,
 1937     mag_create_server_config,
 1938     NULL,
 1939     mag_commands,
 1940     mag_register_hooks
 1941 };