From 5c6dd5e800b879efdce3bbc3a16f32c5e78b4917 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Sun, 15 May 2005 23:42:11 +0000 Subject: [PATCH] r6800: A big GENSEC update: 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 (This used to be commit 0101728d8e2ed9419eb31fe95047944a718ba135) --- source4/auth/gensec/gensec.c | 147 +++++++++++-- source4/auth/gensec/gensec.h | 7 +- source4/auth/gensec/gensec_gssapi.c | 261 ++++++++++++++---------- source4/auth/gensec/gensec_krb5.c | 131 ++++-------- source4/auth/gensec/spnego.c | 204 ++++++++---------- source4/auth/kerberos/kerberos.h | 5 + source4/auth/kerberos/kerberos.mk | 1 + source4/auth/kerberos/kerberos_util.c | 120 +++++++++++ source4/auth/kerberos/kerberos_verify.c | 9 +- source4/auth/ntlmssp/ntlmssp.c | 7 +- source4/include/structs.h | 1 + 11 files changed, 558 insertions(+), 335 deletions(-) create mode 100644 source4/auth/kerberos/kerberos_util.c diff --git a/source4/auth/gensec/gensec.c b/source4/auth/gensec/gensec.c index 77ba58fcde9..1608f211140 100644 --- a/source4/auth/gensec/gensec.c +++ b/source4/auth/gensec/gensec.c @@ -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 * diff --git a/source4/auth/gensec/gensec.h b/source4/auth/gensec/gensec.h index 268881e4ba1..be6731abfa9 100644 --- a/source4/auth/gensec/gensec.h +++ b/source4/auth/gensec/gensec.h @@ -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 { diff --git a/source4/auth/gensec/gensec_gssapi.c b/source4/auth/gensec/gensec_gssapi.c index b93d6ee33dc..e57739c85c8 100644 --- a/source4/auth/gensec/gensec_gssapi.c +++ b/source4/auth/gensec/gensec_gssapi.c @@ -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; } diff --git a/source4/auth/gensec/gensec_krb5.c b/source4/auth/gensec/gensec_krb5.c index c850d93fce8..9e2d113cb80 100644 --- a/source4/auth/gensec/gensec_krb5.c +++ b/source4/auth/gensec/gensec_krb5.c @@ -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; } diff --git a/source4/auth/gensec/spnego.c b/source4/auth/gensec/spnego.c index 3d9dbfb1e7f..f5d1dd2238a 100644 --- a/source4/auth/gensec/spnego.c +++ b/source4/auth/gensec/spnego.c @@ -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, diff --git a/source4/auth/kerberos/kerberos.h b/source4/auth/kerberos/kerberos.h index 4daf0ea07a7..ec7df4c2f14 100644 --- a/source4/auth/kerberos/kerberos.h +++ b/source4/auth/kerberos/kerberos.h @@ -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 */ diff --git a/source4/auth/kerberos/kerberos.mk b/source4/auth/kerberos/kerberos.mk index a43e6bb5179..38c8862747d 100644 --- a/source4/auth/kerberos/kerberos.mk +++ b/source4/auth/kerberos/kerberos.mk @@ -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/source4/auth/kerberos/kerberos_util.c b/source4/auth/kerberos/kerberos_util.c new file mode 100644 index 00000000000..55975b2594d --- /dev/null +++ b/source4/auth/kerberos/kerberos_util.c @@ -0,0 +1,120 @@ +/* + Unix SMB/CIFS implementation. + + Kerberos utility functions for GENSEC + + Copyright (C) Andrew Bartlett 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; +} diff --git a/source4/auth/kerberos/kerberos_verify.c b/source4/auth/kerberos/kerberos_verify.c index 927b12d454b..0497e3effa2 100644 --- a/source4/auth/kerberos/kerberos_verify.c +++ b/source4/auth/kerberos/kerberos_verify.c @@ -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); diff --git a/source4/auth/ntlmssp/ntlmssp.c b/source4/auth/ntlmssp/ntlmssp.c index ac007ae3abc..a8c5828295e 100644 --- a/source4/auth/ntlmssp/ntlmssp.c +++ b/source4/auth/ntlmssp/ntlmssp.c @@ -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, diff --git a/source4/include/structs.h b/source4/include/structs.h index 8204cb769de..4b20559f909 100644 --- a/source4/include/structs.h +++ b/source4/include/structs.h @@ -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; -- 2.34.1