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
,
53 struct 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 /* We need to get our protocol handler into the right state. So first
89 we ask it to generate the initial message. Actually the client has already
90 sent its own initial message, so we're going to drop this one on the floor.
91 The client might have sent a different message, for example with different
92 negotiation options, but as far as I can tell this won't hurt us. (Unless
93 the client sent a different username or domain, in which case that's their
94 problem for telling us the wrong username or domain.)
95 Since we have a copy of the initial message that the client sent, we could
96 resolve any discrepancies if we had to.
98 dummy_msg
= data_blob_null
;
99 reply
= data_blob_null
;
100 status
= ntlmssp_update(ntlmssp_state
, dummy_msg
, &reply
);
101 data_blob_free(&dummy_msg
);
102 data_blob_free(&reply
);
104 if (!NT_STATUS_EQUAL(status
, NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
105 DEBUG(1, ("Failed to create initial message! [%s]\n",
110 /* Now we are ready to handle the server's actual response. */
111 status
= ntlmssp_update(ntlmssp_state
, challenge_msg
, &reply
);
113 if (!NT_STATUS_EQUAL(status
, NT_STATUS_OK
)) {
114 DEBUG(1, ("We didn't get a response to the challenge! [%s]\n",
116 data_blob_free(&reply
);
119 *auth_msg
= data_blob(reply
.data
, reply
.length
);
120 status
= NT_STATUS_OK
;
123 ntlmssp_end(&ntlmssp_state
);
127 static bool check_client_uid(struct winbindd_cli_state
*state
, uid_t uid
)
134 ret
= sys_getpeereid(state
->sock
, &ret_uid
);
136 DEBUG(1, ("check_client_uid: Could not get socket peer uid: %s; "
137 "denying access\n", strerror(errno
)));
141 if (uid
!= ret_uid
) {
142 DEBUG(1, ("check_client_uid: Client lied about its uid: said %u, "
143 "actually was %u; denying access\n",
144 (unsigned int)uid
, (unsigned int)ret_uid
));
151 void winbindd_ccache_ntlm_auth(struct winbindd_cli_state
*state
)
153 struct winbindd_domain
*domain
;
154 fstring name_domain
, name_user
;
156 /* Ensure null termination */
157 state
->request
->data
.ccache_ntlm_auth
.user
[
158 sizeof(state
->request
->data
.ccache_ntlm_auth
.user
)-1]='\0';
160 DEBUG(3, ("[%5lu]: perform NTLM auth on behalf of user %s\n", (unsigned long)state
->pid
,
161 state
->request
->data
.ccache_ntlm_auth
.user
));
163 /* Parse domain and username */
165 if (!canonicalize_username(state
->request
->data
.ccache_ntlm_auth
.user
,
166 name_domain
, name_user
)) {
167 DEBUG(5,("winbindd_ccache_ntlm_auth: cannot parse domain and user from name [%s]\n",
168 state
->request
->data
.ccache_ntlm_auth
.user
));
169 request_error(state
);
173 domain
= find_auth_domain(state
->request
->flags
, name_domain
);
175 if (domain
== NULL
) {
176 DEBUG(5,("winbindd_ccache_ntlm_auth: can't get domain [%s]\n",
178 request_error(state
);
182 if (!check_client_uid(state
, state
->request
->data
.ccache_ntlm_auth
.uid
)) {
183 request_error(state
);
187 sendto_domain(state
, domain
);
190 enum winbindd_result
winbindd_dual_ccache_ntlm_auth(struct winbindd_domain
*domain
,
191 struct winbindd_cli_state
*state
)
193 NTSTATUS result
= NT_STATUS_NOT_SUPPORTED
;
194 struct WINBINDD_MEMORY_CREDS
*entry
;
195 DATA_BLOB initial
, challenge
, auth
;
196 fstring name_domain
, name_user
;
197 uint32 initial_blob_len
, challenge_blob_len
, extra_len
;
199 /* Ensure null termination */
200 state
->request
->data
.ccache_ntlm_auth
.user
[
201 sizeof(state
->request
->data
.ccache_ntlm_auth
.user
)-1]='\0';
203 DEBUG(3, ("winbindd_dual_ccache_ntlm_auth: [%5lu]: perform NTLM auth on "
204 "behalf of user %s (dual)\n", (unsigned long)state
->pid
,
205 state
->request
->data
.ccache_ntlm_auth
.user
));
207 /* validate blob lengths */
208 initial_blob_len
= state
->request
->data
.ccache_ntlm_auth
.initial_blob_len
;
209 challenge_blob_len
= state
->request
->data
.ccache_ntlm_auth
.challenge_blob_len
;
210 extra_len
= state
->request
->extra_len
;
212 if (initial_blob_len
> extra_len
|| challenge_blob_len
> extra_len
||
213 initial_blob_len
+ challenge_blob_len
> extra_len
||
214 initial_blob_len
+ challenge_blob_len
< initial_blob_len
||
215 initial_blob_len
+ challenge_blob_len
< challenge_blob_len
) {
217 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: blob lengths overrun "
218 "or wrap. Buffer [%d+%d > %d]\n",
225 /* Parse domain and username */
226 if (!parse_domain_user(state
->request
->data
.ccache_ntlm_auth
.user
, name_domain
, name_user
)) {
227 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: cannot parse "
228 "domain and user from name [%s]\n",
229 state
->request
->data
.ccache_ntlm_auth
.user
));
233 entry
= find_memory_creds_by_name(state
->request
->data
.ccache_ntlm_auth
.user
);
234 if (entry
== NULL
|| entry
->nt_hash
== NULL
|| entry
->lm_hash
== NULL
) {
235 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: could not find "
236 "credentials for user %s\n",
237 state
->request
->data
.ccache_ntlm_auth
.user
));
241 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: found ccache [%s]\n", entry
->username
));
243 if (!client_can_access_ccache_entry(state
->request
->data
.ccache_ntlm_auth
.uid
, entry
)) {
247 if (initial_blob_len
== 0 && challenge_blob_len
== 0) {
248 /* this is just a probe to see if credentials are available. */
249 result
= NT_STATUS_OK
;
250 state
->response
->data
.ccache_ntlm_auth
.auth_blob_len
= 0;
254 initial
= data_blob_const(state
->request
->extra_data
.data
,
256 challenge
= data_blob_const(
257 state
->request
->extra_data
.data
+ initial_blob_len
,
258 state
->request
->data
.ccache_ntlm_auth
.challenge_blob_len
);
260 result
= do_ntlm_auth_with_hashes(name_user
, name_domain
,
261 entry
->lm_hash
, entry
->nt_hash
,
262 initial
, challenge
, &auth
);
263 if (!NT_STATUS_IS_OK(result
)) {
267 state
->response
->extra_data
.data
= talloc_memdup(
268 state
->mem_ctx
, auth
.data
, auth
.length
);
269 if (!state
->response
->extra_data
.data
) {
270 result
= NT_STATUS_NO_MEMORY
;
273 state
->response
->length
+= auth
.length
;
274 state
->response
->data
.ccache_ntlm_auth
.auth_blob_len
= auth
.length
;
276 data_blob_free(&auth
);
279 return NT_STATUS_IS_OK(result
) ? WINBINDD_OK
: WINBINDD_ERROR
;