summaryrefslogtreecommitdiff
path: root/source4/libcli/auth
diff options
context:
space:
mode:
Diffstat (limited to 'source4/libcli/auth')
-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
7 files changed, 930 insertions, 275 deletions
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;
+}
+