summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Bartlett <abartlet@samba.org>2005-05-15 23:42:11 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 13:16:45 -0500
commit5c6dd5e800b879efdce3bbc3a16f32c5e78b4917 (patch)
tree13a8286bebac583bd979d278b7f12fac25c8513b
parent8f96c42027d282eec047d1b25951a33bc2fce71f (diff)
downloadsamba-5c6dd5e800b879efdce3bbc3a16f32c5e78b4917.tar.gz
samba-5c6dd5e800b879efdce3bbc3a16f32c5e78b4917.tar.bz2
samba-5c6dd5e800b879efdce3bbc3a16f32c5e78b4917.zip
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)
-rw-r--r--source4/auth/gensec/gensec.c147
-rw-r--r--source4/auth/gensec/gensec.h7
-rw-r--r--source4/auth/gensec/gensec_gssapi.c261
-rw-r--r--source4/auth/gensec/gensec_krb5.c131
-rw-r--r--source4/auth/gensec/spnego.c204
-rw-r--r--source4/auth/kerberos/kerberos.h5
-rw-r--r--source4/auth/kerberos/kerberos.mk1
-rw-r--r--source4/auth/kerberos/kerberos_util.c120
-rw-r--r--source4/auth/kerberos/kerberos_verify.c9
-rw-r--r--source4/auth/ntlmssp/ntlmssp.c7
-rw-r--r--source4/include/structs.h1
11 files changed, 558 insertions, 335 deletions
diff --git a/source4/auth/gensec/gensec.c b/source4/auth/gensec/gensec.c
index 77ba58fcde..1608f21114 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.
@@ -284,6 +391,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
*
* @note This should also be used when you wish to just start NLTMSSP (for example), as it uses a
diff --git a/source4/auth/gensec/gensec.h b/source4/auth/gensec/gensec.h
index 268881e4ba..be6731abfa 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 b93d6ee33d..e57739c85c 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 c850d93fce..9e2d113cb8 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 3d9dbfb1e7..f5d1dd2238 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 4daf0ea07a..ec7df4c2f1 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 a43e6bb517..38c8862747 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 0000000000..55975b2594
--- /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 <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;
+}
diff --git a/source4/auth/kerberos/kerberos_verify.c b/source4/auth/kerberos/kerberos_verify.c
index 927b12d454..0497e3effa 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 ac007ae3ab..a8c5828295 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 8204cb769d..4b20559f90 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;