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 %d\n", client_uid
));
38 DEBUG(1, ("Access denied to uid %d (expected %d)\n", client_uid
, entry
->uid
));
42 static NTSTATUS
do_ntlm_auth_with_hashes(const char *username
,
44 const unsigned char lm_hash
[LM_HASH_LEN
],
45 const unsigned char nt_hash
[NT_HASH_LEN
],
46 const DATA_BLOB initial_msg
,
47 const DATA_BLOB challenge_msg
,
51 NTLMSSP_STATE
*ntlmssp_state
= NULL
;
52 DATA_BLOB dummy_msg
, reply
;
54 status
= ntlmssp_client_start(&ntlmssp_state
);
56 if (!NT_STATUS_IS_OK(status
)) {
57 DEBUG(1, ("Could not start NTLMSSP client: %s\n",
62 status
= ntlmssp_set_username(ntlmssp_state
, username
);
64 if (!NT_STATUS_IS_OK(status
)) {
65 DEBUG(1, ("Could not set username: %s\n",
70 status
= ntlmssp_set_domain(ntlmssp_state
, domain
);
72 if (!NT_STATUS_IS_OK(status
)) {
73 DEBUG(1, ("Could not set domain: %s\n",
78 status
= ntlmssp_set_hashes(ntlmssp_state
, lm_hash
, nt_hash
);
80 if (!NT_STATUS_IS_OK(status
)) {
81 DEBUG(1, ("Could not set hashes: %s\n",
86 /* We need to get our protocol handler into the right state. So first
87 we ask it to generate the initial message. Actually the client has already
88 sent its own initial message, so we're going to drop this one on the floor.
89 The client might have sent a different message, for example with different
90 negotiation options, but as far as I can tell this won't hurt us. (Unless
91 the client sent a different username or domain, in which case that's their
92 problem for telling us the wrong username or domain.)
93 Since we have a copy of the initial message that the client sent, we could
94 resolve any discrepancies if we had to.
96 dummy_msg
= data_blob_null
;
97 reply
= data_blob_null
;
98 status
= ntlmssp_update(ntlmssp_state
, dummy_msg
, &reply
);
99 data_blob_free(&dummy_msg
);
100 data_blob_free(&reply
);
102 if (!NT_STATUS_EQUAL(status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
103 DEBUG(1, ("Failed to create initial message! [%s]\n",
108 /* Now we are ready to handle the server's actual response. */
109 status
= ntlmssp_update(ntlmssp_state
, challenge_msg
, &reply
);
111 if (!NT_STATUS_EQUAL(status
, NT_STATUS_OK
)) {
112 DEBUG(1, ("We didn't get a response to the challenge! [%s]\n",
114 data_blob_free(&reply
);
118 status
= NT_STATUS_OK
;
121 ntlmssp_end(&ntlmssp_state
);
125 static bool check_client_uid(struct winbindd_cli_state
*state
, uid_t uid
)
132 ret
= sys_getpeereid(state
->sock
, &ret_uid
);
134 DEBUG(1, ("check_client_uid: Could not get socket peer uid: %s; "
135 "denying access\n", strerror(errno
)));
139 if (uid
!= ret_uid
) {
140 DEBUG(1, ("check_client_uid: Client lied about its uid: said %d, "
141 "actually was %d; denying access\n",
149 void winbindd_ccache_ntlm_auth(struct winbindd_cli_state
*state
)
151 struct winbindd_domain
*domain
;
152 fstring name_domain
, name_user
;
154 /* Ensure null termination */
155 state
->request
.data
.ccache_ntlm_auth
.user
[
156 sizeof(state
->request
.data
.ccache_ntlm_auth
.user
)-1]='\0';
158 DEBUG(3, ("[%5lu]: perform NTLM auth on behalf of user %s\n", (unsigned long)state
->pid
,
159 state
->request
.data
.ccache_ntlm_auth
.user
));
161 /* Parse domain and username */
163 if (!canonicalize_username(state
->request
.data
.ccache_ntlm_auth
.user
,
164 name_domain
, name_user
)) {
165 DEBUG(5,("winbindd_ccache_ntlm_auth: cannot parse domain and user from name [%s]\n",
166 state
->request
.data
.ccache_ntlm_auth
.user
));
167 request_error(state
);
171 domain
= find_auth_domain(state
, name_domain
);
173 if (domain
== NULL
) {
174 DEBUG(5,("winbindd_ccache_ntlm_auth: can't get domain [%s]\n",
176 request_error(state
);
180 if (!check_client_uid(state
, state
->request
.data
.ccache_ntlm_auth
.uid
)) {
181 request_error(state
);
185 sendto_domain(state
, domain
);
188 enum winbindd_result
winbindd_dual_ccache_ntlm_auth(struct winbindd_domain
*domain
,
189 struct winbindd_cli_state
*state
)
191 NTSTATUS result
= NT_STATUS_NOT_SUPPORTED
;
192 struct WINBINDD_MEMORY_CREDS
*entry
;
193 DATA_BLOB initial
, challenge
, auth
;
194 fstring name_domain
, name_user
;
195 uint32 initial_blob_len
, challenge_blob_len
, extra_len
;
197 /* Ensure null termination */
198 state
->request
.data
.ccache_ntlm_auth
.user
[
199 sizeof(state
->request
.data
.ccache_ntlm_auth
.user
)-1]='\0';
201 DEBUG(3, ("winbindd_dual_ccache_ntlm_auth: [%5lu]: perform NTLM auth on "
202 "behalf of user %s (dual)\n", (unsigned long)state
->pid
,
203 state
->request
.data
.ccache_ntlm_auth
.user
));
205 /* validate blob lengths */
206 initial_blob_len
= state
->request
.data
.ccache_ntlm_auth
.initial_blob_len
;
207 challenge_blob_len
= state
->request
.data
.ccache_ntlm_auth
.challenge_blob_len
;
208 extra_len
= state
->request
.extra_len
;
210 if (initial_blob_len
> extra_len
|| challenge_blob_len
> extra_len
||
211 initial_blob_len
+ challenge_blob_len
> extra_len
||
212 initial_blob_len
+ challenge_blob_len
< initial_blob_len
||
213 initial_blob_len
+ challenge_blob_len
< challenge_blob_len
) {
215 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: blob lengths overrun "
216 "or wrap. Buffer [%d+%d > %d]\n",
223 /* Parse domain and username */
224 if (!parse_domain_user(state
->request
.data
.ccache_ntlm_auth
.user
, name_domain
, name_user
)) {
225 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: cannot parse "
226 "domain and user from name [%s]\n",
227 state
->request
.data
.ccache_ntlm_auth
.user
));
231 entry
= find_memory_creds_by_name(state
->request
.data
.ccache_ntlm_auth
.user
);
232 if (entry
== NULL
|| entry
->nt_hash
== NULL
|| entry
->lm_hash
== NULL
) {
233 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: could not find "
234 "credentials for user %s\n",
235 state
->request
.data
.ccache_ntlm_auth
.user
));
239 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: found ccache [%s]\n", entry
->username
));
241 if (!client_can_access_ccache_entry(state
->request
.data
.ccache_ntlm_auth
.uid
, entry
)) {
245 if (initial_blob_len
== 0 && challenge_blob_len
== 0) {
246 /* this is just a probe to see if credentials are available. */
247 result
= NT_STATUS_OK
;
248 state
->response
.data
.ccache_ntlm_auth
.auth_blob_len
= 0;
252 initial
= data_blob(state
->request
.extra_data
.data
, initial_blob_len
);
253 challenge
= data_blob(state
->request
.extra_data
.data
+ initial_blob_len
,
254 state
->request
.data
.ccache_ntlm_auth
.challenge_blob_len
);
256 if (!initial
.data
|| !challenge
.data
) {
257 result
= NT_STATUS_NO_MEMORY
;
259 result
= do_ntlm_auth_with_hashes(name_user
, name_domain
,
260 entry
->lm_hash
, entry
->nt_hash
,
261 initial
, challenge
, &auth
);
264 data_blob_free(&initial
);
265 data_blob_free(&challenge
);
267 if (!NT_STATUS_IS_OK(result
)) {
271 state
->response
.extra_data
.data
= smb_xmemdup(auth
.data
, auth
.length
);
272 if (!state
->response
.extra_data
.data
) {
273 result
= NT_STATUS_NO_MEMORY
;
276 state
->response
.length
+= auth
.length
;
277 state
->response
.data
.ccache_ntlm_auth
.auth_blob_len
= auth
.length
;
279 data_blob_free(&auth
);
282 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;