init_creds_pw.c revision 1.4
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 H��gskolan
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
43typedef 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
110struct pa_info_data {
111    krb5_enctype etype;
112    krb5_salt salt;
113    krb5_data *s2kparams;
114};
115
116static void
117free_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
124static krb5_error_code KRB5_CALLCONV
125default_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
155static void
156free_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
199static int
200get_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
223static krb5_error_code
224init_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
264out:
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
273static void
274report_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
297krb5_error_code
298krb5_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
386static krb5_addresses no_addrs = { 0, NULL };
387
388static krb5_error_code
389get_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
516static krb5_error_code
517change_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
635out:
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
645KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
646krb5_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
659static krb5_error_code
660init_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
768static krb5_error_code
769set_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
799static struct pa_info_data *
800pa_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
848static struct pa_info_data *
849pa_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
900static struct pa_info_data *
901pa_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
922struct 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
931static 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
938static PA_DATA *
939find_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
950static struct pa_info_data *
951process_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
971static krb5_error_code
972make_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
1025static krb5_error_code
1026add_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
1074static krb5_error_code
1075pa_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
1112static krb5_error_code
1113pa_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
1129static krb5_error_code
1130pa_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
1154static krb5_error_code
1155pa_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
1192static krb5_error_code
1193process_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
1302static krb5_error_code
1303process_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
1399KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1400krb5_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
1451KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1452krb5_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
1502KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1503krb5_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
1526static krb5_error_code KRB5_CALLCONV
1527keytab_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
1568KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1569krb5_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
1659static krb5_error_code KRB5_CALLCONV
1660keyblock_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
1668KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1669krb5_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
1679KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1680krb5_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
1689KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1690krb5_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
1713static krb5_error_code
1714check_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
1726static krb5_error_code
1727fast_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
1837static krb5_error_code
1838fast_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
1846krb5_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
1914static heim_base_once_t armor_service_once = HEIM_BASE_ONCE_INIT;
1915static heim_ipc armor_service = NULL;
1916
1917static void
1918fast_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
1926static krb5_error_code
1927make_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
2016static krb5_error_code
2017fast_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
2177KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2178krb5_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
2522KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2523krb5_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
2538KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2539krb5_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
2557krb5_error_code
2558krb5_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
2598KRB5_LIB_FUNCTION void KRB5_LIB_CALL
2599krb5_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
2615KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2616krb5_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
2663KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2664krb5_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
2781KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2782krb5_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
2828KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2829krb5_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