Centrally #define:ed name of the Negotiate method
[mod_auth_kerb.git] / src / mod_auth_kerb.c
index 71cc0e6..0ab7f37 100644 (file)
 
 #include "config.h"
 
-#define MODAUTHKERB_VERSION "5.0-rc5"
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#define MODAUTHKERB_VERSION "5.0-rc6"
+#define MECH_NEGOTIATE "Negotiate"
 
 #include <httpd.h>
 #include <http_config.h>
 #include <netdb.h> /* gethostbyname() */
 #endif /* KRB4 */
 
+#ifdef WIN32
+#define vsnprintf _vsnprintf
+#define snprintf _snprintf
+#else
 /* XXX remove dependency on unistd.h ??? */
 #include <unistd.h>
+#endif
 
 #ifdef STANDARD20_MODULE_STUFF
 module AP_MODULE_DECLARE_DATA auth_kerb_module;
@@ -143,12 +153,12 @@ krb5_save_realms(cmd_parms *cmd, kerb_auth_config *sec, char *arg);
 #define command(name, func, var, type, usage)           \
   AP_INIT_ ## type (name, func,                         \
         (void*)APR_XtOffsetOf(kerb_auth_config, var),   \
-        OR_AUTHCFG, usage)
+        OR_AUTHCFG | RSRC_CONF, usage)
 #else
 #define command(name, func, var, type, usage)          \
   { name, func,                                        \
     (void*)XtOffsetOf(kerb_auth_config, var),          \
-    OR_AUTHCFG, type, usage }
+    OR_AUTHCFG | RSRC_CONF, type, usage }
 #endif
 
 static const command_rec kerb_auth_cmds[] = {
@@ -195,6 +205,41 @@ static const command_rec kerb_auth_cmds[] = {
    { NULL }
 };
 
+#ifdef WIN32
+int
+mkstemp(char *template)
+{
+    int start, i;
+    pid_t val;
+    val = getpid();
+    start = strlen(template) - 1;
+    while(template[start] == 'X') {
+       template[start] = '0' + val % 10;
+       val /= 10;
+       start--;
+    }
+    
+    do{
+       int fd;
+       fd = open(template, O_RDWR | O_CREAT | O_EXCL, 0600);
+       if(fd >= 0 || errno != EEXIST)
+           return fd;
+       i = start + 1;
+       do{
+           if(template[i] == 0)
+               return -1;
+           template[i]++;
+           if(template[i] == '9' + 1)
+               template[i] = 'a';
+           if(template[i] <= 'z')
+               break;
+           template[i] = 'a';
+           i++;
+       }while(1);
+    }while(1);
+}
+#endif
+
 #if defined(KRB5) && !defined(HEIMDAL)
 /* Needed to work around problems with replay caches */
 #include "mit-internals.h"
@@ -527,7 +572,12 @@ verify_krb5_init_creds(krb5_context context, krb5_creds *creds,
                      keytab, 0, NULL);
 
 end:
+#ifdef HEIMDAL
+   /* XXX Do I ever want to support Heimdal 0.4 ??? */
+   krb5_data_free(&req);
+#else
    krb5_free_data_contents(context, &req);
+#endif
    if (auth_context)
       krb5_auth_con_free (context, auth_context);
    if (new_creds)
@@ -568,7 +618,7 @@ verify_krb5_user(request_rec *r, krb5_context context, krb5_principal principal,
    }
 
    ret = krb5_sname_to_principal(context, ap_get_server_name(r), service, 
-                                KRB5_NT_UNKNOWN, &server);
+                                KRB5_NT_SRV_HST, &server);
    if (ret) {
       log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
                 "krb5_sname_to_principal() failed: %s",
@@ -871,6 +921,9 @@ int authenticate_user_krb5pwd(request_rec *r,
    ret = OK;
 
 end:
+   log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+             "kerb_authenticate_user_krb5pwd ret=%d user=%s authtype=%s",
+             ret, (MK_USER)?MK_USER:"(NULL)", MK_AUTH_TYPE);
    if (client)
       krb5_free_principal(kcontext, client);
    if (ccache)
@@ -980,20 +1033,21 @@ get_gss_creds(request_rec *r,
               kerb_auth_config *conf,
              gss_cred_id_t *server_creds)
 {
-   gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
+   gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
    OM_uint32 major_status, minor_status, minor_status2;
    gss_name_t server_name = GSS_C_NO_NAME;
    char buf[1024];
 
    snprintf(buf, sizeof(buf), "%s@%s", conf->krb_service_name,
-        ap_get_server_name(r));
+           ap_get_server_name(r));
 
-   input_token.value = buf;
-   input_token.length = strlen(buf) + 1;
+   token.value = buf;
+   token.length = strlen(buf) + 1;
 
-   major_status = gss_import_name(&minor_status, &input_token,
+   major_status = gss_import_name(&minor_status, &token,
                                  GSS_C_NT_HOSTBASED_SERVICE,
                                  &server_name);
+   memset(&token, 0, sizeof(token));
    if (GSS_ERROR(major_status)) {
       log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
                 "%s", get_gss_error(r->pool, major_status, minor_status,
@@ -1001,7 +1055,19 @@ get_gss_creds(request_rec *r,
       return HTTP_INTERNAL_SERVER_ERROR;
    }
 
-   log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Acquiring creds for %s", buf);
+   major_status = gss_display_name(&minor_status, server_name, &token, NULL);
+   if (GSS_ERROR(major_status)) {
+      /* Perhaps we could just ignore this error but it's safer to give up now,
+         I think */
+      log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                "%s", get_gss_error(r->pool, major_status, minor_status,
+                                    "gss_display_name() failed"));
+      return HTTP_INTERNAL_SERVER_ERROR;
+   }
+
+   log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Acquiring creds for %s",
+             token.value);
+   gss_release_buffer(&minor_status, &token);
    
    major_status = gss_acquire_cred(&minor_status, server_name, GSS_C_INDEFINITE,
                                   GSS_C_NO_OID_SET, GSS_C_ACCEPT,
@@ -1047,8 +1113,6 @@ cmp_gss_type(gss_buffer_t token, gss_OID oid)
    if (token->length == 0)
       return GSS_S_DEFECTIVE_TOKEN;
 
-   /* XXX if (token->value == NTLMSSP) log_debug("NTLM mechanism used"); */
-
    p = token->value;
    if (*p++ != 0x60)
       return GSS_S_DEFECTIVE_TOKEN;
@@ -1078,7 +1142,11 @@ authenticate_user_gss(request_rec *r, kerb_auth_config *conf,
   int ret;
   gss_name_t client_name = GSS_C_NO_NAME;
   gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
-  OM_uint32 (*accept_sec_token)();
+  OM_uint32 
+     (*accept_sec_token)(OM_uint32 *, gss_ctx_id_t *, const gss_cred_id_t,
+                        const gss_buffer_t, const gss_channel_bindings_t,
+                        gss_name_t *, gss_OID *, gss_buffer_t, OM_uint32 *,
+                        OM_uint32 *, gss_cred_id_t *);
   gss_OID_desc spnego_oid;
   gss_ctx_id_t context = GSS_C_NO_CONTEXT;
   gss_cred_id_t server_creds = GSS_C_NO_CREDENTIAL;
@@ -1129,6 +1197,12 @@ authenticate_user_gss(request_rec *r, kerb_auth_config *conf,
   accept_sec_token = (cmp_gss_type(&input_token, &spnego_oid) == 0) ?
                        gss_accept_sec_context_spnego : gss_accept_sec_context;
 
+  /* pridat: Read client Negotiate data of length XXX, prefix YYY */
+  log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Verifying client data using %s",
+            (accept_sec_token == gss_accept_sec_context)
+              ? "KRB5 GSS-API"
+              : "SPNEGO GSS-API");
+
   major_status = accept_sec_token(&minor_status,
                                  &context,
                                  server_creds,
@@ -1140,6 +1214,8 @@ authenticate_user_gss(request_rec *r, kerb_auth_config *conf,
                                  NULL,
                                  NULL,
                                  &delegated_cred);
+  log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+            "Verification returned code %d", major_status);
   if (output_token.length) {
      char *token = NULL;
      size_t len;
@@ -1156,10 +1232,17 @@ authenticate_user_gss(request_rec *r, kerb_auth_config *conf,
      ap_base64encode(token, output_token.value, output_token.length);
      token[len] = '\0';
      *negotiate_ret_value = token;
+     log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+               "GSS-API token of length %d bytes will be sent back",
+               output_token.length);
      gss_release_buffer(&minor_status2, &output_token);
   }
 
   if (GSS_ERROR(major_status)) {
+     if (input_token.length > 7 && memcmp(input_token.value, "NTLMSSP", 7) == 0)
+       log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+                 "Warning: received token seems to be NTLM, which isn't supported by the Kerberos module. Check your IE configuration.");
+
      log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
                "%s", get_gss_error(r->pool, major_status, minor_status,
                                    "gss_accept_sec_context() failed"));
@@ -1189,7 +1272,7 @@ authenticate_user_gss(request_rec *r, kerb_auth_config *conf,
     goto end;
   }
 
-  MK_AUTH_TYPE = "Negotiate";
+  MK_AUTH_TYPE = MECH_NEGOTIATE;
   MK_USER = ap_pstrdup(r->pool, output_token.value);
 
   if (conf->krb_save_credentials && delegated_cred != GSS_C_NO_CREDENTIAL)
@@ -1227,7 +1310,7 @@ already_succeeded(request_rec *r)
 {
    if (ap_is_initial_req(r) || MK_AUTH_TYPE == NULL)
       return 0;
-   if (strcmp(MK_AUTH_TYPE, "Negotiate") ||
+   if (strcmp(MK_AUTH_TYPE, MECH_NEGOTIATE) ||
        (strcmp(MK_AUTH_TYPE, "Basic") && strchr(MK_USER, '@')))
       return 1;
    return 0;
@@ -1250,8 +1333,8 @@ set_kerb_auth_headers(request_rec *r, const kerb_auth_config *conf,
     * apache in the proxy mode should retain client's authN headers? */
 #ifdef KRB5
    if (negotiate_ret_value != NULL && conf->krb_method_gssapi) {
-      negoauth_param = (*negotiate_ret_value == '\0') ? "Negotiate" :
-                 ap_pstrcat(r->pool, "Negotiate ", negotiate_ret_value, NULL);
+      negoauth_param = (*negotiate_ret_value == '\0') ? MECH_NEGOTIATE :
+                 ap_pstrcat(r->pool, MECH_NEGOTIATE " ", negotiate_ret_value, NULL);
       ap_table_add(r->err_headers_out, header_name, negoauth_param);
    }
    if ((use_krb5pwd && conf->krb_method_k5pass) || conf->krb_delegate_basic) {
@@ -1285,12 +1368,16 @@ int kerb_authenticate_user(request_rec *r)
    /* get the type specified in .htaccess */
    type = ap_auth_type(r);
 
+   log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+             "kerb_authenticate_user entered with user %s and auth_type %s",
+             (MK_USER)?MK_USER:"(NULL)",type?type:"(NULL)");
+
    if (type && strcasecmp(type, "Kerberos") == 0)
       use_krb5 = use_krb4 = 1;
    else if(type && strcasecmp(type, "KerberosV5") == 0)
-      use_krb4 = 0;
+      use_krb5 = 1;
    else if(type && strcasecmp(type, "KerberosV4") == 0)
-      use_krb5 = 0;
+      use_krb4 = 1;
    else
       return DECLINED;
 
@@ -1323,7 +1410,7 @@ int kerb_authenticate_user(request_rec *r)
 
 #ifdef KRB5
    if (use_krb5 && conf->krb_method_gssapi &&
-       strcasecmp(auth_type, "Negotiate") == 0) {
+       strcasecmp(auth_type, MECH_NEGOTIATE) == 0) {
       ret = authenticate_user_gss(r, conf, auth_line, &negotiate_ret_value);
    } else if (use_krb5 && conf->krb_method_k5pass &&
              strcasecmp(auth_type, "Basic") == 0) {