diff options
author | Andrew Bartlett <abartlet@samba.org> | 2004-06-20 00:58:09 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 12:56:44 -0500 |
commit | be081037e09bb78c0308cd6c7a5d7ae563678b7c (patch) | |
tree | 4bdabbe93653433e696d4abc4ac3b2991e59aed6 | |
parent | 8cb41badd4349b7d9d78ff8e25143929522e4749 (diff) | |
download | samba-be081037e09bb78c0308cd6c7a5d7ae563678b7c.tar.gz samba-be081037e09bb78c0308cd6c7a5d7ae563678b7c.tar.bz2 samba-be081037e09bb78c0308cd6c7a5d7ae563678b7c.zip |
r1200: Add 'gensec', our generic security layer.
This layer is used for DCERPC security, as well as ntlm_auth at this
time. It expect things like SASL and the CIFS layer to use it as
well.
The particular purpose of this layer is to introduce SPENGO, which
needs generic access to the actual implementation mechanisms.
Schannel, due to it's 'interesting' setup properties is in GENSEC, but
is only in the RPC code.
Andrew Bartlett
(This used to be commit 902af49006fb8cfecaadd3cc0c10e2e542083fb1)
-rw-r--r-- | source4/include/includes.h | 3 | ||||
-rw-r--r-- | source4/libcli/auth/gensec.c | 104 | ||||
-rw-r--r-- | source4/libcli/auth/gensec.h | 64 | ||||
-rw-r--r-- | source4/libcli/auth/gensec_ntlmssp.c | 122 | ||||
-rw-r--r-- | source4/libcli/auth/ntlmssp.c | 17 | ||||
-rw-r--r-- | source4/libcli/auth/spnego.c | 522 | ||||
-rw-r--r-- | source4/libcli/auth/spnego.h | 33 | ||||
-rw-r--r-- | source4/libcli/auth/spnego_parse.c | 343 | ||||
-rw-r--r-- | source4/libcli/config.m4 | 5 | ||||
-rw-r--r-- | source4/libcli/util/asn1.c | 2 | ||||
-rw-r--r-- | source4/librpc/rpc/dcerpc.c | 58 | ||||
-rw-r--r-- | source4/librpc/rpc/dcerpc.h | 30 | ||||
-rw-r--r-- | source4/librpc/rpc/dcerpc_auth.c | 63 | ||||
-rw-r--r-- | source4/librpc/rpc/dcerpc_ntlm.c | 132 | ||||
-rw-r--r-- | source4/librpc/rpc/dcerpc_schannel.c | 194 | ||||
-rw-r--r-- | source4/librpc/rpc/dcerpc_util.c | 4 | ||||
-rw-r--r-- | source4/utils/ntlm_auth.c | 832 |
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(); } |