1/*
2 * Copyright (c) 1997 - 2007 Kungliga Tekniska H�gskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the Institute nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include "krb5_locl.h"
35
36RCSID("$Id: init_creds_pw.c 21931 2007-08-27 14:11:55Z lha $");
37
38typedef struct krb5_get_init_creds_ctx {
39    KDCOptions flags;
40    krb5_creds cred;
41    krb5_addresses *addrs;
42    krb5_enctype *etypes;
43    krb5_preauthtype *pre_auth_types;
44    const char *in_tkt_service;
45    unsigned nonce;
46    unsigned pk_nonce;
47
48    krb5_data req_buffer;
49    AS_REQ as_req;
50    int pa_counter;
51
52    const char *password;
53    krb5_s2k_proc key_proc;
54
55    krb5_get_init_creds_tristate req_pac;
56
57    krb5_pk_init_ctx pk_init_ctx;
58    int ic_flags;
59} krb5_get_init_creds_ctx;
60
61static krb5_error_code
62default_s2k_func(krb5_context context, krb5_enctype type,
63		 krb5_const_pointer keyseed,
64		 krb5_salt salt, krb5_data *s2kparms,
65		 krb5_keyblock **key)
66{
67    krb5_error_code ret;
68    krb5_data password;
69    krb5_data opaque;
70
71    password.data = rk_UNCONST(keyseed);
72    password.length = strlen(keyseed);
73    if (s2kparms)
74	opaque = *s2kparms;
75    else
76	krb5_data_zero(&opaque);
77
78    *key = malloc(sizeof(**key));
79    if (*key == NULL)
80	return ENOMEM;
81    ret = krb5_string_to_key_data_salt_opaque(context, type, password,
82					      salt, opaque, *key);
83    if (ret) {
84	free(*key);
85	*key = NULL;
86    }
87    return ret;
88}
89
90static void
91free_init_creds_ctx(krb5_context context, krb5_get_init_creds_ctx *ctx)
92{
93    if (ctx->etypes)
94	free(ctx->etypes);
95    if (ctx->pre_auth_types)
96	free (ctx->pre_auth_types);
97    free_AS_REQ(&ctx->as_req);
98    memset(&ctx->as_req, 0, sizeof(ctx->as_req));
99}
100
101static int
102get_config_time (krb5_context context,
103		 const char *realm,
104		 const char *name,
105		 int def)
106{
107    int ret;
108
109    ret = krb5_config_get_time (context, NULL,
110				"realms",
111				realm,
112				name,
113				NULL);
114    if (ret >= 0)
115	return ret;
116    ret = krb5_config_get_time (context, NULL,
117				"libdefaults",
118				name,
119				NULL);
120    if (ret >= 0)
121	return ret;
122    return def;
123}
124
125static krb5_error_code
126init_cred (krb5_context context,
127	   krb5_creds *cred,
128	   krb5_principal client,
129	   krb5_deltat start_time,
130	   const char *in_tkt_service,
131	   krb5_get_init_creds_opt *options)
132{
133    krb5_error_code ret;
134    krb5_const_realm client_realm;
135    int tmp;
136    krb5_timestamp now;
137
138    krb5_timeofday (context, &now);
139
140    memset (cred, 0, sizeof(*cred));
141
142    if (client)
143	krb5_copy_principal(context, client, &cred->client);
144    else {
145	ret = krb5_get_default_principal (context,
146					  &cred->client);
147	if (ret)
148	    goto out;
149    }
150
151    client_realm = krb5_principal_get_realm (context, cred->client);
152
153    if (start_time)
154	cred->times.starttime  = now + start_time;
155
156    if (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)
157	tmp = options->tkt_life;
158    else
159	tmp = 10 * 60 * 60;
160    cred->times.endtime = now + tmp;
161
162    if ((options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE) &&
163	options->renew_life > 0) {
164	cred->times.renew_till = now + options->renew_life;
165    }
166
167    if (in_tkt_service) {
168	krb5_realm server_realm;
169
170	ret = krb5_parse_name (context, in_tkt_service, &cred->server);
171	if (ret)
172	    goto out;
173	server_realm = strdup (client_realm);
174	free (*krb5_princ_realm(context, cred->server));
175	krb5_princ_set_realm (context, cred->server, &server_realm);
176    } else {
177	ret = krb5_make_principal(context, &cred->server,
178				  client_realm, KRB5_TGS_NAME, client_realm,
179				  NULL);
180	if (ret)
181	    goto out;
182    }
183    return 0;
184
185out:
186    krb5_free_cred_contents (context, cred);
187    return ret;
188}
189
190/*
191 * Print a message (str) to the user about the expiration in `lr'
192 */
193
194static void
195report_expiration (krb5_context context,
196		   krb5_prompter_fct prompter,
197		   krb5_data *data,
198		   const char *str,
199		   time_t now)
200{
201    char *p;
202
203    asprintf (&p, "%s%s", str, ctime(&now));
204    (*prompter) (context, data, NULL, p, 0, NULL);
205    free (p);
206}
207
208/*
209 * Parse the last_req data and show it to the user if it's interesting
210 */
211
212static void
213print_expire (krb5_context context,
214	      krb5_const_realm realm,
215	      krb5_kdc_rep *rep,
216	      krb5_prompter_fct prompter,
217	      krb5_data *data)
218{
219    int i;
220    LastReq *lr = &rep->enc_part.last_req;
221    krb5_timestamp sec;
222    time_t t;
223    krb5_boolean reported = FALSE;
224
225    krb5_timeofday (context, &sec);
226
227    t = sec + get_config_time (context,
228			       realm,
229			       "warn_pwexpire",
230			       7 * 24 * 60 * 60);
231
232    for (i = 0; i < lr->len; ++i) {
233	if (lr->val[i].lr_value <= t) {
234	    switch (abs(lr->val[i].lr_type)) {
235	    case LR_PW_EXPTIME :
236		report_expiration(context, prompter, data,
237				  "Your password will expire at ",
238				  lr->val[i].lr_value);
239		reported = TRUE;
240		break;
241	    case LR_ACCT_EXPTIME :
242		report_expiration(context, prompter, data,
243				  "Your account will expire at ",
244				  lr->val[i].lr_value);
245		reported = TRUE;
246		break;
247	    }
248	}
249    }
250
251    if (!reported
252	&& rep->enc_part.key_expiration
253	&& *rep->enc_part.key_expiration <= t) {
254	report_expiration(context, prompter, data,
255			  "Your password/account will expire at ",
256			  *rep->enc_part.key_expiration);
257    }
258}
259
260static krb5_addresses no_addrs = { 0, NULL };
261
262static krb5_error_code
263get_init_creds_common(krb5_context context,
264		      krb5_principal client,
265		      krb5_deltat start_time,
266		      const char *in_tkt_service,
267		      krb5_get_init_creds_opt *options,
268		      krb5_get_init_creds_ctx *ctx)
269{
270    krb5_get_init_creds_opt default_opt;
271    krb5_error_code ret;
272    krb5_enctype *etypes;
273    krb5_preauthtype *pre_auth_types;
274
275    memset(ctx, 0, sizeof(*ctx));
276
277    if (options == NULL) {
278	krb5_get_init_creds_opt_init (&default_opt);
279	options = &default_opt;
280    } else {
281	_krb5_get_init_creds_opt_free_krb5_error(options);
282    }
283
284    if (options->opt_private) {
285	ctx->password = options->opt_private->password;
286	ctx->key_proc = options->opt_private->key_proc;
287	ctx->req_pac = options->opt_private->req_pac;
288	ctx->pk_init_ctx = options->opt_private->pk_init_ctx;
289	ctx->ic_flags = options->opt_private->flags;
290    } else
291	ctx->req_pac = KRB5_INIT_CREDS_TRISTATE_UNSET;
292
293    if (ctx->key_proc == NULL)
294	ctx->key_proc = default_s2k_func;
295
296    if (ctx->ic_flags & KRB5_INIT_CREDS_CANONICALIZE)
297	ctx->flags.canonicalize = 1;
298
299    ctx->pre_auth_types = NULL;
300    ctx->addrs = NULL;
301    ctx->etypes = NULL;
302    ctx->pre_auth_types = NULL;
303    ctx->in_tkt_service = in_tkt_service;
304
305    ret = init_cred (context, &ctx->cred, client, start_time,
306		     in_tkt_service, options);
307    if (ret)
308	return ret;
309
310    if (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)
311	ctx->flags.forwardable = options->forwardable;
312
313    if (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)
314	ctx->flags.proxiable = options->proxiable;
315
316    if (start_time)
317	ctx->flags.postdated = 1;
318    if (ctx->cred.times.renew_till)
319	ctx->flags.renewable = 1;
320    if (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) {
321	ctx->addrs = options->address_list;
322    } else if (options->opt_private) {
323	switch (options->opt_private->addressless) {
324	case KRB5_INIT_CREDS_TRISTATE_UNSET:
325#if KRB5_ADDRESSLESS_DEFAULT == TRUE
326	    ctx->addrs = &no_addrs;
327#else
328	    ctx->addrs = NULL;
329#endif
330	    break;
331	case KRB5_INIT_CREDS_TRISTATE_FALSE:
332	    ctx->addrs = NULL;
333	    break;
334	case KRB5_INIT_CREDS_TRISTATE_TRUE:
335	    ctx->addrs = &no_addrs;
336	    break;
337	}
338    }
339    if (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) {
340	etypes = malloc((options->etype_list_length + 1)
341			* sizeof(krb5_enctype));
342	if (etypes == NULL) {
343	    krb5_set_error_string(context, "malloc: out of memory");
344	    return ENOMEM;
345	}
346	memcpy (etypes, options->etype_list,
347		options->etype_list_length * sizeof(krb5_enctype));
348	etypes[options->etype_list_length] = ETYPE_NULL;
349	ctx->etypes = etypes;
350    }
351    if (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) {
352	pre_auth_types = malloc((options->preauth_list_length + 1)
353				* sizeof(krb5_preauthtype));
354	if (pre_auth_types == NULL) {
355	    krb5_set_error_string(context, "malloc: out of memory");
356	    return ENOMEM;
357	}
358	memcpy (pre_auth_types, options->preauth_list,
359		options->preauth_list_length * sizeof(krb5_preauthtype));
360	pre_auth_types[options->preauth_list_length] = KRB5_PADATA_NONE;
361	ctx->pre_auth_types = pre_auth_types;
362    }
363    if (options->flags & KRB5_GET_INIT_CREDS_OPT_SALT)
364	;			/* XXX */
365    if (options->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS)
366	ctx->flags.request_anonymous = options->anonymous;
367    return 0;
368}
369
370static krb5_error_code
371change_password (krb5_context context,
372		 krb5_principal client,
373		 const char *password,
374		 char *newpw,
375		 size_t newpw_sz,
376		 krb5_prompter_fct prompter,
377		 void *data,
378		 krb5_get_init_creds_opt *old_options)
379{
380    krb5_prompt prompts[2];
381    krb5_error_code ret;
382    krb5_creds cpw_cred;
383    char buf1[BUFSIZ], buf2[BUFSIZ];
384    krb5_data password_data[2];
385    int result_code;
386    krb5_data result_code_string;
387    krb5_data result_string;
388    char *p;
389    krb5_get_init_creds_opt options;
390
391    memset (&cpw_cred, 0, sizeof(cpw_cred));
392
393    krb5_get_init_creds_opt_init (&options);
394    krb5_get_init_creds_opt_set_tkt_life (&options, 60);
395    krb5_get_init_creds_opt_set_forwardable (&options, FALSE);
396    krb5_get_init_creds_opt_set_proxiable (&options, FALSE);
397    if (old_options && old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)
398	krb5_get_init_creds_opt_set_preauth_list (&options,
399						  old_options->preauth_list,
400						  old_options->preauth_list_length);
401
402    krb5_data_zero (&result_code_string);
403    krb5_data_zero (&result_string);
404
405    ret = krb5_get_init_creds_password (context,
406					&cpw_cred,
407					client,
408					password,
409					prompter,
410					data,
411					0,
412					"kadmin/changepw",
413					&options);
414    if (ret)
415	goto out;
416
417    for(;;) {
418	password_data[0].data   = buf1;
419	password_data[0].length = sizeof(buf1);
420
421	prompts[0].hidden = 1;
422	prompts[0].prompt = "New password: ";
423	prompts[0].reply  = &password_data[0];
424	prompts[0].type   = KRB5_PROMPT_TYPE_NEW_PASSWORD;
425
426	password_data[1].data   = buf2;
427	password_data[1].length = sizeof(buf2);
428
429	prompts[1].hidden = 1;
430	prompts[1].prompt = "Repeat new password: ";
431	prompts[1].reply  = &password_data[1];
432	prompts[1].type   = KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN;
433
434	ret = (*prompter) (context, data, NULL, "Changing password",
435			   2, prompts);
436	if (ret) {
437	    memset (buf1, 0, sizeof(buf1));
438	    memset (buf2, 0, sizeof(buf2));
439	    goto out;
440	}
441
442	if (strcmp (buf1, buf2) == 0)
443	    break;
444	memset (buf1, 0, sizeof(buf1));
445	memset (buf2, 0, sizeof(buf2));
446    }
447
448    ret = krb5_change_password (context,
449				&cpw_cred,
450				buf1,
451				&result_code,
452				&result_code_string,
453				&result_string);
454    if (ret)
455	goto out;
456    asprintf (&p, "%s: %.*s\n",
457	      result_code ? "Error" : "Success",
458	      (int)result_string.length,
459	      result_string.length > 0 ? (char*)result_string.data : "");
460
461    ret = (*prompter) (context, data, NULL, p, 0, NULL);
462    free (p);
463    if (result_code == 0) {
464	strlcpy (newpw, buf1, newpw_sz);
465	ret = 0;
466    } else {
467	krb5_set_error_string (context, "failed changing password");
468	ret = ENOTTY;
469    }
470
471out:
472    memset (buf1, 0, sizeof(buf1));
473    memset (buf2, 0, sizeof(buf2));
474    krb5_data_free (&result_string);
475    krb5_data_free (&result_code_string);
476    krb5_free_cred_contents (context, &cpw_cred);
477    return ret;
478}
479
480krb5_error_code KRB5_LIB_FUNCTION
481krb5_keyblock_key_proc (krb5_context context,
482			krb5_keytype type,
483			krb5_data *salt,
484			krb5_const_pointer keyseed,
485			krb5_keyblock **key)
486{
487    return krb5_copy_keyblock (context, keyseed, key);
488}
489
490krb5_error_code KRB5_LIB_FUNCTION
491krb5_get_init_creds_keytab(krb5_context context,
492			   krb5_creds *creds,
493			   krb5_principal client,
494			   krb5_keytab keytab,
495			   krb5_deltat start_time,
496			   const char *in_tkt_service,
497			   krb5_get_init_creds_opt *options)
498{
499    krb5_get_init_creds_ctx ctx;
500    krb5_error_code ret;
501    krb5_keytab_key_proc_args *a;
502
503    ret = get_init_creds_common(context, client, start_time,
504				in_tkt_service, options, &ctx);
505    if (ret)
506	goto out;
507
508    a = malloc (sizeof(*a));
509    if (a == NULL) {
510	krb5_set_error_string(context, "malloc: out of memory");
511	ret = ENOMEM;
512	goto out;
513    }
514    a->principal = ctx.cred.client;
515    a->keytab    = keytab;
516
517    ret = krb5_get_in_cred (context,
518			    KDCOptions2int(ctx.flags),
519			    ctx.addrs,
520			    ctx.etypes,
521			    ctx.pre_auth_types,
522			    NULL,
523			    krb5_keytab_key_proc,
524			    a,
525			    NULL,
526			    NULL,
527			    &ctx.cred,
528			    NULL);
529    free (a);
530
531    if (ret == 0 && creds)
532	*creds = ctx.cred;
533    else
534	krb5_free_cred_contents (context, &ctx.cred);
535
536 out:
537    free_init_creds_ctx(context, &ctx);
538    return ret;
539}
540
541/*
542 *
543 */
544
545static krb5_error_code
546init_creds_init_as_req (krb5_context context,
547			KDCOptions opts,
548			const krb5_creds *creds,
549			const krb5_addresses *addrs,
550			const krb5_enctype *etypes,
551			AS_REQ *a)
552{
553    krb5_error_code ret;
554
555    memset(a, 0, sizeof(*a));
556
557    a->pvno = 5;
558    a->msg_type = krb_as_req;
559    a->req_body.kdc_options = opts;
560    a->req_body.cname = malloc(sizeof(*a->req_body.cname));
561    if (a->req_body.cname == NULL) {
562	ret = ENOMEM;
563	krb5_set_error_string(context, "malloc: out of memory");
564	goto fail;
565    }
566    a->req_body.sname = malloc(sizeof(*a->req_body.sname));
567    if (a->req_body.sname == NULL) {
568	ret = ENOMEM;
569	krb5_set_error_string(context, "malloc: out of memory");
570	goto fail;
571    }
572
573    ret = _krb5_principal2principalname (a->req_body.cname, creds->client);
574    if (ret)
575	goto fail;
576    ret = copy_Realm(&creds->client->realm, &a->req_body.realm);
577    if (ret)
578	goto fail;
579
580    ret = _krb5_principal2principalname (a->req_body.sname, creds->server);
581    if (ret)
582	goto fail;
583
584    if(creds->times.starttime) {
585	a->req_body.from = malloc(sizeof(*a->req_body.from));
586	if (a->req_body.from == NULL) {
587	    ret = ENOMEM;
588	    krb5_set_error_string(context, "malloc: out of memory");
589	    goto fail;
590	}
591	*a->req_body.from = creds->times.starttime;
592    }
593    if(creds->times.endtime){
594	ALLOC(a->req_body.till, 1);
595	*a->req_body.till = creds->times.endtime;
596    }
597    if(creds->times.renew_till){
598	a->req_body.rtime = malloc(sizeof(*a->req_body.rtime));
599	if (a->req_body.rtime == NULL) {
600	    ret = ENOMEM;
601	    krb5_set_error_string(context, "malloc: out of memory");
602	    goto fail;
603	}
604	*a->req_body.rtime = creds->times.renew_till;
605    }
606    a->req_body.nonce = 0;
607    ret = krb5_init_etype (context,
608			   &a->req_body.etype.len,
609			   &a->req_body.etype.val,
610			   etypes);
611    if (ret)
612	goto fail;
613
614    /*
615     * This means no addresses
616     */
617
618    if (addrs && addrs->len == 0) {
619	a->req_body.addresses = NULL;
620    } else {
621	a->req_body.addresses = malloc(sizeof(*a->req_body.addresses));
622	if (a->req_body.addresses == NULL) {
623	    ret = ENOMEM;
624	    krb5_set_error_string(context, "malloc: out of memory");
625	    goto fail;
626	}
627
628	if (addrs)
629	    ret = krb5_copy_addresses(context, addrs, a->req_body.addresses);
630	else {
631	    ret = krb5_get_all_client_addrs (context, a->req_body.addresses);
632	    if(ret == 0 && a->req_body.addresses->len == 0) {
633		free(a->req_body.addresses);
634		a->req_body.addresses = NULL;
635	    }
636	}
637	if (ret)
638	    goto fail;
639    }
640
641    a->req_body.enc_authorization_data = NULL;
642    a->req_body.additional_tickets = NULL;
643
644    a->padata = NULL;
645
646    return 0;
647 fail:
648    free_AS_REQ(a);
649    memset(a, 0, sizeof(*a));
650    return ret;
651}
652
653struct pa_info_data {
654    krb5_enctype etype;
655    krb5_salt salt;
656    krb5_data *s2kparams;
657};
658
659static void
660free_paid(krb5_context context, struct pa_info_data *ppaid)
661{
662    krb5_free_salt(context, ppaid->salt);
663    if (ppaid->s2kparams)
664	krb5_free_data(context, ppaid->s2kparams);
665}
666
667
668static krb5_error_code
669set_paid(struct pa_info_data *paid, krb5_context context,
670	 krb5_enctype etype,
671	 krb5_salttype salttype, void *salt_string, size_t salt_len,
672	 krb5_data *s2kparams)
673{
674    paid->etype = etype;
675    paid->salt.salttype = salttype;
676    paid->salt.saltvalue.data = malloc(salt_len + 1);
677    if (paid->salt.saltvalue.data == NULL) {
678	krb5_clear_error_string(context);
679	return ENOMEM;
680    }
681    memcpy(paid->salt.saltvalue.data, salt_string, salt_len);
682    ((char *)paid->salt.saltvalue.data)[salt_len] = '\0';
683    paid->salt.saltvalue.length = salt_len;
684    if (s2kparams) {
685	krb5_error_code ret;
686
687	ret = krb5_copy_data(context, s2kparams, &paid->s2kparams);
688	if (ret) {
689	    krb5_clear_error_string(context);
690	    krb5_free_salt(context, paid->salt);
691	    return ret;
692	}
693    } else
694	paid->s2kparams = NULL;
695
696    return 0;
697}
698
699static struct pa_info_data *
700pa_etype_info2(krb5_context context,
701	       const krb5_principal client,
702	       const AS_REQ *asreq,
703	       struct pa_info_data *paid,
704	       heim_octet_string *data)
705{
706    krb5_error_code ret;
707    ETYPE_INFO2 e;
708    size_t sz;
709    int i, j;
710
711    memset(&e, 0, sizeof(e));
712    ret = decode_ETYPE_INFO2(data->data, data->length, &e, &sz);
713    if (ret)
714	goto out;
715    if (e.len == 0)
716	goto out;
717    for (j = 0; j < asreq->req_body.etype.len; j++) {
718	for (i = 0; i < e.len; i++) {
719	    if (asreq->req_body.etype.val[j] == e.val[i].etype) {
720		krb5_salt salt;
721		if (e.val[i].salt == NULL)
722		    ret = krb5_get_pw_salt(context, client, &salt);
723		else {
724		    salt.saltvalue.data = *e.val[i].salt;
725		    salt.saltvalue.length = strlen(*e.val[i].salt);
726		    ret = 0;
727		}
728		if (ret == 0)
729		    ret = set_paid(paid, context, e.val[i].etype,
730				   KRB5_PW_SALT,
731				   salt.saltvalue.data,
732				   salt.saltvalue.length,
733				   e.val[i].s2kparams);
734		if (e.val[i].salt == NULL)
735		    krb5_free_salt(context, salt);
736		if (ret == 0) {
737		    free_ETYPE_INFO2(&e);
738		    return paid;
739		}
740	    }
741	}
742    }
743 out:
744    free_ETYPE_INFO2(&e);
745    return NULL;
746}
747
748static struct pa_info_data *
749pa_etype_info(krb5_context context,
750	      const krb5_principal client,
751	      const AS_REQ *asreq,
752	      struct pa_info_data *paid,
753	      heim_octet_string *data)
754{
755    krb5_error_code ret;
756    ETYPE_INFO e;
757    size_t sz;
758    int i, j;
759
760    memset(&e, 0, sizeof(e));
761    ret = decode_ETYPE_INFO(data->data, data->length, &e, &sz);
762    if (ret)
763	goto out;
764    if (e.len == 0)
765	goto out;
766    for (j = 0; j < asreq->req_body.etype.len; j++) {
767	for (i = 0; i < e.len; i++) {
768	    if (asreq->req_body.etype.val[j] == e.val[i].etype) {
769		krb5_salt salt;
770		salt.salttype = KRB5_PW_SALT;
771		if (e.val[i].salt == NULL)
772		    ret = krb5_get_pw_salt(context, client, &salt);
773		else {
774		    salt.saltvalue = *e.val[i].salt;
775		    ret = 0;
776		}
777		if (e.val[i].salttype)
778		    salt.salttype = *e.val[i].salttype;
779		if (ret == 0) {
780		    ret = set_paid(paid, context, e.val[i].etype,
781				   salt.salttype,
782				   salt.saltvalue.data,
783				   salt.saltvalue.length,
784				   NULL);
785		    if (e.val[i].salt == NULL)
786			krb5_free_salt(context, salt);
787		}
788		if (ret == 0) {
789		    free_ETYPE_INFO(&e);
790		    return paid;
791		}
792	    }
793	}
794    }
795 out:
796    free_ETYPE_INFO(&e);
797    return NULL;
798}
799
800static struct pa_info_data *
801pa_pw_or_afs3_salt(krb5_context context,
802		   const krb5_principal client,
803		   const AS_REQ *asreq,
804		   struct pa_info_data *paid,
805		   heim_octet_string *data)
806{
807    krb5_error_code ret;
808    if (paid->etype == ENCTYPE_NULL)
809	return NULL;
810    ret = set_paid(paid, context,
811		   paid->etype,
812		   paid->salt.salttype,
813		   data->data,
814		   data->length,
815		   NULL);
816    if (ret)
817	return NULL;
818    return paid;
819}
820
821
822struct pa_info {
823    krb5_preauthtype type;
824    struct pa_info_data *(*salt_info)(krb5_context,
825				      const krb5_principal,
826				      const AS_REQ *,
827				      struct pa_info_data *,
828				      heim_octet_string *);
829};
830
831static struct pa_info pa_prefs[] = {
832    { KRB5_PADATA_ETYPE_INFO2, pa_etype_info2 },
833    { KRB5_PADATA_ETYPE_INFO, pa_etype_info },
834    { KRB5_PADATA_PW_SALT, pa_pw_or_afs3_salt },
835    { KRB5_PADATA_AFS3_SALT, pa_pw_or_afs3_salt }
836};
837
838static PA_DATA *
839find_pa_data(const METHOD_DATA *md, int type)
840{
841    int i;
842    if (md == NULL)
843	return NULL;
844    for (i = 0; i < md->len; i++)
845	if (md->val[i].padata_type == type)
846	    return &md->val[i];
847    return NULL;
848}
849
850static struct pa_info_data *
851process_pa_info(krb5_context context,
852		const krb5_principal client,
853		const AS_REQ *asreq,
854		struct pa_info_data *paid,
855		METHOD_DATA *md)
856{
857    struct pa_info_data *p = NULL;
858    int i;
859
860    for (i = 0; p == NULL && i < sizeof(pa_prefs)/sizeof(pa_prefs[0]); i++) {
861	PA_DATA *pa = find_pa_data(md, pa_prefs[i].type);
862	if (pa == NULL)
863	    continue;
864	paid->salt.salttype = pa_prefs[i].type;
865	p = (*pa_prefs[i].salt_info)(context, client, asreq,
866				     paid, &pa->padata_value);
867    }
868    return p;
869}
870
871static krb5_error_code
872make_pa_enc_timestamp(krb5_context context, METHOD_DATA *md,
873		      krb5_enctype etype, krb5_keyblock *key)
874{
875    PA_ENC_TS_ENC p;
876    unsigned char *buf;
877    size_t buf_size;
878    size_t len;
879    EncryptedData encdata;
880    krb5_error_code ret;
881    int32_t usec;
882    int usec2;
883    krb5_crypto crypto;
884
885    krb5_us_timeofday (context, &p.patimestamp, &usec);
886    usec2         = usec;
887    p.pausec      = &usec2;
888
889    ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret);
890    if (ret)
891	return ret;
892    if(buf_size != len)
893	krb5_abortx(context, "internal error in ASN.1 encoder");
894
895    ret = krb5_crypto_init(context, key, 0, &crypto);
896    if (ret) {
897	free(buf);
898	return ret;
899    }
900    ret = krb5_encrypt_EncryptedData(context,
901				     crypto,
902				     KRB5_KU_PA_ENC_TIMESTAMP,
903				     buf,
904				     len,
905				     0,
906				     &encdata);
907    free(buf);
908    krb5_crypto_destroy(context, crypto);
909    if (ret)
910	return ret;
911
912    ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret);
913    free_EncryptedData(&encdata);
914    if (ret)
915	return ret;
916    if(buf_size != len)
917	krb5_abortx(context, "internal error in ASN.1 encoder");
918
919    ret = krb5_padata_add(context, md, KRB5_PADATA_ENC_TIMESTAMP, buf, len);
920    if (ret)
921	free(buf);
922    return ret;
923}
924
925static krb5_error_code
926add_enc_ts_padata(krb5_context context,
927		  METHOD_DATA *md,
928		  krb5_principal client,
929		  krb5_s2k_proc key_proc,
930		  krb5_const_pointer keyseed,
931		  krb5_enctype *enctypes,
932		  unsigned netypes,
933		  krb5_salt *salt,
934		  krb5_data *s2kparams)
935{
936    krb5_error_code ret;
937    krb5_salt salt2;
938    krb5_enctype *ep;
939    int i;
940
941    if(salt == NULL) {
942	/* default to standard salt */
943	ret = krb5_get_pw_salt (context, client, &salt2);
944	salt = &salt2;
945    }
946    if (!enctypes) {
947	enctypes = context->etypes;
948	netypes = 0;
949	for (ep = enctypes; *ep != ETYPE_NULL; ep++)
950	    netypes++;
951    }
952
953    for (i = 0; i < netypes; ++i) {
954	krb5_keyblock *key;
955
956	ret = (*key_proc)(context, enctypes[i], keyseed,
957			  *salt, s2kparams, &key);
958	if (ret)
959	    continue;
960	ret = make_pa_enc_timestamp (context, md, enctypes[i], key);
961	krb5_free_keyblock (context, key);
962	if (ret)
963	    return ret;
964    }
965    if(salt == &salt2)
966	krb5_free_salt(context, salt2);
967    return 0;
968}
969
970static krb5_error_code
971pa_data_to_md_ts_enc(krb5_context context,
972		     const AS_REQ *a,
973		     const krb5_principal client,
974		     krb5_get_init_creds_ctx *ctx,
975		     struct pa_info_data *ppaid,
976		     METHOD_DATA *md)
977{
978    if (ctx->key_proc == NULL || ctx->password == NULL)
979	return 0;
980
981    if (ppaid) {
982	add_enc_ts_padata(context, md, client,
983			  ctx->key_proc, ctx->password,
984			  &ppaid->etype, 1,
985			  &ppaid->salt, ppaid->s2kparams);
986    } else {
987	krb5_salt salt;
988
989	/* make a v5 salted pa-data */
990	add_enc_ts_padata(context, md, client,
991			  ctx->key_proc, ctx->password,
992			  a->req_body.etype.val, a->req_body.etype.len,
993			  NULL, NULL);
994
995	/* make a v4 salted pa-data */
996	salt.salttype = KRB5_PW_SALT;
997	krb5_data_zero(&salt.saltvalue);
998	add_enc_ts_padata(context, md, client,
999			  ctx->key_proc, ctx->password,
1000			  a->req_body.etype.val, a->req_body.etype.len,
1001			  &salt, NULL);
1002    }
1003    return 0;
1004}
1005
1006static krb5_error_code
1007pa_data_to_key_plain(krb5_context context,
1008		     const krb5_principal client,
1009		     krb5_get_init_creds_ctx *ctx,
1010		     krb5_salt salt,
1011		     krb5_data *s2kparams,
1012		     krb5_enctype etype,
1013		     krb5_keyblock **key)
1014{
1015    krb5_error_code ret;
1016
1017    ret = (*ctx->key_proc)(context, etype, ctx->password,
1018			   salt, s2kparams, key);
1019    return ret;
1020}
1021
1022
1023static krb5_error_code
1024pa_data_to_md_pkinit(krb5_context context,
1025		     const AS_REQ *a,
1026		     const krb5_principal client,
1027		     krb5_get_init_creds_ctx *ctx,
1028		     METHOD_DATA *md)
1029{
1030    if (ctx->pk_init_ctx == NULL)
1031	return 0;
1032#ifdef PKINIT
1033    return _krb5_pk_mk_padata(context,
1034			     ctx->pk_init_ctx,
1035			     &a->req_body,
1036			     ctx->pk_nonce,
1037			     md);
1038#else
1039    krb5_set_error_string(context, "no support for PKINIT compiled in");
1040    return EINVAL;
1041#endif
1042}
1043
1044static krb5_error_code
1045pa_data_add_pac_request(krb5_context context,
1046			krb5_get_init_creds_ctx *ctx,
1047			METHOD_DATA *md)
1048{
1049    size_t len, length;
1050    krb5_error_code ret;
1051    PA_PAC_REQUEST req;
1052    void *buf;
1053
1054    switch (ctx->req_pac) {
1055    case KRB5_INIT_CREDS_TRISTATE_UNSET:
1056	return 0; /* don't bother */
1057    case KRB5_INIT_CREDS_TRISTATE_TRUE:
1058	req.include_pac = 1;
1059	break;
1060    case KRB5_INIT_CREDS_TRISTATE_FALSE:
1061	req.include_pac = 0;
1062    }
1063
1064    ASN1_MALLOC_ENCODE(PA_PAC_REQUEST, buf, length,
1065		       &req, &len, ret);
1066    if (ret)
1067	return ret;
1068    if(len != length)
1069	krb5_abortx(context, "internal error in ASN.1 encoder");
1070
1071    ret = krb5_padata_add(context, md, KRB5_PADATA_PA_PAC_REQUEST, buf, len);
1072    if (ret)
1073	free(buf);
1074
1075    return 0;
1076}
1077
1078/*
1079 * Assumes caller always will free `out_md', even on error.
1080 */
1081
1082static krb5_error_code
1083process_pa_data_to_md(krb5_context context,
1084		      const krb5_creds *creds,
1085		      const AS_REQ *a,
1086		      krb5_get_init_creds_ctx *ctx,
1087		      METHOD_DATA *in_md,
1088		      METHOD_DATA **out_md,
1089		      krb5_prompter_fct prompter,
1090		      void *prompter_data)
1091{
1092    krb5_error_code ret;
1093
1094    ALLOC(*out_md, 1);
1095    if (*out_md == NULL) {
1096	krb5_set_error_string(context, "malloc: out of memory");
1097	return ENOMEM;
1098    }
1099    (*out_md)->len = 0;
1100    (*out_md)->val = NULL;
1101
1102    /*
1103     * Make sure we don't sent both ENC-TS and PK-INIT pa data, no
1104     * need to expose our password protecting our PKCS12 key.
1105     */
1106
1107    if (ctx->pk_init_ctx) {
1108
1109	ret = pa_data_to_md_pkinit(context, a, creds->client, ctx, *out_md);
1110	if (ret)
1111	    return ret;
1112
1113    } else if (in_md->len != 0) {
1114	struct pa_info_data paid, *ppaid;
1115
1116	memset(&paid, 0, sizeof(paid));
1117
1118	paid.etype = ENCTYPE_NULL;
1119	ppaid = process_pa_info(context, creds->client, a, &paid, in_md);
1120
1121	pa_data_to_md_ts_enc(context, a, creds->client, ctx, ppaid, *out_md);
1122	if (ppaid)
1123	    free_paid(context, ppaid);
1124    }
1125
1126    pa_data_add_pac_request(context, ctx, *out_md);
1127
1128    if ((*out_md)->len == 0) {
1129	free(*out_md);
1130	*out_md = NULL;
1131    }
1132
1133    return 0;
1134}
1135
1136static krb5_error_code
1137process_pa_data_to_key(krb5_context context,
1138		       krb5_get_init_creds_ctx *ctx,
1139		       krb5_creds *creds,
1140		       AS_REQ *a,
1141		       krb5_kdc_rep *rep,
1142		       const krb5_krbhst_info *hi,
1143		       krb5_keyblock **key)
1144{
1145    struct pa_info_data paid, *ppaid = NULL;
1146    krb5_error_code ret;
1147    krb5_enctype etype;
1148    PA_DATA *pa;
1149
1150    memset(&paid, 0, sizeof(paid));
1151
1152    etype = rep->kdc_rep.enc_part.etype;
1153
1154    if (rep->kdc_rep.padata) {
1155	paid.etype = etype;
1156	ppaid = process_pa_info(context, creds->client, a, &paid,
1157				rep->kdc_rep.padata);
1158    }
1159    if (ppaid == NULL) {
1160	ret = krb5_get_pw_salt (context, creds->client, &paid.salt);
1161	if (ret)
1162	    return ret;
1163	paid.etype = etype;
1164	paid.s2kparams = NULL;
1165    }
1166
1167    pa = NULL;
1168    if (rep->kdc_rep.padata) {
1169	int idx = 0;
1170	pa = krb5_find_padata(rep->kdc_rep.padata->val,
1171			      rep->kdc_rep.padata->len,
1172			      KRB5_PADATA_PK_AS_REP,
1173			      &idx);
1174	if (pa == NULL) {
1175	    idx = 0;
1176	    pa = krb5_find_padata(rep->kdc_rep.padata->val,
1177				  rep->kdc_rep.padata->len,
1178				  KRB5_PADATA_PK_AS_REP_19,
1179				  &idx);
1180	}
1181    }
1182    if (pa && ctx->pk_init_ctx) {
1183#ifdef PKINIT
1184	ret = _krb5_pk_rd_pa_reply(context,
1185				   a->req_body.realm,
1186				   ctx->pk_init_ctx,
1187				   etype,
1188				   hi,
1189				   ctx->pk_nonce,
1190				   &ctx->req_buffer,
1191				   pa,
1192				   key);
1193#else
1194	krb5_set_error_string(context, "no support for PKINIT compiled in");
1195	ret = EINVAL;
1196#endif
1197    } else if (ctx->password)
1198	ret = pa_data_to_key_plain(context, creds->client, ctx,
1199				   paid.salt, paid.s2kparams, etype, key);
1200    else {
1201	krb5_set_error_string(context, "No usable pa data type");
1202	ret = EINVAL;
1203    }
1204
1205    free_paid(context, &paid);
1206    return ret;
1207}
1208
1209static krb5_error_code
1210init_cred_loop(krb5_context context,
1211	       krb5_get_init_creds_opt *init_cred_opts,
1212	       const krb5_prompter_fct prompter,
1213	       void *prompter_data,
1214	       krb5_get_init_creds_ctx *ctx,
1215	       krb5_creds *creds,
1216	       krb5_kdc_rep *ret_as_reply)
1217{
1218    krb5_error_code ret;
1219    krb5_kdc_rep rep;
1220    METHOD_DATA md;
1221    krb5_data resp;
1222    size_t len;
1223    size_t size;
1224    krb5_krbhst_info *hi = NULL;
1225    krb5_sendto_ctx stctx = NULL;
1226
1227
1228    memset(&md, 0, sizeof(md));
1229    memset(&rep, 0, sizeof(rep));
1230
1231    _krb5_get_init_creds_opt_free_krb5_error(init_cred_opts);
1232
1233    if (ret_as_reply)
1234	memset(ret_as_reply, 0, sizeof(*ret_as_reply));
1235
1236    ret = init_creds_init_as_req(context, ctx->flags, creds,
1237				 ctx->addrs, ctx->etypes, &ctx->as_req);
1238    if (ret)
1239	return ret;
1240
1241    ret = krb5_sendto_ctx_alloc(context, &stctx);
1242    if (ret)
1243	goto out;
1244    krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
1245
1246    /* Set a new nonce. */
1247    krb5_generate_random_block (&ctx->nonce, sizeof(ctx->nonce));
1248    ctx->nonce &= 0xffffffff;
1249    /* XXX these just needs to be the same when using Windows PK-INIT */
1250    ctx->pk_nonce = ctx->nonce;
1251
1252    /*
1253     * Increase counter when we want other pre-auth types then
1254     * KRB5_PA_ENC_TIMESTAMP.
1255     */
1256#define MAX_PA_COUNTER 3
1257
1258    ctx->pa_counter = 0;
1259    while (ctx->pa_counter < MAX_PA_COUNTER) {
1260
1261	ctx->pa_counter++;
1262
1263	if (ctx->as_req.padata) {
1264	    free_METHOD_DATA(ctx->as_req.padata);
1265	    free(ctx->as_req.padata);
1266	    ctx->as_req.padata = NULL;
1267	}
1268
1269	/* Set a new nonce. */
1270	ctx->as_req.req_body.nonce = ctx->nonce;
1271
1272	/* fill_in_md_data */
1273	ret = process_pa_data_to_md(context, creds, &ctx->as_req, ctx,
1274				    &md, &ctx->as_req.padata,
1275				    prompter, prompter_data);
1276	if (ret)
1277	    goto out;
1278
1279	krb5_data_free(&ctx->req_buffer);
1280
1281	ASN1_MALLOC_ENCODE(AS_REQ,
1282			   ctx->req_buffer.data, ctx->req_buffer.length,
1283			   &ctx->as_req, &len, ret);
1284	if (ret)
1285	    goto out;
1286	if(len != ctx->req_buffer.length)
1287	    krb5_abortx(context, "internal error in ASN.1 encoder");
1288
1289	ret = krb5_sendto_context (context, stctx, &ctx->req_buffer,
1290				   creds->client->realm, &resp);
1291    	if (ret)
1292	    goto out;
1293
1294	memset (&rep, 0, sizeof(rep));
1295	ret = decode_AS_REP(resp.data, resp.length, &rep.kdc_rep, &size);
1296	if (ret == 0) {
1297	    krb5_data_free(&resp);
1298	    krb5_clear_error_string(context);
1299	    break;
1300	} else {
1301	    /* let's try to parse it as a KRB-ERROR */
1302	    KRB_ERROR error;
1303
1304	    ret = krb5_rd_error(context, &resp, &error);
1305	    if(ret && resp.data && ((char*)resp.data)[0] == 4)
1306		ret = KRB5KRB_AP_ERR_V4_REPLY;
1307	    krb5_data_free(&resp);
1308	    if (ret)
1309		goto out;
1310
1311	    ret = krb5_error_from_rd_error(context, &error, creds);
1312
1313	    /*
1314	     * If no preauth was set and KDC requires it, give it one
1315	     * more try.
1316	     */
1317
1318	    if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED) {
1319		free_METHOD_DATA(&md);
1320		memset(&md, 0, sizeof(md));
1321
1322		if (error.e_data) {
1323		    ret = decode_METHOD_DATA(error.e_data->data,
1324					     error.e_data->length,
1325					     &md,
1326					     NULL);
1327		    if (ret)
1328			krb5_set_error_string(context,
1329					      "failed to decode METHOD DATA");
1330		} else {
1331		    /* XXX guess what the server want here add add md */
1332		}
1333		krb5_free_error_contents(context, &error);
1334		if (ret)
1335		    goto out;
1336	    } else {
1337		_krb5_get_init_creds_opt_set_krb5_error(context,
1338							init_cred_opts,
1339							&error);
1340		if (ret_as_reply)
1341		    rep.error = error;
1342		else
1343		    krb5_free_error_contents(context, &error);
1344		goto out;
1345	    }
1346	}
1347    }
1348
1349    {
1350	krb5_keyblock *key = NULL;
1351	unsigned flags = 0;
1352
1353	if (ctx->flags.request_anonymous)
1354	    flags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH;
1355	if (ctx->flags.canonicalize) {
1356	    flags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
1357	    flags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH;
1358	    flags |= EXTRACT_TICKET_MATCH_REALM;
1359	}
1360
1361	ret = process_pa_data_to_key(context, ctx, creds,
1362				     &ctx->as_req, &rep, hi, &key);
1363	if (ret)
1364	    goto out;
1365
1366	ret = _krb5_extract_ticket(context,
1367				   &rep,
1368				   creds,
1369				   key,
1370				   NULL,
1371				   KRB5_KU_AS_REP_ENC_PART,
1372				   NULL,
1373				   ctx->nonce,
1374				   flags,
1375				   NULL,
1376				   NULL);
1377	krb5_free_keyblock(context, key);
1378    }
1379    /*
1380     * Verify referral data
1381     */
1382    if ((ctx->ic_flags & KRB5_INIT_CREDS_CANONICALIZE) &&
1383	(ctx->ic_flags & KRB5_INIT_CREDS_NO_C_CANON_CHECK) == 0)
1384    {
1385	PA_ClientCanonicalized canon;
1386	krb5_crypto crypto;
1387	krb5_data data;
1388	PA_DATA *pa;
1389	size_t len;
1390
1391	pa = find_pa_data(rep.kdc_rep.padata, KRB5_PADATA_CLIENT_CANONICALIZED);
1392	if (pa == NULL) {
1393	    ret = EINVAL;
1394	    krb5_set_error_string(context, "Client canonicalizion not signed");
1395	    goto out;
1396	}
1397
1398	ret = decode_PA_ClientCanonicalized(pa->padata_value.data,
1399					    pa->padata_value.length,
1400					    &canon, &len);
1401	if (ret) {
1402	    krb5_set_error_string(context, "Failed to decode "
1403				  "PA_ClientCanonicalized");
1404	    goto out;
1405	}
1406
1407	ASN1_MALLOC_ENCODE(PA_ClientCanonicalizedNames, data.data, data.length,
1408			   &canon.names, &len, ret);
1409	if (ret)
1410	    goto out;
1411	if (data.length != len)
1412	    krb5_abortx(context, "internal asn.1 error");
1413
1414	ret = krb5_crypto_init(context, &creds->session, 0, &crypto);
1415	if (ret) {
1416	    free(data.data);
1417	    free_PA_ClientCanonicalized(&canon);
1418	    goto out;
1419	}
1420
1421	ret = krb5_verify_checksum(context, crypto, KRB5_KU_CANONICALIZED_NAMES,
1422				   data.data, data.length,
1423				   &canon.canon_checksum);
1424	krb5_crypto_destroy(context, crypto);
1425	free(data.data);
1426	free_PA_ClientCanonicalized(&canon);
1427	if (ret) {
1428	    krb5_set_error_string(context, "Failed to verify "
1429				  "client canonicalized data");
1430	    goto out;
1431	}
1432    }
1433out:
1434    if (stctx)
1435	krb5_sendto_ctx_free(context, stctx);
1436    krb5_data_free(&ctx->req_buffer);
1437    free_METHOD_DATA(&md);
1438    memset(&md, 0, sizeof(md));
1439
1440    if (ret == 0 && ret_as_reply)
1441	*ret_as_reply = rep;
1442    else
1443	krb5_free_kdc_rep (context, &rep);
1444    return ret;
1445}
1446
1447krb5_error_code KRB5_LIB_FUNCTION
1448krb5_get_init_creds(krb5_context context,
1449		    krb5_creds *creds,
1450		    krb5_principal client,
1451		    krb5_prompter_fct prompter,
1452		    void *data,
1453		    krb5_deltat start_time,
1454		    const char *in_tkt_service,
1455		    krb5_get_init_creds_opt *options)
1456{
1457    krb5_get_init_creds_ctx ctx;
1458    krb5_kdc_rep kdc_reply;
1459    krb5_error_code ret;
1460    char buf[BUFSIZ];
1461    int done;
1462
1463    memset(&kdc_reply, 0, sizeof(kdc_reply));
1464
1465    ret = get_init_creds_common(context, client, start_time,
1466				in_tkt_service, options, &ctx);
1467    if (ret)
1468	goto out;
1469
1470    done = 0;
1471    while(!done) {
1472	memset(&kdc_reply, 0, sizeof(kdc_reply));
1473
1474	ret = init_cred_loop(context,
1475			     options,
1476			     prompter,
1477			     data,
1478			     &ctx,
1479			     &ctx.cred,
1480			     &kdc_reply);
1481
1482	switch (ret) {
1483	case 0 :
1484	    done = 1;
1485	    break;
1486	case KRB5KDC_ERR_KEY_EXPIRED :
1487	    /* try to avoid recursion */
1488
1489	    /* don't try to change password where then where none */
1490	    if (prompter == NULL || ctx.password == NULL)
1491		goto out;
1492
1493	    krb5_clear_error_string (context);
1494
1495	    if (ctx.in_tkt_service != NULL
1496		&& strcmp (ctx.in_tkt_service, "kadmin/changepw") == 0)
1497		goto out;
1498
1499	    ret = change_password (context,
1500				   client,
1501				   ctx.password,
1502				   buf,
1503				   sizeof(buf),
1504				   prompter,
1505				   data,
1506				   options);
1507	    if (ret)
1508		goto out;
1509	    ctx.password = buf;
1510	    break;
1511	default:
1512	    goto out;
1513	}
1514    }
1515
1516    if (prompter)
1517	print_expire (context,
1518		      krb5_principal_get_realm (context, ctx.cred.client),
1519		      &kdc_reply,
1520		      prompter,
1521		      data);
1522
1523 out:
1524    memset (buf, 0, sizeof(buf));
1525    free_init_creds_ctx(context, &ctx);
1526    krb5_free_kdc_rep (context, &kdc_reply);
1527    if (ret == 0)
1528	*creds = ctx.cred;
1529    else
1530	krb5_free_cred_contents (context, &ctx.cred);
1531
1532    return ret;
1533}
1534
1535krb5_error_code KRB5_LIB_FUNCTION
1536krb5_get_init_creds_password(krb5_context context,
1537			     krb5_creds *creds,
1538			     krb5_principal client,
1539			     const char *password,
1540			     krb5_prompter_fct prompter,
1541			     void *data,
1542			     krb5_deltat start_time,
1543			     const char *in_tkt_service,
1544			     krb5_get_init_creds_opt *in_options)
1545{
1546    krb5_get_init_creds_opt *options;
1547    char buf[BUFSIZ];
1548    krb5_error_code ret;
1549
1550    if (in_options == NULL) {
1551	const char *realm = krb5_principal_get_realm(context, client);
1552	ret = krb5_get_init_creds_opt_alloc(context, &options);
1553	if (ret == 0)
1554	    krb5_get_init_creds_opt_set_default_flags(context,
1555						      NULL,
1556						      realm,
1557						      options);
1558    } else
1559	ret = _krb5_get_init_creds_opt_copy(context, in_options, &options);
1560    if (ret)
1561	return ret;
1562
1563    if (password == NULL &&
1564	options->opt_private->password == NULL &&
1565	options->opt_private->pk_init_ctx == NULL)
1566    {
1567	krb5_prompt prompt;
1568	krb5_data password_data;
1569	char *p, *q;
1570
1571	krb5_unparse_name (context, client, &p);
1572	asprintf (&q, "%s's Password: ", p);
1573	free (p);
1574	prompt.prompt = q;
1575	password_data.data   = buf;
1576	password_data.length = sizeof(buf);
1577	prompt.hidden = 1;
1578	prompt.reply  = &password_data;
1579	prompt.type   = KRB5_PROMPT_TYPE_PASSWORD;
1580
1581	ret = (*prompter) (context, data, NULL, NULL, 1, &prompt);
1582	free (q);
1583	if (ret) {
1584	    memset (buf, 0, sizeof(buf));
1585	    krb5_get_init_creds_opt_free(context, options);
1586	    ret = KRB5_LIBOS_PWDINTR;
1587	    krb5_clear_error_string (context);
1588	    return ret;
1589	}
1590	password = password_data.data;
1591    }
1592
1593    if (options->opt_private->password == NULL) {
1594	ret = krb5_get_init_creds_opt_set_pa_password(context, options,
1595						      password, NULL);
1596	if (ret) {
1597	    krb5_get_init_creds_opt_free(context, options);
1598	    memset(buf, 0, sizeof(buf));
1599	    return ret;
1600	}
1601    }
1602
1603    ret = krb5_get_init_creds(context, creds, client, prompter,
1604			      data, start_time, in_tkt_service, options);
1605    krb5_get_init_creds_opt_free(context, options);
1606    memset(buf, 0, sizeof(buf));
1607    return ret;
1608}
1609
1610static krb5_error_code
1611init_creds_keyblock_key_proc (krb5_context context,
1612			      krb5_enctype type,
1613			      krb5_salt salt,
1614			      krb5_const_pointer keyseed,
1615			      krb5_keyblock **key)
1616{
1617    return krb5_copy_keyblock (context, keyseed, key);
1618}
1619
1620krb5_error_code KRB5_LIB_FUNCTION
1621krb5_get_init_creds_keyblock(krb5_context context,
1622			     krb5_creds *creds,
1623			     krb5_principal client,
1624			     krb5_keyblock *keyblock,
1625			     krb5_deltat start_time,
1626			     const char *in_tkt_service,
1627			     krb5_get_init_creds_opt *options)
1628{
1629    struct krb5_get_init_creds_ctx ctx;
1630    krb5_error_code ret;
1631
1632    ret = get_init_creds_common(context, client, start_time,
1633				in_tkt_service, options, &ctx);
1634    if (ret)
1635	goto out;
1636
1637    ret = krb5_get_in_cred (context,
1638			    KDCOptions2int(ctx.flags),
1639			    ctx.addrs,
1640			    ctx.etypes,
1641			    ctx.pre_auth_types,
1642			    NULL,
1643			    init_creds_keyblock_key_proc,
1644			    keyblock,
1645			    NULL,
1646			    NULL,
1647			    &ctx.cred,
1648			    NULL);
1649
1650    if (ret == 0 && creds)
1651	*creds = ctx.cred;
1652    else
1653	krb5_free_cred_contents (context, &ctx.cred);
1654
1655 out:
1656    free_init_creds_ctx(context, &ctx);
1657    return ret;
1658}
1659