1 | /*
|
---|
2 | Unix SMB/CIFS implementation.
|
---|
3 |
|
---|
4 | Handle user credentials (as regards krb5)
|
---|
5 |
|
---|
6 | Copyright (C) Jelmer Vernooij 2005
|
---|
7 | Copyright (C) Tim Potter 2001
|
---|
8 | Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
|
---|
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 "system/kerberos.h"
|
---|
26 | #include "system/gssapi.h"
|
---|
27 | #include "auth/kerberos/kerberos.h"
|
---|
28 | #include "auth/credentials/credentials.h"
|
---|
29 | #include "auth/credentials/credentials_internal.h"
|
---|
30 | #include "auth/credentials/credentials_proto.h"
|
---|
31 | #include "auth/credentials/credentials_krb5.h"
|
---|
32 | #include "auth/kerberos/kerberos_credentials.h"
|
---|
33 | #include "auth/kerberos/kerberos_srv_keytab.h"
|
---|
34 | #include "auth/kerberos/kerberos_util.h"
|
---|
35 | #include "auth/kerberos/pac_utils.h"
|
---|
36 | #include "param/param.h"
|
---|
37 |
|
---|
38 | static void cli_credentials_invalidate_client_gss_creds(
|
---|
39 | struct cli_credentials *cred,
|
---|
40 | enum credentials_obtained obtained);
|
---|
41 |
|
---|
42 | _PUBLIC_ int cli_credentials_get_krb5_context(struct cli_credentials *cred,
|
---|
43 | struct loadparm_context *lp_ctx,
|
---|
44 | struct smb_krb5_context **smb_krb5_context)
|
---|
45 | {
|
---|
46 | int ret;
|
---|
47 | if (cred->smb_krb5_context) {
|
---|
48 | *smb_krb5_context = cred->smb_krb5_context;
|
---|
49 | return 0;
|
---|
50 | }
|
---|
51 |
|
---|
52 | ret = smb_krb5_init_context(cred, lp_ctx,
|
---|
53 | &cred->smb_krb5_context);
|
---|
54 | if (ret) {
|
---|
55 | cred->smb_krb5_context = NULL;
|
---|
56 | return ret;
|
---|
57 | }
|
---|
58 | *smb_krb5_context = cred->smb_krb5_context;
|
---|
59 | return 0;
|
---|
60 | }
|
---|
61 |
|
---|
62 | /* For most predictable behaviour, this needs to be called directly after the cli_credentials_init(),
|
---|
63 | * otherwise we may still have references to the old smb_krb5_context in a credential cache etc
|
---|
64 | */
|
---|
65 | _PUBLIC_ NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
|
---|
66 | struct smb_krb5_context *smb_krb5_context)
|
---|
67 | {
|
---|
68 | if (smb_krb5_context == NULL) {
|
---|
69 | talloc_unlink(cred, cred->smb_krb5_context);
|
---|
70 | cred->smb_krb5_context = NULL;
|
---|
71 | return NT_STATUS_OK;
|
---|
72 | }
|
---|
73 |
|
---|
74 | if (!talloc_reference(cred, smb_krb5_context)) {
|
---|
75 | return NT_STATUS_NO_MEMORY;
|
---|
76 | }
|
---|
77 | cred->smb_krb5_context = smb_krb5_context;
|
---|
78 | return NT_STATUS_OK;
|
---|
79 | }
|
---|
80 |
|
---|
81 | static int cli_credentials_set_from_ccache(struct cli_credentials *cred,
|
---|
82 | struct ccache_container *ccache,
|
---|
83 | enum credentials_obtained obtained,
|
---|
84 | const char **error_string)
|
---|
85 | {
|
---|
86 |
|
---|
87 | krb5_principal princ;
|
---|
88 | krb5_error_code ret;
|
---|
89 | char *name;
|
---|
90 |
|
---|
91 | if (cred->ccache_obtained > obtained) {
|
---|
92 | return 0;
|
---|
93 | }
|
---|
94 |
|
---|
95 | ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context,
|
---|
96 | ccache->ccache, &princ);
|
---|
97 |
|
---|
98 | if (ret) {
|
---|
99 | (*error_string) = talloc_asprintf(cred, "failed to get principal from ccache: %s\n",
|
---|
100 | smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
|
---|
101 | ret, cred));
|
---|
102 | return ret;
|
---|
103 | }
|
---|
104 |
|
---|
105 | ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
|
---|
106 | if (ret) {
|
---|
107 | (*error_string) = talloc_asprintf(cred, "failed to unparse principal from ccache: %s\n",
|
---|
108 | smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
|
---|
109 | ret, cred));
|
---|
110 | return ret;
|
---|
111 | }
|
---|
112 |
|
---|
113 | cli_credentials_set_principal(cred, name, obtained);
|
---|
114 |
|
---|
115 | free(name);
|
---|
116 |
|
---|
117 | krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
|
---|
118 |
|
---|
119 | /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
|
---|
120 | cred->ccache_obtained = obtained;
|
---|
121 |
|
---|
122 | return 0;
|
---|
123 | }
|
---|
124 |
|
---|
125 | /* Free a memory ccache */
|
---|
126 | static int free_mccache(struct ccache_container *ccc)
|
---|
127 | {
|
---|
128 | krb5_cc_destroy(ccc->smb_krb5_context->krb5_context, ccc->ccache);
|
---|
129 |
|
---|
130 | return 0;
|
---|
131 | }
|
---|
132 |
|
---|
133 | /* Free a disk-based ccache */
|
---|
134 | static int free_dccache(struct ccache_container *ccc) {
|
---|
135 | krb5_cc_close(ccc->smb_krb5_context->krb5_context, ccc->ccache);
|
---|
136 |
|
---|
137 | return 0;
|
---|
138 | }
|
---|
139 |
|
---|
140 | _PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred,
|
---|
141 | struct loadparm_context *lp_ctx,
|
---|
142 | const char *name,
|
---|
143 | enum credentials_obtained obtained,
|
---|
144 | const char **error_string)
|
---|
145 | {
|
---|
146 | krb5_error_code ret;
|
---|
147 | krb5_principal princ;
|
---|
148 | struct ccache_container *ccc;
|
---|
149 | if (cred->ccache_obtained > obtained) {
|
---|
150 | return 0;
|
---|
151 | }
|
---|
152 |
|
---|
153 | ccc = talloc(cred, struct ccache_container);
|
---|
154 | if (!ccc) {
|
---|
155 | (*error_string) = error_message(ENOMEM);
|
---|
156 | return ENOMEM;
|
---|
157 | }
|
---|
158 |
|
---|
159 | ret = cli_credentials_get_krb5_context(cred, lp_ctx,
|
---|
160 | &ccc->smb_krb5_context);
|
---|
161 | if (ret) {
|
---|
162 | (*error_string) = error_message(ret);
|
---|
163 | talloc_free(ccc);
|
---|
164 | return ret;
|
---|
165 | }
|
---|
166 | if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
|
---|
167 | talloc_free(ccc);
|
---|
168 | (*error_string) = error_message(ENOMEM);
|
---|
169 | return ENOMEM;
|
---|
170 | }
|
---|
171 |
|
---|
172 | if (name) {
|
---|
173 | ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
|
---|
174 | if (ret) {
|
---|
175 | (*error_string) = talloc_asprintf(cred, "failed to read krb5 ccache: %s: %s\n",
|
---|
176 | name,
|
---|
177 | smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
|
---|
178 | ret, ccc));
|
---|
179 | talloc_free(ccc);
|
---|
180 | return ret;
|
---|
181 | }
|
---|
182 | } else {
|
---|
183 | ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
|
---|
184 | if (ret) {
|
---|
185 | (*error_string) = talloc_asprintf(cred, "failed to read default krb5 ccache: %s\n",
|
---|
186 | smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
|
---|
187 | ret, ccc));
|
---|
188 | talloc_free(ccc);
|
---|
189 | return ret;
|
---|
190 | }
|
---|
191 | }
|
---|
192 |
|
---|
193 | talloc_set_destructor(ccc, free_dccache);
|
---|
194 |
|
---|
195 | ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
|
---|
196 |
|
---|
197 | if (ret == 0) {
|
---|
198 | krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
|
---|
199 | ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
|
---|
200 |
|
---|
201 | if (ret) {
|
---|
202 | (*error_string) = error_message(ret);
|
---|
203 | return ret;
|
---|
204 | }
|
---|
205 |
|
---|
206 | cred->ccache = ccc;
|
---|
207 | cred->ccache_obtained = obtained;
|
---|
208 | talloc_steal(cred, ccc);
|
---|
209 |
|
---|
210 | cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
|
---|
211 | return 0;
|
---|
212 | }
|
---|
213 | return 0;
|
---|
214 | }
|
---|
215 |
|
---|
216 | /*
|
---|
217 | * Indicate the we failed to log in to this service/host with these
|
---|
218 | * credentials. The caller passes an unsigned int which they
|
---|
219 | * initialise to the number of times they would like to retry.
|
---|
220 | *
|
---|
221 | * This method is used to support re-trying with freshly fetched
|
---|
222 | * credentials in case a server is rebuilt while clients have
|
---|
223 | * non-expired tickets. When the client code gets a logon failure they
|
---|
224 | * throw away the existing credentials for the server and retry.
|
---|
225 | */
|
---|
226 | _PUBLIC_ bool cli_credentials_failed_kerberos_login(struct cli_credentials *cred,
|
---|
227 | const char *principal,
|
---|
228 | unsigned int *count)
|
---|
229 | {
|
---|
230 | struct ccache_container *ccc;
|
---|
231 | krb5_creds creds, creds2;
|
---|
232 | int ret;
|
---|
233 |
|
---|
234 | if (principal == NULL) {
|
---|
235 | /* no way to delete if we don't know the principal */
|
---|
236 | return false;
|
---|
237 | }
|
---|
238 |
|
---|
239 | ccc = cred->ccache;
|
---|
240 | if (ccc == NULL) {
|
---|
241 | /* not a kerberos connection */
|
---|
242 | return false;
|
---|
243 | }
|
---|
244 |
|
---|
245 | if (*count > 0) {
|
---|
246 | /* We have already tried discarding the credentials */
|
---|
247 | return false;
|
---|
248 | }
|
---|
249 | (*count)++;
|
---|
250 |
|
---|
251 | ZERO_STRUCT(creds);
|
---|
252 | ret = krb5_parse_name(ccc->smb_krb5_context->krb5_context, principal, &creds.server);
|
---|
253 | if (ret != 0) {
|
---|
254 | return false;
|
---|
255 | }
|
---|
256 |
|
---|
257 | ret = krb5_cc_retrieve_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds, &creds2);
|
---|
258 | if (ret != 0) {
|
---|
259 | /* don't retry - we didn't find these credentials to remove */
|
---|
260 | krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
|
---|
261 | return false;
|
---|
262 | }
|
---|
263 |
|
---|
264 | ret = krb5_cc_remove_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds);
|
---|
265 | krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
|
---|
266 | krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds2);
|
---|
267 | if (ret != 0) {
|
---|
268 | /* don't retry - we didn't find these credentials to
|
---|
269 | * remove. Note that with the current backend this
|
---|
270 | * never happens, as it always returns 0 even if the
|
---|
271 | * creds don't exist, which is why we do a separate
|
---|
272 | * krb5_cc_retrieve_cred() above.
|
---|
273 | */
|
---|
274 | return false;
|
---|
275 | }
|
---|
276 | return true;
|
---|
277 | }
|
---|
278 |
|
---|
279 |
|
---|
280 | static int cli_credentials_new_ccache(struct cli_credentials *cred,
|
---|
281 | struct loadparm_context *lp_ctx,
|
---|
282 | char *ccache_name,
|
---|
283 | struct ccache_container **_ccc,
|
---|
284 | const char **error_string)
|
---|
285 | {
|
---|
286 | bool must_free_cc_name = false;
|
---|
287 | krb5_error_code ret;
|
---|
288 | struct ccache_container *ccc = talloc(cred, struct ccache_container);
|
---|
289 | if (!ccc) {
|
---|
290 | return ENOMEM;
|
---|
291 | }
|
---|
292 |
|
---|
293 | ret = cli_credentials_get_krb5_context(cred, lp_ctx,
|
---|
294 | &ccc->smb_krb5_context);
|
---|
295 | if (ret) {
|
---|
296 | talloc_free(ccc);
|
---|
297 | (*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s",
|
---|
298 | error_message(ret));
|
---|
299 | return ret;
|
---|
300 | }
|
---|
301 | if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
|
---|
302 | talloc_free(ccc);
|
---|
303 | (*error_string) = strerror(ENOMEM);
|
---|
304 | return ENOMEM;
|
---|
305 | }
|
---|
306 |
|
---|
307 | if (!ccache_name) {
|
---|
308 | must_free_cc_name = true;
|
---|
309 |
|
---|
310 | if (lpcfg_parm_bool(lp_ctx, NULL, "credentials", "krb5_cc_file", false)) {
|
---|
311 | ccache_name = talloc_asprintf(ccc, "FILE:/tmp/krb5_cc_samba_%u_%p",
|
---|
312 | (unsigned int)getpid(), ccc);
|
---|
313 | } else {
|
---|
314 | ccache_name = talloc_asprintf(ccc, "MEMORY:%p",
|
---|
315 | ccc);
|
---|
316 | }
|
---|
317 |
|
---|
318 | if (!ccache_name) {
|
---|
319 | talloc_free(ccc);
|
---|
320 | (*error_string) = strerror(ENOMEM);
|
---|
321 | return ENOMEM;
|
---|
322 | }
|
---|
323 | }
|
---|
324 |
|
---|
325 | ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name,
|
---|
326 | &ccc->ccache);
|
---|
327 | if (ret) {
|
---|
328 | (*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n",
|
---|
329 | ccache_name,
|
---|
330 | smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
|
---|
331 | ret, ccc));
|
---|
332 | talloc_free(ccache_name);
|
---|
333 | talloc_free(ccc);
|
---|
334 | return ret;
|
---|
335 | }
|
---|
336 |
|
---|
337 | if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
|
---|
338 | talloc_set_destructor(ccc, free_mccache);
|
---|
339 | } else {
|
---|
340 | talloc_set_destructor(ccc, free_dccache);
|
---|
341 | }
|
---|
342 |
|
---|
343 | if (must_free_cc_name) {
|
---|
344 | talloc_free(ccache_name);
|
---|
345 | }
|
---|
346 |
|
---|
347 | *_ccc = ccc;
|
---|
348 |
|
---|
349 | return 0;
|
---|
350 | }
|
---|
351 |
|
---|
352 | _PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred,
|
---|
353 | struct tevent_context *event_ctx,
|
---|
354 | struct loadparm_context *lp_ctx,
|
---|
355 | char *ccache_name,
|
---|
356 | struct ccache_container **ccc,
|
---|
357 | const char **error_string)
|
---|
358 | {
|
---|
359 | krb5_error_code ret;
|
---|
360 | enum credentials_obtained obtained;
|
---|
361 |
|
---|
362 | if (cred->machine_account_pending) {
|
---|
363 | cli_credentials_set_machine_account(cred, lp_ctx);
|
---|
364 | }
|
---|
365 |
|
---|
366 | if (cred->ccache_obtained >= cred->ccache_threshold &&
|
---|
367 | cred->ccache_obtained > CRED_UNINITIALISED) {
|
---|
368 | time_t lifetime;
|
---|
369 | bool expired = false;
|
---|
370 | ret = smb_krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context,
|
---|
371 | cred->ccache->ccache, &lifetime);
|
---|
372 | if (ret == KRB5_CC_END) {
|
---|
373 | /* If we have a particular ccache set, without
|
---|
374 | * an initial ticket, then assume there is a
|
---|
375 | * good reason */
|
---|
376 | } else if (ret == 0) {
|
---|
377 | if (lifetime == 0) {
|
---|
378 | DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
|
---|
379 | cli_credentials_get_principal(cred, cred)));
|
---|
380 | expired = true;
|
---|
381 | } else if (lifetime < 300) {
|
---|
382 | DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n",
|
---|
383 | cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
|
---|
384 | expired = true;
|
---|
385 | }
|
---|
386 | } else {
|
---|
387 | (*error_string) = talloc_asprintf(cred, "failed to get ccache lifetime: %s\n",
|
---|
388 | smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context,
|
---|
389 | ret, cred));
|
---|
390 | return ret;
|
---|
391 | }
|
---|
392 |
|
---|
393 | DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n",
|
---|
394 | cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
|
---|
395 |
|
---|
396 | if (!expired) {
|
---|
397 | *ccc = cred->ccache;
|
---|
398 | return 0;
|
---|
399 | }
|
---|
400 | }
|
---|
401 | if (cli_credentials_is_anonymous(cred)) {
|
---|
402 | (*error_string) = "Cannot get anonymous kerberos credentials";
|
---|
403 | return EINVAL;
|
---|
404 | }
|
---|
405 |
|
---|
406 | ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string);
|
---|
407 | if (ret) {
|
---|
408 | return ret;
|
---|
409 | }
|
---|
410 |
|
---|
411 | ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, event_ctx, (*ccc)->ccache, &obtained, error_string);
|
---|
412 | if (ret) {
|
---|
413 | return ret;
|
---|
414 | }
|
---|
415 |
|
---|
416 | ret = cli_credentials_set_from_ccache(cred, *ccc,
|
---|
417 | obtained, error_string);
|
---|
418 |
|
---|
419 | cred->ccache = *ccc;
|
---|
420 | cred->ccache_obtained = cred->principal_obtained;
|
---|
421 | if (ret) {
|
---|
422 | return ret;
|
---|
423 | }
|
---|
424 | cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
|
---|
425 | return 0;
|
---|
426 | }
|
---|
427 |
|
---|
428 | _PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred,
|
---|
429 | struct tevent_context *event_ctx,
|
---|
430 | struct loadparm_context *lp_ctx,
|
---|
431 | struct ccache_container **ccc,
|
---|
432 | const char **error_string)
|
---|
433 | {
|
---|
434 | return cli_credentials_get_named_ccache(cred, event_ctx, lp_ctx, NULL, ccc, error_string);
|
---|
435 | }
|
---|
436 |
|
---|
437 | /* We have good reason to think the ccache in these credentials is invalid - blow it away */
|
---|
438 | static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials *cred)
|
---|
439 | {
|
---|
440 | if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
|
---|
441 | talloc_unlink(cred, cred->client_gss_creds);
|
---|
442 | cred->client_gss_creds = NULL;
|
---|
443 | }
|
---|
444 | cred->client_gss_creds_obtained = CRED_UNINITIALISED;
|
---|
445 | }
|
---|
446 |
|
---|
447 | void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred,
|
---|
448 | enum credentials_obtained obtained)
|
---|
449 | {
|
---|
450 | /* If the caller just changed the username/password etc, then
|
---|
451 | * any cached credentials are now invalid */
|
---|
452 | if (obtained >= cred->client_gss_creds_obtained) {
|
---|
453 | if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
|
---|
454 | talloc_unlink(cred, cred->client_gss_creds);
|
---|
455 | cred->client_gss_creds = NULL;
|
---|
456 | }
|
---|
457 | cred->client_gss_creds_obtained = CRED_UNINITIALISED;
|
---|
458 | }
|
---|
459 | /* Now that we know that the data is 'this specified', then
|
---|
460 | * don't allow something less 'known' to be returned as a
|
---|
461 | * ccache. Ie, if the username is on the command line, we
|
---|
462 | * don't want to later guess to use a file-based ccache */
|
---|
463 | if (obtained > cred->client_gss_creds_threshold) {
|
---|
464 | cred->client_gss_creds_threshold = obtained;
|
---|
465 | }
|
---|
466 | }
|
---|
467 |
|
---|
468 | /* We have good reason to think this CCACHE is invalid. Blow it away */
|
---|
469 | static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials *cred)
|
---|
470 | {
|
---|
471 | if (cred->ccache_obtained > CRED_UNINITIALISED) {
|
---|
472 | talloc_unlink(cred, cred->ccache);
|
---|
473 | cred->ccache = NULL;
|
---|
474 | }
|
---|
475 | cred->ccache_obtained = CRED_UNINITIALISED;
|
---|
476 |
|
---|
477 | cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
|
---|
478 | }
|
---|
479 |
|
---|
480 | _PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred,
|
---|
481 | enum credentials_obtained obtained)
|
---|
482 | {
|
---|
483 | /* If the caller just changed the username/password etc, then
|
---|
484 | * any cached credentials are now invalid */
|
---|
485 | if (obtained >= cred->ccache_obtained) {
|
---|
486 | if (cred->ccache_obtained > CRED_UNINITIALISED) {
|
---|
487 | talloc_unlink(cred, cred->ccache);
|
---|
488 | cred->ccache = NULL;
|
---|
489 | }
|
---|
490 | cred->ccache_obtained = CRED_UNINITIALISED;
|
---|
491 | }
|
---|
492 | /* Now that we know that the data is 'this specified', then
|
---|
493 | * don't allow something less 'known' to be returned as a
|
---|
494 | * ccache. i.e, if the username is on the command line, we
|
---|
495 | * don't want to later guess to use a file-based ccache */
|
---|
496 | if (obtained > cred->ccache_threshold) {
|
---|
497 | cred->ccache_threshold = obtained;
|
---|
498 | }
|
---|
499 |
|
---|
500 | cli_credentials_invalidate_client_gss_creds(cred,
|
---|
501 | obtained);
|
---|
502 | }
|
---|
503 |
|
---|
504 | static int free_gssapi_creds(struct gssapi_creds_container *gcc)
|
---|
505 | {
|
---|
506 | OM_uint32 min_stat;
|
---|
507 | (void)gss_release_cred(&min_stat, &gcc->creds);
|
---|
508 | return 0;
|
---|
509 | }
|
---|
510 |
|
---|
511 | _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
|
---|
512 | struct tevent_context *event_ctx,
|
---|
513 | struct loadparm_context *lp_ctx,
|
---|
514 | struct gssapi_creds_container **_gcc,
|
---|
515 | const char **error_string)
|
---|
516 | {
|
---|
517 | int ret = 0;
|
---|
518 | OM_uint32 maj_stat, min_stat;
|
---|
519 | struct gssapi_creds_container *gcc;
|
---|
520 | struct ccache_container *ccache;
|
---|
521 | #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
|
---|
522 | gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
|
---|
523 | #endif
|
---|
524 | krb5_enctype *etypes = NULL;
|
---|
525 |
|
---|
526 | if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold &&
|
---|
527 | cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
|
---|
528 | bool expired = false;
|
---|
529 | OM_uint32 lifetime = 0;
|
---|
530 | gss_cred_usage_t usage = 0;
|
---|
531 | maj_stat = gss_inquire_cred(&min_stat, cred->client_gss_creds->creds,
|
---|
532 | NULL, &lifetime, &usage, NULL);
|
---|
533 | if (maj_stat == GSS_S_CREDENTIALS_EXPIRED) {
|
---|
534 | DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred, cred)));
|
---|
535 | expired = true;
|
---|
536 | } else if (maj_stat == GSS_S_COMPLETE && lifetime < 300) {
|
---|
537 | DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred, cred), lifetime));
|
---|
538 | expired = true;
|
---|
539 | } else if (maj_stat != GSS_S_COMPLETE) {
|
---|
540 | *error_string = talloc_asprintf(cred, "inquiry of credential lifefime via GSSAPI gss_inquire_cred failed: %s\n",
|
---|
541 | gssapi_error_string(cred, maj_stat, min_stat, NULL));
|
---|
542 | return EINVAL;
|
---|
543 | }
|
---|
544 | if (expired) {
|
---|
545 | cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
|
---|
546 | } else {
|
---|
547 | DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n",
|
---|
548 | cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
|
---|
549 |
|
---|
550 | *_gcc = cred->client_gss_creds;
|
---|
551 | return 0;
|
---|
552 | }
|
---|
553 | }
|
---|
554 |
|
---|
555 | ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
|
---|
556 | &ccache, error_string);
|
---|
557 | if (ret) {
|
---|
558 | if (cli_credentials_get_kerberos_state(cred) == CRED_MUST_USE_KERBEROS) {
|
---|
559 | DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string));
|
---|
560 | } else {
|
---|
561 | DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string));
|
---|
562 | }
|
---|
563 | return ret;
|
---|
564 | }
|
---|
565 |
|
---|
566 | gcc = talloc(cred, struct gssapi_creds_container);
|
---|
567 | if (!gcc) {
|
---|
568 | (*error_string) = error_message(ENOMEM);
|
---|
569 | return ENOMEM;
|
---|
570 | }
|
---|
571 |
|
---|
572 | maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
|
---|
573 | &gcc->creds);
|
---|
574 | if ((maj_stat == GSS_S_FAILURE) && (min_stat == (OM_uint32)KRB5_CC_END || min_stat == (OM_uint32) KRB5_CC_NOTFOUND)) {
|
---|
575 | /* This CCACHE is no good. Ensure we don't use it again */
|
---|
576 | cli_credentials_unconditionally_invalidate_ccache(cred);
|
---|
577 |
|
---|
578 | /* Now try again to get a ccache */
|
---|
579 | ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
|
---|
580 | &ccache, error_string);
|
---|
581 | if (ret) {
|
---|
582 | DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret)));
|
---|
583 | return ret;
|
---|
584 | }
|
---|
585 |
|
---|
586 | maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
|
---|
587 | &gcc->creds);
|
---|
588 |
|
---|
589 | }
|
---|
590 |
|
---|
591 | if (maj_stat) {
|
---|
592 | talloc_free(gcc);
|
---|
593 | if (min_stat) {
|
---|
594 | ret = min_stat;
|
---|
595 | } else {
|
---|
596 | ret = EINVAL;
|
---|
597 | }
|
---|
598 | (*error_string) = talloc_asprintf(cred, "gss_krb5_import_cred failed: %s", error_message(ret));
|
---|
599 | return ret;
|
---|
600 | }
|
---|
601 |
|
---|
602 |
|
---|
603 | /*
|
---|
604 | * transfer the enctypes from the smb_krb5_context to the gssapi layer
|
---|
605 | *
|
---|
606 | * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
|
---|
607 | * to configure the enctypes via the krb5.conf.
|
---|
608 | *
|
---|
609 | * And the gss_init_sec_context() creates it's own krb5_context and
|
---|
610 | * the TGS-REQ had all enctypes in it and only the ones configured
|
---|
611 | * and used for the AS-REQ, so it wasn't possible to disable the usage
|
---|
612 | * of AES keys.
|
---|
613 | */
|
---|
614 | min_stat = get_kerberos_allowed_etypes(ccache->smb_krb5_context->krb5_context,
|
---|
615 | &etypes);
|
---|
616 | if (min_stat == 0) {
|
---|
617 | OM_uint32 num_ktypes;
|
---|
618 |
|
---|
619 | for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
|
---|
620 |
|
---|
621 | maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
|
---|
622 | num_ktypes,
|
---|
623 | (int32_t *) etypes);
|
---|
624 | SAFE_FREE(etypes);
|
---|
625 | if (maj_stat) {
|
---|
626 | talloc_free(gcc);
|
---|
627 | if (min_stat) {
|
---|
628 | ret = min_stat;
|
---|
629 | } else {
|
---|
630 | ret = EINVAL;
|
---|
631 | }
|
---|
632 | (*error_string) = talloc_asprintf(cred, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret));
|
---|
633 | return ret;
|
---|
634 | }
|
---|
635 | }
|
---|
636 |
|
---|
637 | #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
|
---|
638 | /*
|
---|
639 | * Don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG.
|
---|
640 | *
|
---|
641 | * This allows us to disable SIGN and SEAL on a TLS connection with
|
---|
642 | * GSS-SPNENO. For example ldaps:// connections.
|
---|
643 | *
|
---|
644 | * https://groups.yahoo.com/neo/groups/cat-ietf/conversations/topics/575
|
---|
645 | * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938
|
---|
646 | */
|
---|
647 | maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
|
---|
648 | GSS_KRB5_CRED_NO_CI_FLAGS_X,
|
---|
649 | &empty_buffer);
|
---|
650 | if (maj_stat) {
|
---|
651 | talloc_free(gcc);
|
---|
652 | if (min_stat) {
|
---|
653 | ret = min_stat;
|
---|
654 | } else {
|
---|
655 | ret = EINVAL;
|
---|
656 | }
|
---|
657 | (*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret));
|
---|
658 | return ret;
|
---|
659 | }
|
---|
660 | #endif
|
---|
661 | cred->client_gss_creds_obtained = cred->ccache_obtained;
|
---|
662 | talloc_set_destructor(gcc, free_gssapi_creds);
|
---|
663 | cred->client_gss_creds = gcc;
|
---|
664 | *_gcc = gcc;
|
---|
665 | return 0;
|
---|
666 | }
|
---|
667 |
|
---|
668 | /**
|
---|
669 | Set a gssapi cred_id_t into the credentials system. (Client case)
|
---|
670 |
|
---|
671 | This grabs the credentials both 'intact' and getting the krb5
|
---|
672 | ccache out of it. This routine can be generalised in future for
|
---|
673 | the case where we deal with GSSAPI mechs other than krb5.
|
---|
674 |
|
---|
675 | On sucess, the caller must not free gssapi_cred, as it now belongs
|
---|
676 | to the credentials system.
|
---|
677 | */
|
---|
678 |
|
---|
679 | int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
|
---|
680 | struct loadparm_context *lp_ctx,
|
---|
681 | gss_cred_id_t gssapi_cred,
|
---|
682 | enum credentials_obtained obtained,
|
---|
683 | const char **error_string)
|
---|
684 | {
|
---|
685 | int ret;
|
---|
686 | OM_uint32 maj_stat, min_stat;
|
---|
687 | struct ccache_container *ccc;
|
---|
688 | struct gssapi_creds_container *gcc;
|
---|
689 | if (cred->client_gss_creds_obtained > obtained) {
|
---|
690 | return 0;
|
---|
691 | }
|
---|
692 |
|
---|
693 | gcc = talloc(cred, struct gssapi_creds_container);
|
---|
694 | if (!gcc) {
|
---|
695 | (*error_string) = error_message(ENOMEM);
|
---|
696 | return ENOMEM;
|
---|
697 | }
|
---|
698 |
|
---|
699 | ret = cli_credentials_new_ccache(cred, lp_ctx, NULL, &ccc, error_string);
|
---|
700 | if (ret != 0) {
|
---|
701 | return ret;
|
---|
702 | }
|
---|
703 |
|
---|
704 | maj_stat = gss_krb5_copy_ccache(&min_stat,
|
---|
705 | gssapi_cred, ccc->ccache);
|
---|
706 | if (maj_stat) {
|
---|
707 | if (min_stat) {
|
---|
708 | ret = min_stat;
|
---|
709 | } else {
|
---|
710 | ret = EINVAL;
|
---|
711 | }
|
---|
712 | if (ret) {
|
---|
713 | (*error_string) = error_message(ENOMEM);
|
---|
714 | }
|
---|
715 | }
|
---|
716 |
|
---|
717 | if (ret == 0) {
|
---|
718 | ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
|
---|
719 | }
|
---|
720 | cred->ccache = ccc;
|
---|
721 | cred->ccache_obtained = obtained;
|
---|
722 | if (ret == 0) {
|
---|
723 | gcc->creds = gssapi_cred;
|
---|
724 | talloc_set_destructor(gcc, free_gssapi_creds);
|
---|
725 |
|
---|
726 | /* set the clinet_gss_creds_obtained here, as it just
|
---|
727 | got set to UNINITIALISED by the calls above */
|
---|
728 | cred->client_gss_creds_obtained = obtained;
|
---|
729 | cred->client_gss_creds = gcc;
|
---|
730 | }
|
---|
731 | return ret;
|
---|
732 | }
|
---|
733 |
|
---|
734 | static int smb_krb5_create_salt_principal(TALLOC_CTX *mem_ctx,
|
---|
735 | const char *samAccountName,
|
---|
736 | const char *realm,
|
---|
737 | const char **salt_principal,
|
---|
738 | const char **error_string)
|
---|
739 | {
|
---|
740 | char *machine_username;
|
---|
741 | bool is_machine_account = false;
|
---|
742 | char *upper_realm;
|
---|
743 | TALLOC_CTX *tmp_ctx;
|
---|
744 | int rc = -1;
|
---|
745 |
|
---|
746 | if (samAccountName == NULL) {
|
---|
747 | *error_string = "Cannot determine salt principal, no "
|
---|
748 | "saltPrincipal or samAccountName specified";
|
---|
749 | return rc;
|
---|
750 | }
|
---|
751 |
|
---|
752 | if (realm == NULL) {
|
---|
753 | *error_string = "Cannot make principal without a realm";
|
---|
754 | return rc;
|
---|
755 | }
|
---|
756 |
|
---|
757 | tmp_ctx = talloc_new(mem_ctx);
|
---|
758 | if (tmp_ctx == NULL) {
|
---|
759 | *error_string = "Cannot allocate talloc context";
|
---|
760 | return rc;
|
---|
761 | }
|
---|
762 |
|
---|
763 | upper_realm = strupper_talloc(tmp_ctx, realm);
|
---|
764 | if (upper_realm == NULL) {
|
---|
765 | *error_string = "Cannot allocate to upper case realm";
|
---|
766 | goto out;
|
---|
767 | }
|
---|
768 |
|
---|
769 | machine_username = strlower_talloc(tmp_ctx, samAccountName);
|
---|
770 | if (!machine_username) {
|
---|
771 | *error_string = "Cannot duplicate samAccountName";
|
---|
772 | goto out;
|
---|
773 | }
|
---|
774 |
|
---|
775 | if (machine_username[strlen(machine_username) - 1] == '$') {
|
---|
776 | machine_username[strlen(machine_username) - 1] = '\0';
|
---|
777 | is_machine_account = true;
|
---|
778 | }
|
---|
779 |
|
---|
780 | if (is_machine_account) {
|
---|
781 | char *lower_realm;
|
---|
782 |
|
---|
783 | lower_realm = strlower_talloc(tmp_ctx, realm);
|
---|
784 | if (lower_realm == NULL) {
|
---|
785 | *error_string = "Cannot allocate to lower case realm";
|
---|
786 | goto out;
|
---|
787 | }
|
---|
788 |
|
---|
789 | *salt_principal = talloc_asprintf(mem_ctx,
|
---|
790 | "host/%s.%s@%s",
|
---|
791 | machine_username,
|
---|
792 | lower_realm,
|
---|
793 | upper_realm);
|
---|
794 | } else {
|
---|
795 | *salt_principal = talloc_asprintf(mem_ctx,
|
---|
796 | "%s@%s",
|
---|
797 | machine_username,
|
---|
798 | upper_realm);
|
---|
799 | }
|
---|
800 | if (*salt_principal == NULL) {
|
---|
801 | *error_string = "Cannot create salt principal";
|
---|
802 | goto out;
|
---|
803 | }
|
---|
804 |
|
---|
805 | rc = 0;
|
---|
806 | out:
|
---|
807 | talloc_free(tmp_ctx);
|
---|
808 | return rc;
|
---|
809 | }
|
---|
810 |
|
---|
811 | /* Get the keytab (actually, a container containing the krb5_keytab)
|
---|
812 | * attached to this context. If this hasn't been done or set before,
|
---|
813 | * it will be generated from the password.
|
---|
814 | */
|
---|
815 | _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred,
|
---|
816 | struct loadparm_context *lp_ctx,
|
---|
817 | struct keytab_container **_ktc)
|
---|
818 | {
|
---|
819 | krb5_error_code ret;
|
---|
820 | struct keytab_container *ktc;
|
---|
821 | struct smb_krb5_context *smb_krb5_context;
|
---|
822 | const char *keytab_name;
|
---|
823 | krb5_keytab keytab;
|
---|
824 | TALLOC_CTX *mem_ctx;
|
---|
825 | const char *username = cli_credentials_get_username(cred);
|
---|
826 | const char *realm = cli_credentials_get_realm(cred);
|
---|
827 | const char *error_string;
|
---|
828 | const char *salt_principal;
|
---|
829 |
|
---|
830 | if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
|
---|
831 | cred->username_obtained))) {
|
---|
832 | *_ktc = cred->keytab;
|
---|
833 | return 0;
|
---|
834 | }
|
---|
835 |
|
---|
836 | if (cli_credentials_is_anonymous(cred)) {
|
---|
837 | return EINVAL;
|
---|
838 | }
|
---|
839 |
|
---|
840 | ret = cli_credentials_get_krb5_context(cred, lp_ctx,
|
---|
841 | &smb_krb5_context);
|
---|
842 | if (ret) {
|
---|
843 | return ret;
|
---|
844 | }
|
---|
845 |
|
---|
846 | mem_ctx = talloc_new(cred);
|
---|
847 | if (!mem_ctx) {
|
---|
848 | return ENOMEM;
|
---|
849 | }
|
---|
850 |
|
---|
851 | /*
|
---|
852 | * FIXME: Currently there is no better way than to create the correct
|
---|
853 | * salt principal by checking if the username ends with a '$'. It would
|
---|
854 | * be better if it is part of the credentials.
|
---|
855 | */
|
---|
856 | ret = smb_krb5_create_salt_principal(mem_ctx,
|
---|
857 | username,
|
---|
858 | realm,
|
---|
859 | &salt_principal,
|
---|
860 | &error_string);
|
---|
861 | if (ret) {
|
---|
862 | talloc_free(mem_ctx);
|
---|
863 | return ret;
|
---|
864 | }
|
---|
865 |
|
---|
866 | ret = smb_krb5_create_memory_keytab(mem_ctx,
|
---|
867 | smb_krb5_context->krb5_context,
|
---|
868 | cli_credentials_get_password(cred),
|
---|
869 | username,
|
---|
870 | realm,
|
---|
871 | salt_principal,
|
---|
872 | cli_credentials_get_kvno(cred),
|
---|
873 | &keytab,
|
---|
874 | &keytab_name);
|
---|
875 | if (ret) {
|
---|
876 | talloc_free(mem_ctx);
|
---|
877 | return ret;
|
---|
878 | }
|
---|
879 |
|
---|
880 | ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
|
---|
881 | keytab, keytab_name, &ktc);
|
---|
882 | if (ret) {
|
---|
883 | talloc_free(mem_ctx);
|
---|
884 | return ret;
|
---|
885 | }
|
---|
886 |
|
---|
887 | cred->keytab_obtained = (MAX(cred->principal_obtained,
|
---|
888 | cred->username_obtained));
|
---|
889 |
|
---|
890 | /* We make this keytab up based on a password. Therefore
|
---|
891 | * match-by-key is acceptable, we can't match on the wrong
|
---|
892 | * principal */
|
---|
893 | ktc->password_based = true;
|
---|
894 |
|
---|
895 | talloc_steal(cred, ktc);
|
---|
896 | cred->keytab = ktc;
|
---|
897 | *_ktc = cred->keytab;
|
---|
898 | talloc_free(mem_ctx);
|
---|
899 | return ret;
|
---|
900 | }
|
---|
901 |
|
---|
902 | /* Given the name of a keytab (presumably in the format
|
---|
903 | * FILE:/etc/krb5.keytab), open it and attach it */
|
---|
904 |
|
---|
905 | _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
|
---|
906 | struct loadparm_context *lp_ctx,
|
---|
907 | const char *keytab_name,
|
---|
908 | enum credentials_obtained obtained)
|
---|
909 | {
|
---|
910 | krb5_error_code ret;
|
---|
911 | struct keytab_container *ktc;
|
---|
912 | struct smb_krb5_context *smb_krb5_context;
|
---|
913 | TALLOC_CTX *mem_ctx;
|
---|
914 |
|
---|
915 | if (cred->keytab_obtained >= obtained) {
|
---|
916 | return 0;
|
---|
917 | }
|
---|
918 |
|
---|
919 | ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
|
---|
920 | if (ret) {
|
---|
921 | return ret;
|
---|
922 | }
|
---|
923 |
|
---|
924 | mem_ctx = talloc_new(cred);
|
---|
925 | if (!mem_ctx) {
|
---|
926 | return ENOMEM;
|
---|
927 | }
|
---|
928 |
|
---|
929 | ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
|
---|
930 | NULL, keytab_name, &ktc);
|
---|
931 | if (ret) {
|
---|
932 | return ret;
|
---|
933 | }
|
---|
934 |
|
---|
935 | cred->keytab_obtained = obtained;
|
---|
936 |
|
---|
937 | talloc_steal(cred, ktc);
|
---|
938 | cred->keytab = ktc;
|
---|
939 | talloc_free(mem_ctx);
|
---|
940 |
|
---|
941 | return ret;
|
---|
942 | }
|
---|
943 |
|
---|
944 | /* Get server gss credentials (in gsskrb5, this means the keytab) */
|
---|
945 |
|
---|
946 | _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
|
---|
947 | struct loadparm_context *lp_ctx,
|
---|
948 | struct gssapi_creds_container **_gcc)
|
---|
949 | {
|
---|
950 | int ret = 0;
|
---|
951 | OM_uint32 maj_stat, min_stat;
|
---|
952 | struct gssapi_creds_container *gcc;
|
---|
953 | struct keytab_container *ktc;
|
---|
954 | struct smb_krb5_context *smb_krb5_context;
|
---|
955 | TALLOC_CTX *mem_ctx;
|
---|
956 | krb5_principal princ;
|
---|
957 | const char *error_string;
|
---|
958 | enum credentials_obtained obtained;
|
---|
959 |
|
---|
960 | mem_ctx = talloc_new(cred);
|
---|
961 | if (!mem_ctx) {
|
---|
962 | return ENOMEM;
|
---|
963 | }
|
---|
964 |
|
---|
965 | ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
|
---|
966 | if (ret) {
|
---|
967 | return ret;
|
---|
968 | }
|
---|
969 |
|
---|
970 | ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
|
---|
971 | if (ret) {
|
---|
972 | DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
|
---|
973 | error_string));
|
---|
974 | talloc_free(mem_ctx);
|
---|
975 | return ret;
|
---|
976 | }
|
---|
977 |
|
---|
978 | if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
|
---|
979 | talloc_free(mem_ctx);
|
---|
980 | *_gcc = cred->server_gss_creds;
|
---|
981 | return 0;
|
---|
982 | }
|
---|
983 |
|
---|
984 | ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
|
---|
985 | if (ret) {
|
---|
986 | DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
|
---|
987 | return ret;
|
---|
988 | }
|
---|
989 |
|
---|
990 | gcc = talloc(cred, struct gssapi_creds_container);
|
---|
991 | if (!gcc) {
|
---|
992 | talloc_free(mem_ctx);
|
---|
993 | return ENOMEM;
|
---|
994 | }
|
---|
995 |
|
---|
996 | if (ktc->password_based || obtained < CRED_SPECIFIED) {
|
---|
997 | /* This creates a GSSAPI cred_id_t for match-by-key with only the keytab set */
|
---|
998 | maj_stat = gss_krb5_import_cred(&min_stat, NULL, NULL, ktc->keytab,
|
---|
999 | &gcc->creds);
|
---|
1000 | } else {
|
---|
1001 | /* This creates a GSSAPI cred_id_t with the principal and keytab set, matching by name */
|
---|
1002 | maj_stat = gss_krb5_import_cred(&min_stat, NULL, princ, ktc->keytab,
|
---|
1003 | &gcc->creds);
|
---|
1004 | }
|
---|
1005 | if (maj_stat) {
|
---|
1006 | if (min_stat) {
|
---|
1007 | ret = min_stat;
|
---|
1008 | } else {
|
---|
1009 | ret = EINVAL;
|
---|
1010 | }
|
---|
1011 | }
|
---|
1012 | if (ret == 0) {
|
---|
1013 | cred->server_gss_creds_obtained = cred->keytab_obtained;
|
---|
1014 | talloc_set_destructor(gcc, free_gssapi_creds);
|
---|
1015 | cred->server_gss_creds = gcc;
|
---|
1016 | *_gcc = gcc;
|
---|
1017 | }
|
---|
1018 | talloc_free(mem_ctx);
|
---|
1019 | return ret;
|
---|
1020 | }
|
---|
1021 |
|
---|
1022 | /**
|
---|
1023 | * Set Kerberos KVNO
|
---|
1024 | */
|
---|
1025 |
|
---|
1026 | _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
|
---|
1027 | int kvno)
|
---|
1028 | {
|
---|
1029 | cred->kvno = kvno;
|
---|
1030 | }
|
---|
1031 |
|
---|
1032 | /**
|
---|
1033 | * Return Kerberos KVNO
|
---|
1034 | */
|
---|
1035 |
|
---|
1036 | _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
|
---|
1037 | {
|
---|
1038 | return cred->kvno;
|
---|
1039 | }
|
---|
1040 |
|
---|
1041 |
|
---|
1042 | const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
|
---|
1043 | {
|
---|
1044 | return cred->salt_principal;
|
---|
1045 | }
|
---|
1046 |
|
---|
1047 | _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
|
---|
1048 | {
|
---|
1049 | talloc_free(cred->salt_principal);
|
---|
1050 | cred->salt_principal = talloc_strdup(cred, principal);
|
---|
1051 | }
|
---|
1052 |
|
---|
1053 | /* The 'impersonate_principal' is used to allow one Kerberos principal
|
---|
1054 | * (and it's associated keytab etc) to impersonate another. The
|
---|
1055 | * ability to do this is controlled by the KDC, but it is generally
|
---|
1056 | * permitted to impersonate anyone to yourself. This allows any
|
---|
1057 | * member of the domain to get the groups of a user. This is also
|
---|
1058 | * known as S4U2Self */
|
---|
1059 |
|
---|
1060 | _PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
|
---|
1061 | {
|
---|
1062 | return cred->impersonate_principal;
|
---|
1063 | }
|
---|
1064 |
|
---|
1065 | /*
|
---|
1066 | * The 'self_service' is the service principal that
|
---|
1067 | * represents the same object (by its objectSid)
|
---|
1068 | * as the client principal (typically our machine account).
|
---|
1069 | * When trying to impersonate 'impersonate_principal' with
|
---|
1070 | * S4U2Self.
|
---|
1071 | */
|
---|
1072 | _PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
|
---|
1073 | {
|
---|
1074 | return cred->self_service;
|
---|
1075 | }
|
---|
1076 |
|
---|
1077 | _PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
|
---|
1078 | const char *principal,
|
---|
1079 | const char *self_service)
|
---|
1080 | {
|
---|
1081 | talloc_free(cred->impersonate_principal);
|
---|
1082 | cred->impersonate_principal = talloc_strdup(cred, principal);
|
---|
1083 | talloc_free(cred->self_service);
|
---|
1084 | cred->self_service = talloc_strdup(cred, self_service);
|
---|
1085 | cli_credentials_set_kerberos_state(cred, CRED_MUST_USE_KERBEROS);
|
---|
1086 | }
|
---|
1087 |
|
---|
1088 | /*
|
---|
1089 | * when impersonating for S4U2proxy we need to set the target principal.
|
---|
1090 | * Similarly, we may only be authorized to do general impersonation to
|
---|
1091 | * some particular services.
|
---|
1092 | *
|
---|
1093 | * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
|
---|
1094 | *
|
---|
1095 | * NULL means that tickets will be obtained for the krbtgt service.
|
---|
1096 | */
|
---|
1097 |
|
---|
1098 | const char *cli_credentials_get_target_service(struct cli_credentials *cred)
|
---|
1099 | {
|
---|
1100 | return cred->target_service;
|
---|
1101 | }
|
---|
1102 |
|
---|
1103 | _PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
|
---|
1104 | {
|
---|
1105 | talloc_free(cred->target_service);
|
---|
1106 | cred->target_service = talloc_strdup(cred, target_service);
|
---|
1107 | }
|
---|
1108 |
|
---|