Use `HTTP' as a default principal name for authentication, instead of `khttp'.
[mod_auth_kerb.git] / src / mod_auth_kerb.c
index d1fe3f9..f9f3ca0 100644 (file)
@@ -72,7 +72,7 @@
 
 #include "config.h"
 
-#define MODAUTHKERB_VERSION "5.0-rc2"
+#define MODAUTHKERB_VERSION "5.0-rc3"
 
 #ifndef APXS1
 #include "ap_compat.h"
@@ -179,7 +179,7 @@ static const command_rec kerb_auth_cmds[] = {
    command("KrbVerifyKDC", ap_set_flag_slot, krb_verify_kdc,
      FLAG, "Verify tickets against keytab to prevent KDC spoofing attacks."),
 
-   command("KrbServiceName", ap_set_file_slot, krb_service_name,
+   command("KrbServiceName", ap_set_string_slot, krb_service_name,
      TAKE1, "Service name to be used by Apache for authentication."),
 
    command("KrbAuthoritative", ap_set_flag_slot, krb_authoritative,
@@ -226,7 +226,7 @@ static void *kerb_dir_create_config(MK_POOL *p, char *d)
 
        rec = (kerb_auth_config *) ap_pcalloc(p, sizeof(kerb_auth_config));
         ((kerb_auth_config *)rec)->krb_verify_kdc = 1;
-       ((kerb_auth_config *)rec)->krb_service_name = "khttp";
+       ((kerb_auth_config *)rec)->krb_service_name = "HTTP";
        ((kerb_auth_config *)rec)->krb_authoritative = 1;
 #ifdef KRB5
        ((kerb_auth_config *)rec)->krb_method_k5pass = 1;
@@ -401,10 +401,10 @@ authenticate_user_krb4pwd(request_rec *r,
 
       if (realm == NULL) {
         ret = krb_get_lrealm(lrealm, 1);
+        if (ret)
+           break;
         realm = lrealm;
       }
-      if (realm == NULL || *realm == '\0')
-        break;
 
       ret = verify_krb4_user(r, (char *)sent_name, 
                             (sent_instance) ? sent_instance : "",
@@ -423,8 +423,9 @@ authenticate_user_krb4pwd(request_rec *r,
    } while (realms && *realms);
 
    if (ret) {
+      /* XXX log only in the verify_krb4_user() call */
       log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Verifying krb4 password failed");
-      ret = (all_principals_unkown = 1 && ret == KDC_PR_UNKNOWN) ?
+      ret = (!conf->krb_authoritative && all_principals_unkown == 1 && ret == KDC_PR_UNKNOWN) ?
                 DECLINED : HTTP_UNAUTHORIZED;
       goto end;
    }
@@ -534,29 +535,30 @@ create_krb5_ccache(krb5_context kcontext,
                   krb5_ccache *ccache)
 {
    char *ccname;
+   int fd;
    krb5_error_code problem;
    int ret;
    krb5_ccache tmp_ccache = NULL;
 
-#ifdef HAVE_KRB5_CC_GEN_NEW
-   problem = krb5_cc_gen_new(kcontext, &krb5_fcc_ops, &tmp_ccache);
-#else
-   /* only older MIT seem to not have the krb5_cc_gen_new() call, so we use
-    * MIT specific call here */
-   problem = krb5_fcc_generate_new(kcontext, &tmp_ccache);
-   /* krb5_fcc_generate_new() doesn't set KRB5_TC_OPENCLOSE, which makes 
-      krb5_cc_initialize() fail */
-   krb5_fcc_set_flags(kcontext, tmp_ccache, KRB5_TC_OPENCLOSE);
-#endif
-   if (problem) {
+   ccname = ap_psprintf(r->pool, "FILE:%s/krb5cc_apache_XXXXXX", P_tmpdir);
+   fd = mkstemp(ccname + strlen("FILE:"));
+   if (fd < 0) {
       log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
-                "Cannot create file for new krb5 ccache: %s",
-                krb5_get_err_text(kcontext, problem));
+                 "mkstemp() failed: %s", strerror(errno));
       ret = HTTP_INTERNAL_SERVER_ERROR;
       goto end;
    }
+   close(fd);
 
-   ccname = ap_pstrdup(r->pool, krb5_cc_get_name(kcontext, tmp_ccache));
+   problem = krb5_cc_resolve(kcontext, ccname, &tmp_ccache);
+   if (problem) {
+      log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                 "krb5_cc_resolve() failed: %s",
+                 krb5_get_err_text(kcontext, problem));
+      ret = HTTP_INTERNAL_SERVER_ERROR;
+      unlink(ccname);
+      goto end;
+   }
 
    problem = krb5_cc_initialize(kcontext, tmp_ccache, princ);
    if (problem) {
@@ -641,6 +643,8 @@ int authenticate_user_krb5pwd(request_rec *r,
    int             ret;
    char            *name = NULL;
    int             all_principals_unkown;
+   char            *ccname = NULL;
+   int             fd;
 
    code = krb5_init_context(&kcontext);
    if (code) {
@@ -657,20 +661,26 @@ int authenticate_user_krb5pwd(request_rec *r,
                 "specifying realm in user name is prohibited");
       ret = HTTP_UNAUTHORIZED;
       goto end;
-   } 
+   }
 
-#ifdef HAVE_KRB5_CC_GEN_NEW
-   code = krb5_cc_gen_new(kcontext, &krb5_mcc_ops, &ccache);
-#else
-   /* only older MIT seem to not have the krb5_cc_gen_new() call, so we use
-    * MIT specific call here */
-   code = krb5_mcc_generate_new(kcontext, &ccache);
-#endif
+   /* XXX Heimdal allows to use the MEMORY: type with empty argument ? */
+   ccname = ap_psprintf(r->pool, "MEMORY:%s/krb5cc_apache_XXXXXX", P_tmpdir);
+   fd = mkstemp(ccname + strlen("MEMORY:"));
+   if (fd < 0) {
+      log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                 "mkstemp() failed: %s", strerror(errno));
+      ret = HTTP_INTERNAL_SERVER_ERROR;
+      goto end;
+   }
+   close(fd);
+
+   code = krb5_cc_resolve(kcontext, ccname, &ccache);
    if (code) {
-      log_rerror(APLOG_MARK, APLOG_ERR, 0, r, 
-                "Cannot generate new ccache: %s",
-                krb5_get_err_text(kcontext, code));
+      log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                 "krb5_cc_resolve() failed: %s",
+                 krb5_get_err_text(kcontext, code));
       ret = HTTP_INTERNAL_SERVER_ERROR;
+      unlink(ccname);
       goto end;
    }
 
@@ -712,10 +722,11 @@ int authenticate_user_krb5pwd(request_rec *r,
    memset((char *)sent_pw, 0, strlen(sent_pw));
 
    if (code) {
+      /* XXX log only in the verify_krb5_user() call */
       log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
                 "Verifying krb5 password failed: %s",
                 krb5_get_err_text(kcontext, code));
-      if (all_principals_unkown = 1 && code == KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN)
+      if (!conf->krb_authoritative && all_principals_unkown == 1 && code == KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN)
         ret = DECLINED;
       else
         ret = HTTP_UNAUTHORIZED;
@@ -756,31 +767,39 @@ end:
  ********************************************************************/
 
 static const char *
-get_gss_error(MK_POOL *p, OM_uint32 error_status, char *prefix)
+get_gss_error(MK_POOL *p, OM_uint32 err_maj, OM_uint32 err_min, char *prefix)
 {
-   OM_uint32 maj_stat, min_stat;
+   OM_uint32 maj_stat, min_stat; 
    OM_uint32 msg_ctx = 0;
    gss_buffer_desc status_string;
-   char buf[1024];
+   char *err_msg;
    size_t len;
 
-   snprintf(buf, sizeof(buf), "%s", prefix);
-   len = strlen(buf);
+   err_msg = ap_pstrdup(p, prefix);
    do {
       maj_stat = gss_display_status (&min_stat,
-                                    error_status,
-                                    GSS_C_MECH_CODE,
+                                    err_maj,
+                                    GSS_C_GSS_CODE,
                                     GSS_C_NO_OID,
                                     &msg_ctx,
                                     &status_string);
-      if (sizeof(buf) > len + status_string.length + 1) {
-         sprintf(buf+len, ": %s", (char*) status_string.value);
-         len += status_string.length;
-      }
+      err_msg = ap_pstrcat(p, err_msg, ": ", (char*) status_string.value, NULL);
+      gss_release_buffer(&min_stat, &status_string);
+      
+      if (GSS_ERROR(maj_stat) || msg_ctx == 0)
+        break;
+
+      maj_stat = gss_display_status (&min_stat,
+                                    err_min,
+                                    GSS_C_MECH_CODE,
+                                    GSS_C_NULL_OID,
+                                    &msg_ctx,
+                                    &status_string);
+      err_msg = ap_pstrcat(p, err_msg, ": ", (char*) status_string.value, NULL);
       gss_release_buffer(&min_stat, &status_string);
    } while (!GSS_ERROR(maj_stat) && msg_ctx != 0);
 
-   return (ap_pstrdup(p, buf));
+   return err_msg;
 }
 
 static int
@@ -837,7 +856,7 @@ store_gss_creds(request_rec *r, kerb_auth_config *conf, char *princ_name,
    if (GSS_ERROR(maj_stat)) {
       log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
         "Cannot store delegated credential (%s)", 
-        get_gss_error(r->pool, min_stat, "gss_krb5_copy_ccache"));
+        get_gss_error(r->pool, maj_stat, min_stat, "gss_krb5_copy_ccache"));
       goto end;
    }
 
@@ -874,7 +893,7 @@ get_gss_creds(request_rec *r,
                                  &server_name);
    if (GSS_ERROR(major_status)) {
       log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
-                "%s", get_gss_error(r->pool, minor_status,
+                "%s", get_gss_error(r->pool, major_status, minor_status,
                 "gss_import_name() failed"));
       return HTTP_INTERNAL_SERVER_ERROR;
    }
@@ -885,7 +904,7 @@ get_gss_creds(request_rec *r,
    gss_release_name(&minor_status2, &server_name);
    if (GSS_ERROR(major_status)) {
       log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
-                "%s", get_gss_error(r->pool, minor_status,
+                "%s", get_gss_error(r->pool, major_status, minor_status,
                                     "gss_acquire_cred() failed"));
       return HTTP_INTERNAL_SERVER_ERROR;
    }
@@ -1026,7 +1045,7 @@ authenticate_user_gss(request_rec *r,
 
   if (GSS_ERROR(major_status)) {
      log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
-               "%s", get_gss_error(r->pool, minor_status,
+               "%s", get_gss_error(r->pool, major_status, minor_status,
                                    "gss_accept_sec_context() failed"));
      ret = HTTP_UNAUTHORIZED;
      goto end;
@@ -1043,7 +1062,7 @@ authenticate_user_gss(request_rec *r,
   gss_release_name(&minor_status, &client_name); 
   if (GSS_ERROR(major_status)) {
     log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
-              "%s", get_gss_error(r->pool, minor_status, 
+              "%s", get_gss_error(r->pool, major_status, minor_status,
                                   "gss_export_name() failed"));
     ret = HTTP_INTERNAL_SERVER_ERROR;
     goto end;