summaryrefslogtreecommitdiff
path: root/source3/nsswitch
diff options
context:
space:
mode:
authorJeremy Allison <jra@samba.org>2006-08-19 01:04:54 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 11:38:43 -0500
commitb41e14abfdc7301d5ae26ad95c031aa36c190fc8 (patch)
tree132c80c91e9ac1a43da7584b48a4a8218a20b9f1 /source3/nsswitch
parent986461b6be03eefd3bd9d9c5f5921e14189813b2 (diff)
downloadsamba-b41e14abfdc7301d5ae26ad95c031aa36c190fc8.tar.gz
samba-b41e14abfdc7301d5ae26ad95c031aa36c190fc8.tar.bz2
samba-b41e14abfdc7301d5ae26ad95c031aa36c190fc8.zip
r17610: Added the ability for firefox to drive the winbindd
ntlm_auth module to allow it to use winbindd cached credentials.The credentials are currently only stored in a krb5 MIT environment - we need to add an option to winbindd to allow passwords to be stored even in an NTLM-only environment. Patch from Robert O'Callahan, modified with some fixes by me. Jeremy. (This used to be commit ae7cc298a113d8984557684bd6ad216cbb27cff3)
Diffstat (limited to 'source3/nsswitch')
-rw-r--r--source3/nsswitch/winbindd.c3
-rw-r--r--source3/nsswitch/winbindd_ccache_access.c282
-rw-r--r--source3/nsswitch/winbindd_dual.c1
-rw-r--r--source3/nsswitch/winbindd_nss.h24
-rw-r--r--source3/nsswitch/winbindd_pam.c4
5 files changed, 310 insertions, 4 deletions
diff --git a/source3/nsswitch/winbindd.c b/source3/nsswitch/winbindd.c
index a4cd724e00..f6e742ac20 100644
--- a/source3/nsswitch/winbindd.c
+++ b/source3/nsswitch/winbindd.c
@@ -253,6 +253,9 @@ static struct winbindd_dispatch_table {
"WINBINDD_PRIV_PIPE_DIR" },
{ WINBINDD_GETDCNAME, winbindd_getdcname, "GETDCNAME" },
+ /* Credential cache access */
+ { WINBINDD_CCACHE_NTLMAUTH, winbindd_ccache_ntlm_auth, "NTLMAUTH" },
+
/* WINS functions */
{ WINBINDD_WINS_BYNAME, winbindd_wins_byname, "WINS_BYNAME" },
diff --git a/source3/nsswitch/winbindd_ccache_access.c b/source3/nsswitch/winbindd_ccache_access.c
new file mode 100644
index 0000000000..35d2cb4e51
--- /dev/null
+++ b/source3/nsswitch/winbindd_ccache_access.c
@@ -0,0 +1,282 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon - cached credentials funcions
+
+ Copyright (C) Robert O'Callahan 2006
+ Copyright (C) Jeremy Allison 2006 (minor fixes to fit into Samba and
+ protect against integer wrap).
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+static BOOL client_can_access_ccache_entry(uid_t client_uid,
+ struct WINBINDD_CCACHE_ENTRY *entry)
+{
+ if (client_uid == entry->uid || client_uid == 0) {
+ DEBUG(10, ("Access granted to uid %d\n", client_uid));
+ return True;
+ }
+
+ DEBUG(1, ("Access denied to uid %d (expected %d)\n", client_uid, entry->uid));
+ return False;
+}
+
+static NTSTATUS do_ntlm_auth_with_password(const char *username,
+ const char *domain,
+ const char *password,
+ const DATA_BLOB initial_msg,
+ const DATA_BLOB challenge_msg,
+ DATA_BLOB *auth_msg)
+{
+ NTSTATUS status;
+ NTLMSSP_STATE *ntlmssp_state = NULL;
+ DATA_BLOB dummy_msg, reply;
+
+ status = ntlmssp_client_start(&ntlmssp_state);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not start NTLMSSP client: %s\n",
+ nt_errstr(status)));
+ goto done;
+ }
+
+ status = ntlmssp_set_username(ntlmssp_state, username);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not set username: %s\n",
+ nt_errstr(status)));
+ goto done;
+ }
+
+ status = ntlmssp_set_domain(ntlmssp_state, domain);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not set domain: %s\n",
+ nt_errstr(status)));
+ goto done;
+ }
+
+ status = ntlmssp_set_password(ntlmssp_state, password);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not set password: %s\n",
+ nt_errstr(status)));
+ goto done;
+ }
+
+ /* We need to get our protocol handler into the right state. So first
+ we ask it to generate the initial message. Actually the client has already
+ sent its own initial message, so we're going to drop this one on the floor.
+ The client might have sent a different message, for example with different
+ negotiation options, but as far as I can tell this won't hurt us. (Unless
+ the client sent a different username or domain, in which case that's their
+ problem for telling us the wrong username or domain.)
+ Since we have a copy of the initial message that the client sent, we could
+ resolve any discrepancies if we had to.
+ */
+ dummy_msg = data_blob(NULL, 0);
+ reply = data_blob(NULL, 0);
+ status = ntlmssp_update(ntlmssp_state, dummy_msg, &reply);
+ data_blob_free(&dummy_msg);
+ data_blob_free(&reply);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ DEBUG(1, ("Failed to create initial message! [%s]\n",
+ nt_errstr(status)));
+ goto done;
+ }
+
+ /* Now we are ready to handle the server's actual response. */
+ status = ntlmssp_update(ntlmssp_state, challenge_msg, &reply);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
+ DEBUG(1, ("We didn't get a response to the challenge! [%s]\n",
+ nt_errstr(status)));
+ data_blob_free(&reply);
+ goto done;
+ }
+ *auth_msg = reply;
+ status = NT_STATUS_OK;
+
+done:
+ ntlmssp_end(&ntlmssp_state);
+ return status;
+}
+
+static BOOL check_client_uid(struct winbindd_cli_state *state, uid_t uid)
+{
+ int ret;
+ uid_t ret_uid;
+
+ ret_uid = (uid_t)-1;
+
+ ret = getpeereid(state->sock, &ret_uid);
+ if (ret != 0) {
+ DEBUG(1, ("check_client_uid: Could not get socket peer uid: %s; "
+ "denying access\n", strerror(errno)));
+ return False;
+ }
+
+ if (uid != ret_uid) {
+ DEBUG(1, ("check_client_uid: Client lied about its uid: said %d, "
+ "actually was %d; denying access\n",
+ uid, ret_uid));
+ return False;
+ }
+
+ return True;
+}
+
+void winbindd_ccache_ntlm_auth(struct winbindd_cli_state *state)
+{
+ struct winbindd_domain *domain;
+ fstring name_domain, name_user;
+
+ /* Ensure null termination */
+ state->request.data.ccache_ntlm_auth.user[
+ sizeof(state->request.data.ccache_ntlm_auth.user)-1]='\0';
+
+ DEBUG(3, ("[%5lu]: perform NTLM auth on behalf of user %s\n", (unsigned long)state->pid,
+ state->request.data.ccache_ntlm_auth.user));
+
+ /* Parse domain and username */
+
+ if (!parse_domain_user(state->request.data.ccache_ntlm_auth.user,
+ name_domain, name_user)) {
+ DEBUG(5,("winbindd_ccache_ntlm_auth: cannot parse domain and user from name [%s]\n",
+ state->request.data.ccache_ntlm_auth.user));
+ request_error(state);
+ return;
+ }
+
+ domain = find_auth_domain(state, name_domain);
+
+ if (domain == NULL) {
+ DEBUG(5,("winbindd_ccache_ntlm_auth: can't get domain [%s]\n",
+ name_domain));
+ request_error(state);
+ return;
+ }
+
+ if (!check_client_uid(state, state->request.data.ccache_ntlm_auth.uid)) {
+ request_error(state);
+ return;
+ }
+
+ sendto_domain(state, domain);
+}
+
+enum winbindd_result winbindd_dual_ccache_ntlm_auth(struct winbindd_domain *domain,
+ struct winbindd_cli_state *state)
+{
+ NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
+ struct WINBINDD_CCACHE_ENTRY *entry;
+ DATA_BLOB initial, challenge, auth;
+ fstring name_domain, name_user;
+ uint32 initial_blob_len, challenge_blob_len, extra_len;
+
+ /* Ensure null termination */
+ state->request.data.ccache_ntlm_auth.user[
+ sizeof(state->request.data.ccache_ntlm_auth.user)-1]='\0';
+
+ DEBUG(3, ("winbindd_dual_ccache_ntlm_auth: [%5lu]: perform NTLM auth on "
+ "behalf of user %s (dual)\n", (unsigned long)state->pid,
+ state->request.data.ccache_ntlm_auth.user));
+
+ /* validate blob lengths */
+ initial_blob_len = state->request.data.ccache_ntlm_auth.initial_blob_len;
+ challenge_blob_len = state->request.data.ccache_ntlm_auth.challenge_blob_len;
+ extra_len = state->request.extra_len;
+
+ if (initial_blob_len > extra_len || challenge_blob_len > extra_len ||
+ initial_blob_len + challenge_blob_len > extra_len ||
+ initial_blob_len + challenge_blob_len < initial_blob_len ||
+ initial_blob_len + challenge_blob_len < challenge_blob_len) {
+
+ DEBUG(10,("winbindd_dual_ccache_ntlm_auth: blob lengths overrun "
+ "or wrap. Buffer [%d+%d > %d]\n",
+ initial_blob_len,
+ challenge_blob_len,
+ extra_len));
+ goto process_result;
+ }
+
+ /* Parse domain and username */
+ if (!parse_domain_user(state->request.data.ccache_ntlm_auth.user, name_domain, name_user)) {
+ DEBUG(10,("winbindd_dual_ccache_ntlm_auth: cannot parse "
+ "domain and user from name [%s]\n",
+ state->request.data.ccache_ntlm_auth.user));
+ goto process_result;
+ }
+
+ entry = get_ccache_by_username(state->request.data.ccache_ntlm_auth.user);
+ if (entry == NULL) {
+ DEBUG(10,("winbindd_dual_ccache_ntlm_auth: could not find "
+ "credentials for user %s\n",
+ state->request.data.ccache_ntlm_auth.user));
+ goto process_result;
+ }
+
+ DEBUG(10,("winbindd_dual_ccache_ntlm_auth: found ccache [%s]\n", entry->ccname));
+
+ if (!client_can_access_ccache_entry(state->request.data.ccache_ntlm_auth.uid, entry)) {
+ goto process_result;
+ }
+
+ if (initial_blob_len == 0 && challenge_blob_len == 0) {
+ /* this is just a probe to see if credentials are available. */
+ result = NT_STATUS_OK;
+ state->response.data.ccache_ntlm_auth.auth_blob_len = 0;
+ goto process_result;
+ }
+
+ initial = data_blob(state->request.extra_data.data, initial_blob_len);
+ challenge = data_blob(state->request.extra_data.data + initial_blob_len,
+ state->request.data.ccache_ntlm_auth.challenge_blob_len);
+
+ if (!initial.data || !challenge.data) {
+ result = NT_STATUS_NO_MEMORY;
+ } else {
+ result = do_ntlm_auth_with_password(name_user, name_domain, entry->pass,
+ initial, challenge, &auth);
+ }
+
+ data_blob_free(&initial);
+ data_blob_free(&challenge);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto process_result;
+ }
+
+ state->response.extra_data.data = smb_xmemdup(auth.data, auth.length);
+ if (!state->response.extra_data.data) {
+ result = NT_STATUS_NO_MEMORY;
+ goto process_result;
+ }
+ state->response.length += auth.length;
+ state->response.data.ccache_ntlm_auth.auth_blob_len = auth.length;
+
+ data_blob_free(&auth);
+
+ process_result:
+ return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
+}
diff --git a/source3/nsswitch/winbindd_dual.c b/source3/nsswitch/winbindd_dual.c
index 32d85688b6..55f897603c 100644
--- a/source3/nsswitch/winbindd_dual.c
+++ b/source3/nsswitch/winbindd_dual.c
@@ -369,6 +369,7 @@ static struct winbindd_child_dispatch_table child_dispatch_table[] = {
{ WINBINDD_ALLOCATE_GID, winbindd_dual_allocate_gid, "ALLOCATE_GID" },
{ WINBINDD_GETUSERDOMGROUPS, winbindd_dual_getuserdomgroups, "GETUSERDOMGROUPS" },
{ WINBINDD_DUAL_GETSIDALIASES, winbindd_dual_getsidaliases, "GETSIDALIASES" },
+ { WINBINDD_CCACHE_NTLMAUTH, winbindd_dual_ccache_ntlm_auth, "CCACHE_NTLM_AUTH" },
/* End of list */
{ WINBINDD_NUM_CMDS, NULL, "NONE" }
diff --git a/source3/nsswitch/winbindd_nss.h b/source3/nsswitch/winbindd_nss.h
index 4a95a3cf42..b19026b4db 100644
--- a/source3/nsswitch/winbindd_nss.h
+++ b/source3/nsswitch/winbindd_nss.h
@@ -35,7 +35,7 @@
/* Update this when you change the interface. */
-#define WINBIND_INTERFACE_VERSION 16
+#define WINBIND_INTERFACE_VERSION 17
/* Have to deal with time_t being 4 or 8 bytes due to structure alignment.
On a 64bit Linux box, we have to support a constant structure size
@@ -153,6 +153,10 @@ enum winbindd_cmd {
WINBINDD_DUAL_USERINFO,
WINBINDD_DUAL_GETSIDALIASES,
+ /* Complete the challenge phase of the NTLM authentication
+ protocol using cached password. */
+ WINBINDD_CCACHE_NTLMAUTH,
+
WINBINDD_NUM_CMDS
};
@@ -292,8 +296,21 @@ struct winbindd_request {
} dual_idmapset;
BOOL list_all_domains;
+ struct {
+ uid_t uid;
+ fstring user;
+ /* the effective uid of the client, must be the uid for 'user'.
+ This is checked by the main daemon, trusted by children. */
+ /* if the blobs are length zero, then this doesn't
+ produce an actual challenge response. It merely
+ succeeds if there are cached credentials available
+ that could be used. */
+ uint32 initial_blob_len; /* blobs in extra_data */
+ uint32 challenge_blob_len;
+ } ccache_ntlm_auth;
+
/* padding -- needed to fix alignment between 32bit and 64bit libs.
- The size if the sizeof the union without the padding aligned on
+ The size is the sizeof the union without the padding aligned on
an 8 byte boundary. --jerry */
char padding[1560];
@@ -426,6 +443,9 @@ struct winbindd_response {
fstring shell;
uint32 group_rid;
} user_info;
+ struct {
+ uint32 auth_blob_len; /* blob in extra_data */
+ } ccache_ntlm_auth;
} data;
/* Variable length return data */
diff --git a/source3/nsswitch/winbindd_pam.c b/source3/nsswitch/winbindd_pam.c
index 66d297090b..365b277160 100644
--- a/source3/nsswitch/winbindd_pam.c
+++ b/source3/nsswitch/winbindd_pam.c
@@ -200,8 +200,8 @@ static NTSTATUS check_info3_in_group(TALLOC_CTX *mem_ctx,
return NT_STATUS_LOGON_FAILURE;
}
-static struct winbindd_domain *find_auth_domain(struct winbindd_cli_state *state,
- const char *domain_name)
+struct winbindd_domain *find_auth_domain(struct winbindd_cli_state *state,
+ const char *domain_name)
{
struct winbindd_domain *domain;