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