diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
commit | 4f5791ebd03eaec1c7da0865a383175b05102712 (patch) | |
tree | 8ce7b00f7a76baa386372422adebbe64510812d4 /source3/auth | |
parent | Initial commit. (diff) | |
download | samba-4f5791ebd03eaec1c7da0865a383175b05102712.tar.xz samba-4f5791ebd03eaec1c7da0865a383175b05102712.zip |
Adding upstream version 2:4.17.12+dfsg.upstream/2%4.17.12+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'source3/auth')
-rw-r--r-- | source3/auth/auth.c | 629 | ||||
-rw-r--r-- | source3/auth/auth_builtin.c | 184 | ||||
-rw-r--r-- | source3/auth/auth_generic.c | 555 | ||||
-rw-r--r-- | source3/auth/auth_ntlmssp.c | 326 | ||||
-rw-r--r-- | source3/auth/auth_sam.c | 315 | ||||
-rw-r--r-- | source3/auth/auth_samba4.c | 398 | ||||
-rw-r--r-- | source3/auth/auth_unix.c | 107 | ||||
-rw-r--r-- | source3/auth/auth_util.c | 2350 | ||||
-rw-r--r-- | source3/auth/auth_winbind.c | 202 | ||||
-rw-r--r-- | source3/auth/check_samsec.c | 634 | ||||
-rw-r--r-- | source3/auth/pampass.c | 896 | ||||
-rw-r--r-- | source3/auth/pass_check.c | 294 | ||||
-rw-r--r-- | source3/auth/proto.h | 438 | ||||
-rw-r--r-- | source3/auth/server_info.c | 791 | ||||
-rw-r--r-- | source3/auth/server_info_sam.c | 124 | ||||
-rw-r--r-- | source3/auth/token_util.c | 1306 | ||||
-rw-r--r-- | source3/auth/user_info.c | 169 | ||||
-rw-r--r-- | source3/auth/user_krb5.c | 263 | ||||
-rw-r--r-- | source3/auth/user_util.c | 447 | ||||
-rw-r--r-- | source3/auth/wscript_build | 62 |
20 files changed, 10490 insertions, 0 deletions
diff --git a/source3/auth/auth.c b/source3/auth/auth.c new file mode 100644 index 0000000..fec19c7 --- /dev/null +++ b/source3/auth/auth.c @@ -0,0 +1,629 @@ +/* + 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" +#include "auth.h" +#include "../lib/tsocket/tsocket.h" + +#include "param/param.h" +#include "../lib/messaging/messaging.h" +#include "lib/global_contexts.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_AUTH + +static_decl_auth; + +static struct auth_init_function_entry *auth_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 = NULL; + + 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(auth_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 = auth_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. +****************************************************************************/ + +NTSTATUS auth_get_ntlm_challenge(struct auth_context *auth_context, + uint8_t chal[8]) +{ + 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)); + memcpy(chal, auth_context->challenge.data, 8); + return NT_STATUS_OK; + } + + auth_context->challenge = data_blob_talloc(auth_context, NULL, 8); + if (auth_context->challenge.data == NULL) { + DBG_WARNING("data_blob_talloc failed\n"); + return NT_STATUS_NO_MEMORY; + } + generate_random_buffer( + auth_context->challenge.data, auth_context->challenge.length); + + auth_context->challenge_set_by = "random"; + + memcpy(chal, auth_context->challenge.data, 8); + return NT_STATUS_OK; +} + + +/** + * 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 pserver_info If successful, contains information about the authentication, + * including a struct samu struct describing the user. + * + * @param pauthoritative Indicates if the result should be treated as final + * result. + * + * @return An NTSTATUS with NT_STATUS_OK or an appropriate error. + * + **/ +NTSTATUS auth_check_ntlm_password(TALLOC_CTX *mem_ctx, + const struct auth_context *auth_context, + const struct auth_usersupplied_info *user_info, + struct auth_serversupplied_info **pserver_info, + uint8_t *pauthoritative) +{ + TALLOC_CTX *frame; + const char *auth_method_name = ""; + /* if all the modules say 'not for me' this is reasonable */ + NTSTATUS nt_status = NT_STATUS_NOT_IMPLEMENTED; + const char *unix_username; + struct auth_methods *auth_method; + struct auth_serversupplied_info *server_info = NULL; + struct dom_sid sid = {0}; + struct imessaging_context *msg_ctx = NULL; + struct loadparm_context *lp_ctx = NULL; + + if (user_info == NULL || auth_context == NULL || pserver_info == NULL) { + return NT_STATUS_LOGON_FAILURE; + } + + frame = talloc_stackframe(); + + if (lp_auth_event_notification()) { + lp_ctx = loadparm_init_s3(frame, loadparm_s3_helpers()); + msg_ctx = imessaging_client_init( + frame, lp_ctx, global_event_context()); + } + + *pauthoritative = 1; + + DEBUG(3, ("check_ntlm_password: Checking password for unmapped user [%s]\\[%s]@[%s] with the new password interface\n", + user_info->client.domain_name, user_info->client.account_name, user_info->workstation_name)); + + DEBUG(3, ("check_ntlm_password: mapped user is: [%s]\\[%s]@[%s]\n", + user_info->mapped.domain_name, user_info->mapped.account_name, user_info->workstation_name)); + + if (auth_context->challenge.length != 8) { + DEBUG(0, ("check_ntlm_password: Invalid challenge stored for this auth context - cannot continue\n")); + nt_status = NT_STATUS_LOGON_FAILURE; + goto fail; + } + + 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->password.response.lanman.length, (int)user_info->password.response.nt.length)); + DEBUG(100, ("lm:\n")); + dump_data(100, user_info->password.response.lanman.data, user_info->password.response.lanman.length); + DEBUG(100, ("nt:\n")); + dump_data(100, user_info->password.response.nt.data, user_info->password.response.nt.length); +#endif + + /* This needs to be sorted: If it doesn't match, what should we do? */ + if (!check_domain_match(user_info->client.account_name, + user_info->mapped.domain_name)) { + nt_status = NT_STATUS_LOGON_FAILURE; + goto fail; + } + + for (auth_method = auth_context->auth_method_list;auth_method; auth_method = auth_method->next) { + + auth_method_name = auth_method->name; + + nt_status = auth_method->auth(auth_context, + auth_method->private_data, + talloc_tos(), + user_info, + &server_info); + + if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_NOT_IMPLEMENTED)) { + break; + } + + DBG_DEBUG("%s had nothing to say\n", auth_method->name); + } + + if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NOT_IMPLEMENTED)) { + *pauthoritative = 0; + nt_status = NT_STATUS_NO_SUCH_USER; + } + + if (!NT_STATUS_IS_OK(nt_status)) { + DBG_INFO("%s authentication for user [%s] FAILED with " + "error %s, authoritative=%u\n", + auth_method_name, + user_info->client.account_name, + nt_errstr(nt_status), + *pauthoritative); + goto fail; + } + + DBG_NOTICE("%s authentication for user [%s] succeeded\n", + auth_method_name, user_info->client.account_name); + + unix_username = server_info->unix_name; + + /* We skip doing this step if the caller asked us not to */ + if (!(user_info->flags & USER_INFO_INFO3_AND_NO_AUTHZ) + && !(server_info->guest)) { + const char *rhost; + + if (tsocket_address_is_inet(user_info->remote_host, "ip")) { + rhost = tsocket_address_inet_addr_string( + user_info->remote_host, talloc_tos()); + if (rhost == NULL) { + nt_status = NT_STATUS_NO_MEMORY; + goto fail; + } + } else { + rhost = "127.0.0.1"; + } + + /* We might not be root if we are an RPC call */ + become_root(); + nt_status = smb_pam_accountcheck(unix_username, rhost); + 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)) { + goto fail; + } + + nt_status = get_user_sid_info3_and_extra(server_info->info3, + &server_info->extra, + &sid); + if (!NT_STATUS_IS_OK(nt_status)) { + sid = (struct dom_sid) {0}; + } + + log_authentication_event(msg_ctx, + lp_ctx, + &auth_context->start_time, + user_info, + nt_status, + server_info->info3->base.logon_domain.string, + server_info->info3->base.account_name.string, + &sid); + + DEBUG(server_info->guest ? 5 : 2, + ("check_ntlm_password: %sauthentication for user " + "[%s] -> [%s] -> [%s] succeeded\n", + server_info->guest ? "guest " : "", + user_info->client.account_name, + user_info->mapped.account_name, + unix_username)); + + *pserver_info = talloc_move(mem_ctx, &server_info); + + TALLOC_FREE(frame); + return NT_STATUS_OK; + +fail: + + /* failed authentication; check for guest lapping */ + + /* + * Please try not to change this string, it is probably in use + * in audit logging tools + */ + DEBUG(2, ("check_ntlm_password: Authentication for user " + "[%s] -> [%s] FAILED with error %s, authoritative=%u\n", + user_info->client.account_name, user_info->mapped.account_name, + nt_errstr(nt_status), *pauthoritative)); + + log_authentication_event(msg_ctx, + lp_ctx, + &auth_context->start_time, + user_info, + nt_status, + NULL, + NULL, + NULL); + + ZERO_STRUCTP(pserver_info); + + TALLOC_FREE(frame); + + return nt_status; +} + +/*************************************************************************** + Clear out a auth_context, and destroy the attached TALLOC_CTX +***************************************************************************/ + +static int auth_context_destructor(void *ptr) +{ + struct auth_context *ctx = talloc_get_type(ptr, struct auth_context); + struct auth_methods *am; + + + /* Free private data of context's authentication methods */ + for (am = ctx->auth_method_list; am; am = am->next) { + TALLOC_FREE(am->private_data); + } + + return 0; +} + +/*************************************************************************** + Make a auth_info struct +***************************************************************************/ + +static NTSTATUS make_auth_context(TALLOC_CTX *mem_ctx, + struct auth_context **auth_context) +{ + struct auth_context *ctx; + + ctx = talloc_zero(mem_ctx, struct auth_context); + if (!ctx) { + DEBUG(0,("make_auth_context: talloc failed!\n")); + return NT_STATUS_NO_MEMORY; + } + + ctx->start_time = timeval_current(); + + talloc_set_destructor((TALLOC_CTX *)ctx, auth_context_destructor); + + *auth_context = ctx; + return NT_STATUS_OK; +} + +static bool load_auth_module( + struct auth_context *auth_context, + const char *module, + struct 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(NULL); + 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(TALLOC_CTX *mem_ctx, + struct auth_context **auth_context, + char **text_list) +{ + struct auth_methods *list = NULL; + struct auth_methods *t, *method = NULL; + NTSTATUS nt_status; + + if (!text_list) { + DEBUG(2,("make_auth_context_text_list: No auth method list!?\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + nt_status = make_auth_context(mem_ctx, auth_context); + + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + for (;*text_list; text_list++) { + if (load_auth_module(*auth_context, *text_list, &t)) { + DLIST_ADD_END(list, t); + } + } + + (*auth_context)->auth_method_list = list; + + /* Look for the first module to provide a prepare_gensec and + * make_auth4_context hook, and set that if provided */ + for (method = (*auth_context)->auth_method_list; method; method = method->next) { + if (method->prepare_gensec && method->make_auth4_context) { + (*auth_context)->prepare_gensec = method->prepare_gensec; + (*auth_context)->make_auth4_context = method->make_auth4_context; + break; + } + } + return NT_STATUS_OK; +} + +static NTSTATUS make_auth_context_specific(TALLOC_CTX *mem_ctx, + struct auth_context **auth_context, + const char *methods) +{ + char **method_list; + NTSTATUS status; + + method_list = str_list_make_v3(talloc_tos(), methods, NULL); + if (method_list == NULL) { + return NT_STATUS_NO_MEMORY; + } + + status = make_auth_context_text_list( + mem_ctx, auth_context, method_list); + + TALLOC_FREE(method_list); + + return status; +} + +/*************************************************************************** + Make a auth_context struct for the auth subsystem +***************************************************************************/ + +NTSTATUS make_auth3_context_for_ntlm(TALLOC_CTX *mem_ctx, + struct auth_context **auth_context) +{ + const char *methods = NULL; + const char *role = NULL; + + switch (lp_server_role()) { + case ROLE_ACTIVE_DIRECTORY_DC: + role = "'active directory domain controller'"; + methods = "samba4"; + break; + case ROLE_DOMAIN_MEMBER: + role = "'domain member'"; + methods = "anonymous sam winbind sam_ignoredomain"; + break; + case ROLE_DOMAIN_BDC: + case ROLE_DOMAIN_PDC: + case ROLE_IPA_DC: + role = "'DC'"; + methods = "anonymous sam winbind sam_ignoredomain"; + break; + case ROLE_STANDALONE: + if (lp_encrypt_passwords()) { + role = "'standalone server', encrypt passwords = yes"; + methods = "anonymous sam_ignoredomain"; + } else { + role = "'standalone server', encrypt passwords = no"; + methods = "anonymous unix"; + } + break; + default: + DEBUG(5,("Unknown auth method!\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + DBG_INFO("Making default auth method list for server role = %s\n", + role); + + return make_auth_context_specific(mem_ctx, auth_context, methods); +} + +NTSTATUS make_auth3_context_for_netlogon(TALLOC_CTX *mem_ctx, + struct auth_context **auth_context) +{ + const char *methods = NULL; + + switch (lp_server_role()) { + case ROLE_DOMAIN_BDC: + case ROLE_DOMAIN_PDC: + case ROLE_IPA_DC: + methods = "sam_netlogon3 winbind"; + break; + + default: + DBG_ERR("Invalid server role!\n"); + return NT_STATUS_INVALID_SERVER_STATE; + } + + return make_auth_context_specific(mem_ctx, auth_context, methods); +} + +NTSTATUS make_auth3_context_for_winbind(TALLOC_CTX *mem_ctx, + struct auth_context **auth_context) +{ + const char *methods = NULL; + + switch (lp_server_role()) { + case ROLE_STANDALONE: + case ROLE_DOMAIN_MEMBER: + case ROLE_DOMAIN_BDC: + case ROLE_DOMAIN_PDC: + case ROLE_IPA_DC: + methods = "sam"; + break; + case ROLE_ACTIVE_DIRECTORY_DC: + methods = "samba4:sam"; + break; + default: + DEBUG(5,("Unknown auth method!\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + return make_auth_context_specific(mem_ctx, auth_context, methods); +} + +bool auth3_context_set_challenge( + struct auth_context *ctx, + const uint8_t chal[8], + const char *challenge_set_by) +{ + ctx->challenge = data_blob_talloc(ctx, chal, 8); + if (ctx->challenge.data == NULL) { + return false; + } + ctx->challenge_set_by = talloc_strdup(ctx, challenge_set_by); + if (ctx->challenge_set_by == NULL) { + return false; + } + return true; +} diff --git a/source3/auth/auth_builtin.c b/source3/auth/auth_builtin.c new file mode 100644 index 0000000..046b979 --- /dev/null +++ b/source3/auth/auth_builtin.c @@ -0,0 +1,184 @@ +/* + 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" +#include "auth.h" +#include "lib/util/string_wrappers.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_AUTH + +/** + * Return a guest logon for anonymous 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_anonymous_security(const struct auth_context *auth_context, + void *my_private_data, + TALLOC_CTX *mem_ctx, + const struct auth_usersupplied_info *user_info, + struct auth_serversupplied_info **server_info) +{ + DEBUG(10, ("Check auth for: [%s]\n", user_info->mapped.account_name)); + + if (user_info->mapped.account_name && *user_info->mapped.account_name) { + /* mark this as 'not for me' */ + return NT_STATUS_NOT_IMPLEMENTED; + } + + switch (user_info->password_state) { + case AUTH_PASSWORD_PLAIN: + if (user_info->password.plaintext != NULL && + strlen(user_info->password.plaintext) > 0) + { + /* mark this as 'not for me' */ + return NT_STATUS_NOT_IMPLEMENTED; + } + break; + case AUTH_PASSWORD_HASH: + if (user_info->password.hash.lanman != NULL) { + /* mark this as 'not for me' */ + return NT_STATUS_NOT_IMPLEMENTED; + } + if (user_info->password.hash.nt != NULL) { + /* mark this as 'not for me' */ + return NT_STATUS_NOT_IMPLEMENTED; + } + break; + case AUTH_PASSWORD_RESPONSE: + if (user_info->password.response.lanman.length == 1) { + if (user_info->password.response.lanman.data[0] != '\0') { + /* mark this as 'not for me' */ + return NT_STATUS_NOT_IMPLEMENTED; + } + } else if (user_info->password.response.lanman.length > 1) { + /* mark this as 'not for me' */ + return NT_STATUS_NOT_IMPLEMENTED; + } + if (user_info->password.response.nt.length > 0) { + /* mark this as 'not for me' */ + return NT_STATUS_NOT_IMPLEMENTED; + } + break; + } + + return make_server_info_anonymous(NULL, server_info); +} + +/* Guest modules initialisation */ + +static NTSTATUS auth_init_anonymous( + struct auth_context *auth_context, + const char *options, + struct auth_methods **auth_method) +{ + struct auth_methods *result; + + result = talloc_zero(auth_context, struct auth_methods); + if (result == NULL) { + return NT_STATUS_NO_MEMORY; + } + result->auth = check_anonymous_security; + result->name = "anonymous"; + + *auth_method = result; + 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 struct auth_usersupplied_info *user_info, + struct auth_serversupplied_info **server_info) +{ + NTSTATUS nt_status; + fstring user; + long error_num; + + DEBUG(10, ("Check auth for: [%s]\n", user_info->mapped.account_name)); + + fstrcpy(user, user_info->client.account_name); + + if (strnequal("NT_STATUS", user, strlen("NT_STATUS"))) { + if (!strupper_m(user)) { + return NT_STATUS_INVALID_PARAMETER; + } + return nt_status_string_to_code(user); + } + + if (!strlower_m(user)) { + return NT_STATUS_INVALID_PARAMETER; + } + 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, + struct auth_methods **auth_method) +{ + struct auth_methods *result; + + result = talloc_zero(auth_context, struct auth_methods); + if (result == NULL) { + return NT_STATUS_NO_MEMORY; + } + result->auth = check_name_to_ntstatus_security; + result->name = "name_to_ntstatus"; + + *auth_method = result; + return NT_STATUS_OK; +} + +#endif /* DEVELOPER */ + +NTSTATUS auth_builtin_init(TALLOC_CTX *mem_ctx) +{ + smb_register_auth(AUTH_INTERFACE_VERSION, "anonymous", auth_init_anonymous); +#ifdef DEVELOPER + 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_generic.c b/source3/auth/auth_generic.c new file mode 100644 index 0000000..6c61eb4 --- /dev/null +++ b/source3/auth/auth_generic.c @@ -0,0 +1,555 @@ +/* + Unix SMB/Netbios implementation. + Version 3.0 + handle GENSEC authentication, server side + + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Andrew Bartlett 2001-2003,2011 + Copyright (C) Simo Sorce 2010. + + 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" +#include <tevent.h> +#include "../lib/util/tevent_ntstatus.h" +#include "auth.h" +#include "../lib/tsocket/tsocket.h" +#include "auth/gensec/gensec.h" +#include "lib/param/param.h" +#ifdef HAVE_KRB5 +#include "auth/kerberos/pac_utils.h" +#include "nsswitch/libwbclient/wbclient.h" +#endif +#include "librpc/crypto/gse.h" +#include "auth/credentials/credentials.h" +#include "lib/param/loadparm.h" +#include "librpc/gen_ndr/dcerpc.h" +#include "source3/lib/substitute.h" + +static NTSTATUS auth3_generate_session_info_pac(struct auth4_context *auth_ctx, + TALLOC_CTX *mem_ctx, + struct smb_krb5_context *smb_krb5_context, + DATA_BLOB *pac_blob, + const char *princ_name, + const struct tsocket_address *remote_address, + uint32_t session_info_flags, + struct auth_session_info **session_info) +{ + enum server_role server_role = lp_server_role(); + TALLOC_CTX *tmp_ctx; + bool is_mapped; + bool is_guest; + char *ntuser; + char *ntdomain; + char *username; + const char *rhost; + struct passwd *pw; + NTSTATUS status; + + tmp_ctx = talloc_new(mem_ctx); + if (!tmp_ctx) { + return NT_STATUS_NO_MEMORY; + } + + if (tsocket_address_is_inet(remote_address, "ip")) { + rhost = tsocket_address_inet_addr_string( + remote_address, tmp_ctx); + if (rhost == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + } else { + rhost = "127.0.0.1"; + } + + if (server_role != ROLE_STANDALONE) { + struct wbcAuthUserParams params = { 0 }; + struct wbcAuthUserInfo *info = NULL; + struct wbcAuthErrorInfo *err = NULL; + struct auth_serversupplied_info *server_info = NULL; + char *original_user_name = NULL; + char *p = NULL; + wbcErr wbc_err; + + if (pac_blob == NULL) { + /* + * This should already be catched at the main + * gensec layer, but better check twice + */ + status = NT_STATUS_INTERNAL_ERROR; + goto done; + } + + /* + * Let winbind decode the PAC. + * This will also store the user + * data in the netsamlogon cache. + * + * This used to be a cache prime + * optimization, but now we delegate + * all logic to winbindd, as we require + * winbindd as domain member anyway. + */ + params.level = WBC_AUTH_USER_LEVEL_PAC; + params.password.pac.data = pac_blob->data; + params.password.pac.length = pac_blob->length; + + /* we are contacting the privileged pipe */ + become_root(); + wbc_err = wbcAuthenticateUserEx(¶ms, &info, &err); + unbecome_root(); + + /* + * As this is merely a cache prime + * WBC_ERR_WINBIND_NOT_AVAILABLE + * is not a fatal error, treat it + * as success. + */ + + switch (wbc_err) { + case WBC_ERR_SUCCESS: + break; + case WBC_ERR_WINBIND_NOT_AVAILABLE: + status = NT_STATUS_NO_LOGON_SERVERS; + DBG_ERR("winbindd not running - " + "but required as domain member: %s\n", + nt_errstr(status)); + goto done; + case WBC_ERR_AUTH_ERROR: + status = NT_STATUS(err->nt_status); + wbcFreeMemory(err); + goto done; + case WBC_ERR_NO_MEMORY: + status = NT_STATUS_NO_MEMORY; + goto done; + default: + status = NT_STATUS_LOGON_FAILURE; + goto done; + } + + status = make_server_info_wbcAuthUserInfo(tmp_ctx, + info->account_name, + info->domain_name, + info, &server_info); + wbcFreeMemory(info); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("make_server_info_wbcAuthUserInfo failed: %s\n", + nt_errstr(status))); + goto done; + } + + /* We skip doing this step if the caller asked us not to */ + if (!(server_info->guest)) { + const char *unix_username = server_info->unix_name; + + /* We might not be root if we are an RPC call */ + become_root(); + status = smb_pam_accountcheck(unix_username, rhost); + unbecome_root(); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("check_ntlm_password: PAM Account for user [%s] " + "FAILED with error %s\n", + unix_username, nt_errstr(status))); + goto done; + } + + DEBUG(5, ("check_ntlm_password: PAM Account for user [%s] " + "succeeded\n", unix_username)); + } + + DEBUG(3, ("Kerberos ticket principal name is [%s]\n", princ_name)); + + p = strchr_m(princ_name, '@'); + if (!p) { + DEBUG(3, ("[%s] Doesn't look like a valid principal\n", + princ_name)); + status = NT_STATUS_LOGON_FAILURE; + goto done; + } + + original_user_name = talloc_strndup(tmp_ctx, princ_name, p - princ_name); + if (original_user_name == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + status = create_local_token(mem_ctx, + server_info, + NULL, + original_user_name, + session_info); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("create_local_token failed: %s\n", + nt_errstr(status))); + goto done; + } + + goto session_info_ready; + } + + /* This is the standalone legacy code path */ + + if (pac_blob != NULL) { + /* + * In standalone mode we don't expect a PAC! + * we only support MIT realms + */ + status = NT_STATUS_BAD_TOKEN_TYPE; + DBG_WARNING("Unexpected PAC for [%s] in standalone mode - %s\n", + princ_name, nt_errstr(status)); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + } + + status = get_user_from_kerberos_info(tmp_ctx, rhost, + princ_name, + &is_mapped, &is_guest, + &ntuser, &ntdomain, + &username, &pw); + if (!NT_STATUS_IS_OK(status)) { + DBG_NOTICE("Failed to map kerberos principal to system user " + "(%s)\n", nt_errstr(status)); + status = NT_STATUS_ACCESS_DENIED; + goto done; + } + + status = make_session_info_krb5(mem_ctx, + ntuser, ntdomain, username, pw, + is_guest, is_mapped, + session_info); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to map kerberos pac to server info (%s)\n", + nt_errstr(status))); + status = nt_status_squash(status); + goto done; + } + +session_info_ready: + + /* setup the string used by %U */ + set_current_user_info((*session_info)->unix_info->sanitized_username, + (*session_info)->unix_info->unix_name, + (*session_info)->info->domain_name); + + /* reload services so that the new %U is taken into account */ + lp_load_with_shares(get_dyn_CONFIGFILE()); + + DEBUG(5, (__location__ "OK: user: %s domain: %s client: %s\n", + (*session_info)->info->account_name, + (*session_info)->info->domain_name, + rhost)); + + status = NT_STATUS_OK; + +done: + TALLOC_FREE(tmp_ctx); + return status; +} + +static struct auth4_context *make_auth4_context_s3(TALLOC_CTX *mem_ctx, struct auth_context *auth_context) +{ + struct auth4_context *auth4_context = talloc_zero(mem_ctx, struct auth4_context); + if (auth4_context == NULL) { + DEBUG(10, ("failed to allocate auth4_context failed\n")); + return NULL; + } + auth4_context->generate_session_info_pac = auth3_generate_session_info_pac; + auth4_context->generate_session_info = auth3_generate_session_info; + auth4_context->get_ntlm_challenge = auth3_get_challenge; + auth4_context->set_ntlm_challenge = auth3_set_challenge; + auth4_context->check_ntlm_password_send = auth3_check_password_send; + auth4_context->check_ntlm_password_recv = auth3_check_password_recv; + auth4_context->private_data = talloc_steal(auth4_context, auth_context); + return auth4_context; +} + +NTSTATUS make_auth4_context(TALLOC_CTX *mem_ctx, struct auth4_context **auth4_context_out) +{ + struct auth_context *auth_context; + NTSTATUS nt_status; + + TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); + NT_STATUS_HAVE_NO_MEMORY(tmp_ctx); + + nt_status = make_auth3_context_for_ntlm(tmp_ctx, &auth_context); + if (!NT_STATUS_IS_OK(nt_status)) { + TALLOC_FREE(tmp_ctx); + return nt_status; + } + + if (auth_context->make_auth4_context) { + nt_status = auth_context->make_auth4_context(auth_context, mem_ctx, auth4_context_out); + TALLOC_FREE(tmp_ctx); + return nt_status; + + } else { + struct auth4_context *auth4_context = make_auth4_context_s3(tmp_ctx, auth_context); + if (auth4_context == NULL) { + TALLOC_FREE(tmp_ctx); + return NT_STATUS_NO_MEMORY; + } + *auth4_context_out = talloc_steal(mem_ctx, auth4_context); + TALLOC_FREE(tmp_ctx); + return NT_STATUS_OK; + } +} + +NTSTATUS auth_generic_prepare(TALLOC_CTX *mem_ctx, + const struct tsocket_address *remote_address, + const struct tsocket_address *local_address, + const char *service_description, + struct gensec_security **gensec_security_out) +{ + struct gensec_security *gensec_security; + struct auth_context *auth_context = NULL; + NTSTATUS nt_status; + + TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); + NT_STATUS_HAVE_NO_MEMORY(tmp_ctx); + + nt_status = make_auth3_context_for_ntlm(tmp_ctx, &auth_context); + if (!NT_STATUS_IS_OK(nt_status)) { + goto done; + } + + if (auth_context->prepare_gensec) { + nt_status = auth_context->prepare_gensec(auth_context, tmp_ctx, + &gensec_security); + if (!NT_STATUS_IS_OK(nt_status)) { + goto done; + } + } else { + const struct gensec_security_ops **backends = NULL; + struct gensec_settings *gensec_settings; + struct loadparm_context *lp_ctx; + size_t idx = 0; + struct cli_credentials *server_credentials; + const char *dns_name; + const char *dns_domain; + bool ok; + struct auth4_context *auth4_context = make_auth4_context_s3(tmp_ctx, auth_context); + if (auth4_context == NULL) { + goto nomem; + } + + lp_ctx = loadparm_init_s3(tmp_ctx, loadparm_s3_helpers()); + if (lp_ctx == NULL) { + DEBUG(10, ("loadparm_init_s3 failed\n")); + nt_status = NT_STATUS_INVALID_SERVER_STATE; + goto done; + } + + gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx); + if (lp_ctx == NULL) { + DEBUG(10, ("lpcfg_gensec_settings failed\n")); + goto nomem; + } + + /* + * This should be a 'netbios domain -> DNS domain' + * mapping, and can currently validly return NULL on + * poorly configured systems. + * + * This is used for the NTLMSSP server + * + */ + dns_name = get_mydnsfullname(); + if (dns_name == NULL) { + dns_name = ""; + } + + dns_domain = get_mydnsdomname(tmp_ctx); + if (dns_domain == NULL) { + dns_domain = ""; + } + + gensec_settings->server_dns_name = strlower_talloc(gensec_settings, dns_name); + if (gensec_settings->server_dns_name == NULL) { + goto nomem; + } + + gensec_settings->server_dns_domain = strlower_talloc(gensec_settings, dns_domain); + if (gensec_settings->server_dns_domain == NULL) { + goto nomem; + } + + backends = talloc_zero_array(gensec_settings, + const struct gensec_security_ops *, 6); + if (backends == NULL) { + goto nomem; + } + gensec_settings->backends = backends; + + gensec_init(); + + /* These need to be in priority order, krb5 before NTLMSSP */ +#if defined(HAVE_KRB5) + backends[idx++] = &gensec_gse_krb5_security_ops; +#endif + + backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP); + + backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_SPNEGO); + + backends[idx++] = gensec_security_by_auth_type(NULL, DCERPC_AUTH_TYPE_SCHANNEL); + + backends[idx++] = gensec_security_by_auth_type(NULL, DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM); + + /* + * This is anonymous for now, because we just use it + * to set the kerberos state at the moment + */ + server_credentials = cli_credentials_init_anon(tmp_ctx); + if (!server_credentials) { + DEBUG(0, ("auth_generic_prepare: Failed to init server credentials\n")); + goto nomem; + } + + ok = cli_credentials_set_conf(server_credentials, lp_ctx); + if (!ok) { + DBG_ERR("Failed to set server credentials defaults " + "from smb.conf.\n"); + goto nomem; + } + + if (lp_security() == SEC_ADS || USE_KERBEROS_KEYTAB) { + cli_credentials_set_kerberos_state(server_credentials, + CRED_USE_KERBEROS_DESIRED, + CRED_SPECIFIED); + } else { + cli_credentials_set_kerberos_state(server_credentials, + CRED_USE_KERBEROS_DISABLED, + CRED_SPECIFIED); + } + + nt_status = gensec_server_start(tmp_ctx, gensec_settings, + auth4_context, &gensec_security); + + if (!NT_STATUS_IS_OK(nt_status)) { + goto done; + } + + nt_status = gensec_set_credentials( + gensec_security, server_credentials); + if (!NT_STATUS_IS_OK(nt_status)) { + goto done; + } + } + + nt_status = gensec_set_remote_address(gensec_security, + remote_address); + if (!NT_STATUS_IS_OK(nt_status)) { + goto done; + } + + nt_status = gensec_set_local_address(gensec_security, + local_address); + if (!NT_STATUS_IS_OK(nt_status)) { + goto done; + } + + nt_status = gensec_set_target_service_description(gensec_security, + service_description); + if (!NT_STATUS_IS_OK(nt_status)) { + goto done; + } + + *gensec_security_out = talloc_move(mem_ctx, &gensec_security); + nt_status = NT_STATUS_OK; + goto done; +nomem: + nt_status = NT_STATUS_NO_MEMORY; +done: + TALLOC_FREE(tmp_ctx); + return nt_status; +} + +/* + * Check a username and password, and return the final session_info. + * We also log the authorization of the session here, just as + * gensec_session_info() does. + */ +NTSTATUS auth_check_password_session_info(struct auth4_context *auth_context, + TALLOC_CTX *mem_ctx, + struct auth_usersupplied_info *user_info, + struct auth_session_info **session_info) +{ + NTSTATUS nt_status; + void *server_info; + uint8_t authoritative = 1; + struct tevent_context *ev = NULL; + struct tevent_req *subreq = NULL; + bool ok; + + ev = samba_tevent_context_init(talloc_tos()); + if (ev == NULL) { + return NT_STATUS_NO_MEMORY; + } + + subreq = auth_context->check_ntlm_password_send(ev, ev, + auth_context, + user_info); + if (subreq == NULL) { + TALLOC_FREE(ev); + return NT_STATUS_NO_MEMORY; + } + ok = tevent_req_poll_ntstatus(subreq, ev, &nt_status); + if (!ok) { + TALLOC_FREE(ev); + return nt_status; + } + nt_status = auth_context->check_ntlm_password_recv(subreq, + talloc_tos(), + &authoritative, + &server_info, + NULL, NULL); + TALLOC_FREE(ev); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + nt_status = auth_context->generate_session_info(auth_context, + mem_ctx, + server_info, + user_info->client.account_name, + AUTH_SESSION_INFO_UNIX_TOKEN | + AUTH_SESSION_INFO_DEFAULT_GROUPS | + AUTH_SESSION_INFO_NTLM, + session_info); + TALLOC_FREE(server_info); + + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + /* + * This is rather redundant (the authentication has just been + * logged, with much the same details), but because we want to + * log all authorizations consistently (be they NLTM, NTLMSSP + * or krb5) we log this info again as an authorization. + */ + log_successful_authz_event(auth_context->msg_ctx, + auth_context->lp_ctx, + user_info->remote_host, + user_info->local_host, + user_info->service_description, + user_info->auth_description, + AUTHZ_TRANSPORT_PROTECTION_SMB, + *session_info); + + return nt_status; +} diff --git a/source3/auth/auth_ntlmssp.c b/source3/auth/auth_ntlmssp.c new file mode 100644 index 0000000..f2deca0 --- /dev/null +++ b/source3/auth/auth_ntlmssp.c @@ -0,0 +1,326 @@ +/* + Unix SMB/Netbios implementation. + Version 3.0 + handle NLTMSSP, server side + + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Andrew Bartlett 2001-2005,2011 + Copyright (C) Stefan Metzmacher 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 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" +#include "auth.h" +#include "libcli/security/security.h" +#include "lib/util/tevent_ntstatus.h" +#include "source3/lib/substitute.h" + +NTSTATUS auth3_generate_session_info(struct auth4_context *auth_context, + TALLOC_CTX *mem_ctx, + void *server_returned_info, + const char *original_user_name, + uint32_t session_info_flags, + struct auth_session_info **session_info) +{ + struct auth_user_info_dc *user_info = NULL; + struct auth_serversupplied_info *server_info = NULL; + NTSTATUS nt_status; + + /* + * This is a hack, some callers... + * + * Some callers pass auth_user_info_dc, the SCHANNEL and + * NCALRPC_AS_SYSTEM gensec modules. + * + * While the rest passes auth3_check_password() returned. + */ + user_info = talloc_get_type(server_returned_info, + struct auth_user_info_dc); + if (user_info != NULL) { + const struct dom_sid *sid; + int cmp; + + /* + * This should only be called from SCHANNEL or NCALRPC_AS_SYSTEM + */ + if (user_info->num_sids != 1) { + return NT_STATUS_INTERNAL_ERROR; + } + sid = &user_info->sids[PRIMARY_USER_SID_INDEX]; + + cmp = dom_sid_compare(sid, &global_sid_System); + if (cmp == 0) { + return make_session_info_system(mem_ctx, session_info); + } + + cmp = dom_sid_compare(sid, &global_sid_Anonymous); + if (cmp == 0) { + return make_session_info_anonymous(mem_ctx, session_info); + } + + return NT_STATUS_INTERNAL_ERROR; + } + + server_info = talloc_get_type_abort(server_returned_info, + struct auth_serversupplied_info); + nt_status = create_local_token(mem_ctx, + server_info, + NULL, + original_user_name, + session_info); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(10, ("create_local_token failed: %s\n", + nt_errstr(nt_status))); + return nt_status; + } + + return NT_STATUS_OK; +} + +/** + * Return the challenge as determined by the authentication subsystem + * @return an 8 byte random challenge + */ + +NTSTATUS auth3_get_challenge(struct auth4_context *auth4_context, + uint8_t chal[8]) +{ + struct auth_context *auth_context = talloc_get_type_abort(auth4_context->private_data, + struct auth_context); + auth_get_ntlm_challenge(auth_context, chal); + return NT_STATUS_OK; +} + +/** + * NTLM2 authentication modifies the effective challenge, + * @param challenge The new challenge value + */ +NTSTATUS auth3_set_challenge(struct auth4_context *auth4_context, const uint8_t *chal, + const char *challenge_set_by) +{ + struct auth_context *auth_context = talloc_get_type_abort(auth4_context->private_data, + struct auth_context); + bool ok; + + ok = auth3_context_set_challenge(auth_context, chal, challenge_set_by); + if (!ok) { + /* + * This can only fail for ENOMEM + */ + return NT_STATUS_NO_MEMORY; + } + + 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. + */ + +struct auth3_check_password_state { + uint8_t authoritative; + void *server_info; + DATA_BLOB nt_session_key; + DATA_BLOB lm_session_key; +}; + +struct tevent_req *auth3_check_password_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct auth4_context *auth4_context, + const struct auth_usersupplied_info *user_info) +{ + struct tevent_req *req = NULL; + struct auth3_check_password_state *state = NULL; + struct auth_context *auth_context = talloc_get_type_abort( + auth4_context->private_data, struct auth_context); + struct auth_usersupplied_info *mapped_user_info = NULL; + struct auth_serversupplied_info *server_info = NULL; + char *sanitized_username = NULL; + NTSTATUS nt_status; + bool username_was_mapped; + + req = tevent_req_create( + mem_ctx, &state, struct auth3_check_password_state); + if (req == NULL) { + return NULL; + } + + /* + * Be authoritative by default. + */ + state->authoritative = 1; + + /* The client has given us its machine name (which we only get over NBT transport). + We need to possibly reload smb.conf if smb.conf includes depend on the machine name. */ + + set_remote_machine_name(user_info->workstation_name, True); + + nt_status = make_user_info_map(talloc_tos(), + &mapped_user_info, + user_info->client.account_name, + user_info->client.domain_name, + user_info->workstation_name, + user_info->remote_host, + user_info->local_host, + user_info->service_description, + user_info->password.response.lanman.data ? &user_info->password.response.lanman : NULL, + user_info->password.response.nt.data ? &user_info->password.response.nt : NULL, + NULL, NULL, NULL, + AUTH_PASSWORD_RESPONSE); + + if (tevent_req_nterror(req, nt_status)) { + return tevent_req_post(req, ev); + } + + mapped_user_info->logon_parameters = user_info->logon_parameters; + + mapped_user_info->flags = user_info->flags; + + sanitized_username = talloc_alpha_strcpy( + state, + user_info->client.account_name, + SAFE_NETBIOS_CHARS "$"); + if (sanitized_username == NULL) { + tevent_req_nterror(req, NT_STATUS_NO_MEMORY); + return tevent_req_post(req, ev); + } + + nt_status = auth_check_ntlm_password(state, + auth_context, + mapped_user_info, + &server_info, + &state->authoritative); + + if (!NT_STATUS_IS_OK(nt_status)) { + DBG_INFO("Checking NTLMSSP password for %s\\%s failed: " + "%s, authoritative=%"PRIu8"\n", + user_info->client.domain_name, + user_info->client.account_name, + nt_errstr(nt_status), + state->authoritative); + } + + username_was_mapped = mapped_user_info->was_mapped; + + TALLOC_FREE(mapped_user_info); + + if (!NT_STATUS_IS_OK(nt_status)) { + nt_status = do_map_to_guest_server_info( + state, + nt_status, + user_info->client.account_name, + user_info->client.domain_name, + &server_info); + if (!tevent_req_nterror(req, nt_status)) { + state->authoritative = 1; + + /* setup the string used by %U */ + set_current_user_info( + sanitized_username, + server_info->unix_name, + server_info->info3->base.logon_domain.string); + + lp_load_with_shares(get_dyn_CONFIGFILE()); + + tevent_req_done(req); + } + state->server_info = server_info; + return tevent_req_post(req, ev); + } + + server_info->nss_token |= username_was_mapped; + + /* setup the string used by %U */ + set_current_user_info(sanitized_username, + server_info->unix_name, + server_info->info3->base.logon_domain.string); + + lp_load_with_shares(get_dyn_CONFIGFILE()); + + /* Clear out the session keys, and pass them to the caller. + * They will not be used in this form again - instead the + * NTLMSSP code will decide on the final correct session key, + * and supply it to create_local_token() */ + + DBG_DEBUG("Got NT session key of length %zu\n", + server_info->session_key.length); + state->nt_session_key = (DATA_BLOB) { + .data = talloc_move( + state, &server_info->session_key.data), + .length = server_info->session_key.length, + }; + server_info->session_key = data_blob_null; + + DBG_DEBUG("Got LM session key of length %zu\n", + server_info->lm_session_key.length); + state->lm_session_key = (DATA_BLOB) { + .data = talloc_move( + state, &server_info->lm_session_key.data), + .length = server_info->lm_session_key.length, + }; + server_info->lm_session_key = data_blob_null; + + state->server_info = server_info; + + tevent_req_done(req); + return tevent_req_post(req, ev); +} + +NTSTATUS auth3_check_password_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + uint8_t *pauthoritative, + void **server_returned_info, + DATA_BLOB *nt_session_key, + DATA_BLOB *lm_session_key) +{ + struct auth3_check_password_state *state = tevent_req_data( + req, struct auth3_check_password_state); + NTSTATUS status; + + if (pauthoritative != NULL) { + *pauthoritative = state->authoritative; + } + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + + if (server_returned_info != NULL) { + *server_returned_info = talloc_move( + mem_ctx, &state->server_info); + } + if (nt_session_key != NULL) { + *nt_session_key = (DATA_BLOB) { + .data = talloc_move( + mem_ctx, &state->nt_session_key.data), + .length = state->nt_session_key.length, + }; + } + if (lm_session_key != NULL) { + *lm_session_key = (DATA_BLOB) { + .data = talloc_move( + mem_ctx, &state->lm_session_key.data), + .length = state->lm_session_key.length, + }; + } + + return NT_STATUS_OK; +} diff --git a/source3/auth/auth_sam.c b/source3/auth/auth_sam.c new file mode 100644 index 0000000..a2ce101 --- /dev/null +++ b/source3/auth/auth_sam.c @@ -0,0 +1,315 @@ +/* + 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" +#include "auth.h" +#include "passdb.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_AUTH + +static NTSTATUS auth_sam_ignoredomain_auth(const struct auth_context *auth_context, + void *my_private_data, + TALLOC_CTX *mem_ctx, + const struct auth_usersupplied_info *user_info, + struct auth_serversupplied_info **server_info) +{ + if (!user_info || !auth_context) { + return NT_STATUS_UNSUCCESSFUL; + } + + if (user_info->mapped.account_name == NULL || + user_info->mapped.account_name[0] == '\0') + { + return NT_STATUS_NOT_IMPLEMENTED; + } + + DBG_DEBUG("Check auth for: [%s]\\[%s]\n", + user_info->mapped.domain_name, + user_info->mapped.account_name); + + return check_sam_security(&auth_context->challenge, mem_ctx, + user_info, server_info); +} + +/* module initialisation */ +static NTSTATUS auth_init_sam_ignoredomain( + struct auth_context *auth_context, + const char *param, + struct auth_methods **auth_method) +{ + struct auth_methods *result; + + result = talloc_zero(auth_context, struct auth_methods); + if (result == NULL) { + return NT_STATUS_NO_MEMORY; + } + result->auth = auth_sam_ignoredomain_auth; + result->name = "sam_ignoredomain"; + + *auth_method = result; + return NT_STATUS_OK; +} + + +/**************************************************************************** +Check SAM security (above) but with a few extra checks. +****************************************************************************/ + +static NTSTATUS auth_samstrict_auth(const struct auth_context *auth_context, + void *my_private_data, + TALLOC_CTX *mem_ctx, + const struct auth_usersupplied_info *user_info, + struct auth_serversupplied_info **server_info) +{ + const char *effective_domain = NULL; + bool is_local_name, is_my_domain; + + if (!user_info || !auth_context) { + return NT_STATUS_LOGON_FAILURE; + } + effective_domain = user_info->mapped.domain_name; + + if (user_info->mapped.account_name == NULL || + user_info->mapped.account_name[0] == '\0') + { + return NT_STATUS_NOT_IMPLEMENTED; + } + + if (lp_server_role() == ROLE_DOMAIN_MEMBER) { + const char *p = NULL; + + p = strchr_m(user_info->mapped.account_name, '@'); + if (p != NULL) { + /* + * This needs to go to the DC, + * even if @ is the last character + */ + return NT_STATUS_NOT_IMPLEMENTED; + } + } + + if (effective_domain == NULL) { + effective_domain = ""; + } + + DBG_DEBUG("Check auth for: [%s]\\[%s]\n", + effective_domain, + user_info->mapped.account_name); + + + if (strequal(effective_domain, "") || strequal(effective_domain, ".")) { + /* + * An empty domain name or '.' should be handled + * as the local SAM name. + */ + effective_domain = lp_netbios_name(); + } + + is_local_name = is_myname(effective_domain); + is_my_domain = strequal(effective_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", + effective_domain, (lp_server_role() == ROLE_DOMAIN_MEMBER + ? "ROLE_DOMAIN_MEMBER" : "ROLE_STANDALONE") )); + return NT_STATUS_NOT_IMPLEMENTED; + } + + break; + case ROLE_DOMAIN_PDC: + case ROLE_DOMAIN_BDC: + case ROLE_IPA_DC: + if (!is_local_name && !is_my_domain) { + /* If we are running on a DC that has PASSDB module with domain + * information, check if DNS forest name is matching the domain + * name. This is the case of IPA domain controller when + * trusted AD DCs attempt to authenticate IPA users using + * the forest root domain (which is the only domain in IPA). + */ + struct pdb_domain_info *dom_info = NULL; + + dom_info = pdb_get_domain_info(mem_ctx); + if ((dom_info != NULL) && (dom_info->dns_forest != NULL)) { + is_my_domain = strequal(user_info->mapped.domain_name, + dom_info->dns_forest); + } + + TALLOC_FREE(dom_info); + if (!is_my_domain) { + DEBUG(6,("check_samstrict_security: %s is not one " + "of my local names or domain name (DC)\n", + effective_domain)); + return NT_STATUS_NOT_IMPLEMENTED; + } + } + + break; + default: /* name is ok */ + break; + } + + return check_sam_security(&auth_context->challenge, mem_ctx, + user_info, server_info); +} + +/* module initialisation */ +static NTSTATUS auth_init_sam( + struct auth_context *auth_context, + const char *param, + struct auth_methods **auth_method) +{ + struct auth_methods *result; + + if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC + && !lp_parm_bool(-1, "server role check", "inhibit", false)) { + DEBUG(0, ("server role = 'active directory domain controller' not compatible with running the auth_sam module. \n")); + DEBUGADD(0, ("You should not set 'auth methods' when running the AD DC.\n")); + exit(1); + } + + result = talloc_zero(auth_context, struct auth_methods); + if (result == NULL) { + return NT_STATUS_NO_MEMORY; + } + result->auth = auth_samstrict_auth; + result->name = "sam"; + *auth_method = result; + return NT_STATUS_OK; +} + +static NTSTATUS auth_sam_netlogon3_auth(const struct auth_context *auth_context, + void *my_private_data, + TALLOC_CTX *mem_ctx, + const struct auth_usersupplied_info *user_info, + struct auth_serversupplied_info **server_info) +{ + const char *effective_domain = NULL; + bool is_my_domain; + + if (!user_info || !auth_context) { + return NT_STATUS_LOGON_FAILURE; + } + effective_domain = user_info->mapped.domain_name; + + if (user_info->mapped.account_name == NULL || + user_info->mapped.account_name[0] == '\0') + { + return NT_STATUS_NOT_IMPLEMENTED; + } + + if (effective_domain == NULL) { + effective_domain = ""; + } + + DBG_DEBUG("Check auth for: [%s]\\[%s]\n", + effective_domain, + user_info->mapped.account_name); + + /* check whether or not we service this domain/workgroup name */ + + switch (lp_server_role()) { + case ROLE_DOMAIN_PDC: + case ROLE_DOMAIN_BDC: + case ROLE_IPA_DC: + break; + default: + DBG_ERR("Invalid server role\n"); + return NT_STATUS_INVALID_SERVER_STATE; + } + + if (strequal(effective_domain, "") || strequal(effective_domain, ".")) { + /* + * An empty domain name or '.' should be handled + * as the local SAM name. + */ + effective_domain = lp_workgroup(); + } + + is_my_domain = strequal(user_info->mapped.domain_name, lp_workgroup()); + if (!is_my_domain) { + /* If we are running on a DC that has PASSDB module with domain + * information, check if DNS forest name is matching the domain + * name. This is the case of IPA domain controller when + * trusted AD DCs attempt to authenticate IPA users using + * the forest root domain (which is the only domain in IPA). + */ + struct pdb_domain_info *dom_info = NULL; + dom_info = pdb_get_domain_info(mem_ctx); + + if ((dom_info != NULL) && (dom_info->dns_forest != NULL)) { + is_my_domain = strequal(user_info->mapped.domain_name, + dom_info->dns_forest); + } + + TALLOC_FREE(dom_info); + } + + if (!is_my_domain) { + DBG_INFO("%s is not our domain name (DC for %s)\n", + effective_domain, lp_workgroup()); + return NT_STATUS_NOT_IMPLEMENTED; + } + + return check_sam_security(&auth_context->challenge, mem_ctx, + user_info, server_info); +} + +/* module initialisation */ +static NTSTATUS auth_init_sam_netlogon3( + struct auth_context *auth_context, + const char *param, + struct auth_methods **auth_method) +{ + struct auth_methods *result; + + if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC + && !lp_parm_bool(-1, "server role check", "inhibit", false)) { + DEBUG(0, ("server role = 'active directory domain controller' " + "not compatible with running the auth_sam module.\n")); + DEBUGADD(0, ("You should not set 'auth methods' when " + "running the AD DC.\n")); + exit(1); + } + + result = talloc_zero(auth_context, struct auth_methods); + if (result == NULL) { + return NT_STATUS_NO_MEMORY; + } + result->auth = auth_sam_netlogon3_auth; + result->name = "sam_netlogon3"; + *auth_method = result; + return NT_STATUS_OK; +} + +NTSTATUS auth_sam_init(TALLOC_CTX *mem_ctx) +{ + smb_register_auth(AUTH_INTERFACE_VERSION, "sam", auth_init_sam); + smb_register_auth(AUTH_INTERFACE_VERSION, "sam_ignoredomain", auth_init_sam_ignoredomain); + smb_register_auth(AUTH_INTERFACE_VERSION, "sam_netlogon3", auth_init_sam_netlogon3); + return NT_STATUS_OK; +} diff --git a/source3/auth/auth_samba4.c b/source3/auth/auth_samba4.c new file mode 100644 index 0000000..6c017ef --- /dev/null +++ b/source3/auth/auth_samba4.c @@ -0,0 +1,398 @@ +/* + Unix SMB/CIFS implementation. + Authenticate against Samba4's auth subsystem + Copyright (C) Volker Lendecke 2008 + Copyright (C) Andrew Bartlett 2010 + + 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" +#include "source3/include/auth.h" +#include "source3/include/messages.h" +#include "source4/auth/auth.h" +#include "auth/auth_sam_reply.h" +#include "param/param.h" +#include "source4/lib/events/events.h" +#include "source4/lib/messaging/messaging.h" +#include "auth/gensec/gensec.h" +#include "auth/credentials/credentials.h" +#include "lib/global_contexts.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_AUTH + +static NTSTATUS make_auth4_context_s4(const struct auth_context *auth_context, + TALLOC_CTX *mem_ctx, + struct auth4_context **auth4_context); + +static struct idr_context *task_id_tree; + +static int free_task_id(struct server_id *server_id) +{ + idr_remove(task_id_tree, server_id->task_id); + return 0; +} + +/* Return a server_id with a unique task_id element. Free the + * returned pointer to de-allocate the task_id via a talloc destructor + * (ie, use talloc_free()) */ +static struct server_id *new_server_id_task(TALLOC_CTX *mem_ctx) +{ + struct messaging_context *msg_ctx; + struct server_id *server_id; + int task_id; + if (!task_id_tree) { + task_id_tree = idr_init(NULL); + if (!task_id_tree) { + return NULL; + } + } + + msg_ctx = global_messaging_context(); + if (msg_ctx == NULL) { + return NULL; + } + + server_id = talloc(mem_ctx, struct server_id); + + if (!server_id) { + return NULL; + } + *server_id = messaging_server_id(msg_ctx); + + /* 0 is the default server_id, so we need to start with 1 */ + task_id = idr_get_new_above(task_id_tree, server_id, 1, INT32_MAX); + + if (task_id == -1) { + talloc_free(server_id); + return NULL; + } + + talloc_set_destructor(server_id, free_task_id); + server_id->task_id = task_id; + return server_id; +} + +/* + * This module is not an ordinary authentication module. It is really + * a way to redirect the whole authentication and authorization stack + * to use the source4 auth code, not a way to just handle NTLM + * authentication. + * + * See the comments above each function for how that hook changes the + * behaviour. + */ + +/* + * This hook is currently used by winbindd only, as all other NTLM + * logins go via the hooks provided by make_auth4_context_s4() below. + * + * This is only left in case we find a way that it might become useful + * in future. Importantly, this routine returns the information + * needed for a NETLOGON SamLogon, not what is needed to establish a + * session. + * + * We expect we may use this hook in the source3/ winbind when this + * services the AD DC. It is tested via pdbtest. + */ + +static NTSTATUS check_samba4_security( + const struct auth_context *auth_context, + void *my_private_data, + TALLOC_CTX *mem_ctx, + const struct auth_usersupplied_info *user_info, + struct auth_serversupplied_info **pserver_info) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct netr_SamInfo3 *info3 = NULL; + NTSTATUS nt_status; + struct auth_user_info_dc *user_info_dc; + struct auth4_context *auth4_context; + uint8_t authoritative = 1; + struct auth_serversupplied_info *server_info = NULL; + + nt_status = make_auth4_context_s4(auth_context, mem_ctx, &auth4_context); + if (!NT_STATUS_IS_OK(nt_status)) { + TALLOC_FREE(frame); + goto done; + } + + nt_status = auth_context_set_challenge(auth4_context, auth_context->challenge.data, "auth_samba4"); + if (!NT_STATUS_IS_OK(nt_status)) { + TALLOC_FREE(auth4_context); + TALLOC_FREE(frame); + return nt_status; + } + + nt_status = auth_check_password(auth4_context, auth4_context, user_info, + &user_info_dc, &authoritative); + if (!NT_STATUS_IS_OK(nt_status)) { + if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER) && + authoritative == 0) + { + nt_status = NT_STATUS_NOT_IMPLEMENTED; + } + TALLOC_FREE(auth4_context); + TALLOC_FREE(frame); + return nt_status; + } + + nt_status = auth_convert_user_info_dc_saminfo3(mem_ctx, + user_info_dc, + &info3); + if (NT_STATUS_IS_OK(nt_status)) { + /* We need the strings from the server_info to be valid as long as the info3 is around */ + talloc_steal(info3, user_info_dc); + } + talloc_free(auth4_context); + + if (!NT_STATUS_IS_OK(nt_status)) { + goto done; + } + + if (user_info->flags & USER_INFO_INFO3_AND_NO_AUTHZ) { + server_info = make_server_info(mem_ctx); + if (server_info == NULL) { + nt_status = NT_STATUS_NO_MEMORY; + goto done; + } + server_info->info3 = talloc_move(server_info, &info3); + } else { + nt_status = make_server_info_info3( + mem_ctx, + user_info->client.account_name, + user_info->mapped.domain_name, + &server_info, + info3); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(10, ("make_server_info_info3 failed: %s\n", + nt_errstr(nt_status))); + goto done; + } + } + + *pserver_info = server_info; + nt_status = NT_STATUS_OK; + + done: + TALLOC_FREE(frame); + return nt_status; +} + +/* + * Hook to allow the source4 set of GENSEC modules to handle + * blob-based authentication mechanisms, without directly linking the + * mechanism code. + * + * This may eventually go away, when the GSSAPI acceptors are merged, + * when we will just rely on the make_auth4_context_s4 hook instead. + * + * Even for NTLMSSP, which has a common module, significant parts of + * the behaviour are overridden here, because it uses the source4 NTLM + * stack and the source4 mapping between the PAC/SamLogon response and + * the local token. + * + * It is important to override all this to ensure that the exact same + * token is generated and used in the SMB and LDAP servers, for NTLM + * and for Kerberos. + */ +static NTSTATUS prepare_gensec(const struct auth_context *auth_context, + TALLOC_CTX *mem_ctx, + struct gensec_security **gensec_context) +{ + NTSTATUS status; + struct loadparm_context *lp_ctx; + struct tevent_context *event_ctx; + TALLOC_CTX *frame = talloc_stackframe(); + struct gensec_security *gensec_ctx; + struct imessaging_context *msg_ctx; + struct cli_credentials *server_credentials; + struct server_id *server_id; + + lp_ctx = loadparm_init_s3(frame, loadparm_s3_helpers()); + if (lp_ctx == NULL) { + DEBUG(1, ("loadparm_init_s3 failed\n")); + TALLOC_FREE(frame); + return NT_STATUS_INVALID_SERVER_STATE; + } + event_ctx = s4_event_context_init(frame); + if (event_ctx == NULL) { + DEBUG(1, ("s4_event_context_init failed\n")); + TALLOC_FREE(frame); + return NT_STATUS_INVALID_SERVER_STATE; + } + + server_id = new_server_id_task(frame); + if (server_id == NULL) { + DEBUG(1, ("new_server_id_task failed\n")); + TALLOC_FREE(frame); + return NT_STATUS_INVALID_SERVER_STATE; + } + + msg_ctx = imessaging_init_discard_incoming(frame, + lp_ctx, + *server_id, + event_ctx); + if (msg_ctx == NULL) { + DEBUG(1, ("imessaging_init_discard_incoming failed\n")); + TALLOC_FREE(frame); + return NT_STATUS_INVALID_SERVER_STATE; + } + + talloc_reparent(frame, msg_ctx, server_id); + + server_credentials + = cli_credentials_init_server(frame, lp_ctx); + if (!server_credentials) { + DEBUG(1, ("Failed to init server credentials")); + TALLOC_FREE(frame); + return NT_STATUS_INVALID_SERVER_STATE; + } + + status = samba_server_gensec_start(mem_ctx, + event_ctx, msg_ctx, + lp_ctx, server_credentials, "cifs", + &gensec_ctx); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start GENSEC server code: %s\n", nt_errstr(status))); + TALLOC_FREE(frame); + return status; + } + + talloc_reparent(frame, gensec_ctx, msg_ctx); + talloc_reparent(frame, gensec_ctx, event_ctx); + talloc_reparent(frame, gensec_ctx, lp_ctx); + talloc_reparent(frame, gensec_ctx, server_credentials); + + gensec_want_feature(gensec_ctx, GENSEC_FEATURE_SESSION_KEY); + gensec_want_feature(gensec_ctx, GENSEC_FEATURE_UNIX_TOKEN); + + *gensec_context = gensec_ctx; + TALLOC_FREE(frame); + return status; +} + +/* + * Hook to allow handling of NTLM authentication for AD operation + * without directly linking the s4 auth stack + * + * This ensures we use the source4 authentication stack, as well as + * the authorization stack to create the user's token. This ensures + * consistency between NTLM logins and NTLMSSP logins, as NTLMSSP is + * handled by the hook above. + */ +static NTSTATUS make_auth4_context_s4(const struct auth_context *auth_context, + TALLOC_CTX *mem_ctx, + struct auth4_context **auth4_context) +{ + NTSTATUS status; + struct loadparm_context *lp_ctx; + struct tevent_context *event_ctx; + TALLOC_CTX *frame = talloc_stackframe(); + struct imessaging_context *msg_ctx; + struct server_id *server_id; + + lp_ctx = loadparm_init_s3(frame, loadparm_s3_helpers()); + if (lp_ctx == NULL) { + DEBUG(1, ("loadparm_init_s3 failed\n")); + TALLOC_FREE(frame); + return NT_STATUS_INVALID_SERVER_STATE; + } + event_ctx = s4_event_context_init(frame); + if (event_ctx == NULL) { + DEBUG(1, ("s4_event_context_init failed\n")); + TALLOC_FREE(frame); + return NT_STATUS_INVALID_SERVER_STATE; + } + + server_id = new_server_id_task(frame); + if (server_id == NULL) { + DEBUG(1, ("new_server_id_task failed\n")); + TALLOC_FREE(frame); + return NT_STATUS_INVALID_SERVER_STATE; + } + + msg_ctx = imessaging_init_discard_incoming(frame, + lp_ctx, + *server_id, + event_ctx); + if (msg_ctx == NULL) { + DEBUG(1, ("imessaging_init_discard_incoming failed\n")); + TALLOC_FREE(frame); + return NT_STATUS_INVALID_SERVER_STATE; + } + talloc_reparent(frame, msg_ctx, server_id); + + /* Allow forcing a specific auth4 module */ + if (!auth_context->forced_samba4_methods) { + status = auth_context_create(mem_ctx, + event_ctx, + msg_ctx, + lp_ctx, + auth4_context); + } else { + const char * const *forced_auth_methods = (const char * const *)str_list_make(mem_ctx, auth_context->forced_samba4_methods, NULL); + status = auth_context_create_methods(mem_ctx, forced_auth_methods, event_ctx, msg_ctx, lp_ctx, NULL, auth4_context); + } + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start auth server code: %s\n", nt_errstr(status))); + TALLOC_FREE(frame); + return status; + } + + talloc_reparent(frame, *auth4_context, msg_ctx); + talloc_reparent(frame, *auth4_context, event_ctx); + talloc_reparent(frame, *auth4_context, lp_ctx); + + TALLOC_FREE(frame); + return status; +} + +/* module initialisation */ +static NTSTATUS auth_init_samba4(struct auth_context *auth_context, + const char *param, + struct auth_methods **auth_method) +{ + struct auth_methods *result; + + gensec_init(); + + result = talloc_zero(auth_context, struct auth_methods); + if (result == NULL) { + return NT_STATUS_NO_MEMORY; + } + result->name = "samba4"; + result->auth = check_samba4_security; + result->prepare_gensec = prepare_gensec; + result->make_auth4_context = make_auth4_context_s4; + + if (param && *param) { + auth_context->forced_samba4_methods = talloc_strdup(result, param); + if (!auth_context->forced_samba4_methods) { + return NT_STATUS_NO_MEMORY; + } + } + + *auth_method = result; + return NT_STATUS_OK; +} + +NTSTATUS auth_samba4_init(TALLOC_CTX *mem_ctx); +NTSTATUS auth_samba4_init(TALLOC_CTX *mem_ctx) +{ + smb_register_auth(AUTH_INTERFACE_VERSION, "samba4", + auth_init_samba4); + return NT_STATUS_OK; +} diff --git a/source3/auth/auth_unix.c b/source3/auth/auth_unix.c new file mode 100644 index 0000000..eaf344d --- /dev/null +++ b/source3/auth/auth_unix.c @@ -0,0 +1,107 @@ +/* + 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" +#include "auth.h" +#include "system/passwd.h" +#include "../lib/tsocket/tsocket.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_AUTH + +/** Check a plaintext username/password + * + * Cannot deal with an encrypted 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 struct auth_usersupplied_info *user_info, + struct auth_serversupplied_info **server_info) +{ + NTSTATUS nt_status; + struct passwd *pass = NULL; + const char *rhost; + + DEBUG(10, ("Check auth for: [%s]\n", user_info->mapped.account_name)); + + if (tsocket_address_is_inet(user_info->remote_host, "ip")) { + rhost = tsocket_address_inet_addr_string(user_info->remote_host, + talloc_tos()); + if (rhost == NULL) { + return NT_STATUS_NO_MEMORY; + } + } else { + rhost = "127.0.0.1"; + } + + become_root(); + pass = Get_Pwnam_alloc(talloc_tos(), user_info->mapped.account_name); + + /** @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->mapped.account_name, + rhost, + user_info->password.plaintext, + true); + + unbecome_root(); + + if (NT_STATUS_IS_OK(nt_status)) { + if (pass != NULL) { + nt_status = make_server_info_pw(mem_ctx, + pass->pw_name, + pass, + server_info); + } else { + /* we need to do something 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, + struct auth_methods **auth_method) +{ + struct auth_methods *result; + + result = talloc_zero(auth_context, struct auth_methods); + if (result == NULL) { + return NT_STATUS_NO_MEMORY; + } + result->name = "unix"; + result->auth = check_unix_security; + + *auth_method = result; + return NT_STATUS_OK; +} + +NTSTATUS auth_unix_init(TALLOC_CTX *mem_ctx) +{ + 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 0000000..b60dd26 --- /dev/null +++ b/source3/auth/auth_util.c @@ -0,0 +1,2350 @@ +/* + Unix SMB/CIFS implementation. + Authentication utility functions + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Andrew Bartlett 2001-2011 + Copyright (C) Jeremy Allison 2000-2001 + Copyright (C) Rafal Szczesniak 2002 + Copyright (C) Volker Lendecke 2006-2008 + + 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" +#include "auth.h" +#include "lib/util_unixsids.h" +#include "../libcli/auth/libcli_auth.h" +#include "rpc_client/init_lsa.h" +#include "../libcli/security/security.h" +#include "../lib/util/util_pw.h" +#include "lib/winbind_util.h" +#include "passdb.h" +#include "../librpc/gen_ndr/ndr_auth.h" +#include "../auth/auth_sam_reply.h" +#include "../librpc/gen_ndr/idmap.h" +#include "lib/param/loadparm.h" +#include "../lib/tsocket/tsocket.h" +#include "rpc_client/util_netlogon.h" +#include "source4/auth/auth.h" +#include "auth/auth_util.h" +#include "source3/lib/substitute.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_AUTH + +/**************************************************************************** + 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(); + const struct loadparm_substitution *lp_sub = + loadparm_s3_global_substitution(); + char *add_script; + int ret; + + add_script = lp_add_user_script(ctx, lp_sub); + 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, 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 after appropriate mapping. +****************************************************************************/ + +NTSTATUS make_user_info_map(TALLOC_CTX *mem_ctx, + struct auth_usersupplied_info **user_info, + const char *smb_name, + const char *client_domain, + const char *workstation_name, + const struct tsocket_address *remote_address, + const struct tsocket_address *local_address, + const char *service_description, + const DATA_BLOB *lm_pwd, + const DATA_BLOB *nt_pwd, + const struct samr_Password *lm_interactive_pwd, + const struct samr_Password *nt_interactive_pwd, + const char *plaintext, + enum auth_password_state password_state) +{ + const char *domain; + NTSTATUS result; + bool was_mapped; + char *internal_username = NULL; + + was_mapped = map_username(talloc_tos(), smb_name, &internal_username); + if (!internal_username) { + return NT_STATUS_NO_MEMORY; + } + + DEBUG(5, ("Mapping user [%s]\\[%s] from workstation [%s]\n", + client_domain, smb_name, workstation_name)); + + /* + * We let the auth stack canonicalize, username + * and domain. + */ + domain = client_domain; + + result = make_user_info(mem_ctx, user_info, smb_name, internal_username, + client_domain, domain, workstation_name, + remote_address, local_address, + service_description, lm_pwd, nt_pwd, + lm_interactive_pwd, nt_interactive_pwd, + plaintext, password_state); + if (NT_STATUS_IS_OK(result)) { + /* did we actually map the user to a different name? */ + (*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(TALLOC_CTX *mem_ctx, + struct auth_usersupplied_info **user_info, + const char *smb_name, + const char *client_domain, + const char *workstation_name, + const struct tsocket_address *remote_address, + const struct tsocket_address *local_address, + uint32_t 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(mem_ctx, user_info, + smb_name, client_domain, + workstation_name, + remote_address, + local_address, + "SamLogon", + lm_pwd_len ? &lm_blob : NULL, + nt_pwd_len ? &nt_blob : NULL, + NULL, NULL, NULL, + AUTH_PASSWORD_RESPONSE); + + 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(TALLOC_CTX *mem_ctx, + struct auth_usersupplied_info **user_info, + const char *smb_name, + const char *client_domain, + const char *workstation_name, + const struct tsocket_address *remote_address, + const struct tsocket_address *local_address, + uint32_t logon_parameters, + const uchar chal[8], + const uchar lm_interactive_pwd[16], + const uchar nt_interactive_pwd[16]) +{ + struct samr_Password lm_pwd; + struct samr_Password nt_pwd; + unsigned char local_lm_response[24]; + unsigned char local_nt_response[24]; + int rc; + + if (lm_interactive_pwd) + memcpy(lm_pwd.hash, lm_interactive_pwd, sizeof(lm_pwd.hash)); + + if (nt_interactive_pwd) + memcpy(nt_pwd.hash, nt_interactive_pwd, sizeof(nt_pwd.hash)); + + if (lm_interactive_pwd) { + rc = SMBOWFencrypt(lm_pwd.hash, chal, + local_lm_response); + if (rc != 0) { + return false; + } + } + + if (nt_interactive_pwd) { + rc = SMBOWFencrypt(nt_pwd.hash, chal, + local_nt_response); + if (rc != 0) { + return false; + } + } + + { + bool ret; + NTSTATUS nt_status; + DATA_BLOB local_lm_blob = data_blob_null; + DATA_BLOB local_nt_blob = data_blob_null; + + if (lm_interactive_pwd) { + local_lm_blob = data_blob(local_lm_response, + sizeof(local_lm_response)); + } + + if (nt_interactive_pwd) { + local_nt_blob = data_blob(local_nt_response, + sizeof(local_nt_response)); + } + + nt_status = make_user_info_map( + mem_ctx, + user_info, + smb_name, client_domain, workstation_name, + remote_address, + local_address, + "SamLogon", + lm_interactive_pwd ? &local_lm_blob : NULL, + nt_interactive_pwd ? &local_nt_blob : NULL, + lm_interactive_pwd ? &lm_pwd : NULL, + nt_interactive_pwd ? &nt_pwd : NULL, + NULL, AUTH_PASSWORD_HASH); + + if (NT_STATUS_IS_OK(nt_status)) { + (*user_info)->logon_parameters = logon_parameters; + (*user_info)->flags |= USER_INFO_INTERACTIVE_LOGON; + } + + ret = NT_STATUS_IS_OK(nt_status) ? true : false; + data_blob_free(&local_lm_blob); + data_blob_free(&local_nt_blob); + return ret; + } +} + + +/**************************************************************************** + Create an auth_usersupplied_data structure +****************************************************************************/ + +bool make_user_info_for_reply(TALLOC_CTX *mem_ctx, + struct auth_usersupplied_info **user_info, + const char *smb_name, + const char *client_domain, + const struct tsocket_address *remote_address, + const struct tsocket_address *local_address, + const char *service_description, + const uint8_t chal[8], + DATA_BLOB plaintext_password) +{ + + DATA_BLOB local_lm_blob; + DATA_BLOB local_nt_blob; + NTSTATUS ret; + char *plaintext_password_string; + /* + * Not encrypted - do so. + */ + + DEBUG(5,("make_user_info_for_reply: User passwords not in encrypted " + "format.\n")); + if (plaintext_password.data && plaintext_password.length) { + 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; + } + + plaintext_password_string = talloc_strndup(talloc_tos(), + (const char *)plaintext_password.data, + plaintext_password.length); + if (!plaintext_password_string) { + return false; + } + + ret = make_user_info(mem_ctx, + user_info, smb_name, smb_name, client_domain, client_domain, + get_remote_machine_name(), + remote_address, + local_address, + service_description, + local_lm_blob.data ? &local_lm_blob : NULL, + local_nt_blob.data ? &local_nt_blob : NULL, + NULL, NULL, + plaintext_password_string, + AUTH_PASSWORD_PLAIN); + + if (plaintext_password_string) { + memset(plaintext_password_string, '\0', strlen(plaintext_password_string)); + talloc_free(plaintext_password_string); + } + + 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(TALLOC_CTX *mem_ctx, + struct auth_usersupplied_info **user_info, + const char *smb_name, + const char *client_domain, + const struct tsocket_address *remote_address, + const struct tsocket_address *local_address, + const char *service_description, + DATA_BLOB lm_resp, DATA_BLOB nt_resp) +{ + bool allow_raw = lp_raw_ntlmv2_auth(); + + if (!allow_raw && nt_resp.length >= 48) { + /* + * NTLMv2_RESPONSE has at least 48 bytes + * and should only be supported via NTLMSSP. + */ + DEBUG(2,("Rejecting raw NTLMv2 authentication with " + "user [%s\\%s] from[%s]\n", + client_domain, smb_name, + tsocket_address_string(remote_address, mem_ctx))); + return NT_STATUS_INVALID_PARAMETER; + } + + return make_user_info(mem_ctx, + user_info, smb_name, smb_name, + client_domain, client_domain, + get_remote_machine_name(), + remote_address, + local_address, + service_description, + lm_resp.data && (lm_resp.length > 0) ? &lm_resp : NULL, + nt_resp.data && (nt_resp.length > 0) ? &nt_resp : NULL, + NULL, NULL, NULL, + AUTH_PASSWORD_RESPONSE); +} + +/**************************************************************************** + Create a guest user_info blob, for anonymous authentication. +****************************************************************************/ + +bool make_user_info_guest(TALLOC_CTX *mem_ctx, + const struct tsocket_address *remote_address, + const struct tsocket_address *local_address, + const char *service_description, + struct auth_usersupplied_info **user_info) +{ + NTSTATUS nt_status; + + nt_status = make_user_info(mem_ctx, + user_info, + "","", + "","", + "", + remote_address, + local_address, + service_description, + NULL, NULL, + NULL, NULL, + NULL, + AUTH_PASSWORD_RESPONSE); + + return NT_STATUS_IS_OK(nt_status) ? true : false; +} + +static NTSTATUS log_nt_token(struct security_token *token) +{ + TALLOC_CTX *frame = talloc_stackframe(); + const struct loadparm_substitution *lp_sub = + loadparm_s3_global_substitution(); + char *command; + char *group_sidstr; + struct dom_sid_buf buf; + size_t i; + + if ((lp_log_nt_token_command(frame, lp_sub) == NULL) || + (strlen(lp_log_nt_token_command(frame, lp_sub)) == 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, + dom_sid_str_buf(&token->sids[i], &buf)); + } + + command = talloc_string_sub( + frame, lp_log_nt_token_command(frame, lp_sub), + "%s", dom_sid_str_buf(&token->sids[0], &buf)); + 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, 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->info3 and + * server_info->sids (the info3/sam groups). Find the unix gids. + */ + +NTSTATUS create_local_token(TALLOC_CTX *mem_ctx, + const struct auth_serversupplied_info *server_info, + DATA_BLOB *session_key, + const char *smb_username, /* for ->sanitized_username, for %U subs */ + struct auth_session_info **session_info_out) +{ + struct security_token *t; + NTSTATUS status; + size_t i; + struct dom_sid tmp_sid; + struct auth_session_info *session_info = NULL; + struct unixid *ids; + + /* Ensure we can't possible take a code path leading to a + * null deref. */ + if (!server_info) { + return NT_STATUS_LOGON_FAILURE; + } + + if (!is_allowed_domain(server_info->info3->base.logon_domain.string)) { + DBG_NOTICE("Authentication failed for user [%s] " + "from firewalled domain [%s]\n", + server_info->info3->base.account_name.string, + server_info->info3->base.logon_domain.string); + return NT_STATUS_AUTHENTICATION_FIREWALL_FAILED; + } + + if (server_info->cached_session_info != NULL) { + session_info = copy_session_info(mem_ctx, + server_info->cached_session_info); + if (session_info == NULL) { + goto nomem; + } + + /* This is a potentially untrusted username for use in %U */ + session_info->unix_info->sanitized_username = + talloc_alpha_strcpy(session_info->unix_info, + smb_username, + SAFE_NETBIOS_CHARS "$"); + if (session_info->unix_info->sanitized_username == NULL) { + goto nomem; + } + + session_info->unique_session_token = GUID_random(); + + *session_info_out = session_info; + return NT_STATUS_OK; + } + + session_info = talloc_zero(mem_ctx, struct auth_session_info); + if (!session_info) { + goto nomem; + } + + session_info->unix_token = talloc_zero(session_info, struct security_unix_token); + if (!session_info->unix_token) { + goto nomem; + } + + session_info->unix_token->uid = server_info->utok.uid; + session_info->unix_token->gid = server_info->utok.gid; + + session_info->unix_info = talloc_zero(session_info, struct auth_user_info_unix); + if (!session_info->unix_info) { + goto nomem; + } + + session_info->unix_info->unix_name = talloc_strdup(session_info, server_info->unix_name); + if (!session_info->unix_info->unix_name) { + goto nomem; + } + + /* This is a potentially untrusted username for use in %U */ + session_info->unix_info->sanitized_username = + talloc_alpha_strcpy(session_info->unix_info, + smb_username, + SAFE_NETBIOS_CHARS "$"); + if (session_info->unix_info->sanitized_username == NULL) { + goto nomem; + } + + if (session_key) { + data_blob_free(&session_info->session_key); + session_info->session_key = data_blob_talloc(session_info, + session_key->data, + session_key->length); + if (!session_info->session_key.data && session_key->length) { + goto nomem; + } + } else { + session_info->session_key = data_blob_talloc( session_info, server_info->session_key.data, + server_info->session_key.length); + } + + /* We need to populate session_info->info with the information found in server_info->info3 */ + status = make_user_info_SamBaseInfo(session_info, "", &server_info->info3->base, + server_info->guest == false, + &session_info->info); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("conversion of info3 into auth_user_info failed!\n")); + goto fail; + } + + /* + * If the user name was mapped to some local unix user, + * we can not make much use of the SIDs the + * domain controller provided us with. + */ + if (server_info->nss_token) { + char *found_username = NULL; + status = create_token_from_username(session_info, + server_info->unix_name, + server_info->guest, + &session_info->unix_token->uid, + &session_info->unix_token->gid, + &found_username, + &session_info->security_token); + if (NT_STATUS_IS_OK(status)) { + session_info->unix_info->unix_name = found_username; + } + } else { + status = create_local_nt_token_from_info3(session_info, + server_info->guest, + server_info->info3, + &server_info->extra, + &session_info->security_token); + } + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* Convert the SIDs to gids. */ + + session_info->unix_token->ngroups = 0; + session_info->unix_token->groups = NULL; + + t = session_info->security_token; + + ids = talloc_array(talloc_tos(), struct unixid, + t->num_sids); + if (ids == NULL) { + goto nomem; + } + + if (!sids_to_unixids(t->sids, t->num_sids, ids)) { + goto nomem; + } + + for (i=0; i<t->num_sids; i++) { + + if (i == 0 && ids[i].type != ID_TYPE_BOTH) { + continue; + } + + if (ids[i].type != ID_TYPE_GID && + ids[i].type != ID_TYPE_BOTH) { + struct dom_sid_buf buf; + DEBUG(10, ("Could not convert SID %s to gid, " + "ignoring it\n", + dom_sid_str_buf(&t->sids[i], &buf))); + continue; + } + if (!add_gid_to_array_unique(session_info->unix_token, + ids[i].id, + &session_info->unix_token->groups, + &session_info->unix_token->ngroups)) { + goto nomem; + } + } + + /* + * 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 + * + * For consistency we also add the "Unix User" SID, + * so that the complete unix token is represented within + * the nt token. + */ + + uid_to_unix_users_sid(session_info->unix_token->uid, &tmp_sid); + status = add_sid_to_array_unique( + session_info->security_token, + &tmp_sid, + &session_info->security_token->sids, + &session_info->security_token->num_sids); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + gid_to_unix_groups_sid(session_info->unix_token->gid, &tmp_sid); + status = add_sid_to_array_unique( + session_info->security_token, + &tmp_sid, + &session_info->security_token->sids, + &session_info->security_token->num_sids); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + for ( i=0; i<session_info->unix_token->ngroups; i++ ) { + gid_to_unix_groups_sid(session_info->unix_token->groups[i], &tmp_sid); + status = add_sid_to_array_unique( + session_info->security_token, + &tmp_sid, + &session_info->security_token->sids, + &session_info->security_token->num_sids); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + } + + security_token_debug(DBGC_AUTH, 10, session_info->security_token); + debug_unix_user_token(DBGC_AUTH, 10, + session_info->unix_token->uid, + session_info->unix_token->gid, + session_info->unix_token->ngroups, + session_info->unix_token->groups); + + status = log_nt_token(session_info->security_token); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + session_info->unique_session_token = GUID_random(); + + *session_info_out = session_info; + return NT_STATUS_OK; +nomem: + status = NT_STATUS_NO_MEMORY; +fail: + TALLOC_FREE(session_info); + return status; +} + +NTSTATUS auth3_user_info_dc_add_hints(struct auth_user_info_dc *user_info_dc, + uid_t uid, + gid_t gid, + uint32_t flags) +{ + uint32_t orig_num_sids = user_info_dc->num_sids; + struct dom_sid tmp_sid = { 0, }; + NTSTATUS status; + + /* + * We add S-5-88-1-X in order to pass the uid + * for the unix token. + */ + sid_compose(&tmp_sid, + &global_sid_Unix_NFS_Users, + (uint32_t)uid); + status = add_sid_to_array_unique(user_info_dc->sids, + &tmp_sid, + &user_info_dc->sids, + &user_info_dc->num_sids); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("add_sid_to_array_unique failed: %s\n", + nt_errstr(status))); + goto fail; + } + + /* + * We add S-5-88-2-X in order to pass the gid + * for the unix token. + */ + sid_compose(&tmp_sid, + &global_sid_Unix_NFS_Groups, + (uint32_t)gid); + status = add_sid_to_array_unique(user_info_dc->sids, + &tmp_sid, + &user_info_dc->sids, + &user_info_dc->num_sids); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("add_sid_to_array_unique failed: %s\n", + nt_errstr(status))); + goto fail; + } + + /* + * We add S-5-88-3-X in order to pass some flags + * (AUTH3_UNIX_HINT_*) to auth3_create_session_info(). + */ + sid_compose(&tmp_sid, + &global_sid_Unix_NFS_Mode, + flags); + status = add_sid_to_array_unique(user_info_dc->sids, + &tmp_sid, + &user_info_dc->sids, + &user_info_dc->num_sids); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("add_sid_to_array_unique failed: %s\n", + nt_errstr(status))); + goto fail; + } + + return NT_STATUS_OK; + +fail: + user_info_dc->num_sids = orig_num_sids; + return status; +} + +static NTSTATUS auth3_session_info_create( + TALLOC_CTX *mem_ctx, + const struct auth_user_info_dc *user_info_dc, + const char *original_user_name, + uint32_t session_info_flags, + struct auth_session_info **session_info_out) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct auth_session_info *session_info = NULL; + uid_t hint_uid = -1; + bool found_hint_uid = false; + uid_t hint_gid = -1; + bool found_hint_gid = false; + uint32_t hint_flags = 0; + bool found_hint_flags = false; + bool need_getpwuid = false; + struct unixid *ids = NULL; + uint32_t num_gids = 0; + gid_t *gids = NULL; + struct dom_sid tmp_sid = { 0, }; + NTSTATUS status; + size_t i; + bool ok; + + *session_info_out = NULL; + + if (user_info_dc->num_sids == 0) { + TALLOC_FREE(frame); + return NT_STATUS_INVALID_TOKEN; + } + + if (user_info_dc->info == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_INVALID_TOKEN; + } + + if (user_info_dc->info->account_name == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_INVALID_TOKEN; + } + + session_info = talloc_zero(mem_ctx, struct auth_session_info); + if (session_info == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + /* keep this under frame for easier cleanup */ + talloc_reparent(mem_ctx, frame, session_info); + + session_info->info = auth_user_info_copy(session_info, + user_info_dc->info); + if (session_info->info == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + + session_info->security_token = talloc_zero(session_info, + struct security_token); + if (session_info->security_token == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + + /* + * Avoid a lot of reallocations and allocate what we'll + * use in most cases. + */ + session_info->security_token->sids = talloc_zero_array( + session_info->security_token, + struct dom_sid, + user_info_dc->num_sids); + if (session_info->security_token->sids == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + + for (i = PRIMARY_USER_SID_INDEX; i < user_info_dc->num_sids; i++) { + struct security_token *nt_token = session_info->security_token; + int cmp; + + /* + * S-1-5-88-X-Y sids are only used to give hints + * to the unix token construction. + * + * S-1-5-88-1-Y gives the uid=Y + * S-1-5-88-2-Y gives the gid=Y + * S-1-5-88-3-Y gives flags=Y: AUTH3_UNIX_HINT_* + */ + cmp = dom_sid_compare_domain(&global_sid_Unix_NFS, + &user_info_dc->sids[i]); + if (cmp == 0) { + bool match; + uint32_t hint = 0; + + match = sid_peek_rid(&user_info_dc->sids[i], &hint); + if (!match) { + continue; + } + + match = dom_sid_in_domain(&global_sid_Unix_NFS_Users, + &user_info_dc->sids[i]); + if (match) { + if (found_hint_uid) { + TALLOC_FREE(frame); + return NT_STATUS_INVALID_TOKEN; + } + found_hint_uid = true; + hint_uid = (uid_t)hint; + continue; + } + + match = dom_sid_in_domain(&global_sid_Unix_NFS_Groups, + &user_info_dc->sids[i]); + if (match) { + if (found_hint_gid) { + TALLOC_FREE(frame); + return NT_STATUS_INVALID_TOKEN; + } + found_hint_gid = true; + hint_gid = (gid_t)hint; + continue; + } + + match = dom_sid_in_domain(&global_sid_Unix_NFS_Mode, + &user_info_dc->sids[i]); + if (match) { + if (found_hint_flags) { + TALLOC_FREE(frame); + return NT_STATUS_INVALID_TOKEN; + } + found_hint_flags = true; + hint_flags = hint; + continue; + } + + continue; + } + + status = add_sid_to_array_unique(nt_token->sids, + &user_info_dc->sids[i], + &nt_token->sids, + &nt_token->num_sids); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); + return status; + } + } + + /* + * We need at least one usable SID + */ + if (session_info->security_token->num_sids == 0) { + TALLOC_FREE(frame); + return NT_STATUS_INVALID_TOKEN; + } + + /* + * We need all tree hints: uid, gid, flags + * or none of them. + */ + if (found_hint_uid || found_hint_gid || found_hint_flags) { + if (!found_hint_uid) { + TALLOC_FREE(frame); + return NT_STATUS_INVALID_TOKEN; + } + + if (!found_hint_gid) { + TALLOC_FREE(frame); + return NT_STATUS_INVALID_TOKEN; + } + + if (!found_hint_flags) { + TALLOC_FREE(frame); + return NT_STATUS_INVALID_TOKEN; + } + } + + if (session_info->info->authenticated) { + session_info_flags |= AUTH_SESSION_INFO_AUTHENTICATED; + } + + status = finalize_local_nt_token(session_info->security_token, + session_info_flags); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); + return status; + } + + /* + * unless set otherwise, the session key is the user session + * key from the auth subsystem + */ + if (user_info_dc->user_session_key.length != 0) { + session_info->session_key = data_blob_dup_talloc(session_info, + user_info_dc->user_session_key); + if (session_info->session_key.data == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + } + + if (!(session_info_flags & AUTH_SESSION_INFO_UNIX_TOKEN)) { + goto done; + } + + session_info->unix_token = talloc_zero(session_info, struct security_unix_token); + if (session_info->unix_token == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + session_info->unix_token->uid = -1; + session_info->unix_token->gid = -1; + + session_info->unix_info = talloc_zero(session_info, struct auth_user_info_unix); + if (session_info->unix_info == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + + /* Convert the SIDs to uid/gids. */ + + ids = talloc_zero_array(frame, struct unixid, + session_info->security_token->num_sids); + if (ids == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + + if (!(hint_flags & AUTH3_UNIX_HINT_DONT_TRANSLATE_FROM_SIDS)) { + ok = sids_to_unixids(session_info->security_token->sids, + session_info->security_token->num_sids, + ids); + if (!ok) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + } + + if (found_hint_uid) { + session_info->unix_token->uid = hint_uid; + } else if (ids[0].type == ID_TYPE_UID) { + /* + * The primary SID resolves to a UID only. + */ + session_info->unix_token->uid = ids[0].id; + } else if (ids[0].type == ID_TYPE_BOTH) { + /* + * The primary SID resolves to a UID and GID, + * use it as uid and add it as first element + * to the groups array. + */ + session_info->unix_token->uid = ids[0].id; + + ok = add_gid_to_array_unique(session_info->unix_token, + session_info->unix_token->uid, + &session_info->unix_token->groups, + &session_info->unix_token->ngroups); + if (!ok) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + } else { + /* + * It we can't get a uid, we can't imporsonate + * the user. + */ + TALLOC_FREE(frame); + return NT_STATUS_INVALID_TOKEN; + } + + if (found_hint_gid) { + session_info->unix_token->gid = hint_gid; + } else { + need_getpwuid = true; + } + + if (hint_flags & AUTH3_UNIX_HINT_QUALIFIED_NAME) { + session_info->unix_info->unix_name = + talloc_asprintf(session_info->unix_info, + "%s%c%s", + session_info->info->domain_name, + *lp_winbind_separator(), + session_info->info->account_name); + if (session_info->unix_info->unix_name == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + } else if (hint_flags & AUTH3_UNIX_HINT_ISLOLATED_NAME) { + session_info->unix_info->unix_name = + talloc_strdup(session_info->unix_info, + session_info->info->account_name); + if (session_info->unix_info->unix_name == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + } else { + need_getpwuid = true; + } + + if (need_getpwuid) { + struct passwd *pwd = NULL; + + /* + * Ask the system for the primary gid + * and the real unix name. + */ + pwd = getpwuid_alloc(frame, session_info->unix_token->uid); + if (pwd == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_INVALID_TOKEN; + } + if (!found_hint_gid) { + session_info->unix_token->gid = pwd->pw_gid; + } + + session_info->unix_info->unix_name = + talloc_strdup(session_info->unix_info, pwd->pw_name); + if (session_info->unix_info->unix_name == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + + TALLOC_FREE(pwd); + } + + ok = add_gid_to_array_unique(session_info->unix_token, + session_info->unix_token->gid, + &session_info->unix_token->groups, + &session_info->unix_token->ngroups); + if (!ok) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + + /* This is a potentially untrusted username for use in %U */ + session_info->unix_info->sanitized_username = + talloc_alpha_strcpy(session_info->unix_info, + original_user_name, + SAFE_NETBIOS_CHARS "$"); + if (session_info->unix_info->sanitized_username == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + + for (i=0; i < session_info->security_token->num_sids; i++) { + + if (ids[i].type != ID_TYPE_GID && + ids[i].type != ID_TYPE_BOTH) { + struct security_token *nt_token = + session_info->security_token; + struct dom_sid_buf buf; + + DEBUG(10, ("Could not convert SID %s to gid, " + "ignoring it\n", + dom_sid_str_buf(&nt_token->sids[i], &buf))); + continue; + } + + ok = add_gid_to_array_unique(session_info->unix_token, + ids[i].id, + &session_info->unix_token->groups, + &session_info->unix_token->ngroups); + if (!ok) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + } + TALLOC_FREE(ids); + + /* + * Now we must get any groups this user has been + * added to in /etc/group and merge them in. + * This has to be done in every code path + * that creates an NT token, as remote users + * may have been added to the local /etc/group + * database. Tokens created merely from the + * info3 structs (via the DC or via the krb5 PAC) + * won't have these local groups. Note the + * groups added here will only be UNIX groups + * (S-1-22-2-XXXX groups) as getgroups_unix_user() + * turns off winbindd before calling getgroups(). + * + * NB. This is duplicating work already + * done in the 'unix_user:' case of + * create_token_from_sid() but won't + * do anything other than be inefficient + * in that case. + */ + if (!(hint_flags & AUTH3_UNIX_HINT_DONT_EXPAND_UNIX_GROUPS)) { + ok = getgroups_unix_user(frame, + session_info->unix_info->unix_name, + session_info->unix_token->gid, + &gids, &num_gids); + if (!ok) { + TALLOC_FREE(frame); + return NT_STATUS_INVALID_TOKEN; + } + } + + for (i=0; i < num_gids; i++) { + + ok = add_gid_to_array_unique(session_info->unix_token, + gids[i], + &session_info->unix_token->groups, + &session_info->unix_token->ngroups); + if (!ok) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + } + TALLOC_FREE(gids); + + if (hint_flags & AUTH3_UNIX_HINT_DONT_TRANSLATE_TO_SIDS) { + /* + * We should not translate the unix token uid/gids + * to S-1-22-X-Y SIDs. + */ + goto done; + } + + /* + * 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 + * + * For consistency we also add the "Unix User" SID, + * so that the complete unix token is represented within + * the nt token. + */ + + uid_to_unix_users_sid(session_info->unix_token->uid, &tmp_sid); + status = add_sid_to_array_unique(session_info->security_token, &tmp_sid, + &session_info->security_token->sids, + &session_info->security_token->num_sids); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); + return status; + } + + gid_to_unix_groups_sid(session_info->unix_token->gid, &tmp_sid); + status = add_sid_to_array_unique(session_info->security_token, &tmp_sid, + &session_info->security_token->sids, + &session_info->security_token->num_sids); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); + return status; + } + + for (i=0; i < session_info->unix_token->ngroups; i++ ) { + struct security_token *nt_token = session_info->security_token; + + gid_to_unix_groups_sid(session_info->unix_token->groups[i], + &tmp_sid); + status = add_sid_to_array_unique(nt_token->sids, + &tmp_sid, + &nt_token->sids, + &nt_token->num_sids); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); + return status; + } + } + +done: + security_token_debug(DBGC_AUTH, 10, session_info->security_token); + if (session_info->unix_token != NULL) { + debug_unix_user_token(DBGC_AUTH, 10, + session_info->unix_token->uid, + session_info->unix_token->gid, + session_info->unix_token->ngroups, + session_info->unix_token->groups); + } + + status = log_nt_token(session_info->security_token); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); + return status; + } + + session_info->unique_session_token = GUID_random(); + + *session_info_out = talloc_move(mem_ctx, &session_info); + TALLOC_FREE(frame); + return NT_STATUS_OK; +} + +/*************************************************************************** + Make (and fill) a server_info struct from a 'struct passwd' by conversion + to a struct samu +***************************************************************************/ + +NTSTATUS make_server_info_pw(TALLOC_CTX *mem_ctx, + const char *unix_username, + const struct passwd *pwd, + struct auth_serversupplied_info **server_info) +{ + NTSTATUS status; + TALLOC_CTX *tmp_ctx = NULL; + struct auth_serversupplied_info *result; + + tmp_ctx = talloc_stackframe(); + if (tmp_ctx == NULL) { + return NT_STATUS_NO_MEMORY; + } + + result = make_server_info(tmp_ctx); + if (result == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + status = passwd_to_SamInfo3(result, + unix_username, + pwd, + &result->info3, + &result->extra); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + result->unix_name = talloc_strdup(result, unix_username); + if (result->unix_name == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + result->utok.uid = pwd->pw_uid; + result->utok.gid = pwd->pw_gid; + + *server_info = talloc_move(mem_ctx, &result); + status = NT_STATUS_OK; +done: + talloc_free(tmp_ctx); + + return status; +} + +static NTSTATUS get_guest_info3(TALLOC_CTX *mem_ctx, + struct netr_SamInfo3 *info3) +{ + const char *guest_account = lp_guest_account(); + struct dom_sid domain_sid; + struct passwd *pwd; + const char *tmp; + + pwd = Get_Pwnam_alloc(mem_ctx, guest_account); + if (pwd == NULL) { + DEBUG(0,("SamInfo3_for_guest: Unable to locate guest " + "account [%s]!\n", guest_account)); + return NT_STATUS_NO_SUCH_USER; + } + + /* Set account name */ + tmp = talloc_strdup(mem_ctx, pwd->pw_name); + if (tmp == NULL) { + return NT_STATUS_NO_MEMORY; + } + init_lsa_String(&info3->base.account_name, tmp); + + /* Set domain name */ + tmp = talloc_strdup(mem_ctx, get_global_sam_name()); + if (tmp == NULL) { + return NT_STATUS_NO_MEMORY; + } + init_lsa_StringLarge(&info3->base.logon_domain, tmp); + + /* Domain sid */ + sid_copy(&domain_sid, get_global_sam_sid()); + + info3->base.domain_sid = dom_sid_dup(mem_ctx, &domain_sid); + if (info3->base.domain_sid == NULL) { + return NT_STATUS_NO_MEMORY; + } + + /* Guest rid */ + info3->base.rid = DOMAIN_RID_GUEST; + + /* Primary gid */ + info3->base.primary_gid = DOMAIN_RID_GUESTS; + + /* Set as guest */ + info3->base.user_flags = NETLOGON_GUEST; + + TALLOC_FREE(pwd); + 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. + + The resulting structure is a 'session_info' because + create_local_token() has already been called on it. This is quite + nasty, as the auth subsystem isn't expect this, but the behavior is + left as-is for now. +***************************************************************************/ + +static NTSTATUS make_new_session_info_guest(TALLOC_CTX *mem_ctx, + struct auth_session_info **_session_info, + struct auth_serversupplied_info **_server_info) +{ + struct auth_session_info *session_info = NULL; + struct auth_serversupplied_info *server_info = NULL; + const char *guest_account = lp_guest_account(); + const char *domain = lp_netbios_name(); + struct netr_SamInfo3 info3; + TALLOC_CTX *tmp_ctx; + NTSTATUS status; + + tmp_ctx = talloc_stackframe(); + if (tmp_ctx == NULL) { + return NT_STATUS_NO_MEMORY; + } + + ZERO_STRUCT(info3); + + status = get_guest_info3(tmp_ctx, &info3); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("get_guest_info3 failed with %s\n", + nt_errstr(status))); + goto done; + } + + status = make_server_info_info3(tmp_ctx, + guest_account, + domain, + &server_info, + &info3); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("make_server_info_info3 failed with %s\n", + nt_errstr(status))); + goto done; + } + + server_info->guest = true; + + /* This should not be done here (we should produce a server + * info, and later construct a session info from it), but for + * now this does not change the previous behavior */ + status = create_local_token(tmp_ctx, server_info, NULL, + server_info->info3->base.account_name.string, + &session_info); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("create_local_token failed: %s\n", + nt_errstr(status))); + goto done; + } + + /* + * It's ugly, but for now it's + * needed to force Builtin_Guests + * here, because memberships of + * Builtin_Guests might be incomplete. + */ + status = add_sid_to_array_unique(session_info->security_token, + &global_sid_Builtin_Guests, + &session_info->security_token->sids, + &session_info->security_token->num_sids); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("Failed to force Builtin_Guests to nt token\n"); + goto done; + } + + /* annoying, but the Guest really does have a session key, and it is + all zeros! */ + session_info->session_key = data_blob_talloc_zero(session_info, 16); + + *_session_info = talloc_move(mem_ctx, &session_info); + *_server_info = talloc_move(mem_ctx, &server_info); + + status = NT_STATUS_OK; +done: + TALLOC_FREE(tmp_ctx); + return status; +} + +/*************************************************************************** + Make (and fill) a auth_session_info struct for a system user login. + This *must* succeed for smbd to start. +***************************************************************************/ + +static NTSTATUS make_new_session_info_system(TALLOC_CTX *mem_ctx, + struct auth_session_info **session_info) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct auth_user_info_dc *user_info_dc = NULL; + uid_t uid = -1; + gid_t gid = -1; + uint32_t hint_flags = 0; + uint32_t session_info_flags = 0; + NTSTATUS status; + + status = auth_system_user_info_dc(frame, lp_netbios_name(), + &user_info_dc); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("auth_system_user_info_dc failed: %s\n", + nt_errstr(status))); + goto done; + } + + /* + * Just get the initial uid/gid + * and don't expand the unix groups. + */ + uid = sec_initial_uid(); + gid = sec_initial_gid(); + hint_flags |= AUTH3_UNIX_HINT_DONT_EXPAND_UNIX_GROUPS; + + /* + * Also avoid sid mapping to gids, + * as well as adding the unix_token uid/gids as + * S-1-22-X-Y SIDs to the nt token. + */ + hint_flags |= AUTH3_UNIX_HINT_DONT_TRANSLATE_FROM_SIDS; + hint_flags |= AUTH3_UNIX_HINT_DONT_TRANSLATE_TO_SIDS; + + /* + * The unix name will be "NT AUTHORITY+SYSTEM", + * where '+' is the "winbind separator" character. + */ + hint_flags |= AUTH3_UNIX_HINT_QUALIFIED_NAME; + status = auth3_user_info_dc_add_hints(user_info_dc, + uid, + gid, + hint_flags); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("auth3_user_info_dc_add_hints failed: %s\n", + nt_errstr(status))); + goto done; + } + + session_info_flags |= AUTH_SESSION_INFO_SIMPLE_PRIVILEGES; + session_info_flags |= AUTH_SESSION_INFO_UNIX_TOKEN; + status = auth3_session_info_create(mem_ctx, user_info_dc, + user_info_dc->info->account_name, + session_info_flags, + session_info); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("auth3_session_info_create failed: %s\n", + nt_errstr(status))); + goto done; + } + +done: + TALLOC_FREE(frame); + return status; +} + +static NTSTATUS make_new_session_info_anonymous(TALLOC_CTX *mem_ctx, + struct auth_session_info **session_info) +{ + TALLOC_CTX *frame = talloc_stackframe(); + const char *guest_account = lp_guest_account(); + struct auth_user_info_dc *user_info_dc = NULL; + struct passwd *pwd = NULL; + uint32_t hint_flags = 0; + uint32_t session_info_flags = 0; + NTSTATUS status; + + /* + * We use the guest account for the unix token + * while we use a true anonymous nt token. + * + * It's very important to have a separate + * nt token for anonymous. + */ + + pwd = Get_Pwnam_alloc(frame, guest_account); + if (pwd == NULL) { + DBG_ERR("Unable to locate guest account [%s]!\n", + guest_account); + status = NT_STATUS_NO_SUCH_USER; + goto done; + } + + status = auth_anonymous_user_info_dc(frame, lp_netbios_name(), + &user_info_dc); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("auth_anonymous_user_info_dc failed: %s\n", + nt_errstr(status))); + goto done; + } + + /* + * Note we don't pass AUTH3_UNIX_HINT_QUALIFIED_NAME + * nor AUTH3_UNIX_HINT_ISOLATED_NAME here + * as we want the unix name be found by getpwuid_alloc(). + */ + + status = auth3_user_info_dc_add_hints(user_info_dc, + pwd->pw_uid, + pwd->pw_gid, + hint_flags); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("auth3_user_info_dc_add_hints failed: %s\n", + nt_errstr(status))); + goto done; + } + + /* + * In future we may want to remove + * AUTH_SESSION_INFO_DEFAULT_GROUPS. + * + * Similar to Windows with EveryoneIncludesAnonymous + * and RestrictAnonymous. + * + * We may introduce AUTH_SESSION_INFO_ANON_WORLD... + * + * But for this is required to keep the existing tests + * working. + */ + session_info_flags |= AUTH_SESSION_INFO_DEFAULT_GROUPS; + session_info_flags |= AUTH_SESSION_INFO_SIMPLE_PRIVILEGES; + session_info_flags |= AUTH_SESSION_INFO_UNIX_TOKEN; + status = auth3_session_info_create(mem_ctx, user_info_dc, + "", + session_info_flags, + session_info); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("auth3_session_info_create failed: %s\n", + nt_errstr(status))); + goto done; + } + +done: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + Fake a auth_session_info just from a username (as a + session_info structure, with create_local_token() already called on + it. +****************************************************************************/ + +NTSTATUS make_session_info_from_username(TALLOC_CTX *mem_ctx, + const char *username, + bool is_guest, + struct auth_session_info **session_info) +{ + struct passwd *pwd; + NTSTATUS status; + struct auth_serversupplied_info *result; + TALLOC_CTX *tmp_ctx; + + tmp_ctx = talloc_stackframe(); + if (tmp_ctx == NULL) { + return NT_STATUS_NO_MEMORY; + } + + pwd = Get_Pwnam_alloc(tmp_ctx, username); + if (pwd == NULL) { + status = NT_STATUS_NO_SUCH_USER; + goto done; + } + + status = make_server_info_pw(tmp_ctx, pwd->pw_name, pwd, &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + result->nss_token = true; + result->guest = is_guest; + + /* Now turn the server_info into a session_info with the full token etc */ + status = create_local_token(mem_ctx, + result, + NULL, + pwd->pw_name, + session_info); + +done: + talloc_free(tmp_ctx); + + return status; +} + +/* This function MUST only used to create the cached server_info for + * guest. + * + * This is a lossy conversion. Variables known to be lost so far + * include: + * + * - nss_token (not needed because the only read doesn't happen + * for the GUEST user, as this routine populates ->security_token + * + * - extra (not needed because the guest account must have valid RIDs per the output of get_guest_info3()) + * + * - The 'server_info' parameter allows the missing 'info3' to be copied across. + */ +static struct auth_serversupplied_info *copy_session_info_serverinfo_guest(TALLOC_CTX *mem_ctx, + const struct auth_session_info *src, + struct auth_serversupplied_info *server_info) +{ + struct auth_serversupplied_info *dst; + NTSTATUS status; + + dst = make_server_info(mem_ctx); + if (dst == NULL) { + return NULL; + } + + /* This element must be provided to convert back to an auth_serversupplied_info */ + SMB_ASSERT(src->unix_info); + + dst->guest = true; + + /* This element must be provided to convert back to an + * auth_serversupplied_info. This needs to be from the + * auth_session_info because the group values in particular + * may change during create_local_token() processing */ + SMB_ASSERT(src->unix_token); + dst->utok.uid = src->unix_token->uid; + dst->utok.gid = src->unix_token->gid; + dst->utok.ngroups = src->unix_token->ngroups; + if (src->unix_token->ngroups != 0) { + dst->utok.groups = (gid_t *)talloc_memdup( + dst, src->unix_token->groups, + sizeof(gid_t)*dst->utok.ngroups); + } else { + dst->utok.groups = NULL; + } + + /* We must have a security_token as otherwise the lossy + * conversion without nss_token would cause create_local_token + * to take the wrong path */ + SMB_ASSERT(src->security_token); + + dst->session_key = data_blob_talloc( dst, src->session_key.data, + src->session_key.length); + + /* This is OK because this functions is only used for the + * GUEST account, which has all-zero keys for both values */ + dst->lm_session_key = data_blob_talloc(dst, src->session_key.data, + src->session_key.length); + + status = copy_netr_SamInfo3(dst, + server_info->info3, + &dst->info3); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(dst); + return NULL; + } + + dst->unix_name = talloc_strdup(dst, src->unix_info->unix_name); + if (!dst->unix_name) { + TALLOC_FREE(dst); + return NULL; + } + + dst->cached_session_info = src; + 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 session_info_set_session_key(struct auth_session_info *info, + DATA_BLOB session_key) +{ + TALLOC_FREE(info->session_key.data); + + info->session_key = data_blob_talloc( + info, session_key.data, session_key.length); + + return (info->session_key.data != NULL); +} + +static struct auth_session_info *guest_info = NULL; +static struct auth_session_info *anonymous_info = NULL; + +static struct auth_serversupplied_info *guest_server_info = NULL; + +bool init_guest_session_info(TALLOC_CTX *mem_ctx) +{ + NTSTATUS status; + + if (guest_info != NULL) + return true; + + status = make_new_session_info_guest(mem_ctx, + &guest_info, + &guest_server_info); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + status = make_new_session_info_anonymous(mem_ctx, + &anonymous_info); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + return true; +} + +bool reinit_guest_session_info(TALLOC_CTX *mem_ctx) +{ + TALLOC_FREE(guest_info); + TALLOC_FREE(guest_server_info); + TALLOC_FREE(anonymous_info); + + DBG_DEBUG("Reinitialing guest info\n"); + + return init_guest_session_info(mem_ctx); +} + +NTSTATUS make_server_info_guest(TALLOC_CTX *mem_ctx, + struct auth_serversupplied_info **server_info) +{ + /* This is trickier than it would appear to need to be because + * we are trying to avoid certain costly operations when the + * structure is converted to a 'auth_session_info' again in + * create_local_token() */ + *server_info = copy_session_info_serverinfo_guest(mem_ctx, guest_info, guest_server_info); + return (*server_info != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY; +} + +NTSTATUS make_session_info_guest(TALLOC_CTX *mem_ctx, + struct auth_session_info **session_info) +{ + *session_info = copy_session_info(mem_ctx, guest_info); + return (*session_info != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY; +} + +NTSTATUS make_server_info_anonymous(TALLOC_CTX *mem_ctx, + struct auth_serversupplied_info **server_info) +{ + if (anonymous_info == NULL) { + return NT_STATUS_UNSUCCESSFUL; + } + + /* + * This is trickier than it would appear to need to be because + * we are trying to avoid certain costly operations when the + * structure is converted to a 'auth_session_info' again in + * create_local_token() + * + * We use a guest server_info, but with the anonymous session info, + * which means create_local_token() will return a copy + * of the anonymous token. + * + * The server info is just used as legacy in order to + * keep existing code working. Maybe some debug messages + * will still refer to guest instead of anonymous. + */ + *server_info = copy_session_info_serverinfo_guest(mem_ctx, anonymous_info, + guest_server_info); + if (*server_info == NULL) { + return NT_STATUS_NO_MEMORY; + } + + return NT_STATUS_OK; +} + +NTSTATUS make_session_info_anonymous(TALLOC_CTX *mem_ctx, + struct auth_session_info **session_info) +{ + if (anonymous_info == NULL) { + return NT_STATUS_UNSUCCESSFUL; + } + + *session_info = copy_session_info(mem_ctx, anonymous_info); + if (*session_info == NULL) { + return NT_STATUS_NO_MEMORY; + } + + return NT_STATUS_OK; +} + +static struct auth_session_info *system_info = NULL; + +NTSTATUS init_system_session_info(TALLOC_CTX *mem_ctx) +{ + if (system_info != NULL) + return NT_STATUS_OK; + + return make_new_session_info_system(mem_ctx, &system_info); +} + +NTSTATUS make_session_info_system(TALLOC_CTX *mem_ctx, + struct auth_session_info **session_info) +{ + if (system_info == NULL) return NT_STATUS_UNSUCCESSFUL; + *session_info = copy_session_info(mem_ctx, system_info); + return (*session_info != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY; +} + +const struct auth_session_info *get_session_info_system(void) +{ + return system_info; +} + +/*************************************************************************** + Purely internal function for make_server_info_info3 +***************************************************************************/ + +static NTSTATUS check_account(TALLOC_CTX *mem_ctx, const char *domain, + const char *username, + const struct dom_sid *sid, + char **found_username, + struct passwd **pwd, + bool *username_was_mapped) +{ + char *orig_dom_user = NULL; + char *dom_user = NULL; + char *lower_username = NULL; + char *real_username = NULL; + struct passwd *passwd; + + lower_username = talloc_strdup(mem_ctx, username); + if (!lower_username) { + return NT_STATUS_NO_MEMORY; + } + if (!strlower_m( lower_username )) { + return NT_STATUS_INVALID_PARAMETER; + } + + orig_dom_user = talloc_asprintf(mem_ctx, + "%s%c%s", + domain, + *lp_winbind_separator(), + lower_username); + if (!orig_dom_user) { + return NT_STATUS_NO_MEMORY; + } + + /* Get the passwd struct. Try to create the account if necessary. */ + + *username_was_mapped = map_username(mem_ctx, orig_dom_user, &dom_user); + if (!dom_user) { + return NT_STATUS_NO_MEMORY; + } + + passwd = smb_getpwnam(mem_ctx, dom_user, &real_username, false); + if (!passwd && !*username_was_mapped) { + struct dom_sid_buf buf; + uid_t uid; + bool ok; + + DBG_DEBUG("Failed to find authenticated user %s via " + "getpwnam(), fallback to sid_to_uid(%s).\n", + dom_user, dom_sid_str_buf(sid, &buf)); + + ok = sid_to_uid(sid, &uid); + if (!ok) { + DBG_ERR("Failed to convert SID %s to a UID (dom_user[%s])\n", + dom_sid_str_buf(sid, &buf), dom_user); + return NT_STATUS_NO_SUCH_USER; + } + passwd = getpwuid_alloc(mem_ctx, uid); + if (!passwd) { + DBG_ERR("Failed to find local account with UID %lld for SID %s (dom_user[%s])\n", + (long long)uid, + dom_sid_str_buf(sid, &buf), + dom_user); + return NT_STATUS_NO_SUCH_USER; + } + real_username = talloc_strdup(mem_ctx, passwd->pw_name); + } + if (!passwd) { + DEBUG(3, ("Failed to find authenticated user %s via " + "getpwnam(), denying access.\n", dom_user)); + return NT_STATUS_NO_SUCH_USER; + } + + if (!real_username) { + return NT_STATUS_NO_MEMORY; + } + + *pwd = passwd; + + /* This is pointless -- there is no support 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 ); + + return NT_STATUS_OK; +} + +/**************************************************************************** + 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, const char *domuser, + char **p_save_username, bool create ) +{ + struct passwd *pw = NULL; + char *p = NULL; + const char *username = NULL; + + /* we only save a copy of the username it has been mangled + by winbindd use default domain */ + *p_save_username = NULL; + + /* don't call map_username() here since it has to be done higher + up the stack so we don't call it multiple times */ + + username = talloc_strdup(mem_ctx, domuser); + if (!username) { + return NULL; + } + + p = strchr_m( username, *lp_winbind_separator() ); + + /* code for a DOMAIN\user string */ + + if ( p ) { + const char *domain = NULL; + + /* split the domain and username into 2 strings */ + *p = '\0'; + domain = username; + p++; + username = p; + + if (strequal(domain, get_global_sam_name())) { + /* + * This typically don't happen + * as check_sam_Security() + * don't call make_server_info_info3() + * and thus check_account(). + * + * But we better keep this. + */ + goto username_only; + } + + pw = Get_Pwnam_alloc( mem_ctx, domuser ); + if (pw == NULL) { + return NULL; + } + /* make sure we get the case of the username correct */ + /* work around 'winbind use default domain = yes' */ + + if ( lp_winbind_use_default_domain() && + !strchr_m( pw->pw_name, *lp_winbind_separator() ) ) { + *p_save_username = talloc_asprintf(mem_ctx, + "%s%c%s", + domain, + *lp_winbind_separator(), + pw->pw_name); + if (!*p_save_username) { + TALLOC_FREE(pw); + return NULL; + } + } else { + *p_save_username = talloc_strdup(mem_ctx, pw->pw_name); + } + + /* whew -- done! */ + return pw; + + } + + /* just lookup a plain username */ +username_only: + 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) { + *p_save_username = talloc_strdup(mem_ctx, 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, + struct auth_serversupplied_info **server_info, + const struct netr_SamInfo3 *info3) +{ + NTSTATUS nt_status; + char *found_username = NULL; + const char *nt_domain; + const char *nt_username; + struct dom_sid user_sid; + struct dom_sid group_sid; + bool username_was_mapped; + struct passwd *pwd; + struct auth_serversupplied_info *result; + struct dom_sid sid; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + + /* + Here is where we should check the list of + trusted domains, and verify that the SID + matches. + */ + + if (!sid_compose(&user_sid, info3->base.domain_sid, info3->base.rid)) { + nt_status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + if (!sid_compose(&group_sid, info3->base.domain_sid, + info3->base.primary_gid)) { + nt_status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + nt_username = talloc_strdup(tmp_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.logon_domain.string); + if (!nt_domain) { + /* If the server didn't give us one, just use the one we sent + * them */ + nt_domain = domain; + } + + /* If getpwnam() fails 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 */ + + /* this call will try to create the user if necessary */ + + sid_copy(&sid, info3->base.domain_sid); + sid_append_rid(&sid, info3->base.rid); + + nt_status = check_account(tmp_ctx, + nt_domain, + nt_username, + &sid, + &found_username, + &pwd, + &username_was_mapped); + + if (!NT_STATUS_IS_OK(nt_status)) { + /* Handle 'map to guest = Bad Uid */ + if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER) && + (lp_security() == SEC_ADS || lp_security() == SEC_DOMAIN) && + lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_UID) { + DBG_NOTICE("Try to map %s to guest account", + nt_username); + nt_status = make_server_info_guest(tmp_ctx, &result); + if (NT_STATUS_IS_OK(nt_status)) { + *server_info = talloc_move(mem_ctx, &result); + } + } + goto out; + } else if ((lp_security() == SEC_ADS || lp_security() == SEC_DOMAIN) && + !is_myname(domain) && pwd->pw_uid < lp_min_domain_uid()) { + /* + * !is_myname(domain) because when smbd starts tries to setup + * the guest user info, calling this function with nobody + * username. Nobody is usually uid 65535 but it can be changed + * to a regular user with 'guest account' parameter + */ + nt_status = NT_STATUS_INVALID_TOKEN; + DBG_NOTICE("Username '%s%s%s' is invalid on this system, " + "it does not meet 'min domain uid' " + "restriction (%u < %u): %s\n", + nt_domain, lp_winbind_separator(), nt_username, + pwd->pw_uid, lp_min_domain_uid(), + nt_errstr(nt_status)); + goto out; + } + + result = make_server_info(tmp_ctx); + if (result == NULL) { + DEBUG(4, ("make_server_info failed!\n")); + nt_status = NT_STATUS_NO_MEMORY; + goto out; + } + + result->unix_name = talloc_strdup(result, found_username); + + /* copy in the info3 */ + nt_status = copy_netr_SamInfo3(result, + info3, + &result->info3); + if (!NT_STATUS_IS_OK(nt_status)) { + goto out; + } + + /* Fill in the unix info we found on the way */ + + result->utok.uid = pwd->pw_uid; + result->utok.gid = pwd->pw_gid; + + /* ensure we are never given NULL session keys */ + + if (all_zero(info3->base.key.key, sizeof(info3->base.key.key))) { + result->session_key = data_blob_null; + } else { + result->session_key = data_blob_talloc( + result, info3->base.key.key, + sizeof(info3->base.key.key)); + } + + if (all_zero(info3->base.LMSessKey.key, + sizeof(info3->base.LMSessKey.key))) { + 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; + + result->guest = (info3->base.user_flags & NETLOGON_GUEST); + + *server_info = talloc_move(mem_ctx, &result); + + nt_status = NT_STATUS_OK; +out: + talloc_free(tmp_ctx); + + return nt_status; +} + +/***************************************************************************** + 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, + struct auth_serversupplied_info **server_info) +{ + struct netr_SamInfo3 info3; + struct netr_SamInfo6 *info6; + + info6 = wbcAuthUserInfo_to_netr_SamInfo6(mem_ctx, info); + if (!info6) { + return NT_STATUS_NO_MEMORY; + } + + info3.base = info6->base; + info3.sidcount = info6->sidcount; + info3.sids = info6->sids; + + return make_server_info_info3(mem_ctx, + sent_nt_username, domain, + server_info, &info3); +} + +/** + * Verify whether or not given domain is trusted. + * + * This should only be used on a DC. + * + * @param domain_name name of the domain to be verified + * @return true if domain is one of the trusted ones or + * false if otherwise + **/ + +bool is_trusted_domain(const char* dom_name) +{ + bool ret; + + if (!IS_DC) { + return false; + } + + if (dom_name == NULL || dom_name[0] == '\0') { + return false; + } + + if (strequal(dom_name, get_global_sam_name())) { + return false; + } + + 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(); + + return ret; +} + + + +/* + on a logon error possibly map the error to success if "map to guest" + is set approriately +*/ +NTSTATUS do_map_to_guest_server_info(TALLOC_CTX *mem_ctx, + NTSTATUS status, + const char *user, + const char *domain, + struct auth_serversupplied_info **server_info) +{ + user = user ? user : ""; + domain = domain ? domain : ""; + + if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) { + if ((lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_USER) || + (lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_PASSWORD)) { + DEBUG(3,("No such user %s [%s] - using guest account\n", + user, domain)); + return make_server_info_guest(mem_ctx, server_info); + } + } else if (NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) { + if (lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_PASSWORD) { + DEBUG(3,("Registered username %s for guest access\n", + user)); + return make_server_info_guest(mem_ctx, server_info); + } + } + + return status; +} + +/* + Extract session key from a session info and return it in a blob + if intent is KEY_USE_16BYTES, truncate it to 16 bytes + + See sections 3.2.4.15 and 3.3.4.2 of MS-SMB + Also see https://lists.samba.org/archive/cifs-protocol/2012-January/002265.html for details + + Note that returned session_key is referencing the original key, it is supposed to be + short-lived. If original session_info->session_key is gone, the reference will be broken. +*/ +NTSTATUS session_extract_session_key(const struct auth_session_info *session_info, DATA_BLOB *session_key, enum session_key_use_intent intent) +{ + + if (session_key == NULL || session_info == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (session_info->session_key.length == 0) { + return NT_STATUS_NO_USER_SESSION_KEY; + } + + *session_key = session_info->session_key; + if (intent == KEY_USE_16BYTES) { + session_key->length = MIN(session_info->session_key.length, 16); + } + return NT_STATUS_OK; +} diff --git a/source3/auth/auth_winbind.c b/source3/auth/auth_winbind.c new file mode 100644 index 0000000..3b8d1a9 --- /dev/null +++ b/source3/auth/auth_winbind.c @@ -0,0 +1,202 @@ +/* + 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" +#include "auth.h" +#include "passdb.h" +#include "nsswitch/libwbclient/wbclient.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 struct auth_usersupplied_info *user_info, + struct auth_serversupplied_info **server_info) +{ + NTSTATUS nt_status; + wbcErr wbc_status; + struct wbcAuthUserParams params; + struct wbcAuthUserInfo *info = NULL; + struct wbcAuthErrorInfo *err = NULL; + + ZERO_STRUCT(params); + + if (!user_info) { + return NT_STATUS_INVALID_PARAMETER; + } + + DEBUG(10, ("Check auth for: [%s]\n", user_info->mapped.account_name)); + + 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->mapped.account_name)); + return NT_STATUS_INVALID_PARAMETER; + } + + if (strequal(user_info->mapped.domain_name, get_global_sam_name())) { + DEBUG(3,("check_winbind_security: Not using winbind, requested domain [%s] was for this SAM.\n", + user_info->mapped.domain_name)); + return NT_STATUS_NOT_IMPLEMENTED; + } + + /* Send off request */ + params.account_name = user_info->client.account_name; + /* + * We need to send the domain name from the client to the DC. With + * NTLMv2 the domain name is part of the hashed second challenge, + * if we change the domain name, the DC will fail to verify the + * challenge cause we changed the domain name, this is like a + * man in the middle attack. + */ + params.domain_name = user_info->client.domain_name; + params.workstation_name = user_info->workstation_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)); + + if (user_info->password.response.nt.length != 0) { + params.password.response.nt_length = + user_info->password.response.nt.length; + params.password.response.nt_data = + user_info->password.response.nt.data; + } + if (user_info->password.response.lanman.length != 0) { + params.password.response.lm_length = + user_info->password.response.lanman.length; + params.password.response.lm_data = + user_info->password.response.lanman.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 pdb_trusted_domain **domains = NULL; + uint32_t num_domains = 0; + NTSTATUS status; + + if (lp_server_role() == ROLE_DOMAIN_MEMBER) { + status = NT_STATUS_NO_LOGON_SERVERS; + DBG_ERR("winbindd not running - " + "but required as domain member: %s\n", + nt_errstr(status)); + return status; + } + + status = pdb_enum_trusted_domains(talloc_tos(), &num_domains, &domains); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("pdb_enum_trusted_domains() failed - %s\n", + nt_errstr(status)); + return status; + } + TALLOC_FREE(domains); + + if (num_domains == 0) { + DBG_DEBUG("winbindd not running - ignoring without " + "trusted domains\n"); + return NT_STATUS_NOT_IMPLEMENTED; + } + + status = NT_STATUS_NO_LOGON_SERVERS; + DBG_ERR("winbindd not running - " + "but required as DC with trusts: %s\n", + nt_errstr(status)); + return status; + } + + if (wbc_status == WBC_ERR_AUTH_ERROR) { + nt_status = NT_STATUS(err->nt_status); + + if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER) && + (err->authoritative == 0)) { + /* + * Trigger a fallback to local SAM + */ + nt_status = NT_STATUS_NOT_IMPLEMENTED; + } + + 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->client.account_name, + user_info->mapped.domain_name, + 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, + struct auth_methods **auth_method) +{ + struct auth_methods *result; + + result = talloc_zero(auth_context, struct auth_methods); + if (result == NULL) { + return NT_STATUS_NO_MEMORY; + } + result->name = "winbind"; + result->auth = check_winbind_security; + + *auth_method = result; + return NT_STATUS_OK; +} + +NTSTATUS auth_winbind_init(TALLOC_CTX *mem_ctx) +{ + return smb_register_auth(AUTH_INTERFACE_VERSION, "winbind", auth_init_winbind); +} diff --git a/source3/auth/check_samsec.c b/source3/auth/check_samsec.c new file mode 100644 index 0000000..8838732 --- /dev/null +++ b/source3/auth/check_samsec.c @@ -0,0 +1,634 @@ +/* + 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" +#include "auth.h" +#include "../libcli/auth/libcli_auth.h" +#include "passdb.h" +#include "lib/util/memcache.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(TALLOC_CTX *mem_ctx, + const char *username, + uint32_t acct_ctrl, + const DATA_BLOB *challenge, + const uint8_t *lm_pw, + const uint8_t *nt_pw, + const struct auth_usersupplied_info *user_info, + DATA_BLOB *user_sess_key, + DATA_BLOB *lm_sess_key) +{ + NTSTATUS status; + struct samr_Password _lm_hash, _nt_hash; + struct samr_Password *lm_hash = NULL; + struct samr_Password *nt_hash = NULL; + + *user_sess_key = data_blob_null; + *lm_sess_key = data_blob_null; + + 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; + } + } + + if (lm_pw) { + memcpy(_lm_hash.hash, lm_pw, sizeof(_lm_hash.hash)); + lm_hash = &_lm_hash; + } + if (nt_pw) { + memcpy(_nt_hash.hash, nt_pw, sizeof(_nt_hash.hash)); + nt_hash = &_nt_hash; + } + switch (user_info->password_state) { + case AUTH_PASSWORD_HASH: + status = hash_password_check(mem_ctx, lp_lanman_auth(), + user_info->password.hash.lanman, + user_info->password.hash.nt, + username, + lm_hash, + nt_hash); + if (NT_STATUS_IS_OK(status)) { + if (nt_pw) { + *user_sess_key = data_blob_talloc(mem_ctx, NULL, 16); + if (!user_sess_key->data) { + return NT_STATUS_NO_MEMORY; + } + SMBsesskeygen_ntv1(nt_pw, user_sess_key->data); + } + } + return status; + + /* Eventually we should test plaintext passwords in their own + * function, not assuming the caller has done a + * mapping */ + case AUTH_PASSWORD_PLAIN: + case AUTH_PASSWORD_RESPONSE: + return ntlm_password_check(mem_ctx, lp_lanman_auth(), + lp_ntlm_auth(), + user_info->logon_parameters, + challenge, + &user_info->password.response.lanman, &user_info->password.response.nt, + username, + user_info->client.account_name, + user_info->client.domain_name, + lm_hash, + nt_hash, + user_sess_key, lm_sess_key); + default: + DEBUG(0,("user_info constructed for user '%s' was invalid - password_state=%u invalid.\n", username, user_info->password_state)); + return NT_STATUS_INTERNAL_ERROR; + } +} + +/**************************************************************************** + 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_t *hours; + struct tm *utctime; + time_t lasttime; + const char *asct; + uint8_t 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 struct auth_usersupplied_info *user_info) +{ + uint32_t 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(talloc_tos(), 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 = NULL; + const char *s = workstation_list; + char *machine_name = talloc_asprintf(mem_ctx, "%s$", user_info->workstation_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->workstation_name)); + if(strequal(tok, user_info->workstation_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); + TALLOC_FREE(machine_name); + + 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 whether the given password is one of the last two + * password history entries. If so, the bad pwcount should + * not be incremented even though the actual password check + * failed. + */ +static bool need_to_increment_bad_pw_count( + const DATA_BLOB *challenge, + struct samu* sampass, + const struct auth_usersupplied_info *user_info) +{ + uint8_t i; + const uint8_t *pwhistory; + uint32_t pwhistory_len; + uint32_t policy_pwhistory_len; + uint32_t acct_ctrl; + const char *username; + TALLOC_CTX *mem_ctx = talloc_stackframe(); + bool result = true; + + pdb_get_account_policy(PDB_POLICY_PASSWORD_HISTORY, + &policy_pwhistory_len); + if (policy_pwhistory_len == 0) { + goto done; + } + + pwhistory = pdb_get_pw_history(sampass, &pwhistory_len); + if (!pwhistory || pwhistory_len == 0) { + goto done; + } + + acct_ctrl = pdb_get_acct_ctrl(sampass); + username = pdb_get_username(sampass); + + for (i=1; i < MIN(MIN(3, policy_pwhistory_len), pwhistory_len); i++) { + const uint8_t *salt; + const uint8_t *nt_pw; + NTSTATUS status; + DATA_BLOB user_sess_key = data_blob_null; + DATA_BLOB lm_sess_key = data_blob_null; + + salt = &pwhistory[i*PW_HISTORY_ENTRY_LEN]; + nt_pw = salt + PW_HISTORY_SALT_LEN; + + if (all_zero(nt_pw, NT_HASH_LEN)) { + /* skip zero password hash */ + continue; + } + + if (!all_zero(salt, PW_HISTORY_SALT_LEN)) { + /* skip nonzero salt (old format entry) */ + continue; + } + + status = sam_password_ok(mem_ctx, + username, acct_ctrl, + challenge, + NULL, nt_pw, + user_info, &user_sess_key, &lm_sess_key); + if (NT_STATUS_IS_OK(status)) { + result = false; + break; + } + } + +done: + TALLOC_FREE(mem_ctx); + return result; +} + +/**************************************************************************** +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. +****************************************************************************/ + +NTSTATUS check_sam_security(const DATA_BLOB *challenge, + TALLOC_CTX *mem_ctx, + const struct auth_usersupplied_info *user_info, + struct 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_badpw = False; + const char *username; + const uint8_t *nt_pw; + const uint8_t *lm_pw; + uint32_t acct_ctrl; + char *mutex_name_by_user = NULL; + struct named_mutex *mtx = NULL; + + /* the returned struct gets kept on the server_info, by means + of a steal further down */ + + sampass = samu_new(mem_ctx); + if (sampass == NULL) { + return NT_STATUS_NO_MEMORY; + } + + /* get the account information */ + + become_root(); + ret = pdb_getsampwnam(sampass, user_info->mapped.account_name); + unbecome_root(); + + if (!ret) { + DEBUG(3,("check_sam_security: Couldn't find user '%s' in " + "passdb.\n", user_info->mapped.account_name)); + TALLOC_FREE(sampass); + return NT_STATUS_NO_SUCH_USER; + } + + acct_ctrl = pdb_get_acct_ctrl(sampass); + username = pdb_get_username(sampass); + nt_pw = pdb_get_nt_passwd(sampass); + lm_pw = pdb_get_lanman_passwd(sampass); + + /* Quit if the account was locked out. */ + if (acct_ctrl & ACB_AUTOLOCK) { + DEBUG(3,("check_sam_security: Account for user %s was locked out.\n", username)); + TALLOC_FREE(sampass); + return NT_STATUS_ACCOUNT_LOCKED_OUT; + } + + nt_status = sam_password_ok(mem_ctx, + username, acct_ctrl, + challenge, lm_pw, nt_pw, + user_info, &user_sess_key, &lm_sess_key); + + /* + * We must re-load the sam acount information under a mutex + * lock to ensure we don't miss any concurrent account lockout + * changes. + */ + + /* Clear out old sampass info. */ + TALLOC_FREE(sampass); + acct_ctrl = 0; + username = NULL; + nt_pw = NULL; + lm_pw = NULL; + + sampass = samu_new(mem_ctx); + if (sampass == NULL) { + return NT_STATUS_NO_MEMORY; + } + + mutex_name_by_user = talloc_asprintf(mem_ctx, + "check_sam_security_mutex_%s", + user_info->mapped.account_name); + if (mutex_name_by_user == NULL) { + nt_status = NT_STATUS_NO_MEMORY; + goto done; + } + + /* Grab the named mutex under root with 30 second timeout. */ + become_root(); + mtx = grab_named_mutex(mem_ctx, mutex_name_by_user, 30); + if (mtx != NULL) { + /* Re-load the account information if we got the mutex. */ + ret = pdb_getsampwnam(sampass, user_info->mapped.account_name); + } + unbecome_root(); + + /* Everything from here on until mtx is freed is done under the mutex.*/ + + if (mtx == NULL) { + DBG_ERR("Acquisition of mutex %s failed " + "for user %s\n", + mutex_name_by_user, + user_info->mapped.account_name); + nt_status = NT_STATUS_INTERNAL_ERROR; + goto done; + } + + if (!ret) { + /* + * Re-load of account failed. This could only happen if the + * user was deleted in the meantime. + */ + DBG_NOTICE("reload of user '%s' in passdb failed.\n", + user_info->mapped.account_name); + nt_status = NT_STATUS_NO_SUCH_USER; + goto done; + } + + /* Re-load the account control info. */ + acct_ctrl = pdb_get_acct_ctrl(sampass); + username = pdb_get_username(sampass); + + /* + * Check if the account is now locked out - now under the mutex. + * This can happen if the server is under + * a password guess attack and the ACB_AUTOLOCK is set by + * another process. + */ + if (acct_ctrl & ACB_AUTOLOCK) { + DBG_NOTICE("Account for user %s was locked out.\n", username); + nt_status = NT_STATUS_ACCOUNT_LOCKED_OUT; + goto done; + } + + /* 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)) { + bool increment_bad_pw_count = false; + + if (NT_STATUS_EQUAL(nt_status,NT_STATUS_WRONG_PASSWORD) && + (acct_ctrl & ACB_NORMAL) && + NT_STATUS_IS_OK(update_login_attempts_status)) + { + increment_bad_pw_count = + need_to_increment_bad_pw_count( + challenge, sampass, user_info); + } + + if (increment_bad_pw_count) { + pdb_increment_bad_password_count(sampass); + updated_badpw = True; + } else { + pdb_update_bad_password_count(sampass, + &updated_badpw); + } + if (updated_badpw){ + NTSTATUS status; + + become_root(); + status = pdb_update_sam_account(sampass); + unbecome_root(); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to modify entry: %s\n", + nt_errstr(status))); + } + } + goto done; + } + + /* + * We must only reset the bad password count if the login was + * successful, including checking account policies + */ + nt_status = sam_account_ok(mem_ctx, sampass, user_info); + if (!NT_STATUS_IS_OK(nt_status)) { + goto done; + } + + if ((acct_ctrl & ACB_NORMAL) && + (pdb_get_bad_password_count(sampass) > 0)){ + NTSTATUS status; + + pdb_set_bad_password_count(sampass, 0, PDB_CHANGED); + pdb_set_bad_password_time(sampass, 0, PDB_CHANGED); + + become_root(); + status = pdb_update_sam_account(sampass); + unbecome_root(); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to modify entry: %s\n", + nt_errstr(status))); + } + } + + become_root(); + nt_status = make_server_info_sam(mem_ctx, sampass, server_info); + 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))); + goto done; + } + + (*server_info)->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; + +done: + /* + * Always flush the getpwsid cache or this will grow indefinetly for + * each NTLM auththentication. + */ + memcache_flush(NULL, PDB_GETPWSID_CACHE); + TALLOC_FREE(sampass); + data_blob_free(&user_sess_key); + data_blob_free(&lm_sess_key); + TALLOC_FREE(mutex_name_by_user); + TALLOC_FREE(mtx); + return nt_status; +} + +/* This helper function for winbindd returns a very similar value to + * what a NETLOGON call would give, without the indirection */ +NTSTATUS check_sam_security_info3(const DATA_BLOB *challenge, + TALLOC_CTX *mem_ctx, + const struct auth_usersupplied_info *user_info, + struct netr_SamInfo3 **pinfo3) +{ + struct auth_serversupplied_info *server_info = NULL; + struct netr_SamInfo3 *info3; + NTSTATUS status; + TALLOC_CTX *frame = talloc_stackframe(); + + status = check_sam_security(challenge, talloc_tos(), user_info, + &server_info); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("check_sam_security failed: %s\n", + nt_errstr(status))); + goto done; + } + + info3 = talloc_zero(mem_ctx, struct netr_SamInfo3); + if (info3 == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + status = serverinfo_to_SamInfo3(server_info, info3); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("serverinfo_to_SamInfo3 failed: %s\n", + nt_errstr(status))); + goto done; + } + *pinfo3 = info3; + status = NT_STATUS_OK; +done: + TALLOC_FREE(frame); + return status; +} diff --git a/source3/auth/pampass.c b/source3/auth/pampass.c new file mode 100644 index 0000000..c3c3d82 --- /dev/null +++ b/source3/auth/pampass.c @@ -0,0 +1,896 @@ +/* + 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" +#include "auth.h" +#include "../libcli/auth/pam_errors.h" +#include "lib/util/string_wrappers.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); + +static char *smb_pam_copy_string(const char *s) +{ + if (s == NULL) { + return NULL; + } + return SMB_STRDUP(s); +} + +static char *smb_pam_copy_fstring(const char *s) +{ + if (s[0] == '\0') { + return NULL; + } + return SMB_STRDUP(s); +} + +/******************************************************************* + 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 = smb_pam_copy_string( + udp->PAM_username); + /* PAM frees resp */ + break; + + case PAM_PROMPT_ECHO_OFF: + reply[replies].resp_retcode = PAM_SUCCESS; + reply[replies].resp = smb_pam_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); + + if (!next_token_talloc(frame, &p, &prompt, NULL)) { + break; + } + + if (strequal(prompt,".")) { + fstrcpy(prompt,"*"); + } + + special_char_sub(prompt); + fstrcpy(t->prompt, prompt); + (void)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); + (void)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; + struct chat_struct *t; + const struct loadparm_substitution *lp_sub = + loadparm_s3_global_substitution(); + 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 = make_pw_chat(lp_passwd_chat(talloc_tos(), lp_sub))) == 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 actually sent: %s\n", current_reply)); +#endif + reply[replies].resp_retcode = PAM_SUCCESS; + reply[replies].resp = smb_pam_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 = smb_pam_copy_fstring( + current_reply); +#ifdef DEBUG_PASSWORD + DEBUG(100,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_OFF: We actually 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; + + *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; + } + +#ifdef HAVE_PAM_RHOST + DEBUG(4,("smb_pam_start: PAM: setting rhost to: %s\n", rhost)); + pam_error = pam_set_item(*pamh, PAM_RHOST, 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 HAVE_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 HAVE_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(const char *user, const char *tty, const 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(const char *user, const char *tty, const 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, const char *rhost) +{ + 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, rhost, 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 * rhost, + 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, rhost, 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 *rhost, + 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, rhost, 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, const char *rhost) +{ + return NT_STATUS_OK; +} + +/* If PAM not used, also no PAM restrictions on sessions. */ +bool smb_pam_claim_session(const char *user, const char *tty, const char *rhost) +{ + return True; +} + +/* If PAM not used, also no PAM restrictions on sessions. */ +bool smb_pam_close_session(const char *in_user, const char *tty, const 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 0000000..d7e3e6a --- /dev/null +++ b/source3/auth/pass_check.c @@ -0,0 +1,294 @@ +/* + 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" +#include "system/passwd.h" +#include "auth.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_AUTH + +#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 + + + + + + + +/**************************************************************************** +core of password checking routine +****************************************************************************/ +static NTSTATUS password_check(const char *user, const char *password, const void *private_data) +{ +#ifdef WITH_PAM + const char *rhost = (const char *)private_data; + return smb_pam_passcheck(user, rhost, password); +#else + + bool ret; + + + + +#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 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 /* 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 *rhost, + const char *password, + bool run_cracker) +{ + char *pass2 = NULL; + + 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) && !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. + */ + + DEBUG(4, ("pass_check: Checking (PAM) password for user %s\n", user)); + +#else /* Not using PAM */ + + DEBUG(4, ("pass_check: Checking password for user %s\n", user)); + + 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_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 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 (!get_this_crypted() || !*get_this_crypted()) { + if (!lp_null_passwords()) { + DEBUG(2, ("Disallowing %s with null password\n", + user)); + return NT_STATUS_LOGON_FAILURE; + } + if (!*password) { + DEBUG(3, + ("Allowing access to %s with null password\n", + user)); + return NT_STATUS_OK; + } + } + +#endif /* defined(WITH_PAM) */ + + /* try it as it came to us */ + nt_status = password_check(user, password, (const void *)rhost); + if NT_STATUS_IS_OK(nt_status) { + 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)) { + if (!strlower_m(pass2)) { + return NT_STATUS_INVALID_PARAMETER; + } + nt_status = password_check(user, pass2, (const void *)rhost); + if (NT_STATUS_IS_OK(nt_status)) { + return (nt_status); + } + } + + return NT_STATUS_WRONG_PASSWORD; +} diff --git a/source3/auth/proto.h b/source3/auth/proto.h new file mode 100644 index 0000000..9bffce7 --- /dev/null +++ b/source3/auth/proto.h @@ -0,0 +1,438 @@ +/* + * Unix SMB/CIFS implementation. + * Password and authentication handling + * + * Copyright (C) Andrew Tridgell 1992-2001 + * Copyright (C) Luke Kenneth Casson Leighton 1996-2000 + * Copyright (C) Jeremy Allison 1997-2001 + * Copyright (C) John H Terpsta 1999-2001 + * Copyright (C) Tim Potter 2000 + * Copyright (C) Andrew Bartlett 2001-2003 + * Copyright (C) Jelmer Vernooij 2002 + * Copyright (C) Rafal Szczesniak 2002 + * Copyright (C) Gerald Carter 2003 + * Copyright (C) Volker Lendecke 2006,2010 + * Copyright (C) Michael Adam 2007 + * Copyright (C) Dan Sledz 2009 + * Copyright (C) Simo Sorce 2010 + * + * 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/>. + */ + +#ifndef _AUTH_PROTO_H_ +#define _AUTH_PROTO_H_ + +/* The following definitions come from auth/auth.c */ + +NTSTATUS smb_register_auth(int version, const char *name, auth_init_function init); +NTSTATUS make_auth3_context_for_ntlm(TALLOC_CTX *mem_ctx, + struct auth_context **auth_context); +NTSTATUS make_auth3_context_for_netlogon(TALLOC_CTX *mem_ctx, + struct auth_context **auth_context); +NTSTATUS make_auth3_context_for_winbind(TALLOC_CTX *mem_ctx, + struct auth_context **auth_context); +bool auth3_context_set_challenge( + struct auth_context *ctx, + const uint8_t chal[8], + const char *challenge_set_by); + +/**************************************************************************** + Try to get a challenge out of the various authentication modules. + Returns a const char of length 8 bytes. +****************************************************************************/ + +NTSTATUS auth_get_ntlm_challenge(struct auth_context *auth_context, + uint8_t chal[8]); + +/** + * 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 mem_ctx The memory context to use to allocate server_info + * + * @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 pserver_info If successful, contains information about the authentication, + * including a struct samu struct describing the user. + * + * @param pauthoritative Indicates if the result should be treated as final + * result. + * + * @return An NTSTATUS with NT_STATUS_OK or an appropriate error. + * + **/ +NTSTATUS auth_check_ntlm_password(TALLOC_CTX *mem_ctx, + const struct auth_context *auth_context, + const struct auth_usersupplied_info *user_info, + struct auth_serversupplied_info **pserver_info, + uint8_t *pauthoritative); + +/* The following definitions come from auth/auth_builtin.c */ + +NTSTATUS auth_builtin_init(TALLOC_CTX *mem_ctx); + +/* The following definitions come from auth/auth_generic.c */ + +NTSTATUS make_auth4_context(TALLOC_CTX *mem_ctx, struct auth4_context **auth4_context_out); +NTSTATUS auth_generic_prepare(TALLOC_CTX *mem_ctx, + const struct tsocket_address *remote_address, + const struct tsocket_address *local_address, + const char *service_description, + struct gensec_security **gensec_security_out); + +NTSTATUS auth_check_password_session_info(struct auth4_context *auth_context, + TALLOC_CTX *mem_ctx, + struct auth_usersupplied_info *user_info, + struct auth_session_info **session_info); + +/* The following definitions come from auth/auth_ntlmssp.c */ + +NTSTATUS auth3_generate_session_info(struct auth4_context *auth_context, + TALLOC_CTX *mem_ctx, + void *server_returned_info, + const char *original_user_name, + uint32_t session_info_flags, + struct auth_session_info **session_info); + +NTSTATUS auth3_get_challenge(struct auth4_context *auth4_context, + uint8_t chal[8]); + +NTSTATUS auth3_set_challenge(struct auth4_context *auth4_context, const uint8_t *chal, + const char *challenge_set_by); + +struct tevent_req *auth3_check_password_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct auth4_context *auth4_context, + const struct auth_usersupplied_info *user_info); +NTSTATUS auth3_check_password_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + uint8_t *pauthoritative, + void **server_returned_info, + DATA_BLOB *nt_session_key, + DATA_BLOB *lm_session_key); + +/* The following definitions come from auth/auth_sam.c */ + +NTSTATUS check_sam_security(const DATA_BLOB *challenge, + TALLOC_CTX *mem_ctx, + const struct auth_usersupplied_info *user_info, + struct auth_serversupplied_info **server_info); +NTSTATUS check_sam_security_info3(const DATA_BLOB *challenge, + TALLOC_CTX *mem_ctx, + const struct auth_usersupplied_info *user_info, + struct netr_SamInfo3 **pinfo3); +NTSTATUS auth_sam_init(TALLOC_CTX *mem_ctx); + +/* The following definitions come from auth/auth_unix.c */ + +NTSTATUS auth_unix_init(TALLOC_CTX *mem_ctx); + +/* The following definitions come from auth/auth_util.c */ +struct tsocket_address; + +NTSTATUS make_user_info_map(TALLOC_CTX *mem_ctx, + struct auth_usersupplied_info **user_info, + const char *smb_name, + const char *client_domain, + const char *workstation_name, + const struct tsocket_address *remote_address, + const struct tsocket_address *local_address, + const char *service_description, + const DATA_BLOB *lm_pwd, + const DATA_BLOB *nt_pwd, + const struct samr_Password *lm_interactive_pwd, + const struct samr_Password *nt_interactive_pwd, + const char *plaintext, + enum auth_password_state password_state); +bool make_user_info_netlogon_network(TALLOC_CTX *mem_ctx, + struct auth_usersupplied_info **user_info, + const char *smb_name, + const char *client_domain, + const char *workstation_name, + const struct tsocket_address *remote_address, + const struct tsocket_address *local_address, + uint32_t logon_parameters, + const unsigned char *lm_network_pwd, + int lm_pwd_len, + const unsigned char *nt_network_pwd, + int nt_pwd_len); +bool make_user_info_netlogon_interactive(TALLOC_CTX *mem_ctx, + struct auth_usersupplied_info **user_info, + const char *smb_name, + const char *client_domain, + const char *workstation_name, + const struct tsocket_address *remote_address, + const struct tsocket_address *local_address, + uint32_t logon_parameters, + const unsigned char chal[8], + const unsigned char lm_interactive_pwd[16], + const unsigned char nt_interactive_pwd[16]); +bool make_user_info_for_reply(TALLOC_CTX *mem_ctx, + struct auth_usersupplied_info **user_info, + const char *smb_name, + const char *client_domain, + const struct tsocket_address *remote_address, + const struct tsocket_address *local_address, + const char *service_description, + const uint8_t chal[8], + DATA_BLOB plaintext_password); +NTSTATUS make_user_info_for_reply_enc(TALLOC_CTX *mem_ctx, + struct auth_usersupplied_info **user_info, + const char *smb_name, + const char *client_domain, + const struct tsocket_address *remote_address, + const struct tsocket_address *local_address, + const char *service_description, + DATA_BLOB lm_resp, DATA_BLOB nt_resp); +bool make_user_info_guest(TALLOC_CTX *mem_ctx, + const struct tsocket_address *remote_address, + const struct tsocket_address *local_address, + const char *service_description, + struct auth_usersupplied_info **user_info); + +struct samu; +NTSTATUS make_server_info_sam(TALLOC_CTX *mem_ctx, + struct samu *sampass, + struct auth_serversupplied_info **pserver_info); +NTSTATUS create_local_token(TALLOC_CTX *mem_ctx, + const struct auth_serversupplied_info *server_info, + DATA_BLOB *session_key, + const char *smb_name, + struct auth_session_info **session_info_out); + +/* + * The unix name should be constructed as DOMAIN+ACCOUNT, + * while '+' will be the "winbind separator" character. + */ +#define AUTH3_UNIX_HINT_QUALIFIED_NAME 0x00000001 +/* + * The unix name will be just ACCOUNT + */ +#define AUTH3_UNIX_HINT_ISLOLATED_NAME 0x00000002 +/* + * Don't translate the nt token SIDS into uid/gids + */ +#define AUTH3_UNIX_HINT_DONT_TRANSLATE_FROM_SIDS 0x00000004 +/* + * Don't translate the unix token uid/gids to S-1-22-X-Y SIDS + */ +#define AUTH3_UNIX_HINT_DONT_TRANSLATE_TO_SIDS 0x00000008 +/* + * The unix token won't get expanded gid values + * from getgroups_unix_user() + */ +#define AUTH3_UNIX_HINT_DONT_EXPAND_UNIX_GROUPS 0x00000010 +NTSTATUS auth3_user_info_dc_add_hints(struct auth_user_info_dc *user_info_dc, + uid_t uid, + gid_t gid, + uint32_t flags); +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 security_token **token); +bool user_in_group_sid(const char *username, const struct dom_sid *group_sid); +bool user_sid_in_group_sid(const struct dom_sid *sid, const struct dom_sid *group_sid); +bool user_in_group(const char *username, const char *groupname); +struct passwd; +NTSTATUS make_server_info_pw(TALLOC_CTX *mem_ctx, + const char *unix_username, + const struct passwd *pwd, + struct auth_serversupplied_info **server_info); +NTSTATUS make_session_info_from_username(TALLOC_CTX *mem_ctx, + const char *username, + bool is_guest, + struct auth_session_info **session_info); +bool init_guest_session_info(TALLOC_CTX *mem_ctx); +bool reinit_guest_session_info(TALLOC_CTX *mem_ctx); +NTSTATUS init_system_session_info(TALLOC_CTX *mem_ctx); +bool session_info_set_session_key(struct auth_session_info *info, + DATA_BLOB session_key); +NTSTATUS make_server_info_guest(TALLOC_CTX *mem_ctx, + struct auth_serversupplied_info **server_info); +NTSTATUS make_session_info_guest(TALLOC_CTX *mem_ctx, + struct auth_session_info **server_info); +NTSTATUS make_server_info_anonymous(TALLOC_CTX *mem_ctx, + struct auth_serversupplied_info **server_info); +NTSTATUS make_session_info_anonymous(TALLOC_CTX *mem_ctx, + struct auth_session_info **psession_info); +NTSTATUS make_session_info_system(TALLOC_CTX *mem_ctx, + struct auth_session_info **session_info); +const struct auth_session_info *get_session_info_system(void); +struct passwd *smb_getpwnam( TALLOC_CTX *mem_ctx, const char *domuser, + char **p_save_username, bool create ); +NTSTATUS make_server_info_info3(TALLOC_CTX *mem_ctx, + const char *sent_nt_username, + const char *domain, + struct auth_serversupplied_info **server_info, + const struct netr_SamInfo3 *info3); +struct wbcAuthUserInfo; +NTSTATUS make_server_info_wbcAuthUserInfo(TALLOC_CTX *mem_ctx, + const char *sent_nt_username, + const char *domain, + const struct wbcAuthUserInfo *info, + struct auth_serversupplied_info **server_info); +bool is_trusted_domain(const char* dom_name); +NTSTATUS session_extract_session_key(const struct auth_session_info *session_info, DATA_BLOB *session_key, enum session_key_use_intent intent); + +/* The following definitions come from auth/user_info.c */ + +NTSTATUS make_user_info(TALLOC_CTX *mem_ctx, + struct auth_usersupplied_info **ret_user_info, + const char *smb_name, + const char *internal_username, + const char *client_domain, + const char *domain, + const char *workstation_name, + const struct tsocket_address *remote_address, + const struct tsocket_address *local_address, + const char *service_description, + const DATA_BLOB *lm_pwd, + const DATA_BLOB *nt_pwd, + const struct samr_Password *lm_interactive_pwd, + const struct samr_Password *nt_interactive_pwd, + const char *plaintext_password, + enum auth_password_state password_state); + +NTSTATUS do_map_to_guest_server_info(TALLOC_CTX *mem_ctx, + NTSTATUS status, + const char *user, + const char *domain, + struct auth_serversupplied_info **server_info); + +/* The following definitions come from auth/auth_winbind.c */ + +NTSTATUS auth_winbind_init(TALLOC_CTX *mem_ctx); + +/* The following definitions come from auth/server_info.c */ + +struct netr_SamInfo2; +struct netr_SamInfo3; +struct netr_SamInfo6; + +struct auth_serversupplied_info *make_server_info(TALLOC_CTX *mem_ctx); +NTSTATUS serverinfo_to_SamInfo2(struct auth_serversupplied_info *server_info, + struct netr_SamInfo2 *sam2); +NTSTATUS serverinfo_to_SamInfo3(const struct auth_serversupplied_info *server_info, + struct netr_SamInfo3 *sam3); +NTSTATUS serverinfo_to_SamInfo6(struct auth_serversupplied_info *server_info, + struct netr_SamInfo6 *sam6); +NTSTATUS create_info3_from_pac_logon_info(TALLOC_CTX *mem_ctx, + const struct PAC_LOGON_INFO *logon_info, + struct netr_SamInfo3 **pp_info3); +NTSTATUS create_info6_from_pac(TALLOC_CTX *mem_ctx, + const struct PAC_LOGON_INFO *logon_info, + const struct PAC_UPN_DNS_INFO *upn_dns_info, + struct netr_SamInfo6 **pp_info6); +NTSTATUS samu_to_SamInfo3(TALLOC_CTX *mem_ctx, + struct samu *samu, + const char *login_server, + struct netr_SamInfo3 **_info3, + struct extra_auth_info *extra); +NTSTATUS passwd_to_SamInfo3(TALLOC_CTX *mem_ctx, + const char *unix_username, + const struct passwd *pwd, + struct netr_SamInfo3 **pinfo3, + struct extra_auth_info *extra); + +/* The following definitions come from auth/pampass.c */ + +bool smb_pam_claim_session(const char *user, const char *tty, const char *rhost); +bool smb_pam_close_session(const char *user, const char *tty, const char *rhost); +NTSTATUS smb_pam_accountcheck(const char *user, const char *rhost); +NTSTATUS smb_pam_passcheck(const char * user, const char * rhost, + const char * password); +bool smb_pam_passchange(const char *user, const char *rhost, + const char *oldpassword, const char *newpassword); + +/* The following definitions come from auth/pass_check.c */ + +NTSTATUS pass_check(const struct passwd *pass, + const char *user, + const char *rhost, + const char *password, + bool run_cracker); + +/* The following definitions come from auth/token_util.c */ + +bool nt_token_check_sid ( const struct dom_sid *sid, const struct security_token *token ); +bool nt_token_check_domain_rid( struct security_token *token, uint32_t rid ); +NTSTATUS get_root_nt_token( struct security_token **token ); +NTSTATUS add_aliases(const struct dom_sid *domain_sid, + struct security_token *token); +NTSTATUS create_local_nt_token(TALLOC_CTX *mem_ctx, + const struct dom_sid *user_sid, + bool is_guest, + int num_groupsids, + const struct dom_sid *groupsids, + struct security_token **token); +NTSTATUS finalize_local_nt_token(struct security_token *result, + uint32_t session_info_flags); +NTSTATUS get_user_sid_info3_and_extra(const struct netr_SamInfo3 *info3, + const struct extra_auth_info *extra, + struct dom_sid *sid); +NTSTATUS create_local_nt_token_from_info3(TALLOC_CTX *mem_ctx, + bool is_guest, + const struct netr_SamInfo3 *info3, + const struct extra_auth_info *extra, + struct security_token **ntok); +void debug_unix_user_token(int dbg_class, int dbg_lev, uid_t uid, gid_t gid, + int n_groups, gid_t *groups); + +/* The following definitions come from auth/user_util.c */ + +bool map_username(TALLOC_CTX *ctx, const char *user_in, char **p_user_out); +bool user_in_netgroup(TALLOC_CTX *ctx, const char *user, const char *ngname); +bool user_in_list(TALLOC_CTX *ctx, const char *user, const char * const *list); + +/* The following definitions come from auth/user_krb5.c */ +struct PAC_LOGON_INFO; +NTSTATUS get_user_from_kerberos_info(TALLOC_CTX *mem_ctx, + const char *cli_name, + const char *princ_name, + bool *is_mapped, + bool *mapped_to_guest, + char **ntuser, + char **ntdomain, + char **username, + struct passwd **_pw); +NTSTATUS make_session_info_krb5(TALLOC_CTX *mem_ctx, + char *ntuser, + char *ntdomain, + char *username, + struct passwd *pw, + bool mapped_to_guest, bool username_was_mapped, + struct auth_session_info **session_info); + +/* The following definitions come from auth/auth_samba4.c */ + +NTSTATUS auth_samba4_init(TALLOC_CTX *mem_ctx); + +#endif /* _AUTH_PROTO_H_ */ diff --git a/source3/auth/server_info.c b/source3/auth/server_info.c new file mode 100644 index 0000000..e5debd4 --- /dev/null +++ b/source3/auth/server_info.c @@ -0,0 +1,791 @@ +/* + Unix SMB/CIFS implementation. + Authentication utility functions + Copyright (C) Volker Lendecke 2010 + + 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" +#include "auth.h" +#include "lib/util_unixsids.h" +#include "../librpc/gen_ndr/netlogon.h" +#include "../libcli/security/security.h" +#include "rpc_client/util_netlogon.h" +#include "nsswitch/libwbclient/wbclient.h" +#include "lib/winbind_util.h" +#include "passdb.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_AUTH + +/*************************************************************************** + Make a server_info struct. Free with TALLOC_FREE(). +***************************************************************************/ + +struct auth_serversupplied_info *make_server_info(TALLOC_CTX *mem_ctx) +{ + struct auth_serversupplied_info *result; + + result = talloc_zero(mem_ctx, struct auth_serversupplied_info); + if (result == NULL) { + DEBUG(0, ("talloc failed\n")); + return NULL; + } + + /* 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; +} + +/**************************************************************************** + inits a netr_SamInfo2 structure from an auth_serversupplied_info. sam2 must + already be initialized and is used as the talloc parent for its members. +*****************************************************************************/ + +NTSTATUS serverinfo_to_SamInfo2(struct auth_serversupplied_info *server_info, + struct netr_SamInfo2 *sam2) +{ + struct netr_SamInfo3 *info3 = NULL; + NTSTATUS status; + + status = copy_netr_SamInfo3(sam2, + server_info->info3, + &info3); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (server_info->session_key.length) { + memcpy(info3->base.key.key, + server_info->session_key.data, + MIN(sizeof(info3->base.key.key), + server_info->session_key.length)); + } + if (server_info->lm_session_key.length) { + memcpy(info3->base.LMSessKey.key, + server_info->lm_session_key.data, + MIN(sizeof(info3->base.LMSessKey.key), + server_info->lm_session_key.length)); + } + + sam2->base = info3->base; + + return NT_STATUS_OK; +} + +/**************************************************************************** + inits a netr_SamInfo3 structure from an auth_serversupplied_info. sam3 must + already be initialized and is used as the talloc parent for its members. +*****************************************************************************/ + +NTSTATUS serverinfo_to_SamInfo3(const struct auth_serversupplied_info *server_info, + struct netr_SamInfo3 *sam3) +{ + struct netr_SamInfo3 *info3 = NULL; + NTSTATUS status; + + status = copy_netr_SamInfo3(sam3, + server_info->info3, + &info3); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (server_info->session_key.length) { + memcpy(info3->base.key.key, + server_info->session_key.data, + MIN(sizeof(info3->base.key.key), + server_info->session_key.length)); + } + if (server_info->lm_session_key.length) { + memcpy(info3->base.LMSessKey.key, + server_info->lm_session_key.data, + MIN(sizeof(info3->base.LMSessKey.key), + server_info->lm_session_key.length)); + } + + sam3->base = info3->base; + + sam3->sidcount = 0; + sam3->sids = NULL; + + return NT_STATUS_OK; +} + +/**************************************************************************** + inits a netr_SamInfo6 structure from an auth_serversupplied_info. sam6 must + already be initialized and is used as the talloc parent for its members. +*****************************************************************************/ + +NTSTATUS serverinfo_to_SamInfo6(struct auth_serversupplied_info *server_info, + struct netr_SamInfo6 *sam6) +{ + struct pdb_domain_info *dominfo; + struct netr_SamInfo3 *info3 = NULL; + NTSTATUS status; + + if ((pdb_capabilities() & PDB_CAP_ADS) == 0) { + DEBUG(10,("Not adding validation info level 6 " + "without ADS passdb backend\n")); + return NT_STATUS_INVALID_INFO_CLASS; + } + + dominfo = pdb_get_domain_info(sam6); + if (dominfo == NULL) { + return NT_STATUS_NO_MEMORY; + } + + status = copy_netr_SamInfo3(sam6, + server_info->info3, + &info3); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (server_info->session_key.length) { + memcpy(info3->base.key.key, + server_info->session_key.data, + MIN(sizeof(info3->base.key.key), + server_info->session_key.length)); + } + if (server_info->lm_session_key.length) { + memcpy(info3->base.LMSessKey.key, + server_info->lm_session_key.data, + MIN(sizeof(info3->base.LMSessKey.key), + server_info->lm_session_key.length)); + } + + sam6->base = info3->base; + + sam6->sidcount = 0; + sam6->sids = NULL; + + sam6->dns_domainname.string = talloc_strdup(sam6, dominfo->dns_domain); + if (sam6->dns_domainname.string == NULL) { + return NT_STATUS_NO_MEMORY; + } + + sam6->principal_name.string = talloc_asprintf( + sam6, "%s@%s", sam6->base.account_name.string, + sam6->dns_domainname.string); + if (sam6->principal_name.string == NULL) { + return NT_STATUS_NO_MEMORY; + } + + return NT_STATUS_OK; +} + +static NTSTATUS append_netr_SidAttr(TALLOC_CTX *mem_ctx, + struct netr_SidAttr **sids, + uint32_t *count, + const struct dom_sid2 *asid, + uint32_t attributes) +{ + uint32_t t = *count; + + *sids = talloc_realloc(mem_ctx, *sids, struct netr_SidAttr, t + 1); + if (*sids == NULL) { + return NT_STATUS_NO_MEMORY; + } + (*sids)[t].sid = dom_sid_dup(*sids, asid); + if ((*sids)[t].sid == NULL) { + return NT_STATUS_NO_MEMORY; + } + (*sids)[t].attributes = attributes; + *count = t + 1; + + return NT_STATUS_OK; +} + +/* Fills the samr_RidWithAttributeArray with the provided sids. + * If it happens that we have additional groups that do not belong + * to the domain, add their sids as extra sids */ +static NTSTATUS group_sids_to_info3(struct netr_SamInfo3 *info3, + const struct dom_sid *sids, + size_t num_sids) +{ + uint32_t attributes = SE_GROUP_MANDATORY | + SE_GROUP_ENABLED_BY_DEFAULT | + SE_GROUP_ENABLED; + struct samr_RidWithAttributeArray *groups; + struct dom_sid *domain_sid; + unsigned int i; + NTSTATUS status; + uint32_t rid; + bool ok; + + domain_sid = info3->base.domain_sid; + groups = &info3->base.groups; + + groups->rids = talloc_array(info3, + struct samr_RidWithAttribute, num_sids); + if (!groups->rids) { + return NT_STATUS_NO_MEMORY; + } + + for (i = 0; i < num_sids; i++) { + ok = sid_peek_check_rid(domain_sid, &sids[i], &rid); + if (ok) { + /* store domain group rid */ + groups->rids[groups->count].rid = rid; + groups->rids[groups->count].attributes = attributes; + groups->count++; + continue; + } + + /* if this wasn't a domain sid, add it as extra sid */ + status = append_netr_SidAttr(info3, &info3->sids, + &info3->sidcount, + &sids[i], attributes); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + + return NT_STATUS_OK; +} + +/* + * Merge resource SIDs, if any, into the passed in info3 structure. + */ + +static NTSTATUS merge_resource_sids(const struct PAC_LOGON_INFO *logon_info, + struct netr_SamInfo3 *info3) +{ + uint32_t i = 0; + const struct PAC_DOMAIN_GROUP_MEMBERSHIP *rg = NULL; + + if (logon_info->info3.base.user_flags & NETLOGON_RESOURCE_GROUPS) { + rg = &logon_info->resource_groups; + } + + if (rg == NULL) { + return NT_STATUS_OK; + } + + if (rg->domain_sid == NULL) { + DEBUG(10, ("Missing Resource Group Domain SID\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + /* The IDL layer would be a better place to check this, but to + * guard the integer addition below, we double-check */ + if (rg->groups.count > 65535) { + DEBUG(10, ("Too much Resource Group RIDs %u\n", + (unsigned)rg->groups.count)); + return NT_STATUS_INVALID_PARAMETER; + } + + /* + * If there are any resource groups (SID Compression) add + * them to the extra sids portion of the info3 in the PAC. + * + * This makes the info3 look like it would if we got the info + * from the DC rather than the PAC. + */ + + /* + * Construct a SID for each RID in the list and then append it + * to the info3. + */ + for (i = 0; i < rg->groups.count; i++) { + NTSTATUS status; + struct dom_sid new_sid; + uint32_t attributes = rg->groups.rids[i].attributes; + struct dom_sid_buf buf; + + sid_compose(&new_sid, + rg->domain_sid, + rg->groups.rids[i].rid); + + DEBUG(10, ("Adding SID %s to extra SIDS\n", + dom_sid_str_buf(&new_sid, &buf))); + + status = append_netr_SidAttr(info3, &info3->sids, + &info3->sidcount, + &new_sid, + attributes); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("failed to append SID %s to extra SIDS: %s\n", + dom_sid_str_buf(&new_sid, &buf), + nt_errstr(status))); + return status; + } + } + + return NT_STATUS_OK; +} + +/* + * Create a copy of an info3 struct from the struct PAC_LOGON_INFO, + * then merge resource SIDs, if any, into it. If successful return + * the created info3 struct. + */ + +NTSTATUS create_info3_from_pac_logon_info(TALLOC_CTX *mem_ctx, + const struct PAC_LOGON_INFO *logon_info, + struct netr_SamInfo3 **pp_info3) +{ + NTSTATUS status; + struct netr_SamInfo3 *info3 = NULL; + + status = copy_netr_SamInfo3(mem_ctx, + &logon_info->info3, + &info3); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = merge_resource_sids(logon_info, info3); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(info3); + return status; + } + *pp_info3 = info3; + return NT_STATUS_OK; +} + +/* + * Create a copy of an info6 struct from the PAC_UPN_DNS_INFO and PAC_LOGON_INFO + * then merge resource SIDs, if any, into it. If successful return the created + * info6 struct. + */ +NTSTATUS create_info6_from_pac(TALLOC_CTX *mem_ctx, + const struct PAC_LOGON_INFO *logon_info, + const struct PAC_UPN_DNS_INFO *upn_dns_info, + struct netr_SamInfo6 **pp_info6) +{ + NTSTATUS status; + struct netr_SamInfo6 *info6 = NULL; + struct netr_SamInfo3 *info3 = NULL; + + info6 = talloc_zero(mem_ctx, struct netr_SamInfo6); + if (info6 == NULL) { + return NT_STATUS_NO_MEMORY; + } + + status = copy_netr_SamInfo3(info6, + &logon_info->info3, + &info3); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(info6); + return status; + } + + status = merge_resource_sids(logon_info, info3); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(info6); + return status; + } + + info6->base = info3->base; + info6->sids = info3->sids; + info6->sidcount = info3->sidcount; + + if (upn_dns_info != NULL) { + info6->dns_domainname.string = talloc_strdup(info6, + upn_dns_info->dns_domain_name); + if (info6->dns_domainname.string == NULL) { + TALLOC_FREE(info6); + return NT_STATUS_NO_MEMORY; + } + info6->principal_name.string = talloc_strdup(info6, + upn_dns_info->upn_name); + if (info6->principal_name.string == NULL) { + TALLOC_FREE(info6); + return NT_STATUS_NO_MEMORY; + } + } + + *pp_info6 = info6; + return NT_STATUS_OK; +} + +/* + * Check if this is a "Unix Users" domain user, or a + * "Unix Groups" domain group, we need to handle it + * in a special way if that's the case. + */ + +static NTSTATUS SamInfo3_handle_sids(const char *username, + const struct dom_sid *user_sid, + const struct dom_sid *group_sid, + struct netr_SamInfo3 *info3, + struct dom_sid *domain_sid, + struct extra_auth_info *extra) +{ + struct dom_sid_buf buf; + + if (sid_check_is_in_unix_users(user_sid)) { + /* in info3 you can only set rids for the user and the + * primary group, and the domain sid must be that of + * the sam domain. + * + * Store a completely bogus value here. + * The real SID is stored in the extra sids. + * Other code will know to look there if (-1) is found + */ + info3->base.rid = (uint32_t)(-1); + sid_copy(&extra->user_sid, user_sid); + + DEBUG(10, ("Unix User found. Rid marked as " + "special and sid (%s) saved as extra sid\n", + dom_sid_str_buf(user_sid, &buf))); + } else { + sid_copy(domain_sid, user_sid); + sid_split_rid(domain_sid, &info3->base.rid); + } + + if (is_null_sid(domain_sid)) { + sid_copy(domain_sid, get_global_sam_sid()); + } + + /* check if this is a "Unix Groups" domain group, + * if so we need special handling */ + if (sid_check_is_in_unix_groups(group_sid)) { + /* in info3 you can only set rids for the user and the + * primary group, and the domain sid must be that of + * the sam domain. + * + * Store a completely bogus value here. + * The real SID is stored in the extra sids. + * Other code will know to look there if (-1) is found + */ + info3->base.primary_gid = (uint32_t)(-1); + sid_copy(&extra->pgid_sid, group_sid); + + DEBUG(10, ("Unix Group found. Rid marked as " + "special and sid (%s) saved as extra sid\n", + dom_sid_str_buf(group_sid, &buf))); + } else { + bool ok = sid_peek_check_rid(domain_sid, group_sid, + &info3->base.primary_gid); + if (!ok) { + struct dom_sid_buf buf2, buf3; + DEBUG(1, ("The primary group domain sid(%s) does not " + "match the domain sid(%s) for %s(%s)\n", + dom_sid_str_buf(group_sid, &buf), + dom_sid_str_buf(domain_sid, &buf2), + username, + dom_sid_str_buf(user_sid, &buf3))); + return NT_STATUS_INVALID_SID; + } + } + return NT_STATUS_OK; +} + +#define RET_NOMEM(ptr) do { \ + if (!ptr) { \ + TALLOC_FREE(info3); \ + return NT_STATUS_NO_MEMORY; \ + } } while(0) + +NTSTATUS samu_to_SamInfo3(TALLOC_CTX *mem_ctx, + struct samu *samu, + const char *login_server, + struct netr_SamInfo3 **_info3, + struct extra_auth_info *extra) +{ + struct netr_SamInfo3 *info3; + const struct dom_sid *user_sid; + const struct dom_sid *group_sid; + struct dom_sid domain_sid = {0}; + struct dom_sid *group_sids; + uint32_t num_group_sids = 0; + const char *tmp; + gid_t *gids; + NTSTATUS status; + + user_sid = pdb_get_user_sid(samu); + group_sid = pdb_get_group_sid(samu); + + if (!user_sid || !group_sid) { + DEBUG(1, ("Sam account is missing sids!\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + info3 = talloc_zero(mem_ctx, struct netr_SamInfo3); + if (!info3) { + return NT_STATUS_NO_MEMORY; + } + + status = SamInfo3_handle_sids(pdb_get_username(samu), + user_sid, + group_sid, + info3, + &domain_sid, + extra); + + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(info3); + return status; + } + + unix_to_nt_time(&info3->base.logon_time, pdb_get_logon_time(samu)); + unix_to_nt_time(&info3->base.logoff_time, get_time_t_max()); + unix_to_nt_time(&info3->base.kickoff_time, get_time_t_max()); + unix_to_nt_time(&info3->base.last_password_change, + pdb_get_pass_last_set_time(samu)); + unix_to_nt_time(&info3->base.allow_password_change, + pdb_get_pass_can_change_time(samu)); + unix_to_nt_time(&info3->base.force_password_change, + pdb_get_pass_must_change_time(samu)); + + tmp = pdb_get_username(samu); + if (tmp) { + info3->base.account_name.string = talloc_strdup(info3, tmp); + RET_NOMEM(info3->base.account_name.string); + } + tmp = pdb_get_fullname(samu); + if (tmp) { + info3->base.full_name.string = talloc_strdup(info3, tmp); + RET_NOMEM(info3->base.full_name.string); + } + tmp = pdb_get_logon_script(samu); + if (tmp) { + info3->base.logon_script.string = talloc_strdup(info3, tmp); + RET_NOMEM(info3->base.logon_script.string); + } + tmp = pdb_get_profile_path(samu); + if (tmp) { + info3->base.profile_path.string = talloc_strdup(info3, tmp); + RET_NOMEM(info3->base.profile_path.string); + } + tmp = pdb_get_homedir(samu); + if (tmp) { + info3->base.home_directory.string = talloc_strdup(info3, tmp); + RET_NOMEM(info3->base.home_directory.string); + } + tmp = pdb_get_dir_drive(samu); + if (tmp) { + info3->base.home_drive.string = talloc_strdup(info3, tmp); + RET_NOMEM(info3->base.home_drive.string); + } + + info3->base.logon_count = pdb_get_logon_count(samu); + info3->base.bad_password_count = pdb_get_bad_password_count(samu); + + info3->base.logon_domain.string = talloc_strdup(info3, + pdb_get_domain(samu)); + RET_NOMEM(info3->base.logon_domain.string); + + info3->base.domain_sid = dom_sid_dup(info3, &domain_sid); + RET_NOMEM(info3->base.domain_sid); + + status = pdb_enum_group_memberships(mem_ctx, samu, + &group_sids, &gids, + &num_group_sids); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to get groups from sam account.\n")); + TALLOC_FREE(info3); + return status; + } + + if (num_group_sids) { + status = group_sids_to_info3(info3, group_sids, num_group_sids); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(info3); + return status; + } + } + + /* We don't need sids and gids after the conversion */ + TALLOC_FREE(group_sids); + TALLOC_FREE(gids); + num_group_sids = 0; + + /* FIXME: should we add other flags ? */ + info3->base.user_flags = NETLOGON_EXTRA_SIDS; + + if (login_server) { + info3->base.logon_server.string = talloc_strdup(info3, login_server); + RET_NOMEM(info3->base.logon_server.string); + } + + info3->base.acct_flags = pdb_get_acct_ctrl(samu); + + *_info3 = info3; + return NT_STATUS_OK; +} + +NTSTATUS passwd_to_SamInfo3(TALLOC_CTX *mem_ctx, + const char *unix_username, + const struct passwd *pwd, + struct netr_SamInfo3 **pinfo3, + struct extra_auth_info *extra) +{ + struct netr_SamInfo3 *info3; + NTSTATUS status; + TALLOC_CTX *tmp_ctx; + const char *domain_name = NULL; + const char *user_name = NULL; + struct dom_sid domain_sid; + struct dom_sid user_sid; + struct dom_sid group_sid; + enum lsa_SidType type; + uint32_t num_sids = 0; + struct dom_sid *user_sids = NULL; + bool is_null; + bool ok; + + tmp_ctx = talloc_stackframe(); + + ok = lookup_name_smbconf(tmp_ctx, + unix_username, + LOOKUP_NAME_ALL, + &domain_name, + &user_name, + &user_sid, + &type); + if (!ok) { + status = NT_STATUS_NO_SUCH_USER; + goto done; + } + + if (type != SID_NAME_USER) { + status = NT_STATUS_NO_SUCH_USER; + goto done; + } + + ok = winbind_lookup_usersids(tmp_ctx, + &user_sid, + &num_sids, + &user_sids); + /* Check if winbind is running */ + if (ok) { + /* + * Winbind is running and the first element of the user_sids + * is the primary group. + */ + if (num_sids > 0) { + group_sid = user_sids[0]; + } + } else { + /* + * Winbind is not running, try to create the group_sid from the + * passwd group id. + */ + + /* + * This can lead to a primary group of S-1-22-2-XX which + * will be rejected by other Samba code. + */ + gid_to_sid(&group_sid, pwd->pw_gid); + } + + /* + * If we are a unix group, or a wellknown/builtin alias, + * set the group_sid to the + * 'Domain Users' RID of 513 which will always resolve to a + * name. + */ + if (sid_check_is_in_unix_groups(&group_sid) || + sid_check_is_in_builtin(&group_sid) || + sid_check_is_in_wellknown_domain(&group_sid)) { + if (sid_check_is_in_unix_users(&user_sid)) { + sid_compose(&group_sid, + get_global_sam_sid(), + DOMAIN_RID_USERS); + } else { + sid_copy(&domain_sid, &user_sid); + sid_split_rid(&domain_sid, NULL); + sid_compose(&group_sid, + &domain_sid, + DOMAIN_RID_USERS); + } + } + + /* Make sure we have a valid group sid */ + is_null = is_null_sid(&group_sid); + if (is_null) { + status = NT_STATUS_NO_SUCH_USER; + goto done; + } + + /* Construct a netr_SamInfo3 from the information we have */ + info3 = talloc_zero(tmp_ctx, struct netr_SamInfo3); + if (!info3) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + info3->base.account_name.string = talloc_strdup(info3, unix_username); + if (info3->base.account_name.string == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + info3->base.logon_domain.string = talloc_strdup(info3, domain_name); + if (info3->base.logon_domain.string == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + ZERO_STRUCT(domain_sid); + + status = SamInfo3_handle_sids(unix_username, + &user_sid, + &group_sid, + info3, + &domain_sid, + extra); + + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + info3->base.domain_sid = dom_sid_dup(info3, &domain_sid); + if (info3->base.domain_sid == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + ok = sid_peek_check_rid(&domain_sid, &group_sid, + &info3->base.primary_gid); + if (!ok) { + struct dom_sid_buf buf1, buf2, buf3; + + DEBUG(1, ("The primary group domain sid(%s) does not " + "match the domain sid(%s) for %s(%s)\n", + dom_sid_str_buf(&group_sid, &buf1), + dom_sid_str_buf(&domain_sid, &buf2), + unix_username, + dom_sid_str_buf(&user_sid, &buf3))); + status = NT_STATUS_INVALID_SID; + goto done; + } + + info3->base.acct_flags = ACB_NORMAL; + + if (num_sids) { + status = group_sids_to_info3(info3, user_sids, num_sids); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + } + + *pinfo3 = talloc_move(mem_ctx, &info3); + + status = NT_STATUS_OK; +done: + talloc_free(tmp_ctx); + + return status; +} diff --git a/source3/auth/server_info_sam.c b/source3/auth/server_info_sam.c new file mode 100644 index 0000000..71a52f8 --- /dev/null +++ b/source3/auth/server_info_sam.c @@ -0,0 +1,124 @@ +/* + 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" +#include "auth.h" +#include "nsswitch/winbind_client.h" +#include "passdb.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_AUTH + + +/*************************************************************************** + 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; + size_t ulen = strlen(username); + const char *nb_name = lp_netbios_name(); + size_t nb_namelen = strlen(nb_name); + + if (ulen == 0 || username[ulen-1] != '$') { + return false; + } + if (nb_namelen != ulen-1) { + return false; + } + ret = strnequal(username, nb_name, ulen-1); + return ret; +} + +/*************************************************************************** + Make (and fill) a user_info struct from a struct samu +***************************************************************************/ + +NTSTATUS make_server_info_sam(TALLOC_CTX *mem_ctx, + struct samu *sampass, + struct auth_serversupplied_info **pserver_info) +{ + struct passwd *pwd; + struct auth_serversupplied_info *server_info; + const char *username = pdb_get_username(sampass); + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + NTSTATUS status; + + server_info = make_server_info(tmp_ctx); + if (server_info == NULL) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + pwd = Get_Pwnam_alloc(tmp_ctx, username); + if (pwd == NULL) { + DEBUG(1, ("User %s in passdb, but getpwnam() fails!\n", + pdb_get_username(sampass))); + status = NT_STATUS_NO_SUCH_USER; + goto out; + } + + status = samu_to_SamInfo3(server_info, + sampass, + lp_netbios_name(), + &server_info->info3, + &server_info->extra); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + + server_info->unix_name = talloc_move(server_info, &pwd->pw_name); + + server_info->utok.gid = pwd->pw_gid; + server_info->utok.uid = pwd->pw_uid; + + if (IS_DC && is_our_machine_account(username)) { + /* + * 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 " + "turning off winbindd requests.\n", username)); + } + + DEBUG(5,("make_server_info_sam: made server info for user %s -> %s\n", + pdb_get_username(sampass), server_info->unix_name)); + + *pserver_info = talloc_move(mem_ctx, &server_info); + + status = NT_STATUS_OK; +out: + talloc_free(tmp_ctx); + + return status; +} diff --git a/source3/auth/token_util.c b/source3/auth/token_util.c new file mode 100644 index 0000000..f508dfd --- /dev/null +++ b/source3/auth/token_util.c @@ -0,0 +1,1306 @@ +/* + * 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" +#include "lib/util_unixsids.h" +#include "system/passwd.h" +#include "auth.h" +#include "secrets.h" +#include "../lib/util/memcache.h" +#include "../librpc/gen_ndr/netlogon.h" +#include "../libcli/security/security.h" +#include "../lib/util/util_pw.h" +#include "passdb.h" +#include "lib/privileges.h" + +/**************************************************************************** + Check for a SID in an struct security_token +****************************************************************************/ + +bool nt_token_check_sid ( const struct dom_sid *sid, const struct security_token *token ) +{ + if ( !sid || !token ) + return False; + + return security_token_has_sid(token, sid); +} + +bool nt_token_check_domain_rid( struct security_token *token, uint32_t rid ) +{ + struct 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 you need to change it. +******************************************************************************/ + +NTSTATUS get_root_nt_token( struct security_token **token ) +{ + struct security_token *for_cache; + struct dom_sid u_sid, g_sid; + struct passwd *pw; + void *cache_data; + NTSTATUS status = NT_STATUS_OK; + + cache_data = memcache_lookup_talloc( + NULL, SINGLETON_CACHE_TALLOC, + data_blob_string_const_null("root_nt_token")); + + if (cache_data != NULL) { + *token = talloc_get_type_abort( + cache_data, struct security_token); + return NT_STATUS_OK; + } + + if ( !(pw = getpwuid(0)) ) { + if ( !(pw = getpwnam("root")) ) { + DBG_ERR("get_root_nt_token: both getpwuid(0) " + "and getpwnam(\"root\") failed!\n"); + return NT_STATUS_NO_SUCH_USER; + } + } + + /* 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); + + status = create_local_nt_token(talloc_tos(), &u_sid, False, + 1, &global_sid_Builtin_Administrators, token); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + security_token_set_privilege(*token, SEC_PRIV_DISK_OPERATOR); + + for_cache = *token; + + memcache_add_talloc( + NULL, SINGLETON_CACHE_TALLOC, + data_blob_string_const_null("root_nt_token"), &for_cache); + + return status; +} + + +/* + * Add alias SIDs from memberships within the partially created token SID list + */ + +NTSTATUS add_aliases(const struct dom_sid *domain_sid, + struct security_token *token) +{ + uint32_t *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->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++) { + struct dom_sid alias_sid; + sid_compose(&alias_sid, domain_sid, aliases[i]); + status = add_sid_to_array_unique(token, &alias_sid, + &token->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 security_token *token, + const struct dom_sid *dom_sid) +{ + struct 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 { + if (dom_sid == NULL) { + return NT_STATUS_INVALID_PARAMETER_MIX; + } + sid_copy(&domadm, dom_sid); + } + sid_append_rid( &domadm, DOMAIN_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->sids, &token->num_sids); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + + return NT_STATUS_OK; +} + +static NTSTATUS add_builtin_guests(struct security_token *token, + const struct dom_sid *dom_sid) +{ + struct dom_sid tmp_sid; + NTSTATUS status; + + /* + * First check the local GUEST account. + */ + sid_compose(&tmp_sid, get_global_sam_sid(), DOMAIN_RID_GUEST); + + if (nt_token_check_sid(&tmp_sid, token)) { + status = add_sid_to_array_unique(token, + &global_sid_Builtin_Guests, + &token->sids, &token->num_sids); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return NT_STATUS_OK; + } + + /* + * First check the local GUESTS group. + */ + sid_compose(&tmp_sid, get_global_sam_sid(), DOMAIN_RID_GUESTS); + + if (nt_token_check_sid(&tmp_sid, token)) { + status = add_sid_to_array_unique(token, + &global_sid_Builtin_Guests, + &token->sids, &token->num_sids); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return NT_STATUS_OK; + } + + if (lp_server_role() != ROLE_DOMAIN_MEMBER) { + return NT_STATUS_OK; + } + + if (dom_sid == NULL) { + return NT_STATUS_INVALID_PARAMETER_MIX; + } + + /* + * First check the domain GUESTS group. + */ + sid_copy(&tmp_sid, dom_sid); + sid_append_rid(&tmp_sid, DOMAIN_RID_GUESTS); + + if (nt_token_check_sid(&tmp_sid, token)) { + status = add_sid_to_array_unique(token, + &global_sid_Builtin_Guests, + &token->sids, &token->num_sids); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return NT_STATUS_OK; + } + + return NT_STATUS_OK; +} + +static NTSTATUS add_local_groups(struct security_token *result, + bool is_guest); + +NTSTATUS get_user_sid_info3_and_extra(const struct netr_SamInfo3 *info3, + const struct extra_auth_info *extra, + struct dom_sid *sid) +{ + /* USER SID */ + if (info3->base.rid == (uint32_t)(-1)) { + /* this is a signal the user was fake and generated, + * the actual SID we want to use is stored in the extra + * sids */ + if (is_null_sid(&extra->user_sid)) { + /* we couldn't find the user sid, bail out */ + DEBUG(3, ("Invalid user SID\n")); + return NT_STATUS_UNSUCCESSFUL; + } + sid_copy(sid, &extra->user_sid); + } else { + sid_copy(sid, info3->base.domain_sid); + sid_append_rid(sid, info3->base.rid); + } + return NT_STATUS_OK; +} + +NTSTATUS create_local_nt_token_from_info3(TALLOC_CTX *mem_ctx, + bool is_guest, + const struct netr_SamInfo3 *info3, + const struct extra_auth_info *extra, + struct security_token **ntok) +{ + struct security_token *usrtok = NULL; + uint32_t session_info_flags = 0; + NTSTATUS status; + uint32_t i; + + DEBUG(10, ("Create local NT token for %s\n", + info3->base.account_name.string)); + + usrtok = talloc_zero(mem_ctx, struct security_token); + if (!usrtok) { + DEBUG(0, ("talloc failed\n")); + return NT_STATUS_NO_MEMORY; + } + + /* Add the user and primary group sid FIRST */ + /* check if the user rid is the special "Domain Guests" rid. + * If so pick the first sid for the extra sids instead as it + * is a local fake account */ + usrtok->sids = talloc_array(usrtok, struct dom_sid, 2); + if (!usrtok->sids) { + TALLOC_FREE(usrtok); + return NT_STATUS_NO_MEMORY; + } + usrtok->num_sids = 2; + + status = get_user_sid_info3_and_extra(info3, extra, &usrtok->sids[0]); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(usrtok); + return status; + } + + /* GROUP SID */ + if (info3->base.primary_gid == (uint32_t)(-1)) { + /* this is a signal the user was fake and generated, + * the actual SID we want to use is stored in the extra + * sids */ + if (is_null_sid(&extra->pgid_sid)) { + /* we couldn't find the user sid, bail out */ + DEBUG(3, ("Invalid group SID\n")); + TALLOC_FREE(usrtok); + return NT_STATUS_UNSUCCESSFUL; + } + sid_copy(&usrtok->sids[1], &extra->pgid_sid); + } else { + sid_copy(&usrtok->sids[1], info3->base.domain_sid); + sid_append_rid(&usrtok->sids[1], + info3->base.primary_gid); + } + + /* 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 = 0; i < info3->base.groups.count; i++) { + struct dom_sid tmp_sid; + + sid_copy(&tmp_sid, info3->base.domain_sid); + sid_append_rid(&tmp_sid, info3->base.groups.rids[i].rid); + + status = add_sid_to_array_unique(usrtok, &tmp_sid, + &usrtok->sids, + &usrtok->num_sids); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("Failed to add SID to nt token\n")); + TALLOC_FREE(usrtok); + return status; + } + } + + /* now also add extra sids if they are not the special user/group + * sids */ + for (i = 0; i < info3->sidcount; i++) { + status = add_sid_to_array_unique(usrtok, + info3->sids[i].sid, + &usrtok->sids, + &usrtok->num_sids); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("Failed to add SID to nt token\n")); + TALLOC_FREE(usrtok); + return status; + } + } + + status = add_local_groups(usrtok, is_guest); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("Failed to add local groups\n")); + TALLOC_FREE(usrtok); + return status; + } + + session_info_flags |= AUTH_SESSION_INFO_DEFAULT_GROUPS; + if (!is_guest) { + session_info_flags |= AUTH_SESSION_INFO_AUTHENTICATED; + } + + status = finalize_local_nt_token(usrtok, session_info_flags); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("Failed to finalize nt token\n")); + TALLOC_FREE(usrtok); + return status; + } + + *ntok = usrtok; + return NT_STATUS_OK; +} + +/******************************************************************* + Create a NT token for the user, expanding local aliases +*******************************************************************/ + +NTSTATUS create_local_nt_token(TALLOC_CTX *mem_ctx, + const struct dom_sid *user_sid, + bool is_guest, + int num_groupsids, + const struct dom_sid *groupsids, + struct security_token **token) +{ + struct security_token *result = NULL; + int i; + NTSTATUS status; + uint32_t session_info_flags = 0; + struct dom_sid_buf buf; + + DEBUG(10, ("Create local NT token for %s\n", + dom_sid_str_buf(user_sid, &buf))); + + if (!(result = talloc_zero(mem_ctx, struct security_token))) { + DEBUG(0, ("talloc failed\n")); + status = NT_STATUS_NO_MEMORY; + goto err; + } + + /* Add the user and primary group sid */ + + status = add_sid_to_array(result, user_sid, + &result->sids, &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { + goto err; + } + + /* For guest, num_groupsids may be zero. */ + if (num_groupsids) { + status = add_sid_to_array(result, &groupsids[0], + &result->sids, + &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { + goto err; + } + } + + /* 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->sids, + &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { + goto err; + } + } + + status = add_local_groups(result, is_guest); + if (!NT_STATUS_IS_OK(status)) { + goto err; + } + + session_info_flags |= AUTH_SESSION_INFO_DEFAULT_GROUPS; + if (!is_guest) { + session_info_flags |= AUTH_SESSION_INFO_AUTHENTICATED; + } + + status = finalize_local_nt_token(result, session_info_flags); + if (!NT_STATUS_IS_OK(status)) { + goto err; + } + + if (is_guest) { + /* + * It's ugly, but for now it's + * needed to add Builtin_Guests + * here, the "local" token only + * consist of S-1-22-* SIDs + * and finalize_local_nt_token() + * doesn't have the chance to + * to detect it need to + * add Builtin_Guests via + * add_builtin_guests(). + */ + status = add_sid_to_array_unique(result, + &global_sid_Builtin_Guests, + &result->sids, + &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("Failed to add SID to nt token\n")); + goto err; + } + } + + *token = result; + return NT_STATUS_SUCCESS; + +err: + TALLOC_FREE(result); + return status; +} + +/*************************************************** + Merge in any groups from /etc/group. +***************************************************/ + +static NTSTATUS add_local_groups(struct security_token *result, + bool is_guest) +{ + gid_t *gids = NULL; + uint32_t getgroups_num_group_sids = 0; + struct passwd *pass = NULL; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + uint32_t i; + + if (is_guest) { + /* + * Guest is a special case. It's always + * a user that can be looked up, but + * result->sids[0] is set to DOMAIN\Guest. + * Lookup by account name instead. + */ + pass = Get_Pwnam_alloc(tmp_ctx, lp_guest_account()); + } else { + uid_t uid; + + /* For non-guest result->sids[0] is always the user sid. */ + if (!sid_to_uid(&result->sids[0], &uid)) { + /* + * Non-mappable SID like SYSTEM. + * Can't be in any /etc/group groups. + */ + TALLOC_FREE(tmp_ctx); + return NT_STATUS_OK; + } + + pass = getpwuid_alloc(tmp_ctx, uid); + if (pass == NULL) { + struct dom_sid_buf buf; + DBG_ERR("SID %s -> getpwuid(%u) failed, is nsswitch configured?\n", + dom_sid_str_buf(&result->sids[0], &buf), + (unsigned int)uid); + TALLOC_FREE(tmp_ctx); + return NT_STATUS_NO_SUCH_USER; + } + } + + if (!pass) { + TALLOC_FREE(tmp_ctx); + return NT_STATUS_UNSUCCESSFUL; + } + + /* + * Now we must get any groups this user has been + * added to in /etc/group and merge them in. + * This has to be done in every code path + * that creates an NT token, as remote users + * may have been added to the local /etc/group + * database. Tokens created merely from the + * info3 structs (via the DC or via the krb5 PAC) + * won't have these local groups. Note the + * groups added here will only be UNIX groups + * (S-1-22-2-XXXX groups) as getgroups_unix_user() + * turns off winbindd before calling getgroups(). + * + * NB. This is duplicating work already + * done in the 'unix_user:' case of + * create_token_from_sid() but won't + * do anything other than be inefficient + * in that case. + */ + + if (!getgroups_unix_user(tmp_ctx, pass->pw_name, pass->pw_gid, + &gids, &getgroups_num_group_sids)) { + DEBUG(1, ("getgroups_unix_user for user %s failed\n", + pass->pw_name)); + TALLOC_FREE(tmp_ctx); + return NT_STATUS_UNSUCCESSFUL; + } + + for (i=0; i<getgroups_num_group_sids; i++) { + NTSTATUS status; + struct dom_sid grp_sid; + gid_to_sid(&grp_sid, gids[i]); + + status = add_sid_to_array_unique(result, + &grp_sid, + &result->sids, + &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("Failed to add UNIX SID to nt token\n")); + TALLOC_FREE(tmp_ctx); + return status; + } + } + TALLOC_FREE(tmp_ctx); + return NT_STATUS_OK; +} + +NTSTATUS finalize_local_nt_token(struct security_token *result, + uint32_t session_info_flags) +{ + struct dom_sid _dom_sid = { 0, }; + struct dom_sid *domain_sid = NULL; + NTSTATUS status; + struct acct_info *info; + bool ok; + + result->privilege_mask = 0; + result->rights_mask = 0; + + if (result->num_sids == 0) { + return NT_STATUS_INVALID_TOKEN; + } + + if (session_info_flags & AUTH_SESSION_INFO_DEFAULT_GROUPS) { + status = add_sid_to_array(result, &global_sid_World, + &result->sids, &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + status = add_sid_to_array(result, &global_sid_Network, + &result->sids, &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + + /* + * Don't expand nested groups of system, anonymous etc + * + * Note that they still get SID_WORLD and SID_NETWORK + * for now in order let existing tests pass. + * + * But SYSTEM doesn't get AUTHENTICATED_USERS + * and ANONYMOUS doesn't get BUILTIN GUESTS anymore. + */ + if (security_token_is_anonymous(result)) { + return NT_STATUS_OK; + } + if (security_token_is_system(result)) { + result->privilege_mask = ~0; + return NT_STATUS_OK; + } + + if (session_info_flags & AUTH_SESSION_INFO_AUTHENTICATED) { + status = add_sid_to_array(result, + &global_sid_Authenticated_Users, + &result->sids, + &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + + /* Add in BUILTIN sids */ + + become_root(); + ok = secrets_fetch_domain_sid(lp_workgroup(), &_dom_sid); + if (ok) { + domain_sid = &_dom_sid; + } else { + DEBUG(3, ("Failed to fetch domain sid for %s\n", + lp_workgroup())); + } + unbecome_root(); + + info = talloc_zero(talloc_tos(), struct acct_info); + if (info == NULL) { + DEBUG(0, ("talloc failed!\n")); + return NT_STATUS_NO_MEMORY; + } + + /* Deal with the BUILTIN\Administrators group. If the SID can + be resolved then assume that the add_aliasmem( S-1-5-32 ) + handled it. */ + + status = pdb_get_aliasinfo(&global_sid_Builtin_Administrators, info); + if (!NT_STATUS_IS_OK(status)) { + + become_root(); + status = create_builtin_administrators(domain_sid); + unbecome_root(); + + if (NT_STATUS_EQUAL(status, NT_STATUS_PROTOCOL_UNREACHABLE)) { + /* Add BUILTIN\Administrators directly to token. */ + status = add_builtin_administrators(result, domain_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. */ + + status = pdb_get_aliasinfo(&global_sid_Builtin_Users, info); + if (!NT_STATUS_IS_OK(status)) { + + become_root(); + status = create_builtin_users(domain_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 the BUILTIN\Guests group. If the SID can + * be resolved then assume that the add_aliasmem( S-1-5-32 ) + * handled it. + */ + status = pdb_get_aliasinfo(&global_sid_Builtin_Guests, info); + if (!NT_STATUS_IS_OK(status)) { + + become_root(); + status = create_builtin_guests(domain_sid); + unbecome_root(); + + /* + * NT_STATUS_PROTOCOL_UNREACHABLE: + * => winbindd is not running. + * + * NT_STATUS_ACCESS_DENIED: + * => no idmap config at all + * and wbint_AllocateGid()/winbind_allocate_gid() + * failed. + * + * NT_STATUS_NO_SUCH_GROUP: + * => no idmap config at all and + * "tdbsam:map builtin = no" means + * wbint_Sids2UnixIDs() fails. + */ + if (NT_STATUS_EQUAL(status, NT_STATUS_PROTOCOL_UNREACHABLE) || + NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) || + NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_GROUP)) { + /* + * Add BUILTIN\Guests directly to token. + * But only if the token already indicates + * real guest access by: + * - local GUEST account + * - local GUESTS group + * - domain GUESTS group + * + * Even if a user was authenticated, it + * can be member of a guest related group. + */ + status = add_builtin_guests(result, domain_sid); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("Failed to check for local " + "Guests membership (%s)\n", + nt_errstr(status))); + /* + * This is a hard error. + */ + return status; + } + } else if (!NT_STATUS_IS_OK(status)) { + DEBUG(2, ("Failed to create " + "BUILTIN\\Guests group %s! Can " + "Winbind allocate gids?\n", + nt_errstr(status))); + /* + * This is a hard error. + */ + return status; + } + } + + TALLOC_FREE(info); + + /* 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(); + return status; + } + + /* Finally the builtin ones */ + + status = add_aliases(&global_sid_Builtin, result); + + if (!NT_STATUS_IS_OK(status)) { + unbecome_root(); + return status; + } + + unbecome_root(); + } + + if (session_info_flags & AUTH_SESSION_INFO_NTLM) { + struct dom_sid tmp_sid = { 0, }; + + ok = dom_sid_parse(SID_NT_NTLM_AUTHENTICATION, &tmp_sid); + if (!ok) { + return NT_STATUS_NO_MEMORY; + } + + status = add_sid_to_array(result, + &tmp_sid, + &result->sids, + &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + + if (session_info_flags & AUTH_SESSION_INFO_SIMPLE_PRIVILEGES) { + if (security_token_has_builtin_administrators(result)) { + result->privilege_mask = ~0; + } + } else { + /* Add privileges based on current user sids */ + get_privileges_for_sids(&result->privilege_mask, result->sids, + result->num_sids); + } + + return NT_STATUS_OK; +} + +/**************************************************************************** + 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])); +} + +/* + * Create an artificial NT token given just a domain SID. + * + * 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. + */ + +static NTSTATUS create_token_from_sid(TALLOC_CTX *mem_ctx, + const struct dom_sid *user_sid, + bool is_guest, + uid_t *uid, gid_t *gid, + char **found_username, + struct security_token **token) +{ + NTSTATUS result = NT_STATUS_NO_SUCH_USER; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + gid_t *gids; + struct dom_sid *group_sids; + struct dom_sid tmp_sid; + uint32_t num_group_sids; + uint32_t num_gids; + uint32_t i; + uint32_t high, low; + bool range_ok; + struct dom_sid_buf buf; + + if (sid_check_is_in_our_sam(user_sid)) { + bool ret; + uint32_t pdb_num_group_sids; + /* 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) failed\n", + dom_sid_str_buf(user_sid, &buf))); + DEBUGADD(1, ("Fall back to unix user\n")); + goto unix_user; + } + + result = pdb_enum_group_memberships(tmp_ctx, sam_acct, + &group_sids, &gids, + &pdb_num_group_sids); + if (!NT_STATUS_IS_OK(result)) { + DEBUG(1, ("enum_group_memberships failed for %s: " + "%s\n", + dom_sid_str_buf(user_sid, &buf), + nt_errstr(result))); + DEBUGADD(1, ("Fall back to unix uid lookup\n")); + goto unix_user; + } + num_group_sids = pdb_num_group_sids; + + /* see the smb_panic() in pdb_default_enum_group_memberships */ + SMB_ASSERT(num_group_sids > 0); + + /* Ensure we're returning the found_username on the right context. */ + *found_username = talloc_strdup(mem_ctx, + pdb_get_username(sam_acct)); + + if (*found_username == NULL) { + result = NT_STATUS_NO_MEMORY; + goto done; + } + + /* + * If the SID from lookup_name() was the guest sid, passdb knows + * about the mapping of guest sid to lp_guest_account() + * username and will return the unix_pw info for a guest + * user. Use it if it's there, else lookup the *uid details + * using Get_Pwnam_alloc(). See bug #6291 for details. JRA. + */ + + /* We must always assign the *uid. */ + if (sam_acct->unix_pw == NULL) { + struct passwd *pwd = Get_Pwnam_alloc(sam_acct, *found_username ); + if (!pwd) { + DEBUG(10, ("Get_Pwnam_alloc failed for %s\n", + *found_username)); + result = NT_STATUS_NO_SUCH_USER; + goto done; + } + result = samu_set_unix(sam_acct, pwd ); + if (!NT_STATUS_IS_OK(result)) { + DEBUG(10, ("samu_set_unix failed for %s\n", + *found_username)); + result = NT_STATUS_NO_SUCH_USER; + goto done; + } + } + *uid = sam_acct->unix_pw->pw_uid; + + } else if (sid_check_is_in_unix_users(user_sid)) { + uint32_t getgroups_num_group_sids; + /* 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: + + if (!sid_to_uid(user_sid, uid)) { + DEBUG(1, ("unix_user case, sid_to_uid for %s failed\n", + dom_sid_str_buf(user_sid, &buf))); + result = NT_STATUS_NO_SUCH_USER; + goto done; + } + + uid_to_unix_users_sid(*uid, &tmp_sid); + user_sid = &tmp_sid; + + pass = getpwuid_alloc(tmp_ctx, *uid); + if (pass == NULL) { + DEBUG(1, ("getpwuid(%u) failed\n", + (unsigned int)*uid)); + goto done; + } + + if (!getgroups_unix_user(tmp_ctx, pass->pw_name, pass->pw_gid, + &gids, &getgroups_num_group_sids)) { + DEBUG(1, ("getgroups_unix_user for user %s failed\n", + pass->pw_name)); + goto done; + } + num_group_sids = getgroups_num_group_sids; + + group_sids = talloc_array(tmp_ctx, struct dom_sid, num_group_sids); + if (group_sids == NULL) { + DEBUG(1, ("talloc_array failed\n")); + result = NT_STATUS_NO_MEMORY; + goto done; + } + + 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); + + /* Ensure we're returning the found_username on the right context. */ + *found_username = talloc_strdup(mem_ctx, pass->pw_name); + if (*found_username == NULL) { + result = NT_STATUS_NO_MEMORY; + goto done; + } + } 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. */ + + /* We must always assign the *uid. */ + if (!sid_to_uid(user_sid, uid)) { + DEBUG(1, ("winbindd case, sid_to_uid for %s failed\n", + dom_sid_str_buf(user_sid, &buf))); + result = NT_STATUS_NO_SUCH_USER; + goto done; + } + + num_group_sids = 1; + group_sids = talloc_array(tmp_ctx, struct dom_sid, num_group_sids); + if (group_sids == NULL) { + DEBUG(1, ("talloc_array failed\n")); + result = NT_STATUS_NO_MEMORY; + goto done; + } + + gids = talloc_array(tmp_ctx, gid_t, num_group_sids); + if (gids == NULL) { + result = NT_STATUS_NO_MEMORY; + goto done; + } + + sid_copy(&group_sids[0], user_sid); + sid_split_rid(&group_sids[0], NULL); + sid_append_rid(&group_sids[0], DOMAIN_RID_USERS); + + if (!sid_to_gid(&group_sids[0], &gids[0])) { + DEBUG(1, ("sid_to_gid(%s) failed\n", + dom_sid_str_buf(&group_sids[0], &buf))); + goto done; + } + + *found_username = NULL; + } + + *gid = gids[0]; + + /* 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; + range_ok = lp_idmap_default_range(&low, &high); + for ( i=0; i<num_gids; i++ ) { + struct dom_sid unix_group_sid; + + /* don't pickup anything managed by Winbind */ + if (range_ok && (gids[i] >= low) && (gids[i] <= high)) { + continue; + } + + gid_to_unix_groups_sid(gids[i], &unix_group_sid); + + 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. */ + result = create_local_nt_token(mem_ctx, user_sid, + is_guest, num_group_sids, group_sids, token); + + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + + result = NT_STATUS_OK; + done: + TALLOC_FREE(tmp_ctx); + return result; +} + +/* + * Create an artificial NT token given just a username. (Initially intended + * 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 security_token **token) +{ + NTSTATUS result = NT_STATUS_NO_SUCH_USER; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + struct dom_sid user_sid; + enum lsa_SidType type; + + 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; + } + + result = create_token_from_sid(mem_ctx, &user_sid, is_guest, uid, gid, found_username, token); + + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + + /* + * If result == NT_STATUS_OK then + * we know we have a valid token. Ensure + * we also have a valid username to match. + */ + + if (*found_username == NULL) { + *found_username = talloc_strdup(mem_ctx, username); + if (*found_username == NULL) { + result = NT_STATUS_NO_MEMORY; + } + } + +done: + TALLOC_FREE(tmp_ctx); + return result; +} + +/*************************************************************************** + Build upon create_token_from_sid: + + Expensive helper function to figure out whether a user given its sid is + member of a particular group. +***************************************************************************/ + +bool user_sid_in_group_sid(const struct dom_sid *sid, const struct dom_sid *group_sid) +{ + NTSTATUS status; + uid_t uid; + gid_t gid; + char *found_username; + struct security_token *token; + bool result = false; + enum lsa_SidType type; + TALLOC_CTX *mem_ctx = talloc_stackframe(); + struct dom_sid_buf buf; + + if (!lookup_sid(mem_ctx, sid, + NULL, NULL, &type)) { + DEBUG(1, ("lookup_sid for %s failed\n", + dom_sid_str_buf(sid, &buf))); + goto done; + } + + if (type != SID_NAME_USER) { + DEBUG(5, ("%s is a %s, not a user\n", + dom_sid_str_buf(sid, &buf), + sid_type_lookup(type))); + goto done; + } + + status = create_token_from_sid(mem_ctx, sid, False, + &uid, &gid, &found_username, + &token); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("could not create token for %s\n", + dom_sid_str_buf(sid, &buf))); + goto done; + } + + result = security_token_has_sid(token, group_sid); + +done: + TALLOC_FREE(mem_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 struct dom_sid *group_sid) +{ + NTSTATUS status; + uid_t uid; + gid_t gid; + char *found_username; + struct security_token *token; + bool result; + TALLOC_CTX *mem_ctx = talloc_stackframe(); + + 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)); + TALLOC_FREE(mem_ctx); + return False; + } + + result = security_token_has_sid(token, group_sid); + + TALLOC_FREE(mem_ctx); + return result; +} + +bool user_in_group(const char *username, const char *groupname) +{ + TALLOC_CTX *mem_ctx = talloc_stackframe(); + struct dom_sid group_sid; + bool ret; + + 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); +} + +/* END */ diff --git a/source3/auth/user_info.c b/source3/auth/user_info.c new file mode 100644 index 0000000..81192a5 --- /dev/null +++ b/source3/auth/user_info.c @@ -0,0 +1,169 @@ +/* + Unix SMB/CIFS implementation. + Authentication utility functions + Copyright (C) Volker Lendecke 2010 + + 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" +#include "auth.h" +#include "librpc/gen_ndr/samr.h" +#include "../lib/tsocket/tsocket.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_AUTH + +static int clear_samr_Password(struct samr_Password *password) +{ + memset(password->hash, '\0', sizeof(password->hash)); + return 0; +} + +static int clear_string(char *password) +{ + memset(password, '\0', strlen(password)); + return 0; +} + +/**************************************************************************** + Create an auth_usersupplied_data structure +****************************************************************************/ + +NTSTATUS make_user_info(TALLOC_CTX *mem_ctx, + struct auth_usersupplied_info **ret_user_info, + const char *smb_name, + const char *internal_username, + const char *client_domain, + const char *domain, + const char *workstation_name, + const struct tsocket_address *remote_address, + const struct tsocket_address *local_address, + const char *service_description, + const DATA_BLOB *lm_pwd, + const DATA_BLOB *nt_pwd, + const struct samr_Password *lm_interactive_pwd, + const struct samr_Password *nt_interactive_pwd, + const char *plaintext_password, + enum auth_password_state password_state) +{ + struct auth_usersupplied_info *user_info; + *ret_user_info = NULL; + + DEBUG(5,("attempting to make a user_info for %s (%s)\n", internal_username, smb_name)); + + user_info = talloc_zero(mem_ctx, struct auth_usersupplied_info); + if (user_info == NULL) { + DEBUG(0,("talloc failed for user_info\n")); + return NT_STATUS_NO_MEMORY; + } + + DEBUG(5,("making strings for %s's user_info struct\n", internal_username)); + + user_info->client.account_name = talloc_strdup(user_info, smb_name); + if (user_info->client.account_name == NULL) { + goto nomem; + } + + user_info->mapped.account_name = talloc_strdup(user_info, internal_username); + if (user_info->mapped.account_name == NULL) { + goto nomem; + } + + user_info->mapped.domain_name = talloc_strdup(user_info, domain); + if (user_info->mapped.domain_name == NULL) { + goto nomem; + } + + user_info->client.domain_name = talloc_strdup(user_info, client_domain); + if (user_info->client.domain_name == NULL) { + goto nomem; + } + + user_info->workstation_name = talloc_strdup(user_info, workstation_name); + if (user_info->workstation_name == NULL) { + goto nomem; + } + + user_info->remote_host = tsocket_address_copy(remote_address, user_info); + if (user_info->remote_host == NULL) { + goto nomem; + } + + if (local_address != NULL) { + user_info->local_host = tsocket_address_copy(local_address, + user_info); + if (user_info->local_host == NULL) { + goto nomem; + } + } + + user_info->service_description = talloc_strdup(user_info, service_description); + if (user_info->service_description == NULL) { + goto nomem; + } + + DEBUG(5,("making blobs for %s's user_info struct\n", internal_username)); + + if (lm_pwd && lm_pwd->data) { + user_info->password.response.lanman = data_blob_talloc(user_info, lm_pwd->data, lm_pwd->length); + if (user_info->password.response.lanman.data == NULL) { + goto nomem; + } + } + if (nt_pwd && nt_pwd->data) { + user_info->password.response.nt = data_blob_talloc(user_info, nt_pwd->data, nt_pwd->length); + if (user_info->password.response.nt.data == NULL) { + goto nomem; + } + } + if (lm_interactive_pwd) { + user_info->password.hash.lanman = talloc(user_info, struct samr_Password); + if (user_info->password.hash.lanman == NULL) { + goto nomem; + } + memcpy(user_info->password.hash.lanman->hash, lm_interactive_pwd->hash, + sizeof(user_info->password.hash.lanman->hash)); + talloc_set_destructor(user_info->password.hash.lanman, clear_samr_Password); + } + + if (nt_interactive_pwd) { + user_info->password.hash.nt = talloc(user_info, struct samr_Password); + if (user_info->password.hash.nt == NULL) { + goto nomem; + } + memcpy(user_info->password.hash.nt->hash, nt_interactive_pwd->hash, + sizeof(user_info->password.hash.nt->hash)); + talloc_set_destructor(user_info->password.hash.nt, clear_samr_Password); + } + + if (plaintext_password) { + user_info->password.plaintext = talloc_strdup(user_info, plaintext_password); + if (user_info->password.plaintext == NULL) { + goto nomem; + } + talloc_set_destructor(user_info->password.plaintext, clear_string); + } + + user_info->password_state = password_state; + + user_info->logon_parameters = 0; + + DEBUG(10,("made a user_info for %s (%s)\n", internal_username, smb_name)); + *ret_user_info = user_info; + return NT_STATUS_OK; +nomem: + TALLOC_FREE(user_info); + return NT_STATUS_NO_MEMORY; +} diff --git a/source3/auth/user_krb5.c b/source3/auth/user_krb5.c new file mode 100644 index 0000000..169bf56 --- /dev/null +++ b/source3/auth/user_krb5.c @@ -0,0 +1,263 @@ +/* + Unix SMB/CIFS implementation. + Authentication utility functions + Copyright (C) Simo Sorce 2010 + + 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" +#include "auth.h" +#include "librpc/gen_ndr/krb5pac.h" +#include "nsswitch/libwbclient/wbclient.h" +#include "passdb.h" +#include "lib/param/loadparm.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_AUTH + +#ifdef HAVE_KRB5 +NTSTATUS get_user_from_kerberos_info(TALLOC_CTX *mem_ctx, + const char *cli_name, + const char *princ_name, + bool *is_mapped, + bool *mapped_to_guest, + char **ntuser, + char **ntdomain, + char **username, + struct passwd **_pw) +{ + NTSTATUS status; + const char *domain = NULL; + const char *realm = NULL; + char *user = NULL; + char *p; + char *fuser = NULL; + char *unixuser = NULL; + struct passwd *pw = NULL; + bool may_retry = false; + + DEBUG(3, ("Kerberos ticket principal name is [%s]\n", princ_name)); + + p = strchr_m(princ_name, '@'); + if (!p) { + DEBUG(3, ("[%s] Doesn't look like a valid principal\n", + princ_name)); + return NT_STATUS_LOGON_FAILURE; + } + + user = talloc_strndup(mem_ctx, princ_name, p - princ_name); + if (!user) { + return NT_STATUS_NO_MEMORY; + } + + realm = p + 1; + + if (!strequal(realm, lp_realm())) { + DEBUG(3, ("Ticket for foreign realm %s@%s\n", user, realm)); + if (!lp_allow_trusted_domains()) { + return NT_STATUS_LOGON_FAILURE; + } + domain = realm; + } else { + domain = lp_workgroup(); + may_retry = true; + } + + fuser = talloc_asprintf(mem_ctx, + "%s%c%s", + domain, + *lp_winbind_separator(), + user); + if (!fuser) { + return NT_STATUS_NO_MEMORY; + } + + *is_mapped = map_username(mem_ctx, fuser, &fuser); + if (!fuser) { + return NT_STATUS_NO_MEMORY; + } + *mapped_to_guest = false; + + pw = smb_getpwnam(mem_ctx, fuser, &unixuser, true); + if (may_retry && pw == NULL && !*is_mapped) { + fuser = talloc_strdup(mem_ctx, user); + if (!fuser) { + return NT_STATUS_NO_MEMORY; + } + pw = smb_getpwnam(mem_ctx, fuser, &unixuser, true); + } + if (pw) { + if (!unixuser) { + return NT_STATUS_NO_MEMORY; + } + /* if a real user check pam account restrictions */ + /* only really performed if "obey pam restriction" is true */ + /* do this before an eventual mapping to guest occurs */ + status = smb_pam_accountcheck(pw->pw_name, cli_name); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("PAM account restrictions prevent user " + "[%s] login\n", unixuser)); + return status; + } + } + if (!pw) { + + /* this was originally the behavior of Samba 2.2, if a user + did not have a local uid but has been authenticated, then + map them to a guest account */ + + if (lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_UID) { + *mapped_to_guest = true; + fuser = talloc_strdup(mem_ctx, lp_guest_account()); + if (!fuser) { + return NT_STATUS_NO_MEMORY; + } + pw = smb_getpwnam(mem_ctx, fuser, &unixuser, false); + } + + /* extra sanity check that the guest account is valid */ + if (!pw) { + DBG_NOTICE("Username %s is invalid on this system\n", + fuser); + return NT_STATUS_LOGON_FAILURE; + } + } + + if (!unixuser) { + return NT_STATUS_NO_MEMORY; + } + + *username = talloc_strdup(mem_ctx, unixuser); + if (!*username) { + return NT_STATUS_NO_MEMORY; + } + *ntuser = user; + *ntdomain = talloc_strdup(mem_ctx, domain); + if (*ntdomain == NULL) { + return NT_STATUS_NO_MEMORY; + } + + *_pw = pw; + + return NT_STATUS_OK; +} + +NTSTATUS make_session_info_krb5(TALLOC_CTX *mem_ctx, + char *ntuser, + char *ntdomain, + char *username, + struct passwd *pw, + bool mapped_to_guest, bool username_was_mapped, + struct auth_session_info **session_info) +{ + NTSTATUS status; + struct auth_serversupplied_info *server_info; + + if (mapped_to_guest) { + status = make_server_info_guest(mem_ctx, &server_info); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("make_server_info_guest failed: %s!\n", + nt_errstr(status))); + return status; + } + + } else { + /* + * We didn't get a PAC, we have to make up the user + * ourselves. Try to ask the pdb backend to provide + * SID consistency with ntlmssp session setup + */ + struct samu *sampass; + + sampass = samu_new(talloc_tos()); + if (sampass == NULL) { + return NT_STATUS_NO_MEMORY; + } + + if (pdb_getsampwnam(sampass, username)) { + DEBUG(10, ("found user %s in passdb, calling " + "make_server_info_sam\n", username)); + status = make_server_info_sam(mem_ctx, + sampass, + &server_info); + } else { + /* + * User not in passdb, make it up artificially + */ + DEBUG(10, ("didn't find user %s in passdb, calling " + "make_server_info_pw\n", username)); + status = make_server_info_pw(mem_ctx, + username, + pw, + &server_info); + } + + TALLOC_FREE(sampass); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("make_server_info_[sam|pw] failed: %s!\n", + nt_errstr(status))); + return status; + } + + /* make_server_info_pw does not set the domain. Without this + * we end up with the local netbios name in substitutions for + * %D. */ + + if (server_info->info3 != NULL) { + server_info->info3->base.logon_domain.string = + talloc_strdup(server_info->info3, ntdomain); + } + } + + server_info->nss_token |= username_was_mapped; + + status = create_local_token(mem_ctx, server_info, NULL, ntuser, session_info); + talloc_free(server_info); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10,("failed to create local token: %s\n", + nt_errstr(status))); + return status; + } + + return NT_STATUS_OK; +} + +#else /* HAVE_KRB5 */ +NTSTATUS get_user_from_kerberos_info(TALLOC_CTX *mem_ctx, + const char *cli_name, + const char *princ_name, + bool *is_mapped, + bool *mapped_to_guest, + char **ntuser, + char **ntdomain, + char **username, + struct passwd **_pw) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS make_session_info_krb5(TALLOC_CTX *mem_ctx, + char *ntuser, + char *ntdomain, + char *username, + struct passwd *pw, + bool mapped_to_guest, bool username_was_mapped, + struct auth_session_info **session_info) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +#endif /* HAVE_KRB5 */ diff --git a/source3/auth/user_util.c b/source3/auth/user_util.c new file mode 100644 index 0000000..cd97d62 --- /dev/null +++ b/source3/auth/user_util.c @@ -0,0 +1,447 @@ +/* + Unix SMB/CIFS implementation. + Username handling + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Jeremy Allison 1997-2001. + 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" +#include "system/filesys.h" +#include "auth.h" +#include "lib/gencache.h" + +/******************************************************************* + Map a username from a dos name to a unix name by looking in the username + map. Note that this modifies the name in place. + This is the main function that should be called *once* on + any incoming or new username - in order to canonicalize the name. + This is being done to de-couple the case conversions from the user mapping + function. Previously, the map_username was being called + every time Get_Pwnam_alloc was called. + Returns True if username was changed, false otherwise. +********************************************************************/ + +static char *last_from = NULL; +static char *last_to = NULL; + +static const char *get_last_from(void) +{ + if (!last_from) { + return ""; + } + return last_from; +} + +static const char *get_last_to(void) +{ + if (!last_to) { + return ""; + } + return last_to; +} + +static bool set_last_from_to(const char *from, const char *to) +{ + char *orig_from = last_from; + char *orig_to = last_to; + + last_from = SMB_STRDUP(from); + last_to = SMB_STRDUP(to); + + SAFE_FREE(orig_from); + SAFE_FREE(orig_to); + + if (!last_from || !last_to) { + SAFE_FREE(last_from); + SAFE_FREE(last_to); + return false; + } + return true; +} + +static char *skip_space(char *s) +{ + while (isspace((int)(*s))) { + s += 1; + } + return s; +} + +static bool fetch_map_from_gencache(TALLOC_CTX *ctx, + const char *user_in, + char **p_user_out) +{ + char *key, *value; + bool found; + + if (lp_username_map_cache_time() == 0) { + return false; + } + + key = talloc_asprintf_strupper_m(ctx, "USERNAME_MAP/%s", + user_in); + if (key == NULL) { + return false; + } + found = gencache_get(key, ctx, &value, NULL); + TALLOC_FREE(key); + if (!found) { + return false; + } + TALLOC_FREE(*p_user_out); + *p_user_out = value; + if (!*p_user_out) { + return false; + } + return true; +} + +static void store_map_in_gencache(TALLOC_CTX *ctx, const char *from, const char *to) +{ + char *key; + int cache_time = lp_username_map_cache_time(); + + if (cache_time == 0) { + return; + } + + key = talloc_asprintf_strupper_m(ctx, "USERNAME_MAP/%s", + from); + if (key == NULL) { + return; + } + gencache_set(key, to, cache_time + time(NULL)); + TALLOC_FREE(key); +} + +/**************************************************************************** + Check if a user is in a netgroup user list. If at first we don't succeed, + try lower case. +****************************************************************************/ + +bool user_in_netgroup(TALLOC_CTX *ctx, const char *user, const char *ngname) +{ +#if defined(HAVE_NETGROUP) && defined(HAVE_INNETGR) + char nis_domain_buf[256]; + const char *nis_domain = NULL; + char *lowercase_user = NULL; + + if (getdomainname(nis_domain_buf, sizeof(nis_domain_buf)) == 0) { + nis_domain = &nis_domain_buf[0]; + } else { + DEBUG(5,("Unable to get default yp domain, " + "let's try without specifying it\n")); + nis_domain = NULL; + } + + DEBUG(5,("looking for user %s of domain %s in netgroup %s\n", + user, nis_domain ? nis_domain : "(ANY)", ngname)); + + if (innetgr(ngname, NULL, user, nis_domain)) { + DEBUG(5,("user_in_netgroup: Found\n")); + return true; + } + + /* + * Ok, innetgr is case sensitive. Try once more with lowercase + * just in case. Attempt to fix #703. JRA. + */ + lowercase_user = talloc_strdup(ctx, user); + if (!lowercase_user) { + return false; + } + if (!strlower_m(lowercase_user)) { + TALLOC_FREE(lowercase_user); + return false; + } + + if (strcmp(user,lowercase_user) == 0) { + /* user name was already lower case! */ + TALLOC_FREE(lowercase_user); + return false; + } + + DEBUG(5,("looking for user %s of domain %s in netgroup %s\n", + lowercase_user, nis_domain ? nis_domain : "(ANY)", ngname)); + + if (innetgr(ngname, NULL, lowercase_user, nis_domain)) { + DEBUG(5,("user_in_netgroup: Found\n")); + TALLOC_FREE(lowercase_user); + return true; + } +#endif /* HAVE_NETGROUP and HAVE_INNETGR */ + return false; +} + +/**************************************************************************** + Check if a user is in a user list - can check combinations of UNIX + and netgroup lists. +****************************************************************************/ + +bool user_in_list(TALLOC_CTX *ctx, const char *user, const char * const *list) +{ + if (!list || !*list) + return False; + + DEBUG(10,("user_in_list: checking user %s in list\n", user)); + + while (*list) { + + DEBUG(10,("user_in_list: checking user |%s| against |%s|\n", + user, *list)); + + /* + * Check raw username. + */ + if (strequal(user, *list)) + return(True); + + /* + * Now check to see if any combination + * of UNIX and netgroups has been specified. + */ + + if(**list == '@') { + /* + * Old behaviour. Check netgroup list + * followed by UNIX list. + */ + if(user_in_netgroup(ctx, user, *list +1)) + return True; + if(user_in_group(user, *list +1)) + return True; + } else if (**list == '+') { + + if((*(*list +1)) == '&') { + /* + * Search UNIX list followed by netgroup. + */ + if(user_in_group(user, *list +2)) + return True; + if(user_in_netgroup(ctx, user, *list +2)) + return True; + + } else { + + /* + * Just search UNIX list. + */ + + if(user_in_group(user, *list +1)) + return True; + } + + } else if (**list == '&') { + + if(*(*list +1) == '+') { + /* + * Search netgroup list followed by UNIX list. + */ + if(user_in_netgroup(ctx, user, *list +2)) + return True; + if(user_in_group(user, *list +2)) + return True; + } else { + /* + * Just search netgroup list. + */ + if(user_in_netgroup(ctx, user, *list +1)) + return True; + } + } + + list++; + } + return(False); +} + +bool map_username(TALLOC_CTX *ctx, const char *user_in, char **p_user_out) +{ + const struct loadparm_substitution *lp_sub = + loadparm_s3_global_substitution(); + FILE *f; + char *mapfile = lp_username_map(talloc_tos(), lp_sub); + char *s; + char buf[512]; + bool mapped_user = False; + char *cmd = lp_username_map_script(talloc_tos(), lp_sub); + + *p_user_out = NULL; + + if (!user_in) + return false; + + /* Initially make a copy of the incoming name. */ + *p_user_out = talloc_strdup(ctx, user_in); + if (!*p_user_out) { + return false; + } + + if (strequal(user_in,get_last_to())) + return false; + + if (strequal(user_in,get_last_from())) { + DEBUG(3,("Mapped user %s to %s\n",user_in,get_last_to())); + TALLOC_FREE(*p_user_out); + *p_user_out = talloc_strdup(ctx, get_last_to()); + return true; + } + + if (fetch_map_from_gencache(ctx, user_in, p_user_out)) { + return true; + } + + /* first try the username map script */ + + if ( *cmd ) { + char **qlines; + char *command = NULL; + int numlines, ret, fd; + + command = talloc_asprintf(ctx, + "%s \"%s\"", + cmd, + user_in); + if (!command) { + return false; + } + + DEBUG(10,("Running [%s]\n", command)); + ret = smbrun(command, &fd, NULL); + DEBUGADD(10,("returned [%d]\n", ret)); + + TALLOC_FREE(command); + + if ( ret != 0 ) { + if (fd != -1) + close(fd); + return False; + } + + numlines = 0; + qlines = fd_lines_load(fd, &numlines, 0, ctx); + DEBUGADD(10,("Lines returned = [%d]\n", numlines)); + close(fd); + + /* should be either no lines or a single line with the mapped username */ + + if (numlines && qlines) { + DEBUG(3,("Mapped user %s to %s\n", user_in, qlines[0] )); + set_last_from_to(user_in, qlines[0]); + store_map_in_gencache(ctx, user_in, qlines[0]); + TALLOC_FREE(*p_user_out); + *p_user_out = talloc_strdup(ctx, qlines[0]); + if (!*p_user_out) { + return false; + } + } + + TALLOC_FREE(qlines); + + return numlines != 0; + } + + /* ok. let's try the mapfile */ + if (!*mapfile) + return False; + + f = fopen(mapfile, "r"); + if (!f) { + DEBUG(0,("can't open username map %s. Error %s\n",mapfile, strerror(errno) )); + return False; + } + + DEBUG(4,("Scanning username map %s\n",mapfile)); + + while((s=fgets_slash(NULL,buf,sizeof(buf),f))!=NULL) { + char *unixname = s; + char *dosname = strchr_m(unixname,'='); + char **dosuserlist; + bool return_if_mapped = False; + + if (!dosname) + continue; + + *dosname++ = 0; + + unixname = skip_space(unixname); + + if ('!' == *unixname) { + return_if_mapped = True; + unixname = skip_space(unixname+1); + } + + if (!*unixname || strchr_m("#;",*unixname)) + continue; + + { + int l = strlen(unixname); + while (l && isspace((int)unixname[l-1])) { + unixname[l-1] = 0; + l--; + } + } + + /* skip lines like 'user = ' */ + + dosuserlist = str_list_make_v3(ctx, dosname, NULL); + if (!dosuserlist) { + DEBUG(0,("Bad username map entry. Unable to build user list. Ignoring.\n")); + continue; + } + + if (strchr_m(dosname,'*') || + user_in_list(ctx, user_in, (const char * const *)dosuserlist)) { + DEBUG(3,("Mapped user %s to %s\n",user_in,unixname)); + mapped_user = True; + + set_last_from_to(user_in, unixname); + store_map_in_gencache(ctx, user_in, unixname); + TALLOC_FREE(*p_user_out); + *p_user_out = talloc_strdup(ctx, unixname); + if (!*p_user_out) { + TALLOC_FREE(dosuserlist); + fclose(f); + return false; + } + + if ( return_if_mapped ) { + TALLOC_FREE(dosuserlist); + fclose(f); + return True; + } + } + + TALLOC_FREE(dosuserlist); + } + + fclose(f); + + /* + * If we didn't successfully map a user in the loop above, + * setup the last_from and last_to as an optimization so + * that we don't scan the file again for the same user. + */ + if (!mapped_user) { + DEBUG(8, ("The user '%s' has no mapping. " + "Skip it next time.\n", user_in)); + set_last_from_to(user_in, user_in); + store_map_in_gencache(ctx, user_in, user_in); + } + + return mapped_user; +} diff --git a/source3/auth/wscript_build b/source3/auth/wscript_build new file mode 100644 index 0000000..97008fc --- /dev/null +++ b/source3/auth/wscript_build @@ -0,0 +1,62 @@ +#!/usr/bin/env python + +bld.SAMBA3_SUBSYSTEM('TOKEN_UTIL', + source='token_util.c', + deps='samba-util pdb') + +bld.SAMBA3_SUBSYSTEM('USER_UTIL', + source='user_util.c', + deps='TOKEN_UTIL') + +bld.SAMBA3_SUBSYSTEM('AUTH_COMMON', + source='''auth_util.c + check_samsec.c + server_info.c + server_info_sam.c + user_info.c''', + deps='TOKEN_UTIL DCUTIL USER_UTIL common_auth') + +bld.SAMBA3_LIBRARY('auth', + source='''auth.c + user_krb5.c + auth_ntlmssp.c + auth_generic.c''', + deps='''PLAINTEXT_AUTH SLCACHE DCUTIL TOKEN_UTIL AUTH_COMMON libcli_netlogon3 samba-hostconfig MESSAGING''', + private_library=True) + +bld.SAMBA3_MODULE('auth_sam', + subsystem='auth', + source='auth_sam.c', + deps='samba-util', + init_function='', + internal_module=True) + +bld.SAMBA3_MODULE('auth_unix', + subsystem='auth', + source='auth_unix.c', + deps='samba-util', + init_function='', + internal_module=bld.SAMBA3_IS_STATIC_MODULE('auth_unix'), + enabled=bld.SAMBA3_IS_ENABLED_MODULE('auth_unix')) + +bld.SAMBA3_MODULE('auth_winbind', + subsystem='auth', + source='auth_winbind.c', + deps='samba-util', + init_function='', + internal_module=True) + +bld.SAMBA3_MODULE('auth_builtin', + subsystem='auth', + source='auth_builtin.c', + deps='samba-util', + init_function='', + internal_module=True) + +bld.SAMBA3_MODULE('auth_samba4', + subsystem='auth', + source='auth_samba4.c', + init_function='', + deps='auth4 samba_server_gensec gensec', + internal_module=bld.SAMBA3_IS_STATIC_MODULE('auth_samba4') and bld.AD_DC_BUILD_IS_ENABLED(), + enabled=bld.SAMBA3_IS_ENABLED_MODULE('auth_samba4') and bld.AD_DC_BUILD_IS_ENABLED()) |