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/>.
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));
39 DEBUG(1, ("Access denied to uid %u (expected %u)\n",
40 (unsigned int)client_uid, (unsigned int)entry->uid));
44 static NTSTATUS do_ntlm_auth_with_hashes(const char *username,
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,
51 uint8_t session_key[16])
54 struct ntlmssp_state *ntlmssp_state = NULL;
55 DATA_BLOB dummy_msg, reply;
57 status = ntlmssp_client_start(NULL,
60 lp_client_ntlmv2_auth(),
63 if (!NT_STATUS_IS_OK(status)) {
64 DEBUG(1, ("Could not start NTLMSSP client: %s\n",
69 status = ntlmssp_set_username(ntlmssp_state, username);
71 if (!NT_STATUS_IS_OK(status)) {
72 DEBUG(1, ("Could not set username: %s\n",
77 status = ntlmssp_set_domain(ntlmssp_state, domain);
79 if (!NT_STATUS_IS_OK(status)) {
80 DEBUG(1, ("Could not set domain: %s\n",
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",
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",
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",
123 data_blob_free(&reply);
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);
134 *auth_msg = data_blob(reply.data, reply.length);
135 memcpy(session_key, ntlmssp_state->session_key.data, 16);
136 status = NT_STATUS_OK;
139 ntlmssp_end(&ntlmssp_state);
143 static bool check_client_uid(struct winbindd_cli_state *state, uid_t uid)
150 ret = sys_getpeereid(state->sock, &ret_uid);
152 DEBUG(1, ("check_client_uid: Could not get socket peer uid: %s; "
153 "denying access\n", strerror(errno)));
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));
167 void winbindd_ccache_ntlm_auth(struct winbindd_cli_state *state)
169 struct winbindd_domain *domain;
170 fstring name_domain, name_user;
172 /* Ensure null termination */
173 state->request->data.ccache_ntlm_auth.user[
174 sizeof(state->request->data.ccache_ntlm_auth.user)-1]='\0';
176 DEBUG(3, ("[%5lu]: perform NTLM auth on behalf of user %s\n", (unsigned long)state->pid,
177 state->request->data.ccache_ntlm_auth.user));
179 /* Parse domain and username */
181 if (!canonicalize_username(state->request->data.ccache_ntlm_auth.user,
182 name_domain, name_user)) {
183 DEBUG(5,("winbindd_ccache_ntlm_auth: cannot parse domain and user from name [%s]\n",
184 state->request->data.ccache_ntlm_auth.user));
185 request_error(state);
189 domain = find_auth_domain(state->request->flags, name_domain);
191 if (domain == NULL) {
192 DEBUG(5,("winbindd_ccache_ntlm_auth: can't get domain [%s]\n",
194 request_error(state);
198 if (!check_client_uid(state, state->request->data.ccache_ntlm_auth.uid)) {
199 request_error(state);
203 sendto_domain(state, domain);
206 enum winbindd_result winbindd_dual_ccache_ntlm_auth(struct winbindd_domain *domain,
207 struct winbindd_cli_state *state)
209 NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
210 struct WINBINDD_MEMORY_CREDS *entry;
211 DATA_BLOB initial, challenge, auth;
212 fstring name_domain, name_user;
213 uint32 initial_blob_len, challenge_blob_len, extra_len;
215 /* Ensure null termination */
216 state->request->data.ccache_ntlm_auth.user[
217 sizeof(state->request->data.ccache_ntlm_auth.user)-1]='\0';
219 DEBUG(3, ("winbindd_dual_ccache_ntlm_auth: [%5lu]: perform NTLM auth on "
220 "behalf of user %s (dual)\n", (unsigned long)state->pid,
221 state->request->data.ccache_ntlm_auth.user));
223 /* validate blob lengths */
224 initial_blob_len = state->request->data.ccache_ntlm_auth.initial_blob_len;
225 challenge_blob_len = state->request->data.ccache_ntlm_auth.challenge_blob_len;
226 extra_len = state->request->extra_len;
228 if (initial_blob_len > extra_len || challenge_blob_len > extra_len ||
229 initial_blob_len + challenge_blob_len > extra_len ||
230 initial_blob_len + challenge_blob_len < initial_blob_len ||
231 initial_blob_len + challenge_blob_len < challenge_blob_len) {
233 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: blob lengths overrun "
234 "or wrap. Buffer [%d+%d > %d]\n",
241 /* Parse domain and username */
242 if (!parse_domain_user(state->request->data.ccache_ntlm_auth.user, name_domain, name_user)) {
243 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: cannot parse "
244 "domain and user from name [%s]\n",
245 state->request->data.ccache_ntlm_auth.user));
249 entry = find_memory_creds_by_name(state->request->data.ccache_ntlm_auth.user);
250 if (entry == NULL || entry->nt_hash == NULL || entry->lm_hash == NULL) {
251 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: could not find "
252 "credentials for user %s\n",
253 state->request->data.ccache_ntlm_auth.user));
257 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: found ccache [%s]\n", entry->username));
259 if (!client_can_access_ccache_entry(state->request->data.ccache_ntlm_auth.uid, entry)) {
263 if (initial_blob_len == 0 && challenge_blob_len == 0) {
264 /* this is just a probe to see if credentials are available. */
265 result = NT_STATUS_OK;
266 state->response->data.ccache_ntlm_auth.auth_blob_len = 0;
270 initial = data_blob_const(state->request->extra_data.data,
272 challenge = data_blob_const(
273 state->request->extra_data.data + initial_blob_len,
274 state->request->data.ccache_ntlm_auth.challenge_blob_len);
276 result = do_ntlm_auth_with_hashes(
277 name_user, name_domain, entry->lm_hash, entry->nt_hash,
278 initial, challenge, &auth,
279 state->response->data.ccache_ntlm_auth.session_key);
281 if (!NT_STATUS_IS_OK(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;
291 state->response->length += auth.length;
292 state->response->data.ccache_ntlm_auth.auth_blob_len = auth.length;
294 data_blob_free(&auth);
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 "
321 state->request->data.ccache_save.user));
322 request_error(state);
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",
331 request_error(state);
335 if (!check_client_uid(state, state->request->data.ccache_save.uid)) {
336 request_error(state);
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",
366 return WINBINDD_ERROR;