2 Unix SMB/CIFS implementation.
3 kerberos utility library
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Remus Koos 2001
6 Copyright (C) Nalin Dahyabhai 2004.
7 Copyright (C) Jeremy Allison 2004.
8 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
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/>.
25 #include "system/kerberos.h"
26 #include "auth/kerberos/kerberos.h"
31 simulate a kinit, putting the tgt in the given credentials cache.
32 Orignally by remus@snapserver.com
34 This version is built to use a keyblock, rather than needing the
37 The impersonate_principal is the principal if NULL, or the principal to impersonate
39 The target_service defaults to the krbtgt if NULL, but could be kpasswd/realm or the local service (if we are doing s4u2self)
41 krb5_error_code
kerberos_kinit_keyblock_cc(krb5_context ctx
, krb5_ccache cc
,
42 krb5_principal principal
, krb5_keyblock
*keyblock
,
43 const char *target_service
,
44 krb5_get_init_creds_opt
*krb_options
,
45 time_t *expire_time
, time_t *kdc_time
)
47 krb5_error_code code
= 0;
50 if ((code
= krb5_get_init_creds_keyblock(ctx
, &my_creds
, principal
, keyblock
,
51 0, target_service
, krb_options
))) {
55 if ((code
= krb5_cc_initialize(ctx
, cc
, principal
))) {
56 krb5_free_cred_contents(ctx
, &my_creds
);
60 if ((code
= krb5_cc_store_cred(ctx
, cc
, &my_creds
))) {
61 krb5_free_cred_contents(ctx
, &my_creds
);
66 *expire_time
= (time_t) my_creds
.times
.endtime
;
70 *kdc_time
= (time_t) my_creds
.times
.starttime
;
73 krb5_free_cred_contents(ctx
, &my_creds
);
79 simulate a kinit, putting the tgt in the given credentials cache.
80 Orignally by remus@snapserver.com
82 The impersonate_principal is the principal if NULL, or the principal to impersonate
84 The self_service, should be the local service (for S4U2Self if impersonate_principal is given).
86 The target_service defaults to the krbtgt if NULL, but could be kpasswd/realm or a remote service (for S4U2Proxy)
89 krb5_error_code
kerberos_kinit_password_cc(krb5_context ctx
, krb5_ccache store_cc
,
90 krb5_principal init_principal
,
91 const char *init_password
,
92 krb5_principal impersonate_principal
,
93 const char *self_service
,
94 const char *target_service
,
95 krb5_get_init_creds_opt
*krb_options
,
96 time_t *expire_time
, time_t *kdc_time
)
98 krb5_error_code code
= 0;
99 krb5_get_creds_opt options
;
100 krb5_principal store_principal
;
101 krb5_creds store_creds
;
102 krb5_creds
*s4u2self_creds
;
103 Ticket s4u2self_ticket
;
104 size_t s4u2self_ticketlen
;
105 krb5_creds
*s4u2proxy_creds
;
106 krb5_principal self_princ
;
108 krb5_principal target_princ
;
110 const char *self_realm
;
111 krb5_principal blacklist_principal
= NULL
;
112 krb5_principal whitelist_principal
= NULL
;
114 if (impersonate_principal
&& self_service
== NULL
) {
119 * If we are not impersonating, then get this ticket for the
120 * target service, otherwise a krbtgt, and get the next ticket
123 code
= krb5_get_init_creds_password(ctx
, &store_creds
,
128 impersonate_principal
? NULL
: target_service
,
134 store_principal
= init_principal
;
136 if (impersonate_principal
== NULL
) {
141 * We are trying S4U2Self now:
143 * As we do not want to expose our TGT in the
144 * krb5_ccache, which is also holds the impersonated creds.
146 * Some low level krb5/gssapi function might use the TGT
147 * identity and let the client act as our machine account.
149 * We need to avoid that and use a temporary krb5_ccache
150 * in order to pass our TGT to the krb5_get_creds() function.
152 code
= krb5_cc_new_unique(ctx
, NULL
, NULL
, &tmp_cc
);
154 krb5_free_cred_contents(ctx
, &store_creds
);
158 code
= krb5_cc_initialize(ctx
, tmp_cc
, store_creds
.client
);
160 krb5_cc_destroy(ctx
, tmp_cc
);
161 krb5_free_cred_contents(ctx
, &store_creds
);
165 code
= krb5_cc_store_cred(ctx
, tmp_cc
, &store_creds
);
167 krb5_free_cred_contents(ctx
, &store_creds
);
168 krb5_cc_destroy(ctx
, tmp_cc
);
173 * we need to remember the client principal of our
174 * TGT and make sure the KDC does not return this
175 * in the impersonated tickets. This can happen
176 * if the KDC does not support S4U2Self and S4U2Proxy.
178 blacklist_principal
= store_creds
.client
;
179 store_creds
.client
= NULL
;
180 krb5_free_cred_contents(ctx
, &store_creds
);
183 * Check if we also need S4U2Proxy or if S4U2Self is
184 * enough in order to get a ticket for the target.
186 if (target_service
== NULL
) {
188 } else if (strcmp(target_service
, self_service
) == 0) {
195 * For S4U2Self we need our own service principal,
196 * which belongs to our own realm (available on
197 * our client principal).
199 self_realm
= krb5_principal_get_realm(ctx
, init_principal
);
201 code
= krb5_parse_name(ctx
, self_service
, &self_princ
);
203 krb5_free_principal(ctx
, blacklist_principal
);
204 krb5_cc_destroy(ctx
, tmp_cc
);
208 code
= krb5_principal_set_realm(ctx
, self_princ
, self_realm
);
210 krb5_free_principal(ctx
, blacklist_principal
);
211 krb5_free_principal(ctx
, self_princ
);
212 krb5_cc_destroy(ctx
, tmp_cc
);
216 code
= krb5_get_creds_opt_alloc(ctx
, &options
);
218 krb5_free_principal(ctx
, blacklist_principal
);
219 krb5_free_principal(ctx
, self_princ
);
220 krb5_cc_destroy(ctx
, tmp_cc
);
226 * If we want S4U2Proxy, we need the forwardable flag
227 * on the S4U2Self ticket.
229 krb5_get_creds_opt_set_options(ctx
, options
, KRB5_GC_FORWARDABLE
);
232 code
= krb5_get_creds_opt_set_impersonate(ctx
, options
,
233 impersonate_principal
);
235 krb5_get_creds_opt_free(ctx
, options
);
236 krb5_free_principal(ctx
, blacklist_principal
);
237 krb5_free_principal(ctx
, self_princ
);
238 krb5_cc_destroy(ctx
, tmp_cc
);
242 code
= krb5_get_creds(ctx
, options
, tmp_cc
,
243 self_princ
, &s4u2self_creds
);
244 krb5_get_creds_opt_free(ctx
, options
);
245 krb5_free_principal(ctx
, self_princ
);
247 krb5_free_principal(ctx
, blacklist_principal
);
248 krb5_cc_destroy(ctx
, tmp_cc
);
253 krb5_cc_destroy(ctx
, tmp_cc
);
256 * Now make sure we store the impersonated principal
257 * and creds instead of the TGT related stuff
258 * in the krb5_ccache of the caller.
260 code
= krb5_copy_creds_contents(ctx
, s4u2self_creds
,
262 krb5_free_creds(ctx
, s4u2self_creds
);
268 * It's important to store the principal the KDC
269 * returned, as otherwise the caller would not find
270 * the S4U2Self ticket in the krb5_ccache lookup.
272 store_principal
= store_creds
.client
;
277 * We are trying S4U2Proxy:
279 * We need the ticket from the S4U2Self step
280 * and our TGT in order to get the delegated ticket.
282 code
= decode_Ticket((const uint8_t *)s4u2self_creds
->ticket
.data
,
283 s4u2self_creds
->ticket
.length
,
285 &s4u2self_ticketlen
);
287 krb5_free_creds(ctx
, s4u2self_creds
);
288 krb5_free_principal(ctx
, blacklist_principal
);
289 krb5_cc_destroy(ctx
, tmp_cc
);
294 * we need to remember the client principal of the
295 * S4U2Self stage and as it needs to match the one we
296 * will get for the S4U2Proxy stage. We need this
297 * in order to detect KDCs which does not support S4U2Proxy.
299 whitelist_principal
= s4u2self_creds
->client
;
300 s4u2self_creds
->client
= NULL
;
301 krb5_free_creds(ctx
, s4u2self_creds
);
304 * For S4U2Proxy we also got a target service principal,
305 * which also belongs to our own realm (available on
306 * our client principal).
308 code
= krb5_parse_name(ctx
, target_service
, &target_princ
);
310 free_Ticket(&s4u2self_ticket
);
311 krb5_free_principal(ctx
, whitelist_principal
);
312 krb5_free_principal(ctx
, blacklist_principal
);
313 krb5_cc_destroy(ctx
, tmp_cc
);
317 code
= krb5_principal_set_realm(ctx
, target_princ
, self_realm
);
319 free_Ticket(&s4u2self_ticket
);
320 krb5_free_principal(ctx
, target_princ
);
321 krb5_free_principal(ctx
, whitelist_principal
);
322 krb5_free_principal(ctx
, blacklist_principal
);
323 krb5_cc_destroy(ctx
, tmp_cc
);
327 code
= krb5_get_creds_opt_alloc(ctx
, &options
);
329 free_Ticket(&s4u2self_ticket
);
330 krb5_free_principal(ctx
, target_princ
);
331 krb5_free_principal(ctx
, whitelist_principal
);
332 krb5_free_principal(ctx
, blacklist_principal
);
333 krb5_cc_destroy(ctx
, tmp_cc
);
337 krb5_get_creds_opt_set_options(ctx
, options
, KRB5_GC_FORWARDABLE
);
338 krb5_get_creds_opt_set_options(ctx
, options
, KRB5_GC_CONSTRAINED_DELEGATION
);
340 code
= krb5_get_creds_opt_set_ticket(ctx
, options
, &s4u2self_ticket
);
341 free_Ticket(&s4u2self_ticket
);
343 krb5_get_creds_opt_free(ctx
, options
);
344 krb5_free_principal(ctx
, target_princ
);
345 krb5_free_principal(ctx
, whitelist_principal
);
346 krb5_free_principal(ctx
, blacklist_principal
);
347 krb5_cc_destroy(ctx
, tmp_cc
);
351 code
= krb5_get_creds(ctx
, options
, tmp_cc
,
352 target_princ
, &s4u2proxy_creds
);
353 krb5_get_creds_opt_free(ctx
, options
);
354 krb5_free_principal(ctx
, target_princ
);
355 krb5_cc_destroy(ctx
, tmp_cc
);
357 krb5_free_principal(ctx
, whitelist_principal
);
358 krb5_free_principal(ctx
, blacklist_principal
);
363 * Now make sure we store the impersonated principal
364 * and creds instead of the TGT related stuff
365 * in the krb5_ccache of the caller.
367 code
= krb5_copy_creds_contents(ctx
, s4u2proxy_creds
,
369 krb5_free_creds(ctx
, s4u2proxy_creds
);
371 krb5_free_principal(ctx
, whitelist_principal
);
372 krb5_free_principal(ctx
, blacklist_principal
);
377 * It's important to store the principal the KDC
378 * returned, as otherwise the caller would not find
379 * the S4U2Self ticket in the krb5_ccache lookup.
381 store_principal
= store_creds
.client
;
384 if (blacklist_principal
&&
385 krb5_principal_compare(ctx
, store_creds
.client
, blacklist_principal
)) {
389 code
= krb5_unparse_name(ctx
, blacklist_principal
, &sp
);
393 code
= krb5_unparse_name(ctx
, impersonate_principal
, &ip
);
397 DEBUG(1, ("kerberos_kinit_password_cc: "
398 "KDC returned self principal[%s] while impersonating [%s]\n",
400 ip
?ip
:"<no memory>"));
405 krb5_free_principal(ctx
, whitelist_principal
);
406 krb5_free_principal(ctx
, blacklist_principal
);
407 krb5_free_cred_contents(ctx
, &store_creds
);
408 return KRB5_FWD_BAD_PRINCIPAL
;
410 if (blacklist_principal
) {
411 krb5_free_principal(ctx
, blacklist_principal
);
414 if (whitelist_principal
&&
415 !krb5_principal_compare(ctx
, store_creds
.client
, whitelist_principal
)) {
419 code
= krb5_unparse_name(ctx
, store_creds
.client
, &sp
);
423 code
= krb5_unparse_name(ctx
, whitelist_principal
, &ep
);
427 DEBUG(1, ("kerberos_kinit_password_cc: "
428 "KDC returned wrong principal[%s] we expected [%s]\n",
430 ep
?ep
:"<no memory>"));
435 krb5_free_principal(ctx
, whitelist_principal
);
436 krb5_free_cred_contents(ctx
, &store_creds
);
437 return KRB5_FWD_BAD_PRINCIPAL
;
439 if (whitelist_principal
) {
440 krb5_free_principal(ctx
, whitelist_principal
);
443 code
= krb5_cc_initialize(ctx
, store_cc
, store_principal
);
445 krb5_free_cred_contents(ctx
, &store_creds
);
449 code
= krb5_cc_store_cred(ctx
, store_cc
, &store_creds
);
451 krb5_free_cred_contents(ctx
, &store_creds
);
456 *expire_time
= (time_t) store_creds
.times
.endtime
;
460 *kdc_time
= (time_t) store_creds
.times
.starttime
;
463 krb5_free_cred_contents(ctx
, &store_creds
);