summaryrefslogtreecommitdiffstats
path: root/source4/auth/ntlm
diff options
context:
space:
mode:
Diffstat (limited to 'source4/auth/ntlm')
-rw-r--r--source4/auth/ntlm/auth.c847
-rw-r--r--source4/auth/ntlm/auth_anonymous.c161
-rw-r--r--source4/auth/ntlm/auth_developer.c216
-rw-r--r--source4/auth/ntlm/auth_sam.c1210
-rw-r--r--source4/auth/ntlm/auth_server_service.c29
-rw-r--r--source4/auth/ntlm/auth_simple.c217
-rw-r--r--source4/auth/ntlm/auth_util.c183
-rw-r--r--source4/auth/ntlm/auth_winbind.c322
-rw-r--r--source4/auth/ntlm/wscript_build51
9 files changed, 3236 insertions, 0 deletions
diff --git a/source4/auth/ntlm/auth.c b/source4/auth/ntlm/auth.c
new file mode 100644
index 0000000..09d660a
--- /dev/null
+++ b/source4/auth/ntlm/auth.c
@@ -0,0 +1,847 @@
+/*
+ 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 <tevent.h>
+#include "../lib/util/tevent_ntstatus.h"
+#include "../lib/util/dlinklist.h"
+#include "auth/auth.h"
+#include "auth/ntlm/auth_proto.h"
+#include "param/param.h"
+#include "dsdb/samdb/samdb.h"
+#include "libcli/wbclient/wbclient.h"
+#include "lib/util/samba_modules.h"
+#include "auth/credentials/credentials.h"
+#include "system/kerberos.h"
+#include "auth/kerberos/kerberos.h"
+#include "auth/kerberos/kerberos_util.h"
+#include "libds/common/roles.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+static NTSTATUS auth_generate_session_info_wrapper(struct auth4_context *auth_context,
+ TALLOC_CTX *mem_ctx,
+ void *server_returned_info,
+ const char *original_user_name,
+ uint32_t session_info_flags,
+ struct auth_session_info **session_info);
+
+/***************************************************************************
+ Set a fixed challenge
+***************************************************************************/
+_PUBLIC_ NTSTATUS auth_context_set_challenge(struct auth4_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;
+}
+
+/****************************************************************************
+ 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 auth4_context *auth_ctx, uint8_t chal[8])
+{
+
+ if (auth_ctx->challenge.data.length == 8) {
+ DEBUG(5, ("auth_get_challenge: returning previous challenge by module %s (normal)\n",
+ auth_ctx->challenge.set_by));
+ memcpy(chal, auth_ctx->challenge.data.data, 8);
+ return NT_STATUS_OK;
+ }
+
+ if (!auth_ctx->challenge.set_by) {
+ 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";
+ }
+
+ DEBUG(10,("auth_get_challenge: challenge set by %s\n",
+ auth_ctx->challenge.set_by));
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * 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 user_info_dc struct.
+ *
+ * The return value takes precedence over the contents of the user_info_dc
+ * 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 user_info_dc structure
+ *
+ * @param user_info_dc 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 auth4_context *auth_ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_user_info_dc **user_info_dc,
+ uint8_t *pauthoritative)
+{
+ struct tevent_req *subreq;
+ struct tevent_context *ev;
+ bool ok;
+ NTSTATUS status;
+
+ /*TODO: create a new event context here! */
+ ev = auth_ctx->event_ctx;
+
+ /*
+ * We are authoritative by default
+ */
+ *pauthoritative = 1;
+
+ subreq = auth_check_password_send(mem_ctx,
+ ev,
+ auth_ctx,
+ user_info);
+ if (subreq == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ok = tevent_req_poll(subreq, ev);
+ if (!ok) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ status = auth_check_password_recv(subreq, mem_ctx,
+ user_info_dc, pauthoritative);
+ TALLOC_FREE(subreq);
+
+ return status;
+}
+
+struct auth_check_password_state {
+ struct tevent_context *ev;
+ struct auth4_context *auth_ctx;
+ const struct auth_usersupplied_info *user_info;
+ struct auth_user_info_dc *user_info_dc;
+ struct auth_method_context *method;
+ uint8_t authoritative;
+};
+
+static void auth_check_password_next(struct tevent_req *req);
+
+/**
+ * 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 user_info_dc struct.
+ *
+ * The return value takes precedence over the contents of the user_info_dc
+ * struct. When the return is other than NT_STATUS_OK the contents
+ * of that structure is undefined.
+ *
+ * @param mem_ctx The memory context the request should operate on
+ *
+ * @param ev The tevent context the request should operate on
+ *
+ * @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 generation function
+ * auth_get_challenge().
+ *
+ * @param user_info Contains the user supplied components, including the passwords.
+ *
+ * @return The request handle or NULL on no memory error.
+ *
+ **/
+
+_PUBLIC_ struct tevent_req *auth_check_password_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct auth4_context *auth_ctx,
+ const struct auth_usersupplied_info *user_info)
+{
+ struct tevent_req *req;
+ struct auth_check_password_state *state;
+ /* if all the modules say 'not for me' this is reasonable */
+ NTSTATUS nt_status;
+ uint8_t chal[8];
+
+ 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 = tevent_req_create(mem_ctx, &state,
+ struct auth_check_password_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ /*
+ * We are authoritative by default.
+ */
+ state->ev = ev;
+ state->auth_ctx = auth_ctx;
+ state->user_info = user_info;
+ state->authoritative = 1;
+
+ if (user_info->mapped.account_name == NULL) {
+ struct auth_usersupplied_info *user_info_tmp;
+
+ /*
+ * We don't really do any mapping here.
+ *
+ * It's up to the backends to do mappings
+ * for their authentication.
+ */
+ user_info_tmp = talloc_zero(state, struct auth_usersupplied_info);
+ if (tevent_req_nomem(user_info_tmp, req)) {
+ return tevent_req_post(req, ev);;
+ }
+
+ /*
+ * The lifetime of user_info is longer than
+ * user_info_tmp, so we don't need to copy the
+ * strings.
+ */
+ *user_info_tmp = *user_info;
+ user_info_tmp->mapped.domain_name = user_info->client.domain_name;
+ user_info_tmp->mapped.account_name = user_info->client.account_name;
+
+ user_info = user_info_tmp;
+ state->user_info = user_info_tmp;
+ }
+
+ DEBUGADD(3,("auth_check_password_send: "
+ "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, chal);
+ if (tevent_req_nterror(req, 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)));
+ return tevent_req_post(req, ev);
+ }
+
+ 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);
+
+ state->method = state->auth_ctx->methods;
+ auth_check_password_next(req);
+ if (!tevent_req_is_in_progress(req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void auth_check_password_done(struct tevent_req *subreq);
+
+static void auth_check_password_next(struct tevent_req *req)
+{
+ struct auth_check_password_state *state =
+ tevent_req_data(req, struct auth_check_password_state);
+ struct tevent_req *subreq = NULL;
+ NTSTATUS status;
+
+ if (state->method == NULL) {
+ state->authoritative = 0;
+ tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER);
+ return;
+ }
+
+ /* check if the module wants to check the password */
+ status = state->method->ops->want_check(state->method, state,
+ state->user_info);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
+ DEBUG(11,("auth_check_password_send: "
+ "%s doesn't want to check\n",
+ state->method->ops->name));
+ state->method = state->method->next;
+ auth_check_password_next(req);
+ return;
+ }
+
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ subreq = state->method->ops->check_password_send(
+ state, state->ev, state->method, state->user_info);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, auth_check_password_done, req);
+}
+
+static void auth_check_password_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct auth_check_password_state *state =
+ tevent_req_data(req,
+ struct auth_check_password_state);
+ bool authoritative = true;
+ NTSTATUS status;
+
+ status = state->method->ops->check_password_recv(subreq, state,
+ &state->user_info_dc,
+ &authoritative);
+ TALLOC_FREE(subreq);
+ if (!authoritative ||
+ NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
+ DEBUG(11,("auth_check_password_send: "
+ "%s passes to the next method\n",
+ state->method->ops->name));
+ state->method = state->method->next;
+ auth_check_password_next(req);
+ return;
+ }
+
+ /* the backend has handled the request */
+
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+/**
+ * Check a user's Plaintext, LM or NTLM password.
+ * async receive function
+ *
+ * The return value takes precedence over the contents of the user_info_dc
+ * struct. When the return is other than NT_STATUS_OK the contents
+ * of that structure is undefined.
+ *
+ *
+ * @param req The async request state
+ *
+ * @param mem_ctx The parent memory context for the user_info_dc structure
+ *
+ * @param user_info_dc 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 tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct auth_user_info_dc **user_info_dc,
+ uint8_t *pauthoritative)
+{
+ struct auth_check_password_state *state =
+ tevent_req_data(req, struct auth_check_password_state);
+ NTSTATUS status = NT_STATUS_OK;
+
+ *pauthoritative = state->authoritative;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ /*
+ * Please try not to change this string, it is probably in use
+ * in audit logging tools
+ */
+ DEBUG(2,("auth_check_password_recv: "
+ "%s authentication for user [%s\\%s] "
+ "FAILED with error %s, authoritative=%u\n",
+ (state->method ? state->method->ops->name : "NO_METHOD"),
+ state->user_info->mapped.domain_name,
+ state->user_info->mapped.account_name,
+ nt_errstr(status), state->authoritative));
+
+ log_authentication_event(state->auth_ctx->msg_ctx,
+ state->auth_ctx->lp_ctx,
+ &state->auth_ctx->start_time,
+ state->user_info, status,
+ NULL, NULL, NULL);
+ tevent_req_received(req);
+ return status;
+ }
+
+ DEBUG(5,("auth_check_password_recv: "
+ "%s authentication for user [%s\\%s] succeeded\n",
+ state->method->ops->name,
+ state->user_info_dc->info->domain_name,
+ state->user_info_dc->info->account_name));
+
+ log_authentication_event(state->auth_ctx->msg_ctx,
+ state->auth_ctx->lp_ctx,
+ &state->auth_ctx->start_time,
+ state->user_info, status,
+ state->user_info_dc->info->domain_name,
+ state->user_info_dc->info->account_name,
+ &state->user_info_dc->sids[0]);
+
+ *user_info_dc = talloc_move(mem_ctx, &state->user_info_dc);
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+struct auth_check_password_wrapper_state {
+ uint8_t authoritative;
+ struct auth_user_info_dc *user_info_dc;
+};
+
+static void auth_check_password_wrapper_done(struct tevent_req *subreq);
+
+static struct tevent_req *auth_check_password_wrapper_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct auth4_context *auth_ctx,
+ const struct auth_usersupplied_info *user_info)
+{
+ struct tevent_req *req = NULL;
+ struct auth_check_password_wrapper *state = NULL;
+ struct tevent_req *subreq = NULL;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct auth_check_password_wrapper_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ subreq = auth_check_password_send(state, ev, auth_ctx, user_info);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq,
+ auth_check_password_wrapper_done,
+ req);
+
+ return req;
+}
+
+static void auth_check_password_wrapper_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct auth_check_password_wrapper_state *state =
+ tevent_req_data(req,
+ struct auth_check_password_wrapper_state);
+ NTSTATUS status;
+
+ status = auth_check_password_recv(subreq, state,
+ &state->user_info_dc,
+ &state->authoritative);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static NTSTATUS auth_check_password_wrapper_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint8_t *pauthoritative,
+ void **server_returned_info,
+ DATA_BLOB *user_session_key,
+ DATA_BLOB *lm_session_key)
+{
+ struct auth_check_password_wrapper_state *state =
+ tevent_req_data(req,
+ struct auth_check_password_wrapper_state);
+ struct auth_user_info_dc *user_info_dc = state->user_info_dc;
+ NTSTATUS status = NT_STATUS_OK;
+
+ *pauthoritative = state->authoritative;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ talloc_steal(mem_ctx, user_info_dc);
+ *server_returned_info = user_info_dc;
+
+ if (user_session_key) {
+ DEBUG(10, ("Got NT session key of length %u\n",
+ (unsigned)user_info_dc->user_session_key.length));
+ *user_session_key = user_info_dc->user_session_key;
+ talloc_steal(mem_ctx, user_session_key->data);
+ user_info_dc->user_session_key = data_blob_null;
+ }
+
+ if (lm_session_key) {
+ DEBUG(10, ("Got LM session key of length %u\n",
+ (unsigned)user_info_dc->lm_session_key.length));
+ *lm_session_key = user_info_dc->lm_session_key;
+ talloc_steal(mem_ctx, lm_session_key->data);
+ user_info_dc->lm_session_key = data_blob_null;
+ }
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+ /* Wrapper because we don't want to expose all callers to needing to
+ * know that session_info is generated from the main ldb, and because
+ * we need to break a depenency loop between the DCE/RPC layer and the
+ * generation of unix tokens via IRPC */
+static NTSTATUS auth_generate_session_info_wrapper(struct auth4_context *auth_context,
+ TALLOC_CTX *mem_ctx,
+ void *server_returned_info,
+ const char *original_user_name,
+ uint32_t session_info_flags,
+ struct auth_session_info **session_info)
+{
+ NTSTATUS status;
+ struct auth_user_info_dc *user_info_dc = talloc_get_type_abort(server_returned_info, struct auth_user_info_dc);
+
+ if (user_info_dc->info->authenticated) {
+ session_info_flags |= AUTH_SESSION_INFO_AUTHENTICATED;
+ }
+
+ status = auth_generate_session_info(mem_ctx, auth_context->lp_ctx,
+ auth_context->sam_ctx, user_info_dc,
+ session_info_flags, session_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if ((session_info_flags & AUTH_SESSION_INFO_UNIX_TOKEN)
+ && NT_STATUS_IS_OK(status)) {
+ status = auth_session_info_fill_unix(auth_context->lp_ctx,
+ original_user_name,
+ *session_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(*session_info);
+ }
+ }
+ return status;
+}
+
+/* Wrapper because we don't want to expose all callers to needing to
+ * know anything about the PAC or auth subsystem internal structures
+ * before we output a struct auth session_info */
+static NTSTATUS auth_generate_session_info_pac(struct auth4_context *auth_ctx,
+ TALLOC_CTX *mem_ctx,
+ struct smb_krb5_context *smb_krb5_context,
+ DATA_BLOB *pac_blob,
+ const char *principal_name,
+ const struct tsocket_address *remote_address,
+ uint32_t session_info_flags,
+ struct auth_session_info **session_info)
+{
+ NTSTATUS status;
+ struct auth_user_info_dc *user_info_dc;
+ TALLOC_CTX *tmp_ctx;
+
+ if (!pac_blob) {
+ /*
+ * This should already be catched at the main
+ * gensec layer, but better check twice
+ */
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ tmp_ctx = talloc_named(mem_ctx, 0, "gensec_gssapi_session_info context");
+ NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
+
+ status = kerberos_pac_blob_to_user_info_dc(tmp_ctx,
+ *pac_blob,
+ smb_krb5_context->krb5_context,
+ &user_info_dc, NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ if (user_info_dc->info->authenticated) {
+ session_info_flags |= AUTH_SESSION_INFO_AUTHENTICATED;
+ }
+
+ status = auth_generate_session_info_wrapper(auth_ctx, mem_ctx,
+ user_info_dc,
+ user_info_dc->info->account_name,
+ session_info_flags, session_info);
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+/***************************************************************************
+ Make a auth_info struct for the auth subsystem
+ - Allow the caller to specify the methods to use, including optionally the SAM to use
+***************************************************************************/
+_PUBLIC_ NTSTATUS auth_context_create_methods(TALLOC_CTX *mem_ctx, const char * const *methods,
+ struct tevent_context *ev,
+ struct imessaging_context *msg,
+ struct loadparm_context *lp_ctx,
+ struct ldb_context *sam_ctx,
+ struct auth4_context **auth_ctx)
+{
+ int i;
+ struct auth4_context *ctx;
+
+ auth4_init();
+
+ if (!ev) {
+ DEBUG(0,("auth_context_create: called with out event context\n"));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ ctx = talloc_zero(mem_ctx, struct auth4_context);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+ ctx->challenge.data = data_blob(NULL, 0);
+ ctx->methods = NULL;
+ ctx->event_ctx = ev;
+ ctx->msg_ctx = msg;
+ ctx->lp_ctx = lp_ctx;
+ ctx->start_time = timeval_current();
+
+ if (sam_ctx) {
+ ctx->sam_ctx = sam_ctx;
+ } else {
+ ctx->sam_ctx = samdb_connect(ctx,
+ ctx->event_ctx,
+ ctx->lp_ctx,
+ system_session(ctx->lp_ctx),
+ NULL,
+ 0);
+ }
+
+ for (i=0; methods && 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);
+ }
+
+ ctx->check_ntlm_password_send = auth_check_password_wrapper_send;
+ ctx->check_ntlm_password_recv = auth_check_password_wrapper_recv;
+ ctx->get_ntlm_challenge = auth_get_challenge;
+ ctx->set_ntlm_challenge = auth_context_set_challenge;
+ ctx->generate_session_info = auth_generate_session_info_wrapper;
+ ctx->generate_session_info_pac = auth_generate_session_info_pac;
+
+ *auth_ctx = ctx;
+
+ return NT_STATUS_OK;
+}
+
+const char **auth_methods_from_lp(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx)
+{
+ char **auth_methods = NULL;
+
+ switch (lpcfg_server_role(lp_ctx)) {
+ case ROLE_STANDALONE:
+ auth_methods = str_list_make(mem_ctx, "anonymous sam_ignoredomain", NULL);
+ break;
+ case ROLE_DOMAIN_MEMBER:
+ case ROLE_DOMAIN_BDC:
+ case ROLE_DOMAIN_PDC:
+ case ROLE_ACTIVE_DIRECTORY_DC:
+ case ROLE_IPA_DC:
+ auth_methods = str_list_make(mem_ctx, "anonymous sam winbind sam_ignoredomain", NULL);
+ break;
+ }
+ return discard_const_p(const char *, auth_methods);
+}
+
+/***************************************************************************
+ 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 tevent_context *ev,
+ struct imessaging_context *msg,
+ struct loadparm_context *lp_ctx,
+ struct auth4_context **auth_ctx)
+{
+ NTSTATUS status;
+ const char **auth_methods;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ auth_methods = auth_methods_from_lp(tmp_ctx, lp_ctx);
+ if (!auth_methods) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ status = auth_context_create_methods(mem_ctx, auth_methods, ev, msg, lp_ctx, NULL, auth_ctx);
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+_PUBLIC_ NTSTATUS auth_context_create_for_netlogon(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct imessaging_context *msg,
+ struct loadparm_context *lp_ctx,
+ struct auth4_context **auth_ctx)
+{
+ NTSTATUS status;
+ char **_auth_methods = NULL;
+ const char **auth_methods = NULL;
+
+ /*
+ * Here we only allow 'sam winbind' instead of
+ * the 'anonymous sam winbind sam_ignoredomain'
+ * we typically use for authentication from clients.
+ */
+ _auth_methods = str_list_make(mem_ctx, "sam winbind", NULL);
+ if (_auth_methods == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ auth_methods = discard_const_p(const char *, _auth_methods);
+
+ status = auth_context_create_methods(mem_ctx, auth_methods, ev, msg,
+ lp_ctx, NULL, auth_ctx);
+ talloc_free(_auth_methods);
+ return status;
+}
+
+/* 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(TALLOC_CTX *mem_ctx,
+ 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(mem_ctx, backends,
+ struct auth_backend, num_backends+1);
+ NT_STATUS_HAVE_NO_MEMORY(backends);
+
+ new_ops = (struct auth_operations *)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 = {
+ AUTH4_INTERFACE_VERSION,
+ sizeof(struct auth_operations),
+ sizeof(struct auth_method_context),
+ sizeof(struct auth4_context),
+ sizeof(struct auth_usersupplied_info),
+ sizeof(struct auth_user_info_dc)
+ };
+
+ return &critical_sizes;
+}
+
+_PUBLIC_ NTSTATUS auth4_init(void)
+{
+ static bool initialized = false;
+#define _MODULE_PROTO(init) extern NTSTATUS init(TALLOC_CTX *);
+ STATIC_auth4_MODULES_PROTO;
+ init_module_fn static_init[] = { STATIC_auth4_MODULES };
+
+ if (initialized) return NT_STATUS_OK;
+ initialized = true;
+
+ run_init_functions(NULL, static_init);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/auth/ntlm/auth_anonymous.c b/source4/auth/ntlm/auth_anonymous.c
new file mode 100644
index 0000000..a25aaca
--- /dev/null
+++ b/source4/auth/ntlm/auth_anonymous.c
@@ -0,0 +1,161 @@
+/*
+ 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 <tevent.h>
+#include "auth/auth.h"
+#include "auth/ntlm/auth_proto.h"
+#include "param/param.h"
+#include "lib/util/tevent_ntstatus.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+_PUBLIC_ NTSTATUS auth4_anonymous_init(TALLOC_CTX *);
+
+/**
+ * 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;
+ }
+
+ switch (user_info->password_state) {
+ case AUTH_PASSWORD_PLAIN:
+ if (user_info->password.plaintext != NULL &&
+ strlen(user_info->password.plaintext) > 0)
+ {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ break;
+ case AUTH_PASSWORD_HASH:
+ if (user_info->password.hash.lanman != NULL) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ if (user_info->password.hash.nt != NULL) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ break;
+ case AUTH_PASSWORD_RESPONSE:
+ if (user_info->password.response.lanman.length == 1) {
+ if (user_info->password.response.lanman.data[0] != '\0') {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ } else if (user_info->password.response.lanman.length > 1) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ if (user_info->password.response.nt.length > 0) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ break;
+ }
+
+ 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.
+ **/
+
+struct anonymous_check_password_state {
+ struct auth_user_info_dc *user_info_dc;
+};
+
+static struct tevent_req *anonymous_check_password_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct auth_method_context *ctx,
+ const struct auth_usersupplied_info *user_info)
+{
+ struct tevent_req *req = NULL;
+ struct anonymous_check_password_state *state = NULL;
+ NTSTATUS status;
+
+ req = tevent_req_create(
+ mem_ctx,
+ &state,
+ struct anonymous_check_password_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ status = auth_anonymous_user_info_dc(
+ state,
+ lpcfg_netbios_name(ctx->auth_ctx->lp_ctx),
+ &state->user_info_dc);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+}
+
+static NTSTATUS anonymous_check_password_recv(
+ struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct auth_user_info_dc **interim_info,
+ bool *authoritative)
+{
+ struct anonymous_check_password_state *state = tevent_req_data(
+ req, struct anonymous_check_password_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+ *interim_info = talloc_move(mem_ctx, &state->user_info_dc);
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+
+static const struct auth_operations anonymous_auth_ops = {
+ .name = "anonymous",
+ .want_check = anonymous_want_check,
+ .check_password_send = anonymous_check_password_send,
+ .check_password_recv = anonymous_check_password_recv,
+};
+
+_PUBLIC_ NTSTATUS auth4_anonymous_init(TALLOC_CTX *ctx)
+{
+ NTSTATUS ret;
+
+ ret = auth_register(ctx, &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 0000000..6e92252
--- /dev/null
+++ b/source4/auth/ntlm/auth_developer.c
@@ -0,0 +1,216 @@
+/*
+ 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 <tevent.h>
+#include "auth/auth.h"
+#include "auth/ntlm/auth_proto.h"
+#include "libcli/security/security.h"
+#include "lib/util/tevent_ntstatus.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+#undef strncasecmp
+
+_PUBLIC_ NTSTATUS auth4_developer_init(TALLOC_CTX *);
+
+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_user_info_dc **_user_info_dc,
+ bool *authoritative)
+{
+ NTSTATUS nt_status;
+ struct auth_user_info_dc *user_info_dc;
+ struct auth_user_info *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);
+
+ user_info_dc = talloc_zero(mem_ctx, struct auth_user_info_dc);
+ NT_STATUS_HAVE_NO_MEMORY(user_info_dc);
+
+ /* This returns a pointer to a struct dom_sid, which is the
+ * same as a 1 element list of struct dom_sid */
+ user_info_dc->num_sids = 1;
+ user_info_dc->sids = dom_sid_parse_talloc(user_info_dc, SID_NT_ANONYMOUS);
+ NT_STATUS_HAVE_NO_MEMORY(user_info_dc->sids);
+
+ /* annoying, but the Anonymous really does have a session key,
+ and it is all zeros! */
+ user_info_dc->user_session_key = data_blob_talloc(user_info_dc, NULL, 16);
+ NT_STATUS_HAVE_NO_MEMORY(user_info_dc->user_session_key.data);
+
+ user_info_dc->lm_session_key = data_blob_talloc(user_info_dc, NULL, 16);
+ NT_STATUS_HAVE_NO_MEMORY(user_info_dc->lm_session_key.data);
+
+ data_blob_clear(&user_info_dc->user_session_key);
+ data_blob_clear(&user_info_dc->lm_session_key);
+
+ user_info_dc->info = info = talloc_zero(user_info_dc, struct auth_user_info);
+ NT_STATUS_HAVE_NO_MEMORY(user_info_dc->info);
+
+ info->account_name = talloc_asprintf(user_info_dc, "NAME TO NTSTATUS %s ANONYMOUS LOGON", user);
+ NT_STATUS_HAVE_NO_MEMORY(info->account_name);
+
+ info->domain_name = talloc_strdup(user_info_dc, "NT AUTHORITY");
+ NT_STATUS_HAVE_NO_MEMORY(info->domain_name);
+
+ info->full_name = talloc_asprintf(user_info_dc, "NAME TO NTSTATUS %s Anonymous Logon", user);
+ NT_STATUS_HAVE_NO_MEMORY(info->full_name);
+
+ info->logon_script = talloc_strdup(user_info_dc, "");
+ NT_STATUS_HAVE_NO_MEMORY(info->logon_script);
+
+ info->profile_path = talloc_strdup(user_info_dc, "");
+ NT_STATUS_HAVE_NO_MEMORY(info->profile_path);
+
+ info->home_directory = talloc_strdup(user_info_dc, "");
+ NT_STATUS_HAVE_NO_MEMORY(info->home_directory);
+
+ info->home_drive = talloc_strdup(user_info_dc, "");
+ NT_STATUS_HAVE_NO_MEMORY(info->home_drive);
+
+ info->last_logon = 0;
+ info->last_logoff = 0;
+ info->acct_expiry = 0;
+ info->last_password_change = 0;
+ info->allow_password_change = 0;
+ info->force_password_change = 0;
+
+ info->logon_count = 0;
+ info->bad_password_count = 0;
+
+ info->acct_flags = ACB_NORMAL;
+
+ info->authenticated = true;
+
+ *_user_info_dc = user_info_dc;
+
+ return nt_status;
+}
+
+struct name_to_ntstatus_check_password_state {
+ struct auth_user_info_dc *user_info_dc;
+ bool authoritative;
+};
+
+static struct tevent_req *name_to_ntstatus_check_password_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct auth_method_context *ctx,
+ const struct auth_usersupplied_info *user_info)
+{
+ struct tevent_req *req = NULL;
+ struct name_to_ntstatus_check_password_state *state = NULL;
+ NTSTATUS status;
+
+ req = tevent_req_create(
+ mem_ctx,
+ &state,
+ struct name_to_ntstatus_check_password_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ status = name_to_ntstatus_check_password(
+ ctx,
+ state,
+ user_info,
+ &state->user_info_dc,
+ &state->authoritative);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+}
+
+static NTSTATUS name_to_ntstatus_check_password_recv(
+ struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct auth_user_info_dc **interim_info,
+ bool *authoritative)
+{
+ struct name_to_ntstatus_check_password_state *state = tevent_req_data(
+ req, struct name_to_ntstatus_check_password_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+ *interim_info = talloc_move(mem_ctx, &state->user_info_dc);
+ *authoritative = state->authoritative;
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+static const struct auth_operations name_to_ntstatus_auth_ops = {
+ .name = "name_to_ntstatus",
+ .want_check = name_to_ntstatus_want_check,
+ .check_password_send = name_to_ntstatus_check_password_send,
+ .check_password_recv = name_to_ntstatus_check_password_recv,
+};
+
+_PUBLIC_ NTSTATUS auth4_developer_init(TALLOC_CTX *ctx)
+{
+ NTSTATUS ret;
+
+ ret = auth_register(ctx, &name_to_ntstatus_auth_ops);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register 'name_to_ntstatus' auth backend!\n"));
+ return ret;
+ }
+
+ return ret;
+}
diff --git a/source4/auth/ntlm/auth_sam.c b/source4/auth/ntlm/auth_sam.c
new file mode 100644
index 0000000..0d50431
--- /dev/null
+++ b/source4/auth/ntlm/auth_sam.c
@@ -0,0 +1,1210 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2009
+ Copyright (C) Gerald Carter 2003
+ Copyright (C) Stefan Metzmacher 2005-2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/time.h"
+#include <ldb.h>
+#include "libcli/ldap/ldap_ndr.h"
+#include "libcli/security/security.h"
+#include "auth/auth.h"
+#include "../libcli/auth/ntlm_check.h"
+#include "auth/ntlm/auth_proto.h"
+#include "auth/auth_sam.h"
+#include "dsdb/samdb/samdb.h"
+#include "dsdb/common/util.h"
+#include "param/param.h"
+#include "librpc/gen_ndr/ndr_irpc_c.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "lib/messaging/irpc.h"
+#include "libcli/auth/libcli_auth.h"
+#include "libds/common/roles.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "system/kerberos.h"
+#include "auth/kerberos/kerberos.h"
+#include "kdc/db-glue.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+NTSTATUS auth_sam_init(void);
+
+extern const char *user_attrs[];
+extern const char *domain_ref_attrs[];
+
+/****************************************************************************
+ 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 auth4_context *auth_context,
+ TALLOC_CTX *mem_ctx,
+ const struct samr_Password *nt_pwd,
+ struct smb_krb5_context *smb_krb5_context,
+ const DATA_BLOB *stored_aes_256_key,
+ const krb5_data *salt,
+ const struct auth_usersupplied_info *user_info,
+ DATA_BLOB *user_sess_key,
+ DATA_BLOB *lm_sess_key)
+{
+ NTSTATUS status;
+
+ switch (user_info->password_state) {
+ case AUTH_PASSWORD_PLAIN:
+ {
+ const struct auth_usersupplied_info *user_info_temp;
+
+ if (nt_pwd == NULL && stored_aes_256_key != NULL && user_info->password.plaintext != NULL) {
+ bool pw_equal;
+ int krb5_ret;
+ DATA_BLOB supplied_aes_256_key;
+ krb5_keyblock key;
+ krb5_data cleartext_data = {
+ .data = user_info->password.plaintext,
+ .length = strlen(user_info->password.plaintext)
+ };
+
+ *lm_sess_key = data_blob_null;
+ *user_sess_key = data_blob_null;
+
+ krb5_ret = smb_krb5_create_key_from_string(smb_krb5_context->krb5_context,
+ NULL,
+ salt,
+ &cleartext_data,
+ ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+ &key);
+ if (krb5_ret) {
+ DBG_ERR("generation of a aes256-cts-hmac-sha1-96 key for password comparison failed: %s",
+ smb_get_krb5_error_message(smb_krb5_context->krb5_context,
+ krb5_ret, mem_ctx));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ supplied_aes_256_key = data_blob_const(KRB5_KEY_DATA(&key),
+ KRB5_KEY_LENGTH(&key));
+
+ pw_equal = data_blob_equal_const_time(&supplied_aes_256_key,
+ stored_aes_256_key);
+
+ krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &key);
+ if (!pw_equal) {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ return NT_STATUS_OK;
+ }
+
+ 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,
+ false,
+ NULL,
+ user_info->password.hash.nt,
+ user_info->mapped.account_name,
+ NULL, nt_pwd);
+ NT_STATUS_NOT_OK_RETURN(status);
+ break;
+
+ case AUTH_PASSWORD_RESPONSE:
+ status = ntlm_password_check(mem_ctx,
+ false,
+ lpcfg_ntlm_auth(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,
+ NULL, nt_pwd,
+ user_sess_key, lm_sess_key);
+ NT_STATUS_NOT_OK_RETURN(status);
+ break;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static void auth_sam_trigger_zero_password(TALLOC_CTX *mem_ctx,
+ struct imessaging_context *msg_ctx,
+ struct tevent_context *event_ctx,
+ struct netr_SendToSamBase *send_to_sam)
+{
+ struct dcerpc_binding_handle *irpc_handle;
+ struct winbind_SendToSam r;
+ struct tevent_req *req;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return;
+ }
+
+ irpc_handle = irpc_binding_handle_by_name(tmp_ctx, msg_ctx,
+ "winbind_server",
+ &ndr_table_winbind);
+ if (irpc_handle == NULL) {
+ DEBUG(1,(__location__ ": Unable to get binding handle for winbind\n"));
+ TALLOC_FREE(tmp_ctx);
+ return;
+ }
+
+ r.in.message = *send_to_sam;
+
+ /*
+ * This seem to rely on the current IRPC implementation,
+ * which delivers the message in the _send function.
+ *
+ * TODO: we need a ONE_WAY IRPC handle and register
+ * a callback and wait for it to be triggered!
+ */
+ req = dcerpc_winbind_SendToSam_r_send(tmp_ctx,
+ event_ctx,
+ irpc_handle,
+ &r);
+
+ /* we aren't interested in a reply */
+ talloc_free(req);
+ TALLOC_FREE(tmp_ctx);
+
+}
+
+/*
+ send a message to the drepl server telling it to initiate a
+ REPL_SECRET getncchanges extended op to fetch the users secrets
+ */
+static void auth_sam_trigger_repl_secret(TALLOC_CTX *mem_ctx,
+ struct imessaging_context *msg_ctx,
+ struct tevent_context *event_ctx,
+ struct ldb_dn *user_dn)
+{
+ struct dcerpc_binding_handle *irpc_handle;
+ struct drepl_trigger_repl_secret r;
+ struct tevent_req *req;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return;
+ }
+
+ irpc_handle = irpc_binding_handle_by_name(tmp_ctx, msg_ctx,
+ "dreplsrv",
+ &ndr_table_irpc);
+ if (irpc_handle == NULL) {
+ DEBUG(1,(__location__ ": Unable to get binding handle for dreplsrv\n"));
+ TALLOC_FREE(tmp_ctx);
+ return;
+ }
+
+ r.in.user_dn = ldb_dn_get_linearized(user_dn);
+
+ /*
+ * This seem to rely on the current IRPC implementation,
+ * which delivers the message in the _send function.
+ *
+ * TODO: we need a ONE_WAY IRPC handle and register
+ * a callback and wait for it to be triggered!
+ */
+ req = dcerpc_drepl_trigger_repl_secret_r_send(tmp_ctx,
+ event_ctx,
+ irpc_handle,
+ &r);
+
+ /* we aren't interested in a reply */
+ talloc_free(req);
+ TALLOC_FREE(tmp_ctx);
+}
+
+static const struct samr_Password *hide_invalid_nthash(const struct samr_Password *in)
+{
+ /*
+ * This is the result of:
+ *
+ * E_md4hash("", zero_string_hash.hash);
+ */
+ static const struct samr_Password zero_string_hash = {
+ .hash = {
+ 0x31, 0xd6, 0xcf, 0xe0, 0xd1, 0x6a, 0xe9, 0x31,
+ 0xb7, 0x3c, 0x59, 0xd7, 0xe0, 0xc0, 0x89, 0xc0,
+ }
+ };
+
+ if (in == NULL) {
+ return NULL;
+ }
+
+ /*
+ * Skip over any all-zero hashes in the history. No known software
+ * stores these but just to be sure
+ */
+ if (all_zero(in->hash, sizeof(in->hash))) {
+ return NULL;
+ }
+
+ /*
+ * This looks odd, but the password_hash module in the past has written
+ * this in the rare situation where (somehow) we didn't have an old NT
+ * hash (one of the old LM-only set paths)
+ *
+ * mem_equal_const_time() is used to avoid a timing attack
+ * when comparing secret data in the server with this constant
+ * value.
+ */
+ if (mem_equal_const_time(in->hash, zero_string_hash.hash, 16)) {
+ in = NULL;
+ }
+
+ return in;
+}
+
+/*
+ * Check that a password is OK, and update badPwdCount if required.
+ */
+
+static NTSTATUS authsam_password_check_and_record(struct auth4_context *auth_context,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_dn *domain_dn,
+ struct ldb_message *msg,
+ const struct auth_usersupplied_info *user_info,
+ DATA_BLOB *user_sess_key,
+ DATA_BLOB *lm_sess_key,
+ bool *authoritative)
+{
+ NTSTATUS nt_status;
+ NTSTATUS auth_status;
+ TALLOC_CTX *tmp_ctx;
+ int i, ret;
+ int history_len = 0;
+ struct ldb_context *sam_ctx = auth_context->sam_ctx;
+ const char * const attrs[] = { "pwdHistoryLength", NULL };
+ struct ldb_message *dom_msg;
+ struct samr_Password *nt_pwd;
+ DATA_BLOB _aes_256_key = data_blob_null;
+ DATA_BLOB *aes_256_key = NULL;
+ krb5_data _salt = { .data = NULL, .length = 0 };
+ krb5_data *salt = NULL;
+ DATA_BLOB salt_data = data_blob_null;
+ struct smb_krb5_context *smb_krb5_context = NULL;
+ const struct ldb_val *sc_val;
+ uint32_t userAccountControl = 0;
+ uint32_t current_kvno = 0;
+ bool am_rodc;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * This call does more than what it appears to do, it also
+ * checks for the account lockout.
+ *
+ * It is done here so that all parts of Samba that read the
+ * password refuse to even operate on it if the account is
+ * locked out, to avoid mistakes like CVE-2013-4496.
+ */
+ nt_status = samdb_result_passwords(tmp_ctx, auth_context->lp_ctx,
+ msg, &nt_pwd);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ TALLOC_FREE(tmp_ctx);
+ return nt_status;
+ }
+
+ userAccountControl = ldb_msg_find_attr_as_uint(msg,
+ "userAccountControl",
+ 0);
+
+ sc_val = ldb_msg_find_ldb_val(msg, "supplementalCredentials");
+
+ if (nt_pwd == NULL && sc_val == NULL) {
+ if (samdb_rodc(auth_context->sam_ctx, &am_rodc) == LDB_SUCCESS && am_rodc) {
+ /*
+ * we don't have passwords for this
+ * account. We are an RODC, and this account
+ * may be one for which we either are denied
+ * REPL_SECRET replication or we haven't yet
+ * done the replication. We return
+ * NT_STATUS_NOT_IMPLEMENTED which tells the
+ * auth code to try the next authentication
+ * mechanism. We also send a message to our
+ * drepl server to tell it to try and
+ * replicate the secrets for this account.
+ *
+ * TODO: Should we only trigger this is detected
+ * there's a chance that the password might be
+ * replicated, we should be able to detect this
+ * based on msDS-NeverRevealGroup.
+ */
+ auth_sam_trigger_repl_secret(auth_context,
+ auth_context->msg_ctx,
+ auth_context->event_ctx,
+ msg->dn);
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ }
+
+ /*
+ * If we don't have an NT password, pull a kerberos key
+ * instead for plaintext.
+ */
+ if (nt_pwd == NULL &&
+ sc_val != NULL &&
+ user_info->password_state == AUTH_PASSWORD_PLAIN)
+ {
+ krb5_error_code krb5_ret;
+
+ krb5_ret = smb_krb5_init_context(tmp_ctx,
+ auth_context->lp_ctx,
+ &smb_krb5_context);
+ if (krb5_ret != 0) {
+ DBG_ERR("Failed to setup krb5_context: %s!",
+ error_message(krb5_ret));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ /*
+ * Get the current salt from the record
+ */
+
+ krb5_ret = dsdb_extract_aes_256_key(smb_krb5_context->krb5_context,
+ tmp_ctx,
+ msg,
+ userAccountControl,
+ NULL, /* kvno */
+ &current_kvno, /* kvno_out */
+ &_aes_256_key,
+ &salt_data);
+ if (krb5_ret == 0) {
+ aes_256_key = &_aes_256_key;
+
+ _salt.data = (char *)salt_data.data;
+ _salt.length = salt_data.length;
+ salt = &_salt;
+ }
+ }
+
+ auth_status = authsam_password_ok(auth_context,
+ tmp_ctx,
+ nt_pwd,
+ smb_krb5_context,
+ aes_256_key,
+ salt,
+ user_info,
+ user_sess_key, lm_sess_key);
+
+ if (NT_STATUS_IS_OK(auth_status)) {
+ if (user_sess_key->data) {
+ talloc_steal(mem_ctx, user_sess_key->data);
+ }
+ if (lm_sess_key->data) {
+ talloc_steal(mem_ctx, lm_sess_key->data);
+ }
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_OK;
+ }
+ *user_sess_key = data_blob_null;
+ *lm_sess_key = data_blob_null;
+
+ if (!NT_STATUS_EQUAL(auth_status, NT_STATUS_WRONG_PASSWORD)) {
+ TALLOC_FREE(tmp_ctx);
+ return auth_status;
+ }
+
+ /*
+ * We only continue if this was a wrong password
+ * and we'll always return NT_STATUS_WRONG_PASSWORD
+ * no matter what error happens.
+ */
+
+ /* pull the domain password property attributes */
+ ret = dsdb_search_one(sam_ctx, tmp_ctx, &dom_msg, domain_dn, LDB_SCOPE_BASE,
+ attrs, 0, "objectClass=domain");
+ if (ret == LDB_SUCCESS) {
+ history_len = ldb_msg_find_attr_as_uint(dom_msg, "pwdHistoryLength", 0);
+ } else if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ DEBUG(3,("Couldn't find domain %s: %s!\n",
+ ldb_dn_get_linearized(domain_dn),
+ ldb_errstring(sam_ctx)));
+ } else {
+ DEBUG(3,("error finding domain %s: %s!\n",
+ ldb_dn_get_linearized(domain_dn),
+ ldb_errstring(sam_ctx)));
+ }
+
+ for (i = 1; i < MIN(history_len, 3); i++) {
+ const struct samr_Password *nt_history_pwd = NULL;
+ NTTIME pwdLastSet;
+ struct timeval tv_now;
+ NTTIME now;
+ int allowed_period_mins;
+ NTTIME allowed_period;
+
+ /* Reset these variables back to starting as empty */
+ aes_256_key = NULL;
+ salt = NULL;
+
+ /*
+ * Obtain the i'th old password from the NT password
+ * history for this user.
+ *
+ * We avoid issues with salts (which are not
+ * recorded for historical AES256 keys) by using the
+ * ntPwdHistory in preference.
+ */
+ nt_status = samdb_result_passwords_from_history(tmp_ctx,
+ auth_context->lp_ctx,
+ msg, i,
+ NULL,
+ &nt_history_pwd);
+
+ /*
+ * Belts and braces: note that
+ * samdb_result_passwords_from_history() currently
+ * does not fail for missing attributes, it only sets
+ * nt_history_pwd = NULL, so "break" and fall down to
+ * the bad password count upate if this happens
+ */
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ break;
+ }
+
+ nt_history_pwd = hide_invalid_nthash(nt_history_pwd);
+
+ /*
+ * We don't have an NT hash from the
+ * ntPwdHistory, but we can still perform the
+ * password check with the AES256
+ * key.
+ *
+ * However, this is the second preference as
+ * it will fail if the account was renamed
+ * prior to a password change (as we won't
+ * have the correct salt available to
+ * calculate the AES256 key).
+ */
+
+ if (nt_history_pwd == NULL && sc_val != NULL &&
+ user_info->password_state == AUTH_PASSWORD_PLAIN &&
+ current_kvno >= i)
+ {
+ krb5_error_code krb5_ret;
+ const uint32_t request_kvno = current_kvno - i;
+
+ /*
+ * Confirm we have a krb5_context set up
+ */
+ if (smb_krb5_context == NULL) {
+ /*
+ * We get here if we had a unicodePwd
+ * for the current password, no
+ * ntPwdHistory, a valid previous
+ * Kerberos history AND are processing
+ * a simple bind.
+ *
+ * This really is a corner case so
+ * favour cleaner code over trying to
+ * allow for an old password. It is
+ * more likely this is just a new
+ * account.
+ *
+ * "break" out of the loop and fall down
+ * to the bad password update
+ */
+ break;
+ }
+
+ /*
+ * Get the current salt from the record
+ */
+
+ krb5_ret = dsdb_extract_aes_256_key(smb_krb5_context->krb5_context,
+ tmp_ctx,
+ msg,
+ userAccountControl,
+ &request_kvno, /* kvno */
+ NULL, /* kvno_out */
+ &_aes_256_key,
+ &salt_data);
+ if (krb5_ret != 0) {
+ break;
+ }
+
+ aes_256_key = &_aes_256_key;
+
+ _salt.data = (char *)salt_data.data;
+ _salt.length = salt_data.length;
+ salt = &_salt;
+
+ } else if (nt_history_pwd == NULL) {
+ /*
+ * If we don't find element 'i' in the
+ * ntPwdHistory and can not fall back to the
+ * kerberos hash, we won't find 'i+1' ...
+ */
+ break;
+ }
+
+ auth_status = authsam_password_ok(auth_context, tmp_ctx,
+ nt_history_pwd,
+ smb_krb5_context,
+ aes_256_key,
+ salt,
+ user_info,
+ user_sess_key,
+ lm_sess_key);
+
+ if (!NT_STATUS_IS_OK(auth_status)) {
+ /*
+ * If this was not a correct password, try the next
+ * one from the history
+ */
+ *user_sess_key = data_blob_null;
+ *lm_sess_key = data_blob_null;
+ continue;
+ }
+
+ if (i != 1) {
+ /*
+ * The authentication was OK, but not against
+ * the previous password, which is stored at index 1.
+ *
+ * We just return the original wrong password.
+ * This skips the update of the bad pwd count,
+ * because this is almost certainly user error
+ * (or automatic login on a computer using a cached
+ * password from before the password change),
+ * not an attack.
+ */
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ if (user_info->flags & USER_INFO_INTERACTIVE_LOGON) {
+ /*
+ * The authentication was OK against the previous password,
+ * but it's not a NTLM network authentication,
+ * LDAP simple bind or something similar.
+ *
+ * We just return the original wrong password.
+ * This skips the update of the bad pwd count,
+ * because this is almost certainly user error
+ * (or automatic login on a computer using a cached
+ * password from before the password change),
+ * not an attack.
+ */
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /*
+ * If the password was OK, it's a NTLM network authentication
+ * and it was the previous password.
+ *
+ * Now we see if it is within the grace period,
+ * so that we don't break cached sessions on other computers
+ * before the user can lock and unlock their other screens
+ * (resetting their cached password).
+ *
+ * See http://support.microsoft.com/kb/906305
+ * OldPasswordAllowedPeriod ("old password allowed period")
+ * is specified in minutes. The default is 60.
+ */
+ allowed_period_mins = lpcfg_old_password_allowed_period(auth_context->lp_ctx);
+ /*
+ * NTTIME uses 100ns units
+ */
+ allowed_period = (NTTIME) allowed_period_mins *
+ 60 * 1000*1000*10;
+ pwdLastSet = samdb_result_nttime(msg, "pwdLastSet", 0);
+ tv_now = timeval_current();
+ now = timeval_to_nttime(&tv_now);
+
+ if (now < pwdLastSet) {
+ /*
+ * time jump?
+ *
+ * We just return the original wrong password.
+ * This skips the update of the bad pwd count,
+ * because this is almost certainly user error
+ * (or automatic login on a computer using a cached
+ * password from before the password change),
+ * not an attack.
+ */
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ if ((now - pwdLastSet) >= allowed_period) {
+ /*
+ * The allowed period is over.
+ *
+ * We just return the original wrong password.
+ * This skips the update of the bad pwd count,
+ * because this is almost certainly user error
+ * (or automatic login on a computer using a cached
+ * password from before the password change),
+ * not an attack.
+ */
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /*
+ * We finally allow the authentication with the
+ * previous password within the allowed period.
+ */
+ if (user_sess_key->data) {
+ talloc_steal(mem_ctx, user_sess_key->data);
+ }
+ if (lm_sess_key->data) {
+ talloc_steal(mem_ctx, lm_sess_key->data);
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return auth_status;
+ }
+
+ /*
+ * If we are not in the allowed period or match an old password,
+ * we didn't return early. Now update the badPwdCount et al.
+ */
+ nt_status = authsam_update_bad_pwd_count(auth_context->sam_ctx,
+ msg, domain_dn);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ /*
+ * We need to return the original
+ * NT_STATUS_WRONG_PASSWORD error, so there isn't
+ * anything more we can do than write something into
+ * the log
+ */
+ DEBUG(0, ("Failed to note bad password for user [%s]: %s\n",
+ user_info->mapped.account_name,
+ nt_errstr(nt_status)));
+ }
+
+ if (samdb_rodc(auth_context->sam_ctx, &am_rodc) == LDB_SUCCESS && am_rodc) {
+ *authoritative = false;
+ }
+
+ TALLOC_FREE(tmp_ctx);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ nt_status = NT_STATUS_WRONG_PASSWORD;
+ }
+ return nt_status;
+}
+
+static NTSTATUS authsam_authenticate(struct auth4_context *auth_context,
+ TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
+ struct ldb_dn *domain_dn,
+ struct ldb_message *msg,
+ const struct auth_usersupplied_info *user_info,
+ DATA_BLOB *user_sess_key, DATA_BLOB *lm_sess_key,
+ bool *authoritative)
+{
+ NTSTATUS nt_status;
+ bool interactive = (user_info->password_state == AUTH_PASSWORD_HASH);
+ uint32_t acct_flags = samdb_result_acct_flags(msg, NULL);
+ struct netr_SendToSamBase *send_to_sam = NULL;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* You can only do an interactive login to normal accounts */
+ if (user_info->flags & USER_INFO_INTERACTIVE_LOGON) {
+ if (!(acct_flags & ACB_NORMAL)) {
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+ if (acct_flags & ACB_SMARTCARD_REQUIRED) {
+ if (acct_flags & ACB_DISABLED) {
+ DEBUG(2,("authsam_authenticate: Account for user '%s' "
+ "was disabled.\n",
+ user_info->mapped.account_name));
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_ACCOUNT_DISABLED;
+ }
+ DEBUG(2,("authsam_authenticate: Account for user '%s' "
+ "requires interactive smartcard logon.\n",
+ user_info->mapped.account_name));
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_SMARTCARD_LOGON_REQUIRED;
+ }
+ }
+
+ nt_status = authsam_password_check_and_record(auth_context, tmp_ctx,
+ domain_dn, msg,
+ user_info,
+ user_sess_key, lm_sess_key,
+ authoritative);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ TALLOC_FREE(tmp_ctx);
+ return nt_status;
+ }
+
+ nt_status = authsam_account_ok(tmp_ctx, auth_context->sam_ctx,
+ user_info->logon_parameters,
+ domain_dn,
+ msg,
+ user_info->workstation_name,
+ user_info->mapped.account_name,
+ false, false);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ TALLOC_FREE(tmp_ctx);
+ return nt_status;
+ }
+
+ nt_status = authsam_logon_success_accounting(auth_context->sam_ctx,
+ msg, domain_dn,
+ interactive,
+ tmp_ctx,
+ &send_to_sam);
+
+ if (send_to_sam != NULL) {
+ auth_sam_trigger_zero_password(tmp_ctx,
+ auth_context->msg_ctx,
+ auth_context->event_ctx,
+ send_to_sam);
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ TALLOC_FREE(tmp_ctx);
+ return nt_status;
+ }
+
+ if (user_sess_key && user_sess_key->data) {
+ talloc_steal(mem_ctx, user_sess_key->data);
+ }
+ if (lm_sess_key && lm_sess_key->data) {
+ talloc_steal(mem_ctx, lm_sess_key->data);
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return nt_status;
+}
+
+
+
+static NTSTATUS authsam_check_password_internals(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_user_info_dc **user_info_dc,
+ bool *authoritative)
+{
+ NTSTATUS nt_status;
+ int result;
+ const char *account_name = user_info->mapped.account_name;
+ struct ldb_message *msg;
+ struct ldb_dn *domain_dn;
+ DATA_BLOB user_sess_key, lm_sess_key;
+ TALLOC_CTX *tmp_ctx;
+ const char *p = NULL;
+
+ if (ctx->auth_ctx->sam_ctx == NULL) {
+ DEBUG(0, ("No SAM available, cannot log in users\n"));
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ 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;
+ }
+
+ domain_dn = ldb_get_default_basedn(ctx->auth_ctx->sam_ctx);
+ if (domain_dn == NULL) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ /*
+ * If we have not already mapped this user, then now is a good
+ * time to do so, before we look it up. We used to do this
+ * earlier, but in a multi-forest environment we want to do
+ * this mapping at the final domain.
+ *
+ * However, on the flip side we may have already mapped the
+ * user if this was an LDAP simple bind, in which case we
+ * really, really want to get back to exactly the same account
+ * we got the DN for.
+ */
+ if (!user_info->cracknames_called) {
+ p = strchr_m(account_name, '@');
+ } else {
+ /*
+ * This is slightly nicer than double-indenting the
+ * block below
+ */
+ p = NULL;
+ }
+
+ if (p != NULL) {
+ const char *nt4_domain = NULL;
+ const char *nt4_account = NULL;
+ bool is_my_domain = false;
+
+ nt_status = crack_name_to_nt4_name(mem_ctx,
+ ctx->auth_ctx->sam_ctx,
+ /*
+ * DRSUAPI_DS_NAME_FORMAT_UPN_FOR_LOGON ?
+ */
+ DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL,
+ account_name,
+ &nt4_domain, &nt4_account);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ is_my_domain = lpcfg_is_mydomain(ctx->auth_ctx->lp_ctx, nt4_domain);
+ if (!is_my_domain) {
+ /*
+ * This is a user within our forest,
+ * but in a different domain,
+ * we're not authoritative
+ */
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ /*
+ * Let's use the NT4 account name for the lookup.
+ */
+ account_name = nt4_account;
+ }
+
+ nt_status = authsam_search_account(tmp_ctx, ctx->auth_ctx->sam_ctx, account_name, domain_dn, &msg);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(tmp_ctx);
+ return nt_status;
+ }
+
+ nt_status = authsam_make_user_info_dc(tmp_ctx, ctx->auth_ctx->sam_ctx,
+ lpcfg_netbios_name(ctx->auth_ctx->lp_ctx),
+ lpcfg_sam_name(ctx->auth_ctx->lp_ctx),
+ lpcfg_sam_dnsname(ctx->auth_ctx->lp_ctx),
+ domain_dn,
+ msg,
+ data_blob_null, data_blob_null,
+ user_info_dc);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(tmp_ctx);
+ return nt_status;
+ }
+
+ result = dsdb_is_protected_user(ctx->auth_ctx->sam_ctx,
+ (*user_info_dc)->sids,
+ (*user_info_dc)->num_sids);
+ /*
+ * We also consider an error result (a negative value) as denying the
+ * authentication.
+ */
+ if (result != 0) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_ACCOUNT_RESTRICTION;
+ }
+
+ nt_status = authsam_authenticate(ctx->auth_ctx, tmp_ctx, ctx->auth_ctx->sam_ctx, domain_dn, msg, user_info,
+ &user_sess_key, &lm_sess_key, authoritative);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(tmp_ctx);
+ return nt_status;
+ }
+
+ (*user_info_dc)->user_session_key = data_blob_talloc(*user_info_dc,
+ user_sess_key.data,
+ user_sess_key.length);
+ if (user_sess_key.data) {
+ if ((*user_info_dc)->user_session_key.data == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ (*user_info_dc)->lm_session_key = data_blob_talloc(*user_info_dc,
+ lm_sess_key.data,
+ lm_sess_key.length);
+ if (lm_sess_key.data) {
+ if ((*user_info_dc)->lm_session_key.data == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ talloc_steal(mem_ctx, *user_info_dc);
+ talloc_free(tmp_ctx);
+
+ return NT_STATUS_OK;
+}
+
+struct authsam_check_password_state {
+ struct auth_user_info_dc *user_info_dc;
+ bool authoritative;
+};
+
+static struct tevent_req *authsam_check_password_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct auth_method_context *ctx,
+ const struct auth_usersupplied_info *user_info)
+{
+ struct tevent_req *req = NULL;
+ struct authsam_check_password_state *state = NULL;
+ NTSTATUS status;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct authsam_check_password_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ /*
+ * authsam_check_password_internals() sets this to false in
+ * the rodc case, otherwise it leaves it untouched. Default to
+ * "we're authoritative".
+ */
+ state->authoritative = true;
+
+ status = authsam_check_password_internals(
+ ctx,
+ state,
+ user_info,
+ &state->user_info_dc,
+ &state->authoritative);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+}
+
+static NTSTATUS authsam_check_password_recv(
+ struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct auth_user_info_dc **interim_info,
+ bool *authoritative)
+{
+ struct authsam_check_password_state *state = tevent_req_data(
+ req, struct authsam_check_password_state);
+ NTSTATUS status;
+
+ *authoritative = state->authoritative;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+ *interim_info = talloc_move(mem_ctx, &state->user_info_dc);
+ tevent_req_received(req);
+ 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;
+}
+
+/****************************************************************************
+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)
+{
+ const char *effective_domain = user_info->mapped.domain_name;
+ bool is_local_name = false;
+ bool is_my_domain = false;
+ const char *p = NULL;
+ struct dsdb_trust_routing_table *trt = NULL;
+ const struct lsa_TrustDomainInfoInfoEx *tdo = NULL;
+ NTSTATUS status;
+
+ if (!user_info->mapped.account_name || !*user_info->mapped.account_name) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (effective_domain == NULL) {
+ effective_domain = "";
+ }
+
+ is_local_name = lpcfg_is_myname(ctx->auth_ctx->lp_ctx,
+ effective_domain);
+
+ /* check whether or not we service this domain/workgroup name */
+ switch (lpcfg_server_role(ctx->auth_ctx->lp_ctx)) {
+ case ROLE_STANDALONE:
+ return NT_STATUS_OK;
+
+ case ROLE_DOMAIN_MEMBER:
+ if (is_local_name) {
+ return NT_STATUS_OK;
+ }
+
+ DBG_DEBUG("%s is not one of my local names (DOMAIN_MEMBER)\n",
+ effective_domain);
+ return NT_STATUS_NOT_IMPLEMENTED;
+
+ case ROLE_ACTIVE_DIRECTORY_DC:
+ /* handled later */
+ break;
+
+ default:
+ DBG_ERR("lpcfg_server_role() has an undefined value\n");
+ return NT_STATUS_INVALID_SERVER_STATE;
+ }
+
+ /*
+ * Now we handle the AD DC case...
+ */
+
+ is_my_domain = lpcfg_is_my_domain_or_realm(ctx->auth_ctx->lp_ctx,
+ effective_domain);
+ if (is_my_domain) {
+ return NT_STATUS_OK;
+ }
+
+ if (user_info->cracknames_called) {
+ /*
+ * The caller already did a cracknames call.
+ */
+ DBG_DEBUG("%s is not own domain name (DC)\n",
+ effective_domain);
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!strequal(effective_domain, "")) {
+ DBG_DEBUG("%s is not own domain name (DC)\n",
+ effective_domain);
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ p = strchr_m(user_info->mapped.account_name, '@');
+ if (p == NULL) {
+ /*
+ * An empty to domain name should be handled
+ * as the local domain name.
+ */
+ return NT_STATUS_OK;
+ }
+
+ effective_domain = p + 1;
+ is_my_domain = lpcfg_is_my_domain_or_realm(ctx->auth_ctx->lp_ctx,
+ effective_domain);
+ if (is_my_domain) {
+ return NT_STATUS_OK;
+ }
+
+ if (strequal(effective_domain, "")) {
+ DBG_DEBUG("authsam_check_password: upn without realm (DC)\n");
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ /*
+ * as last option we check the routing table if the
+ * domain is within our forest.
+ */
+ status = dsdb_trust_routing_table_load(ctx->auth_ctx->sam_ctx,
+ mem_ctx, &trt);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("authsam_check_password: dsdb_trust_routing_table_load() %s\n",
+ nt_errstr(status));
+ return status;
+ }
+
+ tdo = dsdb_trust_routing_by_name(trt, effective_domain);
+ if (tdo == NULL) {
+ DBG_DEBUG("%s is not a known TLN (DC)\n",
+ effective_domain);
+ TALLOC_FREE(trt);
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (!(tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST)) {
+ DBG_DEBUG("%s is not a TLN in our forest (DC)\n",
+ effective_domain);
+ TALLOC_FREE(trt);
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ /*
+ * This principal is within our forest.
+ * we'll later do a crack_name_to_nt4_name()
+ * to check if it's in our domain.
+ */
+ TALLOC_FREE(trt);
+ return NT_STATUS_OK;
+}
+
+static const struct auth_operations sam_ignoredomain_ops = {
+ .name = "sam_ignoredomain",
+ .want_check = authsam_ignoredomain_want_check,
+ .check_password_send = authsam_check_password_send,
+ .check_password_recv = authsam_check_password_recv,
+};
+
+static const struct auth_operations sam_ops = {
+ .name = "sam",
+ .want_check = authsam_want_check,
+ .check_password_send = authsam_check_password_send,
+ .check_password_recv = authsam_check_password_recv,
+};
+
+_PUBLIC_ NTSTATUS auth4_sam_init(TALLOC_CTX *);
+_PUBLIC_ NTSTATUS auth4_sam_init(TALLOC_CTX *ctx)
+{
+ NTSTATUS ret;
+
+ ret = auth_register(ctx, &sam_ops);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register 'sam' auth backend!\n"));
+ return ret;
+ }
+
+ ret = auth_register(ctx, &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_service.c b/source4/auth/ntlm/auth_server_service.c
new file mode 100644
index 0000000..7fbb1fe
--- /dev/null
+++ b/source4/auth/ntlm/auth_server_service.c
@@ -0,0 +1,29 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Bartlett 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/auth.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+NTSTATUS server_service_auth_init(TALLOC_CTX *ctx)
+{
+ return auth4_init();
+}
diff --git a/source4/auth/ntlm/auth_simple.c b/source4/auth/ntlm/auth_simple.c
new file mode 100644
index 0000000..006e4d8
--- /dev/null
+++ b/source4/auth/ntlm/auth_simple.c
@@ -0,0 +1,217 @@
+/*
+ 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 <tevent.h>
+#include "lib/util/tevent_ntstatus.h"
+#include "auth/auth.h"
+#include "dsdb/samdb/samdb.h"
+#include "lib/param/param.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+struct authenticate_ldap_simple_bind_state {
+ bool using_tls;
+ struct auth4_context *auth_context;
+ struct auth_usersupplied_info *user_info;
+ struct auth_session_info *session_info;
+};
+
+static void authenticate_ldap_simple_bind_done(struct tevent_req *subreq);
+
+_PUBLIC_ struct tevent_req *authenticate_ldap_simple_bind_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct imessaging_context *msg,
+ struct loadparm_context *lp_ctx,
+ struct tsocket_address *remote_address,
+ struct tsocket_address *local_address,
+ bool using_tls,
+ const char *dn,
+ const char *password)
+{
+ struct tevent_req *req = NULL;
+ struct authenticate_ldap_simple_bind_state *state = NULL;
+ struct auth_usersupplied_info *user_info = NULL;
+ const char *nt4_domain = NULL;
+ const char *nt4_username = NULL;
+ struct tevent_req *subreq = NULL;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct authenticate_ldap_simple_bind_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->using_tls = using_tls;
+
+ status = auth_context_create(state, ev, msg, lp_ctx,
+ &state->auth_context);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ user_info = talloc_zero(state, struct auth_usersupplied_info);
+ if (tevent_req_nomem(user_info, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->user_info = user_info;
+
+ user_info->client.account_name = dn;
+ /* No client.domain_name, use account_name instead */
+ /* user_info->mapped.* will be filled below */
+
+ user_info->workstation_name = lpcfg_netbios_name(lp_ctx);
+
+ user_info->remote_host = remote_address;
+ user_info->local_host = local_address;
+
+ user_info->service_description = "LDAP";
+
+ if (using_tls) {
+ user_info->auth_description = "simple bind/TLS";
+ } else {
+ user_info->auth_description = "simple bind";
+ }
+
+ user_info->password_state = AUTH_PASSWORD_PLAIN;
+ user_info->password.plaintext = talloc_strdup(user_info, password);
+ if (tevent_req_nomem(user_info->password.plaintext, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ user_info->flags = USER_INFO_CASE_INSENSITIVE_USERNAME |
+ USER_INFO_DONT_CHECK_UNIX_ACCOUNT;
+
+ user_info->logon_parameters =
+ MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT |
+ MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT |
+ MSV1_0_CLEARTEXT_PASSWORD_ALLOWED |
+ MSV1_0_CLEARTEXT_PASSWORD_SUPPLIED;
+
+ status = crack_auto_name_to_nt4_name(state, state->auth_context->sam_ctx,
+ dn, &nt4_domain, &nt4_username);
+ if (!NT_STATUS_IS_OK(status)) {
+ log_authentication_event(msg, lp_ctx,
+ &state->auth_context->start_time,
+ user_info, status,
+ NULL, NULL, NULL);
+ }
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ user_info->orig_client = user_info->client;
+ user_info->client.account_name = nt4_username;
+ user_info->client.domain_name = nt4_domain;
+ user_info->cracknames_called = true;
+
+ subreq = auth_check_password_send(state, ev,
+ state->auth_context,
+ state->user_info);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, authenticate_ldap_simple_bind_done, req);
+
+ return req;
+}
+
+static void authenticate_ldap_simple_bind_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct authenticate_ldap_simple_bind_state *state =
+ tevent_req_data(req,
+ struct authenticate_ldap_simple_bind_state);
+ struct auth4_context *auth_context = state->auth_context;
+ struct auth_usersupplied_info *user_info = state->user_info;
+ const char *nt4_username = user_info->mapped.account_name;
+ const struct tsocket_address *remote_address = user_info->remote_host;
+ const struct tsocket_address *local_address = user_info->local_host;
+ const char *transport_protection = AUTHZ_TRANSPORT_PROTECTION_NONE;
+ struct auth_user_info_dc *user_info_dc = NULL;
+ uint8_t authoritative = 1;
+ uint32_t flags = 0;
+ NTSTATUS nt_status;
+
+ if (state->using_tls) {
+ transport_protection = AUTHZ_TRANSPORT_PROTECTION_TLS;
+ }
+
+ nt_status = auth_check_password_recv(subreq, state,
+ &user_info_dc,
+ &authoritative);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, nt_status)) {
+ return;
+ }
+
+ flags = AUTH_SESSION_INFO_DEFAULT_GROUPS;
+ if (user_info_dc->info->authenticated) {
+ flags |= AUTH_SESSION_INFO_AUTHENTICATED;
+ }
+
+ nt_status = auth_context->generate_session_info(auth_context,
+ state,
+ user_info_dc,
+ nt4_username,
+ flags,
+ &state->session_info);
+ if (tevent_req_nterror(req, nt_status)) {
+ return;
+ }
+
+ log_successful_authz_event(auth_context->msg_ctx,
+ auth_context->lp_ctx,
+ remote_address,
+ local_address,
+ "LDAP",
+ "simple bind",
+ transport_protection,
+ state->session_info);
+
+ tevent_req_done(req);
+}
+
+_PUBLIC_ NTSTATUS authenticate_ldap_simple_bind_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct auth_session_info **session_info)
+{
+ struct authenticate_ldap_simple_bind_state *state =
+ tevent_req_data(req,
+ struct authenticate_ldap_simple_bind_state);
+ NTSTATUS status;
+
+ *session_info = NULL;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ *session_info = talloc_move(mem_ctx, &state->session_info);
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
diff --git a/source4/auth/ntlm/auth_util.c b/source4/auth/ntlm/auth_util.c
new file mode 100644
index 0000000..58e97fb
--- /dev/null
+++ b/source4/auth/ntlm/auth_util.c
@@ -0,0 +1,183 @@
+/*
+ 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/auth/libcli_auth.h"
+#include "param/param.h"
+#include "auth/ntlm/auth_proto.h"
+#include "librpc/gen_ndr/drsuapi.h"
+#include "dsdb/samdb/samdb.h"
+#include "lib/crypto/gnutls_helpers.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+/****************************************************************************
+ Create an auth_usersupplied_data structure after appropriate mapping.
+****************************************************************************/
+
+NTSTATUS encrypt_user_info(TALLOC_CTX *mem_ctx, struct auth4_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)
+{
+ int rc;
+ 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:
+ {
+ uint8_t chal[8];
+ DATA_BLOB chall_blob;
+ user_info_temp = talloc_zero(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->password_state = to_state;
+
+ nt_status = auth_get_challenge(auth_context, chal);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ chall_blob = data_blob_talloc(mem_ctx, chal, 8);
+ if (lpcfg_client_ntlmv2_auth(auth_context->lp_ctx)) {
+ DATA_BLOB names_blob = NTLMv2_generate_names_blob(mem_ctx, lpcfg_netbios_name(auth_context->lp_ctx), lpcfg_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,
+ NULL, /* server_timestamp */
+ &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);
+ rc = SMBOWFencrypt(user_info_in->password.hash.nt->hash, chal, blob.data);
+ if (rc != 0) {
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ }
+ user_info_temp->password.response.nt = blob;
+ if (lpcfg_client_lanman_auth(auth_context->lp_ctx) && user_info_in->password.hash.lanman) {
+ DATA_BLOB lm_blob = data_blob_talloc(mem_ctx, NULL, 24);
+ rc = SMBOWFencrypt(user_info_in->password.hash.lanman->hash, chal, blob.data);
+ if (rc != 0) {
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ }
+ 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_zero(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->password_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;
+}
diff --git a/source4/auth/ntlm/auth_winbind.c b/source4/auth/ntlm/auth_winbind.c
new file mode 100644
index 0000000..719d877
--- /dev/null
+++ b/source4/auth/ntlm/auth_winbind.c
@@ -0,0 +1,322 @@
+/*
+ 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 <tevent.h>
+#include "../lib/util/tevent_ntstatus.h"
+#include "auth/auth.h"
+#include "auth/ntlm/auth_proto.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "lib/messaging/irpc.h"
+#include "param/param.h"
+#include "nsswitch/libwbclient/wbclient.h"
+#include "auth/auth_sam_reply.h"
+#include "libcli/security/security.h"
+#include "dsdb/samdb/samdb.h"
+#include "auth/auth_sam.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+_PUBLIC_ NTSTATUS auth4_winbind_init(TALLOC_CTX *);
+
+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;
+}
+
+struct winbind_check_password_state {
+ struct auth_method_context *ctx;
+ const struct auth_usersupplied_info *user_info;
+ struct winbind_SamLogon req;
+ struct auth_user_info_dc *user_info_dc;
+ bool authoritative;
+};
+
+static void winbind_check_password_done(struct tevent_req *subreq);
+
+/*
+ Authenticate a user with a challenge/response
+ using IRPC to the winbind task
+*/
+static struct tevent_req *winbind_check_password_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct auth_method_context *ctx,
+ const struct auth_usersupplied_info *user_info)
+{
+ struct tevent_req *req = NULL;
+ struct winbind_check_password_state *state = NULL;
+ NTSTATUS status;
+ struct dcerpc_binding_handle *irpc_handle;
+ const struct auth_usersupplied_info *user_info_new;
+ struct netr_IdentityInfo *identity_info;
+ struct imessaging_context *msg_ctx;
+ struct tevent_req *subreq = NULL;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbind_check_password_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ctx = ctx;
+ state->user_info = user_info;
+ state->authoritative = true;
+
+ msg_ctx = imessaging_client_init(state, ctx->auth_ctx->lp_ctx, ev);
+ if (msg_ctx == NULL) {
+ DEBUG(1, ("imessaging_init failed\n"));
+ tevent_req_nterror(req, NT_STATUS_INVALID_SERVER_STATE);
+ return tevent_req_post(req, ev);
+ }
+
+ irpc_handle = irpc_binding_handle_by_name(state, msg_ctx,
+ "winbind_server",
+ &ndr_table_winbind);
+ if (irpc_handle == NULL) {
+ DEBUG(0, ("Winbind authentication for [%s]\\[%s] failed, "
+ "no winbind_server running!\n",
+ user_info->client.domain_name, user_info->client.account_name));
+ tevent_req_nterror(req, NT_STATUS_NO_LOGON_SERVERS);
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * 120 seconds should be enough even for trusted domains.
+ *
+ * Currently winbindd has a much lower limit.
+ * And tests with Windows RODCs show that it
+ * returns NO_LOGON_SERVERS after 90-100 seconds
+ * if it can't reach any RWDC.
+ */
+ dcerpc_binding_handle_set_timeout(irpc_handle, 120);
+
+ if (user_info->flags & USER_INFO_INTERACTIVE_LOGON) {
+ struct netr_PasswordInfo *password_info;
+
+ status = encrypt_user_info(state, ctx->auth_ctx, AUTH_PASSWORD_HASH,
+ user_info, &user_info_new);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ user_info = user_info_new;
+
+ password_info = talloc_zero(state, struct netr_PasswordInfo);
+ if (tevent_req_nomem(password_info, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ password_info->lmpassword = *user_info->password.hash.lanman;
+ password_info->ntpassword = *user_info->password.hash.nt;
+
+ identity_info = &password_info->identity_info;
+ state->req.in.logon_level = 1;
+ state->req.in.logon.password= password_info;
+ } else {
+ struct netr_NetworkInfo *network_info;
+ uint8_t chal[8];
+
+ status = encrypt_user_info(state, ctx->auth_ctx, AUTH_PASSWORD_RESPONSE,
+ user_info, &user_info_new);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ user_info = user_info_new;
+
+ network_info = talloc_zero(state, struct netr_NetworkInfo);
+ if (tevent_req_nomem(network_info, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ status = auth_get_challenge(ctx->auth_ctx, chal);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ memcpy(network_info->challenge, chal, 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;
+ state->req.in.logon_level = 2;
+ state->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 = user_info->logon_id;
+ identity_info->account_name.string = user_info->client.account_name;
+ identity_info->workstation.string = user_info->workstation_name;
+
+ state->req.in.validation_level = 6;
+
+ subreq = dcerpc_winbind_SamLogon_r_send(state, ev, irpc_handle,
+ &state->req);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq,
+ winbind_check_password_done,
+ req);
+
+ return req;
+}
+
+static void winbind_check_password_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct winbind_check_password_state *state =
+ tevent_req_data(req,
+ struct winbind_check_password_state);
+ struct auth_method_context *ctx = state->ctx;
+ const struct auth_usersupplied_info *user_info = state->user_info;
+ struct ldb_dn *domain_dn = NULL;
+ const char *nt4_domain = NULL;
+ const char *nt4_account = NULL;
+ struct ldb_message *msg = NULL;
+ NTSTATUS status;
+
+ status = dcerpc_winbind_SamLogon_r_recv(subreq, state);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ status = NT_STATUS_NO_LOGON_SERVERS;
+ }
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ status = state->req.out.result;
+ if (!NT_STATUS_IS_OK(status)) {
+ if (!state->req.out.authoritative) {
+ state->authoritative = false;
+ }
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ status = make_user_info_dc_netlogon_validation(state,
+ user_info->client.account_name,
+ state->req.in.validation_level,
+ &state->req.out.validation,
+ true, /* This user was authenticated */
+ &state->user_info_dc);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ nt4_domain = state->user_info_dc->info->domain_name;
+ nt4_account = state->user_info_dc->info->account_name;
+
+ if (lpcfg_is_mydomain(ctx->auth_ctx->lp_ctx, nt4_domain)) {
+ domain_dn = ldb_get_default_basedn(ctx->auth_ctx->sam_ctx);
+ }
+
+ if (domain_dn != NULL) {
+ /*
+ * At best, reset the badPwdCount to 0 if the account exists.
+ * This means that lockouts happen at a badPwdCount earlier than
+ * normal, but makes it more fault tolerant.
+ */
+ status = authsam_search_account(state, ctx->auth_ctx->sam_ctx,
+ nt4_account, domain_dn, &msg);
+ if (NT_STATUS_IS_OK(status)) {
+ status = authsam_logon_success_accounting(
+ ctx->auth_ctx->sam_ctx, msg,
+ domain_dn,
+ user_info->flags & USER_INFO_INTERACTIVE_LOGON,
+ NULL, NULL);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ }
+ }
+
+ /*
+ * We need to expand group memberships within our local domain,
+ * as the token might be generated by a trusted domain, unless we're
+ * an RODC.
+ */
+ status = authsam_update_user_info_dc(state->user_info_dc,
+ ctx->auth_ctx->sam_ctx,
+ state->user_info_dc);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static NTSTATUS winbind_check_password_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct auth_user_info_dc **user_info_dc,
+ bool *pauthoritative)
+{
+ struct winbind_check_password_state *state =
+ tevent_req_data(req,
+ struct winbind_check_password_state);
+ NTSTATUS status = NT_STATUS_OK;
+
+ *pauthoritative = state->authoritative;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ *user_info_dc = talloc_move(mem_ctx, &state->user_info_dc);
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+static const struct auth_operations winbind_ops = {
+ .name = "winbind",
+ .want_check = winbind_want_check,
+ .check_password_send = winbind_check_password_send,
+ .check_password_recv = winbind_check_password_recv
+};
+
+_PUBLIC_ NTSTATUS auth4_winbind_init(TALLOC_CTX *ctx)
+{
+ NTSTATUS ret;
+
+ ret = auth_register(ctx, &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/wscript_build b/source4/auth/ntlm/wscript_build
new file mode 100644
index 0000000..3d96230
--- /dev/null
+++ b/source4/auth/ntlm/wscript_build
@@ -0,0 +1,51 @@
+#!/usr/bin/env python
+
+bld.SAMBA_MODULE('auth4_sam_module',
+ source='auth_sam.c',
+ subsystem='auth4',
+ init_function='auth4_sam_init',
+ deps='samdb auth4_sam NTLMSSP_COMMON samba-hostconfig RPC_NDR_IRPC MESSAGING db-glue',
+ enabled=bld.AD_DC_BUILD_IS_ENABLED()
+ )
+
+
+bld.SAMBA_MODULE('auth4_anonymous',
+ source='auth_anonymous.c',
+ subsystem='auth4',
+ init_function='auth4_anonymous_init',
+ deps='tevent'
+ )
+
+
+bld.SAMBA_MODULE('auth4_winbind',
+ source='auth_winbind.c',
+ subsystem='auth4',
+ init_function='auth4_winbind_init',
+ deps='RPC_NDR_WINBIND MESSAGING wbclient'
+ )
+
+
+bld.SAMBA_MODULE('auth4_developer',
+ source='auth_developer.c',
+ subsystem='auth4',
+ init_function='auth4_developer_init',
+ deps='tevent',
+ enabled=bld.env.DEVELOPER_MODE
+ )
+
+
+bld.SAMBA_LIBRARY('auth4',
+ source='auth.c auth_util.c auth_simple.c',
+ autoproto='auth_proto.h',
+ deps='samba-util samba-security samdb samba-credentials tevent-util LIBWBCLIENT_OLD auth_unix_token samba-modules KERBEROS_UTIL',
+ private_library=True
+ )
+
+bld.SAMBA_MODULE('service_auth',
+ source='auth_server_service.c',
+ subsystem='service',
+ init_function='server_service_auth_init',
+ deps='auth4',
+ internal_module=True
+ )
+