s3: More cleanup in winbindd_ads.c:query_user
[Samba.git] / source3 / winbindd / winbindd_ccache_access.c
blobc5a760af05210b2cfe09bf569f5584e03ddbe8be
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"
26 #include "../libcli/auth/ntlmssp.h"
28 #undef DBGC_CLASS
29 #define DBGC_CLASS DBGC_WINBIND
31 static bool client_can_access_ccache_entry(uid_t client_uid,
32 struct WINBINDD_MEMORY_CREDS *entry)
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;
39 DEBUG(1, ("Access denied to uid %u (expected %u)\n",
40 (unsigned int)client_uid, (unsigned int)entry->uid));
41 return False;
44 static 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])
53 NTSTATUS status;
54 struct ntlmssp_state *ntlmssp_state = NULL;
55 DATA_BLOB dummy_msg, reply;
57 status = ntlmssp_client_start(NULL,
58 global_myname(),
59 lp_workgroup(),
60 lp_client_ntlmv2_auth(),
61 &ntlmssp_state);
63 if (!NT_STATUS_IS_OK(status)) {
64 DEBUG(1, ("Could not start NTLMSSP client: %s\n",
65 nt_errstr(status)));
66 goto done;
69 status = ntlmssp_set_username(ntlmssp_state, username);
71 if (!NT_STATUS_IS_OK(status)) {
72 DEBUG(1, ("Could not set username: %s\n",
73 nt_errstr(status)));
74 goto done;
77 status = ntlmssp_set_domain(ntlmssp_state, domain);
79 if (!NT_STATUS_IS_OK(status)) {
80 DEBUG(1, ("Could not set domain: %s\n",
81 nt_errstr(status)));
82 goto done;
85 status = ntlmssp_set_hashes(ntlmssp_state, lm_hash, nt_hash);
87 if (!NT_STATUS_IS_OK(status)) {
88 DEBUG(1, ("Could not set hashes: %s\n",
89 nt_errstr(status)));
90 goto done;
93 ntlmssp_want_feature(ntlmssp_state, NTLMSSP_FEATURE_SESSION_KEY);
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.
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);
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;
117 /* Now we are ready to handle the server's actual response. */
118 status = ntlmssp_update(ntlmssp_state, challenge_msg, &reply);
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;
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;
134 *auth_msg = data_blob(reply.data, reply.length);
135 memcpy(session_key, ntlmssp_state->session_key.data, 16);
136 status = NT_STATUS_OK;
138 done:
139 ntlmssp_end(&ntlmssp_state);
140 return status;
143 static bool check_client_uid(struct winbindd_cli_state *state, uid_t uid)
145 int ret;
146 uid_t ret_uid;
148 ret_uid = (uid_t)-1;
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;
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;
164 return True;
167 void winbindd_ccache_ntlm_auth(struct winbindd_cli_state *state)
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;
176 /* Ensure null termination */
177 state->request->data.ccache_ntlm_auth.user[
178 sizeof(state->request->data.ccache_ntlm_auth.user)-1]='\0';
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));
183 /* Parse domain and username */
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;
193 domain = find_auth_domain(state->request->flags, name_domain);
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;
202 if (!check_client_uid(state, state->request->data.ccache_ntlm_auth.uid)) {
203 request_error(state);
204 return;
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;
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) {
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;
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;
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;
241 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: found ccache [%s]\n", entry->username));
243 if (!client_can_access_ccache_entry(state->request->data.ccache_ntlm_auth.uid, entry)) {
244 goto process_result;
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;
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);
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);
265 if (!NT_STATUS_IS_OK(result)) {
266 goto process_result;
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;
275 state->response->length += auth.length;
276 state->response->data.ccache_ntlm_auth.auth_blob_len = auth.length;
278 data_blob_free(&auth);
280 process_result:
281 if (!NT_STATUS_IS_OK(result)) {
282 request_error(state);
283 return;
285 request_ok(state);
288 void winbindd_ccache_save(struct winbindd_cli_state *state)
290 struct winbindd_domain *domain;
291 fstring name_domain, name_user;
292 NTSTATUS status;
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';
300 DEBUG(3, ("[%5lu]: save password of user %s\n",
301 (unsigned long)state->pid,
302 state->request->data.ccache_save.user));
304 /* Parse domain and username */
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;
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.
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;
332 if (!check_client_uid(state, state->request->data.ccache_save.uid)) {
333 request_error(state);
334 return;
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);
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;
348 request_ok(state);