"Fossies" - the Fresh Open Source Software Archive

Member "src/modules/extra/mod_auth_gss_krb5.c" (9 May 2005, 30717 Bytes) of package /linux/www/apache_httpd_modules/old/modgssapache-0.0.5.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.

    1 /* 
    2  * This module implements kerberos5 authentication. Both native kerberos 
    3  * (via GSS API) and password-based methods are supported. Hacked browser is 
    4  * needed if you want to use native kerberos5 protocol (a patch for the Mozilla
    5  * browser can be found at http://meta.cesnet.cz/software/heimdal/)
    6  */
    7 
    8 #include "httpd.h"
    9 #include "http_config.h"
   10 #include "http_core.h"
   11 #include "http_protocol.h"
   12 #include "http_log.h"
   13 
   14 #include <sys/socket.h>
   15 #include <netdb.h>
   16 
   17 #ifdef HEIMDAL
   18 #include <gssapi.h>
   19 #else
   20 #include <gssapi/gssapi.h>
   21 #include <gssapi/gssapi_generic.h>
   22 #endif
   23 
   24 
   25 #include <krb5.h>
   26 #ifdef AFS
   27 #include <kafs.h>
   28 #endif
   29 
   30 #ifndef HEIMDAL
   31 #define krb5_get_err_text(context,code) error_message(code)
   32 #endif /* !HEIMDAL */
   33 
   34 
   35 module MODULE_VAR_EXPORT gss_krb5_auth_module;
   36 
   37 #ifdef HEIMDAL
   38 extern krb5_keytab gssapi_krb5_keytab;
   39 #endif
   40 
   41 typedef struct {
   42   /* Directives for the GSSAPI part */
   43   char  *gss_krb5_keytab;  
   44   int    gss_save_creds;
   45   char  *gss_krb5_cells;
   46   int    use_gss_auth;
   47   char  *gss_krb5_realms;
   48   char  *gss_krb5_princs;
   49 
   50   /* Directives for the password verification part */
   51   char *krb5_auth_realm;
   52   char *krb5_keytab;
   53   int krb5_save_creds;
   54   int krb5_forwardable;
   55   char *krb5_cells;
   56 } gss_krb5_config_rec;
   57 
   58 static const char *
   59 krb5_save_cells(cmd_parms *, gss_krb5_config_rec *, char *);
   60 
   61 static const char *
   62 gss_save_cells(cmd_parms *, gss_krb5_config_rec *, char *);
   63 
   64 static const char *
   65 gss_save_realms(cmd_parms *, gss_krb5_config_rec *, char *);
   66 
   67 static const char *
   68 krb5_save_realms(cmd_parms *, gss_krb5_config_rec *, char *);
   69 
   70 static const char *
   71 gss_krb5_save_princs(cmd_parms *, gss_krb5_config_rec *, char *);
   72 
   73 static const command_rec gss_krb5_cmds[] =
   74 {
   75   /* Directives for the GSSAPI part */ 
   76  { "GssKrb5Keytab", ap_set_file_slot,
   77     (void*)XtOffsetOf(gss_krb5_config_rec, gss_krb5_keytab),
   78     OR_AUTHCFG, TAKE1, "Kerberos v5 keytab." },
   79   { "GssKrb5SaveCredentials", ap_set_flag_slot,
   80     (void*)XtOffsetOf(gss_krb5_config_rec, gss_save_creds),
   81     OR_AUTHCFG, FLAG, "Save the v5 credential cache?" },
   82 #ifdef AFS
   83   { "GssKrb5AFSCells", gss_save_cells, NULL,
   84      OR_AUTHCFG, RAW_ARGS, "Create pag and AFS tokens for cells" },
   85 #endif
   86   { "GssSaveCredentials", ap_set_flag_slot,
   87     (void*)XtOffsetOf(gss_krb5_config_rec, gss_save_creds),
   88     OR_AUTHCFG, FLAG, "Save the v5 credential cache?" },
   89   { "GssAuth", ap_set_flag_slot,
   90      (void*)XtOffsetOf(gss_krb5_config_rec, use_gss_auth),
   91      OR_AUTHCFG, FLAG, "Whether to use GSS-API authentication" },
   92   { "GssKrb5AuthRealms", gss_save_realms, NULL,
   93      OR_AUTHCFG, RAW_ARGS, "Kerberos realm(s) in which to authenticate users" },
   94 
   95   /* Directives for the password verification part */
   96   { "KrbAuthRealm", krb5_save_realms, NULL,
   97     OR_AUTHCFG, RAW_ARGS, "Kerberos realm in which to authenticate users." },
   98 
   99    { "Krb5Keytab", ap_set_file_slot,
  100     (void*)XtOffsetOf(gss_krb5_config_rec, krb5_keytab),
  101     OR_AUTHCFG, TAKE1, "Kerberos v5 keytab." },
  102 
  103    { "Krb5SaveCredentials", ap_set_flag_slot,
  104     (void*)XtOffsetOf(gss_krb5_config_rec, krb5_save_creds),
  105     OR_AUTHCFG, FLAG, "Save the v5 credential cache?" },
  106 
  107    { "Krb5Forwardable", ap_set_flag_slot,
  108     (void*)XtOffsetOf(gss_krb5_config_rec, krb5_forwardable),
  109     OR_AUTHCFG, FLAG, "Set tickets to be forwardable?" },
  110 
  111   { "GssKrb5ServicePrincipals", gss_krb5_save_princs, NULL,
  112      OR_AUTHCFG, RAW_ARGS, "Kerberos service(s)  to check against" },
  113 
  114 #ifdef AFS
  115    { "Krb5AFSCells", krb5_save_cells, NULL,
  116     OR_AUTHCFG, RAW_ARGS, "Create pag and AFS tokens for cells" },   
  117 #endif
  118   { NULL, NULL, NULL, 0, 0, NULL}
  119 };
  120 
  121 static const char *
  122 gss_save_cells(cmd_parms *cmd, gss_krb5_config_rec *sec, char *arg)
  123 {
  124    sec->gss_krb5_cells = ap_pstrdup(cmd->pool,arg);
  125    return NULL;
  126 }
  127 
  128 static const char *
  129 krb5_save_cells(cmd_parms *cmd, gss_krb5_config_rec *sec, char *arg)
  130 {
  131    sec->krb5_cells = ap_pstrdup(cmd->pool,arg);
  132    return NULL;
  133 }
  134 
  135 static const char *
  136 gss_save_realms(cmd_parms *cmd, gss_krb5_config_rec *sec, char *arg)
  137 {
  138    sec->gss_krb5_realms = ap_pstrdup(cmd->pool,arg);
  139    return NULL;
  140 }
  141 
  142 static const char *
  143 krb5_save_realms(cmd_parms *cmd, gss_krb5_config_rec *sec, char *arg)
  144 {
  145    sec->krb5_auth_realm = ap_pstrdup(cmd->pool,arg);
  146    return NULL;
  147 }
  148 
  149 static void
  150 krb5_cache_cleanup(void *data)
  151 {
  152    krb5_context context;
  153    krb5_ccache  cache;
  154    krb5_error_code problem;
  155    char *cache_name = (char *) data;
  156 
  157    problem = krb5_init_context(&context);
  158    if (problem) {
  159       ap_log_error(APLOG_MARK, APLOG_ERR, NULL, "krb5_init_context() failed");
  160       return;
  161    }
  162 
  163    problem = krb5_cc_resolve(context, cache_name, &cache);
  164    if (problem) {
  165       ap_log_error(APLOG_MARK, APLOG_ERR, NULL, 
  166                    "krb5_cc_resolve() failed (%s: %s)",
  167                cache_name, krb5_get_err_text(context, problem)); 
  168       return;
  169    }
  170 
  171    krb5_cc_destroy(context, cache);
  172    krb5_free_context(context);
  173 }
  174 
  175 static const char *
  176 gss_krb5_save_princs(cmd_parms *cmd, gss_krb5_config_rec *sec, char *arg)
  177 {
  178    sec->gss_krb5_princs = ap_pstrdup(cmd->pool,arg);
  179    return NULL;
  180 }
  181 static void krb5_dummy_cleanup(void *data) {}
  182 
  183 #ifdef AFS
  184 static void krb5_unlog_cleanup(void *data)
  185 {
  186    if (k_hasafs())
  187       k_unlog();
  188 }
  189 #endif 
  190 
  191 static void
  192 note_auth_failure(request_rec *r, const gss_krb5_config_rec *conf)
  193 {
  194    const char *auth_type = NULL;
  195    const char *auth_name = NULL;
  196 
  197    /* get the type specified in .htaccess */
  198    auth_type = ap_auth_type(r);
  199 
  200    /* get the user realm specified in .htaccess */
  201    auth_name = ap_auth_name(r);
  202 
  203    /* XXX should the WWW-Authenticate header be cleared first? */
  204    if (conf->use_gss_auth)
  205       ap_table_add(r->err_headers_out, "WWW-Authenticate", "Negotiate ");
  206    if (auth_type && strncasecmp(auth_type, "KerberosV5", 10) == 0)
  207       ap_table_add(r->err_headers_out, "WWW-Authenticate",
  208                ap_pstrcat(r->pool, "Basic realm=\"", auth_name, "\"", NULL));
  209 }
  210 
  211 static const char *
  212 get_gss_error(pool *p, OM_uint32 error_status, char *prefix)
  213 {
  214    OM_uint32 maj_stat, min_stat;
  215    OM_uint32 msg_ctx = 0;
  216    gss_buffer_desc status_string;
  217    char buf[1024];
  218    size_t len;
  219 
  220    snprintf(buf, sizeof(buf), "%s: ", prefix);
  221    len = strlen(buf);
  222    do {
  223       maj_stat = gss_display_status (&min_stat,
  224                                  error_status,
  225                      GSS_C_MECH_CODE,
  226                      GSS_C_NO_OID,
  227                      &msg_ctx,
  228                      &status_string);
  229       if (sizeof(buf) > len + status_string.length + 1) {
  230 /*
  231          sprintf(buf, "%s:", (char*) status_string.value);
  232 */
  233          sprintf(buf+len, "%s:", (char*) status_string.value);
  234          len += status_string.length;
  235       }
  236       gss_release_buffer(&min_stat, &status_string);
  237    } while (!GSS_ERROR(maj_stat) && msg_ctx != 0);
  238 
  239    return (ap_pstrdup(p, buf));
  240 }
  241 
  242 static void *
  243 gss_krb5_dir_create_config(pool *p, char *dir)
  244 {
  245   gss_krb5_config_rec *rec;
  246 
  247   rec = (gss_krb5_config_rec *) ap_pcalloc(p, sizeof(gss_krb5_config_rec));
  248   return rec;
  249 }
  250 
  251 #ifdef AFS
  252 static void
  253 do_afs_log(krb5_context krb_ctx,
  254            request_rec *r,
  255        krb5_ccache ccache,
  256        char *afs_cells)
  257 {
  258    if (afs_cells && k_hasafs()) {
  259       char *cells = ap_pstrdup(r->pool, afs_cells), *next;
  260 
  261       k_setpag();
  262       for (next=strtok(cells," \t"); next; next = strtok(NULL," \t"))
  263       krb5_afslog(krb_ctx, ccache, next, NULL);
  264       ap_register_cleanup(r->pool, NULL, 
  265                       krb5_unlog_cleanup, krb5_dummy_cleanup);
  266    }
  267 }
  268 #endif
  269 
  270 static int
  271 store_krb5_creds(krb5_context krb_ctx,
  272                  request_rec *r,
  273                  gss_krb5_config_rec *conf,
  274                  krb5_ccache delegated_cred)
  275 {
  276    krb5_error_code problem;
  277    char *cache_name = NULL;
  278    krb5_ccache ccache = NULL;
  279    krb5_principal principal = NULL;
  280 #ifndef HEIMDAL 
  281    OM_uint32 maj_status,min_status;
  282 #endif
  283 
  284    if (delegated_cred == NULL)
  285       return OK; /* XXX */
  286 
  287 #ifdef HEIMDAL
  288    problem = krb5_cc_gen_new(krb_ctx, &krb5_fcc_ops, &ccache);
  289 #else
  290 {
  291      char ccname[40];
  292      int tmpfd;
  293 
  294      snprintf(ccname,sizeof(ccname),"FILE:/tmp/krb5cc_%d_XXXXXX",geteuid());
  295 
  296      if ((tmpfd = mkstemp(ccname+strlen("FILE:")))==-1) {
  297              ap_log_error(APLOG_MARK, APLOG_ERR, NULL,"mkstemp(): %.100s", strerror(errno));
  298              problem = errno;
  299              goto fail;
  300      }
  301      if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) {
  302              ap_log_error(APLOG_MARK, APLOG_ERR, NULL, "fchmod(): %.100s", strerror(errno));
  303              close(tmpfd);
  304              problem = errno;
  305              goto fail;
  306      }
  307      close(tmpfd);
  308      problem = krb5_cc_resolve(krb_ctx, ccname, &ccache);
  309 }
  310 fail:
  311 #endif
  312    if (problem) {
  313       ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
  314                 "krb5_cc_gen_new() failed (%s)",
  315             krb5_get_err_text(krb_ctx, problem));
  316       goto end;
  317    }
  318 
  319 #ifdef HEIMDAL
  320    problem = krb5_cc_get_principal(krb_ctx, delegated_cred, &principal);
  321 #else
  322    problem = krb5_cc_get_principal(krb_ctx, ccache, 
  323                                  &principal);
  324 #endif
  325    if (problem) {
  326       ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
  327                 "krb5_cc_get_principal() failed (%s)",
  328             krb5_get_err_text(krb_ctx, problem));
  329       goto end;
  330    }
  331 
  332    problem = krb5_cc_initialize(krb_ctx, ccache, principal);
  333    if (problem) {
  334        ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
  335                  "krb5_cc_initialize() failed (%s)",
  336              krb5_get_err_text(krb_ctx, problem));
  337        goto end;
  338    }
  339 
  340 #ifdef HEIMDAL
  341    problem = krb5_cc_copy_cache(krb_ctx, delegated_cred, ccache);
  342    if (problem) {
  343       ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
  344                 "krb5_cc_copy_cache() failed (%s)",
  345             krb5_get_err_text(krb_ctx, problem));
  346       goto end;
  347    }
  348 #else   
  349    if ((maj_status = gss_krb5_copy_ccache(&min_status, 
  350                            delegated_cred, 
  351                            ccache))) {
  352       ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
  353                     "gss_krb5_copy_ccache() failed");
  354       goto end;
  355    }
  356 #endif
  357 
  358    cache_name = ap_pstrdup(r->pool, krb5_cc_get_name(krb_ctx, ccache));
  359    ap_table_setn(r->subprocess_env, "KRB5CCNAME", cache_name);
  360    ap_register_cleanup(r->pool, cache_name, 
  361                    krb5_cache_cleanup, krb5_dummy_cleanup);
  362      
  363    krb5_cc_close(krb_ctx, ccache);
  364 
  365 end:
  366    if (principal)
  367       krb5_free_principal(krb_ctx, principal);
  368    if (ccache && problem)
  369       krb5_cc_destroy(krb_ctx, ccache);
  370 
  371    return (problem ? SERVER_ERROR : OK);
  372 }
  373 
  374 static int 
  375 authenticate_user_gss(request_rec *r, 
  376                       gss_krb5_config_rec *conf,
  377                       const char *auth_line)
  378 {
  379   static unsigned char ntlmProtocol [] = {'N', 'T', 'L', 'M', 'S', 'S', 'P', 0};
  380 
  381   OM_uint32 major_status, minor_status, minor_status2;
  382   gss_buffer_desc service = GSS_C_EMPTY_BUFFER;
  383   gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
  384   gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
  385   const char *auth_param = NULL;
  386   char *service_name = NULL;
  387   char *host_name = NULL;
  388   char *services_config = NULL;
  389   krb5_context krb_ctx = NULL;
  390   int ret;
  391   gss_name_t client_name = GSS_C_NO_NAME;
  392   gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT;
  393   gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
  394   gss_cred_id_t my_gss_creds = GSS_C_NO_CREDENTIAL;
  395   gss_name_t my_gss_name = GSS_C_NO_NAME;
  396   char *p;
  397   unsigned char        *kerberosToken       = NULL;
  398   size_t                kerberosTokenLength = 0;
  399   unsigned char        *spnegoToken         = NULL ;
  400   size_t                spnegoTokenLength   = 0;
  401   int                   rc;
  402   int                   spnego_flag=0;
  403   int                   ret_flags=0;
  404 #ifndef HEIMDAL
  405   char *ktname;
  406 #endif
  407 
  408   static int initial_return = HTTP_UNAUTHORIZED;
  409 
  410   if (!ap_is_initial_req(r)) 
  411         return initial_return;
  412   initial_return = HTTP_UNAUTHORIZED;
  413 
  414   krb5_init_context(&krb_ctx);
  415   if (conf->gss_krb5_keytab) {
  416 #ifdef HEIMDAL
  417      if (krb5_kt_resolve(krb_ctx, conf->gss_krb5_keytab, &gssapi_krb5_keytab)) {
  418     ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
  419                  "krb5_kt_resolve() failed");
  420     ret = SERVER_ERROR;
  421     goto end;
  422      }
  423 #else
  424       ktname=ap_pcalloc(r->pool,strlen("KRB5_KTNAME=")+strlen(conf->gss_krb5_keytab)+1);
  425       snprintf(ktname,strlen("KRB5_KTNAME=")+strlen(conf->gss_krb5_keytab)+1, "KRB5_KTNAME=%s",
  426                conf->gss_krb5_keytab);
  427       putenv(ktname);
  428 #endif
  429   }   
  430 
  431   ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, r,
  432                  "Use keytab %s",conf->gss_krb5_keytab);
  433 
  434 
  435   /* XXX add an configuration option for service name specification */
  436   if ( !conf->gss_krb5_princs ){  
  437      conf->gss_krb5_princs = strdup("HTTP");
  438   }
  439   services_config = strdup(conf->gss_krb5_princs);
  440   service_name = strtok(services_config," ");
  441   host_name = r->connection->server->server_hostname;
  442   while ( service_name != NULL )  {
  443      if (service.value)
  444          free(service.value);
  445      service.value = malloc(strlen(service_name)+strlen(host_name)+2);
  446      snprintf(service.value,strlen(service_name)+strlen(host_name)+2,"%s@%s",service_name,host_name);
  447      service.length = strlen((unsigned char *)service.value);
  448      ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, r,
  449                       "Use service principal %s/%s",service_name,host_name);
  450      service_name = strtok(NULL," ");
  451 #if HEIMDAL
  452      major_status = gss_import_name(&minor_status, &service,
  453                                    GSS_C_NT_HOSTBASED_SERVICE, &my_gss_name);
  454 #else
  455      major_status = gss_import_name(&minor_status, &service,
  456                                  gss_nt_service_name, &my_gss_name);
  457 #endif
  458      if (GSS_ERROR(major_status)) {
  459         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
  460                       "%s Used service principal: %s", get_gss_error(r->pool, minor_status,
  461                                           "gss_import_name() failed for service principal"),(unsigned char *)service.value);
  462         ret = SERVER_ERROR;
  463         continue;
  464      }
  465 
  466      major_status = gss_acquire_cred(&minor_status, my_gss_name, GSS_C_INDEFINITE,
  467                                      GSS_C_NO_OID_SET, GSS_C_ACCEPT, &my_gss_creds,
  468                                      NULL, NULL);
  469      if (GSS_ERROR(major_status)) {
  470         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
  471                       "%s Used service principal: %s", get_gss_error(r->pool, minor_status,
  472                                           "gss_acquire_cred() failed"),(unsigned char *)service.value);
  473         ret = SERVER_ERROR;
  474         continue;
  475      }
  476 
  477      /* ap_getword() shifts parameter */
  478      auth_param = ap_getword_white(r->pool, &auth_line);
  479      if (auth_param == NULL) {
  480         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
  481                  "No Authorization parameter from client");
  482         ret = HTTP_UNAUTHORIZED;
  483         goto end;
  484      }
  485 
  486      /* XXX use ap_uudecode() */
  487      input_token.length = ap_base64decode_len(auth_param);
  488      input_token.value = ap_pcalloc(r->pool, input_token.length);
  489      if (input_token.value == NULL) {
  490         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
  491              "Not enough memory");
  492         ret = SERVER_ERROR;
  493         goto end;
  494      }
  495 
  496      input_token.length = ap_base64decode(input_token.value, auth_param);
  497      /* Should check first if SPNEGO token */
  498      if ((rc=parseNegTokenInit (input_token.value,
  499                                 input_token.length,
  500                                 &kerberosToken,
  501                                 &kerberosTokenLength))!=0 ) {
  502          ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
  503                   "parseNegTokenInit failed with rc=%d",rc); 
  504          /* 
  505             Error 1xy -> assume GSSAPI token and continue 
  506          */
  507          if ( rc < 100 || rc > 199 ) {
  508             ret = HTTP_UNAUTHORIZED;
  509             goto end;
  510          } 
  511          if ((input_token.length >= sizeof ntlmProtocol + 1) && 
  512              (!memcmp (input_token.value, ntlmProtocol, sizeof ntlmProtocol))) {
  513             ap_log_rerror (APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r, "received type %d NTLM token", (int) *((unsigned char *)input_token.value + sizeof ntlmProtocol));
  514             ret = HTTP_UNAUTHORIZED;
  515             goto end;
  516          }
  517          spnego_flag=0;
  518      } else {
  519          input_token.length=kerberosTokenLength;
  520          input_token.value = ap_pcalloc(r->pool,input_token.length);
  521          if (input_token.value == NULL) {
  522             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
  523                          "Not enough memory");
  524             ret = SERVER_ERROR;
  525             goto end;
  526          }
  527          memcpy(input_token.value,kerberosToken,input_token.length);
  528          spnego_flag=1;
  529      }
  530 
  531      major_status = gss_accept_sec_context(&minor_status,
  532                                        &gss_context,
  533                        my_gss_creds,
  534                        &input_token,
  535                        GSS_C_NO_CHANNEL_BINDINGS,
  536                        &client_name,
  537                        NULL,
  538                        &output_token,
  539                        &ret_flags,
  540                        NULL,
  541                        &delegated_cred);
  542 
  543 
  544      if (output_token.length) {
  545         char *token = NULL;
  546 
  547         if (spnego_flag) {
  548            if ((rc=makeNegTokenTarg (output_token.value,
  549                                      output_token.length,
  550                                      &spnegoToken,
  551                                      &spnegoTokenLength))!=0 ) {
  552               ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
  553                        "makeNegTokenTarg failed with rc=%d",rc); 
  554               ret = HTTP_UNAUTHORIZED;
  555               goto end;
  556            }
  557         } else {
  558            spnegoToken = output_token.value;
  559            spnegoTokenLength = output_token.length;
  560         }
  561         /* XXX use ap_uuencode() */
  562         token = ap_pcalloc(r->pool,ap_base64encode_len(spnegoTokenLength));
  563         if (token == NULL) {
  564            ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
  565                         "Not enough memory");
  566            ret = SERVER_ERROR;
  567        gss_release_buffer(&minor_status2, &output_token);
  568        goto end;
  569         }
  570         ap_base64encode(token, spnegoToken, spnegoTokenLength);
  571         ap_table_set(r->err_headers_out, "WWW-Authenticate",
  572                  ap_pstrcat(r->pool, "Negotiate ", token, NULL));
  573         gss_release_buffer(&minor_status2, &output_token);
  574      }
  575 
  576      if (GSS_ERROR(major_status)) {
  577         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
  578                   "%s Used service principal: %s", get_gss_error(r->pool, minor_status,
  579               "gss_accept_sec_context() failed"),(unsigned char *)service.value);
  580         ret = HTTP_UNAUTHORIZED;
  581         continue;
  582      } 
  583 
  584      if (major_status & GSS_S_CONTINUE_NEEDED) {
  585         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
  586                  "only one authentication iteration allowed"); 
  587         ret = HTTP_UNAUTHORIZED;
  588         goto end;
  589      }
  590 
  591      if ( !(ret_flags & GSS_C_REPLAY_FLAG || ret_flags & GSS_C_SEQUENCE_FLAG) ){
  592         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, r,
  593                      "%s","GSSAPI Warning: no replay protection !");
  594      }
  595      if ( !(ret_flags & GSS_C_SEQUENCE_FLAG) ){
  596         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, r,
  597                      "%s","GSSAPI Warning: no sequence protection !");
  598      }
  599 
  600      major_status = gss_display_name(&minor_status, client_name, &output_token,
  601                                      NULL);
  602 /*
  603      major_status = gss_export_name(&minor_status, client_name, &output_token);
  604 */
  605      gss_release_name(&minor_status, &client_name); 
  606      if (GSS_ERROR(major_status)) {
  607         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r,
  608                  "%s", get_gss_error(r->pool, minor_status, 
  609                               "gss_export_name() failed"));
  610         ret = SERVER_ERROR;
  611         goto end;
  612      }
  613 
  614   /* If the user comes from a realm specified by configuration don't include
  615       its realm name in the username so that the authorization routine could
  616       work for both Password-based and Ticket-based authentication. It's
  617       administrators responsibility to include only such realm that have
  618       unified principal instances, i.e. if the same principal name occures in
  619       multiple realms, it must be always assigned to a single user.
  620   */    
  621      r->connection->ap_auth_type = "Negotiate";
  622      r->connection->user = ap_pstrdup(r->pool, output_token.value);
  623      p = strchr(r->connection->user, '@');
  624      if (p != NULL) {
  625         const char *realms = conf->gss_krb5_realms;
  626 
  627         while (realms && *realms) {
  628        if (strcmp(p+1, ap_getword_white(r->pool, &realms)) == 0) {
  629           *p = '\0';
  630           break;
  631        }
  632         }
  633      }
  634 
  635      gss_release_buffer(&minor_status, &output_token);
  636 
  637   /* This should be only done if afs token are requested or gss_save creds is 
  638    * specified */
  639   /* gss_export_cred() from the GGF GSS Extensions could be used */
  640 #ifdef AFS
  641      if (delegated_cred != GSS_C_NO_CREDENTIAL &&
  642          (conf->gss_save_creds || (conf->gss_krb5_cells && k_hasafs()))) {  
  643         do_afs_log(krb_ctx, r, delegated_cred->ccache, conf->gss_krb5_cells);
  644         ret = store_krb5_creds(krb_ctx, r, conf, delegated_cred->ccache);
  645         if (ret)
  646        goto end;
  647      }
  648 #else
  649      if (delegated_cred != GSS_C_NO_CREDENTIAL &&
  650         conf->gss_save_creds ) {    
  651         ret = store_krb5_creds(krb_ctx, r, conf, delegated_cred);
  652         if (ret)
  653        goto end;
  654      }
  655 #endif
  656      ret = OK;
  657      goto end;
  658   }
  659 end:
  660   if (delegated_cred)
  661      gss_release_cred(&minor_status, &delegated_cred);
  662 
  663   if (output_token.length) 
  664      gss_release_buffer(&minor_status, &output_token);
  665 
  666   if (client_name != GSS_C_NO_NAME)
  667      gss_release_name(&minor_status, &client_name);
  668 
  669   if (gss_context != GSS_C_NO_CONTEXT)
  670      gss_delete_sec_context(&minor_status, &gss_context, GSS_C_NO_BUFFER);
  671 
  672   krb5_free_context(krb_ctx);
  673   if (my_gss_name != GSS_C_NO_NAME)
  674      gss_release_name(&minor_status, &my_gss_name);
  675 
  676   if (my_gss_creds != GSS_C_NO_CREDENTIAL)
  677      gss_release_cred(&minor_status, &my_gss_creds);
  678 
  679   initial_return=ret;
  680   return ret;
  681 }
  682 
  683 static int
  684 authenticate_user_pwd(request_rec *r,
  685                       gss_krb5_config_rec *conf,
  686               const char *auth_line)
  687 {
  688    const char      *sent_pw = NULL;
  689    const char      *realms = NULL;
  690    char *service_name = NULL;
  691    char *host_name = NULL;
  692    char *service = NULL;
  693    char *services_config = NULL;
  694    krb5_context    kcontext;
  695    krb5_error_code code;
  696    krb5_principal  princ = NULL;
  697    char            errstr[512];
  698    krb5_ccache     ccache = NULL;
  699    int             ret;
  700 #ifndef HEIMDAL
  701    krb5_creds creds;
  702    krb5_principal server;
  703    char ccname[40];
  704    int tmpfd;
  705    char *ktname;
  706 #endif  
  707 
  708    krb5_init_context(&kcontext);
  709    
  710    sent_pw = ap_uudecode(r->pool, auth_line);
  711    r->connection->user = ap_getword (r->pool, &sent_pw, ':');
  712    r->connection->ap_auth_type = "Basic";
  713    
  714    /* do not allow user to override realm setting of server */
  715    if (strchr(r->connection->user,'@')) {
  716       ap_log_reason ("specifying realm in user name is prohibited", r->uri, r);
  717       return  HTTP_UNAUTHORIZED;
  718    }
  719 
  720 #ifdef HEIMDAL
  721    code = krb5_cc_gen_new(kcontext, &krb5_mcc_ops, &ccache);
  722 #else
  723 {
  724      char ccname[40];
  725      int tmpfd;
  726 
  727      snprintf(ccname,sizeof(ccname),"FILE:/tmp/krb5cc_%d_XXXXXX",geteuid());
  728 
  729      if ((tmpfd = mkstemp(ccname+strlen("FILE:")))==-1) {
  730              ap_log_error(APLOG_MARK, APLOG_ERR, NULL,"mkstemp(): %.100s", strerror(errno));
  731              code = errno;
  732              goto fail;
  733      }
  734      if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) {
  735              ap_log_error(APLOG_MARK, APLOG_ERR, NULL,"mkstemp(): %.100s", strerror(errno));
  736              close(tmpfd);
  737              code = errno;
  738              goto fail;
  739      }
  740      close(tmpfd);
  741      code = krb5_cc_resolve(kcontext, ccname, &ccache);
  742 }
  743 fail:
  744 #endif
  745    if (code) {
  746       snprintf(errstr, sizeof(errstr), "krb5_cc_gen_new(): %.100s",
  747            krb5_get_err_text(kcontext, code));
  748       ap_log_reason (errstr, r->uri, r);
  749       ret = SERVER_ERROR;
  750       goto end;
  751    }
  752 
  753    if (conf->krb5_keytab)
  754 #ifdef HEIMDAL
  755       kcontext->default_keytab = conf->krb5_keytab;
  756 #else
  757    {
  758       ktname=ap_pcalloc(r->pool,strlen("KRB5_KTNAME=")+strlen(conf->gss_krb5_keytab)+1); 
  759       snprintf(ktname,strlen("KRB5_KTNAME=")+strlen(conf->gss_krb5_keytab)+1, "KRB5_KTNAME=%s",
  760            conf->gss_krb5_keytab);
  761       putenv(ktname);
  762    }
  763 #endif
  764 
  765    ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, r,
  766                  "Use keytab %s",conf->gss_krb5_keytab);
  767 
  768    realms = conf->krb5_auth_realm;
  769 
  770    do {
  771       code = 0;
  772       if (realms)
  773      code = krb5_set_default_realm(kcontext,
  774                                    ap_getword_white(r->pool, &realms));
  775       if (code)
  776      continue;
  777 
  778       code = krb5_parse_name(kcontext, r->connection->user, &princ);
  779       if (code)
  780      continue;
  781 
  782 
  783       if ( !conf->gss_krb5_princs ){
  784          conf->gss_krb5_princs = strdup("HTTP");
  785       }
  786       services_config = strdup(conf->gss_krb5_princs);
  787       service_name = strtok(services_config," ");
  788       host_name = r->connection->server->server_hostname;
  789       while ( service_name != NULL )  {
  790          if (service)
  791              free(service);
  792          service = malloc(strlen(service_name)+strlen(host_name)+2);
  793          snprintf(service,strlen(service_name)+strlen(host_name)+2,"%s/%s",service_name,host_name);
  794          ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, r,
  795                       "Use service principal %s/%s",service_name,host_name);
  796          service_name = strtok(NULL," ");
  797 
  798 
  799 #ifdef HEIMDAL
  800          code = krb5_verify_user(kcontext, princ, ccache, sent_pw, 1, service);
  801 #else
  802          code = krb5_get_init_creds_password(kcontext, &creds,
  803            princ, (char *)sent_pw, NULL , NULL, 0, service, NULL);
  804 
  805          if (code) 
  806              ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, r,
  807                       "krb5_get_init_creds_password/krb5_verify_user = %s",krb5_get_err_text(kcontext, code));
  808          if (!code) break;
  809       }
  810 
  811       if (code)
  812      continue;
  813 
  814       code = krb5_sname_to_principal(kcontext, NULL, NULL,
  815         KRB5_NT_SRV_HST, &server);
  816       if (code)
  817      continue;
  818 
  819       code = krb5_verify_init_creds(kcontext, &creds, server,
  820         NULL, NULL, NULL);
  821       krb5_free_principal(kcontext, server);
  822       if (code)
  823      continue;
  824     
  825       if (!krb5_kuserok(kcontext, princ, r->connection->user)) 
  826      continue;       
  827 
  828 #endif
  829       if (code == 0)
  830      break;
  831    } while (realms && *realms);
  832 
  833    memset((char *)sent_pw, 0x00, strlen(sent_pw));
  834 
  835    if (code) {
  836       snprintf(errstr, sizeof(errstr), "Verifying krb5 password failed: %s",
  837                krb5_get_err_text(kcontext, code));
  838       ap_log_reason (errstr, r->uri, r);
  839       ret = HTTP_UNAUTHORIZED;
  840       goto end;
  841    }
  842 
  843    if (conf->krb5_save_creds) {
  844 #ifdef AFS
  845       do_afs_log(kcontext, r, ccache, conf->krb5_cells);
  846 #endif
  847       ret = store_krb5_creds(kcontext, r, conf, ccache);
  848       if (ret)
  849          goto end;
  850    }
  851 
  852    ret = OK;
  853 
  854 end:
  855    if (princ)
  856       krb5_free_principal(kcontext, princ);
  857    if (ccache)
  858       krb5_cc_destroy(kcontext, ccache);
  859    krb5_free_context(kcontext);
  860 
  861    return ret;
  862 }
  863 
  864 static int
  865 gss_krb5_authenticate_user(request_rec *r)
  866 {
  867   gss_krb5_config_rec *conf =
  868      (gss_krb5_config_rec *) ap_get_module_config(r->per_dir_config,
  869                           &gss_krb5_auth_module);
  870   const char *auth_type = NULL;
  871   const char *auth_line = NULL;
  872   const char *type = NULL;
  873   int ret;
  874 
  875   /* get the type specified in .htaccess */
  876   type = ap_auth_type(r);
  877 
  878   /* Apache doesn't allow multiple values in AuthType within one directory.
  879    * But since we want to support both the method we added a new configuration
  880    * option (UseGSS) to turn on GSS authentication. */
  881   if (!conf->use_gss_auth &&
  882       (type == NULL || strncasecmp(type, "KerberosV5", 10) != 0)) {
  883      return DECLINED;
  884   }
  885 
  886   /* get what the user sent us in the HTTP header */
  887   auth_line = ap_table_get (r->headers_in, "Authorization");
  888   if (!auth_line) {
  889       note_auth_failure(r, conf);
  890       return HTTP_UNAUTHORIZED;
  891   }
  892   auth_type = ap_getword_white(r->pool, &auth_line);
  893 
  894   if (conf->use_gss_auth &&
  895       strncasecmp(auth_type, "Negotiate", 9) == 0) {
  896      ret = authenticate_user_gss(r, conf, auth_line);
  897   } else
  898      if (type != NULL && strncasecmp(type, "KerberosV5", 10) == 0 &&
  899      strncasecmp(auth_type, "Basic", 5) == 0) {
  900     ret = authenticate_user_pwd(r, conf, auth_line);
  901      }
  902      else {
  903     ret = HTTP_UNAUTHORIZED;
  904      }
  905 
  906   if (ret == HTTP_UNAUTHORIZED)
  907      note_auth_failure(r, conf);
  908 
  909   return ret;
  910 }
  911 
  912 
  913 static int 
  914 gss_krb5_check_user(request_rec *r)
  915 {
  916   gss_krb5_config_rec *conf =
  917      (gss_krb5_config_rec *) ap_get_module_config(r->per_dir_config,
  918                         &gss_krb5_auth_module);
  919   char *user = r->connection->user;
  920   int m = r->method_number;
  921   int method_restricted = 0;
  922   register int x;
  923   const char *t, *w;
  924   const require_line *require;
  925   const array_header *required_users;
  926 
  927   required_users = ap_requires(r);
  928   if (required_users == NULL)
  929     return OK;
  930 
  931   require = (require_line *) required_users->elts;
  932 
  933   for (x = 0; x < required_users->nelts; x++) {
  934      if (! (require[x].method_mask & (1 << m)))
  935     continue;
  936 
  937      method_restricted = 1;
  938 
  939      t = require[x].requirement;
  940      w = ap_getword_white(r->pool, &t);
  941      if (strcmp(w, "valid-user") == 0) {
  942     return OK;
  943      }
  944 
  945      if (strcmp(w, "user") == 0) {
  946     while (t[0] != '\0') {
  947        w = ap_getword_conf(r->pool, &t);
  948            if (strcmp(user, w) == 0) 
  949               return OK;
  950     }
  951      }
  952   }
  953 
  954   if (! method_restricted)
  955      return OK;
  956 
  957   ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  958     "GSS & Krb5 module: access to %s failed, reason: user %s not allowed access",
  959     r->uri, user);
  960 
  961   note_auth_failure(r, conf);
  962 
  963   return HTTP_UNAUTHORIZED;
  964 /* maybe needed for Firefox bug
  965   return HTTP_FORBIDDEN;
  966 */
  967 }
  968 
  969 module MODULE_VAR_EXPORT gss_krb5_auth_module =
  970 {
  971   STANDARD_MODULE_STUFF, 
  972   NULL,                       /* initializer */
  973   gss_krb5_dir_create_config, /* dir config creater */
  974   NULL,                       /* dir merger --- default is to override */
  975   NULL,                       /* server config */
  976   NULL,                       /* merge server config */
  977   gss_krb5_cmds,              /* command table */
  978   NULL,                       /* handlers */
  979   NULL,                       /* filename translation */
  980   gss_krb5_authenticate_user, /* check_user_id */
  981   gss_krb5_check_user,        /* check auth */
  982   NULL,                       /* check access */
  983   NULL,                       /* type_checker */
  984   NULL,                       /* fixups */
  985   NULL,                       /* logger */
  986   NULL,                       /* header parser */
  987   NULL,                       /* child_init */
  988   NULL,                       /* child_exit */
  989   NULL                        /* post read-request */
  990 };