diff options
Diffstat (limited to 'source3/auth')
-rw-r--r-- | source3/auth/auth.c | 550 | ||||
-rw-r--r-- | source3/auth/auth_builtin.c | 174 | ||||
-rw-r--r-- | source3/auth/auth_compat.c | 130 | ||||
-rw-r--r-- | source3/auth/auth_domain.c | 477 | ||||
-rw-r--r-- | source3/auth/auth_ntlmssp.c | 214 | ||||
-rw-r--r-- | source3/auth/auth_sam.c | 450 | ||||
-rw-r--r-- | source3/auth/auth_script.c | 147 | ||||
-rw-r--r-- | source3/auth/auth_server.c | 466 | ||||
-rw-r--r-- | source3/auth/auth_unix.c | 145 | ||||
-rw-r--r-- | source3/auth/auth_util.c | 2166 | ||||
-rw-r--r-- | source3/auth/auth_winbind.c | 153 | ||||
-rw-r--r-- | source3/auth/pampass.c | 886 | ||||
-rw-r--r-- | source3/auth/pass_check.c | 874 | ||||
-rw-r--r-- | source3/auth/token_util.c | 556 |
14 files changed, 7388 insertions, 0 deletions
diff --git a/source3/auth/auth.c b/source3/auth/auth.c new file mode 100644 index 0000000000..754cb7a508 --- /dev/null +++ b/source3/auth/auth.c @@ -0,0 +1,550 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_AUTH + +static_decl_auth; + +static struct auth_init_function_entry *backends = NULL; + +static struct auth_init_function_entry *auth_find_backend_entry(const char *name); + +NTSTATUS smb_register_auth(int version, const char *name, auth_init_function init) +{ + struct auth_init_function_entry *entry = backends; + + if (version != AUTH_INTERFACE_VERSION) { + DEBUG(0,("Can't register auth_method!\n" + "You tried to register an auth module with AUTH_INTERFACE_VERSION %d, while this version of samba uses %d\n", + version,AUTH_INTERFACE_VERSION)); + return NT_STATUS_OBJECT_TYPE_MISMATCH; + } + + if (!name || !init) { + return NT_STATUS_INVALID_PARAMETER; + } + + DEBUG(5,("Attempting to register auth backend %s\n", name)); + + if (auth_find_backend_entry(name)) { + DEBUG(0,("There already is an auth method registered with the name %s!\n", name)); + return NT_STATUS_OBJECT_NAME_COLLISION; + } + + entry = SMB_XMALLOC_P(struct auth_init_function_entry); + entry->name = smb_xstrdup(name); + entry->init = init; + + DLIST_ADD(backends, entry); + DEBUG(5,("Successfully added auth method '%s'\n", name)); + return NT_STATUS_OK; +} + +static struct auth_init_function_entry *auth_find_backend_entry(const char *name) +{ + struct auth_init_function_entry *entry = backends; + + while(entry) { + if (strcmp(entry->name, name)==0) return entry; + entry = entry->next; + } + + return NULL; +} + +/**************************************************************************** + Try to get a challenge out of the various authentication 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; + const 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 by module %s (normal)\n", + auth_context->challenge_set_by)); + return auth_context->challenge.data; + } + + auth_context->challenge_may_be_modified = False; + + for (auth_method = auth_context->auth_method_list; auth_method; auth_method = auth_method->next) { + if (auth_method->get_chal == NULL) { + DEBUG(5, ("auth_get_challenge: module %s did not want to specify a challenge\n", auth_method->name)); + 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: authentication method %s has already specified a challenge. Challenge by %s ignored.\n", + challenge_set_by, auth_method->name)); + continue; + } + + mem_ctx = talloc_init("auth_get_challenge for module %s", auth_method->name); + if (!mem_ctx) { + smb_panic("talloc_init() 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 authentication method %s FAILED.\n", + auth_method->name)); + } else { + DEBUG(5, ("auth_get_challenge: successfully 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)); + auth_context->challenge = data_blob_talloc(auth_context->mem_ctx, + chal, sizeof(chal)); + + challenge_set_by = "random"; + auth_context->challenge_may_be_modified = True; + } + + DEBUG(5, ("auth_context challenge created by %s\n", challenge_set_by)); + DEBUG(5, ("challenge is: \n")); + dump_data(5, 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_myname(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_context Supplies the challenges and some other data. + * Must be created with make_auth_context(), 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 authentication, + * including a struct samu 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) +{ + /* if all the modules say 'not for me' this is reasonable */ + NTSTATUS nt_status = NT_STATUS_NO_SUCH_USER; + const char *unix_username; + auth_methods *auth_method; + TALLOC_CTX *mem_ctx; + + if (!user_info || !auth_context || !server_info) + return NT_STATUS_LOGON_FAILURE; + + DEBUG(3, ("check_ntlm_password: Checking password for unmapped user [%s]\\[%s]@[%s] with the new password interface\n", + user_info->client_domain, user_info->smb_name, user_info->wksta_name)); + + DEBUG(3, ("check_ntlm_password: mapped user is: [%s]\\[%s]@[%s]\n", + user_info->domain, user_info->internal_username, user_info->wksta_name)); + + if (auth_context->challenge.length != 8) { + DEBUG(0, ("check_ntlm_password: Invalid challenge stored for this auth context - cannot continue\n")); + return NT_STATUS_LOGON_FAILURE; + } + + if (auth_context->challenge_set_by) + DEBUG(10, ("check_ntlm_password: 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", + (int)user_info->lm_resp.length, (int)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, user_info->domain)) + return NT_STATUS_LOGON_FAILURE; + + for (auth_method = auth_context->auth_method_list;auth_method; auth_method = auth_method->next) { + NTSTATUS result; + + mem_ctx = talloc_init("%s authentication for user %s\\%s", auth_method->name, + user_info->domain, user_info->smb_name); + + result = auth_method->auth(auth_context, auth_method->private_data, mem_ctx, user_info, server_info); + + /* check if the module did anything */ + if ( NT_STATUS_V(result) == NT_STATUS_V(NT_STATUS_NOT_IMPLEMENTED) ) { + DEBUG(10,("check_ntlm_password: %s had nothing to say\n", auth_method->name)); + talloc_destroy(mem_ctx); + continue; + } + + nt_status = result; + + if (NT_STATUS_IS_OK(nt_status)) { + DEBUG(3, ("check_ntlm_password: %s authentication for user [%s] succeeded\n", + auth_method->name, user_info->smb_name)); + } else { + DEBUG(5, ("check_ntlm_password: %s authentication for user [%s] FAILED with error %s\n", + auth_method->name, user_info->smb_name, nt_errstr(nt_status))); + } + + talloc_destroy(mem_ctx); + + if ( NT_STATUS_IS_OK(nt_status)) + { + break; + } + } + + /* successful authentication */ + + if (NT_STATUS_IS_OK(nt_status)) { + unix_username = (*server_info)->unix_name; + if (!(*server_info)->guest) { + /* We might not be root if we are an RPC call */ + become_root(); + nt_status = smb_pam_accountcheck(unix_username); + unbecome_root(); + + if (NT_STATUS_IS_OK(nt_status)) { + DEBUG(5, ("check_ntlm_password: PAM Account for user [%s] succeeded\n", + unix_username)); + } else { + DEBUG(3, ("check_ntlm_password: PAM Account for user [%s] FAILED with error %s\n", + unix_username, nt_errstr(nt_status))); + } + } + + if (NT_STATUS_IS_OK(nt_status)) { + DEBUG((*server_info)->guest ? 5 : 2, + ("check_ntlm_password: %sauthentication for user [%s] -> [%s] -> [%s] succeeded\n", + (*server_info)->guest ? "guest " : "", + user_info->smb_name, + user_info->internal_username, + unix_username)); + } + + return nt_status; + } + + /* failed authentication; check for guest lapping */ + + DEBUG(2, ("check_ntlm_password: Authentication for user [%s] -> [%s] FAILED with error %s\n", + user_info->smb_name, user_info->internal_username, + 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) +{ + auth_methods *auth_method; + + if (*auth_context) { + /* Free private data of context's authentication methods */ + for (auth_method = (*auth_context)->auth_method_list; auth_method; auth_method = auth_method->next) { + TALLOC_FREE(auth_method->private_data); + } + + 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("authentication context"); + + *auth_context = TALLOC_P(mem_ctx, struct 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; +} + +bool load_auth_module(struct auth_context *auth_context, + const char *module, auth_methods **ret) +{ + static bool initialised_static_modules = False; + + struct auth_init_function_entry *entry; + char *module_name = smb_xstrdup(module); + char *module_params = NULL; + char *p; + bool good = False; + + /* Initialise static modules if not done so yet */ + if(!initialised_static_modules) { + static_init_auth; + initialised_static_modules = True; + } + + DEBUG(5,("load_auth_module: Attempting to find an auth method to match %s\n", + module)); + + p = strchr(module_name, ':'); + if (p) { + *p = 0; + module_params = p+1; + trim_char(module_params, ' ', ' '); + } + + trim_char(module_name, ' ', ' '); + + entry = auth_find_backend_entry(module_name); + + if (entry == NULL) { + if (NT_STATUS_IS_OK(smb_probe_module("auth", module_name))) { + entry = auth_find_backend_entry(module_name); + } + } + + if (entry != NULL) { + if (!NT_STATUS_IS_OK(entry->init(auth_context, module_params, ret))) { + DEBUG(0,("load_auth_module: auth method %s did not correctly init\n", + module_name)); + } else { + DEBUG(5,("load_auth_module: auth method %s has a valid init\n", + module_name)); + good = True; + } + } else { + DEBUG(0,("load_auth_module: can't find auth method %s!\n", module_name)); + } + + SAFE_FREE(module_name); + return good; +} + +/*************************************************************************** + 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; + NTSTATUS nt_status; + + if (!text_list) { + DEBUG(2,("make_auth_context_text_list: 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++) { + if (load_auth_module(*auth_context, *text_list, &t)) { + DLIST_ADD_END(list, t, auth_methods *); + } + } + + (*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() + && !str_list_copy(talloc_tos(), &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 = str_list_make( + talloc_tos(), "guest sam winbind:ntdomain", + NULL); + break; + case SEC_SERVER: + DEBUG(5,("Making default auth method list for security=server\n")); + auth_method_list = str_list_make( + talloc_tos(), "guest sam smbserver", + NULL); + break; + case SEC_USER: + if (lp_encrypted_passwords()) { + if ((lp_server_role() == ROLE_DOMAIN_PDC) || (lp_server_role() == ROLE_DOMAIN_BDC)) { + DEBUG(5,("Making default auth method list for DC, security=user, encrypt passwords = yes\n")); + auth_method_list = str_list_make( + talloc_tos(), + "guest sam winbind:trustdomain", + NULL); + } else { + DEBUG(5,("Making default auth method list for standalone security=user, encrypt passwords = yes\n")); + auth_method_list = str_list_make( + talloc_tos(), "guest sam", + NULL); + } + } else { + DEBUG(5,("Making default auth method list for security=user, encrypt passwords = no\n")); + auth_method_list = str_list_make( + talloc_tos(), "guest unix", NULL); + } + 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 = str_list_make( + talloc_tos(), "guest sam", NULL); + } else { + DEBUG(5,("Making default auth method list for security=share, encrypt passwords = no\n")); + auth_method_list = str_list_make( + talloc_tos(), "guest unix", NULL); + } + break; + case SEC_ADS: + DEBUG(5,("Making default auth method list for security=ADS\n")); + auth_method_list = str_list_make( + talloc_tos(), "guest sam winbind:ntdomain", + NULL); + break; + default: + DEBUG(5,("Unknown auth method!\n")); + return NT_STATUS_UNSUCCESSFUL; + } + } else { + DEBUG(5,("Using specified auth order\n")); + } + + nt_status = make_auth_context_text_list(auth_context, + auth_method_list); + + TALLOC_FREE(auth_method_list); + 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_talloc((*auth_context)->mem_ctx, chal, 8); + (*auth_context)->challenge_set_by = "fixed"; + return nt_status; +} + + diff --git a/source3/auth/auth_builtin.c b/source3/auth/auth_builtin.c new file mode 100644 index 0000000000..3741f29779 --- /dev/null +++ b/source3/auth/auth_builtin.c @@ -0,0 +1,174 @@ +/* + Unix SMB/CIFS implementation. + Generic authentication types + Copyright (C) Andrew Bartlett 2001-2002 + Copyright (C) Jelmer Vernooij 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_AUTH + +/** + * Return a guest logon for guest users (username = "") + * + * Typically used as the first module in the auth chain, this allows + * guest logons to be dealt with in one place. Non-guest 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) +{ + /* mark this as 'not for me' */ + NTSTATUS nt_status = NT_STATUS_NOT_IMPLEMENTED; + + if (!(user_info->internal_username + && *user_info->internal_username)) { + nt_status = make_server_info_guest(NULL, server_info); + } + + return nt_status; +} + +/* Guest modules initialisation */ + +static NTSTATUS auth_init_guest(struct auth_context *auth_context, const char *options, auth_methods **auth_method) +{ + if (!make_auth_methods(auth_context, auth_method)) + return NT_STATUS_NO_MEMORY; + + (*auth_method)->auth = check_guest_security; + (*auth_method)->name = "guest"; + return NT_STATUS_OK; +} + +#ifdef DEVELOPER +/** + * 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); + + if (strnequal("NT_STATUS", user, strlen("NT_STATUS"))) { + strupper_m(user); + return nt_status_string_to_code(user); + } + + strlower_m(user); + error_num = strtoul(user, NULL, 16); + + DEBUG(5,("check_name_to_ntstatus_security: Error for user %s was %lx\n", user, error_num)); + + nt_status = NT_STATUS(error_num); + + return nt_status; +} + +/** Module initialisation function */ + +static NTSTATUS auth_init_name_to_ntstatus(struct auth_context *auth_context, const char *param, auth_methods **auth_method) +{ + if (!make_auth_methods(auth_context, auth_method)) + return NT_STATUS_NO_MEMORY; + + (*auth_method)->auth = check_name_to_ntstatus_security; + (*auth_method)->name = "name_to_ntstatus"; + return NT_STATUS_OK; +} + +/** + * Return a 'fixed' challenge instead of a variable one. + * + * The idea of this function is to make packet snifs consistant + * with a fixed challenge, so as to aid debugging. + * + * This module is of no value to end-users. + * + * This module does not actually authenticate the user, but + * just pretenteds to need a specified challenge. + * This module removes *all* security from the challenge-response system + * + * @return NT_STATUS_UNSUCCESSFUL + **/ + +static NTSTATUS check_fixed_challenge_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) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +/**************************************************************************** + Get the challenge out of a password server. +****************************************************************************/ + +static DATA_BLOB auth_get_fixed_challenge(const struct auth_context *auth_context, + void **my_private_data, + TALLOC_CTX *mem_ctx) +{ + const char *challenge = "I am a teapot"; + return data_blob(challenge, 8); +} + + +/** Module initailisation function */ + +static NTSTATUS auth_init_fixed_challenge(struct auth_context *auth_context, const char *param, auth_methods **auth_method) +{ + if (!make_auth_methods(auth_context, auth_method)) + return NT_STATUS_NO_MEMORY; + + (*auth_method)->auth = check_fixed_challenge_security; + (*auth_method)->get_chal = auth_get_fixed_challenge; + (*auth_method)->name = "fixed_challenge"; + return NT_STATUS_OK; +} +#endif /* DEVELOPER */ + +NTSTATUS auth_builtin_init(void) +{ + smb_register_auth(AUTH_INTERFACE_VERSION, "guest", auth_init_guest); +#ifdef DEVELOPER + smb_register_auth(AUTH_INTERFACE_VERSION, "fixed_challenge", auth_init_fixed_challenge); + smb_register_auth(AUTH_INTERFACE_VERSION, "name_to_ntstatus", auth_init_name_to_ntstatus); +#endif + return NT_STATUS_OK; +} diff --git a/source3/auth/auth_compat.c b/source3/auth/auth_compat.c new file mode 100644 index 0000000000..ad2686c003 --- /dev/null +++ b/source3/auth/auth_compat.c @@ -0,0 +1,130 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" + +extern struct auth_context *negprot_global_auth_context; +extern bool global_encrypted_passwords_negotiated; + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_AUTH + +/**************************************************************************** + 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; + 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); + } + TALLOC_FREE(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(const char *smb_name, DATA_BLOB password_blob) +{ + + DATA_BLOB null_password = data_blob_null; + bool encrypted = (global_encrypted_passwords_negotiated && (password_blob.length == 24 || password_blob.length > 46)); + + if (encrypted) { + /* + * The password could be either NTLM or plain LM. Try NTLM first, + * but fall-through as required. + * Vista sends NTLMv2 here - we need to try the client given workgroup. + */ + if (get_session_workgroup()) { + if (NT_STATUS_IS_OK(pass_check_smb(smb_name, get_session_workgroup(), null_password, password_blob, null_password, encrypted))) { + return True; + } + if (NT_STATUS_IS_OK(pass_check_smb(smb_name, get_session_workgroup(), password_blob, null_password, null_password, encrypted))) { + return True; + } + } + + 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..c25e62ab80 --- /dev/null +++ b/source3/auth/auth_domain.c @@ -0,0 +1,477 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_AUTH + +extern bool global_machine_password_needs_changing; +static struct named_mutex *mutex; + +/** + * Connect to a remote server for (inter)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 setup_creds_as domain account to setup credentials as + * @param sec_chan a switch value to distinguish between domain + * member and interdomain authentication + * @param trust_passwd the trust password to establish the + * credentials with. + * + **/ + +static NTSTATUS connect_to_domain_password_server(struct cli_state **cli, + const char *domain, + const char *dc_name, + struct sockaddr_storage *dc_ss, + struct rpc_pipe_client **pipe_ret, + bool *retry) +{ + NTSTATUS result; + struct rpc_pipe_client *netlogon_pipe = NULL; + + *cli = NULL; + + *pipe_ret = NULL; + + /* 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 + Win2k PDC get two connections where one hasn't completed a + session setup yet it will send a TCP reset to the first + connection (tridge) */ + + /* + * With NT4.x DC's *all* authentication must be serialized to avoid + * ACCESS_DENIED errors if 2 auths are done from the same machine. JRA. + */ + + mutex = grab_named_mutex(NULL, dc_name, 10); + if (mutex == NULL) { + return NT_STATUS_NO_LOGON_SERVERS; + } + + /* Attempt connection */ + *retry = True; + result = cli_full_connection(cli, global_myname(), dc_name, dc_ss, 0, + "IPC$", "IPC", "", "", "", 0, Undefined, retry); + + if (!NT_STATUS_IS_OK(result)) { + /* map to something more useful */ + if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) { + result = NT_STATUS_NO_LOGON_SERVERS; + } + + if (*cli) { + cli_shutdown(*cli); + *cli = NULL; + } + + TALLOC_FREE(mutex); + 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>. + */ + + /* open the netlogon pipe. */ + if (lp_client_schannel()) { + /* We also setup the creds chain in the open_schannel call. */ + result = cli_rpc_pipe_open_schannel( + *cli, &ndr_table_netlogon.syntax_id, + PIPE_AUTH_LEVEL_PRIVACY, domain, &netlogon_pipe); + } else { + result = cli_rpc_pipe_open_noauth( + *cli, &ndr_table_netlogon.syntax_id, &netlogon_pipe); + } + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(0,("connect_to_domain_password_server: unable to open the domain client session to \ +machine %s. Error was : %s.\n", dc_name, nt_errstr(result))); + cli_shutdown(*cli); + *cli = NULL; + TALLOC_FREE(mutex); + return result; + } + + if (!lp_client_schannel()) { + /* We need to set up a creds chain on an unauthenticated netlogon pipe. */ + uint32_t neg_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS; + uint32 sec_chan_type = 0; + unsigned char machine_pwd[16]; + const char *account_name; + + if (!get_trust_pw_hash(domain, machine_pwd, &account_name, + &sec_chan_type)) + { + DEBUG(0, ("connect_to_domain_password_server: could not fetch " + "trust account password for domain '%s'\n", + domain)); + cli_shutdown(*cli); + *cli = NULL; + TALLOC_FREE(mutex); + return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + } + + result = rpccli_netlogon_setup_creds(netlogon_pipe, + dc_name, /* server name */ + domain, /* domain */ + global_myname(), /* client name */ + account_name, /* machine account name */ + machine_pwd, + sec_chan_type, + &neg_flags); + + if (!NT_STATUS_IS_OK(result)) { + cli_shutdown(*cli); + *cli = NULL; + TALLOC_FREE(mutex); + return result; + } + } + + if(!netlogon_pipe) { + DEBUG(0,("connect_to_domain_password_server: unable to open the domain client session to \ +machine %s. Error was : %s.\n", dc_name, cli_errstr(*cli))); + cli_shutdown(*cli); + *cli = NULL; + TALLOC_FREE(mutex); + return NT_STATUS_NO_LOGON_SERVERS; + } + + /* We exit here with the mutex *locked*. JRA */ + + *pipe_ret = netlogon_pipe; + + return NT_STATUS_OK; +} + +/*********************************************************************** + 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, + const char *dc_name, + struct sockaddr_storage *dc_ss) + +{ + struct netr_SamInfo3 *info3 = NULL; + struct cli_state *cli = NULL; + struct rpc_pipe_client *netlogon_pipe = NULL; + NTSTATUS nt_status = NT_STATUS_NO_LOGON_SERVERS; + int i; + bool retry = True; + + /* + * 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. + */ + + /* rety loop for robustness */ + + for (i = 0; !NT_STATUS_IS_OK(nt_status) && retry && (i < 3); i++) { + nt_status = connect_to_domain_password_server(&cli, + domain, + dc_name, + dc_ss, + &netlogon_pipe, + &retry); + } + + if ( !NT_STATUS_IS_OK(nt_status) ) { + DEBUG(0,("domain_client_validate: Domain password server not available.\n")); + if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) { + return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE; + } + return nt_status; + } + + /* store a successful connection */ + + saf_store( domain, cli->desthost ); + + /* + * If this call succeeds, we now have lots of info about the user + * in the info3 structure. + */ + + nt_status = rpccli_netlogon_sam_network_logon(netlogon_pipe, + mem_ctx, + user_info->logon_parameters,/* flags such as 'allow workstation logon' */ + dc_name, /* server name */ + user_info->smb_name, /* user name logging on. */ + user_info->client_domain, /* domain name */ + user_info->wksta_name, /* workstation name */ + chal, /* 8 byte challenge. */ + user_info->lm_resp, /* lanman 24 byte response */ + user_info->nt_resp, /* nt 24 byte response */ + &info3); /* info3 out */ + + /* Let go as soon as possible so we avoid any potential deadlocks + with winbind lookup up users or groups. */ + + TALLOC_FREE(mutex); + + 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, + user_info->client_domain, dc_name, + nt_errstr(nt_status))); + + /* map to something more useful */ + if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) { + nt_status = NT_STATUS_NO_LOGON_SERVERS; + } + } else { + nt_status = make_server_info_info3(mem_ctx, + user_info->smb_name, + domain, + server_info, + info3); + + if (NT_STATUS_IS_OK(nt_status)) { + (*server_info)->nss_token |= user_info->was_mapped; + + if ( ! (*server_info)->guest) { + /* if a real user check pam account restrictions */ + /* only really perfomed if "obey pam restriction" is true */ + nt_status = smb_pam_accountcheck((*server_info)->unix_name); + if ( !NT_STATUS_IS_OK(nt_status)) { + DEBUG(1, ("PAM account restriction prevents user login\n")); + cli_shutdown(cli); + TALLOC_FREE(info3); + return nt_status; + } + } + } + + netsamlogon_cache_store(user_info->smb_name, info3); + TALLOC_FREE(info3); + } + + /* 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_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; + const char *domain = lp_workgroup(); + fstring dc_name; + struct sockaddr_storage dc_ss; + + if ( lp_server_role() != ROLE_DOMAIN_MEMBER ) { + DEBUG(0,("check_ntdomain_security: Configuration error! Cannot use " + "ntdomain auth method when not a member of a domain.\n")); + return NT_STATUS_NOT_IMPLEMENTED; + } + + 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(strequal(get_global_sam_name(), user_info->domain)) { + DEBUG(3,("check_ntdomain_security: Requested domain was for this machine.\n")); + return NT_STATUS_NOT_IMPLEMENTED; + } + + /* we need our DC to send the net_sam_logon() request to */ + + if ( !get_dc_name(domain, NULL, dc_name, &dc_ss) ) { + DEBUG(5,("check_ntdomain_security: unable to locate a DC for domain %s\n", + user_info->domain)); + return NT_STATUS_NO_LOGON_SERVERS; + } + + nt_status = domain_client_validate(mem_ctx, + user_info, + domain, + (uchar *)auth_context->challenge.data, + server_info, + dc_name, + &dc_ss); + + return nt_status; +} + +/* module initialisation */ +static NTSTATUS auth_init_ntdomain(struct auth_context *auth_context, const char* param, auth_methods **auth_method) +{ + if (!make_auth_methods(auth_context, auth_method)) { + return NT_STATUS_NO_MEMORY; + } + + (*auth_method)->name = "ntdomain"; + (*auth_method)->auth = check_ntdomain_security; + return NT_STATUS_OK; +} + + +/**************************************************************************** + 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; + fstring dc_name; + struct sockaddr_storage dc_ss; + + 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 or domain name. + */ + + if( strequal(get_global_sam_name(), user_info->domain)) { + DEBUG(3,("check_trustdomain_security: Requested domain [%s] was for this machine.\n", + user_info->domain)); + return NT_STATUS_NOT_IMPLEMENTED; + } + + /* No point is bothering if this is not a trusted domain. + This return makes "map to guest = bad user" work again. + The logic is that if we know nothing about the domain, that + user is not known to us and does not exist */ + + if ( !is_trusted_domain( user_info->domain ) ) + return NT_STATUS_NOT_IMPLEMENTED; + + /* + * Get the trusted account password for the trusted domain + * No need to become_root() as secrets_init() is done at startup. + */ + + if (!pdb_get_trusteddom_pw(user_info->domain, &trust_password, + &sid, &last_change_time)) { + DEBUG(0, ("check_trustdomain_security: could not fetch trust " + "account password for domain %s\n", + user_info->domain)); + return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + } + +#ifdef DEBUG_PASSWORD + DEBUG(100, ("Trust password for domain %s is %s\n", user_info->domain, + trust_password)); +#endif + E_md4hash(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 + (time_t)lp_machine_password_timeout()) + { + global_machine_password_needs_changing = True; + } +#endif + + /* use get_dc_name() for consistency even through we know that it will be + a netbios name */ + + if ( !get_dc_name(user_info->domain, NULL, dc_name, &dc_ss) ) { + DEBUG(5,("check_trustdomain_security: unable to locate a DC for domain %s\n", + user_info->domain)); + return NT_STATUS_NO_LOGON_SERVERS; + } + + nt_status = domain_client_validate(mem_ctx, + user_info, + user_info->domain, + (uchar *)auth_context->challenge.data, + server_info, + dc_name, + &dc_ss); + + return nt_status; +} + +/* module initialisation */ +static NTSTATUS auth_init_trustdomain(struct auth_context *auth_context, const char* param, auth_methods **auth_method) +{ + if (!make_auth_methods(auth_context, auth_method)) { + return NT_STATUS_NO_MEMORY; + } + + (*auth_method)->name = "trustdomain"; + (*auth_method)->auth = check_trustdomain_security; + return NT_STATUS_OK; +} + +NTSTATUS auth_domain_init(void) +{ + smb_register_auth(AUTH_INTERFACE_VERSION, "trustdomain", auth_init_trustdomain); + smb_register_auth(AUTH_INTERFACE_VERSION, "ntdomain", auth_init_ntdomain); + return NT_STATUS_OK; +} diff --git a/source3/auth/auth_ntlmssp.c b/source3/auth/auth_ntlmssp.c new file mode 100644 index 0000000000..0d46b14f97 --- /dev/null +++ b/source3/auth/auth_ntlmssp.c @@ -0,0 +1,214 @@ +/* + Unix SMB/Netbios implementation. + Version 3.0 + handle NLTMSSP, server side + + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Andrew Bartlett 2001-2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" + +/** + * Return the challenge as determined by the authentication subsystem + * @return an 8 byte random challenge + */ + +static const uint8 *auth_ntlmssp_get_challenge(const struct ntlmssp_state *ntlmssp_state) +{ + AUTH_NTLMSSP_STATE *auth_ntlmssp_state = + (AUTH_NTLMSSP_STATE *)ntlmssp_state->auth_context; + return auth_ntlmssp_state->auth_context->get_ntlm_challenge(auth_ntlmssp_state->auth_context); +} + +/** + * Some authentication methods 'fix' the challenge, so we may not be able to set it + * + * @return If the effective challenge used by the auth subsystem may be modified + */ +static bool auth_ntlmssp_may_set_challenge(const struct ntlmssp_state *ntlmssp_state) +{ + AUTH_NTLMSSP_STATE *auth_ntlmssp_state = + (AUTH_NTLMSSP_STATE *)ntlmssp_state->auth_context; + struct auth_context *auth_context = auth_ntlmssp_state->auth_context; + + return auth_context->challenge_may_be_modified; +} + +/** + * NTLM2 authentication modifies the effective challenge, + * @param challenge The new challenge value + */ +static NTSTATUS auth_ntlmssp_set_challenge(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *challenge) +{ + AUTH_NTLMSSP_STATE *auth_ntlmssp_state = + (AUTH_NTLMSSP_STATE *)ntlmssp_state->auth_context; + struct auth_context *auth_context = auth_ntlmssp_state->auth_context; + + SMB_ASSERT(challenge->length == 8); + + auth_context->challenge = data_blob_talloc(auth_context->mem_ctx, + challenge->data, challenge->length); + + auth_context->challenge_set_by = "NTLMSSP callback (NTLM2)"; + + DEBUG(5, ("auth_context challenge set by %s\n", auth_context->challenge_set_by)); + DEBUG(5, ("challenge is: \n")); + dump_data(5, auth_context->challenge.data, auth_context->challenge.length); + return NT_STATUS_OK; +} + +/** + * Check the password on an NTLMSSP login. + * + * Return the session keys used on the connection. + */ + +static NTSTATUS auth_ntlmssp_check_password(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *user_session_key, DATA_BLOB *lm_session_key) +{ + AUTH_NTLMSSP_STATE *auth_ntlmssp_state = + (AUTH_NTLMSSP_STATE *)ntlmssp_state->auth_context; + auth_usersupplied_info *user_info = NULL; + NTSTATUS nt_status; + bool username_was_mapped; + + /* the client has given us its machine name (which we otherwise would not get on port 445). + we need to possibly reload smb.conf if smb.conf includes depend on the machine name */ + + set_remote_machine_name(auth_ntlmssp_state->ntlmssp_state->workstation, True); + + /* setup the string used by %U */ + /* sub_set_smb_name checks for weird internally */ + sub_set_smb_name(auth_ntlmssp_state->ntlmssp_state->user); + + reload_services(True); + + nt_status = make_user_info_map(&user_info, + auth_ntlmssp_state->ntlmssp_state->user, + auth_ntlmssp_state->ntlmssp_state->domain, + auth_ntlmssp_state->ntlmssp_state->workstation, + auth_ntlmssp_state->ntlmssp_state->lm_resp.data ? &auth_ntlmssp_state->ntlmssp_state->lm_resp : NULL, + auth_ntlmssp_state->ntlmssp_state->nt_resp.data ? &auth_ntlmssp_state->ntlmssp_state->nt_resp : NULL, + NULL, NULL, NULL, + True); + + user_info->logon_parameters = MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT; + + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + nt_status = auth_ntlmssp_state->auth_context->check_ntlm_password(auth_ntlmssp_state->auth_context, + user_info, &auth_ntlmssp_state->server_info); + + username_was_mapped = user_info->was_mapped; + + free_user_info(&user_info); + + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + auth_ntlmssp_state->server_info->nss_token |= username_was_mapped; + + nt_status = create_local_token(auth_ntlmssp_state->server_info); + + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(10, ("create_local_token failed: %s\n", + nt_errstr(nt_status))); + return nt_status; + } + + if (auth_ntlmssp_state->server_info->user_session_key.length) { + DEBUG(10, ("Got NT session key of length %u\n", + (unsigned int)auth_ntlmssp_state->server_info->user_session_key.length)); + *user_session_key = data_blob_talloc(auth_ntlmssp_state->mem_ctx, + auth_ntlmssp_state->server_info->user_session_key.data, + auth_ntlmssp_state->server_info->user_session_key.length); + } + if (auth_ntlmssp_state->server_info->lm_session_key.length) { + DEBUG(10, ("Got LM session key of length %u\n", + (unsigned int)auth_ntlmssp_state->server_info->lm_session_key.length)); + *lm_session_key = data_blob_talloc(auth_ntlmssp_state->mem_ctx, + auth_ntlmssp_state->server_info->lm_session_key.data, + auth_ntlmssp_state->server_info->lm_session_key.length); + } + return nt_status; +} + +NTSTATUS auth_ntlmssp_start(AUTH_NTLMSSP_STATE **auth_ntlmssp_state) +{ + NTSTATUS nt_status; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_init("AUTH NTLMSSP context"); + + *auth_ntlmssp_state = TALLOC_ZERO_P(mem_ctx, AUTH_NTLMSSP_STATE); + if (!*auth_ntlmssp_state) { + DEBUG(0,("auth_ntlmssp_start: talloc failed!\n")); + talloc_destroy(mem_ctx); + return NT_STATUS_NO_MEMORY; + } + + ZERO_STRUCTP(*auth_ntlmssp_state); + + (*auth_ntlmssp_state)->mem_ctx = mem_ctx; + + if (!NT_STATUS_IS_OK(nt_status = ntlmssp_server_start(&(*auth_ntlmssp_state)->ntlmssp_state))) { + return nt_status; + } + + if (!NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(&(*auth_ntlmssp_state)->auth_context))) { + return nt_status; + } + + (*auth_ntlmssp_state)->ntlmssp_state->auth_context = (*auth_ntlmssp_state); + (*auth_ntlmssp_state)->ntlmssp_state->get_challenge = auth_ntlmssp_get_challenge; + (*auth_ntlmssp_state)->ntlmssp_state->may_set_challenge = auth_ntlmssp_may_set_challenge; + (*auth_ntlmssp_state)->ntlmssp_state->set_challenge = auth_ntlmssp_set_challenge; + (*auth_ntlmssp_state)->ntlmssp_state->check_password = auth_ntlmssp_check_password; + (*auth_ntlmssp_state)->ntlmssp_state->server_role = (enum server_types)lp_server_role(); + + return NT_STATUS_OK; +} + +void auth_ntlmssp_end(AUTH_NTLMSSP_STATE **auth_ntlmssp_state) +{ + TALLOC_CTX *mem_ctx; + + if (*auth_ntlmssp_state == NULL) { + return; + } + + mem_ctx = (*auth_ntlmssp_state)->mem_ctx; + if ((*auth_ntlmssp_state)->ntlmssp_state) { + ntlmssp_end(&(*auth_ntlmssp_state)->ntlmssp_state); + } + if ((*auth_ntlmssp_state)->auth_context) { + ((*auth_ntlmssp_state)->auth_context->free)(&(*auth_ntlmssp_state)->auth_context); + } + if ((*auth_ntlmssp_state)->server_info) { + TALLOC_FREE((*auth_ntlmssp_state)->server_info); + } + talloc_destroy(mem_ctx); + *auth_ntlmssp_state = NULL; +} + +NTSTATUS auth_ntlmssp_update(AUTH_NTLMSSP_STATE *auth_ntlmssp_state, + const DATA_BLOB request, DATA_BLOB *reply) +{ + return ntlmssp_update(auth_ntlmssp_state->ntlmssp_state, request, reply); +} diff --git a/source3/auth/auth_sam.c b/source3/auth/auth_sam.c new file mode 100644 index 0000000000..50bf15318b --- /dev/null +++ b/source3/auth/auth_sam.c @@ -0,0 +1,450 @@ +/* + Unix SMB/CIFS implementation. + Password and authentication handling + Copyright (C) Andrew Tridgell 1992-2000 + Copyright (C) Luke Kenneth Casson Leighton 1996-2000 + Copyright (C) Andrew Bartlett 2001-2003 + Copyright (C) Gerald Carter 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_AUTH + +/**************************************************************************** + 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, + struct samu *sampass, + const auth_usersupplied_info *user_info, + DATA_BLOB *user_sess_key, + DATA_BLOB *lm_sess_key) +{ + uint32 acct_ctrl; + const uint8 *lm_pw, *nt_pw; + const char *username = pdb_get_username(sampass); + + acct_ctrl = pdb_get_acct_ctrl(sampass); + if (acct_ctrl & ACB_PWNOTREQ) { + if (lp_null_passwords()) { + DEBUG(3,("Account for user '%s' has no password and null passwords are allowed.\n", username)); + return NT_STATUS_OK; + } else { + DEBUG(3,("Account for user '%s' has no password and null passwords are NOT allowed.\n", username)); + return NT_STATUS_LOGON_FAILURE; + } + } + + lm_pw = pdb_get_lanman_passwd(sampass); + nt_pw = pdb_get_nt_passwd(sampass); + + return ntlm_password_check(mem_ctx, &auth_context->challenge, + &user_info->lm_resp, &user_info->nt_resp, + &user_info->lm_interactive_pwd, &user_info->nt_interactive_pwd, + username, + user_info->smb_name, + user_info->client_domain, + lm_pw, nt_pw, user_sess_key, lm_sess_key); +} + +/**************************************************************************** + Check if a user is allowed to logon at this time. Note this is the + servers local time, as logon hours are just specified as a weekly + bitmask. +****************************************************************************/ + +static bool logon_hours_ok(struct samu *sampass) +{ + /* In logon hours first bit is Sunday from 12AM to 1AM */ + const uint8 *hours; + struct tm *utctime; + time_t lasttime; + const char *asct; + uint8 bitmask, bitpos; + + hours = pdb_get_hours(sampass); + if (!hours) { + DEBUG(5,("logon_hours_ok: No hours restrictions for user %s\n",pdb_get_username(sampass))); + return True; + } + + lasttime = time(NULL); + utctime = gmtime(&lasttime); + if (!utctime) { + DEBUG(1, ("logon_hours_ok: failed to get gmtime. Failing logon for user %s\n", + pdb_get_username(sampass) )); + return False; + } + + /* find the corresponding byte and bit */ + bitpos = (utctime->tm_wday * 24 + utctime->tm_hour) % 168; + bitmask = 1 << (bitpos % 8); + + if (! (hours[bitpos/8] & bitmask)) { + struct tm *t = localtime(&lasttime); + if (!t) { + asct = "INVALID TIME"; + } else { + asct = asctime(t); + if (!asct) { + asct = "INVALID TIME"; + } + } + + DEBUG(1, ("logon_hours_ok: Account for user %s not allowed to " + "logon at this time (%s).\n", + pdb_get_username(sampass), asct )); + return False; + } + + asct = asctime(utctime); + DEBUG(5,("logon_hours_ok: user %s allowed to logon at this time (%s)\n", + pdb_get_username(sampass), asct ? asct : "UNKNOWN TIME" )); + + return True; +} + +/**************************************************************************** + Do a specific test for a struct samu being valid for this connection + (ie not disabled, expired and the like). +****************************************************************************/ + +static NTSTATUS sam_account_ok(TALLOC_CTX *mem_ctx, + struct samu *sampass, + const auth_usersupplied_info *user_info) +{ + uint32 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,("sam_account_ok: Account for user '%s' was disabled.\n", pdb_get_username(sampass))); + return NT_STATUS_ACCOUNT_DISABLED; + } + + /* Quit if the account was locked out. */ + if (acct_ctrl & ACB_AUTOLOCK) { + DEBUG(1,("sam_account_ok: Account for user %s was locked out.\n", pdb_get_username(sampass))); + return NT_STATUS_ACCOUNT_LOCKED_OUT; + } + + /* Quit if the account is not allowed to logon at this time. */ + if (! logon_hours_ok(sampass)) { + return NT_STATUS_INVALID_LOGON_HOURS; + } + + /* Test account expire time */ + + kickoff_time = pdb_get_kickoff_time(sampass); + if (kickoff_time != 0 && time(NULL) > kickoff_time) { + DEBUG(1,("sam_account_ok: Account for user '%s' has expired.\n", pdb_get_username(sampass))); + DEBUG(3,("sam_account_ok: Account expired at '%ld' unix time.\n", (long)kickoff_time)); + return NT_STATUS_ACCOUNT_EXPIRED; + } + + if (!(pdb_get_acct_ctrl(sampass) & ACB_PWNOEXP) && !(pdb_get_acct_ctrl(sampass) & ACB_PWNOTREQ)) { + 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" + * for a user account. */ + if (((acct_ctrl & (ACB_WSTRUST|ACB_SVRTRUST)) == 0) && (last_set_time == 0)) { + DEBUG(1,("sam_account_ok: Account for user '%s' password must change!.\n", pdb_get_username(sampass))); + return NT_STATUS_PASSWORD_MUST_CHANGE; + } + + /* check for expired password */ + if (must_change_time < time(NULL) && must_change_time != 0) { + DEBUG(1,("sam_account_ok: Account for user '%s' password expired!.\n", pdb_get_username(sampass))); + DEBUG(1,("sam_account_ok: Password expired at '%s' (%ld) unix time.\n", 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 *tok; + const char *s = workstation_list; + + const char *machine_name = talloc_asprintf(mem_ctx, "%s$", user_info->wksta_name); + if (machine_name == NULL) + return NT_STATUS_NO_MEMORY; + + while (next_token_talloc(mem_ctx, &s, &tok, ",")) { + DEBUG(10,("sam_account_ok: checking for workstation match %s and %s\n", + tok, user_info->wksta_name)); + if(strequal(tok, user_info->wksta_name)) { + invalid_ws = False; + break; + } + if (tok[0] == '+') { + DEBUG(10,("sam_account_ok: checking for workstation %s in group: %s\n", + machine_name, tok + 1)); + if (user_in_group(machine_name, tok + 1)) { + invalid_ws = False; + break; + } + } + TALLOC_FREE(tok); + } + TALLOC_FREE(tok); + + 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) { + if (!(user_info->logon_parameters & MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT)) { + 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) { + if (!(user_info->logon_parameters & MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT)) { + DEBUG(2,("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) +{ + struct samu *sampass=NULL; + bool ret; + NTSTATUS nt_status; + NTSTATUS update_login_attempts_status; + DATA_BLOB user_sess_key = data_blob_null; + DATA_BLOB lm_sess_key = data_blob_null; + bool updated_autolock = False, updated_badpw = False; + + if (!user_info || !auth_context) { + return NT_STATUS_UNSUCCESSFUL; + } + + /* the returned struct gets kept on the server_info, by means + of a steal further down */ + + if ( !(sampass = samu_new( mem_ctx )) ) { + return NT_STATUS_NO_MEMORY; + } + + /* get the account information */ + + become_root(); + ret = pdb_getsampwnam(sampass, user_info->internal_username); + unbecome_root(); + + if (ret == False) { + DEBUG(3,("check_sam_security: Couldn't find user '%s' in " + "passdb.\n", user_info->internal_username)); + TALLOC_FREE(sampass); + return NT_STATUS_NO_SUCH_USER; + } + + /* see if autolock flag needs to be updated */ + if (pdb_get_acct_ctrl(sampass) & ACB_NORMAL) + pdb_update_autolock_flag(sampass, &updated_autolock); + /* Quit if the account was locked out. */ + if (pdb_get_acct_ctrl(sampass) & ACB_AUTOLOCK) { + DEBUG(3,("check_sam_security: Account for user %s was locked out.\n", pdb_get_username(sampass))); + return NT_STATUS_ACCOUNT_LOCKED_OUT; + } + + nt_status = sam_password_ok(auth_context, mem_ctx, sampass, + user_info, &user_sess_key, &lm_sess_key); + + /* Notify passdb backend of login success/failure. If not + NT_STATUS_OK the backend doesn't like the login */ + + update_login_attempts_status = pdb_update_login_attempts(sampass, NT_STATUS_IS_OK(nt_status)); + + if (!NT_STATUS_IS_OK(nt_status)) { + if (NT_STATUS_EQUAL(nt_status,NT_STATUS_WRONG_PASSWORD) && + pdb_get_acct_ctrl(sampass) &ACB_NORMAL && + NT_STATUS_IS_OK(update_login_attempts_status)) + { + pdb_increment_bad_password_count(sampass); + updated_badpw = True; + } else { + pdb_update_bad_password_count(sampass, + &updated_badpw); + } + if (updated_autolock || updated_badpw){ + become_root(); + if(!NT_STATUS_IS_OK(pdb_update_sam_account(sampass))) + DEBUG(1, ("Failed to modify entry.\n")); + unbecome_root(); + } + data_blob_free(&user_sess_key); + data_blob_free(&lm_sess_key); + TALLOC_FREE(sampass); + return nt_status; + } + + if ((pdb_get_acct_ctrl(sampass) & ACB_NORMAL) && + (pdb_get_bad_password_count(sampass) > 0)){ + pdb_set_bad_password_count(sampass, 0, PDB_CHANGED); + pdb_set_bad_password_time(sampass, 0, PDB_CHANGED); + updated_badpw = True; + } + + if (updated_autolock || updated_badpw){ + become_root(); + if(!NT_STATUS_IS_OK(pdb_update_sam_account(sampass))) + DEBUG(1, ("Failed to modify entry.\n")); + unbecome_root(); + } + + nt_status = sam_account_ok(mem_ctx, sampass, user_info); + + if (!NT_STATUS_IS_OK(nt_status)) { + TALLOC_FREE(sampass); + data_blob_free(&user_sess_key); + data_blob_free(&lm_sess_key); + return nt_status; + } + + become_root(); + nt_status = make_server_info_sam(server_info, sampass); + unbecome_root(); + + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0,("check_sam_security: make_server_info_sam() failed with '%s'\n", nt_errstr(nt_status))); + data_blob_free(&user_sess_key); + data_blob_free(&lm_sess_key); + return nt_status; + } + + (*server_info)->user_session_key = + data_blob_talloc(*server_info, user_sess_key.data, + user_sess_key.length); + data_blob_free(&user_sess_key); + + (*server_info)->lm_session_key = + data_blob_talloc(*server_info, lm_sess_key.data, + lm_sess_key.length); + data_blob_free(&lm_sess_key); + + (*server_info)->nss_token |= user_info->was_mapped; + + return nt_status; +} + +/* module initialisation */ +static NTSTATUS auth_init_sam_ignoredomain(struct auth_context *auth_context, const char *param, auth_methods **auth_method) +{ + if (!make_auth_methods(auth_context, auth_method)) { + return NT_STATUS_NO_MEMORY; + } + + (*auth_method)->auth = check_sam_security; + (*auth_method)->name = "sam_ignoredomain"; + return NT_STATUS_OK; +} + + +/**************************************************************************** +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) +{ + bool is_local_name, is_my_domain; + + if (!user_info || !auth_context) { + return NT_STATUS_LOGON_FAILURE; + } + + is_local_name = is_myname(user_info->domain); + is_my_domain = strequal(user_info->domain, lp_workgroup()); + + /* check whether or not we service this domain/workgroup name */ + + switch ( lp_server_role() ) { + case ROLE_STANDALONE: + case ROLE_DOMAIN_MEMBER: + if ( !is_local_name ) { + DEBUG(6,("check_samstrict_security: %s is not one of my local names (%s)\n", + user_info->domain, (lp_server_role() == ROLE_DOMAIN_MEMBER + ? "ROLE_DOMAIN_MEMBER" : "ROLE_STANDALONE") )); + return NT_STATUS_NOT_IMPLEMENTED; + } + case ROLE_DOMAIN_PDC: + case ROLE_DOMAIN_BDC: + if ( !is_local_name && !is_my_domain ) { + DEBUG(6,("check_samstrict_security: %s is not one of my local names or domain name (DC)\n", + user_info->domain)); + return NT_STATUS_NOT_IMPLEMENTED; + } + default: /* name is ok */ + break; + } + + return check_sam_security(auth_context, my_private_data, mem_ctx, user_info, server_info); +} + +/* module initialisation */ +static NTSTATUS auth_init_sam(struct auth_context *auth_context, const char *param, auth_methods **auth_method) +{ + if (!make_auth_methods(auth_context, auth_method)) { + return NT_STATUS_NO_MEMORY; + } + + (*auth_method)->auth = check_samstrict_security; + (*auth_method)->name = "sam"; + return NT_STATUS_OK; +} + +NTSTATUS auth_sam_init(void) +{ + smb_register_auth(AUTH_INTERFACE_VERSION, "sam", auth_init_sam); + smb_register_auth(AUTH_INTERFACE_VERSION, "sam_ignoredomain", auth_init_sam_ignoredomain); + return NT_STATUS_OK; +} diff --git a/source3/auth/auth_script.c b/source3/auth/auth_script.c new file mode 100644 index 0000000000..6cbace71e8 --- /dev/null +++ b/source3/auth/auth_script.c @@ -0,0 +1,147 @@ +/* + Unix SMB/CIFS implementation. + + Call out to a shell script for an authentication check. + + Copyright (C) Jeremy Allison 2005. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" + +#undef malloc + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_AUTH + +/* Create a string containing the supplied : + * domain\n + * user\n + * ascii hex challenge\n + * ascii hex LM response\n + * ascii hex NT response\n\0 + * and execute a shell script to check this. + * Allows external programs to create users on demand. + * Script returns zero on success, non-zero on fail. + */ + +static NTSTATUS script_check_user_credentials(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) +{ + const char *script = lp_parm_const_string( GLOBAL_SECTION_SNUM, "auth_script", "script", NULL); + char *secret_str; + size_t secret_str_len; + char hex_str[49]; + int ret, i; + + if (!script) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (!user_info) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (!auth_context) { + DEBUG(3,("script_check_user_credentials: no auth_info !\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + secret_str_len = strlen(user_info->domain) + 1 + + strlen(user_info->smb_name) + 1 + + 16 + 1 + /* 8 bytes of challenge going to 16 */ + 48 + 1 + /* 24 bytes of challenge going to 48 */ + 48 + 1; + + secret_str = (char *)malloc(secret_str_len); + if (!secret_str) { + return NT_STATUS_NO_MEMORY; + } + + safe_strcpy( secret_str, user_info->domain, secret_str_len - 1); + safe_strcat( secret_str, "\n", secret_str_len - 1); + safe_strcat( secret_str, user_info->smb_name, secret_str_len - 1); + safe_strcat( secret_str, "\n", secret_str_len - 1); + + for (i = 0; i < 8; i++) { + slprintf(&hex_str[i*2], 3, "%02X", auth_context->challenge.data[i]); + } + safe_strcat( secret_str, hex_str, secret_str_len - 1); + safe_strcat( secret_str, "\n", secret_str_len - 1); + + if (user_info->lm_resp.data) { + for (i = 0; i < 24; i++) { + slprintf(&hex_str[i*2], 3, "%02X", user_info->lm_resp.data[i]); + } + safe_strcat( secret_str, hex_str, secret_str_len - 1); + } + safe_strcat( secret_str, "\n", secret_str_len - 1); + + if (user_info->nt_resp.data) { + for (i = 0; i < 24; i++) { + slprintf(&hex_str[i*2], 3, "%02X", user_info->nt_resp.data[i]); + } + safe_strcat( secret_str, hex_str, secret_str_len - 1); + } + safe_strcat( secret_str, "\n", secret_str_len - 1); + + DEBUG(10,("script_check_user_credentials: running %s with parameters:\n%s\n", + script, secret_str )); + + ret = smbrunsecret( script, secret_str); + + SAFE_FREE(secret_str); + + if (ret) { + DEBUG(1,("script_check_user_credentials: failed to authenticate %s\\%s\n", + user_info->domain, user_info->smb_name )); + /* auth failed. */ + return NT_STATUS_NO_SUCH_USER; + } + + /* Cause the auth system to keep going.... */ + return NT_STATUS_NOT_IMPLEMENTED; +} + +/* module initialisation */ +static NTSTATUS auth_init_script(struct auth_context *auth_context, const char *param, auth_methods **auth_method) +{ + if (!make_auth_methods(auth_context, auth_method)) { + return NT_STATUS_NO_MEMORY; + } + + (*auth_method)->name = "script"; + (*auth_method)->auth = script_check_user_credentials; + + if (param && *param) { + /* we load the 'fallback' module - if script isn't here, call this + module */ + auth_methods *priv; + if (!load_auth_module(auth_context, param, &priv)) { + return NT_STATUS_UNSUCCESSFUL; + } + (*auth_method)->private_data = (void *)priv; + } + return NT_STATUS_OK; +} + +NTSTATUS auth_script_init(void); +NTSTATUS auth_script_init(void) +{ + return smb_register_auth(AUTH_INTERFACE_VERSION, "script", auth_init_script); +} diff --git a/source3/auth/auth_server.c b/source3/auth/auth_server.c new file mode 100644 index 0000000000..696b42621e --- /dev/null +++ b/source3/auth/auth_server.c @@ -0,0 +1,466 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_AUTH + +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; + char *desthost = NULL; + struct sockaddr_storage dest_ss; + const char *p; + char *pserver = NULL; + bool connected_ok = False; + struct named_mutex *mutex = NULL; + + if (!(cli = cli_initialise())) + 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_talloc(mem_ctx, &p, &desthost, LIST_SEP)) { + NTSTATUS status; + + desthost = talloc_sub_basic(mem_ctx, + current_user_info.smb_name, + current_user_info.domain, + desthost); + if (!desthost) { + return NULL; + } + strupper_m(desthost); + + if(!resolve_name( desthost, &dest_ss, 0x20)) { + DEBUG(1,("server_cryptkey: Can't resolve address for %s\n",desthost)); + continue; + } + + if (ismyaddr(&dest_ss)) { + DEBUG(1,("Password server loop - disabling password server %s\n",desthost)); + continue; + } + + /* we use a mutex to prevent two connections at once - when a + Win2k PDC get two connections where one hasn't completed a + session setup yet it will send a TCP reset to the first + connection (tridge) */ + + mutex = grab_named_mutex(talloc_tos(), desthost, 10); + if (mutex == NULL) { + cli_shutdown(cli); + return NULL; + } + + status = cli_connect(cli, desthost, &dest_ss); + if (NT_STATUS_IS_OK(status)) { + DEBUG(3,("connected to password server %s\n",desthost)); + connected_ok = True; + break; + } + DEBUG(10,("server_cryptkey: failed to connect to server %s. Error %s\n", + desthost, nt_errstr(status) )); + TALLOC_FREE(mutex); + } + + 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_ss)) { + TALLOC_FREE(mutex); + DEBUG(1,("password server fails session request\n")); + cli_shutdown(cli); + return NULL; + } + + if (strequal(desthost,myhostname())) { + exit_server_cleanly("Password server loop!"); + } + + DEBUG(3,("got session\n")); + + if (!cli_negprot(cli)) { + TALLOC_FREE(mutex); + DEBUG(1,("%s rejected the negprot\n",desthost)); + cli_shutdown(cli); + return NULL; + } + + if (cli->protocol < PROTOCOL_LANMAN2 || + !(cli->sec_mode & NEGOTIATE_SECURITY_USER_LEVEL)) { + TALLOC_FREE(mutex); + DEBUG(1,("%s isn't in user level security mode\n",desthost)); + cli_shutdown(cli); + return NULL; + } + + /* Get the first session setup done quickly, to avoid silly + Win2k bugs. (The next connection to the server will kill + this one... + */ + + if (!NT_STATUS_IS_OK(cli_session_setup(cli, "", "", 0, "", 0, + ""))) { + TALLOC_FREE(mutex); + DEBUG(0,("%s rejected the initial session setup (%s)\n", + desthost, cli_errstr(cli))); + cli_shutdown(cli); + return NULL; + } + + TALLOC_FREE(mutex); + + DEBUG(3,("password server OK\n")); + + return cli; +} + +struct server_security_state { + struct cli_state *cli; +}; + +/**************************************************************************** + Send a 'keepalive' packet down the cli pipe. +****************************************************************************/ + +static bool send_server_keepalive(const struct timeval *now, + void *private_data) +{ + struct server_security_state *state = talloc_get_type_abort( + private_data, struct server_security_state); + + if (!state->cli || !state->cli->initialised) { + return False; + } + + if (send_keepalive(state->cli->fd)) { + return True; + } + + DEBUG( 2, ( "send_server_keepalive: password server keepalive " + "failed.\n")); + cli_shutdown(state->cli); + state->cli = NULL; + return False; +} + +static int destroy_server_security(struct server_security_state *state) +{ + if (state->cli) { + cli_shutdown(state->cli); + } + return 0; +} + +static struct server_security_state *make_server_security_state(struct cli_state *cli) +{ + struct server_security_state *result; + + if (!(result = talloc(NULL, struct server_security_state))) { + DEBUG(0, ("talloc failed\n")); + cli_shutdown(cli); + return NULL; + } + + result->cli = cli; + talloc_set_destructor(result, destroy_server_security); + + if (lp_keepalive() != 0) { + struct timeval interval; + interval.tv_sec = lp_keepalive(); + interval.tv_usec = 0; + + if (event_add_idle(smbd_event_context(), result, interval, + "server_security_keepalive", + send_server_keepalive, + result) == NULL) { + DEBUG(0, ("event_add_idle failed\n")); + TALLOC_FREE(result); + return NULL; + } + } + + return result; +} + +/**************************************************************************** + 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 & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 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 *)make_server_security_state(cli); + return data_blob_null; + } 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; + } + + if (!(*my_private_data = (void *)make_server_security_state(cli))) { + return data_blob(NULL,0); + } + + /* 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; + } +} + + +/**************************************************************************** + 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 server_security_state *state = talloc_get_type_abort( + my_private_data, struct server_security_state); + struct cli_state *cli; + static bool tested_password_server = False; + static bool bad_password_server = False; + NTSTATUS nt_status = NT_STATUS_NOT_IMPLEMENTED; + bool locally_made_cli = False; + + cli = state->cli; + + 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 initialised)\n")); + return NT_STATUS_LOGON_FAILURE; + } + + if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 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; + } + } + + /* + * 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 aren'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())) { + unsigned char badpass[24]; + char *baduser = NULL; + + 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)); + } + + baduser = talloc_asprintf(mem_ctx, + "%s%s", + INVALID_USER_PREFIX, + global_myname()); + if (!baduser) { + return NT_STATUS_NO_MEMORY; + } + + if (NT_STATUS_IS_OK(cli_session_setup(cli, baduser, + (char *)badpass, + sizeof(badpass), + (char *)badpass, + sizeof(badpass), + user_info->domain))) { + + /* + * 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 */ + nt_status = cli_session_setup( + cli, user_info->smb_name, + (char *)user_info->plaintext_password.data, + user_info->plaintext_password.length, + NULL, 0, user_info->domain); + + } else { + nt_status = cli_session_setup( + cli, user_info->smb_name, + (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); + } + + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(1,("password server %s rejected the password: %s\n", + cli->desthost, nt_errstr(nt_status))); + } + + /* 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)) { + fstring real_username; + struct passwd *pass; + + if ( (pass = smb_getpwnam( NULL, user_info->internal_username, + real_username, True )) != NULL ) + { + /* if a real user check pam account restrictions */ + /* only really perfomed if "obey pam restriction" is true */ + nt_status = smb_pam_accountcheck(pass->pw_name); + if ( !NT_STATUS_IS_OK(nt_status)) { + DEBUG(1, ("PAM account restriction prevents user login\n")); + } else { + + nt_status = make_server_info_pw(server_info, pass->pw_name, pass); + } + TALLOC_FREE(pass); + } + else + { + nt_status = NT_STATUS_NO_SUCH_USER; + } + } + + if (locally_made_cli) { + cli_shutdown(cli); + } + + return(nt_status); +} + +static NTSTATUS auth_init_smbserver(struct auth_context *auth_context, const char* param, auth_methods **auth_method) +{ + if (!make_auth_methods(auth_context, auth_method)) { + return NT_STATUS_NO_MEMORY; + } + (*auth_method)->name = "smbserver"; + (*auth_method)->auth = check_smbserver_security; + (*auth_method)->get_chal = auth_get_challenge_server; + return NT_STATUS_OK; +} + +NTSTATUS auth_server_init(void) +{ + return smb_register_auth(AUTH_INTERFACE_VERSION, "smbserver", auth_init_smbserver); +} diff --git a/source3/auth/auth_unix.c b/source3/auth/auth_unix.c new file mode 100644 index 0000000000..58c765226d --- /dev/null +++ b/source3/auth/auth_unix.c @@ -0,0 +1,145 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_AUTH + +/** + * 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(const char *user, const char *password) +{ + struct samu *sampass; + bool ret; + + if ( !(sampass = samu_new( NULL )) ) { + return False; + } + + become_root(); + ret = pdb_getsampwnam(sampass, user); + unbecome_root(); + + if(ret == False) { + DEBUG(0,("pdb_getsampwnam returned NULL\n")); + TALLOC_FREE(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_CHANGED)) { + TALLOC_FREE(sampass); + return False; + } + + if (!pdb_set_plaintext_passwd (sampass, password)) { + TALLOC_FREE(sampass); + return False; + } + + /* Now write it into the file. */ + become_root(); + + ret = NT_STATUS_IS_OK(pdb_update_sam_account (sampass)); + + unbecome_root(); + + if (ret) { + DEBUG(3,("pdb_update_sam_account returned %d\n",ret)); + } + + TALLOC_FREE(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_alloc(talloc_tos(), user_info->internal_username); + + + /** @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, + (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) { + /* if a real user check pam account restrictions */ + /* only really perfomed if "obey pam restriction" is true */ + nt_status = smb_pam_accountcheck(pass->pw_name); + if ( !NT_STATUS_IS_OK(nt_status)) { + DEBUG(1, ("PAM account restriction prevents user login\n")); + } else { + make_server_info_pw(server_info, pass->pw_name, pass); + } + } else { + /* we need to do somthing more useful here */ + nt_status = NT_STATUS_NO_SUCH_USER; + } + } + + TALLOC_FREE(pass); + return nt_status; +} + +/* module initialisation */ +static NTSTATUS auth_init_unix(struct auth_context *auth_context, const char* param, auth_methods **auth_method) +{ + if (!make_auth_methods(auth_context, auth_method)) { + return NT_STATUS_NO_MEMORY; + } + + (*auth_method)->name = "unix"; + (*auth_method)->auth = check_unix_security; + return NT_STATUS_OK; +} + +NTSTATUS auth_unix_init(void) +{ + return smb_register_auth(AUTH_INTERFACE_VERSION, "unix", auth_init_unix); +} diff --git a/source3/auth/auth_util.c b/source3/auth/auth_util.c new file mode 100644 index 0000000000..9220df01c0 --- /dev/null +++ b/source3/auth/auth_util.c @@ -0,0 +1,2166 @@ +/* + Unix SMB/CIFS implementation. + Authentication utility functions + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Andrew Bartlett 2001 + Copyright (C) Jeremy Allison 2000-2001 + Copyright (C) Rafal Szczesniak 2002 + Copyright (C) Volker Lendecke 2006 + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_AUTH + +/**************************************************************************** + Ensure primary group SID is always at position 0 in a + auth_serversupplied_info struct. +****************************************************************************/ + +static void sort_sid_array_for_smbd(auth_serversupplied_info *result, + const DOM_SID *pgroup_sid) +{ + unsigned int i; + + if (!result->sids) { + return; + } + + if (sid_compare(&result->sids[0], pgroup_sid)==0) { + return; + } + + for (i = 1; i < result->num_sids; i++) { + if (sid_compare(pgroup_sid, + &result->sids[i]) == 0) { + sid_copy(&result->sids[i], &result->sids[0]); + sid_copy(&result->sids[0], pgroup_sid); + return; + } + } +} + +/**************************************************************************** + Create a UNIX user on demand. +****************************************************************************/ + +static int smb_create_user(const char *domain, const char *unix_username, const char *homedir) +{ + TALLOC_CTX *ctx = talloc_tos(); + char *add_script; + int ret; + + add_script = talloc_strdup(ctx, lp_adduser_script()); + if (!add_script || !*add_script) { + return -1; + } + add_script = talloc_all_string_sub(ctx, + add_script, + "%u", + unix_username); + if (!add_script) { + return -1; + } + if (domain) { + add_script = talloc_all_string_sub(ctx, + add_script, + "%D", + domain); + if (!add_script) { + return -1; + } + } + if (homedir) { + add_script = talloc_all_string_sub(ctx, + add_script, + "%H", + homedir); + if (!add_script) { + return -1; + } + } + ret = smbrun(add_script,NULL); + flush_pwnam_cache(); + DEBUG(ret ? 0 : 3, + ("smb_create_user: Running the command `%s' gave %d\n", + add_script,ret)); + return ret; +} + +/**************************************************************************** + Create an auth_usersupplied_data structure +****************************************************************************/ + +static NTSTATUS 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 *lm_interactive_pwd, DATA_BLOB *nt_interactive_pwd, + DATA_BLOB *plaintext, + bool encrypted) +{ + + DEBUG(5,("attempting to make a user_info for %s (%s)\n", internal_username, smb_name)); + + *user_info = SMB_MALLOC_P(auth_usersupplied_info); + if (*user_info == NULL) { + DEBUG(0,("malloc failed for user_info (size %lu)\n", (unsigned long)sizeof(*user_info))); + return NT_STATUS_NO_MEMORY; + } + + ZERO_STRUCTP(*user_info); + + DEBUG(5,("making strings for %s's user_info struct\n", internal_username)); + + (*user_info)->smb_name = SMB_STRDUP(smb_name); + if ((*user_info)->smb_name == NULL) { + free_user_info(user_info); + return NT_STATUS_NO_MEMORY; + } + + (*user_info)->internal_username = SMB_STRDUP(internal_username); + if ((*user_info)->internal_username == NULL) { + free_user_info(user_info); + return NT_STATUS_NO_MEMORY; + } + + (*user_info)->domain = SMB_STRDUP(domain); + if ((*user_info)->domain == NULL) { + free_user_info(user_info); + return NT_STATUS_NO_MEMORY; + } + + (*user_info)->client_domain = SMB_STRDUP(client_domain); + if ((*user_info)->client_domain == NULL) { + free_user_info(user_info); + return NT_STATUS_NO_MEMORY; + } + + (*user_info)->wksta_name = SMB_STRDUP(wksta_name); + if ((*user_info)->wksta_name == NULL) { + free_user_info(user_info); + return NT_STATUS_NO_MEMORY; + } + + DEBUG(5,("making blobs for %s's user_info struct\n", internal_username)); + + if (lm_pwd) + (*user_info)->lm_resp = data_blob(lm_pwd->data, lm_pwd->length); + if (nt_pwd) + (*user_info)->nt_resp = data_blob(nt_pwd->data, nt_pwd->length); + if (lm_interactive_pwd) + (*user_info)->lm_interactive_pwd = data_blob(lm_interactive_pwd->data, lm_interactive_pwd->length); + if (nt_interactive_pwd) + (*user_info)->nt_interactive_pwd = data_blob(nt_interactive_pwd->data, nt_interactive_pwd->length); + + if (plaintext) + (*user_info)->plaintext_password = data_blob(plaintext->data, plaintext->length); + + (*user_info)->encrypted = encrypted; + + (*user_info)->logon_parameters = 0; + + DEBUG(10,("made an %sencrypted user_info for %s (%s)\n", encrypted ? "":"un" , internal_username, smb_name)); + + return NT_STATUS_OK; +} + +/**************************************************************************** + Create an auth_usersupplied_data structure after appropriate mapping. +****************************************************************************/ + +NTSTATUS make_user_info_map(auth_usersupplied_info **user_info, + const char *smb_name, + const char *client_domain, + const char *wksta_name, + DATA_BLOB *lm_pwd, DATA_BLOB *nt_pwd, + DATA_BLOB *lm_interactive_pwd, DATA_BLOB *nt_interactive_pwd, + DATA_BLOB *plaintext, + bool encrypted) +{ + const char *domain; + NTSTATUS result; + bool was_mapped; + fstring internal_username; + fstrcpy(internal_username, smb_name); + was_mapped = map_username(internal_username); + + DEBUG(5, ("make_user_info_map: Mapping user [%s]\\[%s] from workstation [%s]\n", + client_domain, smb_name, wksta_name)); + + /* don't allow "" as a domain, fixes a Win9X bug + where it doens't supply a domain for logon script + 'net use' commands. */ + + if ( *client_domain ) + domain = client_domain; + else + domain = lp_workgroup(); + + /* do what win2k does. Always map unknown domains to our own + and let the "passdb backend" handle unknown users. */ + + if ( !is_trusted_domain(domain) && !strequal(domain, get_global_sam_name()) ) + domain = my_sam_name(); + + /* we know that it is a trusted domain (and we are allowing them) or it is our domain */ + + result = make_user_info(user_info, smb_name, internal_username, + client_domain, domain, wksta_name, + lm_pwd, nt_pwd, + lm_interactive_pwd, nt_interactive_pwd, + plaintext, encrypted); + if (NT_STATUS_IS_OK(result)) { + (*user_info)->was_mapped = was_mapped; + } + return result; +} + +/**************************************************************************** + 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, + uint32 logon_parameters, + const uchar *lm_network_pwd, + int lm_pwd_len, + const uchar *nt_network_pwd, + int nt_pwd_len) +{ + bool ret; + NTSTATUS status; + DATA_BLOB lm_blob = data_blob(lm_network_pwd, lm_pwd_len); + DATA_BLOB nt_blob = data_blob(nt_network_pwd, nt_pwd_len); + + status = make_user_info_map(user_info, + smb_name, client_domain, + wksta_name, + lm_pwd_len ? &lm_blob : NULL, + nt_pwd_len ? &nt_blob : NULL, + NULL, NULL, NULL, + True); + + if (NT_STATUS_IS_OK(status)) { + (*user_info)->logon_parameters = logon_parameters; + } + ret = NT_STATUS_IS_OK(status) ? True : False; + + 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, + uint32 logon_parameters, + const uchar chal[8], + const uchar lm_interactive_pwd[16], + const uchar nt_interactive_pwd[16], + const uchar *dc_sess_key) +{ + unsigned char lm_pwd[16]; + unsigned char nt_pwd[16]; + unsigned char local_lm_response[24]; + unsigned char local_nt_response[24]; + unsigned char key[16]; + + 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, 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 + + if (lm_interactive_pwd) + SamOEMhash(lm_pwd, key, sizeof(lm_pwd)); + + if (nt_interactive_pwd) + SamOEMhash(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 + + if (lm_interactive_pwd) + SMBOWFencrypt(lm_pwd, chal, + local_lm_response); + + if (nt_interactive_pwd) + SMBOWFencrypt(nt_pwd, chal, + local_nt_response); + + /* Password info paranoia */ + ZERO_STRUCT(key); + + { + bool ret; + NTSTATUS nt_status; + DATA_BLOB local_lm_blob; + DATA_BLOB local_nt_blob; + + DATA_BLOB lm_interactive_blob; + DATA_BLOB nt_interactive_blob; + + if (lm_interactive_pwd) { + local_lm_blob = data_blob(local_lm_response, + sizeof(local_lm_response)); + lm_interactive_blob = data_blob(lm_pwd, + sizeof(lm_pwd)); + ZERO_STRUCT(lm_pwd); + } + + if (nt_interactive_pwd) { + local_nt_blob = data_blob(local_nt_response, + sizeof(local_nt_response)); + nt_interactive_blob = data_blob(nt_pwd, + sizeof(nt_pwd)); + ZERO_STRUCT(nt_pwd); + } + + nt_status = make_user_info_map( + user_info, + smb_name, client_domain, wksta_name, + lm_interactive_pwd ? &local_lm_blob : NULL, + nt_interactive_pwd ? &local_nt_blob : NULL, + lm_interactive_pwd ? &lm_interactive_blob : NULL, + nt_interactive_pwd ? &nt_interactive_blob : NULL, + NULL, True); + + if (NT_STATUS_IS_OK(nt_status)) { + (*user_info)->logon_parameters = logon_parameters; + } + + ret = NT_STATUS_IS_OK(nt_status) ? True : False; + data_blob_free(&local_lm_blob); + data_blob_free(&local_nt_blob); + data_blob_free(&lm_interactive_blob); + data_blob_free(&nt_interactive_blob); + return ret; + } +} + + +/**************************************************************************** + 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; + NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; + + /* + * 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", + (int)plaintext_password.length)); + dump_data(100, plaintext_password.data, + plaintext_password.length); +#endif + + SMBencrypt( (const char *)plaintext_password.data, + (const uchar*)chal, local_lm_response); + local_lm_blob = data_blob(local_lm_response, 24); + + /* We can't do an NT hash here, as the password needs to be + case insensitive */ + local_nt_blob = data_blob_null; + + } else { + local_lm_blob = data_blob_null; + local_nt_blob = data_blob_null; + } + + ret = make_user_info_map( + user_info, smb_name, client_domain, + get_remote_machine_name(), + local_lm_blob.data ? &local_lm_blob : NULL, + local_nt_blob.data ? &local_nt_blob : NULL, + NULL, NULL, + plaintext_password.data ? &plaintext_password : NULL, + False); + + data_blob_free(&local_lm_blob); + return NT_STATUS_IS_OK(ret) ? True : False; +} + +/**************************************************************************** + Create an auth_usersupplied_data structure +****************************************************************************/ + +NTSTATUS 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) +{ + return make_user_info_map(user_info, smb_name, + client_domain, + get_remote_machine_name(), + lm_resp.data ? &lm_resp : NULL, + nt_resp.data ? &nt_resp : NULL, + NULL, NULL, NULL, + True); +} + +/**************************************************************************** + Create a guest user_info blob, for anonymous authenticaion. +****************************************************************************/ + +bool make_user_info_guest(auth_usersupplied_info **user_info) +{ + NTSTATUS nt_status; + + nt_status = make_user_info(user_info, + "","", + "","", + "", + NULL, NULL, + NULL, NULL, + NULL, + True); + + return NT_STATUS_IS_OK(nt_status) ? True : False; +} + +static int server_info_dtor(auth_serversupplied_info *server_info) +{ + TALLOC_FREE(server_info->sam_account); + ZERO_STRUCTP(server_info); + return 0; +} + +/*************************************************************************** + Make a server_info struct. Free with TALLOC_FREE(). +***************************************************************************/ + +static auth_serversupplied_info *make_server_info(TALLOC_CTX *mem_ctx) +{ + struct auth_serversupplied_info *result; + + result = TALLOC_ZERO_P(mem_ctx, auth_serversupplied_info); + if (result == NULL) { + DEBUG(0, ("talloc failed\n")); + return NULL; + } + + talloc_set_destructor(result, server_info_dtor); + + /* Initialise the uid and gid values to something non-zero + which may save us from giving away root access if there + is a bug in allocating these fields. */ + + result->utok.uid = -1; + result->utok.gid = -1; + return result; +} + +static char *sanitize_username(TALLOC_CTX *mem_ctx, const char *username) +{ + fstring tmp; + + alpha_strcpy(tmp, username, ". _-$", sizeof(tmp)); + return talloc_strdup(mem_ctx, tmp); +} + +/*************************************************************************** + Is the incoming username our own machine account ? + If so, the connection is almost certainly from winbindd. +***************************************************************************/ + +static bool is_our_machine_account(const char *username) +{ + bool ret; + char *truncname = NULL; + size_t ulen = strlen(username); + + if (ulen == 0 || username[ulen-1] != '$') { + return false; + } + truncname = SMB_STRDUP(username); + if (!truncname) { + return false; + } + truncname[ulen-1] = '\0'; + ret = strequal(truncname, global_myname()); + SAFE_FREE(truncname); + return ret; +} + +/*************************************************************************** + Make (and fill) a user_info struct from a struct samu +***************************************************************************/ + +NTSTATUS make_server_info_sam(auth_serversupplied_info **server_info, + struct samu *sampass) +{ + struct passwd *pwd; + gid_t *gids; + auth_serversupplied_info *result; + int i; + size_t num_gids; + DOM_SID unix_group_sid; + const char *username = pdb_get_username(sampass); + NTSTATUS status; + + if ( !(result = make_server_info(NULL)) ) { + return NT_STATUS_NO_MEMORY; + } + + if ( !(pwd = getpwnam_alloc(result, username)) ) { + DEBUG(1, ("User %s in passdb, but getpwnam() fails!\n", + pdb_get_username(sampass))); + TALLOC_FREE(result); + return NT_STATUS_NO_SUCH_USER; + } + + result->sam_account = sampass; + /* Ensure thaat the sampass will be freed with the result */ + talloc_steal(result, sampass); + result->unix_name = pwd->pw_name; + /* Ensure that we keep pwd->pw_name, because we will free pwd below */ + talloc_steal(result, pwd->pw_name); + result->utok.gid = pwd->pw_gid; + result->utok.uid = pwd->pw_uid; + + TALLOC_FREE(pwd); + + result->sanitized_username = sanitize_username(result, + result->unix_name); + if (result->sanitized_username == NULL) { + TALLOC_FREE(result); + return NT_STATUS_NO_MEMORY; + } + + if (IS_DC && is_our_machine_account(username)) { + /* + * Ensure for a connection from our own + * machine account (from winbindd on a DC) + * there are no supplementary groups. + * Prevents loops in calling gid_to_sid(). + */ + result->sids = NULL; + gids = NULL; + result->num_sids = 0; + + /* + * This is a hack of monstrous proportions. + * If we know it's winbindd talking to us, + * we know we must never recurse into it, + * so turn off contacting winbindd for this + * entire process. This will get fixed when + * winbindd doesn't need to talk to smbd on + * a PDC. JRA. + */ + + (void)winbind_off(); + + DEBUG(10, ("make_server_info_sam: our machine account %s " + "setting supplementary group list empty and " + "turning off winbindd requests.\n", + username)); + } else { + status = pdb_enum_group_memberships(result, sampass, + &result->sids, &gids, + &result->num_sids); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("pdb_enum_group_memberships failed: %s\n", + nt_errstr(status))); + result->sam_account = NULL; /* Don't free on error exit. */ + TALLOC_FREE(result); + return status; + } + } + + /* Add the "Unix Group" SID for each gid to catch mapped groups + and their Unix equivalent. This is to solve the backwards + compatibility problem of 'valid users = +ntadmin' where + ntadmin has been paired with "Domain Admins" in the group + mapping table. Otherwise smb.conf would need to be changed + to 'valid user = "Domain Admins"'. --jerry */ + + num_gids = result->num_sids; + for ( i=0; i<num_gids; i++ ) { + if ( !gid_to_unix_groups_sid( gids[i], &unix_group_sid ) ) { + DEBUG(1,("make_server_info_sam: Failed to create SID " + "for gid %d!\n", gids[i])); + continue; + } + status = add_sid_to_array_unique(result, &unix_group_sid, + &result->sids, + &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { + result->sam_account = NULL; /* Don't free on error exit. */ + TALLOC_FREE(result); + return status; + } + } + + /* For now we throw away the gids and convert via sid_to_gid + * later. This needs fixing, but I'd like to get the code straight and + * simple first. */ + + TALLOC_FREE(gids); + + DEBUG(5,("make_server_info_sam: made server info for user %s -> %s\n", + pdb_get_username(sampass), result->unix_name)); + + *server_info = result; + + return NT_STATUS_OK; +} + +static NTSTATUS log_nt_token(NT_USER_TOKEN *token) +{ + TALLOC_CTX *frame = talloc_stackframe(); + char *command; + char *group_sidstr; + size_t i; + + if ((lp_log_nt_token_command() == NULL) || + (strlen(lp_log_nt_token_command()) == 0)) { + TALLOC_FREE(frame); + return NT_STATUS_OK; + } + + group_sidstr = talloc_strdup(frame, ""); + for (i=1; i<token->num_sids; i++) { + group_sidstr = talloc_asprintf( + frame, "%s %s", group_sidstr, + sid_string_talloc(frame, &token->user_sids[i])); + } + + command = talloc_string_sub( + frame, lp_log_nt_token_command(), + "%s", sid_string_talloc(frame, &token->user_sids[0])); + command = talloc_string_sub(frame, command, "%t", group_sidstr); + + if (command == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + + DEBUG(8, ("running command: [%s]\n", command)); + if (smbrun(command, NULL) != 0) { + DEBUG(0, ("Could not log NT token\n")); + TALLOC_FREE(frame); + return NT_STATUS_ACCESS_DENIED; + } + + TALLOC_FREE(frame); + return NT_STATUS_OK; +} + +/* + * Create the token to use from server_info->sam_account and + * server_info->sids (the info3/sam groups). Find the unix gids. + */ + +NTSTATUS create_local_token(auth_serversupplied_info *server_info) +{ + NTSTATUS status; + size_t i; + + /* + * If winbind is not around, we can not make much use of the SIDs the + * domain controller provided us with. Likewise if the user name was + * mapped to some local unix user. + */ + + if (((lp_server_role() == ROLE_DOMAIN_MEMBER) && !winbind_ping()) || + (server_info->nss_token)) { + status = create_token_from_username(server_info, + server_info->unix_name, + server_info->guest, + &server_info->utok.uid, + &server_info->utok.gid, + &server_info->unix_name, + &server_info->ptok); + + } else { + server_info->ptok = create_local_nt_token( + server_info, + pdb_get_user_sid(server_info->sam_account), + server_info->guest, + server_info->num_sids, server_info->sids); + status = server_info->ptok ? + NT_STATUS_OK : NT_STATUS_NO_SUCH_USER; + } + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* Convert the SIDs to gids. */ + + server_info->utok.ngroups = 0; + server_info->utok.groups = NULL; + + /* Start at index 1, where the groups start. */ + + for (i=1; i<server_info->ptok->num_sids; i++) { + gid_t gid; + DOM_SID *sid = &server_info->ptok->user_sids[i]; + + if (!sid_to_gid(sid, &gid)) { + DEBUG(10, ("Could not convert SID %s to gid, " + "ignoring it\n", sid_string_dbg(sid))); + continue; + } + add_gid_to_array_unique(server_info, gid, + &server_info->utok.groups, + &server_info->utok.ngroups); + } + + debug_nt_user_token(DBGC_AUTH, 10, server_info->ptok); + + status = log_nt_token(server_info->ptok); + return status; +} + +/* + * Create an artificial NT token given just a username. (Initially indended + * for force user) + * + * We go through lookup_name() to avoid problems we had with 'winbind use + * default domain'. + * + * We have 3 cases: + * + * unmapped unix users: Go directly to nss to find the user's group. + * + * A passdb user: The list of groups is provided by pdb_enum_group_memberships. + * + * If the user is provided by winbind, the primary gid is set to "domain + * users" of the user's domain. For an explanation why this is necessary, see + * the thread starting at + * http://lists.samba.org/archive/samba-technical/2006-January/044803.html. + */ + +NTSTATUS create_token_from_username(TALLOC_CTX *mem_ctx, const char *username, + bool is_guest, + uid_t *uid, gid_t *gid, + char **found_username, + struct nt_user_token **token) +{ + NTSTATUS result = NT_STATUS_NO_SUCH_USER; + TALLOC_CTX *tmp_ctx; + DOM_SID user_sid; + enum lsa_SidType type; + gid_t *gids; + DOM_SID *group_sids; + DOM_SID unix_group_sid; + size_t num_group_sids; + size_t num_gids; + size_t i; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(0, ("talloc_new failed\n")); + return NT_STATUS_NO_MEMORY; + } + + if (!lookup_name_smbconf(tmp_ctx, username, LOOKUP_NAME_ALL, + NULL, NULL, &user_sid, &type)) { + DEBUG(1, ("lookup_name_smbconf for %s failed\n", username)); + goto done; + } + + if (type != SID_NAME_USER) { + DEBUG(1, ("%s is a %s, not a user\n", username, + sid_type_lookup(type))); + goto done; + } + + if (!sid_to_uid(&user_sid, uid)) { + DEBUG(1, ("sid_to_uid for %s (%s) failed\n", + username, sid_string_dbg(&user_sid))); + goto done; + } + + if (sid_check_is_in_our_domain(&user_sid)) { + bool ret; + + /* This is a passdb user, so ask passdb */ + + struct samu *sam_acct = NULL; + + if ( !(sam_acct = samu_new( tmp_ctx )) ) { + result = NT_STATUS_NO_MEMORY; + goto done; + } + + become_root(); + ret = pdb_getsampwsid(sam_acct, &user_sid); + unbecome_root(); + + if (!ret) { + DEBUG(1, ("pdb_getsampwsid(%s) for user %s failed\n", + sid_string_dbg(&user_sid), username)); + DEBUGADD(1, ("Fall back to unix user %s\n", username)); + goto unix_user; + } + + result = pdb_enum_group_memberships(tmp_ctx, sam_acct, + &group_sids, &gids, + &num_group_sids); + if (!NT_STATUS_IS_OK(result)) { + DEBUG(10, ("enum_group_memberships failed for %s\n", + username)); + DEBUGADD(1, ("Fall back to unix user %s\n", username)); + goto unix_user; + } + + /* see the smb_panic() in pdb_default_enum_group_memberships */ + SMB_ASSERT(num_group_sids > 0); + + *gid = gids[0]; + + /* Ensure we're returning the found_username on the right context. */ + *found_username = talloc_strdup(mem_ctx, + pdb_get_username(sam_acct)); + + } else if (sid_check_is_in_unix_users(&user_sid)) { + + /* This is a unix user not in passdb. We need to ask nss + * directly, without consulting passdb */ + + struct passwd *pass; + + /* + * This goto target is used as a fallback for the passdb + * case. The concrete bug report is when passdb gave us an + * unmapped gid. + */ + + unix_user: + + uid_to_unix_users_sid(*uid, &user_sid); + + pass = getpwuid_alloc(tmp_ctx, *uid); + if (pass == NULL) { + DEBUG(1, ("getpwuid(%d) for user %s failed\n", + *uid, username)); + goto done; + } + + if (!getgroups_unix_user(tmp_ctx, username, pass->pw_gid, + &gids, &num_group_sids)) { + DEBUG(1, ("getgroups_unix_user for user %s failed\n", + username)); + goto done; + } + + if (num_group_sids) { + group_sids = TALLOC_ARRAY(tmp_ctx, DOM_SID, num_group_sids); + if (group_sids == NULL) { + DEBUG(1, ("TALLOC_ARRAY failed\n")); + result = NT_STATUS_NO_MEMORY; + goto done; + } + } else { + group_sids = NULL; + } + + for (i=0; i<num_group_sids; i++) { + gid_to_sid(&group_sids[i], gids[i]); + } + + /* In getgroups_unix_user we always set the primary gid */ + SMB_ASSERT(num_group_sids > 0); + + *gid = gids[0]; + + /* Ensure we're returning the found_username on the right context. */ + *found_username = talloc_strdup(mem_ctx, pass->pw_name); + } else { + + /* This user is from winbind, force the primary gid to the + * user's "domain users" group. Under certain circumstances + * (user comes from NT4), this might be a loss of + * information. But we can not rely on winbind getting the + * correct info. AD might prohibit winbind looking up that + * information. */ + + uint32 dummy; + + num_group_sids = 1; + group_sids = TALLOC_ARRAY(tmp_ctx, DOM_SID, num_group_sids); + if (group_sids == NULL) { + DEBUG(1, ("TALLOC_ARRAY failed\n")); + result = NT_STATUS_NO_MEMORY; + goto done; + } + + sid_copy(&group_sids[0], &user_sid); + sid_split_rid(&group_sids[0], &dummy); + sid_append_rid(&group_sids[0], DOMAIN_GROUP_RID_USERS); + + if (!sid_to_gid(&group_sids[0], gid)) { + DEBUG(1, ("sid_to_gid(%s) failed\n", + sid_string_dbg(&group_sids[0]))); + goto done; + } + + gids = gid; + + /* Ensure we're returning the found_username on the right context. */ + *found_username = talloc_strdup(mem_ctx, username); + } + + /* Add the "Unix Group" SID for each gid to catch mapped groups + and their Unix equivalent. This is to solve the backwards + compatibility problem of 'valid users = +ntadmin' where + ntadmin has been paired with "Domain Admins" in the group + mapping table. Otherwise smb.conf would need to be changed + to 'valid user = "Domain Admins"'. --jerry */ + + num_gids = num_group_sids; + for ( i=0; i<num_gids; i++ ) { + gid_t high, low; + + /* don't pickup anything managed by Winbind */ + + if ( lp_idmap_gid(&low, &high) && (gids[i] >= low) && (gids[i] <= high) ) + continue; + + if ( !gid_to_unix_groups_sid( gids[i], &unix_group_sid ) ) { + DEBUG(1,("create_token_from_username: Failed to create SID " + "for gid %d!\n", gids[i])); + continue; + } + result = add_sid_to_array_unique(tmp_ctx, &unix_group_sid, + &group_sids, &num_group_sids); + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + } + + /* Ensure we're creating the nt_token on the right context. */ + *token = create_local_nt_token(mem_ctx, &user_sid, + is_guest, num_group_sids, group_sids); + + if ((*token == NULL) || (*found_username == NULL)) { + result = NT_STATUS_NO_MEMORY; + goto done; + } + + result = NT_STATUS_OK; + done: + TALLOC_FREE(tmp_ctx); + return result; +} + +/*************************************************************************** + Build upon create_token_from_username: + + Expensive helper function to figure out whether a user given its name is + member of a particular group. +***************************************************************************/ + +bool user_in_group_sid(const char *username, const DOM_SID *group_sid) +{ + NTSTATUS status; + uid_t uid; + gid_t gid; + char *found_username; + struct nt_user_token *token; + bool result; + + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_new(NULL); + if (mem_ctx == NULL) { + DEBUG(0, ("talloc_new failed\n")); + return False; + } + + status = create_token_from_username(mem_ctx, username, False, + &uid, &gid, &found_username, + &token); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("could not create token for %s\n", username)); + return False; + } + + result = nt_token_check_sid(group_sid, token); + + TALLOC_FREE(mem_ctx); + return result; + +} + +bool user_in_group(const char *username, const char *groupname) +{ + TALLOC_CTX *mem_ctx; + DOM_SID group_sid; + bool ret; + + mem_ctx = talloc_new(NULL); + if (mem_ctx == NULL) { + DEBUG(0, ("talloc_new failed\n")); + return False; + } + + ret = lookup_name(mem_ctx, groupname, LOOKUP_NAME_ALL, + NULL, NULL, &group_sid, NULL); + TALLOC_FREE(mem_ctx); + + if (!ret) { + DEBUG(10, ("lookup_name for (%s) failed.\n", groupname)); + return False; + } + + return user_in_group_sid(username, &group_sid); +} + +/*************************************************************************** + Make (and fill) a server_info struct from a 'struct passwd' by conversion + to a struct samu +***************************************************************************/ + +NTSTATUS make_server_info_pw(auth_serversupplied_info **server_info, + char *unix_username, + struct passwd *pwd) +{ + NTSTATUS status; + struct samu *sampass = NULL; + gid_t *gids; + char *qualified_name = NULL; + TALLOC_CTX *mem_ctx = NULL; + DOM_SID u_sid; + enum lsa_SidType type; + auth_serversupplied_info *result; + + if ( !(sampass = samu_new( NULL )) ) { + return NT_STATUS_NO_MEMORY; + } + + status = samu_set_unix( sampass, pwd ); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + result = make_server_info(NULL); + if (result == NULL) { + TALLOC_FREE(sampass); + return NT_STATUS_NO_MEMORY; + } + + result->sam_account = sampass; + + result->unix_name = talloc_strdup(result, unix_username); + result->sanitized_username = sanitize_username(result, unix_username); + + if ((result->unix_name == NULL) + || (result->sanitized_username == NULL)) { + TALLOC_FREE(sampass); + TALLOC_FREE(result); + return NT_STATUS_NO_MEMORY; + } + + result->utok.uid = pwd->pw_uid; + result->utok.gid = pwd->pw_gid; + + status = pdb_enum_group_memberships(result, sampass, + &result->sids, &gids, + &result->num_sids); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("pdb_enum_group_memberships failed: %s\n", + nt_errstr(status))); + TALLOC_FREE(result); + return status; + } + + /* + * The SID returned in server_info->sam_account is based + * on our SAM sid even though for a pure UNIX account this should + * not be the case as it doesn't really exist in the SAM db. + * This causes lookups on "[in]valid users" to fail as they + * will lookup this name as a "Unix User" SID to check against + * the user token. Fix this by adding the "Unix User"\unix_username + * SID to the sid array. The correct fix should probably be + * changing the server_info->sam_account user SID to be a + * S-1-22 Unix SID, but this might break old configs where + * plaintext passwords were used with no SAM backend. + */ + + mem_ctx = talloc_init("make_server_info_pw_tmp"); + if (!mem_ctx) { + TALLOC_FREE(result); + return NT_STATUS_NO_MEMORY; + } + + qualified_name = talloc_asprintf(mem_ctx, "%s\\%s", + unix_users_domain_name(), + unix_username ); + if (!qualified_name) { + TALLOC_FREE(result); + TALLOC_FREE(mem_ctx); + return NT_STATUS_NO_MEMORY; + } + + if (!lookup_name(mem_ctx, qualified_name, LOOKUP_NAME_ALL, + NULL, NULL, + &u_sid, &type)) { + TALLOC_FREE(result); + TALLOC_FREE(mem_ctx); + return NT_STATUS_NO_SUCH_USER; + } + + TALLOC_FREE(mem_ctx); + + if (type != SID_NAME_USER) { + TALLOC_FREE(result); + return NT_STATUS_NO_SUCH_USER; + } + + status = add_sid_to_array_unique(result, &u_sid, + &result->sids, + &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(result); + return status; + } + + /* For now we throw away the gids and convert via sid_to_gid + * later. This needs fixing, but I'd like to get the code straight and + * simple first. */ + TALLOC_FREE(gids); + + *server_info = result; + + return NT_STATUS_OK; +} + +/*************************************************************************** + Make (and fill) a user_info struct for a guest login. + This *must* succeed for smbd to start. If there is no mapping entry for + the guest gid, then create one. +***************************************************************************/ + +static NTSTATUS make_new_server_info_guest(auth_serversupplied_info **server_info) +{ + NTSTATUS status; + struct samu *sampass = NULL; + DOM_SID guest_sid; + bool ret; + char zeros[16]; + fstring tmp; + + if ( !(sampass = samu_new( NULL )) ) { + return NT_STATUS_NO_MEMORY; + } + + sid_copy(&guest_sid, get_global_sam_sid()); + sid_append_rid(&guest_sid, DOMAIN_USER_RID_GUEST); + + become_root(); + ret = pdb_getsampwsid(sampass, &guest_sid); + unbecome_root(); + + if (!ret) { + TALLOC_FREE(sampass); + return NT_STATUS_NO_SUCH_USER; + } + + status = make_server_info_sam(server_info, sampass); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(sampass); + return status; + } + + (*server_info)->guest = True; + + status = create_local_token(*server_info); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("create_local_token failed: %s\n", + nt_errstr(status))); + return status; + } + + /* annoying, but the Guest really does have a session key, and it is + all zeros! */ + ZERO_STRUCT(zeros); + (*server_info)->user_session_key = data_blob(zeros, sizeof(zeros)); + (*server_info)->lm_session_key = data_blob(zeros, sizeof(zeros)); + + alpha_strcpy(tmp, pdb_get_username(sampass), ". _-$", sizeof(tmp)); + (*server_info)->sanitized_username = talloc_strdup(*server_info, tmp); + + return NT_STATUS_OK; +} + +/**************************************************************************** + Fake a auth_serversupplied_info just from a username +****************************************************************************/ + +NTSTATUS make_serverinfo_from_username(TALLOC_CTX *mem_ctx, + const char *username, + bool is_guest, + struct auth_serversupplied_info **presult) +{ + struct auth_serversupplied_info *result; + struct passwd *pwd; + NTSTATUS status; + + pwd = getpwnam_alloc(talloc_tos(), username); + if (pwd == NULL) { + return NT_STATUS_NO_SUCH_USER; + } + + status = make_server_info_pw(&result, pwd->pw_name, pwd); + + TALLOC_FREE(pwd); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + result->nss_token = true; + result->guest = is_guest; + + status = create_local_token(result); + + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(result); + return status; + } + + *presult = result; + return NT_STATUS_OK; +} + + +struct auth_serversupplied_info *copy_serverinfo(TALLOC_CTX *mem_ctx, + auth_serversupplied_info *src) +{ + auth_serversupplied_info *dst; + + dst = make_server_info(mem_ctx); + if (dst == NULL) { + return NULL; + } + + dst->guest = src->guest; + dst->utok.uid = src->utok.uid; + dst->utok.gid = src->utok.gid; + dst->utok.ngroups = src->utok.ngroups; + if (src->utok.ngroups != 0) { + dst->utok.groups = (gid_t *)TALLOC_MEMDUP( + dst, src->utok.groups, + sizeof(gid_t)*dst->utok.ngroups); + } else { + dst->utok.groups = NULL; + } + + if (src->ptok) { + dst->ptok = dup_nt_token(dst, src->ptok); + if (!dst->ptok) { + TALLOC_FREE(dst); + return NULL; + } + } + + dst->user_session_key = data_blob_talloc( dst, src->user_session_key.data, + src->user_session_key.length); + + dst->lm_session_key = data_blob_talloc(dst, src->lm_session_key.data, + src->lm_session_key.length); + + dst->sam_account = samu_new(NULL); + if (!dst->sam_account) { + TALLOC_FREE(dst); + return NULL; + } + + if (!pdb_copy_sam_account(dst->sam_account, src->sam_account)) { + TALLOC_FREE(dst); + return NULL; + } + + dst->pam_handle = NULL; + dst->unix_name = talloc_strdup(dst, src->unix_name); + if (!dst->unix_name) { + TALLOC_FREE(dst); + return NULL; + } + + dst->sanitized_username = talloc_strdup(dst, src->sanitized_username); + if (!dst->sanitized_username) { + TALLOC_FREE(dst); + return NULL; + } + + return dst; +} + +/* + * Set a new session key. Used in the rpc server where we have to override the + * SMB level session key with SystemLibraryDTC + */ + +bool server_info_set_session_key(struct auth_serversupplied_info *info, + DATA_BLOB session_key) +{ + TALLOC_FREE(info->user_session_key.data); + + info->user_session_key = data_blob_talloc( + info, session_key.data, session_key.length); + + return (info->user_session_key.data != NULL); +} + +static auth_serversupplied_info *guest_info = NULL; + +bool init_guest_info(void) +{ + if (guest_info != NULL) + return True; + + return NT_STATUS_IS_OK(make_new_server_info_guest(&guest_info)); +} + +NTSTATUS make_server_info_guest(TALLOC_CTX *mem_ctx, + auth_serversupplied_info **server_info) +{ + *server_info = copy_serverinfo(mem_ctx, guest_info); + return (*server_info != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY; +} + +bool copy_current_user(struct current_user *dst, struct current_user *src) +{ + gid_t *groups; + NT_USER_TOKEN *nt_token; + + groups = (gid_t *)memdup(src->ut.groups, + sizeof(gid_t) * src->ut.ngroups); + if ((src->ut.ngroups != 0) && (groups == NULL)) { + return False; + } + + nt_token = dup_nt_token(NULL, src->nt_user_token); + if (nt_token == NULL) { + SAFE_FREE(groups); + return False; + } + + dst->conn = src->conn; + dst->vuid = src->vuid; + dst->ut.uid = src->ut.uid; + dst->ut.gid = src->ut.gid; + dst->ut.ngroups = src->ut.ngroups; + dst->ut.groups = groups; + dst->nt_user_token = nt_token; + return True; +} + +/*************************************************************************** + Purely internal function for make_server_info_info3 + Fill the sam account from getpwnam +***************************************************************************/ +static NTSTATUS fill_sam_account(TALLOC_CTX *mem_ctx, + const char *domain, + const char *username, + char **found_username, + uid_t *uid, gid_t *gid, + struct samu *account, + bool *username_was_mapped) +{ + NTSTATUS nt_status; + fstring dom_user, lower_username; + fstring real_username; + struct passwd *passwd; + + fstrcpy( lower_username, username ); + strlower_m( lower_username ); + + fstr_sprintf(dom_user, "%s%c%s", domain, *lp_winbind_separator(), + lower_username); + + /* Get the passwd struct. Try to create the account is necessary. */ + + *username_was_mapped = map_username( dom_user ); + + if ( !(passwd = smb_getpwnam( NULL, dom_user, real_username, True )) ) + return NT_STATUS_NO_SUCH_USER; + + *uid = passwd->pw_uid; + *gid = passwd->pw_gid; + + /* This is pointless -- there is no suport for differing + unix and windows names. Make sure to always store the + one we actually looked up and succeeded. Have I mentioned + why I hate the 'winbind use default domain' parameter? + --jerry */ + + *found_username = talloc_strdup( mem_ctx, real_username ); + + DEBUG(5,("fill_sam_account: located username was [%s]\n", *found_username)); + + nt_status = samu_set_unix( account, passwd ); + + TALLOC_FREE(passwd); + + return nt_status; +} + +/**************************************************************************** + Wrapper to allow the getpwnam() call to strip the domain name and + try again in case a local UNIX user is already there. Also run through + the username if we fallback to the username only. + ****************************************************************************/ + +struct passwd *smb_getpwnam( TALLOC_CTX *mem_ctx, char *domuser, + fstring save_username, bool create ) +{ + struct passwd *pw = NULL; + char *p; + fstring username; + + /* we only save a copy of the username it has been mangled + by winbindd use default domain */ + + save_username[0] = '\0'; + + /* don't call map_username() here since it has to be done higher + up the stack so we don't call it mutliple times */ + + fstrcpy( username, domuser ); + + p = strchr_m( username, *lp_winbind_separator() ); + + /* code for a DOMAIN\user string */ + + if ( p ) { + fstring strip_username; + + pw = Get_Pwnam_alloc( mem_ctx, domuser ); + if ( pw ) { + /* make sure we get the case of the username correct */ + /* work around 'winbind use default domain = yes' */ + + if ( !strchr_m( pw->pw_name, *lp_winbind_separator() ) ) { + char *domain; + + /* split the domain and username into 2 strings */ + *p = '\0'; + domain = username; + + fstr_sprintf(save_username, "%s%c%s", domain, *lp_winbind_separator(), pw->pw_name); + } + else + fstrcpy( save_username, pw->pw_name ); + + /* whew -- done! */ + return pw; + } + + /* setup for lookup of just the username */ + /* remember that p and username are overlapping memory */ + + p++; + fstrcpy( strip_username, p ); + fstrcpy( username, strip_username ); + } + + /* just lookup a plain username */ + + pw = Get_Pwnam_alloc(mem_ctx, username); + + /* Create local user if requested but only if winbindd + is not running. We need to protect against cases + where winbindd is failing and then prematurely + creating users in /etc/passwd */ + + if ( !pw && create && !winbind_ping() ) { + /* Don't add a machine account. */ + if (username[strlen(username)-1] == '$') + return NULL; + + smb_create_user(NULL, username, NULL); + pw = Get_Pwnam_alloc(mem_ctx, username); + } + + /* one last check for a valid passwd struct */ + + if ( pw ) + fstrcpy( save_username, pw->pw_name ); + + return pw; +} + +/*************************************************************************** + Make a server_info struct from the info3 returned by a domain logon +***************************************************************************/ + +NTSTATUS make_server_info_info3(TALLOC_CTX *mem_ctx, + const char *sent_nt_username, + const char *domain, + auth_serversupplied_info **server_info, + struct netr_SamInfo3 *info3) +{ + char zeros[16]; + + NTSTATUS nt_status = NT_STATUS_OK; + char *found_username = NULL; + const char *nt_domain; + const char *nt_username; + struct samu *sam_account = NULL; + DOM_SID user_sid; + DOM_SID group_sid; + bool username_was_mapped; + + uid_t uid = (uid_t)-1; + gid_t gid = (gid_t)-1; + + auth_serversupplied_info *result; + + /* + Here is where we should check the list of + trusted domains, and verify that the SID + matches. + */ + + sid_copy(&user_sid, info3->base.domain_sid); + if (!sid_append_rid(&user_sid, info3->base.rid)) { + return NT_STATUS_INVALID_PARAMETER; + } + + sid_copy(&group_sid, info3->base.domain_sid); + if (!sid_append_rid(&group_sid, info3->base.primary_gid)) { + return NT_STATUS_INVALID_PARAMETER; + } + + nt_username = talloc_strdup(mem_ctx, info3->base.account_name.string); + if (!nt_username) { + /* If the server didn't give us one, just use the one we sent + * them */ + nt_username = sent_nt_username; + } + + nt_domain = talloc_strdup(mem_ctx, info3->base.domain.string); + if (!nt_domain) { + /* If the server didn't give us one, just use the one we sent + * them */ + nt_domain = domain; + } + + /* try to fill the SAM account.. If getpwnam() fails, then try the + add user script (2.2.x behavior). + + We use the _unmapped_ username here in an attempt to provide + consistent username mapping behavior between kerberos and NTLM[SSP] + authentication in domain mode security. I.E. Username mapping + should be applied to the fully qualified username + (e.g. DOMAIN\user) and not just the login name. Yes this means we + called map_username() unnecessarily in make_user_info_map() but + that is how the current code is designed. Making the change here + is the least disruptive place. -- jerry */ + + if ( !(sam_account = samu_new( NULL )) ) { + return NT_STATUS_NO_MEMORY; + } + + /* this call will try to create the user if necessary */ + + nt_status = fill_sam_account(mem_ctx, nt_domain, sent_nt_username, + &found_username, &uid, &gid, sam_account, + &username_was_mapped); + + + /* if we still don't have a valid unix account check for + 'map to guest = bad uid' */ + + if (!NT_STATUS_IS_OK(nt_status)) { + TALLOC_FREE( sam_account ); + if ( lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_UID ) { + make_server_info_guest(NULL, server_info); + return NT_STATUS_OK; + } + return nt_status; + } + + if (!pdb_set_nt_username(sam_account, nt_username, PDB_CHANGED)) { + TALLOC_FREE(sam_account); + return NT_STATUS_NO_MEMORY; + } + + if (!pdb_set_username(sam_account, nt_username, PDB_CHANGED)) { + TALLOC_FREE(sam_account); + return NT_STATUS_NO_MEMORY; + } + + if (!pdb_set_domain(sam_account, nt_domain, PDB_CHANGED)) { + TALLOC_FREE(sam_account); + return NT_STATUS_NO_MEMORY; + } + + if (!pdb_set_user_sid(sam_account, &user_sid, PDB_CHANGED)) { + TALLOC_FREE(sam_account); + return NT_STATUS_UNSUCCESSFUL; + } + + if (!pdb_set_group_sid(sam_account, &group_sid, PDB_CHANGED)) { + TALLOC_FREE(sam_account); + return NT_STATUS_UNSUCCESSFUL; + } + + if (!pdb_set_fullname(sam_account, + info3->base.full_name.string, + PDB_CHANGED)) { + TALLOC_FREE(sam_account); + return NT_STATUS_NO_MEMORY; + } + + if (!pdb_set_logon_script(sam_account, + info3->base.logon_script.string, + PDB_CHANGED)) { + TALLOC_FREE(sam_account); + return NT_STATUS_NO_MEMORY; + } + + if (!pdb_set_profile_path(sam_account, + info3->base.profile_path.string, + PDB_CHANGED)) { + TALLOC_FREE(sam_account); + return NT_STATUS_NO_MEMORY; + } + + if (!pdb_set_homedir(sam_account, + info3->base.home_directory.string, + PDB_CHANGED)) { + TALLOC_FREE(sam_account); + return NT_STATUS_NO_MEMORY; + } + + if (!pdb_set_dir_drive(sam_account, + info3->base.home_drive.string, + PDB_CHANGED)) { + TALLOC_FREE(sam_account); + return NT_STATUS_NO_MEMORY; + } + + if (!pdb_set_acct_ctrl(sam_account, info3->base.acct_flags, PDB_CHANGED)) { + TALLOC_FREE(sam_account); + return NT_STATUS_NO_MEMORY; + } + + if (!pdb_set_pass_last_set_time( + sam_account, + nt_time_to_unix(info3->base.last_password_change), + PDB_CHANGED)) { + TALLOC_FREE(sam_account); + return NT_STATUS_NO_MEMORY; + } + + if (!pdb_set_pass_can_change_time( + sam_account, + nt_time_to_unix(info3->base.allow_password_change), + PDB_CHANGED)) { + TALLOC_FREE(sam_account); + return NT_STATUS_NO_MEMORY; + } + + if (!pdb_set_pass_must_change_time( + sam_account, + nt_time_to_unix(info3->base.force_password_change), + PDB_CHANGED)) { + TALLOC_FREE(sam_account); + return NT_STATUS_NO_MEMORY; + } + + result = make_server_info(NULL); + if (result == NULL) { + DEBUG(4, ("make_server_info failed!\n")); + TALLOC_FREE(sam_account); + return NT_STATUS_NO_MEMORY; + } + + /* save this here to _net_sam_logon() doesn't fail (it assumes a + valid struct samu) */ + + result->sam_account = sam_account; + result->unix_name = talloc_strdup(result, found_username); + + result->sanitized_username = sanitize_username(result, + result->unix_name); + if (result->sanitized_username == NULL) { + TALLOC_FREE(result); + return NT_STATUS_NO_MEMORY; + } + + /* Fill in the unix info we found on the way */ + + result->utok.uid = uid; + result->utok.gid = gid; + + /* Create a 'combined' list of all SIDs we might want in the SD */ + + result->num_sids = 0; + result->sids = NULL; + + nt_status = sid_array_from_info3(result, info3, + &result->sids, + &result->num_sids, + false, false); + if (!NT_STATUS_IS_OK(nt_status)) { + TALLOC_FREE(result); + return nt_status; + } + + /* Ensure the primary group sid is at position 0. */ + sort_sid_array_for_smbd(result, &group_sid); + + result->login_server = talloc_strdup(result, + info3->base.logon_server.string); + + /* ensure we are never given NULL session keys */ + + ZERO_STRUCT(zeros); + + if (memcmp(info3->base.key.key, zeros, sizeof(zeros)) == 0) { + result->user_session_key = data_blob_null; + } else { + result->user_session_key = data_blob_talloc( + result, info3->base.key.key, + sizeof(info3->base.key.key)); + } + + if (memcmp(info3->base.LMSessKey.key, zeros, 8) == 0) { + result->lm_session_key = data_blob_null; + } else { + result->lm_session_key = data_blob_talloc( + result, info3->base.LMSessKey.key, + sizeof(info3->base.LMSessKey.key)); + } + + result->nss_token |= username_was_mapped; + + *server_info = result; + + return NT_STATUS_OK; +} + +/***************************************************************************** + Make a server_info struct from the wbcAuthUserInfo returned by a domain logon +******************************************************************************/ + +NTSTATUS make_server_info_wbcAuthUserInfo(TALLOC_CTX *mem_ctx, + const char *sent_nt_username, + const char *domain, + const struct wbcAuthUserInfo *info, + auth_serversupplied_info **server_info) +{ + char zeros[16]; + + NTSTATUS nt_status = NT_STATUS_OK; + char *found_username = NULL; + const char *nt_domain; + const char *nt_username; + struct samu *sam_account = NULL; + DOM_SID user_sid; + DOM_SID group_sid; + bool username_was_mapped; + uint32_t i; + + uid_t uid = (uid_t)-1; + gid_t gid = (gid_t)-1; + + auth_serversupplied_info *result; + + result = make_server_info(NULL); + if (result == NULL) { + DEBUG(4, ("make_server_info failed!\n")); + return NT_STATUS_NO_MEMORY; + } + + /* + Here is where we should check the list of + trusted domains, and verify that the SID + matches. + */ + + memcpy(&user_sid, &info->sids[0].sid, sizeof(user_sid)); + memcpy(&group_sid, &info->sids[1].sid, sizeof(group_sid)); + + if (info->account_name) { + nt_username = talloc_strdup(result, info->account_name); + } else { + /* If the server didn't give us one, just use the one we sent + * them */ + nt_username = talloc_strdup(result, sent_nt_username); + } + if (!nt_username) { + TALLOC_FREE(result); + return NT_STATUS_NO_MEMORY; + } + + if (info->domain_name) { + nt_domain = talloc_strdup(result, info->domain_name); + } else { + /* If the server didn't give us one, just use the one we sent + * them */ + nt_domain = talloc_strdup(result, domain); + } + if (!nt_domain) { + TALLOC_FREE(result); + return NT_STATUS_NO_MEMORY; + } + + /* try to fill the SAM account.. If getpwnam() fails, then try the + add user script (2.2.x behavior). + + We use the _unmapped_ username here in an attempt to provide + consistent username mapping behavior between kerberos and NTLM[SSP] + authentication in domain mode security. I.E. Username mapping + should be applied to the fully qualified username + (e.g. DOMAIN\user) and not just the login name. Yes this means we + called map_username() unnecessarily in make_user_info_map() but + that is how the current code is designed. Making the change here + is the least disruptive place. -- jerry */ + + if ( !(sam_account = samu_new( result )) ) { + TALLOC_FREE(result); + return NT_STATUS_NO_MEMORY; + } + + /* this call will try to create the user if necessary */ + + nt_status = fill_sam_account(result, nt_domain, sent_nt_username, + &found_username, &uid, &gid, sam_account, + &username_was_mapped); + + /* if we still don't have a valid unix account check for + 'map to guest = bad uid' */ + + if (!NT_STATUS_IS_OK(nt_status)) { + TALLOC_FREE( result ); + if ( lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_UID ) { + make_server_info_guest(NULL, server_info); + return NT_STATUS_OK; + } + return nt_status; + } + + if (!pdb_set_nt_username(sam_account, nt_username, PDB_CHANGED)) { + TALLOC_FREE(result); + return NT_STATUS_NO_MEMORY; + } + + if (!pdb_set_username(sam_account, nt_username, PDB_CHANGED)) { + TALLOC_FREE(result); + return NT_STATUS_NO_MEMORY; + } + + if (!pdb_set_domain(sam_account, nt_domain, PDB_CHANGED)) { + TALLOC_FREE(result); + return NT_STATUS_NO_MEMORY; + } + + if (!pdb_set_user_sid(sam_account, &user_sid, PDB_CHANGED)) { + TALLOC_FREE(result); + return NT_STATUS_UNSUCCESSFUL; + } + + if (!pdb_set_group_sid(sam_account, &group_sid, PDB_CHANGED)) { + TALLOC_FREE(result); + return NT_STATUS_UNSUCCESSFUL; + } + + if (!pdb_set_fullname(sam_account, info->full_name, PDB_CHANGED)) { + TALLOC_FREE(result); + return NT_STATUS_NO_MEMORY; + } + + if (!pdb_set_logon_script(sam_account, info->logon_script, PDB_CHANGED)) { + TALLOC_FREE(result); + return NT_STATUS_NO_MEMORY; + } + + if (!pdb_set_profile_path(sam_account, info->profile_path, PDB_CHANGED)) { + TALLOC_FREE(result); + return NT_STATUS_NO_MEMORY; + } + + if (!pdb_set_homedir(sam_account, info->home_directory, PDB_CHANGED)) { + TALLOC_FREE(result); + return NT_STATUS_NO_MEMORY; + } + + if (!pdb_set_dir_drive(sam_account, info->home_drive, PDB_CHANGED)) { + TALLOC_FREE(result); + return NT_STATUS_NO_MEMORY; + } + + if (!pdb_set_acct_ctrl(sam_account, info->acct_flags, PDB_CHANGED)) { + TALLOC_FREE(result); + return NT_STATUS_NO_MEMORY; + } + + if (!pdb_set_pass_last_set_time( + sam_account, + nt_time_to_unix(info->pass_last_set_time), + PDB_CHANGED)) { + TALLOC_FREE(result); + return NT_STATUS_NO_MEMORY; + } + + if (!pdb_set_pass_can_change_time( + sam_account, + nt_time_to_unix(info->pass_can_change_time), + PDB_CHANGED)) { + TALLOC_FREE(result); + return NT_STATUS_NO_MEMORY; + } + + if (!pdb_set_pass_must_change_time( + sam_account, + nt_time_to_unix(info->pass_must_change_time), + PDB_CHANGED)) { + TALLOC_FREE(result); + return NT_STATUS_NO_MEMORY; + } + + /* save this here to _net_sam_logon() doesn't fail (it assumes a + valid struct samu) */ + + result->sam_account = sam_account; + result->unix_name = talloc_strdup(result, found_username); + + result->sanitized_username = sanitize_username(result, + result->unix_name); + result->login_server = talloc_strdup(result, info->logon_server); + + if ((result->unix_name == NULL) + || (result->sanitized_username == NULL) + || (result->login_server == NULL)) { + TALLOC_FREE(result); + return NT_STATUS_NO_MEMORY; + } + + /* Fill in the unix info we found on the way */ + + result->utok.uid = uid; + result->utok.gid = gid; + + /* Create a 'combined' list of all SIDs we might want in the SD */ + + result->num_sids = info->num_sids - 2; + result->sids = talloc_array(result, DOM_SID, result->num_sids); + if (result->sids == NULL) { + TALLOC_FREE(result); + return NT_STATUS_NO_MEMORY; + } + + for (i=0; i < result->num_sids; i++) { + memcpy(&result->sids[i], &info->sids[i+2].sid, sizeof(result->sids[i])); + } + + /* Ensure the primary group sid is at position 0. */ + sort_sid_array_for_smbd(result, &group_sid); + + /* ensure we are never given NULL session keys */ + + ZERO_STRUCT(zeros); + + if (memcmp(info->user_session_key, zeros, sizeof(zeros)) == 0) { + result->user_session_key = data_blob_null; + } else { + result->user_session_key = data_blob_talloc( + result, info->user_session_key, + sizeof(info->user_session_key)); + } + + if (memcmp(info->lm_session_key, zeros, 8) == 0) { + result->lm_session_key = data_blob_null; + } else { + result->lm_session_key = data_blob_talloc( + result, info->lm_session_key, + sizeof(info->lm_session_key)); + } + + result->nss_token |= username_was_mapped; + + *server_info = result; + + return NT_STATUS_OK; +} + +/*************************************************************************** + 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) { + DEBUG(10,("structure was created for %s\n", + (*user_info)->smb_name)); + } + SAFE_FREE((*user_info)->smb_name); + SAFE_FREE((*user_info)->internal_username); + SAFE_FREE((*user_info)->client_domain); + SAFE_FREE((*user_info)->domain); + SAFE_FREE((*user_info)->wksta_name); + data_blob_free(&(*user_info)->lm_resp); + data_blob_free(&(*user_info)->nt_resp); + data_blob_clear_free(&(*user_info)->lm_interactive_pwd); + data_blob_clear_free(&(*user_info)->nt_interactive_pwd); + data_blob_clear_free(&(*user_info)->plaintext_password); + ZERO_STRUCT(**user_info); + } + SAFE_FREE(*user_info); +} + +/*************************************************************************** + 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_P(auth_context->mem_ctx, auth_methods); + if (!*auth_method) { + DEBUG(0,("make_auth_method: malloc failed!\n")); + return False; + } + ZERO_STRUCTP(*auth_method); + + return True; +} + +/** + * Verify whether or not given domain is trusted. + * + * @param domain_name name of the domain to be verified + * @return true if domain is one of the trusted once or + * false if otherwise + **/ + +bool is_trusted_domain(const char* dom_name) +{ + DOM_SID trustdom_sid; + bool ret; + + /* no trusted domains for a standalone server */ + + if ( lp_server_role() == ROLE_STANDALONE ) + return False; + + /* if we are a DC, then check for a direct trust relationships */ + + if ( IS_DC ) { + become_root(); + DEBUG (5,("is_trusted_domain: Checking for domain trust with " + "[%s]\n", dom_name )); + ret = pdb_get_trusteddom_pw(dom_name, NULL, NULL, NULL); + unbecome_root(); + if (ret) + return True; + } + else { + wbcErr result; + + /* If winbind is around, ask it */ + + result = wb_is_trusted_domain(dom_name); + + if (result == WBC_ERR_SUCCESS) { + return True; + } + + if (result == WBC_ERR_DOMAIN_NOT_FOUND) { + /* winbind could not find the domain */ + return False; + } + + /* The only other possible result is that winbind is not up + and running. We need to update the trustdom_cache + ourselves */ + + update_trustdom_cache(); + } + + /* now the trustdom cache should be available a DC could still + * have a transitive trust so fall back to the cache of trusted + * domains (like a domain member would use */ + + if ( trustdom_cache_fetch(dom_name, &trustdom_sid) ) { + return True; + } + + return False; +} + diff --git a/source3/auth/auth_winbind.c b/source3/auth/auth_winbind.c new file mode 100644 index 0000000000..d1b00a3268 --- /dev/null +++ b/source3/auth/auth_winbind.c @@ -0,0 +1,153 @@ +/* + Unix SMB/CIFS implementation. + + Winbind authentication mechnism + + Copyright (C) Tim Potter 2000 + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_AUTH + +/* 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) +{ + NTSTATUS nt_status; + wbcErr wbc_status; + struct wbcAuthUserParams params; + struct wbcAuthUserInfo *info = NULL; + struct wbcAuthErrorInfo *err = NULL; + + if (!user_info) { + return NT_STATUS_INVALID_PARAMETER; + } + + 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)); + return NT_STATUS_INVALID_PARAMETER; + } + + if (strequal(user_info->domain, get_global_sam_name())) { + DEBUG(3,("check_winbind_security: Not using winbind, requested domain [%s] was for this SAM.\n", + user_info->domain)); + return NT_STATUS_NOT_IMPLEMENTED; + } + + /* Send off request */ + + params.account_name = user_info->smb_name; + params.domain_name = user_info->domain; + params.workstation_name = user_info->wksta_name; + + params.flags = 0; + params.parameter_control= user_info->logon_parameters; + + params.level = WBC_AUTH_USER_LEVEL_RESPONSE; + + memcpy(params.password.response.challenge, + auth_context->challenge.data, + sizeof(params.password.response.challenge)); + + params.password.response.nt_length = user_info->nt_resp.length; + params.password.response.nt_data = user_info->nt_resp.data; + params.password.response.lm_length = user_info->lm_resp.length; + params.password.response.lm_data = user_info->lm_resp.data; + + /* we are contacting the privileged pipe */ + become_root(); + wbc_status = wbcAuthenticateUserEx(¶ms, &info, &err); + unbecome_root(); + + if (!WBC_ERROR_IS_OK(wbc_status)) { + DEBUG(10,("check_winbind_security: wbcAuthenticateUserEx failed: %s\n", + wbcErrorString(wbc_status))); + } + + if (wbc_status == WBC_ERR_NO_MEMORY) { + return NT_STATUS_NO_MEMORY; + } + + if (wbc_status == WBC_ERR_WINBIND_NOT_AVAILABLE) { + struct auth_methods *auth_method = + (struct auth_methods *)my_private_data; + + if ( auth_method ) + return auth_method->auth(auth_context, auth_method->private_data, + mem_ctx, user_info, server_info); + else + /* log an error since this should not happen */ + DEBUG(0,("check_winbind_security: ERROR! my_private_data == NULL!\n")); + } + + if (wbc_status == WBC_ERR_AUTH_ERROR) { + nt_status = NT_STATUS(err->nt_status); + wbcFreeMemory(err); + return nt_status; + } + + if (!WBC_ERROR_IS_OK(wbc_status)) { + return NT_STATUS_LOGON_FAILURE; + } + + nt_status = make_server_info_wbcAuthUserInfo(mem_ctx, + user_info->smb_name, + user_info->domain, + info, server_info); + wbcFreeMemory(info); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + (*server_info)->nss_token |= user_info->was_mapped; + + return nt_status; +} + +/* module initialisation */ +static NTSTATUS auth_init_winbind(struct auth_context *auth_context, const char *param, auth_methods **auth_method) +{ + if (!make_auth_methods(auth_context, auth_method)) { + return NT_STATUS_NO_MEMORY; + } + + (*auth_method)->name = "winbind"; + (*auth_method)->auth = check_winbind_security; + + if (param && *param) { + /* we load the 'fallback' module - if winbind isn't here, call this + module */ + auth_methods *priv; + if (!load_auth_module(auth_context, param, &priv)) { + return NT_STATUS_UNSUCCESSFUL; + } + (*auth_method)->private_data = (void *)priv; + } + return NT_STATUS_OK; +} + +NTSTATUS auth_winbind_init(void) +{ + return smb_register_auth(AUTH_INTERFACE_VERSION, "winbind", auth_init_winbind); +} diff --git a/source3/auth/pampass.c b/source3/auth/pampass.c new file mode 100644 index 0000000000..9345eed27a --- /dev/null +++ b/source3/auth/pampass.c @@ -0,0 +1,886 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + * 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" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_AUTH + +#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. + *********************************************************************/ + +#if defined(HAVE_SECURITY_PAM_APPL_H) +#include <security/pam_appl.h> +#elif defined(HAVE_PAM_PAM_APPL_H) +#include <pam/pam_appl.h> +#endif + +/* + * 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) ? SMB_STRDUP(s) : NULL +#define COPY_FSTRING(s) (s[0]) ? SMB_STRDUP(s) : NULL + +/******************************************************************* + PAM error handler. + *********************************************************************/ + +static bool smb_pam_error_handler(pam_handle_t *pamh, int pam_error, const 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, + const 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 = SMB_MALLOC_ARRAY(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) +{ + fstring_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(const char *p) +{ + char *prompt; + char *reply; + struct chat_struct *list = NULL; + struct chat_struct *t; + TALLOC_CTX *frame = talloc_stackframe(); + + while (1) { + t = SMB_MALLOC_P(struct chat_struct); + if (!t) { + DEBUG(0,("make_pw_chat: malloc failed!\n")); + TALLOC_FREE(frame); + return NULL; + } + + ZERO_STRUCTP(t); + + DLIST_ADD_END(list, t, struct chat_struct*); + + if (!next_token_talloc(frame, &p, &prompt, NULL)) { + break; + } + + if (strequal(prompt,".")) { + fstrcpy(prompt,"*"); + } + + special_char_sub(prompt); + fstrcpy(t->prompt, prompt); + strlower_m(t->prompt); + trim_char(t->prompt, ' ', ' '); + + if (!next_token_talloc(frame, &p, &reply, NULL)) { + break; + } + + if (strequal(reply,".")) { + fstrcpy(reply,""); + } + + special_char_sub(reply); + fstrcpy(t->reply, reply); + strlower_m(t->reply); + trim_char(t->reply, ' ', ' '); + + } + TALLOC_FREE(frame); + 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 = SMB_MALLOC_ARRAY(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_char(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)) { + 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_FSTRING(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_char(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)) { + 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_FSTRING(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 = SMB_MALLOC_P(struct pam_conv); + struct smb_pam_userdata *udp = SMB_MALLOC_P(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; + char addr[INET6_ADDRSTRLEN]; + + *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(get_client_fd()); + if (strequal(our_rhost,"UNKNOWN")) + our_rhost = client_addr(get_client_fd(),addr,sizeof(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, const 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: Authentication 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, const 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, const char *user, const 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(const char * user, const 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..813540d9fa --- /dev/null +++ b/source3/auth/pass_check.c @@ -0,0 +1,874 @@ +/* + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* this module is for checking a username/password against a system + password database. The SMB encrypted password support is elsewhere */ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_AUTH + +/* these are kept here to keep the string_combinations function simple */ +static char *ths_user; + +static const char *get_this_user(void) +{ + if (!ths_user) { + return ""; + } + return ths_user; +} + +#if defined(WITH_PAM) || defined(OSF1_ENH_SEC) +static const char *set_this_user(const char *newuser) +{ + char *orig_user = ths_user; + ths_user = SMB_STRDUP(newuser); + SAFE_FREE(orig_user); + return ths_user; +} +#endif + +#if !defined(WITH_PAM) +static char *ths_salt; +/* This must be writable. */ +static char *get_this_salt(void) +{ + return ths_salt; +} + +/* We may be setting a modified version of the same + * string, so don't free before use. */ + +static const char *set_this_salt(const char *newsalt) +{ + char *orig_salt = ths_salt; + ths_salt = SMB_STRDUP(newsalt); + SAFE_FREE(orig_salt); + return ths_salt; +} + +static char *ths_crypted; +static const char *get_this_crypted(void) +{ + if (!ths_crypted) { + return ""; + } + return ths_crypted; +} + +static const char *set_this_crypted(const char *newcrypted) +{ + char *orig_crypted = ths_crypted; + ths_crypted = SMB_STRDUP(newcrypted); + SAFE_FREE(orig_crypted); + return ths_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) +{ + struct tm *t; + 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, get_this_salt()), get_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); + + t = localtime(&expire_time); + if (t) { + const char *asct = asctime(t); + if (asct) { + DEBUG(0,("DCE context expires: %s", asct)); + } + } + + 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) (const 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_ascii(c)) + continue; + s[i] = toupper_ascii(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) (const 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(const char *password) +{ +#ifdef WITH_PAM + return smb_pam_passcheck(get_this_user(), password); +#else + + bool ret; + +#ifdef WITH_AFS + if (afs_auth(get_this_user(), password)) + return NT_STATUS_OK; +#endif /* WITH_AFS */ + +#ifdef WITH_DFS + if (dfs_auth(get_this_user(), password)) + return NT_STATUS_OK; +#endif /* WITH_DFS */ + +#ifdef OSF1_ENH_SEC + + ret = (strcmp(osf1_bigcrypt(password, get_this_salt()), + get_this_crypted()) == 0); + if (!ret) { + DEBUG(2, + ("OSF1_ENH_SEC failed. Trying normal crypt.\n")); + ret = (strcmp((char *)crypt(password, get_this_salt()), get_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, get_this_salt()), get_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, get_this_salt(), get_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, get_this_salt()), get_this_crypted()) == 0) + return NT_STATUS_OK; + else + ret = (strcmp((char *)crypt(password, get_this_salt()), get_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, get_this_salt()), get_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, get_this_salt()), get_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 */ +} + + + +/**************************************************************************** +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 *pass, const char *user, const char *password, + int pwlen, bool (*fn) (const char *, const char *), bool run_cracker) +{ + char *pass2 = NULL; + int level = lp_passwordlevel(); + + NTSTATUS nt_status; + +#ifdef 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. + */ + + if (set_this_user(user) == NULL) { + return NT_STATUS_NO_MEMORY; + } + + 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 (!pass) { + DEBUG(3, ("Couldn't find user %s\n", user)); + return NT_STATUS_NO_SUCH_USER; + } + + + /* Copy into global for the convenience of looping code */ + /* Also the place to keep the 'password' no matter what + crazy struct it started in... */ + if (set_this_crypted(pass->pw_passwd) == NULL) { + return NT_STATUS_NO_MEMORY; + } + if (set_this_salt(pass->pw_passwd) == NULL) { + return NT_STATUS_NO_MEMORY; + } + +#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) { + if (set_this_crypted(spass->sp_pwdp) == NULL) { + return NT_STATUS_NO_MEMORY; + } + if (set_this_salt(spass->sp_pwdp) == NULL) { + return NT_STATUS_NO_MEMORY; + } + } + } +#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) { + if (set_this_crypted(pr_pw->ufld.fd_encrypt) == NULL) { + return NT_STATUS_NO_MEMORY; + } + } + } +#endif + +#ifdef HAVE_GETPWANAM + { + struct passwd_adjunct *pwret; + pwret = getpwanam(s); + if (pwret && pwret->pwa_passwd) { + if (set_this_crypted(pwret->pwa_passwd) == NULL) { + return NT_STATUS_NO_MEMORY; + } + } + } +#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) { + if (set_this_user(mypasswd->ufld.fd_name) == NULL) { + return NT_STATUS_NO_MEMORY; + } + if (set_this_crypted(mypasswd->ufld.fd_encrypt) == NULL) { + return NT_STATUS_NO_MEMORY; + } + } 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) { + if (set_this_crypted(ap->a_password) == NULL) { + endauthent(); + return NT_STATUS_NO_MEMORY; + } + endauthent(); + } + } +#endif + +#if defined(HAVE_TRUNCATED_SALT) + /* crypt on some platforms (HPUX in particular) + won't work with more than 2 salt characters. */ + { + char *trunc_salt = get_this_salt(); + if (!trunc_salt || strlen(trunc_salt) < 2) { + return NT_STATUS_LOGON_FAILURE; + } + trunc_salt[2] = 0; + if (set_this_salt(trunc_salt) == NULL) { + return NT_STATUS_NO_MEMORY; + } + } +#endif + + if (!get_this_crypted() || !*get_this_crypted()) { + if (!lp_null_passwords()) { + DEBUG(2, ("Disallowing %s with null password\n", + get_this_user())); + return NT_STATUS_LOGON_FAILURE; + } + if (!*password) { + DEBUG(3, + ("Allowing access to %s with null password\n", + get_this_user())); + return NT_STATUS_OK; + } + } + +#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)) { + return nt_status; + } + + /* make a copy of it */ + pass2 = talloc_strdup(talloc_tos(), password); + if (!pass2) { + return NT_STATUS_NO_MEMORY; + } + + /* try all lowercase if it's currently all uppercase */ + if (strhasupper(pass2)) { + strlower_m(pass2); + if NT_STATUS_IS_OK(nt_status = password_check(pass2)) { + if (fn) + fn(user, pass2); + return (nt_status); + } + } + + /* give up? */ + if (level < 1) { + return NT_STATUS_WRONG_PASSWORD; + } + + /* last chance - all combinations of up to level chars upper! */ + strlower_m(pass2); + + if (NT_STATUS_IS_OK(nt_status = string_combinations(pass2, password_check, level))) { + if (fn) + fn(user, pass2); + return nt_status; + } + + return NT_STATUS_WRONG_PASSWORD; +} diff --git a/source3/auth/token_util.c b/source3/auth/token_util.c new file mode 100644 index 0000000000..d6cd2ea3a8 --- /dev/null +++ b/source3/auth/token_util.c @@ -0,0 +1,556 @@ +/* + * Unix SMB/CIFS implementation. + * Authentication utility functions + * Copyright (C) Andrew Tridgell 1992-1998 + * Copyright (C) Andrew Bartlett 2001 + * Copyright (C) Jeremy Allison 2000-2001 + * Copyright (C) Rafal Szczesniak 2002 + * Copyright (C) Volker Lendecke 2006 + * Copyright (C) Michael Adam 2007 + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +/* functions moved from auth/auth_util.c to minimize linker deps */ + +#include "includes.h" + +/**************************************************************************** + Check for a SID in an NT_USER_TOKEN +****************************************************************************/ + +bool nt_token_check_sid ( const DOM_SID *sid, const NT_USER_TOKEN *token ) +{ + int i; + + if ( !sid || !token ) + return False; + + for ( i=0; i<token->num_sids; i++ ) { + if ( sid_equal( sid, &token->user_sids[i] ) ) + return True; + } + + return False; +} + +bool nt_token_check_domain_rid( NT_USER_TOKEN *token, uint32 rid ) +{ + DOM_SID domain_sid; + + /* if we are a domain member, the get the domain SID, else for + a DC or standalone server, use our own SID */ + + if ( lp_server_role() == ROLE_DOMAIN_MEMBER ) { + if ( !secrets_fetch_domain_sid( lp_workgroup(), + &domain_sid ) ) { + DEBUG(1,("nt_token_check_domain_rid: Cannot lookup " + "SID for domain [%s]\n", lp_workgroup())); + return False; + } + } + else + sid_copy( &domain_sid, get_global_sam_sid() ); + + sid_append_rid( &domain_sid, rid ); + + return nt_token_check_sid( &domain_sid, token );\ +} + +/****************************************************************************** + Create a token for the root user to be used internally by smbd. + This is similar to running under the context of the LOCAL_SYSTEM account + in Windows. This is a read-only token. Do not modify it or free() it. + Create a copy if your need to change it. +******************************************************************************/ + +NT_USER_TOKEN *get_root_nt_token( void ) +{ + struct nt_user_token *token = NULL; + DOM_SID u_sid, g_sid; + struct passwd *pw; + void *cache_data; + + cache_data = memcache_lookup_talloc( + NULL, SINGLETON_CACHE_TALLOC, + data_blob_string_const("root_nt_token")); + + if (cache_data != NULL) { + return talloc_get_type_abort( + cache_data, struct nt_user_token); + } + + if ( !(pw = sys_getpwnam( "root" )) ) { + DEBUG(0,("get_root_nt_token: getpwnam(\"root\") failed!\n")); + return NULL; + } + + /* get the user and primary group SIDs; although the + BUILTIN\Administrators SId is really the one that matters here */ + + uid_to_sid(&u_sid, pw->pw_uid); + gid_to_sid(&g_sid, pw->pw_gid); + + token = create_local_nt_token(NULL, &u_sid, False, + 1, &global_sid_Builtin_Administrators); + + token->privileges = se_disk_operators; + + memcache_add_talloc( + NULL, SINGLETON_CACHE_TALLOC, + data_blob_string_const("root_nt_token"), token); + + return token; +} + + +/* + * Add alias SIDs from memberships within the partially created token SID list + */ + +NTSTATUS add_aliases(const DOM_SID *domain_sid, + struct nt_user_token *token) +{ + uint32 *aliases; + size_t i, num_aliases; + NTSTATUS status; + TALLOC_CTX *tmp_ctx; + + if (!(tmp_ctx = talloc_init("add_aliases"))) { + return NT_STATUS_NO_MEMORY; + } + + aliases = NULL; + num_aliases = 0; + + status = pdb_enum_alias_memberships(tmp_ctx, domain_sid, + token->user_sids, + token->num_sids, + &aliases, &num_aliases); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("pdb_enum_alias_memberships failed: %s\n", + nt_errstr(status))); + goto done; + } + + for (i=0; i<num_aliases; i++) { + DOM_SID alias_sid; + sid_compose(&alias_sid, domain_sid, aliases[i]); + status = add_sid_to_array_unique(token, &alias_sid, + &token->user_sids, + &token->num_sids); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("add_sid_to_array failed\n")); + goto done; + } + } + +done: + TALLOC_FREE(tmp_ctx); + return NT_STATUS_OK; +} + +/******************************************************************* +*******************************************************************/ + +static NTSTATUS add_builtin_administrators(struct nt_user_token *token, + const DOM_SID *dom_sid) +{ + DOM_SID domadm; + NTSTATUS status; + + /* nothing to do if we aren't in a domain */ + + if ( !(IS_DC || lp_server_role()==ROLE_DOMAIN_MEMBER) ) { + return NT_STATUS_OK; + } + + /* Find the Domain Admins SID */ + + if ( IS_DC ) { + sid_copy( &domadm, get_global_sam_sid() ); + } else { + sid_copy(&domadm, dom_sid); + } + sid_append_rid( &domadm, DOMAIN_GROUP_RID_ADMINS ); + + /* Add Administrators if the user beloongs to Domain Admins */ + + if ( nt_token_check_sid( &domadm, token ) ) { + status = add_sid_to_array(token, + &global_sid_Builtin_Administrators, + &token->user_sids, &token->num_sids); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + + return NT_STATUS_OK; +} + +/** + * Create the requested BUILTIN if it doesn't already exist. This requires + * winbindd to be running. + * + * @param[in] rid BUILTIN rid to create + * @return Normal NTSTATUS return. + */ +static NTSTATUS create_builtin(uint32 rid) +{ + NTSTATUS status = NT_STATUS_OK; + DOM_SID sid; + gid_t gid; + + if (!sid_compose(&sid, &global_sid_Builtin, rid)) { + return NT_STATUS_NO_SUCH_ALIAS; + } + + if (!sid_to_gid(&sid, &gid)) { + if (!lp_winbind_nested_groups() || !winbind_ping()) { + return NT_STATUS_PROTOCOL_UNREACHABLE; + } + status = pdb_create_builtin_alias(rid); + } + return status; +} + +/** + * Add sid as a member of builtin_sid. + * + * @param[in] builtin_sid An existing builtin group. + * @param[in] dom_sid sid to add as a member of builtin_sid. + * @return Normal NTSTATUS return + */ +static NTSTATUS add_sid_to_builtin(const DOM_SID *builtin_sid, + const DOM_SID *dom_sid) +{ + NTSTATUS status = NT_STATUS_OK; + + if (!dom_sid || !builtin_sid) { + return NT_STATUS_INVALID_PARAMETER; + } + + status = pdb_add_aliasmem(builtin_sid, dom_sid); + + if (NT_STATUS_EQUAL(status, NT_STATUS_MEMBER_IN_ALIAS)) { + DEBUG(5, ("add_sid_to_builtin %s is already a member of %s\n", + sid_string_dbg(dom_sid), + sid_string_dbg(builtin_sid))); + return NT_STATUS_OK; + } + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(4, ("add_sid_to_builtin %s could not be added to %s: " + "%s\n", sid_string_dbg(dom_sid), + sid_string_dbg(builtin_sid), nt_errstr(status))); + } + return status; +} + +/******************************************************************* +*******************************************************************/ + +NTSTATUS create_builtin_users(const DOM_SID *dom_sid) +{ + NTSTATUS status; + DOM_SID dom_users; + + status = create_builtin(BUILTIN_ALIAS_RID_USERS); + if ( !NT_STATUS_IS_OK(status) ) { + DEBUG(5,("create_builtin_users: Failed to create Users\n")); + return status; + } + + /* add domain users */ + if ((IS_DC || (lp_server_role() == ROLE_DOMAIN_MEMBER)) + && sid_compose(&dom_users, dom_sid, DOMAIN_GROUP_RID_USERS)) + { + status = add_sid_to_builtin(&global_sid_Builtin_Users, + &dom_users); + } + + return status; +} + +/******************************************************************* +*******************************************************************/ + +NTSTATUS create_builtin_administrators(const DOM_SID *dom_sid) +{ + NTSTATUS status; + DOM_SID dom_admins, root_sid; + fstring root_name; + enum lsa_SidType type; + TALLOC_CTX *ctx; + bool ret; + + status = create_builtin(BUILTIN_ALIAS_RID_ADMINS); + if ( !NT_STATUS_IS_OK(status) ) { + DEBUG(5,("create_builtin_administrators: Failed to create Administrators\n")); + return status; + } + + /* add domain admins */ + if ((IS_DC || (lp_server_role() == ROLE_DOMAIN_MEMBER)) + && sid_compose(&dom_admins, dom_sid, DOMAIN_GROUP_RID_ADMINS)) + { + status = add_sid_to_builtin(&global_sid_Builtin_Administrators, + &dom_admins); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + + /* add root */ + if ( (ctx = talloc_init("create_builtin_administrators")) == NULL ) { + return NT_STATUS_NO_MEMORY; + } + fstr_sprintf( root_name, "%s\\root", get_global_sam_name() ); + ret = lookup_name(ctx, root_name, LOOKUP_NAME_DOMAIN, NULL, NULL, + &root_sid, &type); + TALLOC_FREE( ctx ); + + if ( ret ) { + status = add_sid_to_builtin(&global_sid_Builtin_Administrators, + &root_sid); + } + + return status; +} + + +/******************************************************************* + Create a NT token for the user, expanding local aliases +*******************************************************************/ + +struct nt_user_token *create_local_nt_token(TALLOC_CTX *mem_ctx, + const DOM_SID *user_sid, + bool is_guest, + int num_groupsids, + const DOM_SID *groupsids) +{ + struct nt_user_token *result = NULL; + int i; + NTSTATUS status; + gid_t gid; + DOM_SID dom_sid; + + DEBUG(10, ("Create local NT token for %s\n", + sid_string_dbg(user_sid))); + + if (!(result = TALLOC_ZERO_P(mem_ctx, struct nt_user_token))) { + DEBUG(0, ("talloc failed\n")); + return NULL; + } + + /* Add the user and primary group sid */ + + status = add_sid_to_array(result, user_sid, + &result->user_sids, &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { + return NULL; + } + + /* For guest, num_groupsids may be zero. */ + if (num_groupsids) { + status = add_sid_to_array(result, &groupsids[0], + &result->user_sids, + &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { + return NULL; + } + } + + /* Add in BUILTIN sids */ + + status = add_sid_to_array(result, &global_sid_World, + &result->user_sids, &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { + return NULL; + } + status = add_sid_to_array(result, &global_sid_Network, + &result->user_sids, &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { + return NULL; + } + + if (is_guest) { + status = add_sid_to_array(result, &global_sid_Builtin_Guests, + &result->user_sids, + &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { + return NULL; + } + } else { + status = add_sid_to_array(result, + &global_sid_Authenticated_Users, + &result->user_sids, + &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { + return NULL; + } + } + + /* Now the SIDs we got from authentication. These are the ones from + * the info3 struct or from the pdb_enum_group_memberships, depending + * on who authenticated the user. + * Note that we start the for loop at "1" here, we already added the + * first group sid as primary above. */ + + for (i=1; i<num_groupsids; i++) { + status = add_sid_to_array_unique(result, &groupsids[i], + &result->user_sids, + &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { + return NULL; + } + } + + /* Deal with the BUILTIN\Administrators group. If the SID can + be resolved then assume that the add_aliasmem( S-1-5-32 ) + handled it. */ + + if (!sid_to_gid(&global_sid_Builtin_Administrators, &gid)) { + + become_root(); + if (!secrets_fetch_domain_sid(lp_workgroup(), &dom_sid)) { + status = NT_STATUS_OK; + DEBUG(3, ("Failed to fetch domain sid for %s\n", + lp_workgroup())); + } else { + status = create_builtin_administrators(&dom_sid); + } + unbecome_root(); + + if (NT_STATUS_EQUAL(status, NT_STATUS_PROTOCOL_UNREACHABLE)) { + /* Add BUILTIN\Administrators directly to token. */ + status = add_builtin_administrators(result, &dom_sid); + if ( !NT_STATUS_IS_OK(status) ) { + DEBUG(3, ("Failed to check for local " + "Administrators membership (%s)\n", + nt_errstr(status))); + } + } else if (!NT_STATUS_IS_OK(status)) { + DEBUG(2, ("WARNING: Failed to create " + "BUILTIN\\Administrators group! Can " + "Winbind allocate gids?\n")); + } + } + + /* Deal with the BUILTIN\Users group. If the SID can + be resolved then assume that the add_aliasmem( S-1-5-32 ) + handled it. */ + + if (!sid_to_gid(&global_sid_Builtin_Users, &gid)) { + + become_root(); + if (!secrets_fetch_domain_sid(lp_workgroup(), &dom_sid)) { + status = NT_STATUS_OK; + DEBUG(3, ("Failed to fetch domain sid for %s\n", + lp_workgroup())); + } else { + status = create_builtin_users(&dom_sid); + } + unbecome_root(); + + if (!NT_STATUS_EQUAL(status, NT_STATUS_PROTOCOL_UNREACHABLE) && + !NT_STATUS_IS_OK(status)) + { + DEBUG(2, ("WARNING: Failed to create BUILTIN\\Users group! " + "Can Winbind allocate gids?\n")); + } + } + + /* Deal with local groups */ + + if (lp_winbind_nested_groups()) { + + become_root(); + + /* Now add the aliases. First the one from our local SAM */ + + status = add_aliases(get_global_sam_sid(), result); + + if (!NT_STATUS_IS_OK(status)) { + unbecome_root(); + TALLOC_FREE(result); + return NULL; + } + + /* Finally the builtin ones */ + + status = add_aliases(&global_sid_Builtin, result); + + if (!NT_STATUS_IS_OK(status)) { + unbecome_root(); + TALLOC_FREE(result); + return NULL; + } + + unbecome_root(); + } + + + get_privileges_for_sids(&result->privileges, result->user_sids, + result->num_sids); + return result; +} + +/**************************************************************************** + prints a NT_USER_TOKEN to debug output. +****************************************************************************/ + +void debug_nt_user_token(int dbg_class, int dbg_lev, NT_USER_TOKEN *token) +{ + size_t i; + + if (!token) { + DEBUGC(dbg_class, dbg_lev, ("NT user token: (NULL)\n")); + return; + } + + DEBUGC(dbg_class, dbg_lev, + ("NT user token of user %s\n", + sid_string_dbg(&token->user_sids[0]) )); + DEBUGADDC(dbg_class, dbg_lev, + ("contains %lu SIDs\n", (unsigned long)token->num_sids)); + for (i = 0; i < token->num_sids; i++) + DEBUGADDC(dbg_class, dbg_lev, + ("SID[%3lu]: %s\n", (unsigned long)i, + sid_string_dbg(&token->user_sids[i]))); + + dump_se_priv( dbg_class, dbg_lev, &token->privileges ); +} + +/**************************************************************************** + prints a UNIX 'token' to debug output. +****************************************************************************/ + +void debug_unix_user_token(int dbg_class, int dbg_lev, uid_t uid, gid_t gid, + int n_groups, gid_t *groups) +{ + int i; + DEBUGC(dbg_class, dbg_lev, + ("UNIX token of user %ld\n", (long int)uid)); + + DEBUGADDC(dbg_class, dbg_lev, + ("Primary group is %ld and contains %i supplementary " + "groups\n", (long int)gid, n_groups)); + for (i = 0; i < n_groups; i++) + DEBUGADDC(dbg_class, dbg_lev, ("Group[%3i]: %ld\n", i, + (long int)groups[i])); +} + +/* END */ |