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