diff options
Diffstat (limited to 'source3/auth')
-rw-r--r-- | source3/auth/auth.c | 456 | ||||
-rw-r--r-- | source3/auth/auth_builtin.c | 111 | ||||
-rw-r--r-- | source3/auth/auth_compat.c | 119 | ||||
-rw-r--r-- | source3/auth/auth_domain.c | 571 | ||||
-rw-r--r-- | source3/auth/auth_rhosts.c | 231 | ||||
-rw-r--r-- | source3/auth/auth_sam.c | 452 | ||||
-rw-r--r-- | source3/auth/auth_server.c | 367 | ||||
-rw-r--r-- | source3/auth/auth_unix.c | 129 | ||||
-rw-r--r-- | source3/auth/auth_util.c | 685 | ||||
-rw-r--r-- | source3/auth/auth_winbind.c | 111 | ||||
-rw-r--r-- | source3/auth/pampass.c | 872 | ||||
-rw-r--r-- | source3/auth/pass_check.c | 793 |
12 files changed, 4897 insertions, 0 deletions
diff --git a/source3/auth/auth.c b/source3/auth/auth.c new file mode 100644 index 0000000000..c7b9fcc1d8 --- /dev/null +++ b/source3/auth/auth.c @@ -0,0 +1,456 @@ +/* + Unix SMB/CIFS implementation. + Password and authentication handling + Copyright (C) Andrew Bartlett 2001-2002 + + 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" + +/** List of various built-in authenticaion modules */ + +const struct auth_init_function builtin_auth_init_functions[] = { + { "guest", auth_init_guest }, + { "rhosts", auth_init_rhosts }, + { "hostsequiv", auth_init_hostsequiv }, + { "sam", auth_init_sam }, + { "samstrict", auth_init_samstrict }, + { "unix", auth_init_unix }, + { "smbserver", auth_init_smbserver }, + { "ntdomain", auth_init_ntdomain }, + { "trustdomain", auth_init_trustdomain }, + { "winbind", auth_init_winbind }, +#ifdef DEVELOPER + { "name_to_ntstatus", auth_init_name_to_ntstatus }, +#endif + { NULL, NULL} +}; + +/**************************************************************************** + Try to get a challenge out of the various authenticaion modules. + Returns a const char of length 8 bytes. +****************************************************************************/ + +static const uint8 *get_ntlm_challenge(struct auth_context *auth_context) +{ + DATA_BLOB challenge = data_blob(NULL, 0); + char *challenge_set_by = NULL; + auth_methods *auth_method; + TALLOC_CTX *mem_ctx; + + if (auth_context->challenge.length) { + DEBUG(5, ("get_ntlm_challenge (auth subsystem): returning previous challenge (normal)\n")); + return auth_context->challenge.data; + } + + 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)); + continue; + } + + DEBUG(5, ("auth_get_challenge: getting challenge from module %s\n", auth_method->name)); + if (challenge_set_by != NULL) { + DEBUG(1, ("auth_get_challenge: CONFIGURATION ERROR: authenticaion method %s has already specified a challenge. Challenge by %s ignored.\n", + challenge_set_by, auth_method->name)); + continue; + } + + mem_ctx = talloc_init_named("auth_get_challenge for module %s", auth_method->name); + if (!mem_ctx) { + smb_panic("talloc_init_named() failed!"); + } + + challenge = auth_method->get_chal(auth_context, &auth_method->private_data, mem_ctx); + if (!challenge.length) { + DEBUG(3, ("auth_get_challenge: getting challenge from authenticaion method %s FAILED.\n", + auth_method->name)); + } else { + DEBUG(5, ("auth_get_challenge: sucessfully got challenge from module %s\n", auth_method->name)); + auth_context->challenge = challenge; + challenge_set_by = auth_method->name; + auth_context->challenge_set_method = auth_method; + } + talloc_destroy(mem_ctx); + } + + if (!challenge_set_by) { + uchar chal[8]; + + generate_random_buffer(chal, sizeof(chal), False); + auth_context->challenge = data_blob_talloc(auth_context->mem_ctx, + chal, sizeof(chal)); + + challenge_set_by = "random"; + } + + DEBUG(5, ("auth_context challenge created by %s\n", challenge_set_by)); + DEBUG(5, ("challenge is: \n")); + dump_data(5, auth_context->challenge.data, auth_context->challenge.length); + + SMB_ASSERT(auth_context->challenge.length == 8); + + auth_context->challenge_set_by=challenge_set_by; + + return auth_context->challenge.data; +} + + +/** + * Check user is in correct domain (if required) + * + * @param user Only used to fill in the debug message + * + * @param domain The domain to be verified + * + * @return True if the user can connect with that domain, + * False otherwise. +**/ + +static BOOL check_domain_match(const char *user, const char *domain) +{ + /* + * If we aren't serving to trusted domains, we must make sure that + * the validation request comes from an account in the same domain + * as the Samba server + */ + + if (!lp_allow_trusted_domains() && + !(strequal("", domain) || + strequal(lp_workgroup(), domain) || + is_netbios_alias_or_name(domain))) { + DEBUG(1, ("check_domain_match: Attempt to connect as user %s from domain %s denied.\n", user, domain)); + return False; + } else { + return True; + } +} + +/** + * Check a user's Plaintext, LM or NTLM password. + * + * Check a user's password, as given in the user_info struct and return various + * interesting details in the server_info struct. + * + * This function does NOT need to be in a become_root()/unbecome_root() pair + * as it makes the calls itself when needed. + * + * The return value takes precedence over the contents of the server_info + * struct. When the return is other than NT_STATUS_OK the contents + * of that structure is undefined. + * + * @param user_info Contains the user supplied components, including the passwords. + * Must be created with make_user_info() or one of its wrappers. + * + * @param auth_info Supplies the challenges and some other data. + * Must be created with make_auth_info(), and the challenges should be + * filled in, either at creation or by calling the challenge geneation + * function auth_get_challenge(). + * + * @param server_info If successful, contains information about the authenticaion, + * including a SAM_ACCOUNT struct describing the user. + * + * @return An NTSTATUS with NT_STATUS_OK or an appropriate error. + * + **/ + +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; + auth_methods *auth_method; + TALLOC_CTX *mem_ctx; + + if (!user_info || !auth_context || !server_info) { + return NT_STATUS_LOGON_FAILURE; + } + + DEBUG(3, ("check_password: Checking password for unmapped user [%s]\\[%s]@[%s] with the new password interface\n", + user_info->client_domain.str, user_info->smb_name.str, user_info->wksta_name.str)); + + DEBUG(3, ("check_password: mapped user is: [%s]\\[%s]@[%s]\n", + user_info->domain.str, user_info->internal_username.str, user_info->wksta_name.str)); + if (auth_context->challenge_set_by) { + DEBUG(10, ("auth_context challenge created by %s\n", auth_context->challenge_set_by)); + } + DEBUG(10, ("challenge is: \n")); + dump_data(5, auth_context->challenge.data, auth_context->challenge.length); + +#ifdef DEBUG_PASSWORD + DEBUG(100, ("user_info has passwords of length %d and %d\n", + user_info->lm_resp.length, user_info->nt_resp.length)); + DEBUG(100, ("lm:\n")); + dump_data(100, user_info->lm_resp.data, user_info->lm_resp.length); + DEBUG(100, ("nt:\n")); + dump_data(100, user_info->nt_resp.data, user_info->nt_resp.length); +#endif + + /* This needs to be sorted: If it doesn't match, what should we do? */ + if (!check_domain_match(user_info->smb_name.str, user_info->domain.str)) { + return NT_STATUS_LOGON_FAILURE; + } + + for (auth_method = auth_context->auth_method_list;auth_method; auth_method = auth_method->next) + { + mem_ctx = talloc_init_named("%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); + if (NT_STATUS_IS_OK(nt_status)) { + DEBUG(3, ("check_password: %s authentication for user [%s] suceeded\n", + auth_method->name, user_info->smb_name.str)); + } else { + DEBUG(5, ("check_password: %s authentication for user [%s] FAILED with error %s\n", + auth_method->name, user_info->smb_name.str, nt_errstr(nt_status))); + } + + talloc_destroy(mem_ctx); + + if (NT_STATUS_IS_OK(nt_status)) { + break; + } + } + + /* This is one of the few places the *relies* (rather than just sets defaults + on the value of lp_security(). This needs to change. A new paramater + perhaps? */ + if (lp_security() >= SEC_SERVER) { + 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_password: PAM Account for user [%s] suceeded\n", + pdb_username)); + } else { + DEBUG(3, ("check_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_password: %sauthenticaion for user [%s] -> [%s] -> [%s] suceeded\n", + (*server_info)->guest ? "guest " : "", + user_info->smb_name.str, + user_info->internal_username.str, + pdb_username)); + } + } + + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(2, ("check_password: Authenticaion for user [%s] -> [%s] FAILED with error %s\n", + user_info->smb_name.str, user_info->internal_username.str, + nt_errstr(nt_status))); + ZERO_STRUCTP(server_info); + } + return nt_status; +} + +/*************************************************************************** + Clear out a auth_context, and destroy the attached TALLOC_CTX +***************************************************************************/ + +static void free_auth_context(struct auth_context **auth_context) +{ + if (*auth_context != NULL) { + talloc_destroy((*auth_context)->mem_ctx); + } + *auth_context = NULL; +} + +/*************************************************************************** + Make a auth_info struct +***************************************************************************/ + +static NTSTATUS make_auth_context(struct auth_context **auth_context) +{ + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_init_named("authentication context"); + + *auth_context = talloc(mem_ctx, sizeof(**auth_context)); + if (!*auth_context) { + DEBUG(0,("make_auth_context: talloc failed!\n")); + talloc_destroy(mem_ctx); + return NT_STATUS_NO_MEMORY; + } + ZERO_STRUCTP(*auth_context); + + (*auth_context)->mem_ctx = mem_ctx; + (*auth_context)->check_ntlm_password = check_ntlm_password; + (*auth_context)->get_ntlm_challenge = get_ntlm_challenge; + (*auth_context)->free = free_auth_context; + + return NT_STATUS_OK; +} + +/*************************************************************************** + Make a auth_info struct for the auth subsystem +***************************************************************************/ + +static NTSTATUS make_auth_context_text_list(struct auth_context **auth_context, char **text_list) +{ + auth_methods *list = NULL; + auth_methods *t = NULL; + auth_methods *tmp; + int i; + NTSTATUS nt_status; + + if (!text_list) { + DEBUG(2,("No auth method list!?\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + if (!NT_STATUS_IS_OK(nt_status = make_auth_context(auth_context))) { + return nt_status; + } + + for (;*text_list; text_list++) + { + DEBUG(5,("Attempting to find an auth method to match %s\n", *text_list)); + for (i = 0; builtin_auth_init_functions[i].name; i++) + { + if (strequal(builtin_auth_init_functions[i].name, *text_list)) + { + DEBUG(5,("Found auth method %s (at pos %d)\n", *text_list, i)); + if (builtin_auth_init_functions[i].init(*auth_context, &t)) { + DEBUG(5,("auth method %s has a valid init\n", *text_list)); + t->name = builtin_auth_init_functions[i].name; + DLIST_ADD_END(list, t, tmp); + } else { + DEBUG(0,("auth method %s did not correctly init\n", *text_list)); + } + break; + } + } + } + + (*auth_context)->auth_method_list = list; + + return nt_status; +} + +/*************************************************************************** + Make a auth_context struct for the auth subsystem +***************************************************************************/ + +NTSTATUS make_auth_context_subsystem(struct auth_context **auth_context) +{ + char **auth_method_list = NULL; + NTSTATUS nt_status; + + if (lp_auth_methods() && !lp_list_copy(&auth_method_list, lp_auth_methods())) { + return NT_STATUS_NO_MEMORY; + } + + if (auth_method_list == NULL) { + switch (lp_security()) + { + case SEC_DOMAIN: + DEBUG(5,("Making default auth method list for security=domain\n")); + auth_method_list = lp_list_make("guest samstrict ntdomain"); + break; + case SEC_SERVER: + DEBUG(5,("Making default auth method list for security=server\n")); + auth_method_list = lp_list_make("guest samstrict smbserver"); + break; + case SEC_USER: + if (lp_encrypted_passwords()) { + DEBUG(5,("Making default auth method list for security=user, encrypt passwords = yes\n")); + auth_method_list = lp_list_make("guest sam"); + } else { + DEBUG(5,("Making default auth method list for security=user, encrypt passwords = no\n")); + auth_method_list = lp_list_make("guest unix"); + } + break; + case SEC_SHARE: + if (lp_encrypted_passwords()) { + DEBUG(5,("Making default auth method list for security=share, encrypt passwords = yes\n")); + auth_method_list = lp_list_make("guest sam"); + } else { + DEBUG(5,("Making default auth method list for security=share, encrypt passwords = no\n")); + auth_method_list = lp_list_make("guest unix"); + } + break; + case SEC_ADS: + DEBUG(5,("Making default auth method list for security=ADS\n")); + auth_method_list = lp_list_make("guest samstrict ads ntdomain"); + break; + default: + DEBUG(5,("Unknown auth method!\n")); + return NT_STATUS_UNSUCCESSFUL; + } + } else { + DEBUG(5,("Using specified auth order\n")); + } + + if (!NT_STATUS_IS_OK(nt_status = make_auth_context_text_list(auth_context, auth_method_list))) { + lp_list_free(&auth_method_list); + return nt_status; + } + + lp_list_free(&auth_method_list); + return nt_status; +} + +/*************************************************************************** + Make a auth_info struct with a random challenge +***************************************************************************/ + +NTSTATUS make_auth_context_random(struct auth_context **auth_context) +{ + uchar chal[8]; + NTSTATUS nt_status; + if (!NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(auth_context))) { + return nt_status; + } + + generate_random_buffer(chal, sizeof(chal), False); + (*auth_context)->challenge = data_blob(chal, sizeof(chal)); + + (*auth_context)->challenge_set_by = "random"; + + return nt_status; +} + +/*************************************************************************** + Make a auth_info struct with a fixed challenge +***************************************************************************/ + +NTSTATUS make_auth_context_fixed(struct auth_context **auth_context, uchar chal[8]) +{ + NTSTATUS nt_status; + if (!NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(auth_context))) { + return nt_status; + } + + (*auth_context)->challenge = data_blob(chal, 8); + return nt_status; +} + + diff --git a/source3/auth/auth_builtin.c b/source3/auth/auth_builtin.c new file mode 100644 index 0000000000..6e999b0d14 --- /dev/null +++ b/source3/auth/auth_builtin.c @@ -0,0 +1,111 @@ +/* + Unix SMB/CIFS implementation. + Generic authenticaion types + Copyright (C) Andrew Bartlett 2001 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/** + * Return a guest logon for guest users (username = "") + * + * Typically used as the first module in the auth chain, this allows + * guest logons to be delt with in one place. Non-gust logons 'fail' + * and pass onto the next module. + **/ + +static NTSTATUS check_guest_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) +{ + NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE; + + if (!(user_info->internal_username.str + && *user_info->internal_username.str)) { + if (make_server_info_guest(server_info)) { + nt_status = NT_STATUS_OK; + } else { + nt_status = NT_STATUS_NO_SUCH_USER; + } + } + + return nt_status; +} + +/* Guest modules initialisation */ +BOOL auth_init_guest(struct auth_context *auth_context, auth_methods **auth_method) +{ + if (!make_auth_methods(auth_context, auth_method)) { + return False; + } + + (*auth_method)->auth = check_guest_security; + return True; +} + +/** + * Return an error based on username + * + * This function allows the testing of obsure errors, as well as the generation + * of NT_STATUS -> DOS error mapping tables. + * + * This module is of no value to end-users. + * + * The password is ignored. + * + * @return An NTSTATUS value based on the username + **/ + +static NTSTATUS check_name_to_ntstatus_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) +{ + NTSTATUS nt_status; + fstring user; + long error_num; + fstrcpy(user, user_info->smb_name.str); + + if (strncasecmp("NT_STATUS", user, strlen("NT_STATUS")) == 0) { + strupper(user); + return nt_status_string_to_code(user); + } + + strlower(user); + error_num = strtoul(user, NULL, 16); + + DEBUG(5,("Error for user %s was %lx\n", user, error_num)); + + nt_status = NT_STATUS(error_num); + + return nt_status; +} + +/** Module initailisation function */ +BOOL auth_init_name_to_ntstatus(struct auth_context *auth_context, auth_methods **auth_method) +{ + if (!make_auth_methods(auth_context, auth_method)) { + return False; + } + + (*auth_method)->auth = check_name_to_ntstatus_security; + return True; +} + diff --git a/source3/auth/auth_compat.c b/source3/auth/auth_compat.c new file mode 100644 index 0000000000..857cf2b7d9 --- /dev/null +++ b/source3/auth/auth_compat.c @@ -0,0 +1,119 @@ +/* + Unix SMB/CIFS implementation. + Password and authentication handling + Copyright (C) Andrew Bartlett 2001-2002 + + 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" + +/**************************************************************************** + COMPATIBILITY INTERFACES: + ***************************************************************************/ + +/**************************************************************************** +check if a username/password is OK assuming the password is a 24 byte +SMB hash +return True if the password is correct, False otherwise +****************************************************************************/ + +NTSTATUS check_plaintext_password(const char *smb_name, DATA_BLOB plaintext_password, auth_serversupplied_info **server_info) +{ + struct auth_context *plaintext_auth_context = NULL; + auth_usersupplied_info *user_info = NULL; + const uint8 *chal; + NTSTATUS nt_status; + if (!NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(&plaintext_auth_context))) { + return nt_status; + } + + chal = plaintext_auth_context->get_ntlm_challenge(plaintext_auth_context); + + if (!make_user_info_for_reply(&user_info, + smb_name, lp_workgroup(), chal, + plaintext_password)) { + return NT_STATUS_NO_MEMORY; + } + + nt_status = plaintext_auth_context->check_ntlm_password(plaintext_auth_context, + user_info, server_info); + + (plaintext_auth_context->free)(&plaintext_auth_context); + free_user_info(&user_info); + return nt_status; +} + +static NTSTATUS pass_check_smb(const char *smb_name, + const char *domain, + DATA_BLOB lm_pwd, + DATA_BLOB nt_pwd, + DATA_BLOB plaintext_password, + BOOL encrypted) + +{ + NTSTATUS nt_status; + extern struct auth_context *negprot_global_auth_context; + auth_serversupplied_info *server_info = NULL; + if (encrypted) { + auth_usersupplied_info *user_info = NULL; + make_user_info_for_reply_enc(&user_info, smb_name, + domain, + lm_pwd, + nt_pwd); + nt_status = negprot_global_auth_context->check_ntlm_password(negprot_global_auth_context, + user_info, &server_info); + free_user_info(&user_info); + } else { + nt_status = check_plaintext_password(smb_name, plaintext_password, &server_info); + } + free_server_info(&server_info); + return nt_status; +} + +/**************************************************************************** +check if a username/password pair is ok via the auth subsystem. +return True if the password is correct, False otherwise +****************************************************************************/ +BOOL password_ok(char *smb_name, DATA_BLOB password_blob) +{ + + DATA_BLOB null_password = data_blob(NULL, 0); + extern BOOL global_encrypted_passwords_negotiated; + BOOL encrypted = (global_encrypted_passwords_negotiated && password_blob.length == 24); + + if (encrypted) { + /* + * The password could be either NTLM or plain LM. Try NTLM first, + * but fall-through as required. + * NTLMv2 makes no sense here. + */ + if (NT_STATUS_IS_OK(pass_check_smb(smb_name, lp_workgroup(), null_password, password_blob, null_password, encrypted))) { + return True; + } + + if (NT_STATUS_IS_OK(pass_check_smb(smb_name, lp_workgroup(), password_blob, null_password, null_password, encrypted))) { + return True; + } + } else { + if (NT_STATUS_IS_OK(pass_check_smb(smb_name, lp_workgroup(), null_password, null_password, password_blob, encrypted))) { + return True; + } + } + + return False; +} + + diff --git a/source3/auth/auth_domain.c b/source3/auth/auth_domain.c new file mode 100644 index 0000000000..af353ef812 --- /dev/null +++ b/source3/auth/auth_domain.c @@ -0,0 +1,571 @@ +/* + Unix SMB/CIFS implementation. + Authenticate against a remote domain + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Andrew Bartlett 2001 + + 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" + +BOOL global_machine_password_needs_changing = False; + +extern pstring global_myname; +extern userdom_struct current_user_info; + +/** + * Connect to a remote server for domain security authenticaion. + * + * @param cli the cli to return containing the active connection + * @param server either a machine name or text IP address to + * connect to. + * @param trust_password the trust password to establish the + * credentials with. + * + **/ + +static NTSTATUS connect_to_domain_password_server(struct cli_state **cli, + const char *server, + const char *setup_creds_as, + uint16 sec_chan, + const unsigned char *trust_passwd) +{ + struct in_addr dest_ip; + fstring remote_machine; + NTSTATUS result; + + if (is_ipaddress(server)) { + struct in_addr to_ip; + + /* we shouldn't have 255.255.255.255 forthe IP address of + a password server anyways */ + if ((to_ip.s_addr=inet_addr(server)) == 0xFFFFFFFF) { + DEBUG (0,("connect_to_domain_password_server: inet_addr(%s) returned 0xFFFFFFFF!\n", server)); + return NT_STATUS_UNSUCCESSFUL; + } + + if (!name_status_find("*", 0x20, 0x20, to_ip, remote_machine)) { + DEBUG(0, ("connect_to_domain_password_server: Can't " + "resolve name for IP %s\n", server)); + return NT_STATUS_UNSUCCESSFUL; + } + } else { + fstrcpy(remote_machine, server); + } + + standard_sub_basic(current_user_info.smb_name, remote_machine); + strupper(remote_machine); + + if(!resolve_name( remote_machine, &dest_ip, 0x20)) { + DEBUG(1,("connect_to_domain_password_server: Can't resolve address for %s\n", remote_machine)); + return NT_STATUS_UNSUCCESSFUL; + } + + if (ismyip(dest_ip)) { + DEBUG(1,("connect_to_domain_password_server: Password server loop - not using password server %s\n", + remote_machine)); + return NT_STATUS_UNSUCCESSFUL; + } + + /* TODO: Send a SAMLOGON request to determine whether this is a valid + logonserver. We can avoid a 30-second timeout if the DC is down + if the SAMLOGON request fails as it is only over UDP. */ + + /* we use a mutex to prevent two connections at once - when a NT PDC gets + two connections where one hasn't completed a negprot yet it will send a + TCP reset to the first connection (tridge) */ + if (!message_named_mutex(server, 20)) { + DEBUG(1,("connect_to_domain_password_server: domain mutex failed for %s\n", server)); + return NT_STATUS_UNSUCCESSFUL; + } + + /* Attempt connection */ + result = cli_full_connection(cli, global_myname, server, + &dest_ip, 0, "IPC$", "IPC", "", "", "", 0); + + message_named_mutex_release(server); + + if (!NT_STATUS_IS_OK(result)) { + return result; + } + + /* + * We now have an anonymous connection to IPC$ on the domain password server. + */ + + /* + * Even if the connect succeeds we need to setup the netlogon + * pipe here. We do this as we may just have changed the domain + * account password on the PDC and yet we may be talking to + * a BDC that doesn't have this replicated yet. In this case + * a successful connect to a DC needs to take the netlogon connect + * into account also. This patch from "Bjart Kvarme" <bjart.kvarme@usit.uio.no>. + */ + + if(cli_nt_session_open(*cli, PIPE_NETLOGON) == False) { + DEBUG(0,("connect_to_domain_password_server: unable to open the domain client session to \ +machine %s. Error was : %s.\n", remote_machine, cli_errstr(*cli))); + cli_nt_session_close(*cli); + cli_ulogoff(*cli); + cli_shutdown(*cli); + return NT_STATUS_UNSUCCESSFUL; + } + + snprintf((*cli)->mach_acct, sizeof((*cli)->mach_acct) - 1, "%s$", setup_creds_as); + + if (!(*cli)->mach_acct) { + return NT_STATUS_NO_MEMORY; + } + + result = new_cli_nt_setup_creds(*cli, sec_chan, trust_passwd); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(0,("connect_to_domain_password_server: unable to setup the PDC credentials to machine \ +%s. Error was : %s.\n", remote_machine, nt_errstr(result))); + cli_nt_session_close(*cli); + cli_ulogoff(*cli); + cli_shutdown(*cli); + return result; + } + + return NT_STATUS_OK; +} + +/*********************************************************************** + Utility function to attempt a connection to an IP address of a DC. +************************************************************************/ + +static NTSTATUS attempt_connect_to_dc(struct cli_state **cli, + const char *domain, + struct in_addr *ip, + const char *setup_creds_as, + uint16 sec_chan, + const unsigned char *trust_passwd) +{ + fstring dc_name; + + /* + * Ignore addresses we have already tried. + */ + + if (is_zero_ip(*ip)) + return NT_STATUS_UNSUCCESSFUL; + + if (!lookup_dc_name(global_myname, domain, ip, dc_name)) + return NT_STATUS_UNSUCCESSFUL; + + return connect_to_domain_password_server(cli, dc_name, setup_creds_as, sec_chan, trust_passwd); +} + +/*********************************************************************** + We have been asked to dynamcially determine the IP addresses of + the PDC and BDC's for DOMAIN, and query them in turn. +************************************************************************/ +static NTSTATUS find_connect_pdc(struct cli_state **cli, + const char *domain, + const char *setup_creds_as, + uint16 sec_chan, + unsigned char *trust_passwd, + time_t last_change_time) +{ + struct in_addr *ip_list = NULL; + int count = 0; + int i; + NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; + time_t time_now = time(NULL); + BOOL use_pdc_only = False; + + /* + * If the time the machine password has changed + * was less than an hour ago then we need to contact + * the PDC only, as we cannot be sure domain replication + * has yet taken place. Bug found by Gerald (way to go + * Gerald !). JRA. + */ + + if (time_now - last_change_time < 3600) + use_pdc_only = True; + + if (!get_dc_list(use_pdc_only, domain, &ip_list, &count)) + return NT_STATUS_UNSUCCESSFUL; + + /* + * Firstly try and contact a PDC/BDC who has the same + * network address as any of our interfaces. + */ + for(i = 0; i < count; i++) { + if(!is_local_net(ip_list[i])) + continue; + + if(NT_STATUS_IS_OK(nt_status = + attempt_connect_to_dc(cli, domain, + &ip_list[i], setup_creds_as, + sec_chan, trust_passwd))) + break; + + zero_ip(&ip_list[i]); /* Tried and failed. */ + } + + /* + * Secondly try and contact a random PDC/BDC. + */ + if(!NT_STATUS_IS_OK(nt_status)) { + i = (sys_random() % count); + + if (!is_zero_ip(ip_list[i])) { + if (!NT_STATUS_IS_OK(nt_status = + attempt_connect_to_dc(cli, domain, + &ip_list[i], setup_creds_as, + sec_chan, trust_passwd))) + zero_ip(&ip_list[i]); /* Tried and failed. */ + } + } + + /* + * Finally go through the IP list in turn, ignoring any addresses + * we have already tried. + */ + if(!NT_STATUS_IS_OK(nt_status)) { + /* + * Try and connect to any of the other IP addresses in the PDC/BDC list. + * Note that from a WINS server the #1 IP address is the PDC. + */ + for(i = 0; i < count; i++) { + if (is_zero_ip(ip_list[i])) + continue; + + if (NT_STATUS_IS_OK(nt_status = attempt_connect_to_dc(cli, domain, + &ip_list[i], setup_creds_as, sec_chan, trust_passwd))) + break; + } + } + + SAFE_FREE(ip_list); + return nt_status; +} + +/*********************************************************************** + Do the same as security=server, but using NT Domain calls and a session + key from the machine password. If the server parameter is specified + use it, otherwise figure out a server from the 'password server' param. +************************************************************************/ + +static NTSTATUS domain_client_validate(TALLOC_CTX *mem_ctx, + const auth_usersupplied_info *user_info, + const char *domain, + uchar chal[8], + auth_serversupplied_info **server_info, + char *server, char *setup_creds_as, + uint16 sec_chan, + unsigned char *trust_passwd, + time_t last_change_time) +{ + fstring remote_machine; + NET_USER_INFO_3 info3; + struct cli_state *cli = NULL; + NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; + struct passwd *pass; + + /* + * At this point, smb_apasswd points to the lanman response to + * the challenge in local_challenge, and smb_ntpasswd points to + * the NT response to the challenge in local_challenge. Ship + * these over the secure channel to a domain controller and + * see if they were valid. + */ + + while (!NT_STATUS_IS_OK(nt_status) && + next_token(&server,remote_machine,LIST_SEP,sizeof(remote_machine))) { + if(strequal(remote_machine, "*")) { + nt_status = find_connect_pdc(&cli, domain, setup_creds_as, sec_chan, trust_passwd, last_change_time); + } else { + nt_status = connect_to_domain_password_server(&cli, remote_machine, setup_creds_as, sec_chan, trust_passwd); + } + } + + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0,("domain_client_validate: Domain password server not available.\n")); + return nt_status; + } + + ZERO_STRUCT(info3); + + /* + * If this call succeeds, we now have lots of info about the user + * in the info3 structure. + */ + + nt_status = cli_netlogon_sam_network_logon(cli, mem_ctx, + user_info->smb_name.str, user_info->domain.str, + user_info->wksta_name.str, chal, + user_info->lm_resp, user_info->nt_resp, + &info3); + + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0,("domain_client_validate: unable to validate password " + "for user %s in domain %s to Domain controller %s. " + "Error was %s.\n", user_info->smb_name.str, + user_info->domain.str, cli->srv_name_slash, + nt_errstr(nt_status))); + } else { + char *dom_user; + + /* Check DOMAIN\username first to catch winbind users, then + just the username for local users. */ + + dom_user = talloc_asprintf(mem_ctx, "%s%s%s", user_info->domain.str, + lp_winbind_separator(), + user_info->internal_username.str); + + if (!dom_user) { + DEBUG(0, ("talloc_asprintf failed!\n")); + nt_status = NT_STATUS_NO_MEMORY; + } else { + + if (!(pass = Get_Pwnam(dom_user))) + pass = Get_Pwnam(user_info->internal_username.str); + + if (pass) { + make_server_info_pw(server_info, pass); + if (!server_info) { + nt_status = NT_STATUS_NO_MEMORY; + } + } else { + nt_status = NT_STATUS_NO_SUCH_USER; + } + } + } + + /* Store the user group information in the server_info returned to the caller. */ + + if (NT_STATUS_IS_OK(nt_status) && (info3.num_groups2 != 0)) { + int i; + NT_USER_TOKEN *ptok; + auth_serversupplied_info *pserver_info = *server_info; + + if ((pserver_info->ptok = malloc( sizeof(NT_USER_TOKEN) ) ) == NULL) { + DEBUG(0, ("domain_client_validate: out of memory allocating rid group membership\n")); + nt_status = NT_STATUS_NO_MEMORY; + free_server_info(server_info); + goto done; + } + + ptok = pserver_info->ptok; + ptok->num_sids = (size_t)info3.num_groups2; + + if ((ptok->user_sids = (DOM_SID *)malloc( sizeof(DOM_SID) * ptok->num_sids )) == NULL) { + DEBUG(0, ("domain_client_validate: Out of memory allocating group SIDS\n")); + nt_status = NT_STATUS_NO_MEMORY; + free_server_info(server_info); + goto done; + } + + for (i = 0; i < ptok->num_sids; i++) { + sid_copy(&ptok->user_sids[i], &info3.dom_sid.sid); + sid_append_rid(&ptok->user_sids[i], info3.gids[i].g_rid); + } + + uni_group_cache_store_netlogon(mem_ctx, &info3); + } + +#if 0 + /* + * We don't actually need to do this - plus it fails currently with + * NT_STATUS_INVALID_INFO_CLASS - we need to know *exactly* what to + * send here. JRA. + */ + + if (NT_STATUS_IS_OK(status)) { + if(cli_nt_logoff(&cli, &ctr) == False) { + DEBUG(0,("domain_client_validate: unable to log off user %s in domain \ +%s to Domain controller %s. Error was %s.\n", user, domain, remote_machine, cli_errstr(&cli))); + nt_status = NT_STATUS_LOGON_FAILURE; + } + } +#endif /* 0 */ + + done: + + /* Note - once the cli stream is shutdown the mem_ctx used + to allocate the other_sids and gids structures has been deleted - so + these pointers are no longer valid..... */ + + cli_nt_session_close(cli); + cli_ulogoff(cli); + cli_shutdown(cli); + return nt_status; +} + +/**************************************************************************** + Check for a valid username and password in security=domain mode. +****************************************************************************/ + +static NTSTATUS check_ntdomain_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) +{ + NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE; + char *password_server; + unsigned char trust_passwd[16]; + time_t last_change_time; + char *domain = lp_workgroup(); + + if (!user_info || !server_info || !auth_context) { + DEBUG(1,("check_ntdomain_security: Critical variables not present. Failing.\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + /* + * Check that the requested domain is not our own machine name. + * If it is, we should never check the PDC here, we use our own local + * password file. + */ + + if(is_netbios_alias_or_name(user_info->domain.str)) { + DEBUG(3,("check_ntdomain_security: Requested domain was for this machine.\n")); + return NT_STATUS_LOGON_FAILURE; + } + + /* + * Get the machine account password for our primary domain + * No need to become_root() as secrets_init() is done at startup. + */ + + if (!secrets_fetch_trust_account_password(domain, trust_passwd, &last_change_time)) + { + DEBUG(0, ("check_ntdomain_security: could not fetch trust account password for domain %s\n", lp_workgroup())); + return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + } + + /* Test if machine password is expired and need to be changed */ + if (time(NULL) > last_change_time + lp_machine_password_timeout()) + { + global_machine_password_needs_changing = True; + } + + /* + * Treat each name in the 'password server =' line as a potential + * PDC/BDC. Contact each in turn and try and authenticate. + */ + + password_server = lp_passwordserver(); + + nt_status = domain_client_validate(mem_ctx, user_info, domain, + (uchar *)auth_context->challenge.data, + server_info, + password_server, global_myname, SEC_CHAN_WKSTA, trust_passwd, last_change_time); + return nt_status; +} + +/* module initialisation */ +BOOL auth_init_ntdomain(struct auth_context *auth_context, auth_methods **auth_method) +{ + if (!make_auth_methods(auth_context, auth_method)) { + return False; + } + + (*auth_method)->auth = check_ntdomain_security; + return True; +} + + +/**************************************************************************** + Check for a valid username and password in a trusted domain +****************************************************************************/ + +static NTSTATUS check_trustdomain_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) +{ + NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE; + unsigned char trust_md4_password[16]; + char *trust_password; + time_t last_change_time; + DOM_SID sid; + + if (!user_info || !server_info || !auth_context) { + DEBUG(1,("check_trustdomain_security: Critical variables not present. Failing.\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + /* + * Check that the requested domain is not our own machine name. + * If it is, we should never check the PDC here, we use our own local + * password file. + */ + + if(is_netbios_alias_or_name(user_info->domain.str)) { + DEBUG(3,("check_trustdomain_security: Requested domain was for this machine.\n")); + return NT_STATUS_LOGON_FAILURE; + } + + /* + * Check that the requested domain is not our own domain, + * If it is, we should use our own local password file. + */ + + if(strequal(lp_workgroup(), (user_info->domain.str))) { + DEBUG(3,("check_trustdomain_security: Requested domain was for this domain.\n")); + return NT_STATUS_LOGON_FAILURE; + } + + /* + * Get the machine account password for the trusted domain + * No need to become_root() as secrets_init() is done at startup. + */ + + if (!secrets_fetch_trusted_domain_password(user_info->domain.str, &trust_password, &sid, &last_change_time)) + { + DEBUG(0, ("check_trustdomain_security: could not fetch trust account password for domain %s\n", user_info->domain.str)); + return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + } + +#ifdef DEBUG_PASSWORD + DEBUG(100, ("Trust password for domain %s is %s\n", user_info->domain.str, trust_password)); +#endif + E_md4hash((uchar *)trust_password, trust_md4_password); + SAFE_FREE(trust_password); + +#if 0 + /* Test if machine password is expired and need to be changed */ + if (time(NULL) > last_change_time + lp_machine_password_timeout()) + { + global_machine_password_needs_changing = True; + } +#endif + + nt_status = domain_client_validate(mem_ctx, user_info, user_info->domain.str, + (uchar *)auth_context->challenge.data, + server_info, "*" /* Do a lookup */, + lp_workgroup(), SEC_CHAN_DOMAIN, trust_md4_password, last_change_time); + + return nt_status; +} + +/* module initialisation */ +BOOL auth_init_trustdomain(struct auth_context *auth_context, auth_methods **auth_method) +{ + if (!make_auth_methods(auth_context, auth_method)) { + return False; + } + + (*auth_method)->auth = check_trustdomain_security; + return True; +} diff --git a/source3/auth/auth_rhosts.c b/source3/auth/auth_rhosts.c new file mode 100644 index 0000000000..9586d1d65e --- /dev/null +++ b/source3/auth/auth_rhosts.c @@ -0,0 +1,231 @@ +/* + Unix SMB/CIFS implementation. + Main SMB reply routines + Copyright (C) Andrew Tridgell 1992-1998 + + 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" + +/**************************************************************************** + Read the a hosts.equiv or .rhosts file and check if it + allows this user from this machine. +****************************************************************************/ + +static BOOL check_user_equiv(const char *user, const char *remote, const char *equiv_file) +{ + int plus_allowed = 1; + char *file_host; + char *file_user; + char **lines = file_lines_load(equiv_file, NULL); + int i; + + DEBUG(5, ("check_user_equiv %s %s %s\n", user, remote, equiv_file)); + if (! lines) return False; + for (i=0; lines[i]; i++) { + char *buf = lines[i]; + trim_string(buf," "," "); + + if (buf[0] != '#' && buf[0] != '\n') + { + BOOL is_group = False; + int plus = 1; + char *bp = buf; + if (strcmp(buf, "NO_PLUS\n") == 0) + { + DEBUG(6, ("check_user_equiv NO_PLUS\n")); + plus_allowed = 0; + } + else { + if (buf[0] == '+') + { + bp++; + if (*bp == '\n' && plus_allowed) + { + /* a bare plus means everbody allowed */ + DEBUG(6, ("check_user_equiv everybody allowed\n")); + file_lines_free(lines); + return True; + } + } + else if (buf[0] == '-') + { + bp++; + plus = 0; + } + if (*bp == '@') + { + is_group = True; + bp++; + } + file_host = strtok(bp, " \t\n"); + file_user = strtok(NULL, " \t\n"); + DEBUG(7, ("check_user_equiv %s %s\n", file_host ? file_host : "(null)", + file_user ? file_user : "(null)" )); + if (file_host && *file_host) + { + BOOL host_ok = False; + +#if defined(HAVE_NETGROUP) && defined(HAVE_YP_GET_DEFAULT_DOMAIN) + if (is_group) + { + static char *mydomain = NULL; + if (!mydomain) + yp_get_default_domain(&mydomain); + if (mydomain && innetgr(file_host,remote,user,mydomain)) + host_ok = True; + } +#else + if (is_group) + { + DEBUG(1,("Netgroups not configured\n")); + continue; + } +#endif + + /* is it this host */ + /* the fact that remote has come from a call of gethostbyaddr + * means that it may have the fully qualified domain name + * so we could look up the file version to get it into + * a canonical form, but I would rather just type it + * in full in the equiv file + */ + if (!host_ok && !is_group && strequal(remote, file_host)) + host_ok = True; + + if (!host_ok) + continue; + + /* is it this user */ + if (file_user == 0 || strequal(user, file_user)) + { + DEBUG(5, ("check_user_equiv matched %s%s %s\n", + (plus ? "+" : "-"), file_host, + (file_user ? file_user : ""))); + file_lines_free(lines); + return (plus ? True : False); + } + } + } + } + } + file_lines_free(lines); + return False; +} + + +/**************************************************************************** +check for a possible hosts equiv or rhosts entry for the user +****************************************************************************/ + +static BOOL check_hosts_equiv(struct passwd *pass) +{ + char *fname = NULL; + + if (!pass) + return(False); + + fname = lp_hosts_equiv(); + + /* note: don't allow hosts.equiv on root */ + if (fname && *fname && (pass->pw_uid != 0)) { + if (check_user_equiv(pass->pw_name,client_name(),fname)) + return(True); + } + + return(False); +} + + +/**************************************************************************** + Check for a valid .rhosts/hosts.equiv entry for this user +****************************************************************************/ + +static NTSTATUS check_hostsequiv_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) +{ + NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE; + struct passwd *pass = Get_Pwnam(user_info->internal_username.str); + + if (pass) { + if (check_hosts_equiv(pass)) { + nt_status = NT_STATUS_OK; + make_server_info_pw(server_info, pass); + } + } else { + nt_status = NT_STATUS_NO_SUCH_USER; + } + + return nt_status; +} + +/* module initialisation */ +BOOL auth_init_hostsequiv(struct auth_context *auth_context, auth_methods **auth_method) +{ + if (!make_auth_methods(auth_context, auth_method)) { + return False; + } + + (*auth_method)->auth = check_hostsequiv_security; + return True; +} + + +/**************************************************************************** + Check for a valid .rhosts/hosts.equiv entry for this user +****************************************************************************/ + +static NTSTATUS check_rhosts_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) +{ + NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE; + struct passwd *pass = Get_Pwnam(user_info->internal_username.str); + pstring rhostsfile; + + if (pass) { + char *home = pass->pw_dir; + if (home) { + slprintf(rhostsfile, sizeof(rhostsfile)-1, "%s/.rhosts", home); + become_root(); + if (check_user_equiv(pass->pw_name,client_name(),rhostsfile)) { + nt_status = NT_STATUS_OK; + make_server_info_pw(server_info, pass); + } + unbecome_root(); + } + } else { + nt_status = NT_STATUS_NO_SUCH_USER; + } + + return nt_status; +} + +/* module initialisation */ +BOOL auth_init_rhosts(struct auth_context *auth_context, auth_methods **auth_method) +{ + if (!make_auth_methods(auth_context, auth_method)) { + return False; + } + + (*auth_method)->auth = check_rhosts_security; + return True; +} diff --git a/source3/auth/auth_sam.c b/source3/auth/auth_sam.c new file mode 100644 index 0000000000..6753951c89 --- /dev/null +++ b/source3/auth/auth_sam.c @@ -0,0 +1,452 @@ +/* + 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 + + 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" + +/**************************************************************************** +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. +****************************************************************************/ +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); + memcpy(client_response, ntv2_response.data, sizeof(client_response)); + + ntv2_owf_gen(part_passwd, user, domain, kr); + SMBOWFencrypt_ntv2(kr, sec_blob, client_key_data, (char *)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]) +{ + uint16 acct_ctrl; + const uint8 *nt_pw, *lm_pw; + uint32 auth_flags; + + 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); + } + } + + nt_pw = pdb_get_nt_passwd(sampass); + lm_pw = pdb_get_lanman_passwd(sampass); + + auth_flags = user_info->auth_flags; + + if (nt_pw == NULL) { + 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) { + /* 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\n")); + 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; + } 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()) { + /* 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, we want to check the LM hash below in this case */ + } + } + + if (lm_pw == NULL) { + DEBUG(3,("sam_password_ok: NO LanMan password set for user %s (and no NT password supplied)\n",pdb_get_username(sampass))); + auth_flags &= (~AUTH_FLAG_LM_RESP); + } + + 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))); + return NT_STATUS_LOGON_FAILURE; + } + + 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; + } else { + DEBUG(4,("sam_password_ok: LM password check failed for user %s\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; +} + +/**************************************************************************** + 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) +{ + uint16 acct_ctrl = pdb_get_acct_ctrl(sampass); + char *workstation_list; + time_t kickoff_time; + + DEBUG(4,("sam_account_ok: Checking SMB password for user %s\n",pdb_get_username(sampass))); + + /* Quit if the account was disabled. */ + if (acct_ctrl & ACB_DISABLED) { + DEBUG(1,("Account for user '%s' was disabled.\n", pdb_get_username(sampass))); + return NT_STATUS_ACCOUNT_DISABLED; + } + + /* 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)); + return NT_STATUS_ACCOUNT_EXPIRED; + } + + if (!(pdb_get_acct_ctrl(sampass) & ACB_PWNOEXP)) { + time_t must_change_time = pdb_get_pass_must_change_time(sampass); + time_t last_set_time = pdb_get_pass_last_set_time(sampass); + + /* 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))); + 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", http_timestring(must_change_time), (long)must_change_time)); + return NT_STATUS_PASSWORD_EXPIRED; + } + } + + /* 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) { + BOOL invalid_ws = True; + char *s = workstation_list; + + fstring tok; + + while (next_token(&s, tok, ",", sizeof(tok))) { + DEBUG(10,("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; + break; + } + } + + if (invalid_ws) + return NT_STATUS_INVALID_WORKSTATION; + } + + if (acct_ctrl & ACB_DOMTRUST) { + DEBUG(2,("sam_account_ok: Domain trust account %s denied by server\n", pdb_get_username(sampass))); + return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT; + } + + if (acct_ctrl & ACB_SVRTRUST) { + DEBUG(2,("sam_account_ok: Server trust account %s denied by server\n", pdb_get_username(sampass))); + return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT; + } + + if (acct_ctrl & ACB_WSTRUST) { + DEBUG(4,("sam_account_ok: Wksta trust account %s denied by server\n", pdb_get_username(sampass))); + return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT; + } + + 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 +return an NT_STATUS constant. +****************************************************************************/ + +static NTSTATUS check_sam_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) +{ + SAM_ACCOUNT *sampass=NULL; + BOOL ret; + NTSTATUS nt_status; + uint8 user_sess_key[16]; + const uint8* lm_hash; + + if (!user_info || !auth_context) { + return NT_STATUS_UNSUCCESSFUL; + } + + /* Can't use the talloc version here, becouse the returned struct gets + kept on the server_info */ + if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam(&sampass))) { + return nt_status; + } + + /* get the account information */ + + become_root(); + 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)); + pdb_free_sam(&sampass); + return NT_STATUS_NO_SUCH_USER; + } + + nt_status = sam_password_ok(auth_context, mem_ctx, sampass, user_info, user_sess_key); + + if (!NT_STATUS_IS_OK(nt_status)) { + pdb_free_sam(&sampass); + return nt_status; + } + + nt_status = sam_account_ok(mem_ctx, sampass, user_info); + + if (!NT_STATUS_IS_OK(nt_status)) { + pdb_free_sam(&sampass); + return nt_status; + } + + if (!make_server_info_sam(server_info, sampass)) { + DEBUG(0,("failed to malloc memory for server_info\n")); + return NT_STATUS_NO_MEMORY; + } + + 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)); + + return nt_status; +} + +/* module initialisation */ +BOOL auth_init_sam(struct auth_context *auth_context, auth_methods **auth_method) +{ + if (!make_auth_methods(auth_context, auth_method)) { + return False; + } + + (*auth_method)->auth = check_sam_security; + return True; +} + + +/**************************************************************************** +Check SAM security (above) but with a few extra checks. +****************************************************************************/ + +static NTSTATUS check_samstrict_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. */ + + if (!is_netbios_alias_or_name(user_info->domain.str)) { + return NT_STATUS_NO_SUCH_USER; + } + + return check_sam_security(auth_context, my_private_data, mem_ctx, user_info, server_info); +} + +/* module initialisation */ +BOOL auth_init_samstrict(struct auth_context *auth_context, auth_methods **auth_method) +{ + if (!make_auth_methods(auth_context, auth_method)) { + return False; + } + + (*auth_method)->auth = check_samstrict_security; + return True; +} + + diff --git a/source3/auth/auth_server.c b/source3/auth/auth_server.c new file mode 100644 index 0000000000..5190d45c20 --- /dev/null +++ b/source3/auth/auth_server.c @@ -0,0 +1,367 @@ +/* + Unix SMB/CIFS implementation. + Authenticate to a remote server + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Andrew Bartlett 2001 + + 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" + +extern pstring global_myname; +extern userdom_struct current_user_info; + +/**************************************************************************** + Support for server level security. +****************************************************************************/ + +static struct cli_state *server_cryptkey(TALLOC_CTX *mem_ctx) +{ + struct cli_state *cli = NULL; + fstring desthost; + struct in_addr dest_ip; + char *p, *pserver; + BOOL connected_ok = False; + + if (!(cli = cli_initialise(cli))) + return NULL; + + /* security = server just can't function with spnego */ + cli->use_spnego = False; + + pserver = talloc_strdup(mem_ctx, lp_passwordserver()); + p = pserver; + + while(next_token( &p, desthost, LIST_SEP, sizeof(desthost))) { + standard_sub_basic(current_user_info.smb_name, desthost); + strupper(desthost); + + if(!resolve_name( desthost, &dest_ip, 0x20)) { + DEBUG(1,("server_cryptkey: Can't resolve address for %s\n",desthost)); + continue; + } + + if (ismyip(dest_ip)) { + DEBUG(1,("Password server loop - disabling password server %s\n",desthost)); + continue; + } + + if (cli_connect(cli, desthost, &dest_ip)) { + DEBUG(3,("connected to password server %s\n",desthost)); + connected_ok = True; + break; + } + } + + if (!connected_ok) { + DEBUG(0,("password server not available\n")); + cli_shutdown(cli); + return NULL; + } + + if (!attempt_netbios_session_request(cli, global_myname, desthost, &dest_ip)) + return NULL; + + if (strequal(desthost,myhostname())) { + exit_server("Password server loop!"); + } + + DEBUG(3,("got session\n")); + + if (!cli_negprot(cli)) { + DEBUG(1,("%s rejected the negprot\n",desthost)); + cli_shutdown(cli); + return NULL; + } + + if (cli->protocol < PROTOCOL_LANMAN2 || + !(cli->sec_mode & 1)) { + DEBUG(1,("%s isn't in user level security mode\n",desthost)); + cli_shutdown(cli); + return NULL; + } + + DEBUG(3,("password server OK\n")); + + return cli; +} + +/**************************************************************************** + Clean up our allocated cli. +****************************************************************************/ + +static void free_server_private_data(void **private_data_pointer) +{ + struct cli_state **cli = (struct cli_state **)private_data_pointer; + if (*cli && (*cli)->initialised) { + cli_shutdown(*cli); + } +} + +/**************************************************************************** + Send a 'keepalive' packet down the cli pipe. +****************************************************************************/ + +static void send_server_keepalive(void **private_data_pointer) +{ + struct cli_state **cli = (struct cli_state **)private_data_pointer; + + /* also send a keepalive to the password server if its still + connected */ + if (cli && *cli && (*cli)->initialised) { + if (!send_keepalive((*cli)->fd)) { + DEBUG( 2, ( "password server keepalive failed.\n")); + cli_shutdown(*cli); + } + } +} + +/**************************************************************************** + Get the challenge out of a password server. +****************************************************************************/ + +static DATA_BLOB auth_get_challenge_server(const struct auth_context *auth_context, + void **my_private_data, + TALLOC_CTX *mem_ctx) +{ + struct cli_state *cli = server_cryptkey(mem_ctx); + + if (cli) { + DEBUG(3,("using password server validation\n")); + + if ((cli->sec_mode & 2) == 0) { + /* We can't work with unencrypted password servers + unless 'encrypt passwords = no' */ + DEBUG(5,("make_auth_info_server: Server is unencrypted, no challenge available..\n")); + + /* However, it is still a perfectly fine connection + to pass that unencrypted password over */ + *my_private_data = (void *)cli; + return data_blob(NULL, 0); + + } else if (cli->secblob.length < 8) { + /* We can't do much if we don't get a full challenge */ + DEBUG(2,("make_auth_info_server: Didn't receive a full challenge from server\n")); + cli_shutdown(cli); + return data_blob(NULL, 0); + } + + *my_private_data = (void *)cli; + + /* The return must be allocated on the caller's mem_ctx, as our own will be + destoyed just after the call. */ + return data_blob_talloc(auth_context->mem_ctx, cli->secblob.data,8); + } else { + return data_blob(NULL, 0); + } +} + + +/**************************************************************************** + Check for a valid username and password in security=server mode. + - Validate a password with the password server. +****************************************************************************/ + +static NTSTATUS check_smbserver_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) +{ + struct cli_state *cli; + static unsigned char badpass[24]; + static fstring baduser; + static BOOL tested_password_server = False; + static BOOL bad_password_server = False; + NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE; + BOOL locally_made_cli = False; + + /* + * Check that the requested domain is not our own machine name. + * If it is, we should never check the PDC here, we use our own local + * password file. + */ + + if(is_netbios_alias_or_name(user_info->domain.str)) { + DEBUG(3,("check_smbserver_security: Requested domain was for this machine.\n")); + return NT_STATUS_LOGON_FAILURE; + } + + cli = my_private_data; + + if (cli) { + } else { + cli = server_cryptkey(mem_ctx); + locally_made_cli = True; + } + + if (!cli || !cli->initialised) { + DEBUG(1,("password server is not connected (cli not initilised)\n")); + return NT_STATUS_LOGON_FAILURE; + } + + if ((cli->sec_mode & 2) == 0) { + if (user_info->encrypted) { + DEBUG(1,("password server %s is plaintext, but we are encrypted. This just can't work :-(\n", cli->desthost)); + return NT_STATUS_LOGON_FAILURE; + } + } else { + if (memcmp(cli->secblob.data, auth_context->challenge.data, 8) != 0) { + DEBUG(1,("the challenge that the password server (%s) supplied us is not the one we gave our client. This just can't work :-(\n", cli->desthost)); + return NT_STATUS_LOGON_FAILURE; + } + } + + if(badpass[0] == 0) + memset(badpass, 0x1f, sizeof(badpass)); + + if((user_info->nt_resp.length == sizeof(badpass)) && + !memcmp(badpass, user_info->nt_resp.data, sizeof(badpass))) { + /* + * Very unlikely, our random bad password is the same as the users + * password. + */ + memset(badpass, badpass[0]+1, sizeof(badpass)); + } + + if(baduser[0] == 0) { + fstrcpy(baduser, INVALID_USER_PREFIX); + fstrcat(baduser, global_myname); + } + + /* + * Attempt a session setup with a totally incorrect password. + * If this succeeds with the guest bit *NOT* set then the password + * server is broken and is not correctly setting the guest bit. We + * need to detect this as some versions of NT4.x are broken. JRA. + */ + + /* I sure as hell hope that there arn't servers out there that take + * NTLMv2 and have this bug, as we don't test for that... + * - abartlet@samba.org + */ + + if ((!tested_password_server) && (lp_paranoid_server_security())) { + if (cli_session_setup(cli, baduser, (char *)badpass, sizeof(badpass), + (char *)badpass, sizeof(badpass), user_info->domain.str)) { + + /* + * We connected to the password server so we + * can say we've tested it. + */ + tested_password_server = True; + + if ((SVAL(cli->inbuf,smb_vwv2) & 1) == 0) { + DEBUG(0,("server_validate: password server %s allows users as non-guest \ +with a bad password.\n", cli->desthost)); + DEBUG(0,("server_validate: This is broken (and insecure) behaviour. Please do not \ +use this machine as the password server.\n")); + cli_ulogoff(cli); + + /* + * Password server has the bug. + */ + bad_password_server = True; + return NT_STATUS_LOGON_FAILURE; + } + cli_ulogoff(cli); + } + } else { + + /* + * We have already tested the password server. + * Fail immediately if it has the bug. + */ + + if(bad_password_server) { + DEBUG(0,("server_validate: [1] password server %s allows users as non-guest \ +with a bad password.\n", cli->desthost)); + DEBUG(0,("server_validate: [1] This is broken (and insecure) behaviour. Please do not \ +use this machine as the password server.\n")); + return NT_STATUS_LOGON_FAILURE; + } + } + + /* + * Now we know the password server will correctly set the guest bit, or is + * not guest enabled, we can try with the real password. + */ + + if (!user_info->encrypted) { + /* Plaintext available */ + if (!cli_session_setup(cli, user_info->smb_name.str, + (char *)user_info->plaintext_password.data, + user_info->plaintext_password.length, + NULL, 0, + user_info->domain.str)) { + DEBUG(1,("password server %s rejected the password\n", cli->desthost)); + /* Make this cli_nt_error() when the conversion is in */ + nt_status = cli_nt_error(cli); + } else { + nt_status = NT_STATUS_OK; + } + } else { + if (!cli_session_setup(cli, user_info->smb_name.str, + (char *)user_info->lm_resp.data, + user_info->lm_resp.length, + (char *)user_info->nt_resp.data, + user_info->nt_resp.length, + user_info->domain.str)) { + DEBUG(1,("password server %s rejected the password\n", cli->desthost)); + /* Make this cli_nt_error() when the conversion is in */ + nt_status = cli_nt_error(cli); + } else { + nt_status = NT_STATUS_OK; + } + } + + /* if logged in as guest then reject */ + if ((SVAL(cli->inbuf,smb_vwv2) & 1) != 0) { + DEBUG(1,("password server %s gave us guest only\n", cli->desthost)); + nt_status = NT_STATUS_LOGON_FAILURE; + } + + cli_ulogoff(cli); + + if NT_STATUS_IS_OK(nt_status) { + struct passwd *pass = Get_Pwnam(user_info->internal_username.str); + if (pass) { + if (!make_server_info_pw(server_info, pass)) { + nt_status = NT_STATUS_NO_MEMORY; + } + } else { + nt_status = NT_STATUS_NO_SUCH_USER; + } + } + + if (locally_made_cli) { + cli_shutdown(cli); + } + + return(nt_status); +} + +BOOL auth_init_smbserver(struct auth_context *auth_context, auth_methods **auth_method) +{ + if (!make_auth_methods(auth_context, auth_method)) { + return False; + } + (*auth_method)->auth = check_smbserver_security; + (*auth_method)->get_chal = auth_get_challenge_server; + (*auth_method)->send_keepalive = send_server_keepalive; + (*auth_method)->free_private_data = free_server_private_data; + return True; +} diff --git a/source3/auth/auth_unix.c b/source3/auth/auth_unix.c new file mode 100644 index 0000000000..05646f554e --- /dev/null +++ b/source3/auth/auth_unix.c @@ -0,0 +1,129 @@ +/* + Unix SMB/CIFS implementation. + Password and authentication handling + Copyright (C) Andrew Bartlett 2001 + + 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" + +/** + * update the encrypted smbpasswd file from the plaintext username and password + * + * this ugly hack needs to die, but not quite yet, I think people still use it... + **/ +static BOOL update_smbpassword_file(char *user, char *password) +{ + SAM_ACCOUNT *sampass = NULL; + BOOL ret; + + pdb_init_sam(&sampass); + + become_root(); + ret = pdb_getsampwnam(sampass, user); + unbecome_root(); + + if(ret == False) { + DEBUG(0,("pdb_getsampwnam returned NULL\n")); + pdb_free_sam(&sampass); + return False; + } + + /* + * Remove the account disabled flag - we are updating the + * users password from a login. + */ + if (!pdb_set_acct_ctrl(sampass, pdb_get_acct_ctrl(sampass) & ~ACB_DISABLED)) { + pdb_free_sam(&sampass); + return False; + } + + if (!pdb_set_plaintext_passwd (sampass, password)) { + pdb_free_sam(&sampass); + return False; + } + + /* Now write it into the file. */ + become_root(); + + ret = pdb_update_sam_account (sampass); + + unbecome_root(); + + if (ret) { + DEBUG(3,("pdb_update_sam_account returned %d\n",ret)); + } + + memset(password, '\0', strlen(password)); + + pdb_free_sam(&sampass); + return ret; +} + + +/** Check a plaintext username/password + * + * Cannot deal with an encrupted password in any manner whatsoever, + * unless the account has a null password. + **/ + +static NTSTATUS check_unix_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) +{ + NTSTATUS nt_status; + struct passwd *pass = NULL; + + become_root(); + pass = Get_Pwnam(user_info->internal_username.str); + + + /** @todo This call assumes a ASCII password, no charset transformation is + done. We may need to revisit this **/ + nt_status = pass_check(pass, + pass ? pass->pw_name : user_info->internal_username.str, + (char *)user_info->plaintext_password.data, + user_info->plaintext_password.length-1, + lp_update_encrypted() ? + update_smbpassword_file : NULL, + True); + + unbecome_root(); + + if NT_STATUS_IS_OK(nt_status) { + if (pass) { + make_server_info_pw(server_info, pass); + } else { + /* we need to do somthing more useful here */ + nt_status = NT_STATUS_NO_SUCH_USER; + } + } + + return nt_status; +} + +/* module initialisation */ +BOOL auth_init_unix(struct auth_context *auth_context, auth_methods **auth_method) +{ + if (!make_auth_methods(auth_context, auth_method)) { + return False; + } + + (*auth_method)->auth = check_unix_security; + return True; +} diff --git a/source3/auth/auth_util.c b/source3/auth/auth_util.c new file mode 100644 index 0000000000..d80c927c19 --- /dev/null +++ b/source3/auth/auth_util.c @@ -0,0 +1,685 @@ +/* + Unix SMB/CIFS implementation. + Authentication utility functions + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Andrew Bartlett 2001 + Copyright (C) Jeremy Allison 2000-2001 + + 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" + +extern fstring remote_machine; +extern pstring global_myname; + +/**************************************************************************** + Create a UNIX user on demand. +****************************************************************************/ + +static int smb_create_user(const char *unix_user, const char *homedir) +{ + pstring add_script; + int ret; + + pstrcpy(add_script, lp_adduser_script()); + if (! *add_script) + return -1; + all_string_sub(add_script, "%u", unix_user, sizeof(pstring)); + if (homedir) + all_string_sub(add_script, "%H", homedir, sizeof(pstring)); + ret = smbrun(add_script,NULL); + DEBUG(3,("smb_create_user: Running the command `%s' gave %d\n",add_script,ret)); + return ret; +} + +/**************************************************************************** + Delete a UNIX user on demand. +****************************************************************************/ + +int smb_delete_user(const char *unix_user) +{ + pstring del_script; + int ret; + + pstrcpy(del_script, lp_deluser_script()); + if (! *del_script) + return -1; + all_string_sub(del_script, "%u", unix_user, sizeof(pstring)); + ret = smbrun(del_script,NULL); + DEBUG(3,("smb_delete_user: Running the command `%s' gave %d\n",del_script,ret)); + return ret; +} + +/**************************************************************************** + Add and Delete UNIX users on demand, based on NTSTATUS codes. +****************************************************************************/ + +void smb_user_control(const auth_usersupplied_info *user_info, auth_serversupplied_info *server_info, NTSTATUS nt_status) +{ + struct passwd *pwd=NULL; + + if (NT_STATUS_IS_OK(nt_status)) { + + if (!(server_info->sam_fill_level & SAM_FILL_UNIX)) { + + /* + * User validated ok against Domain controller. + * If the admin wants us to try and create a UNIX + * user on the fly, do so. + */ + + if(lp_adduser_script() && !(pwd = Get_Pwnam(user_info->internal_username.str))) { + smb_create_user(user_info->internal_username.str, NULL); + } + } + } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER)) { + /* + * User failed to validate ok against Domain controller. + * If the failure was "user doesn't exist" and admin + * wants us to try and delete that UNIX user on the fly, + * do so. + */ + if (lp_deluser_script()) { + smb_delete_user(user_info->internal_username.str); + } + } +} + +/**************************************************************************** + Create an auth_usersupplied_data structure +****************************************************************************/ + +static BOOL make_user_info(auth_usersupplied_info **user_info, + const char *smb_name, + const char *internal_username, + 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) +{ + + 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))); + return False; + } + + ZERO_STRUCTP(*user_info); + + DEBUG(5,("making strings for %s's user_info struct\n", internal_username)); + + (*user_info)->smb_name.str = strdup(smb_name); + if ((*user_info)->smb_name.str) { + (*user_info)->smb_name.len = strlen(smb_name); + } else { + free_user_info(user_info); + return False; + } + + (*user_info)->internal_username.str = strdup(internal_username); + if ((*user_info)->internal_username.str) { + (*user_info)->internal_username.len = strlen(internal_username); + } else { + free_user_info(user_info); + return False; + } + + (*user_info)->domain.str = strdup(domain); + if ((*user_info)->domain.str) { + (*user_info)->domain.len = strlen(domain); + } else { + free_user_info(user_info); + return False; + } + + (*user_info)->client_domain.str = strdup(client_domain); + if ((*user_info)->client_domain.str) { + (*user_info)->client_domain.len = strlen(client_domain); + } else { + free_user_info(user_info); + return False; + } + + (*user_info)->wksta_name.str = strdup(wksta_name); + if ((*user_info)->wksta_name.str) { + (*user_info)->wksta_name.len = strlen(wksta_name); + } else { + free_user_info(user_info); + return False; + } + + DEBUG(5,("makeing 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); + + (*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)); + + return True; +} + +/**************************************************************************** + Create an auth_usersupplied_data structure after appropriate mapping. +****************************************************************************/ + +BOOL 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) +{ + const char *domain; + fstring internal_username; + fstrcpy(internal_username, smb_name); + map_username(internal_username); + + 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 + */ + + 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 False; + } + + 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 { + domain = lp_workgroup(); + } + + return make_user_info(user_info, + smb_name, internal_username, + client_domain, domain, + wksta_name, + lm_pwd, nt_pwd, + plaintext, + ntlmssp_flags, encrypted); + +} + +/**************************************************************************** + Create an auth_usersupplied_data, making the DATA_BLOBs here. + Decrypt and encrypt the passwords. +****************************************************************************/ + +BOOL make_user_info_netlogon_network(auth_usersupplied_info **user_info, + const char *smb_name, + const char *client_domain, + const char *wksta_name, + const uchar *lm_network_pwd, int lm_pwd_len, + const uchar *nt_network_pwd, int nt_pwd_len) +{ + BOOL ret; + 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; + } + + ret = make_user_info_map(user_info, + smb_name, client_domain, + wksta_name, + lm_blob, nt_blob, + plaintext_blob, + auth_flags, True); + + data_blob_free(&lm_blob); + data_blob_free(&nt_blob); + return ret; +} + +/**************************************************************************** + Create an auth_usersupplied_data, making the DATA_BLOBs here. + Decrypt and encrypt the passwords. +****************************************************************************/ + +BOOL make_user_info_netlogon_interactive(auth_usersupplied_info **user_info, + const char *smb_name, + const char *client_domain, + const char *wksta_name, + const uchar chal[8], + const uchar lm_interactive_pwd[16], + const uchar nt_interactive_pwd[16], + const uchar *dc_sess_key) +{ + char lm_pwd[16]; + char nt_pwd[16]; + 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); + + if (lm_interactive_pwd) memcpy(lm_pwd, lm_interactive_pwd, sizeof(lm_pwd)); + if (nt_interactive_pwd) memcpy(nt_pwd, nt_interactive_pwd, sizeof(nt_pwd)); + +#ifdef DEBUG_PASSWORD + DEBUG(100,("key:")); + dump_data(100, (char *)key, sizeof(key)); + + DEBUG(100,("lm owf password:")); + dump_data(100, lm_pwd, sizeof(lm_pwd)); + + DEBUG(100,("nt owf password:")); + 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)); + +#ifdef DEBUG_PASSWORD + DEBUG(100,("decrypt of lm owf password:")); + dump_data(100, lm_pwd, sizeof(lm_pwd)); + + DEBUG(100,("decrypt of nt owf password:")); + 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); + + /* Password info paranoia */ + ZERO_STRUCT(lm_pwd); + ZERO_STRUCT(nt_pwd); + ZERO_STRUCT(key); + + { + BOOL ret; + 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); + + if (lm_interactive_pwd) + auth_flags |= AUTH_FLAG_LM_RESP; + if (nt_interactive_pwd) + auth_flags |= AUTH_FLAG_NTLM_RESP; + + ret = make_user_info_map(user_info, + smb_name, client_domain, + wksta_name, + local_lm_blob, + local_nt_blob, + plaintext_blob, + auth_flags, True); + + data_blob_free(&local_lm_blob); + data_blob_free(&local_nt_blob); + return ret; + } +} + + +/**************************************************************************** + Create an auth_usersupplied_data structure +****************************************************************************/ + +BOOL make_user_info_for_reply(auth_usersupplied_info **user_info, + const char *smb_name, + const char *client_domain, + const uint8 chal[8], + DATA_BLOB plaintext_password) +{ + + DATA_BLOB local_lm_blob; + DATA_BLOB local_nt_blob; + BOOL ret = False; + uint32 auth_flags = AUTH_FLAG_NONE; + + /* + * Not encrypted - do so. + */ + + DEBUG(5,("make_user_info_for_reply: User passwords not in encrypted format.\n")); + + if (plaintext_password.data) { + unsigned char local_lm_response[24]; + +#ifdef DEBUG_PASSWORD + DEBUG(10,("Unencrypted password (len %d):\n",plaintext_password.length)); + dump_data(100, plaintext_password.data, plaintext_password.length); +#endif + + SMBencrypt( (const uchar *)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); + } + + ret = make_user_info_map(user_info, smb_name, + client_domain, + remote_machine, + local_lm_blob, + local_nt_blob, + plaintext_password, + auth_flags, False); + + data_blob_free(&local_lm_blob); + return ret; +} + +/**************************************************************************** + Create an auth_usersupplied_data structure +****************************************************************************/ + +BOOL make_user_info_for_reply_enc(auth_usersupplied_info **user_info, + const char *smb_name, + 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, + remote_machine, + lm_resp, + nt_resp, + no_plaintext_blob, + auth_flags, True); +} + +/**************************************************************************** + Create a guest user_info blob, for anonymous authenticaion. +****************************************************************************/ + +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; + + return make_user_info(user_info, + "","", + "","", + "", + nt_blob, lm_blob, + plaintext_blob, + auth_flags, True); +} + +/*************************************************************************** + Make a user_info struct +***************************************************************************/ + +BOOL make_server_info(auth_serversupplied_info **server_info) +{ + *server_info = malloc(sizeof(**server_info)); + if (!*server_info) { + DEBUG(0,("make_server_info: malloc failed!\n")); + return False; + } + ZERO_STRUCTP(*server_info); + return True; +} + +/*************************************************************************** + Make (and fill) a user_info struct from a SAM_ACCOUNT +***************************************************************************/ + +BOOL make_server_info_sam(auth_serversupplied_info **server_info, SAM_ACCOUNT *sampass) +{ + if (!make_server_info(server_info)) { + return False; + } + + (*server_info)->sam_fill_level = SAM_FILL_ALL; + (*server_info)->sam_account = sampass; + + DEBUG(5,("make_server_info_sam: made server info for user %s\n", + pdb_get_username((*server_info)->sam_account))); + return True; +} + +/*************************************************************************** + Make (and fill) a user_info struct from a 'struct passwd' by conversion + to a SAM_ACCOUNT +***************************************************************************/ + +BOOL make_server_info_pw(auth_serversupplied_info **server_info, const struct passwd *pwd) +{ + SAM_ACCOUNT *sampass = NULL; + if (!NT_STATUS_IS_OK(pdb_init_sam_pw(&sampass, pwd))) { + return False; + } + return make_server_info_sam(server_info, sampass); +} + +/*************************************************************************** + Free a user_info struct +***************************************************************************/ + +void free_user_info(auth_usersupplied_info **user_info) +{ + DEBUG(5,("attempting to free (and zero) a user_info structure\n")); + if (*user_info != NULL) { + if ((*user_info)->smb_name.str) { + DEBUG(10,("structure was created for %s\n", (*user_info)->smb_name.str)); + } + SAFE_FREE((*user_info)->smb_name.str); + SAFE_FREE((*user_info)->internal_username.str); + SAFE_FREE((*user_info)->client_domain.str); + SAFE_FREE((*user_info)->domain.str); + SAFE_FREE((*user_info)->wksta_name.str); + data_blob_free(&(*user_info)->lm_resp); + data_blob_free(&(*user_info)->nt_resp); + SAFE_FREE((*user_info)->interactive_password); + data_blob_clear_free(&(*user_info)->plaintext_password); + ZERO_STRUCT(**user_info); + } + SAFE_FREE(*user_info); +} + +/*************************************************************************** + Clear out a server_info struct that has been allocated +***************************************************************************/ + +void free_server_info(auth_serversupplied_info **server_info) +{ + if (*server_info != NULL) { + pdb_free_sam(&(*server_info)->sam_account); + + /* call pam_end here, unless we know we are keeping it */ + delete_nt_token( &(*server_info)->ptok ); + ZERO_STRUCT(**server_info); + } + SAFE_FREE(*server_info); +} + +/*************************************************************************** + Make a server_info struct for a guest user +***************************************************************************/ + +BOOL make_server_info_guest(auth_serversupplied_info **server_info) +{ + struct passwd *pass = getpwnam_alloc(lp_guestaccount()); + + if (pass) { + if (!make_server_info_pw(server_info, pass)) { + passwd_free(&pass); + return False; + } + (*server_info)->guest = True; + passwd_free(&pass); + return True; + } + DEBUG(0,("make_server_info_guest: getpwnam_alloc() failed on guest account!\n")); + return False; +} + +/*************************************************************************** + Make an auth_methods struct +***************************************************************************/ + +BOOL make_auth_methods(struct auth_context *auth_context, auth_methods **auth_method) +{ + if (!auth_context) { + smb_panic("no auth_context supplied to make_auth_methods()!\n"); + } + + if (!auth_method) { + smb_panic("make_auth_methods: pointer to auth_method pointer is NULL!\n"); + } + + *auth_method = talloc(auth_context->mem_ctx, sizeof(**auth_method)); + if (!*auth_method) { + DEBUG(0,("make_auth_method: malloc failed!\n")); + return False; + } + ZERO_STRUCTP(*auth_method); + + return True; +} + +/**************************************************************************** + Delete a SID token. +****************************************************************************/ + +void delete_nt_token(NT_USER_TOKEN **pptoken) +{ + if (*pptoken) { + NT_USER_TOKEN *ptoken = *pptoken; + SAFE_FREE( ptoken->user_sids ); + ZERO_STRUCTP(ptoken); + } + SAFE_FREE(*pptoken); +} + +/**************************************************************************** + Duplicate a SID token. +****************************************************************************/ + +NT_USER_TOKEN *dup_nt_token(NT_USER_TOKEN *ptoken) +{ + NT_USER_TOKEN *token; + + if (!ptoken) + return NULL; + + if ((token = (NT_USER_TOKEN *)malloc( sizeof(NT_USER_TOKEN) ) ) == NULL) + return NULL; + + ZERO_STRUCTP(token); + + if ((token->user_sids = (DOM_SID *)memdup( ptoken->user_sids, sizeof(DOM_SID) * ptoken->num_sids )) == NULL) { + SAFE_FREE(token); + return NULL; + } + + token->num_sids = ptoken->num_sids; + + return token; +} + +/** + * Squash an NT_STATUS in line with security requirements. + * In an attempt to avoid giving the whole game away when users + * are authenticating, NT replaces both NT_STATUS_NO_SUCH_USER and + * NT_STATUS_WRONG_PASSWORD with NT_STATUS_LOGON_FAILURE in certain situations + * (session setups in particular). + * + * @param nt_status NTSTATUS input for squashing. + * @return the 'squashed' nt_status + **/ + +NTSTATUS nt_status_squash(NTSTATUS nt_status) +{ + if NT_STATUS_IS_OK(nt_status) { + return nt_status; + } else if NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER) { + /* Match WinXP and don't give the game away */ + return NT_STATUS_LOGON_FAILURE; + + } else if NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD) { + /* Match WinXP and don't give the game away */ + return NT_STATUS_LOGON_FAILURE; + } else { + return nt_status; + } +} + + + diff --git a/source3/auth/auth_winbind.c b/source3/auth/auth_winbind.c new file mode 100644 index 0000000000..bc19b36b54 --- /dev/null +++ b/source3/auth/auth_winbind.c @@ -0,0 +1,111 @@ +/* + Unix SMB/CIFS implementation. + + Winbind authentication mechnism + + Copyright (C) Tim Potter 2000 + Copyright (C) Andrew Bartlett 2001 + + 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" + +/* Prototypes from common.h */ + +NSS_STATUS winbindd_request(int req_type, + struct winbindd_request *request, + struct winbindd_response *response); + + +/* Authenticate a user with a challenge/response */ + +static NTSTATUS check_winbind_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) +{ + struct winbindd_request request; + struct winbindd_response response; + NSS_STATUS result; + struct passwd *pw; + NTSTATUS nt_status; + + if (!user_info) { + return NT_STATUS_UNSUCCESSFUL; + } + + if (!auth_context) { + DEBUG(3,("Password for user %s cannot be checked because we have no auth_info to get the challenge from.\n", + user_info->internal_username.str)); + return NT_STATUS_UNSUCCESSFUL; + } + + /* Send off request */ + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + snprintf(request.data.auth_crap.user, sizeof(request.data.auth_crap.user), + "%s\\%s", user_info->domain.str, user_info->smb_name.str); + + fstrcpy(request.data.auth_crap.user, user_info->smb_name.str); + fstrcpy(request.data.auth_crap.domain, user_info->domain.str); + + memcpy(request.data.auth_crap.chal, auth_context->challenge.data, sizeof(request.data.auth_crap.chal)); + + request.data.auth_crap.lm_resp_len = MIN(user_info->lm_resp.length, + sizeof(request.data.auth_crap.lm_resp)); + request.data.auth_crap.nt_resp_len = MIN(user_info->nt_resp.length, + sizeof(request.data.auth_crap.nt_resp)); + + memcpy(request.data.auth_crap.lm_resp, user_info->lm_resp.data, + sizeof(request.data.auth_crap.lm_resp_len)); + memcpy(request.data.auth_crap.nt_resp, user_info->nt_resp.data, + request.data.auth_crap.lm_resp_len); + + result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response); + + if (result == NSS_STATUS_SUCCESS) { + + pw = Get_Pwnam(user_info->internal_username.str); + + if (pw) { + if (make_server_info_pw(server_info, pw)) { + nt_status = NT_STATUS_OK; + } else { + nt_status = NT_STATUS_NO_MEMORY; + } + } else { + nt_status = NT_STATUS_NO_SUCH_USER; + } + } else { + nt_status = NT_STATUS_LOGON_FAILURE; + } + + return nt_status; +} + +/* module initialisation */ +BOOL auth_init_winbind(struct auth_context *auth_context, auth_methods **auth_method) +{ + if (!make_auth_methods(auth_context, auth_method)) { + return False; + } + + (*auth_method)->auth = check_winbind_security; + return True; +} diff --git a/source3/auth/pampass.c b/source3/auth/pampass.c new file mode 100644 index 0000000000..1428e929f1 --- /dev/null +++ b/source3/auth/pampass.c @@ -0,0 +1,872 @@ +/* + Unix SMB/CIFS implementation. + PAM Password checking + Copyright (C) Andrew Tridgell 1992-2001 + Copyright (C) John H Terpsta 1999-2001 + Copyright (C) Andrew Bartlett 2001 + Copyright (C) Jeremy Allison 2001 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + * This module provides PAM based functions for validation of + * username/password pairs, account managment, session and access control. + * Note: SMB password checking is done in smbpass.c + */ + +#include "includes.h" + +#ifdef WITH_PAM + +/******************************************************************* + * Handle PAM authentication + * - Access, Authentication, Session, Password + * Note: See PAM Documentation and refer to local system PAM implementation + * which determines what actions/limitations/allowances become affected. + *********************************************************************/ + +#include <security/pam_appl.h> + +/* + * Structure used to communicate between the conversation function + * and the server_login/change password functions. + */ + +struct smb_pam_userdata { + const char *PAM_username; + const char *PAM_password; + const char *PAM_newpassword; +}; + +typedef int (*smb_pam_conv_fn)(int, const struct pam_message **, struct pam_response **, void *appdata_ptr); + +/* + * Macros to help make life easy + */ +#define COPY_STRING(s) (s) ? strdup(s) : NULL + +/******************************************************************* + PAM error handler. + *********************************************************************/ + +static BOOL smb_pam_error_handler(pam_handle_t *pamh, int pam_error, char *msg, int dbglvl) +{ + + if( pam_error != PAM_SUCCESS) { + DEBUG(dbglvl, ("smb_pam_error_handler: PAM: %s : %s\n", + msg, pam_strerror(pamh, pam_error))); + + return False; + } + return True; +} + +/******************************************************************* + This function is a sanity check, to make sure that we NEVER report + failure as sucess. +*********************************************************************/ + +static BOOL smb_pam_nt_status_error_handler(pam_handle_t *pamh, int pam_error, + char *msg, int dbglvl, + NTSTATUS *nt_status) +{ + *nt_status = pam_to_nt_status(pam_error); + + if (smb_pam_error_handler(pamh, pam_error, msg, dbglvl)) + return True; + + if (NT_STATUS_IS_OK(*nt_status)) { + /* Complain LOUDLY */ + DEBUG(0, ("smb_pam_nt_status_error_handler: PAM: BUG: PAM and NT_STATUS \ +error MISMATCH, forcing to NT_STATUS_LOGON_FAILURE")); + *nt_status = NT_STATUS_LOGON_FAILURE; + } + return False; +} + +/* + * PAM conversation function + * Here we assume (for now, at least) that echo on means login name, and + * echo off means password. + */ + +static int smb_pam_conv(int num_msg, + const struct pam_message **msg, + struct pam_response **resp, + void *appdata_ptr) +{ + int replies = 0; + struct pam_response *reply = NULL; + struct smb_pam_userdata *udp = (struct smb_pam_userdata *)appdata_ptr; + + *resp = NULL; + + if (num_msg <= 0) + return PAM_CONV_ERR; + + /* + * Apparantly HPUX has a buggy PAM that doesn't support the + * appdata_ptr. Fail if this is the case. JRA. + */ + + if (udp == NULL) { + DEBUG(0,("smb_pam_conv: PAM on this system is broken - appdata_ptr == NULL !\n")); + return PAM_CONV_ERR; + } + + reply = malloc(sizeof(struct pam_response) * num_msg); + if (!reply) + return PAM_CONV_ERR; + + memset(reply, '\0', sizeof(struct pam_response) * num_msg); + + for (replies = 0; replies < num_msg; replies++) { + switch (msg[replies]->msg_style) { + case PAM_PROMPT_ECHO_ON: + reply[replies].resp_retcode = PAM_SUCCESS; + reply[replies].resp = COPY_STRING(udp->PAM_username); + /* PAM frees resp */ + break; + + case PAM_PROMPT_ECHO_OFF: + reply[replies].resp_retcode = PAM_SUCCESS; + reply[replies].resp = COPY_STRING(udp->PAM_password); + /* PAM frees resp */ + break; + + case PAM_TEXT_INFO: + /* fall through */ + + case PAM_ERROR_MSG: + /* ignore it... */ + reply[replies].resp_retcode = PAM_SUCCESS; + reply[replies].resp = NULL; + break; + + default: + /* Must be an error of some sort... */ + SAFE_FREE(reply); + return PAM_CONV_ERR; + } + } + if (reply) + *resp = reply; + return PAM_SUCCESS; +} + +/* + * PAM password change conversation function + * Here we assume (for now, at least) that echo on means login name, and + * echo off means password. + */ + +static void special_char_sub(char *buf) +{ + all_string_sub(buf, "\\n", "", 0); + all_string_sub(buf, "\\r", "", 0); + all_string_sub(buf, "\\s", " ", 0); + all_string_sub(buf, "\\t", "\t", 0); +} + +static void pwd_sub(char *buf, const char *username, const char *oldpass, const char *newpass) +{ + pstring_sub(buf, "%u", username); + all_string_sub(buf, "%o", oldpass, sizeof(fstring)); + all_string_sub(buf, "%n", newpass, sizeof(fstring)); +} + + +struct chat_struct { + struct chat_struct *next, *prev; + fstring prompt; + fstring reply; +}; + +/************************************************************** + Create a linked list containing chat data. +***************************************************************/ + +static struct chat_struct *make_pw_chat(char *p) +{ + fstring prompt; + fstring reply; + struct chat_struct *list = NULL; + struct chat_struct *t; + struct chat_struct *tmp; + + while (1) { + t = (struct chat_struct *)malloc(sizeof(*t)); + if (!t) { + DEBUG(0,("make_pw_chat: malloc failed!\n")); + return NULL; + } + + ZERO_STRUCTP(t); + + DLIST_ADD_END(list, t, tmp); + + if (!next_token(&p, prompt, NULL, sizeof(fstring))) + break; + + if (strequal(prompt,".")) + fstrcpy(prompt,"*"); + + special_char_sub(prompt); + fstrcpy(t->prompt, prompt); + strlower(t->prompt); + trim_string(t->prompt, " ", " "); + + if (!next_token(&p, reply, NULL, sizeof(fstring))) + break; + + if (strequal(reply,".")) + fstrcpy(reply,""); + + special_char_sub(reply); + fstrcpy(t->reply, reply); + strlower(t->reply); + trim_string(t->reply, " ", " "); + + } + return list; +} + +static void free_pw_chat(struct chat_struct *list) +{ + while (list) { + struct chat_struct *old_head = list; + DLIST_REMOVE(list, list); + SAFE_FREE(old_head); + } +} + +static int smb_pam_passchange_conv(int num_msg, + const struct pam_message **msg, + struct pam_response **resp, + void *appdata_ptr) +{ + int replies = 0; + struct pam_response *reply = NULL; + fstring current_prompt; + fstring current_reply; + struct smb_pam_userdata *udp = (struct smb_pam_userdata *)appdata_ptr; + struct chat_struct *pw_chat= make_pw_chat(lp_passwd_chat()); + struct chat_struct *t; + BOOL found; + *resp = NULL; + + DEBUG(10,("smb_pam_passchange_conv: starting converstation for %d messages\n", num_msg)); + + if (num_msg <= 0) + return PAM_CONV_ERR; + + if (pw_chat == NULL) + return PAM_CONV_ERR; + + /* + * Apparantly HPUX has a buggy PAM that doesn't support the + * appdata_ptr. Fail if this is the case. JRA. + */ + + if (udp == NULL) { + DEBUG(0,("smb_pam_passchange_conv: PAM on this system is broken - appdata_ptr == NULL !\n")); + free_pw_chat(pw_chat); + return PAM_CONV_ERR; + } + + reply = malloc(sizeof(struct pam_response) * num_msg); + if (!reply) { + DEBUG(0,("smb_pam_passchange_conv: malloc for reply failed!\n")); + free_pw_chat(pw_chat); + return PAM_CONV_ERR; + } + + for (replies = 0; replies < num_msg; replies++) { + found = False; + DEBUG(10,("smb_pam_passchange_conv: Processing message %d\n", replies)); + switch (msg[replies]->msg_style) { + case PAM_PROMPT_ECHO_ON: + DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_ON: PAM said: %s\n", msg[replies]->msg)); + fstrcpy(current_prompt, msg[replies]->msg); + trim_string(current_prompt, " ", " "); + for (t=pw_chat; t; t=t->next) { + + DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_ON: trying to match |%s| to |%s|\n", + t->prompt, current_prompt )); + + if (unix_wild_match(t->prompt, current_prompt) == 0) { + fstrcpy(current_reply, t->reply); + DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_ON: We sent: %s\n", current_reply)); + pwd_sub(current_reply, udp->PAM_username, udp->PAM_password, udp->PAM_newpassword); +#ifdef DEBUG_PASSWORD + DEBUG(100,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_ON: We actualy sent: %s\n", current_reply)); +#endif + reply[replies].resp_retcode = PAM_SUCCESS; + reply[replies].resp = COPY_STRING(current_reply); + found = True; + break; + } + } + /* PAM frees resp */ + if (!found) { + DEBUG(3,("smb_pam_passchange_conv: Could not find reply for PAM prompt: %s\n",msg[replies]->msg)); + free_pw_chat(pw_chat); + SAFE_FREE(reply); + return PAM_CONV_ERR; + } + break; + + case PAM_PROMPT_ECHO_OFF: + DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_OFF: PAM said: %s\n", msg[replies]->msg)); + fstrcpy(current_prompt, msg[replies]->msg); + trim_string(current_prompt, " ", " "); + for (t=pw_chat; t; t=t->next) { + + DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_OFF: trying to match |%s| to |%s|\n", + t->prompt, current_prompt )); + + if (unix_wild_match(t->prompt, current_prompt) == 0) { + fstrcpy(current_reply, t->reply); + DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_OFF: We sent: %s\n", current_reply)); + pwd_sub(current_reply, udp->PAM_username, udp->PAM_password, udp->PAM_newpassword); + reply[replies].resp_retcode = PAM_SUCCESS; + reply[replies].resp = COPY_STRING(current_reply); +#ifdef DEBUG_PASSWORD + DEBUG(100,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_OFF: We actualy sent: %s\n", current_reply)); +#endif + found = True; + break; + } + } + /* PAM frees resp */ + + if (!found) { + DEBUG(3,("smb_pam_passchange_conv: Could not find reply for PAM prompt: %s\n",msg[replies]->msg)); + free_pw_chat(pw_chat); + SAFE_FREE(reply); + return PAM_CONV_ERR; + } + break; + + case PAM_TEXT_INFO: + /* fall through */ + + case PAM_ERROR_MSG: + /* ignore it... */ + reply[replies].resp_retcode = PAM_SUCCESS; + reply[replies].resp = NULL; + break; + + default: + /* Must be an error of some sort... */ + free_pw_chat(pw_chat); + SAFE_FREE(reply); + return PAM_CONV_ERR; + } + } + + free_pw_chat(pw_chat); + if (reply) + *resp = reply; + return PAM_SUCCESS; +} + +/*************************************************************************** + Free up a malloced pam_conv struct. +****************************************************************************/ + +static void smb_free_pam_conv(struct pam_conv *pconv) +{ + if (pconv) + SAFE_FREE(pconv->appdata_ptr); + + SAFE_FREE(pconv); +} + +/*************************************************************************** + Allocate a pam_conv struct. +****************************************************************************/ + +static struct pam_conv *smb_setup_pam_conv(smb_pam_conv_fn smb_pam_conv_fnptr, const char *user, + const char *passwd, const char *newpass) +{ + struct pam_conv *pconv = (struct pam_conv *)malloc(sizeof(struct pam_conv)); + struct smb_pam_userdata *udp = (struct smb_pam_userdata *)malloc(sizeof(struct smb_pam_userdata)); + + if (pconv == NULL || udp == NULL) { + SAFE_FREE(pconv); + SAFE_FREE(udp); + return NULL; + } + + udp->PAM_username = user; + udp->PAM_password = passwd; + udp->PAM_newpassword = newpass; + + pconv->conv = smb_pam_conv_fnptr; + pconv->appdata_ptr = (void *)udp; + return pconv; +} + +/* + * PAM Closing out cleanup handler + */ + +static BOOL smb_pam_end(pam_handle_t *pamh, struct pam_conv *smb_pam_conv_ptr) +{ + int pam_error; + + smb_free_pam_conv(smb_pam_conv_ptr); + + if( pamh != NULL ) { + pam_error = pam_end(pamh, 0); + if(smb_pam_error_handler(pamh, pam_error, "End Cleanup Failed", 2) == True) { + DEBUG(4, ("smb_pam_end: PAM: PAM_END OK.\n")); + return True; + } + } + DEBUG(2,("smb_pam_end: PAM: not initialised")); + return False; +} + +/* + * Start PAM authentication for specified account + */ + +static BOOL smb_pam_start(pam_handle_t **pamh, const char *user, const char *rhost, struct pam_conv *pconv) +{ + int pam_error; + const char *our_rhost; + + *pamh = (pam_handle_t *)NULL; + + DEBUG(4,("smb_pam_start: PAM: Init user: %s\n", user)); + + pam_error = pam_start("samba", user, pconv, pamh); + if( !smb_pam_error_handler(*pamh, pam_error, "Init Failed", 0)) { + *pamh = (pam_handle_t *)NULL; + return False; + } + + if (rhost == NULL) { + our_rhost = client_name(); + if (strequal(rhost,"UNKNOWN")) + our_rhost = client_addr(); + } else { + our_rhost = rhost; + } + +#ifdef PAM_RHOST + DEBUG(4,("smb_pam_start: PAM: setting rhost to: %s\n", our_rhost)); + pam_error = pam_set_item(*pamh, PAM_RHOST, our_rhost); + if(!smb_pam_error_handler(*pamh, pam_error, "set rhost failed", 0)) { + smb_pam_end(*pamh, pconv); + *pamh = (pam_handle_t *)NULL; + return False; + } +#endif +#ifdef PAM_TTY + DEBUG(4,("smb_pam_start: PAM: setting tty\n")); + pam_error = pam_set_item(*pamh, PAM_TTY, "samba"); + if (!smb_pam_error_handler(*pamh, pam_error, "set tty failed", 0)) { + smb_pam_end(*pamh, pconv); + *pamh = (pam_handle_t *)NULL; + return False; + } +#endif + DEBUG(4,("smb_pam_start: PAM: Init passed for user: %s\n", user)); + return True; +} + +/* + * PAM Authentication Handler + */ +static NTSTATUS smb_pam_auth(pam_handle_t *pamh, char *user) +{ + int pam_error; + NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE; + + /* + * To enable debugging set in /etc/pam.d/samba: + * auth required /lib/security/pam_pwdb.so nullok shadow audit + */ + + DEBUG(4,("smb_pam_auth: PAM: Authenticate User: %s\n", user)); + pam_error = pam_authenticate(pamh, PAM_SILENT | lp_null_passwords() ? 0 : PAM_DISALLOW_NULL_AUTHTOK); + switch( pam_error ){ + case PAM_AUTH_ERR: + DEBUG(2, ("smb_pam_auth: PAM: Athentication Error for user %s\n", user)); + break; + case PAM_CRED_INSUFFICIENT: + DEBUG(2, ("smb_pam_auth: PAM: Insufficient Credentials for user %s\n", user)); + break; + case PAM_AUTHINFO_UNAVAIL: + DEBUG(2, ("smb_pam_auth: PAM: Authentication Information Unavailable for user %s\n", user)); + break; + case PAM_USER_UNKNOWN: + DEBUG(2, ("smb_pam_auth: PAM: Username %s NOT known to Authentication system\n", user)); + break; + case PAM_MAXTRIES: + DEBUG(2, ("smb_pam_auth: PAM: One or more authentication modules reports user limit for user %s exceeeded\n", user)); + break; + case PAM_ABORT: + DEBUG(0, ("smb_pam_auth: PAM: One or more PAM modules failed to load for user %s\n", user)); + break; + case PAM_SUCCESS: + DEBUG(4, ("smb_pam_auth: PAM: User %s Authenticated OK\n", user)); + break; + default: + DEBUG(0, ("smb_pam_auth: PAM: UNKNOWN ERROR while authenticating user %s\n", user)); + break; + } + + smb_pam_nt_status_error_handler(pamh, pam_error, "Authentication Failure", 2, &nt_status); + return nt_status; +} + +/* + * PAM Account Handler + */ +static NTSTATUS smb_pam_account(pam_handle_t *pamh, const char * user) +{ + int pam_error; + NTSTATUS nt_status = NT_STATUS_ACCOUNT_DISABLED; + + DEBUG(4,("smb_pam_account: PAM: Account Management for User: %s\n", user)); + pam_error = pam_acct_mgmt(pamh, PAM_SILENT); /* Is user account enabled? */ + switch( pam_error ) { + case PAM_AUTHTOK_EXPIRED: + DEBUG(2, ("smb_pam_account: PAM: User %s is valid but password is expired\n", user)); + break; + case PAM_ACCT_EXPIRED: + DEBUG(2, ("smb_pam_account: PAM: User %s no longer permitted to access system\n", user)); + break; + case PAM_AUTH_ERR: + DEBUG(2, ("smb_pam_account: PAM: There was an authentication error for user %s\n", user)); + break; + case PAM_PERM_DENIED: + DEBUG(0, ("smb_pam_account: PAM: User %s is NOT permitted to access system at this time\n", user)); + break; + case PAM_USER_UNKNOWN: + DEBUG(0, ("smb_pam_account: PAM: User \"%s\" is NOT known to account management\n", user)); + break; + case PAM_SUCCESS: + DEBUG(4, ("smb_pam_account: PAM: Account OK for User: %s\n", user)); + break; + default: + DEBUG(0, ("smb_pam_account: PAM: UNKNOWN PAM ERROR (%d) during Account Management for User: %s\n", pam_error, user)); + break; + } + + smb_pam_nt_status_error_handler(pamh, pam_error, "Account Check Failed", 2, &nt_status); + return nt_status; +} + +/* + * PAM Credential Setting + */ + +static NTSTATUS smb_pam_setcred(pam_handle_t *pamh, char * user) +{ + int pam_error; + NTSTATUS nt_status = NT_STATUS_NO_TOKEN; + + /* + * This will allow samba to aquire a kerberos token. And, when + * exporting an AFS cell, be able to /write/ to this cell. + */ + + DEBUG(4,("PAM: Account Management SetCredentials for User: %s\n", user)); + pam_error = pam_setcred(pamh, (PAM_ESTABLISH_CRED|PAM_SILENT)); + switch( pam_error ) { + case PAM_CRED_UNAVAIL: + DEBUG(0, ("smb_pam_setcred: PAM: Credentials not found for user:%s\n", user )); + break; + case PAM_CRED_EXPIRED: + DEBUG(0, ("smb_pam_setcred: PAM: Credentials for user: \"%s\" EXPIRED!\n", user )); + break; + case PAM_USER_UNKNOWN: + DEBUG(0, ("smb_pam_setcred: PAM: User: \"%s\" is NOT known so can not set credentials!\n", user )); + break; + case PAM_CRED_ERR: + DEBUG(0, ("smb_pam_setcred: PAM: Unknown setcredentials error - unable to set credentials for %s\n", user )); + break; + case PAM_SUCCESS: + DEBUG(4, ("smb_pam_setcred: PAM: SetCredentials OK for User: %s\n", user)); + break; + default: + DEBUG(0, ("smb_pam_setcred: PAM: UNKNOWN PAM ERROR (%d) during SetCredentials for User: %s\n", pam_error, user)); + break; + } + + smb_pam_nt_status_error_handler(pamh, pam_error, "Set Credential Failure", 2, &nt_status); + return nt_status; +} + +/* + * PAM Internal Session Handler + */ +static BOOL smb_internal_pam_session(pam_handle_t *pamh, char *user, char *tty, BOOL flag) +{ + int pam_error; + +#ifdef PAM_TTY + DEBUG(4,("smb_internal_pam_session: PAM: tty set to: %s\n", tty)); + pam_error = pam_set_item(pamh, PAM_TTY, tty); + if (!smb_pam_error_handler(pamh, pam_error, "set tty failed", 0)) + return False; +#endif + + if (flag) { + pam_error = pam_open_session(pamh, PAM_SILENT); + if (!smb_pam_error_handler(pamh, pam_error, "session setup failed", 0)) + return False; + } else { + pam_setcred(pamh, (PAM_DELETE_CRED|PAM_SILENT)); /* We don't care if this fails */ + pam_error = pam_close_session(pamh, PAM_SILENT); /* This will probably pick up the error anyway */ + if (!smb_pam_error_handler(pamh, pam_error, "session close failed", 0)) + return False; + } + return (True); +} + +/* + * Internal PAM Password Changer. + */ + +static BOOL smb_pam_chauthtok(pam_handle_t *pamh, const char * user) +{ + int pam_error; + + DEBUG(4,("smb_pam_chauthtok: PAM: Password Change for User: %s\n", user)); + + pam_error = pam_chauthtok(pamh, PAM_SILENT); /* Change Password */ + + switch( pam_error ) { + case PAM_AUTHTOK_ERR: + DEBUG(2, ("PAM: unable to obtain the new authentication token - is password to weak?\n")); + break; + + /* This doesn't seem to be defined on Solaris. JRA */ +#ifdef PAM_AUTHTOK_RECOVER_ERR + case PAM_AUTHTOK_RECOVER_ERR: + DEBUG(2, ("PAM: unable to obtain the old authentication token - was the old password wrong?.\n")); + break; +#endif + + case PAM_AUTHTOK_LOCK_BUSY: + DEBUG(2, ("PAM: unable to change the authentication token since it is currently locked.\n")); + break; + case PAM_AUTHTOK_DISABLE_AGING: + DEBUG(2, ("PAM: Authentication token aging has been disabled.\n")); + break; + case PAM_PERM_DENIED: + DEBUG(0, ("PAM: Permission denied.\n")); + break; + case PAM_TRY_AGAIN: + DEBUG(0, ("PAM: Could not update all authentication token(s). No authentication tokens were updated.\n")); + break; + case PAM_USER_UNKNOWN: + DEBUG(0, ("PAM: User not known to PAM\n")); + break; + case PAM_SUCCESS: + DEBUG(4, ("PAM: Account OK for User: %s\n", user)); + break; + default: + DEBUG(0, ("PAM: UNKNOWN PAM ERROR (%d) for User: %s\n", pam_error, user)); + } + + if(!smb_pam_error_handler(pamh, pam_error, "Password Change Failed", 2)) { + return False; + } + + /* If this point is reached, the password has changed. */ + return True; +} + +/* + * PAM Externally accessible Session handler + */ + +BOOL smb_pam_claim_session(char *user, char *tty, char *rhost) +{ + pam_handle_t *pamh = NULL; + struct pam_conv *pconv = NULL; + + /* Ignore PAM if told to. */ + + if (!lp_obey_pam_restrictions()) + return True; + + if ((pconv = smb_setup_pam_conv(smb_pam_conv, user, NULL, NULL)) == NULL) + return False; + + if (!smb_pam_start(&pamh, user, rhost, pconv)) + return False; + + if (!smb_internal_pam_session(pamh, user, tty, True)) { + smb_pam_end(pamh, pconv); + return False; + } + + return smb_pam_end(pamh, pconv); +} + +/* + * PAM Externally accessible Session handler + */ + +BOOL smb_pam_close_session(char *user, char *tty, char *rhost) +{ + pam_handle_t *pamh = NULL; + struct pam_conv *pconv = NULL; + + /* Ignore PAM if told to. */ + + if (!lp_obey_pam_restrictions()) + return True; + + if ((pconv = smb_setup_pam_conv(smb_pam_conv, user, NULL, NULL)) == NULL) + return False; + + if (!smb_pam_start(&pamh, user, rhost, pconv)) + return False; + + if (!smb_internal_pam_session(pamh, user, tty, False)) { + smb_pam_end(pamh, pconv); + return False; + } + + return smb_pam_end(pamh, pconv); +} + +/* + * PAM Externally accessible Account handler + */ + +NTSTATUS smb_pam_accountcheck(const char * user) +{ + NTSTATUS nt_status = NT_STATUS_ACCOUNT_DISABLED; + pam_handle_t *pamh = NULL; + struct pam_conv *pconv = NULL; + + /* Ignore PAM if told to. */ + + if (!lp_obey_pam_restrictions()) + return NT_STATUS_OK; + + if ((pconv = smb_setup_pam_conv(smb_pam_conv, user, NULL, NULL)) == NULL) + return NT_STATUS_NO_MEMORY; + + if (!smb_pam_start(&pamh, user, NULL, pconv)) + return NT_STATUS_ACCOUNT_DISABLED; + + if (!NT_STATUS_IS_OK(nt_status = smb_pam_account(pamh, user))) + DEBUG(0, ("smb_pam_accountcheck: PAM: Account Validation Failed - Rejecting User %s!\n", user)); + + smb_pam_end(pamh, pconv); + return nt_status; +} + +/* + * PAM Password Validation Suite + */ + +NTSTATUS smb_pam_passcheck(char * user, char * password) +{ + pam_handle_t *pamh = NULL; + NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE; + struct pam_conv *pconv = NULL; + + /* + * Note we can't ignore PAM here as this is the only + * way of doing auths on plaintext passwords when + * compiled --with-pam. + */ + + if ((pconv = smb_setup_pam_conv(smb_pam_conv, user, password, NULL)) == NULL) + return NT_STATUS_LOGON_FAILURE; + + if (!smb_pam_start(&pamh, user, NULL, pconv)) + return NT_STATUS_LOGON_FAILURE; + + if (!NT_STATUS_IS_OK(nt_status = smb_pam_auth(pamh, user))) { + DEBUG(0, ("smb_pam_passcheck: PAM: smb_pam_auth failed - Rejecting User %s !\n", user)); + smb_pam_end(pamh, pconv); + return nt_status; + } + + if (!NT_STATUS_IS_OK(nt_status = smb_pam_account(pamh, user))) { + DEBUG(0, ("smb_pam_passcheck: PAM: smb_pam_account failed - Rejecting User %s !\n", user)); + smb_pam_end(pamh, pconv); + return nt_status; + } + + if (!NT_STATUS_IS_OK(nt_status = smb_pam_setcred(pamh, user))) { + DEBUG(0, ("smb_pam_passcheck: PAM: smb_pam_setcred failed - Rejecting User %s !\n", user)); + smb_pam_end(pamh, pconv); + return nt_status; + } + + smb_pam_end(pamh, pconv); + return nt_status; +} + +/* + * PAM Password Change Suite + */ + +BOOL smb_pam_passchange(const char * user, const char * oldpassword, const char * newpassword) +{ + /* Appropriate quantities of root should be obtained BEFORE calling this function */ + struct pam_conv *pconv = NULL; + pam_handle_t *pamh = NULL; + + if ((pconv = smb_setup_pam_conv(smb_pam_passchange_conv, user, oldpassword, newpassword)) == NULL) + return False; + + if(!smb_pam_start(&pamh, user, NULL, pconv)) + return False; + + if (!smb_pam_chauthtok(pamh, user)) { + DEBUG(0, ("smb_pam_passchange: PAM: Password Change Failed for user %s!\n", user)); + smb_pam_end(pamh, pconv); + return False; + } + + return smb_pam_end(pamh, pconv); +} + +#else + +/* If PAM not used, no PAM restrictions on accounts. */ +NTSTATUS smb_pam_accountcheck(const char * user) +{ + return NT_STATUS_OK; +} + +/* If PAM not used, also no PAM restrictions on sessions. */ +BOOL smb_pam_claim_session(char *user, char *tty, char *rhost) +{ + return True; +} + +/* If PAM not used, also no PAM restrictions on sessions. */ +BOOL smb_pam_close_session(char *in_user, char *tty, char *rhost) +{ + return True; +} +#endif /* WITH_PAM */ diff --git a/source3/auth/pass_check.c b/source3/auth/pass_check.c new file mode 100644 index 0000000000..47c9664a74 --- /dev/null +++ b/source3/auth/pass_check.c @@ -0,0 +1,793 @@ +/* + Unix SMB/CIFS implementation. + Password checking + Copyright (C) Andrew Tridgell 1992-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* this module is for checking a username/password against a system + password database. The SMB encrypted password support is elsewhere */ + +#include "includes.h" + +/* these are kept here to keep the string_combinations function simple */ +static fstring this_user; +#if !defined(WITH_PAM) +static fstring this_salt; +static fstring this_crypted; +#endif + +#ifdef WITH_AFS + +#include <afs/stds.h> +#include <afs/kautils.h> + +/******************************************************************* +check on AFS authentication +********************************************************************/ +static BOOL afs_auth(char *user, char *password) +{ + long password_expires = 0; + char *reason; + + /* For versions of AFS prior to 3.3, this routine has few arguments, */ + /* but since I can't find the old documentation... :-) */ + setpag(); + if (ka_UserAuthenticateGeneral + (KA_USERAUTH_VERSION + KA_USERAUTH_DOSETPAG, user, (char *)0, /* instance */ + (char *)0, /* cell */ + password, 0, /* lifetime, default */ + &password_expires, /*days 'til it expires */ + 0, /* spare 2 */ + &reason) == 0) + { + return (True); + } + DEBUG(1, + ("AFS authentication for \"%s\" failed (%s)\n", user, reason)); + return (False); +} +#endif + + +#ifdef WITH_DFS + +#include <dce/dce_error.h> +#include <dce/sec_login.h> + +/***************************************************************** + This new version of the DFS_AUTH code was donated by Karsten Muuss + <muuss@or.uni-bonn.de>. It fixes the following problems with the + old code : + + - Server credentials may expire + - Client credential cache files have wrong owner + - purge_context() function is called with invalid argument + + This new code was modified to ensure that on exit the uid/gid is + still root, and the original directory is restored. JRA. +******************************************************************/ + +sec_login_handle_t my_dce_sec_context; +int dcelogin_atmost_once = 0; + +/******************************************************************* +check on a DCE/DFS authentication +********************************************************************/ +static BOOL dfs_auth(char *user, char *password) +{ + error_status_t err; + int err2; + int prterr; + signed32 expire_time, current_time; + boolean32 password_reset; + struct passwd *pw; + sec_passwd_rec_t passwd_rec; + sec_login_auth_src_t auth_src = sec_login_auth_src_network; + unsigned char dce_errstr[dce_c_error_string_len]; + gid_t egid; + + if (dcelogin_atmost_once) + return (False); + +#ifdef HAVE_CRYPT + /* + * We only go for a DCE login context if the given password + * matches that stored in the local password file.. + * Assumes local passwd file is kept in sync w/ DCE RGY! + */ + + if (strcmp((char *)crypt(password, this_salt), this_crypted)) + { + return (False); + } +#endif + + sec_login_get_current_context(&my_dce_sec_context, &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr)); + + return (False); + } + + sec_login_certify_identity(my_dce_sec_context, &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr)); + + return (False); + } + + sec_login_get_expiration(my_dce_sec_context, &expire_time, &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr)); + + return (False); + } + + time(¤t_time); + + if (expire_time < (current_time + 60)) + { + struct passwd *pw; + sec_passwd_rec_t *key; + + sec_login_get_pwent(my_dce_sec_context, + (sec_login_passwd_t *) & pw, &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr)); + + return (False); + } + + sec_login_refresh_identity(my_dce_sec_context, &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, ("DCE can't refresh identity. %s\n", + dce_errstr)); + + return (False); + } + + sec_key_mgmt_get_key(rpc_c_authn_dce_secret, NULL, + (unsigned char *)pw->pw_name, + sec_c_key_version_none, + (void **)&key, &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, ("DCE can't get key for %s. %s\n", + pw->pw_name, dce_errstr)); + + return (False); + } + + sec_login_valid_and_cert_ident(my_dce_sec_context, key, + &password_reset, &auth_src, + &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, + ("DCE can't validate and certify identity for %s. %s\n", + pw->pw_name, dce_errstr)); + } + + sec_key_mgmt_free_key(key, &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, ("DCE can't free key.\n", dce_errstr)); + } + } + + if (sec_login_setup_identity((unsigned char *)user, + sec_login_no_flags, + &my_dce_sec_context, &err) == 0) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, ("DCE Setup Identity for %s failed: %s\n", + user, dce_errstr)); + return (False); + } + + sec_login_get_pwent(my_dce_sec_context, + (sec_login_passwd_t *) & pw, &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr)); + + return (False); + } + + sec_login_purge_context(&my_dce_sec_context, &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, ("DCE can't purge context. %s\n", dce_errstr)); + + return (False); + } + + /* + * NB. I'd like to change these to call something like change_to_user() + * instead but currently we don't have a connection + * context to become the correct user. This is already + * fairly platform specific code however, so I think + * this should be ok. I have added code to go + * back to being root on error though. JRA. + */ + + egid = getegid(); + + set_effective_gid(pw->pw_gid); + set_effective_uid(pw->pw_uid); + + if (sec_login_setup_identity((unsigned char *)user, + sec_login_no_flags, + &my_dce_sec_context, &err) == 0) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, ("DCE Setup Identity for %s failed: %s\n", + user, dce_errstr)); + goto err; + } + + sec_login_get_pwent(my_dce_sec_context, + (sec_login_passwd_t *) & pw, &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr)); + goto err; + } + + passwd_rec.version_number = sec_passwd_c_version_none; + passwd_rec.pepper = NULL; + passwd_rec.key.key_type = sec_passwd_plain; + passwd_rec.key.tagged_union.plain = (idl_char *) password; + + sec_login_validate_identity(my_dce_sec_context, + &passwd_rec, &password_reset, + &auth_src, &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, + ("DCE Identity Validation failed for principal %s: %s\n", + user, dce_errstr)); + goto err; + } + + sec_login_certify_identity(my_dce_sec_context, &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, ("DCE certify identity failed: %s\n", dce_errstr)); + goto err; + } + + if (auth_src != sec_login_auth_src_network) + { + DEBUG(0, ("DCE context has no network credentials.\n")); + } + + sec_login_set_context(my_dce_sec_context, &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, + ("DCE login failed for principal %s, cant set context: %s\n", + user, dce_errstr)); + + sec_login_purge_context(&my_dce_sec_context, &err); + goto err; + } + + sec_login_get_pwent(my_dce_sec_context, + (sec_login_passwd_t *) & pw, &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr)); + goto err; + } + + DEBUG(0, ("DCE login succeeded for principal %s on pid %d\n", + user, sys_getpid())); + + DEBUG(3, ("DCE principal: %s\n" + " uid: %d\n" + " gid: %d\n", + pw->pw_name, pw->pw_uid, pw->pw_gid)); + DEBUG(3, (" info: %s\n" + " dir: %s\n" + " shell: %s\n", + pw->pw_gecos, pw->pw_dir, pw->pw_shell)); + + sec_login_get_expiration(my_dce_sec_context, &expire_time, &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr)); + goto err; + } + + set_effective_uid(0); + set_effective_gid(0); + + DEBUG(0, + ("DCE context expires: %s", asctime(localtime(&expire_time)))); + + dcelogin_atmost_once = 1; + return (True); + + err: + + /* Go back to root, JRA. */ + set_effective_uid(0); + set_effective_gid(egid); + return (False); +} + +void dfs_unlogin(void) +{ + error_status_t err; + int err2; + unsigned char dce_errstr[dce_c_error_string_len]; + + sec_login_purge_context(&my_dce_sec_context, &err); + if (err != error_status_ok) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0, + ("DCE purge login context failed for server instance %d: %s\n", + sys_getpid(), dce_errstr)); + } +} +#endif + +#ifdef LINUX_BIGCRYPT +/**************************************************************************** +an enhanced crypt for Linux to handle password longer than 8 characters +****************************************************************************/ +static int linux_bigcrypt(char *password, char *salt1, char *crypted) +{ +#define LINUX_PASSWORD_SEG_CHARS 8 + char salt[3]; + int i; + + StrnCpy(salt, salt1, 2); + crypted += 2; + + for (i = strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) { + char *p = crypt(password, salt) + 2; + if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0) + return (0); + password += LINUX_PASSWORD_SEG_CHARS; + crypted += strlen(p); + } + + return (1); +} +#endif + +#ifdef OSF1_ENH_SEC +/**************************************************************************** +an enhanced crypt for OSF1 +****************************************************************************/ +static char *osf1_bigcrypt(char *password, char *salt1) +{ + static char result[AUTH_MAX_PASSWD_LENGTH] = ""; + char *p1; + char *p2 = password; + char salt[3]; + int i; + int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS; + if (strlen(password) % AUTH_CLEARTEXT_SEG_CHARS) + parts++; + + StrnCpy(salt, salt1, 2); + StrnCpy(result, salt1, 2); + result[2] = '\0'; + + for (i = 0; i < parts; i++) { + p1 = crypt(p2, salt); + strncat(result, p1 + 2, + AUTH_MAX_PASSWD_LENGTH - strlen(p1 + 2) - 1); + StrnCpy(salt, &result[2 + i * AUTH_CIPHERTEXT_SEG_CHARS], 2); + p2 += AUTH_CLEARTEXT_SEG_CHARS; + } + + return (result); +} +#endif + + +/**************************************************************************** +apply a function to upper/lower case combinations +of a string and return true if one of them returns true. +try all combinations with N uppercase letters. +offset is the first char to try and change (start with 0) +it assumes the string starts lowercased +****************************************************************************/ +static NTSTATUS string_combinations2(char *s, int offset, NTSTATUS (*fn) (char *), + int N) +{ + int len = strlen(s); + int i; + NTSTATUS nt_status; + +#ifdef PASSWORD_LENGTH + len = MIN(len, PASSWORD_LENGTH); +#endif + + if (N <= 0 || offset >= len) + return (fn(s)); + + for (i = offset; i < (len - (N - 1)); i++) { + char c = s[i]; + if (!islower(c)) + continue; + s[i] = toupper(c); + if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, i + 1, fn, N - 1),NT_STATUS_WRONG_PASSWORD)) { + return (nt_status); + } + s[i] = c; + } + return (NT_STATUS_WRONG_PASSWORD); +} + +/**************************************************************************** +apply a function to upper/lower case combinations +of a string and return true if one of them returns true. +try all combinations with up to N uppercase letters. +offset is the first char to try and change (start with 0) +it assumes the string starts lowercased +****************************************************************************/ +static NTSTATUS string_combinations(char *s, NTSTATUS (*fn) (char *), int N) +{ + int n; + NTSTATUS nt_status; + for (n = 1; n <= N; n++) + if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, 0, fn, n), NT_STATUS_WRONG_PASSWORD)) + return nt_status; + return NT_STATUS_WRONG_PASSWORD; +} + + +/**************************************************************************** +core of password checking routine +****************************************************************************/ +static NTSTATUS password_check(char *password) +{ +#ifdef WITH_PAM + return smb_pam_passcheck(this_user, password); +#else + + BOOL ret; + +#ifdef WITH_AFS + if (afs_auth(this_user, password)) + return NT_STATUS_OK; +#endif /* WITH_AFS */ + +#ifdef WITH_DFS + if (dfs_auth(this_user, password)) + return NT_STATUS_OK; +#endif /* WITH_DFS */ + +#ifdef OSF1_ENH_SEC + + ret = (strcmp(osf1_bigcrypt(password, this_salt), + this_crypted) == 0); + if (!ret) { + DEBUG(2, + ("OSF1_ENH_SEC failed. Trying normal crypt.\n")); + ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0); + } + if (ret) { + return NT_STATUS_OK; + } else { + return NT_STATUS_WRONG_PASSWORD; + } + +#endif /* OSF1_ENH_SEC */ + +#ifdef ULTRIX_AUTH + ret = (strcmp((char *)crypt16(password, this_salt), this_crypted) == 0); + if (ret) { + return NT_STATUS_OK; + } else { + return NT_STATUS_WRONG_PASSWORD; + } + +#endif /* ULTRIX_AUTH */ + +#ifdef LINUX_BIGCRYPT + ret = (linux_bigcrypt(password, this_salt, this_crypted)); + if (ret) { + return NT_STATUS_OK; + } else { + return NT_STATUS_WRONG_PASSWORD; + } +#endif /* LINUX_BIGCRYPT */ + +#if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS) + + /* + * Some systems have bigcrypt in the C library but might not + * actually use it for the password hashes (HPUX 10.20) is + * a noteable example. So we try bigcrypt first, followed + * by crypt. + */ + + if (strcmp(bigcrypt(password, this_salt), this_crypted) == 0) + return NT_STATUS_OK; + else + ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0); + if (ret) { + return NT_STATUS_OK; + } else { + return NT_STATUS_WRONG_PASSWORD; + } +#else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */ + +#ifdef HAVE_BIGCRYPT + ret = (strcmp(bigcrypt(password, this_salt), this_crypted) == 0); + if (ret) { + return NT_STATUS_OK; + } else { + return NT_STATUS_WRONG_PASSWORD; + } +#endif /* HAVE_BIGCRYPT */ + +#ifndef HAVE_CRYPT + DEBUG(1, ("Warning - no crypt available\n")); + return NT_STATUS_LOGON_FAILURE; +#else /* HAVE_CRYPT */ + ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0); + if (ret) { + return NT_STATUS_OK; + } else { + return NT_STATUS_WRONG_PASSWORD; + } +#endif /* HAVE_CRYPT */ +#endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */ +#endif /* WITH_PAM || KRB4_AUTH || KRB5_AUTH */ +} + + + +/**************************************************************************** +CHECK if a username/password is OK +the function pointer fn() points to a function to call when a successful +match is found and is used to update the encrypted password file +return NT_STATUS_OK on correct match, appropriate error otherwise +****************************************************************************/ + +NTSTATUS pass_check(const struct passwd *input_pass, char *user, char *password, + int pwlen, BOOL (*fn) (char *, char *), BOOL run_cracker) +{ + struct passwd *pass; + pstring pass2; + int level = lp_passwordlevel(); + + NTSTATUS nt_status; + if (password) + password[pwlen] = 0; + +#if DEBUG_PASSWORD + DEBUG(100, ("checking user=[%s] pass=[%s]\n", user, password)); +#endif + + if (!password) + return NT_STATUS_LOGON_FAILURE; + + if (((!*password) || (!pwlen)) && !lp_null_passwords()) + return NT_STATUS_LOGON_FAILURE; + +#if defined(WITH_PAM) + + /* + * If we're using PAM we want to short-circuit all the + * checks below and dive straight into the PAM code. + */ + + fstrcpy(this_user, user); + + DEBUG(4, ("pass_check: Checking (PAM) password for user %s (l=%d)\n", user, pwlen)); + +#else /* Not using PAM */ + + DEBUG(4, ("pass_check: Checking password for user %s (l=%d)\n", user, pwlen)); + + if (!input_pass) { + DEBUG(3, ("Couldn't find user %s\n", user)); + return NT_STATUS_NO_SUCH_USER; + } + + pass = make_modifyable_passwd(input_pass); + +#ifdef HAVE_GETSPNAM + { + struct spwd *spass; + + /* many shadow systems require you to be root to get + the password, in most cases this should already be + the case when this function is called, except + perhaps for IPC password changing requests */ + + spass = getspnam(pass->pw_name); + if (spass && spass->sp_pwdp) + pstrcpy(pass->pw_passwd, spass->sp_pwdp); + } +#elif defined(IA_UINFO) + { + /* Need to get password with SVR4.2's ia_ functions + instead of get{sp,pw}ent functions. Required by + UnixWare 2.x, tested on version + 2.1. (tangent@cyberport.com) */ + uinfo_t uinfo; + if (ia_openinfo(pass->pw_name, &uinfo) != -1) + ia_get_logpwd(uinfo, &(pass->pw_passwd)); + } +#endif + +#ifdef HAVE_GETPRPWNAM + { + struct pr_passwd *pr_pw = getprpwnam(pass->pw_name); + if (pr_pw && pr_pw->ufld.fd_encrypt) + pstrcpy(pass->pw_passwd, pr_pw->ufld.fd_encrypt); + } +#endif + +#ifdef HAVE_GETPWANAM + { + struct passwd_adjunct *pwret; + pwret = getpwanam(s); + if (pwret && pwret->pwa_passwd) + pstrcpy(pass->pw_passwd,pwret->pwa_passwd); + } +#endif + +#ifdef OSF1_ENH_SEC + { + struct pr_passwd *mypasswd; + DEBUG(5, ("Checking password for user %s in OSF1_ENH_SEC\n", + user)); + mypasswd = getprpwnam(user); + if (mypasswd) { + fstrcpy(pass->pw_name, mypasswd->ufld.fd_name); + fstrcpy(pass->pw_passwd, mypasswd->ufld.fd_encrypt); + } else { + DEBUG(5, + ("OSF1_ENH_SEC: No entry for user %s in protected database !\n", + user)); + } + } +#endif + +#ifdef ULTRIX_AUTH + { + AUTHORIZATION *ap = getauthuid(pass->pw_uid); + if (ap) { + fstrcpy(pass->pw_passwd, ap->a_password); + endauthent(); + } + } +#endif + + /* extract relevant info */ + fstrcpy(this_salt, pass->pw_passwd); + +#if defined(HAVE_TRUNCATED_SALT) + /* crypt on some platforms (HPUX in particular) + won't work with more than 2 salt characters. */ + this_salt[2] = 0; +#endif + + /* Copy into global for the convenience of looping code */ + fstrcpy(this_crypted, pass->pw_passwd); + + if (!*this_crypted) { + if (!lp_null_passwords()) { + DEBUG(2, ("Disallowing %s with null password\n", + this_user)); + passwd_free(&pass); + return NT_STATUS_LOGON_FAILURE; + } + if (!*password) { + DEBUG(3, + ("Allowing access to %s with null password\n", + this_user)); + passwd_free(&pass); + return NT_STATUS_OK; + } + } + + passwd_free(&pass); + +#endif /* defined(WITH_PAM) */ + + /* try it as it came to us */ + nt_status = password_check(password); + if NT_STATUS_IS_OK(nt_status) { + if (fn) { + fn(user, password); + } + return (nt_status); + } else if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) { + /* No point continuing if its not the password thats to blame (ie PAM disabled). */ + return (nt_status); + } + + if (!run_cracker) { + return (nt_status); + } + + /* if the password was given to us with mixed case then we don't + * need to proceed as we know it hasn't been case modified by the + * client */ + if (strhasupper(password) && strhaslower(password)) { + passwd_free(&pass); + return nt_status; + } + + /* make a copy of it */ + StrnCpy(pass2, password, sizeof(pstring) - 1); + + /* try all lowercase if it's currently all uppercase */ + if (strhasupper(password)) { + strlower(password); + if NT_STATUS_IS_OK(nt_status = password_check(password)) { + if (fn) + fn(user, password); + return (nt_status); + } + } + + /* give up? */ + if (level < 1) { + /* restore it */ + fstrcpy(password, pass2); + return NT_STATUS_WRONG_PASSWORD; + } + + /* last chance - all combinations of up to level chars upper! */ + strlower(password); + + + if NT_STATUS_IS_OK(nt_status = string_combinations(password, password_check, level)) { + if (fn) + fn(user, password); + return nt_status; + } + + /* restore it */ + fstrcpy(password, pass2); + + return NT_STATUS_WRONG_PASSWORD; +} |