LCOV - code coverage report
Current view: top level - third_party/heimdal/lib/krb5 - fast.c (source / functions) Hit Total Coverage
Test: coverage report for master 98b443d9 Lines: 337 510 66.1 %
Date: 2024-05-31 13:13:24 Functions: 14 15 93.3 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2011 Kungliga Tekniska Högskolan
       3             :  * (Royal Institute of Technology, Stockholm, Sweden).
       4             :  * All rights reserved.
       5             :  *
       6             :  * Redistribution and use in source and binary forms, with or without
       7             :  * modification, are permitted provided that the following conditions
       8             :  * are met:
       9             :  *
      10             :  * 1. Redistributions of source code must retain the above copyright
      11             :  *    notice, this list of conditions and the following disclaimer.
      12             :  *
      13             :  * 2. Redistributions in binary form must reproduce the above copyright
      14             :  *    notice, this list of conditions and the following disclaimer in the
      15             :  *    documentation and/or other materials provided with the distribution.
      16             :  *
      17             :  * 3. Neither the name of the Institute nor the names of its contributors
      18             :  *    may be used to endorse or promote products derived from this software
      19             :  *    without specific prior written permission.
      20             :  *
      21             :  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
      22             :  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      23             :  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
      24             :  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
      25             :  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
      26             :  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
      27             :  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      28             :  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
      29             :  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
      30             :  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
      31             :  * SUCH DAMAGE.
      32             :  */
      33             : 
      34             : #include "krb5_locl.h"
      35             : #ifndef WIN32
      36             : #include <heim-ipc.h>
      37             : #endif
      38             : 
      39             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
      40      180253 : _krb5_fast_cf2(krb5_context context,
      41             :                krb5_keyblock *key1,
      42             :                const char *pepper1,
      43             :                krb5_keyblock *key2,
      44             :                const char *pepper2,
      45             :                krb5_keyblock *armorkey,
      46             :                krb5_crypto *armor_crypto)
      47             : {
      48        6688 :     krb5_crypto crypto1, crypto2;
      49        6688 :     krb5_data pa1, pa2;
      50        6688 :     krb5_error_code ret;
      51             : 
      52      180253 :     ret = krb5_crypto_init(context, key1, 0, &crypto1);
      53      180253 :     if (ret)
      54           0 :         return ret;
      55             : 
      56      180253 :     ret = krb5_crypto_init(context, key2, 0, &crypto2);
      57      180253 :     if (ret) {
      58           0 :         krb5_crypto_destroy(context, crypto1);
      59           0 :         return ret;
      60             :     }
      61             : 
      62      180253 :     pa1.data = rk_UNCONST(pepper1);
      63      180253 :     pa1.length = strlen(pepper1);
      64      180253 :     pa2.data = rk_UNCONST(pepper2);
      65      180253 :     pa2.length = strlen(pepper2);
      66             : 
      67      186941 :     ret = krb5_crypto_fx_cf2(context, crypto1, crypto2, &pa1, &pa2,
      68      180253 :                              key1->keytype, armorkey);
      69      180253 :     krb5_crypto_destroy(context, crypto1);
      70      180253 :     krb5_crypto_destroy(context, crypto2);
      71      180253 :     if (ret)
      72           0 :         return ret;
      73             : 
      74      180253 :     if (armor_crypto) {
      75       92861 :         ret = krb5_crypto_init(context, armorkey, 0, armor_crypto);
      76       92861 :         if (ret)
      77           0 :             krb5_free_keyblock_contents(context, armorkey);
      78             :     }
      79             : 
      80      173565 :     return ret;
      81             : }
      82             : 
      83             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
      84       92861 : _krb5_fast_armor_key(krb5_context context,
      85             :                      krb5_keyblock *subkey,
      86             :                      krb5_keyblock *sessionkey,
      87             :                      krb5_keyblock *armorkey,
      88             :                      krb5_crypto *armor_crypto)
      89             : {
      90       92861 :     return _krb5_fast_cf2(context,
      91             :                           subkey,
      92             :                           "subkeyarmor",
      93             :                           sessionkey,
      94             :                           "ticketarmor",
      95             :                           armorkey,
      96             :                           armor_crypto);
      97             : }
      98             : 
      99             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
     100         324 : _krb5_fast_explicit_armor_key(krb5_context context,
     101             :                               krb5_keyblock *armorkey,
     102             :                               krb5_keyblock *subkey,
     103             :                               krb5_keyblock *explicit_armorkey,
     104             :                               krb5_crypto *explicit_armor_crypto)
     105             : {
     106         324 :     return _krb5_fast_cf2(context,
     107             :                           armorkey,
     108             :                           "explicitarmor",
     109             :                           subkey,
     110             :                           "tgsarmor",
     111             :                           explicit_armorkey,
     112             :                           explicit_armor_crypto);
     113             : }
     114             : 
     115             : static krb5_error_code
     116       63774 : check_fast(krb5_context context, struct krb5_fast_state *state)
     117             : {
     118       61998 :     if (state && (state->flags & KRB5_FAST_EXPECTED)) {
     119           0 :         krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
     120             :                                "Expected FAST, but no FAST "
     121             :                                "was in the response from the KDC");
     122           0 :         return KRB5KRB_AP_ERR_MODIFIED;
     123             :     }
     124       61406 :     return 0;
     125             : }
     126             : 
     127             : static krb5_error_code
     128          17 : make_local_fast_ap_fxarmor(krb5_context context,
     129             :                            krb5_ccache armor_ccache,
     130             :                            krb5_const_realm realm,
     131             :                            krb5_data *armor_value,
     132             :                            krb5_keyblock *armor_key,
     133             :                            krb5_crypto *armor_crypto)
     134             : {
     135          17 :     krb5_auth_context auth_context = NULL;
     136          17 :     krb5_creds cred, *credp = NULL;
     137           0 :     krb5_error_code ret;
     138           0 :     krb5_data empty;
     139           0 :     krb5_const_realm tgs_realm;
     140             : 
     141          17 :     if (armor_ccache == NULL) {
     142           0 :         krb5_set_error_message(context, EINVAL,
     143             :                                "Armor credential cache required");
     144           0 :         return EINVAL;
     145             :     }
     146             : 
     147          17 :     krb5_data_zero(&empty);
     148          17 :     memset(&cred, 0, sizeof(cred));
     149             : 
     150          17 :     ret = krb5_auth_con_init (context, &auth_context);
     151          17 :     if (ret)
     152           0 :         goto out;
     153             : 
     154          17 :     ret = krb5_cc_get_principal(context, armor_ccache, &cred.client);
     155          17 :     if (ret)
     156           0 :         goto out;
     157             : 
     158             :     /*
     159             :      * Make sure we don't ask for a krbtgt/WELLKNOWN:ANONYMOUS
     160             :      */
     161          17 :     if (krb5_principal_is_anonymous(context, cred.client,
     162             :                                     KRB5_ANON_MATCH_UNAUTHENTICATED))
     163           0 :         tgs_realm = realm;
     164             :     else
     165          17 :         tgs_realm = cred.client->realm;
     166             : 
     167          17 :     ret = krb5_make_principal(context, &cred.server,
     168             :                               tgs_realm,
     169             :                               KRB5_TGS_NAME,
     170             :                               tgs_realm,
     171             :                               NULL);
     172          17 :     if (ret)
     173           0 :         goto out;
     174             : 
     175          17 :     ret = krb5_get_credentials(context, 0, armor_ccache, &cred, &credp);
     176          17 :     if (ret)
     177           0 :         goto out;
     178             : 
     179          17 :     ret = krb5_auth_con_add_AuthorizationData(context, auth_context,
     180             :                                               KRB5_AUTHDATA_FX_FAST_ARMOR,
     181             :                                               &empty);
     182          17 :     if (ret)
     183           0 :         goto out;
     184             : 
     185          17 :     ret = krb5_mk_req_extended(context,
     186             :                                &auth_context,
     187             :                                AP_OPTS_USE_SUBKEY,
     188             :                                NULL,
     189             :                                credp,
     190             :                                armor_value);
     191          17 :     if (ret)
     192           0 :         goto out;
     193             : 
     194          17 :     ret = _krb5_fast_armor_key(context,
     195          17 :                                auth_context->local_subkey,
     196          17 :                                auth_context->keyblock,
     197             :                                armor_key,
     198             :                                armor_crypto);
     199          17 :     if (ret)
     200           0 :         goto out;
     201             : 
     202          17 :  out:
     203          17 :     if (auth_context)
     204          17 :         krb5_auth_con_free(context, auth_context);
     205          17 :     if (credp)
     206          17 :         krb5_free_creds(context, credp);
     207          17 :     krb5_free_principal(context, cred.server);
     208          17 :     krb5_free_principal(context, cred.client);
     209             : 
     210          17 :     return ret;
     211             : }
     212             : 
     213             : #ifndef WIN32
     214             : static heim_base_once_t armor_service_once = HEIM_BASE_ONCE_INIT;
     215             : static heim_ipc armor_service = NULL;
     216             : 
     217             : static void
     218           0 : fast_armor_init_ipc(void *ctx)
     219             : {
     220           0 :     heim_ipc *ipc = ctx;
     221           0 :     heim_ipc_init_context("ANY:org.h5l.armor-service", ipc);
     222           0 : }
     223             : #endif
     224             : 
     225             : static krb5_error_code
     226          17 : make_fast_ap_fxarmor(krb5_context context,
     227             :                      struct krb5_fast_state *state,
     228             :                      krb5_const_realm realm,
     229             :                      KrbFastArmor **armor)
     230             : {
     231          17 :     KrbFastArmor *fxarmor = NULL;
     232           0 :     krb5_error_code ret;
     233             : 
     234          17 :     *armor = NULL;
     235             : 
     236          17 :     ALLOC(fxarmor, 1);
     237          17 :     if (fxarmor == NULL) {
     238           0 :         ret = ENOMEM;
     239           0 :         goto out;
     240             :     }
     241             : 
     242          17 :     if (state->flags & KRB5_FAST_AP_ARMOR_SERVICE) {
     243             : #ifdef WIN32
     244             :         krb5_set_error_message(context, ENOTSUP, "Fast armor IPC service not supportted yet on Windows");
     245             :         ret = ENOTSUP;
     246             :         goto out;
     247             : #else
     248           0 :         KERB_ARMOR_SERVICE_REPLY msg;
     249           0 :         krb5_data request, reply;
     250             : 
     251           0 :         heim_base_once_f(&armor_service_once, &armor_service, fast_armor_init_ipc);
     252           0 :         if (armor_service == NULL) {
     253           0 :             krb5_set_error_message(context, ENOENT, "Failed to open fast armor service");
     254           0 :             ret = ENOENT;
     255           0 :             goto out;
     256             :         }
     257             : 
     258           0 :         krb5_data_zero(&reply);
     259             : 
     260           0 :         request.data = rk_UNCONST(realm);
     261           0 :         request.length = strlen(realm);
     262             : 
     263           0 :         ret = heim_ipc_call(armor_service, &request, &reply, NULL);
     264           0 :         if (ret) {
     265           0 :             krb5_set_error_message(context, ret, "Failed to get armor service credential");
     266           0 :             goto out;
     267             :         }
     268             : 
     269           0 :         ret = decode_KERB_ARMOR_SERVICE_REPLY(reply.data, reply.length, &msg, NULL);
     270           0 :         krb5_data_free(&reply);
     271           0 :         if (ret)
     272           0 :             goto out;
     273             : 
     274           0 :         ret = copy_KrbFastArmor(fxarmor, &msg.armor);
     275           0 :         if (ret) {
     276           0 :             free_KERB_ARMOR_SERVICE_REPLY(&msg);
     277           0 :             goto out;
     278             :         }
     279             : 
     280           0 :         ret = krb5_copy_keyblock_contents(context, &msg.armor_key, &state->armor_key);
     281           0 :         free_KERB_ARMOR_SERVICE_REPLY(&msg);
     282           0 :         if (ret)
     283           0 :             goto out;
     284             : 
     285           0 :         ret = krb5_crypto_init(context, &state->armor_key, 0, &state->armor_crypto);
     286           0 :         if (ret)
     287           0 :             goto out;
     288             : #endif /* WIN32 */
     289             :     } else {
     290          17 :         fxarmor->armor_type = 1;
     291             : 
     292          17 :         ret = make_local_fast_ap_fxarmor(context,
     293             :                                          state->armor_ccache,
     294             :                                          realm,
     295          17 :                                          &fxarmor->armor_value,
     296             :                                          &state->armor_key,
     297             :                                          &state->armor_crypto);
     298          17 :         if (ret)
     299           0 :             goto out;
     300             :     }
     301             : 
     302             : 
     303          17 :     *armor = fxarmor;
     304          17 :     fxarmor = NULL;
     305             : 
     306          17 :  out:
     307          17 :     if (fxarmor) {
     308           0 :         free_KrbFastArmor(fxarmor);
     309           0 :         free(fxarmor);
     310             :     }
     311          17 :     return ret;
     312             : }
     313             : 
     314             : static krb5_error_code
     315       43770 : unwrap_fast_rep(krb5_context context,
     316             :                 struct krb5_fast_state *state,
     317             :                 PA_DATA *pa,
     318             :                 KrbFastResponse *fastrep)
     319             : {
     320        1672 :     PA_FX_FAST_REPLY fxfastrep;
     321        1672 :     krb5_error_code ret;
     322             : 
     323       43770 :     memset(&fxfastrep, 0, sizeof(fxfastrep));
     324             : 
     325       43770 :     ret = decode_PA_FX_FAST_REPLY(pa->padata_value.data,
     326             :                                   pa->padata_value.length,
     327             :                                   &fxfastrep, NULL);
     328       43770 :     if (ret)
     329           0 :         return ret;
     330             : 
     331       43770 :     if (fxfastrep.element == choice_PA_FX_FAST_REPLY_armored_data) {
     332        1672 :         krb5_data data;
     333             : 
     334       43770 :         ret = krb5_decrypt_EncryptedData(context,
     335             :                                          state->armor_crypto,
     336             :                                          KRB5_KU_FAST_REP,
     337             :                                          &fxfastrep.u.armored_data.enc_fast_rep,
     338             :                                          &data);
     339       43770 :         if (ret)
     340           0 :             goto out;
     341             : 
     342       43770 :         ret = decode_KrbFastResponse(data.data, data.length, fastrep, NULL);
     343       43770 :         krb5_data_free(&data);
     344       43770 :         if (ret)
     345           0 :             goto out;
     346             : 
     347             :     } else {
     348           0 :         ret = KRB5KDC_ERR_PREAUTH_FAILED;
     349           0 :         goto out;
     350             :     }
     351             : 
     352       43770 :  out:
     353       43770 :     free_PA_FX_FAST_REPLY(&fxfastrep);
     354             : 
     355       43770 :     return ret;
     356             : }
     357             : 
     358             : static krb5_error_code
     359          17 : set_anon_principal(krb5_context context, PrincipalName **p)
     360             : {
     361             : 
     362          17 :     ALLOC((*p), 1);
     363          17 :     if (*p == NULL)
     364           0 :         goto fail;
     365             : 
     366          17 :     (*p)->name_type = KRB5_NT_PRINCIPAL;
     367             : 
     368          17 :     ALLOC_SEQ(&(*p)->name_string, 2);
     369          17 :     if ((*p)->name_string.val == NULL)
     370           0 :         goto fail;
     371             : 
     372          17 :     (*p)->name_string.val[0] = strdup(KRB5_WELLKNOWN_NAME);
     373          17 :     if ((*p)->name_string.val[0] == NULL)
     374           0 :         goto fail;
     375             : 
     376          17 :     (*p)->name_string.val[1] = strdup(KRB5_ANON_NAME);
     377          17 :     if ((*p)->name_string.val[1] == NULL)
     378           0 :         goto fail;
     379             : 
     380          17 :     return 0;
     381           0 :  fail:
     382           0 :     if (*p) {
     383           0 :         if ((*p)->name_string.val) {
     384           0 :             free((*p)->name_string.val[0]);
     385           0 :             free((*p)->name_string.val[1]);
     386           0 :             free((*p)->name_string.val);
     387             :         }
     388           0 :         free(*p);
     389             :     }
     390             : 
     391           0 :     return krb5_enomem(context);
     392             : }
     393             : 
     394             : krb5_error_code
     395       78944 : _krb5_fast_create_armor(krb5_context context,
     396             :                         struct krb5_fast_state *state,
     397             :                         const char *realm)
     398             : {
     399        2856 :     krb5_error_code ret;
     400             : 
     401       78944 :     if (state->armor_crypto == NULL) {
     402       75742 :         if (state->armor_ccache || state->armor_ac || (state->flags & KRB5_FAST_AP_ARMOR_SERVICE)) {
     403             :             /*
     404             :              * Instead of keeping state in FX_COOKIE in the KDC, we
     405             :              * rebuild a new armor key for every request, because this
     406             :              * is what the MIT KDC expect and RFC6113 is vage about
     407             :              * what the behavior should be.
     408             :              */
     409       44825 :             state->type = choice_PA_FX_FAST_REQUEST_armored_data;
     410             :         } else {
     411       30917 :             return check_fast(context, state);
     412             :         }
     413             :     }
     414             : 
     415       48027 :     if (state->type == choice_PA_FX_FAST_REQUEST_armored_data) {
     416       48027 :         if (state->armor_crypto) {
     417        3202 :             krb5_crypto_destroy(context, state->armor_crypto);
     418        3202 :             state->armor_crypto = NULL;
     419             :         }
     420       48027 :         if (state->strengthen_key) {
     421        2488 :             krb5_free_keyblock(context, state->strengthen_key);
     422        2488 :             state->strengthen_key = NULL;
     423             :         }
     424       48027 :         krb5_free_keyblock_contents(context, &state->armor_key);
     425             : 
     426             :         /*
     427             :          * If we have a armor auth context, its because the caller
     428             :          * wants us to do an implicit FAST armor (TGS-REQ).
     429             :          */
     430       48027 :         if (state->armor_ac) {
     431       48010 :             heim_assert((state->flags & KRB5_FAST_AS_REQ) == 0, "FAST AS with AC");
     432             : 
     433       48010 :             ret = _krb5_fast_armor_key(context,
     434       46338 :                                        state->armor_ac->local_subkey,
     435       46338 :                                        state->armor_ac->keyblock,
     436             :                                        &state->armor_key,
     437             :                                        &state->armor_crypto);
     438       48010 :             if (ret)
     439           0 :                 goto out;
     440             :         } else {
     441          17 :             heim_assert((state->flags & KRB5_FAST_AS_REQ) != 0, "FAST TGS without AC");
     442             : 
     443          17 :             if (state->armor_data) {
     444           0 :                 free_KrbFastArmor(state->armor_data);
     445           0 :                 free(state->armor_data);
     446           0 :                 state->armor_data = NULL;
     447             :             }
     448          17 :             ret = make_fast_ap_fxarmor(context, state, realm,
     449             :                                        &state->armor_data);
     450          17 :             if (ret)
     451           0 :                 goto out;
     452             :         }
     453             :     } else {
     454           0 :         heim_abort("unknown state type: %d", (int)state->type);
     455             :     }
     456       46355 :  out:
     457       46355 :     return ret;
     458             : }
     459             : 
     460             : 
     461             : krb5_error_code
     462       78944 : _krb5_fast_wrap_req(krb5_context context,
     463             :                     struct krb5_fast_state *state,
     464             :                     KDC_REQ *req)
     465             : {
     466        2856 :     PA_FX_FAST_REQUEST fxreq;
     467        2856 :     krb5_error_code ret;
     468        2856 :     KrbFastReq fastreq;
     469        2856 :     krb5_data data, aschecksum_data, tgschecksum_data;
     470       78944 :     const krb5_data *checksum_data = NULL;
     471       78944 :     size_t size = 0;
     472       78944 :     krb5_boolean readd_padata_to_outer = FALSE;
     473             : 
     474       78944 :     if (state->flags & KRB5_FAST_DISABLED) {
     475       30917 :         _krb5_debug(context, 10, "fast disabled, not doing any fast wrapping");
     476       30917 :         return 0;
     477             :     }
     478             : 
     479       48027 :     memset(&fxreq, 0, sizeof(fxreq));
     480       48027 :     memset(&fastreq, 0, sizeof(fastreq));
     481       48027 :     krb5_data_zero(&data);
     482       48027 :     krb5_data_zero(&aschecksum_data);
     483       48027 :     krb5_data_zero(&tgschecksum_data);
     484             : 
     485       48027 :     if (state->armor_crypto == NULL)
     486           0 :         return check_fast(context, state);
     487             : 
     488       48027 :     state->flags |= KRB5_FAST_EXPECTED;
     489             : 
     490       48027 :     fastreq.fast_options.hide_client_names = 1;
     491             : 
     492       48027 :     ret = copy_KDC_REQ_BODY(&req->req_body, &fastreq.req_body);
     493       48027 :     if (ret)
     494           0 :         goto out;
     495             : 
     496             :     /*
     497             :      * In the case of a AS-REQ, remove all account names. Want to this
     498             :      * for TGS-REQ too, but due to layering this is tricky.
     499             :      *
     500             :      * 1. TGS-REQ need checksum of REQ-BODY
     501             :      * 2. FAST needs checksum of TGS-REQ, so, FAST needs to happen after TGS-REQ
     502             :      * 3. FAST privacy mangaling needs to happen before TGS-REQ does the checksum in 1.
     503             :      *
     504             :      * So lets not modify the bits for now for TGS-REQ
     505             :      */
     506       48027 :     if (state->flags & KRB5_FAST_AS_REQ) {
     507          17 :         free_KDC_REQ_BODY(&req->req_body);
     508             : 
     509          17 :         req->req_body.realm = strdup(KRB5_ANON_REALM);
     510          17 :         if (req->req_body.realm == NULL) {
     511           0 :             ret = krb5_enomem(context);
     512           0 :             goto out;
     513             :         }
     514             : 
     515          17 :         ret = set_anon_principal(context, &req->req_body.cname);
     516          17 :         if (ret)
     517           0 :             goto out;
     518             : 
     519          17 :         ALLOC(req->req_body.till, 1);
     520          17 :         *req->req_body.till = 0;
     521             : 
     522          17 :         ASN1_MALLOC_ENCODE(KDC_REQ_BODY,
     523             :                            aschecksum_data.data,
     524             :                            aschecksum_data.length,
     525             :                            &req->req_body,
     526             :                            &size, ret);
     527          17 :         if (ret)
     528           0 :             goto out;
     529          17 :         heim_assert(aschecksum_data.length == size, "ASN.1 internal error");
     530             : 
     531          17 :         checksum_data = &aschecksum_data;
     532             : 
     533          17 :         if (req->padata) {
     534          17 :             ret = copy_METHOD_DATA(req->padata, &fastreq.padata);
     535          17 :             free_METHOD_DATA(req->padata);
     536          17 :             if (ret)
     537           0 :                 goto out;
     538             :         }
     539             :     } else {
     540       48010 :         const PA_DATA *tgs_req_ptr = NULL;
     541       48010 :         int tgs_req_idx = 0;
     542        1672 :         size_t i;
     543             : 
     544       48010 :         heim_assert(req->padata != NULL, "req->padata is NULL");
     545             : 
     546       48010 :         tgs_req_ptr = krb5_find_padata(req->padata->val,
     547       46338 :                                        req->padata->len,
     548             :                                        KRB5_PADATA_TGS_REQ,
     549             :                                        &tgs_req_idx);
     550       48010 :         heim_assert(tgs_req_ptr != NULL, "KRB5_PADATA_TGS_REQ not found");
     551       48010 :         heim_assert(tgs_req_idx == 0, "KRB5_PADATA_TGS_REQ not first");
     552             : 
     553       48010 :         tgschecksum_data.data = tgs_req_ptr->padata_value.data;
     554       48010 :         tgschecksum_data.length = tgs_req_ptr->padata_value.length;
     555       48010 :         checksum_data = &tgschecksum_data;
     556             : 
     557             :         /*
     558             :          * Now copy all remaining once to
     559             :          * the fastreq.padata and clear
     560             :          * them in the outer req first,
     561             :          * and remember to readd them later.
     562             :          */
     563       48010 :         readd_padata_to_outer = TRUE;
     564             : 
     565       48912 :         for (i = 1; i < req->padata->len; i++) {
     566         902 :             PA_DATA *val = &req->padata->val[i];
     567             : 
     568         902 :             ret = krb5_padata_add(context,
     569             :                                   &fastreq.padata,
     570         902 :                                   val->padata_type,
     571             :                                   val->padata_value.data,
     572             :                                   val->padata_value.length);
     573         902 :             if (ret) {
     574           0 :                 krb5_set_error_message(context, ret,
     575           0 :                                        N_("malloc: out of memory", ""));
     576           0 :                 goto out;
     577             :             }
     578         902 :             val->padata_value.data = NULL;
     579         902 :             val->padata_value.length = 0;
     580             :         }
     581             : 
     582             :         /*
     583             :          * Only TGS-REQ remaining
     584             :          */
     585       48010 :         req->padata->len = 1;
     586             :     }
     587             : 
     588       48027 :     if (req->padata == NULL) {
     589           0 :         ALLOC(req->padata, 1);
     590           0 :         if (req->padata == NULL) {
     591           0 :             ret = krb5_enomem(context);
     592           0 :             goto out;
     593             :         }
     594             :     }
     595             : 
     596       48027 :     ASN1_MALLOC_ENCODE(KrbFastReq, data.data, data.length, &fastreq, &size, ret);
     597       48027 :     if (ret)
     598           0 :         goto out;
     599       48027 :     heim_assert(data.length == size, "ASN.1 internal error");
     600             : 
     601       48027 :     fxreq.element = state->type;
     602             : 
     603       48027 :     if (state->type == choice_PA_FX_FAST_REQUEST_armored_data) {
     604       48027 :         fxreq.u.armored_data.armor = state->armor_data;
     605       48027 :         state->armor_data = NULL;
     606             : 
     607       48027 :         heim_assert(state->armor_crypto != NULL,
     608             :                     "FAST armor key missing when FAST started");
     609             : 
     610       49699 :         ret = krb5_create_checksum(context, state->armor_crypto,
     611             :                                    KRB5_KU_FAST_REQ_CHKSUM, 0,
     612       48027 :                                    checksum_data->data,
     613       48027 :                                    checksum_data->length,
     614             :                                    &fxreq.u.armored_data.req_checksum);
     615       48027 :         if (ret)
     616           0 :             goto out;
     617             : 
     618       48027 :         ret = krb5_encrypt_EncryptedData(context, state->armor_crypto,
     619             :                                          KRB5_KU_FAST_ENC,
     620             :                                          data.data,
     621             :                                          data.length,
     622             :                                          0,
     623             :                                          &fxreq.u.armored_data.enc_fast_req);
     624       48027 :         krb5_data_free(&data);
     625       48027 :         if (ret)
     626           0 :             goto out;
     627             : 
     628             :     } else {
     629           0 :         krb5_data_free(&data);
     630           0 :         heim_assert(false, "unknown FAST type, internal error");
     631             :     }
     632             : 
     633       48027 :     ASN1_MALLOC_ENCODE(PA_FX_FAST_REQUEST, data.data, data.length, &fxreq, &size, ret);
     634       48027 :     if (ret)
     635           0 :         goto out;
     636       48027 :     heim_assert(data.length == size, "ASN.1 internal error");
     637             : 
     638             : 
     639       48027 :     ret = krb5_padata_add(context, req->padata, KRB5_PADATA_FX_FAST, data.data, data.length);
     640       48027 :     if (ret)
     641           0 :         goto out;
     642       48027 :     krb5_data_zero(&data);
     643             : 
     644       48027 :     if (readd_padata_to_outer) {
     645             :         size_t i;
     646             : 
     647       48912 :         for (i = 0; i < fastreq.padata.len; i++) {
     648         902 :             PA_DATA *val = &fastreq.padata.val[i];
     649             : 
     650         902 :             ret = krb5_padata_add(context,
     651             :                                   req->padata,
     652         902 :                                   val->padata_type,
     653             :                                   val->padata_value.data,
     654             :                                   val->padata_value.length);
     655         902 :             if (ret) {
     656           0 :                 krb5_set_error_message(context, ret,
     657           0 :                                        N_("malloc: out of memory", ""));
     658           0 :                 goto out;
     659             :             }
     660         902 :             val->padata_value.data = NULL;
     661         902 :             val->padata_value.length = 0;
     662             :         }
     663             :     }
     664             : 
     665       48027 :  out:
     666       48027 :     free_KrbFastReq(&fastreq);
     667       48027 :     free_PA_FX_FAST_REQUEST(&fxreq);
     668       48027 :     krb5_data_free(&data);
     669       48027 :     krb5_data_free(&aschecksum_data);
     670             : 
     671       48027 :     return ret;
     672             : }
     673             : 
     674             : krb5_error_code
     675       16708 : _krb5_fast_unwrap_error(krb5_context context,
     676             :                         int32_t nonce,
     677             :                         struct krb5_fast_state *state,
     678             :                         METHOD_DATA *md,
     679             :                         KRB_ERROR *error)
     680             : {
     681         592 :     KrbFastResponse fastrep;
     682         592 :     krb5_error_code ret;
     683         592 :     PA_DATA *pa;
     684         592 :     int idx;
     685             : 
     686       16708 :     if (state->armor_crypto == NULL)
     687       15446 :         return check_fast(context, state);
     688             : 
     689        1262 :     memset(&fastrep, 0, sizeof(fastrep));
     690             : 
     691        1262 :     idx = 0;
     692        1262 :     pa = krb5_find_padata(md->val, md->len, KRB5_PADATA_FX_FAST, &idx);
     693        1262 :     if (pa == NULL) {
     694          32 :         ret = KRB5_KDCREP_MODIFIED;
     695          32 :         krb5_set_error_message(context, ret,
     696          32 :                                N_("FAST fast response is missing FX-FAST", ""));
     697          32 :         goto out;
     698             :     }
     699             : 
     700        1230 :     ret = unwrap_fast_rep(context, state, pa, &fastrep);
     701        1230 :     if (ret)
     702           0 :         goto out;
     703             : 
     704        1230 :     if (fastrep.strengthen_key || nonce != (int32_t)fastrep.nonce) {
     705           0 :         ret = KRB5KDC_ERR_PREAUTH_FAILED;
     706           0 :         goto out;
     707             :     }
     708             : 
     709        1230 :     idx = 0;
     710        1230 :     pa = krb5_find_padata(fastrep.padata.val, fastrep.padata.len, KRB5_PADATA_FX_ERROR, &idx);
     711        1230 :     if (pa == NULL) {
     712           0 :         ret = KRB5_KDCREP_MODIFIED;
     713           0 :         krb5_set_error_message(context, ret, N_("No wrapped error", ""));
     714           0 :         goto out;
     715             :     }
     716             : 
     717        1230 :     free_KRB_ERROR(error);
     718             : 
     719        1230 :     ret = krb5_rd_error(context, &pa->padata_value, error);
     720        1230 :     if (ret)
     721           0 :         goto out;
     722             : 
     723        1230 :     if (error->e_data)
     724           0 :         _krb5_debug(context, 10, "FAST wrapped KBB_ERROR contained e_data: %d",
     725           0 :                      (int)error->e_data->length);
     726             : 
     727        1230 :     free_METHOD_DATA(md);
     728        1230 :     md->val = fastrep.padata.val;
     729        1230 :     md->len = fastrep.padata.len;
     730             : 
     731        1230 :     fastrep.padata.val = NULL;
     732        1230 :     fastrep.padata.len = 0;
     733             : 
     734        1262 :  out:
     735        1262 :     free_KrbFastResponse(&fastrep);
     736        1262 :     return ret;
     737             : }
     738             : 
     739             : krb5_error_code
     740       59951 : _krb5_fast_unwrap_kdc_rep(krb5_context context, int32_t nonce,
     741             :                           krb5_data *chksumdata,
     742             :                           struct krb5_fast_state *state, AS_REP *rep)
     743             : {
     744        2264 :     KrbFastResponse fastrep;
     745        2264 :     krb5_error_code ret;
     746       59951 :     PA_DATA *pa = NULL;
     747       59951 :     int idx = 0;
     748             : 
     749       59951 :     if (state == NULL || state->armor_crypto == NULL || rep->padata == NULL)
     750       18003 :         return check_fast(context, state);
     751             : 
     752             :     /* find PA_FX_FAST_REPLY */
     753             : 
     754       42540 :     pa = krb5_find_padata(rep->padata->val, rep->padata->len,
     755             :                           KRB5_PADATA_FX_FAST, &idx);
     756       42540 :     if (pa == NULL)
     757           0 :         return check_fast(context, state);
     758             : 
     759       42540 :     memset(&fastrep, 0, sizeof(fastrep));
     760             : 
     761       42540 :     ret = unwrap_fast_rep(context, state, pa, &fastrep);
     762       42540 :     if (ret)
     763           0 :         goto out;
     764             : 
     765       42540 :     free_METHOD_DATA(rep->padata);
     766       42540 :     ret = copy_METHOD_DATA(&fastrep.padata, rep->padata);
     767       42540 :     if (ret)
     768           0 :         goto out;
     769             : 
     770       42540 :     if (fastrep.strengthen_key) {
     771       42540 :         if (state->strengthen_key)
     772           0 :             krb5_free_keyblock(context, state->strengthen_key);
     773             : 
     774       42540 :         ret = krb5_copy_keyblock(context, fastrep.strengthen_key, &state->strengthen_key);
     775       42540 :         if (ret)
     776           0 :             goto out;
     777             :     }
     778             : 
     779       42540 :     if (nonce != (int32_t)fastrep.nonce) {
     780           0 :         ret = KRB5KDC_ERR_PREAUTH_FAILED;
     781           0 :         goto out;
     782             :     }
     783       42540 :     if (fastrep.finished) {
     784        1672 :         PrincipalName cname;
     785       42540 :         krb5_realm crealm = NULL;
     786             : 
     787       42540 :         if (chksumdata == NULL) {
     788           0 :             ret = KRB5KDC_ERR_PREAUTH_FAILED;
     789           0 :             goto out;
     790             :         }
     791             : 
     792       42540 :         ret = krb5_verify_checksum(context, state->armor_crypto,
     793             :                                    KRB5_KU_FAST_FINISHED,
     794             :                                    chksumdata->data, chksumdata->length,
     795       40868 :                                    &fastrep.finished->ticket_checksum);
     796       42540 :         if (ret)
     797           0 :             goto out;
     798             : 
     799             :         /* update */
     800       42540 :         ret = copy_Realm(&fastrep.finished->crealm, &crealm);
     801       42540 :         if (ret)
     802           0 :             goto out;
     803       42540 :         free_Realm(&rep->crealm);
     804       42540 :         rep->crealm = crealm;
     805             : 
     806       42540 :         ret = copy_PrincipalName(&fastrep.finished->cname, &cname);
     807       42540 :         if (ret)
     808           0 :             goto out;
     809       42540 :         free_PrincipalName(&rep->cname);
     810       42540 :         rep->cname = cname;
     811           0 :     } else if (chksumdata) {
     812             :         /* expected fastrep.finish but didn't get it */
     813           0 :         ret = KRB5KDC_ERR_PREAUTH_FAILED;
     814             :     }
     815             : 
     816           0 :  out:
     817       42540 :     free_KrbFastResponse(&fastrep);
     818       42540 :     return ret;
     819             : }
     820             : 
     821             : void
     822      144663 : _krb5_fast_free(krb5_context context, struct krb5_fast_state *state)
     823             : {
     824      144663 :     if (state->armor_ccache) {
     825          10 :         if (state->flags & KRB5_FAST_ANON_PKINIT_ARMOR)
     826           0 :             krb5_cc_destroy(context, state->armor_ccache);
     827             :         else
     828          10 :             krb5_cc_close(context, state->armor_ccache);
     829             :     }
     830      144663 :     if (state->armor_service)
     831           0 :         krb5_free_principal(context, state->armor_service);
     832      144663 :     if (state->armor_crypto)
     833       44825 :         krb5_crypto_destroy(context, state->armor_crypto);
     834      144663 :     if (state->strengthen_key)
     835       40052 :         krb5_free_keyblock(context, state->strengthen_key);
     836      144663 :     krb5_free_keyblock_contents(context, &state->armor_key);
     837      144663 :     if (state->armor_data) {
     838           0 :         free_KrbFastArmor(state->armor_data);
     839           0 :         free(state->armor_data);
     840             :     }
     841             : 
     842      144663 :     if (state->anon_pkinit_ctx)
     843         104 :         krb5_init_creds_free(context, state->anon_pkinit_ctx);
     844      144663 :     if (state->anon_pkinit_opt)
     845         104 :         krb5_get_init_creds_opt_free(context, state->anon_pkinit_opt);
     846             : 
     847      144663 :     memset(state, 0, sizeof(*state));
     848      144663 : }
     849             : 
     850             : krb5_error_code
     851         208 : _krb5_fast_anon_pkinit_step(krb5_context context,
     852             :                             krb5_init_creds_context ctx,
     853             :                             struct krb5_fast_state *state,
     854             :                             const krb5_data *in,
     855             :                             krb5_data *out,
     856             :                             krb5_realm *out_realm,
     857             :                             unsigned int *flags)
     858             : {
     859           0 :     krb5_error_code ret;
     860         208 :     krb5_const_realm realm = _krb5_init_creds_get_cred_client(context, ctx)->realm;
     861           0 :     krb5_init_creds_context anon_pk_ctx;
     862         208 :     krb5_principal principal = NULL, anon_pk_client;
     863         208 :     krb5_ccache ccache = NULL;
     864           0 :     krb5_creds cred;
     865         208 :     krb5_data data = { 3, rk_UNCONST("yes") };
     866             : 
     867         208 :     krb5_data_zero(out);
     868         208 :     *out_realm = NULL;
     869             : 
     870         208 :     memset(&cred, 0, sizeof(cred));
     871             : 
     872         208 :     if (state->anon_pkinit_opt == NULL) {
     873         104 :         ret = krb5_get_init_creds_opt_alloc(context, &state->anon_pkinit_opt);
     874         104 :         if (ret)
     875           0 :             goto out;
     876             : 
     877         104 :         krb5_get_init_creds_opt_set_tkt_life(state->anon_pkinit_opt, 60);
     878         104 :         krb5_get_init_creds_opt_set_anonymous(state->anon_pkinit_opt, TRUE);
     879             : 
     880         104 :         ret = krb5_make_principal(context, &principal, realm,
     881             :                                   KRB5_WELLKNOWN_NAME, KRB5_ANON_NAME, NULL);
     882         104 :         if (ret)
     883           0 :             goto out;
     884             : 
     885         104 :         ret = krb5_get_init_creds_opt_set_pkinit(context,
     886             :                                                  state->anon_pkinit_opt,
     887             :                                                  principal,
     888             :                                                  NULL, NULL, NULL, NULL,
     889             :                                                  KRB5_GIC_OPT_PKINIT_ANONYMOUS |
     890             :                                                  KRB5_GIC_OPT_PKINIT_NO_KDC_ANCHOR,
     891             :                                                  NULL, NULL, NULL);
     892         104 :         if (ret)
     893           0 :             goto out;
     894             : 
     895         104 :         ret = krb5_init_creds_init(context, principal, NULL, NULL,
     896             :                                    _krb5_init_creds_get_cred_starttime(context, ctx),
     897             :                                    state->anon_pkinit_opt,
     898             :                                    &state->anon_pkinit_ctx);
     899         104 :         if (ret)
     900           0 :             goto out;
     901             :     }
     902             : 
     903         208 :     anon_pk_ctx = state->anon_pkinit_ctx;
     904             : 
     905         208 :     ret = krb5_init_creds_step(context, anon_pk_ctx, in, out, out_realm, flags);
     906         208 :     if (ret ||
     907         104 :         (*flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE))
     908         208 :         goto out;
     909             : 
     910           0 :     ret = krb5_process_last_request(context, state->anon_pkinit_opt, anon_pk_ctx);
     911           0 :     if (ret)
     912           0 :         goto out;
     913             : 
     914           0 :     ret = krb5_cc_new_unique(context, "MEMORY", NULL, &ccache);
     915           0 :     if (ret)
     916           0 :         goto out;
     917             : 
     918           0 :     ret = krb5_init_creds_get_creds(context, anon_pk_ctx, &cred);
     919           0 :     if (ret)
     920           0 :         goto out;
     921             : 
     922           0 :     if (!cred.flags.b.enc_pa_rep) {
     923           0 :         ret = KRB5KDC_ERR_BADOPTION; /* KDC does not support FAST */
     924           0 :         goto out;
     925             :     }
     926             : 
     927           0 :     anon_pk_client = _krb5_init_creds_get_cred_client(context, anon_pk_ctx);
     928             : 
     929           0 :     ret = krb5_cc_initialize(context, ccache, anon_pk_client);
     930           0 :     if (ret)
     931           0 :         goto out;
     932             : 
     933           0 :     ret = krb5_cc_store_cred(context, ccache, &cred);
     934           0 :     if (ret)
     935           0 :         goto out;
     936             : 
     937           0 :     ret = krb5_cc_set_config(context, ccache, cred.server,
     938             :                              "fast_avail", &data);
     939           0 :     if (ret && ret != KRB5_CC_NOSUPP)
     940           0 :         return ret;
     941             : 
     942           0 :     if (_krb5_pk_is_kdc_verified(context, state->anon_pkinit_opt))
     943           0 :         state->flags |= KRB5_FAST_KDC_VERIFIED;
     944             :     else
     945           0 :         state->flags &= ~(KRB5_FAST_KDC_VERIFIED);
     946             : 
     947           0 :     state->armor_ccache = ccache;
     948           0 :     ccache = NULL;
     949             : 
     950           0 :     krb5_init_creds_free(context, state->anon_pkinit_ctx);
     951           0 :     state->anon_pkinit_ctx = NULL;
     952             : 
     953           0 :     krb5_get_init_creds_opt_free(context, state->anon_pkinit_opt);
     954           0 :     state->anon_pkinit_opt = NULL;
     955             : 
     956         208 : out:
     957         208 :     krb5_free_principal(context, principal);
     958         208 :     krb5_free_cred_contents(context, &cred);
     959         208 :     if (ccache)
     960           0 :         krb5_cc_destroy(context, ccache);
     961             : 
     962         208 :     return ret;
     963             : }

Generated by: LCOV version 1.14