s3:librpc/ndr: use new strlen_m_ext_term() in ndr_charset_length(): fix bug #7594
[Samba.git] / source3 / winbindd / winbindd_ccache_access.c
blob288f9f22d1a742992bdc0f7ee351ae689d970500
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"
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_WINBIND
30 static bool client_can_access_ccache_entry(uid_t client_uid,
31 struct WINBINDD_MEMORY_CREDS *entry)
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;
38 DEBUG(1, ("Access denied to uid %u (expected %u)\n",
39 (unsigned int)client_uid, (unsigned int)entry->uid));
40 return False;
43 static 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])
52 NTSTATUS status;
53 NTLMSSP_STATE *ntlmssp_state = NULL;
54 DATA_BLOB dummy_msg, reply;
56 status = ntlmssp_client_start(&ntlmssp_state);
58 if (!NT_STATUS_IS_OK(status)) {
59 DEBUG(1, ("Could not start NTLMSSP client: %s\n",
60 nt_errstr(status)));
61 goto done;
64 status = ntlmssp_set_username(ntlmssp_state, username);
66 if (!NT_STATUS_IS_OK(status)) {
67 DEBUG(1, ("Could not set username: %s\n",
68 nt_errstr(status)));
69 goto done;
72 status = ntlmssp_set_domain(ntlmssp_state, domain);
74 if (!NT_STATUS_IS_OK(status)) {
75 DEBUG(1, ("Could not set domain: %s\n",
76 nt_errstr(status)));
77 goto done;
80 status = ntlmssp_set_hashes(ntlmssp_state, lm_hash, nt_hash);
82 if (!NT_STATUS_IS_OK(status)) {
83 DEBUG(1, ("Could not set hashes: %s\n",
84 nt_errstr(status)));
85 goto done;
88 ntlmssp_want_feature(ntlmssp_state, NTLMSSP_FEATURE_SESSION_KEY);
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.
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);
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;
112 /* Now we are ready to handle the server's actual response. */
113 status = ntlmssp_update(ntlmssp_state, challenge_msg, &reply);
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;
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;
129 *auth_msg = data_blob(reply.data, reply.length);
130 memcpy(session_key, ntlmssp_state->session_key.data, 16);
131 status = NT_STATUS_OK;
133 done:
134 ntlmssp_end(&ntlmssp_state);
135 return status;
138 static bool check_client_uid(struct winbindd_cli_state *state, uid_t uid)
140 int ret;
141 uid_t ret_uid;
143 ret_uid = (uid_t)-1;
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;
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;
159 return True;
162 void winbindd_ccache_ntlm_auth(struct winbindd_cli_state *state)
164 struct winbindd_domain *domain;
165 fstring name_domain, name_user;
167 /* Ensure null termination */
168 state->request->data.ccache_ntlm_auth.user[
169 sizeof(state->request->data.ccache_ntlm_auth.user)-1]='\0';
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));
174 /* Parse domain and username */
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;
184 domain = find_auth_domain(state->request->flags, name_domain);
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;
193 if (!check_client_uid(state, state->request->data.ccache_ntlm_auth.uid)) {
194 request_error(state);
195 return;
198 sendto_domain(state, domain);
201 enum winbindd_result winbindd_dual_ccache_ntlm_auth(struct winbindd_domain *domain,
202 struct winbindd_cli_state *state)
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;
210 /* Ensure null termination */
211 state->request->data.ccache_ntlm_auth.user[
212 sizeof(state->request->data.ccache_ntlm_auth.user)-1]='\0';
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));
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;
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) {
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;
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;
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;
252 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: found ccache [%s]\n", entry->username));
254 if (!client_can_access_ccache_entry(state->request->data.ccache_ntlm_auth.uid, entry)) {
255 goto process_result;
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;
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);
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);
278 data_blob_free(&initial);
279 data_blob_free(&challenge);
281 if (!NT_STATUS_IS_OK(result)) {
282 goto process_result;
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;
291 state->response->length += auth.length;
292 state->response->data.ccache_ntlm_auth.auth_blob_len = auth.length;
294 data_blob_free(&auth);
296 process_result:
297 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
300 void winbindd_ccache_save(struct winbindd_cli_state *state)
302 struct winbindd_domain *domain;
303 fstring name_domain, name_user;
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';
311 DEBUG(3, ("[%5lu]: save password of user %s\n",
312 (unsigned long)state->pid,
313 state->request->data.ccache_save.user));
315 /* Parse domain and username */
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;
326 domain = find_auth_domain(state->request->flags, name_domain);
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;
335 if (!check_client_uid(state, state->request->data.ccache_save.uid)) {
336 request_error(state);
337 return;
340 sendto_domain(state, domain);
343 enum winbindd_result winbindd_dual_ccache_save(
344 struct winbindd_domain *domain, struct winbindd_cli_state *state)
346 NTSTATUS status = NT_STATUS_NOT_SUPPORTED;
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';
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));
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);
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;
369 return WINBINDD_OK;