r6800: A big GENSEC update:
authorAndrew Bartlett <abartlet@samba.org>
Sun, 15 May 2005 23:42:11 +0000 (23:42 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 18:16:45 +0000 (13:16 -0500)
Finally remove the distinction between 'krb5' and 'ms_krb5'.  We now
don't do kerberos stuff twice on failure.  The solution to this is
slightly more general than perhaps was really required (as this is a
special case), but it works, and I'm happy with the cleanup I achived
in the process.  All modules have been updated to supply a
NULL-terminated list of OIDs.

In that process, SPNEGO code has been generalised, as I realised that
two of the functions should have been identical in behaviour.

Over in the actual modules, I have worked to remove the 'kinit' code
from gensec_krb5, and placed it in kerberos/kerberos_util.c.

The GSSAPI module has been extended to use this, so no longer requires
a manual kinit at the command line.  It will soon loose the
requirement for a on-disk keytab too.

The general kerberos code has also been updated to move from
error_message() to our routine which gets the Heimdal error string
(which may be much more useful) when available.

Andrew Bartlett

source/auth/gensec/gensec.c
source/auth/gensec/gensec.h
source/auth/gensec/gensec_gssapi.c
source/auth/gensec/gensec_krb5.c
source/auth/gensec/spnego.c
source/auth/kerberos/kerberos.h
source/auth/kerberos/kerberos.mk
source/auth/kerberos/kerberos_util.c [new file with mode: 0644]
source/auth/kerberos/kerberos_verify.c
source/auth/ntlmssp/ntlmssp.c
source/include/structs.h

index 77ba58fcde9536855f0b48988f441e46ffe48c51..1608f21114006fcec0cb7f8a54c4bd0166a1b144 100644 (file)
@@ -42,11 +42,15 @@ static const struct gensec_security_ops *gensec_security_by_authtype(uint8_t aut
 
 static const struct gensec_security_ops *gensec_security_by_oid(const char *oid_string)
 {
-       int i;
+       int i, j;
        for (i=0; i < gensec_num_backends; i++) {
-               if (generic_security_ops[i]->oid &&
-                   (strcmp(generic_security_ops[i]->oid, oid_string) == 0)) {
-                       return generic_security_ops[i];
+               if (generic_security_ops[i]->oid) {
+                       for (j=0; generic_security_ops[i]->oid[j]; j++) { 
+                               if (generic_security_ops[i]->oid[j] &&
+                                   (strcmp(generic_security_ops[i]->oid[j], oid_string) == 0)) {
+                                       return generic_security_ops[i];
+                               }
+                       }
                }
        }
 
@@ -85,16 +89,99 @@ const struct gensec_security_ops **gensec_security_all(int *num_backends_out)
        return generic_security_ops;
 }
 
-const char **gensec_security_oids(TALLOC_CTX *mem_ctx, const char *skip) 
+/**
+ * Return a unique list of security subsystems from those specified in
+ * the OID list.  That is, where two OIDs refer to the same module,
+ * return that module only once 
+ *
+ * The list is in the exact order of the OIDs asked for, where available.
+ */
+
+const struct gensec_security_ops_wrapper *gensec_security_by_oid_list(TALLOC_CTX *mem_ctx, 
+                                                                     const char **oid_strings,
+                                                                     const char *skip)
 {
-       int i, j = 0;
-       const char **oid_list;
+       struct gensec_security_ops_wrapper *backends_out;
+       const struct gensec_security_ops **backends;
+       int i, j, k, oid_idx;
+       int num_backends_out = 0;
        int num_backends;
-       const struct gensec_security_ops **ops = gensec_security_all(&num_backends);
+
+       if (!oid_strings) {
+               return NULL;
+       }
+
+       backends = gensec_security_all(&num_backends);
+
+       backends_out = talloc_array(mem_ctx, struct gensec_security_ops_wrapper, 1);
+       if (!backends_out) {
+               return NULL;
+       }
+       backends_out[0].op = NULL;
+       backends_out[0].oid = NULL;
+
+       for (oid_idx = 0; oid_strings[oid_idx]; oid_idx++) {
+               if (strcmp(oid_strings[oid_idx], skip) == 0) {
+                       continue;
+               }
+
+               for (i=0; i < num_backends; i++) {
+                       if (!backends[i]->oid) {
+                               continue;
+                       }
+                       for (j=0; backends[i]->oid[j]; j++) { 
+                               if (!backends[i]->oid[j] ||
+                                   !(strcmp(backends[i]->oid[j], 
+                                           oid_strings[oid_idx]) == 0)) {
+                                       continue;
+                               }
+                               
+                               for (k=0; backends_out[k].op; k++) {
+                                       if (backends_out[k].op == backends[i]) {
+                                               break;
+                                       }
+                               }
+                               
+                               if (k < num_backends_out) {
+                                       /* already in there */
+                                       continue;
+                               }
+
+                               backends_out = talloc_realloc(mem_ctx, backends_out, 
+                                                             struct gensec_security_ops_wrapper, 
+                                                             num_backends_out + 2);
+                               if (!backends_out) {
+                                       return NULL;
+                               }
+                               
+                               backends_out[num_backends_out].op = backends[i];
+                               backends_out[num_backends_out].oid = backends[i]->oid[j];
+                               num_backends_out++;
+                               backends_out[num_backends_out].op = NULL;
+                               backends_out[num_backends_out].oid = NULL;
+                       }
+               }
+       }
+       return backends_out;
+}
+
+/**
+ * Return OIDS from the security subsystems listed
+ */
+
+const char **gensec_security_oids_from_ops(TALLOC_CTX *mem_ctx, 
+                                          const struct gensec_security_ops **ops,                                 
+                                          int num_backends,
+                                          const char *skip) 
+{
+       int i;
+       int j = 0;
+       int k;
+       const char **oid_list;
        if (!ops) {
                return NULL;
        }
-       oid_list = talloc_array(mem_ctx, const char *, num_backends + 1);
+       oid_list = talloc_array(mem_ctx, const char *, 1);
        if (!oid_list) {
                return NULL;
        }
@@ -104,17 +191,37 @@ const char **gensec_security_oids(TALLOC_CTX *mem_ctx, const char *skip)
                        continue;
                }
                
-               if (skip && strcmp(skip, ops[i]->oid)==0) {
-                       continue;
+               for (k = 0; ops[i]->oid[k]; k++) {
+                       if (skip && strcmp(skip, ops[i]->oid[k])==0) {
+                       } else {
+                               oid_list = talloc_realloc(mem_ctx, oid_list, const char *, j + 2);
+                               if (!oid_list) {
+                                       return NULL;
+                               }
+                               oid_list[j] = ops[i]->oid[k];
+                               j++;
+                       }
                }
-
-               oid_list[j] = ops[i]->oid;
-               j++;
        }
        oid_list[j] = NULL;
        return oid_list;
 }
 
+
+/**
+ * Return all the security subsystems currently enabled in GENSEC 
+ */
+
+const char **gensec_security_oids(TALLOC_CTX *mem_ctx, const char *skip) 
+{
+       int num_backends;
+       const struct gensec_security_ops **ops = gensec_security_all(&num_backends);
+       return gensec_security_oids_from_ops(mem_ctx, ops, 
+                                            num_backends, skip);
+}
+
+
+
 /**
   Start the GENSEC system, returning a context pointer.
   @param mem_ctx The parent TALLOC memory context.
@@ -283,6 +390,18 @@ const char *gensec_get_name_by_oid(const char *oid_string)
 }
        
 
+/** 
+ * Start a GENSEC sub-mechanism with a specifed mechansim structure, used in SPNEGO
+ *
+ */
+
+NTSTATUS gensec_start_mech_by_ops(struct gensec_security *gensec_security, 
+                                 const struct gensec_security_ops *ops) 
+{
+       gensec_security->ops = ops;
+       return gensec_start_mech(gensec_security);
+}
+
 /** 
  * Start a GENSEC sub-mechanism by OID, used in SPNEGO
  *
index 268881e4ba1ee58c14318d230fb4f75fd5f68d1c..be6731abfa9d45c7ebd5f38619644868ed1e57d0 100644 (file)
@@ -54,7 +54,7 @@ struct gensec_security_ops {
        const char *name;
        const char *sasl_name;
        uint8_t auth_type;  /* 0 if not offered on DCE-RPC */
-       const char *oid;  /* NULL if not offered by SPNEGO */
+       const char **oid;  /* NULL if not offered by SPNEGO */
        NTSTATUS (*client_start)(struct gensec_security *gensec_security);
        NTSTATUS (*server_start)(struct gensec_security *gensec_security);
        NTSTATUS (*update)(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx,
@@ -92,6 +92,11 @@ struct gensec_security_ops {
        BOOL enabled;
 };
        
+struct gensec_security_ops_wrapper {
+       const struct gensec_security_ops *op;
+       const char *oid;
+};
+
 #define GENSEC_INTERFACE_VERSION 0
 
 struct gensec_security {
index b93d6ee33dc69f8465347b65f4d664cf18420936..e57739c85c8a0479bcc0e9ff3a2453b25d28ac28 100644 (file)
@@ -35,9 +35,6 @@
 static const gss_OID_desc gensec_gss_krb5_mechanism_oid_desc =
         {9, (void *)discard_const_p(char, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02")};
 
-static const gss_OID_desc gensec_gss_spnego_mechanism_oid_desc =
-        {6, (void *)discard_const_p(char, "\x2b\x06\x01\x05\x05\x02")};
-
 struct gensec_gssapi_state {
        gss_ctx_id_t gssapi_context;
        struct gss_channel_bindings_struct *input_chan_bindings;
@@ -48,11 +45,49 @@ struct gensec_gssapi_state {
 
        DATA_BLOB session_key;
        DATA_BLOB pac;
+
+       krb5_context krb5_context;
+       krb5_ccache ccache;
+       const char *ccache_name;
+
+       gss_cred_id_t cred;
 };
+
+static char *gssapi_error_string(TALLOC_CTX *mem_ctx, 
+                                OM_uint32 maj_stat, OM_uint32 min_stat)
+{
+       OM_uint32 disp_min_stat, disp_maj_stat;
+       gss_buffer_desc maj_error_message;
+       gss_buffer_desc min_error_message;
+       OM_uint32 msg_ctx = 0;
+
+       char *ret;
+
+       maj_error_message.value = NULL;
+       min_error_message.value = NULL;
+       
+       disp_maj_stat = gss_display_status(&disp_min_stat, maj_stat, GSS_C_GSS_CODE,
+                          GSS_C_NULL_OID, &msg_ctx, &maj_error_message);
+       disp_maj_stat = gss_display_status(&disp_min_stat, min_stat, GSS_C_MECH_CODE,
+                          GSS_C_NULL_OID, &msg_ctx, &min_error_message);
+       ret = talloc_asprintf(mem_ctx, "%s: %s", (char *)maj_error_message.value, (char *)min_error_message.value);
+
+       gss_release_buffer(&disp_min_stat, &maj_error_message);
+       gss_release_buffer(&disp_min_stat, &min_error_message);
+
+       return ret;
+}
+
+
 static int gensec_gssapi_destory(void *ptr) 
 {
        struct gensec_gssapi_state *gensec_gssapi_state = ptr;
        OM_uint32 maj_stat, min_stat;
+       
+       if (gensec_gssapi_state->cred != GSS_C_NO_CREDENTIAL) {
+               maj_stat = gss_release_cred(&min_stat, 
+                                           &gensec_gssapi_state->cred);
+       }
 
        if (gensec_gssapi_state->gssapi_context != GSS_C_NO_CONTEXT) {
                maj_stat = gss_delete_sec_context (&min_stat,
@@ -66,13 +101,17 @@ static int gensec_gssapi_destory(void *ptr)
        if (gensec_gssapi_state->client_name != GSS_C_NO_NAME) {
                maj_stat = gss_release_name(&min_stat, &gensec_gssapi_state->client_name);
        }
+       if (gensec_gssapi_state->krb5_context) {
+               krb5_free_context(gensec_gssapi_state->krb5_context);
+       }
        return 0;
 }
 
 static NTSTATUS gensec_gssapi_start(struct gensec_security *gensec_security)
 {
        struct gensec_gssapi_state *gensec_gssapi_state;
-
+       krb5_error_code ret;
+       
        gensec_gssapi_state = talloc(gensec_security, struct gensec_gssapi_state);
        if (!gensec_gssapi_state) {
                return NT_STATUS_NO_MEMORY;
@@ -84,8 +123,6 @@ static NTSTATUS gensec_gssapi_start(struct gensec_security *gensec_security)
        gensec_gssapi_state->server_name = GSS_C_NO_NAME;
        gensec_gssapi_state->client_name = GSS_C_NO_NAME;
 
-       talloc_set_destructor(gensec_gssapi_state, gensec_gssapi_destory); 
-
        /* TODO: Fill in channel bindings */
        gensec_gssapi_state->input_chan_bindings = GSS_C_NO_CHANNEL_BINDINGS;
        
@@ -95,6 +132,12 @@ static NTSTATUS gensec_gssapi_start(struct gensec_security *gensec_security)
        gensec_gssapi_state->session_key = data_blob(NULL, 0);
        gensec_gssapi_state->pac = data_blob(NULL, 0);
 
+       gensec_gssapi_state->krb5_context = NULL;
+
+       gensec_gssapi_state->cred = GSS_C_NO_CREDENTIAL;
+
+       talloc_set_destructor(gensec_gssapi_state, gensec_gssapi_destory); 
+
        if (gensec_security->want_features & GENSEC_FEATURE_SESSION_KEY) {
 #ifndef HAVE_GSSKRB5_GET_INITIATOR_SUBKEY
                /* GSSAPI won't give us the session keys, without the
@@ -119,15 +162,29 @@ static NTSTATUS gensec_gssapi_start(struct gensec_security *gensec_security)
 #endif
        }
 
-       if ((strcmp(gensec_security->ops->oid, GENSEC_OID_KERBEROS5) == 0)
-               || (strcmp(gensec_security->ops->oid, GENSEC_OID_KERBEROS5_OLD) == 0)) {
-               gensec_gssapi_state->gss_oid = &gensec_gss_krb5_mechanism_oid_desc;
-       } else if (strcmp(gensec_security->ops->oid, GENSEC_OID_SPNEGO) == 0) {
-               gensec_gssapi_state->gss_oid = &gensec_gss_spnego_mechanism_oid_desc;
-       } else {
-               DEBUG(1, ("gensec_gssapi incorrectly configured - cannot determine gss OID from %s\n", 
-                         gensec_security->ops->oid));
-               return NT_STATUS_INVALID_PARAMETER;
+       gensec_gssapi_state->gss_oid = &gensec_gss_krb5_mechanism_oid_desc;
+       
+       ret = krb5_init_context(&gensec_gssapi_state->krb5_context);
+       if (ret) {
+               DEBUG(1,("gensec_krb5_start: krb5_init_context failed (%s)\n",                                  
+                        smb_get_krb5_error_message(gensec_gssapi_state->krb5_context, 
+                                                   ret, gensec_gssapi_state)));
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+       
+       if (lp_realm() && *lp_realm()) {
+               char *upper_realm = strupper_talloc(gensec_gssapi_state, lp_realm());
+               if (!upper_realm) {
+                       DEBUG(1,("gensec_krb5_start: could not uppercase realm: %s\n", lp_realm()));
+                       return NT_STATUS_NO_MEMORY;
+               }
+               ret = krb5_set_default_realm(gensec_gssapi_state->krb5_context, upper_realm);
+               if (ret) {
+                       DEBUG(1,("gensec_krb5_start: krb5_set_default_realm failed (%s)\n", 
+                                smb_get_krb5_error_message(gensec_gssapi_state->krb5_context, 
+                                                           ret, gensec_gssapi_state)));
+                       return NT_STATUS_INTERNAL_ERROR;
+               }
        }
 
        return NT_STATUS_OK;
@@ -154,10 +211,7 @@ static NTSTATUS gensec_gssapi_client_start(struct gensec_security *gensec_securi
        NTSTATUS nt_status;
        gss_buffer_desc name_token;
        OM_uint32 maj_stat, min_stat;
-
-       gss_OID_desc hostbased = {10, 
-                                 (void *)discard_const_p(char, "\x2a\x86\x48\x86\xf7\x12"
-                                                         "\x01\x02\x01\x04")};
+       const char *ccache_name;
 
        nt_status = gensec_gssapi_start(gensec_security);
        if (!NT_STATUS_IS_OK(nt_status)) {
@@ -168,18 +222,67 @@ static NTSTATUS gensec_gssapi_client_start(struct gensec_security *gensec_securi
 
        name_token.value = talloc_asprintf(gensec_gssapi_state, "%s@%s", gensec_get_target_service(gensec_security), 
                                           gensec_get_target_hostname(gensec_security));
-       DEBUG(0, ("name: %s\n", (char *)name_token.value));
        name_token.length = strlen(name_token.value);
 
        maj_stat = gss_import_name (&min_stat,
                                    &name_token,
-                                   &hostbased,
+                                   GSS_C_NT_HOSTBASED_SERVICE,
                                    &gensec_gssapi_state->server_name);
+       if (maj_stat) {
+               DEBUG(1, ("GSS Import name of %s failed: %s\n",
+                         (char *)name_token.value,
+                         gssapi_error_string(gensec_gssapi_state, maj_stat, min_stat)));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       name_token.value = cli_credentials_get_principal(gensec_get_credentials(gensec_security), gensec_gssapi_state),
+       name_token.length = strlen(name_token.value);
+
+       maj_stat = gss_import_name (&min_stat,
+                                   &name_token,
+                                   GSS_C_NT_USER_NAME,
+                                   &gensec_gssapi_state->client_name);
+       if (maj_stat) {
+               DEBUG(1, ("GSS Import name of %s failed: %s\n",
+                         (char *)name_token.value,
+                         gssapi_error_string(gensec_gssapi_state, maj_stat, min_stat)));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
 
+       initialize_krb5_error_table();
+       
+       nt_status = kinit_to_ccache(gensec_gssapi_state, 
+                                   gensec_get_credentials(gensec_security),
+                                   gensec_gssapi_state->krb5_context, 
+                                   &gensec_gssapi_state->ccache, &gensec_gssapi_state->ccache_name);
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               return nt_status;
+       }
+
+       maj_stat = gss_krb5_ccache_name(&min_stat, 
+                                       gensec_gssapi_state->ccache_name, 
+                                       NULL);
+       if (maj_stat) {
+               DEBUG(1, ("GSS krb5 ccache set %s failed: %s\n",
+                         ccache_name,
+                         gssapi_error_string(gensec_gssapi_state, maj_stat, min_stat)));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
 
+       maj_stat = gss_acquire_cred(&min_stat, 
+                                   gensec_gssapi_state->client_name,
+                                   GSS_C_INDEFINITE,
+                                   GSS_C_NULL_OID_SET,
+                                   GSS_C_INITIATE,
+                                   &gensec_gssapi_state->cred,
+                                   NULL, 
+                                   NULL);
        if (maj_stat) {
+               DEBUG(1, ("Aquiring initiator credentails failed: %s\n", 
+                         gssapi_error_string(gensec_gssapi_state, maj_stat, min_stat)));
                return NT_STATUS_UNSUCCESSFUL;
        }
+
        return NT_STATUS_OK;
 }
 
@@ -256,22 +359,10 @@ static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security,
        } else if (maj_stat == GSS_S_CONTINUE_NEEDED) {
                return NT_STATUS_MORE_PROCESSING_REQUIRED;
        } else {
-               gss_buffer_desc msg1, msg2;
-               OM_uint32 msg_ctx = 0;
-               
-               msg1.value = NULL;
-               msg2.value = NULL;
-               gss_display_status(&min_stat2, maj_stat, GSS_C_GSS_CODE,
-                                  GSS_C_NULL_OID, &msg_ctx, &msg1);
-               gss_display_status(&min_stat2, min_stat, GSS_C_MECH_CODE,
-                                  GSS_C_NULL_OID, &msg_ctx, &msg2);
-               DEBUG(1, ("gensec_gssapi_update: %s : %s\n", (char *)msg1.value, (char *)msg2.value));
-               gss_release_buffer(&min_stat2, &msg1);
-               gss_release_buffer(&min_stat2, &msg2);
-
+               DEBUG(1, ("GSS Update failed: %s\n", 
+                         gssapi_error_string(out_mem_ctx, maj_stat, min_stat)));
                return nt_status;
        }
-       
 }
 
 static NTSTATUS gensec_gssapi_wrap(struct gensec_security *gensec_security, 
@@ -294,6 +385,8 @@ static NTSTATUS gensec_gssapi_wrap(struct gensec_security *gensec_security,
                            &conf_state,
                            &output_token);
        if (GSS_ERROR(maj_stat)) {
+               DEBUG(1, ("GSS Wrap failed: %s\n", 
+                         gssapi_error_string(mem_ctx, maj_stat, min_stat)));
                return NT_STATUS_ACCESS_DENIED;
        }
        *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length);
@@ -327,6 +420,8 @@ static NTSTATUS gensec_gssapi_unwrap(struct gensec_security *gensec_security,
                              &conf_state,
                              &qop_state);
        if (GSS_ERROR(maj_stat)) {
+               DEBUG(1, ("GSS UnWrap failed: %s\n", 
+                         gssapi_error_string(mem_ctx, maj_stat, min_stat)));
                return NT_STATUS_ACCESS_DENIED;
        }
        *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length);
@@ -369,6 +464,8 @@ static NTSTATUS gensec_gssapi_seal_packet(struct gensec_security *gensec_securit
                            &conf_state,
                            &output_token);
        if (GSS_ERROR(maj_stat)) {
+               DEBUG(1, ("GSS Wrap failed: %s\n", 
+                         gssapi_error_string(mem_ctx, maj_stat, min_stat)));
                return NT_STATUS_ACCESS_DENIED;
        }
 
@@ -381,10 +478,9 @@ static NTSTATUS gensec_gssapi_seal_packet(struct gensec_security *gensec_securit
        memcpy(data, ((uint8_t *)output_token.value) + sig_length, length);
        *sig = data_blob_talloc(mem_ctx, (uint8_t *)output_token.value, sig_length);
 
-DEBUG(0,("gensec_gssapi_seal_packet: siglen: %d inlen: %d, wrap_len: %d\n", sig->length, length, output_token.length - sig_length));
-dump_data(0,sig->data, sig->length);
-dump_data(0,data, length);
-dump_data(0,((uint8_t *)output_token.value) + sig_length, output_token.length - sig_length);
+       dump_data_pw("gensec_gssapi_seal_packet: sig\n", sig->data, sig->length);
+       dump_data_pw("gensec_gssapi_seal_packet: clear\n", data, length);
+       dump_data_pw("gensec_gssapi_seal_packet: sealed\n", ((uint8_t *)output_token.value) + sig_length, output_token.length - sig_length);
 
        gss_release_buffer(&min_stat, &output_token);
 
@@ -408,8 +504,7 @@ static NTSTATUS gensec_gssapi_unseal_packet(struct gensec_security *gensec_secur
        gss_qop_t qop_state;
        DATA_BLOB in;
 
-DEBUG(0,("gensec_gssapi_unseal_packet: siglen: %d\n", sig->length));
-dump_data(0,sig->data, sig->length);
+       dump_data_pw("gensec_gssapi_seal_packet: sig\n", sig->data, sig->length);
 
        in = data_blob_talloc(mem_ctx, NULL, sig->length + length);
 
@@ -426,6 +521,8 @@ dump_data(0,sig->data, sig->length);
                              &conf_state,
                              &qop_state);
        if (GSS_ERROR(maj_stat)) {
+               DEBUG(1, ("GSS UnWrap failed: %s\n", 
+                         gssapi_error_string(mem_ctx, maj_stat, min_stat)));
                return NT_STATUS_ACCESS_DENIED;
        }
 
@@ -467,6 +564,8 @@ static NTSTATUS gensec_gssapi_sign_packet(struct gensec_security *gensec_securit
                            &conf_state,
                            &output_token);
        if (GSS_ERROR(maj_stat)) {
+               DEBUG(1, ("GSS Wrap failed: %s\n", 
+                         gssapi_error_string(mem_ctx, maj_stat, min_stat)));
                return NT_STATUS_ACCESS_DENIED;
        }
 
@@ -479,8 +578,7 @@ static NTSTATUS gensec_gssapi_sign_packet(struct gensec_security *gensec_securit
        /*memcpy(data, ((uint8_t *)output_token.value) + sig_length, length);*/
        *sig = data_blob_talloc(mem_ctx, (uint8_t *)output_token.value, sig_length);
 
-DEBUG(0,("gensec_gssapi_sign_packet: siglen: %d\n", sig->length));
-dump_data(0,sig->data, sig->length);
+       dump_data_pw("gensec_gssapi_seal_packet: sig\n", sig->data, sig->length);
 
        gss_release_buffer(&min_stat, &output_token);
 
@@ -500,8 +598,7 @@ static NTSTATUS gensec_gssapi_check_packet(struct gensec_security *gensec_securi
        gss_qop_t qop_state;
        DATA_BLOB in;
 
-DEBUG(0,("gensec_gssapi_check_packet: siglen: %d\n", sig->length));
-dump_data(0,sig->data, sig->length);
+       dump_data_pw("gensec_gssapi_seal_packet: sig\n", sig->data, sig->length);
 
        in = data_blob_talloc(mem_ctx, NULL, sig->length + length);
 
@@ -518,6 +615,8 @@ dump_data(0,sig->data, sig->length);
                              &conf_state,
                              &qop_state);
        if (GSS_ERROR(maj_stat)) {
+               DEBUG(1, ("GSS UnWrap failed: %s\n", 
+                         gssapi_error_string(mem_ctx, maj_stat, min_stat)));
                return NT_STATUS_ACCESS_DENIED;
        }
 
@@ -525,8 +624,6 @@ dump_data(0,sig->data, sig->length);
                return NT_STATUS_INTERNAL_ERROR;
        }
 
-       /*memcpy(data, output_token.value, length);*/
-
        gss_release_buffer(&min_stat, &output_token);
 
        return NT_STATUS_OK;
@@ -564,8 +661,10 @@ static NTSTATUS gensec_gssapi_session_key(struct gensec_security *gensec_securit
        }
 
 #ifdef HAVE_GSSKRB5_GET_INITIATOR_SUBKEY
+       /* Ensure we only call this for GSSAPI/krb5, otherwise things could get very ugly */
        if ((gensec_gssapi_state->gss_oid->length == gensec_gss_krb5_mechanism_oid_desc.length)
-           && (memcmp(gensec_gssapi_state->gss_oid->elements, gensec_gss_krb5_mechanism_oid_desc.elements, gensec_gssapi_state->gss_oid->length) == 0)) {
+           && (memcmp(gensec_gssapi_state->gss_oid->elements, gensec_gss_krb5_mechanism_oid_desc.elements, 
+                      gensec_gssapi_state->gss_oid->length) == 0)) {
                OM_uint32 maj_stat, min_stat;
                gss_buffer_desc skey;
                
@@ -661,33 +760,16 @@ static NTSTATUS gensec_gssapi_session_info(struct gensec_security *gensec_securi
        return NT_STATUS_OK;
 }
 
-/* As a server, this could in theory accept any GSSAPI mech */
-static const struct gensec_security_ops gensec_gssapi_krb5_security_ops = {
-       .name           = "gssapi_krb5",
-       .sasl_name      = "GSSAPI",
-       .auth_type      = DCERPC_AUTH_TYPE_KRB5,
-       .oid            = GENSEC_OID_KERBEROS5,
-       .client_start   = gensec_gssapi_client_start,
-       .server_start   = gensec_gssapi_server_start,
-       .update         = gensec_gssapi_update,
-       .session_key    = gensec_gssapi_session_key,
-       .session_info   = gensec_gssapi_session_info,
-       .sig_size       = gensec_gssapi_sig_size,
-       .sign_packet    = gensec_gssapi_sign_packet,
-       .check_packet   = gensec_gssapi_check_packet,
-       .seal_packet    = gensec_gssapi_seal_packet,
-       .unseal_packet  = gensec_gssapi_unseal_packet,
-       .wrap           = gensec_gssapi_wrap,
-       .unwrap         = gensec_gssapi_unwrap,
-       .have_feature   = gensec_gssapi_have_feature,
-       .enabled        = False
-
+static const char *gensec_krb5_oids[] = { 
+       GENSEC_OID_KERBEROS5,
+       GENSEC_OID_KERBEROS5_OLD,
+       NULL 
 };
 
 /* As a server, this could in theory accept any GSSAPI mech */
-static const struct gensec_security_ops gensec_gssapi_ms_krb5_security_ops = {
-       .name           = "gssapi_ms_krb5",
-       .oid            = GENSEC_OID_KERBEROS5_OLD,
+static const struct gensec_security_ops gensec_gssapi_krb5_security_ops = {
+       .name           = "gssapi_krb5",
+       .oid            = gensec_krb5_oids,
        .client_start   = gensec_gssapi_client_start,
        .server_start   = gensec_gssapi_server_start,
        .update         = gensec_gssapi_update,
@@ -702,26 +784,6 @@ static const struct gensec_security_ops gensec_gssapi_ms_krb5_security_ops = {
        .unwrap         = gensec_gssapi_unwrap,
        .have_feature   = gensec_gssapi_have_feature,
        .enabled        = False
-
-};
-
-static const struct gensec_security_ops gensec_gssapi_spnego_security_ops = {
-       .name           = "gssapi_spnego",
-       .sasl_name      = "GSS-SPNEGO",
-       .oid            = GENSEC_OID_SPNEGO,
-       .client_start   = gensec_gssapi_client_start,
-       .server_start   = gensec_gssapi_server_start,
-       .update         = gensec_gssapi_update,
-       .session_key    = gensec_gssapi_session_key,
-       .sig_size       = gensec_gssapi_sig_size,
-       .sign_packet    = gensec_gssapi_sign_packet,
-       .check_packet   = gensec_gssapi_check_packet,
-       .seal_packet    = gensec_gssapi_seal_packet,
-       .unseal_packet  = gensec_gssapi_unseal_packet,
-       .wrap           = gensec_gssapi_wrap,
-       .unwrap         = gensec_gssapi_unwrap,
-       .have_feature   = gensec_gssapi_have_feature,
-       .enabled        = False
 };
 
 NTSTATUS gensec_gssapi_init(void)
@@ -735,20 +797,5 @@ NTSTATUS gensec_gssapi_init(void)
                return ret;
        }
 
-
-       ret = gensec_register(&gensec_gssapi_ms_krb5_security_ops);
-       if (!NT_STATUS_IS_OK(ret)) {
-               DEBUG(0,("Failed to register '%s' gensec backend!\n",
-                       gensec_gssapi_ms_krb5_security_ops.name));
-               return ret;
-       }
-
-       ret = gensec_register(&gensec_gssapi_spnego_security_ops);
-       if (!NT_STATUS_IS_OK(ret)) {
-               DEBUG(0,("Failed to register '%s' gensec backend!\n",
-                       gensec_gssapi_spnego_security_ops.name));
-               return ret;
-       }
-
        return ret;
 }
index c850d93fce825e5126ac5ab3bf92fe73365efc48..9e2d113cb80fb09537518e19fdd2f9ecceaadd9b 100644 (file)
@@ -53,9 +53,9 @@ struct gensec_krb5_state {
 
 #ifdef KRB5_DO_VERIFY_PAC
 static NTSTATUS gensec_krb5_pac_checksum(DATA_BLOB pac_data,
-                                           struct PAC_SIGNATURE_DATA *sig,
-                                           struct gensec_krb5_state *gensec_krb5_state,
-                                           uint32 keyusage)
+                                        struct PAC_SIGNATURE_DATA *sig,
+                                        struct gensec_krb5_state *gensec_krb5_state,
+                                        uint32 keyusage)
 {
        krb5_error_code ret;
        krb5_crypto crypto;
@@ -222,7 +222,9 @@ static NTSTATUS gensec_krb5_decode_pac(TALLOC_CTX *mem_ctx,
                return status;
        }
 #endif
-       DEBUG(0,("account_name: %s [%s]\n",logon_info->info3.base.account_name.string, logon_info->info3.base.full_name.string));
+       DEBUG(0,("account_name: %s [%s]\n",
+                logon_info->info3.base.account_name.string, 
+                logon_info->info3.base.full_name.string));
        *logon_info_out = logon_info;
 
        return status;
@@ -233,12 +235,10 @@ static int gensec_krb5_destory(void *ptr)
        struct gensec_krb5_state *gensec_krb5_state = ptr;
 
        if (gensec_krb5_state->ticket.length) { 
-               kerberos_free_data_contents(gensec_krb5_state->context, &gensec_krb5_state->ticket); 
-       }
-       if (gensec_krb5_state->ccache) {
-               /* current heimdal - 0.6.3, which we need anyway, fixes segfaults here */
-               krb5_cc_close(gensec_krb5_state->context, gensec_krb5_state->ccache);
+               kerberos_free_data_contents(gensec_krb5_state->context, 
+                                           &gensec_krb5_state->ticket); 
        }
+       /* ccache freed in a child destructor */
 
        krb5_free_keyblock_contents(gensec_krb5_state->context, 
                                    &gensec_krb5_state->keyblock);
@@ -279,7 +279,9 @@ static NTSTATUS gensec_krb5_start(struct gensec_security *gensec_security)
 
        ret = krb5_init_context(&gensec_krb5_state->context);
        if (ret) {
-               DEBUG(1,("gensec_krb5_start: krb5_init_context failed (%s)\n", error_message(ret)));
+               DEBUG(1,("gensec_krb5_start: krb5_init_context failed (%s)\n", 
+                        smb_get_krb5_error_message(gensec_krb5_state->context, 
+                                                   ret, gensec_krb5_state)));
                return NT_STATUS_INTERNAL_ERROR;
        }
 
@@ -291,14 +293,18 @@ static NTSTATUS gensec_krb5_start(struct gensec_security *gensec_security)
                }
                ret = krb5_set_default_realm(gensec_krb5_state->context, upper_realm);
                if (ret) {
-                       DEBUG(1,("gensec_krb5_start: krb5_set_default_realm failed (%s)\n", error_message(ret)));
+                       DEBUG(1,("gensec_krb5_start: krb5_set_default_realm failed (%s)\n", 
+                                smb_get_krb5_error_message(gensec_krb5_state->context, 
+                                                           ret, gensec_krb5_state)));
                        return NT_STATUS_INTERNAL_ERROR;
                }
        }
 
        ret = krb5_auth_con_init(gensec_krb5_state->context, &gensec_krb5_state->auth_context);
        if (ret) {
-               DEBUG(1,("gensec_krb5_start: krb5_auth_con_init failed (%s)\n", error_message(ret)));
+               DEBUG(1,("gensec_krb5_start: krb5_auth_con_init failed (%s)\n", 
+                        smb_get_krb5_error_message(gensec_krb5_state->context, 
+                                                   ret, gensec_krb5_state)));
                return NT_STATUS_INTERNAL_ERROR;
        }
 
@@ -326,6 +332,8 @@ static NTSTATUS gensec_krb5_client_start(struct gensec_security *gensec_security
        struct gensec_krb5_state *gensec_krb5_state;
        krb5_error_code ret;
        NTSTATUS nt_status;
+       const char *ccache_name;
+
        const char *hostname = gensec_get_target_hostname(gensec_security);
        if (!hostname) {
                DEBUG(1, ("Could not determine hostname for target computer, cannot use kerberos\n"));
@@ -347,7 +355,7 @@ static NTSTATUS gensec_krb5_client_start(struct gensec_security *gensec_security
        ret = krb5_cc_default(gensec_krb5_state->context, &gensec_krb5_state->ccache);
        if (ret) {
                DEBUG(1,("krb5_cc_default failed (%s)\n",
-                        error_message(ret)));
+                        smb_get_krb5_error_message(gensec_krb5_state->context, ret, gensec_krb5_state)));
                return NT_STATUS_INTERNAL_ERROR;
        }
        
@@ -370,7 +378,7 @@ static NTSTATUS gensec_krb5_client_start(struct gensec_security *gensec_security
                        return NT_STATUS_OK;
                case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN:
                        DEBUG(3, ("Server [%s] is not registered with our KDC: %s\n", 
-                                 hostname, error_message(ret)));
+                                 hostname, smb_get_krb5_error_message(gensec_krb5_state->context, ret, gensec_krb5_state)));
                        return NT_STATUS_ACCESS_DENIED;
                case KRB5KDC_ERR_PREAUTH_FAILED:
                case KRB5KRB_AP_ERR_TKT_EXPIRED:
@@ -380,7 +388,7 @@ static NTSTATUS gensec_krb5_client_start(struct gensec_security *gensec_security
                case KRB5_KDCREP_SKEW:
                {
                        DEBUG(3, ("kerberos (mk_req) failed: %s\n", 
-                                 error_message(ret)));
+                                 smb_get_krb5_error_message(gensec_krb5_state->context, ret, gensec_krb5_state)));
                        /* fall down to remaining code */
                }
 
@@ -390,59 +398,20 @@ static NTSTATUS gensec_krb5_client_start(struct gensec_security *gensec_security
                case KRB5_CC_NOTFOUND:
                case ENOENT:
                        
-               {
-                       const char *password;
-                       char *ccache_string;
-                       time_t kdc_time = 0;
-                       password = cli_credentials_get_password(gensec_security->credentials);
-                       if (!NT_STATUS_IS_OK(nt_status)) {
-                               return nt_status;
-                       }
-                       
-                       /* this string should be unique */
-                       ccache_string = talloc_asprintf(gensec_krb5_state, "MEMORY:%s:%s:%s", 
-                                                       cli_credentials_get_principal(gensec_security->credentials, gensec_krb5_state), 
-                                                       gensec_get_target_hostname(gensec_security),
-                                                       generate_random_str(gensec_krb5_state, 16));
-
-                       ret = krb5_cc_resolve(gensec_krb5_state->context, ccache_string, &gensec_krb5_state->ccache);
-                       if (ret) {
-                               DEBUG(1,("failed to generate a new krb5 keytab (%s): %s\n", 
-                                        ccache_string,
-                                        error_message(ret)));
-                               return NT_STATUS_INTERNAL_ERROR;
-                       }
-
-                       ret = kerberos_kinit_password_cc(gensec_krb5_state->context, gensec_krb5_state->ccache, 
-                                                        cli_credentials_get_principal(gensec_security->credentials, gensec_krb5_state), 
-                                                        password, NULL, &kdc_time);
-
-                       /* cope with ticket being in the future due to clock skew */
-                       if ((unsigned)kdc_time > time(NULL)) {
-                               time_t t = time(NULL);
-                               int time_offset =(unsigned)kdc_time-t;
-                               DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset));
-                               krb5_set_real_time(gensec_krb5_state->context, t + time_offset + 1, 0);
-                               break;
-                       }
-       
-                       if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
-                               DEBUG(1,("kinit for %s failed (%s)\n", 
-                                        cli_credentials_get_principal(gensec_security->credentials, gensec_krb5_state), 
-                                        error_message(ret)));
-                               return NT_STATUS_TIME_DIFFERENCE_AT_DC;
-                       }
-                       if (ret) {
-                               DEBUG(1,("kinit for %s failed (%s)\n", 
-                                        cli_credentials_get_principal(gensec_security->credentials, gensec_krb5_state), 
-                                        error_message(ret)));
-                               return NT_STATUS_WRONG_PASSWORD;
-                       }
-                       break;
+               nt_status = kinit_to_ccache(gensec_krb5_state,  
+                                           gensec_security->credentials,
+                                           gensec_krb5_state->context, 
+                                           &gensec_krb5_state->ccache, 
+                                           &ccache_name);
+               
+               if (!NT_STATUS_IS_OK(nt_status)) {
+                       return nt_status;
                }
+               break;
+
                default:
                        DEBUG(0, ("kerberos: %s\n", 
-                                 error_message(ret)));
+                                 smb_get_krb5_error_message(gensec_krb5_state->context, ret, gensec_krb5_state)));
                        return NT_STATUS_UNSUCCESSFUL;
                }
        }
@@ -474,7 +443,7 @@ static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security,
        {
                if (ret) {
                        DEBUG(1,("ads_krb5_mk_req (request ticket) failed (%s)\n",
-                                error_message(ret)));
+                                smb_get_krb5_error_message(gensec_krb5_state->context, ret, out_mem_ctx)));
                        nt_status = NT_STATUS_LOGON_FAILURE;
                } else {
                        DATA_BLOB unwrapped_out;
@@ -515,7 +484,7 @@ static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security,
                                  &inbuf, &repl);
                if (ret) {
                        DEBUG(1,("krb5_rd_rep (mutual authentication) failed (%s)\n",
-                                error_message(ret)));
+                                smb_get_krb5_error_message(gensec_krb5_state->context, ret, out_mem_ctx)));
                        dump_data_pw("Mutual authentication message:\n", inbuf.data, inbuf.length);
                        nt_status = NT_STATUS_ACCESS_DENIED;
                } else {
@@ -707,24 +676,16 @@ static BOOL gensec_krb5_have_feature(struct gensec_security *gensec_security,
        return False;
 }
 
+static const char *gensec_krb5_oids[] = { 
+       GENSEC_OID_KERBEROS5,
+       GENSEC_OID_KERBEROS5_OLD,
+       NULL 
+};
 
 static const struct gensec_security_ops gensec_krb5_security_ops = {
        .name           = "krb5",
        .auth_type      = DCERPC_AUTH_TYPE_KRB5,
-       .oid            = GENSEC_OID_KERBEROS5,
-       .client_start   = gensec_krb5_client_start,
-       .server_start   = gensec_krb5_server_start,
-       .update         = gensec_krb5_update,
-       .session_key    = gensec_krb5_session_key,
-       .session_info   = gensec_krb5_session_info,
-       .have_feature   = gensec_krb5_have_feature,
-       .enabled        = False
-};
-
-static const struct gensec_security_ops gensec_ms_krb5_security_ops = {
-       .name           = "ms_krb5",
-       .auth_type      = DCERPC_AUTH_TYPE_KRB5,
-       .oid            = GENSEC_OID_KERBEROS5_OLD,
+       .oid            = gensec_krb5_oids,
        .client_start   = gensec_krb5_client_start,
        .server_start   = gensec_krb5_server_start,
        .update         = gensec_krb5_update,
@@ -734,7 +695,6 @@ static const struct gensec_security_ops gensec_ms_krb5_security_ops = {
        .enabled        = False
 };
 
-
 NTSTATUS gensec_krb5_init(void)
 {
        NTSTATUS ret;
@@ -746,12 +706,5 @@ NTSTATUS gensec_krb5_init(void)
                return ret;
        }
 
-       ret = gensec_register(&gensec_ms_krb5_security_ops);
-       if (!NT_STATUS_IS_OK(ret)) {
-               DEBUG(0,("Failed to register '%s' gensec backend!\n",
-                       gensec_krb5_security_ops.name));
-               return ret;
-       }
-
        return ret;
 }
index 3d9dbfb1e7f973ec8cacb11e96dce40d8ccddbef..f5d1dd2238ae8db6c0957d9729a17a76f2abac04 100644 (file)
@@ -43,6 +43,8 @@ struct spnego_state {
        enum spnego_state_position state_position;
        struct gensec_security *sub_sec_security;
        BOOL no_response_expected;
+
+       const char *neg_oid;
 };
 
 
@@ -247,15 +249,23 @@ static NTSTATUS gensec_spnego_server_try_fallback(struct gensec_security *gensec
                                                  TALLOC_CTX *out_mem_ctx, 
                                                  const DATA_BLOB in, DATA_BLOB *out) 
 {
-       int i;
+       int i,j;
        int num_ops;
        const struct gensec_security_ops **all_ops = gensec_security_all(&num_ops);
        for (i=0; i < num_ops; i++) {
+               BOOL is_spnego;
                NTSTATUS nt_status;
                if (!all_ops[i]->oid) {
                        continue;
                }
-               if (strcasecmp(GENSEC_OID_SPNEGO,all_ops[i]->oid) == 0) {
+
+               is_spnego = False;
+               for (j=0; all_ops[i]->oid[j]; j++) {
+                       if (strcasecmp(GENSEC_OID_SPNEGO,all_ops[i]->oid[j]) == 0) {
+                               is_spnego = True;
+                       }
+               }
+               if (is_spnego) {
                        continue;
                }
 
@@ -266,15 +276,15 @@ static NTSTATUS gensec_spnego_server_try_fallback(struct gensec_security *gensec
                        return nt_status;
                }
                /* select the sub context */
-               nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security,
-                                                    all_ops[i]->oid);
+               nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
+                                                    all_ops[i]);
                if (!NT_STATUS_IS_OK(nt_status)) {
                        talloc_free(spnego_state->sub_sec_security);
                        spnego_state->sub_sec_security = NULL;
                        continue;
                }
                nt_status = gensec_update(spnego_state->sub_sec_security,
-                                                         out_mem_ctx, in, out);
+                                         out_mem_ctx, in, out);
                if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
                        spnego_state->state_position = SPNEGO_FALLBACK;
                        return nt_status;
@@ -288,84 +298,41 @@ static NTSTATUS gensec_spnego_server_try_fallback(struct gensec_security *gensec
 }
 
 /* 
-   Parse the netTokenInit from the client, to the server.  
-
-   
+   Parse the netTokenInit, either from the client, to the server, or
+   from the server to the client.
 */
 
-static NTSTATUS gensec_spnego_server_parse_negTokenInit(struct gensec_security *gensec_security,
-                                                       struct spnego_state *spnego_state, 
-                                                       TALLOC_CTX *out_mem_ctx, 
-                                                       const char **mechType,
-                                                       const DATA_BLOB unwrapped_in, DATA_BLOB *unwrapped_out) 
-{
-       NTSTATUS nt_status;
-
-       if (!mechType || !mechType[0]) {
-               DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
-               return NT_STATUS_INVALID_PARAMETER;
-       }
-
-       nt_status = gensec_subcontext_start(spnego_state,
-                                           gensec_security,
-                                           &spnego_state->sub_sec_security);
-       if (!NT_STATUS_IS_OK(nt_status)) {
-               return nt_status;
-       }
-       /* select the sub context */
-       nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security,
-                                            mechType[0]);
-       if (!NT_STATUS_IS_OK(nt_status)) {
-               talloc_free(spnego_state->sub_sec_security);
-               spnego_state->sub_sec_security = NULL;
-               return nt_status;
-       }
-
-       if (!unwrapped_in.length) {
-               return NT_STATUS_INVALID_PARAMETER;
-       }
-               
-       nt_status = gensec_update(spnego_state->sub_sec_security,
-                                 out_mem_ctx, 
-                                 unwrapped_in,
-                                 unwrapped_out);
-
-       if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(nt_status)) {
-               DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n", 
-                         spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
-               talloc_free(spnego_state->sub_sec_security);
-               spnego_state->sub_sec_security = NULL;
-       }
-       return nt_status;
-}
-
-static NTSTATUS gensec_spnego_client_parse_negTokenInit(struct gensec_security *gensec_security,
-                                                       struct spnego_state *spnego_state, 
-                                                       TALLOC_CTX *out_mem_ctx, 
-                                                       const char **mechType,
-                                                       const DATA_BLOB unwrapped_in, DATA_BLOB *unwrapped_out) 
+static NTSTATUS gensec_spnego_parse_negTokenInit(struct gensec_security *gensec_security,
+                                                struct spnego_state *spnego_state, 
+                                                TALLOC_CTX *out_mem_ctx, 
+                                                const char **mechType,
+                                                const DATA_BLOB unwrapped_in, DATA_BLOB *unwrapped_out) 
 {
        int i;
-       NTSTATUS nt_status;
+       NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
        DATA_BLOB null_data_blob = data_blob(NULL,0);
 
-       for (i=0; mechType && mechType[i]; i++) {
+       const struct gensec_security_ops_wrapper *all_sec
+               = gensec_security_by_oid_list(out_mem_ctx, 
+                                             mechType,
+                                             GENSEC_OID_SPNEGO);
+       for (i=0; all_sec && all_sec[i].op; i++) {
                nt_status = gensec_subcontext_start(spnego_state,
                                                    gensec_security,
                                                    &spnego_state->sub_sec_security);
                if (!NT_STATUS_IS_OK(nt_status)) {
-                       break;
+                       return nt_status;
                }
                /* select the sub context */
-               nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security,
-                                                    mechType[i]);
+               nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
+                                                    all_sec[i].op);
                if (!NT_STATUS_IS_OK(nt_status)) {
                        talloc_free(spnego_state->sub_sec_security);
                        spnego_state->sub_sec_security = NULL;
                        continue;
                }
-               
-               if (i == 0) {
+
+               if ((i == 0) && (strcmp(all_sec[0].oid, mechType[0]) == 0)) {
                        nt_status = gensec_update(spnego_state->sub_sec_security,
                                                  out_mem_ctx, 
                                                  unwrapped_in,
@@ -376,20 +343,51 @@ static NTSTATUS gensec_spnego_client_parse_negTokenInit(struct gensec_security *
                                                  out_mem_ctx, 
                                                  null_data_blob, 
                                                  unwrapped_out);
+                       /* it is likely that a NULL input token will
+                        * not be liked by most mechs, so just push us
+                        * along the merry-go-round again, and hope
+                        * for better luck next time */
+
+                       if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER)) {
+                               nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
+                       }
                }
-               if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(nt_status)) {
+                       
+               if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER) 
+                   && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) 
+                   && !NT_STATUS_IS_OK(nt_status)) {
                        DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n", 
                                  spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
                        talloc_free(spnego_state->sub_sec_security);
                        spnego_state->sub_sec_security = NULL;
-                       /* If the mech failed on first packet generation, pretend it never actually started */
+
+                       /* We started the mech correctly, and the
+                        * input from the other side was valid.
+                        * Return the error (say bad password, invalid
+                        * ticket) */
+                       return nt_status;
+
+               } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER)) {
+                       /* Pretend we never started it (lets the first run find some incompatible demand) */
+
+                       DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed to parse: %s\n", 
+                                 spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
+                       talloc_free(spnego_state->sub_sec_security);
+                       spnego_state->sub_sec_security = NULL;
                        continue;
                }
-               return nt_status;
-       }
-       if (!mechType || !mechType[i]) {
-               DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
+
+               spnego_state->neg_oid = all_sec[i].oid;
+
+               return nt_status; /* OK, INVALID_PARAMETER ore MORE PROCESSING */
        }
+
+       DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
+       /* we could re-negotiate here, but it would only work
+        * if the client or server lied about what it could
+        * support the first time.  Lets keep this code to
+        * reality */
+
        return NT_STATUS_INVALID_PARAMETER;
 }
 
@@ -403,10 +401,10 @@ static NTSTATUS gensec_spnego_client_negTokenInit(struct gensec_security *gensec
                                                  TALLOC_CTX *out_mem_ctx, 
                                                  const DATA_BLOB in, DATA_BLOB *out) 
 {
-       DATA_BLOB null_data_blob = data_blob(NULL,0);
+       DATA_BLOB null_data_blob = data_blob(NULL, 0);
        NTSTATUS nt_status;
        const char **mechTypes = NULL;
-       DATA_BLOB unwrapped_out = data_blob(NULL,0);
+       DATA_BLOB unwrapped_out = data_blob(NULL, 0);
 
        mechTypes = gensec_security_oids(out_mem_ctx, GENSEC_OID_SPNEGO);
 
@@ -482,52 +480,16 @@ static NTSTATUS gensec_spnego_server_negTokenTarg(struct gensec_security *gensec
        spnego_out.negTokenTarg.mechListMIC = null_data_blob;
        spnego_out.negTokenTarg.supportedMech = NULL;
 
-       if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
-               spnego_out.negTokenTarg.supportedMech
-                       = spnego_state->sub_sec_security->ops->oid;
+       if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {   
+               spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
                spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
                spnego_state->state_position = SPNEGO_SERVER_TARG;
        } else if (NT_STATUS_IS_OK(nt_status)) {
                if (unwrapped_out.data) {
-                       spnego_out.negTokenTarg.supportedMech
-                               = spnego_state->sub_sec_security->ops->oid;
+                       spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
                }
                spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
                spnego_state->state_position = SPNEGO_DONE;
-       } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER)) {
-               if (spnego_state->sub_sec_security) {
-                       /* we have a mech, but we just didn't get the input parameter */
-                       spnego_out.negTokenTarg.supportedMech
-                               = spnego_state->sub_sec_security->ops->oid;
-               } else {
-                       const char **mechTypes = gensec_security_oids(out_mem_ctx, GENSEC_OID_SPNEGO);
-                       if (!mechTypes) {
-                               DEBUG(1, ("no GENSEC OID backends available\n"));
-                               return NT_STATUS_INVALID_PARAMETER;
-                       }
-
-                       nt_status = gensec_subcontext_start(spnego_state, 
-                                                           gensec_security, 
-                                                           &spnego_state->sub_sec_security);
-                       if (!NT_STATUS_IS_OK(nt_status)) {
-                               return nt_status;
-                       }
-                       /* select our preferred mech */
-                       nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security,
-                                                            mechTypes[0]);
-                       if (!NT_STATUS_IS_OK(nt_status)) {
-                               talloc_free(spnego_state->sub_sec_security);
-                               spnego_state->sub_sec_security = NULL;
-                               return nt_status;
-                       }
-
-                       /* we should be sending the whole list here */
-                       spnego_out.negTokenTarg.supportedMech = mechTypes[0];
-               }
-                       
-               spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
-               spnego_state->state_position = SPNEGO_SERVER_TARG;
-               nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
        } else {
                spnego_out.negTokenTarg.negResult = SPNEGO_REJECT;
                DEBUG(2, ("SPNEGO login failed: %s\n", nt_errstr(nt_status)));
@@ -588,7 +550,7 @@ static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TA
                                return NT_STATUS_INVALID_PARAMETER;
                        }
                        
-                       nt_status = gensec_spnego_server_parse_negTokenInit(gensec_security,
+                       nt_status = gensec_spnego_parse_negTokenInit(gensec_security,
                                                                     spnego_state,
                                                                     out_mem_ctx, 
                                                                     spnego.negTokenInit.mechTypes,
@@ -613,7 +575,7 @@ static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TA
                        spnego_out.negTokenInit.reqFlags = 0;
                        spnego_out.negTokenInit.mechListMIC
                                = data_blob_string_const(talloc_asprintf(out_mem_ctx, "%s$@%s", lp_netbios_name(), lp_realm()));
-                       spnego_out.negTokenInit.mechToken = unwrapped_out;
+                       spnego_out.negTokenInit.mechToken = data_blob(NULL, 0);
                        
                        if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
                                DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n"));
@@ -662,7 +624,7 @@ static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TA
                        DEBUG(5, ("Server claims it's principal name is %s (ignored)\n", spnego.negTokenInit.targetPrincipal));
                }
 
-               nt_status = gensec_spnego_client_parse_negTokenInit(gensec_security,
+               nt_status = gensec_spnego_parse_negTokenInit(gensec_security,
                                                             spnego_state,
                                                             out_mem_ctx, 
                                                             spnego.negTokenInit.mechTypes,
@@ -674,9 +636,8 @@ static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TA
                        return nt_status;
                }
 
+               my_mechs[0] = spnego_state->neg_oid;
                /* compose reply */
-               my_mechs[0] = spnego_state->sub_sec_security->ops->oid;
-               
                spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
                spnego_out.negTokenInit.mechTypes = my_mechs;
                spnego_out.negTokenInit.reqFlags = 0;
@@ -851,11 +812,16 @@ static BOOL gensec_spnego_have_feature(struct gensec_security *gensec_security,
                                   feature);
 }
 
+static const char *gensec_spnego_oids[] = { 
+       GENSEC_OID_SPNEGO,
+       NULL 
+};
+
 static const struct gensec_security_ops gensec_spnego_security_ops = {
        .name           = "spnego",
        .sasl_name      = "GSS-SPNEGO",
        .auth_type      = DCERPC_AUTH_TYPE_SPNEGO,
-       .oid            = GENSEC_OID_SPNEGO,
+       .oid            = gensec_spnego_oids,
        .client_start   = gensec_spnego_client_start,
        .server_start   = gensec_spnego_server_start,
        .update         = gensec_spnego_update,
index 4daf0ea07a77959860d6a70aa8262ea1f920f6d6..ec7df4c2f145d9a9d50913675b289ce90380a48c 100644 (file)
@@ -95,5 +95,10 @@ BOOL kerberos_compatible_enctypes(krb5_context context, krb5_enctype enctype1, k
 void kerberos_free_data_contents(krb5_context context, krb5_data *pdata);
 krb5_error_code smb_krb5_kt_free_entry(krb5_context context, krb5_keytab_entry *kt_entry);
 char *smb_get_krb5_error_message(krb5_context context, krb5_error_code code, TALLOC_CTX *mem_ctx);
+NTSTATUS kinit_to_ccache(TALLOC_CTX *parent_ctx,
+                        struct cli_credentials *credentials,
+                        krb5_context context,
+                        krb5_ccache *ccache,
+                        const char **ccache_name);
 #endif /* HAVE_KRB5 */
 
index a43e6bb5179fee6f79828c0f5a71fe58940cc03d..38c8862747deedc46ae14fae1333866a8d8f408f 100644 (file)
@@ -5,6 +5,7 @@ INIT_OBJ_FILES = auth/kerberos/kerberos.o
 ADD_OBJ_FILES = \
                auth/kerberos/clikrb5.o \
                auth/kerberos/kerberos_verify.o \
+               auth/kerberos/kerberos_util.o \
                auth/kerberos/gssapi_parse.o
 # End SUBSYSTEM KERBEROS
 #################################
diff --git a/source/auth/kerberos/kerberos_util.c b/source/auth/kerberos/kerberos_util.c
new file mode 100644 (file)
index 0000000..55975b2
--- /dev/null
@@ -0,0 +1,120 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   Kerberos utility functions for GENSEC
+   
+   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "system/kerberos.h"
+#include "system/time.h"
+#include "system/network.h"
+#include "auth/kerberos/kerberos.h"
+#include "auth/auth.h"
+
+struct ccache_container {
+       krb5_context krb5_context;
+       krb5_ccache ccache;
+} ccache_container;
+
+#if 0
+static int free_ccache(void *ptr) {
+       struct ccache_container *ccc = ptr;
+       /* current heimdal - 0.6.3, which we need anyway, fixes segfaults here */
+       krb5_cc_close(ccc->krb5_context, ccc->ccache);
+
+       return 0;
+}
+#endif
+
+/**
+ * Return a freshly allocated ccache (destroyed by destructor on child
+ * of parent_ctx), for a given set of client credentials 
+ */
+
+ NTSTATUS kinit_to_ccache(TALLOC_CTX *parent_ctx,
+                         struct cli_credentials *credentials,
+                         krb5_context context,
+                         krb5_ccache *ccache,
+                         const char **ccache_name) 
+{
+       krb5_error_code ret;
+       const char *password;
+       char *ccache_string;
+       time_t kdc_time = 0;
+       struct ccache_container *mem_ctx = talloc(parent_ctx, struct ccache_container);
+
+       if (!mem_ctx) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       password = cli_credentials_get_password(credentials);
+       
+       /* this string should be unique */
+       ccache_string = talloc_asprintf(mem_ctx, "MEMORY:%s_%s", 
+                                       cli_credentials_get_principal(credentials, mem_ctx), 
+                                       generate_random_str(mem_ctx, 16));
+       
+       ret = krb5_cc_resolve(context, ccache_string, ccache);
+       if (ret) {
+               DEBUG(1,("failed to generate a new krb5 keytab (%s): %s\n", 
+                        ccache_string,
+                        error_message(ret)));
+               talloc_free(mem_ctx);
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
+       mem_ctx->krb5_context = context;
+       mem_ctx->ccache = *ccache;
+
+#if 0
+       talloc_set_destructor(mem_ctx, free_ccache);
+#endif
+       ret = kerberos_kinit_password_cc(context, *ccache, 
+                                        cli_credentials_get_principal(credentials, mem_ctx), 
+                                        password, NULL, &kdc_time);
+       
+       /* cope with ticket being in the future due to clock skew */
+       if ((unsigned)kdc_time > time(NULL)) {
+               time_t t = time(NULL);
+               int time_offset =(unsigned)kdc_time-t;
+               DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset));
+               krb5_set_real_time(context, t + time_offset + 1, 0);
+       }
+       
+       if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
+               DEBUG(1,("kinit for %s failed (%s)\n", 
+                        cli_credentials_get_principal(credentials, mem_ctx), 
+                        smb_get_krb5_error_message(context, 
+                                                   ret, mem_ctx)));
+               talloc_free(mem_ctx);
+               return NT_STATUS_TIME_DIFFERENCE_AT_DC;
+       }
+       if (ret) {
+               DEBUG(1,("kinit for %s failed (%s)\n", 
+                        cli_credentials_get_principal(credentials, mem_ctx), 
+                        smb_get_krb5_error_message(context, 
+                                                   ret, mem_ctx)));
+               talloc_free(mem_ctx);
+               return NT_STATUS_WRONG_PASSWORD;
+       } 
+       *ccache_name = ccache_string;
+
+       return NT_STATUS_OK;
+}
index 927b12d454b586528b3526ab28e31374fabae1e1..0497e3effa233b9aceb0438c047a5babaa114f56 100644 (file)
@@ -93,7 +93,8 @@ static krb5_error_code ads_keytab_verify_ticket(TALLOC_CTX *mem_ctx, krb5_contex
 
        /* Generate the list of principal names which we expect
         * clients might want to use for authenticating to the file
-        * service.  We allow name$,{host,cifs}/{name,fqdn,name.REALM}. */
+        * service.  We allow name$,{host,service}/{name,fqdn,name.REALM}.
+        * (where service is specified by the caller) */
 
        my_name = lp_netbios_name();
 
@@ -103,9 +104,9 @@ static krb5_error_code ads_keytab_verify_ticket(TALLOC_CTX *mem_ctx, krb5_contex
        asprintf(&valid_princ_formats[1], "host/%s@%s", my_name, lp_realm());
        asprintf(&valid_princ_formats[2], "host/%s@%s", my_fqdn, lp_realm());
        asprintf(&valid_princ_formats[3], "host/%s.%s@%s", my_name, lp_realm(), lp_realm());
-       asprintf(&valid_princ_formats[4], "cifs/%s@%s", my_name, lp_realm());
-       asprintf(&valid_princ_formats[5], "cifs/%s@%s", my_fqdn, lp_realm());
-       asprintf(&valid_princ_formats[6], "cifs/%s.%s@%s", my_name, lp_realm(), lp_realm());
+       asprintf(&valid_princ_formats[4], "%s/%s@%s", service, my_name, lp_realm());
+       asprintf(&valid_princ_formats[5], "%s/%s@%s", service, my_fqdn, lp_realm());
+       asprintf(&valid_princ_formats[6], "%s/%s.%s@%s", service, my_name, lp_realm(), lp_realm());
 
        ZERO_STRUCT(kt_entry);
        ZERO_STRUCT(kt_cursor);
index ac007ae3abc57926ca2ae684e22943676ed85cbf..a8c5828295ee78fe7e1dc3e1962570f6db92cdf4 100644 (file)
@@ -325,11 +325,16 @@ NTSTATUS gensec_ntlmssp_start(struct gensec_security *gensec_security)
        return NT_STATUS_OK;
 }
 
+static const char *gensec_ntlmssp_oids[] = { 
+       GENSEC_OID_NTLMSSP, 
+       NULL
+};
+
 static const struct gensec_security_ops gensec_ntlmssp_security_ops = {
        .name           = "ntlmssp",
        .sasl_name      = "NTLM",
        .auth_type      = DCERPC_AUTH_TYPE_NTLMSSP,
-       .oid            = GENSEC_OID_NTLMSSP,
+       .oid            = gensec_ntlmssp_oids,
        .client_start   = gensec_ntlmssp_client_start,
        .server_start   = gensec_ntlmssp_server_start,
        .update         = gensec_ntlmssp_update,
index 8204cb769de089fd08ea1e4846d5883dfc2d956d..4b20559f9098b668fc1bcbd3df90882ead3fc446 100644 (file)
@@ -72,6 +72,7 @@ struct auth_methods;
 struct schannel_state;
 struct spnego_data;
 struct gensec_security;
+struct gensec_security_ops;
 typedef NTSTATUS (*gensec_password_callback)(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx, 
                                             char **password);
 struct gensec_ntlmssp_state;