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
,
52 NTLMSSP_STATE
*ntlmssp_state
= NULL
;
53 DATA_BLOB dummy_msg
, reply
;
55 status
= ntlmssp_client_start(&ntlmssp_state
);
57 if (!NT_STATUS_IS_OK(status
)) {
58 DEBUG(1, ("Could not start NTLMSSP client: %s\n",
63 status
= ntlmssp_set_username(ntlmssp_state
, username
);
65 if (!NT_STATUS_IS_OK(status
)) {
66 DEBUG(1, ("Could not set username: %s\n",
71 status
= ntlmssp_set_domain(ntlmssp_state
, domain
);
73 if (!NT_STATUS_IS_OK(status
)) {
74 DEBUG(1, ("Could not set domain: %s\n",
79 status
= ntlmssp_set_hashes(ntlmssp_state
, lm_hash
, nt_hash
);
81 if (!NT_STATUS_IS_OK(status
)) {
82 DEBUG(1, ("Could not set hashes: %s\n",
87 /* We need to get our protocol handler into the right state. So first
88 we ask it to generate the initial message. Actually the client has already
89 sent its own initial message, so we're going to drop this one on the floor.
90 The client might have sent a different message, for example with different
91 negotiation options, but as far as I can tell this won't hurt us. (Unless
92 the client sent a different username or domain, in which case that's their
93 problem for telling us the wrong username or domain.)
94 Since we have a copy of the initial message that the client sent, we could
95 resolve any discrepancies if we had to.
97 dummy_msg
= data_blob_null
;
98 reply
= data_blob_null
;
99 status
= ntlmssp_update(ntlmssp_state
, dummy_msg
, &reply
);
100 data_blob_free(&dummy_msg
);
101 data_blob_free(&reply
);
103 if (!NT_STATUS_EQUAL(status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
104 DEBUG(1, ("Failed to create initial message! [%s]\n",
109 /* Now we are ready to handle the server's actual response. */
110 status
= ntlmssp_update(ntlmssp_state
, challenge_msg
, &reply
);
112 if (!NT_STATUS_EQUAL(status
, NT_STATUS_OK
)) {
113 DEBUG(1, ("We didn't get a response to the challenge! [%s]\n",
115 data_blob_free(&reply
);
119 status
= NT_STATUS_OK
;
122 ntlmssp_end(&ntlmssp_state
);
126 static bool check_client_uid(struct winbindd_cli_state
*state
, uid_t uid
)
133 ret
= sys_getpeereid(state
->sock
, &ret_uid
);
135 DEBUG(1, ("check_client_uid: Could not get socket peer uid: %s; "
136 "denying access\n", strerror(errno
)));
140 if (uid
!= ret_uid
) {
141 DEBUG(1, ("check_client_uid: Client lied about its uid: said %u, "
142 "actually was %u; denying access\n",
143 (unsigned int)uid
, (unsigned int)ret_uid
));
150 void winbindd_ccache_ntlm_auth(struct winbindd_cli_state
*state
)
152 struct winbindd_domain
*domain
;
153 fstring name_domain
, name_user
;
155 /* Ensure null termination */
156 state
->request
.data
.ccache_ntlm_auth
.user
[
157 sizeof(state
->request
.data
.ccache_ntlm_auth
.user
)-1]='\0';
159 DEBUG(3, ("[%5lu]: perform NTLM auth on behalf of user %s\n", (unsigned long)state
->pid
,
160 state
->request
.data
.ccache_ntlm_auth
.user
));
162 /* Parse domain and username */
164 if (!canonicalize_username(state
->request
.data
.ccache_ntlm_auth
.user
,
165 name_domain
, name_user
)) {
166 DEBUG(5,("winbindd_ccache_ntlm_auth: cannot parse domain and user from name [%s]\n",
167 state
->request
.data
.ccache_ntlm_auth
.user
));
168 request_error(state
);
172 domain
= find_auth_domain(state
, name_domain
);
174 if (domain
== NULL
) {
175 DEBUG(5,("winbindd_ccache_ntlm_auth: can't get domain [%s]\n",
177 request_error(state
);
181 if (!check_client_uid(state
, state
->request
.data
.ccache_ntlm_auth
.uid
)) {
182 request_error(state
);
186 sendto_domain(state
, domain
);
189 enum winbindd_result
winbindd_dual_ccache_ntlm_auth(struct winbindd_domain
*domain
,
190 struct winbindd_cli_state
*state
)
192 NTSTATUS result
= NT_STATUS_NOT_SUPPORTED
;
193 struct WINBINDD_MEMORY_CREDS
*entry
;
194 DATA_BLOB initial
, challenge
, auth
;
195 fstring name_domain
, name_user
;
196 uint32 initial_blob_len
, challenge_blob_len
, extra_len
;
198 /* Ensure null termination */
199 state
->request
.data
.ccache_ntlm_auth
.user
[
200 sizeof(state
->request
.data
.ccache_ntlm_auth
.user
)-1]='\0';
202 DEBUG(3, ("winbindd_dual_ccache_ntlm_auth: [%5lu]: perform NTLM auth on "
203 "behalf of user %s (dual)\n", (unsigned long)state
->pid
,
204 state
->request
.data
.ccache_ntlm_auth
.user
));
206 /* validate blob lengths */
207 initial_blob_len
= state
->request
.data
.ccache_ntlm_auth
.initial_blob_len
;
208 challenge_blob_len
= state
->request
.data
.ccache_ntlm_auth
.challenge_blob_len
;
209 extra_len
= state
->request
.extra_len
;
211 if (initial_blob_len
> extra_len
|| challenge_blob_len
> extra_len
||
212 initial_blob_len
+ challenge_blob_len
> extra_len
||
213 initial_blob_len
+ challenge_blob_len
< initial_blob_len
||
214 initial_blob_len
+ challenge_blob_len
< challenge_blob_len
) {
216 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: blob lengths overrun "
217 "or wrap. Buffer [%d+%d > %d]\n",
224 /* Parse domain and username */
225 if (!parse_domain_user(state
->request
.data
.ccache_ntlm_auth
.user
, name_domain
, name_user
)) {
226 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: cannot parse "
227 "domain and user from name [%s]\n",
228 state
->request
.data
.ccache_ntlm_auth
.user
));
232 entry
= find_memory_creds_by_name(state
->request
.data
.ccache_ntlm_auth
.user
);
233 if (entry
== NULL
|| entry
->nt_hash
== NULL
|| entry
->lm_hash
== NULL
) {
234 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: could not find "
235 "credentials for user %s\n",
236 state
->request
.data
.ccache_ntlm_auth
.user
));
240 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: found ccache [%s]\n", entry
->username
));
242 if (!client_can_access_ccache_entry(state
->request
.data
.ccache_ntlm_auth
.uid
, entry
)) {
246 if (initial_blob_len
== 0 && challenge_blob_len
== 0) {
247 /* this is just a probe to see if credentials are available. */
248 result
= NT_STATUS_OK
;
249 state
->response
.data
.ccache_ntlm_auth
.auth_blob_len
= 0;
253 initial
= data_blob(state
->request
.extra_data
.data
, initial_blob_len
);
254 challenge
= data_blob(state
->request
.extra_data
.data
+ initial_blob_len
,
255 state
->request
.data
.ccache_ntlm_auth
.challenge_blob_len
);
257 if (!initial
.data
|| !challenge
.data
) {
258 result
= NT_STATUS_NO_MEMORY
;
260 result
= do_ntlm_auth_with_hashes(name_user
, name_domain
,
261 entry
->lm_hash
, entry
->nt_hash
,
262 initial
, challenge
, &auth
);
265 data_blob_free(&initial
);
266 data_blob_free(&challenge
);
268 if (!NT_STATUS_IS_OK(result
)) {
272 state
->response
.extra_data
.data
= smb_xmemdup(auth
.data
, auth
.length
);
273 if (!state
->response
.extra_data
.data
) {
274 result
= NT_STATUS_NO_MEMORY
;
277 state
->response
.length
+= auth
.length
;
278 state
->response
.data
.ccache_ntlm_auth
.auth_blob_len
= auth
.length
;
280 data_blob_free(&auth
);
283 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;