diff options
-rw-r--r-- | source4/auth/auth.c | 77 | ||||
-rw-r--r-- | source4/auth/auth.h | 44 | ||||
-rw-r--r-- | source4/auth/auth_builtin.c | 19 | ||||
-rw-r--r-- | source4/auth/auth_compat.c | 17 | ||||
-rw-r--r-- | source4/auth/auth_ntlmssp.c | 47 | ||||
-rw-r--r-- | source4/auth/auth_sam.c | 406 | ||||
-rw-r--r-- | source4/auth/auth_util.c | 238 | ||||
-rw-r--r-- | source4/include/smb.h | 2 | ||||
-rw-r--r-- | source4/libcli/auth/ntlm_check.c | 445 | ||||
-rw-r--r-- | source4/libcli/auth/ntlmssp.c | 246 | ||||
-rw-r--r-- | source4/libcli/auth/ntlmssp.h | 7 | ||||
-rw-r--r-- | source4/libcli/auth/ntlmssp_parse.c | 20 | ||||
-rw-r--r-- | source4/libcli/auth/ntlmssp_sign.c | 67 | ||||
-rw-r--r-- | source4/libcli/config.m4 | 3 | ||||
-rw-r--r-- | source4/libcli/util/smbencrypt.c | 121 | ||||
-rw-r--r-- | source4/smb_server/password.c | 9 | ||||
-rw-r--r-- | source4/smb_server/sesssetup.c | 22 |
17 files changed, 1048 insertions, 742 deletions
diff --git a/source4/auth/auth.c b/source4/auth/auth.c index a41cf72b1f..714bb37c6d 100644 --- a/source4/auth/auth.c +++ b/source4/auth/auth.c @@ -41,6 +41,8 @@ static const uint8 *get_ntlm_challenge(struct auth_context *auth_context) return auth_context->challenge.data; } + auth_context->challenge_may_be_modified = False; + for (auth_method = auth_context->auth_method_list; auth_method; auth_method = auth_method->next) { if (auth_method->get_chal == NULL) { DEBUG(5, ("auth_get_challenge: module %s did not want to specify a challenge\n", auth_method->name)); @@ -80,11 +82,12 @@ static const uint8 *get_ntlm_challenge(struct auth_context *auth_context) chal, sizeof(chal)); challenge_set_by = "random"; + auth_context->challenge_may_be_modified = True; } DEBUG(5, ("auth_context challenge created by %s\n", challenge_set_by)); DEBUG(5, ("challenge is: \n")); - dump_data(5, (const char*)auth_context->challenge.data, auth_context->challenge.length); + dump_data(5, (const char *)auth_context->challenge.data, auth_context->challenge.length); SMB_ASSERT(auth_context->challenge.length == 8); @@ -156,9 +159,8 @@ static NTSTATUS check_ntlm_password(const struct auth_context *auth_context, const struct auth_usersupplied_info *user_info, struct auth_serversupplied_info **server_info) { - - NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE; - const char *pdb_username; + /* if all the modules say 'not for me' this is reasonable */ + NTSTATUS nt_status = NT_STATUS_NO_SUCH_USER; auth_methods *auth_method; TALLOC_CTX *mem_ctx; @@ -181,7 +183,7 @@ static NTSTATUS check_ntlm_password(const struct auth_context *auth_context, auth_context->challenge_set_by)); DEBUG(10, ("challenge is: \n")); - dump_data(5, (const char*)auth_context->challenge.data, auth_context->challenge.length); + dump_data(5, (const char *)auth_context->challenge.data, auth_context->challenge.length); #ifdef DEBUG_PASSWORD DEBUG(100, ("user_info has passwords of length %d and %d\n", @@ -197,12 +199,24 @@ static NTSTATUS check_ntlm_password(const struct auth_context *auth_context, return NT_STATUS_LOGON_FAILURE; for (auth_method = auth_context->auth_method_list;auth_method; auth_method = auth_method->next) { + NTSTATUS result; + mem_ctx = talloc_init("%s authentication for user %s\\%s", auth_method->name, user_info->domain.str, user_info->smb_name.str); - nt_status = auth_method->auth(auth_context, auth_method->private_data, mem_ctx, user_info, server_info); + result = auth_method->auth(auth_context, auth_method->private_data, mem_ctx, user_info, server_info); + + /* check if the module did anything */ + if ( NT_STATUS_V(result) == NT_STATUS_V(NT_STATUS_NOT_IMPLEMENTED) ) { + DEBUG(10,("check_ntlm_password: %s had nothing to say\n", auth_method->name)); + talloc_destroy(mem_ctx); + continue; + } + + nt_status = result; + if (NT_STATUS_IS_OK(nt_status)) { - DEBUG(3, ("check_ntlm_password: %s authentication for user [%s] suceeded\n", + DEBUG(3, ("check_ntlm_password: %s authentication for user [%s] succeeded\n", auth_method->name, user_info->smb_name.str)); } else { DEBUG(5, ("check_ntlm_password: %s authentication for user [%s] FAILED with error %s\n", @@ -211,8 +225,10 @@ static NTSTATUS check_ntlm_password(const struct auth_context *auth_context, talloc_destroy(mem_ctx); - if (NT_STATUS_IS_OK(nt_status)) - break; + if ( NT_STATUS_IS_OK(nt_status)) + { + break; + } } /* This is one of the few places the *relies* (rather than just sets defaults @@ -222,29 +238,12 @@ static NTSTATUS check_ntlm_password(const struct auth_context *auth_context, smb_user_control(user_info, *server_info, nt_status); if (NT_STATUS_IS_OK(nt_status)) { - pdb_username = pdb_get_username((*server_info)->sam_account); - if (!(*server_info)->guest) { - /* We might not be root if we are an RPC call */ - become_root(); - nt_status = smb_pam_accountcheck(pdb_username); - unbecome_root(); - - if (NT_STATUS_IS_OK(nt_status)) { - DEBUG(5, ("check_ntlm_password: PAM Account for user [%s] suceeded\n", - pdb_username)); - } else { - DEBUG(3, ("check_ntlm_password: PAM Account for user [%s] FAILED with error %s\n", - pdb_username, nt_errstr(nt_status))); - } - } - if (NT_STATUS_IS_OK(nt_status)) { DEBUG((*server_info)->guest ? 5 : 2, - ("check_ntlm_password: %sauthentication for user [%s] -> [%s] -> [%s] suceeded\n", + ("check_ntlm_password: %sauthentication for user [%s] -> [%s] succeeded\n", (*server_info)->guest ? "guest " : "", user_info->smb_name.str, - user_info->internal_username.str, - pdb_username)); + user_info->internal_username.str)); } } @@ -263,9 +262,20 @@ static NTSTATUS check_ntlm_password(const struct auth_context *auth_context, static void free_auth_context(struct auth_context **auth_context) { - if (*auth_context != NULL) + auth_methods *auth_method; + + if (*auth_context) { + /* Free private data of context's authentication methods */ + for (auth_method = (*auth_context)->auth_method_list; auth_method; auth_method = auth_method->next) { + if (auth_method->free_private_data) { + auth_method->free_private_data (&auth_method->private_data); + auth_method->private_data = NULL; + } + } + talloc_destroy((*auth_context)->mem_ctx); - *auth_context = NULL; + *auth_context = NULL; + } } /*************************************************************************** @@ -372,7 +382,7 @@ NTSTATUS make_auth_context_subsystem(struct auth_context **auth_context) { case SEC_DOMAIN: DEBUG(5,("Making default auth method list for security=domain\n")); - auth_method_list = str_list_make("guest sam winbind ntdomain", NULL); + auth_method_list = str_list_make("guest sam winbind:ntdomain", NULL); break; case SEC_SERVER: DEBUG(5,("Making default auth method list for security=server\n")); @@ -398,7 +408,7 @@ NTSTATUS make_auth_context_subsystem(struct auth_context **auth_context) break; case SEC_ADS: DEBUG(5,("Making default auth method list for security=ADS\n")); - auth_method_list = str_list_make("guest sam ads winbind ntdomain", NULL); + auth_method_list = str_list_make("guest sam winbind:ntdomain", NULL); break; default: DEBUG(5,("Unknown auth method!\n")); @@ -428,7 +438,7 @@ NTSTATUS make_auth_context_fixed(struct auth_context **auth_context, uchar chal[ return nt_status; } - (*auth_context)->challenge = data_blob(chal, 8); + (*auth_context)->challenge = data_blob_talloc((*auth_context)->mem_ctx, chal, 8); (*auth_context)->challenge_set_by = "fixed"; return nt_status; } @@ -507,7 +517,6 @@ const struct auth_critical_sizes *auth_interface_version(void) sizeof(struct auth_usersupplied_info), sizeof(struct auth_serversupplied_info), sizeof(struct auth_str), - sizeof(struct auth_unistr) }; return &critical_sizes; diff --git a/source4/auth/auth.h b/source4/auth/auth.h index dc12d8196f..22738ffc2c 100644 --- a/source4/auth/auth.h +++ b/source4/auth/auth.h @@ -27,7 +27,8 @@ */ /* version 1 - version from samba 3.0 - metze */ /* version 2 - initial samba4 version - metze */ -#define AUTH_INTERFACE_VERSION 2 +/* version 3 - subsequent samba4 version - abartlet */ +#define AUTH_INTERFACE_VERSION 3 /* AUTH_STR - string */ typedef struct auth_str @@ -36,34 +37,23 @@ typedef struct auth_str char *str; } AUTH_STR; -/* AUTH_UNISTR - unicode string or buffer */ -typedef struct auth_unistr -{ - int len; - uchar *unistr; -} AUTH_UNISTR; - -#define AUTH_FLAG_NONE 0x000000 -#define AUTH_FLAG_PLAINTEXT 0x000001 -#define AUTH_FLAG_LM_RESP 0x000002 -#define AUTH_FLAG_NTLM_RESP 0x000004 -#define AUTH_FLAG_NTLMv2_RESP 0x000008 - typedef struct auth_usersupplied_info { + DATA_BLOB lm_resp; DATA_BLOB nt_resp; + DATA_BLOB lm_interactive_pwd; + DATA_BLOB nt_interactive_pwd; DATA_BLOB plaintext_password; BOOL encrypted; - uint32 auth_flags; - AUTH_STR client_domain; /* domain name string */ AUTH_STR domain; /* domain name after mapping */ AUTH_STR internal_username; /* username after mapping */ AUTH_STR smb_name; /* username before mapping */ AUTH_STR wksta_name; /* workstation name (netbios calling name) unicode string */ + } auth_usersupplied_info; #define SAM_FILL_NAME 0x01 @@ -84,11 +74,9 @@ typedef struct auth_serversupplied_info NT_USER_TOKEN *ptok; - uint8 session_key[16]; - uint8 first_8_lm_hash[8]; - DATA_BLOB nt_session_key; + DATA_BLOB user_session_key; DATA_BLOB lm_session_key; - + uint32 sam_fill_level; /* How far is this structure filled? */ SAM_ACCOUNT *sam_account; @@ -126,7 +114,7 @@ typedef struct auth_methods void *my_private_data, TALLOC_CTX *mem_ctx, const struct auth_usersupplied_info *user_info, - struct auth_serversupplied_info **server_info); + auth_serversupplied_info **server_info); DATA_BLOB (*get_chal)(const struct auth_context *auth_context, void **my_private_data, @@ -140,8 +128,20 @@ typedef struct auth_methods /* Function to send a keepalive message on the above structure */ void (*send_keepalive)(void **private_data); + } auth_methods; +typedef NTSTATUS (*auth_init_function)(struct auth_context *, const char *, struct auth_methods **); + +struct auth_init_function_entry { + const char *name; + /* Function to create a member of the authmethods list */ + + auth_init_function init; + + struct auth_init_function_entry *prev, *next; +}; + typedef struct auth_ntlmssp_state { TALLOC_CTX *mem_ctx; @@ -172,4 +172,4 @@ struct auth_critical_sizes { int sizeof_auth_unistr; }; -#endif /* _SAMBA_AUTH_H */ +#endif /* _SMBAUTH_H_ */ diff --git a/source4/auth/auth_builtin.c b/source4/auth/auth_builtin.c index d82a1ab56b..b5f5a101f4 100644 --- a/source4/auth/auth_builtin.c +++ b/source4/auth/auth_builtin.c @@ -1,6 +1,6 @@ /* Unix SMB/CIFS implementation. - Generic authenticaion types + Generic authentication types Copyright (C) Andrew Bartlett 2001-2002 Copyright (C) Jelmer Vernooij 2002 @@ -38,7 +38,8 @@ static NTSTATUS check_guest_security(const struct auth_context *auth_context, const auth_usersupplied_info *user_info, auth_serversupplied_info **server_info) { - NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE; + /* mark this as 'not for me' */ + NTSTATUS nt_status = NT_STATUS_NOT_IMPLEMENTED; if (!(user_info->internal_username.str && *user_info->internal_username.str)) { @@ -60,6 +61,7 @@ static NTSTATUS auth_init_guest(struct auth_context *auth_context, const char *o return NT_STATUS_OK; } +#ifdef DEVELOPER /** * Return an error based on username * @@ -84,12 +86,12 @@ static NTSTATUS check_name_to_ntstatus_security(const struct auth_context *auth_ long error_num; fstrcpy(user, user_info->smb_name.str); - if (strncasecmp("NT_STATUS", user, strlen("NT_STATUS")) == 0) { - strupper(user); + if (strnequal("NT_STATUS", user, strlen("NT_STATUS"))) { + strupper_m(user); return nt_status_string_to_code(user); } - strlower(user); + strlower_m(user); error_num = strtoul(user, NULL, 16); DEBUG(5,("check_name_to_ntstatus_security: Error for user %s was %lx\n", user, error_num)); @@ -99,7 +101,7 @@ static NTSTATUS check_name_to_ntstatus_security(const struct auth_context *auth_ return nt_status; } -/** Module initailisation function */ +/** Module initialisation function */ static NTSTATUS auth_init_name_to_ntstatus(struct auth_context *auth_context, const char *param, auth_methods **auth_method) { @@ -112,7 +114,7 @@ static NTSTATUS auth_init_name_to_ntstatus(struct auth_context *auth_context, co } /** - * Return a 'fixed' challenge instead of a varaible one. + * Return a 'fixed' challenge instead of a variable one. * * The idea of this function is to make packet snifs consistant * with a fixed challenge, so as to aid debugging. @@ -132,7 +134,7 @@ static NTSTATUS check_fixed_challenge_security(const struct auth_context *auth_c const auth_usersupplied_info *user_info, auth_serversupplied_info **server_info) { - return NT_STATUS_UNSUCCESSFUL; + return NT_STATUS_NOT_IMPLEMENTED; } /**************************************************************************** @@ -160,6 +162,7 @@ static NTSTATUS auth_init_fixed_challenge(struct auth_context *auth_context, con (*auth_method)->name = "fixed_challenge"; return NT_STATUS_OK; } +#endif /* DEVELOPER */ NTSTATUS auth_builtin_init(void) { diff --git a/source4/auth/auth_compat.c b/source4/auth/auth_compat.c index 49cd2e8468..d8c1ad78f0 100644 --- a/source4/auth/auth_compat.c +++ b/source4/auth/auth_compat.c @@ -95,7 +95,6 @@ BOOL password_ok(struct server_context *smb, const char *smb_name, DATA_BLOB pas DATA_BLOB null_password = data_blob(NULL, 0); BOOL encrypted = (smb->negotiate.encrypted_passwords && password_blob.length == 24); - NTSTATUS status; if (encrypted) { /* @@ -103,20 +102,20 @@ BOOL password_ok(struct server_context *smb, const char *smb_name, DATA_BLOB pas * but fall-through as required. * NTLMv2 makes no sense here. */ - status = pass_check_smb(smb, smb_name, lp_workgroup(), null_password, - password_blob, null_password, encrypted); - if (NT_STATUS_IS_OK(status)) { + if (NT_STATUS_IS_OK(pass_check_smb(smb, smb_name, lp_workgroup(), null_password, password_blob, null_password, encrypted))) { return True; } - status = pass_check_smb(smb, smb_name, lp_workgroup(), password_blob, - null_password, null_password, encrypted); + if (NT_STATUS_IS_OK(pass_check_smb(smb, smb_name, lp_workgroup(), password_blob, null_password, null_password, encrypted))) { + return True; + } } else { - status = pass_check_smb(smb, smb_name, lp_workgroup(), null_password, - null_password, password_blob, encrypted); + if (NT_STATUS_IS_OK(pass_check_smb(smb, smb_name, lp_workgroup(), null_password, null_password, password_blob, encrypted))) { + return True; + } } - return NT_STATUS_IS_OK(status); + return False; } diff --git a/source4/auth/auth_ntlmssp.c b/source4/auth/auth_ntlmssp.c index 940630b4c6..a4e0dd5588 100644 --- a/source4/auth/auth_ntlmssp.c +++ b/source4/auth/auth_ntlmssp.c @@ -75,42 +75,33 @@ static NTSTATUS auth_ntlmssp_set_challenge(struct ntlmssp_state *ntlmssp_state, * Return the session keys used on the connection. */ -static NTSTATUS auth_ntlmssp_check_password(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *nt_session_key, DATA_BLOB *lm_session_key) +static NTSTATUS auth_ntlmssp_check_password(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *user_session_key, DATA_BLOB *lm_session_key) { AUTH_NTLMSSP_STATE *auth_ntlmssp_state = ntlmssp_state->auth_context; - uint32 auth_flags = AUTH_FLAG_NONE; auth_usersupplied_info *user_info = NULL; - DATA_BLOB plaintext_password = data_blob(NULL, 0); NTSTATUS nt_status; - if (auth_ntlmssp_state->ntlmssp_state->lm_resp.length) { - auth_flags |= AUTH_FLAG_LM_RESP; - } - - if (auth_ntlmssp_state->ntlmssp_state->nt_resp.length == 24) { - auth_flags |= AUTH_FLAG_NTLM_RESP; - } else if (auth_ntlmssp_state->ntlmssp_state->nt_resp.length > 24) { - auth_flags |= AUTH_FLAG_NTLMv2_RESP; - } - #if 0 /* the client has given us its machine name (which we otherwise would not get on port 445). we need to possibly reload smb.conf if smb.conf includes depend on the machine name */ + set_remote_machine_name(auth_ntlmssp_state->ntlmssp_state->workstation, True); + /* setup the string used by %U */ /* sub_set_smb_name checks for weird internally */ sub_set_smb_name(auth_ntlmssp_state->ntlmssp_state->user); + reload_services(True); -#endif +#endif nt_status = make_user_info_map(&user_info, auth_ntlmssp_state->ntlmssp_state->user, auth_ntlmssp_state->ntlmssp_state->domain, auth_ntlmssp_state->ntlmssp_state->workstation, - auth_ntlmssp_state->ntlmssp_state->lm_resp, - auth_ntlmssp_state->ntlmssp_state->nt_resp, - plaintext_password, - auth_flags, True); + auth_ntlmssp_state->ntlmssp_state->lm_resp.data ? &auth_ntlmssp_state->ntlmssp_state->lm_resp : NULL, + auth_ntlmssp_state->ntlmssp_state->nt_resp.data ? &auth_ntlmssp_state->ntlmssp_state->nt_resp : NULL, + NULL, NULL, NULL, + True); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; @@ -124,19 +115,17 @@ static NTSTATUS auth_ntlmssp_check_password(struct ntlmssp_state *ntlmssp_state, if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; } - if (auth_ntlmssp_state->server_info->nt_session_key.length) { - DEBUG(5, ("Got NT session key of length %u\n", auth_ntlmssp_state->server_info->nt_session_key.length)); - *nt_session_key = data_blob_talloc(auth_ntlmssp_state->mem_ctx, - auth_ntlmssp_state->server_info->nt_session_key.data, - auth_ntlmssp_state->server_info->nt_session_key.length); - } else if (auth_ntlmssp_state->server_info->lm_session_key.length) { - DEBUG(5, ("Got LM session key of length %u\n", auth_ntlmssp_state->server_info->lm_session_key.length)); - *lm_session_key = data_blob_talloc(auth_ntlmssp_state->mem_ctx, + if (auth_ntlmssp_state->server_info->user_session_key.length) { + DEBUG(10, ("Got NT session key of length %u\n", auth_ntlmssp_state->server_info->user_session_key.length)); + *user_session_key = data_blob_talloc(ntlmssp_state->mem_ctx, + auth_ntlmssp_state->server_info->user_session_key.data, + auth_ntlmssp_state->server_info->user_session_key.length); + } + if (auth_ntlmssp_state->server_info->lm_session_key.length) { + DEBUG(10, ("Got LM session key of length %u\n", auth_ntlmssp_state->server_info->lm_session_key.length)); + *lm_session_key = data_blob_talloc(ntlmssp_state->mem_ctx, auth_ntlmssp_state->server_info->lm_session_key.data, auth_ntlmssp_state->server_info->lm_session_key.length); - } else { - *nt_session_key = data_blob_talloc(auth_ntlmssp_state->mem_ctx, - auth_ntlmssp_state->server_info->session_key, 16); } return nt_status; } diff --git a/source4/auth/auth_sam.c b/source4/auth/auth_sam.c index 35635ea474..028fda74fe 100644 --- a/source4/auth/auth_sam.c +++ b/source4/auth/auth_sam.c @@ -3,7 +3,8 @@ Password and authentication handling Copyright (C) Andrew Tridgell 1992-2000 Copyright (C) Luke Kenneth Casson Leighton 1996-2000 - Copyright (C) Andrew Bartlett 2001 + Copyright (C) Andrew Bartlett 2001-2003 + Copyright (C) Gerald Carter 2003 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -26,290 +27,50 @@ #define DBGC_CLASS DBGC_AUTH /**************************************************************************** -core of smb password checking routine. -****************************************************************************/ -static BOOL smb_pwd_check_ntlmv1(DATA_BLOB nt_response, - const uchar *part_passwd, - DATA_BLOB sec_blob, - uint8 user_sess_key[16]) -{ - /* Finish the encryption of part_passwd. */ - uchar p24[24]; - - if (part_passwd == NULL) { - DEBUG(10,("No password set - DISALLOWING access\n")); - /* No password set - always false ! */ - return False; - } - - if (sec_blob.length != 8) { - DEBUG(0, ("smb_pwd_check_ntlmv1: incorrect challenge size (%d)\n", sec_blob.length)); - return False; - } - - if (nt_response.length != 24) { - DEBUG(0, ("smb_pwd_check_ntlmv1: incorrect password length (%d)\n", nt_response.length)); - return False; - } - - SMBOWFencrypt(part_passwd, sec_blob.data, p24); - if (user_sess_key != NULL) - { - SMBsesskeygen_ntv1(part_passwd, NULL, user_sess_key); - } - - - -#if DEBUG_PASSWORD - DEBUG(100,("Part password (P16) was |")); - dump_data(100, part_passwd, 16); - DEBUG(100,("Password from client was |")); - dump_data(100, nt_response.data, nt_response.length); - DEBUG(100,("Given challenge was |")); - dump_data(100, sec_blob.data, sec_blob.length); - DEBUG(100,("Value from encryption was |")); - dump_data(100, p24, 24); -#endif - return (memcmp(p24, nt_response.data, 24) == 0); -} - - -/**************************************************************************** -core of smb password checking routine. (NTLMv2, LMv2) - -Note: The same code works with both NTLMv2 and LMv2. -****************************************************************************/ -static BOOL smb_pwd_check_ntlmv2(const DATA_BLOB ntv2_response, - const uchar *part_passwd, - const DATA_BLOB sec_blob, - const char *user, const char *domain, - uint8 user_sess_key[16]) -{ - /* Finish the encryption of part_passwd. */ - uchar kr[16]; - uchar value_from_encryption[16]; - uchar client_response[16]; - DATA_BLOB client_key_data; - - if (part_passwd == NULL) - { - DEBUG(10,("No password set - DISALLOWING access\n")); - /* No password set - always False */ - return False; - } - - if (ntv2_response.length < 16) { - /* We MUST have more than 16 bytes, or the stuff below will go - crazy... */ - DEBUG(0, ("smb_pwd_check_ntlmv2: incorrect password length (%d)\n", - ntv2_response.length)); - return False; - } - - client_key_data = data_blob(ntv2_response.data+16, ntv2_response.length-16); - /* - todo: should we be checking this for anything? We can't for LMv2, - but for NTLMv2 it is meant to contain the current time etc. - */ - - memcpy(client_response, ntv2_response.data, sizeof(client_response)); - - if (!ntv2_owf_gen(part_passwd, user, domain, kr)) { - return False; - } - - SMBOWFencrypt_ntv2(kr, &sec_blob, &client_key_data, value_from_encryption); - if (user_sess_key != NULL) - { - SMBsesskeygen_ntv2(kr, value_from_encryption, user_sess_key); - } - -#if DEBUG_PASSWORD - DEBUG(100,("Part password (P16) was |")); - dump_data(100, part_passwd, 16); - DEBUG(100,("Password from client was |")); - dump_data(100, ntv2_response.data, ntv2_response.length); - DEBUG(100,("Variable data from client was |")); - dump_data(100, client_key_data.data, client_key_data.length); - DEBUG(100,("Given challenge was |")); - dump_data(100, sec_blob.data, sec_blob.length); - DEBUG(100,("Value from encryption was |")); - dump_data(100, value_from_encryption, 16); -#endif - data_blob_clear_free(&client_key_data); - return (memcmp(value_from_encryption, client_response, 16) == 0); -} - - -/**************************************************************************** Do a specific test for an smb password being correct, given a smb_password and the lanman and NT responses. ****************************************************************************/ + static NTSTATUS sam_password_ok(const struct auth_context *auth_context, TALLOC_CTX *mem_ctx, SAM_ACCOUNT *sampass, const auth_usersupplied_info *user_info, - uint8 user_sess_key[16]) + DATA_BLOB *user_sess_key, + DATA_BLOB *lm_sess_key) { uint16 acct_ctrl; - const uint8 *nt_pw, *lm_pw; - uint32 auth_flags; + const uint8 *lm_pw, *nt_pw; + const char *username = pdb_get_username(sampass); acct_ctrl = pdb_get_acct_ctrl(sampass); - if (acct_ctrl & ACB_PWNOTREQ) - { - if (lp_null_passwords()) - { - DEBUG(3,("Account for user '%s' has no password and null passwords are allowed.\n", pdb_get_username(sampass))); - return(NT_STATUS_OK); - } - else - { - DEBUG(3,("Account for user '%s' has no password and null passwords are NOT allowed.\n", pdb_get_username(sampass))); - return(NT_STATUS_LOGON_FAILURE); - } - } - - auth_flags = user_info->auth_flags; - - if (IS_SAM_DEFAULT(sampass, PDB_NTPASSWD)) { - DEBUG(3,("sam_password_ok: NO NT password stored for user %s.\n", - pdb_get_username(sampass))); - /* No return, we want to check the LM hash below in this case */ - auth_flags &= (~(AUTH_FLAG_NTLMv2_RESP | AUTH_FLAG_NTLM_RESP)); - } - - if (auth_flags & AUTH_FLAG_NTLMv2_RESP) { - nt_pw = pdb_get_nt_passwd(sampass); - /* We have the NT MD4 hash challenge available - see if we can - use it (ie. does it exist in the smbpasswd file). - */ - DEBUG(4,("sam_password_ok: Checking NTLMv2 password with domain [%s]\n", user_info->client_domain.str)); - if (smb_pwd_check_ntlmv2( user_info->nt_resp, - nt_pw, auth_context->challenge, - user_info->smb_name.str, - user_info->client_domain.str, - user_sess_key)) - { - return NT_STATUS_OK; - } - - DEBUG(4,("sam_password_ok: Checking NTLMv2 password without a domain\n")); - if (smb_pwd_check_ntlmv2( user_info->nt_resp, - nt_pw, auth_context->challenge, - user_info->smb_name.str, - "", - user_sess_key)) - { + if (acct_ctrl & ACB_PWNOTREQ) { + if (lp_null_passwords()) { + DEBUG(3,("Account for user '%s' has no password and null passwords are allowed.\n", username)); return NT_STATUS_OK; } else { - DEBUG(3,("sam_password_ok: NTLMv2 password check failed\n")); - return NT_STATUS_WRONG_PASSWORD; - } - } else if (auth_flags & AUTH_FLAG_NTLM_RESP) { - if (lp_ntlm_auth()) { - nt_pw = pdb_get_nt_passwd(sampass); - /* We have the NT MD4 hash challenge available - see if we can - use it (ie. does it exist in the smbpasswd file). - */ - DEBUG(4,("sam_password_ok: Checking NT MD4 password\n")); - if (smb_pwd_check_ntlmv1(user_info->nt_resp, - nt_pw, auth_context->challenge, - user_sess_key)) - { - return NT_STATUS_OK; - } else { - DEBUG(3,("sam_password_ok: NT MD4 password check failed for user %s\n",pdb_get_username(sampass))); - return NT_STATUS_WRONG_PASSWORD; - } - } else { - DEBUG(2,("sam_password_ok: NTLMv1 passwords NOT PERMITTED for user %s\n",pdb_get_username(sampass))); - /* no return, becouse we might pick up LMv2 in the LM feild */ - } + DEBUG(3,("Account for user '%s' has no password and null passwords are NOT allowed.\n", username)); + return NT_STATUS_LOGON_FAILURE; + } } - - if (auth_flags & AUTH_FLAG_LM_RESP) { - if (user_info->lm_resp.length != 24) { - DEBUG(2,("sam_password_ok: invalid LanMan password length (%d) for user %s\n", - user_info->nt_resp.length, pdb_get_username(sampass))); - } - - if (!lp_lanman_auth()) { - DEBUG(3,("sam_password_ok: Lanman passwords NOT PERMITTED for user %s\n",pdb_get_username(sampass))); - } else if (IS_SAM_DEFAULT(sampass, PDB_LMPASSWD)) { - DEBUG(3,("sam_password_ok: NO LanMan password set for user %s (and no NT password supplied)\n",pdb_get_username(sampass))); - } else { - lm_pw = pdb_get_lanman_passwd(sampass); - - DEBUG(4,("sam_password_ok: Checking LM password\n")); - if (smb_pwd_check_ntlmv1(user_info->lm_resp, - lm_pw, auth_context->challenge, - user_sess_key)) - { - return NT_STATUS_OK; - } - } - if (IS_SAM_DEFAULT(sampass, PDB_NTPASSWD)) { - DEBUG(4,("sam_password_ok: LM password check failed for user, no NT password %s\n",pdb_get_username(sampass))); - return NT_STATUS_WRONG_PASSWORD; - } - - nt_pw = pdb_get_nt_passwd(sampass); - - /* This is for 'LMv2' authentication. almost NTLMv2 but limited to 24 bytes. - - related to Win9X, legacy NAS pass-though authentication - */ - DEBUG(4,("sam_password_ok: Checking LMv2 password with domain %s\n", user_info->client_domain.str)); - if (smb_pwd_check_ntlmv2( user_info->lm_resp, - nt_pw, auth_context->challenge, - user_info->smb_name.str, - user_info->client_domain.str, - user_sess_key)) - { - return NT_STATUS_OK; - } - - DEBUG(4,("sam_password_ok: Checking LMv2 password without a domain\n")); - if (smb_pwd_check_ntlmv2( user_info->lm_resp, - nt_pw, auth_context->challenge, - user_info->smb_name.str, - "", - user_sess_key)) - { - return NT_STATUS_OK; - } + lm_pw = pdb_get_lanman_passwd(sampass); + nt_pw = pdb_get_nt_passwd(sampass); - /* Apparently NT accepts NT responses in the LM field - - I think this is related to Win9X pass-though authentication - */ - DEBUG(4,("sam_password_ok: Checking NT MD4 password in LM field\n")); - if (lp_ntlm_auth()) - { - if (smb_pwd_check_ntlmv1(user_info->lm_resp, - nt_pw, auth_context->challenge, - user_sess_key)) - { - return NT_STATUS_OK; - } - DEBUG(3,("sam_password_ok: LM password, NT MD4 password in LM field and LMv2 failed for user %s\n",pdb_get_username(sampass))); - return NT_STATUS_WRONG_PASSWORD; - } else { - DEBUG(3,("sam_password_ok: LM password and LMv2 failed for user %s, and NT MD4 password in LM field not permitted\n",pdb_get_username(sampass))); - return NT_STATUS_WRONG_PASSWORD; - } - - } - - /* Should not be reached, but if they send nothing... */ - DEBUG(3,("sam_password_ok: NEITHER LanMan nor NT password supplied for user %s\n",pdb_get_username(sampass))); - return NT_STATUS_WRONG_PASSWORD; + return ntlm_password_check(mem_ctx, &auth_context->challenge, + &user_info->lm_resp, &user_info->nt_resp, + &user_info->lm_interactive_pwd, &user_info->nt_interactive_pwd, + username, + user_info->smb_name.str, + user_info->client_domain.str, + lm_pw, nt_pw, user_sess_key, lm_sess_key); } + /**************************************************************************** Do a specific test for a SAM_ACCOUNT being vaild for this connection (ie not disabled, expired and the like). ****************************************************************************/ + static NTSTATUS sam_account_ok(TALLOC_CTX *mem_ctx, SAM_ACCOUNT *sampass, const auth_usersupplied_info *user_info) @@ -322,16 +83,22 @@ static NTSTATUS sam_account_ok(TALLOC_CTX *mem_ctx, /* Quit if the account was disabled. */ if (acct_ctrl & ACB_DISABLED) { - DEBUG(1,("Account for user '%s' was disabled.\n", pdb_get_username(sampass))); + DEBUG(1,("sam_account_ok: Account for user '%s' was disabled.\n", pdb_get_username(sampass))); return NT_STATUS_ACCOUNT_DISABLED; } + /* Quit if the account was locked out. */ + if (acct_ctrl & ACB_AUTOLOCK) { + DEBUG(1,("sam_account_ok: Account for user %s was locked out.\n", pdb_get_username(sampass))); + return NT_STATUS_ACCOUNT_LOCKED_OUT; + } + /* Test account expire time */ kickoff_time = pdb_get_kickoff_time(sampass); if (kickoff_time != 0 && time(NULL) > kickoff_time) { - DEBUG(1,("Account for user '%s' has expried.\n", pdb_get_username(sampass))); - DEBUG(3,("Account expired at '%ld' unix time.\n", (long)kickoff_time)); + DEBUG(1,("sam_account_ok: Account for user '%s' has expired.\n", pdb_get_username(sampass))); + DEBUG(3,("sam_account_ok: Account expired at '%ld' unix time.\n", (long)kickoff_time)); return NT_STATUS_ACCOUNT_EXPIRED; } @@ -341,14 +108,14 @@ static NTSTATUS sam_account_ok(TALLOC_CTX *mem_ctx, /* check for immediate expiry "must change at next logon" */ if (must_change_time == 0 && last_set_time != 0) { - DEBUG(1,("Account for user '%s' password must change!.\n", pdb_get_username(sampass))); + DEBUG(1,("sam_account_ok: Account for user '%s' password must change!.\n", pdb_get_username(sampass))); return NT_STATUS_PASSWORD_MUST_CHANGE; } /* check for expired password */ if (must_change_time < time(NULL) && must_change_time != 0) { - DEBUG(1,("Account for user '%s' password expired!.\n", pdb_get_username(sampass))); - DEBUG(1,("Password expired at '%s' (%ld) unix time.\n", timestring(mem_ctx, must_change_time), (long)must_change_time)); + DEBUG(1,("sam_account_ok: Account for user '%s' password expired!.\n", pdb_get_username(sampass))); + DEBUG(1,("sam_account_ok: Password expired at '%s' (%ld) unix time.\n", timestring(mem_ctx, must_change_time), (long)must_change_time)); return NT_STATUS_PASSWORD_EXPIRED; } } @@ -356,8 +123,8 @@ static NTSTATUS sam_account_ok(TALLOC_CTX *mem_ctx, /* Test workstation. Workstation list is comma separated. */ workstation_list = talloc_strdup(mem_ctx, pdb_get_workstations(sampass)); - - if (!workstation_list) return NT_STATUS_NO_MEMORY; + if (!workstation_list) + return NT_STATUS_NO_MEMORY; if (*workstation_list) { BOOL invalid_ws = True; @@ -366,7 +133,7 @@ static NTSTATUS sam_account_ok(TALLOC_CTX *mem_ctx, fstring tok; while (next_token(&s, tok, ",", sizeof(tok))) { - DEBUG(10,("checking for workstation match %s and %s (len=%d)\n", + DEBUG(10,("sam_account_ok: checking for workstation match %s and %s (len=%d)\n", tok, user_info->wksta_name.str, user_info->wksta_name.len)); if(strequal(tok, user_info->wksta_name.str)) { invalid_ws = False; @@ -396,7 +163,6 @@ static NTSTATUS sam_account_ok(TALLOC_CTX *mem_ctx, return NT_STATUS_OK; } - /**************************************************************************** check if a username/password is OK assuming the password is a 24 byte SMB hash supplied in the user_info structure @@ -412,14 +178,14 @@ static NTSTATUS check_sam_security(const struct auth_context *auth_context, SAM_ACCOUNT *sampass=NULL; BOOL ret; NTSTATUS nt_status; - uint8 user_sess_key[16]; - const uint8* lm_hash; + DATA_BLOB user_sess_key = data_blob(NULL, 0); + DATA_BLOB lm_sess_key = data_blob(NULL, 0); if (!user_info || !auth_context) { return NT_STATUS_UNSUCCESSFUL; } - /* Can't use the talloc version here, becouse the returned struct gets + /* Can't use the talloc version here, because the returned struct gets kept on the server_info */ if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam(&sampass))) { return nt_status; @@ -431,21 +197,27 @@ static NTSTATUS check_sam_security(const struct auth_context *auth_context, ret = pdb_getsampwnam(sampass, user_info->internal_username.str); unbecome_root(); - if (ret == False) - { - DEBUG(3,("Couldn't find user '%s' in passdb file.\n", user_info->internal_username.str)); + if (ret == False) { + DEBUG(3,("check_sam_security: Couldn't find user '%s' in passdb file.\n", user_info->internal_username.str)); pdb_free_sam(&sampass); return NT_STATUS_NO_SUCH_USER; } - nt_status = sam_account_ok(mem_ctx, sampass, user_info); + /* Quit if the account was locked out. */ + if (pdb_get_acct_ctrl(sampass) & ACB_AUTOLOCK) { + DEBUG(3,("check_sam_security: Account for user %s was locked out.\n", pdb_get_username(sampass))); + return NT_STATUS_ACCOUNT_LOCKED_OUT; + } + + nt_status = sam_password_ok(auth_context, mem_ctx, sampass, + user_info, &user_sess_key, &lm_sess_key); if (!NT_STATUS_IS_OK(nt_status)) { pdb_free_sam(&sampass); return nt_status; } - nt_status = sam_password_ok(auth_context, mem_ctx, sampass, user_info, user_sess_key); + nt_status = sam_account_ok(mem_ctx, sampass, user_info); if (!NT_STATUS_IS_OK(nt_status)) { pdb_free_sam(&sampass); @@ -457,25 +229,21 @@ static NTSTATUS check_sam_security(const struct auth_context *auth_context, return nt_status; } - lm_hash = pdb_get_lanman_passwd((*server_info)->sam_account); - if (lm_hash) { - memcpy((*server_info)->first_8_lm_hash, lm_hash, 8); - } - - memcpy((*server_info)->session_key, user_sess_key, sizeof(user_sess_key)); + (*server_info)->user_session_key = user_sess_key; + (*server_info)->lm_session_key = lm_sess_key; return nt_status; } /* module initialisation */ -static NTSTATUS auth_init_sam(struct auth_context *auth_context, const char *param, auth_methods **auth_method) +static NTSTATUS auth_init_sam_ignoredomain(struct auth_context *auth_context, const char *param, auth_methods **auth_method) { if (!make_auth_methods(auth_context, auth_method)) { return NT_STATUS_NO_MEMORY; } (*auth_method)->auth = check_sam_security; - (*auth_method)->name = "sam"; + (*auth_method)->name = "sam_ignoredomain"; return NT_STATUS_OK; } @@ -509,57 +277,14 @@ static NTSTATUS check_samstrict_security(const struct auth_context *auth_context } /* module initialisation */ -static NTSTATUS auth_init_samstrict(struct auth_context *auth_context, const char *param, auth_methods **auth_method) +static NTSTATUS auth_init_sam(struct auth_context *auth_context, const char *param, auth_methods **auth_method) { if (!make_auth_methods(auth_context, auth_method)) { return NT_STATUS_NO_MEMORY; } (*auth_method)->auth = check_samstrict_security; - (*auth_method)->name = "samstrict"; - return NT_STATUS_OK; -} - -/**************************************************************************** -Check SAM security (above) but with a few extra checks if we're a DC. -****************************************************************************/ - -static NTSTATUS check_samstrict_dc_security(const struct auth_context *auth_context, - void *my_private_data, - TALLOC_CTX *mem_ctx, - const auth_usersupplied_info *user_info, - auth_serversupplied_info **server_info) -{ - - if (!user_info || !auth_context) { - return NT_STATUS_LOGON_FAILURE; - } - - /* If we are a domain member, we must not - attempt to check the password locally, - unless it is one of our aliases, empty - or our domain if we are a logon server.*/ - - - if ((strcasecmp(lp_workgroup(), user_info->domain.str) != 0) && - (!is_myname(user_info->domain.str))) { - DEBUG(7,("The requested user domain is not the local server name or our domain. [%s]\\[%s]\n", - user_info->domain.str,user_info->internal_username.str)); - return NT_STATUS_NO_SUCH_USER; - } - - return check_sam_security(auth_context, my_private_data, mem_ctx, user_info, server_info); -} - -/* module initialisation */ -static NTSTATUS auth_init_samstrict_dc(struct auth_context *auth_context, const char *param, auth_methods **auth_method) -{ - if (!make_auth_methods(auth_context, auth_method)) { - return NT_STATUS_NO_MEMORY; - } - - (*auth_method)->auth = check_samstrict_dc_security; - (*auth_method)->name = "samstrict_dc"; + (*auth_method)->name = "sam"; return NT_STATUS_OK; } @@ -577,17 +302,8 @@ NTSTATUS auth_sam_init(void) return ret; } - ops.name = "samstrict"; - ops.init = auth_init_samstrict; - ret = register_backend("auth", &ops); - if (!NT_STATUS_IS_OK(ret)) { - DEBUG(0,("Failed to register '%s' auth backend!\n", - ops.name)); - return ret; - } - - ops.name = "samstrict_dc"; - ops.init = auth_init_samstrict_dc; + ops.name = "sam_ignoredomain"; + ops.init = auth_init_sam_ignoredomain; ret = register_backend("auth", &ops); if (!NT_STATUS_IS_OK(ret)) { DEBUG(0,("Failed to register '%s' auth backend!\n", diff --git a/source4/auth/auth_util.c b/source4/auth/auth_util.c index db208008ae..db10514c7e 100644 --- a/source4/auth/auth_util.c +++ b/source4/auth/auth_util.c @@ -35,7 +35,8 @@ extern DOM_SID global_sid_Authenticated_Users; /**************************************************************************** Create a UNIX user on demand. ****************************************************************************/ -static int smb_create_user(const char *unix_user, const char *homedir) + +static int smb_create_user(const char *domain, const char *unix_username, const char *homedir) { pstring add_script; int ret; @@ -43,7 +44,9 @@ static int smb_create_user(const char *unix_user, const char *homedir) pstrcpy(add_script, lp_adduser_script()); if (! *add_script) return -1; - all_string_sub(add_script, "%u", unix_user, sizeof(pstring)); + all_string_sub(add_script, "%u", unix_username, sizeof(pstring)); + if (domain) + all_string_sub(add_script, "%D", domain, sizeof(pstring)); if (homedir) all_string_sub(add_script, "%H", homedir, sizeof(pstring)); ret = smbrun(add_script,NULL); @@ -70,7 +73,7 @@ void smb_user_control(const auth_usersupplied_info *user_info, auth_serversuppli */ if(lp_adduser_script() && !(pwd = Get_Pwnam(user_info->internal_username.str))) { - smb_create_user(user_info->internal_username.str, NULL); + smb_create_user(user_info->domain.str, user_info->internal_username.str, NULL); } } } @@ -116,16 +119,17 @@ static NTSTATUS make_user_info(auth_usersupplied_info **user_info, const char *client_domain, const char *domain, const char *wksta_name, - DATA_BLOB lm_pwd, DATA_BLOB nt_pwd, - DATA_BLOB plaintext, - uint32 auth_flags, BOOL encrypted) + DATA_BLOB *lm_pwd, DATA_BLOB *nt_pwd, + DATA_BLOB *lm_interactive_pwd, DATA_BLOB *nt_interactive_pwd, + DATA_BLOB *plaintext, + BOOL encrypted) { DEBUG(5,("attempting to make a user_info for %s (%s)\n", internal_username, smb_name)); *user_info = malloc(sizeof(**user_info)); if (!user_info) { - DEBUG(0,("malloc failed for user_info (size %d)\n", sizeof(*user_info))); + DEBUG(0,("malloc failed for user_info (size %lu)\n", (unsigned long)sizeof(*user_info))); return NT_STATUS_NO_MEMORY; } @@ -175,12 +179,19 @@ static NTSTATUS make_user_info(auth_usersupplied_info **user_info, DEBUG(5,("making blobs for %s's user_info struct\n", internal_username)); - (*user_info)->lm_resp = data_blob(lm_pwd.data, lm_pwd.length); - (*user_info)->nt_resp = data_blob(nt_pwd.data, nt_pwd.length); - (*user_info)->plaintext_password = data_blob(plaintext.data, plaintext.length); + if (lm_pwd) + (*user_info)->lm_resp = data_blob(lm_pwd->data, lm_pwd->length); + if (nt_pwd) + (*user_info)->nt_resp = data_blob(nt_pwd->data, nt_pwd->length); + if (lm_interactive_pwd) + (*user_info)->lm_interactive_pwd = data_blob(lm_interactive_pwd->data, lm_interactive_pwd->length); + if (nt_interactive_pwd) + (*user_info)->nt_interactive_pwd = data_blob(nt_interactive_pwd->data, nt_interactive_pwd->length); + + if (plaintext) + (*user_info)->plaintext_password = data_blob(plaintext->data, plaintext->length); (*user_info)->encrypted = encrypted; - (*user_info)->auth_flags = auth_flags; DEBUG(10,("made an %sencrypted user_info for %s (%s)\n", encrypted ? "":"un" , internal_username, smb_name)); @@ -195,9 +206,10 @@ NTSTATUS make_user_info_map(auth_usersupplied_info **user_info, const char *smb_name, const char *client_domain, const char *wksta_name, - DATA_BLOB lm_pwd, DATA_BLOB nt_pwd, - DATA_BLOB plaintext, - uint32 ntlmssp_flags, BOOL encrypted) + DATA_BLOB *lm_pwd, DATA_BLOB *nt_pwd, + DATA_BLOB *lm_interactive_pwd, DATA_BLOB *nt_interactive_pwd, + DATA_BLOB *plaintext, + BOOL encrypted) { const char *domain; fstring internal_username; @@ -206,58 +218,22 @@ NTSTATUS make_user_info_map(auth_usersupplied_info **user_info, DEBUG(5, ("make_user_info_map: Mapping user [%s]\\[%s] from workstation [%s]\n", client_domain, smb_name, wksta_name)); - if (lp_allow_trusted_domains() && *client_domain) { - - /* the client could have given us a workstation name - or other crap for the workgroup - we really need a - way of telling if this domain name is one of our - trusted domain names - - Also don't allow "" as a domain, fixes a Win9X bug - where it doens't supply a domain for logon script - 'net use' commands. - - The way I do it here is by checking if the fully - qualified username exists. This is rather reliant - on winbind, but until we have a better method this - will have to do - */ + /* don't allow "" as a domain, fixes a Win9X bug + where it doens't supply a domain for logon script + 'net use' commands. */ + if ( *client_domain ) domain = client_domain; - - if ((smb_name) && (*smb_name)) { /* Don't do this for guests */ - char *user = NULL; - if (asprintf(&user, "%s%s%s", - client_domain, lp_winbind_separator(), - smb_name) < 0) { - DEBUG(0, ("make_user_info_map: asprintf() failed!\n")); - return NT_STATUS_NO_MEMORY; - } - - DEBUG(5, ("make_user_info_map: testing for user %s\n", user)); - - if (Get_Pwnam(user) == NULL) { - DEBUG(5, ("make_user_info_map: test for user %s failed\n", user)); - domain = lp_workgroup(); - DEBUG(5, ("make_user_info_map: trusted domain %s doesn't appear to exist, using %s\n", - client_domain, domain)); - } else { - DEBUG(5, ("make_user_info_map: using trusted domain %s\n", domain)); - } - SAFE_FREE(user); - } - } else { + else domain = lp_workgroup(); - } + + /* we know that it is a trusted domain (and we are allowing them) or it is our domain */ - return make_user_info(user_info, - smb_name, internal_username, - client_domain, domain, - wksta_name, + return make_user_info(user_info, smb_name, internal_username, + client_domain, domain, wksta_name, lm_pwd, nt_pwd, - plaintext, - ntlmssp_flags, encrypted); - + lm_interactive_pwd, nt_interactive_pwd, + plaintext, encrypted); } /**************************************************************************** @@ -276,23 +252,14 @@ BOOL make_user_info_netlogon_network(auth_usersupplied_info **user_info, NTSTATUS nt_status; DATA_BLOB lm_blob = data_blob(lm_network_pwd, lm_pwd_len); DATA_BLOB nt_blob = data_blob(nt_network_pwd, nt_pwd_len); - DATA_BLOB plaintext_blob = data_blob(NULL, 0); - uint32 auth_flags = AUTH_FLAG_NONE; - - if (lm_pwd_len) - auth_flags |= AUTH_FLAG_LM_RESP; - if (nt_pwd_len == 24) { - auth_flags |= AUTH_FLAG_NTLM_RESP; - } else if (nt_pwd_len != 0) { - auth_flags |= AUTH_FLAG_NTLMv2_RESP; - } nt_status = make_user_info_map(user_info, - smb_name, client_domain, - wksta_name, - lm_blob, nt_blob, - plaintext_blob, - auth_flags, True); + smb_name, client_domain, + wksta_name, + lm_pwd_len ? &lm_blob : NULL, + nt_pwd_len ? &nt_blob : NULL, + NULL, NULL, NULL, + True); ret = NT_STATUS_IS_OK(nt_status) ? True : False; @@ -320,7 +287,6 @@ BOOL make_user_info_netlogon_interactive(auth_usersupplied_info **user_info, unsigned char local_lm_response[24]; unsigned char local_nt_response[24]; unsigned char key[16]; - uint32 auth_flags = AUTH_FLAG_NONE; ZERO_STRUCT(key); memcpy(key, dc_sess_key, 8); @@ -339,8 +305,11 @@ BOOL make_user_info_netlogon_interactive(auth_usersupplied_info **user_info, dump_data(100, nt_pwd, sizeof(nt_pwd)); #endif - SamOEMhash((uchar *)lm_pwd, key, sizeof(lm_pwd)); - SamOEMhash((uchar *)nt_pwd, key, sizeof(nt_pwd)); + if (lm_interactive_pwd) + SamOEMhash((uchar *)lm_pwd, key, sizeof(lm_pwd)); + + if (nt_interactive_pwd) + SamOEMhash((uchar *)nt_pwd, key, sizeof(nt_pwd)); #ifdef DEBUG_PASSWORD DEBUG(100,("decrypt of lm owf password:")); @@ -350,37 +319,51 @@ BOOL make_user_info_netlogon_interactive(auth_usersupplied_info **user_info, dump_data(100, nt_pwd, sizeof(nt_pwd)); #endif - SMBOWFencrypt((const unsigned char *)lm_pwd, chal, local_lm_response); - SMBOWFencrypt((const unsigned char *)nt_pwd, chal, local_nt_response); + if (lm_interactive_pwd) + SMBOWFencrypt((const unsigned char *)lm_pwd, chal, local_lm_response); + + if (nt_interactive_pwd) + SMBOWFencrypt((const unsigned char *)nt_pwd, chal, local_nt_response); /* Password info paranoia */ - ZERO_STRUCT(lm_pwd); - ZERO_STRUCT(nt_pwd); ZERO_STRUCT(key); { BOOL ret; NTSTATUS nt_status; - DATA_BLOB local_lm_blob = data_blob(local_lm_response, sizeof(local_lm_response)); - DATA_BLOB local_nt_blob = data_blob(local_nt_response, sizeof(local_nt_response)); - DATA_BLOB plaintext_blob = data_blob(NULL, 0); + DATA_BLOB local_lm_blob; + DATA_BLOB local_nt_blob; - if (lm_interactive_pwd) - auth_flags |= AUTH_FLAG_LM_RESP; - if (nt_interactive_pwd) - auth_flags |= AUTH_FLAG_NTLM_RESP; + DATA_BLOB lm_interactive_blob; + DATA_BLOB nt_interactive_blob; + + if (lm_interactive_pwd) { + local_lm_blob = data_blob(local_lm_response, sizeof(local_lm_response)); + lm_interactive_blob = data_blob(lm_pwd, sizeof(lm_pwd)); + ZERO_STRUCT(lm_pwd); + } + + if (nt_interactive_pwd) { + local_nt_blob = data_blob(local_nt_response, sizeof(local_nt_response)); + nt_interactive_blob = data_blob(nt_pwd, sizeof(nt_pwd)); + ZERO_STRUCT(nt_pwd); + } nt_status = make_user_info_map(user_info, smb_name, client_domain, wksta_name, - local_lm_blob, - local_nt_blob, - plaintext_blob, - auth_flags, True); - + lm_interactive_pwd ? &local_lm_blob : NULL, + nt_interactive_pwd ? &local_nt_blob : NULL, + lm_interactive_pwd ? &lm_interactive_blob : NULL, + nt_interactive_pwd ? &nt_interactive_blob : NULL, + NULL, + True); + ret = NT_STATUS_IS_OK(nt_status) ? True : False; data_blob_free(&local_lm_blob); data_blob_free(&local_nt_blob); + data_blob_free(&lm_interactive_blob); + data_blob_free(&nt_interactive_blob); return ret; } } @@ -400,7 +383,6 @@ BOOL make_user_info_for_reply(auth_usersupplied_info **user_info, DATA_BLOB local_lm_blob; DATA_BLOB local_nt_blob; NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; - uint32 auth_flags = AUTH_FLAG_NONE; /* * Not encrypted - do so. @@ -416,14 +398,13 @@ BOOL make_user_info_for_reply(auth_usersupplied_info **user_info, dump_data(100, plaintext_password.data, plaintext_password.length); #endif - SMBencrypt( (const uchar *)plaintext_password.data, (const uchar*)chal, local_lm_response); + SMBencrypt( (const char *)plaintext_password.data, (const uchar*)chal, local_lm_response); local_lm_blob = data_blob(local_lm_response, 24); /* We can't do an NT hash here, as the password needs to be case insensitive */ local_nt_blob = data_blob(NULL, 0); - auth_flags = (AUTH_FLAG_PLAINTEXT | AUTH_FLAG_LM_RESP); } else { local_lm_blob = data_blob(NULL, 0); local_nt_blob = data_blob(NULL, 0); @@ -432,10 +413,11 @@ BOOL make_user_info_for_reply(auth_usersupplied_info **user_info, ret = make_user_info_map(user_info, smb_name, client_domain, sub_get_remote_machine(), - local_lm_blob, - local_nt_blob, - plaintext_password, - auth_flags, False); + local_lm_blob.data ? &local_lm_blob : NULL, + local_nt_blob.data ? &local_nt_blob : NULL, + NULL, NULL, + plaintext_password.data ? &plaintext_password : NULL, + False); data_blob_free(&local_lm_blob); return NT_STATUS_IS_OK(ret) ? True : False; @@ -450,27 +432,13 @@ NTSTATUS make_user_info_for_reply_enc(auth_usersupplied_info **user_info, const char *client_domain, DATA_BLOB lm_resp, DATA_BLOB nt_resp) { - uint32 auth_flags = AUTH_FLAG_NONE; - - DATA_BLOB no_plaintext_blob = data_blob(NULL, 0); - - if (lm_resp.length == 24) { - auth_flags |= AUTH_FLAG_LM_RESP; - } - if (nt_resp.length == 0) { - } else if (nt_resp.length == 24) { - auth_flags |= AUTH_FLAG_NTLM_RESP; - } else { - auth_flags |= AUTH_FLAG_NTLMv2_RESP; - } - return make_user_info_map(user_info, smb_name, client_domain, sub_get_remote_machine(), - lm_resp, - nt_resp, - no_plaintext_blob, - auth_flags, True); + lm_resp.data ? &lm_resp : NULL, + nt_resp.data ? &nt_resp : NULL, + NULL, NULL, NULL, + True); } /**************************************************************************** @@ -479,19 +447,16 @@ NTSTATUS make_user_info_for_reply_enc(auth_usersupplied_info **user_info, BOOL make_user_info_guest(auth_usersupplied_info **user_info) { - DATA_BLOB lm_blob = data_blob(NULL, 0); - DATA_BLOB nt_blob = data_blob(NULL, 0); - DATA_BLOB plaintext_blob = data_blob(NULL, 0); - uint32 auth_flags = AUTH_FLAG_NONE; NTSTATUS nt_status; nt_status = make_user_info(user_info, - "","", - "","", - "", - nt_blob, lm_blob, - plaintext_blob, - auth_flags, True); + "","", + "","", + "", + NULL, NULL, + NULL, NULL, + NULL, + True); return NT_STATUS_IS_OK(nt_status) ? True : False; } @@ -512,9 +477,9 @@ void debug_nt_user_token(int dbg_class, int dbg_lev, NT_USER_TOKEN *token) DEBUGC(dbg_class, dbg_lev, ("NT user token of user %s\n", sid_to_string(sid_str, &token->user_sids[0]) )); - DEBUGADDC(dbg_class, dbg_lev, ("contains %i SIDs\n", token->num_sids)); + DEBUGADDC(dbg_class, dbg_lev, ("contains %lu SIDs\n", (unsigned long)token->num_sids)); for (i = 0; i < token->num_sids; i++) - DEBUGADDC(dbg_class, dbg_lev, ("SID[%3i]: %s\n", i, + DEBUGADDC(dbg_class, dbg_lev, ("SID[%3lu]: %s\n", (unsigned long)i, sid_to_string(sid_str, &token->user_sids[i]))); } @@ -633,7 +598,7 @@ struct nt_user_token *create_nt_token(uid_t uid, gid_t gid, int ngroups, gid_t * return NULL; } - group_sids = malloc(sizeof(DOM_SID) * ngroups); + group_sids = malloc(sizeof(DOM_SID) * ngroups); if (!group_sids) { DEBUG(0, ("create_nt_token: malloc() failed for DOM_SID list!\n")); return NULL; @@ -856,9 +821,16 @@ NTSTATUS make_server_info_guest(auth_serversupplied_info **server_info) nt_status = make_server_info_sam(server_info, sampass); if (NT_STATUS_IS_OK(nt_status)) { + static const char zeros[16]; (*server_info)->guest = True; + + /* annoying, but the Guest really does have a session key, + and it is all zeros! */ + (*server_info)->user_session_key = data_blob(zeros, sizeof(zeros)); + (*server_info)->lm_session_key = data_blob(zeros, sizeof(zeros)); } + return nt_status; } diff --git a/source4/include/smb.h b/source4/include/smb.h index 520aee487c..7c86511b34 100644 --- a/source4/include/smb.h +++ b/source4/include/smb.h @@ -1120,7 +1120,7 @@ typedef struct user_struct NT_USER_TOKEN *nt_user_token; - uint8 session_key[16]; + DATA_BLOB session_key; char *session_keystr; /* used by utmp and pam session code. TDB key string */ diff --git a/source4/libcli/auth/ntlm_check.c b/source4/libcli/auth/ntlm_check.c new file mode 100644 index 0000000000..a7764f9e98 --- /dev/null +++ b/source4/libcli/auth/ntlm_check.c @@ -0,0 +1,445 @@ +/* + Unix SMB/CIFS implementation. + Password and authentication handling + Copyright (C) Andrew Tridgell 1992-2000 + Copyright (C) Luke Kenneth Casson Leighton 1996-2000 + Copyright (C) Andrew Bartlett 2001-2003 + Copyright (C) Gerald Carter 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_AUTH + +/**************************************************************************** + Core of smb password checking routine. +****************************************************************************/ + +static BOOL smb_pwd_check_ntlmv1(const DATA_BLOB *nt_response, + const uchar *part_passwd, + const DATA_BLOB *sec_blob, + DATA_BLOB *user_sess_key) +{ + /* Finish the encryption of part_passwd. */ + uchar p24[24]; + + if (part_passwd == NULL) { + DEBUG(10,("No password set - DISALLOWING access\n")); + /* No password set - always false ! */ + return False; + } + + if (sec_blob->length != 8) { + DEBUG(0, ("smb_pwd_check_ntlmv1: incorrect challenge size (%lu)\n", + (unsigned long)sec_blob->length)); + return False; + } + + if (nt_response->length != 24) { + DEBUG(0, ("smb_pwd_check_ntlmv1: incorrect password length (%lu)\n", + (unsigned long)nt_response->length)); + return False; + } + + SMBOWFencrypt(part_passwd, sec_blob->data, p24); + if (user_sess_key != NULL) { + *user_sess_key = data_blob(NULL, 16); + SMBsesskeygen_ntv1(part_passwd, NULL, user_sess_key->data); + } + + +#if DEBUG_PASSWORD + DEBUG(100,("Part password (P16) was |\n")); + dump_data(100, part_passwd, 16); + DEBUGADD(100,("Password from client was |\n")); + dump_data(100, nt_response->data, nt_response->length); + DEBUGADD(100,("Given challenge was |\n")); + dump_data(100, sec_blob->data, sec_blob->length); + DEBUGADD(100,("Value from encryption was |\n")); + dump_data(100, p24, 24); +#endif + return (memcmp(p24, nt_response->data, 24) == 0); +} + +/**************************************************************************** + Core of smb password checking routine. (NTLMv2, LMv2) + Note: The same code works with both NTLMv2 and LMv2. +****************************************************************************/ + +static BOOL smb_pwd_check_ntlmv2(const DATA_BLOB *ntv2_response, + const uchar *part_passwd, + const DATA_BLOB *sec_blob, + const char *user, const char *domain, + BOOL upper_case_domain, /* should the domain be transformed into upper case? */ + DATA_BLOB *user_sess_key) +{ + /* Finish the encryption of part_passwd. */ + uchar kr[16]; + uchar value_from_encryption[16]; + uchar client_response[16]; + DATA_BLOB client_key_data; + + if (part_passwd == NULL) { + DEBUG(10,("No password set - DISALLOWING access\n")); + /* No password set - always False */ + return False; + } + + if (sec_blob->length != 8) { + DEBUG(0, ("smb_pwd_check_ntlmv2: incorrect challenge size (%lu)\n", + (unsigned long)sec_blob->length)); + return False; + } + + if (ntv2_response->length < 24) { + /* We MUST have more than 16 bytes, or the stuff below will go + crazy. No known implementation sends less than the 24 bytes + for LMv2, let alone NTLMv2. */ + DEBUG(0, ("smb_pwd_check_ntlmv2: incorrect password length (%lu)\n", + (unsigned long)ntv2_response->length)); + return False; + } + + client_key_data = data_blob(ntv2_response->data+16, ntv2_response->length-16); + /* + todo: should we be checking this for anything? We can't for LMv2, + but for NTLMv2 it is meant to contain the current time etc. + */ + + memcpy(client_response, ntv2_response->data, sizeof(client_response)); + + if (!ntv2_owf_gen(part_passwd, user, domain, upper_case_domain, kr)) { + return False; + } + + SMBOWFencrypt_ntv2(kr, sec_blob, &client_key_data, value_from_encryption); + if (user_sess_key != NULL) { + *user_sess_key = data_blob(NULL, 16); + SMBsesskeygen_ntv2(kr, value_from_encryption, user_sess_key->data); + } + +#if DEBUG_PASSWORD + DEBUG(100,("Part password (P16) was |\n")); + dump_data(100, part_passwd, 16); + DEBUGADD(100,("Password from client was |\n")); + dump_data(100, ntv2_response->data, ntv2_response->length); + DEBUGADD(100,("Variable data from client was |\n")); + dump_data(100, client_key_data.data, client_key_data.length); + DEBUGADD(100,("Given challenge was |\n")); + dump_data(100, sec_blob->data, sec_blob->length); + DEBUGADD(100,("Value from encryption was |\n")); + dump_data(100, value_from_encryption, 16); +#endif + data_blob_clear_free(&client_key_data); + return (memcmp(value_from_encryption, client_response, 16) == 0); +} + +/** + * Check a challenge-response password against the value of the NT or + * LM password hash. + * + * @param mem_ctx talloc context + * @param challenge 8-byte challenge. If all zero, forces plaintext comparison + * @param nt_response 'unicode' NT response to the challenge, or unicode password + * @param lm_response ASCII or LANMAN response to the challenge, or password in DOS code page + * @param username internal Samba username, for log messages + * @param client_username username the client used + * @param client_domain domain name the client used (may be mapped) + * @param nt_pw MD4 unicode password from our passdb or similar + * @param lm_pw LANMAN ASCII password from our passdb or similar + * @param user_sess_key User session key + * @param lm_sess_key LM session key (first 8 bytes of the LM hash) + */ + +NTSTATUS ntlm_password_check(TALLOC_CTX *mem_ctx, + const DATA_BLOB *challenge, + const DATA_BLOB *lm_response, + const DATA_BLOB *nt_response, + const DATA_BLOB *lm_interactive_pwd, + const DATA_BLOB *nt_interactive_pwd, + const char *username, + const char *client_username, + const char *client_domain, + const uint8 *lm_pw, const uint8 *nt_pw, + DATA_BLOB *user_sess_key, + DATA_BLOB *lm_sess_key) +{ + static const unsigned char zeros[8]; + if (nt_pw == NULL) { + DEBUG(3,("ntlm_password_check: NO NT password stored for user %s.\n", + username)); + } + + if (nt_interactive_pwd && nt_interactive_pwd->length && nt_pw) { + if (nt_interactive_pwd->length != 16) { + DEBUG(3,("ntlm_password_check: Interactive logon: Invalid NT password length (%d) supplied for user %s\n", (int)nt_interactive_pwd->length, + username)); + return NT_STATUS_WRONG_PASSWORD; + } + + if (memcmp(nt_interactive_pwd->data, nt_pw, 16) == 0) { + if (user_sess_key) { + *user_sess_key = data_blob(NULL, 16); + SMBsesskeygen_ntv1(nt_pw, NULL, user_sess_key->data); + } + return NT_STATUS_OK; + } else { + DEBUG(3,("ntlm_password_check: Interactive logon: NT password check failed for user %s\n", + username)); + return NT_STATUS_WRONG_PASSWORD; + } + + } else if (lm_interactive_pwd && lm_interactive_pwd->length && lm_pw) { + if (lm_interactive_pwd->length != 16) { + DEBUG(3,("ntlm_password_check: Interactive logon: Invalid LANMAN password length (%d) supplied for user %s\n", (int)lm_interactive_pwd->length, + username)); + return NT_STATUS_WRONG_PASSWORD; + } + + if (!lp_lanman_auth()) { + DEBUG(3,("ntlm_password_check: Interactive logon: only LANMAN password supplied for user %s, and LM passwords are disabled!\n", + username)); + return NT_STATUS_WRONG_PASSWORD; + } + + if (memcmp(lm_interactive_pwd->data, lm_pw, 16) == 0) { + return NT_STATUS_OK; + } else { + DEBUG(3,("ntlm_password_check: Interactive logon: LANMAN password check failed for user %s\n", + username)); + return NT_STATUS_WRONG_PASSWORD; + } + } + + /* Check for cleartext netlogon. Used by Exchange 5.5. */ + if (challenge->length == sizeof(zeros) && + (memcmp(challenge->data, zeros, challenge->length) == 0 )) { + + DEBUG(4,("ntlm_password_check: checking plaintext passwords for user %s\n", + username)); + if (nt_pw && nt_response->length) { + unsigned char pwhash[16]; + mdfour(pwhash, nt_response->data, nt_response->length); + if (memcmp(pwhash, nt_pw, sizeof(pwhash)) == 0) { + return NT_STATUS_OK; + } else { + DEBUG(3,("ntlm_password_check: NT (Unicode) plaintext password check failed for user %s\n", + username)); + return NT_STATUS_WRONG_PASSWORD; + } + + } else if (!lp_lanman_auth()) { + DEBUG(3,("ntlm_password_check: (plaintext password check) LANMAN passwords NOT PERMITTED for user %s\n", + username)); + + } else if (lm_pw && lm_response->length) { + uchar dospwd[14]; + uchar p16[16]; + ZERO_STRUCT(dospwd); + + memcpy(dospwd, lm_response->data, MIN(lm_response->length, sizeof(dospwd))); + /* Only the fisrt 14 chars are considered, password need not be null terminated. */ + + /* we *might* need to upper-case the string here */ + E_P16((const unsigned char *)dospwd, p16); + + if (memcmp(p16, lm_pw, sizeof(p16)) == 0) { + return NT_STATUS_OK; + } else { + DEBUG(3,("ntlm_password_check: LANMAN (ASCII) plaintext password check failed for user %s\n", + username)); + return NT_STATUS_WRONG_PASSWORD; + } + } else { + DEBUG(3, ("Plaintext authentication for user %s attempted, but neither NT nor LM passwords available\n", username)); + return NT_STATUS_WRONG_PASSWORD; + } + } + + if (nt_response->length != 0 && nt_response->length < 24) { + DEBUG(2,("ntlm_password_check: invalid NT password length (%lu) for user %s\n", + (unsigned long)nt_response->length, username)); + } + + if (nt_response->length >= 24 && nt_pw) { + if (nt_response->length > 24) { + /* We have the NT MD4 hash challenge available - see if we can + use it + */ + DEBUG(4,("ntlm_password_check: Checking NTLMv2 password with domain [%s]\n", client_domain)); + if (smb_pwd_check_ntlmv2( nt_response, + nt_pw, challenge, + client_username, + client_domain, + False, + user_sess_key)) { + return NT_STATUS_OK; + } + + DEBUG(4,("ntlm_password_check: Checking NTLMv2 password with uppercased version of domain [%s]\n", client_domain)); + if (smb_pwd_check_ntlmv2( nt_response, + nt_pw, challenge, + client_username, + client_domain, + True, + user_sess_key)) { + return NT_STATUS_OK; + } + + DEBUG(4,("ntlm_password_check: Checking NTLMv2 password without a domain\n")); + if (smb_pwd_check_ntlmv2( nt_response, + nt_pw, challenge, + client_username, + "", + False, + user_sess_key)) { + return NT_STATUS_OK; + } else { + DEBUG(3,("ntlm_password_check: NTLMv2 password check failed\n")); + return NT_STATUS_WRONG_PASSWORD; + } + } + + if (lp_ntlm_auth()) { + /* We have the NT MD4 hash challenge available - see if we can + use it (ie. does it exist in the smbpasswd file). + */ + DEBUG(4,("ntlm_password_check: Checking NT MD4 password\n")); + if (smb_pwd_check_ntlmv1(nt_response, + nt_pw, challenge, + user_sess_key)) { + /* The LM session key for this response is not very secure, + so use it only if we otherwise allow LM authentication */ + + if (lp_lanman_auth() && lm_pw) { + uint8 first_8_lm_hash[16]; + memcpy(first_8_lm_hash, lm_pw, 8); + memset(first_8_lm_hash + 8, '\0', 8); + *lm_sess_key = data_blob(first_8_lm_hash, 16); + } + return NT_STATUS_OK; + } else { + DEBUG(3,("ntlm_password_check: NT MD4 password check failed for user %s\n", + username)); + return NT_STATUS_WRONG_PASSWORD; + } + } else { + DEBUG(2,("ntlm_password_check: NTLMv1 passwords NOT PERMITTED for user %s\n", + username)); + /* no return, becouse we might pick up LMv2 in the LM field */ + } + } + + if (lm_response->length == 0) { + DEBUG(3,("ntlm_password_check: NEITHER LanMan nor NT password supplied for user %s\n", + username)); + return NT_STATUS_WRONG_PASSWORD; + } + + if (lm_response->length < 24) { + DEBUG(2,("ntlm_password_check: invalid LanMan password length (%lu) for user %s\n", + (unsigned long)nt_response->length, username)); + return NT_STATUS_WRONG_PASSWORD; + } + + if (!lp_lanman_auth()) { + DEBUG(3,("ntlm_password_check: Lanman passwords NOT PERMITTED for user %s\n", + username)); + } else if (!lm_pw) { + DEBUG(3,("ntlm_password_check: NO LanMan password set for user %s (and no NT password supplied)\n", + username)); + } else { + DEBUG(4,("ntlm_password_check: Checking LM password\n")); + if (smb_pwd_check_ntlmv1(lm_response, + lm_pw, challenge, + NULL)) { + uint8 first_8_lm_hash[16]; + memcpy(first_8_lm_hash, lm_pw, 8); + memset(first_8_lm_hash + 8, '\0', 8); + *user_sess_key = data_blob(first_8_lm_hash, 16); + *lm_sess_key = data_blob(first_8_lm_hash, 16); + return NT_STATUS_OK; + } + } + + if (!nt_pw) { + DEBUG(4,("ntlm_password_check: LM password check failed for user, no NT password %s\n",username)); + return NT_STATUS_WRONG_PASSWORD; + } + + /* This is for 'LMv2' authentication. almost NTLMv2 but limited to 24 bytes. + - related to Win9X, legacy NAS pass-though authentication + */ + DEBUG(4,("ntlm_password_check: Checking LMv2 password with domain %s\n", client_domain)); + if (smb_pwd_check_ntlmv2( lm_response, + nt_pw, challenge, + client_username, + client_domain, + False, + NULL)) { + return NT_STATUS_OK; + } + + DEBUG(4,("ntlm_password_check: Checking LMv2 password with upper-cased version of domain %s\n", client_domain)); + if (smb_pwd_check_ntlmv2( lm_response, + nt_pw, challenge, + client_username, + client_domain, + True, + NULL)) { + return NT_STATUS_OK; + } + + DEBUG(4,("ntlm_password_check: Checking LMv2 password without a domain\n")); + if (smb_pwd_check_ntlmv2( lm_response, + nt_pw, challenge, + client_username, + "", + False, + NULL)) { + return NT_STATUS_OK; + } + + /* Apparently NT accepts NT responses in the LM field + - I think this is related to Win9X pass-though authentication + */ + DEBUG(4,("ntlm_password_check: Checking NT MD4 password in LM field\n")); + if (lp_ntlm_auth()) { + if (smb_pwd_check_ntlmv1(lm_response, + nt_pw, challenge, + NULL)) { + /* The session key for this response is still very odd. + It not very secure, so use it only if we otherwise + allow LM authentication */ + + if (lp_lanman_auth() && lm_pw) { + uint8 first_8_lm_hash[16]; + memcpy(first_8_lm_hash, lm_pw, 8); + memset(first_8_lm_hash + 8, '\0', 8); + *user_sess_key = data_blob(first_8_lm_hash, 16); + *lm_sess_key = data_blob(first_8_lm_hash, 16); + } + return NT_STATUS_OK; + } + DEBUG(3,("ntlm_password_check: LM password, NT MD4 password in LM field and LMv2 failed for user %s\n",username)); + } else { + DEBUG(3,("ntlm_password_check: LM password and LMv2 failed for user %s, and NT MD4 password in LM field not permitted\n",username)); + } + return NT_STATUS_WRONG_PASSWORD; +} + diff --git a/source4/libcli/auth/ntlmssp.c b/source4/libcli/auth/ntlmssp.c index 5fd3938a7d..00cf4d380a 100644 --- a/source4/libcli/auth/ntlmssp.c +++ b/source4/libcli/auth/ntlmssp.c @@ -282,7 +282,7 @@ void ntlmssp_end(NTLMSSP_STATE **ntlmssp_state) data_blob_free(&(*ntlmssp_state)->chal); data_blob_free(&(*ntlmssp_state)->lm_resp); data_blob_free(&(*ntlmssp_state)->nt_resp); - + data_blob_free(&(*ntlmssp_state)->encrypted_session_key); talloc_destroy(mem_ctx); } @@ -330,7 +330,7 @@ static void ntlmssp_handle_neg_flags(struct ntlmssp_state *ntlmssp_state, ntlmssp_state->unicode = False; } - if (neg_flags & NTLMSSP_NEGOTIATE_LM_KEY && allow_lm) { + if ((neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) && allow_lm) { /* other end forcing us to use LM */ ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_LM_KEY; ntlmssp_state->use_ntlmv2 = False; @@ -338,9 +338,12 @@ static void ntlmssp_handle_neg_flags(struct ntlmssp_state *ntlmssp_state, 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_NTLM2)) { ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2; - ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; } if (!(neg_flags & NTLMSSP_NEGOTIATE_128)) { @@ -403,13 +406,6 @@ static NTSTATUS ntlmssp_server_negotiate(struct ntlmssp_state *ntlmssp_state, ntlmssp_handle_neg_flags(ntlmssp_state, neg_flags, lp_lanman_auth()); - chal_flags = ntlmssp_state->neg_flags; - - target_name = ntlmssp_target_name(ntlmssp_state, - neg_flags, &chal_flags); - if (target_name == NULL) - return NT_STATUS_INVALID_PARAMETER; - /* Ask our caller what challenge they would like in the packet */ cryptkey = ntlmssp_state->get_challenge(ntlmssp_state); @@ -418,9 +414,21 @@ static NTSTATUS ntlmssp_server_negotiate(struct ntlmssp_state *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->mem_ctx, cryptkey, 8); ntlmssp_state->internal_chal = data_blob_talloc(ntlmssp_state->mem_ctx, cryptkey, 8); - /* This should be a 'netbios domain -> DNS domain' mapping */ dnsdomname[0] = '\0'; @@ -429,7 +437,6 @@ static NTSTATUS ntlmssp_server_negotiate(struct ntlmssp_state *ntlmssp_state, dnsname[0] = '\0'; get_myfullname(dnsname); - strlower_m(dnsname); /* This creates the 'blob' of names that appears at the end of the packet */ if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) @@ -486,20 +493,12 @@ static NTSTATUS ntlmssp_server_negotiate(struct ntlmssp_state *ntlmssp_state, * @return Errors or NT_STATUS_OK. */ -static NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state, - const DATA_BLOB request, DATA_BLOB *reply) +static NTSTATUS ntlmssp_server_preauth(struct ntlmssp_state *ntlmssp_state, + const DATA_BLOB request) { - DATA_BLOB encrypted_session_key = data_blob(NULL, 0); - DATA_BLOB nt_session_key = data_blob(NULL, 0); - DATA_BLOB lm_session_key = data_blob(NULL, 0); - DATA_BLOB session_key = data_blob(NULL, 0); uint32 ntlmssp_command, auth_flags; NTSTATUS nt_status; - /* used by NTLM2 */ - BOOL doing_ntlm2 = False; - - uchar session_nonce[16]; uchar session_nonce_hash[16]; const char *parse_string; @@ -507,9 +506,6 @@ static NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state, char *user = NULL; char *workstation = NULL; - /* parse the NTLMSSP packet */ - *reply = data_blob(NULL, 0); - #if 0 file_save("ntlmssp_auth.dat", request.data, request.length); #endif @@ -536,14 +532,14 @@ static NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state, &domain, &user, &workstation, - &encrypted_session_key, + &ntlmssp_state->encrypted_session_key, &auth_flags)) { DEBUG(1, ("ntlmssp_server_auth: failed to parse NTLMSSP:\n")); dump_data(2, (const char *)request.data, request.length); SAFE_FREE(domain); SAFE_FREE(user); SAFE_FREE(workstation); - data_blob_free(&encrypted_session_key); + data_blob_free(&ntlmssp_state->encrypted_session_key); auth_flags = 0; /* Try again with a shorter string (Win9X truncates this packet) */ @@ -572,11 +568,14 @@ static NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state, } } + if (auth_flags) + ntlmssp_handle_neg_flags(ntlmssp_state, auth_flags, lp_lanman_auth()); + if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, domain))) { SAFE_FREE(domain); SAFE_FREE(user); SAFE_FREE(workstation); - data_blob_free(&encrypted_session_key); + data_blob_free(&ntlmssp_state->encrypted_session_key); return nt_status; } @@ -584,7 +583,7 @@ static NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state, SAFE_FREE(domain); SAFE_FREE(user); SAFE_FREE(workstation); - data_blob_free(&encrypted_session_key); + data_blob_free(&ntlmssp_state->encrypted_session_key); return nt_status; } @@ -592,7 +591,7 @@ static NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state, SAFE_FREE(domain); SAFE_FREE(user); SAFE_FREE(workstation); - data_blob_free(&encrypted_session_key); + data_blob_free(&ntlmssp_state->encrypted_session_key); return nt_status; } @@ -618,13 +617,13 @@ static NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state, struct MD5Context md5_session_nonce_ctx; SMB_ASSERT(ntlmssp_state->internal_chal.data && ntlmssp_state->internal_chal.length == 8); - doing_ntlm2 = True; + ntlmssp_state->doing_ntlm2 = True; - memcpy(session_nonce, ntlmssp_state->internal_chal.data, 8); - memcpy(&session_nonce[8], ntlmssp_state->lm_resp.data, 8); + 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, session_nonce, 16); + 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->mem_ctx, session_nonce_hash, 8); @@ -634,69 +633,117 @@ static NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state, /* We changed the effective challenge - set it */ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_state->set_challenge(ntlmssp_state, &ntlmssp_state->chal))) { - data_blob_free(&encrypted_session_key); + data_blob_free(&ntlmssp_state->encrypted_session_key); return nt_status; } } } + return NT_STATUS_OK; +} - /* Finally, actually ask if the password is OK */ - if (!NT_STATUS_IS_OK(nt_status = ntlmssp_state->check_password(ntlmssp_state, &nt_session_key, &lm_session_key))) { - data_blob_free(&encrypted_session_key); - return nt_status; - } +/** + * Next state function for the Authenticate packet + * + * @param ntlmssp_state NTLMSSP State + * @param request The request, as a DATA_BLOB + * @param reply The reply, as an allocated DATA_BLOB, caller to free. + * @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, + DATA_BLOB *reply) +{ + NTSTATUS nt_status; + DATA_BLOB session_key = data_blob(NULL, 0); + + /* parse the NTLMSSP packet */ + *reply = data_blob(NULL, 0); + + if (user_session_key) + dump_data_pw("USER session key:\n", user_session_key->data, user_session_key->length); - dump_data_pw("NT session key:\n", nt_session_key.data, nt_session_key.length); - dump_data_pw("LM first-8:\n", lm_session_key.data, lm_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 (doing_ntlm2) { - if (nt_session_key.data && nt_session_key.length == 16) { + 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->mem_ctx, NULL, 16); - hmac_md5(nt_session_key.data, session_nonce, - sizeof(session_nonce), session_key.data); + 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) { - if (lm_session_key.data && lm_session_key.length >= 8 && - ntlmssp_state->lm_resp.data && ntlmssp_state->lm_resp.length == 24) { - session_key = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 16); - SMBsesskeygen_lmv1(lm_session_key.data, ntlmssp_state->lm_resp.data, - session_key.data); - dump_data_pw("LM session key:\n", session_key.data, session_key.length); + 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->mem_ctx, 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 { + /* use the key unmodified - it's + * probably a NULL key from the guest + * login */ + session_key = *lm_session_key; + } + } else { + DEBUG(10,("ntlmssp_server_auth: Failed to create NTLM session key.\n")); + session_key = data_blob(NULL, 0); } - } else if (nt_session_key.data) { - session_key = nt_session_key; + } 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); + } else if (lm_session_key && lm_session_key->data) { + 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); + } else { + DEBUG(10,("ntlmssp_server_auth: Failed to create unmodified session key.\n")); + session_key = data_blob(NULL, 0); } /* 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 (!encrypted_session_key.data || encrypted_session_key.length != 16) { - data_blob_free(&encrypted_session_key); + 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", - encrypted_session_key.length)); + 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", encrypted_session_key.data, encrypted_session_key.length); - SamOEMhash(encrypted_session_key.data, + dump_data_pw("KEY_EXCH session key (enc):\n", + ntlmssp_state->encrypted_session_key.data, + ntlmssp_state->encrypted_session_key.length); + SamOEMhash(ntlmssp_state->encrypted_session_key.data, session_key.data, - encrypted_session_key.length); + ntlmssp_state->encrypted_session_key.length); ntlmssp_state->session_key = data_blob_talloc(ntlmssp_state->mem_ctx, - encrypted_session_key.data, - encrypted_session_key.length); - dump_data_pw("KEY_EXCH session key:\n", session_key.data, session_key.length); + 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; } - data_blob_free(&encrypted_session_key); + nt_status = ntlmssp_sign_init(ntlmssp_state); + + data_blob_free(&ntlmssp_state->encrypted_session_key); /* allow arbitarily many authentications */ ntlmssp_state->expected_state = NTLMSSP_AUTH; @@ -704,6 +751,47 @@ static NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state, return nt_status; } + +/** + * Next state function for the Authenticate packet + * + * @param ntlmssp_state NTLMSSP State + * @param request The request, as a DATA_BLOB + * @param reply The reply, as an allocated DATA_BLOB, caller to free. + * @return Errors or NT_STATUS_OK. + */ + +static NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state, + const DATA_BLOB request, DATA_BLOB *reply) +{ + DATA_BLOB user_session_key = data_blob(NULL, 0); + DATA_BLOB lm_session_key = data_blob(NULL, 0); + NTSTATUS nt_status; + + /* parse the NTLMSSP packet */ + *reply = data_blob(NULL, 0); + + if (!NT_STATUS_IS_OK(nt_status = ntlmssp_server_preauth(ntlmssp_state, request))) { + 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; + } + + return ntlmssp_server_postauth(ntlmssp_state, &user_session_key, &lm_session_key, reply); +} + /** * Create an NTLMSSP state machine * @@ -874,7 +962,14 @@ static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_state *ntlmssp_state, } if (!ntlmssp_state->password) { + static const uchar zeros[16]; /* do nothing - blobs are zero length */ + + /* session key is all zeros */ + session_key = data_blob_talloc(ntlmssp_state->mem_ctx, 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) { @@ -900,7 +995,7 @@ static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_state *ntlmssp_state, uchar nt_hash[16]; uchar session_nonce[16]; uchar session_nonce_hash[16]; - uchar nt_session_key[16]; + uchar user_session_key[16]; E_md4hash(ntlmssp_state->password, nt_hash); lm_response = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 24); @@ -917,7 +1012,7 @@ static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_state *ntlmssp_state, DEBUG(5, ("NTLMSSP challenge set by NTLM2\n")); DEBUG(5, ("challenge is: \n")); - dump_data(5, session_nonce_hash, 8); + dump_data(5, (const char *)session_nonce_hash, 8); nt_response = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 24); SMBNTencrypt(ntlmssp_state->password, @@ -926,8 +1021,8 @@ static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_state *ntlmssp_state, session_key = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 16); - SMBsesskeygen_ntv1(nt_hash, NULL, nt_session_key); - hmac_md5(nt_session_key, session_nonce, sizeof(session_nonce), session_key.data); + SMBsesskeygen_ntv1(nt_hash, NULL, 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); } else { @@ -964,16 +1059,19 @@ static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_state *ntlmssp_state, /* 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 client_session_key[16]; - - generate_random_buffer(client_session_key, sizeof(client_session_key), False); + generate_random_buffer(client_session_key, sizeof(client_session_key), False); + + /* Encrypt the new session key with the old one */ encrypted_session_key = data_blob(client_session_key, sizeof(client_session_key)); dump_data_pw("KEY_EXCH session key:\n", encrypted_session_key.data, encrypted_session_key.length); - SamOEMhash(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 */ data_blob_free(&session_key); session_key = data_blob_talloc(ntlmssp_state->mem_ctx, client_session_key, sizeof(client_session_key)); - dump_data_pw("KEY_EXCH session key (enc):\n", encrypted_session_key.data, encrypted_session_key.length); } /* this generates the actual auth packet */ @@ -1018,7 +1116,7 @@ NTSTATUS ntlmssp_client_start(NTLMSSP_STATE **ntlmssp_state) *ntlmssp_state = talloc_zero(mem_ctx, sizeof(**ntlmssp_state)); if (!*ntlmssp_state) { - DEBUG(0,("ntlmssp_server_start: talloc failed!\n")); + DEBUG(0,("ntlmssp_client_start: talloc failed!\n")); talloc_destroy(mem_ctx); return NT_STATUS_NO_MEMORY; } diff --git a/source4/libcli/auth/ntlmssp.h b/source4/libcli/auth/ntlmssp.h index 8d2fcab320..d678e26e21 100644 --- a/source4/libcli/auth/ntlmssp.h +++ b/source4/libcli/auth/ntlmssp.h @@ -94,6 +94,13 @@ typedef struct ntlmssp_state DATA_BLOB session_key; uint32 neg_flags; /* the current state of negotiation with the NTLMSSP partner */ + + /* internal variables used by NTLM2 */ + BOOL doing_ntlm2; + uchar session_nonce[16]; + + /* internal variables used by KEY_EXCH (client-supplied user session key */ + DATA_BLOB encrypted_session_key; void *auth_context; diff --git a/source4/libcli/auth/ntlmssp_parse.c b/source4/libcli/auth/ntlmssp_parse.c index 3444db0306..4b3043aec8 100644 --- a/source4/libcli/auth/ntlmssp_parse.c +++ b/source4/libcli/auth/ntlmssp_parse.c @@ -216,7 +216,9 @@ BOOL msrpc_parse(const DATA_BLOB *blob, /* if odd length and unicode */ return False; } - + if (blob->data + ptr < (uint8 *)ptr || blob->data + ptr < blob->data) + return False; + if (0 < len1) { pull_string(NULL, p, blob->data + ptr, sizeof(p), len1, @@ -241,7 +243,10 @@ BOOL msrpc_parse(const DATA_BLOB *blob, if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) { return False; } - + + if (blob->data + ptr < (uint8 *)ptr || blob->data + ptr < blob->data) + return False; + if (0 < len1) { pull_string(NULL, p, blob->data + ptr, sizeof(p), len1, @@ -266,6 +271,10 @@ BOOL msrpc_parse(const DATA_BLOB *blob, if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) { return False; } + + if (blob->data + ptr < (uint8 *)ptr || blob->data + ptr < blob->data) + return False; + *b = data_blob(blob->data + ptr, len1); } break; @@ -274,6 +283,9 @@ BOOL msrpc_parse(const DATA_BLOB *blob, len1 = va_arg(ap, unsigned); /* make sure its in the right format - be strict */ NEED_DATA(len1); + if (blob->data + head_ofs < (uint8 *)head_ofs || blob->data + head_ofs < blob->data) + return False; + *b = data_blob(blob->data + head_ofs, len1); head_ofs += len1; break; @@ -284,6 +296,10 @@ BOOL msrpc_parse(const DATA_BLOB *blob, break; case 'C': s = va_arg(ap, char *); + + if (blob->data + head_ofs < (uint8 *)head_ofs || blob->data + head_ofs < blob->data) + return False; + head_ofs += pull_string(NULL, p, blob->data+head_ofs, sizeof(p), blob->length - head_ofs, STR_ASCII|STR_TERMINATE); diff --git a/source4/libcli/auth/ntlmssp_sign.c b/source4/libcli/auth/ntlmssp_sign.c index 2f510b0f98..5039a842bc 100644 --- a/source4/libcli/auth/ntlmssp_sign.c +++ b/source4/libcli/auth/ntlmssp_sign.c @@ -53,7 +53,7 @@ static void NTLMSSPcalc_ap( unsigned char *hash, unsigned char *data, int len) hash[257] = index_j; } -static void calc_hash(unsigned char *hash, const char *k2, int k2l) +static void calc_hash(unsigned char hash[258], const char *k2, int k2l) { unsigned char j = 0; int ind; @@ -78,7 +78,7 @@ static void calc_hash(unsigned char *hash, const char *k2, int k2l) hash[257] = 0; } -static void calc_ntlmv2_hash(unsigned char hash[16], char digest[16], +static void calc_ntlmv2_hash(unsigned char hash[258], unsigned char digest[16], DATA_BLOB session_key, const char *constant) { @@ -91,8 +91,8 @@ static void calc_ntlmv2_hash(unsigned char hash[16], char digest[16], MD5Init(&ctx3); MD5Update(&ctx3, session_key.data, session_key.length); - MD5Update(&ctx3, (const unsigned char *)constant, strlen(constant)); - MD5Final((unsigned char *)digest, &ctx3); + MD5Update(&ctx3, (const unsigned char *)constant, strlen(constant)+1); + MD5Final(digest, &ctx3); calc_hash(hash, digest, 16); } @@ -109,12 +109,12 @@ static NTSTATUS ntlmssp_make_packet_signature(NTLMSSP_STATE *ntlmssp_state, { if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { HMACMD5Context ctx; - char seq_num[4]; + uchar seq_num[4]; uchar digest[16]; SIVAL(seq_num, 0, ntlmssp_state->ntlmssp_seq_num); hmac_md5_init_limK_to_64((const unsigned char *)(ntlmssp_state->send_sign_const), 16, &ctx); - hmac_md5_update((const unsigned char *)seq_num, 4, &ctx); + hmac_md5_update(seq_num, 4, &ctx); hmac_md5_update(data, length, &ctx); hmac_md5_final(digest, &ctx); @@ -122,13 +122,16 @@ static NTSTATUS ntlmssp_make_packet_signature(NTLMSSP_STATE *ntlmssp_state, , ntlmssp_state->ntlmssp_seq_num)) { return NT_STATUS_NO_MEMORY; } - switch (direction) { - case NTLMSSP_SEND: - NTLMSSPcalc_ap(ntlmssp_state->send_sign_hash, sig->data+4, sig->length-4); - break; - case NTLMSSP_RECEIVE: - NTLMSSPcalc_ap(ntlmssp_state->recv_sign_hash, sig->data+4, sig->length-4); - break; + + if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) { + switch (direction) { + case NTLMSSP_SEND: + NTLMSSPcalc_ap(ntlmssp_state->send_sign_hash, sig->data+4, sig->length-4); + break; + case NTLMSSP_RECEIVE: + NTLMSSPcalc_ap(ntlmssp_state->recv_sign_hash, sig->data+4, sig->length-4); + break; + } } } else { uint32 crc; @@ -145,10 +148,16 @@ static NTSTATUS ntlmssp_make_packet_signature(NTLMSSP_STATE *ntlmssp_state, } NTSTATUS ntlmssp_sign_packet(NTLMSSP_STATE *ntlmssp_state, - const uchar *data, size_t length, - DATA_BLOB *sig) + const uchar *data, size_t length, + DATA_BLOB *sig) { - NTSTATUS nt_status = ntlmssp_make_packet_signature(ntlmssp_state, data, length, NTLMSSP_SEND, sig); + NTSTATUS nt_status; + if (!ntlmssp_state->session_key.length) { + DEBUG(3, ("NO session key, cannot check sign packet\n")); + return NT_STATUS_NO_USER_SESSION_KEY; + } + + nt_status = ntlmssp_make_packet_signature(ntlmssp_state, data, length, NTLMSSP_SEND, sig); /* increment counter on send */ ntlmssp_state->ntlmssp_seq_num++; @@ -168,6 +177,11 @@ NTSTATUS ntlmssp_check_packet(NTLMSSP_STATE *ntlmssp_state, DATA_BLOB local_sig; NTSTATUS nt_status; + if (!ntlmssp_state->session_key.length) { + DEBUG(3, ("NO session key, cannot check packet signature\n")); + return NT_STATUS_NO_USER_SESSION_KEY; + } + if (sig->length < 8) { DEBUG(0, ("NTLMSSP packet check failed due to short signature (%lu bytes)!\n", (unsigned long)sig->length)); @@ -194,8 +208,6 @@ NTSTATUS ntlmssp_check_packet(NTLMSSP_STATE *ntlmssp_state, return NT_STATUS_ACCESS_DENIED; } - data_blob_free(&local_sig); - /* increment counter on recieive */ ntlmssp_state->ntlmssp_seq_num++; @@ -212,6 +224,11 @@ NTSTATUS ntlmssp_seal_packet(NTLMSSP_STATE *ntlmssp_state, uchar *data, size_t length, DATA_BLOB *sig) { + if (!ntlmssp_state->session_key.length) { + DEBUG(3, ("NO session key, cannot seal packet\n")); + return NT_STATUS_NO_USER_SESSION_KEY; + } + DEBUG(10,("ntlmssp_seal_data: seal\n")); dump_data_pw("ntlmssp clear data\n", data, length); if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { @@ -274,6 +291,11 @@ NTSTATUS ntlmssp_unseal_packet(NTLMSSP_STATE *ntlmssp_state, uchar *data, size_t length, DATA_BLOB *sig) { + if (!ntlmssp_state->session_key.length) { + DEBUG(3, ("NO session key, cannot unseal packet\n")); + return NT_STATUS_NO_USER_SESSION_KEY; + } + DEBUG(10,("ntlmssp__unseal_data: seal\n")); dump_data_pw("ntlmssp sealed data\n", data, length); if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { @@ -299,6 +321,11 @@ NTSTATUS ntlmssp_sign_init(NTLMSSP_STATE *ntlmssp_state) DEBUG(3, ("NTLMSSP Sign/Seal - Initialising with flags:\n")); debug_ntlmssp_flags(ntlmssp_state->neg_flags); + if (!ntlmssp_state->session_key.length) { + DEBUG(3, ("NO session key, cannot intialise signing\n")); + return NT_STATUS_NO_USER_SESSION_KEY; + } + if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) { const char *send_sign_const; @@ -337,14 +364,14 @@ NTSTATUS ntlmssp_sign_init(NTLMSSP_STATE *ntlmssp_state) calc_ntlmv2_hash(ntlmssp_state->recv_sign_hash, ntlmssp_state->recv_sign_const, - ntlmssp_state->session_key, send_sign_const); + ntlmssp_state->session_key, recv_sign_const); dump_data_pw("NTLMSSP receive sign hash:\n", ntlmssp_state->recv_sign_hash, sizeof(ntlmssp_state->recv_sign_hash)); calc_ntlmv2_hash(ntlmssp_state->recv_seal_hash, ntlmssp_state->recv_seal_const, - ntlmssp_state->session_key, send_seal_const); + ntlmssp_state->session_key, recv_seal_const); dump_data_pw("NTLMSSP receive seal hash:\n", ntlmssp_state->recv_sign_hash, sizeof(ntlmssp_state->recv_sign_hash)); diff --git a/source4/libcli/config.m4 b/source4/libcli/config.m4 index b5273b9bb2..7fdbe17349 100644 --- a/source4/libcli/config.m4 +++ b/source4/libcli/config.m4 @@ -26,7 +26,8 @@ SMB_SUBSYSTEM(LIBCLI_UTILS,[], SMB_SUBSYSTEM(LIBCLI_AUTH,[], [libcli/auth/ntlmssp.o libcli/auth/ntlmssp_parse.o \ libcli/auth/ntlmssp_sign.o libcli/auth/schannel.o \ - libcli/auth/credentials.o libcli/auth/session.o], + libcli/auth/credentials.o libcli/auth/session.o \ + libcli/auth/ntlm_check.o], libcli/auth/libcli_auth_public_proto.h) SMB_SUBSYSTEM(LIBCLI_NMB,[], diff --git a/source4/libcli/util/smbencrypt.c b/source4/libcli/util/smbencrypt.c index a091805345..4b2c753637 100644 --- a/source4/libcli/util/smbencrypt.c +++ b/source4/libcli/util/smbencrypt.c @@ -28,13 +28,17 @@ /* This implements the X/Open SMB password encryption It takes a password ('unix' string), a 8 byte "crypt key" - and puts 24 bytes of encrypted password into p24 */ -void SMBencrypt(const char *passwd, const uchar *c8, uchar p24[24]) + and puts 24 bytes of encrypted password into p24 + + Returns False if password must have been truncated to create LM hash +*/ +BOOL SMBencrypt(const char *passwd, const uchar *c8, uchar p24[24]) { + BOOL ret; uchar p21[21]; memset(p21,'\0',21); - E_deshash(passwd, p21); + ret = E_deshash(passwd, p21); SMBOWFencrypt(p21, c8, p24); @@ -44,6 +48,8 @@ void SMBencrypt(const char *passwd, const uchar *c8, uchar p24[24]) dump_data(100, (const char *)c8, 8); dump_data(100, (char *)p24, 24); #endif + + return ret; } /** @@ -70,20 +76,29 @@ void E_md4hash(const char *passwd, uchar p16[16]) * Creates the DES forward-only Hash of the users password in DOS ASCII charset * @param passwd password in 'unix' charset. * @param p16 return password hashed with DES, caller allocated 16 byte buffer + * @return False if password was > 14 characters, and therefore may be incorrect, otherwise True + * @note p16 is filled in regardless */ -void E_deshash(const char *passwd, uchar p16[16]) +BOOL E_deshash(const char *passwd, uchar p16[16]) { + BOOL ret = True; fstring dospwd; ZERO_STRUCT(dospwd); /* Password must be converted to DOS charset - null terminated, uppercase. */ push_ascii(dospwd, passwd, sizeof(dospwd), STR_UPPER|STR_TERMINATE); - + /* Only the fisrt 14 chars are considered, password need not be null terminated. */ E_P16((const unsigned char *)dospwd, p16); + if (strlen(dospwd) > 14) { + ret = False; + } + ZERO_STRUCT(dospwd); + + return ret; } /** @@ -118,7 +133,9 @@ void nt_lm_owf_gen(const char *pwd, uchar nt_p16[16], uchar p16[16]) /* Does both the NTLMv2 owfs of a user's password */ BOOL ntv2_owf_gen(const uchar owf[16], - const char *user_in, const char *domain_in, uchar kr_buf[16]) + const char *user_in, const char *domain_in, + BOOL upper_case_domain, /* Transform the domain into UPPER case */ + uchar kr_buf[16]) { smb_ucs2_t *user; smb_ucs2_t *domain; @@ -141,7 +158,9 @@ BOOL ntv2_owf_gen(const uchar owf[16], } strupper_w(user); - strupper_w(domain); + + if (upper_case_domain) + strupper_w(domain); SMB_ASSERT(user_byte_len >= 2); SMB_ASSERT(domain_byte_len >= 2); @@ -217,40 +236,6 @@ void SMBNTencrypt(const char *passwd, uchar *c8, uchar *p24) #endif } -BOOL make_oem_passwd_hash(char data[516], const char *passwd, - const uchar old_pw_hash[16], - BOOL unicode) -{ - int new_pw_len = strlen(passwd) * (unicode ? 2 : 1); - - if (new_pw_len > 512) - { - DEBUG(0,("make_oem_passwd_hash: new password is too long.\n")); - return False; - } - - /* - * Now setup the data area. - * We need to generate a random fill - * for this area to make it harder to - * decrypt. JRA. - */ - generate_random_buffer((unsigned char *)data, 516, False); - push_string(NULL, &data[512 - new_pw_len], passwd, new_pw_len, - STR_NOALIGN | (unicode?STR_UNICODE:STR_ASCII)); - SIVAL(data, 512, new_pw_len); - -#ifdef DEBUG_PASSWORD - DEBUG(100,("make_oem_passwd_hash\n")); - dump_data(100, data, 516); -#endif - SamOEMhash((unsigned char *)data, - (const unsigned char *)old_pw_hash, - 516); - - return True; -} - /* Does the md5 encryption from the Key Response for NTLMv2. */ void SMBOWFencrypt_ntv2(const uchar kr[16], const DATA_BLOB *srv_chal, @@ -329,15 +314,35 @@ void SMBsesskeygen_lmv1(const uchar lm_hash[16], #endif } +void SMBsesskeygen_lm_sess_key(const uchar lm_hash[16], + const uchar lm_resp[24], /* only uses 8 */ + uint8 sess_key[16]) +{ + uchar p24[24]; + uchar partial_lm_hash[16]; + + memcpy(partial_lm_hash, lm_hash, 8); + memset(partial_lm_hash + 8, 0xbd, 8); + + SMBOWFencrypt(partial_lm_hash, lm_resp, p24); + + memcpy(sess_key, p24, 16); + +#ifdef DEBUG_PASSWORD + DEBUG(100, ("SMBsesskeygen_lmv1_jerry:\n")); + dump_data(100, sess_key, 16); +#endif +} + DATA_BLOB NTLMv2_generate_names_blob(const char *hostname, const char *domain) { DATA_BLOB names_blob = data_blob(NULL, 0); msrpc_gen(&names_blob, "aaa", - True, NTLMSSP_NAME_TYPE_DOMAIN, domain, - True, NTLMSSP_NAME_TYPE_SERVER, hostname, - True, 0, ""); + NTLMSSP_NAME_TYPE_DOMAIN, domain, + NTLMSSP_NAME_TYPE_SERVER, hostname, + 0, ""); return names_blob; } @@ -424,7 +429,7 @@ BOOL SMBNTLMv2encrypt(const char *user, const char *domain, const char *password const DATA_BLOB *server_chal, const DATA_BLOB *names_blob, DATA_BLOB *lm_response, DATA_BLOB *nt_response, - DATA_BLOB *nt_session_key) + DATA_BLOB *user_session_key) { uchar nt_hash[16]; uchar ntlm_v2_hash[16]; @@ -434,19 +439,19 @@ BOOL SMBNTLMv2encrypt(const char *user, const char *domain, const char *password the username and domain. This prevents username swapping during the auth exchange */ - if (!ntv2_owf_gen(nt_hash, user, domain, ntlm_v2_hash)) { + if (!ntv2_owf_gen(nt_hash, user, domain, True, ntlm_v2_hash)) { return False; } if (nt_response) { *nt_response = NTLMv2_generate_response(ntlm_v2_hash, server_chal, names_blob); - if (nt_session_key) { - *nt_session_key = data_blob(NULL, 16); + if (user_session_key) { + *user_session_key = data_blob(NULL, 16); /* The NTLMv2 calculations also provide a session key, for signing etc later */ /* use only the first 16 bytes of nt_response for session key */ - SMBsesskeygen_ntv2(ntlm_v2_hash, nt_response->data, nt_session_key->data); + SMBsesskeygen_ntv2(ntlm_v2_hash, nt_response->data, user_session_key->data); } } @@ -471,9 +476,6 @@ BOOL encode_pw_buffer(char buffer[516], const char *password, int string_flags) new_pw_len = push_string(NULL, new_pw, password, sizeof(new_pw), string_flags); - if (new_pw_len > 512) { - return False; - } memcpy(&buffer[512 - new_pw_len], new_pw, new_pw_len); @@ -495,13 +497,14 @@ BOOL encode_pw_buffer(char buffer[516], const char *password, int string_flags) returned password including termination. ************************************************************/ BOOL decode_pw_buffer(char in_buffer[516], char *new_pwrd, - int new_pwrd_size, uint32 *new_pw_len) + int new_pwrd_size, uint32 *new_pw_len, + int string_flags) { int byte_len=0; /* Warning !!! : This function is called from some rpc call. - The password IN the buffer is a UNICODE string. + The password IN the buffer may be a UNICODE string. The password IN new_pwrd is an ASCII string If you reuse that code somewhere else check first. */ @@ -514,15 +517,16 @@ BOOL decode_pw_buffer(char in_buffer[516], char *new_pwrd, dump_data(100, in_buffer, 516); #endif - /* Password cannot be longer than 128 characters */ - if ( (byte_len < 0) || (byte_len > new_pwrd_size - 1)) { + /* Password cannot be longer than the size of the password buffer */ + if ( (byte_len < 0) || (byte_len > 512)) { DEBUG(0, ("decode_pw_buffer: incorrect password length (%d).\n", byte_len)); DEBUG(0, ("decode_pw_buffer: check that 'encrypt passwords = yes'\n")); return False; } - /* decode into the return buffer. Buffer must be a pstring */ - *new_pw_len = pull_string(NULL, new_pwrd, &in_buffer[512 - byte_len], new_pwrd_size, byte_len, STR_UNICODE); + /* decode into the return buffer. Buffer length supplied */ + *new_pw_len = pull_string(NULL, new_pwrd, &in_buffer[512 - byte_len], new_pwrd_size, + byte_len, string_flags); #ifdef DEBUG_PASSWORD DEBUG(100,("decode_pw_buffer: new_pwrd: ")); @@ -533,4 +537,3 @@ BOOL decode_pw_buffer(char in_buffer[516], char *new_pwrd, return True; } - diff --git a/source4/smb_server/password.c b/source4/smb_server/password.c index 2811a14c21..196556819e 100644 --- a/source4/smb_server/password.c +++ b/source4/smb_server/password.c @@ -60,6 +60,8 @@ void invalidate_vuid(struct server_context *smb, uint16 vuid) SAFE_FREE(vuser->unix_homedir); SAFE_FREE(vuser->logon_script); + data_blob_free(&vuser->session_key); + session_yield(vuser); free_server_info(&vuser->server_info); @@ -95,6 +97,10 @@ void invalidate_all_vuids(struct server_context *smb) * @param server_info The token returned from the authentication process. * (now 'owned' by register_vuid) * + * @param session_key The User session key for the login session (now also 'owned' by register_vuid) + * + * @param smb_name The untranslated name of the user + * * @return Newly allocated vuid, biased by an offset. (This allows us to * tell random client vuid's (normally zero) from valid vuids.) * @@ -102,6 +108,7 @@ void invalidate_all_vuids(struct server_context *smb) int register_vuid(struct server_context *smb, struct auth_serversupplied_info *server_info, + DATA_BLOB *session_key, const char *smb_name) { user_struct *vuser = NULL; @@ -184,7 +191,7 @@ int register_vuid(struct server_context *smb, } } - memcpy(vuser->session_key, server_info->session_key, sizeof(vuser->session_key)); + vuser->session_key = *session_key; DEBUG(10,("register_vuid: (%u,%u) %s %s %s guest=%d\n", (unsigned int)vuser->uid, diff --git a/source4/smb_server/sesssetup.c b/source4/smb_server/sesssetup.c index 14e300c191..f42efcb7ec 100644 --- a/source4/smb_server/sesssetup.c +++ b/source4/smb_server/sesssetup.c @@ -44,6 +44,7 @@ static NTSTATUS sesssetup_old(struct request_context *req, union smb_sesssetup * auth_usersupplied_info *user_info = NULL; auth_serversupplied_info *server_info = NULL; DATA_BLOB null_blob; + DATA_BLOB session_key; if (!req->smb->negotiate.done_sesssetup) { req->smb->negotiate.max_send = sess->old.in.bufsize; @@ -63,11 +64,17 @@ static NTSTATUS sesssetup_old(struct request_context *req, union smb_sesssetup * user_info, &server_info); if (!NT_STATUS_IS_OK(status)) { - return NT_STATUS_ACCESS_DENIED; + return nt_status_squash(status); + } + + if (server_info->user_session_key.data) { + session_key = data_blob(server_info->user_session_key.data, server_info->user_session_key.length); + } else { + session_key = data_blob(NULL, 0); } sess->old.out.action = 0; - sess->old.out.vuid = register_vuid(req->smb, server_info, sess->old.in.user); + sess->old.out.vuid = register_vuid(req->smb, server_info, &session_key, sess->old.in.user); sesssetup_common_strings(req, &sess->old.out.os, &sess->old.out.lanman, @@ -85,6 +92,7 @@ static NTSTATUS sesssetup_nt1(struct request_context *req, union smb_sesssetup * NTSTATUS status; auth_usersupplied_info *user_info = NULL; auth_serversupplied_info *server_info = NULL; + DATA_BLOB session_key; if (!req->smb->negotiate.done_sesssetup) { req->smb->negotiate.max_send = sess->nt1.in.bufsize; @@ -103,11 +111,17 @@ static NTSTATUS sesssetup_nt1(struct request_context *req, union smb_sesssetup * user_info, &server_info); if (!NT_STATUS_IS_OK(status)) { - return NT_STATUS_ACCESS_DENIED; + return nt_status_squash(status); + } + + if (server_info->user_session_key.data) { + session_key = data_blob(server_info->user_session_key.data, server_info->user_session_key.length); + } else { + session_key = data_blob(NULL, 0); } sess->nt1.out.action = 0; - sess->nt1.out.vuid = register_vuid(req->smb, server_info, sess->old.in.user); + sess->nt1.out.vuid = register_vuid(req->smb, server_info, &session_key, sess->old.in.user); sesssetup_common_strings(req, &sess->nt1.out.os, &sess->nt1.out.lanman, |