1 /* 2 * lib/krb5/krb/gc_via_tgt.c 3 * 4 * Copyright 1990,1991 by the Massachusetts Institute of Technology. 5 * All Rights Reserved. 6 * 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. 11 * 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. 25 * 26 * 27 * Given a tkt, and a target cred, get it. 28 * Assumes that the kdc_rep has been decrypted. 29 */ 30 31 #include "k5-int.h" 32 #include "int-proto.h" 33 34 #define in_clock_skew(date, now) (labs((date)-(now)) < context->clockskew) 35 36 #define IS_TGS_PRINC(c, p) \ 37 ((krb5_princ_size((c), (p)) == 2) && \ 38 (krb5_princ_component((c), (p), 0)->length == \ 39 KRB5_TGS_NAME_SIZE) && \ 40 (!memcmp(krb5_princ_component((c), (p), 0)->data, \ 41 KRB5_TGS_NAME, KRB5_TGS_NAME_SIZE))) 42 43 static krb5_error_code 44 krb5_kdcrep2creds(krb5_context context, krb5_kdc_rep *pkdcrep, krb5_address *const *address, krb5_data *psectkt, krb5_creds **ppcreds) 45 { 46 krb5_error_code retval; 47 krb5_data *pdata; 48 49 if ((*ppcreds = (krb5_creds *)malloc(sizeof(krb5_creds))) == NULL) { 50 return ENOMEM; 51 } 52 53 memset(*ppcreds, 0, sizeof(krb5_creds)); 54 55 if ((retval = krb5_copy_principal(context, pkdcrep->client, 56 &(*ppcreds)->client))) 57 goto cleanup; 58 59 if ((retval = krb5_copy_principal(context, pkdcrep->enc_part2->server, 60 &(*ppcreds)->server))) 61 goto cleanup; 62 63 if ((retval = krb5_copy_keyblock_contents(context, 64 pkdcrep->enc_part2->session, 65 &(*ppcreds)->keyblock))) 66 goto cleanup; 67 68 if ((retval = krb5_copy_data(context, psectkt, &pdata))) 69 goto cleanup; 70 (*ppcreds)->second_ticket = *pdata; 71 krb5_xfree(pdata); 72 73 (*ppcreds)->ticket_flags = pkdcrep->enc_part2->flags; 74 (*ppcreds)->times = pkdcrep->enc_part2->times; 75 (*ppcreds)->magic = KV5M_CREDS; 76 77 (*ppcreds)->authdata = NULL; /* not used */ 78 (*ppcreds)->is_skey = psectkt->length != 0; 79 80 if (pkdcrep->enc_part2->caddrs) { 81 if ((retval = krb5_copy_addresses(context, pkdcrep->enc_part2->caddrs, 82 &(*ppcreds)->addresses))) 83 goto cleanup_keyblock; 84 } else { 85 /* no addresses in the list means we got what we had */ 86 if ((retval = krb5_copy_addresses(context, address, 87 &(*ppcreds)->addresses))) 88 goto cleanup_keyblock; 89 } 90 91 if ((retval = encode_krb5_ticket(pkdcrep->ticket, &pdata))) 92 goto cleanup_keyblock; 93 94 (*ppcreds)->ticket = *pdata; 95 free(pdata); 96 return 0; 97 98 cleanup_keyblock: 99 krb5_free_keyblock(context, &(*ppcreds)->keyblock); 100 101 cleanup: 102 free (*ppcreds); 103 return retval; 104 } 105 106 static krb5_error_code 107 check_reply_server(krb5_context context, krb5_flags kdcoptions, 108 krb5_creds *in_cred, krb5_kdc_rep *dec_rep) 109 { 110 111 if (!krb5_principal_compare(context, dec_rep->ticket->server, 112 dec_rep->enc_part2->server)) 113 return KRB5_KDCREP_MODIFIED; 114 115 /* Reply is self-consistent. */ 116 117 if (krb5_principal_compare(context, dec_rep->ticket->server, 118 in_cred->server)) 119 return 0; 120 121 /* Server in reply differs from what we requested. */ 122 123 if (kdcoptions & KDC_OPT_CANONICALIZE) { 124 /* in_cred server differs from ticket returned, but ticket 125 returned is consistent and we requested canonicalization. */ 126 #if 0 127 #ifdef DEBUG_REFERRALS 128 printf("gc_via_tkt: in_cred and encoding don't match but referrals requested\n"); 129 krb5int_dbgref_dump_principal("gc_via_tkt: in_cred",in_cred->server); 130 krb5int_dbgref_dump_principal("gc_via_tkt: encoded server",dec_rep->enc_part2->server); 131 #endif 132 #endif 133 return 0; 134 } 135 136 /* We didn't request canonicalization. */ 137 138 if (!IS_TGS_PRINC(context, in_cred->server) || 139 !IS_TGS_PRINC(context, dec_rep->ticket->server)) { 140 /* Canonicalization not requested, and not a TGS referral. */ 141 return KRB5_KDCREP_MODIFIED; 142 } 143 #if 0 144 /* 145 * Is this check needed? find_nxt_kdc() in gc_frm_kdc.c already 146 * effectively checks this. 147 */ 148 if (krb5_realm_compare(context, in_cred->client, in_cred->server) && 149 in_cred->server->data[1].length == in_cred->client->realm.length && 150 !memcmp(in_cred->client->realm.data, in_cred->server->data[1].data, 151 in_cred->client->realm.length)) { 152 /* Attempted to rewrite local TGS. */ 153 return KRB5_KDCREP_MODIFIED; 154 } 155 #endif 156 return 0; 157 } 158 159 krb5_error_code 160 krb5_get_cred_via_tkt (krb5_context context, krb5_creds *tkt, 161 krb5_flags kdcoptions, krb5_address *const *address, 162 krb5_creds *in_cred, krb5_creds **out_cred) 163 { 164 krb5_error_code retval; 165 krb5_kdc_rep *dec_rep; 166 krb5_error *err_reply; 167 krb5_response tgsrep; 168 krb5_enctype *enctypes = 0; 169 170 #ifdef DEBUG_REFERRALS 171 printf("krb5_get_cred_via_tkt starting; referral flag is %s\n", kdcoptions&KDC_OPT_CANONICALIZE?"on":"off"); 172 krb5int_dbgref_dump_principal("krb5_get_cred_via_tkt requested ticket", in_cred->server); 173 krb5int_dbgref_dump_principal("krb5_get_cred_via_tkt TGT in use", tkt->server); 174 #endif 175 176 /* tkt->client must be equal to in_cred->client */ 177 if (!krb5_principal_compare(context, tkt->client, in_cred->client)) 178 return KRB5_PRINC_NOMATCH; 179 180 if (!tkt->ticket.length) 181 return KRB5_NO_TKT_SUPPLIED; 182 183 if ((kdcoptions & KDC_OPT_ENC_TKT_IN_SKEY) && 184 (!in_cred->second_ticket.length)) 185 return(KRB5_NO_2ND_TKT); 186 187 188 /* check if we have the right TGT */ 189 /* tkt->server must be equal to */ 190 /* krbtgt/realmof(cred->server)@realmof(tgt->server) */ 191 /* 192 { 193 krb5_principal tempprinc; 194 if (retval = krb5_tgtname(context, 195 krb5_princ_realm(context, in_cred->server), 196 krb5_princ_realm(context, tkt->server), &tempprinc)) 197 return(retval); 198 199 if (!krb5_principal_compare(context, tempprinc, tkt->server)) { 200 krb5_free_principal(context, tempprinc); 201 return (KRB5_PRINC_NOMATCH); 202 } 203 krb5_free_principal(context, tempprinc); 204 } 205 */ 206 207 if (in_cred->keyblock.enctype) { 208 enctypes = (krb5_enctype *) malloc(sizeof(krb5_enctype)*2); 209 if (!enctypes) 210 return ENOMEM; 211 enctypes[0] = in_cred->keyblock.enctype; 212 enctypes[1] = 0; 213 } 214 215 retval = krb5_send_tgs(context, kdcoptions, &in_cred->times, enctypes, 216 in_cred->server, address, in_cred->authdata, 217 0, /* no padata */ 218 (kdcoptions & KDC_OPT_ENC_TKT_IN_SKEY) ? 219 &in_cred->second_ticket : NULL, 220 tkt, &tgsrep); 221 if (enctypes) 222 free(enctypes); 223 if (retval) { 224 #ifdef DEBUG_REFERRALS 225 printf("krb5_get_cred_via_tkt ending early after send_tgs with: %s\n", 226 error_message(retval)); 227 #endif 228 return retval; 229 } 230 231 switch (tgsrep.message_type) { 232 case KRB5_TGS_REP: 233 break; 234 case KRB5_ERROR: 235 default: 236 if (krb5_is_krb_error(&tgsrep.response)) 237 retval = decode_krb5_error(&tgsrep.response, &err_reply); 238 else 239 retval = KRB5KRB_AP_ERR_MSG_TYPE; 240 241 if (retval) /* neither proper reply nor error! */ 242 goto error_4; 243 244 retval = (krb5_error_code) err_reply->error + ERROR_TABLE_BASE_krb5; 245 if (err_reply->text.length > 0) { 246 #if 0 247 const char *m; 248 #endif 249 switch (err_reply->error) { 250 case KRB_ERR_GENERIC: 251 krb5_set_error_message(context, retval, 252 "KDC returned error string: %s", 253 err_reply->text.data); 254 break; 255 default: 256 #if 0 /* We should stop the KDC from sending back this text, because 257 if the local language doesn't match the KDC's language, we'd 258 just wind up printing out the error message in two languages. 259 Well, when we get some localization. Which is already 260 happening in KfM. */ 261 m = error_message(retval); 262 /* Special case: MIT KDC may return this same string 263 in the e-text field. */ 264 if (strlen (m) == err_reply->text.length-1 265 && !strcmp(m, err_reply->text.data)) 266 break; 267 krb5_set_error_message(context, retval, 268 "%s (KDC supplied additional data: %s)", 269 m, err_reply->text.data); 270 #endif 271 break; 272 } 273 } 274 275 krb5_free_error(context, err_reply); 276 goto error_4; 277 } 278 279 if ((retval = krb5_decode_kdc_rep(context, &tgsrep.response, 280 &tkt->keyblock, &dec_rep))) 281 goto error_4; 282 283 if (dec_rep->msg_type != KRB5_TGS_REP) { 284 retval = KRB5KRB_AP_ERR_MSG_TYPE; 285 goto error_3; 286 } 287 288 /* make sure the response hasn't been tampered with..... */ 289 retval = 0; 290 291 if (!krb5_principal_compare(context, dec_rep->client, tkt->client)) 292 retval = KRB5_KDCREP_MODIFIED; 293 294 if (retval == 0) 295 retval = check_reply_server(context, kdcoptions, in_cred, dec_rep); 296 297 if (dec_rep->enc_part2->nonce != tgsrep.expected_nonce) 298 retval = KRB5_KDCREP_MODIFIED; 299 300 if ((kdcoptions & KDC_OPT_POSTDATED) && 301 (in_cred->times.starttime != 0) && 302 (in_cred->times.starttime != dec_rep->enc_part2->times.starttime)) 303 retval = KRB5_KDCREP_MODIFIED; 304 305 if ((in_cred->times.endtime != 0) && 306 (dec_rep->enc_part2->times.endtime > in_cred->times.endtime)) 307 retval = KRB5_KDCREP_MODIFIED; 308 309 if ((kdcoptions & KDC_OPT_RENEWABLE) && 310 (in_cred->times.renew_till != 0) && 311 (dec_rep->enc_part2->times.renew_till > in_cred->times.renew_till)) 312 retval = KRB5_KDCREP_MODIFIED; 313 314 if ((kdcoptions & KDC_OPT_RENEWABLE_OK) && 315 (dec_rep->enc_part2->flags & KDC_OPT_RENEWABLE) && 316 (in_cred->times.endtime != 0) && 317 (dec_rep->enc_part2->times.renew_till > in_cred->times.endtime)) 318 retval = KRB5_KDCREP_MODIFIED; 319 320 if (retval != 0) 321 goto error_3; 322 323 if (!in_cred->times.starttime && 324 !in_clock_skew(dec_rep->enc_part2->times.starttime, 325 tgsrep.request_time)) { 326 retval = KRB5_KDCREP_SKEW; 327 goto error_3; 328 } 329 330 retval = krb5_kdcrep2creds(context, dec_rep, address, 331 &in_cred->second_ticket, out_cred); 332 333 error_3:; 334 memset(dec_rep->enc_part2->session->contents, 0, 335 dec_rep->enc_part2->session->length); 336 krb5_free_kdc_rep(context, dec_rep); 337 338 error_4:; 339 free(tgsrep.response.data); 340 #ifdef DEBUG_REFERRALS 341 printf("krb5_get_cred_via_tkt ending; %s\n", retval?error_message(retval):"no error"); 342 #endif 343 return retval; 344 } 345