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