Make winbindd_cli_state->response a pointer instead of a struct member
[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 %u\n", (unsigned int)client_uid));
35                 return True;
36         }
37
38         DEBUG(1, ("Access denied to uid %u (expected %u)\n",
39                 (unsigned int)client_uid, (unsigned int)entry->uid));
40         return False;
41 }
42
43 static NTSTATUS do_ntlm_auth_with_hashes(const char *username,
44                                         const char *domain,
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,
49                                         DATA_BLOB *auth_msg)
50 {
51         NTSTATUS status;
52         NTLMSSP_STATE *ntlmssp_state = NULL;
53         DATA_BLOB dummy_msg, reply;
54
55         status = ntlmssp_client_start(&ntlmssp_state);
56
57         if (!NT_STATUS_IS_OK(status)) {
58                 DEBUG(1, ("Could not start NTLMSSP client: %s\n",
59                         nt_errstr(status)));
60                 goto done;
61         }
62
63         status = ntlmssp_set_username(ntlmssp_state, username);
64
65         if (!NT_STATUS_IS_OK(status)) {
66                 DEBUG(1, ("Could not set username: %s\n",
67                         nt_errstr(status)));
68                 goto done;
69         }
70
71         status = ntlmssp_set_domain(ntlmssp_state, domain);
72
73         if (!NT_STATUS_IS_OK(status)) {
74                 DEBUG(1, ("Could not set domain: %s\n",
75                         nt_errstr(status)));
76                 goto done;
77         }
78
79         status = ntlmssp_set_hashes(ntlmssp_state, lm_hash, nt_hash);
80         
81         if (!NT_STATUS_IS_OK(status)) {
82                 DEBUG(1, ("Could not set hashes: %s\n",
83                         nt_errstr(status)));
84                 goto done;
85         }
86
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.
96         */
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);
102
103         if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
104                 DEBUG(1, ("Failed to create initial message! [%s]\n",
105                         nt_errstr(status)));
106                 goto done;
107         }
108
109         /* Now we are ready to handle the server's actual response. */
110         status = ntlmssp_update(ntlmssp_state, challenge_msg, &reply);
111
112         if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
113                 DEBUG(1, ("We didn't get a response to the challenge! [%s]\n",
114                         nt_errstr(status)));
115                 data_blob_free(&reply);
116                 goto done;
117         }
118         *auth_msg = reply;
119         status = NT_STATUS_OK;
120
121 done:
122         ntlmssp_end(&ntlmssp_state);
123         return status;
124 }
125
126 static bool check_client_uid(struct winbindd_cli_state *state, uid_t uid)
127 {
128         int ret;
129         uid_t ret_uid;
130
131         ret_uid = (uid_t)-1;
132
133         ret = sys_getpeereid(state->sock, &ret_uid);
134         if (ret != 0) {
135                 DEBUG(1, ("check_client_uid: Could not get socket peer uid: %s; "
136                         "denying access\n", strerror(errno)));
137                 return False;
138         }
139
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));
144                 return False;
145         }
146
147         return True;
148 }
149
150 void winbindd_ccache_ntlm_auth(struct winbindd_cli_state *state)
151 {
152         struct winbindd_domain *domain;
153         fstring name_domain, name_user;
154
155         /* Ensure null termination */
156         state->request->data.ccache_ntlm_auth.user[
157                         sizeof(state->request->data.ccache_ntlm_auth.user)-1]='\0';
158
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));
161
162         /* Parse domain and username */
163
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);
169                 return;
170         }
171
172         domain = find_auth_domain(state, name_domain);
173
174         if (domain == NULL) {
175                 DEBUG(5,("winbindd_ccache_ntlm_auth: can't get domain [%s]\n",
176                         name_domain));
177                 request_error(state);
178                 return;
179         }
180
181         if (!check_client_uid(state, state->request->data.ccache_ntlm_auth.uid)) {
182                 request_error(state);
183                 return;
184         }
185
186         sendto_domain(state, domain);
187 }
188
189 enum winbindd_result winbindd_dual_ccache_ntlm_auth(struct winbindd_domain *domain,
190                                                 struct winbindd_cli_state *state)
191 {
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;
197
198         /* Ensure null termination */
199         state->request->data.ccache_ntlm_auth.user[
200                 sizeof(state->request->data.ccache_ntlm_auth.user)-1]='\0';
201
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));
205
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;
210
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) {
215
216                 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: blob lengths overrun "
217                         "or wrap. Buffer [%d+%d > %d]\n",
218                         initial_blob_len,
219                         challenge_blob_len,
220                         extra_len));
221                 goto process_result;
222         }
223
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));
229                 goto process_result;
230         }
231
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));
237                 goto process_result;
238         }
239
240         DEBUG(10,("winbindd_dual_ccache_ntlm_auth: found ccache [%s]\n", entry->username));
241
242         if (!client_can_access_ccache_entry(state->request->data.ccache_ntlm_auth.uid, entry)) {
243                 goto process_result;
244         }
245
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;
250                 goto process_result;
251         }
252
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);
256
257         if (!initial.data || !challenge.data) {
258                 result = NT_STATUS_NO_MEMORY;
259         } else {
260                 result = do_ntlm_auth_with_hashes(name_user, name_domain,
261                                                 entry->lm_hash, entry->nt_hash,
262                                                 initial, challenge, &auth);
263         }
264
265         data_blob_free(&initial);
266         data_blob_free(&challenge);
267
268         if (!NT_STATUS_IS_OK(result)) {
269                 goto process_result;
270         }
271
272         state->response->extra_data.data = talloc_memdup(
273                 state->mem_ctx, auth.data, auth.length);
274         if (!state->response->extra_data.data) {
275                 result = NT_STATUS_NO_MEMORY;
276                 goto process_result;
277         }
278         state->response->length += auth.length;
279         state->response->data.ccache_ntlm_auth.auth_blob_len = auth.length;
280
281         data_blob_free(&auth);
282
283   process_result:
284         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
285 }