upgradeprovision: never use xattr it's pointless in this usecase
[Samba/ekacnet.git] / source3 / winbindd / winbindd_ccache_access.c
blob2f71aaae527c6a4ed3ba93fe9e145b4740359e4c
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"
26 #include "ntlmssp.h"
28 #undef DBGC_CLASS
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));
36 return True;
39 DEBUG(1, ("Access denied to uid %u (expected %u)\n",
40 (unsigned int)client_uid, (unsigned int)entry->uid));
41 return False;
44 static NTSTATUS do_ntlm_auth_with_hashes(const char *username,
45 const char *domain,
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,
50 DATA_BLOB *auth_msg)
52 NTSTATUS status;
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",
60 nt_errstr(status)));
61 goto done;
64 status = ntlmssp_set_username(ntlmssp_state, username);
66 if (!NT_STATUS_IS_OK(status)) {
67 DEBUG(1, ("Could not set username: %s\n",
68 nt_errstr(status)));
69 goto done;
72 status = ntlmssp_set_domain(ntlmssp_state, domain);
74 if (!NT_STATUS_IS_OK(status)) {
75 DEBUG(1, ("Could not set domain: %s\n",
76 nt_errstr(status)));
77 goto done;
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",
84 nt_errstr(status)));
85 goto done;
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",
106 nt_errstr(status)));
107 goto done;
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",
115 nt_errstr(status)));
116 data_blob_free(&reply);
117 goto done;
119 *auth_msg = data_blob(reply.data, reply.length);
120 status = NT_STATUS_OK;
122 done:
123 ntlmssp_end(&ntlmssp_state);
124 return status;
127 static bool check_client_uid(struct winbindd_cli_state *state, uid_t uid)
129 int ret;
130 uid_t ret_uid;
132 ret_uid = (uid_t)-1;
134 ret = sys_getpeereid(state->sock, &ret_uid);
135 if (ret != 0) {
136 DEBUG(1, ("check_client_uid: Could not get socket peer uid: %s; "
137 "denying access\n", strerror(errno)));
138 return False;
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));
145 return False;
148 return True;
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);
170 return;
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",
177 name_domain));
178 request_error(state);
179 return;
182 if (!check_client_uid(state, state->request->data.ccache_ntlm_auth.uid)) {
183 request_error(state);
184 return;
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",
219 initial_blob_len,
220 challenge_blob_len,
221 extra_len));
222 goto process_result;
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));
230 goto process_result;
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));
238 goto process_result;
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)) {
244 goto process_result;
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;
251 goto process_result;
254 initial = data_blob_const(state->request->extra_data.data,
255 initial_blob_len);
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)) {
264 goto process_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;
271 goto process_result;
273 state->response->length += auth.length;
274 state->response->data.ccache_ntlm_auth.auth_blob_len = auth.length;
276 data_blob_free(&auth);
278 process_result:
279 return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;