a696db1031a1e4e3df5d2ab2de4aff7255e10571
[ira/wip.git] / source3 / winbindd / winbindd_ccache_access.c
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
30 static 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
42 static 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
120 done:
121         ntlmssp_end(&ntlmssp_state);
122         return status;
123 }
124
125 static 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
149 void 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
188 enum 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 }