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