s3: Update waf build to include missed dependancy on Lion.
[Samba/gebeck_regimport.git] / source3 / winbindd / winbindd_ccache_access.c
blob411b2b4c3ad5cd8c11ab8a3b258f63e051b352e0
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).
9 Copyright (C) Andrew Bartlett 2011
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 3 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "includes.h"
26 #include "winbindd.h"
27 #include "auth/gensec/gensec.h"
28 #include "auth_generic.h"
30 #undef DBGC_CLASS
31 #define DBGC_CLASS DBGC_WINBIND
33 static bool client_can_access_ccache_entry(uid_t client_uid,
34 struct WINBINDD_MEMORY_CREDS *entry)
36 if (client_uid == entry->uid || client_uid == 0) {
37 DEBUG(10, ("Access granted to uid %u\n", (unsigned int)client_uid));
38 return True;
41 DEBUG(1, ("Access denied to uid %u (expected %u)\n",
42 (unsigned int)client_uid, (unsigned int)entry->uid));
43 return False;
46 static NTSTATUS do_ntlm_auth_with_stored_pw(const char *username,
47 const char *domain,
48 const char *password,
49 const DATA_BLOB initial_msg,
50 const DATA_BLOB challenge_msg,
51 DATA_BLOB *auth_msg,
52 uint8_t session_key[16])
54 NTSTATUS status;
55 struct auth_generic_state *auth_generic_state = NULL;
56 DATA_BLOB dummy_msg, reply, session_key_blob;
58 status = auth_generic_client_prepare(NULL, &auth_generic_state);
60 if (!NT_STATUS_IS_OK(status)) {
61 DEBUG(1, ("Could not start NTLMSSP client: %s\n",
62 nt_errstr(status)));
63 goto done;
66 status = auth_generic_set_username(auth_generic_state, username);
68 if (!NT_STATUS_IS_OK(status)) {
69 DEBUG(1, ("Could not set username: %s\n",
70 nt_errstr(status)));
71 goto done;
74 status = auth_generic_set_domain(auth_generic_state, domain);
76 if (!NT_STATUS_IS_OK(status)) {
77 DEBUG(1, ("Could not set domain: %s\n",
78 nt_errstr(status)));
79 goto done;
82 status = auth_generic_set_password(auth_generic_state, password);
84 if (!NT_STATUS_IS_OK(status)) {
85 DEBUG(1, ("Could not set password: %s\n",
86 nt_errstr(status)));
87 goto done;
90 gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SESSION_KEY);
92 status = auth_generic_client_start(auth_generic_state, GENSEC_OID_NTLMSSP);
93 if (!NT_STATUS_IS_OK(status)) {
94 DEBUG(1, ("Could not start NTLMSSP mech: %s\n",
95 nt_errstr(status)));
96 goto done;
99 /* We need to get our protocol handler into the right state. So first
100 we ask it to generate the initial message. Actually the client has already
101 sent its own initial message, so we're going to drop this one on the floor.
102 The client might have sent a different message, for example with different
103 negotiation options, but as far as I can tell this won't hurt us. (Unless
104 the client sent a different username or domain, in which case that's their
105 problem for telling us the wrong username or domain.)
106 Since we have a copy of the initial message that the client sent, we could
107 resolve any discrepancies if we had to.
109 dummy_msg = data_blob_null;
110 reply = data_blob_null;
111 status = gensec_update(auth_generic_state->gensec_security,
112 talloc_tos(), NULL, dummy_msg, &reply);
113 data_blob_free(&reply);
115 if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
116 DEBUG(1, ("Failed to create initial message! [%s]\n",
117 nt_errstr(status)));
118 goto done;
121 /* Now we are ready to handle the server's actual response. */
122 status = gensec_update(auth_generic_state->gensec_security,
123 NULL, NULL, challenge_msg, &reply);
124 if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
125 DEBUG(1, ("We didn't get a response to the challenge! [%s]\n",
126 nt_errstr(status)));
127 data_blob_free(&reply);
128 goto done;
131 status = gensec_session_key(auth_generic_state->gensec_security,
132 talloc_tos(), &session_key_blob);
133 if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
134 DEBUG(1, ("We didn't get the session key we requested! [%s]\n",
135 nt_errstr(status)));
136 data_blob_free(&reply);
137 goto done;
140 if (session_key_blob.length != 16) {
141 DEBUG(1, ("invalid session key length %d\n",
142 (int)session_key_blob.length));
143 data_blob_free(&reply);
144 goto done;
146 memcpy(session_key, session_key_blob.data, 16);
147 data_blob_free(&session_key_blob);
148 *auth_msg = reply;
149 status = NT_STATUS_OK;
151 done:
152 TALLOC_FREE(auth_generic_state);
153 return status;
156 static bool check_client_uid(struct winbindd_cli_state *state, uid_t uid)
158 int ret;
159 uid_t ret_uid;
161 ret_uid = (uid_t)-1;
163 ret = sys_getpeereid(state->sock, &ret_uid);
164 if (ret != 0) {
165 DEBUG(1, ("check_client_uid: Could not get socket peer uid: %s; "
166 "denying access\n", strerror(errno)));
167 return False;
170 if (uid != ret_uid) {
171 DEBUG(1, ("check_client_uid: Client lied about its uid: said %u, "
172 "actually was %u; denying access\n",
173 (unsigned int)uid, (unsigned int)ret_uid));
174 return False;
177 return True;
180 void winbindd_ccache_ntlm_auth(struct winbindd_cli_state *state)
182 struct winbindd_domain *domain;
183 fstring name_domain, name_user;
184 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
185 struct WINBINDD_MEMORY_CREDS *entry;
186 DATA_BLOB initial, challenge, auth;
187 uint32 initial_blob_len, challenge_blob_len, extra_len;
189 /* Ensure null termination */
190 state->request->data.ccache_ntlm_auth.user[
191 sizeof(state->request->data.ccache_ntlm_auth.user)-1]='\0';
193 DEBUG(3, ("[%5lu]: perform NTLM auth on behalf of user %s\n", (unsigned long)state->pid,
194 state->request->data.ccache_ntlm_auth.user));
196 /* Parse domain and username */
198 if (!canonicalize_username(state->request->data.ccache_ntlm_auth.user,
199 name_domain, name_user)) {
200 DEBUG(5,("winbindd_ccache_ntlm_auth: cannot parse domain and user from name [%s]\n",
201 state->request->data.ccache_ntlm_auth.user));
202 request_error(state);
203 return;
206 domain = find_auth_domain(state->request->flags, name_domain);
208 if (domain == NULL) {
209 DEBUG(5,("winbindd_ccache_ntlm_auth: can't get domain [%s]\n",
210 name_domain));
211 request_error(state);
212 return;
215 if (!check_client_uid(state, state->request->data.ccache_ntlm_auth.uid)) {
216 request_error(state);
217 return;
220 /* validate blob lengths */
221 initial_blob_len = state->request->data.ccache_ntlm_auth.initial_blob_len;
222 challenge_blob_len = state->request->data.ccache_ntlm_auth.challenge_blob_len;
223 extra_len = state->request->extra_len;
225 if (initial_blob_len > extra_len || challenge_blob_len > extra_len ||
226 initial_blob_len + challenge_blob_len > extra_len ||
227 initial_blob_len + challenge_blob_len < initial_blob_len ||
228 initial_blob_len + challenge_blob_len < challenge_blob_len) {
230 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: blob lengths overrun "
231 "or wrap. Buffer [%d+%d > %d]\n",
232 initial_blob_len,
233 challenge_blob_len,
234 extra_len));
235 goto process_result;
238 /* Parse domain and username */
239 if (!parse_domain_user(state->request->data.ccache_ntlm_auth.user, name_domain, name_user)) {
240 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: cannot parse "
241 "domain and user from name [%s]\n",
242 state->request->data.ccache_ntlm_auth.user));
243 goto process_result;
246 entry = find_memory_creds_by_name(state->request->data.ccache_ntlm_auth.user);
247 if (entry == NULL || entry->nt_hash == NULL || entry->lm_hash == NULL) {
248 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: could not find "
249 "credentials for user %s\n",
250 state->request->data.ccache_ntlm_auth.user));
251 goto process_result;
254 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: found ccache [%s]\n", entry->username));
256 if (!client_can_access_ccache_entry(state->request->data.ccache_ntlm_auth.uid, entry)) {
257 goto process_result;
260 if (initial_blob_len == 0 && challenge_blob_len == 0) {
261 /* this is just a probe to see if credentials are available. */
262 result = NT_STATUS_OK;
263 state->response->data.ccache_ntlm_auth.auth_blob_len = 0;
264 goto process_result;
267 initial = data_blob_const(state->request->extra_data.data,
268 initial_blob_len);
269 challenge = data_blob_const(
270 state->request->extra_data.data + initial_blob_len,
271 state->request->data.ccache_ntlm_auth.challenge_blob_len);
273 result = do_ntlm_auth_with_stored_pw(
274 name_user, name_domain, entry->pass,
275 initial, challenge, &auth,
276 state->response->data.ccache_ntlm_auth.session_key);
278 if (!NT_STATUS_IS_OK(result)) {
279 goto process_result;
282 state->response->extra_data.data = talloc_memdup(
283 state->mem_ctx, auth.data, auth.length);
284 if (!state->response->extra_data.data) {
285 result = NT_STATUS_NO_MEMORY;
286 goto process_result;
288 state->response->length += auth.length;
289 state->response->data.ccache_ntlm_auth.auth_blob_len = auth.length;
291 data_blob_free(&auth);
293 process_result:
294 if (!NT_STATUS_IS_OK(result)) {
295 request_error(state);
296 return;
298 request_ok(state);
301 void winbindd_ccache_save(struct winbindd_cli_state *state)
303 struct winbindd_domain *domain;
304 fstring name_domain, name_user;
305 NTSTATUS status;
307 /* Ensure null termination */
308 state->request->data.ccache_save.user[
309 sizeof(state->request->data.ccache_save.user)-1]='\0';
310 state->request->data.ccache_save.pass[
311 sizeof(state->request->data.ccache_save.pass)-1]='\0';
313 DEBUG(3, ("[%5lu]: save password of user %s\n",
314 (unsigned long)state->pid,
315 state->request->data.ccache_save.user));
317 /* Parse domain and username */
319 if (!canonicalize_username(state->request->data.ccache_save.user,
320 name_domain, name_user)) {
321 DEBUG(5,("winbindd_ccache_save: cannot parse domain and user "
322 "from name [%s]\n",
323 state->request->data.ccache_save.user));
324 request_error(state);
325 return;
329 * The domain is checked here only for compatibility
330 * reasons. We used to do the winbindd memory ccache for
331 * ntlm_auth in the domain child. With that code, we had to
332 * make sure that we do have a domain around to send this
333 * to. Now we do the memory cache in the parent winbindd,
334 * where it would not matter if we have a domain or not.
337 domain = find_auth_domain(state->request->flags, name_domain);
338 if (domain == NULL) {
339 DEBUG(5, ("winbindd_ccache_save: can't get domain [%s]\n",
340 name_domain));
341 request_error(state);
342 return;
345 if (!check_client_uid(state, state->request->data.ccache_save.uid)) {
346 request_error(state);
347 return;
350 status = winbindd_add_memory_creds(
351 state->request->data.ccache_save.user,
352 state->request->data.ccache_save.uid,
353 state->request->data.ccache_save.pass);
355 if (!NT_STATUS_IS_OK(status)) {
356 DEBUG(1, ("winbindd_add_memory_creds failed %s\n",
357 nt_errstr(status)));
358 request_error(state);
359 return;
361 request_ok(state);