heimdal - fix overlapped identifiers in the "krb5" library
[nivanova/samba-autobuild/.git] / source4 / heimdal / lib / krb5 / init_creds_pw.c
1 /*
2  * Copyright (c) 1997 - 2008 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
36 typedef struct krb5_get_init_creds_ctx {
37     KDCOptions flags;
38     krb5_creds cred;
39     krb5_addresses *addrs;
40     krb5_enctype *etypes;
41     krb5_preauthtype *pre_auth_types;
42     char *in_tkt_service;
43     unsigned nonce;
44     unsigned pk_nonce;
45
46     krb5_data req_buffer;
47     AS_REQ as_req;
48     int pa_counter;
49
50     /* password and keytab_data is freed on completion */
51     char *password;
52     krb5_keytab_key_proc_args *keytab_data;
53
54     krb5_pointer *keyseed;
55     krb5_s2k_proc keyproc;
56
57     krb5_get_init_creds_tristate req_pac;
58
59     krb5_pk_init_ctx pk_init_ctx;
60     int ic_flags;
61
62     METHOD_DATA md;
63     KRB_ERROR error;
64     AS_REP as_rep;
65     EncKDCRepPart enc_part;
66     
67     krb5_prompter_fct prompter;
68     void *prompter_data;
69
70 } krb5_get_init_creds_ctx;
71
72 static krb5_error_code
73 default_s2k_func(krb5_context context, krb5_enctype type,
74                  krb5_const_pointer keyseed,
75                  krb5_salt salt, krb5_data *s2kparms,
76                  krb5_keyblock **key)
77 {
78     krb5_error_code ret;
79     krb5_data password;
80     krb5_data opaque;
81
82     password.data = rk_UNCONST(keyseed);
83     password.length = strlen(keyseed);
84     if (s2kparms)
85         opaque = *s2kparms;
86     else
87         krb5_data_zero(&opaque);
88
89     *key = malloc(sizeof(**key));
90     if (*key == NULL)
91         return ENOMEM;
92     ret = krb5_string_to_key_data_salt_opaque(context, type, password,
93                                               salt, opaque, *key);
94     if (ret) {
95         free(*key);
96         *key = NULL;
97     }
98     return ret;
99 }
100
101 static void
102 free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx)
103 {
104     if (ctx->etypes)
105         free(ctx->etypes);
106     if (ctx->pre_auth_types)
107         free (ctx->pre_auth_types);
108     if (ctx->in_tkt_service)
109         free(ctx->in_tkt_service);
110     if (ctx->password)
111         free(ctx->password);
112     if (ctx->keytab_data)
113         free(ctx->keytab_data);
114     krb5_data_free(&ctx->req_buffer);
115     krb5_free_cred_contents(context, &ctx->cred);
116     free_METHOD_DATA(&ctx->md);
117     free_AS_REP(&ctx->as_rep);
118     free_EncKDCRepPart(&ctx->enc_part);
119     free_KRB_ERROR(&ctx->error);
120     free_AS_REQ(&ctx->as_req);
121     memset(ctx, 0, sizeof(*ctx));
122 }
123
124 static int
125 get_config_time (krb5_context context,
126                  const char *realm,
127                  const char *name,
128                  int def)
129 {
130     int ret;
131
132     ret = krb5_config_get_time (context, NULL,
133                                 "realms",
134                                 realm,
135                                 name,
136                                 NULL);
137     if (ret >= 0)
138         return ret;
139     ret = krb5_config_get_time (context, NULL,
140                                 "libdefaults",
141                                 name,
142                                 NULL);
143     if (ret >= 0)
144         return ret;
145     return def;
146 }
147
148 static krb5_error_code
149 init_cred (krb5_context context,
150            krb5_creds *cred,
151            krb5_principal client,
152            krb5_deltat start_time,
153            krb5_get_init_creds_opt *options)
154 {
155     krb5_error_code ret;
156     int tmp;
157     krb5_timestamp now;
158
159     krb5_timeofday (context, &now);
160
161     memset (cred, 0, sizeof(*cred));
162
163     if (client)
164         krb5_copy_principal(context, client, &cred->client);
165     else {
166         ret = krb5_get_default_principal (context,
167                                           &cred->client);
168         if (ret)
169             goto out;
170     }
171
172     if (start_time)
173         cred->times.starttime  = now + start_time;
174
175     if (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)
176         tmp = options->tkt_life;
177     else
178         tmp = 10 * 60 * 60;
179     cred->times.endtime = now + tmp;
180
181     if ((options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE) &&
182         options->renew_life > 0) {
183         cred->times.renew_till = now + options->renew_life;
184     }
185
186     return 0;
187
188 out:
189     krb5_free_cred_contents (context, cred);
190     return ret;
191 }
192
193 /*
194  * Print a message (str) to the user about the expiration in `lr'
195  */
196
197 static void
198 report_expiration (krb5_context context,
199                    krb5_prompter_fct prompter,
200                    krb5_data *data,
201                    const char *str,
202                    time_t now)
203 {
204     char *p;
205
206     asprintf (&p, "%s%s", str, ctime(&now));
207     (*prompter) (context, data, NULL, p, 0, NULL);
208     free (p);
209 }
210
211 /*
212  * Check the context, and in the case there is a expiration warning,
213  * use the prompter to print the warning.
214  *
215  * @param context A Kerberos 5 context.
216  * @param options An GIC options structure
217  * @param ctx The krb5_init_creds_context check for expiration.
218  */
219
220 static krb5_error_code
221 process_last_request(krb5_context context,
222                      krb5_get_init_creds_opt *options,
223                      krb5_init_creds_context ctx)
224 {
225     krb5_const_realm realm;
226     LastReq *lr;
227     krb5_boolean reported = FALSE;
228     krb5_timestamp sec;
229     time_t t;
230     size_t i;
231
232     /*
233      * First check if there is a API consumer.
234      */
235
236     realm = krb5_principal_get_realm (context, ctx->cred.client);
237     lr = &ctx->enc_part.last_req;
238
239     if (options && options->opt_private && options->opt_private->lr.func) {
240         krb5_last_req_entry **lre;
241
242         lre = calloc(lr->len + 1, sizeof(**lre));
243         if (lre == NULL) {
244             krb5_set_error_message(context, ENOMEM,
245                                    N_("malloc: out of memory", ""));
246             return ENOMEM;
247         }
248         for (i = 0; i < lr->len; i++) {
249             lre[i] = calloc(1, sizeof(*lre[i]));
250             if (lre[i] == NULL)
251                 break;
252             lre[i]->lr_type = lr->val[i].lr_type;
253             lre[i]->value = lr->val[i].lr_value;
254         }
255
256         (*options->opt_private->lr.func)(context, lre,
257                                          options->opt_private->lr.ctx);
258
259         for (i = 0; i < lr->len; i++)
260             free(lre[i]);
261         free(lre);
262     }
263
264     /*
265      * Now check if we should prompt the user
266      */
267
268     if (ctx->prompter == NULL)
269         return 0;
270
271     krb5_timeofday (context, &sec);
272
273     t = sec + get_config_time (context,
274                                realm,
275                                "warn_pwexpire",
276                                7 * 24 * 60 * 60);
277
278     for (i = 0; i < lr->len; ++i) {
279         if (lr->val[i].lr_value <= t) {
280             switch (abs(lr->val[i].lr_type)) {
281             case LR_PW_EXPTIME :
282                 report_expiration(context, ctx->prompter, 
283                                   ctx->prompter_data,
284                                   "Your password will expire at ",
285                                   lr->val[i].lr_value);
286                 reported = TRUE;
287                 break;
288             case LR_ACCT_EXPTIME :
289                 report_expiration(context, ctx->prompter, 
290                                   ctx->prompter_data,
291                                   "Your account will expire at ",
292                                   lr->val[i].lr_value);
293                 reported = TRUE;
294                 break;
295             }
296         }
297     }
298
299     if (!reported
300         && ctx->enc_part.key_expiration
301         && *ctx->enc_part.key_expiration <= t) {
302         report_expiration(context, ctx->prompter, 
303                           ctx->prompter_data,
304                           "Your password/account will expire at ",
305                           *ctx->enc_part.key_expiration);
306     }
307     return 0;
308 }
309
310 static krb5_addresses no_addrs = { 0, NULL };
311
312 static krb5_error_code
313 get_init_creds_common(krb5_context context,
314                       krb5_principal client,
315                       krb5_deltat start_time,
316                       krb5_get_init_creds_opt *options,
317                       krb5_init_creds_context ctx)
318 {
319     krb5_get_init_creds_opt *default_opt = NULL;
320     krb5_error_code ret;
321     krb5_enctype *etypes;
322     krb5_preauthtype *pre_auth_types;
323
324     memset(ctx, 0, sizeof(*ctx));
325
326     if (options == NULL) {
327         const char *realm = krb5_principal_get_realm(context, client);
328
329         krb5_get_init_creds_opt_alloc (context, &default_opt);
330         options = default_opt;
331         krb5_get_init_creds_opt_set_default_flags(context, NULL, realm, options);
332     }
333
334     if (options->opt_private) {
335         if (options->opt_private->password) {
336             ret = krb5_init_creds_set_password(context, ctx, 
337                                                options->opt_private->password);
338             if (ret)
339                 goto out;
340         }
341
342         ctx->keyproc = options->opt_private->key_proc;
343         ctx->req_pac = options->opt_private->req_pac;
344         ctx->pk_init_ctx = options->opt_private->pk_init_ctx;
345         ctx->ic_flags = options->opt_private->flags;
346     } else
347         ctx->req_pac = KRB5_INIT_CREDS_TRISTATE_UNSET;
348
349     if (ctx->keyproc == NULL)
350         ctx->keyproc = default_s2k_func;
351
352     /* Enterprise name implicitly turns on canonicalize */
353     if ((ctx->ic_flags & KRB5_INIT_CREDS_CANONICALIZE) || 
354         krb5_principal_get_type(context, client) == KRB5_NT_ENTERPRISE_PRINCIPAL)
355         ctx->flags.canonicalize = 1;
356
357     ctx->pre_auth_types = NULL;
358     ctx->addrs = NULL;
359     ctx->etypes = NULL;
360     ctx->pre_auth_types = NULL;
361
362     ret = init_cred(context, &ctx->cred, client, start_time, options);
363     if (ret) {
364         if (default_opt)
365             krb5_get_init_creds_opt_free(context, default_opt);
366         return ret;
367     }
368
369     ret = krb5_init_creds_set_service(context, ctx, NULL);
370     if (ret)
371         goto out;
372
373     if (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)
374         ctx->flags.forwardable = options->forwardable;
375
376     if (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)
377         ctx->flags.proxiable = options->proxiable;
378
379     if (start_time)
380         ctx->flags.postdated = 1;
381     if (ctx->cred.times.renew_till)
382         ctx->flags.renewable = 1;
383     if (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) {
384         ctx->addrs = options->address_list;
385     } else if (options->opt_private) {
386         switch (options->opt_private->addressless) {
387         case KRB5_INIT_CREDS_TRISTATE_UNSET:
388 #if KRB5_ADDRESSLESS_DEFAULT == TRUE
389             ctx->addrs = &no_addrs;
390 #else
391             ctx->addrs = NULL;
392 #endif
393             break;
394         case KRB5_INIT_CREDS_TRISTATE_FALSE:
395             ctx->addrs = NULL;
396             break;
397         case KRB5_INIT_CREDS_TRISTATE_TRUE:
398             ctx->addrs = &no_addrs;
399             break;
400         }
401     }
402     if (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) {
403         if (ctx->etypes)
404             free(ctx->etypes);
405
406         etypes = malloc((options->etype_list_length + 1)
407                         * sizeof(krb5_enctype));
408         if (etypes == NULL) {
409             ret = ENOMEM;
410             krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
411             goto out;
412         }
413         memcpy (etypes, options->etype_list,
414                 options->etype_list_length * sizeof(krb5_enctype));
415         etypes[options->etype_list_length] = ETYPE_NULL;
416         ctx->etypes = etypes;
417     }
418     if (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) {
419         pre_auth_types = malloc((options->preauth_list_length + 1)
420                                 * sizeof(krb5_preauthtype));
421         if (pre_auth_types == NULL) {
422             ret = ENOMEM;
423             krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
424             goto out;
425         }
426         memcpy (pre_auth_types, options->preauth_list,
427                 options->preauth_list_length * sizeof(krb5_preauthtype));
428         pre_auth_types[options->preauth_list_length] = KRB5_PADATA_NONE;
429         ctx->pre_auth_types = pre_auth_types;
430     }
431     if (options->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS)
432         ctx->flags.request_anonymous = options->anonymous;
433     if (default_opt)
434         krb5_get_init_creds_opt_free(context, default_opt);
435     return 0;
436  out:
437     if (default_opt)
438         krb5_get_init_creds_opt_free(context, default_opt);
439     return ret;
440 }
441
442 static krb5_error_code
443 change_password (krb5_context context,
444                  krb5_principal client,
445                  const char *password,
446                  char *newpw,
447                  size_t newpw_sz,
448                  krb5_prompter_fct prompter,
449                  void *data,
450                  krb5_get_init_creds_opt *old_options)
451 {
452     krb5_prompt prompts[2];
453     krb5_error_code ret;
454     krb5_creds cpw_cred;
455     char buf1[BUFSIZ], buf2[BUFSIZ];
456     krb5_data password_data[2];
457     int result_code;
458     krb5_data result_code_string;
459     krb5_data result_string;
460     char *p;
461     krb5_get_init_creds_opt *options;
462
463     memset (&cpw_cred, 0, sizeof(cpw_cred));
464
465     ret = krb5_get_init_creds_opt_alloc(context, &options);
466     if (ret)
467         return ret;
468     krb5_get_init_creds_opt_set_tkt_life (options, 60);
469     krb5_get_init_creds_opt_set_forwardable (options, FALSE);
470     krb5_get_init_creds_opt_set_proxiable (options, FALSE);
471     if (old_options && old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)
472         krb5_get_init_creds_opt_set_preauth_list (options,
473                                                   old_options->preauth_list,
474                                                   old_options->preauth_list_length);
475
476     krb5_data_zero (&result_code_string);
477     krb5_data_zero (&result_string);
478
479     ret = krb5_get_init_creds_password (context,
480                                         &cpw_cred,
481                                         client,
482                                         password,
483                                         prompter,
484                                         data,
485                                         0,
486                                         "kadmin/changepw",
487                                         options);
488     krb5_get_init_creds_opt_free(context, options);
489     if (ret)
490         goto out;
491
492     for(;;) {
493         password_data[0].data   = buf1;
494         password_data[0].length = sizeof(buf1);
495
496         prompts[0].hidden = 1;
497         prompts[0].prompt = "New password: ";
498         prompts[0].reply  = &password_data[0];
499         prompts[0].type   = KRB5_PROMPT_TYPE_NEW_PASSWORD;
500
501         password_data[1].data   = buf2;
502         password_data[1].length = sizeof(buf2);
503
504         prompts[1].hidden = 1;
505         prompts[1].prompt = "Repeat new password: ";
506         prompts[1].reply  = &password_data[1];
507         prompts[1].type   = KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN;
508
509         ret = (*prompter) (context, data, NULL, "Changing password",
510                            2, prompts);
511         if (ret) {
512             memset (buf1, 0, sizeof(buf1));
513             memset (buf2, 0, sizeof(buf2));
514             goto out;
515         }
516
517         if (strcmp (buf1, buf2) == 0)
518             break;
519         memset (buf1, 0, sizeof(buf1));
520         memset (buf2, 0, sizeof(buf2));
521     }
522
523     ret = krb5_set_password (context,
524                              &cpw_cred,
525                              buf1,
526                              client,
527                              &result_code,
528                              &result_code_string,
529                              &result_string);
530     if (ret)
531         goto out;
532     asprintf (&p, "%s: %.*s\n",
533               result_code ? "Error" : "Success",
534               (int)result_string.length,
535               result_string.length > 0 ? (char*)result_string.data : "");
536
537     /* return the result */
538     (*prompter) (context, data, NULL, p, 0, NULL);
539
540     free (p);
541     if (result_code == 0) {
542         strlcpy (newpw, buf1, newpw_sz);
543         ret = 0;
544     } else {
545         ret = ENOTTY;
546         krb5_set_error_message(context, ret,
547                                N_("failed changing password", ""));
548     }
549
550 out:
551     memset (buf1, 0, sizeof(buf1));
552     memset (buf2, 0, sizeof(buf2));
553     krb5_data_free (&result_string);
554     krb5_data_free (&result_code_string);
555     krb5_free_cred_contents (context, &cpw_cred);
556     return ret;
557 }
558
559
560 krb5_error_code KRB5_LIB_FUNCTION
561 krb5_keyblock_key_proc (krb5_context context,
562                         krb5_keytype type,
563                         krb5_data *salt,
564                         krb5_const_pointer keyseed,
565                         krb5_keyblock **key)
566 {
567     return krb5_copy_keyblock (context, keyseed, key);
568 }
569
570 /*
571  *
572  */
573
574 static krb5_error_code
575 init_as_req (krb5_context context,
576              KDCOptions opts,
577              const krb5_creds *creds,
578              const krb5_addresses *addrs,
579              const krb5_enctype *etypes,
580              AS_REQ *a)
581 {
582     krb5_error_code ret;
583
584     memset(a, 0, sizeof(*a));
585
586     a->pvno = 5;
587     a->msg_type = krb_as_req;
588     a->req_body.kdc_options = opts;
589     a->req_body.cname = malloc(sizeof(*a->req_body.cname));
590     if (a->req_body.cname == NULL) {
591         ret = ENOMEM;
592         krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
593         goto fail;
594     }
595     a->req_body.sname = malloc(sizeof(*a->req_body.sname));
596     if (a->req_body.sname == NULL) {
597         ret = ENOMEM;
598         krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
599         goto fail;
600     }
601
602     ret = _krb5_principal2principalname (a->req_body.cname, creds->client);
603     if (ret)
604         goto fail;
605     ret = copy_Realm(&creds->client->realm, &a->req_body.realm);
606     if (ret)
607         goto fail;
608
609     ret = _krb5_principal2principalname (a->req_body.sname, creds->server);
610     if (ret)
611         goto fail;
612
613     if(creds->times.starttime) {
614         a->req_body.from = malloc(sizeof(*a->req_body.from));
615         if (a->req_body.from == NULL) {
616             ret = ENOMEM;
617             krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
618             goto fail;
619         }
620         *a->req_body.from = creds->times.starttime;
621     }
622     if(creds->times.endtime){
623         ALLOC(a->req_body.till, 1);
624         *a->req_body.till = creds->times.endtime;
625     }
626     if(creds->times.renew_till){
627         a->req_body.rtime = malloc(sizeof(*a->req_body.rtime));
628         if (a->req_body.rtime == NULL) {
629             ret = ENOMEM;
630             krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
631             goto fail;
632         }
633         *a->req_body.rtime = creds->times.renew_till;
634     }
635     a->req_body.nonce = 0;
636     ret = krb5_init_etype (context,
637                            &a->req_body.etype.len,
638                            &a->req_body.etype.val,
639                            etypes);
640     if (ret)
641         goto fail;
642
643     /*
644      * This means no addresses
645      */
646
647     if (addrs && addrs->len == 0) {
648         a->req_body.addresses = NULL;
649     } else {
650         a->req_body.addresses = malloc(sizeof(*a->req_body.addresses));
651         if (a->req_body.addresses == NULL) {
652             ret = ENOMEM;
653             krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
654             goto fail;
655         }
656
657         if (addrs)
658             ret = krb5_copy_addresses(context, addrs, a->req_body.addresses);
659         else {
660             ret = krb5_get_all_client_addrs (context, a->req_body.addresses);
661             if(ret == 0 && a->req_body.addresses->len == 0) {
662                 free(a->req_body.addresses);
663                 a->req_body.addresses = NULL;
664             }
665         }
666         if (ret)
667             goto fail;
668     }
669
670     a->req_body.enc_authorization_data = NULL;
671     a->req_body.additional_tickets = NULL;
672
673     a->padata = NULL;
674
675     return 0;
676  fail:
677     free_AS_REQ(a);
678     memset(a, 0, sizeof(*a));
679     return ret;
680 }
681
682 struct pa_info_data {
683     krb5_enctype etype;
684     krb5_salt salt;
685     krb5_data *s2kparams;
686 };
687
688 static void
689 free_paid(krb5_context context, struct pa_info_data *ppaid)
690 {
691     krb5_free_salt(context, ppaid->salt);
692     if (ppaid->s2kparams)
693         krb5_free_data(context, ppaid->s2kparams);
694 }
695
696
697 static krb5_error_code
698 set_paid(struct pa_info_data *paid, krb5_context context,
699          krb5_enctype etype,
700          krb5_salttype salttype, void *salt_string, size_t salt_len,
701          krb5_data *s2kparams)
702 {
703     paid->etype = etype;
704     paid->salt.salttype = salttype;
705     paid->salt.saltvalue.data = malloc(salt_len + 1);
706     if (paid->salt.saltvalue.data == NULL) {
707         krb5_clear_error_message(context);
708         return ENOMEM;
709     }
710     memcpy(paid->salt.saltvalue.data, salt_string, salt_len);
711     ((char *)paid->salt.saltvalue.data)[salt_len] = '\0';
712     paid->salt.saltvalue.length = salt_len;
713     if (s2kparams) {
714         krb5_error_code ret;
715
716         ret = krb5_copy_data(context, s2kparams, &paid->s2kparams);
717         if (ret) {
718             krb5_clear_error_message(context);
719             krb5_free_salt(context, paid->salt);
720             return ret;
721         }
722     } else
723         paid->s2kparams = NULL;
724
725     return 0;
726 }
727
728 static struct pa_info_data *
729 pa_etype_info2(krb5_context context,
730                const krb5_principal client,
731                const AS_REQ *asreq,
732                struct pa_info_data *paid,
733                heim_octet_string *data)
734 {
735     krb5_error_code ret;
736     ETYPE_INFO2 e;
737     size_t sz;
738     int i, j;
739
740     memset(&e, 0, sizeof(e));
741     ret = decode_ETYPE_INFO2(data->data, data->length, &e, &sz);
742     if (ret)
743         goto out;
744     if (e.len == 0)
745         goto out;
746     for (j = 0; j < asreq->req_body.etype.len; j++) {
747         for (i = 0; i < e.len; i++) {
748             if (asreq->req_body.etype.val[j] == e.val[i].etype) {
749                 krb5_salt salt;
750                 if (e.val[i].salt == NULL)
751                     ret = krb5_get_pw_salt(context, client, &salt);
752                 else {
753                     salt.saltvalue.data = *e.val[i].salt;
754                     salt.saltvalue.length = strlen(*e.val[i].salt);
755                     ret = 0;
756                 }
757                 if (ret == 0)
758                     ret = set_paid(paid, context, e.val[i].etype,
759                                    KRB5_PW_SALT,
760                                    salt.saltvalue.data,
761                                    salt.saltvalue.length,
762                                    e.val[i].s2kparams);
763                 if (e.val[i].salt == NULL)
764                     krb5_free_salt(context, salt);
765                 if (ret == 0) {
766                     free_ETYPE_INFO2(&e);
767                     return paid;
768                 }
769             }
770         }
771     }
772  out:
773     free_ETYPE_INFO2(&e);
774     return NULL;
775 }
776
777 static struct pa_info_data *
778 pa_etype_info(krb5_context context,
779               const krb5_principal client,
780               const AS_REQ *asreq,
781               struct pa_info_data *paid,
782               heim_octet_string *data)
783 {
784     krb5_error_code ret;
785     ETYPE_INFO e;
786     size_t sz;
787     int i, j;
788
789     memset(&e, 0, sizeof(e));
790     ret = decode_ETYPE_INFO(data->data, data->length, &e, &sz);
791     if (ret)
792         goto out;
793     if (e.len == 0)
794         goto out;
795     for (j = 0; j < asreq->req_body.etype.len; j++) {
796         for (i = 0; i < e.len; i++) {
797             if (asreq->req_body.etype.val[j] == e.val[i].etype) {
798                 krb5_salt salt;
799                 salt.salttype = KRB5_PW_SALT;
800                 if (e.val[i].salt == NULL)
801                     ret = krb5_get_pw_salt(context, client, &salt);
802                 else {
803                     salt.saltvalue = *e.val[i].salt;
804                     ret = 0;
805                 }
806                 if (e.val[i].salttype)
807                     salt.salttype = *e.val[i].salttype;
808                 if (ret == 0) {
809                     ret = set_paid(paid, context, e.val[i].etype,
810                                    salt.salttype,
811                                    salt.saltvalue.data,
812                                    salt.saltvalue.length,
813                                    NULL);
814                     if (e.val[i].salt == NULL)
815                         krb5_free_salt(context, salt);
816                 }
817                 if (ret == 0) {
818                     free_ETYPE_INFO(&e);
819                     return paid;
820                 }
821             }
822         }
823     }
824  out:
825     free_ETYPE_INFO(&e);
826     return NULL;
827 }
828
829 static struct pa_info_data *
830 pa_pw_or_afs3_salt(krb5_context context,
831                    const krb5_principal client,
832                    const AS_REQ *asreq,
833                    struct pa_info_data *paid,
834                    heim_octet_string *data)
835 {
836     krb5_error_code ret;
837     if (paid->etype == ENCTYPE_NULL)
838         return NULL;
839     ret = set_paid(paid, context,
840                    paid->etype,
841                    paid->salt.salttype,
842                    data->data,
843                    data->length,
844                    NULL);
845     if (ret)
846         return NULL;
847     return paid;
848 }
849
850
851 struct pa_info {
852     krb5_preauthtype type;
853     struct pa_info_data *(*salt_info)(krb5_context,
854                                       const krb5_principal,
855                                       const AS_REQ *,
856                                       struct pa_info_data *,
857                                       heim_octet_string *);
858 };
859
860 static struct pa_info pa_prefs[] = {
861     { KRB5_PADATA_ETYPE_INFO2, pa_etype_info2 },
862     { KRB5_PADATA_ETYPE_INFO, pa_etype_info },
863     { KRB5_PADATA_PW_SALT, pa_pw_or_afs3_salt },
864     { KRB5_PADATA_AFS3_SALT, pa_pw_or_afs3_salt }
865 };
866
867 static PA_DATA *
868 find_pa_data(const METHOD_DATA *md, int type)
869 {
870     int i;
871     if (md == NULL)
872         return NULL;
873     for (i = 0; i < md->len; i++)
874         if (md->val[i].padata_type == type)
875             return &md->val[i];
876     return NULL;
877 }
878
879 static struct pa_info_data *
880 process_pa_info(krb5_context context,
881                 const krb5_principal client,
882                 const AS_REQ *asreq,
883                 struct pa_info_data *paid,
884                 METHOD_DATA *md)
885 {
886     struct pa_info_data *p = NULL;
887     int i;
888
889     for (i = 0; p == NULL && i < sizeof(pa_prefs)/sizeof(pa_prefs[0]); i++) {
890         PA_DATA *pa = find_pa_data(md, pa_prefs[i].type);
891         if (pa == NULL)
892             continue;
893         paid->salt.salttype = pa_prefs[i].type;
894         p = (*pa_prefs[i].salt_info)(context, client, asreq,
895                                      paid, &pa->padata_value);
896     }
897     return p;
898 }
899
900 static krb5_error_code
901 make_pa_enc_timestamp(krb5_context context, METHOD_DATA *md,
902                       krb5_enctype etype, krb5_keyblock *key)
903 {
904     PA_ENC_TS_ENC p;
905     unsigned char *buf;
906     size_t buf_size;
907     size_t len;
908     EncryptedData encdata;
909     krb5_error_code ret;
910     int32_t usec;
911     int usec2;
912     krb5_crypto crypto;
913
914     krb5_us_timeofday (context, &p.patimestamp, &usec);
915     usec2         = usec;
916     p.pausec      = &usec2;
917
918     ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret);
919     if (ret)
920         return ret;
921     if(buf_size != len)
922         krb5_abortx(context, "internal error in ASN.1 encoder");
923
924     ret = krb5_crypto_init(context, key, 0, &crypto);
925     if (ret) {
926         free(buf);
927         return ret;
928     }
929     ret = krb5_encrypt_EncryptedData(context,
930                                      crypto,
931                                      KRB5_KU_PA_ENC_TIMESTAMP,
932                                      buf,
933                                      len,
934                                      0,
935                                      &encdata);
936     free(buf);
937     krb5_crypto_destroy(context, crypto);
938     if (ret)
939         return ret;
940
941     ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret);
942     free_EncryptedData(&encdata);
943     if (ret)
944         return ret;
945     if(buf_size != len)
946         krb5_abortx(context, "internal error in ASN.1 encoder");
947
948     ret = krb5_padata_add(context, md, KRB5_PADATA_ENC_TIMESTAMP, buf, len);
949     if (ret)
950         free(buf);
951     return ret;
952 }
953
954 static krb5_error_code
955 add_enc_ts_padata(krb5_context context,
956                   METHOD_DATA *md,
957                   krb5_principal client,
958                   krb5_s2k_proc keyproc,
959                   krb5_const_pointer keyseed,
960                   krb5_enctype *enctypes,
961                   unsigned netypes,
962                   krb5_salt *salt,
963                   krb5_data *s2kparams)
964 {
965     krb5_error_code ret;
966     krb5_salt salt2;
967     krb5_enctype *ep;
968     int i;
969
970     if(salt == NULL) {
971         /* default to standard salt */
972         ret = krb5_get_pw_salt (context, client, &salt2);
973         if (ret)
974             return ret;
975         salt = &salt2;
976     }
977     if (!enctypes) {
978         enctypes = context->etypes;
979         netypes = 0;
980         for (ep = enctypes; *ep != ETYPE_NULL; ep++)
981             netypes++;
982     }
983
984     for (i = 0; i < netypes; ++i) {
985         krb5_keyblock *key;
986
987         ret = (*keyproc)(context, enctypes[i], keyseed,
988                          *salt, s2kparams, &key);
989         if (ret)
990             continue;
991         ret = make_pa_enc_timestamp (context, md, enctypes[i], key);
992         krb5_free_keyblock (context, key);
993         if (ret)
994             return ret;
995     }
996     if(salt == &salt2)
997         krb5_free_salt(context, salt2);
998     return 0;
999 }
1000
1001 static krb5_error_code
1002 pa_data_to_md_ts_enc(krb5_context context,
1003                      const AS_REQ *a,
1004                      const krb5_principal client,
1005                      krb5_get_init_creds_ctx *ctx,
1006                      struct pa_info_data *ppaid,
1007                      METHOD_DATA *md)
1008 {
1009     if (ctx->keyproc == NULL || ctx->keyseed == NULL)
1010         return 0;
1011
1012     if (ppaid) {
1013         add_enc_ts_padata(context, md, client,
1014                           ctx->keyproc, ctx->keyseed,
1015                           &ppaid->etype, 1,
1016                           &ppaid->salt, ppaid->s2kparams);
1017     } else {
1018         krb5_salt salt;
1019
1020         /* make a v5 salted pa-data */
1021         add_enc_ts_padata(context, md, client,
1022                           ctx->keyproc, ctx->keyseed,
1023                           a->req_body.etype.val, a->req_body.etype.len,
1024                           NULL, NULL);
1025
1026         /* make a v4 salted pa-data */
1027         salt.salttype = KRB5_PW_SALT;
1028         krb5_data_zero(&salt.saltvalue);
1029         add_enc_ts_padata(context, md, client,
1030                           ctx->keyproc, ctx->keyseed,
1031                           a->req_body.etype.val, a->req_body.etype.len,
1032                           &salt, NULL);
1033     }
1034     return 0;
1035 }
1036
1037 static krb5_error_code
1038 pa_data_to_key_plain(krb5_context context,
1039                      const krb5_principal client,
1040                      krb5_get_init_creds_ctx *ctx,
1041                      krb5_salt salt,
1042                      krb5_data *s2kparams,
1043                      krb5_enctype etype,
1044                      krb5_keyblock **key)
1045 {
1046     krb5_error_code ret;
1047
1048     ret = (*ctx->keyproc)(context, etype, ctx->keyseed,
1049                            salt, s2kparams, key);
1050     return ret;
1051 }
1052
1053
1054 static krb5_error_code
1055 pa_data_to_md_pkinit(krb5_context context,
1056                      const AS_REQ *a,
1057                      const krb5_principal client,
1058                      krb5_get_init_creds_ctx *ctx,
1059                      METHOD_DATA *md)
1060 {
1061     if (ctx->pk_init_ctx == NULL)
1062         return 0;
1063 #ifdef PKINIT
1064     return _krb5_pk_mk_padata(context,
1065                              ctx->pk_init_ctx,
1066                              &a->req_body,
1067                              ctx->pk_nonce,
1068                              md);
1069 #else
1070     krb5_set_error_message(context, EINVAL,
1071                            N_("no support for PKINIT compiled in", ""));
1072     return EINVAL;
1073 #endif
1074 }
1075
1076 static krb5_error_code
1077 pa_data_add_pac_request(krb5_context context,
1078                         krb5_get_init_creds_ctx *ctx,
1079                         METHOD_DATA *md)
1080 {
1081     size_t len, length;
1082     krb5_error_code ret;
1083     PA_PAC_REQUEST req;
1084     void *buf;
1085
1086     switch (ctx->req_pac) {
1087     case KRB5_INIT_CREDS_TRISTATE_UNSET:
1088         return 0; /* don't bother */
1089     case KRB5_INIT_CREDS_TRISTATE_TRUE:
1090         req.include_pac = 1;
1091         break;
1092     case KRB5_INIT_CREDS_TRISTATE_FALSE:
1093         req.include_pac = 0;
1094     }
1095
1096     ASN1_MALLOC_ENCODE(PA_PAC_REQUEST, buf, length,
1097                        &req, &len, ret);
1098     if (ret)
1099         return ret;
1100     if(len != length)
1101         krb5_abortx(context, "internal error in ASN.1 encoder");
1102
1103     ret = krb5_padata_add(context, md, KRB5_PADATA_PA_PAC_REQUEST, buf, len);
1104     if (ret)
1105         free(buf);
1106
1107     return 0;
1108 }
1109
1110 /*
1111  * Assumes caller always will free `out_md', even on error.
1112  */
1113
1114 static krb5_error_code
1115 process_pa_data_to_md(krb5_context context,
1116                       const krb5_creds *creds,
1117                       const AS_REQ *a,
1118                       krb5_get_init_creds_ctx *ctx,
1119                       METHOD_DATA *in_md,
1120                       METHOD_DATA **out_md,
1121                       krb5_prompter_fct prompter,
1122                       void *prompter_data)
1123 {
1124     krb5_error_code ret;
1125
1126     ALLOC(*out_md, 1);
1127     if (*out_md == NULL) {
1128         krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
1129         return ENOMEM;
1130     }
1131     (*out_md)->len = 0;
1132     (*out_md)->val = NULL;
1133
1134     /*
1135      * Make sure we don't sent both ENC-TS and PK-INIT pa data, no
1136      * need to expose our password protecting our PKCS12 key.
1137      */
1138
1139     if (ctx->pk_init_ctx) {
1140
1141         ret = pa_data_to_md_pkinit(context, a, creds->client, ctx, *out_md);
1142         if (ret)
1143             return ret;
1144
1145     } else if (in_md->len != 0) {
1146         struct pa_info_data paid, *ppaid;
1147
1148         memset(&paid, 0, sizeof(paid));
1149
1150         paid.etype = ENCTYPE_NULL;
1151         ppaid = process_pa_info(context, creds->client, a, &paid, in_md);
1152
1153         pa_data_to_md_ts_enc(context, a, creds->client, ctx, ppaid, *out_md);
1154         if (ppaid)
1155             free_paid(context, ppaid);
1156     }
1157
1158     pa_data_add_pac_request(context, ctx, *out_md);
1159
1160     if ((*out_md)->len == 0) {
1161         free(*out_md);
1162         *out_md = NULL;
1163     }
1164
1165     return 0;
1166 }
1167
1168 static krb5_error_code
1169 process_pa_data_to_key(krb5_context context,
1170                        krb5_get_init_creds_ctx *ctx,
1171                        krb5_creds *creds,
1172                        AS_REQ *a,
1173                        AS_REP *rep,
1174                        const krb5_krbhst_info *hi,
1175                        krb5_keyblock **key)
1176 {
1177     struct pa_info_data paid, *ppaid = NULL;
1178     krb5_error_code ret;
1179     krb5_enctype etype;
1180     PA_DATA *pa;
1181
1182     memset(&paid, 0, sizeof(paid));
1183
1184     etype = rep->enc_part.etype;
1185
1186     if (rep->padata) {
1187         paid.etype = etype;
1188         ppaid = process_pa_info(context, creds->client, a, &paid,
1189                                 rep->padata);
1190     }
1191     if (ppaid == NULL) {
1192         ret = krb5_get_pw_salt (context, creds->client, &paid.salt);
1193         if (ret)
1194             return ret;
1195         paid.etype = etype;
1196         paid.s2kparams = NULL;
1197     }
1198
1199     pa = NULL;
1200     if (rep->padata) {
1201         int idx = 0;
1202         pa = krb5_find_padata(rep->padata->val,
1203                               rep->padata->len,
1204                               KRB5_PADATA_PK_AS_REP,
1205                               &idx);
1206         if (pa == NULL) {
1207             idx = 0;
1208             pa = krb5_find_padata(rep->padata->val,
1209                                   rep->padata->len,
1210                                   KRB5_PADATA_PK_AS_REP_19,
1211                                   &idx);
1212         }
1213     }
1214     if (pa && ctx->pk_init_ctx) {
1215 #ifdef PKINIT
1216         ret = _krb5_pk_rd_pa_reply(context,
1217                                    a->req_body.realm,
1218                                    ctx->pk_init_ctx,
1219                                    etype,
1220                                    hi,
1221                                    ctx->pk_nonce,
1222                                    &ctx->req_buffer,
1223                                    pa,
1224                                    key);
1225 #else
1226         ret = EINVAL;
1227         krb5_set_error_message(context, ret, N_("no support for PKINIT compiled in", ""));
1228 #endif
1229     } else if (ctx->keyseed)
1230         ret = pa_data_to_key_plain(context, creds->client, ctx,
1231                                    paid.salt, paid.s2kparams, etype, key);
1232     else {
1233         ret = EINVAL;
1234         krb5_set_error_message(context, ret, N_("No usable pa data type", ""));
1235     }
1236
1237     free_paid(context, &paid);
1238     return ret;
1239 }
1240
1241 /**
1242  * Start a new context to get a new initial credential.
1243  *
1244  * @param context A Kerberos 5 context.
1245  * @param client The Kerberos principal to get the credential for, if
1246  *     NULL is given, the default principal is used as determined by
1247  *     krb5_get_default_principal().
1248  * @param prompter
1249  * @param prompter_data
1250  * @param start_time the time the ticket should start to be valid or 0 for now.
1251  * @param options a options structure, can be NULL for default options.
1252  * @param rctx A new allocated free with krb5_init_creds_free().
1253  *
1254  * @return 0 for success or an Kerberos 5 error code, see krb5_get_error_message().
1255  *
1256  * @ingroup krb5_credential
1257  */
1258
1259 krb5_error_code KRB5_LIB_FUNCTION
1260 krb5_init_creds_init(krb5_context context,
1261                      krb5_principal client,
1262                      krb5_prompter_fct prompter,
1263                      void *prompter_data,
1264                      krb5_deltat start_time,
1265                      krb5_get_init_creds_opt *options,
1266                      krb5_init_creds_context *rctx)
1267 {
1268     krb5_init_creds_context ctx;
1269     krb5_error_code ret;
1270
1271     *rctx = NULL;
1272
1273     ctx = calloc(1, sizeof(*ctx));
1274     if (ctx == NULL) {
1275         krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
1276         return ENOMEM;
1277     }
1278
1279     ret = get_init_creds_common(context, client, start_time, options, ctx);
1280     if (ret) {
1281         free(ctx);
1282         return ret;
1283     }
1284
1285     /* Set a new nonce. */
1286     krb5_generate_random_block (&ctx->nonce, sizeof(ctx->nonce));
1287     ctx->nonce &= 0x7fffffff;
1288     /* XXX these just needs to be the same when using Windows PK-INIT */
1289     ctx->pk_nonce = ctx->nonce;
1290
1291     ctx->prompter = prompter;
1292     ctx->prompter_data = prompter_data;
1293
1294     *rctx = ctx;
1295
1296     return ret;
1297 }
1298
1299 /**
1300  * Sets the service that the is requested. This call is only neede for
1301  * special initial tickets, by default the a krbtgt is fetched in the default realm.
1302  *
1303  * @param context a Kerberos 5 context.
1304  * @param ctx a krb5_init_creds_context context.
1305  * @param service the service given as a string, for example
1306  *        "kadmind/admin". If NULL, the default krbtgt in the clients
1307  *        realm is set.
1308  *
1309  * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
1310  * @ingroup krb5_credential
1311  */
1312
1313 krb5_error_code KRB5_LIB_FUNCTION
1314 krb5_init_creds_set_service(krb5_context context,
1315                             krb5_init_creds_context ctx,
1316                             const char *service)
1317 {
1318     krb5_const_realm client_realm;
1319     krb5_principal principal;
1320     krb5_error_code ret;
1321
1322     client_realm = krb5_principal_get_realm (context, ctx->cred.client);
1323
1324     if (service) {
1325         ret = krb5_parse_name (context, service, &principal);
1326         if (ret)
1327             return ret;
1328         krb5_principal_set_realm (context, principal, client_realm);
1329     } else {
1330         ret = krb5_make_principal(context, &principal,
1331                                   client_realm, KRB5_TGS_NAME, client_realm,
1332                                   NULL);
1333         if (ret)
1334             return ret;
1335     }
1336     krb5_free_principal(context, ctx->cred.server);
1337     ctx->cred.server = principal;
1338
1339     return 0;
1340 }
1341
1342 /**
1343  * Sets the password that will use for the request.
1344  *
1345  * @param context a Kerberos 5 context.
1346  * @param ctx ctx krb5_init_creds_context context.
1347  * @param password the password to use.
1348  *
1349  * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
1350  * @ingroup krb5_credential
1351  */
1352
1353 krb5_error_code KRB5_LIB_FUNCTION
1354 krb5_init_creds_set_password(krb5_context context,
1355                              krb5_init_creds_context ctx,
1356                              const char *password)
1357 {
1358     if (ctx->password)
1359         free(ctx->password);
1360     if (password) {
1361         ctx->password = strdup(password);
1362         if (ctx->password == NULL) {
1363             krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
1364             return ENOMEM;
1365         }
1366         ctx->keyseed = (void *) ctx->password;
1367     } else {
1368         ctx->keyseed = NULL;
1369         ctx->password = NULL;
1370     }
1371
1372     return 0;
1373 }
1374
1375 static krb5_error_code
1376 keytab_key_proc(krb5_context context, krb5_enctype enctype,
1377                 krb5_const_pointer keyseed,
1378                 krb5_salt salt, krb5_data *s2kparms,
1379                 krb5_keyblock **key)
1380 {
1381     krb5_keytab_key_proc_args *args  = rk_UNCONST(keyseed);
1382     krb5_keytab keytab = args->keytab;
1383     krb5_principal principal = args->principal;
1384     krb5_error_code ret;
1385     krb5_keytab real_keytab;
1386     krb5_keytab_entry entry;
1387
1388     if(keytab == NULL)
1389         krb5_kt_default(context, &real_keytab);
1390     else
1391         real_keytab = keytab;
1392
1393     ret = krb5_kt_get_entry (context, real_keytab, principal,
1394                              0, enctype, &entry);
1395
1396     if (keytab == NULL)
1397         krb5_kt_close (context, real_keytab);
1398
1399     if (ret)
1400         return ret;
1401
1402     ret = krb5_copy_keyblock (context, &entry.keyblock, key);
1403     krb5_kt_free_entry(context, &entry);
1404     return ret;
1405 }
1406
1407
1408 /**
1409  * Set the keytab to use for authentication.
1410  *
1411  * @param context a Kerberos 5 context.
1412  * @param ctx ctx krb5_init_creds_context context.
1413  * @param keytab the keytab to read the key from.
1414  *
1415  * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
1416  * @ingroup krb5_credential
1417  */
1418
1419 krb5_error_code KRB5_LIB_FUNCTION
1420 krb5_init_creds_set_keytab(krb5_context context,
1421                            krb5_init_creds_context ctx,
1422                            krb5_keytab keytab)
1423 {
1424     krb5_keytab_key_proc_args *a;
1425     krb5_keytab_entry entry;
1426     krb5_kt_cursor cursor;
1427     krb5_enctype *etypes = NULL;
1428     krb5_error_code ret;
1429     size_t netypes = 0;
1430     int kvno = 0;
1431     
1432     a = malloc(sizeof(*a));
1433     if (a == NULL) {
1434         krb5_set_error_message(context, ENOMEM,
1435                                N_("malloc: out of memory", ""));
1436         return ENOMEM;
1437     }
1438         
1439     a->principal = ctx->cred.client;
1440     a->keytab    = keytab;
1441
1442     ctx->keytab_data = a;
1443     ctx->keyseed = (void *)a;
1444     ctx->keyproc = keytab_key_proc;
1445
1446     /*
1447      * We need to the KDC what enctypes we support for this keytab,
1448      * esp if the keytab is really a password based entry, then the
1449      * KDC might have more enctypes in the database then what we have
1450      * in the keytab.
1451      */
1452
1453     ret = krb5_kt_start_seq_get(context, keytab, &cursor);
1454     if(ret)
1455         goto out;
1456
1457     while(krb5_kt_next_entry(context, keytab, &entry, &cursor) == 0){
1458         void *ptr;
1459
1460         if (!krb5_principal_compare(context, entry.principal, ctx->cred.client))
1461             goto next;
1462
1463         /* check if we ahve this kvno already */
1464         if (entry.vno > kvno) {
1465             /* remove old list of etype */
1466             if (etypes)
1467                 free(etypes);
1468             etypes = NULL;
1469             netypes = 0;
1470             kvno = entry.vno;
1471         } else if (entry.vno != kvno)
1472             goto next;
1473                 
1474         /* check if enctype is supported */
1475         if (krb5_enctype_valid(context, entry.keyblock.keytype) != 0)
1476             goto next;
1477
1478         /* add enctype to supported list */
1479         ptr = realloc(etypes, sizeof(etypes[0]) * (netypes + 2));
1480         if (ptr == NULL)
1481             goto next;
1482
1483         etypes = ptr;
1484         etypes[netypes] = entry.keyblock.keytype;
1485         etypes[netypes + 1] = ETYPE_NULL;
1486         netypes++;
1487     next:
1488         krb5_kt_free_entry(context, &entry);
1489     }
1490     krb5_kt_end_seq_get(context, keytab, &cursor);
1491
1492     if (etypes) {
1493         if (ctx->etypes)
1494             free(ctx->etypes);
1495         ctx->etypes = etypes;
1496     }
1497
1498  out:
1499     return 0;
1500 }
1501
1502 static krb5_error_code
1503 keyblock_key_proc(krb5_context context, krb5_enctype enctype,
1504                   krb5_const_pointer keyseed,
1505                   krb5_salt salt, krb5_data *s2kparms,
1506                   krb5_keyblock **key)
1507 {
1508     return krb5_copy_keyblock (context, keyseed, key);
1509 }
1510
1511 krb5_error_code KRB5_LIB_FUNCTION
1512 krb5_init_creds_set_keyblock(krb5_context context,
1513                              krb5_init_creds_context ctx,
1514                              krb5_keyblock *keyblock)
1515 {
1516     ctx->keyseed = (void *)keyblock;
1517     ctx->keyproc = keyblock_key_proc;
1518
1519     return 0;
1520 }
1521
1522 /**
1523  * The core loop if krb5_get_init_creds() function family. Create the
1524  * packets and have the caller send them off to the KDC. 
1525  *
1526  * If the caller want all work been done for them, use
1527  * krb5_init_creds_get() instead.
1528  *
1529  * @param context a Kerberos 5 context.
1530  * @param ctx ctx krb5_init_creds_context context.
1531  * @param in input data from KDC, first round it should be reset by krb5_data_zer().
1532  * @param out reply to KDC.
1533  * @param hostinfo KDC address info, first round it can be NULL.
1534  * @param flags status of the round, if 1 is set, continue one more round.
1535  *
1536  * @return 0 for success, or an Kerberos 5 error code, see
1537  *     krb5_get_error_message().
1538  *
1539  * @ingroup krb5_credential
1540  */
1541
1542 krb5_error_code KRB5_LIB_FUNCTION
1543 krb5_init_creds_step(krb5_context context,
1544                      krb5_init_creds_context ctx,
1545                      krb5_data *in,
1546                      krb5_data *out,
1547                      krb5_krbhst_info *hostinfo,
1548                      unsigned int *flags)
1549 {
1550     krb5_error_code ret;
1551     size_t len;
1552     size_t size;
1553
1554     krb5_data_zero(out);
1555
1556     if (ctx->as_req.req_body.cname == NULL) {
1557         ret = init_as_req(context, ctx->flags, &ctx->cred,
1558                           ctx->addrs, ctx->etypes, &ctx->as_req);
1559         if (ret) {
1560             free_init_creds_ctx(context, ctx);
1561             return ret;
1562         }
1563     }
1564
1565 #define MAX_PA_COUNTER 10
1566     if (ctx->pa_counter > MAX_PA_COUNTER) {
1567         krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1568                                N_("Looping %d times while getting "
1569                                   "initial credentials", ""),
1570                                ctx->pa_counter);
1571         return KRB5_GET_IN_TKT_LOOP;
1572     }
1573     ctx->pa_counter++;
1574
1575     /* Lets process the input packet */
1576     if (in && in->length) {
1577         krb5_kdc_rep rep;
1578
1579         memset(&rep, 0, sizeof(rep));
1580
1581         ret = decode_AS_REP(in->data, in->length, &rep.kdc_rep, &size);
1582         if (ret == 0) {
1583             krb5_keyblock *key = NULL;
1584             unsigned eflags = EXTRACT_TICKET_AS_REQ;
1585
1586             if (ctx->flags.canonicalize) {
1587                 eflags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH;
1588                 eflags |= EXTRACT_TICKET_MATCH_REALM;
1589             }
1590             if (ctx->ic_flags & KRB5_INIT_CREDS_NO_C_CANON_CHECK)
1591                 eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
1592
1593             ret = process_pa_data_to_key(context, ctx, &ctx->cred,
1594                                          &ctx->as_req, &rep.kdc_rep, hostinfo, &key);
1595             if (ret) {
1596                 free_AS_REP(&rep.kdc_rep);
1597                 goto out;
1598             }
1599
1600             ret = _krb5_extract_ticket(context,
1601                                        &rep,
1602                                        &ctx->cred,
1603                                        key,
1604                                        NULL,
1605                                        KRB5_KU_AS_REP_ENC_PART,
1606                                        NULL,
1607                                        ctx->nonce,
1608                                        eflags,
1609                                        NULL,
1610                                        NULL);
1611             krb5_free_keyblock(context, key);
1612
1613             *flags = 0;
1614
1615             if (ret == 0)
1616                 ret = copy_EncKDCRepPart(&rep.enc_part, &ctx->enc_part);
1617
1618             free_AS_REP(&rep.kdc_rep);
1619             free_EncASRepPart(&rep.enc_part);
1620
1621             return ret;
1622
1623         } else {
1624             /* let's try to parse it as a KRB-ERROR */
1625
1626             free_KRB_ERROR(&ctx->error);
1627
1628             ret = krb5_rd_error(context, in, &ctx->error);
1629             if(ret && in->length && ((char*)in->data)[0] == 4)
1630                 ret = KRB5KRB_AP_ERR_V4_REPLY;
1631             if (ret)
1632                 goto out;
1633
1634             ret = krb5_error_from_rd_error(context, &ctx->error, &ctx->cred);
1635
1636             /*
1637              * If no preauth was set and KDC requires it, give it one
1638              * more try.
1639              */
1640
1641             if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED) {
1642
1643                 free_METHOD_DATA(&ctx->md);
1644                 memset(&ctx->md, 0, sizeof(ctx->md));
1645
1646                 if (ctx->error.e_data) {
1647                     ret = decode_METHOD_DATA(ctx->error.e_data->data,
1648                                              ctx->error.e_data->length,
1649                                              &ctx->md,
1650                                              NULL);
1651                     if (ret)
1652                         krb5_set_error_message(context, ret,
1653                                                N_("Failed to decode METHOD-DATA", ""));
1654                 } else {
1655                     krb5_set_error_message(context, ret,
1656                                            N_("Preauth required but no preauth "
1657                                               "options send by KDC", ""));
1658                 }
1659             } else if (ret == KRB5KRB_AP_ERR_SKEW && context->kdc_sec_offset == 0) {
1660                 /* 
1661                  * Try adapt to timeskrew when we are using pre-auth, and
1662                  * if there was a time skew, try again.
1663                  */
1664                 krb5_set_real_time(context, ctx->error.stime, -1);
1665                 if (context->kdc_sec_offset)
1666                     ret = 0; 
1667             } else if (ret == KRB5_KDC_ERR_WRONG_REALM && ctx->flags.canonicalize) {
1668                 /* client referal to a new realm */
1669                 if (ctx->error.crealm == NULL) {
1670                     krb5_set_error_message(context, ret,
1671                                            N_("Got a client referral, not but no realm", ""));
1672                     goto out;
1673                 }
1674                 ret = krb5_principal_set_realm(context, 
1675                                                ctx->cred.client,
1676                                                *ctx->error.crealm);
1677             }
1678             if (ret)
1679                 goto out;
1680         }
1681     }
1682
1683     if (ctx->as_req.padata) {
1684         free_METHOD_DATA(ctx->as_req.padata);
1685         free(ctx->as_req.padata);
1686         ctx->as_req.padata = NULL;
1687     }
1688
1689     /* Set a new nonce. */
1690     ctx->as_req.req_body.nonce = ctx->nonce;
1691
1692     /* fill_in_md_data */
1693     ret = process_pa_data_to_md(context, &ctx->cred, &ctx->as_req, ctx,
1694                                 &ctx->md, &ctx->as_req.padata,
1695                                 ctx->prompter, ctx->prompter_data);
1696     if (ret)
1697         goto out;
1698
1699     krb5_data_free(&ctx->req_buffer);
1700
1701     ASN1_MALLOC_ENCODE(AS_REQ,
1702                        ctx->req_buffer.data, ctx->req_buffer.length,
1703                        &ctx->as_req, &len, ret);
1704     if (ret)
1705         goto out;
1706     if(len != ctx->req_buffer.length)
1707         krb5_abortx(context, "internal error in ASN.1 encoder");
1708
1709     out->data = ctx->req_buffer.data;
1710     out->length = ctx->req_buffer.length;
1711
1712     *flags = 1;
1713
1714     return 0;
1715  out:
1716     return ret;
1717 }
1718
1719 /**
1720  * Extract the newly acquired credentials from krb5_init_creds_context
1721  * context.
1722  *
1723  * @param context A Kerberos 5 context.
1724  * @param ctx
1725  * @param cred credentials, free with krb5_free_cred_contents().
1726  *
1727  * @return 0 for sucess or An Kerberos error code, see krb5_get_error_message().
1728  */
1729
1730 krb5_error_code KRB5_LIB_FUNCTION
1731 krb5_init_creds_get_creds(krb5_context context,
1732                           krb5_init_creds_context ctx,
1733                           krb5_creds *cred)
1734 {
1735     return krb5_copy_creds_contents(context, &ctx->cred, cred);
1736 }
1737
1738 /**
1739  * Get the last error from the transaction.
1740  *
1741  * @return Returns 0 or an error code
1742  *
1743  * @ingroup krb5_credential
1744  */
1745
1746 krb5_error_code KRB5_LIB_FUNCTION
1747 krb5_init_creds_get_error(krb5_context context,
1748                           krb5_init_creds_context ctx,
1749                           KRB_ERROR *error)
1750 {
1751     krb5_error_code ret;
1752
1753     ret = copy_KRB_ERROR(&ctx->error, error);
1754     if (ret)
1755         krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
1756
1757     return ret;
1758 }
1759
1760 /**
1761  * Free the krb5_init_creds_context allocated by krb5_init_creds_init().
1762  *
1763  * @param context A Kerberos 5 context.
1764  * @param ctx The krb5_init_creds_context to free.
1765  *
1766  * @ingroup krb5_credential
1767  */
1768
1769 void KRB5_LIB_FUNCTION
1770 krb5_init_creds_free(krb5_context context,
1771                      krb5_init_creds_context ctx)
1772 {
1773     free_init_creds_ctx(context, ctx);
1774     free(ctx);
1775 }
1776
1777 /**
1778  * Get new credentials as setup by the krb5_init_creds_context.
1779  *
1780  * @param context A Kerberos 5 context.
1781  * @param ctx The krb5_init_creds_context to process.
1782  *
1783  * @ingroup krb5_credential
1784  */
1785
1786 krb5_error_code KRB5_LIB_FUNCTION
1787 krb5_init_creds_get(krb5_context context, krb5_init_creds_context ctx)
1788 {
1789     krb5_sendto_ctx stctx = NULL;
1790     krb5_krbhst_info *hostinfo = NULL;
1791     krb5_error_code ret;
1792     krb5_data in, out;
1793     unsigned int flags = 0;
1794
1795     krb5_data_zero(&in);
1796     krb5_data_zero(&out);
1797
1798     ret = krb5_sendto_ctx_alloc(context, &stctx);
1799     if (ret)
1800         goto out;
1801     krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
1802
1803     while (1) {
1804         flags = 0;
1805         ret = krb5_init_creds_step(context, ctx, &in, &out, hostinfo, &flags);
1806         krb5_data_free(&in);
1807         if (ret)
1808             goto out;
1809
1810         if ((flags & 1) == 0)
1811             break;
1812
1813         ret = krb5_sendto_context (context, stctx, &out, 
1814                                    ctx->cred.client->realm, &in);
1815         if (ret)
1816             goto out;
1817
1818     }
1819
1820  out:
1821     if (stctx)
1822         krb5_sendto_ctx_free(context, stctx);
1823
1824     return ret;
1825 }
1826
1827 /**
1828  * Get new credentials using password.
1829  *
1830  * @ingroup krb5_credential
1831  */
1832
1833
1834 krb5_error_code KRB5_LIB_FUNCTION
1835 krb5_get_init_creds_password(krb5_context context,
1836                              krb5_creds *creds,
1837                              krb5_principal client,
1838                              const char *password,
1839                              krb5_prompter_fct prompter,
1840                              void *data,
1841                              krb5_deltat start_time,
1842                              const char *in_tkt_service,
1843                              krb5_get_init_creds_opt *options)
1844 {
1845     krb5_init_creds_context ctx;
1846     char buf[BUFSIZ];
1847     krb5_error_code ret;
1848     int chpw = 0;
1849
1850  again:
1851     ret = krb5_init_creds_init(context, client, prompter, data, start_time, options, &ctx);
1852     if (ret)
1853         goto out;
1854
1855     ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
1856     if (ret)
1857         goto out;
1858
1859     if (prompter != NULL && ctx->password == NULL && password == NULL) {
1860         krb5_prompt prompt;
1861         krb5_data password_data;
1862         char *p, *q;
1863
1864         krb5_unparse_name (context, client, &p);
1865         asprintf (&q, "%s's Password: ", p);
1866         free (p);
1867         prompt.prompt = q;
1868         password_data.data   = buf;
1869         password_data.length = sizeof(buf);
1870         prompt.hidden = 1;
1871         prompt.reply  = &password_data;
1872         prompt.type   = KRB5_PROMPT_TYPE_PASSWORD;
1873
1874         ret = (*prompter) (context, data, NULL, NULL, 1, &prompt);
1875         free (q);
1876         if (ret) {
1877             memset (buf, 0, sizeof(buf));
1878             ret = KRB5_LIBOS_PWDINTR;
1879             krb5_clear_error_message (context);
1880             goto out;
1881         }
1882         password = password_data.data;
1883     }
1884
1885     if (password) {
1886         ret = krb5_init_creds_set_password(context, ctx, password);
1887         if (ret)
1888             goto out;
1889     }
1890
1891     ret = krb5_init_creds_get(context, ctx);
1892     
1893     if (ret == 0)
1894         process_last_request(context, options, ctx);
1895
1896
1897     if (ret == KRB5KDC_ERR_KEY_EXPIRED && chpw == 0) {
1898         char buf2[1024];
1899
1900         /* try to avoid recursion */
1901         if (in_tkt_service != NULL && strcmp(in_tkt_service, "kadmin/changepw") == 0)
1902            goto out;
1903
1904         /* don't try to change password where then where none */
1905         if (prompter == NULL)
1906             goto out;
1907
1908         ret = change_password (context,
1909                                client,
1910                                ctx->password,
1911                                buf2,
1912                                sizeof(buf),
1913                                prompter,
1914                                data,
1915                                options);
1916         if (ret)
1917             goto out;
1918         chpw = 1;
1919         krb5_init_creds_free(context, ctx);
1920         goto again;
1921     }
1922
1923  out:
1924     if (ret == 0)
1925         krb5_init_creds_get_creds(context, ctx, creds);
1926
1927     if (ctx)
1928         krb5_init_creds_free(context, ctx);
1929
1930     memset(buf, 0, sizeof(buf));
1931     return ret;
1932 }
1933
1934 /**
1935  * Get new credentials using keyblock.
1936  *
1937  * @ingroup krb5_credential
1938  */
1939
1940 krb5_error_code KRB5_LIB_FUNCTION
1941 krb5_get_init_creds_keyblock(krb5_context context,
1942                              krb5_creds *creds,
1943                              krb5_principal client,
1944                              krb5_keyblock *keyblock,
1945                              krb5_deltat start_time,
1946                              const char *in_tkt_service,
1947                              krb5_get_init_creds_opt *options)
1948 {
1949     krb5_init_creds_context ctx;
1950     krb5_error_code ret;
1951
1952     memset(creds, 0, sizeof(*creds));
1953
1954     ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx);
1955     if (ret)
1956         goto out;
1957
1958     ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
1959     if (ret)
1960         goto out;
1961
1962     ret = krb5_init_creds_set_keyblock(context, ctx, keyblock);
1963     if (ret)
1964         goto out;
1965
1966     ret = krb5_init_creds_get(context, ctx);
1967
1968     if (ret == 0)
1969         process_last_request(context, options, ctx);
1970
1971  out:
1972     if (ret == 0)
1973         krb5_init_creds_get_creds(context, ctx, creds);
1974
1975     if (ctx)
1976         krb5_init_creds_free(context, ctx);
1977
1978     return ret;
1979 }
1980
1981 /**
1982  * Get new credentials using keytab.
1983  *
1984  * @ingroup krb5_credential
1985  */
1986
1987 krb5_error_code KRB5_LIB_FUNCTION
1988 krb5_get_init_creds_keytab(krb5_context context,
1989                            krb5_creds *creds,
1990                            krb5_principal client,
1991                            krb5_keytab keytab,
1992                            krb5_deltat start_time,
1993                            const char *in_tkt_service,
1994                            krb5_get_init_creds_opt *options)
1995 {
1996     krb5_init_creds_context ctx;
1997     krb5_error_code ret;
1998
1999     memset(creds, 0, sizeof(*creds));
2000
2001     ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx);
2002     if (ret)
2003         goto out;
2004
2005     ret = krb5_init_creds_set_service(context, ctx, in_tkt_service);
2006     if (ret)
2007         goto out;
2008
2009     ret = krb5_init_creds_set_keytab(context, ctx, keytab);
2010     if (ret)
2011         goto out;
2012
2013     ret = krb5_init_creds_get(context, ctx);
2014     if (ret == 0)
2015         process_last_request(context, options, ctx);
2016
2017  out:
2018     if (ret == 0)
2019         krb5_init_creds_get_creds(context, ctx, creds);
2020
2021     if (ctx)
2022         krb5_init_creds_free(context, ctx);
2023
2024     return ret;
2025 }