From 7cabdeb7ec84c7c0b3e9b907e19f4e240b7fc4ca Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 29 Mar 2005 08:24:03 +0000 Subject: r6113: Move GENSEC and the kerberos code out of libcli/auth, and into auth/gensec and auth/kerberos. This also pulls the kerberos configure code out of libads (which is otherwise dead), and into auth/kerberos/kerberos.m4 Andrew Bartlett (This used to be commit e074d63f3dcf4f84239a10879112ebaf1cfa6c4f) --- source4/auth/auth.h | 6 +- source4/auth/gensec/gensec.c | 630 +++++++++++++++ source4/auth/gensec/gensec.h | 117 +++ source4/auth/gensec/gensec.m4 | 12 + source4/auth/gensec/gensec.mk | 86 ++ source4/auth/gensec/gensec_gssapi.c | 376 +++++++++ source4/auth/gensec/gensec_gsskrb5.c | 584 ++++++++++++++ source4/auth/gensec/gensec_krb5.c | 751 ++++++++++++++++++ source4/auth/gensec/gensec_ntlmssp.c | 514 ++++++++++++ source4/auth/gensec/ntlmssp.c | 1322 +++++++++++++++++++++++++++++++ source4/auth/gensec/ntlmssp.h | 190 +++++ source4/auth/gensec/ntlmssp_parse.c | 338 ++++++++ source4/auth/gensec/ntlmssp_sign.c | 449 +++++++++++ source4/auth/gensec/schannel.c | 268 +++++++ source4/auth/gensec/schannel.h | 35 + source4/auth/gensec/schannel_sign.c | 283 +++++++ source4/auth/gensec/schannel_state.c | 229 ++++++ source4/auth/gensec/spnego.c | 884 +++++++++++++++++++++ source4/auth/gensec/spnego.h | 69 ++ source4/auth/gensec/spnego_parse.c | 375 +++++++++ source4/auth/kerberos/clikrb5.c | 478 +++++++++++ source4/auth/kerberos/gssapi_parse.c | 95 +++ source4/auth/kerberos/kerberos.c | 788 ++++++++++++++++++ source4/auth/kerberos/kerberos.h | 99 +++ source4/auth/kerberos/kerberos.m4 | 491 ++++++++++++ source4/auth/kerberos/kerberos.mk | 10 + source4/auth/kerberos/kerberos_verify.c | 486 ++++++++++++ source4/build/smb_build/main.pm | 3 +- source4/configure.in | 3 +- source4/libads/config.m4 | 490 ------------ source4/libcli/auth/clikrb5.c | 478 ----------- source4/libcli/auth/gensec.c | 630 --------------- source4/libcli/auth/gensec.h | 117 --- source4/libcli/auth/gensec.m4 | 12 - source4/libcli/auth/gensec.mk | 91 --- source4/libcli/auth/gensec_gssapi.c | 376 --------- source4/libcli/auth/gensec_gsskrb5.c | 584 -------------- source4/libcli/auth/gensec_krb5.c | 751 ------------------ source4/libcli/auth/gensec_ntlmssp.c | 514 ------------ source4/libcli/auth/gssapi_parse.c | 95 --- source4/libcli/auth/kerberos.c | 788 ------------------ source4/libcli/auth/kerberos.h | 99 --- source4/libcli/auth/kerberos_verify.c | 486 ------------ source4/libcli/auth/ntlmssp.c | 1322 ------------------------------- source4/libcli/auth/ntlmssp.h | 190 ----- source4/libcli/auth/ntlmssp_parse.c | 338 -------- source4/libcli/auth/ntlmssp_sign.c | 449 ----------- source4/libcli/auth/schannel.c | 268 ------- source4/libcli/auth/schannel.h | 35 - source4/libcli/auth/schannel_sign.c | 284 ------- source4/libcli/auth/schannel_state.c | 229 ------ source4/libcli/auth/spnego.c | 884 --------------------- source4/libcli/auth/spnego.h | 69 -- source4/libcli/auth/spnego_parse.c | 375 --------- 54 files changed, 9966 insertions(+), 9959 deletions(-) create mode 100644 source4/auth/gensec/gensec.c create mode 100644 source4/auth/gensec/gensec.h create mode 100644 source4/auth/gensec/gensec.m4 create mode 100644 source4/auth/gensec/gensec.mk create mode 100644 source4/auth/gensec/gensec_gssapi.c create mode 100644 source4/auth/gensec/gensec_gsskrb5.c create mode 100644 source4/auth/gensec/gensec_krb5.c create mode 100644 source4/auth/gensec/gensec_ntlmssp.c create mode 100644 source4/auth/gensec/ntlmssp.c create mode 100644 source4/auth/gensec/ntlmssp.h create mode 100644 source4/auth/gensec/ntlmssp_parse.c create mode 100644 source4/auth/gensec/ntlmssp_sign.c create mode 100644 source4/auth/gensec/schannel.c create mode 100644 source4/auth/gensec/schannel.h create mode 100644 source4/auth/gensec/schannel_sign.c create mode 100644 source4/auth/gensec/schannel_state.c create mode 100644 source4/auth/gensec/spnego.c create mode 100644 source4/auth/gensec/spnego.h create mode 100644 source4/auth/gensec/spnego_parse.c create mode 100644 source4/auth/kerberos/clikrb5.c create mode 100644 source4/auth/kerberos/gssapi_parse.c create mode 100644 source4/auth/kerberos/kerberos.c create mode 100644 source4/auth/kerberos/kerberos.h create mode 100644 source4/auth/kerberos/kerberos.m4 create mode 100644 source4/auth/kerberos/kerberos.mk create mode 100644 source4/auth/kerberos/kerberos_verify.c delete mode 100644 source4/libcli/auth/clikrb5.c delete mode 100644 source4/libcli/auth/gensec.c delete mode 100644 source4/libcli/auth/gensec.h delete mode 100644 source4/libcli/auth/gensec.m4 delete mode 100644 source4/libcli/auth/gensec.mk delete mode 100644 source4/libcli/auth/gensec_gssapi.c delete mode 100644 source4/libcli/auth/gensec_gsskrb5.c delete mode 100644 source4/libcli/auth/gensec_krb5.c delete mode 100644 source4/libcli/auth/gensec_ntlmssp.c delete mode 100644 source4/libcli/auth/gssapi_parse.c delete mode 100644 source4/libcli/auth/kerberos.c delete mode 100644 source4/libcli/auth/kerberos.h delete mode 100644 source4/libcli/auth/kerberos_verify.c delete mode 100644 source4/libcli/auth/ntlmssp.c delete mode 100644 source4/libcli/auth/ntlmssp.h delete mode 100644 source4/libcli/auth/ntlmssp_parse.c delete mode 100644 source4/libcli/auth/ntlmssp_sign.c delete mode 100644 source4/libcli/auth/schannel.c delete mode 100644 source4/libcli/auth/schannel.h delete mode 100644 source4/libcli/auth/schannel_sign.c delete mode 100644 source4/libcli/auth/schannel_state.c delete mode 100644 source4/libcli/auth/spnego.c delete mode 100644 source4/libcli/auth/spnego.h delete mode 100644 source4/libcli/auth/spnego_parse.c (limited to 'source4') diff --git a/source4/auth/auth.h b/source4/auth/auth.h index 425410e088..a9f6b8eac5 100644 --- a/source4/auth/auth.h +++ b/source4/auth/auth.h @@ -22,10 +22,10 @@ #ifndef _SAMBA_AUTH_H #define _SAMBA_AUTH_H -#include "libcli/auth/ntlmssp.h" +#include "auth/gensec/ntlmssp.h" #include "libcli/auth/credentials.h" -#include "libcli/auth/gensec.h" -#include "libcli/auth/spnego.h" +#include "auth/gensec/gensec.h" +#include "auth/gensec/spnego.h" /* modules can use the following to determine if the interface has changed * please increment the version number after each interface change diff --git a/source4/auth/gensec/gensec.c b/source4/auth/gensec/gensec.c new file mode 100644 index 0000000000..cc7327187c --- /dev/null +++ b/source4/auth/gensec/gensec.c @@ -0,0 +1,630 @@ +/* + Unix SMB/CIFS implementation. + + Generic Authentication Interface + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Andrew Bartlett 2004-2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "auth/auth.h" + +/* the list of currently registered GENSEC backends */ +const static struct gensec_security_ops **generic_security_ops; +static int gensec_num_backends; + +static const struct gensec_security_ops *gensec_security_by_authtype(uint8_t auth_type) +{ + int i; + for (i=0; i < gensec_num_backends; i++) { + if (generic_security_ops[i]->auth_type == auth_type) { + return generic_security_ops[i]; + } + } + + return NULL; +} + +static const struct gensec_security_ops *gensec_security_by_oid(const char *oid_string) +{ + int i; + for (i=0; i < gensec_num_backends; i++) { + if (generic_security_ops[i]->oid && + (strcmp(generic_security_ops[i]->oid, oid_string) == 0)) { + return generic_security_ops[i]; + } + } + + return NULL; +} + +static const struct gensec_security_ops *gensec_security_by_sasl_name(const char *sasl_name) +{ + int i; + for (i=0; i < gensec_num_backends; 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; +} + +static const struct gensec_security_ops *gensec_security_by_name(const char *name) +{ + int i; + for (i=0; i < gensec_num_backends; i++) { + if (generic_security_ops[i]->name + && (strcmp(generic_security_ops[i]->name, name) == 0)) { + return generic_security_ops[i]; + } + } + + return NULL; +} + +const struct gensec_security_ops **gensec_security_all(int *num_backends_out) +{ + *num_backends_out = gensec_num_backends; + return generic_security_ops; +} + +const char **gensec_security_oids(TALLOC_CTX *mem_ctx, const char *skip) +{ + int i, j = 0; + const char **oid_list; + int num_backends; + const struct gensec_security_ops **ops = gensec_security_all(&num_backends); + if (!ops) { + return NULL; + } + oid_list = talloc_array(mem_ctx, const char *, num_backends + 1); + if (!oid_list) { + return NULL; + } + + for (i=0; ioid) { + continue; + } + + if (skip && strcmp(skip, ops[i]->oid)==0) { + continue; + } + + oid_list[j] = ops[i]->oid; + j++; + } + oid_list[j] = NULL; + return oid_list; +} + +/** + Start the GENSEC system, returning a context pointer. + @param mem_ctx The parent TALLOC memory context. + @param gensec_security Returned GENSEC context pointer. + @note The mem_ctx is only a parent and may be NULL. +*/ +static NTSTATUS gensec_start(TALLOC_CTX *mem_ctx, struct gensec_security **gensec_security) +{ + (*gensec_security) = talloc(mem_ctx, struct gensec_security); + if (!(*gensec_security)) { + return NT_STATUS_NO_MEMORY; + } + + (*gensec_security)->ops = NULL; + + ZERO_STRUCT((*gensec_security)->target); + + (*gensec_security)->subcontext = False; + (*gensec_security)->want_features = 0; + return NT_STATUS_OK; +} + +/** + * Start a GENSEC subcontext, with a copy of the properties of the parent + * @param mem_ctx The parent TALLOC memory context. + * @param parent The parent GENSEC context + * @param gensec_security Returned GENSEC context pointer. + * @note Used by SPNEGO in particular, for the actual implementation mechanism + */ + +NTSTATUS gensec_subcontext_start(TALLOC_CTX *mem_ctx, + struct gensec_security *parent, + struct gensec_security **gensec_security) +{ + (*gensec_security) = talloc(mem_ctx, struct gensec_security); + if (!(*gensec_security)) { + return NT_STATUS_NO_MEMORY; + } + + (**gensec_security) = *parent; + (*gensec_security)->ops = NULL; + (*gensec_security)->private_data = NULL; + + (*gensec_security)->subcontext = True; + + return NT_STATUS_OK; +} + +/** + Start the GENSEC system, in client mode, returning a context pointer. + @param mem_ctx The parent TALLOC memory context. + @param gensec_security Returned GENSEC context pointer. + @note The mem_ctx is only a parent and may be NULL. +*/ +NTSTATUS gensec_client_start(TALLOC_CTX *mem_ctx, struct gensec_security **gensec_security) +{ + NTSTATUS status; + status = gensec_start(mem_ctx, gensec_security); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + (*gensec_security)->gensec_role = GENSEC_CLIENT; + (*gensec_security)->password_callback = NULL; + + return status; +} + +/** + Start the GENSEC system, in server mode, returning a context pointer. + @param mem_ctx The parent TALLOC memory context. + @param gensec_security Returned GENSEC context pointer. + @note The mem_ctx is only a parent and may be NULL. +*/ +NTSTATUS gensec_server_start(TALLOC_CTX *mem_ctx, struct gensec_security **gensec_security) +{ + NTSTATUS status; + status = gensec_start(mem_ctx, gensec_security); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + (*gensec_security)->gensec_role = GENSEC_SERVER; + + return status; +} + +static NTSTATUS gensec_start_mech(struct gensec_security *gensec_security) +{ + NTSTATUS status; + DEBUG(5, ("Starting GENSEC %smechanism %s\n", + gensec_security->subcontext ? "sub" : "", + gensec_security->ops->name)); + switch (gensec_security->gensec_role) { + case GENSEC_CLIENT: + if (gensec_security->ops->client_start) { + status = gensec_security->ops->client_start(gensec_security); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start GENSEC client mech %s: %s\n", + gensec_security->ops->name, nt_errstr(status))); + } + return status; + } + case GENSEC_SERVER: + if (gensec_security->ops->server_start) { + status = gensec_security->ops->server_start(gensec_security); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start GENSEC server mech %s: %s\n", + gensec_security->ops->name, nt_errstr(status))); + } + return status; + } + } + return NT_STATUS_INVALID_PARAMETER; +} + +/** + * Start a GENSEC sub-mechanism by DCERPC allocated 'auth type' number + * @param gensec_security GENSEC context pointer. + * @param auth_type DCERPC auth type + * @param auth_level DCERPC auth level + */ + +NTSTATUS gensec_start_mech_by_authtype(struct gensec_security *gensec_security, + uint8_t auth_type, uint8_t auth_level) +{ + gensec_security->ops = gensec_security_by_authtype(auth_type); + if (!gensec_security->ops) { + DEBUG(3, ("Could not find GENSEC backend for auth_type=%d\n", (int)auth_type)); + return NT_STATUS_INVALID_PARAMETER; + } + gensec_want_feature(gensec_security, GENSEC_FEATURE_DCE_STYLE); + if (auth_level == DCERPC_AUTH_LEVEL_INTEGRITY) { + gensec_want_feature(gensec_security, GENSEC_FEATURE_SIGN); + } + if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { + gensec_want_feature(gensec_security, GENSEC_FEATURE_SIGN); + gensec_want_feature(gensec_security, GENSEC_FEATURE_SEAL); + } + + return gensec_start_mech(gensec_security); +} + +const char *gensec_get_name_by_authtype(uint8_t authtype) +{ + const struct gensec_security_ops *ops; + ops = gensec_security_by_authtype(authtype); + if (ops) { + return ops->name; + } + return NULL; +} + + +const char *gensec_get_name_by_oid(const char *oid_string) +{ + const struct gensec_security_ops *ops; + ops = gensec_security_by_oid(oid_string); + if (ops) { + return ops->name; + } + return NULL; +} + + +/** + * Start a GENSEC sub-mechanism by OID, used in SPNEGO + * + * @note This should also be used when you wish to just start NLTMSSP (for example), as it uses a + * well-known #define to hook it in. + */ + +NTSTATUS gensec_start_mech_by_oid(struct gensec_security *gensec_security, + const char *mech_oid) +{ + gensec_security->ops = gensec_security_by_oid(mech_oid); + if (!gensec_security->ops) { + DEBUG(3, ("Could not find GENSEC backend for oid=%s\n", mech_oid)); + return NT_STATUS_INVALID_PARAMETER; + } + return gensec_start_mech(gensec_security); +} + +/** + * Start a GENSEC sub-mechanism by a well know SASL name + * + */ + +NTSTATUS gensec_start_mech_by_sasl_name(struct gensec_security *gensec_security, + const char *sasl_name) +{ + gensec_security->ops = gensec_security_by_sasl_name(sasl_name); + if (!gensec_security->ops) { + DEBUG(3, ("Could not find GENSEC backend for sasl_name=%s\n", sasl_name)); + return NT_STATUS_INVALID_PARAMETER; + } + return gensec_start_mech(gensec_security); +} + +/* + wrappers for the gensec function pointers +*/ +NTSTATUS gensec_unseal_packet(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + uint8_t *data, size_t length, + const uint8_t *whole_pdu, size_t pdu_length, + DATA_BLOB *sig) +{ + if (!gensec_security->ops->unseal_packet) { + return NT_STATUS_NOT_IMPLEMENTED; + } + if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { + if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { + return gensec_check_packet(gensec_security, mem_ctx, + data, length, + whole_pdu, pdu_length, + sig); + } + return NT_STATUS_INVALID_PARAMETER; + } + + return gensec_security->ops->unseal_packet(gensec_security, mem_ctx, + data, length, + whole_pdu, pdu_length, + sig); +} + +NTSTATUS gensec_check_packet(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const uint8_t *data, size_t length, + const uint8_t *whole_pdu, size_t pdu_length, + const DATA_BLOB *sig) +{ + if (!gensec_security->ops->check_packet) { + return NT_STATUS_NOT_IMPLEMENTED; + } + if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { + return NT_STATUS_INVALID_PARAMETER; + } + + return gensec_security->ops->check_packet(gensec_security, mem_ctx, data, length, whole_pdu, pdu_length, sig); +} + +NTSTATUS gensec_seal_packet(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + uint8_t *data, size_t length, + const uint8_t *whole_pdu, size_t pdu_length, + DATA_BLOB *sig) +{ + if (!gensec_security->ops->seal_packet) { + return NT_STATUS_NOT_IMPLEMENTED; + } + if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { + if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { + return gensec_sign_packet(gensec_security, mem_ctx, + data, length, + whole_pdu, pdu_length, + sig); + } + return NT_STATUS_INVALID_PARAMETER; + } + + return gensec_security->ops->seal_packet(gensec_security, mem_ctx, data, length, whole_pdu, pdu_length, sig); +} + +NTSTATUS gensec_sign_packet(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const uint8_t *data, size_t length, + const uint8_t *whole_pdu, size_t pdu_length, + DATA_BLOB *sig) +{ + if (!gensec_security->ops->sign_packet) { + return NT_STATUS_NOT_IMPLEMENTED; + } + if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { + return NT_STATUS_INVALID_PARAMETER; + } + + return gensec_security->ops->sign_packet(gensec_security, mem_ctx, data, length, whole_pdu, pdu_length, sig); +} + +size_t gensec_sig_size(struct gensec_security *gensec_security) +{ + if (!gensec_security->ops->sig_size) { + return 0; + } + if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { + return 0; + } + + return gensec_security->ops->sig_size(gensec_security); +} + +NTSTATUS gensec_wrap(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const DATA_BLOB *in, + DATA_BLOB *out) +{ + if (!gensec_security->ops->wrap) { + return NT_STATUS_NOT_IMPLEMENTED; + } + return gensec_security->ops->wrap(gensec_security, mem_ctx, in, out); +} + +NTSTATUS gensec_unwrap(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const DATA_BLOB *in, + DATA_BLOB *out) +{ + if (!gensec_security->ops->unwrap) { + return NT_STATUS_NOT_IMPLEMENTED; + } + return gensec_security->ops->unwrap(gensec_security, mem_ctx, in, out); +} + +NTSTATUS gensec_session_key(struct gensec_security *gensec_security, + DATA_BLOB *session_key) +{ + if (!gensec_security->ops->session_key) { + return NT_STATUS_NOT_IMPLEMENTED; + } + return gensec_security->ops->session_key(gensec_security, session_key); +} + +/** + * Return the credentials of a logged on user, including session keys + * etc. + * + * Only valid after a successful authentication + * + * May only be called once per authentication. + * + */ + +NTSTATUS gensec_session_info(struct gensec_security *gensec_security, + struct auth_session_info **session_info) +{ + if (!gensec_security->ops->session_info) { + return NT_STATUS_NOT_IMPLEMENTED; + } + return gensec_security->ops->session_info(gensec_security, session_info); +} + +/** + * Next state function for the GENSEC state machine + * + * @param gensec_security GENSEC State + * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on + * @param in The request, as a DATA_BLOB + * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx + * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent, + * or NT_STATUS_OK if the user is authenticated. + */ + +NTSTATUS gensec_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, + const DATA_BLOB in, DATA_BLOB *out) +{ + return gensec_security->ops->update(gensec_security, out_mem_ctx, in, out); +} + +/** + * Set the requirement for a certain feature on the connection + * + */ + +void gensec_want_feature(struct gensec_security *gensec_security, + uint32_t feature) +{ + gensec_security->want_features |= feature; +} + +/** + * Check the requirement for a certain feature on the connection + * + */ + +BOOL gensec_have_feature(struct gensec_security *gensec_security, + uint32_t feature) +{ + if (!gensec_security->ops->have_feature) { + return False; + } + return gensec_security->ops->have_feature(gensec_security, feature); +} + +/** + * Associate a credentails structure with a GENSEC context - talloc_reference()s it to the context + * + */ + +NTSTATUS gensec_set_credentials(struct gensec_security *gensec_security, struct cli_credentials *credentials) +{ + gensec_security->credentials = talloc_reference(gensec_security, credentials); + return NT_STATUS_OK; +} + +/** + * Return the credentails structure associated with a GENSEC context + * + */ + +struct cli_credentials *gensec_get_credentials(struct gensec_security *gensec_security) +{ + return gensec_security->credentials; +} + +/** + * Set the target service (such as 'http' or 'host') on a GENSEC context - ensures it is talloc()ed + * + */ + +NTSTATUS gensec_set_target_service(struct gensec_security *gensec_security, const char *service) +{ + gensec_security->target.service = talloc_strdup(gensec_security, service); + if (!gensec_security->target.service) { + return NT_STATUS_NO_MEMORY; + } + return NT_STATUS_OK; +} + +/** + * Set the target hostname (suitable for kerberos resolutation) on a GENSEC context - ensures it is talloc()ed + * + */ + +NTSTATUS gensec_set_target_hostname(struct gensec_security *gensec_security, const char *hostname) +{ + gensec_security->target.hostname = talloc_strdup(gensec_security, hostname); + if (!gensec_security->target.hostname) { + return NT_STATUS_NO_MEMORY; + } + return NT_STATUS_OK; +} + +const char *gensec_get_target_hostname(struct gensec_security *gensec_security) +{ + if (gensec_security->target.hostname) { + return gensec_security->target.hostname; + } + + /* TODO: Add a 'set sockaddr' call, and do a reverse lookup */ + return NULL; +} + +const char *gensec_get_target_service(struct gensec_security *gensec_security) +{ + if (gensec_security->target.service) { + return gensec_security->target.service; + } + + return "host"; +} + +/* + register a GENSEC backend. + + The 'name' can be later used by other backends to find the operations + structure for this backend. +*/ +NTSTATUS gensec_register(const void *_ops) +{ + const struct gensec_security_ops *ops = _ops; + + if (!lp_parm_bool(-1, "gensec", ops->name, ops->enabled)) { + DEBUG(2,("gensec subsystem %s is disabled\n", ops->name)); + return NT_STATUS_OK; + } + + if (gensec_security_by_name(ops->name) != NULL) { + /* its already registered! */ + DEBUG(0,("GENSEC backend '%s' already registered\n", + ops->name)); + return NT_STATUS_OBJECT_NAME_COLLISION; + } + + generic_security_ops = realloc_p(generic_security_ops, + const struct gensec_security_ops *, + gensec_num_backends+1); + if (!generic_security_ops) { + smb_panic("out of memory in gensec_register"); + } + + generic_security_ops[gensec_num_backends] = ops; + + gensec_num_backends++; + + DEBUG(3,("GENSEC backend '%s' registered\n", + ops->name)); + + return NT_STATUS_OK; +} + +/* + return the GENSEC interface version, and the size of some critical types + This can be used by backends to either detect compilation errors, or provide + multiple implementations for different smbd compilation options in one module +*/ +const struct gensec_critical_sizes *gensec_interface_version(void) +{ + static const struct gensec_critical_sizes critical_sizes = { + GENSEC_INTERFACE_VERSION, + sizeof(struct gensec_security_ops), + sizeof(struct gensec_security), + }; + + return &critical_sizes; +} + +/* + initialise the GENSEC subsystem +*/ +NTSTATUS gensec_init(void) +{ + return NT_STATUS_OK; +} diff --git a/source4/auth/gensec/gensec.h b/source4/auth/gensec/gensec.h new file mode 100644 index 0000000000..91c817d48a --- /dev/null +++ b/source4/auth/gensec/gensec.h @@ -0,0 +1,117 @@ +/* + Unix SMB/CIFS implementation. + + Generic Authentication Interface + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Andrew Bartlett 2004-2005 + + 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. +*/ + +#define GENSEC_OID_NTLMSSP "1 3 6 1 4 1 311 2 2 10" +#define GENSEC_OID_SPNEGO "1 3 6 1 5 5 2" +#define GENSEC_OID_KERBEROS5 "1 2 840 113554 1 2 2" +#define GENSEC_OID_KERBEROS5_OLD "1 2 840 48018 1 2 2" +#define GENSEC_OID_KERBEROS5_USER2USER "1 2 840 113554 1 2 2 3" + +struct gensec_security; +struct gensec_target { + const char *principal; + const char *hostname; + const struct sock_addr *addr; + const char *service; +}; + +#define GENSEC_FEATURE_SESSION_KEY 0x00000001 +#define GENSEC_FEATURE_SIGN 0x00000002 +#define GENSEC_FEATURE_SEAL 0x00000004 +#define GENSEC_FEATURE_DCE_STYLE 0x00000008 + +/* GENSEC mode */ +enum gensec_role +{ + GENSEC_SERVER, + GENSEC_CLIENT +}; + +struct auth_session_info; + +struct gensec_security_ops { + const char *name; + const char *sasl_name; + uint8_t auth_type; /* 0 if not offered on DCE-RPC */ + const char *oid; /* NULL if not offered by SPNEGO */ + 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_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx, + uint8_t *data, size_t length, + const uint8_t *whole_pdu, size_t pdu_length, + DATA_BLOB *sig); + NTSTATUS (*sign_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx, + const uint8_t *data, size_t length, + const uint8_t *whole_pdu, size_t pdu_length, + DATA_BLOB *sig); + size_t (*sig_size)(struct gensec_security *gensec_security); + NTSTATUS (*check_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx, + const uint8_t *data, size_t length, + const uint8_t *whole_pdu, size_t pdu_length, + const DATA_BLOB *sig); + NTSTATUS (*unseal_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx, + uint8_t *data, size_t length, + const uint8_t *whole_pdu, size_t pdu_length, + DATA_BLOB *sig); + NTSTATUS (*wrap)(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const DATA_BLOB *in, + DATA_BLOB *out); + NTSTATUS (*unwrap)(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const DATA_BLOB *in, + DATA_BLOB *out); + NTSTATUS (*session_key)(struct gensec_security *gensec_security, DATA_BLOB *session_key); + NTSTATUS (*session_info)(struct gensec_security *gensec_security, + struct auth_session_info **session_info); + BOOL (*have_feature)(struct gensec_security *gensec_security, + uint32_t feature); + BOOL enabled; +}; + +#define GENSEC_INTERFACE_VERSION 0 + +struct gensec_security { + gensec_password_callback password_callback; + void *password_callback_private; + const struct gensec_security_ops *ops; + void *private_data; + struct cli_credentials *credentials; + struct gensec_target target; + enum gensec_role gensec_role; + BOOL subcontext; + uint32_t want_features; +}; + +/* this structure is used by backends to determine the size of some critical types */ +struct gensec_critical_sizes { + int interface_version; + int sizeof_gensec_security_ops; + int sizeof_gensec_security; +}; + + +/* pre-declare schannel structure for schannel backend */ +struct schannel_state; diff --git a/source4/auth/gensec/gensec.m4 b/source4/auth/gensec/gensec.m4 new file mode 100644 index 0000000000..1af0a1d9c8 --- /dev/null +++ b/source4/auth/gensec/gensec.m4 @@ -0,0 +1,12 @@ +SMB_MODULE_DEFAULT(gensec_krb5, NOT) +SMB_MODULE_DEFAULT(gensec_gssapi, NOT) +SMB_MODULE_DEFAULT(gensec_gsskrb5, NOT) + +if test x"$SMB_EXT_LIB_ENABLE_KRB5" = x"YES"; then + # krb5 is now disabled at runtime, not build time + SMB_MODULE_DEFAULT(gensec_krb5, STATIC) + SMB_MODULE_DEFAULT(gensec_gssapi, STATIC) + if test x"$samba_cv_GSS_C_DCE_STYLE" = x"yes"; then + SMB_MODULE_DEFAULT(gensec_gsskrb5, STATIC) + fi +fi diff --git a/source4/auth/gensec/gensec.mk b/source4/auth/gensec/gensec.mk new file mode 100644 index 0000000000..8ed6f7c840 --- /dev/null +++ b/source4/auth/gensec/gensec.mk @@ -0,0 +1,86 @@ +################################# +# Start SUBSYSTEM GENSEC +[SUBSYSTEM::GENSEC] +INIT_FUNCTION = gensec_init +INIT_OBJ_FILES = auth/gensec/gensec.o +REQUIRED_SUBSYSTEMS = \ + SCHANNELDB +# End SUBSYSTEM GENSEC +################################# + +################################################ +# Start MODULE gensec_krb5 +[MODULE::gensec_krb5] +SUBSYSTEM = GENSEC +INIT_FUNCTION = gensec_krb5_init +INIT_OBJ_FILES = auth/gensec/gensec_krb5.o +REQUIRED_SUBSYSTEMS = NDR_KRB5PAC KERBEROS EXT_LIB_KRB5 +# End MODULE gensec_krb5 +################################################ + +################################################ +# Start MODULE gensec_gssapi +[MODULE::gensec_gssapi] +SUBSYSTEM = GENSEC +INIT_FUNCTION = gensec_gssapi_init +INIT_OBJ_FILES = auth/gensec/gensec_gssapi.o +REQUIRED_SUBSYSTEMS = EXT_LIB_KRB5 +# End MODULE gensec_gssapi +################################################ + +################################################ +# Start MODULE gensec_gsskrb5 +[MODULE::gensec_gsskrb5] +SUBSYSTEM = GENSEC +INIT_FUNCTION = gensec_gsskrb5_init +INIT_OBJ_FILES = auth/gensec/gensec_gsskrb5.o +REQUIRED_SUBSYSTEMS = EXT_LIB_KRB5 +# End MODULE gensec_gsskrb5 +################################################ + +################################################ +# Start MODULE gensec_spnego +[MODULE::gensec_spnego] +SUBSYSTEM = GENSEC +INIT_FUNCTION = gensec_spnego_init +INIT_OBJ_FILES = auth/gensec/spnego.o +ADD_OBJ_FILES = \ + auth/gensec/spnego_parse.o +# End MODULE gensec_spnego +################################################ + +################################################ +# Start MODULE gensec_ntlmssp +[MODULE::gensec_ntlmssp] +SUBSYSTEM = GENSEC +INIT_FUNCTION = gensec_ntlmssp_init +INIT_OBJ_FILES = auth/gensec/gensec_ntlmssp.o +ADD_OBJ_FILES = \ + auth/gensec/ntlmssp.o \ + auth/gensec/ntlmssp_parse.o \ + auth/gensec/ntlmssp_sign.o +REQUIRED_SUBSYSTEMS = AUTH +# End MODULE gensec_ntlmssp +################################################ + +################################################ +# Start MODULE gensec_schannel +[MODULE::gensec_schannel] +SUBSYSTEM = GENSEC +INIT_FUNCTION = gensec_schannel_init +INIT_OBJ_FILES = auth/gensec/schannel.o +ADD_OBJ_FILES = \ + auth/gensec/schannel_sign.o +REQUIRED_SUBSYSTEMS = AUTH SCHANNELDB +# End MODULE gensec_ntlmssp +################################################ + +################################################ +# Start SUBSYSTEM SCHANNELDB +[SUBSYSTEM::SCHANNELDB] +INIT_OBJ_FILES = \ + auth/gensec/schannel_state.o +# +# End SUBSYSTEM SCHANNELDB +################################################ + diff --git a/source4/auth/gensec/gensec_gssapi.c b/source4/auth/gensec/gensec_gssapi.c new file mode 100644 index 0000000000..c974b93952 --- /dev/null +++ b/source4/auth/gensec/gensec_gssapi.c @@ -0,0 +1,376 @@ +/* + Unix SMB/CIFS implementation. + + Kerberos backend for GENSEC + + Copyright (C) Andrew Bartlett 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "system/kerberos.h" +#include "auth/auth.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_AUTH + +struct gensec_gssapi_state { + gss_ctx_id_t gssapi_context; + struct gss_channel_bindings_struct *input_chan_bindings; + gss_name_t server_name; + gss_name_t client_name; + OM_uint32 want_flags, got_flags; + const gss_OID_desc *gss_oid; +}; +static int gensec_gssapi_destory(void *ptr) +{ + struct gensec_gssapi_state *gensec_gssapi_state = ptr; + OM_uint32 maj_stat, min_stat; + + if (gensec_gssapi_state->gssapi_context != GSS_C_NO_CONTEXT) { + maj_stat = gss_delete_sec_context (&min_stat, + &gensec_gssapi_state->gssapi_context, + GSS_C_NO_BUFFER); + } + + if (gensec_gssapi_state->server_name != GSS_C_NO_NAME) { + maj_stat = gss_release_name(&min_stat, &gensec_gssapi_state->server_name); + } + if (gensec_gssapi_state->client_name != GSS_C_NO_NAME) { + maj_stat = gss_release_name(&min_stat, &gensec_gssapi_state->client_name); + } + return 0; +} + +static NTSTATUS gensec_gssapi_start(struct gensec_security *gensec_security) +{ + struct gensec_gssapi_state *gensec_gssapi_state; + + gensec_gssapi_state = talloc(gensec_security, struct gensec_gssapi_state); + if (!gensec_gssapi_state) { + return NT_STATUS_NO_MEMORY; + } + + gensec_security->private_data = gensec_gssapi_state; + + gensec_gssapi_state->gssapi_context = GSS_C_NO_CONTEXT; + gensec_gssapi_state->server_name = GSS_C_NO_NAME; + gensec_gssapi_state->client_name = GSS_C_NO_NAME; + + talloc_set_destructor(gensec_gssapi_state, gensec_gssapi_destory); + + /* TODO: Fill in channel bindings */ + gensec_gssapi_state->input_chan_bindings = GSS_C_NO_CHANNEL_BINDINGS; + + gensec_gssapi_state->want_flags = 0; + gensec_gssapi_state->got_flags = 0; + + if (gensec_security->want_features & GENSEC_FEATURE_SESSION_KEY) { + /* GSSAPI won't give us the session keys */ + return NT_STATUS_INVALID_PARAMETER; + } + if (gensec_security->want_features & GENSEC_FEATURE_SIGN) { + gensec_gssapi_state->want_flags |= GSS_C_INTEG_FLAG; + } + if (gensec_security->want_features & GENSEC_FEATURE_SEAL) { + gensec_gssapi_state->want_flags |= GSS_C_CONF_FLAG; + } + + if (strcmp(gensec_security->ops->oid, GENSEC_OID_KERBEROS5) == 0) { + static const gss_OID_desc gensec_gss_krb5_mechanism_oid_desc = + {9, (void *)discard_const_p(char, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02")}; + + gensec_gssapi_state->gss_oid = &gensec_gss_krb5_mechanism_oid_desc; + } else if (strcmp(gensec_security->ops->oid, GENSEC_OID_SPNEGO) == 0) { + static const gss_OID_desc gensec_gss_spnego_mechanism_oid_desc = + {6, (void *)discard_const_p(char, "\x2b\x06\x01\x05\x05\x02")}; + gensec_gssapi_state->gss_oid = &gensec_gss_spnego_mechanism_oid_desc; + } else { + return NT_STATUS_INVALID_PARAMETER; + } + + return NT_STATUS_OK; +} + +static NTSTATUS gensec_gssapi_server_start(struct gensec_security *gensec_security) +{ + NTSTATUS nt_status; + struct gensec_gssapi_state *gensec_gssapi_state; + + nt_status = gensec_gssapi_start(gensec_security); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + gensec_gssapi_state = gensec_security->private_data; + + return NT_STATUS_OK; +} + +static NTSTATUS gensec_gssapi_client_start(struct gensec_security *gensec_security) +{ + struct gensec_gssapi_state *gensec_gssapi_state; + NTSTATUS nt_status; + gss_buffer_desc name_token; + OM_uint32 maj_stat, min_stat; + + gss_OID_desc hostbased = {10, + (void *)discard_const_p(char, "\x2a\x86\x48\x86\xf7\x12" + "\x01\x02\x01\x04")}; + + nt_status = gensec_gssapi_start(gensec_security); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + gensec_gssapi_state = gensec_security->private_data; + + name_token.value = talloc_asprintf(gensec_gssapi_state, "%s@%s", gensec_get_target_service(gensec_security), + gensec_get_target_hostname(gensec_security)); + DEBUG(0, ("name: %s\n", (char *)name_token.value)); + name_token.length = strlen(name_token.value); + + maj_stat = gss_import_name (&min_stat, + &name_token, + &hostbased, + &gensec_gssapi_state->server_name); + + + if (maj_stat) { + return NT_STATUS_UNSUCCESSFUL; + } + return NT_STATUS_OK; +} + + + +/** + * Next state function for the GSSAPI GENSEC mechanism + * + * @param gensec_gssapi_state GSSAPI State + * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on + * @param in The request, as a DATA_BLOB + * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx + * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent, + * or NT_STATUS_OK if the user is authenticated. + */ + +static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security, + TALLOC_CTX *out_mem_ctx, + const DATA_BLOB in, DATA_BLOB *out) +{ + struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data; + NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; + OM_uint32 maj_stat, min_stat; + OM_uint32 min_stat2; + gss_buffer_desc input_token, output_token; + gss_OID gss_oid_p; + input_token.length = in.length; + input_token.value = in.data; + + switch (gensec_security->gensec_role) { + case GENSEC_CLIENT: + { + maj_stat = gss_init_sec_context(&min_stat, + GSS_C_NO_CREDENTIAL, + &gensec_gssapi_state->gssapi_context, + gensec_gssapi_state->server_name, + discard_const_p(gss_OID_desc, gensec_gssapi_state->gss_oid), + gensec_gssapi_state->want_flags, + 0, + gensec_gssapi_state->input_chan_bindings, + &input_token, + NULL, + &output_token, + &gensec_gssapi_state->got_flags, /* ret flags */ + NULL); + break; + } + case GENSEC_SERVER: + { + maj_stat = gss_accept_sec_context(&min_stat, + &gensec_gssapi_state->gssapi_context, + GSS_C_NO_CREDENTIAL, + &input_token, + gensec_gssapi_state->input_chan_bindings, + &gensec_gssapi_state->client_name, + &gss_oid_p, + &output_token, + &gensec_gssapi_state->got_flags, + NULL, + NULL); + gensec_gssapi_state->gss_oid = gss_oid_p; + break; + } + default: + return NT_STATUS_INVALID_PARAMETER; + + } + + *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length); + gss_release_buffer(&min_stat2, &output_token); + + if (maj_stat == GSS_S_COMPLETE) { + return NT_STATUS_OK; + } else if (maj_stat == GSS_S_CONTINUE_NEEDED) { + return NT_STATUS_MORE_PROCESSING_REQUIRED; + } else { + gss_buffer_desc msg1, msg2; + OM_uint32 msg_ctx = 0; + + msg1.value = NULL; + msg2.value = NULL; + gss_display_status(&min_stat2, maj_stat, GSS_C_GSS_CODE, + GSS_C_NULL_OID, &msg_ctx, &msg1); + gss_display_status(&min_stat2, min_stat, GSS_C_MECH_CODE, + GSS_C_NULL_OID, &msg_ctx, &msg2); + DEBUG(1, ("gensec_gssapi_update: %s : %s\n", (char *)msg1.value, (char *)msg2.value)); + gss_release_buffer(&min_stat2, &msg1); + gss_release_buffer(&min_stat2, &msg2); + + return nt_status; + } + +} + +static NTSTATUS gensec_gssapi_wrap(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const DATA_BLOB *in, + DATA_BLOB *out) +{ + struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data; + OM_uint32 maj_stat, min_stat; + gss_buffer_desc input_token, output_token; + int conf_state; + input_token.length = in->length; + input_token.value = in->data; + + maj_stat = gss_wrap(&min_stat, + gensec_gssapi_state->gssapi_context, + gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL), + GSS_C_QOP_DEFAULT, + &input_token, + &conf_state, + &output_token); + if (GSS_ERROR(maj_stat)) { + return NT_STATUS_ACCESS_DENIED; + } + *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length); + + gss_release_buffer(&min_stat, &output_token); + + if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL) + && !conf_state) { + return NT_STATUS_ACCESS_DENIED; + } + return NT_STATUS_OK; +} + +static NTSTATUS gensec_gssapi_unwrap(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const DATA_BLOB *in, + DATA_BLOB *out) +{ + struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data; + OM_uint32 maj_stat, min_stat; + gss_buffer_desc input_token, output_token; + int conf_state; + gss_qop_t qop_state; + input_token.length = in->length; + input_token.value = in->data; + + maj_stat = gss_unwrap(&min_stat, + gensec_gssapi_state->gssapi_context, + &input_token, + &output_token, + &conf_state, + &qop_state); + if (GSS_ERROR(maj_stat)) { + return NT_STATUS_ACCESS_DENIED; + } + *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length); + + gss_release_buffer(&min_stat, &output_token); + + if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL) + && !conf_state) { + return NT_STATUS_ACCESS_DENIED; + } + return NT_STATUS_OK; +} + +static BOOL gensec_gssapi_have_feature(struct gensec_security *gensec_security, + uint32_t feature) +{ + struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data; + if (feature & GENSEC_FEATURE_SIGN) { + return gensec_gssapi_state->got_flags & GSS_C_INTEG_FLAG; + } + if (feature & GENSEC_FEATURE_SEAL) { + return gensec_gssapi_state->got_flags & GSS_C_CONF_FLAG; + } + return False; +} + +/* As a server, this could in theory accept any GSSAPI mech */ +static const struct gensec_security_ops gensec_gssapi_krb5_security_ops = { + .name = "gssapi_krb5", + .sasl_name = "GSSAPI", + .oid = GENSEC_OID_KERBEROS5, + .client_start = gensec_gssapi_client_start, + .server_start = gensec_gssapi_server_start, + .update = gensec_gssapi_update, + .wrap = gensec_gssapi_wrap, + .unwrap = gensec_gssapi_unwrap, + .have_feature = gensec_gssapi_have_feature, + .enabled = False + +}; + +static const struct gensec_security_ops gensec_gssapi_spnego_security_ops = { + .name = "gssapi_spnego", + .sasl_name = "GSS-SPNEGO", + .oid = GENSEC_OID_SPNEGO, + .client_start = gensec_gssapi_client_start, + .server_start = gensec_gssapi_server_start, + .update = gensec_gssapi_update, + .wrap = gensec_gssapi_wrap, + .unwrap = gensec_gssapi_unwrap, + .have_feature = gensec_gssapi_have_feature, + .enabled = False +}; + +NTSTATUS gensec_gssapi_init(void) +{ + NTSTATUS ret; + + ret = gensec_register(&gensec_gssapi_krb5_security_ops); + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(0,("Failed to register '%s' gensec backend!\n", + gensec_gssapi_krb5_security_ops.name)); + return ret; + } + + ret = gensec_register(&gensec_gssapi_spnego_security_ops); + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(0,("Failed to register '%s' gensec backend!\n", + gensec_gssapi_spnego_security_ops.name)); + return ret; + } + + return ret; +} diff --git a/source4/auth/gensec/gensec_gsskrb5.c b/source4/auth/gensec/gensec_gsskrb5.c new file mode 100644 index 0000000000..77e077276b --- /dev/null +++ b/source4/auth/gensec/gensec_gsskrb5.c @@ -0,0 +1,584 @@ +/* + Unix SMB/CIFS implementation. + + GSSAPI KRB5 backend for GENSEC + + Copyright (C) Andrew Bartlett 2004 + Copyright (C) Stefan Metzmacher 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "system/kerberos.h" +#include "system/time.h" +#include "libcli/auth/kerberos.h" +#include "auth/auth.h" + +static const gss_OID_desc gensec_gss_krb5_mechanism_oid_desc = + {9, (void *)discard_const_p(char, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02")}; + +enum GENSEC_GSSKRB5_STATE { + GENSEC_GSSKRB5_CLIENT_START, + GENSEC_GSSKRB5_CLIENT_MUTUAL_AUTH, + GENSEC_GSSKRB5_CLIENT_DCE_STYLE, + GENSEC_GSSKRB5_DONE +}; + +struct gensec_gsskrb5_state { + enum GENSEC_GSSKRB5_STATE state_position; + gss_ctx_id_t gssapi_context; + struct gss_channel_bindings_struct *input_chan_bindings; + gss_name_t server_name; + gss_name_t client_name; + OM_uint32 want_flags, got_flags; +}; + +static int gensec_gsskrb5_destory(void *ptr) +{ + struct gensec_gsskrb5_state *gensec_gsskrb5_state = ptr; + OM_uint32 maj_stat, min_stat; + + if (gensec_gsskrb5_state->gssapi_context != GSS_C_NO_CONTEXT) { + maj_stat = gss_delete_sec_context (&min_stat, + &gensec_gsskrb5_state->gssapi_context, + GSS_C_NO_BUFFER); + } + + if (gensec_gsskrb5_state->server_name != GSS_C_NO_NAME) { + maj_stat = gss_release_name(&min_stat, &gensec_gsskrb5_state->server_name); + } + if (gensec_gsskrb5_state->client_name != GSS_C_NO_NAME) { + maj_stat = gss_release_name(&min_stat, &gensec_gsskrb5_state->client_name); + } + return 0; +} + +static NTSTATUS gensec_gsskrb5_start(struct gensec_security *gensec_security) +{ + struct gensec_gsskrb5_state *gensec_gsskrb5_state; + + gensec_gsskrb5_state = talloc(gensec_security, struct gensec_gsskrb5_state); + if (!gensec_gsskrb5_state) { + return NT_STATUS_NO_MEMORY; + } + + gensec_security->private_data = gensec_gsskrb5_state; + + gensec_gsskrb5_state->gssapi_context = GSS_C_NO_CONTEXT; + gensec_gsskrb5_state->server_name = GSS_C_NO_NAME; + gensec_gsskrb5_state->client_name = GSS_C_NO_NAME; + + talloc_set_destructor(gensec_gsskrb5_state, gensec_gsskrb5_destory); + + /* TODO: Fill in channel bindings */ + gensec_gsskrb5_state->input_chan_bindings = GSS_C_NO_CHANNEL_BINDINGS; + + gensec_gsskrb5_state->want_flags = GSS_C_MUTUAL_FLAG; + gensec_gsskrb5_state->got_flags = 0; + + if (gensec_security->want_features & GENSEC_FEATURE_SESSION_KEY) { + /* GSSAPI won't give us the session keys */ + return NT_STATUS_INVALID_PARAMETER; + } + if (gensec_security->want_features & GENSEC_FEATURE_SIGN) { + gensec_gsskrb5_state->want_flags |= GSS_C_INTEG_FLAG; + } + if (gensec_security->want_features & GENSEC_FEATURE_SEAL) { + gensec_gsskrb5_state->want_flags |= GSS_C_CONF_FLAG; + } + if (gensec_security->want_features & GENSEC_FEATURE_DCE_STYLE) { + gensec_gsskrb5_state->want_flags |= GSS_C_DCE_STYLE; + } + + return NT_STATUS_OK; +} + +static NTSTATUS gensec_gsskrb5_client_start(struct gensec_security *gensec_security) +{ + struct gensec_gsskrb5_state *gensec_gsskrb5_state; + NTSTATUS nt_status; + gss_buffer_desc name_token; + OM_uint32 maj_stat, min_stat; + + gss_OID_desc hostbased = {10, + (void *)discard_const_p(char, "\x2a\x86\x48\x86\xf7\x12" + "\x01\x02\x01\x04")}; + + nt_status = gensec_gsskrb5_start(gensec_security); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + gensec_gsskrb5_state = gensec_security->private_data; + + gensec_gsskrb5_state->state_position = GENSEC_GSSKRB5_CLIENT_START; + + name_token.value = talloc_asprintf(gensec_gsskrb5_state, "%s@%s", gensec_get_target_service(gensec_security), + gensec_get_target_hostname(gensec_security)); + DEBUG(0, ("name: %s\n", (char *)name_token.value)); + name_token.length = strlen(name_token.value); + + maj_stat = gss_import_name (&min_stat, + &name_token, + &hostbased, + &gensec_gsskrb5_state->server_name); + if (maj_stat) { + return NT_STATUS_UNSUCCESSFUL; + } + + return NT_STATUS_OK; +} + +/** + * Next state function for the GSSKRB5 GENSEC mechanism + * + * @param gensec_gsskrb5_state GSSAPI State + * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on + * @param in The request, as a DATA_BLOB + * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx + * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent, + * or NT_STATUS_OK if the user is authenticated. + */ + +static NTSTATUS gensec_gsskrb5_update(struct gensec_security *gensec_security, + TALLOC_CTX *out_mem_ctx, + const DATA_BLOB in, DATA_BLOB *out) +{ + struct gensec_gsskrb5_state *gensec_gsskrb5_state = gensec_security->private_data; + NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; + OM_uint32 maj_stat, min_stat; + OM_uint32 min_stat2; + gss_buffer_desc input_token, output_token; + + *out = data_blob(NULL, 0); + + input_token.length = in.length; + input_token.value = in.data; + + switch (gensec_gsskrb5_state->state_position) { + case GENSEC_GSSKRB5_CLIENT_START: + { + maj_stat = gss_init_sec_context(&min_stat, + GSS_C_NO_CREDENTIAL, + &gensec_gsskrb5_state->gssapi_context, + gensec_gsskrb5_state->server_name, + discard_const_p(gss_OID_desc, &gensec_gss_krb5_mechanism_oid_desc), + gensec_gsskrb5_state->want_flags, + 0, + gensec_gsskrb5_state->input_chan_bindings, + &input_token, + NULL, + &output_token, + &gensec_gsskrb5_state->got_flags, /* ret flags */ + NULL); + *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length); + if (out->length != output_token.length) { + gss_release_buffer(&min_stat2, &output_token); + return NT_STATUS_NO_MEMORY; + } + gss_release_buffer(&min_stat2, &output_token); + + if (maj_stat == GSS_S_COMPLETE) { + gensec_gsskrb5_state->state_position = GENSEC_GSSKRB5_DONE; + return NT_STATUS_OK; + } else if (maj_stat == GSS_S_CONTINUE_NEEDED) { + gensec_gsskrb5_state->state_position = GENSEC_GSSKRB5_CLIENT_MUTUAL_AUTH; + return NT_STATUS_MORE_PROCESSING_REQUIRED; + } else { + gss_buffer_desc msg1, msg2; + OM_uint32 msg_ctx = 0; + + msg1.value = NULL; + msg2.value = NULL; + gss_display_status(&min_stat2, maj_stat, GSS_C_GSS_CODE, + GSS_C_NULL_OID, &msg_ctx, &msg1); + gss_display_status(&min_stat2, min_stat, GSS_C_MECH_CODE, + GSS_C_NULL_OID, &msg_ctx, &msg2); + DEBUG(1, ("gensec_gsskrb5_update: %s : %s\n", (char *)msg1.value, (char *)msg2.value)); + gss_release_buffer(&min_stat2, &msg1); + gss_release_buffer(&min_stat2, &msg2); + + return nt_status; + } + break; + } + case GENSEC_GSSKRB5_CLIENT_MUTUAL_AUTH: + { + maj_stat = gss_init_sec_context(&min_stat, + GSS_C_NO_CREDENTIAL, + &gensec_gsskrb5_state->gssapi_context, + gensec_gsskrb5_state->server_name, + discard_const_p(gss_OID_desc, &gensec_gss_krb5_mechanism_oid_desc), + gensec_gsskrb5_state->want_flags, + 0, + gensec_gsskrb5_state->input_chan_bindings, + &input_token, + NULL, + &output_token, + &gensec_gsskrb5_state->got_flags, /* ret flags */ + NULL); + *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length); + if (out->length != output_token.length) { + gss_release_buffer(&min_stat2, &output_token); + return NT_STATUS_NO_MEMORY; + } + gss_release_buffer(&min_stat2, &output_token); + + if (maj_stat == GSS_S_COMPLETE) { + if (gensec_gsskrb5_state->got_flags & GSS_C_DCE_STYLE) { + gensec_gsskrb5_state->state_position = GENSEC_GSSKRB5_CLIENT_DCE_STYLE; + return NT_STATUS_MORE_PROCESSING_REQUIRED; + } + gensec_gsskrb5_state->state_position = GENSEC_GSSKRB5_DONE; + return NT_STATUS_OK; + } else if (maj_stat == GSS_S_CONTINUE_NEEDED) { + gensec_gsskrb5_state->state_position = GENSEC_GSSKRB5_CLIENT_DCE_STYLE; + return NT_STATUS_MORE_PROCESSING_REQUIRED; + } else { + gss_buffer_desc msg1, msg2; + OM_uint32 msg_ctx = 0; + + msg1.value = NULL; + msg2.value = NULL; + gss_display_status(&min_stat2, maj_stat, GSS_C_GSS_CODE, + GSS_C_NULL_OID, &msg_ctx, &msg1); + gss_display_status(&min_stat2, min_stat, GSS_C_MECH_CODE, + GSS_C_NULL_OID, &msg_ctx, &msg2); + DEBUG(1, ("gensec_gsskrb5_update: %s : %s\n", (char *)msg1.value, (char *)msg2.value)); + gss_release_buffer(&min_stat2, &msg1); + gss_release_buffer(&min_stat2, &msg2); + + return nt_status; + } + break; + } + case GENSEC_GSSKRB5_CLIENT_DCE_STYLE: + { + gensec_gsskrb5_state->state_position = GENSEC_GSSKRB5_DONE; + return NT_STATUS_OK; + } + case GENSEC_GSSKRB5_DONE: + { + return NT_STATUS_OK; + } + default: + return NT_STATUS_INVALID_PARAMETER; + } + + return NT_STATUS_INVALID_PARAMETER; +} + +static NTSTATUS gensec_gsskrb5_wrap(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const DATA_BLOB *in, + DATA_BLOB *out) +{ + struct gensec_gsskrb5_state *gensec_gsskrb5_state = gensec_security->private_data; + OM_uint32 maj_stat, min_stat; + gss_buffer_desc input_token, output_token; + int conf_state; + input_token.length = in->length; + input_token.value = in->data; + + maj_stat = gss_wrap(&min_stat, + gensec_gsskrb5_state->gssapi_context, + gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL), + GSS_C_QOP_DEFAULT, + &input_token, + &conf_state, + &output_token); + if (GSS_ERROR(maj_stat)) { + return NT_STATUS_ACCESS_DENIED; + } + *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length); + + gss_release_buffer(&min_stat, &output_token); + + if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL) + && !conf_state) { + return NT_STATUS_ACCESS_DENIED; + } + return NT_STATUS_OK; +} + +static NTSTATUS gensec_gsskrb5_unwrap(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const DATA_BLOB *in, + DATA_BLOB *out) +{ + struct gensec_gsskrb5_state *gensec_gsskrb5_state = gensec_security->private_data; + OM_uint32 maj_stat, min_stat; + gss_buffer_desc input_token, output_token; + int conf_state; + gss_qop_t qop_state; + input_token.length = in->length; + input_token.value = in->data; + + maj_stat = gss_unwrap(&min_stat, + gensec_gsskrb5_state->gssapi_context, + &input_token, + &output_token, + &conf_state, + &qop_state); + if (GSS_ERROR(maj_stat)) { + return NT_STATUS_ACCESS_DENIED; + } + *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length); + + gss_release_buffer(&min_stat, &output_token); + + if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL) + && !conf_state) { + return NT_STATUS_ACCESS_DENIED; + } + return NT_STATUS_OK; +} + +static size_t gensec_gsskrb5_sig_size(struct gensec_security *gensec_security) +{ + /* not const but work for DCERPC packets and arcfour */ + return 45; +} + +static NTSTATUS gensec_gsskrb5_seal_packet(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + uint8_t *data, size_t length, + const uint8_t *whole_pdu, size_t pdu_length, + DATA_BLOB *sig) +{ + struct gensec_gsskrb5_state *gensec_gsskrb5_state = gensec_security->private_data; + OM_uint32 maj_stat, min_stat; + gss_buffer_desc input_token, output_token; + int conf_state; + ssize_t sig_length = 0; + + input_token.length = length; + input_token.value = data; + + maj_stat = gss_wrap(&min_stat, + gensec_gsskrb5_state->gssapi_context, + gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL), + GSS_C_QOP_DEFAULT, + &input_token, + &conf_state, + &output_token); + if (GSS_ERROR(maj_stat)) { + return NT_STATUS_ACCESS_DENIED; + } + + if (output_token.length < length) { + return NT_STATUS_INTERNAL_ERROR; + } + + sig_length = 45; + + memcpy(data, ((uint8_t *)output_token.value) + sig_length, length); + *sig = data_blob_talloc(mem_ctx, (uint8_t *)output_token.value, sig_length); + +DEBUG(0,("gensec_gsskrb5_seal_packet: siglen: %d inlen: %d, wrap_len: %d\n", sig->length, length, output_token.length - sig_length)); +dump_data(0,sig->data, sig->length); +dump_data(0,data, length); +dump_data(0,((uint8_t *)output_token.value) + sig_length, output_token.length - sig_length); + + gss_release_buffer(&min_stat, &output_token); + + if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL) + && !conf_state) { + return NT_STATUS_ACCESS_DENIED; + } + return NT_STATUS_OK; +} + +static NTSTATUS gensec_gsskrb5_unseal_packet(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + uint8_t *data, size_t length, + const uint8_t *whole_pdu, size_t pdu_length, + DATA_BLOB *sig) +{ + struct gensec_gsskrb5_state *gensec_gsskrb5_state = gensec_security->private_data; + OM_uint32 maj_stat, min_stat; + gss_buffer_desc input_token, output_token; + int conf_state; + gss_qop_t qop_state; + DATA_BLOB in; + +DEBUG(0,("gensec_gsskrb5_unseal_packet: siglen: %d\n", sig->length)); +dump_data(0,sig->data, sig->length); + + in = data_blob_talloc(mem_ctx, NULL, sig->length + length); + + memcpy(in.data, sig->data, sig->length); + memcpy(in.data + sig->length, data, length); + + input_token.length = in.length; + input_token.value = in.data; + + maj_stat = gss_unwrap(&min_stat, + gensec_gsskrb5_state->gssapi_context, + &input_token, + &output_token, + &conf_state, + &qop_state); + if (GSS_ERROR(maj_stat)) { + return NT_STATUS_ACCESS_DENIED; + } + + if (output_token.length != length) { + return NT_STATUS_INTERNAL_ERROR; + } + + memcpy(data, output_token.value, length); + + gss_release_buffer(&min_stat, &output_token); + + if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL) + && !conf_state) { + return NT_STATUS_ACCESS_DENIED; + } + return NT_STATUS_OK; +} + +static NTSTATUS gensec_gsskrb5_sign_packet(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const uint8_t *data, size_t length, + const uint8_t *whole_pdu, size_t pdu_length, + DATA_BLOB *sig) +{ + struct gensec_gsskrb5_state *gensec_gsskrb5_state = gensec_security->private_data; + OM_uint32 maj_stat, min_stat; + gss_buffer_desc input_token, output_token; + int conf_state; + ssize_t sig_length = 0; + + input_token.length = length; + input_token.value = data; + + maj_stat = gss_wrap(&min_stat, + gensec_gsskrb5_state->gssapi_context, + 0, + GSS_C_QOP_DEFAULT, + &input_token, + &conf_state, + &output_token); + if (GSS_ERROR(maj_stat)) { + return NT_STATUS_ACCESS_DENIED; + } + + if (output_token.length < length) { + return NT_STATUS_INTERNAL_ERROR; + } + + sig_length = 45; + + /*memcpy(data, ((uint8_t *)output_token.value) + sig_length, length);*/ + *sig = data_blob_talloc(mem_ctx, (uint8_t *)output_token.value, sig_length); + +DEBUG(0,("gensec_gsskrb5_sign_packet: siglen: %d\n", sig->length)); +dump_data(0,sig->data, sig->length); + + gss_release_buffer(&min_stat, &output_token); + + return NT_STATUS_OK; +} + +static NTSTATUS gensec_gsskrb5_check_packet(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const uint8_t *data, size_t length, + const uint8_t *whole_pdu, size_t pdu_length, + const DATA_BLOB *sig) +{ + struct gensec_gsskrb5_state *gensec_gsskrb5_state = gensec_security->private_data; + OM_uint32 maj_stat, min_stat; + gss_buffer_desc input_token, output_token; + int conf_state; + gss_qop_t qop_state; + DATA_BLOB in; + +DEBUG(0,("gensec_gsskrb5_check_packet: siglen: %d\n", sig->length)); +dump_data(0,sig->data, sig->length); + + in = data_blob_talloc(mem_ctx, NULL, sig->length + length); + + memcpy(in.data, sig->data, sig->length); + memcpy(in.data + sig->length, data, length); + + input_token.length = in.length; + input_token.value = in.data; + + maj_stat = gss_unwrap(&min_stat, + gensec_gsskrb5_state->gssapi_context, + &input_token, + &output_token, + &conf_state, + &qop_state); + if (GSS_ERROR(maj_stat)) { + return NT_STATUS_ACCESS_DENIED; + } + + if (output_token.length != length) { + return NT_STATUS_INTERNAL_ERROR; + } + + /*memcpy(data, output_token.value, length);*/ + + gss_release_buffer(&min_stat, &output_token); + + return NT_STATUS_OK; +} + +static BOOL gensec_gsskrb5_have_feature(struct gensec_security *gensec_security, + uint32_t feature) +{ + struct gensec_gsskrb5_state *gensec_gsskrb5_state = gensec_security->private_data; + if (feature & GENSEC_FEATURE_SIGN) { + return gensec_gsskrb5_state->got_flags & GSS_C_INTEG_FLAG; + } + if (feature & GENSEC_FEATURE_SEAL) { + return gensec_gsskrb5_state->got_flags & GSS_C_CONF_FLAG; + } + return False; +} + +static const struct gensec_security_ops gensec_gsskrb5_security_ops = { + .name = "gsskrb5", + .auth_type = DCERPC_AUTH_TYPE_KRB5, + .oid = GENSEC_OID_KERBEROS5, + .client_start = gensec_gsskrb5_client_start, + .update = gensec_gsskrb5_update, + .sig_size = gensec_gsskrb5_sig_size, + .sign_packet = gensec_gsskrb5_sign_packet, + .check_packet = gensec_gsskrb5_check_packet, + .seal_packet = gensec_gsskrb5_seal_packet, + .unseal_packet = gensec_gsskrb5_unseal_packet, + .wrap = gensec_gsskrb5_wrap, + .unwrap = gensec_gsskrb5_unwrap, + .have_feature = gensec_gsskrb5_have_feature, + .enabled = False +}; + +NTSTATUS gensec_gsskrb5_init(void) +{ + NTSTATUS ret; + + ret = gensec_register(&gensec_gsskrb5_security_ops); + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(0,("Failed to register '%s' gensec backend!\n", + gensec_gsskrb5_security_ops.name)); + return ret; + } + + return ret; +} diff --git a/source4/auth/gensec/gensec_krb5.c b/source4/auth/gensec/gensec_krb5.c new file mode 100644 index 0000000000..bad143f3c8 --- /dev/null +++ b/source4/auth/gensec/gensec_krb5.c @@ -0,0 +1,751 @@ +/* + Unix SMB/CIFS implementation. + + Kerberos backend for GENSEC + + Copyright (C) Andrew Bartlett 2004 + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Luke Howard 2002-2003 + Copyright (C) Stefan Metzmacher 2004-2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "system/kerberos.h" +#include "system/time.h" +#include "auth/kerberos/kerberos.h" +#include "librpc/gen_ndr/ndr_krb5pac.h" +#include "auth/auth.h" + +enum GENSEC_KRB5_STATE { + GENSEC_KRB5_SERVER_START, + GENSEC_KRB5_CLIENT_START, + GENSEC_KRB5_CLIENT_MUTUAL_AUTH, + GENSEC_KRB5_DONE +}; + +struct gensec_krb5_state { + DATA_BLOB session_key; + DATA_BLOB pac; + enum GENSEC_KRB5_STATE state_position; + krb5_context context; + krb5_auth_context auth_context; + krb5_ccache ccache; + krb5_data ticket; + krb5_keyblock keyblock; + char *peer_principal; +}; + +#ifdef KRB5_DO_VERIFY_PAC +static NTSTATUS gensec_krb5_pac_checksum(DATA_BLOB pac_data, + struct PAC_SIGNATURE_DATA *sig, + struct gensec_krb5_state *gensec_krb5_state, + uint32 keyusage) +{ + krb5_error_code ret; + krb5_crypto crypto; + Checksum cksum; + int i; + + cksum.cksumtype = (CKSUMTYPE)sig->type; + cksum.checksum.length = sizeof(sig->signature); + cksum.checksum.data = sig->signature; + + + ret = krb5_crypto_init(gensec_krb5_state->context, + &gensec_krb5_state->keyblock, + 0, + &crypto); + if (ret) { + DEBUG(0,("krb5_crypto_init() failed\n")); + return NT_STATUS_FOOBAR; + } + for (i=0; i < 40; i++) { + keyusage = i; + ret = krb5_verify_checksum(gensec_krb5_state->context, + crypto, + keyusage, + pac_data.data, + pac_data.length, + &cksum); + if (!ret) { + DEBUG(0,("PAC Verified: keyusage: %d\n", keyusage)); + break; + } + } + krb5_crypto_destroy(gensec_krb5_state->context, crypto); + + if (ret) { + DEBUG(0,("NOT verifying PAC checksums yet!\n")); + //return NT_STATUS_LOGON_FAILURE; + } else { + DEBUG(0,("PAC checksums verified!\n")); + } + + return NT_STATUS_OK; +} +#endif + +static NTSTATUS gensec_krb5_decode_pac(TALLOC_CTX *mem_ctx, + struct PAC_LOGON_INFO **logon_info_out, + DATA_BLOB blob, + struct gensec_krb5_state *gensec_krb5_state) +{ + NTSTATUS status; + struct PAC_SIGNATURE_DATA srv_sig; + struct PAC_SIGNATURE_DATA *srv_sig_ptr; + struct PAC_SIGNATURE_DATA kdc_sig; + struct PAC_SIGNATURE_DATA *kdc_sig_ptr; + struct PAC_LOGON_INFO *logon_info = NULL; + struct PAC_DATA pac_data; +#ifdef KRB5_DO_VERIFY_PAC + DATA_BLOB tmp_blob = data_blob(NULL, 0); +#endif + int i; + + status = ndr_pull_struct_blob(&blob, mem_ctx, &pac_data, + (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("can't parse the PAC\n")); + return status; + } + NDR_PRINT_DEBUG(PAC_DATA, &pac_data); + + if (pac_data.num_buffers < 3) { + /* we need logon_ingo, service_key and kdc_key */ + DEBUG(0,("less than 3 PAC buffers\n")); + return NT_STATUS_FOOBAR; + } + + for (i=0; i < pac_data.num_buffers; i++) { + switch (pac_data.buffers[i].type) { + case PAC_TYPE_LOGON_INFO: + if (!pac_data.buffers[i].info) { + break; + } + logon_info = &pac_data.buffers[i].info->logon_info; + break; + case PAC_TYPE_SRV_CHECKSUM: + if (!pac_data.buffers[i].info) { + break; + } + srv_sig_ptr = &pac_data.buffers[i].info->srv_cksum; + srv_sig = pac_data.buffers[i].info->srv_cksum; + break; + case PAC_TYPE_KDC_CHECKSUM: + if (!pac_data.buffers[i].info) { + break; + } + kdc_sig_ptr = &pac_data.buffers[i].info->kdc_cksum; + kdc_sig = pac_data.buffers[i].info->kdc_cksum; + break; + case PAC_TYPE_UNKNOWN_10: + break; + default: + break; + } + } + + if (!logon_info) { + DEBUG(0,("PAC no logon_info\n")); + return NT_STATUS_FOOBAR; + } + + if (!srv_sig_ptr) { + DEBUG(0,("PAC no srv_key\n")); + return NT_STATUS_FOOBAR; + } + + if (!kdc_sig_ptr) { + DEBUG(0,("PAC no kdc_key\n")); + return NT_STATUS_FOOBAR; + } +#ifdef KRB5_DO_VERIFY_PAC + /* clear the kdc_key */ +/* memset((void *)kdc_sig_ptr , '\0', sizeof(*kdc_sig_ptr));*/ + + status = ndr_push_struct_blob(&tmp_blob, mem_ctx, &pac_data, + (ndr_push_flags_fn_t)ndr_push_PAC_DATA); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + status = ndr_pull_struct_blob(&tmp_blob, mem_ctx, &pac_data, + (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("can't parse the PAC\n")); + return status; + } + /*NDR_PRINT_DEBUG(PAC_DATA, &pac_data);*/ + + /* verify by kdc_key */ + status = gensec_krb5_pac_checksum(tmp_blob, &kdc_sig, gensec_krb5_state, 0); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* clear the service_key */ +/* memset((void *)srv_sig_ptr , '\0', sizeof(*srv_sig_ptr));*/ + + status = ndr_push_struct_blob(&tmp_blob, mem_ctx, &pac_data, + (ndr_push_flags_fn_t)ndr_push_PAC_DATA); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + status = ndr_pull_struct_blob(&tmp_blob, mem_ctx, &pac_data, + (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("can't parse the PAC\n")); + return status; + } + NDR_PRINT_DEBUG(PAC_DATA, &pac_data); + + /* verify by servie_key */ + status = gensec_krb5_pac_checksum(tmp_blob, &srv_sig, gensec_krb5_state, 0); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } +#endif + DEBUG(0,("account_name: %s [%s]\n",logon_info->info3.base.account_name.string, logon_info->info3.base.full_name.string)); + *logon_info_out = logon_info; + + return status; +} + +static int gensec_krb5_destory(void *ptr) +{ + struct gensec_krb5_state *gensec_krb5_state = ptr; + + if (gensec_krb5_state->ticket.length) { + kerberos_free_data_contents(gensec_krb5_state->context, &gensec_krb5_state->ticket); + } + if (gensec_krb5_state->ccache) { + /* current heimdal - 0.6.3, which we need anyway, fixes segfaults here */ + krb5_cc_close(gensec_krb5_state->context, gensec_krb5_state->ccache); + } + + krb5_free_keyblock_contents(gensec_krb5_state->context, + &gensec_krb5_state->keyblock); + + if (gensec_krb5_state->auth_context) { + krb5_auth_con_free(gensec_krb5_state->context, + gensec_krb5_state->auth_context); + } + + if (gensec_krb5_state->context) { + krb5_free_context(gensec_krb5_state->context); + } + return 0; +} + +static NTSTATUS gensec_krb5_start(struct gensec_security *gensec_security) +{ + struct gensec_krb5_state *gensec_krb5_state; + krb5_error_code ret = 0; + + gensec_krb5_state = talloc(gensec_security, struct gensec_krb5_state); + if (!gensec_krb5_state) { + return NT_STATUS_NO_MEMORY; + } + + gensec_security->private_data = gensec_krb5_state; + + initialize_krb5_error_table(); + gensec_krb5_state->context = NULL; + gensec_krb5_state->auth_context = NULL; + gensec_krb5_state->ccache = NULL; + ZERO_STRUCT(gensec_krb5_state->ticket); + ZERO_STRUCT(gensec_krb5_state->keyblock); + gensec_krb5_state->session_key = data_blob(NULL, 0); + gensec_krb5_state->pac = data_blob(NULL, 0); + + talloc_set_destructor(gensec_krb5_state, gensec_krb5_destory); + + ret = krb5_init_context(&gensec_krb5_state->context); + if (ret) { + DEBUG(1,("gensec_krb5_start: krb5_init_context failed (%s)\n", error_message(ret))); + return NT_STATUS_INTERNAL_ERROR; + } + + if (lp_realm() && *lp_realm()) { + ret = krb5_set_default_realm(gensec_krb5_state->context, lp_realm()); + if (ret) { + DEBUG(1,("gensec_krb5_start: krb5_set_default_realm failed (%s)\n", error_message(ret))); + return NT_STATUS_INTERNAL_ERROR; + } + } + + ret = krb5_auth_con_init(gensec_krb5_state->context, &gensec_krb5_state->auth_context); + if (ret) { + DEBUG(1,("gensec_krb5_start: krb5_auth_con_init failed (%s)\n", error_message(ret))); + return NT_STATUS_INTERNAL_ERROR; + } + + return NT_STATUS_OK; +} + +static NTSTATUS gensec_krb5_server_start(struct gensec_security *gensec_security) +{ + NTSTATUS nt_status; + struct gensec_krb5_state *gensec_krb5_state; + + nt_status = gensec_krb5_start(gensec_security); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + gensec_krb5_state = gensec_security->private_data; + gensec_krb5_state->state_position = GENSEC_KRB5_SERVER_START; + + return NT_STATUS_OK; +} + +static NTSTATUS gensec_krb5_client_start(struct gensec_security *gensec_security) +{ + struct gensec_krb5_state *gensec_krb5_state; + krb5_error_code ret; + NTSTATUS nt_status; + const char *hostname = gensec_get_target_hostname(gensec_security); + if (!hostname) { + DEBUG(1, ("Could not determine hostname for target computer, cannot use kerberos\n")); + return NT_STATUS_ACCESS_DENIED; + } + + nt_status = gensec_krb5_start(gensec_security); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + gensec_krb5_state = gensec_security->private_data; + gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_START; + + /* TODO: This is effecivly a static/global variable... + + TODO: If the user set a username, we should use an in-memory CCACHE (see below) + */ + ret = krb5_cc_default(gensec_krb5_state->context, &gensec_krb5_state->ccache); + if (ret) { + DEBUG(1,("krb5_cc_default failed (%s)\n", + error_message(ret))); + return NT_STATUS_INTERNAL_ERROR; + } + + while (1) { + { + krb5_data in_data; + in_data.length = 0; + + ret = krb5_mk_req(gensec_krb5_state->context, + &gensec_krb5_state->auth_context, + AP_OPTS_USE_SUBKEY | AP_OPTS_MUTUAL_REQUIRED, + gensec_get_target_service(gensec_security), + hostname, + &in_data, gensec_krb5_state->ccache, + &gensec_krb5_state->ticket); + + } + switch (ret) { + case 0: + return NT_STATUS_OK; + case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN: + DEBUG(3, ("Server [%s] is not registered with our KDC: %s\n", + hostname, error_message(ret))); + return NT_STATUS_ACCESS_DENIED; + case KRB5KDC_ERR_PREAUTH_FAILED: + case KRB5KRB_AP_ERR_TKT_EXPIRED: + case KRB5_CC_END: + /* Too much clock skew - we will need to kinit to re-skew the clock */ + case KRB5KRB_AP_ERR_SKEW: + case KRB5_KDCREP_SKEW: + { + DEBUG(3, ("kerberos (mk_req) failed: %s\n", + error_message(ret))); + /* fall down to remaining code */ + } + + + /* just don't print a message for these really ordinary messages */ + case KRB5_FCC_NOFILE: + case KRB5_CC_NOTFOUND: + case ENOENT: + + { + const char *password; + char *ccache_string; + time_t kdc_time = 0; + password = cli_credentials_get_password(gensec_security->credentials); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + /* this string should be unique */ + ccache_string = talloc_asprintf(gensec_krb5_state, "MEMORY:%s:%s:%s", + cli_credentials_get_principal(gensec_security->credentials, gensec_krb5_state), + gensec_get_target_hostname(gensec_security), + generate_random_str(gensec_krb5_state, 16)); + + ret = krb5_cc_resolve(gensec_krb5_state->context, ccache_string, &gensec_krb5_state->ccache); + if (ret) { + DEBUG(1,("failed to generate a new krb5 keytab (%s): %s\n", + ccache_string, + error_message(ret))); + return NT_STATUS_INTERNAL_ERROR; + } + + ret = kerberos_kinit_password_cc(gensec_krb5_state->context, gensec_krb5_state->ccache, + cli_credentials_get_principal(gensec_security->credentials, gensec_krb5_state), + password, NULL, &kdc_time); + + /* cope with ticket being in the future due to clock skew */ + if ((unsigned)kdc_time > time(NULL)) { + time_t t = time(NULL); + int time_offset =(unsigned)kdc_time-t; + DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset)); + krb5_set_real_time(gensec_krb5_state->context, t + time_offset + 1, 0); + break; + } + + if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) { + DEBUG(1,("kinit for %s failed (%s)\n", + cli_credentials_get_principal(gensec_security->credentials, gensec_krb5_state), + error_message(ret))); + return NT_STATUS_TIME_DIFFERENCE_AT_DC; + } + if (ret) { + DEBUG(1,("kinit for %s failed (%s)\n", + cli_credentials_get_principal(gensec_security->credentials, gensec_krb5_state), + error_message(ret))); + return NT_STATUS_WRONG_PASSWORD; + } + break; + } + default: + DEBUG(0, ("kerberos: %s\n", + error_message(ret))); + return NT_STATUS_UNSUCCESSFUL; + } + } +} + + +/** + * Next state function for the Krb5 GENSEC mechanism + * + * @param gensec_krb5_state KRB5 State + * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on + * @param in The request, as a DATA_BLOB + * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx + * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent, + * or NT_STATUS_OK if the user is authenticated. + */ + +static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security, + TALLOC_CTX *out_mem_ctx, + const DATA_BLOB in, DATA_BLOB *out) +{ + struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data; + krb5_error_code ret = 0; + DATA_BLOB pac; + NTSTATUS nt_status; + + switch (gensec_krb5_state->state_position) { + case GENSEC_KRB5_CLIENT_START: + { + if (ret) { + DEBUG(1,("ads_krb5_mk_req (request ticket) failed (%s)\n", + error_message(ret))); + nt_status = NT_STATUS_LOGON_FAILURE; + } else { + DATA_BLOB unwrapped_out; + +#ifndef GENSEC_SEND_UNWRAPPED_KRB5 /* This should be a switch for the torture code to set */ + unwrapped_out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->ticket.data, gensec_krb5_state->ticket.length); + + /* wrap that up in a nice GSS-API wrapping */ + *out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REQ); +#else + *out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->ticket.data, gensec_krb5_state->ticket.length); +#endif + gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_MUTUAL_AUTH; + nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED; + } + + return nt_status; + } + + case GENSEC_KRB5_CLIENT_MUTUAL_AUTH: + { + krb5_data inbuf; + krb5_ap_rep_enc_part *repl = NULL; + uint8_t tok_id[2]; + DATA_BLOB unwrapped_in; + + if (!gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) { + DEBUG(1,("gensec_gssapi_parse_krb5_wrap(mutual authentication) failed to parse\n")); + dump_data_pw("Mutual authentication message:\n", in.data, in.length); + return NT_STATUS_INVALID_PARAMETER; + } + /* TODO: check the tok_id */ + + inbuf.data = unwrapped_in.data; + inbuf.length = unwrapped_in.length; + ret = krb5_rd_rep(gensec_krb5_state->context, + gensec_krb5_state->auth_context, + &inbuf, &repl); + if (ret) { + DEBUG(1,("krb5_rd_rep (mutual authentication) failed (%s)\n", + error_message(ret))); + dump_data_pw("Mutual authentication message:\n", inbuf.data, inbuf.length); + nt_status = NT_STATUS_ACCESS_DENIED; + } else { + *out = data_blob(NULL, 0); + nt_status = NT_STATUS_OK; + gensec_krb5_state->state_position = GENSEC_KRB5_DONE; + } + if (repl) { + krb5_free_ap_rep_enc_part(gensec_krb5_state->context, repl); + } + return nt_status; + } + + case GENSEC_KRB5_SERVER_START: + { + char *principal; + DATA_BLOB unwrapped_in; + DATA_BLOB unwrapped_out = data_blob(NULL, 0); + uint8_t tok_id[2]; + + if (!in.data) { + *out = unwrapped_out; + return NT_STATUS_MORE_PROCESSING_REQUIRED; + } + + /* Parse the GSSAPI wrapping, if it's there... (win2k3 allows it to be omited) */ + if (!gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) { + nt_status = ads_verify_ticket(out_mem_ctx, + gensec_krb5_state->context, + gensec_krb5_state->auth_context, + lp_realm(), + gensec_get_target_service(gensec_security), &in, + &principal, &pac, &unwrapped_out, + &gensec_krb5_state->keyblock); + } else { + /* TODO: check the tok_id */ + nt_status = ads_verify_ticket(out_mem_ctx, + gensec_krb5_state->context, + gensec_krb5_state->auth_context, + lp_realm(), + gensec_get_target_service(gensec_security), + &unwrapped_in, + &principal, &pac, &unwrapped_out, + &gensec_krb5_state->keyblock); + } + + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + if (pac.data) { + gensec_krb5_state->pac = data_blob_talloc_reference(gensec_krb5_state, &pac); + } + + if (NT_STATUS_IS_OK(nt_status)) { + gensec_krb5_state->state_position = GENSEC_KRB5_DONE; + /* wrap that up in a nice GSS-API wrapping */ +#ifndef GENSEC_SEND_UNWRAPPED_KRB5 + *out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REP); +#else + *out = unwrapped_out; +#endif + gensec_krb5_state->peer_principal = talloc_steal(gensec_krb5_state, principal); + } + return nt_status; + } + case GENSEC_KRB5_DONE: + return NT_STATUS_OK; + } + + return NT_STATUS_INVALID_PARAMETER; +} + +static NTSTATUS gensec_krb5_session_key(struct gensec_security *gensec_security, + DATA_BLOB *session_key) +{ + struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data; + krb5_context context = gensec_krb5_state->context; + krb5_auth_context auth_context = gensec_krb5_state->auth_context; + krb5_keyblock *skey; + krb5_error_code err; + + if (gensec_krb5_state->session_key.data) { + *session_key = gensec_krb5_state->session_key; + return NT_STATUS_OK; + } + + switch (gensec_security->gensec_role) { + case GENSEC_CLIENT: + err = krb5_auth_con_getlocalsubkey(context, auth_context, &skey); + break; + case GENSEC_SERVER: + err = krb5_auth_con_getremotesubkey(context, auth_context, &skey); + break; + } + if (err == 0 && skey != NULL) { + DEBUG(10, ("Got KRB5 session key of length %d\n", KRB5_KEY_LENGTH(skey))); + gensec_krb5_state->session_key = data_blob_talloc(gensec_krb5_state, + KRB5_KEY_DATA(skey), KRB5_KEY_LENGTH(skey)); + *session_key = gensec_krb5_state->session_key; + dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length); + + krb5_free_keyblock(context, skey); + return NT_STATUS_OK; + } else { + DEBUG(10, ("KRB5 error getting session key %d\n", err)); + return NT_STATUS_NO_USER_SESSION_KEY; + } +} + +static NTSTATUS gensec_krb5_session_info(struct gensec_security *gensec_security, + struct auth_session_info **_session_info) +{ + NTSTATUS nt_status; + struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data; + struct auth_serversupplied_info *server_info = NULL; + struct auth_session_info *session_info = NULL; + struct PAC_LOGON_INFO *logon_info; + char *p; + char *principal; + const char *account_name; + const char *realm; + + principal = talloc_strdup(gensec_krb5_state, gensec_krb5_state->peer_principal); + NT_STATUS_HAVE_NO_MEMORY(principal); + + p = strchr(principal, '@'); + if (p) { + *p = '\0'; + p++; + realm = p; + } else { + realm = lp_realm(); + } + account_name = principal; + + /* decode and verify the pac */ + nt_status = gensec_krb5_decode_pac(gensec_krb5_state, &logon_info, gensec_krb5_state->pac, + gensec_krb5_state); + + /* IF we have the PAC - otherwise we need to get this + * data from elsewere - local ldb, or (TODO) lookup of some + * kind... + * + * when heimdal can generate the PAC, we should fail if there's + * no PAC present + */ + + if (NT_STATUS_IS_OK(nt_status)) { + union netr_Validation validation; + validation.sam3 = &logon_info->info3; + nt_status = make_server_info_netlogon_validation(gensec_krb5_state, + account_name, + 3, &validation, + &server_info); + talloc_free(principal); + NT_STATUS_NOT_OK_RETURN(nt_status); + } else { + DATA_BLOB user_sess_key = data_blob(NULL, 0); + DATA_BLOB lm_sess_key = data_blob(NULL, 0); + /* TODO: should we pass the krb5 session key in here? */ + nt_status = sam_get_server_info(gensec_krb5_state, account_name, realm, + user_sess_key, lm_sess_key, + &server_info); + talloc_free(principal); + NT_STATUS_NOT_OK_RETURN(nt_status); + } + + /* references the server_info into the session_info */ + nt_status = auth_generate_session_info(gensec_krb5_state, server_info, &session_info); + talloc_free(server_info); + NT_STATUS_NOT_OK_RETURN(nt_status); + + nt_status = gensec_krb5_session_key(gensec_security, &session_info->session_key); + NT_STATUS_NOT_OK_RETURN(nt_status); + + *_session_info = session_info; + + return NT_STATUS_OK; +} + +static BOOL gensec_krb5_have_feature(struct gensec_security *gensec_security, + uint32_t feature) +{ + if (feature & GENSEC_FEATURE_SESSION_KEY) { + return True; + } + + return False; +} + + +static const struct gensec_security_ops gensec_krb5_security_ops = { + .name = "krb5", + .auth_type = DCERPC_AUTH_TYPE_KRB5, + .oid = GENSEC_OID_KERBEROS5, + .client_start = gensec_krb5_client_start, + .server_start = gensec_krb5_server_start, + .update = gensec_krb5_update, + .session_key = gensec_krb5_session_key, + .session_info = gensec_krb5_session_info, + .have_feature = gensec_krb5_have_feature, + .enabled = False +}; + +static const struct gensec_security_ops gensec_ms_krb5_security_ops = { + .name = "ms_krb5", + .auth_type = DCERPC_AUTH_TYPE_KRB5, + .oid = GENSEC_OID_KERBEROS5_OLD, + .client_start = gensec_krb5_client_start, + .server_start = gensec_krb5_server_start, + .update = gensec_krb5_update, + .session_key = gensec_krb5_session_key, + .session_info = gensec_krb5_session_info, + .have_feature = gensec_krb5_have_feature, + .enabled = False +}; + + +NTSTATUS gensec_krb5_init(void) +{ + NTSTATUS ret; + + ret = gensec_register(&gensec_krb5_security_ops); + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(0,("Failed to register '%s' gensec backend!\n", + gensec_krb5_security_ops.name)); + return ret; + } + + ret = gensec_register(&gensec_ms_krb5_security_ops); + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(0,("Failed to register '%s' gensec backend!\n", + gensec_krb5_security_ops.name)); + return ret; + } + + return ret; +} diff --git a/source4/auth/gensec/gensec_ntlmssp.c b/source4/auth/gensec/gensec_ntlmssp.c new file mode 100644 index 0000000000..5955904886 --- /dev/null +++ b/source4/auth/gensec/gensec_ntlmssp.c @@ -0,0 +1,514 @@ +/* + Unix SMB/CIFS implementation. + + dcerpc authentication operations + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Andrew Bartlett 2004 + Copyright (C) Stefan Metzmacher 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "auth/auth.h" + +struct gensec_ntlmssp_state { + struct auth_context *auth_context; + struct auth_serversupplied_info *server_info; + struct ntlmssp_state *ntlmssp_state; + uint32_t have_features; +}; + + +/** + * Return the challenge as determined by the authentication subsystem + * @return an 8 byte random challenge + */ + +static const uint8_t *auth_ntlmssp_get_challenge(const struct ntlmssp_state *ntlmssp_state) +{ + struct gensec_ntlmssp_state *gensec_ntlmssp_state = ntlmssp_state->auth_context; + NTSTATUS status; + const uint8_t *chal; + + status = auth_get_challenge(gensec_ntlmssp_state->auth_context, &chal); + if (!NT_STATUS_IS_OK(status)) { + return NULL; + } + + return chal; +} + +/** + * Some authentication methods 'fix' the challenge, so we may not be able to set it + * + * @return If the effective challenge used by the auth subsystem may be modified + */ +static BOOL auth_ntlmssp_may_set_challenge(const struct ntlmssp_state *ntlmssp_state) +{ + struct gensec_ntlmssp_state *gensec_ntlmssp_state = ntlmssp_state->auth_context; + + return auth_challenge_may_be_modified(gensec_ntlmssp_state->auth_context); +} + +/** + * NTLM2 authentication modifies the effective challenge, + * @param challenge The new challenge value + */ +static NTSTATUS auth_ntlmssp_set_challenge(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *challenge) +{ + NTSTATUS nt_status; + struct gensec_ntlmssp_state *gensec_ntlmssp_state = ntlmssp_state->auth_context; + struct auth_context *auth_context = gensec_ntlmssp_state->auth_context; + const uint8_t *chal; + + if (challenge->length != 8) { + return NT_STATUS_INVALID_PARAMETER; + } + + chal = challenge->data; + + nt_status = auth_context_set_challenge(auth_context, chal, "NTLMSSP callback (NTLM2)"); + + return nt_status; +} + +/** + * Check the password on an NTLMSSP login. + * + * Return the session keys used on the connection. + */ + +static NTSTATUS auth_ntlmssp_check_password(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *user_session_key, DATA_BLOB *lm_session_key) +{ + struct gensec_ntlmssp_state *gensec_ntlmssp_state = ntlmssp_state->auth_context; + struct auth_usersupplied_info *user_info = NULL; + NTSTATUS nt_status; + + nt_status = make_user_info_map(ntlmssp_state, + gensec_ntlmssp_state->ntlmssp_state->user, + gensec_ntlmssp_state->ntlmssp_state->domain, + gensec_ntlmssp_state->ntlmssp_state->workstation, + gensec_ntlmssp_state->ntlmssp_state->lm_resp.data ? &gensec_ntlmssp_state->ntlmssp_state->lm_resp : NULL, + gensec_ntlmssp_state->ntlmssp_state->nt_resp.data ? &gensec_ntlmssp_state->ntlmssp_state->nt_resp : NULL, + NULL, NULL, NULL, True, + &user_info); + NT_STATUS_NOT_OK_RETURN(nt_status); + + nt_status = auth_check_password(gensec_ntlmssp_state->auth_context, gensec_ntlmssp_state, + user_info, &gensec_ntlmssp_state->server_info); + talloc_free(user_info); + NT_STATUS_NOT_OK_RETURN(nt_status); + + if (gensec_ntlmssp_state->server_info->user_session_key.length) { + DEBUG(10, ("Got NT session key of length %u\n", gensec_ntlmssp_state->server_info->user_session_key.length)); + *user_session_key = data_blob_talloc(ntlmssp_state, + gensec_ntlmssp_state->server_info->user_session_key.data, + gensec_ntlmssp_state->server_info->user_session_key.length); + } + if (gensec_ntlmssp_state->server_info->lm_session_key.length) { + DEBUG(10, ("Got LM session key of length %u\n", gensec_ntlmssp_state->server_info->lm_session_key.length)); + *lm_session_key = data_blob_talloc(ntlmssp_state, + gensec_ntlmssp_state->server_info->lm_session_key.data, + gensec_ntlmssp_state->server_info->lm_session_key.length); + } + return nt_status; +} + +static int gensec_ntlmssp_destroy(void *ptr) +{ + struct gensec_ntlmssp_state *gensec_ntlmssp_state = ptr; + + if (gensec_ntlmssp_state->ntlmssp_state) { + ntlmssp_end(&gensec_ntlmssp_state->ntlmssp_state); + } + + return 0; +} + +static NTSTATUS gensec_ntlmssp_start(struct gensec_security *gensec_security) +{ + struct gensec_ntlmssp_state *gensec_ntlmssp_state; + + gensec_ntlmssp_state = talloc(gensec_security, struct gensec_ntlmssp_state); + if (!gensec_ntlmssp_state) { + return NT_STATUS_NO_MEMORY; + } + + gensec_ntlmssp_state->ntlmssp_state = NULL; + gensec_ntlmssp_state->auth_context = NULL; + gensec_ntlmssp_state->server_info = NULL; + gensec_ntlmssp_state->have_features = 0; + + talloc_set_destructor(gensec_ntlmssp_state, gensec_ntlmssp_destroy); + + gensec_security->private_data = gensec_ntlmssp_state; + return NT_STATUS_OK; +} + +static NTSTATUS gensec_ntlmssp_server_start(struct gensec_security *gensec_security) +{ + NTSTATUS nt_status; + struct ntlmssp_state *ntlmssp_state; + struct gensec_ntlmssp_state *gensec_ntlmssp_state; + + nt_status = gensec_ntlmssp_start(gensec_security); + NT_STATUS_NOT_OK_RETURN(nt_status); + + gensec_ntlmssp_state = gensec_security->private_data; + + nt_status = ntlmssp_server_start(gensec_ntlmssp_state, &gensec_ntlmssp_state->ntlmssp_state); + NT_STATUS_NOT_OK_RETURN(nt_status); + + if (gensec_security->want_features & GENSEC_FEATURE_SIGN) { + gensec_ntlmssp_state->ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN; + } + if (gensec_security->want_features & GENSEC_FEATURE_SEAL) { + gensec_ntlmssp_state->ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SEAL; + } + + nt_status = auth_context_create(gensec_ntlmssp_state, lp_auth_methods(), &gensec_ntlmssp_state->auth_context); + NT_STATUS_NOT_OK_RETURN(nt_status); + + ntlmssp_state = gensec_ntlmssp_state->ntlmssp_state; + ntlmssp_state->auth_context = gensec_ntlmssp_state; + ntlmssp_state->get_challenge = auth_ntlmssp_get_challenge; + ntlmssp_state->may_set_challenge = auth_ntlmssp_may_set_challenge; + ntlmssp_state->set_challenge = auth_ntlmssp_set_challenge; + ntlmssp_state->check_password = auth_ntlmssp_check_password; + ntlmssp_state->server_role = lp_server_role(); + + return NT_STATUS_OK; +} + +static NTSTATUS gensec_ntlmssp_client_start(struct gensec_security *gensec_security) +{ + struct gensec_ntlmssp_state *gensec_ntlmssp_state; + const char *password = NULL; + NTSTATUS nt_status; + + nt_status = gensec_ntlmssp_start(gensec_security); + NT_STATUS_NOT_OK_RETURN(nt_status); + + gensec_ntlmssp_state = gensec_security->private_data; + nt_status = ntlmssp_client_start(gensec_ntlmssp_state, + &gensec_ntlmssp_state->ntlmssp_state); + NT_STATUS_NOT_OK_RETURN(nt_status); + + if (gensec_security->want_features & GENSEC_FEATURE_SESSION_KEY) { + /* + * We need to set this to allow a later SetPassword + * via the SAMR pipe to succeed. Strange.... We could + * also add NTLMSSP_NEGOTIATE_SEAL here. JRA. + * + * Without this, Windows will not create the master key + * that it thinks is only used for NTLMSSP signing and + * sealing. (It is actually pulled out and used directly) + */ + gensec_ntlmssp_state->ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN; + } + if (gensec_security->want_features & GENSEC_FEATURE_SIGN) { + gensec_ntlmssp_state->ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN; + } + if (gensec_security->want_features & GENSEC_FEATURE_SEAL) { + gensec_ntlmssp_state->ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SEAL; + } + + nt_status = ntlmssp_set_domain(gensec_ntlmssp_state->ntlmssp_state, + cli_credentials_get_domain(gensec_security->credentials)); + NT_STATUS_NOT_OK_RETURN(nt_status); + + nt_status = ntlmssp_set_username(gensec_ntlmssp_state->ntlmssp_state, + cli_credentials_get_username(gensec_security->credentials)); + NT_STATUS_NOT_OK_RETURN(nt_status); + + password = cli_credentials_get_password(gensec_security->credentials); + + nt_status = ntlmssp_set_password(gensec_ntlmssp_state->ntlmssp_state, password); + NT_STATUS_NOT_OK_RETURN(nt_status); + + nt_status = ntlmssp_set_workstation(gensec_ntlmssp_state->ntlmssp_state, + cli_credentials_get_workstation(gensec_security->credentials)); + + gensec_security->private_data = gensec_ntlmssp_state; + + return NT_STATUS_OK; +} + +/* + wrappers for the ntlmssp_*() functions +*/ +static NTSTATUS gensec_ntlmssp_unseal_packet(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + uint8_t *data, size_t length, + const uint8_t *whole_pdu, size_t pdu_length, + DATA_BLOB *sig) +{ + struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; + + return ntlmssp_unseal_packet(gensec_ntlmssp_state->ntlmssp_state, mem_ctx, data, length, whole_pdu, pdu_length, sig); +} + +static NTSTATUS gensec_ntlmssp_check_packet(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const uint8_t *data, size_t length, + const uint8_t *whole_pdu, size_t pdu_length, + const DATA_BLOB *sig) +{ + struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; + + return ntlmssp_check_packet(gensec_ntlmssp_state->ntlmssp_state, mem_ctx, data, length, whole_pdu, pdu_length, sig); +} + +static NTSTATUS gensec_ntlmssp_seal_packet(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + uint8_t *data, size_t length, + const uint8_t *whole_pdu, size_t pdu_length, + DATA_BLOB *sig) +{ + struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; + + return ntlmssp_seal_packet(gensec_ntlmssp_state->ntlmssp_state, mem_ctx, data, length, whole_pdu, pdu_length, sig); +} + +static NTSTATUS gensec_ntlmssp_sign_packet(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const uint8_t *data, size_t length, + const uint8_t *whole_pdu, size_t pdu_length, + DATA_BLOB *sig) +{ + struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; + + return ntlmssp_sign_packet(gensec_ntlmssp_state->ntlmssp_state, mem_ctx, data, length, whole_pdu, pdu_length, sig); +} + +static size_t gensec_ntlmssp_sig_size(struct gensec_security *gensec_security) +{ + return NTLMSSP_SIG_SIZE; +} + +static NTSTATUS gensec_ntlmssp_wrap(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const DATA_BLOB *in, + DATA_BLOB *out) +{ + struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; + DATA_BLOB sig; + NTSTATUS nt_status; + + if (gensec_ntlmssp_state->ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SEAL) { + + *out = data_blob_talloc(mem_ctx, NULL, in->length + NTLMSSP_SIG_SIZE); + memcpy(out->data + NTLMSSP_SIG_SIZE, in->data, in->length); + + nt_status = ntlmssp_seal_packet(gensec_ntlmssp_state->ntlmssp_state, mem_ctx, + out->data + NTLMSSP_SIG_SIZE, + out->length - NTLMSSP_SIG_SIZE, + out->data + NTLMSSP_SIG_SIZE, + out->length - NTLMSSP_SIG_SIZE, + &sig); + + if (NT_STATUS_IS_OK(nt_status)) { + memcpy(out->data, sig.data, NTLMSSP_SIG_SIZE); + } + return nt_status; + + } else if ((gensec_ntlmssp_state->ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SIGN) + || (gensec_ntlmssp_state->ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN)) { + + *out = data_blob_talloc(mem_ctx, NULL, in->length + NTLMSSP_SIG_SIZE); + memcpy(out->data + NTLMSSP_SIG_SIZE, in->data, in->length); + + nt_status = ntlmssp_sign_packet(gensec_ntlmssp_state->ntlmssp_state, mem_ctx, + out->data + NTLMSSP_SIG_SIZE, + out->length - NTLMSSP_SIG_SIZE, + out->data + NTLMSSP_SIG_SIZE, + out->length - NTLMSSP_SIG_SIZE, + &sig); + + if (NT_STATUS_IS_OK(nt_status)) { + memcpy(out->data, sig.data, NTLMSSP_SIG_SIZE); + } + return nt_status; + + } else { + *out = *in; + return NT_STATUS_OK; + } +} + + +static NTSTATUS gensec_ntlmssp_unwrap(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const DATA_BLOB *in, + DATA_BLOB *out) +{ + struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; + DATA_BLOB sig; + + if (gensec_ntlmssp_state->ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SEAL) { + if (in->length < NTLMSSP_SIG_SIZE) { + return NT_STATUS_INVALID_PARAMETER; + } + sig.data = in->data; + sig.length = NTLMSSP_SIG_SIZE; + + *out = data_blob_talloc(mem_ctx, in->data + NTLMSSP_SIG_SIZE, in->length - NTLMSSP_SIG_SIZE); + + return ntlmssp_unseal_packet(gensec_ntlmssp_state->ntlmssp_state, mem_ctx, + out->data, out->length, + out->data, out->length, + &sig); + + } else if ((gensec_ntlmssp_state->ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SIGN) + || (gensec_ntlmssp_state->ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN)) { + if (in->length < NTLMSSP_SIG_SIZE) { + return NT_STATUS_INVALID_PARAMETER; + } + sig.data = in->data; + sig.length = NTLMSSP_SIG_SIZE; + + *out = data_blob_talloc(mem_ctx, in->data + NTLMSSP_SIG_SIZE, in->length - NTLMSSP_SIG_SIZE); + + return ntlmssp_check_packet(gensec_ntlmssp_state->ntlmssp_state, mem_ctx, + out->data, out->length, + out->data, out->length, + &sig); + } else { + *out = *in; + return NT_STATUS_OK; + } +} + +static NTSTATUS gensec_ntlmssp_session_key(struct gensec_security *gensec_security, + DATA_BLOB *session_key) +{ + struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; + + return ntlmssp_session_key(gensec_ntlmssp_state->ntlmssp_state, session_key); +} + +/** + * Next state function for the wrapped NTLMSSP state machine + * + * @param gensec_security GENSEC state, initialised to NTLMSSP + * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on + * @param in The request, as a DATA_BLOB + * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx + * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent, + * or NT_STATUS_OK if the user is authenticated. + */ + +static NTSTATUS gensec_ntlmssp_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, + const DATA_BLOB in, DATA_BLOB *out) +{ + struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; + NTSTATUS status; + + status = ntlmssp_update(gensec_ntlmssp_state->ntlmssp_state, out_mem_ctx, in, out); + + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(status)) { + return status; + } + + gensec_ntlmssp_state->have_features = 0; + + if (gensec_ntlmssp_state->ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SIGN) { + gensec_ntlmssp_state->have_features |= GENSEC_FEATURE_SIGN; + } + + if (gensec_ntlmssp_state->ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SEAL) { + gensec_ntlmssp_state->have_features |= GENSEC_FEATURE_SEAL; + } + + if (gensec_ntlmssp_state->ntlmssp_state->session_key.data) { + gensec_ntlmssp_state->have_features |= GENSEC_FEATURE_SESSION_KEY; + } + + return status; +} + +/** + * Return the credentials of a logged on user, including session keys + * etc. + * + * Only valid after a successful authentication + * + * May only be called once per authentication. + * + */ + +static NTSTATUS gensec_ntlmssp_session_info(struct gensec_security *gensec_security, + struct auth_session_info **session_info) +{ + NTSTATUS nt_status; + struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; + + nt_status = auth_generate_session_info(gensec_ntlmssp_state, gensec_ntlmssp_state->server_info, session_info); + NT_STATUS_NOT_OK_RETURN(nt_status); + + (*session_info)->session_key = data_blob_talloc(*session_info, + gensec_ntlmssp_state->ntlmssp_state->session_key.data, + gensec_ntlmssp_state->ntlmssp_state->session_key.length); + + return NT_STATUS_OK; +} + +static BOOL gensec_ntlmssp_have_feature(struct gensec_security *gensec_security, + uint32_t feature) +{ + struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; + if (gensec_ntlmssp_state->have_features & feature) { + return True; + } + + return False; +} + +static const struct gensec_security_ops gensec_ntlmssp_security_ops = { + .name = "ntlmssp", + .sasl_name = "NTLM", + .auth_type = DCERPC_AUTH_TYPE_NTLMSSP, + .oid = GENSEC_OID_NTLMSSP, + .client_start = gensec_ntlmssp_client_start, + .server_start = gensec_ntlmssp_server_start, + .update = gensec_ntlmssp_update, + .sig_size = gensec_ntlmssp_sig_size, + .sign_packet = gensec_ntlmssp_sign_packet, + .check_packet = gensec_ntlmssp_check_packet, + .seal_packet = gensec_ntlmssp_seal_packet, + .unseal_packet = gensec_ntlmssp_unseal_packet, + .wrap = gensec_ntlmssp_wrap, + .unwrap = gensec_ntlmssp_unwrap, + .session_key = gensec_ntlmssp_session_key, + .session_info = gensec_ntlmssp_session_info, + .have_feature = gensec_ntlmssp_have_feature, + .enabled = True +}; + + +NTSTATUS gensec_ntlmssp_init(void) +{ + NTSTATUS ret; + ret = gensec_register(&gensec_ntlmssp_security_ops); + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(0,("Failed to register '%s' gensec backend!\n", + gensec_ntlmssp_security_ops.name)); + return ret; + } + + return ret; +} diff --git a/source4/auth/gensec/ntlmssp.c b/source4/auth/gensec/ntlmssp.c new file mode 100644 index 0000000000..37374d9d39 --- /dev/null +++ b/source4/auth/gensec/ntlmssp.c @@ -0,0 +1,1322 @@ +/* + Unix SMB/Netbios implementation. + Version 3.0 + handle NLTMSSP, client server side parsing + + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Andrew Bartlett 2001-2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "auth/auth.h" +#include "lib/crypto/crypto.h" +#include "pstring.h" + +static NTSTATUS ntlmssp_client_initial(struct ntlmssp_state *ntlmssp_state, + TALLOC_CTX *out_mem_ctx, + DATA_BLOB in, DATA_BLOB *out); +static NTSTATUS ntlmssp_server_negotiate(struct ntlmssp_state *ntlmssp_state, + TALLOC_CTX *out_mem_ctx, + const DATA_BLOB in, DATA_BLOB *out); +static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_state *ntlmssp_state, + TALLOC_CTX *out_mem_ctx, + const DATA_BLOB in, DATA_BLOB *out); +static NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state, + TALLOC_CTX *out_mem_ctx, + const DATA_BLOB in, DATA_BLOB *out); + +/** + * Callbacks for NTLMSSP - for both client and server operating modes + * + */ + +static const struct ntlmssp_callbacks { + enum ntlmssp_role role; + enum ntlmssp_message_type ntlmssp_command; + NTSTATUS (*fn)(struct ntlmssp_state *ntlmssp_state, + TALLOC_CTX *out_mem_ctx, + DATA_BLOB in, DATA_BLOB *out); +} ntlmssp_callbacks[] = { + {NTLMSSP_CLIENT, NTLMSSP_INITIAL, ntlmssp_client_initial}, + {NTLMSSP_SERVER, NTLMSSP_NEGOTIATE, ntlmssp_server_negotiate}, + {NTLMSSP_CLIENT, NTLMSSP_CHALLENGE, ntlmssp_client_challenge}, + {NTLMSSP_SERVER, NTLMSSP_AUTH, ntlmssp_server_auth}, + {NTLMSSP_CLIENT, NTLMSSP_UNKNOWN, NULL}, + {NTLMSSP_SERVER, NTLMSSP_UNKNOWN, NULL} +}; + + +/** + * Print out the NTLMSSP flags for debugging + * @param neg_flags The flags from the packet + */ + +void debug_ntlmssp_flags(uint32_t neg_flags) +{ + DEBUG(3,("Got NTLMSSP neg_flags=0x%08x\n", neg_flags)); + + if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_UNICODE\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_OEM) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_OEM\n")); + if (neg_flags & NTLMSSP_REQUEST_TARGET) + DEBUGADD(4, (" NTLMSSP_REQUEST_TARGET\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_SIGN) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_SIGN\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_SEAL) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_SEAL\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_LM_KEY\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_NETWARE) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NETWARE\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_NTLM) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NTLM\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_ALWAYS_SIGN\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_NTLM2) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NTLM2\n")); + if (neg_flags & NTLMSSP_CHAL_TARGET_INFO) + DEBUGADD(4, (" NTLMSSP_CHAL_TARGET_INFO\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_128) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_128\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_KEY_EXCH\n")); +} + +/** + * Default challenge generation code. + * + */ + +static const uint8_t *get_challenge(const struct ntlmssp_state *ntlmssp_state) +{ + uint8_t *chal = talloc_size(ntlmssp_state, 8); + generate_random_buffer(chal, 8); + + return chal; +} + +/** + * Default 'we can set the challenge to anything we like' implementation + * + */ + +static BOOL may_set_challenge(const struct ntlmssp_state *ntlmssp_state) +{ + return True; +} + +/** + * Default 'we can set the challenge to anything we like' implementation + * + * Does not actually do anything, as the value is always in the structure anyway. + * + */ + +static NTSTATUS set_challenge(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *challenge) +{ + SMB_ASSERT(challenge->length == 8); + return NT_STATUS_OK; +} + +/** + * Set a username on an NTLMSSP context - ensures it is talloc()ed + * + */ + +NTSTATUS ntlmssp_set_username(struct ntlmssp_state *ntlmssp_state, const char *user) +{ + if (!user) { + /* it should be at least "" */ + DEBUG(1, ("NTLMSSP failed to set username - cannot accept NULL username\n")); + return NT_STATUS_INVALID_PARAMETER; + } + ntlmssp_state->user = talloc_strdup(ntlmssp_state, user); + if (!ntlmssp_state->user) { + return NT_STATUS_NO_MEMORY; + } + return NT_STATUS_OK; +} + +/** + * Set a password on an NTLMSSP context - ensures it is talloc()ed + * + */ +NTSTATUS ntlmssp_set_password(struct ntlmssp_state *ntlmssp_state, const char *password) +{ + if (!password) { + ntlmssp_state->password = NULL; + } else { + ntlmssp_state->password = talloc_strdup(ntlmssp_state, password); + if (!ntlmssp_state->password) { + return NT_STATUS_NO_MEMORY; + } + } + return NT_STATUS_OK; +} + +/** + * Set a domain on an NTLMSSP context - ensures it is talloc()ed + * + */ +NTSTATUS ntlmssp_set_domain(struct ntlmssp_state *ntlmssp_state, const char *domain) +{ + ntlmssp_state->domain = talloc_strdup(ntlmssp_state, domain); + if (!ntlmssp_state->domain) { + return NT_STATUS_NO_MEMORY; + } + return NT_STATUS_OK; +} + +/** + * Set a workstation on an NTLMSSP context - ensures it is talloc()ed + * + */ +NTSTATUS ntlmssp_set_workstation(struct ntlmssp_state *ntlmssp_state, const char *workstation) +{ + ntlmssp_state->workstation = talloc_strdup(ntlmssp_state, workstation); + if (!ntlmssp_state->workstation) { + return NT_STATUS_NO_MEMORY; + } + return NT_STATUS_OK; +} + +/** + * Store a DATA_BLOB containing an NTLMSSP response, for use later. + * This copies the data blob + */ + +NTSTATUS ntlmssp_store_response(struct ntlmssp_state *ntlmssp_state, + DATA_BLOB response) +{ + ntlmssp_state->stored_response = data_blob_talloc(ntlmssp_state, + response.data, response.length); + return NT_STATUS_OK; +} + +/** + * Next state function for the NTLMSSP state machine + * + * @param ntlmssp_state NTLMSSP State + * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on + * @param in The request, as a DATA_BLOB + * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx + * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent, + * or NT_STATUS_OK if the user is authenticated. + */ + +NTSTATUS ntlmssp_update(struct ntlmssp_state *ntlmssp_state, + TALLOC_CTX *out_mem_ctx, + const DATA_BLOB in, DATA_BLOB *out) +{ + DATA_BLOB input; + uint32_t ntlmssp_command; + int i; + + *out = data_blob(NULL, 0); + + if (ntlmssp_state->expected_state == NTLMSSP_DONE) { + return NT_STATUS_OK; + } + + if (!out_mem_ctx) { + /* if the caller doesn't want to manage/own the memory, + we can put it on our context */ + out_mem_ctx = ntlmssp_state; + } + + if (!in.length && ntlmssp_state->stored_response.length) { + input = ntlmssp_state->stored_response; + + /* we only want to read the stored response once - overwrite it */ + ntlmssp_state->stored_response = data_blob(NULL, 0); + } else { + input = in; + } + + if (!input.length) { + switch (ntlmssp_state->role) { + case NTLMSSP_CLIENT: + ntlmssp_command = NTLMSSP_INITIAL; + break; + case NTLMSSP_SERVER: + /* 'datagram' mode - no neg packet */ + ntlmssp_command = NTLMSSP_NEGOTIATE; + break; + } + } else { + if (!msrpc_parse(ntlmssp_state, + &input, "Cd", + "NTLMSSP", + &ntlmssp_command)) { + DEBUG(1, ("Failed to parse NTLMSSP packet, could not extract NTLMSSP command\n")); + dump_data(2, input.data, input.length); + return NT_STATUS_INVALID_PARAMETER; + } + } + + if (ntlmssp_command != ntlmssp_state->expected_state) { + DEBUG(1, ("got NTLMSSP command %u, expected %u\n", ntlmssp_command, ntlmssp_state->expected_state)); + return NT_STATUS_INVALID_PARAMETER; + } + + for (i=0; ntlmssp_callbacks[i].fn; i++) { + if (ntlmssp_callbacks[i].role == ntlmssp_state->role + && ntlmssp_callbacks[i].ntlmssp_command == ntlmssp_command + && ntlmssp_callbacks[i].fn) { + return ntlmssp_callbacks[i].fn(ntlmssp_state, out_mem_ctx, input, out); + } + } + + DEBUG(1, ("failed to find NTLMSSP callback for NTLMSSP mode %u, command %u\n", + ntlmssp_state->role, ntlmssp_command)); + + return NT_STATUS_INVALID_PARAMETER; +} + +/** + * 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 + */ + +void ntlmssp_end(struct ntlmssp_state **ntlmssp_state) +{ + (*ntlmssp_state)->ref_count--; + + if ((*ntlmssp_state)->ref_count == 0) { + talloc_free(*ntlmssp_state); + } + + *ntlmssp_state = NULL; + return; +} + +/** + * Determine correct target name flags for reply, given server role + * and negotiated flags + * + * @param ntlmssp_state NTLMSSP State + * @param neg_flags The flags from the packet + * @param chal_flags The flags to be set in the reply packet + * @return The 'target name' string. + */ + +static const char *ntlmssp_target_name(struct ntlmssp_state *ntlmssp_state, + uint32_t neg_flags, uint32_t *chal_flags) +{ + if (neg_flags & NTLMSSP_REQUEST_TARGET) { + *chal_flags |= NTLMSSP_CHAL_TARGET_INFO; + *chal_flags |= NTLMSSP_REQUEST_TARGET; + if (ntlmssp_state->server_role == ROLE_STANDALONE) { + *chal_flags |= NTLMSSP_TARGET_TYPE_SERVER; + return ntlmssp_state->server_name; + } else { + *chal_flags |= NTLMSSP_TARGET_TYPE_DOMAIN; + return ntlmssp_state->get_domain(); + }; + } else { + return ""; + } +} + +static void ntlmssp_handle_neg_flags(struct ntlmssp_state *ntlmssp_state, + uint32_t neg_flags, BOOL allow_lm) { + if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE) { + ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE; + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_OEM; + ntlmssp_state->unicode = True; + } else { + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_UNICODE; + ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_OEM; + ntlmssp_state->unicode = False; + } + + if ((neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) && allow_lm && !ntlmssp_state->use_ntlmv2) { + /* other end forcing us to use LM */ + ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_LM_KEY; + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2; + } else { + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; + } + + if (neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) { + ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN; + } + + if (!(neg_flags & NTLMSSP_NEGOTIATE_SIGN)) { + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SIGN; + } + + if (!(neg_flags & NTLMSSP_NEGOTIATE_SEAL)) { + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SEAL; + } + + if (!(neg_flags & NTLMSSP_NEGOTIATE_NTLM2)) { + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2; + } + + if (!(neg_flags & NTLMSSP_NEGOTIATE_128)) { + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_128; + if (neg_flags & NTLMSSP_NEGOTIATE_56) { + ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_56; + } + } + + if (!(neg_flags & NTLMSSP_NEGOTIATE_56)) { + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_56; + } + + if (!(neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH)) { + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_KEY_EXCH; + } + + if ((neg_flags & NTLMSSP_REQUEST_TARGET)) { + ntlmssp_state->neg_flags |= NTLMSSP_REQUEST_TARGET; + } + +} + +/** + Weaken NTLMSSP keys to cope with down-level clients and servers. + + We probably should have some parameters to control this, but as + it only occours for LM_KEY connections, and this is controlled + by the client lanman auth/lanman auth parameters, it isn't too bad. +*/ + +static void ntlmssp_weaken_keys(struct ntlmssp_state *ntlmssp_state) { + /* Key weakening not performed on the master key for NTLM2 + and does not occour for NTLM1. Therefore we only need + to do this for the LM_KEY. + */ + + if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) { + if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_128) { + + } else if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_56) { + ntlmssp_state->session_key.data[7] = 0xa0; + } else { /* forty bits */ + ntlmssp_state->session_key.data[5] = 0xe5; + ntlmssp_state->session_key.data[6] = 0x38; + ntlmssp_state->session_key.data[7] = 0xb0; + } + ntlmssp_state->session_key.length = 8; + } +} + +/** + * Next state function for the Negotiate packet + * + * @param ntlmssp_state NTLMSSP State + * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on + * @param in The request, as a DATA_BLOB + * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx + * @return Errors or MORE_PROCESSING_REQUIRED if a reply is sent. + */ + +static NTSTATUS ntlmssp_server_negotiate(struct ntlmssp_state *ntlmssp_state, + TALLOC_CTX *out_mem_ctx, + const DATA_BLOB in, DATA_BLOB *out) +{ + DATA_BLOB struct_blob; + fstring dnsname, dnsdomname; + uint32_t neg_flags = 0; + uint32_t ntlmssp_command, chal_flags; + char *cliname=NULL, *domname=NULL; + const uint8_t *cryptkey; + const char *target_name; + + /* parse the NTLMSSP packet */ +#if 0 + file_save("ntlmssp_negotiate.dat", request.data, request.length); +#endif + + if (in.length) { + if (!msrpc_parse(ntlmssp_state, + &in, "CddAA", + "NTLMSSP", + &ntlmssp_command, + &neg_flags, + &cliname, + &domname)) { + DEBUG(1, ("ntlmssp_server_negotiate: failed to parse NTLMSSP:\n")); + dump_data(2, in.data, in.length); + return NT_STATUS_INVALID_PARAMETER; + } + + debug_ntlmssp_flags(neg_flags); + } + + ntlmssp_handle_neg_flags(ntlmssp_state, neg_flags, ntlmssp_state->allow_lm_key); + + /* Ask our caller what challenge they would like in the packet */ + cryptkey = ntlmssp_state->get_challenge(ntlmssp_state); + + /* Check if we may set the challenge */ + if (!ntlmssp_state->may_set_challenge(ntlmssp_state)) { + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2; + } + + /* The flags we send back are not just the negotiated flags, + * they are also 'what is in this packet'. Therfore, we + * operate on 'chal_flags' from here on + */ + + chal_flags = ntlmssp_state->neg_flags; + + /* get the right name to fill in as 'target' */ + target_name = ntlmssp_target_name(ntlmssp_state, + neg_flags, &chal_flags); + if (target_name == NULL) + return NT_STATUS_INVALID_PARAMETER; + + ntlmssp_state->chal = data_blob_talloc(ntlmssp_state, cryptkey, 8); + ntlmssp_state->internal_chal = data_blob_talloc(ntlmssp_state, cryptkey, 8); + + /* This should be a 'netbios domain -> DNS domain' mapping */ + dnsdomname[0] = '\0'; + get_mydomname(dnsdomname); + strlower_m(dnsdomname); + + dnsname[0] = '\0'; + get_myfullname(dnsname); + + /* This creates the 'blob' of names that appears at the end of the packet */ + if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) + { + const char *target_name_dns = ""; + if (chal_flags |= NTLMSSP_TARGET_TYPE_DOMAIN) { + target_name_dns = dnsdomname; + } else if (chal_flags |= NTLMSSP_TARGET_TYPE_SERVER) { + target_name_dns = dnsname; + } + + msrpc_gen(out_mem_ctx, + &struct_blob, "aaaaa", + NTLMSSP_NAME_TYPE_DOMAIN, target_name, + NTLMSSP_NAME_TYPE_SERVER, ntlmssp_state->server_name, + NTLMSSP_NAME_TYPE_DOMAIN_DNS, dnsdomname, + NTLMSSP_NAME_TYPE_SERVER_DNS, dnsname, + 0, ""); + } else { + struct_blob = data_blob(NULL, 0); + } + + { + /* Marshel the packet in the right format, be it unicode or ASCII */ + const char *gen_string; + if (ntlmssp_state->unicode) { + gen_string = "CdUdbddB"; + } else { + gen_string = "CdAdbddB"; + } + + msrpc_gen(out_mem_ctx, + out, gen_string, + "NTLMSSP", + NTLMSSP_CHALLENGE, + target_name, + chal_flags, + cryptkey, 8, + 0, 0, + struct_blob.data, struct_blob.length); + } + + ntlmssp_state->expected_state = NTLMSSP_AUTH; + + return NT_STATUS_MORE_PROCESSING_REQUIRED; +} + +/** + * Next state function for the Authenticate packet + * + * @param ntlmssp_state NTLMSSP State + * @param request The request, as a DATA_BLOB + * @return Errors or NT_STATUS_OK. + */ + +static NTSTATUS ntlmssp_server_preauth(struct ntlmssp_state *ntlmssp_state, + const DATA_BLOB request) +{ + uint32_t ntlmssp_command, auth_flags; + NTSTATUS nt_status; + + uint8_t session_nonce_hash[16]; + + const char *parse_string; + char *domain = NULL; + char *user = NULL; + char *workstation = NULL; + +#if 0 + file_save("ntlmssp_auth.dat", request.data, request.length); +#endif + + if (ntlmssp_state->unicode) { + parse_string = "CdBBUUUBd"; + } else { + parse_string = "CdBBAAABd"; + } + + /* zero these out */ + data_blob_free(&ntlmssp_state->lm_resp); + data_blob_free(&ntlmssp_state->nt_resp); + + ntlmssp_state->user = NULL; + ntlmssp_state->domain = NULL; + ntlmssp_state->workstation = NULL; + + /* now the NTLMSSP encoded auth hashes */ + if (!msrpc_parse(ntlmssp_state, + &request, parse_string, + "NTLMSSP", + &ntlmssp_command, + &ntlmssp_state->lm_resp, + &ntlmssp_state->nt_resp, + &domain, + &user, + &workstation, + &ntlmssp_state->encrypted_session_key, + &auth_flags)) { + DEBUG(10, ("ntlmssp_server_auth: failed to parse NTLMSSP (nonfatal):\n")); + dump_data(10, request.data, request.length); + + /* zero this out */ + data_blob_free(&ntlmssp_state->encrypted_session_key); + auth_flags = 0; + + /* Try again with a shorter string (Win9X truncates this packet) */ + if (ntlmssp_state->unicode) { + parse_string = "CdBBUUU"; + } else { + parse_string = "CdBBAAA"; + } + + /* now the NTLMSSP encoded auth hashes */ + if (!msrpc_parse(ntlmssp_state, + &request, parse_string, + "NTLMSSP", + &ntlmssp_command, + &ntlmssp_state->lm_resp, + &ntlmssp_state->nt_resp, + &domain, + &user, + &workstation)) { + DEBUG(1, ("ntlmssp_server_auth: failed to parse NTLMSSP:\n")); + dump_data(2, request.data, request.length); + + return NT_STATUS_INVALID_PARAMETER; + } + } + + if (auth_flags) + ntlmssp_handle_neg_flags(ntlmssp_state, auth_flags, ntlmssp_state->allow_lm_key); + + if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, domain))) { + /* zero this out */ + data_blob_free(&ntlmssp_state->encrypted_session_key); + return nt_status; + } + + if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, user))) { + /* zero this out */ + data_blob_free(&ntlmssp_state->encrypted_session_key); + return nt_status; + } + + if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_workstation(ntlmssp_state, workstation))) { + /* zero this out */ + data_blob_free(&ntlmssp_state->encrypted_session_key); + return nt_status; + } + + DEBUG(3,("Got user=[%s] domain=[%s] workstation=[%s] len1=%lu len2=%lu\n", + ntlmssp_state->user, ntlmssp_state->domain, ntlmssp_state->workstation, (unsigned long)ntlmssp_state->lm_resp.length, (unsigned long)ntlmssp_state->nt_resp.length)); + +#if 0 + file_save("nthash1.dat", &ntlmssp_state->nt_resp.data, &ntlmssp_state->nt_resp.length); + file_save("lmhash1.dat", &ntlmssp_state->lm_resp.data, &ntlmssp_state->lm_resp.length); +#endif + + /* NTLM2 uses a 'challenge' that is made of up both the server challenge, and a + client challenge + + However, the NTLM2 flag may still be set for the real NTLMv2 logins, be careful. + */ + if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { + if (ntlmssp_state->nt_resp.length == 24 && ntlmssp_state->lm_resp.length == 24) { + struct MD5Context md5_session_nonce_ctx; + SMB_ASSERT(ntlmssp_state->internal_chal.data + && ntlmssp_state->internal_chal.length == 8); + + ntlmssp_state->doing_ntlm2 = True; + + memcpy(ntlmssp_state->session_nonce, ntlmssp_state->internal_chal.data, 8); + memcpy(&ntlmssp_state->session_nonce[8], ntlmssp_state->lm_resp.data, 8); + + MD5Init(&md5_session_nonce_ctx); + MD5Update(&md5_session_nonce_ctx, ntlmssp_state->session_nonce, 16); + MD5Final(session_nonce_hash, &md5_session_nonce_ctx); + + ntlmssp_state->chal = data_blob_talloc(ntlmssp_state, + session_nonce_hash, 8); + + /* LM response is no longer useful, zero it out */ + data_blob_free(&ntlmssp_state->lm_resp); + + /* We changed the effective challenge - set it */ + if (!NT_STATUS_IS_OK(nt_status = + ntlmssp_state->set_challenge(ntlmssp_state, + &ntlmssp_state->chal))) { + /* zero this out */ + data_blob_free(&ntlmssp_state->encrypted_session_key); + return nt_status; + } + + /* LM Key is incompatible... */ + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; + } + } + return NT_STATUS_OK; +} + +/** + * Next state function for the Authenticate packet + * (after authentication - figures out the session keys etc) + * + * @param ntlmssp_state NTLMSSP State + * @return Errors or NT_STATUS_OK. + */ + +static NTSTATUS ntlmssp_server_postauth(struct ntlmssp_state *ntlmssp_state, + DATA_BLOB *user_session_key, + DATA_BLOB *lm_session_key) +{ + NTSTATUS nt_status; + DATA_BLOB session_key = data_blob(NULL, 0); + + if (user_session_key) + dump_data_pw("USER session key:\n", user_session_key->data, user_session_key->length); + + if (lm_session_key) + dump_data_pw("LM first-8:\n", lm_session_key->data, lm_session_key->length); + + /* Handle the different session key derivation for NTLM2 */ + if (ntlmssp_state->doing_ntlm2) { + if (user_session_key && user_session_key->data && user_session_key->length == 16) { + session_key = data_blob_talloc(ntlmssp_state, NULL, 16); + hmac_md5(user_session_key->data, ntlmssp_state->session_nonce, + sizeof(ntlmssp_state->session_nonce), session_key.data); + DEBUG(10,("ntlmssp_server_auth: Created NTLM2 session key.\n")); + dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length); + + } else { + DEBUG(10,("ntlmssp_server_auth: Failed to create NTLM2 session key.\n")); + session_key = data_blob(NULL, 0); + } + } else if ((ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) + /* Ensure we can never get here on NTLMv2 */ + && (ntlmssp_state->nt_resp.length == 0 || ntlmssp_state->nt_resp.length == 24)) { + + if (lm_session_key && lm_session_key->data && lm_session_key->length >= 8) { + if (ntlmssp_state->lm_resp.data && ntlmssp_state->lm_resp.length == 24) { + session_key = data_blob_talloc(ntlmssp_state, NULL, 16); + SMBsesskeygen_lm_sess_key(lm_session_key->data, ntlmssp_state->lm_resp.data, + session_key.data); + DEBUG(10,("ntlmssp_server_auth: Created NTLM session key.\n")); + dump_data_pw("LM session key:\n", session_key.data, session_key.length); + } else { + + /* When there is no LM response, just use zeros */ + static const uint8_t zeros[24]; + session_key = data_blob_talloc(ntlmssp_state, NULL, 16); + SMBsesskeygen_lm_sess_key(zeros, zeros, + session_key.data); + DEBUG(10,("ntlmssp_server_auth: Created NTLM session key.\n")); + dump_data_pw("LM session key:\n", session_key.data, session_key.length); + } + } else { + /* LM Key not selected */ + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; + + DEBUG(10,("ntlmssp_server_auth: Failed to create NTLM session key.\n")); + session_key = data_blob(NULL, 0); + } + + } else if (user_session_key && user_session_key->data) { + session_key = *user_session_key; + DEBUG(10,("ntlmssp_server_auth: Using unmodified nt session key.\n")); + dump_data_pw("unmodified session key:\n", session_key.data, session_key.length); + + /* LM Key not selected */ + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; + + } else if (lm_session_key && lm_session_key->data) { + /* Very weird to have LM key, but no user session key, but anyway.. */ + session_key = *lm_session_key; + DEBUG(10,("ntlmssp_server_auth: Using unmodified lm session key.\n")); + dump_data_pw("unmodified session key:\n", session_key.data, session_key.length); + + /* LM Key not selected */ + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; + + } else { + DEBUG(10,("ntlmssp_server_auth: Failed to create unmodified session key.\n")); + session_key = data_blob(NULL, 0); + + /* LM Key not selected */ + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; + } + + /* With KEY_EXCH, the client supplies the proposed session key, + but encrypts it with the long-term key */ + if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) { + if (!ntlmssp_state->encrypted_session_key.data + || ntlmssp_state->encrypted_session_key.length != 16) { + data_blob_free(&ntlmssp_state->encrypted_session_key); + DEBUG(1, ("Client-supplied KEY_EXCH session key was of invalid length (%u)!\n", + ntlmssp_state->encrypted_session_key.length)); + return NT_STATUS_INVALID_PARAMETER; + } else if (!session_key.data || session_key.length != 16) { + DEBUG(5, ("server session key is invalid (len == %u), cannot do KEY_EXCH!\n", + session_key.length)); + ntlmssp_state->session_key = session_key; + } else { + dump_data_pw("KEY_EXCH session key (enc):\n", + ntlmssp_state->encrypted_session_key.data, + ntlmssp_state->encrypted_session_key.length); + arcfour_crypt(ntlmssp_state->encrypted_session_key.data, + session_key.data, + ntlmssp_state->encrypted_session_key.length); + ntlmssp_state->session_key = data_blob_talloc(ntlmssp_state, + ntlmssp_state->encrypted_session_key.data, + ntlmssp_state->encrypted_session_key.length); + dump_data_pw("KEY_EXCH session key:\n", ntlmssp_state->encrypted_session_key.data, + ntlmssp_state->encrypted_session_key.length); + } + } else { + ntlmssp_state->session_key = session_key; + } + + /* The server might need us to use a partial-strength session key */ + ntlmssp_weaken_keys(ntlmssp_state); + + nt_status = ntlmssp_sign_init(ntlmssp_state); + + data_blob_free(&ntlmssp_state->encrypted_session_key); + + /* allow arbitarily many authentications, but watch that this will cause a + memory leak, until the ntlmssp_state is shutdown + */ + + if (ntlmssp_state->server_multiple_authentications) { + ntlmssp_state->expected_state = NTLMSSP_AUTH; + } else { + ntlmssp_state->expected_state = NTLMSSP_DONE; + } + + return nt_status; +} + + +/** + * Next state function for the Authenticate packet + * + * @param ntlmssp_state NTLMSSP State + * @param in The packet in from the NTLMSSP partner, as a DATA_BLOB + * @param out The reply, as an allocated DATA_BLOB, caller to free. + * @return Errors, NT_STATUS_MORE_PROCESSING_REQUIRED or NT_STATUS_OK. + */ + +static NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state, + TALLOC_CTX *out_mem_ctx, + const DATA_BLOB in, DATA_BLOB *out) +{ + DATA_BLOB user_session_key = data_blob(NULL, 0); + DATA_BLOB lm_session_key = data_blob(NULL, 0); + NTSTATUS nt_status; + + /* zero the outbound NTLMSSP packet */ + *out = data_blob_talloc(out_mem_ctx, NULL, 0); + + if (!NT_STATUS_IS_OK(nt_status = ntlmssp_server_preauth(ntlmssp_state, in))) { + return nt_status; + } + + /* + * Note we don't check here for NTLMv2 auth settings. If NTLMv2 auth + * is required (by "ntlm auth = no" and "lm auth = no" being set in the + * smb.conf file) and no NTLMv2 response was sent then the password check + * will fail here. JRA. + */ + + /* Finally, actually ask if the password is OK */ + + if (!NT_STATUS_IS_OK(nt_status = ntlmssp_state->check_password(ntlmssp_state, + &user_session_key, &lm_session_key))) { + return nt_status; + } + + if (ntlmssp_state->server_use_session_keys) { + return ntlmssp_server_postauth(ntlmssp_state, &user_session_key, &lm_session_key); + } else { + ntlmssp_state->session_key = data_blob(NULL, 0); + return NT_STATUS_OK; + } +} + +/** + * Create an NTLMSSP state machine + * + * @param ntlmssp_state NTLMSSP State, allocated by this function + */ + +NTSTATUS ntlmssp_server_start(TALLOC_CTX *mem_ctx, struct ntlmssp_state **ntlmssp_state) +{ + *ntlmssp_state = talloc(mem_ctx, struct ntlmssp_state); + if (!*ntlmssp_state) { + DEBUG(0,("ntlmssp_server_start: talloc failed!\n")); + return NT_STATUS_NO_MEMORY; + } + ZERO_STRUCTP(*ntlmssp_state); + + (*ntlmssp_state)->role = NTLMSSP_SERVER; + + (*ntlmssp_state)->get_challenge = get_challenge; + (*ntlmssp_state)->set_challenge = set_challenge; + (*ntlmssp_state)->may_set_challenge = may_set_challenge; + + (*ntlmssp_state)->workstation = NULL; + (*ntlmssp_state)->server_name = lp_netbios_name(); + + (*ntlmssp_state)->get_domain = lp_workgroup; + (*ntlmssp_state)->server_role = ROLE_DOMAIN_MEMBER; /* a good default */ + + (*ntlmssp_state)->expected_state = NTLMSSP_NEGOTIATE; + + (*ntlmssp_state)->allow_lm_key = (lp_lanman_auth() + && lp_parm_bool(-1, "ntlmssp_server", "allow_lm_key", False)); + + (*ntlmssp_state)->server_use_session_keys = True; + (*ntlmssp_state)->server_multiple_authentications = False; + + (*ntlmssp_state)->ref_count = 1; + + (*ntlmssp_state)->neg_flags = + NTLMSSP_NEGOTIATE_NTLM; + + if (lp_parm_bool(-1, "ntlmssp_server", "128bit", True)) { + (*ntlmssp_state)->neg_flags |= NTLMSSP_NEGOTIATE_128; + } + + if (lp_parm_bool(-1, "ntlmssp_server", "keyexchange", True)) { + (*ntlmssp_state)->neg_flags |= NTLMSSP_NEGOTIATE_KEY_EXCH; + } + + if (lp_parm_bool(-1, "ntlmssp_server", "ntlm2", True)) { + (*ntlmssp_state)->neg_flags |= NTLMSSP_NEGOTIATE_NTLM2; + } + + return NT_STATUS_OK; +} + +/********************************************************************* + Client side NTLMSSP +*********************************************************************/ + +/** + * Next state function for the Initial packet + * + * @param ntlmssp_state NTLMSSP State + * @param out_mem_ctx The DATA_BLOB *out will be allocated on this context + * @param in The request, as a DATA_BLOB. reply.data must be NULL + * @param out The reply, as an talloc()ed DATA_BLOB, on out_mem_ctx + * @return Errors or NT_STATUS_OK. + */ + +static NTSTATUS ntlmssp_client_initial(struct ntlmssp_state *ntlmssp_state, + TALLOC_CTX *out_mem_ctx, + DATA_BLOB in, DATA_BLOB *out) +{ + if (ntlmssp_state->unicode) { + ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE; + } else { + ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_OEM; + } + + if (ntlmssp_state->use_ntlmv2) { + ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_NTLM2; + } + + /* generate the ntlmssp negotiate packet */ + msrpc_gen(out_mem_ctx, + out, "CddAA", + "NTLMSSP", + NTLMSSP_NEGOTIATE, + ntlmssp_state->neg_flags, + ntlmssp_state->get_domain(), + ntlmssp_state->workstation); + + ntlmssp_state->expected_state = NTLMSSP_CHALLENGE; + + return NT_STATUS_MORE_PROCESSING_REQUIRED; +} + +/** + * Next state function for the Challenge Packet. Generate an auth packet. + * + * @param ntlmssp_state NTLMSSP State + * @param request The request, as a DATA_BLOB. reply.data must be NULL + * @param request The reply, as an allocated DATA_BLOB, caller to free. + * @return Errors or NT_STATUS_OK. + */ + +static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_state *ntlmssp_state, + TALLOC_CTX *out_mem_ctx, + const DATA_BLOB in, DATA_BLOB *out) +{ + uint32_t chal_flags, ntlmssp_command, unkn1, unkn2; + DATA_BLOB server_domain_blob; + DATA_BLOB challenge_blob; + DATA_BLOB struct_blob = data_blob(NULL, 0); + char *server_domain; + const char *chal_parse_string; + const char *auth_gen_string; + uint8_t lm_hash[16]; + DATA_BLOB lm_response = data_blob(NULL, 0); + DATA_BLOB nt_response = data_blob(NULL, 0); + DATA_BLOB session_key = data_blob(NULL, 0); + DATA_BLOB lm_session_key = data_blob(NULL, 0); + DATA_BLOB encrypted_session_key = data_blob(NULL, 0); + NTSTATUS nt_status; + + if (!msrpc_parse(ntlmssp_state, + &in, "CdBd", + "NTLMSSP", + &ntlmssp_command, + &server_domain_blob, + &chal_flags)) { + DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#1)\n")); + dump_data(2, in.data, in.length); + + return NT_STATUS_INVALID_PARAMETER; + } + + data_blob_free(&server_domain_blob); + + DEBUG(3, ("Got challenge flags:\n")); + debug_ntlmssp_flags(chal_flags); + + ntlmssp_handle_neg_flags(ntlmssp_state, chal_flags, ntlmssp_state->allow_lm_key); + + if (ntlmssp_state->unicode) { + if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) { + chal_parse_string = "CdUdbddB"; + } else { + chal_parse_string = "CdUdbdd"; + } + auth_gen_string = "CdBBUUUBd"; + } else { + if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) { + chal_parse_string = "CdAdbddB"; + } else { + chal_parse_string = "CdAdbdd"; + } + + auth_gen_string = "CdBBAAABd"; + } + + DEBUG(3, ("NTLMSSP: Set final flags:\n")); + debug_ntlmssp_flags(ntlmssp_state->neg_flags); + + if (!msrpc_parse(ntlmssp_state, + &in, chal_parse_string, + "NTLMSSP", + &ntlmssp_command, + &server_domain, + &chal_flags, + &challenge_blob, 8, + &unkn1, &unkn2, + &struct_blob)) { + DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#2)\n")); + dump_data(2, in.data, in.length); + return NT_STATUS_INVALID_PARAMETER; + } + + ntlmssp_state->server_domain = server_domain; + + if (challenge_blob.length != 8) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (!ntlmssp_state->password) { + static const uint8_t zeros[16]; + /* do nothing - blobs are zero length */ + + /* session key is all zeros */ + session_key = data_blob_talloc(ntlmssp_state, zeros, 16); + lm_session_key = data_blob_talloc(ntlmssp_state, zeros, 16); + + /* not doing NLTM2 without a password */ + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2; + } else if (ntlmssp_state->use_ntlmv2) { + + if (!struct_blob.length) { + /* be lazy, match win2k - we can't do NTLMv2 without it */ + DEBUG(1, ("Server did not provide 'target information', required for NTLMv2\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + /* TODO: if the remote server is standalone, then we should replace 'domain' + with the server name as supplied above */ + + if (!SMBNTLMv2encrypt(ntlmssp_state->user, + ntlmssp_state->domain, + ntlmssp_state->password, &challenge_blob, + &struct_blob, + &lm_response, &nt_response, + NULL, &session_key)) { + data_blob_free(&challenge_blob); + data_blob_free(&struct_blob); + return NT_STATUS_NO_MEMORY; + } + + /* LM Key is incompatible... */ + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; + + } else if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { + struct MD5Context md5_session_nonce_ctx; + uint8_t nt_hash[16]; + uint8_t session_nonce[16]; + uint8_t session_nonce_hash[16]; + uint8_t user_session_key[16]; + E_md4hash(ntlmssp_state->password, nt_hash); + + lm_response = data_blob_talloc(ntlmssp_state, NULL, 24); + generate_random_buffer(lm_response.data, 8); + memset(lm_response.data+8, 0, 16); + + memcpy(session_nonce, challenge_blob.data, 8); + memcpy(&session_nonce[8], lm_response.data, 8); + + MD5Init(&md5_session_nonce_ctx); + MD5Update(&md5_session_nonce_ctx, challenge_blob.data, 8); + MD5Update(&md5_session_nonce_ctx, lm_response.data, 8); + MD5Final(session_nonce_hash, &md5_session_nonce_ctx); + + DEBUG(5, ("NTLMSSP challenge set by NTLM2\n")); + DEBUG(5, ("challenge is: \n")); + dump_data(5, session_nonce_hash, 8); + + nt_response = data_blob_talloc(ntlmssp_state, NULL, 24); + SMBNTencrypt(ntlmssp_state->password, + session_nonce_hash, + nt_response.data); + + session_key = data_blob_talloc(ntlmssp_state, NULL, 16); + + SMBsesskeygen_ntv1(nt_hash, user_session_key); + hmac_md5(user_session_key, session_nonce, sizeof(session_nonce), session_key.data); + dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length); + + /* LM Key is incompatible... */ + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; + } else { + uint8_t nt_hash[16]; + + if (ntlmssp_state->use_nt_response) { + nt_response = data_blob_talloc(ntlmssp_state, NULL, 24); + SMBNTencrypt(ntlmssp_state->password,challenge_blob.data, + nt_response.data); + E_md4hash(ntlmssp_state->password, nt_hash); + session_key = data_blob_talloc(ntlmssp_state, NULL, 16); + SMBsesskeygen_ntv1(nt_hash, session_key.data); + dump_data_pw("NT session key:\n", session_key.data, session_key.length); + } + + /* lanman auth is insecure, it may be disabled */ + if (lp_client_lanman_auth()) { + lm_response = data_blob_talloc(ntlmssp_state, NULL, 24); + if (!SMBencrypt(ntlmssp_state->password,challenge_blob.data, + lm_response.data)) { + /* If the LM password was too long (and therefore the LM hash being + of the first 14 chars only), don't send it */ + data_blob_free(&lm_response); + + /* LM Key is incompatible with 'long' passwords */ + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; + } else { + E_deshash(ntlmssp_state->password, lm_hash); + lm_session_key = data_blob_talloc(ntlmssp_state, NULL, 16); + memcpy(lm_session_key.data, lm_hash, 8); + memset(&lm_session_key.data[8], '\0', 8); + + if (!ntlmssp_state->use_nt_response) { + session_key = lm_session_key; + } + } + } else { + /* LM Key is incompatible... */ + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; + } + } + + if ((ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) + && lp_client_lanman_auth() && lm_session_key.length == 16) { + DATA_BLOB new_session_key = data_blob_talloc(ntlmssp_state, NULL, 16); + if (lm_response.length == 24) { + SMBsesskeygen_lm_sess_key(lm_session_key.data, lm_response.data, + new_session_key.data); + } else { + static const uint8_t zeros[24]; + SMBsesskeygen_lm_sess_key(lm_session_key.data, zeros, + new_session_key.data); + } + new_session_key.length = 16; + session_key = new_session_key; + dump_data_pw("LM session key\n", session_key.data, session_key.length); + } + + + /* Key exchange encryptes a new client-generated session key with + the password-derived key */ + if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) { + /* Make up a new session key */ + uint8_t client_session_key[16]; + generate_random_buffer(client_session_key, sizeof(client_session_key)); + + /* Encrypt the new session key with the old one */ + encrypted_session_key = data_blob_talloc(ntlmssp_state, + client_session_key, sizeof(client_session_key)); + dump_data_pw("KEY_EXCH session key:\n", encrypted_session_key.data, encrypted_session_key.length); + arcfour_crypt(encrypted_session_key.data, session_key.data, encrypted_session_key.length); + dump_data_pw("KEY_EXCH session key (enc):\n", encrypted_session_key.data, encrypted_session_key.length); + + /* Mark the new session key as the 'real' session key */ + session_key = data_blob_talloc(ntlmssp_state, client_session_key, sizeof(client_session_key)); + } + + /* this generates the actual auth packet */ + if (!msrpc_gen(out_mem_ctx, + out, auth_gen_string, + "NTLMSSP", + NTLMSSP_AUTH, + lm_response.data, lm_response.length, + nt_response.data, nt_response.length, + ntlmssp_state->domain, + ntlmssp_state->user, + ntlmssp_state->workstation, + encrypted_session_key.data, encrypted_session_key.length, + ntlmssp_state->neg_flags)) { + + return NT_STATUS_NO_MEMORY; + } + + ntlmssp_state->session_key = session_key; + + /* The client might be using 56 or 40 bit weakened keys */ + ntlmssp_weaken_keys(ntlmssp_state); + + ntlmssp_state->chal = challenge_blob; + ntlmssp_state->lm_resp = lm_response; + ntlmssp_state->nt_resp = nt_response; + + ntlmssp_state->expected_state = NTLMSSP_DONE; + + nt_status = ntlmssp_sign_init(ntlmssp_state); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(1, ("Could not setup NTLMSSP signing/sealing system (error was: %s)\n", + nt_errstr(nt_status))); + return nt_status; + } + + return nt_status; +} + +NTSTATUS ntlmssp_client_start(TALLOC_CTX *mem_ctx, struct ntlmssp_state **ntlmssp_state) +{ + *ntlmssp_state = talloc(mem_ctx, struct ntlmssp_state); + if (!*ntlmssp_state) { + DEBUG(0,("ntlmssp_client_start: talloc failed!\n")); + return NT_STATUS_NO_MEMORY; + } + ZERO_STRUCTP(*ntlmssp_state); + + (*ntlmssp_state)->role = NTLMSSP_CLIENT; + + (*ntlmssp_state)->workstation = lp_netbios_name(); + (*ntlmssp_state)->get_domain = lp_workgroup; + + (*ntlmssp_state)->unicode = lp_parm_bool(-1, "ntlmssp_client", "unicode", True); + + (*ntlmssp_state)->use_nt_response = lp_parm_bool(-1, "ntlmssp_client", "send_nt_reponse", True); + + (*ntlmssp_state)->allow_lm_key = (lp_lanman_auth() + && lp_parm_bool(-1, "ntlmssp_client", "allow_lm_key", False)); + + (*ntlmssp_state)->use_ntlmv2 = lp_client_ntlmv2_auth(); + + (*ntlmssp_state)->expected_state = NTLMSSP_INITIAL; + + (*ntlmssp_state)->ref_count = 1; + + (*ntlmssp_state)->neg_flags = + NTLMSSP_NEGOTIATE_NTLM | + NTLMSSP_REQUEST_TARGET; + + if (lp_parm_bool(-1, "ntlmssp_client", "128bit", True)) { + (*ntlmssp_state)->neg_flags |= NTLMSSP_NEGOTIATE_128; + } + + if (lp_parm_bool(-1, "ntlmssp_client", "keyexchange", True)) { + (*ntlmssp_state)->neg_flags |= NTLMSSP_NEGOTIATE_KEY_EXCH; + } + + if (lp_parm_bool(-1, "ntlmssp_client", "ntlm2", True)) { + (*ntlmssp_state)->neg_flags |= NTLMSSP_NEGOTIATE_NTLM2; + } else { + /* apparently we can't do ntlmv2 if we don't do ntlm2 */ + (*ntlmssp_state)->use_ntlmv2 = False; + } + + return NT_STATUS_OK; +} + diff --git a/source4/auth/gensec/ntlmssp.h b/source4/auth/gensec/ntlmssp.h new file mode 100644 index 0000000000..e17c133c8b --- /dev/null +++ b/source4/auth/gensec/ntlmssp.h @@ -0,0 +1,190 @@ +/* + Unix SMB/CIFS implementation. + SMB parameters and setup + Copyright (C) Andrew Tridgell 1992-1997 + Copyright (C) Luke Kenneth Casson Leighton 1996-1997 + Copyright (C) Paul Ashton 1997 + + 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 "librpc/gen_ndr/ndr_samr.h" + +/* NTLMSSP mode */ +enum ntlmssp_role +{ + NTLMSSP_SERVER, + NTLMSSP_CLIENT +}; + +/* NTLMSSP message types */ +enum ntlmssp_message_type +{ + NTLMSSP_INITIAL = 0 /* samba internal state */, + NTLMSSP_NEGOTIATE = 1, + NTLMSSP_CHALLENGE = 2, + NTLMSSP_AUTH = 3, + NTLMSSP_UNKNOWN = 4, + NTLMSSP_DONE = 5 /* samba final state */ +}; + +/* NTLMSSP negotiation flags */ +#define NTLMSSP_NEGOTIATE_UNICODE 0x00000001 +#define NTLMSSP_NEGOTIATE_OEM 0x00000002 +#define NTLMSSP_REQUEST_TARGET 0x00000004 +#define NTLMSSP_NEGOTIATE_SIGN 0x00000010 /* Message integrity */ +#define NTLMSSP_NEGOTIATE_SEAL 0x00000020 /* Message confidentiality */ +#define NTLMSSP_NEGOTIATE_DATAGRAM_STYLE 0x00000040 +#define NTLMSSP_NEGOTIATE_LM_KEY 0x00000080 +#define NTLMSSP_NEGOTIATE_NETWARE 0x00000100 +#define NTLMSSP_NEGOTIATE_NTLM 0x00000200 +#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x00001000 +#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x00002000 +#define NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL 0x00004000 +#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x00008000 +#define NTLMSSP_TARGET_TYPE_DOMAIN 0x10000 +#define NTLMSSP_TARGET_TYPE_SERVER 0x20000 +#define NTLMSSP_CHAL_INIT_RESPONSE 0x00010000 + +#define NTLMSSP_CHAL_ACCEPT_RESPONSE 0x00020000 +#define NTLMSSP_CHAL_NON_NT_SESSION_KEY 0x00040000 +#define NTLMSSP_NEGOTIATE_NTLM2 0x00080000 +#define NTLMSSP_CHAL_TARGET_INFO 0x00800000 +#define NTLMSSP_NEGOTIATE_128 0x20000000 /* 128-bit encryption */ +#define NTLMSSP_NEGOTIATE_KEY_EXCH 0x40000000 +#define NTLMSSP_NEGOTIATE_56 0x80000000 + +#define NTLMSSP_NAME_TYPE_SERVER 0x01 +#define NTLMSSP_NAME_TYPE_DOMAIN 0x02 +#define NTLMSSP_NAME_TYPE_SERVER_DNS 0x03 +#define NTLMSSP_NAME_TYPE_DOMAIN_DNS 0x04 + +#define NTLMSSP_SIGN_VERSION 1 + +#define NTLMSSP_SIG_SIZE 16 + +struct ntlmssp_state +{ + uint_t ref_count; + enum ntlmssp_role role; + enum samr_Role server_role; + uint32_t expected_state; + + BOOL unicode; + BOOL use_ntlmv2; + BOOL use_nt_response; /* Set to 'False' to debug what happens when the NT response is omited */ + BOOL allow_lm_key; /* The LM_KEY code is not functional at this point, and it's not + very secure anyway */ + + BOOL server_use_session_keys; /* Set to 'False' for authentication only, + that will never return a session key */ + BOOL server_multiple_authentications; /* Set to 'True' to allow squid 2.5 + style 'challenge caching' */ + + char *user; + char *domain; + const char *workstation; + char *password; + char *server_domain; + + DATA_BLOB internal_chal; /* Random challenge as supplied to the client for NTLM authentication */ + + DATA_BLOB chal; /* Random challenge as input into the actual NTLM (or NTLM2) authentication */ + DATA_BLOB lm_resp; + DATA_BLOB nt_resp; + DATA_BLOB session_key; + + uint32_t neg_flags; /* the current state of negotiation with the NTLMSSP partner */ + + /* internal variables used by NTLM2 */ + BOOL doing_ntlm2; + uint8_t session_nonce[16]; + + /* internal variables used by KEY_EXCH (client-supplied user session key */ + DATA_BLOB encrypted_session_key; + + void *auth_context; + + /** + * Callback to get the 'challenge' used for NTLM authentication. + * + * @param ntlmssp_state This structure + * @return 8 bytes of challenge data, determined by the server to be the challenge for NTLM authentication + * + */ + const uint8_t *(*get_challenge)(const struct ntlmssp_state *ntlmssp_state); + + /** + * Callback to find if the challenge used by NTLM authentication may be modified + * + * The NTLM2 authentication scheme modifies the effective challenge, but this is not compatiable with the + * current 'security=server' implementation.. + * + * @param ntlmssp_state This structure + * @return Can the challenge be set to arbitary values? + * + */ + BOOL (*may_set_challenge)(const struct ntlmssp_state *ntlmssp_state); + + /** + * Callback to set the 'challenge' used for NTLM authentication. + * + * The callback may use the void *auth_context to store state information, but the same value is always available + * from the DATA_BLOB chal on this structure. + * + * @param ntlmssp_state This structure + * @param challange 8 bytes of data, agreed by the client and server to be the effective challenge for NTLM2 authentication + * + */ + NTSTATUS (*set_challenge)(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *challenge); + + /** + * Callback to check the user's password. + * + * The callback must reads the feilds of this structure for the information it needs on the user + * @param ntlmssp_state This structure + * @param nt_session_key If an NT session key is returned by the authentication process, return it here + * @param lm_session_key If an LM session key is returned by the authentication process, return it here + * + */ + NTSTATUS (*check_password)(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *nt_session_key, DATA_BLOB *lm_session_key); + + const char *server_name; + const char *(*get_domain)(void); + + /* SMB Signing */ + uint32_t ntlm_seq_num; + uint32_t ntlm2_send_seq_num; + uint32_t ntlm2_recv_seq_num; + + /* ntlmv2 */ + DATA_BLOB send_sign_key; + DATA_BLOB send_seal_key; + DATA_BLOB recv_sign_key; + DATA_BLOB recv_seal_key; + + uint8_t send_seal_hash[258]; + uint8_t recv_seal_hash[258]; + + /* ntlmv1 */ + uint8_t ntlmssp_hash[258]; + + /* it turns out that we don't always get the + response in at the time we want to process it. + Store it here, until we need it */ + DATA_BLOB stored_response; + +}; + diff --git a/source4/auth/gensec/ntlmssp_parse.c b/source4/auth/gensec/ntlmssp_parse.c new file mode 100644 index 0000000000..42546cb130 --- /dev/null +++ b/source4/auth/gensec/ntlmssp_parse.c @@ -0,0 +1,338 @@ +/* + Unix SMB/CIFS implementation. + simple kerberos5/SPNEGO routines + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Jim McDonough 2002 + Copyright (C) Andrew Bartlett 2002-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" +#include "pstring.h" + +/* + this is a tiny msrpc packet generator. I am only using this to + avoid tying this code to a particular varient of our rpc code. This + generator is not general enough for all our rpc needs, its just + enough for the spnego/ntlmssp code + + format specifiers are: + + U = unicode string (input is unix string) + a = address (input is char *unix_string) + (1 byte type, 1 byte length, unicode/ASCII string, all inline) + A = ASCII string (input is unix string) + B = data blob (pointer + length) + b = data blob in header (pointer + length) + D + d = word (4 bytes) + C = constant ascii string + */ +BOOL msrpc_gen(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, + const char *format, ...) +{ + int i; + ssize_t n; + va_list ap; + char *s; + uint8_t *b; + int head_size=0, data_size=0; + int head_ofs, data_ofs; + int *intargs; + + DATA_BLOB *pointers; + + pointers = talloc_array(mem_ctx, DATA_BLOB, strlen(format)); + intargs = talloc_array(pointers, int, strlen(format)); + + /* first scan the format to work out the header and body size */ + va_start(ap, format); + for (i=0; format[i]; i++) { + switch (format[i]) { + case 'U': + s = va_arg(ap, char *); + head_size += 8; + n = push_ucs2_talloc(pointers, (void **)&pointers[i].data, s); + if (n == -1) { + return False; + } + pointers[i].length = n; + pointers[i].length -= 2; + data_size += pointers[i].length; + break; + case 'A': + s = va_arg(ap, char *); + head_size += 8; + n = push_ascii_talloc(pointers, (char **)&pointers[i].data, s); + if (n == -1) { + return False; + } + pointers[i].length = n; + pointers[i].length -= 1; + data_size += pointers[i].length; + break; + case 'a': + n = va_arg(ap, int); + intargs[i] = n; + s = va_arg(ap, char *); + n = push_ucs2_talloc(pointers, (void **)&pointers[i].data, s); + if (n == -1) { + return False; + } + pointers[i].length = n; + pointers[i].length -= 2; + data_size += pointers[i].length + 4; + break; + case 'B': + b = va_arg(ap, uint8_t *); + head_size += 8; + pointers[i].data = b; + pointers[i].length = va_arg(ap, int); + data_size += pointers[i].length; + break; + case 'b': + b = va_arg(ap, uint8_t *); + pointers[i].data = b; + pointers[i].length = va_arg(ap, int); + head_size += pointers[i].length; + break; + case 'd': + n = va_arg(ap, int); + intargs[i] = n; + head_size += 4; + break; + case 'C': + s = va_arg(ap, char *); + pointers[i].data = (uint8_t *)s; + pointers[i].length = strlen(s)+1; + head_size += pointers[i].length; + break; + } + } + va_end(ap); + + /* allocate the space, then scan the format again to fill in the values */ + *blob = data_blob_talloc(mem_ctx, NULL, head_size + data_size); + + head_ofs = 0; + data_ofs = head_size; + + va_start(ap, format); + for (i=0; format[i]; i++) { + switch (format[i]) { + case 'U': + case 'A': + case 'B': + n = pointers[i].length; + SSVAL(blob->data, head_ofs, n); head_ofs += 2; + SSVAL(blob->data, head_ofs, n); head_ofs += 2; + SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4; + if (pointers[i].data && n) /* don't follow null pointers... */ + memcpy(blob->data+data_ofs, pointers[i].data, n); + data_ofs += n; + break; + case 'a': + n = intargs[i]; + SSVAL(blob->data, data_ofs, n); data_ofs += 2; + + n = pointers[i].length; + SSVAL(blob->data, data_ofs, n); data_ofs += 2; + if (n >= 0) { + memcpy(blob->data+data_ofs, pointers[i].data, n); + } + data_ofs += n; + break; + case 'd': + n = intargs[i]; + SIVAL(blob->data, head_ofs, n); + head_ofs += 4; + break; + case 'b': + n = pointers[i].length; + memcpy(blob->data + head_ofs, pointers[i].data, n); + head_ofs += n; + break; + case 'C': + n = pointers[i].length; + memcpy(blob->data + head_ofs, pointers[i].data, n); + head_ofs += n; + break; + } + } + va_end(ap); + + talloc_free(pointers); + + return True; +} + + +/* a helpful macro to avoid running over the end of our blob */ +#define NEED_DATA(amount) \ +if ((head_ofs + amount) > blob->length) { \ + return False; \ +} + +/* + this is a tiny msrpc packet parser. This the the partner of msrpc_gen + + format specifiers are: + + U = unicode string (output is unix string) + A = ascii string + B = data blob + b = data blob in header + d = word (4 bytes) + C = constant ascii string + */ + +BOOL msrpc_parse(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob, + const char *format, ...) +{ + int i; + va_list ap; + const char **ps, *s; + DATA_BLOB *b; + size_t head_ofs = 0; + uint16_t len1, len2; + uint32_t ptr; + uint32_t *v; + pstring p; + + va_start(ap, format); + for (i=0; format[i]; i++) { + switch (format[i]) { + case 'U': + NEED_DATA(8); + len1 = SVAL(blob->data, head_ofs); head_ofs += 2; + len2 = SVAL(blob->data, head_ofs); head_ofs += 2; + ptr = IVAL(blob->data, head_ofs); head_ofs += 4; + + ps = (const char **)va_arg(ap, char **); + if (len1 == 0 && len2 == 0) { + *ps = ""; + } else { + /* make sure its in the right format - be strict */ + if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) { + return False; + } + if (len1 & 1) { + /* if odd length and unicode */ + return False; + } + if (blob->data + ptr < (uint8_t *)ptr || blob->data + ptr < blob->data) + return False; + + if (0 < len1) { + pull_string(p, blob->data + ptr, sizeof(p), + len1, + STR_UNICODE|STR_NOALIGN); + (*ps) = talloc_strdup(mem_ctx, p); + if (!(*ps)) { + return False; + } + } else { + (*ps) = ""; + } + } + break; + case 'A': + NEED_DATA(8); + len1 = SVAL(blob->data, head_ofs); head_ofs += 2; + len2 = SVAL(blob->data, head_ofs); head_ofs += 2; + ptr = IVAL(blob->data, head_ofs); head_ofs += 4; + + ps = (const char **)va_arg(ap, char **); + /* make sure its in the right format - be strict */ + if (len1 == 0 && len2 == 0) { + *ps = ""; + } else { + if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) { + return False; + } + + if (blob->data + ptr < (uint8_t *)ptr || blob->data + ptr < blob->data) + return False; + + if (0 < len1) { + pull_string(p, blob->data + ptr, sizeof(p), + len1, + STR_ASCII|STR_NOALIGN); + (*ps) = talloc_strdup(mem_ctx, p); + if (!(*ps)) { + return False; + } + } else { + (*ps) = ""; + } + } + break; + case 'B': + NEED_DATA(8); + len1 = SVAL(blob->data, head_ofs); head_ofs += 2; + len2 = SVAL(blob->data, head_ofs); head_ofs += 2; + ptr = IVAL(blob->data, head_ofs); head_ofs += 4; + + b = (DATA_BLOB *)va_arg(ap, void *); + if (len1 == 0 && len2 == 0) { + *b = data_blob_talloc(mem_ctx, NULL, 0); + } else { + /* make sure its in the right format - be strict */ + if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) { + return False; + } + + if (blob->data + ptr < (uint8_t *)ptr || blob->data + ptr < blob->data) + return False; + + *b = data_blob_talloc(mem_ctx, blob->data + ptr, len1); + } + break; + case 'b': + b = (DATA_BLOB *)va_arg(ap, void *); + len1 = va_arg(ap, uint_t); + /* make sure its in the right format - be strict */ + NEED_DATA(len1); + if (blob->data + head_ofs < (uint8_t *)head_ofs || blob->data + head_ofs < blob->data) + return False; + + *b = data_blob_talloc(mem_ctx, blob->data + head_ofs, len1); + head_ofs += len1; + break; + case 'd': + v = va_arg(ap, uint32_t *); + NEED_DATA(4); + *v = IVAL(blob->data, head_ofs); head_ofs += 4; + break; + case 'C': + s = va_arg(ap, char *); + + if (blob->data + head_ofs < (uint8_t *)head_ofs || blob->data + head_ofs < blob->data) + return False; + + head_ofs += pull_string(p, blob->data+head_ofs, sizeof(p), + blob->length - head_ofs, + STR_ASCII|STR_TERMINATE); + if (strcmp(s, p) != 0) { + return False; + } + break; + } + } + va_end(ap); + + return True; +} diff --git a/source4/auth/gensec/ntlmssp_sign.c b/source4/auth/gensec/ntlmssp_sign.c new file mode 100644 index 0000000000..347a85da77 --- /dev/null +++ b/source4/auth/gensec/ntlmssp_sign.c @@ -0,0 +1,449 @@ +/* + * Unix SMB/CIFS implementation. + * Version 3.0 + * NTLMSSP Signing routines + * Copyright (C) Luke Kenneth Casson Leighton 1996-2001 + * Copyright (C) Andrew Bartlett 2003-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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "includes.h" +#include "auth/auth.h" +#include "lib/crypto/crypto.h" + +#define CLI_SIGN "session key to client-to-server signing key magic constant" +#define CLI_SEAL "session key to client-to-server sealing key magic constant" +#define SRV_SIGN "session key to server-to-client signing key magic constant" +#define SRV_SEAL "session key to server-to-client sealing key magic constant" + +/** + * Some notes on then NTLM2 code: + * + * This code works correctly for the sealing part of the problem. If + * we disable the check for valid client signatures, then we see that + * the output of a rpcecho 'sinkdata' at smbd is correct. We get the + * valid data, and it is validly decrypted. + * + * This means that the quantity of data passing though the RC4 sealing + * pad is correct. + * + * This code also correctly matches test values that I have obtained, + * claiming to be the correct output of NTLM2 signature generation. + * + */ + +static void calc_ntlmv2_key(TALLOC_CTX *mem_ctx, + DATA_BLOB *subkey, + DATA_BLOB session_key, + const char *constant) +{ + struct MD5Context ctx3; + *subkey = data_blob_talloc(mem_ctx, NULL, 16); + MD5Init(&ctx3); + MD5Update(&ctx3, session_key.data, session_key.length); + MD5Update(&ctx3, constant, strlen(constant)+1); + MD5Final(subkey->data, &ctx3); +} + +enum ntlmssp_direction { + NTLMSSP_SEND, + NTLMSSP_RECEIVE +}; + +static NTSTATUS ntlmssp_make_packet_signature(struct ntlmssp_state *ntlmssp_state, + TALLOC_CTX *sig_mem_ctx, + const uint8_t *data, size_t length, + const uint8_t *whole_pdu, size_t pdu_length, + enum ntlmssp_direction direction, + DATA_BLOB *sig, BOOL encrypt_sig) +{ + if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { + + HMACMD5Context ctx; + uint8_t digest[16]; + uint8_t seq_num[4]; + + *sig = data_blob_talloc(sig_mem_ctx, NULL, NTLMSSP_SIG_SIZE); + if (!sig->data) { + return NT_STATUS_NO_MEMORY; + } + + switch (direction) { + case NTLMSSP_SEND: + SIVAL(seq_num, 0, ntlmssp_state->ntlm2_send_seq_num); + ntlmssp_state->ntlm2_send_seq_num++; + hmac_md5_init_limK_to_64(ntlmssp_state->send_sign_key.data, + ntlmssp_state->send_sign_key.length, &ctx); + break; + case NTLMSSP_RECEIVE: + SIVAL(seq_num, 0, ntlmssp_state->ntlm2_recv_seq_num); + ntlmssp_state->ntlm2_recv_seq_num++; + hmac_md5_init_limK_to_64(ntlmssp_state->recv_sign_key.data, + ntlmssp_state->recv_sign_key.length, &ctx); + break; + } + hmac_md5_update(seq_num, sizeof(seq_num), &ctx); + hmac_md5_update(whole_pdu, pdu_length, &ctx); + hmac_md5_final(digest, &ctx); + + if (encrypt_sig && ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) { + switch (direction) { + case NTLMSSP_SEND: + arcfour_crypt_sbox(ntlmssp_state->send_seal_hash, digest, 8); + break; + case NTLMSSP_RECEIVE: + arcfour_crypt_sbox(ntlmssp_state->recv_seal_hash, digest, 8); + break; + } + } + + SIVAL(sig->data, 0, NTLMSSP_SIGN_VERSION); + memcpy(sig->data + 4, digest, 8); + memcpy(sig->data + 12, seq_num, 4); + + } else { + uint32_t crc; + crc = crc32_calc_buffer(data, length); + if (!msrpc_gen(sig_mem_ctx, sig, "dddd", NTLMSSP_SIGN_VERSION, 0, crc, ntlmssp_state->ntlm_seq_num)) { + return NT_STATUS_NO_MEMORY; + } + ntlmssp_state->ntlm_seq_num++; + + arcfour_crypt_sbox(ntlmssp_state->ntlmssp_hash, sig->data+4, sig->length-4); + } + dump_data_pw("calculated ntlmssp signature\n", sig->data, sig->length); + return NT_STATUS_OK; +} + +NTSTATUS ntlmssp_sign_packet(struct ntlmssp_state *ntlmssp_state, + TALLOC_CTX *sig_mem_ctx, + const uint8_t *data, size_t length, + const uint8_t *whole_pdu, size_t pdu_length, + DATA_BLOB *sig) +{ + if (!ntlmssp_state->session_key.length) { + DEBUG(3, ("NO session key, cannot check sign packet\n")); + return NT_STATUS_NO_USER_SESSION_KEY; + } + + if (!ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SIGN) { + DEBUG(3, ("NTLMSSP Signing not negotiated - cannot sign packet!\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + return ntlmssp_make_packet_signature(ntlmssp_state, sig_mem_ctx, + data, length, + whole_pdu, pdu_length, + NTLMSSP_SEND, sig, True); +} + +/** + * Check the signature of an incoming packet + * + */ + +NTSTATUS ntlmssp_check_packet(struct ntlmssp_state *ntlmssp_state, + TALLOC_CTX *sig_mem_ctx, + const uint8_t *data, size_t length, + const uint8_t *whole_pdu, size_t pdu_length, + const DATA_BLOB *sig) +{ + DATA_BLOB local_sig; + NTSTATUS nt_status; + + if (!ntlmssp_state->session_key.length) { + DEBUG(3, ("NO session key, cannot check packet signature\n")); + return NT_STATUS_NO_USER_SESSION_KEY; + } + + if (sig->length < 8) { + DEBUG(0, ("NTLMSSP packet check failed due to short signature (%lu bytes)!\n", + (unsigned long)sig->length)); + } + + nt_status = ntlmssp_make_packet_signature(ntlmssp_state, sig_mem_ctx, + data, length, + whole_pdu, pdu_length, + NTLMSSP_RECEIVE, &local_sig, True); + + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0, ("NTLMSSP packet check failed with %s\n", nt_errstr(nt_status))); + return nt_status; + } + + if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { + if (local_sig.length != sig->length || + memcmp(local_sig.data, + sig->data, sig->length) != 0) { + DEBUG(5, ("BAD SIG NTLM2: wanted signature of\n")); + dump_data(5, local_sig.data, local_sig.length); + + DEBUG(5, ("BAD SIG: got signature of\n")); + dump_data(5, sig->data, sig->length); + + DEBUG(0, ("NTLMSSP NTLM2 packet check failed due to invalid signature!\n")); + return NT_STATUS_ACCESS_DENIED; + } + } else { + if (local_sig.length != sig->length || + memcmp(local_sig.data + 8, + sig->data + 8, sig->length - 8) != 0) { + DEBUG(5, ("BAD SIG NTLM1: wanted signature of\n")); + dump_data(5, local_sig.data, local_sig.length); + + DEBUG(5, ("BAD SIG: got signature of\n")); + dump_data(5, sig->data, sig->length); + + DEBUG(0, ("NTLMSSP NTLM1 packet check failed due to invalid signature!\n")); + return NT_STATUS_ACCESS_DENIED; + } + } + dump_data_pw("checked ntlmssp signature\n", sig->data, sig->length); + + return NT_STATUS_OK; +} + + +/** + * Seal data with the NTLMSSP algorithm + * + */ + +NTSTATUS ntlmssp_seal_packet(struct ntlmssp_state *ntlmssp_state, + TALLOC_CTX *sig_mem_ctx, + uint8_t *data, size_t length, + const uint8_t *whole_pdu, size_t pdu_length, + DATA_BLOB *sig) +{ + NTSTATUS nt_status; + if (!ntlmssp_state->session_key.length) { + DEBUG(3, ("NO session key, cannot seal packet\n")); + return NT_STATUS_NO_USER_SESSION_KEY; + } + + if (!ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SEAL) { + DEBUG(3, ("NTLMSSP Sealing not negotiated - cannot seal packet!\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + DEBUG(10,("ntlmssp_seal_data: seal\n")); + dump_data_pw("ntlmssp clear data\n", data, length); + if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { + /* The order of these two operations matters - we must first seal the packet, + then seal the sequence number - this is becouse the send_seal_hash is not + constant, but is is rather updated with each iteration */ + nt_status = ntlmssp_make_packet_signature(ntlmssp_state, sig_mem_ctx, + data, length, + whole_pdu, pdu_length, + NTLMSSP_SEND, sig, False); + arcfour_crypt_sbox(ntlmssp_state->send_seal_hash, data, length); + if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) { + arcfour_crypt_sbox(ntlmssp_state->send_seal_hash, sig->data+4, 8); + } + } else { + uint32_t crc; + crc = crc32_calc_buffer(data, length); + if (!msrpc_gen(sig_mem_ctx, sig, "dddd", NTLMSSP_SIGN_VERSION, 0, crc, ntlmssp_state->ntlm_seq_num)) { + return NT_STATUS_NO_MEMORY; + } + + /* The order of these two operations matters - we must + first seal the packet, then seal the sequence + number - this is becouse the ntlmssp_hash is not + constant, but is is rather updated with each + iteration */ + + arcfour_crypt_sbox(ntlmssp_state->ntlmssp_hash, data, length); + arcfour_crypt_sbox(ntlmssp_state->ntlmssp_hash, sig->data+4, sig->length-4); + /* increment counter on send */ + ntlmssp_state->ntlm_seq_num++; + nt_status = NT_STATUS_OK; + } + dump_data_pw("ntlmssp signature\n", sig->data, sig->length); + dump_data_pw("ntlmssp sealed data\n", data, length); + + + return nt_status; +} + +/** + * Unseal data with the NTLMSSP algorithm + * + */ + +NTSTATUS ntlmssp_unseal_packet(struct ntlmssp_state *ntlmssp_state, + TALLOC_CTX *sig_mem_ctx, + uint8_t *data, size_t length, + const uint8_t *whole_pdu, size_t pdu_length, + DATA_BLOB *sig) +{ + DATA_BLOB local_sig; + NTSTATUS nt_status; + if (!ntlmssp_state->session_key.length) { + DEBUG(3, ("NO session key, cannot unseal packet\n")); + return NT_STATUS_NO_USER_SESSION_KEY; + } + + dump_data_pw("ntlmssp sealed data\n", data, length); + if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { + arcfour_crypt_sbox(ntlmssp_state->recv_seal_hash, data, length); + + nt_status = ntlmssp_make_packet_signature(ntlmssp_state, sig_mem_ctx, + data, length, + whole_pdu, pdu_length, + NTLMSSP_RECEIVE, &local_sig, True); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + if (local_sig.length != sig->length || + memcmp(local_sig.data, + sig->data, sig->length) != 0) { + DEBUG(5, ("BAD SIG NTLM2: wanted signature of\n")); + dump_data(5, local_sig.data, local_sig.length); + + DEBUG(5, ("BAD SIG: got signature of\n")); + dump_data(5, sig->data, sig->length); + + DEBUG(0, ("NTLMSSP NTLM2 packet check failed due to invalid signature!\n")); + return NT_STATUS_ACCESS_DENIED; + } + + dump_data_pw("ntlmssp clear data\n", data, length); + return NT_STATUS_OK; + } else { + arcfour_crypt_sbox(ntlmssp_state->ntlmssp_hash, data, length); + dump_data_pw("ntlmssp clear data\n", data, length); + return ntlmssp_check_packet(ntlmssp_state, sig_mem_ctx, data, length, whole_pdu, pdu_length, sig); + } +} + +/** + Initialise the state for NTLMSSP signing. +*/ +NTSTATUS ntlmssp_sign_init(struct ntlmssp_state *ntlmssp_state) +{ + uint8_t p24[24]; + ZERO_STRUCT(p24); + + DEBUG(3, ("NTLMSSP Sign/Seal - Initialising with flags:\n")); + debug_ntlmssp_flags(ntlmssp_state->neg_flags); + + if (!ntlmssp_state->session_key.length) { + DEBUG(3, ("NO session key, cannot intialise signing\n")); + return NT_STATUS_NO_USER_SESSION_KEY; + } + + if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) + { + DATA_BLOB weak_session_key = ntlmssp_state->session_key; + const char *send_sign_const; + const char *send_seal_const; + const char *recv_sign_const; + const char *recv_seal_const; + + switch (ntlmssp_state->role) { + case NTLMSSP_CLIENT: + send_sign_const = CLI_SIGN; + send_seal_const = CLI_SEAL; + recv_sign_const = SRV_SIGN; + recv_seal_const = SRV_SEAL; + break; + case NTLMSSP_SERVER: + send_sign_const = SRV_SIGN; + send_seal_const = SRV_SEAL; + recv_sign_const = CLI_SIGN; + recv_seal_const = CLI_SEAL; + break; + default: + return NT_STATUS_INTERNAL_ERROR; + } + + /** + Weaken NTLMSSP keys to cope with down-level clients, servers and export restrictions. + + We probably should have some parameters to control this, once we get NTLM2 working. + */ + + + if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_128) { + + } else if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_56) { + weak_session_key.length = 6; + } else { /* forty bits */ + weak_session_key.length = 5; + } + dump_data_pw("NTLMSSP weakend master key:\n", + weak_session_key.data, + weak_session_key.length); + + /* SEND */ + calc_ntlmv2_key(ntlmssp_state, + &ntlmssp_state->send_sign_key, + ntlmssp_state->session_key, send_sign_const); + dump_data_pw("NTLMSSP send sign key:\n", + ntlmssp_state->send_sign_key.data, + ntlmssp_state->send_sign_key.length); + + calc_ntlmv2_key(ntlmssp_state, + &ntlmssp_state->send_seal_key, + weak_session_key, send_seal_const); + dump_data_pw("NTLMSSP send seal key:\n", + ntlmssp_state->send_seal_key.data, + ntlmssp_state->send_seal_key.length); + + arcfour_init(ntlmssp_state->send_seal_hash, + &ntlmssp_state->send_seal_key); + + dump_data_pw("NTLMSSP send sesl hash:\n", + ntlmssp_state->send_seal_hash, + sizeof(ntlmssp_state->send_seal_hash)); + + /* RECV */ + calc_ntlmv2_key(ntlmssp_state, + &ntlmssp_state->recv_sign_key, + ntlmssp_state->session_key, recv_sign_const); + dump_data_pw("NTLMSSP recv sign key:\n", + ntlmssp_state->recv_sign_key.data, + ntlmssp_state->recv_sign_key.length); + + calc_ntlmv2_key(ntlmssp_state, + &ntlmssp_state->recv_seal_key, + weak_session_key, recv_seal_const); + dump_data_pw("NTLMSSP recv seal key:\n", + ntlmssp_state->recv_seal_key.data, + ntlmssp_state->recv_seal_key.length); + arcfour_init(ntlmssp_state->recv_seal_hash, + &ntlmssp_state->recv_seal_key); + + dump_data_pw("NTLMSSP receive seal hash:\n", + ntlmssp_state->recv_seal_hash, + sizeof(ntlmssp_state->recv_seal_hash)); + } else { + DEBUG(5, ("NTLMSSP Sign/Seal - using NTLM1\n")); + + arcfour_init(ntlmssp_state->ntlmssp_hash, + &ntlmssp_state->session_key); + dump_data_pw("NTLMSSP hash:\n", ntlmssp_state->ntlmssp_hash, + sizeof(ntlmssp_state->ntlmssp_hash)); + } + + ntlmssp_state->ntlm_seq_num = 0; + ntlmssp_state->ntlm2_send_seq_num = 0; + ntlmssp_state->ntlm2_recv_seq_num = 0; + + return NT_STATUS_OK; +} diff --git a/source4/auth/gensec/schannel.c b/source4/auth/gensec/schannel.c new file mode 100644 index 0000000000..0657de27d9 --- /dev/null +++ b/source4/auth/gensec/schannel.c @@ -0,0 +1,268 @@ +/* + Unix SMB/CIFS implementation. + + dcerpc schannel operations + + Copyright (C) Andrew Tridgell 2004 + Copyright (C) Andrew Bartlett 2004-2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "librpc/gen_ndr/ndr_schannel.h" +#include "auth/auth.h" +#include "auth/gensec/schannel.h" + +static size_t schannel_sig_size(struct gensec_security *gensec_security) +{ + return 32; +} + +static NTSTATUS schannel_session_key(struct gensec_security *gensec_security, + DATA_BLOB *session_key) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS schannel_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, + const DATA_BLOB in, DATA_BLOB *out) +{ + struct schannel_state *state = gensec_security->private_data; + NTSTATUS status; + struct schannel_bind bind_schannel; + struct schannel_bind_ack bind_schannel_ack; + struct creds_CredentialState *creds; + + const char *workstation; + const char *domain; + *out = data_blob(NULL, 0); + + switch (gensec_security->gensec_role) { + case GENSEC_CLIENT: + if (state->state != SCHANNEL_STATE_START) { + /* we could parse the bind ack, but we don't know what it is yet */ + return NT_STATUS_OK; + } + + state->creds = talloc_reference(state, cli_credentials_get_netlogon_creds(gensec_security->credentials)); + + bind_schannel.unknown1 = 0; +#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 = cli_credentials_get_domain(gensec_security->credentials); + bind_schannel.u.info23.account_name = cli_credentials_get_username(gensec_security->credentials); + bind_schannel.u.info23.dnsdomain = str_format_nbt_domain(out_mem_ctx, fulldomainname); + bind_schannel.u.info23.workstation = str_format_nbt_domain(out_mem_ctx, cli_credentials_get_workstation(gensec_security->credentials)); +#else + bind_schannel.bind_type = 3; + bind_schannel.u.info3.domain = cli_credentials_get_domain(gensec_security->credentials); + bind_schannel.u.info3.workstation = cli_credentials_get_workstation(gensec_security->credentials); +#endif + + status = ndr_push_struct_blob(out, out_mem_ctx, &bind_schannel, + (ndr_push_flags_fn_t)ndr_push_schannel_bind); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("Could not create schannel bind: %s\n", + nt_errstr(status))); + return status; + } + + state->state = SCHANNEL_STATE_UPDATE_1; + + return NT_STATUS_MORE_PROCESSING_REQUIRED; + case GENSEC_SERVER: + + if (state->state != SCHANNEL_STATE_START) { + /* no third leg on this protocol */ + return NT_STATUS_INVALID_PARAMETER; + } + + /* parse the schannel startup blob */ + status = ndr_pull_struct_blob(&in, out_mem_ctx, &bind_schannel, + (ndr_pull_flags_fn_t)ndr_pull_schannel_bind); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (bind_schannel.bind_type == 23) { + workstation = bind_schannel.u.info23.workstation; + domain = bind_schannel.u.info23.domain; + } else { + workstation = bind_schannel.u.info3.workstation; + domain = bind_schannel.u.info3.domain; + } + + /* pull the session key for this client */ + status = schannel_fetch_session_key(out_mem_ctx, workstation, + domain, &creds); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("Could not find session key for attempted schannel connection from %s: %s\n", + workstation, nt_errstr(status))); + return status; + } + + state->creds = talloc_reference(state, creds); + + bind_schannel_ack.unknown1 = 1; + bind_schannel_ack.unknown2 = 0; + bind_schannel_ack.unknown3 = 0x6c0000; + + status = ndr_push_struct_blob(out, out_mem_ctx, &bind_schannel_ack, + (ndr_push_flags_fn_t)ndr_push_schannel_bind_ack); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("Could not return schannel bind ack for client %s: %s\n", + workstation, nt_errstr(status))); + return status; + } + + state->state = SCHANNEL_STATE_UPDATE_1; + + return NT_STATUS_OK; + } + return NT_STATUS_INVALID_PARAMETER; +} + +/** + * Return the struct creds_CredentialState. + * + * Make sure not to call this unless gensec is using schannel... + */ + +NTSTATUS dcerpc_schannel_creds(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + struct creds_CredentialState **creds) +{ + struct schannel_state *state = gensec_security->private_data; + + *creds = talloc_reference(mem_ctx, state->creds); + if (!*creds) { + return NT_STATUS_NO_MEMORY; + } + return NT_STATUS_OK; +} + + +/** + * Return the credentials of a logged on user, including session keys + * etc. + * + * Only valid after a successful authentication + * + * May only be called once per authentication. + * + */ + +static NTSTATUS schannel_session_info(struct gensec_security *gensec_security, + struct auth_session_info **session_info) +{ + (*session_info) = talloc(gensec_security, struct auth_session_info); + NT_STATUS_HAVE_NO_MEMORY(*session_info); + + ZERO_STRUCTP(*session_info); + + return NT_STATUS_OK; +} + +static NTSTATUS schannel_start(struct gensec_security *gensec_security) +{ + struct schannel_state *state; + + state = talloc(gensec_security, struct schannel_state); + if (!state) { + return NT_STATUS_NO_MEMORY; + } + + state->state = SCHANNEL_STATE_START; + state->seq_num = 0; + gensec_security->private_data = state; + + return NT_STATUS_OK; +} + +static NTSTATUS schannel_server_start(struct gensec_security *gensec_security) +{ + NTSTATUS status; + struct schannel_state *state; + + status = schannel_start(gensec_security); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + state = gensec_security->private_data; + state->initiator = False; + + return NT_STATUS_OK; +} + +static NTSTATUS schannel_client_start(struct gensec_security *gensec_security) +{ + NTSTATUS status; + struct schannel_state *state; + + status = schannel_start(gensec_security); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + state = gensec_security->private_data; + state->initiator = True; + + return NT_STATUS_OK; +} + + +static BOOL schannel_have_feature(struct gensec_security *gensec_security, + uint32_t feature) +{ + if (feature & (GENSEC_FEATURE_SIGN | + GENSEC_FEATURE_SEAL)) { + return True; + } + return False; +} + + +static const struct gensec_security_ops gensec_schannel_security_ops = { + .name = "schannel", + .auth_type = DCERPC_AUTH_TYPE_SCHANNEL, + .client_start = schannel_client_start, + .server_start = schannel_server_start, + .update = schannel_update, + .seal_packet = schannel_seal_packet, + .sign_packet = schannel_sign_packet, + .check_packet = schannel_check_packet, + .unseal_packet = schannel_unseal_packet, + .session_key = schannel_session_key, + .session_info = schannel_session_info, + .sig_size = schannel_sig_size, + .have_feature = schannel_have_feature, + .enabled = True +}; + +NTSTATUS gensec_schannel_init(void) +{ + NTSTATUS ret; + ret = gensec_register(&gensec_schannel_security_ops); + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(0,("Failed to register '%s' gensec backend!\n", + gensec_schannel_security_ops.name)); + return ret; + } + + return ret; +} diff --git a/source4/auth/gensec/schannel.h b/source4/auth/gensec/schannel.h new file mode 100644 index 0000000000..c109387c7c --- /dev/null +++ b/source4/auth/gensec/schannel.h @@ -0,0 +1,35 @@ +/* + Unix SMB/CIFS implementation. + + dcerpc schannel operations + + Copyright (C) Andrew Tridgell 2004 + Copyright (C) Andrew Bartlett 2004-2005 + + 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. +*/ + +enum schannel_position { + SCHANNEL_STATE_START = 0, + SCHANNEL_STATE_UPDATE_1 +}; + +struct schannel_state { + enum schannel_position state; + uint32_t seq_num; + BOOL initiator; + struct creds_CredentialState *creds; +}; + diff --git a/source4/auth/gensec/schannel_sign.c b/source4/auth/gensec/schannel_sign.c new file mode 100644 index 0000000000..493b26f6c0 --- /dev/null +++ b/source4/auth/gensec/schannel_sign.c @@ -0,0 +1,283 @@ +/* + Unix SMB/CIFS implementation. + + schannel library code + + Copyright (C) Andrew Tridgell 2004 + Copyright (C) Andrew Bartlett 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "lib/crypto/crypto.h" +#include "auth/auth.h" +#include "auth/gensec/schannel.h" + +#define NETSEC_SIGN_SIGNATURE { 0x77, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00 } +#define NETSEC_SEAL_SIGNATURE { 0x77, 0x00, 0x7a, 0x00, 0xff, 0xff, 0x00, 0x00 } + +/******************************************************************* + Encode or Decode the sequence number (which is symmetric) + ********************************************************************/ +static void netsec_deal_with_seq_num(struct schannel_state *state, + const uint8_t packet_digest[8], + uint8_t seq_num[8]) +{ + static const uint8_t zeros[4]; + uint8_t sequence_key[16]; + uint8_t digest1[16]; + + hmac_md5(state->creds->session_key, zeros, sizeof(zeros), digest1); + hmac_md5(digest1, packet_digest, 8, sequence_key); + arcfour_crypt(seq_num, sequence_key, 8); + + state->seq_num++; +} + + +/******************************************************************* + Calculate the key with which to encode the data payload + ********************************************************************/ +static void netsec_get_sealing_key(const uint8_t session_key[16], + const uint8_t seq_num[8], + uint8_t sealing_key[16]) +{ + static const uint8_t zeros[4]; + uint8_t digest2[16]; + uint8_t sess_kf0[16]; + int i; + + for (i = 0; i < 16; i++) { + sess_kf0[i] = session_key[i] ^ 0xf0; + } + + hmac_md5(sess_kf0, zeros, 4, digest2); + hmac_md5(digest2, seq_num, 8, sealing_key); +} + + +/******************************************************************* + Create a digest over the entire packet (including the data), and + MD5 it with the session key. + ********************************************************************/ +static void schannel_digest(const uint8_t sess_key[16], + const uint8_t netsec_sig[8], + const uint8_t *confounder, + const uint8_t *data, size_t data_len, + uint8_t digest_final[16]) +{ + uint8_t packet_digest[16]; + static const uint8_t zeros[4]; + struct MD5Context ctx; + + MD5Init(&ctx); + MD5Update(&ctx, zeros, 4); + MD5Update(&ctx, netsec_sig, 8); + if (confounder) { + MD5Update(&ctx, confounder, 8); + } + MD5Update(&ctx, data, data_len); + MD5Final(packet_digest, &ctx); + + hmac_md5(sess_key, packet_digest, sizeof(packet_digest), digest_final); +} + + +/* + unseal a packet +*/ +NTSTATUS schannel_unseal_packet(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + uint8_t *data, size_t length, + const uint8_t *whole_pdu, size_t pdu_length, + DATA_BLOB *sig) +{ + struct schannel_state *state = gensec_security->private_data; + + uint8_t digest_final[16]; + uint8_t confounder[8]; + uint8_t seq_num[8]; + uint8_t sealing_key[16]; + static const uint8_t netsec_sig[8] = NETSEC_SEAL_SIGNATURE; + + if (sig->length != 32) { + return NT_STATUS_ACCESS_DENIED; + } + + memcpy(confounder, sig->data+24, 8); + + RSIVAL(seq_num, 0, state->seq_num); + SIVAL(seq_num, 4, state->initiator?0:0x80); + + netsec_get_sealing_key(state->creds->session_key, seq_num, sealing_key); + arcfour_crypt(confounder, sealing_key, 8); + arcfour_crypt(data, sealing_key, length); + + schannel_digest(state->creds->session_key, + netsec_sig, confounder, + data, length, digest_final); + + if (memcmp(digest_final, sig->data+16, 8) != 0) { + dump_data_pw("calc digest:", digest_final, 8); + dump_data_pw("wire digest:", sig->data+16, 8); + return NT_STATUS_ACCESS_DENIED; + } + + netsec_deal_with_seq_num(state, digest_final, seq_num); + + if (memcmp(seq_num, sig->data+8, 8) != 0) { + dump_data_pw("calc seq num:", seq_num, 8); + dump_data_pw("wire seq num:", sig->data+8, 8); + return NT_STATUS_ACCESS_DENIED; + } + + return NT_STATUS_OK; +} + +/* + check the signature on a packet +*/ +NTSTATUS schannel_check_packet(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const uint8_t *data, size_t length, + const uint8_t *whole_pdu, size_t pdu_length, + const DATA_BLOB *sig) +{ + struct schannel_state *state = gensec_security->private_data; + + uint8_t digest_final[16]; + uint8_t seq_num[8]; + static const uint8_t netsec_sig[8] = NETSEC_SIGN_SIGNATURE; + + /* w2k sends just 24 bytes and skip the confounder */ + if (sig->length != 32 && sig->length != 24) { + return NT_STATUS_ACCESS_DENIED; + } + + RSIVAL(seq_num, 0, state->seq_num); + SIVAL(seq_num, 4, state->initiator?0:0x80); + + dump_data_pw("seq_num:\n", seq_num, 8); + dump_data_pw("sess_key:\n", state->creds->session_key, 16); + + schannel_digest(state->creds->session_key, + netsec_sig, NULL, + data, length, digest_final); + + netsec_deal_with_seq_num(state, digest_final, seq_num); + + if (memcmp(seq_num, sig->data+8, 8) != 0) { + dump_data_pw("calc seq num:", seq_num, 8); + dump_data_pw("wire seq num:", sig->data+8, 8); + return NT_STATUS_ACCESS_DENIED; + } + + if (memcmp(digest_final, sig->data+16, 8) != 0) { + dump_data_pw("calc digest:", digest_final, 8); + dump_data_pw("wire digest:", sig->data+16, 8); + return NT_STATUS_ACCESS_DENIED; + } + + return NT_STATUS_OK; +} + + +/* + seal a packet +*/ +NTSTATUS schannel_seal_packet(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + uint8_t *data, size_t length, + const uint8_t *whole_pdu, size_t pdu_length, + DATA_BLOB *sig) +{ + struct schannel_state *state = gensec_security->private_data; + + uint8_t digest_final[16]; + uint8_t confounder[8]; + uint8_t seq_num[8]; + uint8_t sealing_key[16]; + static const uint8_t netsec_sig[8] = NETSEC_SEAL_SIGNATURE; + + generate_random_buffer(confounder, 8); + + RSIVAL(seq_num, 0, state->seq_num); + SIVAL(seq_num, 4, state->initiator?0x80:0); + + schannel_digest(state->creds->session_key, + netsec_sig, confounder, + data, length, digest_final); + + netsec_get_sealing_key(state->creds->session_key, seq_num, sealing_key); + arcfour_crypt(confounder, sealing_key, 8); + arcfour_crypt(data, sealing_key, length); + + netsec_deal_with_seq_num(state, digest_final, seq_num); + + (*sig) = data_blob_talloc(mem_ctx, NULL, 32); + + memcpy(sig->data, netsec_sig, 8); + memcpy(sig->data+8, seq_num, 8); + memcpy(sig->data+16, digest_final, 8); + memcpy(sig->data+24, confounder, 8); + + dump_data_pw("signature:", sig->data+ 0, 8); + dump_data_pw("seq_num :", sig->data+ 8, 8); + dump_data_pw("digest :", sig->data+16, 8); + dump_data_pw("confound :", sig->data+24, 8); + + return NT_STATUS_OK; +} + + +/* + sign a packet +*/ +NTSTATUS schannel_sign_packet(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const uint8_t *data, size_t length, + const uint8_t *whole_pdu, size_t pdu_length, + DATA_BLOB *sig) +{ + struct schannel_state *state = gensec_security->private_data; + + uint8_t digest_final[16]; + uint8_t seq_num[8]; + static const uint8_t netsec_sig[8] = NETSEC_SIGN_SIGNATURE; + + RSIVAL(seq_num, 0, state->seq_num); + SIVAL(seq_num, 4, state->initiator?0x80:0); + + schannel_digest(state->creds->session_key, + netsec_sig, NULL, + data, length, digest_final); + + netsec_deal_with_seq_num(state, digest_final, seq_num); + + (*sig) = data_blob_talloc(mem_ctx, NULL, 32); + + memcpy(sig->data, netsec_sig, 8); + memcpy(sig->data+8, seq_num, 8); + memcpy(sig->data+16, digest_final, 8); + memset(sig->data+24, 0, 8); + + dump_data_pw("signature:", sig->data+ 0, 8); + dump_data_pw("seq_num :", sig->data+ 8, 8); + dump_data_pw("digest :", sig->data+16, 8); + dump_data_pw("confound :", sig->data+24, 8); + + return NT_STATUS_OK; +} diff --git a/source4/auth/gensec/schannel_state.c b/source4/auth/gensec/schannel_state.c new file mode 100644 index 0000000000..b2d632a1f0 --- /dev/null +++ b/source4/auth/gensec/schannel_state.c @@ -0,0 +1,229 @@ +/* + Unix SMB/CIFS implementation. + + module to store/fetch session keys for the schannel server + + Copyright (C) Andrew Tridgell 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "system/time.h" +#include "auth/auth.h" +#include "lib/ldb/include/ldb.h" +#include "db_wrap.h" + +/* a reasonable amount of time to keep credentials live */ +#define SCHANNEL_CREDENTIALS_EXPIRY 600 + +/* + connect to the schannel ldb +*/ +static struct ldb_context *schannel_db_connect(TALLOC_CTX *mem_ctx) +{ + char *path; + struct ldb_context *ldb; + + path = smbd_tmp_path(mem_ctx, "schannel.ldb"); + if (!path) { + return NULL; + } + + ldb = ldb_wrap_connect(mem_ctx, path, 0, NULL); + talloc_free(path); + if (!ldb) { + return NULL; + } + + return ldb; +} + +/* + remember an established session key for a netr server authentication + use a simple ldb structure +*/ +NTSTATUS schannel_store_session_key(TALLOC_CTX *mem_ctx, + struct creds_CredentialState *creds) +{ + struct ldb_context *ldb; + struct ldb_message *msg; + struct ldb_val val, seed; + char *s; + char *f; + char *sct; + char *rid; + time_t expiry = time(NULL) + SCHANNEL_CREDENTIALS_EXPIRY; + int ret; + + ldb = schannel_db_connect(mem_ctx); + if (ldb == NULL) { + return NT_STATUS_NO_MEMORY; + } + + s = talloc_asprintf(mem_ctx, "%u", (unsigned int)expiry); + + if (s == NULL) { + talloc_free(ldb); + return NT_STATUS_NO_MEMORY; + } + + f = talloc_asprintf(mem_ctx, "%u", (unsigned int)creds->negotiate_flags); + + if (f == NULL) { + talloc_free(ldb); + return NT_STATUS_NO_MEMORY; + } + + sct = talloc_asprintf(mem_ctx, "%u", (unsigned int)creds->secure_channel_type); + + if (sct == NULL) { + talloc_free(ldb); + return NT_STATUS_NO_MEMORY; + } + + rid = talloc_asprintf(mem_ctx, "%u", (unsigned int)creds->rid); + + if (rid == NULL) { + talloc_free(ldb); + return NT_STATUS_NO_MEMORY; + } + + msg = ldb_msg_new(mem_ctx); + if (msg == NULL) { + talloc_free(ldb); + return NT_STATUS_NO_MEMORY; + } + + msg->dn = talloc_asprintf(msg, "computerName=%s", creds->computer_name); + if (msg->dn == NULL) { + talloc_free(ldb); + talloc_free(msg); + return NT_STATUS_NO_MEMORY; + } + + val.data = creds->session_key; + val.length = sizeof(creds->session_key); + + seed.data = creds->seed.data; + seed.length = sizeof(creds->seed.data); + + ldb_msg_add_value(ldb, msg, "sessionKey", &val); + ldb_msg_add_value(ldb, msg, "seed", &seed); + ldb_msg_add_string(ldb, msg, "expiry", s); + ldb_msg_add_string(ldb, msg, "negotiateFlags", f); + ldb_msg_add_string(ldb, msg, "secureChannelType", sct); + ldb_msg_add_string(ldb, msg, "accountName", creds->account_name); + ldb_msg_add_string(ldb, msg, "computerName", creds->computer_name); + ldb_msg_add_string(ldb, msg, "flatname", creds->domain); + ldb_msg_add_string(ldb, msg, "rid", rid); + + ldb_delete(ldb, msg->dn); + + ret = ldb_add(ldb, msg); + + talloc_free(s); + + if (ret != 0) { + DEBUG(0,("Unable to add %s to session key db - %s\n", + msg->dn, ldb_errstring(ldb))); + talloc_free(ldb); + talloc_free(msg); + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + + talloc_free(msg); + talloc_free(ldb); + + return NT_STATUS_OK; +} + + +/* + read back a credentials back for a computer +*/ +NTSTATUS schannel_fetch_session_key(TALLOC_CTX *mem_ctx, + const char *computer_name, + const char *domain, + struct creds_CredentialState **creds) +{ + struct ldb_context *ldb; + time_t expiry; + struct ldb_message **res; + int ret; + const struct ldb_val *val; + char *expr=NULL; + + *creds = talloc_zero(mem_ctx, struct creds_CredentialState); + if (!*creds) { + return NT_STATUS_NO_MEMORY; + } + + ldb = schannel_db_connect(mem_ctx); + if (ldb == NULL) { + return NT_STATUS_NO_MEMORY; + } + + expr = talloc_asprintf(mem_ctx, "(&(computerName=%s)(flatname=%s))", computer_name, domain); + if (expr == NULL) { + talloc_free(ldb); + return NT_STATUS_NO_MEMORY; + } + + ret = ldb_search(ldb, NULL, LDB_SCOPE_SUBTREE, expr, NULL, &res); + if (ret != 1) { + talloc_free(ldb); + return NT_STATUS_INVALID_HANDLE; + } + + expiry = ldb_msg_find_uint(res[0], "expiry", 0); + if (expiry < time(NULL)) { + DEBUG(1,("schannel: attempt to use expired session key for %s\n", computer_name)); + talloc_free(ldb); + return NT_STATUS_INVALID_HANDLE; + } + + val = ldb_msg_find_ldb_val(res[0], "sessionKey"); + if (val == NULL || val->length != 16) { + talloc_free(ldb); + return NT_STATUS_INVALID_HANDLE; + } + + memcpy((*creds)->session_key, val->data, 16); + + val = ldb_msg_find_ldb_val(res[0], "seed"); + if (val == NULL || val->length != 8) { + talloc_free(ldb); + return NT_STATUS_INVALID_HANDLE; + } + + memcpy((*creds)->seed.data, val->data, 8); + + (*creds)->negotiate_flags = ldb_msg_find_int(res[0], "negotiateFlags", 0); + + (*creds)->secure_channel_type = ldb_msg_find_int(res[0], "secureChannelType", 0); + + (*creds)->account_name = talloc_reference(*creds, ldb_msg_find_string(res[0], "accountName", NULL)); + + (*creds)->computer_name = talloc_reference(*creds, ldb_msg_find_string(res[0], "computerName", NULL)); + + (*creds)->domain = talloc_reference(*creds, ldb_msg_find_string(res[0], "flatname", NULL)); + + (*creds)->rid = ldb_msg_find_uint(res[0], "rid", 0); + + talloc_free(ldb); + + return NT_STATUS_OK; +} diff --git a/source4/auth/gensec/spnego.c b/source4/auth/gensec/spnego.c new file mode 100644 index 0000000000..f5a091cd78 --- /dev/null +++ b/source4/auth/gensec/spnego.c @@ -0,0 +1,884 @@ +/* + Unix SMB/CIFS implementation. + + RFC2478 Compliant SPNEGO implementation + + Copyright (C) Jim McDonough 2003 + Copyright (C) Andrew Bartlett 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "auth/auth.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_AUTH + +enum spnego_state_position { + SPNEGO_SERVER_START, + SPNEGO_CLIENT_START, + SPNEGO_SERVER_TARG, + SPNEGO_CLIENT_TARG, + SPNEGO_FALLBACK, + SPNEGO_DONE +}; + +struct spnego_state { + uint_t ref_count; + enum spnego_message_type expected_packet; + enum spnego_state_position state_position; + struct gensec_security *sub_sec_security; + BOOL no_response_expected; +}; + + +static NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_security) +{ + struct spnego_state *spnego_state; + + spnego_state = talloc(gensec_security, struct spnego_state); + if (!spnego_state) { + return NT_STATUS_NO_MEMORY; + } + + spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT; + spnego_state->state_position = SPNEGO_CLIENT_START; + spnego_state->sub_sec_security = NULL; + spnego_state->no_response_expected = False; + + gensec_security->private_data = spnego_state; + return NT_STATUS_OK; +} + +static NTSTATUS gensec_spnego_server_start(struct gensec_security *gensec_security) +{ + struct spnego_state *spnego_state; + + spnego_state = talloc(gensec_security, struct spnego_state); + if (!spnego_state) { + return NT_STATUS_NO_MEMORY; + } + + spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT; + spnego_state->state_position = SPNEGO_SERVER_START; + spnego_state->sub_sec_security = NULL; + spnego_state->no_response_expected = False; + + gensec_security->private_data = spnego_state; + return NT_STATUS_OK; +} + +/* + wrappers for the spnego_*() functions +*/ +static NTSTATUS gensec_spnego_unseal_packet(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + uint8_t *data, size_t length, + const uint8_t *whole_pdu, size_t pdu_length, + DATA_BLOB *sig) +{ + 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; + } + + return gensec_unseal_packet(spnego_state->sub_sec_security, + mem_ctx, + data, length, + whole_pdu, pdu_length, + sig); +} + +static NTSTATUS gensec_spnego_check_packet(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const uint8_t *data, size_t length, + const uint8_t *whole_pdu, size_t pdu_length, + const DATA_BLOB *sig) +{ + 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; + } + + return gensec_check_packet(spnego_state->sub_sec_security, + mem_ctx, + data, length, + whole_pdu, pdu_length, + sig); +} + +static NTSTATUS gensec_spnego_seal_packet(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + uint8_t *data, size_t length, + const uint8_t *whole_pdu, size_t pdu_length, + DATA_BLOB *sig) +{ + 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; + } + + return gensec_seal_packet(spnego_state->sub_sec_security, + mem_ctx, + data, length, + whole_pdu, pdu_length, + sig); +} + +static NTSTATUS gensec_spnego_sign_packet(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const uint8_t *data, size_t length, + const uint8_t *whole_pdu, size_t pdu_length, + DATA_BLOB *sig) +{ + 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; + } + + return gensec_sign_packet(spnego_state->sub_sec_security, + mem_ctx, + data, length, + whole_pdu, pdu_length, + sig); +} + +static NTSTATUS gensec_spnego_wrap(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const DATA_BLOB *in, + DATA_BLOB *out) +{ + struct spnego_state *spnego_state = gensec_security->private_data; + + if (spnego_state->state_position != SPNEGO_DONE + && spnego_state->state_position != SPNEGO_FALLBACK) { + DEBUG(1, ("gensec_spnego_wrap: wrong state for wrap\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + return gensec_wrap(spnego_state->sub_sec_security, + mem_ctx, in, out); +} + +static NTSTATUS gensec_spnego_unwrap(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const DATA_BLOB *in, + DATA_BLOB *out) +{ + struct spnego_state *spnego_state = gensec_security->private_data; + + if (spnego_state->state_position != SPNEGO_DONE + && spnego_state->state_position != SPNEGO_FALLBACK) { + DEBUG(1, ("gensec_spnego_unwrap: wrong state for unwrap\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + return gensec_unwrap(spnego_state->sub_sec_security, + mem_ctx, in, out); +} + +static size_t gensec_spnego_sig_size(struct gensec_security *gensec_security) +{ + struct spnego_state *spnego_state = gensec_security->private_data; + + if (spnego_state->state_position != SPNEGO_DONE + && spnego_state->state_position != SPNEGO_FALLBACK) { + return 0; + } + + return gensec_sig_size(spnego_state->sub_sec_security); +} + +static NTSTATUS gensec_spnego_session_key(struct gensec_security *gensec_security, + DATA_BLOB *session_key) +{ + struct spnego_state *spnego_state = gensec_security->private_data; + if (!spnego_state->sub_sec_security) { + return NT_STATUS_INVALID_PARAMETER; + } + + return gensec_session_key(spnego_state->sub_sec_security, + session_key); +} + +static NTSTATUS gensec_spnego_session_info(struct gensec_security *gensec_security, + struct auth_session_info **session_info) +{ + struct spnego_state *spnego_state = gensec_security->private_data; + if (!spnego_state->sub_sec_security) { + return NT_STATUS_INVALID_PARAMETER; + } + + return gensec_session_info(spnego_state->sub_sec_security, + session_info); +} + +/** Fallback to another GENSEC mechanism, based on magic strings + * + * This is the 'fallback' case, where we don't get SPNEGO, and have to + * try all the other options (and hope they all have a magic string + * they check) +*/ + +static NTSTATUS gensec_spnego_server_try_fallback(struct gensec_security *gensec_security, + struct spnego_state *spnego_state, + TALLOC_CTX *out_mem_ctx, + const DATA_BLOB in, DATA_BLOB *out) +{ + int i; + int num_ops; + const struct gensec_security_ops **all_ops = gensec_security_all(&num_ops); + for (i=0; i < num_ops; i++) { + NTSTATUS nt_status; + if (!all_ops[i]->oid) { + continue; + } + if (strcasecmp(GENSEC_OID_SPNEGO,all_ops[i]->oid) == 0) { + continue; + } + + nt_status = gensec_subcontext_start(spnego_state, + gensec_security, + &spnego_state->sub_sec_security); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + /* select the sub context */ + nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security, + all_ops[i]->oid); + if (!NT_STATUS_IS_OK(nt_status)) { + talloc_free(spnego_state->sub_sec_security); + spnego_state->sub_sec_security = NULL; + continue; + } + nt_status = gensec_update(spnego_state->sub_sec_security, + out_mem_ctx, in, out); + if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + spnego_state->state_position = SPNEGO_FALLBACK; + return nt_status; + } + talloc_free(spnego_state->sub_sec_security); + spnego_state->sub_sec_security = NULL; + } + DEBUG(1, ("Failed to parse SPNEGO request\n")); + return NT_STATUS_INVALID_PARAMETER; + +} + +/* + Parse the netTokenInit from the client, to the server. + + +*/ + +static NTSTATUS gensec_spnego_server_parse_negTokenInit(struct gensec_security *gensec_security, + struct spnego_state *spnego_state, + TALLOC_CTX *out_mem_ctx, + const char **mechType, + const DATA_BLOB unwrapped_in, DATA_BLOB *unwrapped_out) +{ + NTSTATUS nt_status; + + if (!mechType || !mechType[0]) { + DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + nt_status = gensec_subcontext_start(spnego_state, + gensec_security, + &spnego_state->sub_sec_security); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + /* select the sub context */ + nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security, + mechType[0]); + if (!NT_STATUS_IS_OK(nt_status)) { + talloc_free(spnego_state->sub_sec_security); + spnego_state->sub_sec_security = NULL; + return nt_status; + } + + if (!unwrapped_in.length) { + return NT_STATUS_INVALID_PARAMETER; + } + + nt_status = gensec_update(spnego_state->sub_sec_security, + out_mem_ctx, + unwrapped_in, + unwrapped_out); + + if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(nt_status)) { + DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n", + spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status))); + talloc_free(spnego_state->sub_sec_security); + spnego_state->sub_sec_security = NULL; + } + return nt_status; +} + +static NTSTATUS gensec_spnego_client_parse_negTokenInit(struct gensec_security *gensec_security, + struct spnego_state *spnego_state, + TALLOC_CTX *out_mem_ctx, + const char **mechType, + const DATA_BLOB unwrapped_in, DATA_BLOB *unwrapped_out) +{ + int i; + NTSTATUS nt_status; + DATA_BLOB null_data_blob = data_blob(NULL,0); + + for (i=0; mechType && mechType[i]; i++) { + nt_status = gensec_subcontext_start(spnego_state, + gensec_security, + &spnego_state->sub_sec_security); + if (!NT_STATUS_IS_OK(nt_status)) { + break; + } + /* select the sub context */ + nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security, + mechType[i]); + if (!NT_STATUS_IS_OK(nt_status)) { + talloc_free(spnego_state->sub_sec_security); + spnego_state->sub_sec_security = NULL; + continue; + } + + if (i == 0) { + nt_status = gensec_update(spnego_state->sub_sec_security, + out_mem_ctx, + unwrapped_in, + unwrapped_out); + } else { + /* only get the helping start blob for the first OID */ + nt_status = gensec_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) && !NT_STATUS_IS_OK(nt_status)) { + DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n", + spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status))); + talloc_free(spnego_state->sub_sec_security); + spnego_state->sub_sec_security = NULL; + } + return nt_status; + } + if (!mechType || !mechType[i]) { + DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n")); + } + return NT_STATUS_INVALID_PARAMETER; +} + +/** create a client negTokenInit + * + * This is the case, where the client is the first one who sends data +*/ + +static NTSTATUS gensec_spnego_client_negTokenInit(struct gensec_security *gensec_security, + struct spnego_state *spnego_state, + TALLOC_CTX *out_mem_ctx, + const DATA_BLOB in, DATA_BLOB *out) +{ + DATA_BLOB null_data_blob = data_blob(NULL,0); + NTSTATUS nt_status; + const char **mechTypes = NULL; + DATA_BLOB unwrapped_out = data_blob(NULL,0); + + mechTypes = gensec_security_oids(out_mem_ctx, GENSEC_OID_SPNEGO); + + if (!mechTypes) { + DEBUG(1, ("no GENSEC OID backends available\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + nt_status = gensec_subcontext_start(spnego_state, + gensec_security, + &spnego_state->sub_sec_security); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + /* select our preferred mech */ + nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security, + mechTypes[0]); + if (!NT_STATUS_IS_OK(nt_status)) { + talloc_free(spnego_state->sub_sec_security); + spnego_state->sub_sec_security = NULL; + return nt_status; + } + nt_status = gensec_update(spnego_state->sub_sec_security, + out_mem_ctx, in, &unwrapped_out); + if (NT_STATUS_IS_OK(nt_status) || NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + struct spnego_data spnego_out; + spnego_out.type = SPNEGO_NEG_TOKEN_INIT; + spnego_out.negTokenInit.mechTypes = mechTypes; + spnego_out.negTokenInit.reqFlags = 0; + spnego_out.negTokenInit.mechListMIC = null_data_blob; + spnego_out.negTokenInit.mechToken = unwrapped_out; + + if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) { + DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + /* set next state */ + spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG; + spnego_state->state_position = SPNEGO_CLIENT_TARG; + + if (NT_STATUS_IS_OK(nt_status)) { + spnego_state->no_response_expected = True; + } + + return NT_STATUS_MORE_PROCESSING_REQUIRED; + } + talloc_free(spnego_state->sub_sec_security); + spnego_state->sub_sec_security = NULL; + + DEBUG(1, ("Failed to setup SPNEGO negTokenInit request: %s\n", nt_errstr(nt_status))); + return NT_STATUS_INVALID_PARAMETER; +} + + +/** create a client negTokenTarg + * + * This is the case, where the client is the first one who sends data +*/ + +static NTSTATUS gensec_spnego_server_negTokenTarg(struct gensec_security *gensec_security, + struct spnego_state *spnego_state, + TALLOC_CTX *out_mem_ctx, + NTSTATUS nt_status, + const DATA_BLOB unwrapped_out, DATA_BLOB *out) +{ + struct spnego_data spnego_out; + DATA_BLOB null_data_blob = data_blob(NULL, 0); + + /* compose reply */ + spnego_out.type = SPNEGO_NEG_TOKEN_TARG; + spnego_out.negTokenTarg.responseToken = unwrapped_out; + spnego_out.negTokenTarg.mechListMIC = null_data_blob; + spnego_out.negTokenTarg.supportedMech = NULL; + + if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + spnego_out.negTokenTarg.supportedMech + = spnego_state->sub_sec_security->ops->oid; + spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE; + spnego_state->state_position = SPNEGO_SERVER_TARG; + } else if (NT_STATUS_IS_OK(nt_status)) { + if (unwrapped_out.data) { + spnego_out.negTokenTarg.supportedMech + = spnego_state->sub_sec_security->ops->oid; + } + spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED; + spnego_state->state_position = SPNEGO_DONE; + } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER)) { + if (spnego_state->sub_sec_security) { + /* we have a mech, but we just didn't get the input parameter */ + spnego_out.negTokenTarg.supportedMech + = spnego_state->sub_sec_security->ops->oid; + } else { + const char **mechTypes = gensec_security_oids(out_mem_ctx, GENSEC_OID_SPNEGO); + if (!mechTypes) { + DEBUG(1, ("no GENSEC OID backends available\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + nt_status = gensec_subcontext_start(spnego_state, + gensec_security, + &spnego_state->sub_sec_security); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + /* select our preferred mech */ + nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security, + mechTypes[0]); + if (!NT_STATUS_IS_OK(nt_status)) { + talloc_free(spnego_state->sub_sec_security); + spnego_state->sub_sec_security = NULL; + return nt_status; + } + + /* we should be sending the whole list here */ + spnego_out.negTokenTarg.supportedMech = mechTypes[0]; + } + + spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE; + spnego_state->state_position = SPNEGO_SERVER_TARG; + nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED; + } else { + spnego_out.negTokenTarg.negResult = SPNEGO_REJECT; + DEBUG(2, ("SPNEGO login failed: %s\n", nt_errstr(nt_status))); + spnego_state->state_position = SPNEGO_DONE; + } + + if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) { + DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG; + + return nt_status; +} + + +static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, + const DATA_BLOB in, DATA_BLOB *out) +{ + struct spnego_state *spnego_state = gensec_security->private_data; + DATA_BLOB null_data_blob = data_blob(NULL, 0); + DATA_BLOB unwrapped_out = data_blob(NULL, 0); + struct spnego_data spnego_out; + struct spnego_data spnego; + + ssize_t len; + + *out = data_blob(NULL, 0); + + if (!out_mem_ctx) { + out_mem_ctx = spnego_state; + } + + /* and switch into the state machine */ + + switch (spnego_state->state_position) { + case SPNEGO_FALLBACK: + return gensec_update(spnego_state->sub_sec_security, + out_mem_ctx, in, out); + case SPNEGO_SERVER_START: + { + if (in.length) { + NTSTATUS nt_status; + + len = spnego_read_data(in, &spnego); + if (len == -1) { + return gensec_spnego_server_try_fallback(gensec_security, spnego_state, out_mem_ctx, in, out); + } + /* client sent NegTargetInit, we send NegTokenTarg */ + + /* OK, so it's real SPNEGO, check the packet's the one we expect */ + if (spnego.type != spnego_state->expected_packet) { + DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type, + spnego_state->expected_packet)); + dump_data(1, in.data, in.length); + spnego_free_data(&spnego); + return NT_STATUS_INVALID_PARAMETER; + } + + nt_status = gensec_spnego_server_parse_negTokenInit(gensec_security, + spnego_state, + out_mem_ctx, + spnego.negTokenInit.mechTypes, + spnego.negTokenInit.mechToken, + &unwrapped_out); + + nt_status = gensec_spnego_server_negTokenTarg(gensec_security, + spnego_state, + out_mem_ctx, + nt_status, + unwrapped_out, + out); + + spnego_free_data(&spnego); + + return nt_status; + } else { + const char **mechlist = gensec_security_oids(out_mem_ctx, GENSEC_OID_SPNEGO); + + spnego_out.type = SPNEGO_NEG_TOKEN_INIT; + spnego_out.negTokenInit.mechTypes = mechlist; + spnego_out.negTokenInit.reqFlags = 0; + spnego_out.negTokenInit.mechListMIC + = data_blob_string_const(talloc_asprintf(out_mem_ctx, "%s$@%s", lp_netbios_name(), lp_realm())); + spnego_out.negTokenInit.mechToken = unwrapped_out; + + if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) { + DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + /* set next state */ + spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG; + spnego_state->state_position = SPNEGO_SERVER_TARG; + + return NT_STATUS_MORE_PROCESSING_REQUIRED; + } + } + + case SPNEGO_CLIENT_START: + { + /* The server offers a list of mechanisms */ + + const char *my_mechs[] = {NULL, NULL}; + NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER; + + if (!in.length) { + /* client to produce negTokenInit */ + return gensec_spnego_client_negTokenInit(gensec_security, spnego_state, + out_mem_ctx, in, out); + } + + len = spnego_read_data(in, &spnego); + + if (len == -1) { + DEBUG(1, ("Invalid SPNEGO request:\n")); + dump_data(1, in.data, in.length); + return NT_STATUS_INVALID_PARAMETER; + } + + /* OK, so it's real SPNEGO, check the packet's the one we expect */ + if (spnego.type != spnego_state->expected_packet) { + DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type, + spnego_state->expected_packet)); + dump_data(1, in.data, in.length); + spnego_free_data(&spnego); + return NT_STATUS_INVALID_PARAMETER; + } + + if (spnego.negTokenInit.targetPrincipal) { + DEBUG(5, ("Server claims it's principal name is %s (ignored)\n", spnego.negTokenInit.targetPrincipal)); + } + + nt_status = gensec_spnego_client_parse_negTokenInit(gensec_security, + spnego_state, + out_mem_ctx, + spnego.negTokenInit.mechTypes, + spnego.negTokenInit.mechToken, + &unwrapped_out); + + if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(nt_status)) { + spnego_free_data(&spnego); + return nt_status; + } + + /* compose reply */ + my_mechs[0] = spnego_state->sub_sec_security->ops->oid; + + spnego_out.type = SPNEGO_NEG_TOKEN_INIT; + spnego_out.negTokenInit.mechTypes = my_mechs; + spnego_out.negTokenInit.reqFlags = 0; + spnego_out.negTokenInit.mechListMIC = null_data_blob; + spnego_out.negTokenInit.mechToken = unwrapped_out; + + if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) { + DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + /* set next state */ + spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG; + spnego_state->state_position = SPNEGO_CLIENT_TARG; + + if (NT_STATUS_IS_OK(nt_status)) { + spnego_state->no_response_expected = True; + } + + return NT_STATUS_MORE_PROCESSING_REQUIRED; + } + case SPNEGO_SERVER_TARG: + { + NTSTATUS nt_status; + if (!in.length) { + return NT_STATUS_INVALID_PARAMETER; + } + + len = spnego_read_data(in, &spnego); + + if (len == -1) { + DEBUG(1, ("Invalid SPNEGO request:\n")); + dump_data(1, in.data, in.length); + return NT_STATUS_INVALID_PARAMETER; + } + + /* OK, so it's real SPNEGO, check the packet's the one we expect */ + if (spnego.type != spnego_state->expected_packet) { + DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type, + spnego_state->expected_packet)); + dump_data(1, in.data, in.length); + spnego_free_data(&spnego); + return NT_STATUS_INVALID_PARAMETER; + } + + if (!spnego_state->sub_sec_security) { + DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + nt_status = gensec_update(spnego_state->sub_sec_security, + out_mem_ctx, + spnego.negTokenTarg.responseToken, + &unwrapped_out); + + nt_status = gensec_spnego_server_negTokenTarg(gensec_security, + spnego_state, + out_mem_ctx, + nt_status, + unwrapped_out, + out); + + spnego_free_data(&spnego); + + return nt_status; + } + case SPNEGO_CLIENT_TARG: + { + NTSTATUS nt_status; + if (!in.length) { + return NT_STATUS_INVALID_PARAMETER; + } + + len = spnego_read_data(in, &spnego); + + if (len == -1) { + DEBUG(1, ("Invalid SPNEGO request:\n")); + dump_data(1, in.data, in.length); + return NT_STATUS_INVALID_PARAMETER; + } + + /* OK, so it's real SPNEGO, check the packet's the one we expect */ + if (spnego.type != spnego_state->expected_packet) { + DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type, + spnego_state->expected_packet)); + dump_data(1, in.data, in.length); + spnego_free_data(&spnego); + return NT_STATUS_INVALID_PARAMETER; + } + + if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) { + return NT_STATUS_ACCESS_DENIED; + } + + if (spnego_state->no_response_expected) { + if (spnego.negTokenTarg.negResult != SPNEGO_ACCEPT_COMPLETED) { + DEBUG(3,("GENSEC SPNEGO: client GENSEC accepted, but server rejected (bad password?)\n")); + nt_status = NT_STATUS_INVALID_PARAMETER; + } else if (spnego.negTokenTarg.responseToken.length) { + DEBUG(2,("GENSEC SPNEGO: client GENSEC accepted, but server continued negotiation!\n")); + nt_status = NT_STATUS_INVALID_PARAMETER; + } else { + nt_status = NT_STATUS_OK; + } + } else { + nt_status = gensec_update(spnego_state->sub_sec_security, + out_mem_ctx, + spnego.negTokenTarg.responseToken, + &unwrapped_out); + + if (NT_STATUS_IS_OK(nt_status)) { + spnego_state->no_response_expected = True; + } + } + + spnego_free_data(&spnego); + + if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) + && !NT_STATUS_IS_OK(nt_status)) { + DEBUG(1, ("SPNEGO(%s) login failed: %s\n", + spnego_state->sub_sec_security->ops->name, + nt_errstr(nt_status))); + return nt_status; + } + + if (unwrapped_out.length) { + /* compose reply */ + spnego_out.type = SPNEGO_NEG_TOKEN_TARG; + spnego_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT; + spnego_out.negTokenTarg.supportedMech = NULL; + spnego_out.negTokenTarg.responseToken = unwrapped_out; + spnego_out.negTokenTarg.mechListMIC = null_data_blob; + + if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) { + DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + spnego_state->state_position = SPNEGO_CLIENT_TARG; + nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED; + } else { + + /* all done - server has accepted, and we agree */ + *out = null_data_blob; + + if (spnego.negTokenTarg.negResult != SPNEGO_ACCEPT_COMPLETED) { + /* unless of course it did not accept */ + DEBUG(1,("gensec_update ok but not accepted\n")); + nt_status = NT_STATUS_INVALID_PARAMETER; + } + } + + spnego_state->state_position = SPNEGO_DONE; + + return nt_status; + } + case SPNEGO_DONE: + return NT_STATUS_OK; + } + return NT_STATUS_INVALID_PARAMETER; +} + +static BOOL gensec_spnego_have_feature(struct gensec_security *gensec_security, + uint32_t feature) +{ + struct spnego_state *spnego_state = gensec_security->private_data; + if (!spnego_state->sub_sec_security) { + return False; + } + + return gensec_have_feature(spnego_state->sub_sec_security, + feature); +} + +static const struct gensec_security_ops gensec_spnego_security_ops = { + .name = "spnego", + .sasl_name = "GSS-SPNEGO", + .auth_type = DCERPC_AUTH_TYPE_SPNEGO, + .oid = GENSEC_OID_SPNEGO, + .client_start = gensec_spnego_client_start, + .server_start = gensec_spnego_server_start, + .update = gensec_spnego_update, + .seal_packet = gensec_spnego_seal_packet, + .sign_packet = gensec_spnego_sign_packet, + .sig_size = gensec_spnego_sig_size, + .check_packet = gensec_spnego_check_packet, + .unseal_packet = gensec_spnego_unseal_packet, + .wrap = gensec_spnego_wrap, + .unwrap = gensec_spnego_unwrap, + .session_key = gensec_spnego_session_key, + .session_info = gensec_spnego_session_info, + .have_feature = gensec_spnego_have_feature, + .enabled = True +}; + +NTSTATUS gensec_spnego_init(void) +{ + NTSTATUS ret; + ret = gensec_register(&gensec_spnego_security_ops); + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(0,("Failed to register '%s' gensec backend!\n", + gensec_spnego_security_ops.name)); + return ret; + } + + return ret; +} diff --git a/source4/auth/gensec/spnego.h b/source4/auth/gensec/spnego.h new file mode 100644 index 0000000000..1064370146 --- /dev/null +++ b/source4/auth/gensec/spnego.h @@ -0,0 +1,69 @@ +/* + Unix SMB/CIFS implementation. + + RFC2478 Compliant SPNEGO implementation + + Copyright (C) Jim McDonough 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. +*/ + +#ifndef SAMBA_SPNEGO_H +#define SAMBA_SPNEGO_H + +#define SPNEGO_DELEG_FLAG 0x01 +#define SPNEGO_MUTUAL_FLAG 0x02 +#define SPNEGO_REPLAY_FLAG 0x04 +#define SPNEGO_SEQUENCE_FLAG 0x08 +#define SPNEGO_ANON_FLAG 0x10 +#define SPNEGO_CONF_FLAG 0x20 +#define SPNEGO_INTEG_FLAG 0x40 +#define SPNEGO_REQ_FLAG 0x80 + +enum spnego_negResult { + SPNEGO_ACCEPT_COMPLETED = 0, + SPNEGO_ACCEPT_INCOMPLETE = 1, + SPNEGO_REJECT = 2, + SPNEGO_NONE_RESULT = 3 +}; + +struct spnego_negTokenInit { + const char **mechTypes; + int reqFlags; + DATA_BLOB mechToken; + DATA_BLOB mechListMIC; + char *targetPrincipal; +}; + +struct spnego_negTokenTarg { + uint8_t negResult; + const char *supportedMech; + DATA_BLOB responseToken; + DATA_BLOB mechListMIC; +}; + +struct spnego_data { + int type; + struct spnego_negTokenInit negTokenInit; + struct spnego_negTokenTarg negTokenTarg; +}; + +enum spnego_message_type { + SPNEGO_NEG_TOKEN_INIT = 0, + SPNEGO_NEG_TOKEN_TARG = 1, +}; + +#endif diff --git a/source4/auth/gensec/spnego_parse.c b/source4/auth/gensec/spnego_parse.c new file mode 100644 index 0000000000..e48c32f0da --- /dev/null +++ b/source4/auth/gensec/spnego_parse.c @@ -0,0 +1,375 @@ +/* + Unix SMB/CIFS implementation. + + RFC2478 Compliant SPNEGO implementation + + Copyright (C) Jim McDonough 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" +#include "auth/auth.h" +#include "asn_1.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_AUTH + +static BOOL read_negTokenInit(struct 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; + uint8_t context; + if (!asn1_peek_uint8(asn1, &context)) { + asn1->has_error = True; + break; + } + + switch (context) { + /* Read mechTypes */ + case ASN1_CONTEXT(0): + asn1_start_tag(asn1, ASN1_CONTEXT(0)); + asn1_start_tag(asn1, ASN1_SEQUENCE(0)); + + token->mechTypes = talloc(NULL, const char *); + for (i = 0; !asn1->has_error && + 0 < asn1_tag_remaining(asn1); i++) { + token->mechTypes = talloc_realloc(NULL, + token->mechTypes, + const char *, i+2); + asn1_read_OID(asn1, token->mechTypes + i); + if (token->mechTypes[i]) { + talloc_steal(token->mechTypes, + 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): + { + uint8_t type_peek; + asn1_start_tag(asn1, ASN1_CONTEXT(3)); + if (!asn1_peek_uint8(asn1, &type_peek)) { + asn1->has_error = True; + break; + } + if (type_peek == 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->targetPrincipal = 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(struct 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(struct 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)) { + uint8_t context; + if (!asn1_peek_uint8(asn1, &context)) { + asn1->has_error = True; + break; + } + + switch (context) { + 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(struct asn1_data *asn1, struct spnego_negTokenTarg *token) +{ + asn1_push_tag(asn1, ASN1_CONTEXT(1)); + asn1_push_tag(asn1, ASN1_SEQUENCE(0)); + + if (token->negResult != SPNEGO_NONE_RESULT) { + 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 spnego_read_data(DATA_BLOB data, struct spnego_data *token) +{ + struct asn1_data asn1; + ssize_t ret = -1; + uint8_t context; + + ZERO_STRUCTP(token); + ZERO_STRUCT(asn1); + + if (data.length == 0) { + return ret; + } + + asn1_load(&asn1, data); + + if (!asn1_peek_uint8(&asn1, &context)) { + asn1.has_error = True; + } else { + switch (context) { + case ASN1_APPLICATION(0): + asn1_start_tag(&asn1, ASN1_APPLICATION(0)); + asn1_check_OID(&asn1, GENSEC_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: + asn1.has_error = True; + break; + } + } + + if (!asn1.has_error) ret = asn1.ofs; + asn1_free(&asn1); + + return ret; +} + +ssize_t spnego_write_data(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, struct spnego_data *spnego) +{ + struct 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, GENSEC_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 spnego_free_data(struct spnego_data *spnego) +{ + BOOL ret = True; + + if (!spnego) goto out; + + switch(spnego->type) { + case SPNEGO_NEG_TOKEN_INIT: + if (spnego->negTokenInit.mechTypes) { + talloc_free(spnego->negTokenInit.mechTypes); + } + data_blob_free(&spnego->negTokenInit.mechToken); + data_blob_free(&spnego->negTokenInit.mechListMIC); + talloc_free(spnego->negTokenInit.targetPrincipal); + break; + case SPNEGO_NEG_TOKEN_TARG: + if (spnego->negTokenTarg.supportedMech) { + talloc_free(discard_const(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/auth/kerberos/clikrb5.c b/source4/auth/kerberos/clikrb5.c new file mode 100644 index 0000000000..ec8f60fbb3 --- /dev/null +++ b/source4/auth/kerberos/clikrb5.c @@ -0,0 +1,478 @@ +/* + Unix SMB/CIFS implementation. + simple kerberos5 routines for active directory + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Luke Howard 2002-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" +#include "system/network.h" +#include "system/kerberos.h" +#include "system/time.h" +#include "auth/kerberos/kerberos.h" + +#ifdef HAVE_KRB5 + +#ifndef HAVE_KRB5_SET_REAL_TIME +/* + * This function is not in the Heimdal mainline. + */ + krb5_error_code krb5_set_real_time(krb5_context context, int32_t seconds, int32_t microseconds) +{ + krb5_error_code ret; + int32_t sec, usec; + + ret = krb5_us_timeofday(context, &sec, &usec); + if (ret) + return ret; + + context->kdc_sec_offset = seconds - sec; + context->kdc_usec_offset = microseconds - usec; + + return 0; +} +#endif + +#if defined(HAVE_KRB5_SET_DEFAULT_IN_TKT_ETYPES) && !defined(HAVE_KRB5_SET_DEFAULT_TGS_KTYPES) + krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc) +{ + return krb5_set_default_in_tkt_etypes(ctx, enc); +} +#endif + +#if defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) +/* HEIMDAL */ + void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr) +{ + pkaddr->addr_type = KRB5_ADDRESS_INET; + pkaddr->address.length = sizeof(((struct sockaddr_in *)paddr)->sin_addr); + pkaddr->address.data = (char *)&(((struct sockaddr_in *)paddr)->sin_addr); +} +#elif defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) +/* MIT */ + void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr) +{ + pkaddr->addrtype = ADDRTYPE_INET; + pkaddr->length = sizeof(((struct sockaddr_in *)paddr)->sin_addr); + pkaddr->contents = (krb5_octet *)&(((struct sockaddr_in *)paddr)->sin_addr); +} +#else +#error UNKNOWN_ADDRTYPE +#endif + +#if defined(HAVE_KRB5_PRINCIPAL2SALT) && defined(HAVE_KRB5_USE_ENCTYPE) && defined(HAVE_KRB5_STRING_TO_KEY) && defined(HAVE_KRB5_ENCRYPT_BLOCK) + int create_kerberos_key_from_string_direct(krb5_context context, + krb5_principal host_princ, + krb5_data *password, + krb5_keyblock *key, + krb5_enctype enctype) +{ + int ret; + krb5_data salt; + krb5_encrypt_block eblock; + + ret = krb5_principal2salt(context, host_princ, &salt); + if (ret) { + DEBUG(1,("krb5_principal2salt failed (%s)\n", error_message(ret))); + return ret; + } + krb5_use_enctype(context, &eblock, enctype); + ret = krb5_string_to_key(context, &eblock, key, password, &salt); + SAFE_FREE(salt.data); + return ret; +} +#elif defined(HAVE_KRB5_GET_PW_SALT) && defined(HAVE_KRB5_STRING_TO_KEY_SALT) + int create_kerberos_key_from_string_direct(krb5_context context, + krb5_principal host_princ, + krb5_data *password, + krb5_keyblock *key, + krb5_enctype enctype) +{ + int ret; + krb5_salt salt; + + ret = krb5_get_pw_salt(context, host_princ, &salt); + if (ret) { + DEBUG(1,("krb5_get_pw_salt failed (%s)\n", error_message(ret))); + return ret; + } + return krb5_string_to_key_salt(context, enctype, password->data, + salt, key); +} +#else +#error UNKNOWN_CREATE_KEY_FUNCTIONS +#endif + + int create_kerberos_key_from_string(krb5_context context, + krb5_principal host_princ, + krb5_data *password, + krb5_keyblock *key, + krb5_enctype enctype) +{ + krb5_principal salt_princ = NULL; + int ret; + /* + * Check if we've determined that the KDC is salting keys for this + * principal/enctype in a non-obvious way. If it is, try to match + * its behavior. + */ + salt_princ = kerberos_fetch_salt_princ_for_host_princ(context, host_princ, enctype); + ret = create_kerberos_key_from_string_direct(context, salt_princ ? salt_princ : host_princ, password, key, enctype); + if (salt_princ) { + krb5_free_principal(context, salt_princ); + } + return ret; +} + +#if defined(HAVE_KRB5_GET_PERMITTED_ENCTYPES) + krb5_error_code get_kerberos_allowed_etypes(krb5_context context, + krb5_enctype **enctypes) +{ + return krb5_get_permitted_enctypes(context, enctypes); +} +#elif defined(HAVE_KRB5_GET_DEFAULT_IN_TKT_ETYPES) + krb5_error_code get_kerberos_allowed_etypes(krb5_context context, + krb5_enctype **enctypes) +{ + return krb5_get_default_in_tkt_etypes(context, enctypes); +} +#else +#error UNKNOWN_GET_ENCTYPES_FUNCTIONS +#endif + + void free_kerberos_etypes(krb5_context context, + krb5_enctype *enctypes) +{ +#if defined(HAVE_KRB5_FREE_KTYPES) + krb5_free_ktypes(context, enctypes); + return; +#else + SAFE_FREE(enctypes); + return; +#endif +} + +#if defined(HAVE_KRB5_AUTH_CON_SETKEY) && !defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY) + krb5_error_code krb5_auth_con_setuseruserkey(krb5_context context, + krb5_auth_context auth_context, + krb5_keyblock *keyblock) +{ + return krb5_auth_con_setkey(context, auth_context, keyblock); +} +#endif + + DATA_BLOB get_auth_data_from_tkt(TALLOC_CTX *mem_ctx, + krb5_ticket *tkt) +{ + DATA_BLOB auth_data = data_blob(NULL, 0); +#if defined(HAVE_KRB5_TKT_ENC_PART2) + if (tkt && tkt->enc_part2 + && tkt->enc_part2->authorization_data + && tkt->enc_part2->authorization_data[0] + && tkt->enc_part2->authorization_data[0]->length) + auth_data = data_blob(tkt->enc_part2->authorization_data[0]->contents, + tkt->enc_part2->authorization_data[0]->length); +#else + if (tkt && tkt->ticket.authorization_data && tkt->ticket.authorization_data->len) + auth_data = data_blob(tkt->ticket.authorization_data->val->ad_data.data, + tkt->ticket.authorization_data->val->ad_data.length); +#endif + return auth_data; +} + + krb5_const_principal get_principal_from_tkt(krb5_ticket *tkt) +{ +#if defined(HAVE_KRB5_TKT_ENC_PART2) + return tkt->enc_part2->client; +#else + return tkt->client; +#endif +} + +#if !defined(HAVE_KRB5_LOCATE_KDC) + krb5_error_code krb5_locate_kdc(krb5_context ctx, const krb5_data *realm, struct sockaddr **addr_pp, int *naddrs, int get_masters) +{ + krb5_krbhst_handle hnd; + krb5_krbhst_info *hinfo; + krb5_error_code rc; + int num_kdcs, i; + struct sockaddr *sa; + struct addrinfo *ai; + + *addr_pp = NULL; + *naddrs = 0; + + rc = krb5_krbhst_init(ctx, realm->data, KRB5_KRBHST_KDC, &hnd); + if (rc) { + DEBUG(0, ("krb5_locate_kdc: krb5_krbhst_init failed (%s)\n", error_message(rc))); + return rc; + } + + for ( num_kdcs = 0; (rc = krb5_krbhst_next(ctx, hnd, &hinfo) == 0); num_kdcs++) + ; + + krb5_krbhst_reset(ctx, hnd); + + if (!num_kdcs) { + DEBUG(0, ("krb5_locate_kdc: zero kdcs found !\n")); + krb5_krbhst_free(ctx, hnd); + return -1; + } + + sa = malloc_array_p(struct sockaddr, num_kdcs); + if (!sa) { + DEBUG(0, ("krb5_locate_kdc: malloc failed\n")); + krb5_krbhst_free(ctx, hnd); + naddrs = 0; + return -1; + } + + memset(sa, '\0', sizeof(struct sockaddr) * num_kdcs ); + + for (i = 0; i < num_kdcs && (rc = krb5_krbhst_next(ctx, hnd, &hinfo) == 0); i++) { + +#if defined(HAVE_KRB5_KRBHST_GET_ADDRINFO) + rc = krb5_krbhst_get_addrinfo(ctx, hinfo, &ai); + if (rc) { + DEBUG(0,("krb5_krbhst_get_addrinfo failed: %s\n", error_message(rc))); + continue; + } +#endif + if (hinfo->ai && hinfo->ai->ai_family == AF_INET) + memcpy(&sa[i], hinfo->ai->ai_addr, sizeof(struct sockaddr)); + } + + krb5_krbhst_free(ctx, hnd); + + *naddrs = num_kdcs; + *addr_pp = sa; + return 0; +} +#endif + +#if !defined(HAVE_KRB5_FREE_UNPARSED_NAME) + void krb5_free_unparsed_name(krb5_context context, char *val) +{ + SAFE_FREE(val); +} +#endif + + void kerberos_free_data_contents(krb5_context context, krb5_data *pdata) +{ +#if defined(HAVE_KRB5_FREE_DATA_CONTENTS) + if (pdata->data) { + krb5_free_data_contents(context, pdata); + } +#else + SAFE_FREE(pdata->data); +#endif +} + + void kerberos_set_creds_enctype(krb5_creds *pcreds, int enctype) +{ +#if defined(HAVE_KRB5_KEYBLOCK_IN_CREDS) + KRB5_KEY_TYPE((&pcreds->keyblock)) = enctype; +#elif defined(HAVE_KRB5_SESSION_IN_CREDS) + KRB5_KEY_TYPE((&pcreds->session)) = enctype; +#else +#error UNKNOWN_KEYBLOCK_MEMBER_IN_KRB5_CREDS_STRUCT +#endif +} + + BOOL kerberos_compatible_enctypes(krb5_context context, + krb5_enctype enctype1, + krb5_enctype enctype2) +{ +#if defined(HAVE_KRB5_C_ENCTYPE_COMPARE) + krb5_boolean similar = 0; + + krb5_c_enctype_compare(context, enctype1, enctype2, &similar); + return similar ? True : False; +#elif defined(HAVE_KRB5_ENCTYPES_COMPATIBLE_KEYS) + return krb5_enctypes_compatible_keys(context, enctype1, enctype2) ? True : False; +#endif +} + +static BOOL ads_cleanup_expired_creds(krb5_context context, + krb5_ccache ccache, + krb5_creds *credsp) +{ + krb5_error_code retval; + TALLOC_CTX *mem_ctx = talloc_init("ticket expied time"); + if (!mem_ctx) { + return False; + } + + DEBUG(3, ("Ticket in ccache[%s] expiration %s\n", + krb5_cc_default_name(context), + http_timestring(mem_ctx, credsp->times.endtime))); + + talloc_free(mem_ctx); + + /* we will probably need new tickets if the current ones + will expire within 10 seconds. + */ + if (credsp->times.endtime >= (time(NULL) + 10)) + return False; + + /* heimdal won't remove creds from a file ccache, and + perhaps we shouldn't anyway, since internally we + use memory ccaches, and a FILE one probably means that + we're using creds obtained outside of our exectuable + */ + if (StrCaseCmp(krb5_cc_get_type(context, ccache), "FILE") == 0) { + DEBUG(5, ("ads_cleanup_expired_creds: We do not remove creds from a FILE ccache\n")); + return False; + } + + retval = krb5_cc_remove_cred(context, ccache, 0, credsp); + if (retval) { + DEBUG(1, ("ads_cleanup_expired_creds: krb5_cc_remove_cred failed, err %s\n", + error_message(retval))); + /* If we have an error in this, we want to display it, + but continue as though we deleted it */ + } + return True; +} + +/* + we can't use krb5_mk_req because w2k wants the service to be in a particular format +*/ +krb5_error_code ads_krb5_mk_req(krb5_context context, + krb5_auth_context *auth_context, + const krb5_flags ap_req_options, + const char *principal, + krb5_ccache ccache, + krb5_data *outbuf) +{ + krb5_error_code retval; + krb5_principal server; + krb5_creds * credsp; + krb5_creds creds; + krb5_data in_data; + BOOL creds_ready = False; + + TALLOC_CTX *mem_ctx = NULL; + + retval = krb5_parse_name(context, principal, &server); + if (retval) { + DEBUG(1,("ads_krb5_mk_req: Failed to parse principal %s\n", principal)); + return retval; + } + + /* obtain ticket & session key */ + ZERO_STRUCT(creds); + if ((retval = krb5_copy_principal(context, server, &creds.server))) { + DEBUG(1,("krb5_copy_principal failed (%s)\n", + error_message(retval))); + goto cleanup_princ; + } + + if ((retval = krb5_cc_get_principal(context, ccache, &creds.client))) { + /* This can commonly fail on smbd startup with no ticket in the cache. + * Report at higher level than 1. */ + DEBUG(3,("ads_krb5_mk_req: krb5_cc_get_principal failed (%s)\n", + error_message(retval))); + goto cleanup_creds; + } + + while(!creds_ready) { + if ((retval = krb5_get_credentials(context, 0, ccache, + &creds, &credsp))) { + DEBUG(1,("ads_krb5_mk_req: krb5_get_credentials failed for %s (%s)\n", + principal, error_message(retval))); + goto cleanup_creds; + } + + /* cope with ticket being in the future due to clock skew */ + if ((unsigned)credsp->times.starttime > time(NULL)) { + time_t t = time(NULL); + int time_offset =(unsigned)credsp->times.starttime-t; + DEBUG(4,("ads_krb5_mk_req: Advancing clock by %d seconds to cope with clock skew\n", time_offset)); + krb5_set_real_time(context, t + time_offset + 1, 0); + } + + if (!ads_cleanup_expired_creds(context, ccache, credsp)) + creds_ready = True; + } + + mem_ctx = talloc_init("ticket expied time"); + if (!mem_ctx) { + retval = ENOMEM; + goto cleanup_creds; + } + DEBUG(10,("Ticket (%s) in ccache (%s) is valid until: (%s - %d)\n", + principal, krb5_cc_default_name(context), + http_timestring(mem_ctx, (unsigned)credsp->times.endtime), + (unsigned)credsp->times.endtime)); + + in_data.length = 0; + retval = krb5_mk_req_extended(context, auth_context, ap_req_options, + &in_data, credsp, outbuf); + if (retval) { + DEBUG(1,("ads_krb5_mk_req: krb5_mk_req_extended failed (%s)\n", + error_message(retval))); + } + + krb5_free_creds(context, credsp); + +cleanup_creds: + krb5_free_cred_contents(context, &creds); + +cleanup_princ: + krb5_free_principal(context, server); + + return retval; +} + +#if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING) && !defined(HAVE_KRB5_PRINC_COMPONENT) + const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i ) +{ + static krb5_data kdata; + + kdata.data = discard_const(krb5_principal_get_comp_string(context, principal, i)); + kdata.length = strlen(kdata.data); + return &kdata; +} +#endif + + krb5_error_code smb_krb5_kt_free_entry(krb5_context context, krb5_keytab_entry *kt_entry) +{ +#if defined(HAVE_KRB5_KT_FREE_ENTRY) + return krb5_kt_free_entry(context, kt_entry); +#elif defined(HAVE_KRB5_FREE_KEYTAB_ENTRY_CONTENTS) + return krb5_free_keytab_entry_contents(context, kt_entry); +#else +#error UNKNOWN_KT_FREE_FUNCTION +#endif +} + + char *smb_get_krb5_error_message(krb5_context context, krb5_error_code code, TALLOC_CTX *mem_ctx) +{ + char *ret; + +#if defined(HAVE_KRB5_GET_ERROR_STRING) && defined(HAVE_KRB5_FREE_ERROR_STRING) + char *context_error = krb5_get_error_string(context); + ret = talloc_asprintf(mem_ctx, "%s: %s", error_message(code), context_error); + krb5_free_error_string(context, context_error); +#else + ret = talloc_strdup(mem_ctx, error_message(code)); +#endif + return ret; +} + +#endif diff --git a/source4/auth/kerberos/gssapi_parse.c b/source4/auth/kerberos/gssapi_parse.c new file mode 100644 index 0000000000..2c2c4e17e5 --- /dev/null +++ b/source4/auth/kerberos/gssapi_parse.c @@ -0,0 +1,95 @@ +/* + Unix SMB/CIFS implementation. + + simple GSSAPI wrappers + + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Jim McDonough 2002 + Copyright (C) Luke Howard 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" +#include "asn_1.h" +#include "system/kerberos.h" +#include "auth/gensec/gensec.h" + +/* + generate a krb5 GSS-API wrapper packet given a ticket +*/ +DATA_BLOB gensec_gssapi_gen_krb5_wrap(TALLOC_CTX *mem_ctx, const DATA_BLOB *ticket, const uint8_t tok_id[2]) +{ + struct asn1_data data; + DATA_BLOB ret = data_blob(NULL,0); + + if (!ticket->data) { + return ret; + } + + ZERO_STRUCT(data); + + asn1_push_tag(&data, ASN1_APPLICATION(0)); + asn1_write_OID(&data, GENSEC_OID_KERBEROS5); + + asn1_write(&data, tok_id, 2); + asn1_write(&data, ticket->data, ticket->length); + asn1_pop_tag(&data); + + if (data.has_error) { + DEBUG(1,("Failed to build krb5 wrapper at offset %d\n", (int)data.ofs)); + asn1_free(&data); + } + + ret = data_blob_talloc(mem_ctx, data.data, data.length); + asn1_free(&data); + + return ret; +} + +/* + parse a krb5 GSS-API wrapper packet giving a ticket +*/ +BOOL gensec_gssapi_parse_krb5_wrap(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob, DATA_BLOB *ticket, uint8_t tok_id[2]) +{ + BOOL ret; + struct asn1_data data; + int data_remaining; + + asn1_load(&data, *blob); + asn1_start_tag(&data, ASN1_APPLICATION(0)); + asn1_check_OID(&data, GENSEC_OID_KERBEROS5); + + data_remaining = asn1_tag_remaining(&data); + + if (data_remaining < 3) { + data.has_error = True; + } else { + asn1_read(&data, tok_id, 2); + data_remaining -= 2; + *ticket = data_blob_talloc(mem_ctx, NULL, data_remaining); + asn1_read(&data, ticket->data, ticket->length); + } + + asn1_end_tag(&data); + + ret = !data.has_error; + + asn1_free(&data); + + return ret; +} + + diff --git a/source4/auth/kerberos/kerberos.c b/source4/auth/kerberos/kerberos.c new file mode 100644 index 0000000000..98b530e7cf --- /dev/null +++ b/source4/auth/kerberos/kerberos.c @@ -0,0 +1,788 @@ +/* + Unix SMB/CIFS implementation. + kerberos utility library + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Remus Koos 2001 + Copyright (C) Nalin Dahyabhai 2004. + Copyright (C) Jeremy Allison 2004. + Copyright (C) Andrew Bartlett 2004-2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "system/kerberos.h" +#include "system/time.h" +#include "auth/kerberos/kerberos.h" +#include "secrets.h" +#include "pstring.h" +#include "ads.h" + +#ifdef HAVE_KRB5 + +#define LIBADS_CCACHE_NAME "MEMORY:libads" + +/* + we use a prompter to avoid a crash bug in the kerberos libs when + dealing with empty passwords + this prompter is just a string copy ... +*/ +static krb5_error_code +kerb_prompter(krb5_context ctx, void *data, + const char *name, + const char *banner, + int num_prompts, + krb5_prompt prompts[]) +{ + if (num_prompts == 0) return 0; + + memset(prompts[0].reply->data, '\0', prompts[0].reply->length); + if (prompts[0].reply->length > 0) { + if (data) { + strncpy(prompts[0].reply->data, data, prompts[0].reply->length-1); + prompts[0].reply->length = strlen(prompts[0].reply->data); + } else { + prompts[0].reply->length = 0; + } + } + return 0; +} + +/* + simulate a kinit, putting the tgt in the given credentials cache. + Orignally by remus@snapserver.com +*/ + int kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc, + const char *principal, const char *password, + time_t *expire_time, time_t *kdc_time) +{ + krb5_error_code code = 0; + krb5_principal me; + krb5_creds my_creds; + krb5_get_init_creds_opt options; + + if ((code = krb5_parse_name(ctx, principal, &me))) { + return code; + } + + krb5_get_init_creds_opt_init(&options); + + if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, password, + kerb_prompter, + NULL, 0, NULL, &options))) { + krb5_free_principal(ctx, me); + return code; + } + + if ((code = krb5_cc_initialize(ctx, cc, me))) { + krb5_free_cred_contents(ctx, &my_creds); + krb5_free_principal(ctx, me); + return code; + } + + if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) { + krb5_free_cred_contents(ctx, &my_creds); + krb5_free_principal(ctx, me); + return code; + } + + if (expire_time) { + *expire_time = (time_t) my_creds.times.endtime; + } + + if (kdc_time) { + *kdc_time = (time_t) my_creds.times.starttime; + } + + krb5_free_cred_contents(ctx, &my_creds); + krb5_free_principal(ctx, me); + + return 0; +} + +/* + simulate a kinit, putting the tgt in the given credentials cache. + If cache_name == NULL place in default cache location. + + Orignally by remus@snapserver.com +*/ +int kerberos_kinit_password(const char *principal, + const char *password, + int time_offset, + time_t *expire_time, + const char *cache_name, + time_t *kdc_time) +{ + int code; + krb5_context ctx = NULL; + krb5_ccache cc = NULL; + + if ((code = krb5_init_context(&ctx))) + return code; + + if (time_offset != 0) { + krb5_set_real_time(ctx, time(NULL) + time_offset, 0); + } + + if ((code = krb5_cc_resolve(ctx, cache_name ? + cache_name : krb5_cc_default_name(ctx), &cc))) { + krb5_free_context(ctx); + return code; + } + + code = kerberos_kinit_password_cc(ctx, cc, principal, password, expire_time, kdc_time); + + krb5_cc_close(ctx, cc); + krb5_free_context(ctx); + + return code; +} + +/* run kinit to setup our ccache */ +int ads_kinit_password(struct ads_struct *ads) +{ + char *s; + int ret; + + if (asprintf(&s, "%s@%s", ads->auth.user_name, ads->auth.realm) == -1) { + return KRB5_CC_NOMEM; + } + + if (!ads->auth.password) { + return KRB5_LIBOS_CANTREADPWD; + } + + ret = kerberos_kinit_password(s, ads->auth.password, ads->auth.time_offset, + &ads->auth.expire, NULL, NULL); + + if (ret) { + DEBUG(0,("kerberos_kinit_password %s failed: %s\n", + s, error_message(ret))); + } + free(s); + return ret; +} + +int ads_kdestroy(const char *cc_name) +{ + krb5_error_code code; + krb5_context ctx = NULL; + krb5_ccache cc = NULL; + + if ((code = krb5_init_context (&ctx))) { + DEBUG(3, ("ads_kdestroy: kdb5_init_context failed: %s\n", + error_message(code))); + return code; + } + + if (!cc_name) { + if ((code = krb5_cc_default(ctx, &cc))) { + krb5_free_context(ctx); + return code; + } + } else { + if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) { + DEBUG(3, ("ads_kdestroy: krb5_cc_resolve failed: %s\n", + error_message(code))); + krb5_free_context(ctx); + return code; + } + } + + if ((code = krb5_cc_destroy (ctx, cc))) { + DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n", + error_message(code))); + } + + krb5_free_context (ctx); + return code; +} + +/************************************************************************ + Routine to fetch the salting principal for a service. Active + Directory may use a non-obvious principal name to generate the salt + when it determines the key to use for encrypting tickets for a service, + and hopefully we detected that when we joined the domain. + ************************************************************************/ + +static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype) +{ + char *ret = NULL; + +#if 0 + asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, service, enctype); + if (!key) { + return NULL; + } + ret = (char *)secrets_fetch(key, NULL); + SAFE_FREE(key); +#endif + return ret; +} + +/************************************************************************ + Routine to get the salting principal for this service. Active + Directory may use a non-obvious principal name to generate the salt + when it determines the key to use for encrypting tickets for a service, + and hopefully we detected that when we joined the domain. + Caller must free if return is not null. + ************************************************************************/ + +krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context, + krb5_principal host_princ, + int enctype) +{ + char *unparsed_name = NULL, *salt_princ_s = NULL; + krb5_principal ret_princ = NULL; + + if (krb5_unparse_name(context, host_princ, &unparsed_name) != 0) { + return (krb5_principal)NULL; + } + + if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) { + krb5_free_unparsed_name(context, unparsed_name); + return (krb5_principal)NULL; + } + + if (krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) { + krb5_free_unparsed_name(context, unparsed_name); + SAFE_FREE(salt_princ_s); + return (krb5_principal)NULL; + } + krb5_free_unparsed_name(context, unparsed_name); + SAFE_FREE(salt_princ_s); + return ret_princ; +} + +/************************************************************************ + Routine to set the salting principal for this service. Active + Directory may use a non-obvious principal name to generate the salt + when it determines the key to use for encrypting tickets for a service, + and hopefully we detected that when we joined the domain. + Setting principal to NULL deletes this entry. + ************************************************************************/ + + BOOL kerberos_secrets_store_salting_principal(const char *service, + int enctype, + const char *principal) +{ + char *key = NULL; + BOOL ret = False; + krb5_context context = NULL; + krb5_principal princ = NULL; + char *princ_s = NULL; + char *unparsed_name = NULL; + + krb5_init_context(&context); + if (!context) { + return False; + } + if (strchr_m(service, '@')) { + asprintf(&princ_s, "%s", service); + } else { + asprintf(&princ_s, "%s@%s", service, lp_realm()); + } + + if (krb5_parse_name(context, princ_s, &princ) != 0) { + goto out; + + } + if (krb5_unparse_name(context, princ, &unparsed_name) != 0) { + goto out; + } + + asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype); + if (!key) { + goto out; + } + +#if 0 + if ((principal != NULL) && (strlen(principal) > 0)) { + ret = secrets_store(key, principal, strlen(principal) + 1); + } else { + ret = secrets_delete(key); + } +#endif + + out: + + SAFE_FREE(key); + SAFE_FREE(princ_s); + + if (unparsed_name) { + krb5_free_unparsed_name(context, unparsed_name); + } + if (context) { + krb5_free_context(context); + } + + return ret; +} + +/************************************************************************ + Routine to get initial credentials as a service ticket for the local machine. + Returns a buffer initialized with krb5_mk_req_extended. + ************************************************************************/ + +static krb5_error_code get_service_ticket(krb5_context ctx, + krb5_ccache ccache, + const char *service_principal, + int enctype, + krb5_data *p_outbuf) +{ + krb5_creds creds, *new_creds = NULL; + char *service_s = NULL; + char *machine_account = NULL, *password = NULL; + krb5_data in_data; + krb5_auth_context auth_context = NULL; + krb5_error_code err = 0; + + ZERO_STRUCT(creds); + + asprintf(&machine_account, "%s$@%s", lp_netbios_name(), lp_realm()); + if (machine_account == NULL) { + goto out; + } + password = secrets_fetch_machine_password(lp_workgroup()); + if (password == NULL) { + goto out; + } + if ((err = kerberos_kinit_password(machine_account, password, 0, NULL, LIBADS_CCACHE_NAME, NULL)) != 0) { + DEBUG(0,("get_service_ticket: kerberos_kinit_password %s@%s failed: %s\n", + machine_account, + lp_realm(), + error_message(err))); + goto out; + } + + /* Ok - the above call has gotten a TGT. Now we need to get a service + ticket to ourselves. */ + + /* Set up the enctype and client and server principal fields for krb5_get_credentials. */ + kerberos_set_creds_enctype(&creds, enctype); + + if ((err = krb5_cc_get_principal(ctx, ccache, &creds.client))) { + DEBUG(3, ("get_service_ticket: krb5_cc_get_principal failed: %s\n", + error_message(err))); + goto out; + } + + if (strchr_m(service_principal, '@')) { + asprintf(&service_s, "%s", service_principal); + } else { + asprintf(&service_s, "%s@%s", service_principal, lp_realm()); + } + + if ((err = krb5_parse_name(ctx, service_s, &creds.server))) { + DEBUG(0,("get_service_ticket: krb5_parse_name %s failed: %s\n", + service_s, error_message(err))); + goto out; + } + + if ((err = krb5_get_credentials(ctx, 0, ccache, &creds, &new_creds))) { + DEBUG(5,("get_service_ticket: krb5_get_credentials for %s enctype %d failed: %s\n", + service_s, enctype, error_message(err))); + goto out; + } + + memset(&in_data, '\0', sizeof(in_data)); + if ((err = krb5_mk_req_extended(ctx, &auth_context, 0, &in_data, + new_creds, p_outbuf)) != 0) { + DEBUG(0,("get_service_ticket: krb5_mk_req_extended failed: %s\n", + error_message(err))); + goto out; + } + + out: + + if (auth_context) { + krb5_auth_con_free(ctx, auth_context); + } + if (new_creds) { + krb5_free_creds(ctx, new_creds); + } + if (creds.server) { + krb5_free_principal(ctx, creds.server); + } + if (creds.client) { + krb5_free_principal(ctx, creds.client); + } + + SAFE_FREE(service_s); + SAFE_FREE(password); + SAFE_FREE(machine_account); + return err; +} + +/************************************************************************ + Check if the machine password can be used in conjunction with the salting_principal + to generate a key which will successfully decrypt the AP_REQ already + gotten as a message to the local machine. + ************************************************************************/ + +static BOOL verify_service_password(krb5_context ctx, + int enctype, + const char *salting_principal, + krb5_data *in_data) +{ + BOOL ret = False; + krb5_principal salting_kprinc = NULL; + krb5_ticket *ticket = NULL; + krb5_keyblock key; + krb5_data passdata; + char *salting_s = NULL; + char *password = NULL; + krb5_auth_context auth_context = NULL; + krb5_error_code err; + + memset(&passdata, '\0', sizeof(passdata)); + memset(&key, '\0', sizeof(key)); + + password = secrets_fetch_machine_password(lp_workgroup()); + if (password == NULL) { + goto out; + } + + if (strchr_m(salting_principal, '@')) { + asprintf(&salting_s, "%s", salting_principal); + } else { + asprintf(&salting_s, "%s@%s", salting_principal, lp_realm()); + } + + if ((err = krb5_parse_name(ctx, salting_s, &salting_kprinc))) { + DEBUG(0,("verify_service_password: krb5_parse_name %s failed: %s\n", + salting_s, error_message(err))); + goto out; + } + + passdata.length = strlen(password); + passdata.data = (char*)password; + if ((err = create_kerberos_key_from_string_direct(ctx, salting_kprinc, &passdata, &key, enctype))) { + DEBUG(0,("verify_service_password: create_kerberos_key_from_string %d failed: %s\n", + enctype, error_message(err))); + goto out; + } + + if ((err = krb5_auth_con_init(ctx, &auth_context)) != 0) { + DEBUG(0,("verify_service_password: krb5_auth_con_init failed %s\n", error_message(err))); + goto out; + } + + if ((err = krb5_auth_con_setuseruserkey(ctx, auth_context, &key)) != 0) { + DEBUG(0,("verify_service_password: krb5_auth_con_setuseruserkey failed %s\n", error_message(err))); + goto out; + } + + if (!(err = krb5_rd_req(ctx, &auth_context, in_data, NULL, NULL, NULL, &ticket))) { + DEBUG(10,("verify_service_password: decrypted message with enctype %u salt %s!\n", + (unsigned int)enctype, salting_s)); + ret = True; + } + + out: + + memset(&passdata, 0, sizeof(passdata)); + krb5_free_keyblock_contents(ctx, &key); + if (ticket != NULL) { + krb5_free_ticket(ctx, ticket); + } + if (salting_kprinc) { + krb5_free_principal(ctx, salting_kprinc); + } + SAFE_FREE(salting_s); + SAFE_FREE(password); + return ret; +} + +/************************************************************************ + * + * From the current draft of kerberos-clarifications: + * + * It is not possible to reliably generate a user's key given a pass + * phrase without contacting the KDC, since it will not be known + * whether alternate salt or parameter values are required. + * + * And because our server has a password, we have this exact problem. We + * make multiple guesses as to which principal name provides the salt which + * the KDC is using. + * + ************************************************************************/ + +static void kerberos_derive_salting_principal_for_enctype(const char *service_principal, + krb5_context ctx, + krb5_ccache ccache, + krb5_enctype enctype, + krb5_enctype *enctypes) +{ + char *salting_principals[3] = {NULL, NULL, NULL}, *second_principal = NULL; + krb5_error_code err = 0; + krb5_data outbuf; + int i, j; + + memset(&outbuf, '\0', sizeof(outbuf)); + + /* Check that the service_principal is useful. */ + if ((service_principal == NULL) || (strlen(service_principal) == 0)) { + return; + } + + /* Generate our first guess -- the principal as-given. */ + asprintf(&salting_principals[0], "%s", service_principal); + if ((salting_principals[0] == NULL) || (strlen(salting_principals[0]) == 0)) { + return; + } + + /* Generate our second guess -- the computer's principal, as Win2k3. */ + asprintf(&second_principal, "host/%s.%s", lp_netbios_name(), lp_realm()); + if (second_principal != NULL) { + strlower_m(second_principal); + asprintf(&salting_principals[1], "%s@%s", second_principal, lp_realm()); + SAFE_FREE(second_principal); + } + if ((salting_principals[1] == NULL) || (strlen(salting_principals[1]) == 0)) { + goto out; + } + + /* Generate our third guess -- the computer's principal, as Win2k. */ + asprintf(&second_principal, "HOST/%s", lp_netbios_name()); + if (second_principal != NULL) { + strlower_m(second_principal + 5); + asprintf(&salting_principals[2], "%s@%s", + second_principal, lp_realm()); + SAFE_FREE(second_principal); + } + if ((salting_principals[2] == NULL) || (strlen(salting_principals[2]) == 0)) { + goto out; + } + + /* Get a service ticket for ourselves into our memory ccache. */ + /* This will commonly fail if there is no principal by that name (and we're trying + many names). So don't print a debug 0 error. */ + + if ((err = get_service_ticket(ctx, ccache, service_principal, enctype, &outbuf)) != 0) { + DEBUG(3, ("verify_service_password: get_service_ticket failed: %s\n", + error_message(err))); + goto out; + } + + /* At this point we have a message to ourselves, salted only the KDC knows how. We + have to work out what that salting is. */ + + /* Try and find the correct salting principal. */ + for (i = 0; i < sizeof(salting_principals) / sizeof(salting_principals[i]); i++) { + if (verify_service_password(ctx, enctype, salting_principals[i], &outbuf)) { + break; + } + } + + /* If we failed to get a match, return. */ + if (i >= sizeof(salting_principals) / sizeof(salting_principals[i])) { + goto out; + } + + /* If we succeeded, store the principal for use for all enctypes which + * share the same cipher and string-to-key function. Doing this here + * allows servers which just pass a keytab to krb5_rd_req() to work + * correctly. */ + for (j = 0; enctypes[j] != 0; j++) { + if (enctype != enctypes[j]) { + /* If this enctype isn't compatible with the one which + * we used, skip it. */ + + if (!kerberos_compatible_enctypes(ctx, enctypes[j], enctype)) + continue; + } + /* If the principal which gives us the proper salt is the one + * which we would normally guess, don't bother noting anything + * in the secrets tdb. */ + if (strcmp(service_principal, salting_principals[i]) != 0) { + kerberos_secrets_store_salting_principal(service_principal, + enctypes[j], + salting_principals[i]); + } + } + + out : + + kerberos_free_data_contents(ctx, &outbuf); + SAFE_FREE(salting_principals[0]); + SAFE_FREE(salting_principals[1]); + SAFE_FREE(salting_principals[2]); + SAFE_FREE(second_principal); +} + +/************************************************************************ + Go through all the possible enctypes for this principal. + ************************************************************************/ + +static void kerberos_derive_salting_principal_direct(krb5_context context, + krb5_ccache ccache, + krb5_enctype *enctypes, + char *service_principal) +{ + int i; + + /* Try for each enctype separately, because the rules are + * different for different enctypes. */ + for (i = 0; enctypes[i] != 0; i++) { + /* Delete secrets entry first. */ + kerberos_secrets_store_salting_principal(service_principal, 0, NULL); +#ifdef ENCTYPE_ARCFOUR_HMAC + if (enctypes[i] == ENCTYPE_ARCFOUR_HMAC) { + /* Of course this'll always work, so just save + * ourselves the effort. */ + continue; + } +#endif + /* Try to figure out what's going on with this + * principal. */ + kerberos_derive_salting_principal_for_enctype(service_principal, + context, + ccache, + enctypes[i], + enctypes); + } +} + +/************************************************************************ + Wrapper function for the above. + ************************************************************************/ + +BOOL kerberos_derive_salting_principal(char *service_principal) +{ + krb5_context context = NULL; + krb5_enctype *enctypes = NULL; + krb5_ccache ccache = NULL; + krb5_error_code ret = 0; + + initialize_krb5_error_table(); + if ((ret = krb5_init_context(&context)) != 0) { + DEBUG(1,("kerberos_derive_cifs_salting_principals: krb5_init_context failed. %s\n", + error_message(ret))); + return False; + } + if ((ret = get_kerberos_allowed_etypes(context, &enctypes)) != 0) { + DEBUG(1,("kerberos_derive_cifs_salting_principals: get_kerberos_allowed_etypes failed. %s\n", + error_message(ret))); + goto out; + } + + if ((ret = krb5_cc_resolve(context, LIBADS_CCACHE_NAME, &ccache)) != 0) { + DEBUG(3, ("get_service_ticket: krb5_cc_resolve for %s failed: %s\n", + LIBADS_CCACHE_NAME, error_message(ret))); + goto out; + } + + kerberos_derive_salting_principal_direct(context, ccache, enctypes, service_principal); + + out: + if (enctypes) { + free_kerberos_etypes(context, enctypes); + } + if (ccache) { + krb5_cc_destroy(context, ccache); + } + if (context) { + krb5_free_context(context); + } + + return ret ? False : True; +} + +/************************************************************************ + Core function to try and determine what salt is being used for any keytab + keys. + ************************************************************************/ + +BOOL kerberos_derive_cifs_salting_principals(void) +{ + fstring my_fqdn; + char *service = NULL; + krb5_context context = NULL; + krb5_enctype *enctypes = NULL; + krb5_ccache ccache = NULL; + krb5_error_code ret = 0; + BOOL retval = False; + + initialize_krb5_error_table(); + if ((ret = krb5_init_context(&context)) != 0) { + DEBUG(1,("kerberos_derive_cifs_salting_principals: krb5_init_context failed. %s\n", + error_message(ret))); + return False; + } + if ((ret = get_kerberos_allowed_etypes(context, &enctypes)) != 0) { + DEBUG(1,("kerberos_derive_cifs_salting_principals: get_kerberos_allowed_etypes failed. %s\n", + error_message(ret))); + goto out; + } + + if ((ret = krb5_cc_resolve(context, LIBADS_CCACHE_NAME, &ccache)) != 0) { + DEBUG(3, ("get_service_ticket: krb5_cc_resolve for %s failed: %s\n", + LIBADS_CCACHE_NAME, error_message(ret))); + goto out; + } + + if (asprintf(&service, "%s$", lp_netbios_name()) != -1) { + strlower_m(service); + kerberos_derive_salting_principal_direct(context, ccache, enctypes, service); + SAFE_FREE(service); + } + if (asprintf(&service, "cifs/%s", lp_netbios_name()) != -1) { + strlower_m(service); + kerberos_derive_salting_principal_direct(context, ccache, enctypes, service); + SAFE_FREE(service); + } + if (asprintf(&service, "host/%s", lp_netbios_name()) != -1) { + strlower_m(service); + kerberos_derive_salting_principal_direct(context, ccache, enctypes, service); + SAFE_FREE(service); + } + if (asprintf(&service, "cifs/%s.%s", lp_netbios_name(), lp_realm()) != -1) { + strlower_m(service); + kerberos_derive_salting_principal_direct(context, ccache, enctypes, service); + SAFE_FREE(service); + } + if (asprintf(&service, "host/%s.%s", lp_netbios_name(), lp_realm()) != -1) { + strlower_m(service); + kerberos_derive_salting_principal_direct(context, ccache, enctypes, service); + SAFE_FREE(service); + } + name_to_fqdn(my_fqdn, lp_netbios_name()); + if (asprintf(&service, "cifs/%s", my_fqdn) != -1) { + strlower_m(service); + kerberos_derive_salting_principal_direct(context, ccache, enctypes, service); + SAFE_FREE(service); + } + if (asprintf(&service, "host/%s", my_fqdn) != -1) { + strlower_m(service); + kerberos_derive_salting_principal_direct(context, ccache, enctypes, service); + SAFE_FREE(service); + } + + retval = True; + + out: + if (enctypes) { + free_kerberos_etypes(context, enctypes); + } + if (ccache) { + krb5_cc_destroy(context, ccache); + } + if (context) { + krb5_free_context(context); + } + return retval; +} +#endif diff --git a/source4/auth/kerberos/kerberos.h b/source4/auth/kerberos/kerberos.h new file mode 100644 index 0000000000..4daf0ea07a --- /dev/null +++ b/source4/auth/kerberos/kerberos.h @@ -0,0 +1,99 @@ +/* + Unix SMB/CIFS implementation. + simple kerberos5 routines for active directory + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Luke Howard 2002-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. +*/ + +#if defined(HAVE_KRB5) + +/* not really ASN.1, but RFC 1964 */ +#define TOK_ID_KRB_AP_REQ "\x01\x00" +#define TOK_ID_KRB_AP_REP "\x02\x00" +#define TOK_ID_KRB_ERROR "\x03\x00" +#define TOK_ID_GSS_GETMIC "\x01\x01" +#define TOK_ID_GSS_WRAP "\x02\x01" + +#ifdef HAVE_KRB5_KEYBLOCK_KEYVALUE +#define KRB5_KEY_TYPE(k) ((k)->keytype) +#define KRB5_KEY_LENGTH(k) ((k)->keyvalue.length) +#define KRB5_KEY_DATA(k) ((k)->keyvalue.data) +#else +#define KRB5_KEY_TYPE(k) ((k)->enctype) +#define KRB5_KEY_LENGTH(k) ((k)->length) +#define KRB5_KEY_DATA(k) ((k)->contents) +#endif /* HAVE_KRB5_KEYBLOCK_KEYVALUE */ + +#ifndef HAVE_KRB5_SET_REAL_TIME +krb5_error_code krb5_set_real_time(krb5_context context, int32_t seconds, int32_t microseconds); +#endif + +#ifndef HAVE_KRB5_SET_DEFAULT_TGS_KTYPES +krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc); +#endif + +#if defined(HAVE_KRB5_AUTH_CON_SETKEY) && !defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY) +krb5_error_code krb5_auth_con_setuseruserkey(krb5_context context, krb5_auth_context auth_context, krb5_keyblock *keyblock); +#endif + +#ifndef HAVE_KRB5_FREE_UNPARSED_NAME +void krb5_free_unparsed_name(krb5_context ctx, char *val); +#endif + +#if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING) && !defined(HAVE_KRB5_PRINC_COMPONENT) +const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i ); +#endif + +/* Samba wrapper function for krb5 functionality. */ +void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr); +int create_kerberos_key_from_string(krb5_context context, krb5_principal host_princ, krb5_data *password, krb5_keyblock *key, krb5_enctype enctype); +int create_kerberos_key_from_string_direct(krb5_context context, krb5_principal host_princ, krb5_data *password, krb5_keyblock *key, krb5_enctype enctype); +krb5_const_principal get_principal_from_tkt(krb5_ticket *tkt); +krb5_error_code krb5_locate_kdc(krb5_context ctx, const krb5_data *realm, struct sockaddr **addr_pp, int *naddrs, int get_masters); +krb5_error_code get_kerberos_allowed_etypes(krb5_context context, krb5_enctype **enctypes); +void free_kerberos_etypes(krb5_context context, krb5_enctype *enctypes); +BOOL get_krb5_smb_session_key(krb5_context context, krb5_auth_context auth_context, DATA_BLOB *session_key, BOOL remote); +krb5_error_code ads_krb5_mk_req(krb5_context context, + krb5_auth_context *auth_context, + const krb5_flags ap_req_options, + const char *principal, + krb5_ccache ccache, + krb5_data *outbuf); +DATA_BLOB get_auth_data_from_tkt(TALLOC_CTX *mem_ctx, + krb5_ticket *tkt); + +NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx, + krb5_context context, + krb5_auth_context auth_context, + const char *realm, const char *service, + const DATA_BLOB *ticket, + char **principal, DATA_BLOB *auth_data, + DATA_BLOB *ap_rep, + krb5_keyblock *keyblock); +int kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc, + const char *principal, const char *password, + time_t *expire_time, time_t *kdc_time); +krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context, + krb5_principal host_princ, + int enctype); +void kerberos_set_creds_enctype(krb5_creds *pcreds, int enctype); +BOOL kerberos_compatible_enctypes(krb5_context context, krb5_enctype enctype1, krb5_enctype enctype2); +void kerberos_free_data_contents(krb5_context context, krb5_data *pdata); +krb5_error_code smb_krb5_kt_free_entry(krb5_context context, krb5_keytab_entry *kt_entry); +char *smb_get_krb5_error_message(krb5_context context, krb5_error_code code, TALLOC_CTX *mem_ctx); +#endif /* HAVE_KRB5 */ + diff --git a/source4/auth/kerberos/kerberos.m4 b/source4/auth/kerberos/kerberos.m4 new file mode 100644 index 0000000000..f18386a91a --- /dev/null +++ b/source4/auth/kerberos/kerberos.m4 @@ -0,0 +1,491 @@ +################################################# +# KRB5 support +KRB5_CFLAGS="" +KRB5_CPPFLAGS="" +KRB5_LDFLAGS="" +KRB5_LIBS="" +with_krb5_support=auto +krb5_withval=auto +AC_MSG_CHECKING([for KRB5 support]) + +# Do no harm to the values of CFLAGS and LIBS while testing for +# Kerberos support. +AC_ARG_WITH(krb5, +[ --with-krb5=base-dir Locate Kerberos 5 support (default=auto)], + [ case "$withval" in + no) + with_krb5_support=no + AC_MSG_RESULT(no) + krb5_withval=no + ;; + yes) + with_krb5_support=yes + AC_MSG_RESULT(yes) + krb5_withval=yes + ;; + auto) + with_krb5_support=auto + AC_MSG_RESULT(auto) + krb5_withval=auto + ;; + *) + with_krb5_support=yes + AC_MSG_RESULT(yes) + krb5_withval=$withval + KRB5CONFIG="$krb5_withval/bin/krb5-config" + ;; + esac ], + AC_MSG_RESULT($with_krb5_support) +) + +if test x$with_krb5_support != x"no"; then + FOUND_KRB5=no + FOUND_KRB5_VIA_CONFIG=no + + ################################################# + # check for krb5-config from recent MIT and Heimdal kerberos 5 + AC_MSG_CHECKING(for working specified location for krb5-config) + if test x$KRB5CONFIG != "x"; then + if test -x "$KRB5CONFIG"; then + ac_save_CFLAGS=$CFLAGS + CFLAGS="";export CFLAGS + ac_save_LDFLAGS=$LDFLAGS + LDFLAGS="";export LDFLAGS + KRB5_LIBS="`$KRB5CONFIG --libs gssapi`" + KRB5_CFLAGS="`$KRB5CONFIG --cflags | sed s/@INCLUDE_des@//`" + KRB5_CPPFLAGS="`$KRB5CONFIG --cflags | sed s/@INCLUDE_des@//`" + CFLAGS=$ac_save_CFLAGS;export CFLAGS + LDFLAGS=$ac_save_LDFLAGS;export LDFLAGS + FOUND_KRB5=yes + FOUND_KRB5_VIA_CONFIG=yes + AC_MSG_RESULT(yes. Found $KRB5CONFIG) + else + AC_MSG_RESULT(no. Fallback to specified directory) + fi + else + AC_MSG_RESULT(no. Fallback to finding krb5-config in path) + ################################################# + # check for krb5-config from recent MIT and Heimdal kerberos 5 + AC_PATH_PROG(KRB5CONFIG, krb5-config) + AC_MSG_CHECKING(for working krb5-config in path) + if test -x "$KRB5CONFIG"; then + ac_save_CFLAGS=$CFLAGS + CFLAGS="";export CFLAGS + ac_save_LDFLAGS=$LDFLAGS + LDFLAGS="";export LDFLAGS + KRB5_LIBS="`$KRB5CONFIG --libs gssapi`" + KRB5_CFLAGS="`$KRB5CONFIG --cflags | sed s/@INCLUDE_des@//`" + KRB5_CPPFLAGS="`$KRB5CONFIG --cflags | sed s/@INCLUDE_des@//`" + CFLAGS=$ac_save_CFLAGS;export CFLAGS + LDFLAGS=$ac_save_LDFLAGS;export LDFLAGS + FOUND_KRB5=yes + FOUND_KRB5_VIA_CONFIG=yes + AC_MSG_RESULT(yes. Found $KRB5CONFIG) + else + AC_MSG_RESULT(no. Fallback to previous krb5 detection strategy) + fi + fi + + if test x$FOUND_KRB5 != x"yes"; then + ################################################# + # check for location of Kerberos 5 install + AC_MSG_CHECKING(for kerberos 5 install path) + case "$krb5_withval" in + no) + AC_MSG_RESULT(no krb5-path given) + ;; + yes) + AC_MSG_RESULT(/usr) + FOUND_KRB5=yes + ;; + *) + AC_MSG_RESULT($krb5_withval) + KRB5_CFLAGS="-I$krb5_withval/include" + KRB5_CPPFLAGS="-I$krb5_withval/include" + KRB5_LDFLAGS="-L$krb5_withval/lib" + FOUND_KRB5=yes + ;; + esac + fi + + if test x$FOUND_KRB5 != x"yes"; then + ################################################# + # see if this box has the SuSE location for the heimdal krb implementation + AC_MSG_CHECKING(for /usr/include/heimdal) + if test -d /usr/include/heimdal; then + if test -f /usr/lib/heimdal/lib/libkrb5.a; then + KRB5_CFLAGS="-I/usr/include/heimdal" + KRB5_CPPFLAGS="-I/usr/include/heimdal" + KRB5_LDFLAGS="-L/usr/lib/heimdal/lib" + AC_MSG_RESULT(yes) + else + KRB5_CFLAGS="-I/usr/include/heimdal" + KRB5_CPPFLAGS="-I/usr/include/heimdal" + AC_MSG_RESULT(yes) + fi + else + AC_MSG_RESULT(no) + fi + fi + + if test x$FOUND_KRB5 != x"yes"; then + ################################################# + # see if this box has the RedHat location for kerberos + AC_MSG_CHECKING(for /usr/kerberos) + if test -d /usr/kerberos -a -f /usr/kerberos/lib/libkrb5.a; then + KRB5_LDFLAGS="-L/usr/kerberos/lib" + KRB5_CFLAGS="-I/usr/kerberos/include" + KRB5_CPPFLAGS="-I/usr/kerberos/include" + AC_MSG_RESULT(yes) + else + AC_MSG_RESULT(no) + fi + fi + + ac_save_CFLAGS=$CFLAGS + ac_save_CPPFLAGS=$CPPFLAGS + ac_save_LDFLAGS=$LDFLAGS + + #MIT needs this, to let us see 'internal' parts of the headers we use + KRB5_CFLAGS="${KRB5_CFLAGS} -DKRB5_PRIVATE -DKRB5_DEPRECATED" + + #Heimdal needs this + #TODO: we need to parse KRB5_LIBS for -L path + # and set -Wl,-rpath -Wl,path + + CFLAGS="$CFLAGS $KRB5_CFLAGS" + CPPFLAGS="$CPPFLAGS $KRB5_CPPFLAGS" + LDFLAGS="$LDFLAGS $KRB5_LDFLAGS" + + KRB5_LIBS="$KRB5_LDFLAGS $KRB5_LIBS" + + # now check for krb5.h. Some systems have the libraries without the headers! + # note that this check is done here to allow for different kerberos + # include paths + AC_CHECK_HEADERS(krb5.h) + + if test x"$ac_cv_header_krb5_h" = x"no"; then + # Give a warning if KRB5 support was not explicitly requested, + # i.e with_krb5_support = auto, otherwise die with an error. + if test x"$with_krb5_support" = x"yes"; then + AC_MSG_ERROR([KRB5 cannot be supported without krb5.h]) + else + AC_MSG_WARN([KRB5 cannot be supported without krb5.h]) + fi + # Turn off AD support and restore CFLAGS and LIBS variables + with_krb5_support="no" + fi + + CFLAGS=$ac_save_CFLAGS + CPPFLAGS=$ac_save_CPPFLAGS + LDFLAGS=$ac_save_LDFLAGS +fi + +# Now we have determined whether we really want KRB5 support + +if test x"$with_krb5_support" != x"no"; then + ac_save_CFLAGS=$CFLAGS + ac_save_CPPFLAGS=$CPPFLAGS + ac_save_LDFLAGS=$LDFLAGS + ac_save_LIBS=$LIBS + + CFLAGS="$CFLAGS $KRB5_CFLAGS" + CPPFLAGS="$CPPFLAGS $KRB5_CPPFLAGS" + LDFLAGS="$LDFLAGS $KRB5_LDFLAGS" + + # now check for gssapi headers. This is also done here to allow for + # different kerberos include paths + AC_CHECK_HEADERS(gssapi.h gssapi/gssapi_generic.h gssapi/gssapi.h com_err.h) + + ################################################################## + # we might need the k5crypto and com_err libraries on some systems + AC_CHECK_LIB_EXT(com_err, KRB5_LIBS, _et_list) + AC_CHECK_LIB_EXT(k5crypto, KRB5_LIBS, krb5_encrypt_data) + + # Heimdal checks. + # But only if we didn't have a krb5-config to tell us this already + if test x"$FOUND_KRB5_VIA_CONFIG" != x"yes"; then + AC_CHECK_LIB_EXT(crypto, KRB5_LIBS, des_set_key) + AC_CHECK_LIB_EXT(asn1, KRB5_LIBS, copy_Authenticator) + AC_CHECK_LIB_EXT(roken, KRB5_LIBS, roken_getaddrinfo_hostspec) + fi + + # Heimdal checks. On static Heimdal gssapi must be linked before krb5. + AC_CHECK_LIB_EXT(gssapi, KRB5_LIBS, gss_display_status,[],[], + AC_DEFINE(HAVE_GSSAPI,1,[Whether GSSAPI is available])) + + ######################################################## + # now see if we can find the krb5 libs in standard paths + # or as specified above + AC_CHECK_LIB_EXT(krb5, KRB5_LIBS, krb5_mk_req_extended) + AC_CHECK_LIB_EXT(krb5, KRB5_LIBS, krb5_kt_compare) + + ######################################################## + # now see if we can find the gssapi libs in standard paths + if test x"$ac_cv_lib_ext_gssapi_gss_display_status" != x"yes"; then + AC_CHECK_LIB_EXT(gssapi_krb5, KRB5_LIBS,gss_display_status,[],[], + AC_DEFINE(HAVE_GSSAPI,1,[Whether GSSAPI is available])) + fi + + AC_CHECK_FUNC_EXT(krb5_set_real_time, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_set_default_in_tkt_etypes, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_set_default_tgs_ktypes, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_principal2salt, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_use_enctype, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_string_to_key, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_get_pw_salt, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_string_to_key_salt, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_auth_con_setkey, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_auth_con_setuseruserkey, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_locate_kdc, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_get_permitted_enctypes, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_get_default_in_tkt_etypes, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_free_ktypes, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_free_data_contents, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_principal_get_comp_string, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_free_unparsed_name, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_free_keytab_entry_contents, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_kt_free_entry, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_krbhst_get_addrinfo, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_verify_checksum, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_c_verify_checksum, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_ticket_get_authorization_data_type, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_krbhst_get_addrinfo, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_c_enctype_compare, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_enctypes_compatible_keys, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_get_error_string, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_free_error_string, $KRB5_LIBS) + + LIBS="$LIBS $KRB5_LIBS" + + AC_CACHE_CHECK([for krb5_encrypt_block type], + samba_cv_HAVE_KRB5_ENCRYPT_BLOCK,[ + AC_TRY_COMPILE([#include ], + [krb5_encrypt_block block;], + samba_cv_HAVE_KRB5_ENCRYPT_BLOCK=yes, + samba_cv_HAVE_KRB5_ENCRYPT_BLOCK=no)]) + + if test x"$samba_cv_HAVE_KRB5_ENCRYPT_BLOCK" = x"yes"; then + AC_DEFINE(HAVE_KRB5_ENCRYPT_BLOCK,1, + [Whether the type krb5_encrypt_block exists]) + fi + + AC_CACHE_CHECK([for addrtype in krb5_address], + samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS,[ + AC_TRY_COMPILE([#include ], + [krb5_address kaddr; kaddr.addrtype = ADDRTYPE_INET;], + samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS=yes, + samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS=no)]) + if test x"$samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS" = x"yes"; then + AC_DEFINE(HAVE_ADDRTYPE_IN_KRB5_ADDRESS,1, + [Whether the krb5_address struct has a addrtype property]) + fi + + AC_CACHE_CHECK([for addr_type in krb5_address], + samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS,[ + AC_TRY_COMPILE([#include ], + [krb5_address kaddr; kaddr.addr_type = KRB5_ADDRESS_INET;], + samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS=yes, + samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS=no)]) + if test x"$samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS" = x"yes"; then + AC_DEFINE(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS,1, + [Whether the krb5_address struct has a addr_type property]) + fi + + AC_CACHE_CHECK([for enc_part2 in krb5_ticket], + samba_cv_HAVE_KRB5_TKT_ENC_PART2,[ + AC_TRY_COMPILE([#include ], + [krb5_ticket tkt; tkt.enc_part2->authorization_data[0]->contents = NULL;], + samba_cv_HAVE_KRB5_TKT_ENC_PART2=yes, + samba_cv_HAVE_KRB5_TKT_ENC_PART2=no)]) + if test x"$samba_cv_HAVE_KRB5_TKT_ENC_PART2" = x"yes"; then + AC_DEFINE(HAVE_KRB5_TKT_ENC_PART2,1, + [Whether the krb5_ticket struct has a enc_part2 property]) + fi + + AC_CACHE_CHECK([for keyblock in krb5_creds], + samba_cv_HAVE_KRB5_KEYBLOCK_IN_CREDS,[ + AC_TRY_COMPILE([#include ], + [krb5_creds creds; krb5_keyblock kb; creds.keyblock = kb;], + samba_cv_HAVE_KRB5_KEYBLOCK_IN_CREDS=yes, + samba_cv_HAVE_KRB5_KEYBLOCK_IN_CREDS=no)]) + + if test x"$samba_cv_HAVE_KRB5_KEYBLOCK_IN_CREDS" = x"yes"; then + AC_DEFINE(HAVE_KRB5_KEYBLOCK_IN_CREDS,1, + [Whether the krb5_creds struct has a keyblock property]) + fi + + AC_CACHE_CHECK([for session in krb5_creds], + samba_cv_HAVE_KRB5_SESSION_IN_CREDS,[ + AC_TRY_COMPILE([#include ], + [krb5_creds creds; krb5_keyblock kb; creds.session = kb;], + samba_cv_HAVE_KRB5_SESSION_IN_CREDS=yes, + samba_cv_HAVE_KRB5_SESSION_IN_CREDS=no)]) + + if test x"$samba_cv_HAVE_KRB5_SESSION_IN_CREDS" = x"yes"; then + AC_DEFINE(HAVE_KRB5_SESSION_IN_CREDS,1, + [Whether the krb5_creds struct has a session property]) + fi + + AC_CACHE_CHECK([for keyvalue in krb5_keyblock], + samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE,[ + AC_TRY_COMPILE([#include ], + [krb5_keyblock key; key.keyvalue.data = NULL;], + samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE=yes, + samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE=no)]) + if test x"$samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE" = x"yes"; then + AC_DEFINE(HAVE_KRB5_KEYBLOCK_KEYVALUE,1, + [Whether the krb5_keyblock struct has a keyvalue property]) + fi + + AC_CACHE_CHECK([for ENCTYPE_ARCFOUR_HMAC_MD5], + samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5,[ + AC_TRY_COMPILE([#include ], + [krb5_enctype enctype; enctype = ENCTYPE_ARCFOUR_HMAC_MD5;], + samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5=yes, + samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5=no)]) + AC_CACHE_CHECK([for KEYTYPE_ARCFOUR_56], + samba_cv_HAVE_KEYTYPE_ARCFOUR_56,[ + AC_TRY_COMPILE([#include ], + [krb5_keytype keytype; keytype = KEYTYPE_ARCFOUR_56;], + samba_cv_HAVE_KEYTYPE_ARCFOUR_56=yes, + samba_cv_HAVE_KEYTYPE_ARCFOUR_56=no)]) + # Heimdals with KEYTYPE_ARCFOUR but not KEYTYPE_ARCFOUR_56 are broken + # w.r.t. arcfour and windows, so we must not enable it here + if test x"$samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5" = x"yes" -a\ + x"$samba_cv_HAVE_KEYTYPE_ARCFOUR_56" = x"yes"; then + AC_DEFINE(HAVE_ENCTYPE_ARCFOUR_HMAC_MD5,1, + [Whether the ENCTYPE_ARCFOUR_HMAC_MD5 key type is available]) + fi + + AC_CACHE_CHECK([for AP_OPTS_USE_SUBKEY], + samba_cv_HAVE_AP_OPTS_USE_SUBKEY,[ + AC_TRY_COMPILE([#include ], + [krb5_flags ap_options; ap_options = AP_OPTS_USE_SUBKEY;], + samba_cv_HAVE_AP_OPTS_USE_SUBKEY=yes, + samba_cv_HAVE_AP_OPTS_USE_SUBKEY=no)]) + if test x"$samba_cv_HAVE_AP_OPTS_USE_SUBKEY" = x"yes"; then + AC_DEFINE(HAVE_AP_OPTS_USE_SUBKEY,1, + [Whether the AP_OPTS_USE_SUBKEY ap option is available]) + fi + + AC_CACHE_CHECK([for KV5M_KEYTAB], + samba_cv_HAVE_KV5M_KEYTAB,[ + AC_TRY_COMPILE([#include ], + [krb5_keytab_entry entry; entry.magic = KV5M_KEYTAB;], + samba_cv_HAVE_KV5M_KEYTAB=yes, + samba_cv_HAVE_KV5M_KEYTAB=no)]) + if test x"$samba_cv_HAVE_KV5M_KEYTAB" = x"yes"; then + AC_DEFINE(HAVE_KV5M_KEYTAB,1, + [Whether the KV5M_KEYTAB option is available]) + fi + + AC_CACHE_CHECK([for the krb5_princ_component macro], + samba_cv_HAVE_KRB5_PRINC_COMPONENT,[ + AC_TRY_LINK([#include ], + [const krb5_data *pkdata; krb5_context context; krb5_principal principal; + pkdata = krb5_princ_component(context, principal, 0);], + samba_cv_HAVE_KRB5_PRINC_COMPONENT=yes, + samba_cv_HAVE_KRB5_PRINC_COMPONENT=no)]) + if test x"$samba_cv_HAVE_KRB5_PRINC_COMPONENT" = x"yes"; then + AC_DEFINE(HAVE_KRB5_PRINC_COMPONENT,1, + [Whether krb5_princ_component is available]) + fi + + AC_CACHE_CHECK([for key in krb5_keytab_entry], + samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEY,[ + AC_TRY_COMPILE([#include ], + [krb5_keytab_entry entry; krb5_keyblock e; entry.key = e;], + samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEY=yes, + samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEY=no)]) + if test x"$samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEY" = x"yes"; then + AC_DEFINE(HAVE_KRB5_KEYTAB_ENTRY_KEY,1, + [Whether krb5_keytab_entry has key member]) + fi + + AC_CACHE_CHECK([for keyblock in krb5_keytab_entry], + samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK,[ + AC_TRY_COMPILE([#include ], + [krb5_keytab_entry entry; entry.keyblock.keytype = 0;], + samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK=yes, + samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK=no)]) + if test x"$samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK" = x"yes"; then + AC_DEFINE(HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK,1, + [Whether krb5_keytab_entry has keyblock member]) + fi + + AC_CACHE_CHECK([for WRFILE: keytab support], + samba_cv_HAVE_WRFILE_KEYTAB,[ + AC_TRY_RUN([ + #include + main() + { + krb5_context context; + krb5_keytab keytab; + krb5_init_context(&context); + return krb5_kt_resolve(context, "WRFILE:api", &keytab); + }], + samba_cv_HAVE_WRFILE_KEYTAB=yes, + samba_cv_HAVE_WRFILE_KEYTAB=no)]) + if test x"$samba_cv_HAVE_WRFILE_KEYTAB" = x"yes"; then + AC_DEFINE(HAVE_WRFILE_KEYTAB,1, + [Whether the WRFILE:-keytab is supported]) + fi + + AC_CACHE_CHECK([for krb5_princ_realm returns krb5_realm or krb5_data], + samba_cv_KRB5_PRINC_REALM_RETURNS_REALM,[ + AC_TRY_COMPILE([#include ], + [krb5_context context;krb5_principal principal;krb5_realm realm; + realm = *krb5_princ_realm(context, principal);], + samba_cv_KRB5_PRINC_REALM_RETURNS_REALM=yes, + samba_cv_KRB5_PRINC_REALM_RETURNS_REALM=no)]) + if test x"$samba_cv_KRB5_PRINC_REALM_RETURNS_REALM" = x"yes"; then + AC_DEFINE(KRB5_PRINC_REALM_RETURNS_REALM,1, + [Whether krb5_princ_realm returns krb5_realm or krb5_data]) + fi + + # TODO: check all gssapi headers for this + AC_CACHE_CHECK([for GSS_C_DCE_STYLE in gssapi.h], + samba_cv_GSS_C_DCE_STYLE,[ + AC_TRY_COMPILE([#include ], + [int flags = GSS_C_DCE_STYLE;], + samba_cv_GSS_C_DCE_STYLE=yes, + samba_cv_GSS_C_DCE_STYLE=no)]) + + if test x"$ac_cv_lib_ext_krb5_krb5_mk_req_extended" = x"yes"; then + AC_DEFINE(HAVE_KRB5,1,[Whether to have KRB5 support]) + AC_MSG_CHECKING(whether KRB5 support is used) + SMB_EXT_LIB_ENABLE(KRB5,YES) + AC_MSG_RESULT(yes) + echo "KRB5_CFLAGS: ${KRB5_CFLAGS}" + echo "KRB5_CPPFLAGS: ${KRB5_CPPFLAGS}" + echo "KRB5_LDFLAGS: ${KRB5_LDFLAGS}" + echo "KRB5_LIBS: ${KRB5_LIBS}" + else + if test x"$with_krb5_support" = x"yes"; then + AC_MSG_ERROR(a working krb5 library is needed for KRB5 support) + else + AC_MSG_WARN(a working krb5 library is needed for KRB5 support) + fi + KRB5_CFLAGS="" + KRB5_CPPFLAGS="" + KRB5_LDFLAGS="" + KRB5_LIBS="" + with_krb5_support=no + fi + + CFLAGS=$ac_save_CFLAGS + CPPFLAGS=$ac_save_CPPFLAGS + LDFLAGS=$ac_save_LDFLAGS + LIBS="$ac_save_LIBS" + + # as a nasty hack add the krb5 stuff to the global vars, + # at some point this should not be needed anymore when the build system + # can handle that alone + CFLAGS="$CFLAGS $KRB5_CFLAGS" + CPPFLAGS="$CPPFLAGS $KRB5_CPPFLAGS" + LDFLAGS="$LDFLAGS $KRB5_LDFLAGS" +fi + +SMB_EXT_LIB(KRB5,[${KRB5_LIBS}],[${KRB5_CFLAGS}],[${KRB5_CPPFLAGS}],[${KRB5_LDFLAGS}]) + diff --git a/source4/auth/kerberos/kerberos.mk b/source4/auth/kerberos/kerberos.mk new file mode 100644 index 0000000000..a43e6bb517 --- /dev/null +++ b/source4/auth/kerberos/kerberos.mk @@ -0,0 +1,10 @@ +################################# +# Start SUBSYSTEM KERBEROS +[SUBSYSTEM::KERBEROS] +INIT_OBJ_FILES = auth/kerberos/kerberos.o +ADD_OBJ_FILES = \ + auth/kerberos/clikrb5.o \ + auth/kerberos/kerberos_verify.o \ + auth/kerberos/gssapi_parse.o +# End SUBSYSTEM KERBEROS +################################# diff --git a/source4/auth/kerberos/kerberos_verify.c b/source4/auth/kerberos/kerberos_verify.c new file mode 100644 index 0000000000..3188e603cd --- /dev/null +++ b/source4/auth/kerberos/kerberos_verify.c @@ -0,0 +1,486 @@ +/* + Unix SMB/CIFS implementation. + kerberos utility library + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Remus Koos 2001 + Copyright (C) Luke Howard 2003 + Copyright (C) Guenther Deschner 2003 + Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2003 + Copyright (C) Andrew Bartlett 2004-2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "system/kerberos.h" +#include "auth/kerberos/kerberos.h" +#include "asn_1.h" +#include "lib/ldb/include/ldb.h" +#include "secrets.h" +#include "pstring.h" + +#ifdef HAVE_KRB5 + +#if !defined(HAVE_KRB5_PRINC_COMPONENT) +const krb5_data *krb5_princ_component(krb5_context, krb5_principal, int ); +#endif +static DATA_BLOB unwrap_pac(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data) +{ + DATA_BLOB out; + DATA_BLOB pac_contents = data_blob(NULL, 0); + struct asn1_data data; + int data_type; + if (!auth_data->length) { + return data_blob(NULL, 0); + } + + asn1_load(&data, *auth_data); + asn1_start_tag(&data, ASN1_SEQUENCE(0)); + asn1_start_tag(&data, ASN1_SEQUENCE(0)); + asn1_start_tag(&data, ASN1_CONTEXT(0)); + asn1_read_Integer(&data, &data_type); + asn1_end_tag(&data); + asn1_start_tag(&data, ASN1_CONTEXT(1)); + asn1_read_OctetString(&data, &pac_contents); + asn1_end_tag(&data); + asn1_end_tag(&data); + asn1_end_tag(&data); + asn1_free(&data); + + out = data_blob_talloc(mem_ctx, pac_contents.data, pac_contents.length); + + data_blob_free(&pac_contents); + + return out; +} + +/********************************************************************************** + Try to verify a ticket using the system keytab... the system keytab has kvno -1 entries, so + it's more like what microsoft does... see comment in utils/net_ads.c in the + ads_keytab_add_entry function for details. +***********************************************************************************/ + +static krb5_error_code ads_keytab_verify_ticket(TALLOC_CTX *mem_ctx, krb5_context context, + krb5_auth_context auth_context, + const char *service, + const DATA_BLOB *ticket, krb5_data *p_packet, + krb5_ticket **pp_tkt, + krb5_keyblock *keyblock) +{ + krb5_error_code ret = 0; + krb5_keytab keytab = NULL; + krb5_kt_cursor kt_cursor; + krb5_keytab_entry kt_entry; + char *valid_princ_formats[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL }; + char *entry_princ_s = NULL; + const char *my_name, *my_fqdn; + int i; + int number_matched_principals = 0; + const char *last_error_message; + + /* Generate the list of principal names which we expect + * clients might want to use for authenticating to the file + * service. We allow name$,{host,cifs}/{name,fqdn,name.REALM}. */ + + my_name = lp_netbios_name(); + + my_fqdn = name_to_fqdn(mem_ctx, my_name); + + asprintf(&valid_princ_formats[0], "%s$@%s", my_name, lp_realm()); + asprintf(&valid_princ_formats[1], "host/%s@%s", my_name, lp_realm()); + asprintf(&valid_princ_formats[2], "host/%s@%s", my_fqdn, lp_realm()); + asprintf(&valid_princ_formats[3], "host/%s.%s@%s", my_name, lp_realm(), lp_realm()); + asprintf(&valid_princ_formats[4], "cifs/%s@%s", my_name, lp_realm()); + asprintf(&valid_princ_formats[5], "cifs/%s@%s", my_fqdn, lp_realm()); + asprintf(&valid_princ_formats[6], "cifs/%s.%s@%s", my_name, lp_realm(), lp_realm()); + + ZERO_STRUCT(kt_entry); + ZERO_STRUCT(kt_cursor); + + ret = krb5_kt_default(context, &keytab); + if (ret) { + DEBUG(1, ("ads_keytab_verify_ticket: krb5_kt_default failed (%s)\n", + smb_get_krb5_error_message(context, ret, mem_ctx))); + goto out; + } + + /* Iterate through the keytab. For each key, if the principal + * name case-insensitively matches one of the allowed formats, + * try verifying the ticket using that principal. */ + + ret = krb5_kt_start_seq_get(context, keytab, &kt_cursor); + if (ret) { + last_error_message = smb_get_krb5_error_message(context, ret, mem_ctx); + DEBUG(1, ("ads_keytab_verify_ticket: krb5_kt_start_seq_get failed (%s)\n", + last_error_message)); + goto out; + } + + ret = krb5_kt_start_seq_get(context, keytab, &kt_cursor); + if (ret != KRB5_KT_END && ret != ENOENT ) { + ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; /* Pick an error... */ + while (ret && (krb5_kt_next_entry(context, keytab, &kt_entry, &kt_cursor) == 0)) { + krb5_error_code upn_ret; + upn_ret = krb5_unparse_name(context, kt_entry.principal, &entry_princ_s); + if (upn_ret) { + last_error_message = smb_get_krb5_error_message(context, ret, mem_ctx); + DEBUG(1, ("ads_keytab_verify_ticket: krb5_unparse_name failed (%s)\n", + last_error_message)); + ret = upn_ret; + break; + } + for (i = 0; i < ARRAY_SIZE(valid_princ_formats); i++) { + if (!strequal(entry_princ_s, valid_princ_formats[i])) { + continue; + } + + number_matched_principals++; + p_packet->length = ticket->length; + p_packet->data = (krb5_pointer)ticket->data; + *pp_tkt = NULL; + ret = krb5_rd_req(context, &auth_context, p_packet, kt_entry.principal, keytab, NULL, pp_tkt); + if (ret) { + last_error_message = smb_get_krb5_error_message(context, ret, mem_ctx); + DEBUG(10, ("ads_keytab_verify_ticket: krb5_rd_req(%s) failed: %s\n", + entry_princ_s, last_error_message)); + } else { + DEBUG(3,("ads_keytab_verify_ticket: krb5_rd_req succeeded for principal %s\n", + entry_princ_s)); + break; + } + } + + /* Free the name we parsed. */ + krb5_free_unparsed_name(context, entry_princ_s); + entry_princ_s = NULL; + + /* Free the entry we just read. */ + smb_krb5_kt_free_entry(context, &kt_entry); + ZERO_STRUCT(kt_entry); + } + krb5_kt_end_seq_get(context, keytab, &kt_cursor); + } + + ZERO_STRUCT(kt_cursor); + + out: + + if (ret) { + if (!number_matched_principals) { + DEBUG(3, ("ads_keytab_verify_ticket: no keytab principals matched expected file service name.\n")); + } else { + DEBUG(3, ("ads_keytab_verify_ticket: krb5_rd_req failed for all %d matched keytab principals\n", + number_matched_principals)); + } + DEBUG(3, ("ads_keytab_verify_ticket: last error: %s\n", last_error_message)); + } + + if (entry_princ_s) { + krb5_free_unparsed_name(context, entry_princ_s); + } + + { + krb5_keytab_entry zero_kt_entry; + ZERO_STRUCT(zero_kt_entry); + if (memcmp(&zero_kt_entry, &kt_entry, sizeof(krb5_keytab_entry))) { + smb_krb5_kt_free_entry(context, &kt_entry); + } + } + + { + krb5_kt_cursor zero_csr; + ZERO_STRUCT(zero_csr); + if ((memcmp(&kt_cursor, &zero_csr, sizeof(krb5_kt_cursor)) != 0) && keytab) { + krb5_kt_end_seq_get(context, keytab, &kt_cursor); + } + } + + if (keytab) { + krb5_kt_close(context, keytab); + } + + return ret; +} + +/********************************************************************************** + Try to verify a ticket using the secrets.tdb. +***********************************************************************************/ + +static krb5_error_code ads_secrets_verify_ticket(TALLOC_CTX *mem_ctx, krb5_context context, + krb5_auth_context auth_context, + krb5_principal host_princ, + const DATA_BLOB *ticket, krb5_data *p_packet, + krb5_ticket **pp_tkt, + krb5_keyblock *keyblock) +{ + krb5_error_code ret = 0; + krb5_error_code our_ret; + krb5_data password; + krb5_enctype *enctypes = NULL; + int i; + const struct ldb_val *password_v; + struct ldb_context *ldb; + int ldb_ret; + struct ldb_message **msgs; + const char *base_dn = SECRETS_PRIMARY_DOMAIN_DN; + const char *attrs[] = { + "secret", + NULL + }; + + ZERO_STRUCTP(keyblock); + + /* Local secrets are stored in secrets.ldb */ + ldb = secrets_db_connect(mem_ctx); + if (!ldb) { + return ENOENT; + } + + /* search for the secret record */ + ldb_ret = gendb_search(ldb, + mem_ctx, base_dn, &msgs, attrs, + SECRETS_PRIMARY_REALM_FILTER, + lp_realm()); + if (ldb_ret == 0) { + DEBUG(1, ("Could not find domain join record for %s\n", + lp_realm())); + return ENOENT; + } else if (ldb_ret != 1) { + DEBUG(1, ("Found %d records matching cn=%s under DN %s\n", ldb_ret, + lp_realm(), base_dn)); + return ENOENT; + } + + password_v = ldb_msg_find_ldb_val(msgs[0], "secret"); + + password.data = password_v->data; + password.length = password_v->length; + + /* CIFS doesn't use addresses in tickets. This would break NAT. JRA */ + + if ((ret = get_kerberos_allowed_etypes(context, &enctypes))) { + DEBUG(1,("ads_secrets_verify_ticket: krb5_get_permitted_enctypes failed (%s)\n", + error_message(ret))); + return ret; + } + + p_packet->length = ticket->length; + p_packet->data = (krb5_pointer)ticket->data; + + /* We need to setup a auth context with each possible encoding type in turn. */ + + ret = KRB5_BAD_ENCTYPE; + for (i=0;enctypes[i];i++) { + krb5_keyblock *key = NULL; + + if (!(key = malloc_p(krb5_keyblock))) { + break; + } + + if (create_kerberos_key_from_string(context, host_princ, &password, key, enctypes[i])) { + SAFE_FREE(key); + continue; + } + + krb5_auth_con_setuseruserkey(context, auth_context, key); + + krb5_free_keyblock(context, key); + + our_ret = krb5_rd_req(context, &auth_context, p_packet, + NULL, + NULL, NULL, pp_tkt); + if (!our_ret) { + + DEBUG(10,("ads_secrets_verify_ticket: enc type [%u] decrypted message !\n", + (unsigned int)enctypes[i] )); + ret = our_ret; + break; + } + + DEBUG((our_ret != KRB5_BAD_ENCTYPE) ? 3 : 10, + ("ads_secrets_verify_ticket: enc type [%u] failed to decrypt with error %s\n", + (unsigned int)enctypes[i], smb_get_krb5_error_message(context, our_ret, mem_ctx))); + + if (our_ret != KRB5_BAD_ENCTYPE) { + ret = our_ret; + } + } + + free_kerberos_etypes(context, enctypes); + + return ret; +} + +/********************************************************************************** + Verify an incoming ticket and parse out the principal name and + authorization_data if available. +***********************************************************************************/ + + NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx, + krb5_context context, + krb5_auth_context auth_context, + const char *realm, const char *service, + const DATA_BLOB *ticket, + char **principal, DATA_BLOB *auth_data, + DATA_BLOB *ap_rep, + krb5_keyblock *keyblock) +{ + NTSTATUS sret = NT_STATUS_LOGON_FAILURE; + krb5_data packet; + krb5_ticket *tkt = NULL; + krb5_rcache rcache = NULL; + int ret; + + krb5_principal host_princ = NULL; + char *host_princ_s = NULL; + BOOL got_replay_mutex = False; + + char *malloc_principal; + + ZERO_STRUCT(packet); + ZERO_STRUCTP(auth_data); + ZERO_STRUCTP(ap_rep); + + /* This whole process is far more complex than I would + like. We have to go through all this to allow us to store + the secret internally, instead of using /etc/krb5.keytab */ + + asprintf(&host_princ_s, "%s$", lp_netbios_name()); + strlower_m(host_princ_s); + ret = krb5_parse_name(context, host_princ_s, &host_princ); + if (ret) { + DEBUG(1,("ads_verify_ticket: krb5_parse_name(%s) failed (%s)\n", + host_princ_s, error_message(ret))); + goto out; + } + + + /* Lock a mutex surrounding the replay as there is no locking in the MIT krb5 + * code surrounding the replay cache... */ + + if (!grab_server_mutex("replay cache mutex")) { + DEBUG(1,("ads_verify_ticket: unable to protect replay cache with mutex.\n")); + goto out; + } + + got_replay_mutex = True; + + /* + * JRA. We must set the rcache here. This will prevent replay attacks. + */ + + ret = krb5_get_server_rcache(context, krb5_princ_component(context, host_princ, 0), &rcache); + if (ret) { + DEBUG(1,("ads_verify_ticket: krb5_get_server_rcache failed (%s)\n", error_message(ret))); + goto out; + } + + ret = krb5_auth_con_setrcache(context, auth_context, rcache); + if (ret) { + DEBUG(1,("ads_verify_ticket: krb5_auth_con_setrcache failed (%s)\n", error_message(ret))); + goto out; + } + + ret = ads_keytab_verify_ticket(mem_ctx, context, auth_context, + service, ticket, &packet, &tkt, keyblock); + if (ret) { + DEBUG(10, ("ads_secrets_verify_ticket: using host principal: [%s]\n", host_princ_s)); + ret = ads_secrets_verify_ticket(mem_ctx, context, auth_context, + host_princ, ticket, + &packet, &tkt, keyblock); + } + + release_server_mutex(); + got_replay_mutex = False; + + if (ret) { + DEBUG(3,("ads_verify_ticket: krb5_rd_req with auth failed (%s)\n", + smb_get_krb5_error_message(context, ret, mem_ctx))); + goto out; + } + + ret = krb5_mk_rep(context, auth_context, &packet); + if (ret) { + DEBUG(3,("ads_verify_ticket: Failed to generate mutual authentication reply (%s)\n", + smb_get_krb5_error_message(context, ret, mem_ctx))); + goto out; + } + + *ap_rep = data_blob_talloc(mem_ctx, packet.data, packet.length); + SAFE_FREE(packet.data); + packet.length = 0; + +#if 0 + file_save("/tmp/ticket.dat", ticket->data, ticket->length); +#endif + + *auth_data = get_auth_data_from_tkt(mem_ctx, tkt); + + *auth_data = unwrap_pac(mem_ctx, auth_data); + +#if 0 + if (tkt->enc_part2) { + file_save("/tmp/authdata.dat", + tkt->enc_part2->authorization_data[0]->contents, + tkt->enc_part2->authorization_data[0]->length); + } +#endif + + if ((ret = krb5_unparse_name(context, get_principal_from_tkt(tkt), + &malloc_principal))) { + DEBUG(3,("ads_verify_ticket: krb5_unparse_name failed (%s)\n", + smb_get_krb5_error_message(context, ret, mem_ctx))); + sret = NT_STATUS_LOGON_FAILURE; + goto out; + } + + *principal = talloc_strdup(mem_ctx, malloc_principal); + SAFE_FREE(malloc_principal); + if (!principal) { + DEBUG(3,("ads_verify_ticket: talloc_strdup() failed\n")); + sret = NT_STATUS_NO_MEMORY; + goto out; + } + + sret = NT_STATUS_OK; + + out: + + if (got_replay_mutex) { + release_server_mutex(); + } + + if (!NT_STATUS_IS_OK(sret)) { + data_blob_free(auth_data); + } + + if (!NT_STATUS_IS_OK(sret)) { + data_blob_free(ap_rep); + } + + if (host_princ) { + krb5_free_principal(context, host_princ); + } + + if (tkt != NULL) { + krb5_free_ticket(context, tkt); + } + + SAFE_FREE(host_princ_s); + + return sret; +} + +#endif /* HAVE_KRB5 */ diff --git a/source4/build/smb_build/main.pm b/source4/build/smb_build/main.pm index 381c548ac1..1dee5be910 100644 --- a/source4/build/smb_build/main.pm +++ b/source4/build/smb_build/main.pm @@ -44,7 +44,8 @@ sub smb_build_main($) "ldap_server/config.mk", "winbind/config.mk", "nbt_server/config.mk", - "libcli/auth/gensec.mk", + "auth/gensec/gensec.mk", + "auth/kerberos/kerberos.mk", "libcli/auth/config.mk", "libcli/ldap/config.mk", "libcli/config.mk", diff --git a/source4/configure.in b/source4/configure.in index 366f038030..4b49591589 100644 --- a/source4/configure.in +++ b/source4/configure.in @@ -20,7 +20,8 @@ SMB_INCLUDE_M4(lib/ldb/config.m4) SMB_INCLUDE_M4(lib/events/config.m4) SMB_INCLUDE_M4(lib/cmdline/config.m4) SMB_INCLUDE_M4(param/config.m4) -SMB_INCLUDE_M4(libcli/auth/gensec.m4) +SMB_INCLUDE_M4(auth/kerberos/kerberos.m4) +SMB_INCLUDE_M4(auth/gensec/gensec.m4) SMB_INCLUDE_M4(libcli/config.m4) SMB_INCLUDE_M4(librpc/config.m4) SMB_INCLUDE_M4(smbd/process_model.m4) diff --git a/source4/libads/config.m4 b/source4/libads/config.m4 index 8c27dba49e..a9e3fb5678 100644 --- a/source4/libads/config.m4 +++ b/source4/libads/config.m4 @@ -86,494 +86,4 @@ if test x"$with_ldap_support" != x"no"; then LIBS=$ac_save_LIBS fi -################################################# -# KRB5 support -KRB5_CFLAGS="" -KRB5_CPPFLAGS="" -KRB5_LDFLAGS="" -KRB5_LIBS="" -with_krb5_support=auto -krb5_withval=auto -AC_MSG_CHECKING([for KRB5 support]) - -# Do no harm to the values of CFLAGS and LIBS while testing for -# Kerberos support. -AC_ARG_WITH(krb5, -[ --with-krb5=base-dir Locate Kerberos 5 support (default=auto)], - [ case "$withval" in - no) - with_krb5_support=no - AC_MSG_RESULT(no) - krb5_withval=no - ;; - yes) - with_krb5_support=yes - AC_MSG_RESULT(yes) - krb5_withval=yes - ;; - auto) - with_krb5_support=auto - AC_MSG_RESULT(auto) - krb5_withval=auto - ;; - *) - with_krb5_support=yes - AC_MSG_RESULT(yes) - krb5_withval=$withval - KRB5CONFIG="$krb5_withval/bin/krb5-config" - ;; - esac ], - AC_MSG_RESULT($with_krb5_support) -) - -if test x$with_krb5_support != x"no"; then - FOUND_KRB5=no - FOUND_KRB5_VIA_CONFIG=no - - ################################################# - # check for krb5-config from recent MIT and Heimdal kerberos 5 - AC_MSG_CHECKING(for working specified location for krb5-config) - if test x$KRB5CONFIG != "x"; then - if test -x "$KRB5CONFIG"; then - ac_save_CFLAGS=$CFLAGS - CFLAGS="";export CFLAGS - ac_save_LDFLAGS=$LDFLAGS - LDFLAGS="";export LDFLAGS - KRB5_LIBS="`$KRB5CONFIG --libs gssapi`" - KRB5_CFLAGS="`$KRB5CONFIG --cflags | sed s/@INCLUDE_des@//`" - KRB5_CPPFLAGS="`$KRB5CONFIG --cflags | sed s/@INCLUDE_des@//`" - CFLAGS=$ac_save_CFLAGS;export CFLAGS - LDFLAGS=$ac_save_LDFLAGS;export LDFLAGS - FOUND_KRB5=yes - FOUND_KRB5_VIA_CONFIG=yes - AC_MSG_RESULT(yes. Found $KRB5CONFIG) - else - AC_MSG_RESULT(no. Fallback to specified directory) - fi - else - AC_MSG_RESULT(no. Fallback to finding krb5-config in path) - ################################################# - # check for krb5-config from recent MIT and Heimdal kerberos 5 - AC_PATH_PROG(KRB5CONFIG, krb5-config) - AC_MSG_CHECKING(for working krb5-config in path) - if test -x "$KRB5CONFIG"; then - ac_save_CFLAGS=$CFLAGS - CFLAGS="";export CFLAGS - ac_save_LDFLAGS=$LDFLAGS - LDFLAGS="";export LDFLAGS - KRB5_LIBS="`$KRB5CONFIG --libs gssapi`" - KRB5_CFLAGS="`$KRB5CONFIG --cflags | sed s/@INCLUDE_des@//`" - KRB5_CPPFLAGS="`$KRB5CONFIG --cflags | sed s/@INCLUDE_des@//`" - CFLAGS=$ac_save_CFLAGS;export CFLAGS - LDFLAGS=$ac_save_LDFLAGS;export LDFLAGS - FOUND_KRB5=yes - FOUND_KRB5_VIA_CONFIG=yes - AC_MSG_RESULT(yes. Found $KRB5CONFIG) - else - AC_MSG_RESULT(no. Fallback to previous krb5 detection strategy) - fi - fi - - if test x$FOUND_KRB5 != x"yes"; then - ################################################# - # check for location of Kerberos 5 install - AC_MSG_CHECKING(for kerberos 5 install path) - case "$krb5_withval" in - no) - AC_MSG_RESULT(no krb5-path given) - ;; - yes) - AC_MSG_RESULT(/usr) - FOUND_KRB5=yes - ;; - *) - AC_MSG_RESULT($krb5_withval) - KRB5_CFLAGS="-I$krb5_withval/include" - KRB5_CPPFLAGS="-I$krb5_withval/include" - KRB5_LDFLAGS="-L$krb5_withval/lib" - FOUND_KRB5=yes - ;; - esac - fi - - if test x$FOUND_KRB5 != x"yes"; then - ################################################# - # see if this box has the SuSE location for the heimdal krb implementation - AC_MSG_CHECKING(for /usr/include/heimdal) - if test -d /usr/include/heimdal; then - if test -f /usr/lib/heimdal/lib/libkrb5.a; then - KRB5_CFLAGS="-I/usr/include/heimdal" - KRB5_CPPFLAGS="-I/usr/include/heimdal" - KRB5_LDFLAGS="-L/usr/lib/heimdal/lib" - AC_MSG_RESULT(yes) - else - KRB5_CFLAGS="-I/usr/include/heimdal" - KRB5_CPPFLAGS="-I/usr/include/heimdal" - AC_MSG_RESULT(yes) - fi - else - AC_MSG_RESULT(no) - fi - fi - - if test x$FOUND_KRB5 != x"yes"; then - ################################################# - # see if this box has the RedHat location for kerberos - AC_MSG_CHECKING(for /usr/kerberos) - if test -d /usr/kerberos -a -f /usr/kerberos/lib/libkrb5.a; then - KRB5_LDFLAGS="-L/usr/kerberos/lib" - KRB5_CFLAGS="-I/usr/kerberos/include" - KRB5_CPPFLAGS="-I/usr/kerberos/include" - AC_MSG_RESULT(yes) - else - AC_MSG_RESULT(no) - fi - fi - - ac_save_CFLAGS=$CFLAGS - ac_save_CPPFLAGS=$CPPFLAGS - ac_save_LDFLAGS=$LDFLAGS - - #MIT needs this, to let us see 'internal' parts of the headers we use - KRB5_CFLAGS="${KRB5_CFLAGS} -DKRB5_PRIVATE -DKRB5_DEPRECATED" - - #Heimdal needs this - #TODO: we need to parse KRB5_LIBS for -L path - # and set -Wl,-rpath -Wl,path - - CFLAGS="$CFLAGS $KRB5_CFLAGS" - CPPFLAGS="$CPPFLAGS $KRB5_CPPFLAGS" - LDFLAGS="$LDFLAGS $KRB5_LDFLAGS" - - KRB5_LIBS="$KRB5_LDFLAGS $KRB5_LIBS" - - # now check for krb5.h. Some systems have the libraries without the headers! - # note that this check is done here to allow for different kerberos - # include paths - AC_CHECK_HEADERS(krb5.h) - - if test x"$ac_cv_header_krb5_h" = x"no"; then - # Give a warning if KRB5 support was not explicitly requested, - # i.e with_krb5_support = auto, otherwise die with an error. - if test x"$with_krb5_support" = x"yes"; then - AC_MSG_ERROR([KRB5 cannot be supported without krb5.h]) - else - AC_MSG_WARN([KRB5 cannot be supported without krb5.h]) - fi - # Turn off AD support and restore CFLAGS and LIBS variables - with_krb5_support="no" - fi - - CFLAGS=$ac_save_CFLAGS - CPPFLAGS=$ac_save_CPPFLAGS - LDFLAGS=$ac_save_LDFLAGS -fi - -# Now we have determined whether we really want KRB5 support - -if test x"$with_krb5_support" != x"no"; then - ac_save_CFLAGS=$CFLAGS - ac_save_CPPFLAGS=$CPPFLAGS - ac_save_LDFLAGS=$LDFLAGS - ac_save_LIBS=$LIBS - - CFLAGS="$CFLAGS $KRB5_CFLAGS" - CPPFLAGS="$CPPFLAGS $KRB5_CPPFLAGS" - LDFLAGS="$LDFLAGS $KRB5_LDFLAGS" - - # now check for gssapi headers. This is also done here to allow for - # different kerberos include paths - AC_CHECK_HEADERS(gssapi.h gssapi/gssapi_generic.h gssapi/gssapi.h com_err.h) - - ################################################################## - # we might need the k5crypto and com_err libraries on some systems - AC_CHECK_LIB_EXT(com_err, KRB5_LIBS, _et_list) - AC_CHECK_LIB_EXT(k5crypto, KRB5_LIBS, krb5_encrypt_data) - - # Heimdal checks. - # But only if we didn't have a krb5-config to tell us this already - if test x"$FOUND_KRB5_VIA_CONFIG" != x"yes"; then - AC_CHECK_LIB_EXT(crypto, KRB5_LIBS, des_set_key) - AC_CHECK_LIB_EXT(asn1, KRB5_LIBS, copy_Authenticator) - AC_CHECK_LIB_EXT(roken, KRB5_LIBS, roken_getaddrinfo_hostspec) - fi - - # Heimdal checks. On static Heimdal gssapi must be linked before krb5. - AC_CHECK_LIB_EXT(gssapi, KRB5_LIBS, gss_display_status,[],[], - AC_DEFINE(HAVE_GSSAPI,1,[Whether GSSAPI is available])) - - ######################################################## - # now see if we can find the krb5 libs in standard paths - # or as specified above - AC_CHECK_LIB_EXT(krb5, KRB5_LIBS, krb5_mk_req_extended) - AC_CHECK_LIB_EXT(krb5, KRB5_LIBS, krb5_kt_compare) - - ######################################################## - # now see if we can find the gssapi libs in standard paths - if test x"$ac_cv_lib_ext_gssapi_gss_display_status" != x"yes"; then - AC_CHECK_LIB_EXT(gssapi_krb5, KRB5_LIBS,gss_display_status,[],[], - AC_DEFINE(HAVE_GSSAPI,1,[Whether GSSAPI is available])) - fi - - AC_CHECK_FUNC_EXT(krb5_set_real_time, $KRB5_LIBS) - AC_CHECK_FUNC_EXT(krb5_set_default_in_tkt_etypes, $KRB5_LIBS) - AC_CHECK_FUNC_EXT(krb5_set_default_tgs_ktypes, $KRB5_LIBS) - AC_CHECK_FUNC_EXT(krb5_principal2salt, $KRB5_LIBS) - AC_CHECK_FUNC_EXT(krb5_use_enctype, $KRB5_LIBS) - AC_CHECK_FUNC_EXT(krb5_string_to_key, $KRB5_LIBS) - AC_CHECK_FUNC_EXT(krb5_get_pw_salt, $KRB5_LIBS) - AC_CHECK_FUNC_EXT(krb5_string_to_key_salt, $KRB5_LIBS) - AC_CHECK_FUNC_EXT(krb5_auth_con_setkey, $KRB5_LIBS) - AC_CHECK_FUNC_EXT(krb5_auth_con_setuseruserkey, $KRB5_LIBS) - AC_CHECK_FUNC_EXT(krb5_locate_kdc, $KRB5_LIBS) - AC_CHECK_FUNC_EXT(krb5_get_permitted_enctypes, $KRB5_LIBS) - AC_CHECK_FUNC_EXT(krb5_get_default_in_tkt_etypes, $KRB5_LIBS) - AC_CHECK_FUNC_EXT(krb5_free_ktypes, $KRB5_LIBS) - AC_CHECK_FUNC_EXT(krb5_free_data_contents, $KRB5_LIBS) - AC_CHECK_FUNC_EXT(krb5_principal_get_comp_string, $KRB5_LIBS) - AC_CHECK_FUNC_EXT(krb5_free_unparsed_name, $KRB5_LIBS) - AC_CHECK_FUNC_EXT(krb5_free_keytab_entry_contents, $KRB5_LIBS) - AC_CHECK_FUNC_EXT(krb5_kt_free_entry, $KRB5_LIBS) - AC_CHECK_FUNC_EXT(krb5_krbhst_get_addrinfo, $KRB5_LIBS) - AC_CHECK_FUNC_EXT(krb5_verify_checksum, $KRB5_LIBS) - AC_CHECK_FUNC_EXT(krb5_c_verify_checksum, $KRB5_LIBS) - AC_CHECK_FUNC_EXT(krb5_ticket_get_authorization_data_type, $KRB5_LIBS) - AC_CHECK_FUNC_EXT(krb5_krbhst_get_addrinfo, $KRB5_LIBS) - AC_CHECK_FUNC_EXT(krb5_c_enctype_compare, $KRB5_LIBS) - AC_CHECK_FUNC_EXT(krb5_enctypes_compatible_keys, $KRB5_LIBS) - AC_CHECK_FUNC_EXT(krb5_get_error_string, $KRB5_LIBS) - AC_CHECK_FUNC_EXT(krb5_free_error_string, $KRB5_LIBS) - - LIBS="$LIBS $KRB5_LIBS" - - AC_CACHE_CHECK([for krb5_encrypt_block type], - samba_cv_HAVE_KRB5_ENCRYPT_BLOCK,[ - AC_TRY_COMPILE([#include ], - [krb5_encrypt_block block;], - samba_cv_HAVE_KRB5_ENCRYPT_BLOCK=yes, - samba_cv_HAVE_KRB5_ENCRYPT_BLOCK=no)]) - - if test x"$samba_cv_HAVE_KRB5_ENCRYPT_BLOCK" = x"yes"; then - AC_DEFINE(HAVE_KRB5_ENCRYPT_BLOCK,1, - [Whether the type krb5_encrypt_block exists]) - fi - - AC_CACHE_CHECK([for addrtype in krb5_address], - samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS,[ - AC_TRY_COMPILE([#include ], - [krb5_address kaddr; kaddr.addrtype = ADDRTYPE_INET;], - samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS=yes, - samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS=no)]) - if test x"$samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS" = x"yes"; then - AC_DEFINE(HAVE_ADDRTYPE_IN_KRB5_ADDRESS,1, - [Whether the krb5_address struct has a addrtype property]) - fi - - AC_CACHE_CHECK([for addr_type in krb5_address], - samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS,[ - AC_TRY_COMPILE([#include ], - [krb5_address kaddr; kaddr.addr_type = KRB5_ADDRESS_INET;], - samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS=yes, - samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS=no)]) - if test x"$samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS" = x"yes"; then - AC_DEFINE(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS,1, - [Whether the krb5_address struct has a addr_type property]) - fi - - AC_CACHE_CHECK([for enc_part2 in krb5_ticket], - samba_cv_HAVE_KRB5_TKT_ENC_PART2,[ - AC_TRY_COMPILE([#include ], - [krb5_ticket tkt; tkt.enc_part2->authorization_data[0]->contents = NULL;], - samba_cv_HAVE_KRB5_TKT_ENC_PART2=yes, - samba_cv_HAVE_KRB5_TKT_ENC_PART2=no)]) - if test x"$samba_cv_HAVE_KRB5_TKT_ENC_PART2" = x"yes"; then - AC_DEFINE(HAVE_KRB5_TKT_ENC_PART2,1, - [Whether the krb5_ticket struct has a enc_part2 property]) - fi - - AC_CACHE_CHECK([for keyblock in krb5_creds], - samba_cv_HAVE_KRB5_KEYBLOCK_IN_CREDS,[ - AC_TRY_COMPILE([#include ], - [krb5_creds creds; krb5_keyblock kb; creds.keyblock = kb;], - samba_cv_HAVE_KRB5_KEYBLOCK_IN_CREDS=yes, - samba_cv_HAVE_KRB5_KEYBLOCK_IN_CREDS=no)]) - - if test x"$samba_cv_HAVE_KRB5_KEYBLOCK_IN_CREDS" = x"yes"; then - AC_DEFINE(HAVE_KRB5_KEYBLOCK_IN_CREDS,1, - [Whether the krb5_creds struct has a keyblock property]) - fi - - AC_CACHE_CHECK([for session in krb5_creds], - samba_cv_HAVE_KRB5_SESSION_IN_CREDS,[ - AC_TRY_COMPILE([#include ], - [krb5_creds creds; krb5_keyblock kb; creds.session = kb;], - samba_cv_HAVE_KRB5_SESSION_IN_CREDS=yes, - samba_cv_HAVE_KRB5_SESSION_IN_CREDS=no)]) - - if test x"$samba_cv_HAVE_KRB5_SESSION_IN_CREDS" = x"yes"; then - AC_DEFINE(HAVE_KRB5_SESSION_IN_CREDS,1, - [Whether the krb5_creds struct has a session property]) - fi - - AC_CACHE_CHECK([for keyvalue in krb5_keyblock], - samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE,[ - AC_TRY_COMPILE([#include ], - [krb5_keyblock key; key.keyvalue.data = NULL;], - samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE=yes, - samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE=no)]) - if test x"$samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE" = x"yes"; then - AC_DEFINE(HAVE_KRB5_KEYBLOCK_KEYVALUE,1, - [Whether the krb5_keyblock struct has a keyvalue property]) - fi - - AC_CACHE_CHECK([for ENCTYPE_ARCFOUR_HMAC_MD5], - samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5,[ - AC_TRY_COMPILE([#include ], - [krb5_enctype enctype; enctype = ENCTYPE_ARCFOUR_HMAC_MD5;], - samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5=yes, - samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5=no)]) - AC_CACHE_CHECK([for KEYTYPE_ARCFOUR_56], - samba_cv_HAVE_KEYTYPE_ARCFOUR_56,[ - AC_TRY_COMPILE([#include ], - [krb5_keytype keytype; keytype = KEYTYPE_ARCFOUR_56;], - samba_cv_HAVE_KEYTYPE_ARCFOUR_56=yes, - samba_cv_HAVE_KEYTYPE_ARCFOUR_56=no)]) - # Heimdals with KEYTYPE_ARCFOUR but not KEYTYPE_ARCFOUR_56 are broken - # w.r.t. arcfour and windows, so we must not enable it here - if test x"$samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5" = x"yes" -a\ - x"$samba_cv_HAVE_KEYTYPE_ARCFOUR_56" = x"yes"; then - AC_DEFINE(HAVE_ENCTYPE_ARCFOUR_HMAC_MD5,1, - [Whether the ENCTYPE_ARCFOUR_HMAC_MD5 key type is available]) - fi - - AC_CACHE_CHECK([for AP_OPTS_USE_SUBKEY], - samba_cv_HAVE_AP_OPTS_USE_SUBKEY,[ - AC_TRY_COMPILE([#include ], - [krb5_flags ap_options; ap_options = AP_OPTS_USE_SUBKEY;], - samba_cv_HAVE_AP_OPTS_USE_SUBKEY=yes, - samba_cv_HAVE_AP_OPTS_USE_SUBKEY=no)]) - if test x"$samba_cv_HAVE_AP_OPTS_USE_SUBKEY" = x"yes"; then - AC_DEFINE(HAVE_AP_OPTS_USE_SUBKEY,1, - [Whether the AP_OPTS_USE_SUBKEY ap option is available]) - fi - - AC_CACHE_CHECK([for KV5M_KEYTAB], - samba_cv_HAVE_KV5M_KEYTAB,[ - AC_TRY_COMPILE([#include ], - [krb5_keytab_entry entry; entry.magic = KV5M_KEYTAB;], - samba_cv_HAVE_KV5M_KEYTAB=yes, - samba_cv_HAVE_KV5M_KEYTAB=no)]) - if test x"$samba_cv_HAVE_KV5M_KEYTAB" = x"yes"; then - AC_DEFINE(HAVE_KV5M_KEYTAB,1, - [Whether the KV5M_KEYTAB option is available]) - fi - - AC_CACHE_CHECK([for the krb5_princ_component macro], - samba_cv_HAVE_KRB5_PRINC_COMPONENT,[ - AC_TRY_LINK([#include ], - [const krb5_data *pkdata; krb5_context context; krb5_principal principal; - pkdata = krb5_princ_component(context, principal, 0);], - samba_cv_HAVE_KRB5_PRINC_COMPONENT=yes, - samba_cv_HAVE_KRB5_PRINC_COMPONENT=no)]) - if test x"$samba_cv_HAVE_KRB5_PRINC_COMPONENT" = x"yes"; then - AC_DEFINE(HAVE_KRB5_PRINC_COMPONENT,1, - [Whether krb5_princ_component is available]) - fi - - AC_CACHE_CHECK([for key in krb5_keytab_entry], - samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEY,[ - AC_TRY_COMPILE([#include ], - [krb5_keytab_entry entry; krb5_keyblock e; entry.key = e;], - samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEY=yes, - samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEY=no)]) - if test x"$samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEY" = x"yes"; then - AC_DEFINE(HAVE_KRB5_KEYTAB_ENTRY_KEY,1, - [Whether krb5_keytab_entry has key member]) - fi - - AC_CACHE_CHECK([for keyblock in krb5_keytab_entry], - samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK,[ - AC_TRY_COMPILE([#include ], - [krb5_keytab_entry entry; entry.keyblock.keytype = 0;], - samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK=yes, - samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK=no)]) - if test x"$samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK" = x"yes"; then - AC_DEFINE(HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK,1, - [Whether krb5_keytab_entry has keyblock member]) - fi - - AC_CACHE_CHECK([for WRFILE: keytab support], - samba_cv_HAVE_WRFILE_KEYTAB,[ - AC_TRY_RUN([ - #include - main() - { - krb5_context context; - krb5_keytab keytab; - krb5_init_context(&context); - return krb5_kt_resolve(context, "WRFILE:api", &keytab); - }], - samba_cv_HAVE_WRFILE_KEYTAB=yes, - samba_cv_HAVE_WRFILE_KEYTAB=no)]) - if test x"$samba_cv_HAVE_WRFILE_KEYTAB" = x"yes"; then - AC_DEFINE(HAVE_WRFILE_KEYTAB,1, - [Whether the WRFILE:-keytab is supported]) - fi - - AC_CACHE_CHECK([for krb5_princ_realm returns krb5_realm or krb5_data], - samba_cv_KRB5_PRINC_REALM_RETURNS_REALM,[ - AC_TRY_COMPILE([#include ], - [krb5_context context;krb5_principal principal;krb5_realm realm; - realm = *krb5_princ_realm(context, principal);], - samba_cv_KRB5_PRINC_REALM_RETURNS_REALM=yes, - samba_cv_KRB5_PRINC_REALM_RETURNS_REALM=no)]) - if test x"$samba_cv_KRB5_PRINC_REALM_RETURNS_REALM" = x"yes"; then - AC_DEFINE(KRB5_PRINC_REALM_RETURNS_REALM,1, - [Whether krb5_princ_realm returns krb5_realm or krb5_data]) - fi - - # TODO: check all gssapi headers for this - AC_CACHE_CHECK([for GSS_C_DCE_STYLE in gssapi.h], - samba_cv_GSS_C_DCE_STYLE,[ - AC_TRY_COMPILE([#include ], - [int flags = GSS_C_DCE_STYLE;], - samba_cv_GSS_C_DCE_STYLE=yes, - samba_cv_GSS_C_DCE_STYLE=no)]) - - if test x"$ac_cv_lib_ext_krb5_krb5_mk_req_extended" = x"yes"; then - AC_DEFINE(HAVE_KRB5,1,[Whether to have KRB5 support]) - AC_MSG_CHECKING(whether KRB5 support is used) - SMB_EXT_LIB_ENABLE(KRB5,YES) - AC_MSG_RESULT(yes) - echo "KRB5_CFLAGS: ${KRB5_CFLAGS}" - echo "KRB5_CPPFLAGS: ${KRB5_CPPFLAGS}" - echo "KRB5_LDFLAGS: ${KRB5_LDFLAGS}" - echo "KRB5_LIBS: ${KRB5_LIBS}" - else - if test x"$with_krb5_support" = x"yes"; then - AC_MSG_ERROR(a working krb5 library is needed for KRB5 support) - else - AC_MSG_WARN(a working krb5 library is needed for KRB5 support) - fi - KRB5_CFLAGS="" - KRB5_CPPFLAGS="" - KRB5_LDFLAGS="" - KRB5_LIBS="" - with_krb5_support=no - fi - - CFLAGS=$ac_save_CFLAGS - CPPFLAGS=$ac_save_CPPFLAGS - LDFLAGS=$ac_save_LDFLAGS - LIBS="$ac_save_LIBS" - - # as a nasty hack add the krb5 stuff to the global vars, - # at some point this should not be needed anymore when the build system - # can handle that alone - CFLAGS="$CFLAGS $KRB5_CFLAGS" - CPPFLAGS="$CPPFLAGS $KRB5_CPPFLAGS" - LDFLAGS="$LDFLAGS $KRB5_LDFLAGS" -fi - SMB_EXT_LIB(LDAP,[${LDAP_LIBS}],[${LDAP_CFLAGS}],[${LDAP_CPPFLAGS}],[${LDAP_LDFLAGS}]) -SMB_EXT_LIB(KRB5,[${KRB5_LIBS}],[${KRB5_CFLAGS}],[${KRB5_CPPFLAGS}],[${KRB5_LDFLAGS}]) diff --git a/source4/libcli/auth/clikrb5.c b/source4/libcli/auth/clikrb5.c deleted file mode 100644 index b7bd710304..0000000000 --- a/source4/libcli/auth/clikrb5.c +++ /dev/null @@ -1,478 +0,0 @@ -/* - Unix SMB/CIFS implementation. - simple kerberos5 routines for active directory - Copyright (C) Andrew Tridgell 2001 - Copyright (C) Luke Howard 2002-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" -#include "system/network.h" -#include "system/kerberos.h" -#include "system/time.h" -#include "libcli/auth/kerberos.h" - -#ifdef HAVE_KRB5 - -#ifndef HAVE_KRB5_SET_REAL_TIME -/* - * This function is not in the Heimdal mainline. - */ - krb5_error_code krb5_set_real_time(krb5_context context, int32_t seconds, int32_t microseconds) -{ - krb5_error_code ret; - int32_t sec, usec; - - ret = krb5_us_timeofday(context, &sec, &usec); - if (ret) - return ret; - - context->kdc_sec_offset = seconds - sec; - context->kdc_usec_offset = microseconds - usec; - - return 0; -} -#endif - -#if defined(HAVE_KRB5_SET_DEFAULT_IN_TKT_ETYPES) && !defined(HAVE_KRB5_SET_DEFAULT_TGS_KTYPES) - krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc) -{ - return krb5_set_default_in_tkt_etypes(ctx, enc); -} -#endif - -#if defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) -/* HEIMDAL */ - void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr) -{ - pkaddr->addr_type = KRB5_ADDRESS_INET; - pkaddr->address.length = sizeof(((struct sockaddr_in *)paddr)->sin_addr); - pkaddr->address.data = (char *)&(((struct sockaddr_in *)paddr)->sin_addr); -} -#elif defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) -/* MIT */ - void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr) -{ - pkaddr->addrtype = ADDRTYPE_INET; - pkaddr->length = sizeof(((struct sockaddr_in *)paddr)->sin_addr); - pkaddr->contents = (krb5_octet *)&(((struct sockaddr_in *)paddr)->sin_addr); -} -#else -#error UNKNOWN_ADDRTYPE -#endif - -#if defined(HAVE_KRB5_PRINCIPAL2SALT) && defined(HAVE_KRB5_USE_ENCTYPE) && defined(HAVE_KRB5_STRING_TO_KEY) && defined(HAVE_KRB5_ENCRYPT_BLOCK) - int create_kerberos_key_from_string_direct(krb5_context context, - krb5_principal host_princ, - krb5_data *password, - krb5_keyblock *key, - krb5_enctype enctype) -{ - int ret; - krb5_data salt; - krb5_encrypt_block eblock; - - ret = krb5_principal2salt(context, host_princ, &salt); - if (ret) { - DEBUG(1,("krb5_principal2salt failed (%s)\n", error_message(ret))); - return ret; - } - krb5_use_enctype(context, &eblock, enctype); - ret = krb5_string_to_key(context, &eblock, key, password, &salt); - SAFE_FREE(salt.data); - return ret; -} -#elif defined(HAVE_KRB5_GET_PW_SALT) && defined(HAVE_KRB5_STRING_TO_KEY_SALT) - int create_kerberos_key_from_string_direct(krb5_context context, - krb5_principal host_princ, - krb5_data *password, - krb5_keyblock *key, - krb5_enctype enctype) -{ - int ret; - krb5_salt salt; - - ret = krb5_get_pw_salt(context, host_princ, &salt); - if (ret) { - DEBUG(1,("krb5_get_pw_salt failed (%s)\n", error_message(ret))); - return ret; - } - return krb5_string_to_key_salt(context, enctype, password->data, - salt, key); -} -#else -#error UNKNOWN_CREATE_KEY_FUNCTIONS -#endif - - int create_kerberos_key_from_string(krb5_context context, - krb5_principal host_princ, - krb5_data *password, - krb5_keyblock *key, - krb5_enctype enctype) -{ - krb5_principal salt_princ = NULL; - int ret; - /* - * Check if we've determined that the KDC is salting keys for this - * principal/enctype in a non-obvious way. If it is, try to match - * its behavior. - */ - salt_princ = kerberos_fetch_salt_princ_for_host_princ(context, host_princ, enctype); - ret = create_kerberos_key_from_string_direct(context, salt_princ ? salt_princ : host_princ, password, key, enctype); - if (salt_princ) { - krb5_free_principal(context, salt_princ); - } - return ret; -} - -#if defined(HAVE_KRB5_GET_PERMITTED_ENCTYPES) - krb5_error_code get_kerberos_allowed_etypes(krb5_context context, - krb5_enctype **enctypes) -{ - return krb5_get_permitted_enctypes(context, enctypes); -} -#elif defined(HAVE_KRB5_GET_DEFAULT_IN_TKT_ETYPES) - krb5_error_code get_kerberos_allowed_etypes(krb5_context context, - krb5_enctype **enctypes) -{ - return krb5_get_default_in_tkt_etypes(context, enctypes); -} -#else -#error UNKNOWN_GET_ENCTYPES_FUNCTIONS -#endif - - void free_kerberos_etypes(krb5_context context, - krb5_enctype *enctypes) -{ -#if defined(HAVE_KRB5_FREE_KTYPES) - krb5_free_ktypes(context, enctypes); - return; -#else - SAFE_FREE(enctypes); - return; -#endif -} - -#if defined(HAVE_KRB5_AUTH_CON_SETKEY) && !defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY) - krb5_error_code krb5_auth_con_setuseruserkey(krb5_context context, - krb5_auth_context auth_context, - krb5_keyblock *keyblock) -{ - return krb5_auth_con_setkey(context, auth_context, keyblock); -} -#endif - - DATA_BLOB get_auth_data_from_tkt(TALLOC_CTX *mem_ctx, - krb5_ticket *tkt) -{ - DATA_BLOB auth_data = data_blob(NULL, 0); -#if defined(HAVE_KRB5_TKT_ENC_PART2) - if (tkt && tkt->enc_part2 - && tkt->enc_part2->authorization_data - && tkt->enc_part2->authorization_data[0] - && tkt->enc_part2->authorization_data[0]->length) - auth_data = data_blob(tkt->enc_part2->authorization_data[0]->contents, - tkt->enc_part2->authorization_data[0]->length); -#else - if (tkt && tkt->ticket.authorization_data && tkt->ticket.authorization_data->len) - auth_data = data_blob(tkt->ticket.authorization_data->val->ad_data.data, - tkt->ticket.authorization_data->val->ad_data.length); -#endif - return auth_data; -} - - krb5_const_principal get_principal_from_tkt(krb5_ticket *tkt) -{ -#if defined(HAVE_KRB5_TKT_ENC_PART2) - return tkt->enc_part2->client; -#else - return tkt->client; -#endif -} - -#if !defined(HAVE_KRB5_LOCATE_KDC) - krb5_error_code krb5_locate_kdc(krb5_context ctx, const krb5_data *realm, struct sockaddr **addr_pp, int *naddrs, int get_masters) -{ - krb5_krbhst_handle hnd; - krb5_krbhst_info *hinfo; - krb5_error_code rc; - int num_kdcs, i; - struct sockaddr *sa; - struct addrinfo *ai; - - *addr_pp = NULL; - *naddrs = 0; - - rc = krb5_krbhst_init(ctx, realm->data, KRB5_KRBHST_KDC, &hnd); - if (rc) { - DEBUG(0, ("krb5_locate_kdc: krb5_krbhst_init failed (%s)\n", error_message(rc))); - return rc; - } - - for ( num_kdcs = 0; (rc = krb5_krbhst_next(ctx, hnd, &hinfo) == 0); num_kdcs++) - ; - - krb5_krbhst_reset(ctx, hnd); - - if (!num_kdcs) { - DEBUG(0, ("krb5_locate_kdc: zero kdcs found !\n")); - krb5_krbhst_free(ctx, hnd); - return -1; - } - - sa = malloc_array_p(struct sockaddr, num_kdcs); - if (!sa) { - DEBUG(0, ("krb5_locate_kdc: malloc failed\n")); - krb5_krbhst_free(ctx, hnd); - naddrs = 0; - return -1; - } - - memset(sa, '\0', sizeof(struct sockaddr) * num_kdcs ); - - for (i = 0; i < num_kdcs && (rc = krb5_krbhst_next(ctx, hnd, &hinfo) == 0); i++) { - -#if defined(HAVE_KRB5_KRBHST_GET_ADDRINFO) - rc = krb5_krbhst_get_addrinfo(ctx, hinfo, &ai); - if (rc) { - DEBUG(0,("krb5_krbhst_get_addrinfo failed: %s\n", error_message(rc))); - continue; - } -#endif - if (hinfo->ai && hinfo->ai->ai_family == AF_INET) - memcpy(&sa[i], hinfo->ai->ai_addr, sizeof(struct sockaddr)); - } - - krb5_krbhst_free(ctx, hnd); - - *naddrs = num_kdcs; - *addr_pp = sa; - return 0; -} -#endif - -#if !defined(HAVE_KRB5_FREE_UNPARSED_NAME) - void krb5_free_unparsed_name(krb5_context context, char *val) -{ - SAFE_FREE(val); -} -#endif - - void kerberos_free_data_contents(krb5_context context, krb5_data *pdata) -{ -#if defined(HAVE_KRB5_FREE_DATA_CONTENTS) - if (pdata->data) { - krb5_free_data_contents(context, pdata); - } -#else - SAFE_FREE(pdata->data); -#endif -} - - void kerberos_set_creds_enctype(krb5_creds *pcreds, int enctype) -{ -#if defined(HAVE_KRB5_KEYBLOCK_IN_CREDS) - KRB5_KEY_TYPE((&pcreds->keyblock)) = enctype; -#elif defined(HAVE_KRB5_SESSION_IN_CREDS) - KRB5_KEY_TYPE((&pcreds->session)) = enctype; -#else -#error UNKNOWN_KEYBLOCK_MEMBER_IN_KRB5_CREDS_STRUCT -#endif -} - - BOOL kerberos_compatible_enctypes(krb5_context context, - krb5_enctype enctype1, - krb5_enctype enctype2) -{ -#if defined(HAVE_KRB5_C_ENCTYPE_COMPARE) - krb5_boolean similar = 0; - - krb5_c_enctype_compare(context, enctype1, enctype2, &similar); - return similar ? True : False; -#elif defined(HAVE_KRB5_ENCTYPES_COMPATIBLE_KEYS) - return krb5_enctypes_compatible_keys(context, enctype1, enctype2) ? True : False; -#endif -} - -static BOOL ads_cleanup_expired_creds(krb5_context context, - krb5_ccache ccache, - krb5_creds *credsp) -{ - krb5_error_code retval; - TALLOC_CTX *mem_ctx = talloc_init("ticket expied time"); - if (!mem_ctx) { - return False; - } - - DEBUG(3, ("Ticket in ccache[%s] expiration %s\n", - krb5_cc_default_name(context), - http_timestring(mem_ctx, credsp->times.endtime))); - - talloc_free(mem_ctx); - - /* we will probably need new tickets if the current ones - will expire within 10 seconds. - */ - if (credsp->times.endtime >= (time(NULL) + 10)) - return False; - - /* heimdal won't remove creds from a file ccache, and - perhaps we shouldn't anyway, since internally we - use memory ccaches, and a FILE one probably means that - we're using creds obtained outside of our exectuable - */ - if (StrCaseCmp(krb5_cc_get_type(context, ccache), "FILE") == 0) { - DEBUG(5, ("ads_cleanup_expired_creds: We do not remove creds from a FILE ccache\n")); - return False; - } - - retval = krb5_cc_remove_cred(context, ccache, 0, credsp); - if (retval) { - DEBUG(1, ("ads_cleanup_expired_creds: krb5_cc_remove_cred failed, err %s\n", - error_message(retval))); - /* If we have an error in this, we want to display it, - but continue as though we deleted it */ - } - return True; -} - -/* - we can't use krb5_mk_req because w2k wants the service to be in a particular format -*/ -krb5_error_code ads_krb5_mk_req(krb5_context context, - krb5_auth_context *auth_context, - const krb5_flags ap_req_options, - const char *principal, - krb5_ccache ccache, - krb5_data *outbuf) -{ - krb5_error_code retval; - krb5_principal server; - krb5_creds * credsp; - krb5_creds creds; - krb5_data in_data; - BOOL creds_ready = False; - - TALLOC_CTX *mem_ctx = NULL; - - retval = krb5_parse_name(context, principal, &server); - if (retval) { - DEBUG(1,("ads_krb5_mk_req: Failed to parse principal %s\n", principal)); - return retval; - } - - /* obtain ticket & session key */ - ZERO_STRUCT(creds); - if ((retval = krb5_copy_principal(context, server, &creds.server))) { - DEBUG(1,("krb5_copy_principal failed (%s)\n", - error_message(retval))); - goto cleanup_princ; - } - - if ((retval = krb5_cc_get_principal(context, ccache, &creds.client))) { - /* This can commonly fail on smbd startup with no ticket in the cache. - * Report at higher level than 1. */ - DEBUG(3,("ads_krb5_mk_req: krb5_cc_get_principal failed (%s)\n", - error_message(retval))); - goto cleanup_creds; - } - - while(!creds_ready) { - if ((retval = krb5_get_credentials(context, 0, ccache, - &creds, &credsp))) { - DEBUG(1,("ads_krb5_mk_req: krb5_get_credentials failed for %s (%s)\n", - principal, error_message(retval))); - goto cleanup_creds; - } - - /* cope with ticket being in the future due to clock skew */ - if ((unsigned)credsp->times.starttime > time(NULL)) { - time_t t = time(NULL); - int time_offset =(unsigned)credsp->times.starttime-t; - DEBUG(4,("ads_krb5_mk_req: Advancing clock by %d seconds to cope with clock skew\n", time_offset)); - krb5_set_real_time(context, t + time_offset + 1, 0); - } - - if (!ads_cleanup_expired_creds(context, ccache, credsp)) - creds_ready = True; - } - - mem_ctx = talloc_init("ticket expied time"); - if (!mem_ctx) { - retval = ENOMEM; - goto cleanup_creds; - } - DEBUG(10,("Ticket (%s) in ccache (%s) is valid until: (%s - %d)\n", - principal, krb5_cc_default_name(context), - http_timestring(mem_ctx, (unsigned)credsp->times.endtime), - (unsigned)credsp->times.endtime)); - - in_data.length = 0; - retval = krb5_mk_req_extended(context, auth_context, ap_req_options, - &in_data, credsp, outbuf); - if (retval) { - DEBUG(1,("ads_krb5_mk_req: krb5_mk_req_extended failed (%s)\n", - error_message(retval))); - } - - krb5_free_creds(context, credsp); - -cleanup_creds: - krb5_free_cred_contents(context, &creds); - -cleanup_princ: - krb5_free_principal(context, server); - - return retval; -} - -#if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING) && !defined(HAVE_KRB5_PRINC_COMPONENT) - const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i ) -{ - static krb5_data kdata; - - kdata.data = discard_const(krb5_principal_get_comp_string(context, principal, i)); - kdata.length = strlen(kdata.data); - return &kdata; -} -#endif - - krb5_error_code smb_krb5_kt_free_entry(krb5_context context, krb5_keytab_entry *kt_entry) -{ -#if defined(HAVE_KRB5_KT_FREE_ENTRY) - return krb5_kt_free_entry(context, kt_entry); -#elif defined(HAVE_KRB5_FREE_KEYTAB_ENTRY_CONTENTS) - return krb5_free_keytab_entry_contents(context, kt_entry); -#else -#error UNKNOWN_KT_FREE_FUNCTION -#endif -} - - char *smb_get_krb5_error_message(krb5_context context, krb5_error_code code, TALLOC_CTX *mem_ctx) -{ - char *ret; - -#if defined(HAVE_KRB5_GET_ERROR_STRING) && defined(HAVE_KRB5_FREE_ERROR_STRING) - char *context_error = krb5_get_error_string(context); - ret = talloc_asprintf(mem_ctx, "%s: %s", error_message(code), context_error); - krb5_free_error_string(context, context_error); -#else - ret = talloc_strdup(mem_ctx, error_message(code)); -#endif - return ret; -} - -#endif diff --git a/source4/libcli/auth/gensec.c b/source4/libcli/auth/gensec.c deleted file mode 100644 index cc7327187c..0000000000 --- a/source4/libcli/auth/gensec.c +++ /dev/null @@ -1,630 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - Generic Authentication Interface - - Copyright (C) Andrew Tridgell 2003 - Copyright (C) Andrew Bartlett 2004-2005 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include "includes.h" -#include "auth/auth.h" - -/* the list of currently registered GENSEC backends */ -const static struct gensec_security_ops **generic_security_ops; -static int gensec_num_backends; - -static const struct gensec_security_ops *gensec_security_by_authtype(uint8_t auth_type) -{ - int i; - for (i=0; i < gensec_num_backends; i++) { - if (generic_security_ops[i]->auth_type == auth_type) { - return generic_security_ops[i]; - } - } - - return NULL; -} - -static const struct gensec_security_ops *gensec_security_by_oid(const char *oid_string) -{ - int i; - for (i=0; i < gensec_num_backends; i++) { - if (generic_security_ops[i]->oid && - (strcmp(generic_security_ops[i]->oid, oid_string) == 0)) { - return generic_security_ops[i]; - } - } - - return NULL; -} - -static const struct gensec_security_ops *gensec_security_by_sasl_name(const char *sasl_name) -{ - int i; - for (i=0; i < gensec_num_backends; 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; -} - -static const struct gensec_security_ops *gensec_security_by_name(const char *name) -{ - int i; - for (i=0; i < gensec_num_backends; i++) { - if (generic_security_ops[i]->name - && (strcmp(generic_security_ops[i]->name, name) == 0)) { - return generic_security_ops[i]; - } - } - - return NULL; -} - -const struct gensec_security_ops **gensec_security_all(int *num_backends_out) -{ - *num_backends_out = gensec_num_backends; - return generic_security_ops; -} - -const char **gensec_security_oids(TALLOC_CTX *mem_ctx, const char *skip) -{ - int i, j = 0; - const char **oid_list; - int num_backends; - const struct gensec_security_ops **ops = gensec_security_all(&num_backends); - if (!ops) { - return NULL; - } - oid_list = talloc_array(mem_ctx, const char *, num_backends + 1); - if (!oid_list) { - return NULL; - } - - for (i=0; ioid) { - continue; - } - - if (skip && strcmp(skip, ops[i]->oid)==0) { - continue; - } - - oid_list[j] = ops[i]->oid; - j++; - } - oid_list[j] = NULL; - return oid_list; -} - -/** - Start the GENSEC system, returning a context pointer. - @param mem_ctx The parent TALLOC memory context. - @param gensec_security Returned GENSEC context pointer. - @note The mem_ctx is only a parent and may be NULL. -*/ -static NTSTATUS gensec_start(TALLOC_CTX *mem_ctx, struct gensec_security **gensec_security) -{ - (*gensec_security) = talloc(mem_ctx, struct gensec_security); - if (!(*gensec_security)) { - return NT_STATUS_NO_MEMORY; - } - - (*gensec_security)->ops = NULL; - - ZERO_STRUCT((*gensec_security)->target); - - (*gensec_security)->subcontext = False; - (*gensec_security)->want_features = 0; - return NT_STATUS_OK; -} - -/** - * Start a GENSEC subcontext, with a copy of the properties of the parent - * @param mem_ctx The parent TALLOC memory context. - * @param parent The parent GENSEC context - * @param gensec_security Returned GENSEC context pointer. - * @note Used by SPNEGO in particular, for the actual implementation mechanism - */ - -NTSTATUS gensec_subcontext_start(TALLOC_CTX *mem_ctx, - struct gensec_security *parent, - struct gensec_security **gensec_security) -{ - (*gensec_security) = talloc(mem_ctx, struct gensec_security); - if (!(*gensec_security)) { - return NT_STATUS_NO_MEMORY; - } - - (**gensec_security) = *parent; - (*gensec_security)->ops = NULL; - (*gensec_security)->private_data = NULL; - - (*gensec_security)->subcontext = True; - - return NT_STATUS_OK; -} - -/** - Start the GENSEC system, in client mode, returning a context pointer. - @param mem_ctx The parent TALLOC memory context. - @param gensec_security Returned GENSEC context pointer. - @note The mem_ctx is only a parent and may be NULL. -*/ -NTSTATUS gensec_client_start(TALLOC_CTX *mem_ctx, struct gensec_security **gensec_security) -{ - NTSTATUS status; - status = gensec_start(mem_ctx, gensec_security); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - (*gensec_security)->gensec_role = GENSEC_CLIENT; - (*gensec_security)->password_callback = NULL; - - return status; -} - -/** - Start the GENSEC system, in server mode, returning a context pointer. - @param mem_ctx The parent TALLOC memory context. - @param gensec_security Returned GENSEC context pointer. - @note The mem_ctx is only a parent and may be NULL. -*/ -NTSTATUS gensec_server_start(TALLOC_CTX *mem_ctx, struct gensec_security **gensec_security) -{ - NTSTATUS status; - status = gensec_start(mem_ctx, gensec_security); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - (*gensec_security)->gensec_role = GENSEC_SERVER; - - return status; -} - -static NTSTATUS gensec_start_mech(struct gensec_security *gensec_security) -{ - NTSTATUS status; - DEBUG(5, ("Starting GENSEC %smechanism %s\n", - gensec_security->subcontext ? "sub" : "", - gensec_security->ops->name)); - switch (gensec_security->gensec_role) { - case GENSEC_CLIENT: - if (gensec_security->ops->client_start) { - status = gensec_security->ops->client_start(gensec_security); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("Failed to start GENSEC client mech %s: %s\n", - gensec_security->ops->name, nt_errstr(status))); - } - return status; - } - case GENSEC_SERVER: - if (gensec_security->ops->server_start) { - status = gensec_security->ops->server_start(gensec_security); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("Failed to start GENSEC server mech %s: %s\n", - gensec_security->ops->name, nt_errstr(status))); - } - return status; - } - } - return NT_STATUS_INVALID_PARAMETER; -} - -/** - * Start a GENSEC sub-mechanism by DCERPC allocated 'auth type' number - * @param gensec_security GENSEC context pointer. - * @param auth_type DCERPC auth type - * @param auth_level DCERPC auth level - */ - -NTSTATUS gensec_start_mech_by_authtype(struct gensec_security *gensec_security, - uint8_t auth_type, uint8_t auth_level) -{ - gensec_security->ops = gensec_security_by_authtype(auth_type); - if (!gensec_security->ops) { - DEBUG(3, ("Could not find GENSEC backend for auth_type=%d\n", (int)auth_type)); - return NT_STATUS_INVALID_PARAMETER; - } - gensec_want_feature(gensec_security, GENSEC_FEATURE_DCE_STYLE); - if (auth_level == DCERPC_AUTH_LEVEL_INTEGRITY) { - gensec_want_feature(gensec_security, GENSEC_FEATURE_SIGN); - } - if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { - gensec_want_feature(gensec_security, GENSEC_FEATURE_SIGN); - gensec_want_feature(gensec_security, GENSEC_FEATURE_SEAL); - } - - return gensec_start_mech(gensec_security); -} - -const char *gensec_get_name_by_authtype(uint8_t authtype) -{ - const struct gensec_security_ops *ops; - ops = gensec_security_by_authtype(authtype); - if (ops) { - return ops->name; - } - return NULL; -} - - -const char *gensec_get_name_by_oid(const char *oid_string) -{ - const struct gensec_security_ops *ops; - ops = gensec_security_by_oid(oid_string); - if (ops) { - return ops->name; - } - return NULL; -} - - -/** - * Start a GENSEC sub-mechanism by OID, used in SPNEGO - * - * @note This should also be used when you wish to just start NLTMSSP (for example), as it uses a - * well-known #define to hook it in. - */ - -NTSTATUS gensec_start_mech_by_oid(struct gensec_security *gensec_security, - const char *mech_oid) -{ - gensec_security->ops = gensec_security_by_oid(mech_oid); - if (!gensec_security->ops) { - DEBUG(3, ("Could not find GENSEC backend for oid=%s\n", mech_oid)); - return NT_STATUS_INVALID_PARAMETER; - } - return gensec_start_mech(gensec_security); -} - -/** - * Start a GENSEC sub-mechanism by a well know SASL name - * - */ - -NTSTATUS gensec_start_mech_by_sasl_name(struct gensec_security *gensec_security, - const char *sasl_name) -{ - gensec_security->ops = gensec_security_by_sasl_name(sasl_name); - if (!gensec_security->ops) { - DEBUG(3, ("Could not find GENSEC backend for sasl_name=%s\n", sasl_name)); - return NT_STATUS_INVALID_PARAMETER; - } - return gensec_start_mech(gensec_security); -} - -/* - wrappers for the gensec function pointers -*/ -NTSTATUS gensec_unseal_packet(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - uint8_t *data, size_t length, - const uint8_t *whole_pdu, size_t pdu_length, - DATA_BLOB *sig) -{ - if (!gensec_security->ops->unseal_packet) { - return NT_STATUS_NOT_IMPLEMENTED; - } - if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { - if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { - return gensec_check_packet(gensec_security, mem_ctx, - data, length, - whole_pdu, pdu_length, - sig); - } - return NT_STATUS_INVALID_PARAMETER; - } - - return gensec_security->ops->unseal_packet(gensec_security, mem_ctx, - data, length, - whole_pdu, pdu_length, - sig); -} - -NTSTATUS gensec_check_packet(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - const uint8_t *data, size_t length, - const uint8_t *whole_pdu, size_t pdu_length, - const DATA_BLOB *sig) -{ - if (!gensec_security->ops->check_packet) { - return NT_STATUS_NOT_IMPLEMENTED; - } - if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { - return NT_STATUS_INVALID_PARAMETER; - } - - return gensec_security->ops->check_packet(gensec_security, mem_ctx, data, length, whole_pdu, pdu_length, sig); -} - -NTSTATUS gensec_seal_packet(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - uint8_t *data, size_t length, - const uint8_t *whole_pdu, size_t pdu_length, - DATA_BLOB *sig) -{ - if (!gensec_security->ops->seal_packet) { - return NT_STATUS_NOT_IMPLEMENTED; - } - if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { - if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { - return gensec_sign_packet(gensec_security, mem_ctx, - data, length, - whole_pdu, pdu_length, - sig); - } - return NT_STATUS_INVALID_PARAMETER; - } - - return gensec_security->ops->seal_packet(gensec_security, mem_ctx, data, length, whole_pdu, pdu_length, sig); -} - -NTSTATUS gensec_sign_packet(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - const uint8_t *data, size_t length, - const uint8_t *whole_pdu, size_t pdu_length, - DATA_BLOB *sig) -{ - if (!gensec_security->ops->sign_packet) { - return NT_STATUS_NOT_IMPLEMENTED; - } - if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { - return NT_STATUS_INVALID_PARAMETER; - } - - return gensec_security->ops->sign_packet(gensec_security, mem_ctx, data, length, whole_pdu, pdu_length, sig); -} - -size_t gensec_sig_size(struct gensec_security *gensec_security) -{ - if (!gensec_security->ops->sig_size) { - return 0; - } - if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { - return 0; - } - - return gensec_security->ops->sig_size(gensec_security); -} - -NTSTATUS gensec_wrap(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - const DATA_BLOB *in, - DATA_BLOB *out) -{ - if (!gensec_security->ops->wrap) { - return NT_STATUS_NOT_IMPLEMENTED; - } - return gensec_security->ops->wrap(gensec_security, mem_ctx, in, out); -} - -NTSTATUS gensec_unwrap(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - const DATA_BLOB *in, - DATA_BLOB *out) -{ - if (!gensec_security->ops->unwrap) { - return NT_STATUS_NOT_IMPLEMENTED; - } - return gensec_security->ops->unwrap(gensec_security, mem_ctx, in, out); -} - -NTSTATUS gensec_session_key(struct gensec_security *gensec_security, - DATA_BLOB *session_key) -{ - if (!gensec_security->ops->session_key) { - return NT_STATUS_NOT_IMPLEMENTED; - } - return gensec_security->ops->session_key(gensec_security, session_key); -} - -/** - * Return the credentials of a logged on user, including session keys - * etc. - * - * Only valid after a successful authentication - * - * May only be called once per authentication. - * - */ - -NTSTATUS gensec_session_info(struct gensec_security *gensec_security, - struct auth_session_info **session_info) -{ - if (!gensec_security->ops->session_info) { - return NT_STATUS_NOT_IMPLEMENTED; - } - return gensec_security->ops->session_info(gensec_security, session_info); -} - -/** - * Next state function for the GENSEC state machine - * - * @param gensec_security GENSEC State - * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on - * @param in The request, as a DATA_BLOB - * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx - * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent, - * or NT_STATUS_OK if the user is authenticated. - */ - -NTSTATUS gensec_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, - const DATA_BLOB in, DATA_BLOB *out) -{ - return gensec_security->ops->update(gensec_security, out_mem_ctx, in, out); -} - -/** - * Set the requirement for a certain feature on the connection - * - */ - -void gensec_want_feature(struct gensec_security *gensec_security, - uint32_t feature) -{ - gensec_security->want_features |= feature; -} - -/** - * Check the requirement for a certain feature on the connection - * - */ - -BOOL gensec_have_feature(struct gensec_security *gensec_security, - uint32_t feature) -{ - if (!gensec_security->ops->have_feature) { - return False; - } - return gensec_security->ops->have_feature(gensec_security, feature); -} - -/** - * Associate a credentails structure with a GENSEC context - talloc_reference()s it to the context - * - */ - -NTSTATUS gensec_set_credentials(struct gensec_security *gensec_security, struct cli_credentials *credentials) -{ - gensec_security->credentials = talloc_reference(gensec_security, credentials); - return NT_STATUS_OK; -} - -/** - * Return the credentails structure associated with a GENSEC context - * - */ - -struct cli_credentials *gensec_get_credentials(struct gensec_security *gensec_security) -{ - return gensec_security->credentials; -} - -/** - * Set the target service (such as 'http' or 'host') on a GENSEC context - ensures it is talloc()ed - * - */ - -NTSTATUS gensec_set_target_service(struct gensec_security *gensec_security, const char *service) -{ - gensec_security->target.service = talloc_strdup(gensec_security, service); - if (!gensec_security->target.service) { - return NT_STATUS_NO_MEMORY; - } - return NT_STATUS_OK; -} - -/** - * Set the target hostname (suitable for kerberos resolutation) on a GENSEC context - ensures it is talloc()ed - * - */ - -NTSTATUS gensec_set_target_hostname(struct gensec_security *gensec_security, const char *hostname) -{ - gensec_security->target.hostname = talloc_strdup(gensec_security, hostname); - if (!gensec_security->target.hostname) { - return NT_STATUS_NO_MEMORY; - } - return NT_STATUS_OK; -} - -const char *gensec_get_target_hostname(struct gensec_security *gensec_security) -{ - if (gensec_security->target.hostname) { - return gensec_security->target.hostname; - } - - /* TODO: Add a 'set sockaddr' call, and do a reverse lookup */ - return NULL; -} - -const char *gensec_get_target_service(struct gensec_security *gensec_security) -{ - if (gensec_security->target.service) { - return gensec_security->target.service; - } - - return "host"; -} - -/* - register a GENSEC backend. - - The 'name' can be later used by other backends to find the operations - structure for this backend. -*/ -NTSTATUS gensec_register(const void *_ops) -{ - const struct gensec_security_ops *ops = _ops; - - if (!lp_parm_bool(-1, "gensec", ops->name, ops->enabled)) { - DEBUG(2,("gensec subsystem %s is disabled\n", ops->name)); - return NT_STATUS_OK; - } - - if (gensec_security_by_name(ops->name) != NULL) { - /* its already registered! */ - DEBUG(0,("GENSEC backend '%s' already registered\n", - ops->name)); - return NT_STATUS_OBJECT_NAME_COLLISION; - } - - generic_security_ops = realloc_p(generic_security_ops, - const struct gensec_security_ops *, - gensec_num_backends+1); - if (!generic_security_ops) { - smb_panic("out of memory in gensec_register"); - } - - generic_security_ops[gensec_num_backends] = ops; - - gensec_num_backends++; - - DEBUG(3,("GENSEC backend '%s' registered\n", - ops->name)); - - return NT_STATUS_OK; -} - -/* - return the GENSEC interface version, and the size of some critical types - This can be used by backends to either detect compilation errors, or provide - multiple implementations for different smbd compilation options in one module -*/ -const struct gensec_critical_sizes *gensec_interface_version(void) -{ - static const struct gensec_critical_sizes critical_sizes = { - GENSEC_INTERFACE_VERSION, - sizeof(struct gensec_security_ops), - sizeof(struct gensec_security), - }; - - return &critical_sizes; -} - -/* - initialise the GENSEC subsystem -*/ -NTSTATUS gensec_init(void) -{ - return NT_STATUS_OK; -} diff --git a/source4/libcli/auth/gensec.h b/source4/libcli/auth/gensec.h deleted file mode 100644 index 91c817d48a..0000000000 --- a/source4/libcli/auth/gensec.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - Generic Authentication Interface - - Copyright (C) Andrew Tridgell 2003 - Copyright (C) Andrew Bartlett 2004-2005 - - 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. -*/ - -#define GENSEC_OID_NTLMSSP "1 3 6 1 4 1 311 2 2 10" -#define GENSEC_OID_SPNEGO "1 3 6 1 5 5 2" -#define GENSEC_OID_KERBEROS5 "1 2 840 113554 1 2 2" -#define GENSEC_OID_KERBEROS5_OLD "1 2 840 48018 1 2 2" -#define GENSEC_OID_KERBEROS5_USER2USER "1 2 840 113554 1 2 2 3" - -struct gensec_security; -struct gensec_target { - const char *principal; - const char *hostname; - const struct sock_addr *addr; - const char *service; -}; - -#define GENSEC_FEATURE_SESSION_KEY 0x00000001 -#define GENSEC_FEATURE_SIGN 0x00000002 -#define GENSEC_FEATURE_SEAL 0x00000004 -#define GENSEC_FEATURE_DCE_STYLE 0x00000008 - -/* GENSEC mode */ -enum gensec_role -{ - GENSEC_SERVER, - GENSEC_CLIENT -}; - -struct auth_session_info; - -struct gensec_security_ops { - const char *name; - const char *sasl_name; - uint8_t auth_type; /* 0 if not offered on DCE-RPC */ - const char *oid; /* NULL if not offered by SPNEGO */ - 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_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx, - uint8_t *data, size_t length, - const uint8_t *whole_pdu, size_t pdu_length, - DATA_BLOB *sig); - NTSTATUS (*sign_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx, - const uint8_t *data, size_t length, - const uint8_t *whole_pdu, size_t pdu_length, - DATA_BLOB *sig); - size_t (*sig_size)(struct gensec_security *gensec_security); - NTSTATUS (*check_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx, - const uint8_t *data, size_t length, - const uint8_t *whole_pdu, size_t pdu_length, - const DATA_BLOB *sig); - NTSTATUS (*unseal_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx, - uint8_t *data, size_t length, - const uint8_t *whole_pdu, size_t pdu_length, - DATA_BLOB *sig); - NTSTATUS (*wrap)(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - const DATA_BLOB *in, - DATA_BLOB *out); - NTSTATUS (*unwrap)(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - const DATA_BLOB *in, - DATA_BLOB *out); - NTSTATUS (*session_key)(struct gensec_security *gensec_security, DATA_BLOB *session_key); - NTSTATUS (*session_info)(struct gensec_security *gensec_security, - struct auth_session_info **session_info); - BOOL (*have_feature)(struct gensec_security *gensec_security, - uint32_t feature); - BOOL enabled; -}; - -#define GENSEC_INTERFACE_VERSION 0 - -struct gensec_security { - gensec_password_callback password_callback; - void *password_callback_private; - const struct gensec_security_ops *ops; - void *private_data; - struct cli_credentials *credentials; - struct gensec_target target; - enum gensec_role gensec_role; - BOOL subcontext; - uint32_t want_features; -}; - -/* this structure is used by backends to determine the size of some critical types */ -struct gensec_critical_sizes { - int interface_version; - int sizeof_gensec_security_ops; - int sizeof_gensec_security; -}; - - -/* pre-declare schannel structure for schannel backend */ -struct schannel_state; diff --git a/source4/libcli/auth/gensec.m4 b/source4/libcli/auth/gensec.m4 deleted file mode 100644 index 6ccf45ad7e..0000000000 --- a/source4/libcli/auth/gensec.m4 +++ /dev/null @@ -1,12 +0,0 @@ -SMB_MODULE_DEFAULT(gensec_krb5, NOT) -SMB_MODULE_DEFAULT(gensec_gssapi, NOT) -SMB_MODULE_DEFAULT(gensec_gsskrb5, NOT) - -if test x"$SMB_EXT_LIB_ENABLE_KRB5" = x"YES"; then - # enable this when krb5 is fully working - SMB_MODULE_DEFAULT(gensec_krb5, STATIC) - SMB_MODULE_DEFAULT(gensec_gssapi, STATIC) - if test x"$samba_cv_GSS_C_DCE_STYLE" = x"yes"; then - SMB_MODULE_DEFAULT(gensec_gsskrb5, STATIC) - fi -fi diff --git a/source4/libcli/auth/gensec.mk b/source4/libcli/auth/gensec.mk deleted file mode 100644 index b4c612da14..0000000000 --- a/source4/libcli/auth/gensec.mk +++ /dev/null @@ -1,91 +0,0 @@ -################################# -# Start SUBSYSTEM GENSEC -[SUBSYSTEM::GENSEC] -INIT_FUNCTION = gensec_init -INIT_OBJ_FILES = libcli/auth/gensec.o -REQUIRED_SUBSYSTEMS = \ - SCHANNELDB -# End SUBSYSTEM GENSEC -################################# - -################################################ -# Start MODULE gensec_krb5 -[MODULE::gensec_krb5] -SUBSYSTEM = GENSEC -INIT_FUNCTION = gensec_krb5_init -INIT_OBJ_FILES = libcli/auth/gensec_krb5.o -ADD_OBJ_FILES = \ - libcli/auth/clikrb5.o \ - libcli/auth/kerberos.o \ - libcli/auth/kerberos_verify.o \ - libcli/auth/gssapi_parse.o -REQUIRED_SUBSYSTEMS = NDR_KRB5PAC EXT_LIB_KRB5 -# End MODULE gensec_krb5 -################################################ - -################################################ -# Start MODULE gensec_gssapi -[MODULE::gensec_gssapi] -SUBSYSTEM = GENSEC -INIT_FUNCTION = gensec_gssapi_init -INIT_OBJ_FILES = libcli/auth/gensec_gssapi.o -REQUIRED_SUBSYSTEMS = EXT_LIB_KRB5 -# End MODULE gensec_gssapi -################################################ - -################################################ -# Start MODULE gensec_gsskrb5 -[MODULE::gensec_gsskrb5] -SUBSYSTEM = GENSEC -INIT_FUNCTION = gensec_gsskrb5_init -INIT_OBJ_FILES = libcli/auth/gensec_gsskrb5.o -REQUIRED_SUBSYSTEMS = EXT_LIB_KRB5 -# End MODULE gensec_gsskrb5 -################################################ - -################################################ -# Start MODULE gensec_spnego -[MODULE::gensec_spnego] -SUBSYSTEM = GENSEC -INIT_FUNCTION = gensec_spnego_init -INIT_OBJ_FILES = libcli/auth/spnego.o -ADD_OBJ_FILES = \ - libcli/auth/spnego_parse.o -# End MODULE gensec_spnego -################################################ - -################################################ -# Start MODULE gensec_ntlmssp -[MODULE::gensec_ntlmssp] -SUBSYSTEM = GENSEC -INIT_FUNCTION = gensec_ntlmssp_init -INIT_OBJ_FILES = libcli/auth/gensec_ntlmssp.o -ADD_OBJ_FILES = \ - libcli/auth/ntlmssp.o \ - libcli/auth/ntlmssp_parse.o \ - libcli/auth/ntlmssp_sign.o -REQUIRED_SUBSYSTEMS = AUTH -# End MODULE gensec_ntlmssp -################################################ - -################################################ -# Start MODULE gensec_schannel -[MODULE::gensec_schannel] -SUBSYSTEM = GENSEC -INIT_FUNCTION = gensec_schannel_init -INIT_OBJ_FILES = libcli/auth/schannel.o -ADD_OBJ_FILES = \ - libcli/auth/schannel_sign.o -REQUIRED_SUBSYSTEMS = AUTH SCHANNELDB -# End MODULE gensec_ntlmssp -################################################ - -################################################ -# Start SUBSYSTEM SCHANNELDB -[SUBSYSTEM::SCHANNELDB] -INIT_OBJ_FILES = \ - libcli/auth/schannel_state.o -# -# End SUBSYSTEM SCHANNELDB -################################################ - diff --git a/source4/libcli/auth/gensec_gssapi.c b/source4/libcli/auth/gensec_gssapi.c deleted file mode 100644 index c974b93952..0000000000 --- a/source4/libcli/auth/gensec_gssapi.c +++ /dev/null @@ -1,376 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - Kerberos backend for GENSEC - - Copyright (C) Andrew Bartlett 2004 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include "includes.h" -#include "system/kerberos.h" -#include "auth/auth.h" - -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_AUTH - -struct gensec_gssapi_state { - gss_ctx_id_t gssapi_context; - struct gss_channel_bindings_struct *input_chan_bindings; - gss_name_t server_name; - gss_name_t client_name; - OM_uint32 want_flags, got_flags; - const gss_OID_desc *gss_oid; -}; -static int gensec_gssapi_destory(void *ptr) -{ - struct gensec_gssapi_state *gensec_gssapi_state = ptr; - OM_uint32 maj_stat, min_stat; - - if (gensec_gssapi_state->gssapi_context != GSS_C_NO_CONTEXT) { - maj_stat = gss_delete_sec_context (&min_stat, - &gensec_gssapi_state->gssapi_context, - GSS_C_NO_BUFFER); - } - - if (gensec_gssapi_state->server_name != GSS_C_NO_NAME) { - maj_stat = gss_release_name(&min_stat, &gensec_gssapi_state->server_name); - } - if (gensec_gssapi_state->client_name != GSS_C_NO_NAME) { - maj_stat = gss_release_name(&min_stat, &gensec_gssapi_state->client_name); - } - return 0; -} - -static NTSTATUS gensec_gssapi_start(struct gensec_security *gensec_security) -{ - struct gensec_gssapi_state *gensec_gssapi_state; - - gensec_gssapi_state = talloc(gensec_security, struct gensec_gssapi_state); - if (!gensec_gssapi_state) { - return NT_STATUS_NO_MEMORY; - } - - gensec_security->private_data = gensec_gssapi_state; - - gensec_gssapi_state->gssapi_context = GSS_C_NO_CONTEXT; - gensec_gssapi_state->server_name = GSS_C_NO_NAME; - gensec_gssapi_state->client_name = GSS_C_NO_NAME; - - talloc_set_destructor(gensec_gssapi_state, gensec_gssapi_destory); - - /* TODO: Fill in channel bindings */ - gensec_gssapi_state->input_chan_bindings = GSS_C_NO_CHANNEL_BINDINGS; - - gensec_gssapi_state->want_flags = 0; - gensec_gssapi_state->got_flags = 0; - - if (gensec_security->want_features & GENSEC_FEATURE_SESSION_KEY) { - /* GSSAPI won't give us the session keys */ - return NT_STATUS_INVALID_PARAMETER; - } - if (gensec_security->want_features & GENSEC_FEATURE_SIGN) { - gensec_gssapi_state->want_flags |= GSS_C_INTEG_FLAG; - } - if (gensec_security->want_features & GENSEC_FEATURE_SEAL) { - gensec_gssapi_state->want_flags |= GSS_C_CONF_FLAG; - } - - if (strcmp(gensec_security->ops->oid, GENSEC_OID_KERBEROS5) == 0) { - static const gss_OID_desc gensec_gss_krb5_mechanism_oid_desc = - {9, (void *)discard_const_p(char, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02")}; - - gensec_gssapi_state->gss_oid = &gensec_gss_krb5_mechanism_oid_desc; - } else if (strcmp(gensec_security->ops->oid, GENSEC_OID_SPNEGO) == 0) { - static const gss_OID_desc gensec_gss_spnego_mechanism_oid_desc = - {6, (void *)discard_const_p(char, "\x2b\x06\x01\x05\x05\x02")}; - gensec_gssapi_state->gss_oid = &gensec_gss_spnego_mechanism_oid_desc; - } else { - return NT_STATUS_INVALID_PARAMETER; - } - - return NT_STATUS_OK; -} - -static NTSTATUS gensec_gssapi_server_start(struct gensec_security *gensec_security) -{ - NTSTATUS nt_status; - struct gensec_gssapi_state *gensec_gssapi_state; - - nt_status = gensec_gssapi_start(gensec_security); - if (!NT_STATUS_IS_OK(nt_status)) { - return nt_status; - } - - gensec_gssapi_state = gensec_security->private_data; - - return NT_STATUS_OK; -} - -static NTSTATUS gensec_gssapi_client_start(struct gensec_security *gensec_security) -{ - struct gensec_gssapi_state *gensec_gssapi_state; - NTSTATUS nt_status; - gss_buffer_desc name_token; - OM_uint32 maj_stat, min_stat; - - gss_OID_desc hostbased = {10, - (void *)discard_const_p(char, "\x2a\x86\x48\x86\xf7\x12" - "\x01\x02\x01\x04")}; - - nt_status = gensec_gssapi_start(gensec_security); - if (!NT_STATUS_IS_OK(nt_status)) { - return nt_status; - } - - gensec_gssapi_state = gensec_security->private_data; - - name_token.value = talloc_asprintf(gensec_gssapi_state, "%s@%s", gensec_get_target_service(gensec_security), - gensec_get_target_hostname(gensec_security)); - DEBUG(0, ("name: %s\n", (char *)name_token.value)); - name_token.length = strlen(name_token.value); - - maj_stat = gss_import_name (&min_stat, - &name_token, - &hostbased, - &gensec_gssapi_state->server_name); - - - if (maj_stat) { - return NT_STATUS_UNSUCCESSFUL; - } - return NT_STATUS_OK; -} - - - -/** - * Next state function for the GSSAPI GENSEC mechanism - * - * @param gensec_gssapi_state GSSAPI State - * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on - * @param in The request, as a DATA_BLOB - * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx - * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent, - * or NT_STATUS_OK if the user is authenticated. - */ - -static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security, - TALLOC_CTX *out_mem_ctx, - const DATA_BLOB in, DATA_BLOB *out) -{ - struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data; - NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; - OM_uint32 maj_stat, min_stat; - OM_uint32 min_stat2; - gss_buffer_desc input_token, output_token; - gss_OID gss_oid_p; - input_token.length = in.length; - input_token.value = in.data; - - switch (gensec_security->gensec_role) { - case GENSEC_CLIENT: - { - maj_stat = gss_init_sec_context(&min_stat, - GSS_C_NO_CREDENTIAL, - &gensec_gssapi_state->gssapi_context, - gensec_gssapi_state->server_name, - discard_const_p(gss_OID_desc, gensec_gssapi_state->gss_oid), - gensec_gssapi_state->want_flags, - 0, - gensec_gssapi_state->input_chan_bindings, - &input_token, - NULL, - &output_token, - &gensec_gssapi_state->got_flags, /* ret flags */ - NULL); - break; - } - case GENSEC_SERVER: - { - maj_stat = gss_accept_sec_context(&min_stat, - &gensec_gssapi_state->gssapi_context, - GSS_C_NO_CREDENTIAL, - &input_token, - gensec_gssapi_state->input_chan_bindings, - &gensec_gssapi_state->client_name, - &gss_oid_p, - &output_token, - &gensec_gssapi_state->got_flags, - NULL, - NULL); - gensec_gssapi_state->gss_oid = gss_oid_p; - break; - } - default: - return NT_STATUS_INVALID_PARAMETER; - - } - - *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length); - gss_release_buffer(&min_stat2, &output_token); - - if (maj_stat == GSS_S_COMPLETE) { - return NT_STATUS_OK; - } else if (maj_stat == GSS_S_CONTINUE_NEEDED) { - return NT_STATUS_MORE_PROCESSING_REQUIRED; - } else { - gss_buffer_desc msg1, msg2; - OM_uint32 msg_ctx = 0; - - msg1.value = NULL; - msg2.value = NULL; - gss_display_status(&min_stat2, maj_stat, GSS_C_GSS_CODE, - GSS_C_NULL_OID, &msg_ctx, &msg1); - gss_display_status(&min_stat2, min_stat, GSS_C_MECH_CODE, - GSS_C_NULL_OID, &msg_ctx, &msg2); - DEBUG(1, ("gensec_gssapi_update: %s : %s\n", (char *)msg1.value, (char *)msg2.value)); - gss_release_buffer(&min_stat2, &msg1); - gss_release_buffer(&min_stat2, &msg2); - - return nt_status; - } - -} - -static NTSTATUS gensec_gssapi_wrap(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - const DATA_BLOB *in, - DATA_BLOB *out) -{ - struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data; - OM_uint32 maj_stat, min_stat; - gss_buffer_desc input_token, output_token; - int conf_state; - input_token.length = in->length; - input_token.value = in->data; - - maj_stat = gss_wrap(&min_stat, - gensec_gssapi_state->gssapi_context, - gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL), - GSS_C_QOP_DEFAULT, - &input_token, - &conf_state, - &output_token); - if (GSS_ERROR(maj_stat)) { - return NT_STATUS_ACCESS_DENIED; - } - *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length); - - gss_release_buffer(&min_stat, &output_token); - - if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL) - && !conf_state) { - return NT_STATUS_ACCESS_DENIED; - } - return NT_STATUS_OK; -} - -static NTSTATUS gensec_gssapi_unwrap(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - const DATA_BLOB *in, - DATA_BLOB *out) -{ - struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data; - OM_uint32 maj_stat, min_stat; - gss_buffer_desc input_token, output_token; - int conf_state; - gss_qop_t qop_state; - input_token.length = in->length; - input_token.value = in->data; - - maj_stat = gss_unwrap(&min_stat, - gensec_gssapi_state->gssapi_context, - &input_token, - &output_token, - &conf_state, - &qop_state); - if (GSS_ERROR(maj_stat)) { - return NT_STATUS_ACCESS_DENIED; - } - *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length); - - gss_release_buffer(&min_stat, &output_token); - - if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL) - && !conf_state) { - return NT_STATUS_ACCESS_DENIED; - } - return NT_STATUS_OK; -} - -static BOOL gensec_gssapi_have_feature(struct gensec_security *gensec_security, - uint32_t feature) -{ - struct gensec_gssapi_state *gensec_gssapi_state = gensec_security->private_data; - if (feature & GENSEC_FEATURE_SIGN) { - return gensec_gssapi_state->got_flags & GSS_C_INTEG_FLAG; - } - if (feature & GENSEC_FEATURE_SEAL) { - return gensec_gssapi_state->got_flags & GSS_C_CONF_FLAG; - } - return False; -} - -/* As a server, this could in theory accept any GSSAPI mech */ -static const struct gensec_security_ops gensec_gssapi_krb5_security_ops = { - .name = "gssapi_krb5", - .sasl_name = "GSSAPI", - .oid = GENSEC_OID_KERBEROS5, - .client_start = gensec_gssapi_client_start, - .server_start = gensec_gssapi_server_start, - .update = gensec_gssapi_update, - .wrap = gensec_gssapi_wrap, - .unwrap = gensec_gssapi_unwrap, - .have_feature = gensec_gssapi_have_feature, - .enabled = False - -}; - -static const struct gensec_security_ops gensec_gssapi_spnego_security_ops = { - .name = "gssapi_spnego", - .sasl_name = "GSS-SPNEGO", - .oid = GENSEC_OID_SPNEGO, - .client_start = gensec_gssapi_client_start, - .server_start = gensec_gssapi_server_start, - .update = gensec_gssapi_update, - .wrap = gensec_gssapi_wrap, - .unwrap = gensec_gssapi_unwrap, - .have_feature = gensec_gssapi_have_feature, - .enabled = False -}; - -NTSTATUS gensec_gssapi_init(void) -{ - NTSTATUS ret; - - ret = gensec_register(&gensec_gssapi_krb5_security_ops); - if (!NT_STATUS_IS_OK(ret)) { - DEBUG(0,("Failed to register '%s' gensec backend!\n", - gensec_gssapi_krb5_security_ops.name)); - return ret; - } - - ret = gensec_register(&gensec_gssapi_spnego_security_ops); - if (!NT_STATUS_IS_OK(ret)) { - DEBUG(0,("Failed to register '%s' gensec backend!\n", - gensec_gssapi_spnego_security_ops.name)); - return ret; - } - - return ret; -} diff --git a/source4/libcli/auth/gensec_gsskrb5.c b/source4/libcli/auth/gensec_gsskrb5.c deleted file mode 100644 index 77e077276b..0000000000 --- a/source4/libcli/auth/gensec_gsskrb5.c +++ /dev/null @@ -1,584 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - GSSAPI KRB5 backend for GENSEC - - Copyright (C) Andrew Bartlett 2004 - Copyright (C) Stefan Metzmacher 2005 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include "includes.h" -#include "system/kerberos.h" -#include "system/time.h" -#include "libcli/auth/kerberos.h" -#include "auth/auth.h" - -static const gss_OID_desc gensec_gss_krb5_mechanism_oid_desc = - {9, (void *)discard_const_p(char, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02")}; - -enum GENSEC_GSSKRB5_STATE { - GENSEC_GSSKRB5_CLIENT_START, - GENSEC_GSSKRB5_CLIENT_MUTUAL_AUTH, - GENSEC_GSSKRB5_CLIENT_DCE_STYLE, - GENSEC_GSSKRB5_DONE -}; - -struct gensec_gsskrb5_state { - enum GENSEC_GSSKRB5_STATE state_position; - gss_ctx_id_t gssapi_context; - struct gss_channel_bindings_struct *input_chan_bindings; - gss_name_t server_name; - gss_name_t client_name; - OM_uint32 want_flags, got_flags; -}; - -static int gensec_gsskrb5_destory(void *ptr) -{ - struct gensec_gsskrb5_state *gensec_gsskrb5_state = ptr; - OM_uint32 maj_stat, min_stat; - - if (gensec_gsskrb5_state->gssapi_context != GSS_C_NO_CONTEXT) { - maj_stat = gss_delete_sec_context (&min_stat, - &gensec_gsskrb5_state->gssapi_context, - GSS_C_NO_BUFFER); - } - - if (gensec_gsskrb5_state->server_name != GSS_C_NO_NAME) { - maj_stat = gss_release_name(&min_stat, &gensec_gsskrb5_state->server_name); - } - if (gensec_gsskrb5_state->client_name != GSS_C_NO_NAME) { - maj_stat = gss_release_name(&min_stat, &gensec_gsskrb5_state->client_name); - } - return 0; -} - -static NTSTATUS gensec_gsskrb5_start(struct gensec_security *gensec_security) -{ - struct gensec_gsskrb5_state *gensec_gsskrb5_state; - - gensec_gsskrb5_state = talloc(gensec_security, struct gensec_gsskrb5_state); - if (!gensec_gsskrb5_state) { - return NT_STATUS_NO_MEMORY; - } - - gensec_security->private_data = gensec_gsskrb5_state; - - gensec_gsskrb5_state->gssapi_context = GSS_C_NO_CONTEXT; - gensec_gsskrb5_state->server_name = GSS_C_NO_NAME; - gensec_gsskrb5_state->client_name = GSS_C_NO_NAME; - - talloc_set_destructor(gensec_gsskrb5_state, gensec_gsskrb5_destory); - - /* TODO: Fill in channel bindings */ - gensec_gsskrb5_state->input_chan_bindings = GSS_C_NO_CHANNEL_BINDINGS; - - gensec_gsskrb5_state->want_flags = GSS_C_MUTUAL_FLAG; - gensec_gsskrb5_state->got_flags = 0; - - if (gensec_security->want_features & GENSEC_FEATURE_SESSION_KEY) { - /* GSSAPI won't give us the session keys */ - return NT_STATUS_INVALID_PARAMETER; - } - if (gensec_security->want_features & GENSEC_FEATURE_SIGN) { - gensec_gsskrb5_state->want_flags |= GSS_C_INTEG_FLAG; - } - if (gensec_security->want_features & GENSEC_FEATURE_SEAL) { - gensec_gsskrb5_state->want_flags |= GSS_C_CONF_FLAG; - } - if (gensec_security->want_features & GENSEC_FEATURE_DCE_STYLE) { - gensec_gsskrb5_state->want_flags |= GSS_C_DCE_STYLE; - } - - return NT_STATUS_OK; -} - -static NTSTATUS gensec_gsskrb5_client_start(struct gensec_security *gensec_security) -{ - struct gensec_gsskrb5_state *gensec_gsskrb5_state; - NTSTATUS nt_status; - gss_buffer_desc name_token; - OM_uint32 maj_stat, min_stat; - - gss_OID_desc hostbased = {10, - (void *)discard_const_p(char, "\x2a\x86\x48\x86\xf7\x12" - "\x01\x02\x01\x04")}; - - nt_status = gensec_gsskrb5_start(gensec_security); - if (!NT_STATUS_IS_OK(nt_status)) { - return nt_status; - } - - gensec_gsskrb5_state = gensec_security->private_data; - - gensec_gsskrb5_state->state_position = GENSEC_GSSKRB5_CLIENT_START; - - name_token.value = talloc_asprintf(gensec_gsskrb5_state, "%s@%s", gensec_get_target_service(gensec_security), - gensec_get_target_hostname(gensec_security)); - DEBUG(0, ("name: %s\n", (char *)name_token.value)); - name_token.length = strlen(name_token.value); - - maj_stat = gss_import_name (&min_stat, - &name_token, - &hostbased, - &gensec_gsskrb5_state->server_name); - if (maj_stat) { - return NT_STATUS_UNSUCCESSFUL; - } - - return NT_STATUS_OK; -} - -/** - * Next state function for the GSSKRB5 GENSEC mechanism - * - * @param gensec_gsskrb5_state GSSAPI State - * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on - * @param in The request, as a DATA_BLOB - * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx - * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent, - * or NT_STATUS_OK if the user is authenticated. - */ - -static NTSTATUS gensec_gsskrb5_update(struct gensec_security *gensec_security, - TALLOC_CTX *out_mem_ctx, - const DATA_BLOB in, DATA_BLOB *out) -{ - struct gensec_gsskrb5_state *gensec_gsskrb5_state = gensec_security->private_data; - NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; - OM_uint32 maj_stat, min_stat; - OM_uint32 min_stat2; - gss_buffer_desc input_token, output_token; - - *out = data_blob(NULL, 0); - - input_token.length = in.length; - input_token.value = in.data; - - switch (gensec_gsskrb5_state->state_position) { - case GENSEC_GSSKRB5_CLIENT_START: - { - maj_stat = gss_init_sec_context(&min_stat, - GSS_C_NO_CREDENTIAL, - &gensec_gsskrb5_state->gssapi_context, - gensec_gsskrb5_state->server_name, - discard_const_p(gss_OID_desc, &gensec_gss_krb5_mechanism_oid_desc), - gensec_gsskrb5_state->want_flags, - 0, - gensec_gsskrb5_state->input_chan_bindings, - &input_token, - NULL, - &output_token, - &gensec_gsskrb5_state->got_flags, /* ret flags */ - NULL); - *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length); - if (out->length != output_token.length) { - gss_release_buffer(&min_stat2, &output_token); - return NT_STATUS_NO_MEMORY; - } - gss_release_buffer(&min_stat2, &output_token); - - if (maj_stat == GSS_S_COMPLETE) { - gensec_gsskrb5_state->state_position = GENSEC_GSSKRB5_DONE; - return NT_STATUS_OK; - } else if (maj_stat == GSS_S_CONTINUE_NEEDED) { - gensec_gsskrb5_state->state_position = GENSEC_GSSKRB5_CLIENT_MUTUAL_AUTH; - return NT_STATUS_MORE_PROCESSING_REQUIRED; - } else { - gss_buffer_desc msg1, msg2; - OM_uint32 msg_ctx = 0; - - msg1.value = NULL; - msg2.value = NULL; - gss_display_status(&min_stat2, maj_stat, GSS_C_GSS_CODE, - GSS_C_NULL_OID, &msg_ctx, &msg1); - gss_display_status(&min_stat2, min_stat, GSS_C_MECH_CODE, - GSS_C_NULL_OID, &msg_ctx, &msg2); - DEBUG(1, ("gensec_gsskrb5_update: %s : %s\n", (char *)msg1.value, (char *)msg2.value)); - gss_release_buffer(&min_stat2, &msg1); - gss_release_buffer(&min_stat2, &msg2); - - return nt_status; - } - break; - } - case GENSEC_GSSKRB5_CLIENT_MUTUAL_AUTH: - { - maj_stat = gss_init_sec_context(&min_stat, - GSS_C_NO_CREDENTIAL, - &gensec_gsskrb5_state->gssapi_context, - gensec_gsskrb5_state->server_name, - discard_const_p(gss_OID_desc, &gensec_gss_krb5_mechanism_oid_desc), - gensec_gsskrb5_state->want_flags, - 0, - gensec_gsskrb5_state->input_chan_bindings, - &input_token, - NULL, - &output_token, - &gensec_gsskrb5_state->got_flags, /* ret flags */ - NULL); - *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length); - if (out->length != output_token.length) { - gss_release_buffer(&min_stat2, &output_token); - return NT_STATUS_NO_MEMORY; - } - gss_release_buffer(&min_stat2, &output_token); - - if (maj_stat == GSS_S_COMPLETE) { - if (gensec_gsskrb5_state->got_flags & GSS_C_DCE_STYLE) { - gensec_gsskrb5_state->state_position = GENSEC_GSSKRB5_CLIENT_DCE_STYLE; - return NT_STATUS_MORE_PROCESSING_REQUIRED; - } - gensec_gsskrb5_state->state_position = GENSEC_GSSKRB5_DONE; - return NT_STATUS_OK; - } else if (maj_stat == GSS_S_CONTINUE_NEEDED) { - gensec_gsskrb5_state->state_position = GENSEC_GSSKRB5_CLIENT_DCE_STYLE; - return NT_STATUS_MORE_PROCESSING_REQUIRED; - } else { - gss_buffer_desc msg1, msg2; - OM_uint32 msg_ctx = 0; - - msg1.value = NULL; - msg2.value = NULL; - gss_display_status(&min_stat2, maj_stat, GSS_C_GSS_CODE, - GSS_C_NULL_OID, &msg_ctx, &msg1); - gss_display_status(&min_stat2, min_stat, GSS_C_MECH_CODE, - GSS_C_NULL_OID, &msg_ctx, &msg2); - DEBUG(1, ("gensec_gsskrb5_update: %s : %s\n", (char *)msg1.value, (char *)msg2.value)); - gss_release_buffer(&min_stat2, &msg1); - gss_release_buffer(&min_stat2, &msg2); - - return nt_status; - } - break; - } - case GENSEC_GSSKRB5_CLIENT_DCE_STYLE: - { - gensec_gsskrb5_state->state_position = GENSEC_GSSKRB5_DONE; - return NT_STATUS_OK; - } - case GENSEC_GSSKRB5_DONE: - { - return NT_STATUS_OK; - } - default: - return NT_STATUS_INVALID_PARAMETER; - } - - return NT_STATUS_INVALID_PARAMETER; -} - -static NTSTATUS gensec_gsskrb5_wrap(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - const DATA_BLOB *in, - DATA_BLOB *out) -{ - struct gensec_gsskrb5_state *gensec_gsskrb5_state = gensec_security->private_data; - OM_uint32 maj_stat, min_stat; - gss_buffer_desc input_token, output_token; - int conf_state; - input_token.length = in->length; - input_token.value = in->data; - - maj_stat = gss_wrap(&min_stat, - gensec_gsskrb5_state->gssapi_context, - gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL), - GSS_C_QOP_DEFAULT, - &input_token, - &conf_state, - &output_token); - if (GSS_ERROR(maj_stat)) { - return NT_STATUS_ACCESS_DENIED; - } - *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length); - - gss_release_buffer(&min_stat, &output_token); - - if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL) - && !conf_state) { - return NT_STATUS_ACCESS_DENIED; - } - return NT_STATUS_OK; -} - -static NTSTATUS gensec_gsskrb5_unwrap(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - const DATA_BLOB *in, - DATA_BLOB *out) -{ - struct gensec_gsskrb5_state *gensec_gsskrb5_state = gensec_security->private_data; - OM_uint32 maj_stat, min_stat; - gss_buffer_desc input_token, output_token; - int conf_state; - gss_qop_t qop_state; - input_token.length = in->length; - input_token.value = in->data; - - maj_stat = gss_unwrap(&min_stat, - gensec_gsskrb5_state->gssapi_context, - &input_token, - &output_token, - &conf_state, - &qop_state); - if (GSS_ERROR(maj_stat)) { - return NT_STATUS_ACCESS_DENIED; - } - *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length); - - gss_release_buffer(&min_stat, &output_token); - - if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL) - && !conf_state) { - return NT_STATUS_ACCESS_DENIED; - } - return NT_STATUS_OK; -} - -static size_t gensec_gsskrb5_sig_size(struct gensec_security *gensec_security) -{ - /* not const but work for DCERPC packets and arcfour */ - return 45; -} - -static NTSTATUS gensec_gsskrb5_seal_packet(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - uint8_t *data, size_t length, - const uint8_t *whole_pdu, size_t pdu_length, - DATA_BLOB *sig) -{ - struct gensec_gsskrb5_state *gensec_gsskrb5_state = gensec_security->private_data; - OM_uint32 maj_stat, min_stat; - gss_buffer_desc input_token, output_token; - int conf_state; - ssize_t sig_length = 0; - - input_token.length = length; - input_token.value = data; - - maj_stat = gss_wrap(&min_stat, - gensec_gsskrb5_state->gssapi_context, - gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL), - GSS_C_QOP_DEFAULT, - &input_token, - &conf_state, - &output_token); - if (GSS_ERROR(maj_stat)) { - return NT_STATUS_ACCESS_DENIED; - } - - if (output_token.length < length) { - return NT_STATUS_INTERNAL_ERROR; - } - - sig_length = 45; - - memcpy(data, ((uint8_t *)output_token.value) + sig_length, length); - *sig = data_blob_talloc(mem_ctx, (uint8_t *)output_token.value, sig_length); - -DEBUG(0,("gensec_gsskrb5_seal_packet: siglen: %d inlen: %d, wrap_len: %d\n", sig->length, length, output_token.length - sig_length)); -dump_data(0,sig->data, sig->length); -dump_data(0,data, length); -dump_data(0,((uint8_t *)output_token.value) + sig_length, output_token.length - sig_length); - - gss_release_buffer(&min_stat, &output_token); - - if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL) - && !conf_state) { - return NT_STATUS_ACCESS_DENIED; - } - return NT_STATUS_OK; -} - -static NTSTATUS gensec_gsskrb5_unseal_packet(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - uint8_t *data, size_t length, - const uint8_t *whole_pdu, size_t pdu_length, - DATA_BLOB *sig) -{ - struct gensec_gsskrb5_state *gensec_gsskrb5_state = gensec_security->private_data; - OM_uint32 maj_stat, min_stat; - gss_buffer_desc input_token, output_token; - int conf_state; - gss_qop_t qop_state; - DATA_BLOB in; - -DEBUG(0,("gensec_gsskrb5_unseal_packet: siglen: %d\n", sig->length)); -dump_data(0,sig->data, sig->length); - - in = data_blob_talloc(mem_ctx, NULL, sig->length + length); - - memcpy(in.data, sig->data, sig->length); - memcpy(in.data + sig->length, data, length); - - input_token.length = in.length; - input_token.value = in.data; - - maj_stat = gss_unwrap(&min_stat, - gensec_gsskrb5_state->gssapi_context, - &input_token, - &output_token, - &conf_state, - &qop_state); - if (GSS_ERROR(maj_stat)) { - return NT_STATUS_ACCESS_DENIED; - } - - if (output_token.length != length) { - return NT_STATUS_INTERNAL_ERROR; - } - - memcpy(data, output_token.value, length); - - gss_release_buffer(&min_stat, &output_token); - - if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL) - && !conf_state) { - return NT_STATUS_ACCESS_DENIED; - } - return NT_STATUS_OK; -} - -static NTSTATUS gensec_gsskrb5_sign_packet(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - const uint8_t *data, size_t length, - const uint8_t *whole_pdu, size_t pdu_length, - DATA_BLOB *sig) -{ - struct gensec_gsskrb5_state *gensec_gsskrb5_state = gensec_security->private_data; - OM_uint32 maj_stat, min_stat; - gss_buffer_desc input_token, output_token; - int conf_state; - ssize_t sig_length = 0; - - input_token.length = length; - input_token.value = data; - - maj_stat = gss_wrap(&min_stat, - gensec_gsskrb5_state->gssapi_context, - 0, - GSS_C_QOP_DEFAULT, - &input_token, - &conf_state, - &output_token); - if (GSS_ERROR(maj_stat)) { - return NT_STATUS_ACCESS_DENIED; - } - - if (output_token.length < length) { - return NT_STATUS_INTERNAL_ERROR; - } - - sig_length = 45; - - /*memcpy(data, ((uint8_t *)output_token.value) + sig_length, length);*/ - *sig = data_blob_talloc(mem_ctx, (uint8_t *)output_token.value, sig_length); - -DEBUG(0,("gensec_gsskrb5_sign_packet: siglen: %d\n", sig->length)); -dump_data(0,sig->data, sig->length); - - gss_release_buffer(&min_stat, &output_token); - - return NT_STATUS_OK; -} - -static NTSTATUS gensec_gsskrb5_check_packet(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - const uint8_t *data, size_t length, - const uint8_t *whole_pdu, size_t pdu_length, - const DATA_BLOB *sig) -{ - struct gensec_gsskrb5_state *gensec_gsskrb5_state = gensec_security->private_data; - OM_uint32 maj_stat, min_stat; - gss_buffer_desc input_token, output_token; - int conf_state; - gss_qop_t qop_state; - DATA_BLOB in; - -DEBUG(0,("gensec_gsskrb5_check_packet: siglen: %d\n", sig->length)); -dump_data(0,sig->data, sig->length); - - in = data_blob_talloc(mem_ctx, NULL, sig->length + length); - - memcpy(in.data, sig->data, sig->length); - memcpy(in.data + sig->length, data, length); - - input_token.length = in.length; - input_token.value = in.data; - - maj_stat = gss_unwrap(&min_stat, - gensec_gsskrb5_state->gssapi_context, - &input_token, - &output_token, - &conf_state, - &qop_state); - if (GSS_ERROR(maj_stat)) { - return NT_STATUS_ACCESS_DENIED; - } - - if (output_token.length != length) { - return NT_STATUS_INTERNAL_ERROR; - } - - /*memcpy(data, output_token.value, length);*/ - - gss_release_buffer(&min_stat, &output_token); - - return NT_STATUS_OK; -} - -static BOOL gensec_gsskrb5_have_feature(struct gensec_security *gensec_security, - uint32_t feature) -{ - struct gensec_gsskrb5_state *gensec_gsskrb5_state = gensec_security->private_data; - if (feature & GENSEC_FEATURE_SIGN) { - return gensec_gsskrb5_state->got_flags & GSS_C_INTEG_FLAG; - } - if (feature & GENSEC_FEATURE_SEAL) { - return gensec_gsskrb5_state->got_flags & GSS_C_CONF_FLAG; - } - return False; -} - -static const struct gensec_security_ops gensec_gsskrb5_security_ops = { - .name = "gsskrb5", - .auth_type = DCERPC_AUTH_TYPE_KRB5, - .oid = GENSEC_OID_KERBEROS5, - .client_start = gensec_gsskrb5_client_start, - .update = gensec_gsskrb5_update, - .sig_size = gensec_gsskrb5_sig_size, - .sign_packet = gensec_gsskrb5_sign_packet, - .check_packet = gensec_gsskrb5_check_packet, - .seal_packet = gensec_gsskrb5_seal_packet, - .unseal_packet = gensec_gsskrb5_unseal_packet, - .wrap = gensec_gsskrb5_wrap, - .unwrap = gensec_gsskrb5_unwrap, - .have_feature = gensec_gsskrb5_have_feature, - .enabled = False -}; - -NTSTATUS gensec_gsskrb5_init(void) -{ - NTSTATUS ret; - - ret = gensec_register(&gensec_gsskrb5_security_ops); - if (!NT_STATUS_IS_OK(ret)) { - DEBUG(0,("Failed to register '%s' gensec backend!\n", - gensec_gsskrb5_security_ops.name)); - return ret; - } - - return ret; -} diff --git a/source4/libcli/auth/gensec_krb5.c b/source4/libcli/auth/gensec_krb5.c deleted file mode 100644 index 453485d816..0000000000 --- a/source4/libcli/auth/gensec_krb5.c +++ /dev/null @@ -1,751 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - Kerberos backend for GENSEC - - Copyright (C) Andrew Bartlett 2004 - Copyright (C) Andrew Tridgell 2001 - Copyright (C) Luke Howard 2002-2003 - Copyright (C) Stefan Metzmacher 2004-2005 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include "includes.h" -#include "system/kerberos.h" -#include "system/time.h" -#include "libcli/auth/kerberos.h" -#include "librpc/gen_ndr/ndr_krb5pac.h" -#include "auth/auth.h" - -enum GENSEC_KRB5_STATE { - GENSEC_KRB5_SERVER_START, - GENSEC_KRB5_CLIENT_START, - GENSEC_KRB5_CLIENT_MUTUAL_AUTH, - GENSEC_KRB5_DONE -}; - -struct gensec_krb5_state { - DATA_BLOB session_key; - DATA_BLOB pac; - enum GENSEC_KRB5_STATE state_position; - krb5_context context; - krb5_auth_context auth_context; - krb5_ccache ccache; - krb5_data ticket; - krb5_keyblock keyblock; - char *peer_principal; -}; - -#ifdef KRB5_DO_VERIFY_PAC -static NTSTATUS gensec_krb5_pac_checksum(DATA_BLOB pac_data, - struct PAC_SIGNATURE_DATA *sig, - struct gensec_krb5_state *gensec_krb5_state, - uint32 keyusage) -{ - krb5_error_code ret; - krb5_crypto crypto; - Checksum cksum; - int i; - - cksum.cksumtype = (CKSUMTYPE)sig->type; - cksum.checksum.length = sizeof(sig->signature); - cksum.checksum.data = sig->signature; - - - ret = krb5_crypto_init(gensec_krb5_state->context, - &gensec_krb5_state->keyblock, - 0, - &crypto); - if (ret) { - DEBUG(0,("krb5_crypto_init() failed\n")); - return NT_STATUS_FOOBAR; - } - for (i=0; i < 40; i++) { - keyusage = i; - ret = krb5_verify_checksum(gensec_krb5_state->context, - crypto, - keyusage, - pac_data.data, - pac_data.length, - &cksum); - if (!ret) { - DEBUG(0,("PAC Verified: keyusage: %d\n", keyusage)); - break; - } - } - krb5_crypto_destroy(gensec_krb5_state->context, crypto); - - if (ret) { - DEBUG(0,("NOT verifying PAC checksums yet!\n")); - //return NT_STATUS_LOGON_FAILURE; - } else { - DEBUG(0,("PAC checksums verified!\n")); - } - - return NT_STATUS_OK; -} -#endif - -static NTSTATUS gensec_krb5_decode_pac(TALLOC_CTX *mem_ctx, - struct PAC_LOGON_INFO **logon_info_out, - DATA_BLOB blob, - struct gensec_krb5_state *gensec_krb5_state) -{ - NTSTATUS status; - struct PAC_SIGNATURE_DATA srv_sig; - struct PAC_SIGNATURE_DATA *srv_sig_ptr; - struct PAC_SIGNATURE_DATA kdc_sig; - struct PAC_SIGNATURE_DATA *kdc_sig_ptr; - struct PAC_LOGON_INFO *logon_info = NULL; - struct PAC_DATA pac_data; -#ifdef KRB5_DO_VERIFY_PAC - DATA_BLOB tmp_blob = data_blob(NULL, 0); -#endif - int i; - - status = ndr_pull_struct_blob(&blob, mem_ctx, &pac_data, - (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(0,("can't parse the PAC\n")); - return status; - } - NDR_PRINT_DEBUG(PAC_DATA, &pac_data); - - if (pac_data.num_buffers < 3) { - /* we need logon_ingo, service_key and kdc_key */ - DEBUG(0,("less than 3 PAC buffers\n")); - return NT_STATUS_FOOBAR; - } - - for (i=0; i < pac_data.num_buffers; i++) { - switch (pac_data.buffers[i].type) { - case PAC_TYPE_LOGON_INFO: - if (!pac_data.buffers[i].info) { - break; - } - logon_info = &pac_data.buffers[i].info->logon_info; - break; - case PAC_TYPE_SRV_CHECKSUM: - if (!pac_data.buffers[i].info) { - break; - } - srv_sig_ptr = &pac_data.buffers[i].info->srv_cksum; - srv_sig = pac_data.buffers[i].info->srv_cksum; - break; - case PAC_TYPE_KDC_CHECKSUM: - if (!pac_data.buffers[i].info) { - break; - } - kdc_sig_ptr = &pac_data.buffers[i].info->kdc_cksum; - kdc_sig = pac_data.buffers[i].info->kdc_cksum; - break; - case PAC_TYPE_UNKNOWN_10: - break; - default: - break; - } - } - - if (!logon_info) { - DEBUG(0,("PAC no logon_info\n")); - return NT_STATUS_FOOBAR; - } - - if (!srv_sig_ptr) { - DEBUG(0,("PAC no srv_key\n")); - return NT_STATUS_FOOBAR; - } - - if (!kdc_sig_ptr) { - DEBUG(0,("PAC no kdc_key\n")); - return NT_STATUS_FOOBAR; - } -#ifdef KRB5_DO_VERIFY_PAC - /* clear the kdc_key */ -/* memset((void *)kdc_sig_ptr , '\0', sizeof(*kdc_sig_ptr));*/ - - status = ndr_push_struct_blob(&tmp_blob, mem_ctx, &pac_data, - (ndr_push_flags_fn_t)ndr_push_PAC_DATA); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - status = ndr_pull_struct_blob(&tmp_blob, mem_ctx, &pac_data, - (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(0,("can't parse the PAC\n")); - return status; - } - /*NDR_PRINT_DEBUG(PAC_DATA, &pac_data);*/ - - /* verify by kdc_key */ - status = gensec_krb5_pac_checksum(tmp_blob, &kdc_sig, gensec_krb5_state, 0); - - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - /* clear the service_key */ -/* memset((void *)srv_sig_ptr , '\0', sizeof(*srv_sig_ptr));*/ - - status = ndr_push_struct_blob(&tmp_blob, mem_ctx, &pac_data, - (ndr_push_flags_fn_t)ndr_push_PAC_DATA); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - status = ndr_pull_struct_blob(&tmp_blob, mem_ctx, &pac_data, - (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(0,("can't parse the PAC\n")); - return status; - } - NDR_PRINT_DEBUG(PAC_DATA, &pac_data); - - /* verify by servie_key */ - status = gensec_krb5_pac_checksum(tmp_blob, &srv_sig, gensec_krb5_state, 0); - - if (!NT_STATUS_IS_OK(status)) { - return status; - } -#endif - DEBUG(0,("account_name: %s [%s]\n",logon_info->info3.base.account_name.string, logon_info->info3.base.full_name.string)); - *logon_info_out = logon_info; - - return status; -} - -static int gensec_krb5_destory(void *ptr) -{ - struct gensec_krb5_state *gensec_krb5_state = ptr; - - if (gensec_krb5_state->ticket.length) { - kerberos_free_data_contents(gensec_krb5_state->context, &gensec_krb5_state->ticket); - } - if (gensec_krb5_state->ccache) { - /* current heimdal - 0.6.3, which we need anyway, fixes segfaults here */ - krb5_cc_close(gensec_krb5_state->context, gensec_krb5_state->ccache); - } - - krb5_free_keyblock_contents(gensec_krb5_state->context, - &gensec_krb5_state->keyblock); - - if (gensec_krb5_state->auth_context) { - krb5_auth_con_free(gensec_krb5_state->context, - gensec_krb5_state->auth_context); - } - - if (gensec_krb5_state->context) { - krb5_free_context(gensec_krb5_state->context); - } - return 0; -} - -static NTSTATUS gensec_krb5_start(struct gensec_security *gensec_security) -{ - struct gensec_krb5_state *gensec_krb5_state; - krb5_error_code ret = 0; - - gensec_krb5_state = talloc(gensec_security, struct gensec_krb5_state); - if (!gensec_krb5_state) { - return NT_STATUS_NO_MEMORY; - } - - gensec_security->private_data = gensec_krb5_state; - - initialize_krb5_error_table(); - gensec_krb5_state->context = NULL; - gensec_krb5_state->auth_context = NULL; - gensec_krb5_state->ccache = NULL; - ZERO_STRUCT(gensec_krb5_state->ticket); - ZERO_STRUCT(gensec_krb5_state->keyblock); - gensec_krb5_state->session_key = data_blob(NULL, 0); - gensec_krb5_state->pac = data_blob(NULL, 0); - - talloc_set_destructor(gensec_krb5_state, gensec_krb5_destory); - - ret = krb5_init_context(&gensec_krb5_state->context); - if (ret) { - DEBUG(1,("gensec_krb5_start: krb5_init_context failed (%s)\n", error_message(ret))); - return NT_STATUS_INTERNAL_ERROR; - } - - if (lp_realm() && *lp_realm()) { - ret = krb5_set_default_realm(gensec_krb5_state->context, lp_realm()); - if (ret) { - DEBUG(1,("gensec_krb5_start: krb5_set_default_realm failed (%s)\n", error_message(ret))); - return NT_STATUS_INTERNAL_ERROR; - } - } - - ret = krb5_auth_con_init(gensec_krb5_state->context, &gensec_krb5_state->auth_context); - if (ret) { - DEBUG(1,("gensec_krb5_start: krb5_auth_con_init failed (%s)\n", error_message(ret))); - return NT_STATUS_INTERNAL_ERROR; - } - - return NT_STATUS_OK; -} - -static NTSTATUS gensec_krb5_server_start(struct gensec_security *gensec_security) -{ - NTSTATUS nt_status; - struct gensec_krb5_state *gensec_krb5_state; - - nt_status = gensec_krb5_start(gensec_security); - if (!NT_STATUS_IS_OK(nt_status)) { - return nt_status; - } - - gensec_krb5_state = gensec_security->private_data; - gensec_krb5_state->state_position = GENSEC_KRB5_SERVER_START; - - return NT_STATUS_OK; -} - -static NTSTATUS gensec_krb5_client_start(struct gensec_security *gensec_security) -{ - struct gensec_krb5_state *gensec_krb5_state; - krb5_error_code ret; - NTSTATUS nt_status; - const char *hostname = gensec_get_target_hostname(gensec_security); - if (!hostname) { - DEBUG(1, ("Could not determine hostname for target computer, cannot use kerberos\n")); - return NT_STATUS_ACCESS_DENIED; - } - - nt_status = gensec_krb5_start(gensec_security); - if (!NT_STATUS_IS_OK(nt_status)) { - return nt_status; - } - - gensec_krb5_state = gensec_security->private_data; - gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_START; - - /* TODO: This is effecivly a static/global variable... - - TODO: If the user set a username, we should use an in-memory CCACHE (see below) - */ - ret = krb5_cc_default(gensec_krb5_state->context, &gensec_krb5_state->ccache); - if (ret) { - DEBUG(1,("krb5_cc_default failed (%s)\n", - error_message(ret))); - return NT_STATUS_INTERNAL_ERROR; - } - - while (1) { - { - krb5_data in_data; - in_data.length = 0; - - ret = krb5_mk_req(gensec_krb5_state->context, - &gensec_krb5_state->auth_context, - AP_OPTS_USE_SUBKEY | AP_OPTS_MUTUAL_REQUIRED, - gensec_get_target_service(gensec_security), - hostname, - &in_data, gensec_krb5_state->ccache, - &gensec_krb5_state->ticket); - - } - switch (ret) { - case 0: - return NT_STATUS_OK; - case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN: - DEBUG(3, ("Server [%s] is not registered with our KDC: %s\n", - hostname, error_message(ret))); - return NT_STATUS_ACCESS_DENIED; - case KRB5KDC_ERR_PREAUTH_FAILED: - case KRB5KRB_AP_ERR_TKT_EXPIRED: - case KRB5_CC_END: - /* Too much clock skew - we will need to kinit to re-skew the clock */ - case KRB5KRB_AP_ERR_SKEW: - case KRB5_KDCREP_SKEW: - { - DEBUG(3, ("kerberos (mk_req) failed: %s\n", - error_message(ret))); - /* fall down to remaining code */ - } - - - /* just don't print a message for these really ordinary messages */ - case KRB5_FCC_NOFILE: - case KRB5_CC_NOTFOUND: - case ENOENT: - - { - const char *password; - char *ccache_string; - time_t kdc_time = 0; - password = cli_credentials_get_password(gensec_security->credentials); - if (!NT_STATUS_IS_OK(nt_status)) { - return nt_status; - } - - /* this string should be unique */ - ccache_string = talloc_asprintf(gensec_krb5_state, "MEMORY:%s:%s:%s", - cli_credentials_get_principal(gensec_security->credentials, gensec_krb5_state), - gensec_get_target_hostname(gensec_security), - generate_random_str(gensec_krb5_state, 16)); - - ret = krb5_cc_resolve(gensec_krb5_state->context, ccache_string, &gensec_krb5_state->ccache); - if (ret) { - DEBUG(1,("failed to generate a new krb5 keytab (%s): %s\n", - ccache_string, - error_message(ret))); - return NT_STATUS_INTERNAL_ERROR; - } - - ret = kerberos_kinit_password_cc(gensec_krb5_state->context, gensec_krb5_state->ccache, - cli_credentials_get_principal(gensec_security->credentials, gensec_krb5_state), - password, NULL, &kdc_time); - - /* cope with ticket being in the future due to clock skew */ - if ((unsigned)kdc_time > time(NULL)) { - time_t t = time(NULL); - int time_offset =(unsigned)kdc_time-t; - DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset)); - krb5_set_real_time(gensec_krb5_state->context, t + time_offset + 1, 0); - break; - } - - if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) { - DEBUG(1,("kinit for %s failed (%s)\n", - cli_credentials_get_principal(gensec_security->credentials, gensec_krb5_state), - error_message(ret))); - return NT_STATUS_TIME_DIFFERENCE_AT_DC; - } - if (ret) { - DEBUG(1,("kinit for %s failed (%s)\n", - cli_credentials_get_principal(gensec_security->credentials, gensec_krb5_state), - error_message(ret))); - return NT_STATUS_WRONG_PASSWORD; - } - break; - } - default: - DEBUG(0, ("kerberos: %s\n", - error_message(ret))); - return NT_STATUS_UNSUCCESSFUL; - } - } -} - - -/** - * Next state function for the Krb5 GENSEC mechanism - * - * @param gensec_krb5_state KRB5 State - * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on - * @param in The request, as a DATA_BLOB - * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx - * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent, - * or NT_STATUS_OK if the user is authenticated. - */ - -static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security, - TALLOC_CTX *out_mem_ctx, - const DATA_BLOB in, DATA_BLOB *out) -{ - struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data; - krb5_error_code ret = 0; - DATA_BLOB pac; - NTSTATUS nt_status; - - switch (gensec_krb5_state->state_position) { - case GENSEC_KRB5_CLIENT_START: - { - if (ret) { - DEBUG(1,("ads_krb5_mk_req (request ticket) failed (%s)\n", - error_message(ret))); - nt_status = NT_STATUS_LOGON_FAILURE; - } else { - DATA_BLOB unwrapped_out; - -#ifndef GENSEC_SEND_UNWRAPPED_KRB5 /* This should be a switch for the torture code to set */ - unwrapped_out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->ticket.data, gensec_krb5_state->ticket.length); - - /* wrap that up in a nice GSS-API wrapping */ - *out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REQ); -#else - *out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->ticket.data, gensec_krb5_state->ticket.length); -#endif - gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_MUTUAL_AUTH; - nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED; - } - - return nt_status; - } - - case GENSEC_KRB5_CLIENT_MUTUAL_AUTH: - { - krb5_data inbuf; - krb5_ap_rep_enc_part *repl = NULL; - uint8_t tok_id[2]; - DATA_BLOB unwrapped_in; - - if (!gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) { - DEBUG(1,("gensec_gssapi_parse_krb5_wrap(mutual authentication) failed to parse\n")); - dump_data_pw("Mutual authentication message:\n", in.data, in.length); - return NT_STATUS_INVALID_PARAMETER; - } - /* TODO: check the tok_id */ - - inbuf.data = unwrapped_in.data; - inbuf.length = unwrapped_in.length; - ret = krb5_rd_rep(gensec_krb5_state->context, - gensec_krb5_state->auth_context, - &inbuf, &repl); - if (ret) { - DEBUG(1,("krb5_rd_rep (mutual authentication) failed (%s)\n", - error_message(ret))); - dump_data_pw("Mutual authentication message:\n", inbuf.data, inbuf.length); - nt_status = NT_STATUS_ACCESS_DENIED; - } else { - *out = data_blob(NULL, 0); - nt_status = NT_STATUS_OK; - gensec_krb5_state->state_position = GENSEC_KRB5_DONE; - } - if (repl) { - krb5_free_ap_rep_enc_part(gensec_krb5_state->context, repl); - } - return nt_status; - } - - case GENSEC_KRB5_SERVER_START: - { - char *principal; - DATA_BLOB unwrapped_in; - DATA_BLOB unwrapped_out = data_blob(NULL, 0); - uint8_t tok_id[2]; - - if (!in.data) { - *out = unwrapped_out; - return NT_STATUS_MORE_PROCESSING_REQUIRED; - } - - /* Parse the GSSAPI wrapping, if it's there... (win2k3 allows it to be omited) */ - if (!gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) { - nt_status = ads_verify_ticket(out_mem_ctx, - gensec_krb5_state->context, - gensec_krb5_state->auth_context, - lp_realm(), - gensec_get_target_service(gensec_security), &in, - &principal, &pac, &unwrapped_out, - &gensec_krb5_state->keyblock); - } else { - /* TODO: check the tok_id */ - nt_status = ads_verify_ticket(out_mem_ctx, - gensec_krb5_state->context, - gensec_krb5_state->auth_context, - lp_realm(), - gensec_get_target_service(gensec_security), - &unwrapped_in, - &principal, &pac, &unwrapped_out, - &gensec_krb5_state->keyblock); - } - - if (!NT_STATUS_IS_OK(nt_status)) { - return nt_status; - } - - if (pac.data) { - gensec_krb5_state->pac = data_blob_talloc_reference(gensec_krb5_state, &pac); - } - - if (NT_STATUS_IS_OK(nt_status)) { - gensec_krb5_state->state_position = GENSEC_KRB5_DONE; - /* wrap that up in a nice GSS-API wrapping */ -#ifndef GENSEC_SEND_UNWRAPPED_KRB5 - *out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REP); -#else - *out = unwrapped_out; -#endif - gensec_krb5_state->peer_principal = talloc_steal(gensec_krb5_state, principal); - } - return nt_status; - } - case GENSEC_KRB5_DONE: - return NT_STATUS_OK; - } - - return NT_STATUS_INVALID_PARAMETER; -} - -static NTSTATUS gensec_krb5_session_key(struct gensec_security *gensec_security, - DATA_BLOB *session_key) -{ - struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data; - krb5_context context = gensec_krb5_state->context; - krb5_auth_context auth_context = gensec_krb5_state->auth_context; - krb5_keyblock *skey; - krb5_error_code err; - - if (gensec_krb5_state->session_key.data) { - *session_key = gensec_krb5_state->session_key; - return NT_STATUS_OK; - } - - switch (gensec_security->gensec_role) { - case GENSEC_CLIENT: - err = krb5_auth_con_getlocalsubkey(context, auth_context, &skey); - break; - case GENSEC_SERVER: - err = krb5_auth_con_getremotesubkey(context, auth_context, &skey); - break; - } - if (err == 0 && skey != NULL) { - DEBUG(10, ("Got KRB5 session key of length %d\n", KRB5_KEY_LENGTH(skey))); - gensec_krb5_state->session_key = data_blob_talloc(gensec_krb5_state, - KRB5_KEY_DATA(skey), KRB5_KEY_LENGTH(skey)); - *session_key = gensec_krb5_state->session_key; - dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length); - - krb5_free_keyblock(context, skey); - return NT_STATUS_OK; - } else { - DEBUG(10, ("KRB5 error getting session key %d\n", err)); - return NT_STATUS_NO_USER_SESSION_KEY; - } -} - -static NTSTATUS gensec_krb5_session_info(struct gensec_security *gensec_security, - struct auth_session_info **_session_info) -{ - NTSTATUS nt_status; - struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data; - struct auth_serversupplied_info *server_info = NULL; - struct auth_session_info *session_info = NULL; - struct PAC_LOGON_INFO *logon_info; - char *p; - char *principal; - const char *account_name; - const char *realm; - - principal = talloc_strdup(gensec_krb5_state, gensec_krb5_state->peer_principal); - NT_STATUS_HAVE_NO_MEMORY(principal); - - p = strchr(principal, '@'); - if (p) { - *p = '\0'; - p++; - realm = p; - } else { - realm = lp_realm(); - } - account_name = principal; - - /* decode and verify the pac */ - nt_status = gensec_krb5_decode_pac(gensec_krb5_state, &logon_info, gensec_krb5_state->pac, - gensec_krb5_state); - - /* IF we have the PAC - otherwise we need to get this - * data from elsewere - local ldb, or (TODO) lookup of some - * kind... - * - * when heimdal can generate the PAC, we should fail if there's - * no PAC present - */ - - if (NT_STATUS_IS_OK(nt_status)) { - union netr_Validation validation; - validation.sam3 = &logon_info->info3; - nt_status = make_server_info_netlogon_validation(gensec_krb5_state, - account_name, - 3, &validation, - &server_info); - talloc_free(principal); - NT_STATUS_NOT_OK_RETURN(nt_status); - } else { - DATA_BLOB user_sess_key = data_blob(NULL, 0); - DATA_BLOB lm_sess_key = data_blob(NULL, 0); - /* TODO: should we pass the krb5 session key in here? */ - nt_status = sam_get_server_info(gensec_krb5_state, account_name, realm, - user_sess_key, lm_sess_key, - &server_info); - talloc_free(principal); - NT_STATUS_NOT_OK_RETURN(nt_status); - } - - /* references the server_info into the session_info */ - nt_status = auth_generate_session_info(gensec_krb5_state, server_info, &session_info); - talloc_free(server_info); - NT_STATUS_NOT_OK_RETURN(nt_status); - - nt_status = gensec_krb5_session_key(gensec_security, &session_info->session_key); - NT_STATUS_NOT_OK_RETURN(nt_status); - - *_session_info = session_info; - - return NT_STATUS_OK; -} - -static BOOL gensec_krb5_have_feature(struct gensec_security *gensec_security, - uint32_t feature) -{ - if (feature & GENSEC_FEATURE_SESSION_KEY) { - return True; - } - - return False; -} - - -static const struct gensec_security_ops gensec_krb5_security_ops = { - .name = "krb5", - .auth_type = DCERPC_AUTH_TYPE_KRB5, - .oid = GENSEC_OID_KERBEROS5, - .client_start = gensec_krb5_client_start, - .server_start = gensec_krb5_server_start, - .update = gensec_krb5_update, - .session_key = gensec_krb5_session_key, - .session_info = gensec_krb5_session_info, - .have_feature = gensec_krb5_have_feature, - .enabled = False -}; - -static const struct gensec_security_ops gensec_ms_krb5_security_ops = { - .name = "ms_krb5", - .auth_type = DCERPC_AUTH_TYPE_KRB5, - .oid = GENSEC_OID_KERBEROS5_OLD, - .client_start = gensec_krb5_client_start, - .server_start = gensec_krb5_server_start, - .update = gensec_krb5_update, - .session_key = gensec_krb5_session_key, - .session_info = gensec_krb5_session_info, - .have_feature = gensec_krb5_have_feature, - .enabled = False -}; - - -NTSTATUS gensec_krb5_init(void) -{ - NTSTATUS ret; - - ret = gensec_register(&gensec_krb5_security_ops); - if (!NT_STATUS_IS_OK(ret)) { - DEBUG(0,("Failed to register '%s' gensec backend!\n", - gensec_krb5_security_ops.name)); - return ret; - } - - ret = gensec_register(&gensec_ms_krb5_security_ops); - if (!NT_STATUS_IS_OK(ret)) { - DEBUG(0,("Failed to register '%s' gensec backend!\n", - gensec_krb5_security_ops.name)); - return ret; - } - - return ret; -} diff --git a/source4/libcli/auth/gensec_ntlmssp.c b/source4/libcli/auth/gensec_ntlmssp.c deleted file mode 100644 index 5955904886..0000000000 --- a/source4/libcli/auth/gensec_ntlmssp.c +++ /dev/null @@ -1,514 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - dcerpc authentication operations - - Copyright (C) Andrew Tridgell 2003 - Copyright (C) Andrew Bartlett 2004 - Copyright (C) Stefan Metzmacher 2005 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include "includes.h" -#include "auth/auth.h" - -struct gensec_ntlmssp_state { - struct auth_context *auth_context; - struct auth_serversupplied_info *server_info; - struct ntlmssp_state *ntlmssp_state; - uint32_t have_features; -}; - - -/** - * Return the challenge as determined by the authentication subsystem - * @return an 8 byte random challenge - */ - -static const uint8_t *auth_ntlmssp_get_challenge(const struct ntlmssp_state *ntlmssp_state) -{ - struct gensec_ntlmssp_state *gensec_ntlmssp_state = ntlmssp_state->auth_context; - NTSTATUS status; - const uint8_t *chal; - - status = auth_get_challenge(gensec_ntlmssp_state->auth_context, &chal); - if (!NT_STATUS_IS_OK(status)) { - return NULL; - } - - return chal; -} - -/** - * Some authentication methods 'fix' the challenge, so we may not be able to set it - * - * @return If the effective challenge used by the auth subsystem may be modified - */ -static BOOL auth_ntlmssp_may_set_challenge(const struct ntlmssp_state *ntlmssp_state) -{ - struct gensec_ntlmssp_state *gensec_ntlmssp_state = ntlmssp_state->auth_context; - - return auth_challenge_may_be_modified(gensec_ntlmssp_state->auth_context); -} - -/** - * NTLM2 authentication modifies the effective challenge, - * @param challenge The new challenge value - */ -static NTSTATUS auth_ntlmssp_set_challenge(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *challenge) -{ - NTSTATUS nt_status; - struct gensec_ntlmssp_state *gensec_ntlmssp_state = ntlmssp_state->auth_context; - struct auth_context *auth_context = gensec_ntlmssp_state->auth_context; - const uint8_t *chal; - - if (challenge->length != 8) { - return NT_STATUS_INVALID_PARAMETER; - } - - chal = challenge->data; - - nt_status = auth_context_set_challenge(auth_context, chal, "NTLMSSP callback (NTLM2)"); - - return nt_status; -} - -/** - * Check the password on an NTLMSSP login. - * - * Return the session keys used on the connection. - */ - -static NTSTATUS auth_ntlmssp_check_password(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *user_session_key, DATA_BLOB *lm_session_key) -{ - struct gensec_ntlmssp_state *gensec_ntlmssp_state = ntlmssp_state->auth_context; - struct auth_usersupplied_info *user_info = NULL; - NTSTATUS nt_status; - - nt_status = make_user_info_map(ntlmssp_state, - gensec_ntlmssp_state->ntlmssp_state->user, - gensec_ntlmssp_state->ntlmssp_state->domain, - gensec_ntlmssp_state->ntlmssp_state->workstation, - gensec_ntlmssp_state->ntlmssp_state->lm_resp.data ? &gensec_ntlmssp_state->ntlmssp_state->lm_resp : NULL, - gensec_ntlmssp_state->ntlmssp_state->nt_resp.data ? &gensec_ntlmssp_state->ntlmssp_state->nt_resp : NULL, - NULL, NULL, NULL, True, - &user_info); - NT_STATUS_NOT_OK_RETURN(nt_status); - - nt_status = auth_check_password(gensec_ntlmssp_state->auth_context, gensec_ntlmssp_state, - user_info, &gensec_ntlmssp_state->server_info); - talloc_free(user_info); - NT_STATUS_NOT_OK_RETURN(nt_status); - - if (gensec_ntlmssp_state->server_info->user_session_key.length) { - DEBUG(10, ("Got NT session key of length %u\n", gensec_ntlmssp_state->server_info->user_session_key.length)); - *user_session_key = data_blob_talloc(ntlmssp_state, - gensec_ntlmssp_state->server_info->user_session_key.data, - gensec_ntlmssp_state->server_info->user_session_key.length); - } - if (gensec_ntlmssp_state->server_info->lm_session_key.length) { - DEBUG(10, ("Got LM session key of length %u\n", gensec_ntlmssp_state->server_info->lm_session_key.length)); - *lm_session_key = data_blob_talloc(ntlmssp_state, - gensec_ntlmssp_state->server_info->lm_session_key.data, - gensec_ntlmssp_state->server_info->lm_session_key.length); - } - return nt_status; -} - -static int gensec_ntlmssp_destroy(void *ptr) -{ - struct gensec_ntlmssp_state *gensec_ntlmssp_state = ptr; - - if (gensec_ntlmssp_state->ntlmssp_state) { - ntlmssp_end(&gensec_ntlmssp_state->ntlmssp_state); - } - - return 0; -} - -static NTSTATUS gensec_ntlmssp_start(struct gensec_security *gensec_security) -{ - struct gensec_ntlmssp_state *gensec_ntlmssp_state; - - gensec_ntlmssp_state = talloc(gensec_security, struct gensec_ntlmssp_state); - if (!gensec_ntlmssp_state) { - return NT_STATUS_NO_MEMORY; - } - - gensec_ntlmssp_state->ntlmssp_state = NULL; - gensec_ntlmssp_state->auth_context = NULL; - gensec_ntlmssp_state->server_info = NULL; - gensec_ntlmssp_state->have_features = 0; - - talloc_set_destructor(gensec_ntlmssp_state, gensec_ntlmssp_destroy); - - gensec_security->private_data = gensec_ntlmssp_state; - return NT_STATUS_OK; -} - -static NTSTATUS gensec_ntlmssp_server_start(struct gensec_security *gensec_security) -{ - NTSTATUS nt_status; - struct ntlmssp_state *ntlmssp_state; - struct gensec_ntlmssp_state *gensec_ntlmssp_state; - - nt_status = gensec_ntlmssp_start(gensec_security); - NT_STATUS_NOT_OK_RETURN(nt_status); - - gensec_ntlmssp_state = gensec_security->private_data; - - nt_status = ntlmssp_server_start(gensec_ntlmssp_state, &gensec_ntlmssp_state->ntlmssp_state); - NT_STATUS_NOT_OK_RETURN(nt_status); - - if (gensec_security->want_features & GENSEC_FEATURE_SIGN) { - gensec_ntlmssp_state->ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN; - } - if (gensec_security->want_features & GENSEC_FEATURE_SEAL) { - gensec_ntlmssp_state->ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SEAL; - } - - nt_status = auth_context_create(gensec_ntlmssp_state, lp_auth_methods(), &gensec_ntlmssp_state->auth_context); - NT_STATUS_NOT_OK_RETURN(nt_status); - - ntlmssp_state = gensec_ntlmssp_state->ntlmssp_state; - ntlmssp_state->auth_context = gensec_ntlmssp_state; - ntlmssp_state->get_challenge = auth_ntlmssp_get_challenge; - ntlmssp_state->may_set_challenge = auth_ntlmssp_may_set_challenge; - ntlmssp_state->set_challenge = auth_ntlmssp_set_challenge; - ntlmssp_state->check_password = auth_ntlmssp_check_password; - ntlmssp_state->server_role = lp_server_role(); - - return NT_STATUS_OK; -} - -static NTSTATUS gensec_ntlmssp_client_start(struct gensec_security *gensec_security) -{ - struct gensec_ntlmssp_state *gensec_ntlmssp_state; - const char *password = NULL; - NTSTATUS nt_status; - - nt_status = gensec_ntlmssp_start(gensec_security); - NT_STATUS_NOT_OK_RETURN(nt_status); - - gensec_ntlmssp_state = gensec_security->private_data; - nt_status = ntlmssp_client_start(gensec_ntlmssp_state, - &gensec_ntlmssp_state->ntlmssp_state); - NT_STATUS_NOT_OK_RETURN(nt_status); - - if (gensec_security->want_features & GENSEC_FEATURE_SESSION_KEY) { - /* - * We need to set this to allow a later SetPassword - * via the SAMR pipe to succeed. Strange.... We could - * also add NTLMSSP_NEGOTIATE_SEAL here. JRA. - * - * Without this, Windows will not create the master key - * that it thinks is only used for NTLMSSP signing and - * sealing. (It is actually pulled out and used directly) - */ - gensec_ntlmssp_state->ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN; - } - if (gensec_security->want_features & GENSEC_FEATURE_SIGN) { - gensec_ntlmssp_state->ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN; - } - if (gensec_security->want_features & GENSEC_FEATURE_SEAL) { - gensec_ntlmssp_state->ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SEAL; - } - - nt_status = ntlmssp_set_domain(gensec_ntlmssp_state->ntlmssp_state, - cli_credentials_get_domain(gensec_security->credentials)); - NT_STATUS_NOT_OK_RETURN(nt_status); - - nt_status = ntlmssp_set_username(gensec_ntlmssp_state->ntlmssp_state, - cli_credentials_get_username(gensec_security->credentials)); - NT_STATUS_NOT_OK_RETURN(nt_status); - - password = cli_credentials_get_password(gensec_security->credentials); - - nt_status = ntlmssp_set_password(gensec_ntlmssp_state->ntlmssp_state, password); - NT_STATUS_NOT_OK_RETURN(nt_status); - - nt_status = ntlmssp_set_workstation(gensec_ntlmssp_state->ntlmssp_state, - cli_credentials_get_workstation(gensec_security->credentials)); - - gensec_security->private_data = gensec_ntlmssp_state; - - return NT_STATUS_OK; -} - -/* - wrappers for the ntlmssp_*() functions -*/ -static NTSTATUS gensec_ntlmssp_unseal_packet(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - uint8_t *data, size_t length, - const uint8_t *whole_pdu, size_t pdu_length, - DATA_BLOB *sig) -{ - struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; - - return ntlmssp_unseal_packet(gensec_ntlmssp_state->ntlmssp_state, mem_ctx, data, length, whole_pdu, pdu_length, sig); -} - -static NTSTATUS gensec_ntlmssp_check_packet(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - const uint8_t *data, size_t length, - const uint8_t *whole_pdu, size_t pdu_length, - const DATA_BLOB *sig) -{ - struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; - - return ntlmssp_check_packet(gensec_ntlmssp_state->ntlmssp_state, mem_ctx, data, length, whole_pdu, pdu_length, sig); -} - -static NTSTATUS gensec_ntlmssp_seal_packet(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - uint8_t *data, size_t length, - const uint8_t *whole_pdu, size_t pdu_length, - DATA_BLOB *sig) -{ - struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; - - return ntlmssp_seal_packet(gensec_ntlmssp_state->ntlmssp_state, mem_ctx, data, length, whole_pdu, pdu_length, sig); -} - -static NTSTATUS gensec_ntlmssp_sign_packet(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - const uint8_t *data, size_t length, - const uint8_t *whole_pdu, size_t pdu_length, - DATA_BLOB *sig) -{ - struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; - - return ntlmssp_sign_packet(gensec_ntlmssp_state->ntlmssp_state, mem_ctx, data, length, whole_pdu, pdu_length, sig); -} - -static size_t gensec_ntlmssp_sig_size(struct gensec_security *gensec_security) -{ - return NTLMSSP_SIG_SIZE; -} - -static NTSTATUS gensec_ntlmssp_wrap(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - const DATA_BLOB *in, - DATA_BLOB *out) -{ - struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; - DATA_BLOB sig; - NTSTATUS nt_status; - - if (gensec_ntlmssp_state->ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SEAL) { - - *out = data_blob_talloc(mem_ctx, NULL, in->length + NTLMSSP_SIG_SIZE); - memcpy(out->data + NTLMSSP_SIG_SIZE, in->data, in->length); - - nt_status = ntlmssp_seal_packet(gensec_ntlmssp_state->ntlmssp_state, mem_ctx, - out->data + NTLMSSP_SIG_SIZE, - out->length - NTLMSSP_SIG_SIZE, - out->data + NTLMSSP_SIG_SIZE, - out->length - NTLMSSP_SIG_SIZE, - &sig); - - if (NT_STATUS_IS_OK(nt_status)) { - memcpy(out->data, sig.data, NTLMSSP_SIG_SIZE); - } - return nt_status; - - } else if ((gensec_ntlmssp_state->ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SIGN) - || (gensec_ntlmssp_state->ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN)) { - - *out = data_blob_talloc(mem_ctx, NULL, in->length + NTLMSSP_SIG_SIZE); - memcpy(out->data + NTLMSSP_SIG_SIZE, in->data, in->length); - - nt_status = ntlmssp_sign_packet(gensec_ntlmssp_state->ntlmssp_state, mem_ctx, - out->data + NTLMSSP_SIG_SIZE, - out->length - NTLMSSP_SIG_SIZE, - out->data + NTLMSSP_SIG_SIZE, - out->length - NTLMSSP_SIG_SIZE, - &sig); - - if (NT_STATUS_IS_OK(nt_status)) { - memcpy(out->data, sig.data, NTLMSSP_SIG_SIZE); - } - return nt_status; - - } else { - *out = *in; - return NT_STATUS_OK; - } -} - - -static NTSTATUS gensec_ntlmssp_unwrap(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - const DATA_BLOB *in, - DATA_BLOB *out) -{ - struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; - DATA_BLOB sig; - - if (gensec_ntlmssp_state->ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SEAL) { - if (in->length < NTLMSSP_SIG_SIZE) { - return NT_STATUS_INVALID_PARAMETER; - } - sig.data = in->data; - sig.length = NTLMSSP_SIG_SIZE; - - *out = data_blob_talloc(mem_ctx, in->data + NTLMSSP_SIG_SIZE, in->length - NTLMSSP_SIG_SIZE); - - return ntlmssp_unseal_packet(gensec_ntlmssp_state->ntlmssp_state, mem_ctx, - out->data, out->length, - out->data, out->length, - &sig); - - } else if ((gensec_ntlmssp_state->ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SIGN) - || (gensec_ntlmssp_state->ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN)) { - if (in->length < NTLMSSP_SIG_SIZE) { - return NT_STATUS_INVALID_PARAMETER; - } - sig.data = in->data; - sig.length = NTLMSSP_SIG_SIZE; - - *out = data_blob_talloc(mem_ctx, in->data + NTLMSSP_SIG_SIZE, in->length - NTLMSSP_SIG_SIZE); - - return ntlmssp_check_packet(gensec_ntlmssp_state->ntlmssp_state, mem_ctx, - out->data, out->length, - out->data, out->length, - &sig); - } else { - *out = *in; - return NT_STATUS_OK; - } -} - -static NTSTATUS gensec_ntlmssp_session_key(struct gensec_security *gensec_security, - DATA_BLOB *session_key) -{ - struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; - - return ntlmssp_session_key(gensec_ntlmssp_state->ntlmssp_state, session_key); -} - -/** - * Next state function for the wrapped NTLMSSP state machine - * - * @param gensec_security GENSEC state, initialised to NTLMSSP - * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on - * @param in The request, as a DATA_BLOB - * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx - * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent, - * or NT_STATUS_OK if the user is authenticated. - */ - -static NTSTATUS gensec_ntlmssp_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, - const DATA_BLOB in, DATA_BLOB *out) -{ - struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; - NTSTATUS status; - - status = ntlmssp_update(gensec_ntlmssp_state->ntlmssp_state, out_mem_ctx, in, out); - - if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(status)) { - return status; - } - - gensec_ntlmssp_state->have_features = 0; - - if (gensec_ntlmssp_state->ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SIGN) { - gensec_ntlmssp_state->have_features |= GENSEC_FEATURE_SIGN; - } - - if (gensec_ntlmssp_state->ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SEAL) { - gensec_ntlmssp_state->have_features |= GENSEC_FEATURE_SEAL; - } - - if (gensec_ntlmssp_state->ntlmssp_state->session_key.data) { - gensec_ntlmssp_state->have_features |= GENSEC_FEATURE_SESSION_KEY; - } - - return status; -} - -/** - * Return the credentials of a logged on user, including session keys - * etc. - * - * Only valid after a successful authentication - * - * May only be called once per authentication. - * - */ - -static NTSTATUS gensec_ntlmssp_session_info(struct gensec_security *gensec_security, - struct auth_session_info **session_info) -{ - NTSTATUS nt_status; - struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; - - nt_status = auth_generate_session_info(gensec_ntlmssp_state, gensec_ntlmssp_state->server_info, session_info); - NT_STATUS_NOT_OK_RETURN(nt_status); - - (*session_info)->session_key = data_blob_talloc(*session_info, - gensec_ntlmssp_state->ntlmssp_state->session_key.data, - gensec_ntlmssp_state->ntlmssp_state->session_key.length); - - return NT_STATUS_OK; -} - -static BOOL gensec_ntlmssp_have_feature(struct gensec_security *gensec_security, - uint32_t feature) -{ - struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; - if (gensec_ntlmssp_state->have_features & feature) { - return True; - } - - return False; -} - -static const struct gensec_security_ops gensec_ntlmssp_security_ops = { - .name = "ntlmssp", - .sasl_name = "NTLM", - .auth_type = DCERPC_AUTH_TYPE_NTLMSSP, - .oid = GENSEC_OID_NTLMSSP, - .client_start = gensec_ntlmssp_client_start, - .server_start = gensec_ntlmssp_server_start, - .update = gensec_ntlmssp_update, - .sig_size = gensec_ntlmssp_sig_size, - .sign_packet = gensec_ntlmssp_sign_packet, - .check_packet = gensec_ntlmssp_check_packet, - .seal_packet = gensec_ntlmssp_seal_packet, - .unseal_packet = gensec_ntlmssp_unseal_packet, - .wrap = gensec_ntlmssp_wrap, - .unwrap = gensec_ntlmssp_unwrap, - .session_key = gensec_ntlmssp_session_key, - .session_info = gensec_ntlmssp_session_info, - .have_feature = gensec_ntlmssp_have_feature, - .enabled = True -}; - - -NTSTATUS gensec_ntlmssp_init(void) -{ - NTSTATUS ret; - ret = gensec_register(&gensec_ntlmssp_security_ops); - if (!NT_STATUS_IS_OK(ret)) { - DEBUG(0,("Failed to register '%s' gensec backend!\n", - gensec_ntlmssp_security_ops.name)); - return ret; - } - - return ret; -} diff --git a/source4/libcli/auth/gssapi_parse.c b/source4/libcli/auth/gssapi_parse.c deleted file mode 100644 index 89929c8c6d..0000000000 --- a/source4/libcli/auth/gssapi_parse.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - simple GSSAPI wrappers - - Copyright (C) Andrew Tridgell 2001 - Copyright (C) Jim McDonough 2002 - Copyright (C) Luke Howard 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" -#include "asn_1.h" -#include "system/kerberos.h" -#include "libcli/auth/gensec.h" - -/* - generate a krb5 GSS-API wrapper packet given a ticket -*/ -DATA_BLOB gensec_gssapi_gen_krb5_wrap(TALLOC_CTX *mem_ctx, const DATA_BLOB *ticket, const uint8_t tok_id[2]) -{ - struct asn1_data data; - DATA_BLOB ret = data_blob(NULL,0); - - if (!ticket->data) { - return ret; - } - - ZERO_STRUCT(data); - - asn1_push_tag(&data, ASN1_APPLICATION(0)); - asn1_write_OID(&data, GENSEC_OID_KERBEROS5); - - asn1_write(&data, tok_id, 2); - asn1_write(&data, ticket->data, ticket->length); - asn1_pop_tag(&data); - - if (data.has_error) { - DEBUG(1,("Failed to build krb5 wrapper at offset %d\n", (int)data.ofs)); - asn1_free(&data); - } - - ret = data_blob_talloc(mem_ctx, data.data, data.length); - asn1_free(&data); - - return ret; -} - -/* - parse a krb5 GSS-API wrapper packet giving a ticket -*/ -BOOL gensec_gssapi_parse_krb5_wrap(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob, DATA_BLOB *ticket, uint8_t tok_id[2]) -{ - BOOL ret; - struct asn1_data data; - int data_remaining; - - asn1_load(&data, *blob); - asn1_start_tag(&data, ASN1_APPLICATION(0)); - asn1_check_OID(&data, GENSEC_OID_KERBEROS5); - - data_remaining = asn1_tag_remaining(&data); - - if (data_remaining < 3) { - data.has_error = True; - } else { - asn1_read(&data, tok_id, 2); - data_remaining -= 2; - *ticket = data_blob_talloc(mem_ctx, NULL, data_remaining); - asn1_read(&data, ticket->data, ticket->length); - } - - asn1_end_tag(&data); - - ret = !data.has_error; - - asn1_free(&data); - - return ret; -} - - diff --git a/source4/libcli/auth/kerberos.c b/source4/libcli/auth/kerberos.c deleted file mode 100644 index 89b4108280..0000000000 --- a/source4/libcli/auth/kerberos.c +++ /dev/null @@ -1,788 +0,0 @@ -/* - Unix SMB/CIFS implementation. - kerberos utility library - Copyright (C) Andrew Tridgell 2001 - Copyright (C) Remus Koos 2001 - Copyright (C) Nalin Dahyabhai 2004. - Copyright (C) Jeremy Allison 2004. - Copyright (C) Andrew Bartlett 2004-2005 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include "includes.h" -#include "system/kerberos.h" -#include "system/time.h" -#include "libcli/auth/kerberos.h" -#include "secrets.h" -#include "pstring.h" -#include "ads.h" - -#ifdef HAVE_KRB5 - -#define LIBADS_CCACHE_NAME "MEMORY:libads" - -/* - we use a prompter to avoid a crash bug in the kerberos libs when - dealing with empty passwords - this prompter is just a string copy ... -*/ -static krb5_error_code -kerb_prompter(krb5_context ctx, void *data, - const char *name, - const char *banner, - int num_prompts, - krb5_prompt prompts[]) -{ - if (num_prompts == 0) return 0; - - memset(prompts[0].reply->data, '\0', prompts[0].reply->length); - if (prompts[0].reply->length > 0) { - if (data) { - strncpy(prompts[0].reply->data, data, prompts[0].reply->length-1); - prompts[0].reply->length = strlen(prompts[0].reply->data); - } else { - prompts[0].reply->length = 0; - } - } - return 0; -} - -/* - simulate a kinit, putting the tgt in the given credentials cache. - Orignally by remus@snapserver.com -*/ - int kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc, - const char *principal, const char *password, - time_t *expire_time, time_t *kdc_time) -{ - krb5_error_code code = 0; - krb5_principal me; - krb5_creds my_creds; - krb5_get_init_creds_opt options; - - if ((code = krb5_parse_name(ctx, principal, &me))) { - return code; - } - - krb5_get_init_creds_opt_init(&options); - - if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, password, - kerb_prompter, - NULL, 0, NULL, &options))) { - krb5_free_principal(ctx, me); - return code; - } - - if ((code = krb5_cc_initialize(ctx, cc, me))) { - krb5_free_cred_contents(ctx, &my_creds); - krb5_free_principal(ctx, me); - return code; - } - - if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) { - krb5_free_cred_contents(ctx, &my_creds); - krb5_free_principal(ctx, me); - return code; - } - - if (expire_time) { - *expire_time = (time_t) my_creds.times.endtime; - } - - if (kdc_time) { - *kdc_time = (time_t) my_creds.times.starttime; - } - - krb5_free_cred_contents(ctx, &my_creds); - krb5_free_principal(ctx, me); - - return 0; -} - -/* - simulate a kinit, putting the tgt in the given credentials cache. - If cache_name == NULL place in default cache location. - - Orignally by remus@snapserver.com -*/ -int kerberos_kinit_password(const char *principal, - const char *password, - int time_offset, - time_t *expire_time, - const char *cache_name, - time_t *kdc_time) -{ - int code; - krb5_context ctx = NULL; - krb5_ccache cc = NULL; - - if ((code = krb5_init_context(&ctx))) - return code; - - if (time_offset != 0) { - krb5_set_real_time(ctx, time(NULL) + time_offset, 0); - } - - if ((code = krb5_cc_resolve(ctx, cache_name ? - cache_name : krb5_cc_default_name(ctx), &cc))) { - krb5_free_context(ctx); - return code; - } - - code = kerberos_kinit_password_cc(ctx, cc, principal, password, expire_time, kdc_time); - - krb5_cc_close(ctx, cc); - krb5_free_context(ctx); - - return code; -} - -/* run kinit to setup our ccache */ -int ads_kinit_password(struct ads_struct *ads) -{ - char *s; - int ret; - - if (asprintf(&s, "%s@%s", ads->auth.user_name, ads->auth.realm) == -1) { - return KRB5_CC_NOMEM; - } - - if (!ads->auth.password) { - return KRB5_LIBOS_CANTREADPWD; - } - - ret = kerberos_kinit_password(s, ads->auth.password, ads->auth.time_offset, - &ads->auth.expire, NULL, NULL); - - if (ret) { - DEBUG(0,("kerberos_kinit_password %s failed: %s\n", - s, error_message(ret))); - } - free(s); - return ret; -} - -int ads_kdestroy(const char *cc_name) -{ - krb5_error_code code; - krb5_context ctx = NULL; - krb5_ccache cc = NULL; - - if ((code = krb5_init_context (&ctx))) { - DEBUG(3, ("ads_kdestroy: kdb5_init_context failed: %s\n", - error_message(code))); - return code; - } - - if (!cc_name) { - if ((code = krb5_cc_default(ctx, &cc))) { - krb5_free_context(ctx); - return code; - } - } else { - if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) { - DEBUG(3, ("ads_kdestroy: krb5_cc_resolve failed: %s\n", - error_message(code))); - krb5_free_context(ctx); - return code; - } - } - - if ((code = krb5_cc_destroy (ctx, cc))) { - DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n", - error_message(code))); - } - - krb5_free_context (ctx); - return code; -} - -/************************************************************************ - Routine to fetch the salting principal for a service. Active - Directory may use a non-obvious principal name to generate the salt - when it determines the key to use for encrypting tickets for a service, - and hopefully we detected that when we joined the domain. - ************************************************************************/ - -static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype) -{ - char *ret = NULL; - -#if 0 - asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, service, enctype); - if (!key) { - return NULL; - } - ret = (char *)secrets_fetch(key, NULL); - SAFE_FREE(key); -#endif - return ret; -} - -/************************************************************************ - Routine to get the salting principal for this service. Active - Directory may use a non-obvious principal name to generate the salt - when it determines the key to use for encrypting tickets for a service, - and hopefully we detected that when we joined the domain. - Caller must free if return is not null. - ************************************************************************/ - -krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context, - krb5_principal host_princ, - int enctype) -{ - char *unparsed_name = NULL, *salt_princ_s = NULL; - krb5_principal ret_princ = NULL; - - if (krb5_unparse_name(context, host_princ, &unparsed_name) != 0) { - return (krb5_principal)NULL; - } - - if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) { - krb5_free_unparsed_name(context, unparsed_name); - return (krb5_principal)NULL; - } - - if (krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) { - krb5_free_unparsed_name(context, unparsed_name); - SAFE_FREE(salt_princ_s); - return (krb5_principal)NULL; - } - krb5_free_unparsed_name(context, unparsed_name); - SAFE_FREE(salt_princ_s); - return ret_princ; -} - -/************************************************************************ - Routine to set the salting principal for this service. Active - Directory may use a non-obvious principal name to generate the salt - when it determines the key to use for encrypting tickets for a service, - and hopefully we detected that when we joined the domain. - Setting principal to NULL deletes this entry. - ************************************************************************/ - - BOOL kerberos_secrets_store_salting_principal(const char *service, - int enctype, - const char *principal) -{ - char *key = NULL; - BOOL ret = False; - krb5_context context = NULL; - krb5_principal princ = NULL; - char *princ_s = NULL; - char *unparsed_name = NULL; - - krb5_init_context(&context); - if (!context) { - return False; - } - if (strchr_m(service, '@')) { - asprintf(&princ_s, "%s", service); - } else { - asprintf(&princ_s, "%s@%s", service, lp_realm()); - } - - if (krb5_parse_name(context, princ_s, &princ) != 0) { - goto out; - - } - if (krb5_unparse_name(context, princ, &unparsed_name) != 0) { - goto out; - } - - asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype); - if (!key) { - goto out; - } - -#if 0 - if ((principal != NULL) && (strlen(principal) > 0)) { - ret = secrets_store(key, principal, strlen(principal) + 1); - } else { - ret = secrets_delete(key); - } -#endif - - out: - - SAFE_FREE(key); - SAFE_FREE(princ_s); - - if (unparsed_name) { - krb5_free_unparsed_name(context, unparsed_name); - } - if (context) { - krb5_free_context(context); - } - - return ret; -} - -/************************************************************************ - Routine to get initial credentials as a service ticket for the local machine. - Returns a buffer initialized with krb5_mk_req_extended. - ************************************************************************/ - -static krb5_error_code get_service_ticket(krb5_context ctx, - krb5_ccache ccache, - const char *service_principal, - int enctype, - krb5_data *p_outbuf) -{ - krb5_creds creds, *new_creds = NULL; - char *service_s = NULL; - char *machine_account = NULL, *password = NULL; - krb5_data in_data; - krb5_auth_context auth_context = NULL; - krb5_error_code err = 0; - - ZERO_STRUCT(creds); - - asprintf(&machine_account, "%s$@%s", lp_netbios_name(), lp_realm()); - if (machine_account == NULL) { - goto out; - } - password = secrets_fetch_machine_password(lp_workgroup()); - if (password == NULL) { - goto out; - } - if ((err = kerberos_kinit_password(machine_account, password, 0, NULL, LIBADS_CCACHE_NAME, NULL)) != 0) { - DEBUG(0,("get_service_ticket: kerberos_kinit_password %s@%s failed: %s\n", - machine_account, - lp_realm(), - error_message(err))); - goto out; - } - - /* Ok - the above call has gotten a TGT. Now we need to get a service - ticket to ourselves. */ - - /* Set up the enctype and client and server principal fields for krb5_get_credentials. */ - kerberos_set_creds_enctype(&creds, enctype); - - if ((err = krb5_cc_get_principal(ctx, ccache, &creds.client))) { - DEBUG(3, ("get_service_ticket: krb5_cc_get_principal failed: %s\n", - error_message(err))); - goto out; - } - - if (strchr_m(service_principal, '@')) { - asprintf(&service_s, "%s", service_principal); - } else { - asprintf(&service_s, "%s@%s", service_principal, lp_realm()); - } - - if ((err = krb5_parse_name(ctx, service_s, &creds.server))) { - DEBUG(0,("get_service_ticket: krb5_parse_name %s failed: %s\n", - service_s, error_message(err))); - goto out; - } - - if ((err = krb5_get_credentials(ctx, 0, ccache, &creds, &new_creds))) { - DEBUG(5,("get_service_ticket: krb5_get_credentials for %s enctype %d failed: %s\n", - service_s, enctype, error_message(err))); - goto out; - } - - memset(&in_data, '\0', sizeof(in_data)); - if ((err = krb5_mk_req_extended(ctx, &auth_context, 0, &in_data, - new_creds, p_outbuf)) != 0) { - DEBUG(0,("get_service_ticket: krb5_mk_req_extended failed: %s\n", - error_message(err))); - goto out; - } - - out: - - if (auth_context) { - krb5_auth_con_free(ctx, auth_context); - } - if (new_creds) { - krb5_free_creds(ctx, new_creds); - } - if (creds.server) { - krb5_free_principal(ctx, creds.server); - } - if (creds.client) { - krb5_free_principal(ctx, creds.client); - } - - SAFE_FREE(service_s); - SAFE_FREE(password); - SAFE_FREE(machine_account); - return err; -} - -/************************************************************************ - Check if the machine password can be used in conjunction with the salting_principal - to generate a key which will successfully decrypt the AP_REQ already - gotten as a message to the local machine. - ************************************************************************/ - -static BOOL verify_service_password(krb5_context ctx, - int enctype, - const char *salting_principal, - krb5_data *in_data) -{ - BOOL ret = False; - krb5_principal salting_kprinc = NULL; - krb5_ticket *ticket = NULL; - krb5_keyblock key; - krb5_data passdata; - char *salting_s = NULL; - char *password = NULL; - krb5_auth_context auth_context = NULL; - krb5_error_code err; - - memset(&passdata, '\0', sizeof(passdata)); - memset(&key, '\0', sizeof(key)); - - password = secrets_fetch_machine_password(lp_workgroup()); - if (password == NULL) { - goto out; - } - - if (strchr_m(salting_principal, '@')) { - asprintf(&salting_s, "%s", salting_principal); - } else { - asprintf(&salting_s, "%s@%s", salting_principal, lp_realm()); - } - - if ((err = krb5_parse_name(ctx, salting_s, &salting_kprinc))) { - DEBUG(0,("verify_service_password: krb5_parse_name %s failed: %s\n", - salting_s, error_message(err))); - goto out; - } - - passdata.length = strlen(password); - passdata.data = (char*)password; - if ((err = create_kerberos_key_from_string_direct(ctx, salting_kprinc, &passdata, &key, enctype))) { - DEBUG(0,("verify_service_password: create_kerberos_key_from_string %d failed: %s\n", - enctype, error_message(err))); - goto out; - } - - if ((err = krb5_auth_con_init(ctx, &auth_context)) != 0) { - DEBUG(0,("verify_service_password: krb5_auth_con_init failed %s\n", error_message(err))); - goto out; - } - - if ((err = krb5_auth_con_setuseruserkey(ctx, auth_context, &key)) != 0) { - DEBUG(0,("verify_service_password: krb5_auth_con_setuseruserkey failed %s\n", error_message(err))); - goto out; - } - - if (!(err = krb5_rd_req(ctx, &auth_context, in_data, NULL, NULL, NULL, &ticket))) { - DEBUG(10,("verify_service_password: decrypted message with enctype %u salt %s!\n", - (unsigned int)enctype, salting_s)); - ret = True; - } - - out: - - memset(&passdata, 0, sizeof(passdata)); - krb5_free_keyblock_contents(ctx, &key); - if (ticket != NULL) { - krb5_free_ticket(ctx, ticket); - } - if (salting_kprinc) { - krb5_free_principal(ctx, salting_kprinc); - } - SAFE_FREE(salting_s); - SAFE_FREE(password); - return ret; -} - -/************************************************************************ - * - * From the current draft of kerberos-clarifications: - * - * It is not possible to reliably generate a user's key given a pass - * phrase without contacting the KDC, since it will not be known - * whether alternate salt or parameter values are required. - * - * And because our server has a password, we have this exact problem. We - * make multiple guesses as to which principal name provides the salt which - * the KDC is using. - * - ************************************************************************/ - -static void kerberos_derive_salting_principal_for_enctype(const char *service_principal, - krb5_context ctx, - krb5_ccache ccache, - krb5_enctype enctype, - krb5_enctype *enctypes) -{ - char *salting_principals[3] = {NULL, NULL, NULL}, *second_principal = NULL; - krb5_error_code err = 0; - krb5_data outbuf; - int i, j; - - memset(&outbuf, '\0', sizeof(outbuf)); - - /* Check that the service_principal is useful. */ - if ((service_principal == NULL) || (strlen(service_principal) == 0)) { - return; - } - - /* Generate our first guess -- the principal as-given. */ - asprintf(&salting_principals[0], "%s", service_principal); - if ((salting_principals[0] == NULL) || (strlen(salting_principals[0]) == 0)) { - return; - } - - /* Generate our second guess -- the computer's principal, as Win2k3. */ - asprintf(&second_principal, "host/%s.%s", lp_netbios_name(), lp_realm()); - if (second_principal != NULL) { - strlower_m(second_principal); - asprintf(&salting_principals[1], "%s@%s", second_principal, lp_realm()); - SAFE_FREE(second_principal); - } - if ((salting_principals[1] == NULL) || (strlen(salting_principals[1]) == 0)) { - goto out; - } - - /* Generate our third guess -- the computer's principal, as Win2k. */ - asprintf(&second_principal, "HOST/%s", lp_netbios_name()); - if (second_principal != NULL) { - strlower_m(second_principal + 5); - asprintf(&salting_principals[2], "%s@%s", - second_principal, lp_realm()); - SAFE_FREE(second_principal); - } - if ((salting_principals[2] == NULL) || (strlen(salting_principals[2]) == 0)) { - goto out; - } - - /* Get a service ticket for ourselves into our memory ccache. */ - /* This will commonly fail if there is no principal by that name (and we're trying - many names). So don't print a debug 0 error. */ - - if ((err = get_service_ticket(ctx, ccache, service_principal, enctype, &outbuf)) != 0) { - DEBUG(3, ("verify_service_password: get_service_ticket failed: %s\n", - error_message(err))); - goto out; - } - - /* At this point we have a message to ourselves, salted only the KDC knows how. We - have to work out what that salting is. */ - - /* Try and find the correct salting principal. */ - for (i = 0; i < sizeof(salting_principals) / sizeof(salting_principals[i]); i++) { - if (verify_service_password(ctx, enctype, salting_principals[i], &outbuf)) { - break; - } - } - - /* If we failed to get a match, return. */ - if (i >= sizeof(salting_principals) / sizeof(salting_principals[i])) { - goto out; - } - - /* If we succeeded, store the principal for use for all enctypes which - * share the same cipher and string-to-key function. Doing this here - * allows servers which just pass a keytab to krb5_rd_req() to work - * correctly. */ - for (j = 0; enctypes[j] != 0; j++) { - if (enctype != enctypes[j]) { - /* If this enctype isn't compatible with the one which - * we used, skip it. */ - - if (!kerberos_compatible_enctypes(ctx, enctypes[j], enctype)) - continue; - } - /* If the principal which gives us the proper salt is the one - * which we would normally guess, don't bother noting anything - * in the secrets tdb. */ - if (strcmp(service_principal, salting_principals[i]) != 0) { - kerberos_secrets_store_salting_principal(service_principal, - enctypes[j], - salting_principals[i]); - } - } - - out : - - kerberos_free_data_contents(ctx, &outbuf); - SAFE_FREE(salting_principals[0]); - SAFE_FREE(salting_principals[1]); - SAFE_FREE(salting_principals[2]); - SAFE_FREE(second_principal); -} - -/************************************************************************ - Go through all the possible enctypes for this principal. - ************************************************************************/ - -static void kerberos_derive_salting_principal_direct(krb5_context context, - krb5_ccache ccache, - krb5_enctype *enctypes, - char *service_principal) -{ - int i; - - /* Try for each enctype separately, because the rules are - * different for different enctypes. */ - for (i = 0; enctypes[i] != 0; i++) { - /* Delete secrets entry first. */ - kerberos_secrets_store_salting_principal(service_principal, 0, NULL); -#ifdef ENCTYPE_ARCFOUR_HMAC - if (enctypes[i] == ENCTYPE_ARCFOUR_HMAC) { - /* Of course this'll always work, so just save - * ourselves the effort. */ - continue; - } -#endif - /* Try to figure out what's going on with this - * principal. */ - kerberos_derive_salting_principal_for_enctype(service_principal, - context, - ccache, - enctypes[i], - enctypes); - } -} - -/************************************************************************ - Wrapper function for the above. - ************************************************************************/ - -BOOL kerberos_derive_salting_principal(char *service_principal) -{ - krb5_context context = NULL; - krb5_enctype *enctypes = NULL; - krb5_ccache ccache = NULL; - krb5_error_code ret = 0; - - initialize_krb5_error_table(); - if ((ret = krb5_init_context(&context)) != 0) { - DEBUG(1,("kerberos_derive_cifs_salting_principals: krb5_init_context failed. %s\n", - error_message(ret))); - return False; - } - if ((ret = get_kerberos_allowed_etypes(context, &enctypes)) != 0) { - DEBUG(1,("kerberos_derive_cifs_salting_principals: get_kerberos_allowed_etypes failed. %s\n", - error_message(ret))); - goto out; - } - - if ((ret = krb5_cc_resolve(context, LIBADS_CCACHE_NAME, &ccache)) != 0) { - DEBUG(3, ("get_service_ticket: krb5_cc_resolve for %s failed: %s\n", - LIBADS_CCACHE_NAME, error_message(ret))); - goto out; - } - - kerberos_derive_salting_principal_direct(context, ccache, enctypes, service_principal); - - out: - if (enctypes) { - free_kerberos_etypes(context, enctypes); - } - if (ccache) { - krb5_cc_destroy(context, ccache); - } - if (context) { - krb5_free_context(context); - } - - return ret ? False : True; -} - -/************************************************************************ - Core function to try and determine what salt is being used for any keytab - keys. - ************************************************************************/ - -BOOL kerberos_derive_cifs_salting_principals(void) -{ - fstring my_fqdn; - char *service = NULL; - krb5_context context = NULL; - krb5_enctype *enctypes = NULL; - krb5_ccache ccache = NULL; - krb5_error_code ret = 0; - BOOL retval = False; - - initialize_krb5_error_table(); - if ((ret = krb5_init_context(&context)) != 0) { - DEBUG(1,("kerberos_derive_cifs_salting_principals: krb5_init_context failed. %s\n", - error_message(ret))); - return False; - } - if ((ret = get_kerberos_allowed_etypes(context, &enctypes)) != 0) { - DEBUG(1,("kerberos_derive_cifs_salting_principals: get_kerberos_allowed_etypes failed. %s\n", - error_message(ret))); - goto out; - } - - if ((ret = krb5_cc_resolve(context, LIBADS_CCACHE_NAME, &ccache)) != 0) { - DEBUG(3, ("get_service_ticket: krb5_cc_resolve for %s failed: %s\n", - LIBADS_CCACHE_NAME, error_message(ret))); - goto out; - } - - if (asprintf(&service, "%s$", lp_netbios_name()) != -1) { - strlower_m(service); - kerberos_derive_salting_principal_direct(context, ccache, enctypes, service); - SAFE_FREE(service); - } - if (asprintf(&service, "cifs/%s", lp_netbios_name()) != -1) { - strlower_m(service); - kerberos_derive_salting_principal_direct(context, ccache, enctypes, service); - SAFE_FREE(service); - } - if (asprintf(&service, "host/%s", lp_netbios_name()) != -1) { - strlower_m(service); - kerberos_derive_salting_principal_direct(context, ccache, enctypes, service); - SAFE_FREE(service); - } - if (asprintf(&service, "cifs/%s.%s", lp_netbios_name(), lp_realm()) != -1) { - strlower_m(service); - kerberos_derive_salting_principal_direct(context, ccache, enctypes, service); - SAFE_FREE(service); - } - if (asprintf(&service, "host/%s.%s", lp_netbios_name(), lp_realm()) != -1) { - strlower_m(service); - kerberos_derive_salting_principal_direct(context, ccache, enctypes, service); - SAFE_FREE(service); - } - name_to_fqdn(my_fqdn, lp_netbios_name()); - if (asprintf(&service, "cifs/%s", my_fqdn) != -1) { - strlower_m(service); - kerberos_derive_salting_principal_direct(context, ccache, enctypes, service); - SAFE_FREE(service); - } - if (asprintf(&service, "host/%s", my_fqdn) != -1) { - strlower_m(service); - kerberos_derive_salting_principal_direct(context, ccache, enctypes, service); - SAFE_FREE(service); - } - - retval = True; - - out: - if (enctypes) { - free_kerberos_etypes(context, enctypes); - } - if (ccache) { - krb5_cc_destroy(context, ccache); - } - if (context) { - krb5_free_context(context); - } - return retval; -} -#endif diff --git a/source4/libcli/auth/kerberos.h b/source4/libcli/auth/kerberos.h deleted file mode 100644 index 4daf0ea07a..0000000000 --- a/source4/libcli/auth/kerberos.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - Unix SMB/CIFS implementation. - simple kerberos5 routines for active directory - Copyright (C) Andrew Tridgell 2001 - Copyright (C) Luke Howard 2002-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. -*/ - -#if defined(HAVE_KRB5) - -/* not really ASN.1, but RFC 1964 */ -#define TOK_ID_KRB_AP_REQ "\x01\x00" -#define TOK_ID_KRB_AP_REP "\x02\x00" -#define TOK_ID_KRB_ERROR "\x03\x00" -#define TOK_ID_GSS_GETMIC "\x01\x01" -#define TOK_ID_GSS_WRAP "\x02\x01" - -#ifdef HAVE_KRB5_KEYBLOCK_KEYVALUE -#define KRB5_KEY_TYPE(k) ((k)->keytype) -#define KRB5_KEY_LENGTH(k) ((k)->keyvalue.length) -#define KRB5_KEY_DATA(k) ((k)->keyvalue.data) -#else -#define KRB5_KEY_TYPE(k) ((k)->enctype) -#define KRB5_KEY_LENGTH(k) ((k)->length) -#define KRB5_KEY_DATA(k) ((k)->contents) -#endif /* HAVE_KRB5_KEYBLOCK_KEYVALUE */ - -#ifndef HAVE_KRB5_SET_REAL_TIME -krb5_error_code krb5_set_real_time(krb5_context context, int32_t seconds, int32_t microseconds); -#endif - -#ifndef HAVE_KRB5_SET_DEFAULT_TGS_KTYPES -krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc); -#endif - -#if defined(HAVE_KRB5_AUTH_CON_SETKEY) && !defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY) -krb5_error_code krb5_auth_con_setuseruserkey(krb5_context context, krb5_auth_context auth_context, krb5_keyblock *keyblock); -#endif - -#ifndef HAVE_KRB5_FREE_UNPARSED_NAME -void krb5_free_unparsed_name(krb5_context ctx, char *val); -#endif - -#if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING) && !defined(HAVE_KRB5_PRINC_COMPONENT) -const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i ); -#endif - -/* Samba wrapper function for krb5 functionality. */ -void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr); -int create_kerberos_key_from_string(krb5_context context, krb5_principal host_princ, krb5_data *password, krb5_keyblock *key, krb5_enctype enctype); -int create_kerberos_key_from_string_direct(krb5_context context, krb5_principal host_princ, krb5_data *password, krb5_keyblock *key, krb5_enctype enctype); -krb5_const_principal get_principal_from_tkt(krb5_ticket *tkt); -krb5_error_code krb5_locate_kdc(krb5_context ctx, const krb5_data *realm, struct sockaddr **addr_pp, int *naddrs, int get_masters); -krb5_error_code get_kerberos_allowed_etypes(krb5_context context, krb5_enctype **enctypes); -void free_kerberos_etypes(krb5_context context, krb5_enctype *enctypes); -BOOL get_krb5_smb_session_key(krb5_context context, krb5_auth_context auth_context, DATA_BLOB *session_key, BOOL remote); -krb5_error_code ads_krb5_mk_req(krb5_context context, - krb5_auth_context *auth_context, - const krb5_flags ap_req_options, - const char *principal, - krb5_ccache ccache, - krb5_data *outbuf); -DATA_BLOB get_auth_data_from_tkt(TALLOC_CTX *mem_ctx, - krb5_ticket *tkt); - -NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx, - krb5_context context, - krb5_auth_context auth_context, - const char *realm, const char *service, - const DATA_BLOB *ticket, - char **principal, DATA_BLOB *auth_data, - DATA_BLOB *ap_rep, - krb5_keyblock *keyblock); -int kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc, - const char *principal, const char *password, - time_t *expire_time, time_t *kdc_time); -krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context, - krb5_principal host_princ, - int enctype); -void kerberos_set_creds_enctype(krb5_creds *pcreds, int enctype); -BOOL kerberos_compatible_enctypes(krb5_context context, krb5_enctype enctype1, krb5_enctype enctype2); -void kerberos_free_data_contents(krb5_context context, krb5_data *pdata); -krb5_error_code smb_krb5_kt_free_entry(krb5_context context, krb5_keytab_entry *kt_entry); -char *smb_get_krb5_error_message(krb5_context context, krb5_error_code code, TALLOC_CTX *mem_ctx); -#endif /* HAVE_KRB5 */ - diff --git a/source4/libcli/auth/kerberos_verify.c b/source4/libcli/auth/kerberos_verify.c deleted file mode 100644 index a1dfe1056e..0000000000 --- a/source4/libcli/auth/kerberos_verify.c +++ /dev/null @@ -1,486 +0,0 @@ -/* - Unix SMB/CIFS implementation. - kerberos utility library - Copyright (C) Andrew Tridgell 2001 - Copyright (C) Remus Koos 2001 - Copyright (C) Luke Howard 2003 - Copyright (C) Guenther Deschner 2003 - Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2003 - Copyright (C) Andrew Bartlett 2004-2005 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include "includes.h" -#include "system/kerberos.h" -#include "libcli/auth/kerberos.h" -#include "asn_1.h" -#include "lib/ldb/include/ldb.h" -#include "secrets.h" -#include "pstring.h" - -#ifdef HAVE_KRB5 - -#if !defined(HAVE_KRB5_PRINC_COMPONENT) -const krb5_data *krb5_princ_component(krb5_context, krb5_principal, int ); -#endif -static DATA_BLOB unwrap_pac(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data) -{ - DATA_BLOB out; - DATA_BLOB pac_contents = data_blob(NULL, 0); - struct asn1_data data; - int data_type; - if (!auth_data->length) { - return data_blob(NULL, 0); - } - - asn1_load(&data, *auth_data); - asn1_start_tag(&data, ASN1_SEQUENCE(0)); - asn1_start_tag(&data, ASN1_SEQUENCE(0)); - asn1_start_tag(&data, ASN1_CONTEXT(0)); - asn1_read_Integer(&data, &data_type); - asn1_end_tag(&data); - asn1_start_tag(&data, ASN1_CONTEXT(1)); - asn1_read_OctetString(&data, &pac_contents); - asn1_end_tag(&data); - asn1_end_tag(&data); - asn1_end_tag(&data); - asn1_free(&data); - - out = data_blob_talloc(mem_ctx, pac_contents.data, pac_contents.length); - - data_blob_free(&pac_contents); - - return out; -} - -/********************************************************************************** - Try to verify a ticket using the system keytab... the system keytab has kvno -1 entries, so - it's more like what microsoft does... see comment in utils/net_ads.c in the - ads_keytab_add_entry function for details. -***********************************************************************************/ - -static krb5_error_code ads_keytab_verify_ticket(TALLOC_CTX *mem_ctx, krb5_context context, - krb5_auth_context auth_context, - const char *service, - const DATA_BLOB *ticket, krb5_data *p_packet, - krb5_ticket **pp_tkt, - krb5_keyblock *keyblock) -{ - krb5_error_code ret = 0; - krb5_keytab keytab = NULL; - krb5_kt_cursor kt_cursor; - krb5_keytab_entry kt_entry; - char *valid_princ_formats[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL }; - char *entry_princ_s = NULL; - const char *my_name, *my_fqdn; - int i; - int number_matched_principals = 0; - const char *last_error_message; - - /* Generate the list of principal names which we expect - * clients might want to use for authenticating to the file - * service. We allow name$,{host,cifs}/{name,fqdn,name.REALM}. */ - - my_name = lp_netbios_name(); - - my_fqdn = name_to_fqdn(mem_ctx, my_name); - - asprintf(&valid_princ_formats[0], "%s$@%s", my_name, lp_realm()); - asprintf(&valid_princ_formats[1], "host/%s@%s", my_name, lp_realm()); - asprintf(&valid_princ_formats[2], "host/%s@%s", my_fqdn, lp_realm()); - asprintf(&valid_princ_formats[3], "host/%s.%s@%s", my_name, lp_realm(), lp_realm()); - asprintf(&valid_princ_formats[4], "cifs/%s@%s", my_name, lp_realm()); - asprintf(&valid_princ_formats[5], "cifs/%s@%s", my_fqdn, lp_realm()); - asprintf(&valid_princ_formats[6], "cifs/%s.%s@%s", my_name, lp_realm(), lp_realm()); - - ZERO_STRUCT(kt_entry); - ZERO_STRUCT(kt_cursor); - - ret = krb5_kt_default(context, &keytab); - if (ret) { - DEBUG(1, ("ads_keytab_verify_ticket: krb5_kt_default failed (%s)\n", - smb_get_krb5_error_message(context, ret, mem_ctx))); - goto out; - } - - /* Iterate through the keytab. For each key, if the principal - * name case-insensitively matches one of the allowed formats, - * try verifying the ticket using that principal. */ - - ret = krb5_kt_start_seq_get(context, keytab, &kt_cursor); - if (ret) { - last_error_message = smb_get_krb5_error_message(context, ret, mem_ctx); - DEBUG(1, ("ads_keytab_verify_ticket: krb5_kt_start_seq_get failed (%s)\n", - last_error_message)); - goto out; - } - - ret = krb5_kt_start_seq_get(context, keytab, &kt_cursor); - if (ret != KRB5_KT_END && ret != ENOENT ) { - ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; /* Pick an error... */ - while (ret && (krb5_kt_next_entry(context, keytab, &kt_entry, &kt_cursor) == 0)) { - krb5_error_code upn_ret; - upn_ret = krb5_unparse_name(context, kt_entry.principal, &entry_princ_s); - if (upn_ret) { - last_error_message = smb_get_krb5_error_message(context, ret, mem_ctx); - DEBUG(1, ("ads_keytab_verify_ticket: krb5_unparse_name failed (%s)\n", - last_error_message)); - ret = upn_ret; - break; - } - for (i = 0; i < ARRAY_SIZE(valid_princ_formats); i++) { - if (!strequal(entry_princ_s, valid_princ_formats[i])) { - continue; - } - - number_matched_principals++; - p_packet->length = ticket->length; - p_packet->data = (krb5_pointer)ticket->data; - *pp_tkt = NULL; - ret = krb5_rd_req(context, &auth_context, p_packet, kt_entry.principal, keytab, NULL, pp_tkt); - if (ret) { - last_error_message = smb_get_krb5_error_message(context, ret, mem_ctx); - DEBUG(10, ("ads_keytab_verify_ticket: krb5_rd_req(%s) failed: %s\n", - entry_princ_s, last_error_message)); - } else { - DEBUG(3,("ads_keytab_verify_ticket: krb5_rd_req succeeded for principal %s\n", - entry_princ_s)); - break; - } - } - - /* Free the name we parsed. */ - krb5_free_unparsed_name(context, entry_princ_s); - entry_princ_s = NULL; - - /* Free the entry we just read. */ - smb_krb5_kt_free_entry(context, &kt_entry); - ZERO_STRUCT(kt_entry); - } - krb5_kt_end_seq_get(context, keytab, &kt_cursor); - } - - ZERO_STRUCT(kt_cursor); - - out: - - if (ret) { - if (!number_matched_principals) { - DEBUG(3, ("ads_keytab_verify_ticket: no keytab principals matched expected file service name.\n")); - } else { - DEBUG(3, ("ads_keytab_verify_ticket: krb5_rd_req failed for all %d matched keytab principals\n", - number_matched_principals)); - } - DEBUG(3, ("ads_keytab_verify_ticket: last error: %s\n", last_error_message)); - } - - if (entry_princ_s) { - krb5_free_unparsed_name(context, entry_princ_s); - } - - { - krb5_keytab_entry zero_kt_entry; - ZERO_STRUCT(zero_kt_entry); - if (memcmp(&zero_kt_entry, &kt_entry, sizeof(krb5_keytab_entry))) { - smb_krb5_kt_free_entry(context, &kt_entry); - } - } - - { - krb5_kt_cursor zero_csr; - ZERO_STRUCT(zero_csr); - if ((memcmp(&kt_cursor, &zero_csr, sizeof(krb5_kt_cursor)) != 0) && keytab) { - krb5_kt_end_seq_get(context, keytab, &kt_cursor); - } - } - - if (keytab) { - krb5_kt_close(context, keytab); - } - - return ret; -} - -/********************************************************************************** - Try to verify a ticket using the secrets.tdb. -***********************************************************************************/ - -static krb5_error_code ads_secrets_verify_ticket(TALLOC_CTX *mem_ctx, krb5_context context, - krb5_auth_context auth_context, - krb5_principal host_princ, - const DATA_BLOB *ticket, krb5_data *p_packet, - krb5_ticket **pp_tkt, - krb5_keyblock *keyblock) -{ - krb5_error_code ret = 0; - krb5_error_code our_ret; - krb5_data password; - krb5_enctype *enctypes = NULL; - int i; - const struct ldb_val *password_v; - struct ldb_context *ldb; - int ldb_ret; - struct ldb_message **msgs; - const char *base_dn = SECRETS_PRIMARY_DOMAIN_DN; - const char *attrs[] = { - "secret", - NULL - }; - - ZERO_STRUCTP(keyblock); - - /* Local secrets are stored in secrets.ldb */ - ldb = secrets_db_connect(mem_ctx); - if (!ldb) { - return ENOENT; - } - - /* search for the secret record */ - ldb_ret = gendb_search(ldb, - mem_ctx, base_dn, &msgs, attrs, - SECRETS_PRIMARY_REALM_FILTER, - lp_realm()); - if (ldb_ret == 0) { - DEBUG(1, ("Could not find domain join record for %s\n", - lp_realm())); - return ENOENT; - } else if (ldb_ret != 1) { - DEBUG(1, ("Found %d records matching cn=%s under DN %s\n", ldb_ret, - lp_realm(), base_dn)); - return ENOENT; - } - - password_v = ldb_msg_find_ldb_val(msgs[0], "secret"); - - password.data = password_v->data; - password.length = password_v->length; - - /* CIFS doesn't use addresses in tickets. This would break NAT. JRA */ - - if ((ret = get_kerberos_allowed_etypes(context, &enctypes))) { - DEBUG(1,("ads_secrets_verify_ticket: krb5_get_permitted_enctypes failed (%s)\n", - error_message(ret))); - return ret; - } - - p_packet->length = ticket->length; - p_packet->data = (krb5_pointer)ticket->data; - - /* We need to setup a auth context with each possible encoding type in turn. */ - - ret = KRB5_BAD_ENCTYPE; - for (i=0;enctypes[i];i++) { - krb5_keyblock *key = NULL; - - if (!(key = malloc_p(krb5_keyblock))) { - break; - } - - if (create_kerberos_key_from_string(context, host_princ, &password, key, enctypes[i])) { - SAFE_FREE(key); - continue; - } - - krb5_auth_con_setuseruserkey(context, auth_context, key); - - krb5_free_keyblock(context, key); - - our_ret = krb5_rd_req(context, &auth_context, p_packet, - NULL, - NULL, NULL, pp_tkt); - if (!our_ret) { - - DEBUG(10,("ads_secrets_verify_ticket: enc type [%u] decrypted message !\n", - (unsigned int)enctypes[i] )); - ret = our_ret; - break; - } - - DEBUG((our_ret != KRB5_BAD_ENCTYPE) ? 3 : 10, - ("ads_secrets_verify_ticket: enc type [%u] failed to decrypt with error %s\n", - (unsigned int)enctypes[i], smb_get_krb5_error_message(context, our_ret, mem_ctx))); - - if (our_ret != KRB5_BAD_ENCTYPE) { - ret = our_ret; - } - } - - free_kerberos_etypes(context, enctypes); - - return ret; -} - -/********************************************************************************** - Verify an incoming ticket and parse out the principal name and - authorization_data if available. -***********************************************************************************/ - - NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx, - krb5_context context, - krb5_auth_context auth_context, - const char *realm, const char *service, - const DATA_BLOB *ticket, - char **principal, DATA_BLOB *auth_data, - DATA_BLOB *ap_rep, - krb5_keyblock *keyblock) -{ - NTSTATUS sret = NT_STATUS_LOGON_FAILURE; - krb5_data packet; - krb5_ticket *tkt = NULL; - krb5_rcache rcache = NULL; - int ret; - - krb5_principal host_princ = NULL; - char *host_princ_s = NULL; - BOOL got_replay_mutex = False; - - char *malloc_principal; - - ZERO_STRUCT(packet); - ZERO_STRUCTP(auth_data); - ZERO_STRUCTP(ap_rep); - - /* This whole process is far more complex than I would - like. We have to go through all this to allow us to store - the secret internally, instead of using /etc/krb5.keytab */ - - asprintf(&host_princ_s, "%s$", lp_netbios_name()); - strlower_m(host_princ_s); - ret = krb5_parse_name(context, host_princ_s, &host_princ); - if (ret) { - DEBUG(1,("ads_verify_ticket: krb5_parse_name(%s) failed (%s)\n", - host_princ_s, error_message(ret))); - goto out; - } - - - /* Lock a mutex surrounding the replay as there is no locking in the MIT krb5 - * code surrounding the replay cache... */ - - if (!grab_server_mutex("replay cache mutex")) { - DEBUG(1,("ads_verify_ticket: unable to protect replay cache with mutex.\n")); - goto out; - } - - got_replay_mutex = True; - - /* - * JRA. We must set the rcache here. This will prevent replay attacks. - */ - - ret = krb5_get_server_rcache(context, krb5_princ_component(context, host_princ, 0), &rcache); - if (ret) { - DEBUG(1,("ads_verify_ticket: krb5_get_server_rcache failed (%s)\n", error_message(ret))); - goto out; - } - - ret = krb5_auth_con_setrcache(context, auth_context, rcache); - if (ret) { - DEBUG(1,("ads_verify_ticket: krb5_auth_con_setrcache failed (%s)\n", error_message(ret))); - goto out; - } - - ret = ads_keytab_verify_ticket(mem_ctx, context, auth_context, - service, ticket, &packet, &tkt, keyblock); - if (ret) { - DEBUG(10, ("ads_secrets_verify_ticket: using host principal: [%s]\n", host_princ_s)); - ret = ads_secrets_verify_ticket(mem_ctx, context, auth_context, - host_princ, ticket, - &packet, &tkt, keyblock); - } - - release_server_mutex(); - got_replay_mutex = False; - - if (ret) { - DEBUG(3,("ads_verify_ticket: krb5_rd_req with auth failed (%s)\n", - smb_get_krb5_error_message(context, ret, mem_ctx))); - goto out; - } - - ret = krb5_mk_rep(context, auth_context, &packet); - if (ret) { - DEBUG(3,("ads_verify_ticket: Failed to generate mutual authentication reply (%s)\n", - smb_get_krb5_error_message(context, ret, mem_ctx))); - goto out; - } - - *ap_rep = data_blob_talloc(mem_ctx, packet.data, packet.length); - SAFE_FREE(packet.data); - packet.length = 0; - -#if 0 - file_save("/tmp/ticket.dat", ticket->data, ticket->length); -#endif - - *auth_data = get_auth_data_from_tkt(mem_ctx, tkt); - - *auth_data = unwrap_pac(mem_ctx, auth_data); - -#if 0 - if (tkt->enc_part2) { - file_save("/tmp/authdata.dat", - tkt->enc_part2->authorization_data[0]->contents, - tkt->enc_part2->authorization_data[0]->length); - } -#endif - - if ((ret = krb5_unparse_name(context, get_principal_from_tkt(tkt), - &malloc_principal))) { - DEBUG(3,("ads_verify_ticket: krb5_unparse_name failed (%s)\n", - smb_get_krb5_error_message(context, ret, mem_ctx))); - sret = NT_STATUS_LOGON_FAILURE; - goto out; - } - - *principal = talloc_strdup(mem_ctx, malloc_principal); - SAFE_FREE(malloc_principal); - if (!principal) { - DEBUG(3,("ads_verify_ticket: talloc_strdup() failed\n")); - sret = NT_STATUS_NO_MEMORY; - goto out; - } - - sret = NT_STATUS_OK; - - out: - - if (got_replay_mutex) { - release_server_mutex(); - } - - if (!NT_STATUS_IS_OK(sret)) { - data_blob_free(auth_data); - } - - if (!NT_STATUS_IS_OK(sret)) { - data_blob_free(ap_rep); - } - - if (host_princ) { - krb5_free_principal(context, host_princ); - } - - if (tkt != NULL) { - krb5_free_ticket(context, tkt); - } - - SAFE_FREE(host_princ_s); - - return sret; -} - -#endif /* HAVE_KRB5 */ diff --git a/source4/libcli/auth/ntlmssp.c b/source4/libcli/auth/ntlmssp.c deleted file mode 100644 index 37374d9d39..0000000000 --- a/source4/libcli/auth/ntlmssp.c +++ /dev/null @@ -1,1322 +0,0 @@ -/* - Unix SMB/Netbios implementation. - Version 3.0 - handle NLTMSSP, client server side parsing - - Copyright (C) Andrew Tridgell 2001 - Copyright (C) Andrew Bartlett 2001-2005 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include "includes.h" -#include "auth/auth.h" -#include "lib/crypto/crypto.h" -#include "pstring.h" - -static NTSTATUS ntlmssp_client_initial(struct ntlmssp_state *ntlmssp_state, - TALLOC_CTX *out_mem_ctx, - DATA_BLOB in, DATA_BLOB *out); -static NTSTATUS ntlmssp_server_negotiate(struct ntlmssp_state *ntlmssp_state, - TALLOC_CTX *out_mem_ctx, - const DATA_BLOB in, DATA_BLOB *out); -static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_state *ntlmssp_state, - TALLOC_CTX *out_mem_ctx, - const DATA_BLOB in, DATA_BLOB *out); -static NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state, - TALLOC_CTX *out_mem_ctx, - const DATA_BLOB in, DATA_BLOB *out); - -/** - * Callbacks for NTLMSSP - for both client and server operating modes - * - */ - -static const struct ntlmssp_callbacks { - enum ntlmssp_role role; - enum ntlmssp_message_type ntlmssp_command; - NTSTATUS (*fn)(struct ntlmssp_state *ntlmssp_state, - TALLOC_CTX *out_mem_ctx, - DATA_BLOB in, DATA_BLOB *out); -} ntlmssp_callbacks[] = { - {NTLMSSP_CLIENT, NTLMSSP_INITIAL, ntlmssp_client_initial}, - {NTLMSSP_SERVER, NTLMSSP_NEGOTIATE, ntlmssp_server_negotiate}, - {NTLMSSP_CLIENT, NTLMSSP_CHALLENGE, ntlmssp_client_challenge}, - {NTLMSSP_SERVER, NTLMSSP_AUTH, ntlmssp_server_auth}, - {NTLMSSP_CLIENT, NTLMSSP_UNKNOWN, NULL}, - {NTLMSSP_SERVER, NTLMSSP_UNKNOWN, NULL} -}; - - -/** - * Print out the NTLMSSP flags for debugging - * @param neg_flags The flags from the packet - */ - -void debug_ntlmssp_flags(uint32_t neg_flags) -{ - DEBUG(3,("Got NTLMSSP neg_flags=0x%08x\n", neg_flags)); - - if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_UNICODE\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_OEM) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_OEM\n")); - if (neg_flags & NTLMSSP_REQUEST_TARGET) - DEBUGADD(4, (" NTLMSSP_REQUEST_TARGET\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_SIGN) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_SIGN\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_SEAL) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_SEAL\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_LM_KEY\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_NETWARE) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NETWARE\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_NTLM) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NTLM\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_ALWAYS_SIGN\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_NTLM2) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NTLM2\n")); - if (neg_flags & NTLMSSP_CHAL_TARGET_INFO) - DEBUGADD(4, (" NTLMSSP_CHAL_TARGET_INFO\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_128) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_128\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_KEY_EXCH\n")); -} - -/** - * Default challenge generation code. - * - */ - -static const uint8_t *get_challenge(const struct ntlmssp_state *ntlmssp_state) -{ - uint8_t *chal = talloc_size(ntlmssp_state, 8); - generate_random_buffer(chal, 8); - - return chal; -} - -/** - * Default 'we can set the challenge to anything we like' implementation - * - */ - -static BOOL may_set_challenge(const struct ntlmssp_state *ntlmssp_state) -{ - return True; -} - -/** - * Default 'we can set the challenge to anything we like' implementation - * - * Does not actually do anything, as the value is always in the structure anyway. - * - */ - -static NTSTATUS set_challenge(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *challenge) -{ - SMB_ASSERT(challenge->length == 8); - return NT_STATUS_OK; -} - -/** - * Set a username on an NTLMSSP context - ensures it is talloc()ed - * - */ - -NTSTATUS ntlmssp_set_username(struct ntlmssp_state *ntlmssp_state, const char *user) -{ - if (!user) { - /* it should be at least "" */ - DEBUG(1, ("NTLMSSP failed to set username - cannot accept NULL username\n")); - return NT_STATUS_INVALID_PARAMETER; - } - ntlmssp_state->user = talloc_strdup(ntlmssp_state, user); - if (!ntlmssp_state->user) { - return NT_STATUS_NO_MEMORY; - } - return NT_STATUS_OK; -} - -/** - * Set a password on an NTLMSSP context - ensures it is talloc()ed - * - */ -NTSTATUS ntlmssp_set_password(struct ntlmssp_state *ntlmssp_state, const char *password) -{ - if (!password) { - ntlmssp_state->password = NULL; - } else { - ntlmssp_state->password = talloc_strdup(ntlmssp_state, password); - if (!ntlmssp_state->password) { - return NT_STATUS_NO_MEMORY; - } - } - return NT_STATUS_OK; -} - -/** - * Set a domain on an NTLMSSP context - ensures it is talloc()ed - * - */ -NTSTATUS ntlmssp_set_domain(struct ntlmssp_state *ntlmssp_state, const char *domain) -{ - ntlmssp_state->domain = talloc_strdup(ntlmssp_state, domain); - if (!ntlmssp_state->domain) { - return NT_STATUS_NO_MEMORY; - } - return NT_STATUS_OK; -} - -/** - * Set a workstation on an NTLMSSP context - ensures it is talloc()ed - * - */ -NTSTATUS ntlmssp_set_workstation(struct ntlmssp_state *ntlmssp_state, const char *workstation) -{ - ntlmssp_state->workstation = talloc_strdup(ntlmssp_state, workstation); - if (!ntlmssp_state->workstation) { - return NT_STATUS_NO_MEMORY; - } - return NT_STATUS_OK; -} - -/** - * Store a DATA_BLOB containing an NTLMSSP response, for use later. - * This copies the data blob - */ - -NTSTATUS ntlmssp_store_response(struct ntlmssp_state *ntlmssp_state, - DATA_BLOB response) -{ - ntlmssp_state->stored_response = data_blob_talloc(ntlmssp_state, - response.data, response.length); - return NT_STATUS_OK; -} - -/** - * Next state function for the NTLMSSP state machine - * - * @param ntlmssp_state NTLMSSP State - * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on - * @param in The request, as a DATA_BLOB - * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx - * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent, - * or NT_STATUS_OK if the user is authenticated. - */ - -NTSTATUS ntlmssp_update(struct ntlmssp_state *ntlmssp_state, - TALLOC_CTX *out_mem_ctx, - const DATA_BLOB in, DATA_BLOB *out) -{ - DATA_BLOB input; - uint32_t ntlmssp_command; - int i; - - *out = data_blob(NULL, 0); - - if (ntlmssp_state->expected_state == NTLMSSP_DONE) { - return NT_STATUS_OK; - } - - if (!out_mem_ctx) { - /* if the caller doesn't want to manage/own the memory, - we can put it on our context */ - out_mem_ctx = ntlmssp_state; - } - - if (!in.length && ntlmssp_state->stored_response.length) { - input = ntlmssp_state->stored_response; - - /* we only want to read the stored response once - overwrite it */ - ntlmssp_state->stored_response = data_blob(NULL, 0); - } else { - input = in; - } - - if (!input.length) { - switch (ntlmssp_state->role) { - case NTLMSSP_CLIENT: - ntlmssp_command = NTLMSSP_INITIAL; - break; - case NTLMSSP_SERVER: - /* 'datagram' mode - no neg packet */ - ntlmssp_command = NTLMSSP_NEGOTIATE; - break; - } - } else { - if (!msrpc_parse(ntlmssp_state, - &input, "Cd", - "NTLMSSP", - &ntlmssp_command)) { - DEBUG(1, ("Failed to parse NTLMSSP packet, could not extract NTLMSSP command\n")); - dump_data(2, input.data, input.length); - return NT_STATUS_INVALID_PARAMETER; - } - } - - if (ntlmssp_command != ntlmssp_state->expected_state) { - DEBUG(1, ("got NTLMSSP command %u, expected %u\n", ntlmssp_command, ntlmssp_state->expected_state)); - return NT_STATUS_INVALID_PARAMETER; - } - - for (i=0; ntlmssp_callbacks[i].fn; i++) { - if (ntlmssp_callbacks[i].role == ntlmssp_state->role - && ntlmssp_callbacks[i].ntlmssp_command == ntlmssp_command - && ntlmssp_callbacks[i].fn) { - return ntlmssp_callbacks[i].fn(ntlmssp_state, out_mem_ctx, input, out); - } - } - - DEBUG(1, ("failed to find NTLMSSP callback for NTLMSSP mode %u, command %u\n", - ntlmssp_state->role, ntlmssp_command)); - - return NT_STATUS_INVALID_PARAMETER; -} - -/** - * 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 - */ - -void ntlmssp_end(struct ntlmssp_state **ntlmssp_state) -{ - (*ntlmssp_state)->ref_count--; - - if ((*ntlmssp_state)->ref_count == 0) { - talloc_free(*ntlmssp_state); - } - - *ntlmssp_state = NULL; - return; -} - -/** - * Determine correct target name flags for reply, given server role - * and negotiated flags - * - * @param ntlmssp_state NTLMSSP State - * @param neg_flags The flags from the packet - * @param chal_flags The flags to be set in the reply packet - * @return The 'target name' string. - */ - -static const char *ntlmssp_target_name(struct ntlmssp_state *ntlmssp_state, - uint32_t neg_flags, uint32_t *chal_flags) -{ - if (neg_flags & NTLMSSP_REQUEST_TARGET) { - *chal_flags |= NTLMSSP_CHAL_TARGET_INFO; - *chal_flags |= NTLMSSP_REQUEST_TARGET; - if (ntlmssp_state->server_role == ROLE_STANDALONE) { - *chal_flags |= NTLMSSP_TARGET_TYPE_SERVER; - return ntlmssp_state->server_name; - } else { - *chal_flags |= NTLMSSP_TARGET_TYPE_DOMAIN; - return ntlmssp_state->get_domain(); - }; - } else { - return ""; - } -} - -static void ntlmssp_handle_neg_flags(struct ntlmssp_state *ntlmssp_state, - uint32_t neg_flags, BOOL allow_lm) { - if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE) { - ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE; - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_OEM; - ntlmssp_state->unicode = True; - } else { - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_UNICODE; - ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_OEM; - ntlmssp_state->unicode = False; - } - - if ((neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) && allow_lm && !ntlmssp_state->use_ntlmv2) { - /* other end forcing us to use LM */ - ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_LM_KEY; - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2; - } else { - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; - } - - if (neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) { - ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN; - } - - if (!(neg_flags & NTLMSSP_NEGOTIATE_SIGN)) { - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SIGN; - } - - if (!(neg_flags & NTLMSSP_NEGOTIATE_SEAL)) { - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SEAL; - } - - if (!(neg_flags & NTLMSSP_NEGOTIATE_NTLM2)) { - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2; - } - - if (!(neg_flags & NTLMSSP_NEGOTIATE_128)) { - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_128; - if (neg_flags & NTLMSSP_NEGOTIATE_56) { - ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_56; - } - } - - if (!(neg_flags & NTLMSSP_NEGOTIATE_56)) { - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_56; - } - - if (!(neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH)) { - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_KEY_EXCH; - } - - if ((neg_flags & NTLMSSP_REQUEST_TARGET)) { - ntlmssp_state->neg_flags |= NTLMSSP_REQUEST_TARGET; - } - -} - -/** - Weaken NTLMSSP keys to cope with down-level clients and servers. - - We probably should have some parameters to control this, but as - it only occours for LM_KEY connections, and this is controlled - by the client lanman auth/lanman auth parameters, it isn't too bad. -*/ - -static void ntlmssp_weaken_keys(struct ntlmssp_state *ntlmssp_state) { - /* Key weakening not performed on the master key for NTLM2 - and does not occour for NTLM1. Therefore we only need - to do this for the LM_KEY. - */ - - if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) { - if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_128) { - - } else if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_56) { - ntlmssp_state->session_key.data[7] = 0xa0; - } else { /* forty bits */ - ntlmssp_state->session_key.data[5] = 0xe5; - ntlmssp_state->session_key.data[6] = 0x38; - ntlmssp_state->session_key.data[7] = 0xb0; - } - ntlmssp_state->session_key.length = 8; - } -} - -/** - * Next state function for the Negotiate packet - * - * @param ntlmssp_state NTLMSSP State - * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on - * @param in The request, as a DATA_BLOB - * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx - * @return Errors or MORE_PROCESSING_REQUIRED if a reply is sent. - */ - -static NTSTATUS ntlmssp_server_negotiate(struct ntlmssp_state *ntlmssp_state, - TALLOC_CTX *out_mem_ctx, - const DATA_BLOB in, DATA_BLOB *out) -{ - DATA_BLOB struct_blob; - fstring dnsname, dnsdomname; - uint32_t neg_flags = 0; - uint32_t ntlmssp_command, chal_flags; - char *cliname=NULL, *domname=NULL; - const uint8_t *cryptkey; - const char *target_name; - - /* parse the NTLMSSP packet */ -#if 0 - file_save("ntlmssp_negotiate.dat", request.data, request.length); -#endif - - if (in.length) { - if (!msrpc_parse(ntlmssp_state, - &in, "CddAA", - "NTLMSSP", - &ntlmssp_command, - &neg_flags, - &cliname, - &domname)) { - DEBUG(1, ("ntlmssp_server_negotiate: failed to parse NTLMSSP:\n")); - dump_data(2, in.data, in.length); - return NT_STATUS_INVALID_PARAMETER; - } - - debug_ntlmssp_flags(neg_flags); - } - - ntlmssp_handle_neg_flags(ntlmssp_state, neg_flags, ntlmssp_state->allow_lm_key); - - /* Ask our caller what challenge they would like in the packet */ - cryptkey = ntlmssp_state->get_challenge(ntlmssp_state); - - /* Check if we may set the challenge */ - if (!ntlmssp_state->may_set_challenge(ntlmssp_state)) { - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2; - } - - /* The flags we send back are not just the negotiated flags, - * they are also 'what is in this packet'. Therfore, we - * operate on 'chal_flags' from here on - */ - - chal_flags = ntlmssp_state->neg_flags; - - /* get the right name to fill in as 'target' */ - target_name = ntlmssp_target_name(ntlmssp_state, - neg_flags, &chal_flags); - if (target_name == NULL) - return NT_STATUS_INVALID_PARAMETER; - - ntlmssp_state->chal = data_blob_talloc(ntlmssp_state, cryptkey, 8); - ntlmssp_state->internal_chal = data_blob_talloc(ntlmssp_state, cryptkey, 8); - - /* This should be a 'netbios domain -> DNS domain' mapping */ - dnsdomname[0] = '\0'; - get_mydomname(dnsdomname); - strlower_m(dnsdomname); - - dnsname[0] = '\0'; - get_myfullname(dnsname); - - /* This creates the 'blob' of names that appears at the end of the packet */ - if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) - { - const char *target_name_dns = ""; - if (chal_flags |= NTLMSSP_TARGET_TYPE_DOMAIN) { - target_name_dns = dnsdomname; - } else if (chal_flags |= NTLMSSP_TARGET_TYPE_SERVER) { - target_name_dns = dnsname; - } - - msrpc_gen(out_mem_ctx, - &struct_blob, "aaaaa", - NTLMSSP_NAME_TYPE_DOMAIN, target_name, - NTLMSSP_NAME_TYPE_SERVER, ntlmssp_state->server_name, - NTLMSSP_NAME_TYPE_DOMAIN_DNS, dnsdomname, - NTLMSSP_NAME_TYPE_SERVER_DNS, dnsname, - 0, ""); - } else { - struct_blob = data_blob(NULL, 0); - } - - { - /* Marshel the packet in the right format, be it unicode or ASCII */ - const char *gen_string; - if (ntlmssp_state->unicode) { - gen_string = "CdUdbddB"; - } else { - gen_string = "CdAdbddB"; - } - - msrpc_gen(out_mem_ctx, - out, gen_string, - "NTLMSSP", - NTLMSSP_CHALLENGE, - target_name, - chal_flags, - cryptkey, 8, - 0, 0, - struct_blob.data, struct_blob.length); - } - - ntlmssp_state->expected_state = NTLMSSP_AUTH; - - return NT_STATUS_MORE_PROCESSING_REQUIRED; -} - -/** - * Next state function for the Authenticate packet - * - * @param ntlmssp_state NTLMSSP State - * @param request The request, as a DATA_BLOB - * @return Errors or NT_STATUS_OK. - */ - -static NTSTATUS ntlmssp_server_preauth(struct ntlmssp_state *ntlmssp_state, - const DATA_BLOB request) -{ - uint32_t ntlmssp_command, auth_flags; - NTSTATUS nt_status; - - uint8_t session_nonce_hash[16]; - - const char *parse_string; - char *domain = NULL; - char *user = NULL; - char *workstation = NULL; - -#if 0 - file_save("ntlmssp_auth.dat", request.data, request.length); -#endif - - if (ntlmssp_state->unicode) { - parse_string = "CdBBUUUBd"; - } else { - parse_string = "CdBBAAABd"; - } - - /* zero these out */ - data_blob_free(&ntlmssp_state->lm_resp); - data_blob_free(&ntlmssp_state->nt_resp); - - ntlmssp_state->user = NULL; - ntlmssp_state->domain = NULL; - ntlmssp_state->workstation = NULL; - - /* now the NTLMSSP encoded auth hashes */ - if (!msrpc_parse(ntlmssp_state, - &request, parse_string, - "NTLMSSP", - &ntlmssp_command, - &ntlmssp_state->lm_resp, - &ntlmssp_state->nt_resp, - &domain, - &user, - &workstation, - &ntlmssp_state->encrypted_session_key, - &auth_flags)) { - DEBUG(10, ("ntlmssp_server_auth: failed to parse NTLMSSP (nonfatal):\n")); - dump_data(10, request.data, request.length); - - /* zero this out */ - data_blob_free(&ntlmssp_state->encrypted_session_key); - auth_flags = 0; - - /* Try again with a shorter string (Win9X truncates this packet) */ - if (ntlmssp_state->unicode) { - parse_string = "CdBBUUU"; - } else { - parse_string = "CdBBAAA"; - } - - /* now the NTLMSSP encoded auth hashes */ - if (!msrpc_parse(ntlmssp_state, - &request, parse_string, - "NTLMSSP", - &ntlmssp_command, - &ntlmssp_state->lm_resp, - &ntlmssp_state->nt_resp, - &domain, - &user, - &workstation)) { - DEBUG(1, ("ntlmssp_server_auth: failed to parse NTLMSSP:\n")); - dump_data(2, request.data, request.length); - - return NT_STATUS_INVALID_PARAMETER; - } - } - - if (auth_flags) - ntlmssp_handle_neg_flags(ntlmssp_state, auth_flags, ntlmssp_state->allow_lm_key); - - if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, domain))) { - /* zero this out */ - data_blob_free(&ntlmssp_state->encrypted_session_key); - return nt_status; - } - - if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, user))) { - /* zero this out */ - data_blob_free(&ntlmssp_state->encrypted_session_key); - return nt_status; - } - - if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_workstation(ntlmssp_state, workstation))) { - /* zero this out */ - data_blob_free(&ntlmssp_state->encrypted_session_key); - return nt_status; - } - - DEBUG(3,("Got user=[%s] domain=[%s] workstation=[%s] len1=%lu len2=%lu\n", - ntlmssp_state->user, ntlmssp_state->domain, ntlmssp_state->workstation, (unsigned long)ntlmssp_state->lm_resp.length, (unsigned long)ntlmssp_state->nt_resp.length)); - -#if 0 - file_save("nthash1.dat", &ntlmssp_state->nt_resp.data, &ntlmssp_state->nt_resp.length); - file_save("lmhash1.dat", &ntlmssp_state->lm_resp.data, &ntlmssp_state->lm_resp.length); -#endif - - /* NTLM2 uses a 'challenge' that is made of up both the server challenge, and a - client challenge - - However, the NTLM2 flag may still be set for the real NTLMv2 logins, be careful. - */ - if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { - if (ntlmssp_state->nt_resp.length == 24 && ntlmssp_state->lm_resp.length == 24) { - struct MD5Context md5_session_nonce_ctx; - SMB_ASSERT(ntlmssp_state->internal_chal.data - && ntlmssp_state->internal_chal.length == 8); - - ntlmssp_state->doing_ntlm2 = True; - - memcpy(ntlmssp_state->session_nonce, ntlmssp_state->internal_chal.data, 8); - memcpy(&ntlmssp_state->session_nonce[8], ntlmssp_state->lm_resp.data, 8); - - MD5Init(&md5_session_nonce_ctx); - MD5Update(&md5_session_nonce_ctx, ntlmssp_state->session_nonce, 16); - MD5Final(session_nonce_hash, &md5_session_nonce_ctx); - - ntlmssp_state->chal = data_blob_talloc(ntlmssp_state, - session_nonce_hash, 8); - - /* LM response is no longer useful, zero it out */ - data_blob_free(&ntlmssp_state->lm_resp); - - /* We changed the effective challenge - set it */ - if (!NT_STATUS_IS_OK(nt_status = - ntlmssp_state->set_challenge(ntlmssp_state, - &ntlmssp_state->chal))) { - /* zero this out */ - data_blob_free(&ntlmssp_state->encrypted_session_key); - return nt_status; - } - - /* LM Key is incompatible... */ - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; - } - } - return NT_STATUS_OK; -} - -/** - * Next state function for the Authenticate packet - * (after authentication - figures out the session keys etc) - * - * @param ntlmssp_state NTLMSSP State - * @return Errors or NT_STATUS_OK. - */ - -static NTSTATUS ntlmssp_server_postauth(struct ntlmssp_state *ntlmssp_state, - DATA_BLOB *user_session_key, - DATA_BLOB *lm_session_key) -{ - NTSTATUS nt_status; - DATA_BLOB session_key = data_blob(NULL, 0); - - if (user_session_key) - dump_data_pw("USER session key:\n", user_session_key->data, user_session_key->length); - - if (lm_session_key) - dump_data_pw("LM first-8:\n", lm_session_key->data, lm_session_key->length); - - /* Handle the different session key derivation for NTLM2 */ - if (ntlmssp_state->doing_ntlm2) { - if (user_session_key && user_session_key->data && user_session_key->length == 16) { - session_key = data_blob_talloc(ntlmssp_state, NULL, 16); - hmac_md5(user_session_key->data, ntlmssp_state->session_nonce, - sizeof(ntlmssp_state->session_nonce), session_key.data); - DEBUG(10,("ntlmssp_server_auth: Created NTLM2 session key.\n")); - dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length); - - } else { - DEBUG(10,("ntlmssp_server_auth: Failed to create NTLM2 session key.\n")); - session_key = data_blob(NULL, 0); - } - } else if ((ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) - /* Ensure we can never get here on NTLMv2 */ - && (ntlmssp_state->nt_resp.length == 0 || ntlmssp_state->nt_resp.length == 24)) { - - if (lm_session_key && lm_session_key->data && lm_session_key->length >= 8) { - if (ntlmssp_state->lm_resp.data && ntlmssp_state->lm_resp.length == 24) { - session_key = data_blob_talloc(ntlmssp_state, NULL, 16); - SMBsesskeygen_lm_sess_key(lm_session_key->data, ntlmssp_state->lm_resp.data, - session_key.data); - DEBUG(10,("ntlmssp_server_auth: Created NTLM session key.\n")); - dump_data_pw("LM session key:\n", session_key.data, session_key.length); - } else { - - /* When there is no LM response, just use zeros */ - static const uint8_t zeros[24]; - session_key = data_blob_talloc(ntlmssp_state, NULL, 16); - SMBsesskeygen_lm_sess_key(zeros, zeros, - session_key.data); - DEBUG(10,("ntlmssp_server_auth: Created NTLM session key.\n")); - dump_data_pw("LM session key:\n", session_key.data, session_key.length); - } - } else { - /* LM Key not selected */ - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; - - DEBUG(10,("ntlmssp_server_auth: Failed to create NTLM session key.\n")); - session_key = data_blob(NULL, 0); - } - - } else if (user_session_key && user_session_key->data) { - session_key = *user_session_key; - DEBUG(10,("ntlmssp_server_auth: Using unmodified nt session key.\n")); - dump_data_pw("unmodified session key:\n", session_key.data, session_key.length); - - /* LM Key not selected */ - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; - - } else if (lm_session_key && lm_session_key->data) { - /* Very weird to have LM key, but no user session key, but anyway.. */ - session_key = *lm_session_key; - DEBUG(10,("ntlmssp_server_auth: Using unmodified lm session key.\n")); - dump_data_pw("unmodified session key:\n", session_key.data, session_key.length); - - /* LM Key not selected */ - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; - - } else { - DEBUG(10,("ntlmssp_server_auth: Failed to create unmodified session key.\n")); - session_key = data_blob(NULL, 0); - - /* LM Key not selected */ - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; - } - - /* With KEY_EXCH, the client supplies the proposed session key, - but encrypts it with the long-term key */ - if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) { - if (!ntlmssp_state->encrypted_session_key.data - || ntlmssp_state->encrypted_session_key.length != 16) { - data_blob_free(&ntlmssp_state->encrypted_session_key); - DEBUG(1, ("Client-supplied KEY_EXCH session key was of invalid length (%u)!\n", - ntlmssp_state->encrypted_session_key.length)); - return NT_STATUS_INVALID_PARAMETER; - } else if (!session_key.data || session_key.length != 16) { - DEBUG(5, ("server session key is invalid (len == %u), cannot do KEY_EXCH!\n", - session_key.length)); - ntlmssp_state->session_key = session_key; - } else { - dump_data_pw("KEY_EXCH session key (enc):\n", - ntlmssp_state->encrypted_session_key.data, - ntlmssp_state->encrypted_session_key.length); - arcfour_crypt(ntlmssp_state->encrypted_session_key.data, - session_key.data, - ntlmssp_state->encrypted_session_key.length); - ntlmssp_state->session_key = data_blob_talloc(ntlmssp_state, - ntlmssp_state->encrypted_session_key.data, - ntlmssp_state->encrypted_session_key.length); - dump_data_pw("KEY_EXCH session key:\n", ntlmssp_state->encrypted_session_key.data, - ntlmssp_state->encrypted_session_key.length); - } - } else { - ntlmssp_state->session_key = session_key; - } - - /* The server might need us to use a partial-strength session key */ - ntlmssp_weaken_keys(ntlmssp_state); - - nt_status = ntlmssp_sign_init(ntlmssp_state); - - data_blob_free(&ntlmssp_state->encrypted_session_key); - - /* allow arbitarily many authentications, but watch that this will cause a - memory leak, until the ntlmssp_state is shutdown - */ - - if (ntlmssp_state->server_multiple_authentications) { - ntlmssp_state->expected_state = NTLMSSP_AUTH; - } else { - ntlmssp_state->expected_state = NTLMSSP_DONE; - } - - return nt_status; -} - - -/** - * Next state function for the Authenticate packet - * - * @param ntlmssp_state NTLMSSP State - * @param in The packet in from the NTLMSSP partner, as a DATA_BLOB - * @param out The reply, as an allocated DATA_BLOB, caller to free. - * @return Errors, NT_STATUS_MORE_PROCESSING_REQUIRED or NT_STATUS_OK. - */ - -static NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state, - TALLOC_CTX *out_mem_ctx, - const DATA_BLOB in, DATA_BLOB *out) -{ - DATA_BLOB user_session_key = data_blob(NULL, 0); - DATA_BLOB lm_session_key = data_blob(NULL, 0); - NTSTATUS nt_status; - - /* zero the outbound NTLMSSP packet */ - *out = data_blob_talloc(out_mem_ctx, NULL, 0); - - if (!NT_STATUS_IS_OK(nt_status = ntlmssp_server_preauth(ntlmssp_state, in))) { - return nt_status; - } - - /* - * Note we don't check here for NTLMv2 auth settings. If NTLMv2 auth - * is required (by "ntlm auth = no" and "lm auth = no" being set in the - * smb.conf file) and no NTLMv2 response was sent then the password check - * will fail here. JRA. - */ - - /* Finally, actually ask if the password is OK */ - - if (!NT_STATUS_IS_OK(nt_status = ntlmssp_state->check_password(ntlmssp_state, - &user_session_key, &lm_session_key))) { - return nt_status; - } - - if (ntlmssp_state->server_use_session_keys) { - return ntlmssp_server_postauth(ntlmssp_state, &user_session_key, &lm_session_key); - } else { - ntlmssp_state->session_key = data_blob(NULL, 0); - return NT_STATUS_OK; - } -} - -/** - * Create an NTLMSSP state machine - * - * @param ntlmssp_state NTLMSSP State, allocated by this function - */ - -NTSTATUS ntlmssp_server_start(TALLOC_CTX *mem_ctx, struct ntlmssp_state **ntlmssp_state) -{ - *ntlmssp_state = talloc(mem_ctx, struct ntlmssp_state); - if (!*ntlmssp_state) { - DEBUG(0,("ntlmssp_server_start: talloc failed!\n")); - return NT_STATUS_NO_MEMORY; - } - ZERO_STRUCTP(*ntlmssp_state); - - (*ntlmssp_state)->role = NTLMSSP_SERVER; - - (*ntlmssp_state)->get_challenge = get_challenge; - (*ntlmssp_state)->set_challenge = set_challenge; - (*ntlmssp_state)->may_set_challenge = may_set_challenge; - - (*ntlmssp_state)->workstation = NULL; - (*ntlmssp_state)->server_name = lp_netbios_name(); - - (*ntlmssp_state)->get_domain = lp_workgroup; - (*ntlmssp_state)->server_role = ROLE_DOMAIN_MEMBER; /* a good default */ - - (*ntlmssp_state)->expected_state = NTLMSSP_NEGOTIATE; - - (*ntlmssp_state)->allow_lm_key = (lp_lanman_auth() - && lp_parm_bool(-1, "ntlmssp_server", "allow_lm_key", False)); - - (*ntlmssp_state)->server_use_session_keys = True; - (*ntlmssp_state)->server_multiple_authentications = False; - - (*ntlmssp_state)->ref_count = 1; - - (*ntlmssp_state)->neg_flags = - NTLMSSP_NEGOTIATE_NTLM; - - if (lp_parm_bool(-1, "ntlmssp_server", "128bit", True)) { - (*ntlmssp_state)->neg_flags |= NTLMSSP_NEGOTIATE_128; - } - - if (lp_parm_bool(-1, "ntlmssp_server", "keyexchange", True)) { - (*ntlmssp_state)->neg_flags |= NTLMSSP_NEGOTIATE_KEY_EXCH; - } - - if (lp_parm_bool(-1, "ntlmssp_server", "ntlm2", True)) { - (*ntlmssp_state)->neg_flags |= NTLMSSP_NEGOTIATE_NTLM2; - } - - return NT_STATUS_OK; -} - -/********************************************************************* - Client side NTLMSSP -*********************************************************************/ - -/** - * Next state function for the Initial packet - * - * @param ntlmssp_state NTLMSSP State - * @param out_mem_ctx The DATA_BLOB *out will be allocated on this context - * @param in The request, as a DATA_BLOB. reply.data must be NULL - * @param out The reply, as an talloc()ed DATA_BLOB, on out_mem_ctx - * @return Errors or NT_STATUS_OK. - */ - -static NTSTATUS ntlmssp_client_initial(struct ntlmssp_state *ntlmssp_state, - TALLOC_CTX *out_mem_ctx, - DATA_BLOB in, DATA_BLOB *out) -{ - if (ntlmssp_state->unicode) { - ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE; - } else { - ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_OEM; - } - - if (ntlmssp_state->use_ntlmv2) { - ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_NTLM2; - } - - /* generate the ntlmssp negotiate packet */ - msrpc_gen(out_mem_ctx, - out, "CddAA", - "NTLMSSP", - NTLMSSP_NEGOTIATE, - ntlmssp_state->neg_flags, - ntlmssp_state->get_domain(), - ntlmssp_state->workstation); - - ntlmssp_state->expected_state = NTLMSSP_CHALLENGE; - - return NT_STATUS_MORE_PROCESSING_REQUIRED; -} - -/** - * Next state function for the Challenge Packet. Generate an auth packet. - * - * @param ntlmssp_state NTLMSSP State - * @param request The request, as a DATA_BLOB. reply.data must be NULL - * @param request The reply, as an allocated DATA_BLOB, caller to free. - * @return Errors or NT_STATUS_OK. - */ - -static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_state *ntlmssp_state, - TALLOC_CTX *out_mem_ctx, - const DATA_BLOB in, DATA_BLOB *out) -{ - uint32_t chal_flags, ntlmssp_command, unkn1, unkn2; - DATA_BLOB server_domain_blob; - DATA_BLOB challenge_blob; - DATA_BLOB struct_blob = data_blob(NULL, 0); - char *server_domain; - const char *chal_parse_string; - const char *auth_gen_string; - uint8_t lm_hash[16]; - DATA_BLOB lm_response = data_blob(NULL, 0); - DATA_BLOB nt_response = data_blob(NULL, 0); - DATA_BLOB session_key = data_blob(NULL, 0); - DATA_BLOB lm_session_key = data_blob(NULL, 0); - DATA_BLOB encrypted_session_key = data_blob(NULL, 0); - NTSTATUS nt_status; - - if (!msrpc_parse(ntlmssp_state, - &in, "CdBd", - "NTLMSSP", - &ntlmssp_command, - &server_domain_blob, - &chal_flags)) { - DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#1)\n")); - dump_data(2, in.data, in.length); - - return NT_STATUS_INVALID_PARAMETER; - } - - data_blob_free(&server_domain_blob); - - DEBUG(3, ("Got challenge flags:\n")); - debug_ntlmssp_flags(chal_flags); - - ntlmssp_handle_neg_flags(ntlmssp_state, chal_flags, ntlmssp_state->allow_lm_key); - - if (ntlmssp_state->unicode) { - if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) { - chal_parse_string = "CdUdbddB"; - } else { - chal_parse_string = "CdUdbdd"; - } - auth_gen_string = "CdBBUUUBd"; - } else { - if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) { - chal_parse_string = "CdAdbddB"; - } else { - chal_parse_string = "CdAdbdd"; - } - - auth_gen_string = "CdBBAAABd"; - } - - DEBUG(3, ("NTLMSSP: Set final flags:\n")); - debug_ntlmssp_flags(ntlmssp_state->neg_flags); - - if (!msrpc_parse(ntlmssp_state, - &in, chal_parse_string, - "NTLMSSP", - &ntlmssp_command, - &server_domain, - &chal_flags, - &challenge_blob, 8, - &unkn1, &unkn2, - &struct_blob)) { - DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#2)\n")); - dump_data(2, in.data, in.length); - return NT_STATUS_INVALID_PARAMETER; - } - - ntlmssp_state->server_domain = server_domain; - - if (challenge_blob.length != 8) { - return NT_STATUS_INVALID_PARAMETER; - } - - if (!ntlmssp_state->password) { - static const uint8_t zeros[16]; - /* do nothing - blobs are zero length */ - - /* session key is all zeros */ - session_key = data_blob_talloc(ntlmssp_state, zeros, 16); - lm_session_key = data_blob_talloc(ntlmssp_state, zeros, 16); - - /* not doing NLTM2 without a password */ - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2; - } else if (ntlmssp_state->use_ntlmv2) { - - if (!struct_blob.length) { - /* be lazy, match win2k - we can't do NTLMv2 without it */ - DEBUG(1, ("Server did not provide 'target information', required for NTLMv2\n")); - return NT_STATUS_INVALID_PARAMETER; - } - - /* TODO: if the remote server is standalone, then we should replace 'domain' - with the server name as supplied above */ - - if (!SMBNTLMv2encrypt(ntlmssp_state->user, - ntlmssp_state->domain, - ntlmssp_state->password, &challenge_blob, - &struct_blob, - &lm_response, &nt_response, - NULL, &session_key)) { - data_blob_free(&challenge_blob); - data_blob_free(&struct_blob); - return NT_STATUS_NO_MEMORY; - } - - /* LM Key is incompatible... */ - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; - - } else if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { - struct MD5Context md5_session_nonce_ctx; - uint8_t nt_hash[16]; - uint8_t session_nonce[16]; - uint8_t session_nonce_hash[16]; - uint8_t user_session_key[16]; - E_md4hash(ntlmssp_state->password, nt_hash); - - lm_response = data_blob_talloc(ntlmssp_state, NULL, 24); - generate_random_buffer(lm_response.data, 8); - memset(lm_response.data+8, 0, 16); - - memcpy(session_nonce, challenge_blob.data, 8); - memcpy(&session_nonce[8], lm_response.data, 8); - - MD5Init(&md5_session_nonce_ctx); - MD5Update(&md5_session_nonce_ctx, challenge_blob.data, 8); - MD5Update(&md5_session_nonce_ctx, lm_response.data, 8); - MD5Final(session_nonce_hash, &md5_session_nonce_ctx); - - DEBUG(5, ("NTLMSSP challenge set by NTLM2\n")); - DEBUG(5, ("challenge is: \n")); - dump_data(5, session_nonce_hash, 8); - - nt_response = data_blob_talloc(ntlmssp_state, NULL, 24); - SMBNTencrypt(ntlmssp_state->password, - session_nonce_hash, - nt_response.data); - - session_key = data_blob_talloc(ntlmssp_state, NULL, 16); - - SMBsesskeygen_ntv1(nt_hash, user_session_key); - hmac_md5(user_session_key, session_nonce, sizeof(session_nonce), session_key.data); - dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length); - - /* LM Key is incompatible... */ - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; - } else { - uint8_t nt_hash[16]; - - if (ntlmssp_state->use_nt_response) { - nt_response = data_blob_talloc(ntlmssp_state, NULL, 24); - SMBNTencrypt(ntlmssp_state->password,challenge_blob.data, - nt_response.data); - E_md4hash(ntlmssp_state->password, nt_hash); - session_key = data_blob_talloc(ntlmssp_state, NULL, 16); - SMBsesskeygen_ntv1(nt_hash, session_key.data); - dump_data_pw("NT session key:\n", session_key.data, session_key.length); - } - - /* lanman auth is insecure, it may be disabled */ - if (lp_client_lanman_auth()) { - lm_response = data_blob_talloc(ntlmssp_state, NULL, 24); - if (!SMBencrypt(ntlmssp_state->password,challenge_blob.data, - lm_response.data)) { - /* If the LM password was too long (and therefore the LM hash being - of the first 14 chars only), don't send it */ - data_blob_free(&lm_response); - - /* LM Key is incompatible with 'long' passwords */ - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; - } else { - E_deshash(ntlmssp_state->password, lm_hash); - lm_session_key = data_blob_talloc(ntlmssp_state, NULL, 16); - memcpy(lm_session_key.data, lm_hash, 8); - memset(&lm_session_key.data[8], '\0', 8); - - if (!ntlmssp_state->use_nt_response) { - session_key = lm_session_key; - } - } - } else { - /* LM Key is incompatible... */ - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; - } - } - - if ((ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) - && lp_client_lanman_auth() && lm_session_key.length == 16) { - DATA_BLOB new_session_key = data_blob_talloc(ntlmssp_state, NULL, 16); - if (lm_response.length == 24) { - SMBsesskeygen_lm_sess_key(lm_session_key.data, lm_response.data, - new_session_key.data); - } else { - static const uint8_t zeros[24]; - SMBsesskeygen_lm_sess_key(lm_session_key.data, zeros, - new_session_key.data); - } - new_session_key.length = 16; - session_key = new_session_key; - dump_data_pw("LM session key\n", session_key.data, session_key.length); - } - - - /* Key exchange encryptes a new client-generated session key with - the password-derived key */ - if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) { - /* Make up a new session key */ - uint8_t client_session_key[16]; - generate_random_buffer(client_session_key, sizeof(client_session_key)); - - /* Encrypt the new session key with the old one */ - encrypted_session_key = data_blob_talloc(ntlmssp_state, - client_session_key, sizeof(client_session_key)); - dump_data_pw("KEY_EXCH session key:\n", encrypted_session_key.data, encrypted_session_key.length); - arcfour_crypt(encrypted_session_key.data, session_key.data, encrypted_session_key.length); - dump_data_pw("KEY_EXCH session key (enc):\n", encrypted_session_key.data, encrypted_session_key.length); - - /* Mark the new session key as the 'real' session key */ - session_key = data_blob_talloc(ntlmssp_state, client_session_key, sizeof(client_session_key)); - } - - /* this generates the actual auth packet */ - if (!msrpc_gen(out_mem_ctx, - out, auth_gen_string, - "NTLMSSP", - NTLMSSP_AUTH, - lm_response.data, lm_response.length, - nt_response.data, nt_response.length, - ntlmssp_state->domain, - ntlmssp_state->user, - ntlmssp_state->workstation, - encrypted_session_key.data, encrypted_session_key.length, - ntlmssp_state->neg_flags)) { - - return NT_STATUS_NO_MEMORY; - } - - ntlmssp_state->session_key = session_key; - - /* The client might be using 56 or 40 bit weakened keys */ - ntlmssp_weaken_keys(ntlmssp_state); - - ntlmssp_state->chal = challenge_blob; - ntlmssp_state->lm_resp = lm_response; - ntlmssp_state->nt_resp = nt_response; - - ntlmssp_state->expected_state = NTLMSSP_DONE; - - nt_status = ntlmssp_sign_init(ntlmssp_state); - if (!NT_STATUS_IS_OK(nt_status)) { - DEBUG(1, ("Could not setup NTLMSSP signing/sealing system (error was: %s)\n", - nt_errstr(nt_status))); - return nt_status; - } - - return nt_status; -} - -NTSTATUS ntlmssp_client_start(TALLOC_CTX *mem_ctx, struct ntlmssp_state **ntlmssp_state) -{ - *ntlmssp_state = talloc(mem_ctx, struct ntlmssp_state); - if (!*ntlmssp_state) { - DEBUG(0,("ntlmssp_client_start: talloc failed!\n")); - return NT_STATUS_NO_MEMORY; - } - ZERO_STRUCTP(*ntlmssp_state); - - (*ntlmssp_state)->role = NTLMSSP_CLIENT; - - (*ntlmssp_state)->workstation = lp_netbios_name(); - (*ntlmssp_state)->get_domain = lp_workgroup; - - (*ntlmssp_state)->unicode = lp_parm_bool(-1, "ntlmssp_client", "unicode", True); - - (*ntlmssp_state)->use_nt_response = lp_parm_bool(-1, "ntlmssp_client", "send_nt_reponse", True); - - (*ntlmssp_state)->allow_lm_key = (lp_lanman_auth() - && lp_parm_bool(-1, "ntlmssp_client", "allow_lm_key", False)); - - (*ntlmssp_state)->use_ntlmv2 = lp_client_ntlmv2_auth(); - - (*ntlmssp_state)->expected_state = NTLMSSP_INITIAL; - - (*ntlmssp_state)->ref_count = 1; - - (*ntlmssp_state)->neg_flags = - NTLMSSP_NEGOTIATE_NTLM | - NTLMSSP_REQUEST_TARGET; - - if (lp_parm_bool(-1, "ntlmssp_client", "128bit", True)) { - (*ntlmssp_state)->neg_flags |= NTLMSSP_NEGOTIATE_128; - } - - if (lp_parm_bool(-1, "ntlmssp_client", "keyexchange", True)) { - (*ntlmssp_state)->neg_flags |= NTLMSSP_NEGOTIATE_KEY_EXCH; - } - - if (lp_parm_bool(-1, "ntlmssp_client", "ntlm2", True)) { - (*ntlmssp_state)->neg_flags |= NTLMSSP_NEGOTIATE_NTLM2; - } else { - /* apparently we can't do ntlmv2 if we don't do ntlm2 */ - (*ntlmssp_state)->use_ntlmv2 = False; - } - - return NT_STATUS_OK; -} - diff --git a/source4/libcli/auth/ntlmssp.h b/source4/libcli/auth/ntlmssp.h deleted file mode 100644 index e17c133c8b..0000000000 --- a/source4/libcli/auth/ntlmssp.h +++ /dev/null @@ -1,190 +0,0 @@ -/* - Unix SMB/CIFS implementation. - SMB parameters and setup - Copyright (C) Andrew Tridgell 1992-1997 - Copyright (C) Luke Kenneth Casson Leighton 1996-1997 - Copyright (C) Paul Ashton 1997 - - 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 "librpc/gen_ndr/ndr_samr.h" - -/* NTLMSSP mode */ -enum ntlmssp_role -{ - NTLMSSP_SERVER, - NTLMSSP_CLIENT -}; - -/* NTLMSSP message types */ -enum ntlmssp_message_type -{ - NTLMSSP_INITIAL = 0 /* samba internal state */, - NTLMSSP_NEGOTIATE = 1, - NTLMSSP_CHALLENGE = 2, - NTLMSSP_AUTH = 3, - NTLMSSP_UNKNOWN = 4, - NTLMSSP_DONE = 5 /* samba final state */ -}; - -/* NTLMSSP negotiation flags */ -#define NTLMSSP_NEGOTIATE_UNICODE 0x00000001 -#define NTLMSSP_NEGOTIATE_OEM 0x00000002 -#define NTLMSSP_REQUEST_TARGET 0x00000004 -#define NTLMSSP_NEGOTIATE_SIGN 0x00000010 /* Message integrity */ -#define NTLMSSP_NEGOTIATE_SEAL 0x00000020 /* Message confidentiality */ -#define NTLMSSP_NEGOTIATE_DATAGRAM_STYLE 0x00000040 -#define NTLMSSP_NEGOTIATE_LM_KEY 0x00000080 -#define NTLMSSP_NEGOTIATE_NETWARE 0x00000100 -#define NTLMSSP_NEGOTIATE_NTLM 0x00000200 -#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x00001000 -#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x00002000 -#define NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL 0x00004000 -#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x00008000 -#define NTLMSSP_TARGET_TYPE_DOMAIN 0x10000 -#define NTLMSSP_TARGET_TYPE_SERVER 0x20000 -#define NTLMSSP_CHAL_INIT_RESPONSE 0x00010000 - -#define NTLMSSP_CHAL_ACCEPT_RESPONSE 0x00020000 -#define NTLMSSP_CHAL_NON_NT_SESSION_KEY 0x00040000 -#define NTLMSSP_NEGOTIATE_NTLM2 0x00080000 -#define NTLMSSP_CHAL_TARGET_INFO 0x00800000 -#define NTLMSSP_NEGOTIATE_128 0x20000000 /* 128-bit encryption */ -#define NTLMSSP_NEGOTIATE_KEY_EXCH 0x40000000 -#define NTLMSSP_NEGOTIATE_56 0x80000000 - -#define NTLMSSP_NAME_TYPE_SERVER 0x01 -#define NTLMSSP_NAME_TYPE_DOMAIN 0x02 -#define NTLMSSP_NAME_TYPE_SERVER_DNS 0x03 -#define NTLMSSP_NAME_TYPE_DOMAIN_DNS 0x04 - -#define NTLMSSP_SIGN_VERSION 1 - -#define NTLMSSP_SIG_SIZE 16 - -struct ntlmssp_state -{ - uint_t ref_count; - enum ntlmssp_role role; - enum samr_Role server_role; - uint32_t expected_state; - - BOOL unicode; - BOOL use_ntlmv2; - BOOL use_nt_response; /* Set to 'False' to debug what happens when the NT response is omited */ - BOOL allow_lm_key; /* The LM_KEY code is not functional at this point, and it's not - very secure anyway */ - - BOOL server_use_session_keys; /* Set to 'False' for authentication only, - that will never return a session key */ - BOOL server_multiple_authentications; /* Set to 'True' to allow squid 2.5 - style 'challenge caching' */ - - char *user; - char *domain; - const char *workstation; - char *password; - char *server_domain; - - DATA_BLOB internal_chal; /* Random challenge as supplied to the client for NTLM authentication */ - - DATA_BLOB chal; /* Random challenge as input into the actual NTLM (or NTLM2) authentication */ - DATA_BLOB lm_resp; - DATA_BLOB nt_resp; - DATA_BLOB session_key; - - uint32_t neg_flags; /* the current state of negotiation with the NTLMSSP partner */ - - /* internal variables used by NTLM2 */ - BOOL doing_ntlm2; - uint8_t session_nonce[16]; - - /* internal variables used by KEY_EXCH (client-supplied user session key */ - DATA_BLOB encrypted_session_key; - - void *auth_context; - - /** - * Callback to get the 'challenge' used for NTLM authentication. - * - * @param ntlmssp_state This structure - * @return 8 bytes of challenge data, determined by the server to be the challenge for NTLM authentication - * - */ - const uint8_t *(*get_challenge)(const struct ntlmssp_state *ntlmssp_state); - - /** - * Callback to find if the challenge used by NTLM authentication may be modified - * - * The NTLM2 authentication scheme modifies the effective challenge, but this is not compatiable with the - * current 'security=server' implementation.. - * - * @param ntlmssp_state This structure - * @return Can the challenge be set to arbitary values? - * - */ - BOOL (*may_set_challenge)(const struct ntlmssp_state *ntlmssp_state); - - /** - * Callback to set the 'challenge' used for NTLM authentication. - * - * The callback may use the void *auth_context to store state information, but the same value is always available - * from the DATA_BLOB chal on this structure. - * - * @param ntlmssp_state This structure - * @param challange 8 bytes of data, agreed by the client and server to be the effective challenge for NTLM2 authentication - * - */ - NTSTATUS (*set_challenge)(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *challenge); - - /** - * Callback to check the user's password. - * - * The callback must reads the feilds of this structure for the information it needs on the user - * @param ntlmssp_state This structure - * @param nt_session_key If an NT session key is returned by the authentication process, return it here - * @param lm_session_key If an LM session key is returned by the authentication process, return it here - * - */ - NTSTATUS (*check_password)(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *nt_session_key, DATA_BLOB *lm_session_key); - - const char *server_name; - const char *(*get_domain)(void); - - /* SMB Signing */ - uint32_t ntlm_seq_num; - uint32_t ntlm2_send_seq_num; - uint32_t ntlm2_recv_seq_num; - - /* ntlmv2 */ - DATA_BLOB send_sign_key; - DATA_BLOB send_seal_key; - DATA_BLOB recv_sign_key; - DATA_BLOB recv_seal_key; - - uint8_t send_seal_hash[258]; - uint8_t recv_seal_hash[258]; - - /* ntlmv1 */ - uint8_t ntlmssp_hash[258]; - - /* it turns out that we don't always get the - response in at the time we want to process it. - Store it here, until we need it */ - DATA_BLOB stored_response; - -}; - diff --git a/source4/libcli/auth/ntlmssp_parse.c b/source4/libcli/auth/ntlmssp_parse.c deleted file mode 100644 index 42546cb130..0000000000 --- a/source4/libcli/auth/ntlmssp_parse.c +++ /dev/null @@ -1,338 +0,0 @@ -/* - Unix SMB/CIFS implementation. - simple kerberos5/SPNEGO routines - Copyright (C) Andrew Tridgell 2001 - Copyright (C) Jim McDonough 2002 - Copyright (C) Andrew Bartlett 2002-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" -#include "pstring.h" - -/* - this is a tiny msrpc packet generator. I am only using this to - avoid tying this code to a particular varient of our rpc code. This - generator is not general enough for all our rpc needs, its just - enough for the spnego/ntlmssp code - - format specifiers are: - - U = unicode string (input is unix string) - a = address (input is char *unix_string) - (1 byte type, 1 byte length, unicode/ASCII string, all inline) - A = ASCII string (input is unix string) - B = data blob (pointer + length) - b = data blob in header (pointer + length) - D - d = word (4 bytes) - C = constant ascii string - */ -BOOL msrpc_gen(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, - const char *format, ...) -{ - int i; - ssize_t n; - va_list ap; - char *s; - uint8_t *b; - int head_size=0, data_size=0; - int head_ofs, data_ofs; - int *intargs; - - DATA_BLOB *pointers; - - pointers = talloc_array(mem_ctx, DATA_BLOB, strlen(format)); - intargs = talloc_array(pointers, int, strlen(format)); - - /* first scan the format to work out the header and body size */ - va_start(ap, format); - for (i=0; format[i]; i++) { - switch (format[i]) { - case 'U': - s = va_arg(ap, char *); - head_size += 8; - n = push_ucs2_talloc(pointers, (void **)&pointers[i].data, s); - if (n == -1) { - return False; - } - pointers[i].length = n; - pointers[i].length -= 2; - data_size += pointers[i].length; - break; - case 'A': - s = va_arg(ap, char *); - head_size += 8; - n = push_ascii_talloc(pointers, (char **)&pointers[i].data, s); - if (n == -1) { - return False; - } - pointers[i].length = n; - pointers[i].length -= 1; - data_size += pointers[i].length; - break; - case 'a': - n = va_arg(ap, int); - intargs[i] = n; - s = va_arg(ap, char *); - n = push_ucs2_talloc(pointers, (void **)&pointers[i].data, s); - if (n == -1) { - return False; - } - pointers[i].length = n; - pointers[i].length -= 2; - data_size += pointers[i].length + 4; - break; - case 'B': - b = va_arg(ap, uint8_t *); - head_size += 8; - pointers[i].data = b; - pointers[i].length = va_arg(ap, int); - data_size += pointers[i].length; - break; - case 'b': - b = va_arg(ap, uint8_t *); - pointers[i].data = b; - pointers[i].length = va_arg(ap, int); - head_size += pointers[i].length; - break; - case 'd': - n = va_arg(ap, int); - intargs[i] = n; - head_size += 4; - break; - case 'C': - s = va_arg(ap, char *); - pointers[i].data = (uint8_t *)s; - pointers[i].length = strlen(s)+1; - head_size += pointers[i].length; - break; - } - } - va_end(ap); - - /* allocate the space, then scan the format again to fill in the values */ - *blob = data_blob_talloc(mem_ctx, NULL, head_size + data_size); - - head_ofs = 0; - data_ofs = head_size; - - va_start(ap, format); - for (i=0; format[i]; i++) { - switch (format[i]) { - case 'U': - case 'A': - case 'B': - n = pointers[i].length; - SSVAL(blob->data, head_ofs, n); head_ofs += 2; - SSVAL(blob->data, head_ofs, n); head_ofs += 2; - SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4; - if (pointers[i].data && n) /* don't follow null pointers... */ - memcpy(blob->data+data_ofs, pointers[i].data, n); - data_ofs += n; - break; - case 'a': - n = intargs[i]; - SSVAL(blob->data, data_ofs, n); data_ofs += 2; - - n = pointers[i].length; - SSVAL(blob->data, data_ofs, n); data_ofs += 2; - if (n >= 0) { - memcpy(blob->data+data_ofs, pointers[i].data, n); - } - data_ofs += n; - break; - case 'd': - n = intargs[i]; - SIVAL(blob->data, head_ofs, n); - head_ofs += 4; - break; - case 'b': - n = pointers[i].length; - memcpy(blob->data + head_ofs, pointers[i].data, n); - head_ofs += n; - break; - case 'C': - n = pointers[i].length; - memcpy(blob->data + head_ofs, pointers[i].data, n); - head_ofs += n; - break; - } - } - va_end(ap); - - talloc_free(pointers); - - return True; -} - - -/* a helpful macro to avoid running over the end of our blob */ -#define NEED_DATA(amount) \ -if ((head_ofs + amount) > blob->length) { \ - return False; \ -} - -/* - this is a tiny msrpc packet parser. This the the partner of msrpc_gen - - format specifiers are: - - U = unicode string (output is unix string) - A = ascii string - B = data blob - b = data blob in header - d = word (4 bytes) - C = constant ascii string - */ - -BOOL msrpc_parse(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob, - const char *format, ...) -{ - int i; - va_list ap; - const char **ps, *s; - DATA_BLOB *b; - size_t head_ofs = 0; - uint16_t len1, len2; - uint32_t ptr; - uint32_t *v; - pstring p; - - va_start(ap, format); - for (i=0; format[i]; i++) { - switch (format[i]) { - case 'U': - NEED_DATA(8); - len1 = SVAL(blob->data, head_ofs); head_ofs += 2; - len2 = SVAL(blob->data, head_ofs); head_ofs += 2; - ptr = IVAL(blob->data, head_ofs); head_ofs += 4; - - ps = (const char **)va_arg(ap, char **); - if (len1 == 0 && len2 == 0) { - *ps = ""; - } else { - /* make sure its in the right format - be strict */ - if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) { - return False; - } - if (len1 & 1) { - /* if odd length and unicode */ - return False; - } - if (blob->data + ptr < (uint8_t *)ptr || blob->data + ptr < blob->data) - return False; - - if (0 < len1) { - pull_string(p, blob->data + ptr, sizeof(p), - len1, - STR_UNICODE|STR_NOALIGN); - (*ps) = talloc_strdup(mem_ctx, p); - if (!(*ps)) { - return False; - } - } else { - (*ps) = ""; - } - } - break; - case 'A': - NEED_DATA(8); - len1 = SVAL(blob->data, head_ofs); head_ofs += 2; - len2 = SVAL(blob->data, head_ofs); head_ofs += 2; - ptr = IVAL(blob->data, head_ofs); head_ofs += 4; - - ps = (const char **)va_arg(ap, char **); - /* make sure its in the right format - be strict */ - if (len1 == 0 && len2 == 0) { - *ps = ""; - } else { - if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) { - return False; - } - - if (blob->data + ptr < (uint8_t *)ptr || blob->data + ptr < blob->data) - return False; - - if (0 < len1) { - pull_string(p, blob->data + ptr, sizeof(p), - len1, - STR_ASCII|STR_NOALIGN); - (*ps) = talloc_strdup(mem_ctx, p); - if (!(*ps)) { - return False; - } - } else { - (*ps) = ""; - } - } - break; - case 'B': - NEED_DATA(8); - len1 = SVAL(blob->data, head_ofs); head_ofs += 2; - len2 = SVAL(blob->data, head_ofs); head_ofs += 2; - ptr = IVAL(blob->data, head_ofs); head_ofs += 4; - - b = (DATA_BLOB *)va_arg(ap, void *); - if (len1 == 0 && len2 == 0) { - *b = data_blob_talloc(mem_ctx, NULL, 0); - } else { - /* make sure its in the right format - be strict */ - if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) { - return False; - } - - if (blob->data + ptr < (uint8_t *)ptr || blob->data + ptr < blob->data) - return False; - - *b = data_blob_talloc(mem_ctx, blob->data + ptr, len1); - } - break; - case 'b': - b = (DATA_BLOB *)va_arg(ap, void *); - len1 = va_arg(ap, uint_t); - /* make sure its in the right format - be strict */ - NEED_DATA(len1); - if (blob->data + head_ofs < (uint8_t *)head_ofs || blob->data + head_ofs < blob->data) - return False; - - *b = data_blob_talloc(mem_ctx, blob->data + head_ofs, len1); - head_ofs += len1; - break; - case 'd': - v = va_arg(ap, uint32_t *); - NEED_DATA(4); - *v = IVAL(blob->data, head_ofs); head_ofs += 4; - break; - case 'C': - s = va_arg(ap, char *); - - if (blob->data + head_ofs < (uint8_t *)head_ofs || blob->data + head_ofs < blob->data) - return False; - - head_ofs += pull_string(p, blob->data+head_ofs, sizeof(p), - blob->length - head_ofs, - STR_ASCII|STR_TERMINATE); - if (strcmp(s, p) != 0) { - return False; - } - break; - } - } - va_end(ap); - - return True; -} diff --git a/source4/libcli/auth/ntlmssp_sign.c b/source4/libcli/auth/ntlmssp_sign.c deleted file mode 100644 index 347a85da77..0000000000 --- a/source4/libcli/auth/ntlmssp_sign.c +++ /dev/null @@ -1,449 +0,0 @@ -/* - * Unix SMB/CIFS implementation. - * Version 3.0 - * NTLMSSP Signing routines - * Copyright (C) Luke Kenneth Casson Leighton 1996-2001 - * Copyright (C) Andrew Bartlett 2003-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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include "includes.h" -#include "auth/auth.h" -#include "lib/crypto/crypto.h" - -#define CLI_SIGN "session key to client-to-server signing key magic constant" -#define CLI_SEAL "session key to client-to-server sealing key magic constant" -#define SRV_SIGN "session key to server-to-client signing key magic constant" -#define SRV_SEAL "session key to server-to-client sealing key magic constant" - -/** - * Some notes on then NTLM2 code: - * - * This code works correctly for the sealing part of the problem. If - * we disable the check for valid client signatures, then we see that - * the output of a rpcecho 'sinkdata' at smbd is correct. We get the - * valid data, and it is validly decrypted. - * - * This means that the quantity of data passing though the RC4 sealing - * pad is correct. - * - * This code also correctly matches test values that I have obtained, - * claiming to be the correct output of NTLM2 signature generation. - * - */ - -static void calc_ntlmv2_key(TALLOC_CTX *mem_ctx, - DATA_BLOB *subkey, - DATA_BLOB session_key, - const char *constant) -{ - struct MD5Context ctx3; - *subkey = data_blob_talloc(mem_ctx, NULL, 16); - MD5Init(&ctx3); - MD5Update(&ctx3, session_key.data, session_key.length); - MD5Update(&ctx3, constant, strlen(constant)+1); - MD5Final(subkey->data, &ctx3); -} - -enum ntlmssp_direction { - NTLMSSP_SEND, - NTLMSSP_RECEIVE -}; - -static NTSTATUS ntlmssp_make_packet_signature(struct ntlmssp_state *ntlmssp_state, - TALLOC_CTX *sig_mem_ctx, - const uint8_t *data, size_t length, - const uint8_t *whole_pdu, size_t pdu_length, - enum ntlmssp_direction direction, - DATA_BLOB *sig, BOOL encrypt_sig) -{ - if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { - - HMACMD5Context ctx; - uint8_t digest[16]; - uint8_t seq_num[4]; - - *sig = data_blob_talloc(sig_mem_ctx, NULL, NTLMSSP_SIG_SIZE); - if (!sig->data) { - return NT_STATUS_NO_MEMORY; - } - - switch (direction) { - case NTLMSSP_SEND: - SIVAL(seq_num, 0, ntlmssp_state->ntlm2_send_seq_num); - ntlmssp_state->ntlm2_send_seq_num++; - hmac_md5_init_limK_to_64(ntlmssp_state->send_sign_key.data, - ntlmssp_state->send_sign_key.length, &ctx); - break; - case NTLMSSP_RECEIVE: - SIVAL(seq_num, 0, ntlmssp_state->ntlm2_recv_seq_num); - ntlmssp_state->ntlm2_recv_seq_num++; - hmac_md5_init_limK_to_64(ntlmssp_state->recv_sign_key.data, - ntlmssp_state->recv_sign_key.length, &ctx); - break; - } - hmac_md5_update(seq_num, sizeof(seq_num), &ctx); - hmac_md5_update(whole_pdu, pdu_length, &ctx); - hmac_md5_final(digest, &ctx); - - if (encrypt_sig && ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) { - switch (direction) { - case NTLMSSP_SEND: - arcfour_crypt_sbox(ntlmssp_state->send_seal_hash, digest, 8); - break; - case NTLMSSP_RECEIVE: - arcfour_crypt_sbox(ntlmssp_state->recv_seal_hash, digest, 8); - break; - } - } - - SIVAL(sig->data, 0, NTLMSSP_SIGN_VERSION); - memcpy(sig->data + 4, digest, 8); - memcpy(sig->data + 12, seq_num, 4); - - } else { - uint32_t crc; - crc = crc32_calc_buffer(data, length); - if (!msrpc_gen(sig_mem_ctx, sig, "dddd", NTLMSSP_SIGN_VERSION, 0, crc, ntlmssp_state->ntlm_seq_num)) { - return NT_STATUS_NO_MEMORY; - } - ntlmssp_state->ntlm_seq_num++; - - arcfour_crypt_sbox(ntlmssp_state->ntlmssp_hash, sig->data+4, sig->length-4); - } - dump_data_pw("calculated ntlmssp signature\n", sig->data, sig->length); - return NT_STATUS_OK; -} - -NTSTATUS ntlmssp_sign_packet(struct ntlmssp_state *ntlmssp_state, - TALLOC_CTX *sig_mem_ctx, - const uint8_t *data, size_t length, - const uint8_t *whole_pdu, size_t pdu_length, - DATA_BLOB *sig) -{ - if (!ntlmssp_state->session_key.length) { - DEBUG(3, ("NO session key, cannot check sign packet\n")); - return NT_STATUS_NO_USER_SESSION_KEY; - } - - if (!ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SIGN) { - DEBUG(3, ("NTLMSSP Signing not negotiated - cannot sign packet!\n")); - return NT_STATUS_INVALID_PARAMETER; - } - - return ntlmssp_make_packet_signature(ntlmssp_state, sig_mem_ctx, - data, length, - whole_pdu, pdu_length, - NTLMSSP_SEND, sig, True); -} - -/** - * Check the signature of an incoming packet - * - */ - -NTSTATUS ntlmssp_check_packet(struct ntlmssp_state *ntlmssp_state, - TALLOC_CTX *sig_mem_ctx, - const uint8_t *data, size_t length, - const uint8_t *whole_pdu, size_t pdu_length, - const DATA_BLOB *sig) -{ - DATA_BLOB local_sig; - NTSTATUS nt_status; - - if (!ntlmssp_state->session_key.length) { - DEBUG(3, ("NO session key, cannot check packet signature\n")); - return NT_STATUS_NO_USER_SESSION_KEY; - } - - if (sig->length < 8) { - DEBUG(0, ("NTLMSSP packet check failed due to short signature (%lu bytes)!\n", - (unsigned long)sig->length)); - } - - nt_status = ntlmssp_make_packet_signature(ntlmssp_state, sig_mem_ctx, - data, length, - whole_pdu, pdu_length, - NTLMSSP_RECEIVE, &local_sig, True); - - if (!NT_STATUS_IS_OK(nt_status)) { - DEBUG(0, ("NTLMSSP packet check failed with %s\n", nt_errstr(nt_status))); - return nt_status; - } - - if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { - if (local_sig.length != sig->length || - memcmp(local_sig.data, - sig->data, sig->length) != 0) { - DEBUG(5, ("BAD SIG NTLM2: wanted signature of\n")); - dump_data(5, local_sig.data, local_sig.length); - - DEBUG(5, ("BAD SIG: got signature of\n")); - dump_data(5, sig->data, sig->length); - - DEBUG(0, ("NTLMSSP NTLM2 packet check failed due to invalid signature!\n")); - return NT_STATUS_ACCESS_DENIED; - } - } else { - if (local_sig.length != sig->length || - memcmp(local_sig.data + 8, - sig->data + 8, sig->length - 8) != 0) { - DEBUG(5, ("BAD SIG NTLM1: wanted signature of\n")); - dump_data(5, local_sig.data, local_sig.length); - - DEBUG(5, ("BAD SIG: got signature of\n")); - dump_data(5, sig->data, sig->length); - - DEBUG(0, ("NTLMSSP NTLM1 packet check failed due to invalid signature!\n")); - return NT_STATUS_ACCESS_DENIED; - } - } - dump_data_pw("checked ntlmssp signature\n", sig->data, sig->length); - - return NT_STATUS_OK; -} - - -/** - * Seal data with the NTLMSSP algorithm - * - */ - -NTSTATUS ntlmssp_seal_packet(struct ntlmssp_state *ntlmssp_state, - TALLOC_CTX *sig_mem_ctx, - uint8_t *data, size_t length, - const uint8_t *whole_pdu, size_t pdu_length, - DATA_BLOB *sig) -{ - NTSTATUS nt_status; - if (!ntlmssp_state->session_key.length) { - DEBUG(3, ("NO session key, cannot seal packet\n")); - return NT_STATUS_NO_USER_SESSION_KEY; - } - - if (!ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SEAL) { - DEBUG(3, ("NTLMSSP Sealing not negotiated - cannot seal packet!\n")); - return NT_STATUS_INVALID_PARAMETER; - } - - DEBUG(10,("ntlmssp_seal_data: seal\n")); - dump_data_pw("ntlmssp clear data\n", data, length); - if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { - /* The order of these two operations matters - we must first seal the packet, - then seal the sequence number - this is becouse the send_seal_hash is not - constant, but is is rather updated with each iteration */ - nt_status = ntlmssp_make_packet_signature(ntlmssp_state, sig_mem_ctx, - data, length, - whole_pdu, pdu_length, - NTLMSSP_SEND, sig, False); - arcfour_crypt_sbox(ntlmssp_state->send_seal_hash, data, length); - if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) { - arcfour_crypt_sbox(ntlmssp_state->send_seal_hash, sig->data+4, 8); - } - } else { - uint32_t crc; - crc = crc32_calc_buffer(data, length); - if (!msrpc_gen(sig_mem_ctx, sig, "dddd", NTLMSSP_SIGN_VERSION, 0, crc, ntlmssp_state->ntlm_seq_num)) { - return NT_STATUS_NO_MEMORY; - } - - /* The order of these two operations matters - we must - first seal the packet, then seal the sequence - number - this is becouse the ntlmssp_hash is not - constant, but is is rather updated with each - iteration */ - - arcfour_crypt_sbox(ntlmssp_state->ntlmssp_hash, data, length); - arcfour_crypt_sbox(ntlmssp_state->ntlmssp_hash, sig->data+4, sig->length-4); - /* increment counter on send */ - ntlmssp_state->ntlm_seq_num++; - nt_status = NT_STATUS_OK; - } - dump_data_pw("ntlmssp signature\n", sig->data, sig->length); - dump_data_pw("ntlmssp sealed data\n", data, length); - - - return nt_status; -} - -/** - * Unseal data with the NTLMSSP algorithm - * - */ - -NTSTATUS ntlmssp_unseal_packet(struct ntlmssp_state *ntlmssp_state, - TALLOC_CTX *sig_mem_ctx, - uint8_t *data, size_t length, - const uint8_t *whole_pdu, size_t pdu_length, - DATA_BLOB *sig) -{ - DATA_BLOB local_sig; - NTSTATUS nt_status; - if (!ntlmssp_state->session_key.length) { - DEBUG(3, ("NO session key, cannot unseal packet\n")); - return NT_STATUS_NO_USER_SESSION_KEY; - } - - dump_data_pw("ntlmssp sealed data\n", data, length); - if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { - arcfour_crypt_sbox(ntlmssp_state->recv_seal_hash, data, length); - - nt_status = ntlmssp_make_packet_signature(ntlmssp_state, sig_mem_ctx, - data, length, - whole_pdu, pdu_length, - NTLMSSP_RECEIVE, &local_sig, True); - if (!NT_STATUS_IS_OK(nt_status)) { - return nt_status; - } - - if (local_sig.length != sig->length || - memcmp(local_sig.data, - sig->data, sig->length) != 0) { - DEBUG(5, ("BAD SIG NTLM2: wanted signature of\n")); - dump_data(5, local_sig.data, local_sig.length); - - DEBUG(5, ("BAD SIG: got signature of\n")); - dump_data(5, sig->data, sig->length); - - DEBUG(0, ("NTLMSSP NTLM2 packet check failed due to invalid signature!\n")); - return NT_STATUS_ACCESS_DENIED; - } - - dump_data_pw("ntlmssp clear data\n", data, length); - return NT_STATUS_OK; - } else { - arcfour_crypt_sbox(ntlmssp_state->ntlmssp_hash, data, length); - dump_data_pw("ntlmssp clear data\n", data, length); - return ntlmssp_check_packet(ntlmssp_state, sig_mem_ctx, data, length, whole_pdu, pdu_length, sig); - } -} - -/** - Initialise the state for NTLMSSP signing. -*/ -NTSTATUS ntlmssp_sign_init(struct ntlmssp_state *ntlmssp_state) -{ - uint8_t p24[24]; - ZERO_STRUCT(p24); - - DEBUG(3, ("NTLMSSP Sign/Seal - Initialising with flags:\n")); - debug_ntlmssp_flags(ntlmssp_state->neg_flags); - - if (!ntlmssp_state->session_key.length) { - DEBUG(3, ("NO session key, cannot intialise signing\n")); - return NT_STATUS_NO_USER_SESSION_KEY; - } - - if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) - { - DATA_BLOB weak_session_key = ntlmssp_state->session_key; - const char *send_sign_const; - const char *send_seal_const; - const char *recv_sign_const; - const char *recv_seal_const; - - switch (ntlmssp_state->role) { - case NTLMSSP_CLIENT: - send_sign_const = CLI_SIGN; - send_seal_const = CLI_SEAL; - recv_sign_const = SRV_SIGN; - recv_seal_const = SRV_SEAL; - break; - case NTLMSSP_SERVER: - send_sign_const = SRV_SIGN; - send_seal_const = SRV_SEAL; - recv_sign_const = CLI_SIGN; - recv_seal_const = CLI_SEAL; - break; - default: - return NT_STATUS_INTERNAL_ERROR; - } - - /** - Weaken NTLMSSP keys to cope with down-level clients, servers and export restrictions. - - We probably should have some parameters to control this, once we get NTLM2 working. - */ - - - if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_128) { - - } else if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_56) { - weak_session_key.length = 6; - } else { /* forty bits */ - weak_session_key.length = 5; - } - dump_data_pw("NTLMSSP weakend master key:\n", - weak_session_key.data, - weak_session_key.length); - - /* SEND */ - calc_ntlmv2_key(ntlmssp_state, - &ntlmssp_state->send_sign_key, - ntlmssp_state->session_key, send_sign_const); - dump_data_pw("NTLMSSP send sign key:\n", - ntlmssp_state->send_sign_key.data, - ntlmssp_state->send_sign_key.length); - - calc_ntlmv2_key(ntlmssp_state, - &ntlmssp_state->send_seal_key, - weak_session_key, send_seal_const); - dump_data_pw("NTLMSSP send seal key:\n", - ntlmssp_state->send_seal_key.data, - ntlmssp_state->send_seal_key.length); - - arcfour_init(ntlmssp_state->send_seal_hash, - &ntlmssp_state->send_seal_key); - - dump_data_pw("NTLMSSP send sesl hash:\n", - ntlmssp_state->send_seal_hash, - sizeof(ntlmssp_state->send_seal_hash)); - - /* RECV */ - calc_ntlmv2_key(ntlmssp_state, - &ntlmssp_state->recv_sign_key, - ntlmssp_state->session_key, recv_sign_const); - dump_data_pw("NTLMSSP recv sign key:\n", - ntlmssp_state->recv_sign_key.data, - ntlmssp_state->recv_sign_key.length); - - calc_ntlmv2_key(ntlmssp_state, - &ntlmssp_state->recv_seal_key, - weak_session_key, recv_seal_const); - dump_data_pw("NTLMSSP recv seal key:\n", - ntlmssp_state->recv_seal_key.data, - ntlmssp_state->recv_seal_key.length); - arcfour_init(ntlmssp_state->recv_seal_hash, - &ntlmssp_state->recv_seal_key); - - dump_data_pw("NTLMSSP receive seal hash:\n", - ntlmssp_state->recv_seal_hash, - sizeof(ntlmssp_state->recv_seal_hash)); - } else { - DEBUG(5, ("NTLMSSP Sign/Seal - using NTLM1\n")); - - arcfour_init(ntlmssp_state->ntlmssp_hash, - &ntlmssp_state->session_key); - dump_data_pw("NTLMSSP hash:\n", ntlmssp_state->ntlmssp_hash, - sizeof(ntlmssp_state->ntlmssp_hash)); - } - - ntlmssp_state->ntlm_seq_num = 0; - ntlmssp_state->ntlm2_send_seq_num = 0; - ntlmssp_state->ntlm2_recv_seq_num = 0; - - return NT_STATUS_OK; -} diff --git a/source4/libcli/auth/schannel.c b/source4/libcli/auth/schannel.c deleted file mode 100644 index 3dbf10580b..0000000000 --- a/source4/libcli/auth/schannel.c +++ /dev/null @@ -1,268 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - dcerpc schannel operations - - Copyright (C) Andrew Tridgell 2004 - Copyright (C) Andrew Bartlett 2004-2005 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include "includes.h" -#include "librpc/gen_ndr/ndr_schannel.h" -#include "auth/auth.h" -#include "libcli/auth/schannel.h" - -static size_t schannel_sig_size(struct gensec_security *gensec_security) -{ - return 32; -} - -static NTSTATUS schannel_session_key(struct gensec_security *gensec_security, - DATA_BLOB *session_key) -{ - return NT_STATUS_NOT_IMPLEMENTED; -} - -static NTSTATUS schannel_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, - const DATA_BLOB in, DATA_BLOB *out) -{ - struct schannel_state *state = gensec_security->private_data; - NTSTATUS status; - struct schannel_bind bind_schannel; - struct schannel_bind_ack bind_schannel_ack; - struct creds_CredentialState *creds; - - const char *workstation; - const char *domain; - *out = data_blob(NULL, 0); - - switch (gensec_security->gensec_role) { - case GENSEC_CLIENT: - if (state->state != SCHANNEL_STATE_START) { - /* we could parse the bind ack, but we don't know what it is yet */ - return NT_STATUS_OK; - } - - state->creds = talloc_reference(state, cli_credentials_get_netlogon_creds(gensec_security->credentials)); - - bind_schannel.unknown1 = 0; -#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 = cli_credentials_get_domain(gensec_security->credentials); - bind_schannel.u.info23.account_name = cli_credentials_get_username(gensec_security->credentials); - bind_schannel.u.info23.dnsdomain = str_format_nbt_domain(out_mem_ctx, fulldomainname); - bind_schannel.u.info23.workstation = str_format_nbt_domain(out_mem_ctx, cli_credentials_get_workstation(gensec_security->credentials)); -#else - bind_schannel.bind_type = 3; - bind_schannel.u.info3.domain = cli_credentials_get_domain(gensec_security->credentials); - bind_schannel.u.info3.workstation = cli_credentials_get_workstation(gensec_security->credentials); -#endif - - status = ndr_push_struct_blob(out, out_mem_ctx, &bind_schannel, - (ndr_push_flags_fn_t)ndr_push_schannel_bind); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(3, ("Could not create schannel bind: %s\n", - nt_errstr(status))); - return status; - } - - state->state = SCHANNEL_STATE_UPDATE_1; - - return NT_STATUS_MORE_PROCESSING_REQUIRED; - case GENSEC_SERVER: - - if (state->state != SCHANNEL_STATE_START) { - /* no third leg on this protocol */ - return NT_STATUS_INVALID_PARAMETER; - } - - /* parse the schannel startup blob */ - status = ndr_pull_struct_blob(&in, out_mem_ctx, &bind_schannel, - (ndr_pull_flags_fn_t)ndr_pull_schannel_bind); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - if (bind_schannel.bind_type == 23) { - workstation = bind_schannel.u.info23.workstation; - domain = bind_schannel.u.info23.domain; - } else { - workstation = bind_schannel.u.info3.workstation; - domain = bind_schannel.u.info3.domain; - } - - /* pull the session key for this client */ - status = schannel_fetch_session_key(out_mem_ctx, workstation, - domain, &creds); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(3, ("Could not find session key for attempted schannel connection from %s: %s\n", - workstation, nt_errstr(status))); - return status; - } - - state->creds = talloc_reference(state, creds); - - bind_schannel_ack.unknown1 = 1; - bind_schannel_ack.unknown2 = 0; - bind_schannel_ack.unknown3 = 0x6c0000; - - status = ndr_push_struct_blob(out, out_mem_ctx, &bind_schannel_ack, - (ndr_push_flags_fn_t)ndr_push_schannel_bind_ack); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(3, ("Could not return schannel bind ack for client %s: %s\n", - workstation, nt_errstr(status))); - return status; - } - - state->state = SCHANNEL_STATE_UPDATE_1; - - return NT_STATUS_OK; - } - return NT_STATUS_INVALID_PARAMETER; -} - -/** - * Return the struct creds_CredentialState. - * - * Make sure not to call this unless gensec is using schannel... - */ - -NTSTATUS dcerpc_schannel_creds(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - struct creds_CredentialState **creds) -{ - struct schannel_state *state = gensec_security->private_data; - - *creds = talloc_reference(mem_ctx, state->creds); - if (!*creds) { - return NT_STATUS_NO_MEMORY; - } - return NT_STATUS_OK; -} - - -/** - * Return the credentials of a logged on user, including session keys - * etc. - * - * Only valid after a successful authentication - * - * May only be called once per authentication. - * - */ - -static NTSTATUS schannel_session_info(struct gensec_security *gensec_security, - struct auth_session_info **session_info) -{ - (*session_info) = talloc(gensec_security, struct auth_session_info); - NT_STATUS_HAVE_NO_MEMORY(*session_info); - - ZERO_STRUCTP(*session_info); - - return NT_STATUS_OK; -} - -static NTSTATUS schannel_start(struct gensec_security *gensec_security) -{ - struct schannel_state *state; - - state = talloc(gensec_security, struct schannel_state); - if (!state) { - return NT_STATUS_NO_MEMORY; - } - - state->state = SCHANNEL_STATE_START; - state->seq_num = 0; - gensec_security->private_data = state; - - return NT_STATUS_OK; -} - -static NTSTATUS schannel_server_start(struct gensec_security *gensec_security) -{ - NTSTATUS status; - struct schannel_state *state; - - status = schannel_start(gensec_security); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - state = gensec_security->private_data; - state->initiator = False; - - return NT_STATUS_OK; -} - -static NTSTATUS schannel_client_start(struct gensec_security *gensec_security) -{ - NTSTATUS status; - struct schannel_state *state; - - status = schannel_start(gensec_security); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - state = gensec_security->private_data; - state->initiator = True; - - return NT_STATUS_OK; -} - - -static BOOL schannel_have_feature(struct gensec_security *gensec_security, - uint32_t feature) -{ - if (feature & (GENSEC_FEATURE_SIGN | - GENSEC_FEATURE_SEAL)) { - return True; - } - return False; -} - - -static const struct gensec_security_ops gensec_schannel_security_ops = { - .name = "schannel", - .auth_type = DCERPC_AUTH_TYPE_SCHANNEL, - .client_start = schannel_client_start, - .server_start = schannel_server_start, - .update = schannel_update, - .seal_packet = schannel_seal_packet, - .sign_packet = schannel_sign_packet, - .check_packet = schannel_check_packet, - .unseal_packet = schannel_unseal_packet, - .session_key = schannel_session_key, - .session_info = schannel_session_info, - .sig_size = schannel_sig_size, - .have_feature = schannel_have_feature, - .enabled = True -}; - -NTSTATUS gensec_schannel_init(void) -{ - NTSTATUS ret; - ret = gensec_register(&gensec_schannel_security_ops); - if (!NT_STATUS_IS_OK(ret)) { - DEBUG(0,("Failed to register '%s' gensec backend!\n", - gensec_schannel_security_ops.name)); - return ret; - } - - return ret; -} diff --git a/source4/libcli/auth/schannel.h b/source4/libcli/auth/schannel.h deleted file mode 100644 index c109387c7c..0000000000 --- a/source4/libcli/auth/schannel.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - dcerpc schannel operations - - Copyright (C) Andrew Tridgell 2004 - Copyright (C) Andrew Bartlett 2004-2005 - - 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. -*/ - -enum schannel_position { - SCHANNEL_STATE_START = 0, - SCHANNEL_STATE_UPDATE_1 -}; - -struct schannel_state { - enum schannel_position state; - uint32_t seq_num; - BOOL initiator; - struct creds_CredentialState *creds; -}; - diff --git a/source4/libcli/auth/schannel_sign.c b/source4/libcli/auth/schannel_sign.c deleted file mode 100644 index 3b493bd0d3..0000000000 --- a/source4/libcli/auth/schannel_sign.c +++ /dev/null @@ -1,284 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - schannel library code - - Copyright (C) Andrew Tridgell 2004 - Copyright (C) Andrew Bartlett 2005 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include "includes.h" -#include "lib/crypto/crypto.h" -#include "libcli/auth/schannel.h" -#include "libcli/auth/gensec.h" -#include "libcli/auth/credentials.h" - -#define NETSEC_SIGN_SIGNATURE { 0x77, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00 } -#define NETSEC_SEAL_SIGNATURE { 0x77, 0x00, 0x7a, 0x00, 0xff, 0xff, 0x00, 0x00 } - -/******************************************************************* - Encode or Decode the sequence number (which is symmetric) - ********************************************************************/ -static void netsec_deal_with_seq_num(struct schannel_state *state, - const uint8_t packet_digest[8], - uint8_t seq_num[8]) -{ - static const uint8_t zeros[4]; - uint8_t sequence_key[16]; - uint8_t digest1[16]; - - hmac_md5(state->creds->session_key, zeros, sizeof(zeros), digest1); - hmac_md5(digest1, packet_digest, 8, sequence_key); - arcfour_crypt(seq_num, sequence_key, 8); - - state->seq_num++; -} - - -/******************************************************************* - Calculate the key with which to encode the data payload - ********************************************************************/ -static void netsec_get_sealing_key(const uint8_t session_key[16], - const uint8_t seq_num[8], - uint8_t sealing_key[16]) -{ - static const uint8_t zeros[4]; - uint8_t digest2[16]; - uint8_t sess_kf0[16]; - int i; - - for (i = 0; i < 16; i++) { - sess_kf0[i] = session_key[i] ^ 0xf0; - } - - hmac_md5(sess_kf0, zeros, 4, digest2); - hmac_md5(digest2, seq_num, 8, sealing_key); -} - - -/******************************************************************* - Create a digest over the entire packet (including the data), and - MD5 it with the session key. - ********************************************************************/ -static void schannel_digest(const uint8_t sess_key[16], - const uint8_t netsec_sig[8], - const uint8_t *confounder, - const uint8_t *data, size_t data_len, - uint8_t digest_final[16]) -{ - uint8_t packet_digest[16]; - static const uint8_t zeros[4]; - struct MD5Context ctx; - - MD5Init(&ctx); - MD5Update(&ctx, zeros, 4); - MD5Update(&ctx, netsec_sig, 8); - if (confounder) { - MD5Update(&ctx, confounder, 8); - } - MD5Update(&ctx, data, data_len); - MD5Final(packet_digest, &ctx); - - hmac_md5(sess_key, packet_digest, sizeof(packet_digest), digest_final); -} - - -/* - unseal a packet -*/ -NTSTATUS schannel_unseal_packet(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - uint8_t *data, size_t length, - const uint8_t *whole_pdu, size_t pdu_length, - DATA_BLOB *sig) -{ - struct schannel_state *state = gensec_security->private_data; - - uint8_t digest_final[16]; - uint8_t confounder[8]; - uint8_t seq_num[8]; - uint8_t sealing_key[16]; - static const uint8_t netsec_sig[8] = NETSEC_SEAL_SIGNATURE; - - if (sig->length != 32) { - return NT_STATUS_ACCESS_DENIED; - } - - memcpy(confounder, sig->data+24, 8); - - RSIVAL(seq_num, 0, state->seq_num); - SIVAL(seq_num, 4, state->initiator?0:0x80); - - netsec_get_sealing_key(state->creds->session_key, seq_num, sealing_key); - arcfour_crypt(confounder, sealing_key, 8); - arcfour_crypt(data, sealing_key, length); - - schannel_digest(state->creds->session_key, - netsec_sig, confounder, - data, length, digest_final); - - if (memcmp(digest_final, sig->data+16, 8) != 0) { - dump_data_pw("calc digest:", digest_final, 8); - dump_data_pw("wire digest:", sig->data+16, 8); - return NT_STATUS_ACCESS_DENIED; - } - - netsec_deal_with_seq_num(state, digest_final, seq_num); - - if (memcmp(seq_num, sig->data+8, 8) != 0) { - dump_data_pw("calc seq num:", seq_num, 8); - dump_data_pw("wire seq num:", sig->data+8, 8); - return NT_STATUS_ACCESS_DENIED; - } - - return NT_STATUS_OK; -} - -/* - check the signature on a packet -*/ -NTSTATUS schannel_check_packet(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - const uint8_t *data, size_t length, - const uint8_t *whole_pdu, size_t pdu_length, - const DATA_BLOB *sig) -{ - struct schannel_state *state = gensec_security->private_data; - - uint8_t digest_final[16]; - uint8_t seq_num[8]; - static const uint8_t netsec_sig[8] = NETSEC_SIGN_SIGNATURE; - - /* w2k sends just 24 bytes and skip the confounder */ - if (sig->length != 32 && sig->length != 24) { - return NT_STATUS_ACCESS_DENIED; - } - - RSIVAL(seq_num, 0, state->seq_num); - SIVAL(seq_num, 4, state->initiator?0:0x80); - - dump_data_pw("seq_num:\n", seq_num, 8); - dump_data_pw("sess_key:\n", state->creds->session_key, 16); - - schannel_digest(state->creds->session_key, - netsec_sig, NULL, - data, length, digest_final); - - netsec_deal_with_seq_num(state, digest_final, seq_num); - - if (memcmp(seq_num, sig->data+8, 8) != 0) { - dump_data_pw("calc seq num:", seq_num, 8); - dump_data_pw("wire seq num:", sig->data+8, 8); - return NT_STATUS_ACCESS_DENIED; - } - - if (memcmp(digest_final, sig->data+16, 8) != 0) { - dump_data_pw("calc digest:", digest_final, 8); - dump_data_pw("wire digest:", sig->data+16, 8); - return NT_STATUS_ACCESS_DENIED; - } - - return NT_STATUS_OK; -} - - -/* - seal a packet -*/ -NTSTATUS schannel_seal_packet(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - uint8_t *data, size_t length, - const uint8_t *whole_pdu, size_t pdu_length, - DATA_BLOB *sig) -{ - struct schannel_state *state = gensec_security->private_data; - - uint8_t digest_final[16]; - uint8_t confounder[8]; - uint8_t seq_num[8]; - uint8_t sealing_key[16]; - static const uint8_t netsec_sig[8] = NETSEC_SEAL_SIGNATURE; - - generate_random_buffer(confounder, 8); - - RSIVAL(seq_num, 0, state->seq_num); - SIVAL(seq_num, 4, state->initiator?0x80:0); - - schannel_digest(state->creds->session_key, - netsec_sig, confounder, - data, length, digest_final); - - netsec_get_sealing_key(state->creds->session_key, seq_num, sealing_key); - arcfour_crypt(confounder, sealing_key, 8); - arcfour_crypt(data, sealing_key, length); - - netsec_deal_with_seq_num(state, digest_final, seq_num); - - (*sig) = data_blob_talloc(mem_ctx, NULL, 32); - - memcpy(sig->data, netsec_sig, 8); - memcpy(sig->data+8, seq_num, 8); - memcpy(sig->data+16, digest_final, 8); - memcpy(sig->data+24, confounder, 8); - - dump_data_pw("signature:", sig->data+ 0, 8); - dump_data_pw("seq_num :", sig->data+ 8, 8); - dump_data_pw("digest :", sig->data+16, 8); - dump_data_pw("confound :", sig->data+24, 8); - - return NT_STATUS_OK; -} - - -/* - sign a packet -*/ -NTSTATUS schannel_sign_packet(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - const uint8_t *data, size_t length, - const uint8_t *whole_pdu, size_t pdu_length, - DATA_BLOB *sig) -{ - struct schannel_state *state = gensec_security->private_data; - - uint8_t digest_final[16]; - uint8_t seq_num[8]; - static const uint8_t netsec_sig[8] = NETSEC_SIGN_SIGNATURE; - - RSIVAL(seq_num, 0, state->seq_num); - SIVAL(seq_num, 4, state->initiator?0x80:0); - - schannel_digest(state->creds->session_key, - netsec_sig, NULL, - data, length, digest_final); - - netsec_deal_with_seq_num(state, digest_final, seq_num); - - (*sig) = data_blob_talloc(mem_ctx, NULL, 32); - - memcpy(sig->data, netsec_sig, 8); - memcpy(sig->data+8, seq_num, 8); - memcpy(sig->data+16, digest_final, 8); - memset(sig->data+24, 0, 8); - - dump_data_pw("signature:", sig->data+ 0, 8); - dump_data_pw("seq_num :", sig->data+ 8, 8); - dump_data_pw("digest :", sig->data+16, 8); - dump_data_pw("confound :", sig->data+24, 8); - - return NT_STATUS_OK; -} diff --git a/source4/libcli/auth/schannel_state.c b/source4/libcli/auth/schannel_state.c deleted file mode 100644 index b2d632a1f0..0000000000 --- a/source4/libcli/auth/schannel_state.c +++ /dev/null @@ -1,229 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - module to store/fetch session keys for the schannel server - - Copyright (C) Andrew Tridgell 2004 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include "includes.h" -#include "system/time.h" -#include "auth/auth.h" -#include "lib/ldb/include/ldb.h" -#include "db_wrap.h" - -/* a reasonable amount of time to keep credentials live */ -#define SCHANNEL_CREDENTIALS_EXPIRY 600 - -/* - connect to the schannel ldb -*/ -static struct ldb_context *schannel_db_connect(TALLOC_CTX *mem_ctx) -{ - char *path; - struct ldb_context *ldb; - - path = smbd_tmp_path(mem_ctx, "schannel.ldb"); - if (!path) { - return NULL; - } - - ldb = ldb_wrap_connect(mem_ctx, path, 0, NULL); - talloc_free(path); - if (!ldb) { - return NULL; - } - - return ldb; -} - -/* - remember an established session key for a netr server authentication - use a simple ldb structure -*/ -NTSTATUS schannel_store_session_key(TALLOC_CTX *mem_ctx, - struct creds_CredentialState *creds) -{ - struct ldb_context *ldb; - struct ldb_message *msg; - struct ldb_val val, seed; - char *s; - char *f; - char *sct; - char *rid; - time_t expiry = time(NULL) + SCHANNEL_CREDENTIALS_EXPIRY; - int ret; - - ldb = schannel_db_connect(mem_ctx); - if (ldb == NULL) { - return NT_STATUS_NO_MEMORY; - } - - s = talloc_asprintf(mem_ctx, "%u", (unsigned int)expiry); - - if (s == NULL) { - talloc_free(ldb); - return NT_STATUS_NO_MEMORY; - } - - f = talloc_asprintf(mem_ctx, "%u", (unsigned int)creds->negotiate_flags); - - if (f == NULL) { - talloc_free(ldb); - return NT_STATUS_NO_MEMORY; - } - - sct = talloc_asprintf(mem_ctx, "%u", (unsigned int)creds->secure_channel_type); - - if (sct == NULL) { - talloc_free(ldb); - return NT_STATUS_NO_MEMORY; - } - - rid = talloc_asprintf(mem_ctx, "%u", (unsigned int)creds->rid); - - if (rid == NULL) { - talloc_free(ldb); - return NT_STATUS_NO_MEMORY; - } - - msg = ldb_msg_new(mem_ctx); - if (msg == NULL) { - talloc_free(ldb); - return NT_STATUS_NO_MEMORY; - } - - msg->dn = talloc_asprintf(msg, "computerName=%s", creds->computer_name); - if (msg->dn == NULL) { - talloc_free(ldb); - talloc_free(msg); - return NT_STATUS_NO_MEMORY; - } - - val.data = creds->session_key; - val.length = sizeof(creds->session_key); - - seed.data = creds->seed.data; - seed.length = sizeof(creds->seed.data); - - ldb_msg_add_value(ldb, msg, "sessionKey", &val); - ldb_msg_add_value(ldb, msg, "seed", &seed); - ldb_msg_add_string(ldb, msg, "expiry", s); - ldb_msg_add_string(ldb, msg, "negotiateFlags", f); - ldb_msg_add_string(ldb, msg, "secureChannelType", sct); - ldb_msg_add_string(ldb, msg, "accountName", creds->account_name); - ldb_msg_add_string(ldb, msg, "computerName", creds->computer_name); - ldb_msg_add_string(ldb, msg, "flatname", creds->domain); - ldb_msg_add_string(ldb, msg, "rid", rid); - - ldb_delete(ldb, msg->dn); - - ret = ldb_add(ldb, msg); - - talloc_free(s); - - if (ret != 0) { - DEBUG(0,("Unable to add %s to session key db - %s\n", - msg->dn, ldb_errstring(ldb))); - talloc_free(ldb); - talloc_free(msg); - return NT_STATUS_INTERNAL_DB_CORRUPTION; - } - - talloc_free(msg); - talloc_free(ldb); - - return NT_STATUS_OK; -} - - -/* - read back a credentials back for a computer -*/ -NTSTATUS schannel_fetch_session_key(TALLOC_CTX *mem_ctx, - const char *computer_name, - const char *domain, - struct creds_CredentialState **creds) -{ - struct ldb_context *ldb; - time_t expiry; - struct ldb_message **res; - int ret; - const struct ldb_val *val; - char *expr=NULL; - - *creds = talloc_zero(mem_ctx, struct creds_CredentialState); - if (!*creds) { - return NT_STATUS_NO_MEMORY; - } - - ldb = schannel_db_connect(mem_ctx); - if (ldb == NULL) { - return NT_STATUS_NO_MEMORY; - } - - expr = talloc_asprintf(mem_ctx, "(&(computerName=%s)(flatname=%s))", computer_name, domain); - if (expr == NULL) { - talloc_free(ldb); - return NT_STATUS_NO_MEMORY; - } - - ret = ldb_search(ldb, NULL, LDB_SCOPE_SUBTREE, expr, NULL, &res); - if (ret != 1) { - talloc_free(ldb); - return NT_STATUS_INVALID_HANDLE; - } - - expiry = ldb_msg_find_uint(res[0], "expiry", 0); - if (expiry < time(NULL)) { - DEBUG(1,("schannel: attempt to use expired session key for %s\n", computer_name)); - talloc_free(ldb); - return NT_STATUS_INVALID_HANDLE; - } - - val = ldb_msg_find_ldb_val(res[0], "sessionKey"); - if (val == NULL || val->length != 16) { - talloc_free(ldb); - return NT_STATUS_INVALID_HANDLE; - } - - memcpy((*creds)->session_key, val->data, 16); - - val = ldb_msg_find_ldb_val(res[0], "seed"); - if (val == NULL || val->length != 8) { - talloc_free(ldb); - return NT_STATUS_INVALID_HANDLE; - } - - memcpy((*creds)->seed.data, val->data, 8); - - (*creds)->negotiate_flags = ldb_msg_find_int(res[0], "negotiateFlags", 0); - - (*creds)->secure_channel_type = ldb_msg_find_int(res[0], "secureChannelType", 0); - - (*creds)->account_name = talloc_reference(*creds, ldb_msg_find_string(res[0], "accountName", NULL)); - - (*creds)->computer_name = talloc_reference(*creds, ldb_msg_find_string(res[0], "computerName", NULL)); - - (*creds)->domain = talloc_reference(*creds, ldb_msg_find_string(res[0], "flatname", NULL)); - - (*creds)->rid = ldb_msg_find_uint(res[0], "rid", 0); - - talloc_free(ldb); - - return NT_STATUS_OK; -} diff --git a/source4/libcli/auth/spnego.c b/source4/libcli/auth/spnego.c deleted file mode 100644 index f5a091cd78..0000000000 --- a/source4/libcli/auth/spnego.c +++ /dev/null @@ -1,884 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - RFC2478 Compliant SPNEGO implementation - - Copyright (C) Jim McDonough 2003 - Copyright (C) Andrew Bartlett 2004 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include "includes.h" -#include "auth/auth.h" - -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_AUTH - -enum spnego_state_position { - SPNEGO_SERVER_START, - SPNEGO_CLIENT_START, - SPNEGO_SERVER_TARG, - SPNEGO_CLIENT_TARG, - SPNEGO_FALLBACK, - SPNEGO_DONE -}; - -struct spnego_state { - uint_t ref_count; - enum spnego_message_type expected_packet; - enum spnego_state_position state_position; - struct gensec_security *sub_sec_security; - BOOL no_response_expected; -}; - - -static NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_security) -{ - struct spnego_state *spnego_state; - - spnego_state = talloc(gensec_security, struct spnego_state); - if (!spnego_state) { - return NT_STATUS_NO_MEMORY; - } - - spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT; - spnego_state->state_position = SPNEGO_CLIENT_START; - spnego_state->sub_sec_security = NULL; - spnego_state->no_response_expected = False; - - gensec_security->private_data = spnego_state; - return NT_STATUS_OK; -} - -static NTSTATUS gensec_spnego_server_start(struct gensec_security *gensec_security) -{ - struct spnego_state *spnego_state; - - spnego_state = talloc(gensec_security, struct spnego_state); - if (!spnego_state) { - return NT_STATUS_NO_MEMORY; - } - - spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT; - spnego_state->state_position = SPNEGO_SERVER_START; - spnego_state->sub_sec_security = NULL; - spnego_state->no_response_expected = False; - - gensec_security->private_data = spnego_state; - return NT_STATUS_OK; -} - -/* - wrappers for the spnego_*() functions -*/ -static NTSTATUS gensec_spnego_unseal_packet(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - uint8_t *data, size_t length, - const uint8_t *whole_pdu, size_t pdu_length, - DATA_BLOB *sig) -{ - 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; - } - - return gensec_unseal_packet(spnego_state->sub_sec_security, - mem_ctx, - data, length, - whole_pdu, pdu_length, - sig); -} - -static NTSTATUS gensec_spnego_check_packet(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - const uint8_t *data, size_t length, - const uint8_t *whole_pdu, size_t pdu_length, - const DATA_BLOB *sig) -{ - 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; - } - - return gensec_check_packet(spnego_state->sub_sec_security, - mem_ctx, - data, length, - whole_pdu, pdu_length, - sig); -} - -static NTSTATUS gensec_spnego_seal_packet(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - uint8_t *data, size_t length, - const uint8_t *whole_pdu, size_t pdu_length, - DATA_BLOB *sig) -{ - 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; - } - - return gensec_seal_packet(spnego_state->sub_sec_security, - mem_ctx, - data, length, - whole_pdu, pdu_length, - sig); -} - -static NTSTATUS gensec_spnego_sign_packet(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - const uint8_t *data, size_t length, - const uint8_t *whole_pdu, size_t pdu_length, - DATA_BLOB *sig) -{ - 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; - } - - return gensec_sign_packet(spnego_state->sub_sec_security, - mem_ctx, - data, length, - whole_pdu, pdu_length, - sig); -} - -static NTSTATUS gensec_spnego_wrap(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - const DATA_BLOB *in, - DATA_BLOB *out) -{ - struct spnego_state *spnego_state = gensec_security->private_data; - - if (spnego_state->state_position != SPNEGO_DONE - && spnego_state->state_position != SPNEGO_FALLBACK) { - DEBUG(1, ("gensec_spnego_wrap: wrong state for wrap\n")); - return NT_STATUS_INVALID_PARAMETER; - } - - return gensec_wrap(spnego_state->sub_sec_security, - mem_ctx, in, out); -} - -static NTSTATUS gensec_spnego_unwrap(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - const DATA_BLOB *in, - DATA_BLOB *out) -{ - struct spnego_state *spnego_state = gensec_security->private_data; - - if (spnego_state->state_position != SPNEGO_DONE - && spnego_state->state_position != SPNEGO_FALLBACK) { - DEBUG(1, ("gensec_spnego_unwrap: wrong state for unwrap\n")); - return NT_STATUS_INVALID_PARAMETER; - } - - return gensec_unwrap(spnego_state->sub_sec_security, - mem_ctx, in, out); -} - -static size_t gensec_spnego_sig_size(struct gensec_security *gensec_security) -{ - struct spnego_state *spnego_state = gensec_security->private_data; - - if (spnego_state->state_position != SPNEGO_DONE - && spnego_state->state_position != SPNEGO_FALLBACK) { - return 0; - } - - return gensec_sig_size(spnego_state->sub_sec_security); -} - -static NTSTATUS gensec_spnego_session_key(struct gensec_security *gensec_security, - DATA_BLOB *session_key) -{ - struct spnego_state *spnego_state = gensec_security->private_data; - if (!spnego_state->sub_sec_security) { - return NT_STATUS_INVALID_PARAMETER; - } - - return gensec_session_key(spnego_state->sub_sec_security, - session_key); -} - -static NTSTATUS gensec_spnego_session_info(struct gensec_security *gensec_security, - struct auth_session_info **session_info) -{ - struct spnego_state *spnego_state = gensec_security->private_data; - if (!spnego_state->sub_sec_security) { - return NT_STATUS_INVALID_PARAMETER; - } - - return gensec_session_info(spnego_state->sub_sec_security, - session_info); -} - -/** Fallback to another GENSEC mechanism, based on magic strings - * - * This is the 'fallback' case, where we don't get SPNEGO, and have to - * try all the other options (and hope they all have a magic string - * they check) -*/ - -static NTSTATUS gensec_spnego_server_try_fallback(struct gensec_security *gensec_security, - struct spnego_state *spnego_state, - TALLOC_CTX *out_mem_ctx, - const DATA_BLOB in, DATA_BLOB *out) -{ - int i; - int num_ops; - const struct gensec_security_ops **all_ops = gensec_security_all(&num_ops); - for (i=0; i < num_ops; i++) { - NTSTATUS nt_status; - if (!all_ops[i]->oid) { - continue; - } - if (strcasecmp(GENSEC_OID_SPNEGO,all_ops[i]->oid) == 0) { - continue; - } - - nt_status = gensec_subcontext_start(spnego_state, - gensec_security, - &spnego_state->sub_sec_security); - if (!NT_STATUS_IS_OK(nt_status)) { - return nt_status; - } - /* select the sub context */ - nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security, - all_ops[i]->oid); - if (!NT_STATUS_IS_OK(nt_status)) { - talloc_free(spnego_state->sub_sec_security); - spnego_state->sub_sec_security = NULL; - continue; - } - nt_status = gensec_update(spnego_state->sub_sec_security, - out_mem_ctx, in, out); - if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { - spnego_state->state_position = SPNEGO_FALLBACK; - return nt_status; - } - talloc_free(spnego_state->sub_sec_security); - spnego_state->sub_sec_security = NULL; - } - DEBUG(1, ("Failed to parse SPNEGO request\n")); - return NT_STATUS_INVALID_PARAMETER; - -} - -/* - Parse the netTokenInit from the client, to the server. - - -*/ - -static NTSTATUS gensec_spnego_server_parse_negTokenInit(struct gensec_security *gensec_security, - struct spnego_state *spnego_state, - TALLOC_CTX *out_mem_ctx, - const char **mechType, - const DATA_BLOB unwrapped_in, DATA_BLOB *unwrapped_out) -{ - NTSTATUS nt_status; - - if (!mechType || !mechType[0]) { - DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n")); - return NT_STATUS_INVALID_PARAMETER; - } - - nt_status = gensec_subcontext_start(spnego_state, - gensec_security, - &spnego_state->sub_sec_security); - if (!NT_STATUS_IS_OK(nt_status)) { - return nt_status; - } - /* select the sub context */ - nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security, - mechType[0]); - if (!NT_STATUS_IS_OK(nt_status)) { - talloc_free(spnego_state->sub_sec_security); - spnego_state->sub_sec_security = NULL; - return nt_status; - } - - if (!unwrapped_in.length) { - return NT_STATUS_INVALID_PARAMETER; - } - - nt_status = gensec_update(spnego_state->sub_sec_security, - out_mem_ctx, - unwrapped_in, - unwrapped_out); - - if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(nt_status)) { - DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n", - spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status))); - talloc_free(spnego_state->sub_sec_security); - spnego_state->sub_sec_security = NULL; - } - return nt_status; -} - -static NTSTATUS gensec_spnego_client_parse_negTokenInit(struct gensec_security *gensec_security, - struct spnego_state *spnego_state, - TALLOC_CTX *out_mem_ctx, - const char **mechType, - const DATA_BLOB unwrapped_in, DATA_BLOB *unwrapped_out) -{ - int i; - NTSTATUS nt_status; - DATA_BLOB null_data_blob = data_blob(NULL,0); - - for (i=0; mechType && mechType[i]; i++) { - nt_status = gensec_subcontext_start(spnego_state, - gensec_security, - &spnego_state->sub_sec_security); - if (!NT_STATUS_IS_OK(nt_status)) { - break; - } - /* select the sub context */ - nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security, - mechType[i]); - if (!NT_STATUS_IS_OK(nt_status)) { - talloc_free(spnego_state->sub_sec_security); - spnego_state->sub_sec_security = NULL; - continue; - } - - if (i == 0) { - nt_status = gensec_update(spnego_state->sub_sec_security, - out_mem_ctx, - unwrapped_in, - unwrapped_out); - } else { - /* only get the helping start blob for the first OID */ - nt_status = gensec_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) && !NT_STATUS_IS_OK(nt_status)) { - DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n", - spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status))); - talloc_free(spnego_state->sub_sec_security); - spnego_state->sub_sec_security = NULL; - } - return nt_status; - } - if (!mechType || !mechType[i]) { - DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n")); - } - return NT_STATUS_INVALID_PARAMETER; -} - -/** create a client negTokenInit - * - * This is the case, where the client is the first one who sends data -*/ - -static NTSTATUS gensec_spnego_client_negTokenInit(struct gensec_security *gensec_security, - struct spnego_state *spnego_state, - TALLOC_CTX *out_mem_ctx, - const DATA_BLOB in, DATA_BLOB *out) -{ - DATA_BLOB null_data_blob = data_blob(NULL,0); - NTSTATUS nt_status; - const char **mechTypes = NULL; - DATA_BLOB unwrapped_out = data_blob(NULL,0); - - mechTypes = gensec_security_oids(out_mem_ctx, GENSEC_OID_SPNEGO); - - if (!mechTypes) { - DEBUG(1, ("no GENSEC OID backends available\n")); - return NT_STATUS_INVALID_PARAMETER; - } - - nt_status = gensec_subcontext_start(spnego_state, - gensec_security, - &spnego_state->sub_sec_security); - if (!NT_STATUS_IS_OK(nt_status)) { - return nt_status; - } - /* select our preferred mech */ - nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security, - mechTypes[0]); - if (!NT_STATUS_IS_OK(nt_status)) { - talloc_free(spnego_state->sub_sec_security); - spnego_state->sub_sec_security = NULL; - return nt_status; - } - nt_status = gensec_update(spnego_state->sub_sec_security, - out_mem_ctx, in, &unwrapped_out); - if (NT_STATUS_IS_OK(nt_status) || NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { - struct spnego_data spnego_out; - spnego_out.type = SPNEGO_NEG_TOKEN_INIT; - spnego_out.negTokenInit.mechTypes = mechTypes; - spnego_out.negTokenInit.reqFlags = 0; - spnego_out.negTokenInit.mechListMIC = null_data_blob; - spnego_out.negTokenInit.mechToken = unwrapped_out; - - if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) { - DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n")); - return NT_STATUS_INVALID_PARAMETER; - } - - /* set next state */ - spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG; - spnego_state->state_position = SPNEGO_CLIENT_TARG; - - if (NT_STATUS_IS_OK(nt_status)) { - spnego_state->no_response_expected = True; - } - - return NT_STATUS_MORE_PROCESSING_REQUIRED; - } - talloc_free(spnego_state->sub_sec_security); - spnego_state->sub_sec_security = NULL; - - DEBUG(1, ("Failed to setup SPNEGO negTokenInit request: %s\n", nt_errstr(nt_status))); - return NT_STATUS_INVALID_PARAMETER; -} - - -/** create a client negTokenTarg - * - * This is the case, where the client is the first one who sends data -*/ - -static NTSTATUS gensec_spnego_server_negTokenTarg(struct gensec_security *gensec_security, - struct spnego_state *spnego_state, - TALLOC_CTX *out_mem_ctx, - NTSTATUS nt_status, - const DATA_BLOB unwrapped_out, DATA_BLOB *out) -{ - struct spnego_data spnego_out; - DATA_BLOB null_data_blob = data_blob(NULL, 0); - - /* compose reply */ - spnego_out.type = SPNEGO_NEG_TOKEN_TARG; - spnego_out.negTokenTarg.responseToken = unwrapped_out; - spnego_out.negTokenTarg.mechListMIC = null_data_blob; - spnego_out.negTokenTarg.supportedMech = NULL; - - if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { - spnego_out.negTokenTarg.supportedMech - = spnego_state->sub_sec_security->ops->oid; - spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE; - spnego_state->state_position = SPNEGO_SERVER_TARG; - } else if (NT_STATUS_IS_OK(nt_status)) { - if (unwrapped_out.data) { - spnego_out.negTokenTarg.supportedMech - = spnego_state->sub_sec_security->ops->oid; - } - spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED; - spnego_state->state_position = SPNEGO_DONE; - } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER)) { - if (spnego_state->sub_sec_security) { - /* we have a mech, but we just didn't get the input parameter */ - spnego_out.negTokenTarg.supportedMech - = spnego_state->sub_sec_security->ops->oid; - } else { - const char **mechTypes = gensec_security_oids(out_mem_ctx, GENSEC_OID_SPNEGO); - if (!mechTypes) { - DEBUG(1, ("no GENSEC OID backends available\n")); - return NT_STATUS_INVALID_PARAMETER; - } - - nt_status = gensec_subcontext_start(spnego_state, - gensec_security, - &spnego_state->sub_sec_security); - if (!NT_STATUS_IS_OK(nt_status)) { - return nt_status; - } - /* select our preferred mech */ - nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security, - mechTypes[0]); - if (!NT_STATUS_IS_OK(nt_status)) { - talloc_free(spnego_state->sub_sec_security); - spnego_state->sub_sec_security = NULL; - return nt_status; - } - - /* we should be sending the whole list here */ - spnego_out.negTokenTarg.supportedMech = mechTypes[0]; - } - - spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE; - spnego_state->state_position = SPNEGO_SERVER_TARG; - nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED; - } else { - spnego_out.negTokenTarg.negResult = SPNEGO_REJECT; - DEBUG(2, ("SPNEGO login failed: %s\n", nt_errstr(nt_status))); - spnego_state->state_position = SPNEGO_DONE; - } - - if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) { - DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n")); - return NT_STATUS_INVALID_PARAMETER; - } - - spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG; - - return nt_status; -} - - -static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, - const DATA_BLOB in, DATA_BLOB *out) -{ - struct spnego_state *spnego_state = gensec_security->private_data; - DATA_BLOB null_data_blob = data_blob(NULL, 0); - DATA_BLOB unwrapped_out = data_blob(NULL, 0); - struct spnego_data spnego_out; - struct spnego_data spnego; - - ssize_t len; - - *out = data_blob(NULL, 0); - - if (!out_mem_ctx) { - out_mem_ctx = spnego_state; - } - - /* and switch into the state machine */ - - switch (spnego_state->state_position) { - case SPNEGO_FALLBACK: - return gensec_update(spnego_state->sub_sec_security, - out_mem_ctx, in, out); - case SPNEGO_SERVER_START: - { - if (in.length) { - NTSTATUS nt_status; - - len = spnego_read_data(in, &spnego); - if (len == -1) { - return gensec_spnego_server_try_fallback(gensec_security, spnego_state, out_mem_ctx, in, out); - } - /* client sent NegTargetInit, we send NegTokenTarg */ - - /* OK, so it's real SPNEGO, check the packet's the one we expect */ - if (spnego.type != spnego_state->expected_packet) { - DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type, - spnego_state->expected_packet)); - dump_data(1, in.data, in.length); - spnego_free_data(&spnego); - return NT_STATUS_INVALID_PARAMETER; - } - - nt_status = gensec_spnego_server_parse_negTokenInit(gensec_security, - spnego_state, - out_mem_ctx, - spnego.negTokenInit.mechTypes, - spnego.negTokenInit.mechToken, - &unwrapped_out); - - nt_status = gensec_spnego_server_negTokenTarg(gensec_security, - spnego_state, - out_mem_ctx, - nt_status, - unwrapped_out, - out); - - spnego_free_data(&spnego); - - return nt_status; - } else { - const char **mechlist = gensec_security_oids(out_mem_ctx, GENSEC_OID_SPNEGO); - - spnego_out.type = SPNEGO_NEG_TOKEN_INIT; - spnego_out.negTokenInit.mechTypes = mechlist; - spnego_out.negTokenInit.reqFlags = 0; - spnego_out.negTokenInit.mechListMIC - = data_blob_string_const(talloc_asprintf(out_mem_ctx, "%s$@%s", lp_netbios_name(), lp_realm())); - spnego_out.negTokenInit.mechToken = unwrapped_out; - - if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) { - DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n")); - return NT_STATUS_INVALID_PARAMETER; - } - - /* set next state */ - spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG; - spnego_state->state_position = SPNEGO_SERVER_TARG; - - return NT_STATUS_MORE_PROCESSING_REQUIRED; - } - } - - case SPNEGO_CLIENT_START: - { - /* The server offers a list of mechanisms */ - - const char *my_mechs[] = {NULL, NULL}; - NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER; - - if (!in.length) { - /* client to produce negTokenInit */ - return gensec_spnego_client_negTokenInit(gensec_security, spnego_state, - out_mem_ctx, in, out); - } - - len = spnego_read_data(in, &spnego); - - if (len == -1) { - DEBUG(1, ("Invalid SPNEGO request:\n")); - dump_data(1, in.data, in.length); - return NT_STATUS_INVALID_PARAMETER; - } - - /* OK, so it's real SPNEGO, check the packet's the one we expect */ - if (spnego.type != spnego_state->expected_packet) { - DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type, - spnego_state->expected_packet)); - dump_data(1, in.data, in.length); - spnego_free_data(&spnego); - return NT_STATUS_INVALID_PARAMETER; - } - - if (spnego.negTokenInit.targetPrincipal) { - DEBUG(5, ("Server claims it's principal name is %s (ignored)\n", spnego.negTokenInit.targetPrincipal)); - } - - nt_status = gensec_spnego_client_parse_negTokenInit(gensec_security, - spnego_state, - out_mem_ctx, - spnego.negTokenInit.mechTypes, - spnego.negTokenInit.mechToken, - &unwrapped_out); - - if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(nt_status)) { - spnego_free_data(&spnego); - return nt_status; - } - - /* compose reply */ - my_mechs[0] = spnego_state->sub_sec_security->ops->oid; - - spnego_out.type = SPNEGO_NEG_TOKEN_INIT; - spnego_out.negTokenInit.mechTypes = my_mechs; - spnego_out.negTokenInit.reqFlags = 0; - spnego_out.negTokenInit.mechListMIC = null_data_blob; - spnego_out.negTokenInit.mechToken = unwrapped_out; - - if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) { - DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n")); - return NT_STATUS_INVALID_PARAMETER; - } - - /* set next state */ - spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG; - spnego_state->state_position = SPNEGO_CLIENT_TARG; - - if (NT_STATUS_IS_OK(nt_status)) { - spnego_state->no_response_expected = True; - } - - return NT_STATUS_MORE_PROCESSING_REQUIRED; - } - case SPNEGO_SERVER_TARG: - { - NTSTATUS nt_status; - if (!in.length) { - return NT_STATUS_INVALID_PARAMETER; - } - - len = spnego_read_data(in, &spnego); - - if (len == -1) { - DEBUG(1, ("Invalid SPNEGO request:\n")); - dump_data(1, in.data, in.length); - return NT_STATUS_INVALID_PARAMETER; - } - - /* OK, so it's real SPNEGO, check the packet's the one we expect */ - if (spnego.type != spnego_state->expected_packet) { - DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type, - spnego_state->expected_packet)); - dump_data(1, in.data, in.length); - spnego_free_data(&spnego); - return NT_STATUS_INVALID_PARAMETER; - } - - if (!spnego_state->sub_sec_security) { - DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n")); - return NT_STATUS_INVALID_PARAMETER; - } - - nt_status = gensec_update(spnego_state->sub_sec_security, - out_mem_ctx, - spnego.negTokenTarg.responseToken, - &unwrapped_out); - - nt_status = gensec_spnego_server_negTokenTarg(gensec_security, - spnego_state, - out_mem_ctx, - nt_status, - unwrapped_out, - out); - - spnego_free_data(&spnego); - - return nt_status; - } - case SPNEGO_CLIENT_TARG: - { - NTSTATUS nt_status; - if (!in.length) { - return NT_STATUS_INVALID_PARAMETER; - } - - len = spnego_read_data(in, &spnego); - - if (len == -1) { - DEBUG(1, ("Invalid SPNEGO request:\n")); - dump_data(1, in.data, in.length); - return NT_STATUS_INVALID_PARAMETER; - } - - /* OK, so it's real SPNEGO, check the packet's the one we expect */ - if (spnego.type != spnego_state->expected_packet) { - DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type, - spnego_state->expected_packet)); - dump_data(1, in.data, in.length); - spnego_free_data(&spnego); - return NT_STATUS_INVALID_PARAMETER; - } - - if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) { - return NT_STATUS_ACCESS_DENIED; - } - - if (spnego_state->no_response_expected) { - if (spnego.negTokenTarg.negResult != SPNEGO_ACCEPT_COMPLETED) { - DEBUG(3,("GENSEC SPNEGO: client GENSEC accepted, but server rejected (bad password?)\n")); - nt_status = NT_STATUS_INVALID_PARAMETER; - } else if (spnego.negTokenTarg.responseToken.length) { - DEBUG(2,("GENSEC SPNEGO: client GENSEC accepted, but server continued negotiation!\n")); - nt_status = NT_STATUS_INVALID_PARAMETER; - } else { - nt_status = NT_STATUS_OK; - } - } else { - nt_status = gensec_update(spnego_state->sub_sec_security, - out_mem_ctx, - spnego.negTokenTarg.responseToken, - &unwrapped_out); - - if (NT_STATUS_IS_OK(nt_status)) { - spnego_state->no_response_expected = True; - } - } - - spnego_free_data(&spnego); - - if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) - && !NT_STATUS_IS_OK(nt_status)) { - DEBUG(1, ("SPNEGO(%s) login failed: %s\n", - spnego_state->sub_sec_security->ops->name, - nt_errstr(nt_status))); - return nt_status; - } - - if (unwrapped_out.length) { - /* compose reply */ - spnego_out.type = SPNEGO_NEG_TOKEN_TARG; - spnego_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT; - spnego_out.negTokenTarg.supportedMech = NULL; - spnego_out.negTokenTarg.responseToken = unwrapped_out; - spnego_out.negTokenTarg.mechListMIC = null_data_blob; - - if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) { - DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n")); - return NT_STATUS_INVALID_PARAMETER; - } - - spnego_state->state_position = SPNEGO_CLIENT_TARG; - nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED; - } else { - - /* all done - server has accepted, and we agree */ - *out = null_data_blob; - - if (spnego.negTokenTarg.negResult != SPNEGO_ACCEPT_COMPLETED) { - /* unless of course it did not accept */ - DEBUG(1,("gensec_update ok but not accepted\n")); - nt_status = NT_STATUS_INVALID_PARAMETER; - } - } - - spnego_state->state_position = SPNEGO_DONE; - - return nt_status; - } - case SPNEGO_DONE: - return NT_STATUS_OK; - } - return NT_STATUS_INVALID_PARAMETER; -} - -static BOOL gensec_spnego_have_feature(struct gensec_security *gensec_security, - uint32_t feature) -{ - struct spnego_state *spnego_state = gensec_security->private_data; - if (!spnego_state->sub_sec_security) { - return False; - } - - return gensec_have_feature(spnego_state->sub_sec_security, - feature); -} - -static const struct gensec_security_ops gensec_spnego_security_ops = { - .name = "spnego", - .sasl_name = "GSS-SPNEGO", - .auth_type = DCERPC_AUTH_TYPE_SPNEGO, - .oid = GENSEC_OID_SPNEGO, - .client_start = gensec_spnego_client_start, - .server_start = gensec_spnego_server_start, - .update = gensec_spnego_update, - .seal_packet = gensec_spnego_seal_packet, - .sign_packet = gensec_spnego_sign_packet, - .sig_size = gensec_spnego_sig_size, - .check_packet = gensec_spnego_check_packet, - .unseal_packet = gensec_spnego_unseal_packet, - .wrap = gensec_spnego_wrap, - .unwrap = gensec_spnego_unwrap, - .session_key = gensec_spnego_session_key, - .session_info = gensec_spnego_session_info, - .have_feature = gensec_spnego_have_feature, - .enabled = True -}; - -NTSTATUS gensec_spnego_init(void) -{ - NTSTATUS ret; - ret = gensec_register(&gensec_spnego_security_ops); - if (!NT_STATUS_IS_OK(ret)) { - DEBUG(0,("Failed to register '%s' gensec backend!\n", - gensec_spnego_security_ops.name)); - return ret; - } - - return ret; -} diff --git a/source4/libcli/auth/spnego.h b/source4/libcli/auth/spnego.h deleted file mode 100644 index 1064370146..0000000000 --- a/source4/libcli/auth/spnego.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - RFC2478 Compliant SPNEGO implementation - - Copyright (C) Jim McDonough 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. -*/ - -#ifndef SAMBA_SPNEGO_H -#define SAMBA_SPNEGO_H - -#define SPNEGO_DELEG_FLAG 0x01 -#define SPNEGO_MUTUAL_FLAG 0x02 -#define SPNEGO_REPLAY_FLAG 0x04 -#define SPNEGO_SEQUENCE_FLAG 0x08 -#define SPNEGO_ANON_FLAG 0x10 -#define SPNEGO_CONF_FLAG 0x20 -#define SPNEGO_INTEG_FLAG 0x40 -#define SPNEGO_REQ_FLAG 0x80 - -enum spnego_negResult { - SPNEGO_ACCEPT_COMPLETED = 0, - SPNEGO_ACCEPT_INCOMPLETE = 1, - SPNEGO_REJECT = 2, - SPNEGO_NONE_RESULT = 3 -}; - -struct spnego_negTokenInit { - const char **mechTypes; - int reqFlags; - DATA_BLOB mechToken; - DATA_BLOB mechListMIC; - char *targetPrincipal; -}; - -struct spnego_negTokenTarg { - uint8_t negResult; - const char *supportedMech; - DATA_BLOB responseToken; - DATA_BLOB mechListMIC; -}; - -struct spnego_data { - int type; - struct spnego_negTokenInit negTokenInit; - struct spnego_negTokenTarg negTokenTarg; -}; - -enum spnego_message_type { - SPNEGO_NEG_TOKEN_INIT = 0, - SPNEGO_NEG_TOKEN_TARG = 1, -}; - -#endif diff --git a/source4/libcli/auth/spnego_parse.c b/source4/libcli/auth/spnego_parse.c deleted file mode 100644 index e48c32f0da..0000000000 --- a/source4/libcli/auth/spnego_parse.c +++ /dev/null @@ -1,375 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - RFC2478 Compliant SPNEGO implementation - - Copyright (C) Jim McDonough 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" -#include "auth/auth.h" -#include "asn_1.h" - -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_AUTH - -static BOOL read_negTokenInit(struct 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; - uint8_t context; - if (!asn1_peek_uint8(asn1, &context)) { - asn1->has_error = True; - break; - } - - switch (context) { - /* Read mechTypes */ - case ASN1_CONTEXT(0): - asn1_start_tag(asn1, ASN1_CONTEXT(0)); - asn1_start_tag(asn1, ASN1_SEQUENCE(0)); - - token->mechTypes = talloc(NULL, const char *); - for (i = 0; !asn1->has_error && - 0 < asn1_tag_remaining(asn1); i++) { - token->mechTypes = talloc_realloc(NULL, - token->mechTypes, - const char *, i+2); - asn1_read_OID(asn1, token->mechTypes + i); - if (token->mechTypes[i]) { - talloc_steal(token->mechTypes, - 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): - { - uint8_t type_peek; - asn1_start_tag(asn1, ASN1_CONTEXT(3)); - if (!asn1_peek_uint8(asn1, &type_peek)) { - asn1->has_error = True; - break; - } - if (type_peek == 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->targetPrincipal = 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(struct 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(struct 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)) { - uint8_t context; - if (!asn1_peek_uint8(asn1, &context)) { - asn1->has_error = True; - break; - } - - switch (context) { - 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(struct asn1_data *asn1, struct spnego_negTokenTarg *token) -{ - asn1_push_tag(asn1, ASN1_CONTEXT(1)); - asn1_push_tag(asn1, ASN1_SEQUENCE(0)); - - if (token->negResult != SPNEGO_NONE_RESULT) { - 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 spnego_read_data(DATA_BLOB data, struct spnego_data *token) -{ - struct asn1_data asn1; - ssize_t ret = -1; - uint8_t context; - - ZERO_STRUCTP(token); - ZERO_STRUCT(asn1); - - if (data.length == 0) { - return ret; - } - - asn1_load(&asn1, data); - - if (!asn1_peek_uint8(&asn1, &context)) { - asn1.has_error = True; - } else { - switch (context) { - case ASN1_APPLICATION(0): - asn1_start_tag(&asn1, ASN1_APPLICATION(0)); - asn1_check_OID(&asn1, GENSEC_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: - asn1.has_error = True; - break; - } - } - - if (!asn1.has_error) ret = asn1.ofs; - asn1_free(&asn1); - - return ret; -} - -ssize_t spnego_write_data(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, struct spnego_data *spnego) -{ - struct 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, GENSEC_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 spnego_free_data(struct spnego_data *spnego) -{ - BOOL ret = True; - - if (!spnego) goto out; - - switch(spnego->type) { - case SPNEGO_NEG_TOKEN_INIT: - if (spnego->negTokenInit.mechTypes) { - talloc_free(spnego->negTokenInit.mechTypes); - } - data_blob_free(&spnego->negTokenInit.mechToken); - data_blob_free(&spnego->negTokenInit.mechListMIC); - talloc_free(spnego->negTokenInit.targetPrincipal); - break; - case SPNEGO_NEG_TOKEN_TARG: - if (spnego->negTokenTarg.supportedMech) { - talloc_free(discard_const(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; -} - -- cgit