summaryrefslogtreecommitdiffstats
path: root/source3/auth
diff options
context:
space:
mode:
Diffstat (limited to 'source3/auth')
-rw-r--r--source3/auth/auth.c639
-rw-r--r--source3/auth/auth_builtin.c184
-rw-r--r--source3/auth/auth_generic.c557
-rw-r--r--source3/auth/auth_ntlmssp.c326
-rw-r--r--source3/auth/auth_sam.c315
-rw-r--r--source3/auth/auth_samba4.c401
-rw-r--r--source3/auth/auth_unix.c107
-rw-r--r--source3/auth/auth_util.c2368
-rw-r--r--source3/auth/auth_winbind.c202
-rw-r--r--source3/auth/check_samsec.c641
-rw-r--r--source3/auth/pampass.c896
-rw-r--r--source3/auth/pass_check.c294
-rw-r--r--source3/auth/proto.h438
-rw-r--r--source3/auth/server_info.c789
-rw-r--r--source3/auth/server_info_sam.c124
-rw-r--r--source3/auth/token_util.c1338
-rw-r--r--source3/auth/user_info.c169
-rw-r--r--source3/auth/user_krb5.c263
-rw-r--r--source3/auth/user_util.c447
-rw-r--r--source3/auth/wscript_build62
20 files changed, 10560 insertions, 0 deletions
diff --git a/source3/auth/auth.c b/source3/auth/auth.c
new file mode 100644
index 0000000..b83e98f
--- /dev/null
+++ b/source3/auth/auth.c
@@ -0,0 +1,639 @@
+/*
+ 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) {
+ DBG_INFO("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 generation
+ * 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;
+
+ DBG_NOTICE("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);
+
+ DBG_NOTICE("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,
+ NULL /* client_audit_info */,
+ NULL /* server_audit_info */);
+
+ 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,
+ NULL /* client_audit_info */,
+ NULL /* server_audit_info */);
+
+ 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..646fbea
--- /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 obscure 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..f957045
--- /dev/null
+++ b/source3/auth/auth_generic.c
@@ -0,0 +1,557 @@
+/*
+ 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 caught 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(&params, &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,
+ NULL /* client_audit_info */,
+ NULL /* server_audit_info */);
+
+ return nt_status;
+}
diff --git a/source3/auth/auth_ntlmssp.c b/source3/auth/auth_ntlmssp.c
new file mode 100644
index 0000000..73938dc
--- /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].sid;
+
+ 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..bae34e5
--- /dev/null
+++ b/source3/auth/auth_samba4.c
@@ -0,0 +1,401 @@
+/*
+ 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"
+#include "lib/util/idtree.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,
+ AUTH_INCLUDE_RESOURCE_GROUPS,
+ &info3,
+ NULL);
+ 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\n"));
+ 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..abc5e95
--- /dev/null
+++ b/source3/auth/auth_util.c
@@ -0,0 +1,2368 @@
+/*
+ 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 "dom_sid.h"
+#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;
+ bool is_allowed = false;
+
+ /* 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)) {
+ is_allowed = true;
+ }
+
+ /* Check if we have extra info about the user. */
+ if (dom_sid_in_domain(&global_sid_Unix_Users,
+ &server_info->extra.user_sid) ||
+ dom_sid_in_domain(&global_sid_Unix_Groups,
+ &server_info->extra.pgid_sid))
+ {
+ is_allowed = true;
+ }
+
+ if (!is_allowed) {
+ 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_attrs_unique(user_info_dc->sids,
+ &tmp_sid,
+ SE_GROUP_DEFAULT_FLAGS,
+ &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_attrs_unique(user_info_dc->sids,
+ &tmp_sid,
+ SE_GROUP_DEFAULT_FLAGS,
+ &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_attrs_unique(user_info_dc->sids,
+ &tmp_sid,
+ SE_GROUP_DEFAULT_FLAGS,
+ &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].sid);
+ if (cmp == 0) {
+ bool match;
+ uint32_t hint = 0;
+
+ match = sid_peek_rid(&user_info_dc->sids[i].sid, &hint);
+ if (!match) {
+ continue;
+ }
+
+ match = dom_sid_in_domain(&global_sid_Unix_NFS_Users,
+ &user_info_dc->sids[i].sid);
+ 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].sid);
+ 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].sid);
+ 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].sid,
+ &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 (!(user_info_dc->info->user_flags & NETLOGON_GUEST)) {
+ 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\n",
+ 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 appropriately
+*/
+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..46db4f2
--- /dev/null
+++ b/source3/auth/auth_winbind.c
@@ -0,0 +1,202 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind authentication mechanism
+
+ 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(&params, &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..1e55fed
--- /dev/null
+++ b/source3/auth/check_samsec.c
@@ -0,0 +1,641 @@
+/*
+ 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(),
+ lp_ntlm_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) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ SMBsesskeygen_ntv1(nt_pw, user_sess_key->data);
+ }
+ }
+ break;
+
+ /* 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:
+ status = 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);
+ break;
+ default:
+ DEBUG(0,("user_info constructed for user '%s' was invalid - password_state=%u invalid.\n", username, user_info->password_state));
+ status = NT_STATUS_INTERNAL_ERROR;
+ }
+done:
+ ZERO_STRUCTP(lm_hash);
+ ZERO_STRUCTP(nt_hash);
+ return status;
+}
+
+/****************************************************************************
+ 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 account 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 indefinitely 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..d64a6e8
--- /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 management, 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 success.
+*********************************************************************/
+
+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\n"));
+ *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;
+
+ /*
+ * Apparently 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)
+{
+ string_sub(buf, "%u", username, sizeof(fstring));
+ 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 conversation 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;
+
+ /*
+ * Apparently 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\n"));
+ 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 acquire 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..3b74c8f
--- /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 that's 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..d40422d
--- /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 generation
+ * 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..1eae636
--- /dev/null
+++ b/source3/auth/server_info.c
@@ -0,0 +1,789 @@
+/*
+ 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_DEFAULT_FLAGS;
+ 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..023ad7c
--- /dev/null
+++ b/source3/auth/token_util.c
@@ -0,0 +1,1338 @@
+/*
+ * 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;
+}
+
+static struct security_token *init_local_nt_token(TALLOC_CTX *mem_ctx)
+{
+ /*
+ * We do not have a method to populate the claims into this
+ * buffer in the source3/ stack. When that changes, we will
+ * instead make this optional based on lp_acl_claims_evaluation()
+ */
+
+ struct security_token *result
+ = security_token_initialise(mem_ctx,
+ CLAIMS_EVALUATION_NEVER);
+
+ if (result == NULL) {
+ DBG_ERR("talloc failed for security_token\n");
+ return NULL;
+ }
+
+ return result;
+}
+
+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 = init_local_nt_token(mem_ctx);
+ if (!usrtok) {
+ 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)));
+
+ result = init_local_nt_token(mem_ctx);
+ if (result == NULL) {
+ 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)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ char *s = NULL;
+ int i;
+
+ s = talloc_asprintf(frame,
+ "UNIX token of user %ld\n",
+ (long int)uid);
+
+ talloc_asprintf_addbuf(
+ &s,
+ "Primary group is %ld and contains %i supplementary "
+ "groups\n",
+ (long int)gid,
+ n_groups);
+ for (i = 0; i < n_groups; i++) {
+ talloc_asprintf_addbuf(&s,
+ "Group[%3i]: %ld\n",
+ i,
+ (long int)groups[i]);
+ }
+
+ DEBUGC(dbg_class, dbg_lev, ("%s", s ? s : "(NULL)"));
+ TALLOC_FREE(frame);
+}
+
+/*
+ * 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())