1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/krb/gc_via_tkt.c */
4 * Copyright 1990,1991,2007-2009 by the Massachusetts Institute of Technology.
7 * Export of this software from the United States of America may
8 * require a specific license from the United States Government.
9 * It is the responsibility of any person or organization contemplating
10 * export to obtain such a license before exporting.
12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13 * distribute this software and its documentation for any purpose and
14 * without fee is hereby granted, provided that the above copyright
15 * notice appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation, and that
17 * the name of M.I.T. not be used in advertising or publicity pertaining
18 * to distribution of the software without specific, written prior
19 * permission. Furthermore if you modify this software you must label
20 * your software as modified software and not distribute it in such a
21 * fashion that it might be confused with the original M.I.T. software.
22 * M.I.T. makes no representations about the suitability of
23 * this software for any purpose. It is provided "as is" without express
24 * or implied warranty.
28 * Given a tkt, and a target cred, get it.
29 * Assumes that the kdc_rep has been decrypted.
33 #include "int-proto.h"
36 static krb5_error_code
37 kdcrep2creds(krb5_context context, krb5_kdc_rep *pkdcrep, krb5_address *const *address,
38 krb5_data *psectkt, krb5_creds **ppcreds)
40 krb5_error_code retval;
43 if ((*ppcreds = (krb5_creds *)calloc(1,sizeof(krb5_creds))) == NULL) {
47 if ((retval = krb5_copy_principal(context, pkdcrep->client,
48 &(*ppcreds)->client)))
51 if ((retval = krb5_copy_principal(context, pkdcrep->enc_part2->server,
52 &(*ppcreds)->server)))
55 if ((retval = krb5_copy_keyblock_contents(context,
56 pkdcrep->enc_part2->session,
57 &(*ppcreds)->keyblock)))
59 TRACE_TGS_REPLY(context, (*ppcreds)->client, (*ppcreds)->server,
60 &(*ppcreds)->keyblock);
62 if ((retval = krb5_copy_data(context, psectkt, &pdata)))
63 goto cleanup_keyblock;
64 (*ppcreds)->second_ticket = *pdata;
67 (*ppcreds)->ticket_flags = pkdcrep->enc_part2->flags;
68 (*ppcreds)->times = pkdcrep->enc_part2->times;
69 (*ppcreds)->magic = KV5M_CREDS;
71 (*ppcreds)->authdata = NULL; /* not used */
72 (*ppcreds)->is_skey = psectkt->length != 0;
74 if (pkdcrep->enc_part2->caddrs) {
75 if ((retval = krb5_copy_addresses(context, pkdcrep->enc_part2->caddrs,
76 &(*ppcreds)->addresses)))
77 goto cleanup_keyblock;
79 /* no addresses in the list means we got what we had */
80 if ((retval = krb5_copy_addresses(context, address,
81 &(*ppcreds)->addresses)))
82 goto cleanup_keyblock;
85 if ((retval = encode_krb5_ticket(pkdcrep->ticket, &pdata)))
86 goto cleanup_keyblock;
88 (*ppcreds)->ticket = *pdata;
93 krb5_free_keyblock_contents(context, &(*ppcreds)->keyblock);
101 static krb5_error_code
102 check_reply_server(krb5_context context, krb5_flags kdcoptions,
103 krb5_creds *in_cred, krb5_kdc_rep *dec_rep)
106 if (!krb5_principal_compare(context, dec_rep->ticket->server,
107 dec_rep->enc_part2->server))
108 return KRB5_KDCREP_MODIFIED;
110 /* Reply is self-consistent. */
112 if (krb5_principal_compare(context, dec_rep->ticket->server,
116 /* Server in reply differs from what we requested. */
118 if (kdcoptions & KDC_OPT_CANONICALIZE) {
119 /* in_cred server differs from ticket returned, but ticket
120 returned is consistent and we requested canonicalization. */
122 TRACE_CHECK_REPLY_SERVER_DIFFERS(context, in_cred->server,
123 dec_rep->enc_part2->server);
127 /* We didn't request canonicalization. */
129 if (!IS_TGS_PRINC(context, in_cred->server) ||
130 !IS_TGS_PRINC(context, dec_rep->ticket->server)) {
131 /* Canonicalization not requested, and not a TGS referral. */
132 return KRB5_KDCREP_MODIFIED;
136 * Is this check needed? find_nxt_kdc() in gc_frm_kdc.c already
137 * effectively checks this.
139 if (krb5_realm_compare(context, in_cred->client, in_cred->server) &&
140 data_eq(*in_cred->server->data[1], *in_cred->client->realm)) {
141 /* Attempted to rewrite local TGS. */
142 return KRB5_KDCREP_MODIFIED;
148 /* Return true if a TGS credential is for the client's local realm. */
150 tgt_is_local_realm(krb5_creds *tgt)
152 return (tgt->server->length == 2
153 && data_eq_string(tgt->server->data[0], KRB5_TGS_NAME)
154 && data_eq(tgt->server->data[1], tgt->client->realm)
155 && data_eq(tgt->server->realm, tgt->client->realm));
159 krb5_get_cred_via_tkt(krb5_context context, krb5_creds *tkt,
160 krb5_flags kdcoptions, krb5_address *const *address,
161 krb5_creds *in_cred, krb5_creds **out_cred)
163 return krb5_get_cred_via_tkt_ext (context, tkt,
165 NULL, in_cred, NULL, NULL,
166 NULL, NULL, out_cred, NULL);
170 krb5int_make_tgs_request(krb5_context context,
171 struct krb5int_fast_request_state *fast_state,
173 krb5_flags kdcoptions,
174 krb5_address *const *address,
175 krb5_pa_data **in_padata,
177 krb5_error_code (*pacb_fct)(krb5_context,
182 krb5_data *request_data,
183 krb5_timestamp *timestamp,
185 krb5_keyblock **subkey)
187 krb5_error_code retval;
188 krb5_enctype *enctypes = NULL;
189 krb5_boolean second_tkt;
191 request_data->data = NULL;
195 /* tkt->client must be equal to in_cred->client */
196 if (!krb5_principal_compare(context, tkt->client, in_cred->client))
197 return KRB5_PRINC_NOMATCH;
199 if (!tkt->ticket.length)
200 return KRB5_NO_TKT_SUPPLIED;
202 second_tkt = ((kdcoptions & (KDC_OPT_ENC_TKT_IN_SKEY |
203 KDC_OPT_CNAME_IN_ADDL_TKT)) != 0);
204 if (second_tkt && !in_cred->second_ticket.length)
205 return KRB5_NO_2ND_TKT;
207 if (in_cred->keyblock.enctype) {
208 enctypes = (krb5_enctype *)malloc(sizeof(krb5_enctype)*2);
209 if (enctypes == NULL)
211 enctypes[0] = in_cred->keyblock.enctype;
215 retval = krb5int_make_tgs_request_ext(context, fast_state, kdcoptions,
217 enctypes, in_cred->server, address,
218 in_cred->authdata, in_padata,
220 &in_cred->second_ticket : 0,
221 tkt, pacb_fct, pacb_data,
223 timestamp, nonce, subkey);
224 if (enctypes != NULL)
231 krb5int_process_tgs_reply(krb5_context context,
232 struct krb5int_fast_request_state *fast_state,
233 krb5_data *response_data,
235 krb5_flags kdcoptions,
236 krb5_address *const *address,
237 krb5_pa_data **in_padata,
239 krb5_timestamp timestamp,
241 krb5_keyblock *subkey,
242 krb5_pa_data ***out_padata,
243 krb5_pa_data ***out_enc_padata,
244 krb5_creds **out_cred)
246 krb5_error_code retval;
247 krb5_kdc_rep *dec_rep = NULL;
248 krb5_error *err_reply = NULL;
249 krb5_boolean s4u2self;
251 s4u2self = krb5int_find_pa_data(context, in_padata,
252 KRB5_PADATA_S4U_X509_USER) ||
253 krb5int_find_pa_data(context, in_padata,
254 KRB5_PADATA_FOR_USER);
256 if (krb5_is_krb_error(response_data)) {
257 retval = decode_krb5_error(response_data, &err_reply);
260 retval = krb5int_fast_process_error(context, fast_state,
261 &err_reply, NULL, NULL);
264 retval = (krb5_error_code) err_reply->error + ERROR_TABLE_BASE_krb5;
265 if (err_reply->text.length > 0) {
266 switch (err_reply->error) {
267 case KRB_ERR_GENERIC:
268 krb5_set_error_message(context, retval,
269 _("KDC returned error string: %.*s"),
270 err_reply->text.length,
271 err_reply->text.data);
273 case KDC_ERR_S_PRINCIPAL_UNKNOWN:
276 if (err_reply->server &&
277 krb5_unparse_name(context, err_reply->server, &s_name) == 0) {
278 krb5_set_error_message(context, retval,
279 _("Server %s not found in Kerberos "
280 "database"), s_name);
281 krb5_free_unparsed_name(context, s_name);
283 /* In case there's a stale S_PRINCIPAL_UNKNOWN
284 report already noted. */
285 krb5_clear_error_message(context);
290 krb5_free_error(context, err_reply);
292 } else if (!krb5_is_tgs_rep(response_data)) {
293 retval = KRB5KRB_AP_ERR_MSG_TYPE;
297 /* Unfortunately, Heimdal at least up through 1.2 encrypts using
298 the session key not the subsession key. So we try both. */
299 retval = krb5int_decode_tgs_rep(context, fast_state, response_data, subkey,
300 KRB5_KEYUSAGE_TGS_REP_ENCPART_SUBKEY,
303 TRACE_TGS_REPLY_DECODE_SESSION(context, &tkt->keyblock);
304 if ((krb5int_decode_tgs_rep(context, fast_state, response_data,
306 KRB5_KEYUSAGE_TGS_REP_ENCPART_SESSKEY, &dec_rep)) == 0)
312 if (dec_rep->msg_type != KRB5_TGS_REP) {
313 retval = KRB5KRB_AP_ERR_MSG_TYPE;
318 * Don't trust the ok-as-delegate flag from foreign KDCs unless the
319 * cross-realm TGT also had the ok-as-delegate flag set.
321 if (!tgt_is_local_realm(tkt)
322 && !(tkt->ticket_flags & TKT_FLG_OK_AS_DELEGATE))
323 dec_rep->enc_part2->flags &= ~TKT_FLG_OK_AS_DELEGATE;
325 /* make sure the response hasn't been tampered with..... */
328 if (s4u2self && !IS_TGS_PRINC(context, dec_rep->ticket->server)) {
329 /* Final hop, check whether KDC supports S4U2Self */
330 if (krb5_principal_compare(context, dec_rep->client, in_cred->server))
331 retval = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
332 } else if ((kdcoptions & KDC_OPT_CNAME_IN_ADDL_TKT) == 0) {
333 /* XXX for constrained delegation this check must be performed by caller
334 * as we don't have access to the key to decrypt the evidence ticket.
336 if (!krb5_principal_compare(context, dec_rep->client, tkt->client))
337 retval = KRB5_KDCREP_MODIFIED;
341 retval = check_reply_server(context, kdcoptions, in_cred, dec_rep);
343 if (dec_rep->enc_part2->nonce != nonce)
344 retval = KRB5_KDCREP_MODIFIED;
346 if ((kdcoptions & KDC_OPT_POSTDATED) &&
347 (in_cred->times.starttime != 0) &&
348 (in_cred->times.starttime != dec_rep->enc_part2->times.starttime))
349 retval = KRB5_KDCREP_MODIFIED;
351 if ((in_cred->times.endtime != 0) &&
352 (dec_rep->enc_part2->times.endtime > in_cred->times.endtime))
353 retval = KRB5_KDCREP_MODIFIED;
355 if ((kdcoptions & KDC_OPT_RENEWABLE) &&
356 (in_cred->times.renew_till != 0) &&
357 (dec_rep->enc_part2->times.renew_till > in_cred->times.renew_till))
358 retval = KRB5_KDCREP_MODIFIED;
360 if ((kdcoptions & KDC_OPT_RENEWABLE_OK) &&
361 (dec_rep->enc_part2->flags & KDC_OPT_RENEWABLE) &&
362 (in_cred->times.endtime != 0) &&
363 (dec_rep->enc_part2->times.renew_till > in_cred->times.endtime))
364 retval = KRB5_KDCREP_MODIFIED;
369 if (!in_cred->times.starttime &&
370 !in_clock_skew(dec_rep->enc_part2->times.starttime,
372 retval = KRB5_KDCREP_SKEW;
376 if (out_padata != NULL) {
377 *out_padata = dec_rep->padata;
378 dec_rep->padata = NULL;
380 if (out_enc_padata != NULL) {
381 *out_enc_padata = dec_rep->enc_part2->enc_padata;
382 dec_rep->enc_part2->enc_padata = NULL;
385 retval = kdcrep2creds(context, dec_rep, address,
386 &in_cred->second_ticket, out_cred);
391 if (dec_rep != NULL) {
392 memset(dec_rep->enc_part2->session->contents, 0,
393 dec_rep->enc_part2->session->length);
394 krb5_free_kdc_rep(context, dec_rep);
401 krb5_get_cred_via_tkt_ext(krb5_context context, krb5_creds *tkt,
402 krb5_flags kdcoptions, krb5_address *const *address,
403 krb5_pa_data **in_padata,
405 krb5_error_code (*pacb_fct)(krb5_context,
410 krb5_pa_data ***out_padata,
411 krb5_pa_data ***out_enc_padata,
412 krb5_creds **out_cred,
413 krb5_keyblock **out_subkey)
415 krb5_error_code retval;
416 krb5_data request_data;
417 krb5_data response_data;
418 krb5_timestamp timestamp;
420 krb5_keyblock *subkey = NULL;
421 int tcp_only = 0, use_master = 0;
422 struct krb5int_fast_request_state *fast_state = NULL;
424 request_data.data = NULL;
425 request_data.length = 0;
426 response_data.data = NULL;
427 response_data.length = 0;
429 retval = krb5int_fast_make_state(context, &fast_state);
433 TRACE_GET_CRED_VIA_TKT_EXT(context, in_cred->server, tkt->server,
436 retval = krb5int_make_tgs_request(context, fast_state, tkt, kdcoptions,
437 address, in_padata, in_cred,
439 &request_data, ×tamp, &nonce,
446 retval = krb5_sendto_kdc(context, &request_data,
447 krb5_princ_realm(context, in_cred->server),
448 &response_data, &use_master, tcp_only);
450 if (krb5_is_krb_error(&response_data)) {
452 krb5_error *err_reply;
453 retval = decode_krb5_error(&response_data, &err_reply);
456 retval = krb5int_fast_process_error(context, fast_state,
457 &err_reply, NULL, NULL);
460 if (err_reply->error == KRB_ERR_RESPONSE_TOO_BIG) {
462 krb5_free_error(context, err_reply);
463 krb5_free_data_contents(context, &response_data);
466 krb5_free_error(context, err_reply);
472 retval = krb5int_process_tgs_reply(context, fast_state, &response_data,
473 tkt, kdcoptions, address,
475 timestamp, nonce, subkey,
477 out_enc_padata, out_cred);
482 krb5int_fast_free_state(context, fast_state);
483 TRACE_GET_CRED_VIA_TKT_EXT_RETURN(context, retval);
485 krb5_free_data_contents(context, &request_data);
486 krb5_free_data_contents(context, &response_data);
488 if (subkey != NULL) {
489 if (retval == 0 && out_subkey != NULL)
490 *out_subkey = subkey;
492 krb5_free_keyblock(context, subkey);