1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* kdc/kdc_util.c - Utility functions for the KDC implementation */
3 /*
4  * Copyright 1990,1991,2007,2008,2009 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  * Copyright (c) 2006-2008, Novell, Inc.
28  * All rights reserved.
29  *
30  * Redistribution and use in source and binary forms, with or without
31  * modification, are permitted provided that the following conditions are met:
32  *
33  *   * Redistributions of source code must retain the above copyright notice,
34  *       this list of conditions and the following disclaimer.
35  *   * Redistributions in binary form must reproduce the above copyright
36  *       notice, this list of conditions and the following disclaimer in the
37  *       documentation and/or other materials provided with the distribution.
38  *   * The copyright holder's name is not used to endorse or promote products
39  *       derived from this software without specific prior written permission.
40  *
41  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
42  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
45  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
46  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
47  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
48  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
49  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
50  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
51  * POSSIBILITY OF SUCH DAMAGE.
52  */
53 
54 #include "k5-int.h"
55 #include "kdc_util.h"
56 #include "extern.h"
57 #include <stdio.h>
58 #include <ctype.h>
59 #include <syslog.h>
60 #include <kadm5/admin.h>
61 #include "adm_proto.h"
62 #include "net-server.h"
63 #include <limits.h>
64 
65 #ifdef KRBCONF_VAGUE_ERRORS
66 const int vague_errors = 1;
67 #else
68 const int vague_errors = 0;
69 #endif
70 
71 static krb5_error_code kdc_rd_ap_req(kdc_realm_t *kdc_active_realm,
72                                      krb5_ap_req *apreq,
73                                      krb5_auth_context auth_context,
74                                      krb5_db_entry **server,
75                                      krb5_keyblock **tgskey);
76 static krb5_error_code find_server_key(krb5_context,
77                                        krb5_db_entry *, krb5_enctype,
78                                        krb5_kvno, krb5_keyblock **,
79                                        krb5_kvno *);
80 
81 /*
82  * Returns TRUE if the kerberos principal is the name of a Kerberos ticket
83  * service.
84  */
85 krb5_boolean
krb5_is_tgs_principal(krb5_const_principal principal)86 krb5_is_tgs_principal(krb5_const_principal principal)
87 {
88     if (krb5_princ_size(kdc_context, principal) != 2)
89         return FALSE;
90     if (data_eq_string(*krb5_princ_component(kdc_context, principal, 0),
91                        KRB5_TGS_NAME))
92         return TRUE;
93     else
94         return FALSE;
95 }
96 
97 /* Returns TRUE if principal is the name of a cross-realm TGS. */
98 krb5_boolean
is_cross_tgs_principal(krb5_const_principal principal)99 is_cross_tgs_principal(krb5_const_principal principal)
100 {
101     return krb5_is_tgs_principal(principal) &&
102         !data_eq(principal->data[1], principal->realm);
103 }
104 
105 /* Return true if princ is the name of a local TGS for any realm. */
106 krb5_boolean
is_local_tgs_principal(krb5_const_principal principal)107 is_local_tgs_principal(krb5_const_principal principal)
108 {
109     return krb5_is_tgs_principal(principal) &&
110         data_eq(principal->data[1], principal->realm);
111 }
112 
113 /*
114  * given authentication data (provides seed for checksum), verify checksum
115  * for source data.
116  */
117 static krb5_error_code
comp_cksum(krb5_context kcontext,krb5_data * source,krb5_ticket * ticket,krb5_checksum * his_cksum)118 comp_cksum(krb5_context kcontext, krb5_data *source, krb5_ticket *ticket,
119            krb5_checksum *his_cksum)
120 {
121     krb5_error_code       retval;
122     krb5_boolean          valid;
123 
124     if (!krb5_c_valid_cksumtype(his_cksum->checksum_type))
125         return KRB5KDC_ERR_SUMTYPE_NOSUPP;
126 
127     /* must be collision proof */
128     if (!krb5_c_is_coll_proof_cksum(his_cksum->checksum_type))
129         return KRB5KRB_AP_ERR_INAPP_CKSUM;
130 
131     /* verify checksum */
132     if ((retval = krb5_c_verify_checksum(kcontext, ticket->enc_part2->session,
133                                          KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
134                                          source, his_cksum, &valid)))
135         return(retval);
136 
137     if (!valid)
138         return(KRB5KRB_AP_ERR_BAD_INTEGRITY);
139 
140     return(0);
141 }
142 
143 /* If a header ticket is decrypted, *ticket_out is filled in even on error. */
144 krb5_error_code
kdc_process_tgs_req(kdc_realm_t * kdc_active_realm,krb5_kdc_req * request,const krb5_fulladdr * from,krb5_data * pkt,krb5_ticket ** ticket_out,krb5_db_entry ** krbtgt_ptr,krb5_keyblock ** tgskey,krb5_keyblock ** subkey,krb5_pa_data ** pa_tgs_req)145 kdc_process_tgs_req(kdc_realm_t *kdc_active_realm,
146                     krb5_kdc_req *request, const krb5_fulladdr *from,
147                     krb5_data *pkt, krb5_ticket **ticket_out,
148                     krb5_db_entry **krbtgt_ptr,
149                     krb5_keyblock **tgskey,
150                     krb5_keyblock **subkey,
151                     krb5_pa_data **pa_tgs_req)
152 {
153     krb5_pa_data        * tmppa;
154     krb5_ap_req         * apreq;
155     krb5_error_code       retval;
156     krb5_authdata **authdata = NULL;
157     krb5_data             scratch1;
158     krb5_data           * scratch = NULL;
159     krb5_auth_context     auth_context = NULL;
160     krb5_authenticator  * authenticator = NULL;
161     krb5_checksum       * his_cksum = NULL;
162     krb5_db_entry       * krbtgt = NULL;
163     krb5_ticket         * ticket;
164 
165     *ticket_out = NULL;
166     *krbtgt_ptr = NULL;
167     *tgskey = NULL;
168 
169     tmppa = krb5int_find_pa_data(kdc_context,
170                                  request->padata, KRB5_PADATA_AP_REQ);
171     if (!tmppa)
172         return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
173 
174     scratch1.length = tmppa->length;
175     scratch1.data = (char *)tmppa->contents;
176     if ((retval = decode_krb5_ap_req(&scratch1, &apreq)))
177         return retval;
178     ticket = apreq->ticket;
179 
180     if (isflagset(apreq->ap_options, AP_OPTS_USE_SESSION_KEY) ||
181         isflagset(apreq->ap_options, AP_OPTS_MUTUAL_REQUIRED)) {
182         krb5_klog_syslog(LOG_INFO, _("TGS_REQ: SESSION KEY or MUTUAL"));
183         retval = KRB5KDC_ERR_POLICY;
184         goto cleanup;
185     }
186 
187     if ((retval = krb5_auth_con_init(kdc_context, &auth_context)))
188         goto cleanup;
189 
190     /* Don't use a replay cache. */
191     if ((retval = krb5_auth_con_setflags(kdc_context, auth_context, 0)))
192         goto cleanup;
193 
194     if ((retval = krb5_auth_con_setaddrs(kdc_context, auth_context, NULL,
195                                          from->address)) )
196         goto cleanup_auth_context;
197 
198     retval = kdc_rd_ap_req(kdc_active_realm,
199                            apreq, auth_context, &krbtgt, tgskey);
200     if (retval)
201         goto cleanup_auth_context;
202 
203     if ((retval = krb5_auth_con_getrecvsubkey(kdc_context,
204                                               auth_context, subkey)))
205         goto cleanup_auth_context;
206 
207     if ((retval = krb5_auth_con_getauthenticator(kdc_context, auth_context,
208                                                  &authenticator)))
209         goto cleanup_auth_context;
210 
211     retval = krb5_find_authdata(kdc_context,
212                                 ticket->enc_part2->authorization_data,
213                                 authenticator->authorization_data,
214                                 KRB5_AUTHDATA_FX_ARMOR, &authdata);
215     if (retval != 0)
216         goto cleanup_authenticator;
217     if (authdata&& authdata[0]) {
218         k5_setmsg(kdc_context, KRB5KDC_ERR_POLICY,
219                   "ticket valid only as FAST armor");
220         retval = KRB5KDC_ERR_POLICY;
221         krb5_free_authdata(kdc_context, authdata);
222         goto cleanup_authenticator;
223     }
224     krb5_free_authdata(kdc_context, authdata);
225 
226 
227     /* Check for a checksum */
228     if (!(his_cksum = authenticator->checksum)) {
229         retval = KRB5KRB_AP_ERR_INAPP_CKSUM;
230         goto cleanup_authenticator;
231     }
232 
233     /*
234      * Check application checksum vs. tgs request
235      *
236      * We try checksumming the req-body two different ways: first we
237      * try reaching into the raw asn.1 stream (if available), and
238      * checksum that directly; if that fails, then we try encoding
239      * using our local asn.1 library.
240      */
241     if (pkt && (fetch_asn1_field((unsigned char *) pkt->data,
242                                  1, 4, &scratch1) >= 0)) {
243         if (comp_cksum(kdc_context, &scratch1, ticket, his_cksum)) {
244             if (!(retval = encode_krb5_kdc_req_body(request, &scratch)))
245                 retval = comp_cksum(kdc_context, scratch, ticket, his_cksum);
246             krb5_free_data(kdc_context, scratch);
247             if (retval)
248                 goto cleanup_authenticator;
249         }
250     }
251 
252     *pa_tgs_req = tmppa;
253     *krbtgt_ptr = krbtgt;
254     krbtgt = NULL;
255 
256 cleanup_authenticator:
257     krb5_free_authenticator(kdc_context, authenticator);
258 
259 cleanup_auth_context:
260     krb5_auth_con_free(kdc_context, auth_context);
261 
262 cleanup:
263     if (retval != 0) {
264         krb5_free_keyblock(kdc_context, *tgskey);
265         *tgskey = NULL;
266     }
267     if (apreq->ticket->enc_part2 != NULL) {
268         /* Steal the decrypted ticket pointer, even on error. */
269         *ticket_out = apreq->ticket;
270         apreq->ticket = NULL;
271     }
272     krb5_free_ap_req(kdc_context, apreq);
273     krb5_db_free_principal(kdc_context, krbtgt);
274     return retval;
275 }
276 
277 /*
278  * This is a KDC wrapper around krb5_rd_req_decoded_anyflag().
279  *
280  * We can't depend on KDB-as-keytab for handling the AP-REQ here for
281  * optimization reasons: we want to minimize the number of KDB lookups.  We'll
282  * need the KDB entry for the TGS principal, and the TGS key used to decrypt
283  * the TGT, elsewhere in the TGS code.
284  *
285  * This function also implements key rollover support for kvno 0 cross-realm
286  * TGTs issued by AD.
287  */
288 static
289 krb5_error_code
kdc_rd_ap_req(kdc_realm_t * kdc_active_realm,krb5_ap_req * apreq,krb5_auth_context auth_context,krb5_db_entry ** server,krb5_keyblock ** tgskey)290 kdc_rd_ap_req(kdc_realm_t *kdc_active_realm,
291               krb5_ap_req *apreq, krb5_auth_context auth_context,
292               krb5_db_entry **server, krb5_keyblock **tgskey)
293 {
294     krb5_error_code     retval;
295     krb5_enctype        search_enctype = apreq->ticket->enc_part.enctype;
296     krb5_boolean        match_enctype = 1;
297     krb5_kvno           kvno;
298     size_t              tries = 3;
299 
300     /*
301      * When we issue tickets we use the first key in the principals' highest
302      * kvno keyset.  For non-cross-realm krbtgt principals we want to only
303      * allow the use of the first key of the principal's keyset that matches
304      * the given kvno.
305      */
306     if (krb5_is_tgs_principal(apreq->ticket->server) &&
307         !is_cross_tgs_principal(apreq->ticket->server)) {
308         search_enctype = -1;
309         match_enctype = 0;
310     }
311 
312     retval = kdc_get_server_key(kdc_context, apreq->ticket, 0, match_enctype,
313                                 server, NULL, NULL);
314     if (retval)
315         return retval;
316 
317     *tgskey = NULL;
318     kvno = apreq->ticket->enc_part.kvno;
319     do {
320         krb5_free_keyblock(kdc_context, *tgskey);
321         retval = find_server_key(kdc_context,
322                                  *server, search_enctype, kvno, tgskey, &kvno);
323         if (retval)
324             continue;
325 
326         /* Make the TGS key available to krb5_rd_req_decoded_anyflag() */
327         retval = krb5_auth_con_setuseruserkey(kdc_context, auth_context,
328                                               *tgskey);
329         if (retval)
330             return retval;
331 
332         retval = krb5_rd_req_decoded_anyflag(kdc_context, &auth_context, apreq,
333                                              apreq->ticket->server,
334                                              kdc_active_realm->realm_keytab,
335                                              NULL, NULL);
336 
337         /* If the ticket was decrypted, don't try any more keys. */
338         if (apreq->ticket->enc_part2 != NULL)
339             break;
340 
341     } while (retval && apreq->ticket->enc_part.kvno == 0 && kvno-- > 1 &&
342              --tries > 0);
343 
344     return retval;
345 }
346 
347 /*
348  * The KDC should take the keytab associated with the realm and pass
349  * that to the krb5_rd_req_decoded_anyflag(), but we still need to use
350  * the service (TGS, here) key elsewhere.  This approach is faster than
351  * the KDB keytab approach too.
352  *
353  * This is also used by do_tgs_req() for u2u auth.
354  */
355 krb5_error_code
kdc_get_server_key(krb5_context context,krb5_ticket * ticket,unsigned int flags,krb5_boolean match_enctype,krb5_db_entry ** server_ptr,krb5_keyblock ** key,krb5_kvno * kvno)356 kdc_get_server_key(krb5_context context,
357                    krb5_ticket *ticket, unsigned int flags,
358                    krb5_boolean match_enctype, krb5_db_entry **server_ptr,
359                    krb5_keyblock **key, krb5_kvno *kvno)
360 {
361     krb5_error_code       retval;
362     krb5_db_entry       * server = NULL;
363     krb5_enctype          search_enctype = -1;
364     krb5_kvno             search_kvno = -1;
365 
366     if (match_enctype)
367         search_enctype = ticket->enc_part.enctype;
368     if (ticket->enc_part.kvno)
369         search_kvno = ticket->enc_part.kvno;
370 
371     *server_ptr = NULL;
372 
373     retval = krb5_db_get_principal(context, ticket->server, flags,
374                                    &server);
375     if (retval == KRB5_KDB_NOENTRY) {
376         char *sname;
377         if (!krb5_unparse_name(context, ticket->server, &sname)) {
378             limit_string(sname);
379             krb5_klog_syslog(LOG_ERR,
380                              _("TGS_REQ: UNKNOWN SERVER: server='%s'"), sname);
381             free(sname);
382         }
383         return KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
384     } else if (retval)
385         return retval;
386     if (server->attributes & KRB5_KDB_DISALLOW_SVR ||
387         server->attributes & KRB5_KDB_DISALLOW_ALL_TIX) {
388         retval = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
389         goto errout;
390     }
391 
392     if (key) {
393         retval = find_server_key(context, server, search_enctype, search_kvno,
394                                  key, kvno);
395         if (retval)
396             goto errout;
397     }
398     *server_ptr = server;
399     server = NULL;
400     return 0;
401 
402 errout:
403     krb5_db_free_principal(context, server);
404     return retval;
405 }
406 
407 /*
408  * A utility function to get the right key from a KDB entry.  Used in handling
409  * of kvno 0 TGTs, for example.
410  */
411 static
412 krb5_error_code
find_server_key(krb5_context context,krb5_db_entry * server,krb5_enctype enctype,krb5_kvno kvno,krb5_keyblock ** key_out,krb5_kvno * kvno_out)413 find_server_key(krb5_context context,
414                 krb5_db_entry *server, krb5_enctype enctype, krb5_kvno kvno,
415                 krb5_keyblock **key_out, krb5_kvno *kvno_out)
416 {
417     krb5_error_code       retval;
418     krb5_key_data       * server_key;
419     krb5_keyblock       * key;
420 
421     *key_out = NULL;
422     retval = krb5_dbe_find_enctype(context, server, enctype, -1,
423                                    kvno ? (krb5_int32)kvno : -1, &server_key);
424     if (retval)
425         return retval;
426     if (!server_key)
427         return KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
428     if ((key = (krb5_keyblock *)malloc(sizeof *key)) == NULL)
429         return ENOMEM;
430     retval = krb5_dbe_decrypt_key_data(context, NULL, server_key,
431                                        key, NULL);
432     if (retval)
433         goto errout;
434     if (enctype != -1) {
435         krb5_boolean similar;
436         retval = krb5_c_enctype_compare(context, enctype, key->enctype,
437                                         &similar);
438         if (retval)
439             goto errout;
440         if (!similar) {
441             retval = KRB5_KDB_NO_PERMITTED_KEY;
442             goto errout;
443         }
444         key->enctype = enctype;
445     }
446     *key_out = key;
447     key = NULL;
448     if (kvno_out)
449         *kvno_out = server_key->key_data_kvno;
450 errout:
451     krb5_free_keyblock(context, key);
452     return retval;
453 }
454 
455 /* Find the first key data entry (of a valid enctype) of the highest kvno in
456  * entry, and decrypt it into *key_out. */
457 krb5_error_code
get_first_current_key(krb5_context context,krb5_db_entry * entry,krb5_keyblock * key_out)458 get_first_current_key(krb5_context context, krb5_db_entry *entry,
459                       krb5_keyblock *key_out)
460 {
461     krb5_error_code ret;
462     krb5_key_data *kd;
463 
464     memset(key_out, 0, sizeof(*key_out));
465     ret = krb5_dbe_find_enctype(context, entry, -1, -1, 0, &kd);
466     if (ret)
467         return ret;
468     return krb5_dbe_decrypt_key_data(context, NULL, kd, key_out, NULL);
469 }
470 
471 /*
472  * If candidate is the local TGT for realm, set *alias_out to candidate and
473  * *storage_out to NULL.  Otherwise, load the local TGT into *storage_out and
474  * set *alias_out to *storage_out.  In either case, set *key_out to the
475  * decrypted first key of the local TGT.
476  *
477  * In the future we might generalize this to a small per-request principal
478  * cache.  For now, it saves a load operation in the common case where the AS
479  * server or TGS header ticket server is the local TGT.
480  */
481 krb5_error_code
get_local_tgt(krb5_context context,const krb5_data * realm,krb5_db_entry * candidate,krb5_db_entry ** alias_out,krb5_db_entry ** storage_out,krb5_keyblock * key_out)482 get_local_tgt(krb5_context context, const krb5_data *realm,
483               krb5_db_entry *candidate, krb5_db_entry **alias_out,
484               krb5_db_entry **storage_out, krb5_keyblock *key_out)
485 {
486     krb5_error_code ret;
487     krb5_principal princ;
488     krb5_db_entry *storage = NULL, *tgt;
489 
490     *alias_out = NULL;
491     *storage_out = NULL;
492     memset(key_out, 0, sizeof(*key_out));
493 
494     ret = krb5_build_principal_ext(context, &princ, realm->length, realm->data,
495                                    KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
496                                    realm->length, realm->data, 0);
497     if (ret)
498         goto cleanup;
499 
500     if (!krb5_principal_compare(context, candidate->princ, princ)) {
501         ret = krb5_db_get_principal(context, princ, 0, &storage);
502         if (ret)
503             goto cleanup;
504         tgt = storage;
505     } else {
506         tgt = candidate;
507     }
508 
509     ret = get_first_current_key(context, tgt, key_out);
510     if (ret)
511         goto cleanup;
512 
513     *alias_out = tgt;
514     *storage_out = storage;
515     storage = NULL;
516 
517 cleanup:
518     krb5_db_free_principal(context, storage);
519     krb5_free_principal(context, princ);
520     return ret;
521 }
522 
523 /* This probably wants to be updated if you support last_req stuff */
524 
525 static krb5_last_req_entry nolrentry = { KV5M_LAST_REQ_ENTRY, KRB5_LRQ_NONE, 0 };
526 static krb5_last_req_entry *nolrarray[] = { &nolrentry, 0 };
527 
528 krb5_error_code
fetch_last_req_info(krb5_db_entry * dbentry,krb5_last_req_entry *** lrentry)529 fetch_last_req_info(krb5_db_entry *dbentry, krb5_last_req_entry ***lrentry)
530 {
531     *lrentry = nolrarray;
532     return 0;
533 }
534 
535 
536 /* XXX!  This is a temporary place-holder */
537 
538 krb5_error_code
check_hot_list(krb5_ticket * ticket)539 check_hot_list(krb5_ticket *ticket)
540 {
541     return 0;
542 }
543 
544 
545 /* Convert an API error code to a protocol error code. */
546 int
errcode_to_protocol(krb5_error_code code)547 errcode_to_protocol(krb5_error_code code)
548 {
549     int protcode;
550 
551     protcode = code - ERROR_TABLE_BASE_krb5;
552     return (protcode >= 0 && protcode <= 128) ? protcode : KRB_ERR_GENERIC;
553 }
554 
555 /* Return -1 if the AS or TGS request is disallowed due to KDC policy on
556  * anonymous tickets. */
557 int
check_anon(kdc_realm_t * kdc_active_realm,krb5_principal client,krb5_principal server)558 check_anon(kdc_realm_t *kdc_active_realm,
559            krb5_principal client, krb5_principal server)
560 {
561     /* If restrict_anon is set, reject requests from anonymous clients to
562      * server principals other than local TGTs. */
563     if (kdc_active_realm->realm_restrict_anon &&
564         krb5_principal_compare_any_realm(kdc_context, client,
565                                          krb5_anonymous_principal()) &&
566         !is_local_tgs_principal(server))
567         return -1;
568     return 0;
569 }
570 
571 /*
572  * Routines that validate a AS request; checks a lot of things.  :-)
573  *
574  * Returns a Kerberos protocol error number, which is _not_ the same
575  * as a com_err error number!
576  */
577 int
validate_as_request(kdc_realm_t * kdc_active_realm,krb5_kdc_req * request,krb5_db_entry * client,krb5_db_entry * server,krb5_timestamp kdc_time,const char ** status,krb5_pa_data *** e_data)578 validate_as_request(kdc_realm_t *kdc_active_realm,
579                     krb5_kdc_req *request, krb5_db_entry *client,
580                     krb5_db_entry *server, krb5_timestamp kdc_time,
581                     const char **status, krb5_pa_data ***e_data)
582 {
583     krb5_error_code ret;
584 
585     /*
586      * If an option is set that is only allowed in TGS requests, complain.
587      */
588     if (request->kdc_options & AS_INVALID_OPTIONS) {
589         *status = "INVALID AS OPTIONS";
590         return KDC_ERR_BADOPTION;
591     }
592 
593     /* The client must not be expired */
594     if (client->expiration && ts_after(kdc_time, client->expiration)) {
595         *status = "CLIENT EXPIRED";
596         if (vague_errors)
597             return(KRB_ERR_GENERIC);
598         else
599             return(KDC_ERR_NAME_EXP);
600     }
601 
602     /* The client's password must not be expired, unless the server is
603        a KRB5_KDC_PWCHANGE_SERVICE. */
604     if (client->pw_expiration && ts_after(kdc_time, client->pw_expiration) &&
605         !isflagset(server->attributes, KRB5_KDB_PWCHANGE_SERVICE)) {
606         *status = "CLIENT KEY EXPIRED";
607         if (vague_errors)
608             return(KRB_ERR_GENERIC);
609         else
610             return(KDC_ERR_KEY_EXP);
611     }
612 
613     /* The server must not be expired */
614     if (server->expiration && ts_after(kdc_time, server->expiration)) {
615         *status = "SERVICE EXPIRED";
616         return(KDC_ERR_SERVICE_EXP);
617     }
618 
619     /*
620      * If the client requires password changing, then only allow the
621      * pwchange service.
622      */
623     if (isflagset(client->attributes, KRB5_KDB_REQUIRES_PWCHANGE) &&
624         !isflagset(server->attributes, KRB5_KDB_PWCHANGE_SERVICE)) {
625         *status = "REQUIRED PWCHANGE";
626         return(KDC_ERR_KEY_EXP);
627     }
628 
629     /* Client and server must allow postdating tickets */
630     if ((isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) ||
631          isflagset(request->kdc_options, KDC_OPT_POSTDATED)) &&
632         (isflagset(client->attributes, KRB5_KDB_DISALLOW_POSTDATED) ||
633          isflagset(server->attributes, KRB5_KDB_DISALLOW_POSTDATED))) {
634         *status = "POSTDATE NOT ALLOWED";
635         return(KDC_ERR_CANNOT_POSTDATE);
636     }
637 
638     /* Check to see if client is locked out */
639     if (isflagset(client->attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
640         *status = "CLIENT LOCKED OUT";
641         return(KDC_ERR_CLIENT_REVOKED);
642     }
643 
644     /* Check to see if server is locked out */
645     if (isflagset(server->attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
646         *status = "SERVICE LOCKED OUT";
647         return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
648     }
649 
650     /* Check to see if server is allowed to be a service */
651     if (isflagset(server->attributes, KRB5_KDB_DISALLOW_SVR)) {
652         *status = "SERVICE NOT ALLOWED";
653         return(KDC_ERR_MUST_USE_USER2USER);
654     }
655 
656     if (check_anon(kdc_active_realm, client->princ, request->server) != 0) {
657         *status = "ANONYMOUS NOT ALLOWED";
658         return(KDC_ERR_POLICY);
659     }
660 
661     /* Perform KDB module policy checks. */
662     ret = krb5_db_check_policy_as(kdc_context, request, client, server,
663                                   kdc_time, status, e_data);
664     if (ret && ret != KRB5_PLUGIN_OP_NOTSUPP)
665         return errcode_to_protocol(ret);
666 
667     return 0;
668 }
669 
670 /*
671  * Compute ticket flags based on the request, the client and server DB entry
672  * (which may prohibit forwardable or proxiable tickets), and the header
673  * ticket.  client may be NULL for a TGS request (although it may be set, such
674  * as for an S4U2Self request).  header_enc may be NULL for an AS request.
675  */
676 krb5_flags
get_ticket_flags(krb5_flags reqflags,krb5_db_entry * client,krb5_db_entry * server,krb5_enc_tkt_part * header_enc)677 get_ticket_flags(krb5_flags reqflags, krb5_db_entry *client,
678                  krb5_db_entry *server, krb5_enc_tkt_part *header_enc)
679 {
680     krb5_flags flags;
681 
682     /* Indicate support for encrypted padata (RFC 6806), and set flags based on
683      * request options and the header ticket. */
684     flags = OPTS2FLAGS(reqflags) | TKT_FLG_ENC_PA_REP;
685     if (reqflags & KDC_OPT_POSTDATED)
686         flags |= TKT_FLG_INVALID;
687     if (header_enc != NULL)
688         flags |= COPY_TKT_FLAGS(header_enc->flags);
689     if (header_enc == NULL)
690         flags |= TKT_FLG_INITIAL;
691 
692     /* For TGS requests, indicate if the service is marked ok-as-delegate. */
693     if (header_enc != NULL && (server->attributes & KRB5_KDB_OK_AS_DELEGATE))
694         flags |= TKT_FLG_OK_AS_DELEGATE;
695 
696     /* Unset PROXIABLE if it is disallowed. */
697     if (client != NULL && (client->attributes & KRB5_KDB_DISALLOW_PROXIABLE))
698         flags &= ~TKT_FLG_PROXIABLE;
699     if (server->attributes & KRB5_KDB_DISALLOW_PROXIABLE)
700         flags &= ~TKT_FLG_PROXIABLE;
701     if (header_enc != NULL && !(header_enc->flags & TKT_FLG_PROXIABLE))
702         flags &= ~TKT_FLG_PROXIABLE;
703 
704     /* Unset FORWARDABLE if it is disallowed. */
705     if (client != NULL && (client->attributes & KRB5_KDB_DISALLOW_FORWARDABLE))
706         flags &= ~TKT_FLG_FORWARDABLE;
707     if (server->attributes & KRB5_KDB_DISALLOW_FORWARDABLE)
708         flags &= ~TKT_FLG_FORWARDABLE;
709     if (header_enc != NULL && !(header_enc->flags & TKT_FLG_FORWARDABLE))
710         flags &= ~TKT_FLG_FORWARDABLE;
711 
712     /* We don't currently handle issuing anonymous tickets based on
713      * non-anonymous ones. */
714     if (header_enc != NULL && !(header_enc->flags & TKT_FLG_ANONYMOUS))
715         flags &= ~TKT_FLG_ANONYMOUS;
716 
717     return flags;
718 }
719 
720 /* Return KRB5KDC_ERR_POLICY if indicators does not contain the required auth
721  * indicators for server, ENOMEM on allocation error, 0 otherwise. */
722 krb5_error_code
check_indicators(krb5_context context,krb5_db_entry * server,krb5_data * const * indicators)723 check_indicators(krb5_context context, krb5_db_entry *server,
724                  krb5_data *const *indicators)
725 {
726     krb5_error_code ret;
727     char *str = NULL, *copy = NULL, *save, *ind;
728 
729     ret = krb5_dbe_get_string(context, server, KRB5_KDB_SK_REQUIRE_AUTH, &str);
730     if (ret || str == NULL)
731         goto cleanup;
732     copy = strdup(str);
733     if (copy == NULL) {
734         ret = ENOMEM;
735         goto cleanup;
736     }
737 
738     /* Look for any of the space-separated strings in indicators. */
739     ind = strtok_r(copy, " ", &save);
740     while (ind != NULL) {
741         if (authind_contains(indicators, ind))
742             goto cleanup;
743         ind = strtok_r(NULL, " ", &save);
744     }
745 
746     ret = KRB5KDC_ERR_POLICY;
747     k5_setmsg(context, ret,
748               _("Required auth indicators not present in ticket: %s"), str);
749 
750 cleanup:
751     krb5_dbe_free_string(context, str);
752     free(copy);
753     return ret;
754 }
755 
756 #define ASN1_ID_CLASS   (0xc0)
757 #define ASN1_ID_TYPE    (0x20)
758 #define ASN1_ID_TAG     (0x1f)
759 #define ASN1_CLASS_UNIV (0)
760 #define ASN1_CLASS_APP  (1)
761 #define ASN1_CLASS_CTX  (2)
762 #define ASN1_CLASS_PRIV (3)
763 #define asn1_id_constructed(x)  (x & ASN1_ID_TYPE)
764 #define asn1_id_primitive(x)    (!asn1_id_constructed(x))
765 #define asn1_id_class(x)        ((x & ASN1_ID_CLASS) >> 6)
766 #define asn1_id_tag(x)          (x & ASN1_ID_TAG)
767 
768 /*
769  * asn1length - return encoded length of value.
770  *
771  * passed a pointer into the asn.1 stream, which is updated
772  * to point right after the length bits.
773  *
774  * returns -1 on failure.
775  */
776 static int
asn1length(unsigned char ** astream)777 asn1length(unsigned char **astream)
778 {
779     int length;         /* resulting length */
780     int sublen;         /* sublengths */
781     int blen;           /* bytes of length */
782     unsigned char *p;   /* substring searching */
783 
784     if (**astream & 0x80) {
785         blen = **astream & 0x7f;
786         if (blen > 3) {
787             return(-1);
788         }
789         for (++*astream, length = 0; blen; ++*astream, blen--) {
790             length = (length << 8) | **astream;
791         }
792         if (length == 0) {
793             /* indefinite length, figure out by hand */
794             p = *astream;
795             p++;
796             while (1) {
797                 /* compute value length. */
798                 if ((sublen = asn1length(&p)) < 0) {
799                     return(-1);
800                 }
801                 p += sublen;
802                 /* check for termination */
803                 if ((!*p++) && (!*p)) {
804                     p++;
805                     break;
806                 }
807             }
808             length = p - *astream;
809         }
810     } else {
811         length = **astream;
812         ++*astream;
813     }
814     return(length);
815 }
816 
817 /*
818  * fetch_asn1_field - return raw asn.1 stream of subfield.
819  *
820  * this routine is passed a context-dependent tag number and "level" and returns
821  * the size and length of the corresponding level subfield.
822  *
823  * levels and are numbered starting from 1.
824  *
825  * returns 0 on success, -1 otherwise.
826  */
827 int
fetch_asn1_field(unsigned char * astream,unsigned int level,unsigned int field,krb5_data * data)828 fetch_asn1_field(unsigned char *astream, unsigned int level,
829                  unsigned int field, krb5_data *data)
830 {
831     unsigned char *estream;     /* end of stream */
832     int classes;                /* # classes seen so far this level */
833     unsigned int levels = 0;            /* levels seen so far */
834     int lastlevel = 1000;       /* last level seen */
835     int length;                 /* various lengths */
836     int tag;                    /* tag number */
837     unsigned char savelen;      /* saved length of our field */
838 
839     classes = -1;
840     /* we assume that the first identifier/length will tell us
841        how long the entire stream is. */
842     astream++;
843     estream = astream;
844     if ((length = asn1length(&astream)) < 0) {
845         return(-1);
846     }
847     estream += length;
848     /* search down the stream, checking identifiers.  we process identifiers
849        until we hit the "level" we want, and then process that level for our
850        subfield, always making sure we don't go off the end of the stream.  */
851     while (astream < estream) {
852         if (!asn1_id_constructed(*astream)) {
853             return(-1);
854         }
855         if (asn1_id_class(*astream) == ASN1_CLASS_CTX) {
856             if ((tag = (int)asn1_id_tag(*astream)) <= lastlevel) {
857                 levels++;
858                 classes = -1;
859             }
860             lastlevel = tag;
861             if (levels == level) {
862                 /* in our context-dependent class, is this the one we're looking for ? */
863                 if (tag == (int)field) {
864                     /* return length and data */
865                     astream++;
866                     savelen = *astream;
867                     if ((length = asn1length(&astream)) < 0) {
868                         return(-1);
869                     }
870                     data->length = length;
871                     /* if the field length is indefinite, we will have to subtract two
872                        (terminating octets) from the length returned since we don't want
873                        to pass any info from the "wrapper" back.  asn1length will always return
874                        the *total* length of the field, not just what's contained in it */
875                     if ((savelen & 0xff) == 0x80) {
876                         data->length -=2 ;
877                     }
878                     data->data = (char *)astream;
879                     return(0);
880                 } else if (tag <= classes) {
881                     /* we've seen this class before, something must be wrong */
882                     return(-1);
883                 } else {
884                     classes = tag;
885                 }
886             }
887         }
888         /* if we're not on our level yet, process this value.  otherwise skip over it */
889         astream++;
890         if ((length = asn1length(&astream)) < 0) {
891             return(-1);
892         }
893         if (levels == level) {
894             astream += length;
895         }
896     }
897     return(-1);
898 }
899 
900 /* Return true if we believe server can support enctype as a session key. */
901 static krb5_boolean
dbentry_supports_enctype(kdc_realm_t * kdc_active_realm,krb5_db_entry * server,krb5_enctype enctype)902 dbentry_supports_enctype(kdc_realm_t *kdc_active_realm, krb5_db_entry *server,
903                          krb5_enctype enctype)
904 {
905     krb5_error_code     retval;
906     krb5_key_data       *datap;
907     char                *etypes_str = NULL;
908     krb5_enctype        default_enctypes[1] = { 0 };
909     krb5_enctype        *etypes = NULL;
910     krb5_boolean        in_list;
911 
912     /* Look up the supported session key enctypes list in the KDB. */
913     retval = krb5_dbe_get_string(kdc_context, server,
914                                  KRB5_KDB_SK_SESSION_ENCTYPES,
915                                  &etypes_str);
916     if (retval == 0 && etypes_str != NULL && *etypes_str != '\0') {
917         /* Pass a fake profile key for tracing of unrecognized tokens. */
918         retval = krb5int_parse_enctype_list(kdc_context, "KDB-session_etypes",
919                                             etypes_str, default_enctypes,
920                                             &etypes);
921         if (retval == 0 && etypes != NULL && etypes[0]) {
922             in_list = k5_etypes_contains(etypes, enctype);
923             free(etypes_str);
924             free(etypes);
925             return in_list;
926         }
927         /* Fall through on error or empty list */
928     }
929     free(etypes_str);
930     free(etypes);
931 
932     /* Assume the server supports any enctype it has a long-term key for. */
933     return !krb5_dbe_find_enctype(kdc_context, server, enctype, -1, 0, &datap);
934 }
935 
936 /*
937  * This function returns the keytype which should be selected for the
938  * session key.  It is based on the ordered list which the user
939  * requested, and what the KDC and the application server can support.
940  */
941 krb5_enctype
select_session_keytype(kdc_realm_t * kdc_active_realm,krb5_db_entry * server,int nktypes,krb5_enctype * ktype)942 select_session_keytype(kdc_realm_t *kdc_active_realm, krb5_db_entry *server,
943                        int nktypes, krb5_enctype *ktype)
944 {
945     int         i;
946 
947     for (i = 0; i < nktypes; i++) {
948         if (!krb5_c_valid_enctype(ktype[i]))
949             continue;
950 
951         if (!krb5_is_permitted_enctype(kdc_context, ktype[i]))
952             continue;
953 
954         if (dbentry_supports_enctype(kdc_active_realm, server, ktype[i]))
955             return ktype[i];
956     }
957 
958     return 0;
959 }
960 
961 /*
962  * Limit strings to a "reasonable" length to prevent crowding out of
963  * other useful information in the log entry
964  */
965 #define NAME_LENGTH_LIMIT 128
966 
limit_string(char * name)967 void limit_string(char *name)
968 {
969     int     i;
970 
971     if (!name)
972         return;
973 
974     if (strlen(name) < NAME_LENGTH_LIMIT)
975         return;
976 
977     i = NAME_LENGTH_LIMIT-4;
978     name[i++] = '.';
979     name[i++] = '.';
980     name[i++] = '.';
981     name[i] = '\0';
982     return;
983 }
984 
985 /* Wrapper of krb5_enctype_to_name() to include the PKINIT types. */
986 static krb5_error_code
enctype_name(krb5_enctype ktype,char * buf,size_t buflen)987 enctype_name(krb5_enctype ktype, char *buf, size_t buflen)
988 {
989     const char *name, *prefix = "";
990     size_t len;
991 
992     if (buflen == 0)
993         return EINVAL;
994     *buf = '\0'; /* ensure these are always valid C-strings */
995 
996     if (!krb5_c_valid_enctype(ktype))
997         prefix = "UNSUPPORTED:";
998     else if (krb5int_c_deprecated_enctype(ktype))
999         prefix = "DEPRECATED:";
1000     len = strlcpy(buf, prefix, buflen);
1001     if (len >= buflen)
1002         return ENOMEM;
1003     buflen -= len;
1004     buf += len;
1005 
1006     /* rfc4556 recommends that clients wishing to indicate support for these
1007      * pkinit algorithms include them in the etype field of the AS-REQ. */
1008     if (ktype == ENCTYPE_DSA_SHA1_CMS)
1009         name = "id-dsa-with-sha1-CmsOID";
1010     else if (ktype == ENCTYPE_MD5_RSA_CMS)
1011         name = "md5WithRSAEncryption-CmsOID";
1012     else if (ktype == ENCTYPE_SHA1_RSA_CMS)
1013         name = "sha-1WithRSAEncryption-CmsOID";
1014     else if (ktype == ENCTYPE_RC2_CBC_ENV)
1015         name = "rc2-cbc-EnvOID";
1016     else if (ktype == ENCTYPE_RSA_ENV)
1017         name = "rsaEncryption-EnvOID";
1018     else if (ktype == ENCTYPE_RSA_ES_OAEP_ENV)
1019         name = "id-RSAES-OAEP-EnvOID";
1020     else if (ktype == ENCTYPE_DES3_CBC_ENV)
1021         name = "des-ede3-cbc-EnvOID";
1022     else
1023         return krb5_enctype_to_name(ktype, FALSE, buf, buflen);
1024 
1025     if (strlcpy(buf, name, buflen) >= buflen)
1026         return ENOMEM;
1027     return 0;
1028 }
1029 
1030 char *
ktypes2str(krb5_enctype * ktype,int nktypes)1031 ktypes2str(krb5_enctype *ktype, int nktypes)
1032 {
1033     struct k5buf buf;
1034     int i;
1035     char name[64];
1036 
1037     if (nktypes < 0)
1038         return NULL;
1039 
1040     k5_buf_init_dynamic(&buf);
1041     k5_buf_add_fmt(&buf, "%d etypes {", nktypes);
1042     for (i = 0; i < nktypes; i++) {
1043         enctype_name(ktype[i], name, sizeof(name));
1044         k5_buf_add_fmt(&buf, "%s%s(%ld)", i ? ", " : "", name, (long)ktype[i]);
1045     }
1046     k5_buf_add(&buf, "}");
1047     return buf.data;
1048 }
1049 
1050 char *
rep_etypes2str(krb5_kdc_rep * rep)1051 rep_etypes2str(krb5_kdc_rep *rep)
1052 {
1053     struct k5buf buf;
1054     char name[64];
1055     krb5_enctype etype;
1056 
1057     k5_buf_init_dynamic(&buf);
1058     k5_buf_add(&buf, "etypes {rep=");
1059     enctype_name(rep->enc_part.enctype, name, sizeof(name));
1060     k5_buf_add_fmt(&buf, "%s(%ld)", name, (long)rep->enc_part.enctype);
1061 
1062     if (rep->ticket != NULL) {
1063         etype = rep->ticket->enc_part.enctype;
1064         enctype_name(etype, name, sizeof(name));
1065         k5_buf_add_fmt(&buf, ", tkt=%s(%ld)", name, (long)etype);
1066     }
1067 
1068     if (rep->ticket != NULL && rep->ticket->enc_part2 != NULL &&
1069         rep->ticket->enc_part2->session != NULL) {
1070         etype = rep->ticket->enc_part2->session->enctype;
1071         enctype_name(etype, name, sizeof(name));
1072         k5_buf_add_fmt(&buf, ", ses=%s(%ld)", name, (long)etype);
1073     }
1074 
1075     k5_buf_add(&buf, "}");
1076     return buf.data;
1077 }
1078 
1079 static krb5_error_code
verify_for_user_checksum(krb5_context context,krb5_keyblock * key,krb5_pa_for_user * req)1080 verify_for_user_checksum(krb5_context context,
1081                          krb5_keyblock *key,
1082                          krb5_pa_for_user *req)
1083 {
1084     krb5_error_code             code;
1085     int                         i;
1086     krb5_int32                  name_type;
1087     char                        *p;
1088     krb5_data                   data;
1089     krb5_boolean                valid = FALSE;
1090 
1091     if (!krb5_c_is_keyed_cksum(req->cksum.checksum_type)) {
1092         return KRB5KRB_AP_ERR_INAPP_CKSUM;
1093     }
1094 
1095     /*
1096      * Checksum is over name type and string components of
1097      * client principal name and auth_package.
1098      */
1099     data.length = 4;
1100     for (i = 0; i < krb5_princ_size(context, req->user); i++) {
1101         data.length += krb5_princ_component(context, req->user, i)->length;
1102     }
1103     data.length += krb5_princ_realm(context, req->user)->length;
1104     data.length += req->auth_package.length;
1105 
1106     p = data.data = malloc(data.length);
1107     if (data.data == NULL) {
1108         return ENOMEM;
1109     }
1110 
1111     name_type = krb5_princ_type(context, req->user);
1112     p[0] = (name_type >> 0 ) & 0xFF;
1113     p[1] = (name_type >> 8 ) & 0xFF;
1114     p[2] = (name_type >> 16) & 0xFF;
1115     p[3] = (name_type >> 24) & 0xFF;
1116     p += 4;
1117 
1118     for (i = 0; i < krb5_princ_size(context, req->user); i++) {
1119         if (krb5_princ_component(context, req->user, i)->length > 0) {
1120             memcpy(p, krb5_princ_component(context, req->user, i)->data,
1121                    krb5_princ_component(context, req->user, i)->length);
1122         }
1123         p += krb5_princ_component(context, req->user, i)->length;
1124     }
1125 
1126     if (krb5_princ_realm(context, req->user)->length > 0) {
1127         memcpy(p, krb5_princ_realm(context, req->user)->data,
1128                krb5_princ_realm(context, req->user)->length);
1129     }
1130     p += krb5_princ_realm(context, req->user)->length;
1131 
1132     if (req->auth_package.length > 0)
1133         memcpy(p, req->auth_package.data, req->auth_package.length);
1134     p += req->auth_package.length;
1135 
1136     code = krb5_c_verify_checksum(context,
1137                                   key,
1138                                   KRB5_KEYUSAGE_APP_DATA_CKSUM,
1139                                   &data,
1140                                   &req->cksum,
1141                                   &valid);
1142 
1143     if (code == 0 && valid == FALSE)
1144         code = KRB5KRB_AP_ERR_MODIFIED;
1145 
1146     free(data.data);
1147 
1148     return code;
1149 }
1150 
1151 /*
1152  * Legacy protocol transition (Windows 2003 and above)
1153  */
1154 static krb5_error_code
kdc_process_for_user(kdc_realm_t * kdc_active_realm,krb5_pa_data * pa_data,krb5_keyblock * tgs_session,krb5_pa_s4u_x509_user ** s4u_x509_user,const char ** status)1155 kdc_process_for_user(kdc_realm_t *kdc_active_realm,
1156                      krb5_pa_data *pa_data,
1157                      krb5_keyblock *tgs_session,
1158                      krb5_pa_s4u_x509_user **s4u_x509_user,
1159                      const char **status)
1160 {
1161     krb5_error_code             code;
1162     krb5_pa_for_user            *for_user;
1163     krb5_data                   req_data;
1164 
1165     req_data.length = pa_data->length;
1166     req_data.data = (char *)pa_data->contents;
1167 
1168     code = decode_krb5_pa_for_user(&req_data, &for_user);
1169     if (code) {
1170         *status = "DECODE_PA_FOR_USER";
1171         return code;
1172     }
1173 
1174     code = verify_for_user_checksum(kdc_context, tgs_session, for_user);
1175     if (code) {
1176         *status = "INVALID_S4U2SELF_CHECKSUM";
1177         krb5_free_pa_for_user(kdc_context, for_user);
1178         return code;
1179     }
1180 
1181     *s4u_x509_user = calloc(1, sizeof(krb5_pa_s4u_x509_user));
1182     if (*s4u_x509_user == NULL) {
1183         krb5_free_pa_for_user(kdc_context, for_user);
1184         return ENOMEM;
1185     }
1186 
1187     (*s4u_x509_user)->user_id.user = for_user->user;
1188     for_user->user = NULL;
1189     krb5_free_pa_for_user(kdc_context, for_user);
1190 
1191     return 0;
1192 }
1193 
1194 static krb5_error_code
verify_s4u_x509_user_checksum(krb5_context context,krb5_keyblock * key,krb5_data * req_data,krb5_int32 kdc_req_nonce,krb5_pa_s4u_x509_user * req)1195 verify_s4u_x509_user_checksum(krb5_context context,
1196                               krb5_keyblock *key,
1197                               krb5_data *req_data,
1198                               krb5_int32 kdc_req_nonce,
1199                               krb5_pa_s4u_x509_user *req)
1200 {
1201     krb5_error_code             code;
1202     krb5_data                   scratch;
1203     krb5_boolean                valid = FALSE;
1204 
1205     if (enctype_requires_etype_info_2(key->enctype) &&
1206         !krb5_c_is_keyed_cksum(req->cksum.checksum_type))
1207         return KRB5KRB_AP_ERR_INAPP_CKSUM;
1208 
1209     if (req->user_id.nonce != kdc_req_nonce)
1210         return KRB5KRB_AP_ERR_MODIFIED;
1211 
1212     /*
1213      * Verify checksum over the encoded userid. If that fails,
1214      * re-encode, and verify that. This is similar to the
1215      * behaviour in kdc_process_tgs_req().
1216      */
1217     if (fetch_asn1_field((unsigned char *)req_data->data, 1, 0, &scratch) < 0)
1218         return ASN1_PARSE_ERROR;
1219 
1220     code = krb5_c_verify_checksum(context,
1221                                   key,
1222                                   KRB5_KEYUSAGE_PA_S4U_X509_USER_REQUEST,
1223                                   &scratch,
1224                                   &req->cksum,
1225                                   &valid);
1226     if (code != 0)
1227         return code;
1228 
1229     if (valid == FALSE) {
1230         krb5_data *data;
1231 
1232         code = encode_krb5_s4u_userid(&req->user_id, &data);
1233         if (code != 0)
1234             return code;
1235 
1236         code = krb5_c_verify_checksum(context,
1237                                       key,
1238                                       KRB5_KEYUSAGE_PA_S4U_X509_USER_REQUEST,
1239                                       data,
1240                                       &req->cksum,
1241                                       &valid);
1242 
1243         krb5_free_data(context, data);
1244 
1245         if (code != 0)
1246             return code;
1247     }
1248 
1249     return valid ? 0 : KRB5KRB_AP_ERR_MODIFIED;
1250 }
1251 
1252 /*
1253  * New protocol transition request (Windows 2008 and above)
1254  */
1255 static krb5_error_code
kdc_process_s4u_x509_user(krb5_context context,krb5_kdc_req * request,krb5_pa_data * pa_data,krb5_keyblock * tgs_subkey,krb5_keyblock * tgs_session,krb5_pa_s4u_x509_user ** s4u_x509_user,const char ** status)1256 kdc_process_s4u_x509_user(krb5_context context,
1257                           krb5_kdc_req *request,
1258                           krb5_pa_data *pa_data,
1259                           krb5_keyblock *tgs_subkey,
1260                           krb5_keyblock *tgs_session,
1261                           krb5_pa_s4u_x509_user **s4u_x509_user,
1262                           const char **status)
1263 {
1264     krb5_error_code             code;
1265     krb5_data                   req_data;
1266 
1267     req_data.length = pa_data->length;
1268     req_data.data = (char *)pa_data->contents;
1269 
1270     code = decode_krb5_pa_s4u_x509_user(&req_data, s4u_x509_user);
1271     if (code) {
1272         *status = "DECODE_PA_S4U_X509_USER";
1273         return code;
1274     }
1275 
1276     code = verify_s4u_x509_user_checksum(context,
1277                                          tgs_subkey ? tgs_subkey :
1278                                          tgs_session,
1279                                          &req_data,
1280                                          request->nonce, *s4u_x509_user);
1281 
1282     if (code) {
1283         *status = "INVALID_S4U2SELF_CHECKSUM";
1284         krb5_free_pa_s4u_x509_user(context, *s4u_x509_user);
1285         *s4u_x509_user = NULL;
1286         return code;
1287     }
1288 
1289     if (krb5_princ_size(context, (*s4u_x509_user)->user_id.user) == 0 &&
1290         (*s4u_x509_user)->user_id.subject_cert.length == 0) {
1291         *status = "INVALID_S4U2SELF_REQUEST";
1292         krb5_free_pa_s4u_x509_user(context, *s4u_x509_user);
1293         *s4u_x509_user = NULL;
1294         return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
1295     }
1296 
1297     return 0;
1298 }
1299 
1300 krb5_error_code
kdc_make_s4u2self_rep(krb5_context context,krb5_keyblock * tgs_subkey,krb5_keyblock * tgs_session,krb5_pa_s4u_x509_user * req_s4u_user,krb5_kdc_rep * reply,krb5_enc_kdc_rep_part * reply_encpart)1301 kdc_make_s4u2self_rep(krb5_context context,
1302                       krb5_keyblock *tgs_subkey,
1303                       krb5_keyblock *tgs_session,
1304                       krb5_pa_s4u_x509_user *req_s4u_user,
1305                       krb5_kdc_rep *reply,
1306                       krb5_enc_kdc_rep_part *reply_encpart)
1307 {
1308     krb5_error_code             code;
1309     krb5_data                   *der_user_id = NULL, *der_s4u_x509_user = NULL;
1310     krb5_pa_s4u_x509_user       rep_s4u_user;
1311     krb5_pa_data                *pa = NULL;
1312     krb5_enctype                enctype;
1313     krb5_keyusage               usage;
1314 
1315     memset(&rep_s4u_user, 0, sizeof(rep_s4u_user));
1316 
1317     rep_s4u_user.user_id.nonce   = req_s4u_user->user_id.nonce;
1318     rep_s4u_user.user_id.user    = req_s4u_user->user_id.user;
1319     rep_s4u_user.user_id.options =
1320         req_s4u_user->user_id.options & KRB5_S4U_OPTS_USE_REPLY_KEY_USAGE;
1321 
1322     code = encode_krb5_s4u_userid(&rep_s4u_user.user_id, &der_user_id);
1323     if (code != 0)
1324         goto cleanup;
1325 
1326     if (req_s4u_user->user_id.options & KRB5_S4U_OPTS_USE_REPLY_KEY_USAGE)
1327         usage = KRB5_KEYUSAGE_PA_S4U_X509_USER_REPLY;
1328     else
1329         usage = KRB5_KEYUSAGE_PA_S4U_X509_USER_REQUEST;
1330 
1331     code = krb5_c_make_checksum(context, req_s4u_user->cksum.checksum_type,
1332                                 tgs_subkey != NULL ? tgs_subkey : tgs_session,
1333                                 usage, der_user_id, &rep_s4u_user.cksum);
1334     if (code != 0)
1335         goto cleanup;
1336 
1337     code = encode_krb5_pa_s4u_x509_user(&rep_s4u_user, &der_s4u_x509_user);
1338     if (code != 0)
1339         goto cleanup;
1340 
1341     code = k5_add_pa_data_from_data(&reply->padata, KRB5_PADATA_S4U_X509_USER,
1342                                     der_s4u_x509_user);
1343     if (code != 0)
1344         goto cleanup;
1345 
1346     if (tgs_subkey != NULL)
1347         enctype = tgs_subkey->enctype;
1348     else
1349         enctype = tgs_session->enctype;
1350 
1351     /*
1352      * Owing to a bug in Windows, unkeyed checksums were used for older
1353      * enctypes, including rc4-hmac. A forthcoming workaround for this
1354      * includes the checksum bytes in the encrypted padata.
1355      */
1356     if (enctype_requires_etype_info_2(enctype) == FALSE) {
1357         code = k5_alloc_pa_data(KRB5_PADATA_S4U_X509_USER,
1358                                 req_s4u_user->cksum.length +
1359                                 rep_s4u_user.cksum.length, &pa);
1360         if (code != 0)
1361             goto cleanup;
1362         memcpy(pa->contents,
1363                req_s4u_user->cksum.contents, req_s4u_user->cksum.length);
1364         memcpy(&pa->contents[req_s4u_user->cksum.length],
1365                rep_s4u_user.cksum.contents, rep_s4u_user.cksum.length);
1366 
1367         code = k5_add_pa_data_element(&reply_encpart->enc_padata, &pa);
1368         if (code != 0)
1369             goto cleanup;
1370     }
1371 
1372 cleanup:
1373     if (rep_s4u_user.cksum.contents != NULL)
1374         krb5_free_checksum_contents(context, &rep_s4u_user.cksum);
1375     krb5_free_data(context, der_user_id);
1376     krb5_free_data(context, der_s4u_x509_user);
1377     k5_free_pa_data_element(pa);
1378     return code;
1379 }
1380 
1381 /* Return true if princ canonicalizes to the same principal as entry's. */
1382 krb5_boolean
is_client_db_alias(krb5_context context,const krb5_db_entry * entry,krb5_const_principal princ)1383 is_client_db_alias(krb5_context context, const krb5_db_entry *entry,
1384                    krb5_const_principal princ)
1385 {
1386     krb5_error_code ret;
1387     krb5_db_entry *self;
1388     krb5_boolean is_self = FALSE;
1389 
1390     ret = krb5_db_get_principal(context, princ,
1391                                 KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY, &self);
1392     if (!ret) {
1393         is_self = krb5_principal_compare(context, entry->princ, self->princ);
1394         krb5_db_free_principal(context, self);
1395     }
1396 
1397     return is_self;
1398 }
1399 
1400 /*
1401  * If S4U2Self padata is present in request, verify the checksum and set
1402  * *s4u_x509_user to the S4U2Self request.  If the requested client realm is
1403  * local, look up the client and set *princ_ptr to its DB entry.
1404  */
1405 krb5_error_code
kdc_process_s4u2self_req(kdc_realm_t * kdc_active_realm,krb5_kdc_req * request,const krb5_db_entry * server,krb5_keyblock * tgs_subkey,krb5_keyblock * tgs_session,krb5_pa_s4u_x509_user ** s4u_x509_user,krb5_db_entry ** princ_ptr,const char ** status)1406 kdc_process_s4u2self_req(kdc_realm_t *kdc_active_realm,
1407                          krb5_kdc_req *request,
1408                          const krb5_db_entry *server,
1409                          krb5_keyblock *tgs_subkey,
1410                          krb5_keyblock *tgs_session,
1411                          krb5_pa_s4u_x509_user **s4u_x509_user,
1412                          krb5_db_entry **princ_ptr,
1413                          const char **status)
1414 {
1415     krb5_error_code             code;
1416     krb5_pa_data                *pa_data;
1417     krb5_db_entry               *princ;
1418     krb5_s4u_userid             *id;
1419 
1420     *princ_ptr = NULL;
1421 
1422     pa_data = krb5int_find_pa_data(kdc_context,
1423                                    request->padata, KRB5_PADATA_S4U_X509_USER);
1424     if (pa_data != NULL) {
1425         code = kdc_process_s4u_x509_user(kdc_context,
1426                                          request,
1427                                          pa_data,
1428                                          tgs_subkey,
1429                                          tgs_session,
1430                                          s4u_x509_user,
1431                                          status);
1432         if (code != 0)
1433             return code;
1434     } else {
1435         pa_data = krb5int_find_pa_data(kdc_context,
1436                                        request->padata, KRB5_PADATA_FOR_USER);
1437         if (pa_data != NULL) {
1438             code = kdc_process_for_user(kdc_active_realm,
1439                                         pa_data,
1440                                         tgs_session,
1441                                         s4u_x509_user,
1442                                         status);
1443             if (code != 0)
1444                 return code;
1445         } else
1446             return 0;
1447     }
1448     id = &(*s4u_x509_user)->user_id;
1449 
1450     if (data_eq(server->princ->realm, id->user->realm)) {
1451         if (id->subject_cert.length != 0) {
1452             code = krb5_db_get_s4u_x509_principal(kdc_context,
1453                                                   &id->subject_cert, id->user,
1454                                                   KRB5_KDB_FLAG_INCLUDE_PAC,
1455                                                   &princ);
1456             if (code == 0 && id->user->length == 0) {
1457                 krb5_free_principal(kdc_context, id->user);
1458                 code = krb5_copy_principal(kdc_context, princ->princ,
1459                                            &id->user);
1460             }
1461         } else {
1462             code = krb5_db_get_principal(kdc_context, id->user,
1463                                          KRB5_KDB_FLAG_INCLUDE_PAC, &princ);
1464         }
1465         if (code == KRB5_KDB_NOENTRY) {
1466             *status = "UNKNOWN_S4U2SELF_PRINCIPAL";
1467             return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
1468         } else if (code) {
1469             *status = "LOOKING_UP_S4U2SELF_PRINCIPAL";
1470             return code; /* caller can free for_user */
1471         }
1472 
1473         /* Ignore password expiration and needchange attributes (as Windows
1474          * does), since S4U2Self is not password authentication. */
1475         princ->pw_expiration = 0;
1476         clear(princ->attributes, KRB5_KDB_REQUIRES_PWCHANGE);
1477 
1478         *princ_ptr = princ;
1479     }
1480 
1481     return 0;
1482 }
1483 
1484 /*
1485  * Determine if an S4U2Proxy request is authorized.  Set **stkt_ad_info to the
1486  * KDB authdata handle for the second ticket if the KDB module supplied one.
1487  * Set *stkt_authdata_client to the subject client name if the KDB module
1488  * supplied one; it must do so for a cross-realm request to be authorized.
1489  */
1490 krb5_error_code
kdc_process_s4u2proxy_req(kdc_realm_t * kdc_active_realm,unsigned int flags,krb5_kdc_req * request,const krb5_enc_tkt_part * t2enc,krb5_db_entry * krbtgt,krb5_keyblock * krbtgt_key,const krb5_db_entry * server,krb5_keyblock * server_key,krb5_const_principal server_princ,const krb5_db_entry * proxy,krb5_const_principal proxy_princ,void * ad_info,void ** stkt_ad_info,krb5_principal * stkt_authdata_client,const char ** status)1491 kdc_process_s4u2proxy_req(kdc_realm_t *kdc_active_realm, unsigned int flags,
1492                           krb5_kdc_req *request,
1493                           const krb5_enc_tkt_part *t2enc,
1494                           krb5_db_entry *krbtgt, krb5_keyblock *krbtgt_key,
1495                           const krb5_db_entry *server,
1496                           krb5_keyblock *server_key,
1497                           krb5_const_principal server_princ,
1498                           const krb5_db_entry *proxy,
1499                           krb5_const_principal proxy_princ,
1500                           void *ad_info, void **stkt_ad_info,
1501                           krb5_principal *stkt_authdata_client,
1502                           const char **status)
1503 {
1504     krb5_error_code errcode;
1505     krb5_boolean support_rbcd;
1506     krb5_principal client_princ = t2enc->client;
1507 
1508     /* Check if the client supports resource-based constrained delegation. */
1509     errcode = kdc_get_pa_pac_rbcd(kdc_context, request->padata, &support_rbcd);
1510     if (errcode)
1511         return errcode;
1512 
1513     errcode = krb5_db_get_authdata_info(kdc_context, flags,
1514                                         t2enc->authorization_data,
1515                                         t2enc->client, proxy_princ, server_key,
1516                                         krbtgt_key, krbtgt,
1517                                         t2enc->times.authtime, stkt_ad_info,
1518                                         stkt_authdata_client);
1519     if (errcode != 0 && errcode != KRB5_PLUGIN_OP_NOTSUPP) {
1520         *status = "NOT_ALLOWED_TO_DELEGATE";
1521         return errcode;
1522     }
1523 
1524     /* For RBCD we require that both client and impersonator's authdata have
1525      * been verified. */
1526     if (errcode != 0 || ad_info == NULL)
1527         support_rbcd = FALSE;
1528 
1529     /* For an RBCD final request, the KDB module must be able to recover the
1530      * reply ticket client name from the evidence ticket authorization data. */
1531     if (isflagset(flags, KRB5_KDB_FLAG_CROSS_REALM)) {
1532         if (*stkt_authdata_client == NULL ||
1533             (*stkt_authdata_client)->realm.length == 0) {
1534             *status = "UNSUPPORTED_S4U2PROXY_REQUEST";
1535             return KRB5KDC_ERR_BADOPTION;
1536         }
1537 
1538         client_princ = *stkt_authdata_client;
1539     }
1540 
1541     /* If both are in the same realm, try allowed_to_delegate first. */
1542     if (krb5_realm_compare(kdc_context, server->princ, proxy_princ)) {
1543 
1544         errcode = krb5_db_check_allowed_to_delegate(kdc_context, client_princ,
1545                                                     server, proxy_princ);
1546         if (errcode != 0 && errcode != KRB5KDC_ERR_POLICY &&
1547             errcode != KRB5_PLUGIN_OP_NOTSUPP)
1548             return errcode;
1549 
1550         if (errcode == 0) {
1551 
1552             /*
1553              * In legacy constrained-delegation, the evidence ticket must be
1554              * forwardable.  This check deliberately causes an error response
1555              * even if the delegation is also authorized by resource-based
1556              * constrained delegation (which does not require a forwardable
1557              * evidence ticket).  Windows KDCs behave the same way.
1558              */
1559             if (!isflagset(t2enc->flags, TKT_FLG_FORWARDABLE)) {
1560                 *status = "EVIDENCE_TKT_NOT_FORWARDABLE";
1561                 return KRB5KDC_ERR_BADOPTION;
1562             }
1563 
1564             return 0;
1565         }
1566         /* Fall back to resource-based constrained-delegation. */
1567     }
1568 
1569     if (!support_rbcd) {
1570         *status = "UNSUPPORTED_S4U2PROXY_REQUEST";
1571         return KRB5KDC_ERR_BADOPTION;
1572     }
1573 
1574     /* If we are issuing a referral, the KDC in the resource realm will check
1575      * if delegation is allowed. */
1576     if (isflagset(flags, KRB5_KDB_FLAG_ISSUING_REFERRAL))
1577         return 0;
1578 
1579     errcode = krb5_db_allowed_to_delegate_from(kdc_context, client_princ,
1580                                                server_princ, ad_info, proxy);
1581     if (errcode)
1582         *status = "NOT_ALLOWED_TO_DELEGATE";
1583     return errcode;
1584 }
1585 
1586 krb5_error_code
kdc_check_transited_list(kdc_realm_t * kdc_active_realm,const krb5_data * trans,const krb5_data * realm1,const krb5_data * realm2)1587 kdc_check_transited_list(kdc_realm_t *kdc_active_realm,
1588                          const krb5_data *trans,
1589                          const krb5_data *realm1,
1590                          const krb5_data *realm2)
1591 {
1592     krb5_error_code             code;
1593 
1594     /* Check against the KDB module.  Treat this answer as authoritative if the
1595      * method is supported and doesn't explicitly pass control. */
1596     code = krb5_db_check_transited_realms(kdc_context, trans, realm1, realm2);
1597     if (code != KRB5_PLUGIN_OP_NOTSUPP && code != KRB5_PLUGIN_NO_HANDLE)
1598         return code;
1599 
1600     /* Check using krb5.conf [capaths] or hierarchical relationships. */
1601     return krb5_check_transited_list(kdc_context, trans, realm1, realm2);
1602 }
1603 
1604 krb5_boolean
enctype_requires_etype_info_2(krb5_enctype enctype)1605 enctype_requires_etype_info_2(krb5_enctype enctype)
1606 {
1607     switch(enctype) {
1608     case ENCTYPE_DES3_CBC_SHA1:
1609     case ENCTYPE_DES3_CBC_RAW:
1610     case ENCTYPE_ARCFOUR_HMAC:
1611     case ENCTYPE_ARCFOUR_HMAC_EXP :
1612         return 0;
1613     default:
1614         return krb5_c_valid_enctype(enctype);
1615     }
1616 }
1617 
1618 void
kdc_get_ticket_endtime(kdc_realm_t * kdc_active_realm,krb5_timestamp starttime,krb5_timestamp endtime,krb5_timestamp till,krb5_db_entry * client,krb5_db_entry * server,krb5_timestamp * out_endtime)1619 kdc_get_ticket_endtime(kdc_realm_t *kdc_active_realm,
1620                        krb5_timestamp starttime,
1621                        krb5_timestamp endtime,
1622                        krb5_timestamp till,
1623                        krb5_db_entry *client,
1624                        krb5_db_entry *server,
1625                        krb5_timestamp *out_endtime)
1626 {
1627     krb5_timestamp until;
1628     krb5_deltat life;
1629 
1630     if (till == 0)
1631         till = kdc_infinity;
1632 
1633     until = ts_min(till, endtime);
1634 
1635     /* Determine the requested lifetime, capped at the maximum valid time
1636      * interval. */
1637     life = ts_delta(until, starttime);
1638     if (ts_after(until, starttime) && life < 0)
1639         life = INT32_MAX;
1640 
1641     if (client != NULL && client->max_life != 0)
1642         life = min(life, client->max_life);
1643     if (server->max_life != 0)
1644         life = min(life, server->max_life);
1645     if (kdc_active_realm->realm_maxlife != 0)
1646         life = min(life, kdc_active_realm->realm_maxlife);
1647 
1648     *out_endtime = ts_incr(starttime, life);
1649 }
1650 
1651 /*
1652  * Set tkt->renew_till to the requested renewable lifetime as modified by
1653  * policy.  Set the TKT_FLG_RENEWABLE flag if we set a nonzero renew_till.
1654  * client and tgt may be NULL.
1655  */
1656 void
kdc_get_ticket_renewtime(kdc_realm_t * realm,krb5_kdc_req * request,krb5_enc_tkt_part * tgt,krb5_db_entry * client,krb5_db_entry * server,krb5_enc_tkt_part * tkt)1657 kdc_get_ticket_renewtime(kdc_realm_t *realm, krb5_kdc_req *request,
1658                          krb5_enc_tkt_part *tgt, krb5_db_entry *client,
1659                          krb5_db_entry *server, krb5_enc_tkt_part *tkt)
1660 {
1661     krb5_timestamp rtime, max_rlife;
1662 
1663     clear(tkt->flags, TKT_FLG_RENEWABLE);
1664     tkt->times.renew_till = 0;
1665 
1666     /* Don't issue renewable tickets if the client or server don't allow it,
1667      * or if this is a TGS request and the TGT isn't renewable. */
1668     if (server->attributes & KRB5_KDB_DISALLOW_RENEWABLE)
1669         return;
1670     if (client != NULL && (client->attributes & KRB5_KDB_DISALLOW_RENEWABLE))
1671         return;
1672     if (tgt != NULL && !(tgt->flags & TKT_FLG_RENEWABLE))
1673         return;
1674 
1675     /* Determine the requested renewable time. */
1676     if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE))
1677         rtime = request->rtime ? request->rtime : kdc_infinity;
1678     else if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE_OK) &&
1679              ts_after(request->till, tkt->times.endtime))
1680         rtime = request->till;
1681     else
1682         return;
1683 
1684     /* Truncate it to the allowable renewable time. */
1685     if (tgt != NULL)
1686         rtime = ts_min(rtime, tgt->times.renew_till);
1687     max_rlife = min(server->max_renewable_life, realm->realm_maxrlife);
1688     if (client != NULL)
1689         max_rlife = min(max_rlife, client->max_renewable_life);
1690     rtime = ts_min(rtime, ts_incr(tkt->times.starttime, max_rlife));
1691 
1692     /* If the client only specified renewable-ok, don't issue a renewable
1693      * ticket unless the truncated renew time exceeds the ticket end time. */
1694     if (!isflagset(request->kdc_options, KDC_OPT_RENEWABLE) &&
1695         !ts_after(rtime, tkt->times.endtime))
1696         return;
1697 
1698     setflag(tkt->flags, TKT_FLG_RENEWABLE);
1699     tkt->times.renew_till = rtime;
1700 }
1701 
1702 /**
1703  * Handle protected negotiation of FAST using enc_padata
1704  * - If ENCPADATA_REQ_ENC_PA_REP is present, then:
1705  * - Return ENCPADATA_REQ_ENC_PA_REP with checksum of AS-REQ from client
1706  * - Include PADATA_FX_FAST in the enc_padata to indicate FAST
1707  * @pre @c out_enc_padata has space for at least two more padata
1708  * @param index in/out index into @c out_enc_padata for next item
1709  */
1710 krb5_error_code
kdc_handle_protected_negotiation(krb5_context context,krb5_data * req_pkt,krb5_kdc_req * request,const krb5_keyblock * reply_key,krb5_pa_data *** out_enc_padata)1711 kdc_handle_protected_negotiation(krb5_context context,
1712                                  krb5_data *req_pkt, krb5_kdc_req *request,
1713                                  const krb5_keyblock *reply_key,
1714                                  krb5_pa_data ***out_enc_padata)
1715 {
1716     krb5_error_code retval = 0;
1717     krb5_checksum checksum;
1718     krb5_data *der_cksum = NULL;
1719     krb5_pa_data *pa_in;
1720 
1721     memset(&checksum, 0, sizeof(checksum));
1722 
1723     pa_in = krb5int_find_pa_data(context, request->padata,
1724                                  KRB5_ENCPADATA_REQ_ENC_PA_REP);
1725     if (pa_in == NULL)
1726         return 0;
1727 
1728     /* Compute and encode a checksum over the AS-REQ. */
1729     retval = krb5_c_make_checksum(context, 0, reply_key, KRB5_KEYUSAGE_AS_REQ,
1730                                   req_pkt, &checksum);
1731     if (retval != 0)
1732         goto cleanup;
1733     retval = encode_krb5_checksum(&checksum, &der_cksum);
1734     if (retval != 0)
1735         goto cleanup;
1736 
1737     retval = k5_add_pa_data_from_data(out_enc_padata,
1738                                       KRB5_ENCPADATA_REQ_ENC_PA_REP,
1739                                       der_cksum);
1740     if (retval)
1741         goto cleanup;
1742 
1743     /* Add a zero-length PA-FX-FAST element to the list. */
1744     retval = k5_add_empty_pa_data(out_enc_padata, KRB5_PADATA_FX_FAST);
1745 
1746 cleanup:
1747     krb5_free_checksum_contents(context, &checksum);
1748     krb5_free_data(context, der_cksum);
1749     return retval;
1750 }
1751 
1752 krb5_error_code
kdc_get_pa_pac_options(krb5_context context,krb5_pa_data ** in_padata,krb5_pa_pac_options ** pac_options_out)1753 kdc_get_pa_pac_options(krb5_context context, krb5_pa_data **in_padata,
1754                        krb5_pa_pac_options **pac_options_out)
1755 {
1756     krb5_pa_data *pa;
1757     krb5_data der_pac_options;
1758 
1759     *pac_options_out = NULL;
1760 
1761     pa = krb5int_find_pa_data(context, in_padata, KRB5_PADATA_PAC_OPTIONS);
1762     if (pa == NULL)
1763         return 0;
1764 
1765     der_pac_options = make_data(pa->contents, pa->length);
1766     return decode_krb5_pa_pac_options(&der_pac_options, pac_options_out);
1767 }
1768 
1769 krb5_error_code
kdc_add_pa_pac_options(krb5_context context,krb5_kdc_req * request,krb5_pa_data *** out_enc_padata)1770 kdc_add_pa_pac_options(krb5_context context, krb5_kdc_req *request,
1771                        krb5_pa_data ***out_enc_padata)
1772 {
1773     krb5_error_code ret;
1774     krb5_pa_pac_options *pac_options = NULL;
1775     krb5_data *der_pac_options;
1776 
1777     ret = kdc_get_pa_pac_options(context, request->padata, &pac_options);
1778     if (ret || pac_options == NULL)
1779         return ret;
1780 
1781     /* Only return supported PAC options (currently only resource-based
1782      * constrained delegation support). */
1783     pac_options->options &= KRB5_PA_PAC_OPTIONS_RBCD;
1784     if (pac_options->options == 0) {
1785         free(pac_options);
1786         return 0;
1787     }
1788 
1789     ret = encode_krb5_pa_pac_options(pac_options, &der_pac_options);
1790     free(pac_options);
1791     if (ret)
1792         return ret;
1793 
1794     ret = k5_add_pa_data_from_data(out_enc_padata, KRB5_PADATA_PAC_OPTIONS,
1795                                    der_pac_options);
1796     krb5_free_data(context, der_pac_options);
1797     return ret;
1798 }
1799 
1800 krb5_error_code
kdc_get_pa_pac_rbcd(krb5_context context,krb5_pa_data ** in_padata,krb5_boolean * supported)1801 kdc_get_pa_pac_rbcd(krb5_context context, krb5_pa_data **in_padata,
1802                     krb5_boolean *supported)
1803 {
1804     krb5_error_code retval;
1805     krb5_pa_pac_options *pac_options = NULL;
1806 
1807     *supported = FALSE;
1808 
1809     retval = kdc_get_pa_pac_options(context, in_padata, &pac_options);
1810     if (retval || !pac_options)
1811         return retval;
1812 
1813     if (pac_options->options & KRB5_PA_PAC_OPTIONS_RBCD)
1814         *supported = TRUE;
1815 
1816     free(pac_options);
1817     return 0;
1818 }
1819 
1820 /*
1821  * Although the KDC doesn't call this function directly,
1822  * process_tcp_connection_read() in net-server.c does call it.
1823  */
1824 krb5_error_code
make_toolong_error(void * handle,krb5_data ** out)1825 make_toolong_error (void *handle, krb5_data **out)
1826 {
1827     krb5_error errpkt;
1828     krb5_error_code retval;
1829     krb5_data *scratch;
1830     struct server_handle *h = handle;
1831 
1832     retval = krb5_us_timeofday(h->kdc_err_context,
1833                                &errpkt.stime, &errpkt.susec);
1834     if (retval)
1835         return retval;
1836     errpkt.error = KRB_ERR_FIELD_TOOLONG;
1837     errpkt.server = h->kdc_realmlist[0]->realm_tgsprinc;
1838     errpkt.client = NULL;
1839     errpkt.cusec = 0;
1840     errpkt.ctime = 0;
1841     errpkt.text.length = 0;
1842     errpkt.text.data = 0;
1843     errpkt.e_data.length = 0;
1844     errpkt.e_data.data = 0;
1845     scratch = malloc(sizeof(*scratch));
1846     if (scratch == NULL)
1847         return ENOMEM;
1848     retval = krb5_mk_error(h->kdc_err_context, &errpkt, scratch);
1849     if (retval) {
1850         free(scratch);
1851         return retval;
1852     }
1853 
1854     *out = scratch;
1855     return 0;
1856 }
1857 
reset_for_hangup(void * ctx)1858 void reset_for_hangup(void *ctx)
1859 {
1860     int k;
1861     struct server_handle *h = ctx;
1862 
1863     for (k = 0; k < h->kdc_numrealms; k++)
1864         krb5_db_refresh_config(h->kdc_realmlist[k]->realm_context);
1865 }
1866