2 * lib/krb5/krb/gc_via_tgt.c
4 * Copyright 1990,1991,2007,2008 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.
27 * Given a tkt, and a target cred, get it.
28 * Assumes that the kdc_rep has been decrypted.
32 #include "int-proto.h"
34 static krb5_error_code
35 krb5_kdcrep2creds(krb5_context context, krb5_kdc_rep *pkdcrep, krb5_address *const *address, krb5_data *psectkt, krb5_creds **ppcreds)
37 krb5_error_code retval;
40 if ((*ppcreds = (krb5_creds *)calloc(1,sizeof(krb5_creds))) == NULL) {
44 if ((retval = krb5_copy_principal(context, pkdcrep->client,
45 &(*ppcreds)->client)))
48 if ((retval = krb5_copy_principal(context, pkdcrep->enc_part2->server,
49 &(*ppcreds)->server)))
52 if ((retval = krb5_copy_keyblock_contents(context,
53 pkdcrep->enc_part2->session,
54 &(*ppcreds)->keyblock)))
57 if ((retval = krb5_copy_data(context, psectkt, &pdata)))
59 (*ppcreds)->second_ticket = *pdata;
62 (*ppcreds)->ticket_flags = pkdcrep->enc_part2->flags;
63 (*ppcreds)->times = pkdcrep->enc_part2->times;
64 (*ppcreds)->magic = KV5M_CREDS;
66 (*ppcreds)->authdata = NULL; /* not used */
67 (*ppcreds)->is_skey = psectkt->length != 0;
69 if (pkdcrep->enc_part2->caddrs) {
70 if ((retval = krb5_copy_addresses(context, pkdcrep->enc_part2->caddrs,
71 &(*ppcreds)->addresses)))
72 goto cleanup_keyblock;
74 /* no addresses in the list means we got what we had */
75 if ((retval = krb5_copy_addresses(context, address,
76 &(*ppcreds)->addresses)))
77 goto cleanup_keyblock;
80 if ((retval = encode_krb5_ticket(pkdcrep->ticket, &pdata)))
81 goto cleanup_keyblock;
83 (*ppcreds)->ticket = *pdata;
88 krb5_free_keyblock(context, &(*ppcreds)->keyblock);
96 static krb5_error_code
97 check_reply_server(krb5_context context, krb5_flags kdcoptions,
98 krb5_creds *in_cred, krb5_kdc_rep *dec_rep)
101 if (!krb5_principal_compare(context, dec_rep->ticket->server,
102 dec_rep->enc_part2->server))
103 return KRB5_KDCREP_MODIFIED;
105 /* Reply is self-consistent. */
107 if (krb5_principal_compare(context, dec_rep->ticket->server,
111 /* Server in reply differs from what we requested. */
113 if (kdcoptions & KDC_OPT_CANONICALIZE) {
114 /* in_cred server differs from ticket returned, but ticket
115 returned is consistent and we requested canonicalization. */
117 #ifdef DEBUG_REFERRALS
118 printf("gc_via_tkt: in_cred and encoding don't match but referrals requested\n");
119 krb5int_dbgref_dump_principal("gc_via_tkt: in_cred",in_cred->server);
120 krb5int_dbgref_dump_principal("gc_via_tkt: encoded server",dec_rep->enc_part2->server);
126 /* We didn't request canonicalization. */
128 if (!IS_TGS_PRINC(context, in_cred->server) ||
129 !IS_TGS_PRINC(context, dec_rep->ticket->server)) {
130 /* Canonicalization not requested, and not a TGS referral. */
131 return KRB5_KDCREP_MODIFIED;
135 * Is this check needed? find_nxt_kdc() in gc_frm_kdc.c already
136 * effectively checks this.
138 if (krb5_realm_compare(context, in_cred->client, in_cred->server) &&
139 data_eq(*in_cred->server->data[1], *in_cred->client->realm) {
140 /* Attempted to rewrite local TGS. */
141 return KRB5_KDCREP_MODIFIED;
148 krb5_get_cred_via_tkt (krb5_context context, krb5_creds *tkt,
149 krb5_flags kdcoptions, krb5_address *const *address,
150 krb5_creds *in_cred, krb5_creds **out_cred)
152 krb5_error_code retval;
153 krb5_kdc_rep *dec_rep;
154 krb5_error *err_reply;
155 krb5_response tgsrep;
156 krb5_enctype *enctypes = 0;
158 #ifdef DEBUG_REFERRALS
159 printf("krb5_get_cred_via_tkt starting; referral flag is %s\n", kdcoptions&KDC_OPT_CANONICALIZE?"on":"off");
160 krb5int_dbgref_dump_principal("krb5_get_cred_via_tkt requested ticket", in_cred->server);
161 krb5int_dbgref_dump_principal("krb5_get_cred_via_tkt TGT in use", tkt->server);
164 /* tkt->client must be equal to in_cred->client */
165 if (!krb5_principal_compare(context, tkt->client, in_cred->client))
166 return KRB5_PRINC_NOMATCH;
168 if (!tkt->ticket.length)
169 return KRB5_NO_TKT_SUPPLIED;
171 if ((kdcoptions & KDC_OPT_ENC_TKT_IN_SKEY) &&
172 (!in_cred->second_ticket.length))
173 return(KRB5_NO_2ND_TKT);
176 /* check if we have the right TGT */
177 /* tkt->server must be equal to */
178 /* krbtgt/realmof(cred->server)@realmof(tgt->server) */
181 krb5_principal tempprinc;
182 if (retval = krb5_tgtname(context,
183 krb5_princ_realm(context, in_cred->server),
184 krb5_princ_realm(context, tkt->server), &tempprinc))
187 if (!krb5_principal_compare(context, tempprinc, tkt->server)) {
188 krb5_free_principal(context, tempprinc);
189 return (KRB5_PRINC_NOMATCH);
191 krb5_free_principal(context, tempprinc);
195 if (in_cred->keyblock.enctype) {
196 enctypes = (krb5_enctype *) malloc(sizeof(krb5_enctype)*2);
199 enctypes[0] = in_cred->keyblock.enctype;
203 retval = krb5_send_tgs(context, kdcoptions, &in_cred->times, enctypes,
204 in_cred->server, address, in_cred->authdata,
206 (kdcoptions & KDC_OPT_ENC_TKT_IN_SKEY) ?
207 &in_cred->second_ticket : NULL,
212 #ifdef DEBUG_REFERRALS
213 printf("krb5_get_cred_via_tkt ending early after send_tgs with: %s\n",
214 error_message(retval));
219 switch (tgsrep.message_type) {
224 if (krb5_is_krb_error(&tgsrep.response))
225 retval = decode_krb5_error(&tgsrep.response, &err_reply);
227 retval = KRB5KRB_AP_ERR_MSG_TYPE;
229 if (retval) /* neither proper reply nor error! */
232 retval = (krb5_error_code) err_reply->error + ERROR_TABLE_BASE_krb5;
233 if (err_reply->text.length > 0) {
237 switch (err_reply->error) {
238 case KRB_ERR_GENERIC:
239 krb5_set_error_message(context, retval,
240 "KDC returned error string: %.*s",
241 err_reply->text.length,
242 err_reply->text.data);
244 case KDC_ERR_S_PRINCIPAL_UNKNOWN:
247 if (krb5_unparse_name(context, in_cred->server, &s_name) == 0) {
248 krb5_set_error_message(context, retval,
249 "Server %s not found in Kerberos database",
251 krb5_free_unparsed_name(context, s_name);
253 /* In case there's a stale S_PRINCIPAL_UNKNOWN
254 report already noted. */
255 krb5_clear_error_message(context);
259 #if 0 /* We should stop the KDC from sending back this text, because
260 if the local language doesn't match the KDC's language, we'd
261 just wind up printing out the error message in two languages.
262 Well, when we get some localization. Which is already
264 m = error_message(retval);
265 /* Special case: MIT KDC may return this same string
266 in the e-text field. */
267 if (strlen (m) == err_reply->text.length-1
268 && !strcmp(m, err_reply->text.data))
270 krb5_set_error_message(context, retval,
271 "%s (KDC supplied additional data: %s)",
272 m, err_reply->text.data);
278 krb5_free_error(context, err_reply);
282 if ((retval = krb5_decode_kdc_rep(context, &tgsrep.response,
283 &tkt->keyblock, &dec_rep)))
286 if (dec_rep->msg_type != KRB5_TGS_REP) {
287 retval = KRB5KRB_AP_ERR_MSG_TYPE;
291 /* make sure the response hasn't been tampered with..... */
294 if (!krb5_principal_compare(context, dec_rep->client, tkt->client))
295 retval = KRB5_KDCREP_MODIFIED;
298 retval = check_reply_server(context, kdcoptions, in_cred, dec_rep);
300 if (dec_rep->enc_part2->nonce != tgsrep.expected_nonce)
301 retval = KRB5_KDCREP_MODIFIED;
303 if ((kdcoptions & KDC_OPT_POSTDATED) &&
304 (in_cred->times.starttime != 0) &&
305 (in_cred->times.starttime != dec_rep->enc_part2->times.starttime))
306 retval = KRB5_KDCREP_MODIFIED;
308 if ((in_cred->times.endtime != 0) &&
309 (dec_rep->enc_part2->times.endtime > in_cred->times.endtime))
310 retval = KRB5_KDCREP_MODIFIED;
312 if ((kdcoptions & KDC_OPT_RENEWABLE) &&
313 (in_cred->times.renew_till != 0) &&
314 (dec_rep->enc_part2->times.renew_till > in_cred->times.renew_till))
315 retval = KRB5_KDCREP_MODIFIED;
317 if ((kdcoptions & KDC_OPT_RENEWABLE_OK) &&
318 (dec_rep->enc_part2->flags & KDC_OPT_RENEWABLE) &&
319 (in_cred->times.endtime != 0) &&
320 (dec_rep->enc_part2->times.renew_till > in_cred->times.endtime))
321 retval = KRB5_KDCREP_MODIFIED;
326 if (!in_cred->times.starttime &&
327 !in_clock_skew(dec_rep->enc_part2->times.starttime,
328 tgsrep.request_time)) {
329 retval = KRB5_KDCREP_SKEW;
333 retval = krb5_kdcrep2creds(context, dec_rep, address,
334 &in_cred->second_ticket, out_cred);
337 memset(dec_rep->enc_part2->session->contents, 0,
338 dec_rep->enc_part2->session->length);
339 krb5_free_kdc_rep(context, dec_rep);
342 free(tgsrep.response.data);
343 #ifdef DEBUG_REFERRALS
344 printf("krb5_get_cred_via_tkt ending; %s\n", retval?error_message(retval):"no error");