source: trunk/server/source3/winbindd/winbindd_ccache_access.c

Last change on this file was 745, checked in by Silvan Scherrer, 13 years ago

Samba Server: updated trunk to 3.6.0

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