source: branches/samba-3.2.x/source/winbindd/winbindd_ccache_access.c

Last change on this file was 133, checked in by Paul Smedley, 17 years ago

Update trunk to 3.2.0pre3

File size: 8.5 KB
Line 
1/*
2 Unix SMB/CIFS implementation.
3
4 Winbind daemon - cached credentials funcions
5
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).
9
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.
14
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.
19
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/>.
22*/
23
24#include "includes.h"
25#include "winbindd.h"
26
27#undef DBGC_CLASS
28#define DBGC_CLASS DBGC_WINBIND
29
30static bool client_can_access_ccache_entry(uid_t client_uid,
31 struct WINBINDD_MEMORY_CREDS *entry)
32{
33 if (client_uid == entry->uid || client_uid == 0) {
34 DEBUG(10, ("Access granted to uid %d\n", client_uid));
35 return True;
36 }
37
38 DEBUG(1, ("Access denied to uid %d (expected %d)\n", client_uid, entry->uid));
39 return False;
40}
41
42static NTSTATUS do_ntlm_auth_with_hashes(const char *username,
43 const char *domain,
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,
48 DATA_BLOB *auth_msg)
49{
50 NTSTATUS status;
51 NTLMSSP_STATE *ntlmssp_state = NULL;
52 DATA_BLOB dummy_msg, reply;
53
54 status = ntlmssp_client_start(&ntlmssp_state);
55
56 if (!NT_STATUS_IS_OK(status)) {
57 DEBUG(1, ("Could not start NTLMSSP client: %s\n",
58 nt_errstr(status)));
59 goto done;
60 }
61
62 status = ntlmssp_set_username(ntlmssp_state, username);
63
64 if (!NT_STATUS_IS_OK(status)) {
65 DEBUG(1, ("Could not set username: %s\n",
66 nt_errstr(status)));
67 goto done;
68 }
69
70 status = ntlmssp_set_domain(ntlmssp_state, domain);
71
72 if (!NT_STATUS_IS_OK(status)) {
73 DEBUG(1, ("Could not set domain: %s\n",
74 nt_errstr(status)));
75 goto done;
76 }
77
78 status = ntlmssp_set_hashes(ntlmssp_state, lm_hash, nt_hash);
79
80 if (!NT_STATUS_IS_OK(status)) {
81 DEBUG(1, ("Could not set hashes: %s\n",
82 nt_errstr(status)));
83 goto done;
84 }
85
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.
95 */
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);
101
102 if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
103 DEBUG(1, ("Failed to create initial message! [%s]\n",
104 nt_errstr(status)));
105 goto done;
106 }
107
108 /* Now we are ready to handle the server's actual response. */
109 status = ntlmssp_update(ntlmssp_state, challenge_msg, &reply);
110
111 if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
112 DEBUG(1, ("We didn't get a response to the challenge! [%s]\n",
113 nt_errstr(status)));
114 data_blob_free(&reply);
115 goto done;
116 }
117 *auth_msg = reply;
118 status = NT_STATUS_OK;
119
120done:
121 ntlmssp_end(&ntlmssp_state);
122 return status;
123}
124
125static bool check_client_uid(struct winbindd_cli_state *state, uid_t uid)
126{
127 int ret;
128 uid_t ret_uid;
129
130 ret_uid = (uid_t)-1;
131
132 ret = sys_getpeereid(state->sock, &ret_uid);
133 if (ret != 0) {
134 DEBUG(1, ("check_client_uid: Could not get socket peer uid: %s; "
135 "denying access\n", strerror(errno)));
136 return False;
137 }
138
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",
142 uid, ret_uid));
143 return False;
144 }
145
146 return True;
147}
148
149void winbindd_ccache_ntlm_auth(struct winbindd_cli_state *state)
150{
151 struct winbindd_domain *domain;
152 fstring name_domain, name_user;
153
154 /* Ensure null termination */
155 state->request.data.ccache_ntlm_auth.user[
156 sizeof(state->request.data.ccache_ntlm_auth.user)-1]='\0';
157
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));
160
161 /* Parse domain and username */
162
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);
168 return;
169 }
170
171 domain = find_auth_domain(state, name_domain);
172
173 if (domain == NULL) {
174 DEBUG(5,("winbindd_ccache_ntlm_auth: can't get domain [%s]\n",
175 name_domain));
176 request_error(state);
177 return;
178 }
179
180 if (!check_client_uid(state, state->request.data.ccache_ntlm_auth.uid)) {
181 request_error(state);
182 return;
183 }
184
185 sendto_domain(state, domain);
186}
187
188enum winbindd_result winbindd_dual_ccache_ntlm_auth(struct winbindd_domain *domain,
189 struct winbindd_cli_state *state)
190{
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;
196
197 /* Ensure null termination */
198 state->request.data.ccache_ntlm_auth.user[
199 sizeof(state->request.data.ccache_ntlm_auth.user)-1]='\0';
200
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));
204
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;
209
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) {
214
215 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: blob lengths overrun "
216 "or wrap. Buffer [%d+%d > %d]\n",
217 initial_blob_len,
218 challenge_blob_len,
219 extra_len));
220 goto process_result;
221 }
222
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));
228 goto process_result;
229 }
230
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));
236 goto process_result;
237 }
238
239 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: found ccache [%s]\n", entry->username));
240
241 if (!client_can_access_ccache_entry(state->request.data.ccache_ntlm_auth.uid, entry)) {
242 goto process_result;
243 }
244
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;
249 goto process_result;
250 }
251
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);
255
256 if (!initial.data || !challenge.data) {
257 result = NT_STATUS_NO_MEMORY;
258 } else {
259 result = do_ntlm_auth_with_hashes(name_user, name_domain,
260 entry->lm_hash, entry->nt_hash,
261 initial, challenge, &auth);
262 }
263
264 data_blob_free(&initial);
265 data_blob_free(&challenge);
266
267 if (!NT_STATUS_IS_OK(result)) {
268 goto process_result;
269 }
270
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;
274 goto process_result;
275 }
276 state->response.length += auth.length;
277 state->response.data.ccache_ntlm_auth.auth_blob_len = auth.length;
278
279 data_blob_free(&auth);
280
281 process_result:
282 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
283}
Note: See TracBrowser for help on using the repository browser.