s3: Add the session key to the ccache_ntlm_auth response
[Samba/fernandojvsilva.git] / source3 / winbindd / winbindd_ccache_access.c
blob436e9076994be1c96567d497ddc237042976a6e2
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 "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(&ntlmssp_state);
59 if (!NT_STATUS_IS_OK(status)) {
60 DEBUG(1, ("Could not start NTLMSSP client: %s\n",
61 nt_errstr(status)));
62 goto done;
65 status = ntlmssp_set_username(ntlmssp_state, username);
67 if (!NT_STATUS_IS_OK(status)) {
68 DEBUG(1, ("Could not set username: %s\n",
69 nt_errstr(status)));
70 goto done;
73 status = ntlmssp_set_domain(ntlmssp_state, domain);
75 if (!NT_STATUS_IS_OK(status)) {
76 DEBUG(1, ("Could not set domain: %s\n",
77 nt_errstr(status)));
78 goto done;
81 status = ntlmssp_set_hashes(ntlmssp_state, lm_hash, nt_hash);
83 if (!NT_STATUS_IS_OK(status)) {
84 DEBUG(1, ("Could not set hashes: %s\n",
85 nt_errstr(status)));
86 goto done;
89 ntlmssp_want_feature(ntlmssp_state, NTLMSSP_FEATURE_SESSION_KEY);
91 /* We need to get our protocol handler into the right state. So first
92 we ask it to generate the initial message. Actually the client has already
93 sent its own initial message, so we're going to drop this one on the floor.
94 The client might have sent a different message, for example with different
95 negotiation options, but as far as I can tell this won't hurt us. (Unless
96 the client sent a different username or domain, in which case that's their
97 problem for telling us the wrong username or domain.)
98 Since we have a copy of the initial message that the client sent, we could
99 resolve any discrepancies if we had to.
101 dummy_msg = data_blob_null;
102 reply = data_blob_null;
103 status = ntlmssp_update(ntlmssp_state, dummy_msg, &reply);
104 data_blob_free(&dummy_msg);
105 data_blob_free(&reply);
107 if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
108 DEBUG(1, ("Failed to create initial message! [%s]\n",
109 nt_errstr(status)));
110 goto done;
113 /* Now we are ready to handle the server's actual response. */
114 status = ntlmssp_update(ntlmssp_state, challenge_msg, &reply);
116 if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
117 DEBUG(1, ("We didn't get a response to the challenge! [%s]\n",
118 nt_errstr(status)));
119 data_blob_free(&reply);
120 goto done;
123 if (ntlmssp_state->session_key.length != 16) {
124 DEBUG(1, ("invalid session key length %d\n",
125 (int)ntlmssp_state->session_key.length));
126 data_blob_free(&reply);
127 goto done;
130 *auth_msg = data_blob(reply.data, reply.length);
131 memcpy(session_key, ntlmssp_state->session_key.data, 16);
132 status = NT_STATUS_OK;
134 done:
135 ntlmssp_end(&ntlmssp_state);
136 return status;
139 static bool check_client_uid(struct winbindd_cli_state *state, uid_t uid)
141 int ret;
142 uid_t ret_uid;
144 ret_uid = (uid_t)-1;
146 ret = sys_getpeereid(state->sock, &ret_uid);
147 if (ret != 0) {
148 DEBUG(1, ("check_client_uid: Could not get socket peer uid: %s; "
149 "denying access\n", strerror(errno)));
150 return False;
153 if (uid != ret_uid) {
154 DEBUG(1, ("check_client_uid: Client lied about its uid: said %u, "
155 "actually was %u; denying access\n",
156 (unsigned int)uid, (unsigned int)ret_uid));
157 return False;
160 return True;
163 void winbindd_ccache_ntlm_auth(struct winbindd_cli_state *state)
165 struct winbindd_domain *domain;
166 fstring name_domain, name_user;
168 /* Ensure null termination */
169 state->request->data.ccache_ntlm_auth.user[
170 sizeof(state->request->data.ccache_ntlm_auth.user)-1]='\0';
172 DEBUG(3, ("[%5lu]: perform NTLM auth on behalf of user %s\n", (unsigned long)state->pid,
173 state->request->data.ccache_ntlm_auth.user));
175 /* Parse domain and username */
177 if (!canonicalize_username(state->request->data.ccache_ntlm_auth.user,
178 name_domain, name_user)) {
179 DEBUG(5,("winbindd_ccache_ntlm_auth: cannot parse domain and user from name [%s]\n",
180 state->request->data.ccache_ntlm_auth.user));
181 request_error(state);
182 return;
185 domain = find_auth_domain(state->request->flags, name_domain);
187 if (domain == NULL) {
188 DEBUG(5,("winbindd_ccache_ntlm_auth: can't get domain [%s]\n",
189 name_domain));
190 request_error(state);
191 return;
194 if (!check_client_uid(state, state->request->data.ccache_ntlm_auth.uid)) {
195 request_error(state);
196 return;
199 sendto_domain(state, domain);
202 enum winbindd_result winbindd_dual_ccache_ntlm_auth(struct winbindd_domain *domain,
203 struct winbindd_cli_state *state)
205 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
206 struct WINBINDD_MEMORY_CREDS *entry;
207 DATA_BLOB initial, challenge, auth;
208 fstring name_domain, name_user;
209 uint32 initial_blob_len, challenge_blob_len, extra_len;
211 /* Ensure null termination */
212 state->request->data.ccache_ntlm_auth.user[
213 sizeof(state->request->data.ccache_ntlm_auth.user)-1]='\0';
215 DEBUG(3, ("winbindd_dual_ccache_ntlm_auth: [%5lu]: perform NTLM auth on "
216 "behalf of user %s (dual)\n", (unsigned long)state->pid,
217 state->request->data.ccache_ntlm_auth.user));
219 /* validate blob lengths */
220 initial_blob_len = state->request->data.ccache_ntlm_auth.initial_blob_len;
221 challenge_blob_len = state->request->data.ccache_ntlm_auth.challenge_blob_len;
222 extra_len = state->request->extra_len;
224 if (initial_blob_len > extra_len || challenge_blob_len > extra_len ||
225 initial_blob_len + challenge_blob_len > extra_len ||
226 initial_blob_len + challenge_blob_len < initial_blob_len ||
227 initial_blob_len + challenge_blob_len < challenge_blob_len) {
229 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: blob lengths overrun "
230 "or wrap. Buffer [%d+%d > %d]\n",
231 initial_blob_len,
232 challenge_blob_len,
233 extra_len));
234 goto process_result;
237 /* Parse domain and username */
238 if (!parse_domain_user(state->request->data.ccache_ntlm_auth.user, name_domain, name_user)) {
239 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: cannot parse "
240 "domain and user from name [%s]\n",
241 state->request->data.ccache_ntlm_auth.user));
242 goto process_result;
245 entry = find_memory_creds_by_name(state->request->data.ccache_ntlm_auth.user);
246 if (entry == NULL || entry->nt_hash == NULL || entry->lm_hash == NULL) {
247 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: could not find "
248 "credentials for user %s\n",
249 state->request->data.ccache_ntlm_auth.user));
250 goto process_result;
253 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: found ccache [%s]\n", entry->username));
255 if (!client_can_access_ccache_entry(state->request->data.ccache_ntlm_auth.uid, entry)) {
256 goto process_result;
259 if (initial_blob_len == 0 && challenge_blob_len == 0) {
260 /* this is just a probe to see if credentials are available. */
261 result = NT_STATUS_OK;
262 state->response->data.ccache_ntlm_auth.auth_blob_len = 0;
263 goto process_result;
266 initial = data_blob_const(state->request->extra_data.data,
267 initial_blob_len);
268 challenge = data_blob_const(
269 state->request->extra_data.data + initial_blob_len,
270 state->request->data.ccache_ntlm_auth.challenge_blob_len);
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);
277 if (!NT_STATUS_IS_OK(result)) {
278 goto process_result;
281 state->response->extra_data.data = talloc_memdup(
282 state->mem_ctx, auth.data, auth.length);
283 if (!state->response->extra_data.data) {
284 result = NT_STATUS_NO_MEMORY;
285 goto process_result;
287 state->response->length += auth.length;
288 state->response->data.ccache_ntlm_auth.auth_blob_len = auth.length;
290 data_blob_free(&auth);
292 process_result:
293 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
296 void winbindd_ccache_save(struct winbindd_cli_state *state)
298 struct winbindd_domain *domain;
299 fstring name_domain, name_user;
301 /* Ensure null termination */
302 state->request->data.ccache_save.user[
303 sizeof(state->request->data.ccache_save.user)-1]='\0';
304 state->request->data.ccache_save.pass[
305 sizeof(state->request->data.ccache_save.pass)-1]='\0';
307 DEBUG(3, ("[%5lu]: save passord of user %s\n",
308 (unsigned long)state->pid,
309 state->request->data.ccache_save.user));
311 /* Parse domain and username */
313 if (!canonicalize_username(state->request->data.ccache_ntlm_auth.user,
314 name_domain, name_user)) {
315 DEBUG(5,("winbindd_ccache_save: cannot parse domain and user "
316 "from name [%s]\n",
317 state->request->data.ccache_save.user));
318 request_error(state);
319 return;
322 domain = find_auth_domain(state->request->flags, name_domain);
324 if (domain == NULL) {
325 DEBUG(5, ("winbindd_ccache_save: can't get domain [%s]\n",
326 name_domain));
327 request_error(state);
328 return;
331 if (!check_client_uid(state, state->request->data.ccache_save.uid)) {
332 request_error(state);
333 return;
336 sendto_domain(state, domain);
339 enum winbindd_result winbindd_dual_ccache_save(
340 struct winbindd_domain *domain, struct winbindd_cli_state *state)
342 NTSTATUS status = NT_STATUS_NOT_SUPPORTED;
344 /* Ensure null termination */
345 state->request->data.ccache_save.user[
346 sizeof(state->request->data.ccache_save.user)-1]='\0';
347 state->request->data.ccache_save.pass[
348 sizeof(state->request->data.ccache_save.pass)-1]='\0';
350 DEBUG(3, ("winbindd_dual_ccache_save: [%5lu]: save password of user "
351 "%s\n", (unsigned long)state->pid,
352 state->request->data.ccache_save.user));
354 status = winbindd_add_memory_creds(
355 state->request->data.ccache_save.user,
356 state->request->data.ccache_save.uid,
357 state->request->data.ccache_save.pass);
359 if (!NT_STATUS_IS_OK(status)) {
360 DEBUG(1, ("winbindd_add_memory_creds failed %s\n",
361 nt_errstr(status)));
362 return WINBINDD_ERROR;
365 return WINBINDD_OK;