xref: /freebsd/crypto/krb5/src/lib/krb5/krb/get_in_tkt.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/krb/get_in_tkt.c */
3 /*
4  * Copyright 1990,1991, 2003, 2008 by the Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26 
27 #include "k5-int.h"
28 #include "int-proto.h"
29 #include "os-proto.h"
30 #include "fast.h"
31 #include "init_creds_ctx.h"
32 
33 /* some typedef's for the function args to make things look a bit cleaner */
34 
35 static krb5_error_code make_preauth_list (krb5_context,
36                                           krb5_preauthtype *,
37                                           int, krb5_pa_data ***);
38 static krb5_error_code sort_krb5_padata_sequence(krb5_context context,
39                                                  krb5_data *realm,
40                                                  krb5_pa_data **padata);
41 
42 /*
43  * Decrypt the AS reply in ctx, populating ctx->reply->enc_part2.  If
44  * strengthen_key is not null, combine it with the reply key as specified in
45  * RFC 6113 section 5.4.3.  Place the key used in *key_out.
46  */
47 static krb5_error_code
decrypt_as_reply(krb5_context context,krb5_init_creds_context ctx,const krb5_keyblock * strengthen_key,krb5_keyblock * key_out)48 decrypt_as_reply(krb5_context context, krb5_init_creds_context ctx,
49                  const krb5_keyblock *strengthen_key, krb5_keyblock *key_out)
50 {
51     krb5_error_code ret;
52     krb5_keyblock key;
53     krb5_responder_fn responder;
54     void *responder_data;
55 
56     memset(key_out, 0, sizeof(*key_out));
57     memset(&key, 0, sizeof(key));
58 
59     if (ctx->as_key.length) {
60         /* The reply key was computed or replaced during preauth processing;
61          * try it. */
62         TRACE_INIT_CREDS_AS_KEY_PREAUTH(context, &ctx->as_key);
63         ret = krb5int_fast_reply_key(context, strengthen_key, &ctx->as_key,
64                                      &key);
65         if (ret)
66             return ret;
67         ret = krb5_kdc_rep_decrypt_proc(context, &key, NULL, ctx->reply);
68         if (!ret) {
69             *key_out = key;
70             return 0;
71         }
72         krb5_free_keyblock_contents(context, &key);
73         TRACE_INIT_CREDS_PREAUTH_DECRYPT_FAIL(context, ret);
74 
75         /*
76          * For two reasons, we fall back to trying or retrying the gak_fct if
77          * this fails:
78          *
79          * 1. The KDC might encrypt the reply using a different enctype than
80          *    the AS key we computed during preauth.
81          *
82          * 2. For 1.1.1 and prior KDC's, when SAM is used with USE_SAD_AS_KEY,
83          *    the AS-REP is encrypted in the client long-term key instead of
84          *    the SAD.
85          *
86          * The gak_fct for krb5_get_init_creds_with_password() caches the
87          * password, so this fallback does not result in a second password
88          * prompt.
89          */
90     } else {
91         /*
92          * No AS key was computed during preauth processing, perhaps because
93          * preauth was not used.  If the caller supplied a responder callback,
94          * possibly invoke it before calling the gak_fct for real.
95          */
96         k5_gic_opt_get_responder(ctx->opt, &responder, &responder_data);
97         if (responder != NULL) {
98             /* Indicate a need for the AS key by calling the gak_fct with a
99              * NULL as_key. */
100             ret = ctx->gak_fct(context, ctx->request->client, ctx->etype, NULL,
101                                NULL, NULL, NULL, NULL, ctx->gak_data,
102                                ctx->rctx.items);
103             if (ret)
104                 return ret;
105 
106             /* If that produced a responder question, invoke the responder. */
107             if (!k5_response_items_empty(ctx->rctx.items)) {
108                 ret = (*responder)(context, responder_data, &ctx->rctx);
109                 if (ret)
110                     return ret;
111             }
112         }
113     }
114 
115     /* Compute or re-compute the AS key, prompting for the password if
116      * necessary. */
117     TRACE_INIT_CREDS_GAK(context, &ctx->salt, &ctx->s2kparams);
118     ret = ctx->gak_fct(context, ctx->request->client,
119                        ctx->reply->enc_part.enctype, ctx->prompter,
120                        ctx->prompter_data, &ctx->salt, &ctx->s2kparams,
121                        &ctx->as_key, ctx->gak_data, ctx->rctx.items);
122     if (ret)
123         return ret;
124     TRACE_INIT_CREDS_AS_KEY_GAK(context, &ctx->as_key);
125 
126     ret = krb5int_fast_reply_key(context, strengthen_key, &ctx->as_key, &key);
127     if (ret)
128         return ret;
129     ret = krb5_kdc_rep_decrypt_proc(context, &key, NULL, ctx->reply);
130     if (ret) {
131         krb5_free_keyblock_contents(context, &key);
132         return ret;
133     }
134 
135     *key_out = key;
136     return 0;
137 }
138 
139 /**
140  * Fully anonymous replies include a pa_pkinit_kx padata type including the KDC
141  * contribution key.  This routine confirms that the session key is of the
142  * right form for fully anonymous requests.  It is here rather than in the
143  * preauth code because the session key cannot be verified until the AS reply
144  * is decrypted and the preauth code all runs before the AS reply is decrypted.
145  */
146 static krb5_error_code
verify_anonymous(krb5_context context,krb5_kdc_req * request,krb5_kdc_rep * reply,krb5_keyblock * as_key)147 verify_anonymous( krb5_context context, krb5_kdc_req *request,
148                   krb5_kdc_rep *reply, krb5_keyblock *as_key)
149 {
150     krb5_error_code ret = 0;
151     krb5_pa_data *pa;
152     krb5_data scratch;
153     krb5_keyblock *kdc_key = NULL, *expected = NULL;
154     krb5_enc_data *enc = NULL;
155     krb5_keyblock *session = reply->enc_part2->session;
156 
157     if (!krb5_principal_compare_any_realm(context, request->client,
158                                           krb5_anonymous_principal()))
159         return 0; /* Only applies to fully anonymous */
160     pa = krb5int_find_pa_data(context, reply->padata, KRB5_PADATA_PKINIT_KX);
161     if (pa == NULL)
162         goto verification_error;
163     scratch.length = pa->length;
164     scratch.data = (char *) pa->contents;
165     ret = decode_krb5_enc_data( &scratch, &enc);
166     if (ret)
167         goto cleanup;
168     scratch.data = k5alloc(enc->ciphertext.length, &ret);
169     if (ret)
170         goto cleanup;
171     scratch.length = enc->ciphertext.length;
172     ret = krb5_c_decrypt(context, as_key, KRB5_KEYUSAGE_PA_PKINIT_KX,
173                          NULL /*cipherstate*/, enc, &scratch);
174     if (ret) {
175         free(scratch.data);
176         goto cleanup;
177     }
178     ret = decode_krb5_encryption_key( &scratch, &kdc_key);
179     zap(scratch.data, scratch.length);
180     free(scratch.data);
181     if (ret)
182         goto cleanup;
183     ret = krb5_c_fx_cf2_simple(context, kdc_key, "PKINIT",
184                                as_key, "KEYEXCHANGE", &expected);
185     if (ret)
186         goto cleanup;
187     if ((expected->enctype != session->enctype) ||
188         (expected->length != session->length) ||
189         (memcmp(expected->contents, session->contents, expected->length) != 0))
190         goto verification_error;
191 cleanup:
192     if (kdc_key)
193         krb5_free_keyblock(context, kdc_key);
194     if (expected)
195         krb5_free_keyblock(context, expected);
196     if (enc)
197         krb5_free_enc_data(context, enc);
198     return ret;
199 verification_error:
200     ret = KRB5_KDCREP_MODIFIED;
201     k5_setmsg(context, ret,
202               _("Reply has wrong form of session key for anonymous request"));
203     goto cleanup;
204 }
205 
206 static krb5_error_code
verify_as_reply(krb5_context context,krb5_timestamp time_now,krb5_kdc_req * request,krb5_kdc_rep * as_reply)207 verify_as_reply(krb5_context            context,
208                 krb5_timestamp          time_now,
209                 krb5_kdc_req            *request,
210                 krb5_kdc_rep            *as_reply)
211 {
212     krb5_error_code             retval;
213     int                         canon_req;
214     int                         canon_ok;
215     krb5_timestamp              time_offset;
216 
217     /* check the contents for sanity: */
218     if (!as_reply->enc_part2->times.starttime)
219         as_reply->enc_part2->times.starttime =
220             as_reply->enc_part2->times.authtime;
221 
222     /*
223      * We only allow the AS-REP server name to be changed if the
224      * caller set the canonicalize flag (or requested an enterprise
225      * principal) and we requested (and received) a TGT.
226      */
227     canon_req = ((request->kdc_options & KDC_OPT_CANONICALIZE) != 0) ||
228         request->client->type == KRB5_NT_ENTERPRISE_PRINCIPAL ||
229         (request->kdc_options & KDC_OPT_REQUEST_ANONYMOUS);
230     if (canon_req) {
231         canon_ok = IS_TGS_PRINC(request->server) &&
232             IS_TGS_PRINC(as_reply->enc_part2->server);
233     } else
234         canon_ok = 0;
235 
236     if ((!canon_ok &&
237          !krb5_principal_compare(context, as_reply->enc_part2->server, request->server))
238         || (!canon_req && !krb5_principal_compare(context, as_reply->client, request->client))
239         || !krb5_principal_compare(context, as_reply->enc_part2->server, as_reply->ticket->server)
240         || (request->nonce != as_reply->enc_part2->nonce)
241         /* XXX check for extraneous flags */
242         /* XXX || (!krb5_addresses_compare(context, addrs, as_reply->enc_part2->caddrs)) */
243         || ((request->kdc_options & KDC_OPT_POSTDATED) &&
244             (request->from != 0) &&
245             (request->from != as_reply->enc_part2->times.starttime))
246         || ((request->till != 0) &&
247             ts_after(as_reply->enc_part2->times.endtime, request->till))
248         || ((request->kdc_options & KDC_OPT_RENEWABLE) &&
249             (request->rtime != 0) &&
250             ts_after(as_reply->enc_part2->times.renew_till, request->rtime))
251         || ((request->kdc_options & KDC_OPT_RENEWABLE_OK) &&
252             !(request->kdc_options & KDC_OPT_RENEWABLE) &&
253             (as_reply->enc_part2->flags & KDC_OPT_RENEWABLE) &&
254             (request->till != 0) &&
255             ts_after(as_reply->enc_part2->times.renew_till, request->till))
256     ) {
257         return KRB5_KDCREP_MODIFIED;
258     }
259 
260     if (context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) {
261         time_offset = ts_delta(as_reply->enc_part2->times.authtime, time_now);
262         retval = krb5_set_time_offsets(context, time_offset, 0);
263         if (retval)
264             return retval;
265     } else {
266         if ((request->from == 0) &&
267             !ts_within(as_reply->enc_part2->times.starttime, time_now,
268                        context->clockskew))
269             return (KRB5_KDCREP_SKEW);
270     }
271     return 0;
272 }
273 
274 static krb5_error_code
stash_as_reply(krb5_context context,krb5_kdc_rep * as_reply,krb5_creds * creds,krb5_ccache ccache)275 stash_as_reply(krb5_context             context,
276                krb5_kdc_rep             *as_reply,
277                krb5_creds *             creds,
278                krb5_ccache              ccache)
279 {
280     krb5_error_code             retval;
281     krb5_data *                 packet;
282     krb5_principal              client;
283     krb5_principal              server;
284 
285     client = NULL;
286     server = NULL;
287 
288     if (!creds->client)
289         if ((retval = krb5_copy_principal(context, as_reply->client, &client)))
290             goto cleanup;
291 
292     if (!creds->server)
293         if ((retval = krb5_copy_principal(context, as_reply->enc_part2->server,
294                                           &server)))
295             goto cleanup;
296 
297     /* fill in the credentials */
298     if ((retval = krb5_copy_keyblock_contents(context,
299                                               as_reply->enc_part2->session,
300                                               &creds->keyblock)))
301         goto cleanup;
302 
303     creds->times = as_reply->enc_part2->times;
304     creds->is_skey = FALSE;             /* this is an AS_REQ, so cannot
305                                            be encrypted in skey */
306     creds->ticket_flags = as_reply->enc_part2->flags;
307     if ((retval = krb5_copy_addresses(context, as_reply->enc_part2->caddrs,
308                                       &creds->addresses)))
309         goto cleanup;
310 
311     creds->second_ticket.length = 0;
312     creds->second_ticket.data = 0;
313 
314     if ((retval = encode_krb5_ticket(as_reply->ticket, &packet)))
315         goto cleanup;
316 
317     creds->ticket = *packet;
318     free(packet);
319 
320     /* store it in the ccache! */
321     if (ccache)
322         if ((retval = krb5_cc_store_cred(context, ccache, creds)))
323             goto cleanup;
324 
325     if (!creds->client)
326         creds->client = client;
327     if (!creds->server)
328         creds->server = server;
329 
330 cleanup:
331     if (retval) {
332         if (client)
333             krb5_free_principal(context, client);
334         if (server)
335             krb5_free_principal(context, server);
336         if (creds->keyblock.contents) {
337             memset(creds->keyblock.contents, 0,
338                    creds->keyblock.length);
339             free(creds->keyblock.contents);
340             creds->keyblock.contents = 0;
341             creds->keyblock.length = 0;
342         }
343         if (creds->ticket.data) {
344             free(creds->ticket.data);
345             creds->ticket.data = 0;
346         }
347         if (creds->addresses) {
348             krb5_free_addresses(context, creds->addresses);
349             creds->addresses = 0;
350         }
351     }
352     return (retval);
353 }
354 
355 static krb5_error_code
make_preauth_list(krb5_context context,krb5_preauthtype * ptypes,int nptypes,krb5_pa_data *** ret_list)356 make_preauth_list(krb5_context  context,
357                   krb5_preauthtype *    ptypes,
358                   int                   nptypes,
359                   krb5_pa_data ***      ret_list)
360 {
361     krb5_preauthtype *          ptypep;
362     krb5_pa_data **             preauthp;
363     int                         i;
364 
365     if (nptypes < 0) {
366         for (nptypes=0, ptypep = ptypes; *ptypep; ptypep++, nptypes++)
367             ;
368     }
369 
370     /* allocate space for a NULL to terminate the list */
371 
372     if ((preauthp =
373          (krb5_pa_data **) malloc((nptypes+1)*sizeof(krb5_pa_data *))) == NULL)
374         return(ENOMEM);
375 
376     for (i=0; i<nptypes; i++) {
377         if ((preauthp[i] =
378              (krb5_pa_data *) malloc(sizeof(krb5_pa_data))) == NULL) {
379             for (; i>=0; i--)
380                 free(preauthp[i]);
381             free(preauthp);
382             return (ENOMEM);
383         }
384         preauthp[i]->magic = KV5M_PA_DATA;
385         preauthp[i]->pa_type = ptypes[i];
386         preauthp[i]->length = 0;
387         preauthp[i]->contents = 0;
388     }
389 
390     /* fill in the terminating NULL */
391 
392     preauthp[nptypes] = NULL;
393 
394     *ret_list = preauthp;
395     return 0;
396 }
397 
398 #define MAX_IN_TKT_LOOPS 16
399 
400 /* Sort a pa_data sequence so that types named in the "preferred_preauth_types"
401  * libdefaults entry are listed before any others. */
402 static krb5_error_code
sort_krb5_padata_sequence(krb5_context context,krb5_data * realm,krb5_pa_data ** padata)403 sort_krb5_padata_sequence(krb5_context context, krb5_data *realm,
404                           krb5_pa_data **padata)
405 {
406     int i, j, base;
407     krb5_error_code ret;
408     const char *p;
409     long l;
410     char *q, *preauth_types = NULL;
411     krb5_pa_data *tmp;
412     int need_free_string = 1;
413 
414     if ((padata == NULL) || (padata[0] == NULL)) {
415         return 0;
416     }
417 
418     ret = krb5int_libdefault_string(context, realm, KRB5_CONF_PREFERRED_PREAUTH_TYPES,
419                                     &preauth_types);
420     if ((ret != 0) || (preauth_types == NULL)) {
421         /* Try to use PKINIT first. */
422         preauth_types = "17, 16, 15, 14";
423         need_free_string = 0;
424     }
425 
426 #ifdef DEBUG
427     fprintf (stderr, "preauth data types before sorting:");
428     for (i = 0; padata[i]; i++) {
429         fprintf (stderr, " %d", padata[i]->pa_type);
430     }
431     fprintf (stderr, "\n");
432 #endif
433 
434     base = 0;
435     for (p = preauth_types; *p != '\0';) {
436         /* skip whitespace to find an entry */
437         p += strspn(p, ", ");
438         if (*p != '\0') {
439             /* see if we can extract a number */
440             l = strtol(p, &q, 10);
441             if ((q != NULL) && (q > p)) {
442                 /* got a valid number; search for a matching entry */
443                 for (i = base; padata[i] != NULL; i++) {
444                     /* bubble the matching entry to the front of the list */
445                     if (padata[i]->pa_type == l) {
446                         tmp = padata[i];
447                         for (j = i; j > base; j--)
448                             padata[j] = padata[j - 1];
449                         padata[base] = tmp;
450                         base++;
451                         break;
452                     }
453                 }
454                 p = q;
455             } else {
456                 break;
457             }
458         }
459     }
460     if (need_free_string)
461         free(preauth_types);
462 
463 #ifdef DEBUG
464     fprintf (stderr, "preauth data types after sorting:");
465     for (i = 0; padata[i]; i++)
466         fprintf (stderr, " %d", padata[i]->pa_type);
467     fprintf (stderr, "\n");
468 #endif
469 
470     return 0;
471 }
472 
473 static krb5_error_code
build_in_tkt_name(krb5_context context,const char * in_tkt_service,krb5_const_principal client,krb5_principal * server_out)474 build_in_tkt_name(krb5_context context,
475                   const char *in_tkt_service,
476                   krb5_const_principal client,
477                   krb5_principal *server_out)
478 {
479     krb5_error_code ret;
480     krb5_principal server = NULL;
481 
482     *server_out = NULL;
483 
484     if (in_tkt_service) {
485         ret = krb5_parse_name_flags(context, in_tkt_service,
486                                     KRB5_PRINCIPAL_PARSE_IGNORE_REALM,
487                                     &server);
488         if (ret)
489             return ret;
490         krb5_free_data_contents(context, &server->realm);
491         ret = krb5int_copy_data_contents(context, &client->realm,
492                                          &server->realm);
493         if (ret) {
494             krb5_free_principal(context, server);
495             return ret;
496         }
497     } else {
498         ret = krb5_build_principal_ext(context, &server,
499                                        client->realm.length,
500                                        client->realm.data,
501                                        KRB5_TGS_NAME_SIZE,
502                                        KRB5_TGS_NAME,
503                                        client->realm.length,
504                                        client->realm.data,
505                                        0);
506         if (ret)
507             return ret;
508     }
509 
510     *server_out = server;
511     return 0;
512 }
513 
514 void KRB5_CALLCONV
krb5_init_creds_free(krb5_context context,krb5_init_creds_context ctx)515 krb5_init_creds_free(krb5_context context,
516                      krb5_init_creds_context ctx)
517 {
518     if (ctx == NULL)
519         return;
520 
521     k5_response_items_free(ctx->rctx.items);
522     free(ctx->in_tkt_service);
523     zapfree(ctx->gakpw.storage.data, ctx->gakpw.storage.length);
524     k5_preauth_request_context_fini(context, ctx);
525     krb5_free_error(context, ctx->err_reply);
526     krb5_free_pa_data(context, ctx->err_padata);
527     krb5_free_cred_contents(context, &ctx->cred);
528     krb5_free_kdc_req(context, ctx->request);
529     krb5_free_kdc_rep(context, ctx->reply);
530     krb5_free_data(context, ctx->outer_request_body);
531     krb5_free_data(context, ctx->inner_request_body);
532     krb5_free_data(context, ctx->encoded_previous_request);
533     krb5int_fast_free_state(context, ctx->fast_state);
534     krb5_free_pa_data(context, ctx->optimistic_padata);
535     krb5_free_pa_data(context, ctx->method_padata);
536     krb5_free_pa_data(context, ctx->more_padata);
537     krb5_free_data_contents(context, &ctx->salt);
538     krb5_free_data_contents(context, &ctx->s2kparams);
539     krb5_free_keyblock_contents(context, &ctx->as_key);
540     k5_json_release(ctx->cc_config_in);
541     k5_json_release(ctx->cc_config_out);
542     free(ctx);
543 }
544 
545 krb5_error_code
k5_init_creds_get(krb5_context context,krb5_init_creds_context ctx,krb5_boolean use_primary,struct kdclist * kdcs)546 k5_init_creds_get(krb5_context context, krb5_init_creds_context ctx,
547                   krb5_boolean use_primary, struct kdclist *kdcs)
548 {
549     krb5_error_code code;
550     krb5_data request;
551     krb5_data reply;
552     krb5_data realm;
553     unsigned int flags = 0;
554     int no_udp = 0;
555 
556     request.length = 0;
557     request.data = NULL;
558     reply.length = 0;
559     reply.data = NULL;
560     realm.length = 0;
561     realm.data = NULL;
562 
563     for (;;) {
564         code = krb5_init_creds_step(context,
565                                     ctx,
566                                     &reply,
567                                     &request,
568                                     &realm,
569                                     &flags);
570         if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !no_udp) {
571             TRACE_INIT_CREDS_RETRY_TCP(context);
572             no_udp = 1;
573         } else if (code != 0 || !(flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE))
574             break;
575 
576         krb5_free_data_contents(context, &reply);
577 
578         code = k5_sendto_kdc(context, &request, &realm, use_primary, no_udp,
579                              &reply, kdcs);
580         if (code != 0)
581             break;
582 
583         krb5_free_data_contents(context, &request);
584         krb5_free_data_contents(context, &realm);
585     }
586 
587     krb5_free_data_contents(context, &request);
588     krb5_free_data_contents(context, &reply);
589     krb5_free_data_contents(context, &realm);
590 
591     return code;
592 }
593 
594 /* Heimdal API */
595 krb5_error_code KRB5_CALLCONV
krb5_init_creds_get(krb5_context context,krb5_init_creds_context ctx)596 krb5_init_creds_get(krb5_context context,
597                     krb5_init_creds_context ctx)
598 {
599     return k5_init_creds_get(context, ctx, FALSE, NULL);
600 }
601 
602 krb5_error_code KRB5_CALLCONV
krb5_init_creds_get_creds(krb5_context context,krb5_init_creds_context ctx,krb5_creds * creds)603 krb5_init_creds_get_creds(krb5_context context,
604                           krb5_init_creds_context ctx,
605                           krb5_creds *creds)
606 {
607     if (!ctx->complete)
608         return KRB5_NO_TKT_SUPPLIED;
609 
610     return k5_copy_creds_contents(context, &ctx->cred, creds);
611 }
612 
613 krb5_error_code KRB5_CALLCONV
krb5_init_creds_get_times(krb5_context context,krb5_init_creds_context ctx,krb5_ticket_times * times)614 krb5_init_creds_get_times(krb5_context context,
615                           krb5_init_creds_context ctx,
616                           krb5_ticket_times *times)
617 {
618     if (!ctx->complete)
619         return KRB5_NO_TKT_SUPPLIED;
620 
621     *times = ctx->cred.times;
622 
623     return 0;
624 }
625 
626 krb5_error_code KRB5_CALLCONV
krb5_init_creds_get_error(krb5_context context,krb5_init_creds_context ctx,krb5_error ** error)627 krb5_init_creds_get_error(krb5_context context,
628                           krb5_init_creds_context ctx,
629                           krb5_error **error)
630 {
631     krb5_error_code code;
632     krb5_error *ret = NULL;
633 
634     *error = NULL;
635 
636     if (ctx->err_reply == NULL)
637         return 0;
638 
639     ret = k5alloc(sizeof(*ret), &code);
640     if (code != 0)
641         goto cleanup;
642 
643     ret->magic = KV5M_ERROR;
644     ret->ctime = ctx->err_reply->ctime;
645     ret->cusec = ctx->err_reply->cusec;
646     ret->susec = ctx->err_reply->susec;
647     ret->stime = ctx->err_reply->stime;
648     ret->error = ctx->err_reply->error;
649 
650     if (ctx->err_reply->client != NULL) {
651         code = krb5_copy_principal(context, ctx->err_reply->client,
652                                    &ret->client);
653         if (code != 0)
654             goto cleanup;
655     }
656 
657     code = krb5_copy_principal(context, ctx->err_reply->server, &ret->server);
658     if (code != 0)
659         goto cleanup;
660 
661     code = krb5int_copy_data_contents(context, &ctx->err_reply->text,
662                                       &ret->text);
663     if (code != 0)
664         goto cleanup;
665 
666     code = krb5int_copy_data_contents(context, &ctx->err_reply->e_data,
667                                       &ret->e_data);
668     if (code != 0)
669         goto cleanup;
670 
671     *error = ret;
672 
673 cleanup:
674     if (code != 0)
675         krb5_free_error(context, ret);
676 
677     return code;
678 }
679 
680 /* Return the current time, possibly using the offset from a previously
681  * received preauth-required error. */
682 krb5_error_code
k5_init_creds_current_time(krb5_context context,krb5_init_creds_context ctx,krb5_boolean allow_unauth,krb5_timestamp * time_out,krb5_int32 * usec_out)683 k5_init_creds_current_time(krb5_context context, krb5_init_creds_context ctx,
684                            krb5_boolean allow_unauth, krb5_timestamp *time_out,
685                            krb5_int32 *usec_out)
686 {
687     if (ctx->pa_offset_state != NO_OFFSET &&
688         (allow_unauth || ctx->pa_offset_state == AUTH_OFFSET) &&
689         (context->library_options & KRB5_LIBOPT_SYNC_KDCTIME)) {
690         /* Use the offset we got from a preauth-required error. */
691         return k5_time_with_offset(ctx->pa_offset, ctx->pa_offset_usec,
692                                    time_out, usec_out);
693     } else {
694         /* Use the time offset from the context, or no offset. */
695         return krb5_us_timeofday(context, time_out, usec_out);
696     }
697 }
698 
699 /* Set the timestamps for ctx->request based on the desired lifetimes. */
700 static krb5_error_code
set_request_times(krb5_context context,krb5_init_creds_context ctx)701 set_request_times(krb5_context context, krb5_init_creds_context ctx)
702 {
703     krb5_error_code code;
704     krb5_timestamp from, now;
705     krb5_int32 now_ms;
706 
707     code = k5_init_creds_current_time(context, ctx, TRUE, &now, &now_ms);
708     if (code != 0)
709         return code;
710 
711     /* Omit request start time unless the caller explicitly asked for one. */
712     from = ts_incr(now, ctx->start_time);
713     if (ctx->start_time != 0)
714         ctx->request->from = from;
715 
716     ctx->request->till = ts_incr(from, ctx->tkt_life);
717 
718     if (ctx->renew_life > 0) {
719         /* Don't ask for a smaller renewable time than the lifetime. */
720         ctx->request->rtime = ts_incr(from, ctx->renew_life);
721         if (ts_after(ctx->request->till, ctx->request->rtime))
722             ctx->request->rtime = ctx->request->till;
723         ctx->request->kdc_options &= ~KDC_OPT_RENEWABLE_OK;
724     } else {
725         ctx->request->rtime = 0;
726     }
727 
728     return 0;
729 }
730 
731 static void
read_allowed_preauth_type(krb5_context context,krb5_init_creds_context ctx)732 read_allowed_preauth_type(krb5_context context, krb5_init_creds_context ctx)
733 {
734     krb5_error_code ret;
735     krb5_data config;
736     char *tmp, *p;
737     krb5_ccache in_ccache = k5_gic_opt_get_in_ccache(ctx->opt);
738 
739     ctx->allowed_preauth_type = KRB5_PADATA_NONE;
740     if (in_ccache == NULL)
741         return;
742     memset(&config, 0, sizeof(config));
743     if (krb5_cc_get_config(context, in_ccache, ctx->request->server,
744                            KRB5_CC_CONF_PA_TYPE, &config) != 0)
745         return;
746     tmp = k5memdup0(config.data, config.length, &ret);
747     krb5_free_data_contents(context, &config);
748     if (tmp == NULL)
749         return;
750     ctx->allowed_preauth_type = strtol(tmp, &p, 10);
751     if (p == NULL || *p != '\0')
752         ctx->allowed_preauth_type = KRB5_PADATA_NONE;
753     free(tmp);
754 }
755 
756 /* Return true if encrypted timestamp is disabled for realm. */
757 static krb5_boolean
encts_disabled(profile_t profile,const krb5_data * realm)758 encts_disabled(profile_t profile, const krb5_data *realm)
759 {
760     krb5_error_code ret;
761     char *realmstr;
762     int bval;
763 
764     realmstr = k5memdup0(realm->data, realm->length, &ret);
765     if (realmstr == NULL)
766         return FALSE;
767     ret = profile_get_boolean(profile, KRB5_CONF_REALMS, realmstr,
768                               KRB5_CONF_DISABLE_ENCRYPTED_TIMESTAMP, FALSE,
769                               &bval);
770     free(realmstr);
771     return (ret == 0) ? bval : FALSE;
772 }
773 
774 /**
775  * Throw away any pre-authentication realm state and begin with a
776  * unauthenticated or optimistically authenticated request.  If fast_upgrade is
777  * set, use FAST for this request.
778  */
779 static krb5_error_code
restart_init_creds_loop(krb5_context context,krb5_init_creds_context ctx,krb5_boolean fast_upgrade)780 restart_init_creds_loop(krb5_context context, krb5_init_creds_context ctx,
781                         krb5_boolean fast_upgrade)
782 {
783     krb5_error_code code = 0;
784 
785     krb5_free_pa_data(context, ctx->optimistic_padata);
786     krb5_free_pa_data(context, ctx->method_padata);
787     krb5_free_pa_data(context, ctx->more_padata);
788     krb5_free_pa_data(context, ctx->err_padata);
789     krb5_free_error(context, ctx->err_reply);
790     ctx->optimistic_padata = ctx->method_padata = ctx->more_padata = NULL;
791     ctx->err_padata = NULL;
792     ctx->err_reply = NULL;
793     ctx->selected_preauth_type = KRB5_PADATA_NONE;
794 
795     krb5int_fast_free_state(context, ctx->fast_state);
796     ctx->fast_state = NULL;
797     code = krb5int_fast_make_state(context, &ctx->fast_state);
798     if (code != 0)
799         goto cleanup;
800     if (fast_upgrade)
801         ctx->fast_state->fast_state_flags |= KRB5INT_FAST_DO_FAST;
802 
803     k5_preauth_request_context_fini(context, ctx);
804     k5_preauth_request_context_init(context, ctx);
805     krb5_free_data(context, ctx->outer_request_body);
806     ctx->outer_request_body = NULL;
807     if (ctx->opt->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) {
808         code = make_preauth_list(context, ctx->opt->preauth_list,
809                                  ctx->opt->preauth_list_length,
810                                  &ctx->optimistic_padata);
811         if (code)
812             goto cleanup;
813     }
814 
815     /* Never set encts_disabled back to false, so it can't be circumvented with
816      * client realm referrals. */
817     if (encts_disabled(context->profile, &ctx->request->client->realm))
818         ctx->encts_disabled = TRUE;
819 
820     krb5_free_principal(context, ctx->request->server);
821     ctx->request->server = NULL;
822 
823     code = build_in_tkt_name(context, ctx->in_tkt_service,
824                              ctx->request->client,
825                              &ctx->request->server);
826     if (code != 0)
827         goto cleanup;
828 
829     code = krb5int_fast_as_armor(context, ctx->fast_state, ctx->opt,
830                                  ctx->request);
831     if (code != 0)
832         goto cleanup;
833     /* give the preauth plugins a chance to prep the request body */
834     k5_preauth_prepare_request(context, ctx->opt, ctx->request);
835 
836     code = krb5int_fast_prep_req_body(context, ctx->fast_state,
837                                       ctx->request,
838                                       &ctx->outer_request_body);
839     if (code != 0)
840         goto cleanup;
841 
842     /* Read the allowed preauth type for this server principal from the input
843      * ccache, if the application supplied one. */
844     read_allowed_preauth_type(context, ctx);
845 
846 cleanup:
847     return code;
848 }
849 
850 krb5_error_code KRB5_CALLCONV
krb5_init_creds_init(krb5_context context,krb5_principal client,krb5_prompter_fct prompter,void * data,krb5_deltat start_time,krb5_get_init_creds_opt * opt,krb5_init_creds_context * pctx)851 krb5_init_creds_init(krb5_context context,
852                      krb5_principal client,
853                      krb5_prompter_fct prompter,
854                      void *data,
855                      krb5_deltat start_time,
856                      krb5_get_init_creds_opt *opt,
857                      krb5_init_creds_context *pctx)
858 {
859     krb5_error_code code;
860     krb5_init_creds_context ctx;
861     int tmp;
862     char *str = NULL;
863 
864     TRACE_INIT_CREDS(context, client);
865 
866     ctx = k5alloc(sizeof(*ctx), &code);
867     if (code != 0)
868         goto cleanup;
869 
870     ctx->request = k5alloc(sizeof(krb5_kdc_req), &code);
871     if (code != 0)
872         goto cleanup;
873     ctx->info_pa_permitted = TRUE;
874     code = krb5_copy_principal(context, client, &ctx->request->client);
875     if (code != 0)
876         goto cleanup;
877 
878     ctx->prompter = prompter;
879     ctx->prompter_data = data;
880     ctx->gak_fct = krb5_get_as_key_password;
881     ctx->gak_data = &ctx->gakpw;
882 
883     ctx->start_time = start_time;
884 
885     if (opt == NULL) {
886         ctx->opt = &ctx->opt_storage;
887         krb5_get_init_creds_opt_init(ctx->opt);
888     } else {
889         ctx->opt = opt;
890     }
891 
892     code = k5_response_items_new(&ctx->rctx.items);
893     if (code != 0)
894         goto cleanup;
895 
896     /* Initialise request parameters as per krb5_get_init_creds() */
897     ctx->request->kdc_options = context->kdc_default_options;
898 
899     /* forwardable */
900     if (ctx->opt->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)
901         tmp = ctx->opt->forwardable;
902     else if (krb5int_libdefault_boolean(context, &ctx->request->client->realm,
903                                         KRB5_CONF_FORWARDABLE, &tmp) == 0)
904         ;
905     else
906         tmp = 0;
907     if (tmp)
908         ctx->request->kdc_options |= KDC_OPT_FORWARDABLE;
909 
910     /* proxiable */
911     if (ctx->opt->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)
912         tmp = ctx->opt->proxiable;
913     else if (krb5int_libdefault_boolean(context, &ctx->request->client->realm,
914                                         KRB5_CONF_PROXIABLE, &tmp) == 0)
915         ;
916     else
917         tmp = 0;
918     if (tmp)
919         ctx->request->kdc_options |= KDC_OPT_PROXIABLE;
920 
921     /* canonicalize */
922     if (ctx->opt->flags & KRB5_GET_INIT_CREDS_OPT_CANONICALIZE)
923         tmp = 1;
924     else if (krb5int_libdefault_boolean(context, &ctx->request->client->realm,
925                                         KRB5_CONF_CANONICALIZE, &tmp) == 0)
926         ;
927     else
928         tmp = 0;
929     if (tmp)
930         ctx->request->kdc_options |= KDC_OPT_CANONICALIZE;
931 
932     /* allow_postdate */
933     if (ctx->start_time > 0)
934         ctx->request->kdc_options |= KDC_OPT_ALLOW_POSTDATE | KDC_OPT_POSTDATED;
935 
936     /* ticket lifetime */
937     if (ctx->opt->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)
938         ctx->tkt_life = ctx->opt->tkt_life;
939     else if (krb5int_libdefault_string(context, &ctx->request->client->realm,
940                                        KRB5_CONF_TICKET_LIFETIME, &str) == 0) {
941         code = krb5_string_to_deltat(str, &ctx->tkt_life);
942         if (code != 0)
943             goto cleanup;
944         free(str);
945         str = NULL;
946     } else
947         ctx->tkt_life = 24 * 60 * 60; /* previously hardcoded in kinit */
948 
949     /* renewable lifetime */
950     if (ctx->opt->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE)
951         ctx->renew_life = ctx->opt->renew_life;
952     else if (krb5int_libdefault_string(context, &ctx->request->client->realm,
953                                        KRB5_CONF_RENEW_LIFETIME, &str) == 0) {
954         code = krb5_string_to_deltat(str, &ctx->renew_life);
955         if (code != 0)
956             goto cleanup;
957         free(str);
958         str = NULL;
959     } else
960         ctx->renew_life = 0;
961 
962     if (ctx->renew_life > 0)
963         ctx->request->kdc_options |= KDC_OPT_RENEWABLE;
964 
965     /* enctypes */
966     if (ctx->opt->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) {
967         ctx->request->ktype =
968             k5memdup(ctx->opt->etype_list,
969                      ctx->opt->etype_list_length * sizeof(krb5_enctype),
970                      &code);
971         if (code != 0)
972             goto cleanup;
973         ctx->request->nktypes = ctx->opt->etype_list_length;
974     } else if (krb5_get_default_in_tkt_ktypes(context,
975                                               &ctx->request->ktype) == 0) {
976         ctx->request->nktypes = k5_count_etypes(ctx->request->ktype);
977     } else {
978         /* there isn't any useful default here. */
979         code = KRB5_CONFIG_ETYPE_NOSUPP;
980         goto cleanup;
981     }
982 
983     /*
984      * Set a default enctype for optimistic preauth.  If we're not doing
985      * optimistic preauth, this should ordinarily get overwritten when we
986      * process the etype-info2 of the preauth-required error.
987      */
988     if (ctx->request->nktypes > 0)
989         ctx->etype = ctx->request->ktype[0];
990 
991     /* addresses */
992     if (ctx->opt->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) {
993         code = krb5_copy_addresses(context, ctx->opt->address_list,
994                                    &ctx->request->addresses);
995         if (code != 0)
996             goto cleanup;
997     } else if (krb5int_libdefault_boolean(context, &ctx->request->client->realm,
998                                           KRB5_CONF_NOADDRESSES, &tmp) != 0
999                || tmp) {
1000         ctx->request->addresses = NULL;
1001     } else {
1002         code = krb5_os_localaddr(context, &ctx->request->addresses);
1003         if (code != 0)
1004             goto cleanup;
1005     }
1006 
1007     if (ctx->opt->flags & KRB5_GET_INIT_CREDS_OPT_SALT) {
1008         code = krb5int_copy_data_contents(context, ctx->opt->salt, &ctx->salt);
1009         if (code != 0)
1010             goto cleanup;
1011         ctx->default_salt = FALSE;
1012     } else {
1013         ctx->salt = empty_data();
1014         ctx->default_salt = TRUE;
1015     }
1016 
1017     /* Anonymous. */
1018     if (ctx->opt->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS) {
1019         ctx->request->kdc_options |= KDC_OPT_REQUEST_ANONYMOUS;
1020         /* Remap @REALM to WELLKNOWN/ANONYMOUS@REALM. */
1021         if (client->length == 1 && client->data[0].length ==0) {
1022             krb5_principal new_client;
1023             code = krb5_build_principal_ext(context, &new_client,
1024                                             client->realm.length,
1025                                             client->realm.data,
1026                                             strlen(KRB5_WELLKNOWN_NAMESTR),
1027                                             KRB5_WELLKNOWN_NAMESTR,
1028                                             strlen(KRB5_ANONYMOUS_PRINCSTR),
1029                                             KRB5_ANONYMOUS_PRINCSTR,
1030                                             0);
1031             if (code)
1032                 goto cleanup;
1033             krb5_free_principal(context, ctx->request->client);
1034             ctx->request->client = new_client;
1035             ctx->request->client->type = KRB5_NT_WELLKNOWN;
1036         }
1037     }
1038     /* We will also handle anonymous if the input principal is the anonymous
1039      * principal. */
1040     if (krb5_principal_compare_any_realm(context, ctx->request->client,
1041                                          krb5_anonymous_principal())) {
1042         ctx->request->kdc_options |= KDC_OPT_REQUEST_ANONYMOUS;
1043         ctx->request->client->type = KRB5_NT_WELLKNOWN;
1044     }
1045 
1046     *pctx = ctx;
1047     ctx = NULL;
1048 
1049 cleanup:
1050     krb5_init_creds_free(context, ctx);
1051     free(str);
1052 
1053     return code;
1054 }
1055 
1056 krb5_error_code KRB5_CALLCONV
krb5_init_creds_set_service(krb5_context context,krb5_init_creds_context ctx,const char * service)1057 krb5_init_creds_set_service(krb5_context context,
1058                             krb5_init_creds_context ctx,
1059                             const char *service)
1060 {
1061     char *s;
1062 
1063     TRACE_INIT_CREDS_SERVICE(context, service);
1064 
1065     s = strdup(service);
1066     if (s == NULL)
1067         return ENOMEM;
1068 
1069     free(ctx->in_tkt_service);
1070     ctx->in_tkt_service = s;
1071 
1072     return restart_init_creds_loop(context, ctx, FALSE);
1073 }
1074 
1075 static krb5_error_code
init_creds_validate_reply(krb5_context context,krb5_init_creds_context ctx,krb5_data * reply)1076 init_creds_validate_reply(krb5_context context,
1077                           krb5_init_creds_context ctx,
1078                           krb5_data *reply)
1079 {
1080     krb5_error_code code;
1081     krb5_error *error = NULL;
1082     krb5_kdc_rep *as_reply = NULL;
1083 
1084     krb5_free_error(context, ctx->err_reply);
1085     ctx->err_reply = NULL;
1086 
1087     krb5_free_kdc_rep(context, ctx->reply);
1088     ctx->reply = NULL;
1089 
1090     if (krb5_is_krb_error(reply)) {
1091         code = decode_krb5_error(reply, &error);
1092         if (code != 0)
1093             return code;
1094 
1095         assert(error != NULL);
1096 
1097         TRACE_INIT_CREDS_ERROR_REPLY(context,
1098                                      error->error + ERROR_TABLE_BASE_krb5);
1099         if (error->error == KRB_ERR_RESPONSE_TOO_BIG) {
1100             krb5_free_error(context, error);
1101             return KRB5KRB_ERR_RESPONSE_TOO_BIG;
1102         } else {
1103             ctx->err_reply = error;
1104             return 0;
1105         }
1106     }
1107 
1108     /*
1109      * Check to make sure it isn't a V4 reply.
1110      */
1111     if (reply->length != 0 && !krb5_is_as_rep(reply)) {
1112 /* these are in <kerberosIV/prot.h> as well but it isn't worth including. */
1113 #define V4_KRB_PROT_VERSION     4
1114 #define V4_AUTH_MSG_ERR_REPLY   (5<<1)
1115         /* check here for V4 reply */
1116         unsigned int t_switch;
1117 
1118         /* From v4 g_in_tkt.c: This used to be
1119            switch (pkt_msg_type(rpkt) & ~1) {
1120            but SCO 3.2v4 cc compiled that incorrectly.  */
1121         t_switch = reply->data[1];
1122         t_switch &= ~1;
1123 
1124         if (t_switch == V4_AUTH_MSG_ERR_REPLY
1125             && reply->data[0] == V4_KRB_PROT_VERSION) {
1126             code = KRB5KRB_AP_ERR_V4_REPLY;
1127         } else {
1128             code = KRB5KRB_AP_ERR_MSG_TYPE;
1129         }
1130         return code;
1131     }
1132 
1133     /* It must be a KRB_AS_REP message, or an bad returned packet */
1134     code = decode_krb5_as_rep(reply, &as_reply);
1135     if (code != 0)
1136         return code;
1137 
1138     if (as_reply->msg_type != KRB5_AS_REP) {
1139         krb5_free_kdc_rep(context, as_reply);
1140         return KRB5KRB_AP_ERR_MSG_TYPE;
1141     }
1142 
1143     ctx->reply = as_reply;
1144 
1145     return 0;
1146 }
1147 
1148 static krb5_error_code
save_selected_preauth_type(krb5_context context,krb5_ccache ccache,krb5_init_creds_context ctx)1149 save_selected_preauth_type(krb5_context context, krb5_ccache ccache,
1150                            krb5_init_creds_context ctx)
1151 {
1152     krb5_data config_data;
1153     char *tmp;
1154     krb5_error_code code;
1155 
1156     if (ctx->selected_preauth_type == KRB5_PADATA_NONE)
1157         return 0;
1158     if (asprintf(&tmp, "%ld", (long)ctx->selected_preauth_type) < 0)
1159         return ENOMEM;
1160     config_data = string2data(tmp);
1161     code = krb5_cc_set_config(context, ccache, ctx->cred.server,
1162                               KRB5_CC_CONF_PA_TYPE, &config_data);
1163     free(tmp);
1164     return code;
1165 }
1166 
1167 static krb5_error_code
clear_cc_config_out_data(krb5_context context,krb5_init_creds_context ctx)1168 clear_cc_config_out_data(krb5_context context, krb5_init_creds_context ctx)
1169 {
1170     k5_json_release(ctx->cc_config_out);
1171     ctx->cc_config_out = NULL;
1172     return k5_json_object_create(&ctx->cc_config_out);
1173 }
1174 
1175 static krb5_error_code
read_cc_config_in_data(krb5_context context,krb5_init_creds_context ctx)1176 read_cc_config_in_data(krb5_context context, krb5_init_creds_context ctx)
1177 {
1178     k5_json_value val;
1179     krb5_data config;
1180     char *encoded;
1181     krb5_error_code code;
1182     krb5_ccache in_ccache = k5_gic_opt_get_in_ccache(ctx->opt);
1183 
1184     k5_json_release(ctx->cc_config_in);
1185     ctx->cc_config_in = NULL;
1186 
1187     if (in_ccache == NULL)
1188         return 0;
1189 
1190     memset(&config, 0, sizeof(config));
1191     code = krb5_cc_get_config(context, in_ccache, ctx->request->server,
1192                               KRB5_CC_CONF_PA_CONFIG_DATA, &config);
1193     if (code)
1194         return code;
1195 
1196     encoded = k5memdup0(config.data, config.length, &code);
1197     krb5_free_data_contents(context, &config);
1198     if (encoded == NULL)
1199         return ENOMEM;
1200 
1201     code = k5_json_decode(encoded, &val);
1202     free(encoded);
1203     if (code)
1204         return code;
1205     if (k5_json_get_tid(val) != K5_JSON_TID_OBJECT) {
1206         k5_json_release(val);
1207         return EINVAL;
1208     }
1209     ctx->cc_config_in = val;
1210     return 0;
1211 }
1212 
1213 static krb5_error_code
save_cc_config_out_data(krb5_context context,krb5_ccache ccache,krb5_init_creds_context ctx)1214 save_cc_config_out_data(krb5_context context, krb5_ccache ccache,
1215                         krb5_init_creds_context ctx)
1216 {
1217     krb5_data config;
1218     char *encoded;
1219     krb5_error_code code;
1220 
1221     if (ctx->cc_config_out == NULL ||
1222         k5_json_object_count(ctx->cc_config_out) == 0)
1223         return 0;
1224     code = k5_json_encode(ctx->cc_config_out, &encoded);
1225     if (code)
1226         return code;
1227     config = string2data(encoded);
1228     code = krb5_cc_set_config(context, ccache, ctx->cred.server,
1229                               KRB5_CC_CONF_PA_CONFIG_DATA, &config);
1230     free(encoded);
1231     return code;
1232 }
1233 
1234 /* Add a KERB-PA-PAC-REQUEST pa-data item if the gic options require one. */
1235 static krb5_error_code
maybe_add_pac_request(krb5_context context,krb5_init_creds_context ctx)1236 maybe_add_pac_request(krb5_context context, krb5_init_creds_context ctx)
1237 {
1238     krb5_error_code code;
1239     krb5_pa_pac_req pac_req;
1240     krb5_data *encoded;
1241     int val;
1242 
1243     val = k5_gic_opt_pac_request(ctx->opt);
1244     if (val == -1)
1245         return 0;
1246 
1247     pac_req.include_pac = val;
1248     code = encode_krb5_pa_pac_req(&pac_req, &encoded);
1249     if (code)
1250         return code;
1251     code = k5_add_pa_data_from_data(&ctx->request->padata,
1252                                     KRB5_PADATA_PAC_REQUEST, encoded);
1253     krb5_free_data(context, encoded);
1254     return code;
1255 }
1256 
1257 static krb5_error_code
init_creds_step_request(krb5_context context,krb5_init_creds_context ctx,krb5_data * out)1258 init_creds_step_request(krb5_context context,
1259                         krb5_init_creds_context ctx,
1260                         krb5_data *out)
1261 {
1262     krb5_error_code code;
1263     krb5_preauthtype pa_type;
1264     krb5_data copy;
1265     struct errinfo save = EMPTY_ERRINFO;
1266     uint32_t rcode = (ctx->err_reply == NULL) ? 0 : ctx->err_reply->error;
1267 
1268     if (ctx->loopcount >= MAX_IN_TKT_LOOPS) {
1269         code = KRB5_GET_IN_TKT_LOOP;
1270         goto cleanup;
1271     }
1272 
1273     /* RFC 6113 requires a new nonce for the inner request on each try. */
1274     code = k5_generate_nonce(context, &ctx->request->nonce);
1275     if (code != 0)
1276         goto cleanup;
1277 
1278     /* Reset the request timestamps, possibly adjusting to the KDC time. */
1279     code = set_request_times(context, ctx);
1280     if (code != 0)
1281         goto cleanup;
1282 
1283     krb5_free_data(context, ctx->inner_request_body);
1284     ctx->inner_request_body = NULL;
1285     code = encode_krb5_kdc_req_body(ctx->request, &ctx->inner_request_body);
1286     if (code)
1287         goto cleanup;
1288 
1289     /*
1290      * Read cached preauth configuration data for this server principal from
1291      * the in_ccache, if the application supplied one, and delete any that was
1292      * stored by a previous (clearly failed) module.
1293      */
1294     read_cc_config_in_data(context, ctx);
1295     clear_cc_config_out_data(context, ctx);
1296 
1297     ctx->request->padata = NULL;
1298     if (ctx->optimistic_padata != NULL) {
1299         /* Our first attempt, using an optimistic padata list. */
1300         TRACE_INIT_CREDS_PREAUTH_OPTIMISTIC(context);
1301         code = k5_preauth(context, ctx, ctx->optimistic_padata, TRUE,
1302                           &ctx->request->padata, &ctx->selected_preauth_type);
1303         krb5_free_pa_data(context, ctx->optimistic_padata);
1304         ctx->optimistic_padata = NULL;
1305         if (code) {
1306             /* Make an unauthenticated request. */
1307             krb5_clear_error_message(context);
1308             code = 0;
1309         }
1310     } else if (ctx->more_padata != NULL) {
1311         /* Continuing after KDC_ERR_MORE_PREAUTH_DATA_REQUIRED. */
1312         TRACE_INIT_CREDS_PREAUTH_MORE(context, ctx->selected_preauth_type);
1313         code = k5_preauth(context, ctx, ctx->more_padata, TRUE,
1314                           &ctx->request->padata, &pa_type);
1315     } else if (rcode == KDC_ERR_PREAUTH_FAILED) {
1316         /* Report the KDC-side failure code if we can't try another mech. */
1317         code = KRB5KDC_ERR_PREAUTH_FAILED;
1318     } else if (rcode && rcode != KDC_ERR_PREAUTH_REQUIRED) {
1319         /* Retrying after an error (possibly mechanism-specific), using error
1320          * padata to figure out what to change. */
1321         TRACE_INIT_CREDS_PREAUTH_TRYAGAIN(context, ctx->err_reply->error,
1322                                           ctx->selected_preauth_type);
1323         code = k5_preauth_tryagain(context, ctx, ctx->selected_preauth_type,
1324                                    ctx->err_reply, ctx->err_padata,
1325                                    &ctx->request->padata);
1326         if (code) {
1327             krb5_clear_error_message(context);
1328             code = ctx->err_reply->error + ERROR_TABLE_BASE_krb5;
1329         }
1330     }
1331     /* Don't continue after a keyboard interrupt. */
1332     if (code == KRB5_LIBOS_PWDINTR)
1333         goto cleanup;
1334     /* Don't continue if fallback is disabled. */
1335     if (code && ctx->fallback_disabled)
1336         goto cleanup;
1337     if (code) {
1338         /* See if we can try a different preauth mech before giving up. */
1339         k5_save_ctx_error(context, code, &save);
1340         ctx->selected_preauth_type = KRB5_PADATA_NONE;
1341     }
1342 
1343     if (ctx->request->padata == NULL && ctx->method_padata != NULL) {
1344         /* Retrying after KDC_ERR_PREAUTH_REQUIRED, or trying again with a
1345          * different mechanism after a failure. */
1346         TRACE_INIT_CREDS_PREAUTH(context);
1347         code = k5_preauth(context, ctx, ctx->method_padata, TRUE,
1348                           &ctx->request->padata, &ctx->selected_preauth_type);
1349         if (code) {
1350             if (save.code != 0)
1351                 code = k5_restore_ctx_error(context, &save);
1352             goto cleanup;
1353         }
1354     }
1355     if (ctx->request->padata == NULL)
1356         TRACE_INIT_CREDS_PREAUTH_NONE(context);
1357 
1358     /* Remember when we sent this request (after any preauth delay). */
1359     ctx->request_time = time(NULL);
1360 
1361     if (ctx->encoded_previous_request != NULL) {
1362         krb5_free_data(context, ctx->encoded_previous_request);
1363         ctx->encoded_previous_request = NULL;
1364     }
1365     if (ctx->info_pa_permitted) {
1366         code = k5_add_empty_pa_data(&ctx->request->padata,
1367                                     KRB5_PADATA_AS_FRESHNESS);
1368         if (code)
1369             goto cleanup;
1370         code = k5_add_empty_pa_data(&ctx->request->padata,
1371                                     KRB5_ENCPADATA_REQ_ENC_PA_REP);
1372     }
1373     if (code)
1374         goto cleanup;
1375 
1376     if (ctx->subject_cert != NULL) {
1377         code = krb5int_copy_data_contents(context, ctx->subject_cert, &copy);
1378         if (code)
1379             goto cleanup;
1380         code = k5_add_pa_data_from_data(&ctx->request->padata,
1381                                         KRB5_PADATA_S4U_X509_USER, &copy);
1382         krb5_free_data_contents(context, &copy);
1383         if (code)
1384             goto cleanup;
1385     }
1386 
1387     code = maybe_add_pac_request(context, ctx);
1388     if (code)
1389         goto cleanup;
1390 
1391     code = krb5int_fast_prep_req(context, ctx->fast_state,
1392                                  ctx->request, ctx->outer_request_body,
1393                                  encode_krb5_as_req,
1394                                  &ctx->encoded_previous_request);
1395     if (code != 0)
1396         goto cleanup;
1397 
1398     code = krb5int_copy_data_contents(context,
1399                                       ctx->encoded_previous_request,
1400                                       out);
1401     if (code != 0)
1402         goto cleanup;
1403 
1404 cleanup:
1405     krb5_free_pa_data(context, ctx->request->padata);
1406     ctx->request->padata = NULL;
1407     k5_clear_error(&save);
1408     return code;
1409 }
1410 
1411 /* Ensure that the reply enctype was among the requested enctypes. */
1412 static krb5_error_code
check_reply_enctype(krb5_init_creds_context ctx)1413 check_reply_enctype(krb5_init_creds_context ctx)
1414 {
1415     int i;
1416 
1417     for (i = 0; i < ctx->request->nktypes; i++) {
1418         if (ctx->request->ktype[i] == ctx->reply->enc_part.enctype)
1419             return 0;
1420     }
1421     return KRB5_CONFIG_ETYPE_NOSUPP;
1422 }
1423 
1424 /* Note the difference between the KDC's time, as reported to us in a
1425  * preauth-required error, and the current time. */
1426 static void
note_req_timestamp(krb5_context context,krb5_init_creds_context ctx,krb5_timestamp kdc_time,krb5_int32 kdc_usec)1427 note_req_timestamp(krb5_context context, krb5_init_creds_context ctx,
1428                    krb5_timestamp kdc_time, krb5_int32 kdc_usec)
1429 {
1430     krb5_timestamp now;
1431     krb5_int32 usec;
1432 
1433     if (k5_time_with_offset(0, 0, &now, &usec) != 0)
1434         return;
1435     ctx->pa_offset = ts_delta(kdc_time, now);
1436     ctx->pa_offset_usec = kdc_usec - usec;
1437     ctx->pa_offset_state = (ctx->fast_state->armor_key != NULL) ?
1438         AUTH_OFFSET : UNAUTH_OFFSET;
1439 }
1440 
1441 /*
1442  * Determine whether err is a client referral to another realm, given the
1443  * previously requested client principal name.
1444  *
1445  * RFC 6806 Section 7 requires that KDCs return the referral realm in an error
1446  * type WRONG_REALM, but Microsoft Windows Server 2003 (and possibly others)
1447  * return the realm in a PRINCIPAL_UNKNOWN message.
1448  */
1449 static krb5_boolean
is_referral(krb5_context context,krb5_error * err,krb5_principal client)1450 is_referral(krb5_context context, krb5_error *err, krb5_principal client)
1451 {
1452     if (err->error != KDC_ERR_WRONG_REALM &&
1453         err->error != KDC_ERR_C_PRINCIPAL_UNKNOWN)
1454         return FALSE;
1455     if (err->client == NULL)
1456         return FALSE;
1457     return !krb5_realm_compare(context, err->client, client);
1458 }
1459 
1460 /* Transfer error padata to method data in ctx and sort it according to
1461  * configuration. */
1462 static krb5_error_code
accept_method_data(krb5_context context,krb5_init_creds_context ctx)1463 accept_method_data(krb5_context context, krb5_init_creds_context ctx)
1464 {
1465     krb5_free_pa_data(context, ctx->method_padata);
1466     ctx->method_padata = ctx->err_padata;
1467     ctx->err_padata = NULL;
1468     return sort_krb5_padata_sequence(context, &ctx->request->client->realm,
1469                                      ctx->method_padata);
1470 }
1471 
1472 /* Return the password expiry time indicated by enc_part2.  Set *is_last_req
1473  * if the information came from a last_req value. */
1474 static void
get_expiry_times(krb5_enc_kdc_rep_part * enc_part2,krb5_timestamp * pw_exp,krb5_timestamp * acct_exp,krb5_boolean * is_last_req)1475 get_expiry_times(krb5_enc_kdc_rep_part *enc_part2, krb5_timestamp *pw_exp,
1476                  krb5_timestamp *acct_exp, krb5_boolean *is_last_req)
1477 {
1478     krb5_last_req_entry **last_req;
1479     krb5_int32 lr_type;
1480 
1481     *pw_exp = 0;
1482     *acct_exp = 0;
1483     *is_last_req = FALSE;
1484 
1485     /* Look for last-req entries for password or account expiration. */
1486     if (enc_part2->last_req) {
1487         for (last_req = enc_part2->last_req; *last_req; last_req++) {
1488             lr_type = (*last_req)->lr_type;
1489             if (lr_type == KRB5_LRQ_ALL_PW_EXPTIME ||
1490                 lr_type == KRB5_LRQ_ONE_PW_EXPTIME) {
1491                 *is_last_req = TRUE;
1492                 *pw_exp = (*last_req)->value;
1493             } else if (lr_type == KRB5_LRQ_ALL_ACCT_EXPTIME ||
1494                        lr_type == KRB5_LRQ_ONE_ACCT_EXPTIME) {
1495                 *is_last_req = TRUE;
1496                 *acct_exp = (*last_req)->value;
1497             }
1498         }
1499     }
1500 
1501     /* If we didn't find any, use the ambiguous key_exp field. */
1502     if (*is_last_req == FALSE)
1503         *pw_exp = enc_part2->key_exp;
1504 }
1505 
1506 /*
1507  * Send an appropriate warning prompter if as_reply indicates that the password
1508  * is going to expire soon.  If an expire callback was provided, use that
1509  * instead.
1510  */
1511 static void
warn_pw_expiry(krb5_context context,krb5_get_init_creds_opt * options,krb5_prompter_fct prompter,void * data,const char * in_tkt_service,krb5_kdc_rep * as_reply)1512 warn_pw_expiry(krb5_context context, krb5_get_init_creds_opt *options,
1513                krb5_prompter_fct prompter, void *data,
1514                const char *in_tkt_service, krb5_kdc_rep *as_reply)
1515 {
1516     krb5_error_code ret;
1517     krb5_expire_callback_func expire_cb;
1518     void *expire_data;
1519     krb5_timestamp pw_exp, acct_exp, now;
1520     krb5_boolean is_last_req;
1521     uint32_t interval;
1522     char ts[256], banner[1024];
1523 
1524     if (as_reply == NULL || as_reply->enc_part2 == NULL)
1525         return;
1526 
1527     get_expiry_times(as_reply->enc_part2, &pw_exp, &acct_exp, &is_last_req);
1528 
1529     k5_gic_opt_get_expire_cb(options, &expire_cb, &expire_data);
1530     if (expire_cb != NULL) {
1531         /* Invoke the expire callback and don't send prompter warnings. */
1532         (*expire_cb)(context, expire_data, pw_exp, acct_exp, is_last_req);
1533         return;
1534     }
1535 
1536     /* Don't warn if no password expiry value was sent. */
1537     if (pw_exp == 0)
1538         return;
1539 
1540     /* Don't warn if the password is being changed. */
1541     if (in_tkt_service && strcmp(in_tkt_service, "kadmin/changepw") == 0)
1542         return;
1543 
1544     /*
1545      * If the expiry time came from a last_req field, assume the KDC wants us
1546      * to warn.  Otherwise, warn only if the expiry time is less than a week
1547      * from now.
1548      */
1549     ret = krb5_timeofday(context, &now);
1550     if (ret != 0)
1551         return;
1552     interval = ts_interval(now, pw_exp);
1553     if (!is_last_req && (!interval || interval > 7 * 24 * 60 * 60))
1554         return;
1555 
1556     if (!prompter)
1557         return;
1558 
1559     ret = krb5_timestamp_to_string(pw_exp, ts, sizeof(ts));
1560     if (ret != 0)
1561         return;
1562 
1563     if (interval < 3600) {
1564         snprintf(banner, sizeof(banner),
1565                  _("Warning: Your password will expire in less than one hour "
1566                    "on %s"), ts);
1567     } else if (interval < 86400 * 2) {
1568         snprintf(banner, sizeof(banner),
1569                  _("Warning: Your password will expire in %d hour%s on %s"),
1570                  interval / 3600, interval < 7200 ? "" : "s", ts);
1571     } else {
1572         snprintf(banner, sizeof(banner),
1573                  _("Warning: Your password will expire in %d days on %s"),
1574                  interval / 86400, ts);
1575     }
1576 
1577     /* PROMPTER_INVOCATION */
1578     (*prompter)(context, data, 0, banner, 0, 0);
1579 }
1580 
1581 /* Display a warning via the prompter if a deprecated enctype was used for
1582  * either the reply key or the session key. */
1583 static void
warn_deprecated(krb5_context context,krb5_init_creds_context ctx,krb5_enctype as_key_enctype)1584 warn_deprecated(krb5_context context, krb5_init_creds_context ctx,
1585                 krb5_enctype as_key_enctype)
1586 {
1587     krb5_enctype etype;
1588     char encbuf[128], banner[256];
1589 
1590     if (ctx->prompter == NULL)
1591         return;
1592 
1593     if (krb5int_c_deprecated_enctype(as_key_enctype))
1594         etype = as_key_enctype;
1595     else if (krb5int_c_deprecated_enctype(ctx->cred.keyblock.enctype))
1596         etype = ctx->cred.keyblock.enctype;
1597     else
1598         return;
1599 
1600     if (krb5_enctype_to_name(etype, FALSE, encbuf, sizeof(encbuf)) != 0)
1601         return;
1602     snprintf(banner, sizeof(banner),
1603              _("Warning: encryption type %s used for authentication is "
1604                "deprecated and will be disabled"), encbuf);
1605 
1606     /* PROMPTER_INVOCATION */
1607     (*ctx->prompter)(context, ctx->prompter_data, NULL, banner, 0, NULL);
1608 }
1609 
1610 /*
1611  * If ctx specifies an output ccache, create or refresh it (atomically, if
1612  * possible) with the obtained credential and any appropriate ccache
1613  * configuration.
1614  */
1615 static krb5_error_code
write_out_ccache(krb5_context context,krb5_init_creds_context ctx,krb5_boolean fast_avail)1616 write_out_ccache(krb5_context context, krb5_init_creds_context ctx,
1617                  krb5_boolean fast_avail)
1618 {
1619     krb5_error_code ret;
1620     krb5_ccache out_ccache = k5_gic_opt_get_out_ccache(ctx->opt);
1621     krb5_ccache mcc = NULL;
1622     krb5_data yes = string2data("yes");
1623 
1624     if (out_ccache == NULL)
1625         return 0;
1626 
1627     ret = krb5_cc_new_unique(context, "MEMORY", NULL, &mcc);
1628     if (ret)
1629         goto cleanup;
1630 
1631     ret = krb5_cc_initialize(context, mcc, ctx->cred.client);
1632     if (ret)
1633         goto cleanup;
1634 
1635     if (fast_avail) {
1636         ret = krb5_cc_set_config(context, mcc, ctx->cred.server,
1637                                  KRB5_CC_CONF_FAST_AVAIL, &yes);
1638         if (ret)
1639             goto cleanup;
1640     }
1641 
1642     ret = save_selected_preauth_type(context, mcc, ctx);
1643     if (ret)
1644         goto cleanup;
1645 
1646     ret = save_cc_config_out_data(context, mcc, ctx);
1647     if (ret)
1648         goto cleanup;
1649 
1650     ret = k5_cc_store_primary_cred(context, mcc, &ctx->cred);
1651     if (ret)
1652         goto cleanup;
1653 
1654     ret = krb5_cc_move(context, mcc, out_ccache);
1655     if (ret)
1656         goto cleanup;
1657     mcc = NULL;
1658 
1659 cleanup:
1660     if (mcc != NULL)
1661         krb5_cc_destroy(context, mcc);
1662     return ret;
1663 }
1664 
1665 static krb5_error_code
init_creds_step_reply(krb5_context context,krb5_init_creds_context ctx,krb5_data * in)1666 init_creds_step_reply(krb5_context context,
1667                       krb5_init_creds_context ctx,
1668                       krb5_data *in)
1669 {
1670     krb5_error_code code;
1671     krb5_pa_data **kdc_padata = NULL;
1672     krb5_preauthtype kdc_pa_type;
1673     krb5_boolean retry = FALSE;
1674     int canon_flag = 0;
1675     uint32_t reply_code;
1676     krb5_keyblock *strengthen_key = NULL;
1677     krb5_keyblock encrypting_key;
1678     krb5_boolean fast_avail;
1679 
1680     encrypting_key.length = 0;
1681     encrypting_key.contents = NULL;
1682 
1683     /* process previous KDC response */
1684     code = init_creds_validate_reply(context, ctx, in);
1685     if (code != 0)
1686         goto cleanup;
1687 
1688     /* per referrals draft, enterprise principals imply canonicalization */
1689     canon_flag = ((ctx->request->kdc_options & KDC_OPT_CANONICALIZE) != 0) ||
1690         ctx->request->client->type == KRB5_NT_ENTERPRISE_PRINCIPAL;
1691 
1692     if (ctx->err_reply != NULL) {
1693         krb5_free_pa_data(context, ctx->more_padata);
1694         krb5_free_pa_data(context, ctx->err_padata);
1695         ctx->more_padata = ctx->err_padata = NULL;
1696         code = krb5int_fast_process_error(context, ctx->fast_state,
1697                                           &ctx->err_reply, &ctx->err_padata,
1698                                           &retry);
1699         if (code != 0)
1700             goto cleanup;
1701         reply_code = ctx->err_reply->error;
1702         if (!ctx->restarted &&
1703             k5_upgrade_to_fast_p(context, ctx->fast_state, ctx->err_padata)) {
1704             /* Retry with FAST after discovering that the KDC supports
1705              * it.  (FAST negotiation usually avoids this restart.) */
1706             TRACE_FAST_PADATA_UPGRADE(context);
1707             ctx->restarted = TRUE;
1708             code = restart_init_creds_loop(context, ctx, TRUE);
1709         } else if (!ctx->restarted && reply_code == KDC_ERR_PREAUTH_FAILED &&
1710                    ctx->selected_preauth_type == KRB5_PADATA_NONE) {
1711             /* The KDC didn't like our informational padata (probably a pre-1.7
1712              * MIT krb5 KDC).  Retry without it. */
1713             ctx->info_pa_permitted = FALSE;
1714             ctx->restarted = TRUE;
1715             code = restart_init_creds_loop(context, ctx, FALSE);
1716         } else if (reply_code == KDC_ERR_PREAUTH_EXPIRED) {
1717             /* We sent an expired KDC cookie.  Start over, allowing another
1718              * FAST upgrade. */
1719             ctx->restarted = FALSE;
1720             code = restart_init_creds_loop(context, ctx, FALSE);
1721         } else if (ctx->identify_realm &&
1722                    (reply_code == KDC_ERR_PREAUTH_REQUIRED ||
1723                     reply_code == KDC_ERR_KEY_EXP)) {
1724             /* The client exists in this realm; we can stop. */
1725             ctx->complete = TRUE;
1726             goto cleanup;
1727         } else if (reply_code == KDC_ERR_PREAUTH_REQUIRED && retry) {
1728             note_req_timestamp(context, ctx, ctx->err_reply->stime,
1729                                ctx->err_reply->susec);
1730             code = accept_method_data(context, ctx);
1731         } else if (reply_code == KDC_ERR_PREAUTH_FAILED && retry) {
1732             note_req_timestamp(context, ctx, ctx->err_reply->stime,
1733                                ctx->err_reply->susec);
1734             /* Don't try again with the mechanism that failed. */
1735             code = k5_preauth_note_failed(ctx, ctx->selected_preauth_type);
1736             if (code)
1737                 goto cleanup;
1738             ctx->selected_preauth_type = KRB5_PADATA_NONE;
1739             /* Accept or update method data if the KDC sent it. */
1740             if (ctx->err_padata != NULL)
1741                 code = accept_method_data(context, ctx);
1742         } else if (reply_code == KDC_ERR_MORE_PREAUTH_DATA_REQUIRED && retry) {
1743             ctx->more_padata = ctx->err_padata;
1744             ctx->err_padata = NULL;
1745         } else if (canon_flag && is_referral(context, ctx->err_reply,
1746                                              ctx->request->client)) {
1747             TRACE_INIT_CREDS_REFERRAL(context, &ctx->err_reply->client->realm);
1748             /* Rewrite request.client with realm from error reply */
1749             krb5_free_data_contents(context, &ctx->request->client->realm);
1750             code = krb5int_copy_data_contents(context,
1751                                               &ctx->err_reply->client->realm,
1752                                               &ctx->request->client->realm);
1753             if (code != 0)
1754                 goto cleanup;
1755             /* Reset per-realm negotiation state. */
1756             ctx->restarted = FALSE;
1757             ctx->info_pa_permitted = TRUE;
1758             code = restart_init_creds_loop(context, ctx, FALSE);
1759         } else {
1760             if (retry && ctx->selected_preauth_type != KRB5_PADATA_NONE) {
1761                 code = 0;
1762             } else {
1763                 /* error + no hints (or no preauth mech) = give up */
1764                 code = (krb5_error_code)reply_code + ERROR_TABLE_BASE_krb5;
1765             }
1766         }
1767 
1768         /* Return error code, or continue with next iteration */
1769         goto cleanup;
1770     }
1771 
1772     /* We have a response. Process it. */
1773     assert(ctx->reply != NULL);
1774 
1775     /* Check for replies (likely forged) with unasked-for enctypes. */
1776     code = check_reply_enctype(ctx);
1777     if (code != 0)
1778         goto cleanup;
1779 
1780     /* process any preauth data in the as_reply */
1781     code = krb5int_fast_process_response(context, ctx->fast_state,
1782                                          ctx->reply, &strengthen_key);
1783     if (code != 0)
1784         goto cleanup;
1785 
1786     if (ctx->identify_realm) {
1787         /* Just getting a reply means the client exists in this realm. */
1788         ctx->complete = TRUE;
1789         goto cleanup;
1790     }
1791 
1792     code = sort_krb5_padata_sequence(context, &ctx->request->client->realm,
1793                                      ctx->reply->padata);
1794     if (code != 0)
1795         goto cleanup;
1796 
1797     ctx->etype = ctx->reply->enc_part.enctype;
1798 
1799     /* Process the final reply padata.  Don't restrict the preauth types or
1800      * record a selected preauth type. */
1801     ctx->allowed_preauth_type = KRB5_PADATA_NONE;
1802     code = k5_preauth(context, ctx, ctx->reply->padata, FALSE, &kdc_padata,
1803                       &kdc_pa_type);
1804     if (code != 0)
1805         goto cleanup;
1806 
1807     /*
1808      * If we haven't gotten a salt from another source yet, set up one
1809      * corresponding to the client principal returned by the KDC.  We
1810      * could get the same effect by passing local_as_reply->client to
1811      * gak_fct below, but that would put the canonicalized client name
1812      * in the prompt, which raises issues of needing to sanitize
1813      * unprintable characters.  So for now we just let it affect the
1814      * salt.  local_as_reply->client will be checked later on in
1815      * verify_as_reply.
1816      */
1817     if (ctx->default_salt) {
1818         code = krb5_principal2salt(context, ctx->reply->client, &ctx->salt);
1819         TRACE_INIT_CREDS_SALT_PRINC(context, &ctx->salt);
1820         if (code != 0)
1821             goto cleanup;
1822     }
1823 
1824     code = decrypt_as_reply(context, ctx, strengthen_key, &encrypting_key);
1825     if (code)
1826         goto cleanup;
1827     TRACE_INIT_CREDS_DECRYPTED_REPLY(context, ctx->reply->enc_part2->session);
1828 
1829     code = krb5int_fast_verify_nego(context, ctx->fast_state,
1830                                     ctx->reply, ctx->encoded_previous_request,
1831                                     &encrypting_key, &fast_avail);
1832     if (code)
1833         goto cleanup;
1834     code = verify_as_reply(context, ctx->request_time,
1835                            ctx->request, ctx->reply);
1836     if (code != 0)
1837         goto cleanup;
1838     code = verify_anonymous(context, ctx->request, ctx->reply,
1839                             &ctx->as_key);
1840     if (code)
1841         goto cleanup;
1842 
1843     code = stash_as_reply(context, ctx->reply, &ctx->cred, NULL);
1844     if (code != 0)
1845         goto cleanup;
1846     code = write_out_ccache(context, ctx, fast_avail);
1847     if (code)
1848         k5_prependmsg(context, code, _("Failed to store credentials"));
1849 
1850     k5_preauth_request_context_fini(context, ctx);
1851 
1852     /* success */
1853     ctx->complete = TRUE;
1854     warn_pw_expiry(context, ctx->opt, ctx->prompter, ctx->prompter_data,
1855                    ctx->in_tkt_service, ctx->reply);
1856     warn_deprecated(context, ctx, encrypting_key.enctype);
1857 
1858 cleanup:
1859     krb5_free_pa_data(context, kdc_padata);
1860     krb5_free_keyblock(context, strengthen_key);
1861     krb5_free_keyblock_contents(context, &encrypting_key);
1862 
1863     return code;
1864 }
1865 
1866 /*
1867  * Do next step of credentials acquisition.
1868  *
1869  * On success returns 0 or KRB5KRB_ERR_RESPONSE_TOO_BIG if the request
1870  * should be sent with TCP.
1871  */
1872 krb5_error_code KRB5_CALLCONV
krb5_init_creds_step(krb5_context context,krb5_init_creds_context ctx,krb5_data * in,krb5_data * out,krb5_data * realm,unsigned int * flags)1873 krb5_init_creds_step(krb5_context context,
1874                      krb5_init_creds_context ctx,
1875                      krb5_data *in,
1876                      krb5_data *out,
1877                      krb5_data *realm,
1878                      unsigned int *flags)
1879 {
1880     krb5_error_code code, code2;
1881 
1882     *flags = 0;
1883 
1884     out->data = NULL;
1885     out->length = 0;
1886 
1887     realm->data = NULL;
1888     realm->length = 0;
1889 
1890     if (ctx->complete)
1891         return EINVAL;
1892 
1893     code = k5_preauth_check_context(context, ctx);
1894     if (code)
1895         return code;
1896 
1897     if (in->length != 0) {
1898         code = init_creds_step_reply(context, ctx, in);
1899         if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG) {
1900             code2 = krb5int_copy_data_contents(context,
1901                                                ctx->encoded_previous_request,
1902                                                out);
1903             if (code2 != 0) {
1904                 code = code2;
1905                 goto cleanup;
1906             }
1907             goto copy_realm;
1908         }
1909         if (code != 0 || ctx->complete)
1910             goto cleanup;
1911     } else {
1912         code = restart_init_creds_loop(context, ctx, FALSE);
1913         if (code)
1914             goto cleanup;
1915     }
1916 
1917     code = init_creds_step_request(context, ctx, out);
1918     if (code != 0)
1919         goto cleanup;
1920 
1921     /* Only a new request increments the loop count, not a TCP retry */
1922     ctx->loopcount++;
1923 
1924 copy_realm:
1925     assert(ctx->request->server != NULL);
1926 
1927     code2 = krb5int_copy_data_contents(context,
1928                                        &ctx->request->server->realm,
1929                                        realm);
1930     if (code2 != 0) {
1931         code = code2;
1932         goto cleanup;
1933     }
1934 
1935 cleanup:
1936     if (code == KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN) {
1937         char *client_name;
1938 
1939         /* See if we can produce a more detailed error message */
1940         code2 = krb5_unparse_name(context, ctx->request->client, &client_name);
1941         if (code2 == 0) {
1942             k5_setmsg(context, code,
1943                       _("Client '%s' not found in Kerberos database"),
1944                       client_name);
1945             krb5_free_unparsed_name(context, client_name);
1946         }
1947     }
1948 
1949     *flags = ctx->complete ? 0 : KRB5_INIT_CREDS_STEP_FLAG_CONTINUE;
1950     return code;
1951 }
1952 
1953 static krb5_error_code
try_init_creds(krb5_context context,krb5_creds * creds,krb5_principal client,krb5_prompter_fct prompter,void * prompter_data,krb5_deltat start_time,const char * in_tkt_service,krb5_get_init_creds_opt * options,get_as_key_fn gak_fct,void * gak_data,krb5_boolean use_primary,struct kdclist * kdcs,krb5_kdc_rep ** as_reply)1954 try_init_creds(krb5_context context, krb5_creds *creds, krb5_principal client,
1955                krb5_prompter_fct prompter, void *prompter_data,
1956                krb5_deltat start_time, const char *in_tkt_service,
1957                krb5_get_init_creds_opt *options, get_as_key_fn gak_fct,
1958                void *gak_data, krb5_boolean use_primary, struct kdclist *kdcs,
1959                krb5_kdc_rep **as_reply)
1960 {
1961     krb5_error_code code;
1962     krb5_init_creds_context ctx = NULL;
1963 
1964     code = krb5_init_creds_init(context,
1965                                 client,
1966                                 prompter,
1967                                 prompter_data,
1968                                 start_time,
1969                                 options,
1970                                 &ctx);
1971     if (code != 0)
1972         goto cleanup;
1973 
1974     ctx->gak_fct = gak_fct;
1975     ctx->gak_data = gak_data;
1976 
1977     if (in_tkt_service) {
1978         code = krb5_init_creds_set_service(context, ctx, in_tkt_service);
1979         if (code != 0)
1980             goto cleanup;
1981     }
1982 
1983     code = k5_init_creds_get(context, ctx, use_primary, kdcs);
1984     if (code != 0)
1985         goto cleanup;
1986 
1987     code = krb5_init_creds_get_creds(context, ctx, creds);
1988     if (code != 0)
1989         goto cleanup;
1990 
1991     if (as_reply != NULL) {
1992         *as_reply = ctx->reply;
1993         ctx->reply = NULL;
1994     }
1995 
1996 cleanup:
1997     krb5_init_creds_free(context, ctx);
1998 
1999     return code;
2000 }
2001 
2002 krb5_error_code
k5_get_init_creds(krb5_context context,krb5_creds * creds,krb5_principal client,krb5_prompter_fct prompter,void * prompter_data,krb5_deltat start_time,const char * in_tkt_service,krb5_get_init_creds_opt * options,get_as_key_fn gak_fct,void * gak_data,krb5_kdc_rep ** as_reply)2003 k5_get_init_creds(krb5_context context, krb5_creds *creds,
2004                   krb5_principal client, krb5_prompter_fct prompter,
2005                   void *prompter_data, krb5_deltat start_time,
2006                   const char *in_tkt_service, krb5_get_init_creds_opt *options,
2007                   get_as_key_fn gak_fct, void *gak_data,
2008                   krb5_kdc_rep **as_reply)
2009 {
2010     krb5_error_code ret;
2011     struct kdclist *kdcs = NULL;
2012     struct errinfo errsave = EMPTY_ERRINFO;
2013 
2014     ret = k5_kdclist_create(&kdcs);
2015     if (ret)
2016         goto cleanup;
2017 
2018     /* Try getting the requested ticket from any KDC. */
2019     ret = try_init_creds(context, creds, client, prompter, prompter_data,
2020                          start_time, in_tkt_service, options, gak_fct,
2021                          gak_data, FALSE, kdcs, as_reply);
2022     if (!ret)
2023         goto cleanup;
2024 
2025     /* If all of the KDCs are unavailable, or if the error was due to a user
2026      * interrupt, fail. */
2027     if (ret == KRB5_KDC_UNREACH || ret == KRB5_REALM_CANT_RESOLVE ||
2028         ret == KRB5_LIBOS_PWDINTR || ret == KRB5_LIBOS_CANTREADPWD)
2029         goto cleanup;
2030 
2031     /* If any reply came from a replica, try again with only primary KDCs. */
2032     if (k5_kdclist_any_replicas(context, kdcs)) {
2033         k5_save_ctx_error(context, ret, &errsave);
2034         TRACE_INIT_CREDS_PRIMARY(context);
2035         ret = try_init_creds(context, creds, client, prompter, prompter_data,
2036                              start_time, in_tkt_service, options, gak_fct,
2037                              gak_data, TRUE, NULL, as_reply);
2038         if (ret == KRB5_KDC_UNREACH || ret == KRB5_REALM_CANT_RESOLVE ||
2039             ret == KRB5_REALM_UNKNOWN) {
2040             /* We couldn't contact a primary KDC; return the error from the
2041              * replica we were able to contact. */
2042             ret = k5_restore_ctx_error(context, &errsave);
2043         }
2044     }
2045 
2046 cleanup:
2047     k5_kdclist_free(kdcs);
2048     k5_clear_error(&errsave);
2049     return ret;
2050 }
2051 
2052 krb5_error_code
k5_identify_realm(krb5_context context,krb5_principal client,const krb5_data * subject_cert,krb5_principal * client_out)2053 k5_identify_realm(krb5_context context, krb5_principal client,
2054                   const krb5_data *subject_cert, krb5_principal *client_out)
2055 {
2056     krb5_error_code ret;
2057     krb5_get_init_creds_opt *opts = NULL;
2058     krb5_init_creds_context ctx = NULL;
2059 
2060     *client_out = NULL;
2061 
2062     ret = krb5_get_init_creds_opt_alloc(context, &opts);
2063     if (ret)
2064         goto cleanup;
2065     krb5_get_init_creds_opt_set_tkt_life(opts, 15);
2066     krb5_get_init_creds_opt_set_renew_life(opts, 0);
2067     krb5_get_init_creds_opt_set_forwardable(opts, 0);
2068     krb5_get_init_creds_opt_set_proxiable(opts, 0);
2069     krb5_get_init_creds_opt_set_canonicalize(opts, 1);
2070 
2071     ret = krb5_init_creds_init(context, client, NULL, NULL, 0, opts, &ctx);
2072     if (ret)
2073         goto cleanup;
2074 
2075     ctx->identify_realm = TRUE;
2076     ctx->subject_cert = subject_cert;
2077 
2078     ret = k5_init_creds_get(context, ctx, FALSE, NULL);
2079     if (ret)
2080         goto cleanup;
2081 
2082     TRACE_INIT_CREDS_IDENTIFIED_REALM(context, &ctx->request->client->realm);
2083     ret = krb5_copy_principal(context, ctx->request->client, client_out);
2084 
2085 cleanup:
2086     krb5_get_init_creds_opt_free(context, opts);
2087     krb5_init_creds_free(context, ctx);
2088     return ret;
2089 }
2090 
2091 krb5_error_code
k5_populate_gic_opt(krb5_context context,krb5_get_init_creds_opt ** out,krb5_flags options,krb5_address * const * addrs,krb5_enctype * ktypes,krb5_preauthtype * pre_auth_types,krb5_creds * creds)2092 k5_populate_gic_opt(krb5_context context, krb5_get_init_creds_opt **out,
2093                     krb5_flags options, krb5_address *const *addrs,
2094                     krb5_enctype *ktypes, krb5_preauthtype *pre_auth_types,
2095                     krb5_creds *creds)
2096 {
2097     int i;
2098     krb5_timestamp starttime;
2099     krb5_deltat lifetime;
2100     krb5_get_init_creds_opt *opt;
2101     krb5_error_code retval;
2102 
2103     *out = NULL;
2104     retval = krb5_get_init_creds_opt_alloc(context, &opt);
2105     if (retval)
2106         return(retval);
2107 
2108     if (addrs)
2109         krb5_get_init_creds_opt_set_address_list(opt, (krb5_address **) addrs);
2110     if (ktypes) {
2111         i = k5_count_etypes(ktypes);
2112         if (i)
2113             krb5_get_init_creds_opt_set_etype_list(opt, ktypes, i);
2114     }
2115     if (pre_auth_types) {
2116         for (i=0; pre_auth_types[i]; i++);
2117         if (i)
2118             krb5_get_init_creds_opt_set_preauth_list(opt, pre_auth_types, i);
2119     }
2120     if (options&KDC_OPT_FORWARDABLE)
2121         krb5_get_init_creds_opt_set_forwardable(opt, 1);
2122     else krb5_get_init_creds_opt_set_forwardable(opt, 0);
2123     if (options&KDC_OPT_PROXIABLE)
2124         krb5_get_init_creds_opt_set_proxiable(opt, 1);
2125     else krb5_get_init_creds_opt_set_proxiable(opt, 0);
2126     if (creds && creds->times.endtime) {
2127         retval = krb5_timeofday(context, &starttime);
2128         if (retval)
2129             goto cleanup;
2130         if (creds->times.starttime) starttime = creds->times.starttime;
2131         lifetime = ts_delta(creds->times.endtime, starttime);
2132         krb5_get_init_creds_opt_set_tkt_life(opt, lifetime);
2133     }
2134     *out = opt;
2135     return 0;
2136 
2137 cleanup:
2138     krb5_get_init_creds_opt_free(context, opt);
2139     return retval;
2140 }
2141