summaryrefslogtreecommitdiffstats
path: root/source/auth
diff options
context:
space:
mode:
Diffstat (limited to 'source/auth')
-rw-r--r--source/auth/auth.c534
-rw-r--r--source/auth/auth_builtin.c175
-rw-r--r--source/auth/auth_compat.c122
-rw-r--r--source/auth/auth_domain.c424
-rw-r--r--source/auth/auth_ntlmssp.c200
-rw-r--r--source/auth/auth_rhosts.c257
-rw-r--r--source/auth/auth_sam.c343
-rw-r--r--source/auth/auth_server.c417
-rw-r--r--source/auth/auth_unix.c136
-rw-r--r--source/auth/auth_util.c1500
-rw-r--r--source/auth/auth_winbind.c171
-rw-r--r--source/auth/pampass.c875
-rw-r--r--source/auth/pass_check.c783
13 files changed, 5937 insertions, 0 deletions
diff --git a/source/auth/auth.c b/source/auth/auth.c
new file mode 100644
index 00000000000..1b49699fbca
--- /dev/null
+++ b/source/auth/auth.c
@@ -0,0 +1,534 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Bartlett 2001-2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+static struct auth_init_function_entry *backends = NULL;
+
+static struct auth_init_function_entry *auth_find_backend_entry(const char *name);
+
+NTSTATUS smb_register_auth(int version, const char *name, auth_init_function init)
+{
+ struct auth_init_function_entry *entry = backends;
+
+ if (version != AUTH_INTERFACE_VERSION) {
+ DEBUG(0,("Can't register auth_method!\n"
+ "You tried to register an auth module with AUTH_INTERFACE_VERSION %d, while this version of samba uses %d\n",
+ version,AUTH_INTERFACE_VERSION));
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+ }
+
+ if (!name || !init) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG(5,("Attempting to register auth backend %s\n", name));
+
+ if (auth_find_backend_entry(name)) {
+ DEBUG(0,("There already is an auth method registered with the name %s!\n", name));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ entry = smb_xmalloc(sizeof(struct auth_init_function_entry));
+ entry->name = smb_xstrdup(name);
+ entry->init = init;
+
+ DLIST_ADD(backends, entry);
+ DEBUG(5,("Successfully added auth method '%s'\n", name));
+ return NT_STATUS_OK;
+}
+
+static struct auth_init_function_entry *auth_find_backend_entry(const char *name)
+{
+ struct auth_init_function_entry *entry = backends;
+
+ while(entry) {
+ if (strcmp(entry->name, name)==0) return entry;
+ entry = entry->next;
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ Try to get a challenge out of the various authentication modules.
+ Returns a const char of length 8 bytes.
+****************************************************************************/
+
+static const uint8 *get_ntlm_challenge(struct auth_context *auth_context)
+{
+ DATA_BLOB challenge = data_blob(NULL, 0);
+ const char *challenge_set_by = NULL;
+ auth_methods *auth_method;
+ TALLOC_CTX *mem_ctx;
+
+ if (auth_context->challenge.length) {
+ DEBUG(5, ("get_ntlm_challenge (auth subsystem): returning previous challenge by module %s (normal)\n",
+ auth_context->challenge_set_by));
+ return auth_context->challenge.data;
+ }
+
+ auth_context->challenge_may_be_modified = False;
+
+ for (auth_method = auth_context->auth_method_list; auth_method; auth_method = auth_method->next) {
+ if (auth_method->get_chal == NULL) {
+ DEBUG(5, ("auth_get_challenge: module %s did not want to specify a challenge\n", auth_method->name));
+ continue;
+ }
+
+ DEBUG(5, ("auth_get_challenge: getting challenge from module %s\n", auth_method->name));
+ if (challenge_set_by != NULL) {
+ DEBUG(1, ("auth_get_challenge: CONFIGURATION ERROR: authentication method %s has already specified a challenge. Challenge by %s ignored.\n",
+ challenge_set_by, auth_method->name));
+ continue;
+ }
+
+ mem_ctx = talloc_init("auth_get_challenge for module %s", auth_method->name);
+ if (!mem_ctx) {
+ smb_panic("talloc_init() failed!");
+ }
+
+ challenge = auth_method->get_chal(auth_context, &auth_method->private_data, mem_ctx);
+ if (!challenge.length) {
+ DEBUG(3, ("auth_get_challenge: getting challenge from authentication method %s FAILED.\n",
+ auth_method->name));
+ } else {
+ DEBUG(5, ("auth_get_challenge: sucessfully got challenge from module %s\n", auth_method->name));
+ auth_context->challenge = challenge;
+ challenge_set_by = auth_method->name;
+ auth_context->challenge_set_method = auth_method;
+ }
+ talloc_destroy(mem_ctx);
+ }
+
+ if (!challenge_set_by) {
+ uchar chal[8];
+
+ generate_random_buffer(chal, sizeof(chal), False);
+ auth_context->challenge = data_blob_talloc(auth_context->mem_ctx,
+ chal, sizeof(chal));
+
+ challenge_set_by = "random";
+ auth_context->challenge_may_be_modified = True;
+ }
+
+ DEBUG(5, ("auth_context challenge created by %s\n", challenge_set_by));
+ DEBUG(5, ("challenge is: \n"));
+ dump_data(5, (const char *)auth_context->challenge.data, auth_context->challenge.length);
+
+ SMB_ASSERT(auth_context->challenge.length == 8);
+
+ auth_context->challenge_set_by=challenge_set_by;
+
+ return auth_context->challenge.data;
+}
+
+
+/**
+ * Check user is in correct domain (if required)
+ *
+ * @param user Only used to fill in the debug message
+ *
+ * @param domain The domain to be verified
+ *
+ * @return True if the user can connect with that domain,
+ * False otherwise.
+**/
+
+static BOOL check_domain_match(const char *user, const char *domain)
+{
+ /*
+ * If we aren't serving to trusted domains, we must make sure that
+ * the validation request comes from an account in the same domain
+ * as the Samba server
+ */
+
+ if (!lp_allow_trusted_domains() &&
+ !(strequal("", domain) ||
+ strequal(lp_workgroup(), domain) ||
+ is_myname(domain))) {
+ DEBUG(1, ("check_domain_match: Attempt to connect as user %s from domain %s denied.\n", user, domain));
+ return False;
+ } else {
+ return True;
+ }
+}
+
+/**
+ * Check a user's Plaintext, LM or NTLM password.
+ *
+ * Check a user's password, as given in the user_info struct and return various
+ * interesting details in the server_info struct.
+ *
+ * This function does NOT need to be in a become_root()/unbecome_root() pair
+ * as it makes the calls itself when needed.
+ *
+ * The return value takes precedence over the contents of the server_info
+ * struct. When the return is other than NT_STATUS_OK the contents
+ * of that structure is undefined.
+ *
+ * @param user_info Contains the user supplied components, including the passwords.
+ * Must be created with make_user_info() or one of its wrappers.
+ *
+ * @param auth_context Supplies the challenges and some other data.
+ * Must be created with make_auth_context(), and the challenges should be
+ * filled in, either at creation or by calling the challenge geneation
+ * function auth_get_challenge().
+ *
+ * @param server_info If successful, contains information about the authentication,
+ * including a SAM_ACCOUNT struct describing the user.
+ *
+ * @return An NTSTATUS with NT_STATUS_OK or an appropriate error.
+ *
+ **/
+
+static NTSTATUS check_ntlm_password(const struct auth_context *auth_context,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_serversupplied_info **server_info)
+{
+ /* if all the modules say 'not for me' this is reasonable */
+ NTSTATUS nt_status = NT_STATUS_NO_SUCH_USER;
+ const char *unix_username;
+ auth_methods *auth_method;
+ TALLOC_CTX *mem_ctx;
+
+ if (!user_info || !auth_context || !server_info)
+ return NT_STATUS_LOGON_FAILURE;
+
+ DEBUG(3, ("check_ntlm_password: Checking password for unmapped user [%s]\\[%s]@[%s] with the new password interface\n",
+ user_info->client_domain.str, user_info->smb_name.str, user_info->wksta_name.str));
+
+ DEBUG(3, ("check_ntlm_password: mapped user is: [%s]\\[%s]@[%s]\n",
+ user_info->domain.str, user_info->internal_username.str, user_info->wksta_name.str));
+
+ if (auth_context->challenge.length != 8) {
+ DEBUG(0, ("check_ntlm_password: Invalid challenge stored for this auth context - cannot continue\n"));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+
+ if (auth_context->challenge_set_by)
+ DEBUG(10, ("check_ntlm_password: auth_context challenge created by %s\n",
+ auth_context->challenge_set_by));
+
+ DEBUG(10, ("challenge is: \n"));
+ dump_data(5, (const char *)auth_context->challenge.data, auth_context->challenge.length);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("user_info has passwords of length %d and %d\n",
+ user_info->lm_resp.length, user_info->nt_resp.length));
+ DEBUG(100, ("lm:\n"));
+ dump_data(100, user_info->lm_resp.data, user_info->lm_resp.length);
+ DEBUG(100, ("nt:\n"));
+ dump_data(100, user_info->nt_resp.data, user_info->nt_resp.length);
+#endif
+
+ /* This needs to be sorted: If it doesn't match, what should we do? */
+ if (!check_domain_match(user_info->smb_name.str, user_info->domain.str))
+ return NT_STATUS_LOGON_FAILURE;
+
+ for (auth_method = auth_context->auth_method_list;auth_method; auth_method = auth_method->next) {
+ NTSTATUS result;
+
+ mem_ctx = talloc_init("%s authentication for user %s\\%s", auth_method->name,
+ user_info->domain.str, user_info->smb_name.str);
+
+ result = auth_method->auth(auth_context, auth_method->private_data, mem_ctx, user_info, server_info);
+
+ /* check if the module did anything */
+ if ( NT_STATUS_V(result) == NT_STATUS_V(NT_STATUS_NOT_IMPLEMENTED) ) {
+ DEBUG(10,("check_ntlm_password: %s had nothing to say\n", auth_method->name));
+ talloc_destroy(mem_ctx);
+ continue;
+ }
+
+ nt_status = result;
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(3, ("check_ntlm_password: %s authentication for user [%s] succeeded\n",
+ auth_method->name, user_info->smb_name.str));
+ } else {
+ DEBUG(5, ("check_ntlm_password: %s authentication for user [%s] FAILED with error %s\n",
+ auth_method->name, user_info->smb_name.str, nt_errstr(nt_status)));
+ }
+
+ talloc_destroy(mem_ctx);
+
+ if ( NT_STATUS_IS_OK(nt_status))
+ {
+ break;
+ }
+ }
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ unix_username = (*server_info)->unix_name;
+ if (!(*server_info)->guest) {
+ /* We might not be root if we are an RPC call */
+ become_root();
+ nt_status = smb_pam_accountcheck(unix_username);
+ unbecome_root();
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(5, ("check_ntlm_password: PAM Account for user [%s] succeeded\n",
+ unix_username));
+ } else {
+ DEBUG(3, ("check_ntlm_password: PAM Account for user [%s] FAILED with error %s\n",
+ unix_username, nt_errstr(nt_status)));
+ }
+ }
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ DEBUG((*server_info)->guest ? 5 : 2,
+ ("check_ntlm_password: %sauthentication for user [%s] -> [%s] -> [%s] succeeded\n",
+ (*server_info)->guest ? "guest " : "",
+ user_info->smb_name.str,
+ user_info->internal_username.str,
+ unix_username));
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(2, ("check_ntlm_password: Authentication for user [%s] -> [%s] FAILED with error %s\n",
+ user_info->smb_name.str, user_info->internal_username.str,
+ nt_errstr(nt_status)));
+ ZERO_STRUCTP(server_info);
+ }
+ return nt_status;
+}
+
+/***************************************************************************
+ Clear out a auth_context, and destroy the attached TALLOC_CTX
+***************************************************************************/
+
+static void free_auth_context(struct auth_context **auth_context)
+{
+ auth_methods *auth_method;
+
+ if (*auth_context) {
+ /* Free private data of context's authentication methods */
+ for (auth_method = (*auth_context)->auth_method_list; auth_method; auth_method = auth_method->next) {
+ if (auth_method->free_private_data) {
+ auth_method->free_private_data (&auth_method->private_data);
+ auth_method->private_data = NULL;
+ }
+ }
+
+ talloc_destroy((*auth_context)->mem_ctx);
+ *auth_context = NULL;
+ }
+}
+
+/***************************************************************************
+ Make a auth_info struct
+***************************************************************************/
+
+static NTSTATUS make_auth_context(struct auth_context **auth_context)
+{
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_init("authentication context");
+
+ *auth_context = talloc(mem_ctx, sizeof(**auth_context));
+ if (!*auth_context) {
+ DEBUG(0,("make_auth_context: talloc failed!\n"));
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ ZERO_STRUCTP(*auth_context);
+
+ (*auth_context)->mem_ctx = mem_ctx;
+ (*auth_context)->check_ntlm_password = check_ntlm_password;
+ (*auth_context)->get_ntlm_challenge = get_ntlm_challenge;
+ (*auth_context)->free = free_auth_context;
+
+ return NT_STATUS_OK;
+}
+
+BOOL load_auth_module(struct auth_context *auth_context,
+ const char *module, auth_methods **ret)
+{
+ static BOOL initialised_static_modules = False;
+
+ struct auth_init_function_entry *entry;
+ char *module_name = smb_xstrdup(module);
+ char *module_params = NULL;
+ char *p;
+ BOOL good = False;
+
+ /* Initialise static modules if not done so yet */
+ if(!initialised_static_modules) {
+ static_init_auth;
+ initialised_static_modules = True;
+ }
+
+ DEBUG(5,("load_auth_module: Attempting to find an auth method to match %s\n",
+ module));
+
+ p = strchr(module_name, ':');
+ if (p) {
+ *p = 0;
+ module_params = p+1;
+ trim_char(module_params, ' ', ' ');
+ }
+
+ trim_char(module_name, ' ', ' ');
+
+ entry = auth_find_backend_entry(module_name);
+
+ if (entry == NULL) {
+ if (NT_STATUS_IS_OK(smb_probe_module("auth", module_name))) {
+ entry = auth_find_backend_entry(module_name);
+ }
+ }
+
+ if (entry != NULL) {
+ if (!NT_STATUS_IS_OK(entry->init(auth_context, module_params, ret))) {
+ DEBUG(0,("load_auth_module: auth method %s did not correctly init\n",
+ module_name));
+ } else {
+ DEBUG(5,("load_auth_module: auth method %s has a valid init\n",
+ module_name));
+ good = True;
+ }
+ } else {
+ DEBUG(0,("load_auth_module: can't find auth method %s!\n", module_name));
+ }
+
+ SAFE_FREE(module_name);
+ return good;
+}
+
+/***************************************************************************
+ Make a auth_info struct for the auth subsystem
+***************************************************************************/
+
+static NTSTATUS make_auth_context_text_list(struct auth_context **auth_context, char **text_list)
+{
+ auth_methods *list = NULL;
+ auth_methods *t = NULL;
+ auth_methods *tmp;
+ NTSTATUS nt_status;
+
+ if (!text_list) {
+ DEBUG(2,("make_auth_context_text_list: No auth method list!?\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = make_auth_context(auth_context)))
+ return nt_status;
+
+ for (;*text_list; text_list++) {
+ if (load_auth_module(*auth_context, *text_list, &t)) {
+ DLIST_ADD_END(list, t, tmp);
+ }
+ }
+
+ (*auth_context)->auth_method_list = list;
+
+ return nt_status;
+}
+
+/***************************************************************************
+ Make a auth_context struct for the auth subsystem
+***************************************************************************/
+
+NTSTATUS make_auth_context_subsystem(struct auth_context **auth_context)
+{
+ char **auth_method_list = NULL;
+ NTSTATUS nt_status;
+
+ if (lp_auth_methods() && !str_list_copy(&auth_method_list, lp_auth_methods())) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (auth_method_list == NULL) {
+ switch (lp_security())
+ {
+ case SEC_DOMAIN:
+ DEBUG(5,("Making default auth method list for security=domain\n"));
+ auth_method_list = str_list_make("guest sam winbind:ntdomain", NULL);
+ break;
+ case SEC_SERVER:
+ DEBUG(5,("Making default auth method list for security=server\n"));
+ auth_method_list = str_list_make("guest sam smbserver", NULL);
+ break;
+ case SEC_USER:
+ if (lp_encrypted_passwords()) {
+ if ((lp_server_role() == ROLE_DOMAIN_PDC) || (lp_server_role() == ROLE_DOMAIN_BDC)) {
+ DEBUG(5,("Making default auth method list for DC, security=user, encrypt passwords = yes\n"));
+ auth_method_list = str_list_make("guest sam winbind:trustdomain", NULL);
+ } else {
+ DEBUG(5,("Making default auth method list for standalone security=user, encrypt passwords = yes\n"));
+ auth_method_list = str_list_make("guest sam", NULL);
+ }
+ } else {
+ DEBUG(5,("Making default auth method list for security=user, encrypt passwords = no\n"));
+ auth_method_list = str_list_make("guest unix", NULL);
+ }
+ break;
+ case SEC_SHARE:
+ if (lp_encrypted_passwords()) {
+ DEBUG(5,("Making default auth method list for security=share, encrypt passwords = yes\n"));
+ auth_method_list = str_list_make("guest sam", NULL);
+ } else {
+ DEBUG(5,("Making default auth method list for security=share, encrypt passwords = no\n"));
+ auth_method_list = str_list_make("guest unix", NULL);
+ }
+ break;
+ case SEC_ADS:
+ DEBUG(5,("Making default auth method list for security=ADS\n"));
+ auth_method_list = str_list_make("guest sam winbind:ntdomain", NULL);
+ break;
+ default:
+ DEBUG(5,("Unknown auth method!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ } else {
+ DEBUG(5,("Using specified auth order\n"));
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = make_auth_context_text_list(auth_context, auth_method_list))) {
+ str_list_free(&auth_method_list);
+ return nt_status;
+ }
+
+ str_list_free(&auth_method_list);
+ return nt_status;
+}
+
+/***************************************************************************
+ Make a auth_info struct with a fixed challenge
+***************************************************************************/
+
+NTSTATUS make_auth_context_fixed(struct auth_context **auth_context, uchar chal[8])
+{
+ NTSTATUS nt_status;
+ if (!NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(auth_context))) {
+ return nt_status;
+ }
+
+ (*auth_context)->challenge = data_blob_talloc((*auth_context)->mem_ctx, chal, 8);
+ (*auth_context)->challenge_set_by = "fixed";
+ return nt_status;
+}
+
+
diff --git a/source/auth/auth_builtin.c b/source/auth/auth_builtin.c
new file mode 100644
index 00000000000..96c2221652e
--- /dev/null
+++ b/source/auth/auth_builtin.c
@@ -0,0 +1,175 @@
+/*
+ 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 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+/**
+ * Return a guest logon for guest users (username = "")
+ *
+ * Typically used as the first module in the auth chain, this allows
+ * guest logons to be dealt with in one place. Non-guest logons 'fail'
+ * and pass onto the next module.
+ **/
+
+static NTSTATUS check_guest_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ /* mark this as 'not for me' */
+ NTSTATUS nt_status = NT_STATUS_NOT_IMPLEMENTED;
+
+ if (!(user_info->internal_username.str
+ && *user_info->internal_username.str)) {
+ nt_status = make_server_info_guest(server_info);
+ }
+
+ return nt_status;
+}
+
+/* Guest modules initialisation */
+
+static NTSTATUS auth_init_guest(struct auth_context *auth_context, const char *options, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method))
+ return NT_STATUS_NO_MEMORY;
+
+ (*auth_method)->auth = check_guest_security;
+ (*auth_method)->name = "guest";
+ return NT_STATUS_OK;
+}
+
+#ifdef DEVELOPER
+/**
+ * Return an error based on username
+ *
+ * This function allows the testing of obsure errors, as well as the generation
+ * of NT_STATUS -> DOS error mapping tables.
+ *
+ * This module is of no value to end-users.
+ *
+ * The password is ignored.
+ *
+ * @return An NTSTATUS value based on the username
+ **/
+
+static NTSTATUS check_name_to_ntstatus_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ NTSTATUS nt_status;
+ fstring user;
+ long error_num;
+ fstrcpy(user, user_info->smb_name.str);
+
+ if (strnequal("NT_STATUS", user, strlen("NT_STATUS"))) {
+ strupper_m(user);
+ return nt_status_string_to_code(user);
+ }
+
+ strlower_m(user);
+ error_num = strtoul(user, NULL, 16);
+
+ DEBUG(5,("check_name_to_ntstatus_security: Error for user %s was %lx\n", user, error_num));
+
+ nt_status = NT_STATUS(error_num);
+
+ return nt_status;
+}
+
+/** Module initialisation function */
+
+static NTSTATUS auth_init_name_to_ntstatus(struct auth_context *auth_context, const char *param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method))
+ return NT_STATUS_NO_MEMORY;
+
+ (*auth_method)->auth = check_name_to_ntstatus_security;
+ (*auth_method)->name = "name_to_ntstatus";
+ return NT_STATUS_OK;
+}
+
+/**
+ * Return a 'fixed' challenge instead of a variable one.
+ *
+ * The idea of this function is to make packet snifs consistant
+ * with a fixed challenge, so as to aid debugging.
+ *
+ * This module is of no value to end-users.
+ *
+ * This module does not actually authenticate the user, but
+ * just pretenteds to need a specified challenge.
+ * This module removes *all* security from the challenge-response system
+ *
+ * @return NT_STATUS_UNSUCCESSFUL
+ **/
+
+static NTSTATUS check_fixed_challenge_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************************
+ Get the challenge out of a password server.
+****************************************************************************/
+
+static DATA_BLOB auth_get_fixed_challenge(const struct auth_context *auth_context,
+ void **my_private_data,
+ TALLOC_CTX *mem_ctx)
+{
+ const char *challenge = "I am a teapot";
+ return data_blob(challenge, 8);
+}
+
+
+/** Module initailisation function */
+
+static NTSTATUS auth_init_fixed_challenge(struct auth_context *auth_context, const char *param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method))
+ return NT_STATUS_NO_MEMORY;
+
+ (*auth_method)->auth = check_fixed_challenge_security;
+ (*auth_method)->get_chal = auth_get_fixed_challenge;
+ (*auth_method)->name = "fixed_challenge";
+ return NT_STATUS_OK;
+}
+#endif /* DEVELOPER */
+
+NTSTATUS auth_builtin_init(void)
+{
+ smb_register_auth(AUTH_INTERFACE_VERSION, "guest", auth_init_guest);
+#ifdef DEVELOPER
+ smb_register_auth(AUTH_INTERFACE_VERSION, "fixed_challenge", auth_init_fixed_challenge);
+ smb_register_auth(AUTH_INTERFACE_VERSION, "name_to_ntstatus", auth_init_name_to_ntstatus);
+#endif
+ return NT_STATUS_OK;
+}
diff --git a/source/auth/auth_compat.c b/source/auth/auth_compat.c
new file mode 100644
index 00000000000..a70f1e98b72
--- /dev/null
+++ b/source/auth/auth_compat.c
@@ -0,0 +1,122 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Bartlett 2001-2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+/****************************************************************************
+ COMPATIBILITY INTERFACES:
+ ***************************************************************************/
+
+/****************************************************************************
+check if a username/password is OK assuming the password is a 24 byte
+SMB hash
+return True if the password is correct, False otherwise
+****************************************************************************/
+
+NTSTATUS check_plaintext_password(const char *smb_name, DATA_BLOB plaintext_password, auth_serversupplied_info **server_info)
+{
+ struct auth_context *plaintext_auth_context = NULL;
+ auth_usersupplied_info *user_info = NULL;
+ const uint8 *chal;
+ NTSTATUS nt_status;
+ if (!NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(&plaintext_auth_context))) {
+ return nt_status;
+ }
+
+ chal = plaintext_auth_context->get_ntlm_challenge(plaintext_auth_context);
+
+ if (!make_user_info_for_reply(&user_info,
+ smb_name, lp_workgroup(), chal,
+ plaintext_password)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ nt_status = plaintext_auth_context->check_ntlm_password(plaintext_auth_context,
+ user_info, server_info);
+
+ (plaintext_auth_context->free)(&plaintext_auth_context);
+ free_user_info(&user_info);
+ return nt_status;
+}
+
+static NTSTATUS pass_check_smb(const char *smb_name,
+ const char *domain,
+ DATA_BLOB lm_pwd,
+ DATA_BLOB nt_pwd,
+ DATA_BLOB plaintext_password,
+ BOOL encrypted)
+
+{
+ NTSTATUS nt_status;
+ extern struct auth_context *negprot_global_auth_context;
+ auth_serversupplied_info *server_info = NULL;
+ if (encrypted) {
+ auth_usersupplied_info *user_info = NULL;
+ make_user_info_for_reply_enc(&user_info, smb_name,
+ domain,
+ lm_pwd,
+ nt_pwd);
+ nt_status = negprot_global_auth_context->check_ntlm_password(negprot_global_auth_context,
+ user_info, &server_info);
+ free_user_info(&user_info);
+ } else {
+ nt_status = check_plaintext_password(smb_name, plaintext_password, &server_info);
+ }
+ free_server_info(&server_info);
+ return nt_status;
+}
+
+/****************************************************************************
+check if a username/password pair is ok via the auth subsystem.
+return True if the password is correct, False otherwise
+****************************************************************************/
+BOOL password_ok(char *smb_name, DATA_BLOB password_blob)
+{
+
+ DATA_BLOB null_password = data_blob(NULL, 0);
+ extern BOOL global_encrypted_passwords_negotiated;
+ BOOL encrypted = (global_encrypted_passwords_negotiated && password_blob.length == 24);
+
+ if (encrypted) {
+ /*
+ * The password could be either NTLM or plain LM. Try NTLM first,
+ * but fall-through as required.
+ * NTLMv2 makes no sense here.
+ */
+ if (NT_STATUS_IS_OK(pass_check_smb(smb_name, lp_workgroup(), null_password, password_blob, null_password, encrypted))) {
+ return True;
+ }
+
+ if (NT_STATUS_IS_OK(pass_check_smb(smb_name, lp_workgroup(), password_blob, null_password, null_password, encrypted))) {
+ return True;
+ }
+ } else {
+ if (NT_STATUS_IS_OK(pass_check_smb(smb_name, lp_workgroup(), null_password, null_password, password_blob, encrypted))) {
+ return True;
+ }
+ }
+
+ return False;
+}
+
+
diff --git a/source/auth/auth_domain.c b/source/auth/auth_domain.c
new file mode 100644
index 00000000000..fdff0b52f96
--- /dev/null
+++ b/source/auth/auth_domain.c
@@ -0,0 +1,424 @@
+/*
+ Unix SMB/CIFS implementation.
+ Authenticate against a remote domain
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Andrew Bartlett 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+extern BOOL global_machine_password_needs_changing;
+
+/**
+ * Connect to a remote server for (inter)domain security authenticaion.
+ *
+ * @param cli the cli to return containing the active connection
+ * @param server either a machine name or text IP address to
+ * connect to.
+ * @param setup_creds_as domain account to setup credentials as
+ * @param sec_chan a switch value to distinguish between domain
+ * member and interdomain authentication
+ * @param trust_passwd the trust password to establish the
+ * credentials with.
+ *
+ **/
+
+static NTSTATUS connect_to_domain_password_server(struct cli_state **cli,
+ const char *domain, const char *dc_name,
+ struct in_addr dc_ip,
+ const char *setup_creds_as,
+ uint16 sec_chan,
+ const unsigned char *trust_passwd,
+ BOOL *retry)
+{
+ NTSTATUS result;
+
+ /* TODO: Send a SAMLOGON request to determine whether this is a valid
+ logonserver. We can avoid a 30-second timeout if the DC is down
+ if the SAMLOGON request fails as it is only over UDP. */
+
+ /* we use a mutex to prevent two connections at once - when a
+ Win2k PDC get two connections where one hasn't completed a
+ session setup yet it will send a TCP reset to the first
+ connection (tridge) */
+
+ /*
+ * With NT4.x DC's *all* authentication must be serialized to avoid
+ * ACCESS_DENIED errors if 2 auths are done from the same machine. JRA.
+ */
+
+ if (!grab_server_mutex(dc_name))
+ return NT_STATUS_NO_LOGON_SERVERS;
+
+ /* Attempt connection */
+ *retry = True;
+ result = cli_full_connection(cli, global_myname(), dc_name, &dc_ip, 0,
+ "IPC$", "IPC", "", "", "", 0, Undefined, retry);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ /* map to something more useful */
+ if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) {
+ result = NT_STATUS_NO_LOGON_SERVERS;
+ }
+
+ release_server_mutex();
+ return result;
+ }
+
+ /*
+ * We now have an anonymous connection to IPC$ on the domain password server.
+ */
+
+ /*
+ * Even if the connect succeeds we need to setup the netlogon
+ * pipe here. We do this as we may just have changed the domain
+ * account password on the PDC and yet we may be talking to
+ * a BDC that doesn't have this replicated yet. In this case
+ * a successful connect to a DC needs to take the netlogon connect
+ * into account also. This patch from "Bjart Kvarme" <bjart.kvarme@usit.uio.no>.
+ */
+
+ if(cli_nt_session_open(*cli, PI_NETLOGON) == False) {
+ DEBUG(0,("connect_to_domain_password_server: unable to open the domain client session to \
+machine %s. Error was : %s.\n", dc_name, cli_errstr(*cli)));
+ cli_nt_session_close(*cli);
+ cli_ulogoff(*cli);
+ cli_shutdown(*cli);
+ release_server_mutex();
+ return NT_STATUS_NO_LOGON_SERVERS;
+ }
+
+ fstr_sprintf((*cli)->mach_acct, "%s$", setup_creds_as);
+
+ /* This must be the remote domain (not ours) for schannel */
+
+ fstrcpy( (*cli)->domain, domain );
+
+ result = cli_nt_establish_netlogon(*cli, sec_chan, trust_passwd);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0,("connect_to_domain_password_server: unable to setup the NETLOGON credentials to machine \
+%s. Error was : %s.\n", dc_name, nt_errstr(result)));
+ cli_nt_session_close(*cli);
+ cli_ulogoff(*cli);
+ cli_shutdown(*cli);
+ release_server_mutex();
+ return result;
+ }
+
+ /* We exit here with the mutex *locked*. JRA */
+
+ return NT_STATUS_OK;
+}
+
+/***********************************************************************
+ Do the same as security=server, but using NT Domain calls and a session
+ key from the machine password. If the server parameter is specified
+ use it, otherwise figure out a server from the 'password server' param.
+************************************************************************/
+
+static NTSTATUS domain_client_validate(TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ const char *domain,
+ uchar chal[8],
+ auth_serversupplied_info **server_info,
+ const char *dc_name, struct in_addr dc_ip,
+ const char *setup_creds_as,
+ uint16 sec_chan,
+ unsigned char trust_passwd[16],
+ time_t last_change_time)
+{
+ NET_USER_INFO_3 info3;
+ struct cli_state *cli = NULL;
+ NTSTATUS nt_status = NT_STATUS_NO_LOGON_SERVERS;
+ int i;
+ BOOL retry = True;
+
+ /*
+ * At this point, smb_apasswd points to the lanman response to
+ * the challenge in local_challenge, and smb_ntpasswd points to
+ * the NT response to the challenge in local_challenge. Ship
+ * these over the secure channel to a domain controller and
+ * see if they were valid.
+ */
+
+ /* rety loop for robustness */
+
+ for (i = 0; !NT_STATUS_IS_OK(nt_status) && retry && (i < 3); i++) {
+ nt_status = connect_to_domain_password_server(&cli, domain, dc_name,
+ dc_ip, setup_creds_as, sec_chan, trust_passwd, &retry);
+ }
+
+ if ( !NT_STATUS_IS_OK(nt_status) ) {
+ DEBUG(0,("domain_client_validate: Domain password server not available.\n"));
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
+ return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
+ }
+ return nt_status;
+ }
+
+ ZERO_STRUCT(info3);
+
+ /*
+ * If this call succeeds, we now have lots of info about the user
+ * in the info3 structure.
+ */
+
+ nt_status = cli_netlogon_sam_network_logon(cli, mem_ctx,
+ NULL, user_info->smb_name.str, user_info->domain.str,
+ user_info->wksta_name.str, chal, user_info->lm_resp,
+ user_info->nt_resp, &info3);
+
+ /* let go as soon as possible so we avoid any potential deadlocks
+ with winbind lookup up users or groups */
+
+ release_server_mutex();
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0,("domain_client_validate: unable to validate password "
+ "for user %s in domain %s to Domain controller %s. "
+ "Error was %s.\n", user_info->smb_name.str,
+ user_info->domain.str, cli->srv_name_slash,
+ nt_errstr(nt_status)));
+
+ /* map to something more useful */
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
+ nt_status = NT_STATUS_NO_LOGON_SERVERS;
+ }
+ } else {
+ nt_status = make_server_info_info3(mem_ctx, user_info->internal_username.str,
+ user_info->smb_name.str, domain, server_info, &info3);
+ netsamlogon_cache_store( mem_ctx, &info3 );
+ }
+
+#if 0
+ /*
+ * We don't actually need to do this - plus it fails currently with
+ * NT_STATUS_INVALID_INFO_CLASS - we need to know *exactly* what to
+ * send here. JRA.
+ */
+
+ if (NT_STATUS_IS_OK(status)) {
+ if(cli_nt_logoff(&cli, &ctr) == False) {
+ DEBUG(0,("domain_client_validate: unable to log off user %s in domain \
+%s to Domain controller %s. Error was %s.\n", user, domain, dc_name, cli_errstr(&cli)));
+ nt_status = NT_STATUS_LOGON_FAILURE;
+ }
+ }
+#endif /* 0 */
+
+ /* Note - once the cli stream is shutdown the mem_ctx used
+ to allocate the other_sids and gids structures has been deleted - so
+ these pointers are no longer valid..... */
+
+ cli_nt_session_close(cli);
+ cli_ulogoff(cli);
+ cli_shutdown(cli);
+ return nt_status;
+}
+
+/****************************************************************************
+ Check for a valid username and password in security=domain mode.
+****************************************************************************/
+
+static NTSTATUS check_ntdomain_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
+ unsigned char trust_passwd[16];
+ time_t last_change_time;
+ const char *domain = lp_workgroup();
+ uint32 sec_channel_type = 0;
+ fstring dc_name;
+ struct in_addr dc_ip;
+
+ if ( lp_server_role() != ROLE_DOMAIN_MEMBER ) {
+ DEBUG(0,("check_ntdomain_security: Configuration error! Cannot use "
+ "ntdomain auth method when not a member of a domain.\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!user_info || !server_info || !auth_context) {
+ DEBUG(1,("check_ntdomain_security: Critical variables not present. Failing.\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /*
+ * Check that the requested domain is not our own machine name.
+ * If it is, we should never check the PDC here, we use our own local
+ * password file.
+ */
+
+ if(strequal(get_global_sam_name(), user_info->domain.str)) {
+ DEBUG(3,("check_ntdomain_security: Requested domain was for this machine.\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ /*
+ * Get the machine account password for our primary domain
+ * No need to become_root() as secrets_init() is done at startup.
+ */
+
+ if (!secrets_fetch_trust_account_password(domain, trust_passwd, &last_change_time, &sec_channel_type))
+ {
+ DEBUG(0, ("check_ntdomain_security: could not fetch trust account password for domain '%s'\n", domain));
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+
+ /* Test if machine password has expired and needs to be changed */
+ if (lp_machine_password_timeout()) {
+ if (last_change_time > 0 &&
+ time(NULL) > (last_change_time +
+ lp_machine_password_timeout())) {
+ global_machine_password_needs_changing = True;
+ }
+ }
+
+ /* we need our DC to send the net_sam_logon() request to */
+
+ if ( !get_dc_name(domain, NULL, dc_name, &dc_ip) ) {
+ DEBUG(5,("check_ntdomain_security: unable to locate a DC for domain %s\n",
+ user_info->domain.str));
+ return NT_STATUS_NO_LOGON_SERVERS;
+ }
+
+ nt_status = domain_client_validate(mem_ctx, user_info, domain,
+ (uchar *)auth_context->challenge.data, server_info, dc_name, dc_ip,
+ global_myname(), sec_channel_type,trust_passwd, last_change_time);
+
+ return nt_status;
+}
+
+/* module initialisation */
+static NTSTATUS auth_init_ntdomain(struct auth_context *auth_context, const char* param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*auth_method)->name = "ntdomain";
+ (*auth_method)->auth = check_ntdomain_security;
+ return NT_STATUS_OK;
+}
+
+
+/****************************************************************************
+ Check for a valid username and password in a trusted domain
+****************************************************************************/
+
+static NTSTATUS check_trustdomain_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
+ unsigned char trust_md4_password[16];
+ char *trust_password;
+ time_t last_change_time;
+ DOM_SID sid;
+ fstring dc_name;
+ struct in_addr dc_ip;
+
+ if (!user_info || !server_info || !auth_context) {
+ DEBUG(1,("check_trustdomain_security: Critical variables not present. Failing.\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /*
+ * Check that the requested domain is not our own machine name or domain name.
+ */
+
+ if( strequal(get_global_sam_name(), user_info->domain.str)) {
+ DEBUG(3,("check_trustdomain_security: Requested domain [%s] was for this machine.\n",
+ user_info->domain.str));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ /* No point is bothering if this is not a trusted domain.
+ This return makes "map to guest = bad user" work again.
+ The logic is that if we know nothing about the domain, that
+ user is known to us and does not exist */
+
+ if ( !is_trusted_domain( user_info->domain.str ) )
+ return NT_STATUS_NOT_IMPLEMENTED;
+
+ /*
+ * Get the trusted account password for the trusted domain
+ * No need to become_root() as secrets_init() is done at startup.
+ */
+
+ if (!secrets_fetch_trusted_domain_password(user_info->domain.str, &trust_password, &sid, &last_change_time))
+ {
+ DEBUG(0, ("check_trustdomain_security: could not fetch trust account password for domain %s\n", user_info->domain.str));
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("Trust password for domain %s is %s\n", user_info->domain.str, trust_password));
+#endif
+ E_md4hash(trust_password, trust_md4_password);
+ SAFE_FREE(trust_password);
+
+#if 0
+ /* Test if machine password is expired and need to be changed */
+ if (time(NULL) > last_change_time + lp_machine_password_timeout())
+ {
+ global_machine_password_needs_changing = True;
+ }
+#endif
+
+ /* use get_dc_name() for consistency even through we know that it will be
+ a netbios name */
+
+ if ( !get_dc_name(user_info->domain.str, NULL, dc_name, &dc_ip) ) {
+ DEBUG(5,("check_trustdomain_security: unable to locate a DC for domain %s\n",
+ user_info->domain.str));
+ return NT_STATUS_NO_LOGON_SERVERS;
+ }
+
+ nt_status = domain_client_validate(mem_ctx, user_info, user_info->domain.str,
+ (uchar *)auth_context->challenge.data, server_info, dc_name, dc_ip,
+ lp_workgroup(), SEC_CHAN_DOMAIN, trust_md4_password, last_change_time);
+
+ return nt_status;
+}
+
+/* module initialisation */
+static NTSTATUS auth_init_trustdomain(struct auth_context *auth_context, const char* param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*auth_method)->name = "trustdomain";
+ (*auth_method)->auth = check_trustdomain_security;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS auth_domain_init(void)
+{
+ smb_register_auth(AUTH_INTERFACE_VERSION, "trustdomain", auth_init_trustdomain);
+ smb_register_auth(AUTH_INTERFACE_VERSION, "ntdomain", auth_init_ntdomain);
+ return NT_STATUS_OK;
+}
diff --git a/source/auth/auth_ntlmssp.c b/source/auth/auth_ntlmssp.c
new file mode 100644
index 00000000000..a5ce101e5e7
--- /dev/null
+++ b/source/auth/auth_ntlmssp.c
@@ -0,0 +1,200 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 3.0
+ handle NLTMSSP, server side
+
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Andrew Bartlett 2001-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/**
+ * Return the challenge as determined by the authentication subsystem
+ * @return an 8 byte random challenge
+ */
+
+static const uint8 *auth_ntlmssp_get_challenge(const struct ntlmssp_state *ntlmssp_state)
+{
+ AUTH_NTLMSSP_STATE *auth_ntlmssp_state = ntlmssp_state->auth_context;
+ return auth_ntlmssp_state->auth_context->get_ntlm_challenge(auth_ntlmssp_state->auth_context);
+}
+
+/**
+ * Some authentication methods 'fix' the challenge, so we may not be able to set it
+ *
+ * @return If the effective challenge used by the auth subsystem may be modified
+ */
+static BOOL auth_ntlmssp_may_set_challenge(const struct ntlmssp_state *ntlmssp_state)
+{
+ AUTH_NTLMSSP_STATE *auth_ntlmssp_state = ntlmssp_state->auth_context;
+ struct auth_context *auth_context = auth_ntlmssp_state->auth_context;
+
+ return auth_context->challenge_may_be_modified;
+}
+
+/**
+ * NTLM2 authentication modifies the effective challange,
+ * @param challenge The new challenge value
+ */
+static NTSTATUS auth_ntlmssp_set_challenge(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *challenge)
+{
+ AUTH_NTLMSSP_STATE *auth_ntlmssp_state = ntlmssp_state->auth_context;
+ struct auth_context *auth_context = auth_ntlmssp_state->auth_context;
+
+ SMB_ASSERT(challenge->length == 8);
+
+ auth_context->challenge = data_blob_talloc(auth_context->mem_ctx,
+ challenge->data, challenge->length);
+
+ auth_context->challenge_set_by = "NTLMSSP callback (NTLM2)";
+
+ DEBUG(5, ("auth_context challenge set by %s\n", auth_context->challenge_set_by));
+ DEBUG(5, ("challenge is: \n"));
+ dump_data(5, (const char *)auth_context->challenge.data, auth_context->challenge.length);
+ return NT_STATUS_OK;
+}
+
+/**
+ * Check the password on an NTLMSSP login.
+ *
+ * Return the session keys used on the connection.
+ */
+
+static NTSTATUS auth_ntlmssp_check_password(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *nt_session_key, DATA_BLOB *lm_session_key)
+{
+ AUTH_NTLMSSP_STATE *auth_ntlmssp_state = ntlmssp_state->auth_context;
+ uint32 auth_flags = AUTH_FLAG_NONE;
+ auth_usersupplied_info *user_info = NULL;
+ DATA_BLOB plaintext_password = data_blob(NULL, 0);
+ NTSTATUS nt_status;
+
+ if (auth_ntlmssp_state->ntlmssp_state->lm_resp.length) {
+ auth_flags |= AUTH_FLAG_LM_RESP;
+ }
+
+ if (auth_ntlmssp_state->ntlmssp_state->nt_resp.length == 24) {
+ auth_flags |= AUTH_FLAG_NTLM_RESP;
+ } else if (auth_ntlmssp_state->ntlmssp_state->nt_resp.length > 24) {
+ auth_flags |= AUTH_FLAG_NTLMv2_RESP;
+ }
+
+ /* the client has given us its machine name (which we otherwise would not get on port 445).
+ we need to possibly reload smb.conf if smb.conf includes depend on the machine name */
+
+ set_remote_machine_name(auth_ntlmssp_state->ntlmssp_state->workstation, True);
+
+ /* setup the string used by %U */
+ /* sub_set_smb_name checks for weird internally */
+ sub_set_smb_name(auth_ntlmssp_state->ntlmssp_state->user);
+
+ reload_services(True);
+
+ nt_status = make_user_info_map(&user_info,
+ auth_ntlmssp_state->ntlmssp_state->user,
+ auth_ntlmssp_state->ntlmssp_state->domain,
+ auth_ntlmssp_state->ntlmssp_state->workstation,
+ auth_ntlmssp_state->ntlmssp_state->lm_resp,
+ auth_ntlmssp_state->ntlmssp_state->nt_resp,
+ plaintext_password,
+ auth_flags, True);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ nt_status = auth_ntlmssp_state->auth_context->check_ntlm_password(auth_ntlmssp_state->auth_context,
+ user_info, &auth_ntlmssp_state->server_info);
+
+ free_user_info(&user_info);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ if (auth_ntlmssp_state->server_info->nt_session_key.length) {
+ DEBUG(10, ("Got NT session key of length %u\n", auth_ntlmssp_state->server_info->nt_session_key.length));
+ *nt_session_key = data_blob_talloc(auth_ntlmssp_state->mem_ctx,
+ auth_ntlmssp_state->server_info->nt_session_key.data,
+ auth_ntlmssp_state->server_info->nt_session_key.length);
+ }
+ if (auth_ntlmssp_state->server_info->lm_session_key.length) {
+ DEBUG(10, ("Got LM session key of length %u\n", auth_ntlmssp_state->server_info->lm_session_key.length));
+ *lm_session_key = data_blob_talloc(auth_ntlmssp_state->mem_ctx,
+ auth_ntlmssp_state->server_info->lm_session_key.data,
+ auth_ntlmssp_state->server_info->lm_session_key.length);
+ }
+ return nt_status;
+}
+
+NTSTATUS auth_ntlmssp_start(AUTH_NTLMSSP_STATE **auth_ntlmssp_state)
+{
+ NTSTATUS nt_status;
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_init("AUTH NTLMSSP context");
+
+ *auth_ntlmssp_state = talloc_zero(mem_ctx, sizeof(**auth_ntlmssp_state));
+ if (!*auth_ntlmssp_state) {
+ DEBUG(0,("auth_ntlmssp_start: talloc failed!\n"));
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ZERO_STRUCTP(*auth_ntlmssp_state);
+
+ (*auth_ntlmssp_state)->mem_ctx = mem_ctx;
+
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_server_start(&(*auth_ntlmssp_state)->ntlmssp_state))) {
+ return nt_status;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(&(*auth_ntlmssp_state)->auth_context))) {
+ return nt_status;
+ }
+
+ (*auth_ntlmssp_state)->ntlmssp_state->auth_context = (*auth_ntlmssp_state);
+ (*auth_ntlmssp_state)->ntlmssp_state->get_challenge = auth_ntlmssp_get_challenge;
+ (*auth_ntlmssp_state)->ntlmssp_state->may_set_challenge = auth_ntlmssp_may_set_challenge;
+ (*auth_ntlmssp_state)->ntlmssp_state->set_challenge = auth_ntlmssp_set_challenge;
+ (*auth_ntlmssp_state)->ntlmssp_state->check_password = auth_ntlmssp_check_password;
+ (*auth_ntlmssp_state)->ntlmssp_state->server_role = lp_server_role();
+
+ return NT_STATUS_OK;
+}
+
+void auth_ntlmssp_end(AUTH_NTLMSSP_STATE **auth_ntlmssp_state)
+{
+ TALLOC_CTX *mem_ctx = (*auth_ntlmssp_state)->mem_ctx;
+
+ if ((*auth_ntlmssp_state)->ntlmssp_state) {
+ ntlmssp_end(&(*auth_ntlmssp_state)->ntlmssp_state);
+ }
+ if ((*auth_ntlmssp_state)->auth_context) {
+ ((*auth_ntlmssp_state)->auth_context->free)(&(*auth_ntlmssp_state)->auth_context);
+ }
+ if ((*auth_ntlmssp_state)->server_info) {
+ free_server_info(&(*auth_ntlmssp_state)->server_info);
+ }
+ talloc_destroy(mem_ctx);
+ *auth_ntlmssp_state = NULL;
+}
+
+NTSTATUS auth_ntlmssp_update(AUTH_NTLMSSP_STATE *auth_ntlmssp_state,
+ const DATA_BLOB request, DATA_BLOB *reply)
+{
+ return ntlmssp_update(auth_ntlmssp_state->ntlmssp_state, request, reply);
+}
diff --git a/source/auth/auth_rhosts.c b/source/auth/auth_rhosts.c
new file mode 100644
index 00000000000..b295df9328f
--- /dev/null
+++ b/source/auth/auth_rhosts.c
@@ -0,0 +1,257 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main SMB reply routines
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+/****************************************************************************
+ Read the a hosts.equiv or .rhosts file and check if it
+ allows this user from this machine.
+****************************************************************************/
+
+static BOOL check_user_equiv(const char *user, const char *remote, const char *equiv_file)
+{
+ int plus_allowed = 1;
+ char *file_host;
+ char *file_user;
+ char **lines = file_lines_load(equiv_file, NULL);
+ int i;
+
+ DEBUG(5, ("check_user_equiv %s %s %s\n", user, remote, equiv_file));
+ if (! lines) return False;
+ for (i=0; lines[i]; i++) {
+ char *buf = lines[i];
+ trim_char(buf,' ',' ');
+
+ if (buf[0] != '#' && buf[0] != '\n')
+ {
+ BOOL is_group = False;
+ int plus = 1;
+ char *bp = buf;
+ if (strcmp(buf, "NO_PLUS\n") == 0)
+ {
+ DEBUG(6, ("check_user_equiv NO_PLUS\n"));
+ plus_allowed = 0;
+ }
+ else {
+ if (buf[0] == '+')
+ {
+ bp++;
+ if (*bp == '\n' && plus_allowed)
+ {
+ /* a bare plus means everbody allowed */
+ DEBUG(6, ("check_user_equiv everybody allowed\n"));
+ file_lines_free(lines);
+ return True;
+ }
+ }
+ else if (buf[0] == '-')
+ {
+ bp++;
+ plus = 0;
+ }
+ if (*bp == '@')
+ {
+ is_group = True;
+ bp++;
+ }
+ file_host = strtok(bp, " \t\n");
+ file_user = strtok(NULL, " \t\n");
+ DEBUG(7, ("check_user_equiv %s %s\n", file_host ? file_host : "(null)",
+ file_user ? file_user : "(null)" ));
+ if (file_host && *file_host)
+ {
+ BOOL host_ok = False;
+
+#if defined(HAVE_NETGROUP) && defined(HAVE_YP_GET_DEFAULT_DOMAIN)
+ if (is_group)
+ {
+ static char *mydomain = NULL;
+ if (!mydomain)
+ yp_get_default_domain(&mydomain);
+ if (mydomain && innetgr(file_host,remote,user,mydomain))
+ host_ok = True;
+ }
+#else
+ if (is_group)
+ {
+ DEBUG(1,("Netgroups not configured\n"));
+ continue;
+ }
+#endif
+
+ /* is it this host */
+ /* the fact that remote has come from a call of gethostbyaddr
+ * means that it may have the fully qualified domain name
+ * so we could look up the file version to get it into
+ * a canonical form, but I would rather just type it
+ * in full in the equiv file
+ */
+ if (!host_ok && !is_group && strequal(remote, file_host))
+ host_ok = True;
+
+ if (!host_ok)
+ continue;
+
+ /* is it this user */
+ if (file_user == 0 || strequal(user, file_user))
+ {
+ DEBUG(5, ("check_user_equiv matched %s%s %s\n",
+ (plus ? "+" : "-"), file_host,
+ (file_user ? file_user : "")));
+ file_lines_free(lines);
+ return (plus ? True : False);
+ }
+ }
+ }
+ }
+ }
+ file_lines_free(lines);
+ return False;
+}
+
+/****************************************************************************
+check for a possible hosts equiv or rhosts entry for the user
+****************************************************************************/
+
+static BOOL check_hosts_equiv(SAM_ACCOUNT *account)
+{
+ uid_t uid;
+ char *fname = NULL;
+
+ fname = lp_hosts_equiv();
+ if (!NT_STATUS_IS_OK(sid_to_uid(pdb_get_user_sid(account), &uid)))
+ return False;
+
+ /* note: don't allow hosts.equiv on root */
+ if (fname && *fname && uid != 0) {
+ if (check_user_equiv(pdb_get_username(account),client_name(),fname))
+ return True;
+ }
+
+ return False;
+}
+
+
+/****************************************************************************
+ Check for a valid .rhosts/hosts.equiv entry for this user
+****************************************************************************/
+
+static NTSTATUS check_hostsequiv_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ NTSTATUS nt_status;
+ SAM_ACCOUNT *account = NULL;
+ if (!NT_STATUS_IS_OK(nt_status =
+ auth_get_sam_account(user_info->internal_username.str,
+ &account))) {
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER))
+ nt_status = NT_STATUS_NOT_IMPLEMENTED;
+ return nt_status;
+ }
+
+ if (check_hosts_equiv(account)) {
+ nt_status = make_server_info_sam(server_info, account);
+ } else {
+ pdb_free_sam(&account);
+ nt_status = NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return nt_status;
+}
+
+/* module initialisation */
+static NTSTATUS auth_init_hostsequiv(struct auth_context *auth_context, const char* param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*auth_method)->auth = check_hostsequiv_security;
+ (*auth_method)->name = "hostsequiv";
+ return NT_STATUS_OK;
+}
+
+
+/****************************************************************************
+ Check for a valid .rhosts/hosts.equiv entry for this user
+****************************************************************************/
+
+static NTSTATUS check_rhosts_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ NTSTATUS nt_status;
+ SAM_ACCOUNT *account = NULL;
+ pstring rhostsfile;
+ const char *home;
+
+ if (!NT_STATUS_IS_OK(nt_status =
+ auth_get_sam_account(user_info->internal_username.str,
+ &account))) {
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER))
+ nt_status = NT_STATUS_NOT_IMPLEMENTED;
+ return nt_status;
+ }
+
+ home = pdb_get_unix_homedir(account);
+
+ if (home) {
+ slprintf(rhostsfile, sizeof(rhostsfile)-1, "%s/.rhosts", home);
+ become_root();
+ if (check_user_equiv(pdb_get_username(account),client_name(),rhostsfile)) {
+ nt_status = make_server_info_sam(server_info, account);
+ } else {
+ pdb_free_sam(&account);
+ }
+ unbecome_root();
+ } else {
+ pdb_free_sam(&account);
+ nt_status = NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return nt_status;
+}
+
+/* module initialisation */
+static NTSTATUS auth_init_rhosts(struct auth_context *auth_context, const char *param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*auth_method)->auth = check_rhosts_security;
+ (*auth_method)->name = "rhosts";
+ return NT_STATUS_OK;
+}
+
+NTSTATUS auth_rhosts_init(void)
+{
+ smb_register_auth(AUTH_INTERFACE_VERSION, "rhosts", auth_init_rhosts);
+ smb_register_auth(AUTH_INTERFACE_VERSION, "hostsequiv", auth_init_hostsequiv);
+ return NT_STATUS_OK;
+}
diff --git a/source/auth/auth_sam.c b/source/auth/auth_sam.c
new file mode 100644
index 00000000000..b35cc78d6b2
--- /dev/null
+++ b/source/auth/auth_sam.c
@@ -0,0 +1,343 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+ Copyright (C) Andrew Bartlett 2001-2003
+ Copyright (C) Gerald Carter 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+/****************************************************************************
+ Do a specific test for an smb password being correct, given a smb_password and
+ the lanman and NT responses.
+****************************************************************************/
+
+static NTSTATUS sam_password_ok(const struct auth_context *auth_context,
+ TALLOC_CTX *mem_ctx,
+ SAM_ACCOUNT *sampass,
+ const auth_usersupplied_info *user_info,
+ DATA_BLOB *user_sess_key,
+ DATA_BLOB *lm_sess_key)
+{
+ uint16 acct_ctrl;
+ const uint8 *lm_pw, *nt_pw;
+ const char *username = pdb_get_username(sampass);
+
+ acct_ctrl = pdb_get_acct_ctrl(sampass);
+ if (acct_ctrl & ACB_PWNOTREQ) {
+ if (lp_null_passwords()) {
+ DEBUG(3,("Account for user '%s' has no password and null passwords are allowed.\n", username));
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(3,("Account for user '%s' has no password and null passwords are NOT allowed.\n", username));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+ }
+
+ lm_pw = pdb_get_lanman_passwd(sampass);
+ nt_pw = pdb_get_nt_passwd(sampass);
+
+ return ntlm_password_check(mem_ctx, &auth_context->challenge,
+ &user_info->lm_resp, &user_info->nt_resp,
+ username,
+ user_info->smb_name.str,
+ user_info->client_domain.str,
+ lm_pw, nt_pw, user_sess_key, lm_sess_key);
+}
+
+
+/****************************************************************************
+ Do a specific test for a SAM_ACCOUNT being vaild for this connection
+ (ie not disabled, expired and the like).
+****************************************************************************/
+
+static NTSTATUS sam_account_ok(TALLOC_CTX *mem_ctx,
+ SAM_ACCOUNT *sampass,
+ const auth_usersupplied_info *user_info)
+{
+ uint16 acct_ctrl = pdb_get_acct_ctrl(sampass);
+ char *workstation_list;
+ time_t kickoff_time;
+
+ DEBUG(4,("sam_account_ok: Checking SMB password for user %s\n",pdb_get_username(sampass)));
+
+ /* Quit if the account was disabled. */
+ if (acct_ctrl & ACB_DISABLED) {
+ DEBUG(1,("sam_account_ok: Account for user '%s' was disabled.\n", pdb_get_username(sampass)));
+ return NT_STATUS_ACCOUNT_DISABLED;
+ }
+
+ /* Quit if the account was locked out. */
+ if (acct_ctrl & ACB_AUTOLOCK) {
+ DEBUG(1,("sam_account_ok: Account for user %s was locked out.\n", pdb_get_username(sampass)));
+ return NT_STATUS_ACCOUNT_LOCKED_OUT;
+ }
+
+ /* Test account expire time */
+
+ kickoff_time = pdb_get_kickoff_time(sampass);
+ if (kickoff_time != 0 && time(NULL) > kickoff_time) {
+ DEBUG(1,("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)) {
+ time_t must_change_time = pdb_get_pass_must_change_time(sampass);
+ time_t last_set_time = pdb_get_pass_last_set_time(sampass);
+
+ /* check for immediate expiry "must change at next logon" */
+ if (must_change_time == 0 && last_set_time != 0) {
+ DEBUG(1,("sam_account_ok: Account for user '%s' password must change!.\n", pdb_get_username(sampass)));
+ return NT_STATUS_PASSWORD_MUST_CHANGE;
+ }
+
+ /* check for expired password */
+ if (must_change_time < time(NULL) && must_change_time != 0) {
+ DEBUG(1,("sam_account_ok: Account for user '%s' password expired!.\n", pdb_get_username(sampass)));
+ DEBUG(1,("sam_account_ok: Password expired at '%s' (%ld) unix time.\n", http_timestring(must_change_time), (long)must_change_time));
+ return NT_STATUS_PASSWORD_EXPIRED;
+ }
+ }
+
+ /* Test workstation. Workstation list is comma separated. */
+
+ workstation_list = talloc_strdup(mem_ctx, pdb_get_workstations(sampass));
+ if (!workstation_list)
+ return NT_STATUS_NO_MEMORY;
+
+ if (*workstation_list) {
+ BOOL invalid_ws = True;
+ const char *s = workstation_list;
+
+ fstring tok;
+
+ while (next_token(&s, tok, ",", sizeof(tok))) {
+ DEBUG(10,("sam_account_ok: checking for workstation match %s and %s (len=%d)\n",
+ tok, user_info->wksta_name.str, user_info->wksta_name.len));
+ if(strequal(tok, user_info->wksta_name.str)) {
+ invalid_ws = False;
+ break;
+ }
+ }
+
+ if (invalid_ws)
+ return NT_STATUS_INVALID_WORKSTATION;
+ }
+
+ if (acct_ctrl & ACB_DOMTRUST) {
+ DEBUG(2,("sam_account_ok: Domain trust account %s denied by server\n", pdb_get_username(sampass)));
+ return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
+ }
+
+ if (acct_ctrl & ACB_SVRTRUST) {
+ DEBUG(2,("sam_account_ok: Server trust account %s denied by server\n", pdb_get_username(sampass)));
+ return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
+ }
+
+ if (acct_ctrl & ACB_WSTRUST) {
+ DEBUG(4,("sam_account_ok: Wksta trust account %s denied by server\n", pdb_get_username(sampass)));
+ return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+check if a username/password is OK assuming the password is a 24 byte
+SMB hash supplied in the user_info structure
+return an NT_STATUS constant.
+****************************************************************************/
+
+static NTSTATUS check_sam_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ SAM_ACCOUNT *sampass=NULL;
+ BOOL ret;
+ NTSTATUS nt_status;
+ DATA_BLOB user_sess_key = data_blob(NULL, 0);
+ DATA_BLOB lm_sess_key = data_blob(NULL, 0);
+ BOOL updated_autolock = False, updated_badpw = False;
+
+ if (!user_info || !auth_context) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Can't use the talloc version here, because the returned struct gets
+ kept on the server_info */
+ if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam(&sampass))) {
+ return nt_status;
+ }
+
+ /* get the account information */
+
+ become_root();
+ ret = pdb_getsampwnam(sampass, user_info->internal_username.str);
+ unbecome_root();
+
+ if (ret == False) {
+ DEBUG(3,("check_sam_security: Couldn't find user '%s' in passdb file.\n", user_info->internal_username.str));
+ pdb_free_sam(&sampass);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ /* see if autolock flag needs to be updated */
+ if (pdb_get_acct_ctrl(sampass) & ACB_NORMAL)
+ pdb_update_autolock_flag(sampass, &updated_autolock);
+ /* Quit if the account was locked out. */
+ if (pdb_get_acct_ctrl(sampass) & ACB_AUTOLOCK) {
+ DEBUG(3,("check_sam_security: Account for user %s was locked out.\n", pdb_get_username(sampass)));
+ return NT_STATUS_ACCOUNT_LOCKED_OUT;
+ }
+
+ nt_status = sam_password_ok(auth_context, mem_ctx, sampass,
+ user_info, &user_sess_key, &lm_sess_key);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ if (NT_STATUS_EQUAL(nt_status,NT_STATUS_WRONG_PASSWORD) &&
+ pdb_get_acct_ctrl(sampass) &ACB_NORMAL) {
+ pdb_increment_bad_password_count(sampass);
+ updated_badpw = True;
+ } else {
+ pdb_update_bad_password_count(sampass,
+ &updated_badpw);
+ }
+ if (updated_autolock || updated_badpw){
+ become_root();
+ if(!pdb_update_sam_account(sampass))
+ DEBUG(1, ("Failed to modify entry.\n"));
+ unbecome_root();
+ }
+ pdb_free_sam(&sampass);
+ return nt_status;
+ }
+
+ if ((pdb_get_acct_ctrl(sampass) & ACB_NORMAL) &&
+ (pdb_get_bad_password_count(sampass) > 0)){
+ pdb_set_bad_password_count(sampass, 0, PDB_CHANGED);
+ pdb_set_bad_password_time(sampass, 0, PDB_CHANGED);
+ updated_badpw = True;
+ }
+
+ if (updated_autolock || updated_badpw){
+ become_root();
+ if(!pdb_update_sam_account(sampass))
+ DEBUG(1, ("Failed to modify entry.\n"));
+ unbecome_root();
+ }
+
+ nt_status = sam_account_ok(mem_ctx, sampass, user_info);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ pdb_free_sam(&sampass);
+ return nt_status;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = make_server_info_sam(server_info, sampass))) {
+ DEBUG(0,("check_sam_security: make_server_info_sam() failed with '%s'\n", nt_errstr(nt_status)));
+ return nt_status;
+ }
+
+ (*server_info)->nt_session_key = user_sess_key;
+ (*server_info)->lm_session_key = lm_sess_key;
+
+ return nt_status;
+}
+
+/* module initialisation */
+static NTSTATUS auth_init_sam_ignoredomain(struct auth_context *auth_context, const char *param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*auth_method)->auth = check_sam_security;
+ (*auth_method)->name = "sam_ignoredomain";
+ return NT_STATUS_OK;
+}
+
+
+/****************************************************************************
+Check SAM security (above) but with a few extra checks.
+****************************************************************************/
+
+static NTSTATUS check_samstrict_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ BOOL is_local_name, is_my_domain;
+
+ if (!user_info || !auth_context) {
+ return NT_STATUS_LOGON_FAILURE;
+ }
+
+ is_local_name = is_myname(user_info->domain.str);
+ is_my_domain = strequal(user_info->domain.str, lp_workgroup());
+
+ /* check whether or not we service this domain/workgroup name */
+
+ switch ( lp_server_role() ) {
+ case ROLE_STANDALONE:
+ case ROLE_DOMAIN_MEMBER:
+ if ( !is_local_name ) {
+ DEBUG(6,("check_samstrict_security: %s is not one of my local names (%s)\n",
+ user_info->domain.str, (lp_server_role() == ROLE_DOMAIN_MEMBER
+ ? "ROLE_DOMAIN_MEMBER" : "ROLE_STANDALONE") ));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ case ROLE_DOMAIN_PDC:
+ case ROLE_DOMAIN_BDC:
+ if ( !is_local_name && !is_my_domain ) {
+ DEBUG(6,("check_samstrict_security: %s is not one of my local names or domain name (DC)\n",
+ user_info->domain.str));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ default: /* name is ok */
+ break;
+ }
+
+ return check_sam_security(auth_context, my_private_data, mem_ctx, user_info, server_info);
+}
+
+/* module initialisation */
+static NTSTATUS auth_init_sam(struct auth_context *auth_context, const char *param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*auth_method)->auth = check_samstrict_security;
+ (*auth_method)->name = "sam";
+ return NT_STATUS_OK;
+}
+
+NTSTATUS auth_sam_init(void)
+{
+ smb_register_auth(AUTH_INTERFACE_VERSION, "sam", auth_init_sam);
+ smb_register_auth(AUTH_INTERFACE_VERSION, "sam_ignoredomain", auth_init_sam_ignoredomain);
+ return NT_STATUS_OK;
+}
diff --git a/source/auth/auth_server.c b/source/auth/auth_server.c
new file mode 100644
index 00000000000..bc611ec229b
--- /dev/null
+++ b/source/auth/auth_server.c
@@ -0,0 +1,417 @@
+/*
+ Unix SMB/CIFS implementation.
+ Authenticate to a remote server
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Andrew Bartlett 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+extern userdom_struct current_user_info;
+
+/****************************************************************************
+ Support for server level security.
+****************************************************************************/
+
+static struct cli_state *server_cryptkey(TALLOC_CTX *mem_ctx)
+{
+ struct cli_state *cli = NULL;
+ fstring desthost;
+ struct in_addr dest_ip;
+ const char *p;
+ char *pserver;
+ BOOL connected_ok = False;
+
+ if (!(cli = cli_initialise(cli)))
+ return NULL;
+
+ /* security = server just can't function with spnego */
+ cli->use_spnego = False;
+
+ pserver = talloc_strdup(mem_ctx, lp_passwordserver());
+ p = pserver;
+
+ while(next_token( &p, desthost, LIST_SEP, sizeof(desthost))) {
+ standard_sub_basic(current_user_info.smb_name, desthost, sizeof(desthost));
+ strupper_m(desthost);
+
+ if(!resolve_name( desthost, &dest_ip, 0x20)) {
+ DEBUG(1,("server_cryptkey: Can't resolve address for %s\n",desthost));
+ continue;
+ }
+
+ if (ismyip(dest_ip)) {
+ DEBUG(1,("Password server loop - disabling password server %s\n",desthost));
+ continue;
+ }
+
+ /* we use a mutex to prevent two connections at once - when a
+ Win2k PDC get two connections where one hasn't completed a
+ session setup yet it will send a TCP reset to the first
+ connection (tridge) */
+
+ if (!grab_server_mutex(desthost)) {
+ return NULL;
+ }
+
+ if (cli_connect(cli, desthost, &dest_ip)) {
+ DEBUG(3,("connected to password server %s\n",desthost));
+ connected_ok = True;
+ break;
+ }
+ }
+
+ if (!connected_ok) {
+ release_server_mutex();
+ DEBUG(0,("password server not available\n"));
+ cli_shutdown(cli);
+ return NULL;
+ }
+
+ if (!attempt_netbios_session_request(cli, global_myname(),
+ desthost, &dest_ip)) {
+ release_server_mutex();
+ DEBUG(1,("password server fails session request\n"));
+ cli_shutdown(cli);
+ return NULL;
+ }
+
+ if (strequal(desthost,myhostname())) {
+ exit_server("Password server loop!");
+ }
+
+ DEBUG(3,("got session\n"));
+
+ if (!cli_negprot(cli)) {
+ DEBUG(1,("%s rejected the negprot\n",desthost));
+ release_server_mutex();
+ cli_shutdown(cli);
+ return NULL;
+ }
+
+ if (cli->protocol < PROTOCOL_LANMAN2 ||
+ !(cli->sec_mode & NEGOTIATE_SECURITY_USER_LEVEL)) {
+ DEBUG(1,("%s isn't in user level security mode\n",desthost));
+ release_server_mutex();
+ cli_shutdown(cli);
+ return NULL;
+ }
+
+ /* Get the first session setup done quickly, to avoid silly
+ Win2k bugs. (The next connection to the server will kill
+ this one...
+ */
+
+ if (!cli_session_setup(cli, "", "", 0, "", 0,
+ "")) {
+ DEBUG(0,("%s rejected the initial session setup (%s)\n",
+ desthost, cli_errstr(cli)));
+ release_server_mutex();
+ cli_shutdown(cli);
+ return NULL;
+ }
+
+ release_server_mutex();
+
+ DEBUG(3,("password server OK\n"));
+
+ return cli;
+}
+
+/****************************************************************************
+ Clean up our allocated cli.
+****************************************************************************/
+
+static void free_server_private_data(void **private_data_pointer)
+{
+ struct cli_state **cli = (struct cli_state **)private_data_pointer;
+ if (*cli && (*cli)->initialised) {
+ DEBUG(10, ("Shutting down smbserver connection\n"));
+ cli_shutdown(*cli);
+ }
+ *private_data_pointer = NULL;
+}
+
+/****************************************************************************
+ Send a 'keepalive' packet down the cli pipe.
+****************************************************************************/
+
+static void send_server_keepalive(void **private_data_pointer)
+{
+ /* also send a keepalive to the password server if its still
+ connected */
+ if (private_data_pointer) {
+ struct cli_state *cli = (struct cli_state *)(*private_data_pointer);
+ if (cli && cli->initialised) {
+ if (!send_keepalive(cli->fd)) {
+ DEBUG( 2, ( "send_server_keepalive: password server keepalive failed.\n"));
+ cli_shutdown(cli);
+ *private_data_pointer = NULL;
+ }
+ }
+ }
+}
+
+/****************************************************************************
+ Get the challenge out of a password server.
+****************************************************************************/
+
+static DATA_BLOB auth_get_challenge_server(const struct auth_context *auth_context,
+ void **my_private_data,
+ TALLOC_CTX *mem_ctx)
+{
+ struct cli_state *cli = server_cryptkey(mem_ctx);
+
+ if (cli) {
+ DEBUG(3,("using password server validation\n"));
+
+ if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) {
+ /* We can't work with unencrypted password servers
+ unless 'encrypt passwords = no' */
+ DEBUG(5,("make_auth_info_server: Server is unencrypted, no challenge available..\n"));
+
+ /* However, it is still a perfectly fine connection
+ to pass that unencrypted password over */
+ *my_private_data = (void *)cli;
+ return data_blob(NULL, 0);
+
+ } else if (cli->secblob.length < 8) {
+ /* We can't do much if we don't get a full challenge */
+ DEBUG(2,("make_auth_info_server: Didn't receive a full challenge from server\n"));
+ cli_shutdown(cli);
+ return data_blob(NULL, 0);
+ }
+
+ *my_private_data = (void *)cli;
+
+ /* The return must be allocated on the caller's mem_ctx, as our own will be
+ destoyed just after the call. */
+ return data_blob_talloc(auth_context->mem_ctx, cli->secblob.data,8);
+ } else {
+ return data_blob(NULL, 0);
+ }
+}
+
+
+/****************************************************************************
+ Check for a valid username and password in security=server mode.
+ - Validate a password with the password server.
+****************************************************************************/
+
+static NTSTATUS check_smbserver_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ struct cli_state *cli;
+ static unsigned char badpass[24];
+ static fstring baduser;
+ static BOOL tested_password_server = False;
+ static BOOL bad_password_server = False;
+ NTSTATUS nt_status = NT_STATUS_NOT_IMPLEMENTED;
+ BOOL locally_made_cli = False;
+
+ /*
+ * Check that the requested domain is not our own machine name.
+ * If it is, we should never check the PDC here, we use our own local
+ * password file.
+ */
+
+ if(is_myname(user_info->domain.str)) {
+ DEBUG(3,("check_smbserver_security: Requested domain was for this machine.\n"));
+ return nt_status;
+ }
+
+ cli = my_private_data;
+
+ if (cli) {
+ } else {
+ cli = server_cryptkey(mem_ctx);
+ locally_made_cli = True;
+ }
+
+ if (!cli || !cli->initialised) {
+ DEBUG(1,("password server is not connected (cli not initilised)\n"));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+
+ if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) {
+ if (user_info->encrypted) {
+ DEBUG(1,("password server %s is plaintext, but we are encrypted. This just can't work :-(\n", cli->desthost));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+ } else {
+ if (memcmp(cli->secblob.data, auth_context->challenge.data, 8) != 0) {
+ DEBUG(1,("the challenge that the password server (%s) supplied us is not the one we gave our client. This just can't work :-(\n", cli->desthost));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+ }
+
+ if(badpass[0] == 0)
+ memset(badpass, 0x1f, sizeof(badpass));
+
+ if((user_info->nt_resp.length == sizeof(badpass)) &&
+ !memcmp(badpass, user_info->nt_resp.data, sizeof(badpass))) {
+ /*
+ * Very unlikely, our random bad password is the same as the users
+ * password.
+ */
+ memset(badpass, badpass[0]+1, sizeof(badpass));
+ }
+
+ if(baduser[0] == 0) {
+ fstrcpy(baduser, INVALID_USER_PREFIX);
+ fstrcat(baduser, global_myname());
+ }
+
+ /*
+ * Attempt a session setup with a totally incorrect password.
+ * If this succeeds with the guest bit *NOT* set then the password
+ * server is broken and is not correctly setting the guest bit. We
+ * need to detect this as some versions of NT4.x are broken. JRA.
+ */
+
+ /* I sure as hell hope that there aren't servers out there that take
+ * NTLMv2 and have this bug, as we don't test for that...
+ * - abartlet@samba.org
+ */
+
+ if ((!tested_password_server) && (lp_paranoid_server_security())) {
+ if (cli_session_setup(cli, baduser, (char *)badpass, sizeof(badpass),
+ (char *)badpass, sizeof(badpass), user_info->domain.str)) {
+
+ /*
+ * We connected to the password server so we
+ * can say we've tested it.
+ */
+ tested_password_server = True;
+
+ if ((SVAL(cli->inbuf,smb_vwv2) & 1) == 0) {
+ DEBUG(0,("server_validate: password server %s allows users as non-guest \
+with a bad password.\n", cli->desthost));
+ DEBUG(0,("server_validate: This is broken (and insecure) behaviour. Please do not \
+use this machine as the password server.\n"));
+ cli_ulogoff(cli);
+
+ /*
+ * Password server has the bug.
+ */
+ bad_password_server = True;
+ return NT_STATUS_LOGON_FAILURE;
+ }
+ cli_ulogoff(cli);
+ }
+ } else {
+
+ /*
+ * We have already tested the password server.
+ * Fail immediately if it has the bug.
+ */
+
+ if(bad_password_server) {
+ DEBUG(0,("server_validate: [1] password server %s allows users as non-guest \
+with a bad password.\n", cli->desthost));
+ DEBUG(0,("server_validate: [1] This is broken (and insecure) behaviour. Please do not \
+use this machine as the password server.\n"));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+ }
+
+ /*
+ * Now we know the password server will correctly set the guest bit, or is
+ * not guest enabled, we can try with the real password.
+ */
+
+ if (!user_info->encrypted) {
+ /* Plaintext available */
+ if (!cli_session_setup(cli, user_info->smb_name.str,
+ (char *)user_info->plaintext_password.data,
+ user_info->plaintext_password.length,
+ NULL, 0,
+ user_info->domain.str)) {
+ DEBUG(1,("password server %s rejected the password\n", cli->desthost));
+ /* Make this cli_nt_error() when the conversion is in */
+ nt_status = cli_nt_error(cli);
+ } else {
+ nt_status = NT_STATUS_OK;
+ }
+ } else {
+ if (!cli_session_setup(cli, user_info->smb_name.str,
+ (char *)user_info->lm_resp.data,
+ user_info->lm_resp.length,
+ (char *)user_info->nt_resp.data,
+ user_info->nt_resp.length,
+ user_info->domain.str)) {
+ DEBUG(1,("password server %s rejected the password\n", cli->desthost));
+ /* Make this cli_nt_error() when the conversion is in */
+ nt_status = cli_nt_error(cli);
+ } else {
+ nt_status = NT_STATUS_OK;
+ }
+ }
+
+ /* if logged in as guest then reject */
+ if ((SVAL(cli->inbuf,smb_vwv2) & 1) != 0) {
+ DEBUG(1,("password server %s gave us guest only\n", cli->desthost));
+ nt_status = NT_STATUS_LOGON_FAILURE;
+ }
+
+ cli_ulogoff(cli);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ fstring real_username;
+ struct passwd *pass;
+
+ if ( (pass = smb_getpwnam( user_info->internal_username.str,
+ real_username, True )) != NULL )
+ {
+ nt_status = make_server_info_pw(server_info, pass->pw_name, pass);
+ }
+ else
+ {
+ nt_status = NT_STATUS_NO_SUCH_USER;
+ }
+ }
+
+ if (locally_made_cli) {
+ cli_shutdown(cli);
+ }
+
+ return(nt_status);
+}
+
+static NTSTATUS auth_init_smbserver(struct auth_context *auth_context, const char* param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ (*auth_method)->name = "smbserver";
+ (*auth_method)->auth = check_smbserver_security;
+ (*auth_method)->get_chal = auth_get_challenge_server;
+ (*auth_method)->send_keepalive = send_server_keepalive;
+ (*auth_method)->free_private_data = free_server_private_data;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS auth_server_init(void)
+{
+ return smb_register_auth(AUTH_INTERFACE_VERSION, "smbserver", auth_init_smbserver);
+}
diff --git a/source/auth/auth_unix.c b/source/auth/auth_unix.c
new file mode 100644
index 00000000000..f744cba0c43
--- /dev/null
+++ b/source/auth/auth_unix.c
@@ -0,0 +1,136 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Bartlett 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+/**
+ * update the encrypted smbpasswd file from the plaintext username and password
+ *
+ * this ugly hack needs to die, but not quite yet, I think people still use it...
+ **/
+static BOOL update_smbpassword_file(const char *user, const char *password)
+{
+ SAM_ACCOUNT *sampass = NULL;
+ BOOL ret;
+
+ pdb_init_sam(&sampass);
+
+ become_root();
+ ret = pdb_getsampwnam(sampass, user);
+ unbecome_root();
+
+ if(ret == False) {
+ DEBUG(0,("pdb_getsampwnam returned NULL\n"));
+ pdb_free_sam(&sampass);
+ return False;
+ }
+
+ /*
+ * Remove the account disabled flag - we are updating the
+ * users password from a login.
+ */
+ if (!pdb_set_acct_ctrl(sampass, pdb_get_acct_ctrl(sampass) & ~ACB_DISABLED, PDB_CHANGED)) {
+ pdb_free_sam(&sampass);
+ return False;
+ }
+
+ if (!pdb_set_plaintext_passwd (sampass, password)) {
+ pdb_free_sam(&sampass);
+ return False;
+ }
+
+ /* Now write it into the file. */
+ become_root();
+
+ ret = pdb_update_sam_account (sampass);
+
+ unbecome_root();
+
+ if (ret) {
+ DEBUG(3,("pdb_update_sam_account returned %d\n",ret));
+ }
+
+ pdb_free_sam(&sampass);
+ return ret;
+}
+
+
+/** Check a plaintext username/password
+ *
+ * Cannot deal with an encrupted password in any manner whatsoever,
+ * unless the account has a null password.
+ **/
+
+static NTSTATUS check_unix_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ NTSTATUS nt_status;
+ struct passwd *pass = NULL;
+
+ become_root();
+ pass = Get_Pwnam(user_info->internal_username.str);
+
+
+ /** @todo This call assumes a ASCII password, no charset transformation is
+ done. We may need to revisit this **/
+ nt_status = pass_check(pass,
+ pass ? pass->pw_name : user_info->internal_username.str,
+ (char *)user_info->plaintext_password.data,
+ user_info->plaintext_password.length-1,
+ lp_update_encrypted() ?
+ update_smbpassword_file : NULL,
+ True);
+
+ unbecome_root();
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ if (pass) {
+ make_server_info_pw(server_info, pass->pw_name, pass);
+ } else {
+ /* we need to do somthing more useful here */
+ nt_status = NT_STATUS_NO_SUCH_USER;
+ }
+ }
+
+ return nt_status;
+}
+
+/* module initialisation */
+static NTSTATUS auth_init_unix(struct auth_context *auth_context, const char* param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*auth_method)->name = "unix";
+ (*auth_method)->auth = check_unix_security;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS auth_unix_init(void)
+{
+ return smb_register_auth(AUTH_INTERFACE_VERSION, "unix", auth_init_unix);
+}
diff --git a/source/auth/auth_util.c b/source/auth/auth_util.c
new file mode 100644
index 00000000000..a823991bcad
--- /dev/null
+++ b/source/auth/auth_util.c
@@ -0,0 +1,1500 @@
+/*
+ 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
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+extern DOM_SID global_sid_World;
+extern DOM_SID global_sid_Network;
+extern DOM_SID global_sid_Builtin_Guests;
+extern DOM_SID global_sid_Authenticated_Users;
+
+
+/****************************************************************************
+ Create a UNIX user on demand.
+****************************************************************************/
+
+static int smb_create_user(const char *domain, const char *unix_username, const char *homedir)
+{
+ pstring add_script;
+ int ret;
+
+ pstrcpy(add_script, lp_adduser_script());
+ if (! *add_script)
+ return -1;
+ all_string_sub(add_script, "%u", unix_username, sizeof(pstring));
+ if (domain)
+ all_string_sub(add_script, "%D", domain, sizeof(pstring));
+ if (homedir)
+ all_string_sub(add_script, "%H", homedir, sizeof(pstring));
+ ret = smbrun(add_script,NULL);
+ DEBUG(3,("smb_create_user: Running the command `%s' gave %d\n",add_script,ret));
+ return ret;
+}
+
+/****************************************************************************
+ Add and Delete UNIX users on demand, based on NTSTATUS codes.
+ We don't care about RID's here so ignore.
+****************************************************************************/
+
+void auth_add_user_script(const char *domain, const char *username)
+{
+ uint32 rid;
+ /*
+ * User validated ok against Domain controller.
+ * If the admin wants us to try and create a UNIX
+ * user on the fly, do so.
+ */
+
+ if ( *lp_adduser_script() )
+ smb_create_user(domain, username, NULL);
+ else {
+ DEBUG(10,("auth_add_user_script: no 'add user script'. Asking winbindd\n"));
+
+ /* should never get here is we a re a domain member running winbindd
+ However, a host set for 'security = server' might run winbindd for
+ account allocation */
+
+ if ( !winbind_create_user(username, NULL) ) {
+ DEBUG(5,("auth_add_user_script: winbindd_create_user() failed\n"));
+ rid = 0;
+ }
+ }
+}
+
+/****************************************************************************
+ Create a SAM_ACCOUNT - either by looking in the pdb, or by faking it up from
+ unix info.
+****************************************************************************/
+
+NTSTATUS auth_get_sam_account(const char *user, SAM_ACCOUNT **account)
+{
+ BOOL pdb_ret;
+ NTSTATUS nt_status;
+ if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam(account))) {
+ return nt_status;
+ }
+
+ become_root();
+ pdb_ret = pdb_getsampwnam(*account, user);
+ unbecome_root();
+
+ if (!pdb_ret) {
+
+ struct passwd *pass = Get_Pwnam(user);
+ if (!pass)
+ return NT_STATUS_NO_SUCH_USER;
+
+ if (!NT_STATUS_IS_OK(nt_status = pdb_fill_sam_pw(*account, pass))) {
+ return nt_status;
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Create an auth_usersupplied_data structure
+****************************************************************************/
+
+static NTSTATUS make_user_info(auth_usersupplied_info **user_info,
+ const char *smb_name,
+ const char *internal_username,
+ const char *client_domain,
+ const char *domain,
+ const char *wksta_name,
+ DATA_BLOB lm_pwd, DATA_BLOB nt_pwd,
+ DATA_BLOB plaintext,
+ uint32 auth_flags, BOOL encrypted)
+{
+
+ DEBUG(5,("attempting to make a user_info for %s (%s)\n", internal_username, smb_name));
+
+ *user_info = malloc(sizeof(**user_info));
+ if (!user_info) {
+ DEBUG(0,("malloc failed for user_info (size %lu)\n", (unsigned long)sizeof(*user_info)));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ZERO_STRUCTP(*user_info);
+
+ DEBUG(5,("making strings for %s's user_info struct\n", internal_username));
+
+ (*user_info)->smb_name.str = strdup(smb_name);
+ if ((*user_info)->smb_name.str) {
+ (*user_info)->smb_name.len = strlen(smb_name);
+ } else {
+ free_user_info(user_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*user_info)->internal_username.str = strdup(internal_username);
+ if ((*user_info)->internal_username.str) {
+ (*user_info)->internal_username.len = strlen(internal_username);
+ } else {
+ free_user_info(user_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*user_info)->domain.str = strdup(domain);
+ if ((*user_info)->domain.str) {
+ (*user_info)->domain.len = strlen(domain);
+ } else {
+ free_user_info(user_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*user_info)->client_domain.str = strdup(client_domain);
+ if ((*user_info)->client_domain.str) {
+ (*user_info)->client_domain.len = strlen(client_domain);
+ } else {
+ free_user_info(user_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*user_info)->wksta_name.str = strdup(wksta_name);
+ if ((*user_info)->wksta_name.str) {
+ (*user_info)->wksta_name.len = strlen(wksta_name);
+ } else {
+ free_user_info(user_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ DEBUG(5,("making blobs for %s's user_info struct\n", internal_username));
+
+ (*user_info)->lm_resp = data_blob(lm_pwd.data, lm_pwd.length);
+ (*user_info)->nt_resp = data_blob(nt_pwd.data, nt_pwd.length);
+ (*user_info)->plaintext_password = data_blob(plaintext.data, plaintext.length);
+
+ (*user_info)->encrypted = encrypted;
+ (*user_info)->auth_flags = auth_flags;
+
+ DEBUG(10,("made an %sencrypted user_info for %s (%s)\n", encrypted ? "":"un" , internal_username, smb_name));
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Create an auth_usersupplied_data structure after appropriate mapping.
+****************************************************************************/
+
+NTSTATUS make_user_info_map(auth_usersupplied_info **user_info,
+ const char *smb_name,
+ const char *client_domain,
+ const char *wksta_name,
+ DATA_BLOB lm_pwd, DATA_BLOB nt_pwd,
+ DATA_BLOB plaintext,
+ uint32 ntlmssp_flags, BOOL encrypted)
+{
+ const char *domain;
+ fstring internal_username;
+ fstrcpy(internal_username, smb_name);
+ map_username(internal_username);
+
+ DEBUG(5, ("make_user_info_map: Mapping user [%s]\\[%s] from workstation [%s]\n",
+ client_domain, smb_name, wksta_name));
+
+ /* don't allow "" as a domain, fixes a Win9X bug
+ where it doens't supply a domain for logon script
+ 'net use' commands. */
+
+ if ( *client_domain )
+ domain = client_domain;
+ else
+ domain = lp_workgroup();
+
+ /* do what win2k does. Always map unknown domains to our own
+ and let the "passdb backend" handle unknown users. */
+
+ if ( !is_trusted_domain(domain) && !strequal(domain, get_global_sam_name()) )
+ domain = get_default_sam_name();
+
+ /* we know that it is a trusted domain (and we are allowing them) or it is our domain */
+
+ return make_user_info(user_info, smb_name, internal_username,
+ client_domain, domain, wksta_name, lm_pwd, nt_pwd,
+ plaintext, ntlmssp_flags, encrypted);
+}
+
+/****************************************************************************
+ Create an auth_usersupplied_data, making the DATA_BLOBs here.
+ Decrypt and encrypt the passwords.
+****************************************************************************/
+
+BOOL make_user_info_netlogon_network(auth_usersupplied_info **user_info,
+ const char *smb_name,
+ const char *client_domain,
+ const char *wksta_name,
+ const uchar *lm_network_pwd, int lm_pwd_len,
+ const uchar *nt_network_pwd, int nt_pwd_len)
+{
+ BOOL ret;
+ NTSTATUS nt_status;
+ DATA_BLOB lm_blob = data_blob(lm_network_pwd, lm_pwd_len);
+ DATA_BLOB nt_blob = data_blob(nt_network_pwd, nt_pwd_len);
+ DATA_BLOB plaintext_blob = data_blob(NULL, 0);
+ uint32 auth_flags = AUTH_FLAG_NONE;
+
+ if (lm_pwd_len)
+ auth_flags |= AUTH_FLAG_LM_RESP;
+ if (nt_pwd_len == 24) {
+ auth_flags |= AUTH_FLAG_NTLM_RESP;
+ } else if (nt_pwd_len != 0) {
+ auth_flags |= AUTH_FLAG_NTLMv2_RESP;
+ }
+
+ nt_status = make_user_info_map(user_info,
+ smb_name, client_domain,
+ wksta_name,
+ lm_blob, nt_blob,
+ plaintext_blob,
+ auth_flags, True);
+
+ ret = NT_STATUS_IS_OK(nt_status) ? True : False;
+
+ data_blob_free(&lm_blob);
+ data_blob_free(&nt_blob);
+ return ret;
+}
+
+/****************************************************************************
+ Create an auth_usersupplied_data, making the DATA_BLOBs here.
+ Decrypt and encrypt the passwords.
+****************************************************************************/
+
+BOOL make_user_info_netlogon_interactive(auth_usersupplied_info **user_info,
+ const char *smb_name,
+ const char *client_domain,
+ const char *wksta_name,
+ const uchar chal[8],
+ const uchar lm_interactive_pwd[16],
+ const uchar nt_interactive_pwd[16],
+ const uchar *dc_sess_key)
+{
+ char lm_pwd[16];
+ char nt_pwd[16];
+ unsigned char local_lm_response[24];
+ unsigned char local_nt_response[24];
+ unsigned char key[16];
+ uint32 auth_flags = AUTH_FLAG_NONE;
+
+ ZERO_STRUCT(key);
+ memcpy(key, dc_sess_key, 8);
+
+ if (lm_interactive_pwd) memcpy(lm_pwd, lm_interactive_pwd, sizeof(lm_pwd));
+ if (nt_interactive_pwd) memcpy(nt_pwd, nt_interactive_pwd, sizeof(nt_pwd));
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("key:"));
+ dump_data(100, (char *)key, sizeof(key));
+
+ DEBUG(100,("lm owf password:"));
+ dump_data(100, lm_pwd, sizeof(lm_pwd));
+
+ DEBUG(100,("nt owf password:"));
+ dump_data(100, nt_pwd, sizeof(nt_pwd));
+#endif
+
+ SamOEMhash((uchar *)lm_pwd, key, sizeof(lm_pwd));
+ SamOEMhash((uchar *)nt_pwd, key, sizeof(nt_pwd));
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("decrypt of lm owf password:"));
+ dump_data(100, lm_pwd, sizeof(lm_pwd));
+
+ DEBUG(100,("decrypt of nt owf password:"));
+ dump_data(100, nt_pwd, sizeof(nt_pwd));
+#endif
+
+ SMBOWFencrypt((const unsigned char *)lm_pwd, chal, local_lm_response);
+ SMBOWFencrypt((const unsigned char *)nt_pwd, chal, local_nt_response);
+
+ /* Password info paranoia */
+ ZERO_STRUCT(lm_pwd);
+ ZERO_STRUCT(nt_pwd);
+ ZERO_STRUCT(key);
+
+ {
+ BOOL ret;
+ NTSTATUS nt_status;
+ DATA_BLOB local_lm_blob = data_blob(local_lm_response, sizeof(local_lm_response));
+ DATA_BLOB local_nt_blob = data_blob(local_nt_response, sizeof(local_nt_response));
+ DATA_BLOB plaintext_blob = data_blob(NULL, 0);
+
+ if (lm_interactive_pwd)
+ auth_flags |= AUTH_FLAG_LM_RESP;
+ if (nt_interactive_pwd)
+ auth_flags |= AUTH_FLAG_NTLM_RESP;
+
+ nt_status = make_user_info_map(user_info,
+ smb_name, client_domain,
+ wksta_name,
+ local_lm_blob,
+ local_nt_blob,
+ plaintext_blob,
+ auth_flags, True);
+
+ 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(auth_usersupplied_info **user_info,
+ const char *smb_name,
+ const char *client_domain,
+ const uint8 chal[8],
+ DATA_BLOB plaintext_password)
+{
+
+ DATA_BLOB local_lm_blob;
+ DATA_BLOB local_nt_blob;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ uint32 auth_flags = AUTH_FLAG_NONE;
+
+ /*
+ * Not encrypted - do so.
+ */
+
+ DEBUG(5,("make_user_info_for_reply: User passwords not in encrypted format.\n"));
+
+ if (plaintext_password.data) {
+ unsigned char local_lm_response[24];
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(10,("Unencrypted password (len %d):\n",plaintext_password.length));
+ dump_data(100, plaintext_password.data, plaintext_password.length);
+#endif
+
+ SMBencrypt( (const char *)plaintext_password.data, (const uchar*)chal, local_lm_response);
+ local_lm_blob = data_blob(local_lm_response, 24);
+
+ /* We can't do an NT hash here, as the password needs to be
+ case insensitive */
+ local_nt_blob = data_blob(NULL, 0);
+
+ auth_flags = (AUTH_FLAG_PLAINTEXT | AUTH_FLAG_LM_RESP);
+ } else {
+ local_lm_blob = data_blob(NULL, 0);
+ local_nt_blob = data_blob(NULL, 0);
+ }
+
+ ret = make_user_info_map(user_info, smb_name,
+ client_domain,
+ get_remote_machine_name(),
+ local_lm_blob,
+ local_nt_blob,
+ plaintext_password,
+ auth_flags, False);
+
+ data_blob_free(&local_lm_blob);
+ return NT_STATUS_IS_OK(ret) ? True : False;
+}
+
+/****************************************************************************
+ Create an auth_usersupplied_data structure
+****************************************************************************/
+
+NTSTATUS make_user_info_for_reply_enc(auth_usersupplied_info **user_info,
+ const char *smb_name,
+ const char *client_domain,
+ DATA_BLOB lm_resp, DATA_BLOB nt_resp)
+{
+ uint32 auth_flags = AUTH_FLAG_NONE;
+
+ DATA_BLOB no_plaintext_blob = data_blob(NULL, 0);
+
+ if (lm_resp.length == 24) {
+ auth_flags |= AUTH_FLAG_LM_RESP;
+ }
+ if (nt_resp.length == 0) {
+ } else if (nt_resp.length == 24) {
+ auth_flags |= AUTH_FLAG_NTLM_RESP;
+ } else {
+ auth_flags |= AUTH_FLAG_NTLMv2_RESP;
+ }
+
+ return make_user_info_map(user_info, smb_name,
+ client_domain,
+ get_remote_machine_name(),
+ lm_resp,
+ nt_resp,
+ no_plaintext_blob,
+ auth_flags, True);
+}
+
+/****************************************************************************
+ Create a guest user_info blob, for anonymous authenticaion.
+****************************************************************************/
+
+BOOL make_user_info_guest(auth_usersupplied_info **user_info)
+{
+ DATA_BLOB lm_blob = data_blob(NULL, 0);
+ DATA_BLOB nt_blob = data_blob(NULL, 0);
+ DATA_BLOB plaintext_blob = data_blob(NULL, 0);
+ uint32 auth_flags = AUTH_FLAG_NONE;
+ NTSTATUS nt_status;
+
+ nt_status = make_user_info(user_info,
+ "","",
+ "","",
+ "",
+ nt_blob, lm_blob,
+ plaintext_blob,
+ auth_flags, True);
+
+ return NT_STATUS_IS_OK(nt_status) ? True : False;
+}
+
+/****************************************************************************
+ prints a NT_USER_TOKEN to debug output.
+****************************************************************************/
+
+void debug_nt_user_token(int dbg_class, int dbg_lev, NT_USER_TOKEN *token)
+{
+ fstring sid_str;
+ size_t i;
+
+ if (!token) {
+ DEBUGC(dbg_class, dbg_lev, ("NT user token: (NULL)\n"));
+ return;
+ }
+
+ DEBUGC(dbg_class, dbg_lev, ("NT user token of user %s\n",
+ sid_to_string(sid_str, &token->user_sids[0]) ));
+ DEBUGADDC(dbg_class, dbg_lev, ("contains %lu SIDs\n", (unsigned long)token->num_sids));
+ for (i = 0; i < token->num_sids; i++)
+ DEBUGADDC(dbg_class, dbg_lev, ("SID[%3lu]: %s\n", (unsigned long)i,
+ sid_to_string(sid_str, &token->user_sids[i])));
+}
+
+/****************************************************************************
+ prints a UNIX 'token' to debug output.
+****************************************************************************/
+
+void debug_unix_user_token(int dbg_class, int dbg_lev, uid_t uid, gid_t gid, int n_groups, gid_t *groups)
+{
+ int i;
+ DEBUGC(dbg_class, dbg_lev, ("UNIX token of user %ld\n", (long int)uid));
+
+ DEBUGADDC(dbg_class, dbg_lev, ("Primary group is %ld and contains %i supplementary groups\n", (long int)gid, n_groups));
+ for (i = 0; i < n_groups; i++)
+ DEBUGADDC(dbg_class, dbg_lev, ("Group[%3i]: %ld\n", i,
+ (long int)groups[i]));
+}
+
+/****************************************************************************
+ Create the SID list for this user.
+****************************************************************************/
+
+static NTSTATUS create_nt_user_token(const DOM_SID *user_sid, const DOM_SID *group_sid,
+ int n_groupSIDs, DOM_SID *groupSIDs,
+ BOOL is_guest, NT_USER_TOKEN **token)
+{
+ NTSTATUS nt_status = NT_STATUS_OK;
+ NT_USER_TOKEN *ptoken;
+ int i;
+ int sid_ndx;
+
+ if ((ptoken = malloc( sizeof(NT_USER_TOKEN) ) ) == NULL) {
+ DEBUG(0, ("create_nt_token: Out of memory allocating token\n"));
+ nt_status = NT_STATUS_NO_MEMORY;
+ return nt_status;
+ }
+
+ ZERO_STRUCTP(ptoken);
+
+ ptoken->num_sids = n_groupSIDs + 5;
+
+ if ((ptoken->user_sids = (DOM_SID *)malloc( sizeof(DOM_SID) * ptoken->num_sids )) == NULL) {
+ DEBUG(0, ("create_nt_token: Out of memory allocating SIDs\n"));
+ nt_status = NT_STATUS_NO_MEMORY;
+ return nt_status;
+ }
+
+ memset((char*)ptoken->user_sids,0,sizeof(DOM_SID) * ptoken->num_sids);
+
+ /*
+ * Note - user SID *MUST* be first in token !
+ * se_access_check depends on this.
+ *
+ * Primary group SID is second in token. Convention.
+ */
+
+ sid_copy(&ptoken->user_sids[PRIMARY_USER_SID_INDEX], user_sid);
+ if (group_sid)
+ sid_copy(&ptoken->user_sids[PRIMARY_GROUP_SID_INDEX], group_sid);
+
+ /*
+ * Finally add the "standard" SIDs.
+ * The only difference between guest and "anonymous" (which we
+ * don't really support) is the addition of Authenticated_Users.
+ */
+
+ sid_copy(&ptoken->user_sids[2], &global_sid_World);
+ sid_copy(&ptoken->user_sids[3], &global_sid_Network);
+
+ if (is_guest)
+ sid_copy(&ptoken->user_sids[4], &global_sid_Builtin_Guests);
+ else
+ sid_copy(&ptoken->user_sids[4], &global_sid_Authenticated_Users);
+
+ sid_ndx = 5; /* next available spot */
+
+ for (i = 0; i < n_groupSIDs; i++) {
+ size_t check_sid_idx;
+ for (check_sid_idx = 1; check_sid_idx < ptoken->num_sids; check_sid_idx++) {
+ if (sid_equal(&ptoken->user_sids[check_sid_idx],
+ &groupSIDs[i])) {
+ break;
+ }
+ }
+
+ if (check_sid_idx >= ptoken->num_sids) /* Not found already */ {
+ sid_copy(&ptoken->user_sids[sid_ndx++], &groupSIDs[i]);
+ } else {
+ ptoken->num_sids--;
+ }
+ }
+
+ debug_nt_user_token(DBGC_AUTH, 10, ptoken);
+
+ *token = ptoken;
+
+ return nt_status;
+}
+
+/****************************************************************************
+ Create the SID list for this user.
+****************************************************************************/
+
+NT_USER_TOKEN *create_nt_token(uid_t uid, gid_t gid, int ngroups, gid_t *groups, BOOL is_guest)
+{
+ DOM_SID user_sid;
+ DOM_SID group_sid;
+ DOM_SID *group_sids;
+ NT_USER_TOKEN *token;
+ int i;
+
+ if (!NT_STATUS_IS_OK(uid_to_sid(&user_sid, uid))) {
+ return NULL;
+ }
+ if (!NT_STATUS_IS_OK(gid_to_sid(&group_sid, gid))) {
+ return NULL;
+ }
+
+ group_sids = malloc(sizeof(DOM_SID) * ngroups);
+ if (!group_sids) {
+ DEBUG(0, ("create_nt_token: malloc() failed for DOM_SID list!\n"));
+ return NULL;
+ }
+
+ for (i = 0; i < ngroups; i++) {
+ if (!NT_STATUS_IS_OK(gid_to_sid(&(group_sids)[i], (groups)[i]))) {
+ DEBUG(1, ("create_nt_token: failed to convert gid %ld to a sid!\n", (long int)groups[i]));
+ SAFE_FREE(group_sids);
+ return NULL;
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(create_nt_user_token(&user_sid, &group_sid,
+ ngroups, group_sids, is_guest, &token))) {
+ SAFE_FREE(group_sids);
+ return NULL;
+ }
+
+ SAFE_FREE(group_sids);
+
+ return token;
+}
+
+/******************************************************************************
+ * this function returns the groups (SIDs) of the local SAM the user is in.
+ * If this samba server is a DC of the domain the user belongs to, it returns
+ * both domain groups and local / builtin groups. If the user is in a trusted
+ * domain, or samba is a member server of a domain, then this function returns
+ * local and builtin groups the user is a member of.
+ *
+ * currently this is a hack, as there is no sam implementation that is capable
+ * of groups.
+ *
+ * NOTE!! This function will fail if you pass in a winbind user without
+ * the domain --jerry
+ ******************************************************************************/
+
+static NTSTATUS get_user_groups(const char *username, uid_t uid, gid_t gid,
+ int *n_groups, DOM_SID **groups, gid_t **unix_groups)
+{
+ int n_unix_groups;
+ int i;
+
+ *n_groups = 0;
+ *groups = NULL;
+
+ /* Try winbind first */
+
+ if ( strchr(username, *lp_winbind_separator()) ) {
+ n_unix_groups = winbind_getgroups( username, unix_groups );
+
+ DEBUG(10,("get_user_groups: winbind_getgroups(%s): result = %s\n", username,
+ n_unix_groups == -1 ? "FAIL" : "SUCCESS"));
+
+ if ( n_unix_groups == -1 )
+ return NT_STATUS_NO_SUCH_USER; /* what should this return value be? */
+ }
+ else {
+ /* fallback to getgrouplist() */
+
+ n_unix_groups = groups_max();
+
+ if ((*unix_groups = malloc( sizeof(gid_t) * n_unix_groups ) ) == NULL) {
+ DEBUG(0, ("get_user_groups: Out of memory allocating unix group list\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (sys_getgrouplist(username, gid, *unix_groups, &n_unix_groups) == -1) {
+
+ gid_t *groups_tmp;
+
+ groups_tmp = Realloc(*unix_groups, sizeof(gid_t) * n_unix_groups);
+
+ if (!groups_tmp) {
+ SAFE_FREE(*unix_groups);
+ return NT_STATUS_NO_MEMORY;
+ }
+ *unix_groups = groups_tmp;
+
+ if (sys_getgrouplist(username, gid, *unix_groups, &n_unix_groups) == -1) {
+ DEBUG(0, ("get_user_groups: failed to get the unix group list\n"));
+ SAFE_FREE(*unix_groups);
+ return NT_STATUS_NO_SUCH_USER; /* what should this return value be? */
+ }
+ }
+ }
+
+ debug_unix_user_token(DBGC_CLASS, 5, uid, gid, n_unix_groups, *unix_groups);
+
+ /* now setup the space for storing the SIDS */
+
+ if (n_unix_groups > 0) {
+
+ *groups = malloc(sizeof(DOM_SID) * n_unix_groups);
+
+ if (!*groups) {
+ DEBUG(0, ("get_user_group: malloc() failed for DOM_SID list!\n"));
+ SAFE_FREE(*unix_groups);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ *n_groups = n_unix_groups;
+
+ for (i = 0; i < *n_groups; i++) {
+ if (!NT_STATUS_IS_OK(gid_to_sid(&(*groups)[i], (*unix_groups)[i]))) {
+ DEBUG(1, ("get_user_groups: failed to convert gid %ld to a sid!\n",
+ (long int)(*unix_groups)[i+1]));
+ SAFE_FREE(*groups);
+ SAFE_FREE(*unix_groups);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Make a user_info struct
+***************************************************************************/
+
+static NTSTATUS make_server_info(auth_serversupplied_info **server_info)
+{
+ *server_info = malloc(sizeof(**server_info));
+ if (!*server_info) {
+ DEBUG(0,("make_server_info: malloc failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ ZERO_STRUCTP(*server_info);
+
+ /* 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. */
+
+ (*server_info)->uid = -1;
+ (*server_info)->gid = -1;
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+Fill a server_info struct from a SAM_ACCOUNT with their groups
+***************************************************************************/
+
+static NTSTATUS add_user_groups(auth_serversupplied_info **server_info,
+ const char * unix_username,
+ SAM_ACCOUNT *sampass,
+ uid_t uid, gid_t gid)
+{
+ NTSTATUS nt_status;
+ const DOM_SID *user_sid = pdb_get_user_sid(sampass);
+ const DOM_SID *group_sid = pdb_get_group_sid(sampass);
+ int n_groupSIDs = 0;
+ DOM_SID *groupSIDs = NULL;
+ gid_t *unix_groups = NULL;
+ NT_USER_TOKEN *token;
+ BOOL is_guest;
+ uint32 rid;
+
+ nt_status = get_user_groups(unix_username, uid, gid,
+ &n_groupSIDs, &groupSIDs, &unix_groups);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(4,("get_user_groups_from_local_sam failed\n"));
+ free_server_info(server_info);
+ return nt_status;
+ }
+
+ is_guest = (sid_peek_rid(user_sid, &rid) && rid == DOMAIN_USER_RID_GUEST);
+
+ if (!NT_STATUS_IS_OK(nt_status = create_nt_user_token(user_sid, group_sid,
+ n_groupSIDs, groupSIDs, is_guest,
+ &token)))
+ {
+ DEBUG(4,("create_nt_user_token failed\n"));
+ SAFE_FREE(groupSIDs);
+ SAFE_FREE(unix_groups);
+ free_server_info(server_info);
+ return nt_status;
+ }
+
+ SAFE_FREE(groupSIDs);
+
+ (*server_info)->n_groups = n_groupSIDs;
+ (*server_info)->groups = unix_groups;
+ (*server_info)->ptok = token;
+
+ return nt_status;
+}
+
+/***************************************************************************
+Fill a server_info struct from a SAM_ACCOUNT with its privileges
+***************************************************************************/
+
+static NTSTATUS add_privileges(auth_serversupplied_info **server_info)
+{
+ PRIVILEGE_SET *privs = NULL;
+
+ init_privilege(&privs);
+ if (!pdb_get_privilege_set((*server_info)->ptok->user_sids, (*server_info)->ptok->num_sids, privs))
+ DEBUG(1, ("Could not add privileges\n"));
+
+ (*server_info)->privs = privs;
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Make (and fill) a user_info struct from a SAM_ACCOUNT
+***************************************************************************/
+
+NTSTATUS make_server_info_sam(auth_serversupplied_info **server_info,
+ SAM_ACCOUNT *sampass)
+{
+ NTSTATUS nt_status;
+ struct passwd *pwd;
+
+ if (!NT_STATUS_IS_OK(nt_status = make_server_info(server_info)))
+ return nt_status;
+
+ (*server_info)->sam_account = sampass;
+
+ if ( !(pwd = getpwnam_alloc(pdb_get_username(sampass))) ) {
+ DEBUG(1, ("User %s in passdb, but getpwnam() fails!\n",
+ pdb_get_username(sampass)));
+ free_server_info(server_info);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+ (*server_info)->unix_name = smb_xstrdup(pwd->pw_name);
+ (*server_info)->gid = pwd->pw_gid;
+ (*server_info)->uid = pwd->pw_uid;
+
+ passwd_free(&pwd);
+
+ if (!NT_STATUS_IS_OK(nt_status = add_user_groups(server_info, pdb_get_username(sampass),
+ sampass,
+ (*server_info)->uid,
+ (*server_info)->gid)))
+ {
+ free_server_info(server_info);
+ return nt_status;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = add_privileges(server_info))) {
+ free_server_info(server_info);
+ return nt_status;
+ }
+
+ (*server_info)->sam_fill_level = SAM_FILL_ALL;
+ DEBUG(5,("make_server_info_sam: made server info for user %s -> %s\n",
+ pdb_get_username(sampass),
+ (*server_info)->unix_name));
+
+ return nt_status;
+}
+
+/***************************************************************************
+ Make (and fill) a user_info struct from a 'struct passwd' by conversion
+ to a SAM_ACCOUNT
+***************************************************************************/
+
+NTSTATUS make_server_info_pw(auth_serversupplied_info **server_info,
+ char *unix_username,
+ struct passwd *pwd)
+{
+ NTSTATUS nt_status;
+ SAM_ACCOUNT *sampass = NULL;
+ if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam_pw(&sampass, pwd))) {
+ return nt_status;
+ }
+ if (!NT_STATUS_IS_OK(nt_status = make_server_info(server_info))) {
+ return nt_status;
+ }
+
+ (*server_info)->sam_account = sampass;
+
+ if (!NT_STATUS_IS_OK(nt_status = add_user_groups(server_info, unix_username,
+ sampass, pwd->pw_uid, pwd->pw_gid)))
+ {
+ return nt_status;
+ }
+
+ (*server_info)->unix_name = smb_xstrdup(unix_username);
+
+ (*server_info)->sam_fill_level = SAM_FILL_ALL;
+ (*server_info)->uid = pwd->pw_uid;
+ (*server_info)->gid = pwd->pw_gid;
+ return nt_status;
+}
+
+/***************************************************************************
+ Make (and fill) a user_info struct for a guest login.
+***************************************************************************/
+
+NTSTATUS make_server_info_guest(auth_serversupplied_info **server_info)
+{
+ NTSTATUS nt_status;
+ SAM_ACCOUNT *sampass = NULL;
+ DOM_SID guest_sid;
+
+ if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam(&sampass))) {
+ return nt_status;
+ }
+
+ sid_copy(&guest_sid, get_global_sam_sid());
+ sid_append_rid(&guest_sid, DOMAIN_USER_RID_GUEST);
+
+ become_root();
+ if (!pdb_getsampwsid(sampass, &guest_sid)) {
+ unbecome_root();
+ return NT_STATUS_NO_SUCH_USER;
+ }
+ unbecome_root();
+
+ nt_status = make_server_info_sam(server_info, sampass);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ static const char zeros[16];
+ (*server_info)->guest = True;
+
+ /* annoying, but the Guest really does have a session key,
+ and it is all zeros! */
+ (*server_info)->nt_session_key = data_blob(zeros, sizeof(zeros));
+ (*server_info)->lm_session_key = data_blob(zeros, sizeof(zeros));
+ }
+
+ return nt_status;
+}
+
+/***************************************************************************
+ Purely internal function for make_server_info_info3
+ Fill the sam account from getpwnam
+***************************************************************************/
+static NTSTATUS fill_sam_account(TALLOC_CTX *mem_ctx,
+ const char *domain,
+ const char *username,
+ char **found_username,
+ uid_t *uid, gid_t *gid,
+ SAM_ACCOUNT **sam_account)
+{
+ fstring dom_user;
+ fstring real_username;
+ struct passwd *passwd;
+
+ fstr_sprintf(dom_user, "%s%s%s", domain, lp_winbind_separator(),
+ username);
+
+ /* get the passwd struct but don't create the user if he/she
+ does not exist. We were explicitly called from a following
+ a winbindd authentication request so we should assume that
+ nss_winbindd is working */
+
+ if ( !(passwd = smb_getpwnam( dom_user, real_username, True )) )
+ return NT_STATUS_NO_SUCH_USER;
+
+ *uid = passwd->pw_uid;
+ *gid = passwd->pw_gid;
+
+ /* This is pointless -- there is no suport for differing
+ unix and windows names. Make sure to always store the
+ one we actually looked up and succeeded. Have I mentioned
+ why I hate the 'winbind use default domain' parameter?
+ --jerry */
+
+ *found_username = talloc_strdup( mem_ctx, real_username );
+
+ DEBUG(5,("fill_sam_account: located username was [%s]\n",
+ *found_username));
+
+ return pdb_init_sam_pw(sam_account, passwd);
+}
+
+/****************************************************************************
+ 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( char *domuser, fstring save_username, BOOL create )
+{
+ struct passwd *pw = NULL;
+ char *p;
+ fstring mapped_username;
+ fstring strip_username;
+
+ /* we only save a copy of the username it has been mangled
+ by winbindd use default domain */
+
+ save_username[0] = '\0';
+
+ /* save a local copy of the username and run it through the
+ username map */
+
+ fstrcpy( mapped_username, domuser );
+ map_username( mapped_username );
+
+ p = strchr_m( mapped_username, *lp_winbind_separator() );
+
+ /* code for a DOMAIN\user string */
+
+ if ( p ) {
+ pw = Get_Pwnam( domuser );
+ if ( pw ) {
+ /* make sure we get the case of the username correct */
+ /* work around 'winbind use default domain = yes' */
+
+ if ( !strchr_m( pw->pw_name, *lp_winbind_separator() ) ) {
+ char *domain;
+
+ domain = mapped_username;
+ *p = '\0';
+ fstr_sprintf(save_username, "%s%c%s", domain, *lp_winbind_separator(), pw->pw_name);
+ }
+ else
+ fstrcpy( save_username, pw->pw_name );
+
+ /* whew -- done! */
+ return pw;
+ }
+
+ /* setup for lookup of just the username */
+ /* remember that p and mapped_username are overlapping memory */
+
+ p++;
+ fstrcpy( strip_username, p );
+ fstrcpy( mapped_username, strip_username );
+ }
+
+ /* just lookup a plain username */
+
+ pw = Get_Pwnam(mapped_username);
+
+ /* Create local user if requested. */
+
+ if ( !pw && create ) {
+ /* Don't add a machine account. */
+ if (mapped_username[strlen(mapped_username)-1] == '$')
+ return NULL;
+
+ auth_add_user_script(NULL, mapped_username);
+ pw = Get_Pwnam(mapped_username);
+ }
+
+ /* one last check for a valid passwd struct */
+
+ if ( pw )
+ fstrcpy( save_username, pw->pw_name );
+
+ return pw;
+}
+
+/***************************************************************************
+ Make a server_info struct from the info3 returned by a domain logon
+***************************************************************************/
+
+NTSTATUS make_server_info_info3(TALLOC_CTX *mem_ctx,
+ const char *internal_username,
+ const char *sent_nt_username,
+ const char *domain,
+ auth_serversupplied_info **server_info,
+ NET_USER_INFO_3 *info3)
+{
+ static const char zeros[16];
+
+ NTSTATUS nt_status = NT_STATUS_OK;
+ char *found_username;
+ const char *nt_domain;
+ const char *nt_username;
+
+ SAM_ACCOUNT *sam_account = NULL;
+ DOM_SID user_sid;
+ DOM_SID group_sid;
+
+ uid_t uid;
+ gid_t gid;
+
+ int n_lgroupSIDs;
+ DOM_SID *lgroupSIDs = NULL;
+
+ gid_t *unix_groups = NULL;
+ NT_USER_TOKEN *token;
+
+ DOM_SID *all_group_SIDs;
+ size_t i;
+
+ /*
+ Here is where we should check the list of
+ trusted domains, and verify that the SID
+ matches.
+ */
+
+ sid_copy(&user_sid, &info3->dom_sid.sid);
+ if (!sid_append_rid(&user_sid, info3->user_rid)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ sid_copy(&group_sid, &info3->dom_sid.sid);
+ if (!sid_append_rid(&group_sid, info3->group_rid)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!(nt_username = unistr2_tdup(mem_ctx, &(info3->uni_user_name)))) {
+ /* If the server didn't give us one, just use the one we sent them */
+ nt_username = sent_nt_username;
+ }
+
+ if (!(nt_domain = unistr2_tdup(mem_ctx, &(info3->uni_logon_dom)))) {
+ /* If the server didn't give us one, just use the one we sent them */
+ nt_domain = domain;
+ }
+
+ /* try to fill the SAM account.. If getpwnam() fails, then try the
+ add user script (2.2.x behavior) */
+
+ nt_status = fill_sam_account(mem_ctx, nt_domain, internal_username,
+ &found_username, &uid, &gid, &sam_account);
+
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER)) {
+ DEBUG(3,("User %s does not exist, trying to add it\n",
+ internal_username));
+ auth_add_user_script(nt_domain, internal_username);
+ nt_status = fill_sam_account(mem_ctx, nt_domain,
+ internal_username, &found_username,
+ &uid, &gid, &sam_account);
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("make_server_info_info3: pdb_init_sam failed!\n"));
+ return nt_status;
+ }
+
+ if (!pdb_set_nt_username(sam_account, nt_username, PDB_CHANGED)) {
+ pdb_free_sam(&sam_account);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!pdb_set_username(sam_account, nt_username, PDB_CHANGED)) {
+ pdb_free_sam(&sam_account);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!pdb_set_domain(sam_account, nt_domain, PDB_CHANGED)) {
+ pdb_free_sam(&sam_account);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!pdb_set_user_sid(sam_account, &user_sid, PDB_CHANGED)) {
+ pdb_free_sam(&sam_account);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!pdb_set_group_sid(sam_account, &group_sid, PDB_CHANGED)) {
+ pdb_free_sam(&sam_account);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!pdb_set_fullname(sam_account, unistr2_static(&(info3->uni_full_name)),
+ PDB_CHANGED)) {
+ pdb_free_sam(&sam_account);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!pdb_set_logon_script(sam_account, unistr2_static(&(info3->uni_logon_script)), PDB_CHANGED)) {
+ pdb_free_sam(&sam_account);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!pdb_set_profile_path(sam_account, unistr2_static(&(info3->uni_profile_path)), PDB_CHANGED)) {
+ pdb_free_sam(&sam_account);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!pdb_set_homedir(sam_account, unistr2_static(&(info3->uni_home_dir)), PDB_CHANGED)) {
+ pdb_free_sam(&sam_account);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!pdb_set_dir_drive(sam_account, unistr2_static(&(info3->uni_dir_drive)), PDB_CHANGED)) {
+ pdb_free_sam(&sam_account);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = make_server_info(server_info))) {
+ DEBUG(4, ("make_server_info failed!\n"));
+ pdb_free_sam(&sam_account);
+ return nt_status;
+ }
+
+ /* save this here to _net_sam_logon() doesn't fail (it assumes a
+ valid SAM_ACCOUNT) */
+
+ (*server_info)->sam_account = sam_account;
+
+ (*server_info)->unix_name = smb_xstrdup(found_username);
+
+ /* Fill in the unix info we found on the way */
+
+ (*server_info)->sam_fill_level = SAM_FILL_ALL;
+ (*server_info)->uid = uid;
+ (*server_info)->gid = gid;
+
+ /* Store the user group information in the server_info
+ returned to the caller. */
+
+ nt_status = get_user_groups((*server_info)->unix_name,
+ uid, gid, &n_lgroupSIDs, &lgroupSIDs, &unix_groups);
+
+ if ( !NT_STATUS_IS_OK(nt_status) ) {
+ DEBUG(4,("get_user_groups failed\n"));
+ return nt_status;
+ }
+
+ (*server_info)->groups = unix_groups;
+ (*server_info)->n_groups = n_lgroupSIDs;
+
+ /* Create a 'combined' list of all SIDs we might want in the SD */
+
+ all_group_SIDs = malloc(sizeof(DOM_SID) * (info3->num_groups2 +info3->num_other_sids));
+
+ if (!all_group_SIDs) {
+ DEBUG(0, ("malloc() failed for DOM_SID list!\n"));
+ SAFE_FREE(lgroupSIDs);
+ free_server_info(server_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+#if 0 /* JERRY -- no such thing as local groups in current code */
+ /* Copy the 'local' sids */
+ memcpy(all_group_SIDs, lgroupSIDs, sizeof(DOM_SID) * n_lgroupSIDs);
+ SAFE_FREE(lgroupSIDs);
+#endif
+
+ /* and create (by appending rids) the 'domain' sids */
+
+ for (i = 0; i < info3->num_groups2; i++) {
+
+ sid_copy(&all_group_SIDs[i], &(info3->dom_sid.sid));
+
+ if (!sid_append_rid(&all_group_SIDs[i], info3->gids[i].g_rid)) {
+
+ nt_status = NT_STATUS_INVALID_PARAMETER;
+
+ DEBUG(3,("could not append additional group rid 0x%x\n",
+ info3->gids[i].g_rid));
+
+ SAFE_FREE(lgroupSIDs);
+ free_server_info(server_info);
+
+ return nt_status;
+
+ }
+ }
+
+ /* Copy 'other' sids. We need to do sid filtering here to
+ prevent possible elevation of privileges. See:
+
+ http://www.microsoft.com/windows2000/techinfo/administration/security/sidfilter.asp
+ */
+
+ for (i = 0; i < info3->num_other_sids; i++) {
+ sid_copy(&all_group_SIDs[info3->num_groups2 + i],
+ &info3->other_sids[i].sid);
+ }
+
+ /* Where are the 'global' sids... */
+
+ /* can the user be guest? if yes, where is it stored? */
+
+ nt_status = create_nt_user_token(&user_sid, &group_sid,
+ info3->num_groups2 + info3->num_other_sids,
+ all_group_SIDs, False, &token);
+
+ if ( !NT_STATUS_IS_OK(nt_status) ) {
+ DEBUG(4,("create_nt_user_token failed\n"));
+ SAFE_FREE(all_group_SIDs);
+ free_server_info(server_info);
+ return nt_status;
+ }
+
+ (*server_info)->ptok = token;
+
+ SAFE_FREE(all_group_SIDs);
+
+ /* ensure we are never given NULL session keys */
+
+ if (memcmp(info3->user_sess_key, zeros, sizeof(zeros)) == 0) {
+ (*server_info)->nt_session_key = data_blob(NULL, 0);
+ } else {
+ (*server_info)->nt_session_key = data_blob(info3->user_sess_key, sizeof(info3->user_sess_key));
+ }
+
+ if (memcmp(info3->padding, zeros, sizeof(zeros)) == 0) {
+ (*server_info)->lm_session_key = data_blob(NULL, 0);
+ } else {
+ (*server_info)->lm_session_key = data_blob(info3->padding, 16);
+ }
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Free a user_info struct
+***************************************************************************/
+
+void free_user_info(auth_usersupplied_info **user_info)
+{
+ DEBUG(5,("attempting to free (and zero) a user_info structure\n"));
+ if (*user_info != NULL) {
+ if ((*user_info)->smb_name.str) {
+ DEBUG(10,("structure was created for %s\n", (*user_info)->smb_name.str));
+ }
+ SAFE_FREE((*user_info)->smb_name.str);
+ SAFE_FREE((*user_info)->internal_username.str);
+ SAFE_FREE((*user_info)->client_domain.str);
+ SAFE_FREE((*user_info)->domain.str);
+ SAFE_FREE((*user_info)->wksta_name.str);
+ data_blob_free(&(*user_info)->lm_resp);
+ data_blob_free(&(*user_info)->nt_resp);
+ SAFE_FREE((*user_info)->interactive_password);
+ data_blob_clear_free(&(*user_info)->plaintext_password);
+ ZERO_STRUCT(**user_info);
+ }
+ SAFE_FREE(*user_info);
+}
+
+/***************************************************************************
+ Clear out a server_info struct that has been allocated
+***************************************************************************/
+
+void free_server_info(auth_serversupplied_info **server_info)
+{
+ DEBUG(5,("attempting to free (and zero) a server_info structure\n"));
+ if (*server_info != NULL) {
+ pdb_free_sam(&(*server_info)->sam_account);
+
+ /* call pam_end here, unless we know we are keeping it */
+ delete_nt_token( &(*server_info)->ptok );
+ SAFE_FREE((*server_info)->groups);
+ SAFE_FREE((*server_info)->unix_name);
+ data_blob_free(&(*server_info)->lm_session_key);
+ data_blob_free(&(*server_info)->nt_session_key);
+ ZERO_STRUCT(**server_info);
+ }
+ SAFE_FREE(*server_info);
+}
+
+/***************************************************************************
+ Make an auth_methods struct
+***************************************************************************/
+
+BOOL make_auth_methods(struct auth_context *auth_context, auth_methods **auth_method)
+{
+ if (!auth_context) {
+ smb_panic("no auth_context supplied to make_auth_methods()!\n");
+ }
+
+ if (!auth_method) {
+ smb_panic("make_auth_methods: pointer to auth_method pointer is NULL!\n");
+ }
+
+ *auth_method = talloc(auth_context->mem_ctx, sizeof(**auth_method));
+ if (!*auth_method) {
+ DEBUG(0,("make_auth_method: malloc failed!\n"));
+ return False;
+ }
+ ZERO_STRUCTP(*auth_method);
+
+ return True;
+}
+
+/****************************************************************************
+ Delete a SID token.
+****************************************************************************/
+
+void delete_nt_token(NT_USER_TOKEN **pptoken)
+{
+ if (*pptoken) {
+ NT_USER_TOKEN *ptoken = *pptoken;
+ SAFE_FREE( ptoken->user_sids );
+ ZERO_STRUCTP(ptoken);
+ }
+ SAFE_FREE(*pptoken);
+}
+
+/****************************************************************************
+ Duplicate a SID token.
+****************************************************************************/
+
+NT_USER_TOKEN *dup_nt_token(NT_USER_TOKEN *ptoken)
+{
+ NT_USER_TOKEN *token;
+
+ if (!ptoken)
+ return NULL;
+
+ if ((token = (NT_USER_TOKEN *)malloc( sizeof(NT_USER_TOKEN) ) ) == NULL)
+ return NULL;
+
+ ZERO_STRUCTP(token);
+
+ if ((token->user_sids = (DOM_SID *)memdup( ptoken->user_sids, sizeof(DOM_SID) * ptoken->num_sids )) == NULL) {
+ SAFE_FREE(token);
+ return NULL;
+ }
+
+ token->num_sids = ptoken->num_sids;
+
+ return token;
+}
+
+/**
+ * Squash an NT_STATUS in line with security requirements.
+ * In an attempt to avoid giving the whole game away when users
+ * are authenticating, NT replaces both NT_STATUS_NO_SUCH_USER and
+ * NT_STATUS_WRONG_PASSWORD with NT_STATUS_LOGON_FAILURE in certain situations
+ * (session setups in particular).
+ *
+ * @param nt_status NTSTATUS input for squashing.
+ * @return the 'squashed' nt_status
+ **/
+
+NTSTATUS nt_status_squash(NTSTATUS nt_status)
+{
+ if NT_STATUS_IS_OK(nt_status) {
+ return nt_status;
+ } else if NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER) {
+ /* Match WinXP and don't give the game away */
+ return NT_STATUS_LOGON_FAILURE;
+
+ } else if NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD) {
+ /* Match WinXP and don't give the game away */
+ return NT_STATUS_LOGON_FAILURE;
+ } else {
+ return nt_status;
+ }
+}
+
+
+/**
+ * Verify whether or not given domain is trusted.
+ *
+ * @param domain_name name of the domain to be verified
+ * @return true if domain is one of the trusted once or
+ * false if otherwise
+ **/
+
+BOOL is_trusted_domain(const char* dom_name)
+{
+ DOM_SID trustdom_sid;
+ char *pass = NULL;
+ time_t lct;
+ BOOL ret;
+
+ /* no trusted domains for a standalone server */
+
+ if ( lp_server_role() == ROLE_STANDALONE )
+ return False;
+
+ /* if we are a DC, then check for a direct trust relationships */
+
+ if (lp_server_role() == ROLE_DOMAIN_BDC || lp_server_role() == ROLE_DOMAIN_PDC) {
+ become_root();
+ ret = secrets_fetch_trusted_domain_password(dom_name, &pass, &trustdom_sid, &lct);
+ unbecome_root();
+ SAFE_FREE(pass);
+ if (ret)
+ return True;
+ }
+ else {
+ /* if winbindd is not up and we are a domain member) then we need to update the
+ trustdom_cache ourselves */
+
+ if ( !winbind_ping() )
+ update_trustdom_cache();
+ }
+
+ /* now the trustdom cache should be available a DC could still
+ * have a transitive trust so fall back to the cache of trusted
+ * domains (like a domain member would use */
+
+ if ( trustdom_cache_fetch(dom_name, &trustdom_sid) ) {
+ return True;
+ }
+
+ return False;
+}
+
diff --git a/source/auth/auth_winbind.c b/source/auth/auth_winbind.c
new file mode 100644
index 00000000000..0e2820313e3
--- /dev/null
+++ b/source/auth/auth_winbind.c
@@ -0,0 +1,171 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind authentication mechnism
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Andrew Bartlett 2001 - 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+static NTSTATUS get_info3_from_ndr(TALLOC_CTX *mem_ctx, struct winbindd_response *response, NET_USER_INFO_3 *info3)
+{
+ uint8 *info3_ndr;
+ size_t len = response->length - sizeof(struct winbindd_response);
+ prs_struct ps;
+ if (len > 0) {
+ info3_ndr = response->extra_data;
+ if (!prs_init(&ps, len, mem_ctx, UNMARSHALL)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ prs_copy_data_in(&ps, (char *)info3_ndr, len);
+ prs_set_offset(&ps,0);
+ if (!net_io_user_info3("", info3, &ps, 1, 3)) {
+ DEBUG(2, ("get_info3_from_ndr: could not parse info3 struct!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ prs_mem_free(&ps);
+
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(2, ("get_info3_from_ndr: No info3 struct found!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+}
+
+/* Authenticate a user with a challenge/response */
+
+static NTSTATUS check_winbind_security(const struct auth_context *auth_context,
+ void *my_private_data,
+ TALLOC_CTX *mem_ctx,
+ const auth_usersupplied_info *user_info,
+ auth_serversupplied_info **server_info)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ NTSTATUS nt_status;
+ NET_USER_INFO_3 info3;
+
+ if (!user_info) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!auth_context) {
+ DEBUG(3,("Password for user %s cannot be checked because we have no auth_info to get the challenge from.\n",
+ user_info->internal_username.str));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (strequal(user_info->domain.str, get_global_sam_name())) {
+ DEBUG(3,("check_winbind_security: Not using winbind, requested domain [%s] was for this SAM.\n",
+ user_info->domain.str));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.flags = WBFLAG_PAM_INFO3_NDR;
+
+ push_utf8_fstring(request.data.auth_crap.user,
+ user_info->smb_name.str);
+ push_utf8_fstring(request.data.auth_crap.domain,
+ user_info->domain.str);
+ push_utf8_fstring(request.data.auth_crap.workstation,
+ user_info->wksta_name.str);
+
+ memcpy(request.data.auth_crap.chal, auth_context->challenge.data, sizeof(request.data.auth_crap.chal));
+
+ request.data.auth_crap.lm_resp_len = MIN(user_info->lm_resp.length,
+ sizeof(request.data.auth_crap.lm_resp));
+ request.data.auth_crap.nt_resp_len = MIN(user_info->nt_resp.length,
+ sizeof(request.data.auth_crap.nt_resp));
+
+ memcpy(request.data.auth_crap.lm_resp, user_info->lm_resp.data,
+ request.data.auth_crap.lm_resp_len);
+ memcpy(request.data.auth_crap.nt_resp, user_info->nt_resp.data,
+ request.data.auth_crap.nt_resp_len);
+
+ /* we are contacting the privileged pipe */
+ become_root();
+ result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response);
+ unbecome_root();
+
+ if ( result == NSS_STATUS_UNAVAIL ) {
+ struct auth_methods *auth_method = my_private_data;
+
+ if ( auth_method )
+ return auth_method->auth(auth_context, auth_method->private_data,
+ mem_ctx, user_info, server_info);
+ else
+ /* log an error since this should not happen */
+ DEBUG(0,("check_winbind_security: ERROR! my_private_data == NULL!\n"));
+ }
+
+ nt_status = NT_STATUS(response.data.auth.nt_status);
+
+ if (result == NSS_STATUS_SUCCESS && response.extra_data) {
+ if (NT_STATUS_IS_OK(nt_status)) {
+
+ if (NT_STATUS_IS_OK(nt_status = get_info3_from_ndr(mem_ctx, &response, &info3)))
+ {
+ nt_status = make_server_info_info3(mem_ctx,
+ user_info->internal_username.str,
+ user_info->smb_name.str, user_info->domain.str,
+ server_info, &info3);
+ }
+
+ }
+ } else if (NT_STATUS_IS_OK(nt_status)) {
+ nt_status = NT_STATUS_NO_LOGON_SERVERS;
+ }
+
+ return nt_status;
+}
+
+/* module initialisation */
+static NTSTATUS auth_init_winbind(struct auth_context *auth_context, const char *param, auth_methods **auth_method)
+{
+ if (!make_auth_methods(auth_context, auth_method)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*auth_method)->name = "winbind";
+ (*auth_method)->auth = check_winbind_security;
+
+ if (param && *param) {
+ /* we load the 'fallback' module - if winbind isn't here, call this
+ module */
+ if (!load_auth_module(auth_context, param, (auth_methods **)&(*auth_method)->private_data)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ }
+ return NT_STATUS_OK;
+}
+
+NTSTATUS auth_winbind_init(void)
+{
+ return smb_register_auth(AUTH_INTERFACE_VERSION, "winbind", auth_init_winbind);
+}
diff --git a/source/auth/pampass.c b/source/auth/pampass.c
new file mode 100644
index 00000000000..3239686a20d
--- /dev/null
+++ b/source/auth/pampass.c
@@ -0,0 +1,875 @@
+/*
+ Unix SMB/CIFS implementation.
+ PAM Password checking
+ Copyright (C) Andrew Tridgell 1992-2001
+ Copyright (C) John H Terpsta 1999-2001
+ Copyright (C) Andrew Bartlett 2001
+ Copyright (C) Jeremy Allison 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ * This module provides PAM based functions for validation of
+ * username/password pairs, account managment, session and access control.
+ * Note: SMB password checking is done in smbpass.c
+ */
+
+#include "includes.h"
+
+#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.
+ *********************************************************************/
+
+#include <security/pam_appl.h>
+
+/*
+ * Structure used to communicate between the conversation function
+ * and the server_login/change password functions.
+ */
+
+struct smb_pam_userdata {
+ const char *PAM_username;
+ const char *PAM_password;
+ const char *PAM_newpassword;
+};
+
+typedef int (*smb_pam_conv_fn)(int, const struct pam_message **, struct pam_response **, void *appdata_ptr);
+
+/*
+ * Macros to help make life easy
+ */
+#define COPY_STRING(s) (s) ? strdup(s) : NULL
+
+/*******************************************************************
+ PAM error handler.
+ *********************************************************************/
+
+static BOOL smb_pam_error_handler(pam_handle_t *pamh, int pam_error, const char *msg, int dbglvl)
+{
+
+ if( pam_error != PAM_SUCCESS) {
+ DEBUG(dbglvl, ("smb_pam_error_handler: PAM: %s : %s\n",
+ msg, pam_strerror(pamh, pam_error)));
+
+ return False;
+ }
+ return True;
+}
+
+/*******************************************************************
+ This function is a sanity check, to make sure that we NEVER report
+ failure as sucess.
+*********************************************************************/
+
+static BOOL smb_pam_nt_status_error_handler(pam_handle_t *pamh, int pam_error,
+ const char *msg, int dbglvl,
+ NTSTATUS *nt_status)
+{
+ *nt_status = pam_to_nt_status(pam_error);
+
+ if (smb_pam_error_handler(pamh, pam_error, msg, dbglvl))
+ return True;
+
+ if (NT_STATUS_IS_OK(*nt_status)) {
+ /* Complain LOUDLY */
+ DEBUG(0, ("smb_pam_nt_status_error_handler: PAM: BUG: PAM and NT_STATUS \
+error MISMATCH, forcing to NT_STATUS_LOGON_FAILURE"));
+ *nt_status = NT_STATUS_LOGON_FAILURE;
+ }
+ return False;
+}
+
+/*
+ * PAM conversation function
+ * Here we assume (for now, at least) that echo on means login name, and
+ * echo off means password.
+ */
+
+static int smb_pam_conv(int num_msg,
+ const struct pam_message **msg,
+ struct pam_response **resp,
+ void *appdata_ptr)
+{
+ int replies = 0;
+ struct pam_response *reply = NULL;
+ struct smb_pam_userdata *udp = (struct smb_pam_userdata *)appdata_ptr;
+
+ *resp = NULL;
+
+ if (num_msg <= 0)
+ return PAM_CONV_ERR;
+
+ /*
+ * Apparantly HPUX has a buggy PAM that doesn't support the
+ * appdata_ptr. Fail if this is the case. JRA.
+ */
+
+ if (udp == NULL) {
+ DEBUG(0,("smb_pam_conv: PAM on this system is broken - appdata_ptr == NULL !\n"));
+ return PAM_CONV_ERR;
+ }
+
+ reply = malloc(sizeof(struct pam_response) * num_msg);
+ if (!reply)
+ return PAM_CONV_ERR;
+
+ memset(reply, '\0', sizeof(struct pam_response) * num_msg);
+
+ for (replies = 0; replies < num_msg; replies++) {
+ switch (msg[replies]->msg_style) {
+ case PAM_PROMPT_ECHO_ON:
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies].resp = COPY_STRING(udp->PAM_username);
+ /* PAM frees resp */
+ break;
+
+ case PAM_PROMPT_ECHO_OFF:
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies].resp = COPY_STRING(udp->PAM_password);
+ /* PAM frees resp */
+ break;
+
+ case PAM_TEXT_INFO:
+ /* fall through */
+
+ case PAM_ERROR_MSG:
+ /* ignore it... */
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies].resp = NULL;
+ break;
+
+ default:
+ /* Must be an error of some sort... */
+ SAFE_FREE(reply);
+ return PAM_CONV_ERR;
+ }
+ }
+ if (reply)
+ *resp = reply;
+ return PAM_SUCCESS;
+}
+
+/*
+ * PAM password change conversation function
+ * Here we assume (for now, at least) that echo on means login name, and
+ * echo off means password.
+ */
+
+static void special_char_sub(char *buf)
+{
+ all_string_sub(buf, "\\n", "", 0);
+ all_string_sub(buf, "\\r", "", 0);
+ all_string_sub(buf, "\\s", " ", 0);
+ all_string_sub(buf, "\\t", "\t", 0);
+}
+
+static void pwd_sub(char *buf, const char *username, const char *oldpass, const char *newpass)
+{
+ fstring_sub(buf, "%u", username);
+ all_string_sub(buf, "%o", oldpass, sizeof(fstring));
+ all_string_sub(buf, "%n", newpass, sizeof(fstring));
+}
+
+
+struct chat_struct {
+ struct chat_struct *next, *prev;
+ fstring prompt;
+ fstring reply;
+};
+
+/**************************************************************
+ Create a linked list containing chat data.
+***************************************************************/
+
+static struct chat_struct *make_pw_chat(char *p)
+{
+ fstring prompt;
+ fstring reply;
+ struct chat_struct *list = NULL;
+ struct chat_struct *t;
+ struct chat_struct *tmp;
+
+ while (1) {
+ t = (struct chat_struct *)malloc(sizeof(*t));
+ if (!t) {
+ DEBUG(0,("make_pw_chat: malloc failed!\n"));
+ return NULL;
+ }
+
+ ZERO_STRUCTP(t);
+
+ DLIST_ADD_END(list, t, tmp);
+
+ if (!next_token(&p, prompt, NULL, sizeof(fstring)))
+ break;
+
+ if (strequal(prompt,"."))
+ fstrcpy(prompt,"*");
+
+ special_char_sub(prompt);
+ fstrcpy(t->prompt, prompt);
+ strlower_m(t->prompt);
+ trim_char(t->prompt, ' ', ' ');
+
+ if (!next_token(&p, reply, NULL, sizeof(fstring)))
+ break;
+
+ if (strequal(reply,"."))
+ fstrcpy(reply,"");
+
+ special_char_sub(reply);
+ fstrcpy(t->reply, reply);
+ strlower_m(t->reply);
+ trim_char(t->reply, ' ', ' ');
+
+ }
+ return list;
+}
+
+static void free_pw_chat(struct chat_struct *list)
+{
+ while (list) {
+ struct chat_struct *old_head = list;
+ DLIST_REMOVE(list, list);
+ SAFE_FREE(old_head);
+ }
+}
+
+static int smb_pam_passchange_conv(int num_msg,
+ const struct pam_message **msg,
+ struct pam_response **resp,
+ void *appdata_ptr)
+{
+ int replies = 0;
+ struct pam_response *reply = NULL;
+ fstring current_prompt;
+ fstring current_reply;
+ struct smb_pam_userdata *udp = (struct smb_pam_userdata *)appdata_ptr;
+ struct chat_struct *pw_chat= make_pw_chat(lp_passwd_chat());
+ struct chat_struct *t;
+ BOOL found;
+ *resp = NULL;
+
+ DEBUG(10,("smb_pam_passchange_conv: starting converstation for %d messages\n", num_msg));
+
+ if (num_msg <= 0)
+ return PAM_CONV_ERR;
+
+ if (pw_chat == NULL)
+ return PAM_CONV_ERR;
+
+ /*
+ * Apparantly HPUX has a buggy PAM that doesn't support the
+ * appdata_ptr. Fail if this is the case. JRA.
+ */
+
+ if (udp == NULL) {
+ DEBUG(0,("smb_pam_passchange_conv: PAM on this system is broken - appdata_ptr == NULL !\n"));
+ free_pw_chat(pw_chat);
+ return PAM_CONV_ERR;
+ }
+
+ reply = malloc(sizeof(struct pam_response) * num_msg);
+ if (!reply) {
+ DEBUG(0,("smb_pam_passchange_conv: malloc for reply failed!\n"));
+ free_pw_chat(pw_chat);
+ return PAM_CONV_ERR;
+ }
+
+ for (replies = 0; replies < num_msg; replies++) {
+ found = False;
+ DEBUG(10,("smb_pam_passchange_conv: Processing message %d\n", replies));
+ switch (msg[replies]->msg_style) {
+ case PAM_PROMPT_ECHO_ON:
+ DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_ON: PAM said: %s\n", msg[replies]->msg));
+ fstrcpy(current_prompt, msg[replies]->msg);
+ trim_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) == 0) {
+ fstrcpy(current_reply, t->reply);
+ DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_ON: We sent: %s\n", current_reply));
+ pwd_sub(current_reply, udp->PAM_username, udp->PAM_password, udp->PAM_newpassword);
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_ON: We actualy sent: %s\n", current_reply));
+#endif
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies].resp = COPY_STRING(current_reply);
+ found = True;
+ break;
+ }
+ }
+ /* PAM frees resp */
+ if (!found) {
+ DEBUG(3,("smb_pam_passchange_conv: Could not find reply for PAM prompt: %s\n",msg[replies]->msg));
+ free_pw_chat(pw_chat);
+ SAFE_FREE(reply);
+ return PAM_CONV_ERR;
+ }
+ break;
+
+ case PAM_PROMPT_ECHO_OFF:
+ DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_OFF: PAM said: %s\n", msg[replies]->msg));
+ fstrcpy(current_prompt, msg[replies]->msg);
+ trim_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) == 0) {
+ fstrcpy(current_reply, t->reply);
+ DEBUG(10,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_OFF: We sent: %s\n", current_reply));
+ pwd_sub(current_reply, udp->PAM_username, udp->PAM_password, udp->PAM_newpassword);
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies].resp = COPY_STRING(current_reply);
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("smb_pam_passchange_conv: PAM_PROMPT_ECHO_OFF: We actualy sent: %s\n", current_reply));
+#endif
+ found = True;
+ break;
+ }
+ }
+ /* PAM frees resp */
+
+ if (!found) {
+ DEBUG(3,("smb_pam_passchange_conv: Could not find reply for PAM prompt: %s\n",msg[replies]->msg));
+ free_pw_chat(pw_chat);
+ SAFE_FREE(reply);
+ return PAM_CONV_ERR;
+ }
+ break;
+
+ case PAM_TEXT_INFO:
+ /* fall through */
+
+ case PAM_ERROR_MSG:
+ /* ignore it... */
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies].resp = NULL;
+ break;
+
+ default:
+ /* Must be an error of some sort... */
+ free_pw_chat(pw_chat);
+ SAFE_FREE(reply);
+ return PAM_CONV_ERR;
+ }
+ }
+
+ free_pw_chat(pw_chat);
+ if (reply)
+ *resp = reply;
+ return PAM_SUCCESS;
+}
+
+/***************************************************************************
+ Free up a malloced pam_conv struct.
+****************************************************************************/
+
+static void smb_free_pam_conv(struct pam_conv *pconv)
+{
+ if (pconv)
+ SAFE_FREE(pconv->appdata_ptr);
+
+ SAFE_FREE(pconv);
+}
+
+/***************************************************************************
+ Allocate a pam_conv struct.
+****************************************************************************/
+
+static struct pam_conv *smb_setup_pam_conv(smb_pam_conv_fn smb_pam_conv_fnptr, const char *user,
+ const char *passwd, const char *newpass)
+{
+ struct pam_conv *pconv = (struct pam_conv *)malloc(sizeof(struct pam_conv));
+ struct smb_pam_userdata *udp = (struct smb_pam_userdata *)malloc(sizeof(struct smb_pam_userdata));
+
+ if (pconv == NULL || udp == NULL) {
+ SAFE_FREE(pconv);
+ SAFE_FREE(udp);
+ return NULL;
+ }
+
+ udp->PAM_username = user;
+ udp->PAM_password = passwd;
+ udp->PAM_newpassword = newpass;
+
+ pconv->conv = smb_pam_conv_fnptr;
+ pconv->appdata_ptr = (void *)udp;
+ return pconv;
+}
+
+/*
+ * PAM Closing out cleanup handler
+ */
+
+static BOOL smb_pam_end(pam_handle_t *pamh, struct pam_conv *smb_pam_conv_ptr)
+{
+ int pam_error;
+
+ smb_free_pam_conv(smb_pam_conv_ptr);
+
+ if( pamh != NULL ) {
+ pam_error = pam_end(pamh, 0);
+ if(smb_pam_error_handler(pamh, pam_error, "End Cleanup Failed", 2) == True) {
+ DEBUG(4, ("smb_pam_end: PAM: PAM_END OK.\n"));
+ return True;
+ }
+ }
+ DEBUG(2,("smb_pam_end: PAM: not initialised"));
+ return False;
+}
+
+/*
+ * Start PAM authentication for specified account
+ */
+
+static BOOL smb_pam_start(pam_handle_t **pamh, const char *user, const char *rhost, struct pam_conv *pconv)
+{
+ int pam_error;
+ const char *our_rhost;
+
+ *pamh = (pam_handle_t *)NULL;
+
+ DEBUG(4,("smb_pam_start: PAM: Init user: %s\n", user));
+
+ pam_error = pam_start("samba", user, pconv, pamh);
+ if( !smb_pam_error_handler(*pamh, pam_error, "Init Failed", 0)) {
+ *pamh = (pam_handle_t *)NULL;
+ return False;
+ }
+
+ if (rhost == NULL) {
+ our_rhost = client_name();
+ if (strequal(rhost,"UNKNOWN"))
+ our_rhost = client_addr();
+ } else {
+ our_rhost = rhost;
+ }
+
+#ifdef PAM_RHOST
+ DEBUG(4,("smb_pam_start: PAM: setting rhost to: %s\n", our_rhost));
+ pam_error = pam_set_item(*pamh, PAM_RHOST, our_rhost);
+ if(!smb_pam_error_handler(*pamh, pam_error, "set rhost failed", 0)) {
+ smb_pam_end(*pamh, pconv);
+ *pamh = (pam_handle_t *)NULL;
+ return False;
+ }
+#endif
+#ifdef PAM_TTY
+ DEBUG(4,("smb_pam_start: PAM: setting tty\n"));
+ pam_error = pam_set_item(*pamh, PAM_TTY, "samba");
+ if (!smb_pam_error_handler(*pamh, pam_error, "set tty failed", 0)) {
+ smb_pam_end(*pamh, pconv);
+ *pamh = (pam_handle_t *)NULL;
+ return False;
+ }
+#endif
+ DEBUG(4,("smb_pam_start: PAM: Init passed for user: %s\n", user));
+ return True;
+}
+
+/*
+ * PAM Authentication Handler
+ */
+static NTSTATUS smb_pam_auth(pam_handle_t *pamh, 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: Athentication Error for user %s\n", user));
+ break;
+ case PAM_CRED_INSUFFICIENT:
+ DEBUG(2, ("smb_pam_auth: PAM: Insufficient Credentials for user %s\n", user));
+ break;
+ case PAM_AUTHINFO_UNAVAIL:
+ DEBUG(2, ("smb_pam_auth: PAM: Authentication Information Unavailable for user %s\n", user));
+ break;
+ case PAM_USER_UNKNOWN:
+ DEBUG(2, ("smb_pam_auth: PAM: Username %s NOT known to Authentication system\n", user));
+ break;
+ case PAM_MAXTRIES:
+ DEBUG(2, ("smb_pam_auth: PAM: One or more authentication modules reports user limit for user %s exceeeded\n", user));
+ break;
+ case PAM_ABORT:
+ DEBUG(0, ("smb_pam_auth: PAM: One or more PAM modules failed to load for user %s\n", user));
+ break;
+ case PAM_SUCCESS:
+ DEBUG(4, ("smb_pam_auth: PAM: User %s Authenticated OK\n", user));
+ break;
+ default:
+ DEBUG(0, ("smb_pam_auth: PAM: UNKNOWN ERROR while authenticating user %s\n", user));
+ break;
+ }
+
+ smb_pam_nt_status_error_handler(pamh, pam_error, "Authentication Failure", 2, &nt_status);
+ return nt_status;
+}
+
+/*
+ * PAM Account Handler
+ */
+static NTSTATUS smb_pam_account(pam_handle_t *pamh, const char * user)
+{
+ int pam_error;
+ NTSTATUS nt_status = NT_STATUS_ACCOUNT_DISABLED;
+
+ DEBUG(4,("smb_pam_account: PAM: Account Management for User: %s\n", user));
+ pam_error = pam_acct_mgmt(pamh, PAM_SILENT); /* Is user account enabled? */
+ switch( pam_error ) {
+ case PAM_AUTHTOK_EXPIRED:
+ DEBUG(2, ("smb_pam_account: PAM: User %s is valid but password is expired\n", user));
+ break;
+ case PAM_ACCT_EXPIRED:
+ DEBUG(2, ("smb_pam_account: PAM: User %s no longer permitted to access system\n", user));
+ break;
+ case PAM_AUTH_ERR:
+ DEBUG(2, ("smb_pam_account: PAM: There was an authentication error for user %s\n", user));
+ break;
+ case PAM_PERM_DENIED:
+ DEBUG(0, ("smb_pam_account: PAM: User %s is NOT permitted to access system at this time\n", user));
+ break;
+ case PAM_USER_UNKNOWN:
+ DEBUG(0, ("smb_pam_account: PAM: User \"%s\" is NOT known to account management\n", user));
+ break;
+ case PAM_SUCCESS:
+ DEBUG(4, ("smb_pam_account: PAM: Account OK for User: %s\n", user));
+ break;
+ default:
+ DEBUG(0, ("smb_pam_account: PAM: UNKNOWN PAM ERROR (%d) during Account Management for User: %s\n", pam_error, user));
+ break;
+ }
+
+ smb_pam_nt_status_error_handler(pamh, pam_error, "Account Check Failed", 2, &nt_status);
+ return nt_status;
+}
+
+/*
+ * PAM Credential Setting
+ */
+
+static NTSTATUS smb_pam_setcred(pam_handle_t *pamh, const char * user)
+{
+ int pam_error;
+ NTSTATUS nt_status = NT_STATUS_NO_TOKEN;
+
+ /*
+ * This will allow samba to aquire a kerberos token. And, when
+ * exporting an AFS cell, be able to /write/ to this cell.
+ */
+
+ DEBUG(4,("PAM: Account Management SetCredentials for User: %s\n", user));
+ pam_error = pam_setcred(pamh, (PAM_ESTABLISH_CRED|PAM_SILENT));
+ switch( pam_error ) {
+ case PAM_CRED_UNAVAIL:
+ DEBUG(0, ("smb_pam_setcred: PAM: Credentials not found for user:%s\n", user ));
+ break;
+ case PAM_CRED_EXPIRED:
+ DEBUG(0, ("smb_pam_setcred: PAM: Credentials for user: \"%s\" EXPIRED!\n", user ));
+ break;
+ case PAM_USER_UNKNOWN:
+ DEBUG(0, ("smb_pam_setcred: PAM: User: \"%s\" is NOT known so can not set credentials!\n", user ));
+ break;
+ case PAM_CRED_ERR:
+ DEBUG(0, ("smb_pam_setcred: PAM: Unknown setcredentials error - unable to set credentials for %s\n", user ));
+ break;
+ case PAM_SUCCESS:
+ DEBUG(4, ("smb_pam_setcred: PAM: SetCredentials OK for User: %s\n", user));
+ break;
+ default:
+ DEBUG(0, ("smb_pam_setcred: PAM: UNKNOWN PAM ERROR (%d) during SetCredentials for User: %s\n", pam_error, user));
+ break;
+ }
+
+ smb_pam_nt_status_error_handler(pamh, pam_error, "Set Credential Failure", 2, &nt_status);
+ return nt_status;
+}
+
+/*
+ * PAM Internal Session Handler
+ */
+static BOOL smb_internal_pam_session(pam_handle_t *pamh, const char *user, const char *tty, BOOL flag)
+{
+ int pam_error;
+
+#ifdef PAM_TTY
+ DEBUG(4,("smb_internal_pam_session: PAM: tty set to: %s\n", tty));
+ pam_error = pam_set_item(pamh, PAM_TTY, tty);
+ if (!smb_pam_error_handler(pamh, pam_error, "set tty failed", 0))
+ return False;
+#endif
+
+ if (flag) {
+ pam_error = pam_open_session(pamh, PAM_SILENT);
+ if (!smb_pam_error_handler(pamh, pam_error, "session setup failed", 0))
+ return False;
+ } else {
+ pam_setcred(pamh, (PAM_DELETE_CRED|PAM_SILENT)); /* We don't care if this fails */
+ pam_error = pam_close_session(pamh, PAM_SILENT); /* This will probably pick up the error anyway */
+ if (!smb_pam_error_handler(pamh, pam_error, "session close failed", 0))
+ return False;
+ }
+ return (True);
+}
+
+/*
+ * Internal PAM Password Changer.
+ */
+
+static BOOL smb_pam_chauthtok(pam_handle_t *pamh, const char * user)
+{
+ int pam_error;
+
+ DEBUG(4,("smb_pam_chauthtok: PAM: Password Change for User: %s\n", user));
+
+ pam_error = pam_chauthtok(pamh, PAM_SILENT); /* Change Password */
+
+ switch( pam_error ) {
+ case PAM_AUTHTOK_ERR:
+ DEBUG(2, ("PAM: unable to obtain the new authentication token - is password to weak?\n"));
+ break;
+
+ /* This doesn't seem to be defined on Solaris. JRA */
+#ifdef PAM_AUTHTOK_RECOVER_ERR
+ case PAM_AUTHTOK_RECOVER_ERR:
+ DEBUG(2, ("PAM: unable to obtain the old authentication token - was the old password wrong?.\n"));
+ break;
+#endif
+
+ case PAM_AUTHTOK_LOCK_BUSY:
+ DEBUG(2, ("PAM: unable to change the authentication token since it is currently locked.\n"));
+ break;
+ case PAM_AUTHTOK_DISABLE_AGING:
+ DEBUG(2, ("PAM: Authentication token aging has been disabled.\n"));
+ break;
+ case PAM_PERM_DENIED:
+ DEBUG(0, ("PAM: Permission denied.\n"));
+ break;
+ case PAM_TRY_AGAIN:
+ DEBUG(0, ("PAM: Could not update all authentication token(s). No authentication tokens were updated.\n"));
+ break;
+ case PAM_USER_UNKNOWN:
+ DEBUG(0, ("PAM: User not known to PAM\n"));
+ break;
+ case PAM_SUCCESS:
+ DEBUG(4, ("PAM: Account OK for User: %s\n", user));
+ break;
+ default:
+ DEBUG(0, ("PAM: UNKNOWN PAM ERROR (%d) for User: %s\n", pam_error, user));
+ }
+
+ if(!smb_pam_error_handler(pamh, pam_error, "Password Change Failed", 2)) {
+ return False;
+ }
+
+ /* If this point is reached, the password has changed. */
+ return True;
+}
+
+/*
+ * PAM Externally accessible Session handler
+ */
+
+BOOL smb_pam_claim_session(char *user, char *tty, char *rhost)
+{
+ pam_handle_t *pamh = NULL;
+ struct pam_conv *pconv = NULL;
+
+ /* Ignore PAM if told to. */
+
+ if (!lp_obey_pam_restrictions())
+ return True;
+
+ if ((pconv = smb_setup_pam_conv(smb_pam_conv, user, NULL, NULL)) == NULL)
+ return False;
+
+ if (!smb_pam_start(&pamh, user, rhost, pconv))
+ return False;
+
+ if (!smb_internal_pam_session(pamh, user, tty, True)) {
+ smb_pam_end(pamh, pconv);
+ return False;
+ }
+
+ return smb_pam_end(pamh, pconv);
+}
+
+/*
+ * PAM Externally accessible Session handler
+ */
+
+BOOL smb_pam_close_session(char *user, char *tty, char *rhost)
+{
+ pam_handle_t *pamh = NULL;
+ struct pam_conv *pconv = NULL;
+
+ /* Ignore PAM if told to. */
+
+ if (!lp_obey_pam_restrictions())
+ return True;
+
+ if ((pconv = smb_setup_pam_conv(smb_pam_conv, user, NULL, NULL)) == NULL)
+ return False;
+
+ if (!smb_pam_start(&pamh, user, rhost, pconv))
+ return False;
+
+ if (!smb_internal_pam_session(pamh, user, tty, False)) {
+ smb_pam_end(pamh, pconv);
+ return False;
+ }
+
+ return smb_pam_end(pamh, pconv);
+}
+
+/*
+ * PAM Externally accessible Account handler
+ */
+
+NTSTATUS smb_pam_accountcheck(const char * user)
+{
+ NTSTATUS nt_status = NT_STATUS_ACCOUNT_DISABLED;
+ pam_handle_t *pamh = NULL;
+ struct pam_conv *pconv = NULL;
+
+ /* Ignore PAM if told to. */
+
+ if (!lp_obey_pam_restrictions())
+ return NT_STATUS_OK;
+
+ if ((pconv = smb_setup_pam_conv(smb_pam_conv, user, NULL, NULL)) == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ if (!smb_pam_start(&pamh, user, NULL, pconv))
+ return NT_STATUS_ACCOUNT_DISABLED;
+
+ if (!NT_STATUS_IS_OK(nt_status = smb_pam_account(pamh, user)))
+ DEBUG(0, ("smb_pam_accountcheck: PAM: Account Validation Failed - Rejecting User %s!\n", user));
+
+ smb_pam_end(pamh, pconv);
+ return nt_status;
+}
+
+/*
+ * PAM Password Validation Suite
+ */
+
+NTSTATUS smb_pam_passcheck(const char * user, const char * password)
+{
+ pam_handle_t *pamh = NULL;
+ NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
+ struct pam_conv *pconv = NULL;
+
+ /*
+ * Note we can't ignore PAM here as this is the only
+ * way of doing auths on plaintext passwords when
+ * compiled --with-pam.
+ */
+
+ if ((pconv = smb_setup_pam_conv(smb_pam_conv, user, password, NULL)) == NULL)
+ return NT_STATUS_LOGON_FAILURE;
+
+ if (!smb_pam_start(&pamh, user, NULL, pconv))
+ return NT_STATUS_LOGON_FAILURE;
+
+ if (!NT_STATUS_IS_OK(nt_status = smb_pam_auth(pamh, user))) {
+ DEBUG(0, ("smb_pam_passcheck: PAM: smb_pam_auth failed - Rejecting User %s !\n", user));
+ smb_pam_end(pamh, pconv);
+ return nt_status;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = smb_pam_account(pamh, user))) {
+ DEBUG(0, ("smb_pam_passcheck: PAM: smb_pam_account failed - Rejecting User %s !\n", user));
+ smb_pam_end(pamh, pconv);
+ return nt_status;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = smb_pam_setcred(pamh, user))) {
+ DEBUG(0, ("smb_pam_passcheck: PAM: smb_pam_setcred failed - Rejecting User %s !\n", user));
+ smb_pam_end(pamh, pconv);
+ return nt_status;
+ }
+
+ smb_pam_end(pamh, pconv);
+ return nt_status;
+}
+
+/*
+ * PAM Password Change Suite
+ */
+
+BOOL smb_pam_passchange(const char * user, const char * oldpassword, const char * newpassword)
+{
+ /* Appropriate quantities of root should be obtained BEFORE calling this function */
+ struct pam_conv *pconv = NULL;
+ pam_handle_t *pamh = NULL;
+
+ if ((pconv = smb_setup_pam_conv(smb_pam_passchange_conv, user, oldpassword, newpassword)) == NULL)
+ return False;
+
+ if(!smb_pam_start(&pamh, user, NULL, pconv))
+ return False;
+
+ if (!smb_pam_chauthtok(pamh, user)) {
+ DEBUG(0, ("smb_pam_passchange: PAM: Password Change Failed for user %s!\n", user));
+ smb_pam_end(pamh, pconv);
+ return False;
+ }
+
+ return smb_pam_end(pamh, pconv);
+}
+
+#else
+
+/* If PAM not used, no PAM restrictions on accounts. */
+NTSTATUS smb_pam_accountcheck(const char * user)
+{
+ return NT_STATUS_OK;
+}
+
+/* If PAM not used, also no PAM restrictions on sessions. */
+BOOL smb_pam_claim_session(char *user, char *tty, char *rhost)
+{
+ return True;
+}
+
+/* If PAM not used, also no PAM restrictions on sessions. */
+BOOL smb_pam_close_session(char *in_user, char *tty, char *rhost)
+{
+ return True;
+}
+#endif /* WITH_PAM */
diff --git a/source/auth/pass_check.c b/source/auth/pass_check.c
new file mode 100644
index 00000000000..1ac8c1815a6
--- /dev/null
+++ b/source/auth/pass_check.c
@@ -0,0 +1,783 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password checking
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* this module is for checking a username/password against a system
+ password database. The SMB encrypted password support is elsewhere */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+/* these are kept here to keep the string_combinations function simple */
+static fstring this_user;
+#if !defined(WITH_PAM)
+static fstring this_salt;
+static fstring this_crypted;
+#endif
+
+#ifdef WITH_AFS
+
+#include <afs/stds.h>
+#include <afs/kautils.h>
+
+/*******************************************************************
+check on AFS authentication
+********************************************************************/
+static BOOL afs_auth(char *user, char *password)
+{
+ long password_expires = 0;
+ char *reason;
+
+ /* For versions of AFS prior to 3.3, this routine has few arguments, */
+ /* but since I can't find the old documentation... :-) */
+ setpag();
+ if (ka_UserAuthenticateGeneral
+ (KA_USERAUTH_VERSION + KA_USERAUTH_DOSETPAG, user, (char *)0, /* instance */
+ (char *)0, /* cell */
+ password, 0, /* lifetime, default */
+ &password_expires, /*days 'til it expires */
+ 0, /* spare 2 */
+ &reason) == 0)
+ {
+ return (True);
+ }
+ DEBUG(1,
+ ("AFS authentication for \"%s\" failed (%s)\n", user, reason));
+ return (False);
+}
+#endif
+
+
+#ifdef WITH_DFS
+
+#include <dce/dce_error.h>
+#include <dce/sec_login.h>
+
+/*****************************************************************
+ This new version of the DFS_AUTH code was donated by Karsten Muuss
+ <muuss@or.uni-bonn.de>. It fixes the following problems with the
+ old code :
+
+ - Server credentials may expire
+ - Client credential cache files have wrong owner
+ - purge_context() function is called with invalid argument
+
+ This new code was modified to ensure that on exit the uid/gid is
+ still root, and the original directory is restored. JRA.
+******************************************************************/
+
+sec_login_handle_t my_dce_sec_context;
+int dcelogin_atmost_once = 0;
+
+/*******************************************************************
+check on a DCE/DFS authentication
+********************************************************************/
+static BOOL dfs_auth(char *user, char *password)
+{
+ error_status_t err;
+ int err2;
+ int prterr;
+ signed32 expire_time, current_time;
+ boolean32 password_reset;
+ struct passwd *pw;
+ sec_passwd_rec_t passwd_rec;
+ sec_login_auth_src_t auth_src = sec_login_auth_src_network;
+ unsigned char dce_errstr[dce_c_error_string_len];
+ gid_t egid;
+
+ if (dcelogin_atmost_once)
+ return (False);
+
+#ifdef HAVE_CRYPT
+ /*
+ * We only go for a DCE login context if the given password
+ * matches that stored in the local password file..
+ * Assumes local passwd file is kept in sync w/ DCE RGY!
+ */
+
+ if (strcmp((char *)crypt(password, this_salt), this_crypted))
+ {
+ return (False);
+ }
+#endif
+
+ sec_login_get_current_context(&my_dce_sec_context, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
+
+ return (False);
+ }
+
+ sec_login_certify_identity(my_dce_sec_context, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't get current context. %s\n", dce_errstr));
+
+ return (False);
+ }
+
+ sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
+
+ return (False);
+ }
+
+ time(&current_time);
+
+ if (expire_time < (current_time + 60))
+ {
+ struct passwd *pw;
+ sec_passwd_rec_t *key;
+
+ sec_login_get_pwent(my_dce_sec_context,
+ (sec_login_passwd_t *) & pw, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
+
+ return (False);
+ }
+
+ sec_login_refresh_identity(my_dce_sec_context, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't refresh identity. %s\n",
+ dce_errstr));
+
+ return (False);
+ }
+
+ sec_key_mgmt_get_key(rpc_c_authn_dce_secret, NULL,
+ (unsigned char *)pw->pw_name,
+ sec_c_key_version_none,
+ (void **)&key, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't get key for %s. %s\n",
+ pw->pw_name, dce_errstr));
+
+ return (False);
+ }
+
+ sec_login_valid_and_cert_ident(my_dce_sec_context, key,
+ &password_reset, &auth_src,
+ &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0,
+ ("DCE can't validate and certify identity for %s. %s\n",
+ pw->pw_name, dce_errstr));
+ }
+
+ sec_key_mgmt_free_key(key, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't free key.\n", dce_errstr));
+ }
+ }
+
+ if (sec_login_setup_identity((unsigned char *)user,
+ sec_login_no_flags,
+ &my_dce_sec_context, &err) == 0)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
+ user, dce_errstr));
+ return (False);
+ }
+
+ sec_login_get_pwent(my_dce_sec_context,
+ (sec_login_passwd_t *) & pw, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
+
+ return (False);
+ }
+
+ sec_login_purge_context(&my_dce_sec_context, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't purge context. %s\n", dce_errstr));
+
+ return (False);
+ }
+
+ /*
+ * NB. I'd like to change these to call something like change_to_user()
+ * instead but currently we don't have a connection
+ * context to become the correct user. This is already
+ * fairly platform specific code however, so I think
+ * this should be ok. I have added code to go
+ * back to being root on error though. JRA.
+ */
+
+ egid = getegid();
+
+ set_effective_gid(pw->pw_gid);
+ set_effective_uid(pw->pw_uid);
+
+ if (sec_login_setup_identity((unsigned char *)user,
+ sec_login_no_flags,
+ &my_dce_sec_context, &err) == 0)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE Setup Identity for %s failed: %s\n",
+ user, dce_errstr));
+ goto err;
+ }
+
+ sec_login_get_pwent(my_dce_sec_context,
+ (sec_login_passwd_t *) & pw, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
+ goto err;
+ }
+
+ passwd_rec.version_number = sec_passwd_c_version_none;
+ passwd_rec.pepper = NULL;
+ passwd_rec.key.key_type = sec_passwd_plain;
+ passwd_rec.key.tagged_union.plain = (idl_char *) password;
+
+ sec_login_validate_identity(my_dce_sec_context,
+ &passwd_rec, &password_reset,
+ &auth_src, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0,
+ ("DCE Identity Validation failed for principal %s: %s\n",
+ user, dce_errstr));
+ goto err;
+ }
+
+ sec_login_certify_identity(my_dce_sec_context, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE certify identity failed: %s\n", dce_errstr));
+ goto err;
+ }
+
+ if (auth_src != sec_login_auth_src_network)
+ {
+ DEBUG(0, ("DCE context has no network credentials.\n"));
+ }
+
+ sec_login_set_context(my_dce_sec_context, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0,
+ ("DCE login failed for principal %s, cant set context: %s\n",
+ user, dce_errstr));
+
+ sec_login_purge_context(&my_dce_sec_context, &err);
+ goto err;
+ }
+
+ sec_login_get_pwent(my_dce_sec_context,
+ (sec_login_passwd_t *) & pw, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't get pwent. %s\n", dce_errstr));
+ goto err;
+ }
+
+ DEBUG(0, ("DCE login succeeded for principal %s on pid %d\n",
+ user, sys_getpid()));
+
+ DEBUG(3, ("DCE principal: %s\n"
+ " uid: %d\n"
+ " gid: %d\n",
+ pw->pw_name, pw->pw_uid, pw->pw_gid));
+ DEBUG(3, (" info: %s\n"
+ " dir: %s\n"
+ " shell: %s\n",
+ pw->pw_gecos, pw->pw_dir, pw->pw_shell));
+
+ sec_login_get_expiration(my_dce_sec_context, &expire_time, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0, ("DCE can't get expiration. %s\n", dce_errstr));
+ goto err;
+ }
+
+ set_effective_uid(0);
+ set_effective_gid(0);
+
+ DEBUG(0,
+ ("DCE context expires: %s", asctime(localtime(&expire_time))));
+
+ dcelogin_atmost_once = 1;
+ return (True);
+
+ err:
+
+ /* Go back to root, JRA. */
+ set_effective_uid(0);
+ set_effective_gid(egid);
+ return (False);
+}
+
+void dfs_unlogin(void)
+{
+ error_status_t err;
+ int err2;
+ unsigned char dce_errstr[dce_c_error_string_len];
+
+ sec_login_purge_context(&my_dce_sec_context, &err);
+ if (err != error_status_ok)
+ {
+ dce_error_inq_text(err, dce_errstr, &err2);
+ DEBUG(0,
+ ("DCE purge login context failed for server instance %d: %s\n",
+ sys_getpid(), dce_errstr));
+ }
+}
+#endif
+
+#ifdef LINUX_BIGCRYPT
+/****************************************************************************
+an enhanced crypt for Linux to handle password longer than 8 characters
+****************************************************************************/
+static int linux_bigcrypt(char *password, char *salt1, char *crypted)
+{
+#define LINUX_PASSWORD_SEG_CHARS 8
+ char salt[3];
+ int i;
+
+ StrnCpy(salt, salt1, 2);
+ crypted += 2;
+
+ for (i = strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) {
+ char *p = crypt(password, salt) + 2;
+ if (strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0)
+ return (0);
+ password += LINUX_PASSWORD_SEG_CHARS;
+ crypted += strlen(p);
+ }
+
+ return (1);
+}
+#endif
+
+#ifdef OSF1_ENH_SEC
+/****************************************************************************
+an enhanced crypt for OSF1
+****************************************************************************/
+static char *osf1_bigcrypt(char *password, char *salt1)
+{
+ static char result[AUTH_MAX_PASSWD_LENGTH] = "";
+ char *p1;
+ char *p2 = password;
+ char salt[3];
+ int i;
+ int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS;
+ if (strlen(password) % AUTH_CLEARTEXT_SEG_CHARS)
+ parts++;
+
+ StrnCpy(salt, salt1, 2);
+ StrnCpy(result, salt1, 2);
+ result[2] = '\0';
+
+ for (i = 0; i < parts; i++) {
+ p1 = crypt(p2, salt);
+ strncat(result, p1 + 2,
+ AUTH_MAX_PASSWD_LENGTH - strlen(p1 + 2) - 1);
+ StrnCpy(salt, &result[2 + i * AUTH_CIPHERTEXT_SEG_CHARS], 2);
+ p2 += AUTH_CLEARTEXT_SEG_CHARS;
+ }
+
+ return (result);
+}
+#endif
+
+
+/****************************************************************************
+apply a function to upper/lower case combinations
+of a string and return true if one of them returns true.
+try all combinations with N uppercase letters.
+offset is the first char to try and change (start with 0)
+it assumes the string starts lowercased
+****************************************************************************/
+static NTSTATUS string_combinations2(char *s, int offset, NTSTATUS (*fn) (const char *),
+ int N)
+{
+ int len = strlen(s);
+ int i;
+ NTSTATUS nt_status;
+
+#ifdef PASSWORD_LENGTH
+ len = MIN(len, PASSWORD_LENGTH);
+#endif
+
+ if (N <= 0 || offset >= len)
+ return (fn(s));
+
+ for (i = offset; i < (len - (N - 1)); i++) {
+ char c = s[i];
+ if (!islower(c))
+ continue;
+ s[i] = toupper(c);
+ if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, i + 1, fn, N - 1),NT_STATUS_WRONG_PASSWORD)) {
+ return (nt_status);
+ }
+ s[i] = c;
+ }
+ return (NT_STATUS_WRONG_PASSWORD);
+}
+
+/****************************************************************************
+apply a function to upper/lower case combinations
+of a string and return true if one of them returns true.
+try all combinations with up to N uppercase letters.
+offset is the first char to try and change (start with 0)
+it assumes the string starts lowercased
+****************************************************************************/
+static NTSTATUS string_combinations(char *s, NTSTATUS (*fn) (const char *), int N)
+{
+ int n;
+ NTSTATUS nt_status;
+ for (n = 1; n <= N; n++)
+ if (!NT_STATUS_EQUAL(nt_status = string_combinations2(s, 0, fn, n), NT_STATUS_WRONG_PASSWORD))
+ return nt_status;
+ return NT_STATUS_WRONG_PASSWORD;
+}
+
+
+/****************************************************************************
+core of password checking routine
+****************************************************************************/
+static NTSTATUS password_check(const char *password)
+{
+#ifdef WITH_PAM
+ return smb_pam_passcheck(this_user, password);
+#else
+
+ BOOL ret;
+
+#ifdef WITH_AFS
+ if (afs_auth(this_user, password))
+ return NT_STATUS_OK;
+#endif /* WITH_AFS */
+
+#ifdef WITH_DFS
+ if (dfs_auth(this_user, password))
+ return NT_STATUS_OK;
+#endif /* WITH_DFS */
+
+#ifdef OSF1_ENH_SEC
+
+ ret = (strcmp(osf1_bigcrypt(password, this_salt),
+ this_crypted) == 0);
+ if (!ret) {
+ DEBUG(2,
+ ("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
+ ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
+ }
+ if (ret) {
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+#endif /* OSF1_ENH_SEC */
+
+#ifdef ULTRIX_AUTH
+ ret = (strcmp((char *)crypt16(password, this_salt), this_crypted) == 0);
+ if (ret) {
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+#endif /* ULTRIX_AUTH */
+
+#ifdef LINUX_BIGCRYPT
+ ret = (linux_bigcrypt(password, this_salt, this_crypted));
+ if (ret) {
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+#endif /* LINUX_BIGCRYPT */
+
+#if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS)
+
+ /*
+ * Some systems have bigcrypt in the C library but might not
+ * actually use it for the password hashes (HPUX 10.20) is
+ * a noteable example. So we try bigcrypt first, followed
+ * by crypt.
+ */
+
+ if (strcmp(bigcrypt(password, this_salt), this_crypted) == 0)
+ return NT_STATUS_OK;
+ else
+ ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
+ if (ret) {
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+#else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
+
+#ifdef HAVE_BIGCRYPT
+ ret = (strcmp(bigcrypt(password, this_salt), this_crypted) == 0);
+ if (ret) {
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+#endif /* HAVE_BIGCRYPT */
+
+#ifndef HAVE_CRYPT
+ DEBUG(1, ("Warning - no crypt available\n"));
+ return NT_STATUS_LOGON_FAILURE;
+#else /* HAVE_CRYPT */
+ ret = (strcmp((char *)crypt(password, this_salt), this_crypted) == 0);
+ if (ret) {
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+#endif /* HAVE_CRYPT */
+#endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
+#endif /* WITH_PAM */
+}
+
+
+
+/****************************************************************************
+CHECK if a username/password is OK
+the function pointer fn() points to a function to call when a successful
+match is found and is used to update the encrypted password file
+return NT_STATUS_OK on correct match, appropriate error otherwise
+****************************************************************************/
+
+NTSTATUS pass_check(const struct passwd *pass, const char *user, const char *password,
+ int pwlen, BOOL (*fn) (const char *, const char *), BOOL run_cracker)
+{
+ pstring pass2;
+ int level = lp_passwordlevel();
+
+ NTSTATUS nt_status;
+
+#if DEBUG_PASSWORD
+ DEBUG(100, ("checking user=[%s] pass=[%s]\n", user, password));
+#endif
+
+ if (!password)
+ return NT_STATUS_LOGON_FAILURE;
+
+ if (((!*password) || (!pwlen)) && !lp_null_passwords())
+ return NT_STATUS_LOGON_FAILURE;
+
+#if defined(WITH_PAM)
+
+ /*
+ * If we're using PAM we want to short-circuit all the
+ * checks below and dive straight into the PAM code.
+ */
+
+ fstrcpy(this_user, user);
+
+ DEBUG(4, ("pass_check: Checking (PAM) password for user %s (l=%d)\n", user, pwlen));
+
+#else /* Not using PAM */
+
+ DEBUG(4, ("pass_check: Checking password for user %s (l=%d)\n", user, pwlen));
+
+ if (!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... */
+ fstrcpy(this_crypted, pass->pw_passwd);
+ fstrcpy(this_salt, pass->pw_passwd);
+
+#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) {
+ fstrcpy(this_crypted, spass->sp_pwdp);
+ fstrcpy(this_salt, spass->sp_pwdp);
+ }
+ }
+#elif defined(IA_UINFO)
+ {
+ /* Need to get password with SVR4.2's ia_ functions
+ instead of get{sp,pw}ent functions. Required by
+ UnixWare 2.x, tested on version
+ 2.1. (tangent@cyberport.com) */
+ uinfo_t uinfo;
+ if (ia_openinfo(pass->pw_name, &uinfo) != -1)
+ ia_get_logpwd(uinfo, &(pass->pw_passwd));
+ }
+#endif
+
+#ifdef HAVE_GETPRPWNAM
+ {
+ struct pr_passwd *pr_pw = getprpwnam(pass->pw_name);
+ if (pr_pw && pr_pw->ufld.fd_encrypt)
+ fstrcpy(this_crypted, pr_pw->ufld.fd_encrypt);
+ }
+#endif
+
+#ifdef HAVE_GETPWANAM
+ {
+ struct passwd_adjunct *pwret;
+ pwret = getpwanam(s);
+ if (pwret && pwret->pwa_passwd)
+ fstrcpy(this_crypted, pwret->pwa_passwd);
+ }
+#endif
+
+#ifdef OSF1_ENH_SEC
+ {
+ struct pr_passwd *mypasswd;
+ DEBUG(5, ("Checking password for user %s in OSF1_ENH_SEC\n",
+ user));
+ mypasswd = getprpwnam(user);
+ if (mypasswd) {
+ fstrcpy(this_user, mypasswd->ufld.fd_name);
+ fstrcpy(this_crypted, mypasswd->ufld.fd_encrypt);
+ } else {
+ DEBUG(5,
+ ("OSF1_ENH_SEC: No entry for user %s in protected database !\n",
+ user));
+ }
+ }
+#endif
+
+#ifdef ULTRIX_AUTH
+ {
+ AUTHORIZATION *ap = getauthuid(pass->pw_uid);
+ if (ap) {
+ fstrcpy(this_crypted, ap->a_password);
+ endauthent();
+ }
+ }
+#endif
+
+#if defined(HAVE_TRUNCATED_SALT)
+ /* crypt on some platforms (HPUX in particular)
+ won't work with more than 2 salt characters. */
+ this_salt[2] = 0;
+#endif
+
+ if (!*this_crypted) {
+ if (!lp_null_passwords()) {
+ DEBUG(2, ("Disallowing %s with null password\n",
+ this_user));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+ if (!*password) {
+ DEBUG(3,
+ ("Allowing access to %s with null password\n",
+ this_user));
+ return NT_STATUS_OK;
+ }
+ }
+
+#endif /* defined(WITH_PAM) */
+
+ /* try it as it came to us */
+ nt_status = password_check(password);
+ if NT_STATUS_IS_OK(nt_status) {
+ if (fn) {
+ fn(user, password);
+ }
+ return (nt_status);
+ } else if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) {
+ /* No point continuing if its not the password thats to blame (ie PAM disabled). */
+ return (nt_status);
+ }
+
+ if (!run_cracker) {
+ return (nt_status);
+ }
+
+ /* if the password was given to us with mixed case then we don't
+ * need to proceed as we know it hasn't been case modified by the
+ * client */
+ if (strhasupper(password) && strhaslower(password)) {
+ return nt_status;
+ }
+
+ /* make a copy of it */
+ pstrcpy(pass2, password);
+
+ /* try all lowercase if it's currently all uppercase */
+ if (strhasupper(pass2)) {
+ strlower_m(pass2);
+ if NT_STATUS_IS_OK(nt_status = password_check(pass2)) {
+ if (fn)
+ fn(user, pass2);
+ return (nt_status);
+ }
+ }
+
+ /* give up? */
+ if (level < 1) {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* last chance - all combinations of up to level chars upper! */
+ strlower_m(pass2);
+
+ if (NT_STATUS_IS_OK(nt_status = string_combinations(pass2, password_check, level))) {
+ if (fn)
+ fn(user, pass2);
+ return nt_status;
+ }
+
+ return NT_STATUS_WRONG_PASSWORD;
+}