Home | History | Annotate | Line # | Download | only in krb5
      1 /*	$NetBSD: init_creds_pw.c,v 1.4 2023/06/19 21:41:44 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1997 - 2008 Kungliga Tekniska Hgskolan
      5  * (Royal Institute of Technology, Stockholm, Sweden).
      6  * All rights reserved.
      7  *
      8  * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  *
     14  * 1. Redistributions of source code must retain the above copyright
     15  *    notice, this list of conditions and the following disclaimer.
     16  *
     17  * 2. Redistributions in binary form must reproduce the above copyright
     18  *    notice, this list of conditions and the following disclaimer in the
     19  *    documentation and/or other materials provided with the distribution.
     20  *
     21  * 3. Neither the name of the Institute nor the names of its contributors
     22  *    may be used to endorse or promote products derived from this software
     23  *    without specific prior written permission.
     24  *
     25  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
     26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
     29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     35  * SUCH DAMAGE.
     36  */
     37 
     38 #include "krb5_locl.h"
     39 #ifndef WIN32
     40 #include <heim-ipc.h>
     41 #endif /* WIN32 */
     42 
     43 typedef struct krb5_get_init_creds_ctx {
     44     KDCOptions flags;
     45     krb5_creds cred;
     46     krb5_addresses *addrs;
     47     krb5_enctype *etypes;
     48     krb5_preauthtype *pre_auth_types;
     49     char *in_tkt_service;
     50     unsigned nonce;
     51     unsigned pk_nonce;
     52 
     53     krb5_data req_buffer;
     54     AS_REQ as_req;
     55     int pa_counter;
     56 
     57     /* password and keytab_data is freed on completion */
     58     char *password;
     59     krb5_keytab_key_proc_args *keytab_data;
     60 
     61     krb5_pointer *keyseed;
     62     krb5_s2k_proc keyproc;
     63 
     64     krb5_get_init_creds_tristate req_pac;
     65 
     66     krb5_pk_init_ctx pk_init_ctx;
     67     int ic_flags;
     68 
     69     struct {
     70 	unsigned change_password:1;
     71     } runflags;
     72 
     73     int used_pa_types;
     74 #define  USED_PKINIT	1
     75 #define  USED_PKINIT_W2K	2
     76 #define  USED_ENC_TS_GUESS	4
     77 #define  USED_ENC_TS_INFO	8
     78 
     79     METHOD_DATA md;
     80     KRB_ERROR error;
     81     AS_REP as_rep;
     82     EncKDCRepPart enc_part;
     83 
     84     krb5_prompter_fct prompter;
     85     void *prompter_data;
     86 
     87     struct pa_info_data *ppaid;
     88     struct fast_state {
     89 	enum PA_FX_FAST_REQUEST_enum type;
     90 	unsigned int flags;
     91 #define KRB5_FAST_REPLY_KEY_USE_TO_ENCRYPT_THE_REPLY 1
     92 #define KRB5_FAST_REPLY_KEY_USE_IN_TRANSACTION 2
     93 #define KRB5_FAST_KDC_REPLY_KEY_REPLACED 4
     94 #define KRB5_FAST_REPLY_REPLY_VERIFED 8
     95 #define KRB5_FAST_STRONG 16
     96 #define KRB5_FAST_EXPECTED 32 /* in exchange with KDC, fast was discovered */
     97 #define KRB5_FAST_REQUIRED 64 /* fast required by action of caller */
     98 #define KRB5_FAST_DISABLED 128
     99 #define KRB5_FAST_AP_ARMOR_SERVICE 256
    100 	krb5_keyblock *reply_key;
    101 	krb5_ccache armor_ccache;
    102 	krb5_principal armor_service;
    103 	krb5_crypto armor_crypto;
    104 	krb5_keyblock armor_key;
    105 	krb5_keyblock *strengthen_key;
    106     } fast_state;
    107 } krb5_get_init_creds_ctx;
    108 
    109 
    110 struct pa_info_data {
    111     krb5_enctype etype;
    112     krb5_salt salt;
    113     krb5_data *s2kparams;
    114 };
    115 
    116 static void
    117 free_paid(krb5_context context, struct pa_info_data *ppaid)
    118 {
    119     krb5_free_salt(context, ppaid->salt);
    120     if (ppaid->s2kparams)
    121 	krb5_free_data(context, ppaid->s2kparams);
    122 }
    123 
    124 static krb5_error_code KRB5_CALLCONV
    125 default_s2k_func(krb5_context context, krb5_enctype type,
    126 		 krb5_const_pointer keyseed,
    127 		 krb5_salt salt, krb5_data *s2kparms,
    128 		 krb5_keyblock **key)
    129 {
    130     krb5_error_code ret;
    131     krb5_data password;
    132     krb5_data opaque;
    133 
    134     _krb5_debug(context, 5, "krb5_get_init_creds: using default_s2k_func");
    135 
    136     password.data = rk_UNCONST(keyseed);
    137     password.length = strlen(keyseed);
    138     if (s2kparms)
    139 	opaque = *s2kparms;
    140     else
    141 	krb5_data_zero(&opaque);
    142 
    143     *key = malloc(sizeof(**key));
    144     if (*key == NULL)
    145 	return ENOMEM;
    146     ret = krb5_string_to_key_data_salt_opaque(context, type, password,
    147 					      salt, opaque, *key);
    148     if (ret) {
    149 	free(*key);
    150 	*key = NULL;
    151     }
    152     return ret;
    153 }
    154 
    155 static void
    156 free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx)
    157 {
    158     if (ctx->etypes)
    159 	free(ctx->etypes);
    160     if (ctx->pre_auth_types)
    161 	free (ctx->pre_auth_types);
    162     if (ctx->in_tkt_service)
    163 	free(ctx->in_tkt_service);
    164     if (ctx->keytab_data)
    165 	free(ctx->keytab_data);
    166     if (ctx->password) {
    167 	size_t len;
    168 	len = strlen(ctx->password);
    169 	memset_s(ctx->password, len, 0, len);
    170 	free(ctx->password);
    171     }
    172     /*
    173      * FAST state (we don't close the armor_ccache because we might have
    174      * to destroy it, and how would we know? also, the caller should
    175      * take care of cleaning up the armor_ccache).
    176      */
    177     if (ctx->fast_state.armor_service)
    178 	krb5_free_principal(context, ctx->fast_state.armor_service);
    179     if (ctx->fast_state.armor_crypto)
    180 	krb5_crypto_destroy(context, ctx->fast_state.armor_crypto);
    181     if (ctx->fast_state.strengthen_key)
    182 	krb5_free_keyblock(context, ctx->fast_state.strengthen_key);
    183     krb5_free_keyblock_contents(context, &ctx->fast_state.armor_key);
    184 
    185     krb5_data_free(&ctx->req_buffer);
    186     krb5_free_cred_contents(context, &ctx->cred);
    187     free_METHOD_DATA(&ctx->md);
    188     free_AS_REP(&ctx->as_rep);
    189     free_EncKDCRepPart(&ctx->enc_part);
    190     free_KRB_ERROR(&ctx->error);
    191     free_AS_REQ(&ctx->as_req);
    192     if (ctx->ppaid) {
    193 	free_paid(context, ctx->ppaid);
    194 	free(ctx->ppaid);
    195     }
    196     memset_s(ctx, sizeof(*ctx), 0, sizeof(*ctx));
    197 }
    198 
    199 static int
    200 get_config_time (krb5_context context,
    201 		 const char *realm,
    202 		 const char *name,
    203 		 int def)
    204 {
    205     int ret;
    206 
    207     ret = krb5_config_get_time (context, NULL,
    208 				"realms",
    209 				realm,
    210 				name,
    211 				NULL);
    212     if (ret >= 0)
    213 	return ret;
    214     ret = krb5_config_get_time (context, NULL,
    215 				"libdefaults",
    216 				name,
    217 				NULL);
    218     if (ret >= 0)
    219 	return ret;
    220     return def;
    221 }
    222 
    223 static krb5_error_code
    224 init_cred (krb5_context context,
    225 	   krb5_creds *cred,
    226 	   krb5_principal client,
    227 	   krb5_deltat start_time,
    228 	   krb5_get_init_creds_opt *options)
    229 {
    230     krb5_error_code ret;
    231     int tmp;
    232     krb5_timestamp now;
    233 
    234     krb5_timeofday (context, &now);
    235 
    236     memset (cred, 0, sizeof(*cred));
    237 
    238     if (client)
    239 	ret = krb5_copy_principal(context, client, &cred->client);
    240     else
    241 	ret = krb5_get_default_principal(context, &cred->client);
    242     if (ret)
    243         goto out;
    244 
    245     if (start_time)
    246 	cred->times.starttime  = now + start_time;
    247 
    248     if (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)
    249 	tmp = options->tkt_life;
    250     else
    251 	tmp = KRB5_TKT_LIFETIME_DEFAULT;
    252     cred->times.endtime = now + tmp;
    253 
    254     if ((options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE)) {
    255 	if (options->renew_life > 0)
    256 	    tmp = options->renew_life;
    257 	else
    258 	    tmp = KRB5_TKT_RENEW_LIFETIME_DEFAULT;
    259 	cred->times.renew_till = now + tmp;
    260     }
    261 
    262     return 0;
    263 
    264 out:
    265     krb5_free_cred_contents (context, cred);
    266     return ret;
    267 }
    268 
    269 /*
    270  * Print a message (str) to the user about the expiration in `lr'
    271  */
    272 
    273 static void
    274 report_expiration (krb5_context context,
    275 		   krb5_prompter_fct prompter,
    276 		   krb5_data *data,
    277 		   const char *str,
    278 		   time_t now)
    279 {
    280     char *p = NULL;
    281 
    282     if (asprintf(&p, "%s%s", str, ctime(&now)) < 0 || p == NULL)
    283 	return;
    284     (*prompter)(context, data, NULL, p, 0, NULL);
    285     free(p);
    286 }
    287 
    288 /*
    289  * Check the context, and in the case there is a expiration warning,
    290  * use the prompter to print the warning.
    291  *
    292  * @param context A Kerberos 5 context.
    293  * @param options An GIC options structure
    294  * @param ctx The krb5_init_creds_context check for expiration.
    295  */
    296 
    297 krb5_error_code
    298 krb5_process_last_request(krb5_context context,
    299 			  krb5_get_init_creds_opt *options,
    300 			  krb5_init_creds_context ctx)
    301 {
    302     krb5_const_realm realm;
    303     LastReq *lr;
    304     krb5_boolean reported = FALSE;
    305     krb5_timestamp sec;
    306     time_t t;
    307     size_t i;
    308 
    309     /*
    310      * First check if there is a API consumer.
    311      */
    312 
    313     realm = krb5_principal_get_realm (context, ctx->cred.client);
    314     lr = &ctx->enc_part.last_req;
    315 
    316     if (options && options->opt_private && options->opt_private->lr.func) {
    317 	krb5_last_req_entry **lre;
    318 
    319 	lre = calloc(lr->len + 1, sizeof(*lre));
    320 	if (lre == NULL)
    321 	    return krb5_enomem(context);
    322 	for (i = 0; i < lr->len; i++) {
    323 	    lre[i] = calloc(1, sizeof(*lre[i]));
    324 	    if (lre[i] == NULL)
    325 		break;
    326 	    lre[i]->lr_type = lr->val[i].lr_type;
    327 	    lre[i]->value = lr->val[i].lr_value;
    328 	}
    329 
    330 	(*options->opt_private->lr.func)(context, lre,
    331 					 options->opt_private->lr.ctx);
    332 
    333 	for (i = 0; i < lr->len; i++)
    334 	    free(lre[i]);
    335 	free(lre);
    336     }
    337 
    338     /*
    339      * Now check if we should prompt the user
    340      */
    341 
    342     if (ctx->prompter == NULL)
    343         return 0;
    344 
    345     krb5_timeofday (context, &sec);
    346 
    347     t = sec + get_config_time (context,
    348 			       realm,
    349 			       "warn_pwexpire",
    350 			       7 * 24 * 60 * 60);
    351 
    352     for (i = 0; i < lr->len; ++i) {
    353 	if (lr->val[i].lr_value <= t) {
    354 	    switch (lr->val[i].lr_type) {
    355 	    case LR_PW_EXPTIME :
    356 		report_expiration(context, ctx->prompter,
    357 				  ctx->prompter_data,
    358 				  "Your password will expire at ",
    359 				  lr->val[i].lr_value);
    360 		reported = TRUE;
    361 		break;
    362 	    case LR_ACCT_EXPTIME :
    363 		report_expiration(context, ctx->prompter,
    364 				  ctx->prompter_data,
    365 				  "Your account will expire at ",
    366 				  lr->val[i].lr_value);
    367 		reported = TRUE;
    368 		break;
    369             default:
    370                 break;
    371 	    }
    372 	}
    373     }
    374 
    375     if (!reported
    376 	&& ctx->enc_part.key_expiration
    377 	&& *ctx->enc_part.key_expiration <= t) {
    378         report_expiration(context, ctx->prompter,
    379 			  ctx->prompter_data,
    380 			  "Your password/account will expire at ",
    381 			  *ctx->enc_part.key_expiration);
    382     }
    383     return 0;
    384 }
    385 
    386 static krb5_addresses no_addrs = { 0, NULL };
    387 
    388 static krb5_error_code
    389 get_init_creds_common(krb5_context context,
    390 		      krb5_principal client,
    391 		      krb5_deltat start_time,
    392 		      krb5_get_init_creds_opt *options,
    393 		      krb5_init_creds_context ctx)
    394 {
    395     krb5_get_init_creds_opt *default_opt = NULL;
    396     krb5_error_code ret;
    397     krb5_enctype *etypes;
    398     krb5_preauthtype *pre_auth_types;
    399 
    400     memset(ctx, 0, sizeof(*ctx));
    401 
    402     if (options == NULL) {
    403 	const char *realm = krb5_principal_get_realm(context, client);
    404 
    405         krb5_get_init_creds_opt_alloc (context, &default_opt);
    406 	options = default_opt;
    407 	krb5_get_init_creds_opt_set_default_flags(context, NULL, realm, options);
    408     }
    409 
    410     if (options->opt_private) {
    411 	if (options->opt_private->password) {
    412 	    ret = krb5_init_creds_set_password(context, ctx,
    413 					       options->opt_private->password);
    414 	    if (ret)
    415 		goto out;
    416 	}
    417 
    418 	ctx->keyproc = options->opt_private->key_proc;
    419 	ctx->req_pac = options->opt_private->req_pac;
    420 	ctx->pk_init_ctx = options->opt_private->pk_init_ctx;
    421 	ctx->ic_flags = options->opt_private->flags;
    422     } else
    423 	ctx->req_pac = KRB5_INIT_CREDS_TRISTATE_UNSET;
    424 
    425     if (ctx->keyproc == NULL)
    426 	ctx->keyproc = default_s2k_func;
    427 
    428     /* Enterprise name implicitly turns on canonicalize */
    429     if ((ctx->ic_flags & KRB5_INIT_CREDS_CANONICALIZE) ||
    430 	krb5_principal_get_type(context, client) == KRB5_NT_ENTERPRISE_PRINCIPAL)
    431 	ctx->flags.canonicalize = 1;
    432 
    433     ctx->pre_auth_types = NULL;
    434     ctx->addrs = NULL;
    435     ctx->etypes = NULL;
    436     ctx->pre_auth_types = NULL;
    437 
    438     ret = init_cred(context, &ctx->cred, client, start_time, options);
    439     if (ret) {
    440 	if (default_opt)
    441 	    krb5_get_init_creds_opt_free(context, default_opt);
    442 	return ret;
    443     }
    444 
    445     ret = krb5_init_creds_set_service(context, ctx, NULL);
    446     if (ret)
    447 	goto out;
    448 
    449     if (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)
    450 	ctx->flags.forwardable = options->forwardable;
    451 
    452     if (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)
    453 	ctx->flags.proxiable = options->proxiable;
    454 
    455     if (start_time)
    456 	ctx->flags.postdated = 1;
    457     if (ctx->cred.times.renew_till)
    458 	ctx->flags.renewable = 1;
    459     if (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) {
    460 	ctx->addrs = options->address_list;
    461     } else if (options->opt_private) {
    462 	switch (options->opt_private->addressless) {
    463 	case KRB5_INIT_CREDS_TRISTATE_UNSET:
    464 #if KRB5_ADDRESSLESS_DEFAULT == TRUE
    465 	    ctx->addrs = &no_addrs;
    466 #else
    467 	    ctx->addrs = NULL;
    468 #endif
    469 	    break;
    470 	case KRB5_INIT_CREDS_TRISTATE_FALSE:
    471 	    ctx->addrs = NULL;
    472 	    break;
    473 	case KRB5_INIT_CREDS_TRISTATE_TRUE:
    474 	    ctx->addrs = &no_addrs;
    475 	    break;
    476 	}
    477     }
    478     if (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) {
    479 	if (ctx->etypes)
    480 	    free(ctx->etypes);
    481 
    482 	etypes = malloc((options->etype_list_length + 1)
    483 			* sizeof(krb5_enctype));
    484 	if (etypes == NULL) {
    485 	    ret = krb5_enomem(context);
    486 	    goto out;
    487 	}
    488 	memcpy (etypes, options->etype_list,
    489 		options->etype_list_length * sizeof(krb5_enctype));
    490 	etypes[options->etype_list_length] = ETYPE_NULL;
    491 	ctx->etypes = etypes;
    492     }
    493     if (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) {
    494 	pre_auth_types = malloc((options->preauth_list_length + 1)
    495 				* sizeof(krb5_preauthtype));
    496 	if (pre_auth_types == NULL) {
    497 	    ret = krb5_enomem(context);
    498 	    goto out;
    499 	}
    500 	memcpy (pre_auth_types, options->preauth_list,
    501 		options->preauth_list_length * sizeof(krb5_preauthtype));
    502 	pre_auth_types[options->preauth_list_length] = KRB5_PADATA_NONE;
    503 	ctx->pre_auth_types = pre_auth_types;
    504     }
    505     if (options->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS)
    506 	ctx->flags.request_anonymous = options->anonymous;
    507     if (default_opt)
    508         krb5_get_init_creds_opt_free(context, default_opt);
    509     return 0;
    510  out:
    511     if (default_opt)
    512 	krb5_get_init_creds_opt_free(context, default_opt);
    513     return ret;
    514 }
    515 
    516 static krb5_error_code
    517 change_password (krb5_context context,
    518 		 krb5_principal client,
    519 		 const char *password,
    520 		 char *newpw,
    521 		 size_t newpw_sz,
    522 		 krb5_prompter_fct prompter,
    523 		 void *data,
    524 		 krb5_get_init_creds_opt *old_options)
    525 {
    526     krb5_prompt prompts[2];
    527     krb5_error_code ret;
    528     krb5_creds cpw_cred;
    529     char buf1[BUFSIZ], buf2[BUFSIZ];
    530     krb5_data password_data[2];
    531     int result_code;
    532     krb5_data result_code_string;
    533     krb5_data result_string;
    534     char *p;
    535     krb5_get_init_creds_opt *options;
    536 
    537     heim_assert(prompter != NULL, "unexpected NULL prompter");
    538 
    539     memset (&cpw_cred, 0, sizeof(cpw_cred));
    540 
    541     ret = krb5_get_init_creds_opt_alloc(context, &options);
    542     if (ret)
    543         return ret;
    544     krb5_get_init_creds_opt_set_tkt_life (options, 60);
    545     krb5_get_init_creds_opt_set_forwardable (options, FALSE);
    546     krb5_get_init_creds_opt_set_proxiable (options, FALSE);
    547     if (old_options &&
    548         (old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST))
    549 	krb5_get_init_creds_opt_set_preauth_list(options,
    550 						 old_options->preauth_list,
    551 						 old_options->preauth_list_length);
    552     if (old_options &&
    553         (old_options->flags & KRB5_GET_INIT_CREDS_OPT_CHANGE_PASSWORD_PROMPT))
    554         krb5_get_init_creds_opt_set_change_password_prompt(options,
    555                                                            old_options->change_password_prompt);
    556 
    557     krb5_data_zero (&result_code_string);
    558     krb5_data_zero (&result_string);
    559 
    560     ret = krb5_get_init_creds_password (context,
    561 					&cpw_cred,
    562 					client,
    563 					password,
    564 					prompter,
    565 					data,
    566 					0,
    567 					"kadmin/changepw",
    568 					options);
    569     krb5_get_init_creds_opt_free(context, options);
    570     if (ret)
    571 	goto out;
    572 
    573     for(;;) {
    574 	password_data[0].data   = buf1;
    575 	password_data[0].length = sizeof(buf1);
    576 
    577 	prompts[0].hidden = 1;
    578 	prompts[0].prompt = "New password: ";
    579 	prompts[0].reply  = &password_data[0];
    580 	prompts[0].type   = KRB5_PROMPT_TYPE_NEW_PASSWORD;
    581 
    582 	password_data[1].data   = buf2;
    583 	password_data[1].length = sizeof(buf2);
    584 
    585 	prompts[1].hidden = 1;
    586 	prompts[1].prompt = "Repeat new password: ";
    587 	prompts[1].reply  = &password_data[1];
    588 	prompts[1].type   = KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN;
    589 
    590 	ret = (*prompter) (context, data, NULL, "Changing password",
    591 			   2, prompts);
    592 	if (ret) {
    593 	    memset (buf1, 0, sizeof(buf1));
    594 	    memset (buf2, 0, sizeof(buf2));
    595 	    goto out;
    596 	}
    597 
    598 	if (strcmp (buf1, buf2) == 0)
    599 	    break;
    600 	memset (buf1, 0, sizeof(buf1));
    601 	memset (buf2, 0, sizeof(buf2));
    602     }
    603 
    604     ret = krb5_set_password (context,
    605 			     &cpw_cred,
    606 			     buf1,
    607 			     client,
    608 			     &result_code,
    609 			     &result_code_string,
    610 			     &result_string);
    611     if (ret)
    612 	goto out;
    613     if (asprintf(&p, "%s: %.*s\n",
    614 		 result_code ? "Error" : "Success",
    615 		 (int)result_string.length,
    616 		 result_string.length > 0 ? (char*)result_string.data : "") < 0)
    617     {
    618 	ret = ENOMEM;
    619 	goto out;
    620     }
    621 
    622     /* return the result */
    623     (*prompter) (context, data, NULL, p, 0, NULL);
    624 
    625     free (p);
    626     if (result_code == 0) {
    627 	strlcpy (newpw, buf1, newpw_sz);
    628 	ret = 0;
    629     } else {
    630 	ret = ENOTTY;
    631 	krb5_set_error_message(context, ret,
    632 			       N_("failed changing password", ""));
    633     }
    634 
    635 out:
    636     memset_s(buf1, sizeof(buf1), 0, sizeof(buf1));
    637     memset_s(buf2, sizeof(buf2), 0, sizeof(buf2));
    638     krb5_data_free (&result_string);
    639     krb5_data_free (&result_code_string);
    640     krb5_free_cred_contents (context, &cpw_cred);
    641     return ret;
    642 }
    643 
    644 
    645 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
    646 krb5_keyblock_key_proc (krb5_context context,
    647 			krb5_keytype type,
    648 			krb5_data *salt,
    649 			krb5_const_pointer keyseed,
    650 			krb5_keyblock **key)
    651 {
    652     return krb5_copy_keyblock (context, keyseed, key);
    653 }
    654 
    655 /*
    656  *
    657  */
    658 
    659 static krb5_error_code
    660 init_as_req (krb5_context context,
    661 	     KDCOptions opts,
    662 	     const krb5_creds *creds,
    663 	     const krb5_addresses *addrs,
    664 	     const krb5_enctype *etypes,
    665 	     AS_REQ *a)
    666 {
    667     krb5_error_code ret;
    668 
    669     memset(a, 0, sizeof(*a));
    670 
    671     a->pvno = 5;
    672     a->msg_type = krb_as_req;
    673     a->req_body.kdc_options = opts;
    674     a->req_body.cname = malloc(sizeof(*a->req_body.cname));
    675     if (a->req_body.cname == NULL) {
    676 	ret = krb5_enomem(context);
    677 	goto fail;
    678     }
    679     a->req_body.sname = malloc(sizeof(*a->req_body.sname));
    680     if (a->req_body.sname == NULL) {
    681 	ret = krb5_enomem(context);
    682 	goto fail;
    683     }
    684 
    685     ret = _krb5_principal2principalname (a->req_body.cname, creds->client);
    686     if (ret)
    687 	goto fail;
    688     ret = copy_Realm(&creds->client->realm, &a->req_body.realm);
    689     if (ret)
    690 	goto fail;
    691 
    692     ret = _krb5_principal2principalname (a->req_body.sname, creds->server);
    693     if (ret)
    694 	goto fail;
    695 
    696     if(creds->times.starttime) {
    697 	a->req_body.from = malloc(sizeof(*a->req_body.from));
    698 	if (a->req_body.from == NULL) {
    699 	    ret = krb5_enomem(context);
    700 	    goto fail;
    701 	}
    702 	*a->req_body.from = creds->times.starttime;
    703     }
    704     if(creds->times.endtime){
    705 	if ((ALLOC(a->req_body.till, 1)) != NULL)
    706             *a->req_body.till = creds->times.endtime;
    707         else {
    708             ret = krb5_enomem(context);
    709             goto fail;
    710         }
    711     }
    712     if(creds->times.renew_till){
    713 	a->req_body.rtime = malloc(sizeof(*a->req_body.rtime));
    714 	if (a->req_body.rtime == NULL) {
    715 	    ret = krb5_enomem(context);
    716 	    goto fail;
    717 	}
    718 	*a->req_body.rtime = creds->times.renew_till;
    719     }
    720     a->req_body.nonce = 0;
    721     ret = _krb5_init_etype(context,
    722 			   KRB5_PDU_AS_REQUEST,
    723 			   &a->req_body.etype.len,
    724 			   &a->req_body.etype.val,
    725 			   etypes);
    726     if (ret)
    727 	goto fail;
    728 
    729     /*
    730      * This means no addresses
    731      */
    732 
    733     if (addrs && addrs->len == 0) {
    734 	a->req_body.addresses = NULL;
    735     } else {
    736 	a->req_body.addresses = malloc(sizeof(*a->req_body.addresses));
    737 	if (a->req_body.addresses == NULL) {
    738 	    ret = krb5_enomem(context);
    739 	    goto fail;
    740 	}
    741 
    742 	if (addrs)
    743 	    ret = krb5_copy_addresses(context, addrs, a->req_body.addresses);
    744 	else {
    745 	    ret = krb5_get_all_client_addrs (context, a->req_body.addresses);
    746 	    if(ret == 0 && a->req_body.addresses->len == 0) {
    747 		free(a->req_body.addresses);
    748 		a->req_body.addresses = NULL;
    749 	    }
    750 	}
    751 	if (ret)
    752 	    goto fail;
    753     }
    754 
    755     a->req_body.enc_authorization_data = NULL;
    756     a->req_body.additional_tickets = NULL;
    757 
    758     a->padata = NULL;
    759 
    760     return 0;
    761  fail:
    762     free_AS_REQ(a);
    763     memset_s(a, sizeof(*a), 0, sizeof(*a));
    764     return ret;
    765 }
    766 
    767 
    768 static krb5_error_code
    769 set_paid(struct pa_info_data *paid, krb5_context context,
    770 	 krb5_enctype etype,
    771 	 krb5_salttype salttype, void *salt_string, size_t salt_len,
    772 	 krb5_data *s2kparams)
    773 {
    774     paid->etype = etype;
    775     paid->salt.salttype = salttype;
    776     paid->salt.saltvalue.data = malloc(salt_len + 1);
    777     if (paid->salt.saltvalue.data == NULL) {
    778 	krb5_clear_error_message(context);
    779 	return ENOMEM;
    780     }
    781     memcpy(paid->salt.saltvalue.data, salt_string, salt_len);
    782     ((char *)paid->salt.saltvalue.data)[salt_len] = '\0';
    783     paid->salt.saltvalue.length = salt_len;
    784     if (s2kparams) {
    785 	krb5_error_code ret;
    786 
    787 	ret = krb5_copy_data(context, s2kparams, &paid->s2kparams);
    788 	if (ret) {
    789 	    krb5_clear_error_message(context);
    790 	    krb5_free_salt(context, paid->salt);
    791 	    return ret;
    792 	}
    793     } else
    794 	paid->s2kparams = NULL;
    795 
    796     return 0;
    797 }
    798 
    799 static struct pa_info_data *
    800 pa_etype_info2(krb5_context context,
    801 	       const krb5_principal client,
    802 	       const AS_REQ *asreq,
    803 	       struct pa_info_data *paid,
    804 	       heim_octet_string *data)
    805 {
    806     krb5_error_code ret;
    807     ETYPE_INFO2 e;
    808     size_t sz;
    809     size_t i, j;
    810 
    811     memset(&e, 0, sizeof(e));
    812     ret = decode_ETYPE_INFO2(data->data, data->length, &e, &sz);
    813     if (ret)
    814 	goto out;
    815     if (e.len == 0)
    816 	goto out;
    817     for (j = 0; j < asreq->req_body.etype.len; j++) {
    818 	for (i = 0; i < e.len; i++) {
    819 	    if (asreq->req_body.etype.val[j] == e.val[i].etype) {
    820 		krb5_salt salt;
    821 		if (e.val[i].salt == NULL)
    822 		    ret = krb5_get_pw_salt(context, client, &salt);
    823 		else {
    824 		    salt.saltvalue.data = *e.val[i].salt;
    825 		    salt.saltvalue.length = strlen(*e.val[i].salt);
    826 		    ret = 0;
    827 		}
    828 		if (ret == 0)
    829 		    ret = set_paid(paid, context, e.val[i].etype,
    830 				   KRB5_PW_SALT,
    831 				   salt.saltvalue.data,
    832 				   salt.saltvalue.length,
    833 				   e.val[i].s2kparams);
    834 		if (e.val[i].salt == NULL)
    835 		    krb5_free_salt(context, salt);
    836 		if (ret == 0) {
    837 		    free_ETYPE_INFO2(&e);
    838 		    return paid;
    839 		}
    840 	    }
    841 	}
    842     }
    843  out:
    844     free_ETYPE_INFO2(&e);
    845     return NULL;
    846 }
    847 
    848 static struct pa_info_data *
    849 pa_etype_info(krb5_context context,
    850 	      const krb5_principal client,
    851 	      const AS_REQ *asreq,
    852 	      struct pa_info_data *paid,
    853 	      heim_octet_string *data)
    854 {
    855     krb5_error_code ret;
    856     ETYPE_INFO e;
    857     size_t sz;
    858     size_t i, j;
    859 
    860     memset(&e, 0, sizeof(e));
    861     ret = decode_ETYPE_INFO(data->data, data->length, &e, &sz);
    862     if (ret)
    863 	goto out;
    864     if (e.len == 0)
    865 	goto out;
    866     for (j = 0; j < asreq->req_body.etype.len; j++) {
    867 	for (i = 0; i < e.len; i++) {
    868 	    if (asreq->req_body.etype.val[j] == e.val[i].etype) {
    869 		krb5_salt salt;
    870 		salt.salttype = KRB5_PW_SALT;
    871 		if (e.val[i].salt == NULL)
    872 		    ret = krb5_get_pw_salt(context, client, &salt);
    873 		else {
    874 		    salt.saltvalue = *e.val[i].salt;
    875 		    ret = 0;
    876 		}
    877 		if (e.val[i].salttype)
    878 		    salt.salttype = *e.val[i].salttype;
    879 		if (ret == 0) {
    880 		    ret = set_paid(paid, context, e.val[i].etype,
    881 				   salt.salttype,
    882 				   salt.saltvalue.data,
    883 				   salt.saltvalue.length,
    884 				   NULL);
    885 		    if (e.val[i].salt == NULL)
    886 			krb5_free_salt(context, salt);
    887 		}
    888 		if (ret == 0) {
    889 		    free_ETYPE_INFO(&e);
    890 		    return paid;
    891 		}
    892 	    }
    893 	}
    894     }
    895  out:
    896     free_ETYPE_INFO(&e);
    897     return NULL;
    898 }
    899 
    900 static struct pa_info_data *
    901 pa_pw_or_afs3_salt(krb5_context context,
    902 		   const krb5_principal client,
    903 		   const AS_REQ *asreq,
    904 		   struct pa_info_data *paid,
    905 		   heim_octet_string *data)
    906 {
    907     krb5_error_code ret;
    908     if (paid->etype == KRB5_ENCTYPE_NULL)
    909 	return NULL;
    910     ret = set_paid(paid, context,
    911 		   paid->etype,
    912 		   paid->salt.salttype,
    913 		   data->data,
    914 		   data->length,
    915 		   NULL);
    916     if (ret)
    917 	return NULL;
    918     return paid;
    919 }
    920 
    921 
    922 struct pa_info {
    923     krb5_preauthtype type;
    924     struct pa_info_data *(*salt_info)(krb5_context,
    925 				      const krb5_principal,
    926 				      const AS_REQ *,
    927 				      struct pa_info_data *,
    928 				      heim_octet_string *);
    929 };
    930 
    931 static struct pa_info pa_prefs[] = {
    932     { KRB5_PADATA_ETYPE_INFO2, pa_etype_info2 },
    933     { KRB5_PADATA_ETYPE_INFO, pa_etype_info },
    934     { KRB5_PADATA_PW_SALT, pa_pw_or_afs3_salt },
    935     { KRB5_PADATA_AFS3_SALT, pa_pw_or_afs3_salt }
    936 };
    937 
    938 static PA_DATA *
    939 find_pa_data(const METHOD_DATA *md, unsigned type)
    940 {
    941     size_t i;
    942     if (md == NULL)
    943 	return NULL;
    944     for (i = 0; i < md->len; i++)
    945 	if (md->val[i].padata_type == type)
    946 	    return &md->val[i];
    947     return NULL;
    948 }
    949 
    950 static struct pa_info_data *
    951 process_pa_info(krb5_context context,
    952 		const krb5_principal client,
    953 		const AS_REQ *asreq,
    954 		struct pa_info_data *paid,
    955 		METHOD_DATA *md)
    956 {
    957     struct pa_info_data *p = NULL;
    958     size_t i;
    959 
    960     for (i = 0; p == NULL && i < sizeof(pa_prefs)/sizeof(pa_prefs[0]); i++) {
    961 	PA_DATA *pa = find_pa_data(md, pa_prefs[i].type);
    962 	if (pa == NULL)
    963 	    continue;
    964 	paid->salt.salttype = (krb5_salttype)pa_prefs[i].type;
    965 	p = (*pa_prefs[i].salt_info)(context, client, asreq,
    966 				     paid, &pa->padata_value);
    967     }
    968     return p;
    969 }
    970 
    971 static krb5_error_code
    972 make_pa_enc_timestamp(krb5_context context, METHOD_DATA *md,
    973 		      krb5_enctype etype, krb5_keyblock *key)
    974 {
    975     PA_ENC_TS_ENC p;
    976     unsigned char *buf;
    977     size_t buf_size;
    978     size_t len = 0;
    979     EncryptedData encdata;
    980     krb5_error_code ret;
    981     int32_t usec;
    982     int usec2;
    983     krb5_crypto crypto;
    984 
    985     krb5_us_timeofday (context, &p.patimestamp, &usec);
    986     usec2         = usec;
    987     p.pausec      = &usec2;
    988 
    989     ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret);
    990     if (ret)
    991 	return ret;
    992     if(buf_size != len)
    993 	krb5_abortx(context, "internal error in ASN.1 encoder");
    994 
    995     ret = krb5_crypto_init(context, key, 0, &crypto);
    996     if (ret) {
    997 	free(buf);
    998 	return ret;
    999     }
   1000     ret = krb5_encrypt_EncryptedData(context,
   1001 				     crypto,
   1002 				     KRB5_KU_PA_ENC_TIMESTAMP,
   1003 				     buf,
   1004 				     len,
   1005 				     0,
   1006 				     &encdata);
   1007     free(buf);
   1008     krb5_crypto_destroy(context, crypto);
   1009     if (ret)
   1010 	return ret;
   1011 
   1012     ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret);
   1013     free_EncryptedData(&encdata);
   1014     if (ret)
   1015 	return ret;
   1016     if(buf_size != len)
   1017 	krb5_abortx(context, "internal error in ASN.1 encoder");
   1018 
   1019     ret = krb5_padata_add(context, md, KRB5_PADATA_ENC_TIMESTAMP, buf, len);
   1020     if (ret)
   1021 	free(buf);
   1022     return ret;
   1023 }
   1024 
   1025 static krb5_error_code
   1026 add_enc_ts_padata(krb5_context context,
   1027 		  METHOD_DATA *md,
   1028 		  krb5_principal client,
   1029 		  krb5_s2k_proc keyproc,
   1030 		  krb5_const_pointer keyseed,
   1031 		  krb5_enctype *enctypes,
   1032 		  unsigned netypes,
   1033 		  krb5_salt *salt,
   1034 		  krb5_data *s2kparams)
   1035 {
   1036     krb5_error_code ret;
   1037     krb5_salt salt2;
   1038     krb5_enctype *ep;
   1039     size_t i;
   1040 
   1041     if(salt == NULL) {
   1042 	/* default to standard salt */
   1043 	ret = krb5_get_pw_salt (context, client, &salt2);
   1044 	if (ret)
   1045 	    return ret;
   1046 	salt = &salt2;
   1047     }
   1048     if (!enctypes) {
   1049 	enctypes = context->etypes;
   1050 	netypes = 0;
   1051 	for (ep = enctypes; *ep != (krb5_enctype)ETYPE_NULL; ep++)
   1052 	    netypes++;
   1053     }
   1054 
   1055     for (i = 0; i < netypes; ++i) {
   1056 	krb5_keyblock *key;
   1057 
   1058 	_krb5_debug(context, 5, "krb5_get_init_creds: using ENC-TS with enctype %d", enctypes[i]);
   1059 
   1060 	ret = (*keyproc)(context, enctypes[i], keyseed,
   1061 			 *salt, s2kparams, &key);
   1062 	if (ret)
   1063 	    continue;
   1064 	ret = make_pa_enc_timestamp (context, md, enctypes[i], key);
   1065 	krb5_free_keyblock (context, key);
   1066 	if (ret)
   1067 	    return ret;
   1068     }
   1069     if(salt == &salt2)
   1070 	krb5_free_salt(context, salt2);
   1071     return 0;
   1072 }
   1073 
   1074 static krb5_error_code
   1075 pa_data_to_md_ts_enc(krb5_context context,
   1076 		     const AS_REQ *a,
   1077 		     const krb5_principal client,
   1078 		     krb5_get_init_creds_ctx *ctx,
   1079 		     struct pa_info_data *ppaid,
   1080 		     METHOD_DATA *md)
   1081 {
   1082     if (ctx->keyproc == NULL || ctx->keyseed == NULL)
   1083 	return 0;
   1084 
   1085     if (ppaid) {
   1086 	add_enc_ts_padata(context, md, client,
   1087 			  ctx->keyproc, ctx->keyseed,
   1088 			  &ppaid->etype, 1,
   1089 			  &ppaid->salt, ppaid->s2kparams);
   1090     } else {
   1091 	krb5_salt salt;
   1092 
   1093 	_krb5_debug(context, 5, "krb5_get_init_creds: pa-info not found, guessing salt");
   1094 
   1095 	/* make a v5 salted pa-data */
   1096 	add_enc_ts_padata(context, md, client,
   1097 			  ctx->keyproc, ctx->keyseed,
   1098 			  a->req_body.etype.val, a->req_body.etype.len,
   1099 			  NULL, NULL);
   1100 
   1101 	/* make a v4 salted pa-data */
   1102 	salt.salttype = KRB5_PW_SALT;
   1103 	krb5_data_zero(&salt.saltvalue);
   1104 	add_enc_ts_padata(context, md, client,
   1105 			  ctx->keyproc, ctx->keyseed,
   1106 			  a->req_body.etype.val, a->req_body.etype.len,
   1107 			  &salt, NULL);
   1108     }
   1109     return 0;
   1110 }
   1111 
   1112 static krb5_error_code
   1113 pa_data_to_key_plain(krb5_context context,
   1114 		     const krb5_principal client,
   1115 		     krb5_get_init_creds_ctx *ctx,
   1116 		     krb5_salt salt,
   1117 		     krb5_data *s2kparams,
   1118 		     krb5_enctype etype,
   1119 		     krb5_keyblock **key)
   1120 {
   1121     krb5_error_code ret;
   1122 
   1123     ret = (*ctx->keyproc)(context, etype, ctx->keyseed,
   1124 			   salt, s2kparams, key);
   1125     return ret;
   1126 }
   1127 
   1128 
   1129 static krb5_error_code
   1130 pa_data_to_md_pkinit(krb5_context context,
   1131 		     const AS_REQ *a,
   1132 		     const krb5_principal client,
   1133 		     int win2k,
   1134 		     krb5_get_init_creds_ctx *ctx,
   1135 		     METHOD_DATA *md)
   1136 {
   1137     if (ctx->pk_init_ctx == NULL)
   1138 	return 0;
   1139 #ifdef PKINIT
   1140     return _krb5_pk_mk_padata(context,
   1141 			      ctx->pk_init_ctx,
   1142 			      ctx->ic_flags,
   1143 			      win2k,
   1144 			      &a->req_body,
   1145 			      ctx->pk_nonce,
   1146 			      md);
   1147 #else
   1148     krb5_set_error_message(context, EINVAL,
   1149 			   N_("no support for PKINIT compiled in", ""));
   1150     return EINVAL;
   1151 #endif
   1152 }
   1153 
   1154 static krb5_error_code
   1155 pa_data_add_pac_request(krb5_context context,
   1156 			krb5_get_init_creds_ctx *ctx,
   1157 			METHOD_DATA *md)
   1158 {
   1159     size_t len = 0, length;
   1160     krb5_error_code ret;
   1161     PA_PAC_REQUEST req;
   1162     void *buf;
   1163 
   1164     switch (ctx->req_pac) {
   1165     case KRB5_INIT_CREDS_TRISTATE_UNSET:
   1166 	return 0; /* don't bother */
   1167     case KRB5_INIT_CREDS_TRISTATE_TRUE:
   1168 	req.include_pac = 1;
   1169 	break;
   1170     case KRB5_INIT_CREDS_TRISTATE_FALSE:
   1171 	req.include_pac = 0;
   1172     }
   1173 
   1174     ASN1_MALLOC_ENCODE(PA_PAC_REQUEST, buf, length,
   1175 		       &req, &len, ret);
   1176     if (ret)
   1177 	return ret;
   1178     if(len != length)
   1179 	krb5_abortx(context, "internal error in ASN.1 encoder");
   1180 
   1181     ret = krb5_padata_add(context, md, KRB5_PADATA_PA_PAC_REQUEST, buf, len);
   1182     if (ret)
   1183 	free(buf);
   1184 
   1185     return 0;
   1186 }
   1187 
   1188 /*
   1189  * Assumes caller always will free `out_md', even on error.
   1190  */
   1191 
   1192 static krb5_error_code
   1193 process_pa_data_to_md(krb5_context context,
   1194 		      const krb5_creds *creds,
   1195 		      const AS_REQ *a,
   1196 		      krb5_get_init_creds_ctx *ctx,
   1197 		      METHOD_DATA *in_md,
   1198 		      METHOD_DATA **out_md,
   1199 		      krb5_prompter_fct prompter,
   1200 		      void *prompter_data)
   1201 {
   1202     krb5_error_code ret;
   1203 
   1204     ALLOC(*out_md, 1);
   1205     if (*out_md == NULL)
   1206 	return krb5_enomem(context);
   1207 
   1208     (*out_md)->len = 0;
   1209     (*out_md)->val = NULL;
   1210 
   1211     if (_krb5_have_debug(context, 5)) {
   1212 	unsigned i;
   1213 	_krb5_debug(context, 5, "KDC send %d patypes", in_md->len);
   1214 	for (i = 0; i < in_md->len; i++)
   1215 	    _krb5_debug(context, 5, "KDC send PA-DATA type: %d", in_md->val[i].padata_type);
   1216     }
   1217 
   1218     /*
   1219      * Make sure we don't sent both ENC-TS and PK-INIT pa data, no
   1220      * need to expose our password protecting our PKCS12 key.
   1221      */
   1222 
   1223     if (ctx->pk_init_ctx) {
   1224 
   1225  	_krb5_debug(context, 5, "krb5_get_init_creds: "
   1226 		    "prepareing PKINIT padata (%s)",
   1227  		    (ctx->used_pa_types & USED_PKINIT_W2K) ? "win2k" : "ietf");
   1228 
   1229  	if (ctx->used_pa_types & USED_PKINIT_W2K) {
   1230  	    krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
   1231  				   "Already tried pkinit, looping");
   1232  	    return KRB5_GET_IN_TKT_LOOP;
   1233  	}
   1234 
   1235 	ret = pa_data_to_md_pkinit(context, a, creds->client,
   1236 				   (ctx->used_pa_types & USED_PKINIT),
   1237 				   ctx, *out_md);
   1238 	if (ret)
   1239 	    return ret;
   1240 
   1241 	if (ctx->used_pa_types & USED_PKINIT)
   1242 	    ctx->used_pa_types |= USED_PKINIT_W2K;
   1243  	else
   1244  	    ctx->used_pa_types |= USED_PKINIT;
   1245 
   1246     } else if (in_md->len != 0) {
   1247 	struct pa_info_data *paid, *ppaid;
   1248  	unsigned flag;
   1249 
   1250 	paid = calloc(1, sizeof(*paid));
   1251         if (paid == NULL)
   1252             return krb5_enomem(context);
   1253 
   1254 	paid->etype = KRB5_ENCTYPE_NULL;
   1255 	ppaid = process_pa_info(context, creds->client, a, paid, in_md);
   1256 
   1257  	if (ppaid)
   1258  	    flag = USED_ENC_TS_INFO;
   1259  	else
   1260  	    flag = USED_ENC_TS_GUESS;
   1261 
   1262  	if (ctx->used_pa_types & flag) {
   1263  	    if (ppaid)
   1264  		free_paid(context, ppaid);
   1265             free(paid);
   1266  	    krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
   1267  				   "Already tried ENC-TS-%s, looping",
   1268  				   flag == USED_ENC_TS_INFO ? "info" : "guess");
   1269  	    return KRB5_GET_IN_TKT_LOOP;
   1270  	}
   1271 
   1272 	pa_data_to_md_ts_enc(context, a, creds->client, ctx, ppaid, *out_md);
   1273 
   1274 	ctx->used_pa_types |= flag;
   1275 
   1276 	if (ppaid) {
   1277 	    if (ctx->ppaid) {
   1278 		free_paid(context, ctx->ppaid);
   1279 		free(ctx->ppaid);
   1280 	    }
   1281 	    ctx->ppaid = ppaid;
   1282 	} else
   1283 	    free(paid);
   1284     }
   1285 
   1286     pa_data_add_pac_request(context, ctx, *out_md);
   1287 
   1288     if ((ctx->fast_state.flags & KRB5_FAST_DISABLED) == 0) {
   1289  	ret = krb5_padata_add(context, *out_md, KRB5_PADATA_REQ_ENC_PA_REP, NULL, 0);
   1290  	if (ret)
   1291  	    return ret;
   1292     }
   1293 
   1294     if ((*out_md)->len == 0) {
   1295 	free(*out_md);
   1296 	*out_md = NULL;
   1297     }
   1298 
   1299     return 0;
   1300 }
   1301 
   1302 static krb5_error_code
   1303 process_pa_data_to_key(krb5_context context,
   1304 		       krb5_get_init_creds_ctx *ctx,
   1305 		       krb5_creds *creds,
   1306 		       AS_REQ *a,
   1307 		       AS_REP *rep,
   1308 		       const krb5_krbhst_info *hi,
   1309 		       krb5_keyblock **key)
   1310 {
   1311     struct pa_info_data paid, *ppaid = NULL;
   1312     krb5_error_code ret;
   1313     krb5_enctype etype;
   1314     PA_DATA *pa;
   1315 
   1316     memset(&paid, 0, sizeof(paid));
   1317 
   1318     etype = rep->enc_part.etype;
   1319 
   1320     if (rep->padata) {
   1321 	paid.etype = etype;
   1322 	ppaid = process_pa_info(context, creds->client, a, &paid,
   1323 				rep->padata);
   1324     }
   1325     if (ppaid == NULL)
   1326 	ppaid = ctx->ppaid;
   1327     if (ppaid == NULL) {
   1328 	ret = krb5_get_pw_salt (context, creds->client, &paid.salt);
   1329 	if (ret)
   1330 	    return ret;
   1331 	paid.etype = etype;
   1332 	paid.s2kparams = NULL;
   1333 	ppaid = &paid;
   1334     }
   1335 
   1336     pa = NULL;
   1337     if (rep->padata) {
   1338 	int idx = 0;
   1339 	pa = krb5_find_padata(rep->padata->val,
   1340 			      rep->padata->len,
   1341 			      KRB5_PADATA_PK_AS_REP,
   1342 			      &idx);
   1343 	if (pa == NULL) {
   1344 	    idx = 0;
   1345 	    pa = krb5_find_padata(rep->padata->val,
   1346 				  rep->padata->len,
   1347 				  KRB5_PADATA_PK_AS_REP_19,
   1348 				  &idx);
   1349 	}
   1350     }
   1351     if (pa && ctx->pk_init_ctx) {
   1352 #ifdef PKINIT
   1353 	_krb5_debug(context, 5, "krb5_get_init_creds: using PKINIT");
   1354 
   1355 	ret = _krb5_pk_rd_pa_reply(context,
   1356 				   a->req_body.realm,
   1357 				   ctx->pk_init_ctx,
   1358 				   etype,
   1359 				   hi,
   1360 				   ctx->pk_nonce,
   1361 				   &ctx->req_buffer,
   1362 				   pa,
   1363 				   key);
   1364 #else
   1365 	ret = EINVAL;
   1366 	krb5_set_error_message(context, ret, N_("no support for PKINIT compiled in", ""));
   1367 #endif
   1368     } else if (ctx->keyseed) {
   1369  	_krb5_debug(context, 5, "krb5_get_init_creds: using keyproc");
   1370 	ret = pa_data_to_key_plain(context, creds->client, ctx,
   1371 				   ppaid->salt, ppaid->s2kparams, etype, key);
   1372     } else {
   1373 	ret = EINVAL;
   1374 	krb5_set_error_message(context, ret, N_("No usable pa data type", ""));
   1375     }
   1376 
   1377     free_paid(context, &paid);
   1378     return ret;
   1379 }
   1380 
   1381 /**
   1382  * Start a new context to get a new initial credential.
   1383  *
   1384  * @param context A Kerberos 5 context.
   1385  * @param client The Kerberos principal to get the credential for, if
   1386  *     NULL is given, the default principal is used as determined by
   1387  *     krb5_get_default_principal().
   1388  * @param prompter
   1389  * @param prompter_data
   1390  * @param start_time the time the ticket should start to be valid or 0 for now.
   1391  * @param options a options structure, can be NULL for default options.
   1392  * @param rctx A new allocated free with krb5_init_creds_free().
   1393  *
   1394  * @return 0 for success or an Kerberos 5 error code, see krb5_get_error_message().
   1395  *
   1396  * @ingroup krb5_credential
   1397  */
   1398 
   1399 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1400 krb5_init_creds_init(krb5_context context,
   1401 		     krb5_principal client,
   1402 		     krb5_prompter_fct prompter,
   1403 		     void *prompter_data,
   1404 		     krb5_deltat start_time,
   1405 		     krb5_get_init_creds_opt *options,
   1406 		     krb5_init_creds_context *rctx)
   1407 {
   1408     krb5_init_creds_context ctx;
   1409     krb5_error_code ret;
   1410 
   1411     *rctx = NULL;
   1412 
   1413     ctx = calloc(1, sizeof(*ctx));
   1414     if (ctx == NULL)
   1415 	return krb5_enomem(context);
   1416 
   1417     ret = get_init_creds_common(context, client, start_time, options, ctx);
   1418     if (ret) {
   1419 	free(ctx);
   1420 	return ret;
   1421     }
   1422 
   1423     /* Set a new nonce. */
   1424     krb5_generate_random_block (&ctx->nonce, sizeof(ctx->nonce));
   1425     ctx->nonce &= 0x7fffffff;
   1426     /* XXX these just needs to be the same when using Windows PK-INIT */
   1427     ctx->pk_nonce = ctx->nonce;
   1428 
   1429     ctx->prompter = prompter;
   1430     ctx->prompter_data = prompter_data;
   1431 
   1432     *rctx = ctx;
   1433 
   1434     return ret;
   1435 }
   1436 
   1437 /**
   1438  * Sets the service that the is requested. This call is only neede for
   1439  * special initial tickets, by default the a krbtgt is fetched in the default realm.
   1440  *
   1441  * @param context a Kerberos 5 context.
   1442  * @param ctx a krb5_init_creds_context context.
   1443  * @param service the service given as a string, for example
   1444  *        "kadmind/admin". If NULL, the default krbtgt in the clients
   1445  *        realm is set.
   1446  *
   1447  * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
   1448  * @ingroup krb5_credential
   1449  */
   1450 
   1451 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1452 krb5_init_creds_set_service(krb5_context context,
   1453 			    krb5_init_creds_context ctx,
   1454 			    const char *service)
   1455 {
   1456     krb5_const_realm client_realm;
   1457     krb5_principal principal;
   1458     krb5_error_code ret;
   1459 
   1460     client_realm = krb5_principal_get_realm (context, ctx->cred.client);
   1461 
   1462     if (service) {
   1463 	ret = krb5_parse_name (context, service, &principal);
   1464 	if (ret)
   1465 	    return ret;
   1466 	krb5_principal_set_realm (context, principal, client_realm);
   1467     } else {
   1468 	ret = krb5_make_principal(context, &principal,
   1469 				  client_realm, KRB5_TGS_NAME, client_realm,
   1470 				  NULL);
   1471 	if (ret)
   1472 	    return ret;
   1473     }
   1474 
   1475     /*
   1476      * This is for Windows RODC that are picky about what name type
   1477      * the server principal have, and the really strange part is that
   1478      * they are picky about the AS-REQ name type and not the TGS-REQ
   1479      * later. Oh well.
   1480      */
   1481 
   1482     if (krb5_principal_is_krbtgt(context, principal))
   1483 	krb5_principal_set_type(context, principal, KRB5_NT_SRV_INST);
   1484 
   1485     krb5_free_principal(context, ctx->cred.server);
   1486     ctx->cred.server = principal;
   1487 
   1488     return 0;
   1489 }
   1490 
   1491 /**
   1492  * Sets the password that will use for the request.
   1493  *
   1494  * @param context a Kerberos 5 context.
   1495  * @param ctx ctx krb5_init_creds_context context.
   1496  * @param password the password to use.
   1497  *
   1498  * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
   1499  * @ingroup krb5_credential
   1500  */
   1501 
   1502 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1503 krb5_init_creds_set_password(krb5_context context,
   1504 			     krb5_init_creds_context ctx,
   1505 			     const char *password)
   1506 {
   1507     if (ctx->password) {
   1508 	size_t len;
   1509 	len = strlen(ctx->password);
   1510 	memset_s(ctx->password, len, 0, len);
   1511 	free(ctx->password);
   1512     }
   1513     if (password) {
   1514 	ctx->password = strdup(password);
   1515 	if (ctx->password == NULL)
   1516 	    return krb5_enomem(context);
   1517 	ctx->keyseed = (void *) ctx->password;
   1518     } else {
   1519 	ctx->keyseed = NULL;
   1520 	ctx->password = NULL;
   1521     }
   1522 
   1523     return 0;
   1524 }
   1525 
   1526 static krb5_error_code KRB5_CALLCONV
   1527 keytab_key_proc(krb5_context context, krb5_enctype enctype,
   1528 		krb5_const_pointer keyseed,
   1529 		krb5_salt salt, krb5_data *s2kparms,
   1530 		krb5_keyblock **key)
   1531 {
   1532     krb5_keytab_key_proc_args *args  = rk_UNCONST(keyseed);
   1533     krb5_keytab keytab = args->keytab;
   1534     krb5_principal principal = args->principal;
   1535     krb5_error_code ret;
   1536     krb5_keytab real_keytab;
   1537     krb5_keytab_entry entry;
   1538 
   1539     if(keytab == NULL)
   1540 	krb5_kt_default(context, &real_keytab);
   1541     else
   1542 	real_keytab = keytab;
   1543 
   1544     ret = krb5_kt_get_entry (context, real_keytab, principal,
   1545 			     0, enctype, &entry);
   1546     if (ret == 0) {
   1547         ret = krb5_copy_keyblock(context, &entry.keyblock, key);
   1548         krb5_kt_free_entry(context, &entry);
   1549     }
   1550 
   1551     if (keytab == NULL)
   1552 	krb5_kt_close (context, real_keytab);
   1553     return ret;
   1554 }
   1555 
   1556 
   1557 /**
   1558  * Set the keytab to use for authentication.
   1559  *
   1560  * @param context a Kerberos 5 context.
   1561  * @param ctx ctx krb5_init_creds_context context.
   1562  * @param keytab the keytab to read the key from.
   1563  *
   1564  * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
   1565  * @ingroup krb5_credential
   1566  */
   1567 
   1568 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1569 krb5_init_creds_set_keytab(krb5_context context,
   1570 			   krb5_init_creds_context ctx,
   1571 			   krb5_keytab keytab)
   1572 {
   1573     krb5_keytab_key_proc_args *a;
   1574     krb5_keytab_entry entry;
   1575     krb5_kt_cursor cursor;
   1576     krb5_enctype *etypes = NULL;
   1577     krb5_error_code ret;
   1578     size_t netypes = 0;
   1579     int kvno = 0, found = 0;
   1580 
   1581     a = malloc(sizeof(*a));
   1582     if (a == NULL)
   1583 	return krb5_enomem(context);
   1584 
   1585     a->principal = ctx->cred.client;
   1586     a->keytab    = keytab;
   1587 
   1588     ctx->keytab_data = a;
   1589     ctx->keyseed = (void *)a;
   1590     ctx->keyproc = keytab_key_proc;
   1591 
   1592     /*
   1593      * We need to the KDC what enctypes we support for this keytab,
   1594      * esp if the keytab is really a password based entry, then the
   1595      * KDC might have more enctypes in the database then what we have
   1596      * in the keytab.
   1597      */
   1598 
   1599     ret = krb5_kt_start_seq_get(context, keytab, &cursor);
   1600     if(ret)
   1601 	goto out;
   1602 
   1603     while(krb5_kt_next_entry(context, keytab, &entry, &cursor) == 0){
   1604 	void *ptr;
   1605 
   1606 	if (!krb5_principal_compare(context, entry.principal, ctx->cred.client))
   1607 	    goto next;
   1608 
   1609 	found = 1;
   1610 
   1611 	/* check if we ahve this kvno already */
   1612 	if (entry.vno > kvno) {
   1613 	    /* remove old list of etype */
   1614 	    if (etypes)
   1615 		free(etypes);
   1616 	    etypes = NULL;
   1617 	    netypes = 0;
   1618 	    kvno = entry.vno;
   1619 	} else if (entry.vno != kvno)
   1620 	    goto next;
   1621 
   1622 	/* check if enctype is supported */
   1623 	if (krb5_enctype_valid(context, entry.keyblock.keytype) != 0)
   1624 	    goto next;
   1625 
   1626 	/* add enctype to supported list */
   1627 	ptr = realloc(etypes, sizeof(etypes[0]) * (netypes + 2));
   1628 	if (ptr == NULL) {
   1629 	    free(etypes);
   1630 	    ret = krb5_enomem(context);
   1631 	    goto out;
   1632 	}
   1633 
   1634 	etypes = ptr;
   1635 	etypes[netypes] = entry.keyblock.keytype;
   1636 	etypes[netypes + 1] = ETYPE_NULL;
   1637 	netypes++;
   1638     next:
   1639 	krb5_kt_free_entry(context, &entry);
   1640     }
   1641     krb5_kt_end_seq_get(context, keytab, &cursor);
   1642 
   1643     if (etypes) {
   1644 	if (ctx->etypes)
   1645 	    free(ctx->etypes);
   1646 	ctx->etypes = etypes;
   1647     }
   1648 
   1649  out:
   1650     if (!found) {
   1651 	if (ret == 0)
   1652 	    ret = KRB5_KT_NOTFOUND;
   1653 	_krb5_kt_principal_not_found(context, ret, keytab, ctx->cred.client, 0, 0);
   1654     }
   1655 
   1656     return ret;
   1657 }
   1658 
   1659 static krb5_error_code KRB5_CALLCONV
   1660 keyblock_key_proc(krb5_context context, krb5_enctype enctype,
   1661 		  krb5_const_pointer keyseed,
   1662 		  krb5_salt salt, krb5_data *s2kparms,
   1663 		  krb5_keyblock **key)
   1664 {
   1665     return krb5_copy_keyblock (context, keyseed, key);
   1666 }
   1667 
   1668 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1669 krb5_init_creds_set_keyblock(krb5_context context,
   1670 			     krb5_init_creds_context ctx,
   1671 			     krb5_keyblock *keyblock)
   1672 {
   1673     ctx->keyseed = (void *)keyblock;
   1674     ctx->keyproc = keyblock_key_proc;
   1675 
   1676     return 0;
   1677 }
   1678 
   1679 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1680 krb5_init_creds_set_fast_ccache(krb5_context context,
   1681 				krb5_init_creds_context ctx,
   1682 				krb5_ccache fast_ccache)
   1683 {
   1684     ctx->fast_state.armor_ccache = fast_ccache;
   1685     ctx->fast_state.flags |= KRB5_FAST_REQUIRED;
   1686     return 0;
   1687 }
   1688 
   1689 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   1690 krb5_init_creds_set_fast_ap_armor_service(krb5_context context,
   1691 					  krb5_init_creds_context ctx,
   1692 					  krb5_const_principal armor_service)
   1693 {
   1694     krb5_error_code ret;
   1695 
   1696     if (ctx->fast_state.armor_service)
   1697 	krb5_free_principal(context, ctx->fast_state.armor_service);
   1698     if (armor_service) {
   1699 	ret = krb5_copy_principal(context, armor_service, &ctx->fast_state.armor_service);
   1700 	if (ret)
   1701 	    return ret;
   1702     } else {
   1703 	ctx->fast_state.armor_service = NULL;
   1704     }
   1705     ctx->fast_state.flags |= KRB5_FAST_REQUIRED | KRB5_FAST_AP_ARMOR_SERVICE;
   1706     return 0;
   1707 }
   1708 
   1709 /*
   1710  * FAST
   1711  */
   1712 
   1713 static krb5_error_code
   1714 check_fast(krb5_context context, struct fast_state *state)
   1715 {
   1716     if (state->flags & KRB5_FAST_EXPECTED) {
   1717 	krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
   1718 			       "Expected FAST, but no FAST "
   1719 			       "was in the response from the KDC");
   1720 	return KRB5KRB_AP_ERR_MODIFIED;
   1721     }
   1722     return 0;
   1723 }
   1724 
   1725 
   1726 static krb5_error_code
   1727 fast_unwrap_as_rep(krb5_context context, int32_t nonce,
   1728 		   krb5_data *chksumdata,
   1729 		   struct fast_state *state, AS_REP *rep)
   1730 {
   1731     PA_FX_FAST_REPLY fxfastrep;
   1732     KrbFastResponse fastrep;
   1733     krb5_error_code ret;
   1734     PA_DATA *pa = NULL;
   1735     int idx = 0;
   1736 
   1737     if (state->armor_crypto == NULL || rep->padata == NULL)
   1738 	return check_fast(context, state);
   1739 
   1740     /* find PA_FX_FAST_REPLY */
   1741 
   1742     pa = krb5_find_padata(rep->padata->val, rep->padata->len,
   1743 			  KRB5_PADATA_FX_FAST, &idx);
   1744     if (pa == NULL)
   1745 	return check_fast(context, state);
   1746 
   1747     memset(&fxfastrep, 0, sizeof(fxfastrep));
   1748     memset(&fastrep, 0, sizeof(fastrep));
   1749 
   1750     ret = decode_PA_FX_FAST_REPLY(pa->padata_value.data, pa->padata_value.length, &fxfastrep, NULL);
   1751     if (ret)
   1752 	return ret;
   1753 
   1754     if (fxfastrep.element == choice_PA_FX_FAST_REPLY_armored_data) {
   1755 	krb5_data data;
   1756 	ret = krb5_decrypt_EncryptedData(context,
   1757 					 state->armor_crypto,
   1758 					 KRB5_KU_FAST_REP,
   1759 					 &fxfastrep.u.armored_data.enc_fast_rep,
   1760 					 &data);
   1761 	if (ret)
   1762 	    goto out;
   1763 
   1764 	ret = decode_KrbFastResponse(data.data, data.length, &fastrep, NULL);
   1765 	krb5_data_free(&data);
   1766 	if (ret)
   1767 	    goto out;
   1768 
   1769     } else {
   1770 	ret = KRB5KDC_ERR_PREAUTH_FAILED;
   1771 	goto out;
   1772     }
   1773 
   1774     free_METHOD_DATA(rep->padata);
   1775     ret = copy_METHOD_DATA(&fastrep.padata, rep->padata);
   1776     if (ret)
   1777 	goto out;
   1778 
   1779     if (fastrep.strengthen_key) {
   1780 	if (state->strengthen_key)
   1781 	    krb5_free_keyblock(context, state->strengthen_key);
   1782 
   1783 	ret = krb5_copy_keyblock(context, fastrep.strengthen_key, &state->strengthen_key);
   1784 	if (ret)
   1785 	    goto out;
   1786     }
   1787 
   1788     if (nonce != fastrep.nonce) {
   1789 	ret = KRB5KDC_ERR_PREAUTH_FAILED;
   1790 	goto out;
   1791     }
   1792     if (fastrep.finished) {
   1793 	PrincipalName cname;
   1794 	krb5_realm crealm = NULL;
   1795 
   1796 	if (chksumdata == NULL) {
   1797 	    ret = KRB5KDC_ERR_PREAUTH_FAILED;
   1798 	    goto out;
   1799 	}
   1800 
   1801 	ret = krb5_verify_checksum(context, state->armor_crypto,
   1802 				   KRB5_KU_FAST_FINISHED,
   1803 				   chksumdata->data, chksumdata->length,
   1804 				   &fastrep.finished->ticket_checksum);
   1805 	if (ret)
   1806 	    goto out;
   1807 
   1808 	/* update */
   1809 	ret = copy_Realm(&fastrep.finished->crealm, &crealm);
   1810 	if (ret)
   1811 	    goto out;
   1812 	free_Realm(&rep->crealm);
   1813 	rep->crealm = crealm;
   1814 
   1815 	ret = copy_PrincipalName(&fastrep.finished->cname, &cname);
   1816 	if (ret)
   1817 	    goto out;
   1818 	free_PrincipalName(&rep->cname);
   1819 	rep->cname = cname;
   1820 
   1821 #if 0 /* store authenticated checksum as kdc-offset */
   1822 	fastrep->finished.timestamp;
   1823 	fastrep->finished.usec = 0;
   1824 #endif
   1825 
   1826     } else if (chksumdata) {
   1827 	/* expected fastrep.finish but didn't get it */
   1828 	ret = KRB5KDC_ERR_PREAUTH_FAILED;
   1829     }
   1830 
   1831  out:
   1832     free_PA_FX_FAST_REPLY(&fxfastrep);
   1833 
   1834     return ret;
   1835 }
   1836 
   1837 static krb5_error_code
   1838 fast_unwrap_error(krb5_context context, struct fast_state *state, KRB_ERROR *error)
   1839 {
   1840     if (state->armor_crypto == NULL)
   1841 	return check_fast(context, state);
   1842 
   1843     return 0;
   1844 }
   1845 
   1846 krb5_error_code
   1847 _krb5_make_fast_ap_fxarmor(krb5_context context,
   1848 			   krb5_ccache armor_ccache,
   1849 			   krb5_data *armor_value,
   1850 			   krb5_keyblock *armor_key,
   1851 			   krb5_crypto *armor_crypto)
   1852 {
   1853     krb5_auth_context auth_context = NULL;
   1854     krb5_creds cred, *credp = NULL;
   1855     krb5_error_code ret;
   1856     krb5_data empty;
   1857 
   1858     krb5_data_zero(&empty);
   1859 
   1860     memset(&cred, 0, sizeof(cred));
   1861 
   1862     ret = krb5_auth_con_init (context, &auth_context);
   1863     if (ret)
   1864 	goto out;
   1865 
   1866     ret = krb5_cc_get_principal(context, armor_ccache, &cred.client);
   1867     if (ret)
   1868 	goto out;
   1869 
   1870     ret = krb5_make_principal(context, &cred.server,
   1871 			      cred.client->realm,
   1872 			      KRB5_TGS_NAME,
   1873 			      cred.client->realm,
   1874 			      NULL);
   1875     if (ret) {
   1876 	krb5_free_principal(context, cred.client);
   1877 	goto out;
   1878     }
   1879 
   1880     ret = krb5_get_credentials(context, 0, armor_ccache, &cred, &credp);
   1881     krb5_free_principal(context, cred.server);
   1882     krb5_free_principal(context, cred.client);
   1883     if (ret)
   1884 	goto out;
   1885 
   1886     ret = krb5_auth_con_add_AuthorizationData(context, auth_context, KRB5_PADATA_FX_FAST_ARMOR, &empty);
   1887     if (ret)
   1888 	goto out;
   1889 
   1890     ret = krb5_mk_req_extended(context,
   1891 			       &auth_context,
   1892 			       AP_OPTS_USE_SUBKEY,
   1893 			       NULL,
   1894 			       credp,
   1895 			       armor_value);
   1896     krb5_free_creds(context, credp);
   1897     if (ret)
   1898 	goto out;
   1899 
   1900     ret = _krb5_fast_armor_key(context,
   1901 			       auth_context->local_subkey,
   1902 			       auth_context->keyblock,
   1903 			       armor_key,
   1904 			       armor_crypto);
   1905     if (ret)
   1906 	goto out;
   1907 
   1908  out:
   1909     krb5_auth_con_free(context, auth_context);
   1910     return ret;
   1911 }
   1912 
   1913 #ifndef WIN32
   1914 static heim_base_once_t armor_service_once = HEIM_BASE_ONCE_INIT;
   1915 static heim_ipc armor_service = NULL;
   1916 
   1917 static void
   1918 fast_armor_init_ipc(void *ctx)
   1919 {
   1920     heim_ipc *ipc = ctx;
   1921     heim_ipc_init_context("ANY:org.h5l.armor-service", ipc);
   1922 }
   1923 #endif /* WIN32 */
   1924 
   1925 
   1926 static krb5_error_code
   1927 make_fast_ap_fxarmor(krb5_context context,
   1928 		     struct fast_state *state,
   1929 		     const char *realm,
   1930 		     KrbFastArmor **armor)
   1931 {
   1932     KrbFastArmor *fxarmor = NULL;
   1933     krb5_error_code ret;
   1934 
   1935     if (state->armor_crypto)
   1936 	krb5_crypto_destroy(context, state->armor_crypto);
   1937     krb5_free_keyblock_contents(context, &state->armor_key);
   1938 
   1939 
   1940     ALLOC(fxarmor, 1);
   1941     if (fxarmor == NULL)
   1942 	return krb5_enomem(context);
   1943 
   1944     if (state->flags & KRB5_FAST_AP_ARMOR_SERVICE) {
   1945 #ifdef WIN32
   1946 	krb5_set_error_message(context, ENOTSUP, "Fast armor IPC service not supportted yet on Windows");
   1947 	ret = ENOTSUP;
   1948         goto out;
   1949 #else /* WIN32 */
   1950 	KERB_ARMOR_SERVICE_REPLY msg;
   1951 	krb5_data request, reply;
   1952 
   1953 	heim_base_once_f(&armor_service_once, &armor_service, fast_armor_init_ipc);
   1954 	if (armor_service == NULL) {
   1955 	    krb5_set_error_message(context, ENOENT, "Failed to open fast armor service");
   1956             ret = ENOENT;
   1957 	    goto out;
   1958 	}
   1959 
   1960 	krb5_data_zero(&reply);
   1961 
   1962 	request.data = rk_UNCONST(realm);
   1963 	request.length = strlen(realm);
   1964 
   1965 	ret = heim_ipc_call(armor_service, &request, &reply, NULL);
   1966 	heim_release(send);
   1967 	if (ret) {
   1968 	    krb5_set_error_message(context, ret, "Failed to get armor service credential");
   1969 	    goto out;
   1970 	}
   1971 
   1972 	ret = decode_KERB_ARMOR_SERVICE_REPLY(reply.data, reply.length, &msg, NULL);
   1973 	krb5_data_free(&reply);
   1974 	if (ret)
   1975 	    goto out;
   1976 
   1977 	ret = copy_KrbFastArmor(fxarmor, &msg.armor);
   1978 	if (ret) {
   1979 	    free_KERB_ARMOR_SERVICE_REPLY(&msg);
   1980 	    goto out;
   1981 	}
   1982 
   1983 	ret = krb5_copy_keyblock_contents(context, &msg.armor_key, &state->armor_key);
   1984 	free_KERB_ARMOR_SERVICE_REPLY(&msg);
   1985 	if (ret)
   1986 	    goto out;
   1987 
   1988 	ret = krb5_crypto_init(context, &state->armor_key, 0, &state->armor_crypto);
   1989 	if (ret)
   1990 	    goto out;
   1991 #endif /* WIN32 */
   1992     } else {
   1993 
   1994 	fxarmor->armor_type = 1;
   1995 
   1996 	ret = _krb5_make_fast_ap_fxarmor(context,
   1997 					 state->armor_ccache,
   1998 					 &fxarmor->armor_value,
   1999 					 &state->armor_key,
   2000 					 &state->armor_crypto);
   2001 	if (ret)
   2002 	    goto out;
   2003     }
   2004 
   2005 
   2006     *armor = fxarmor;
   2007     fxarmor = NULL;
   2008  out:
   2009     if (fxarmor) {
   2010 	free_KrbFastArmor(fxarmor);
   2011 	free(fxarmor);
   2012     }
   2013     return ret;
   2014 }
   2015 
   2016 static krb5_error_code
   2017 fast_wrap_req(krb5_context context, struct fast_state *state, KDC_REQ *req)
   2018 {
   2019     KrbFastArmor *fxarmor = NULL;
   2020     PA_FX_FAST_REQUEST fxreq;
   2021     krb5_error_code ret;
   2022     KrbFastReq fastreq;
   2023     krb5_data data;
   2024     size_t size;
   2025 
   2026     if (state->flags & KRB5_FAST_DISABLED) {
   2027 	_krb5_debug(context, 10, "fast disabled, not doing any fast wrapping");
   2028 	return 0;
   2029     }
   2030 
   2031     memset(&fxreq, 0, sizeof(fxreq));
   2032     memset(&fastreq, 0, sizeof(fastreq));
   2033     krb5_data_zero(&data);
   2034 
   2035     if (state->armor_crypto == NULL) {
   2036 	if (state->armor_ccache) {
   2037 	    /*
   2038 	     * Instead of keeping state in FX_COOKIE in the KDC, we
   2039 	     * rebuild a new armor key for every request, because this
   2040 	     * is what the MIT KDC expect and RFC6113 is vage about
   2041 	     * what the behavior should be.
   2042 	     */
   2043 	    state->type = choice_PA_FX_FAST_REQUEST_armored_data;
   2044 	} else {
   2045 	    return check_fast(context, state);
   2046 	}
   2047     }
   2048 
   2049     state->flags |= KRB5_FAST_EXPECTED;
   2050 
   2051     fastreq.fast_options.hide_client_names = 1;
   2052 
   2053     ret = copy_KDC_REQ_BODY(&req->req_body, &fastreq.req_body);
   2054     free_KDC_REQ_BODY(&req->req_body);
   2055 
   2056     req->req_body.realm = strdup(KRB5_ANON_REALM);
   2057     if ((ALLOC(req->req_body.cname, 1)) != NULL) {
   2058         req->req_body.cname->name_type = KRB5_NT_WELLKNOWN;
   2059     if ((ALLOC(req->req_body.cname->name_string.val, 2)) != NULL) {
   2060         req->req_body.cname->name_string.len = 2;
   2061         req->req_body.cname->name_string.val[0] = strdup(KRB5_WELLKNOWN_NAME);
   2062         req->req_body.cname->name_string.val[1] = strdup(KRB5_ANON_NAME);
   2063         if (req->req_body.cname->name_string.val[0] == NULL ||
   2064             req->req_body.cname->name_string.val[1] == NULL)
   2065             ret = krb5_enomem(context);
   2066       } else
   2067           ret = krb5_enomem(context);
   2068     } else
   2069         ret = krb5_enomem(context);
   2070     if ((ALLOC(req->req_body.till, 1)) != NULL)
   2071         *req->req_body.till = 0;
   2072     else
   2073         ret = krb5_enomem(context);
   2074     if (ret)
   2075         goto out;
   2076 
   2077     if (req->padata) {
   2078 	ret = copy_METHOD_DATA(req->padata, &fastreq.padata);
   2079 	free_METHOD_DATA(req->padata);
   2080     } else {
   2081 	if ((ALLOC(req->padata, 1)) == NULL)
   2082             ret = krb5_enomem(context);
   2083     }
   2084     if (ret)
   2085         goto out;
   2086 
   2087     ASN1_MALLOC_ENCODE(KrbFastReq, data.data, data.length, &fastreq, &size, ret);
   2088     if (ret)
   2089 	goto out;
   2090     heim_assert(data.length == size, "ASN.1 internal error");
   2091 
   2092     fxreq.element = state->type;
   2093 
   2094     if (state->type == choice_PA_FX_FAST_REQUEST_armored_data) {
   2095 	size_t len;
   2096 	void *buf;
   2097 
   2098 	ret = make_fast_ap_fxarmor(context, state, fastreq.req_body.realm, &fxreq.u.armored_data.armor);
   2099 	if (ret)
   2100 	    goto out;
   2101 
   2102 	heim_assert(state->armor_crypto != NULL, "FAST armor key missing when FAST started");
   2103 
   2104 	ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, len, &req->req_body, &size, ret);
   2105 	if (ret)
   2106 	    goto out;
   2107 	heim_assert(len == size, "ASN.1 internal error");
   2108 
   2109 	ret = krb5_create_checksum(context, state->armor_crypto,
   2110 				   KRB5_KU_FAST_REQ_CHKSUM, 0,
   2111 				   buf, len,
   2112 				   &fxreq.u.armored_data.req_checksum);
   2113 	free(buf);
   2114 	if (ret)
   2115 	    goto out;
   2116 
   2117 	ret = krb5_encrypt_EncryptedData(context, state->armor_crypto,
   2118 					 KRB5_KU_FAST_ENC,
   2119 					 data.data,
   2120 					 data.length,
   2121 					 0,
   2122 					 &fxreq.u.armored_data.enc_fast_req);
   2123 	krb5_data_free(&data);
   2124         if (ret)
   2125             goto out;
   2126 
   2127     } else {
   2128 	krb5_data_free(&data);
   2129 	heim_assert(false, "unknown FAST type, internal error");
   2130     }
   2131 
   2132     ASN1_MALLOC_ENCODE(PA_FX_FAST_REQUEST, data.data, data.length, &fxreq, &size, ret);
   2133     if (ret)
   2134 	goto out;
   2135     heim_assert(data.length == size, "ASN.1 internal error");
   2136 
   2137 
   2138     ret = krb5_padata_add(context, req->padata, KRB5_PADATA_FX_FAST, data.data, data.length);
   2139     if (ret)
   2140 	goto out;
   2141     krb5_data_zero(&data);
   2142 
   2143  out:
   2144     free_PA_FX_FAST_REQUEST(&fxreq);
   2145     free_KrbFastReq(&fastreq);
   2146     if (fxarmor) {
   2147 	free_KrbFastArmor(fxarmor);
   2148 	free(fxarmor);
   2149     }
   2150     krb5_data_free(&data);
   2151 
   2152     return ret;
   2153 }
   2154 
   2155 
   2156 /**
   2157  * The core loop if krb5_get_init_creds() function family. Create the
   2158  * packets and have the caller send them off to the KDC.
   2159  *
   2160  * If the caller want all work been done for them, use
   2161  * krb5_init_creds_get() instead.
   2162  *
   2163  * @param context a Kerberos 5 context.
   2164  * @param ctx ctx krb5_init_creds_context context.
   2165  * @param in input data from KDC, first round it should be reset by krb5_data_zer().
   2166  * @param out reply to KDC.
   2167  * @param hostinfo KDC address info, first round it can be NULL.
   2168  * @param flags status of the round, if
   2169  *        KRB5_INIT_CREDS_STEP_FLAG_CONTINUE is set, continue one more round.
   2170  *
   2171  * @return 0 for success, or an Kerberos 5 error code, see
   2172  *     krb5_get_error_message().
   2173  *
   2174  * @ingroup krb5_credential
   2175  */
   2176 
   2177 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   2178 krb5_init_creds_step(krb5_context context,
   2179 		     krb5_init_creds_context ctx,
   2180 		     krb5_data *in,
   2181 		     krb5_data *out,
   2182 		     krb5_krbhst_info *hostinfo,
   2183 		     unsigned int *flags)
   2184 {
   2185     krb5_error_code ret;
   2186     size_t len = 0;
   2187     size_t size;
   2188     AS_REQ req2;
   2189 
   2190     krb5_data_zero(out);
   2191 
   2192     if (ctx->as_req.req_body.cname == NULL) {
   2193 	ret = init_as_req(context, ctx->flags, &ctx->cred,
   2194 			  ctx->addrs, ctx->etypes, &ctx->as_req);
   2195 	if (ret) {
   2196 	    free_init_creds_ctx(context, ctx);
   2197 	    return ret;
   2198 	}
   2199     }
   2200 
   2201 #define MAX_PA_COUNTER 10
   2202     if (ctx->pa_counter > MAX_PA_COUNTER) {
   2203 	krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
   2204 			       N_("Looping %d times while getting "
   2205 				  "initial credentials", ""),
   2206 			       ctx->pa_counter);
   2207 	return KRB5_GET_IN_TKT_LOOP;
   2208     }
   2209     ctx->pa_counter++;
   2210 
   2211     _krb5_debug(context, 5, "krb5_get_init_creds: loop %d", ctx->pa_counter);
   2212 
   2213     /* Lets process the input packet */
   2214     if (in && in->length) {
   2215 	krb5_kdc_rep rep;
   2216 
   2217 	memset(&rep, 0, sizeof(rep));
   2218 
   2219 	_krb5_debug(context, 5, "krb5_get_init_creds: processing input");
   2220 
   2221 	ret = decode_AS_REP(in->data, in->length, &rep.kdc_rep, &size);
   2222 	if (ret == 0) {
   2223 	    unsigned eflags = EXTRACT_TICKET_AS_REQ | EXTRACT_TICKET_TIMESYNC;
   2224 	    krb5_data data;
   2225 
   2226 	    /*
   2227 	     * Unwrap AS-REP
   2228 	     */
   2229 	    ASN1_MALLOC_ENCODE(Ticket, data.data, data.length,
   2230 			       &rep.kdc_rep.ticket, &size, ret);
   2231 	    if (ret)
   2232 		goto out;
   2233 	    heim_assert(data.length == size, "ASN.1 internal error");
   2234 
   2235 	    ret = fast_unwrap_as_rep(context, ctx->nonce, &data,
   2236 				     &ctx->fast_state, &rep.kdc_rep);
   2237 	    krb5_data_free(&data);
   2238 	    if (ret)
   2239 		goto out;
   2240 
   2241 	    /*
   2242 	     * Now check and extract the ticket
   2243 	     */
   2244 
   2245 	    if (ctx->flags.canonicalize) {
   2246 		eflags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH;
   2247 		eflags |= EXTRACT_TICKET_MATCH_REALM;
   2248 	    }
   2249 	    if (ctx->ic_flags & KRB5_INIT_CREDS_NO_C_CANON_CHECK)
   2250 		eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
   2251 	    if (ctx->flags.request_anonymous)
   2252 		eflags |= EXTRACT_TICKET_MATCH_ANON;
   2253 
   2254 	    ret = process_pa_data_to_key(context, ctx, &ctx->cred,
   2255 					 &ctx->as_req, &rep.kdc_rep,
   2256 					 hostinfo, &ctx->fast_state.reply_key);
   2257 	    if (ret) {
   2258 		free_AS_REP(&rep.kdc_rep);
   2259 		goto out;
   2260 	    }
   2261 
   2262 	    _krb5_debug(context, 5, "krb5_get_init_creds: extracting ticket");
   2263 
   2264 	    ret = _krb5_extract_ticket(context,
   2265 				       &rep,
   2266 				       &ctx->cred,
   2267 				       ctx->fast_state.reply_key,
   2268 				       NULL,
   2269 				       KRB5_KU_AS_REP_ENC_PART,
   2270 				       NULL,
   2271 				       ctx->nonce,
   2272 				       eflags,
   2273 				       &ctx->req_buffer,
   2274 				       NULL,
   2275 				       NULL);
   2276 	    if (ret == 0 && ctx->pk_init_ctx) {
   2277 		PA_DATA *pa_pkinit_kx;
   2278 		int idx = 0;
   2279 
   2280 		pa_pkinit_kx =
   2281 		    krb5_find_padata(rep.kdc_rep.padata->val,
   2282 				     rep.kdc_rep.padata->len,
   2283 				     KRB5_PADATA_PKINIT_KX,
   2284 				     &idx);
   2285 
   2286 		ret = _krb5_pk_kx_confirm(context, ctx->pk_init_ctx,
   2287 					  ctx->fast_state.reply_key,
   2288 					  &ctx->cred.session,
   2289 					  pa_pkinit_kx);
   2290 		if (ret)
   2291 		    krb5_set_error_message(context, ret,
   2292 					   N_("Failed to confirm PA-PKINIT-KX", ""));
   2293 		else if (pa_pkinit_kx != NULL)
   2294 		    ctx->ic_flags |= KRB5_INIT_CREDS_PKINIT_KX_VALID;
   2295 	    }
   2296 	    if (ret == 0)
   2297 		ret = copy_EncKDCRepPart(&rep.enc_part, &ctx->enc_part);
   2298 
   2299 	    krb5_free_keyblock(context, ctx->fast_state.reply_key);
   2300 	    ctx->fast_state.reply_key = NULL;
   2301 	    *flags = 0;
   2302 
   2303 	    free_AS_REP(&rep.kdc_rep);
   2304 	    free_EncASRepPart(&rep.enc_part);
   2305 
   2306 	    return ret;
   2307 
   2308 	} else {
   2309 	    /* let's try to parse it as a KRB-ERROR */
   2310 
   2311 	    _krb5_debug(context, 5, "krb5_get_init_creds: got an error");
   2312 
   2313 	    free_KRB_ERROR(&ctx->error);
   2314 
   2315 	    ret = krb5_rd_error(context, in, &ctx->error);
   2316 	    if(ret && in->length && ((char*)in->data)[0] == 4)
   2317 		ret = KRB5KRB_AP_ERR_V4_REPLY;
   2318 	    if (ret) {
   2319 		_krb5_debug(context, 5, "krb5_get_init_creds: failed to read error");
   2320 		goto out;
   2321 	    }
   2322 
   2323 	    /*
   2324 	     * Unwrap KRB-ERROR
   2325 	     */
   2326 	    ret = fast_unwrap_error(context, &ctx->fast_state, &ctx->error);
   2327 	    if (ret)
   2328 		goto out;
   2329 
   2330 	    /*
   2331 	     *
   2332 	     */
   2333 
   2334 	    ret = krb5_error_from_rd_error(context, &ctx->error, &ctx->cred);
   2335 
   2336 	    _krb5_debug(context, 5, "krb5_get_init_creds: KRB-ERROR %d", ret);
   2337 
   2338 	    /*
   2339 	     * If no preauth was set and KDC requires it, give it one
   2340 	     * more try.
   2341 	     */
   2342 
   2343 	    if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED) {
   2344 
   2345 	        free_METHOD_DATA(&ctx->md);
   2346 		memset_s(&ctx->md, sizeof(ctx->md), 0, sizeof(ctx->md));
   2347 
   2348 		if (ctx->error.e_data) {
   2349 		    ret = decode_METHOD_DATA(ctx->error.e_data->data,
   2350 					     ctx->error.e_data->length,
   2351 					     &ctx->md,
   2352 					     NULL);
   2353 		    if (ret)
   2354 			krb5_set_error_message(context, ret,
   2355 					       N_("Failed to decode METHOD-DATA", ""));
   2356 		} else {
   2357 		    krb5_set_error_message(context, ret,
   2358 					   N_("Preauth required but no preauth "
   2359 					      "options send by KDC", ""));
   2360 		}
   2361 	    } else if (ret == KRB5KRB_AP_ERR_SKEW && context->kdc_sec_offset == 0) {
   2362 		/*
   2363 		 * Try adapt to timeskrew when we are using pre-auth, and
   2364 		 * if there was a time skew, try again.
   2365 		 */
   2366 		krb5_set_real_time(context, ctx->error.stime, -1);
   2367 		if (context->kdc_sec_offset)
   2368 		    ret = 0;
   2369 
   2370 		_krb5_debug(context, 10, "init_creds: err skew updateing kdc offset to %d",
   2371 			    context->kdc_sec_offset);
   2372 
   2373 		ctx->used_pa_types = 0;
   2374 
   2375 	    } else if (ret == KRB5_KDC_ERR_WRONG_REALM && ctx->flags.canonicalize) {
   2376 	        /* client referal to a new realm */
   2377 
   2378 		if (ctx->error.crealm == NULL) {
   2379 		    krb5_set_error_message(context, ret,
   2380 					   N_("Got a client referral, not but no realm", ""));
   2381 		    goto out;
   2382 		}
   2383 		_krb5_debug(context, 5,
   2384 			    "krb5_get_init_creds: got referal to realm %s",
   2385 			    *ctx->error.crealm);
   2386 
   2387 		ret = krb5_principal_set_realm(context,
   2388 					       ctx->cred.client,
   2389 					       *ctx->error.crealm);
   2390 		if (ret)
   2391 		    goto out;
   2392 
   2393 		if (krb5_principal_is_krbtgt(context, ctx->cred.server)) {
   2394 		    ret = krb5_init_creds_set_service(context, ctx, NULL);
   2395 		    if (ret)
   2396 			goto out;
   2397 		}
   2398 
   2399 		free_AS_REQ(&ctx->as_req);
   2400 		memset_s(&ctx->as_req, sizeof(ctx->as_req), 0, sizeof(ctx->as_req));
   2401 
   2402 		ctx->used_pa_types = 0;
   2403 	    } else if (ret == KRB5KDC_ERR_KEY_EXP && ctx->runflags.change_password == 0 && ctx->prompter) {
   2404 		char buf2[1024];
   2405 
   2406 		ctx->runflags.change_password = 1;
   2407 
   2408 		ctx->prompter(context, ctx->prompter_data, NULL, N_("Password has expired", ""), 0, NULL);
   2409 
   2410 
   2411 		/* try to avoid recursion */
   2412 		if (ctx->in_tkt_service != NULL && strcmp(ctx->in_tkt_service, "kadmin/changepw") == 0)
   2413 		    goto out;
   2414 
   2415                 /* don't try to change password where then where none */
   2416                 if (ctx->prompter == NULL)
   2417                     goto out;
   2418 
   2419 		ret = change_password(context,
   2420 				      ctx->cred.client,
   2421 				      ctx->password,
   2422 				      buf2,
   2423 				      sizeof(buf2),
   2424 				      ctx->prompter,
   2425 				      ctx->prompter_data,
   2426 				      NULL);
   2427 		if (ret)
   2428 		    goto out;
   2429 
   2430 		krb5_init_creds_set_password(context, ctx, buf2);
   2431 
   2432  		ctx->used_pa_types = 0;
   2433 		ret = 0;
   2434 
   2435  	    } else if (ret == KRB5KDC_ERR_PREAUTH_FAILED) {
   2436 
   2437  		if (ctx->fast_state.flags & KRB5_FAST_DISABLED)
   2438  		    goto out;
   2439  		if (ctx->fast_state.flags & (KRB5_FAST_REQUIRED | KRB5_FAST_EXPECTED))
   2440  		    goto out;
   2441 
   2442  		_krb5_debug(context, 10, "preauth failed with FAST, "
   2443 			    "and told by KD or user, trying w/o FAST");
   2444 
   2445  		ctx->fast_state.flags |= KRB5_FAST_DISABLED;
   2446  		ctx->used_pa_types = 0;
   2447 		ret = 0;
   2448 	    }
   2449 	    if (ret)
   2450 		goto out;
   2451 	}
   2452     }
   2453 
   2454     if (ctx->as_req.req_body.cname == NULL) {
   2455 	ret = init_as_req(context, ctx->flags, &ctx->cred,
   2456 			  ctx->addrs, ctx->etypes, &ctx->as_req);
   2457 	if (ret) {
   2458 	    free_init_creds_ctx(context, ctx);
   2459 	    return ret;
   2460 	}
   2461     }
   2462 
   2463     if (ctx->as_req.padata) {
   2464 	free_METHOD_DATA(ctx->as_req.padata);
   2465 	free(ctx->as_req.padata);
   2466 	ctx->as_req.padata = NULL;
   2467     }
   2468 
   2469     /* Set a new nonce. */
   2470     ctx->as_req.req_body.nonce = ctx->nonce;
   2471 
   2472     /* fill_in_md_data */
   2473     ret = process_pa_data_to_md(context, &ctx->cred, &ctx->as_req, ctx,
   2474 				&ctx->md, &ctx->as_req.padata,
   2475 				ctx->prompter, ctx->prompter_data);
   2476     if (ret)
   2477 	goto out;
   2478 
   2479     /*
   2480      * Wrap with FAST
   2481      */
   2482     copy_AS_REQ(&ctx->as_req, &req2);
   2483 
   2484     ret = fast_wrap_req(context, &ctx->fast_state, &req2);
   2485     if (ret) {
   2486 	free_AS_REQ(&req2);
   2487 	goto out;
   2488     }
   2489 
   2490     krb5_data_free(&ctx->req_buffer);
   2491 
   2492     ASN1_MALLOC_ENCODE(AS_REQ,
   2493 		       ctx->req_buffer.data, ctx->req_buffer.length,
   2494 		       &req2, &len, ret);
   2495     free_AS_REQ(&req2);
   2496     if (ret)
   2497 	goto out;
   2498     if(len != ctx->req_buffer.length)
   2499 	krb5_abortx(context, "internal error in ASN.1 encoder");
   2500 
   2501     out->data = ctx->req_buffer.data;
   2502     out->length = ctx->req_buffer.length;
   2503 
   2504     *flags = KRB5_INIT_CREDS_STEP_FLAG_CONTINUE;
   2505 
   2506     return 0;
   2507  out:
   2508     return ret;
   2509 }
   2510 
   2511 /**
   2512  * Extract the newly acquired credentials from krb5_init_creds_context
   2513  * context.
   2514  *
   2515  * @param context A Kerberos 5 context.
   2516  * @param ctx
   2517  * @param cred credentials, free with krb5_free_cred_contents().
   2518  *
   2519  * @return 0 for sucess or An Kerberos error code, see krb5_get_error_message().
   2520  */
   2521 
   2522 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   2523 krb5_init_creds_get_creds(krb5_context context,
   2524 			  krb5_init_creds_context ctx,
   2525 			  krb5_creds *cred)
   2526 {
   2527     return krb5_copy_creds_contents(context, &ctx->cred, cred);
   2528 }
   2529 
   2530 /**
   2531  * Get the last error from the transaction.
   2532  *
   2533  * @return Returns 0 or an error code
   2534  *
   2535  * @ingroup krb5_credential
   2536  */
   2537 
   2538 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   2539 krb5_init_creds_get_error(krb5_context context,
   2540 			  krb5_init_creds_context ctx,
   2541 			  KRB_ERROR *error)
   2542 {
   2543     krb5_error_code ret;
   2544 
   2545     ret = copy_KRB_ERROR(&ctx->error, error);
   2546     if (ret)
   2547 	krb5_enomem(context);
   2548 
   2549     return ret;
   2550 }
   2551 
   2552 /**
   2553  *
   2554  * @ingroup krb5_credential
   2555  */
   2556 
   2557 krb5_error_code
   2558 krb5_init_creds_store(krb5_context context,
   2559 		      krb5_init_creds_context ctx,
   2560 		      krb5_ccache id)
   2561 {
   2562     krb5_error_code ret;
   2563 
   2564     if (ctx->cred.client == NULL) {
   2565 	ret = KRB5KDC_ERR_PREAUTH_REQUIRED;
   2566 	krb5_set_error_message(context, ret, "init creds not completed yet");
   2567 	return ret;
   2568     }
   2569 
   2570     ret = krb5_cc_initialize(context, id, ctx->cred.client);
   2571     if (ret)
   2572 	return ret;
   2573 
   2574     ret = krb5_cc_store_cred(context, id, &ctx->cred);
   2575     if (ret)
   2576 	return ret;
   2577 
   2578     if (ctx->cred.flags.b.enc_pa_rep) {
   2579 	krb5_data data = { 3, rk_UNCONST("yes") };
   2580 	ret = krb5_cc_set_config(context, id, ctx->cred.server,
   2581 				 "fast_avail", &data);
   2582 	if (ret)
   2583 	    return ret;
   2584     }
   2585 
   2586     return ret;
   2587 }
   2588 
   2589 /**
   2590  * Free the krb5_init_creds_context allocated by krb5_init_creds_init().
   2591  *
   2592  * @param context A Kerberos 5 context.
   2593  * @param ctx The krb5_init_creds_context to free.
   2594  *
   2595  * @ingroup krb5_credential
   2596  */
   2597 
   2598 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
   2599 krb5_init_creds_free(krb5_context context,
   2600 		     krb5_init_creds_context ctx)
   2601 {
   2602     free_init_creds_ctx(context, ctx);
   2603     free(ctx);
   2604 }
   2605 
   2606 /**
   2607  * Get new credentials as setup by the krb5_init_creds_context.
   2608  *
   2609  * @param context A Kerberos 5 context.
   2610  * @param ctx The krb5_init_creds_context to process.
   2611  *
   2612  * @ingroup krb5_credential
   2613  */
   2614 
   2615 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   2616 krb5_init_creds_get(krb5_context context, krb5_init_creds_context ctx)
   2617 {
   2618     krb5_sendto_ctx stctx = NULL;
   2619     krb5_krbhst_info *hostinfo = NULL;
   2620     krb5_error_code ret;
   2621     krb5_data in, out;
   2622     unsigned int flags = 0;
   2623 
   2624     krb5_data_zero(&in);
   2625     krb5_data_zero(&out);
   2626 
   2627     ret = krb5_sendto_ctx_alloc(context, &stctx);
   2628     if (ret)
   2629 	goto out;
   2630     krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
   2631 
   2632     while (1) {
   2633 	flags = 0;
   2634 	ret = krb5_init_creds_step(context, ctx, &in, &out, hostinfo, &flags);
   2635 	krb5_data_free(&in);
   2636 	if (ret)
   2637 	    goto out;
   2638 
   2639 	if ((flags & 1) == 0)
   2640 	    break;
   2641 
   2642 	ret = krb5_sendto_context (context, stctx, &out,
   2643 				   ctx->cred.client->realm, &in);
   2644     	if (ret)
   2645 	    goto out;
   2646 
   2647     }
   2648 
   2649  out:
   2650     if (stctx)
   2651 	krb5_sendto_ctx_free(context, stctx);
   2652 
   2653     return ret;
   2654 }
   2655 
   2656 /**
   2657  * Get new credentials using password.
   2658  *
   2659  * @ingroup krb5_credential
   2660  */
   2661 
   2662 
   2663 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   2664 krb5_get_init_creds_password(krb5_context context,
   2665 			     krb5_creds *creds,
   2666 			     krb5_principal client,
   2667 			     const char *password,
   2668 			     krb5_prompter_fct prompter,
   2669 			     void *data,
   2670 			     krb5_deltat start_time,
   2671 			     const char *in_tkt_service,
   2672 			     krb5_get_init_creds_opt *options)
   2673 {
   2674     krb5_init_creds_context ctx;
   2675     char buf[BUFSIZ], buf2[BUFSIZ];
   2676     krb5_error_code ret;
   2677     int chpw = 0;
   2678 
   2679  again:
   2680     ret = krb5_init_creds_init(context, client, prompter, data, start_time, options, &ctx);
   2681     if (ret)
   2682 	goto out;
   2683 
   2684     ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
   2685     if (ret)
   2686 	goto out;
   2687 
   2688     if (prompter != NULL && ctx->password == NULL && password == NULL) {
   2689 	krb5_prompt prompt;
   2690 	krb5_data password_data;
   2691 	char *p, *q = NULL;
   2692 	int aret;
   2693 
   2694 	ret = krb5_unparse_name(context, client, &p);
   2695 	if (ret)
   2696 	    goto out;
   2697 
   2698 	aret = asprintf(&q, "%s's Password: ", p);
   2699 	free (p);
   2700 	if (aret == -1 || q == NULL) {
   2701 	    ret = krb5_enomem(context);
   2702 	    goto out;
   2703 	}
   2704 	prompt.prompt = q;
   2705 	password_data.data   = buf;
   2706 	password_data.length = sizeof(buf);
   2707 	prompt.hidden = 1;
   2708 	prompt.reply  = &password_data;
   2709 	prompt.type   = KRB5_PROMPT_TYPE_PASSWORD;
   2710 
   2711 	ret = (*prompter) (context, data, NULL, NULL, 1, &prompt);
   2712 	free (q);
   2713 	if (ret) {
   2714 	    memset_s(buf, sizeof(buf), 0, sizeof(buf));
   2715 	    ret = KRB5_LIBOS_PWDINTR;
   2716 	    krb5_clear_error_message (context);
   2717 	    goto out;
   2718 	}
   2719 	password = password_data.data;
   2720     }
   2721 
   2722     if (password) {
   2723 	ret = krb5_init_creds_set_password(context, ctx, password);
   2724 	if (ret)
   2725 	    goto out;
   2726     }
   2727 
   2728     ret = krb5_init_creds_get(context, ctx);
   2729 
   2730     if (ret == 0)
   2731 	krb5_process_last_request(context, options, ctx);
   2732 
   2733 
   2734     if (ret == KRB5KDC_ERR_KEY_EXPIRED && chpw == 0) {
   2735 	/* try to avoid recursion */
   2736 	if (in_tkt_service != NULL && strcmp(in_tkt_service, "kadmin/changepw") == 0)
   2737 	   goto out;
   2738 
   2739 	/* don't try to change password where then where none */
   2740 	if (prompter == NULL)
   2741 	    goto out;
   2742 
   2743 	if ((options->flags & KRB5_GET_INIT_CREDS_OPT_CHANGE_PASSWORD_PROMPT) &&
   2744             !options->change_password_prompt)
   2745 		goto out;
   2746 
   2747 	ret = change_password (context,
   2748 			       client,
   2749 			       ctx->password,
   2750 			       buf2,
   2751 			       sizeof(buf2),
   2752 			       prompter,
   2753 			       data,
   2754 			       options);
   2755 	if (ret)
   2756 	    goto out;
   2757 	password = buf2;
   2758 	chpw = 1;
   2759 	krb5_init_creds_free(context, ctx);
   2760 	goto again;
   2761     }
   2762 
   2763  out:
   2764     if (ret == 0)
   2765 	krb5_init_creds_get_creds(context, ctx, creds);
   2766 
   2767     if (ctx)
   2768 	krb5_init_creds_free(context, ctx);
   2769 
   2770     memset_s(buf, sizeof(buf), 0, sizeof(buf));
   2771     memset_s(buf2, sizeof(buf), 0, sizeof(buf2));
   2772     return ret;
   2773 }
   2774 
   2775 /**
   2776  * Get new credentials using keyblock.
   2777  *
   2778  * @ingroup krb5_credential
   2779  */
   2780 
   2781 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   2782 krb5_get_init_creds_keyblock(krb5_context context,
   2783 			     krb5_creds *creds,
   2784 			     krb5_principal client,
   2785 			     krb5_keyblock *keyblock,
   2786 			     krb5_deltat start_time,
   2787 			     const char *in_tkt_service,
   2788 			     krb5_get_init_creds_opt *options)
   2789 {
   2790     krb5_init_creds_context ctx;
   2791     krb5_error_code ret;
   2792 
   2793     memset(creds, 0, sizeof(*creds));
   2794 
   2795     ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx);
   2796     if (ret)
   2797 	goto out;
   2798 
   2799     ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
   2800     if (ret)
   2801 	goto out;
   2802 
   2803     ret = krb5_init_creds_set_keyblock(context, ctx, keyblock);
   2804     if (ret)
   2805 	goto out;
   2806 
   2807     ret = krb5_init_creds_get(context, ctx);
   2808 
   2809     if (ret == 0)
   2810         krb5_process_last_request(context, options, ctx);
   2811 
   2812  out:
   2813     if (ret == 0)
   2814 	krb5_init_creds_get_creds(context, ctx, creds);
   2815 
   2816     if (ctx)
   2817 	krb5_init_creds_free(context, ctx);
   2818 
   2819     return ret;
   2820 }
   2821 
   2822 /**
   2823  * Get new credentials using keytab.
   2824  *
   2825  * @ingroup krb5_credential
   2826  */
   2827 
   2828 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
   2829 krb5_get_init_creds_keytab(krb5_context context,
   2830 			   krb5_creds *creds,
   2831 			   krb5_principal client,
   2832 			   krb5_keytab keytab,
   2833 			   krb5_deltat start_time,
   2834 			   const char *in_tkt_service,
   2835 			   krb5_get_init_creds_opt *options)
   2836 {
   2837     krb5_init_creds_context ctx;
   2838     krb5_keytab_entry ktent;
   2839     krb5_error_code ret;
   2840 
   2841     memset(&ktent, 0, sizeof(ktent));
   2842     memset(creds, 0, sizeof(*creds));
   2843 
   2844     if (strcmp(client->realm, "") == 0) {
   2845         /*
   2846          * Referral realm.  We have a keytab, so pick a realm by
   2847          * matching in the keytab.
   2848          */
   2849         ret = krb5_kt_get_entry(context, keytab, client, 0, 0, &ktent);
   2850         if (ret == 0)
   2851             client = ktent.principal;
   2852     }
   2853 
   2854     ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx);
   2855     if (ret)
   2856 	goto out;
   2857 
   2858     ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
   2859     if (ret)
   2860 	goto out;
   2861 
   2862     ret = krb5_init_creds_set_keytab(context, ctx, keytab);
   2863     if (ret)
   2864 	goto out;
   2865 
   2866     ret = krb5_init_creds_get(context, ctx);
   2867     if (ret == 0)
   2868         krb5_process_last_request(context, options, ctx);
   2869 
   2870  out:
   2871     krb5_kt_free_entry(context, &ktent);
   2872     if (ret == 0)
   2873 	krb5_init_creds_get_creds(context, ctx, creds);
   2874 
   2875     if (ctx)
   2876 	krb5_init_creds_free(context, ctx);
   2877 
   2878     return ret;
   2879 }
   2880