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(&ntlmssp_state
);
59 if (!NT_STATUS_IS_OK(status
)) {
60 DEBUG(1, ("Could not start NTLMSSP client: %s\n",
65 status
= ntlmssp_set_username(ntlmssp_state
, username
);
67 if (!NT_STATUS_IS_OK(status
)) {
68 DEBUG(1, ("Could not set username: %s\n",
73 status
= ntlmssp_set_domain(ntlmssp_state
, domain
);
75 if (!NT_STATUS_IS_OK(status
)) {
76 DEBUG(1, ("Could not set domain: %s\n",
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",
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",
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",
119 data_blob_free(&reply
);
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
);
130 *auth_msg
= data_blob(reply
.data
, reply
.length
);
131 memcpy(session_key
, ntlmssp_state
->session_key
.data
, 16);
132 status
= NT_STATUS_OK
;
135 ntlmssp_end(&ntlmssp_state
);
139 static bool check_client_uid(struct winbindd_cli_state
*state
, uid_t uid
)
146 ret
= sys_getpeereid(state
->sock
, &ret_uid
);
148 DEBUG(1, ("check_client_uid: Could not get socket peer uid: %s; "
149 "denying access\n", strerror(errno
)));
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
));
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
);
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",
190 request_error(state
);
194 if (!check_client_uid(state
, state
->request
->data
.ccache_ntlm_auth
.uid
)) {
195 request_error(state
);
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",
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
));
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
));
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
)) {
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;
266 initial
= data_blob_const(state
->request
->extra_data
.data
,
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
)) {
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
;
287 state
->response
->length
+= auth
.length
;
288 state
->response
->data
.ccache_ntlm_auth
.auth_blob_len
= auth
.length
;
290 data_blob_free(&auth
);
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 password 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 "
317 state
->request
->data
.ccache_save
.user
));
318 request_error(state
);
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",
327 request_error(state
);
331 if (!check_client_uid(state
, state
->request
->data
.ccache_save
.uid
)) {
332 request_error(state
);
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",
362 return WINBINDD_ERROR
;