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