155682Smarkm/* 2233294Sstas * Copyright (c) 1997 - 2008 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 36233294Sstas#include "krb5_locl.h" 37233294Sstas#include <assert.h> 3855682Smarkm 39233294Sstasstatic krb5_error_code 40233294Sstasget_cred_kdc_capath(krb5_context, krb5_kdc_flags, 41233294Sstas krb5_ccache, krb5_creds *, krb5_principal, 42233294Sstas Ticket *, krb5_creds **, krb5_creds ***); 4355682Smarkm 4455682Smarkm/* 4555682Smarkm * Take the `body' and encode it into `padata' using the credentials 4655682Smarkm * in `creds'. 4755682Smarkm */ 4855682Smarkm 4955682Smarkmstatic krb5_error_code 50233294Sstasmake_pa_tgs_req(krb5_context context, 5155682Smarkm krb5_auth_context ac, 5255682Smarkm KDC_REQ_BODY *body, 5355682Smarkm PA_DATA *padata, 54233294Sstas krb5_creds *creds) 5555682Smarkm{ 5655682Smarkm u_char *buf; 5755682Smarkm size_t buf_size; 58233294Sstas size_t len = 0; 5955682Smarkm krb5_data in_data; 6055682Smarkm krb5_error_code ret; 6155682Smarkm 62103423Snectar ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret); 63103423Snectar if (ret) 64103423Snectar goto out; 65103423Snectar if(buf_size != len) 66103423Snectar krb5_abortx(context, "internal error in ASN.1 encoder"); 6755682Smarkm 6855682Smarkm in_data.length = len; 69103423Snectar in_data.data = buf; 70178825Sdfr ret = _krb5_mk_req_internal(context, &ac, 0, &in_data, creds, 71178825Sdfr &padata->padata_value, 72178825Sdfr KRB5_KU_TGS_REQ_AUTH_CKSUM, 73233294Sstas KRB5_KU_TGS_REQ_AUTH); 74178825Sdfr out: 7555682Smarkm free (buf); 7655682Smarkm if(ret) 7755682Smarkm return ret; 7872445Sassar padata->padata_type = KRB5_PADATA_TGS_REQ; 7955682Smarkm return 0; 8055682Smarkm} 8155682Smarkm 8255682Smarkm/* 8355682Smarkm * Set the `enc-authorization-data' in `req_body' based on `authdata' 8455682Smarkm */ 8555682Smarkm 8655682Smarkmstatic krb5_error_code 8755682Smarkmset_auth_data (krb5_context context, 8855682Smarkm KDC_REQ_BODY *req_body, 8955682Smarkm krb5_authdata *authdata, 90233294Sstas krb5_keyblock *subkey) 9155682Smarkm{ 9255682Smarkm if(authdata->len) { 93233294Sstas size_t len = 0, buf_size; 9455682Smarkm unsigned char *buf; 9555682Smarkm krb5_crypto crypto; 9655682Smarkm krb5_error_code ret; 9755682Smarkm 98178825Sdfr ASN1_MALLOC_ENCODE(AuthorizationData, buf, buf_size, authdata, 99178825Sdfr &len, ret); 100103423Snectar if (ret) 10155682Smarkm return ret; 102178825Sdfr if (buf_size != len) 103178825Sdfr krb5_abortx(context, "internal error in ASN.1 encoder"); 10455682Smarkm 10555682Smarkm ALLOC(req_body->enc_authorization_data, 1); 10655682Smarkm if (req_body->enc_authorization_data == NULL) { 10755682Smarkm free (buf); 108233294Sstas krb5_set_error_message(context, ENOMEM, 109233294Sstas N_("malloc: out of memory", "")); 11078527Sassar return ENOMEM; 11155682Smarkm } 112233294Sstas ret = krb5_crypto_init(context, subkey, 0, &crypto); 11355682Smarkm if (ret) { 11455682Smarkm free (buf); 11555682Smarkm free (req_body->enc_authorization_data); 116178825Sdfr req_body->enc_authorization_data = NULL; 11755682Smarkm return ret; 11855682Smarkm } 119233294Sstas krb5_encrypt_EncryptedData(context, 12055682Smarkm crypto, 121233294Sstas KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY, 12255682Smarkm buf, 12355682Smarkm len, 12455682Smarkm 0, 12555682Smarkm req_body->enc_authorization_data); 12655682Smarkm free (buf); 12755682Smarkm krb5_crypto_destroy(context, crypto); 12855682Smarkm } else { 12955682Smarkm req_body->enc_authorization_data = NULL; 13055682Smarkm } 13155682Smarkm return 0; 132233294Sstas} 13355682Smarkm 13455682Smarkm/* 13555682Smarkm * Create a tgs-req in `t' with `addresses', `flags', `second_ticket' 13655682Smarkm * (if not-NULL), `in_creds', `krbtgt', and returning the generated 13755682Smarkm * subkey in `subkey'. 13855682Smarkm */ 13955682Smarkm 14055682Smarkmstatic krb5_error_code 14155682Smarkminit_tgs_req (krb5_context context, 14255682Smarkm krb5_ccache ccache, 14355682Smarkm krb5_addresses *addresses, 14455682Smarkm krb5_kdc_flags flags, 14555682Smarkm Ticket *second_ticket, 14655682Smarkm krb5_creds *in_creds, 14755682Smarkm krb5_creds *krbtgt, 14855682Smarkm unsigned nonce, 149178825Sdfr const METHOD_DATA *padata, 15055682Smarkm krb5_keyblock **subkey, 151233294Sstas TGS_REQ *t) 15255682Smarkm{ 153233294Sstas krb5_auth_context ac = NULL; 154103423Snectar krb5_error_code ret = 0; 15555682Smarkm 15655682Smarkm memset(t, 0, sizeof(*t)); 15755682Smarkm t->pvno = 5; 15855682Smarkm t->msg_type = krb_tgs_req; 15955682Smarkm if (in_creds->session.keytype) { 160103423Snectar ALLOC_SEQ(&t->req_body.etype, 1); 161103423Snectar if(t->req_body.etype.val == NULL) { 162103423Snectar ret = ENOMEM; 163233294Sstas krb5_set_error_message(context, ret, 164233294Sstas N_("malloc: out of memory", "")); 165103423Snectar goto fail; 166103423Snectar } 167103423Snectar t->req_body.etype.val[0] = in_creds->session.keytype; 16855682Smarkm } else { 169233294Sstas ret = _krb5_init_etype(context, 170233294Sstas KRB5_PDU_TGS_REQUEST, 171233294Sstas &t->req_body.etype.len, 172233294Sstas &t->req_body.etype.val, 173233294Sstas NULL); 17455682Smarkm } 17555682Smarkm if (ret) 17655682Smarkm goto fail; 17755682Smarkm t->req_body.addresses = addresses; 17855682Smarkm t->req_body.kdc_options = flags.b; 17955682Smarkm ret = copy_Realm(&in_creds->server->realm, &t->req_body.realm); 18055682Smarkm if (ret) 18155682Smarkm goto fail; 18255682Smarkm ALLOC(t->req_body.sname, 1); 18355682Smarkm if (t->req_body.sname == NULL) { 18455682Smarkm ret = ENOMEM; 185233294Sstas krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); 18655682Smarkm goto fail; 18755682Smarkm } 18872445Sassar 18972445Sassar /* some versions of some code might require that the client be 19072445Sassar present in TGS-REQs, but this is clearly against the spec */ 19172445Sassar 19255682Smarkm ret = copy_PrincipalName(&in_creds->server->name, t->req_body.sname); 19355682Smarkm if (ret) 19455682Smarkm goto fail; 19555682Smarkm 19655682Smarkm /* req_body.till should be NULL if there is no endtime specified, 19755682Smarkm but old MIT code (like DCE secd) doesn't like that */ 19855682Smarkm ALLOC(t->req_body.till, 1); 19955682Smarkm if(t->req_body.till == NULL){ 20055682Smarkm ret = ENOMEM; 201233294Sstas krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); 20255682Smarkm goto fail; 20355682Smarkm } 20455682Smarkm *t->req_body.till = in_creds->times.endtime; 205233294Sstas 20655682Smarkm t->req_body.nonce = nonce; 20755682Smarkm if(second_ticket){ 20855682Smarkm ALLOC(t->req_body.additional_tickets, 1); 20955682Smarkm if (t->req_body.additional_tickets == NULL) { 21055682Smarkm ret = ENOMEM; 211233294Sstas krb5_set_error_message(context, ret, 212233294Sstas N_("malloc: out of memory", "")); 21355682Smarkm goto fail; 21455682Smarkm } 21555682Smarkm ALLOC_SEQ(t->req_body.additional_tickets, 1); 21655682Smarkm if (t->req_body.additional_tickets->val == NULL) { 21755682Smarkm ret = ENOMEM; 218233294Sstas krb5_set_error_message(context, ret, 219233294Sstas N_("malloc: out of memory", "")); 22055682Smarkm goto fail; 22155682Smarkm } 222233294Sstas ret = copy_Ticket(second_ticket, t->req_body.additional_tickets->val); 22355682Smarkm if (ret) 22455682Smarkm goto fail; 22555682Smarkm } 22655682Smarkm ALLOC(t->padata, 1); 22755682Smarkm if (t->padata == NULL) { 22855682Smarkm ret = ENOMEM; 229233294Sstas krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); 23055682Smarkm goto fail; 23155682Smarkm } 232178825Sdfr ALLOC_SEQ(t->padata, 1 + padata->len); 23355682Smarkm if (t->padata->val == NULL) { 23455682Smarkm ret = ENOMEM; 235233294Sstas krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); 23655682Smarkm goto fail; 23755682Smarkm } 238178825Sdfr { 239233294Sstas size_t i; 240178825Sdfr for (i = 0; i < padata->len; i++) { 241178825Sdfr ret = copy_PA_DATA(&padata->val[i], &t->padata->val[i + 1]); 242178825Sdfr if (ret) { 243233294Sstas krb5_set_error_message(context, ret, 244233294Sstas N_("malloc: out of memory", "")); 245178825Sdfr goto fail; 246178825Sdfr } 247178825Sdfr } 248178825Sdfr } 24955682Smarkm 250233294Sstas ret = krb5_auth_con_init(context, &ac); 251233294Sstas if(ret) 252233294Sstas goto fail; 25355682Smarkm 254233294Sstas ret = krb5_auth_con_generatelocalsubkey(context, ac, &krbtgt->session); 255233294Sstas if (ret) 256233294Sstas goto fail; 257127808Snectar 258233294Sstas ret = set_auth_data (context, &t->req_body, &in_creds->authdata, 259233294Sstas ac->local_subkey); 260233294Sstas if (ret) 261233294Sstas goto fail; 262127808Snectar 263233294Sstas ret = make_pa_tgs_req(context, 264233294Sstas ac, 265233294Sstas &t->req_body, 266233294Sstas &t->padata->val[0], 267233294Sstas krbtgt); 268233294Sstas if(ret) 269233294Sstas goto fail; 27055682Smarkm 271233294Sstas ret = krb5_auth_con_getlocalsubkey(context, ac, subkey); 272233294Sstas if (ret) 273233294Sstas goto fail; 27455682Smarkm 275233294Sstasfail: 276233294Sstas if (ac) 27755682Smarkm krb5_auth_con_free(context, ac); 278127808Snectar if (ret) { 279127808Snectar t->req_body.addresses = NULL; 28055682Smarkm free_TGS_REQ (t); 281127808Snectar } 28255682Smarkm return ret; 28355682Smarkm} 28455682Smarkm 285127808Snectarkrb5_error_code 286127808Snectar_krb5_get_krbtgt(krb5_context context, 287127808Snectar krb5_ccache id, 288127808Snectar krb5_realm realm, 289127808Snectar krb5_creds **cred) 29055682Smarkm{ 29155682Smarkm krb5_error_code ret; 29255682Smarkm krb5_creds tmp_cred; 29355682Smarkm 29455682Smarkm memset(&tmp_cred, 0, sizeof(tmp_cred)); 29555682Smarkm 296127808Snectar ret = krb5_cc_get_principal(context, id, &tmp_cred.client); 297127808Snectar if (ret) 298127808Snectar return ret; 299127808Snectar 300233294Sstas ret = krb5_make_principal(context, 30155682Smarkm &tmp_cred.server, 30255682Smarkm realm, 30355682Smarkm KRB5_TGS_NAME, 30455682Smarkm realm, 30555682Smarkm NULL); 306127808Snectar if(ret) { 307127808Snectar krb5_free_principal(context, tmp_cred.client); 30855682Smarkm return ret; 309127808Snectar } 31055682Smarkm ret = krb5_get_credentials(context, 31155682Smarkm KRB5_GC_CACHED, 31255682Smarkm id, 31355682Smarkm &tmp_cred, 31455682Smarkm cred); 315127808Snectar krb5_free_principal(context, tmp_cred.client); 31655682Smarkm krb5_free_principal(context, tmp_cred.server); 31755682Smarkm if(ret) 31855682Smarkm return ret; 31955682Smarkm return 0; 32055682Smarkm} 32155682Smarkm 32255682Smarkm/* DCE compatible decrypt proc */ 323233294Sstasstatic krb5_error_code KRB5_CALLCONV 32455682Smarkmdecrypt_tkt_with_subkey (krb5_context context, 32555682Smarkm krb5_keyblock *key, 32655682Smarkm krb5_key_usage usage, 327233294Sstas krb5_const_pointer skey, 32855682Smarkm krb5_kdc_rep *dec_rep) 32955682Smarkm{ 330233294Sstas const krb5_keyblock *subkey = skey; 331233294Sstas krb5_error_code ret = 0; 33255682Smarkm krb5_data data; 33355682Smarkm size_t size; 33455682Smarkm krb5_crypto crypto; 335233294Sstas 336233294Sstas assert(usage == 0); 337233294Sstas 338233294Sstas krb5_data_zero(&data); 339233294Sstas 340233294Sstas /* 341233294Sstas * start out with trying with subkey if we have one 342233294Sstas */ 343233294Sstas if (subkey) { 344178825Sdfr ret = krb5_crypto_init(context, subkey, 0, &crypto); 34572445Sassar if (ret) 34672445Sassar return ret; 34755682Smarkm ret = krb5_decrypt_EncryptedData (context, 34855682Smarkm crypto, 34955682Smarkm KRB5_KU_TGS_REP_ENC_PART_SUB_KEY, 35055682Smarkm &dec_rep->kdc_rep.enc_part, 35155682Smarkm &data); 352233294Sstas /* 353233294Sstas * If the is Windows 2000 DC, we need to retry with key usage 354233294Sstas * 8 when doing ARCFOUR. 355233294Sstas */ 356233294Sstas if (ret && subkey->keytype == ETYPE_ARCFOUR_HMAC_MD5) { 357233294Sstas ret = krb5_decrypt_EncryptedData(context, 358233294Sstas crypto, 359233294Sstas 8, 360233294Sstas &dec_rep->kdc_rep.enc_part, 361233294Sstas &data); 362233294Sstas } 36355682Smarkm krb5_crypto_destroy(context, crypto); 36455682Smarkm } 365233294Sstas if (subkey == NULL || ret) { 366233294Sstas ret = krb5_crypto_init(context, key, 0, &crypto); 367233294Sstas if (ret) 368233294Sstas return ret; 369233294Sstas ret = krb5_decrypt_EncryptedData (context, 370233294Sstas crypto, 371233294Sstas KRB5_KU_TGS_REP_ENC_PART_SESSION, 372233294Sstas &dec_rep->kdc_rep.enc_part, 373233294Sstas &data); 374233294Sstas krb5_crypto_destroy(context, crypto); 375233294Sstas } 37655682Smarkm if (ret) 37755682Smarkm return ret; 378233294Sstas 379233294Sstas ret = decode_EncASRepPart(data.data, 380233294Sstas data.length, 381233294Sstas &dec_rep->enc_part, 382233294Sstas &size); 383233294Sstas if (ret) 384233294Sstas ret = decode_EncTGSRepPart(data.data, 38555682Smarkm data.length, 386233294Sstas &dec_rep->enc_part, 38755682Smarkm &size); 38855682Smarkm if (ret) 389233294Sstas krb5_set_error_message(context, ret, 390233294Sstas N_("Failed to decode encpart in ticket", "")); 39155682Smarkm krb5_data_free (&data); 39255682Smarkm return ret; 39355682Smarkm} 39455682Smarkm 39555682Smarkmstatic krb5_error_code 396233294Sstasget_cred_kdc(krb5_context context, 397233294Sstas krb5_ccache id, 398233294Sstas krb5_kdc_flags flags, 399233294Sstas krb5_addresses *addresses, 400233294Sstas krb5_creds *in_creds, 401233294Sstas krb5_creds *krbtgt, 402233294Sstas krb5_principal impersonate_principal, 403233294Sstas Ticket *second_ticket, 404233294Sstas krb5_creds *out_creds) 40555682Smarkm{ 40655682Smarkm TGS_REQ req; 40755682Smarkm krb5_data enc; 40855682Smarkm krb5_data resp; 40955682Smarkm krb5_kdc_rep rep; 41055682Smarkm KRB_ERROR error; 41155682Smarkm krb5_error_code ret; 41255682Smarkm unsigned nonce; 41355682Smarkm krb5_keyblock *subkey = NULL; 414233294Sstas size_t len = 0; 415178825Sdfr Ticket second_ticket_data; 416178825Sdfr METHOD_DATA padata; 417233294Sstas 418178825Sdfr krb5_data_zero(&resp); 419178825Sdfr krb5_data_zero(&enc); 420178825Sdfr padata.val = NULL; 421178825Sdfr padata.len = 0; 422178825Sdfr 42355682Smarkm krb5_generate_random_block(&nonce, sizeof(nonce)); 42455682Smarkm nonce &= 0xffffffff; 425233294Sstas 426178825Sdfr if(flags.b.enc_tkt_in_skey && second_ticket == NULL){ 427233294Sstas ret = decode_Ticket(in_creds->second_ticket.data, 428233294Sstas in_creds->second_ticket.length, 429178825Sdfr &second_ticket_data, &len); 43055682Smarkm if(ret) 43155682Smarkm return ret; 432178825Sdfr second_ticket = &second_ticket_data; 43355682Smarkm } 43455682Smarkm 435178825Sdfr 436178825Sdfr if (impersonate_principal) { 437178825Sdfr krb5_crypto crypto; 438178825Sdfr PA_S4U2Self self; 439178825Sdfr krb5_data data; 440178825Sdfr void *buf; 441233294Sstas size_t size = 0; 442178825Sdfr 443178825Sdfr self.name = impersonate_principal->name; 444178825Sdfr self.realm = impersonate_principal->realm; 445178825Sdfr self.auth = estrdup("Kerberos"); 446233294Sstas 447178825Sdfr ret = _krb5_s4u2self_to_checksumdata(context, &self, &data); 448178825Sdfr if (ret) { 449178825Sdfr free(self.auth); 450178825Sdfr goto out; 451178825Sdfr } 452178825Sdfr 453178825Sdfr ret = krb5_crypto_init(context, &krbtgt->session, 0, &crypto); 454178825Sdfr if (ret) { 455178825Sdfr free(self.auth); 456178825Sdfr krb5_data_free(&data); 457178825Sdfr goto out; 458178825Sdfr } 459178825Sdfr 460178825Sdfr ret = krb5_create_checksum(context, 461178825Sdfr crypto, 462178825Sdfr KRB5_KU_OTHER_CKSUM, 463178825Sdfr 0, 464178825Sdfr data.data, 465233294Sstas data.length, 466178825Sdfr &self.cksum); 467178825Sdfr krb5_crypto_destroy(context, crypto); 468178825Sdfr krb5_data_free(&data); 469178825Sdfr if (ret) { 470178825Sdfr free(self.auth); 471178825Sdfr goto out; 472178825Sdfr } 473178825Sdfr 474178825Sdfr ASN1_MALLOC_ENCODE(PA_S4U2Self, buf, len, &self, &size, ret); 475178825Sdfr free(self.auth); 476178825Sdfr free_Checksum(&self.cksum); 477178825Sdfr if (ret) 478178825Sdfr goto out; 479178825Sdfr if (len != size) 480178825Sdfr krb5_abortx(context, "internal asn1 error"); 481233294Sstas 482233294Sstas ret = krb5_padata_add(context, &padata, KRB5_PADATA_FOR_USER, buf, len); 483178825Sdfr if (ret) 484178825Sdfr goto out; 485178825Sdfr } 486178825Sdfr 48755682Smarkm ret = init_tgs_req (context, 48855682Smarkm id, 48955682Smarkm addresses, 49055682Smarkm flags, 491178825Sdfr second_ticket, 49255682Smarkm in_creds, 49355682Smarkm krbtgt, 49455682Smarkm nonce, 495178825Sdfr &padata, 496233294Sstas &subkey, 497233294Sstas &req); 49855682Smarkm if (ret) 49955682Smarkm goto out; 50055682Smarkm 501178825Sdfr ASN1_MALLOC_ENCODE(TGS_REQ, enc.data, enc.length, &req, &len, ret); 502233294Sstas if (ret) 50355682Smarkm goto out; 504178825Sdfr if(enc.length != len) 505103423Snectar krb5_abortx(context, "internal error in ASN.1 encoder"); 50655682Smarkm 50755682Smarkm /* don't free addresses */ 50855682Smarkm req.req_body.addresses = NULL; 50955682Smarkm free_TGS_REQ(&req); 51055682Smarkm 51155682Smarkm /* 51255682Smarkm * Send and receive 51355682Smarkm */ 514178825Sdfr { 515178825Sdfr krb5_sendto_ctx stctx; 516178825Sdfr ret = krb5_sendto_ctx_alloc(context, &stctx); 517178825Sdfr if (ret) 518178825Sdfr return ret; 519178825Sdfr krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL); 52055682Smarkm 521178825Sdfr ret = krb5_sendto_context (context, stctx, &enc, 522178825Sdfr krbtgt->server->name.name_string.val[1], 523178825Sdfr &resp); 524178825Sdfr krb5_sendto_ctx_free(context, stctx); 525178825Sdfr } 52655682Smarkm if(ret) 52755682Smarkm goto out; 52855682Smarkm 52955682Smarkm memset(&rep, 0, sizeof(rep)); 530233294Sstas if(decode_TGS_REP(resp.data, resp.length, &rep.kdc_rep, &len) == 0) { 531233294Sstas unsigned eflags = 0; 532233294Sstas 533233294Sstas ret = krb5_copy_principal(context, 534233294Sstas in_creds->client, 53555682Smarkm &out_creds->client); 53655682Smarkm if(ret) 537233294Sstas goto out2; 538233294Sstas ret = krb5_copy_principal(context, 539233294Sstas in_creds->server, 54055682Smarkm &out_creds->server); 54155682Smarkm if(ret) 542233294Sstas goto out2; 54355682Smarkm /* this should go someplace else */ 54455682Smarkm out_creds->times.endtime = in_creds->times.endtime; 54555682Smarkm 546233294Sstas /* XXX should do better testing */ 547233294Sstas if (flags.b.constrained_delegation || impersonate_principal) 548233294Sstas eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH; 549233294Sstas 55055682Smarkm ret = _krb5_extract_ticket(context, 55155682Smarkm &rep, 55255682Smarkm out_creds, 55355682Smarkm &krbtgt->session, 55455682Smarkm NULL, 555233294Sstas 0, 55655682Smarkm &krbtgt->addresses, 55755682Smarkm nonce, 558233294Sstas eflags, 55955682Smarkm decrypt_tkt_with_subkey, 56055682Smarkm subkey); 561233294Sstas out2: 56255682Smarkm krb5_free_kdc_rep(context, &rep); 56378527Sassar } else if(krb5_rd_error(context, &resp, &error) == 0) { 56478527Sassar ret = krb5_error_from_rd_error(context, &error, in_creds); 56578527Sassar krb5_free_error_contents(context, &error); 566233294Sstas } else if(resp.length > 0 && ((char*)resp.data)[0] == 4) { 56755682Smarkm ret = KRB5KRB_AP_ERR_V4_REPLY; 568233294Sstas krb5_clear_error_message(context); 56978527Sassar } else { 57055682Smarkm ret = KRB5KRB_AP_ERR_MSG_TYPE; 571233294Sstas krb5_clear_error_message(context); 57278527Sassar } 573178825Sdfr 574178825Sdfrout: 575178825Sdfr if (second_ticket == &second_ticket_data) 576178825Sdfr free_Ticket(&second_ticket_data); 577178825Sdfr free_METHOD_DATA(&padata); 57855682Smarkm krb5_data_free(&resp); 579178825Sdfr krb5_data_free(&enc); 580233294Sstas if(subkey) 581233294Sstas krb5_free_keyblock(context, subkey); 58255682Smarkm return ret; 583233294Sstas 58455682Smarkm} 58555682Smarkm 586233294Sstas/* 587233294Sstas * same as above, just get local addresses first if the krbtgt have 588233294Sstas * them and the realm is not addressless 589233294Sstas */ 590233294Sstas 59178527Sassarstatic krb5_error_code 592233294Sstasget_cred_kdc_address(krb5_context context, 593233294Sstas krb5_ccache id, 594233294Sstas krb5_kdc_flags flags, 595233294Sstas krb5_addresses *addrs, 596233294Sstas krb5_creds *in_creds, 597233294Sstas krb5_creds *krbtgt, 598233294Sstas krb5_principal impersonate_principal, 599233294Sstas Ticket *second_ticket, 600233294Sstas krb5_creds *out_creds) 60178527Sassar{ 60278527Sassar krb5_error_code ret; 603233294Sstas krb5_addresses addresses = { 0, NULL }; 60478527Sassar 605233294Sstas /* 606233294Sstas * Inherit the address-ness of the krbtgt if the address is not 607233294Sstas * specified. 608233294Sstas */ 60978527Sassar 610233294Sstas if (addrs == NULL && krbtgt->addresses.len != 0) { 611233294Sstas krb5_boolean noaddr; 61255682Smarkm 613233294Sstas krb5_appdefault_boolean(context, NULL, krbtgt->server->realm, 614233294Sstas "no-addresses", FALSE, &noaddr); 615233294Sstas 616233294Sstas if (!noaddr) { 617233294Sstas krb5_get_all_client_addrs(context, &addresses); 618233294Sstas /* XXX this sucks. */ 619233294Sstas addrs = &addresses; 620233294Sstas if(addresses.len == 0) 621233294Sstas addrs = NULL; 622233294Sstas } 623233294Sstas } 624233294Sstas ret = get_cred_kdc(context, id, flags, addrs, in_creds, 625233294Sstas krbtgt, impersonate_principal, 626233294Sstas second_ticket, out_creds); 62755682Smarkm krb5_free_addresses(context, &addresses); 62855682Smarkm return ret; 62955682Smarkm} 63055682Smarkm 631233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 63255682Smarkmkrb5_get_kdc_cred(krb5_context context, 63355682Smarkm krb5_ccache id, 63455682Smarkm krb5_kdc_flags flags, 63555682Smarkm krb5_addresses *addresses, 63655682Smarkm Ticket *second_ticket, 63755682Smarkm krb5_creds *in_creds, 63855682Smarkm krb5_creds **out_creds 63955682Smarkm ) 64055682Smarkm{ 64155682Smarkm krb5_error_code ret; 64255682Smarkm krb5_creds *krbtgt; 64378527Sassar 64455682Smarkm *out_creds = calloc(1, sizeof(**out_creds)); 64578527Sassar if(*out_creds == NULL) { 646233294Sstas krb5_set_error_message(context, ENOMEM, 647233294Sstas N_("malloc: out of memory", "")); 64855682Smarkm return ENOMEM; 64978527Sassar } 650127808Snectar ret = _krb5_get_krbtgt (context, 651127808Snectar id, 652127808Snectar in_creds->server->realm, 653127808Snectar &krbtgt); 65455682Smarkm if(ret) { 65555682Smarkm free(*out_creds); 656233294Sstas *out_creds = NULL; 65755682Smarkm return ret; 65855682Smarkm } 659233294Sstas ret = get_cred_kdc(context, id, flags, addresses, 660178825Sdfr in_creds, krbtgt, NULL, NULL, *out_creds); 66155682Smarkm krb5_free_creds (context, krbtgt); 662233294Sstas if(ret) { 66355682Smarkm free(*out_creds); 664233294Sstas *out_creds = NULL; 665233294Sstas } 66655682Smarkm return ret; 66755682Smarkm} 66855682Smarkm 669233294Sstasstatic int 670233294Sstasnot_found(krb5_context context, krb5_const_principal p, krb5_error_code code) 671178825Sdfr{ 672178825Sdfr krb5_error_code ret; 673178825Sdfr char *str; 67455682Smarkm 675178825Sdfr ret = krb5_unparse_name(context, p, &str); 676178825Sdfr if(ret) { 677233294Sstas krb5_clear_error_message(context); 678233294Sstas return code; 679178825Sdfr } 680233294Sstas krb5_set_error_message(context, code, 681233294Sstas N_("Matching credential (%s) not found", ""), str); 682178825Sdfr free(str); 683233294Sstas return code; 684178825Sdfr} 685178825Sdfr 68655682Smarkmstatic krb5_error_code 68755682Smarkmfind_cred(krb5_context context, 68855682Smarkm krb5_ccache id, 68955682Smarkm krb5_principal server, 69055682Smarkm krb5_creds **tgts, 69155682Smarkm krb5_creds *out_creds) 69255682Smarkm{ 69355682Smarkm krb5_error_code ret; 69455682Smarkm krb5_creds mcreds; 695178825Sdfr 696178825Sdfr krb5_cc_clear_mcred(&mcreds); 69755682Smarkm mcreds.server = server; 698233294Sstas ret = krb5_cc_retrieve_cred(context, id, KRB5_TC_DONT_MATCH_REALM, 69955682Smarkm &mcreds, out_creds); 70055682Smarkm if(ret == 0) 70155682Smarkm return 0; 70255682Smarkm while(tgts && *tgts){ 703233294Sstas if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM, 70455682Smarkm &mcreds, *tgts)){ 70555682Smarkm ret = krb5_copy_creds_contents(context, *tgts, out_creds); 70655682Smarkm return ret; 70755682Smarkm } 70855682Smarkm tgts++; 70955682Smarkm } 710233294Sstas return not_found(context, server, KRB5_CC_NOTFOUND); 71155682Smarkm} 71255682Smarkm 71355682Smarkmstatic krb5_error_code 714233294Sstasadd_cred(krb5_context context, krb5_creds const *tkt, krb5_creds ***tgts) 71555682Smarkm{ 71655682Smarkm int i; 71755682Smarkm krb5_error_code ret; 71855682Smarkm krb5_creds **tmp = *tgts; 71978527Sassar 72055682Smarkm for(i = 0; tmp && tmp[i]; i++); /* XXX */ 72155682Smarkm tmp = realloc(tmp, (i+2)*sizeof(*tmp)); 72278527Sassar if(tmp == NULL) { 723233294Sstas krb5_set_error_message(context, ENOMEM, 724233294Sstas N_("malloc: out of memory", "")); 72555682Smarkm return ENOMEM; 72678527Sassar } 72755682Smarkm *tgts = tmp; 72855682Smarkm ret = krb5_copy_creds(context, tkt, &tmp[i]); 72955682Smarkm tmp[i+1] = NULL; 73055682Smarkm return ret; 73155682Smarkm} 73255682Smarkm 73355682Smarkmstatic krb5_error_code 734233294Sstasget_cred_kdc_capath_worker(krb5_context context, 735233294Sstas krb5_kdc_flags flags, 736233294Sstas krb5_ccache ccache, 737233294Sstas krb5_creds *in_creds, 738233294Sstas krb5_const_realm try_realm, 739233294Sstas krb5_principal impersonate_principal, 740233294Sstas Ticket *second_ticket, 741233294Sstas krb5_creds **out_creds, 742233294Sstas krb5_creds ***ret_tgts) 74355682Smarkm{ 74455682Smarkm krb5_error_code ret; 74555682Smarkm krb5_creds *tgt, tmp_creds; 746233294Sstas krb5_const_realm client_realm, server_realm; 747233294Sstas int ok_as_delegate = 1; 74855682Smarkm 74955682Smarkm *out_creds = NULL; 75055682Smarkm 751178825Sdfr client_realm = krb5_principal_get_realm(context, in_creds->client); 752178825Sdfr server_realm = krb5_principal_get_realm(context, in_creds->server); 75355682Smarkm memset(&tmp_creds, 0, sizeof(tmp_creds)); 75455682Smarkm ret = krb5_copy_principal(context, in_creds->client, &tmp_creds.client); 75555682Smarkm if(ret) 75655682Smarkm return ret; 75772445Sassar 75855682Smarkm ret = krb5_make_principal(context, 75955682Smarkm &tmp_creds.server, 76072445Sassar try_realm, 76155682Smarkm KRB5_TGS_NAME, 762233294Sstas server_realm, 76355682Smarkm NULL); 76455682Smarkm if(ret){ 76555682Smarkm krb5_free_principal(context, tmp_creds.client); 76655682Smarkm return ret; 76755682Smarkm } 76855682Smarkm { 76955682Smarkm krb5_creds tgts; 770233294Sstas 771233294Sstas ret = find_cred(context, ccache, tmp_creds.server, 77255682Smarkm *ret_tgts, &tgts); 77355682Smarkm if(ret == 0){ 774233294Sstas /* only allow implicit ok_as_delegate if the realm is the clients realm */ 775233294Sstas if (strcmp(try_realm, client_realm) != 0 || strcmp(try_realm, server_realm) != 0) 776233294Sstas ok_as_delegate = tgts.flags.b.ok_as_delegate; 777233294Sstas 77855682Smarkm *out_creds = calloc(1, sizeof(**out_creds)); 77978527Sassar if(*out_creds == NULL) { 78055682Smarkm ret = ENOMEM; 781233294Sstas krb5_set_error_message(context, ret, 782233294Sstas N_("malloc: out of memory", "")); 78378527Sassar } else { 784233294Sstas ret = get_cred_kdc_address(context, ccache, flags, NULL, 785233294Sstas in_creds, &tgts, 786233294Sstas impersonate_principal, 787233294Sstas second_ticket, 788233294Sstas *out_creds); 78972445Sassar if (ret) { 79055682Smarkm free (*out_creds); 79172445Sassar *out_creds = NULL; 792233294Sstas } else if (ok_as_delegate == 0) 793233294Sstas (*out_creds)->flags.b.ok_as_delegate = 0; 79455682Smarkm } 795178825Sdfr krb5_free_cred_contents(context, &tgts); 79655682Smarkm krb5_free_principal(context, tmp_creds.server); 79755682Smarkm krb5_free_principal(context, tmp_creds.client); 79855682Smarkm return ret; 79955682Smarkm } 80055682Smarkm } 801233294Sstas if(krb5_realm_compare(context, in_creds->client, in_creds->server)) 802233294Sstas return not_found(context, in_creds->server, KRB5_CC_NOTFOUND); 803233294Sstas 80455682Smarkm /* XXX this can loop forever */ 80555682Smarkm while(1){ 806178825Sdfr heim_general_string tgt_inst; 80772445Sassar 808233294Sstas ret = get_cred_kdc_capath(context, flags, ccache, &tmp_creds, 809233294Sstas NULL, NULL, &tgt, ret_tgts); 81055682Smarkm if(ret) { 81155682Smarkm krb5_free_principal(context, tmp_creds.server); 81255682Smarkm krb5_free_principal(context, tmp_creds.client); 81355682Smarkm return ret; 81455682Smarkm } 815233294Sstas /* 816233294Sstas * if either of the chain or the ok_as_delegate was stripped 817233294Sstas * by the kdc, make sure we strip it too. 818233294Sstas */ 819233294Sstas if (ok_as_delegate == 0 || tgt->flags.b.ok_as_delegate == 0) { 820233294Sstas ok_as_delegate = 0; 821233294Sstas tgt->flags.b.ok_as_delegate = 0; 822233294Sstas } 823233294Sstas 824233294Sstas ret = add_cred(context, tgt, ret_tgts); 82555682Smarkm if(ret) { 82655682Smarkm krb5_free_principal(context, tmp_creds.server); 82755682Smarkm krb5_free_principal(context, tmp_creds.client); 82855682Smarkm return ret; 82955682Smarkm } 83055682Smarkm tgt_inst = tgt->server->name.name_string.val[1]; 83155682Smarkm if(strcmp(tgt_inst, server_realm) == 0) 83255682Smarkm break; 83355682Smarkm krb5_free_principal(context, tmp_creds.server); 834233294Sstas ret = krb5_make_principal(context, &tmp_creds.server, 83555682Smarkm tgt_inst, KRB5_TGS_NAME, server_realm, NULL); 83655682Smarkm if(ret) { 83755682Smarkm krb5_free_principal(context, tmp_creds.server); 83855682Smarkm krb5_free_principal(context, tmp_creds.client); 83955682Smarkm return ret; 84055682Smarkm } 84155682Smarkm ret = krb5_free_creds(context, tgt); 84255682Smarkm if(ret) { 84355682Smarkm krb5_free_principal(context, tmp_creds.server); 84455682Smarkm krb5_free_principal(context, tmp_creds.client); 84555682Smarkm return ret; 84655682Smarkm } 84755682Smarkm } 848233294Sstas 84955682Smarkm krb5_free_principal(context, tmp_creds.server); 85055682Smarkm krb5_free_principal(context, tmp_creds.client); 85155682Smarkm *out_creds = calloc(1, sizeof(**out_creds)); 85278527Sassar if(*out_creds == NULL) { 85355682Smarkm ret = ENOMEM; 854233294Sstas krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); 85578527Sassar } else { 856233294Sstas ret = get_cred_kdc_address (context, ccache, flags, NULL, 857233294Sstas in_creds, tgt, impersonate_principal, 858233294Sstas second_ticket, *out_creds); 85972445Sassar if (ret) { 86055682Smarkm free (*out_creds); 86172445Sassar *out_creds = NULL; 86272445Sassar } 86355682Smarkm } 86455682Smarkm krb5_free_creds(context, tgt); 86555682Smarkm return ret; 86655682Smarkm} 86755682Smarkm 868233294Sstas/* 869233294Sstasget_cred(server) 870233294Sstas creds = cc_get_cred(server) 871233294Sstas if(creds) return creds 872233294Sstas tgt = cc_get_cred(krbtgt/server_realm@any_realm) 873233294Sstas if(tgt) 874233294Sstas return get_cred_tgt(server, tgt) 875233294Sstas if(client_realm == server_realm) 876233294Sstas return NULL 877233294Sstas tgt = get_cred(krbtgt/server_realm@client_realm) 878233294Sstas while(tgt_inst != server_realm) 879233294Sstas tgt = get_cred(krbtgt/server_realm@tgt_inst) 880233294Sstas return get_cred_tgt(server, tgt) 881233294Sstas */ 882233294Sstas 883233294Sstasstatic krb5_error_code 884233294Sstasget_cred_kdc_capath(krb5_context context, 885233294Sstas krb5_kdc_flags flags, 886233294Sstas krb5_ccache ccache, 887233294Sstas krb5_creds *in_creds, 888233294Sstas krb5_principal impersonate_principal, 889233294Sstas Ticket *second_ticket, 890233294Sstas krb5_creds **out_creds, 891233294Sstas krb5_creds ***ret_tgts) 89278527Sassar{ 893233294Sstas krb5_error_code ret; 894233294Sstas krb5_const_realm client_realm, server_realm, try_realm; 895233294Sstas 896233294Sstas client_realm = krb5_principal_get_realm(context, in_creds->client); 897233294Sstas server_realm = krb5_principal_get_realm(context, in_creds->server); 898233294Sstas 899233294Sstas try_realm = client_realm; 900233294Sstas ret = get_cred_kdc_capath_worker(context, flags, ccache, in_creds, try_realm, 901233294Sstas impersonate_principal, second_ticket, out_creds, 902233294Sstas ret_tgts); 903233294Sstas 904233294Sstas if (ret == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) { 905233294Sstas try_realm = krb5_config_get_string(context, NULL, "capaths", 906233294Sstas client_realm, server_realm, NULL); 907233294Sstas 908233294Sstas if (try_realm != NULL && strcmp(try_realm, client_realm)) { 909233294Sstas ret = get_cred_kdc_capath_worker(context, flags, ccache, in_creds, 910233294Sstas try_realm, impersonate_principal, 911233294Sstas second_ticket, out_creds, ret_tgts); 912233294Sstas } 913233294Sstas } 914233294Sstas 915233294Sstas return ret; 91678527Sassar} 91778527Sassar 918233294Sstasstatic krb5_error_code 919233294Sstasget_cred_kdc_referral(krb5_context context, 920233294Sstas krb5_kdc_flags flags, 921233294Sstas krb5_ccache ccache, 922233294Sstas krb5_creds *in_creds, 923233294Sstas krb5_principal impersonate_principal, 924233294Sstas Ticket *second_ticket, 925233294Sstas krb5_creds **out_creds, 926233294Sstas krb5_creds ***ret_tgts) 927233294Sstas{ 928233294Sstas krb5_const_realm client_realm; 929233294Sstas krb5_error_code ret; 930233294Sstas krb5_creds tgt, referral, ticket; 931233294Sstas int loop = 0; 932233294Sstas int ok_as_delegate = 1; 933233294Sstas 934233294Sstas if (in_creds->server->name.name_string.len < 2 && !flags.b.canonicalize) { 935233294Sstas krb5_set_error_message(context, KRB5KDC_ERR_PATH_NOT_ACCEPTED, 936233294Sstas N_("Name too short to do referals, skipping", "")); 937233294Sstas return KRB5KDC_ERR_PATH_NOT_ACCEPTED; 938233294Sstas } 939233294Sstas 940233294Sstas memset(&tgt, 0, sizeof(tgt)); 941233294Sstas memset(&ticket, 0, sizeof(ticket)); 942233294Sstas 943233294Sstas flags.b.canonicalize = 1; 944233294Sstas 945233294Sstas *out_creds = NULL; 946233294Sstas 947233294Sstas client_realm = krb5_principal_get_realm(context, in_creds->client); 948233294Sstas 949233294Sstas /* find tgt for the clients base realm */ 950233294Sstas { 951233294Sstas krb5_principal tgtname; 952233294Sstas 953233294Sstas ret = krb5_make_principal(context, &tgtname, 954233294Sstas client_realm, 955233294Sstas KRB5_TGS_NAME, 956233294Sstas client_realm, 957233294Sstas NULL); 958233294Sstas if(ret) 959233294Sstas return ret; 960233294Sstas 961233294Sstas ret = find_cred(context, ccache, tgtname, *ret_tgts, &tgt); 962233294Sstas krb5_free_principal(context, tgtname); 963233294Sstas if (ret) 964233294Sstas return ret; 965233294Sstas } 966233294Sstas 967233294Sstas referral = *in_creds; 968233294Sstas ret = krb5_copy_principal(context, in_creds->server, &referral.server); 969233294Sstas if (ret) { 970233294Sstas krb5_free_cred_contents(context, &tgt); 971233294Sstas return ret; 972233294Sstas } 973233294Sstas ret = krb5_principal_set_realm(context, referral.server, client_realm); 974233294Sstas if (ret) { 975233294Sstas krb5_free_cred_contents(context, &tgt); 976233294Sstas krb5_free_principal(context, referral.server); 977233294Sstas return ret; 978233294Sstas } 979233294Sstas 980233294Sstas while (loop++ < 17) { 981233294Sstas krb5_creds **tickets; 982233294Sstas krb5_creds mcreds; 983233294Sstas char *referral_realm; 984233294Sstas 985233294Sstas /* Use cache if we are not doing impersonation or contrainte deleg */ 986233294Sstas if (impersonate_principal == NULL || flags.b.constrained_delegation) { 987233294Sstas krb5_cc_clear_mcred(&mcreds); 988233294Sstas mcreds.server = referral.server; 989233294Sstas ret = krb5_cc_retrieve_cred(context, ccache, 0, &mcreds, &ticket); 990233294Sstas } else 991233294Sstas ret = EINVAL; 992233294Sstas 993233294Sstas if (ret) { 994233294Sstas ret = get_cred_kdc_address(context, ccache, flags, NULL, 995233294Sstas &referral, &tgt, impersonate_principal, 996233294Sstas second_ticket, &ticket); 997233294Sstas if (ret) 998233294Sstas goto out; 999233294Sstas } 1000233294Sstas 1001233294Sstas /* Did we get the right ticket ? */ 1002233294Sstas if (krb5_principal_compare_any_realm(context, 1003233294Sstas referral.server, 1004233294Sstas ticket.server)) 1005233294Sstas break; 1006233294Sstas 1007233294Sstas if (!krb5_principal_is_krbtgt(context, ticket.server)) { 1008233294Sstas krb5_set_error_message(context, KRB5KRB_AP_ERR_NOT_US, 1009233294Sstas N_("Got back an non krbtgt " 1010233294Sstas "ticket referrals", "")); 1011233294Sstas ret = KRB5KRB_AP_ERR_NOT_US; 1012233294Sstas goto out; 1013233294Sstas } 1014233294Sstas 1015233294Sstas referral_realm = ticket.server->name.name_string.val[1]; 1016233294Sstas 1017233294Sstas /* check that there are no referrals loops */ 1018233294Sstas tickets = *ret_tgts; 1019233294Sstas 1020233294Sstas krb5_cc_clear_mcred(&mcreds); 1021233294Sstas mcreds.server = ticket.server; 1022233294Sstas 1023233294Sstas while(tickets && *tickets){ 1024233294Sstas if(krb5_compare_creds(context, 1025233294Sstas KRB5_TC_DONT_MATCH_REALM, 1026233294Sstas &mcreds, 1027233294Sstas *tickets)) 1028233294Sstas { 1029233294Sstas krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP, 1030233294Sstas N_("Referral from %s " 1031233294Sstas "loops back to realm %s", ""), 1032233294Sstas tgt.server->realm, 1033233294Sstas referral_realm); 1034233294Sstas ret = KRB5_GET_IN_TKT_LOOP; 1035233294Sstas goto out; 1036233294Sstas } 1037233294Sstas tickets++; 1038233294Sstas } 1039233294Sstas 1040233294Sstas /* 1041233294Sstas * if either of the chain or the ok_as_delegate was stripped 1042233294Sstas * by the kdc, make sure we strip it too. 1043233294Sstas */ 1044233294Sstas 1045233294Sstas if (ok_as_delegate == 0 || ticket.flags.b.ok_as_delegate == 0) { 1046233294Sstas ok_as_delegate = 0; 1047233294Sstas ticket.flags.b.ok_as_delegate = 0; 1048233294Sstas } 1049233294Sstas 1050233294Sstas ret = add_cred(context, &ticket, ret_tgts); 1051233294Sstas if (ret) 1052233294Sstas goto out; 1053233294Sstas 1054233294Sstas /* try realm in the referral */ 1055233294Sstas ret = krb5_principal_set_realm(context, 1056233294Sstas referral.server, 1057233294Sstas referral_realm); 1058233294Sstas krb5_free_cred_contents(context, &tgt); 1059233294Sstas tgt = ticket; 1060233294Sstas memset(&ticket, 0, sizeof(ticket)); 1061233294Sstas if (ret) 1062233294Sstas goto out; 1063233294Sstas } 1064233294Sstas 1065233294Sstas ret = krb5_copy_creds(context, &ticket, out_creds); 1066233294Sstas 1067233294Sstasout: 1068233294Sstas krb5_free_principal(context, referral.server); 1069233294Sstas krb5_free_cred_contents(context, &tgt); 1070233294Sstas krb5_free_cred_contents(context, &ticket); 1071233294Sstas return ret; 1072233294Sstas} 1073233294Sstas 1074233294Sstas 1075233294Sstas/* 1076233294Sstas * Glue function between referrals version and old client chasing 1077233294Sstas * codebase. 1078233294Sstas */ 1079233294Sstas 1080233294Sstaskrb5_error_code 1081233294Sstas_krb5_get_cred_kdc_any(krb5_context context, 1082233294Sstas krb5_kdc_flags flags, 108355682Smarkm krb5_ccache ccache, 108455682Smarkm krb5_creds *in_creds, 1085233294Sstas krb5_principal impersonate_principal, 1086233294Sstas Ticket *second_ticket, 108755682Smarkm krb5_creds **out_creds, 108855682Smarkm krb5_creds ***ret_tgts) 108955682Smarkm{ 1090233294Sstas krb5_error_code ret; 1091233294Sstas krb5_deltat offset; 1092233294Sstas 1093233294Sstas ret = krb5_cc_get_kdc_offset(context, ccache, &offset); 1094233294Sstas if (ret) { 1095233294Sstas context->kdc_sec_offset = offset; 1096233294Sstas context->kdc_usec_offset = 0; 1097233294Sstas } 1098233294Sstas 1099233294Sstas ret = get_cred_kdc_referral(context, 1100233294Sstas flags, 1101233294Sstas ccache, 1102233294Sstas in_creds, 1103233294Sstas impersonate_principal, 1104233294Sstas second_ticket, 1105233294Sstas out_creds, 1106233294Sstas ret_tgts); 1107233294Sstas if (ret == 0 || flags.b.canonicalize) 1108233294Sstas return ret; 1109233294Sstas return get_cred_kdc_capath(context, 1110233294Sstas flags, 1111233294Sstas ccache, 1112233294Sstas in_creds, 1113233294Sstas impersonate_principal, 1114233294Sstas second_ticket, 1115233294Sstas out_creds, 1116233294Sstas ret_tgts); 111755682Smarkm} 111855682Smarkm 1119233294Sstas 1120233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 112155682Smarkmkrb5_get_credentials_with_flags(krb5_context context, 112255682Smarkm krb5_flags options, 112355682Smarkm krb5_kdc_flags flags, 112455682Smarkm krb5_ccache ccache, 112555682Smarkm krb5_creds *in_creds, 112655682Smarkm krb5_creds **out_creds) 112755682Smarkm{ 112855682Smarkm krb5_error_code ret; 112955682Smarkm krb5_creds **tgts; 113072445Sassar krb5_creds *res_creds; 113155682Smarkm int i; 1132233294Sstas 1133233294Sstas if (in_creds->session.keytype) { 1134233294Sstas ret = krb5_enctype_valid(context, in_creds->session.keytype); 1135233294Sstas if (ret) 1136233294Sstas return ret; 1137233294Sstas } 1138233294Sstas 113972445Sassar *out_creds = NULL; 114072445Sassar res_creds = calloc(1, sizeof(*res_creds)); 114178527Sassar if (res_creds == NULL) { 1142233294Sstas krb5_set_error_message(context, ENOMEM, 1143233294Sstas N_("malloc: out of memory", "")); 114455682Smarkm return ENOMEM; 114578527Sassar } 114655682Smarkm 1147178825Sdfr if (in_creds->session.keytype) 1148178825Sdfr options |= KRB5_TC_MATCH_KEYTYPE; 1149178825Sdfr 1150233294Sstas /* 1151178825Sdfr * If we got a credential, check if credential is expired before 1152178825Sdfr * returning it. 1153178825Sdfr */ 115455682Smarkm ret = krb5_cc_retrieve_cred(context, 1155178825Sdfr ccache, 1156178825Sdfr in_creds->session.keytype ? 1157178825Sdfr KRB5_TC_MATCH_KEYTYPE : 0, 1158178825Sdfr in_creds, res_creds); 1159233294Sstas /* 1160178825Sdfr * If we got a credential, check if credential is expired before 1161178825Sdfr * returning it, but only if KRB5_GC_EXPIRED_OK is not set. 1162178825Sdfr */ 1163178825Sdfr if (ret == 0) { 1164178825Sdfr krb5_timestamp timeret; 1165178825Sdfr 1166178825Sdfr /* If expired ok, don't bother checking */ 1167178825Sdfr if(options & KRB5_GC_EXPIRED_OK) { 1168178825Sdfr *out_creds = res_creds; 1169178825Sdfr return 0; 1170178825Sdfr } 1171233294Sstas 1172178825Sdfr krb5_timeofday(context, &timeret); 1173178825Sdfr if(res_creds->times.endtime > timeret) { 1174178825Sdfr *out_creds = res_creds; 1175178825Sdfr return 0; 1176178825Sdfr } 1177178825Sdfr if(options & KRB5_GC_CACHED) 1178178825Sdfr krb5_cc_remove_cred(context, ccache, 0, res_creds); 1179178825Sdfr 1180178825Sdfr } else if(ret != KRB5_CC_END) { 1181178825Sdfr free(res_creds); 1182178825Sdfr return ret; 118372445Sassar } 118472445Sassar free(res_creds); 1185233294Sstas if(options & KRB5_GC_CACHED) 1186233294Sstas return not_found(context, in_creds->server, KRB5_CC_NOTFOUND); 1187233294Sstas 118855682Smarkm if(options & KRB5_GC_USER_USER) 118955682Smarkm flags.b.enc_tkt_in_skey = 1; 1190178825Sdfr if (flags.b.enc_tkt_in_skey) 1191178825Sdfr options |= KRB5_GC_NO_STORE; 1192178825Sdfr 119355682Smarkm tgts = NULL; 1194233294Sstas ret = _krb5_get_cred_kdc_any(context, flags, ccache, 1195233294Sstas in_creds, NULL, NULL, out_creds, &tgts); 119672445Sassar for(i = 0; tgts && tgts[i]; i++) { 119755682Smarkm krb5_cc_store_cred(context, ccache, tgts[i]); 119855682Smarkm krb5_free_creds(context, tgts[i]); 119955682Smarkm } 120055682Smarkm free(tgts); 1201178825Sdfr if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0) 120255682Smarkm krb5_cc_store_cred(context, ccache, *out_creds); 120355682Smarkm return ret; 120455682Smarkm} 120555682Smarkm 1206233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 120755682Smarkmkrb5_get_credentials(krb5_context context, 120855682Smarkm krb5_flags options, 120955682Smarkm krb5_ccache ccache, 121055682Smarkm krb5_creds *in_creds, 121155682Smarkm krb5_creds **out_creds) 121255682Smarkm{ 121355682Smarkm krb5_kdc_flags flags; 121455682Smarkm flags.i = 0; 121555682Smarkm return krb5_get_credentials_with_flags(context, options, flags, 121655682Smarkm ccache, in_creds, out_creds); 121755682Smarkm} 1218178825Sdfr 1219178825Sdfrstruct krb5_get_creds_opt_data { 1220178825Sdfr krb5_principal self; 1221178825Sdfr krb5_flags options; 1222178825Sdfr krb5_enctype enctype; 1223178825Sdfr Ticket *ticket; 1224178825Sdfr}; 1225178825Sdfr 1226178825Sdfr 1227233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1228178825Sdfrkrb5_get_creds_opt_alloc(krb5_context context, krb5_get_creds_opt *opt) 1229178825Sdfr{ 1230178825Sdfr *opt = calloc(1, sizeof(**opt)); 1231178825Sdfr if (*opt == NULL) { 1232233294Sstas krb5_set_error_message(context, ENOMEM, 1233233294Sstas N_("malloc: out of memory", "")); 1234178825Sdfr return ENOMEM; 1235178825Sdfr } 1236178825Sdfr return 0; 1237178825Sdfr} 1238178825Sdfr 1239233294SstasKRB5_LIB_FUNCTION void KRB5_LIB_CALL 1240178825Sdfrkrb5_get_creds_opt_free(krb5_context context, krb5_get_creds_opt opt) 1241178825Sdfr{ 1242178825Sdfr if (opt->self) 1243178825Sdfr krb5_free_principal(context, opt->self); 1244233294Sstas if (opt->ticket) { 1245233294Sstas free_Ticket(opt->ticket); 1246233294Sstas free(opt->ticket); 1247233294Sstas } 1248178825Sdfr memset(opt, 0, sizeof(*opt)); 1249178825Sdfr free(opt); 1250178825Sdfr} 1251178825Sdfr 1252233294SstasKRB5_LIB_FUNCTION void KRB5_LIB_CALL 1253178825Sdfrkrb5_get_creds_opt_set_options(krb5_context context, 1254178825Sdfr krb5_get_creds_opt opt, 1255178825Sdfr krb5_flags options) 1256178825Sdfr{ 1257178825Sdfr opt->options = options; 1258178825Sdfr} 1259178825Sdfr 1260233294SstasKRB5_LIB_FUNCTION void KRB5_LIB_CALL 1261178825Sdfrkrb5_get_creds_opt_add_options(krb5_context context, 1262178825Sdfr krb5_get_creds_opt opt, 1263178825Sdfr krb5_flags options) 1264178825Sdfr{ 1265178825Sdfr opt->options |= options; 1266178825Sdfr} 1267178825Sdfr 1268233294SstasKRB5_LIB_FUNCTION void KRB5_LIB_CALL 1269178825Sdfrkrb5_get_creds_opt_set_enctype(krb5_context context, 1270178825Sdfr krb5_get_creds_opt opt, 1271178825Sdfr krb5_enctype enctype) 1272178825Sdfr{ 1273178825Sdfr opt->enctype = enctype; 1274178825Sdfr} 1275178825Sdfr 1276233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1277178825Sdfrkrb5_get_creds_opt_set_impersonate(krb5_context context, 1278178825Sdfr krb5_get_creds_opt opt, 1279178825Sdfr krb5_const_principal self) 1280178825Sdfr{ 1281178825Sdfr if (opt->self) 1282178825Sdfr krb5_free_principal(context, opt->self); 1283178825Sdfr return krb5_copy_principal(context, self, &opt->self); 1284178825Sdfr} 1285178825Sdfr 1286233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1287178825Sdfrkrb5_get_creds_opt_set_ticket(krb5_context context, 1288178825Sdfr krb5_get_creds_opt opt, 1289178825Sdfr const Ticket *ticket) 1290178825Sdfr{ 1291178825Sdfr if (opt->ticket) { 1292178825Sdfr free_Ticket(opt->ticket); 1293178825Sdfr free(opt->ticket); 1294178825Sdfr opt->ticket = NULL; 1295178825Sdfr } 1296178825Sdfr if (ticket) { 1297178825Sdfr krb5_error_code ret; 1298178825Sdfr 1299178825Sdfr opt->ticket = malloc(sizeof(*ticket)); 1300178825Sdfr if (opt->ticket == NULL) { 1301233294Sstas krb5_set_error_message(context, ENOMEM, 1302233294Sstas N_("malloc: out of memory", "")); 1303178825Sdfr return ENOMEM; 1304178825Sdfr } 1305178825Sdfr ret = copy_Ticket(ticket, opt->ticket); 1306178825Sdfr if (ret) { 1307178825Sdfr free(opt->ticket); 1308178825Sdfr opt->ticket = NULL; 1309233294Sstas krb5_set_error_message(context, ret, 1310233294Sstas N_("malloc: out of memory", "")); 1311178825Sdfr return ret; 1312178825Sdfr } 1313178825Sdfr } 1314178825Sdfr return 0; 1315178825Sdfr} 1316178825Sdfr 1317178825Sdfr 1318178825Sdfr 1319233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1320178825Sdfrkrb5_get_creds(krb5_context context, 1321178825Sdfr krb5_get_creds_opt opt, 1322178825Sdfr krb5_ccache ccache, 1323178825Sdfr krb5_const_principal inprinc, 1324178825Sdfr krb5_creds **out_creds) 1325178825Sdfr{ 1326178825Sdfr krb5_kdc_flags flags; 1327178825Sdfr krb5_flags options; 1328178825Sdfr krb5_creds in_creds; 1329178825Sdfr krb5_error_code ret; 1330178825Sdfr krb5_creds **tgts; 1331178825Sdfr krb5_creds *res_creds; 1332178825Sdfr int i; 1333233294Sstas 1334233294Sstas if (opt && opt->enctype) { 1335233294Sstas ret = krb5_enctype_valid(context, opt->enctype); 1336233294Sstas if (ret) 1337233294Sstas return ret; 1338233294Sstas } 1339233294Sstas 1340178825Sdfr memset(&in_creds, 0, sizeof(in_creds)); 1341178825Sdfr in_creds.server = rk_UNCONST(inprinc); 1342178825Sdfr 1343178825Sdfr ret = krb5_cc_get_principal(context, ccache, &in_creds.client); 1344178825Sdfr if (ret) 1345178825Sdfr return ret; 1346178825Sdfr 1347233294Sstas if (opt) 1348233294Sstas options = opt->options; 1349233294Sstas else 1350233294Sstas options = 0; 1351178825Sdfr flags.i = 0; 1352178825Sdfr 1353178825Sdfr *out_creds = NULL; 1354178825Sdfr res_creds = calloc(1, sizeof(*res_creds)); 1355178825Sdfr if (res_creds == NULL) { 1356178825Sdfr krb5_free_principal(context, in_creds.client); 1357233294Sstas krb5_set_error_message(context, ENOMEM, 1358233294Sstas N_("malloc: out of memory", "")); 1359178825Sdfr return ENOMEM; 1360178825Sdfr } 1361178825Sdfr 1362233294Sstas if (opt && opt->enctype) { 1363178825Sdfr in_creds.session.keytype = opt->enctype; 1364178825Sdfr options |= KRB5_TC_MATCH_KEYTYPE; 1365178825Sdfr } 1366178825Sdfr 1367233294Sstas /* 1368178825Sdfr * If we got a credential, check if credential is expired before 1369178825Sdfr * returning it. 1370178825Sdfr */ 1371178825Sdfr ret = krb5_cc_retrieve_cred(context, 1372178825Sdfr ccache, 1373233294Sstas options & KRB5_TC_MATCH_KEYTYPE, 1374178825Sdfr &in_creds, res_creds); 1375233294Sstas /* 1376178825Sdfr * If we got a credential, check if credential is expired before 1377178825Sdfr * returning it, but only if KRB5_GC_EXPIRED_OK is not set. 1378178825Sdfr */ 1379178825Sdfr if (ret == 0) { 1380178825Sdfr krb5_timestamp timeret; 1381178825Sdfr 1382178825Sdfr /* If expired ok, don't bother checking */ 1383178825Sdfr if(options & KRB5_GC_EXPIRED_OK) { 1384178825Sdfr *out_creds = res_creds; 1385178825Sdfr krb5_free_principal(context, in_creds.client); 1386233294Sstas goto out; 1387178825Sdfr } 1388233294Sstas 1389178825Sdfr krb5_timeofday(context, &timeret); 1390178825Sdfr if(res_creds->times.endtime > timeret) { 1391178825Sdfr *out_creds = res_creds; 1392178825Sdfr krb5_free_principal(context, in_creds.client); 1393233294Sstas goto out; 1394178825Sdfr } 1395178825Sdfr if(options & KRB5_GC_CACHED) 1396178825Sdfr krb5_cc_remove_cred(context, ccache, 0, res_creds); 1397178825Sdfr 1398178825Sdfr } else if(ret != KRB5_CC_END) { 1399178825Sdfr free(res_creds); 1400178825Sdfr krb5_free_principal(context, in_creds.client); 1401233294Sstas goto out; 1402178825Sdfr } 1403178825Sdfr free(res_creds); 1404178825Sdfr if(options & KRB5_GC_CACHED) { 1405178825Sdfr krb5_free_principal(context, in_creds.client); 1406233294Sstas ret = not_found(context, in_creds.server, KRB5_CC_NOTFOUND); 1407233294Sstas goto out; 1408178825Sdfr } 1409178825Sdfr if(options & KRB5_GC_USER_USER) { 1410178825Sdfr flags.b.enc_tkt_in_skey = 1; 1411178825Sdfr options |= KRB5_GC_NO_STORE; 1412178825Sdfr } 1413178825Sdfr if (options & KRB5_GC_FORWARDABLE) 1414178825Sdfr flags.b.forwardable = 1; 1415178825Sdfr if (options & KRB5_GC_NO_TRANSIT_CHECK) 1416178825Sdfr flags.b.disable_transited_check = 1; 1417178825Sdfr if (options & KRB5_GC_CONSTRAINED_DELEGATION) { 1418178825Sdfr flags.b.request_anonymous = 1; /* XXX ARGH confusion */ 1419178825Sdfr flags.b.constrained_delegation = 1; 1420178825Sdfr } 1421233294Sstas if (options & KRB5_GC_CANONICALIZE) 1422233294Sstas flags.b.canonicalize = 1; 1423178825Sdfr 1424178825Sdfr tgts = NULL; 1425233294Sstas ret = _krb5_get_cred_kdc_any(context, flags, ccache, 1426233294Sstas &in_creds, opt->self, opt->ticket, 1427233294Sstas out_creds, &tgts); 1428178825Sdfr krb5_free_principal(context, in_creds.client); 1429178825Sdfr for(i = 0; tgts && tgts[i]; i++) { 1430178825Sdfr krb5_cc_store_cred(context, ccache, tgts[i]); 1431178825Sdfr krb5_free_creds(context, tgts[i]); 1432178825Sdfr } 1433178825Sdfr free(tgts); 1434178825Sdfr if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0) 1435178825Sdfr krb5_cc_store_cred(context, ccache, *out_creds); 1436233294Sstas 1437233294Sstas out: 1438233294Sstas _krb5_debug(context, 5, "krb5_get_creds: ret = %d", ret); 1439233294Sstas 1440178825Sdfr return ret; 1441178825Sdfr} 1442178825Sdfr 1443178825Sdfr/* 1444178825Sdfr * 1445178825Sdfr */ 1446178825Sdfr 1447233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1448178825Sdfrkrb5_get_renewed_creds(krb5_context context, 1449178825Sdfr krb5_creds *creds, 1450178825Sdfr krb5_const_principal client, 1451178825Sdfr krb5_ccache ccache, 1452178825Sdfr const char *in_tkt_service) 1453178825Sdfr{ 1454178825Sdfr krb5_error_code ret; 1455178825Sdfr krb5_kdc_flags flags; 1456178825Sdfr krb5_creds in, *template, *out = NULL; 1457178825Sdfr 1458178825Sdfr memset(&in, 0, sizeof(in)); 1459178825Sdfr memset(creds, 0, sizeof(*creds)); 1460178825Sdfr 1461178825Sdfr ret = krb5_copy_principal(context, client, &in.client); 1462178825Sdfr if (ret) 1463178825Sdfr return ret; 1464178825Sdfr 1465178825Sdfr if (in_tkt_service) { 1466178825Sdfr ret = krb5_parse_name(context, in_tkt_service, &in.server); 1467178825Sdfr if (ret) { 1468178825Sdfr krb5_free_principal(context, in.client); 1469178825Sdfr return ret; 1470178825Sdfr } 1471178825Sdfr } else { 1472178825Sdfr const char *realm = krb5_principal_get_realm(context, client); 1473233294Sstas 1474178825Sdfr ret = krb5_make_principal(context, &in.server, realm, KRB5_TGS_NAME, 1475178825Sdfr realm, NULL); 1476178825Sdfr if (ret) { 1477178825Sdfr krb5_free_principal(context, in.client); 1478178825Sdfr return ret; 1479178825Sdfr } 1480178825Sdfr } 1481178825Sdfr 1482178825Sdfr flags.i = 0; 1483178825Sdfr flags.b.renewable = flags.b.renew = 1; 1484178825Sdfr 1485178825Sdfr /* 1486178825Sdfr * Get template from old credential cache for the same entry, if 1487178825Sdfr * this failes, no worries. 1488178825Sdfr */ 1489178825Sdfr ret = krb5_get_credentials(context, KRB5_GC_CACHED, ccache, &in, &template); 1490178825Sdfr if (ret == 0) { 1491178825Sdfr flags.b.forwardable = template->flags.b.forwardable; 1492178825Sdfr flags.b.proxiable = template->flags.b.proxiable; 1493178825Sdfr krb5_free_creds (context, template); 1494178825Sdfr } 1495178825Sdfr 1496178825Sdfr ret = krb5_get_kdc_cred(context, ccache, flags, NULL, NULL, &in, &out); 1497178825Sdfr krb5_free_principal(context, in.client); 1498178825Sdfr krb5_free_principal(context, in.server); 1499178825Sdfr if (ret) 1500178825Sdfr return ret; 1501178825Sdfr 1502178825Sdfr ret = krb5_copy_creds_contents(context, out, creds); 1503178825Sdfr krb5_free_creds(context, out); 1504178825Sdfr 1505178825Sdfr return ret; 1506178825Sdfr} 1507