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