librpc: Shorten dcerpc_binding_handle_call a bit
[Samba/id10ts.git] / source3 / winbindd / winbindd_ccache_access.c
blob5557b959f05afbf54c34e2c74ad06a3d614851e6
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;
160 gid_t ret_gid;
162 ret_uid = (uid_t)-1;
164 ret = getpeereid(state->sock, &ret_uid, &ret_gid);
165 if (ret != 0) {
166 DEBUG(1, ("check_client_uid: Could not get socket peer uid: %s; "
167 "denying access\n", strerror(errno)));
168 return False;
171 if (uid != ret_uid) {
172 DEBUG(1, ("check_client_uid: Client lied about its uid: said %u, "
173 "actually was %u; denying access\n",
174 (unsigned int)uid, (unsigned int)ret_uid));
175 return False;
178 return True;
181 void winbindd_ccache_ntlm_auth(struct winbindd_cli_state *state)
183 struct winbindd_domain *domain;
184 fstring name_domain, name_user;
185 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
186 struct WINBINDD_MEMORY_CREDS *entry;
187 DATA_BLOB initial, challenge, auth;
188 uint32 initial_blob_len, challenge_blob_len, extra_len;
190 /* Ensure null termination */
191 state->request->data.ccache_ntlm_auth.user[
192 sizeof(state->request->data.ccache_ntlm_auth.user)-1]='\0';
194 DEBUG(3, ("[%5lu]: perform NTLM auth on behalf of user %s\n", (unsigned long)state->pid,
195 state->request->data.ccache_ntlm_auth.user));
197 /* Parse domain and username */
199 if (!canonicalize_username(state->request->data.ccache_ntlm_auth.user,
200 name_domain, name_user)) {
201 DEBUG(5,("winbindd_ccache_ntlm_auth: cannot parse domain and user from name [%s]\n",
202 state->request->data.ccache_ntlm_auth.user));
203 request_error(state);
204 return;
207 domain = find_auth_domain(state->request->flags, name_domain);
209 if (domain == NULL) {
210 DEBUG(5,("winbindd_ccache_ntlm_auth: can't get domain [%s]\n",
211 name_domain));
212 request_error(state);
213 return;
216 if (!check_client_uid(state, state->request->data.ccache_ntlm_auth.uid)) {
217 request_error(state);
218 return;
221 /* validate blob lengths */
222 initial_blob_len = state->request->data.ccache_ntlm_auth.initial_blob_len;
223 challenge_blob_len = state->request->data.ccache_ntlm_auth.challenge_blob_len;
224 extra_len = state->request->extra_len;
226 if (initial_blob_len > extra_len || challenge_blob_len > extra_len ||
227 initial_blob_len + challenge_blob_len > extra_len ||
228 initial_blob_len + challenge_blob_len < initial_blob_len ||
229 initial_blob_len + challenge_blob_len < challenge_blob_len) {
231 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: blob lengths overrun "
232 "or wrap. Buffer [%d+%d > %d]\n",
233 initial_blob_len,
234 challenge_blob_len,
235 extra_len));
236 goto process_result;
239 /* Parse domain and username */
240 if (!parse_domain_user(state->request->data.ccache_ntlm_auth.user, name_domain, name_user)) {
241 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: cannot parse "
242 "domain and user from name [%s]\n",
243 state->request->data.ccache_ntlm_auth.user));
244 goto process_result;
247 entry = find_memory_creds_by_name(state->request->data.ccache_ntlm_auth.user);
248 if (entry == NULL || entry->nt_hash == NULL || entry->lm_hash == NULL) {
249 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: could not find "
250 "credentials for user %s\n",
251 state->request->data.ccache_ntlm_auth.user));
252 goto process_result;
255 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: found ccache [%s]\n", entry->username));
257 if (!client_can_access_ccache_entry(state->request->data.ccache_ntlm_auth.uid, entry)) {
258 goto process_result;
261 if (initial_blob_len == 0 && challenge_blob_len == 0) {
262 /* this is just a probe to see if credentials are available. */
263 result = NT_STATUS_OK;
264 state->response->data.ccache_ntlm_auth.auth_blob_len = 0;
265 goto process_result;
268 initial = data_blob_const(state->request->extra_data.data,
269 initial_blob_len);
270 challenge = data_blob_const(
271 state->request->extra_data.data + initial_blob_len,
272 state->request->data.ccache_ntlm_auth.challenge_blob_len);
274 result = do_ntlm_auth_with_stored_pw(
275 name_user, name_domain, entry->pass,
276 initial, challenge, &auth,
277 state->response->data.ccache_ntlm_auth.session_key);
279 if (!NT_STATUS_IS_OK(result)) {
280 goto process_result;
283 state->response->extra_data.data = talloc_memdup(
284 state->mem_ctx, auth.data, auth.length);
285 if (!state->response->extra_data.data) {
286 result = NT_STATUS_NO_MEMORY;
287 goto process_result;
289 state->response->length += auth.length;
290 state->response->data.ccache_ntlm_auth.auth_blob_len = auth.length;
292 data_blob_free(&auth);
294 process_result:
295 if (!NT_STATUS_IS_OK(result)) {
296 request_error(state);
297 return;
299 request_ok(state);
302 void winbindd_ccache_save(struct winbindd_cli_state *state)
304 struct winbindd_domain *domain;
305 fstring name_domain, name_user;
306 NTSTATUS status;
308 /* Ensure null termination */
309 state->request->data.ccache_save.user[
310 sizeof(state->request->data.ccache_save.user)-1]='\0';
311 state->request->data.ccache_save.pass[
312 sizeof(state->request->data.ccache_save.pass)-1]='\0';
314 DEBUG(3, ("[%5lu]: save password of user %s\n",
315 (unsigned long)state->pid,
316 state->request->data.ccache_save.user));
318 /* Parse domain and username */
320 if (!canonicalize_username(state->request->data.ccache_save.user,
321 name_domain, name_user)) {
322 DEBUG(5,("winbindd_ccache_save: cannot parse domain and user "
323 "from name [%s]\n",
324 state->request->data.ccache_save.user));
325 request_error(state);
326 return;
330 * The domain is checked here only for compatibility
331 * reasons. We used to do the winbindd memory ccache for
332 * ntlm_auth in the domain child. With that code, we had to
333 * make sure that we do have a domain around to send this
334 * to. Now we do the memory cache in the parent winbindd,
335 * where it would not matter if we have a domain or not.
338 domain = find_auth_domain(state->request->flags, name_domain);
339 if (domain == NULL) {
340 DEBUG(5, ("winbindd_ccache_save: can't get domain [%s]\n",
341 name_domain));
342 request_error(state);
343 return;
346 if (!check_client_uid(state, state->request->data.ccache_save.uid)) {
347 request_error(state);
348 return;
351 status = winbindd_add_memory_creds(
352 state->request->data.ccache_save.user,
353 state->request->data.ccache_save.uid,
354 state->request->data.ccache_save.pass);
356 if (!NT_STATUS_IS_OK(status)) {
357 DEBUG(1, ("winbindd_add_memory_creds failed %s\n",
358 nt_errstr(status)));
359 request_error(state);
360 return;
362 request_ok(state);