LCOV - code coverage report
Current view: top level - third_party/heimdal/lib/krb5 - get_cred.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 697 1039 67.1 %
Date: 2024-04-21 15:09:00 Functions: 28 30 93.3 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
       3             :  * (Royal Institute of Technology, Stockholm, Sweden).
       4             :  * All rights reserved.
       5             :  *
       6             :  * Portions Copyright (c) 2009 - 2010 Apple Inc. All rights reserved.
       7             :  *
       8             :  * Redistribution and use in source and binary forms, with or without
       9             :  * modification, are permitted provided that the following conditions
      10             :  * are met:
      11             :  *
      12             :  * 1. Redistributions of source code must retain the above copyright
      13             :  *    notice, this list of conditions and the following disclaimer.
      14             :  *
      15             :  * 2. Redistributions in binary form must reproduce the above copyright
      16             :  *    notice, this list of conditions and the following disclaimer in the
      17             :  *    documentation and/or other materials provided with the distribution.
      18             :  *
      19             :  * 3. Neither the name of the Institute nor the names of its contributors
      20             :  *    may be used to endorse or promote products derived from this software
      21             :  *    without specific prior written permission.
      22             :  *
      23             :  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
      24             :  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      25             :  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
      26             :  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
      27             :  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
      28             :  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
      29             :  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      30             :  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
      31             :  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
      32             :  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
      33             :  * SUCH DAMAGE.
      34             :  */
      35             : 
      36             : #include "krb5_locl.h"
      37             : #include <assert.h>
      38             : 
      39             : static krb5_error_code
      40             : get_cred_kdc_capath(krb5_context, krb5_kdc_flags,
      41             :                     krb5_ccache, struct krb5_fast_state *,
      42             :                     krb5_creds *, krb5_principal,
      43             :                     Ticket *, const char *, const char *,
      44             :                     krb5_creds **, krb5_creds ***);
      45             : 
      46             : /*
      47             :  * Take the `body' and encode it into `padata' using the credentials
      48             :  * in `creds'.
      49             :  */
      50             : 
      51             : static krb5_error_code
      52       48002 : make_pa_tgs_req(krb5_context context,
      53             :                 krb5_auth_context *ac,
      54             :                 KDC_REQ_BODY *body,
      55             :                 krb5_ccache ccache,
      56             :                 krb5_creds *creds,
      57             :                 krb5_data *tgs_req)
      58             : {
      59        1658 :     krb5_error_code ret;
      60        1658 :     krb5_data in_data;
      61        1658 :     size_t buf_size;
      62       48002 :     size_t len = 0;
      63        1658 :     uint8_t *buf;
      64             : 
      65       48002 :     ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
      66       48002 :     if (ret)
      67           0 :         return ret;
      68             : 
      69       48002 :     if(buf_size != len)
      70           0 :         krb5_abortx(context, "internal error in ASN.1 encoder");
      71             : 
      72       48002 :     in_data.length = len;
      73       48002 :     in_data.data   = buf;
      74       48002 :     ret = _krb5_mk_req_internal(context, ac, 0, &in_data,
      75             :                                 creds, tgs_req,
      76             :                                 KRB5_KU_TGS_REQ_AUTH_CKSUM,
      77             :                                 KRB5_KU_TGS_REQ_AUTH);
      78       48002 :     free (buf);
      79       48002 :     return ret;
      80             : }
      81             : 
      82             : /*
      83             :  * Set the `enc-authorization-data' in `req_body' based on `authdata'
      84             :  */
      85             : 
      86             : static krb5_error_code
      87       48002 : set_auth_data (krb5_context context,
      88             :                KDC_REQ_BODY *req_body,
      89             :                krb5_authdata *authdata,
      90             :                krb5_keyblock *subkey)
      91             : {
      92       48002 :     if(authdata->len) {
      93           0 :         size_t len = 0, buf_size;
      94           0 :         unsigned char *buf;
      95           0 :         krb5_crypto crypto;
      96           0 :         krb5_error_code ret;
      97             : 
      98           0 :         ASN1_MALLOC_ENCODE(AuthorizationData, buf, buf_size, authdata,
      99             :                            &len, ret);
     100           0 :         if (ret)
     101           0 :             return ret;
     102           0 :         if (buf_size != len)
     103           0 :             krb5_abortx(context, "internal error in ASN.1 encoder");
     104             : 
     105           0 :         ALLOC(req_body->enc_authorization_data, 1);
     106           0 :         if (req_body->enc_authorization_data == NULL) {
     107           0 :             free (buf);
     108           0 :             return krb5_enomem(context);
     109             :         }
     110           0 :         ret = krb5_crypto_init(context, subkey, 0, &crypto);
     111           0 :         if (ret) {
     112           0 :             free (buf);
     113           0 :             free (req_body->enc_authorization_data);
     114           0 :             req_body->enc_authorization_data = NULL;
     115           0 :             return ret;
     116             :         }
     117           0 :         ret = krb5_encrypt_EncryptedData(context,
     118             :                                          crypto,
     119             :                                          KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY,
     120             :                                          buf,
     121             :                                          len,
     122             :                                          0,
     123             :                                          req_body->enc_authorization_data);
     124           0 :         free (buf);
     125           0 :         krb5_crypto_destroy(context, crypto);
     126           0 :         return ret;
     127             :     } else {
     128       48002 :         req_body->enc_authorization_data = NULL;
     129       48002 :         return 0;
     130             :     }
     131             : }
     132             : 
     133             : /*
     134             :  * Create a tgs-req in `t' with `addresses', `flags', `second_ticket'
     135             :  * (if not-NULL), `in_creds', `krbtgt', and returning the generated
     136             :  * subkey in `subkey'.
     137             :  */
     138             : 
     139             : static krb5_error_code
     140       48002 : init_tgs_req (krb5_context context,
     141             :               krb5_ccache ccache,
     142             :               struct krb5_fast_state *state,
     143             :               krb5_addresses *addresses,
     144             :               krb5_kdc_flags flags,
     145             :               Ticket *second_ticket,
     146             :               krb5_creds *in_creds,
     147             :               krb5_creds *krbtgt,
     148             :               unsigned nonce,
     149             :               const METHOD_DATA *padata,
     150             :               krb5_keyblock **subkey,
     151             :               TGS_REQ *t)
     152             : {
     153       48002 :     krb5_auth_context ac = NULL;
     154       48002 :     krb5_error_code ret = 0;
     155        1658 :     krb5_data tgs_req;
     156             : 
     157       48002 :     krb5_data_zero(&tgs_req);
     158       48002 :     memset(t, 0, sizeof(*t));
     159             : 
     160       48002 :     t->pvno = 5;
     161       48002 :     t->msg_type = krb_tgs_req;
     162       48002 :     if (in_creds->session.keytype) {
     163           0 :         ALLOC_SEQ(&t->req_body.etype, 1);
     164           0 :         if(t->req_body.etype.val == NULL) {
     165           0 :             ret = krb5_enomem(context);
     166           0 :             goto fail;
     167             :         }
     168           0 :         t->req_body.etype.val[0] = in_creds->session.keytype;
     169             :     } else {
     170       48002 :         ret = _krb5_init_etype(context,
     171             :                                KRB5_PDU_TGS_REQUEST,
     172             :                                &t->req_body.etype.len,
     173       48002 :                                &t->req_body.etype.val,
     174             :                                NULL);
     175             :     }
     176       48002 :     if (ret)
     177           0 :         goto fail;
     178       48002 :     t->req_body.addresses = addresses;
     179       48002 :     t->req_body.kdc_options = flags.b;
     180       48002 :     t->req_body.kdc_options.forwardable = krbtgt->flags.b.forwardable;
     181       48002 :     t->req_body.kdc_options.renewable = krbtgt->flags.b.renewable;
     182       48002 :     t->req_body.kdc_options.proxiable = krbtgt->flags.b.proxiable;
     183       48002 :     ret = copy_Realm(&in_creds->server->realm, &t->req_body.realm);
     184       48002 :     if (ret)
     185           0 :         goto fail;
     186       48002 :     ALLOC(t->req_body.sname, 1);
     187       48002 :     if (t->req_body.sname == NULL) {
     188           0 :         ret = krb5_enomem(context);
     189           0 :         goto fail;
     190             :     }
     191             : 
     192             :     /* some versions of some code might require that the client be
     193             :        present in TGS-REQs, but this is clearly against the spec */
     194             : 
     195       48002 :     ret = copy_PrincipalName(&in_creds->server->name, t->req_body.sname);
     196       48002 :     if (ret)
     197           0 :         goto fail;
     198             : 
     199       48002 :     if (krbtgt->times.starttime) {
     200       48002 :         ALLOC(t->req_body.from, 1);
     201       48002 :         if(t->req_body.from == NULL){
     202           0 :             ret = krb5_enomem(context);
     203           0 :             goto fail;
     204             :         }
     205       48002 :         *t->req_body.from = in_creds->times.starttime;
     206             :     }
     207             : 
     208             :     /* req_body.till should be NULL if there is no endtime specified,
     209             :        but old MIT code (like DCE secd) doesn't like that */
     210       48002 :     ALLOC(t->req_body.till, 1);
     211       48002 :     if(t->req_body.till == NULL){
     212           0 :         ret = krb5_enomem(context);
     213           0 :         goto fail;
     214             :     }
     215       48002 :     *t->req_body.till = in_creds->times.endtime;
     216             : 
     217       48002 :     if (t->req_body.kdc_options.renewable && krbtgt->times.renew_till) {
     218        3085 :         ALLOC(t->req_body.rtime, 1);
     219        3085 :         if(t->req_body.rtime == NULL){
     220           0 :             ret = krb5_enomem(context);
     221           0 :             goto fail;
     222             :         }
     223        3085 :         *t->req_body.rtime = in_creds->times.renew_till;
     224             :     }
     225             : 
     226       48002 :     t->req_body.nonce = nonce;
     227       48002 :     if(second_ticket){
     228          20 :         ALLOC(t->req_body.additional_tickets, 1);
     229          20 :         if (t->req_body.additional_tickets == NULL) {
     230           0 :             ret = krb5_enomem(context);
     231           0 :             goto fail;
     232             :         }
     233          20 :         ALLOC_SEQ(t->req_body.additional_tickets, 1);
     234          20 :         if (t->req_body.additional_tickets->val == NULL) {
     235           0 :             ret = krb5_enomem(context);
     236           0 :             goto fail;
     237             :         }
     238          20 :         ret = copy_Ticket(second_ticket, t->req_body.additional_tickets->val);
     239          20 :         if (ret)
     240           0 :             goto fail;
     241             :     }
     242             : 
     243       48002 :     ret = krb5_auth_con_init(context, &ac);
     244       48002 :     if(ret)
     245           0 :         goto fail;
     246             : 
     247       48002 :     ret = krb5_auth_con_generatelocalsubkey(context, ac, &krbtgt->session);
     248       48002 :     if (ret)
     249           0 :         goto fail;
     250             : 
     251       48002 :     if (state) {
     252        1658 :         krb5_data empty;
     253             : 
     254       48002 :         krb5_data_zero(&empty);
     255       48002 :         ret = krb5_auth_con_add_AuthorizationData(context, ac,
     256             :                                                   KRB5_AUTHDATA_FX_FAST_USED,
     257             :                                                    &empty);
     258       48002 :         if (ret)
     259           0 :             goto fail;
     260             :     }
     261             : 
     262       49660 :     ret = set_auth_data(context, &t->req_body,
     263       48002 :                         &in_creds->authdata, ac->local_subkey);
     264       48002 :     if (ret)
     265           0 :         goto fail;
     266             : 
     267       48002 :     ret = make_pa_tgs_req(context,
     268             :                           &ac,
     269             :                           &t->req_body,
     270             :                           ccache,
     271             :                           krbtgt,
     272             :                           &tgs_req);
     273       48002 :     if(ret)
     274           0 :         goto fail;
     275             : 
     276             :     /*
     277             :      * Add KRB5_PADATA_TGS_REQ first
     278             :      * followed by all others.
     279             :      */
     280             : 
     281       48002 :     if (t->padata == NULL) {
     282       48002 :         ALLOC(t->padata, 1);
     283       48002 :         if (t->padata == NULL) {
     284           0 :             ret = krb5_enomem(context);
     285           0 :             goto fail;
     286             :         }
     287             :     }
     288             : 
     289       48002 :     ret = krb5_padata_add(context, t->padata, KRB5_PADATA_TGS_REQ,
     290             :                           tgs_req.data, tgs_req.length);
     291       48002 :     if (ret)
     292           0 :         goto fail;
     293             : 
     294       48002 :     krb5_data_zero(&tgs_req);
     295             : 
     296             :     {
     297        1658 :         size_t i;
     298       50562 :         for (i = 0; i < padata->len; i++) {
     299         902 :             const PA_DATA *val1 = &padata->val[i];
     300           0 :             PA_DATA val2;
     301             : 
     302         902 :             ret = copy_PA_DATA(val1, &val2);
     303         902 :             if (ret) {
     304           0 :                 krb5_set_error_message(context, ret,
     305           0 :                                        N_("malloc: out of memory", ""));
     306           0 :                 goto fail;
     307             :             }
     308             : 
     309         902 :             ret = krb5_padata_add(context, t->padata,
     310         902 :                                   val2.padata_type,
     311             :                                   val2.padata_value.data,
     312             :                                   val2.padata_value.length);
     313         902 :             if (ret) {
     314           0 :                 free_PA_DATA(&val2);
     315             : 
     316           0 :                 krb5_set_error_message(context, ret,
     317           0 :                                        N_("malloc: out of memory", ""));
     318           0 :                 goto fail;
     319             :             }
     320             :         }
     321             :     }
     322             : 
     323       48002 :     if (state) {
     324       48002 :         state->armor_ac = ac;
     325       48002 :         ret = _krb5_fast_create_armor(context, state, NULL);
     326       48002 :         state->armor_ac = NULL;
     327       48002 :         if (ret)
     328           0 :             goto fail;
     329             : 
     330       48002 :         ret = _krb5_fast_wrap_req(context, state, t);
     331       48002 :         if (ret)
     332           0 :             goto fail;
     333             : 
     334             :         /* Its ok if there is no fast in the TGS-REP, older heimdal only support it in the AS code path */
     335       48002 :         state->flags &= ~KRB5_FAST_EXPECTED;
     336             :     }
     337             : 
     338       48002 :     ret = krb5_auth_con_getlocalsubkey(context, ac, subkey);
     339       48002 :     if (ret)
     340           0 :         goto fail;
     341             : 
     342       48002 : fail:
     343       48002 :     if (ac)
     344       48002 :         krb5_auth_con_free(context, ac);
     345       48002 :     if (ret) {
     346           0 :         t->req_body.addresses = NULL;
     347           0 :         free_TGS_REQ (t);
     348             :     }
     349       48002 :     krb5_data_free(&tgs_req);
     350             : 
     351       48002 :     return ret;
     352             : }
     353             : 
     354             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
     355       23851 : _krb5_get_krbtgt(krb5_context context,
     356             :                  krb5_ccache  id,
     357             :                  krb5_realm realm,
     358             :                  krb5_creds **cred)
     359             : {
     360        1035 :     krb5_error_code ret;
     361        1035 :     krb5_creds tmp_cred;
     362             : 
     363       23851 :     memset(&tmp_cred, 0, sizeof(tmp_cred));
     364             : 
     365       23851 :     ret = krb5_cc_get_principal(context, id, &tmp_cred.client);
     366       23851 :     if (ret)
     367           0 :         return ret;
     368             : 
     369       23851 :     if (realm == NULL)
     370           0 :         realm = tmp_cred.client->realm;
     371             : 
     372       23851 :     ret = krb5_make_principal(context,
     373             :                               &tmp_cred.server,
     374             :                               realm,
     375             :                               KRB5_TGS_NAME,
     376             :                               realm,
     377             :                               NULL);
     378       23851 :     if(ret) {
     379           0 :         krb5_free_principal(context, tmp_cred.client);
     380           0 :         return ret;
     381             :     }
     382             :     /*
     383             :      * The forwardable TGT might not be the start TGT, in which case, it is
     384             :      * generally, but not always already cached.  Just in case, get it again if
     385             :      * lost.
     386             :      */
     387       23851 :     ret = krb5_get_credentials(context,
     388             :                                0,
     389             :                                id,
     390             :                                &tmp_cred,
     391             :                                cred);
     392       23851 :     krb5_free_principal(context, tmp_cred.client);
     393       23851 :     krb5_free_principal(context, tmp_cred.server);
     394       23851 :     if(ret)
     395           9 :         return ret;
     396       22807 :     return 0;
     397             : }
     398             : 
     399             : static krb5_error_code
     400       46174 : fast_tgs_strengthen_key(krb5_context context,
     401             :                         struct krb5_fast_state *state,
     402             :                         krb5_keyblock *reply_key,
     403             :                         krb5_keyblock *extract_key)
     404             : {
     405        1658 :     krb5_error_code ret;
     406             : 
     407       46174 :     if (state && state->strengthen_key) {
     408       42504 :         _krb5_debug(context, 5, "_krb5_fast_tgs_strengthen_key");
     409             :         
     410       42504 :         if (state->strengthen_key->keytype != reply_key->keytype) {
     411           0 :             krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
     412           0 :                                    N_("strengthen_key %d not same enctype as reply key %d", ""),
     413           0 :                                    state->strengthen_key->keytype, reply_key->keytype);
     414           0 :             return KRB5KRB_AP_ERR_MODIFIED;
     415             :         }
     416             : 
     417       42504 :         ret = _krb5_fast_cf2(context,
     418             :                              state->strengthen_key,
     419             :                              "strengthenkey",
     420             :                              reply_key,
     421             :                              "replykey",
     422             :                              extract_key,
     423             :                              NULL);
     424       42504 :         if (ret)
     425           0 :             return ret;
     426             :     } else {
     427        3670 :         ret = krb5_copy_keyblock_contents(context, reply_key, extract_key);
     428        3670 :         if (ret)
     429           0 :             return ret;
     430             :     }
     431             : 
     432       44516 :     return 0;
     433             : }
     434             : 
     435             : /* DCE compatible decrypt proc */
     436             : static krb5_error_code KRB5_CALLCONV
     437       46174 : decrypt_tkt_with_subkey (krb5_context context,
     438             :                          krb5_keyblock *key,
     439             :                          krb5_key_usage usage,
     440             :                          krb5_const_pointer skey,
     441             :                          krb5_kdc_rep *dec_rep)
     442             : {
     443        1658 :     struct krb5_decrypt_tkt_with_subkey_state *state;
     444       46174 :     krb5_error_code ret = 0;
     445        1658 :     krb5_data data;
     446        1658 :     size_t size;
     447        1658 :     krb5_crypto crypto;
     448        1658 :     krb5_keyblock extract_key;
     449             : 
     450       46174 :     state = (struct krb5_decrypt_tkt_with_subkey_state *)skey;
     451             : 
     452       46174 :     assert(usage == 0);
     453             : 
     454       46174 :     krb5_data_zero(&data);
     455             : 
     456             :     /*
     457             :      * start out with trying with subkey if we have one
     458             :      */
     459       46174 :     if (state->subkey) {
     460       46174 :         ret = fast_tgs_strengthen_key(context, state->fast_state,
     461             :                                       state->subkey, &extract_key);
     462       46174 :         if (ret)
     463           0 :             return ret;
     464             : 
     465       46174 :         ret = krb5_crypto_init(context, &extract_key, 0, &crypto);
     466       46174 :         krb5_free_keyblock_contents(context, &extract_key);
     467       46174 :         if (ret)
     468           0 :             return ret;
     469       47832 :         ret = krb5_decrypt_EncryptedData (context,
     470             :                                           crypto,
     471             :                                           KRB5_KU_TGS_REP_ENC_PART_SUB_KEY,
     472       46174 :                                           &dec_rep->kdc_rep.enc_part,
     473             :                                           &data);
     474             :         /*
     475             :          * If the is Windows 2000 DC, we need to retry with key usage
     476             :          * 8 when doing ARCFOUR.
     477             :          */
     478       46174 :         if (ret && state->subkey->keytype == ETYPE_ARCFOUR_HMAC_MD5) {
     479           0 :             ret = krb5_decrypt_EncryptedData(context,
     480             :                                              crypto,
     481             :                                              8,
     482           0 :                                              &dec_rep->kdc_rep.enc_part,
     483             :                                              &data);
     484             :         }
     485       46174 :         krb5_crypto_destroy(context, crypto);
     486             :     }
     487       46174 :     if (state->subkey == NULL || ret) {
     488           0 :         ret = fast_tgs_strengthen_key(context, state->fast_state, key, &extract_key);
     489           0 :         if (ret)
     490           0 :             return ret;
     491             : 
     492           0 :         ret = krb5_crypto_init(context, key, 0, &crypto);
     493           0 :         if (ret)
     494           0 :             return ret;
     495           0 :         ret = krb5_decrypt_EncryptedData (context,
     496             :                                           crypto,
     497             :                                           KRB5_KU_TGS_REP_ENC_PART_SESSION,
     498           0 :                                           &dec_rep->kdc_rep.enc_part,
     499             :                                           &data);
     500           0 :         krb5_crypto_destroy(context, crypto);
     501             :     }
     502       46174 :     if (ret)
     503           0 :         return ret;
     504             : 
     505       47832 :     ret = decode_EncASRepPart(data.data,
     506             :                               data.length,
     507       46174 :                               &dec_rep->enc_part,
     508             :                               &size);
     509       46174 :     if (ret)
     510       46174 :         ret = decode_EncTGSRepPart(data.data,
     511             :                                    data.length,
     512       44516 :                                    &dec_rep->enc_part,
     513             :                                    &size);
     514       46174 :     if (ret)
     515           0 :       krb5_set_error_message(context, ret,
     516           0 :                              N_("Failed to decode encpart in ticket", ""));
     517       46174 :     krb5_data_free (&data);
     518       46174 :     return ret;
     519             : }
     520             : 
     521             : static krb5_error_code
     522       48002 : get_cred_kdc(krb5_context context,
     523             :              krb5_ccache id,
     524             :              struct krb5_fast_state *fast_state,
     525             :              krb5_kdc_flags flags,
     526             :              krb5_addresses *addresses,
     527             :              krb5_creds *in_creds,
     528             :              krb5_creds *krbtgt,
     529             :              krb5_principal impersonate_principal,
     530             :              Ticket *second_ticket,
     531             :              const char *kdc_hostname,
     532             :              const char *sitename,
     533             :              krb5_creds *out_creds)
     534             : {
     535        1658 :     TGS_REQ req;
     536        1658 :     krb5_data enc;
     537        1658 :     krb5_data resp;
     538        1658 :     krb5_kdc_rep rep;
     539        1658 :     krb5_error_code ret;
     540        1658 :     unsigned nonce;
     541       48002 :     krb5_keyblock *subkey = NULL;
     542       48002 :     size_t len = 0;
     543        1658 :     Ticket second_ticket_data;
     544        1658 :     METHOD_DATA padata;
     545             : 
     546       48002 :     memset(&rep, 0, sizeof(rep));
     547       48002 :     krb5_data_zero(&resp);
     548       48002 :     krb5_data_zero(&enc);
     549       48002 :     padata.val = NULL;
     550       48002 :     padata.len = 0;
     551             : 
     552       48002 :     krb5_generate_random_block(&nonce, sizeof(nonce));
     553       48002 :     nonce &= 0xffffffff;
     554             : 
     555       48002 :     if(flags.b.enc_tkt_in_skey && second_ticket == NULL){
     556           0 :         ret = decode_Ticket(in_creds->second_ticket.data,
     557             :                             in_creds->second_ticket.length,
     558             :                             &second_ticket_data, &len);
     559           0 :         if(ret)
     560           0 :             return ret;
     561           0 :         second_ticket = &second_ticket_data;
     562             :     }
     563             : 
     564             : 
     565       48002 :     if (impersonate_principal) {
     566           0 :         krb5_crypto crypto;
     567           0 :         PA_S4U2Self self;
     568           0 :         krb5_data data;
     569           0 :         void *buf;
     570         902 :         size_t size = 0;
     571             : 
     572         902 :         self.name = impersonate_principal->name;
     573         902 :         self.realm = impersonate_principal->realm;
     574         902 :         self.auth = estrdup("Kerberos");
     575             : 
     576         902 :         ret = _krb5_s4u2self_to_checksumdata(context, &self, &data);
     577         902 :         if (ret) {
     578           0 :             free(self.auth);
     579           0 :             goto out;
     580             :         }
     581             : 
     582         902 :         ret = krb5_crypto_init(context, &krbtgt->session, 0, &crypto);
     583         902 :         if (ret) {
     584           0 :             free(self.auth);
     585           0 :             krb5_data_free(&data);
     586           0 :             goto out;
     587             :         }
     588             : 
     589         902 :         ret = krb5_create_checksum(context,
     590             :                                    crypto,
     591             :                                    KRB5_KU_OTHER_CKSUM,
     592             :                                    0,
     593             :                                    data.data,
     594             :                                    data.length,
     595             :                                    &self.cksum);
     596         902 :         krb5_crypto_destroy(context, crypto);
     597         902 :         krb5_data_free(&data);
     598         902 :         if (ret) {
     599           0 :             free(self.auth);
     600           0 :             goto out;
     601             :         }
     602             : 
     603         902 :         ASN1_MALLOC_ENCODE(PA_S4U2Self, buf, len, &self, &size, ret);
     604         902 :         free(self.auth);
     605         902 :         free_Checksum(&self.cksum);
     606         902 :         if (ret)
     607           0 :             goto out;
     608         902 :         if (len != size)
     609           0 :             krb5_abortx(context, "internal asn1 error");
     610             : 
     611         902 :         ret = krb5_padata_add(context, &padata, KRB5_PADATA_FOR_USER, buf, len);
     612         902 :         if (ret)
     613           0 :             goto out;
     614             :     }
     615             : 
     616       48002 :     ret = init_tgs_req (context,
     617             :                         id,
     618             :                         fast_state,
     619             :                         addresses,
     620             :                         flags,
     621             :                         second_ticket,
     622             :                         in_creds,
     623             :                         krbtgt,
     624             :                         nonce,
     625             :                         &padata,
     626             :                         &subkey,
     627             :                         &req);
     628       48002 :     if (ret)
     629           0 :         goto out;
     630             : 
     631       48002 :     ASN1_MALLOC_ENCODE(TGS_REQ, enc.data, enc.length, &req, &len, ret);
     632       48002 :     if (ret)
     633           0 :         goto out;
     634       48002 :     if(enc.length != len)
     635           0 :         krb5_abortx(context, "internal error in ASN.1 encoder");
     636             : 
     637             :     /* don't free addresses */
     638       48002 :     req.req_body.addresses = NULL;
     639       48002 :     free_TGS_REQ(&req);
     640             : 
     641             :     /*
     642             :      * Send and receive
     643             :      */
     644             :     {
     645        1658 :         krb5_sendto_ctx stctx;
     646       48002 :         ret = krb5_sendto_ctx_alloc(context, &stctx);
     647       48002 :         if (ret)
     648           0 :             return ret;
     649       48002 :         krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
     650             : 
     651       48002 :         if (kdc_hostname)
     652           0 :             krb5_sendto_set_hostname(context, stctx, kdc_hostname);
     653       48002 :         if (sitename)
     654           0 :             krb5_sendto_set_sitename(context, stctx, sitename);
     655             : 
     656       49660 :         ret = krb5_sendto_context (context, stctx, &enc,
     657       48002 :                                    krbtgt->server->name.name_string.val[1],
     658             :                                    &resp);
     659       48002 :         krb5_sendto_ctx_free(context, stctx);
     660             :     }
     661       48002 :     if(ret)
     662         576 :         goto out;
     663             : 
     664       47426 :     if(decode_TGS_REP(resp.data, resp.length, &rep.kdc_rep, &len) == 0) {
     665        1658 :         struct krb5_decrypt_tkt_with_subkey_state state;
     666       46174 :         unsigned eflags = 0;
     667        1658 :         krb5_data data;
     668        1658 :         size_t size;
     669             : 
     670       46174 :         ASN1_MALLOC_ENCODE(Ticket, data.data, data.length,
     671             :                            &rep.kdc_rep.ticket, &size, ret);
     672       46174 :         if (ret)
     673           0 :             goto out;
     674       46174 :         heim_assert(data.length == size, "ASN.1 internal error");
     675             : 
     676       46174 :         ret = _krb5_fast_unwrap_kdc_rep(context, nonce, &data,
     677             :                                         fast_state, &rep.kdc_rep);
     678       46174 :         krb5_data_free(&data);
     679       46174 :         if (ret)
     680           0 :             goto out;
     681             : 
     682       47832 :         ret = krb5_copy_principal(context,
     683       46174 :                                   in_creds->client,
     684             :                                   &out_creds->client);
     685       46174 :         if(ret)
     686           0 :             goto out;
     687       47832 :         ret = krb5_copy_principal(context,
     688       46174 :                                   in_creds->server,
     689             :                                   &out_creds->server);
     690       46174 :         if(ret)
     691           0 :             goto out;
     692             :         /* this should go someplace else */
     693       46174 :         out_creds->times.endtime = in_creds->times.endtime;
     694             : 
     695             :         /* XXX should do better testing */
     696       46174 :         if (flags.b.cname_in_addl_tkt || impersonate_principal)
     697         630 :             eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
     698       46174 :         if (flags.b.request_anonymous)
     699           0 :             eflags |= EXTRACT_TICKET_MATCH_ANON;
     700             : 
     701       46174 :         state.subkey = subkey;
     702       46174 :         state.fast_state = fast_state;
     703             : 
     704       46174 :         ret = _krb5_extract_ticket(context,
     705             :                                    &rep,
     706             :                                    out_creds,
     707             :                                    &krbtgt->session,
     708             :                                    NULL,
     709             :                                    0,
     710             :                                    &krbtgt->addresses,
     711             :                                    nonce,
     712             :                                    eflags,
     713             :                                    NULL,
     714             :                                    decrypt_tkt_with_subkey,
     715             :                                    &state);
     716        1252 :     } else if(krb5_rd_error(context, &resp, &rep.error) == 0) {
     717           0 :         METHOD_DATA md;
     718             : 
     719        1252 :         memset(&md, 0, sizeof(md));
     720             : 
     721        1252 :         if (rep.error.e_data) {
     722           0 :             KERB_ERROR_DATA error_data;
     723             : 
     724        1226 :             memset(&error_data, 0, sizeof(error_data));
     725             : 
     726             :             /* First try to decode the e-data as KERB-ERROR-DATA. */
     727        1226 :             ret = decode_KERB_ERROR_DATA(rep.error.e_data->data,
     728        1226 :                                          rep.error.e_data->length,
     729             :                                          &error_data,
     730             :                                          &len);
     731        1226 :             if (ret) {
     732             :                 /* That failed, so try to decode it as METHOD-DATA. */
     733        1220 :                 ret = decode_METHOD_DATA(rep.error.e_data->data,
     734        1220 :                                          rep.error.e_data->length,
     735             :                                          &md, NULL);
     736        1220 :                 if (ret) {
     737           0 :                     krb5_set_error_message(context, ret,
     738           0 :                                            N_("Failed to decode METHOD-DATA", ""));
     739           0 :                     goto out;
     740             :                 }
     741           6 :             } else if (len != rep.error.e_data->length) {
     742             :                 /* Trailing data — just ignore the error. */
     743           0 :                 free_KERB_ERROR_DATA(&error_data);
     744             :             } else {
     745             :                 /* OK. */
     746           6 :                 free_KERB_ERROR_DATA(&error_data);
     747             :             }
     748             :         }
     749             : 
     750        1252 :         ret = _krb5_fast_unwrap_error(context, nonce, fast_state, &md, &rep.error);
     751        1252 :         free_METHOD_DATA(&md);
     752        1252 :         if (ret)
     753          32 :             goto out;
     754             : 
     755        1220 :         ret = krb5_error_from_rd_error(context, &rep.error, in_creds);
     756             : 
     757             :         /* log the failure */
     758        1220 :         if (_krb5_have_debug(context, 5)) {
     759           0 :             const char *str = krb5_get_error_message(context, ret);
     760           0 :             _krb5_debug(context, 5, "parse_tgs_rep: KRB-ERROR %d/%s", ret, str);
     761           0 :             krb5_free_error_message(context, str);
     762             :         }
     763           0 :     } else if(resp.length > 0 && ((char*)resp.data)[0] == 4) {
     764           0 :         ret = KRB5KRB_AP_ERR_V4_REPLY;
     765           0 :         krb5_clear_error_message(context);
     766             :     } else {
     767           0 :         ret = KRB5KRB_AP_ERR_MSG_TYPE;
     768           0 :         krb5_clear_error_message(context);
     769             :     }
     770             : 
     771       48002 : out:
     772       48002 :     krb5_free_kdc_rep(context, &rep);
     773       48002 :     if (second_ticket == &second_ticket_data)
     774           0 :         free_Ticket(&second_ticket_data);
     775       48002 :     free_METHOD_DATA(&padata);
     776       48002 :     krb5_data_free(&resp);
     777       48002 :     krb5_data_free(&enc);
     778       48002 :     if(subkey)
     779       48002 :         krb5_free_keyblock(context, subkey);
     780       46344 :     return ret;
     781             : 
     782             : }
     783             : 
     784             : /*
     785             :  * same as above, just get local addresses first if the krbtgt have
     786             :  * them and the realm is not addressless
     787             :  */
     788             : 
     789             : static krb5_error_code
     790       24948 : get_cred_kdc_address(krb5_context context,
     791             :                      krb5_ccache id,
     792             :                      struct krb5_fast_state *fast_state,
     793             :                      krb5_kdc_flags flags,
     794             :                      krb5_addresses *addrs,
     795             :                      krb5_creds *in_creds,
     796             :                      krb5_creds *krbtgt,
     797             :                      krb5_principal impersonate_principal,
     798             :                      Ticket *second_ticket,
     799             :                      const char *kdc_hostname,
     800             :                      const char *sitename,
     801             :                      krb5_creds *out_creds)
     802             : {
     803         623 :     krb5_error_code ret;
     804       24948 :     krb5_addresses addresses = { 0, NULL };
     805             : 
     806             :     /*
     807             :      * Inherit the address-ness of the krbtgt if the address is not
     808             :      * specified.
     809             :      */
     810             : 
     811       24948 :     if (addrs == NULL && krbtgt->addresses.len != 0) {
     812           0 :         krb5_boolean noaddr;
     813             : 
     814           0 :         krb5_appdefault_boolean(context, NULL, krbtgt->server->realm,
     815             :                                 "no-addresses", FALSE, &noaddr);
     816             : 
     817           0 :         if (!noaddr) {
     818           0 :             ret = krb5_get_all_client_addrs(context, &addresses);
     819           0 :             if (ret)
     820           0 :                 return ret;
     821             :             /* XXX this sucks. */
     822           0 :             addrs = &addresses;
     823           0 :             if(addresses.len == 0)
     824           0 :                 addrs = NULL;
     825             :         }
     826             :     }
     827       24948 :     ret = get_cred_kdc(context, id, fast_state, flags, addrs,
     828             :                        in_creds, krbtgt, impersonate_principal,
     829             :                        second_ticket, kdc_hostname, sitename, out_creds);
     830       24948 :     krb5_free_addresses(context, &addresses);
     831       24948 :     return ret;
     832             : }
     833             : 
     834             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
     835       23063 : krb5_get_kdc_cred(krb5_context context,
     836             :                   krb5_ccache id,
     837             :                   krb5_kdc_flags flags,
     838             :                   krb5_addresses *addresses,
     839             :                   Ticket  *second_ticket,
     840             :                   krb5_creds *in_creds,
     841             :                   krb5_creds **out_creds
     842             :                   )
     843             : {
     844        1035 :     krb5_error_code ret;
     845        1035 :     krb5_creds *krbtgt;
     846        1035 :     struct krb5_fast_state fast_state;
     847             : 
     848       23063 :     memset(&fast_state, 0, sizeof(fast_state));
     849             : 
     850       23063 :     *out_creds = calloc(1, sizeof(**out_creds));
     851       23063 :     if(*out_creds == NULL)
     852           0 :         return krb5_enomem(context);
     853       24098 :     ret = _krb5_get_krbtgt (context,
     854             :                             id,
     855       23063 :                             in_creds->server->realm,
     856             :                             &krbtgt);
     857       23063 :     if(ret) {
     858           9 :         free(*out_creds);
     859           9 :         *out_creds = NULL;
     860           9 :         return ret;
     861             :     }
     862       23054 :     ret = get_cred_kdc(context, id, &fast_state, flags,
     863             :                        addresses, in_creds, krbtgt,
     864             :                        NULL, NULL, NULL, NULL, *out_creds);
     865       23054 :     krb5_free_creds (context, krbtgt);
     866       23054 :     _krb5_fast_free(context, &fast_state);
     867       23054 :     if(ret) {
     868           2 :         free(*out_creds);
     869           2 :         *out_creds = NULL;
     870             :     }
     871       22019 :     return ret;
     872             : }
     873             : 
     874             : static int
     875        2966 : not_found(krb5_context context, krb5_const_principal p, krb5_error_code code)
     876             : {
     877           0 :     krb5_error_code ret;
     878           0 :     char *str;
     879           0 :     const char *err;
     880             : 
     881        2966 :     ret = krb5_unparse_name(context, p, &str);
     882        2966 :     if(ret) {
     883           0 :         krb5_clear_error_message(context);
     884           0 :         return code;
     885             :     }
     886        2966 :     err = krb5_get_error_message(context, code);
     887        2966 :     krb5_set_error_message(context, code, N_("%s (%s)", ""), err, str);
     888        2966 :     krb5_free_error_message(context, err);
     889        2966 :     free(str);
     890        2966 :     return code;
     891             : }
     892             : 
     893             : static krb5_error_code
     894       24438 : find_cred(krb5_context context,
     895             :           krb5_ccache id,
     896             :           krb5_principal server,
     897             :           krb5_creds **tgts,
     898             :           krb5_creds *out_creds)
     899             : {
     900         623 :     krb5_error_code ret;
     901         623 :     krb5_creds mcreds;
     902             : 
     903       24438 :     krb5_cc_clear_mcred(&mcreds);
     904       24438 :     mcreds.server = server;
     905       24438 :     krb5_timeofday(context, &mcreds.times.endtime);
     906       24438 :     ret = krb5_cc_retrieve_cred(context, id,
     907             :                                 KRB5_TC_DONT_MATCH_REALM |
     908             :                                 KRB5_TC_MATCH_TIMES,
     909             :                                 &mcreds, out_creds);
     910       24438 :     if(ret == 0)
     911       23667 :         return 0;
     912         148 :     while(tgts && *tgts){
     913           0 :         if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM,
     914             :                               &mcreds, *tgts)){
     915           0 :             ret = krb5_copy_creds_contents(context, *tgts, out_creds);
     916           0 :             return ret;
     917             :         }
     918           0 :         tgts++;
     919             :     }
     920         148 :     return not_found(context, server, KRB5_CC_NOTFOUND);
     921             : }
     922             : 
     923             : static krb5_error_code
     924        2098 : add_cred(krb5_context context, krb5_creds const *tkt, krb5_creds ***tgts)
     925             : {
     926           0 :     int i;
     927           0 :     krb5_error_code ret;
     928        2098 :     krb5_creds **tmp = *tgts;
     929             : 
     930        2098 :     for(i = 0; tmp && tmp[i]; i++); /* XXX */
     931        2098 :     tmp = realloc(tmp, (i+2)*sizeof(*tmp));
     932        2098 :     if(tmp == NULL)
     933           0 :         return krb5_enomem(context);
     934        2098 :     *tgts = tmp;
     935        2098 :     ret = krb5_copy_creds(context, tkt, &tmp[i]);
     936        2098 :     tmp[i+1] = NULL;
     937        2098 :     return ret;
     938             : }
     939             : 
     940             : static krb5_error_code
     941       21881 : get_cred_kdc_capath_worker(krb5_context context,
     942             :                            krb5_kdc_flags flags,
     943             :                            krb5_ccache ccache,
     944             :                            struct krb5_fast_state *fast_state,
     945             :                            krb5_creds *in_creds,
     946             :                            krb5_const_realm try_realm,
     947             :                            krb5_principal impersonate_principal,
     948             :                            Ticket *second_ticket,
     949             :                            const char *kdc_hostname,
     950             :                            const char *sitename,
     951             :                            krb5_creds **out_creds,
     952             :                            krb5_creds ***ret_tgts)
     953             : {
     954         623 :     krb5_error_code ret;
     955       21881 :     krb5_creds *tgt = NULL;
     956         623 :     krb5_creds tmp_creds;
     957         623 :     krb5_const_realm client_realm, server_realm;
     958       21881 :     int ok_as_delegate = 1;
     959             : 
     960       21881 :     *out_creds = calloc(1, sizeof(**out_creds));
     961       21881 :     if (*out_creds == NULL)
     962           0 :         return krb5_enomem(context);
     963             : 
     964       21881 :     memset(&tmp_creds, 0, sizeof(tmp_creds));
     965             : 
     966       21881 :     client_realm = krb5_principal_get_realm(context, in_creds->client);
     967       21881 :     server_realm = krb5_principal_get_realm(context, in_creds->server);
     968       21881 :     ret = krb5_copy_principal(context, in_creds->client, &tmp_creds.client);
     969       21881 :     if (ret)
     970           0 :         goto out;
     971             : 
     972       21881 :     ret = krb5_make_principal(context,
     973             :                               &tmp_creds.server,
     974             :                               try_realm,
     975             :                               KRB5_TGS_NAME,
     976             :                               server_realm,
     977             :                               NULL);
     978       21881 :     if (ret)
     979           0 :         goto out;
     980             : 
     981             :     {
     982         623 :         krb5_creds tgts;
     983             : 
     984             :         /*
     985             :          * If we have krbtgt/server_realm@try_realm cached, use it and we're
     986             :          * done.
     987             :          */
     988       21881 :         ret = find_cred(context, ccache, tmp_creds.server,
     989             :                         *ret_tgts, &tgts);
     990       21881 :         if (ret == 0) {
     991             :             /* only allow implicit ok_as_delegate if the realm is the clients realm */
     992       21761 :             if (strcmp(try_realm, client_realm) != 0
     993       21761 :                  || strcmp(try_realm, server_realm) != 0) {
     994          15 :                 ok_as_delegate = tgts.flags.b.ok_as_delegate;
     995             :             }
     996             : 
     997       21761 :             ret = get_cred_kdc_address(context, ccache, fast_state,
     998             :                                        flags, NULL,
     999             :                                        in_creds, &tgts,
    1000             :                                        impersonate_principal,
    1001             :                                        second_ticket,
    1002             :                                        kdc_hostname,
    1003             :                                        sitename,
    1004             :                                        *out_creds);
    1005       21761 :             krb5_free_cred_contents(context, &tgts);
    1006       42769 :             if (ret == 0 &&
    1007       21008 :                 !krb5_principal_compare(context, in_creds->server,
    1008       21008 :                                         (*out_creds)->server)) {
    1009        2016 :                 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
    1010             :             }
    1011       21761 :             if (ret == 0 && ok_as_delegate == 0)
    1012           3 :                 (*out_creds)->flags.b.ok_as_delegate = 0;
    1013             : 
    1014       21761 :             goto out;
    1015             :         }
    1016             :     }
    1017             : 
    1018         120 :     if (krb5_realm_compare(context, in_creds->client, in_creds->server)) {
    1019          28 :         ret = not_found(context, in_creds->server, KRB5_CC_NOTFOUND);
    1020          28 :         goto out;
    1021             :     }
    1022             : 
    1023             :     /*
    1024             :      * XXX This can loop forever, plus we recurse, so we can't just keep a
    1025             :      * count here.  The count would have to get passed around by reference.
    1026             :      *
    1027             :      * The KDCs check for transit loops for us, and capath data is finite, so
    1028             :      * in fact we'll fall out of this loop at some point.  We should do our own
    1029             :      * transit loop checking (like get_cred_kdc_referral()), and we should
    1030             :      * impose a max number of iterations altogether.  But barring malicious or
    1031             :      * broken KDCs, this is good enough.
    1032             :      */
    1033           0 :     while (1) {
    1034           0 :         heim_general_string tgt_inst;
    1035             : 
    1036          92 :         ret = get_cred_kdc_capath(context, flags, ccache, fast_state,
    1037             :                                   &tmp_creds, NULL, NULL,
    1038             :                                   kdc_hostname, sitename,
    1039             :                                   &tgt, ret_tgts);
    1040          92 :         if (ret)
    1041          10 :             goto out;
    1042             : 
    1043             :         /*
    1044             :          * if either of the chain or the ok_as_delegate was stripped
    1045             :          * by the kdc, make sure we strip it too.
    1046             :          */
    1047          82 :         if (ok_as_delegate == 0 || tgt->flags.b.ok_as_delegate == 0) {
    1048          82 :             ok_as_delegate = 0;
    1049          82 :             tgt->flags.b.ok_as_delegate = 0;
    1050             :         }
    1051             : 
    1052          82 :         ret = add_cred(context, tgt, ret_tgts);
    1053          82 :         if (ret)
    1054           0 :             goto out;
    1055          82 :         tgt_inst = tgt->server->name.name_string.val[1];
    1056          82 :         if (strcmp(tgt_inst, server_realm) == 0)
    1057          82 :             break;
    1058           0 :         krb5_free_principal(context, tmp_creds.server);
    1059           0 :         tmp_creds.server = NULL;
    1060           0 :         ret = krb5_make_principal(context, &tmp_creds.server,
    1061             :                                   tgt_inst, KRB5_TGS_NAME, server_realm, NULL);
    1062           0 :         if (ret)
    1063           0 :             goto out;
    1064           0 :         ret = krb5_free_creds(context, tgt);
    1065           0 :         tgt = NULL;
    1066           0 :         if (ret)
    1067           0 :             goto out;
    1068             :     }
    1069             : 
    1070          82 :     ret = get_cred_kdc_address(context, ccache, fast_state, flags, NULL,
    1071             :                                in_creds, tgt, impersonate_principal,
    1072             :                                second_ticket, kdc_hostname, sitename, *out_creds);
    1073         116 :     if (ret == 0 &&
    1074          34 :         !krb5_principal_compare(context, in_creds->server,
    1075          34 :                                     (*out_creds)->server)) {
    1076           0 :         krb5_free_cred_contents(context, *out_creds);
    1077           0 :         ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
    1078             :     }
    1079          82 :     if (ret == 0 && ok_as_delegate == 0)
    1080          34 :         (*out_creds)->flags.b.ok_as_delegate = 0;
    1081             : 
    1082          48 : out:
    1083       21881 :     if (ret) {
    1084        2855 :         krb5_free_creds(context, *out_creds);
    1085        2855 :         *out_creds = NULL;
    1086             :     }
    1087       21881 :     if (tmp_creds.server)
    1088       21881 :         krb5_free_principal(context, tmp_creds.server);
    1089       21881 :     if (tmp_creds.client)
    1090       21881 :         krb5_free_principal(context, tmp_creds.client);
    1091       21881 :     if (tgt)
    1092          82 :         krb5_free_creds(context, tgt);
    1093       21258 :     return ret;
    1094             : }
    1095             : 
    1096             : /*
    1097             : get_cred(server)
    1098             :         creds = cc_get_cred(server)
    1099             :         if(creds) return creds
    1100             :         tgt = cc_get_cred(krbtgt/server_realm@any_realm)
    1101             :         if(tgt)
    1102             :                 return get_cred_tgt(server, tgt)
    1103             :         if(client_realm == server_realm)
    1104             :                 return NULL
    1105             :         tgt = get_cred(krbtgt/server_realm@client_realm)
    1106             :         while(tgt_inst != server_realm)
    1107             :                 tgt = get_cred(krbtgt/server_realm@tgt_inst)
    1108             :         return get_cred_tgt(server, tgt)
    1109             :         */
    1110             : 
    1111             : static krb5_error_code
    1112       21881 : get_cred_kdc_capath(krb5_context context,
    1113             :                     krb5_kdc_flags flags,
    1114             :                     krb5_ccache ccache,
    1115             :                     struct krb5_fast_state *fast_state,
    1116             :                     krb5_creds *in_creds,
    1117             :                     krb5_principal impersonate_principal,
    1118             :                     Ticket *second_ticket,
    1119             :                     const char *kdc_hostname,
    1120             :                     const char *sitename,
    1121             :                     krb5_creds **out_creds,
    1122             :                     krb5_creds ***ret_tgts)
    1123             : {
    1124         623 :     krb5_error_code ret;
    1125         623 :     krb5_const_realm client_realm, server_realm, try_realm;
    1126             : 
    1127       21881 :     client_realm = krb5_principal_get_realm(context, in_creds->client);
    1128       21881 :     server_realm = krb5_principal_get_realm(context, in_creds->server);
    1129             : 
    1130       21881 :     try_realm = client_realm;
    1131       21881 :     ret = get_cred_kdc_capath_worker(context, flags, ccache, fast_state,
    1132             :                                      in_creds, try_realm, impersonate_principal,
    1133             :                                      second_ticket, kdc_hostname, sitename,
    1134             :                                      out_creds, ret_tgts);
    1135             : 
    1136       21881 :     if (ret == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) {
    1137        2797 :         try_realm = krb5_config_get_string(context, NULL, "capaths",
    1138             :                                            client_realm, server_realm, NULL);
    1139             : 
    1140        2797 :         if (try_realm != NULL && strcmp(try_realm, client_realm) != 0) {
    1141           0 :             ret = get_cred_kdc_capath_worker(context, flags, ccache, fast_state,
    1142             :                                              in_creds, try_realm, impersonate_principal,
    1143             :                                              second_ticket, kdc_hostname, sitename,
    1144             :                                              out_creds, ret_tgts);
    1145             :         }
    1146             :     }
    1147             : 
    1148       21881 :     return ret;
    1149             : }
    1150             : 
    1151        5402 : static krb5_boolean skip_referrals(krb5_principal server,
    1152             :                                    krb5_kdc_flags *flags)
    1153             : {
    1154        5402 :     return server->name.name_string.len < 2 && !flags->b.canonicalize;
    1155             : }
    1156             : 
    1157             : /*
    1158             :  * Get a service ticket from a KDC by chasing referrals from a start realm.
    1159             :  *
    1160             :  * All referral TGTs produced in the process are thrown away when we're done.
    1161             :  * We don't store them, and we don't allow other search mechanisms (capaths) to
    1162             :  * use referral TGTs produced here.
    1163             :  */
    1164             : static krb5_error_code
    1165        2557 : get_cred_kdc_referral(krb5_context context,
    1166             :                       krb5_kdc_flags flags,
    1167             :                       krb5_ccache ccache,
    1168             :                       struct krb5_fast_state *fast_state,
    1169             :                       krb5_creds *in_creds,
    1170             :                       krb5_principal impersonate_principal,
    1171             :                       Ticket *second_ticket,
    1172             :                       const char *kdc_hostname,
    1173             :                       const char *sitename,
    1174             :                       krb5_creds **out_creds)
    1175             : {
    1176        2557 :     krb5_realm start_realm = NULL;
    1177           0 :     krb5_data config_start_realm;
    1178           0 :     krb5_error_code ret;
    1179           0 :     krb5_creds tgt, referral, ticket;
    1180        2557 :     krb5_creds **referral_tgts = NULL;  /* used for loop detection */
    1181        2557 :     int loop = 0;
    1182        2557 :     int ok_as_delegate = 1;
    1183           0 :     int want_tgt;
    1184           0 :     size_t i;
    1185             : 
    1186        2557 :     if (skip_referrals(in_creds->server, &flags)) {
    1187           0 :         krb5_set_error_message(context, KRB5KDC_ERR_PATH_NOT_ACCEPTED,
    1188           0 :                                N_("Name too short to do referals, skipping", ""));
    1189           0 :         return KRB5KDC_ERR_PATH_NOT_ACCEPTED;
    1190             :     }
    1191             : 
    1192        2557 :     memset(&tgt, 0, sizeof(tgt));
    1193        2557 :     memset(&ticket, 0, sizeof(ticket));
    1194             : 
    1195        2557 :     flags.b.canonicalize = 1;
    1196             : 
    1197        2557 :     *out_creds = NULL;
    1198             : 
    1199             : 
    1200        2557 :     ret = krb5_cc_get_config(context, ccache, NULL, "start_realm", &config_start_realm);
    1201        2557 :     if (ret == 0) {
    1202        2529 :         start_realm = strndup(config_start_realm.data, config_start_realm.length);
    1203        2529 :         krb5_data_free(&config_start_realm);
    1204             :     } else {
    1205          28 :         start_realm = strdup(krb5_principal_get_realm(context, in_creds->client));
    1206             :     }
    1207        2557 :     if (start_realm == NULL)
    1208           0 :         return krb5_enomem(context);
    1209             : 
    1210             :     /* find tgt for the clients base realm */
    1211             :     {
    1212           0 :         krb5_principal tgtname;
    1213             : 
    1214        2557 :         ret = krb5_make_principal(context, &tgtname,
    1215             :                                   start_realm,
    1216             :                                   KRB5_TGS_NAME,
    1217             :                                   start_realm,
    1218             :                                   NULL);
    1219        2557 :         if (ret) {
    1220           0 :             free(start_realm);
    1221          28 :             return ret;
    1222             :         }
    1223             : 
    1224        2557 :         ret = find_cred(context, ccache, tgtname, NULL, &tgt);
    1225        2557 :         krb5_free_principal(context, tgtname);
    1226        2557 :         if (ret) {
    1227          28 :             free(start_realm);
    1228          28 :             return ret;
    1229             :         }
    1230             :     }
    1231             : 
    1232             :     /*
    1233             :      * If the desired service principal service/host@REALM is not a TGT, start
    1234             :      * by asking for a ticket for service/host@START_REALM and process referrals
    1235             :      * from there.
    1236             :      *
    1237             :      * However, when we ask for a TGT, krbtgt/A@B, we're actually looking for a
    1238             :      * path to realm B, so that we can explicitly obtain a ticket for krbtgt/A
    1239             :      * from B, and not some other realm.  Therefore, in this case our starting
    1240             :      * point will be krbtgt/B@START_REALM.  Only once we obtain a ticket for
    1241             :      * krbtgt/B@some-transit, do we switch to requesting krbtgt/A@B on our
    1242             :      * final request.
    1243             :      */
    1244        2529 :     referral = *in_creds;
    1245        5058 :     want_tgt = in_creds->server->realm[0] != '\0' &&
    1246        2529 :                krb5_principal_is_krbtgt(context, in_creds->server);
    1247        2529 :     if (!want_tgt)
    1248        1089 :         ret = krb5_copy_principal(context, in_creds->server, &referral.server);
    1249             :     else
    1250        1440 :         ret = krb5_make_principal(context, &referral.server, start_realm,
    1251        1440 :                                   KRB5_TGS_NAME, in_creds->server->realm, NULL);
    1252             : 
    1253        2529 :     if (ret) {
    1254           0 :         krb5_free_cred_contents(context, &tgt);
    1255           0 :         free(start_realm);
    1256           0 :         return ret;
    1257             :     }
    1258        2529 :     if (!want_tgt)
    1259        1089 :         ret = krb5_principal_set_realm(context, referral.server, start_realm);
    1260        2529 :     free(start_realm);
    1261        2529 :     start_realm = NULL;
    1262        2529 :     if (ret) {
    1263           0 :         krb5_free_cred_contents(context, &tgt);
    1264           0 :         krb5_free_principal(context, referral.server);
    1265           0 :         return ret;
    1266             :     }
    1267             : 
    1268        4545 :     while (loop++ < 17) {
    1269           0 :         krb5_creds **tickets;
    1270           0 :         krb5_creds mcreds;
    1271           0 :         char *referral_realm;
    1272             : 
    1273             :         /* Use cache if we are not doing impersonation or contrained deleg */
    1274        4545 :         if (impersonate_principal == NULL && !flags.b.cname_in_addl_tkt) {
    1275        4399 :             krb5_cc_clear_mcred(&mcreds);
    1276        4399 :             mcreds.server = referral.server;
    1277        4399 :             krb5_timeofday(context, &mcreds.times.endtime);
    1278        4399 :             ret = krb5_cc_retrieve_cred(context, ccache, KRB5_TC_MATCH_TIMES,
    1279             :                                         &mcreds, &ticket);
    1280             :         } else
    1281         146 :             ret = EINVAL;
    1282             : 
    1283        4545 :         if (ret) {
    1284        3105 :             ret = get_cred_kdc_address(context, ccache, fast_state, flags, NULL,
    1285             :                                        &referral, &tgt, impersonate_principal,
    1286             :                                        second_ticket, kdc_hostname, sitename, &ticket);
    1287        3105 :             if (ret)
    1288        2465 :                 goto out;
    1289             :         }
    1290             : 
    1291             :         /*
    1292             :          * Did we get the right ticket?
    1293             :          *
    1294             :          * If we weren't asking for a TGT, then we don't mind if we took a realm
    1295             :          * change (referral.server has a referral realm, not necessarily the
    1296             :          * original).
    1297             :          *
    1298             :          * However, if we were looking for a TGT (which wouldn't be the start
    1299             :          * TGT, since that one must be in the ccache) then we actually want the
    1300             :          * one from the realm we wanted, since otherwise a _referral_ will
    1301             :          * confuse us and we will store that referral.  In Heimdal we mostly
    1302             :          * never ask krb5_get_cred*() for TGTs, but some sites have code to ask
    1303             :          * for a ktbgt/REMOTE.REALM@REMOTE.REALM, and one could always use
    1304             :          * kgetcred(1) to get here asking for a krbtgt/C@D and we need to handle
    1305             :          * the case where last hop we get is krbtgt/C@B (in which case we must
    1306             :          * stop so we don't beat up on B for the remaining tries).
    1307             :          */
    1308        5600 :         if (!want_tgt &&
    1309        2080 :             krb5_principal_compare(context, referral.server, ticket.server))
    1310          64 :             break;
    1311             : 
    1312        3456 :         if (!krb5_principal_is_krbtgt(context, ticket.server)) {
    1313           0 :             krb5_set_error_message(context, KRB5KRB_AP_ERR_NOT_US,
    1314           0 :                                    N_("Got back an non krbtgt "
    1315             :                                       "ticket referrals", ""));
    1316           0 :             ret = KRB5KRB_AP_ERR_NOT_US;
    1317           0 :             goto out;
    1318             :         }
    1319             : 
    1320        3456 :         referral_realm = ticket.server->name.name_string.val[1];
    1321             : 
    1322             :         /* check that there are no referrals loops */
    1323        3456 :         tickets = referral_tgts;
    1324             : 
    1325        3456 :         krb5_cc_clear_mcred(&mcreds);
    1326        3456 :         mcreds.server = ticket.server;
    1327             : 
    1328        3456 :         while (tickets && *tickets){
    1329        1440 :             if (krb5_compare_creds(context,
    1330             :                                   KRB5_TC_DONT_MATCH_REALM,
    1331             :                                   &mcreds,
    1332             :                                   *tickets)) {
    1333        1440 :                 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
    1334        1440 :                                        N_("Referral from %s "
    1335             :                                           "loops back to realm %s", ""),
    1336        1440 :                                        tgt.server->realm,
    1337             :                                        referral_realm);
    1338        1440 :                 ret = KRB5_GET_IN_TKT_LOOP;
    1339        1440 :                 goto out;
    1340             :             }
    1341           0 :             tickets++;
    1342             :         }
    1343             : 
    1344             :         /*
    1345             :          * if either of the chain or the ok_as_delegate was stripped
    1346             :          * by the kdc, make sure we strip it too.
    1347             :          */
    1348             : 
    1349        2016 :         if (ok_as_delegate == 0 || ticket.flags.b.ok_as_delegate == 0) {
    1350        2016 :             ok_as_delegate = 0;
    1351        2016 :             ticket.flags.b.ok_as_delegate = 0;
    1352             :         }
    1353             : 
    1354        2016 :         _krb5_debug(context, 6, "get_cred_kdc_referral: got referral "
    1355        2016 :                     "to %s from %s", referral_realm, referral.server->realm);
    1356        2016 :         ret = add_cred(context, &ticket, &referral_tgts);
    1357        2016 :         if (ret)
    1358           0 :             goto out;
    1359             : 
    1360             :         /* try realm in the referral */
    1361        2016 :         if (!want_tgt || strcmp(referral_realm, in_creds->server->realm) != 0)
    1362         576 :             ret = krb5_principal_set_realm(context,
    1363             :                                            referral.server,
    1364             :                                            referral_realm);
    1365             :         else {
    1366             :             /*
    1367             :              * Now that we have a ticket for the desired realm, we reset
    1368             :              * want_tgt and reinstate the desired principal so that the we can
    1369             :              * match it and break out of the loop.
    1370             :              */
    1371        1440 :             want_tgt = 0;
    1372        1440 :             krb5_free_principal(context, referral.server);
    1373        1440 :             referral.server = NULL;
    1374        1440 :             ret = krb5_copy_principal(context, in_creds->server, &referral.server);
    1375             :         }
    1376        2016 :         krb5_free_cred_contents(context, &tgt);
    1377        2016 :         tgt = ticket;
    1378        2016 :         memset(&ticket, 0, sizeof(ticket));
    1379        2016 :         if (ret)
    1380           0 :             goto out;
    1381             :     }
    1382             : 
    1383          64 :     ret = krb5_copy_creds(context, &ticket, out_creds);
    1384             : 
    1385        2529 : out:
    1386        4545 :     for (i = 0; referral_tgts && referral_tgts[i]; i++)
    1387        2016 :         krb5_free_creds(context, referral_tgts[i]);
    1388        2529 :     free(referral_tgts);
    1389        2529 :     krb5_free_principal(context, referral.server);
    1390        2529 :     krb5_free_cred_contents(context, &tgt);
    1391        2529 :     krb5_free_cred_contents(context, &ticket);
    1392        2529 :     return ret;
    1393             : }
    1394             : 
    1395             : 
    1396             : /*
    1397             :  * Glue function between referrals version and old client chasing
    1398             :  * codebase.
    1399             :  */
    1400             : 
    1401             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1402       21789 : _krb5_get_cred_kdc_any(krb5_context context,
    1403             :                        krb5_kdc_flags flags,
    1404             :                        krb5_ccache ccache,
    1405             :                        struct krb5_fast_state *fast_state,
    1406             :                        krb5_creds *in_creds,
    1407             :                        krb5_principal impersonate_principal,
    1408             :                        Ticket *second_ticket,
    1409             :                        krb5_creds **out_creds,
    1410             :                        krb5_creds ***ret_tgts)
    1411             : {
    1412       21789 :     char *kdc_hostname = NULL;
    1413       21789 :     char *sitename = NULL;
    1414         623 :     krb5_error_code ret;
    1415         623 :     krb5_deltat offset;
    1416         623 :     krb5_data data;
    1417             : 
    1418       21789 :     krb5_data_zero(&data);
    1419             : 
    1420             :     /*
    1421             :      * If we are using LKDC, lets pull out the addreses from the
    1422             :      * ticket and use that.
    1423             :      */
    1424             :     
    1425       21789 :     ret = krb5_cc_get_config(context, ccache, NULL, "lkdc-hostname", &data);
    1426       21789 :     if (ret == 0) {
    1427           0 :         if ((kdc_hostname = strndup(data.data, data.length)) == NULL) {
    1428           0 :             ret = krb5_enomem(context);
    1429           0 :             goto out;
    1430             :         }
    1431           0 :         krb5_data_free(&data);
    1432             :     }
    1433             : 
    1434       21789 :     ret = krb5_cc_get_config(context, ccache, NULL, "sitename", &data);
    1435       21789 :     if (ret == 0) {
    1436           0 :         if ((sitename = strndup(data.data, data.length)) == NULL) {
    1437           0 :             ret = krb5_enomem(context);
    1438           0 :             goto out;
    1439             :         }
    1440           0 :         krb5_data_free(&data);
    1441             :     }
    1442             : 
    1443       21789 :     ret = krb5_cc_get_kdc_offset(context, ccache, &offset);
    1444       21789 :     if (ret == 0) {
    1445       21789 :         context->kdc_sec_offset = offset;
    1446       21789 :         context->kdc_usec_offset = 0;
    1447             :     }
    1448             : 
    1449       21789 :     if (strcmp(in_creds->server->realm, "") != 0) {
    1450             :         /*
    1451             :          * Non-empty realm?  Try capaths first.  We might have local
    1452             :          * policy (capaths) to honor.
    1453             :          */
    1454       21789 :         ret = get_cred_kdc_capath(context,
    1455             :                                   flags,
    1456             :                                   ccache,
    1457             :                                   fast_state,
    1458             :                                   in_creds,
    1459             :                                   impersonate_principal,
    1460             :                                   second_ticket,
    1461             :                                   kdc_hostname,
    1462             :                                   sitename,
    1463             :                                   out_creds,
    1464             :                                   ret_tgts);
    1465       21789 :         if (ret == 0 || skip_referrals(in_creds->server, &flags))
    1466       19232 :             goto out;
    1467             :     }
    1468             : 
    1469             :     /* Otherwise try referrals */
    1470        2557 :     ret = get_cred_kdc_referral(context,
    1471             :                                 flags,
    1472             :                                 ccache,
    1473             :                                 fast_state,
    1474             :                                 in_creds,
    1475             :                                 impersonate_principal,
    1476             :                                 second_ticket,
    1477             :                                 kdc_hostname,
    1478             :                                 sitename,
    1479             :                                 out_creds);
    1480             :     
    1481       21789 : out:
    1482       21789 :     krb5_data_free(&data);
    1483       21789 :     free(kdc_hostname);
    1484       21789 :     free(sitename);
    1485       21789 :     return ret;
    1486             : }
    1487             : 
    1488             : static krb5_error_code
    1489      105229 : check_cc(krb5_context context, krb5_flags options, krb5_ccache ccache,
    1490             :          krb5_creds *in_creds, krb5_creds *out_creds)
    1491             : {
    1492        4140 :     krb5_error_code ret;
    1493        4140 :     krb5_timestamp now;
    1494      105229 :     krb5_creds mcreds = *in_creds;
    1495             : 
    1496      105229 :     krb5_timeofday(context, &now);
    1497             : 
    1498      105229 :     if (!(options & KRB5_GC_EXPIRED_OK) &&
    1499      105205 :         mcreds.times.endtime < now) {
    1500      105155 :         mcreds.times.renew_till = 0;
    1501      105155 :         krb5_timeofday(context, &mcreds.times.endtime);
    1502      105155 :         options |= KRB5_TC_MATCH_TIMES;
    1503             :     }
    1504             : 
    1505      105229 :     if (mcreds.server->name.name_type == KRB5_NT_SRV_HST_NEEDS_CANON) {
    1506             :         /* Avoid name canonicalization in krb5_cc_retrieve_cred() */
    1507           0 :         krb5_principal_set_type(context, mcreds.server, KRB5_NT_SRV_HST);
    1508             :     }
    1509             : 
    1510      105229 :     if (options & KRB5_GC_ANONYMOUS) {
    1511           0 :         ret = krb5_make_principal(context,
    1512             :                                   &mcreds.client,
    1513           0 :                                   krb5_principal_get_realm(context, mcreds.client),
    1514             :                                   KRB5_WELLKNOWN_NAME,
    1515             :                                   KRB5_ANON_NAME,
    1516             :                                   NULL);
    1517           0 :         if (ret)
    1518           0 :             return ret;
    1519             :     }
    1520             : 
    1521      105229 :     ret = krb5_cc_retrieve_cred(context, ccache,
    1522             :                                 (options &
    1523             :                                  (KRB5_TC_DONT_MATCH_REALM |
    1524             :                                   KRB5_TC_MATCH_KEYTYPE |
    1525             :                                   KRB5_TC_MATCH_TIMES)),
    1526             :                                 &mcreds, out_creds);
    1527             : 
    1528      105229 :     if (options & KRB5_GC_ANONYMOUS)
    1529           0 :         krb5_free_principal(context, mcreds.client);
    1530             : 
    1531      105229 :     if (ret == 0 && out_creds->server->realm &&
    1532       83449 :         out_creds->server->realm[0] == '\0') {
    1533           0 :         Ticket ticket;
    1534             : 
    1535             :         /*
    1536             :          * We only write tickets to the ccache that have been validated, as in,
    1537             :          * the sname/srealm from the KDC-REP enc-part have been checked to
    1538             :          * match the sname/realm from the Ticket from the KDC-REP.
    1539             :          *
    1540             :          * Our caller needs the canonical realm of the service in order to be
    1541             :          * able to get forwarded credentials for it when destination-TGT
    1542             :          * forwarding is enabled.
    1543             :          *
    1544             :          * As well, gss_init_sec_context() ought to arrange for
    1545             :          * gss_inquire_context() to output the canonical acceptor name on the
    1546             :          * initiator side.
    1547             :          */
    1548           0 :         ret = decode_Ticket(out_creds->ticket.data, out_creds->ticket.length,
    1549             :                             &ticket, NULL);
    1550           0 :         if (ret == 0) {
    1551           0 :             ret = krb5_principal_set_realm(context, out_creds->server,
    1552           0 :                                            ticket.realm);
    1553           0 :             free_Ticket(&ticket);
    1554             :         } else {
    1555           0 :             krb5_free_cred_contents(context, out_creds);
    1556             :         }
    1557             :     }
    1558      101089 :     return ret;
    1559             : }
    1560             : 
    1561             : static void
    1562       17129 : store_cred(krb5_context context, krb5_ccache ccache,
    1563             :            krb5_const_principal server_princ, krb5_creds *creds)
    1564             : {
    1565       17129 :     if (context->no_ticket_store)
    1566           0 :         return;
    1567       17193 :     if (!krb5_principal_compare(context, creds->server, server_princ) &&
    1568          64 :         !krb5_principal_is_krbtgt(context, server_princ)) {
    1569          64 :         krb5_principal tmp_princ = creds->server;
    1570             :         /*
    1571             :          * Store the cred with the pre-canon server princ first so it
    1572             :          * can be found quickly in the future.
    1573             :          */
    1574          64 :         creds->server = (krb5_principal)server_princ;
    1575          64 :         krb5_cc_store_cred(context, ccache, creds);
    1576          64 :         creds->server = tmp_princ;
    1577             :         /* Then store again with the canonicalized server princ */
    1578             :     }
    1579       17129 :     krb5_cc_store_cred(context, ccache, creds);
    1580             : }
    1581             : 
    1582             : 
    1583             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1584      100868 : krb5_get_credentials_with_flags(krb5_context context,
    1585             :                                 krb5_flags options,
    1586             :                                 krb5_kdc_flags flags,
    1587             :                                 krb5_ccache ccache,
    1588             :                                 krb5_creds *in_creds,
    1589             :                                 krb5_creds **out_creds)
    1590             : {
    1591        4140 :     struct krb5_fast_state fast_state;
    1592        4140 :     krb5_error_code ret;
    1593      100868 :     krb5_name_canon_iterator name_canon_iter = NULL;
    1594        4140 :     krb5_name_canon_rule_options rule_opts;
    1595      100868 :     krb5_const_principal try_princ = NULL;
    1596      100868 :     krb5_principal save_princ = in_creds->server;
    1597        4140 :     krb5_creds **tgts;
    1598        4140 :     krb5_creds *res_creds;
    1599        4140 :     int i;
    1600             : 
    1601      100868 :     memset(&fast_state, 0, sizeof(fast_state));
    1602             : 
    1603      100868 :     if (_krb5_have_debug(context, 5)) {
    1604           0 :         char *unparsed;
    1605             : 
    1606           0 :         ret = krb5_unparse_name(context, in_creds->server, &unparsed);
    1607           0 :         if (ret) {
    1608           0 :             _krb5_debug(context, 5, "krb5_get_creds: unable to display "
    1609             :                         "requested service principal");
    1610             :         } else {
    1611           0 :             _krb5_debug(context, 5, "krb5_get_creds: requesting a ticket "
    1612             :                         "for %s", unparsed);
    1613           0 :             free(unparsed);
    1614             :         }
    1615             :     }
    1616             : 
    1617      100868 :     if (in_creds->session.keytype) {
    1618           0 :         ret = krb5_enctype_valid(context, in_creds->session.keytype);
    1619           0 :         if (ret)
    1620           0 :             return ret;
    1621           0 :         options |= KRB5_TC_MATCH_KEYTYPE;
    1622             :     }
    1623             : 
    1624      100868 :     *out_creds = NULL;
    1625      100868 :     res_creds = calloc(1, sizeof(*res_creds));
    1626      100868 :     if (res_creds == NULL)
    1627           0 :         return krb5_enomem(context);
    1628             : 
    1629      100868 :     ret = krb5_name_canon_iterator_start(context, in_creds->server,
    1630             :                                          &name_canon_iter);
    1631      100868 :     if (ret)
    1632           0 :         goto out;
    1633             : 
    1634      100868 : next_rule:
    1635      101174 :     krb5_free_cred_contents(context, res_creds);
    1636      101174 :     memset(res_creds, 0, sizeof (*res_creds));
    1637      101174 :     ret = krb5_name_canon_iterate(context, &name_canon_iter, &try_princ,
    1638             :                                   &rule_opts);
    1639      101174 :     in_creds->server = rk_UNCONST(try_princ);
    1640      101174 :     if (ret)
    1641           0 :         goto out;
    1642             : 
    1643      101174 :     if (name_canon_iter == NULL) {
    1644         306 :         if (options & KRB5_GC_CACHED)
    1645           9 :             ret = KRB5_CC_NOTFOUND;
    1646             :         else
    1647         297 :             ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
    1648         306 :         goto out;
    1649             :     }
    1650             : 
    1651      100868 :     ret = check_cc(context, options, ccache, in_creds, res_creds);
    1652      100868 :     if (ret == 0) {
    1653       83449 :         *out_creds = res_creds;
    1654       83449 :         res_creds = NULL;
    1655       83449 :         goto out;
    1656       17419 :     } else if(ret != KRB5_CC_END) {
    1657           0 :         goto out;
    1658             :     }
    1659       17419 :     if (options & KRB5_GC_CACHED)
    1660           9 :         goto next_rule;
    1661             : 
    1662       17410 :     if(options & KRB5_GC_USER_USER)
    1663           0 :         flags.b.enc_tkt_in_skey = 1;
    1664       17410 :     if (flags.b.enc_tkt_in_skey)
    1665           0 :         options |= KRB5_GC_NO_STORE;
    1666             : 
    1667       17410 :     tgts = NULL;
    1668       17410 :     ret = _krb5_get_cred_kdc_any(context, flags, ccache, &fast_state,
    1669             :                                  in_creds, NULL, NULL, out_creds, &tgts);
    1670       18114 :     for (i = 0; tgts && tgts[i]; i++) {
    1671          81 :         if ((options & KRB5_GC_NO_STORE) == 0)
    1672          81 :             krb5_cc_store_cred(context, ccache, tgts[i]);
    1673          81 :         krb5_free_creds(context, tgts[i]);
    1674             :     }
    1675       17410 :     free(tgts);
    1676             : 
    1677             :     /* We don't yet have TGS w/ FAST, so we can't protect KBR-ERRORs */
    1678       17410 :     if (ret == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN &&
    1679         297 :         !(rule_opts & KRB5_NCRO_USE_FAST))
    1680         297 :         goto next_rule;
    1681             : 
    1682       17113 :     if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
    1683       17079 :         store_cred(context, ccache, in_creds->server, *out_creds);
    1684             : 
    1685       17113 :     if (ret == 0 && _krb5_have_debug(context, 5)) {
    1686           0 :         char *unparsed;
    1687             : 
    1688           0 :         ret = krb5_unparse_name(context, (*out_creds)->server, &unparsed);
    1689           0 :         if (ret) {
    1690           0 :             _krb5_debug(context, 5, "krb5_get_creds: unable to display "
    1691             :                         "service principal");
    1692             :         } else {
    1693           0 :             _krb5_debug(context, 5, "krb5_get_creds: got a ticket for %s",
    1694             :                         unparsed);
    1695           0 :             free(unparsed);
    1696             :         }
    1697             :     }
    1698             : 
    1699       17113 : out:
    1700      100868 :     in_creds->server = save_princ;
    1701      100868 :     krb5_free_creds(context, res_creds);
    1702      100868 :     krb5_free_name_canon_iterator(context, name_canon_iter);
    1703      100868 :     _krb5_fast_free(context, &fast_state);
    1704      100868 :     if (ret)
    1705         340 :         return not_found(context, in_creds->server, ret);
    1706       96388 :     return 0;
    1707             : }
    1708             : 
    1709             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1710      100868 : krb5_get_credentials(krb5_context context,
    1711             :                      krb5_flags options,
    1712             :                      krb5_ccache ccache,
    1713             :                      krb5_creds *in_creds,
    1714             :                      krb5_creds **out_creds)
    1715             : {
    1716        4140 :     krb5_kdc_flags flags;
    1717      100868 :     flags.i = 0;
    1718      100868 :     return krb5_get_credentials_with_flags(context, options, flags,
    1719             :                                            ccache, in_creds, out_creds);
    1720             : }
    1721             : 
    1722             : struct krb5_get_creds_opt_data {
    1723             :     krb5_principal self;
    1724             :     krb5_flags options;
    1725             :     krb5_enctype enctype;
    1726             :     Ticket *ticket;
    1727             : };
    1728             : 
    1729             : 
    1730             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1731        3227 : krb5_get_creds_opt_alloc(krb5_context context, krb5_get_creds_opt *opt)
    1732             : {
    1733        3227 :     *opt = calloc(1, sizeof(**opt));
    1734        3227 :     if (*opt == NULL)
    1735           0 :         return krb5_enomem(context);
    1736        3227 :     return 0;
    1737             : }
    1738             : 
    1739             : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
    1740         345 : krb5_get_creds_opt_free(krb5_context context, krb5_get_creds_opt opt)
    1741             : {
    1742         345 :     if (opt->self)
    1743          38 :         krb5_free_principal(context, opt->self);
    1744         345 :     if (opt->ticket) {
    1745          16 :         free_Ticket(opt->ticket);
    1746          16 :         free(opt->ticket);
    1747             :     }
    1748         345 :     memset(opt, 0, sizeof(*opt));
    1749         345 :     free(opt);
    1750         345 : }
    1751             : 
    1752             : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
    1753          36 : krb5_get_creds_opt_set_options(krb5_context context,
    1754             :                                krb5_get_creds_opt opt,
    1755             :                                krb5_flags options)
    1756             : {
    1757          36 :     opt->options = options;
    1758          36 : }
    1759             : 
    1760             : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
    1761        6216 : krb5_get_creds_opt_add_options(krb5_context context,
    1762             :                                krb5_get_creds_opt opt,
    1763             :                                krb5_flags options)
    1764             : {
    1765        6216 :     opt->options |= options;
    1766        6216 : }
    1767             : 
    1768             : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
    1769           0 : krb5_get_creds_opt_set_enctype(krb5_context context,
    1770             :                                krb5_get_creds_opt opt,
    1771             :                                krb5_enctype enctype)
    1772             : {
    1773           0 :     opt->enctype = enctype;
    1774           0 : }
    1775             : 
    1776             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1777         758 : krb5_get_creds_opt_set_impersonate(krb5_context context,
    1778             :                                    krb5_get_creds_opt opt,
    1779             :                                    krb5_const_principal self)
    1780             : {
    1781         758 :     if (opt->self)
    1782           0 :         krb5_free_principal(context, opt->self);
    1783         758 :     return krb5_copy_principal(context, self, &opt->self);
    1784             : }
    1785             : 
    1786             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1787          18 : krb5_get_creds_opt_set_ticket(krb5_context context,
    1788             :                               krb5_get_creds_opt opt,
    1789             :                               const Ticket *ticket)
    1790             : {
    1791          18 :     if (opt->ticket) {
    1792           0 :         free_Ticket(opt->ticket);
    1793           0 :         free(opt->ticket);
    1794           0 :         opt->ticket = NULL;
    1795             :     }
    1796          18 :     if (ticket) {
    1797           0 :         krb5_error_code ret;
    1798             : 
    1799          18 :         opt->ticket = malloc(sizeof(*ticket));
    1800          18 :         if (opt->ticket == NULL)
    1801           0 :             return krb5_enomem(context);
    1802          18 :         ret = copy_Ticket(ticket, opt->ticket);
    1803          18 :         if (ret) {
    1804           0 :             free(opt->ticket);
    1805           0 :             opt->ticket = NULL;
    1806           0 :             krb5_set_error_message(context, ret,
    1807           0 :                                    N_("malloc: out of memory", ""));
    1808           0 :             return ret;
    1809             :         }
    1810             :     }
    1811          18 :     return 0;
    1812             : }
    1813             : 
    1814             : 
    1815             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1816        4379 : krb5_get_creds(krb5_context context,
    1817             :                krb5_get_creds_opt opt,
    1818             :                krb5_ccache ccache,
    1819             :                krb5_const_principal inprinc,
    1820             :                krb5_creds **out_creds)
    1821             : {
    1822           0 :     struct krb5_fast_state fast_state;
    1823           0 :     krb5_kdc_flags flags;
    1824           0 :     krb5_flags options;
    1825           0 :     krb5_creds in_creds;
    1826           0 :     krb5_error_code ret;
    1827           0 :     krb5_creds **tgts;
    1828           0 :     krb5_creds *res_creds;
    1829        4379 :     krb5_const_principal try_princ = NULL;
    1830        4379 :     krb5_name_canon_iterator name_canon_iter = NULL;
    1831           0 :     krb5_name_canon_rule_options rule_opts;
    1832           0 :     int i;
    1833           0 :     int type;
    1834           0 :     const char *comp;
    1835             : 
    1836        4379 :     memset(&fast_state, 0, sizeof(fast_state));
    1837        4379 :     memset(&in_creds, 0, sizeof(in_creds));
    1838        4379 :     in_creds.server = rk_UNCONST(inprinc);
    1839             : 
    1840        4379 :     if (_krb5_have_debug(context, 5)) {
    1841           0 :         char *unparsed;
    1842             : 
    1843           0 :         ret = krb5_unparse_name(context, in_creds.server, &unparsed);
    1844           0 :         if (ret) {
    1845           0 :             _krb5_debug(context, 5, "krb5_get_creds: unable to display "
    1846             :                         "requested service principal");
    1847             :         } else {
    1848           0 :             _krb5_debug(context, 5, "krb5_get_creds: requesting a ticket "
    1849             :                         "for %s", unparsed);
    1850           0 :             free(unparsed);
    1851             :         }
    1852             :     }
    1853             : 
    1854        4379 :     if (opt && opt->enctype) {
    1855           0 :         ret = krb5_enctype_valid(context, opt->enctype);
    1856           0 :         if (ret)
    1857           0 :             return ret;
    1858             :     }
    1859             : 
    1860        4379 :     ret = krb5_cc_get_principal(context, ccache, &in_creds.client);
    1861        4379 :     if (ret)
    1862           0 :         return ret;
    1863             : 
    1864        4379 :     if (opt)
    1865        4379 :         options = opt->options;
    1866             :     else
    1867           0 :         options = 0;
    1868        4379 :     flags.i = 0;
    1869             : 
    1870        4379 :     *out_creds = NULL;
    1871        4379 :     res_creds = calloc(1, sizeof(*res_creds));
    1872        4379 :     if (res_creds == NULL) {
    1873           0 :         krb5_free_principal(context, in_creds.client);
    1874           0 :         return krb5_enomem(context);
    1875             :     }
    1876             : 
    1877        4379 :     if (opt && opt->enctype) {
    1878           0 :         in_creds.session.keytype = opt->enctype;
    1879           0 :         options |= KRB5_TC_MATCH_KEYTYPE;
    1880             :     }
    1881             : 
    1882        4379 :     ret = krb5_name_canon_iterator_start(context, in_creds.server,
    1883             :                                          &name_canon_iter);
    1884        4379 :     if (ret)
    1885           0 :         goto out;
    1886             : 
    1887        4379 : next_rule:
    1888        4811 :     ret = krb5_name_canon_iterate(context, &name_canon_iter, &try_princ,
    1889             :                                   &rule_opts);
    1890        4811 :     in_creds.server = rk_UNCONST(try_princ);
    1891        4811 :     if (ret)
    1892           0 :         goto out;
    1893             : 
    1894        4811 :     if (name_canon_iter == NULL) {
    1895         432 :         if (options & KRB5_GC_CACHED)
    1896           0 :             ret = KRB5_CC_NOTFOUND;
    1897             :         else
    1898         432 :             ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
    1899         432 :         goto out;
    1900             :     }
    1901             : 
    1902        4379 :     if ((options & KRB5_GC_CONSTRAINED_DELEGATION) == 0) {
    1903        4361 :         ret = check_cc(context, options, ccache, &in_creds, res_creds);
    1904        4361 :         if (ret == 0) {
    1905           0 :             *out_creds = res_creds;
    1906           0 :             res_creds = NULL;
    1907           0 :             goto out;
    1908        4361 :         } else if (ret != KRB5_CC_END) {
    1909           0 :             goto out;
    1910             :         }
    1911             :     }
    1912        4379 :     if (options & KRB5_GC_CACHED)
    1913           0 :         goto next_rule;
    1914             : 
    1915        4379 :     type = krb5_principal_get_type(context, try_princ);
    1916        4379 :     comp = krb5_principal_get_comp_string(context, try_princ, 0);
    1917        4379 :     if ((type == KRB5_NT_SRV_HST || type == KRB5_NT_UNKNOWN) &&
    1918          94 :         comp != NULL && strcmp(comp, "host") == 0)
    1919          30 :         flags.b.canonicalize = 1;
    1920        4379 :     if (rule_opts & KRB5_NCRO_NO_REFERRALS)
    1921           0 :         flags.b.canonicalize = 0;
    1922             :     else
    1923        4379 :         flags.b.canonicalize = (options & KRB5_GC_CANONICALIZE) ? 1 : 0;
    1924        4379 :     if (options & KRB5_GC_USER_USER) {
    1925           0 :         flags.b.enc_tkt_in_skey = 1;
    1926           0 :         options |= KRB5_GC_NO_STORE;
    1927             :     }
    1928        4379 :     if (options & KRB5_GC_FORWARDABLE)
    1929          18 :         flags.b.forwardable = 1;
    1930        4379 :     if (options & KRB5_GC_NO_TRANSIT_CHECK)
    1931           0 :         flags.b.disable_transited_check = 1;
    1932        4379 :     if (options & KRB5_GC_CONSTRAINED_DELEGATION)
    1933          18 :         flags.b.cname_in_addl_tkt = 1;
    1934        4379 :     if (options & KRB5_GC_ANONYMOUS)
    1935           0 :         flags.b.request_anonymous = 1;
    1936             : 
    1937        4379 :     tgts = NULL;
    1938        4379 :     ret = _krb5_get_cred_kdc_any(context, flags, ccache, &fast_state,
    1939             :                                  &in_creds, opt ? opt->self : 0,
    1940             :                                  opt ? opt->ticket : 0, out_creds,
    1941             :                                  &tgts);
    1942        4380 :     for (i = 0; tgts && tgts[i]; i++) {
    1943           1 :         if ((options & KRB5_GC_NO_STORE) == 0)
    1944           1 :             krb5_cc_store_cred(context, ccache, tgts[i]);
    1945           1 :         krb5_free_creds(context, tgts[i]);
    1946             :     }
    1947        4379 :     free(tgts);
    1948             : 
    1949             :     /* We don't yet have TGS w/ FAST, so we can't protect KBR-ERRORs */
    1950        4379 :     if (ret == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN &&
    1951         432 :         !(rule_opts & KRB5_NCRO_USE_FAST))
    1952         432 :         goto next_rule;
    1953             : 
    1954        3947 :     if (ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
    1955          50 :         store_cred(context, ccache, inprinc, *out_creds);
    1956             : 
    1957        3947 :     if (ret == 0 && _krb5_have_debug(context, 5)) {
    1958           0 :         char *unparsed;
    1959             : 
    1960           0 :         ret = krb5_unparse_name(context, (*out_creds)->server, &unparsed);
    1961           0 :         if (ret) {
    1962           0 :             _krb5_debug(context, 5, "krb5_get_creds: unable to display "
    1963             :                         "service principal");
    1964             :         } else {
    1965           0 :             _krb5_debug(context, 5, "krb5_get_creds: got a ticket for %s",
    1966             :                         unparsed);
    1967           0 :             free(unparsed);
    1968             :         }
    1969             :     }
    1970             : 
    1971        3947 : out:
    1972        4379 :     _krb5_fast_free(context, &fast_state);
    1973        4379 :     krb5_free_creds(context, res_creds);
    1974        4379 :     krb5_free_principal(context, in_creds.client);
    1975        4379 :     krb5_free_name_canon_iterator(context, name_canon_iter);
    1976        4379 :     if (ret)
    1977        2450 :         return not_found(context, inprinc, ret);
    1978        1929 :     return ret;
    1979             : }
    1980             : 
    1981             : /*
    1982             :  *
    1983             :  */
    1984             : 
    1985             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    1986           0 : krb5_get_renewed_creds(krb5_context context,
    1987             :                        krb5_creds *creds,
    1988             :                        krb5_const_principal client,
    1989             :                        krb5_ccache ccache,
    1990             :                        const char *in_tkt_service)
    1991             : {
    1992           0 :     krb5_error_code ret;
    1993           0 :     krb5_kdc_flags flags;
    1994           0 :     krb5_creds in, *template, *out = NULL;
    1995             : 
    1996           0 :     memset(&in, 0, sizeof(in));
    1997           0 :     memset(creds, 0, sizeof(*creds));
    1998             : 
    1999           0 :     ret = krb5_copy_principal(context, client, &in.client);
    2000           0 :     if (ret)
    2001           0 :         return ret;
    2002             : 
    2003           0 :     if (in_tkt_service) {
    2004           0 :         ret = krb5_parse_name(context, in_tkt_service, &in.server);
    2005           0 :         if (ret) {
    2006           0 :             krb5_free_principal(context, in.client);
    2007           0 :             return ret;
    2008             :         }
    2009             :     } else {
    2010           0 :         const char *realm = krb5_principal_get_realm(context, client);
    2011             : 
    2012           0 :         ret = krb5_make_principal(context, &in.server, realm, KRB5_TGS_NAME,
    2013             :                                   realm, NULL);
    2014           0 :         if (ret) {
    2015           0 :             krb5_free_principal(context, in.client);
    2016           0 :             return ret;
    2017             :         }
    2018             :     }
    2019             : 
    2020           0 :     flags.i = 0;
    2021           0 :     flags.b.renewable = flags.b.renew = 1;
    2022             : 
    2023             :     /*
    2024             :      * Get template from old credential cache for the same entry, if
    2025             :      * this failes, no worries.
    2026             :      */
    2027           0 :     ret = krb5_get_credentials(context, KRB5_GC_CACHED, ccache, &in, &template);
    2028           0 :     if (ret == 0) {
    2029           0 :         flags.b.forwardable = template->flags.b.forwardable;
    2030           0 :         flags.b.proxiable = template->flags.b.proxiable;
    2031           0 :         krb5_free_creds (context, template);
    2032             :     }
    2033             : 
    2034           0 :     ret = krb5_get_kdc_cred(context, ccache, flags, NULL, NULL, &in, &out);
    2035           0 :     krb5_free_principal(context, in.client);
    2036           0 :     krb5_free_principal(context, in.server);
    2037           0 :     if (ret)
    2038           0 :         return ret;
    2039             : 
    2040           0 :     ret = krb5_copy_creds_contents(context, out, creds);
    2041           0 :     krb5_free_creds(context, out);
    2042             : 
    2043           0 :     return ret;
    2044             : }

Generated by: LCOV version 1.14