diff options
-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(); } |