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