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