WHATSNEW: Update changes since 3.3.0pre2.
[Samba.git] / source / winbindd / winbindd_ccache_access.c
bloba696db1031a1e4e3df5d2ab2de4aff7255e10571
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind daemon - cached credentials funcions
6 Copyright (C) Robert O'Callahan 2006
7 Copyright (C) Jeremy Allison 2006 (minor fixes to fit into Samba and
8 protect against integer wrap).
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "includes.h"
25 #include "winbindd.h"
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_WINBIND
30 static bool client_can_access_ccache_entry(uid_t client_uid,
31 struct WINBINDD_MEMORY_CREDS *entry)
33 if (client_uid == entry->uid || client_uid == 0) {
34 DEBUG(10, ("Access granted to uid %d\n", client_uid));
35 return True;
38 DEBUG(1, ("Access denied to uid %d (expected %d)\n", client_uid, entry->uid));
39 return False;
42 static NTSTATUS do_ntlm_auth_with_hashes(const char *username,
43 const char *domain,
44 const unsigned char lm_hash[LM_HASH_LEN],
45 const unsigned char nt_hash[NT_HASH_LEN],
46 const DATA_BLOB initial_msg,
47 const DATA_BLOB challenge_msg,
48 DATA_BLOB *auth_msg)
50 NTSTATUS status;
51 NTLMSSP_STATE *ntlmssp_state = NULL;
52 DATA_BLOB dummy_msg, reply;
54 status = ntlmssp_client_start(&ntlmssp_state);
56 if (!NT_STATUS_IS_OK(status)) {
57 DEBUG(1, ("Could not start NTLMSSP client: %s\n",
58 nt_errstr(status)));
59 goto done;
62 status = ntlmssp_set_username(ntlmssp_state, username);
64 if (!NT_STATUS_IS_OK(status)) {
65 DEBUG(1, ("Could not set username: %s\n",
66 nt_errstr(status)));
67 goto done;
70 status = ntlmssp_set_domain(ntlmssp_state, domain);
72 if (!NT_STATUS_IS_OK(status)) {
73 DEBUG(1, ("Could not set domain: %s\n",
74 nt_errstr(status)));
75 goto done;
78 status = ntlmssp_set_hashes(ntlmssp_state, lm_hash, nt_hash);
80 if (!NT_STATUS_IS_OK(status)) {
81 DEBUG(1, ("Could not set hashes: %s\n",
82 nt_errstr(status)));
83 goto done;
86 /* We need to get our protocol handler into the right state. So first
87 we ask it to generate the initial message. Actually the client has already
88 sent its own initial message, so we're going to drop this one on the floor.
89 The client might have sent a different message, for example with different
90 negotiation options, but as far as I can tell this won't hurt us. (Unless
91 the client sent a different username or domain, in which case that's their
92 problem for telling us the wrong username or domain.)
93 Since we have a copy of the initial message that the client sent, we could
94 resolve any discrepancies if we had to.
96 dummy_msg = data_blob_null;
97 reply = data_blob_null;
98 status = ntlmssp_update(ntlmssp_state, dummy_msg, &reply);
99 data_blob_free(&dummy_msg);
100 data_blob_free(&reply);
102 if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
103 DEBUG(1, ("Failed to create initial message! [%s]\n",
104 nt_errstr(status)));
105 goto done;
108 /* Now we are ready to handle the server's actual response. */
109 status = ntlmssp_update(ntlmssp_state, challenge_msg, &reply);
111 if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
112 DEBUG(1, ("We didn't get a response to the challenge! [%s]\n",
113 nt_errstr(status)));
114 data_blob_free(&reply);
115 goto done;
117 *auth_msg = reply;
118 status = NT_STATUS_OK;
120 done:
121 ntlmssp_end(&ntlmssp_state);
122 return status;
125 static bool check_client_uid(struct winbindd_cli_state *state, uid_t uid)
127 int ret;
128 uid_t ret_uid;
130 ret_uid = (uid_t)-1;
132 ret = sys_getpeereid(state->sock, &ret_uid);
133 if (ret != 0) {
134 DEBUG(1, ("check_client_uid: Could not get socket peer uid: %s; "
135 "denying access\n", strerror(errno)));
136 return False;
139 if (uid != ret_uid) {
140 DEBUG(1, ("check_client_uid: Client lied about its uid: said %d, "
141 "actually was %d; denying access\n",
142 uid, ret_uid));
143 return False;
146 return True;
149 void winbindd_ccache_ntlm_auth(struct winbindd_cli_state *state)
151 struct winbindd_domain *domain;
152 fstring name_domain, name_user;
154 /* Ensure null termination */
155 state->request.data.ccache_ntlm_auth.user[
156 sizeof(state->request.data.ccache_ntlm_auth.user)-1]='\0';
158 DEBUG(3, ("[%5lu]: perform NTLM auth on behalf of user %s\n", (unsigned long)state->pid,
159 state->request.data.ccache_ntlm_auth.user));
161 /* Parse domain and username */
163 if (!canonicalize_username(state->request.data.ccache_ntlm_auth.user,
164 name_domain, name_user)) {
165 DEBUG(5,("winbindd_ccache_ntlm_auth: cannot parse domain and user from name [%s]\n",
166 state->request.data.ccache_ntlm_auth.user));
167 request_error(state);
168 return;
171 domain = find_auth_domain(state, name_domain);
173 if (domain == NULL) {
174 DEBUG(5,("winbindd_ccache_ntlm_auth: can't get domain [%s]\n",
175 name_domain));
176 request_error(state);
177 return;
180 if (!check_client_uid(state, state->request.data.ccache_ntlm_auth.uid)) {
181 request_error(state);
182 return;
185 sendto_domain(state, domain);
188 enum winbindd_result winbindd_dual_ccache_ntlm_auth(struct winbindd_domain *domain,
189 struct winbindd_cli_state *state)
191 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
192 struct WINBINDD_MEMORY_CREDS *entry;
193 DATA_BLOB initial, challenge, auth;
194 fstring name_domain, name_user;
195 uint32 initial_blob_len, challenge_blob_len, extra_len;
197 /* Ensure null termination */
198 state->request.data.ccache_ntlm_auth.user[
199 sizeof(state->request.data.ccache_ntlm_auth.user)-1]='\0';
201 DEBUG(3, ("winbindd_dual_ccache_ntlm_auth: [%5lu]: perform NTLM auth on "
202 "behalf of user %s (dual)\n", (unsigned long)state->pid,
203 state->request.data.ccache_ntlm_auth.user));
205 /* validate blob lengths */
206 initial_blob_len = state->request.data.ccache_ntlm_auth.initial_blob_len;
207 challenge_blob_len = state->request.data.ccache_ntlm_auth.challenge_blob_len;
208 extra_len = state->request.extra_len;
210 if (initial_blob_len > extra_len || challenge_blob_len > extra_len ||
211 initial_blob_len + challenge_blob_len > extra_len ||
212 initial_blob_len + challenge_blob_len < initial_blob_len ||
213 initial_blob_len + challenge_blob_len < challenge_blob_len) {
215 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: blob lengths overrun "
216 "or wrap. Buffer [%d+%d > %d]\n",
217 initial_blob_len,
218 challenge_blob_len,
219 extra_len));
220 goto process_result;
223 /* Parse domain and username */
224 if (!parse_domain_user(state->request.data.ccache_ntlm_auth.user, name_domain, name_user)) {
225 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: cannot parse "
226 "domain and user from name [%s]\n",
227 state->request.data.ccache_ntlm_auth.user));
228 goto process_result;
231 entry = find_memory_creds_by_name(state->request.data.ccache_ntlm_auth.user);
232 if (entry == NULL || entry->nt_hash == NULL || entry->lm_hash == NULL) {
233 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: could not find "
234 "credentials for user %s\n",
235 state->request.data.ccache_ntlm_auth.user));
236 goto process_result;
239 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: found ccache [%s]\n", entry->username));
241 if (!client_can_access_ccache_entry(state->request.data.ccache_ntlm_auth.uid, entry)) {
242 goto process_result;
245 if (initial_blob_len == 0 && challenge_blob_len == 0) {
246 /* this is just a probe to see if credentials are available. */
247 result = NT_STATUS_OK;
248 state->response.data.ccache_ntlm_auth.auth_blob_len = 0;
249 goto process_result;
252 initial = data_blob(state->request.extra_data.data, initial_blob_len);
253 challenge = data_blob(state->request.extra_data.data + initial_blob_len,
254 state->request.data.ccache_ntlm_auth.challenge_blob_len);
256 if (!initial.data || !challenge.data) {
257 result = NT_STATUS_NO_MEMORY;
258 } else {
259 result = do_ntlm_auth_with_hashes(name_user, name_domain,
260 entry->lm_hash, entry->nt_hash,
261 initial, challenge, &auth);
264 data_blob_free(&initial);
265 data_blob_free(&challenge);
267 if (!NT_STATUS_IS_OK(result)) {
268 goto process_result;
271 state->response.extra_data.data = smb_xmemdup(auth.data, auth.length);
272 if (!state->response.extra_data.data) {
273 result = NT_STATUS_NO_MEMORY;
274 goto process_result;
276 state->response.length += auth.length;
277 state->response.data.ccache_ntlm_auth.auth_blob_len = auth.length;
279 data_blob_free(&auth);
281 process_result:
282 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;