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