source: branches/samba-3.5.x/source3/winbindd/winbindd_ccache_access.c

Last change on this file was 414, checked in by Herwig Bauernfeind, 15 years ago

Samba 3.5.0: Initial import

File size: 11.0 KB
Line 
1/*
2 Unix SMB/CIFS implementation.
3
4 Winbind daemon - cached credentials funcions
5
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).
9
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.
14
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.
19
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/>.
22*/
23
24#include "includes.h"
25#include "winbindd.h"
26
27#undef DBGC_CLASS
28#define DBGC_CLASS DBGC_WINBIND
29
30static bool client_can_access_ccache_entry(uid_t client_uid,
31 struct WINBINDD_MEMORY_CREDS *entry)
32{
33 if (client_uid == entry->uid || client_uid == 0) {
34 DEBUG(10, ("Access granted to uid %u\n", (unsigned int)client_uid));
35 return True;
36 }
37
38 DEBUG(1, ("Access denied to uid %u (expected %u)\n",
39 (unsigned int)client_uid, (unsigned int)entry->uid));
40 return False;
41}
42
43static NTSTATUS do_ntlm_auth_with_hashes(const char *username,
44 const char *domain,
45 const unsigned char lm_hash[LM_HASH_LEN],
46 const unsigned char nt_hash[NT_HASH_LEN],
47 const DATA_BLOB initial_msg,
48 const DATA_BLOB challenge_msg,
49 DATA_BLOB *auth_msg,
50 uint8_t session_key[16])
51{
52 NTSTATUS status;
53 NTLMSSP_STATE *ntlmssp_state = NULL;
54 DATA_BLOB dummy_msg, reply;
55
56 status = ntlmssp_client_start(&ntlmssp_state);
57
58 if (!NT_STATUS_IS_OK(status)) {
59 DEBUG(1, ("Could not start NTLMSSP client: %s\n",
60 nt_errstr(status)));
61 goto done;
62 }
63
64 status = ntlmssp_set_username(ntlmssp_state, username);
65
66 if (!NT_STATUS_IS_OK(status)) {
67 DEBUG(1, ("Could not set username: %s\n",
68 nt_errstr(status)));
69 goto done;
70 }
71
72 status = ntlmssp_set_domain(ntlmssp_state, domain);
73
74 if (!NT_STATUS_IS_OK(status)) {
75 DEBUG(1, ("Could not set domain: %s\n",
76 nt_errstr(status)));
77 goto done;
78 }
79
80 status = ntlmssp_set_hashes(ntlmssp_state, lm_hash, nt_hash);
81
82 if (!NT_STATUS_IS_OK(status)) {
83 DEBUG(1, ("Could not set hashes: %s\n",
84 nt_errstr(status)));
85 goto done;
86 }
87
88 ntlmssp_want_feature(ntlmssp_state, NTLMSSP_FEATURE_SESSION_KEY);
89
90 /* We need to get our protocol handler into the right state. So first
91 we ask it to generate the initial message. Actually the client has already
92 sent its own initial message, so we're going to drop this one on the floor.
93 The client might have sent a different message, for example with different
94 negotiation options, but as far as I can tell this won't hurt us. (Unless
95 the client sent a different username or domain, in which case that's their
96 problem for telling us the wrong username or domain.)
97 Since we have a copy of the initial message that the client sent, we could
98 resolve any discrepancies if we had to.
99 */
100 dummy_msg = data_blob_null;
101 reply = data_blob_null;
102 status = ntlmssp_update(ntlmssp_state, dummy_msg, &reply);
103 data_blob_free(&dummy_msg);
104 data_blob_free(&reply);
105
106 if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
107 DEBUG(1, ("Failed to create initial message! [%s]\n",
108 nt_errstr(status)));
109 goto done;
110 }
111
112 /* Now we are ready to handle the server's actual response. */
113 status = ntlmssp_update(ntlmssp_state, challenge_msg, &reply);
114
115 if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
116 DEBUG(1, ("We didn't get a response to the challenge! [%s]\n",
117 nt_errstr(status)));
118 data_blob_free(&reply);
119 goto done;
120 }
121
122 if (ntlmssp_state->session_key.length != 16) {
123 DEBUG(1, ("invalid session key length %d\n",
124 (int)ntlmssp_state->session_key.length));
125 data_blob_free(&reply);
126 goto done;
127 }
128
129 *auth_msg = data_blob(reply.data, reply.length);
130 memcpy(session_key, ntlmssp_state->session_key.data, 16);
131 status = NT_STATUS_OK;
132
133done:
134 ntlmssp_end(&ntlmssp_state);
135 return status;
136}
137
138static bool check_client_uid(struct winbindd_cli_state *state, uid_t uid)
139{
140 int ret;
141 uid_t ret_uid;
142
143 ret_uid = (uid_t)-1;
144
145 ret = sys_getpeereid(state->sock, &ret_uid);
146 if (ret != 0) {
147 DEBUG(1, ("check_client_uid: Could not get socket peer uid: %s; "
148 "denying access\n", strerror(errno)));
149 return False;
150 }
151
152 if (uid != ret_uid) {
153 DEBUG(1, ("check_client_uid: Client lied about its uid: said %u, "
154 "actually was %u; denying access\n",
155 (unsigned int)uid, (unsigned int)ret_uid));
156 return False;
157 }
158
159 return True;
160}
161
162void winbindd_ccache_ntlm_auth(struct winbindd_cli_state *state)
163{
164 struct winbindd_domain *domain;
165 fstring name_domain, name_user;
166
167 /* Ensure null termination */
168 state->request->data.ccache_ntlm_auth.user[
169 sizeof(state->request->data.ccache_ntlm_auth.user)-1]='\0';
170
171 DEBUG(3, ("[%5lu]: perform NTLM auth on behalf of user %s\n", (unsigned long)state->pid,
172 state->request->data.ccache_ntlm_auth.user));
173
174 /* Parse domain and username */
175
176 if (!canonicalize_username(state->request->data.ccache_ntlm_auth.user,
177 name_domain, name_user)) {
178 DEBUG(5,("winbindd_ccache_ntlm_auth: cannot parse domain and user from name [%s]\n",
179 state->request->data.ccache_ntlm_auth.user));
180 request_error(state);
181 return;
182 }
183
184 domain = find_auth_domain(state->request->flags, name_domain);
185
186 if (domain == NULL) {
187 DEBUG(5,("winbindd_ccache_ntlm_auth: can't get domain [%s]\n",
188 name_domain));
189 request_error(state);
190 return;
191 }
192
193 if (!check_client_uid(state, state->request->data.ccache_ntlm_auth.uid)) {
194 request_error(state);
195 return;
196 }
197
198 sendto_domain(state, domain);
199}
200
201enum winbindd_result winbindd_dual_ccache_ntlm_auth(struct winbindd_domain *domain,
202 struct winbindd_cli_state *state)
203{
204 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
205 struct WINBINDD_MEMORY_CREDS *entry;
206 DATA_BLOB initial, challenge, auth;
207 fstring name_domain, name_user;
208 uint32 initial_blob_len, challenge_blob_len, extra_len;
209
210 /* Ensure null termination */
211 state->request->data.ccache_ntlm_auth.user[
212 sizeof(state->request->data.ccache_ntlm_auth.user)-1]='\0';
213
214 DEBUG(3, ("winbindd_dual_ccache_ntlm_auth: [%5lu]: perform NTLM auth on "
215 "behalf of user %s (dual)\n", (unsigned long)state->pid,
216 state->request->data.ccache_ntlm_auth.user));
217
218 /* validate blob lengths */
219 initial_blob_len = state->request->data.ccache_ntlm_auth.initial_blob_len;
220 challenge_blob_len = state->request->data.ccache_ntlm_auth.challenge_blob_len;
221 extra_len = state->request->extra_len;
222
223 if (initial_blob_len > extra_len || challenge_blob_len > extra_len ||
224 initial_blob_len + challenge_blob_len > extra_len ||
225 initial_blob_len + challenge_blob_len < initial_blob_len ||
226 initial_blob_len + challenge_blob_len < challenge_blob_len) {
227
228 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: blob lengths overrun "
229 "or wrap. Buffer [%d+%d > %d]\n",
230 initial_blob_len,
231 challenge_blob_len,
232 extra_len));
233 goto process_result;
234 }
235
236 /* Parse domain and username */
237 if (!parse_domain_user(state->request->data.ccache_ntlm_auth.user, name_domain, name_user)) {
238 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: cannot parse "
239 "domain and user from name [%s]\n",
240 state->request->data.ccache_ntlm_auth.user));
241 goto process_result;
242 }
243
244 entry = find_memory_creds_by_name(state->request->data.ccache_ntlm_auth.user);
245 if (entry == NULL || entry->nt_hash == NULL || entry->lm_hash == NULL) {
246 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: could not find "
247 "credentials for user %s\n",
248 state->request->data.ccache_ntlm_auth.user));
249 goto process_result;
250 }
251
252 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: found ccache [%s]\n", entry->username));
253
254 if (!client_can_access_ccache_entry(state->request->data.ccache_ntlm_auth.uid, entry)) {
255 goto process_result;
256 }
257
258 if (initial_blob_len == 0 && challenge_blob_len == 0) {
259 /* this is just a probe to see if credentials are available. */
260 result = NT_STATUS_OK;
261 state->response->data.ccache_ntlm_auth.auth_blob_len = 0;
262 goto process_result;
263 }
264
265 initial = data_blob(state->request->extra_data.data, initial_blob_len);
266 challenge = data_blob(state->request->extra_data.data + initial_blob_len,
267 state->request->data.ccache_ntlm_auth.challenge_blob_len);
268
269 if (!initial.data || !challenge.data) {
270 result = NT_STATUS_NO_MEMORY;
271 } else {
272 result = do_ntlm_auth_with_hashes(
273 name_user, name_domain, entry->lm_hash, entry->nt_hash,
274 initial, challenge, &auth,
275 state->response->data.ccache_ntlm_auth.session_key);
276 }
277
278 data_blob_free(&initial);
279 data_blob_free(&challenge);
280
281 if (!NT_STATUS_IS_OK(result)) {
282 goto process_result;
283 }
284
285 state->response->extra_data.data = talloc_memdup(
286 state->mem_ctx, auth.data, auth.length);
287 if (!state->response->extra_data.data) {
288 result = NT_STATUS_NO_MEMORY;
289 goto process_result;
290 }
291 state->response->length += auth.length;
292 state->response->data.ccache_ntlm_auth.auth_blob_len = auth.length;
293
294 data_blob_free(&auth);
295
296 process_result:
297 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
298}
299
300void winbindd_ccache_save(struct winbindd_cli_state *state)
301{
302 struct winbindd_domain *domain;
303 fstring name_domain, name_user;
304
305 /* Ensure null termination */
306 state->request->data.ccache_save.user[
307 sizeof(state->request->data.ccache_save.user)-1]='\0';
308 state->request->data.ccache_save.pass[
309 sizeof(state->request->data.ccache_save.pass)-1]='\0';
310
311 DEBUG(3, ("[%5lu]: save password of user %s\n",
312 (unsigned long)state->pid,
313 state->request->data.ccache_save.user));
314
315 /* Parse domain and username */
316
317 if (!canonicalize_username(state->request->data.ccache_ntlm_auth.user,
318 name_domain, name_user)) {
319 DEBUG(5,("winbindd_ccache_save: cannot parse domain and user "
320 "from name [%s]\n",
321 state->request->data.ccache_save.user));
322 request_error(state);
323 return;
324 }
325
326 domain = find_auth_domain(state->request->flags, name_domain);
327
328 if (domain == NULL) {
329 DEBUG(5, ("winbindd_ccache_save: can't get domain [%s]\n",
330 name_domain));
331 request_error(state);
332 return;
333 }
334
335 if (!check_client_uid(state, state->request->data.ccache_save.uid)) {
336 request_error(state);
337 return;
338 }
339
340 sendto_domain(state, domain);
341}
342
343enum winbindd_result winbindd_dual_ccache_save(
344 struct winbindd_domain *domain, struct winbindd_cli_state *state)
345{
346 NTSTATUS status = NT_STATUS_NOT_SUPPORTED;
347
348 /* Ensure null termination */
349 state->request->data.ccache_save.user[
350 sizeof(state->request->data.ccache_save.user)-1]='\0';
351 state->request->data.ccache_save.pass[
352 sizeof(state->request->data.ccache_save.pass)-1]='\0';
353
354 DEBUG(3, ("winbindd_dual_ccache_save: [%5lu]: save password of user "
355 "%s\n", (unsigned long)state->pid,
356 state->request->data.ccache_save.user));
357
358 status = winbindd_add_memory_creds(
359 state->request->data.ccache_save.user,
360 state->request->data.ccache_save.uid,
361 state->request->data.ccache_save.pass);
362
363 if (!NT_STATUS_IS_OK(status)) {
364 DEBUG(1, ("winbindd_add_memory_creds failed %s\n",
365 nt_errstr(status)));
366 return WINBINDD_ERROR;
367 }
368
369 return WINBINDD_OK;
370}
Note: See TracBrowser for help on using the repository browser.