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