"Fossies" - the Fresh Open Source Software Archive

Member "mod_spnego-0.2.0/mod_spnego.c" (8 May 2011, 41060 Bytes) of package /linux/www/apache_httpd_modules/old/mod_spnego-0.2.0.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_spnego.c" see the Fossies "Dox" file reference documentation.

    1 /* -----------------------------------------------------------------------------
    2  * mod_spnego.c is an Apache module that supports authentication via the RFC
    3  * 2478 SPNEGO GSS-API mechanism.
    4  *
    5  * mod_spnego supports Apache 1.3 and 2.0.
    6  *
    7  * Author: Frank Balluffi and Markus Moeller
    8  *
    9  * Copyright (C) 2002-2007 Frank Balluffi and Markus Moeller. All rights
   10  * reserved.
   11  *
   12  * Licensed under the Apache License, Version 2.0 (the "License");
   13  * you may not use this file except in compliance with the License.
   14  * You may obtain a copy of the License at
   15  *
   16  *     http://www.apache.org/licenses/LICENSE-2.0
   17  *
   18  * Unless required by applicable law or agreed to in writing, software
   19  * distributed under the License is distributed on an "AS IS" BASIS,
   20  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   21  * See the License for the specific language governing permissions and
   22  * limitations under the License.
   23  * -----------------------------------------------------------------------------
   24  */
   25 
   26 #include "httpd.h"
   27 #include "http_config.h"
   28 #include "http_core.h"
   29 #include "http_log.h"
   30 #include "http_protocol.h"
   31 #include "http_request.h"
   32 
   33 #ifdef APACHE13
   34 #else
   35 #include "apr_base64.h"
   36 #include "apr_env.h"
   37 #include "apr_strings.h"
   38 #endif
   39 
   40 /*
   41  * PORTABLE_APLOG_DEBUG and PORTABLE_APLOG_INFO are hacks.
   42  */
   43 
   44 /*
   45  * Apache 1.3 stores user in connection substructure of request_rec. Apache 2.0
   46  * stores user in request_rec.
   47  */
   48 
   49 #ifdef APACHE13
   50 #define PORTABLE_APLOG_ERR  APLOG_ERR
   51 #define PORTABLE_APLOG_INFO APLOG_INFO
   52 #define PORTABLE_USER       connection->user
   53 #else
   54 #define PORTABLE_APLOG_ERR  APLOG_ERR, 0
   55 #define PORTABLE_APLOG_INFO APLOG_INFO, 0
   56 #define PORTABLE_USER       user
   57 #endif
   58 
   59 #ifdef APACHE13
   60 #define AP_METHOD_BIT          1
   61 #define AP_MODULE_DECLARE_DATA MODULE_VAR_EXPORT
   62 #define apr_array_header_t     array_header
   63 #define apr_base64_decode      ap_base64decode
   64 #define apr_base64_decode_len  ap_base64decode_len
   65 #define apr_base64_encode      ap_base64encode
   66 #define apr_base64_encode_len  ap_base64encode_len
   67 #define apr_pcalloc            ap_pcalloc
   68 #define apr_pool_t             pool
   69 #define apr_pstrcat            ap_pstrcat
   70 #define apr_pstrdup            ap_pstrdup
   71 #define apr_table_add          ap_table_add
   72 #define apr_table_get          ap_table_get
   73 #define apr_table_set          ap_table_set
   74 #define APR_SUCCESS            0
   75 extern int apr_env_set(const char *envvar,
   76     const char *value,
   77     pool * pool);
   78 #endif
   79 
   80 /* Apache and MIT GSS-API define strcasecmp and strncasecmp. */
   81 
   82 #ifdef HAVE_SSPI
   83 #ifndef WIN32_LEAN_AND_MEAN
   84 #define WIN32_LEAN_AND_MEAN
   85 #endif
   86 #define _UNICODE
   87 #ifdef UNICODE
   88 #undef UNICODE
   89 #endif
   90 #include <winsock2.h>
   91 #define SECURITY_WIN32
   92 #include <security.h>
   93 #include <schannel.h>
   94 #define eMessage(x,y)   FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, \
   95                                       NULL, x, 0, y, 0, NULL);
   96 #else
   97 #undef strcasecmp
   98 #undef strncasecmp
   99 
  100 #ifdef HEIMDAL
  101 #include <gssapi.h>
  102 #define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE
  103 #elif defined(SEAM)
  104 #include <gssapi/gssapi.h>
  105 #define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE
  106 #else
  107 #include <gssapi/gssapi_generic.h>
  108 #endif
  109 
  110 #ifndef HAVE_SPNEGO
  111 #include "krb5help.h"
  112 #include "spnegohelp.h"
  113 #endif
  114 #endif
  115 #include <errno.h>
  116 
  117 typedef struct DIRECTORY_CONFIG_st {
  118     const char *krb5KeyTabFile;
  119     const char *krb5ServiceName;
  120     int krb5AuthorizeFlag;
  121     int krb5RemoveDomain;
  122 } DIRECTORY_CONFIG;
  123 
  124 typedef struct SERVER_CONFIG_st {
  125     /* krb5AuthEachReq is not used by Apache 1.3. */
  126 
  127     int krb5AuthEachReq;
  128 } SERVER_CONFIG;
  129 
  130 #ifndef APACHE13
  131 static char *connectionUser = NULL;
  132 #endif
  133 
  134 module AP_MODULE_DECLARE_DATA spnego_module;
  135 
  136 /* -----------------------------------------------------------------------------
  137  * logGssApiError writes GSS-API messages to Apache's error log.
  138  *
  139  * Returns nothing.
  140  * -----------------------------------------------------------------------------
  141  */
  142 
  143 #ifdef HAVE_SSPI
  144 static void
  145 logSSPIError(const char *file,
  146     int line,
  147     int level,
  148     const request_rec * r,
  149     char *s,
  150     SECURITY_STATUS maj_stat)
  151 {
  152     LPSTR pBuffer = NULL;
  153 
  154     eMessage(maj_stat, (LPSTR) & pBuffer);
  155 
  156 #ifdef APACHE13
  157     ap_log_rerror(file, line, level, r, "%s; SSPI: %s(%d)", s, (char *) pBuffer ? pBuffer : "", maj_stat);
  158 #else
  159     ap_log_rerror(file, line, level, 0, r, "%s; SSPI: %s(%d)", s, (char *) pBuffer ? pBuffer : "", maj_stat);
  160 #endif
  161 
  162     if (pBuffer)
  163     LocalFree(pBuffer);
  164 
  165 }
  166 #else
  167 static void
  168 logGssApiError(const char *file,
  169     int line,
  170     int level,
  171     const request_rec * r,
  172     char *s,
  173     OM_uint32 maj_stat,
  174     OM_uint32 min_stat)
  175 {
  176     OM_uint32 gmaj_stat;
  177     OM_uint32 gmin_stat;
  178     gss_buffer_desc msg;
  179     OM_uint32 msg_ctx = 0;
  180     char *p;
  181 
  182     while (!msg_ctx) {
  183     gmaj_stat = gss_display_status(&gmin_stat,
  184         maj_stat,
  185         GSS_C_GSS_CODE,
  186         GSS_C_NULL_OID,
  187         &msg_ctx,
  188         &msg);
  189 
  190     if (gmaj_stat == GSS_S_COMPLETE) {
  191         p = msg.value + msg.length;
  192         *p = '\0';
  193 #ifdef APACHE13
  194         ap_log_rerror(file, line, level, r, "%s; GSS-API: %s", s, (char *) msg.value);
  195 #else
  196         ap_log_rerror(file, line, level, 0, r, "%s; GSS-API: %s", s, (char *) msg.value);
  197 #endif
  198         gss_release_buffer(&gmin_stat, &msg);
  199         break;
  200     }
  201     gss_release_buffer(&gmin_stat, &msg);
  202     }
  203 
  204     msg_ctx = 0;
  205 
  206     while (!msg_ctx) {
  207     gmaj_stat = gss_display_status(&gmin_stat,
  208         min_stat,
  209         GSS_C_MECH_CODE,
  210         GSS_C_NULL_OID,
  211         &msg_ctx,
  212         &msg);
  213 
  214     if (gmaj_stat == GSS_S_COMPLETE) {
  215         p = msg.value + msg.length;
  216         *p = '\0';
  217 #ifdef APACHE13
  218         ap_log_rerror(file, line, level, r, "%s; GSS-API mechanism: %s", s, (char *) msg.value);
  219 #else
  220         ap_log_rerror(file, line, level, 0, r, "%s; GSS-API mechanism: %s", s, (char *) msg.value);
  221 #endif
  222         gss_release_buffer(&gmin_stat, &msg);
  223         break;
  224     }
  225     gss_release_buffer(&gmin_stat, &msg);
  226     }
  227 }
  228 #endif
  229 
  230 /* -----------------------------------------------------------------------------
  231  * handlePoolCleanup handles pool cleanup.
  232  *
  233  * Is registered by calling apr_pool_cleanup_register. Is called by Apache.
  234  *
  235  * Returns an Apache status code.
  236  * -----------------------------------------------------------------------------
  237  */
  238 
  239 #ifndef APACHE13
  240 static apr_status_t
  241 handlePoolCleanup(void *data)
  242 {
  243     connectionUser = NULL;
  244     return APR_SUCCESS;
  245 }
  246 #endif
  247 
  248 #ifdef HAVE_SSPI
  249 static int
  250 handleSpnegoTokenSSPI(request_rec * r,
  251     const unsigned char *inputToken,
  252     size_t inputTokenLength,
  253     unsigned char **outputToken,
  254     size_t * outputTokenLength)
  255 {
  256     char *ServiceName = NULL;
  257     char *Krb5ServiceName = NULL;
  258     CredHandle server_creds;
  259     SecBufferDesc input_desc;
  260     SecBufferDesc output_desc;
  261     SecBuffer send_tok, recv_tok;
  262     SecPkgContext_Names names;
  263     CtxtHandle new_context;
  264     static PCtxtHandle context = NULL;
  265     DIRECTORY_CONFIG *directoryConfig = ap_get_module_config(r->per_dir_config, &spnego_module);
  266     ULONG global_asc_flags = ASC_REQ_ALLOCATE_MEMORY | ASC_REQ_MUTUAL_AUTH;
  267     ULONG ret_flags = 0;
  268     SECURITY_STATUS maj_stat;
  269     TimeStamp expiry;
  270 
  271     errno = 0;
  272     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: entering handleSpnegoTokenSSPI");
  273     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: KRB5 service name is %s", directoryConfig->krb5ServiceName ? directoryConfig->krb5ServiceName : "NULL");
  274 
  275     new_context.dwUpper = 0;
  276     new_context.dwLower = 0;
  277 
  278     input_desc.cBuffers = 1;
  279     input_desc.ulVersion = SECBUFFER_VERSION;
  280     input_desc.pBuffers = &recv_tok;
  281 
  282     output_desc.cBuffers = 1;
  283     output_desc.ulVersion = SECBUFFER_VERSION;
  284     output_desc.pBuffers = &send_tok;
  285 
  286     recv_tok.cbBuffer = (unsigned long) inputTokenLength;
  287     recv_tok.BufferType = SECBUFFER_TOKEN;
  288     recv_tok.pvBuffer = malloc(inputTokenLength);
  289 
  290     if (!recv_tok.pvBuffer) {
  291     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_ERR, r, "mod_spnego: mcalloc failed for recv_tok.pvBuffer");
  292     return HTTP_INTERNAL_SERVER_ERROR;
  293     }
  294     memcpy(recv_tok.pvBuffer, inputToken, inputTokenLength);
  295 
  296 #ifdef APACHE13
  297     Krb5ServiceName = apr_pstrdup(r->connection->pool, directoryConfig->krb5ServiceName);
  298 #else
  299     Krb5ServiceName = apr_pstrdup(r->pool, directoryConfig->krb5ServiceName);
  300 #endif
  301     ServiceName = strtok(Krb5ServiceName, " ");
  302 
  303     while (ServiceName != NULL) {
  304     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: Try service name %s", ServiceName);
  305     maj_stat = AcquireCredentialsHandle(
  306         ServiceName,
  307         "Negotiate",
  308         SECPKG_CRED_INBOUND,
  309         NULL,       // no logon id
  310          NULL,      // no auth data
  311          NULL,      // no get key fn
  312          NULL,      // no get key arg
  313          &server_creds,
  314         &expiry
  315         );
  316     if (maj_stat != SEC_E_OK) {
  317         logSSPIError(APLOG_MARK, APLOG_ERR, r, "mod_spnego: AcquireCredentialsHandle failed", maj_stat);
  318         continue;
  319     }
  320     send_tok.cbBuffer = 0;
  321     send_tok.pvBuffer = NULL;
  322     send_tok.BufferType = SECBUFFER_TOKEN;
  323     maj_stat = AcceptSecurityContext(&server_creds,
  324         context,
  325         &input_desc,
  326         global_asc_flags,
  327         SECURITY_NATIVE_DREP,
  328         &new_context,
  329         &output_desc,
  330         &ret_flags,
  331         &expiry
  332         );
  333 
  334     FreeContextBuffer(recv_tok.pvBuffer);
  335     recv_tok.pvBuffer = NULL;
  336 
  337     if (maj_stat != SEC_E_OK && maj_stat != SEC_I_CONTINUE_NEEDED) {
  338         logSSPIError(APLOG_MARK, APLOG_ERR, r, "mod_spnego: AcceptSecurityContext failed", maj_stat);
  339         ServiceName = strtok(NULL, " ");
  340         continue;
  341     } else {
  342         if (send_tok.cbBuffer != 0) {
  343         *outputTokenLength = send_tok.cbBuffer;
  344         *outputToken = (char *) malloc(*outputTokenLength);
  345         if (!*outputToken) {
  346             ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_ERR, r, "mod_spnego: mcalloc failed for *outputToken");
  347             DeleteSecurityContext(context);
  348             context = NULL;
  349             return HTTP_INTERNAL_SERVER_ERROR;
  350         }
  351         memcpy(*outputToken, send_tok.pvBuffer, send_tok.cbBuffer);
  352         }
  353         FreeContextBuffer(send_tok.pvBuffer);
  354         send_tok.pvBuffer = NULL;
  355         break;
  356     }
  357 
  358     }
  359     if (!ServiceName) {
  360     logSSPIError(APLOG_MARK, APLOG_ERR, r, "mod_spnego: handleSpnegoTokenSSPI failed", SEC_E_INTERNAL_ERROR);
  361     DeleteSecurityContext(context);
  362     context = NULL;
  363     return HTTP_INTERNAL_SERVER_ERROR;
  364     }
  365     if (maj_stat == SEC_E_OK) {
  366     maj_stat = QueryContextAttributes(&new_context, SECPKG_ATTR_NAMES, &names);
  367     DeleteSecurityContext(&new_context);
  368     if (maj_stat != SEC_E_OK) {
  369         logSSPIError(APLOG_MARK, APLOG_ERR, r, "mod_spnego: QueryContextAttributes failed", maj_stat);
  370         DeleteSecurityContext(context);
  371         context = NULL;
  372         return HTTP_INTERNAL_SERVER_ERROR;
  373     }
  374 #ifdef APACHE13
  375     r->PORTABLE_USER = apr_pstrdup(r->connection->pool, names.sUserName);
  376 #else
  377     r->PORTABLE_USER = apr_pstrdup(r->pool, names.sUserName);
  378 #endif
  379     if (directoryConfig->krb5RemoveDomain) {
  380         char *p;
  381         if ((p = strchr(r->PORTABLE_USER, '\\')) != NULL) {
  382         p++;
  383         r->PORTABLE_USER = p;
  384         }
  385     }
  386 #ifndef APACHE13
  387     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: setting connection user to %s", (char *) r->PORTABLE_USER);
  388     connectionUser = apr_pstrdup(r->connection->pool, r->PORTABLE_USER);
  389     apr_pool_cleanup_register(r->connection->pool, NULL, handlePoolCleanup, apr_pool_cleanup_null);
  390 #endif
  391     DeleteSecurityContext(context);
  392     context = NULL;
  393     return OK;
  394     } else {
  395     if (maj_stat == SEC_I_CONTINUE_NEEDED) {
  396         DeleteSecurityContext(context);
  397         context = malloc(sizeof(CtxtHandle));
  398         if (!context) {
  399         ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_ERR, r, "mod_spnego: malloc failed for context");
  400         return HTTP_INTERNAL_SERVER_ERROR;
  401         }
  402         memcpy(context, &new_context, sizeof(CtxtHandle));
  403     } else {
  404         DeleteSecurityContext(context);
  405         context = NULL;
  406     }
  407     return HTTP_UNAUTHORIZED;
  408     }
  409 
  410 }
  411 #else
  412 #ifdef HAVE_SPNEGO
  413 #define HANDLE_TOKEN_FUNCTION "handleSpnegoToken"
  414 /* -----------------------------------------------------------------------------
  415  * handleSpnegoToken handles an RFC 2478 SPNEGO GSS-API token.
  416  *
  417  * If handleSpnegoToken is successful, call free (outputToken), where
  418  * outputToken is of type unsigned char *, to free the memory allocated by
  419  * handleSpnegoToken.
  420  *
  421  * Returns an Apache response code.
  422  * -----------------------------------------------------------------------------
  423  */
  424 
  425 static int
  426 handleSpnegoToken(request_rec * r,
  427     const unsigned char *inputToken,
  428     size_t inputTokenLength,
  429     unsigned char **outputToken,
  430     size_t * outputTokenLength)
  431 #else
  432 #define HANDLE_TOKEN_FUNCTION "handleKerberosToken"
  433 /* -----------------------------------------------------------------------------
  434  * handleKerberosToken handles an RFC 1964 Kerberos GSS-API token.
  435  *
  436  * Returns an Apache response code.
  437  * -----------------------------------------------------------------------------
  438  */
  439 
  440 static int
  441 handleKerberosToken(request_rec * r,
  442     const unsigned char *inputToken,
  443     size_t inputTokenLength,
  444     unsigned char **outputToken,
  445     size_t * outputTokenLength)
  446 #endif
  447 {
  448     gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER;
  449     gss_buffer_desc serviceBuffer = GSS_C_EMPTY_BUFFER;
  450     gss_name_t clientName = GSS_C_NO_NAME;
  451     static gss_ctx_id_t context = GSS_C_NO_CONTEXT;
  452     gss_cred_id_t credential = GSS_C_NO_CREDENTIAL;
  453     DIRECTORY_CONFIG *directoryConfig = ap_get_module_config(r->per_dir_config, &spnego_module);
  454     gss_buffer_desc gssInputToken = GSS_C_EMPTY_BUFFER;
  455     OM_uint32 majorStatus;
  456     OM_uint32 minorStatus1;
  457     OM_uint32 minorStatus2;
  458     gss_buffer_desc gssOutputToken = GSS_C_EMPTY_BUFFER;
  459     int rc = OK;
  460     gss_name_t serverName = GSS_C_NO_NAME;
  461     char *ServiceName = NULL;
  462     char *Krb5ServiceName = NULL;
  463 
  464     errno = 0;
  465     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: entering %s", HANDLE_TOKEN_FUNCTION);
  466     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: KRB5 service name is %s", directoryConfig->krb5ServiceName ? directoryConfig->krb5ServiceName : "NULL");
  467 
  468 #ifdef APACHE13
  469     Krb5ServiceName = apr_pstrdup(r->connection->pool, directoryConfig->krb5ServiceName);
  470 #else
  471     Krb5ServiceName = apr_pstrdup(r->pool, directoryConfig->krb5ServiceName);
  472 #endif
  473     ServiceName = strtok(Krb5ServiceName, " ");
  474     while (ServiceName != NULL) {
  475 #ifdef APACHE13
  476     serviceBuffer.value = apr_pstrdup(r->connection->pool, ServiceName);
  477 #else
  478     serviceBuffer.value = apr_pstrdup(r->pool, ServiceName);
  479 #endif
  480     serviceBuffer.length = strlen(serviceBuffer.value);
  481     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: service name %s selected", ServiceName);
  482 /*
  483  * Check if service contains a '/' to determine if this is a Kerberos service principal name.
  484  * If so don't do name canonicalisation as would be done for a gss service name.
  485  * This should allow Virtual hosts. 
  486  */
  487     if (!strcasecmp(ServiceName, "GSS_C_NO_NAME")) {
  488         serverName = GSS_C_NO_NAME;
  489         majorStatus = GSS_S_COMPLETE;
  490         ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: Use GSS_C_NO_NAME");
  491     } else if (strchr(ServiceName, '/')) {
  492         majorStatus = gss_import_name(&minorStatus1,
  493         &serviceBuffer,
  494         (gss_OID) GSS_C_NULL_OID,
  495         &serverName);
  496     } else {
  497         majorStatus = gss_import_name(&minorStatus1,
  498         &serviceBuffer,
  499         (gss_OID) gss_nt_service_name,
  500         &serverName);
  501     }
  502     ServiceName = strtok(NULL, " ");
  503 
  504     if (majorStatus != GSS_S_COMPLETE) {
  505         logGssApiError(APLOG_MARK, APLOG_ERR, r, "mod_spnego: gss_import_name failed", majorStatus, minorStatus1);
  506         return HTTP_INTERNAL_SERVER_ERROR;
  507     }
  508     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: gss_import_name succeeded");
  509 
  510     /* Optionally, set environment variable KRB5_KTNAME. */
  511 
  512     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: KRB5 key tab file is %s", directoryConfig->krb5KeyTabFile ? directoryConfig->krb5KeyTabFile : "NULL");
  513 
  514     if (directoryConfig->krb5KeyTabFile) {
  515         if (apr_env_set("KRB5_KTNAME", directoryConfig->krb5KeyTabFile, r->pool) != APR_SUCCESS)
  516         ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_ERR, r, "mod_spnego: unable to set KRB5_KTNAME to %s", directoryConfig->krb5KeyTabFile);
  517         else
  518 #ifdef HEIMDAL
  519         gsskrb5_register_acceptor_identity(directoryConfig->krb5KeyTabFile);
  520 #endif
  521         ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: set KRB5_KTNAME to %s", directoryConfig->krb5KeyTabFile);
  522     }
  523     majorStatus = gss_acquire_cred(&minorStatus1,
  524         serverName,
  525         0,
  526         GSS_C_NULL_OID_SET,
  527         GSS_C_ACCEPT,
  528         &credential,
  529         NULL,
  530         NULL);
  531 
  532     if (serverName != GSS_C_NO_NAME) {
  533         gss_release_name(&minorStatus2, &serverName);
  534         serverName = GSS_C_NO_NAME;
  535         ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: released server name");
  536     }
  537     if (majorStatus != GSS_S_COMPLETE) {
  538         logGssApiError(APLOG_MARK, APLOG_ERR, r, "mod_spnego: gss_acquire_cred failed", majorStatus, minorStatus1);
  539         rc = HTTP_INTERNAL_SERVER_ERROR;
  540         continue;
  541     }
  542     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: gss_acquire_cred succeeded");
  543 
  544     /*
  545      * Normally, gss_accept_sec_context returns GSS_S_CONTINUE_NEEDED if it needs
  546      * to be called again. In this context, gss_accept_sec_context should not
  547      * return GSS_S_CONTINUE_NEEDED. If gss_accept_sec_context returns
  548      * GSS_S_CONTINUE_NEEDED, the decision to release clientName is based on it's
  549      * value (GSS_C_NO_NAME or not GSS_C_NO_NAME) and gssOutputToken is delegated to
  550      * gss_release_buffer -- RFC 2744 is not clear about this case.
  551      */
  552 
  553     gssInputToken.value = (unsigned char *) inputToken;
  554     gssInputToken.length = inputTokenLength;
  555 
  556     if (context != GSS_C_NO_CONTEXT)
  557         ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: gss_accept_sec_context continuation");
  558 
  559     majorStatus = gss_accept_sec_context(&minorStatus1,
  560         &context,
  561         credential,
  562         &gssInputToken,
  563         GSS_C_NO_CHANNEL_BINDINGS,
  564         &clientName,
  565         NULL,
  566         &gssOutputToken,
  567         NULL,
  568         NULL,
  569         NULL);
  570 
  571     if (credential != GSS_C_NO_CREDENTIAL) {
  572         gss_release_cred(&minorStatus2, &credential);
  573         credential = GSS_C_NO_CREDENTIAL;
  574         ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: released credential");
  575     }
  576     if (majorStatus == GSS_S_CONTINUE_NEEDED)
  577         ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: gss_accept_sec_context continuation needed");
  578 
  579     if (majorStatus != GSS_S_COMPLETE && majorStatus != GSS_S_CONTINUE_NEEDED) {
  580         if (context != GSS_C_NO_CONTEXT) {
  581         gss_delete_sec_context(&minorStatus2, &context, GSS_C_NO_BUFFER);
  582         context = GSS_C_NO_CONTEXT;
  583         ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: released context");
  584         }
  585         logGssApiError(APLOG_MARK, APLOG_ERR, r, "mod_spnego: gss_accept_sec_context failed", majorStatus, minorStatus1);
  586         rc = HTTP_INTERNAL_SERVER_ERROR;
  587         continue;
  588     }
  589     if (majorStatus == GSS_S_COMPLETE) {
  590         ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: gss_accept_sec_context succeeded");
  591 
  592         /* Pass client (or user) name to authorization hook. */
  593 
  594         majorStatus = gss_display_name(&minorStatus1, clientName, &buffer, NULL);
  595 
  596         if (majorStatus != GSS_S_COMPLETE) {
  597         logGssApiError(APLOG_MARK, APLOG_ERR, r, "mod_spnego: gss_display_name failed", majorStatus, minorStatus1);
  598         rc = HTTP_INTERNAL_SERVER_ERROR;
  599         if (context != GSS_C_NO_CONTEXT) {
  600             gss_delete_sec_context(&minorStatus2, &context, GSS_C_NO_BUFFER);
  601             context = GSS_C_NO_CONTEXT;
  602             ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: released context");
  603         }
  604         goto cleanup;
  605         }
  606         /*
  607          * http://httpd.apache.org/docs/misc/API.html says, "You can also see how some
  608          * bugs have manifested themself, such as setting connection->user to a value
  609          * from r->pool -- in this case connection exists for the lifetime of ptrans,
  610          * which is longer than r->pool (especially if r->pool is a subrequest!). So
  611          * the correct thing to do is to allocate from connection->pool." Apache 1.3
  612          * stores user in connection substructure of request_rec. Apache 2.0 stores
  613          * user in request_rec.
  614          */
  615 #ifdef APACHE13
  616         r->PORTABLE_USER = apr_pstrdup(r->connection->pool, buffer.value);
  617 #else
  618         r->PORTABLE_USER = apr_pstrdup(r->pool, buffer.value);
  619 #endif
  620         if (directoryConfig->krb5RemoveDomain) {
  621         char *p;
  622         if ((p = strchr(r->PORTABLE_USER, '@')) != NULL) {
  623             *p = '\0';
  624         }
  625         }
  626 #ifndef APACHE13
  627         ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: setting connection user to %s", (char *) r->PORTABLE_USER);
  628         connectionUser = apr_pstrdup(r->connection->pool, r->PORTABLE_USER);
  629         apr_pool_cleanup_register(r->connection->pool, NULL, handlePoolCleanup, apr_pool_cleanup_null);
  630 #endif
  631 
  632         gss_release_buffer(&minorStatus1, &buffer);
  633         if (context != GSS_C_NO_CONTEXT) {
  634         gss_delete_sec_context(&minorStatus2, &context, GSS_C_NO_BUFFER);
  635         context = GSS_C_NO_CONTEXT;
  636         ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: released context");
  637         }
  638     }
  639     if (gssOutputToken.length) {
  640 
  641         *outputToken = malloc((int) gssOutputToken.length);
  642 
  643         if (!*outputToken) {
  644         ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_ERR, r, "mod_spnego: apr_pcalloc failed");
  645         rc = HTTP_INTERNAL_SERVER_ERROR;
  646         goto cleanup;
  647         }
  648         memcpy(*outputToken, gssOutputToken.value, gssOutputToken.length);
  649         *outputTokenLength = gssOutputToken.length;
  650     }
  651     if (majorStatus == GSS_S_COMPLETE)
  652         rc = OK;
  653     else
  654         rc = HTTP_UNAUTHORIZED;
  655     goto cleanup;
  656     }
  657   cleanup:
  658 
  659     if (clientName != GSS_C_NO_NAME) {
  660     gss_release_name(&minorStatus1, &clientName);
  661     clientName = GSS_C_NO_NAME;
  662     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: released client name");
  663     }
  664     gss_release_buffer(&minorStatus1, &gssOutputToken);
  665     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: released output token");
  666     return rc;
  667 }
  668 
  669 #ifndef HAVE_SPNEGO
  670 /* -----------------------------------------------------------------------------
  671  * handleSpnegoToken handles an RFC 2478 SPNEGO GSS-API token.
  672  *
  673  * If handleSpnegoToken is successful, call free (outputSpnegoToken), where
  674  * outputSpnegoToken is of type unsigned char *, to free the memory allocated by
  675  * handleSpnegoToken.
  676  *
  677  * Returns an Apache response code.
  678  * -----------------------------------------------------------------------------
  679  */
  680 
  681 static int
  682 handleSpnegoToken(request_rec * r,
  683     const unsigned char *inputSpnegoToken,
  684     size_t inputSpnegoTokenLength,
  685     unsigned char **outputSpnegoToken,
  686     size_t * outputSpnegoTokenLength)
  687 {
  688     /* Patch by Frank Taylor */
  689     int brokenOID = 0;
  690     unsigned char *inputKerberosToken = NULL;
  691     size_t inputKerberosTokenLength = 0;
  692     long negResult;
  693     unsigned char *outputKerberosToken = NULL;
  694     size_t outputKerberosTokenLength = 0;
  695     int rc;
  696 
  697     errno = 0;
  698     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: entering handleSpnegoToken");
  699 
  700     if (!parseSpnegoInitialToken(inputSpnegoToken,
  701         inputSpnegoTokenLength,
  702         &krb5GssApi,
  703         &inputKerberosToken,
  704         &inputKerberosTokenLength)) {
  705     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_ERR, r, "mod_spnego: parseSpnegoInitialToken failed for 1.2.840.113554.1.2.2");
  706 
  707     /*
  708      * The correct mechanism OID does not work, let's just check the broken MS
  709      * one in case this is an old W2K client. - Frank Taylor
  710      */
  711 
  712     if (inputKerberosToken)
  713         free(inputKerberosToken);
  714     inputKerberosToken = NULL;
  715     if (!parseSpnegoInitialToken(inputSpnegoToken,
  716         inputSpnegoTokenLength,
  717         &msKrb5GssApiLegacy,
  718         &inputKerberosToken,
  719         &inputKerberosTokenLength)) {
  720         ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_ERR, r, "mod_spnego: parseSpnegoInitialToken failed for 1.2.840.48018.1.2.2");
  721 
  722         /*
  723          * Ideally, handleKerberosToken should be called by authenticateUser,
  724          * not by handleSpnegoToken. For more information, see comment in
  725          * authenticateUser.
  726          */
  727 
  728         rc = handleKerberosToken(r,
  729         inputSpnegoToken,
  730         inputSpnegoTokenLength,
  731         outputSpnegoToken,
  732         outputSpnegoTokenLength);
  733         ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: handleKerberosToken returned %d", rc);
  734         if (inputKerberosToken)
  735         free(inputKerberosToken);
  736         inputKerberosToken = NULL;
  737         return rc;
  738     }
  739     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: parseSpnegoInitialToken succeeded for 1.2.840.48018.1.2.2 -- probably Windows 2000 client");
  740     brokenOID = 1;
  741     }
  742     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: parseSpnegoInitialToken succeeded");
  743     rc = handleKerberosToken(r,
  744     inputKerberosToken,
  745     inputKerberosTokenLength,
  746     &outputKerberosToken,
  747     &outputKerberosTokenLength);
  748     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: handleKerberosToken returned %d", rc);
  749 
  750     if (inputKerberosToken)
  751     free(inputKerberosToken);
  752 
  753     negResult = 0;
  754 
  755     if (!makeSpnegoTargetToken(&negResult,
  756     /* Frank Taylor */
  757         (brokenOID ? &msKrb5GssApiLegacy : &krb5GssApi),
  758         outputKerberosToken,
  759         outputKerberosTokenLength,
  760         NULL,
  761         0,
  762         outputSpnegoToken,
  763         outputSpnegoTokenLength)) {
  764     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_ERR, r, "mod_spnego: makeSpnegoTargetToken failed");
  765     return HTTP_INTERNAL_SERVER_ERROR;
  766     }
  767     return rc;
  768 }
  769 #endif
  770 #endif
  771 
  772 /* -----------------------------------------------------------------------------
  773  * authenticateUser authenticates a user (or Apache client).
  774  *
  775  * Returns an Apache response code.
  776  * -----------------------------------------------------------------------------
  777  */
  778 
  779 static int
  780 authenticateUser(request_rec * r)
  781 {
  782     static unsigned char ntlmProtocol[] =
  783     {'N', 'T', 'L', 'M', 'S', 'S', 'P', 0};
  784 
  785     const char *authType = NULL;
  786     const char *authValue = NULL;
  787     unsigned char *inputToken = NULL;
  788     size_t inputTokenLength = 0;
  789     unsigned char *outputToken = NULL;
  790     size_t outputTokenLength = 0;
  791     int rc;
  792     SERVER_CONFIG *serverConfig = ap_get_module_config(r->server->module_config, &spnego_module);
  793     char *string = NULL;
  794 
  795     errno = 0;
  796     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: entering authenticateUser");
  797 
  798     /* Check AuthType. */
  799 
  800     authType = ap_auth_type(r);
  801 
  802     if (!authType || strcasecmp(authType, "SPNEGO")) {
  803     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: unrecognized AuthType \'%s\'", authType ? authType : "NULL");
  804     return DECLINED;
  805     }
  806     /*
  807      * mod_auth_digest calls ap_is_initial_req for ap_hook_post_read_request hook
  808      * function and returns DECLINED if ap_is_initial_req returns 0.
  809      * 
  810      * Returning DECLINED if ap_is_initial_req returns 0 caused the following error:
  811      * 
  812      * [Mon Aug 04 20:54:29 2003] [crit] [client 10.155.131.145] configuration error:couldn't check user.  No user file?: /index.html
  813      */
  814 
  815     if (!ap_is_initial_req(r) && !connectionUser) {
  816     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: ap_is_initial_req returned 0");
  817 #ifdef APACHE13
  818     rc = OK;
  819 #else
  820     rc = HTTP_UNAUTHORIZED;
  821 #endif
  822     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: authenticateUser returning %d", rc);
  823     return rc;
  824     } else if (!ap_is_initial_req(r) && connectionUser) {
  825     if (!r->PORTABLE_USER) {
  826 #ifdef APACHE13
  827         r->PORTABLE_USER = apr_pstrdup(r->connection->pool, connectionUser);
  828 #else
  829         r->PORTABLE_USER = apr_pstrdup(r->pool, connectionUser);
  830 #endif
  831     }
  832     return OK;
  833     }
  834 #ifndef APACHE13
  835     if (!serverConfig->krb5AuthEachReq && connectionUser) {
  836     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: setting request user to %s", connectionUser);
  837     r->PORTABLE_USER = apr_pstrdup(r->pool, connectionUser);
  838     return OK;
  839     }
  840 #endif
  841 
  842     authValue = apr_table_get(r->headers_in, "Authorization");
  843 
  844     if (!authValue) {
  845     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: sending 401 and \"WWW-Authenticate: Negotiate\"");
  846     apr_table_add(r->err_headers_out, "WWW-Authenticate", "Negotiate");
  847     return HTTP_UNAUTHORIZED;
  848     }
  849     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: Authorization value is \"%s\"", authValue);
  850     string = ap_getword_white(r->pool, &authValue);
  851 
  852     if (!strncasecmp(string, "Negotiate", 9)) {
  853     string = ap_getword_white(r->pool, &authValue);
  854 
  855     if (!string)
  856         return HTTP_UNAUTHORIZED;
  857 
  858     inputTokenLength = apr_base64_decode_len(string);
  859 
  860 #ifdef APACHE13
  861     inputToken = apr_pcalloc(r->pool,
  862         (int) inputTokenLength);
  863 #else
  864     inputToken = apr_pcalloc(r->pool,
  865         inputTokenLength);
  866 #endif
  867 
  868     if (!inputToken) {
  869         ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_ERR, r, "mod_spnego: apr_pcalloc failed");
  870         return HTTP_INTERNAL_SERVER_ERROR;
  871     }
  872     inputTokenLength = apr_base64_decode((char *) inputToken, string);
  873     string = NULL;
  874 
  875 #ifdef HAVE_SSPI
  876     rc = handleSpnegoTokenSSPI(r,
  877         inputToken,
  878         inputTokenLength,
  879         &outputToken,
  880         &outputTokenLength);
  881 
  882     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: handleSpnegoTokenSSPI returned %d", rc);
  883 #else
  884     /* Check if NTLM token. */
  885 
  886     if (inputTokenLength >= sizeof ntlmProtocol + 1)
  887         if (!memcmp(inputToken, ntlmProtocol, sizeof ntlmProtocol)) {
  888         ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_ERR, r, "mod_spnego: received type %d NTLM token", (int) *(inputToken + sizeof ntlmProtocol));
  889         return HTTP_UNAUTHORIZED;
  890         }
  891     /*
  892      * Ideally, authenticateUser should parse RFC 2743 InitialContextTokens,
  893      * look at mechType element and call handleSpnegoToken or
  894      * handleKerberosToken. Because d2i_GSSAPI_INITIAL_CONTEXT_TOKEN fails to
  895      * parse RFC 1964 Kerberos tokens (they contain non-DER token IDs after
  896      * mechType element), handleSpnegoToken passes input tokens to
  897      * handleKerberosToken if parseSpnegoInitialToken fails.
  898      */
  899 
  900     rc = handleSpnegoToken(r,
  901         inputToken,
  902         inputTokenLength,
  903         &outputToken,
  904         &outputTokenLength);
  905 
  906     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: handleSpnegoToken returned %d", rc);
  907 #endif
  908     string = apr_pcalloc(r->pool, apr_base64_encode_len((int) outputTokenLength));
  909 
  910     if (!string) {
  911         ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_ERR, r, "mod_spnego: apr_pcalloc failed");
  912         if (outputToken) {
  913         free(outputToken);
  914         outputToken = NULL;
  915         }
  916         return HTTP_INTERNAL_SERVER_ERROR;
  917     }
  918     apr_base64_encode(string, (const char *) outputToken, (int) outputTokenLength);
  919     if (outputToken) {
  920         free(outputToken);
  921         outputToken = NULL;
  922     }
  923     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: WWW-Authenticate value is \"Negotiate %s\"", string);
  924 
  925     /* Does apr_table_set copy string? */
  926 
  927     apr_table_set(r->err_headers_out,
  928         "WWW-Authenticate",
  929         apr_pstrcat(r->pool, "Negotiate ", string, NULL));
  930     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: authenticateUser returning %d", rc);
  931     return rc;
  932     }
  933     return HTTP_UNAUTHORIZED;
  934 }
  935 
  936 /* -----------------------------------------------------------------------------
  937  * authorizeUser authorizes a user (or Apache client).
  938  *
  939  * Returns an Apache response code.
  940  * -----------------------------------------------------------------------------
  941  */
  942 
  943 static int
  944 authorizeUser(request_rec * r)
  945 {
  946     const char *authType = NULL;
  947     int methodRestricted = 0;
  948     require_line *reqs;
  949     DIRECTORY_CONFIG *directoryConfig = ap_get_module_config(r->per_dir_config, &spnego_module);
  950     const apr_array_header_t *reqs_arr = ap_requires(r);
  951     int rc = OK;
  952     const char *require;
  953     const char *word;
  954     register int i;
  955 
  956     errno = 0;
  957     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: entering authorizeUser");
  958     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: user is %s", r->PORTABLE_USER);
  959 
  960     /* Check AuthType. */
  961     authType = ap_auth_type(r);
  962 
  963     if (!authType || strcasecmp(authType, "SPNEGO")) {
  964     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: unrecognized AuthType \'%s\'", authType ? authType : "NULL");
  965     return DECLINED;
  966     }
  967     if (!directoryConfig->krb5AuthorizeFlag) {
  968     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: Authorize Flag = 0");
  969     return DECLINED;
  970     }
  971     if (!ap_is_initial_req(r) && !r->PORTABLE_USER) {
  972     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: ap_is_initial_req returned 0");
  973 #ifdef APACHE13
  974     rc = OK;
  975 #else
  976     rc = HTTP_UNAUTHORIZED;
  977 #endif
  978     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: authorizeUser returning %d", rc);
  979     return rc;
  980     }
  981     /*
  982      * What does the following check do? The author's testing shows that if Require
  983      * is not defined, authenticateUser and authorizeUser are not called.
  984      */
  985 
  986     if (!reqs_arr) {
  987     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: Require directive not present");
  988     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: authorizeUser returning OK");
  989     return OK;
  990     }
  991     reqs = (require_line *) reqs_arr->elts;
  992 
  993     for (i = 0; i < reqs_arr->nelts; i++) {
  994     require = reqs[i].requirement;
  995     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: Require[%d] %s", i, require);
  996 
  997     if (!(reqs[i].method_mask & (AP_METHOD_BIT << r->method_number))) {
  998         ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "continue");
  999         continue;
 1000     }
 1001     methodRestricted = 1;
 1002     word = ap_getword_white(r->pool, (const char **) &require);
 1003 
 1004     if (!strcmp(word, "valid-user")) {
 1005         ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: authorizeUser returning OK");
 1006         return OK;
 1007     } else if (!strcmp(word, "user")) {
 1008         while (require[0]) {
 1009         word = ap_getword_conf(r->pool, (const char **) &require);
 1010 
 1011         if (!strcmp(r->PORTABLE_USER, word)) {
 1012             ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: authorizeUser returning OK");
 1013             return OK;
 1014         }
 1015         }
 1016     }
 1017     }
 1018 
 1019     if (!methodRestricted) {
 1020     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: authorizeUser returning OK");
 1021     return OK;
 1022     }
 1023     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "access to %s failed, reason: user %s not allowed access", r->uri, r->PORTABLE_USER);
 1024     ap_log_rerror(APLOG_MARK, PORTABLE_APLOG_INFO, r, "mod_spnego: authorizeUser returning HTTP_UNAUTHORIZED");
 1025     return HTTP_UNAUTHORIZED;
 1026 }
 1027 
 1028 /* -----------------------------------------------------------------------------
 1029  * setKrb5AuthEachReq sets the krb5AuthEachReq element in a SERVER_CONFIG
 1030  * structure.
 1031  *
 1032  * Returns NULL.
 1033  * -----------------------------------------------------------------------------
 1034  */
 1035 
 1036 static const char *
 1037 setKrb5AuthEachReq(cmd_parms * cmd,
 1038     void *dummy,
 1039     int krb5AuthEachReq)
 1040 {
 1041     SERVER_CONFIG *serverConfig = ap_get_module_config(cmd->server->module_config,
 1042     &spnego_module);
 1043     printf("mod_spnego: setKrb5AuthEachReq krb5AuthEachReq=%d\n", krb5AuthEachReq);
 1044     serverConfig->krb5AuthEachReq = krb5AuthEachReq;
 1045     return NULL;
 1046 }
 1047 
 1048 #ifndef HAVE_SSPI
 1049 /* -----------------------------------------------------------------------------
 1050  * setKrb5KeyTabFile sets the krb5KeyTabFile element in a DIRECTORY_CONFIG
 1051  * structure.
 1052  *
 1053  * Returns NULL.
 1054  * -----------------------------------------------------------------------------
 1055  */
 1056 
 1057 static const char *
 1058 setKrb5KeyTabFile(cmd_parms * cmd,
 1059     void *config,
 1060     const char *krb5KeyTabFile)
 1061 {
 1062     printf("mod_spnego: setKrb5KeyTabFile krb5KeyTabFile=%s\n", krb5KeyTabFile);
 1063     ((DIRECTORY_CONFIG *) config)->krb5KeyTabFile = apr_pstrdup(cmd->pool, krb5KeyTabFile);
 1064     return NULL;
 1065 }
 1066 #endif
 1067 
 1068 /* -----------------------------------------------------------------------------
 1069  * setKrb5ServiceName sets the krb5ServiceName element in a DIRECTORY_CONFIG
 1070  * structure.
 1071  *
 1072  * Returns NULL.
 1073  * -----------------------------------------------------------------------------
 1074  */
 1075 
 1076 static const char *
 1077 setKrb5ServiceName(cmd_parms * cmd,
 1078     void *config,
 1079     const char *krb5ServiceName)
 1080 {
 1081     printf("mod_spnego: setKrb5ServiceName krb5ServiceName=%s\n", krb5ServiceName);
 1082     ((DIRECTORY_CONFIG *) config)->krb5ServiceName = apr_pstrdup(cmd->pool, krb5ServiceName);
 1083     return NULL;
 1084 }
 1085 
 1086 /* -----------------------------------------------------------------------------
 1087  * setKrb5RemoveDomain sets the krb5RemoveDomain flag in a DIRECTORY_CONFIG
 1088  * structure.
 1089  *
 1090  * Returns NULL.
 1091  * -----------------------------------------------------------------------------
 1092  */
 1093 
 1094 static const char *
 1095 setKrb5RemoveDomain(cmd_parms * cmd,
 1096     void *config,
 1097     const char *krb5RemoveDomain)
 1098 {
 1099     printf("mod_spnego: setKrb5RemoveDomain krb5RemoveDomain=%d\n", atoi(krb5RemoveDomain));
 1100     ((DIRECTORY_CONFIG *) config)->krb5RemoveDomain = atoi(krb5RemoveDomain);
 1101     return NULL;
 1102 }
 1103 
 1104 /* -----------------------------------------------------------------------------
 1105  * setKrb5AuthorizeFlag creates a per directory configuration structure.
 1106  *
 1107  * Returns NULL
 1108  * -----------------------------------------------------------------------------
 1109  */
 1110 
 1111 static const char *
 1112 setKrb5AuthorizeFlag(cmd_parms * cmd,
 1113     void *config,
 1114     const char *krb5AuthorizeFlag)
 1115 {
 1116     printf("mod_spnego: setKrb5AuthorizeFlag krb5AuthorizeFlag=%d\n", atoi(krb5AuthorizeFlag));
 1117     ((DIRECTORY_CONFIG *) config)->krb5AuthorizeFlag = atoi(krb5AuthorizeFlag);
 1118     return NULL;
 1119 }
 1120 
 1121 /* -----------------------------------------------------------------------------
 1122  * createDirConfig creates a per directory configuration structure.
 1123  *
 1124  * Returns a pointer to the configuration structure.
 1125  * -----------------------------------------------------------------------------
 1126  */
 1127 
 1128 static void *
 1129 createDirConfig(apr_pool_t * p,
 1130     char *dir)
 1131 {
 1132     DIRECTORY_CONFIG *directoryConfig = NULL;
 1133 
 1134     printf("mod_spnego: entering createDirConfig for ");
 1135     printf(dir ? dir : "NULL");
 1136     printf("\n");
 1137 
 1138     if (!dir)
 1139     return NULL;
 1140 
 1141     directoryConfig = (DIRECTORY_CONFIG *) apr_pcalloc(p, sizeof(DIRECTORY_CONFIG));
 1142 
 1143     if (!directoryConfig)
 1144     return NULL;
 1145 
 1146     directoryConfig->krb5KeyTabFile = NULL;
 1147     directoryConfig->krb5ServiceName = NULL;
 1148     directoryConfig->krb5AuthorizeFlag = 0;
 1149     directoryConfig->krb5RemoveDomain = 0;
 1150     return directoryConfig;
 1151 }
 1152 
 1153 /* -----------------------------------------------------------------------------
 1154  * createServerConfig creates a per server configuration structure.
 1155  *
 1156  * Returns a pointer to the configuration structure.
 1157  * -----------------------------------------------------------------------------
 1158  */
 1159 
 1160 static void *
 1161 createServerConfig(apr_pool_t * p,
 1162     server_rec * s)
 1163 {
 1164     SERVER_CONFIG *serverConfig = NULL;
 1165 
 1166     printf("mod_spnego: entering createServerConfig\n");
 1167 
 1168     serverConfig = (SERVER_CONFIG *) apr_pcalloc(p, sizeof(SERVER_CONFIG));
 1169 
 1170     if (!serverConfig)
 1171     return NULL;
 1172 
 1173     serverConfig->krb5AuthEachReq = 1;
 1174     return serverConfig;
 1175 }
 1176 
 1177 static const command_rec spnegoDirectives[] =
 1178 {
 1179     /* Krb5AuthEachReq is not applicable to Apache 1.3. */
 1180 
 1181 #ifndef APACHE13
 1182     {"Krb5AuthEachReq",
 1183     (cmd_func) setKrb5AuthEachReq,
 1184     NULL,
 1185     RSRC_CONF,
 1186     FLAG,
 1187     "Require Kerberos V5 authentication for each request."},
 1188 #endif
 1189 
 1190 #ifndef HAVE_SSPI
 1191     {"Krb5KeyTabFile",
 1192 #ifdef APACHE13
 1193     setKrb5KeyTabFile,
 1194 #else
 1195     (cmd_func) setKrb5KeyTabFile,
 1196 #endif
 1197     NULL,
 1198     OR_AUTHCFG,
 1199     TAKE1,
 1200     "Kerberos V5 key tab file."},
 1201 #endif
 1202 
 1203     {"Krb5ServiceName",
 1204 #ifdef APACHE13
 1205     setKrb5ServiceName,
 1206 #else
 1207     (cmd_func) setKrb5ServiceName,
 1208 #endif
 1209     NULL,
 1210     OR_AUTHCFG,
 1211     RAW_ARGS,
 1212     "Kerberos V5 service name."},
 1213 
 1214     {"Krb5AuthorizeFlag",
 1215 #ifdef APACHE13
 1216     setKrb5AuthorizeFlag,
 1217 #else
 1218     (cmd_func) setKrb5AuthorizeFlag,
 1219 #endif
 1220     NULL,
 1221     OR_AUTHCFG,
 1222     RAW_ARGS,
 1223     "Kerberos V5 Authorize Flag."},
 1224 
 1225     {"Krb5RemoveDomain",
 1226 #ifdef APACHE13
 1227     setKrb5RemoveDomain,
 1228 #else
 1229     (cmd_func) setKrb5RemoveDomain,
 1230 #endif
 1231     NULL,
 1232     OR_AUTHCFG,
 1233     RAW_ARGS,
 1234     "Kerberos V5 remove domain from username."},
 1235 
 1236     {NULL}
 1237 };
 1238 
 1239 #ifdef APACHE13
 1240 module MODULE_VAR_EXPORT spnego_module =
 1241 {
 1242     STANDARD_MODULE_STUFF,
 1243     NULL,           /* init */
 1244     createDirConfig,        /* create_dir_config */
 1245     NULL,           /* merge_dir_config */
 1246     createServerConfig,     /* create_server_config */
 1247     NULL,           /* merge_server_config */
 1248     spnegoDirectives,       /* command_rec */
 1249     NULL,           /* handler_rec */
 1250     NULL,           /* translate_handler */
 1251     authenticateUser,       /* ap_check_user_id */
 1252     authorizeUser,      /* auth_checker */
 1253     NULL,           /* access_checker */
 1254     NULL,           /* type_checker */
 1255     NULL,           /* fixer_upper */
 1256     NULL,           /* logger */
 1257     NULL,           /* header_parser */
 1258     NULL,           /* child_init */
 1259     NULL,           /* child_exit */
 1260     NULL            /* post_read_request */
 1261 };
 1262 #else
 1263 
 1264 /* -----------------------------------------------------------------------------
 1265  * registerHooks registers the module's hook functions.
 1266  *
 1267  * Returns nothing.
 1268  * -----------------------------------------------------------------------------
 1269  */
 1270 
 1271 static void
 1272 registerHooks(apr_pool_t * p)
 1273 {
 1274     printf("mod_spnego: entering registerHooks\n");
 1275     ap_hook_check_user_id(authenticateUser, NULL, NULL, APR_HOOK_MIDDLE);
 1276     ap_hook_auth_checker(authorizeUser, NULL, NULL, APR_HOOK_MIDDLE);
 1277 }
 1278 
 1279 module AP_MODULE_DECLARE_DATA spnego_module =
 1280 {
 1281     STANDARD20_MODULE_STUFF,
 1282     createDirConfig,        /* create_dir_config */
 1283     NULL,           /* merge_dir_config */
 1284     createServerConfig,     /* create_server_config */
 1285     NULL,           /* merge_server_config */
 1286     spnegoDirectives,       /* cmds */
 1287     registerHooks       /* register_hooks */
 1288 };
 1289 #endif