From 3045ecfa1df7506a826e53728231ca00451ccef4 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 25 Apr 2005 05:03:50 +0000 Subject: [PATCH] r6458: Split up NTLMSSP into a new directory, and into seperate files for the client and server logic code. In future, this may allow us to build only the NTLMSSP client, and not the server, but in the short-term, it allows me greater sainity in moving around these files. Andrew Bartlett (This used to be commit 2f22841c6753e3d5816c12bd463b71f74e1d8796) --- source4/auth/auth.h | 1 - source4/auth/gensec/gensec.mk | 15 +- source4/auth/gensec/ntlmssp.c | 1605 ----------------- source4/auth/ntlmssp/ntlmssp.c | 442 +++++ source4/auth/{gensec => ntlmssp}/ntlmssp.h | 0 source4/auth/ntlmssp/ntlmssp.mk | 14 + source4/auth/ntlmssp/ntlmssp_client.c | 445 +++++ .../auth/{gensec => ntlmssp}/ntlmssp_parse.c | 0 source4/auth/ntlmssp/ntlmssp_server.c | 765 ++++++++ .../auth/{gensec => ntlmssp}/ntlmssp_sign.c | 1 + source4/build/smb_build/main.pm | 1 + source4/torture/auth/ntlmssp.c | 1 + 12 files changed, 1670 insertions(+), 1620 deletions(-) delete mode 100644 source4/auth/gensec/ntlmssp.c create mode 100644 source4/auth/ntlmssp/ntlmssp.c rename source4/auth/{gensec => ntlmssp}/ntlmssp.h (100%) create mode 100644 source4/auth/ntlmssp/ntlmssp.mk create mode 100644 source4/auth/ntlmssp/ntlmssp_client.c rename source4/auth/{gensec => ntlmssp}/ntlmssp_parse.c (100%) create mode 100644 source4/auth/ntlmssp/ntlmssp_server.c rename source4/auth/{gensec => ntlmssp}/ntlmssp_sign.c (99%) diff --git a/source4/auth/auth.h b/source4/auth/auth.h index a9f6b8eac54..f64017832d9 100644 --- a/source4/auth/auth.h +++ b/source4/auth/auth.h @@ -22,7 +22,6 @@ #ifndef _SAMBA_AUTH_H #define _SAMBA_AUTH_H -#include "auth/gensec/ntlmssp.h" #include "libcli/auth/credentials.h" #include "auth/gensec/gensec.h" #include "auth/gensec/spnego.h" diff --git a/source4/auth/gensec/gensec.mk b/source4/auth/gensec/gensec.mk index 7b5fa7ae361..02823fc11f5 100644 --- a/source4/auth/gensec/gensec.mk +++ b/source4/auth/gensec/gensec.mk @@ -49,19 +49,6 @@ ADD_OBJ_FILES = \ # End MODULE gensec_spnego ################################################ -################################################ -# Start MODULE gensec_ntlmssp -[MODULE::gensec_ntlmssp] -SUBSYSTEM = GENSEC -INIT_FUNCTION = gensec_ntlmssp_init -INIT_OBJ_FILES = auth/gensec/ntlmssp.o -ADD_OBJ_FILES = \ - auth/gensec/ntlmssp_parse.o \ - auth/gensec/ntlmssp_sign.o -REQUIRED_SUBSYSTEMS = AUTH -# End MODULE gensec_ntlmssp -################################################ - ################################################ # Start MODULE gensec_schannel [MODULE::gensec_schannel] @@ -71,7 +58,7 @@ INIT_OBJ_FILES = auth/gensec/schannel.o ADD_OBJ_FILES = \ auth/gensec/schannel_sign.o REQUIRED_SUBSYSTEMS = AUTH SCHANNELDB -# End MODULE gensec_ntlmssp +# End MODULE gensec_schannel ################################################ ################################################ diff --git a/source4/auth/gensec/ntlmssp.c b/source4/auth/gensec/ntlmssp.c deleted file mode 100644 index 5b40a412bd8..00000000000 --- a/source4/auth/gensec/ntlmssp.c +++ /dev/null @@ -1,1605 +0,0 @@ -/* - Unix SMB/Netbios implementation. - Version 3.0 - handle NLTMSSP, client server side parsing - - Copyright (C) Andrew Tridgell 2001 - Copyright (C) Andrew Bartlett 2001-2005 - Copyright (C) Stefan Metzmacher 2005 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include "includes.h" -#include "auth/auth.h" -#include "lib/crypto/crypto.h" -#include "pstring.h" - -static NTSTATUS ntlmssp_client_initial(struct ntlmssp_state *ntlmssp_state, - TALLOC_CTX *out_mem_ctx, - DATA_BLOB in, DATA_BLOB *out); -static NTSTATUS ntlmssp_server_negotiate(struct ntlmssp_state *ntlmssp_state, - TALLOC_CTX *out_mem_ctx, - const DATA_BLOB in, DATA_BLOB *out); -static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_state *ntlmssp_state, - TALLOC_CTX *out_mem_ctx, - const DATA_BLOB in, DATA_BLOB *out); -static NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state, - TALLOC_CTX *out_mem_ctx, - const DATA_BLOB in, DATA_BLOB *out); - -/** - * Callbacks for NTLMSSP - for both client and server operating modes - * - */ - -static const struct ntlmssp_callbacks { - enum ntlmssp_role role; - enum ntlmssp_message_type ntlmssp_command; - NTSTATUS (*fn)(struct ntlmssp_state *ntlmssp_state, - TALLOC_CTX *out_mem_ctx, - DATA_BLOB in, DATA_BLOB *out); -} ntlmssp_callbacks[] = { - {NTLMSSP_CLIENT, NTLMSSP_INITIAL, ntlmssp_client_initial}, - {NTLMSSP_SERVER, NTLMSSP_NEGOTIATE, ntlmssp_server_negotiate}, - {NTLMSSP_CLIENT, NTLMSSP_CHALLENGE, ntlmssp_client_challenge}, - {NTLMSSP_SERVER, NTLMSSP_AUTH, ntlmssp_server_auth}, -}; - - -/** - * Print out the NTLMSSP flags for debugging - * @param neg_flags The flags from the packet - */ - -void debug_ntlmssp_flags(uint32_t neg_flags) -{ - DEBUG(3,("Got NTLMSSP neg_flags=0x%08x\n", neg_flags)); - - if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_UNICODE\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_OEM) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_OEM\n")); - if (neg_flags & NTLMSSP_REQUEST_TARGET) - DEBUGADD(4, (" NTLMSSP_REQUEST_TARGET\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_SIGN) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_SIGN\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_SEAL) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_SEAL\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_LM_KEY\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_NETWARE) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NETWARE\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_NTLM) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NTLM\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_ALWAYS_SIGN\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_NTLM2) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NTLM2\n")); - if (neg_flags & NTLMSSP_CHAL_TARGET_INFO) - DEBUGADD(4, (" NTLMSSP_CHAL_TARGET_INFO\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_128) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_128\n")); - if (neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) - DEBUGADD(4, (" NTLMSSP_NEGOTIATE_KEY_EXCH\n")); -} - -/** - * Default challenge generation code. - * - */ - -static const uint8_t *get_challenge(const struct ntlmssp_state *ntlmssp_state) -{ - uint8_t *chal = talloc_size(ntlmssp_state, 8); - generate_random_buffer(chal, 8); - - return chal; -} - -/** - * Default 'we can set the challenge to anything we like' implementation - * - */ - -static BOOL may_set_challenge(const struct ntlmssp_state *ntlmssp_state) -{ - return True; -} - -/** - * Default 'we can set the challenge to anything we like' implementation - * - * Does not actually do anything, as the value is always in the structure anyway. - * - */ - -static NTSTATUS set_challenge(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *challenge) -{ - SMB_ASSERT(challenge->length == 8); - return NT_STATUS_OK; -} - -/** - * Set a username on an NTLMSSP context - ensures it is talloc()ed - * - */ - -NTSTATUS ntlmssp_set_username(struct ntlmssp_state *ntlmssp_state, const char *user) -{ - if (!user) { - /* it should be at least "" */ - DEBUG(1, ("NTLMSSP failed to set username - cannot accept NULL username\n")); - return NT_STATUS_INVALID_PARAMETER; - } - ntlmssp_state->user = talloc_strdup(ntlmssp_state, user); - if (!ntlmssp_state->user) { - return NT_STATUS_NO_MEMORY; - } - return NT_STATUS_OK; -} - -/** - * Set a password on an NTLMSSP context - ensures it is talloc()ed - * - */ -NTSTATUS ntlmssp_set_password(struct ntlmssp_state *ntlmssp_state, const char *password) -{ - if (!password) { - ntlmssp_state->password = NULL; - } else { - ntlmssp_state->password = talloc_strdup(ntlmssp_state, password); - if (!ntlmssp_state->password) { - return NT_STATUS_NO_MEMORY; - } - } - return NT_STATUS_OK; -} - -/** - * Set a domain on an NTLMSSP context - ensures it is talloc()ed - * - */ -NTSTATUS ntlmssp_set_domain(struct ntlmssp_state *ntlmssp_state, const char *domain) -{ - ntlmssp_state->domain = talloc_strdup(ntlmssp_state, domain); - if (!ntlmssp_state->domain) { - return NT_STATUS_NO_MEMORY; - } - return NT_STATUS_OK; -} - -/** - * Set a workstation on an NTLMSSP context - ensures it is talloc()ed - * - */ -NTSTATUS ntlmssp_set_workstation(struct ntlmssp_state *ntlmssp_state, const char *workstation) -{ - ntlmssp_state->workstation = talloc_strdup(ntlmssp_state, workstation); - if (!ntlmssp_state->workstation) { - return NT_STATUS_NO_MEMORY; - } - return NT_STATUS_OK; -} - -/** - * Store a DATA_BLOB containing an NTLMSSP response, for use later. - * This copies the data blob - */ - -NTSTATUS ntlmssp_store_response(struct ntlmssp_state *ntlmssp_state, - DATA_BLOB response) -{ - ntlmssp_state->stored_response = data_blob_talloc(ntlmssp_state, - response.data, response.length); - return NT_STATUS_OK; -} - -/** - * Next state function for the wrapped NTLMSSP state machine - * - * @param gensec_security GENSEC state, initialised to NTLMSSP - * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on - * @param in The request, as a DATA_BLOB - * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx - * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent, - * or NT_STATUS_OK if the user is authenticated. - */ - -static NTSTATUS gensec_ntlmssp_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, - const DATA_BLOB in, DATA_BLOB *out) -{ - struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; - struct ntlmssp_state *ntlmssp_state = gensec_ntlmssp_state->ntlmssp_state; - NTSTATUS status; - - DATA_BLOB input; - uint32_t ntlmssp_command; - int i; - - *out = data_blob(NULL, 0); - - if (ntlmssp_state->expected_state == NTLMSSP_DONE) { - return NT_STATUS_OK; - } - - if (!out_mem_ctx) { - /* if the caller doesn't want to manage/own the memory, - we can put it on our context */ - out_mem_ctx = ntlmssp_state; - } - - if (!in.length && ntlmssp_state->stored_response.length) { - input = ntlmssp_state->stored_response; - - /* we only want to read the stored response once - overwrite it */ - ntlmssp_state->stored_response = data_blob(NULL, 0); - } else { - input = in; - } - - if (!input.length) { - switch (ntlmssp_state->role) { - case NTLMSSP_CLIENT: - ntlmssp_command = NTLMSSP_INITIAL; - break; - case NTLMSSP_SERVER: - /* 'datagram' mode - no neg packet */ - ntlmssp_command = NTLMSSP_NEGOTIATE; - break; - } - } else { - if (!msrpc_parse(ntlmssp_state, - &input, "Cd", - "NTLMSSP", - &ntlmssp_command)) { - DEBUG(1, ("Failed to parse NTLMSSP packet, could not extract NTLMSSP command\n")); - dump_data(2, input.data, input.length); - return NT_STATUS_INVALID_PARAMETER; - } - } - - if (ntlmssp_command != ntlmssp_state->expected_state) { - DEBUG(1, ("got NTLMSSP command %u, expected %u\n", ntlmssp_command, ntlmssp_state->expected_state)); - return NT_STATUS_INVALID_PARAMETER; - } - - for (i=0; i < ARRAY_SIZE(ntlmssp_callbacks); i++) { - if (ntlmssp_callbacks[i].role == ntlmssp_state->role - && ntlmssp_callbacks[i].ntlmssp_command == ntlmssp_command) { - status = ntlmssp_callbacks[i].fn(ntlmssp_state, out_mem_ctx, input, out); - break; - } - } - - if (i == ARRAY_SIZE(ntlmssp_callbacks)) { - - DEBUG(1, ("failed to find NTLMSSP callback for NTLMSSP mode %u, command %u\n", - ntlmssp_state->role, ntlmssp_command)); - - return NT_STATUS_INVALID_PARAMETER; - } - - if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(status)) { - return status; - } - - gensec_ntlmssp_state->have_features = 0; - - if (gensec_ntlmssp_state->ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SIGN) { - gensec_ntlmssp_state->have_features |= GENSEC_FEATURE_SIGN; - } - - if (gensec_ntlmssp_state->ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SEAL) { - gensec_ntlmssp_state->have_features |= GENSEC_FEATURE_SEAL; - } - - if (gensec_ntlmssp_state->ntlmssp_state->session_key.data) { - gensec_ntlmssp_state->have_features |= GENSEC_FEATURE_SESSION_KEY; - } - - return status; -} - -/** - * Return the NTLMSSP master session key - * - * @param ntlmssp_state NTLMSSP State - */ - -NTSTATUS gensec_ntlmssp_session_key(struct gensec_security *gensec_security, - DATA_BLOB *session_key) -{ - struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; - struct ntlmssp_state *ntlmssp_state = gensec_ntlmssp_state->ntlmssp_state; - - if (!ntlmssp_state->session_key.data) { - return NT_STATUS_NO_USER_SESSION_KEY; - } - *session_key = ntlmssp_state->session_key; - - return NT_STATUS_OK; -} - -/** - * Determine correct target name flags for reply, given server role - * and negotiated flags - * - * @param ntlmssp_state NTLMSSP State - * @param neg_flags The flags from the packet - * @param chal_flags The flags to be set in the reply packet - * @return The 'target name' string. - */ - -static const char *ntlmssp_target_name(struct ntlmssp_state *ntlmssp_state, - uint32_t neg_flags, uint32_t *chal_flags) -{ - if (neg_flags & NTLMSSP_REQUEST_TARGET) { - *chal_flags |= NTLMSSP_CHAL_TARGET_INFO; - *chal_flags |= NTLMSSP_REQUEST_TARGET; - if (ntlmssp_state->server_role == ROLE_STANDALONE) { - *chal_flags |= NTLMSSP_TARGET_TYPE_SERVER; - return ntlmssp_state->server_name; - } else { - *chal_flags |= NTLMSSP_TARGET_TYPE_DOMAIN; - return ntlmssp_state->get_domain(); - }; - } else { - return ""; - } -} - -static void ntlmssp_handle_neg_flags(struct ntlmssp_state *ntlmssp_state, - uint32_t neg_flags, BOOL allow_lm) { - if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE) { - ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE; - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_OEM; - ntlmssp_state->unicode = True; - } else { - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_UNICODE; - ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_OEM; - ntlmssp_state->unicode = False; - } - - if ((neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) && allow_lm && !ntlmssp_state->use_ntlmv2) { - /* other end forcing us to use LM */ - ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_LM_KEY; - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2; - } else { - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; - } - - if (neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) { - ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN; - } - - if (!(neg_flags & NTLMSSP_NEGOTIATE_SIGN)) { - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SIGN; - } - - if (!(neg_flags & NTLMSSP_NEGOTIATE_SEAL)) { - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SEAL; - } - - if (!(neg_flags & NTLMSSP_NEGOTIATE_NTLM2)) { - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2; - } - - if (!(neg_flags & NTLMSSP_NEGOTIATE_128)) { - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_128; - if (neg_flags & NTLMSSP_NEGOTIATE_56) { - ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_56; - } - } - - if (!(neg_flags & NTLMSSP_NEGOTIATE_56)) { - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_56; - } - - if (!(neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH)) { - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_KEY_EXCH; - } - - if ((neg_flags & NTLMSSP_REQUEST_TARGET)) { - ntlmssp_state->neg_flags |= NTLMSSP_REQUEST_TARGET; - } - -} - -/** - Weaken NTLMSSP keys to cope with down-level clients and servers. - - We probably should have some parameters to control this, but as - it only occours for LM_KEY connections, and this is controlled - by the client lanman auth/lanman auth parameters, it isn't too bad. -*/ - -static void ntlmssp_weaken_keys(struct ntlmssp_state *ntlmssp_state) { - /* Key weakening not performed on the master key for NTLM2 - and does not occour for NTLM1. Therefore we only need - to do this for the LM_KEY. - */ - - if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) { - if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_128) { - - } else if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_56) { - ntlmssp_state->session_key.data[7] = 0xa0; - } else { /* forty bits */ - ntlmssp_state->session_key.data[5] = 0xe5; - ntlmssp_state->session_key.data[6] = 0x38; - ntlmssp_state->session_key.data[7] = 0xb0; - } - ntlmssp_state->session_key.length = 8; - } -} - -/** - * Next state function for the Negotiate packet - * - * @param ntlmssp_state NTLMSSP State - * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on - * @param in The request, as a DATA_BLOB - * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx - * @return Errors or MORE_PROCESSING_REQUIRED if a reply is sent. - */ - -static NTSTATUS ntlmssp_server_negotiate(struct ntlmssp_state *ntlmssp_state, - TALLOC_CTX *out_mem_ctx, - const DATA_BLOB in, DATA_BLOB *out) -{ - DATA_BLOB struct_blob; - fstring dnsname, dnsdomname; - uint32_t neg_flags = 0; - uint32_t ntlmssp_command, chal_flags; - char *cliname=NULL, *domname=NULL; - const uint8_t *cryptkey; - const char *target_name; - - /* parse the NTLMSSP packet */ -#if 0 - file_save("ntlmssp_negotiate.dat", request.data, request.length); -#endif - - if (in.length) { - if (!msrpc_parse(ntlmssp_state, - &in, "CddAA", - "NTLMSSP", - &ntlmssp_command, - &neg_flags, - &cliname, - &domname)) { - DEBUG(1, ("ntlmssp_server_negotiate: failed to parse NTLMSSP:\n")); - dump_data(2, in.data, in.length); - return NT_STATUS_INVALID_PARAMETER; - } - - debug_ntlmssp_flags(neg_flags); - } - - ntlmssp_handle_neg_flags(ntlmssp_state, neg_flags, ntlmssp_state->allow_lm_key); - - /* Ask our caller what challenge they would like in the packet */ - cryptkey = ntlmssp_state->get_challenge(ntlmssp_state); - - /* Check if we may set the challenge */ - if (!ntlmssp_state->may_set_challenge(ntlmssp_state)) { - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2; - } - - /* The flags we send back are not just the negotiated flags, - * they are also 'what is in this packet'. Therfore, we - * operate on 'chal_flags' from here on - */ - - chal_flags = ntlmssp_state->neg_flags; - - /* get the right name to fill in as 'target' */ - target_name = ntlmssp_target_name(ntlmssp_state, - neg_flags, &chal_flags); - if (target_name == NULL) - return NT_STATUS_INVALID_PARAMETER; - - ntlmssp_state->chal = data_blob_talloc(ntlmssp_state, cryptkey, 8); - ntlmssp_state->internal_chal = data_blob_talloc(ntlmssp_state, cryptkey, 8); - - /* This should be a 'netbios domain -> DNS domain' mapping */ - dnsdomname[0] = '\0'; - get_mydomname(dnsdomname); - strlower_m(dnsdomname); - - dnsname[0] = '\0'; - get_myfullname(dnsname); - - /* This creates the 'blob' of names that appears at the end of the packet */ - if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) - { - const char *target_name_dns = ""; - if (chal_flags |= NTLMSSP_TARGET_TYPE_DOMAIN) { - target_name_dns = dnsdomname; - } else if (chal_flags |= NTLMSSP_TARGET_TYPE_SERVER) { - target_name_dns = dnsname; - } - - msrpc_gen(out_mem_ctx, - &struct_blob, "aaaaa", - NTLMSSP_NAME_TYPE_DOMAIN, target_name, - NTLMSSP_NAME_TYPE_SERVER, ntlmssp_state->server_name, - NTLMSSP_NAME_TYPE_DOMAIN_DNS, dnsdomname, - NTLMSSP_NAME_TYPE_SERVER_DNS, dnsname, - 0, ""); - } else { - struct_blob = data_blob(NULL, 0); - } - - { - /* Marshel the packet in the right format, be it unicode or ASCII */ - const char *gen_string; - if (ntlmssp_state->unicode) { - gen_string = "CdUdbddB"; - } else { - gen_string = "CdAdbddB"; - } - - msrpc_gen(out_mem_ctx, - out, gen_string, - "NTLMSSP", - NTLMSSP_CHALLENGE, - target_name, - chal_flags, - cryptkey, 8, - 0, 0, - struct_blob.data, struct_blob.length); - } - - ntlmssp_state->expected_state = NTLMSSP_AUTH; - - return NT_STATUS_MORE_PROCESSING_REQUIRED; -} - -/** - * Next state function for the Authenticate packet - * - * @param ntlmssp_state NTLMSSP State - * @param request The request, as a DATA_BLOB - * @return Errors or NT_STATUS_OK. - */ - -static NTSTATUS ntlmssp_server_preauth(struct ntlmssp_state *ntlmssp_state, - const DATA_BLOB request) -{ - uint32_t ntlmssp_command, auth_flags; - NTSTATUS nt_status; - - uint8_t session_nonce_hash[16]; - - const char *parse_string; - char *domain = NULL; - char *user = NULL; - char *workstation = NULL; - -#if 0 - file_save("ntlmssp_auth.dat", request.data, request.length); -#endif - - if (ntlmssp_state->unicode) { - parse_string = "CdBBUUUBd"; - } else { - parse_string = "CdBBAAABd"; - } - - /* zero these out */ - data_blob_free(&ntlmssp_state->lm_resp); - data_blob_free(&ntlmssp_state->nt_resp); - - ntlmssp_state->user = NULL; - ntlmssp_state->domain = NULL; - ntlmssp_state->workstation = NULL; - - /* now the NTLMSSP encoded auth hashes */ - if (!msrpc_parse(ntlmssp_state, - &request, parse_string, - "NTLMSSP", - &ntlmssp_command, - &ntlmssp_state->lm_resp, - &ntlmssp_state->nt_resp, - &domain, - &user, - &workstation, - &ntlmssp_state->encrypted_session_key, - &auth_flags)) { - DEBUG(10, ("ntlmssp_server_auth: failed to parse NTLMSSP (nonfatal):\n")); - dump_data(10, request.data, request.length); - - /* zero this out */ - data_blob_free(&ntlmssp_state->encrypted_session_key); - auth_flags = 0; - - /* Try again with a shorter string (Win9X truncates this packet) */ - if (ntlmssp_state->unicode) { - parse_string = "CdBBUUU"; - } else { - parse_string = "CdBBAAA"; - } - - /* now the NTLMSSP encoded auth hashes */ - if (!msrpc_parse(ntlmssp_state, - &request, parse_string, - "NTLMSSP", - &ntlmssp_command, - &ntlmssp_state->lm_resp, - &ntlmssp_state->nt_resp, - &domain, - &user, - &workstation)) { - DEBUG(1, ("ntlmssp_server_auth: failed to parse NTLMSSP:\n")); - dump_data(2, request.data, request.length); - - return NT_STATUS_INVALID_PARAMETER; - } - } - - if (auth_flags) - ntlmssp_handle_neg_flags(ntlmssp_state, auth_flags, ntlmssp_state->allow_lm_key); - - if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, domain))) { - /* zero this out */ - data_blob_free(&ntlmssp_state->encrypted_session_key); - return nt_status; - } - - if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, user))) { - /* zero this out */ - data_blob_free(&ntlmssp_state->encrypted_session_key); - return nt_status; - } - - if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_workstation(ntlmssp_state, workstation))) { - /* zero this out */ - data_blob_free(&ntlmssp_state->encrypted_session_key); - return nt_status; - } - - DEBUG(3,("Got user=[%s] domain=[%s] workstation=[%s] len1=%lu len2=%lu\n", - ntlmssp_state->user, ntlmssp_state->domain, ntlmssp_state->workstation, (unsigned long)ntlmssp_state->lm_resp.length, (unsigned long)ntlmssp_state->nt_resp.length)); - -#if 0 - file_save("nthash1.dat", &ntlmssp_state->nt_resp.data, &ntlmssp_state->nt_resp.length); - file_save("lmhash1.dat", &ntlmssp_state->lm_resp.data, &ntlmssp_state->lm_resp.length); -#endif - - /* NTLM2 uses a 'challenge' that is made of up both the server challenge, and a - client challenge - - However, the NTLM2 flag may still be set for the real NTLMv2 logins, be careful. - */ - if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { - if (ntlmssp_state->nt_resp.length == 24 && ntlmssp_state->lm_resp.length == 24) { - struct MD5Context md5_session_nonce_ctx; - SMB_ASSERT(ntlmssp_state->internal_chal.data - && ntlmssp_state->internal_chal.length == 8); - - ntlmssp_state->doing_ntlm2 = True; - - memcpy(ntlmssp_state->session_nonce, ntlmssp_state->internal_chal.data, 8); - memcpy(&ntlmssp_state->session_nonce[8], ntlmssp_state->lm_resp.data, 8); - - MD5Init(&md5_session_nonce_ctx); - MD5Update(&md5_session_nonce_ctx, ntlmssp_state->session_nonce, 16); - MD5Final(session_nonce_hash, &md5_session_nonce_ctx); - - ntlmssp_state->chal = data_blob_talloc(ntlmssp_state, - session_nonce_hash, 8); - - /* LM response is no longer useful, zero it out */ - data_blob_free(&ntlmssp_state->lm_resp); - - /* We changed the effective challenge - set it */ - if (!NT_STATUS_IS_OK(nt_status = - ntlmssp_state->set_challenge(ntlmssp_state, - &ntlmssp_state->chal))) { - /* zero this out */ - data_blob_free(&ntlmssp_state->encrypted_session_key); - return nt_status; - } - - /* LM Key is incompatible... */ - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; - } - } - return NT_STATUS_OK; -} - -/** - * Next state function for the Authenticate packet - * (after authentication - figures out the session keys etc) - * - * @param ntlmssp_state NTLMSSP State - * @return Errors or NT_STATUS_OK. - */ - -static NTSTATUS ntlmssp_server_postauth(struct ntlmssp_state *ntlmssp_state, - DATA_BLOB *user_session_key, - DATA_BLOB *lm_session_key) -{ - NTSTATUS nt_status; - DATA_BLOB session_key = data_blob(NULL, 0); - - if (user_session_key) - dump_data_pw("USER session key:\n", user_session_key->data, user_session_key->length); - - if (lm_session_key) - dump_data_pw("LM first-8:\n", lm_session_key->data, lm_session_key->length); - - /* Handle the different session key derivation for NTLM2 */ - if (ntlmssp_state->doing_ntlm2) { - if (user_session_key && user_session_key->data && user_session_key->length == 16) { - session_key = data_blob_talloc(ntlmssp_state, NULL, 16); - hmac_md5(user_session_key->data, ntlmssp_state->session_nonce, - sizeof(ntlmssp_state->session_nonce), session_key.data); - DEBUG(10,("ntlmssp_server_auth: Created NTLM2 session key.\n")); - dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length); - - } else { - DEBUG(10,("ntlmssp_server_auth: Failed to create NTLM2 session key.\n")); - session_key = data_blob(NULL, 0); - } - } else if ((ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) - /* Ensure we can never get here on NTLMv2 */ - && (ntlmssp_state->nt_resp.length == 0 || ntlmssp_state->nt_resp.length == 24)) { - - if (lm_session_key && lm_session_key->data && lm_session_key->length >= 8) { - if (ntlmssp_state->lm_resp.data && ntlmssp_state->lm_resp.length == 24) { - session_key = data_blob_talloc(ntlmssp_state, NULL, 16); - SMBsesskeygen_lm_sess_key(lm_session_key->data, ntlmssp_state->lm_resp.data, - session_key.data); - DEBUG(10,("ntlmssp_server_auth: Created NTLM session key.\n")); - dump_data_pw("LM session key:\n", session_key.data, session_key.length); - } else { - - /* When there is no LM response, just use zeros */ - static const uint8_t zeros[24]; - session_key = data_blob_talloc(ntlmssp_state, NULL, 16); - SMBsesskeygen_lm_sess_key(zeros, zeros, - session_key.data); - DEBUG(10,("ntlmssp_server_auth: Created NTLM session key.\n")); - dump_data_pw("LM session key:\n", session_key.data, session_key.length); - } - } else { - /* LM Key not selected */ - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; - - DEBUG(10,("ntlmssp_server_auth: Failed to create NTLM session key.\n")); - session_key = data_blob(NULL, 0); - } - - } else if (user_session_key && user_session_key->data) { - session_key = *user_session_key; - DEBUG(10,("ntlmssp_server_auth: Using unmodified nt session key.\n")); - dump_data_pw("unmodified session key:\n", session_key.data, session_key.length); - - /* LM Key not selected */ - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; - - } else if (lm_session_key && lm_session_key->data) { - /* Very weird to have LM key, but no user session key, but anyway.. */ - session_key = *lm_session_key; - DEBUG(10,("ntlmssp_server_auth: Using unmodified lm session key.\n")); - dump_data_pw("unmodified session key:\n", session_key.data, session_key.length); - - /* LM Key not selected */ - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; - - } else { - DEBUG(10,("ntlmssp_server_auth: Failed to create unmodified session key.\n")); - session_key = data_blob(NULL, 0); - - /* LM Key not selected */ - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; - } - - /* With KEY_EXCH, the client supplies the proposed session key, - but encrypts it with the long-term key */ - if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) { - if (!ntlmssp_state->encrypted_session_key.data - || ntlmssp_state->encrypted_session_key.length != 16) { - data_blob_free(&ntlmssp_state->encrypted_session_key); - DEBUG(1, ("Client-supplied KEY_EXCH session key was of invalid length (%u)!\n", - ntlmssp_state->encrypted_session_key.length)); - return NT_STATUS_INVALID_PARAMETER; - } else if (!session_key.data || session_key.length != 16) { - DEBUG(5, ("server session key is invalid (len == %u), cannot do KEY_EXCH!\n", - session_key.length)); - ntlmssp_state->session_key = session_key; - } else { - dump_data_pw("KEY_EXCH session key (enc):\n", - ntlmssp_state->encrypted_session_key.data, - ntlmssp_state->encrypted_session_key.length); - arcfour_crypt(ntlmssp_state->encrypted_session_key.data, - session_key.data, - ntlmssp_state->encrypted_session_key.length); - ntlmssp_state->session_key = data_blob_talloc(ntlmssp_state, - ntlmssp_state->encrypted_session_key.data, - ntlmssp_state->encrypted_session_key.length); - dump_data_pw("KEY_EXCH session key:\n", ntlmssp_state->encrypted_session_key.data, - ntlmssp_state->encrypted_session_key.length); - } - } else { - ntlmssp_state->session_key = session_key; - } - - /* The server might need us to use a partial-strength session key */ - ntlmssp_weaken_keys(ntlmssp_state); - - nt_status = ntlmssp_sign_init(ntlmssp_state); - - data_blob_free(&ntlmssp_state->encrypted_session_key); - - /* allow arbitarily many authentications, but watch that this will cause a - memory leak, until the ntlmssp_state is shutdown - */ - - if (ntlmssp_state->server_multiple_authentications) { - ntlmssp_state->expected_state = NTLMSSP_AUTH; - } else { - ntlmssp_state->expected_state = NTLMSSP_DONE; - } - - return nt_status; -} - - -/** - * Next state function for the Authenticate packet - * - * @param ntlmssp_state NTLMSSP State - * @param in The packet in from the NTLMSSP partner, as a DATA_BLOB - * @param out The reply, as an allocated DATA_BLOB, caller to free. - * @return Errors, NT_STATUS_MORE_PROCESSING_REQUIRED or NT_STATUS_OK. - */ - -static NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state, - TALLOC_CTX *out_mem_ctx, - const DATA_BLOB in, DATA_BLOB *out) -{ - DATA_BLOB user_session_key = data_blob(NULL, 0); - DATA_BLOB lm_session_key = data_blob(NULL, 0); - NTSTATUS nt_status; - - /* zero the outbound NTLMSSP packet */ - *out = data_blob_talloc(out_mem_ctx, NULL, 0); - - if (!NT_STATUS_IS_OK(nt_status = ntlmssp_server_preauth(ntlmssp_state, in))) { - return nt_status; - } - - /* - * Note we don't check here for NTLMv2 auth settings. If NTLMv2 auth - * is required (by "ntlm auth = no" and "lm auth = no" being set in the - * smb.conf file) and no NTLMv2 response was sent then the password check - * will fail here. JRA. - */ - - /* Finally, actually ask if the password is OK */ - - if (!NT_STATUS_IS_OK(nt_status = ntlmssp_state->check_password(ntlmssp_state, - &user_session_key, &lm_session_key))) { - return nt_status; - } - - if (ntlmssp_state->server_use_session_keys) { - return ntlmssp_server_postauth(ntlmssp_state, &user_session_key, &lm_session_key); - } else { - ntlmssp_state->session_key = data_blob(NULL, 0); - return NT_STATUS_OK; - } -} - -/** - * Create an NTLMSSP state machine - * - * @param ntlmssp_state NTLMSSP State, allocated by this function - */ - -NTSTATUS ntlmssp_server_start(TALLOC_CTX *mem_ctx, struct ntlmssp_state **ntlmssp_state) -{ - *ntlmssp_state = talloc(mem_ctx, struct ntlmssp_state); - if (!*ntlmssp_state) { - DEBUG(0,("ntlmssp_server_start: talloc failed!\n")); - return NT_STATUS_NO_MEMORY; - } - ZERO_STRUCTP(*ntlmssp_state); - - (*ntlmssp_state)->role = NTLMSSP_SERVER; - - (*ntlmssp_state)->get_challenge = get_challenge; - (*ntlmssp_state)->set_challenge = set_challenge; - (*ntlmssp_state)->may_set_challenge = may_set_challenge; - - (*ntlmssp_state)->workstation = NULL; - (*ntlmssp_state)->server_name = lp_netbios_name(); - - (*ntlmssp_state)->get_domain = lp_workgroup; - (*ntlmssp_state)->server_role = ROLE_DOMAIN_MEMBER; /* a good default */ - - (*ntlmssp_state)->expected_state = NTLMSSP_NEGOTIATE; - - (*ntlmssp_state)->allow_lm_key = (lp_lanman_auth() - && lp_parm_bool(-1, "ntlmssp_server", "allow_lm_key", False)); - - (*ntlmssp_state)->server_use_session_keys = True; - (*ntlmssp_state)->server_multiple_authentications = False; - - (*ntlmssp_state)->ref_count = 1; - - (*ntlmssp_state)->neg_flags = - NTLMSSP_NEGOTIATE_NTLM; - - if (lp_parm_bool(-1, "ntlmssp_server", "128bit", True)) { - (*ntlmssp_state)->neg_flags |= NTLMSSP_NEGOTIATE_128; - } - - if (lp_parm_bool(-1, "ntlmssp_server", "keyexchange", True)) { - (*ntlmssp_state)->neg_flags |= NTLMSSP_NEGOTIATE_KEY_EXCH; - } - - if (lp_parm_bool(-1, "ntlmssp_server", "ntlm2", True)) { - (*ntlmssp_state)->neg_flags |= NTLMSSP_NEGOTIATE_NTLM2; - } - - return NT_STATUS_OK; -} - -/********************************************************************* - Client side NTLMSSP -*********************************************************************/ - -/** - * Next state function for the Initial packet - * - * @param ntlmssp_state NTLMSSP State - * @param out_mem_ctx The DATA_BLOB *out will be allocated on this context - * @param in The request, as a DATA_BLOB. reply.data must be NULL - * @param out The reply, as an talloc()ed DATA_BLOB, on out_mem_ctx - * @return Errors or NT_STATUS_OK. - */ - -static NTSTATUS ntlmssp_client_initial(struct ntlmssp_state *ntlmssp_state, - TALLOC_CTX *out_mem_ctx, - DATA_BLOB in, DATA_BLOB *out) -{ - if (ntlmssp_state->unicode) { - ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE; - } else { - ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_OEM; - } - - if (ntlmssp_state->use_ntlmv2) { - ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_NTLM2; - } - - /* generate the ntlmssp negotiate packet */ - msrpc_gen(out_mem_ctx, - out, "CddAA", - "NTLMSSP", - NTLMSSP_NEGOTIATE, - ntlmssp_state->neg_flags, - ntlmssp_state->get_domain(), - ntlmssp_state->workstation); - - ntlmssp_state->expected_state = NTLMSSP_CHALLENGE; - - return NT_STATUS_MORE_PROCESSING_REQUIRED; -} - -/** - * Next state function for the Challenge Packet. Generate an auth packet. - * - * @param ntlmssp_state NTLMSSP State - * @param request The request, as a DATA_BLOB. reply.data must be NULL - * @param request The reply, as an allocated DATA_BLOB, caller to free. - * @return Errors or NT_STATUS_OK. - */ - -static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_state *ntlmssp_state, - TALLOC_CTX *out_mem_ctx, - const DATA_BLOB in, DATA_BLOB *out) -{ - uint32_t chal_flags, ntlmssp_command, unkn1, unkn2; - DATA_BLOB server_domain_blob; - DATA_BLOB challenge_blob; - DATA_BLOB struct_blob = data_blob(NULL, 0); - char *server_domain; - const char *chal_parse_string; - const char *auth_gen_string; - uint8_t lm_hash[16]; - DATA_BLOB lm_response = data_blob(NULL, 0); - DATA_BLOB nt_response = data_blob(NULL, 0); - DATA_BLOB session_key = data_blob(NULL, 0); - DATA_BLOB lm_session_key = data_blob(NULL, 0); - DATA_BLOB encrypted_session_key = data_blob(NULL, 0); - NTSTATUS nt_status; - - if (!msrpc_parse(ntlmssp_state, - &in, "CdBd", - "NTLMSSP", - &ntlmssp_command, - &server_domain_blob, - &chal_flags)) { - DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#1)\n")); - dump_data(2, in.data, in.length); - - return NT_STATUS_INVALID_PARAMETER; - } - - data_blob_free(&server_domain_blob); - - DEBUG(3, ("Got challenge flags:\n")); - debug_ntlmssp_flags(chal_flags); - - ntlmssp_handle_neg_flags(ntlmssp_state, chal_flags, ntlmssp_state->allow_lm_key); - - if (ntlmssp_state->unicode) { - if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) { - chal_parse_string = "CdUdbddB"; - } else { - chal_parse_string = "CdUdbdd"; - } - auth_gen_string = "CdBBUUUBd"; - } else { - if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) { - chal_parse_string = "CdAdbddB"; - } else { - chal_parse_string = "CdAdbdd"; - } - - auth_gen_string = "CdBBAAABd"; - } - - DEBUG(3, ("NTLMSSP: Set final flags:\n")); - debug_ntlmssp_flags(ntlmssp_state->neg_flags); - - if (!msrpc_parse(ntlmssp_state, - &in, chal_parse_string, - "NTLMSSP", - &ntlmssp_command, - &server_domain, - &chal_flags, - &challenge_blob, 8, - &unkn1, &unkn2, - &struct_blob)) { - DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#2)\n")); - dump_data(2, in.data, in.length); - return NT_STATUS_INVALID_PARAMETER; - } - - ntlmssp_state->server_domain = server_domain; - - if (challenge_blob.length != 8) { - return NT_STATUS_INVALID_PARAMETER; - } - - if (!ntlmssp_state->password) { - static const uint8_t zeros[16]; - /* do nothing - blobs are zero length */ - - /* session key is all zeros */ - session_key = data_blob_talloc(ntlmssp_state, zeros, 16); - lm_session_key = data_blob_talloc(ntlmssp_state, zeros, 16); - - /* not doing NLTM2 without a password */ - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2; - } else if (ntlmssp_state->use_ntlmv2) { - - if (!struct_blob.length) { - /* be lazy, match win2k - we can't do NTLMv2 without it */ - DEBUG(1, ("Server did not provide 'target information', required for NTLMv2\n")); - return NT_STATUS_INVALID_PARAMETER; - } - - /* TODO: if the remote server is standalone, then we should replace 'domain' - with the server name as supplied above */ - - if (!SMBNTLMv2encrypt(ntlmssp_state->user, - ntlmssp_state->domain, - ntlmssp_state->password, &challenge_blob, - &struct_blob, - &lm_response, &nt_response, - NULL, &session_key)) { - data_blob_free(&challenge_blob); - data_blob_free(&struct_blob); - return NT_STATUS_NO_MEMORY; - } - - /* LM Key is incompatible... */ - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; - - } else if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { - struct MD5Context md5_session_nonce_ctx; - uint8_t nt_hash[16]; - uint8_t session_nonce[16]; - uint8_t session_nonce_hash[16]; - uint8_t user_session_key[16]; - E_md4hash(ntlmssp_state->password, nt_hash); - - lm_response = data_blob_talloc(ntlmssp_state, NULL, 24); - generate_random_buffer(lm_response.data, 8); - memset(lm_response.data+8, 0, 16); - - memcpy(session_nonce, challenge_blob.data, 8); - memcpy(&session_nonce[8], lm_response.data, 8); - - MD5Init(&md5_session_nonce_ctx); - MD5Update(&md5_session_nonce_ctx, challenge_blob.data, 8); - MD5Update(&md5_session_nonce_ctx, lm_response.data, 8); - MD5Final(session_nonce_hash, &md5_session_nonce_ctx); - - DEBUG(5, ("NTLMSSP challenge set by NTLM2\n")); - DEBUG(5, ("challenge is: \n")); - dump_data(5, session_nonce_hash, 8); - - nt_response = data_blob_talloc(ntlmssp_state, NULL, 24); - SMBNTencrypt(ntlmssp_state->password, - session_nonce_hash, - nt_response.data); - - session_key = data_blob_talloc(ntlmssp_state, NULL, 16); - - SMBsesskeygen_ntv1(nt_hash, user_session_key); - hmac_md5(user_session_key, session_nonce, sizeof(session_nonce), session_key.data); - dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length); - - /* LM Key is incompatible... */ - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; - } else { - uint8_t nt_hash[16]; - - if (ntlmssp_state->use_nt_response) { - nt_response = data_blob_talloc(ntlmssp_state, NULL, 24); - SMBNTencrypt(ntlmssp_state->password,challenge_blob.data, - nt_response.data); - E_md4hash(ntlmssp_state->password, nt_hash); - session_key = data_blob_talloc(ntlmssp_state, NULL, 16); - SMBsesskeygen_ntv1(nt_hash, session_key.data); - dump_data_pw("NT session key:\n", session_key.data, session_key.length); - } - - /* lanman auth is insecure, it may be disabled */ - if (lp_client_lanman_auth()) { - lm_response = data_blob_talloc(ntlmssp_state, NULL, 24); - if (!SMBencrypt(ntlmssp_state->password,challenge_blob.data, - lm_response.data)) { - /* If the LM password was too long (and therefore the LM hash being - of the first 14 chars only), don't send it */ - data_blob_free(&lm_response); - - /* LM Key is incompatible with 'long' passwords */ - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; - } else { - E_deshash(ntlmssp_state->password, lm_hash); - lm_session_key = data_blob_talloc(ntlmssp_state, NULL, 16); - memcpy(lm_session_key.data, lm_hash, 8); - memset(&lm_session_key.data[8], '\0', 8); - - if (!ntlmssp_state->use_nt_response) { - session_key = lm_session_key; - } - } - } else { - /* LM Key is incompatible... */ - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; - } - } - - if ((ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) - && lp_client_lanman_auth() && lm_session_key.length == 16) { - DATA_BLOB new_session_key = data_blob_talloc(ntlmssp_state, NULL, 16); - if (lm_response.length == 24) { - SMBsesskeygen_lm_sess_key(lm_session_key.data, lm_response.data, - new_session_key.data); - } else { - static const uint8_t zeros[24]; - SMBsesskeygen_lm_sess_key(lm_session_key.data, zeros, - new_session_key.data); - } - new_session_key.length = 16; - session_key = new_session_key; - dump_data_pw("LM session key\n", session_key.data, session_key.length); - } - - - /* Key exchange encryptes a new client-generated session key with - the password-derived key */ - if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) { - /* Make up a new session key */ - uint8_t client_session_key[16]; - generate_random_buffer(client_session_key, sizeof(client_session_key)); - - /* Encrypt the new session key with the old one */ - encrypted_session_key = data_blob_talloc(ntlmssp_state, - client_session_key, sizeof(client_session_key)); - dump_data_pw("KEY_EXCH session key:\n", encrypted_session_key.data, encrypted_session_key.length); - arcfour_crypt(encrypted_session_key.data, session_key.data, encrypted_session_key.length); - dump_data_pw("KEY_EXCH session key (enc):\n", encrypted_session_key.data, encrypted_session_key.length); - - /* Mark the new session key as the 'real' session key */ - session_key = data_blob_talloc(ntlmssp_state, client_session_key, sizeof(client_session_key)); - } - - /* this generates the actual auth packet */ - if (!msrpc_gen(out_mem_ctx, - out, auth_gen_string, - "NTLMSSP", - NTLMSSP_AUTH, - lm_response.data, lm_response.length, - nt_response.data, nt_response.length, - ntlmssp_state->domain, - ntlmssp_state->user, - ntlmssp_state->workstation, - encrypted_session_key.data, encrypted_session_key.length, - ntlmssp_state->neg_flags)) { - - return NT_STATUS_NO_MEMORY; - } - - ntlmssp_state->session_key = session_key; - - /* The client might be using 56 or 40 bit weakened keys */ - ntlmssp_weaken_keys(ntlmssp_state); - - ntlmssp_state->chal = challenge_blob; - ntlmssp_state->lm_resp = lm_response; - ntlmssp_state->nt_resp = nt_response; - - ntlmssp_state->expected_state = NTLMSSP_DONE; - - nt_status = ntlmssp_sign_init(ntlmssp_state); - if (!NT_STATUS_IS_OK(nt_status)) { - DEBUG(1, ("Could not setup NTLMSSP signing/sealing system (error was: %s)\n", - nt_errstr(nt_status))); - return nt_status; - } - - return nt_status; -} - -static NTSTATUS ntlmssp_client_start(TALLOC_CTX *mem_ctx, struct ntlmssp_state **ntlmssp_state) -{ - *ntlmssp_state = talloc(mem_ctx, struct ntlmssp_state); - if (!*ntlmssp_state) { - DEBUG(0,("ntlmssp_client_start: talloc failed!\n")); - return NT_STATUS_NO_MEMORY; - } - ZERO_STRUCTP(*ntlmssp_state); - - (*ntlmssp_state)->role = NTLMSSP_CLIENT; - - (*ntlmssp_state)->workstation = lp_netbios_name(); - (*ntlmssp_state)->get_domain = lp_workgroup; - - (*ntlmssp_state)->unicode = lp_parm_bool(-1, "ntlmssp_client", "unicode", True); - - (*ntlmssp_state)->use_nt_response = lp_parm_bool(-1, "ntlmssp_client", "send_nt_reponse", True); - - (*ntlmssp_state)->allow_lm_key = (lp_lanman_auth() - && lp_parm_bool(-1, "ntlmssp_client", "allow_lm_key", False)); - - (*ntlmssp_state)->use_ntlmv2 = lp_client_ntlmv2_auth(); - - (*ntlmssp_state)->expected_state = NTLMSSP_INITIAL; - - (*ntlmssp_state)->ref_count = 1; - - (*ntlmssp_state)->neg_flags = - NTLMSSP_NEGOTIATE_NTLM | - NTLMSSP_REQUEST_TARGET; - - if (lp_parm_bool(-1, "ntlmssp_client", "128bit", True)) { - (*ntlmssp_state)->neg_flags |= NTLMSSP_NEGOTIATE_128; - } - - if (lp_parm_bool(-1, "ntlmssp_client", "keyexchange", True)) { - (*ntlmssp_state)->neg_flags |= NTLMSSP_NEGOTIATE_KEY_EXCH; - } - - if (lp_parm_bool(-1, "ntlmssp_client", "ntlm2", True)) { - (*ntlmssp_state)->neg_flags |= NTLMSSP_NEGOTIATE_NTLM2; - } else { - /* apparently we can't do ntlmv2 if we don't do ntlm2 */ - (*ntlmssp_state)->use_ntlmv2 = False; - } - - return NT_STATUS_OK; -} - -/** - * Return the challenge as determined by the authentication subsystem - * @return an 8 byte random challenge - */ - -static const uint8_t *auth_ntlmssp_get_challenge(const struct ntlmssp_state *ntlmssp_state) -{ - struct gensec_ntlmssp_state *gensec_ntlmssp_state = ntlmssp_state->auth_context; - NTSTATUS status; - const uint8_t *chal; - - status = auth_get_challenge(gensec_ntlmssp_state->auth_context, &chal); - if (!NT_STATUS_IS_OK(status)) { - return NULL; - } - - return chal; -} - -/** - * Some authentication methods 'fix' the challenge, so we may not be able to set it - * - * @return If the effective challenge used by the auth subsystem may be modified - */ -static BOOL auth_ntlmssp_may_set_challenge(const struct ntlmssp_state *ntlmssp_state) -{ - struct gensec_ntlmssp_state *gensec_ntlmssp_state = ntlmssp_state->auth_context; - - return auth_challenge_may_be_modified(gensec_ntlmssp_state->auth_context); -} - -/** - * NTLM2 authentication modifies the effective challenge, - * @param challenge The new challenge value - */ -static NTSTATUS auth_ntlmssp_set_challenge(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *challenge) -{ - NTSTATUS nt_status; - struct gensec_ntlmssp_state *gensec_ntlmssp_state = ntlmssp_state->auth_context; - struct auth_context *auth_context = gensec_ntlmssp_state->auth_context; - const uint8_t *chal; - - if (challenge->length != 8) { - return NT_STATUS_INVALID_PARAMETER; - } - - chal = challenge->data; - - nt_status = auth_context_set_challenge(auth_context, chal, "NTLMSSP callback (NTLM2)"); - - return nt_status; -} - -/** - * Check the password on an NTLMSSP login. - * - * Return the session keys used on the connection. - */ - -static NTSTATUS auth_ntlmssp_check_password(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *user_session_key, DATA_BLOB *lm_session_key) -{ - struct gensec_ntlmssp_state *gensec_ntlmssp_state = ntlmssp_state->auth_context; - struct auth_usersupplied_info *user_info = NULL; - NTSTATUS nt_status; - - nt_status = make_user_info_map(ntlmssp_state, - gensec_ntlmssp_state->ntlmssp_state->user, - gensec_ntlmssp_state->ntlmssp_state->domain, - gensec_ntlmssp_state->ntlmssp_state->workstation, - gensec_ntlmssp_state->ntlmssp_state->lm_resp.data ? &gensec_ntlmssp_state->ntlmssp_state->lm_resp : NULL, - gensec_ntlmssp_state->ntlmssp_state->nt_resp.data ? &gensec_ntlmssp_state->ntlmssp_state->nt_resp : NULL, - NULL, NULL, NULL, True, - &user_info); - NT_STATUS_NOT_OK_RETURN(nt_status); - - nt_status = auth_check_password(gensec_ntlmssp_state->auth_context, gensec_ntlmssp_state, - user_info, &gensec_ntlmssp_state->server_info); - talloc_free(user_info); - NT_STATUS_NOT_OK_RETURN(nt_status); - - if (gensec_ntlmssp_state->server_info->user_session_key.length) { - DEBUG(10, ("Got NT session key of length %u\n", gensec_ntlmssp_state->server_info->user_session_key.length)); - *user_session_key = data_blob_talloc(ntlmssp_state, - gensec_ntlmssp_state->server_info->user_session_key.data, - gensec_ntlmssp_state->server_info->user_session_key.length); - } - if (gensec_ntlmssp_state->server_info->lm_session_key.length) { - DEBUG(10, ("Got LM session key of length %u\n", gensec_ntlmssp_state->server_info->lm_session_key.length)); - *lm_session_key = data_blob_talloc(ntlmssp_state, - gensec_ntlmssp_state->server_info->lm_session_key.data, - gensec_ntlmssp_state->server_info->lm_session_key.length); - } - return nt_status; -} - -static NTSTATUS gensec_ntlmssp_start(struct gensec_security *gensec_security) -{ - struct gensec_ntlmssp_state *gensec_ntlmssp_state; - - gensec_ntlmssp_state = talloc(gensec_security, struct gensec_ntlmssp_state); - if (!gensec_ntlmssp_state) { - return NT_STATUS_NO_MEMORY; - } - - gensec_ntlmssp_state->ntlmssp_state = NULL; - gensec_ntlmssp_state->auth_context = NULL; - gensec_ntlmssp_state->server_info = NULL; - gensec_ntlmssp_state->have_features = 0; - - gensec_security->private_data = gensec_ntlmssp_state; - return NT_STATUS_OK; -} - -static NTSTATUS gensec_ntlmssp_server_start(struct gensec_security *gensec_security) -{ - NTSTATUS nt_status; - struct ntlmssp_state *ntlmssp_state; - struct gensec_ntlmssp_state *gensec_ntlmssp_state; - - nt_status = gensec_ntlmssp_start(gensec_security); - NT_STATUS_NOT_OK_RETURN(nt_status); - - gensec_ntlmssp_state = gensec_security->private_data; - - nt_status = ntlmssp_server_start(gensec_ntlmssp_state, &gensec_ntlmssp_state->ntlmssp_state); - NT_STATUS_NOT_OK_RETURN(nt_status); - - if (gensec_security->want_features & GENSEC_FEATURE_SIGN) { - gensec_ntlmssp_state->ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN; - } - if (gensec_security->want_features & GENSEC_FEATURE_SEAL) { - gensec_ntlmssp_state->ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SEAL; - } - - nt_status = auth_context_create(gensec_ntlmssp_state, lp_auth_methods(), &gensec_ntlmssp_state->auth_context); - NT_STATUS_NOT_OK_RETURN(nt_status); - - ntlmssp_state = gensec_ntlmssp_state->ntlmssp_state; - ntlmssp_state->auth_context = gensec_ntlmssp_state; - ntlmssp_state->get_challenge = auth_ntlmssp_get_challenge; - ntlmssp_state->may_set_challenge = auth_ntlmssp_may_set_challenge; - ntlmssp_state->set_challenge = auth_ntlmssp_set_challenge; - ntlmssp_state->check_password = auth_ntlmssp_check_password; - ntlmssp_state->server_role = lp_server_role(); - - return NT_STATUS_OK; -} - -static NTSTATUS gensec_ntlmssp_client_start(struct gensec_security *gensec_security) -{ - struct gensec_ntlmssp_state *gensec_ntlmssp_state; - const char *password = NULL; - NTSTATUS nt_status; - - nt_status = gensec_ntlmssp_start(gensec_security); - NT_STATUS_NOT_OK_RETURN(nt_status); - - gensec_ntlmssp_state = gensec_security->private_data; - nt_status = ntlmssp_client_start(gensec_ntlmssp_state, - &gensec_ntlmssp_state->ntlmssp_state); - NT_STATUS_NOT_OK_RETURN(nt_status); - - if (gensec_security->want_features & GENSEC_FEATURE_SESSION_KEY) { - /* - * We need to set this to allow a later SetPassword - * via the SAMR pipe to succeed. Strange.... We could - * also add NTLMSSP_NEGOTIATE_SEAL here. JRA. - * - * Without this, Windows will not create the master key - * that it thinks is only used for NTLMSSP signing and - * sealing. (It is actually pulled out and used directly) - */ - gensec_ntlmssp_state->ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN; - } - if (gensec_security->want_features & GENSEC_FEATURE_SIGN) { - gensec_ntlmssp_state->ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN; - } - if (gensec_security->want_features & GENSEC_FEATURE_SEAL) { - gensec_ntlmssp_state->ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SEAL; - } - - nt_status = ntlmssp_set_domain(gensec_ntlmssp_state->ntlmssp_state, - cli_credentials_get_domain(gensec_security->credentials)); - NT_STATUS_NOT_OK_RETURN(nt_status); - - nt_status = ntlmssp_set_username(gensec_ntlmssp_state->ntlmssp_state, - cli_credentials_get_username(gensec_security->credentials)); - NT_STATUS_NOT_OK_RETURN(nt_status); - - password = cli_credentials_get_password(gensec_security->credentials); - - nt_status = ntlmssp_set_password(gensec_ntlmssp_state->ntlmssp_state, password); - NT_STATUS_NOT_OK_RETURN(nt_status); - - nt_status = ntlmssp_set_workstation(gensec_ntlmssp_state->ntlmssp_state, - cli_credentials_get_workstation(gensec_security->credentials)); - - gensec_security->private_data = gensec_ntlmssp_state; - - return NT_STATUS_OK; -} - -/** - * Return the credentials of a logged on user, including session keys - * etc. - * - * Only valid after a successful authentication - * - * May only be called once per authentication. - * - */ - -static NTSTATUS gensec_ntlmssp_session_info(struct gensec_security *gensec_security, - struct auth_session_info **session_info) -{ - NTSTATUS nt_status; - struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; - - nt_status = auth_generate_session_info(gensec_ntlmssp_state, gensec_ntlmssp_state->server_info, session_info); - NT_STATUS_NOT_OK_RETURN(nt_status); - - (*session_info)->session_key = data_blob_talloc(*session_info, - gensec_ntlmssp_state->ntlmssp_state->session_key.data, - gensec_ntlmssp_state->ntlmssp_state->session_key.length); - - return NT_STATUS_OK; -} - -static BOOL gensec_ntlmssp_have_feature(struct gensec_security *gensec_security, - uint32_t feature) -{ - struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; - if (gensec_ntlmssp_state->have_features & feature) { - return True; - } - - return False; -} - -static const struct gensec_security_ops gensec_ntlmssp_security_ops = { - .name = "ntlmssp", - .sasl_name = "NTLM", - .auth_type = DCERPC_AUTH_TYPE_NTLMSSP, - .oid = GENSEC_OID_NTLMSSP, - .client_start = gensec_ntlmssp_client_start, - .server_start = gensec_ntlmssp_server_start, - .update = gensec_ntlmssp_update, - .sig_size = gensec_ntlmssp_sig_size, - .sign_packet = gensec_ntlmssp_sign_packet, - .check_packet = gensec_ntlmssp_check_packet, - .seal_packet = gensec_ntlmssp_seal_packet, - .unseal_packet = gensec_ntlmssp_unseal_packet, - .wrap = gensec_ntlmssp_wrap, - .unwrap = gensec_ntlmssp_unwrap, - .session_key = gensec_ntlmssp_session_key, - .session_info = gensec_ntlmssp_session_info, - .have_feature = gensec_ntlmssp_have_feature, - .enabled = True -}; - - -NTSTATUS gensec_ntlmssp_init(void) -{ - NTSTATUS ret; - ret = gensec_register(&gensec_ntlmssp_security_ops); - if (!NT_STATUS_IS_OK(ret)) { - DEBUG(0,("Failed to register '%s' gensec backend!\n", - gensec_ntlmssp_security_ops.name)); - return ret; - } - - return ret; -} diff --git a/source4/auth/ntlmssp/ntlmssp.c b/source4/auth/ntlmssp/ntlmssp.c new file mode 100644 index 00000000000..d0bd98419e7 --- /dev/null +++ b/source4/auth/ntlmssp/ntlmssp.c @@ -0,0 +1,442 @@ +/* + Unix SMB/Netbios implementation. + Version 3.0 + handle NLTMSSP, client server side parsing + + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Andrew Bartlett 2001-2005 + Copyright (C) Stefan Metzmacher 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "auth/auth.h" +#include "lib/crypto/crypto.h" +#include "auth/ntlmssp/ntlmssp.h" + +/** + * Callbacks for NTLMSSP - for both client and server operating modes + * + */ + +static const struct ntlmssp_callbacks { + enum ntlmssp_role role; + enum ntlmssp_message_type ntlmssp_command; + NTSTATUS (*fn)(struct ntlmssp_state *ntlmssp_state, + TALLOC_CTX *out_mem_ctx, + DATA_BLOB in, DATA_BLOB *out); +} ntlmssp_callbacks[] = { + {NTLMSSP_CLIENT, NTLMSSP_INITIAL, ntlmssp_client_initial}, + {NTLMSSP_SERVER, NTLMSSP_NEGOTIATE, ntlmssp_server_negotiate}, + {NTLMSSP_CLIENT, NTLMSSP_CHALLENGE, ntlmssp_client_challenge}, + {NTLMSSP_SERVER, NTLMSSP_AUTH, ntlmssp_server_auth}, +}; + + +/** + * Print out the NTLMSSP flags for debugging + * @param neg_flags The flags from the packet + */ + +void debug_ntlmssp_flags(uint32_t neg_flags) +{ + DEBUG(3,("Got NTLMSSP neg_flags=0x%08x\n", neg_flags)); + + if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_UNICODE\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_OEM) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_OEM\n")); + if (neg_flags & NTLMSSP_REQUEST_TARGET) + DEBUGADD(4, (" NTLMSSP_REQUEST_TARGET\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_SIGN) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_SIGN\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_SEAL) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_SEAL\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_LM_KEY\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_NETWARE) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NETWARE\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_NTLM) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NTLM\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_ALWAYS_SIGN\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_NTLM2) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NTLM2\n")); + if (neg_flags & NTLMSSP_CHAL_TARGET_INFO) + DEBUGADD(4, (" NTLMSSP_CHAL_TARGET_INFO\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_128) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_128\n")); + if (neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) + DEBUGADD(4, (" NTLMSSP_NEGOTIATE_KEY_EXCH\n")); +} + +/** + * Set a username on an NTLMSSP context - ensures it is talloc()ed + * + */ + +NTSTATUS ntlmssp_set_username(struct ntlmssp_state *ntlmssp_state, const char *user) +{ + if (!user) { + /* it should be at least "" */ + DEBUG(1, ("NTLMSSP failed to set username - cannot accept NULL username\n")); + return NT_STATUS_INVALID_PARAMETER; + } + ntlmssp_state->user = talloc_strdup(ntlmssp_state, user); + if (!ntlmssp_state->user) { + return NT_STATUS_NO_MEMORY; + } + return NT_STATUS_OK; +} + +/** + * Set a password on an NTLMSSP context - ensures it is talloc()ed + * + */ +NTSTATUS ntlmssp_set_password(struct ntlmssp_state *ntlmssp_state, const char *password) +{ + if (!password) { + ntlmssp_state->password = NULL; + } else { + ntlmssp_state->password = talloc_strdup(ntlmssp_state, password); + if (!ntlmssp_state->password) { + return NT_STATUS_NO_MEMORY; + } + } + return NT_STATUS_OK; +} + +/** + * Set a domain on an NTLMSSP context - ensures it is talloc()ed + * + */ +NTSTATUS ntlmssp_set_domain(struct ntlmssp_state *ntlmssp_state, const char *domain) +{ + ntlmssp_state->domain = talloc_strdup(ntlmssp_state, domain); + if (!ntlmssp_state->domain) { + return NT_STATUS_NO_MEMORY; + } + return NT_STATUS_OK; +} + +/** + * Set a workstation on an NTLMSSP context - ensures it is talloc()ed + * + */ +NTSTATUS ntlmssp_set_workstation(struct ntlmssp_state *ntlmssp_state, const char *workstation) +{ + ntlmssp_state->workstation = talloc_strdup(ntlmssp_state, workstation); + if (!ntlmssp_state->workstation) { + return NT_STATUS_NO_MEMORY; + } + return NT_STATUS_OK; +} + +/** + * Store a DATA_BLOB containing an NTLMSSP response, for use later. + * This copies the data blob + */ + +NTSTATUS ntlmssp_store_response(struct ntlmssp_state *ntlmssp_state, + DATA_BLOB response) +{ + ntlmssp_state->stored_response = data_blob_talloc(ntlmssp_state, + response.data, response.length); + return NT_STATUS_OK; +} + +/** + * Next state function for the wrapped NTLMSSP state machine + * + * @param gensec_security GENSEC state, initialised to NTLMSSP + * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on + * @param in The request, as a DATA_BLOB + * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx + * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent, + * or NT_STATUS_OK if the user is authenticated. + */ + +static NTSTATUS gensec_ntlmssp_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, + const DATA_BLOB in, DATA_BLOB *out) +{ + struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; + struct ntlmssp_state *ntlmssp_state = gensec_ntlmssp_state->ntlmssp_state; + NTSTATUS status; + + DATA_BLOB input; + uint32_t ntlmssp_command; + int i; + + *out = data_blob(NULL, 0); + + if (ntlmssp_state->expected_state == NTLMSSP_DONE) { + return NT_STATUS_OK; + } + + if (!out_mem_ctx) { + /* if the caller doesn't want to manage/own the memory, + we can put it on our context */ + out_mem_ctx = ntlmssp_state; + } + + if (!in.length && ntlmssp_state->stored_response.length) { + input = ntlmssp_state->stored_response; + + /* we only want to read the stored response once - overwrite it */ + ntlmssp_state->stored_response = data_blob(NULL, 0); + } else { + input = in; + } + + if (!input.length) { + switch (ntlmssp_state->role) { + case NTLMSSP_CLIENT: + ntlmssp_command = NTLMSSP_INITIAL; + break; + case NTLMSSP_SERVER: + /* 'datagram' mode - no neg packet */ + ntlmssp_command = NTLMSSP_NEGOTIATE; + break; + } + } else { + if (!msrpc_parse(ntlmssp_state, + &input, "Cd", + "NTLMSSP", + &ntlmssp_command)) { + DEBUG(1, ("Failed to parse NTLMSSP packet, could not extract NTLMSSP command\n")); + dump_data(2, input.data, input.length); + return NT_STATUS_INVALID_PARAMETER; + } + } + + if (ntlmssp_command != ntlmssp_state->expected_state) { + DEBUG(1, ("got NTLMSSP command %u, expected %u\n", ntlmssp_command, ntlmssp_state->expected_state)); + return NT_STATUS_INVALID_PARAMETER; + } + + for (i=0; i < ARRAY_SIZE(ntlmssp_callbacks); i++) { + if (ntlmssp_callbacks[i].role == ntlmssp_state->role + && ntlmssp_callbacks[i].ntlmssp_command == ntlmssp_command) { + status = ntlmssp_callbacks[i].fn(ntlmssp_state, out_mem_ctx, input, out); + break; + } + } + + if (i == ARRAY_SIZE(ntlmssp_callbacks)) { + + DEBUG(1, ("failed to find NTLMSSP callback for NTLMSSP mode %u, command %u\n", + ntlmssp_state->role, ntlmssp_command)); + + return NT_STATUS_INVALID_PARAMETER; + } + + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(status)) { + return status; + } + + gensec_ntlmssp_state->have_features = 0; + + if (gensec_ntlmssp_state->ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SIGN) { + gensec_ntlmssp_state->have_features |= GENSEC_FEATURE_SIGN; + } + + if (gensec_ntlmssp_state->ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SEAL) { + gensec_ntlmssp_state->have_features |= GENSEC_FEATURE_SEAL; + } + + if (gensec_ntlmssp_state->ntlmssp_state->session_key.data) { + gensec_ntlmssp_state->have_features |= GENSEC_FEATURE_SESSION_KEY; + } + + return status; +} + +/** + * Return the NTLMSSP master session key + * + * @param ntlmssp_state NTLMSSP State + */ + +NTSTATUS gensec_ntlmssp_session_key(struct gensec_security *gensec_security, + DATA_BLOB *session_key) +{ + struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; + struct ntlmssp_state *ntlmssp_state = gensec_ntlmssp_state->ntlmssp_state; + + if (!ntlmssp_state->session_key.data) { + return NT_STATUS_NO_USER_SESSION_KEY; + } + *session_key = ntlmssp_state->session_key; + + return NT_STATUS_OK; +} + +void ntlmssp_handle_neg_flags(struct ntlmssp_state *ntlmssp_state, + uint32_t neg_flags, BOOL allow_lm) +{ + if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE) { + ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE; + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_OEM; + ntlmssp_state->unicode = True; + } else { + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_UNICODE; + ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_OEM; + ntlmssp_state->unicode = False; + } + + if ((neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) && allow_lm && !ntlmssp_state->use_ntlmv2) { + /* other end forcing us to use LM */ + ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_LM_KEY; + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2; + } else { + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; + } + + if (neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) { + ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN; + } + + if (!(neg_flags & NTLMSSP_NEGOTIATE_SIGN)) { + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SIGN; + } + + if (!(neg_flags & NTLMSSP_NEGOTIATE_SEAL)) { + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SEAL; + } + + if (!(neg_flags & NTLMSSP_NEGOTIATE_NTLM2)) { + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2; + } + + if (!(neg_flags & NTLMSSP_NEGOTIATE_128)) { + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_128; + if (neg_flags & NTLMSSP_NEGOTIATE_56) { + ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_56; + } + } + + if (!(neg_flags & NTLMSSP_NEGOTIATE_56)) { + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_56; + } + + if (!(neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH)) { + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_KEY_EXCH; + } + + if ((neg_flags & NTLMSSP_REQUEST_TARGET)) { + ntlmssp_state->neg_flags |= NTLMSSP_REQUEST_TARGET; + } + +} + +/** + Weaken NTLMSSP keys to cope with down-level clients and servers. + + We probably should have some parameters to control this, but as + it only occours for LM_KEY connections, and this is controlled + by the client lanman auth/lanman auth parameters, it isn't too bad. +*/ + +void ntlmssp_weaken_keys(struct ntlmssp_state *ntlmssp_state) +{ + /* Key weakening not performed on the master key for NTLM2 + and does not occour for NTLM1. Therefore we only need + to do this for the LM_KEY. + */ + + if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) { + if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_128) { + + } else if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_56) { + ntlmssp_state->session_key.data[7] = 0xa0; + } else { /* forty bits */ + ntlmssp_state->session_key.data[5] = 0xe5; + ntlmssp_state->session_key.data[6] = 0x38; + ntlmssp_state->session_key.data[7] = 0xb0; + } + ntlmssp_state->session_key.length = 8; + } +} + +static BOOL gensec_ntlmssp_have_feature(struct gensec_security *gensec_security, + uint32_t feature) +{ + struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; + if (gensec_ntlmssp_state->have_features & feature) { + return True; + } + + return False; +} + +NTSTATUS gensec_ntlmssp_start(struct gensec_security *gensec_security) +{ + struct gensec_ntlmssp_state *gensec_ntlmssp_state; + + gensec_ntlmssp_state = talloc(gensec_security, struct gensec_ntlmssp_state); + if (!gensec_ntlmssp_state) { + return NT_STATUS_NO_MEMORY; + } + + gensec_ntlmssp_state->ntlmssp_state = NULL; + gensec_ntlmssp_state->auth_context = NULL; + gensec_ntlmssp_state->server_info = NULL; + gensec_ntlmssp_state->have_features = 0; + + gensec_security->private_data = gensec_ntlmssp_state; + return NT_STATUS_OK; +} + +static const struct gensec_security_ops gensec_ntlmssp_security_ops = { + .name = "ntlmssp", + .sasl_name = "NTLM", + .auth_type = DCERPC_AUTH_TYPE_NTLMSSP, + .oid = GENSEC_OID_NTLMSSP, + .client_start = gensec_ntlmssp_client_start, + .server_start = gensec_ntlmssp_server_start, + .update = gensec_ntlmssp_update, + .sig_size = gensec_ntlmssp_sig_size, + .sign_packet = gensec_ntlmssp_sign_packet, + .check_packet = gensec_ntlmssp_check_packet, + .seal_packet = gensec_ntlmssp_seal_packet, + .unseal_packet = gensec_ntlmssp_unseal_packet, + .wrap = gensec_ntlmssp_wrap, + .unwrap = gensec_ntlmssp_unwrap, + .session_key = gensec_ntlmssp_session_key, + .session_info = gensec_ntlmssp_session_info, + .have_feature = gensec_ntlmssp_have_feature, + .enabled = True +}; + + +NTSTATUS gensec_ntlmssp_init(void) +{ + NTSTATUS ret; + ret = gensec_register(&gensec_ntlmssp_security_ops); + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(0,("Failed to register '%s' gensec backend!\n", + gensec_ntlmssp_security_ops.name)); + return ret; + } + + return ret; +} diff --git a/source4/auth/gensec/ntlmssp.h b/source4/auth/ntlmssp/ntlmssp.h similarity index 100% rename from source4/auth/gensec/ntlmssp.h rename to source4/auth/ntlmssp/ntlmssp.h diff --git a/source4/auth/ntlmssp/ntlmssp.mk b/source4/auth/ntlmssp/ntlmssp.mk new file mode 100644 index 00000000000..551c211d71f --- /dev/null +++ b/source4/auth/ntlmssp/ntlmssp.mk @@ -0,0 +1,14 @@ +################################################ +# Start MODULE gensec_ntlmssp +[MODULE::gensec_ntlmssp] +SUBSYSTEM = GENSEC +INIT_FUNCTION = gensec_ntlmssp_init +INIT_OBJ_FILES = auth/ntlmssp/ntlmssp.o +ADD_OBJ_FILES = \ + auth/ntlmssp/ntlmssp_parse.o \ + auth/ntlmssp/ntlmssp_sign.o \ + auth/ntlmssp/ntlmssp_client.o \ + auth/ntlmssp/ntlmssp_server.o +REQUIRED_SUBSYSTEMS = AUTH +# End MODULE gensec_ntlmssp +################################################ diff --git a/source4/auth/ntlmssp/ntlmssp_client.c b/source4/auth/ntlmssp/ntlmssp_client.c new file mode 100644 index 00000000000..bbde1d954b0 --- /dev/null +++ b/source4/auth/ntlmssp/ntlmssp_client.c @@ -0,0 +1,445 @@ +/* + Unix SMB/Netbios implementation. + Version 3.0 + handle NLTMSSP, client server side parsing + + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Andrew Bartlett 2001-2005 + Copyright (C) Stefan Metzmacher 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "auth/auth.h" +#include "auth/ntlmssp/ntlmssp.h" +#include "lib/crypto/crypto.h" + +/********************************************************************* + Client side NTLMSSP +*********************************************************************/ + +/** + * Next state function for the Initial packet + * + * @param ntlmssp_state NTLMSSP State + * @param out_mem_ctx The DATA_BLOB *out will be allocated on this context + * @param in The request, as a DATA_BLOB. reply.data must be NULL + * @param out The reply, as an talloc()ed DATA_BLOB, on out_mem_ctx + * @return Errors or NT_STATUS_OK. + */ + +NTSTATUS ntlmssp_client_initial(struct ntlmssp_state *ntlmssp_state, + TALLOC_CTX *out_mem_ctx, + DATA_BLOB in, DATA_BLOB *out) +{ + if (ntlmssp_state->unicode) { + ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE; + } else { + ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_OEM; + } + + if (ntlmssp_state->use_ntlmv2) { + ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_NTLM2; + } + + /* generate the ntlmssp negotiate packet */ + msrpc_gen(out_mem_ctx, + out, "CddAA", + "NTLMSSP", + NTLMSSP_NEGOTIATE, + ntlmssp_state->neg_flags, + ntlmssp_state->get_domain(), + ntlmssp_state->workstation); + + ntlmssp_state->expected_state = NTLMSSP_CHALLENGE; + + return NT_STATUS_MORE_PROCESSING_REQUIRED; +} + +/** + * Next state function for the Challenge Packet. Generate an auth packet. + * + * @param ntlmssp_state NTLMSSP State + * @param request The request, as a DATA_BLOB. reply.data must be NULL + * @param request The reply, as an allocated DATA_BLOB, caller to free. + * @return Errors or NT_STATUS_OK. + */ + +NTSTATUS ntlmssp_client_challenge(struct ntlmssp_state *ntlmssp_state, + TALLOC_CTX *out_mem_ctx, + const DATA_BLOB in, DATA_BLOB *out) +{ + uint32_t chal_flags, ntlmssp_command, unkn1, unkn2; + DATA_BLOB server_domain_blob; + DATA_BLOB challenge_blob; + DATA_BLOB struct_blob = data_blob(NULL, 0); + char *server_domain; + const char *chal_parse_string; + const char *auth_gen_string; + uint8_t lm_hash[16]; + DATA_BLOB lm_response = data_blob(NULL, 0); + DATA_BLOB nt_response = data_blob(NULL, 0); + DATA_BLOB session_key = data_blob(NULL, 0); + DATA_BLOB lm_session_key = data_blob(NULL, 0); + DATA_BLOB encrypted_session_key = data_blob(NULL, 0); + NTSTATUS nt_status; + + if (!msrpc_parse(ntlmssp_state, + &in, "CdBd", + "NTLMSSP", + &ntlmssp_command, + &server_domain_blob, + &chal_flags)) { + DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#1)\n")); + dump_data(2, in.data, in.length); + + return NT_STATUS_INVALID_PARAMETER; + } + + data_blob_free(&server_domain_blob); + + DEBUG(3, ("Got challenge flags:\n")); + debug_ntlmssp_flags(chal_flags); + + ntlmssp_handle_neg_flags(ntlmssp_state, chal_flags, ntlmssp_state->allow_lm_key); + + if (ntlmssp_state->unicode) { + if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) { + chal_parse_string = "CdUdbddB"; + } else { + chal_parse_string = "CdUdbdd"; + } + auth_gen_string = "CdBBUUUBd"; + } else { + if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) { + chal_parse_string = "CdAdbddB"; + } else { + chal_parse_string = "CdAdbdd"; + } + + auth_gen_string = "CdBBAAABd"; + } + + DEBUG(3, ("NTLMSSP: Set final flags:\n")); + debug_ntlmssp_flags(ntlmssp_state->neg_flags); + + if (!msrpc_parse(ntlmssp_state, + &in, chal_parse_string, + "NTLMSSP", + &ntlmssp_command, + &server_domain, + &chal_flags, + &challenge_blob, 8, + &unkn1, &unkn2, + &struct_blob)) { + DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#2)\n")); + dump_data(2, in.data, in.length); + return NT_STATUS_INVALID_PARAMETER; + } + + ntlmssp_state->server_domain = server_domain; + + if (challenge_blob.length != 8) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (!ntlmssp_state->password) { + static const uint8_t zeros[16]; + /* do nothing - blobs are zero length */ + + /* session key is all zeros */ + session_key = data_blob_talloc(ntlmssp_state, zeros, 16); + lm_session_key = data_blob_talloc(ntlmssp_state, zeros, 16); + + /* not doing NLTM2 without a password */ + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2; + } else if (ntlmssp_state->use_ntlmv2) { + + if (!struct_blob.length) { + /* be lazy, match win2k - we can't do NTLMv2 without it */ + DEBUG(1, ("Server did not provide 'target information', required for NTLMv2\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + /* TODO: if the remote server is standalone, then we should replace 'domain' + with the server name as supplied above */ + + if (!SMBNTLMv2encrypt(ntlmssp_state->user, + ntlmssp_state->domain, + ntlmssp_state->password, &challenge_blob, + &struct_blob, + &lm_response, &nt_response, + NULL, &session_key)) { + data_blob_free(&challenge_blob); + data_blob_free(&struct_blob); + return NT_STATUS_NO_MEMORY; + } + + /* LM Key is incompatible... */ + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; + + } else if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { + struct MD5Context md5_session_nonce_ctx; + uint8_t nt_hash[16]; + uint8_t session_nonce[16]; + uint8_t session_nonce_hash[16]; + uint8_t user_session_key[16]; + E_md4hash(ntlmssp_state->password, nt_hash); + + lm_response = data_blob_talloc(ntlmssp_state, NULL, 24); + generate_random_buffer(lm_response.data, 8); + memset(lm_response.data+8, 0, 16); + + memcpy(session_nonce, challenge_blob.data, 8); + memcpy(&session_nonce[8], lm_response.data, 8); + + MD5Init(&md5_session_nonce_ctx); + MD5Update(&md5_session_nonce_ctx, challenge_blob.data, 8); + MD5Update(&md5_session_nonce_ctx, lm_response.data, 8); + MD5Final(session_nonce_hash, &md5_session_nonce_ctx); + + DEBUG(5, ("NTLMSSP challenge set by NTLM2\n")); + DEBUG(5, ("challenge is: \n")); + dump_data(5, session_nonce_hash, 8); + + nt_response = data_blob_talloc(ntlmssp_state, NULL, 24); + SMBNTencrypt(ntlmssp_state->password, + session_nonce_hash, + nt_response.data); + + session_key = data_blob_talloc(ntlmssp_state, NULL, 16); + + SMBsesskeygen_ntv1(nt_hash, user_session_key); + hmac_md5(user_session_key, session_nonce, sizeof(session_nonce), session_key.data); + dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length); + + /* LM Key is incompatible... */ + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; + } else { + uint8_t nt_hash[16]; + + if (ntlmssp_state->use_nt_response) { + nt_response = data_blob_talloc(ntlmssp_state, NULL, 24); + SMBNTencrypt(ntlmssp_state->password,challenge_blob.data, + nt_response.data); + E_md4hash(ntlmssp_state->password, nt_hash); + session_key = data_blob_talloc(ntlmssp_state, NULL, 16); + SMBsesskeygen_ntv1(nt_hash, session_key.data); + dump_data_pw("NT session key:\n", session_key.data, session_key.length); + } + + /* lanman auth is insecure, it may be disabled */ + if (lp_client_lanman_auth()) { + lm_response = data_blob_talloc(ntlmssp_state, NULL, 24); + if (!SMBencrypt(ntlmssp_state->password,challenge_blob.data, + lm_response.data)) { + /* If the LM password was too long (and therefore the LM hash being + of the first 14 chars only), don't send it */ + data_blob_free(&lm_response); + + /* LM Key is incompatible with 'long' passwords */ + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; + } else { + E_deshash(ntlmssp_state->password, lm_hash); + lm_session_key = data_blob_talloc(ntlmssp_state, NULL, 16); + memcpy(lm_session_key.data, lm_hash, 8); + memset(&lm_session_key.data[8], '\0', 8); + + if (!ntlmssp_state->use_nt_response) { + session_key = lm_session_key; + } + } + } else { + /* LM Key is incompatible... */ + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; + } + } + + if ((ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) + && lp_client_lanman_auth() && lm_session_key.length == 16) { + DATA_BLOB new_session_key = data_blob_talloc(ntlmssp_state, NULL, 16); + if (lm_response.length == 24) { + SMBsesskeygen_lm_sess_key(lm_session_key.data, lm_response.data, + new_session_key.data); + } else { + static const uint8_t zeros[24]; + SMBsesskeygen_lm_sess_key(lm_session_key.data, zeros, + new_session_key.data); + } + new_session_key.length = 16; + session_key = new_session_key; + dump_data_pw("LM session key\n", session_key.data, session_key.length); + } + + + /* Key exchange encryptes a new client-generated session key with + the password-derived key */ + if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) { + /* Make up a new session key */ + uint8_t client_session_key[16]; + generate_random_buffer(client_session_key, sizeof(client_session_key)); + + /* Encrypt the new session key with the old one */ + encrypted_session_key = data_blob_talloc(ntlmssp_state, + client_session_key, sizeof(client_session_key)); + dump_data_pw("KEY_EXCH session key:\n", encrypted_session_key.data, encrypted_session_key.length); + arcfour_crypt(encrypted_session_key.data, session_key.data, encrypted_session_key.length); + dump_data_pw("KEY_EXCH session key (enc):\n", encrypted_session_key.data, encrypted_session_key.length); + + /* Mark the new session key as the 'real' session key */ + session_key = data_blob_talloc(ntlmssp_state, client_session_key, sizeof(client_session_key)); + } + + /* this generates the actual auth packet */ + if (!msrpc_gen(out_mem_ctx, + out, auth_gen_string, + "NTLMSSP", + NTLMSSP_AUTH, + lm_response.data, lm_response.length, + nt_response.data, nt_response.length, + ntlmssp_state->domain, + ntlmssp_state->user, + ntlmssp_state->workstation, + encrypted_session_key.data, encrypted_session_key.length, + ntlmssp_state->neg_flags)) { + + return NT_STATUS_NO_MEMORY; + } + + ntlmssp_state->session_key = session_key; + + /* The client might be using 56 or 40 bit weakened keys */ + ntlmssp_weaken_keys(ntlmssp_state); + + ntlmssp_state->chal = challenge_blob; + ntlmssp_state->lm_resp = lm_response; + ntlmssp_state->nt_resp = nt_response; + + ntlmssp_state->expected_state = NTLMSSP_DONE; + + nt_status = ntlmssp_sign_init(ntlmssp_state); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(1, ("Could not setup NTLMSSP signing/sealing system (error was: %s)\n", + nt_errstr(nt_status))); + return nt_status; + } + + return nt_status; +} + +static NTSTATUS ntlmssp_client_start(TALLOC_CTX *mem_ctx, struct ntlmssp_state **ntlmssp_state) +{ + *ntlmssp_state = talloc(mem_ctx, struct ntlmssp_state); + if (!*ntlmssp_state) { + DEBUG(0,("ntlmssp_client_start: talloc failed!\n")); + return NT_STATUS_NO_MEMORY; + } + ZERO_STRUCTP(*ntlmssp_state); + + (*ntlmssp_state)->role = NTLMSSP_CLIENT; + + (*ntlmssp_state)->workstation = lp_netbios_name(); + (*ntlmssp_state)->get_domain = lp_workgroup; + + (*ntlmssp_state)->unicode = lp_parm_bool(-1, "ntlmssp_client", "unicode", True); + + (*ntlmssp_state)->use_nt_response = lp_parm_bool(-1, "ntlmssp_client", "send_nt_reponse", True); + + (*ntlmssp_state)->allow_lm_key = (lp_lanman_auth() + && lp_parm_bool(-1, "ntlmssp_client", "allow_lm_key", False)); + + (*ntlmssp_state)->use_ntlmv2 = lp_client_ntlmv2_auth(); + + (*ntlmssp_state)->expected_state = NTLMSSP_INITIAL; + + (*ntlmssp_state)->ref_count = 1; + + (*ntlmssp_state)->neg_flags = + NTLMSSP_NEGOTIATE_NTLM | + NTLMSSP_REQUEST_TARGET; + + if (lp_parm_bool(-1, "ntlmssp_client", "128bit", True)) { + (*ntlmssp_state)->neg_flags |= NTLMSSP_NEGOTIATE_128; + } + + if (lp_parm_bool(-1, "ntlmssp_client", "keyexchange", True)) { + (*ntlmssp_state)->neg_flags |= NTLMSSP_NEGOTIATE_KEY_EXCH; + } + + if (lp_parm_bool(-1, "ntlmssp_client", "ntlm2", True)) { + (*ntlmssp_state)->neg_flags |= NTLMSSP_NEGOTIATE_NTLM2; + } else { + /* apparently we can't do ntlmv2 if we don't do ntlm2 */ + (*ntlmssp_state)->use_ntlmv2 = False; + } + + return NT_STATUS_OK; +} + +NTSTATUS gensec_ntlmssp_client_start(struct gensec_security *gensec_security) +{ + struct gensec_ntlmssp_state *gensec_ntlmssp_state; + const char *password = NULL; + NTSTATUS nt_status; + + nt_status = gensec_ntlmssp_start(gensec_security); + NT_STATUS_NOT_OK_RETURN(nt_status); + + gensec_ntlmssp_state = gensec_security->private_data; + nt_status = ntlmssp_client_start(gensec_ntlmssp_state, + &gensec_ntlmssp_state->ntlmssp_state); + NT_STATUS_NOT_OK_RETURN(nt_status); + + if (gensec_security->want_features & GENSEC_FEATURE_SESSION_KEY) { + /* + * We need to set this to allow a later SetPassword + * via the SAMR pipe to succeed. Strange.... We could + * also add NTLMSSP_NEGOTIATE_SEAL here. JRA. + * + * Without this, Windows will not create the master key + * that it thinks is only used for NTLMSSP signing and + * sealing. (It is actually pulled out and used directly) + */ + gensec_ntlmssp_state->ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN; + } + if (gensec_security->want_features & GENSEC_FEATURE_SIGN) { + gensec_ntlmssp_state->ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN; + } + if (gensec_security->want_features & GENSEC_FEATURE_SEAL) { + gensec_ntlmssp_state->ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SEAL; + } + + nt_status = ntlmssp_set_domain(gensec_ntlmssp_state->ntlmssp_state, + cli_credentials_get_domain(gensec_security->credentials)); + NT_STATUS_NOT_OK_RETURN(nt_status); + + nt_status = ntlmssp_set_username(gensec_ntlmssp_state->ntlmssp_state, + cli_credentials_get_username(gensec_security->credentials)); + NT_STATUS_NOT_OK_RETURN(nt_status); + + password = cli_credentials_get_password(gensec_security->credentials); + + nt_status = ntlmssp_set_password(gensec_ntlmssp_state->ntlmssp_state, password); + NT_STATUS_NOT_OK_RETURN(nt_status); + + nt_status = ntlmssp_set_workstation(gensec_ntlmssp_state->ntlmssp_state, + cli_credentials_get_workstation(gensec_security->credentials)); + + gensec_security->private_data = gensec_ntlmssp_state; + + return NT_STATUS_OK; +} + diff --git a/source4/auth/gensec/ntlmssp_parse.c b/source4/auth/ntlmssp/ntlmssp_parse.c similarity index 100% rename from source4/auth/gensec/ntlmssp_parse.c rename to source4/auth/ntlmssp/ntlmssp_parse.c diff --git a/source4/auth/ntlmssp/ntlmssp_server.c b/source4/auth/ntlmssp/ntlmssp_server.c new file mode 100644 index 00000000000..d1e97aeb1fd --- /dev/null +++ b/source4/auth/ntlmssp/ntlmssp_server.c @@ -0,0 +1,765 @@ +/* + Unix SMB/Netbios implementation. + Version 3.0 + handle NLTMSSP, client server side parsing + + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Andrew Bartlett 2001-2005 + Copyright (C) Stefan Metzmacher 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "auth/auth.h" +#include "auth/ntlmssp/ntlmssp.h" +#include "lib/crypto/crypto.h" +#include "pstring.h" + +/** + * Default challenge generation code. + * + */ + +static const uint8_t *get_challenge(const struct ntlmssp_state *ntlmssp_state) +{ + uint8_t *chal = talloc_size(ntlmssp_state, 8); + generate_random_buffer(chal, 8); + + return chal; +} + +/** + * Default 'we can set the challenge to anything we like' implementation + * + */ + +static BOOL may_set_challenge(const struct ntlmssp_state *ntlmssp_state) +{ + return True; +} + +/** + * Default 'we can set the challenge to anything we like' implementation + * + * Does not actually do anything, as the value is always in the structure anyway. + * + */ + +static NTSTATUS set_challenge(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *challenge) +{ + SMB_ASSERT(challenge->length == 8); + return NT_STATUS_OK; +} + +/** + * Determine correct target name flags for reply, given server role + * and negotiated flags + * + * @param ntlmssp_state NTLMSSP State + * @param neg_flags The flags from the packet + * @param chal_flags The flags to be set in the reply packet + * @return The 'target name' string. + */ + +static const char *ntlmssp_target_name(struct ntlmssp_state *ntlmssp_state, + uint32_t neg_flags, uint32_t *chal_flags) +{ + if (neg_flags & NTLMSSP_REQUEST_TARGET) { + *chal_flags |= NTLMSSP_CHAL_TARGET_INFO; + *chal_flags |= NTLMSSP_REQUEST_TARGET; + if (ntlmssp_state->server_role == ROLE_STANDALONE) { + *chal_flags |= NTLMSSP_TARGET_TYPE_SERVER; + return ntlmssp_state->server_name; + } else { + *chal_flags |= NTLMSSP_TARGET_TYPE_DOMAIN; + return ntlmssp_state->get_domain(); + }; + } else { + return ""; + } +} + +/** + * Next state function for the Negotiate packet + * + * @param ntlmssp_state NTLMSSP State + * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on + * @param in The request, as a DATA_BLOB + * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx + * @return Errors or MORE_PROCESSING_REQUIRED if a reply is sent. + */ + +NTSTATUS ntlmssp_server_negotiate(struct ntlmssp_state *ntlmssp_state, + TALLOC_CTX *out_mem_ctx, + const DATA_BLOB in, DATA_BLOB *out) +{ + DATA_BLOB struct_blob; + fstring dnsname, dnsdomname; + uint32_t neg_flags = 0; + uint32_t ntlmssp_command, chal_flags; + char *cliname=NULL, *domname=NULL; + const uint8_t *cryptkey; + const char *target_name; + + /* parse the NTLMSSP packet */ +#if 0 + file_save("ntlmssp_negotiate.dat", request.data, request.length); +#endif + + if (in.length) { + if (!msrpc_parse(ntlmssp_state, + &in, "CddAA", + "NTLMSSP", + &ntlmssp_command, + &neg_flags, + &cliname, + &domname)) { + DEBUG(1, ("ntlmssp_server_negotiate: failed to parse NTLMSSP:\n")); + dump_data(2, in.data, in.length); + return NT_STATUS_INVALID_PARAMETER; + } + + debug_ntlmssp_flags(neg_flags); + } + + ntlmssp_handle_neg_flags(ntlmssp_state, neg_flags, ntlmssp_state->allow_lm_key); + + /* Ask our caller what challenge they would like in the packet */ + cryptkey = ntlmssp_state->get_challenge(ntlmssp_state); + + /* Check if we may set the challenge */ + if (!ntlmssp_state->may_set_challenge(ntlmssp_state)) { + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2; + } + + /* The flags we send back are not just the negotiated flags, + * they are also 'what is in this packet'. Therfore, we + * operate on 'chal_flags' from here on + */ + + chal_flags = ntlmssp_state->neg_flags; + + /* get the right name to fill in as 'target' */ + target_name = ntlmssp_target_name(ntlmssp_state, + neg_flags, &chal_flags); + if (target_name == NULL) + return NT_STATUS_INVALID_PARAMETER; + + ntlmssp_state->chal = data_blob_talloc(ntlmssp_state, cryptkey, 8); + ntlmssp_state->internal_chal = data_blob_talloc(ntlmssp_state, cryptkey, 8); + + /* This should be a 'netbios domain -> DNS domain' mapping */ + dnsdomname[0] = '\0'; + get_mydomname(dnsdomname); + strlower_m(dnsdomname); + + dnsname[0] = '\0'; + get_myfullname(dnsname); + + /* This creates the 'blob' of names that appears at the end of the packet */ + if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) + { + const char *target_name_dns = ""; + if (chal_flags |= NTLMSSP_TARGET_TYPE_DOMAIN) { + target_name_dns = dnsdomname; + } else if (chal_flags |= NTLMSSP_TARGET_TYPE_SERVER) { + target_name_dns = dnsname; + } + + msrpc_gen(out_mem_ctx, + &struct_blob, "aaaaa", + NTLMSSP_NAME_TYPE_DOMAIN, target_name, + NTLMSSP_NAME_TYPE_SERVER, ntlmssp_state->server_name, + NTLMSSP_NAME_TYPE_DOMAIN_DNS, dnsdomname, + NTLMSSP_NAME_TYPE_SERVER_DNS, dnsname, + 0, ""); + } else { + struct_blob = data_blob(NULL, 0); + } + + { + /* Marshel the packet in the right format, be it unicode or ASCII */ + const char *gen_string; + if (ntlmssp_state->unicode) { + gen_string = "CdUdbddB"; + } else { + gen_string = "CdAdbddB"; + } + + msrpc_gen(out_mem_ctx, + out, gen_string, + "NTLMSSP", + NTLMSSP_CHALLENGE, + target_name, + chal_flags, + cryptkey, 8, + 0, 0, + struct_blob.data, struct_blob.length); + } + + ntlmssp_state->expected_state = NTLMSSP_AUTH; + + return NT_STATUS_MORE_PROCESSING_REQUIRED; +} + +/** + * Next state function for the Authenticate packet + * + * @param ntlmssp_state NTLMSSP State + * @param request The request, as a DATA_BLOB + * @return Errors or NT_STATUS_OK. + */ + +static NTSTATUS ntlmssp_server_preauth(struct ntlmssp_state *ntlmssp_state, + const DATA_BLOB request) +{ + uint32_t ntlmssp_command, auth_flags; + NTSTATUS nt_status; + + uint8_t session_nonce_hash[16]; + + const char *parse_string; + char *domain = NULL; + char *user = NULL; + char *workstation = NULL; + +#if 0 + file_save("ntlmssp_auth.dat", request.data, request.length); +#endif + + if (ntlmssp_state->unicode) { + parse_string = "CdBBUUUBd"; + } else { + parse_string = "CdBBAAABd"; + } + + /* zero these out */ + data_blob_free(&ntlmssp_state->lm_resp); + data_blob_free(&ntlmssp_state->nt_resp); + + ntlmssp_state->user = NULL; + ntlmssp_state->domain = NULL; + ntlmssp_state->workstation = NULL; + + /* now the NTLMSSP encoded auth hashes */ + if (!msrpc_parse(ntlmssp_state, + &request, parse_string, + "NTLMSSP", + &ntlmssp_command, + &ntlmssp_state->lm_resp, + &ntlmssp_state->nt_resp, + &domain, + &user, + &workstation, + &ntlmssp_state->encrypted_session_key, + &auth_flags)) { + DEBUG(10, ("ntlmssp_server_auth: failed to parse NTLMSSP (nonfatal):\n")); + dump_data(10, request.data, request.length); + + /* zero this out */ + data_blob_free(&ntlmssp_state->encrypted_session_key); + auth_flags = 0; + + /* Try again with a shorter string (Win9X truncates this packet) */ + if (ntlmssp_state->unicode) { + parse_string = "CdBBUUU"; + } else { + parse_string = "CdBBAAA"; + } + + /* now the NTLMSSP encoded auth hashes */ + if (!msrpc_parse(ntlmssp_state, + &request, parse_string, + "NTLMSSP", + &ntlmssp_command, + &ntlmssp_state->lm_resp, + &ntlmssp_state->nt_resp, + &domain, + &user, + &workstation)) { + DEBUG(1, ("ntlmssp_server_auth: failed to parse NTLMSSP:\n")); + dump_data(2, request.data, request.length); + + return NT_STATUS_INVALID_PARAMETER; + } + } + + if (auth_flags) + ntlmssp_handle_neg_flags(ntlmssp_state, auth_flags, ntlmssp_state->allow_lm_key); + + if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, domain))) { + /* zero this out */ + data_blob_free(&ntlmssp_state->encrypted_session_key); + return nt_status; + } + + if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, user))) { + /* zero this out */ + data_blob_free(&ntlmssp_state->encrypted_session_key); + return nt_status; + } + + if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_workstation(ntlmssp_state, workstation))) { + /* zero this out */ + data_blob_free(&ntlmssp_state->encrypted_session_key); + return nt_status; + } + + DEBUG(3,("Got user=[%s] domain=[%s] workstation=[%s] len1=%lu len2=%lu\n", + ntlmssp_state->user, ntlmssp_state->domain, ntlmssp_state->workstation, (unsigned long)ntlmssp_state->lm_resp.length, (unsigned long)ntlmssp_state->nt_resp.length)); + +#if 0 + file_save("nthash1.dat", &ntlmssp_state->nt_resp.data, &ntlmssp_state->nt_resp.length); + file_save("lmhash1.dat", &ntlmssp_state->lm_resp.data, &ntlmssp_state->lm_resp.length); +#endif + + /* NTLM2 uses a 'challenge' that is made of up both the server challenge, and a + client challenge + + However, the NTLM2 flag may still be set for the real NTLMv2 logins, be careful. + */ + if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { + if (ntlmssp_state->nt_resp.length == 24 && ntlmssp_state->lm_resp.length == 24) { + struct MD5Context md5_session_nonce_ctx; + SMB_ASSERT(ntlmssp_state->internal_chal.data + && ntlmssp_state->internal_chal.length == 8); + + ntlmssp_state->doing_ntlm2 = True; + + memcpy(ntlmssp_state->session_nonce, ntlmssp_state->internal_chal.data, 8); + memcpy(&ntlmssp_state->session_nonce[8], ntlmssp_state->lm_resp.data, 8); + + MD5Init(&md5_session_nonce_ctx); + MD5Update(&md5_session_nonce_ctx, ntlmssp_state->session_nonce, 16); + MD5Final(session_nonce_hash, &md5_session_nonce_ctx); + + ntlmssp_state->chal = data_blob_talloc(ntlmssp_state, + session_nonce_hash, 8); + + /* LM response is no longer useful, zero it out */ + data_blob_free(&ntlmssp_state->lm_resp); + + /* We changed the effective challenge - set it */ + if (!NT_STATUS_IS_OK(nt_status = + ntlmssp_state->set_challenge(ntlmssp_state, + &ntlmssp_state->chal))) { + /* zero this out */ + data_blob_free(&ntlmssp_state->encrypted_session_key); + return nt_status; + } + + /* LM Key is incompatible... */ + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; + } + } + return NT_STATUS_OK; +} + +/** + * Next state function for the Authenticate packet + * (after authentication - figures out the session keys etc) + * + * @param ntlmssp_state NTLMSSP State + * @return Errors or NT_STATUS_OK. + */ + +static NTSTATUS ntlmssp_server_postauth(struct ntlmssp_state *ntlmssp_state, + DATA_BLOB *user_session_key, + DATA_BLOB *lm_session_key) +{ + NTSTATUS nt_status; + DATA_BLOB session_key = data_blob(NULL, 0); + + if (user_session_key) + dump_data_pw("USER session key:\n", user_session_key->data, user_session_key->length); + + if (lm_session_key) + dump_data_pw("LM first-8:\n", lm_session_key->data, lm_session_key->length); + + /* Handle the different session key derivation for NTLM2 */ + if (ntlmssp_state->doing_ntlm2) { + if (user_session_key && user_session_key->data && user_session_key->length == 16) { + session_key = data_blob_talloc(ntlmssp_state, NULL, 16); + hmac_md5(user_session_key->data, ntlmssp_state->session_nonce, + sizeof(ntlmssp_state->session_nonce), session_key.data); + DEBUG(10,("ntlmssp_server_auth: Created NTLM2 session key.\n")); + dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length); + + } else { + DEBUG(10,("ntlmssp_server_auth: Failed to create NTLM2 session key.\n")); + session_key = data_blob(NULL, 0); + } + } else if ((ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) + /* Ensure we can never get here on NTLMv2 */ + && (ntlmssp_state->nt_resp.length == 0 || ntlmssp_state->nt_resp.length == 24)) { + + if (lm_session_key && lm_session_key->data && lm_session_key->length >= 8) { + if (ntlmssp_state->lm_resp.data && ntlmssp_state->lm_resp.length == 24) { + session_key = data_blob_talloc(ntlmssp_state, NULL, 16); + SMBsesskeygen_lm_sess_key(lm_session_key->data, ntlmssp_state->lm_resp.data, + session_key.data); + DEBUG(10,("ntlmssp_server_auth: Created NTLM session key.\n")); + dump_data_pw("LM session key:\n", session_key.data, session_key.length); + } else { + + /* When there is no LM response, just use zeros */ + static const uint8_t zeros[24]; + session_key = data_blob_talloc(ntlmssp_state, NULL, 16); + SMBsesskeygen_lm_sess_key(zeros, zeros, + session_key.data); + DEBUG(10,("ntlmssp_server_auth: Created NTLM session key.\n")); + dump_data_pw("LM session key:\n", session_key.data, session_key.length); + } + } else { + /* LM Key not selected */ + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; + + DEBUG(10,("ntlmssp_server_auth: Failed to create NTLM session key.\n")); + session_key = data_blob(NULL, 0); + } + + } else if (user_session_key && user_session_key->data) { + session_key = *user_session_key; + DEBUG(10,("ntlmssp_server_auth: Using unmodified nt session key.\n")); + dump_data_pw("unmodified session key:\n", session_key.data, session_key.length); + + /* LM Key not selected */ + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; + + } else if (lm_session_key && lm_session_key->data) { + /* Very weird to have LM key, but no user session key, but anyway.. */ + session_key = *lm_session_key; + DEBUG(10,("ntlmssp_server_auth: Using unmodified lm session key.\n")); + dump_data_pw("unmodified session key:\n", session_key.data, session_key.length); + + /* LM Key not selected */ + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; + + } else { + DEBUG(10,("ntlmssp_server_auth: Failed to create unmodified session key.\n")); + session_key = data_blob(NULL, 0); + + /* LM Key not selected */ + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; + } + + /* With KEY_EXCH, the client supplies the proposed session key, + but encrypts it with the long-term key */ + if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) { + if (!ntlmssp_state->encrypted_session_key.data + || ntlmssp_state->encrypted_session_key.length != 16) { + data_blob_free(&ntlmssp_state->encrypted_session_key); + DEBUG(1, ("Client-supplied KEY_EXCH session key was of invalid length (%u)!\n", + ntlmssp_state->encrypted_session_key.length)); + return NT_STATUS_INVALID_PARAMETER; + } else if (!session_key.data || session_key.length != 16) { + DEBUG(5, ("server session key is invalid (len == %u), cannot do KEY_EXCH!\n", + session_key.length)); + ntlmssp_state->session_key = session_key; + } else { + dump_data_pw("KEY_EXCH session key (enc):\n", + ntlmssp_state->encrypted_session_key.data, + ntlmssp_state->encrypted_session_key.length); + arcfour_crypt(ntlmssp_state->encrypted_session_key.data, + session_key.data, + ntlmssp_state->encrypted_session_key.length); + ntlmssp_state->session_key = data_blob_talloc(ntlmssp_state, + ntlmssp_state->encrypted_session_key.data, + ntlmssp_state->encrypted_session_key.length); + dump_data_pw("KEY_EXCH session key:\n", ntlmssp_state->encrypted_session_key.data, + ntlmssp_state->encrypted_session_key.length); + } + } else { + ntlmssp_state->session_key = session_key; + } + + /* The server might need us to use a partial-strength session key */ + ntlmssp_weaken_keys(ntlmssp_state); + + nt_status = ntlmssp_sign_init(ntlmssp_state); + + data_blob_free(&ntlmssp_state->encrypted_session_key); + + /* allow arbitarily many authentications, but watch that this will cause a + memory leak, until the ntlmssp_state is shutdown + */ + + if (ntlmssp_state->server_multiple_authentications) { + ntlmssp_state->expected_state = NTLMSSP_AUTH; + } else { + ntlmssp_state->expected_state = NTLMSSP_DONE; + } + + return nt_status; +} + + +/** + * Next state function for the Authenticate packet + * + * @param ntlmssp_state NTLMSSP State + * @param in The packet in from the NTLMSSP partner, as a DATA_BLOB + * @param out The reply, as an allocated DATA_BLOB, caller to free. + * @return Errors, NT_STATUS_MORE_PROCESSING_REQUIRED or NT_STATUS_OK. + */ + +NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state, + TALLOC_CTX *out_mem_ctx, + const DATA_BLOB in, DATA_BLOB *out) +{ + DATA_BLOB user_session_key = data_blob(NULL, 0); + DATA_BLOB lm_session_key = data_blob(NULL, 0); + NTSTATUS nt_status; + + /* zero the outbound NTLMSSP packet */ + *out = data_blob_talloc(out_mem_ctx, NULL, 0); + + if (!NT_STATUS_IS_OK(nt_status = ntlmssp_server_preauth(ntlmssp_state, in))) { + return nt_status; + } + + /* + * Note we don't check here for NTLMv2 auth settings. If NTLMv2 auth + * is required (by "ntlm auth = no" and "lm auth = no" being set in the + * smb.conf file) and no NTLMv2 response was sent then the password check + * will fail here. JRA. + */ + + /* Finally, actually ask if the password is OK */ + + if (!NT_STATUS_IS_OK(nt_status = ntlmssp_state->check_password(ntlmssp_state, + &user_session_key, &lm_session_key))) { + return nt_status; + } + + if (ntlmssp_state->server_use_session_keys) { + return ntlmssp_server_postauth(ntlmssp_state, &user_session_key, &lm_session_key); + } else { + ntlmssp_state->session_key = data_blob(NULL, 0); + return NT_STATUS_OK; + } +} + +/** + * Create an NTLMSSP state machine + * + * @param ntlmssp_state NTLMSSP State, allocated by this function + */ + +static NTSTATUS ntlmssp_server_start(TALLOC_CTX *mem_ctx, struct ntlmssp_state **ntlmssp_state) +{ + *ntlmssp_state = talloc(mem_ctx, struct ntlmssp_state); + if (!*ntlmssp_state) { + DEBUG(0,("ntlmssp_server_start: talloc failed!\n")); + return NT_STATUS_NO_MEMORY; + } + ZERO_STRUCTP(*ntlmssp_state); + + (*ntlmssp_state)->role = NTLMSSP_SERVER; + + (*ntlmssp_state)->get_challenge = get_challenge; + (*ntlmssp_state)->set_challenge = set_challenge; + (*ntlmssp_state)->may_set_challenge = may_set_challenge; + + (*ntlmssp_state)->workstation = NULL; + (*ntlmssp_state)->server_name = lp_netbios_name(); + + (*ntlmssp_state)->get_domain = lp_workgroup; + (*ntlmssp_state)->server_role = ROLE_DOMAIN_MEMBER; /* a good default */ + + (*ntlmssp_state)->expected_state = NTLMSSP_NEGOTIATE; + + (*ntlmssp_state)->allow_lm_key = (lp_lanman_auth() + && lp_parm_bool(-1, "ntlmssp_server", "allow_lm_key", False)); + + (*ntlmssp_state)->server_use_session_keys = True; + (*ntlmssp_state)->server_multiple_authentications = False; + + (*ntlmssp_state)->ref_count = 1; + + (*ntlmssp_state)->neg_flags = + NTLMSSP_NEGOTIATE_NTLM; + + if (lp_parm_bool(-1, "ntlmssp_server", "128bit", True)) { + (*ntlmssp_state)->neg_flags |= NTLMSSP_NEGOTIATE_128; + } + + if (lp_parm_bool(-1, "ntlmssp_server", "keyexchange", True)) { + (*ntlmssp_state)->neg_flags |= NTLMSSP_NEGOTIATE_KEY_EXCH; + } + + if (lp_parm_bool(-1, "ntlmssp_server", "ntlm2", True)) { + (*ntlmssp_state)->neg_flags |= NTLMSSP_NEGOTIATE_NTLM2; + } + + return NT_STATUS_OK; +} + +/** + * Return the challenge as determined by the authentication subsystem + * @return an 8 byte random challenge + */ + +static const uint8_t *auth_ntlmssp_get_challenge(const struct ntlmssp_state *ntlmssp_state) +{ + struct gensec_ntlmssp_state *gensec_ntlmssp_state = ntlmssp_state->auth_context; + NTSTATUS status; + const uint8_t *chal; + + status = auth_get_challenge(gensec_ntlmssp_state->auth_context, &chal); + if (!NT_STATUS_IS_OK(status)) { + return NULL; + } + + return chal; +} + +/** + * Some authentication methods 'fix' the challenge, so we may not be able to set it + * + * @return If the effective challenge used by the auth subsystem may be modified + */ +static BOOL auth_ntlmssp_may_set_challenge(const struct ntlmssp_state *ntlmssp_state) +{ + struct gensec_ntlmssp_state *gensec_ntlmssp_state = ntlmssp_state->auth_context; + + return auth_challenge_may_be_modified(gensec_ntlmssp_state->auth_context); +} + +/** + * NTLM2 authentication modifies the effective challenge, + * @param challenge The new challenge value + */ +static NTSTATUS auth_ntlmssp_set_challenge(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *challenge) +{ + NTSTATUS nt_status; + struct gensec_ntlmssp_state *gensec_ntlmssp_state = ntlmssp_state->auth_context; + struct auth_context *auth_context = gensec_ntlmssp_state->auth_context; + const uint8_t *chal; + + if (challenge->length != 8) { + return NT_STATUS_INVALID_PARAMETER; + } + + chal = challenge->data; + + nt_status = auth_context_set_challenge(auth_context, chal, "NTLMSSP callback (NTLM2)"); + + return nt_status; +} + +/** + * Check the password on an NTLMSSP login. + * + * Return the session keys used on the connection. + */ + +static NTSTATUS auth_ntlmssp_check_password(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *user_session_key, DATA_BLOB *lm_session_key) +{ + struct gensec_ntlmssp_state *gensec_ntlmssp_state = ntlmssp_state->auth_context; + struct auth_usersupplied_info *user_info = NULL; + NTSTATUS nt_status; + + nt_status = make_user_info_map(ntlmssp_state, + gensec_ntlmssp_state->ntlmssp_state->user, + gensec_ntlmssp_state->ntlmssp_state->domain, + gensec_ntlmssp_state->ntlmssp_state->workstation, + gensec_ntlmssp_state->ntlmssp_state->lm_resp.data ? &gensec_ntlmssp_state->ntlmssp_state->lm_resp : NULL, + gensec_ntlmssp_state->ntlmssp_state->nt_resp.data ? &gensec_ntlmssp_state->ntlmssp_state->nt_resp : NULL, + NULL, NULL, NULL, True, + &user_info); + NT_STATUS_NOT_OK_RETURN(nt_status); + + nt_status = auth_check_password(gensec_ntlmssp_state->auth_context, gensec_ntlmssp_state, + user_info, &gensec_ntlmssp_state->server_info); + talloc_free(user_info); + NT_STATUS_NOT_OK_RETURN(nt_status); + + if (gensec_ntlmssp_state->server_info->user_session_key.length) { + DEBUG(10, ("Got NT session key of length %u\n", gensec_ntlmssp_state->server_info->user_session_key.length)); + *user_session_key = data_blob_talloc(ntlmssp_state, + gensec_ntlmssp_state->server_info->user_session_key.data, + gensec_ntlmssp_state->server_info->user_session_key.length); + } + if (gensec_ntlmssp_state->server_info->lm_session_key.length) { + DEBUG(10, ("Got LM session key of length %u\n", gensec_ntlmssp_state->server_info->lm_session_key.length)); + *lm_session_key = data_blob_talloc(ntlmssp_state, + gensec_ntlmssp_state->server_info->lm_session_key.data, + gensec_ntlmssp_state->server_info->lm_session_key.length); + } + return nt_status; +} + +NTSTATUS gensec_ntlmssp_server_start(struct gensec_security *gensec_security) +{ + NTSTATUS nt_status; + struct ntlmssp_state *ntlmssp_state; + struct gensec_ntlmssp_state *gensec_ntlmssp_state; + + nt_status = gensec_ntlmssp_start(gensec_security); + NT_STATUS_NOT_OK_RETURN(nt_status); + + gensec_ntlmssp_state = gensec_security->private_data; + + nt_status = ntlmssp_server_start(gensec_ntlmssp_state, &gensec_ntlmssp_state->ntlmssp_state); + NT_STATUS_NOT_OK_RETURN(nt_status); + + if (gensec_security->want_features & GENSEC_FEATURE_SIGN) { + gensec_ntlmssp_state->ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN; + } + if (gensec_security->want_features & GENSEC_FEATURE_SEAL) { + gensec_ntlmssp_state->ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SEAL; + } + + nt_status = auth_context_create(gensec_ntlmssp_state, lp_auth_methods(), &gensec_ntlmssp_state->auth_context); + NT_STATUS_NOT_OK_RETURN(nt_status); + + ntlmssp_state = gensec_ntlmssp_state->ntlmssp_state; + ntlmssp_state->auth_context = gensec_ntlmssp_state; + ntlmssp_state->get_challenge = auth_ntlmssp_get_challenge; + ntlmssp_state->may_set_challenge = auth_ntlmssp_may_set_challenge; + ntlmssp_state->set_challenge = auth_ntlmssp_set_challenge; + ntlmssp_state->check_password = auth_ntlmssp_check_password; + ntlmssp_state->server_role = lp_server_role(); + + return NT_STATUS_OK; +} + +/** + * Return the credentials of a logged on user, including session keys + * etc. + * + * Only valid after a successful authentication + * + * May only be called once per authentication. + * + */ + +NTSTATUS gensec_ntlmssp_session_info(struct gensec_security *gensec_security, + struct auth_session_info **session_info) +{ + NTSTATUS nt_status; + struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; + + nt_status = auth_generate_session_info(gensec_ntlmssp_state, gensec_ntlmssp_state->server_info, session_info); + NT_STATUS_NOT_OK_RETURN(nt_status); + + (*session_info)->session_key = data_blob_talloc(*session_info, + gensec_ntlmssp_state->ntlmssp_state->session_key.data, + gensec_ntlmssp_state->ntlmssp_state->session_key.length); + + return NT_STATUS_OK; +} diff --git a/source4/auth/gensec/ntlmssp_sign.c b/source4/auth/ntlmssp/ntlmssp_sign.c similarity index 99% rename from source4/auth/gensec/ntlmssp_sign.c rename to source4/auth/ntlmssp/ntlmssp_sign.c index dba3dbee291..222cb3e607f 100644 --- a/source4/auth/gensec/ntlmssp_sign.c +++ b/source4/auth/ntlmssp/ntlmssp_sign.c @@ -22,6 +22,7 @@ #include "includes.h" #include "auth/auth.h" +#include "auth/ntlmssp/ntlmssp.h" #include "lib/crypto/crypto.h" #define CLI_SIGN "session key to client-to-server signing key magic constant" diff --git a/source4/build/smb_build/main.pm b/source4/build/smb_build/main.pm index e42e15b1f83..3598d49f299 100644 --- a/source4/build/smb_build/main.pm +++ b/source4/build/smb_build/main.pm @@ -47,6 +47,7 @@ sub smb_build_main($) "nbt_server/config.mk", "auth/gensec/gensec.mk", "auth/kerberos/kerberos.mk", + "auth/ntlmssp/ntlmssp.mk", "libcli/auth/config.mk", "libcli/ldap/config.mk", "libcli/config.mk", diff --git a/source4/torture/auth/ntlmssp.c b/source4/torture/auth/ntlmssp.c index 6fb07914dd0..a29f99757c0 100644 --- a/source4/torture/auth/ntlmssp.c +++ b/source4/torture/auth/ntlmssp.c @@ -20,6 +20,7 @@ #include "includes.h" #include "auth/auth.h" +#include "auth/ntlmssp/ntlmssp.h" #include "lib/cmdline/popt_common.h" BOOL torture_ntlmssp_self_check(void) -- 2.34.1