diff options
33 files changed, 1569 insertions, 1397 deletions
diff --git a/source4/auth/auth.h b/source4/auth/auth.h index c20b8dbf6f..0c8f71d859 100644 --- a/source4/auth/auth.h +++ b/source4/auth/auth.h @@ -96,6 +96,8 @@ struct auth_serversupplied_info struct auth_session_info { TALLOC_CTX *mem_ctx; + + int refcount; /* NT group information taken from the info3 structure */ NT_USER_TOKEN *nt_user_token; @@ -117,7 +119,8 @@ struct auth_context { BOOL challenge_may_be_modified; struct auth_methods *challenge_set_method; - /* What order are the various methods in? Try to stop it changing under us */ + + /* methods, in the order they should be called */ struct auth_methods *auth_method_list; TALLOC_CTX *mem_ctx; @@ -165,15 +168,6 @@ struct auth_init_function_entry { struct auth_init_function_entry *prev, *next; }; -struct auth_ntlmssp_state -{ - TALLOC_CTX *mem_ctx; - struct auth_context *auth_context; - struct auth_serversupplied_info *server_info; - struct ntlmssp_state *ntlmssp_state; -}; - -#define auth_ops __XXX_ERROR_BLA struct auth_operations { /* the name of the backend */ const char *name; @@ -188,11 +182,9 @@ struct auth_critical_sizes { int sizeof_auth_operations; int sizeof_auth_methods; int sizeof_auth_context; - int sizeof_auth_ntlmssp_state; int sizeof_auth_usersupplied_info; int sizeof_auth_serversupplied_info; int sizeof_auth_str; - int sizeof_auth_unistr; }; #endif /* _SMBAUTH_H_ */ diff --git a/source4/auth/auth_ntlmssp.c b/source4/auth/auth_ntlmssp.c deleted file mode 100644 index 183363a363..0000000000 --- a/source4/auth/auth_ntlmssp.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - Unix SMB/Netbios implementation. - Version 3.0 - handle NLTMSSP, server side - - Copyright (C) Andrew Tridgell 2001 - Copyright (C) Andrew Bartlett 2001-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" - -/** - * 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 auth_ntlmssp_state *auth_ntlmssp_state = ntlmssp_state->auth_context; - - return auth_ntlmssp_state->auth_context->get_ntlm_challenge(auth_ntlmssp_state->auth_context); -} - -/** - * 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 auth_ntlmssp_state *auth_ntlmssp_state = ntlmssp_state->auth_context; - - return auth_ntlmssp_state->auth_context->challenge_may_be_modified; -} - -/** - * 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) -{ - struct auth_ntlmssp_state *auth_ntlmssp_state = ntlmssp_state->auth_context; - struct auth_context *auth_context = auth_ntlmssp_state->auth_context; - - SMB_ASSERT(challenge->length == 8); - - auth_context->challenge = data_blob_talloc(auth_context->mem_ctx, - challenge->data, challenge->length); - - auth_context->challenge_set_by = "NTLMSSP callback (NTLM2)"; - - DEBUG(5, ("auth_context challenge set by %s\n", auth_context->challenge_set_by)); - DEBUG(5, ("challenge is: \n")); - dump_data(5, (const char *)auth_context->challenge.data, auth_context->challenge.length); - return NT_STATUS_OK; -} - -/** - * 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 auth_ntlmssp_state *auth_ntlmssp_state = ntlmssp_state->auth_context; - struct auth_usersupplied_info *user_info = NULL; - NTSTATUS nt_status; - -#if 0 - /* the client has given us its machine name (which we otherwise would not get on port 445). - we need to possibly reload smb.conf if smb.conf includes depend on the machine name */ - - set_remote_machine_name(auth_ntlmssp_state->ntlmssp_state->workstation, True); - - /* setup the string used by %U */ - /* sub_set_smb_name checks for weird internally */ - sub_set_smb_name(auth_ntlmssp_state->ntlmssp_state->user); - - reload_services(True); - -#endif - nt_status = make_user_info_map(&user_info, - auth_ntlmssp_state->ntlmssp_state->user, - auth_ntlmssp_state->ntlmssp_state->domain, - auth_ntlmssp_state->ntlmssp_state->workstation, - auth_ntlmssp_state->ntlmssp_state->lm_resp.data ? &auth_ntlmssp_state->ntlmssp_state->lm_resp : NULL, - auth_ntlmssp_state->ntlmssp_state->nt_resp.data ? &auth_ntlmssp_state->ntlmssp_state->nt_resp : NULL, - NULL, NULL, NULL, - True); - - if (!NT_STATUS_IS_OK(nt_status)) { - return nt_status; - } - - nt_status = auth_ntlmssp_state->auth_context->check_ntlm_password(auth_ntlmssp_state->auth_context, - user_info, &auth_ntlmssp_state->server_info); - - free_user_info(&user_info); - - if (!NT_STATUS_IS_OK(nt_status)) { - return nt_status; - } - if (auth_ntlmssp_state->server_info->user_session_key.length) { - DEBUG(10, ("Got NT session key of length %u\n", auth_ntlmssp_state->server_info->user_session_key.length)); - *user_session_key = data_blob_talloc(ntlmssp_state->mem_ctx, - auth_ntlmssp_state->server_info->user_session_key.data, - auth_ntlmssp_state->server_info->user_session_key.length); - } - if (auth_ntlmssp_state->server_info->lm_session_key.length) { - DEBUG(10, ("Got LM session key of length %u\n", auth_ntlmssp_state->server_info->lm_session_key.length)); - *lm_session_key = data_blob_talloc(ntlmssp_state->mem_ctx, - auth_ntlmssp_state->server_info->lm_session_key.data, - auth_ntlmssp_state->server_info->lm_session_key.length); - } - return nt_status; -} - -NTSTATUS auth_ntlmssp_start(struct auth_ntlmssp_state **auth_ntlmssp_state) -{ - NTSTATUS nt_status; - TALLOC_CTX *mem_ctx; - - mem_ctx = talloc_init("AUTH NTLMSSP context"); - - *auth_ntlmssp_state = talloc_zero(mem_ctx, sizeof(**auth_ntlmssp_state)); - if (!*auth_ntlmssp_state) { - DEBUG(0,("auth_ntlmssp_start: talloc failed!\n")); - talloc_destroy(mem_ctx); - return NT_STATUS_NO_MEMORY; - } - - ZERO_STRUCTP(*auth_ntlmssp_state); - - (*auth_ntlmssp_state)->mem_ctx = mem_ctx; - - if (!NT_STATUS_IS_OK(nt_status = ntlmssp_server_start(&(*auth_ntlmssp_state)->ntlmssp_state))) { - return nt_status; - } - - if (!NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(&(*auth_ntlmssp_state)->auth_context))) { - return nt_status; - } - - (*auth_ntlmssp_state)->ntlmssp_state->auth_context = (*auth_ntlmssp_state); - (*auth_ntlmssp_state)->ntlmssp_state->get_challenge = auth_ntlmssp_get_challenge; - (*auth_ntlmssp_state)->ntlmssp_state->may_set_challenge = auth_ntlmssp_may_set_challenge; - (*auth_ntlmssp_state)->ntlmssp_state->set_challenge = auth_ntlmssp_set_challenge; - (*auth_ntlmssp_state)->ntlmssp_state->check_password = auth_ntlmssp_check_password; - (*auth_ntlmssp_state)->ntlmssp_state->server_role = lp_server_role(); - - return NT_STATUS_OK; -} - -void auth_ntlmssp_end(struct auth_ntlmssp_state **auth_ntlmssp_state) -{ - TALLOC_CTX *mem_ctx = (*auth_ntlmssp_state)->mem_ctx; - - if ((*auth_ntlmssp_state)->ntlmssp_state) { - ntlmssp_end(&(*auth_ntlmssp_state)->ntlmssp_state); - } - if ((*auth_ntlmssp_state)->auth_context) { - free_auth_context(&(*auth_ntlmssp_state)->auth_context); - } - if ((*auth_ntlmssp_state)->server_info) { - free_server_info(&(*auth_ntlmssp_state)->server_info); - } - talloc_destroy(mem_ctx); - *auth_ntlmssp_state = NULL; -} - - -/** - * Next state function for the wrapped NTLMSSP state machine - * - * @param auth_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 auth_ntlmssp_update(struct auth_ntlmssp_state *auth_ntlmssp_state, - TALLOC_CTX *out_mem_ctx, - const DATA_BLOB in, DATA_BLOB *out) -{ - return ntlmssp_update(auth_ntlmssp_state->ntlmssp_state, - out_mem_ctx, - in, out); -} - -/** - * 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 auth_ntlmssp_get_session_info(struct auth_ntlmssp_state *auth_ntlmssp_state, - struct auth_session_info **session_info) -{ - NTSTATUS nt_status; - nt_status = make_session_info(auth_ntlmssp_state->server_info, session_info); - - if (!NT_STATUS_IS_OK(nt_status)) { - return nt_status; - } - - /* the session_info owns this now */ - auth_ntlmssp_state->server_info = NULL; - - (*session_info)->session_key = data_blob_talloc((*session_info)->mem_ctx, - auth_ntlmssp_state->ntlmssp_state->session_key.data, - auth_ntlmssp_state->ntlmssp_state->session_key.length); - - (*session_info)->workstation = talloc_strdup((*session_info)->mem_ctx, - auth_ntlmssp_state->ntlmssp_state->workstation); - - return NT_STATUS_OK; -} diff --git a/source4/auth/auth_util.c b/source4/auth/auth_util.c index 097f504538..06947999b3 100644 --- a/source4/auth/auth_util.c +++ b/source4/auth/auth_util.c @@ -590,6 +590,7 @@ NTSTATUS make_session_info(struct auth_serversupplied_info *server_info, return NT_STATUS_NO_MEMORY; } + (*session_info)->refcount = 1; (*session_info)->mem_ctx = server_info->mem_ctx; server_info->mem_ctx = NULL; /* make sure not to accidentily destory it, and this information is now constant */ @@ -611,6 +612,22 @@ NTSTATUS make_session_info(struct auth_serversupplied_info *server_info, return nt_status; } +/*************************************************************************** + Clear out a server_info struct that has been allocated +***************************************************************************/ + +void free_session_info(struct auth_session_info **session_info) +{ + DEBUG(5,("attempting to free a session_info structure\n")); + if (!*session_info) { + (*session_info)->refcount--; + if ((*session_info)->refcount <= 0) { + talloc_destroy((*session_info)->mem_ctx); + } + } + *session_info = NULL; +} + /** * Squash an NT_STATUS in line with security requirements. * In an attempt to avoid giving the whole game away when users diff --git a/source4/auth/config.m4 b/source4/auth/config.m4 index 01e4574d94..3c4f86ecea 100644 --- a/source4/auth/config.m4 +++ b/source4/auth/config.m4 @@ -3,4 +3,4 @@ dnl # AUTH Server subsystem SMB_MODULE_MK(auth_sam,AUTH,STATIC,auth/config.mk) SMB_MODULE_MK(auth_builtin,AUTH,STATIC,auth/config.mk) -SMB_SUBSYSTEM_MK(AUTH,auth/config.mk) +SMB_SUBSYSTEM_MK(AUTH,auth/config.mk,[],[],[SAMDB]) diff --git a/source4/auth/config.mk b/source4/auth/config.mk index c9b47e745b..b4082cb9e5 100644 --- a/source4/auth/config.mk +++ b/source4/auth/config.mk @@ -5,6 +5,8 @@ [MODULE::auth_sam] INIT_OBJ_FILES = \ auth/auth_sam.o +REQUIRED_SUBSYSTEMS = \ + SAMDB # End MODULE auth_sam ####################### @@ -22,7 +24,6 @@ INIT_OBJ_FILES = \ INIT_OBJ_FILES = \ auth/auth.o ADD_OBJ_FILES = \ - auth/auth_ntlmssp.o \ auth/auth_util.o \ auth/pampass.o \ auth/pass_check.o diff --git a/source4/libcli/auth/gensec.c b/source4/libcli/auth/gensec.c index 138c4af35c..f6d6db9e62 100644 --- a/source4/libcli/auth/gensec.c +++ b/source4/libcli/auth/gensec.c @@ -23,46 +23,14 @@ #include "includes.h" -static const struct gensec_security_ops gensec_ntlmssp_security_ops = { - .name = "ntlmssp", - .sasl_name = "NTLM", - .auth_type = DCERPC_AUTH_TYPE_NTLMSSP, - .oid = OID_NTLMSSP, - .client_start = gensec_ntlmssp_client_start, - .update = gensec_ntlmssp_update, - .seal = gensec_ntlmssp_seal_packet, - .sign = gensec_ntlmssp_sign_packet, - .check_sig = gensec_ntlmssp_check_packet, - .unseal = gensec_ntlmssp_unseal_packet, - .session_key = gensec_ntlmssp_session_key, - .end = gensec_ntlmssp_end -}; - - -static const struct gensec_security_ops gensec_spnego_security_ops = { - .name = "spnego", - .sasl_name = "GSS-SPNEGO", - .oid = OID_SPNEGO, - .client_start = gensec_spnego_client_start, - .update = gensec_spnego_update, - .seal = gensec_spnego_seal_packet, - .sign = gensec_spnego_sign_packet, - .check_sig = gensec_spnego_check_packet, - .unseal = gensec_spnego_unseal_packet, - .session_key = gensec_spnego_session_key, - .end = gensec_spnego_end -}; - -static const struct gensec_security_ops *generic_security_ops[] = { - &gensec_ntlmssp_security_ops, - &gensec_spnego_security_ops, - NULL -}; - -const struct gensec_security_ops *gensec_security_by_authtype(uint8_t auth_type) +/* the list of currently registered GENSEC backends */ +const static struct gensec_security_ops **generic_security_ops; +static int num_backends; + +static const struct gensec_security_ops *gensec_security_by_authtype(uint8_t auth_type) { int i; - for (i=0; generic_security_ops[i]; i++) { + for (i=0; i < num_backends; i++) { if (generic_security_ops[i]->auth_type == auth_type) { return generic_security_ops[i]; } @@ -71,10 +39,10 @@ const struct gensec_security_ops *gensec_security_by_authtype(uint8_t auth_type) return NULL; } -const struct gensec_security_ops *gensec_security_by_oid(const char *oid) +static const struct gensec_security_ops *gensec_security_by_oid(const char *oid) { int i; - for (i=0; generic_security_ops[i]; i++) { + for (i=0; i < num_backends; i++) { if (generic_security_ops[i]->oid && (strcmp(generic_security_ops[i]->oid, oid) == 0)) { return generic_security_ops[i]; @@ -84,10 +52,10 @@ const struct gensec_security_ops *gensec_security_by_oid(const char *oid) return NULL; } -const struct gensec_security_ops *gensec_security_by_sasl_name(const char *sasl_name) +static const struct gensec_security_ops *gensec_security_by_sasl_name(const char *sasl_name) { int i; - for (i=0; generic_security_ops[i]; i++) { + for (i=0; i < 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]; @@ -97,8 +65,359 @@ const struct gensec_security_ops *gensec_security_by_sasl_name(const char *sasl_ return NULL; } -const struct gensec_security_ops **gensec_security_all(void) +static const struct gensec_security_ops *gensec_security_by_name(const char *name) +{ + int i; + for (i=0; i < 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 = num_backends; return generic_security_ops; } +static NTSTATUS gensec_start(struct gensec_security **gensec_security) +{ + TALLOC_CTX *mem_ctx; + /* awaiting a correct fix from metze */ + if (!gensec_init()) { + return NT_STATUS_INTERNAL_ERROR; + } + + mem_ctx = talloc_init("gensec_security struct"); + if (!mem_ctx) { + return NT_STATUS_NO_MEMORY; + } + + (*gensec_security) = talloc_p(mem_ctx, struct gensec_security); + if (!(*gensec_security)) { + talloc_destroy(mem_ctx); + return NT_STATUS_NO_MEMORY; + } + + (*gensec_security)->mem_ctx = mem_ctx; + (*gensec_security)->ops = NULL; + + return NT_STATUS_OK; +} + +NTSTATUS gensec_client_start(struct gensec_security **gensec_security) +{ + NTSTATUS status; + status = gensec_start(gensec_security); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + (*gensec_security)->gensec_role = GENSEC_CLIENT; + (*gensec_security)->password_callback = NULL; + + ZERO_STRUCT((*gensec_security)->user); + + return status; +} + +NTSTATUS gensec_server_start(struct gensec_security **gensec_security) +{ + NTSTATUS status; + status = gensec_start(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; + 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, ("Faild 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, ("Faild to start GENSEC server mech %s: %s\n", + gensec_security->ops->name, nt_errstr(status))); + } + return status; + } + } + return NT_STATUS_INVALID_PARAMETER; +} + +NTSTATUS gensec_start_mech_by_authtype(struct gensec_security *gensec_security, + uint8_t authtype) +{ + gensec_security->ops = gensec_security_by_authtype(authtype); + if (!gensec_security->ops) { + DEBUG(1, ("Could not find GENSEC backend for authtype=%d\n", (int)authtype)); + return NT_STATUS_INVALID_PARAMETER; + } + return gensec_start_mech(gensec_security); +} + +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(1, ("Could not find GENSEC backend for oid=%s\n", mech_oid)); + return NT_STATUS_INVALID_PARAMETER; + } + return gensec_start_mech(gensec_security); +} + +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(1, ("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, DATA_BLOB *sig) +{ + return gensec_security->ops->unseal_packet(gensec_security, mem_ctx, data, length, sig); +} + +NTSTATUS gensec_check_packet(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const uint8_t *data, size_t length, + const DATA_BLOB *sig) +{ + return gensec_security->ops->check_packet(gensec_security, mem_ctx, data, length, sig); +} + +NTSTATUS gensec_seal_packet(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + uint8_t *data, size_t length, + DATA_BLOB *sig) +{ + return gensec_security->ops->seal_packet(gensec_security, mem_ctx, data, length, sig); +} + +NTSTATUS gensec_sign_packet(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const uint8_t *data, size_t length, + DATA_BLOB *sig) +{ + return gensec_security->ops->sign_packet(gensec_security, mem_ctx, data, length, sig); +} + +NTSTATUS gensec_session_key(struct gensec_security *gensec_security, + DATA_BLOB *session_key) +{ + return gensec_security->ops->session_key(gensec_security, session_key); +} + +NTSTATUS gensec_session_info(struct gensec_security *gensec_security, + struct auth_session_info **session_info) +{ + 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); +} + +void gensec_end(struct gensec_security **gensec_security) +{ + if ((*gensec_security)->ops) { + (*gensec_security)->ops->end(*gensec_security); + } + (*gensec_security)->private_data = NULL; + talloc_destroy((*gensec_security)->mem_ctx); + + gensec_security = NULL; +} + +/** + * Set a username on a GENSEC context - ensures it is talloc()ed + * + */ + +NTSTATUS gensec_set_username(struct gensec_security *gensec_security, const char *user) +{ + gensec_security->user.name = talloc_strdup(gensec_security->mem_ctx, user); + if (!gensec_security->user.name) { + return NT_STATUS_NO_MEMORY; + } + return NT_STATUS_OK; +} + +/** + * Set a domain on a GENSEC context - ensures it is talloc()ed + * + */ + +NTSTATUS gensec_set_domain(struct gensec_security *gensec_security, const char *domain) +{ + gensec_security->user.domain = talloc_strdup(gensec_security->mem_ctx, domain); + if (!gensec_security->user.domain) { + return NT_STATUS_NO_MEMORY; + } + return NT_STATUS_OK; +} + +/** + * Set the password outright on GENSEC context - ensures it is talloc()ed, and that we will + * not do a callback + * + */ + +NTSTATUS gensec_set_password(struct gensec_security *gensec_security, + const char *password) +{ + gensec_security->user.password = talloc_strdup(gensec_security->mem_ctx, password); + if (!gensec_security->user.password) { + return NT_STATUS_NO_MEMORY; + } + return NT_STATUS_OK; +} + +/** + * Set a password callback, if the gensec module we use demands a password + */ + +void gensec_set_password_callback(struct gensec_security *gensec_security, + gensec_password_callback callback, void *callback_private_data) +{ + gensec_security->password_callback = callback; + gensec_security->password_callback_private = callback_private_data; +} + +/** + * Get (or call back for) a password. + */ + +NTSTATUS gensec_get_password(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + char **password) +{ + if (gensec_security->user.password) { + *password = talloc_strdup(mem_ctx, gensec_security->user.password); + if (!*password) { + return NT_STATUS_NO_MEMORY; + } else { + return NT_STATUS_OK; + } + } + if (!gensec_security->password_callback) { + return NT_STATUS_INVALID_PARAMETER; + } + return gensec_security->password_callback(gensec_security, mem_ctx, password); +} + +/* + register a GENSEC backend. + + The 'name' can be later used by other backends to find the operations + structure for this backend. +*/ +static NTSTATUS gensec_register(const void *_ops) +{ + const struct gensec_security_ops *ops = _ops; + + 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(generic_security_ops, sizeof(generic_security_ops[0]) * (num_backends+1)); + if (!generic_security_ops) { + smb_panic("out of memory in gensec_register"); + } + + generic_security_ops[num_backends] = ops; + + 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 +*/ +BOOL gensec_init(void) +{ + static BOOL initialised; + NTSTATUS status; + + /* this is *completly* the wrong way to do this */ + if (initialised) { + return True; + } + + status = register_subsystem("gensec", gensec_register); + if (!NT_STATUS_IS_OK(status)) { + return False; + } + + /* FIXME: Perhaps panic if a basic backend, such as NTLMSSP, fails to initialise? */ + gensec_ntlmssp_init(); + gensec_spengo_init(); + gensec_dcerpc_schannel_init(); + + initialised = True; + DEBUG(3,("GENSEC subsystem version %d initialised\n", GENSEC_INTERFACE_VERSION)); + return True; +} diff --git a/source4/libcli/auth/gensec.h b/source4/libcli/auth/gensec.h index 2a469e0f57..463b484a7f 100644 --- a/source4/libcli/auth/gensec.h +++ b/source4/libcli/auth/gensec.h @@ -27,6 +27,7 @@ struct gensec_user { const char *domain; const char *name; const char *password; + char schan_session_key[16]; }; /* GENSEC mode */ enum gensec_role @@ -38,27 +39,47 @@ enum gensec_role struct gensec_security_ops { const char *name; const char *sasl_name; - uint8 auth_type; + uint8 auth_type; /* 0 if not offered on DCE-RPC */ const char *oid; /* NULL if not offered by SPENGO */ NTSTATUS (*client_start)(struct gensec_security *gensec_security); NTSTATUS (*server_start)(struct gensec_security *gensec_security); NTSTATUS (*update)(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, const DATA_BLOB in, DATA_BLOB *out); - NTSTATUS (*seal)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx, + NTSTATUS (*seal_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx, uint8_t *data, size_t length, DATA_BLOB *sig); - NTSTATUS (*sign)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx, + NTSTATUS (*sign_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx, const uint8_t *data, size_t length, DATA_BLOB *sig); - NTSTATUS (*check_sig)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx, - const uint8_t *data, size_t length, const DATA_BLOB *sig); - NTSTATUS (*unseal)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx, - uint8_t *data, size_t length, DATA_BLOB *sig); + NTSTATUS (*check_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx, + const uint8_t *data, size_t length, const DATA_BLOB *sig); + NTSTATUS (*unseal_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx, + uint8_t *data, size_t length, DATA_BLOB *sig); NTSTATUS (*session_key)(struct gensec_security *gensec_security, DATA_BLOB *session_key); + NTSTATUS (*session_info)(struct gensec_security *gensec_security, + struct auth_session_info **session_info); void (*end)(struct gensec_security *gensec_security); }; +typedef NTSTATUS (*gensec_password_callback)(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx, + char **password); + +#define GENSEC_INTERFACE_VERSION 0 + struct gensec_security { - struct gensec_user user; - void *private_data; + TALLOC_CTX *mem_ctx; + gensec_password_callback password_callback; + void *password_callback_private; const struct gensec_security_ops *ops; + void *private_data; + struct gensec_user user; + enum gensec_role gensec_role; }; +/* 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; +}; + + + diff --git a/source4/libcli/auth/gensec_ntlmssp.c b/source4/libcli/auth/gensec_ntlmssp.c index f7e9dddd2f..9f7c4c6f86 100644 --- a/source4/libcli/auth/gensec_ntlmssp.c +++ b/source4/libcli/auth/gensec_ntlmssp.c @@ -23,33 +23,220 @@ #include "includes.h" +struct gensec_ntlmssp_state { + TALLOC_CTX *mem_ctx; + struct auth_context *auth_context; + struct auth_serversupplied_info *server_info; + struct ntlmssp_state *ntlmssp_state; +}; -NTSTATUS gensec_ntlmssp_client_start(struct gensec_security *gensec_security) + +/** + * 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; + + return gensec_ntlmssp_state->auth_context->get_ntlm_challenge(gensec_ntlmssp_state->auth_context); +} + +/** + * 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 gensec_ntlmssp_state->auth_context->challenge_may_be_modified; +} + +/** + * 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) +{ + struct gensec_ntlmssp_state *gensec_ntlmssp_state = ntlmssp_state->auth_context; + struct auth_context *auth_context = gensec_ntlmssp_state->auth_context; + + SMB_ASSERT(challenge->length == 8); + + auth_context->challenge = data_blob_talloc(auth_context->mem_ctx, + challenge->data, challenge->length); + + auth_context->challenge_set_by = "NTLMSSP callback (NTLM2)"; + + DEBUG(5, ("auth_context challenge set by %s\n", auth_context->challenge_set_by)); + DEBUG(5, ("challenge is: \n")); + dump_data(5, (const char *)auth_context->challenge.data, auth_context->challenge.length); + return NT_STATUS_OK; +} + +/** + * 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; + +#if 0 + /* the client has given us its machine name (which we otherwise would not get on port 445). + we need to possibly reload smb.conf if smb.conf includes depend on the machine name */ + + set_remote_machine_name(gensec_ntlmssp_state->ntlmssp_state->workstation, True); + + /* setup the string used by %U */ + /* sub_set_smb_name checks for weird internally */ + sub_set_smb_name(gensec_ntlmssp_state->ntlmssp_state->user); + + reload_services(True); + +#endif + nt_status = make_user_info_map(&user_info, + 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); + + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + nt_status = gensec_ntlmssp_state->auth_context->check_ntlm_password(gensec_ntlmssp_state->auth_context, + user_info, &gensec_ntlmssp_state->server_info); + + free_user_info(&user_info); + + if (!NT_STATUS_IS_OK(nt_status)) { + 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->mem_ctx, + 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->mem_ctx, + gensec_ntlmssp_state->server_info->lm_session_key.data, + gensec_ntlmssp_state->server_info->lm_session_key.length); + } + return nt_status; +} + +static NTSTATUS gensec_ntlmssp_start(struct gensec_security *gensec_security) +{ + struct gensec_ntlmssp_state *gensec_ntlmssp_state; + + TALLOC_CTX *mem_ctx = talloc_init("gensec_ntlmssp"); + if (!mem_ctx) { + return NT_STATUS_NO_MEMORY; + } + + gensec_ntlmssp_state = talloc_p(mem_ctx, struct gensec_ntlmssp_state); + if (!gensec_ntlmssp_state) { + return NT_STATUS_NO_MEMORY; + } + + gensec_ntlmssp_state->mem_ctx = mem_ctx; + gensec_ntlmssp_state->ntlmssp_state = NULL; + gensec_ntlmssp_state->auth_context = NULL; + gensec_ntlmssp_state->server_info = NULL; + + 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; + NTSTATUS status; + struct ntlmssp_state *ntlmssp_state; + struct gensec_ntlmssp_state *gensec_ntlmssp_state; + + status = gensec_ntlmssp_start(gensec_security); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + gensec_ntlmssp_state = gensec_security->private_data; + + if (!NT_STATUS_IS_OK(nt_status = ntlmssp_server_start(&gensec_ntlmssp_state->ntlmssp_state))) { + return nt_status; + } + + ntlmssp_state = gensec_ntlmssp_state->ntlmssp_state; + if (!NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(&gensec_ntlmssp_state->auth_context))) { + return nt_status; + } + + 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 ntlmssp_state *ntlmssp_state = NULL; + struct gensec_ntlmssp_state *gensec_ntlmssp_state; + char *password = NULL; + NTSTATUS status; + status = gensec_ntlmssp_start(gensec_security); + if (!NT_STATUS_IS_OK(status)) { + return status; + } - status = ntlmssp_client_start(&ntlmssp_state); + gensec_ntlmssp_state = gensec_security->private_data; + status = ntlmssp_client_start(&gensec_ntlmssp_state->ntlmssp_state); if (!NT_STATUS_IS_OK(status)) { return status; } - status = ntlmssp_set_domain(ntlmssp_state, gensec_security->user.domain); + status = ntlmssp_set_domain(gensec_ntlmssp_state->ntlmssp_state, + gensec_security->user.domain); if (!NT_STATUS_IS_OK(status)) { return status; } - status = ntlmssp_set_username(ntlmssp_state, gensec_security->user.name); + status = ntlmssp_set_username(gensec_ntlmssp_state->ntlmssp_state, + gensec_security->user.name); if (!NT_STATUS_IS_OK(status)) { return status; } - status = ntlmssp_set_password(ntlmssp_state, gensec_security->user.password); + status = gensec_get_password(gensec_security, gensec_ntlmssp_state->mem_ctx, &password); if (!NT_STATUS_IS_OK(status)) { return status; } - - gensec_security->private_data = ntlmssp_state; + + if (password) { + status = ntlmssp_set_password(gensec_ntlmssp_state->ntlmssp_state, + password); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + + gensec_security->private_data = gensec_ntlmssp_state; return status; } @@ -57,66 +244,154 @@ NTSTATUS gensec_ntlmssp_client_start(struct gensec_security *gensec_security) /* wrappers for the ntlmssp_*() functions */ -NTSTATUS gensec_ntlmssp_unseal_packet(struct gensec_security *gensec_security, +static NTSTATUS gensec_ntlmssp_unseal_packet(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx, uint8_t *data, size_t length, DATA_BLOB *sig) { - struct ntlmssp_state *ntlmssp_state = gensec_security->private_data; + struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; - return ntlmssp_unseal_packet(ntlmssp_state, mem_ctx, data, length, sig); + return ntlmssp_unseal_packet(gensec_ntlmssp_state->ntlmssp_state, mem_ctx, data, length, sig); } -NTSTATUS gensec_ntlmssp_check_packet(struct gensec_security *gensec_security, +static NTSTATUS gensec_ntlmssp_check_packet(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx, const uint8_t *data, size_t length, const DATA_BLOB *sig) { - struct ntlmssp_state *ntlmssp_state = gensec_security->private_data; + struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; - return ntlmssp_check_packet(ntlmssp_state, mem_ctx, data, length, sig); + return ntlmssp_check_packet(gensec_ntlmssp_state->ntlmssp_state, mem_ctx, data, length, sig); } -NTSTATUS gensec_ntlmssp_seal_packet(struct gensec_security *gensec_security, +static NTSTATUS gensec_ntlmssp_seal_packet(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx, uint8_t *data, size_t length, DATA_BLOB *sig) { - struct ntlmssp_state *ntlmssp_state = gensec_security->private_data; + struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; - return ntlmssp_seal_packet(ntlmssp_state, mem_ctx, data, length, sig); + return ntlmssp_seal_packet(gensec_ntlmssp_state->ntlmssp_state, mem_ctx, data, length, sig); } -NTSTATUS gensec_ntlmssp_sign_packet(struct gensec_security *gensec_security, +static NTSTATUS gensec_ntlmssp_sign_packet(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx, const uint8_t *data, size_t length, DATA_BLOB *sig) { - struct ntlmssp_state *ntlmssp_state = gensec_security->private_data; + struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; - return ntlmssp_sign_packet(ntlmssp_state, mem_ctx, data, length, sig); + return ntlmssp_sign_packet(gensec_ntlmssp_state->ntlmssp_state, mem_ctx, data, length, sig); } -NTSTATUS gensec_ntlmssp_session_key(struct gensec_security *gensec_security, +static NTSTATUS gensec_ntlmssp_session_key(struct gensec_security *gensec_security, DATA_BLOB *session_key) { - struct ntlmssp_state *ntlmssp_state = gensec_security->private_data; + struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; - return ntlmssp_session_key(ntlmssp_state, session_key); + return ntlmssp_session_key(gensec_ntlmssp_state->ntlmssp_state, session_key); } -NTSTATUS gensec_ntlmssp_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, +/** + * Next state function for the wrapped NTLMSSP state machine + * + * @param gensec_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. + */ + +static NTSTATUS gensec_ntlmssp_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, const DATA_BLOB in, DATA_BLOB *out) { - struct ntlmssp_state *ntlmssp_state = gensec_security->private_data; + struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; + + return ntlmssp_update(gensec_ntlmssp_state->ntlmssp_state, out_mem_ctx, in, out); +} + +/** + * 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 = make_session_info(gensec_ntlmssp_state->server_info, session_info); + + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + /* the session_info owns this now */ + gensec_ntlmssp_state->server_info = NULL; + + (*session_info)->session_key = data_blob_talloc((*session_info)->mem_ctx, + gensec_ntlmssp_state->ntlmssp_state->session_key.data, + gensec_ntlmssp_state->ntlmssp_state->session_key.length); + + (*session_info)->workstation = talloc_strdup((*session_info)->mem_ctx, + gensec_ntlmssp_state->ntlmssp_state->workstation); - return ntlmssp_update(ntlmssp_state, out_mem_ctx, in, out); + return NT_STATUS_OK; } -void gensec_ntlmssp_end(struct gensec_security *gensec_security) +static void gensec_ntlmssp_end(struct gensec_security *gensec_security) { - struct ntlmssp_state *ntlmssp_state = gensec_security->private_data; + struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; - ntlmssp_end(&ntlmssp_state); + if (gensec_ntlmssp_state->ntlmssp_state) { + ntlmssp_end(&gensec_ntlmssp_state->ntlmssp_state); + } + if (gensec_ntlmssp_state->auth_context) { + free_auth_context(&gensec_ntlmssp_state->auth_context); + } + if (gensec_ntlmssp_state->server_info) { + free_server_info(&gensec_ntlmssp_state->server_info); + } + talloc_destroy(gensec_ntlmssp_state->mem_ctx); gensec_security->private_data = NULL; } + +static const struct gensec_security_ops gensec_ntlmssp_security_ops = { + .name = "ntlmssp", + .sasl_name = "NTLM", + .auth_type = DCERPC_AUTH_TYPE_NTLMSSP, + .oid = OID_NTLMSSP, + .client_start = gensec_ntlmssp_client_start, + .server_start = gensec_ntlmssp_server_start, + .update = gensec_ntlmssp_update, + .seal_packet = gensec_ntlmssp_seal_packet, + .sign_packet = gensec_ntlmssp_sign_packet, + .check_packet = gensec_ntlmssp_check_packet, + .unseal_packet = gensec_ntlmssp_unseal_packet, + .session_key = gensec_ntlmssp_session_key, + .session_info = gensec_ntlmssp_session_info, + .end = gensec_ntlmssp_end +}; + + +NTSTATUS gensec_ntlmssp_init(void) +{ + NTSTATUS ret; + ret = register_backend("gensec", &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; + } + + /* ugly cludge, but we need the auth subsystem for this to work */ + auth_init(); + + return ret; +} diff --git a/source4/libcli/auth/ntlmssp_sign.c b/source4/libcli/auth/ntlmssp_sign.c index d680da9495..80ce1cccc0 100644 --- a/source4/libcli/auth/ntlmssp_sign.c +++ b/source4/libcli/auth/ntlmssp_sign.c @@ -160,8 +160,6 @@ static NTSTATUS ntlmssp_make_packet_signature(struct ntlmssp_state *ntlmssp_stat return NT_STATUS_NO_MEMORY; } - dump_data_pw("ntlmssp hash:\n", ntlmssp_state->ntlmssp_hash, - sizeof(ntlmssp_state->ntlmssp_hash)); NTLMSSPcalc_ap(ntlmssp_state->ntlmssp_hash, sig->data+4, sig->length-4); } dump_data_pw("calculated ntlmssp signature\n", sig->data, sig->length); diff --git a/source4/libcli/auth/spnego.c b/source4/libcli/auth/spnego.c index 321b13afdc..bb9d2504ac 100644 --- a/source4/libcli/auth/spnego.c +++ b/source4/libcli/auth/spnego.c @@ -27,7 +27,25 @@ #undef DBGC_CLASS #define DBGC_CLASS DBGC_AUTH -NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_security) +enum spnego_state_position { + SPNEGO_SERVER_START, + SPNEGO_CLIENT_GET_MECHS, + SPNEGO_CLIENT_SEND_MECHS, + SPNEGO_TARG, + SPNEGO_FALLBACK, + SPNEGO_DONE +}; + +struct spnego_state { + TALLOC_CTX *mem_ctx; + uint_t ref_count; + enum spnego_message_type expected_packet; + enum spnego_message_type state_position; + negResult_t result; + struct gensec_security *sub_sec_security; +}; + +static NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_security) { struct spnego_state *spnego_state; TALLOC_CTX *mem_ctx = talloc_init("gensec_spengo_client_start"); @@ -40,11 +58,11 @@ NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_security) return NT_STATUS_NO_MEMORY; } - spnego_state->role = SPNEGO_CLIENT; spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT; spnego_state->state_position = SPNEGO_CLIENT_GET_MECHS; spnego_state->result = SPNEGO_ACCEPT_INCOMPLETE; spnego_state->mem_ctx = mem_ctx; + spnego_state->sub_sec_security = NULL; gensec_security->private_data = spnego_state; return NT_STATUS_OK; @@ -53,9 +71,9 @@ NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_security) /* wrappers for the spnego_*() functions */ -NTSTATUS gensec_spnego_unseal_packet(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - uint8_t *data, size_t length, DATA_BLOB *sig) +static NTSTATUS gensec_spnego_unseal_packet(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + uint8_t *data, size_t length, DATA_BLOB *sig) { struct spnego_state *spnego_state = gensec_security->private_data; @@ -64,11 +82,11 @@ NTSTATUS gensec_spnego_unseal_packet(struct gensec_security *gensec_security, return NT_STATUS_INVALID_PARAMETER; } - return spnego_state->sub_sec_security.ops->unseal(&spnego_state->sub_sec_security, - mem_ctx, data, length, sig); + return gensec_unseal_packet(spnego_state->sub_sec_security, + mem_ctx, data, length, sig); } -NTSTATUS gensec_spnego_check_packet(struct gensec_security *gensec_security, +static NTSTATUS gensec_spnego_check_packet(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx, const uint8_t *data, size_t length, const DATA_BLOB *sig) @@ -81,11 +99,11 @@ NTSTATUS gensec_spnego_check_packet(struct gensec_security *gensec_security, return NT_STATUS_INVALID_PARAMETER; } - return spnego_state->sub_sec_security.ops->check_sig(&spnego_state->sub_sec_security, - mem_ctx, data, length, sig); + return gensec_check_packet(spnego_state->sub_sec_security, + mem_ctx, data, length, sig); } -NTSTATUS gensec_spnego_seal_packet(struct gensec_security *gensec_security, +static NTSTATUS gensec_spnego_seal_packet(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx, uint8_t *data, size_t length, DATA_BLOB *sig) @@ -98,11 +116,11 @@ NTSTATUS gensec_spnego_seal_packet(struct gensec_security *gensec_security, return NT_STATUS_INVALID_PARAMETER; } - return spnego_state->sub_sec_security.ops->seal(&spnego_state->sub_sec_security, - mem_ctx, data, length, sig); + return gensec_seal_packet(spnego_state->sub_sec_security, + mem_ctx, data, length, sig); } -NTSTATUS gensec_spnego_sign_packet(struct gensec_security *gensec_security, +static NTSTATUS gensec_spnego_sign_packet(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx, const uint8_t *data, size_t length, DATA_BLOB *sig) @@ -114,11 +132,11 @@ NTSTATUS gensec_spnego_sign_packet(struct gensec_security *gensec_security, return NT_STATUS_INVALID_PARAMETER; } - return spnego_state->sub_sec_security.ops->sign(&spnego_state->sub_sec_security, - mem_ctx, data, length, sig); + return gensec_sign_packet(spnego_state->sub_sec_security, + mem_ctx, data, length, sig); } -NTSTATUS gensec_spnego_session_key(struct gensec_security *gensec_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; @@ -127,11 +145,11 @@ NTSTATUS gensec_spnego_session_key(struct gensec_security *gensec_security, return NT_STATUS_INVALID_PARAMETER; } - return spnego_state->sub_sec_security.ops->session_key(&spnego_state->sub_sec_security, - session_key); + return gensec_session_key(spnego_state->sub_sec_security, + session_key); } -NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, +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; @@ -139,7 +157,6 @@ NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TALLOC_CT DATA_BLOB unwrapped_out; struct spnego_data spnego_out; struct spnego_data spnego; - const struct gensec_security_ops *op; ssize_t len; @@ -148,32 +165,38 @@ NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TALLOC_CT } if (spnego_state->state_position == SPNEGO_FALLBACK) { - return spnego_state->sub_sec_security.ops->update(&spnego_state->sub_sec_security, - out_mem_ctx, in, out); + return gensec_update(spnego_state->sub_sec_security, + out_mem_ctx, in, out); } len = spnego_read_data(in, &spnego); if (len == -1 && spnego_state->state_position == SPNEGO_SERVER_START) { int i; - const struct gensec_security_ops **all_ops = gensec_security_all(); - for (i=0; all_ops[i]; i++) { + 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; - op = all_ops[i]; - if (!op->oid) { + if (!all_ops[i]->oid) { continue; } - nt_status = op->server_start(&spnego_state->sub_sec_security); + nt_status = gensec_server_start(&spnego_state->sub_sec_security); if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security, + all_ops[i]->oid); + if (!NT_STATUS_IS_OK(nt_status)) { + gensec_end(&spnego_state->sub_sec_security); continue; } - nt_status = op->update(&spnego_state->sub_sec_security, - out_mem_ctx, in, out); + 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; } - op->end(&spnego_state->sub_sec_security); + gensec_end(&spnego_state->sub_sec_security); } DEBUG(1, ("Failed to parse SPENGO request\n")); return NT_STATUS_INVALID_PARAMETER; @@ -196,33 +219,33 @@ NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TALLOC_CT NTSTATUS nt_status; for (i=0; mechType[i]; i++) { - op = gensec_security_by_oid(mechType[i]); - if (!op) { - continue; + nt_status = gensec_client_start(&spnego_state->sub_sec_security); + if (!NT_STATUS_IS_OK(nt_status)) { + break; } - spnego_state->sub_sec_security.ops = op; - spnego_state->sub_sec_security.user = gensec_security->user; - - nt_status = op->client_start(&spnego_state->sub_sec_security); + nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security, + mechType[i]); if (!NT_STATUS_IS_OK(nt_status)) { - op->end(&spnego_state->sub_sec_security); + gensec_end(&spnego_state->sub_sec_security); continue; } + if (i == 0) { - nt_status = op->update(&spnego_state->sub_sec_security, - out_mem_ctx, - spnego.negTokenInit.mechToken, - &unwrapped_out); + nt_status = gensec_update(spnego_state->sub_sec_security, + out_mem_ctx, + spnego.negTokenInit.mechToken, + &unwrapped_out); } else { /* only get the helping start blob for the first OID */ - nt_status = op->update(&spnego_state->sub_sec_security, - out_mem_ctx, - null_data_blob, - &unwrapped_out); + 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)) { - DEBUG(1, ("SPENGO(%s) NEG_TOKEN_INIT failed: %s\n", op->name, nt_errstr(nt_status))); - op->end(&spnego_state->sub_sec_security); + DEBUG(1, ("SPENGO(%s) NEG_TOKEN_INIT failed: %s\n", + spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status))); + gensec_end(&spnego_state->sub_sec_security); } else { break; } @@ -237,7 +260,7 @@ NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TALLOC_CT } /* compose reply */ - my_mechs[0] = op->oid; + my_mechs[0] = spnego_state->sub_sec_security->ops->oid; spnego_out.type = SPNEGO_NEG_TOKEN_INIT; spnego_out.negTokenInit.mechTypes = my_mechs; @@ -261,12 +284,11 @@ NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TALLOC_CT return NT_STATUS_ACCESS_DENIED; } - op = spnego_state->sub_sec_security.ops; if (spnego.negTokenTarg.responseToken.length) { - nt_status = op->update(&spnego_state->sub_sec_security, - out_mem_ctx, - spnego.negTokenTarg.responseToken, - &unwrapped_out); + nt_status = gensec_update(spnego_state->sub_sec_security, + out_mem_ctx, + spnego.negTokenTarg.responseToken, + &unwrapped_out); } else { unwrapped_out = data_blob(NULL, 0); nt_status = NT_STATUS_OK; @@ -284,7 +306,9 @@ NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TALLOC_CT /* compose reply */ spnego_out.type = SPNEGO_NEG_TOKEN_TARG; spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE; - spnego_out.negTokenTarg.supportedMech = op->oid; + spnego_out.negTokenTarg.supportedMech + = spnego_state->sub_sec_security->ops->oid; +; spnego_out.negTokenTarg.responseToken = unwrapped_out; spnego_out.negTokenTarg.mechListMIC = null_data_blob; @@ -296,7 +320,9 @@ NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TALLOC_CT } else if (NT_STATUS_IS_OK(nt_status)) { spnego_state->state_position = SPNEGO_DONE; } else { - DEBUG(1, ("SPENGO(%s) login failed: %s\n", op->name, nt_errstr(nt_status))); + DEBUG(1, ("SPENGO(%s) login failed: %s\n", + spnego_state->sub_sec_security->ops->name, + nt_errstr(nt_status))); return nt_status; } @@ -309,13 +335,42 @@ NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TALLOC_CT } } -void gensec_spnego_end(struct gensec_security *gensec_security) +static void gensec_spnego_end(struct gensec_security *gensec_security) { struct spnego_state *spnego_state = gensec_security->private_data; - - spnego_state->sub_sec_security.ops->end(&spnego_state->sub_sec_security); + + if (spnego_state->sub_sec_security) { + gensec_end(&spnego_state->sub_sec_security); + } talloc_destroy(spnego_state->mem_ctx); gensec_security->private_data = NULL; } + +static const struct gensec_security_ops gensec_spnego_security_ops = { + .name = "spnego", + .sasl_name = "GSS-SPNEGO", + .oid = OID_SPNEGO, + .client_start = gensec_spnego_client_start, + .update = gensec_spnego_update, + .seal_packet = gensec_spnego_seal_packet, + .sign_packet = gensec_spnego_sign_packet, + .check_packet = gensec_spnego_check_packet, + .unseal_packet = gensec_spnego_unseal_packet, + .session_key = gensec_spnego_session_key, + .end = gensec_spnego_end +}; + +NTSTATUS gensec_spengo_init(void) +{ + NTSTATUS ret; + ret = register_backend("gensec", &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 index 890301314b..60ef4c1d36 100644 --- a/source4/libcli/auth/spnego.h +++ b/source4/libcli/auth/spnego.h @@ -23,12 +23,6 @@ #ifndef SAMBA_SPNEGO_H #define SAMBA_SPNEGO_H -/* SPNEGO mode */ -enum spnego_role -{ - SPNEGO_SERVER, - SPNEGO_CLIENT -}; #define SPNEGO_DELEG_FLAG 0x01 #define SPNEGO_MUTUAL_FLAG 0x02 @@ -70,23 +64,4 @@ enum spnego_message_type { SPNEGO_NEG_TOKEN_TARG = 1, }; -enum spnego_state_position { - SPNEGO_SERVER_START, - SPNEGO_CLIENT_GET_MECHS, - SPNEGO_CLIENT_SEND_MECHS, - SPNEGO_TARG, - SPNEGO_FALLBACK, - SPNEGO_DONE -}; - -struct spnego_state { - TALLOC_CTX *mem_ctx; - uint_t ref_count; - enum spnego_role role; - enum spnego_message_type expected_packet; - enum spnego_message_type state_position; - negResult_t result; - struct gensec_security sub_sec_security; -}; - #endif diff --git a/source4/libcli/config.m4 b/source4/libcli/config.m4 index 992f84005d..9e19499003 100644 --- a/source4/libcli/config.m4 +++ b/source4/libcli/config.m4 @@ -54,7 +54,8 @@ SMB_SUBSYSTEM(LIBCLI_AUTH,[], libcli/auth/kerberos_verify.o libcli/auth/clikrb5.o libcli/auth/gensec.o - libcli/auth/gensec_ntlmssp.o]) + libcli/auth/gensec_ntlmssp.o], + [], [AUTH SCHANNELDB]) SMB_SUBSYSTEM(LIBCLI_NMB,[], [libcli/unexpected.o diff --git a/source4/librpc/ndr/ndr.c b/source4/librpc/ndr/ndr.c index 6b89cb4cd2..d2e85f6aec 100644 --- a/source4/librpc/ndr/ndr.c +++ b/source4/librpc/ndr/ndr.c @@ -741,7 +741,7 @@ NTSTATUS ndr_pull_union_blob(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, uint32_t leve /* pull a struct from a blob using NDR */ -NTSTATUS ndr_pull_struct_blob(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, void *p, +NTSTATUS ndr_pull_struct_blob(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx, void *p, NTSTATUS (*fn)(struct ndr_pull *, int , void *)) { struct ndr_pull *ndr; diff --git a/source4/librpc/rpc/dcerpc.c b/source4/librpc/rpc/dcerpc.c index 5d5469da7f..8afc556528 100644 --- a/source4/librpc/rpc/dcerpc.c +++ b/source4/librpc/rpc/dcerpc.c @@ -42,9 +42,7 @@ struct dcerpc_pipe *dcerpc_pipe_init(void) p->mem_ctx = mem_ctx; p->call_id = 1; p->security_state.auth_info = NULL; - ZERO_STRUCT(p->security_state.generic_state.user); - p->security_state.generic_state.private_data = NULL; - p->security_state.generic_state.ops = NULL; + p->security_state.generic_state = NULL; p->binding_string = NULL; p->flags = 0; p->srv_max_xmit_frag = 0; @@ -60,8 +58,8 @@ void dcerpc_pipe_close(struct dcerpc_pipe *p) if (!p) return; p->reference_count--; if (p->reference_count <= 0) { - if (p->security_state.generic_state.ops) { - p->security_state.generic_state.ops->end(&p->security_state.generic_state); + if (p->security_state.generic_state) { + gensec_end(&p->security_state.generic_state); } p->transport.shutdown_pipe(p); talloc_destroy(p->mem_ctx); @@ -132,7 +130,7 @@ static NTSTATUS dcerpc_pull_request_sign(struct dcerpc_pipe *p, DATA_BLOB auth_blob; /* non-signed packets are simpler */ - if (!p->security_state.auth_info || !p->security_state.generic_state.ops) { + if (!p->security_state.auth_info || !p->security_state.generic_state) { return dcerpc_pull(blob, mem_ctx, pkt); } @@ -186,21 +184,19 @@ static NTSTATUS dcerpc_pull_request_sign(struct dcerpc_pipe *p, /* check signature or unseal the packet */ switch (p->security_state.auth_info->auth_level) { case DCERPC_AUTH_LEVEL_PRIVACY: - status = p->security_state - .generic_state.ops->unseal(&p->security_state.generic_state, - mem_ctx, - pkt->u.response.stub_and_verifier.data, - pkt->u.response.stub_and_verifier.length, - &auth.credentials); + status = gensec_unseal_packet(p->security_state.generic_state, + mem_ctx, + pkt->u.response.stub_and_verifier.data, + pkt->u.response.stub_and_verifier.length, + &auth.credentials); break; - + case DCERPC_AUTH_LEVEL_INTEGRITY: - status = p->security_state - .generic_state.ops->check_sig(&p->security_state.generic_state, - mem_ctx, - pkt->u.response.stub_and_verifier.data, - pkt->u.response.stub_and_verifier.length, - &auth.credentials); + status = gensec_check_packet(p->security_state.generic_state, + mem_ctx, + pkt->u.response.stub_and_verifier.data, + pkt->u.response.stub_and_verifier.length, + &auth.credentials); break; case DCERPC_AUTH_LEVEL_NONE: @@ -232,7 +228,7 @@ static NTSTATUS dcerpc_push_request_sign(struct dcerpc_pipe *p, struct ndr_push *ndr; /* non-signed packets are simpler */ - if (!p->security_state.auth_info || !p->security_state.generic_state.ops) { + if (!p->security_state.auth_info || !p->security_state.generic_state) { return dcerpc_push_auth(blob, mem_ctx, pkt, p->security_state.auth_info); } @@ -257,21 +253,19 @@ static NTSTATUS dcerpc_push_request_sign(struct dcerpc_pipe *p, /* sign or seal the packet */ switch (p->security_state.auth_info->auth_level) { case DCERPC_AUTH_LEVEL_PRIVACY: - status = p->security_state - .generic_state.ops->seal(&p->security_state.generic_state, - mem_ctx, - ndr->data + DCERPC_REQUEST_LENGTH, - ndr->offset - DCERPC_REQUEST_LENGTH, - &p->security_state.auth_info->credentials); + status = gensec_seal_packet(p->security_state.generic_state, + mem_ctx, + ndr->data + DCERPC_REQUEST_LENGTH, + ndr->offset - DCERPC_REQUEST_LENGTH, + &p->security_state.auth_info->credentials); break; case DCERPC_AUTH_LEVEL_INTEGRITY: - status = p->security_state - .generic_state.ops->sign(&p->security_state.generic_state, - mem_ctx, - ndr->data + DCERPC_REQUEST_LENGTH, - ndr->offset - DCERPC_REQUEST_LENGTH, - &p->security_state.auth_info->credentials); + status = gensec_sign_packet(p->security_state.generic_state, + mem_ctx, + ndr->data + DCERPC_REQUEST_LENGTH, + ndr->offset - DCERPC_REQUEST_LENGTH, + &p->security_state.auth_info->credentials); break; case DCERPC_AUTH_LEVEL_NONE: diff --git a/source4/librpc/rpc/dcerpc.h b/source4/librpc/rpc/dcerpc.h index a513b72a16..4f50b261e2 100644 --- a/source4/librpc/rpc/dcerpc.h +++ b/source4/librpc/rpc/dcerpc.h @@ -28,7 +28,7 @@ enum dcerpc_transport_t {NCACN_NP, NCACN_IP_TCP}; struct dcerpc_pipe; struct dcerpc_security { struct dcerpc_auth *auth_info; - struct gensec_security generic_state; + struct gensec_security *generic_state; }; struct dcerpc_pipe { diff --git a/source4/librpc/rpc/dcerpc_auth.c b/source4/librpc/rpc/dcerpc_auth.c index e5fad1f082..07601e4724 100644 --- a/source4/librpc/rpc/dcerpc_auth.c +++ b/source4/librpc/rpc/dcerpc_auth.c @@ -56,15 +56,14 @@ NTSTATUS dcerpc_bind_auth(struct dcerpc_pipe *p, uint8_t auth_type, return NT_STATUS_NO_MEMORY; } - if (!p->security_state.generic_state.ops) { - - p->security_state.generic_state.ops = gensec_security_by_authtype(auth_type); - if (!p->security_state.generic_state.ops) { - status = NT_STATUS_INVALID_PARAMETER; - goto done; + if (!p->security_state.generic_state) { + status = gensec_client_start(&p->security_state.generic_state); + if (!NT_STATUS_IS_OK(status)) { + return status; } - status = p->security_state.generic_state.ops->client_start(&p->security_state.generic_state); + status = gensec_start_mech_by_authtype(p->security_state.generic_state, auth_type); + if (!NT_STATUS_IS_OK(status)) { return status; } @@ -90,10 +89,10 @@ NTSTATUS dcerpc_bind_auth(struct dcerpc_pipe *p, uint8_t auth_type, p->security_state.auth_info->auth_level = DCERPC_AUTH_LEVEL_NONE; } - status = p->security_state.generic_state.ops->update(&p->security_state.generic_state, mem_ctx, - p->security_state.auth_info->credentials, - &credentials); - + status = gensec_update(p->security_state.generic_state, mem_ctx, + p->security_state.auth_info->credentials, + &credentials); + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { goto done; } @@ -105,9 +104,9 @@ NTSTATUS dcerpc_bind_auth(struct dcerpc_pipe *p, uint8_t auth_type, goto done; } - status = p->security_state.generic_state.ops->update(&p->security_state.generic_state, mem_ctx, - p->security_state.auth_info->credentials, - &credentials); + status = gensec_update(p->security_state.generic_state, mem_ctx, + p->security_state.auth_info->credentials, + &credentials); if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { goto done; diff --git a/source4/librpc/rpc/dcerpc_ntlm.c b/source4/librpc/rpc/dcerpc_ntlm.c index 398e3f1aa3..0f02669eb1 100644 --- a/source4/librpc/rpc/dcerpc_ntlm.c +++ b/source4/librpc/rpc/dcerpc_ntlm.c @@ -33,12 +33,47 @@ NTSTATUS dcerpc_bind_auth_ntlm(struct dcerpc_pipe *p, { NTSTATUS status; - p->security_state.generic_state.user.domain = domain; - p->security_state.generic_state.user.name = username; - p->security_state.generic_state.user.password = password; + status = gensec_client_start(&p->security_state.generic_state); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start GENSEC client mode: %s\n", nt_errstr(status))); + return status; + } + status = gensec_set_domain(p->security_state.generic_state, domain); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start set GENSEC client domain to %s: %s\n", + domain, nt_errstr(status))); + return status; + } + + status = gensec_set_username(p->security_state.generic_state, username); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start set GENSEC client username to %s: %s\n", + username, nt_errstr(status))); + return status; + } + + status = gensec_set_password(p->security_state.generic_state, password); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start set GENSEC client password: %s\n", + nt_errstr(status))); + return status; + } + + status = gensec_start_mech_by_authtype(p->security_state.generic_state, DCERPC_AUTH_TYPE_NTLMSSP); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start set GENSEC client NTLMSSP mechanism: %s\n", + nt_errstr(status))); + return status; + } + status = dcerpc_bind_auth(p, DCERPC_AUTH_TYPE_NTLMSSP, uuid, version); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(2, ("Failed to bind to pipe with NTLMSSP: %s\n", nt_errstr(status))); + return status; + } + return status; } diff --git a/source4/librpc/rpc/dcerpc_schannel.c b/source4/librpc/rpc/dcerpc_schannel.c index b43dd0788a..ffe60b1bae 100644 --- a/source4/librpc/rpc/dcerpc_schannel.c +++ b/source4/librpc/rpc/dcerpc_schannel.c @@ -22,21 +22,24 @@ #include "includes.h" -#define DCERPC_SCHANNEL_STATE_START 0 -#define DCERPC_SCHANNEL_STATE_UPDATE_1 1 +enum schannel_position { + DCERPC_SCHANNEL_STATE_START = 0, + DCERPC_SCHANNEL_STATE_UPDATE_1 +}; struct dcerpc_schannel_state { TALLOC_CTX *mem_ctx; - uint8_t state; - struct schannel_bind bind_schannel; + enum schannel_position state; struct schannel_state *schannel_state; + struct creds_CredentialState creds; + char *account_name; }; static NTSTATUS dcerpc_schannel_key(struct dcerpc_pipe *p, - const char *domain, - const char *username, - const char *password, - int chan_type, + const char *domain, + const char *username, + const char *password, + int chan_type, uint8_t new_session_key[16]); /* @@ -45,39 +48,39 @@ static NTSTATUS dcerpc_schannel_key(struct dcerpc_pipe *p, These will become static again, when we get dynamic registration, and decrpc_schannel_security_ops come back here. */ -static NTSTATUS dcerpc_schannel_unseal(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - uint8_t *data, size_t length, DATA_BLOB *sig) +static NTSTATUS dcerpc_schannel_unseal_packet(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + uint8_t *data, size_t length, DATA_BLOB *sig) { struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data; - + return schannel_unseal_packet(dce_schan_state->schannel_state, mem_ctx, data, length, sig); } -static NTSTATUS dcerpc_schannel_check_sig(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - const uint8_t *data, size_t length, - const DATA_BLOB *sig) +static NTSTATUS dcerpc_schannel_check_packet(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const uint8_t *data, size_t length, + const DATA_BLOB *sig) { struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data; return schannel_check_packet(dce_schan_state->schannel_state, data, length, sig); } -static NTSTATUS dcerpc_schannel_seal(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - uint8_t *data, size_t length, - DATA_BLOB *sig) +static NTSTATUS dcerpc_schannel_seal_packet(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + uint8_t *data, size_t length, + DATA_BLOB *sig) { struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data; return schannel_seal_packet(dce_schan_state->schannel_state, mem_ctx, data, length, sig); } -static NTSTATUS dcerpc_schannel_sign(struct gensec_security *gensec_security, - TALLOC_CTX *mem_ctx, - const uint8_t *data, size_t length, - DATA_BLOB *sig) +static NTSTATUS dcerpc_schannel_sign_packet(struct gensec_security *gensec_security, + TALLOC_CTX *mem_ctx, + const uint8_t *data, size_t length, + DATA_BLOB *sig) { struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data; @@ -85,47 +88,233 @@ static NTSTATUS dcerpc_schannel_sign(struct gensec_security *gensec_security, } static NTSTATUS dcerpc_schannel_session_key(struct gensec_security *gensec_security, - DATA_BLOB *session_key) + DATA_BLOB *session_key) { return NT_STATUS_NOT_IMPLEMENTED; } static NTSTATUS dcerpc_schannel_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, - const DATA_BLOB in, DATA_BLOB *out) + const DATA_BLOB in, DATA_BLOB *out) { struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data; NTSTATUS status; struct schannel_bind bind_schannel; + struct schannel_bind_ack bind_schannel_ack; + const char *account_name; + + switch (gensec_security->gensec_role) { + case GENSEC_CLIENT: + if (dce_schan_state->state != DCERPC_SCHANNEL_STATE_START) { + /* we could parse the bind ack, but we don't know what it is yet */ + return NT_STATUS_OK; + } + + 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 = gensec_security->user.domain; + bind_schannel.u.info23.account_name = gensec_security->user.name; + 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, gensec_security->user.name); +#else + bind_schannel.bind_type = 3; + bind_schannel.u.info3.domain = gensec_security->user.domain; + bind_schannel.u.info3.account_name = gensec_security->user.name; +#endif + + status = ndr_push_struct_blob(out, 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; + } + + dce_schan_state->state = DCERPC_SCHANNEL_STATE_UPDATE_1; + + return NT_STATUS_MORE_PROCESSING_REQUIRED; + case GENSEC_SERVER: + + if (dce_schan_state->state != DCERPC_SCHANNEL_STATE_START) { + /* no third leg on this protocol */ + return NT_STATUS_OK; + } + + /* 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) { + account_name = bind_schannel.u.info23.account_name; + } else { + account_name = bind_schannel.u.info3.account_name; + } + + /* pull the session key for this client */ + status = schannel_fetch_session_key(out_mem_ctx, account_name, &dce_schan_state->creds); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("Could not find session key for attempted schannel connection on %s: %s\n", + account_name, nt_errstr(status))); + return status; + } + + dce_schan_state->account_name = talloc_strdup(dce_schan_state->mem_ctx, account_name); + + /* start up the schannel server code */ + status = schannel_start(&dce_schan_state->schannel_state, + dce_schan_state->creds.session_key, False); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("Could not initialise schannel state for account %s: %s\n", + account_name, nt_errstr(status))); + return status; + } + + 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 account %s: %s\n", + account_name, nt_errstr(status))); + return status; + } + + dce_schan_state->state = DCERPC_SCHANNEL_STATE_UPDATE_1; + + return NT_STATUS_MORE_PROCESSING_REQUIRED; + } + return NT_STATUS_INVALID_PARAMETER; +} + +/** + * 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 dcerpc_schannel_session_info(struct gensec_security *gensec_security, + struct auth_session_info **session_info) +{ + struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data; + TALLOC_CTX *mem_ctx; + mem_ctx = talloc_init("dcerpc_schannel_start"); + if (!mem_ctx) { + return NT_STATUS_NO_MEMORY; + } - if (dce_schan_state->state != DCERPC_SCHANNEL_STATE_START) { - return NT_STATUS_OK; + (*session_info) = talloc_p(mem_ctx, struct auth_session_info); + if (*session_info == NULL) { + talloc_destroy(mem_ctx); + return NT_STATUS_NO_MEMORY; } - dce_schan_state->state = DCERPC_SCHANNEL_STATE_UPDATE_1; + ZERO_STRUCTP(*session_info); + (*session_info)->mem_ctx = mem_ctx; + (*session_info)->refcount = 1; + + (*session_info)->workstation = talloc_strdup(mem_ctx, dce_schan_state->account_name); + if ((*session_info)->workstation == NULL) { + talloc_destroy(mem_ctx); + return NT_STATUS_NO_MEMORY; + } + return NT_STATUS_OK; +} + + +/** + * 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 dcerpc_schannel_state *dce_schan_state = gensec_security->private_data; - 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 = gensec_security->user.domain; - bind_schannel.u.info23.account_name = gensec_security->user.name; - bind_schannel.u.info23.dnsdomain = str_format_nbt_domain(dce_schan_state->mem_ctx, fulldomainname); - bind_schannel.u.info23.workstation = str_format_nbt_domain(dce_schan_state->mem_ctx, gensec_security->user.name); -#else - bind_schannel.bind_type = 3; - bind_schannel.u.info3.domain = gensec_security->user.domain; - bind_schannel.u.info3.account_name = gensec_security->user.name; -#endif + *creds = talloc_p(mem_ctx, struct creds_CredentialState); + if (*creds) { + return NT_STATUS_NO_MEMORY; + } + + **creds = dce_schan_state->creds; + return NT_STATUS_OK; +} + + +static NTSTATUS dcerpc_schannel_start(struct gensec_security *gensec_security) +{ + struct dcerpc_schannel_state *dce_schan_state; + TALLOC_CTX *mem_ctx; + mem_ctx = talloc_init("dcerpc_schannel_start"); + if (!mem_ctx) { + return NT_STATUS_NO_MEMORY; + } + + dce_schan_state = talloc_p(mem_ctx, struct dcerpc_schannel_state); + if (!dce_schan_state) { + talloc_destroy(mem_ctx); + return NT_STATUS_NO_MEMORY; + } + + dce_schan_state->mem_ctx = mem_ctx; + dce_schan_state->state = DCERPC_SCHANNEL_STATE_START; + + + gensec_security->private_data = dce_schan_state; + + return NT_STATUS_OK; +} + +static NTSTATUS dcerpc_schannel_server_start(struct gensec_security *gensec_security) +{ + NTSTATUS status; + + status = dcerpc_schannel_start(gensec_security); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + return NT_STATUS_OK; +} + +static NTSTATUS dcerpc_schannel_client_start(struct gensec_security *gensec_security) +{ + NTSTATUS status; + struct dcerpc_schannel_state *dce_schan_state; + + status = dcerpc_schannel_start(gensec_security); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + dce_schan_state = gensec_security->private_data; - status = ndr_push_struct_blob(out, dce_schan_state->mem_ctx, &bind_schannel, - (ndr_push_flags_fn_t)ndr_push_schannel_bind); + status = schannel_start(&dce_schan_state->schannel_state, + gensec_security->user.schan_session_key, + True); if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start schannel client\n")); return status; } - return NT_STATUS_MORE_PROCESSING_REQUIRED; + dump_data_pw("session key:\n", dce_schan_state->schannel_state->session_key, 16); + return NT_STATUS_OK; } +/* + end crypto state +*/ static void dcerpc_schannel_end(struct gensec_security *gensec_security) { struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data; @@ -138,18 +327,6 @@ static void dcerpc_schannel_end(struct gensec_security *gensec_security) } -static const struct gensec_security_ops gensec_dcerpc_schannel_security_ops = { - .name = "dcerpc_schannel", - .auth_type = DCERPC_AUTH_TYPE_SCHANNEL, - .update = dcerpc_schannel_update, - .seal = dcerpc_schannel_seal, - .sign = dcerpc_schannel_sign, - .check_sig = dcerpc_schannel_check_sig, - .unseal = dcerpc_schannel_unseal, - .session_key = dcerpc_schannel_session_key, - .end = dcerpc_schannel_end -}; - /* get a schannel key using a netlogon challenge on a secondary pipe */ @@ -251,11 +428,13 @@ NTSTATUS dcerpc_bind_auth_schannel(struct dcerpc_pipe *p, const char *password) { NTSTATUS status; - struct dcerpc_schannel_state *dce_schan_state; - TALLOC_CTX *mem_ctx; - uint8_t session_key[16]; int chan_type = 0; + status = gensec_client_start(&p->security_state.generic_state); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (p->flags & DCERPC_SCHANNEL_BDC) { chan_type = SEC_CHAN_BDC; } else if (p->flags & DCERPC_SCHANNEL_WORKSTATION) { @@ -265,44 +444,75 @@ NTSTATUS dcerpc_bind_auth_schannel(struct dcerpc_pipe *p, } status = dcerpc_schannel_key(p, domain, - username, - password, - chan_type, session_key); + username, + password, + chan_type, + p->security_state.generic_state->user.schan_session_key); if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to fetch schannel session key: %s\n", nt_errstr(status))); + gensec_end(&p->security_state.generic_state); return status; } - - mem_ctx = talloc_init("dcerpc_schannel_start"); - if (!mem_ctx) { - return NT_STATUS_NO_MEMORY; + + status = gensec_set_username(p->security_state.generic_state, username); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to set schannel username to %s: %s\n", username, nt_errstr(status))); + gensec_end(&p->security_state.generic_state); + return status; + } + + status = gensec_set_domain(p->security_state.generic_state, domain); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to set schannel domain to %s: %s\n", domain, nt_errstr(status))); + gensec_end(&p->security_state.generic_state); + return status; } + + status = gensec_start_mech_by_authtype(p->security_state.generic_state, DCERPC_AUTH_TYPE_SCHANNEL); - dce_schan_state = talloc_p(mem_ctx, struct dcerpc_schannel_state); - if (!dce_schan_state) { - talloc_destroy(mem_ctx); - return NT_STATUS_NO_MEMORY; + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start SCHANNEL GENSEC backend: %s\n", nt_errstr(status))); + gensec_end(&p->security_state.generic_state); + return status; } - dce_schan_state->mem_ctx = mem_ctx; + status = dcerpc_bind_auth(p, DCERPC_AUTH_TYPE_SCHANNEL, + uuid, version); - status = schannel_start(&dce_schan_state->schannel_state, session_key, True); if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to bind to pipe with SCHANNEL: %s\n", nt_errstr(status))); + gensec_end(&p->security_state.generic_state); return status; } - dce_schan_state->state = DCERPC_SCHANNEL_STATE_START; - - p->security_state.generic_state.user.domain = domain; - p->security_state.generic_state.user.name = username; - p->security_state.generic_state.user.password = password; + return NT_STATUS_OK; +} - p->security_state.generic_state.ops = &gensec_dcerpc_schannel_security_ops; - p->security_state.generic_state.private_data = dce_schan_state; - dump_data_pw("session key:\n", dce_schan_state->schannel_state->session_key, 16); +static const struct gensec_security_ops gensec_dcerpc_schannel_security_ops = { + .name = "dcerpc_schannel", + .auth_type = DCERPC_AUTH_TYPE_SCHANNEL, + .client_start = dcerpc_schannel_client_start, + .server_start = dcerpc_schannel_server_start, + .update = dcerpc_schannel_update, + .seal_packet = dcerpc_schannel_seal_packet, + .sign_packet = dcerpc_schannel_sign_packet, + .check_packet = dcerpc_schannel_check_packet, + .unseal_packet = dcerpc_schannel_unseal_packet, + .session_key = dcerpc_schannel_session_key, + .session_info = dcerpc_schannel_session_info, + .end = dcerpc_schannel_end +}; - status = dcerpc_bind_auth(p, DCERPC_AUTH_TYPE_SCHANNEL, - uuid, version); +NTSTATUS gensec_dcerpc_schannel_init(void) +{ + NTSTATUS ret; + ret = register_backend("gensec", &gensec_dcerpc_schannel_security_ops); + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(0,("Failed to register '%s' gensec backend!\n", + gensec_dcerpc_schannel_security_ops.name)); + return ret; + } - return status; + return ret; } diff --git a/source4/librpc/rpc/dcerpc_util.c b/source4/librpc/rpc/dcerpc_util.c index 82aa5aa1f3..d5cd2ab6fe 100644 --- a/source4/librpc/rpc/dcerpc_util.c +++ b/source4/librpc/rpc/dcerpc_util.c @@ -503,6 +503,7 @@ static NTSTATUS dcerpc_pipe_connect_ncacn_np(struct dcerpc_pipe **p, if (!NT_STATUS_IS_OK(status)) { DEBUG(0,("Failed to bind to uuid %s - %s\n", pipe_uuid, nt_errstr(status))); dcerpc_pipe_close(*p); + *p = NULL; return status; } @@ -569,6 +570,7 @@ static NTSTATUS dcerpc_pipe_connect_ncacn_ip_tcp(struct dcerpc_pipe **p, DEBUG(0,("Failed to bind to uuid %s - %s\n", pipe_uuid, nt_errstr(status))); dcerpc_pipe_close(*p); + *p = NULL; return status; } @@ -697,8 +699,8 @@ NTSTATUS dcerpc_fetch_session_key(struct dcerpc_pipe *p, { struct cli_tree *tree; - if (p->security_state.generic_state.ops) { - return p->security_state.generic_state.ops->session_key(&p->security_state.generic_state, session_key); + if (p->security_state.generic_state) { + return gensec_session_key(p->security_state.generic_state, session_key); } tree = dcerpc_smb_tree(p); diff --git a/source4/ntvfs/ipc/vfs_ipc.c b/source4/ntvfs/ipc/vfs_ipc.c index cf8b49ebb8..f6a0015f1d 100644 --- a/source4/ntvfs/ipc/vfs_ipc.c +++ b/source4/ntvfs/ipc/vfs_ipc.c @@ -243,9 +243,9 @@ static NTSTATUS ipc_open_generic(struct smbsrv_request *req, const char *fname, /* tell the RPC layer the session_info */ if (req->user_ctx->vuser) { - /* - * TODO: we need to reference count the entire session_info - */ + /* The session info is refcount-increased in the + dcesrv_endpoint_search_connect() function */ + session_info = req->user_ctx->vuser->session_info; } diff --git a/source4/rpc_server/config.m4 b/source4/rpc_server/config.m4 index 3f64cdcf4b..73cae66456 100644 --- a/source4/rpc_server/config.m4 +++ b/source4/rpc_server/config.m4 @@ -3,6 +3,7 @@ dnl # DCERPC Server subsystem SMB_SUBSYSTEM_MK(DCERPC_COMMON,rpc_server/config.mk) SMB_SUBSYSTEM_MK(SAMDB,rpc_server/config.mk) +SMB_SUBSYSTEM_MK(SCHANNELDB,rpc_server/config.mk) SMB_MODULE_MK(dcerpc_rpcecho,DCERPC,STATIC,rpc_server/config.mk) SMB_MODULE_MK(dcerpc_epmapper,DCERPC,STATIC,rpc_server/config.mk) diff --git a/source4/rpc_server/config.mk b/source4/rpc_server/config.mk index 9d9f12d9aa..dbe8b8344e 100644 --- a/source4/rpc_server/config.mk +++ b/source4/rpc_server/config.mk @@ -19,12 +19,22 @@ INIT_OBJ_FILES = \ ADD_OBJ_FILES = \ rpc_server/samr/samr_utils.o REQUIRED_SUBSYSTEMS = \ + DCERPC_COMMON \ LIBLDB # # End SUBSYSTEM SAMDB ################################################ ################################################ +# Start SUBSYSTEM SCHANNELDB +[SUBSYSTEM::SCHANNELDB] +INIT_OBJ_FILES = \ + rpc_server/netlogon/schannel_state.o +# +# End SUBSYSTEM SCHANNELDB +################################################ + +################################################ # Start MODULE dcerpc_rpcecho [MODULE::dcerpc_rpcecho] INIT_OBJ_FILES = \ @@ -98,10 +108,9 @@ REQUIRED_SUBSYSTEMS = \ [MODULE::dcerpc_netlogon] INIT_OBJ_FILES = \ rpc_server/netlogon/dcerpc_netlogon.o -ADD_OBJ_FILES = \ - rpc_server/netlogon/schannel_state.o REQUIRED_SUBSYSTEMS = \ - DCERPC_COMMON + DCERPC_COMMON \ + SCHANNELDB # End MODULE dcerpc_netlogon ################################################ @@ -135,10 +144,9 @@ INIT_OBJ_FILES = \ ADD_OBJ_FILES = \ rpc_server/dcerpc_tcp.o \ rpc_server/dcesrv_auth.o \ - rpc_server/dcesrv_crypto.o \ - rpc_server/dcesrv_crypto_ntlmssp.o \ - rpc_server/dcesrv_crypto_schannel.o \ rpc_server/handles.o +REQUIRED_SUBSYSTEMS = \ + LIBCLI_AUTH # # End SUBSYSTEM DCERPC ################################################ diff --git a/source4/rpc_server/dcerpc_server.c b/source4/rpc_server/dcerpc_server.c index 90ebaf285c..e5c4c120a5 100644 --- a/source4/rpc_server/dcerpc_server.c +++ b/source4/rpc_server/dcerpc_server.c @@ -146,9 +146,9 @@ static struct dcesrv_call_state *dcesrv_find_call(struct dcesrv_connection *dce_ register an interface on an endpoint */ NTSTATUS dcesrv_interface_register(struct dcesrv_context *dce_ctx, - const char *ep_name, - const struct dcesrv_interface *iface, - const struct security_descriptor *sd) + const char *ep_name, + const struct dcesrv_interface *iface, + const struct security_descriptor *sd) { struct dcesrv_ep_description ep_description; struct dcesrv_endpoint *ep; @@ -269,8 +269,7 @@ NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx, (*p)->handles = NULL; (*p)->partial_input = data_blob(NULL, 0); (*p)->auth_state.auth_info = NULL; - (*p)->auth_state.crypto_ctx.private_data = NULL; - (*p)->auth_state.crypto_ctx.ops = NULL; + (*p)->auth_state.gensec_security = NULL; (*p)->auth_state.session_info = NULL; return NT_STATUS_OK; @@ -298,6 +297,7 @@ NTSTATUS dcesrv_endpoint_search_connect(struct dcesrv_context *dce_ctx, return status; } + session_info->refcount++; (*dce_conn_p)->auth_state.session_info = session_info; /* TODO: check security descriptor of the endpoint here @@ -323,8 +323,12 @@ void dcesrv_endpoint_disconnect(struct dcesrv_connection *p) dcesrv_handle_destroy(p, p->handles); } - if (p->auth_state.crypto_ctx.ops) { - p->auth_state.crypto_ctx.ops->end(&p->auth_state); + if (p->auth_state.gensec_security) { + gensec_end(&p->auth_state.gensec_security); + } + + if (p->auth_state.session_info) { + free_session_info(&p->auth_state.session_info); } talloc_destroy(p->mem_ctx); @@ -1027,7 +1031,7 @@ static int num_ep_servers; The 'type' is used to specify whether this is for a disk, printer or IPC$ share */ -static NTSTATUS dcerpc_register_ep_server(void *_ep_server) +static NTSTATUS dcerpc_register_ep_server(const void *_ep_server) { const struct dcesrv_endpoint_server *ep_server = _ep_server; diff --git a/source4/rpc_server/dcerpc_server.h b/source4/rpc_server/dcerpc_server.h index 3ad768b32e..b1754dd4a3 100644 --- a/source4/rpc_server/dcerpc_server.h +++ b/source4/rpc_server/dcerpc_server.h @@ -94,31 +94,10 @@ struct dcesrv_handle { void (*destroy)(struct dcesrv_connection *, struct dcesrv_handle *); }; -struct dcesrv_crypto_ops { - const char *name; - uint8 auth_type; - NTSTATUS (*start)(struct dcesrv_auth *auth, DATA_BLOB *auth_blob); - NTSTATUS (*update)(struct dcesrv_auth *auth, TALLOC_CTX *out_mem_ctx, - const DATA_BLOB in, DATA_BLOB *out); - NTSTATUS (*seal)(struct dcesrv_auth *auth, TALLOC_CTX *sig_mem_ctx, - uint8_t *data, size_t length, DATA_BLOB *sig); - NTSTATUS (*sign)(struct dcesrv_auth *auth, TALLOC_CTX *sig_mem_ctx, - const uint8_t *data, size_t length, DATA_BLOB *sig); - NTSTATUS (*check_sig)(struct dcesrv_auth *auth, TALLOC_CTX *sig_mem_ctx, - const uint8_t *data, size_t length, const DATA_BLOB *sig); - NTSTATUS (*unseal)(struct dcesrv_auth *auth, TALLOC_CTX *sig_mem_ctx, - uint8_t *data, size_t length, DATA_BLOB *sig); - NTSTATUS (*session_key)(struct dcesrv_auth *auth, uint8_t session_key[16]); - void (*end)(struct dcesrv_auth *auth); -}; - /* hold the authentication state information */ struct dcesrv_auth { struct dcerpc_auth *auth_info; - struct { - void *private_data; - const struct dcesrv_crypto_ops *ops; - } crypto_ctx; + struct gensec_security *gensec_security; struct auth_session_info *session_info; }; diff --git a/source4/rpc_server/dcesrv_auth.c b/source4/rpc_server/dcesrv_auth.c index 26053b47b9..84a5460d68 100644 --- a/source4/rpc_server/dcesrv_auth.c +++ b/source4/rpc_server/dcesrv_auth.c @@ -4,6 +4,7 @@ server side dcerpc authentication code Copyright (C) Andrew Tridgell 2003 + Copyright (C) Stefan (metze) Metzmacher 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 @@ -23,6 +24,48 @@ #include "includes.h" /* + startup the cryptographic side of an authenticated dcerpc server +*/ +NTSTATUS dcesrv_crypto_select_type(struct dcesrv_connection *dce_conn, + struct dcesrv_auth *auth) +{ + NTSTATUS status; + if (auth->auth_info->auth_level != DCERPC_AUTH_LEVEL_INTEGRITY && + auth->auth_info->auth_level != DCERPC_AUTH_LEVEL_PRIVACY) { + DEBUG(2,("auth_level %d not supported in dcesrv auth\n", + auth->auth_info->auth_level)); + return NT_STATUS_INVALID_PARAMETER; + } + + if (auth->gensec_security != NULL) { + /* TODO: + * this this function should not be called + * twice per dcesrv_connection! + * + * so we need to find out the right + * dcerpc error to return + */ + } + + status = gensec_server_start(&auth->gensec_security); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start GENSEC server code: %s\n", nt_errstr(status))); + return status; + } + + status = gensec_start_mech_by_authtype(auth->gensec_security, auth->auth_info->auth_type); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start GENSEC mech-specific server code (%d): %s\n", + (int)auth->auth_info->auth_type, + nt_errstr(status))); + return status; + } + + return status; +} + +/* parse any auth information from a dcerpc bind request return False if we can't handle the auth request for some reason (in which case we send a bind_nak) @@ -56,40 +99,43 @@ BOOL dcesrv_auth_bind(struct dcesrv_call_state *call) return False; } - status = dcesrv_crypto_start(&dce_conn->auth_state, &dce_conn->auth_state.auth_info->credentials); - if (!NT_STATUS_IS_OK(status)) { - return False; - } - return True; } /* - add any auth information needed in a bind ack + add any auth information needed in a bind ack, and process the authentication + information found in the bind. */ BOOL dcesrv_auth_bind_ack(struct dcesrv_call_state *call, struct dcerpc_packet *pkt) { struct dcesrv_connection *dce_conn = call->conn; NTSTATUS status; - if (!call->conn->auth_state.crypto_ctx.ops) { + if (!call->conn->auth_state.gensec_security) { return True; } - status = dcesrv_crypto_update(&dce_conn->auth_state, - call->mem_ctx, - dce_conn->auth_state.auth_info->credentials, - &dce_conn->auth_state.auth_info->credentials); - if (!NT_STATUS_IS_OK(status) && - !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + status = gensec_update(dce_conn->auth_state.gensec_security, + call->mem_ctx, + dce_conn->auth_state.auth_info->credentials, + &dce_conn->auth_state.auth_info->credentials); + + if (NT_STATUS_IS_OK(status)) { + status = gensec_session_info(dce_conn->auth_state.gensec_security, + &dce_conn->auth_state.session_info); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to establish session_info: %s\n", nt_errstr(status))); + return False; + } + return True; + } else if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + dce_conn->auth_state.auth_info->auth_pad_length = 0; + dce_conn->auth_state.auth_info->auth_reserved = 0; + return True; + } else { DEBUG(2, ("Failed to start dcesrv auth negotiate: %s\n", nt_errstr(status))); return False; } - - dce_conn->auth_state.auth_info->auth_pad_length = 0; - dce_conn->auth_state.auth_info->auth_reserved = 0; - - return True; } @@ -103,7 +149,7 @@ BOOL dcesrv_auth_auth3(struct dcesrv_call_state *call) NTSTATUS status; if (!dce_conn->auth_state.auth_info || - !dce_conn->auth_state.crypto_ctx.ops || + !dce_conn->auth_state.gensec_security || pkt->u.auth.auth_info.length == 0) { return False; } @@ -116,11 +162,19 @@ BOOL dcesrv_auth_auth3(struct dcesrv_call_state *call) return False; } - status = dcesrv_crypto_update(&dce_conn->auth_state, - call->mem_ctx, - dce_conn->auth_state.auth_info->credentials, - &dce_conn->auth_state.auth_info->credentials); - if (!NT_STATUS_IS_OK(status)) { + status = gensec_update(dce_conn->auth_state.gensec_security, + call->mem_ctx, + dce_conn->auth_state.auth_info->credentials, + &dce_conn->auth_state.auth_info->credentials); + if (NT_STATUS_IS_OK(status)) { + status = gensec_session_info(dce_conn->auth_state.gensec_security, + &dce_conn->auth_state.session_info); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to establish session_info: %s\n", nt_errstr(status))); + return False; + } + return True; + } else { DEBUG(4, ("dcesrv_auth_auth3: failed to authenticate: %s\n", nt_errstr(status))); return False; @@ -143,7 +197,7 @@ BOOL dcesrv_auth_request(struct dcesrv_call_state *call) NTSTATUS status; if (!dce_conn->auth_state.auth_info || - !dce_conn->auth_state.crypto_ctx.ops) { + !dce_conn->auth_state.gensec_security) { return True; } @@ -177,7 +231,7 @@ BOOL dcesrv_auth_request(struct dcesrv_call_state *call) /* check signature or unseal the packet */ switch (dce_conn->auth_state.auth_info->auth_level) { case DCERPC_AUTH_LEVEL_PRIVACY: - status = dcesrv_crypto_unseal(&dce_conn->auth_state, + status = gensec_unseal_packet(dce_conn->auth_state.gensec_security, call->mem_ctx, pkt->u.request.stub_and_verifier.data, pkt->u.request.stub_and_verifier.length, @@ -185,11 +239,11 @@ BOOL dcesrv_auth_request(struct dcesrv_call_state *call) break; case DCERPC_AUTH_LEVEL_INTEGRITY: - status = dcesrv_crypto_check_sig(&dce_conn->auth_state, - call->mem_ctx, - pkt->u.request.stub_and_verifier.data, - pkt->u.request.stub_and_verifier.length, - &auth.credentials); + status = gensec_check_packet(dce_conn->auth_state.gensec_security, + call->mem_ctx, + pkt->u.request.stub_and_verifier.data, + pkt->u.request.stub_and_verifier.length, + &auth.credentials); break; default: @@ -218,7 +272,7 @@ BOOL dcesrv_auth_response(struct dcesrv_call_state *call, struct ndr_push *ndr; /* non-signed packets are simple */ - if (!dce_conn->auth_state.auth_info || !dce_conn->auth_state.crypto_ctx.ops) { + if (!dce_conn->auth_state.auth_info || !dce_conn->auth_state.gensec_security) { status = dcerpc_push_auth(blob, call->mem_ctx, pkt, NULL); return NT_STATUS_IS_OK(status); } @@ -244,15 +298,15 @@ BOOL dcesrv_auth_response(struct dcesrv_call_state *call, /* sign or seal the packet */ switch (dce_conn->auth_state.auth_info->auth_level) { case DCERPC_AUTH_LEVEL_PRIVACY: - status = dcesrv_crypto_seal(&dce_conn->auth_state, - call->mem_ctx, - ndr->data + DCERPC_REQUEST_LENGTH, - ndr->offset - DCERPC_REQUEST_LENGTH, - &dce_conn->auth_state.auth_info->credentials); + status = gensec_seal_packet(dce_conn->auth_state.gensec_security, + call->mem_ctx, + ndr->data + DCERPC_REQUEST_LENGTH, + ndr->offset - DCERPC_REQUEST_LENGTH, + &dce_conn->auth_state.auth_info->credentials); break; case DCERPC_AUTH_LEVEL_INTEGRITY: - status = dcesrv_crypto_sign(&dce_conn->auth_state, + status = gensec_sign_packet(dce_conn->auth_state.gensec_security, call->mem_ctx, ndr->data + DCERPC_REQUEST_LENGTH, ndr->offset - DCERPC_REQUEST_LENGTH, diff --git a/source4/rpc_server/dcesrv_crypto.c b/source4/rpc_server/dcesrv_crypto.c deleted file mode 100644 index 7765815f3b..0000000000 --- a/source4/rpc_server/dcesrv_crypto.c +++ /dev/null @@ -1,148 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - server side dcerpc authentication code - crypto support - - Copyright (C) Andrew Tridgell 2004 - Copyright (C) Stefan (metze) Metzmacher 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. -*/ - -/* - this provides a crypto interface to the various backends (such as - NTLMSSP and SCHANNEL) for the rpc server code -*/ - -#include "includes.h" - -/* - startup the cryptographic side of an authenticated dcerpc server -*/ -NTSTATUS dcesrv_crypto_select_type(struct dcesrv_connection *dce_conn, - struct dcesrv_auth *auth) -{ - if (auth->auth_info->auth_level != DCERPC_AUTH_LEVEL_INTEGRITY && - auth->auth_info->auth_level != DCERPC_AUTH_LEVEL_PRIVACY) { - DEBUG(2,("auth_level %d not supported in dcesrv auth\n", - auth->auth_info->auth_level)); - return NT_STATUS_INVALID_PARAMETER; - } - - if (auth->crypto_ctx.ops != NULL) { - /* TODO: - * this this function should not be called - * twice per dcesrv_connection! - * - * so we need to find out the right - * dcerpc error to return - */ - } - - /* - * TODO: - * maybe a dcesrv_crypto_find_backend_by_type() whould be better here - * to make thinks more generic - */ - auth->crypto_ctx.ops = dcesrv_crypto_backend_bytype(auth->auth_info->auth_type); - if (auth->crypto_ctx.ops == NULL) { - DEBUG(2,("dcesrv auth_type %d not supported\n", auth->auth_info->auth_type)); - return NT_STATUS_INVALID_PARAMETER; - } - - return NT_STATUS_OK; -} - -/* - start crypto state -*/ -NTSTATUS dcesrv_crypto_start(struct dcesrv_auth *auth, DATA_BLOB *auth_blob) -{ - return auth->crypto_ctx.ops->start(auth, auth_blob); -} - -/* - update crypto state -*/ -NTSTATUS dcesrv_crypto_update(struct dcesrv_auth *auth, - TALLOC_CTX *out_mem_ctx, - const DATA_BLOB in, DATA_BLOB *out) -{ - return auth->crypto_ctx.ops->update(auth, out_mem_ctx, in, out); -} - -/* - seal a packet -*/ -NTSTATUS dcesrv_crypto_seal(struct dcesrv_auth *auth, TALLOC_CTX *sig_mem_ctx, - uint8_t *data, size_t length, DATA_BLOB *sig) -{ - return auth->crypto_ctx.ops->seal(auth, sig_mem_ctx, data, length, sig); -} - -/* - sign a packet -*/ -NTSTATUS dcesrv_crypto_sign(struct dcesrv_auth *auth, TALLOC_CTX *sig_mem_ctx, - const uint8_t *data, size_t length, DATA_BLOB *sig) -{ - return auth->crypto_ctx.ops->sign(auth, sig_mem_ctx, data, length, sig); -} - -/* - check a packet signature -*/ -NTSTATUS dcesrv_crypto_check_sig(struct dcesrv_auth *auth, TALLOC_CTX *sig_mem_ctx, - const uint8_t *data, size_t length, const DATA_BLOB *sig) -{ - return auth->crypto_ctx.ops->check_sig(auth, sig_mem_ctx, data, length, sig); -} - -/* - unseal a packet -*/ -NTSTATUS dcesrv_crypto_unseal(struct dcesrv_auth *auth, TALLOC_CTX *sig_mem_ctx, - uint8_t *data, size_t length, DATA_BLOB *sig) -{ - return auth->crypto_ctx.ops->unseal(auth, sig_mem_ctx, data, length, sig); -} - -/* - get the negotiated session key -*/ -NTSTATUS dcesrv_crypto_session_key(struct dcesrv_auth *auth, uint8_t session_key[16]) -{ - return auth->crypto_ctx.ops->session_key(auth, session_key); -} - -/* - end crypto state -*/ -void dcesrv_crypto_end(struct dcesrv_auth *auth) -{ - auth->crypto_ctx.ops->end(auth); -} - -const struct dcesrv_crypto_ops *dcesrv_crypto_backend_bytype(uint8_t auth_type) -{ - switch (auth_type) { - case DCERPC_AUTH_TYPE_SCHANNEL: - return dcesrv_crypto_schannel_get_ops(); - case DCERPC_AUTH_TYPE_NTLMSSP: - return dcesrv_crypto_ntlmssp_get_ops(); - } - - return NULL; -} diff --git a/source4/rpc_server/dcesrv_crypto_ntlmssp.c b/source4/rpc_server/dcesrv_crypto_ntlmssp.c deleted file mode 100644 index 35029a0fee..0000000000 --- a/source4/rpc_server/dcesrv_crypto_ntlmssp.c +++ /dev/null @@ -1,159 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - server side dcerpc authentication code - NTLMSSP auth/crypto code - - Copyright (C) Andrew Tridgell 2004 - Copyright (C) Stefan (metze) Metzmacher 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. -*/ - -/* - this provides the NTLMSSP backend for server side rpc -*/ - -#include "includes.h" - - -/* - start crypto state -*/ -static NTSTATUS dcesrv_crypto_ntlmssp_start(struct dcesrv_auth *auth, DATA_BLOB *auth_blob) -{ - struct auth_ntlmssp_state *ntlmssp = NULL; - NTSTATUS status; - - /* the auth_blob is ignored here, and is handled in the call - to auth_ntlmssp_update() */ - - status = auth_ntlmssp_start(&ntlmssp); - - auth->crypto_ctx.private_data = ntlmssp; - - return status; -} - -/* - update crypto state -*/ -static NTSTATUS dcesrv_crypto_ntlmssp_update(struct dcesrv_auth *auth, TALLOC_CTX *out_mem_ctx, - const DATA_BLOB in, DATA_BLOB *out) -{ - struct auth_ntlmssp_state *auth_ntlmssp_state = auth->crypto_ctx.private_data; - NTSTATUS status; - - status = auth_ntlmssp_update(auth_ntlmssp_state, out_mem_ctx, in, out); - if (NT_STATUS_IS_OK(status)) { - /* TODO: what is when the session_info is already set */ - return auth_ntlmssp_get_session_info(auth_ntlmssp_state, &auth->session_info); - } - - return status; -} - -/* - seal a packet -*/ -static NTSTATUS dcesrv_crypto_ntlmssp_seal(struct dcesrv_auth *auth, TALLOC_CTX *sig_mem_ctx, - uint8_t *data, size_t length, DATA_BLOB *sig) -{ - struct auth_ntlmssp_state *auth_ntlmssp_state = auth->crypto_ctx.private_data; - - return ntlmssp_seal_packet(auth_ntlmssp_state->ntlmssp_state, sig_mem_ctx, data, length, sig); -} - -/* - sign a packet -*/ -static NTSTATUS dcesrv_crypto_ntlmssp_sign(struct dcesrv_auth *auth, TALLOC_CTX *sig_mem_ctx, - const uint8_t *data, size_t length, DATA_BLOB *sig) -{ - struct auth_ntlmssp_state *auth_ntlmssp_state = auth->crypto_ctx.private_data; - - return ntlmssp_sign_packet(auth_ntlmssp_state->ntlmssp_state, sig_mem_ctx, data, length, sig); -} - -/* - check a packet signature -*/ -static NTSTATUS dcesrv_crypto_ntlmssp_check_sig(struct dcesrv_auth *auth, TALLOC_CTX *sig_mem_ctx, - const uint8_t *data, size_t length, const DATA_BLOB *sig) -{ - struct auth_ntlmssp_state *auth_ntlmssp_state = auth->crypto_ctx.private_data; - - return ntlmssp_check_packet(auth_ntlmssp_state->ntlmssp_state, sig_mem_ctx, data, length, sig); -} - -/* - unseal a packet -*/ -static NTSTATUS dcesrv_crypto_ntlmssp_unseal(struct dcesrv_auth *auth, TALLOC_CTX *sig_mem_ctx, - uint8_t *data, size_t length, DATA_BLOB *sig) -{ - struct auth_ntlmssp_state *auth_ntlmssp_state = auth->crypto_ctx.private_data; - - return ntlmssp_unseal_packet(auth_ntlmssp_state->ntlmssp_state, sig_mem_ctx, data, length, sig); -} - -/* - get the session key -*/ -static NTSTATUS dcesrv_crypto_ntlmssp_session_key(struct dcesrv_auth *auth, uint8_t session_key[16]) -{ - struct auth_ntlmssp_state *auth_ntlmssp_state = auth->crypto_ctx.private_data; - - if (auth_ntlmssp_state->ntlmssp_state->session_key.length != 16) { - return NT_STATUS_NO_USER_SESSION_KEY; - } - memcpy(session_key, auth_ntlmssp_state->ntlmssp_state->session_key.data, 16); - - return NT_STATUS_OK; -} - -/* - end crypto state -*/ -static void dcesrv_crypto_ntlmssp_end(struct dcesrv_auth *auth) -{ - struct auth_ntlmssp_state *auth_ntlmssp_state = auth->crypto_ctx.private_data; - - auth->crypto_ctx.private_data = NULL; - - auth_ntlmssp_end(&auth_ntlmssp_state); - - return; -} - -static const struct dcesrv_crypto_ops dcesrv_crypto_ntlmssp_ops = { - .name = "ntlmssp", - .auth_type = DCERPC_AUTH_TYPE_NTLMSSP, - .start = dcesrv_crypto_ntlmssp_start, - .update = dcesrv_crypto_ntlmssp_update, - .seal = dcesrv_crypto_ntlmssp_seal, - .sign = dcesrv_crypto_ntlmssp_sign, - .check_sig = dcesrv_crypto_ntlmssp_check_sig, - .unseal = dcesrv_crypto_ntlmssp_unseal, - .session_key = dcesrv_crypto_ntlmssp_session_key, - .end = dcesrv_crypto_ntlmssp_end -}; - -/* - startup the cryptographic side of an authenticated dcerpc server -*/ -const struct dcesrv_crypto_ops *dcesrv_crypto_ntlmssp_get_ops(void) -{ - return &dcesrv_crypto_ntlmssp_ops; -} diff --git a/source4/rpc_server/dcesrv_crypto_schannel.c b/source4/rpc_server/dcesrv_crypto_schannel.c deleted file mode 100644 index 5da1a171f9..0000000000 --- a/source4/rpc_server/dcesrv_crypto_schannel.c +++ /dev/null @@ -1,239 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - server side dcerpc authentication code - schannel auth/crypto code - - 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" - -struct srv_schannel_state { - TALLOC_CTX *mem_ctx; - struct schannel_bind bind_info; - struct schannel_state *state; -}; - -static NTSTATUS schannel_setup_session_info(struct srv_schannel_state *schannel, - const char *account_name, - struct auth_session_info **session_info) -{ - TALLOC_CTX *mem_ctx; - - mem_ctx = talloc_init("schannel_setup"); - if (mem_ctx == NULL) { - return NT_STATUS_NO_MEMORY; - } - - (*session_info) = talloc_p(mem_ctx, struct auth_session_info); - if (*session_info == NULL) { - talloc_destroy(mem_ctx); - return NT_STATUS_NO_MEMORY; - } - - ZERO_STRUCTP(*session_info); - - (*session_info)->workstation = talloc_strdup(mem_ctx, account_name); - if ((*session_info)->workstation == NULL) { - return NT_STATUS_NO_MEMORY; - } - - /* TODO: fill in the rest of the session_info structure */ - - return NT_STATUS_OK; -} - - -/* - start crypto state -*/ -static NTSTATUS dcesrv_crypto_schannel_start(struct dcesrv_auth *auth, DATA_BLOB *auth_blob) -{ - struct srv_schannel_state *schannel = NULL; - NTSTATUS status; - TALLOC_CTX *mem_ctx; - const char *account_name; - struct schannel_bind_ack ack; - struct creds_CredentialState creds; - - mem_ctx = talloc_init("schannel_start"); - if (!mem_ctx) { - return NT_STATUS_NO_MEMORY; - } - - schannel = talloc_p(mem_ctx, struct srv_schannel_state); - if (!schannel) { - talloc_destroy(mem_ctx); - return NT_STATUS_NO_MEMORY; - } - - schannel->mem_ctx = mem_ctx; - - /* parse the schannel startup blob */ - status = ndr_pull_struct_blob(auth_blob, mem_ctx, &schannel->bind_info, - (ndr_pull_flags_fn_t)ndr_pull_schannel_bind); - if (!NT_STATUS_IS_OK(status)) { - talloc_destroy(mem_ctx); - return status; - } - - if (schannel->bind_info.bind_type == 23) { - account_name = schannel->bind_info.u.info23.account_name; - } else { - account_name = schannel->bind_info.u.info3.account_name; - } - - /* pull the session key for this client */ - status = schannel_fetch_session_key(mem_ctx, account_name, &creds); - if (!NT_STATUS_IS_OK(status)) { - talloc_destroy(mem_ctx); - return status; - } - - /* start up the schannel server code */ - status = schannel_start(&schannel->state, creds.session_key, False); - if (!NT_STATUS_IS_OK(status)) { - talloc_destroy(mem_ctx); - return status; - } - - status = schannel_setup_session_info(schannel, account_name, - &auth->session_info); - if (!NT_STATUS_IS_OK(status)) { - talloc_destroy(mem_ctx); - return status; - } - - auth->crypto_ctx.private_data = schannel; - - ack.unknown1 = 1; - ack.unknown2 = 0; - ack.unknown3 = 0x6c0000; - - status = ndr_push_struct_blob(auth_blob, mem_ctx, &ack, - (ndr_push_flags_fn_t)ndr_push_schannel_bind_ack); - if (!NT_STATUS_IS_OK(status)) { - talloc_destroy(mem_ctx); - return NT_STATUS_INVALID_PARAMETER; - } - - return status; -} - -/* - update crypto state -*/ -static NTSTATUS dcesrv_crypto_schannel_update(struct dcesrv_auth *auth, TALLOC_CTX *out_mem_ctx, - const DATA_BLOB in, DATA_BLOB *out) -{ - return NT_STATUS_OK; -} - -/* - seal a packet -*/ -static NTSTATUS dcesrv_crypto_schannel_seal(struct dcesrv_auth *auth, TALLOC_CTX *sig_mem_ctx, - uint8_t *data, size_t length, DATA_BLOB *sig) -{ - struct srv_schannel_state *srv_schannel_state = auth->crypto_ctx.private_data; - - return schannel_seal_packet(srv_schannel_state->state, sig_mem_ctx, data, length, sig); -} - -/* - sign a packet -*/ -static NTSTATUS dcesrv_crypto_schannel_sign(struct dcesrv_auth *auth, TALLOC_CTX *sig_mem_ctx, - const uint8_t *data, size_t length, DATA_BLOB *sig) -{ - struct srv_schannel_state *srv_schannel_state = auth->crypto_ctx.private_data; - - return schannel_sign_packet(srv_schannel_state->state, sig_mem_ctx, data, length, sig); -} - -/* - check a packet signature -*/ -static NTSTATUS dcesrv_crypto_schannel_check_sig(struct dcesrv_auth *auth, TALLOC_CTX *sig_mem_ctx, - const uint8_t *data, size_t length, const DATA_BLOB *sig) -{ - struct srv_schannel_state *srv_schannel_state = auth->crypto_ctx.private_data; - - return schannel_check_packet(srv_schannel_state->state, data, length, sig); -} - -/* - unseal a packet -*/ -static NTSTATUS dcesrv_crypto_schannel_unseal(struct dcesrv_auth *auth, TALLOC_CTX *sig_mem_ctx, - uint8_t *data, size_t length, DATA_BLOB *sig) -{ - struct srv_schannel_state *srv_schannel_state = auth->crypto_ctx.private_data; - - return schannel_unseal_packet(srv_schannel_state->state, sig_mem_ctx, data, length, sig); -} - -/* - get the session key -*/ -static NTSTATUS dcesrv_crypto_schannel_session_key(struct dcesrv_auth *auth, uint8_t session_key[16]) -{ - struct srv_schannel_state *srv_schannel_state = auth->crypto_ctx.private_data; - - memcpy(session_key, srv_schannel_state->state->session_key, 16); - - return NT_STATUS_OK; -} - -/* - end crypto state -*/ -static void dcesrv_crypto_schannel_end(struct dcesrv_auth *auth) -{ - struct srv_schannel_state *srv_schannel_state = auth->crypto_ctx.private_data; - - if (srv_schannel_state == NULL) { - return; - } - - schannel_end(&srv_schannel_state->state); - - talloc_destroy(srv_schannel_state->mem_ctx); - - auth->crypto_ctx.private_data = NULL; -} - -static const struct dcesrv_crypto_ops dcesrv_crypto_schannel_ops = { - .name = "schannel", - .auth_type = DCERPC_AUTH_TYPE_SCHANNEL, - .start = dcesrv_crypto_schannel_start, - .update = dcesrv_crypto_schannel_update, - .seal = dcesrv_crypto_schannel_seal, - .sign = dcesrv_crypto_schannel_sign, - .check_sig = dcesrv_crypto_schannel_check_sig, - .unseal = dcesrv_crypto_schannel_unseal, - .session_key = dcesrv_crypto_schannel_session_key, - .end = dcesrv_crypto_schannel_end -}; - -/* - startup the cryptographic side of an authenticated dcerpc server -*/ -const struct dcesrv_crypto_ops *dcesrv_crypto_schannel_get_ops(void) -{ - return &dcesrv_crypto_schannel_ops; -} diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c index 80bbb6b583..7eb4c0e815 100644 --- a/source4/rpc_server/netlogon/dcerpc_netlogon.c +++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c @@ -58,21 +58,15 @@ static NTSTATUS netlogon_schannel_setup(struct dcesrv_call_state *dce_call) state->mem_ctx = mem_ctx; state->authenticated = True; - state->creds = talloc_p(mem_ctx, struct creds_CredentialState); - if (state->creds == NULL) { - talloc_destroy(mem_ctx); - return NT_STATUS_NO_MEMORY; - } - ZERO_STRUCTP(state->creds); - if (dce_call->conn->auth_state.session_info == NULL) { talloc_destroy(mem_ctx); return NT_STATUS_NO_USER_SESSION_KEY; } - status = schannel_fetch_session_key(mem_ctx, - dce_call->conn->auth_state.session_info->workstation, - state->creds); + status = dcerpc_schannel_creds(dce_call->conn->auth_state.gensec_security, + mem_ctx, + &state->creds); + if (!NT_STATUS_IS_OK(status)) { talloc_destroy(mem_ctx); return status; diff --git a/source4/rpc_server/samr/samr_password.c b/source4/rpc_server/samr/samr_password.c index 988c52e4ee..f1947022a2 100644 --- a/source4/rpc_server/samr/samr_password.c +++ b/source4/rpc_server/samr/samr_password.c @@ -693,7 +693,7 @@ NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call, { char new_pass[512]; uint32_t new_pass_len; - DATA_BLOB session_key; + DATA_BLOB session_key = data_blob(NULL, 0); session_key = data_blob(NULL,0); @@ -703,7 +703,7 @@ NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call, if (session_key.length == 0) { DEBUG(3,("Bad session key in samr_set_password\n")); - return NT_STATUS_WRONG_PASSWORD; + return NT_STATUS_NO_USER_SESSION_KEY; } arcfour_crypt_blob(pwbuf->data, 516, &session_key); @@ -740,7 +740,7 @@ NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call, char new_pass[512]; uint32_t new_pass_len; DATA_BLOB co_session_key; - DATA_BLOB session_key; + DATA_BLOB session_key = data_blob(NULL, 0); struct MD5Context ctx; session_key = data_blob(NULL,0); @@ -749,6 +749,11 @@ NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call, session_key = dce_call->conn->auth_state.session_info->session_key; } + if (session_key.length == 0) { + DEBUG(3,("Bad session key in samr_set_password\n")); + return NT_STATUS_NO_USER_SESSION_KEY; + } + co_session_key = data_blob_talloc(mem_ctx, NULL, 16); if (!co_session_key.data) { return NT_STATUS_NO_MEMORY; diff --git a/source4/smb_server/password.c b/source4/smb_server/password.c index 499023374f..6132e9f84f 100644 --- a/source4/smb_server/password.c +++ b/source4/smb_server/password.c @@ -58,7 +58,7 @@ void invalidate_vuid(struct smbsrv_connection *smb, uint16_t vuid) session_yield(vuser); - talloc_destroy(vuser->session_info->mem_ctx); + free_session_info(&vuser->session_info); DLIST_REMOVE(smb->users.validated_users, vuser); diff --git a/source4/torture/rpc/schannel.c b/source4/torture/rpc/schannel.c index e120c0d2ad..f6c94b145a 100644 --- a/source4/torture/rpc/schannel.c +++ b/source4/torture/rpc/schannel.c @@ -92,7 +92,10 @@ static BOOL test_schannel(TALLOC_CTX *mem_ctx, goto failed; } - test_samr_ops(p, mem_ctx); + if (!test_samr_ops(p, mem_ctx)) { + printf("Failed to process schannel secured ops\n"); + goto failed; + } torture_leave_domain(join_ctx); return True; diff --git a/source4/utils/ntlm_auth.c b/source4/utils/ntlm_auth.c index 7690a549f7..1ae9075fba 100644 --- a/source4/utils/ntlm_auth.c +++ b/source4/utils/ntlm_auth.c @@ -35,6 +35,7 @@ enum stdio_helper_mode { SQUID_2_5_NTLMSSP, NTLMSSP_CLIENT_1, GSS_SPNEGO_CLIENT, + GSS_SPNEGO_SERVER, NTLM_SERVER_1, NUM_HELPER_MODES }; @@ -44,19 +45,19 @@ enum stdio_helper_mode { typedef void (*stdio_helper_function)(enum stdio_helper_mode stdio_helper_mode, - char *buf, int length); + char *buf, int length, void **private); static void manage_squid_basic_request (enum stdio_helper_mode stdio_helper_mode, - char *buf, int length); + char *buf, int length, void **private); -static void manage_squid_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode, - char *buf, int length); - -static void manage_gensec_client_request (enum stdio_helper_mode stdio_helper_mode, - char *buf, int length); +static void manage_gensec_request (enum stdio_helper_mode stdio_helper_mode, + char *buf, int length, void **private); static void manage_ntlm_server_1_request (enum stdio_helper_mode stdio_helper_mode, - char *buf, int length); + char *buf, int length, void **private); + +static void manage_squid_request(enum stdio_helper_mode helper_mode, + stdio_helper_function fn, void *private); static const struct { enum stdio_helper_mode mode; @@ -65,9 +66,10 @@ static const struct { } stdio_helper_protocols[] = { { SQUID_2_4_BASIC, "squid-2.4-basic", manage_squid_basic_request}, { SQUID_2_5_BASIC, "squid-2.5-basic", manage_squid_basic_request}, - { SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_squid_ntlmssp_request}, - { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_gensec_client_request}, - { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gensec_client_request}, + { SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_gensec_request}, + { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gensec_request}, + { GSS_SPNEGO_SERVER, "gss-spnego-server", manage_gensec_request}, + { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_gensec_request}, { NTLM_SERVER_1, "ntlm-server-1", manage_ntlm_server_1_request}, { NUM_HELPER_MODES, NULL, NULL} }; @@ -172,149 +174,8 @@ static NTSTATUS local_pw_check_specified(const char *username, } -static NTSTATUS local_pw_check(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *user_session_key, DATA_BLOB *lm_session_key) -{ - NTSTATUS nt_status; - uint8 lm_pw[16], nt_pw[16]; - uint8_t *lm_pwd, *nt_pwd; - - E_md4hash(opt_password, nt_pw); - if (E_deshash(opt_password, lm_pw)) { - lm_pwd = lm_pw; - } else { - lm_pwd = NULL; - } - nt_pwd = nt_pw; - - nt_status = ntlm_password_check(ntlmssp_state->mem_ctx, - &ntlmssp_state->chal, - &ntlmssp_state->lm_resp, - &ntlmssp_state->nt_resp, - NULL, NULL, - ntlmssp_state->user, - ntlmssp_state->user, - ntlmssp_state->domain, - lm_pwd, nt_pwd, user_session_key, lm_session_key); - - if (NT_STATUS_IS_OK(nt_status)) { - ntlmssp_state->auth_context = talloc_asprintf(ntlmssp_state->mem_ctx, - "%s%c%s", ntlmssp_state->domain, - *lp_winbind_separator(), - ntlmssp_state->user); - } else { - DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n", - ntlmssp_state->domain, ntlmssp_state->user, ntlmssp_state->workstation, - nt_errstr(nt_status))); - ntlmssp_state->auth_context = NULL; - } - return nt_status; -} - -static NTSTATUS ntlm_auth_start_ntlmssp_server(struct ntlmssp_state **ntlmssp_state) -{ - NTSTATUS status = ntlmssp_server_start(ntlmssp_state); - - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("Could not start NTLMSSP client: %s\n", - nt_errstr(status))); - return status; - } - - /* Have we been given a local password, or should we ask winbind? */ - if (opt_password) { - (*ntlmssp_state)->check_password = local_pw_check; - (*ntlmssp_state)->get_domain = lp_workgroup; - (*ntlmssp_state)->get_global_myname = global_myname; - } else { - DEBUG(0, ("Winbind not supported in Samba4 ntlm_auth yet, specify --password\n")); - exit(1); - } - return NT_STATUS_OK; -} - -static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode, - char *buf, int length) -{ - static struct ntlmssp_state *ntlmssp_state = NULL; - DATA_BLOB request, reply; - NTSTATUS nt_status; - - if (strlen(buf) < 2) { - DEBUG(1, ("NTLMSSP query [%s] invalid", buf)); - x_fprintf(x_stdout, "BH\n"); - return; - } - - if (strlen(buf) > 3) { - request = base64_decode_data_blob(buf + 3); - } else { - request = data_blob(NULL, 0); - } - - if ((strncmp(buf, "PW ", 3) == 0)) { - /* The calling application wants us to use a local password (rather than winbindd) */ - - opt_password = strndup((const char *)request.data, request.length); - - if (opt_password == NULL) { - DEBUG(1, ("Out of memory\n")); - x_fprintf(x_stdout, "BH\n"); - data_blob_free(&request); - return; - } - - x_fprintf(x_stdout, "OK\n"); - data_blob_free(&request); - return; - } - - if (strncmp(buf, "YR", 2) == 0) { - if (ntlmssp_state) - ntlmssp_end(&ntlmssp_state); - } else if (strncmp(buf, "KK", 2) == 0) { - - } else { - DEBUG(1, ("NTLMSSP query [%s] invalid", buf)); - x_fprintf(x_stdout, "BH\n"); - return; - } - - if (!ntlmssp_state) { - if (!NT_STATUS_IS_OK(nt_status = ntlm_auth_start_ntlmssp_server(&ntlmssp_state))) { - x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status)); - return; - } - } - - DEBUG(10, ("got NTLMSSP packet:\n")); - dump_data(10, (const char *)request.data, request.length); - - nt_status = ntlmssp_update(ntlmssp_state, NULL, request, &reply); - - if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { - char *reply_base64 = base64_encode_data_blob(reply); - x_fprintf(x_stdout, "TT %s\n", reply_base64); - SAFE_FREE(reply_base64); - data_blob_free(&reply); - DEBUG(10, ("NTLMSSP challenge\n")); - } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) { - x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status)); - DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status))); - - ntlmssp_end(&ntlmssp_state); - } else if (!NT_STATUS_IS_OK(nt_status)) { - x_fprintf(x_stdout, "NA %s\n", nt_errstr(nt_status)); - DEBUG(10, ("NTLMSSP %s\n", nt_errstr(nt_status))); - } else { - x_fprintf(x_stdout, "AF %s\n", (char *)ntlmssp_state->auth_context); - DEBUG(10, ("NTLMSSP OK!\n")); - } - - data_blob_free(&request); -} - static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode, - char *buf, int length) + char *buf, int length, void **private) { char *user, *pass; user=buf; @@ -340,16 +201,14 @@ static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode, } } -static void manage_gensec_client_request(enum stdio_helper_mode stdio_helper_mode, - char *buf, int length) +/* This is a bit hairy, but the basic idea is to do a password callback + to the calling application. The callback comes from within gensec */ + +static void manage_gensec_get_pw_request(enum stdio_helper_mode stdio_helper_mode, + char *buf, int length, void **private) { DATA_BLOB in; - DATA_BLOB out; - char *out_base64; - static struct gensec_security gensec_state; - NTSTATUS nt_status; - BOOL first = False; - + struct gensec_security **gensec_state = (struct gensec_security **)private; if (strlen(buf) < 2) { DEBUG(1, ("query [%s] invalid", buf)); x_fprintf(x_stdout, "BH\n"); @@ -364,11 +223,10 @@ static void manage_gensec_client_request(enum stdio_helper_mode stdio_helper_mod if (strncmp(buf, "PW ", 3) == 0) { - /* We asked for a password and obviously got it :-) */ - - opt_password = strndup((const char *)in.data, in.length); + (*gensec_state)->password_callback_private = talloc_strndup((*gensec_state)->mem_ctx, + (const char *)in.data, in.length); - if (opt_password == NULL) { + if ((*gensec_state)->password_callback_private == NULL) { DEBUG(1, ("Out of memory\n")); x_fprintf(x_stdout, "BH\n"); data_blob_free(&in); @@ -379,39 +237,124 @@ static void manage_gensec_client_request(enum stdio_helper_mode stdio_helper_mod data_blob_free(&in); return; } + DEBUG(1, ("Asked for (and expected) a password\n")); + x_fprintf(x_stdout, "BH\n"); + data_blob_free(&in); +} + +/* + * Callback for gensec, to ask the calling application for a password. Uses the above function + * for the stdio part of this. + */ + +static NTSTATUS get_password(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx, + char **password) +{ + *password = NULL; + + /* Ask for a password */ + x_fprintf(x_stdout, "PW\n"); + gensec_security->password_callback_private = NULL; + + manage_squid_request(NUM_HELPER_MODES /* bogus */, manage_gensec_get_pw_request, &gensec_security); + *password = (char *)gensec_security->password_callback_private; + if (*password) { + return NT_STATUS_OK; + } else { + return NT_STATUS_INVALID_PARAMETER; + } +} + +static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode, + char *buf, int length, void **private) +{ + DATA_BLOB in; + DATA_BLOB out = data_blob(NULL, 0); + char *out_base64 = NULL; + const char *reply_arg = NULL; + struct gensec_security **gensec_state = (struct gensec_security **)private; + NTSTATUS nt_status; + BOOL first = False; + const char *reply_code; + + if (strlen(buf) < 2) { + DEBUG(1, ("query [%s] invalid", buf)); + x_fprintf(x_stdout, "BH\n"); + return; + } + + if (strlen(buf) > 3) { + in = base64_decode_data_blob(buf + 3); + } else { + in = data_blob(NULL, 0); + } + if (strncmp(buf, "YR", 2) == 0) { - if (gensec_state.ops) { - gensec_state.ops->end(&gensec_state); - gensec_state.ops = NULL; + if (gensec_state && *gensec_state) { + gensec_end(gensec_state); + *gensec_state = NULL; } + } else if ( (strncmp(buf, "OK", 2) == 0)) { + /* do nothing */ + data_blob_free(&in); + return; } else if ( (strncmp(buf, "TT ", 3) != 0) && - (strncmp(buf, "AF ", 3) != 0) && - (strncmp(buf, "NA ", 3) != 0) ) { + (strncmp(buf, "KK ", 3) != 0) && + (strncmp(buf, "AF ", 3) != 0) && + (strncmp(buf, "NA ", 3) != 0) && + (strncmp(buf, "PW ", 3) != 0)) { DEBUG(1, ("SPNEGO request [%s] invalid\n", buf)); x_fprintf(x_stdout, "BH\n"); data_blob_free(&in); return; } - if (!opt_password) { - x_fprintf(x_stdout, "PW\n"); - data_blob_free(&in); - return; - } - /* setup gensec */ - if (!gensec_state.ops) { - if (stdio_helper_mode == GSS_SPNEGO_CLIENT) { - gensec_state.ops = gensec_security_by_oid(OID_SPNEGO); - } else if (stdio_helper_mode == NTLMSSP_CLIENT_1) { - gensec_state.ops = gensec_security_by_oid(OID_NTLMSSP); - } else { - exit(1); + if (!(gensec_state && *gensec_state)) { + switch (stdio_helper_mode) { + case GSS_SPNEGO_CLIENT: + case NTLMSSP_CLIENT_1: + /* setup the client side */ + + if (!NT_STATUS_IS_OK(gensec_client_start(gensec_state))) { + exit(1); + } + gensec_set_username(*gensec_state, opt_username); + gensec_set_domain(*gensec_state, opt_domain); + if (opt_password) { + if (!NT_STATUS_IS_OK(gensec_set_password(*gensec_state, opt_password))) { + DEBUG(1, ("Out of memory\n")); + x_fprintf(x_stdout, "BH\n"); + data_blob_free(&in); + return; + } + } else { + gensec_set_password_callback(*gensec_state, get_password, NULL); + } + + break; + case GSS_SPNEGO_SERVER: + case SQUID_2_5_NTLMSSP: + if (!NT_STATUS_IS_OK(gensec_server_start(gensec_state))) { + exit(1); + } + break; + default: + abort(); + } + + switch (stdio_helper_mode) { + case GSS_SPNEGO_CLIENT: + case GSS_SPNEGO_SERVER: + nt_status = gensec_start_mech_by_oid(*gensec_state, OID_SPNEGO); + break; + case NTLMSSP_CLIENT_1: + case SQUID_2_5_NTLMSSP: + nt_status = gensec_start_mech_by_oid(*gensec_state, OID_NTLMSSP); + break; + default: + abort(); } - gensec_state.user.name = opt_username; - gensec_state.user.domain = opt_domain; - gensec_state.user.password = opt_password; - nt_status = gensec_state.ops->client_start(&gensec_state); if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(1, ("SPENGO login failed to initialise: %s\n", nt_errstr(nt_status))); @@ -423,33 +366,104 @@ static void manage_gensec_client_request(enum stdio_helper_mode stdio_helper_mod } } + if (strncmp(buf, "PW ", 3) == 0) { + + if (!NT_STATUS_IS_OK(gensec_set_password(*gensec_state, + talloc_strndup((*gensec_state)->mem_ctx, + (const char *)in.data, + in.length)))) { + DEBUG(1, ("Out of memory\n")); + x_fprintf(x_stdout, "BH\n"); + data_blob_free(&in); + return; + } + + x_fprintf(x_stdout, "OK\n"); + data_blob_free(&in); + return; + } + /* update */ - nt_status = gensec_state.ops->update(&gensec_state, NULL, in, &out); + nt_status = gensec_update(*gensec_state, NULL, in, &out); - if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + /* don't leak 'bad password'/'no such user' info to the network client */ + nt_status = nt_status_squash(nt_status); + if (out.length) { out_base64 = base64_encode_data_blob(out); + } else { + out_base64 = NULL; + } + if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + reply_arg = "*"; if (first) { - x_fprintf(x_stdout, "YR %s\n", out_base64); - } else { - x_fprintf(x_stdout, "KK %s\n", out_base64); + reply_code = "YR"; + } else if ((*gensec_state)->gensec_role == GENSEC_CLIENT) { + reply_code = "KK"; + } else if ((*gensec_state)->gensec_role == GENSEC_SERVER) { + reply_code = "TT"; + } else { + abort(); } - SAFE_FREE(out_base64); + } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) { + reply_code = "BH"; + reply_arg = nt_errstr(nt_status); + DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status))); + } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) { + reply_code = "BH"; + reply_arg = nt_errstr(nt_status); + DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status))); } else if (!NT_STATUS_IS_OK(nt_status)) { - DEBUG(1, ("SPENGO login failed: %s\n", nt_errstr(nt_status))); - x_fprintf(x_stdout, "BH\n"); + reply_code = "NA"; + reply_arg = nt_errstr(nt_status); + DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status))); + } else if /* OK */ ((*gensec_state)->gensec_role == GENSEC_SERVER) { + struct auth_session_info *session_info; + + nt_status = gensec_session_info(*gensec_state, &session_info); + if (!NT_STATUS_IS_OK(nt_status)) { + reply_code = "BH"; + reply_arg = nt_errstr(nt_status); + DEBUG(1, ("GENSEC failed to retreive the session info: %s\n", nt_errstr(nt_status))); + } else { + + reply_code = "AF"; + reply_arg = talloc_asprintf((*gensec_state)->mem_ctx, + "%s%s%s", session_info->server_info->domain, + lp_winbind_separator(), session_info->server_info->account_name); + talloc_destroy(session_info->mem_ctx); + } + } else if ((*gensec_state)->gensec_role == GENSEC_SERVER) { + reply_code = "AF"; + reply_arg = NULL; } else { - x_fprintf(x_stdout, "AF\n"); + abort(); } + switch (stdio_helper_mode) { + case GSS_SPNEGO_SERVER: + if (out_base64) { + x_fprintf(x_stdout, "%s %s %s\n", reply_code, out_base64, reply_arg); + } else { + x_fprintf(x_stdout, "%s %s\n", reply_code, reply_arg); + } + default: + if (out_base64) { + x_fprintf(x_stdout, "%s %s\n", reply_code, out_base64); + } else { + x_fprintf(x_stdout, "%s %s\n", reply_code, reply_arg); + } + } + + SAFE_FREE(out_base64); return; } static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode, - char *buf, int length) + char *buf, int length, void **private) { char *request, *parameter; static DATA_BLOB challenge; @@ -643,7 +657,7 @@ static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mod } } -static void manage_squid_request(enum stdio_helper_mode helper_mode, stdio_helper_function fn) +static void manage_squid_request(enum stdio_helper_mode helper_mode, stdio_helper_function fn, void *private) { char buf[SQUID_BUFFER_SIZE+1]; int length; @@ -684,16 +698,16 @@ static void manage_squid_request(enum stdio_helper_mode helper_mode, stdio_helpe return; } - fn(helper_mode, buf, length); + fn(helper_mode, buf, length, private); } - static void squid_stream(enum stdio_helper_mode stdio_mode, stdio_helper_function fn) { /* initialize FDescs */ x_setbuf(x_stdout, NULL); x_setbuf(x_stderr, NULL); + void *private = NULL; while(1) { - manage_squid_request(stdio_mode, fn); + manage_squid_request(stdio_mode, fn, &private); } } |