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