LCOV - code coverage report
Current view: top level - third_party/heimdal/lib/krb5 - init_creds_pw.c (source / functions) Hit Total Coverage
Test: coverage report for master 98b443d9 Lines: 1217 1910 63.7 %
Date: 2024-05-31 13:13:24 Functions: 66 88 75.0 %

          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             :  * Portions Copyright (c) 2021, PADL Software Pty Ltd. All rights reserved.
       8             :  *
       9             :  * Redistribution and use in source and binary forms, with or without
      10             :  * modification, are permitted provided that the following conditions
      11             :  * are met:
      12             :  *
      13             :  * 1. Redistributions of source code must retain the above copyright
      14             :  *    notice, this list of conditions and the following disclaimer.
      15             :  *
      16             :  * 2. Redistributions in binary form must reproduce the above copyright
      17             :  *    notice, this list of conditions and the following disclaimer in the
      18             :  *    documentation and/or other materials provided with the distribution.
      19             :  *
      20             :  * 3. Neither the name of the Institute nor the names of its contributors
      21             :  *    may be used to endorse or promote products derived from this software
      22             :  *    without specific prior written permission.
      23             :  *
      24             :  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
      25             :  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      26             :  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
      27             :  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
      28             :  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
      29             :  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
      30             :  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      31             :  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
      32             :  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
      33             :  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
      34             :  * SUCH DAMAGE.
      35             :  */
      36             : 
      37             : #include "krb5_locl.h"
      38             : 
      39             : #include <heimbasepriv.h>
      40             : 
      41             : struct pa_info_data {
      42             :     krb5_enctype etype;
      43             :     krb5_salt salt;
      44             :     krb5_data *s2kparams;
      45             : };
      46             : 
      47             : struct krb5_gss_init_ctx_data {
      48             :     krb5_gssic_step step;
      49             :     krb5_gssic_finish finish;
      50             :     krb5_gssic_release_cred release_cred;
      51             :     krb5_gssic_delete_sec_context delete_sec_context;
      52             : 
      53             :     const struct gss_OID_desc_struct *mech;
      54             :     struct gss_cred_id_t_desc_struct *cred;
      55             : 
      56             :     struct {
      57             :         unsigned int release_cred : 1;
      58             :     } flags;
      59             : };
      60             : 
      61             : struct krb5_get_init_creds_ctx {
      62             :     KDCOptions flags;
      63             :     krb5_creds cred;
      64             :     const krb5_addresses *addrs;
      65             :     krb5_enctype *etypes;
      66             :     krb5_preauthtype *pre_auth_types;
      67             :     char *in_tkt_service;
      68             :     unsigned nonce;
      69             :     unsigned pk_nonce;
      70             : 
      71             :     krb5_data req_buffer;
      72             :     AS_REQ as_req;
      73             :     int pa_counter;
      74             : 
      75             :     /* password and keytab_data is freed on completion */
      76             :     char *password;
      77             :     krb5_keytab_key_proc_args *keytab_data;
      78             : 
      79             :     krb5_pointer *keyseed;
      80             :     krb5_s2k_proc keyproc;
      81             : 
      82             :     krb5_get_init_creds_tristate req_pac;
      83             : 
      84             :     krb5_pk_init_ctx pk_init_ctx;
      85             :     krb5_gss_init_ctx gss_init_ctx;
      86             :     int ic_flags;
      87             : 
      88             :     char *kdc_hostname;
      89             :     char *sitename;
      90             : 
      91             :     struct {
      92             :         unsigned int change_password:1;
      93             :         unsigned int change_password_prompt:1;
      94             :         unsigned int allow_enc_pa_rep:1;
      95             :         unsigned int allow_save_as_reply_key:1;
      96             :     } runflags;
      97             : 
      98             :     struct pa_info_data paid;
      99             : 
     100             :     METHOD_DATA md;
     101             :     KRB_ERROR error;
     102             :     EncKDCRepPart enc_part;
     103             : 
     104             :     krb5_prompter_fct prompter;
     105             :     void *prompter_data;
     106             :     int warned_user;
     107             : 
     108             :     struct pa_info_data *ppaid;
     109             : 
     110             :     struct krb5_fast_state fast_state;
     111             :     krb5_enctype as_enctype;
     112             :     krb5_keyblock *as_reply_key;
     113             : 
     114             :     /* current and available pa mechansm in this exchange */
     115             :     struct pa_auth_mech *pa_mech;
     116             :     heim_array_t available_pa_mechs;
     117             :     const char *pa_used;
     118             : 
     119             :     struct {
     120             :         struct timeval run_time;
     121             :     } stats;
     122             : };
     123             : 
     124             : static void
     125       57944 : free_paid(krb5_context context, struct pa_info_data *ppaid)
     126             : {
     127       57944 :     krb5_free_salt(context, ppaid->salt);
     128       57944 :     if (ppaid->s2kparams)
     129       39779 :         krb5_free_data(context, ppaid->s2kparams);
     130       57944 :     memset(ppaid, 0, sizeof(*ppaid));
     131       57944 : }
     132             : 
     133             : static krb5_error_code KRB5_CALLCONV
     134       27832 : default_s2k_func(krb5_context context, krb5_enctype type,
     135             :                  krb5_const_pointer keyseed,
     136             :                  krb5_salt salt, krb5_data *s2kparms,
     137             :                  krb5_keyblock **key)
     138             : {
     139        1178 :     krb5_error_code ret;
     140        1178 :     krb5_data password;
     141        1178 :     krb5_data opaque;
     142             : 
     143       27832 :     if (_krb5_have_debug(context, 5)) {
     144           0 :         char *str = NULL;
     145           0 :         ret = krb5_enctype_to_string(context, type, &str);
     146           0 :         if (ret)
     147           0 :             return ret;
     148             : 
     149           0 :         _krb5_debug(context, 5, "krb5_get_init_creds: using default_s2k_func: %s (%d)", str, (int)type);
     150           0 :         free(str);
     151             :     }
     152             : 
     153       27832 :     password.data = rk_UNCONST(keyseed);
     154       27832 :     password.length = keyseed ? strlen(keyseed) : 0;
     155       27832 :     if (s2kparms)
     156       26616 :         opaque = *s2kparms;
     157             :     else
     158        1216 :         krb5_data_zero(&opaque);
     159             : 
     160       27832 :     *key = malloc(sizeof(**key));
     161       27832 :     if (*key == NULL)
     162           0 :         return krb5_enomem(context);
     163       27832 :     ret = krb5_string_to_key_data_salt_opaque(context, type, password,
     164             :                                               salt, opaque, *key);
     165       27832 :     if (ret) {
     166           0 :         free(*key);
     167           0 :         *key = NULL;
     168             :     }
     169       26654 :     return ret;
     170             : }
     171             : 
     172             : static void
     173       16303 : free_gss_init_ctx(krb5_context context, krb5_gss_init_ctx gssic)
     174             : {
     175       16303 :     if (gssic == NULL)
     176       15711 :         return;
     177             : 
     178           0 :     if (gssic->flags.release_cred)
     179           0 :         gssic->release_cred(context, gssic, gssic->cred);
     180           0 :     free(gssic);
     181             : }
     182             : 
     183             : static void
     184       16303 : free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx)
     185             : {
     186       16303 :     if (ctx->etypes)
     187          31 :         free(ctx->etypes);
     188       16303 :     if (ctx->pre_auth_types)
     189           0 :         free (ctx->pre_auth_types);
     190       16303 :     if (ctx->in_tkt_service)
     191           0 :         free(ctx->in_tkt_service);
     192       16303 :     if (ctx->keytab_data)
     193           7 :         free(ctx->keytab_data);
     194       16303 :     if (ctx->password) {
     195         589 :         size_t len;
     196       16158 :         len = strlen(ctx->password);
     197       16158 :         memset_s(ctx->password, len, 0, len);
     198       16158 :         free(ctx->password);
     199             :     }
     200       16303 :     free_gss_init_ctx(context, ctx->gss_init_ctx);
     201             :     /*
     202             :      * FAST state
     203             :      */
     204       16303 :     _krb5_fast_free(context, &ctx->fast_state);
     205       16303 :     if (ctx->as_reply_key)
     206          20 :         krb5_free_keyblock(context, ctx->as_reply_key);
     207             : 
     208       16303 :     krb5_data_free(&ctx->req_buffer);
     209       16303 :     krb5_free_cred_contents(context, &ctx->cred);
     210       16303 :     free_METHOD_DATA(&ctx->md);
     211       16303 :     free_EncKDCRepPart(&ctx->enc_part);
     212       16303 :     free_KRB_ERROR(&ctx->error);
     213       16303 :     free_AS_REQ(&ctx->as_req);
     214             : 
     215       16303 :     heim_release(ctx->available_pa_mechs);
     216       16303 :     heim_release(ctx->pa_mech);
     217       16303 :     ctx->pa_mech = NULL;
     218       16303 :     free(ctx->kdc_hostname);
     219       16303 :     free(ctx->sitename);
     220       16303 :     free_paid(context, &ctx->paid);
     221       16303 :     memset_s(ctx, sizeof(*ctx), 0, sizeof(*ctx));
     222       16303 : }
     223             : 
     224             : static krb5_deltat
     225        1466 : get_config_time (krb5_context context,
     226             :                  const char *realm,
     227             :                  const char *name,
     228             :                  int def)
     229             : {
     230           0 :     krb5_deltat ret;
     231             : 
     232        1466 :     ret = krb5_config_get_time (context, NULL,
     233             :                                 "realms",
     234             :                                 realm,
     235             :                                 name,
     236             :                                 NULL);
     237        1466 :     if (ret >= 0)
     238           0 :         return ret;
     239        1466 :     ret = krb5_config_get_time (context, NULL,
     240             :                                 "libdefaults",
     241             :                                 name,
     242             :                                 NULL);
     243        1466 :     if (ret >= 0)
     244           0 :         return ret;
     245        1466 :     return def;
     246             : }
     247             : 
     248             : static krb5_error_code
     249       16303 : init_cred (krb5_context context,
     250             :            krb5_creds *cred,
     251             :            krb5_principal client,
     252             :            krb5_deltat start_time,
     253             :            krb5_get_init_creds_opt *options)
     254             : {
     255         592 :     krb5_error_code ret;
     256         592 :     krb5_deltat tmp;
     257         592 :     krb5_timestamp now;
     258             : 
     259       16303 :     krb5_timeofday (context, &now);
     260             : 
     261       16303 :     memset (cred, 0, sizeof(*cred));
     262             : 
     263       16303 :     if (client)
     264       16303 :         ret = krb5_copy_principal(context, client, &cred->client);
     265             :     else
     266           0 :         ret = krb5_get_default_principal(context, &cred->client);
     267       16303 :     if (ret)
     268           0 :         goto out;
     269             : 
     270       16303 :     if (start_time)
     271           0 :         cred->times.starttime  = now + start_time;
     272             : 
     273       16303 :     if (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)
     274       12112 :         tmp = options->tkt_life;
     275             :     else
     276        4191 :         tmp = KRB5_TKT_LIFETIME_DEFAULT;
     277       16303 :     cred->times.endtime = now + tmp;
     278             : 
     279       16303 :     if ((options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE)) {
     280        1621 :         if (options->renew_life > 0)
     281          32 :             tmp = options->renew_life;
     282             :         else
     283        1589 :             tmp = KRB5_TKT_RENEW_LIFETIME_DEFAULT;
     284        1621 :         cred->times.renew_till = now + tmp;
     285             :     }
     286             : 
     287       15711 :     return 0;
     288             : 
     289           0 : out:
     290           0 :     krb5_free_cred_contents (context, cred);
     291           0 :     return ret;
     292             : }
     293             : 
     294             : /*
     295             :  * Print a message (str) to the user about the expiration in `lr'
     296             :  */
     297             : 
     298             : static void
     299           4 : report_expiration (krb5_context context,
     300             :                    krb5_prompter_fct prompter,
     301             :                    krb5_data *data,
     302             :                    const char *str,
     303             :                    time_t now)
     304             : {
     305           4 :     char *p = NULL;
     306             : 
     307           4 :     if (asprintf(&p, "%s%s", str, ctime(&now)) < 0 || p == NULL)
     308           0 :         return;
     309           4 :     (*prompter)(context, data, NULL, p, 0, NULL);
     310           4 :     free(p);
     311             : }
     312             : 
     313             : /*
     314             :  * Check the context, and in the case there is a expiration warning,
     315             :  * use the prompter to print the warning.
     316             :  *
     317             :  * @param context A Kerberos 5 context.
     318             :  * @param options An GIC options structure
     319             :  * @param ctx The krb5_init_creds_context check for expiration.
     320             :  */
     321             : 
     322             : krb5_error_code
     323       13757 : krb5_process_last_request(krb5_context context,
     324             :                           krb5_get_init_creds_opt *options,
     325             :                           krb5_init_creds_context ctx)
     326             : {
     327         592 :     LastReq *lr;
     328         592 :     size_t i;
     329             : 
     330             :     /*
     331             :      * First check if there is a API consumer.
     332             :      */
     333             : 
     334       13757 :     lr = &ctx->enc_part.last_req;
     335             : 
     336       13757 :     if (options && options->opt_private && options->opt_private->lr.func) {
     337           0 :         krb5_last_req_entry **lre;
     338             : 
     339           0 :         lre = calloc(lr->len + 1, sizeof(*lre));
     340           0 :         if (lre == NULL)
     341           0 :             return krb5_enomem(context);
     342             : 
     343           0 :         for (i = 0; i < lr->len; i++) {
     344           0 :             lre[i] = calloc(1, sizeof(*lre[i]));
     345           0 :             if (lre[i] == NULL)
     346           0 :                 break;
     347           0 :             lre[i]->lr_type = lr->val[i].lr_type;
     348           0 :             lre[i]->value = lr->val[i].lr_value;
     349             :         }
     350             : 
     351           0 :         (*options->opt_private->lr.func)(context, lre,
     352           0 :                                          options->opt_private->lr.ctx);
     353             : 
     354           0 :         for (i = 0; i < lr->len; i++)
     355           0 :             free(lre[i]);
     356           0 :         free(lre);
     357             :     }
     358             : 
     359       13757 :     return krb5_init_creds_warn_user(context, ctx);
     360             : }
     361             : 
     362             : /**
     363             :  * Warn the user using prompter in the krb5_init_creds_context about
     364             :  * possible password and account expiration.
     365             :  *
     366             :  * @param context a Kerberos 5 context.
     367             :  * @param ctx a krb5_init_creds_context context.
     368             :  *
     369             :  * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
     370             :  * @ingroup krb5_credential
     371             :  */
     372             : 
     373             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
     374       13870 : krb5_init_creds_warn_user(krb5_context context,
     375             :                           krb5_init_creds_context ctx)
     376             : {
     377         592 :     krb5_timestamp sec;
     378         592 :     krb5_const_realm realm;
     379       13870 :     krb5_enctype weak_enctype = KRB5_ENCTYPE_NULL;
     380         592 :     LastReq *lr;
     381         592 :     unsigned i;
     382         592 :     time_t t;
     383             : 
     384       13870 :     if (ctx->prompter == NULL)
     385       11699 :         return 0;
     386             : 
     387        1579 :     if (ctx->warned_user)
     388         113 :         return 0;
     389             : 
     390        1466 :     ctx->warned_user = 1;
     391             : 
     392        1466 :     krb5_timeofday (context, &sec);
     393             : 
     394        1466 :     realm = krb5_principal_get_realm (context, ctx->cred.client);
     395        1466 :     lr = &ctx->enc_part.last_req;
     396             : 
     397        1466 :     t = sec + get_config_time (context,
     398             :                                realm,
     399             :                                "warn_pwexpire",
     400             :                                7 * 24 * 60 * 60);
     401             : 
     402        2932 :     for (i = 0; i < lr->len; ++i) {
     403        1466 :         if (lr->val[i].lr_value <= t) {
     404         175 :             switch (lr->val[i].lr_type) {
     405           4 :             case LR_PW_EXPTIME :
     406           4 :                 report_expiration(context, ctx->prompter,
     407           4 :                                   ctx->prompter_data,
     408             :                                   "Your password will expire at ",
     409           4 :                                   lr->val[i].lr_value);
     410           4 :                 break;
     411           0 :             case LR_ACCT_EXPTIME :
     412           0 :                 report_expiration(context, ctx->prompter,
     413           0 :                                   ctx->prompter_data,
     414             :                                   "Your account will expire at ",
     415           0 :                                   lr->val[i].lr_value);
     416           0 :                 break;
     417         171 :             default:
     418         171 :                 break;
     419             :             }
     420             :         }
     421             :     }
     422             : 
     423        1466 :     if (krb5_is_enctype_weak(context, ctx->as_enctype))
     424          41 :         weak_enctype = ctx->as_enctype;
     425        1425 :     else if (krb5_is_enctype_weak(context, ctx->cred.session.keytype))
     426           1 :         weak_enctype = ctx->cred.session.keytype;
     427             : 
     428        1466 :     if (ctx->prompter && weak_enctype != KRB5_ENCTYPE_NULL) {
     429          42 :         int suppress = krb5_config_get_bool_default(context, NULL, false,
     430             :                                                     "libdefaults",
     431             :                                                     "suppress_weak_enctype", NULL);
     432          42 :         if (!suppress) {
     433          42 :             char *str = NULL, *p = NULL;
     434           0 :             int aret;
     435             : 
     436          42 :             (void) krb5_enctype_to_string(context, weak_enctype, &str);
     437          42 :             aret = asprintf(&p, "Encryption type %s(%d) used for authentication is weak and will be deprecated",
     438          42 :                             str ? str : "unknown", weak_enctype);
     439          42 :             if (aret >= 0 && p) {
     440          42 :                 (*ctx->prompter)(context, ctx->prompter_data, NULL, p, 0, NULL);
     441          42 :                 free(p);
     442             :             }
     443          42 :             free(str);
     444             :         }
     445             :     }
     446             : 
     447        1466 :     return 0;
     448             : }
     449             : 
     450             : static const krb5_addresses no_addrs = { 0, NULL };
     451             : 
     452             : static krb5_error_code
     453       16303 : get_init_creds_common(krb5_context context,
     454             :                       krb5_principal client,
     455             :                       krb5_prompter_fct prompter,
     456             :                       void *prompter_data,
     457             :                       krb5_deltat start_time,
     458             :                       krb5_get_init_creds_opt *options,
     459             :                       krb5_init_creds_context ctx)
     460             : {
     461       16303 :     krb5_get_init_creds_opt *default_opt = NULL;
     462         592 :     krb5_error_code ret;
     463         592 :     krb5_enctype *etypes;
     464         592 :     krb5_preauthtype *pre_auth_types;
     465             : 
     466       16303 :     memset(ctx, 0, sizeof(*ctx));
     467             : 
     468       16303 :     if (options == NULL) {
     469          48 :         const char *realm = krb5_principal_get_realm(context, client);
     470             : 
     471          48 :         ret = krb5_get_init_creds_opt_alloc(context, &default_opt);
     472          48 :         if (ret)
     473           0 :             return ret;
     474          48 :         options = default_opt;
     475          48 :         krb5_get_init_creds_opt_set_default_flags(context, NULL, realm, options);
     476             :     }
     477             : 
     478       16303 :     if (options->opt_private) {
     479       16303 :         if (options->opt_private->password) {
     480           0 :             ret = krb5_init_creds_set_password(context, ctx,
     481           0 :                                                options->opt_private->password);
     482           0 :             if (ret)
     483           0 :                 goto out;
     484             :         }
     485             : 
     486       16303 :         ctx->keyproc = options->opt_private->key_proc;
     487       16303 :         ctx->req_pac = options->opt_private->req_pac;
     488       16303 :         ctx->pk_init_ctx = options->opt_private->pk_init_ctx;
     489       16303 :         ctx->ic_flags = options->opt_private->flags;
     490             :     } else
     491           0 :         ctx->req_pac = KRB5_INIT_CREDS_TRISTATE_UNSET;
     492             : 
     493       16303 :     if (ctx->keyproc == NULL)
     494       16303 :         ctx->keyproc = default_s2k_func;
     495             : 
     496       16303 :     if (ctx->ic_flags & KRB5_INIT_CREDS_CANONICALIZE)
     497       14532 :         ctx->flags.canonicalize = 1;
     498       16303 :     if (krb5_principal_get_type(context, client) == KRB5_NT_ENTERPRISE_PRINCIPAL)
     499        1008 :         ctx->flags.canonicalize = 1;
     500             : 
     501       16303 :     ctx->pre_auth_types = NULL;
     502       16303 :     ctx->addrs = NULL;
     503       16303 :     ctx->etypes = NULL;
     504       16303 :     ctx->pre_auth_types = NULL;
     505             : 
     506       16303 :     ret = init_cred(context, &ctx->cred, client, start_time, options);
     507       16303 :     if (ret)
     508           0 :         goto out;
     509             : 
     510       16303 :     ret = krb5_init_creds_set_service(context, ctx, NULL);
     511       16303 :     if (ret)
     512           0 :         goto out;
     513             : 
     514       16303 :     if (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)
     515       13594 :         ctx->flags.forwardable = options->forwardable;
     516             : 
     517       16303 :     if (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)
     518       12008 :         ctx->flags.proxiable = options->proxiable;
     519             : 
     520       16303 :     if (start_time)
     521           0 :         ctx->flags.postdated = 1;
     522       16303 :     if (ctx->cred.times.renew_till)
     523        1621 :         ctx->flags.renewable = 1;
     524       16303 :     if (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) {
     525           3 :         ctx->addrs = options->address_list;
     526       16300 :     } else if (options->opt_private) {
     527       16300 :         switch (options->opt_private->addressless) {
     528        4305 :         case KRB5_INIT_CREDS_TRISTATE_UNSET:
     529             : #if KRB5_ADDRESSLESS_DEFAULT == TRUE
     530        4305 :             ctx->addrs = &no_addrs;
     531             : #else
     532             :             ctx->addrs = NULL;
     533             : #endif
     534        4305 :             break;
     535           0 :         case KRB5_INIT_CREDS_TRISTATE_FALSE:
     536           0 :             ctx->addrs = NULL;
     537           0 :             break;
     538       11995 :         case KRB5_INIT_CREDS_TRISTATE_TRUE:
     539       11995 :             ctx->addrs = &no_addrs;
     540       11995 :             break;
     541             :         }
     542             :     }
     543       16303 :     if (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) {
     544          26 :         if (ctx->etypes)
     545           0 :             free(ctx->etypes);
     546             : 
     547          26 :         etypes = malloc((options->etype_list_length + 1)
     548             :                         * sizeof(krb5_enctype));
     549          26 :         if (etypes == NULL) {
     550           0 :             ret = krb5_enomem(context);
     551           0 :             goto out;
     552             :         }
     553          26 :         memcpy (etypes, options->etype_list,
     554          26 :                 options->etype_list_length * sizeof(krb5_enctype));
     555          26 :         etypes[options->etype_list_length] = ETYPE_NULL;
     556          26 :         ctx->etypes = etypes;
     557             :     }
     558       16303 :     if (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) {
     559           0 :         pre_auth_types = malloc((options->preauth_list_length + 1)
     560             :                                 * sizeof(krb5_preauthtype));
     561           0 :         if (pre_auth_types == NULL) {
     562           0 :             ret = krb5_enomem(context);
     563           0 :             goto out;
     564             :         }
     565           0 :         memcpy (pre_auth_types, options->preauth_list,
     566           0 :                 options->preauth_list_length * sizeof(krb5_preauthtype));
     567           0 :         pre_auth_types[options->preauth_list_length] = KRB5_PADATA_NONE;
     568           0 :         ctx->pre_auth_types = pre_auth_types;
     569             :     }
     570       16303 :     if (options->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS)
     571         104 :         ctx->flags.request_anonymous = options->anonymous;
     572             : 
     573       16303 :     ctx->prompter = prompter;
     574       16303 :     ctx->prompter_data = prompter_data;
     575             : 
     576       16303 :     if ((options->flags & KRB5_GET_INIT_CREDS_OPT_CHANGE_PASSWORD_PROMPT) &&
     577           0 :         !options->change_password_prompt)
     578           0 :         ctx->runflags.change_password_prompt = 0;
     579             :     else
     580       16303 :         ctx->runflags.change_password_prompt = ctx->prompter != NULL;
     581             : 
     582       16303 :     if (options->opt_private) {
     583       16303 :         if (options->opt_private->fast_armor_ccache_name) {
     584             :             /* Open the caller-supplied FAST ccache and set the caller flags */
     585          10 :             ret = krb5_cc_resolve(context, options->opt_private->fast_armor_ccache_name,
     586             :                                   &ctx->fast_state.armor_ccache);
     587          10 :             if (ret)
     588           0 :                 goto out;
     589             :         }
     590             : 
     591       16303 :         ctx->fast_state.flags = options->opt_private->fast_flags;
     592             :     }
     593             : 
     594             :     /*
     595             :      * If FAST is required with a real credential cache, then the KDC
     596             :      * will be verified.  This allows the
     597             :      * krb5_get_init_creds_opt_set_fast API to work like MIT without
     598             :      * exposing KRB5_FAST_KDC_VERIFIED to callers
     599             :      */
     600       16303 :     if (ctx->fast_state.flags & KRB5_FAST_REQUIRED)
     601          10 :         ctx->fast_state.flags |= KRB5_FAST_KDC_VERIFIED;
     602             : 
     603       16293 :  out:
     604       16303 :     if (default_opt)
     605          48 :         krb5_get_init_creds_opt_free(context, default_opt);
     606       15711 :     return ret;
     607             : }
     608             : 
     609             : static krb5_error_code
     610           4 : change_password (krb5_context context,
     611             :                  krb5_principal client,
     612             :                  const char *password,
     613             :                  char *newpw,
     614             :                  size_t newpw_sz,
     615             :                  krb5_prompter_fct prompter,
     616             :                  void *data,
     617             :                  krb5_get_init_creds_opt *old_options)
     618             : {
     619           0 :     krb5_prompt prompts[2];
     620           0 :     krb5_error_code ret;
     621           0 :     krb5_creds cpw_cred;
     622           0 :     char buf1[BUFSIZ], buf2[BUFSIZ];
     623           0 :     krb5_data password_data[2];
     624           0 :     int result_code;
     625           0 :     krb5_data result_code_string;
     626           0 :     krb5_data result_string;
     627           0 :     char *p;
     628           0 :     krb5_get_init_creds_opt *options;
     629             : 
     630           4 :     heim_assert(prompter != NULL, "unexpected NULL prompter");
     631             : 
     632           4 :     memset (&cpw_cred, 0, sizeof(cpw_cred));
     633             : 
     634           4 :     ret = krb5_get_init_creds_opt_alloc(context, &options);
     635           4 :     if (ret)
     636           0 :         return ret;
     637           4 :     krb5_get_init_creds_opt_set_tkt_life (options, 60);
     638           4 :     krb5_get_init_creds_opt_set_forwardable (options, FALSE);
     639           4 :     krb5_get_init_creds_opt_set_proxiable (options, FALSE);
     640           4 :     if (old_options &&
     641           0 :         (old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST))
     642           0 :         krb5_get_init_creds_opt_set_preauth_list(options,
     643             :                                                  old_options->preauth_list,
     644             :                                                  old_options->preauth_list_length);
     645           4 :     if (old_options &&
     646           0 :         (old_options->flags & KRB5_GET_INIT_CREDS_OPT_CHANGE_PASSWORD_PROMPT))
     647           0 :         krb5_get_init_creds_opt_set_change_password_prompt(options,
     648             :                                                            old_options->change_password_prompt);
     649             : 
     650           4 :     krb5_data_zero (&result_code_string);
     651           4 :     krb5_data_zero (&result_string);
     652             : 
     653           4 :     ret = krb5_get_init_creds_password (context,
     654             :                                         &cpw_cred,
     655             :                                         client,
     656             :                                         password,
     657             :                                         prompter,
     658             :                                         data,
     659             :                                         0,
     660             :                                         "kadmin/changepw",
     661             :                                         options);
     662           4 :     krb5_get_init_creds_opt_free(context, options);
     663           4 :     if (ret)
     664           0 :         goto out;
     665             : 
     666           0 :     for(;;) {
     667           4 :         password_data[0].data   = buf1;
     668           4 :         password_data[0].length = sizeof(buf1);
     669             : 
     670           4 :         prompts[0].hidden = 1;
     671           4 :         prompts[0].prompt = "New password: ";
     672           4 :         prompts[0].reply  = &password_data[0];
     673           4 :         prompts[0].type   = KRB5_PROMPT_TYPE_NEW_PASSWORD;
     674             : 
     675           4 :         password_data[1].data   = buf2;
     676           4 :         password_data[1].length = sizeof(buf2);
     677             : 
     678           4 :         prompts[1].hidden = 1;
     679           4 :         prompts[1].prompt = "Repeat new password: ";
     680           4 :         prompts[1].reply  = &password_data[1];
     681           4 :         prompts[1].type   = KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN;
     682             : 
     683           4 :         ret = (*prompter) (context, data, NULL, "Changing password",
     684             :                            2, prompts);
     685           4 :         if (ret) {
     686           0 :             memset (buf1, 0, sizeof(buf1));
     687           0 :             memset (buf2, 0, sizeof(buf2));
     688           0 :             goto out;
     689             :         }
     690             : 
     691           4 :         if (strcmp (buf1, buf2) == 0)
     692           4 :             break;
     693           0 :         memset (buf1, 0, sizeof(buf1));
     694           0 :         memset (buf2, 0, sizeof(buf2));
     695             :     }
     696             : 
     697           4 :     ret = krb5_set_password (context,
     698             :                              &cpw_cred,
     699             :                              buf1,
     700             :                              client,
     701             :                              &result_code,
     702             :                              &result_code_string,
     703             :                              &result_string);
     704           4 :     if (ret)
     705           0 :         goto out;
     706             : 
     707           8 :     if (asprintf(&p, "%s: %.*s\n",
     708           4 :                  result_code ? "Error" : "Success",
     709           4 :                  (int)result_string.length,
     710           4 :                  result_string.length > 0 ? (char*)result_string.data : "") < 0)
     711             :     {
     712           0 :         ret = krb5_enomem(context);
     713           0 :         goto out;
     714             :     }
     715             : 
     716             :     /* return the result */
     717           4 :     (*prompter) (context, data, NULL, p, 0, NULL);
     718             : 
     719           4 :     if (result_code == 0) {
     720           4 :         strlcpy (newpw, buf1, newpw_sz);
     721           4 :         ret = 0;
     722             :     } else {
     723           0 :         krb5_set_error_message(context, ret = KRB5_CHPW_FAIL,
     724           0 :                                N_("failed changing password: %s", ""), p);
     725             :     }
     726           4 :     free (p);
     727             : 
     728           4 : out:
     729           4 :     memset_s(buf1, sizeof(buf1), 0, sizeof(buf1));
     730           4 :     memset_s(buf2, sizeof(buf2), 0, sizeof(buf2));
     731           4 :     krb5_data_free (&result_string);
     732           4 :     krb5_data_free (&result_code_string);
     733           4 :     krb5_free_cred_contents (context, &cpw_cred);
     734           4 :     return ret;
     735             : }
     736             : 
     737             : 
     738             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
     739           0 : krb5_keyblock_key_proc (krb5_context context,
     740             :                         krb5_keytype type,
     741             :                         krb5_data *salt,
     742             :                         krb5_const_pointer keyseed,
     743             :                         krb5_keyblock **key)
     744             : {
     745           0 :     return krb5_copy_keyblock (context, keyseed, key);
     746             : }
     747             : 
     748             : /*
     749             :  *
     750             :  */
     751             : 
     752             : static krb5_error_code
     753       16303 : init_as_req (krb5_context context,
     754             :              KDCOptions opts,
     755             :              const krb5_creds *creds,
     756             :              const krb5_addresses *addrs,
     757             :              const krb5_enctype *etypes,
     758             :              AS_REQ *a)
     759             : {
     760         592 :     krb5_error_code ret;
     761             : 
     762       16303 :     memset(a, 0, sizeof(*a));
     763             : 
     764       16303 :     a->pvno = 5;
     765       16303 :     a->msg_type = krb_as_req;
     766       16303 :     a->req_body.kdc_options = opts;
     767       16303 :     a->req_body.cname = calloc(1, sizeof(*a->req_body.cname));
     768       16303 :     if (a->req_body.cname == NULL) {
     769           0 :         ret = krb5_enomem(context);
     770           0 :         goto fail;
     771             :     }
     772       16303 :     a->req_body.sname = calloc(1, sizeof(*a->req_body.sname));
     773       16303 :     if (a->req_body.sname == NULL) {
     774           0 :         ret = krb5_enomem(context);
     775           0 :         goto fail;
     776             :     }
     777             : 
     778       16303 :     ret = _krb5_principal2principalname (a->req_body.cname, creds->client);
     779       16303 :     if (ret)
     780           0 :         goto fail;
     781       16303 :     ret = copy_Realm(&creds->client->realm, &a->req_body.realm);
     782       16303 :     if (ret)
     783           0 :         goto fail;
     784             : 
     785       16303 :     ret = _krb5_principal2principalname (a->req_body.sname, creds->server);
     786       16303 :     if (ret)
     787           0 :         goto fail;
     788             : 
     789       16303 :     if(creds->times.starttime) {
     790           0 :         a->req_body.from = malloc(sizeof(*a->req_body.from));
     791           0 :         if (a->req_body.from == NULL) {
     792           0 :             ret = krb5_enomem(context);
     793           0 :             goto fail;
     794             :         }
     795           0 :         *a->req_body.from = creds->times.starttime;
     796             :     }
     797       16303 :     if(creds->times.endtime){
     798       16303 :         if ((ALLOC(a->req_body.till, 1)) != NULL)
     799       16303 :             *a->req_body.till = creds->times.endtime;
     800             :         else {
     801           0 :             ret = krb5_enomem(context);
     802           0 :             goto fail;
     803             :         }
     804             :     }
     805       16303 :     if(creds->times.renew_till){
     806        1621 :         a->req_body.rtime = malloc(sizeof(*a->req_body.rtime));
     807        1621 :         if (a->req_body.rtime == NULL) {
     808           0 :             ret = krb5_enomem(context);
     809           0 :             goto fail;
     810             :         }
     811        1621 :         *a->req_body.rtime = creds->times.renew_till;
     812             :     }
     813       16303 :     a->req_body.nonce = 0;
     814       16895 :     ret = _krb5_init_etype(context,
     815             :                            KRB5_PDU_AS_REQUEST,
     816             :                            &a->req_body.etype.len,
     817       16303 :                            &a->req_body.etype.val,
     818             :                            etypes);
     819       16303 :     if (ret)
     820           0 :         goto fail;
     821             : 
     822             :     /*
     823             :      * This means no addresses
     824             :      */
     825             : 
     826       16303 :     if (addrs && addrs->len == 0) {
     827       16300 :         a->req_body.addresses = NULL;
     828             :     } else {
     829           3 :         a->req_body.addresses = malloc(sizeof(*a->req_body.addresses));
     830           3 :         if (a->req_body.addresses == NULL) {
     831           0 :             ret = krb5_enomem(context);
     832           0 :             goto fail;
     833             :         }
     834             : 
     835           3 :         if (addrs)
     836           3 :             ret = krb5_copy_addresses(context, addrs, a->req_body.addresses);
     837             :         else {
     838           0 :             ret = krb5_get_all_client_addrs (context, a->req_body.addresses);
     839           0 :             if(ret == 0 && a->req_body.addresses->len == 0) {
     840           0 :                 free(a->req_body.addresses);
     841           0 :                 a->req_body.addresses = NULL;
     842             :             }
     843             :         }
     844           3 :         if (ret)
     845           0 :             goto fail;
     846             :     }
     847             : 
     848       16303 :     a->req_body.enc_authorization_data = NULL;
     849       16303 :     a->req_body.additional_tickets = NULL;
     850             : 
     851       16303 :     a->padata = NULL;
     852             : 
     853       16303 :     return 0;
     854           0 :  fail:
     855           0 :     free_AS_REQ(a);
     856           0 :     memset_s(a, sizeof(*a), 0, sizeof(*a));
     857           0 :     return ret;
     858             : }
     859             : 
     860             : 
     861             : static krb5_error_code
     862       40388 : set_paid(struct pa_info_data *paid, krb5_context context,
     863             :          krb5_enctype etype,
     864             :          krb5_salttype salttype, void *salt_string, size_t salt_len,
     865             :          krb5_data *s2kparams)
     866             : {
     867       40388 :     paid->etype = etype;
     868       40388 :     paid->salt.salttype = salttype;
     869       40388 :     paid->salt.saltvalue.data = malloc(salt_len + 1);
     870       40388 :     if (paid->salt.saltvalue.data == NULL) {
     871           0 :         krb5_clear_error_message(context);
     872           0 :         return krb5_enomem(context);
     873             :     }
     874       40388 :     memcpy(paid->salt.saltvalue.data, salt_string, salt_len);
     875       40388 :     ((char *)paid->salt.saltvalue.data)[salt_len] = '\0';
     876       40388 :     paid->salt.saltvalue.length = salt_len;
     877       40388 :     if (s2kparams) {
     878        1761 :         krb5_error_code ret;
     879             : 
     880       39779 :         ret = krb5_copy_data(context, s2kparams, &paid->s2kparams);
     881       39779 :         if (ret) {
     882           0 :             krb5_clear_error_message(context);
     883           0 :             krb5_free_salt(context, paid->salt);
     884           0 :             return ret;
     885             :         }
     886             :     } else
     887         609 :         paid->s2kparams = NULL;
     888             : 
     889       38624 :     return 0;
     890             : }
     891             : 
     892             : static struct pa_info_data *
     893       40388 : pa_etype_info2(krb5_context context,
     894             :                const krb5_principal client,
     895             :                const AS_REQ *asreq,
     896             :                struct pa_info_data *paid,
     897             :                heim_octet_string *data)
     898             : {
     899        1764 :     krb5_error_code ret;
     900        1764 :     ETYPE_INFO2 e;
     901        1764 :     size_t sz;
     902        1764 :     size_t i, j;
     903             : 
     904       40388 :     memset(&e, 0, sizeof(e));
     905       40388 :     ret = decode_ETYPE_INFO2(data->data, data->length, &e, &sz);
     906       40388 :     if (ret)
     907           0 :         goto out;
     908       40388 :     if (e.len == 0)
     909           0 :         goto out;
     910       41438 :     for (j = 0; j < asreq->req_body.etype.len; j++) {
     911       42488 :         for (i = 0; i < e.len; i++) {
     912             : 
     913       41438 :             if (krb5_enctype_valid(context, e.val[i].etype) != 0)
     914           0 :                 continue;
     915             : 
     916       41438 :             if (asreq->req_body.etype.val[j] == e.val[i].etype) {
     917        1764 :                 krb5_salt salt;
     918       40388 :                 if (e.val[i].salt == NULL)
     919         615 :                     ret = krb5_get_pw_salt(context, client, &salt);
     920             :                 else {
     921       39773 :                     salt.saltvalue.data = *e.val[i].salt;
     922       39773 :                     salt.saltvalue.length = strlen(*e.val[i].salt);
     923       39773 :                     ret = 0;
     924             :                 }
     925       40388 :                 if (ret == 0)
     926       40388 :                     ret = set_paid(paid, context, e.val[i].etype,
     927             :                                    KRB5_PW_SALT,
     928             :                                    salt.saltvalue.data,
     929             :                                    salt.saltvalue.length,
     930       40388 :                                    e.val[i].s2kparams);
     931       40388 :                 if (e.val[i].salt == NULL)
     932         615 :                     krb5_free_salt(context, salt);
     933       40388 :                 if (ret == 0) {
     934       40388 :                     free_ETYPE_INFO2(&e);
     935       40388 :                     return paid;
     936             :                 }
     937             :             }
     938             :         }
     939             :     }
     940           0 :  out:
     941           0 :     free_ETYPE_INFO2(&e);
     942           0 :     return NULL;
     943             : }
     944             : 
     945             : static struct pa_info_data *
     946           0 : pa_etype_info(krb5_context context,
     947             :               const krb5_principal client,
     948             :               const AS_REQ *asreq,
     949             :               struct pa_info_data *paid,
     950             :               heim_octet_string *data)
     951             : {
     952           0 :     krb5_error_code ret;
     953           0 :     ETYPE_INFO e;
     954           0 :     size_t sz;
     955           0 :     size_t i, j;
     956             : 
     957           0 :     memset(&e, 0, sizeof(e));
     958           0 :     ret = decode_ETYPE_INFO(data->data, data->length, &e, &sz);
     959           0 :     if (ret)
     960           0 :         goto out;
     961           0 :     if (e.len == 0)
     962           0 :         goto out;
     963           0 :     for (j = 0; j < asreq->req_body.etype.len; j++) {
     964           0 :         for (i = 0; i < e.len; i++) {
     965             : 
     966           0 :             if (krb5_enctype_valid(context, e.val[i].etype) != 0)
     967           0 :                 continue;
     968             : 
     969           0 :             if (asreq->req_body.etype.val[j] == e.val[i].etype) {
     970           0 :                 krb5_salt salt;
     971           0 :                 salt.salttype = KRB5_PW_SALT;
     972           0 :                 if (e.val[i].salt == NULL)
     973           0 :                     ret = krb5_get_pw_salt(context, client, &salt);
     974             :                 else {
     975           0 :                     salt.saltvalue = *e.val[i].salt;
     976           0 :                     ret = 0;
     977             :                 }
     978           0 :                 if (e.val[i].salttype)
     979           0 :                     salt.salttype = *e.val[i].salttype;
     980           0 :                 if (ret == 0) {
     981           0 :                     ret = set_paid(paid, context, e.val[i].etype,
     982             :                                    salt.salttype,
     983             :                                    salt.saltvalue.data,
     984             :                                    salt.saltvalue.length,
     985             :                                    NULL);
     986           0 :                     if (e.val[i].salt == NULL)
     987           0 :                         krb5_free_salt(context, salt);
     988             :                 }
     989           0 :                 if (ret == 0) {
     990           0 :                     free_ETYPE_INFO(&e);
     991           0 :                     return paid;
     992             :                 }
     993             :             }
     994             :         }
     995             :     }
     996           0 :  out:
     997           0 :     free_ETYPE_INFO(&e);
     998           0 :     return NULL;
     999             : }
    1000             : 
    1001             : static struct pa_info_data *
    1002           0 : pa_pw_or_afs3_salt(krb5_context context,
    1003             :                    const krb5_principal client,
    1004             :                    const AS_REQ *asreq,
    1005             :                    struct pa_info_data *paid,
    1006             :                    heim_octet_string *data)
    1007             : {
    1008           0 :     krb5_error_code ret;
    1009           0 :     if (paid->etype == KRB5_ENCTYPE_NULL)
    1010           0 :         return NULL;
    1011           0 :     if (krb5_enctype_valid(context, paid->etype) != 0)
    1012           0 :         return NULL;
    1013             : 
    1014           0 :     ret = set_paid(paid, context,
    1015             :                    paid->etype,
    1016             :                    paid->salt.salttype,
    1017             :                    data->data,
    1018             :                    data->length,
    1019             :                    NULL);
    1020           0 :     if (ret)
    1021           0 :         return NULL;
    1022           0 :     return paid;
    1023             : }
    1024             : 
    1025             : 
    1026             : static krb5_error_code
    1027       14109 : make_pa_enc_timestamp(krb5_context context, METHOD_DATA *md,
    1028             :                       krb5_enctype etype, krb5_keyblock *key)
    1029             : {
    1030         592 :     PA_ENC_TS_ENC p;
    1031         592 :     unsigned char *buf;
    1032         592 :     size_t buf_size;
    1033       14109 :     size_t len = 0;
    1034         592 :     EncryptedData encdata;
    1035         592 :     krb5_error_code ret;
    1036         592 :     int32_t usec;
    1037         592 :     int usec2;
    1038         592 :     krb5_crypto crypto;
    1039             : 
    1040       14109 :     krb5_us_timeofday (context, &p.patimestamp, &usec);
    1041       14109 :     usec2         = usec;
    1042       14109 :     p.pausec      = &usec2;
    1043             : 
    1044       14109 :     ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret);
    1045       14109 :     if (ret)
    1046           0 :         return ret;
    1047       14109 :     if(buf_size != len)
    1048           0 :         krb5_abortx(context, "internal error in ASN.1 encoder");
    1049             : 
    1050       14109 :     ret = krb5_crypto_init(context, key, 0, &crypto);
    1051       14109 :     if (ret) {
    1052           0 :         free(buf);
    1053           0 :         return ret;
    1054             :     }
    1055       14109 :     ret = krb5_encrypt_EncryptedData(context,
    1056             :                                      crypto,
    1057             :                                      KRB5_KU_PA_ENC_TIMESTAMP,
    1058             :                                      buf,
    1059             :                                      len,
    1060             :                                      0,
    1061             :                                      &encdata);
    1062       14109 :     free(buf);
    1063       14109 :     krb5_crypto_destroy(context, crypto);
    1064       14109 :     if (ret)
    1065           0 :         return ret;
    1066             : 
    1067       14109 :     ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret);
    1068       14109 :     free_EncryptedData(&encdata);
    1069       14109 :     if (ret)
    1070           0 :         return ret;
    1071       14109 :     if(buf_size != len)
    1072           0 :         krb5_abortx(context, "internal error in ASN.1 encoder");
    1073             : 
    1074       14109 :     ret = krb5_padata_add(context, md, KRB5_PADATA_ENC_TIMESTAMP, buf, len);
    1075       14109 :     if (ret)
    1076           0 :         free(buf);
    1077       13517 :     return ret;
    1078             : }
    1079             : 
    1080             : static krb5_error_code
    1081       14109 : add_enc_ts_padata(krb5_context context,
    1082             :                   METHOD_DATA *md,
    1083             :                   krb5_principal client,
    1084             :                   krb5_s2k_proc keyproc,
    1085             :                   krb5_const_pointer keyseed,
    1086             :                   krb5_enctype *enctypes,
    1087             :                   unsigned netypes,
    1088             :                   krb5_salt *salt,
    1089             :                   krb5_data *s2kparams)
    1090             : {
    1091         592 :     krb5_error_code ret;
    1092         592 :     krb5_salt salt2;
    1093         592 :     krb5_enctype *ep;
    1094         592 :     size_t i;
    1095             : 
    1096       14109 :     memset(&salt2, 0, sizeof(salt2));
    1097             : 
    1098       14109 :     if(salt == NULL) {
    1099             :         /* default to standard salt */
    1100           0 :         ret = krb5_get_pw_salt (context, client, &salt2);
    1101           0 :         if (ret)
    1102           0 :             return ret;
    1103           0 :         salt = &salt2;
    1104             :     }
    1105       14109 :     if (!enctypes) {
    1106           0 :         enctypes = context->etypes;
    1107           0 :         netypes = 0;
    1108           0 :         for (ep = enctypes; *ep != ETYPE_NULL; ep++)
    1109           0 :             netypes++;
    1110             :     }
    1111             : 
    1112       28218 :     for (i = 0; i < netypes; ++i) {
    1113         592 :         krb5_keyblock *key;
    1114             : 
    1115       14109 :         _krb5_debug(context, 5, "krb5_get_init_creds: using ENC-TS with enctype %d", enctypes[i]);
    1116             : 
    1117       14109 :         ret = (*keyproc)(context, enctypes[i], keyseed,
    1118             :                          *salt, s2kparams, &key);
    1119       14109 :         if (ret)
    1120           0 :             continue;
    1121       14109 :         ret = make_pa_enc_timestamp (context, md, enctypes[i], key);
    1122       14109 :         krb5_free_keyblock (context, key);
    1123       14109 :         if (ret)
    1124           0 :             return ret;
    1125             :     }
    1126       14109 :     if(salt == &salt2)
    1127           0 :         krb5_free_salt(context, salt2);
    1128       13517 :     return 0;
    1129             : }
    1130             : 
    1131             : static krb5_error_code
    1132       14109 : pa_data_to_md_ts_enc(krb5_context context,
    1133             :                      const AS_REQ *a,
    1134             :                      const krb5_principal client,
    1135             :                      krb5_init_creds_context ctx,
    1136             :                      struct pa_info_data *ppaid,
    1137             :                      METHOD_DATA *md)
    1138             : {
    1139       14109 :     if (ctx->keyproc == NULL || ctx->keyseed == NULL)
    1140           0 :         return 0;
    1141             : 
    1142       14109 :     if (ppaid) {
    1143       14109 :         add_enc_ts_padata(context, md, client,
    1144       13517 :                           ctx->keyproc, ctx->keyseed,
    1145             :                           &ppaid->etype, 1,
    1146             :                           &ppaid->salt, ppaid->s2kparams);
    1147             :     } else {
    1148           0 :         krb5_salt salt;
    1149             : 
    1150           0 :         _krb5_debug(context, 5, "krb5_get_init_creds: pa-info not found, guessing salt");
    1151             : 
    1152             :         /* make a v5 salted pa-data */
    1153           0 :         add_enc_ts_padata(context, md, client,
    1154           0 :                           ctx->keyproc, ctx->keyseed,
    1155           0 :                           a->req_body.etype.val, a->req_body.etype.len,
    1156             :                           NULL, NULL);
    1157             : 
    1158             :         /* make a v4 salted pa-data */
    1159           0 :         salt.salttype = KRB5_PW_SALT;
    1160           0 :         krb5_data_zero(&salt.saltvalue);
    1161           0 :         add_enc_ts_padata(context, md, client,
    1162           0 :                           ctx->keyproc, ctx->keyseed,
    1163           0 :                           a->req_body.etype.val, a->req_body.etype.len,
    1164             :                           &salt, NULL);
    1165             :     }
    1166       13517 :     return 0;
    1167             : }
    1168             : 
    1169             : static krb5_error_code
    1170       13763 : pa_data_to_key_plain(krb5_context context,
    1171             :                      const krb5_principal client,
    1172             :                      krb5_init_creds_context ctx,
    1173             :                      krb5_salt salt,
    1174             :                      krb5_data *s2kparams,
    1175             :                      krb5_enctype etype,
    1176             :                      krb5_keyblock **key)
    1177             : {
    1178         592 :     krb5_error_code ret;
    1179             : 
    1180       14355 :     ret = (*ctx->keyproc)(context, etype, ctx->keyseed,
    1181             :                            salt, s2kparams, key);
    1182       13763 :     return ret;
    1183             : }
    1184             : 
    1185             : struct pkinit_context {
    1186             :     unsigned int win2k : 1;
    1187             :     unsigned int used_pkinit : 1;
    1188             : };
    1189             : 
    1190             : 
    1191             : static krb5_error_code
    1192         124 : pa_data_to_md_pkinit(krb5_context context,
    1193             :                      const AS_REQ *a,
    1194             :                      const krb5_principal client,
    1195             :                      int win2k,
    1196             :                      krb5_init_creds_context ctx,
    1197             :                      METHOD_DATA *md)
    1198             : {
    1199         124 :     if (ctx->pk_init_ctx == NULL)
    1200           0 :         return 0;
    1201             : #ifdef PKINIT
    1202         124 :     return _krb5_pk_mk_padata(context,
    1203         124 :                               ctx->pk_init_ctx,
    1204             :                               ctx->ic_flags,
    1205             :                               win2k,
    1206             :                               &a->req_body,
    1207             :                               ctx->pk_nonce,
    1208             :                               md);
    1209             : #else
    1210             :     krb5_set_error_message(context, EINVAL,
    1211             :                            N_("no support for PKINIT compiled in", ""));
    1212             :     return EINVAL;
    1213             : #endif
    1214             : }
    1215             : 
    1216             : static krb5_error_code
    1217         124 : pkinit_configure_ietf(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx)
    1218             : {
    1219         124 :     struct pkinit_context *pkinit_ctx = pa_ctx;
    1220             : 
    1221         124 :     pkinit_ctx->win2k = 0;
    1222             : 
    1223         124 :     if (ctx->pk_init_ctx == NULL)
    1224           0 :         return HEIM_ERR_PA_CANT_CONTINUE;
    1225             : 
    1226         124 :     return 0;
    1227             : }
    1228             : 
    1229             : static krb5_error_code
    1230         124 : pkinit_configure_win(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx)
    1231             : {
    1232         124 :     struct pkinit_context *pkinit_ctx = pa_ctx;
    1233             : 
    1234         124 :     pkinit_ctx->win2k = 1;
    1235         124 :     pkinit_ctx->used_pkinit = 0;
    1236             : 
    1237         124 :     if (ctx->pk_init_ctx == NULL)
    1238           0 :         return HEIM_ERR_PA_CANT_CONTINUE;
    1239             : 
    1240         124 :     return 0;
    1241             : }
    1242             : 
    1243             : static krb5_error_code
    1244         137 : pkinit_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a,
    1245             :             const AS_REP *rep, METHOD_DATA *in_md, METHOD_DATA *out_md)
    1246             : {
    1247         137 :     krb5_error_code ret = HEIM_ERR_PA_CANT_CONTINUE;
    1248         137 :     struct pkinit_context *pkinit_ctx = pa_ctx;
    1249             : 
    1250         137 :     if (rep == NULL) {
    1251         124 :         if (pkinit_ctx->used_pkinit) {
    1252           0 :             krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
    1253             :                                    "Already tried PKINIT(%s), looping",
    1254           0 :                                    pkinit_ctx->win2k ? "win2k" : "ietf");
    1255             :         } else {
    1256         124 :             ret = pa_data_to_md_pkinit(context, a, ctx->cred.client,
    1257         124 :                                        (pkinit_ctx->win2k != 0),
    1258             :                                        ctx, out_md);
    1259         124 :             if (ret == 0)
    1260         124 :                 ret = HEIM_ERR_PA_CONTINUE_NEEDED;
    1261             : 
    1262         124 :             pkinit_ctx->used_pkinit = 1;
    1263             :         }
    1264          13 :     } else if (pa) {
    1265          13 :         ret = _krb5_pk_rd_pa_reply(context,
    1266          13 :                                    a->req_body.realm,
    1267          13 :                                    ctx->pk_init_ctx,
    1268          13 :                                    rep->enc_part.etype,
    1269             :                                    ctx->pk_nonce,
    1270          13 :                                    &ctx->req_buffer,
    1271             :                                    pa,
    1272             :                                    &ctx->fast_state.reply_key);
    1273          13 :         if (ret == 0)
    1274          13 :             ctx->runflags.allow_save_as_reply_key = 1;
    1275             :     }
    1276             : 
    1277         137 :     return ret;
    1278             : }
    1279             : 
    1280             : static void
    1281         248 : pkinit_release(void *pa_ctx)
    1282             : {
    1283         248 : }
    1284             : 
    1285             : /*
    1286             :  * GSS-API pre-authentication support
    1287             :  */
    1288             : 
    1289             : struct pa_gss_context {
    1290             :     struct gss_ctx_id_t_desc_struct *context_handle;
    1291             :     int open;
    1292             : };
    1293             : 
    1294             : static krb5_error_code
    1295           0 : pa_gss_configure(krb5_context context,
    1296             :                  krb5_init_creds_context ctx,
    1297             :                  void *pa_ctx)
    1298             : {
    1299           0 :     krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
    1300           0 :     struct pa_gss_context *pa_gss_ctx = pa_ctx;
    1301             : 
    1302           0 :     if (gssic == NULL)
    1303           0 :         return HEIM_ERR_PA_CANT_CONTINUE;
    1304             : 
    1305           0 :     pa_gss_ctx->context_handle = NULL;
    1306           0 :     pa_gss_ctx->open = 0;
    1307             : 
    1308           0 :     return 0;
    1309             : }
    1310             : 
    1311             : static krb5_error_code
    1312           0 : pa_data_to_md_gss(krb5_context context,
    1313             :                   const AS_REQ *a,
    1314             :                   const krb5_creds *creds,
    1315             :                   krb5_init_creds_context ctx,
    1316             :                   struct pa_gss_context *pa_gss_ctx,
    1317             :                   PA_DATA *pa,
    1318             :                   METHOD_DATA *out_md)
    1319             : {
    1320           0 :     krb5_error_code ret;
    1321           0 :     krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
    1322           0 :     krb5_data req_body;
    1323           0 :     krb5_data *input_token, output_token;
    1324           0 :     size_t len = 0;
    1325             : 
    1326           0 :     krb5_data_zero(&req_body);
    1327           0 :     krb5_data_zero(&output_token);
    1328             : 
    1329           0 :     input_token = pa ? &pa->padata_value : NULL;
    1330             : 
    1331           0 :     if ((input_token == NULL || input_token->length == 0) &&
    1332           0 :         pa_gss_ctx->context_handle) {
    1333           0 :         krb5_set_error_message(context, HEIM_ERR_PA_CANT_CONTINUE,
    1334             :                                "Missing GSS preauthentication data from KDC");
    1335           0 :         return HEIM_ERR_PA_CANT_CONTINUE;
    1336             :     }
    1337             : 
    1338           0 :     ASN1_MALLOC_ENCODE(KDC_REQ_BODY, req_body.data, req_body.length,
    1339             :                        &ctx->as_req.req_body, &len, ret);
    1340           0 :     if (ret)
    1341           0 :         goto out;
    1342           0 :     heim_assert(req_body.length == len, "ASN.1 internal error");
    1343             : 
    1344           0 :     ret = gssic->step(context, gssic, creds, &pa_gss_ctx->context_handle,
    1345             :                       ctx->flags, &req_body,
    1346             :                       input_token, &output_token);
    1347             : 
    1348             :     /*
    1349             :      * If FAST authenticated the KDC (which will be the case unless anonymous
    1350             :      * PKINIT was used without KDC certificate validation) then we can relax
    1351             :      * the mutual authentication requirement.
    1352             :      */
    1353           0 :     if (ret == KRB5_MUTUAL_FAILED &&
    1354           0 :         (ctx->fast_state.flags & KRB5_FAST_EXPECTED) &&
    1355           0 :         (ctx->fast_state.flags & KRB5_FAST_KDC_VERIFIED))
    1356           0 :         ret = 0;
    1357           0 :     if (ret == 0) {
    1358             :         /*
    1359             :          * Always require a strengthen key if FAST was used, to avoid a MITM
    1360             :          * attack that could result in unintended privilege escalation should
    1361             :          * the KDC add positive authorization data from the armor ticket.
    1362             :          */
    1363           0 :         if ((ctx->fast_state.flags & KRB5_FAST_EXPECTED) &&
    1364           0 :             ctx->fast_state.strengthen_key == NULL) {
    1365           0 :             krb5_set_error_message(context, HEIM_ERR_PA_CANT_CONTINUE,
    1366             :                                    "FAST GSS pre-authentication without strengthen key");
    1367           0 :             ret = KRB5_KDCREP_MODIFIED;
    1368           0 :             goto out;
    1369             :         }
    1370             : 
    1371           0 :         pa_gss_ctx->open = 1;
    1372             :     }
    1373             : 
    1374           0 :     if (output_token.length) {
    1375           0 :         ret = krb5_padata_add(context, out_md, KRB5_PADATA_GSS,
    1376             :                               output_token.data, output_token.length);
    1377           0 :         if (ret)
    1378           0 :             goto out;
    1379             : 
    1380           0 :         krb5_data_zero(&output_token);
    1381             :     }
    1382             : 
    1383           0 : out:
    1384           0 :     krb5_data_free(&output_token);
    1385           0 :     krb5_data_free(&req_body);
    1386             : 
    1387           0 :     return ret;
    1388             : }
    1389             : 
    1390             : static krb5_error_code
    1391           0 : pa_gss_step(krb5_context context,
    1392             :             krb5_init_creds_context ctx,
    1393             :             void *pa_ctx,
    1394             :             PA_DATA *pa,
    1395             :             const AS_REQ *a,
    1396             :             const AS_REP *rep,
    1397             :             METHOD_DATA *in_md,
    1398             :             METHOD_DATA *out_md)
    1399             : {
    1400           0 :     krb5_error_code ret;
    1401           0 :     krb5_principal cname;
    1402           0 :     krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
    1403           0 :     struct pa_gss_context *pa_gss_ctx = pa_ctx;
    1404             : 
    1405           0 :     heim_assert(gssic != NULL, "invalid context passed to pa_gss_step");
    1406             : 
    1407           0 :     if (!pa_gss_ctx->open) {
    1408           0 :         ret = pa_data_to_md_gss(context, a, &ctx->cred, ctx,
    1409             :                                 pa_gss_ctx, pa, out_md);
    1410           0 :         if (ret == HEIM_ERR_PA_CONTINUE_NEEDED && rep) {
    1411           0 :             krb5_set_error_message(context, KRB5_PREAUTH_FAILED,
    1412             :                                    "KDC sent AS-REP before GSS "
    1413             :                                    "pre-authentication completed");
    1414           0 :             ret = KRB5_KDCREP_MODIFIED;
    1415           0 :         } else if (ret == 0 && rep == NULL) {
    1416           0 :             ret = HEIM_ERR_PA_CONTINUE_NEEDED; /* odd number of legs */
    1417             :         }
    1418           0 :         if (ret)
    1419           0 :             return ret;
    1420           0 :     } else if (pa && pa->padata_value.length) {
    1421           0 :         krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
    1422             :                                "Already completed GSS pre-authentication");
    1423           0 :         return KRB5_GET_IN_TKT_LOOP;
    1424           0 :     } else if (rep == NULL) {
    1425           0 :         krb5_set_error_message(context, KRB5_PREAUTH_FAILED,
    1426             :                                "Completed GSS pre-authentication before KDC");
    1427           0 :         return KRB5_PREAUTH_FAILED;
    1428             :     }
    1429             : 
    1430           0 :     heim_assert(pa_gss_ctx->open,
    1431             :                 "GSS pre-authentication incomplete");
    1432             : 
    1433           0 :     ret = gssic->finish(context, gssic, &ctx->cred,
    1434           0 :                         pa_gss_ctx->context_handle, ctx->nonce,
    1435           0 :                         rep->enc_part.etype, &cname,
    1436             :                         &ctx->fast_state.reply_key);
    1437           0 :     if (ret)
    1438           0 :         return ret;
    1439             : 
    1440             :     {
    1441           0 :         char *from = NULL;
    1442           0 :         char *to = NULL;
    1443             : 
    1444           0 :         if (krb5_unparse_name(context, ctx->cred.client, &from) == 0) {
    1445           0 :             if (krb5_unparse_name(context, cname, &to) == 0) {
    1446           0 :                 _krb5_debug(context, 1, "pa_gss_step: %s as %s",
    1447             :                             from, to);
    1448           0 :                 krb5_xfree(to);
    1449             :             }
    1450           0 :             krb5_xfree(from);
    1451             :         }
    1452             :     }
    1453             : 
    1454           0 :     if (krb5_principal_is_federated(context, ctx->cred.client)) {
    1455             :         /*
    1456             :          * The well-known federated name will be replaced with the cname
    1457             :          * in the AS-REP, but save the locally mapped initiator name in the
    1458             :          * cred for logging.
    1459             :          */
    1460           0 :         krb5_free_principal(context, ctx->cred.client);
    1461           0 :         ctx->cred.client = cname;
    1462             : 
    1463           0 :         ctx->ic_flags |= KRB5_INIT_CREDS_NO_C_CANON_CHECK;
    1464             :     } else {
    1465           0 :         krb5_free_principal(context, cname);
    1466             :     }
    1467             : 
    1468           0 :     ctx->runflags.allow_save_as_reply_key = 1;
    1469             : 
    1470           0 :     gssic->delete_sec_context(context, gssic, pa_gss_ctx->context_handle);
    1471           0 :     pa_gss_ctx->context_handle = NULL;
    1472           0 :     pa_gss_ctx->open = 0;
    1473             : 
    1474           0 :     return 0;
    1475             : }
    1476             : 
    1477             : static krb5_error_code
    1478           0 : pa_gss_restart(krb5_context context,
    1479             :                krb5_init_creds_context ctx,
    1480             :                void *pa_ctx)
    1481             : {
    1482           0 :     krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
    1483           0 :     struct pa_gss_context *pa_gss_ctx = pa_ctx;
    1484             : 
    1485           0 :     if (gssic == NULL)
    1486           0 :         return HEIM_ERR_PA_CANT_CONTINUE;
    1487             : 
    1488           0 :     gssic->delete_sec_context(context, gssic, pa_gss_ctx->context_handle);
    1489           0 :     pa_gss_ctx->context_handle = NULL;
    1490           0 :     pa_gss_ctx->open = 0;
    1491             : 
    1492           0 :     return 0;
    1493             : }
    1494             : 
    1495             : static void
    1496           0 : pa_gss_release(void *pa_ctx)
    1497             : {
    1498           0 : }
    1499             : 
    1500             : krb5_error_code
    1501         170 : _krb5_make_pa_enc_challenge(krb5_context context,
    1502             :                             krb5_crypto crypto,
    1503             :                             krb5_key_usage usage,
    1504             :                             METHOD_DATA *md)
    1505             : {
    1506           0 :     PA_ENC_TS_ENC p;
    1507           0 :     unsigned char *buf;
    1508           0 :     size_t buf_size;
    1509         170 :     size_t len = 0;
    1510           0 :     EncryptedData encdata;
    1511           0 :     krb5_error_code ret;
    1512           0 :     int32_t usec;
    1513           0 :     int usec2;
    1514             : 
    1515         170 :     krb5_us_timeofday (context, &p.patimestamp, &usec);
    1516         170 :     usec2         = usec;
    1517         170 :     p.pausec      = &usec2;
    1518             : 
    1519         170 :     ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret);
    1520         170 :     if (ret)
    1521           0 :         return ret;
    1522         170 :     if(buf_size != len)
    1523           0 :         krb5_abortx(context, "internal error in ASN.1 encoder");
    1524             : 
    1525         170 :     ret = krb5_encrypt_EncryptedData(context,
    1526             :                                      crypto,
    1527             :                                      usage,
    1528             :                                      buf,
    1529             :                                      len,
    1530             :                                      0,
    1531             :                                      &encdata);
    1532         170 :     free(buf);
    1533         170 :     if (ret)
    1534           0 :         return ret;
    1535             : 
    1536         170 :     ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret);
    1537         170 :     free_EncryptedData(&encdata);
    1538         170 :     if (ret)
    1539           0 :         return ret;
    1540         170 :     if(buf_size != len)
    1541           0 :         krb5_abortx(context, "internal error in ASN.1 encoder");
    1542             : 
    1543         170 :     ret = krb5_padata_add(context, md, KRB5_PADATA_ENCRYPTED_CHALLENGE, buf, len);
    1544         170 :     if (ret)
    1545           0 :         free(buf);
    1546         170 :     return ret;
    1547             : }
    1548             : 
    1549             : krb5_error_code
    1550         177 : _krb5_validate_pa_enc_challenge(krb5_context context,
    1551             :                                 krb5_crypto crypto,
    1552             :                                 krb5_key_usage usage,
    1553             :                                 EncryptedData *enc_data,
    1554             :                                 const char *peer_name)
    1555             : {
    1556           0 :     krb5_error_code ret;
    1557           0 :     krb5_data ts_data;
    1558           0 :     PA_ENC_TS_ENC p;
    1559           0 :     time_t timestamp;
    1560           0 :     int32_t usec;
    1561           0 :     size_t size;
    1562             : 
    1563         177 :     ret = krb5_decrypt_EncryptedData(context, crypto, usage, enc_data, &ts_data);
    1564         177 :     if (ret)
    1565           6 :         return ret;
    1566             : 
    1567         171 :     ret = decode_PA_ENC_TS_ENC(ts_data.data,
    1568             :                                ts_data.length,
    1569             :                                &p,
    1570             :                                &size);
    1571         171 :     krb5_data_free(&ts_data);
    1572         171 :     if(ret){
    1573           0 :         ret = KRB5KDC_ERR_PREAUTH_FAILED;
    1574           0 :         _krb5_debug(context, 5, "Failed to decode PA-ENC-TS_ENC -- %s", peer_name);
    1575           0 :         goto out;
    1576             :     }
    1577             : 
    1578         171 :     krb5_us_timeofday(context, &timestamp, &usec);
    1579             : 
    1580         171 :     if (krb5_time_abs(timestamp, p.patimestamp) > context->max_skew) {
    1581           0 :         char client_time[100];
    1582             : 
    1583           1 :         krb5_format_time(context, p.patimestamp,
    1584             :                          client_time, sizeof(client_time), TRUE);
    1585             : 
    1586           1 :         ret = KRB5KRB_AP_ERR_SKEW;
    1587           1 :         _krb5_debug(context, 0, "Too large time skew, "
    1588             :                     "client time %s is out by %u > %d seconds -- %s",
    1589             :                     client_time,
    1590           1 :                     (unsigned)krb5_time_abs(timestamp, p.patimestamp),
    1591           1 :                     (int)context->max_skew,
    1592             :                     peer_name);
    1593             :     } else {
    1594         170 :         ret = 0;
    1595             :     }
    1596             : 
    1597         171 :  out:
    1598         171 :     free_PA_ENC_TS_ENC(&p);
    1599             : 
    1600         171 :     return ret;
    1601             : }
    1602             : 
    1603             : 
    1604             : static struct pa_info_data *
    1605             : process_pa_info(krb5_context, const krb5_principal, const AS_REQ *, struct pa_info_data *, METHOD_DATA *);
    1606             : 
    1607             : 
    1608             : static krb5_error_code
    1609          24 : enc_chal_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a,
    1610             :               const AS_REP *rep, METHOD_DATA *in_md, METHOD_DATA *out_md)
    1611             : {
    1612           0 :     struct pa_info_data paid, *ppaid;
    1613           0 :     krb5_keyblock challengekey;
    1614           0 :     krb5_data pepper1, pepper2;
    1615          24 :     krb5_crypto crypto = NULL;
    1616           0 :     krb5_enctype aenctype;
    1617           0 :     krb5_error_code ret;
    1618             : 
    1619          24 :     memset(&paid, 0, sizeof(paid));
    1620             : 
    1621          24 :     if (rep == NULL)
    1622          17 :         paid.etype = KRB5_ENCTYPE_NULL;
    1623             :     else
    1624           7 :         paid.etype = rep->enc_part.etype;
    1625          24 :     ppaid = process_pa_info(context, ctx->cred.client, a, &paid, in_md);
    1626             : 
    1627             :     /*
    1628             :      * If we don't have ppaid, it's because the KDC has not sent any
    1629             :      * salt info. Let's do the first roundtrip so the KDC has a chance
    1630             :      * to send some.
    1631             :      */
    1632          24 :     if (ppaid == NULL) {
    1633          10 :         _krb5_debug(context, 5, "no ppaid found");
    1634          10 :         return HEIM_ERR_PA_CONTINUE_NEEDED;
    1635             :     }
    1636          14 :     if (ppaid->etype == KRB5_ENCTYPE_NULL) {
    1637           0 :         return HEIM_ERR_PA_CANT_CONTINUE;
    1638             :     }
    1639             : 
    1640          14 :     if (ctx->fast_state.reply_key)
    1641           7 :         krb5_free_keyblock(context, ctx->fast_state.reply_key);
    1642             : 
    1643          14 :     ret = pa_data_to_key_plain(context, ctx->cred.client, ctx,
    1644             :                                ppaid->salt, ppaid->s2kparams, ppaid->etype,
    1645             :                                &ctx->fast_state.reply_key);
    1646          14 :     free_paid(context, &paid);
    1647          14 :     if (ret) {
    1648           0 :         _krb5_debug(context, 5, "enc-chal: failed to build key");
    1649           0 :         return ret;
    1650             :     }
    1651             : 
    1652          14 :     ret = krb5_crypto_init(context, ctx->fast_state.reply_key, 0, &crypto);
    1653          14 :     if (ret)
    1654           0 :         return ret;
    1655             : 
    1656          14 :     krb5_crypto_getenctype(context, ctx->fast_state.armor_crypto, &aenctype);
    1657             : 
    1658          14 :     pepper1.data = rep ? "kdcchallengearmor" : "clientchallengearmor";
    1659          14 :     pepper1.length = strlen(pepper1.data);
    1660          14 :     pepper2.data = "challengelongterm";
    1661          14 :     pepper2.length = strlen(pepper2.data);
    1662             : 
    1663          14 :     ret = krb5_crypto_fx_cf2(context, ctx->fast_state.armor_crypto, crypto,
    1664             :                              &pepper1, &pepper2, aenctype,
    1665             :                              &challengekey);
    1666          14 :     krb5_crypto_destroy(context, crypto);
    1667          14 :     if (ret)
    1668           0 :         return ret;
    1669             : 
    1670          14 :     ret = krb5_crypto_init(context, &challengekey, 0, &crypto);
    1671          14 :     krb5_free_keyblock_contents(context, &challengekey);
    1672          14 :     if (ret)
    1673           0 :         return ret;
    1674             : 
    1675          14 :     if (rep) {
    1676           0 :         EncryptedData enc_data;
    1677           0 :         size_t size;
    1678             : 
    1679           7 :         _krb5_debug(context, 5, "ENC_CHAL rep key");
    1680             : 
    1681           7 :         if (ctx->fast_state.strengthen_key == NULL) {
    1682           0 :             krb5_crypto_destroy(context, crypto);
    1683           0 :             _krb5_debug(context, 5, "ENC_CHAL w/o strengthen_key");
    1684           0 :             return KRB5_KDCREP_MODIFIED;
    1685             :         }
    1686             : 
    1687           7 :         if (pa == NULL) {
    1688           0 :             krb5_crypto_destroy(context, crypto);
    1689           0 :             _krb5_debug(context, 0, "KDC response missing");
    1690           0 :             return HEIM_ERR_PA_CANT_CONTINUE;
    1691             :         }
    1692             : 
    1693           7 :         ret = decode_EncryptedData(pa->padata_value.data,
    1694             :                                    pa->padata_value.length,
    1695             :                                    &enc_data,
    1696             :                                    &size);
    1697           7 :         if (ret) {
    1698           0 :             ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
    1699           0 :             _krb5_debug(context, 5, "Failed to decode ENC_CHAL KDC reply");
    1700           0 :             return ret;
    1701             :         }
    1702             : 
    1703           7 :         ret = _krb5_validate_pa_enc_challenge(context, crypto,
    1704             :                                               KRB5_KU_ENC_CHALLENGE_KDC,
    1705             :                                               &enc_data,
    1706             :                                               "KDC");
    1707           7 :         free_EncryptedData(&enc_data);
    1708           7 :         krb5_crypto_destroy(context, crypto);
    1709             : 
    1710           7 :         return ret;
    1711             : 
    1712             :     } else {
    1713             : 
    1714           7 :         ret = _krb5_make_pa_enc_challenge(context, crypto,
    1715             :                                           KRB5_KU_ENC_CHALLENGE_CLIENT,
    1716             :                                           out_md);
    1717           7 :         krb5_crypto_destroy(context, crypto);
    1718           7 :         if (ret) {
    1719           0 :             _krb5_debug(context, 5, "enc-chal: failed build enc challenge");
    1720           0 :             return ret;
    1721             :         }
    1722             : 
    1723           7 :         return HEIM_ERR_PA_CONTINUE_NEEDED;
    1724             :     }
    1725             : }
    1726             : 
    1727             : struct enc_ts_context {
    1728             :     int used_pa_types;
    1729             : #define  USED_ENC_TS_GUESS      4
    1730             : #define  USED_ENC_TS_INFO       8
    1731             : #define  USED_ENC_TS_RENEG      16
    1732             :     krb5_principal user;
    1733             : };
    1734             : 
    1735             : static krb5_error_code
    1736         515 : enc_ts_restart(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx)
    1737             : {
    1738         515 :     struct enc_ts_context *pactx = (struct enc_ts_context *)pa_ctx;
    1739         515 :     pactx->used_pa_types = 0;
    1740         515 :     krb5_free_principal(context, pactx->user);
    1741         515 :     pactx->user = NULL;
    1742         515 :     return 0;
    1743             : }
    1744             : 
    1745             : static krb5_error_code
    1746       44542 : enc_ts_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa,
    1747             :             const AS_REQ *a,
    1748             :             const AS_REP *rep,
    1749             :             METHOD_DATA *in_md, METHOD_DATA *out_md)
    1750             : {
    1751       44542 :     struct enc_ts_context *pactx = (struct enc_ts_context *)pa_ctx;
    1752        1776 :     struct pa_info_data paid, *ppaid;
    1753        1776 :     krb5_error_code ret;
    1754        1776 :     const char *state;
    1755        1776 :     unsigned flag;
    1756             : 
    1757             :     /*
    1758             :      * Keep track of the user we used so that we can restart
    1759             :      * authentication when we get referrals.
    1760             :      */
    1761             : 
    1762       44542 :     if (pactx->user && !krb5_principal_compare(context, pactx->user, ctx->cred.client)) {
    1763           0 :         pactx->used_pa_types = 0;
    1764           0 :         krb5_free_principal(context, pactx->user);
    1765           0 :         pactx->user = NULL;
    1766             :     }
    1767             : 
    1768       44542 :     if (pactx->user == NULL) {
    1769       16684 :         ret = krb5_copy_principal(context, ctx->cred.client, &pactx->user);
    1770       16684 :         if (ret)
    1771           0 :             return ret;
    1772             :     }
    1773             : 
    1774       44542 :     memset(&paid, 0, sizeof(paid));
    1775             : 
    1776       44542 :     if (rep == NULL)
    1777       29609 :         paid.etype = KRB5_ENCTYPE_NULL;
    1778             :     else
    1779       13749 :         paid.etype = rep->enc_part.etype;
    1780             : 
    1781       44542 :     ppaid = process_pa_info(context, ctx->cred.client, a, &paid, in_md);
    1782             : 
    1783       44542 :     if (rep) {
    1784             :         /*
    1785             :          * Some KDC's don't send salt info in the reply when there is
    1786             :          * success pre-auth happened before, so use cached copy (or
    1787             :          * even better, if there is just one pre-auth, save reply-key).
    1788             :          */
    1789       13749 :         if (ppaid == NULL && ctx->paid.etype != KRB5_ENCTYPE_NULL) {
    1790         620 :             ppaid = &ctx->paid;
    1791             : 
    1792       13129 :         } else if (ppaid == NULL) {
    1793           0 :             _krb5_debug(context, 0, "no paid when building key, build a default salt structure ?");
    1794           0 :             return HEIM_ERR_PA_CANT_CONTINUE;
    1795             :         }
    1796             : 
    1797       14341 :         ret = pa_data_to_key_plain(context, ctx->cred.client, ctx,
    1798       13749 :                                    ppaid->salt, ppaid->s2kparams, rep->enc_part.etype,
    1799             :                                    &ctx->fast_state.reply_key);
    1800       13749 :         free_paid(context, &paid);
    1801       13749 :         return ret;
    1802             :     }
    1803             : 
    1804             :     /*
    1805             :      * If we don't have ppaid, it's because the KDC has not sent any
    1806             :      * salt info. Let's do the first roundtrip so the KDC has a chance
    1807             :      * to send some.
    1808             :      *
    1809             :      * Don't bother guessing, it sounds like a good idea until you run
    1810             :      * into KDCs that are doing failed auth counting based on the
    1811             :      * ENC_TS tries.
    1812             :      *
    1813             :      * Stashing the salt for the next run is a different issue and
    1814             :      * could be considered in the future.
    1815             :      */
    1816             : 
    1817       30793 :     if (ppaid == NULL) {
    1818       16684 :         _krb5_debug(context, 5,
    1819             :                      "TS-ENC: waiting for KDC to set pw-salt/etype_info{,2}");
    1820       16684 :         return HEIM_ERR_PA_CONTINUE_NEEDED;
    1821             :     }
    1822       14109 :     if (ppaid->etype == KRB5_ENCTYPE_NULL) {
    1823           0 :         free_paid(context, &paid);
    1824           0 :         _krb5_debug(context, 5,
    1825             :                      "TS-ENC: kdc proposes enctype NULL ?");
    1826           0 :         return HEIM_ERR_PA_CANT_CONTINUE;
    1827             :     }
    1828             : 
    1829             :     /*
    1830             :      * We have to allow the KDC to re-negotiate the PA-TS data
    1831             :      * once, this is since a windows read only
    1832             :      * KDC that doesn't have the keys simply guesses what the
    1833             :      * master is supposed to support. The case where this
    1834             :      * breaks is when the RO-KDC is a newer version than the RW-KDC
    1835             :      * and the RO-KDC announced a enctype that the older doesn't
    1836             :      * support.
    1837             :      */
    1838       14109 :     if (pactx->used_pa_types & USED_ENC_TS_INFO) {
    1839           0 :         flag = USED_ENC_TS_RENEG;
    1840           0 :         state = "reneg";
    1841             :     } else {
    1842       14109 :         flag = USED_ENC_TS_INFO;
    1843       14109 :         state = "info";
    1844             :     }
    1845             : 
    1846       14109 :     if (pactx->used_pa_types & flag) {
    1847           0 :         free_paid(context, &paid);
    1848           0 :         krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
    1849             :                                "Already tried ENC-TS-%s, looping", state);
    1850           0 :         return KRB5_GET_IN_TKT_LOOP;
    1851             :     }
    1852             : 
    1853       14109 :     pactx->used_pa_types |= flag;
    1854             : 
    1855       14109 :     free_paid(context, &ctx->paid);
    1856       14109 :     ctx->paid = *ppaid;
    1857             : 
    1858       14109 :     ret = pa_data_to_md_ts_enc(context, a, ctx->cred.client, ctx, ppaid, out_md);
    1859       14109 :     if (ret)
    1860           0 :         return ret;
    1861             : 
    1862       13517 :     return HEIM_ERR_PA_CONTINUE_NEEDED;
    1863             : }
    1864             : 
    1865             : static void
    1866       16179 : enc_ts_release(void *pa_ctx)
    1867             : {
    1868       16179 :     struct enc_ts_context *pactx = (struct enc_ts_context *)pa_ctx;
    1869             : 
    1870       16179 :     if (pactx->user)
    1871       16169 :         krb5_free_principal(NULL, pactx->user);
    1872       16179 : }
    1873             : 
    1874             : static krb5_error_code
    1875       30934 : pa_pac_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a,
    1876             :             const AS_REP *rep, METHOD_DATA *in_md, METHOD_DATA *out_md)
    1877             : {
    1878       30934 :     size_t len = 0, length;
    1879        1184 :     krb5_error_code ret;
    1880        1184 :     PA_PAC_REQUEST req;
    1881        1184 :     void *buf;
    1882             : 
    1883       30934 :     switch (ctx->req_pac) {
    1884       28079 :     case KRB5_INIT_CREDS_TRISTATE_UNSET:
    1885       28079 :         return 0; /* don't bother */
    1886        1671 :     case KRB5_INIT_CREDS_TRISTATE_TRUE:
    1887        1671 :         req.include_pac = 1;
    1888        1671 :         break;
    1889           0 :     case KRB5_INIT_CREDS_TRISTATE_FALSE:
    1890           0 :         req.include_pac = 0;
    1891             :     }
    1892             : 
    1893        1671 :     ASN1_MALLOC_ENCODE(PA_PAC_REQUEST, buf, length,
    1894             :                        &req, &len, ret);
    1895        1671 :     if (ret)
    1896           0 :         return ret;
    1897        1671 :     heim_assert(len == length, "internal error in ASN.1 encoder");
    1898             : 
    1899        1671 :     ret = krb5_padata_add(context, out_md, KRB5_PADATA_PA_PAC_REQUEST, buf, len);
    1900        1671 :     if (ret)
    1901           0 :         free(buf);
    1902             : 
    1903        1671 :     return 0;
    1904             : }
    1905             : 
    1906             : static krb5_error_code
    1907       30934 : pa_enc_pa_rep_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a,
    1908             :                    const AS_REP *rep, METHOD_DATA *in_md, METHOD_DATA *out_md)
    1909             : {
    1910       30934 :     if (ctx->runflags.allow_enc_pa_rep)
    1911       30934 :         return krb5_padata_add(context, out_md, KRB5_PADATA_REQ_ENC_PA_REP, NULL, 0);
    1912             : 
    1913           0 :     return 0;
    1914             : }
    1915             : 
    1916             : static krb5_error_code
    1917       30934 : pa_fx_cookie_step(krb5_context context,
    1918             :                   krb5_init_creds_context ctx,
    1919             :                   void *pa_ctx,
    1920             :                   PA_DATA *pa,
    1921             :                   const AS_REQ *a,
    1922             :                   const AS_REP *rep,
    1923             :                   METHOD_DATA *in_md,
    1924             :                   METHOD_DATA *out_md)
    1925             : {
    1926        1184 :     krb5_error_code ret;
    1927        1184 :     void *cookie;
    1928        1184 :     PA_DATA *pad;
    1929       30934 :     int idx = 0;
    1930             : 
    1931       30934 :     pad = krb5_find_padata(in_md->val, in_md->len, KRB5_PADATA_FX_COOKIE, &idx);
    1932       30934 :     if (pad == NULL) {
    1933             :         /*
    1934             :          * RFC 6113 5.4.3: PA-FX-COOKIE MUST be included if the KDC
    1935             :          * expects at least one more message from the client.
    1936             :          */
    1937       30927 :         if (ctx->error.error_code == KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED)
    1938           0 :             return KRB5_PREAUTH_FAILED;
    1939             :         else
    1940       30927 :             return 0;
    1941             :     }
    1942             : 
    1943           7 :     cookie = malloc(pad->padata_value.length);
    1944           7 :     if (cookie == NULL)
    1945           0 :         return krb5_enomem(context);
    1946             : 
    1947           7 :     memcpy(cookie, pad->padata_value.data, pad->padata_value.length);
    1948             : 
    1949           7 :     ret = krb5_padata_add(context, out_md, KRB5_PADATA_FX_COOKIE,
    1950             :                           cookie, pad->padata_value.length);
    1951           7 :     if (ret)
    1952           0 :         free(cookie);
    1953             :     else
    1954           7 :         _krb5_debug(context, 5, "Mirrored FX-COOKIE to KDC");
    1955             : 
    1956           7 :     return ret;
    1957             : }
    1958             : 
    1959             : typedef struct pa_info_data *(*pa_salt_info_f)(krb5_context, const krb5_principal, const AS_REQ *, struct pa_info_data *, heim_octet_string *);
    1960             : typedef krb5_error_code (*pa_configure_f)(krb5_context, krb5_init_creds_context, void *);
    1961             : typedef krb5_error_code (*pa_restart_f)(krb5_context, krb5_init_creds_context, void *);
    1962             : typedef krb5_error_code (*pa_step_f)(krb5_context, krb5_init_creds_context, void *, PA_DATA *, const AS_REQ *, const AS_REP *, METHOD_DATA *, METHOD_DATA *);
    1963             : typedef void            (*pa_release_f)(void *);
    1964             : 
    1965             : static const struct patype {
    1966             :     int type;
    1967             :     const char *name;
    1968             :     int flags;
    1969             : #define PA_F_ANNOUNCE           1
    1970             : #define PA_F_CONFIG             2
    1971             : #define PA_F_FAST               4 /* available inside FAST */
    1972             : #define PA_F_NOT_FAST           8 /* only available without FAST */
    1973             :     size_t pa_ctx_size;
    1974             :     pa_salt_info_f salt_info;
    1975             :     /**
    1976             :      * Return 0 if the PA-mechanism is available and optionally set pa_ctx pointer to non-NULL.
    1977             :      */
    1978             :     pa_configure_f configure;
    1979             :     /**
    1980             :      * Return 0 if the PA-mechanism can be restarted (time skew, referrals, etc)
    1981             :      */
    1982             :     pa_restart_f restart;
    1983             :     /**
    1984             :      * Return 0 when complete, HEIM_ERR_PA_CONTINUE_NEEDED if more steps are required
    1985             :      */
    1986             :     pa_step_f step;
    1987             :     pa_release_f release;
    1988             : } patypes[] = {
    1989             :     {
    1990             :         KRB5_PADATA_PK_AS_REP,
    1991             :         "PKINIT(IETF)",
    1992             :         PA_F_FAST | PA_F_NOT_FAST,
    1993             :         sizeof(struct pkinit_context),
    1994             :         NULL,
    1995             :         pkinit_configure_ietf,
    1996             :         NULL,
    1997             :         pkinit_step,
    1998             :         pkinit_release
    1999             :     },
    2000             :     {
    2001             :         KRB5_PADATA_PK_AS_REP_19,
    2002             :         "PKINIT(win)",
    2003             :         PA_F_FAST | PA_F_NOT_FAST,
    2004             :         sizeof(struct pkinit_context),
    2005             :         NULL,
    2006             :         pkinit_configure_win,
    2007             :         NULL,
    2008             :         pkinit_step,
    2009             :         pkinit_release
    2010             :     },
    2011             :     {
    2012             :         KRB5_PADATA_GSS,
    2013             :         "GSS",
    2014             :         PA_F_FAST | PA_F_NOT_FAST,
    2015             :         sizeof(struct pa_gss_context),
    2016             :         NULL,
    2017             :         pa_gss_configure,
    2018             :         pa_gss_restart,
    2019             :         pa_gss_step,
    2020             :         pa_gss_release
    2021             :     },
    2022             :     {
    2023             :         KRB5_PADATA_ENCRYPTED_CHALLENGE,
    2024             :         "ENCRYPTED_CHALLENGE",
    2025             :         PA_F_FAST,
    2026             :         0,
    2027             :         NULL,
    2028             :         NULL,
    2029             :         NULL,
    2030             :         enc_chal_step,
    2031             :         NULL
    2032             :     },
    2033             :     {
    2034             :         KRB5_PADATA_ENC_TIMESTAMP,
    2035             :         "ENCRYPTED_TIMESTAMP",
    2036             :         PA_F_NOT_FAST,
    2037             :         sizeof(struct enc_ts_context),
    2038             :         NULL,
    2039             :         NULL,
    2040             :         enc_ts_restart,
    2041             :         enc_ts_step,
    2042             :         enc_ts_release
    2043             :     },
    2044             :     {
    2045             :         KRB5_PADATA_PA_PAC_REQUEST,
    2046             :         "PA_PAC_REQUEST",
    2047             :         PA_F_CONFIG,
    2048             :         0,
    2049             :         NULL,
    2050             :         NULL,
    2051             :         NULL,
    2052             :         pa_pac_step,
    2053             :         NULL
    2054             :     },
    2055             :     {
    2056             :         KRB5_PADATA_REQ_ENC_PA_REP,
    2057             :         "REQ-ENC-PA-REP",
    2058             :         PA_F_CONFIG,
    2059             :         0,
    2060             :         NULL,
    2061             :         NULL,
    2062             :         NULL,
    2063             :         pa_enc_pa_rep_step,
    2064             :         NULL
    2065             :     },
    2066             :     {
    2067             :         KRB5_PADATA_FX_COOKIE,
    2068             :         "FX-COOKIE",
    2069             :         PA_F_CONFIG,
    2070             :         0,
    2071             :         NULL,
    2072             :         NULL,
    2073             :         NULL,
    2074             :         pa_fx_cookie_step,
    2075             :         NULL
    2076             :     },
    2077             : #define patype_salt(n, f) { KRB5_PADATA_##n, #n, 0, 0, f, NULL, NULL, NULL, NULL }
    2078             :     patype_salt(ETYPE_INFO2, pa_etype_info2),
    2079             :     patype_salt(ETYPE_INFO, pa_etype_info),
    2080             :     patype_salt(PW_SALT, pa_pw_or_afs3_salt),
    2081             :     patype_salt(AFS3_SALT, pa_pw_or_afs3_salt),
    2082             : #undef patype_salt
    2083             :     /* below are just for pretty printing */
    2084             : #define patype_info(n) { KRB5_PADATA_##n, #n, 0, 0, NULL, NULL, NULL, NULL, NULL }
    2085             :     patype_info(AUTHENTICATION_SET),
    2086             :     patype_info(AUTH_SET_SELECTED),
    2087             :     patype_info(FX_FAST),
    2088             :     patype_info(FX_ERROR),
    2089             :     patype_info(PKINIT_KX),
    2090             :     patype_info(PK_AS_REQ)
    2091             : #undef patype_info
    2092             : };
    2093             : 
    2094             : static const char *
    2095           6 : get_pa_type_name(int type)
    2096             : {
    2097           0 :     size_t n;
    2098          58 :     for (n = 0; n < sizeof(patypes)/sizeof(patypes[0]); n++)
    2099          58 :         if (type == patypes[n].type)
    2100           6 :             return patypes[n].name;
    2101           0 :     return "unknown";
    2102             : }
    2103             : 
    2104             : /*
    2105             :  *
    2106             :  */
    2107             : 
    2108             : struct pa_auth_mech {
    2109             :     const struct patype *patype;
    2110             :     struct pa_auth_mech *next; /* when doing authentication sets */
    2111             :     char pactx[1];
    2112             : };
    2113             : 
    2114             : /*
    2115             :  *
    2116             :  */
    2117             : 
    2118             : static struct pa_info_data *
    2119       57715 : process_pa_info(krb5_context context,
    2120             :                 const krb5_principal client,
    2121             :                 const AS_REQ *asreq,
    2122             :                 struct pa_info_data *paid,
    2123             :                 METHOD_DATA *md)
    2124             : {
    2125       57715 :     struct pa_info_data *p = NULL;
    2126        2362 :     PA_DATA *pa;
    2127        2362 :     size_t i;
    2128             : 
    2129       57715 :     if (md == NULL)
    2130         614 :         return NULL;
    2131             : 
    2132      721313 :     for (i = 0; p == NULL && i < sizeof(patypes)/sizeof(patypes[0]); i++) {
    2133      664218 :         int idx = 0;
    2134             : 
    2135      664218 :         if (patypes[i].salt_info == NULL)
    2136      623830 :             continue;
    2137             : 
    2138      107216 :         pa = krb5_find_padata(md->val, md->len, patypes[i].type, &idx);
    2139      107216 :         if (pa == NULL)
    2140       66828 :             continue;
    2141             : 
    2142       40388 :         paid->salt.salttype = (krb5_salttype)patypes[i].type;
    2143       40388 :         p = patypes[i].salt_info(context, client, asreq, paid, &pa->padata_value);
    2144             :     }
    2145       54739 :     return p;
    2146             : }
    2147             : 
    2148             : static krb5_error_code
    2149       30934 : pa_announce(krb5_context context,
    2150             :             int types,
    2151             :             krb5_init_creds_context ctx,
    2152             :             METHOD_DATA *in_md,
    2153             :             METHOD_DATA *out_md)
    2154             : {
    2155       30934 :     krb5_error_code ret = 0;
    2156        1184 :     size_t n;
    2157             : 
    2158      587746 :     for (n = 0; ret == 0 && n < sizeof(patypes)/sizeof(patypes[0]); n++) {
    2159      556812 :         if ((patypes[n].flags & types) == 0)
    2160      464010 :             continue;
    2161             : 
    2162       92802 :         if (patypes[n].step)
    2163       92802 :             patypes[n].step(context, ctx, NULL, NULL, NULL, NULL, in_md, out_md);
    2164             :         else
    2165           0 :             ret = krb5_padata_add(context, out_md, patypes[n].type, NULL, 0);
    2166             :     }
    2167       30934 :     return ret;
    2168             : }
    2169             : 
    2170             : 
    2171             : static void HEIM_CALLCONV
    2172       32606 : mech_dealloc(void *ctx)
    2173             : {
    2174       32606 :     struct pa_auth_mech *pa_mech = ctx;
    2175       32606 :     if (pa_mech->patype->release)
    2176       16427 :         pa_mech->patype->release((void *)&pa_mech->pactx[0]);
    2177       32606 : }
    2178             : 
    2179             : static const struct heim_type_data pa_auth_mech_object = {
    2180             :     HEIM_TID_PA_AUTH_MECH,
    2181             :     "heim-pa-mech-context",
    2182             :     NULL,
    2183             :     mech_dealloc,
    2184             :     NULL,
    2185             :     NULL,
    2186             :     NULL,
    2187             :     NULL
    2188             : };
    2189             : 
    2190             : static struct pa_auth_mech *
    2191       32606 : pa_mech_create(krb5_context context, krb5_init_creds_context ctx, int pa_type)
    2192             : {
    2193        1184 :     struct pa_auth_mech *pa_mech;
    2194       32606 :     const struct patype *patype = NULL;
    2195        1184 :     size_t n;
    2196             : 
    2197      178589 :     for (n = 0; patype == NULL && n < sizeof(patypes)/sizeof(patypes[0]); n++) {
    2198      145983 :         if (patypes[n].type == pa_type)
    2199       32606 :             patype = &patypes[n];
    2200             :     }
    2201       32606 :     if (patype == NULL)
    2202           0 :         return NULL;
    2203             : 
    2204       32606 :     pa_mech = _heim_alloc_object(&pa_auth_mech_object, sizeof(*pa_mech) - 1 + patype->pa_ctx_size);
    2205       32606 :     if (pa_mech == NULL)
    2206           0 :         return NULL;
    2207             : 
    2208       32606 :     pa_mech->patype = patype;
    2209             : 
    2210       32606 :     if (pa_mech->patype->configure) {
    2211           0 :         krb5_error_code ret;
    2212             : 
    2213         248 :         ret = pa_mech->patype->configure(context, ctx, &pa_mech->pactx[0]);
    2214         248 :         if (ret) {
    2215           0 :             heim_release(pa_mech);
    2216           0 :             return NULL;
    2217             :         }
    2218             :     }
    2219             : 
    2220       32606 :     _krb5_debug(context, 5, "Adding PA mech: %s", patype->name);
    2221             : 
    2222       32606 :     return pa_mech;
    2223             : }
    2224             : 
    2225             : static void
    2226       32606 : pa_mech_add(krb5_context context, krb5_init_creds_context ctx, int pa_type)
    2227             : {
    2228        1184 :     struct pa_auth_mech *mech;
    2229             : 
    2230       32606 :     mech = pa_mech_create(context, ctx, pa_type);
    2231       32606 :     if (mech) {
    2232       32606 :         heim_array_append_value(ctx->available_pa_mechs, mech);
    2233       32606 :         heim_release(mech);
    2234             :     }
    2235       32606 : }
    2236             : 
    2237             : static krb5_error_code
    2238       16303 : pa_configure(krb5_context context,
    2239             :              krb5_init_creds_context ctx,
    2240             :              METHOD_DATA *in_md)
    2241             : {
    2242       16303 :     ctx->available_pa_mechs = heim_array_create();
    2243             : 
    2244       16303 :     if (ctx->gss_init_ctx) {
    2245           0 :         pa_mech_add(context, ctx, KRB5_PADATA_GSS);
    2246       16303 :     } else if (ctx->pk_init_ctx) {
    2247         124 :         pa_mech_add(context, ctx, KRB5_PADATA_PK_AS_REP);
    2248         124 :         pa_mech_add(context, ctx, KRB5_PADATA_PK_AS_REP_19);
    2249       16179 :     } else if (ctx->keyproc || ctx->keyseed || ctx->prompter) {
    2250       16179 :         pa_mech_add(context, ctx, KRB5_PADATA_ENCRYPTED_CHALLENGE);
    2251       16179 :         pa_mech_add(context, ctx, KRB5_PADATA_ENC_TIMESTAMP);
    2252             :     }
    2253             :     /* XXX setup context based on KDC reply */
    2254             : 
    2255       16303 :     return 0;
    2256             : }
    2257             : 
    2258             : static krb5_error_code
    2259         515 : pa_restart(krb5_context context,
    2260             :            krb5_init_creds_context ctx)
    2261             : {
    2262         515 :     krb5_error_code ret = HEIM_ERR_PA_CANT_CONTINUE;
    2263             : 
    2264         515 :     if (ctx->pa_mech && ctx->pa_mech->patype->restart)
    2265         515 :         ret = ctx->pa_mech->patype->restart(context, ctx, (void *)&ctx->pa_mech->pactx[0]);
    2266             : 
    2267         515 :     return ret;
    2268             : }
    2269             : 
    2270             : 
    2271             : static krb5_error_code
    2272       44703 : pa_step(krb5_context context,
    2273             :         krb5_init_creds_context ctx,
    2274             :         const AS_REQ *a,
    2275             :         const AS_REP *rep,
    2276             :         METHOD_DATA *in_md,
    2277             :         METHOD_DATA *out_md)
    2278             : {
    2279        1776 :     krb5_error_code ret;
    2280       44703 :     PA_DATA *pa = NULL;
    2281        2368 :     int idx;
    2282             : 
    2283       42927 :  next:
    2284        2368 :     do {
    2285       60872 :         if (ctx->pa_mech == NULL) {
    2286       32472 :             size_t len = heim_array_get_length(ctx->available_pa_mechs);
    2287       32472 :             if (len == 0) {
    2288           0 :                 _krb5_debug(context, 0, "no more available_pa_mechs to try");
    2289           0 :                 return HEIM_ERR_NO_MORE_PA_MECHS;
    2290             :             }
    2291             : 
    2292       32472 :             ctx->pa_mech = heim_array_copy_value(ctx->available_pa_mechs, 0);
    2293       32472 :             heim_array_delete_value(ctx->available_pa_mechs, 0);
    2294             :         }
    2295             : 
    2296       60872 :         if (ctx->fast_state.armor_crypto) {
    2297          24 :             if ((ctx->pa_mech->patype->flags & PA_F_FAST) == 0) {
    2298           0 :                 _krb5_debug(context, 0, "pa-mech %s dropped under FAST (not supported)",
    2299           0 :                             ctx->pa_mech->patype->name);
    2300           0 :                 heim_release(ctx->pa_mech);
    2301           0 :                 ctx->pa_mech = NULL;
    2302           0 :                 continue;
    2303             :             }
    2304             :         } else {
    2305       60848 :             if ((ctx->pa_mech->patype->flags & PA_F_NOT_FAST) == 0) {
    2306       16169 :                 _krb5_debug(context, 0, "dropped pa-mech %s since not running under FAST",
    2307       16169 :                             ctx->pa_mech->patype->name);
    2308       16169 :                 heim_release(ctx->pa_mech);
    2309       16169 :                 ctx->pa_mech = NULL;
    2310       16169 :                 continue;
    2311             :             }
    2312             :         }
    2313             : 
    2314       44703 :         _krb5_debug(context, 0, "pa-mech trying: %s, searching for %d",
    2315       44703 :                     ctx->pa_mech->patype->name, ctx->pa_mech->patype->type);
    2316             : 
    2317       44703 :         idx = 0;
    2318       44703 :         if (in_md)
    2319       44083 :             pa = krb5_find_padata(in_md->val, in_md->len, ctx->pa_mech->patype->type, &idx);
    2320             :         else
    2321         614 :             pa = NULL;
    2322             : 
    2323       60872 :     } while (ctx->pa_mech == NULL);
    2324             : 
    2325       44703 :     _krb5_debug(context, 5, "Stepping pa-mech: %s", ctx->pa_mech->patype->name);
    2326             : 
    2327       44703 :     ret = ctx->pa_mech->patype->step(context, ctx, (void *)&ctx->pa_mech->pactx[0], pa, a, rep, in_md, out_md);
    2328       44703 :     _krb5_debug(context, 10, "PA type %s returned %d", ctx->pa_mech->patype->name, ret);
    2329       44703 :     if (ret == 0) {
    2330       13769 :         struct pa_auth_mech *next_pa = ctx->pa_mech->next;
    2331             : 
    2332       13769 :         if (next_pa) {
    2333           0 :             _krb5_debug(context, 5, "Next PA type in set is: %s",
    2334           0 :                          next_pa->patype->name);
    2335           0 :             ret = HEIM_ERR_PA_CONTINUE_NEEDED;
    2336       13769 :         } else if (rep == NULL) {
    2337           0 :             _krb5_debug(context, 5, "PA %s done, but no ticket in sight!!!",
    2338           0 :                          ctx->pa_mech->patype->name);
    2339           0 :             ret = HEIM_ERR_PA_CANT_CONTINUE;
    2340             :         } else {
    2341       13769 :             ctx->pa_used = ctx->pa_mech->patype->name;
    2342             :         }
    2343             : 
    2344       13769 :         heim_retain(next_pa);
    2345       13769 :         heim_release(ctx->pa_mech);
    2346       13769 :         ctx->pa_mech = next_pa;
    2347             :     }
    2348             : 
    2349       44703 :     if (ret == HEIM_ERR_PA_CANT_CONTINUE) {
    2350           0 :         if (ctx->pa_mech) {
    2351           0 :             _krb5_debug(context, 5, "Dropping PA type %s", ctx->pa_mech->patype->name);
    2352           0 :             heim_release(ctx->pa_mech);
    2353           0 :             ctx->pa_mech = NULL;
    2354             :         }
    2355           0 :         goto next;
    2356       44703 :     } else if (ret == HEIM_ERR_PA_CONTINUE_NEEDED) {
    2357       30934 :         _krb5_debug(context, 5, "Continue needed for %s", ctx->pa_mech->patype->name);
    2358       13769 :     } else if (ret != 0) {
    2359           0 :         _krb5_debug(context, 5, "Other error from mech %s: %d", ctx->pa_mech->patype->name, ret);
    2360           0 :         heim_release(ctx->pa_mech);
    2361           0 :         ctx->pa_mech = NULL;
    2362             :     }
    2363             : 
    2364       42927 :     return ret;
    2365             : }
    2366             : 
    2367             : static void
    2368       44083 : log_kdc_pa_types(krb5_context context, METHOD_DATA *in_md)
    2369             : {
    2370       44083 :     if (_krb5_have_debug(context, 5)) {
    2371           0 :         unsigned i;
    2372           4 :         _krb5_debug(context, 5, "KDC sent %d patypes", in_md->len);
    2373          10 :         for (i = 0; i < in_md->len; i++)
    2374          12 :             _krb5_debug(context, 5, "KDC sent PA-DATA type: %d (%s)",
    2375           6 :                          in_md->val[i].padata_type,
    2376           6 :                          get_pa_type_name(in_md->val[i].padata_type));
    2377             :     }
    2378       44083 : }
    2379             : 
    2380             : /*
    2381             :  * Assumes caller always will free `out_md', even on error.
    2382             :  */
    2383             : 
    2384             : static krb5_error_code
    2385       30934 : process_pa_data_to_md(krb5_context context,
    2386             :                       const krb5_creds *creds,
    2387             :                       const AS_REQ *a,
    2388             :                       krb5_init_creds_context ctx,
    2389             :                       METHOD_DATA *in_md,
    2390             :                       METHOD_DATA **out_md)
    2391             : {
    2392        1184 :     krb5_error_code ret;
    2393             : 
    2394       30934 :     ALLOC(*out_md, 1);
    2395       30934 :     if (*out_md == NULL) {
    2396           0 :         return krb5_enomem(context);
    2397             :     }
    2398       30934 :     (*out_md)->len = 0;
    2399       30934 :     (*out_md)->val = NULL;
    2400             : 
    2401       30934 :     log_kdc_pa_types(context, in_md);
    2402             : 
    2403       30934 :     ret = pa_step(context, ctx, a, NULL, in_md, *out_md);
    2404       30934 :     if (ret == HEIM_ERR_PA_CONTINUE_NEEDED) {
    2405       30934 :         _krb5_debug(context, 0, "pamech need more stepping");
    2406           0 :     } else if (ret == 0) {
    2407           0 :         _krb5_debug(context, 0, "pamech done step");
    2408             :     } else {
    2409           0 :         return ret;
    2410             :     }
    2411             : 
    2412             :     /*
    2413             :      * Send announcement (what we support) and configuration (user
    2414             :      * introduced behavior change)
    2415             :      */
    2416       30934 :     ret = pa_announce(context, PA_F_ANNOUNCE|PA_F_CONFIG, ctx, in_md, *out_md);
    2417             : 
    2418             :     /*
    2419             :      *
    2420             :      */
    2421             : 
    2422       30934 :     if ((*out_md)->len == 0) {
    2423           0 :         free(*out_md);
    2424           0 :         *out_md = NULL;
    2425             :     }
    2426             : 
    2427       29750 :     return ret;
    2428             : }
    2429             : 
    2430             : static krb5_error_code
    2431       13769 : process_pa_data_to_key(krb5_context context,
    2432             :                        krb5_init_creds_context ctx,
    2433             :                        krb5_creds *creds,
    2434             :                        AS_REQ *a,
    2435             :                        AS_REP *rep,
    2436             :                        krb5_keyblock **key)
    2437             : {
    2438       13769 :     struct pa_info_data paid, *ppaid = NULL;
    2439         592 :     krb5_error_code ret;
    2440       13769 :     krb5_enctype etype = rep->enc_part.etype;
    2441             : 
    2442       13769 :     memset(&paid, 0, sizeof(paid));
    2443             : 
    2444       13769 :     if (rep->padata)
    2445       13149 :         log_kdc_pa_types(context, rep->padata);
    2446             : 
    2447       13769 :     if (rep->padata) {
    2448       13149 :         paid.etype = etype;
    2449       13149 :         ppaid = process_pa_info(context, creds->client, a, &paid,
    2450             :                                 rep->padata);
    2451             :     }
    2452       13763 :     if (ppaid == NULL) {
    2453         633 :         if (ctx->paid.etype == KRB5_ENCTYPE_NULL) {
    2454          13 :             ctx->paid.etype = etype;
    2455          13 :             ctx->paid.s2kparams = NULL;
    2456          13 :             ret = krb5_get_pw_salt (context, creds->client, &ctx->paid.salt);
    2457          13 :             if (ret)
    2458           0 :                 return ret;
    2459             :         }
    2460             :     }
    2461             : 
    2462       13769 :     ret = pa_step(context, ctx, a, rep, rep->padata, NULL);
    2463       13769 :     if (ret == HEIM_ERR_PA_CONTINUE_NEEDED) {
    2464           0 :         _krb5_debug(context, 0, "In final stretch and pa require more stepping ?");
    2465           0 :         return ret;
    2466       13769 :     } else if (ret == 0) {
    2467       13769 :         _krb5_debug(context, 0, "final pamech done step");
    2468       13769 :         goto out;
    2469             :     } else {
    2470           0 :         return ret;
    2471             :     }
    2472       13769 :  out:
    2473       13769 :     free_paid(context, &paid);
    2474       13769 :     return ret;
    2475             : }
    2476             : 
    2477             : /*
    2478             :  *
    2479             :  */
    2480             : 
    2481             : static krb5_error_code
    2482       16303 : capture_lkdc_domain(krb5_context context,
    2483             :                     krb5_init_creds_context ctx)
    2484             : {
    2485         592 :     size_t len;
    2486             : 
    2487       16303 :     len = strlen(_krb5_wellknown_lkdc);
    2488             : 
    2489       16303 :     if (ctx->kdc_hostname != NULL ||
    2490       16303 :         strncmp(ctx->cred.client->realm, _krb5_wellknown_lkdc, len) != 0 ||
    2491           0 :         ctx->cred.client->realm[len] != ':')
    2492       15711 :         return 0;
    2493             : 
    2494           0 :     ctx->kdc_hostname = strdup(&ctx->cred.client->realm[len + 1]);
    2495             : 
    2496           0 :     _krb5_debug(context, 5, "krb5_get_init_creds: setting LKDC hostname to: %s",
    2497             :                 ctx->kdc_hostname);
    2498           0 :     return 0;
    2499             : }
    2500             : 
    2501             : /**
    2502             :  * Start a new context to get a new initial credential.
    2503             :  *
    2504             :  * @param context A Kerberos 5 context.
    2505             :  * @param client The Kerberos principal to get the credential for, if
    2506             :  *     NULL is given, the default principal is used as determined by
    2507             :  *     krb5_get_default_principal().
    2508             :  * @param prompter
    2509             :  * @param prompter_data
    2510             :  * @param start_time the time the ticket should start to be valid or 0 for now.
    2511             :  * @param options a options structure, can be NULL for default options.
    2512             :  * @param rctx A new allocated free with krb5_init_creds_free().
    2513             :  *
    2514             :  * @return 0 for success or an Kerberos 5 error code, see krb5_get_error_message().
    2515             :  *
    2516             :  * @ingroup krb5_credential
    2517             :  */
    2518             : 
    2519             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    2520       16303 : krb5_init_creds_init(krb5_context context,
    2521             :                      krb5_principal client,
    2522             :                      krb5_prompter_fct prompter,
    2523             :                      void *prompter_data,
    2524             :                      krb5_deltat start_time,
    2525             :                      krb5_get_init_creds_opt *options,
    2526             :                      krb5_init_creds_context *rctx)
    2527             : {
    2528         592 :     krb5_init_creds_context ctx;
    2529         592 :     krb5_error_code ret;
    2530             : 
    2531       16303 :     *rctx = NULL;
    2532             : 
    2533       16303 :     ctx = calloc(1, sizeof(*ctx));
    2534       16303 :     if (ctx == NULL)
    2535           0 :         return krb5_enomem(context);
    2536             : 
    2537       16303 :     ret = get_init_creds_common(context, client, prompter, prompter_data,
    2538             :                                  start_time, options, ctx);
    2539       16303 :     if (ret) {
    2540           0 :         free(ctx);
    2541           0 :         return ret;
    2542             :     }
    2543             : 
    2544             :     /* Set a new nonce. */
    2545             :     /* FIXME should generate a new nonce for each AS-REQ */
    2546       16303 :     krb5_generate_random_block (&ctx->nonce, sizeof(ctx->nonce));
    2547       16303 :     ctx->nonce &= 0x7fffffff;
    2548             :     /* XXX these just need to be the same when using Windows PK-INIT */
    2549       16303 :     ctx->pk_nonce = ctx->nonce;
    2550             : 
    2551       16303 :     ctx->prompter = prompter;
    2552       16303 :     ctx->prompter_data = prompter_data;
    2553             : 
    2554             :     /* pick up hostname from LKDC realm name */
    2555       16303 :     ret = capture_lkdc_domain(context, ctx);
    2556       16303 :     if (ret) {
    2557           0 :         free_init_creds_ctx(context, ctx);
    2558           0 :         return ret;
    2559             :     }
    2560             : 
    2561       16303 :     ctx->runflags.allow_enc_pa_rep = 1;
    2562             : 
    2563       16303 :     ctx->fast_state.flags |= KRB5_FAST_AS_REQ;
    2564             : 
    2565       16303 :     *rctx = ctx;
    2566             : 
    2567       16303 :     return ret;
    2568             : }
    2569             : 
    2570             : /**
    2571             :  * Set the KDC hostname for the initial request, it will not be
    2572             :  * considered in referrals to another KDC.
    2573             :  *
    2574             :  * @param context a Kerberos 5 context.
    2575             :  * @param ctx a krb5_init_creds_context context.
    2576             :  * @param hostname the hostname for the KDC of realm
    2577             :  *
    2578             :  * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
    2579             :  * @ingroup krb5_credential
    2580             :  */
    2581             : 
    2582             : krb5_error_code KRB5_LIB_FUNCTION
    2583           0 : krb5_init_creds_set_kdc_hostname(krb5_context context,
    2584             :                                  krb5_init_creds_context ctx,
    2585             :                                  const char *hostname)
    2586             : {
    2587           0 :     if (ctx->kdc_hostname)
    2588           0 :         free(ctx->kdc_hostname);
    2589           0 :     ctx->kdc_hostname = strdup(hostname);
    2590           0 :     if (ctx->kdc_hostname == NULL)
    2591           0 :         return krb5_enomem(context);
    2592           0 :     return 0;
    2593             : }
    2594             : 
    2595             : /**
    2596             :  * Set the sitename for the request
    2597             :  *
    2598             :  */
    2599             : 
    2600             : krb5_error_code KRB5_LIB_FUNCTION
    2601           0 : krb5_init_creds_set_sitename(krb5_context context,
    2602             :                              krb5_init_creds_context ctx,
    2603             :                              const char *sitename)
    2604             : {
    2605           0 :     if (ctx->sitename)
    2606           0 :         free(ctx->sitename);
    2607           0 :     ctx->sitename = strdup(sitename);
    2608           0 :     if (ctx->sitename == NULL)
    2609           0 :         return krb5_enomem(context);
    2610           0 :     return 0;
    2611             : }
    2612             : 
    2613             : /**
    2614             :  * Sets the service that the is requested. This call is only neede for
    2615             :  * special initial tickets, by default the a krbtgt is fetched in the default realm.
    2616             :  *
    2617             :  * @param context a Kerberos 5 context.
    2618             :  * @param ctx a krb5_init_creds_context context.
    2619             :  * @param service the service given as a string, for example
    2620             :  *        "kadmind/admin". If NULL, the default krbtgt in the clients
    2621             :  *        realm is set.
    2622             :  *
    2623             :  * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
    2624             :  * @ingroup krb5_credential
    2625             :  */
    2626             : 
    2627             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    2628       32384 : krb5_init_creds_set_service(krb5_context context,
    2629             :                             krb5_init_creds_context ctx,
    2630             :                             const char *service)
    2631             : {
    2632        1184 :     krb5_const_realm client_realm;
    2633        1184 :     krb5_principal principal;
    2634        1184 :     krb5_error_code ret;
    2635             : 
    2636       32384 :     client_realm = krb5_principal_get_realm (context, ctx->cred.client);
    2637             : 
    2638       32384 :     if (service) {
    2639          19 :         ret = krb5_parse_name (context, service, &principal);
    2640          19 :         if (ret)
    2641           0 :             return ret;
    2642          19 :         ret = krb5_principal_set_realm (context, principal, client_realm);
    2643          19 :         if (ret) {
    2644           0 :             krb5_free_principal(context, principal);
    2645           0 :             return ret;
    2646             :         }
    2647             :     } else {
    2648       32365 :         ret = krb5_make_principal(context, &principal,
    2649             :                                   client_realm, KRB5_TGS_NAME, client_realm,
    2650             :                                   NULL);
    2651       32365 :         if (ret)
    2652           0 :             return ret;
    2653             :     }
    2654             : 
    2655             :     /*
    2656             :      * This is for Windows RODC that are picky about what name type
    2657             :      * the server principal have, and the really strange part is that
    2658             :      * they are picky about the AS-REQ name type and not the TGS-REQ
    2659             :      * later. Oh well.
    2660             :      */
    2661             : 
    2662       32384 :     if (krb5_principal_is_krbtgt(context, principal))
    2663       32368 :         krb5_principal_set_type(context, principal, KRB5_NT_SRV_INST);
    2664             : 
    2665       32384 :     krb5_free_principal(context, ctx->cred.server);
    2666       32384 :     ctx->cred.server = principal;
    2667             : 
    2668       32384 :     return 0;
    2669             : }
    2670             : 
    2671             : /**
    2672             :  * Sets the password that will use for the request.
    2673             :  *
    2674             :  * @param context a Kerberos 5 context.
    2675             :  * @param ctx ctx krb5_init_creds_context context.
    2676             :  * @param password the password to use.
    2677             :  *
    2678             :  * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
    2679             :  * @ingroup krb5_credential
    2680             :  */
    2681             : 
    2682             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    2683       16162 : krb5_init_creds_set_password(krb5_context context,
    2684             :                              krb5_init_creds_context ctx,
    2685             :                              const char *password)
    2686             : {
    2687       16162 :     if (ctx->password) {
    2688           0 :         size_t len;
    2689           4 :         len = strlen(ctx->password);
    2690           4 :         memset_s(ctx->password, len, 0, len);
    2691           4 :         free(ctx->password);
    2692             :     }
    2693       16162 :     if (password) {
    2694       16162 :         ctx->password = strdup(password);
    2695       16162 :         if (ctx->password == NULL)
    2696           0 :             return krb5_enomem(context);
    2697       16162 :         ctx->keyseed = (void *) ctx->password;
    2698             :     } else {
    2699           0 :         ctx->keyseed = NULL;
    2700           0 :         ctx->password = NULL;
    2701             :     }
    2702             : 
    2703       15573 :     return 0;
    2704             : }
    2705             : 
    2706             : static krb5_error_code KRB5_CALLCONV
    2707          14 : keytab_key_proc(krb5_context context, krb5_enctype enctype,
    2708             :                 krb5_const_pointer keyseed,
    2709             :                 krb5_salt salt, krb5_data *s2kparms,
    2710             :                 krb5_keyblock **key)
    2711             : {
    2712          14 :     krb5_keytab_key_proc_args *args  = rk_UNCONST(keyseed);
    2713          14 :     krb5_keytab keytab = args->keytab;
    2714          14 :     krb5_principal principal = args->principal;
    2715           0 :     krb5_error_code ret;
    2716          14 :     krb5_keytab real_keytab = NULL;
    2717           0 :     krb5_keytab_entry entry;
    2718             : 
    2719          14 :     if (keytab == NULL) {
    2720           0 :         ret = krb5_kt_default(context, &real_keytab);
    2721           0 :         if (ret)
    2722           0 :             return ret;
    2723           0 :         keytab = real_keytab;
    2724             :     }
    2725             : 
    2726          14 :     ret = krb5_kt_get_entry (context, keytab, principal, 0, enctype, &entry);
    2727          14 :     if (ret == 0) {
    2728          14 :         ret = krb5_copy_keyblock(context, &entry.keyblock, key);
    2729          14 :         krb5_kt_free_entry(context, &entry);
    2730             :     }
    2731             : 
    2732          14 :     krb5_kt_close(context, real_keytab);
    2733          14 :     return ret;
    2734             : }
    2735             : 
    2736             : 
    2737             : /**
    2738             :  * Set the keytab to use for authentication.
    2739             :  *
    2740             :  * @param context a Kerberos 5 context.
    2741             :  * @param ctx ctx krb5_init_creds_context context.
    2742             :  * @param keytab the keytab to read the key from.
    2743             :  *
    2744             :  * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
    2745             :  * @ingroup krb5_credential
    2746             :  */
    2747             : 
    2748             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    2749           7 : krb5_init_creds_set_keytab(krb5_context context,
    2750             :                            krb5_init_creds_context ctx,
    2751             :                            krb5_keytab keytab)
    2752             : {
    2753           0 :     krb5_keytab_key_proc_args *a;
    2754           0 :     krb5_keytab_entry entry;
    2755           0 :     krb5_kt_cursor cursor;
    2756           7 :     krb5_enctype *etypes = NULL;
    2757           0 :     krb5_error_code ret;
    2758           7 :     size_t netypes = 0;
    2759           7 :     int kvno = 0, found = 0;
    2760           0 :     unsigned n;
    2761             : 
    2762           7 :     a = malloc(sizeof(*a));
    2763           7 :     if (a == NULL)
    2764           0 :         return krb5_enomem(context);
    2765             : 
    2766           7 :     a->principal = ctx->cred.client;
    2767           7 :     a->keytab    = keytab;
    2768             : 
    2769           7 :     ctx->keytab_data = a;
    2770           7 :     ctx->keyseed = (void *)a;
    2771           7 :     ctx->keyproc = keytab_key_proc;
    2772             : 
    2773             :     /*
    2774             :      * We need to tell the KDC what enctypes we support for this keytab,
    2775             :      * especially if the keytab is really a password based entry, then the
    2776             :      * KDC might have more enctypes in the database then what we have
    2777             :      * in the keytab.
    2778             :      */
    2779             : 
    2780           7 :     ret = krb5_kt_start_seq_get(context, keytab, &cursor);
    2781           7 :     if(ret)
    2782           0 :         goto out;
    2783             : 
    2784         117 :     while(krb5_kt_next_entry(context, keytab, &entry, &cursor) == 0){
    2785           0 :         void *ptr;
    2786             : 
    2787         110 :         if (!krb5_principal_compare(context, entry.principal, ctx->cred.client))
    2788          83 :             goto next;
    2789             : 
    2790          27 :         found = 1;
    2791             : 
    2792             :         /* check if we have this kvno already */
    2793          27 :         if (entry.vno > kvno) {
    2794             :             /* remove old list of etype */
    2795           7 :             if (etypes)
    2796           0 :                 free(etypes);
    2797           7 :             etypes = NULL;
    2798           7 :             netypes = 0;
    2799           7 :             kvno = entry.vno;
    2800          20 :         } else if (entry.vno != kvno)
    2801           6 :             goto next;
    2802             : 
    2803             :         /* check if enctype is supported */
    2804          21 :         if (krb5_enctype_valid(context, entry.keyblock.keytype) != 0)
    2805           0 :             goto next;
    2806             : 
    2807             :         /*
    2808             :          * If user already provided a enctype list, use that as an
    2809             :          * additonal filter.
    2810             :          */
    2811          21 :         if (ctx->etypes) {
    2812          10 :             for (n = 0; ctx->etypes[n] != KRB5_ENCTYPE_NULL; n++) {
    2813           6 :                 if (ctx->etypes[n] == entry.keyblock.keytype)
    2814           2 :                     break;
    2815             :             }
    2816           6 :             if (ctx->etypes[n] == KRB5_ENCTYPE_NULL)
    2817           4 :                 goto next;
    2818             :         }
    2819             : 
    2820             :         /* add enctype to supported list */
    2821          17 :         ptr = realloc(etypes, sizeof(etypes[0]) * (netypes + 2));
    2822          17 :         if (ptr == NULL) {
    2823           0 :             free(etypes);
    2824           0 :             ret = krb5_enomem(context);
    2825           0 :             goto out;
    2826             :         }
    2827             : 
    2828          17 :         etypes = ptr;
    2829          17 :         etypes[netypes] = entry.keyblock.keytype;
    2830          17 :         etypes[netypes + 1] = ETYPE_NULL;
    2831          17 :         netypes++;
    2832         110 :     next:
    2833         110 :         krb5_kt_free_entry(context, &entry);
    2834             :     }
    2835           7 :     krb5_kt_end_seq_get(context, keytab, &cursor);
    2836             : 
    2837           7 :     if (etypes) {
    2838           7 :         if (ctx->etypes)
    2839           2 :             free(ctx->etypes);
    2840           7 :         ctx->etypes = etypes;
    2841             :     }
    2842             : 
    2843           0 :  out:
    2844           7 :     if (!found) {
    2845           0 :         if (ret == 0)
    2846           0 :             ret = KRB5_KT_NOTFOUND;
    2847           0 :         _krb5_kt_principal_not_found(context, ret, keytab, ctx->cred.client, 0, 0);
    2848             :     }
    2849             : 
    2850           7 :     return ret;
    2851             : }
    2852             : 
    2853             : static krb5_error_code KRB5_CALLCONV
    2854          26 : keyblock_key_proc(krb5_context context, krb5_enctype enctype,
    2855             :                   krb5_const_pointer keyseed,
    2856             :                   krb5_salt salt, krb5_data *s2kparms,
    2857             :                   krb5_keyblock **key)
    2858             : {
    2859          26 :     return krb5_copy_keyblock (context, keyseed, key);
    2860             : }
    2861             : 
    2862             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    2863          14 : krb5_init_creds_set_keyblock(krb5_context context,
    2864             :                              krb5_init_creds_context ctx,
    2865             :                              krb5_keyblock *keyblock)
    2866             : {
    2867          14 :     ctx->keyseed = (void *)keyblock;
    2868          14 :     ctx->keyproc = keyblock_key_proc;
    2869             : 
    2870          14 :     return 0;
    2871             : }
    2872             : 
    2873             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    2874           0 : krb5_init_creds_set_fast_ccache(krb5_context context,
    2875             :                                 krb5_init_creds_context ctx,
    2876             :                                 krb5_ccache fast_ccache)
    2877             : {
    2878           0 :     ctx->fast_state.armor_ccache = fast_ccache;
    2879           0 :     ctx->fast_state.flags |= KRB5_FAST_REQUIRED;
    2880           0 :     ctx->fast_state.flags |= KRB5_FAST_KDC_VERIFIED;
    2881           0 :     return 0;
    2882             : }
    2883             : 
    2884             : static krb5_error_code
    2885       13757 : validate_pkinit_fx(krb5_context context,
    2886             :                    krb5_init_creds_context ctx,
    2887             :                    AS_REP *rep,
    2888             :                    krb5_keyblock *ticket_sessionkey)
    2889             : {
    2890       13757 :     PA_DATA *pa = NULL;
    2891       13757 :     int idx = 0;
    2892             : 
    2893       13757 :     if (rep->padata)
    2894       13139 :         pa = krb5_find_padata(rep->padata->val, rep->padata->len, KRB5_PADATA_PKINIT_KX, &idx);
    2895             : 
    2896       13751 :     if (pa == NULL) {
    2897       13744 :         if (ctx->flags.request_anonymous && ctx->pk_init_ctx) {
    2898             :             /* XXX handle the case where pkinit is not used */
    2899           0 :             krb5_set_error_message(context, KRB5_KDCREP_MODIFIED,
    2900           0 :                                    N_("Requested anonymous with PKINIT and KDC didn't set PKINIT_KX", ""));
    2901           0 :             return KRB5_KDCREP_MODIFIED;
    2902             :         }
    2903             : 
    2904       13152 :         return 0;
    2905             :     }
    2906             : 
    2907          13 :     heim_assert(ctx->fast_state.reply_key != NULL, "must have a reply key at this stage");
    2908             : 
    2909          13 :     return _krb5_pk_kx_confirm(context,
    2910             :                                ctx->pk_init_ctx,
    2911             :                                ctx->fast_state.reply_key,
    2912             :                                ticket_sessionkey,
    2913             :                                pa);
    2914             : }
    2915             : 
    2916             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    2917           0 : krb5_init_creds_set_fast_ap_armor_service(krb5_context context,
    2918             :                                           krb5_init_creds_context ctx,
    2919             :                                           krb5_const_principal armor_service)
    2920             : {
    2921           0 :     krb5_error_code ret;
    2922             : 
    2923           0 :     if (ctx->fast_state.armor_service)
    2924           0 :         krb5_free_principal(context, ctx->fast_state.armor_service);
    2925           0 :     if (armor_service) {
    2926           0 :         ret = krb5_copy_principal(context, armor_service, &ctx->fast_state.armor_service);
    2927           0 :         if (ret)
    2928           0 :             return ret;
    2929             :     } else {
    2930           0 :         ctx->fast_state.armor_service = NULL;
    2931             :     }
    2932           0 :     ctx->fast_state.flags |= KRB5_FAST_AP_ARMOR_SERVICE;
    2933           0 :     return 0;
    2934             : }
    2935             : 
    2936             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    2937           0 : krb5_init_creds_set_fast_anon_pkinit(krb5_context context,
    2938             :                                      krb5_init_creds_context ctx)
    2939             : {
    2940           0 :     if (ctx->fast_state.armor_ccache)
    2941           0 :         return EINVAL;
    2942             : 
    2943           0 :     ctx->fast_state.flags |= KRB5_FAST_REQUIRED;
    2944           0 :     ctx->fast_state.flags |= KRB5_FAST_ANON_PKINIT_ARMOR;
    2945           0 :     return 0;
    2946             : }
    2947             : 
    2948             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    2949         104 : _krb5_init_creds_set_fast_anon_pkinit_optimistic(krb5_context context,
    2950             :                                                  krb5_init_creds_context ctx)
    2951             : {
    2952         104 :     if (ctx->fast_state.armor_ccache)
    2953           0 :         return EINVAL;
    2954             : 
    2955         104 :     ctx->fast_state.flags |= KRB5_FAST_REQUIRED;
    2956         104 :     ctx->fast_state.flags |= KRB5_FAST_ANON_PKINIT_ARMOR;
    2957         104 :     ctx->fast_state.flags |= KRB5_FAST_OPTIMISTIC;
    2958         104 :     return 0;
    2959             : }
    2960             : 
    2961             : static size_t
    2962       13525 : available_padata_count(METHOD_DATA *md)
    2963             : {
    2964       13525 :     size_t i, count = 0;
    2965             : 
    2966       84541 :     for (i = 0; i < md->len; i++) {
    2967       70424 :         PA_DATA *pa = &md->val[i];
    2968             : 
    2969       70424 :         if (pa->padata_type == KRB5_PADATA_FX_COOKIE ||
    2970       67457 :             pa->padata_type == KRB5_PADATA_FX_ERROR)
    2971          14 :             continue;
    2972             : 
    2973       70410 :         count++;
    2974             :     }
    2975             : 
    2976       14117 :     return count;
    2977             : }
    2978             : 
    2979             : static krb5_error_code
    2980       45528 : init_creds_step(krb5_context context,
    2981             :                 krb5_init_creds_context ctx,
    2982             :                 const krb5_data *in,
    2983             :                 krb5_data *out,
    2984             :                 krb5_realm *out_realm,
    2985             :                 unsigned int *flags)
    2986             : {
    2987        1776 :     struct timeval start_time, end_time;
    2988        1776 :     krb5_data checksum_data;
    2989        1776 :     krb5_error_code ret;
    2990       45528 :     size_t len = 0;
    2991        1776 :     size_t size;
    2992        1776 :     AS_REQ req2;
    2993             : 
    2994       45528 :     gettimeofday(&start_time, NULL);
    2995             : 
    2996       45528 :     krb5_data_zero(out);
    2997       45528 :     *out_realm = NULL;
    2998       45528 :     krb5_data_zero(&checksum_data);
    2999             : 
    3000       45528 :     if (ctx->as_req.req_body.cname == NULL) {
    3001       16895 :         ret = init_as_req(context, ctx->flags, &ctx->cred,
    3002       16303 :                           ctx->addrs, ctx->etypes, &ctx->as_req);
    3003       16303 :         if (ret)
    3004           0 :             return ret;
    3005       16303 :         if (ctx->fast_state.flags & KRB5_FAST_REQUIRED)
    3006             :             ;
    3007       16293 :         else if (ctx->fast_state.flags & KRB5_FAST_AP_ARMOR_SERVICE)
    3008             :             /* Check with armor service if there is FAST */;
    3009             :         else
    3010       16293 :             ctx->fast_state.flags |= KRB5_FAST_DISABLED;
    3011             : 
    3012             : 
    3013             :         /* XXX should happen after we get back reply from KDC */
    3014       16303 :         pa_configure(context, ctx, NULL);
    3015             :     }
    3016             : 
    3017             : #define MAX_PA_COUNTER 15
    3018       45528 :     if (ctx->pa_counter > MAX_PA_COUNTER) {
    3019           0 :         krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
    3020           0 :                                N_("Looping %d times while getting "
    3021             :                                   "initial credentials", ""),
    3022             :                                ctx->pa_counter);
    3023           0 :         return KRB5_GET_IN_TKT_LOOP;
    3024             :     }
    3025       45528 :     ctx->pa_counter++;
    3026             : 
    3027       45528 :     _krb5_debug(context, 5, "krb5_get_init_creds: loop %d", ctx->pa_counter);
    3028             : 
    3029             :     /* Lets process the input packet */
    3030       45528 :     if (in && in->length) {
    3031        1184 :         krb5_kdc_rep rep;
    3032             : 
    3033       29225 :         memset(&rep, 0, sizeof(rep));
    3034             : 
    3035       29225 :         _krb5_debug(context, 5, "krb5_get_init_creds: processing input");
    3036             : 
    3037       29225 :         ret = decode_AS_REP(in->data, in->length, &rep.kdc_rep, &size);
    3038       29225 :         if (ret == 0) {
    3039       13769 :             unsigned eflags = EXTRACT_TICKET_AS_REQ | EXTRACT_TICKET_TIMESYNC;
    3040         592 :             krb5_data data;
    3041             : 
    3042             :             /*
    3043             :              * Unwrap AS-REP
    3044             :              */
    3045       13769 :             ASN1_MALLOC_ENCODE(Ticket, data.data, data.length,
    3046             :                                &rep.kdc_rep.ticket, &size, ret);
    3047       13769 :             if (ret)
    3048           0 :                 goto out;
    3049       13769 :             heim_assert(data.length == size, "ASN.1 internal error");
    3050             : 
    3051       13769 :             ret = _krb5_fast_unwrap_kdc_rep(context, ctx->nonce, &data,
    3052             :                                             &ctx->fast_state, &rep.kdc_rep);
    3053       13769 :             krb5_data_free(&data);
    3054       13769 :             if (ret)
    3055           0 :                 goto out;
    3056             : 
    3057             :             /*
    3058             :              * Now check and extract the ticket
    3059             :              */
    3060             : 
    3061       13769 :             if (ctx->flags.canonicalize) {
    3062       12982 :                 eflags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH;
    3063       12982 :                 eflags |= EXTRACT_TICKET_MATCH_REALM;
    3064             :             }
    3065       13769 :             if (ctx->ic_flags & KRB5_INIT_CREDS_NO_C_CANON_CHECK)
    3066       12752 :                 eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
    3067       13769 :             if (ctx->flags.request_anonymous)
    3068           0 :                 eflags |= EXTRACT_TICKET_MATCH_ANON;
    3069             : 
    3070       13769 :             ret = process_pa_data_to_key(context, ctx, &ctx->cred,
    3071             :                                          &ctx->as_req, &rep.kdc_rep,
    3072             :                                          &ctx->fast_state.reply_key);
    3073       13769 :             if (ret) {
    3074           0 :                 free_AS_REP(&rep.kdc_rep);
    3075           0 :                 goto out;
    3076             :             }
    3077             : 
    3078       13769 :             if (ctx->fast_state.strengthen_key) {
    3079           0 :                 krb5_keyblock result;
    3080             : 
    3081           7 :                 _krb5_debug(context, 5, "krb5_get_init_creds: FAST strengthen_key");
    3082             : 
    3083           7 :                 ret = _krb5_fast_cf2(context,
    3084             :                                      ctx->fast_state.strengthen_key,
    3085             :                                      "strengthenkey",
    3086             :                                      ctx->fast_state.reply_key,
    3087             :                                      "replykey",
    3088             :                                      &result,
    3089             :                                      NULL);
    3090           7 :                 if (ret) {
    3091           0 :                     free_AS_REP(&rep.kdc_rep);
    3092           0 :                     goto out;
    3093             :                 }
    3094             : 
    3095           7 :                 ctx->runflags.allow_save_as_reply_key = 1;
    3096             : 
    3097           7 :                 krb5_free_keyblock_contents(context, ctx->fast_state.reply_key);
    3098           7 :                 *ctx->fast_state.reply_key = result;
    3099             :             }
    3100             : 
    3101       13769 :             _krb5_debug(context, 5, "krb5_get_init_creds: extracting ticket");
    3102             : 
    3103       13769 :             ret = _krb5_extract_ticket(context,
    3104             :                                        &rep,
    3105             :                                        &ctx->cred,
    3106             :                                        ctx->fast_state.reply_key,
    3107             :                                        NULL,
    3108             :                                        KRB5_KU_AS_REP_ENC_PART,
    3109             :                                        NULL,
    3110             :                                        ctx->nonce,
    3111             :                                        eflags,
    3112             :                                        &ctx->req_buffer,
    3113             :                                        NULL,
    3114             :                                        NULL);
    3115             : 
    3116       13769 :             if (ret == 0)
    3117       13757 :                 ret = copy_EncKDCRepPart(&rep.enc_part, &ctx->enc_part);
    3118       13769 :             if (ret == 0)
    3119       13757 :                 ret = validate_pkinit_fx(context, ctx, &rep.kdc_rep, &ctx->cred.session);
    3120             : 
    3121       13769 :             ctx->as_enctype = ctx->fast_state.reply_key->keytype;
    3122             : 
    3123       13769 :             if (ctx->runflags.allow_save_as_reply_key) {
    3124          20 :                 ctx->as_reply_key = ctx->fast_state.reply_key;
    3125          20 :                 ctx->fast_state.reply_key = NULL;
    3126             :             } else {
    3127       13749 :                 krb5_free_keyblock(context, ctx->fast_state.reply_key);
    3128       13749 :                 ctx->fast_state.reply_key = NULL;
    3129             :             }
    3130       13769 :             ctx->ic_flags |= KRB5_INIT_CREDS_DONE;
    3131       13769 :             *flags = 0;
    3132             : 
    3133       13769 :             free_AS_REP(&rep.kdc_rep);
    3134       13769 :             free_EncASRepPart(&rep.enc_part);
    3135             : 
    3136       13769 :             gettimeofday(&end_time, NULL);
    3137       13769 :             timevalsub(&end_time, &start_time);
    3138       13769 :             timevaladd(&ctx->stats.run_time, &end_time);
    3139             : 
    3140       13769 :             _krb5_debug(context, 1, "krb5_get_init_creds: wc: %lld.%06ld",
    3141       13769 :                         (long long)ctx->stats.run_time.tv_sec,
    3142       13769 :                         (long)ctx->stats.run_time.tv_usec);
    3143       13769 :             return ret;
    3144             : 
    3145             :         } else {
    3146             :             /* let's try to parse it as a KRB-ERROR */
    3147             : 
    3148       15456 :             _krb5_debug(context, 5, "krb5_get_init_creds: got an KRB-ERROR from KDC");
    3149             : 
    3150       15456 :             free_KRB_ERROR(&ctx->error);
    3151             : 
    3152       15456 :             ret = krb5_rd_error(context, in, &ctx->error);
    3153       15456 :             if(ret && in->length && ((char*)in->data)[0] == 4)
    3154           0 :                 ret = KRB5KRB_AP_ERR_V4_REPLY;
    3155       15456 :             if (ret) {
    3156           0 :                 _krb5_debug(context, 5, "krb5_get_init_creds: failed to read error");
    3157           0 :                 goto out;
    3158             :             }
    3159             : 
    3160             :             /*
    3161             :              * Unwrap method-data, if there is any,
    3162             :              * fast_unwrap_error() below might replace it with a
    3163             :              * wrapped version if we are using FAST.
    3164             :              */
    3165             : 
    3166       15456 :             free_METHOD_DATA(&ctx->md);
    3167       15456 :             memset(&ctx->md, 0, sizeof(ctx->md));
    3168             : 
    3169       15456 :             if (ctx->error.e_data) {
    3170         592 :                 KERB_ERROR_DATA error_data;
    3171         592 :                 krb5_error_code ret2;
    3172             : 
    3173       14511 :                 memset(&error_data, 0, sizeof(error_data));
    3174             : 
    3175             :                 /* First try to decode the e-data as KERB-ERROR-DATA. */
    3176       14511 :                 ret2 = decode_KERB_ERROR_DATA(ctx->error.e_data->data,
    3177       13919 :                                               ctx->error.e_data->length,
    3178             :                                               &error_data,
    3179             :                                               &len);
    3180       14511 :                 if (ret2) {
    3181             :                     /* That failed, so try to decode it as METHOD-DATA. */
    3182       15065 :                     ret2 = decode_METHOD_DATA(ctx->error.e_data->data,
    3183       14473 :                                               ctx->error.e_data->length,
    3184             :                                               &ctx->md,
    3185             :                                               NULL);
    3186       14473 :                     if (ret2) {
    3187             :                         /*
    3188             :                          * Just ignore any error, the error will be pushed
    3189             :                          * out from krb5_error_from_rd_error() if there
    3190             :                          * was one.
    3191             :                          */
    3192           0 :                         _krb5_debug(context, 5, N_("Failed to decode METHOD-DATA", ""));
    3193             :                     }
    3194          38 :                 } else if (len != ctx->error.e_data->length) {
    3195             :                     /* Trailing data — just ignore the error. */
    3196           0 :                     free_KERB_ERROR_DATA(&error_data);
    3197             :                 } else {
    3198             :                     /* OK. */
    3199          38 :                     free_KERB_ERROR_DATA(&error_data);
    3200             :                 }
    3201             :             }
    3202             : 
    3203             :             /*
    3204             :              * Unwrap KRB-ERROR, we are always calling this so that
    3205             :              * FAST can tell us if your peer KDC suddenly dropped FAST
    3206             :              * wrapping and its really an attacker's packet (or a bug
    3207             :              * in the KDC).
    3208             :              */
    3209       15456 :             ret = _krb5_fast_unwrap_error(context, ctx->nonce, &ctx->fast_state,
    3210             :                                           &ctx->md, &ctx->error);
    3211       15456 :             if (ret)
    3212           0 :                 goto out;
    3213             : 
    3214             :             /*
    3215             :              *
    3216             :              */
    3217             : 
    3218       15456 :             ret = krb5_error_from_rd_error(context, &ctx->error, &ctx->cred);
    3219             : 
    3220             :             /* log the failure */
    3221       15456 :             if (_krb5_have_debug(context, 5)) {
    3222           2 :                 const char *str = krb5_get_error_message(context, ret);
    3223           2 :                 _krb5_debug(context, 5, "krb5_get_init_creds: KRB-ERROR %d/%s", ret, str);
    3224           2 :                 krb5_free_error_message(context, str);
    3225             :             }
    3226             : 
    3227             :             /*
    3228             :              * Handle special error codes
    3229             :              */
    3230             : 
    3231       15456 :             if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED
    3232        1932 :                 || ret == KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED
    3233        1340 :                 || ret == KRB5KDC_ERR_ETYPE_NOSUPP)
    3234             :             {
    3235             :                 /*
    3236             :                  * If no preauth was set and KDC requires it, give it one
    3237             :                  * more try.
    3238             :                  *
    3239             :                  * If the KDC returned KRB5KDC_ERR_ETYPE_NOSUPP, just loop
    3240             :                  * one more time since that might mean we are dealing with
    3241             :                  * a Windows KDC that is confused about what enctypes are
    3242             :                  * available.
    3243             :                  */
    3244             : 
    3245       14117 :                 if (available_padata_count(&ctx->md) == 0) {
    3246           1 :                     krb5_set_error_message(context, ret,
    3247           1 :                                            N_("Preauth required but no preauth "
    3248             :                                               "options sent by KDC", ""));
    3249           1 :                     goto out;
    3250             :                 }
    3251        1339 :             } else if (ret == KRB5KRB_AP_ERR_SKEW && context->kdc_sec_offset == 0) {
    3252             :                 /*
    3253             :                  * Try adapt to timeskrew when we are using pre-auth, and
    3254             :                  * if there was a time skew, try again.
    3255             :                  */
    3256           0 :                 krb5_set_real_time(context, ctx->error.stime, -1);
    3257           0 :                 if (context->kdc_sec_offset)
    3258           0 :                     ret = 0;
    3259             : 
    3260           0 :                 _krb5_debug(context, 10, "init_creds: err skew updating kdc offset to %d",
    3261             :                             context->kdc_sec_offset);
    3262           0 :                 if (ret)
    3263           0 :                     goto out;
    3264             : 
    3265           0 :                 pa_restart(context, ctx);
    3266             : 
    3267        1850 :             } else if (ret == KRB5_KDC_ERR_WRONG_REALM && ctx->flags.canonicalize) {
    3268             :                 /* client referral to a new realm */
    3269           0 :                 char *ref_realm;
    3270             : 
    3271         511 :                 if (ctx->error.crealm == NULL) {
    3272           0 :                     krb5_set_error_message(context, ret,
    3273           0 :                                            N_("Got a client referral, not but no realm", ""));
    3274           0 :                     goto out;
    3275             :                 }
    3276         511 :                 ref_realm = *ctx->error.crealm;
    3277             : 
    3278         511 :                 _krb5_debug(context, 5, "krb5_get_init_creds: referral to realm %s",
    3279             :                             ref_realm);
    3280             : 
    3281             :                 /*
    3282             :                  * If its a krbtgt, lets update the requested krbtgt too
    3283             :                  */
    3284         511 :                 if (krb5_principal_is_krbtgt(context, ctx->cred.server)) {
    3285             : 
    3286         511 :                     free(ctx->cred.server->name.name_string.val[1]);
    3287         511 :                     ctx->cred.server->name.name_string.val[1] = strdup(ref_realm);
    3288         511 :                     if (ctx->cred.server->name.name_string.val[1] == NULL) {
    3289           0 :                         ret = krb5_enomem(context);
    3290           0 :                         goto out;
    3291             :                     }
    3292             : 
    3293         511 :                     free_PrincipalName(ctx->as_req.req_body.sname);
    3294         511 :                     ret = _krb5_principal2principalname(ctx->as_req.req_body.sname, ctx->cred.server);
    3295         511 :                     if (ret)
    3296           0 :                         goto out;
    3297             :                 }
    3298             : 
    3299         511 :                 free(ctx->as_req.req_body.realm);
    3300         511 :                 ret = copy_Realm(&ref_realm, &ctx->as_req.req_body.realm);
    3301         511 :                 if (ret)
    3302           0 :                     goto out;
    3303             : 
    3304         511 :                 ret = krb5_principal_set_realm(context,
    3305             :                                                ctx->cred.client,
    3306         511 :                                                *ctx->error.crealm);
    3307         511 :                 if (ret)
    3308           0 :                     goto out;
    3309             : 
    3310         511 :                 ret = krb5_unparse_name(context, ctx->cred.client, &ref_realm);
    3311         511 :                 if (ret == 0) {
    3312         511 :                     _krb5_debug(context, 5, "krb5_get_init_creds: got referral to %s", ref_realm);
    3313         511 :                     krb5_xfree(ref_realm);
    3314             :                 }
    3315             : 
    3316         511 :                 pa_restart(context, ctx);
    3317             : 
    3318         828 :             } else if (ret == KRB5KDC_ERR_KEY_EXP && ctx->runflags.change_password == 0 &&
    3319           4 :                        ctx->runflags.change_password_prompt) {
    3320           0 :                 char buf2[1024];
    3321             : 
    3322           4 :                 ctx->runflags.change_password = 1;
    3323             : 
    3324           4 :                 ctx->prompter(context, ctx->prompter_data, NULL, N_("Password has expired", ""), 0, NULL);
    3325             : 
    3326             :                 /* try to avoid recursion */
    3327           4 :                 if (ctx->in_tkt_service != NULL && strcmp(ctx->in_tkt_service, "kadmin/changepw") == 0)
    3328           0 :                     goto out;
    3329             : 
    3330             :                 /* don't include prompter in runtime */
    3331           4 :                 gettimeofday(&end_time, NULL);
    3332           4 :                 timevalsub(&end_time, &start_time);
    3333           4 :                 timevaladd(&ctx->stats.run_time, &end_time);
    3334             : 
    3335           4 :                 ret = change_password(context,
    3336             :                                       ctx->cred.client,
    3337           4 :                                       ctx->password,
    3338             :                                       buf2,
    3339             :                                       sizeof(buf2),
    3340             :                                       ctx->prompter,
    3341             :                                       ctx->prompter_data,
    3342             :                                       NULL);
    3343           4 :                 if (ret)
    3344           0 :                     goto out;
    3345             : 
    3346           4 :                 gettimeofday(&start_time, NULL);
    3347             : 
    3348           4 :                 krb5_init_creds_set_password(context, ctx, buf2);
    3349             : 
    3350           4 :                 pa_restart(context, ctx);
    3351             : 
    3352         824 :             } else if (ret == KRB5KDC_ERR_PREAUTH_FAILED) {
    3353             : 
    3354             :                 /*
    3355             :                  * Old MIT KDC can't handle KRB5_PADATA_REQ_ENC_PA_REP,
    3356             :                  * so drop it and try again. But only try that for MIT
    3357             :                  * Kerberos servers by keying of no METHOD-DATA.
    3358             :                  */
    3359         344 :                 if (ctx->runflags.allow_enc_pa_rep) {
    3360         344 :                     if (ctx->md.len != 0) {
    3361         344 :                         _krb5_debug(context, 10, "Server sent PA data with KRB-ERROR, "
    3362             :                                     "so not a pre 1.7 MIT KDC and won't retry w/o ENC-PA-REQ");
    3363         344 :                         goto out;
    3364             :                     }
    3365           0 :                     _krb5_debug(context, 10, "Disabling allow_enc_pa_rep and trying again");
    3366           0 :                     ctx->runflags.allow_enc_pa_rep = 0;
    3367           0 :                     goto retry;
    3368             :                 }
    3369             : 
    3370           0 :                 if (ctx->fast_state.flags & KRB5_FAST_DISABLED) {
    3371           0 :                     _krb5_debug(context, 10, "FAST disabled and got preauth failed");
    3372           0 :                     goto out;
    3373             :                 }
    3374             : 
    3375           0 :             retry:
    3376           0 :                 pa_restart(context, ctx);
    3377             : 
    3378         480 :             } else if (ctx->fast_state.flags & KRB5_FAST_OPTIMISTIC) {
    3379           0 :                 _krb5_debug(context, 10,
    3380             :                             "Some other error %d failed with optimistic FAST, trying w/o FAST", ret);
    3381             : 
    3382           0 :                 ctx->fast_state.flags &= ~KRB5_FAST_OPTIMISTIC;
    3383           0 :                 ctx->fast_state.flags &= ~KRB5_FAST_REQUIRED;
    3384           0 :                 ctx->fast_state.flags &= ~KRB5_FAST_ANON_PKINIT_ARMOR;
    3385           0 :                 ctx->fast_state.flags |= KRB5_FAST_DISABLED;
    3386         592 :                 pa_restart(context, ctx);
    3387             :             } else {
    3388             :                 /* some other error code from the KDC, lets' return it to the user */
    3389         480 :                 goto out;
    3390             :             }
    3391             :         }
    3392             :     }
    3393             : 
    3394       30934 :     if (ctx->as_req.padata) {
    3395       14631 :         free_METHOD_DATA(ctx->as_req.padata);
    3396       14631 :         free(ctx->as_req.padata);
    3397       14631 :         ctx->as_req.padata = NULL;
    3398             :     }
    3399             : 
    3400       32118 :     ret = _krb5_fast_create_armor(context, &ctx->fast_state,
    3401       30934 :                                   ctx->cred.client->realm);
    3402       30934 :     if (ret)
    3403           0 :         goto out;
    3404             : 
    3405             :     /* Set a new nonce. */
    3406       30934 :     ctx->as_req.req_body.nonce = ctx->nonce;
    3407             : 
    3408             : 
    3409             :     /*
    3410             :      * Step and announce PA-DATA
    3411             :      */
    3412             : 
    3413       30934 :     ret = process_pa_data_to_md(context, &ctx->cred, &ctx->as_req, ctx,
    3414             :                                 &ctx->md, &ctx->as_req.padata);
    3415       30934 :     if (ret)
    3416           0 :         goto out;
    3417             : 
    3418             : 
    3419             :     /*
    3420             :      * Wrap with FAST
    3421             :      */
    3422       30934 :     ret = copy_AS_REQ(&ctx->as_req, &req2);
    3423       30934 :     if (ret)
    3424           0 :         goto out;
    3425             : 
    3426       30934 :     ret = _krb5_fast_wrap_req(context,
    3427             :                               &ctx->fast_state,
    3428             :                               &req2);
    3429             : 
    3430       30934 :     krb5_data_free(&checksum_data);
    3431       30934 :     if (ret) {
    3432           0 :         free_AS_REQ(&req2);
    3433           0 :         goto out;
    3434             :     }
    3435             : 
    3436       30934 :     krb5_data_free(&ctx->req_buffer);
    3437             : 
    3438       30934 :     ASN1_MALLOC_ENCODE(AS_REQ,
    3439             :                        ctx->req_buffer.data, ctx->req_buffer.length,
    3440             :                        &req2, &len, ret);
    3441       30934 :     free_AS_REQ(&req2);
    3442       30934 :     if (ret)
    3443           0 :         goto out;
    3444       30934 :     if(len != ctx->req_buffer.length)
    3445           0 :         krb5_abortx(context, "internal error in ASN.1 encoder");
    3446             : 
    3447       32118 :     ret = krb5_data_copy(out,
    3448       30934 :                          ctx->req_buffer.data,
    3449             :                          ctx->req_buffer.length);
    3450       30934 :     if (ret)
    3451           0 :         goto out;
    3452             : 
    3453       30934 :     *out_realm = strdup(ctx->cred.client->realm);
    3454       30934 :     if (*out_realm == NULL) {
    3455           0 :         krb5_data_free(out);
    3456           0 :         ret = ENOMEM;
    3457           0 :         goto out;
    3458             :     }
    3459             : 
    3460       30934 :     *flags = KRB5_INIT_CREDS_STEP_FLAG_CONTINUE;
    3461             : 
    3462       30934 :     gettimeofday(&end_time, NULL);
    3463       30934 :     timevalsub(&end_time, &start_time);
    3464       30934 :     timevaladd(&ctx->stats.run_time, &end_time);
    3465             : 
    3466       30934 :     return 0;
    3467         825 :  out:
    3468         825 :     return ret;
    3469             : }
    3470             : 
    3471             : /**
    3472             :  * The core loop if krb5_get_init_creds() function family. Create the
    3473             :  * packets and have the caller send them off to the KDC.
    3474             :  *
    3475             :  * If the caller want all work been done for them, use
    3476             :  * krb5_init_creds_get() instead.
    3477             :  *
    3478             :  * @param context a Kerberos 5 context.
    3479             :  * @param ctx ctx krb5_init_creds_context context.
    3480             :  * @param in input data from KDC, first round it should be reset by krb5_data_zero().
    3481             :  * @param out reply to KDC. The caller needs to call krb5_data_free()
    3482             :  * @param out_realm the destination realm for 'out', free with krb5_xfree()
    3483             :  * @param flags status of the round, if
    3484             :  *        KRB5_INIT_CREDS_STEP_FLAG_CONTINUE is set, continue one more round.
    3485             :  *
    3486             :  * @return 0 for success, or an Kerberos 5 error code, see
    3487             :  *     krb5_get_error_message().
    3488             :  *
    3489             :  * @ingroup krb5_credential
    3490             :  */
    3491             : 
    3492             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    3493       45632 : krb5_init_creds_step(krb5_context context,
    3494             :                      krb5_init_creds_context ctx,
    3495             :                      const krb5_data *in,
    3496             :                      krb5_data *out,
    3497             :                      krb5_realm *out_realm,
    3498             :                      unsigned int *flags)
    3499             : {
    3500        1776 :     krb5_error_code ret;
    3501        1776 :     krb5_data empty;
    3502             : 
    3503       45632 :     krb5_data_zero(&empty);
    3504       45632 :     krb5_data_zero(out);
    3505       45632 :     *out_realm = NULL;
    3506             : 
    3507       45632 :     if ((ctx->fast_state.flags & KRB5_FAST_ANON_PKINIT_ARMOR) &&
    3508         208 :         ctx->fast_state.armor_ccache == NULL) {
    3509         208 :         ret = _krb5_fast_anon_pkinit_step(context, ctx, &ctx->fast_state,
    3510             :                                           in, out, out_realm, flags);
    3511         208 :         if (ret && (ctx->fast_state.flags & KRB5_FAST_OPTIMISTIC)) {
    3512         104 :             _krb5_debug(context, 5, "Preauth failed with optimistic "
    3513             :                         "FAST, trying w/o FAST");
    3514         104 :             ctx->fast_state.flags &= ~KRB5_FAST_OPTIMISTIC;
    3515         104 :             ctx->fast_state.flags &= ~KRB5_FAST_REQUIRED;
    3516         104 :             ctx->fast_state.flags &= ~KRB5_FAST_ANON_PKINIT_ARMOR;
    3517         104 :         } else if (ret ||
    3518         104 :                    (*flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE))
    3519         104 :             return ret;
    3520             : 
    3521         104 :         in = &empty;
    3522             :     }
    3523             : 
    3524       45528 :     return init_creds_step(context, ctx, in, out, out_realm, flags);
    3525             : }
    3526             : 
    3527             : /**
    3528             :  * Extract the newly acquired credentials from krb5_init_creds_context
    3529             :  * context.
    3530             :  *
    3531             :  * @param context A Kerberos 5 context.
    3532             :  * @param ctx
    3533             :  * @param cred credentials, free with krb5_free_cred_contents().
    3534             :  *
    3535             :  * @return 0 for sucess or An Kerberos error code, see krb5_get_error_message().
    3536             :  */
    3537             : 
    3538             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    3539       13757 : krb5_init_creds_get_creds(krb5_context context,
    3540             :                           krb5_init_creds_context ctx,
    3541             :                           krb5_creds *cred)
    3542             : {
    3543       13757 :     return krb5_copy_creds_contents(context, &ctx->cred, cred);
    3544             : }
    3545             : 
    3546             : /**
    3547             :  * Extract the as-reply key from the context.
    3548             :  *
    3549             :  * Only allowed when the as-reply-key is not directly derived from the
    3550             :  * password like PK-INIT, GSS, FAST hardened key, etc.
    3551             :  *
    3552             :  * @param context A Kerberos 5 context.
    3553             :  * @param ctx ctx krb5_init_creds_context context.
    3554             :  * @param as_reply_key keyblock, free with krb5_free_keyblock_contents().
    3555             :  *
    3556             :  * @return 0 for sucess or An Kerberos error code, see krb5_get_error_message().
    3557             :  */
    3558             : 
    3559             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    3560           0 : krb5_init_creds_get_as_reply_key(krb5_context context,
    3561             :                                  krb5_init_creds_context ctx,
    3562             :                                  krb5_keyblock *as_reply_key)
    3563             : {
    3564           0 :     if (ctx->as_reply_key == NULL)
    3565           0 :         return KRB5KDC_ERR_PREAUTH_REQUIRED;
    3566           0 :     return krb5_copy_keyblock_contents(context, ctx->as_reply_key, as_reply_key);
    3567             : }
    3568             : 
    3569             : KRB5_LIB_FUNCTION krb5_timestamp KRB5_LIB_CALL
    3570         104 : _krb5_init_creds_get_cred_starttime(krb5_context context, krb5_init_creds_context ctx)
    3571             : {
    3572         104 :     return ctx->cred.times.starttime;
    3573             : }
    3574             : 
    3575             : KRB5_LIB_FUNCTION krb5_timestamp KRB5_LIB_CALL
    3576           0 : _krb5_init_creds_get_cred_endtime(krb5_context context, krb5_init_creds_context ctx)
    3577             : {
    3578           0 :     return ctx->cred.times.endtime;
    3579             : }
    3580             : 
    3581             : KRB5_LIB_FUNCTION krb5_principal KRB5_LIB_CALL
    3582         208 : _krb5_init_creds_get_cred_client(krb5_context context, krb5_init_creds_context ctx)
    3583             : {
    3584         208 :     return ctx->cred.client;
    3585             : }
    3586             : 
    3587             : /**
    3588             :  * Get the last error from the transaction.
    3589             :  *
    3590             :  * @return Returns 0 or an error code
    3591             :  *
    3592             :  * @ingroup krb5_credential
    3593             :  */
    3594             : 
    3595             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    3596           0 : krb5_init_creds_get_error(krb5_context context,
    3597             :                           krb5_init_creds_context ctx,
    3598             :                           KRB_ERROR *error)
    3599             : {
    3600           0 :     krb5_error_code ret;
    3601             : 
    3602           0 :     ret = copy_KRB_ERROR(&ctx->error, error);
    3603           0 :     if (ret)
    3604           0 :         krb5_enomem(context);
    3605             : 
    3606           0 :     return ret;
    3607             : }
    3608             : 
    3609             : /**
    3610             :  * Store config
    3611             :  *
    3612             :  * @param context A Kerberos 5 context.
    3613             :  * @param ctx The krb5_init_creds_context to free.
    3614             :  * @param id store
    3615             :  *
    3616             :  * @return Returns 0 or an error code
    3617             :  *
    3618             :  * @ingroup krb5_credential
    3619             :  */
    3620             : 
    3621             : krb5_error_code KRB5_LIB_FUNCTION
    3622         113 : krb5_init_creds_store_config(krb5_context context,
    3623             :                              krb5_init_creds_context ctx,
    3624             :                              krb5_ccache id)
    3625             : {
    3626           0 :     krb5_error_code ret;
    3627             : 
    3628         113 :     if (ctx->kdc_hostname) {
    3629           0 :         krb5_data data;
    3630           0 :         data.length = strlen(ctx->kdc_hostname);
    3631           0 :         data.data = ctx->kdc_hostname;
    3632             : 
    3633           0 :         ret = krb5_cc_set_config(context, id, NULL, "lkdc-hostname", &data);
    3634           0 :         if (ret)
    3635           0 :             return ret;
    3636             :     }
    3637         113 :     if (ctx->sitename) {
    3638           0 :         krb5_data data;
    3639           0 :         data.length = strlen(ctx->sitename);
    3640           0 :         data.data = ctx->sitename;
    3641             : 
    3642           0 :         ret = krb5_cc_set_config(context, id, NULL, "sitename", &data);
    3643           0 :         if (ret)
    3644           0 :             return ret;
    3645             :     }
    3646             : 
    3647         113 :     return 0;
    3648             : }
    3649             : 
    3650             : /**
    3651             :  *
    3652             :  * @ingroup krb5_credential
    3653             :  */
    3654             : 
    3655             : krb5_error_code
    3656         113 : krb5_init_creds_store(krb5_context context,
    3657             :                       krb5_init_creds_context ctx,
    3658             :                       krb5_ccache id)
    3659             : {
    3660           0 :     krb5_error_code ret;
    3661             : 
    3662         113 :     if (ctx->cred.client == NULL) {
    3663           0 :         ret = KRB5KDC_ERR_PREAUTH_REQUIRED;
    3664           0 :         krb5_set_error_message(context, ret, "init creds not completed yet");
    3665           0 :         return ret;
    3666             :     }
    3667             : 
    3668         113 :     ret = krb5_cc_initialize(context, id, ctx->cred.client);
    3669         113 :     if (ret)
    3670           0 :         return ret;
    3671             : 
    3672         113 :     ret = krb5_cc_store_cred(context, id, &ctx->cred);
    3673         113 :     if (ret)
    3674           0 :         return ret;
    3675             : 
    3676         113 :     if (ctx->cred.flags.b.enc_pa_rep) {
    3677         113 :         krb5_data data = { 3, rk_UNCONST("yes") };
    3678         113 :         ret = krb5_cc_set_config(context, id, ctx->cred.server,
    3679             :                                  "fast_avail", &data);
    3680         113 :         if (ret && ret != KRB5_CC_NOSUPP)
    3681           0 :             return ret;
    3682             :     }
    3683             : 
    3684         113 :     return 0;
    3685             : }
    3686             : 
    3687             : /**
    3688             :  * Free the krb5_init_creds_context allocated by krb5_init_creds_init().
    3689             :  *
    3690             :  * @param context A Kerberos 5 context.
    3691             :  * @param ctx The krb5_init_creds_context to free.
    3692             :  *
    3693             :  * @ingroup krb5_credential
    3694             :  */
    3695             : 
    3696             : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
    3697       16303 : krb5_init_creds_free(krb5_context context,
    3698             :                      krb5_init_creds_context ctx)
    3699             : {
    3700       16303 :     free_init_creds_ctx(context, ctx);
    3701       16303 :     free(ctx);
    3702       16303 : }
    3703             : 
    3704             : /**
    3705             :  * Get new credentials as setup by the krb5_init_creds_context.
    3706             :  *
    3707             :  * @param context A Kerberos 5 context.
    3708             :  * @param ctx The krb5_init_creds_context to process.
    3709             :  *
    3710             :  * @ingroup krb5_credential
    3711             :  */
    3712             : 
    3713             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    3714       16199 : krb5_init_creds_get(krb5_context context, krb5_init_creds_context ctx)
    3715             : {
    3716       16199 :     krb5_sendto_ctx stctx = NULL;
    3717         592 :     krb5_error_code ret;
    3718         592 :     krb5_data in, out;
    3719       16199 :     unsigned int flags = 0;
    3720             : 
    3721       16199 :     krb5_data_zero(&in);
    3722       16199 :     krb5_data_zero(&out);
    3723             : 
    3724       16199 :     ret = krb5_sendto_ctx_alloc(context, &stctx);
    3725       16199 :     if (ret)
    3726           0 :         goto out;
    3727       16199 :     krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
    3728             : 
    3729       16199 :     if (ctx->kdc_hostname)
    3730           0 :         krb5_sendto_set_hostname(context, stctx, ctx->kdc_hostname);
    3731       16199 :     if (ctx->sitename)
    3732           0 :         krb5_sendto_set_sitename(context, stctx, ctx->sitename);
    3733             : 
    3734       29225 :     while (1) {
    3735        1776 :         struct timeval nstart, nend;
    3736       45424 :         krb5_realm realm = NULL;
    3737             : 
    3738       45424 :         flags = 0;
    3739       45424 :         ret = krb5_init_creds_step(context, ctx, &in, &out, &realm, &flags);
    3740       45424 :         krb5_data_free(&in);
    3741       45424 :         if (ret)
    3742        2442 :             goto out;
    3743             : 
    3744       44691 :         if ((flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE) == 0)
    3745       13165 :             break;
    3746             : 
    3747       30934 :         gettimeofday(&nstart, NULL);
    3748             : 
    3749       30934 :         ret = krb5_sendto_context (context, stctx, &out, realm, &in);
    3750       30934 :         krb5_data_free(&out);
    3751       30934 :         free(realm);
    3752       30934 :         if (ret)
    3753        1709 :             goto out;
    3754             : 
    3755       29225 :         gettimeofday(&nend, NULL);
    3756       29225 :         timevalsub(&nend, &nstart);
    3757       29225 :         timevaladd(&ctx->stats.run_time, &nend);
    3758             :     }
    3759             : 
    3760       16199 :  out:
    3761       16199 :     if (stctx)
    3762       16199 :         krb5_sendto_ctx_free(context, stctx);
    3763             : 
    3764       16199 :     return ret;
    3765             : }
    3766             : 
    3767             : /**
    3768             :  * Get new credentials using password.
    3769             :  *
    3770             :  * @ingroup krb5_credential
    3771             :  */
    3772             : 
    3773             : 
    3774             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    3775       16061 : krb5_get_init_creds_password(krb5_context context,
    3776             :                              krb5_creds *creds,
    3777             :                              krb5_principal client,
    3778             :                              const char *password,
    3779             :                              krb5_prompter_fct prompter,
    3780             :                              void *data,
    3781             :                              krb5_deltat start_time,
    3782             :                              const char *in_tkt_service,
    3783             :                              krb5_get_init_creds_opt *options)
    3784             : {
    3785         589 :     krb5_init_creds_context ctx;
    3786         589 :     char buf[BUFSIZ], buf2[BUFSIZ];
    3787         589 :     krb5_error_code ret;
    3788       16061 :     int chpw = 0;
    3789             : 
    3790       16061 :  again:
    3791       16061 :     ret = krb5_init_creds_init(context, client, prompter, data, start_time, options, &ctx);
    3792       16061 :     if (ret)
    3793           0 :         goto out;
    3794             : 
    3795       16061 :     ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
    3796       16061 :     if (ret)
    3797           0 :         goto out;
    3798             : 
    3799       16061 :     if (prompter != NULL && ctx->password == NULL && password == NULL) {
    3800           0 :         krb5_prompt prompt;
    3801           0 :         krb5_data password_data;
    3802           6 :         char *p, *q = NULL;
    3803           0 :         int aret;
    3804             : 
    3805           6 :         ret = krb5_unparse_name(context, client, &p);
    3806           6 :         if (ret)
    3807           0 :             goto out;
    3808             : 
    3809           6 :         aret = asprintf(&q, "%s's Password: ", p);
    3810           6 :         free (p);
    3811           6 :         if (aret == -1 || q == NULL) {
    3812           0 :             ret = krb5_enomem(context);
    3813           0 :             goto out;
    3814             :         }
    3815           6 :         prompt.prompt = q;
    3816           6 :         password_data.data   = buf;
    3817           6 :         password_data.length = sizeof(buf);
    3818           6 :         prompt.hidden = 1;
    3819           6 :         prompt.reply  = &password_data;
    3820           6 :         prompt.type   = KRB5_PROMPT_TYPE_PASSWORD;
    3821             : 
    3822           6 :         ret = (*prompter) (context, data, NULL, NULL, 1, &prompt);
    3823           6 :         free (q);
    3824           6 :         if (ret) {
    3825           0 :             memset_s(buf, sizeof(buf), 0, sizeof(buf));
    3826           0 :             ret = KRB5_LIBOS_PWDINTR;
    3827           0 :             krb5_clear_error_message (context);
    3828           0 :             goto out;
    3829             :         }
    3830           6 :         password = password_data.data;
    3831             :     }
    3832             : 
    3833       16061 :     if (password) {
    3834       16061 :         ret = krb5_init_creds_set_password(context, ctx, password);
    3835       16061 :         if (ret)
    3836           0 :             goto out;
    3837             :     }
    3838             : 
    3839       16061 :     ret = krb5_init_creds_get(context, ctx);
    3840             : 
    3841       16061 :     if (ret == 0)
    3842       13631 :         krb5_process_last_request(context, options, ctx);
    3843             : 
    3844             : 
    3845       16061 :     if (ret == KRB5KDC_ERR_KEY_EXPIRED && chpw == 0) {
    3846             :         /* try to avoid recursion */
    3847           0 :         if (in_tkt_service != NULL && strcmp(in_tkt_service, "kadmin/changepw") == 0)
    3848           0 :            goto out;
    3849             : 
    3850             :         /* don't try to change password if no prompter or prompting disabled */
    3851           0 :         if (!ctx->runflags.change_password_prompt)
    3852           0 :             goto out;
    3853             : 
    3854           0 :         ret = change_password (context,
    3855             :                                client,
    3856           0 :                                ctx->password,
    3857             :                                buf2,
    3858             :                                sizeof(buf2),
    3859             :                                prompter,
    3860             :                                data,
    3861             :                                options);
    3862           0 :         if (ret)
    3863           0 :             goto out;
    3864           0 :         password = buf2;
    3865           0 :         chpw = 1;
    3866           0 :         krb5_init_creds_free(context, ctx);
    3867           0 :         goto again;
    3868             :     }
    3869             : 
    3870       16061 :  out:
    3871       16061 :     if (ret == 0)
    3872       13631 :         krb5_init_creds_get_creds(context, ctx, creds);
    3873             : 
    3874       16061 :     if (ctx)
    3875       16061 :         krb5_init_creds_free(context, ctx);
    3876             : 
    3877       16061 :     memset_s(buf, sizeof(buf), 0, sizeof(buf));
    3878       16061 :     memset_s(buf2, sizeof(buf), 0, sizeof(buf2));
    3879       16061 :     return ret;
    3880             : }
    3881             : 
    3882             : /**
    3883             :  * Get new credentials using keyblock.
    3884             :  *
    3885             :  * @ingroup krb5_credential
    3886             :  */
    3887             : 
    3888             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    3889          14 : krb5_get_init_creds_keyblock(krb5_context context,
    3890             :                              krb5_creds *creds,
    3891             :                              krb5_principal client,
    3892             :                              krb5_keyblock *keyblock,
    3893             :                              krb5_deltat start_time,
    3894             :                              const char *in_tkt_service,
    3895             :                              krb5_get_init_creds_opt *options)
    3896             : {
    3897           3 :     krb5_init_creds_context ctx;
    3898           3 :     krb5_error_code ret;
    3899             : 
    3900          14 :     memset(creds, 0, sizeof(*creds));
    3901             : 
    3902          14 :     ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx);
    3903          14 :     if (ret)
    3904           0 :         goto out;
    3905             : 
    3906          14 :     ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
    3907          14 :     if (ret)
    3908           0 :         goto out;
    3909             : 
    3910          14 :     ret = krb5_init_creds_set_keyblock(context, ctx, keyblock);
    3911          14 :     if (ret)
    3912           0 :         goto out;
    3913             : 
    3914          14 :     ret = krb5_init_creds_get(context, ctx);
    3915             : 
    3916          14 :     if (ret == 0)
    3917          13 :         krb5_process_last_request(context, options, ctx);
    3918             : 
    3919           1 :  out:
    3920          14 :     if (ret == 0)
    3921          13 :         krb5_init_creds_get_creds(context, ctx, creds);
    3922             : 
    3923          14 :     if (ctx)
    3924          14 :         krb5_init_creds_free(context, ctx);
    3925             : 
    3926          14 :     return ret;
    3927             : }
    3928             : 
    3929             : /**
    3930             :  * Get new credentials using keytab.
    3931             :  *
    3932             :  * @ingroup krb5_credential
    3933             :  */
    3934             : 
    3935             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    3936           0 : krb5_get_init_creds_keytab(krb5_context context,
    3937             :                            krb5_creds *creds,
    3938             :                            krb5_principal client,
    3939             :                            krb5_keytab keytab,
    3940             :                            krb5_deltat start_time,
    3941             :                            const char *in_tkt_service,
    3942             :                            krb5_get_init_creds_opt *options)
    3943             : {
    3944           0 :     krb5_init_creds_context ctx;
    3945           0 :     krb5_keytab_entry ktent;
    3946           0 :     krb5_error_code ret;
    3947             : 
    3948           0 :     memset(&ktent, 0, sizeof(ktent));
    3949           0 :     memset(creds, 0, sizeof(*creds));
    3950             : 
    3951           0 :     if (strcmp(client->realm, "") == 0) {
    3952             :         /*
    3953             :          * Referral realm.  We have a keytab, so pick a realm by
    3954             :          * matching in the keytab.
    3955             :          */
    3956           0 :         ret = krb5_kt_get_entry(context, keytab, client, 0, 0, &ktent);
    3957           0 :         if (ret == 0)
    3958           0 :             client = ktent.principal;
    3959             :     }
    3960             : 
    3961           0 :     ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx);
    3962           0 :     if (ret)
    3963           0 :         goto out;
    3964             : 
    3965           0 :     ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
    3966           0 :     if (ret)
    3967           0 :         goto out;
    3968             : 
    3969           0 :     ret = krb5_init_creds_set_keytab(context, ctx, keytab);
    3970           0 :     if (ret)
    3971           0 :         goto out;
    3972             : 
    3973           0 :     ret = krb5_init_creds_get(context, ctx);
    3974           0 :     if (ret == 0)
    3975           0 :         krb5_process_last_request(context, options, ctx);
    3976             : 
    3977           0 :  out:
    3978           0 :     krb5_kt_free_entry(context, &ktent);
    3979           0 :     if (ret == 0)
    3980           0 :         krb5_init_creds_get_creds(context, ctx, creds);
    3981             : 
    3982           0 :     if (ctx)
    3983           0 :         krb5_init_creds_free(context, ctx);
    3984             : 
    3985           0 :     return ret;
    3986             : }
    3987             : 
    3988             : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
    3989           0 : _krb5_init_creds_set_gss_mechanism(krb5_context context,
    3990             :                                    krb5_gss_init_ctx gssic,
    3991             :                                    const struct gss_OID_desc_struct *gss_mech)
    3992             : {
    3993           0 :     gssic->mech = gss_mech; /* OIDs are interned, so no copy required */
    3994           0 : }
    3995             : 
    3996             : KRB5_LIB_FUNCTION const struct gss_OID_desc_struct * KRB5_LIB_CALL
    3997           0 : _krb5_init_creds_get_gss_mechanism(krb5_context context,
    3998             :                                    krb5_gss_init_ctx gssic)
    3999             : {
    4000           0 :     return gssic->mech;
    4001             : }
    4002             : 
    4003             : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
    4004           0 : _krb5_init_creds_set_gss_cred(krb5_context context,
    4005             :                               krb5_gss_init_ctx gssic,
    4006             :                               struct gss_cred_id_t_desc_struct *gss_cred)
    4007             : {
    4008           0 :     if (gssic->cred != gss_cred && gssic->flags.release_cred)
    4009           0 :         gssic->release_cred(context, gssic, gssic->cred);
    4010             : 
    4011           0 :     gssic->cred = gss_cred;
    4012           0 :     gssic->flags.release_cred = 1;
    4013           0 : }
    4014             : 
    4015             : KRB5_LIB_FUNCTION const struct gss_cred_id_t_desc_struct * KRB5_LIB_CALL
    4016           0 : _krb5_init_creds_get_gss_cred(krb5_context context,
    4017             :                               krb5_gss_init_ctx gssic)
    4018             : {
    4019           0 :     return gssic->cred;
    4020             : }
    4021             : 
    4022             : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    4023           0 : _krb5_init_creds_init_gss(krb5_context context,
    4024             :                           krb5_init_creds_context ctx,
    4025             :                           krb5_gssic_step step,
    4026             :                           krb5_gssic_finish finish,
    4027             :                           krb5_gssic_release_cred release_cred,
    4028             :                           krb5_gssic_delete_sec_context delete_sec_context,
    4029             :                           const struct gss_cred_id_t_desc_struct *gss_cred,
    4030             :                           const struct gss_OID_desc_struct *gss_mech,
    4031             :                           unsigned int flags)
    4032             : {
    4033           0 :     krb5_gss_init_ctx gssic;
    4034             : 
    4035           0 :     gssic = calloc(1, sizeof(*gssic));
    4036           0 :     if (gssic == NULL)
    4037           0 :         return krb5_enomem(context);
    4038             : 
    4039           0 :     if (ctx->gss_init_ctx)
    4040           0 :         free_gss_init_ctx(context, ctx->gss_init_ctx);
    4041           0 :     ctx->gss_init_ctx = gssic;
    4042             : 
    4043           0 :     gssic->cred = (struct gss_cred_id_t_desc_struct *)gss_cred;
    4044           0 :     gssic->mech = gss_mech;
    4045           0 :     if (flags & KRB5_GSS_IC_FLAG_RELEASE_CRED)
    4046           0 :         gssic->flags.release_cred = 1;
    4047             : 
    4048           0 :     gssic->step = step;
    4049           0 :     gssic->finish = finish;
    4050           0 :     gssic->release_cred = release_cred;
    4051           0 :     gssic->delete_sec_context = delete_sec_context;
    4052             : 
    4053           0 :     return 0;
    4054             : }

Generated by: LCOV version 1.14