VERSION: Raise version number up to 3.4.6.
[Samba/gbeck.git] / source3 / winbindd / winbindd_ccache_access.c
blobdd6d352960bc886303cfbcfa8799583d3ed1989e
1 /*
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/>.
24 #include "includes.h"
25 #include "winbindd.h"
27 #undef DBGC_CLASS
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 %u\n", (unsigned int)client_uid));
35 return True;
38 DEBUG(1, ("Access denied to uid %u (expected %u)\n",
39 (unsigned int)client_uid, (unsigned int)entry->uid));
40 return False;
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)
51 NTSTATUS status;
52 NTLMSSP_STATE *ntlmssp_state = NULL;
53 DATA_BLOB dummy_msg, reply;
55 status = ntlmssp_client_start(&ntlmssp_state);
57 if (!NT_STATUS_IS_OK(status)) {
58 DEBUG(1, ("Could not start NTLMSSP client: %s\n",
59 nt_errstr(status)));
60 goto done;
63 status = ntlmssp_set_username(ntlmssp_state, username);
65 if (!NT_STATUS_IS_OK(status)) {
66 DEBUG(1, ("Could not set username: %s\n",
67 nt_errstr(status)));
68 goto done;
71 status = ntlmssp_set_domain(ntlmssp_state, domain);
73 if (!NT_STATUS_IS_OK(status)) {
74 DEBUG(1, ("Could not set domain: %s\n",
75 nt_errstr(status)));
76 goto done;
79 status = ntlmssp_set_hashes(ntlmssp_state, lm_hash, nt_hash);
81 if (!NT_STATUS_IS_OK(status)) {
82 DEBUG(1, ("Could not set hashes: %s\n",
83 nt_errstr(status)));
84 goto done;
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.
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);
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;
109 /* Now we are ready to handle the server's actual response. */
110 status = ntlmssp_update(ntlmssp_state, challenge_msg, &reply);
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;
118 *auth_msg = reply;
119 status = NT_STATUS_OK;
121 done:
122 ntlmssp_end(&ntlmssp_state);
123 return status;
126 static bool check_client_uid(struct winbindd_cli_state *state, uid_t uid)
128 int ret;
129 uid_t ret_uid;
131 ret_uid = (uid_t)-1;
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;
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;
147 return True;
150 void winbindd_ccache_ntlm_auth(struct winbindd_cli_state *state)
152 struct winbindd_domain *domain;
153 fstring name_domain, name_user;
155 /* Ensure null termination */
156 state->request.data.ccache_ntlm_auth.user[
157 sizeof(state->request.data.ccache_ntlm_auth.user)-1]='\0';
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));
162 /* Parse domain and username */
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;
172 domain = find_auth_domain(state, name_domain);
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;
181 if (!check_client_uid(state, state->request.data.ccache_ntlm_auth.uid)) {
182 request_error(state);
183 return;
186 sendto_domain(state, domain);
189 enum winbindd_result winbindd_dual_ccache_ntlm_auth(struct winbindd_domain *domain,
190 struct winbindd_cli_state *state)
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;
198 /* Ensure null termination */
199 state->request.data.ccache_ntlm_auth.user[
200 sizeof(state->request.data.ccache_ntlm_auth.user)-1]='\0';
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));
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;
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) {
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;
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;
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;
240 DEBUG(10,("winbindd_dual_ccache_ntlm_auth: found ccache [%s]\n", entry->username));
242 if (!client_can_access_ccache_entry(state->request.data.ccache_ntlm_auth.uid, entry)) {
243 goto process_result;
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;
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);
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);
265 data_blob_free(&initial);
266 data_blob_free(&challenge);
268 if (!NT_STATUS_IS_OK(result)) {
269 goto process_result;
272 state->response.extra_data.data = smb_xmemdup(auth.data, auth.length);
273 if (!state->response.extra_data.data) {
274 result = NT_STATUS_NO_MEMORY;
275 goto process_result;
277 state->response.length += auth.length;
278 state->response.data.ccache_ntlm_auth.auth_blob_len = auth.length;
280 data_blob_free(&auth);
282 process_result:
283 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;