summaryrefslogtreecommitdiff
path: root/source4/auth/ntlm
diff options
context:
space:
mode:
Diffstat (limited to 'source4/auth/ntlm')
-rw-r--r--source4/auth/ntlm/auth.c538
-rw-r--r--source4/auth/ntlm/auth_anonymous.c78
-rw-r--r--source4/auth/ntlm/auth_developer.c207
-rw-r--r--source4/auth/ntlm/auth_proto.h50
-rw-r--r--source4/auth/ntlm/auth_sam.c449
-rw-r--r--source4/auth/ntlm/auth_server.c225
-rw-r--r--source4/auth/ntlm/auth_simple.c103
-rw-r--r--source4/auth/ntlm/auth_unix.c844
-rw-r--r--source4/auth/ntlm/auth_util.c260
-rw-r--r--source4/auth/ntlm/auth_winbind.c282
-rw-r--r--source4/auth/ntlm/config.mk86
-rw-r--r--source4/auth/ntlm/ntlm_check.c603
-rw-r--r--source4/auth/ntlm/ntlm_check.h75
-rw-r--r--source4/auth/ntlm/pam_errors.c125
-rw-r--r--source4/auth/ntlm/pam_errors.h47
15 files changed, 3972 insertions, 0 deletions
diff --git a/source4/auth/ntlm/auth.c b/source4/auth/ntlm/auth.c
new file mode 100644
index 0000000000..0f1ef3ccdb
--- /dev/null
+++ b/source4/auth/ntlm/auth.c
@@ -0,0 +1,538 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Bartlett 2001-2002
+ Copyright (C) Stefan Metzmacher 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/util/dlinklist.h"
+#include "auth/auth.h"
+#include "auth/ntlm/auth_proto.h"
+#include "lib/events/events.h"
+#include "param/param.h"
+
+/***************************************************************************
+ Set a fixed challenge
+***************************************************************************/
+_PUBLIC_ NTSTATUS auth_context_set_challenge(struct auth_context *auth_ctx, const uint8_t chal[8], const char *set_by)
+{
+ auth_ctx->challenge.set_by = talloc_strdup(auth_ctx, set_by);
+ NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.set_by);
+
+ auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
+ NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Set a fixed challenge
+***************************************************************************/
+bool auth_challenge_may_be_modified(struct auth_context *auth_ctx)
+{
+ return auth_ctx->challenge.may_be_modified;
+}
+
+/****************************************************************************
+ Try to get a challenge out of the various authentication modules.
+ Returns a const char of length 8 bytes.
+****************************************************************************/
+_PUBLIC_ NTSTATUS auth_get_challenge(struct auth_context *auth_ctx, const uint8_t **_chal)
+{
+ NTSTATUS nt_status;
+ struct auth_method_context *method;
+
+ if (auth_ctx->challenge.data.length) {
+ DEBUG(5, ("auth_get_challenge: returning previous challenge by module %s (normal)\n",
+ auth_ctx->challenge.set_by));
+ *_chal = auth_ctx->challenge.data.data;
+ return NT_STATUS_OK;
+ }
+
+ for (method = auth_ctx->methods; method; method = method->next) {
+ DATA_BLOB challenge = data_blob(NULL,0);
+
+ nt_status = method->ops->get_challenge(method, auth_ctx, &challenge);
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NOT_IMPLEMENTED)) {
+ continue;
+ }
+
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ if (challenge.length != 8) {
+ DEBUG(0, ("auth_get_challenge: invalid challenge (length %u) by mothod [%s]\n",
+ (unsigned)challenge.length, method->ops->name));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ auth_ctx->challenge.data = challenge;
+ auth_ctx->challenge.set_by = method->ops->name;
+
+ break;
+ }
+
+ if (!auth_ctx->challenge.set_by) {
+ uint8_t chal[8];
+ generate_random_buffer(chal, 8);
+
+ auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
+ NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
+ auth_ctx->challenge.set_by = "random";
+
+ auth_ctx->challenge.may_be_modified = true;
+ }
+
+ DEBUG(10,("auth_get_challenge: challenge set by %s\n",
+ auth_ctx->challenge.set_by));
+
+ *_chal = auth_ctx->challenge.data.data;
+ return NT_STATUS_OK;
+}
+
+struct auth_check_password_sync_state {
+ bool finished;
+ NTSTATUS status;
+ struct auth_serversupplied_info *server_info;
+};
+
+static void auth_check_password_sync_callback(struct auth_check_password_request *req,
+ void *private_data)
+{
+ struct auth_check_password_sync_state *s = talloc_get_type(private_data,
+ struct auth_check_password_sync_state);
+
+ s->finished = true;
+ s->status = auth_check_password_recv(req, s, &s->server_info);
+}
+
+/**
+ * Check a user's Plaintext, LM or NTLM password.
+ * (sync version)
+ *
+ * Check a user's password, as given in the user_info struct and return various
+ * interesting details in the server_info struct.
+ *
+ * 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 auth_ctx Supplies the challenges and some other data.
+ * Must be created with auth_context_create(), and the challenges should be
+ * filled in, either at creation or by calling the challenge geneation
+ * function auth_get_challenge().
+ *
+ * @param user_info Contains the user supplied components, including the passwords.
+ *
+ * @param mem_ctx The parent memory context for the server_info structure
+ *
+ * @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.
+ *
+ **/
+
+_PUBLIC_ NTSTATUS auth_check_password(struct auth_context *auth_ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_serversupplied_info **server_info)
+{
+ struct auth_check_password_sync_state *sync_state;
+ NTSTATUS status;
+
+ sync_state = talloc_zero(auth_ctx, struct auth_check_password_sync_state);
+ NT_STATUS_HAVE_NO_MEMORY(sync_state);
+
+ auth_check_password_send(auth_ctx, user_info, auth_check_password_sync_callback, sync_state);
+
+ while (!sync_state->finished) {
+ event_loop_once(auth_ctx->event_ctx);
+ }
+
+ status = sync_state->status;
+
+ if (NT_STATUS_IS_OK(status)) {
+ *server_info = talloc_steal(mem_ctx, sync_state->server_info);
+ }
+
+ talloc_free(sync_state);
+ return status;
+}
+
+struct auth_check_password_request {
+ struct auth_context *auth_ctx;
+ const struct auth_usersupplied_info *user_info;
+ struct auth_serversupplied_info *server_info;
+ struct auth_method_context *method;
+ NTSTATUS status;
+ struct {
+ void (*fn)(struct auth_check_password_request *req, void *private_data);
+ void *private_data;
+ } callback;
+};
+
+static void auth_check_password_async_timed_handler(struct event_context *ev, struct timed_event *te,
+ struct timeval t, void *ptr)
+{
+ struct auth_check_password_request *req = talloc_get_type(ptr, struct auth_check_password_request);
+ req->status = req->method->ops->check_password(req->method, req, req->user_info, &req->server_info);
+ req->callback.fn(req, req->callback.private_data);
+}
+
+/**
+ * Check a user's Plaintext, LM or NTLM password.
+ * async send hook
+ *
+ * Check a user's password, as given in the user_info struct and return various
+ * interesting details in the server_info struct.
+ *
+ * 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 auth_ctx 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 user_info Contains the user supplied components, including the passwords.
+ *
+ * @param callback A callback function which will be called when the operation is finished.
+ * The callback function needs to call auth_check_password_recv() to get the return values
+ *
+ * @param private_data A private pointer which will ba passed to the callback function
+ *
+ **/
+
+_PUBLIC_ void auth_check_password_send(struct auth_context *auth_ctx,
+ const struct auth_usersupplied_info *user_info,
+ void (*callback)(struct auth_check_password_request *req, void *private_data),
+ void *private_data)
+{
+ /* if all the modules say 'not for me' this is reasonable */
+ NTSTATUS nt_status;
+ struct auth_method_context *method;
+ const uint8_t *challenge;
+ struct auth_usersupplied_info *user_info_tmp;
+ struct auth_check_password_request *req = NULL;
+
+ DEBUG(3, ("auth_check_password_send: Checking password for unmapped user [%s]\\[%s]@[%s]\n",
+ user_info->client.domain_name, user_info->client.account_name, user_info->workstation_name));
+
+ req = talloc_zero(auth_ctx, struct auth_check_password_request);
+ if (!req) {
+ callback(NULL, private_data);
+ return;
+ }
+ req->auth_ctx = auth_ctx;
+ req->user_info = user_info;
+ req->callback.fn = callback;
+ req->callback.private_data = private_data;
+
+ if (!user_info->mapped_state) {
+ nt_status = map_user_info(req, lp_workgroup(auth_ctx->lp_ctx), user_info, &user_info_tmp);
+ if (!NT_STATUS_IS_OK(nt_status)) goto failed;
+ user_info = user_info_tmp;
+ req->user_info = user_info_tmp;
+ }
+
+ DEBUGADD(3,("auth_check_password_send: mapped user is: [%s]\\[%s]@[%s]\n",
+ user_info->mapped.domain_name, user_info->mapped.account_name, user_info->workstation_name));
+
+ nt_status = auth_get_challenge(auth_ctx, &challenge);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("auth_check_password_send: Invalid challenge (length %u) stored for this auth context set_by %s - cannot continue: %s\n",
+ (unsigned)auth_ctx->challenge.data.length, auth_ctx->challenge.set_by, nt_errstr(nt_status)));
+ goto failed;
+ }
+
+ if (auth_ctx->challenge.set_by) {
+ DEBUG(10, ("auth_check_password_send: auth_context challenge created by %s\n",
+ auth_ctx->challenge.set_by));
+ }
+
+ DEBUG(10, ("auth_check_password_send: challenge is: \n"));
+ dump_data(5, auth_ctx->challenge.data.data, auth_ctx->challenge.data.length);
+
+ nt_status = NT_STATUS_NO_SUCH_USER; /* If all the modules say 'not for me', then this is reasonable */
+ for (method = auth_ctx->methods; method; method = method->next) {
+ NTSTATUS result;
+ struct timed_event *te = NULL;
+
+ /* check if the module wants to chek the password */
+ result = method->ops->want_check(method, req, user_info);
+ if (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
+ DEBUG(11,("auth_check_password_send: %s had nothing to say\n", method->ops->name));
+ continue;
+ }
+
+ nt_status = result;
+ req->method = method;
+
+ if (!NT_STATUS_IS_OK(nt_status)) break;
+
+ te = event_add_timed(auth_ctx->event_ctx, req,
+ timeval_zero(),
+ auth_check_password_async_timed_handler, req);
+ if (!te) {
+ nt_status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+ return;
+ }
+
+failed:
+ req->status = nt_status;
+ req->callback.fn(req, req->callback.private_data);
+}
+
+/**
+ * Check a user's Plaintext, LM or NTLM password.
+ * async receive function
+ *
+ * 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 req The async auth_check_password state, passes to the callers callback function
+ *
+ * @param mem_ctx The parent memory context for the server_info structure
+ *
+ * @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.
+ *
+ **/
+
+_PUBLIC_ NTSTATUS auth_check_password_recv(struct auth_check_password_request *req,
+ TALLOC_CTX *mem_ctx,
+ struct auth_serversupplied_info **server_info)
+{
+ NTSTATUS status;
+
+ NT_STATUS_HAVE_NO_MEMORY(req);
+
+ if (NT_STATUS_IS_OK(req->status)) {
+ DEBUG(5,("auth_check_password_recv: %s authentication for user [%s\\%s] succeeded\n",
+ req->method->ops->name, req->server_info->domain_name, req->server_info->account_name));
+
+ *server_info = talloc_steal(mem_ctx, req->server_info);
+ } else {
+ DEBUG(2,("auth_check_password_recv: %s authentication for user [%s\\%s] FAILED with error %s\n",
+ (req->method ? req->method->ops->name : "NO_METHOD"),
+ req->user_info->mapped.domain_name,
+ req->user_info->mapped.account_name,
+ nt_errstr(req->status)));
+ }
+
+ status = req->status;
+ talloc_free(req);
+ return status;
+}
+
+/***************************************************************************
+ Make a auth_info struct for the auth subsystem
+ - Allow the caller to specify the methods to use
+***************************************************************************/
+_PUBLIC_ NTSTATUS auth_context_create_methods(TALLOC_CTX *mem_ctx, const char **methods,
+ struct event_context *ev,
+ struct messaging_context *msg,
+ struct loadparm_context *lp_ctx,
+ struct auth_context **auth_ctx)
+{
+ int i;
+ struct auth_context *ctx;
+
+ if (!methods) {
+ DEBUG(0,("auth_context_create: No auth method list!?\n"));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (!ev) {
+ DEBUG(0,("auth_context_create: called with out event context\n"));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (!msg) {
+ DEBUG(0,("auth_context_create: called with out messaging context\n"));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ ctx = talloc(mem_ctx, struct auth_context);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+ ctx->challenge.set_by = NULL;
+ ctx->challenge.may_be_modified = false;
+ ctx->challenge.data = data_blob(NULL, 0);
+ ctx->methods = NULL;
+ ctx->event_ctx = ev;
+ ctx->msg_ctx = msg;
+ ctx->lp_ctx = lp_ctx;
+
+ for (i=0; methods[i] ; i++) {
+ struct auth_method_context *method;
+
+ method = talloc(ctx, struct auth_method_context);
+ NT_STATUS_HAVE_NO_MEMORY(method);
+
+ method->ops = auth_backend_byname(methods[i]);
+ if (!method->ops) {
+ DEBUG(1,("auth_context_create: failed to find method=%s\n",
+ methods[i]));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ method->auth_ctx = ctx;
+ method->depth = i;
+ DLIST_ADD_END(ctx->methods, method, struct auth_method_context *);
+ }
+
+ if (!ctx->methods) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ *auth_ctx = ctx;
+
+ return NT_STATUS_OK;
+}
+/***************************************************************************
+ Make a auth_info struct for the auth subsystem
+ - Uses default auth_methods, depending on server role and smb.conf settings
+***************************************************************************/
+_PUBLIC_ NTSTATUS auth_context_create(TALLOC_CTX *mem_ctx,
+ struct event_context *ev,
+ struct messaging_context *msg,
+ struct loadparm_context *lp_ctx,
+ struct auth_context **auth_ctx)
+{
+ const char **auth_methods = NULL;
+ switch (lp_server_role(lp_ctx)) {
+ case ROLE_STANDALONE:
+ auth_methods = lp_parm_string_list(mem_ctx, lp_ctx, NULL, "auth methods", "standalone", NULL);
+ break;
+ case ROLE_DOMAIN_MEMBER:
+ auth_methods = lp_parm_string_list(mem_ctx, lp_ctx, NULL, "auth methods", "member server", NULL);
+ break;
+ case ROLE_DOMAIN_CONTROLLER:
+ auth_methods = lp_parm_string_list(mem_ctx, lp_ctx, NULL, "auth methods", "domain controller", NULL);
+ break;
+ }
+ return auth_context_create_methods(mem_ctx, auth_methods, ev, msg, lp_ctx, auth_ctx);
+}
+
+
+/* the list of currently registered AUTH backends */
+static struct auth_backend {
+ const struct auth_operations *ops;
+} *backends = NULL;
+static int num_backends;
+
+/*
+ register a AUTH backend.
+
+ The 'name' can be later used by other backends to find the operations
+ structure for this backend.
+*/
+_PUBLIC_ NTSTATUS auth_register(const struct auth_operations *ops)
+{
+ struct auth_operations *new_ops;
+
+ if (auth_backend_byname(ops->name) != NULL) {
+ /* its already registered! */
+ DEBUG(0,("AUTH backend '%s' already registered\n",
+ ops->name));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ backends = talloc_realloc(talloc_autofree_context(), backends,
+ struct auth_backend, num_backends+1);
+ NT_STATUS_HAVE_NO_MEMORY(backends);
+
+ new_ops = talloc_memdup(backends, ops, sizeof(*ops));
+ NT_STATUS_HAVE_NO_MEMORY(new_ops);
+ new_ops->name = talloc_strdup(new_ops, ops->name);
+ NT_STATUS_HAVE_NO_MEMORY(new_ops->name);
+
+ backends[num_backends].ops = new_ops;
+
+ num_backends++;
+
+ DEBUG(3,("AUTH backend '%s' registered\n",
+ ops->name));
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return the operations structure for a named backend of the specified type
+*/
+const struct auth_operations *auth_backend_byname(const char *name)
+{
+ int i;
+
+ for (i=0;i<num_backends;i++) {
+ if (strcmp(backends[i].ops->name, name) == 0) {
+ return backends[i].ops;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ return the AUTH interface version, and the size of some critical types
+ This can be used by backends to either detect compilation errors, or provide
+ multiple implementations for different smbd compilation options in one module
+*/
+const struct auth_critical_sizes *auth_interface_version(void)
+{
+ static const struct auth_critical_sizes critical_sizes = {
+ AUTH_INTERFACE_VERSION,
+ sizeof(struct auth_operations),
+ sizeof(struct auth_method_context),
+ sizeof(struct auth_context),
+ sizeof(struct auth_usersupplied_info),
+ sizeof(struct auth_serversupplied_info)
+ };
+
+ return &critical_sizes;
+}
+
+_PUBLIC_ NTSTATUS auth_init(void)
+{
+ static bool initialized = false;
+ extern NTSTATUS auth_developer_init(void);
+ extern NTSTATUS auth_winbind_init(void);
+ extern NTSTATUS auth_anonymous_init(void);
+ extern NTSTATUS auth_unix_init(void);
+ extern NTSTATUS auth_sam_init(void);
+ extern NTSTATUS auth_server_init(void);
+
+ init_module_fn static_init[] = { STATIC_auth_MODULES };
+
+ if (initialized) return NT_STATUS_OK;
+ initialized = true;
+
+ run_init_functions(static_init);
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS server_service_auth_init(void)
+{
+ return auth_init();
+}
diff --git a/source4/auth/ntlm/auth_anonymous.c b/source4/auth/ntlm/auth_anonymous.c
new file mode 100644
index 0000000000..c889071878
--- /dev/null
+++ b/source4/auth/ntlm/auth_anonymous.c
@@ -0,0 +1,78 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Anonymous Authentification
+
+ Copyright (C) Stefan Metzmacher 2004-2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/auth.h"
+#include "auth/ntlm/auth_proto.h"
+#include "param/param.h"
+
+/**
+ * Return a anonymous logon for anonymous users (username = "")
+ *
+ * Typically used as the first module in the auth chain, this allows
+ * anonymou logons to be dealt with in one place. Non-anonymou logons 'fail'
+ * and pass onto the next module.
+ **/
+static NTSTATUS anonymous_want_check(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info)
+{
+ if (user_info->client.account_name && *user_info->client.account_name) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * Return a anonymous logon for anonymous users (username = "")
+ *
+ * Typically used as the first module in the auth chain, this allows
+ * anonymou logons to be dealt with in one place. Non-anonymou logons 'fail'
+ * and pass onto the next module.
+ **/
+static NTSTATUS anonymous_check_password(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_serversupplied_info **_server_info)
+{
+ return auth_anonymous_server_info(mem_ctx, lp_netbios_name(ctx->auth_ctx->lp_ctx), _server_info);
+}
+
+static const struct auth_operations anonymous_auth_ops = {
+ .name = "anonymous",
+ .get_challenge = auth_get_challenge_not_implemented,
+ .want_check = anonymous_want_check,
+ .check_password = anonymous_check_password
+};
+
+_PUBLIC_ NTSTATUS auth_anonymous_init(void)
+{
+ NTSTATUS ret;
+
+ ret = auth_register(&anonymous_auth_ops);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register 'anonymous' auth backend!\n"));
+ return ret;
+ }
+
+ return ret;
+}
diff --git a/source4/auth/ntlm/auth_developer.c b/source4/auth/ntlm/auth_developer.c
new file mode 100644
index 0000000000..3b8c83c349
--- /dev/null
+++ b/source4/auth/ntlm/auth_developer.c
@@ -0,0 +1,207 @@
+/*
+ Unix SMB/CIFS implementation.
+ Generic authentication types
+ Copyright (C) Andrew Bartlett 2001-2002
+ Copyright (C) Jelmer Vernooij 2002
+ Copyright (C) Stefan Metzmacher 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/auth.h"
+#include "auth/ntlm/auth_proto.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_samr.h"
+
+static NTSTATUS name_to_ntstatus_want_check(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info)
+{
+ return NT_STATUS_OK;
+}
+
+/**
+ * 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 name_to_ntstatus_check_password(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_serversupplied_info **_server_info)
+{
+ NTSTATUS nt_status;
+ struct auth_serversupplied_info *server_info;
+ uint32_t error_num;
+ const char *user;
+
+ user = user_info->client.account_name;
+
+ if (strncasecmp("NT_STATUS", user, strlen("NT_STATUS")) == 0) {
+ nt_status = nt_status_string_to_code(user);
+ } else {
+ error_num = strtoul(user, NULL, 16);
+ DEBUG(5,("name_to_ntstatus_check_password: Error for user %s was 0x%08X\n", user, error_num));
+ nt_status = NT_STATUS(error_num);
+ }
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ server_info = talloc(mem_ctx, struct auth_serversupplied_info);
+ NT_STATUS_HAVE_NO_MEMORY(server_info);
+
+ server_info->account_sid = dom_sid_parse_talloc(server_info, SID_NT_ANONYMOUS);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->account_sid);
+
+ /* is this correct? */
+ server_info->primary_group_sid = dom_sid_parse_talloc(server_info, SID_BUILTIN_GUESTS);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->primary_group_sid);
+
+ server_info->n_domain_groups = 0;
+ server_info->domain_groups = NULL;
+
+ /* annoying, but the Anonymous really does have a session key,
+ and it is all zeros! */
+ server_info->user_session_key = data_blob_talloc(server_info, NULL, 16);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->user_session_key.data);
+
+ server_info->lm_session_key = data_blob_talloc(server_info, NULL, 16);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->lm_session_key.data);
+
+ data_blob_clear(&server_info->user_session_key);
+ data_blob_clear(&server_info->lm_session_key);
+
+ server_info->account_name = talloc_asprintf(server_info, "NAME TO NTSTATUS %s ANONYMOUS LOGON", user);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->account_name);
+
+ server_info->domain_name = talloc_strdup(server_info, "NT AUTHORITY");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->domain_name);
+
+ server_info->full_name = talloc_asprintf(server_info, "NAME TO NTSTATUS %s Anonymous Logon", user);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->full_name);
+
+ server_info->logon_script = talloc_strdup(server_info, "");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->logon_script);
+
+ server_info->profile_path = talloc_strdup(server_info, "");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->profile_path);
+
+ server_info->home_directory = talloc_strdup(server_info, "");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->home_directory);
+
+ server_info->home_drive = talloc_strdup(server_info, "");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->home_drive);
+
+ server_info->last_logon = 0;
+ server_info->last_logoff = 0;
+ server_info->acct_expiry = 0;
+ server_info->last_password_change = 0;
+ server_info->allow_password_change = 0;
+ server_info->force_password_change = 0;
+
+ server_info->logon_count = 0;
+ server_info->bad_password_count = 0;
+
+ server_info->acct_flags = ACB_NORMAL;
+
+ server_info->authenticated = false;
+
+ *_server_info = server_info;
+
+ return nt_status;
+}
+
+static const struct auth_operations name_to_ntstatus_auth_ops = {
+ .name = "name_to_ntstatus",
+ .get_challenge = auth_get_challenge_not_implemented,
+ .want_check = name_to_ntstatus_want_check,
+ .check_password = name_to_ntstatus_check_password
+};
+
+/**
+ * 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 fixed_challenge_get_challenge(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx, DATA_BLOB *_blob)
+{
+ DATA_BLOB blob;
+ const char *challenge = "I am a teapot";
+
+ blob = data_blob_talloc(mem_ctx, challenge, 8);
+ NT_STATUS_HAVE_NO_MEMORY(blob.data);
+
+ *_blob = blob;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS fixed_challenge_want_check(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info)
+{
+ /* don't handle any users */
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS fixed_challenge_check_password(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_serversupplied_info **_server_info)
+{
+ /* don't handle any users */
+ return NT_STATUS_NO_SUCH_USER;
+}
+
+static const struct auth_operations fixed_challenge_auth_ops = {
+ .name = "fixed_challenge",
+ .get_challenge = fixed_challenge_get_challenge,
+ .want_check = fixed_challenge_want_check,
+ .check_password = fixed_challenge_check_password
+};
+
+_PUBLIC_ NTSTATUS auth_developer_init(void)
+{
+ NTSTATUS ret;
+
+ ret = auth_register(&name_to_ntstatus_auth_ops);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register 'name_to_ntstatus' auth backend!\n"));
+ return ret;
+ }
+
+ ret = auth_register(&fixed_challenge_auth_ops);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register 'fixed_challenge' auth backend!\n"));
+ return ret;
+ }
+
+ return ret;
+}
diff --git a/source4/auth/ntlm/auth_proto.h b/source4/auth/ntlm/auth_proto.h
new file mode 100644
index 0000000000..572c1a4ca7
--- /dev/null
+++ b/source4/auth/ntlm/auth_proto.h
@@ -0,0 +1,50 @@
+#ifndef __AUTH_NTLM_AUTH_PROTO_H__
+#define __AUTH_NTLM_AUTH_PROTO_H__
+
+#undef _PRINTF_ATTRIBUTE
+#define _PRINTF_ATTRIBUTE(a1, a2) PRINTF_ATTRIBUTE(a1, a2)
+/* This file was automatically generated by mkproto.pl. DO NOT EDIT */
+
+/* this file contains prototypes for functions that are private
+ * to this subsystem or library. These functions should not be
+ * used outside this particular subsystem! */
+
+
+/* The following definitions come from auth/ntlm/auth.c */
+
+
+/***************************************************************************
+ Set a fixed challenge
+***************************************************************************/
+bool auth_challenge_may_be_modified(struct auth_context *auth_ctx) ;
+const struct auth_operations *auth_backend_byname(const char *name);
+const struct auth_critical_sizes *auth_interface_version(void);
+NTSTATUS server_service_auth_init(void);
+
+/* The following definitions come from auth/ntlm/auth_util.c */
+
+NTSTATUS auth_get_challenge_not_implemented(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx, DATA_BLOB *challenge);
+
+/****************************************************************************
+ Create an auth_usersupplied_data structure after appropriate mapping.
+****************************************************************************/
+NTSTATUS map_user_info(TALLOC_CTX *mem_ctx,
+ const char *default_domain,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_usersupplied_info **user_info_mapped);
+
+/****************************************************************************
+ Create an auth_usersupplied_data structure after appropriate mapping.
+****************************************************************************/
+NTSTATUS encrypt_user_info(TALLOC_CTX *mem_ctx, struct auth_context *auth_context,
+ enum auth_password_state to_state,
+ const struct auth_usersupplied_info *user_info_in,
+ const struct auth_usersupplied_info **user_info_encrypted);
+
+/* The following definitions come from auth/ntlm/auth_simple.c */
+
+#undef _PRINTF_ATTRIBUTE
+#define _PRINTF_ATTRIBUTE(a1, a2)
+
+#endif /* __AUTH_NTLM_AUTH_PROTO_H__ */
+
diff --git a/source4/auth/ntlm/auth_sam.c b/source4/auth/ntlm/auth_sam.c
new file mode 100644
index 0000000000..2c13cd963d
--- /dev/null
+++ b/source4/auth/ntlm/auth_sam.c
@@ -0,0 +1,449 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2004
+ Copyright (C) Gerald Carter 2003
+ Copyright (C) Stefan Metzmacher 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
+#include "system/time.h"
+#include "lib/ldb/include/ldb.h"
+#include "util/util_ldb.h"
+#include "auth/auth.h"
+#include "auth/ntlm/ntlm_check.h"
+#include "auth/ntlm/auth_proto.h"
+#include "auth/auth_sam.h"
+#include "dsdb/samdb/samdb.h"
+#include "libcli/security/security.h"
+#include "libcli/ldap/ldap_ndr.h"
+#include "param/param.h"
+
+extern const char *user_attrs[];
+extern const char *domain_ref_attrs[];
+
+/****************************************************************************
+ Look for the specified user in the sam, return ldb result structures
+****************************************************************************/
+
+static NTSTATUS authsam_search_account(TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
+ const char *account_name,
+ const char *domain_name,
+ struct ldb_message ***ret_msgs,
+ struct ldb_message ***ret_msgs_domain_ref)
+{
+ struct ldb_message **msgs_tmp;
+ struct ldb_message **msgs;
+ struct ldb_message **msgs_domain_ref;
+ struct ldb_dn *partitions_basedn = samdb_partitions_dn(sam_ctx, mem_ctx);
+
+ int ret;
+ int ret_domain;
+
+ struct ldb_dn *domain_dn = NULL;
+
+ if (domain_name) {
+ domain_dn = samdb_domain_to_dn(sam_ctx, mem_ctx, domain_name);
+ if (!domain_dn) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ }
+
+ /* pull the user attributes */
+ ret = gendb_search(sam_ctx, mem_ctx, domain_dn, &msgs, user_attrs,
+ "(&(sAMAccountName=%s)(objectclass=user))",
+ ldb_binary_encode_string(mem_ctx, account_name));
+ if (ret == -1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (ret == 0) {
+ DEBUG(3,("sam_search_user: Couldn't find user [%s\\%s] in samdb, under %s\n",
+ domain_name, account_name, ldb_dn_get_linearized(domain_dn)));
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (ret > 1) {
+ DEBUG(0,("Found %d records matching user [%s]\n", ret, account_name));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (!domain_dn) {
+ struct dom_sid *domain_sid;
+
+ domain_sid = samdb_result_sid_prefix(mem_ctx, msgs[0], "objectSid");
+ if (!domain_sid) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* find the domain's DN */
+ ret = gendb_search(sam_ctx, mem_ctx, NULL, &msgs_tmp, NULL,
+ "(&(objectSid=%s)(objectClass=domain))",
+ ldap_encode_ndr_dom_sid(mem_ctx, domain_sid));
+ if (ret == -1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (ret == 0) {
+ DEBUG(3,("check_sam_security: Couldn't find domain_sid [%s] in passdb file.\n",
+ dom_sid_string(mem_ctx, domain_sid)));
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (ret > 1) {
+ DEBUG(0,("Found %d records matching domain_sid [%s]\n",
+ ret, dom_sid_string(mem_ctx, domain_sid)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ domain_dn = msgs_tmp[0]->dn;
+ }
+
+ ret_domain = gendb_search(sam_ctx, mem_ctx, partitions_basedn, &msgs_domain_ref, domain_ref_attrs,
+ "(nCName=%s)", ldb_dn_get_linearized(domain_dn));
+ if (ret_domain == -1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (ret_domain == 0) {
+ DEBUG(3,("check_sam_security: Couldn't find domain [%s] in passdb file.\n",
+ ldb_dn_get_linearized(msgs_tmp[0]->dn)));
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (ret_domain > 1) {
+ DEBUG(0,("Found %d records matching domain [%s]\n",
+ ret_domain, ldb_dn_get_linearized(msgs_tmp[0]->dn)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ *ret_msgs = msgs;
+ *ret_msgs_domain_ref = msgs_domain_ref;
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Do a specific test for an smb password being correct, given a smb_password and
+ the lanman and NT responses.
+****************************************************************************/
+static NTSTATUS authsam_password_ok(struct auth_context *auth_context,
+ TALLOC_CTX *mem_ctx,
+ uint16_t acct_flags,
+ const struct samr_Password *lm_pwd,
+ const struct samr_Password *nt_pwd,
+ const struct auth_usersupplied_info *user_info,
+ DATA_BLOB *user_sess_key,
+ DATA_BLOB *lm_sess_key)
+{
+ NTSTATUS status;
+
+ if (acct_flags & ACB_PWNOTREQ) {
+ if (lp_null_passwords(auth_context->lp_ctx)) {
+ DEBUG(3,("Account for user '%s' has no password and null passwords are allowed.\n",
+ user_info->mapped.account_name));
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(3,("Account for user '%s' has no password and null passwords are NOT allowed.\n",
+ user_info->mapped.account_name));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+ }
+
+ switch (user_info->password_state) {
+ case AUTH_PASSWORD_PLAIN:
+ {
+ const struct auth_usersupplied_info *user_info_temp;
+ status = encrypt_user_info(mem_ctx, auth_context,
+ AUTH_PASSWORD_HASH,
+ user_info, &user_info_temp);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to convert plaintext password to password HASH: %s\n", nt_errstr(status)));
+ return status;
+ }
+ user_info = user_info_temp;
+
+ /*fall through*/
+ }
+ case AUTH_PASSWORD_HASH:
+ *lm_sess_key = data_blob(NULL, 0);
+ *user_sess_key = data_blob(NULL, 0);
+ status = hash_password_check(mem_ctx,
+ auth_context->lp_ctx,
+ user_info->password.hash.lanman,
+ user_info->password.hash.nt,
+ user_info->mapped.account_name,
+ lm_pwd, nt_pwd);
+ NT_STATUS_NOT_OK_RETURN(status);
+ break;
+
+ case AUTH_PASSWORD_RESPONSE:
+ status = ntlm_password_check(mem_ctx,
+ auth_context->lp_ctx,
+ user_info->logon_parameters,
+ &auth_context->challenge.data,
+ &user_info->password.response.lanman,
+ &user_info->password.response.nt,
+ user_info->mapped.account_name,
+ user_info->client.account_name,
+ user_info->client.domain_name,
+ lm_pwd, nt_pwd,
+ user_sess_key, lm_sess_key);
+ NT_STATUS_NOT_OK_RETURN(status);
+ break;
+ }
+
+ if (user_sess_key && user_sess_key->data) {
+ talloc_steal(auth_context, user_sess_key->data);
+ }
+ if (lm_sess_key && lm_sess_key->data) {
+ talloc_steal(auth_context, lm_sess_key->data);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+
+static NTSTATUS authsam_authenticate(struct auth_context *auth_context,
+ TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
+ struct ldb_message **msgs,
+ struct ldb_message **msgs_domain_ref,
+ const struct auth_usersupplied_info *user_info,
+ DATA_BLOB *user_sess_key, DATA_BLOB *lm_sess_key)
+{
+ struct samr_Password *lm_pwd, *nt_pwd;
+ NTSTATUS nt_status;
+ struct ldb_dn *domain_dn = samdb_result_dn(sam_ctx, mem_ctx, msgs_domain_ref[0], "nCName", NULL);
+
+ uint16_t acct_flags = samdb_result_acct_flags(sam_ctx, mem_ctx, msgs[0], domain_dn);
+
+ /* Quit if the account was locked out. */
+ if (acct_flags & ACB_AUTOLOCK) {
+ DEBUG(3,("check_sam_security: Account for user %s was locked out.\n",
+ user_info->mapped.account_name));
+ return NT_STATUS_ACCOUNT_LOCKED_OUT;
+ }
+
+ /* You can only do an interactive login to normal accounts */
+ if (user_info->flags & USER_INFO_INTERACTIVE_LOGON) {
+ if (!(acct_flags & ACB_NORMAL)) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+ }
+
+ nt_status = samdb_result_passwords(mem_ctx, msgs[0], &lm_pwd, &nt_pwd);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ nt_status = authsam_password_ok(auth_context, mem_ctx,
+ acct_flags, lm_pwd, nt_pwd,
+ user_info, user_sess_key, lm_sess_key);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ nt_status = authsam_account_ok(mem_ctx, sam_ctx,
+ user_info->logon_parameters,
+ msgs[0],
+ msgs_domain_ref[0],
+ user_info->workstation_name,
+ user_info->mapped.account_name);
+
+ return nt_status;
+}
+
+
+
+static NTSTATUS authsam_check_password_internals(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *domain,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_serversupplied_info **server_info)
+{
+ NTSTATUS nt_status;
+ const char *account_name = user_info->mapped.account_name;
+ struct ldb_message **msgs;
+ struct ldb_message **domain_ref_msgs;
+ struct ldb_context *sam_ctx;
+ DATA_BLOB user_sess_key, lm_sess_key;
+ TALLOC_CTX *tmp_ctx;
+
+ if (!account_name || !*account_name) {
+ /* 'not for me' */
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ sam_ctx = samdb_connect(tmp_ctx, ctx->auth_ctx->event_ctx, ctx->auth_ctx->lp_ctx, system_session(mem_ctx, ctx->auth_ctx->lp_ctx));
+ if (sam_ctx == NULL) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ nt_status = authsam_search_account(tmp_ctx, sam_ctx, account_name, domain, &msgs, &domain_ref_msgs);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(tmp_ctx);
+ return nt_status;
+ }
+
+ nt_status = authsam_authenticate(ctx->auth_ctx, tmp_ctx, sam_ctx, msgs, domain_ref_msgs, user_info,
+ &user_sess_key, &lm_sess_key);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(tmp_ctx);
+ return nt_status;
+ }
+
+ nt_status = authsam_make_server_info(tmp_ctx, sam_ctx, lp_netbios_name(ctx->auth_ctx->lp_ctx),
+ msgs[0], domain_ref_msgs[0],
+ user_sess_key, lm_sess_key,
+ server_info);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(tmp_ctx);
+ return nt_status;
+ }
+
+ talloc_steal(mem_ctx, *server_info);
+ talloc_free(tmp_ctx);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS authsam_ignoredomain_want_check(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info)
+{
+ if (!user_info->mapped.account_name || !*user_info->mapped.account_name) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS authsam_ignoredomain_check_password(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_serversupplied_info **server_info)
+{
+ return authsam_check_password_internals(ctx, mem_ctx, NULL, user_info, server_info);
+}
+
+/****************************************************************************
+Check SAM security (above) but with a few extra checks.
+****************************************************************************/
+static NTSTATUS authsam_want_check(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info)
+{
+ bool is_local_name, is_my_domain;
+
+ if (!user_info->mapped.account_name || !*user_info->mapped.account_name) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ is_local_name = lp_is_myname(ctx->auth_ctx->lp_ctx,
+ user_info->mapped.domain_name);
+ is_my_domain = lp_is_mydomain(ctx->auth_ctx->lp_ctx,
+ user_info->mapped.domain_name);
+
+ /* check whether or not we service this domain/workgroup name */
+ switch (lp_server_role(ctx->auth_ctx->lp_ctx)) {
+ case ROLE_STANDALONE:
+ return NT_STATUS_OK;
+
+ case ROLE_DOMAIN_MEMBER:
+ if (!is_local_name) {
+ DEBUG(6,("authsam_check_password: %s is not one of my local names (DOMAIN_MEMBER)\n",
+ user_info->mapped.domain_name));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return NT_STATUS_OK;
+
+ case ROLE_DOMAIN_CONTROLLER:
+ if (!is_local_name && !is_my_domain) {
+ DEBUG(6,("authsam_check_password: %s is not one of my local names or domain name (DC)\n",
+ user_info->mapped.domain_name));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(6,("authsam_check_password: lp_server_role() has an undefined value\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************************
+Check SAM security (above) but with a few extra checks.
+****************************************************************************/
+static NTSTATUS authsam_check_password(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_serversupplied_info **server_info)
+{
+ const char *domain;
+
+ /* check whether or not we service this domain/workgroup name */
+ switch (lp_server_role(ctx->auth_ctx->lp_ctx)) {
+ case ROLE_STANDALONE:
+ case ROLE_DOMAIN_MEMBER:
+ domain = lp_netbios_name(ctx->auth_ctx->lp_ctx);
+ break;
+
+ case ROLE_DOMAIN_CONTROLLER:
+ domain = lp_workgroup(ctx->auth_ctx->lp_ctx);
+ break;
+
+ default:
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ return authsam_check_password_internals(ctx, mem_ctx, domain, user_info, server_info);
+}
+
+static const struct auth_operations sam_ignoredomain_ops = {
+ .name = "sam_ignoredomain",
+ .get_challenge = auth_get_challenge_not_implemented,
+ .want_check = authsam_ignoredomain_want_check,
+ .check_password = authsam_ignoredomain_check_password
+};
+
+static const struct auth_operations sam_ops = {
+ .name = "sam",
+ .get_challenge = auth_get_challenge_not_implemented,
+ .want_check = authsam_want_check,
+ .check_password = authsam_check_password
+};
+
+_PUBLIC_ NTSTATUS auth_sam_init(void)
+{
+ NTSTATUS ret;
+
+ ret = auth_register(&sam_ops);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register 'sam' auth backend!\n"));
+ return ret;
+ }
+
+ ret = auth_register(&sam_ignoredomain_ops);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register 'sam_ignoredomain' auth backend!\n"));
+ return ret;
+ }
+
+ return ret;
+}
diff --git a/source4/auth/ntlm/auth_server.c b/source4/auth/ntlm/auth_server.c
new file mode 100644
index 0000000000..f154cf0425
--- /dev/null
+++ b/source4/auth/ntlm/auth_server.c
@@ -0,0 +1,225 @@
+/*
+ Unix SMB/CIFS implementation.
+ Authenticate by using a remote server
+ Copyright (C) Andrew Bartlett 2001-2002, 2008
+ Copyright (C) Jelmer Vernooij 2002
+ Copyright (C) Stefan Metzmacher 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/auth.h"
+#include "auth/ntlm/auth_proto.h"
+#include "auth/credentials/credentials.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_samr.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.h"
+
+/* This version of 'security=server' rewirtten from scratch for Samba4
+ * libraries in 2008 */
+
+
+static NTSTATUS server_want_check(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info)
+{
+ return NT_STATUS_OK;
+}
+/**
+ * The challenge from the target server, when operating in security=server
+ **/
+static NTSTATUS server_get_challenge(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx, DATA_BLOB *_blob)
+{
+ struct smb_composite_connect io;
+ struct smbcli_options smb_options;
+ const char **host_list;
+ NTSTATUS status;
+
+ /* Make a connection to the target server, found by 'password server' in smb.conf */
+
+ lp_smbcli_options(ctx->auth_ctx->lp_ctx, &smb_options);
+
+ /* Make a negprot, WITHOUT SPNEGO, so we get a challenge nice an easy */
+ io.in.options.use_spnego = false;
+
+ /* Hope we don't get * (the default), as this won't work... */
+ host_list = lp_passwordserver(ctx->auth_ctx->lp_ctx);
+ if (!host_list) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ io.in.dest_host = host_list[0];
+ if (strequal(io.in.dest_host, "*")) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ io.in.dest_ports = lp_smb_ports(ctx->auth_ctx->lp_ctx);
+
+ io.in.called_name = strupper_talloc(mem_ctx, io.in.dest_host);
+
+ /* We don't want to get as far as the session setup */
+ io.in.credentials = NULL;
+ io.in.service = NULL;
+
+ io.in.workgroup = ""; /* only used with SPNEGO, disabled above */
+
+ io.in.options = smb_options;
+
+ status = smb_composite_connect(&io, mem_ctx, lp_resolve_context(ctx->auth_ctx->lp_ctx),
+ ctx->auth_ctx->event_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ *_blob = io.out.tree->session->transport->negotiate.secblob;
+ ctx->private_data = talloc_steal(ctx, io.out.tree->session);
+ }
+ return NT_STATUS_OK;
+}
+
+/**
+ * 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 server_check_password(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_serversupplied_info **_server_info)
+{
+ NTSTATUS nt_status;
+ struct auth_serversupplied_info *server_info;
+ struct cli_credentials *creds;
+ const char *user;
+ struct smb_composite_sesssetup session_setup;
+
+ struct smbcli_session *session = talloc_get_type(ctx->private_data, struct smbcli_session);
+
+ creds = cli_credentials_init(mem_ctx);
+
+ NT_STATUS_HAVE_NO_MEMORY(creds);
+
+ cli_credentials_set_username(creds, user_info->client.account_name, CRED_SPECIFIED);
+ cli_credentials_set_domain(creds, user_info->client.domain_name, CRED_SPECIFIED);
+
+ switch (user_info->password_state) {
+ case AUTH_PASSWORD_PLAIN:
+ cli_credentials_set_password(creds, user_info->password.plaintext,
+ CRED_SPECIFIED);
+ break;
+ case AUTH_PASSWORD_HASH:
+ cli_credentials_set_nt_hash(creds, user_info->password.hash.nt,
+ CRED_SPECIFIED);
+ break;
+
+ case AUTH_PASSWORD_RESPONSE:
+ cli_credentials_set_ntlm_response(creds, &user_info->password.response.lanman, &user_info->password.response.nt, CRED_SPECIFIED);
+ break;
+ }
+
+ session_setup.in.sesskey = session->transport->negotiate.sesskey;
+ session_setup.in.capabilities = session->transport->negotiate.capabilities;
+
+ session_setup.in.credentials = creds;
+ session_setup.in.workgroup = ""; /* Only used with SPNEGO, which we are not doing */
+
+ /* Check password with remove server - this should be async some day */
+ nt_status = smb_composite_sesssetup(session, &session_setup);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ server_info = talloc(mem_ctx, struct auth_serversupplied_info);
+ NT_STATUS_HAVE_NO_MEMORY(server_info);
+
+ server_info->account_sid = dom_sid_parse_talloc(server_info, SID_NT_ANONYMOUS);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->account_sid);
+
+ /* is this correct? */
+ server_info->primary_group_sid = dom_sid_parse_talloc(server_info, SID_BUILTIN_GUESTS);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->primary_group_sid);
+
+ server_info->n_domain_groups = 0;
+ server_info->domain_groups = NULL;
+
+ /* annoying, but the Anonymous really does have a session key,
+ and it is all zeros! */
+ server_info->user_session_key = data_blob(NULL, 0);
+ server_info->lm_session_key = data_blob(NULL, 0);
+
+ server_info->account_name = talloc_strdup(server_info, user_info->client.account_name);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->account_name);
+
+ server_info->domain_name = talloc_strdup(server_info, user_info->client.domain_name);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->domain_name);
+
+ server_info->full_name = NULL;
+
+ server_info->logon_script = talloc_strdup(server_info, "");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->logon_script);
+
+ server_info->profile_path = talloc_strdup(server_info, "");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->profile_path);
+
+ server_info->home_directory = talloc_strdup(server_info, "");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->home_directory);
+
+ server_info->home_drive = talloc_strdup(server_info, "");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->home_drive);
+
+ server_info->last_logon = 0;
+ server_info->last_logoff = 0;
+ server_info->acct_expiry = 0;
+ server_info->last_password_change = 0;
+ server_info->allow_password_change = 0;
+ server_info->force_password_change = 0;
+
+ server_info->logon_count = 0;
+ server_info->bad_password_count = 0;
+
+ server_info->acct_flags = ACB_NORMAL;
+
+ server_info->authenticated = false;
+
+ *_server_info = server_info;
+
+ return nt_status;
+}
+
+static const struct auth_operations server_auth_ops = {
+ .name = "server",
+ .get_challenge = server_get_challenge,
+ .want_check = server_want_check,
+ .check_password = server_check_password
+};
+
+_PUBLIC_ NTSTATUS auth_server_init(void)
+{
+ NTSTATUS ret;
+
+ ret = auth_register(&server_auth_ops);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register 'server' auth backend!\n"));
+ return ret;
+ }
+
+ return ret;
+}
diff --git a/source4/auth/ntlm/auth_simple.c b/source4/auth/ntlm/auth_simple.c
new file mode 100644
index 0000000000..e7039c3657
--- /dev/null
+++ b/source4/auth/ntlm/auth_simple.c
@@ -0,0 +1,103 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ auth functions
+
+ Copyright (C) Simo Sorce 2005
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Andrew Bartlett 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/auth.h"
+#include "lib/events/events.h"
+#include "param/param.h"
+#include "auth/session_proto.h"
+
+/*
+ It's allowed to pass NULL as session_info,
+ when the caller doesn't need a session_info
+*/
+_PUBLIC_ NTSTATUS authenticate_username_pw(TALLOC_CTX *mem_ctx,
+ struct event_context *ev,
+ struct messaging_context *msg,
+ struct loadparm_context *lp_ctx,
+ const char *nt4_domain,
+ const char *nt4_username,
+ const char *password,
+ struct auth_session_info **session_info)
+{
+ struct auth_context *auth_context;
+ struct auth_usersupplied_info *user_info;
+ struct auth_serversupplied_info *server_info;
+ NTSTATUS nt_status;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+
+ if (!tmp_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ nt_status = auth_context_create(tmp_ctx,
+ ev, msg,
+ lp_ctx,
+ &auth_context);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(tmp_ctx);
+ return nt_status;
+ }
+
+ user_info = talloc(tmp_ctx, struct auth_usersupplied_info);
+ if (!user_info) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ user_info->mapped_state = true;
+ user_info->client.account_name = nt4_username;
+ user_info->mapped.account_name = nt4_username;
+ user_info->client.domain_name = nt4_domain;
+ user_info->mapped.domain_name = nt4_domain;
+
+ user_info->workstation_name = NULL;
+
+ user_info->remote_host = NULL;
+
+ user_info->password_state = AUTH_PASSWORD_PLAIN;
+ user_info->password.plaintext = talloc_strdup(user_info, password);
+
+ user_info->flags = USER_INFO_CASE_INSENSITIVE_USERNAME |
+ USER_INFO_DONT_CHECK_UNIX_ACCOUNT;
+
+ user_info->logon_parameters = 0;
+
+ nt_status = auth_check_password(auth_context, tmp_ctx, user_info, &server_info);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(tmp_ctx);
+ return nt_status;
+ }
+
+ if (session_info) {
+ nt_status = auth_generate_session_info(tmp_ctx, ev, lp_ctx, server_info, session_info);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ talloc_steal(mem_ctx, *session_info);
+ }
+ }
+
+ talloc_free(tmp_ctx);
+ return nt_status;
+}
+
diff --git a/source4/auth/ntlm/auth_unix.c b/source4/auth/ntlm/auth_unix.c
new file mode 100644
index 0000000000..1717b9d0e1
--- /dev/null
+++ b/source4/auth/ntlm/auth_unix.c
@@ -0,0 +1,844 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Bartlett 2001
+ Copyright (C) Jeremy Allison 2001
+ Copyright (C) Simo Sorce 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/auth.h"
+#include "auth/ntlm/auth_proto.h"
+#include "system/passwd.h" /* needed by some systems for struct passwd */
+#include "lib/socket/socket.h"
+#include "auth/ntlm/pam_errors.h"
+#include "param/param.h"
+
+/* TODO: look at how to best fill in parms retrieveing a struct passwd info
+ * except in case USER_INFO_DONT_CHECK_UNIX_ACCOUNT is set
+ */
+static NTSTATUS authunix_make_server_info(TALLOC_CTX *mem_ctx,
+ const char *netbios_name,
+ const struct auth_usersupplied_info *user_info,
+ struct passwd *pwd,
+ struct auth_serversupplied_info **_server_info)
+{
+ struct auth_serversupplied_info *server_info;
+ NTSTATUS status;
+
+ /* This is a real, real hack */
+ if (pwd->pw_uid == 0) {
+ status = auth_system_server_info(mem_ctx, netbios_name, &server_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ server_info->account_name = talloc_steal(server_info, pwd->pw_name);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->account_name);
+
+ server_info->domain_name = talloc_strdup(server_info, "unix");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->domain_name);
+ } else {
+ server_info = talloc(mem_ctx, struct auth_serversupplied_info);
+ NT_STATUS_HAVE_NO_MEMORY(server_info);
+
+ server_info->authenticated = true;
+
+ server_info->account_name = talloc_steal(server_info, pwd->pw_name);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->account_name);
+
+ server_info->domain_name = talloc_strdup(server_info, "unix");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->domain_name);
+
+ /* This isn't in any way correct.. */
+ server_info->account_sid = NULL;
+ server_info->primary_group_sid = NULL;
+ server_info->n_domain_groups = 0;
+ server_info->domain_groups = NULL;
+ }
+ server_info->user_session_key = data_blob(NULL,0);
+ server_info->lm_session_key = data_blob(NULL,0);
+
+ server_info->full_name = talloc_steal(server_info, pwd->pw_gecos);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->full_name);
+ server_info->logon_script = talloc_strdup(server_info, "");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->logon_script);
+ server_info->profile_path = talloc_strdup(server_info, "");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->profile_path);
+ server_info->home_directory = talloc_strdup(server_info, "");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->home_directory);
+ server_info->home_drive = talloc_strdup(server_info, "");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->home_drive);
+
+ server_info->last_logon = 0;
+ server_info->last_logoff = 0;
+ server_info->acct_expiry = 0;
+ server_info->last_password_change = 0;
+ server_info->allow_password_change = 0;
+ server_info->force_password_change = 0;
+ server_info->logon_count = 0;
+ server_info->bad_password_count = 0;
+ server_info->acct_flags = 0;
+
+ *_server_info = server_info;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS talloc_getpwnam(TALLOC_CTX *ctx, const char *username, struct passwd **pws)
+{
+ struct passwd *ret;
+ struct passwd *from;
+
+ *pws = NULL;
+
+ ret = talloc(ctx, struct passwd);
+ NT_STATUS_HAVE_NO_MEMORY(ret);
+
+ from = getpwnam(username);
+ if (!from) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ ret->pw_name = talloc_strdup(ctx, from->pw_name);
+ NT_STATUS_HAVE_NO_MEMORY(ret->pw_name);
+
+ ret->pw_passwd = talloc_strdup(ctx, from->pw_passwd);
+ NT_STATUS_HAVE_NO_MEMORY(ret->pw_passwd);
+
+ ret->pw_uid = from->pw_uid;
+ ret->pw_gid = from->pw_gid;
+ ret->pw_gecos = talloc_strdup(ctx, from->pw_gecos);
+ NT_STATUS_HAVE_NO_MEMORY(ret->pw_gecos);
+
+ ret->pw_dir = talloc_strdup(ctx, from->pw_dir);
+ NT_STATUS_HAVE_NO_MEMORY(ret->pw_dir);
+
+ ret->pw_shell = talloc_strdup(ctx, from->pw_shell);
+ NT_STATUS_HAVE_NO_MEMORY(ret->pw_shell);
+
+ *pws = ret;
+
+ return NT_STATUS_OK;
+}
+
+
+#ifdef HAVE_SECURITY_PAM_APPL_H
+#include <security/pam_appl.h>
+
+struct smb_pam_user_info {
+ const char *account_name;
+ const char *plaintext_password;
+};
+
+#define COPY_STRING(s) (s) ? strdup(s) : NULL
+
+/*
+ * Check user password
+ * Currently it uses PAM only and fails on systems without PAM
+ * Samba3 code located in pass_check.c is to ugly to be used directly it will
+ * need major rework that's why pass_check.c is still there.
+*/
+
+static int smb_pam_conv(int num_msg, const struct pam_message **msg,
+ struct pam_response **reply, void *appdata_ptr)
+{
+ struct smb_pam_user_info *info = (struct smb_pam_user_info *)appdata_ptr;
+ int num;
+
+ if (num_msg <= 0) {
+ *reply = NULL;
+ return PAM_CONV_ERR;
+ }
+
+ /*
+ * Apparantly HPUX has a buggy PAM that doesn't support the
+ * data pointer. Fail if this is the case. JRA.
+ */
+
+ if (info == NULL) {
+ *reply = NULL;
+ return PAM_CONV_ERR;
+ }
+
+ /*
+ * PAM frees memory in reply messages by itself
+ * so use malloc instead of talloc here.
+ */
+ *reply = malloc_array_p(struct pam_response, num_msg);
+ if (*reply == NULL) {
+ return PAM_CONV_ERR;
+ }
+
+ for (num = 0; num < num_msg; num++) {
+ switch (msg[num]->msg_style) {
+ case PAM_PROMPT_ECHO_ON:
+ (*reply)[num].resp_retcode = PAM_SUCCESS;
+ (*reply)[num].resp = COPY_STRING(info->account_name);
+ break;
+
+ case PAM_PROMPT_ECHO_OFF:
+ (*reply)[num].resp_retcode = PAM_SUCCESS;
+ (*reply)[num].resp = COPY_STRING(info->plaintext_password);
+ break;
+
+ case PAM_TEXT_INFO:
+ (*reply)[num].resp_retcode = PAM_SUCCESS;
+ (*reply)[num].resp = NULL;
+ DEBUG(4,("PAM Info message in conversation function: %s\n", (msg[num]->msg)));
+ break;
+
+ case PAM_ERROR_MSG:
+ (*reply)[num].resp_retcode = PAM_SUCCESS;
+ (*reply)[num].resp = NULL;
+ DEBUG(4,("PAM Error message in conversation function: %s\n", (msg[num]->msg)));
+ break;
+
+ default:
+ while (num > 0) {
+ SAFE_FREE((*reply)[num-1].resp);
+ num--;
+ }
+ SAFE_FREE(*reply);
+ *reply = NULL;
+ DEBUG(1,("Error: PAM subsystme sent an UNKNOWN message type to the conversation function!\n"));
+ return PAM_CONV_ERR;
+ }
+ }
+
+ return PAM_SUCCESS;
+}
+
+/*
+ * Start PAM authentication for specified account
+ */
+
+static NTSTATUS smb_pam_start(pam_handle_t **pamh, const char *account_name, const char *remote_host, struct pam_conv *pconv)
+{
+ int pam_error;
+
+ if (account_name == NULL || remote_host == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG(4,("smb_pam_start: PAM: Init user: %s\n", account_name));
+
+ pam_error = pam_start("samba", account_name, pconv, pamh);
+ if (pam_error != PAM_SUCCESS) {
+ /* no valid pamh here, can we reliably call pam_strerror ? */
+ DEBUG(4,("smb_pam_start: pam_start failed!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+#ifdef PAM_RHOST
+ DEBUG(4,("smb_pam_start: PAM: setting rhost to: %s\n", remote_host));
+ pam_error = pam_set_item(*pamh, PAM_RHOST, remote_host);
+ if (pam_error != PAM_SUCCESS) {
+ NTSTATUS nt_status;
+
+ DEBUG(4,("smb_pam_start: setting rhost failed with error: %s\n",
+ pam_strerror(*pamh, pam_error)));
+ nt_status = pam_to_nt_status(pam_error);
+
+ pam_error = pam_end(*pamh, 0);
+ if (pam_error != PAM_SUCCESS) {
+ /* no vaild pamh here, can we reliably call pam_strerror ? */
+ DEBUG(4,("smb_pam_start: clean up failed, pam_end gave error %d.\n",
+ pam_error));
+ return pam_to_nt_status(pam_error);
+ }
+ return nt_status;
+ }
+#endif
+#ifdef PAM_TTY
+ DEBUG(4,("smb_pam_start: PAM: setting tty\n"));
+ pam_error = pam_set_item(*pamh, PAM_TTY, "samba");
+ if (pam_error != PAM_SUCCESS) {
+ NTSTATUS nt_status;
+
+ DEBUG(4,("smb_pam_start: setting tty failed with error: %s\n",
+ pam_strerror(*pamh, pam_error)));
+ nt_status = pam_to_nt_status(pam_error);
+
+ pam_error = pam_end(*pamh, 0);
+ if (pam_error != PAM_SUCCESS) {
+ /* no vaild pamh here, can we reliably call pam_strerror ? */
+ DEBUG(4,("smb_pam_start: clean up failed, pam_end gave error %d.\n",
+ pam_error));
+ return pam_to_nt_status(pam_error);
+ }
+ return nt_status;
+ }
+#endif
+ DEBUG(4,("smb_pam_start: PAM: Init passed for user: %s\n", account_name));
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS smb_pam_end(pam_handle_t *pamh)
+{
+ int pam_error;
+
+ if (pamh != NULL) {
+ pam_error = pam_end(pamh, 0);
+ if (pam_error != PAM_SUCCESS) {
+ /* no vaild pamh here, can we reliably call pam_strerror ? */
+ DEBUG(4,("smb_pam_end: clean up failed, pam_end gave error %d.\n",
+ pam_error));
+ return pam_to_nt_status(pam_error);
+ }
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(2,("smb_pam_end: pamh is NULL, PAM not initialized ?\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/*
+ * PAM Authentication Handler
+ */
+static NTSTATUS smb_pam_auth(pam_handle_t *pamh, bool allow_null_passwords, const char *user)
+{
+ int pam_error;
+
+ /*
+ * 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 | allow_null_passwords ? 0 : PAM_DISALLOW_NULL_AUTHTOK);
+ switch( pam_error ){
+ case PAM_AUTH_ERR:
+ DEBUG(2, ("smb_pam_auth: PAM: Authentication Error for user %s\n", user));
+ break;
+ case PAM_CRED_INSUFFICIENT:
+ DEBUG(2, ("smb_pam_auth: PAM: Insufficient Credentials for user %s\n", user));
+ break;
+ case PAM_AUTHINFO_UNAVAIL:
+ DEBUG(2, ("smb_pam_auth: PAM: Authentication Information Unavailable for user %s\n", user));
+ break;
+ case PAM_USER_UNKNOWN:
+ DEBUG(2, ("smb_pam_auth: PAM: Username %s NOT known to Authentication system\n", user));
+ break;
+ case PAM_MAXTRIES:
+ DEBUG(2, ("smb_pam_auth: PAM: One or more authentication modules reports user limit for user %s exceeeded\n", user));
+ break;
+ case PAM_ABORT:
+ DEBUG(0, ("smb_pam_auth: PAM: One or more PAM modules failed to load for user %s\n", user));
+ break;
+ case PAM_SUCCESS:
+ DEBUG(4, ("smb_pam_auth: PAM: User %s Authenticated OK\n", user));
+ break;
+ default:
+ DEBUG(0, ("smb_pam_auth: PAM: UNKNOWN ERROR while authenticating user %s\n", user));
+ break;
+ }
+
+ return pam_to_nt_status(pam_error);
+}
+
+/*
+ * PAM Account Handler
+ */
+static NTSTATUS smb_pam_account(pam_handle_t *pamh, const char * user)
+{
+ int pam_error;
+
+ 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;
+ }
+
+ return pam_to_nt_status(pam_error);
+}
+
+/*
+ * PAM Credential Setting
+ */
+
+static NTSTATUS smb_pam_setcred(pam_handle_t *pamh, const char * user)
+{
+ int pam_error;
+
+ /*
+ * 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;
+ }
+
+ return pam_to_nt_status(pam_error);
+}
+
+static NTSTATUS check_unix_password(TALLOC_CTX *ctx, struct loadparm_context *lp_ctx,
+ const struct auth_usersupplied_info *user_info, struct passwd **pws)
+{
+ struct smb_pam_user_info *info;
+ struct pam_conv *pamconv;
+ pam_handle_t *pamh;
+ NTSTATUS nt_status;
+
+ info = talloc(ctx, struct smb_pam_user_info);
+ if (info == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ info->account_name = user_info->mapped.account_name;
+ info->plaintext_password = user_info->password.plaintext;
+
+ pamconv = talloc(ctx, struct pam_conv);
+ if (pamconv == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ pamconv->conv = smb_pam_conv;
+ pamconv->appdata_ptr = (void *)info;
+
+ /* TODO:
+ * check for user_info->flags & USER_INFO_CASE_INSENSITIVE_USERNAME
+ * if true set up a crack name routine.
+ */
+
+ nt_status = smb_pam_start(&pamh, user_info->mapped.account_name, user_info->remote_host ? user_info->remote_host->addr : NULL, pamconv);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ nt_status = smb_pam_auth(pamh, lp_null_passwords(lp_ctx), user_info->mapped.account_name);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ smb_pam_end(pamh);
+ return nt_status;
+ }
+
+ if ( ! (user_info->flags & USER_INFO_DONT_CHECK_UNIX_ACCOUNT)) {
+
+ nt_status = smb_pam_account(pamh, user_info->mapped.account_name);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ smb_pam_end(pamh);
+ return nt_status;
+ }
+
+ nt_status = smb_pam_setcred(pamh, user_info->mapped.account_name);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ smb_pam_end(pamh);
+ return nt_status;
+ }
+ }
+
+ smb_pam_end(pamh);
+
+ nt_status = talloc_getpwnam(ctx, user_info->mapped.account_name, pws);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+#else
+
+/****************************************************************************
+core of password checking routine
+****************************************************************************/
+static NTSTATUS password_check(const char *username, const char *password,
+ const char *crypted, const char *salt)
+{
+ bool ret;
+
+#ifdef WITH_AFS
+ if (afs_auth(username, password))
+ return NT_STATUS_OK;
+#endif /* WITH_AFS */
+
+#ifdef WITH_DFS
+ if (dfs_auth(username, password))
+ return NT_STATUS_OK;
+#endif /* WITH_DFS */
+
+#ifdef OSF1_ENH_SEC
+
+ ret = (strcmp(osf1_bigcrypt(password, salt), crypted) == 0);
+
+ if (!ret) {
+ DEBUG(2,
+ ("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
+ ret = (strcmp((char *)crypt(password, salt), 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, salt), crypted) == 0);
+ if (ret) {
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+#endif /* ULTRIX_AUTH */
+
+#ifdef LINUX_BIGCRYPT
+ ret = (linux_bigcrypt(password, salt, 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, salt), crypted) == 0)
+ return NT_STATUS_OK;
+ else
+ ret = (strcmp((char *)crypt(password, salt), 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, salt), 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, salt), 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 */
+}
+
+static NTSTATUS check_unix_password(TALLOC_CTX *ctx, struct loadparm_context *lp_ctx,
+ const struct auth_usersupplied_info *user_info, struct passwd **ret_passwd)
+{
+ char *username;
+ char *password;
+ char *pwcopy;
+ char *salt;
+ char *crypted;
+ struct passwd *pws;
+ NTSTATUS nt_status;
+ int level = lp_passwordlevel(lp_ctx);
+
+ *ret_passwd = NULL;
+
+ username = talloc_strdup(ctx, user_info->mapped.account_name);
+ password = talloc_strdup(ctx, user_info->password.plaintext);
+
+ nt_status = talloc_getpwnam(ctx, username, &pws);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ crypted = pws->pw_passwd;
+ salt = pws->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(pws->pw_name);
+ if (spass && spass->sp_pwdp) {
+ crypted = talloc_strdup(ctx, spass->sp_pwdp);
+ NT_STATUS_HAVE_NO_MEMORY(crypted);
+ salt = talloc_strdup(ctx, spass->sp_pwdp);
+ NT_STATUS_HAVE_NO_MEMORY(salt);
+ }
+ }
+#elif defined(IA_UINFO)
+ {
+ char *ia_password;
+ /* 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(pws->pw_name, &uinfo) != -1) {
+ ia_get_logpwd(uinfo, &ia_password);
+ crypted = talloc_strdup(ctx, ia_password);
+ NT_STATUS_HAVE_NO_MEMORY(crypted);
+ }
+ }
+#endif
+
+#ifdef HAVE_GETPRPWNAM
+ {
+ struct pr_passwd *pr_pw = getprpwnam(pws->pw_name);
+ if (pr_pw && pr_pw->ufld.fd_encrypt) {
+ crypted = talloc_strdup(ctx, pr_pw->ufld.fd_encrypt);
+ NT_STATUS_HAVE_NO_MEMORY(crypted);
+ }
+ }
+#endif
+
+#ifdef HAVE_GETPWANAM
+ {
+ struct passwd_adjunct *pwret;
+ pwret = getpwanam(s);
+ if (pwret && pwret->pwa_passwd) {
+ crypted = talloc_strdup(ctx, pwret->pwa_passwd);
+ NT_STATUS_HAVE_NO_MEMORY(crypted);
+ }
+ }
+#endif
+
+#ifdef OSF1_ENH_SEC
+ {
+ struct pr_passwd *mypasswd;
+ DEBUG(5,("Checking password for user %s in OSF1_ENH_SEC\n", username));
+ mypasswd = getprpwnam(username);
+ if (mypasswd) {
+ username = talloc_strdup(ctx, mypasswd->ufld.fd_name);
+ NT_STATUS_HAVE_NO_MEMORY(username);
+ crypted = talloc_strdup(ctx, mypasswd->ufld.fd_encrypt);
+ NT_STATUS_HAVE_NO_MEMORY(crypted);
+ } else {
+ DEBUG(5,("OSF1_ENH_SEC: No entry for user %s in protected database !\n", username));
+ }
+ }
+#endif
+
+#ifdef ULTRIX_AUTH
+ {
+ AUTHORIZATION *ap = getauthuid(pws->pw_uid);
+ if (ap) {
+ crypted = talloc_strdup(ctx, ap->a_password);
+ endauthent();
+ NT_STATUS_HAVE_NO_MEMORY(crypted);
+ }
+ }
+#endif
+
+#if defined(HAVE_TRUNCATED_SALT)
+ /* crypt on some platforms (HPUX in particular)
+ won't work with more than 2 salt characters. */
+ salt[2] = 0;
+#endif
+
+ if (crypted[0] == '\0') {
+ if (!lp_null_passwords(lp_ctx)) {
+ DEBUG(2, ("Disallowing %s with null password\n", username));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+ if (password == NULL) {
+ DEBUG(3, ("Allowing access to %s with null password\n", username));
+ *ret_passwd = pws;
+ return NT_STATUS_OK;
+ }
+ }
+
+ /* try it as it came to us */
+ nt_status = password_check(username, password, crypted, salt);
+ if (NT_STATUS_IS_OK(nt_status)) {
+ *ret_passwd = pws;
+ 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 ( user_info->flags | USER_INFO_CASE_INSENSITIVE_PASSWORD) {
+ 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 */
+ pwcopy = talloc_strdup(ctx, password);
+ if (!pwcopy)
+ return NT_STATUS_NO_MEMORY;
+
+ /* try all lowercase if it's currently all uppercase */
+ if (strhasupper(pwcopy)) {
+ strlower(pwcopy);
+ nt_status = password_check(username, pwcopy, crypted, salt);
+ if NT_STATUS_IS_OK(nt_status) {
+ *ret_passwd = pws;
+ return nt_status;
+ }
+ }
+
+ /* give up? */
+ if (level < 1) {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* last chance - all combinations of up to level chars upper! */
+ strlower(pwcopy);
+
+#if 0
+ if (NT_STATUS_IS_OK(nt_status = string_combinations(pwcopy, password_check, level))) {
+ *ret_passwd = pws;
+ return nt_status;
+ }
+#endif
+ return NT_STATUS_WRONG_PASSWORD;
+}
+
+#endif
+
+/** Check a plaintext username/password
+ *
+ **/
+
+static NTSTATUS authunix_want_check(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info)
+{
+ if (!user_info->mapped.account_name || !*user_info->mapped.account_name) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS authunix_check_password(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_serversupplied_info **server_info)
+{
+ TALLOC_CTX *check_ctx;
+ NTSTATUS nt_status;
+ struct passwd *pwd;
+
+ if (user_info->password_state != AUTH_PASSWORD_PLAIN) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ check_ctx = talloc_named_const(mem_ctx, 0, "check_unix_password");
+ if (check_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ nt_status = check_unix_password(check_ctx, ctx->auth_ctx->lp_ctx, user_info, &pwd);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(check_ctx);
+ return nt_status;
+ }
+
+ nt_status = authunix_make_server_info(mem_ctx, lp_netbios_name(ctx->auth_ctx->lp_ctx),
+ user_info, pwd, server_info);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(check_ctx);
+ return nt_status;
+ }
+
+ talloc_free(check_ctx);
+ return NT_STATUS_OK;
+}
+
+static const struct auth_operations unix_ops = {
+ .name = "unix",
+ .get_challenge = auth_get_challenge_not_implemented,
+ .want_check = authunix_want_check,
+ .check_password = authunix_check_password
+};
+
+_PUBLIC_ NTSTATUS auth_unix_init(void)
+{
+ NTSTATUS ret;
+
+ ret = auth_register(&unix_ops);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register unix auth backend!\n"));
+ return ret;
+ }
+
+ return ret;
+}
diff --git a/source4/auth/ntlm/auth_util.c b/source4/auth/ntlm/auth_util.c
new file mode 100644
index 0000000000..1d86b858cf
--- /dev/null
+++ b/source4/auth/ntlm/auth_util.c
@@ -0,0 +1,260 @@
+/*
+ Unix SMB/CIFS implementation.
+ Authentication utility functions
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Andrew Bartlett 2001
+ Copyright (C) Jeremy Allison 2000-2001
+ Copyright (C) Rafal Szczesniak 2002
+ Copyright (C) Stefan Metzmacher 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/auth.h"
+#include "libcli/security/security.h"
+#include "libcli/auth/libcli_auth.h"
+#include "dsdb/samdb/samdb.h"
+#include "auth/credentials/credentials.h"
+#include "param/param.h"
+
+/* this default function can be used by mostly all backends
+ * which don't want to set a challenge
+ */
+NTSTATUS auth_get_challenge_not_implemented(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx, DATA_BLOB *challenge)
+{
+ /* we don't want to set a challenge */
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************************
+ Create an auth_usersupplied_data structure after appropriate mapping.
+****************************************************************************/
+
+NTSTATUS map_user_info(TALLOC_CTX *mem_ctx,
+ const char *default_domain,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_usersupplied_info **user_info_mapped)
+{
+ const char *domain;
+ char *account_name;
+ char *d;
+ DEBUG(5,("map_user_info: Mapping user [%s]\\[%s] from workstation [%s]\n",
+ user_info->client.domain_name, user_info->client.account_name, user_info->workstation_name));
+
+ account_name = talloc_strdup(mem_ctx, user_info->client.account_name);
+ if (!account_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* don't allow "" as a domain, fixes a Win9X bug
+ where it doesn't supply a domain for logon script
+ 'net use' commands. */
+
+ /* Split user@realm names into user and realm components. This is TODO to fix with proper userprincipalname support */
+ if (user_info->client.domain_name && *user_info->client.domain_name) {
+ domain = user_info->client.domain_name;
+ } else if (strchr_m(user_info->client.account_name, '@')) {
+ d = strchr_m(account_name, '@');
+ if (!d) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ d[0] = '\0';
+ d++;
+ domain = d;
+ } else {
+ domain = default_domain;
+ }
+
+ *user_info_mapped = talloc(mem_ctx, struct auth_usersupplied_info);
+ if (!*user_info_mapped) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ if (!talloc_reference(*user_info_mapped, user_info)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ **user_info_mapped = *user_info;
+ (*user_info_mapped)->mapped_state = true;
+ (*user_info_mapped)->mapped.domain_name = talloc_strdup(*user_info_mapped, domain);
+ (*user_info_mapped)->mapped.account_name = talloc_strdup(*user_info_mapped, account_name);
+ talloc_free(account_name);
+ if (!(*user_info_mapped)->mapped.domain_name
+ || !(*user_info_mapped)->mapped.account_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Create an auth_usersupplied_data structure after appropriate mapping.
+****************************************************************************/
+
+NTSTATUS encrypt_user_info(TALLOC_CTX *mem_ctx, struct auth_context *auth_context,
+ enum auth_password_state to_state,
+ const struct auth_usersupplied_info *user_info_in,
+ const struct auth_usersupplied_info **user_info_encrypted)
+{
+ NTSTATUS nt_status;
+ struct auth_usersupplied_info *user_info_temp;
+ switch (to_state) {
+ case AUTH_PASSWORD_RESPONSE:
+ switch (user_info_in->password_state) {
+ case AUTH_PASSWORD_PLAIN:
+ {
+ const struct auth_usersupplied_info *user_info_temp2;
+ nt_status = encrypt_user_info(mem_ctx, auth_context,
+ AUTH_PASSWORD_HASH,
+ user_info_in, &user_info_temp2);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ user_info_in = user_info_temp2;
+ /* fall through */
+ }
+ case AUTH_PASSWORD_HASH:
+ {
+ const uint8_t *challenge;
+ DATA_BLOB chall_blob;
+ user_info_temp = talloc(mem_ctx, struct auth_usersupplied_info);
+ if (!user_info_temp) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ if (!talloc_reference(user_info_temp, user_info_in)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ *user_info_temp = *user_info_in;
+ user_info_temp->mapped_state = to_state;
+
+ nt_status = auth_get_challenge(auth_context, &challenge);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ chall_blob = data_blob_talloc(mem_ctx, challenge, 8);
+ if (lp_client_ntlmv2_auth(auth_context->lp_ctx)) {
+ DATA_BLOB names_blob = NTLMv2_generate_names_blob(mem_ctx, lp_iconv_convenience(auth_context->lp_ctx), lp_netbios_name(auth_context->lp_ctx), lp_workgroup(auth_context->lp_ctx));
+ DATA_BLOB lmv2_response, ntlmv2_response, lmv2_session_key, ntlmv2_session_key;
+
+ if (!SMBNTLMv2encrypt_hash(user_info_temp,
+ user_info_in->client.account_name,
+ user_info_in->client.domain_name,
+ user_info_in->password.hash.nt->hash, &chall_blob,
+ &names_blob,
+ &lmv2_response, &ntlmv2_response,
+ &lmv2_session_key, &ntlmv2_session_key)) {
+ data_blob_free(&names_blob);
+ return NT_STATUS_NO_MEMORY;
+ }
+ data_blob_free(&names_blob);
+ user_info_temp->password.response.lanman = lmv2_response;
+ user_info_temp->password.response.nt = ntlmv2_response;
+
+ data_blob_free(&lmv2_session_key);
+ data_blob_free(&ntlmv2_session_key);
+ } else {
+ DATA_BLOB blob = data_blob_talloc(mem_ctx, NULL, 24);
+ SMBOWFencrypt(user_info_in->password.hash.nt->hash, challenge, blob.data);
+
+ user_info_temp->password.response.nt = blob;
+ if (lp_client_lanman_auth(auth_context->lp_ctx) && user_info_in->password.hash.lanman) {
+ DATA_BLOB lm_blob = data_blob_talloc(mem_ctx, NULL, 24);
+ SMBOWFencrypt(user_info_in->password.hash.lanman->hash, challenge, blob.data);
+ user_info_temp->password.response.lanman = lm_blob;
+ } else {
+ /* if not sending the LM password, send the NT password twice */
+ user_info_temp->password.response.lanman = user_info_temp->password.response.nt;
+ }
+ }
+
+ user_info_in = user_info_temp;
+ /* fall through */
+ }
+ case AUTH_PASSWORD_RESPONSE:
+ *user_info_encrypted = user_info_in;
+ }
+ break;
+ case AUTH_PASSWORD_HASH:
+ {
+ switch (user_info_in->password_state) {
+ case AUTH_PASSWORD_PLAIN:
+ {
+ struct samr_Password lanman;
+ struct samr_Password nt;
+
+ user_info_temp = talloc(mem_ctx, struct auth_usersupplied_info);
+ if (!user_info_temp) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ if (!talloc_reference(user_info_temp, user_info_in)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ *user_info_temp = *user_info_in;
+ user_info_temp->mapped_state = to_state;
+
+ if (E_deshash(user_info_in->password.plaintext, lanman.hash)) {
+ user_info_temp->password.hash.lanman = talloc(user_info_temp,
+ struct samr_Password);
+ *user_info_temp->password.hash.lanman = lanman;
+ } else {
+ user_info_temp->password.hash.lanman = NULL;
+ }
+
+ E_md4hash(user_info_in->password.plaintext, nt.hash);
+ user_info_temp->password.hash.nt = talloc(user_info_temp,
+ struct samr_Password);
+ *user_info_temp->password.hash.nt = nt;
+
+ user_info_in = user_info_temp;
+ /* fall through */
+ }
+ case AUTH_PASSWORD_HASH:
+ *user_info_encrypted = user_info_in;
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+ break;
+ }
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/**
+ * 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
+ **/
+_PUBLIC_ NTSTATUS auth_nt_status_squash(NTSTATUS nt_status)
+{
+ 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;
+ }
+
+ return nt_status;
+}
diff --git a/source4/auth/ntlm/auth_winbind.c b/source4/auth/ntlm/auth_winbind.c
new file mode 100644
index 0000000000..ac63b242e4
--- /dev/null
+++ b/source4/auth/ntlm/auth_winbind.c
@@ -0,0 +1,282 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind authentication mechnism
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Andrew Bartlett 2001 - 2002
+ Copyright (C) Stefan Metzmacher 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/auth.h"
+#include "auth/ntlm/auth_proto.h"
+#include "auth/session_proto.h"
+#include "nsswitch/winbind_client.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
+#include "librpc/gen_ndr/ndr_winbind.h"
+#include "lib/messaging/irpc.h"
+#include "param/param.h"
+
+static NTSTATUS get_info3_from_ndr(TALLOC_CTX *mem_ctx, struct smb_iconv_convenience *iconv_convenience, struct winbindd_response *response, struct netr_SamInfo3 *info3)
+{
+ size_t len = response->length - sizeof(struct winbindd_response);
+ if (len > 4) {
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ blob.length = len - 4;
+ blob.data = (uint8_t *)(((char *)response->extra_data.data) + 4);
+
+ ndr_err = ndr_pull_struct_blob(&blob, mem_ctx,
+ iconv_convenience, info3,
+ (ndr_pull_flags_fn_t)ndr_pull_netr_SamInfo3);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(2, ("get_info3_from_ndr: No info3 struct found!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+}
+
+static NTSTATUS winbind_want_check(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info)
+{
+ if (!user_info->mapped.account_name || !*user_info->mapped.account_name) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ /* TODO: maybe limit the user scope to remote users only */
+ return NT_STATUS_OK;
+}
+
+/*
+ Authenticate a user with a challenge/response
+ using the samba3 winbind protocol
+*/
+static NTSTATUS winbind_check_password_samba3(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_serversupplied_info **server_info)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ NTSTATUS nt_status;
+ struct netr_SamInfo3 info3;
+
+ /* Send off request */
+ const struct auth_usersupplied_info *user_info_temp;
+ nt_status = encrypt_user_info(mem_ctx, ctx->auth_ctx,
+ AUTH_PASSWORD_RESPONSE,
+ user_info, &user_info_temp);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ user_info = user_info_temp;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+ request.flags = WBFLAG_PAM_INFO3_NDR;
+
+ request.data.auth_crap.logon_parameters = user_info->logon_parameters;
+
+ safe_strcpy(request.data.auth_crap.user,
+ user_info->client.account_name, sizeof(fstring));
+ safe_strcpy(request.data.auth_crap.domain,
+ user_info->client.domain_name, sizeof(fstring));
+ safe_strcpy(request.data.auth_crap.workstation,
+ user_info->workstation_name, sizeof(fstring));
+
+ memcpy(request.data.auth_crap.chal, ctx->auth_ctx->challenge.data.data, sizeof(request.data.auth_crap.chal));
+
+ request.data.auth_crap.lm_resp_len = MIN(user_info->password.response.lanman.length,
+ sizeof(request.data.auth_crap.lm_resp));
+ request.data.auth_crap.nt_resp_len = MIN(user_info->password.response.nt.length,
+ sizeof(request.data.auth_crap.nt_resp));
+
+ memcpy(request.data.auth_crap.lm_resp, user_info->password.response.lanman.data,
+ request.data.auth_crap.lm_resp_len);
+ memcpy(request.data.auth_crap.nt_resp, user_info->password.response.nt.data,
+ request.data.auth_crap.nt_resp_len);
+
+ result = winbindd_request_response(WINBINDD_PAM_AUTH_CRAP, &request, &response);
+
+ nt_status = NT_STATUS(response.data.auth.nt_status);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ if (result == NSS_STATUS_SUCCESS && response.extra_data.data) {
+ union netr_Validation validation;
+
+ nt_status = get_info3_from_ndr(mem_ctx, lp_iconv_convenience(ctx->auth_ctx->lp_ctx), &response, &info3);
+ SAFE_FREE(response.extra_data.data);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ validation.sam3 = &info3;
+ nt_status = make_server_info_netlogon_validation(mem_ctx,
+ user_info->client.account_name,
+ 3, &validation,
+ server_info);
+ return nt_status;
+ } else if (result == NSS_STATUS_SUCCESS && !response.extra_data.data) {
+ DEBUG(0, ("Winbindd authenticated the user [%s]\\[%s], "
+ "but did not include the required info3 reply!\n",
+ user_info->client.domain_name, user_info->client.account_name));
+ return NT_STATUS_INSUFFICIENT_LOGON_INFO;
+ } else if (NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1, ("Winbindd authentication for [%s]\\[%s] failed, "
+ "but no error code is available!\n",
+ user_info->client.domain_name, user_info->client.account_name));
+ return NT_STATUS_NO_LOGON_SERVERS;
+ }
+
+ return nt_status;
+}
+
+struct winbind_check_password_state {
+ struct winbind_SamLogon req;
+};
+
+/*
+ Authenticate a user with a challenge/response
+ using IRPC to the winbind task
+*/
+static NTSTATUS winbind_check_password(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_serversupplied_info **server_info)
+{
+ NTSTATUS status;
+ struct server_id *winbind_servers;
+ struct winbind_check_password_state *s;
+ const struct auth_usersupplied_info *user_info_new;
+ struct netr_IdentityInfo *identity_info;
+
+ s = talloc(mem_ctx, struct winbind_check_password_state);
+ NT_STATUS_HAVE_NO_MEMORY(s);
+
+ winbind_servers = irpc_servers_byname(ctx->auth_ctx->msg_ctx, s, "winbind_server");
+ if ((winbind_servers == NULL) || (winbind_servers[0].id == 0)) {
+ DEBUG(0, ("Winbind authentication for [%s]\\[%s] failed, "
+ "no winbind_server running!\n",
+ user_info->client.domain_name, user_info->client.account_name));
+ return NT_STATUS_NO_LOGON_SERVERS;
+ }
+
+ if (user_info->flags & USER_INFO_INTERACTIVE_LOGON) {
+ struct netr_PasswordInfo *password_info;
+
+ status = encrypt_user_info(s, ctx->auth_ctx, AUTH_PASSWORD_HASH,
+ user_info, &user_info_new);
+ NT_STATUS_NOT_OK_RETURN(status);
+ user_info = user_info_new;
+
+ password_info = talloc(s, struct netr_PasswordInfo);
+ NT_STATUS_HAVE_NO_MEMORY(password_info);
+
+ password_info->lmpassword = *user_info->password.hash.lanman;
+ password_info->ntpassword = *user_info->password.hash.nt;
+
+ identity_info = &password_info->identity_info;
+ s->req.in.logon_level = 1;
+ s->req.in.logon.password= password_info;
+ } else {
+ struct netr_NetworkInfo *network_info;
+ const uint8_t *challenge;
+
+ status = encrypt_user_info(s, ctx->auth_ctx, AUTH_PASSWORD_RESPONSE,
+ user_info, &user_info_new);
+ NT_STATUS_NOT_OK_RETURN(status);
+ user_info = user_info_new;
+
+ network_info = talloc(s, struct netr_NetworkInfo);
+ NT_STATUS_HAVE_NO_MEMORY(network_info);
+
+ status = auth_get_challenge(ctx->auth_ctx, &challenge);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ memcpy(network_info->challenge, challenge, sizeof(network_info->challenge));
+
+ network_info->nt.length = user_info->password.response.nt.length;
+ network_info->nt.data = user_info->password.response.nt.data;
+
+ network_info->lm.length = user_info->password.response.lanman.length;
+ network_info->lm.data = user_info->password.response.lanman.data;
+
+ identity_info = &network_info->identity_info;
+ s->req.in.logon_level = 2;
+ s->req.in.logon.network = network_info;
+ }
+
+ identity_info->domain_name.string = user_info->client.domain_name;
+ identity_info->parameter_control = user_info->logon_parameters; /* see MSV1_0_* */
+ identity_info->logon_id_low = 0;
+ identity_info->logon_id_high = 0;
+ identity_info->account_name.string = user_info->client.account_name;
+ identity_info->workstation.string = user_info->workstation_name;
+
+ s->req.in.validation_level = 3;
+
+ status = IRPC_CALL(ctx->auth_ctx->msg_ctx, winbind_servers[0],
+ winbind, WINBIND_SAMLOGON,
+ &s->req, s);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = make_server_info_netlogon_validation(mem_ctx,
+ user_info->client.account_name,
+ s->req.in.validation_level,
+ &s->req.out.validation,
+ server_info);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+}
+
+static const struct auth_operations winbind_samba3_ops = {
+ .name = "winbind_samba3",
+ .get_challenge = auth_get_challenge_not_implemented,
+ .want_check = winbind_want_check,
+ .check_password = winbind_check_password_samba3
+};
+
+static const struct auth_operations winbind_ops = {
+ .name = "winbind",
+ .get_challenge = auth_get_challenge_not_implemented,
+ .want_check = winbind_want_check,
+ .check_password = winbind_check_password
+};
+
+_PUBLIC_ NTSTATUS auth_winbind_init(void)
+{
+ NTSTATUS ret;
+
+ ret = auth_register(&winbind_samba3_ops);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register 'winbind_samba3' auth backend!\n"));
+ return ret;
+ }
+
+ ret = auth_register(&winbind_ops);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register 'winbind' auth backend!\n"));
+ return ret;
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/auth/ntlm/config.mk b/source4/auth/ntlm/config.mk
new file mode 100644
index 0000000000..f31c2b7279
--- /dev/null
+++ b/source4/auth/ntlm/config.mk
@@ -0,0 +1,86 @@
+# NTLM auth server subsystem
+
+[SUBSYSTEM::ntlm_check]
+PRIVATE_DEPENDENCIES = LIBSAMBA-UTIL
+
+ntlm_check_OBJ_FILES = $(addprefix $(authsrcdir)/ntlm/, ntlm_check.o)
+
+#######################
+# Start MODULE auth_sam
+[MODULE::auth_sam_module]
+# gensec_krb5 and gensec_gssapi depend on it
+INIT_FUNCTION = auth_sam_init
+SUBSYSTEM = auth
+PRIVATE_DEPENDENCIES = \
+ SAMDB auth_sam ntlm_check
+# End MODULE auth_sam
+#######################
+
+auth_sam_module_OBJ_FILES = $(addprefix $(authsrcdir)/ntlm/, auth_sam.o)
+
+#######################
+# Start MODULE auth_anonymous
+[MODULE::auth_anonymous]
+INIT_FUNCTION = auth_anonymous_init
+SUBSYSTEM = auth
+# End MODULE auth_anonymous
+#######################
+
+auth_anonymous_OBJ_FILES = $(addprefix $(authsrcdir)/ntlm/, auth_anonymous.o)
+
+#######################
+# Start MODULE auth_anonymous
+[MODULE::auth_server]
+INIT_FUNCTION = auth_server_init
+SUBSYSTEM = auth
+PRIVATE_DEPENDENCIES = LIBSAMBA-UTIL LIBCLI_SMB
+OUTPUT_TYPE = SHARED_LIBRARY
+# End MODULE auth_server
+#######################
+
+auth_server_OBJ_FILES = $(addprefix $(authsrcdir)/ntlm/, auth_server.o)
+
+#######################
+# Start MODULE auth_winbind
+[MODULE::auth_winbind]
+INIT_FUNCTION = auth_winbind_init
+SUBSYSTEM = auth
+PRIVATE_DEPENDENCIES = NDR_WINBIND MESSAGING LIBWINBIND-CLIENT
+# End MODULE auth_winbind
+#######################
+
+auth_winbind_OBJ_FILES = $(addprefix $(authsrcdir)/ntlm/, auth_winbind.o)
+
+#######################
+# Start MODULE auth_developer
+[MODULE::auth_developer]
+INIT_FUNCTION = auth_developer_init
+SUBSYSTEM = auth
+# End MODULE auth_developer
+#######################
+
+auth_developer_OBJ_FILES = $(addprefix $(authsrcdir)/ntlm/, auth_developer.o)
+
+[MODULE::auth_unix]
+INIT_FUNCTION = auth_unix_init
+SUBSYSTEM = auth
+PRIVATE_DEPENDENCIES = CRYPT PAM PAM_ERRORS NSS_WRAPPER
+
+auth_unix_OBJ_FILES = $(addprefix $(authsrcdir)/ntlm/, auth_unix.o)
+
+[SUBSYSTEM::PAM_ERRORS]
+
+#VERSION = 0.0.1
+#SO_VERSION = 0
+PAM_ERRORS_OBJ_FILES = $(addprefix $(authsrcdir)/ntlm/, pam_errors.o)
+
+[MODULE::auth]
+INIT_FUNCTION = server_service_auth_init
+SUBSYSTEM = service
+PRIVATE_DEPENDENCIES = LIBSAMBA-UTIL LIBSECURITY SAMDB CREDENTIALS
+
+auth_OBJ_FILES = $(addprefix $(authsrcdir)/ntlm/, auth.o auth_util.o auth_simple.o)
+$(eval $(call proto_header_template,$(authsrcdir)/auth_proto.h,$(auth_OBJ_FILES:.o=.c)))
+
+# PUBLIC_HEADERS += auth/auth.h
+
diff --git a/source4/auth/ntlm/ntlm_check.c b/source4/auth/ntlm/ntlm_check.c
new file mode 100644
index 0000000000..0dbbce0edc
--- /dev/null
+++ b/source4/auth/ntlm/ntlm_check.c
@@ -0,0 +1,603 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2004
+ Copyright (C) Gerald Carter 2003
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/crypto/crypto.h"
+#include "librpc/gen_ndr/netlogon.h"
+#include "libcli/auth/libcli_auth.h"
+#include "param/param.h"
+#include "auth/ntlm/ntlm_check.h"
+
+/****************************************************************************
+ Core of smb password checking routine.
+****************************************************************************/
+
+static bool smb_pwd_check_ntlmv1(TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *nt_response,
+ const uint8_t *part_passwd,
+ const DATA_BLOB *sec_blob,
+ DATA_BLOB *user_sess_key)
+{
+ /* Finish the encryption of part_passwd. */
+ uint8_t p24[24];
+
+ if (part_passwd == NULL) {
+ DEBUG(10,("No password set - DISALLOWING access\n"));
+ /* No password set - always false ! */
+ return false;
+ }
+
+ if (sec_blob->length != 8) {
+ DEBUG(0, ("smb_pwd_check_ntlmv1: incorrect challenge size (%lu)\n",
+ (unsigned long)sec_blob->length));
+ return false;
+ }
+
+ if (nt_response->length != 24) {
+ DEBUG(0, ("smb_pwd_check_ntlmv1: incorrect password length (%lu)\n",
+ (unsigned long)nt_response->length));
+ return false;
+ }
+
+ SMBOWFencrypt(part_passwd, sec_blob->data, p24);
+
+#if DEBUG_PASSWORD
+ DEBUG(100,("Part password (P16) was |\n"));
+ dump_data(100, part_passwd, 16);
+ DEBUGADD(100,("Password from client was |\n"));
+ dump_data(100, nt_response->data, nt_response->length);
+ DEBUGADD(100,("Given challenge was |\n"));
+ dump_data(100, sec_blob->data, sec_blob->length);
+ DEBUGADD(100,("Value from encryption was |\n"));
+ dump_data(100, p24, 24);
+#endif
+ if (memcmp(p24, nt_response->data, 24) == 0) {
+ if (user_sess_key != NULL) {
+ *user_sess_key = data_blob_talloc(mem_ctx, NULL, 16);
+ SMBsesskeygen_ntv1(part_passwd, user_sess_key->data);
+ }
+ return true;
+ }
+ return false;
+}
+
+/****************************************************************************
+ Core of smb password checking routine. (NTLMv2, LMv2)
+ Note: The same code works with both NTLMv2 and LMv2.
+****************************************************************************/
+
+static bool smb_pwd_check_ntlmv2(TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *ntv2_response,
+ const uint8_t *part_passwd,
+ const DATA_BLOB *sec_blob,
+ const char *user, const char *domain,
+ bool upper_case_domain, /* should the domain be transformed into upper case? */
+ DATA_BLOB *user_sess_key)
+{
+ /* Finish the encryption of part_passwd. */
+ uint8_t kr[16];
+ uint8_t value_from_encryption[16];
+ DATA_BLOB client_key_data;
+
+ if (part_passwd == NULL) {
+ DEBUG(10,("No password set - DISALLOWING access\n"));
+ /* No password set - always false */
+ return false;
+ }
+
+ if (sec_blob->length != 8) {
+ DEBUG(0, ("smb_pwd_check_ntlmv2: incorrect challenge size (%lu)\n",
+ (unsigned long)sec_blob->length));
+ return false;
+ }
+
+ if (ntv2_response->length < 24) {
+ /* We MUST have more than 16 bytes, or the stuff below will go
+ crazy. No known implementation sends less than the 24 bytes
+ for LMv2, let alone NTLMv2. */
+ DEBUG(0, ("smb_pwd_check_ntlmv2: incorrect password length (%lu)\n",
+ (unsigned long)ntv2_response->length));
+ return false;
+ }
+
+ client_key_data = data_blob_talloc(mem_ctx, ntv2_response->data+16, ntv2_response->length-16);
+ /*
+ todo: should we be checking this for anything? We can't for LMv2,
+ but for NTLMv2 it is meant to contain the current time etc.
+ */
+
+ if (!ntv2_owf_gen(part_passwd, user, domain, upper_case_domain, kr)) {
+ return false;
+ }
+
+ SMBOWFencrypt_ntv2(kr, sec_blob, &client_key_data, value_from_encryption);
+
+#if DEBUG_PASSWORD
+ DEBUG(100,("Part password (P16) was |\n"));
+ dump_data(100, part_passwd, 16);
+ DEBUGADD(100,("Password from client was |\n"));
+ dump_data(100, ntv2_response->data, ntv2_response->length);
+ DEBUGADD(100,("Variable data from client was |\n"));
+ dump_data(100, client_key_data.data, client_key_data.length);
+ DEBUGADD(100,("Given challenge was |\n"));
+ dump_data(100, sec_blob->data, sec_blob->length);
+ DEBUGADD(100,("Value from encryption was |\n"));
+ dump_data(100, value_from_encryption, 16);
+#endif
+ data_blob_clear_free(&client_key_data);
+ if (memcmp(value_from_encryption, ntv2_response->data, 16) == 0) {
+ if (user_sess_key != NULL) {
+ *user_sess_key = data_blob_talloc(mem_ctx, NULL, 16);
+ SMBsesskeygen_ntv2(kr, value_from_encryption, user_sess_key->data);
+ }
+ return true;
+ }
+ return false;
+}
+
+/****************************************************************************
+ Core of smb password checking routine. (NTLMv2, LMv2)
+ Note: The same code works with both NTLMv2 and LMv2.
+****************************************************************************/
+
+static bool smb_sess_key_ntlmv2(TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *ntv2_response,
+ const uint8_t *part_passwd,
+ const DATA_BLOB *sec_blob,
+ const char *user, const char *domain,
+ bool upper_case_domain, /* should the domain be transformed into upper case? */
+ DATA_BLOB *user_sess_key)
+{
+ /* Finish the encryption of part_passwd. */
+ uint8_t kr[16];
+ uint8_t value_from_encryption[16];
+ DATA_BLOB client_key_data;
+
+ if (part_passwd == NULL) {
+ DEBUG(10,("No password set - DISALLOWING access\n"));
+ /* No password set - always false */
+ return false;
+ }
+
+ if (sec_blob->length != 8) {
+ DEBUG(0, ("smb_sess_key_ntlmv2: incorrect challenge size (%lu)\n",
+ (unsigned long)sec_blob->length));
+ return false;
+ }
+
+ if (ntv2_response->length < 24) {
+ /* We MUST have more than 16 bytes, or the stuff below will go
+ crazy. No known implementation sends less than the 24 bytes
+ for LMv2, let alone NTLMv2. */
+ DEBUG(0, ("smb_sess_key_ntlmv2: incorrect password length (%lu)\n",
+ (unsigned long)ntv2_response->length));
+ return false;
+ }
+
+ client_key_data = data_blob_talloc(mem_ctx, ntv2_response->data+16, ntv2_response->length-16);
+
+ if (!ntv2_owf_gen(part_passwd, user, domain, upper_case_domain, kr)) {
+ return false;
+ }
+
+ SMBOWFencrypt_ntv2(kr, sec_blob, &client_key_data, value_from_encryption);
+ *user_sess_key = data_blob_talloc(mem_ctx, NULL, 16);
+ SMBsesskeygen_ntv2(kr, value_from_encryption, user_sess_key->data);
+ return true;
+}
+
+/**
+ * Compare password hashes against those from the SAM
+ *
+ * @param mem_ctx talloc context
+ * @param client_lanman LANMAN password hash, as supplied by the client
+ * @param client_nt NT (MD4) password hash, as supplied by the client
+ * @param username internal Samba username, for log messages
+ * @param client_username username the client used
+ * @param client_domain domain name the client used (may be mapped)
+ * @param stored_lanman LANMAN password hash, as stored on the SAM
+ * @param stored_nt NT (MD4) password hash, as stored on the SAM
+ * @param user_sess_key User session key
+ * @param lm_sess_key LM session key (first 8 bytes of the LM hash)
+ */
+
+NTSTATUS hash_password_check(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ const struct samr_Password *client_lanman,
+ const struct samr_Password *client_nt,
+ const char *username,
+ const struct samr_Password *stored_lanman,
+ const struct samr_Password *stored_nt)
+{
+ if (stored_nt == NULL) {
+ DEBUG(3,("ntlm_password_check: NO NT password stored for user %s.\n",
+ username));
+ }
+
+ if (client_nt && stored_nt) {
+ if (memcmp(client_nt->hash, stored_nt->hash, sizeof(stored_nt->hash)) == 0) {
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(3,("ntlm_password_check: Interactive logon: NT password check failed for user %s\n",
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ } else if (client_lanman && stored_lanman) {
+ if (!lp_lanman_auth(lp_ctx)) {
+ DEBUG(3,("ntlm_password_check: Interactive logon: only LANMAN password supplied for user %s, and LM passwords are disabled!\n",
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ if (strchr_m(username, '@')) {
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ if (memcmp(client_lanman->hash, stored_lanman->hash, sizeof(stored_lanman->hash)) == 0) {
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(3,("ntlm_password_check: Interactive logon: LANMAN password check failed for user %s\n",
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ }
+ if (strchr_m(username, '@')) {
+ return NT_STATUS_NOT_FOUND;
+ }
+ return NT_STATUS_WRONG_PASSWORD;
+}
+
+/**
+ * Check a challenge-response password against the value of the NT or
+ * LM password hash.
+ *
+ * @param mem_ctx talloc context
+ * @param challenge 8-byte challenge. If all zero, forces plaintext comparison
+ * @param nt_response 'unicode' NT response to the challenge, or unicode password
+ * @param lm_response ASCII or LANMAN response to the challenge, or password in DOS code page
+ * @param username internal Samba username, for log messages
+ * @param client_username username the client used
+ * @param client_domain domain name the client used (may be mapped)
+ * @param stored_lanman LANMAN ASCII password from our passdb or similar
+ * @param stored_nt MD4 unicode password from our passdb or similar
+ * @param user_sess_key User session key
+ * @param lm_sess_key LM session key (first 8 bytes of the LM hash)
+ */
+
+NTSTATUS ntlm_password_check(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ uint32_t logon_parameters,
+ const DATA_BLOB *challenge,
+ const DATA_BLOB *lm_response,
+ const DATA_BLOB *nt_response,
+ const char *username,
+ const char *client_username,
+ const char *client_domain,
+ const struct samr_Password *stored_lanman,
+ const struct samr_Password *stored_nt,
+ DATA_BLOB *user_sess_key,
+ DATA_BLOB *lm_sess_key)
+{
+ const static uint8_t zeros[8];
+ DATA_BLOB tmp_sess_key;
+
+ if (stored_nt == NULL) {
+ DEBUG(3,("ntlm_password_check: NO NT password stored for user %s.\n",
+ username));
+ }
+
+ *lm_sess_key = data_blob(NULL, 0);
+ *user_sess_key = data_blob(NULL, 0);
+
+ /* Check for cleartext netlogon. Used by Exchange 5.5. */
+ if ((logon_parameters & MSV1_0_CLEARTEXT_PASSWORD_ALLOWED)
+ && challenge->length == sizeof(zeros)
+ && (memcmp(challenge->data, zeros, challenge->length) == 0 )) {
+ struct samr_Password client_nt;
+ struct samr_Password client_lm;
+ char *unix_pw = NULL;
+ bool lm_ok;
+
+ DEBUG(4,("ntlm_password_check: checking plaintext passwords for user %s\n",
+ username));
+ mdfour(client_nt.hash, nt_response->data, nt_response->length);
+
+ if (lm_response->length &&
+ (convert_string_talloc(mem_ctx, lp_iconv_convenience(lp_ctx), CH_DOS, CH_UNIX,
+ lm_response->data, lm_response->length,
+ (void **)&unix_pw) != -1)) {
+ if (E_deshash(unix_pw, client_lm.hash)) {
+ lm_ok = true;
+ } else {
+ lm_ok = false;
+ }
+ } else {
+ lm_ok = false;
+ }
+ return hash_password_check(mem_ctx,
+ lp_ctx,
+ lm_ok ? &client_lm : NULL,
+ nt_response->length ? &client_nt : NULL,
+ username,
+ stored_lanman, stored_nt);
+ }
+
+ if (nt_response->length != 0 && nt_response->length < 24) {
+ DEBUG(2,("ntlm_password_check: invalid NT password length (%lu) for user %s\n",
+ (unsigned long)nt_response->length, username));
+ }
+
+ if (nt_response->length > 24 && stored_nt) {
+ /* We have the NT MD4 hash challenge available - see if we can
+ use it
+ */
+ DEBUG(4,("ntlm_password_check: Checking NTLMv2 password with domain [%s]\n", client_domain));
+ if (smb_pwd_check_ntlmv2(mem_ctx,
+ nt_response,
+ stored_nt->hash, challenge,
+ client_username,
+ client_domain,
+ false,
+ user_sess_key)) {
+ *lm_sess_key = *user_sess_key;
+ if (user_sess_key->length) {
+ lm_sess_key->length = 8;
+ }
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(4,("ntlm_password_check: Checking NTLMv2 password with uppercased version of domain [%s]\n", client_domain));
+ if (smb_pwd_check_ntlmv2(mem_ctx,
+ nt_response,
+ stored_nt->hash, challenge,
+ client_username,
+ client_domain,
+ true,
+ user_sess_key)) {
+ *lm_sess_key = *user_sess_key;
+ if (user_sess_key->length) {
+ lm_sess_key->length = 8;
+ }
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(4,("ntlm_password_check: Checking NTLMv2 password without a domain\n"));
+ if (smb_pwd_check_ntlmv2(mem_ctx,
+ nt_response,
+ stored_nt->hash, challenge,
+ client_username,
+ "",
+ false,
+ user_sess_key)) {
+ *lm_sess_key = *user_sess_key;
+ if (user_sess_key->length) {
+ lm_sess_key->length = 8;
+ }
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(3,("ntlm_password_check: NTLMv2 password check failed\n"));
+ }
+ } else if (nt_response->length == 24 && stored_nt) {
+ if (lp_ntlm_auth(lp_ctx)) {
+ /* We have the NT MD4 hash challenge available - see if we can
+ use it (ie. does it exist in the smbpasswd file).
+ */
+ DEBUG(4,("ntlm_password_check: Checking NT MD4 password\n"));
+ if (smb_pwd_check_ntlmv1(mem_ctx,
+ nt_response,
+ stored_nt->hash, challenge,
+ user_sess_key)) {
+ /* The LM session key for this response is not very secure,
+ so use it only if we otherwise allow LM authentication */
+
+ if (lp_lanman_auth(lp_ctx) && stored_lanman) {
+ *lm_sess_key = data_blob_talloc(mem_ctx, stored_lanman->hash, 8);
+ }
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(3,("ntlm_password_check: NT MD4 password check failed for user %s\n",
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ } else {
+ DEBUG(2,("ntlm_password_check: NTLMv1 passwords NOT PERMITTED for user %s\n",
+ username));
+ /* no return, becouse we might pick up LMv2 in the LM field */
+ }
+ }
+
+ if (lm_response->length == 0) {
+ DEBUG(3,("ntlm_password_check: NEITHER LanMan nor NT password supplied for user %s\n",
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ if (lm_response->length < 24) {
+ DEBUG(2,("ntlm_password_check: invalid LanMan password length (%lu) for user %s\n",
+ (unsigned long)nt_response->length, username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ if (!lp_lanman_auth(lp_ctx)) {
+ DEBUG(3,("ntlm_password_check: Lanman passwords NOT PERMITTED for user %s\n",
+ username));
+ } else if (!stored_lanman) {
+ DEBUG(3,("ntlm_password_check: NO LanMan password set for user %s (and no NT password supplied)\n",
+ username));
+ } else if (strchr_m(username, '@')) {
+ DEBUG(3,("ntlm_password_check: NO LanMan password allowed for username@realm logins (user: %s)\n",
+ username));
+ } else {
+ DEBUG(4,("ntlm_password_check: Checking LM password\n"));
+ if (smb_pwd_check_ntlmv1(mem_ctx,
+ lm_response,
+ stored_lanman->hash, challenge,
+ NULL)) {
+ /* The session key for this response is still very odd.
+ It not very secure, so use it only if we otherwise
+ allow LM authentication */
+
+ if (lp_lanman_auth(lp_ctx) && stored_lanman) {
+ uint8_t first_8_lm_hash[16];
+ memcpy(first_8_lm_hash, stored_lanman->hash, 8);
+ memset(first_8_lm_hash + 8, '\0', 8);
+ *user_sess_key = data_blob_talloc(mem_ctx, first_8_lm_hash, 16);
+ *lm_sess_key = data_blob_talloc(mem_ctx, stored_lanman->hash, 8);
+ }
+ return NT_STATUS_OK;
+ }
+ }
+
+ if (!stored_nt) {
+ DEBUG(4,("ntlm_password_check: LM password check failed for user, no NT password %s\n",username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* This is for 'LMv2' authentication. almost NTLMv2 but limited to 24 bytes.
+ - related to Win9X, legacy NAS pass-though authentication
+ */
+ DEBUG(4,("ntlm_password_check: Checking LMv2 password with domain %s\n", client_domain));
+ if (smb_pwd_check_ntlmv2(mem_ctx,
+ lm_response,
+ stored_nt->hash, challenge,
+ client_username,
+ client_domain,
+ false,
+ &tmp_sess_key)) {
+ if (nt_response->length > 24) {
+ /* If NTLMv2 authentication has preceeded us
+ * (even if it failed), then use the session
+ * key from that. See the RPC-SAMLOGON
+ * torture test */
+ smb_sess_key_ntlmv2(mem_ctx,
+ nt_response,
+ stored_nt->hash, challenge,
+ client_username,
+ client_domain,
+ false,
+ user_sess_key);
+ } else {
+ /* Otherwise, use the LMv2 session key */
+ *user_sess_key = tmp_sess_key;
+ }
+ *lm_sess_key = *user_sess_key;
+ if (user_sess_key->length) {
+ lm_sess_key->length = 8;
+ }
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(4,("ntlm_password_check: Checking LMv2 password with upper-cased version of domain %s\n", client_domain));
+ if (smb_pwd_check_ntlmv2(mem_ctx,
+ lm_response,
+ stored_nt->hash, challenge,
+ client_username,
+ client_domain,
+ true,
+ &tmp_sess_key)) {
+ if (nt_response->length > 24) {
+ /* If NTLMv2 authentication has preceeded us
+ * (even if it failed), then use the session
+ * key from that. See the RPC-SAMLOGON
+ * torture test */
+ smb_sess_key_ntlmv2(mem_ctx,
+ nt_response,
+ stored_nt->hash, challenge,
+ client_username,
+ client_domain,
+ true,
+ user_sess_key);
+ } else {
+ /* Otherwise, use the LMv2 session key */
+ *user_sess_key = tmp_sess_key;
+ }
+ *lm_sess_key = *user_sess_key;
+ if (user_sess_key->length) {
+ lm_sess_key->length = 8;
+ }
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(4,("ntlm_password_check: Checking LMv2 password without a domain\n"));
+ if (smb_pwd_check_ntlmv2(mem_ctx,
+ lm_response,
+ stored_nt->hash, challenge,
+ client_username,
+ "",
+ false,
+ &tmp_sess_key)) {
+ if (nt_response->length > 24) {
+ /* If NTLMv2 authentication has preceeded us
+ * (even if it failed), then use the session
+ * key from that. See the RPC-SAMLOGON
+ * torture test */
+ smb_sess_key_ntlmv2(mem_ctx,
+ nt_response,
+ stored_nt->hash, challenge,
+ client_username,
+ "",
+ false,
+ user_sess_key);
+ } else {
+ /* Otherwise, use the LMv2 session key */
+ *user_sess_key = tmp_sess_key;
+ }
+ *lm_sess_key = *user_sess_key;
+ if (user_sess_key->length) {
+ lm_sess_key->length = 8;
+ }
+ return NT_STATUS_OK;
+ }
+
+ /* Apparently NT accepts NT responses in the LM field
+ - I think this is related to Win9X pass-though authentication
+ */
+ DEBUG(4,("ntlm_password_check: Checking NT MD4 password in LM field\n"));
+ if (lp_ntlm_auth(lp_ctx)) {
+ if (smb_pwd_check_ntlmv1(mem_ctx,
+ lm_response,
+ stored_nt->hash, challenge,
+ NULL)) {
+ /* The session key for this response is still very odd.
+ It not very secure, so use it only if we otherwise
+ allow LM authentication */
+
+ if (lp_lanman_auth(lp_ctx) && stored_lanman) {
+ uint8_t first_8_lm_hash[16];
+ memcpy(first_8_lm_hash, stored_lanman->hash, 8);
+ memset(first_8_lm_hash + 8, '\0', 8);
+ *user_sess_key = data_blob_talloc(mem_ctx, first_8_lm_hash, 16);
+ *lm_sess_key = data_blob_talloc(mem_ctx, stored_lanman->hash, 8);
+ }
+ return NT_STATUS_OK;
+ }
+ DEBUG(3,("ntlm_password_check: LM password, NT MD4 password in LM field and LMv2 failed for user %s\n",username));
+ } else {
+ DEBUG(3,("ntlm_password_check: LM password and LMv2 failed for user %s, and NT MD4 password in LM field not permitted\n",username));
+ }
+
+ /* Try and match error codes */
+ if (strchr_m(username, '@')) {
+ return NT_STATUS_NOT_FOUND;
+ }
+ return NT_STATUS_WRONG_PASSWORD;
+}
+
diff --git a/source4/auth/ntlm/ntlm_check.h b/source4/auth/ntlm/ntlm_check.h
new file mode 100644
index 0000000000..eb115b74d6
--- /dev/null
+++ b/source4/auth/ntlm/ntlm_check.h
@@ -0,0 +1,75 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2004
+ Copyright (C) Gerald Carter 2003
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+/**
+ * Compare password hashes against those from the SAM
+ *
+ * @param mem_ctx talloc context
+ * @param client_lanman LANMAN password hash, as supplied by the client
+ * @param client_nt NT (MD4) password hash, as supplied by the client
+ * @param username internal Samba username, for log messages
+ * @param client_username username the client used
+ * @param client_domain domain name the client used (may be mapped)
+ * @param stored_lanman LANMAN password hash, as stored on the SAM
+ * @param stored_nt NT (MD4) password hash, as stored on the SAM
+ * @param user_sess_key User session key
+ * @param lm_sess_key LM session key (first 8 bytes of the LM hash)
+ */
+
+NTSTATUS hash_password_check(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ const struct samr_Password *client_lanman,
+ const struct samr_Password *client_nt,
+ const char *username,
+ const struct samr_Password *stored_lanman,
+ const struct samr_Password *stored_nt);
+
+/**
+ * Check a challenge-response password against the value of the NT or
+ * LM password hash.
+ *
+ * @param mem_ctx talloc context
+ * @param challenge 8-byte challenge. If all zero, forces plaintext comparison
+ * @param nt_response 'unicode' NT response to the challenge, or unicode password
+ * @param lm_response ASCII or LANMAN response to the challenge, or password in DOS code page
+ * @param username internal Samba username, for log messages
+ * @param client_username username the client used
+ * @param client_domain domain name the client used (may be mapped)
+ * @param stored_lanman LANMAN ASCII password from our passdb or similar
+ * @param stored_nt MD4 unicode password from our passdb or similar
+ * @param user_sess_key User session key
+ * @param lm_sess_key LM session key (first 8 bytes of the LM hash)
+ */
+
+NTSTATUS ntlm_password_check(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ uint32_t logon_parameters,
+ const DATA_BLOB *challenge,
+ const DATA_BLOB *lm_response,
+ const DATA_BLOB *nt_response,
+ const char *username,
+ const char *client_username,
+ const char *client_domain,
+ const struct samr_Password *stored_lanman,
+ const struct samr_Password *stored_nt,
+ DATA_BLOB *user_sess_key,
+ DATA_BLOB *lm_sess_key);
diff --git a/source4/auth/ntlm/pam_errors.c b/source4/auth/ntlm/pam_errors.c
new file mode 100644
index 0000000000..9774ad8727
--- /dev/null
+++ b/source4/auth/ntlm/pam_errors.c
@@ -0,0 +1,125 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * PAM error mapping functions
+ * Copyright (C) Andrew Bartlett 2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+
+#ifdef WITH_HAVE_SECURITY_PAM_APPL_H
+#include <security/pam_appl.h>
+
+#if defined(PAM_AUTHTOK_RECOVERY_ERR) && !defined(PAM_AUTHTOK_RECOVER_ERR)
+#define PAM_AUTHTOK_RECOVER_ERR PAM_AUTHTOK_RECOVERY_ERR
+#endif
+
+/* PAM -> NT_STATUS map */
+static const struct {
+ int pam_code;
+ NTSTATUS ntstatus;
+} pam_to_nt_status_map[] = {
+ {PAM_OPEN_ERR, NT_STATUS_UNSUCCESSFUL},
+ {PAM_SYMBOL_ERR, NT_STATUS_UNSUCCESSFUL},
+ {PAM_SERVICE_ERR, NT_STATUS_UNSUCCESSFUL},
+ {PAM_SYSTEM_ERR, NT_STATUS_UNSUCCESSFUL},
+ {PAM_BUF_ERR, NT_STATUS_UNSUCCESSFUL},
+ {PAM_PERM_DENIED, NT_STATUS_ACCESS_DENIED},
+ {PAM_AUTH_ERR, NT_STATUS_WRONG_PASSWORD},
+ {PAM_CRED_INSUFFICIENT, NT_STATUS_INSUFFICIENT_LOGON_INFO}, /* FIXME: Is this correct? */
+ {PAM_AUTHINFO_UNAVAIL, NT_STATUS_LOGON_FAILURE},
+ {PAM_USER_UNKNOWN, NT_STATUS_NO_SUCH_USER},
+ {PAM_MAXTRIES, NT_STATUS_REMOTE_SESSION_LIMIT}, /* FIXME: Is this correct? */
+ {PAM_NEW_AUTHTOK_REQD, NT_STATUS_PASSWORD_MUST_CHANGE},
+ {PAM_ACCT_EXPIRED, NT_STATUS_ACCOUNT_EXPIRED},
+ {PAM_SESSION_ERR, NT_STATUS_INSUFFICIENT_RESOURCES},
+ {PAM_CRED_UNAVAIL, NT_STATUS_NO_TOKEN}, /* FIXME: Is this correct? */
+ {PAM_CRED_EXPIRED, NT_STATUS_PASSWORD_EXPIRED}, /* FIXME: Is this correct? */
+ {PAM_CRED_ERR, NT_STATUS_UNSUCCESSFUL},
+ {PAM_AUTHTOK_ERR, NT_STATUS_UNSUCCESSFUL},
+#ifdef PAM_AUTHTOK_RECOVER_ERR
+ {PAM_AUTHTOK_RECOVER_ERR, NT_STATUS_UNSUCCESSFUL},
+#endif
+ {PAM_AUTHTOK_EXPIRED, NT_STATUS_PASSWORD_EXPIRED},
+ {PAM_SUCCESS, NT_STATUS_OK}
+};
+
+/* NT_STATUS -> PAM map */
+static const struct {
+ NTSTATUS ntstatus;
+ int pam_code;
+} nt_status_to_pam_map[] = {
+ {NT_STATUS_UNSUCCESSFUL, PAM_SYSTEM_ERR},
+ {NT_STATUS_NO_SUCH_USER, PAM_USER_UNKNOWN},
+ {NT_STATUS_WRONG_PASSWORD, PAM_AUTH_ERR},
+ {NT_STATUS_LOGON_FAILURE, PAM_AUTH_ERR},
+ {NT_STATUS_ACCOUNT_EXPIRED, PAM_ACCT_EXPIRED},
+ {NT_STATUS_PASSWORD_EXPIRED, PAM_AUTHTOK_EXPIRED},
+ {NT_STATUS_PASSWORD_MUST_CHANGE, PAM_NEW_AUTHTOK_REQD},
+ {NT_STATUS_OK, PAM_SUCCESS}
+};
+
+/*****************************************************************************
+convert a PAM error to a NT status32 code
+ *****************************************************************************/
+NTSTATUS pam_to_nt_status(int pam_error)
+{
+ int i;
+ if (pam_error == 0) return NT_STATUS_OK;
+
+ for (i=0; NT_STATUS_V(pam_to_nt_status_map[i].ntstatus); i++) {
+ if (pam_error == pam_to_nt_status_map[i].pam_code)
+ return pam_to_nt_status_map[i].ntstatus;
+ }
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/*****************************************************************************
+convert an NT status32 code to a PAM error
+ *****************************************************************************/
+int nt_status_to_pam(NTSTATUS nt_status)
+{
+ int i;
+ if NT_STATUS_IS_OK(nt_status) return PAM_SUCCESS;
+
+ for (i=0; NT_STATUS_V(nt_status_to_pam_map[i].ntstatus); i++) {
+ if (NT_STATUS_EQUAL(nt_status,nt_status_to_pam_map[i].ntstatus))
+ return nt_status_to_pam_map[i].pam_code;
+ }
+ return PAM_SYSTEM_ERR;
+}
+
+#else
+
+/*****************************************************************************
+convert a PAM error to a NT status32 code
+ *****************************************************************************/
+NTSTATUS pam_to_nt_status(int pam_error)
+{
+ if (pam_error == 0) return NT_STATUS_OK;
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/*****************************************************************************
+convert an NT status32 code to a PAM error
+ *****************************************************************************/
+int nt_status_to_pam(NTSTATUS nt_status)
+{
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_OK)) return 0;
+ return 4; /* PAM_SYSTEM_ERR */
+}
+
+#endif
+
diff --git a/source4/auth/ntlm/pam_errors.h b/source4/auth/ntlm/pam_errors.h
new file mode 100644
index 0000000000..959e1f3517
--- /dev/null
+++ b/source4/auth/ntlm/pam_errors.h
@@ -0,0 +1,47 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * PAM error mapping functions
+ * Copyright (C) Andrew Bartlett 2002
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __AUTH_NTLM_PAM_ERRORS_H__
+#define __AUTH_NTLM_PAM_ERRORS_H__
+
+/* The following definitions come from auth/pam_errors.c */
+
+
+/*****************************************************************************
+convert a PAM error to a NT status32 code
+ *****************************************************************************/
+NTSTATUS pam_to_nt_status(int pam_error);
+
+/*****************************************************************************
+convert an NT status32 code to a PAM error
+ *****************************************************************************/
+int nt_status_to_pam(NTSTATUS nt_status);
+
+/*****************************************************************************
+convert a PAM error to a NT status32 code
+ *****************************************************************************/
+NTSTATUS pam_to_nt_status(int pam_error);
+
+/*****************************************************************************
+convert an NT status32 code to a PAM error
+ *****************************************************************************/
+int nt_status_to_pam(NTSTATUS nt_status);
+
+#endif /* __AUTH_NTLM_PAM_ERRORS_H__ */
+