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

Generated by: LCOV version 1.13