summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source4/include/includes.h3
-rw-r--r--source4/libcli/auth/gensec.c104
-rw-r--r--source4/libcli/auth/gensec.h64
-rw-r--r--source4/libcli/auth/gensec_ntlmssp.c122
-rw-r--r--source4/libcli/auth/ntlmssp.c17
-rw-r--r--source4/libcli/auth/spnego.c522
-rw-r--r--source4/libcli/auth/spnego.h33
-rw-r--r--source4/libcli/auth/spnego_parse.c343
-rw-r--r--source4/libcli/config.m45
-rw-r--r--source4/libcli/util/asn1.c2
-rw-r--r--source4/librpc/rpc/dcerpc.c58
-rw-r--r--source4/librpc/rpc/dcerpc.h30
-rw-r--r--source4/librpc/rpc/dcerpc_auth.c63
-rw-r--r--source4/librpc/rpc/dcerpc_ntlm.c132
-rw-r--r--source4/librpc/rpc/dcerpc_schannel.c194
-rw-r--r--source4/librpc/rpc/dcerpc_util.c4
-rw-r--r--source4/utils/ntlm_auth.c832
17 files changed, 1169 insertions, 1359 deletions
diff --git a/source4/include/includes.h b/source4/include/includes.h
index 25725bdf4a..cd5de0d64e 100644
--- a/source4/include/includes.h
+++ b/source4/include/includes.h
@@ -649,11 +649,12 @@ extern int errno;
#include "md5.h"
#include "hmacmd5.h"
-#include "libcli/auth/spnego.h"
#include "libcli/auth/ntlmssp.h"
#include "libcli/auth/credentials.h"
#include "libcli/auth/schannel.h"
#include "libcli/auth/kerberos.h"
+#include "libcli/auth/gensec.h"
+#include "libcli/auth/spnego.h"
#include "auth/auth.h"
diff --git a/source4/libcli/auth/gensec.c b/source4/libcli/auth/gensec.c
new file mode 100644
index 0000000000..138c4af35c
--- /dev/null
+++ b/source4/libcli/auth/gensec.c
@@ -0,0 +1,104 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Generic Authentication Interface
+
+ Copyright (C) Andrew Tridgell 2003
+ 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"
+
+static const struct gensec_security_ops gensec_ntlmssp_security_ops = {
+ .name = "ntlmssp",
+ .sasl_name = "NTLM",
+ .auth_type = DCERPC_AUTH_TYPE_NTLMSSP,
+ .oid = OID_NTLMSSP,
+ .client_start = gensec_ntlmssp_client_start,
+ .update = gensec_ntlmssp_update,
+ .seal = gensec_ntlmssp_seal_packet,
+ .sign = gensec_ntlmssp_sign_packet,
+ .check_sig = gensec_ntlmssp_check_packet,
+ .unseal = gensec_ntlmssp_unseal_packet,
+ .session_key = gensec_ntlmssp_session_key,
+ .end = gensec_ntlmssp_end
+};
+
+
+static const struct gensec_security_ops gensec_spnego_security_ops = {
+ .name = "spnego",
+ .sasl_name = "GSS-SPNEGO",
+ .oid = OID_SPNEGO,
+ .client_start = gensec_spnego_client_start,
+ .update = gensec_spnego_update,
+ .seal = gensec_spnego_seal_packet,
+ .sign = gensec_spnego_sign_packet,
+ .check_sig = gensec_spnego_check_packet,
+ .unseal = gensec_spnego_unseal_packet,
+ .session_key = gensec_spnego_session_key,
+ .end = gensec_spnego_end
+};
+
+static const struct gensec_security_ops *generic_security_ops[] = {
+ &gensec_ntlmssp_security_ops,
+ &gensec_spnego_security_ops,
+ NULL
+};
+
+const struct gensec_security_ops *gensec_security_by_authtype(uint8_t auth_type)
+{
+ int i;
+ for (i=0; generic_security_ops[i]; i++) {
+ if (generic_security_ops[i]->auth_type == auth_type) {
+ return generic_security_ops[i];
+ }
+ }
+
+ return NULL;
+}
+
+const struct gensec_security_ops *gensec_security_by_oid(const char *oid)
+{
+ int i;
+ for (i=0; generic_security_ops[i]; i++) {
+ if (generic_security_ops[i]->oid &&
+ (strcmp(generic_security_ops[i]->oid, oid) == 0)) {
+ return generic_security_ops[i];
+ }
+ }
+
+ return NULL;
+}
+
+const struct gensec_security_ops *gensec_security_by_sasl_name(const char *sasl_name)
+{
+ int i;
+ for (i=0; generic_security_ops[i]; i++) {
+ if (generic_security_ops[i]->sasl_name
+ && (strcmp(generic_security_ops[i]->sasl_name, sasl_name) == 0)) {
+ return generic_security_ops[i];
+ }
+ }
+
+ return NULL;
+}
+
+const struct gensec_security_ops **gensec_security_all(void)
+{
+ return generic_security_ops;
+}
+
diff --git a/source4/libcli/auth/gensec.h b/source4/libcli/auth/gensec.h
new file mode 100644
index 0000000000..2a469e0f57
--- /dev/null
+++ b/source4/libcli/auth/gensec.h
@@ -0,0 +1,64 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Generic Authentication Interface
+
+ Copyright (C) Andrew Tridgell 2003
+ 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.
+*/
+
+
+struct gensec_security;
+struct gensec_user {
+ const char *domain;
+ const char *name;
+ const char *password;
+};
+/* GENSEC mode */
+enum gensec_role
+{
+ GENSEC_SERVER,
+ GENSEC_CLIENT
+};
+
+struct gensec_security_ops {
+ const char *name;
+ const char *sasl_name;
+ uint8 auth_type;
+ const char *oid; /* NULL if not offered by SPENGO */
+ 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,
+ const DATA_BLOB in, DATA_BLOB *out);
+ NTSTATUS (*seal)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx,
+ uint8_t *data, size_t length, DATA_BLOB *sig);
+ NTSTATUS (*sign)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx,
+ const uint8_t *data, size_t length, DATA_BLOB *sig);
+ NTSTATUS (*check_sig)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx,
+ const uint8_t *data, size_t length, const DATA_BLOB *sig);
+ NTSTATUS (*unseal)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx,
+ uint8_t *data, size_t length, DATA_BLOB *sig);
+ NTSTATUS (*session_key)(struct gensec_security *gensec_security, DATA_BLOB *session_key);
+ void (*end)(struct gensec_security *gensec_security);
+};
+
+struct gensec_security {
+ struct gensec_user user;
+ void *private_data;
+ const struct gensec_security_ops *ops;
+};
+
diff --git a/source4/libcli/auth/gensec_ntlmssp.c b/source4/libcli/auth/gensec_ntlmssp.c
new file mode 100644
index 0000000000..f7e9dddd2f
--- /dev/null
+++ b/source4/libcli/auth/gensec_ntlmssp.c
@@ -0,0 +1,122 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ dcerpc authentication operations
+
+ Copyright (C) Andrew Tridgell 2003
+ 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"
+
+
+NTSTATUS gensec_ntlmssp_client_start(struct gensec_security *gensec_security)
+{
+ struct ntlmssp_state *ntlmssp_state = NULL;
+ NTSTATUS status;
+
+ status = ntlmssp_client_start(&ntlmssp_state);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = ntlmssp_set_domain(ntlmssp_state, gensec_security->user.domain);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = ntlmssp_set_username(ntlmssp_state, gensec_security->user.name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = ntlmssp_set_password(ntlmssp_state, gensec_security->user.password);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ gensec_security->private_data = ntlmssp_state;
+
+ return status;
+}
+
+/*
+ wrappers for the ntlmssp_*() functions
+*/
+NTSTATUS gensec_ntlmssp_unseal_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ uint8_t *data, size_t length, DATA_BLOB *sig)
+{
+ struct ntlmssp_state *ntlmssp_state = gensec_security->private_data;
+
+ return ntlmssp_unseal_packet(ntlmssp_state, mem_ctx, data, length, sig);
+}
+
+NTSTATUS gensec_ntlmssp_check_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const uint8_t *data, size_t length,
+ const DATA_BLOB *sig)
+{
+ struct ntlmssp_state *ntlmssp_state = gensec_security->private_data;
+
+ return ntlmssp_check_packet(ntlmssp_state, mem_ctx, data, length, sig);
+}
+
+NTSTATUS gensec_ntlmssp_seal_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ uint8_t *data, size_t length,
+ DATA_BLOB *sig)
+{
+ struct ntlmssp_state *ntlmssp_state = gensec_security->private_data;
+
+ return ntlmssp_seal_packet(ntlmssp_state, mem_ctx, data, length, sig);
+}
+
+NTSTATUS gensec_ntlmssp_sign_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const uint8_t *data, size_t length,
+ DATA_BLOB *sig)
+{
+ struct ntlmssp_state *ntlmssp_state = gensec_security->private_data;
+
+ return ntlmssp_sign_packet(ntlmssp_state, mem_ctx, data, length, sig);
+}
+
+NTSTATUS gensec_ntlmssp_session_key(struct gensec_security *gensec_security,
+ DATA_BLOB *session_key)
+{
+ struct ntlmssp_state *ntlmssp_state = gensec_security->private_data;
+
+ return ntlmssp_session_key(ntlmssp_state, session_key);
+}
+
+NTSTATUS gensec_ntlmssp_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx,
+ const DATA_BLOB in, DATA_BLOB *out)
+{
+ struct ntlmssp_state *ntlmssp_state = gensec_security->private_data;
+
+ return ntlmssp_update(ntlmssp_state, out_mem_ctx, in, out);
+}
+
+void gensec_ntlmssp_end(struct gensec_security *gensec_security)
+{
+ struct ntlmssp_state *ntlmssp_state = gensec_security->private_data;
+
+ ntlmssp_end(&ntlmssp_state);
+
+ gensec_security->private_data = NULL;
+}
diff --git a/source4/libcli/auth/ntlmssp.c b/source4/libcli/auth/ntlmssp.c
index dab8506b81..6830db3f90 100644
--- a/source4/libcli/auth/ntlmssp.c
+++ b/source4/libcli/auth/ntlmssp.c
@@ -287,6 +287,23 @@ NTSTATUS ntlmssp_update(struct ntlmssp_state *ntlmssp_state,
}
/**
+ * Return the NTLMSSP master session key
+ *
+ * @param ntlmssp_state NTLMSSP State
+ */
+
+NTSTATUS ntlmssp_session_key(struct ntlmssp_state *ntlmssp_state,
+ DATA_BLOB *session_key)
+{
+ if (!ntlmssp_state->session_key.data) {
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+ *session_key = ntlmssp_state->session_key;
+
+ return NT_STATUS_OK;
+}
+
+/**
* End an NTLMSSP state machine
*
* @param ntlmssp_state NTLMSSP State, free()ed by this function
diff --git a/source4/libcli/auth/spnego.c b/source4/libcli/auth/spnego.c
index ddc98f883b..ae7a3b4042 100644
--- a/source4/libcli/auth/spnego.c
+++ b/source4/libcli/auth/spnego.c
@@ -2,8 +2,9 @@
Unix SMB/CIFS implementation.
RFC2478 Compliant SPNEGO implementation
-
- Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+ 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
@@ -26,318 +27,295 @@
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_AUTH
-static BOOL read_negTokenInit(ASN1_DATA *asn1, struct spnego_negTokenInit *token)
+NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_security)
{
- ZERO_STRUCTP(token);
-
- asn1_start_tag(asn1, ASN1_CONTEXT(0));
- asn1_start_tag(asn1, ASN1_SEQUENCE(0));
-
- while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
- int i;
-
- switch (asn1->data[asn1->ofs]) {
- /* Read mechTypes */
- case ASN1_CONTEXT(0):
- asn1_start_tag(asn1, ASN1_CONTEXT(0));
- asn1_start_tag(asn1, ASN1_SEQUENCE(0));
-
- token->mechTypes = malloc(sizeof(*token->mechTypes));
- for (i = 0; !asn1->has_error &&
- 0 < asn1_tag_remaining(asn1); i++) {
- token->mechTypes =
- realloc(token->mechTypes, (i + 2) *
- sizeof(*token->mechTypes));
- asn1_read_OID(asn1, token->mechTypes + i);
- }
- token->mechTypes[i] = NULL;
-
- asn1_end_tag(asn1);
- asn1_end_tag(asn1);
- break;
- /* Read reqFlags */
- case ASN1_CONTEXT(1):
- asn1_start_tag(asn1, ASN1_CONTEXT(1));
- asn1_read_Integer(asn1, &token->reqFlags);
- token->reqFlags |= SPNEGO_REQ_FLAG;
- asn1_end_tag(asn1);
- break;
- /* Read mechToken */
- case ASN1_CONTEXT(2):
- asn1_start_tag(asn1, ASN1_CONTEXT(2));
- asn1_read_OctetString(asn1, &token->mechToken);
- asn1_end_tag(asn1);
- break;
- /* Read mecListMIC */
- case ASN1_CONTEXT(3):
- asn1_start_tag(asn1, ASN1_CONTEXT(3));
- if (asn1->data[asn1->ofs] == ASN1_OCTET_STRING) {
- asn1_read_OctetString(asn1,
- &token->mechListMIC);
- } else {
- /* RFC 2478 says we have an Octet String here,
- but W2k sends something different... */
- char *mechListMIC;
- asn1_push_tag(asn1, ASN1_SEQUENCE(0));
- asn1_push_tag(asn1, ASN1_CONTEXT(0));
- asn1_read_GeneralString(asn1, &mechListMIC);
- asn1_pop_tag(asn1);
- asn1_pop_tag(asn1);
-
- token->mechListMIC =
- data_blob(mechListMIC, strlen(mechListMIC));
- SAFE_FREE(mechListMIC);
- }
- asn1_end_tag(asn1);
- break;
- default:
- asn1->has_error = True;
- break;
- }
+ struct spnego_state *spnego_state;
+ TALLOC_CTX *mem_ctx = talloc_init("gensec_spengo_client_start");
+ if (!mem_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ spnego_state = talloc_p(mem_ctx, struct spnego_state);
+
+ if (!spnego_state) {
+ return NT_STATUS_NO_MEMORY;
}
- asn1_end_tag(asn1);
- asn1_end_tag(asn1);
+ spnego_state->role = SPNEGO_CLIENT;
+ spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
+ spnego_state->state_position = SPNEGO_CLIENT_GET_MECHS;
+ spnego_state->result = SPNEGO_ACCEPT_INCOMPLETE;
+ spnego_state->mem_ctx = mem_ctx;
- return !asn1->has_error;
+ gensec_security->private_data = spnego_state;
+ return NT_STATUS_OK;
}
-static BOOL write_negTokenInit(ASN1_DATA *asn1, struct spnego_negTokenInit *token)
+/*
+ wrappers for the spnego_*() functions
+*/
+NTSTATUS gensec_spnego_unseal_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ uint8_t *data, size_t length, DATA_BLOB *sig)
{
- asn1_push_tag(asn1, ASN1_CONTEXT(0));
- asn1_push_tag(asn1, ASN1_SEQUENCE(0));
+ struct spnego_state *spnego_state = gensec_security->private_data;
- /* Write mechTypes */
- if (token->mechTypes && *token->mechTypes) {
- int i;
-
- asn1_push_tag(asn1, ASN1_CONTEXT(0));
- asn1_push_tag(asn1, ASN1_SEQUENCE(0));
- for (i = 0; token->mechTypes[i]; i++) {
- asn1_write_OID(asn1, token->mechTypes[i]);
- }
- asn1_pop_tag(asn1);
- asn1_pop_tag(asn1);
+ if (spnego_state->state_position != SPNEGO_DONE
+ && spnego_state->state_position != SPNEGO_FALLBACK) {
+ return NT_STATUS_INVALID_PARAMETER;
}
+
+ return spnego_state->sub_sec_security.ops->unseal(&spnego_state->sub_sec_security,
+ mem_ctx, data, length, sig);
+}
- /* write reqFlags */
- if (token->reqFlags & SPNEGO_REQ_FLAG) {
- int flags = token->reqFlags & ~SPNEGO_REQ_FLAG;
+NTSTATUS gensec_spnego_check_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const uint8_t *data, size_t length,
+ const DATA_BLOB *sig)
+{
+ struct spnego_state *spnego_state = gensec_security->private_data;
- asn1_push_tag(asn1, ASN1_CONTEXT(1));
- asn1_write_Integer(asn1, flags);
- asn1_pop_tag(asn1);
+ return NT_STATUS_NOT_IMPLEMENTED;
+ if (spnego_state->state_position != SPNEGO_DONE
+ && spnego_state->state_position != SPNEGO_FALLBACK) {
+ return NT_STATUS_INVALID_PARAMETER;
}
+
+ return spnego_state->sub_sec_security.ops->check_sig(&spnego_state->sub_sec_security,
+ mem_ctx, data, length, sig);
+}
- /* write mechToken */
- if (token->mechToken.data) {
- asn1_push_tag(asn1, ASN1_CONTEXT(2));
- asn1_write_OctetString(asn1, token->mechToken.data,
- token->mechToken.length);
- asn1_pop_tag(asn1);
- }
+NTSTATUS gensec_spnego_seal_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ uint8_t *data, size_t length,
+ DATA_BLOB *sig)
+{
+ struct spnego_state *spnego_state = gensec_security->private_data;
- /* write mechListMIC */
- if (token->mechListMIC.data) {
- asn1_push_tag(asn1, ASN1_CONTEXT(3));
-#if 0
- /* This is what RFC 2478 says ... */
- asn1_write_OctetString(asn1, token->mechListMIC.data,
- token->mechListMIC.length);
-#else
- /* ... but unfortunately this is what Windows
- sends/expects */
- asn1_push_tag(asn1, ASN1_SEQUENCE(0));
- asn1_push_tag(asn1, ASN1_CONTEXT(0));
- asn1_push_tag(asn1, ASN1_GENERAL_STRING);
- asn1_write(asn1, token->mechListMIC.data,
- token->mechListMIC.length);
- asn1_pop_tag(asn1);
- asn1_pop_tag(asn1);
- asn1_pop_tag(asn1);
-#endif
- asn1_pop_tag(asn1);
+ return NT_STATUS_NOT_IMPLEMENTED;
+ if (spnego_state->state_position != SPNEGO_DONE
+ && spnego_state->state_position != SPNEGO_FALLBACK) {
+ return NT_STATUS_INVALID_PARAMETER;
}
+
+ return spnego_state->sub_sec_security.ops->seal(&spnego_state->sub_sec_security,
+ mem_ctx, data, length, sig);
+}
- asn1_pop_tag(asn1);
- asn1_pop_tag(asn1);
+NTSTATUS gensec_spnego_sign_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const uint8_t *data, size_t length,
+ DATA_BLOB *sig)
+{
+ struct spnego_state *spnego_state = gensec_security->private_data;
- return !asn1->has_error;
+ if (spnego_state->state_position != SPNEGO_DONE
+ && spnego_state->state_position != SPNEGO_FALLBACK) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return spnego_state->sub_sec_security.ops->sign(&spnego_state->sub_sec_security,
+ mem_ctx, data, length, sig);
}
-static BOOL read_negTokenTarg(ASN1_DATA *asn1, struct spnego_negTokenTarg *token)
+NTSTATUS gensec_spnego_session_key(struct gensec_security *gensec_security,
+ DATA_BLOB *session_key)
{
- ZERO_STRUCTP(token);
-
- asn1_start_tag(asn1, ASN1_CONTEXT(1));
- asn1_start_tag(asn1, ASN1_SEQUENCE(0));
-
- while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
- switch (asn1->data[asn1->ofs]) {
- case ASN1_CONTEXT(0):
- asn1_start_tag(asn1, ASN1_CONTEXT(0));
- asn1_start_tag(asn1, ASN1_ENUMERATED);
- asn1_read_uint8(asn1, &token->negResult);
- asn1_end_tag(asn1);
- asn1_end_tag(asn1);
- break;
- case ASN1_CONTEXT(1):
- asn1_start_tag(asn1, ASN1_CONTEXT(1));
- asn1_read_OID(asn1, &token->supportedMech);
- asn1_end_tag(asn1);
- break;
- case ASN1_CONTEXT(2):
- asn1_start_tag(asn1, ASN1_CONTEXT(2));
- asn1_read_OctetString(asn1, &token->responseToken);
- asn1_end_tag(asn1);
- break;
- case ASN1_CONTEXT(3):
- asn1_start_tag(asn1, ASN1_CONTEXT(3));
- asn1_read_OctetString(asn1, &token->mechListMIC);
- asn1_end_tag(asn1);
- break;
- default:
- asn1->has_error = True;
- break;
- }
+ struct spnego_state *spnego_state = gensec_security->private_data;
+ if (spnego_state->state_position != SPNEGO_DONE
+ && spnego_state->state_position != SPNEGO_FALLBACK) {
+ return NT_STATUS_INVALID_PARAMETER;
}
-
- asn1_end_tag(asn1);
- asn1_end_tag(asn1);
-
- return !asn1->has_error;
+
+ return spnego_state->sub_sec_security.ops->session_key(&spnego_state->sub_sec_security,
+ session_key);
}
-static BOOL write_negTokenTarg(ASN1_DATA *asn1, struct spnego_negTokenTarg *token)
+NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx,
+ const DATA_BLOB in, DATA_BLOB *out)
{
- asn1_push_tag(asn1, ASN1_CONTEXT(1));
- asn1_push_tag(asn1, ASN1_SEQUENCE(0));
-
- asn1_push_tag(asn1, ASN1_CONTEXT(0));
- asn1_write_enumerated(asn1, token->negResult);
- asn1_pop_tag(asn1);
+ struct spnego_state *spnego_state = gensec_security->private_data;
+ DATA_BLOB null_data_blob = data_blob(NULL, 0);
+ DATA_BLOB unwrapped_out;
+ struct spnego_data spnego_out;
+ struct spnego_data spnego;
+ const struct gensec_security_ops *op;
- if (token->supportedMech) {
- asn1_push_tag(asn1, ASN1_CONTEXT(1));
- asn1_write_OID(asn1, token->supportedMech);
- asn1_pop_tag(asn1);
- }
+ ssize_t len;
- if (token->responseToken.data) {
- asn1_push_tag(asn1, ASN1_CONTEXT(2));
- asn1_write_OctetString(asn1, token->responseToken.data,
- token->responseToken.length);
- asn1_pop_tag(asn1);
+ if (!out_mem_ctx) {
+ out_mem_ctx = spnego_state->mem_ctx;
}
- if (token->mechListMIC.data) {
- asn1_push_tag(asn1, ASN1_CONTEXT(3));
- asn1_write_OctetString(asn1, token->mechListMIC.data,
- token->mechListMIC.length);
- asn1_pop_tag(asn1);
+ if (spnego_state->state_position == SPNEGO_FALLBACK) {
+ return spnego_state->sub_sec_security.ops->update(&spnego_state->sub_sec_security,
+ out_mem_ctx, in, out);
}
- asn1_pop_tag(asn1);
- asn1_pop_tag(asn1);
-
- return !asn1->has_error;
-}
+ len = read_spnego_data(in, &spnego);
-ssize_t read_spnego_data(DATA_BLOB data, struct spnego_data *token)
-{
- ASN1_DATA asn1;
- ssize_t ret = -1;
-
- ZERO_STRUCTP(token);
- ZERO_STRUCT(asn1);
- asn1_load(&asn1, data);
-
- switch (asn1.data[asn1.ofs]) {
- case ASN1_APPLICATION(0):
- asn1_start_tag(&asn1, ASN1_APPLICATION(0));
- asn1_check_OID(&asn1, OID_SPNEGO);
- if (read_negTokenInit(&asn1, &token->negTokenInit)) {
- token->type = SPNEGO_NEG_TOKEN_INIT;
+ if (len == -1 && spnego_state->state_position == SPNEGO_SERVER_START) {
+ int i;
+ const struct gensec_security_ops **all_ops = gensec_security_all();
+ for (i=0; all_ops[i]; i++) {
+ NTSTATUS nt_status;
+ op = all_ops[i];
+ if (!op->oid) {
+ continue;
+ }
+ nt_status = op->server_start(&spnego_state->sub_sec_security);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ continue;
+ }
+ nt_status = op->update(&spnego_state->sub_sec_security,
+ 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;
+ }
+ op->end(&spnego_state->sub_sec_security);
}
- asn1_end_tag(&asn1);
- break;
- case ASN1_CONTEXT(1):
- if (read_negTokenTarg(&asn1, &token->negTokenTarg)) {
- token->type = SPNEGO_NEG_TOKEN_TARG;
+ DEBUG(1, ("Failed to parse SPENGO request\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ } else {
+
+ if (spnego.type != spnego_state->expected_packet) {
+ free_spnego_data(&spnego);
+ DEBUG(1, ("Invalid SPENGO request: %d, expected %d\n", spnego.type,
+ spnego_state->expected_packet));
+ return NT_STATUS_INVALID_PARAMETER;
}
- break;
- default:
- break;
- }
- if (!asn1.has_error) ret = asn1.ofs;
- asn1_free(&asn1);
-
- return ret;
-}
+ if (spnego_state->state_position == SPNEGO_CLIENT_GET_MECHS) {
-ssize_t write_spnego_data(DATA_BLOB *blob, struct spnego_data *spnego)
-{
- ASN1_DATA asn1;
- ssize_t ret = -1;
-
- ZERO_STRUCT(asn1);
-
- switch (spnego->type) {
- case SPNEGO_NEG_TOKEN_INIT:
- asn1_push_tag(&asn1, ASN1_APPLICATION(0));
- asn1_write_OID(&asn1, OID_SPNEGO);
- write_negTokenInit(&asn1, &spnego->negTokenInit);
- asn1_pop_tag(&asn1);
- break;
- case SPNEGO_NEG_TOKEN_TARG:
- write_negTokenTarg(&asn1, &spnego->negTokenTarg);
- break;
- default:
- asn1.has_error = True;
- break;
- }
+ /* The server offers a list of mechanisms */
+
+ char **mechType = spnego.negTokenInit.mechTypes;
+ char *my_mechs[] = {NULL, NULL};
+ int i;
+ NTSTATUS nt_status;
+
+ for (i=0; mechType[i]; i++) {
+ op = gensec_security_by_oid(mechType[i]);
+ if (!op) {
+ continue;
+ }
+ spnego_state->sub_sec_security.ops = op;
+ spnego_state->sub_sec_security.user = gensec_security->user;
+
+ nt_status = op->client_start(&spnego_state->sub_sec_security);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ op->end(&spnego_state->sub_sec_security);
+ continue;
+ }
+ if (i == 0) {
+ nt_status = op->update(&spnego_state->sub_sec_security,
+ out_mem_ctx,
+ spnego.negTokenInit.mechToken,
+ &unwrapped_out);
+ } else {
+ /* only get the helping start blob for the first OID */
+ nt_status = op->update(&spnego_state->sub_sec_security,
+ out_mem_ctx,
+ null_data_blob,
+ &unwrapped_out);
+ }
+ if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ DEBUG(1, ("SPENGO(%s) NEG_TOKEN_INIT failed: %s\n", op->name, nt_errstr(nt_status)));
+ op->end(&spnego_state->sub_sec_security);
+ } else {
+ break;
+ }
+ }
+ if (!mechType[i]) {
+ DEBUG(1, ("SPENGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
+ }
- if (!asn1.has_error) {
- *blob = data_blob(asn1.data, asn1.length);
- ret = asn1.ofs;
- }
- asn1_free(&asn1);
+ free_spnego_data(&spnego);
+ if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ return nt_status;
+ }
+
+ /* compose reply */
+ my_mechs[0] = op->oid;
+
+ spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
+ spnego_out.negTokenInit.mechTypes = my_mechs;
+ spnego_out.negTokenInit.reqFlags = 0;
+ spnego_out.negTokenInit.mechListMIC = null_data_blob;
+ spnego_out.negTokenInit.mechToken = unwrapped_out;
+
+ if (write_spnego_data(out_mem_ctx, out, &spnego_out) == -1) {
+ DEBUG(1, ("Failed to write SPENGO reply to NEG_TOKEN_INIT\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
- return ret;
-}
+ /* set next state */
+ spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
+ spnego_state->state_position = SPNEGO_TARG;
-BOOL free_spnego_data(struct spnego_data *spnego)
-{
- BOOL ret = True;
+ return nt_status;
+ } else if (spnego_state->state_position == SPNEGO_TARG) {
+ NTSTATUS nt_status;
+ if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
- if (!spnego) goto out;
+ op = spnego_state->sub_sec_security.ops;
+ if (spnego.negTokenTarg.responseToken.length) {
+ nt_status = op->update(&spnego_state->sub_sec_security,
+ out_mem_ctx,
+ spnego.negTokenTarg.responseToken,
+ &unwrapped_out);
+ } else {
+ unwrapped_out = data_blob(NULL, 0);
+ nt_status = NT_STATUS_OK;
+ }
+
+ if (NT_STATUS_IS_OK(nt_status)
+ && (spnego.negTokenTarg.negResult != SPNEGO_ACCEPT_COMPLETED)) {
+ nt_status = NT_STATUS_INVALID_PARAMETER;
+ }
- switch(spnego->type) {
- case SPNEGO_NEG_TOKEN_INIT:
- if (spnego->negTokenInit.mechTypes) {
- int i;
- for (i = 0; spnego->negTokenInit.mechTypes[i]; i++) {
- free(spnego->negTokenInit.mechTypes[i]);
+ spnego_state->result = spnego.negTokenTarg.negResult;
+ free_spnego_data(&spnego);
+
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ /* compose reply */
+ spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
+ spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
+ spnego_out.negTokenTarg.supportedMech = op->oid;
+ spnego_out.negTokenTarg.responseToken = unwrapped_out;
+ spnego_out.negTokenTarg.mechListMIC = null_data_blob;
+
+ if (write_spnego_data(out_mem_ctx, out, &spnego_out) == -1) {
+ DEBUG(1, ("Failed to write SPENGO reply to NEG_TOKEN_TARG\n"));
+ return NT_STATUS_INVALID_PARAMETER;
}
- free(spnego->negTokenInit.mechTypes);
- }
- data_blob_free(&spnego->negTokenInit.mechToken);
- data_blob_free(&spnego->negTokenInit.mechListMIC);
- break;
- case SPNEGO_NEG_TOKEN_TARG:
- if (spnego->negTokenTarg.supportedMech) {
- free(spnego->negTokenTarg.supportedMech);
+ spnego_state->state_position = SPNEGO_TARG;
+ } else if (NT_STATUS_IS_OK(nt_status)) {
+ spnego_state->state_position = SPNEGO_DONE;
+ } else {
+ DEBUG(1, ("SPENGO(%s) login failed: %s\n", op->name, nt_errstr(nt_status)));
+ return nt_status;
+ }
+
+ return nt_status;
+ } else {
+ free_spnego_data(&spnego);
+ DEBUG(1, ("Invalid SPENGO request: %d\n", spnego.type));
+ return NT_STATUS_INVALID_PARAMETER;
}
- data_blob_free(&spnego->negTokenTarg.responseToken);
- data_blob_free(&spnego->negTokenTarg.mechListMIC);
- break;
- default:
- ret = False;
- break;
}
- ZERO_STRUCTP(spnego);
-out:
- return ret;
}
+void gensec_spnego_end(struct gensec_security *gensec_security)
+{
+ struct spnego_state *spnego_state = gensec_security->private_data;
+
+ spnego_state->sub_sec_security.ops->end(&spnego_state->sub_sec_security);
+
+ talloc_destroy(spnego_state->mem_ctx);
+
+ gensec_security->private_data = NULL;
+}
diff --git a/source4/libcli/auth/spnego.h b/source4/libcli/auth/spnego.h
index e30fa13d26..890301314b 100644
--- a/source4/libcli/auth/spnego.h
+++ b/source4/libcli/auth/spnego.h
@@ -23,6 +23,12 @@
#ifndef SAMBA_SPNEGO_H
#define SAMBA_SPNEGO_H
+/* SPNEGO mode */
+enum spnego_role
+{
+ SPNEGO_SERVER,
+ SPNEGO_CLIENT
+};
#define SPNEGO_DELEG_FLAG 0x01
#define SPNEGO_MUTUAL_FLAG 0x02
@@ -33,9 +39,6 @@
#define SPNEGO_INTEG_FLAG 0x40
#define SPNEGO_REQ_FLAG 0x80
-#define SPNEGO_NEG_TOKEN_INIT 0
-#define SPNEGO_NEG_TOKEN_TARG 1
-
typedef enum _spnego_negResult {
SPNEGO_ACCEPT_COMPLETED = 0,
SPNEGO_ACCEPT_INCOMPLETE = 1,
@@ -62,4 +65,28 @@ struct spnego_data {
struct spnego_negTokenTarg negTokenTarg;
};
+enum spnego_message_type {
+ SPNEGO_NEG_TOKEN_INIT = 0,
+ SPNEGO_NEG_TOKEN_TARG = 1,
+};
+
+enum spnego_state_position {
+ SPNEGO_SERVER_START,
+ SPNEGO_CLIENT_GET_MECHS,
+ SPNEGO_CLIENT_SEND_MECHS,
+ SPNEGO_TARG,
+ SPNEGO_FALLBACK,
+ SPNEGO_DONE
+};
+
+struct spnego_state {
+ TALLOC_CTX *mem_ctx;
+ uint_t ref_count;
+ enum spnego_role role;
+ enum spnego_message_type expected_packet;
+ enum spnego_message_type state_position;
+ negResult_t result;
+ struct gensec_security sub_sec_security;
+};
+
#endif
diff --git a/source4/libcli/auth/spnego_parse.c b/source4/libcli/auth/spnego_parse.c
new file mode 100644
index 0000000000..6425e0c167
--- /dev/null
+++ b/source4/libcli/auth/spnego_parse.c
@@ -0,0 +1,343 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ RFC2478 Compliant SPNEGO implementation
+
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+
+ 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+static BOOL read_negTokenInit(ASN1_DATA *asn1, struct spnego_negTokenInit *token)
+{
+ ZERO_STRUCTP(token);
+
+ asn1_start_tag(asn1, ASN1_CONTEXT(0));
+ asn1_start_tag(asn1, ASN1_SEQUENCE(0));
+
+ while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
+ int i;
+
+ switch (asn1->data[asn1->ofs]) {
+ /* Read mechTypes */
+ case ASN1_CONTEXT(0):
+ asn1_start_tag(asn1, ASN1_CONTEXT(0));
+ asn1_start_tag(asn1, ASN1_SEQUENCE(0));
+
+ token->mechTypes = malloc(sizeof(*token->mechTypes));
+ for (i = 0; !asn1->has_error &&
+ 0 < asn1_tag_remaining(asn1); i++) {
+ token->mechTypes =
+ realloc(token->mechTypes, (i + 2) *
+ sizeof(*token->mechTypes));
+ asn1_read_OID(asn1, token->mechTypes + i);
+ }
+ token->mechTypes[i] = NULL;
+
+ asn1_end_tag(asn1);
+ asn1_end_tag(asn1);
+ break;
+ /* Read reqFlags */
+ case ASN1_CONTEXT(1):
+ asn1_start_tag(asn1, ASN1_CONTEXT(1));
+ asn1_read_Integer(asn1, &token->reqFlags);
+ token->reqFlags |= SPNEGO_REQ_FLAG;
+ asn1_end_tag(asn1);
+ break;
+ /* Read mechToken */
+ case ASN1_CONTEXT(2):
+ asn1_start_tag(asn1, ASN1_CONTEXT(2));
+ asn1_read_OctetString(asn1, &token->mechToken);
+ asn1_end_tag(asn1);
+ break;
+ /* Read mecListMIC */
+ case ASN1_CONTEXT(3):
+ asn1_start_tag(asn1, ASN1_CONTEXT(3));
+ if (asn1->data[asn1->ofs] == ASN1_OCTET_STRING) {
+ asn1_read_OctetString(asn1,
+ &token->mechListMIC);
+ } else {
+ /* RFC 2478 says we have an Octet String here,
+ but W2k sends something different... */
+ char *mechListMIC;
+ asn1_push_tag(asn1, ASN1_SEQUENCE(0));
+ asn1_push_tag(asn1, ASN1_CONTEXT(0));
+ asn1_read_GeneralString(asn1, &mechListMIC);
+ asn1_pop_tag(asn1);
+ asn1_pop_tag(asn1);
+
+ token->mechListMIC =
+ data_blob(mechListMIC, strlen(mechListMIC));
+ SAFE_FREE(mechListMIC);
+ }
+ asn1_end_tag(asn1);
+ break;
+ default:
+ asn1->has_error = True;
+ break;
+ }
+ }
+
+ asn1_end_tag(asn1);
+ asn1_end_tag(asn1);
+
+ return !asn1->has_error;
+}
+
+static BOOL write_negTokenInit(ASN1_DATA *asn1, struct spnego_negTokenInit *token)
+{
+ asn1_push_tag(asn1, ASN1_CONTEXT(0));
+ asn1_push_tag(asn1, ASN1_SEQUENCE(0));
+
+ /* Write mechTypes */
+ if (token->mechTypes && *token->mechTypes) {
+ int i;
+
+ asn1_push_tag(asn1, ASN1_CONTEXT(0));
+ asn1_push_tag(asn1, ASN1_SEQUENCE(0));
+ for (i = 0; token->mechTypes[i]; i++) {
+ asn1_write_OID(asn1, token->mechTypes[i]);
+ }
+ asn1_pop_tag(asn1);
+ asn1_pop_tag(asn1);
+ }
+
+ /* write reqFlags */
+ if (token->reqFlags & SPNEGO_REQ_FLAG) {
+ int flags = token->reqFlags & ~SPNEGO_REQ_FLAG;
+
+ asn1_push_tag(asn1, ASN1_CONTEXT(1));
+ asn1_write_Integer(asn1, flags);
+ asn1_pop_tag(asn1);
+ }
+
+ /* write mechToken */
+ if (token->mechToken.data) {
+ asn1_push_tag(asn1, ASN1_CONTEXT(2));
+ asn1_write_OctetString(asn1, token->mechToken.data,
+ token->mechToken.length);
+ asn1_pop_tag(asn1);
+ }
+
+ /* write mechListMIC */
+ if (token->mechListMIC.data) {
+ asn1_push_tag(asn1, ASN1_CONTEXT(3));
+#if 0
+ /* This is what RFC 2478 says ... */
+ asn1_write_OctetString(asn1, token->mechListMIC.data,
+ token->mechListMIC.length);
+#else
+ /* ... but unfortunately this is what Windows
+ sends/expects */
+ asn1_push_tag(asn1, ASN1_SEQUENCE(0));
+ asn1_push_tag(asn1, ASN1_CONTEXT(0));
+ asn1_push_tag(asn1, ASN1_GENERAL_STRING);
+ asn1_write(asn1, token->mechListMIC.data,
+ token->mechListMIC.length);
+ asn1_pop_tag(asn1);
+ asn1_pop_tag(asn1);
+ asn1_pop_tag(asn1);
+#endif
+ asn1_pop_tag(asn1);
+ }
+
+ asn1_pop_tag(asn1);
+ asn1_pop_tag(asn1);
+
+ return !asn1->has_error;
+}
+
+static BOOL read_negTokenTarg(ASN1_DATA *asn1, struct spnego_negTokenTarg *token)
+{
+ ZERO_STRUCTP(token);
+
+ asn1_start_tag(asn1, ASN1_CONTEXT(1));
+ asn1_start_tag(asn1, ASN1_SEQUENCE(0));
+
+ while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
+ switch (asn1->data[asn1->ofs]) {
+ case ASN1_CONTEXT(0):
+ asn1_start_tag(asn1, ASN1_CONTEXT(0));
+ asn1_start_tag(asn1, ASN1_ENUMERATED);
+ asn1_read_uint8(asn1, &token->negResult);
+ asn1_end_tag(asn1);
+ asn1_end_tag(asn1);
+ break;
+ case ASN1_CONTEXT(1):
+ asn1_start_tag(asn1, ASN1_CONTEXT(1));
+ asn1_read_OID(asn1, &token->supportedMech);
+ asn1_end_tag(asn1);
+ break;
+ case ASN1_CONTEXT(2):
+ asn1_start_tag(asn1, ASN1_CONTEXT(2));
+ asn1_read_OctetString(asn1, &token->responseToken);
+ asn1_end_tag(asn1);
+ break;
+ case ASN1_CONTEXT(3):
+ asn1_start_tag(asn1, ASN1_CONTEXT(3));
+ asn1_read_OctetString(asn1, &token->mechListMIC);
+ asn1_end_tag(asn1);
+ break;
+ default:
+ asn1->has_error = True;
+ break;
+ }
+ }
+
+ asn1_end_tag(asn1);
+ asn1_end_tag(asn1);
+
+ return !asn1->has_error;
+}
+
+static BOOL write_negTokenTarg(ASN1_DATA *asn1, struct spnego_negTokenTarg *token)
+{
+ asn1_push_tag(asn1, ASN1_CONTEXT(1));
+ asn1_push_tag(asn1, ASN1_SEQUENCE(0));
+
+ asn1_push_tag(asn1, ASN1_CONTEXT(0));
+ asn1_write_enumerated(asn1, token->negResult);
+ asn1_pop_tag(asn1);
+
+ if (token->supportedMech) {
+ asn1_push_tag(asn1, ASN1_CONTEXT(1));
+ asn1_write_OID(asn1, token->supportedMech);
+ asn1_pop_tag(asn1);
+ }
+
+ if (token->responseToken.data) {
+ asn1_push_tag(asn1, ASN1_CONTEXT(2));
+ asn1_write_OctetString(asn1, token->responseToken.data,
+ token->responseToken.length);
+ asn1_pop_tag(asn1);
+ }
+
+ if (token->mechListMIC.data) {
+ asn1_push_tag(asn1, ASN1_CONTEXT(3));
+ asn1_write_OctetString(asn1, token->mechListMIC.data,
+ token->mechListMIC.length);
+ asn1_pop_tag(asn1);
+ }
+
+ asn1_pop_tag(asn1);
+ asn1_pop_tag(asn1);
+
+ return !asn1->has_error;
+}
+
+ssize_t read_spnego_data(DATA_BLOB data, struct spnego_data *token)
+{
+ ASN1_DATA asn1;
+ ssize_t ret = -1;
+
+ ZERO_STRUCTP(token);
+ ZERO_STRUCT(asn1);
+ asn1_load(&asn1, data);
+
+ switch (asn1.data[asn1.ofs]) {
+ case ASN1_APPLICATION(0):
+ asn1_start_tag(&asn1, ASN1_APPLICATION(0));
+ asn1_check_OID(&asn1, OID_SPNEGO);
+ if (read_negTokenInit(&asn1, &token->negTokenInit)) {
+ token->type = SPNEGO_NEG_TOKEN_INIT;
+ }
+ asn1_end_tag(&asn1);
+ break;
+ case ASN1_CONTEXT(1):
+ if (read_negTokenTarg(&asn1, &token->negTokenTarg)) {
+ token->type = SPNEGO_NEG_TOKEN_TARG;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (!asn1.has_error) ret = asn1.ofs;
+ asn1_free(&asn1);
+
+ return ret;
+}
+
+ssize_t write_spnego_data(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, struct spnego_data *spnego)
+{
+ ASN1_DATA asn1;
+ ssize_t ret = -1;
+
+ ZERO_STRUCT(asn1);
+
+ switch (spnego->type) {
+ case SPNEGO_NEG_TOKEN_INIT:
+ asn1_push_tag(&asn1, ASN1_APPLICATION(0));
+ asn1_write_OID(&asn1, OID_SPNEGO);
+ write_negTokenInit(&asn1, &spnego->negTokenInit);
+ asn1_pop_tag(&asn1);
+ break;
+ case SPNEGO_NEG_TOKEN_TARG:
+ write_negTokenTarg(&asn1, &spnego->negTokenTarg);
+ break;
+ default:
+ asn1.has_error = True;
+ break;
+ }
+
+ if (!asn1.has_error) {
+ *blob = data_blob_talloc(mem_ctx, asn1.data, asn1.length);
+ ret = asn1.ofs;
+ }
+ asn1_free(&asn1);
+
+ return ret;
+}
+
+BOOL free_spnego_data(struct spnego_data *spnego)
+{
+ BOOL ret = True;
+
+ if (!spnego) goto out;
+
+ switch(spnego->type) {
+ case SPNEGO_NEG_TOKEN_INIT:
+ if (spnego->negTokenInit.mechTypes) {
+ int i;
+ for (i = 0; spnego->negTokenInit.mechTypes[i]; i++) {
+ free(spnego->negTokenInit.mechTypes[i]);
+ }
+ free(spnego->negTokenInit.mechTypes);
+ }
+ data_blob_free(&spnego->negTokenInit.mechToken);
+ data_blob_free(&spnego->negTokenInit.mechListMIC);
+ break;
+ case SPNEGO_NEG_TOKEN_TARG:
+ if (spnego->negTokenTarg.supportedMech) {
+ free(spnego->negTokenTarg.supportedMech);
+ }
+ data_blob_free(&spnego->negTokenTarg.responseToken);
+ data_blob_free(&spnego->negTokenTarg.mechListMIC);
+ break;
+ default:
+ ret = False;
+ break;
+ }
+ ZERO_STRUCTP(spnego);
+out:
+ return ret;
+}
+
diff --git a/source4/libcli/config.m4 b/source4/libcli/config.m4
index 49c690c944..992f84005d 100644
--- a/source4/libcli/config.m4
+++ b/source4/libcli/config.m4
@@ -42,6 +42,7 @@ SMB_SUBSYSTEM(LIBCLI_UTILS,[],
SMB_SUBSYSTEM(LIBCLI_AUTH,[],
[libcli/auth/spnego.o
+ libcli/auth/spnego_parse.o
libcli/auth/ntlmssp.o
libcli/auth/ntlmssp_parse.o
libcli/auth/ntlmssp_sign.o
@@ -51,7 +52,9 @@ SMB_SUBSYSTEM(LIBCLI_AUTH,[],
libcli/auth/ntlm_check.o
libcli/auth/kerberos.o
libcli/auth/kerberos_verify.o
- libcli/auth/clikrb5.o])
+ libcli/auth/clikrb5.o
+ libcli/auth/gensec.o
+ libcli/auth/gensec_ntlmssp.o])
SMB_SUBSYSTEM(LIBCLI_NMB,[],
[libcli/unexpected.o
diff --git a/source4/libcli/util/asn1.c b/source4/libcli/util/asn1.c
index 05bc5eace8..943ce4d1c1 100644
--- a/source4/libcli/util/asn1.c
+++ b/source4/libcli/util/asn1.c
@@ -329,7 +329,7 @@ BOOL asn1_read_OID(ASN1_DATA *data, char **OID)
if (!asn1_start_tag(data, ASN1_OID)) return False;
asn1_read_uint8(data, &b);
- oid = talloc_asprintf_append(mem_ctx, oid, "%u", b/40);
+ oid = talloc_asprintf(mem_ctx, "%u", b/40);
oid = talloc_asprintf_append(mem_ctx, oid, " %u", b%40);
while (asn1_tag_remaining(data) > 0) {
diff --git a/source4/librpc/rpc/dcerpc.c b/source4/librpc/rpc/dcerpc.c
index 82b6fa9a4e..5d5469da7f 100644
--- a/source4/librpc/rpc/dcerpc.c
+++ b/source4/librpc/rpc/dcerpc.c
@@ -42,9 +42,9 @@ struct dcerpc_pipe *dcerpc_pipe_init(void)
p->mem_ctx = mem_ctx;
p->call_id = 1;
p->security_state.auth_info = NULL;
- ZERO_STRUCT(p->security_state.user);
- p->security_state.private_data = NULL;
- p->security_state.ops = NULL;
+ ZERO_STRUCT(p->security_state.generic_state.user);
+ p->security_state.generic_state.private_data = NULL;
+ p->security_state.generic_state.ops = NULL;
p->binding_string = NULL;
p->flags = 0;
p->srv_max_xmit_frag = 0;
@@ -60,8 +60,8 @@ void dcerpc_pipe_close(struct dcerpc_pipe *p)
if (!p) return;
p->reference_count--;
if (p->reference_count <= 0) {
- if (p->security_state.ops) {
- p->security_state.ops->end(&p->security_state);
+ if (p->security_state.generic_state.ops) {
+ p->security_state.generic_state.ops->end(&p->security_state.generic_state);
}
p->transport.shutdown_pipe(p);
talloc_destroy(p->mem_ctx);
@@ -132,7 +132,7 @@ static NTSTATUS dcerpc_pull_request_sign(struct dcerpc_pipe *p,
DATA_BLOB auth_blob;
/* non-signed packets are simpler */
- if (!p->security_state.auth_info || !p->security_state.ops) {
+ if (!p->security_state.auth_info || !p->security_state.generic_state.ops) {
return dcerpc_pull(blob, mem_ctx, pkt);
}
@@ -186,19 +186,21 @@ static NTSTATUS dcerpc_pull_request_sign(struct dcerpc_pipe *p,
/* check signature or unseal the packet */
switch (p->security_state.auth_info->auth_level) {
case DCERPC_AUTH_LEVEL_PRIVACY:
- status = p->security_state.ops->unseal(&p->security_state,
- mem_ctx,
- pkt->u.response.stub_and_verifier.data,
- pkt->u.response.stub_and_verifier.length,
- &auth.credentials);
+ status = p->security_state
+ .generic_state.ops->unseal(&p->security_state.generic_state,
+ mem_ctx,
+ pkt->u.response.stub_and_verifier.data,
+ pkt->u.response.stub_and_verifier.length,
+ &auth.credentials);
break;
case DCERPC_AUTH_LEVEL_INTEGRITY:
- status = p->security_state.ops->check_sig(&p->security_state,
- mem_ctx,
- pkt->u.response.stub_and_verifier.data,
- pkt->u.response.stub_and_verifier.length,
- &auth.credentials);
+ status = p->security_state
+ .generic_state.ops->check_sig(&p->security_state.generic_state,
+ mem_ctx,
+ pkt->u.response.stub_and_verifier.data,
+ pkt->u.response.stub_and_verifier.length,
+ &auth.credentials);
break;
case DCERPC_AUTH_LEVEL_NONE:
@@ -230,7 +232,7 @@ static NTSTATUS dcerpc_push_request_sign(struct dcerpc_pipe *p,
struct ndr_push *ndr;
/* non-signed packets are simpler */
- if (!p->security_state.auth_info || !p->security_state.ops) {
+ if (!p->security_state.auth_info || !p->security_state.generic_state.ops) {
return dcerpc_push_auth(blob, mem_ctx, pkt, p->security_state.auth_info);
}
@@ -255,19 +257,21 @@ static NTSTATUS dcerpc_push_request_sign(struct dcerpc_pipe *p,
/* sign or seal the packet */
switch (p->security_state.auth_info->auth_level) {
case DCERPC_AUTH_LEVEL_PRIVACY:
- status = p->security_state.ops->seal(&p->security_state,
- mem_ctx,
- ndr->data + DCERPC_REQUEST_LENGTH,
- ndr->offset - DCERPC_REQUEST_LENGTH,
- &p->security_state.auth_info->credentials);
+ status = p->security_state
+ .generic_state.ops->seal(&p->security_state.generic_state,
+ mem_ctx,
+ ndr->data + DCERPC_REQUEST_LENGTH,
+ ndr->offset - DCERPC_REQUEST_LENGTH,
+ &p->security_state.auth_info->credentials);
break;
case DCERPC_AUTH_LEVEL_INTEGRITY:
- status = p->security_state.ops->sign(&p->security_state,
- mem_ctx,
- ndr->data + DCERPC_REQUEST_LENGTH,
- ndr->offset - DCERPC_REQUEST_LENGTH,
- &p->security_state.auth_info->credentials);
+ status = p->security_state
+ .generic_state.ops->sign(&p->security_state.generic_state,
+ mem_ctx,
+ ndr->data + DCERPC_REQUEST_LENGTH,
+ ndr->offset - DCERPC_REQUEST_LENGTH,
+ &p->security_state.auth_info->credentials);
break;
case DCERPC_AUTH_LEVEL_NONE:
diff --git a/source4/librpc/rpc/dcerpc.h b/source4/librpc/rpc/dcerpc.h
index 7694a9c230..a513b72a16 100644
--- a/source4/librpc/rpc/dcerpc.h
+++ b/source4/librpc/rpc/dcerpc.h
@@ -25,38 +25,10 @@ enum dcerpc_transport_t {NCACN_NP, NCACN_IP_TCP};
/*
this defines a generic security context for signed/sealed dcerpc pipes.
*/
-struct dcerpc_security;
struct dcerpc_pipe;
-
-struct dcerpc_user {
- const char *domain;
- const char *name;
- const char *password;
-};
-
-struct dcesrv_security_ops {
- const char *name;
- uint8 auth_type;
- NTSTATUS (*start)(struct dcerpc_pipe *dce_pipe, struct dcerpc_security *dce_sec);
- NTSTATUS (*update)(struct dcerpc_security *dce_sec, TALLOC_CTX *out_mem_ctx,
- const DATA_BLOB in, DATA_BLOB *out);
- NTSTATUS (*seal)(struct dcerpc_security *dce_sec, TALLOC_CTX *sig_mem_ctx,
- uint8_t *data, size_t length, DATA_BLOB *sig);
- NTSTATUS (*sign)(struct dcerpc_security *dce_sec, TALLOC_CTX *sig_mem_ctx,
- const uint8_t *data, size_t length, DATA_BLOB *sig);
- NTSTATUS (*check_sig)(struct dcerpc_security *dce_sec, TALLOC_CTX *sig_mem_ctx,
- const uint8_t *data, size_t length, const DATA_BLOB *sig);
- NTSTATUS (*unseal)(struct dcerpc_security *dce_sec, TALLOC_CTX *sig_mem_ctx,
- uint8_t *data, size_t length, DATA_BLOB *sig);
- NTSTATUS (*session_key)(struct dcerpc_security *, DATA_BLOB *session_key);
- void (*end)(struct dcerpc_security *dce_sec);
-};
-
struct dcerpc_security {
struct dcerpc_auth *auth_info;
- struct dcerpc_user user;
- void *private_data;
- const struct dcesrv_security_ops *ops;
+ struct gensec_security generic_state;
};
struct dcerpc_pipe {
diff --git a/source4/librpc/rpc/dcerpc_auth.c b/source4/librpc/rpc/dcerpc_auth.c
index 021249847a..e5fad1f082 100644
--- a/source4/librpc/rpc/dcerpc_auth.c
+++ b/source4/librpc/rpc/dcerpc_auth.c
@@ -1,9 +1,11 @@
/*
Unix SMB/CIFS implementation.
- dcerpc authentication operations
+ Generic Authentication Interface
Copyright (C) Andrew Tridgell 2003
+ 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
@@ -42,24 +44,8 @@ NTSTATUS dcerpc_bind_auth_none(struct dcerpc_pipe *p,
return status;
}
-const struct dcesrv_security_ops *dcerpc_security_by_authtype(uint8_t auth_type)
-{
- switch (auth_type) {
- case DCERPC_AUTH_TYPE_SCHANNEL:
- return dcerpc_schannel_security_get_ops();
-
- case DCERPC_AUTH_TYPE_NTLMSSP:
- return dcerpc_ntlmssp_security_get_ops();
- }
-
- return NULL;
-}
-
NTSTATUS dcerpc_bind_auth(struct dcerpc_pipe *p, uint8_t auth_type,
- const char *uuid, uint_t version,
- const char *domain,
- const char *username,
- const char *password)
+ const char *uuid, uint_t version)
{
NTSTATUS status;
TALLOC_CTX *mem_ctx;
@@ -69,20 +55,19 @@ NTSTATUS dcerpc_bind_auth(struct dcerpc_pipe *p, uint8_t auth_type,
if (!mem_ctx) {
return NT_STATUS_NO_MEMORY;
}
-
- p->security_state.ops = dcerpc_security_by_authtype(auth_type);
- if (!p->security_state.ops) {
- status = NT_STATUS_INVALID_PARAMETER;
- goto done;
- }
-
- p->security_state.user.domain = domain;
- p->security_state.user.name = username;
- p->security_state.user.password = password;
-
- status = p->security_state.ops->start(p, &p->security_state);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
+
+ if (!p->security_state.generic_state.ops) {
+
+ p->security_state.generic_state.ops = gensec_security_by_authtype(auth_type);
+ if (!p->security_state.generic_state.ops) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ status = p->security_state.generic_state.ops->client_start(&p->security_state.generic_state);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
}
p->security_state.auth_info = talloc(p->mem_ctx, sizeof(*p->security_state.auth_info));
@@ -105,9 +90,9 @@ NTSTATUS dcerpc_bind_auth(struct dcerpc_pipe *p, uint8_t auth_type,
p->security_state.auth_info->auth_level = DCERPC_AUTH_LEVEL_NONE;
}
- status = p->security_state.ops->update(&p->security_state, mem_ctx,
- p->security_state.auth_info->credentials,
- &credentials);
+ status = p->security_state.generic_state.ops->update(&p->security_state.generic_state, mem_ctx,
+ p->security_state.auth_info->credentials,
+ &credentials);
if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
goto done;
@@ -120,9 +105,9 @@ NTSTATUS dcerpc_bind_auth(struct dcerpc_pipe *p, uint8_t auth_type,
goto done;
}
- status = p->security_state.ops->update(&p->security_state, mem_ctx,
- p->security_state.auth_info->credentials,
- &credentials);
+ status = p->security_state.generic_state.ops->update(&p->security_state.generic_state, mem_ctx,
+ p->security_state.auth_info->credentials,
+ &credentials);
if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
goto done;
@@ -140,3 +125,5 @@ done:
return status;
}
+
+
diff --git a/source4/librpc/rpc/dcerpc_ntlm.c b/source4/librpc/rpc/dcerpc_ntlm.c
index 2cfecd939f..398e3f1aa3 100644
--- a/source4/librpc/rpc/dcerpc_ntlm.c
+++ b/source4/librpc/rpc/dcerpc_ntlm.c
@@ -23,127 +23,7 @@
#include "includes.h"
/*
- wrappers for the ntlmssp_*() functions
-*/
-static NTSTATUS dcerpc_ntlmssp_unseal(struct dcerpc_security *dcerpc_security,
- TALLOC_CTX *mem_ctx,
- uint8_t *data, size_t length, DATA_BLOB *sig)
-{
- struct ntlmssp_state *ntlmssp_state = dcerpc_security->private_data;
-
- return ntlmssp_unseal_packet(ntlmssp_state, mem_ctx, data, length, sig);
-}
-
-static NTSTATUS dcerpc_ntlmssp_check_sig(struct dcerpc_security *dcerpc_security,
- TALLOC_CTX *mem_ctx,
- const uint8_t *data, size_t length,
- const DATA_BLOB *sig)
-{
- struct ntlmssp_state *ntlmssp_state = dcerpc_security->private_data;
-
- return ntlmssp_check_packet(ntlmssp_state, mem_ctx, data, length, sig);
-}
-
-static NTSTATUS dcerpc_ntlmssp_seal(struct dcerpc_security *dcerpc_security,
- TALLOC_CTX *mem_ctx,
- uint8_t *data, size_t length,
- DATA_BLOB *sig)
-{
- struct ntlmssp_state *ntlmssp_state = dcerpc_security->private_data;
-
- return ntlmssp_seal_packet(ntlmssp_state, mem_ctx, data, length, sig);
-}
-
-static NTSTATUS dcerpc_ntlmssp_sign(struct dcerpc_security *dcerpc_security,
- TALLOC_CTX *mem_ctx,
- const uint8_t *data, size_t length,
- DATA_BLOB *sig)
-{
- struct ntlmssp_state *ntlmssp_state = dcerpc_security->private_data;
-
- return ntlmssp_sign_packet(ntlmssp_state, mem_ctx, data, length, sig);
-}
-
-static NTSTATUS dcerpc_ntlmssp_session_key(struct dcerpc_security *dcerpc_security,
- DATA_BLOB *session_key)
-{
- struct ntlmssp_state *ntlmssp_state = dcerpc_security->private_data;
-
- if (!ntlmssp_state->session_key.data) {
- return NT_STATUS_NO_USER_SESSION_KEY;
- }
- *session_key = ntlmssp_state->session_key;
-
- return NT_STATUS_OK;
-}
-
-static NTSTATUS dcerpc_ntlmssp_start(struct dcerpc_pipe *dce_pipe, struct dcerpc_security *dcerpc_security)
-{
- struct ntlmssp_state *ntlmssp_state = NULL;
- NTSTATUS status;
-
- status = ntlmssp_client_start(&ntlmssp_state);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
-
- status = ntlmssp_set_domain(ntlmssp_state, dcerpc_security->user.domain);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
-
- status = ntlmssp_set_username(ntlmssp_state, dcerpc_security->user.name);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
-
- status = ntlmssp_set_password(ntlmssp_state, dcerpc_security->user.password);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
-
- dcerpc_security->private_data = ntlmssp_state;
-
- return status;
-}
-
-static NTSTATUS dcerpc_ntlmssp_update(struct dcerpc_security *dcerpc_security, TALLOC_CTX *out_mem_ctx,
- const DATA_BLOB in, DATA_BLOB *out)
-{
- struct ntlmssp_state *ntlmssp_state = dcerpc_security->private_data;
-
- return ntlmssp_update(ntlmssp_state, out_mem_ctx, in, out);
-}
-
-static void dcerpc_ntlmssp_end(struct dcerpc_security *dcerpc_security)
-{
- struct ntlmssp_state *ntlmssp_state = dcerpc_security->private_data;
-
- ntlmssp_end(&ntlmssp_state);
-
- dcerpc_security->private_data = NULL;
-}
-
-static const struct dcesrv_security_ops dcerpc_ntlmssp_security_ops = {
- .name = "ntlmssp",
- .auth_type = DCERPC_AUTH_TYPE_NTLMSSP,
- .start = dcerpc_ntlmssp_start,
- .update = dcerpc_ntlmssp_update,
- .seal = dcerpc_ntlmssp_seal,
- .sign = dcerpc_ntlmssp_sign,
- .check_sig = dcerpc_ntlmssp_check_sig,
- .unseal = dcerpc_ntlmssp_unseal,
- .session_key = dcerpc_ntlmssp_session_key,
- .end = dcerpc_ntlmssp_end
-};
-
-const struct dcesrv_security_ops *dcerpc_ntlmssp_security_get_ops(void)
-{
- return &dcerpc_ntlmssp_security_ops;
-}
-
-/*
- do ntlm style authentication on a dcerpc pipe
+ do ntlm style authentication on a gensec pipe
*/
NTSTATUS dcerpc_bind_auth_ntlm(struct dcerpc_pipe *p,
const char *uuid, uint_t version,
@@ -153,12 +33,12 @@ NTSTATUS dcerpc_bind_auth_ntlm(struct dcerpc_pipe *p,
{
NTSTATUS status;
+ p->security_state.generic_state.user.domain = domain;
+ p->security_state.generic_state.user.name = username;
+ p->security_state.generic_state.user.password = password;
+
status = dcerpc_bind_auth(p, DCERPC_AUTH_TYPE_NTLMSSP,
- uuid, version,
- domain, username,
- password);
+ uuid, version);
return status;
}
-
-
diff --git a/source4/librpc/rpc/dcerpc_schannel.c b/source4/librpc/rpc/dcerpc_schannel.c
index df15edfb6f..b43dd0788a 100644
--- a/source4/librpc/rpc/dcerpc_schannel.c
+++ b/source4/librpc/rpc/dcerpc_schannel.c
@@ -32,109 +32,68 @@ struct dcerpc_schannel_state {
struct schannel_state *schannel_state;
};
+static NTSTATUS dcerpc_schannel_key(struct dcerpc_pipe *p,
+ const char *domain,
+ const char *username,
+ const char *password,
+ int chan_type,
+ uint8_t new_session_key[16]);
+
/*
wrappers for the schannel_*() functions
+
+ These will become static again, when we get dynamic registration, and
+ decrpc_schannel_security_ops come back here.
*/
-static NTSTATUS dcerpc_schannel_unseal(struct dcerpc_security *dcerpc_security,
+static NTSTATUS dcerpc_schannel_unseal(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
uint8_t *data, size_t length, DATA_BLOB *sig)
{
- struct dcerpc_schannel_state *dce_schan_state = dcerpc_security->private_data;
+ struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data;
return schannel_unseal_packet(dce_schan_state->schannel_state, mem_ctx, data, length, sig);
}
-static NTSTATUS dcerpc_schannel_check_sig(struct dcerpc_security *dcerpc_security,
+static NTSTATUS dcerpc_schannel_check_sig(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
const uint8_t *data, size_t length,
const DATA_BLOB *sig)
{
- struct dcerpc_schannel_state *dce_schan_state = dcerpc_security->private_data;
+ struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data;
return schannel_check_packet(dce_schan_state->schannel_state, data, length, sig);
}
-static NTSTATUS dcerpc_schannel_seal(struct dcerpc_security *dcerpc_security,
+static NTSTATUS dcerpc_schannel_seal(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
uint8_t *data, size_t length,
DATA_BLOB *sig)
{
- struct dcerpc_schannel_state *dce_schan_state = dcerpc_security->private_data;
+ struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data;
return schannel_seal_packet(dce_schan_state->schannel_state, mem_ctx, data, length, sig);
}
-static NTSTATUS dcerpc_schannel_sign(struct dcerpc_security *dcerpc_security,
+static NTSTATUS dcerpc_schannel_sign(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
const uint8_t *data, size_t length,
DATA_BLOB *sig)
{
- struct dcerpc_schannel_state *dce_schan_state = dcerpc_security->private_data;
+ struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data;
return schannel_sign_packet(dce_schan_state->schannel_state, mem_ctx, data, length, sig);
}
-static NTSTATUS dcerpc_schannel_session_key(struct dcerpc_security *dcerpc_security,
+static NTSTATUS dcerpc_schannel_session_key(struct gensec_security *gensec_security,
DATA_BLOB *session_key)
{
return NT_STATUS_NOT_IMPLEMENTED;
}
-static NTSTATUS dcerpc_schannel_start(struct dcerpc_pipe *p, struct dcerpc_security *dcerpc_security)
-{
- struct dcerpc_schannel_state *dce_schan_state;
- TALLOC_CTX *mem_ctx;
- NTSTATUS status;
- uint8_t session_key[16];
- int chan_type = 0;
-
- if (p->flags & DCERPC_SCHANNEL_BDC) {
- chan_type = SEC_CHAN_BDC;
- } else if (p->flags & DCERPC_SCHANNEL_WORKSTATION) {
- chan_type = SEC_CHAN_WKSTA;
- } else if (p->flags & DCERPC_SCHANNEL_DOMAIN) {
- chan_type = SEC_CHAN_DOMAIN;
- }
-
- status = dcerpc_schannel_key(p, dcerpc_security->user.domain,
- dcerpc_security->user.name,
- dcerpc_security->user.password,
- chan_type, session_key);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
-
- mem_ctx = talloc_init("dcerpc_schannel_start");
- if (!mem_ctx) {
- return NT_STATUS_NO_MEMORY;
- }
-
- dce_schan_state = talloc_p(mem_ctx, struct dcerpc_schannel_state);
- if (!dce_schan_state) {
- talloc_destroy(mem_ctx);
- return NT_STATUS_NO_MEMORY;
- }
-
- dce_schan_state->mem_ctx = mem_ctx;
-
- status = schannel_start(&dce_schan_state->schannel_state, session_key, True);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
-
- dce_schan_state->state = DCERPC_SCHANNEL_STATE_START;
-
- dcerpc_security->private_data = dce_schan_state;
-
- dump_data_pw("session key:\n", dce_schan_state->schannel_state->session_key, 16);
-
- return status;
-}
-
-static NTSTATUS dcerpc_schannel_update(struct dcerpc_security *dcerpc_security, TALLOC_CTX *out_mem_ctx,
+static NTSTATUS dcerpc_schannel_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx,
const DATA_BLOB in, DATA_BLOB *out)
{
- struct dcerpc_schannel_state *dce_schan_state = dcerpc_security->private_data;
+ struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data;
NTSTATUS status;
struct schannel_bind bind_schannel;
@@ -148,14 +107,14 @@ static NTSTATUS dcerpc_schannel_update(struct dcerpc_security *dcerpc_security,
#if 0
/* to support this we'd need to have access to the full domain name */
bind_schannel.bind_type = 23;
- bind_schannel.u.info23.domain = dcerpc_security->user.domain;
- bind_schannel.u.info23.account_name = dcerpc_security->user.name;
+ bind_schannel.u.info23.domain = gensec_security->user.domain;
+ bind_schannel.u.info23.account_name = gensec_security->user.name;
bind_schannel.u.info23.dnsdomain = str_format_nbt_domain(dce_schan_state->mem_ctx, fulldomainname);
- bind_schannel.u.info23.workstation = str_format_nbt_domain(dce_schan_state->mem_ctx, dcerpc_security->user.name);
+ bind_schannel.u.info23.workstation = str_format_nbt_domain(dce_schan_state->mem_ctx, gensec_security->user.name);
#else
bind_schannel.bind_type = 3;
- bind_schannel.u.info3.domain = dcerpc_security->user.domain;
- bind_schannel.u.info3.account_name = dcerpc_security->user.name;
+ bind_schannel.u.info3.domain = gensec_security->user.domain;
+ bind_schannel.u.info3.account_name = gensec_security->user.name;
#endif
status = ndr_push_struct_blob(out, dce_schan_state->mem_ctx, &bind_schannel,
@@ -167,27 +126,39 @@ static NTSTATUS dcerpc_schannel_update(struct dcerpc_security *dcerpc_security,
return NT_STATUS_MORE_PROCESSING_REQUIRED;
}
-static void dcerpc_schannel_end(struct dcerpc_security *dcerpc_security)
+static void dcerpc_schannel_end(struct gensec_security *gensec_security)
{
- struct dcerpc_schannel_state *dce_schan_state = dcerpc_security->private_data;
+ struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data;
schannel_end(&dce_schan_state->schannel_state);
talloc_destroy(dce_schan_state->mem_ctx);
- dcerpc_security->private_data = NULL;
+ gensec_security->private_data = NULL;
}
+static const struct gensec_security_ops gensec_dcerpc_schannel_security_ops = {
+ .name = "dcerpc_schannel",
+ .auth_type = DCERPC_AUTH_TYPE_SCHANNEL,
+ .update = dcerpc_schannel_update,
+ .seal = dcerpc_schannel_seal,
+ .sign = dcerpc_schannel_sign,
+ .check_sig = dcerpc_schannel_check_sig,
+ .unseal = dcerpc_schannel_unseal,
+ .session_key = dcerpc_schannel_session_key,
+ .end = dcerpc_schannel_end
+};
+
/*
get a schannel key using a netlogon challenge on a secondary pipe
*/
-NTSTATUS dcerpc_schannel_key(struct dcerpc_pipe *p,
- const char *domain,
- const char *username,
- const char *password,
- int chan_type,
- uint8_t new_session_key[16])
+static NTSTATUS dcerpc_schannel_key(struct dcerpc_pipe *p,
+ const char *domain,
+ const char *username,
+ const char *password,
+ int chan_type,
+ uint8_t new_session_key[16])
{
NTSTATUS status;
struct dcerpc_pipe *p2;
@@ -269,24 +240,6 @@ NTSTATUS dcerpc_schannel_key(struct dcerpc_pipe *p,
return NT_STATUS_OK;
}
-const struct dcesrv_security_ops dcerpc_schannel_security_ops = {
- .name = "schannel",
- .auth_type = DCERPC_AUTH_TYPE_SCHANNEL,
- .start = dcerpc_schannel_start,
- .update = dcerpc_schannel_update,
- .seal = dcerpc_schannel_seal,
- .sign = dcerpc_schannel_sign,
- .check_sig = dcerpc_schannel_check_sig,
- .unseal = dcerpc_schannel_unseal,
- .session_key = dcerpc_schannel_session_key,
- .end = dcerpc_schannel_end
-};
-
-const struct dcesrv_security_ops *dcerpc_schannel_security_get_ops(void)
-{
- return &dcerpc_schannel_security_ops;
-}
-
/*
do a schannel style bind on a dcerpc pipe. The username is usually
of the form HOSTNAME$ and the password is the domain trust password
@@ -298,11 +251,58 @@ NTSTATUS dcerpc_bind_auth_schannel(struct dcerpc_pipe *p,
const char *password)
{
NTSTATUS status;
+ struct dcerpc_schannel_state *dce_schan_state;
+ TALLOC_CTX *mem_ctx;
+ uint8_t session_key[16];
+ int chan_type = 0;
+
+ if (p->flags & DCERPC_SCHANNEL_BDC) {
+ chan_type = SEC_CHAN_BDC;
+ } else if (p->flags & DCERPC_SCHANNEL_WORKSTATION) {
+ chan_type = SEC_CHAN_WKSTA;
+ } else if (p->flags & DCERPC_SCHANNEL_DOMAIN) {
+ chan_type = SEC_CHAN_DOMAIN;
+ }
+
+ status = dcerpc_schannel_key(p, domain,
+ username,
+ password,
+ chan_type, session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ mem_ctx = talloc_init("dcerpc_schannel_start");
+ if (!mem_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ dce_schan_state = talloc_p(mem_ctx, struct dcerpc_schannel_state);
+ if (!dce_schan_state) {
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ dce_schan_state->mem_ctx = mem_ctx;
+
+ status = schannel_start(&dce_schan_state->schannel_state, session_key, True);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ dce_schan_state->state = DCERPC_SCHANNEL_STATE_START;
+
+ p->security_state.generic_state.user.domain = domain;
+ p->security_state.generic_state.user.name = username;
+ p->security_state.generic_state.user.password = password;
+
+ p->security_state.generic_state.ops = &gensec_dcerpc_schannel_security_ops;
+ p->security_state.generic_state.private_data = dce_schan_state;
+
+ dump_data_pw("session key:\n", dce_schan_state->schannel_state->session_key, 16);
status = dcerpc_bind_auth(p, DCERPC_AUTH_TYPE_SCHANNEL,
- uuid, version,
- domain, username,
- password);
+ uuid, version);
return status;
}
diff --git a/source4/librpc/rpc/dcerpc_util.c b/source4/librpc/rpc/dcerpc_util.c
index 8c9b273896..82aa5aa1f3 100644
--- a/source4/librpc/rpc/dcerpc_util.c
+++ b/source4/librpc/rpc/dcerpc_util.c
@@ -697,8 +697,8 @@ NTSTATUS dcerpc_fetch_session_key(struct dcerpc_pipe *p,
{
struct cli_tree *tree;
- if (p->security_state.ops) {
- return p->security_state.ops->session_key(&p->security_state, session_key);
+ if (p->security_state.generic_state.ops) {
+ return p->security_state.generic_state.ops->session_key(&p->security_state.generic_state, session_key);
}
tree = dcerpc_smb_tree(p);
diff --git a/source4/utils/ntlm_auth.c b/source4/utils/ntlm_auth.c
index d70b0b5d2b..7690a549f7 100644
--- a/source4/utils/ntlm_auth.c
+++ b/source4/utils/ntlm_auth.c
@@ -34,7 +34,6 @@ enum stdio_helper_mode {
SQUID_2_5_BASIC,
SQUID_2_5_NTLMSSP,
NTLMSSP_CLIENT_1,
- GSS_SPNEGO,
GSS_SPNEGO_CLIENT,
NTLM_SERVER_1,
NUM_HELPER_MODES
@@ -53,14 +52,8 @@ static void manage_squid_basic_request (enum stdio_helper_mode stdio_helper_mode
static void manage_squid_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
char *buf, int length);
-static void manage_client_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
- char *buf, int length);
-
-static void manage_gss_spnego_request (enum stdio_helper_mode stdio_helper_mode,
- char *buf, int length);
-
-static void manage_gss_spnego_client_request (enum stdio_helper_mode stdio_helper_mode,
- char *buf, int length);
+static void manage_gensec_client_request (enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length);
static void manage_ntlm_server_1_request (enum stdio_helper_mode stdio_helper_mode,
char *buf, int length);
@@ -73,9 +66,8 @@ static const struct {
{ SQUID_2_4_BASIC, "squid-2.4-basic", manage_squid_basic_request},
{ SQUID_2_5_BASIC, "squid-2.5-basic", manage_squid_basic_request},
{ SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_squid_ntlmssp_request},
- { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_client_ntlmssp_request},
- { GSS_SPNEGO, "gss-spnego", manage_gss_spnego_request},
- { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gss_spnego_client_request},
+ { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_gensec_client_request},
+ { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gensec_client_request},
{ NTLM_SERVER_1, "ntlm-server-1", manage_ntlm_server_1_request},
{ NUM_HELPER_MODES, NULL, NULL}
};
@@ -218,52 +210,6 @@ static NTSTATUS local_pw_check(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *u
return nt_status;
}
-static NTSTATUS ntlm_auth_start_ntlmssp_client(struct ntlmssp_state **client_ntlmssp_state)
-{
- NTSTATUS status;
- if ( (opt_username == NULL) || (opt_domain == NULL) ) {
- DEBUG(1, ("Need username and domain for NTLMSSP\n"));
- return status;
- }
-
- status = ntlmssp_client_start(client_ntlmssp_state);
-
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(1, ("Could not start NTLMSSP client: %s\n",
- nt_errstr(status)));
- ntlmssp_end(client_ntlmssp_state);
- return status;
- }
-
- status = ntlmssp_set_username(*client_ntlmssp_state, opt_username);
-
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(1, ("Could not set username: %s\n",
- nt_errstr(status)));
- ntlmssp_end(client_ntlmssp_state);
- return status;
- }
-
- status = ntlmssp_set_domain(*client_ntlmssp_state, opt_domain);
-
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(1, ("Could not set domain: %s\n",
- nt_errstr(status)));
- ntlmssp_end(client_ntlmssp_state);
- return status;
- }
-
- status = ntlmssp_set_password(*client_ntlmssp_state, opt_password);
-
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(1, ("Could not set password: %s\n",
- nt_errstr(status)));
- ntlmssp_end(client_ntlmssp_state);
- return status;
- }
- return NT_STATUS_OK;
-}
-
static NTSTATUS ntlm_auth_start_ntlmssp_server(struct ntlmssp_state **ntlmssp_state)
{
NTSTATUS status = ntlmssp_server_start(ntlmssp_state);
@@ -367,102 +313,6 @@ static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mod
data_blob_free(&request);
}
-static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
- char *buf, int length)
-{
- static struct ntlmssp_state *ntlmssp_state = NULL;
- DATA_BLOB request, reply;
- NTSTATUS nt_status;
- BOOL first = False;
-
- if (strlen(buf) < 2) {
- DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
- x_fprintf(x_stdout, "BH\n");
- return;
- }
-
- if (strlen(buf) > 3) {
- request = base64_decode_data_blob(buf + 3);
- } else {
- request = data_blob(NULL, 0);
- }
-
- if (strncmp(buf, "PW ", 3) == 0) {
- /* We asked for a password and obviously got it :-) */
-
- opt_password = strndup((const char *)request.data, request.length);
-
- if (opt_password == NULL) {
- DEBUG(1, ("Out of memory\n"));
- x_fprintf(x_stdout, "BH\n");
- data_blob_free(&request);
- return;
- }
-
- x_fprintf(x_stdout, "OK\n");
- data_blob_free(&request);
- return;
- }
-
- if (opt_password == NULL) {
-
- /* Request a password from the calling process. After
- sending it, the calling process should retry asking for the negotiate. */
-
- DEBUG(10, ("Requesting password\n"));
- x_fprintf(x_stdout, "PW\n");
- return;
- }
-
- if (strncmp(buf, "YR", 2) == 0) {
- if (ntlmssp_state)
- ntlmssp_end(&ntlmssp_state);
- } else if (strncmp(buf, "TT", 2) == 0) {
-
- } else {
- DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
- x_fprintf(x_stdout, "BH\n");
- return;
- }
-
- if (!ntlmssp_state) {
- if (!NT_STATUS_IS_OK(nt_status = ntlm_auth_start_ntlmssp_client(&ntlmssp_state))) {
- x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
- return;
- }
- first = True;
- }
-
- DEBUG(10, ("got NTLMSSP packet:\n"));
- dump_data(10, (const char *)request.data, request.length);
-
- nt_status = ntlmssp_update(ntlmssp_state, NULL, request, &reply);
-
- if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
- char *reply_base64 = base64_encode_data_blob(reply);
- if (first) {
- x_fprintf(x_stdout, "YR %s\n", reply_base64);
- } else {
- x_fprintf(x_stdout, "KK %s\n", reply_base64);
- }
- SAFE_FREE(reply_base64);
- data_blob_free(&reply);
- DEBUG(10, ("NTLMSSP challenge\n"));
- } else if (NT_STATUS_IS_OK(nt_status)) {
- x_fprintf(x_stdout, "AF\n");
- DEBUG(10, ("NTLMSSP OK!\n"));
- if (ntlmssp_state)
- ntlmssp_end(&ntlmssp_state);
- } else {
- x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
- DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status)));
- if (ntlmssp_state)
- ntlmssp_end(&ntlmssp_state);
- }
-
- data_blob_free(&request);
-}
-
static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode,
char *buf, int length)
{
@@ -490,654 +340,111 @@ static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode,
}
}
-static void offer_gss_spnego_mechs(void) {
-
- DATA_BLOB token;
- struct spnego_data spnego;
- ssize_t len;
- char *reply_base64;
-
- pstring principal;
- pstring myname_lower;
-
- ZERO_STRUCT(spnego);
-
- pstrcpy(myname_lower, global_myname());
- strlower_m(myname_lower);
-
- pstr_sprintf(principal, "%s$@%s", myname_lower, lp_realm());
-
- /* Server negTokenInit (mech offerings) */
- spnego.type = SPNEGO_NEG_TOKEN_INIT;
- spnego.negTokenInit.mechTypes = smb_xmalloc(sizeof(char *) * 3);
-#ifdef HAVE_KRB5
- spnego.negTokenInit.mechTypes[0] = smb_xstrdup(OID_KERBEROS5_OLD);
- spnego.negTokenInit.mechTypes[1] = smb_xstrdup(OID_NTLMSSP);
- spnego.negTokenInit.mechTypes[2] = NULL;
-#else
- spnego.negTokenInit.mechTypes[0] = smb_xstrdup(OID_NTLMSSP);
- spnego.negTokenInit.mechTypes[1] = NULL;
-#endif
-
-
- spnego.negTokenInit.mechListMIC = data_blob(principal,
- strlen(principal));
-
- len = write_spnego_data(&token, &spnego);
- free_spnego_data(&spnego);
-
- if (len == -1) {
- DEBUG(1, ("Could not write SPNEGO data blob\n"));
- x_fprintf(x_stdout, "BH\n");
- return;
- }
-
- reply_base64 = base64_encode_data_blob(token);
- x_fprintf(x_stdout, "TT %s *\n", reply_base64);
-
- SAFE_FREE(reply_base64);
- data_blob_free(&token);
- DEBUG(10, ("sent SPNEGO negTokenInit\n"));
- return;
-}
-
-static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
- char *buf, int length)
+static void manage_gensec_client_request(enum stdio_helper_mode stdio_helper_mode,
+ char *buf, int length)
{
- static struct ntlmssp_state *ntlmssp_state = NULL;
- struct spnego_data request, response;
- DATA_BLOB token;
- NTSTATUS status;
- ssize_t len;
-
- char *user = NULL;
- char *domain = NULL;
-
- const char *reply_code;
- char *reply_base64;
- pstring reply_argument;
+ DATA_BLOB in;
+ DATA_BLOB out;
+ char *out_base64;
+ static struct gensec_security gensec_state;
+ NTSTATUS nt_status;
+ BOOL first = False;
if (strlen(buf) < 2) {
- DEBUG(1, ("SPENGO query [%s] invalid", buf));
+ DEBUG(1, ("query [%s] invalid", buf));
x_fprintf(x_stdout, "BH\n");
return;
}
- if (strncmp(buf, "YR", 2) == 0) {
- if (ntlmssp_state)
- ntlmssp_end(&ntlmssp_state);
- } else if (strncmp(buf, "KK", 2) == 0) {
-
- } else {
- DEBUG(1, ("SPENGO query [%s] invalid", buf));
- x_fprintf(x_stdout, "BH\n");
- return;
- }
-
- if ( (strlen(buf) == 2)) {
-
- /* no client data, get the negTokenInit offering
- mechanisms */
-
- offer_gss_spnego_mechs();
- return;
- }
-
- /* All subsequent requests have a blob. This might be negTokenInit or negTokenTarg */
-
- if (strlen(buf) <= 3) {
- DEBUG(1, ("GSS-SPNEGO query [%s] invalid\n", buf));
- x_fprintf(x_stdout, "BH\n");
- return;
- }
-
- token = base64_decode_data_blob(buf + 3);
- len = read_spnego_data(token, &request);
- data_blob_free(&token);
-
- if (len == -1) {
- DEBUG(1, ("GSS-SPNEGO query [%s] invalid", buf));
- x_fprintf(x_stdout, "BH\n");
- return;
- }
-
- if (request.type == SPNEGO_NEG_TOKEN_INIT) {
-
- /* Second request from Client. This is where the
- client offers its mechanism to use. */
-
- if ( (request.negTokenInit.mechTypes == NULL) ||
- (request.negTokenInit.mechTypes[0] == NULL) ) {
- DEBUG(1, ("Client did not offer any mechanism"));
- x_fprintf(x_stdout, "BH\n");
- return;
- }
-
- if (strcmp(request.negTokenInit.mechTypes[0], OID_NTLMSSP) == 0) {
-
- if ( request.negTokenInit.mechToken.data == NULL ) {
- DEBUG(1, ("Client did not provide NTLMSSP data\n"));
- x_fprintf(x_stdout, "BH\n");
- return;
- }
-
- if ( ntlmssp_state != NULL ) {
- DEBUG(1, ("Client wants a new NTLMSSP challenge, but "
- "already got one\n"));
- x_fprintf(x_stdout, "BH\n");
- ntlmssp_end(&ntlmssp_state);
- return;
- }
-
- if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_server(&ntlmssp_state))) {
- x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
- return;
- }
-
- DEBUG(10, ("got NTLMSSP packet:\n"));
- dump_data(10, (const char *)request.negTokenInit.mechToken.data,
- request.negTokenInit.mechToken.length);
-
- response.type = SPNEGO_NEG_TOKEN_TARG;
- response.negTokenTarg.supportedMech = strdup(OID_NTLMSSP);
- response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
-
- status = ntlmssp_update(ntlmssp_state,
- NULL,
- request.negTokenInit.mechToken,
- &response.negTokenTarg.responseToken);
- }
-
-#ifdef HAVE_KRB5
- if (strcmp(request.negTokenInit.mechTypes[0], OID_KERBEROS5_OLD) == 0) {
-
- char *principal;
- DATA_BLOB auth_data;
- DATA_BLOB ap_rep;
- DATA_BLOB session_key;
-
- if ( request.negTokenInit.mechToken.data == NULL ) {
- DEBUG(1, ("Client did not provide Kerberos data\n"));
- x_fprintf(x_stdout, "BH\n");
- return;
- }
-
- response.type = SPNEGO_NEG_TOKEN_TARG;
- response.negTokenTarg.supportedMech = strdup(OID_KERBEROS5_OLD);
- response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
- response.negTokenTarg.responseToken = data_blob(NULL, 0);
-
- status = ads_verify_ticket(lp_realm(),
- &request.negTokenInit.mechToken,
- &principal, &auth_data, &ap_rep,
- &session_key);
-
- /* Now in "principal" we have the name we are
- authenticated as. */
-
- if (NT_STATUS_IS_OK(status)) {
-
- domain = strchr(principal, '@');
-
- if (domain == NULL) {
- DEBUG(1, ("Did not get a valid principal "
- "from ads_verify_ticket\n"));
- x_fprintf(x_stdout, "BH\n");
- return;
- }
-
- *domain++ = '\0';
- domain = strdup(domain);
- user = strdup(principal);
-
- data_blob_free(&ap_rep);
- data_blob_free(&auth_data);
-
- SAFE_FREE(principal);
- }
- }
-#endif
-
- } else {
-
- if ( (request.negTokenTarg.supportedMech == NULL) ||
- ( strcmp(request.negTokenTarg.supportedMech, OID_NTLMSSP) != 0 ) ) {
- /* Kerberos should never send a negTokenTarg, OID_NTLMSSP
- is the only one we support that sends this stuff */
- DEBUG(1, ("Got a negTokenTarg for something non-NTLMSSP: %s\n",
- request.negTokenTarg.supportedMech));
- x_fprintf(x_stdout, "BH\n");
- return;
- }
-
- if (request.negTokenTarg.responseToken.data == NULL) {
- DEBUG(1, ("Got a negTokenTarg without a responseToken!\n"));
- x_fprintf(x_stdout, "BH\n");
- return;
- }
-
- status = ntlmssp_update(ntlmssp_state,
- NULL,
- request.negTokenTarg.responseToken,
- &response.negTokenTarg.responseToken);
-
- response.type = SPNEGO_NEG_TOKEN_TARG;
- response.negTokenTarg.supportedMech = strdup(OID_NTLMSSP);
- response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
-
- if (NT_STATUS_IS_OK(status)) {
- user = strdup(ntlmssp_state->user);
- domain = strdup(ntlmssp_state->domain);
- ntlmssp_end(&ntlmssp_state);
- }
- }
-
- free_spnego_data(&request);
-
- if (NT_STATUS_IS_OK(status)) {
- response.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
- reply_code = "AF";
- pstr_sprintf(reply_argument, "%s\\%s", domain, user);
- } else if (NT_STATUS_EQUAL(status,
- NT_STATUS_MORE_PROCESSING_REQUIRED)) {
- response.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
- reply_code = "TT";
- pstr_sprintf(reply_argument, "*");
+ if (strlen(buf) > 3) {
+ in = base64_decode_data_blob(buf + 3);
} else {
- response.negTokenTarg.negResult = SPNEGO_REJECT;
- reply_code = "NA";
- pstrcpy(reply_argument, nt_errstr(status));
- }
-
- SAFE_FREE(user);
- SAFE_FREE(domain);
-
- len = write_spnego_data(&token, &response);
- free_spnego_data(&response);
-
- if (len == -1) {
- DEBUG(1, ("Could not write SPNEGO data blob\n"));
- x_fprintf(x_stdout, "BH\n");
- return;
- }
-
- reply_base64 = base64_encode_data_blob(token);
-
- x_fprintf(x_stdout, "%s %s %s\n",
- reply_code, reply_base64, reply_argument);
-
- SAFE_FREE(reply_base64);
- data_blob_free(&token);
-
- return;
-}
-
-static struct ntlmssp_state *client_ntlmssp_state = NULL;
-
-static BOOL manage_client_ntlmssp_init(struct spnego_data spnego)
-{
- NTSTATUS status;
- DATA_BLOB null_blob = data_blob(NULL, 0);
- DATA_BLOB to_server;
- char *to_server_base64;
- const char *my_mechs[] = {OID_NTLMSSP, NULL};
-
- DEBUG(10, ("Got spnego negTokenInit with NTLMSSP\n"));
-
- if (client_ntlmssp_state != NULL) {
- DEBUG(1, ("Request for initial SPNEGO request where "
- "we already have a state\n"));
- return False;
- }
-
- if (!client_ntlmssp_state) {
- if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_client(&client_ntlmssp_state))) {
- x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
- return False;
- }
- }
-
-
- if (opt_password == NULL) {
-
- /* Request a password from the calling process. After
- sending it, the calling process should retry with
- the negTokenInit. */
-
- DEBUG(10, ("Requesting password\n"));
- x_fprintf(x_stdout, "PW\n");
- return True;
- }
-
- spnego.type = SPNEGO_NEG_TOKEN_INIT;
- spnego.negTokenInit.mechTypes = my_mechs;
- spnego.negTokenInit.reqFlags = 0;
- spnego.negTokenInit.mechListMIC = null_blob;
-
- status = ntlmssp_update(client_ntlmssp_state,
- NULL,
- null_blob,
- &spnego.negTokenInit.mechToken);
-
- if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
- DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED, got: %s\n",
- nt_errstr(status)));
- ntlmssp_end(&client_ntlmssp_state);
- return False;
- }
-
- write_spnego_data(&to_server, &spnego);
- data_blob_free(&spnego.negTokenInit.mechToken);
-
- to_server_base64 = base64_encode_data_blob(to_server);
- data_blob_free(&to_server);
- x_fprintf(x_stdout, "KK %s\n", to_server_base64);
- SAFE_FREE(to_server_base64);
- return True;
-}
-
-static void manage_client_ntlmssp_targ(struct spnego_data spnego)
-{
- NTSTATUS status;
- DATA_BLOB null_blob = data_blob(NULL, 0);
- DATA_BLOB request;
- DATA_BLOB to_server;
- char *to_server_base64;
-
- DEBUG(10, ("Got spnego negTokenTarg with NTLMSSP\n"));
-
- if (client_ntlmssp_state == NULL) {
- DEBUG(1, ("Got NTLMSSP tArg without a client state\n"));
- x_fprintf(x_stdout, "BH\n");
- ntlmssp_end(&client_ntlmssp_state);
- return;
- }
-
- if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
- x_fprintf(x_stdout, "NA\n");
- ntlmssp_end(&client_ntlmssp_state);
- return;
- }
-
- if (spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) {
- x_fprintf(x_stdout, "AF\n");
- ntlmssp_end(&client_ntlmssp_state);
- return;
- }
-
- status = ntlmssp_update(client_ntlmssp_state,
- NULL,
- spnego.negTokenTarg.responseToken,
- &request);
-
- if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
- DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED from "
- "ntlmssp_update, got: %s\n",
- nt_errstr(status)));
- x_fprintf(x_stdout, "BH\n");
- data_blob_free(&request);
- ntlmssp_end(&client_ntlmssp_state);
- return;
- }
-
- spnego.type = SPNEGO_NEG_TOKEN_TARG;
- spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
- spnego.negTokenTarg.supportedMech = OID_NTLMSSP;
- spnego.negTokenTarg.responseToken = request;
- spnego.negTokenTarg.mechListMIC = null_blob;
-
- write_spnego_data(&to_server, &spnego);
- data_blob_free(&request);
-
- to_server_base64 = base64_encode_data_blob(to_server);
- data_blob_free(&to_server);
- x_fprintf(x_stdout, "KK %s\n", to_server_base64);
- SAFE_FREE(to_server_base64);
- return;
-}
-
-#ifdef HAVE_KRB5
-
-static BOOL manage_client_krb5_init(struct spnego_data spnego)
-{
- char *principal;
- DATA_BLOB tkt, to_server;
- DATA_BLOB session_key_krb5 = data_blob(NULL, 0);
- struct spnego_data reply;
- char *reply_base64;
- int retval;
-
- const char *my_mechs[] = {OID_KERBEROS5_OLD, NULL};
- ssize_t len;
-
- if ( (spnego.negTokenInit.mechListMIC.data == NULL) ||
- (spnego.negTokenInit.mechListMIC.length == 0) ) {
- DEBUG(1, ("Did not get a principal for krb5\n"));
- return False;
+ in = data_blob(NULL, 0);
}
- principal = malloc(spnego.negTokenInit.mechListMIC.length+1);
-
- if (principal == NULL) {
- DEBUG(1, ("Could not malloc principal\n"));
- return False;
- }
-
- memcpy(principal, spnego.negTokenInit.mechListMIC.data,
- spnego.negTokenInit.mechListMIC.length);
- principal[spnego.negTokenInit.mechListMIC.length] = '\0';
-
- retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5);
-
- if (retval) {
-
- pstring user;
-
- /* Let's try to first get the TGT, for that we need a
- password. */
-
- if (opt_password == NULL) {
- DEBUG(10, ("Requesting password\n"));
- x_fprintf(x_stdout, "PW\n");
- return True;
- }
-
- pstr_sprintf(user, "%s@%s", opt_username, opt_domain);
-
- if ((retval = kerberos_kinit_password(user, opt_password,
- 0, NULL))) {
- DEBUG(10, ("Requesting TGT failed: %s\n", error_message(retval)));
- return False;
- }
-
- retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5);
-
- if (retval) {
- DEBUG(10, ("Kinit suceeded, but getting a ticket failed: %s\n", error_message(retval)));
- return False;
- }
- }
-
- data_blob_free(&session_key_krb5);
-
- ZERO_STRUCT(reply);
-
- reply.type = SPNEGO_NEG_TOKEN_INIT;
- reply.negTokenInit.mechTypes = my_mechs;
- reply.negTokenInit.reqFlags = 0;
- reply.negTokenInit.mechToken = tkt;
- reply.negTokenInit.mechListMIC = data_blob(NULL, 0);
-
- len = write_spnego_data(&to_server, &reply);
- data_blob_free(&tkt);
-
- if (len == -1) {
- DEBUG(1, ("Could not write SPNEGO data blob\n"));
- return False;
- }
-
- reply_base64 = base64_encode_data_blob(to_server);
- x_fprintf(x_stdout, "KK %s *\n", reply_base64);
-
- SAFE_FREE(reply_base64);
- data_blob_free(&to_server);
- DEBUG(10, ("sent GSS-SPNEGO KERBEROS5 negTokenInit\n"));
- return True;
-}
-
-static void manage_client_krb5_targ(struct spnego_data spnego)
-{
- switch (spnego.negTokenTarg.negResult) {
- case SPNEGO_ACCEPT_INCOMPLETE:
- DEBUG(1, ("Got a Kerberos negTokenTarg with ACCEPT_INCOMPLETE\n"));
- x_fprintf(x_stdout, "BH\n");
- break;
- case SPNEGO_ACCEPT_COMPLETED:
- DEBUG(10, ("Accept completed\n"));
- x_fprintf(x_stdout, "AF\n");
- break;
- case SPNEGO_REJECT:
- DEBUG(10, ("Rejected\n"));
- x_fprintf(x_stdout, "NA\n");
- break;
- default:
- DEBUG(1, ("Got an invalid negTokenTarg\n"));
- x_fprintf(x_stdout, "AF\n");
- }
-}
-
-#endif
-
-static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode,
- char *buf, int length)
-{
- DATA_BLOB request;
- struct spnego_data spnego;
- ssize_t len;
-
- if (strlen(buf) <= 3) {
- DEBUG(1, ("SPNEGO query [%s] too short\n", buf));
- x_fprintf(x_stdout, "BH\n");
- return;
- }
-
- request = base64_decode_data_blob(buf+3);
-
if (strncmp(buf, "PW ", 3) == 0) {
/* We asked for a password and obviously got it :-) */
- opt_password = strndup((const char *)request.data, request.length);
+ opt_password = strndup((const char *)in.data, in.length);
if (opt_password == NULL) {
DEBUG(1, ("Out of memory\n"));
x_fprintf(x_stdout, "BH\n");
- data_blob_free(&request);
+ data_blob_free(&in);
return;
}
x_fprintf(x_stdout, "OK\n");
- data_blob_free(&request);
+ data_blob_free(&in);
return;
}
-
- if ( (strncmp(buf, "TT ", 3) != 0) &&
+ if (strncmp(buf, "YR", 2) == 0) {
+ if (gensec_state.ops) {
+ gensec_state.ops->end(&gensec_state);
+ gensec_state.ops = NULL;
+ }
+ } else if ( (strncmp(buf, "TT ", 3) != 0) &&
(strncmp(buf, "AF ", 3) != 0) &&
(strncmp(buf, "NA ", 3) != 0) ) {
DEBUG(1, ("SPNEGO request [%s] invalid\n", buf));
x_fprintf(x_stdout, "BH\n");
- data_blob_free(&request);
+ data_blob_free(&in);
return;
}
- /* So we got a server challenge to generate a SPNEGO
- client-to-server request... */
-
- len = read_spnego_data(request, &spnego);
- data_blob_free(&request);
-
- if (len == -1) {
- DEBUG(1, ("Could not read SPNEGO data for [%s]\n", buf));
- x_fprintf(x_stdout, "BH\n");
+ if (!opt_password) {
+ x_fprintf(x_stdout, "PW\n");
+ data_blob_free(&in);
return;
}
- if (spnego.type == SPNEGO_NEG_TOKEN_INIT) {
-
- /* The server offers a list of mechanisms */
-
- char **mechType = spnego.negTokenInit.mechTypes;
-
- while (*mechType != NULL) {
-
-#ifdef HAVE_KRB5
- if ( (strcmp(*mechType, OID_KERBEROS5_OLD) == 0) ||
- (strcmp(*mechType, OID_KERBEROS5) == 0) ) {
- if (manage_client_krb5_init(spnego))
- goto out;
- }
-#endif
-
- if (strcmp(*mechType, OID_NTLMSSP) == 0) {
- if (manage_client_ntlmssp_init(spnego))
- goto out;
- }
-
- mechType++;
+ /* setup gensec */
+ if (!gensec_state.ops) {
+ if (stdio_helper_mode == GSS_SPNEGO_CLIENT) {
+ gensec_state.ops = gensec_security_by_oid(OID_SPNEGO);
+ } else if (stdio_helper_mode == NTLMSSP_CLIENT_1) {
+ gensec_state.ops = gensec_security_by_oid(OID_NTLMSSP);
+ } else {
+ exit(1);
}
+ gensec_state.user.name = opt_username;
+ gensec_state.user.domain = opt_domain;
+ gensec_state.user.password = opt_password;
+ nt_status = gensec_state.ops->client_start(&gensec_state);
- DEBUG(1, ("Server offered no compatible mechanism\n"));
- x_fprintf(x_stdout, "BH\n");
- return;
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1, ("SPENGO login failed to initialise: %s\n", nt_errstr(nt_status)));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+ if (!in.length) {
+ first = True;
+ }
}
+
+ /* update */
- if (spnego.type == SPNEGO_NEG_TOKEN_TARG) {
-
- if (spnego.negTokenTarg.supportedMech == NULL) {
- /* On accept/reject Windows does not send the
- mechanism anymore. Handle that here and
- shut down the mechanisms. */
-
- switch (spnego.negTokenTarg.negResult) {
- case SPNEGO_ACCEPT_COMPLETED:
- x_fprintf(x_stdout, "AF\n");
- break;
- case SPNEGO_REJECT:
- x_fprintf(x_stdout, "NA\n");
- break;
- default:
- DEBUG(1, ("Got a negTokenTarg with no mech and an "
- "unknown negResult: %d\n",
- spnego.negTokenTarg.negResult));
- x_fprintf(x_stdout, "BH\n");
- }
-
- ntlmssp_end(&client_ntlmssp_state);
- goto out;
- }
+ nt_status = gensec_state.ops->update(&gensec_state, NULL, in, &out);
+
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
- if (strcmp(spnego.negTokenTarg.supportedMech,
- OID_NTLMSSP) == 0) {
- manage_client_ntlmssp_targ(spnego);
- goto out;
+ out_base64 = base64_encode_data_blob(out);
+ if (first) {
+ x_fprintf(x_stdout, "YR %s\n", out_base64);
+ } else {
+ x_fprintf(x_stdout, "KK %s\n", out_base64);
}
+ SAFE_FREE(out_base64);
-#if HAVE_KRB5
- if (strcmp(spnego.negTokenTarg.supportedMech,
- OID_KERBEROS5_OLD) == 0) {
- manage_client_krb5_targ(spnego);
- goto out;
- }
-#endif
+ } else if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1, ("SPENGO login failed: %s\n", nt_errstr(nt_status)));
+ x_fprintf(x_stdout, "BH\n");
+ } else {
+ x_fprintf(x_stdout, "AF\n");
}
- DEBUG(1, ("Got an SPNEGO token I could not handle [%s]!\n", buf));
- x_fprintf(x_stdout, "BH\n");
- return;
-
- out:
- free_spnego_data(&spnego);
return;
}
@@ -1428,6 +735,7 @@ enum {
{ "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
{ "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
{ "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
+ { "username", 0, POPT_ARG_STRING, &opt_username, OPT_PASSWORD, "Username"},
{ "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},
POPT_COMMON_SAMBA
POPT_TABLEEND
@@ -1469,6 +777,10 @@ enum {
return 1;
}
+ if (opt_domain == NULL) {
+ opt_domain = lp_workgroup();
+ }
+
if (helper_protocol) {
int i;
for (i=0; i<NUM_HELPER_MODES; i++) {
@@ -1492,10 +804,6 @@ enum {
exit(1);
}
- if (opt_domain == NULL) {
- opt_domain = lp_workgroup();
- }
-
if (opt_workstation == NULL) {
opt_workstation = lp_netbios_name();
}