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/>.
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
));
38 DEBUG(1, ("Access denied to uid %u (expected %u)\n",
39 (unsigned int)client_uid
, (unsigned int)entry
->uid
));
43 static NTSTATUS
do_ntlm_auth_with_hashes(const char *username
,
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
,
50 uint8_t session_key
[16])
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",
64 status
= ntlmssp_set_username(ntlmssp_state
, username
);
66 if (!NT_STATUS_IS_OK(status
)) {
67 DEBUG(1, ("Could not set username: %s\n",
72 status
= ntlmssp_set_domain(ntlmssp_state
, domain
);
74 if (!NT_STATUS_IS_OK(status
)) {
75 DEBUG(1, ("Could not set domain: %s\n",
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",
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",
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",
118 data_blob_free(&reply
);
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
);
129 *auth_msg
= data_blob(reply
.data
, reply
.length
);
130 memcpy(session_key
, ntlmssp_state
->session_key
.data
, 16);
131 status
= NT_STATUS_OK
;
134 ntlmssp_end(&ntlmssp_state
);
138 static bool check_client_uid(struct winbindd_cli_state
*state
, uid_t uid
)
145 ret
= sys_getpeereid(state
->sock
, &ret_uid
);
147 DEBUG(1, ("check_client_uid: Could not get socket peer uid: %s; "
148 "denying access\n", strerror(errno
)));
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
));
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
);
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",
189 request_error(state
);
193 if (!check_client_uid(state
, state
->request
->data
.ccache_ntlm_auth
.uid
)) {
194 request_error(state
);
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",
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
));
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
));
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
)) {
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;
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
;
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
)) {
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
;