4 * Copyright 1990,1991,2007,2008 by the Massachusetts Institute of Technology.
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.
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.
27 * Utility functions for the KDC implementation.
30 * Copyright (c) 2006-2008, Novell, Inc.
31 * All rights reserved.
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions are met:
36 * * Redistributions of source code must retain the above copyright notice,
37 * this list of conditions and the following disclaimer.
38 * * Redistributions in binary form must reproduce the above copyright
39 * notice, this list of conditions and the following disclaimer in the
40 * documentation and/or other materials provided with the distribution.
41 * * The copyright holder's name is not used to endorse or promote products
42 * derived from this software without specific prior written permission.
44 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
45 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
46 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
47 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
48 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
49 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
50 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
51 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
52 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
53 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
54 * POSSIBILITY OF SUCH DAMAGE.
64 #include "adm_proto.h"
68 static char *kdc_current_rcname = (char *) NULL;
69 krb5_deltat rc_lifetime; /* See kdc_initialize_rcache() */
74 * initialize the replay cache.
77 kdc_initialize_rcache(krb5_context kcontext, char *rcache_name)
79 krb5_error_code retval;
83 rcname = (rcache_name) ? rcache_name : kdc_current_rcname;
85 /* rc_lifetime used elsewhere to verify we're not */
86 /* replaying really old data */
87 rc_lifetime = kcontext->clockskew;
91 if (!(retval = krb5_rc_resolve_full(kcontext, &kdc_rcache, rcname))) {
92 /* Recover or initialize the replay cache */
93 if (!(retval = krb5_rc_recover(kcontext, kdc_rcache)) ||
94 !(retval = krb5_rc_initialize(kcontext,
98 /* Expunge the replay cache */
99 if (!(retval = krb5_rc_expunge(kcontext, kdc_rcache))) {
100 sname = kdc_current_rcname;
101 kdc_current_rcname = strdup(rcname);
107 krb5_rc_close(kcontext, kdc_rcache);
114 * concatenate first two authdata arrays, returning an allocated replacement.
115 * The replacement should be freed with krb5_free_authdata().
118 concat_authorization_data(krb5_authdata **first, krb5_authdata **second,
119 krb5_authdata ***output)
122 register krb5_authdata **ptr, **retdata;
124 /* count up the entries */
127 for (ptr = first; *ptr; ptr++)
130 for (ptr = second; *ptr; ptr++)
133 retdata = (krb5_authdata **)malloc((i+1)*sizeof(*retdata));
136 retdata[i] = 0; /* null-terminated array */
137 for (i = 0, j = 0, ptr = first; j < 2 ; ptr = second, j++)
138 while (ptr && *ptr) {
139 /* now walk & copy */
140 retdata[i] = (krb5_authdata *)malloc(sizeof(*retdata[i]));
142 krb5_free_authdata(kdc_context, retdata);
146 if (!(retdata[i]->contents =
147 (krb5_octet *)malloc(retdata[i]->length))) {
148 free((char *)retdata[i]);
150 krb5_free_authdata(kdc_context, retdata);
153 memcpy((char *) retdata[i]->contents,
154 (char *)(*ptr)->contents,
165 realm_compare(krb5_const_principal princ1, krb5_const_principal princ2)
167 return krb5_realm_compare(kdc_context, princ1, princ2);
171 is_local_principal(krb5_const_principal princ1)
173 return krb5_realm_compare(kdc_context, princ1, tgs_server);
177 * Returns TRUE if the kerberos principal is the name of a Kerberos ticket
180 krb5_boolean krb5_is_tgs_principal(krb5_const_principal principal)
182 if ((krb5_princ_size(kdc_context, principal) > 0) &&
183 data_eq_string (*krb5_princ_component(kdc_context, principal, 0),
190 * given authentication data (provides seed for checksum), verify checksum
193 static krb5_error_code
194 comp_cksum(krb5_context kcontext, krb5_data *source, krb5_ticket *ticket,
195 krb5_checksum *his_cksum)
197 krb5_error_code retval;
200 if (!krb5_c_valid_cksumtype(his_cksum->checksum_type))
201 return KRB5KDC_ERR_SUMTYPE_NOSUPP;
203 /* must be collision proof */
204 if (!krb5_c_is_coll_proof_cksum(his_cksum->checksum_type))
205 return KRB5KRB_AP_ERR_INAPP_CKSUM;
207 /* verify checksum */
208 if ((retval = krb5_c_verify_checksum(kcontext, ticket->enc_part2->session,
209 KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
210 source, his_cksum, &valid)))
214 return(KRB5KRB_AP_ERR_BAD_INTEGRITY);
220 find_pa_data(krb5_pa_data **padata, krb5_preauthtype pa_type)
222 krb5_pa_data **tmppa;
227 for (tmppa = padata; *tmppa != NULL; tmppa++) {
228 if ((*tmppa)->pa_type == pa_type)
236 kdc_process_tgs_req(krb5_kdc_req *request, const krb5_fulladdr *from,
237 krb5_data *pkt, krb5_ticket **ticket,
238 krb5_db_entry *krbtgt, int *nprincs,
239 krb5_keyblock **subkey)
241 krb5_pa_data * tmppa;
243 krb5_error_code retval;
245 krb5_data * scratch = NULL;
246 krb5_boolean foreign_server = FALSE;
247 krb5_auth_context auth_context = NULL;
248 krb5_authenticator * authenticator = NULL;
249 krb5_checksum * his_cksum = NULL;
250 krb5_keyblock * key = NULL;
255 tmppa = find_pa_data(request->padata, KRB5_PADATA_AP_REQ);
257 return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
259 scratch1.length = tmppa->length;
260 scratch1.data = (char *)tmppa->contents;
261 if ((retval = decode_krb5_ap_req(&scratch1, &apreq)))
264 if (isflagset(apreq->ap_options, AP_OPTS_USE_SESSION_KEY) ||
265 isflagset(apreq->ap_options, AP_OPTS_MUTUAL_REQUIRED)) {
266 krb5_klog_syslog(LOG_INFO, "TGS_REQ: SESSION KEY or MUTUAL");
267 retval = KRB5KDC_ERR_POLICY;
271 /* If the "server" principal in the ticket is not something
272 in the local realm, then we must refuse to service the request
273 if the client claims to be from the local realm.
275 If we don't do this, then some other realm's nasty KDC can
276 claim to be authenticating a client from our realm, and we'll
277 give out tickets concurring with it!
279 we set a flag here for checking below.
281 foreign_server = !is_local_principal(apreq->ticket->server);
283 if ((retval = krb5_auth_con_init(kdc_context, &auth_context)))
286 if ((retval = krb5_auth_con_setaddrs(kdc_context, auth_context, NULL,
288 goto cleanup_auth_context;
290 if ((retval = krb5_auth_con_setrcache(kdc_context, auth_context,
292 goto cleanup_auth_context;
295 if ((retval = kdc_get_server_key(apreq->ticket, 0, foreign_server,
296 krbtgt, nprincs, &key, &kvno)))
297 goto cleanup_auth_context;
299 * We do not use the KDB keytab because other parts of the TGS need the TGT key.
301 retval = krb5_auth_con_setuseruserkey(kdc_context, auth_context, key);
302 krb5_free_keyblock(kdc_context, key);
304 goto cleanup_auth_context;
306 if ((retval = krb5_rd_req_decoded_anyflag(kdc_context, &auth_context, apreq,
307 apreq->ticket->server,
308 kdc_active_realm->realm_keytab,
312 * I'm not so sure that this is right, but it's better than nothing
315 * If we choke in the rd_req because of the replay cache, then attempt
316 * to reinitialize the replay cache because somebody could have deleted
317 * it from underneath us (e.g. a cron job)
319 if ((retval == KRB5_RC_IO_IO) ||
320 (retval == KRB5_RC_IO_UNKNOWN)) {
321 (void) krb5_rc_close(kdc_context, kdc_rcache);
322 kdc_rcache = (krb5_rcache) NULL;
323 if (!(retval = kdc_initialize_rcache(kdc_context, (char *) NULL))) {
324 if ((retval = krb5_auth_con_setrcache(kdc_context, auth_context,
326 (retval = krb5_rd_req_decoded_anyflag(kdc_context, &auth_context,
327 apreq, apreq->ticket->server,
328 kdc_active_realm->realm_keytab,
331 goto cleanup_auth_context;
334 goto cleanup_auth_context;
336 goto cleanup_auth_context;
340 /* "invalid flag" tickets can must be used to validate */
341 if (isflagset((*ticket)->enc_part2->flags, TKT_FLG_INVALID)
342 && !isflagset(request->kdc_options, KDC_OPT_VALIDATE)) {
343 retval = KRB5KRB_AP_ERR_TKT_INVALID;
344 goto cleanup_auth_context;
347 if ((retval = krb5_auth_con_getrecvsubkey(kdc_context,
348 auth_context, subkey)))
349 goto cleanup_auth_context;
351 if ((retval = krb5_auth_con_getauthenticator(kdc_context, auth_context,
353 goto cleanup_auth_context;
355 /* Check for a checksum */
356 if (!(his_cksum = authenticator->checksum)) {
357 retval = KRB5KRB_AP_ERR_INAPP_CKSUM;
358 goto cleanup_authenticator;
361 /* make sure the client is of proper lineage (see above) */
362 if (foreign_server && !find_pa_data(request->padata, KRB5_PADATA_FOR_USER)) {
363 if (is_local_principal((*ticket)->enc_part2->client)) {
364 /* someone in a foreign realm claiming to be local */
365 krb5_klog_syslog(LOG_INFO, "PROCESS_TGS: failed lineage check");
366 retval = KRB5KDC_ERR_POLICY;
367 goto cleanup_authenticator;
372 * Check application checksum vs. tgs request
374 * We try checksumming the req-body two different ways: first we
375 * try reaching into the raw asn.1 stream (if available), and
376 * checksum that directly; if that fails, then we try encoding
377 * using our local asn.1 library.
379 if (pkt && (fetch_asn1_field((unsigned char *) pkt->data,
380 1, 4, &scratch1) >= 0)) {
381 if (comp_cksum(kdc_context, &scratch1, *ticket, his_cksum)) {
382 if (!(retval = encode_krb5_kdc_req_body(request, &scratch)))
383 retval = comp_cksum(kdc_context, scratch, *ticket, his_cksum);
384 krb5_free_data(kdc_context, scratch);
388 cleanup_authenticator:
389 krb5_free_authenticator(kdc_context, authenticator);
391 cleanup_auth_context:
392 /* We do not want the free of the auth_context to close the rcache */
394 (void) krb5_auth_con_setrcache(kdc_context, auth_context, 0);
396 krb5_auth_con_free(kdc_context, auth_context);
399 krb5_free_ap_req(kdc_context, apreq);
403 /* XXX This function should no longer be necessary.
404 * The KDC should take the keytab associated with the realm and pass that to
405 * the krb5_rd_req_decode(). --proven
407 * It's actually still used by do_tgs_req() for u2u auth, and not too
411 kdc_get_server_key(krb5_ticket *ticket, unsigned int flags,
412 krb5_boolean match_enctype, krb5_db_entry *server,
413 int *nprincs, krb5_keyblock **key, krb5_kvno *kvno)
415 krb5_error_code retval;
416 krb5_boolean more, similar;
417 krb5_key_data * server_key;
421 retval = krb5_db_get_principal_ext(kdc_context,
431 return(KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE);
432 } else if (*nprincs != 1) {
435 if (!krb5_unparse_name(kdc_context, ticket->server, &sname)) {
436 krb5_klog_syslog(LOG_ERR,"TGS_REQ: UNKNOWN SERVER: server='%s'",
440 return(KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN);
442 if (server->attributes & KRB5_KDB_DISALLOW_SVR ||
443 server->attributes & KRB5_KDB_DISALLOW_ALL_TIX) {
444 retval = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
447 retval = krb5_dbe_find_enctype(kdc_context, server,
448 match_enctype ? ticket->enc_part.enctype : -1,
449 -1, (krb5_int32)ticket->enc_part.kvno,
454 retval = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
457 if ((*key = (krb5_keyblock *)malloc(sizeof **key))) {
458 retval = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock,
463 retval = krb5_c_enctype_compare(kdc_context, ticket->enc_part.enctype,
464 (*key)->enctype, &similar);
468 retval = KRB5_KDB_NO_PERMITTED_KEY;
471 (*key)->enctype = ticket->enc_part.enctype;
472 *kvno = server_key->key_data_kvno;
475 krb5_db_free_principal(kdc_context, server, *nprincs);
482 /* This probably wants to be updated if you support last_req stuff */
484 static krb5_last_req_entry nolrentry = { KV5M_LAST_REQ_ENTRY, KRB5_LRQ_NONE, 0 };
485 static krb5_last_req_entry *nolrarray[] = { &nolrentry, 0 };
488 fetch_last_req_info(krb5_db_entry *dbentry, krb5_last_req_entry ***lrentry)
490 *lrentry = nolrarray;
495 /* XXX! This is a temporary place-holder */
498 check_hot_list(krb5_ticket *ticket)
504 #define MAX_REALM_LN 500
508 * subrealm - determine if r2 is a subrealm of r1
510 * SUBREALM takes two realms, r1 and r2, and
511 * determines if r2 is a subrealm of r1.
512 * r2 is a subrealm of r1 if (r1 is a prefix
513 * of r2 AND r1 and r2 begin with a /) or if
514 * (r1 is a suffix of r2 and neither r1 nor r2
517 * RETURNS: If r2 is a subrealm, and r1 is a prefix, the number
518 * of characters in the suffix of r2 is returned as a
521 * If r2 is a subrealm, and r1 is a suffix, the number
522 * of characters in the prefix of r2 is returned as a
525 * If r2 is not a subrealm, SUBREALM returns 0.
528 subrealm(char *r1, char *r2)
533 if(l2 <= l1) return(0);
534 if((*r1 == '/') && (*r2 == '/') && (strncmp(r1,r2,l1) == 0)) return(l1-l2);
535 if((*r1 != '/') && (*r2 != '/') && (strncmp(r1,r2+l2-l1,l1) == 0))
541 * add_to_transited Adds the name of the realm which issued the
542 * ticket granting ticket on which the new ticket to
543 * be issued is based (note that this is the same as
544 * the realm of the server listed in the ticket
547 * ASSUMPTIONS: This procedure assumes that the transited field from
548 * the existing ticket granting ticket already appears
549 * in compressed form. It will add the new realm while
550 * maintaining that form. As long as each successive
551 * realm is added using this (or a similar) routine, the
552 * transited field will be in compressed form. The
553 * basis step is an empty transited field which is, by
554 * its nature, in its most compressed form.
556 * ARGUMENTS: krb5_data *tgt_trans Transited field from TGT
557 * krb5_data *new_trans The transited field for the new ticket
558 * krb5_principal tgs Name of ticket granting server
559 * This includes the realm of the KDC
560 * that issued the ticket granting
561 * ticket. This is the realm that is
562 * to be added to the transited field.
563 * krb5_principal client Name of the client
564 * krb5_principal server The name of the requested server.
565 * This may be the an intermediate
566 * ticket granting server.
568 * The last two argument are needed since they are
569 * implicitly part of the transited field of the new ticket
570 * even though they are not explicitly listed.
572 * RETURNS: krb5_error_code - Success, or out of memory
574 * MODIFIES: new_trans: ->length will contain the length of the new
577 * If ->data was not null when this procedure
578 * is called, the memory referenced by ->data
579 * will be deallocated.
581 * Memory will be allocated for the new transited field
582 * ->data will be updated to point to the newly
585 * BUGS: The space allocated for the new transited field is the
586 * maximum that might be needed given the old transited field,
587 * and the realm to be added. This length is calculated
588 * assuming that no compression of the new realm is possible.
589 * This has no adverse consequences other than the allocation
590 * of more space than required.
592 * This procedure will not yet use the null subfield notation,
593 * and it will get confused if it sees it.
595 * This procedure does not check for quoted commas in realm
600 data2string (krb5_data *d)
603 s = malloc(d->length + 1);
605 memcpy(s, d->data, d->length);
612 add_to_transited(krb5_data *tgt_trans, krb5_data *new_trans,
613 krb5_principal tgs, krb5_principal client,
614 krb5_principal server)
616 krb5_error_code retval;
619 char *otrans, *otrans_ptr;
622 /* The following are for stepping through the transited field */
624 char prev[MAX_REALM_LN];
625 char next[MAX_REALM_LN];
626 char current[MAX_REALM_LN];
627 char exp[MAX_REALM_LN]; /* Expanded current realm name */
630 int clst, nlst; /* count of last character in current and next */
631 int pl, pl1; /* prefix length */
632 int added; /* TRUE = new realm has been added */
634 realm = data2string(krb5_princ_realm(kdc_context, tgs));
638 otrans = data2string(tgt_trans);
639 if (otrans == NULL) {
643 /* Keep track of start so we can free */
647 +1 for extra comma which may be added between
648 +1 for potential space when leading slash in realm */
649 bufsize = strlen(realm) + strlen(otrans) + 3;
650 if (bufsize > MAX_REALM_LN)
651 bufsize = MAX_REALM_LN;
652 if (!(trans = (char *) malloc(bufsize))) {
657 if (new_trans->data) free(new_trans->data);
658 new_trans->data = trans;
659 new_trans->length = 0;
663 /* For the purpose of appending, the realm preceding the first */
664 /* realm in the transited field is considered the null realm */
668 /* read field into current */
669 for (i = 0; *otrans != '\0';) {
670 if (*otrans == '\\') {
671 if (*(++otrans) == '\0')
676 if (*otrans == ',') {
680 current[i++] = *otrans++;
681 if (i >= MAX_REALM_LN) {
682 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
688 added = (krb5_princ_realm(kdc_context, client)->length == strlen(realm) &&
689 !strncmp(krb5_princ_realm(kdc_context, client)->data, realm, strlen(realm))) ||
690 (krb5_princ_realm(kdc_context, server)->length == strlen(realm) &&
691 !strncmp(krb5_princ_realm(kdc_context, server)->data, realm, strlen(realm)));
695 /* figure out expanded form of current name */
697 clst = strlen(current) - 1;
698 if (current[0] == ' ') {
699 strncpy(exp, current+1, sizeof(exp) - 1);
700 exp[sizeof(exp) - 1] = '\0';
702 else if ((current[0] == '/') && (prev[0] == '/')) {
703 strncpy(exp, prev, sizeof(exp) - 1);
704 exp[sizeof(exp) - 1] = '\0';
705 if (strlen(exp) + strlen(current) + 1 >= MAX_REALM_LN) {
706 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
709 strncat(exp, current, sizeof(exp) - 1 - strlen(exp));
711 else if (current[clst] == '.') {
712 strncpy(exp, current, sizeof(exp) - 1);
713 exp[sizeof(exp) - 1] = '\0';
714 if (strlen(exp) + strlen(prev) + 1 >= MAX_REALM_LN) {
715 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
718 strncat(exp, prev, sizeof(exp) - 1 - strlen(exp));
721 strncpy(exp, current, sizeof(exp) - 1);
722 exp[sizeof(exp) - 1] = '\0';
725 /* read field into next */
726 for (i = 0; *otrans != '\0';) {
727 if (*otrans == '\\') {
728 if (*(++otrans) == '\0')
733 if (*otrans == ',') {
737 next[i++] = *otrans++;
738 if (i >= MAX_REALM_LN) {
739 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
746 if (!strcmp(exp, realm)) added = TRUE;
748 /* If we still have to insert the new realm */
752 /* Is the next field compressed? If not, and if the new */
753 /* realm is a subrealm of the current realm, compress */
754 /* the new realm, and insert immediately following the */
755 /* current one. Note that we can not do this if the next*/
756 /* field is already compressed since it would mess up */
757 /* what has already been done. In most cases, this is */
758 /* not a problem because the realm to be added will be a */
759 /* subrealm of the next field too, and we will catch */
760 /* it in a future iteration. */
762 /* Note that the second test here is an unsigned comparison,
763 so the first half (or a cast) is also required. */
764 assert(nlst < 0 || nlst < (int)sizeof(next));
765 if ((nlst < 0 || next[nlst] != '.') &&
767 (pl = subrealm(exp, realm))) {
769 current[sizeof(current) - 1] = '\0';
770 if (strlen(current) + (pl>0?pl:-pl) + 2 >= MAX_REALM_LN) {
771 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
774 strncat(current, ",", sizeof(current) - 1 - strlen(current));
776 strncat(current, realm, (unsigned) pl);
779 strncat(current, realm+strlen(realm)+pl, (unsigned) (-pl));
783 /* Whether or not the next field is compressed, if the */
784 /* realm to be added is a superrealm of the current realm,*/
785 /* then the current realm can be compressed. First the */
786 /* realm to be added must be compressed relative to the */
787 /* previous realm (if possible), and then the current */
788 /* realm compressed relative to the new realm. Note that */
789 /* if the realm to be added is also a superrealm of the */
790 /* previous realm, it would have been added earlier, and */
791 /* we would not reach this step this time around. */
793 else if ((pl = subrealm(realm, exp))) {
796 if ((pl1 = subrealm(prev,realm))) {
797 if (strlen(current) + (pl1>0?pl1:-pl1) + 1 >= MAX_REALM_LN) {
798 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
802 strncat(current, realm, (unsigned) pl1);
805 strncat(current, realm+strlen(realm)+pl1, (unsigned) (-pl1));
808 else { /* If not a subrealm */
809 if ((realm[0] == '/') && prev[0]) {
810 if (strlen(current) + 2 >= MAX_REALM_LN) {
811 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
814 strncat(current, " ", sizeof(current) - 1 - strlen(current));
815 current[sizeof(current) - 1] = '\0';
817 if (strlen(current) + strlen(realm) + 1 >= MAX_REALM_LN) {
818 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
821 strncat(current, realm, sizeof(current) - 1 - strlen(current));
822 current[sizeof(current) - 1] = '\0';
824 if (strlen(current) + (pl>0?pl:-pl) + 2 >= MAX_REALM_LN) {
825 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
828 strncat(current,",", sizeof(current) - 1 - strlen(current));
829 current[sizeof(current) - 1] = '\0';
831 strncat(current, exp, (unsigned) pl);
834 strncat(current, exp+strlen(exp)+pl, (unsigned)(-pl));
839 if (new_trans->length != 0) {
840 if (strlcat(trans, ",", bufsize) >= bufsize) {
841 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
845 if (strlcat(trans, current, bufsize) >= bufsize) {
846 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
849 new_trans->length = strlen(trans);
851 strncpy(prev, exp, sizeof(prev) - 1);
852 prev[sizeof(prev) - 1] = '\0';
853 strncpy(current, next, sizeof(current) - 1);
854 current[sizeof(current) - 1] = '\0';
858 if (new_trans->length != 0) {
859 if (strlcat(trans, ",", bufsize) >= bufsize) {
860 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
864 if((realm[0] == '/') && trans[0]) {
865 if (strlcat(trans, " ", bufsize) >= bufsize) {
866 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
870 if (strlcat(trans, realm, bufsize) >= bufsize) {
871 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
874 new_trans->length = strlen(trans);
885 * Routines that validate a AS request; checks a lot of things. :-)
887 * Returns a Kerberos protocol error number, which is _not_ the same
888 * as a com_err error number!
890 #define AS_INVALID_OPTIONS (KDC_OPT_FORWARDED | KDC_OPT_PROXY |\
891 KDC_OPT_VALIDATE | KDC_OPT_RENEW | KDC_OPT_ENC_TKT_IN_SKEY)
893 validate_as_request(register krb5_kdc_req *request, krb5_db_entry client,
894 krb5_db_entry server, krb5_timestamp kdc_time,
900 * If an option is set that is only allowed in TGS requests, complain.
902 if (request->kdc_options & AS_INVALID_OPTIONS) {
903 *status = "INVALID AS OPTIONS";
904 return KDC_ERR_BADOPTION;
907 /* The client's password must not be expired, unless the server is
908 a KRB5_KDC_PWCHANGE_SERVICE. */
909 if (client.pw_expiration && client.pw_expiration < kdc_time &&
910 !isflagset(server.attributes, KRB5_KDB_PWCHANGE_SERVICE)) {
911 *status = "CLIENT KEY EXPIRED";
912 #ifdef KRBCONF_VAGUE_ERRORS
913 return(KRB_ERR_GENERIC);
915 return(KDC_ERR_KEY_EXP);
919 /* The client must not be expired */
920 if (client.expiration && client.expiration < kdc_time) {
921 *status = "CLIENT EXPIRED";
922 #ifdef KRBCONF_VAGUE_ERRORS
923 return(KRB_ERR_GENERIC);
925 return(KDC_ERR_NAME_EXP);
929 /* The server must not be expired */
930 if (server.expiration && server.expiration < kdc_time) {
931 *status = "SERVICE EXPIRED";
932 return(KDC_ERR_SERVICE_EXP);
936 * If the client requires password changing, then only allow the
939 if (isflagset(client.attributes, KRB5_KDB_REQUIRES_PWCHANGE) &&
940 !isflagset(server.attributes, KRB5_KDB_PWCHANGE_SERVICE)) {
941 *status = "REQUIRED PWCHANGE";
942 return(KDC_ERR_KEY_EXP);
945 /* Client and server must allow postdating tickets */
946 if ((isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) ||
947 isflagset(request->kdc_options, KDC_OPT_POSTDATED)) &&
948 (isflagset(client.attributes, KRB5_KDB_DISALLOW_POSTDATED) ||
949 isflagset(server.attributes, KRB5_KDB_DISALLOW_POSTDATED))) {
950 *status = "POSTDATE NOT ALLOWED";
951 return(KDC_ERR_CANNOT_POSTDATE);
955 * A Windows KDC will return KDC_ERR_PREAUTH_REQUIRED instead of
956 * KDC_ERR_POLICY in the following case:
958 * - KDC_OPT_FORWARDABLE is set in KDCOptions but local
959 * policy has KRB5_KDB_DISALLOW_FORWARDABLE set for the
961 * - KRB5_KDB_REQUIRES_PRE_AUTH is set for the client but
962 * preauthentication data is absent in the request.
964 * Hence, this check most be done after the check for preauth
965 * data, and is now performed by validate_forwardable().
968 /* Client and server must allow forwardable tickets */
969 if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) &&
970 (isflagset(client.attributes, KRB5_KDB_DISALLOW_FORWARDABLE) ||
971 isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))) {
972 *status = "FORWARDABLE NOT ALLOWED";
973 return(KDC_ERR_POLICY);
977 /* Client and server must allow renewable tickets */
978 if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE) &&
979 (isflagset(client.attributes, KRB5_KDB_DISALLOW_RENEWABLE) ||
980 isflagset(server.attributes, KRB5_KDB_DISALLOW_RENEWABLE))) {
981 *status = "RENEWABLE NOT ALLOWED";
982 return(KDC_ERR_POLICY);
985 /* Client and server must allow proxiable tickets */
986 if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE) &&
987 (isflagset(client.attributes, KRB5_KDB_DISALLOW_PROXIABLE) ||
988 isflagset(server.attributes, KRB5_KDB_DISALLOW_PROXIABLE))) {
989 *status = "PROXIABLE NOT ALLOWED";
990 return(KDC_ERR_POLICY);
993 /* Check to see if client is locked out */
994 if (isflagset(client.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
995 *status = "CLIENT LOCKED OUT";
996 return(KDC_ERR_CLIENT_REVOKED);
999 /* Check to see if server is locked out */
1000 if (isflagset(server.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
1001 *status = "SERVICE LOCKED OUT";
1002 return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
1005 /* Check to see if server is allowed to be a service */
1006 if (isflagset(server.attributes, KRB5_KDB_DISALLOW_SVR)) {
1007 *status = "SERVICE NOT ALLOWED";
1008 return(KDC_ERR_MUST_USE_USER2USER);
1012 * Check against local policy
1014 errcode = against_local_policy_as(request, client, server,
1023 validate_forwardable(krb5_kdc_req *request, krb5_db_entry client,
1024 krb5_db_entry server, krb5_timestamp kdc_time,
1025 const char **status)
1028 if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) &&
1029 (isflagset(client.attributes, KRB5_KDB_DISALLOW_FORWARDABLE) ||
1030 isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))) {
1031 *status = "FORWARDABLE NOT ALLOWED";
1032 return(KDC_ERR_POLICY);
1037 #define ASN1_ID_CLASS (0xc0)
1038 #define ASN1_ID_TYPE (0x20)
1039 #define ASN1_ID_TAG (0x1f)
1040 #define ASN1_CLASS_UNIV (0)
1041 #define ASN1_CLASS_APP (1)
1042 #define ASN1_CLASS_CTX (2)
1043 #define ASN1_CLASS_PRIV (3)
1044 #define asn1_id_constructed(x) (x & ASN1_ID_TYPE)
1045 #define asn1_id_primitive(x) (!asn1_id_constructed(x))
1046 #define asn1_id_class(x) ((x & ASN1_ID_CLASS) >> 6)
1047 #define asn1_id_tag(x) (x & ASN1_ID_TAG)
1050 * asn1length - return encoded length of value.
1052 * passed a pointer into the asn.1 stream, which is updated
1053 * to point right after the length bits.
1055 * returns -1 on failure.
1058 asn1length(unsigned char **astream)
1060 int length; /* resulting length */
1061 int sublen; /* sublengths */
1062 int blen; /* bytes of length */
1063 unsigned char *p; /* substring searching */
1065 if (**astream & 0x80) {
1066 blen = **astream & 0x7f;
1070 for (++*astream, length = 0; blen; ++*astream, blen--) {
1071 length = (length << 8) | **astream;
1074 /* indefinite length, figure out by hand */
1078 /* compute value length. */
1079 if ((sublen = asn1length(&p)) < 0) {
1083 /* check for termination */
1084 if ((!*p++) && (!*p)) {
1089 length = p - *astream;
1099 * fetch_asn1_field - return raw asn.1 stream of subfield.
1101 * this routine is passed a context-dependent tag number and "level" and returns
1102 * the size and length of the corresponding level subfield.
1104 * levels and are numbered starting from 1.
1106 * returns 0 on success, -1 otherwise.
1109 fetch_asn1_field(unsigned char *astream, unsigned int level,
1110 unsigned int field, krb5_data *data)
1112 unsigned char *estream; /* end of stream */
1113 int classes; /* # classes seen so far this level */
1114 unsigned int levels = 0; /* levels seen so far */
1115 int lastlevel = 1000; /* last level seen */
1116 int length; /* various lengths */
1117 int tag; /* tag number */
1118 unsigned char savelen; /* saved length of our field */
1121 /* we assume that the first identifier/length will tell us
1122 how long the entire stream is. */
1125 if ((length = asn1length(&astream)) < 0) {
1129 /* search down the stream, checking identifiers. we process identifiers
1130 until we hit the "level" we want, and then process that level for our
1131 subfield, always making sure we don't go off the end of the stream. */
1132 while (astream < estream) {
1133 if (!asn1_id_constructed(*astream)) {
1136 if (asn1_id_class(*astream) == ASN1_CLASS_CTX) {
1137 if ((tag = (int)asn1_id_tag(*astream)) <= lastlevel) {
1142 if (levels == level) {
1143 /* in our context-dependent class, is this the one we're looking for ? */
1144 if (tag == (int)field) {
1145 /* return length and data */
1148 if ((data->length = asn1length(&astream)) < 0) {
1151 /* if the field length is indefinite, we will have to subtract two
1152 (terminating octets) from the length returned since we don't want
1153 to pass any info from the "wrapper" back. asn1length will always return
1154 the *total* length of the field, not just what's contained in it */
1155 if ((savelen & 0xff) == 0x80) {
1158 data->data = (char *)astream;
1160 } else if (tag <= classes) {
1161 /* we've seen this class before, something must be wrong */
1168 /* if we're not on our level yet, process this value. otherwise skip over it */
1170 if ((length = asn1length(&astream)) < 0) {
1173 if (levels == level) {
1181 * Routines that validate a TGS request; checks a lot of things. :-)
1183 * Returns a Kerberos protocol error number, which is _not_ the same
1184 * as a com_err error number!
1186 #define TGS_OPTIONS_HANDLED (KDC_OPT_FORWARDABLE | KDC_OPT_FORWARDED | \
1187 KDC_OPT_PROXIABLE | KDC_OPT_PROXY | \
1188 KDC_OPT_ALLOW_POSTDATE | KDC_OPT_POSTDATED | \
1189 KDC_OPT_RENEWABLE | KDC_OPT_RENEWABLE_OK | \
1190 KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_RENEW | \
1191 KDC_OPT_VALIDATE | KDC_OPT_CANONICALIZE | KDC_OPT_CNAME_IN_ADDL_TKT)
1192 #define NO_TGT_OPTION (KDC_OPT_FORWARDED | KDC_OPT_PROXY | KDC_OPT_RENEW | \
1196 validate_tgs_request(register krb5_kdc_req *request, krb5_db_entry server,
1197 krb5_ticket *ticket, krb5_timestamp kdc_time,
1198 const char **status)
1204 * If an illegal option is set, ignore it.
1206 request->kdc_options &= TGS_OPTIONS_HANDLED;
1208 /* Check to see if server has expired */
1209 if (server.expiration && server.expiration < kdc_time) {
1210 *status = "SERVICE EXPIRED";
1211 return(KDC_ERR_SERVICE_EXP);
1215 * Verify that the server principal in authdat->ticket is correct
1216 * (either the ticket granting service or the service that was
1217 * originally requested)
1219 if (request->kdc_options & NO_TGT_OPTION) {
1220 if (!krb5_principal_compare(kdc_context, ticket->server, request->server)) {
1221 *status = "SERVER DIDN'T MATCH TICKET FOR RENEW/FORWARD/ETC";
1222 return(KDC_ERR_SERVER_NOMATCH);
1226 * OK, we need to validate the krbtgt service in the ticket.
1228 * The krbtgt service is of the form:
1229 * krbtgt/realm-A@realm-B
1231 * Realm A is the "server realm"; the realm of the
1232 * server of the requested ticket must match this realm.
1233 * Of course, it should be a realm serviced by this KDC.
1235 * Realm B is the "client realm"; this is what should be
1236 * added to the transited field. (which is done elsewhere)
1239 /* Make sure there are two components... */
1240 if (krb5_princ_size(kdc_context, ticket->server) != 2) {
1241 *status = "BAD TGS SERVER LENGTH";
1242 return KRB_AP_ERR_NOT_US;
1244 /* ...that the first component is krbtgt... */
1245 if (!krb5_is_tgs_principal(ticket->server)) {
1246 *status = "BAD TGS SERVER NAME";
1247 return KRB_AP_ERR_NOT_US;
1249 /* ...and that the second component matches the server realm... */
1250 if ((krb5_princ_size(kdc_context, ticket->server) <= 1) ||
1251 !data_eq(*krb5_princ_component(kdc_context, ticket->server, 1),
1252 *krb5_princ_realm(kdc_context, request->server))) {
1253 *status = "BAD TGS SERVER INSTANCE";
1254 return KRB_AP_ERR_NOT_US;
1256 /* XXX add check that second component must match locally
1260 /* Server must allow TGS based issuances */
1261 if (isflagset(server.attributes, KRB5_KDB_DISALLOW_TGT_BASED)) {
1262 *status = "TGT BASED NOT ALLOWED";
1263 return(KDC_ERR_POLICY);
1267 /* TGS must be forwardable to get forwarded or forwardable ticket */
1268 if ((isflagset(request->kdc_options, KDC_OPT_FORWARDED) ||
1269 isflagset(request->kdc_options, KDC_OPT_FORWARDABLE)) &&
1270 !isflagset(ticket->enc_part2->flags, TKT_FLG_FORWARDABLE)) {
1271 *status = "TGT NOT FORWARDABLE";
1273 return KDC_ERR_BADOPTION;
1276 /* TGS must be proxiable to get proxiable ticket */
1277 if ((isflagset(request->kdc_options, KDC_OPT_PROXY) ||
1278 isflagset(request->kdc_options, KDC_OPT_PROXIABLE)) &&
1279 !isflagset(ticket->enc_part2->flags, TKT_FLG_PROXIABLE)) {
1280 *status = "TGT NOT PROXIABLE";
1281 return KDC_ERR_BADOPTION;
1284 /* TGS must allow postdating to get postdated ticket */
1285 if ((isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) ||
1286 isflagset(request->kdc_options, KDC_OPT_POSTDATED)) &&
1287 !isflagset(ticket->enc_part2->flags, TKT_FLG_MAY_POSTDATE)) {
1288 *status = "TGT NOT POSTDATABLE";
1289 return KDC_ERR_BADOPTION;
1292 /* can only validate invalid tix */
1293 if (isflagset(request->kdc_options, KDC_OPT_VALIDATE) &&
1294 !isflagset(ticket->enc_part2->flags, TKT_FLG_INVALID)) {
1295 *status = "VALIDATE VALID TICKET";
1296 return KDC_ERR_BADOPTION;
1299 /* can only renew renewable tix */
1300 if ((isflagset(request->kdc_options, KDC_OPT_RENEW) ||
1301 isflagset(request->kdc_options, KDC_OPT_RENEWABLE)) &&
1302 !isflagset(ticket->enc_part2->flags, TKT_FLG_RENEWABLE)) {
1303 *status = "TICKET NOT RENEWABLE";
1304 return KDC_ERR_BADOPTION;
1307 /* can not proxy ticket granting tickets */
1308 if (isflagset(request->kdc_options, KDC_OPT_PROXY) &&
1309 (!request->server->data ||
1310 !data_eq_string(request->server->data[0], KRB5_TGS_NAME))) {
1311 *status = "CAN'T PROXY TGT";
1312 return KDC_ERR_BADOPTION;
1315 /* Server must allow forwardable tickets */
1316 if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) &&
1317 isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE)) {
1318 *status = "NON-FORWARDABLE TICKET";
1319 return(KDC_ERR_POLICY);
1322 /* Server must allow renewable tickets */
1323 if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE) &&
1324 isflagset(server.attributes, KRB5_KDB_DISALLOW_RENEWABLE)) {
1325 *status = "NON-RENEWABLE TICKET";
1326 return(KDC_ERR_POLICY);
1329 /* Server must allow proxiable tickets */
1330 if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE) &&
1331 isflagset(server.attributes, KRB5_KDB_DISALLOW_PROXIABLE)) {
1332 *status = "NON-PROXIABLE TICKET";
1333 return(KDC_ERR_POLICY);
1336 /* Server must allow postdated tickets */
1337 if (isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) &&
1338 isflagset(server.attributes, KRB5_KDB_DISALLOW_POSTDATED)) {
1339 *status = "NON-POSTDATABLE TICKET";
1340 return(KDC_ERR_CANNOT_POSTDATE);
1343 /* Server must allow DUP SKEY requests */
1344 if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY) &&
1345 isflagset(server.attributes, KRB5_KDB_DISALLOW_DUP_SKEY)) {
1346 *status = "DUP_SKEY DISALLOWED";
1347 return(KDC_ERR_POLICY);
1350 /* Server must not be locked out */
1351 if (isflagset(server.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
1352 *status = "SERVER LOCKED OUT";
1353 return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
1356 /* Server must be allowed to be a service */
1357 if (isflagset(server.attributes, KRB5_KDB_DISALLOW_SVR)) {
1358 *status = "SERVER NOT ALLOWED";
1359 return(KDC_ERR_MUST_USE_USER2USER);
1362 /* Check the hot list */
1363 if (check_hot_list(ticket)) {
1364 *status = "HOT_LIST";
1365 return(KRB_AP_ERR_REPEAT);
1368 /* Check the start time vs. the KDC time */
1369 if (isflagset(request->kdc_options, KDC_OPT_VALIDATE)) {
1370 if (ticket->enc_part2->times.starttime > kdc_time) {
1371 *status = "NOT_YET_VALID";
1372 return(KRB_AP_ERR_TKT_NYV);
1377 * Check the renew_till time. The endtime was already
1378 * been checked in the initial authentication check.
1380 if (isflagset(request->kdc_options, KDC_OPT_RENEW) &&
1381 (ticket->enc_part2->times.renew_till < kdc_time)) {
1382 *status = "TKT_EXPIRED";
1383 return(KRB_AP_ERR_TKT_EXPIRED);
1387 * Checks for ENC_TKT_IN_SKEY:
1389 * (1) Make sure the second ticket exists
1390 * (2) Make sure it is a ticket granting ticket
1392 if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) {
1393 if (!request->second_ticket ||
1394 !request->second_ticket[st_idx]) {
1395 *status = "NO_2ND_TKT";
1396 return(KDC_ERR_BADOPTION);
1398 if (!krb5_principal_compare(kdc_context, request->second_ticket[st_idx]->server,
1400 *status = "2ND_TKT_NOT_TGS";
1401 return(KDC_ERR_POLICY);
1405 if (isflagset(request->kdc_options, KDC_OPT_CNAME_IN_ADDL_TKT)) {
1406 if (!request->second_ticket ||
1407 !request->second_ticket[st_idx]) {
1408 *status = "NO_2ND_TKT";
1409 return(KDC_ERR_BADOPTION);
1414 /* Check for hardware preauthentication */
1415 if (isflagset(server.attributes, KRB5_KDB_REQUIRES_HW_AUTH) &&
1416 !isflagset(ticket->enc_part2->flags,TKT_FLG_HW_AUTH)) {
1417 *status = "NO HW PREAUTH";
1418 return KRB_ERR_GENERIC;
1421 /* Check for any kind of preauthentication */
1422 if (isflagset(server.attributes, KRB5_KDB_REQUIRES_PRE_AUTH) &&
1423 !isflagset(ticket->enc_part2->flags, TKT_FLG_PRE_AUTH)) {
1424 *status = "NO PREAUTH";
1425 return KRB_ERR_GENERIC;
1429 * Check local policy
1431 errcode = against_local_policy_tgs(request, server, ticket, status);
1440 * This function returns 1 if the dbentry has a key for a specified
1441 * keytype, and 0 if not.
1444 dbentry_has_key_for_enctype(krb5_context context, krb5_db_entry *client,
1445 krb5_enctype enctype)
1447 krb5_error_code retval;
1448 krb5_key_data *datap;
1450 retval = krb5_dbe_find_enctype(context, client, enctype,
1459 * This function returns 1 if the entity referenced by this
1460 * structure can support the a particular encryption system, and 0 if
1463 * XXX eventually this information should be looked up in the
1464 * database. Since it isn't, we use some hueristics and attribute
1465 * options bits for now.
1468 dbentry_supports_enctype(krb5_context context, krb5_db_entry *client,
1469 krb5_enctype enctype)
1472 * If it's DES_CBC_MD5, there's a bit in the attribute mask which
1473 * checks to see if we support it. For now, treat it as always
1476 * In theory everything's supposed to support DES_CBC_MD5, but
1477 * that's not the reality....
1479 if (enctype == ENCTYPE_DES_CBC_MD5)
1483 * XXX we assume everything can understand DES_CBC_CRC
1485 if (enctype == ENCTYPE_DES_CBC_CRC)
1489 * If we have a key for the encryption system, we assume it's
1492 return dbentry_has_key_for_enctype(context, client, enctype);
1496 * This function returns the keytype which should be selected for the
1497 * session key. It is based on the ordered list which the user
1498 * requested, and what the KDC and the application server can support.
1501 select_session_keytype(krb5_context context, krb5_db_entry *server,
1502 int nktypes, krb5_enctype *ktype)
1506 for (i = 0; i < nktypes; i++) {
1507 if (!krb5_c_valid_enctype(ktype[i]))
1510 if (!krb5_is_permitted_enctype(context, ktype[i]))
1513 if (dbentry_supports_enctype(context, server, ktype[i]))
1520 * This function returns salt information for a particular client_key
1523 get_salt_from_key(krb5_context context, krb5_principal client,
1524 krb5_key_data *client_key, krb5_data *salt)
1526 krb5_error_code retval;
1530 salt->length = SALT_TYPE_NO_LENGTH;
1532 if (client_key->key_data_ver == 1)
1535 switch (client_key->key_data_type[1]) {
1536 case KRB5_KDB_SALTTYPE_NORMAL:
1538 case KRB5_KDB_SALTTYPE_V4:
1539 /* send an empty (V4) salt */
1543 case KRB5_KDB_SALTTYPE_NOREALM:
1544 if ((retval = krb5_principal2salt_norealm(context, client, salt)))
1547 case KRB5_KDB_SALTTYPE_AFS3:
1548 /* send the same salt as with onlyrealm - but with no type info,
1549 we just hope they figure it out on the other end. */
1550 /* fall through to onlyrealm: */
1551 case KRB5_KDB_SALTTYPE_ONLYREALM:
1552 realm = krb5_princ_realm(context, client);
1553 salt->length = realm->length;
1554 if ((salt->data = malloc(realm->length)) == NULL)
1556 memcpy(salt->data, realm->data, realm->length);
1558 case KRB5_KDB_SALTTYPE_SPECIAL:
1559 salt->length = client_key->key_data_length[1];
1560 if ((salt->data = malloc(salt->length)) == NULL)
1562 memcpy(salt->data, client_key->key_data_contents[1], salt->length);
1569 * Limit strings to a "reasonable" length to prevent crowding out of
1570 * other useful information in the log entry
1572 #define NAME_LENGTH_LIMIT 128
1574 void limit_string(char *name)
1581 if (strlen(name) < NAME_LENGTH_LIMIT)
1584 i = NAME_LENGTH_LIMIT-4;
1593 * L10_2 = log10(2**x), rounded up; log10(2) ~= 0.301.
1595 #define L10_2(x) ((int)(((x * 301) + 999) / 1000))
1598 * Max length of sprintf("%ld") for an int of type T; includes leading
1599 * minus sign and terminating NUL.
1601 #define D_LEN(t) (L10_2(sizeof(t) * CHAR_BIT) + 2)
1604 ktypes2str(char *s, size_t len, int nktypes, krb5_enctype *ktype)
1607 char stmp[D_LEN(krb5_enctype) + 1];
1611 || len < (sizeof(" etypes {...}") + D_LEN(int))) {
1616 snprintf(s, len, "%d etypes {", nktypes);
1617 for (i = 0; i < nktypes; i++) {
1618 snprintf(stmp, sizeof(stmp), "%s%ld", i ? " " : "", (long)ktype[i]);
1619 if (strlen(s) + strlen(stmp) + sizeof("}") > len)
1621 strlcat(s, stmp, len);
1625 * We broke out of the loop. Try to truncate the list.
1628 while (p - s + sizeof("...}") > len) {
1629 while (p > s && *p != ' ' && *p != '{')
1631 if (p > s && *p == ' ') {
1636 strlcat(s, "...", len);
1638 strlcat(s, "}", len);
1643 rep_etypes2str(char *s, size_t len, krb5_kdc_rep *rep)
1645 char stmp[sizeof("ses=") + D_LEN(krb5_enctype)];
1647 if (len < (3 * D_LEN(krb5_enctype)
1648 + sizeof("etypes {rep= tkt= ses=}"))) {
1653 snprintf(s, len, "etypes {rep=%ld", (long)rep->enc_part.enctype);
1655 if (rep->ticket != NULL) {
1656 snprintf(stmp, sizeof(stmp),
1657 " tkt=%ld", (long)rep->ticket->enc_part.enctype);
1658 strlcat(s, stmp, len);
1661 if (rep->ticket != NULL
1662 && rep->ticket->enc_part2 != NULL
1663 && rep->ticket->enc_part2->session != NULL) {
1664 snprintf(stmp, sizeof(stmp), " ses=%ld",
1665 (long)rep->ticket->enc_part2->session->enctype);
1666 strlcat(s, stmp, len);
1668 strlcat(s, "}", len);
1673 get_principal_locked (krb5_context kcontext,
1674 krb5_const_principal search_for,
1675 krb5_db_entry *entries, int *nentries,
1678 return krb5_db_get_principal (kcontext, search_for, entries, nentries,
1683 get_principal (krb5_context kcontext,
1684 krb5_const_principal search_for,
1685 krb5_db_entry *entries, int *nentries, krb5_boolean *more)
1687 /* Eventually this will be used to manage locking while looking up
1688 principals in the database. */
1689 return get_principal_locked (kcontext, search_for, entries, nentries,
1695 sign_db_authdata (krb5_context context,
1697 krb5_const_principal client_princ,
1698 krb5_db_entry *client,
1699 krb5_db_entry *server,
1700 krb5_db_entry *krbtgt,
1701 krb5_keyblock *client_key,
1702 krb5_keyblock *server_key,
1703 krb5_timestamp authtime,
1704 krb5_authdata **tgs_authdata,
1705 krb5_authdata ***ret_authdata,
1706 krb5_db_entry *ad_entry,
1709 krb5_error_code code;
1710 kdb_sign_auth_data_req req;
1711 kdb_sign_auth_data_rep rep;
1715 *ret_authdata = NULL;
1716 memset(ad_entry, 0, sizeof(*ad_entry));
1719 memset(&req, 0, sizeof(req));
1720 memset(&rep, 0, sizeof(rep));
1723 req.client_princ = client_princ;
1724 req.client = client;
1725 req.server = server;
1726 req.krbtgt = krbtgt;
1727 req.client_key = client_key;
1728 req.server_key = server_key;
1729 req.authtime = authtime;
1730 req.auth_data = tgs_authdata;
1732 rep.entry = ad_entry;
1735 req_data.data = (void *)&req;
1736 req_data.length = sizeof(req);
1738 rep_data.data = (void *)&rep;
1739 rep_data.length = sizeof(rep);
1741 code = krb5_db_invoke(context,
1742 KRB5_KDB_METHOD_SIGN_AUTH_DATA,
1746 *ret_authdata = rep.auth_data;
1747 *ad_nprincs = rep.nprincs;
1752 static krb5_error_code
1753 verify_s4u2self_checksum(krb5_context context,
1755 krb5_pa_for_user *req)
1757 krb5_error_code code;
1759 krb5_int32 name_type;
1762 krb5_boolean valid = FALSE;
1764 if (!krb5_c_is_keyed_cksum(req->cksum.checksum_type)) {
1765 return KRB5KRB_AP_ERR_INAPP_CKSUM;
1769 * Checksum is over name type and string components of
1770 * client principal name and auth_package.
1773 for (i = 0; i < krb5_princ_size(context, req->user); i++) {
1774 data.length += krb5_princ_component(context, req->user, i)->length;
1776 data.length += krb5_princ_realm(context, req->user)->length;
1777 data.length += req->auth_package.length;
1779 p = data.data = malloc(data.length);
1780 if (data.data == NULL) {
1784 name_type = krb5_princ_type(context, req->user);
1785 p[0] = (name_type >> 0 ) & 0xFF;
1786 p[1] = (name_type >> 8 ) & 0xFF;
1787 p[2] = (name_type >> 16) & 0xFF;
1788 p[3] = (name_type >> 24) & 0xFF;
1791 for (i = 0; i < krb5_princ_size(context, req->user); i++) {
1792 memcpy(p, krb5_princ_component(context, req->user, i)->data,
1793 krb5_princ_component(context, req->user, i)->length);
1794 p += krb5_princ_component(context, req->user, i)->length;
1797 memcpy(p, krb5_princ_realm(context, req->user)->data,
1798 krb5_princ_realm(context, req->user)->length);
1799 p += krb5_princ_realm(context, req->user)->length;
1801 memcpy(p, req->auth_package.data, req->auth_package.length);
1802 p += req->auth_package.length;
1804 code = krb5_c_verify_checksum(context,
1806 KRB5_KEYUSAGE_APP_DATA_CKSUM,
1811 if (code == 0 && valid == FALSE)
1812 code = KRB5KRB_AP_ERR_BAD_INTEGRITY;
1820 * Protocol transition validation code based on AS-REQ
1824 validate_s4u2self_request(krb5_kdc_req *request,
1825 const krb5_db_entry *client,
1826 krb5_timestamp kdc_time,
1827 const char **status)
1830 krb5_db_entry server = { 0 };
1832 /* The client's password must not be expired, unless the server is
1833 a KRB5_KDC_PWCHANGE_SERVICE. */
1834 if (client->pw_expiration && client->pw_expiration < kdc_time) {
1835 *status = "CLIENT KEY EXPIRED";
1836 return KDC_ERR_KEY_EXP;
1839 /* The client must not be expired */
1840 if (client->expiration && client->expiration < kdc_time) {
1841 *status = "CLIENT EXPIRED";
1842 return KDC_ERR_NAME_EXP;
1846 * If the client requires password changing, then return an
1847 * error; S4U2Self cannot be used to change a password.
1849 if (isflagset(client->attributes, KRB5_KDB_REQUIRES_PWCHANGE)) {
1850 *status = "REQUIRED PWCHANGE";
1851 return KDC_ERR_KEY_EXP;
1854 /* Check to see if client is locked out */
1855 if (isflagset(client->attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
1856 *status = "CLIENT LOCKED OUT";
1857 return KDC_ERR_C_PRINCIPAL_UNKNOWN;
1861 * Check against local policy
1863 errcode = against_local_policy_as(request, *client, server,
1872 * Protocol transition (S4U2Self)
1875 kdc_process_s4u2self_req(krb5_context context,
1876 krb5_kdc_req *request,
1877 krb5_const_principal client_princ,
1878 const krb5_db_entry *server,
1879 krb5_keyblock *subkey,
1880 krb5_timestamp kdc_time,
1881 krb5_pa_for_user **for_user,
1882 krb5_db_entry *princ,
1884 const char **status)
1886 krb5_error_code code;
1887 krb5_pa_data **pa_data;
1892 memset(princ, 0, sizeof(*princ));
1894 if (request->padata == NULL) {
1898 for (pa_data = request->padata; *pa_data != NULL; pa_data++) {
1899 if ((*pa_data)->pa_type == KRB5_PADATA_FOR_USER)
1902 if (*pa_data == NULL) {
1908 * Ignore request if the server principal is a TGS, not so much
1909 * to avoid unconstrained tickets being issued (as that would
1910 * require knowing the TGS key anyway) but so that we do not
1911 * block the server referral path.
1913 if (krb5_is_tgs_principal(server->princ)) {
1918 *status = "PROCESS_S4U2SELF_REQUEST";
1920 req_data.length = (*pa_data)->length;
1921 req_data.data = (char *)(*pa_data)->contents;
1923 code = decode_krb5_pa_for_user(&req_data, for_user);
1928 if (krb5_princ_type(context, (*for_user)->user) !=
1929 KRB5_NT_ENTERPRISE_PRINCIPAL) {
1930 *status = "INVALID_S4U2SELF_REQUEST";
1931 return KRB5KDC_ERR_POLICY;
1934 code = verify_s4u2self_checksum(context, subkey, *for_user);
1936 *status = "INVALID_S4U2SELF_CHECKSUM";
1937 krb5_free_pa_for_user(kdc_context, *for_user);
1941 if (!krb5_principal_compare_flags(context, request->server, client_princ,
1942 KRB5_PRINCIPAL_COMPARE_ENTERPRISE)) {
1943 *status = "INVALID_S4U2SELF_REQUEST";
1944 return KRB5KDC_ERR_POLICY;
1948 * Protocol transition is mutually exclusive with renew/forward/etc
1949 * as well as user-to-user and constrained delegation.
1951 * We can assert from this check that the header ticket was a TGT, as
1952 * that is validated previously in validate_tgs_request().
1954 if (request->kdc_options & (NO_TGT_OPTION | KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_CNAME_IN_ADDL_TKT)) {
1955 return KRB5KDC_ERR_BADOPTION;
1959 * Do not attempt to lookup principals in foreign realms.
1961 if (is_local_principal((*for_user)->user)) {
1963 code = krb5_db_get_principal_ext(kdc_context,
1965 KRB5_KDB_FLAG_INCLUDE_PAC,
1966 princ, nprincs, &more);
1968 *status = "LOOKING_UP_S4U2SELF_PRINCIPAL";
1970 return code; /* caller can free for_user */
1974 *status = "NON_UNIQUE_S4U2SELF_PRINCIPAL";
1975 return KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE;
1976 } else if (*nprincs != 1) {
1977 *status = "UNKNOWN_S4U2SELF_PRINCIPAL";
1978 return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
1981 code = validate_s4u2self_request(request, princ, kdc_time, status);
1992 static krb5_error_code
1993 check_allowed_to_delegate_to(krb5_context context,
1994 const krb5_db_entry *server,
1995 krb5_const_principal proxy)
1997 kdb_check_allowed_to_delegate_req req;
2000 krb5_error_code code;
2002 /* Can't get a TGT (otherwise it would be unconstrained delegation) */
2003 if (krb5_is_tgs_principal(proxy)) {
2004 return KRB5KDC_ERR_POLICY;
2007 /* Must be in same realm */
2008 if (!krb5_realm_compare(context, server->princ, proxy)) {
2009 return KRB5KDC_ERR_BADOPTION;
2012 req.server = server;
2015 req_data.data = (void *)&req;
2016 req_data.length = sizeof(req);
2018 rep_data.data = NULL;
2019 rep_data.length = 0;
2021 code = krb5_db_invoke(context,
2022 KRB5_KDB_METHOD_CHECK_ALLOWED_TO_DELEGATE,
2025 if (code == KRB5_KDB_DBTYPE_NOSUP) {
2026 code = KRB5KDC_ERR_POLICY;
2029 assert(rep_data.length == 0);
2035 kdc_process_s4u2proxy_req(krb5_context context,
2036 krb5_kdc_req *request,
2037 const krb5_enc_tkt_part *t2enc,
2038 const krb5_db_entry *server,
2039 krb5_const_principal server_princ,
2040 krb5_const_principal proxy_princ,
2041 const char **status)
2043 krb5_error_code errcode;
2046 * Constrained delegation is mutually exclusive with renew/forward/etc.
2047 * We can assert from this check that the header ticket was a TGT, as
2048 * that is validated previously in validate_tgs_request().
2050 if (request->kdc_options & (NO_TGT_OPTION | KDC_OPT_ENC_TKT_IN_SKEY)) {
2051 return KRB5KDC_ERR_BADOPTION;
2054 /* Ensure that evidence ticket server matches TGT client */
2055 if (!krb5_principal_compare(kdc_context,
2056 server->princ, /* after canon */
2058 return KRB5KDC_ERR_SERVER_NOMATCH;
2061 if (!isflagset(t2enc->flags, TKT_FLG_FORWARDABLE)) {
2062 *status = "EVIDENCE_TKT_NOT_FORWARDABLE";
2063 return KRB5_TKT_NOT_FORWARDABLE;
2066 /* Backend policy check */
2067 errcode = check_allowed_to_delegate_to(kdc_context,
2068 server, proxy_princ);
2070 *status = "NOT_ALLOWED_TO_DELEGATE";
2078 kdc_check_transited_list(krb5_context context,
2079 const krb5_data *trans,
2080 const krb5_data *realm1,
2081 const krb5_data *realm2)
2083 krb5_error_code code;
2084 kdb_check_transited_realms_req req;
2088 /* First check using krb5.conf */
2089 code = krb5_check_transited_list(kdc_context, trans, realm1, realm2);
2093 memset(&req, 0, sizeof(req));
2095 req.tr_contents = trans;
2096 req.client_realm = realm1;
2097 req.server_realm = realm2;
2099 req_data.data = (void *)&req;
2100 req_data.length = sizeof(req);
2102 rep_data.data = NULL;
2103 rep_data.length = 0;
2105 code = krb5_db_invoke(context,
2106 KRB5_KDB_METHOD_CHECK_TRANSITED_REALMS,
2109 if (code == KRB5_KDB_DBTYPE_NOSUP) {
2113 assert(rep_data.length == 0);
2119 audit_as_request(krb5_kdc_req *request,
2120 krb5_db_entry *client,
2121 krb5_db_entry *server,
2122 krb5_timestamp authtime,
2123 krb5_error_code errcode)
2125 krb5_error_code code;
2126 kdb_audit_as_req req;
2130 memset(&req, 0, sizeof(req));
2132 req.request = request;
2133 req.client = client;
2134 req.server = server;
2135 req.authtime = authtime;
2136 req.error_code = errcode;
2138 req_data.data = (void *)&req;
2139 req_data.length = sizeof(req);
2141 rep_data.data = NULL;
2142 rep_data.length = 0;
2144 code = krb5_db_invoke(kdc_context,
2145 KRB5_KDB_METHOD_AUDIT_AS,
2148 if (code == KRB5_KDB_DBTYPE_NOSUP) {
2152 assert(rep_data.length == 0);
2158 audit_tgs_request(krb5_kdc_req *request,
2159 krb5_const_principal client,
2160 krb5_db_entry *server,
2161 krb5_timestamp authtime,
2162 krb5_error_code errcode)
2164 krb5_error_code code;
2165 kdb_audit_tgs_req req;
2169 memset(&req, 0, sizeof(req));
2171 req.request = request;
2172 req.client = client;
2173 req.server = server;
2174 req.authtime = authtime;
2175 req.error_code = errcode;
2177 req_data.data = (void *)&req;
2178 req_data.length = sizeof(req);
2180 rep_data.data = NULL;
2181 rep_data.length = 0;
2183 code = krb5_db_invoke(kdc_context,
2184 KRB5_KDB_METHOD_AUDIT_TGS,
2187 if (code == KRB5_KDB_DBTYPE_NOSUP) {
2191 assert(rep_data.length == 0);
2197 validate_transit_path(krb5_context context,
2198 krb5_const_principal client,
2199 krb5_db_entry *server,
2200 krb5_db_entry *krbtgt)
2203 if (isflagset(server->attributes, KRB5_KDB_XREALM_NON_TRANSITIVE)) {
2204 return KRB5KDC_ERR_PATH_NOT_ACCEPTED;
2208 if (isflagset(krbtgt->attributes, KRB5_KDB_XREALM_NON_TRANSITIVE) &&
2209 (!krb5_principal_compare(context, server->princ, krbtgt->princ) ||
2210 !krb5_realm_compare(context, client, krbtgt->princ))) {
2211 return KRB5KDC_ERR_PATH_NOT_ACCEPTED;
2218 /* Main logging routines for ticket requests.
2220 There are a few simple cases -- unparseable requests mainly --
2221 where messages are logged otherwise, but once a ticket request can
2222 be decoded in some basic way, these routines are used for logging
2225 /* "status" is null to indicate success. */
2226 /* Someday, pass local address/port as well. */
2228 log_as_req(const krb5_fulladdr *from,
2229 krb5_kdc_req *request, krb5_kdc_rep *reply,
2230 const char *cname, const char *sname,
2231 krb5_timestamp authtime,
2232 const char *status, krb5_error_code errcode, const char *emsg)
2234 const char *fromstring = 0;
2235 char fromstringbuf[70];
2237 const char *cname2 = cname ? cname : "<unknown client>";
2238 const char *sname2 = sname ? sname : "<unknown server>";
2240 fromstring = inet_ntop(ADDRTYPE2FAMILY (from->address->addrtype),
2241 from->address->contents,
2242 fromstringbuf, sizeof(fromstringbuf));
2244 fromstring = "<unknown>";
2245 ktypes2str(ktypestr, sizeof(ktypestr),
2246 request->nktypes, request->ktype);
2248 if (status == NULL) {
2250 char rep_etypestr[128];
2251 rep_etypes2str(rep_etypestr, sizeof(rep_etypestr), reply);
2252 krb5_klog_syslog(LOG_INFO,
2253 "AS_REQ (%s) %s: ISSUE: authtime %d, %s, %s for %s",
2254 ktypestr, fromstring, authtime,
2255 rep_etypestr, cname2, sname2);
2258 krb5_klog_syslog(LOG_INFO, "AS_REQ (%s) %s: %s: %s for %s%s%s",
2259 ktypestr, fromstring, status,
2260 cname2, sname2, emsg ? ", " : "", emsg ? emsg : "");
2263 /* Sun (OpenSolaris) version would probably something like this.
2264 The client and server names passed can be null, unlike in the
2265 logging routines used above. Note that a struct in_addr is
2266 used, but the real address could be an IPv6 address. */
2267 audit_krb5kdc_as_req(some in_addr *, (in_port_t)from->port, 0,
2268 cname, sname, errcode);
2272 /* Here "status" must be non-null. Error code
2273 KRB5KDC_ERR_SERVER_NOMATCH is handled specially. */
2275 log_tgs_req(const krb5_fulladdr *from,
2276 krb5_kdc_req *request, krb5_kdc_rep *reply,
2277 const char *cname, const char *sname, const char *altcname,
2278 krb5_timestamp authtime,
2279 const char *status, krb5_error_code errcode, const char *emsg)
2282 const char *fromstring = 0;
2283 char fromstringbuf[70];
2284 char rep_etypestr[128];
2286 fromstring = inet_ntop(ADDRTYPE2FAMILY(from->address->addrtype),
2287 from->address->contents,
2288 fromstringbuf, sizeof(fromstringbuf));
2290 fromstring = "<unknown>";
2291 ktypes2str(ktypestr, sizeof(ktypestr), request->nktypes, request->ktype);
2293 rep_etypes2str(rep_etypestr, sizeof(rep_etypestr), reply);
2295 rep_etypestr[0] = 0;
2297 /* Differences: server-nomatch message logs 2nd ticket's client
2298 name (useful), and doesn't log ktypestr (probably not
2300 if (errcode != KRB5KDC_ERR_SERVER_NOMATCH)
2301 krb5_klog_syslog(LOG_INFO,
2302 "TGS_REQ (%s) %s: %s: authtime %d, %s%s %s for %s%s%s",
2304 fromstring, status, authtime,
2306 !errcode ? "," : "",
2307 cname ? cname : "<unknown client>",
2308 sname ? sname : "<unknown server>",
2309 errcode ? ", " : "",
2310 errcode ? emsg : "");
2312 krb5_klog_syslog(LOG_INFO,
2313 "TGS_REQ %s: %s: authtime %d, %s for %s, 2nd tkt client %s",
2314 fromstring, status, authtime,
2315 cname ? cname : "<unknown client>",
2316 sname ? sname : "<unknown server>",
2317 altcname ? altcname : "<unknown>");
2319 /* OpenSolaris: audit_krb5kdc_tgs_req(...) or
2320 audit_krb5kdc_tgs_req_2ndtktmm(...) */
2324 log_tgs_alt_tgt(krb5_principal p)
2327 if (krb5_unparse_name(kdc_context, p, &sname)) {
2328 krb5_klog_syslog(LOG_INFO,
2329 "TGS_REQ: issuing alternate <un-unparseable> TGT");
2331 limit_string(sname);
2332 krb5_klog_syslog(LOG_INFO, "TGS_REQ: issuing TGT %s", sname);
2335 /* OpenSolaris: audit_krb5kdc_tgs_req_alt_tgt(...) */