4 * Copyright 1990,1991,2007,2008,2009 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() */
72 #ifdef KRBCONF_VAGUE_ERRORS
73 const int vague_errors = 1;
75 const int vague_errors = 0;
80 * initialize the replay cache.
83 kdc_initialize_rcache(krb5_context kcontext, char *rcache_name)
85 krb5_error_code retval;
89 rcname = (rcache_name) ? rcache_name : kdc_current_rcname;
91 /* rc_lifetime used elsewhere to verify we're not */
92 /* replaying really old data */
93 rc_lifetime = kcontext->clockskew;
97 if (!(retval = krb5_rc_resolve_full(kcontext, &kdc_rcache, rcname))) {
98 /* Recover or initialize the replay cache */
99 if (!(retval = krb5_rc_recover(kcontext, kdc_rcache)) ||
100 !(retval = krb5_rc_initialize(kcontext,
102 kcontext->clockskew))
104 /* Expunge the replay cache */
105 if (!(retval = krb5_rc_expunge(kcontext, kdc_rcache))) {
106 sname = kdc_current_rcname;
107 kdc_current_rcname = strdup(rcname);
113 krb5_rc_close(kcontext, kdc_rcache);
120 * concatenate first two authdata arrays, returning an allocated replacement.
121 * The replacement should be freed with krb5_free_authdata().
124 concat_authorization_data(krb5_authdata **first, krb5_authdata **second,
125 krb5_authdata ***output)
128 register krb5_authdata **ptr, **retdata;
130 /* count up the entries */
133 for (ptr = first; *ptr; ptr++)
136 for (ptr = second; *ptr; ptr++)
139 retdata = (krb5_authdata **)malloc((i+1)*sizeof(*retdata));
142 retdata[i] = 0; /* null-terminated array */
143 for (i = 0, j = 0, ptr = first; j < 2 ; ptr = second, j++)
144 while (ptr && *ptr) {
145 /* now walk & copy */
146 retdata[i] = (krb5_authdata *)malloc(sizeof(*retdata[i]));
148 krb5_free_authdata(kdc_context, retdata);
152 if (!(retdata[i]->contents =
153 (krb5_octet *)malloc(retdata[i]->length))) {
156 krb5_free_authdata(kdc_context, retdata);
159 memcpy(retdata[i]->contents, (*ptr)->contents, retdata[i]->length);
169 realm_compare(krb5_const_principal princ1, krb5_const_principal princ2)
171 return krb5_realm_compare(kdc_context, princ1, princ2);
175 is_local_principal(krb5_const_principal princ1)
177 return krb5_realm_compare(kdc_context, princ1, tgs_server);
181 * Returns TRUE if the kerberos principal is the name of a Kerberos ticket
184 krb5_boolean krb5_is_tgs_principal(krb5_const_principal principal)
186 if ((krb5_princ_size(kdc_context, principal) > 0) &&
187 data_eq_string (*krb5_princ_component(kdc_context, principal, 0),
194 * given authentication data (provides seed for checksum), verify checksum
197 static krb5_error_code
198 comp_cksum(krb5_context kcontext, krb5_data *source, krb5_ticket *ticket,
199 krb5_checksum *his_cksum)
201 krb5_error_code retval;
204 if (!krb5_c_valid_cksumtype(his_cksum->checksum_type))
205 return KRB5KDC_ERR_SUMTYPE_NOSUPP;
207 /* must be collision proof */
208 if (!krb5_c_is_coll_proof_cksum(his_cksum->checksum_type))
209 return KRB5KRB_AP_ERR_INAPP_CKSUM;
211 /* verify checksum */
212 if ((retval = krb5_c_verify_checksum(kcontext, ticket->enc_part2->session,
213 KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
214 source, his_cksum, &valid)))
218 return(KRB5KRB_AP_ERR_BAD_INTEGRITY);
224 find_pa_data(krb5_pa_data **padata, krb5_preauthtype pa_type)
226 return krb5int_find_pa_data(kdc_context, padata, pa_type);
230 kdc_process_tgs_req(krb5_kdc_req *request, const krb5_fulladdr *from,
231 krb5_data *pkt, krb5_ticket **ticket,
232 krb5_db_entry *krbtgt, int *nprincs,
233 krb5_keyblock **subkey,
234 krb5_pa_data **pa_tgs_req)
236 krb5_pa_data * tmppa;
238 krb5_error_code retval;
239 krb5_authdata **authdata = NULL;
241 krb5_data * scratch = NULL;
242 krb5_boolean foreign_server = FALSE;
243 krb5_auth_context auth_context = NULL;
244 krb5_authenticator * authenticator = NULL;
245 krb5_checksum * his_cksum = NULL;
246 krb5_keyblock * key = NULL;
251 tmppa = find_pa_data(request->padata, KRB5_PADATA_AP_REQ);
253 return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
255 scratch1.length = tmppa->length;
256 scratch1.data = (char *)tmppa->contents;
257 if ((retval = decode_krb5_ap_req(&scratch1, &apreq)))
260 if (isflagset(apreq->ap_options, AP_OPTS_USE_SESSION_KEY) ||
261 isflagset(apreq->ap_options, AP_OPTS_MUTUAL_REQUIRED)) {
262 krb5_klog_syslog(LOG_INFO, "TGS_REQ: SESSION KEY or MUTUAL");
263 retval = KRB5KDC_ERR_POLICY;
267 /* If the "server" principal in the ticket is not something
268 in the local realm, then we must refuse to service the request
269 if the client claims to be from the local realm.
271 If we don't do this, then some other realm's nasty KDC can
272 claim to be authenticating a client from our realm, and we'll
273 give out tickets concurring with it!
275 we set a flag here for checking below.
277 foreign_server = !is_local_principal(apreq->ticket->server);
279 if ((retval = krb5_auth_con_init(kdc_context, &auth_context)))
282 if ((retval = krb5_auth_con_setaddrs(kdc_context, auth_context, NULL,
284 goto cleanup_auth_context;
286 if ((retval = krb5_auth_con_setrcache(kdc_context, auth_context,
288 goto cleanup_auth_context;
291 if ((retval = kdc_get_server_key(apreq->ticket, 0, foreign_server,
292 krbtgt, nprincs, &key, &kvno)))
293 goto cleanup_auth_context;
295 * We do not use the KDB keytab because other parts of the TGS need the TGT key.
297 retval = krb5_auth_con_setuseruserkey(kdc_context, auth_context, key);
298 krb5_free_keyblock(kdc_context, key);
300 goto cleanup_auth_context;
302 if ((retval = krb5_rd_req_decoded_anyflag(kdc_context, &auth_context, apreq,
303 apreq->ticket->server,
304 kdc_active_realm->realm_keytab,
308 * I'm not so sure that this is right, but it's better than nothing
311 * If we choke in the rd_req because of the replay cache, then attempt
312 * to reinitialize the replay cache because somebody could have deleted
313 * it from underneath us (e.g. a cron job)
315 if ((retval == KRB5_RC_IO_IO) ||
316 (retval == KRB5_RC_IO_UNKNOWN)) {
317 (void) krb5_rc_close(kdc_context, kdc_rcache);
318 kdc_rcache = (krb5_rcache) NULL;
319 if (!(retval = kdc_initialize_rcache(kdc_context, (char *) NULL))) {
320 if ((retval = krb5_auth_con_setrcache(kdc_context, auth_context,
322 (retval = krb5_rd_req_decoded_anyflag(kdc_context, &auth_context,
323 apreq, apreq->ticket->server,
324 kdc_active_realm->realm_keytab,
327 goto cleanup_auth_context;
330 goto cleanup_auth_context;
332 goto cleanup_auth_context;
336 /* "invalid flag" tickets can must be used to validate */
337 if (isflagset((*ticket)->enc_part2->flags, TKT_FLG_INVALID)
338 && !isflagset(request->kdc_options, KDC_OPT_VALIDATE)) {
339 retval = KRB5KRB_AP_ERR_TKT_INVALID;
340 goto cleanup_auth_context;
343 if ((retval = krb5_auth_con_getrecvsubkey(kdc_context,
344 auth_context, subkey)))
345 goto cleanup_auth_context;
347 if ((retval = krb5_auth_con_getauthenticator(kdc_context, auth_context,
349 goto cleanup_auth_context;
351 retval = krb5int_find_authdata(kdc_context,
352 (*ticket)->enc_part2->authorization_data,
353 authenticator->authorization_data,
354 KRB5_AUTHDATA_FX_ARMOR, &authdata);
356 goto cleanup_authenticator;
357 if (authdata&& authdata[0]) {
358 krb5_set_error_message(kdc_context, KRB5KDC_ERR_POLICY,
359 "ticket valid only as FAST armor");
360 retval = KRB5KDC_ERR_POLICY;
361 krb5_free_authdata(kdc_context, authdata);
362 goto cleanup_authenticator;
364 krb5_free_authdata(kdc_context, authdata);
367 /* Check for a checksum */
368 if (!(his_cksum = authenticator->checksum)) {
369 retval = KRB5KRB_AP_ERR_INAPP_CKSUM;
370 goto cleanup_authenticator;
373 /* make sure the client is of proper lineage (see above) */
374 if (foreign_server &&
375 !find_pa_data(request->padata, KRB5_PADATA_FOR_USER)) {
376 if (is_local_principal((*ticket)->enc_part2->client)) {
377 /* someone in a foreign realm claiming to be local */
378 krb5_klog_syslog(LOG_INFO, "PROCESS_TGS: failed lineage check");
379 retval = KRB5KDC_ERR_POLICY;
380 goto cleanup_authenticator;
385 * Check application checksum vs. tgs request
387 * We try checksumming the req-body two different ways: first we
388 * try reaching into the raw asn.1 stream (if available), and
389 * checksum that directly; if that fails, then we try encoding
390 * using our local asn.1 library.
392 if (pkt && (fetch_asn1_field((unsigned char *) pkt->data,
393 1, 4, &scratch1) >= 0)) {
394 if (comp_cksum(kdc_context, &scratch1, *ticket, his_cksum)) {
395 if (!(retval = encode_krb5_kdc_req_body(request, &scratch)))
396 retval = comp_cksum(kdc_context, scratch, *ticket, his_cksum);
397 krb5_free_data(kdc_context, scratch);
403 cleanup_authenticator:
404 krb5_free_authenticator(kdc_context, authenticator);
406 cleanup_auth_context:
407 /* We do not want the free of the auth_context to close the rcache */
409 (void) krb5_auth_con_setrcache(kdc_context, auth_context, 0);
411 krb5_auth_con_free(kdc_context, auth_context);
414 krb5_free_ap_req(kdc_context, apreq);
418 /* XXX This function should no longer be necessary.
419 * The KDC should take the keytab associated with the realm and pass that to
420 * the krb5_rd_req_decode(). --proven
422 * It's actually still used by do_tgs_req() for u2u auth, and not too
426 kdc_get_server_key(krb5_ticket *ticket, unsigned int flags,
427 krb5_boolean match_enctype, krb5_db_entry *server,
428 int *nprincs, krb5_keyblock **key, krb5_kvno *kvno)
430 krb5_error_code retval;
431 krb5_boolean more, similar;
432 krb5_key_data * server_key;
433 krb5_keyblock * mkey_ptr;
437 retval = krb5_db_get_principal_ext(kdc_context,
447 return(KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE);
448 } else if (*nprincs != 1) {
451 if (!krb5_unparse_name(kdc_context, ticket->server, &sname)) {
453 krb5_klog_syslog(LOG_ERR,"TGS_REQ: UNKNOWN SERVER: server='%s'",
457 return(KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN);
459 if (server->attributes & KRB5_KDB_DISALLOW_SVR ||
460 server->attributes & KRB5_KDB_DISALLOW_ALL_TIX) {
461 retval = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
465 if ((retval = krb5_dbe_find_mkey(kdc_context, master_keylist, server,
467 krb5_keylist_node *tmp_mkey_list;
468 /* try refreshing master key list */
469 /* XXX it would nice if we had the mkvno here for optimization */
470 if (krb5_db_fetch_mkey_list(kdc_context, master_princ,
471 &master_keyblock, 0, &tmp_mkey_list) == 0) {
472 krb5_dbe_free_key_list(kdc_context, master_keylist);
473 master_keylist = tmp_mkey_list;
474 retval = krb5_db_set_mkey_list(kdc_context, master_keylist);
477 if ((retval = krb5_dbe_find_mkey(kdc_context, master_keylist,
478 server, &mkey_ptr))) {
486 retval = krb5_dbe_find_enctype(kdc_context, server,
487 match_enctype ? ticket->enc_part.enctype : -1,
488 -1, (krb5_int32)ticket->enc_part.kvno,
493 retval = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
496 if ((*key = (krb5_keyblock *)malloc(sizeof **key))) {
497 retval = krb5_dbekd_decrypt_key_data(kdc_context, mkey_ptr,
502 retval = krb5_c_enctype_compare(kdc_context, ticket->enc_part.enctype,
503 (*key)->enctype, &similar);
507 retval = KRB5_KDB_NO_PERMITTED_KEY;
510 (*key)->enctype = ticket->enc_part.enctype;
511 *kvno = server_key->key_data_kvno;
514 krb5_db_free_principal(kdc_context, server, *nprincs);
521 /* This probably wants to be updated if you support last_req stuff */
523 static krb5_last_req_entry nolrentry = { KV5M_LAST_REQ_ENTRY, KRB5_LRQ_NONE, 0 };
524 static krb5_last_req_entry *nolrarray[] = { &nolrentry, 0 };
527 fetch_last_req_info(krb5_db_entry *dbentry, krb5_last_req_entry ***lrentry)
529 *lrentry = nolrarray;
534 /* XXX! This is a temporary place-holder */
537 check_hot_list(krb5_ticket *ticket)
543 #define MAX_REALM_LN 500
547 * subrealm - determine if r2 is a subrealm of r1
549 * SUBREALM takes two realms, r1 and r2, and
550 * determines if r2 is a subrealm of r1.
551 * r2 is a subrealm of r1 if (r1 is a prefix
552 * of r2 AND r1 and r2 begin with a /) or if
553 * (r1 is a suffix of r2 and neither r1 nor r2
556 * RETURNS: If r2 is a subrealm, and r1 is a prefix, the number
557 * of characters in the suffix of r2 is returned as a
560 * If r2 is a subrealm, and r1 is a suffix, the number
561 * of characters in the prefix of r2 is returned as a
564 * If r2 is not a subrealm, SUBREALM returns 0.
567 subrealm(char *r1, char *r2)
572 if(l2 <= l1) return(0);
573 if((*r1 == '/') && (*r2 == '/') && (strncmp(r1,r2,l1) == 0)) return(l1-l2);
574 if((*r1 != '/') && (*r2 != '/') && (strncmp(r1,r2+l2-l1,l1) == 0))
580 * add_to_transited Adds the name of the realm which issued the
581 * ticket granting ticket on which the new ticket to
582 * be issued is based (note that this is the same as
583 * the realm of the server listed in the ticket
586 * ASSUMPTIONS: This procedure assumes that the transited field from
587 * the existing ticket granting ticket already appears
588 * in compressed form. It will add the new realm while
589 * maintaining that form. As long as each successive
590 * realm is added using this (or a similar) routine, the
591 * transited field will be in compressed form. The
592 * basis step is an empty transited field which is, by
593 * its nature, in its most compressed form.
595 * ARGUMENTS: krb5_data *tgt_trans Transited field from TGT
596 * krb5_data *new_trans The transited field for the new ticket
597 * krb5_principal tgs Name of ticket granting server
598 * This includes the realm of the KDC
599 * that issued the ticket granting
600 * ticket. This is the realm that is
601 * to be added to the transited field.
602 * krb5_principal client Name of the client
603 * krb5_principal server The name of the requested server.
604 * This may be the an intermediate
605 * ticket granting server.
607 * The last two argument are needed since they are
608 * implicitly part of the transited field of the new ticket
609 * even though they are not explicitly listed.
611 * RETURNS: krb5_error_code - Success, or out of memory
613 * MODIFIES: new_trans: ->length will contain the length of the new
616 * If ->data was not null when this procedure
617 * is called, the memory referenced by ->data
618 * will be deallocated.
620 * Memory will be allocated for the new transited field
621 * ->data will be updated to point to the newly
624 * BUGS: The space allocated for the new transited field is the
625 * maximum that might be needed given the old transited field,
626 * and the realm to be added. This length is calculated
627 * assuming that no compression of the new realm is possible.
628 * This has no adverse consequences other than the allocation
629 * of more space than required.
631 * This procedure will not yet use the null subfield notation,
632 * and it will get confused if it sees it.
634 * This procedure does not check for quoted commas in realm
639 data2string (krb5_data *d)
642 s = malloc(d->length + 1);
644 memcpy(s, d->data, d->length);
651 add_to_transited(krb5_data *tgt_trans, krb5_data *new_trans,
652 krb5_principal tgs, krb5_principal client,
653 krb5_principal server)
655 krb5_error_code retval;
658 char *otrans, *otrans_ptr;
661 /* The following are for stepping through the transited field */
663 char prev[MAX_REALM_LN];
664 char next[MAX_REALM_LN];
665 char current[MAX_REALM_LN];
666 char exp[MAX_REALM_LN]; /* Expanded current realm name */
669 int clst, nlst; /* count of last character in current and next */
670 int pl, pl1; /* prefix length */
671 int added; /* TRUE = new realm has been added */
673 realm = data2string(krb5_princ_realm(kdc_context, tgs));
677 otrans = data2string(tgt_trans);
678 if (otrans == NULL) {
682 /* Keep track of start so we can free */
686 +1 for extra comma which may be added between
687 +1 for potential space when leading slash in realm */
688 bufsize = strlen(realm) + strlen(otrans) + 3;
689 if (bufsize > MAX_REALM_LN)
690 bufsize = MAX_REALM_LN;
691 if (!(trans = (char *) malloc(bufsize))) {
696 if (new_trans->data) free(new_trans->data);
697 new_trans->data = trans;
698 new_trans->length = 0;
702 /* For the purpose of appending, the realm preceding the first */
703 /* realm in the transited field is considered the null realm */
707 /* read field into current */
708 for (i = 0; *otrans != '\0';) {
709 if (*otrans == '\\') {
710 if (*(++otrans) == '\0')
715 if (*otrans == ',') {
719 current[i++] = *otrans++;
720 if (i >= MAX_REALM_LN) {
721 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
727 added = (krb5_princ_realm(kdc_context, client)->length == strlen(realm) &&
728 !strncmp(krb5_princ_realm(kdc_context, client)->data, realm, strlen(realm))) ||
729 (krb5_princ_realm(kdc_context, server)->length == strlen(realm) &&
730 !strncmp(krb5_princ_realm(kdc_context, server)->data, realm, strlen(realm)));
734 /* figure out expanded form of current name */
736 clst = strlen(current) - 1;
737 if (current[0] == ' ') {
738 strncpy(exp, current+1, sizeof(exp) - 1);
739 exp[sizeof(exp) - 1] = '\0';
741 else if ((current[0] == '/') && (prev[0] == '/')) {
742 strncpy(exp, prev, sizeof(exp) - 1);
743 exp[sizeof(exp) - 1] = '\0';
744 if (strlen(exp) + strlen(current) + 1 >= MAX_REALM_LN) {
745 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
748 strncat(exp, current, sizeof(exp) - 1 - strlen(exp));
750 else if (current[clst] == '.') {
751 strncpy(exp, current, sizeof(exp) - 1);
752 exp[sizeof(exp) - 1] = '\0';
753 if (strlen(exp) + strlen(prev) + 1 >= MAX_REALM_LN) {
754 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
757 strncat(exp, prev, sizeof(exp) - 1 - strlen(exp));
760 strncpy(exp, current, sizeof(exp) - 1);
761 exp[sizeof(exp) - 1] = '\0';
764 /* read field into next */
765 for (i = 0; *otrans != '\0';) {
766 if (*otrans == '\\') {
767 if (*(++otrans) == '\0')
772 if (*otrans == ',') {
776 next[i++] = *otrans++;
777 if (i >= MAX_REALM_LN) {
778 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
785 if (!strcmp(exp, realm)) added = TRUE;
787 /* If we still have to insert the new realm */
791 /* Is the next field compressed? If not, and if the new */
792 /* realm is a subrealm of the current realm, compress */
793 /* the new realm, and insert immediately following the */
794 /* current one. Note that we can not do this if the next*/
795 /* field is already compressed since it would mess up */
796 /* what has already been done. In most cases, this is */
797 /* not a problem because the realm to be added will be a */
798 /* subrealm of the next field too, and we will catch */
799 /* it in a future iteration. */
801 /* Note that the second test here is an unsigned comparison,
802 so the first half (or a cast) is also required. */
803 assert(nlst < 0 || nlst < (int)sizeof(next));
804 if ((nlst < 0 || next[nlst] != '.') &&
806 (pl = subrealm(exp, realm))) {
808 current[sizeof(current) - 1] = '\0';
809 if (strlen(current) + (pl>0?pl:-pl) + 2 >= MAX_REALM_LN) {
810 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
813 strncat(current, ",", sizeof(current) - 1 - strlen(current));
815 strncat(current, realm, (unsigned) pl);
818 strncat(current, realm+strlen(realm)+pl, (unsigned) (-pl));
822 /* Whether or not the next field is compressed, if the */
823 /* realm to be added is a superrealm of the current realm,*/
824 /* then the current realm can be compressed. First the */
825 /* realm to be added must be compressed relative to the */
826 /* previous realm (if possible), and then the current */
827 /* realm compressed relative to the new realm. Note that */
828 /* if the realm to be added is also a superrealm of the */
829 /* previous realm, it would have been added earlier, and */
830 /* we would not reach this step this time around. */
832 else if ((pl = subrealm(realm, exp))) {
835 if ((pl1 = subrealm(prev,realm))) {
836 if (strlen(current) + (pl1>0?pl1:-pl1) + 1 >= MAX_REALM_LN) {
837 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
841 strncat(current, realm, (unsigned) pl1);
844 strncat(current, realm+strlen(realm)+pl1, (unsigned) (-pl1));
847 else { /* If not a subrealm */
848 if ((realm[0] == '/') && prev[0]) {
849 if (strlen(current) + 2 >= MAX_REALM_LN) {
850 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
853 strncat(current, " ", sizeof(current) - 1 - strlen(current));
854 current[sizeof(current) - 1] = '\0';
856 if (strlen(current) + strlen(realm) + 1 >= MAX_REALM_LN) {
857 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
860 strncat(current, realm, sizeof(current) - 1 - strlen(current));
861 current[sizeof(current) - 1] = '\0';
863 if (strlen(current) + (pl>0?pl:-pl) + 2 >= MAX_REALM_LN) {
864 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
867 strncat(current,",", sizeof(current) - 1 - strlen(current));
868 current[sizeof(current) - 1] = '\0';
870 strncat(current, exp, (unsigned) pl);
873 strncat(current, exp+strlen(exp)+pl, (unsigned)(-pl));
878 if (new_trans->length != 0) {
879 if (strlcat(trans, ",", bufsize) >= bufsize) {
880 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
884 if (strlcat(trans, current, bufsize) >= bufsize) {
885 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
888 new_trans->length = strlen(trans);
890 strncpy(prev, exp, sizeof(prev) - 1);
891 prev[sizeof(prev) - 1] = '\0';
892 strncpy(current, next, sizeof(current) - 1);
893 current[sizeof(current) - 1] = '\0';
897 if (new_trans->length != 0) {
898 if (strlcat(trans, ",", bufsize) >= bufsize) {
899 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
903 if((realm[0] == '/') && trans[0]) {
904 if (strlcat(trans, " ", bufsize) >= bufsize) {
905 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
909 if (strlcat(trans, realm, bufsize) >= bufsize) {
910 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
913 new_trans->length = strlen(trans);
924 * Routines that validate a AS request; checks a lot of things. :-)
926 * Returns a Kerberos protocol error number, which is _not_ the same
927 * as a com_err error number!
929 #define AS_INVALID_OPTIONS (KDC_OPT_FORWARDED | KDC_OPT_PROXY |\
930 KDC_OPT_VALIDATE | KDC_OPT_RENEW | \
931 KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_CNAME_IN_ADDL_TKT)
933 validate_as_request(register krb5_kdc_req *request, krb5_db_entry client,
934 krb5_db_entry server, krb5_timestamp kdc_time,
940 * If an option is set that is only allowed in TGS requests, complain.
942 if (request->kdc_options & AS_INVALID_OPTIONS) {
943 *status = "INVALID AS OPTIONS";
944 return KDC_ERR_BADOPTION;
947 /* The client must not be expired */
948 if (client.expiration && client.expiration < kdc_time) {
949 *status = "CLIENT EXPIRED";
951 return(KRB_ERR_GENERIC);
953 return(KDC_ERR_NAME_EXP);
956 /* The client's password must not be expired, unless the server is
957 a KRB5_KDC_PWCHANGE_SERVICE. */
958 if (client.pw_expiration && client.pw_expiration < kdc_time &&
959 !isflagset(server.attributes, KRB5_KDB_PWCHANGE_SERVICE)) {
960 *status = "CLIENT KEY EXPIRED";
962 return(KRB_ERR_GENERIC);
964 return(KDC_ERR_KEY_EXP);
967 /* The server must not be expired */
968 if (server.expiration && server.expiration < kdc_time) {
969 *status = "SERVICE EXPIRED";
970 return(KDC_ERR_SERVICE_EXP);
974 * If the client requires password changing, then only allow the
977 if (isflagset(client.attributes, KRB5_KDB_REQUIRES_PWCHANGE) &&
978 !isflagset(server.attributes, KRB5_KDB_PWCHANGE_SERVICE)) {
979 *status = "REQUIRED PWCHANGE";
980 return(KDC_ERR_KEY_EXP);
983 /* Client and server must allow postdating tickets */
984 if ((isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) ||
985 isflagset(request->kdc_options, KDC_OPT_POSTDATED)) &&
986 (isflagset(client.attributes, KRB5_KDB_DISALLOW_POSTDATED) ||
987 isflagset(server.attributes, KRB5_KDB_DISALLOW_POSTDATED))) {
988 *status = "POSTDATE NOT ALLOWED";
989 return(KDC_ERR_CANNOT_POSTDATE);
993 * A Windows KDC will return KDC_ERR_PREAUTH_REQUIRED instead of
994 * KDC_ERR_POLICY in the following case:
996 * - KDC_OPT_FORWARDABLE is set in KDCOptions but local
997 * policy has KRB5_KDB_DISALLOW_FORWARDABLE set for the
999 * - KRB5_KDB_REQUIRES_PRE_AUTH is set for the client but
1000 * preauthentication data is absent in the request.
1002 * Hence, this check most be done after the check for preauth
1003 * data, and is now performed by validate_forwardable() (the
1004 * contents of which were previously below).
1007 /* Client and server must allow renewable tickets */
1008 if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE) &&
1009 (isflagset(client.attributes, KRB5_KDB_DISALLOW_RENEWABLE) ||
1010 isflagset(server.attributes, KRB5_KDB_DISALLOW_RENEWABLE))) {
1011 *status = "RENEWABLE NOT ALLOWED";
1012 return(KDC_ERR_POLICY);
1015 /* Client and server must allow proxiable tickets */
1016 if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE) &&
1017 (isflagset(client.attributes, KRB5_KDB_DISALLOW_PROXIABLE) ||
1018 isflagset(server.attributes, KRB5_KDB_DISALLOW_PROXIABLE))) {
1019 *status = "PROXIABLE NOT ALLOWED";
1020 return(KDC_ERR_POLICY);
1023 /* Check to see if client is locked out */
1024 if (isflagset(client.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
1025 *status = "CLIENT LOCKED OUT";
1026 return(KDC_ERR_CLIENT_REVOKED);
1029 /* Check to see if server is locked out */
1030 if (isflagset(server.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
1031 *status = "SERVICE LOCKED OUT";
1032 return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
1035 /* Check to see if server is allowed to be a service */
1036 if (isflagset(server.attributes, KRB5_KDB_DISALLOW_SVR)) {
1037 *status = "SERVICE NOT ALLOWED";
1038 return(KDC_ERR_MUST_USE_USER2USER);
1042 * Check against local policy
1044 errcode = against_local_policy_as(request, client, server,
1053 validate_forwardable(krb5_kdc_req *request, krb5_db_entry client,
1054 krb5_db_entry server, krb5_timestamp kdc_time,
1055 const char **status)
1058 if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) &&
1059 (isflagset(client.attributes, KRB5_KDB_DISALLOW_FORWARDABLE) ||
1060 isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))) {
1061 *status = "FORWARDABLE NOT ALLOWED";
1062 return(KDC_ERR_POLICY);
1067 #define ASN1_ID_CLASS (0xc0)
1068 #define ASN1_ID_TYPE (0x20)
1069 #define ASN1_ID_TAG (0x1f)
1070 #define ASN1_CLASS_UNIV (0)
1071 #define ASN1_CLASS_APP (1)
1072 #define ASN1_CLASS_CTX (2)
1073 #define ASN1_CLASS_PRIV (3)
1074 #define asn1_id_constructed(x) (x & ASN1_ID_TYPE)
1075 #define asn1_id_primitive(x) (!asn1_id_constructed(x))
1076 #define asn1_id_class(x) ((x & ASN1_ID_CLASS) >> 6)
1077 #define asn1_id_tag(x) (x & ASN1_ID_TAG)
1080 * asn1length - return encoded length of value.
1082 * passed a pointer into the asn.1 stream, which is updated
1083 * to point right after the length bits.
1085 * returns -1 on failure.
1088 asn1length(unsigned char **astream)
1090 int length; /* resulting length */
1091 int sublen; /* sublengths */
1092 int blen; /* bytes of length */
1093 unsigned char *p; /* substring searching */
1095 if (**astream & 0x80) {
1096 blen = **astream & 0x7f;
1100 for (++*astream, length = 0; blen; ++*astream, blen--) {
1101 length = (length << 8) | **astream;
1104 /* indefinite length, figure out by hand */
1108 /* compute value length. */
1109 if ((sublen = asn1length(&p)) < 0) {
1113 /* check for termination */
1114 if ((!*p++) && (!*p)) {
1119 length = p - *astream;
1129 * fetch_asn1_field - return raw asn.1 stream of subfield.
1131 * this routine is passed a context-dependent tag number and "level" and returns
1132 * the size and length of the corresponding level subfield.
1134 * levels and are numbered starting from 1.
1136 * returns 0 on success, -1 otherwise.
1139 fetch_asn1_field(unsigned char *astream, unsigned int level,
1140 unsigned int field, krb5_data *data)
1142 unsigned char *estream; /* end of stream */
1143 int classes; /* # classes seen so far this level */
1144 unsigned int levels = 0; /* levels seen so far */
1145 int lastlevel = 1000; /* last level seen */
1146 int length; /* various lengths */
1147 int tag; /* tag number */
1148 unsigned char savelen; /* saved length of our field */
1151 /* we assume that the first identifier/length will tell us
1152 how long the entire stream is. */
1155 if ((length = asn1length(&astream)) < 0) {
1159 /* search down the stream, checking identifiers. we process identifiers
1160 until we hit the "level" we want, and then process that level for our
1161 subfield, always making sure we don't go off the end of the stream. */
1162 while (astream < estream) {
1163 if (!asn1_id_constructed(*astream)) {
1166 if (asn1_id_class(*astream) == ASN1_CLASS_CTX) {
1167 if ((tag = (int)asn1_id_tag(*astream)) <= lastlevel) {
1172 if (levels == level) {
1173 /* in our context-dependent class, is this the one we're looking for ? */
1174 if (tag == (int)field) {
1175 /* return length and data */
1178 if ((data->length = asn1length(&astream)) < 0) {
1181 /* if the field length is indefinite, we will have to subtract two
1182 (terminating octets) from the length returned since we don't want
1183 to pass any info from the "wrapper" back. asn1length will always return
1184 the *total* length of the field, not just what's contained in it */
1185 if ((savelen & 0xff) == 0x80) {
1188 data->data = (char *)astream;
1190 } else if (tag <= classes) {
1191 /* we've seen this class before, something must be wrong */
1198 /* if we're not on our level yet, process this value. otherwise skip over it */
1200 if ((length = asn1length(&astream)) < 0) {
1203 if (levels == level) {
1211 * Routines that validate a TGS request; checks a lot of things. :-)
1213 * Returns a Kerberos protocol error number, which is _not_ the same
1214 * as a com_err error number!
1216 #define TGS_OPTIONS_HANDLED (KDC_OPT_FORWARDABLE | KDC_OPT_FORWARDED | \
1217 KDC_OPT_PROXIABLE | KDC_OPT_PROXY | \
1218 KDC_OPT_ALLOW_POSTDATE | KDC_OPT_POSTDATED | \
1219 KDC_OPT_RENEWABLE | KDC_OPT_RENEWABLE_OK | \
1220 KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_RENEW | \
1221 KDC_OPT_VALIDATE | KDC_OPT_CANONICALIZE | KDC_OPT_CNAME_IN_ADDL_TKT)
1222 #define NO_TGT_OPTION (KDC_OPT_FORWARDED | KDC_OPT_PROXY | KDC_OPT_RENEW | \
1226 validate_tgs_request(register krb5_kdc_req *request, krb5_db_entry server,
1227 krb5_ticket *ticket, krb5_timestamp kdc_time,
1228 const char **status)
1234 * If an illegal option is set, ignore it.
1236 request->kdc_options &= TGS_OPTIONS_HANDLED;
1238 /* Check to see if server has expired */
1239 if (server.expiration && server.expiration < kdc_time) {
1240 *status = "SERVICE EXPIRED";
1241 return(KDC_ERR_SERVICE_EXP);
1245 * Verify that the server principal in authdat->ticket is correct
1246 * (either the ticket granting service or the service that was
1247 * originally requested)
1249 if (request->kdc_options & NO_TGT_OPTION) {
1250 if (!krb5_principal_compare(kdc_context, ticket->server, request->server)) {
1251 *status = "SERVER DIDN'T MATCH TICKET FOR RENEW/FORWARD/ETC";
1252 return(KDC_ERR_SERVER_NOMATCH);
1256 * OK, we need to validate the krbtgt service in the ticket.
1258 * The krbtgt service is of the form:
1259 * krbtgt/realm-A@realm-B
1261 * Realm A is the "server realm"; the realm of the
1262 * server of the requested ticket must match this realm.
1263 * Of course, it should be a realm serviced by this KDC.
1265 * Realm B is the "client realm"; this is what should be
1266 * added to the transited field. (which is done elsewhere)
1269 /* Make sure there are two components... */
1270 if (krb5_princ_size(kdc_context, ticket->server) != 2) {
1271 *status = "BAD TGS SERVER LENGTH";
1272 return KRB_AP_ERR_NOT_US;
1274 /* ...that the first component is krbtgt... */
1275 if (!krb5_is_tgs_principal(ticket->server)) {
1276 *status = "BAD TGS SERVER NAME";
1277 return KRB_AP_ERR_NOT_US;
1279 /* ...and that the second component matches the server realm... */
1280 if ((krb5_princ_size(kdc_context, ticket->server) <= 1) ||
1281 !data_eq(*krb5_princ_component(kdc_context, ticket->server, 1),
1282 *krb5_princ_realm(kdc_context, request->server))) {
1283 *status = "BAD TGS SERVER INSTANCE";
1284 return KRB_AP_ERR_NOT_US;
1286 /* XXX add check that second component must match locally
1290 /* Server must allow TGS based issuances */
1291 if (isflagset(server.attributes, KRB5_KDB_DISALLOW_TGT_BASED)) {
1292 *status = "TGT BASED NOT ALLOWED";
1293 return(KDC_ERR_POLICY);
1297 /* TGS must be forwardable to get forwarded or forwardable ticket */
1298 if ((isflagset(request->kdc_options, KDC_OPT_FORWARDED) ||
1299 isflagset(request->kdc_options, KDC_OPT_FORWARDABLE)) &&
1300 !isflagset(ticket->enc_part2->flags, TKT_FLG_FORWARDABLE)) {
1301 *status = "TGT NOT FORWARDABLE";
1303 return KDC_ERR_BADOPTION;
1306 /* TGS must be proxiable to get proxiable ticket */
1307 if ((isflagset(request->kdc_options, KDC_OPT_PROXY) ||
1308 isflagset(request->kdc_options, KDC_OPT_PROXIABLE)) &&
1309 !isflagset(ticket->enc_part2->flags, TKT_FLG_PROXIABLE)) {
1310 *status = "TGT NOT PROXIABLE";
1311 return KDC_ERR_BADOPTION;
1314 /* TGS must allow postdating to get postdated ticket */
1315 if ((isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) ||
1316 isflagset(request->kdc_options, KDC_OPT_POSTDATED)) &&
1317 !isflagset(ticket->enc_part2->flags, TKT_FLG_MAY_POSTDATE)) {
1318 *status = "TGT NOT POSTDATABLE";
1319 return KDC_ERR_BADOPTION;
1322 /* can only validate invalid tix */
1323 if (isflagset(request->kdc_options, KDC_OPT_VALIDATE) &&
1324 !isflagset(ticket->enc_part2->flags, TKT_FLG_INVALID)) {
1325 *status = "VALIDATE VALID TICKET";
1326 return KDC_ERR_BADOPTION;
1329 /* can only renew renewable tix */
1330 if ((isflagset(request->kdc_options, KDC_OPT_RENEW) ||
1331 isflagset(request->kdc_options, KDC_OPT_RENEWABLE)) &&
1332 !isflagset(ticket->enc_part2->flags, TKT_FLG_RENEWABLE)) {
1333 *status = "TICKET NOT RENEWABLE";
1334 return KDC_ERR_BADOPTION;
1337 /* can not proxy ticket granting tickets */
1338 if (isflagset(request->kdc_options, KDC_OPT_PROXY) &&
1339 (!request->server->data ||
1340 !data_eq_string(request->server->data[0], KRB5_TGS_NAME))) {
1341 *status = "CAN'T PROXY TGT";
1342 return KDC_ERR_BADOPTION;
1345 /* Server must allow forwardable tickets */
1346 if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) &&
1347 isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE)) {
1348 *status = "NON-FORWARDABLE TICKET";
1349 return(KDC_ERR_POLICY);
1352 /* Server must allow renewable tickets */
1353 if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE) &&
1354 isflagset(server.attributes, KRB5_KDB_DISALLOW_RENEWABLE)) {
1355 *status = "NON-RENEWABLE TICKET";
1356 return(KDC_ERR_POLICY);
1359 /* Server must allow proxiable tickets */
1360 if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE) &&
1361 isflagset(server.attributes, KRB5_KDB_DISALLOW_PROXIABLE)) {
1362 *status = "NON-PROXIABLE TICKET";
1363 return(KDC_ERR_POLICY);
1366 /* Server must allow postdated tickets */
1367 if (isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) &&
1368 isflagset(server.attributes, KRB5_KDB_DISALLOW_POSTDATED)) {
1369 *status = "NON-POSTDATABLE TICKET";
1370 return(KDC_ERR_CANNOT_POSTDATE);
1373 /* Server must allow DUP SKEY requests */
1374 if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY) &&
1375 isflagset(server.attributes, KRB5_KDB_DISALLOW_DUP_SKEY)) {
1376 *status = "DUP_SKEY DISALLOWED";
1377 return(KDC_ERR_POLICY);
1380 /* Server must not be locked out */
1381 if (isflagset(server.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
1382 *status = "SERVER LOCKED OUT";
1383 return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
1386 /* Server must be allowed to be a service */
1387 if (isflagset(server.attributes, KRB5_KDB_DISALLOW_SVR)) {
1388 *status = "SERVER NOT ALLOWED";
1389 return(KDC_ERR_MUST_USE_USER2USER);
1392 /* Check the hot list */
1393 if (check_hot_list(ticket)) {
1394 *status = "HOT_LIST";
1395 return(KRB_AP_ERR_REPEAT);
1398 /* Check the start time vs. the KDC time */
1399 if (isflagset(request->kdc_options, KDC_OPT_VALIDATE)) {
1400 if (ticket->enc_part2->times.starttime > kdc_time) {
1401 *status = "NOT_YET_VALID";
1402 return(KRB_AP_ERR_TKT_NYV);
1407 * Check the renew_till time. The endtime was already
1408 * been checked in the initial authentication check.
1410 if (isflagset(request->kdc_options, KDC_OPT_RENEW) &&
1411 (ticket->enc_part2->times.renew_till < kdc_time)) {
1412 *status = "TKT_EXPIRED";
1413 return(KRB_AP_ERR_TKT_EXPIRED);
1417 * Checks for ENC_TKT_IN_SKEY:
1419 * (1) Make sure the second ticket exists
1420 * (2) Make sure it is a ticket granting ticket
1422 if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) {
1423 if (!request->second_ticket ||
1424 !request->second_ticket[st_idx]) {
1425 *status = "NO_2ND_TKT";
1426 return(KDC_ERR_BADOPTION);
1428 if (!krb5_principal_compare(kdc_context, request->second_ticket[st_idx]->server,
1430 *status = "2ND_TKT_NOT_TGS";
1431 return(KDC_ERR_POLICY);
1435 if (isflagset(request->kdc_options, KDC_OPT_CNAME_IN_ADDL_TKT)) {
1436 if (!request->second_ticket ||
1437 !request->second_ticket[st_idx]) {
1438 *status = "NO_2ND_TKT";
1439 return(KDC_ERR_BADOPTION);
1444 /* Check for hardware preauthentication */
1445 if (isflagset(server.attributes, KRB5_KDB_REQUIRES_HW_AUTH) &&
1446 !isflagset(ticket->enc_part2->flags,TKT_FLG_HW_AUTH)) {
1447 *status = "NO HW PREAUTH";
1448 return KRB_ERR_GENERIC;
1451 /* Check for any kind of preauthentication */
1452 if (isflagset(server.attributes, KRB5_KDB_REQUIRES_PRE_AUTH) &&
1453 !isflagset(ticket->enc_part2->flags, TKT_FLG_PRE_AUTH)) {
1454 *status = "NO PREAUTH";
1455 return KRB_ERR_GENERIC;
1459 * Check local policy
1461 errcode = against_local_policy_tgs(request, server, ticket, status);
1470 * This function returns 1 if the dbentry has a key for a specified
1471 * keytype, and 0 if not.
1474 dbentry_has_key_for_enctype(krb5_context context, krb5_db_entry *client,
1475 krb5_enctype enctype)
1477 krb5_error_code retval;
1478 krb5_key_data *datap;
1480 retval = krb5_dbe_find_enctype(context, client, enctype,
1489 * This function returns 1 if the entity referenced by this
1490 * structure can support the a particular encryption system, and 0 if
1493 * XXX eventually this information should be looked up in the
1494 * database. Since it isn't, we use some hueristics and attribute
1495 * options bits for now.
1498 dbentry_supports_enctype(krb5_context context, krb5_db_entry *client,
1499 krb5_enctype enctype)
1502 * If it's DES_CBC_MD5, there's a bit in the attribute mask which
1503 * checks to see if we support it. For now, treat it as always
1506 * In theory everything's supposed to support DES_CBC_MD5, but
1507 * that's not the reality....
1509 if (enctype == ENCTYPE_DES_CBC_MD5)
1513 * XXX we assume everything can understand DES_CBC_CRC
1515 if (enctype == ENCTYPE_DES_CBC_CRC)
1519 * If we have a key for the encryption system, we assume it's
1522 return dbentry_has_key_for_enctype(context, client, enctype);
1526 * This function returns the keytype which should be selected for the
1527 * session key. It is based on the ordered list which the user
1528 * requested, and what the KDC and the application server can support.
1531 select_session_keytype(krb5_context context, krb5_db_entry *server,
1532 int nktypes, krb5_enctype *ktype)
1536 for (i = 0; i < nktypes; i++) {
1537 if (!krb5_c_valid_enctype(ktype[i]))
1540 if (!krb5_is_permitted_enctype(context, ktype[i]))
1543 if (dbentry_supports_enctype(context, server, ktype[i]))
1550 * This function returns salt information for a particular client_key
1553 get_salt_from_key(krb5_context context, krb5_principal client,
1554 krb5_key_data *client_key, krb5_data *salt)
1556 krb5_error_code retval;
1560 salt->length = SALT_TYPE_NO_LENGTH;
1562 if (client_key->key_data_ver == 1)
1565 switch (client_key->key_data_type[1]) {
1566 case KRB5_KDB_SALTTYPE_NORMAL:
1568 * The client could infer the salt from the principal, but
1569 * might use the wrong principal name if this is an alias. So
1570 * it's more reliable to send an explicit salt.
1572 if ((retval = krb5_principal2salt(context, client, salt)))
1575 case KRB5_KDB_SALTTYPE_V4:
1576 /* send an empty (V4) salt */
1580 case KRB5_KDB_SALTTYPE_NOREALM:
1581 if ((retval = krb5_principal2salt_norealm(context, client, salt)))
1584 case KRB5_KDB_SALTTYPE_AFS3:
1585 /* send the same salt as with onlyrealm - but with no type info,
1586 we just hope they figure it out on the other end. */
1587 /* fall through to onlyrealm: */
1588 case KRB5_KDB_SALTTYPE_ONLYREALM:
1589 realm = krb5_princ_realm(context, client);
1590 salt->length = realm->length;
1591 if ((salt->data = malloc(realm->length)) == NULL)
1593 memcpy(salt->data, realm->data, realm->length);
1595 case KRB5_KDB_SALTTYPE_SPECIAL:
1596 salt->length = client_key->key_data_length[1];
1597 if ((salt->data = malloc(salt->length)) == NULL)
1599 memcpy(salt->data, client_key->key_data_contents[1], salt->length);
1606 * Limit strings to a "reasonable" length to prevent crowding out of
1607 * other useful information in the log entry
1609 #define NAME_LENGTH_LIMIT 128
1611 void limit_string(char *name)
1618 if (strlen(name) < NAME_LENGTH_LIMIT)
1621 i = NAME_LENGTH_LIMIT-4;
1630 * L10_2 = log10(2**x), rounded up; log10(2) ~= 0.301.
1632 #define L10_2(x) ((int)(((x * 301) + 999) / 1000))
1635 * Max length of sprintf("%ld") for an int of type T; includes leading
1636 * minus sign and terminating NUL.
1638 #define D_LEN(t) (L10_2(sizeof(t) * CHAR_BIT) + 2)
1641 ktypes2str(char *s, size_t len, int nktypes, krb5_enctype *ktype)
1644 char stmp[D_LEN(krb5_enctype) + 1];
1648 || len < (sizeof(" etypes {...}") + D_LEN(int))) {
1653 snprintf(s, len, "%d etypes {", nktypes);
1654 for (i = 0; i < nktypes; i++) {
1655 snprintf(stmp, sizeof(stmp), "%s%ld", i ? " " : "", (long)ktype[i]);
1656 if (strlen(s) + strlen(stmp) + sizeof("}") > len)
1658 strlcat(s, stmp, len);
1662 * We broke out of the loop. Try to truncate the list.
1665 while (p - s + sizeof("...}") > len) {
1666 while (p > s && *p != ' ' && *p != '{')
1668 if (p > s && *p == ' ') {
1673 strlcat(s, "...", len);
1675 strlcat(s, "}", len);
1680 rep_etypes2str(char *s, size_t len, krb5_kdc_rep *rep)
1682 char stmp[sizeof("ses=") + D_LEN(krb5_enctype)];
1684 if (len < (3 * D_LEN(krb5_enctype)
1685 + sizeof("etypes {rep= tkt= ses=}"))) {
1690 snprintf(s, len, "etypes {rep=%ld", (long)rep->enc_part.enctype);
1692 if (rep->ticket != NULL) {
1693 snprintf(stmp, sizeof(stmp),
1694 " tkt=%ld", (long)rep->ticket->enc_part.enctype);
1695 strlcat(s, stmp, len);
1698 if (rep->ticket != NULL
1699 && rep->ticket->enc_part2 != NULL
1700 && rep->ticket->enc_part2->session != NULL) {
1701 snprintf(stmp, sizeof(stmp), " ses=%ld",
1702 (long)rep->ticket->enc_part2->session->enctype);
1703 strlcat(s, stmp, len);
1705 strlcat(s, "}", len);
1710 get_principal_locked (krb5_context kcontext,
1711 krb5_const_principal search_for,
1712 krb5_db_entry *entries, int *nentries,
1715 return krb5_db_get_principal (kcontext, search_for, entries, nentries,
1720 get_principal (krb5_context kcontext,
1721 krb5_const_principal search_for,
1722 krb5_db_entry *entries, int *nentries, krb5_boolean *more)
1724 /* Eventually this will be used to manage locking while looking up
1725 principals in the database. */
1726 return get_principal_locked (kcontext, search_for, entries, nentries,
1732 sign_db_authdata (krb5_context context,
1734 krb5_const_principal client_princ,
1735 krb5_db_entry *client,
1736 krb5_db_entry *server,
1737 krb5_db_entry *krbtgt,
1738 krb5_keyblock *client_key,
1739 krb5_keyblock *server_key,
1740 krb5_timestamp authtime,
1741 krb5_authdata **tgs_authdata,
1742 krb5_keyblock *session_key,
1743 krb5_authdata ***ret_authdata,
1744 krb5_db_entry *ad_entry,
1747 krb5_error_code code;
1748 kdb_sign_auth_data_req req;
1749 kdb_sign_auth_data_rep rep;
1753 *ret_authdata = NULL;
1754 memset(ad_entry, 0, sizeof(*ad_entry));
1757 memset(&req, 0, sizeof(req));
1758 memset(&rep, 0, sizeof(rep));
1761 req.client_princ = client_princ;
1762 req.client = client;
1763 req.server = server;
1764 req.krbtgt = krbtgt;
1765 req.client_key = client_key;
1766 req.server_key = server_key;
1767 req.authtime = authtime;
1768 req.auth_data = tgs_authdata;
1769 req.session_key = session_key;
1771 rep.entry = ad_entry;
1774 req_data.data = (void *)&req;
1775 req_data.length = sizeof(req);
1777 rep_data.data = (void *)&rep;
1778 rep_data.length = sizeof(rep);
1780 code = krb5_db_invoke(context,
1781 KRB5_KDB_METHOD_SIGN_AUTH_DATA,
1785 *ret_authdata = rep.auth_data;
1786 *ad_nprincs = rep.nprincs;
1791 static krb5_error_code
1792 verify_for_user_checksum(krb5_context context,
1794 krb5_pa_for_user *req)
1796 krb5_error_code code;
1798 krb5_int32 name_type;
1801 krb5_boolean valid = FALSE;
1803 if (!krb5_c_is_keyed_cksum(req->cksum.checksum_type)) {
1804 return KRB5KRB_AP_ERR_INAPP_CKSUM;
1808 * Checksum is over name type and string components of
1809 * client principal name and auth_package.
1812 for (i = 0; i < krb5_princ_size(context, req->user); i++) {
1813 data.length += krb5_princ_component(context, req->user, i)->length;
1815 data.length += krb5_princ_realm(context, req->user)->length;
1816 data.length += req->auth_package.length;
1818 p = data.data = malloc(data.length);
1819 if (data.data == NULL) {
1823 name_type = krb5_princ_type(context, req->user);
1824 p[0] = (name_type >> 0 ) & 0xFF;
1825 p[1] = (name_type >> 8 ) & 0xFF;
1826 p[2] = (name_type >> 16) & 0xFF;
1827 p[3] = (name_type >> 24) & 0xFF;
1830 for (i = 0; i < krb5_princ_size(context, req->user); i++) {
1831 memcpy(p, krb5_princ_component(context, req->user, i)->data,
1832 krb5_princ_component(context, req->user, i)->length);
1833 p += krb5_princ_component(context, req->user, i)->length;
1836 memcpy(p, krb5_princ_realm(context, req->user)->data,
1837 krb5_princ_realm(context, req->user)->length);
1838 p += krb5_princ_realm(context, req->user)->length;
1840 memcpy(p, req->auth_package.data, req->auth_package.length);
1841 p += req->auth_package.length;
1843 code = krb5_c_verify_checksum(context,
1845 KRB5_KEYUSAGE_APP_DATA_CKSUM,
1850 if (code == 0 && valid == FALSE)
1851 code = KRB5KRB_AP_ERR_MODIFIED;
1859 * Legacy protocol transition (Windows 2003 and above)
1861 static krb5_error_code
1862 kdc_process_for_user(krb5_context context,
1863 krb5_pa_data *pa_data,
1864 krb5_keyblock *tgs_session,
1865 krb5_pa_s4u_x509_user **s4u_x509_user,
1866 const char **status)
1868 krb5_error_code code;
1869 krb5_pa_for_user *for_user;
1872 req_data.length = pa_data->length;
1873 req_data.data = (char *)pa_data->contents;
1875 code = decode_krb5_pa_for_user(&req_data, &for_user);
1879 code = verify_for_user_checksum(context, tgs_session, for_user);
1881 *status = "INVALID_S4U2SELF_CHECKSUM";
1882 krb5_free_pa_for_user(kdc_context, for_user);
1886 *s4u_x509_user = calloc(1, sizeof(krb5_pa_s4u_x509_user));
1887 if (*s4u_x509_user == NULL) {
1888 krb5_free_pa_for_user(kdc_context, for_user);
1892 (*s4u_x509_user)->user_id.user = for_user->user;
1893 for_user->user = NULL;
1894 krb5_free_pa_for_user(context, for_user);
1899 static krb5_error_code
1900 verify_s4u_x509_user_checksum(krb5_context context,
1902 krb5_data *req_data,
1903 krb5_int32 kdc_req_nonce,
1904 krb5_pa_s4u_x509_user *req)
1906 krb5_error_code code;
1908 krb5_boolean valid = FALSE;
1910 if (enctype_requires_etype_info_2(key->enctype) &&
1911 !krb5_c_is_keyed_cksum(req->cksum.checksum_type))
1912 return KRB5KRB_AP_ERR_INAPP_CKSUM;
1914 if (req->user_id.nonce != kdc_req_nonce)
1915 return KRB5KRB_AP_ERR_MODIFIED;
1918 * Verify checksum over the encoded userid. If that fails,
1919 * re-encode, and verify that. This is similar to the
1920 * behaviour in kdc_process_tgs_req().
1922 if (fetch_asn1_field((unsigned char *)req_data->data, 1, 0, &scratch) < 0)
1923 return ASN1_PARSE_ERROR;
1925 code = krb5_c_verify_checksum(context,
1927 KRB5_KEYUSAGE_PA_S4U_X509_USER_REQUEST,
1934 if (valid == FALSE) {
1937 code = encode_krb5_s4u_userid(&req->user_id, &data);
1941 code = krb5_c_verify_checksum(context,
1943 KRB5_KEYUSAGE_PA_S4U_X509_USER_REQUEST,
1948 krb5_free_data(context, data);
1954 return valid ? 0 : KRB5KRB_AP_ERR_MODIFIED;
1958 * New protocol transition request (Windows 2008 and above)
1960 static krb5_error_code
1961 kdc_process_s4u_x509_user(krb5_context context,
1962 krb5_kdc_req *request,
1963 krb5_pa_data *pa_data,
1964 krb5_keyblock *tgs_subkey,
1965 krb5_keyblock *tgs_session,
1966 krb5_pa_s4u_x509_user **s4u_x509_user,
1967 const char **status)
1969 krb5_error_code code;
1972 req_data.length = pa_data->length;
1973 req_data.data = (char *)pa_data->contents;
1975 code = decode_krb5_pa_s4u_x509_user(&req_data, s4u_x509_user);
1979 code = verify_s4u_x509_user_checksum(context,
1980 tgs_subkey ? tgs_subkey :
1983 request->nonce, *s4u_x509_user);
1986 *status = "INVALID_S4U2SELF_CHECKSUM";
1987 krb5_free_pa_s4u_x509_user(context, *s4u_x509_user);
1988 *s4u_x509_user = NULL;
1992 if (krb5_princ_size(context, (*s4u_x509_user)->user_id.user) == 0 ||
1993 (*s4u_x509_user)->user_id.subject_cert.length != 0) {
1994 *status = "INVALID_S4U2SELF_REQUEST";
1995 krb5_free_pa_s4u_x509_user(context, *s4u_x509_user);
1996 *s4u_x509_user = NULL;
1997 return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
2004 kdc_make_s4u2self_rep(krb5_context context,
2005 krb5_keyblock *tgs_subkey,
2006 krb5_keyblock *tgs_session,
2007 krb5_pa_s4u_x509_user *req_s4u_user,
2008 krb5_kdc_rep *reply,
2009 krb5_enc_kdc_rep_part *reply_encpart)
2011 krb5_error_code code;
2012 krb5_data *data = NULL;
2013 krb5_pa_s4u_x509_user rep_s4u_user;
2014 krb5_pa_data padata;
2015 krb5_enctype enctype;
2016 krb5_keyusage usage;
2018 memset(&rep_s4u_user, 0, sizeof(rep_s4u_user));
2020 rep_s4u_user.user_id.nonce = req_s4u_user->user_id.nonce;
2021 rep_s4u_user.user_id.user = req_s4u_user->user_id.user;
2022 rep_s4u_user.user_id.options =
2023 req_s4u_user->user_id.options & KRB5_S4U_OPTS_USE_REPLY_KEY_USAGE;
2025 code = encode_krb5_s4u_userid(&rep_s4u_user.user_id, &data);
2029 if (req_s4u_user->user_id.options & KRB5_S4U_OPTS_USE_REPLY_KEY_USAGE)
2030 usage = KRB5_KEYUSAGE_PA_S4U_X509_USER_REPLY;
2032 usage = KRB5_KEYUSAGE_PA_S4U_X509_USER_REQUEST;
2034 code = krb5_c_make_checksum(context, req_s4u_user->cksum.checksum_type,
2035 tgs_subkey != NULL ? tgs_subkey : tgs_session,
2037 &rep_s4u_user.cksum);
2041 krb5_free_data(context, data);
2044 code = encode_krb5_pa_s4u_x509_user(&rep_s4u_user, &data);
2048 padata.magic = KV5M_PA_DATA;
2049 padata.pa_type = KRB5_PADATA_S4U_X509_USER;
2050 padata.length = data->length;
2051 padata.contents = (krb5_octet *)data->data;
2053 code = add_pa_data_element(context, &padata, &reply->padata, FALSE);
2060 if (tgs_subkey != NULL)
2061 enctype = tgs_subkey->enctype;
2063 enctype = tgs_session->enctype;
2066 * Owing to a bug in Windows, unkeyed checksums were used for older
2067 * enctypes, including rc4-hmac. A forthcoming workaround for this
2068 * includes the checksum bytes in the encrypted padata.
2070 if ((req_s4u_user->user_id.options & KRB5_S4U_OPTS_USE_REPLY_KEY_USAGE) &&
2071 enctype_requires_etype_info_2(enctype) == FALSE) {
2072 padata.length = req_s4u_user->cksum.length +
2073 rep_s4u_user.cksum.length;
2074 padata.contents = malloc(padata.length);
2075 if (padata.contents == NULL) {
2080 memcpy(padata.contents,
2081 req_s4u_user->cksum.contents,
2082 req_s4u_user->cksum.length);
2083 memcpy(&padata.contents[req_s4u_user->cksum.length],
2084 rep_s4u_user.cksum.contents,
2085 rep_s4u_user.cksum.length);
2087 code = add_pa_data_element(context,&padata,
2088 &reply_encpart->enc_padata, FALSE);
2094 if (rep_s4u_user.cksum.contents != NULL)
2095 krb5_free_checksum_contents(context, &rep_s4u_user.cksum);
2096 krb5_free_data(context, data);
2102 * Protocol transition (S4U2Self)
2105 kdc_process_s4u2self_req(krb5_context context,
2106 krb5_kdc_req *request,
2107 krb5_const_principal client_princ,
2108 const krb5_db_entry *server,
2109 krb5_keyblock *tgs_subkey,
2110 krb5_keyblock *tgs_session,
2111 krb5_timestamp kdc_time,
2112 krb5_pa_s4u_x509_user **s4u_x509_user,
2113 krb5_db_entry *princ,
2115 const char **status)
2117 krb5_error_code code;
2118 krb5_pa_data *pa_data;
2123 memset(princ, 0, sizeof(*princ));
2125 pa_data = find_pa_data(request->padata, KRB5_PADATA_S4U_X509_USER);
2126 if (pa_data != NULL) {
2127 code = kdc_process_s4u_x509_user(context,
2137 pa_data = find_pa_data(request->padata, KRB5_PADATA_FOR_USER);
2138 if (pa_data != NULL) {
2139 code = kdc_process_for_user(context,
2151 * We need to compare the client name in the TGT with the requested
2152 * server name. Supporting server name aliases without assuming a
2153 * global name service makes this difficult to do.
2155 * The comparison below handles the following cases (note that the
2156 * term "principal name" below excludes the realm).
2158 * (1) The requested service is a host-based service with two name
2159 * components, in which case we assume the principal name to
2160 * contain sufficient qualifying information. The realm is
2161 * ignored for the purpose of comparison.
2163 * (2) The requested service name is an enterprise principal name:
2164 * the service principal name is compared with the unparsed
2165 * form of the client name (including its realm).
2167 * (3) The requested service is some other name type: an exact
2168 * match is required.
2170 * An alternative would be to look up the server once again with
2171 * FLAG_CANONICALIZE | FLAG_CLIENT_REFERRALS_ONLY set, do an exact
2172 * match between the returned name and client_princ. However, this
2173 * assumes that the client set FLAG_CANONICALIZE when requesting
2174 * the TGT and that we have a global name service.
2177 switch (krb5_princ_type(kdc_context, request->server)) {
2178 case KRB5_NT_SRV_HST: /* (1) */
2179 if (krb5_princ_size(kdc_context, request->server) == 2)
2180 flags |= KRB5_PRINCIPAL_COMPARE_IGNORE_REALM;
2182 case KRB5_NT_ENTERPRISE_PRINCIPAL: /* (2) */
2183 flags |= KRB5_PRINCIPAL_COMPARE_ENTERPRISE;
2189 if (!krb5_principal_compare_flags(context,
2193 *status = "INVALID_S4U2SELF_REQUEST";
2194 return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; /* match Windows error code */
2198 * Protocol transition is mutually exclusive with renew/forward/etc
2199 * as well as user-to-user and constrained delegation. This check
2200 * is also made in validate_as_request().
2202 * We can assert from this check that the header ticket was a TGT, as
2203 * that is validated previously in validate_tgs_request().
2205 if (request->kdc_options & AS_INVALID_OPTIONS) {
2206 *status = "INVALID AS OPTIONS";
2207 return KRB5KDC_ERR_BADOPTION;
2211 * Do not attempt to lookup principals in foreign realms.
2213 if (is_local_principal((*s4u_x509_user)->user_id.user)) {
2214 krb5_db_entry no_server;
2217 code = krb5_db_get_principal_ext(kdc_context,
2218 (*s4u_x509_user)->user_id.user,
2219 KRB5_KDB_FLAG_INCLUDE_PAC,
2220 princ, nprincs, &more);
2222 *status = "LOOKING_UP_S4U2SELF_PRINCIPAL";
2224 return code; /* caller can free for_user */
2228 *status = "NON_UNIQUE_S4U2SELF_PRINCIPAL";
2229 return KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE;
2230 } else if (*nprincs != 1) {
2231 *status = "UNKNOWN_S4U2SELF_PRINCIPAL";
2232 return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
2235 memset(&no_server, 0, sizeof(no_server));
2237 code = validate_as_request(request, *princ,
2238 no_server, kdc_time, status);
2247 static krb5_error_code
2248 check_allowed_to_delegate_to(krb5_context context,
2249 const krb5_db_entry *server,
2250 krb5_const_principal proxy)
2252 kdb_check_allowed_to_delegate_req req;
2255 krb5_error_code code;
2257 /* Can't get a TGT (otherwise it would be unconstrained delegation) */
2258 if (krb5_is_tgs_principal(proxy)) {
2259 return KRB5KDC_ERR_POLICY;
2262 /* Must be in same realm */
2263 if (!krb5_realm_compare(context, server->princ, proxy)) {
2264 return KRB5KDC_ERR_POLICY;
2267 req.server = server;
2270 req_data.data = (void *)&req;
2271 req_data.length = sizeof(req);
2273 rep_data.data = NULL;
2274 rep_data.length = 0;
2276 code = krb5_db_invoke(context,
2277 KRB5_KDB_METHOD_CHECK_ALLOWED_TO_DELEGATE,
2280 if (code == KRB5_KDB_DBTYPE_NOSUP) {
2281 code = KRB5KDC_ERR_POLICY;
2284 assert(rep_data.length == 0);
2290 kdc_process_s4u2proxy_req(krb5_context context,
2291 krb5_kdc_req *request,
2292 const krb5_enc_tkt_part *t2enc,
2293 const krb5_db_entry *server,
2294 krb5_const_principal server_princ,
2295 krb5_const_principal proxy_princ,
2296 const char **status)
2298 krb5_error_code errcode;
2301 * Constrained delegation is mutually exclusive with renew/forward/etc.
2302 * We can assert from this check that the header ticket was a TGT, as
2303 * that is validated previously in validate_tgs_request().
2305 if (request->kdc_options & (NO_TGT_OPTION | KDC_OPT_ENC_TKT_IN_SKEY)) {
2306 return KRB5KDC_ERR_BADOPTION;
2309 /* Ensure that evidence ticket server matches TGT client */
2310 if (!krb5_principal_compare(kdc_context,
2311 server->princ, /* after canon */
2313 return KRB5KDC_ERR_SERVER_NOMATCH;
2316 if (!isflagset(t2enc->flags, TKT_FLG_FORWARDABLE)) {
2317 *status = "EVIDENCE_TKT_NOT_FORWARDABLE";
2318 return KRB5_TKT_NOT_FORWARDABLE;
2321 /* Backend policy check */
2322 errcode = check_allowed_to_delegate_to(kdc_context,
2323 server, proxy_princ);
2325 *status = "NOT_ALLOWED_TO_DELEGATE";
2333 kdc_check_transited_list(krb5_context context,
2334 const krb5_data *trans,
2335 const krb5_data *realm1,
2336 const krb5_data *realm2)
2338 krb5_error_code code;
2339 kdb_check_transited_realms_req req;
2343 /* First check using krb5.conf */
2344 code = krb5_check_transited_list(kdc_context, trans, realm1, realm2);
2348 memset(&req, 0, sizeof(req));
2350 req.tr_contents = trans;
2351 req.client_realm = realm1;
2352 req.server_realm = realm2;
2354 req_data.data = (void *)&req;
2355 req_data.length = sizeof(req);
2357 rep_data.data = NULL;
2358 rep_data.length = 0;
2360 code = krb5_db_invoke(context,
2361 KRB5_KDB_METHOD_CHECK_TRANSITED_REALMS,
2364 if (code == KRB5_KDB_DBTYPE_NOSUP) {
2368 assert(rep_data.length == 0);
2374 validate_transit_path(krb5_context context,
2375 krb5_const_principal client,
2376 krb5_db_entry *server,
2377 krb5_db_entry *krbtgt)
2380 if (isflagset(server->attributes, KRB5_KDB_XREALM_NON_TRANSITIVE)) {
2381 return KRB5KDC_ERR_PATH_NOT_ACCEPTED;
2385 if (isflagset(krbtgt->attributes, KRB5_KDB_XREALM_NON_TRANSITIVE) &&
2386 (!krb5_principal_compare(context, server->princ, krbtgt->princ) ||
2387 !krb5_realm_compare(context, client, krbtgt->princ))) {
2388 return KRB5KDC_ERR_PATH_NOT_ACCEPTED;
2395 /* Main logging routines for ticket requests.
2397 There are a few simple cases -- unparseable requests mainly --
2398 where messages are logged otherwise, but once a ticket request can
2399 be decoded in some basic way, these routines are used for logging
2402 /* "status" is null to indicate success. */
2403 /* Someday, pass local address/port as well. */
2404 /* Currently no info about name canonicalization is logged. */
2406 log_as_req(const krb5_fulladdr *from,
2407 krb5_kdc_req *request, krb5_kdc_rep *reply,
2408 krb5_db_entry *client, const char *cname,
2409 krb5_db_entry *server, const char *sname,
2410 krb5_timestamp authtime,
2411 const char *status, krb5_error_code errcode, const char *emsg)
2413 const char *fromstring = 0;
2414 char fromstringbuf[70];
2416 const char *cname2 = cname ? cname : "<unknown client>";
2417 const char *sname2 = sname ? sname : "<unknown server>";
2419 fromstring = inet_ntop(ADDRTYPE2FAMILY (from->address->addrtype),
2420 from->address->contents,
2421 fromstringbuf, sizeof(fromstringbuf));
2423 fromstring = "<unknown>";
2424 ktypes2str(ktypestr, sizeof(ktypestr),
2425 request->nktypes, request->ktype);
2427 if (status == NULL) {
2429 char rep_etypestr[128];
2430 rep_etypes2str(rep_etypestr, sizeof(rep_etypestr), reply);
2431 krb5_klog_syslog(LOG_INFO,
2432 "AS_REQ (%s) %s: ISSUE: authtime %d, %s, %s for %s",
2433 ktypestr, fromstring, authtime,
2434 rep_etypestr, cname2, sname2);
2437 krb5_klog_syslog(LOG_INFO, "AS_REQ (%s) %s: %s: %s for %s%s%s",
2438 ktypestr, fromstring, status,
2439 cname2, sname2, emsg ? ", " : "", emsg ? emsg : "");
2442 /* Sun (OpenSolaris) version would probably something like this.
2443 The client and server names passed can be null, unlike in the
2444 logging routines used above. Note that a struct in_addr is
2445 used, but the real address could be an IPv6 address. */
2446 audit_krb5kdc_as_req(some in_addr *, (in_port_t)from->port, 0,
2447 cname, sname, errcode);
2451 kdb_audit_as_req req;
2455 memset(&req, 0, sizeof(req));
2457 req.request = request;
2458 req.client = client;
2459 req.server = server;
2460 req.authtime = authtime;
2461 req.error_code = errcode;
2463 req_data.data = (void *)&req;
2464 req_data.length = sizeof(req);
2466 rep_data.data = NULL;
2467 rep_data.length = 0;
2469 (void) krb5_db_invoke(kdc_context,
2470 KRB5_KDB_METHOD_AUDIT_AS,
2473 assert(rep_data.length == 0);
2478 /* Here "status" must be non-null. Error code
2479 KRB5KDC_ERR_SERVER_NOMATCH is handled specially.
2481 Currently no info about name canonicalization is logged. */
2483 log_tgs_req(const krb5_fulladdr *from,
2484 krb5_kdc_req *request, krb5_kdc_rep *reply,
2485 const char *cname, const char *sname, const char *altcname,
2486 krb5_timestamp authtime,
2487 unsigned int c_flags, const char *s4u_name,
2488 const char *status, krb5_error_code errcode, const char *emsg)
2491 const char *fromstring = 0;
2492 char fromstringbuf[70];
2493 char rep_etypestr[128];
2495 fromstring = inet_ntop(ADDRTYPE2FAMILY(from->address->addrtype),
2496 from->address->contents,
2497 fromstringbuf, sizeof(fromstringbuf));
2499 fromstring = "<unknown>";
2500 ktypes2str(ktypestr, sizeof(ktypestr), request->nktypes, request->ktype);
2502 rep_etypes2str(rep_etypestr, sizeof(rep_etypestr), reply);
2504 rep_etypestr[0] = 0;
2506 /* Differences: server-nomatch message logs 2nd ticket's client
2507 name (useful), and doesn't log ktypestr (probably not
2509 if (errcode != KRB5KDC_ERR_SERVER_NOMATCH) {
2510 krb5_klog_syslog(LOG_INFO,
2511 "TGS_REQ (%s) %s: %s: authtime %d, %s%s %s for %s%s%s",
2513 fromstring, status, authtime,
2515 !errcode ? "," : "",
2516 cname ? cname : "<unknown client>",
2517 sname ? sname : "<unknown server>",
2518 errcode ? ", " : "",
2519 errcode ? emsg : "");
2521 assert(isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION) ||
2522 isflagset(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION));
2523 if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION))
2524 krb5_klog_syslog(LOG_INFO,
2525 "... PROTOCOL-TRANSITION s4u-client=%s",
2527 else if (isflagset(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION))
2528 krb5_klog_syslog(LOG_INFO,
2529 "... CONSTRAINED-DELEGATION s4u-client=%s",
2533 krb5_klog_syslog(LOG_INFO,
2534 "TGS_REQ %s: %s: authtime %d, %s for %s, 2nd tkt client %s",
2535 fromstring, status, authtime,
2536 cname ? cname : "<unknown client>",
2537 sname ? sname : "<unknown server>",
2538 altcname ? altcname : "<unknown>");
2540 /* OpenSolaris: audit_krb5kdc_tgs_req(...) or
2541 audit_krb5kdc_tgs_req_2ndtktmm(...) */
2542 /* ... krb5_db_invoke ... */
2546 log_tgs_alt_tgt(krb5_principal p)
2549 if (krb5_unparse_name(kdc_context, p, &sname)) {
2550 krb5_klog_syslog(LOG_INFO,
2551 "TGS_REQ: issuing alternate <un-unparseable> TGT");
2553 limit_string(sname);
2554 krb5_klog_syslog(LOG_INFO, "TGS_REQ: issuing TGT %s", sname);
2557 /* OpenSolaris: audit_krb5kdc_tgs_req_alt_tgt(...) */
2561 enctype_requires_etype_info_2(krb5_enctype enctype)
2564 case ENCTYPE_DES_CBC_CRC:
2565 case ENCTYPE_DES_CBC_MD4:
2566 case ENCTYPE_DES_CBC_MD5:
2567 case ENCTYPE_DES3_CBC_SHA1:
2568 case ENCTYPE_DES3_CBC_RAW:
2569 case ENCTYPE_ARCFOUR_HMAC:
2570 case ENCTYPE_ARCFOUR_HMAC_EXP :
2573 return krb5_c_valid_enctype(enctype);
2577 /* XXX where are the generic helper routines for this? */
2579 add_pa_data_element(krb5_context context,
2580 krb5_pa_data *padata,
2581 krb5_pa_data ***inout_padata,
2587 if (*inout_padata != NULL) {
2588 for (i = 0; (*inout_padata)[i] != NULL; i++)
2593 p = realloc(*inout_padata, (i + 2) * sizeof(krb5_pa_data *));
2599 p[i] = (krb5_pa_data *)malloc(sizeof(krb5_pa_data));
2607 p[i]->contents = (krb5_octet *)malloc(padata->length);
2608 if (p[i]->contents == NULL) {
2614 memcpy(p[i]->contents, padata->contents, padata->length);