155682Smarkm/* 2233294Sstas * Copyright (c) 1997 - 2001 Kungliga Tekniska H��gskolan 3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden). 4233294Sstas * All rights reserved. 555682Smarkm * 6233294Sstas * Portions Copyright (c) 2009 Apple Inc. All rights reserved. 755682Smarkm * 8233294Sstas * Redistribution and use in source and binary forms, with or without 9233294Sstas * modification, are permitted provided that the following conditions 10233294Sstas * are met: 1155682Smarkm * 12233294Sstas * 1. Redistributions of source code must retain the above copyright 13233294Sstas * notice, this list of conditions and the following disclaimer. 1455682Smarkm * 15233294Sstas * 2. Redistributions in binary form must reproduce the above copyright 16233294Sstas * notice, this list of conditions and the following disclaimer in the 17233294Sstas * documentation and/or other materials provided with the distribution. 1855682Smarkm * 19233294Sstas * 3. Neither the name of the Institute nor the names of its contributors 20233294Sstas * may be used to endorse or promote products derived from this software 21233294Sstas * without specific prior written permission. 22233294Sstas * 23233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26233294Sstas * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33233294Sstas * SUCH DAMAGE. 3455682Smarkm */ 3555682Smarkm 3655682Smarkm#include "krb5_locl.h" 3755682Smarkm 38233294Sstas/** 39233294Sstas * Free ticket and content 40233294Sstas * 41233294Sstas * @param context a Kerberos 5 context 42233294Sstas * @param ticket ticket to free 43233294Sstas * 44233294Sstas * @return Returns 0 to indicate success. Otherwise an kerberos et 45233294Sstas * error code is returned, see krb5_get_error_message(). 46233294Sstas * 47233294Sstas * @ingroup krb5 48233294Sstas */ 4955682Smarkm 50233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 5155682Smarkmkrb5_free_ticket(krb5_context context, 5255682Smarkm krb5_ticket *ticket) 5355682Smarkm{ 5455682Smarkm free_EncTicketPart(&ticket->ticket); 5555682Smarkm krb5_free_principal(context, ticket->client); 5655682Smarkm krb5_free_principal(context, ticket->server); 57178825Sdfr free(ticket); 5855682Smarkm return 0; 5955682Smarkm} 6055682Smarkm 61233294Sstas/** 62233294Sstas * Copy ticket and content 63233294Sstas * 64233294Sstas * @param context a Kerberos 5 context 65233294Sstas * @param from ticket to copy 66233294Sstas * @param to new copy of ticket, free with krb5_free_ticket() 67233294Sstas * 68233294Sstas * @return Returns 0 to indicate success. Otherwise an kerberos et 69233294Sstas * error code is returned, see krb5_get_error_message(). 70233294Sstas * 71233294Sstas * @ingroup krb5 72233294Sstas */ 73233294Sstas 74233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 7555682Smarkmkrb5_copy_ticket(krb5_context context, 7655682Smarkm const krb5_ticket *from, 7755682Smarkm krb5_ticket **to) 7855682Smarkm{ 7955682Smarkm krb5_error_code ret; 80127808Snectar krb5_ticket *tmp; 81127808Snectar 82127808Snectar *to = NULL; 83127808Snectar tmp = malloc(sizeof(*tmp)); 8478527Sassar if(tmp == NULL) { 85233294Sstas krb5_set_error_message(context, ENOMEM, 86233294Sstas N_("malloc: out of memory", "")); 8755682Smarkm return ENOMEM; 8878527Sassar } 8955682Smarkm if((ret = copy_EncTicketPart(&from->ticket, &tmp->ticket))){ 9055682Smarkm free(tmp); 9155682Smarkm return ret; 9255682Smarkm } 9355682Smarkm ret = krb5_copy_principal(context, from->client, &tmp->client); 9455682Smarkm if(ret){ 9555682Smarkm free_EncTicketPart(&tmp->ticket); 96127808Snectar free(tmp); 9755682Smarkm return ret; 9855682Smarkm } 99127808Snectar ret = krb5_copy_principal(context, from->server, &tmp->server); 10055682Smarkm if(ret){ 10155682Smarkm krb5_free_principal(context, tmp->client); 10255682Smarkm free_EncTicketPart(&tmp->ticket); 103127808Snectar free(tmp); 10455682Smarkm return ret; 10555682Smarkm } 10655682Smarkm *to = tmp; 10755682Smarkm return 0; 10855682Smarkm} 109178825Sdfr 110233294Sstas/** 111233294Sstas * Return client principal in ticket 112233294Sstas * 113233294Sstas * @param context a Kerberos 5 context 114233294Sstas * @param ticket ticket to copy 115233294Sstas * @param client client principal, free with krb5_free_principal() 116233294Sstas * 117233294Sstas * @return Returns 0 to indicate success. Otherwise an kerberos et 118233294Sstas * error code is returned, see krb5_get_error_message(). 119233294Sstas * 120233294Sstas * @ingroup krb5 121233294Sstas */ 122233294Sstas 123233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 124178825Sdfrkrb5_ticket_get_client(krb5_context context, 125178825Sdfr const krb5_ticket *ticket, 126178825Sdfr krb5_principal *client) 127178825Sdfr{ 128178825Sdfr return krb5_copy_principal(context, ticket->client, client); 129178825Sdfr} 130178825Sdfr 131233294Sstas/** 132233294Sstas * Return server principal in ticket 133233294Sstas * 134233294Sstas * @param context a Kerberos 5 context 135233294Sstas * @param ticket ticket to copy 136233294Sstas * @param server server principal, free with krb5_free_principal() 137233294Sstas * 138233294Sstas * @return Returns 0 to indicate success. Otherwise an kerberos et 139233294Sstas * error code is returned, see krb5_get_error_message(). 140233294Sstas * 141233294Sstas * @ingroup krb5 142233294Sstas */ 143233294Sstas 144233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 145178825Sdfrkrb5_ticket_get_server(krb5_context context, 146178825Sdfr const krb5_ticket *ticket, 147178825Sdfr krb5_principal *server) 148178825Sdfr{ 149178825Sdfr return krb5_copy_principal(context, ticket->server, server); 150178825Sdfr} 151178825Sdfr 152233294Sstas/** 153233294Sstas * Return end time of ticket 154233294Sstas * 155233294Sstas * @param context a Kerberos 5 context 156233294Sstas * @param ticket ticket to copy 157233294Sstas * 158233294Sstas * @return end time of ticket 159233294Sstas * 160233294Sstas * @ingroup krb5 161233294Sstas */ 162233294Sstas 163233294SstasKRB5_LIB_FUNCTION time_t KRB5_LIB_CALL 164178825Sdfrkrb5_ticket_get_endtime(krb5_context context, 165178825Sdfr const krb5_ticket *ticket) 166178825Sdfr{ 167178825Sdfr return ticket->ticket.endtime; 168178825Sdfr} 169178825Sdfr 170233294Sstas/** 171233294Sstas * Get the flags from the Kerberos ticket 172233294Sstas * 173233294Sstas * @param context Kerberos context 174233294Sstas * @param ticket Kerberos ticket 175233294Sstas * 176233294Sstas * @return ticket flags 177233294Sstas * 178233294Sstas * @ingroup krb5_ticket 179233294Sstas */ 180233294SstasKRB5_LIB_FUNCTION unsigned long KRB5_LIB_CALL 181233294Sstaskrb5_ticket_get_flags(krb5_context context, 182233294Sstas const krb5_ticket *ticket) 183233294Sstas{ 184233294Sstas return TicketFlags2int(ticket->ticket.flags); 185233294Sstas} 186233294Sstas 187178825Sdfrstatic int 188178825Sdfrfind_type_in_ad(krb5_context context, 189233294Sstas int type, 190178825Sdfr krb5_data *data, 191178825Sdfr krb5_boolean *found, 192178825Sdfr krb5_boolean failp, 193178825Sdfr krb5_keyblock *sessionkey, 194178825Sdfr const AuthorizationData *ad, 195178825Sdfr int level) 196178825Sdfr{ 197178825Sdfr krb5_error_code ret = 0; 198233294Sstas size_t i; 199178825Sdfr 200178825Sdfr if (level > 9) { 201178825Sdfr ret = ENOENT; /* XXX */ 202233294Sstas krb5_set_error_message(context, ret, 203233294Sstas N_("Authorization data nested deeper " 204233294Sstas "then %d levels, stop searching", ""), 205233294Sstas level); 206178825Sdfr goto out; 207178825Sdfr } 208178825Sdfr 209178825Sdfr /* 210178825Sdfr * Only copy out the element the first time we get to it, we need 211178825Sdfr * to run over the whole authorization data fields to check if 212178825Sdfr * there are any container clases we need to care about. 213178825Sdfr */ 214178825Sdfr for (i = 0; i < ad->len; i++) { 215178825Sdfr if (!*found && ad->val[i].ad_type == type) { 216178825Sdfr ret = der_copy_octet_string(&ad->val[i].ad_data, data); 217178825Sdfr if (ret) { 218233294Sstas krb5_set_error_message(context, ret, 219233294Sstas N_("malloc: out of memory", "")); 220178825Sdfr goto out; 221178825Sdfr } 222178825Sdfr *found = TRUE; 223178825Sdfr continue; 224178825Sdfr } 225178825Sdfr switch (ad->val[i].ad_type) { 226178825Sdfr case KRB5_AUTHDATA_IF_RELEVANT: { 227178825Sdfr AuthorizationData child; 228178825Sdfr ret = decode_AuthorizationData(ad->val[i].ad_data.data, 229178825Sdfr ad->val[i].ad_data.length, 230178825Sdfr &child, 231178825Sdfr NULL); 232178825Sdfr if (ret) { 233233294Sstas krb5_set_error_message(context, ret, 234233294Sstas N_("Failed to decode " 235233294Sstas "IF_RELEVANT with %d", ""), 236233294Sstas (int)ret); 237178825Sdfr goto out; 238178825Sdfr } 239178825Sdfr ret = find_type_in_ad(context, type, data, found, FALSE, 240178825Sdfr sessionkey, &child, level + 1); 241178825Sdfr free_AuthorizationData(&child); 242178825Sdfr if (ret) 243178825Sdfr goto out; 244178825Sdfr break; 245178825Sdfr } 246178825Sdfr#if 0 /* XXX test */ 247178825Sdfr case KRB5_AUTHDATA_KDC_ISSUED: { 248178825Sdfr AD_KDCIssued child; 249178825Sdfr 250178825Sdfr ret = decode_AD_KDCIssued(ad->val[i].ad_data.data, 251178825Sdfr ad->val[i].ad_data.length, 252178825Sdfr &child, 253178825Sdfr NULL); 254178825Sdfr if (ret) { 255233294Sstas krb5_set_error_message(context, ret, 256233294Sstas N_("Failed to decode " 257233294Sstas "AD_KDCIssued with %d", ""), 258233294Sstas ret); 259178825Sdfr goto out; 260178825Sdfr } 261178825Sdfr if (failp) { 262178825Sdfr krb5_boolean valid; 263178825Sdfr krb5_data buf; 264178825Sdfr size_t len; 265178825Sdfr 266233294Sstas ASN1_MALLOC_ENCODE(AuthorizationData, buf.data, buf.length, 267178825Sdfr &child.elements, &len, ret); 268178825Sdfr if (ret) { 269178825Sdfr free_AD_KDCIssued(&child); 270233294Sstas krb5_clear_error_message(context); 271178825Sdfr goto out; 272178825Sdfr } 273178825Sdfr if(buf.length != len) 274178825Sdfr krb5_abortx(context, "internal error in ASN.1 encoder"); 275178825Sdfr 276178825Sdfr ret = krb5_c_verify_checksum(context, sessionkey, 19, &buf, 277178825Sdfr &child.ad_checksum, &valid); 278178825Sdfr krb5_data_free(&buf); 279178825Sdfr if (ret) { 280178825Sdfr free_AD_KDCIssued(&child); 281178825Sdfr goto out; 282178825Sdfr } 283178825Sdfr if (!valid) { 284233294Sstas krb5_clear_error_message(context); 285178825Sdfr ret = ENOENT; 286178825Sdfr free_AD_KDCIssued(&child); 287178825Sdfr goto out; 288178825Sdfr } 289178825Sdfr } 290178825Sdfr ret = find_type_in_ad(context, type, data, found, failp, sessionkey, 291178825Sdfr &child.elements, level + 1); 292178825Sdfr free_AD_KDCIssued(&child); 293178825Sdfr if (ret) 294178825Sdfr goto out; 295178825Sdfr break; 296178825Sdfr } 297178825Sdfr#endif 298178825Sdfr case KRB5_AUTHDATA_AND_OR: 299178825Sdfr if (!failp) 300178825Sdfr break; 301178825Sdfr ret = ENOENT; /* XXX */ 302233294Sstas krb5_set_error_message(context, ret, 303233294Sstas N_("Authorization data contains " 304233294Sstas "AND-OR element that is unknown to the " 305233294Sstas "application", "")); 306178825Sdfr goto out; 307178825Sdfr default: 308178825Sdfr if (!failp) 309178825Sdfr break; 310178825Sdfr ret = ENOENT; /* XXX */ 311233294Sstas krb5_set_error_message(context, ret, 312233294Sstas N_("Authorization data contains " 313233294Sstas "unknown type (%d) ", ""), 314233294Sstas ad->val[i].ad_type); 315178825Sdfr goto out; 316178825Sdfr } 317178825Sdfr } 318178825Sdfrout: 319178825Sdfr if (ret) { 320178825Sdfr if (*found) { 321178825Sdfr krb5_data_free(data); 322178825Sdfr *found = 0; 323178825Sdfr } 324178825Sdfr } 325178825Sdfr return ret; 326178825Sdfr} 327178825Sdfr 328233294Sstas/** 329233294Sstas * Extract the authorization data type of type from the ticket. Store 330233294Sstas * the field in data. This function is to use for kerberos 331233294Sstas * applications. 332233294Sstas * 333233294Sstas * @param context a Kerberos 5 context 334233294Sstas * @param ticket Kerberos ticket 335233294Sstas * @param type type to fetch 336233294Sstas * @param data returned data, free with krb5_data_free() 337233294Sstas * 338233294Sstas * @ingroup krb5 339178825Sdfr */ 340178825Sdfr 341233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 342178825Sdfrkrb5_ticket_get_authorization_data_type(krb5_context context, 343178825Sdfr krb5_ticket *ticket, 344178825Sdfr int type, 345178825Sdfr krb5_data *data) 346178825Sdfr{ 347178825Sdfr AuthorizationData *ad; 348178825Sdfr krb5_error_code ret; 349178825Sdfr krb5_boolean found = FALSE; 350178825Sdfr 351178825Sdfr krb5_data_zero(data); 352178825Sdfr 353178825Sdfr ad = ticket->ticket.authorization_data; 354178825Sdfr if (ticket->ticket.authorization_data == NULL) { 355233294Sstas krb5_set_error_message(context, ENOENT, 356233294Sstas N_("Ticket have not authorization data", "")); 357178825Sdfr return ENOENT; /* XXX */ 358178825Sdfr } 359178825Sdfr 360178825Sdfr ret = find_type_in_ad(context, type, data, &found, TRUE, 361178825Sdfr &ticket->ticket.key, ad, 0); 362178825Sdfr if (ret) 363178825Sdfr return ret; 364178825Sdfr if (!found) { 365233294Sstas krb5_set_error_message(context, ENOENT, 366233294Sstas N_("Ticket have not " 367233294Sstas "authorization data of type %d", ""), 368233294Sstas type); 369178825Sdfr return ENOENT; /* XXX */ 370178825Sdfr } 371178825Sdfr return 0; 372178825Sdfr} 373233294Sstas 374233294Sstasstatic krb5_error_code 375233294Sstascheck_server_referral(krb5_context context, 376233294Sstas krb5_kdc_rep *rep, 377233294Sstas unsigned flags, 378233294Sstas krb5_const_principal requested, 379233294Sstas krb5_const_principal returned, 380233294Sstas krb5_keyblock * key) 381233294Sstas{ 382233294Sstas krb5_error_code ret; 383233294Sstas PA_ServerReferralData ref; 384233294Sstas krb5_crypto session; 385233294Sstas EncryptedData ed; 386233294Sstas size_t len; 387233294Sstas krb5_data data; 388233294Sstas PA_DATA *pa; 389233294Sstas int i = 0, cmp; 390233294Sstas 391233294Sstas if (rep->kdc_rep.padata == NULL) 392233294Sstas goto noreferral; 393233294Sstas 394233294Sstas pa = krb5_find_padata(rep->kdc_rep.padata->val, 395233294Sstas rep->kdc_rep.padata->len, 396233294Sstas KRB5_PADATA_SERVER_REFERRAL, &i); 397233294Sstas if (pa == NULL) 398233294Sstas goto noreferral; 399233294Sstas 400233294Sstas memset(&ed, 0, sizeof(ed)); 401233294Sstas memset(&ref, 0, sizeof(ref)); 402233294Sstas 403233294Sstas ret = decode_EncryptedData(pa->padata_value.data, 404233294Sstas pa->padata_value.length, 405233294Sstas &ed, &len); 406233294Sstas if (ret) 407233294Sstas return ret; 408233294Sstas if (len != pa->padata_value.length) { 409233294Sstas free_EncryptedData(&ed); 410233294Sstas krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, 411233294Sstas N_("Referral EncryptedData wrong for realm %s", 412233294Sstas "realm"), requested->realm); 413233294Sstas return KRB5KRB_AP_ERR_MODIFIED; 414233294Sstas } 415233294Sstas 416233294Sstas ret = krb5_crypto_init(context, key, 0, &session); 417233294Sstas if (ret) { 418233294Sstas free_EncryptedData(&ed); 419233294Sstas return ret; 420233294Sstas } 421233294Sstas 422233294Sstas ret = krb5_decrypt_EncryptedData(context, session, 423233294Sstas KRB5_KU_PA_SERVER_REFERRAL, 424233294Sstas &ed, &data); 425233294Sstas free_EncryptedData(&ed); 426233294Sstas krb5_crypto_destroy(context, session); 427233294Sstas if (ret) 428233294Sstas return ret; 429233294Sstas 430233294Sstas ret = decode_PA_ServerReferralData(data.data, data.length, &ref, &len); 431233294Sstas if (ret) { 432233294Sstas krb5_data_free(&data); 433233294Sstas return ret; 434233294Sstas } 435233294Sstas krb5_data_free(&data); 436233294Sstas 437233294Sstas if (strcmp(requested->realm, returned->realm) != 0) { 438233294Sstas free_PA_ServerReferralData(&ref); 439233294Sstas krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, 440233294Sstas N_("server ref realm mismatch, " 441233294Sstas "requested realm %s got back %s", ""), 442233294Sstas requested->realm, returned->realm); 443233294Sstas return KRB5KRB_AP_ERR_MODIFIED; 444233294Sstas } 445233294Sstas 446233294Sstas if (krb5_principal_is_krbtgt(context, returned)) { 447233294Sstas const char *realm = returned->name.name_string.val[1]; 448233294Sstas 449233294Sstas if (ref.referred_realm == NULL 450233294Sstas || strcmp(*ref.referred_realm, realm) != 0) 451233294Sstas { 452233294Sstas free_PA_ServerReferralData(&ref); 453233294Sstas krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, 454233294Sstas N_("tgt returned with wrong ref", "")); 455233294Sstas return KRB5KRB_AP_ERR_MODIFIED; 456233294Sstas } 457233294Sstas } else if (krb5_principal_compare(context, returned, requested) == 0) { 458233294Sstas free_PA_ServerReferralData(&ref); 459233294Sstas krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, 460233294Sstas N_("req princ no same as returned", "")); 461233294Sstas return KRB5KRB_AP_ERR_MODIFIED; 462233294Sstas } 463233294Sstas 464233294Sstas if (ref.requested_principal_name) { 465233294Sstas cmp = _krb5_principal_compare_PrincipalName(context, 466233294Sstas requested, 467233294Sstas ref.requested_principal_name); 468233294Sstas if (!cmp) { 469233294Sstas free_PA_ServerReferralData(&ref); 470233294Sstas krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, 471233294Sstas N_("referred principal not same " 472233294Sstas "as requested", "")); 473233294Sstas return KRB5KRB_AP_ERR_MODIFIED; 474233294Sstas } 475233294Sstas } else if (flags & EXTRACT_TICKET_AS_REQ) { 476233294Sstas free_PA_ServerReferralData(&ref); 477233294Sstas krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, 478233294Sstas N_("Requested principal missing on AS-REQ", "")); 479233294Sstas return KRB5KRB_AP_ERR_MODIFIED; 480233294Sstas } 481233294Sstas 482233294Sstas free_PA_ServerReferralData(&ref); 483233294Sstas 484233294Sstas return ret; 485233294Sstasnoreferral: 486233294Sstas /* 487233294Sstas * Expect excact match or that we got a krbtgt 488233294Sstas */ 489233294Sstas if (krb5_principal_compare(context, requested, returned) != TRUE && 490233294Sstas (krb5_realm_compare(context, requested, returned) != TRUE && 491233294Sstas krb5_principal_is_krbtgt(context, returned) != TRUE)) 492233294Sstas { 493233294Sstas krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, 494233294Sstas N_("Not same server principal returned " 495233294Sstas "as requested", "")); 496233294Sstas return KRB5KRB_AP_ERR_MODIFIED; 497233294Sstas } 498233294Sstas return 0; 499233294Sstas} 500233294Sstas 501233294Sstas 502233294Sstas/* 503233294Sstas * Verify referral data 504233294Sstas */ 505233294Sstas 506233294Sstas 507233294Sstasstatic krb5_error_code 508233294Sstascheck_client_referral(krb5_context context, 509233294Sstas krb5_kdc_rep *rep, 510233294Sstas krb5_const_principal requested, 511233294Sstas krb5_const_principal mapped, 512233294Sstas krb5_keyblock const * key) 513233294Sstas{ 514233294Sstas krb5_error_code ret; 515233294Sstas PA_ClientCanonicalized canon; 516233294Sstas krb5_crypto crypto; 517233294Sstas krb5_data data; 518233294Sstas PA_DATA *pa; 519233294Sstas size_t len; 520233294Sstas int i = 0; 521233294Sstas 522233294Sstas if (rep->kdc_rep.padata == NULL) 523233294Sstas goto noreferral; 524233294Sstas 525233294Sstas pa = krb5_find_padata(rep->kdc_rep.padata->val, 526233294Sstas rep->kdc_rep.padata->len, 527233294Sstas KRB5_PADATA_CLIENT_CANONICALIZED, &i); 528233294Sstas if (pa == NULL) 529233294Sstas goto noreferral; 530233294Sstas 531233294Sstas ret = decode_PA_ClientCanonicalized(pa->padata_value.data, 532233294Sstas pa->padata_value.length, 533233294Sstas &canon, &len); 534233294Sstas if (ret) { 535233294Sstas krb5_set_error_message(context, ret, 536233294Sstas N_("Failed to decode ClientCanonicalized " 537233294Sstas "from realm %s", ""), requested->realm); 538233294Sstas return ret; 539233294Sstas } 540233294Sstas 541233294Sstas ASN1_MALLOC_ENCODE(PA_ClientCanonicalizedNames, data.data, data.length, 542233294Sstas &canon.names, &len, ret); 543233294Sstas if (ret) { 544233294Sstas free_PA_ClientCanonicalized(&canon); 545233294Sstas return ret; 546233294Sstas } 547233294Sstas if (data.length != len) 548233294Sstas krb5_abortx(context, "internal asn.1 error"); 549233294Sstas 550233294Sstas ret = krb5_crypto_init(context, key, 0, &crypto); 551233294Sstas if (ret) { 552233294Sstas free(data.data); 553233294Sstas free_PA_ClientCanonicalized(&canon); 554233294Sstas return ret; 555233294Sstas } 556233294Sstas 557233294Sstas ret = krb5_verify_checksum(context, crypto, KRB5_KU_CANONICALIZED_NAMES, 558233294Sstas data.data, data.length, 559233294Sstas &canon.canon_checksum); 560233294Sstas krb5_crypto_destroy(context, crypto); 561233294Sstas free(data.data); 562233294Sstas if (ret) { 563233294Sstas krb5_set_error_message(context, ret, 564233294Sstas N_("Failed to verify client canonicalized " 565233294Sstas "data from realm %s", ""), 566233294Sstas requested->realm); 567233294Sstas free_PA_ClientCanonicalized(&canon); 568233294Sstas return ret; 569233294Sstas } 570233294Sstas 571233294Sstas if (!_krb5_principal_compare_PrincipalName(context, 572233294Sstas requested, 573233294Sstas &canon.names.requested_name)) 574233294Sstas { 575233294Sstas free_PA_ClientCanonicalized(&canon); 576233294Sstas krb5_set_error_message(context, KRB5_PRINC_NOMATCH, 577233294Sstas N_("Requested name doesn't match" 578233294Sstas " in client referral", "")); 579233294Sstas return KRB5_PRINC_NOMATCH; 580233294Sstas } 581233294Sstas if (!_krb5_principal_compare_PrincipalName(context, 582233294Sstas mapped, 583233294Sstas &canon.names.mapped_name)) 584233294Sstas { 585233294Sstas free_PA_ClientCanonicalized(&canon); 586233294Sstas krb5_set_error_message(context, KRB5_PRINC_NOMATCH, 587233294Sstas N_("Mapped name doesn't match" 588233294Sstas " in client referral", "")); 589233294Sstas return KRB5_PRINC_NOMATCH; 590233294Sstas } 591233294Sstas 592233294Sstas return 0; 593233294Sstas 594233294Sstasnoreferral: 595233294Sstas if (krb5_principal_compare(context, requested, mapped) == FALSE) { 596233294Sstas krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, 597233294Sstas N_("Not same client principal returned " 598233294Sstas "as requested", "")); 599233294Sstas return KRB5KRB_AP_ERR_MODIFIED; 600233294Sstas } 601233294Sstas return 0; 602233294Sstas} 603233294Sstas 604233294Sstas 605233294Sstasstatic krb5_error_code KRB5_CALLCONV 606233294Sstasdecrypt_tkt (krb5_context context, 607233294Sstas krb5_keyblock *key, 608233294Sstas krb5_key_usage usage, 609233294Sstas krb5_const_pointer decrypt_arg, 610233294Sstas krb5_kdc_rep *dec_rep) 611233294Sstas{ 612233294Sstas krb5_error_code ret; 613233294Sstas krb5_data data; 614233294Sstas size_t size; 615233294Sstas krb5_crypto crypto; 616233294Sstas 617233294Sstas ret = krb5_crypto_init(context, key, 0, &crypto); 618233294Sstas if (ret) 619233294Sstas return ret; 620233294Sstas 621233294Sstas ret = krb5_decrypt_EncryptedData (context, 622233294Sstas crypto, 623233294Sstas usage, 624233294Sstas &dec_rep->kdc_rep.enc_part, 625233294Sstas &data); 626233294Sstas krb5_crypto_destroy(context, crypto); 627233294Sstas 628233294Sstas if (ret) 629233294Sstas return ret; 630233294Sstas 631233294Sstas ret = decode_EncASRepPart(data.data, 632233294Sstas data.length, 633233294Sstas &dec_rep->enc_part, 634233294Sstas &size); 635233294Sstas if (ret) 636233294Sstas ret = decode_EncTGSRepPart(data.data, 637233294Sstas data.length, 638233294Sstas &dec_rep->enc_part, 639233294Sstas &size); 640233294Sstas krb5_data_free (&data); 641233294Sstas if (ret) { 642233294Sstas krb5_set_error_message(context, ret, 643233294Sstas N_("Failed to decode encpart in ticket", "")); 644233294Sstas return ret; 645233294Sstas } 646233294Sstas return 0; 647233294Sstas} 648233294Sstas 649233294Sstasint 650233294Sstas_krb5_extract_ticket(krb5_context context, 651233294Sstas krb5_kdc_rep *rep, 652233294Sstas krb5_creds *creds, 653233294Sstas krb5_keyblock *key, 654233294Sstas krb5_const_pointer keyseed, 655233294Sstas krb5_key_usage key_usage, 656233294Sstas krb5_addresses *addrs, 657233294Sstas unsigned nonce, 658233294Sstas unsigned flags, 659233294Sstas krb5_decrypt_proc decrypt_proc, 660233294Sstas krb5_const_pointer decryptarg) 661233294Sstas{ 662233294Sstas krb5_error_code ret; 663233294Sstas krb5_principal tmp_principal; 664233294Sstas size_t len = 0; 665233294Sstas time_t tmp_time; 666233294Sstas krb5_timestamp sec_now; 667233294Sstas 668233294Sstas /* decrypt */ 669233294Sstas 670233294Sstas if (decrypt_proc == NULL) 671233294Sstas decrypt_proc = decrypt_tkt; 672233294Sstas 673233294Sstas ret = (*decrypt_proc)(context, key, key_usage, decryptarg, rep); 674233294Sstas if (ret) 675233294Sstas goto out; 676233294Sstas 677233294Sstas /* save session key */ 678233294Sstas 679233294Sstas creds->session.keyvalue.length = 0; 680233294Sstas creds->session.keyvalue.data = NULL; 681233294Sstas creds->session.keytype = rep->enc_part.key.keytype; 682233294Sstas ret = krb5_data_copy (&creds->session.keyvalue, 683233294Sstas rep->enc_part.key.keyvalue.data, 684233294Sstas rep->enc_part.key.keyvalue.length); 685233294Sstas if (ret) { 686233294Sstas krb5_clear_error_message(context); 687233294Sstas goto out; 688233294Sstas } 689233294Sstas 690233294Sstas /* compare client and save */ 691233294Sstas ret = _krb5_principalname2krb5_principal (context, 692233294Sstas &tmp_principal, 693233294Sstas rep->kdc_rep.cname, 694233294Sstas rep->kdc_rep.crealm); 695233294Sstas if (ret) 696233294Sstas goto out; 697233294Sstas 698233294Sstas /* check client referral and save principal */ 699233294Sstas /* anonymous here ? */ 700233294Sstas if((flags & EXTRACT_TICKET_ALLOW_CNAME_MISMATCH) == 0) { 701233294Sstas ret = check_client_referral(context, rep, 702233294Sstas creds->client, 703233294Sstas tmp_principal, 704233294Sstas &creds->session); 705233294Sstas if (ret) { 706233294Sstas krb5_free_principal (context, tmp_principal); 707233294Sstas goto out; 708233294Sstas } 709233294Sstas } 710233294Sstas krb5_free_principal (context, creds->client); 711233294Sstas creds->client = tmp_principal; 712233294Sstas 713233294Sstas /* check server referral and save principal */ 714233294Sstas ret = _krb5_principalname2krb5_principal (context, 715233294Sstas &tmp_principal, 716320907Sdelphij rep->enc_part.sname, 717320907Sdelphij rep->enc_part.srealm); 718233294Sstas if (ret) 719233294Sstas goto out; 720233294Sstas if((flags & EXTRACT_TICKET_ALLOW_SERVER_MISMATCH) == 0){ 721233294Sstas ret = check_server_referral(context, 722233294Sstas rep, 723233294Sstas flags, 724233294Sstas creds->server, 725233294Sstas tmp_principal, 726233294Sstas &creds->session); 727233294Sstas if (ret) { 728233294Sstas krb5_free_principal (context, tmp_principal); 729233294Sstas goto out; 730233294Sstas } 731233294Sstas } 732233294Sstas krb5_free_principal(context, creds->server); 733233294Sstas creds->server = tmp_principal; 734233294Sstas 735233294Sstas /* verify names */ 736233294Sstas if(flags & EXTRACT_TICKET_MATCH_REALM){ 737233294Sstas const char *srealm = krb5_principal_get_realm(context, creds->server); 738233294Sstas const char *crealm = krb5_principal_get_realm(context, creds->client); 739233294Sstas 740233294Sstas if (strcmp(rep->enc_part.srealm, srealm) != 0 || 741233294Sstas strcmp(rep->enc_part.srealm, crealm) != 0) 742233294Sstas { 743233294Sstas ret = KRB5KRB_AP_ERR_MODIFIED; 744233294Sstas krb5_clear_error_message(context); 745233294Sstas goto out; 746233294Sstas } 747233294Sstas } 748233294Sstas 749233294Sstas /* compare nonces */ 750233294Sstas 751233294Sstas if (nonce != (unsigned)rep->enc_part.nonce) { 752233294Sstas ret = KRB5KRB_AP_ERR_MODIFIED; 753233294Sstas krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); 754233294Sstas goto out; 755233294Sstas } 756233294Sstas 757233294Sstas /* set kdc-offset */ 758233294Sstas 759233294Sstas krb5_timeofday (context, &sec_now); 760233294Sstas if (rep->enc_part.flags.initial 761233294Sstas && (flags & EXTRACT_TICKET_TIMESYNC) 762233294Sstas && context->kdc_sec_offset == 0 763233294Sstas && krb5_config_get_bool (context, NULL, 764233294Sstas "libdefaults", 765233294Sstas "kdc_timesync", 766233294Sstas NULL)) { 767233294Sstas context->kdc_sec_offset = rep->enc_part.authtime - sec_now; 768233294Sstas krb5_timeofday (context, &sec_now); 769233294Sstas } 770233294Sstas 771233294Sstas /* check all times */ 772233294Sstas 773233294Sstas if (rep->enc_part.starttime) { 774233294Sstas tmp_time = *rep->enc_part.starttime; 775233294Sstas } else 776233294Sstas tmp_time = rep->enc_part.authtime; 777233294Sstas 778233294Sstas if (creds->times.starttime == 0 779233294Sstas && abs(tmp_time - sec_now) > context->max_skew) { 780233294Sstas ret = KRB5KRB_AP_ERR_SKEW; 781233294Sstas krb5_set_error_message (context, ret, 782233294Sstas N_("time skew (%d) larger than max (%d)", ""), 783233294Sstas abs(tmp_time - sec_now), 784233294Sstas (int)context->max_skew); 785233294Sstas goto out; 786233294Sstas } 787233294Sstas 788233294Sstas if (creds->times.starttime != 0 789233294Sstas && tmp_time != creds->times.starttime) { 790233294Sstas krb5_clear_error_message (context); 791233294Sstas ret = KRB5KRB_AP_ERR_MODIFIED; 792233294Sstas goto out; 793233294Sstas } 794233294Sstas 795233294Sstas creds->times.starttime = tmp_time; 796233294Sstas 797233294Sstas if (rep->enc_part.renew_till) { 798233294Sstas tmp_time = *rep->enc_part.renew_till; 799233294Sstas } else 800233294Sstas tmp_time = 0; 801233294Sstas 802233294Sstas if (creds->times.renew_till != 0 803233294Sstas && tmp_time > creds->times.renew_till) { 804233294Sstas krb5_clear_error_message (context); 805233294Sstas ret = KRB5KRB_AP_ERR_MODIFIED; 806233294Sstas goto out; 807233294Sstas } 808233294Sstas 809233294Sstas creds->times.renew_till = tmp_time; 810233294Sstas 811233294Sstas creds->times.authtime = rep->enc_part.authtime; 812233294Sstas 813233294Sstas if (creds->times.endtime != 0 814233294Sstas && rep->enc_part.endtime > creds->times.endtime) { 815233294Sstas krb5_clear_error_message (context); 816233294Sstas ret = KRB5KRB_AP_ERR_MODIFIED; 817233294Sstas goto out; 818233294Sstas } 819233294Sstas 820233294Sstas creds->times.endtime = rep->enc_part.endtime; 821233294Sstas 822233294Sstas if(rep->enc_part.caddr) 823233294Sstas krb5_copy_addresses (context, rep->enc_part.caddr, &creds->addresses); 824233294Sstas else if(addrs) 825233294Sstas krb5_copy_addresses (context, addrs, &creds->addresses); 826233294Sstas else { 827233294Sstas creds->addresses.len = 0; 828233294Sstas creds->addresses.val = NULL; 829233294Sstas } 830233294Sstas creds->flags.b = rep->enc_part.flags; 831233294Sstas 832233294Sstas creds->authdata.len = 0; 833233294Sstas creds->authdata.val = NULL; 834233294Sstas 835233294Sstas /* extract ticket */ 836233294Sstas ASN1_MALLOC_ENCODE(Ticket, creds->ticket.data, creds->ticket.length, 837233294Sstas &rep->kdc_rep.ticket, &len, ret); 838233294Sstas if(ret) 839233294Sstas goto out; 840233294Sstas if (creds->ticket.length != len) 841233294Sstas krb5_abortx(context, "internal error in ASN.1 encoder"); 842233294Sstas creds->second_ticket.length = 0; 843233294Sstas creds->second_ticket.data = NULL; 844233294Sstas 845233294Sstas 846233294Sstasout: 847233294Sstas memset (rep->enc_part.key.keyvalue.data, 0, 848233294Sstas rep->enc_part.key.keyvalue.length); 849233294Sstas return ret; 850233294Sstas} 851