xref: /illumos-gate/usr/src/cmd/krb5/krb5kdc/kdc_util.c (revision 55fea89d)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * kdc/kdc_util.c
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * Copyright 1990,1991 by the Massachusetts Institute of Technology.
57c478bd9Sstevel@tonic-gate  * All Rights Reserved.
67c478bd9Sstevel@tonic-gate  *
77c478bd9Sstevel@tonic-gate  * Export of this software from the United States of America may
87c478bd9Sstevel@tonic-gate  *   require a specific license from the United States Government.
97c478bd9Sstevel@tonic-gate  *   It is the responsibility of any person or organization contemplating
107c478bd9Sstevel@tonic-gate  *   export to obtain such a license before exporting.
11*55fea89dSDan Cross  *
127c478bd9Sstevel@tonic-gate  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
137c478bd9Sstevel@tonic-gate  * distribute this software and its documentation for any purpose and
147c478bd9Sstevel@tonic-gate  * without fee is hereby granted, provided that the above copyright
157c478bd9Sstevel@tonic-gate  * notice appear in all copies and that both that copyright notice and
167c478bd9Sstevel@tonic-gate  * this permission notice appear in supporting documentation, and that
177c478bd9Sstevel@tonic-gate  * the name of M.I.T. not be used in advertising or publicity pertaining
187c478bd9Sstevel@tonic-gate  * to distribution of the software without specific, written prior
197c478bd9Sstevel@tonic-gate  * permission.  Furthermore if you modify this software you must label
207c478bd9Sstevel@tonic-gate  * your software as modified software and not distribute it in such a
217c478bd9Sstevel@tonic-gate  * fashion that it might be confused with the original M.I.T. software.
227c478bd9Sstevel@tonic-gate  * M.I.T. makes no representations about the suitability of
237c478bd9Sstevel@tonic-gate  * this software for any purpose.  It is provided "as is" without express
247c478bd9Sstevel@tonic-gate  * or implied warranty.
25*55fea89dSDan Cross  *
267c478bd9Sstevel@tonic-gate  *
277c478bd9Sstevel@tonic-gate  * Utility functions for the KDC implementation.
287c478bd9Sstevel@tonic-gate  */
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate 
317c478bd9Sstevel@tonic-gate 
327c478bd9Sstevel@tonic-gate #include "k5-int.h"
337c478bd9Sstevel@tonic-gate #include "kdc_util.h"
347c478bd9Sstevel@tonic-gate #include "extern.h"
357c478bd9Sstevel@tonic-gate #include <stdio.h>
3656a424ccSmp #include <ctype.h>
377c478bd9Sstevel@tonic-gate #include <syslog.h>
387c478bd9Sstevel@tonic-gate #include "adm.h"
397c478bd9Sstevel@tonic-gate #include "adm_proto.h"
4056a424ccSmp #include <limits.h>
417c478bd9Sstevel@tonic-gate 
427c478bd9Sstevel@tonic-gate #ifdef USE_RCACHE
437c478bd9Sstevel@tonic-gate static char *kdc_current_rcname = (char *) NULL;
447c478bd9Sstevel@tonic-gate krb5_deltat rc_lifetime; /* See kdc_initialize_rcache() */
457c478bd9Sstevel@tonic-gate #endif
467c478bd9Sstevel@tonic-gate 
477c478bd9Sstevel@tonic-gate #ifdef USE_RCACHE
487c478bd9Sstevel@tonic-gate /*
497c478bd9Sstevel@tonic-gate  * initialize the replay cache.
507c478bd9Sstevel@tonic-gate  */
517c478bd9Sstevel@tonic-gate krb5_error_code
kdc_initialize_rcache(krb5_context kcontext,char * rcache_name)5256a424ccSmp kdc_initialize_rcache(krb5_context kcontext, char *rcache_name)
537c478bd9Sstevel@tonic-gate {
547c478bd9Sstevel@tonic-gate     krb5_error_code	retval;
557c478bd9Sstevel@tonic-gate     char		*rcname;
567c478bd9Sstevel@tonic-gate     char		*sname;
577c478bd9Sstevel@tonic-gate 
587c478bd9Sstevel@tonic-gate     rcname = (rcache_name) ? rcache_name : kdc_current_rcname;
597c478bd9Sstevel@tonic-gate 
607c478bd9Sstevel@tonic-gate     /* rc_lifetime used elsewhere to verify we're not */
617c478bd9Sstevel@tonic-gate     /*  replaying really old data                     */
627c478bd9Sstevel@tonic-gate     rc_lifetime = kcontext->clockskew;
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate     if (!rcname)
657c478bd9Sstevel@tonic-gate 	rcname = KDCRCACHE;
667c478bd9Sstevel@tonic-gate     if (!(retval = krb5_rc_resolve_full(kcontext, &kdc_rcache, rcname))) {
677c478bd9Sstevel@tonic-gate 	/* Recover or initialize the replay cache */
687c478bd9Sstevel@tonic-gate 	if (!(retval = krb5_rc_recover(kcontext, kdc_rcache)) ||
697c478bd9Sstevel@tonic-gate 	    !(retval = krb5_rc_initialize(kcontext,
707c478bd9Sstevel@tonic-gate 					  kdc_rcache,
717c478bd9Sstevel@tonic-gate 					  kcontext->clockskew))
727c478bd9Sstevel@tonic-gate 	    ) {
737c478bd9Sstevel@tonic-gate 	    /* Expunge the replay cache */
747c478bd9Sstevel@tonic-gate 	    if (!(retval = krb5_rc_expunge(kcontext, kdc_rcache))) {
757c478bd9Sstevel@tonic-gate 		sname = kdc_current_rcname;
767c478bd9Sstevel@tonic-gate 		kdc_current_rcname = strdup(rcname);
777c478bd9Sstevel@tonic-gate 		if (sname)
787c478bd9Sstevel@tonic-gate 		    free(sname);
797c478bd9Sstevel@tonic-gate 	    }
807c478bd9Sstevel@tonic-gate 	}
817c478bd9Sstevel@tonic-gate 	if (retval)
827c478bd9Sstevel@tonic-gate 	    krb5_rc_close(kcontext, kdc_rcache);
837c478bd9Sstevel@tonic-gate     }
847c478bd9Sstevel@tonic-gate     return(retval);
857c478bd9Sstevel@tonic-gate }
867c478bd9Sstevel@tonic-gate #endif
877c478bd9Sstevel@tonic-gate 
887c478bd9Sstevel@tonic-gate /*
897c478bd9Sstevel@tonic-gate  * concatenate first two authdata arrays, returning an allocated replacement.
907c478bd9Sstevel@tonic-gate  * The replacement should be freed with krb5_free_authdata().
917c478bd9Sstevel@tonic-gate  */
927c478bd9Sstevel@tonic-gate krb5_error_code
concat_authorization_data(krb5_authdata ** first,krb5_authdata ** second,krb5_authdata *** output)9356a424ccSmp concat_authorization_data(krb5_authdata **first, krb5_authdata **second,
9456a424ccSmp 			  krb5_authdata ***output)
957c478bd9Sstevel@tonic-gate {
967c478bd9Sstevel@tonic-gate     register int i, j;
977c478bd9Sstevel@tonic-gate     register krb5_authdata **ptr, **retdata;
987c478bd9Sstevel@tonic-gate 
997c478bd9Sstevel@tonic-gate     /* count up the entries */
1007c478bd9Sstevel@tonic-gate     i = 0;
1017c478bd9Sstevel@tonic-gate     if (first)
1027c478bd9Sstevel@tonic-gate 	for (ptr = first; *ptr; ptr++)
1037c478bd9Sstevel@tonic-gate 	    i++;
1047c478bd9Sstevel@tonic-gate     if (second)
1057c478bd9Sstevel@tonic-gate 	for (ptr = second; *ptr; ptr++)
1067c478bd9Sstevel@tonic-gate 	    i++;
107*55fea89dSDan Cross 
1087c478bd9Sstevel@tonic-gate     retdata = (krb5_authdata **)malloc((i+1)*sizeof(*retdata));
1097c478bd9Sstevel@tonic-gate     if (!retdata)
1107c478bd9Sstevel@tonic-gate 	return ENOMEM;
1117c478bd9Sstevel@tonic-gate     retdata[i] = 0;			/* null-terminated array */
1127c478bd9Sstevel@tonic-gate     for (i = 0, j = 0, ptr = first; j < 2 ; ptr = second, j++)
1137c478bd9Sstevel@tonic-gate 	while (ptr && *ptr) {
1147c478bd9Sstevel@tonic-gate 	    /* now walk & copy */
1157c478bd9Sstevel@tonic-gate 	    retdata[i] = (krb5_authdata *)malloc(sizeof(*retdata[i]));
1167c478bd9Sstevel@tonic-gate 	    if (!retdata[i]) {
1177c478bd9Sstevel@tonic-gate 		krb5_free_authdata(kdc_context, retdata);
1187c478bd9Sstevel@tonic-gate 		return ENOMEM;
1197c478bd9Sstevel@tonic-gate 	    }
1207c478bd9Sstevel@tonic-gate 	    *retdata[i] = **ptr;
1217c478bd9Sstevel@tonic-gate 	    if (!(retdata[i]->contents =
1227c478bd9Sstevel@tonic-gate 		  (krb5_octet *)malloc(retdata[i]->length))) {
1237c478bd9Sstevel@tonic-gate 		free((char *)retdata[i]);
1247c478bd9Sstevel@tonic-gate 		retdata[i] = 0;
1257c478bd9Sstevel@tonic-gate 		krb5_free_authdata(kdc_context, retdata);
1267c478bd9Sstevel@tonic-gate 		return ENOMEM;
1277c478bd9Sstevel@tonic-gate 	    }
1287c478bd9Sstevel@tonic-gate 	    memcpy((char *) retdata[i]->contents,
1297c478bd9Sstevel@tonic-gate 		   (char *)(*ptr)->contents,
1307c478bd9Sstevel@tonic-gate 		   retdata[i]->length);
1317c478bd9Sstevel@tonic-gate 
1327c478bd9Sstevel@tonic-gate 	    ptr++;
1337c478bd9Sstevel@tonic-gate 	    i++;
1347c478bd9Sstevel@tonic-gate 	}
1357c478bd9Sstevel@tonic-gate     *output = retdata;
1367c478bd9Sstevel@tonic-gate     return 0;
1377c478bd9Sstevel@tonic-gate }
1387c478bd9Sstevel@tonic-gate 
1397c478bd9Sstevel@tonic-gate krb5_boolean
realm_compare(krb5_principal princ1,krb5_principal princ2)14056a424ccSmp realm_compare(krb5_principal princ1, krb5_principal princ2)
1417c478bd9Sstevel@tonic-gate {
1427c478bd9Sstevel@tonic-gate   krb5_data *realm1 = krb5_princ_realm(kdc_context, princ1);
1437c478bd9Sstevel@tonic-gate   krb5_data *realm2 = krb5_princ_realm(kdc_context, princ2);
1447c478bd9Sstevel@tonic-gate 
1457c478bd9Sstevel@tonic-gate   return((realm1->length == realm2->length) &&
1467c478bd9Sstevel@tonic-gate          !memcmp(realm1->data, realm2->data, realm1->length));
1477c478bd9Sstevel@tonic-gate }
1487c478bd9Sstevel@tonic-gate 
1497c478bd9Sstevel@tonic-gate /*
1507c478bd9Sstevel@tonic-gate  * Returns TRUE if the kerberos principal is the name of a Kerberos ticket
1517c478bd9Sstevel@tonic-gate  * service.
1527c478bd9Sstevel@tonic-gate  */
krb5_is_tgs_principal(krb5_principal principal)15356a424ccSmp krb5_boolean krb5_is_tgs_principal(krb5_principal principal)
1547c478bd9Sstevel@tonic-gate {
15556a424ccSmp 	if ((krb5_princ_size(kdc_context, principal) > 0) &&
1567c478bd9Sstevel@tonic-gate 	    (krb5_princ_component(kdc_context, principal, 0)->length ==
1577c478bd9Sstevel@tonic-gate 	     KRB5_TGS_NAME_SIZE) &&
1587c478bd9Sstevel@tonic-gate 	    (!memcmp(krb5_princ_component(kdc_context, principal, 0)->data,
1597c478bd9Sstevel@tonic-gate 		     KRB5_TGS_NAME, KRB5_TGS_NAME_SIZE)))
1607c478bd9Sstevel@tonic-gate 		return TRUE;
1617c478bd9Sstevel@tonic-gate 	return FALSE;
1627c478bd9Sstevel@tonic-gate }
1637c478bd9Sstevel@tonic-gate 
1647c478bd9Sstevel@tonic-gate /*
1657c478bd9Sstevel@tonic-gate  * given authentication data (provides seed for checksum), verify checksum
1667c478bd9Sstevel@tonic-gate  * for source data.
1677c478bd9Sstevel@tonic-gate  */
1687c478bd9Sstevel@tonic-gate static krb5_error_code
comp_cksum(krb5_context kcontext,krb5_data * source,krb5_ticket * ticket,krb5_checksum * his_cksum)16956a424ccSmp comp_cksum(krb5_context kcontext, krb5_data *source, krb5_ticket *ticket,
17056a424ccSmp 	   krb5_checksum *his_cksum)
1717c478bd9Sstevel@tonic-gate {
1727c478bd9Sstevel@tonic-gate     krb5_error_code 	  retval;
1737c478bd9Sstevel@tonic-gate     krb5_boolean	  valid;
1747c478bd9Sstevel@tonic-gate 
175*55fea89dSDan Cross     if (!krb5_c_valid_cksumtype(his_cksum->checksum_type))
1767c478bd9Sstevel@tonic-gate 	return KRB5KDC_ERR_SUMTYPE_NOSUPP;
1777c478bd9Sstevel@tonic-gate 
1787c478bd9Sstevel@tonic-gate     /* must be collision proof */
179505d05c7Sgtb     if (!krb5_c_is_coll_proof_cksum(his_cksum->checksum_type))
1807c478bd9Sstevel@tonic-gate 	return KRB5KRB_AP_ERR_INAPP_CKSUM;
1817c478bd9Sstevel@tonic-gate 
1827c478bd9Sstevel@tonic-gate     /* verify checksum */
1837c478bd9Sstevel@tonic-gate     if ((retval = krb5_c_verify_checksum(kcontext, ticket->enc_part2->session,
1847c478bd9Sstevel@tonic-gate 					 KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
1857c478bd9Sstevel@tonic-gate 					 source, his_cksum, &valid)))
1867c478bd9Sstevel@tonic-gate 	return(retval);
1877c478bd9Sstevel@tonic-gate 
1887c478bd9Sstevel@tonic-gate     if (!valid)
1897c478bd9Sstevel@tonic-gate 	return(KRB5KRB_AP_ERR_BAD_INTEGRITY);
1907c478bd9Sstevel@tonic-gate 
1917c478bd9Sstevel@tonic-gate     return(0);
1927c478bd9Sstevel@tonic-gate }
1937c478bd9Sstevel@tonic-gate 
194*55fea89dSDan Cross krb5_error_code
kdc_process_tgs_req(krb5_kdc_req * request,const krb5_fulladdr * from,krb5_data * pkt,krb5_ticket ** ticket,krb5_keyblock ** subkey)19556a424ccSmp kdc_process_tgs_req(krb5_kdc_req *request, const krb5_fulladdr *from,
19656a424ccSmp 		    krb5_data *pkt, krb5_ticket **ticket,
19756a424ccSmp 		    krb5_keyblock **subkey)
1987c478bd9Sstevel@tonic-gate {
1997c478bd9Sstevel@tonic-gate     krb5_pa_data       ** tmppa;
2007c478bd9Sstevel@tonic-gate     krb5_ap_req 	* apreq;
2017c478bd9Sstevel@tonic-gate     krb5_error_code 	  retval;
2027c478bd9Sstevel@tonic-gate     krb5_data		  scratch1;
2037c478bd9Sstevel@tonic-gate     krb5_data 		* scratch = NULL;
2047c478bd9Sstevel@tonic-gate     krb5_boolean 	  foreign_server = FALSE;
2057c478bd9Sstevel@tonic-gate     krb5_auth_context 	  auth_context = NULL;
2067c478bd9Sstevel@tonic-gate     krb5_authenticator	* authenticator = NULL;
2077c478bd9Sstevel@tonic-gate     krb5_checksum 	* his_cksum = NULL;
20856a424ccSmp /*    krb5_keyblock 	* key = NULL;*/
20956a424ccSmp /*    krb5_kvno 		  kvno = 0;*/
2107c478bd9Sstevel@tonic-gate 
2117c478bd9Sstevel@tonic-gate     if (!request->padata)
2127c478bd9Sstevel@tonic-gate 	return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
2137c478bd9Sstevel@tonic-gate     for (tmppa = request->padata; *tmppa; tmppa++) {
2147c478bd9Sstevel@tonic-gate 	if ((*tmppa)->pa_type == KRB5_PADATA_AP_REQ)
2157c478bd9Sstevel@tonic-gate 	    break;
2167c478bd9Sstevel@tonic-gate     }
2177c478bd9Sstevel@tonic-gate     if (!*tmppa)			/* cannot find any AP_REQ */
2187c478bd9Sstevel@tonic-gate 	return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
2197c478bd9Sstevel@tonic-gate 
2207c478bd9Sstevel@tonic-gate     scratch1.length = (*tmppa)->length;
2217c478bd9Sstevel@tonic-gate     scratch1.data = (char *)(*tmppa)->contents;
2227c478bd9Sstevel@tonic-gate     if ((retval = decode_krb5_ap_req(&scratch1, &apreq)))
2237c478bd9Sstevel@tonic-gate 	return retval;
224*55fea89dSDan Cross 
2257c478bd9Sstevel@tonic-gate     if (isflagset(apreq->ap_options, AP_OPTS_USE_SESSION_KEY) ||
2267c478bd9Sstevel@tonic-gate 	isflagset(apreq->ap_options, AP_OPTS_MUTUAL_REQUIRED)) {
2277c478bd9Sstevel@tonic-gate 	krb5_klog_syslog(LOG_INFO, "TGS_REQ: SESSION KEY or MUTUAL");
2287c478bd9Sstevel@tonic-gate 	retval = KRB5KDC_ERR_POLICY;
2297c478bd9Sstevel@tonic-gate 	goto cleanup;
2307c478bd9Sstevel@tonic-gate     }
2317c478bd9Sstevel@tonic-gate 
2327c478bd9Sstevel@tonic-gate     /* If the "server" principal in the ticket is not something
2337c478bd9Sstevel@tonic-gate        in the local realm, then we must refuse to service the request
2347c478bd9Sstevel@tonic-gate        if the client claims to be from the local realm.
235*55fea89dSDan Cross 
2367c478bd9Sstevel@tonic-gate        If we don't do this, then some other realm's nasty KDC can
2377c478bd9Sstevel@tonic-gate        claim to be authenticating a client from our realm, and we'll
2387c478bd9Sstevel@tonic-gate        give out tickets concurring with it!
239*55fea89dSDan Cross 
2407c478bd9Sstevel@tonic-gate        we set a flag here for checking below.
2417c478bd9Sstevel@tonic-gate        */
2427c478bd9Sstevel@tonic-gate     if ((krb5_princ_realm(kdc_context, apreq->ticket->server)->length !=
2437c478bd9Sstevel@tonic-gate 	 krb5_princ_realm(kdc_context, tgs_server)->length) ||
2447c478bd9Sstevel@tonic-gate 	memcmp(krb5_princ_realm(kdc_context, apreq->ticket->server)->data,
2457c478bd9Sstevel@tonic-gate 	       krb5_princ_realm(kdc_context, tgs_server)->data,
2467c478bd9Sstevel@tonic-gate 	       krb5_princ_realm(kdc_context, tgs_server)->length))
2477c478bd9Sstevel@tonic-gate 	foreign_server = TRUE;
2487c478bd9Sstevel@tonic-gate 
2497c478bd9Sstevel@tonic-gate     if ((retval = krb5_auth_con_init(kdc_context, &auth_context)))
2507c478bd9Sstevel@tonic-gate 	goto cleanup;
2517c478bd9Sstevel@tonic-gate 
2527c478bd9Sstevel@tonic-gate     if ((retval = krb5_auth_con_setaddrs(kdc_context, auth_context, NULL,
2537c478bd9Sstevel@tonic-gate 					 from->address)) )
2547c478bd9Sstevel@tonic-gate 	goto cleanup_auth_context;
2557c478bd9Sstevel@tonic-gate #ifdef USE_RCACHE
2567c478bd9Sstevel@tonic-gate     if ((retval = krb5_auth_con_setrcache(kdc_context, auth_context,
2577c478bd9Sstevel@tonic-gate 					  kdc_rcache)))
2587c478bd9Sstevel@tonic-gate 	goto cleanup_auth_context;
2597c478bd9Sstevel@tonic-gate #endif
2607c478bd9Sstevel@tonic-gate 
2617c478bd9Sstevel@tonic-gate /*
2627c478bd9Sstevel@tonic-gate     if ((retval = kdc_get_server_key(apreq->ticket, &key, &kvno)))
2637c478bd9Sstevel@tonic-gate 	goto cleanup_auth_context;
2647c478bd9Sstevel@tonic-gate */
2657c478bd9Sstevel@tonic-gate 
2667c478bd9Sstevel@tonic-gate     /*
267*55fea89dSDan Cross      * XXX This is currently wrong but to fix it will require making a
2687c478bd9Sstevel@tonic-gate      * new keytab for groveling over the kdb.
2697c478bd9Sstevel@tonic-gate      */
2707c478bd9Sstevel@tonic-gate /*
2717c478bd9Sstevel@tonic-gate     retval = krb5_auth_con_setuseruserkey(kdc_context, auth_context, key);
2727c478bd9Sstevel@tonic-gate     krb5_free_keyblock(kdc_context, key);
273*55fea89dSDan Cross     if (retval)
2747c478bd9Sstevel@tonic-gate 	goto cleanup_auth_context;
2757c478bd9Sstevel@tonic-gate */
2767c478bd9Sstevel@tonic-gate 
277*55fea89dSDan Cross     if ((retval = krb5_rd_req_decoded_anyflag(kdc_context, &auth_context, apreq,
278*55fea89dSDan Cross 				      apreq->ticket->server,
2797c478bd9Sstevel@tonic-gate 				      kdc_active_realm->realm_keytab,
2807c478bd9Sstevel@tonic-gate 				      NULL, ticket))) {
2817c478bd9Sstevel@tonic-gate #ifdef USE_RCACHE
2827c478bd9Sstevel@tonic-gate 	/*
2837c478bd9Sstevel@tonic-gate 	 * I'm not so sure that this is right, but it's better than nothing
2847c478bd9Sstevel@tonic-gate 	 * at all.
2857c478bd9Sstevel@tonic-gate 	 *
2867c478bd9Sstevel@tonic-gate 	 * If we choke in the rd_req because of the replay cache, then attempt
2877c478bd9Sstevel@tonic-gate 	 * to reinitialize the replay cache because somebody could have deleted
2887c478bd9Sstevel@tonic-gate 	 * it from underneath us (e.g. a cron job)
2897c478bd9Sstevel@tonic-gate 	 */
2907c478bd9Sstevel@tonic-gate 	if ((retval == KRB5_RC_IO_IO) ||
2917c478bd9Sstevel@tonic-gate 	    (retval == KRB5_RC_IO_UNKNOWN)) {
2927c478bd9Sstevel@tonic-gate 	    (void) krb5_rc_close(kdc_context, kdc_rcache);
2937c478bd9Sstevel@tonic-gate 	    kdc_rcache = (krb5_rcache) NULL;
2947c478bd9Sstevel@tonic-gate 	    if (!(retval = kdc_initialize_rcache(kdc_context, (char *) NULL))) {
2957c478bd9Sstevel@tonic-gate 		if ((retval = krb5_auth_con_setrcache(kdc_context, auth_context,
2967c478bd9Sstevel@tonic-gate 						      kdc_rcache)) ||
2977c478bd9Sstevel@tonic-gate 		    (retval = krb5_rd_req_decoded_anyflag(kdc_context, &auth_context,
2987c478bd9Sstevel@tonic-gate 						  apreq, apreq->ticket->server,
2997c478bd9Sstevel@tonic-gate 				      		 kdc_active_realm->realm_keytab,
3007c478bd9Sstevel@tonic-gate 						  NULL, ticket))
3017c478bd9Sstevel@tonic-gate 		    )
3027c478bd9Sstevel@tonic-gate 		    goto cleanup_auth_context;
3037c478bd9Sstevel@tonic-gate 	    }
3047c478bd9Sstevel@tonic-gate 	} else
305*55fea89dSDan Cross 	    goto cleanup_auth_context;
3067c478bd9Sstevel@tonic-gate #else
307*55fea89dSDan Cross 	goto cleanup_auth_context;
3087c478bd9Sstevel@tonic-gate #endif
3097c478bd9Sstevel@tonic-gate     }
3107c478bd9Sstevel@tonic-gate 
3117c478bd9Sstevel@tonic-gate     /* "invalid flag" tickets can must be used to validate */
3127c478bd9Sstevel@tonic-gate     if (isflagset((*ticket)->enc_part2->flags, TKT_FLG_INVALID)
3137c478bd9Sstevel@tonic-gate 	&& !isflagset(request->kdc_options, KDC_OPT_VALIDATE)) {
3147c478bd9Sstevel@tonic-gate         retval = KRB5KRB_AP_ERR_TKT_INVALID;
3157c478bd9Sstevel@tonic-gate 	goto cleanup_auth_context;
3167c478bd9Sstevel@tonic-gate     }
3177c478bd9Sstevel@tonic-gate 
31856a424ccSmp     if ((retval = krb5_auth_con_getrecvsubkey(kdc_context,
31956a424ccSmp 					      auth_context, subkey)))
3207c478bd9Sstevel@tonic-gate 	goto cleanup_auth_context;
3217c478bd9Sstevel@tonic-gate 
3227c478bd9Sstevel@tonic-gate     if ((retval = krb5_auth_con_getauthenticator(kdc_context, auth_context,
3237c478bd9Sstevel@tonic-gate 						 &authenticator)))
3247c478bd9Sstevel@tonic-gate 	goto cleanup_auth_context;
3257c478bd9Sstevel@tonic-gate 
3267c478bd9Sstevel@tonic-gate     /* Check for a checksum */
3277c478bd9Sstevel@tonic-gate     if (!(his_cksum = authenticator->checksum)) {
328*55fea89dSDan Cross 	retval = KRB5KRB_AP_ERR_INAPP_CKSUM;
3297c478bd9Sstevel@tonic-gate 	goto cleanup_authenticator;
3307c478bd9Sstevel@tonic-gate     }
3317c478bd9Sstevel@tonic-gate 
3327c478bd9Sstevel@tonic-gate     /* make sure the client is of proper lineage (see above) */
3337c478bd9Sstevel@tonic-gate     if (foreign_server) {
334*55fea89dSDan Cross 	krb5_data *tkt_realm = krb5_princ_realm(kdc_context,
3357c478bd9Sstevel@tonic-gate 						(*ticket)->enc_part2->client);
3367c478bd9Sstevel@tonic-gate 	krb5_data *tgs_realm = krb5_princ_realm(kdc_context, tgs_server);
3377c478bd9Sstevel@tonic-gate 	if (tkt_realm->length == tgs_realm->length &&
3387c478bd9Sstevel@tonic-gate 	    !memcmp(tkt_realm->data, tgs_realm->data, tgs_realm->length)) {
3397c478bd9Sstevel@tonic-gate 	    /* someone in a foreign realm claiming to be local */
3407c478bd9Sstevel@tonic-gate 	    krb5_klog_syslog(LOG_INFO, "PROCESS_TGS: failed lineage check");
3417c478bd9Sstevel@tonic-gate 	    retval = KRB5KDC_ERR_POLICY;
3427c478bd9Sstevel@tonic-gate 	    goto cleanup_authenticator;
3437c478bd9Sstevel@tonic-gate 	}
3447c478bd9Sstevel@tonic-gate     }
3457c478bd9Sstevel@tonic-gate 
3467c478bd9Sstevel@tonic-gate     /*
3477c478bd9Sstevel@tonic-gate      * Check application checksum vs. tgs request
348*55fea89dSDan Cross      *
3497c478bd9Sstevel@tonic-gate      * We try checksumming the req-body two different ways: first we
3507c478bd9Sstevel@tonic-gate      * try reaching into the raw asn.1 stream (if available), and
3517c478bd9Sstevel@tonic-gate      * checksum that directly; if that fails, then we try encoding
3527c478bd9Sstevel@tonic-gate      * using our local asn.1 library.
3537c478bd9Sstevel@tonic-gate      */
3547c478bd9Sstevel@tonic-gate     if (pkt && (fetch_asn1_field((unsigned char *) pkt->data,
3557c478bd9Sstevel@tonic-gate 				 1, 4, &scratch1) >= 0)) {
3567c478bd9Sstevel@tonic-gate 	if (comp_cksum(kdc_context, &scratch1, *ticket, his_cksum)) {
357*55fea89dSDan Cross 	    if (!(retval = encode_krb5_kdc_req_body(request, &scratch)))
3587c478bd9Sstevel@tonic-gate 	        retval = comp_cksum(kdc_context, scratch, *ticket, his_cksum);
3597c478bd9Sstevel@tonic-gate 	    krb5_free_data(kdc_context, scratch);
3607c478bd9Sstevel@tonic-gate 	}
3617c478bd9Sstevel@tonic-gate     }
3627c478bd9Sstevel@tonic-gate 
3637c478bd9Sstevel@tonic-gate cleanup_authenticator:
3647c478bd9Sstevel@tonic-gate     krb5_free_authenticator(kdc_context, authenticator);
3657c478bd9Sstevel@tonic-gate 
3667c478bd9Sstevel@tonic-gate cleanup_auth_context:
3677c478bd9Sstevel@tonic-gate     /* We do not want the free of the auth_context to close the rcache */
3687c478bd9Sstevel@tonic-gate #ifdef USE_RCACHE
3697c478bd9Sstevel@tonic-gate     (void)  krb5_auth_con_setrcache(kdc_context, auth_context, 0);
3707c478bd9Sstevel@tonic-gate #endif
3717c478bd9Sstevel@tonic-gate     krb5_auth_con_free(kdc_context, auth_context);
3727c478bd9Sstevel@tonic-gate 
3737c478bd9Sstevel@tonic-gate cleanup:
3747c478bd9Sstevel@tonic-gate     krb5_free_ap_req(kdc_context, apreq);
3757c478bd9Sstevel@tonic-gate     return retval;
3767c478bd9Sstevel@tonic-gate }
3777c478bd9Sstevel@tonic-gate 
378*55fea89dSDan Cross /* XXX This function should no longer be necessary.
379*55fea89dSDan Cross  * The KDC should take the keytab associated with the realm and pass that to
3807c478bd9Sstevel@tonic-gate  * the krb5_rd_req_decode(). --proven
3817c478bd9Sstevel@tonic-gate  *
3827c478bd9Sstevel@tonic-gate  * It's actually still used by do_tgs_req() for u2u auth, and not too
3837c478bd9Sstevel@tonic-gate  * much else. -- tlyu
3847c478bd9Sstevel@tonic-gate  */
3857c478bd9Sstevel@tonic-gate krb5_error_code
kdc_get_server_key(krb5_ticket * ticket,krb5_keyblock ** key,krb5_kvno * kvno)38656a424ccSmp kdc_get_server_key(krb5_ticket *ticket, krb5_keyblock **key, krb5_kvno *kvno)
3877c478bd9Sstevel@tonic-gate {
3887c478bd9Sstevel@tonic-gate     krb5_error_code 	  retval;
3897c478bd9Sstevel@tonic-gate     krb5_db_entry 	  server;
3907c478bd9Sstevel@tonic-gate     krb5_boolean 	  more;
3917c478bd9Sstevel@tonic-gate     int	nprincs;
3927c478bd9Sstevel@tonic-gate     krb5_key_data	* server_key;
3937c478bd9Sstevel@tonic-gate 
3947c478bd9Sstevel@tonic-gate     nprincs = 1;
3957c478bd9Sstevel@tonic-gate 
3967c478bd9Sstevel@tonic-gate     if ((retval = krb5_db_get_principal(kdc_context, ticket->server,
3977c478bd9Sstevel@tonic-gate 					&server, &nprincs,
3987c478bd9Sstevel@tonic-gate 					&more))) {
3997c478bd9Sstevel@tonic-gate 	return(retval);
4007c478bd9Sstevel@tonic-gate     }
4017c478bd9Sstevel@tonic-gate     if (more) {
4027c478bd9Sstevel@tonic-gate 	krb5_db_free_principal(kdc_context, &server, nprincs);
4037c478bd9Sstevel@tonic-gate 	return(KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE);
4047c478bd9Sstevel@tonic-gate     } else if (nprincs != 1) {
4057c478bd9Sstevel@tonic-gate 	char *sname;
4067c478bd9Sstevel@tonic-gate 
4077c478bd9Sstevel@tonic-gate 	krb5_db_free_principal(kdc_context, &server, nprincs);
4087c478bd9Sstevel@tonic-gate 	if (!krb5_unparse_name(kdc_context, ticket->server, &sname)) {
40946736d35Ssemery 	    limit_string(sname);
4107c478bd9Sstevel@tonic-gate 	    krb5_klog_syslog(LOG_ERR,"TGS_REQ: UNKNOWN SERVER: server='%s'",
4117c478bd9Sstevel@tonic-gate 			     sname);
4127c478bd9Sstevel@tonic-gate 	    free(sname);
4137c478bd9Sstevel@tonic-gate 	}
4147c478bd9Sstevel@tonic-gate 	return(KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN);
4157c478bd9Sstevel@tonic-gate     }
4167c478bd9Sstevel@tonic-gate     retval = krb5_dbe_find_enctype(kdc_context, &server,
4177c478bd9Sstevel@tonic-gate 				   ticket->enc_part.enctype, -1,
4187c478bd9Sstevel@tonic-gate 				   ticket->enc_part.kvno, &server_key);
4197c478bd9Sstevel@tonic-gate     if (retval)
4207c478bd9Sstevel@tonic-gate 	goto errout;
4217c478bd9Sstevel@tonic-gate     if (!server_key) {
4227c478bd9Sstevel@tonic-gate 	retval = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
4237c478bd9Sstevel@tonic-gate 	goto errout;
4247c478bd9Sstevel@tonic-gate     }
4257c478bd9Sstevel@tonic-gate     *kvno = server_key->key_data_kvno;
4267c478bd9Sstevel@tonic-gate     if ((*key = (krb5_keyblock *)malloc(sizeof **key))) {
4277c478bd9Sstevel@tonic-gate 	retval = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock,
4287c478bd9Sstevel@tonic-gate 					     server_key,
4297c478bd9Sstevel@tonic-gate 					     *key, NULL);
4307c478bd9Sstevel@tonic-gate     } else
4317c478bd9Sstevel@tonic-gate 	retval = ENOMEM;
4327c478bd9Sstevel@tonic-gate errout:
4337c478bd9Sstevel@tonic-gate     krb5_db_free_principal(kdc_context, &server, nprincs);
4347c478bd9Sstevel@tonic-gate     return retval;
4357c478bd9Sstevel@tonic-gate }
4367c478bd9Sstevel@tonic-gate 
4377c478bd9Sstevel@tonic-gate /* This probably wants to be updated if you support last_req stuff */
4387c478bd9Sstevel@tonic-gate 
4397c478bd9Sstevel@tonic-gate static krb5_last_req_entry nolrentry = { KV5M_LAST_REQ_ENTRY, KRB5_LRQ_NONE, 0 };
4407c478bd9Sstevel@tonic-gate static krb5_last_req_entry *nolrarray[] = { &nolrentry, 0 };
4417c478bd9Sstevel@tonic-gate 
4427c478bd9Sstevel@tonic-gate krb5_error_code
fetch_last_req_info(krb5_db_entry * dbentry,krb5_last_req_entry *** lrentry)44356a424ccSmp fetch_last_req_info(krb5_db_entry *dbentry, krb5_last_req_entry ***lrentry)
4447c478bd9Sstevel@tonic-gate {
4457c478bd9Sstevel@tonic-gate     *lrentry = nolrarray;
4467c478bd9Sstevel@tonic-gate     return 0;
4477c478bd9Sstevel@tonic-gate }
4487c478bd9Sstevel@tonic-gate 
4497c478bd9Sstevel@tonic-gate 
4507c478bd9Sstevel@tonic-gate /* XXX!  This is a temporary place-holder */
4517c478bd9Sstevel@tonic-gate 
4527c478bd9Sstevel@tonic-gate krb5_error_code
check_hot_list(krb5_ticket * ticket)45356a424ccSmp check_hot_list(krb5_ticket *ticket)
4547c478bd9Sstevel@tonic-gate {
4557c478bd9Sstevel@tonic-gate     return 0;
4567c478bd9Sstevel@tonic-gate }
4577c478bd9Sstevel@tonic-gate 
4587c478bd9Sstevel@tonic-gate 
4597c478bd9Sstevel@tonic-gate #define MAX_REALM_LN 500
4607c478bd9Sstevel@tonic-gate 
4617c478bd9Sstevel@tonic-gate 
462*55fea89dSDan Cross /*
4637c478bd9Sstevel@tonic-gate  * subrealm - determine if r2 is a subrealm of r1
4647c478bd9Sstevel@tonic-gate  *
465*55fea89dSDan Cross  *            SUBREALM takes two realms, r1 and r2, and
466*55fea89dSDan Cross  *            determines if r2 is a subrealm of r1.
4677c478bd9Sstevel@tonic-gate  *            r2 is a subrealm of r1 if (r1 is a prefix
468*55fea89dSDan Cross  *            of r2 AND r1 and r2 begin with a /) or if
4697c478bd9Sstevel@tonic-gate  *            (r1 is a suffix of r2 and neither r1 nor r2
4707c478bd9Sstevel@tonic-gate  *            begin with a /).
4717c478bd9Sstevel@tonic-gate  *
4727c478bd9Sstevel@tonic-gate  * RETURNS:   If r2 is a subrealm, and r1 is a prefix, the number
4737c478bd9Sstevel@tonic-gate  *            of characters in the suffix of r2 is returned as a
4747c478bd9Sstevel@tonic-gate  *            negative number.
4757c478bd9Sstevel@tonic-gate  *
4767c478bd9Sstevel@tonic-gate  *            If r2 is a subrealm, and r1 is a suffix, the number
4777c478bd9Sstevel@tonic-gate  *            of characters in the prefix of r2 is returned as a
4787c478bd9Sstevel@tonic-gate  *            positive number.
4797c478bd9Sstevel@tonic-gate  *
4807c478bd9Sstevel@tonic-gate  *            If r2 is not a subrealm, SUBREALM returns 0.
4817c478bd9Sstevel@tonic-gate  */
4827c478bd9Sstevel@tonic-gate static  int
subrealm(char * r1,char * r2)48356a424ccSmp subrealm(char *r1, char *r2)
4847c478bd9Sstevel@tonic-gate {
48556a424ccSmp     size_t l1,l2;
4867c478bd9Sstevel@tonic-gate     l1 = strlen(r1);
4877c478bd9Sstevel@tonic-gate     l2 = strlen(r2);
4887c478bd9Sstevel@tonic-gate     if(l2 <= l1) return(0);
4897c478bd9Sstevel@tonic-gate     if((*r1 == '/') && (*r2 == '/') && (strncmp(r1,r2,l1) == 0)) return(l1-l2);
4907c478bd9Sstevel@tonic-gate     if((*r1 != '/') && (*r2 != '/') && (strncmp(r1,r2+l2-l1,l1) == 0))
4917c478bd9Sstevel@tonic-gate 	return(l2-l1);
4927c478bd9Sstevel@tonic-gate     return(0);
4937c478bd9Sstevel@tonic-gate }
4947c478bd9Sstevel@tonic-gate 
4957c478bd9Sstevel@tonic-gate /*
4967c478bd9Sstevel@tonic-gate  * add_to_transited  Adds the name of the realm which issued the
4977c478bd9Sstevel@tonic-gate  *                   ticket granting ticket on which the new ticket to
4987c478bd9Sstevel@tonic-gate  *                   be issued is based (note that this is the same as
4997c478bd9Sstevel@tonic-gate  *                   the realm of the server listed in the ticket
500*55fea89dSDan Cross  *                   granting ticket.
5017c478bd9Sstevel@tonic-gate  *
5027c478bd9Sstevel@tonic-gate  * ASSUMPTIONS:  This procedure assumes that the transited field from
5037c478bd9Sstevel@tonic-gate  *               the existing ticket granting ticket already appears
5047c478bd9Sstevel@tonic-gate  *               in compressed form.  It will add the new realm while
5057c478bd9Sstevel@tonic-gate  *               maintaining that form.   As long as each successive
5067c478bd9Sstevel@tonic-gate  *               realm is added using this (or a similar) routine, the
5077c478bd9Sstevel@tonic-gate  *               transited field will be in compressed form.  The
5087c478bd9Sstevel@tonic-gate  *               basis step is an empty transited field which is, by
5097c478bd9Sstevel@tonic-gate  *               its nature, in its most compressed form.
5107c478bd9Sstevel@tonic-gate  *
5117c478bd9Sstevel@tonic-gate  * ARGUMENTS: krb5_data *tgt_trans  Transited field from TGT
5127c478bd9Sstevel@tonic-gate  *            krb5_data *new_trans  The transited field for the new ticket
5137c478bd9Sstevel@tonic-gate  *            krb5_principal tgs    Name of ticket granting server
5147c478bd9Sstevel@tonic-gate  *                                  This includes the realm of the KDC
5157c478bd9Sstevel@tonic-gate  *                                  that issued the ticket granting
5167c478bd9Sstevel@tonic-gate  *                                  ticket.  This is the realm that is
5177c478bd9Sstevel@tonic-gate  *                                  to be added to the transited field.
5187c478bd9Sstevel@tonic-gate  *            krb5_principal client Name of the client
5197c478bd9Sstevel@tonic-gate  *            krb5_principal server The name of the requested server.
5207c478bd9Sstevel@tonic-gate  *                                  This may be the an intermediate
5217c478bd9Sstevel@tonic-gate  *                                  ticket granting server.
5227c478bd9Sstevel@tonic-gate  *
5237c478bd9Sstevel@tonic-gate  *            The last two argument are needed since they are
5247c478bd9Sstevel@tonic-gate  *            implicitly part of the transited field of the new ticket
5257c478bd9Sstevel@tonic-gate  *            even though they are not explicitly listed.
5267c478bd9Sstevel@tonic-gate  *
5277c478bd9Sstevel@tonic-gate  * RETURNS:   krb5_error_code - Success, or out of memory
5287c478bd9Sstevel@tonic-gate  *
5297c478bd9Sstevel@tonic-gate  * MODIFIES:  new_trans:  ->length will contain the length of the new
5307c478bd9Sstevel@tonic-gate  *                        transited field.
531*55fea89dSDan Cross  *
5327c478bd9Sstevel@tonic-gate  *                        If ->data was not null when this procedure
5337c478bd9Sstevel@tonic-gate  *                        is called, the memory referenced by ->data
534*55fea89dSDan Cross  *                        will be deallocated.
5357c478bd9Sstevel@tonic-gate  *
5367c478bd9Sstevel@tonic-gate  *                        Memory will be allocated for the new transited field
5377c478bd9Sstevel@tonic-gate  *                        ->data will be updated to point to the newly
538*55fea89dSDan Cross  *                        allocated memory.
5397c478bd9Sstevel@tonic-gate  *
5407c478bd9Sstevel@tonic-gate  * BUGS:  The space allocated for the new transited field is the
5417c478bd9Sstevel@tonic-gate  *        maximum that might be needed given the old transited field,
5427c478bd9Sstevel@tonic-gate  *        and the realm to be added.  This length is calculated
5437c478bd9Sstevel@tonic-gate  *        assuming that no compression of the new realm is possible.
5447c478bd9Sstevel@tonic-gate  *        This has no adverse consequences other than the allocation
545*55fea89dSDan Cross  *        of more space than required.
5467c478bd9Sstevel@tonic-gate  *
5477c478bd9Sstevel@tonic-gate  *        This procedure will not yet use the null subfield notation,
5487c478bd9Sstevel@tonic-gate  *        and it will get confused if it sees it.
5497c478bd9Sstevel@tonic-gate  *
5507c478bd9Sstevel@tonic-gate  *        This procedure does not check for quoted commas in realm
5517c478bd9Sstevel@tonic-gate  *        names.
5527c478bd9Sstevel@tonic-gate  */
5537c478bd9Sstevel@tonic-gate 
554159d09a2SMark Phalan static char *
data2string(krb5_data * d)555159d09a2SMark Phalan data2string (krb5_data *d)
556159d09a2SMark Phalan {
557159d09a2SMark Phalan     char *s;
558159d09a2SMark Phalan     s = malloc(d->length + 1);
559159d09a2SMark Phalan     if (s) {
560159d09a2SMark Phalan 	memcpy(s, d->data, d->length);
561159d09a2SMark Phalan 	s[d->length] = 0;
562159d09a2SMark Phalan     }
563159d09a2SMark Phalan     return s;
564159d09a2SMark Phalan }
565159d09a2SMark Phalan 
566*55fea89dSDan Cross krb5_error_code
add_to_transited(krb5_data * tgt_trans,krb5_data * new_trans,krb5_principal tgs,krb5_principal client,krb5_principal server)56756a424ccSmp add_to_transited(krb5_data *tgt_trans, krb5_data *new_trans,
56856a424ccSmp 		 krb5_principal tgs, krb5_principal client,
56956a424ccSmp 		 krb5_principal server)
5707c478bd9Sstevel@tonic-gate {
5717c478bd9Sstevel@tonic-gate   krb5_error_code retval;
5727c478bd9Sstevel@tonic-gate   char        *realm;
5737c478bd9Sstevel@tonic-gate   char        *trans;
5747c478bd9Sstevel@tonic-gate   char        *otrans, *otrans_ptr;
5757c478bd9Sstevel@tonic-gate 
5767c478bd9Sstevel@tonic-gate   /* The following are for stepping through the transited field     */
5777c478bd9Sstevel@tonic-gate 
5787c478bd9Sstevel@tonic-gate   char        prev[MAX_REALM_LN];
5797c478bd9Sstevel@tonic-gate   char        next[MAX_REALM_LN];
5807c478bd9Sstevel@tonic-gate   char        current[MAX_REALM_LN];
5817c478bd9Sstevel@tonic-gate   char        exp[MAX_REALM_LN];      /* Expanded current realm name     */
5827c478bd9Sstevel@tonic-gate 
5837c478bd9Sstevel@tonic-gate   int	      i;
5847c478bd9Sstevel@tonic-gate   int         clst, nlst;    /* count of last character in current and next */
5857c478bd9Sstevel@tonic-gate   int         pl, pl1;       /* prefix length                               */
5867c478bd9Sstevel@tonic-gate   int         added;         /* TRUE = new realm has been added             */
5877c478bd9Sstevel@tonic-gate 
588159d09a2SMark Phalan   realm = data2string(krb5_princ_realm(kdc_context, tgs));
589159d09a2SMark Phalan   if (realm == NULL)
590159d09a2SMark Phalan       return(ENOMEM);
5917c478bd9Sstevel@tonic-gate 
592159d09a2SMark Phalan   otrans = data2string(tgt_trans);
593159d09a2SMark Phalan   if (otrans == NULL) {
594159d09a2SMark Phalan       free(realm);
595159d09a2SMark Phalan       return(ENOMEM);
5967c478bd9Sstevel@tonic-gate   }
5977c478bd9Sstevel@tonic-gate   /* Keep track of start so we can free */
5987c478bd9Sstevel@tonic-gate   otrans_ptr = otrans;
5997c478bd9Sstevel@tonic-gate 
600*55fea89dSDan Cross   /* +1 for null,
6017c478bd9Sstevel@tonic-gate      +1 for extra comma which may be added between
6027c478bd9Sstevel@tonic-gate      +1 for potential space when leading slash in realm */
6037c478bd9Sstevel@tonic-gate   if (!(trans = (char *) malloc(strlen(realm) + strlen(otrans) + 3))) {
6047c478bd9Sstevel@tonic-gate     retval = ENOMEM;
6057c478bd9Sstevel@tonic-gate     goto fail;
6067c478bd9Sstevel@tonic-gate   }
6077c478bd9Sstevel@tonic-gate 
6087c478bd9Sstevel@tonic-gate   if (new_trans->data)  free(new_trans->data);
6097c478bd9Sstevel@tonic-gate   new_trans->data = trans;
6107c478bd9Sstevel@tonic-gate   new_trans->length = 0;
6117c478bd9Sstevel@tonic-gate 
6127c478bd9Sstevel@tonic-gate   trans[0] = '\0';
6137c478bd9Sstevel@tonic-gate 
6147c478bd9Sstevel@tonic-gate   /* For the purpose of appending, the realm preceding the first */
6157c478bd9Sstevel@tonic-gate   /* realm in the transited field is considered the null realm   */
6167c478bd9Sstevel@tonic-gate 
6177c478bd9Sstevel@tonic-gate   prev[0] = '\0';
6187c478bd9Sstevel@tonic-gate 
6197c478bd9Sstevel@tonic-gate   /* read field into current */
6207c478bd9Sstevel@tonic-gate   for (i = 0; *otrans != '\0';) {
62156a424ccSmp       if (*otrans == '\\') {
62256a424ccSmp 	  if (*(++otrans) == '\0')
62356a424ccSmp 	      break;
62456a424ccSmp 	  else
62556a424ccSmp 	      continue;
62656a424ccSmp       }
62756a424ccSmp       if (*otrans == ',') {
62856a424ccSmp 	  otrans++;
62956a424ccSmp 	  break;
63056a424ccSmp       }
63156a424ccSmp       current[i++] = *otrans++;
63256a424ccSmp       if (i >= MAX_REALM_LN) {
63356a424ccSmp 	  retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
63456a424ccSmp 	  goto fail;
63556a424ccSmp       }
6367c478bd9Sstevel@tonic-gate   }
6377c478bd9Sstevel@tonic-gate   current[i] = '\0';
6387c478bd9Sstevel@tonic-gate 
6397c478bd9Sstevel@tonic-gate   added = (krb5_princ_realm(kdc_context, client)->length == strlen(realm) &&
6407c478bd9Sstevel@tonic-gate            !strncmp(krb5_princ_realm(kdc_context, client)->data, realm, strlen(realm))) ||
6417c478bd9Sstevel@tonic-gate           (krb5_princ_realm(kdc_context, server)->length == strlen(realm) &&
6427c478bd9Sstevel@tonic-gate            !strncmp(krb5_princ_realm(kdc_context, server)->data, realm, strlen(realm)));
6437c478bd9Sstevel@tonic-gate 
6447c478bd9Sstevel@tonic-gate   while (current[0]) {
6457c478bd9Sstevel@tonic-gate 
6467c478bd9Sstevel@tonic-gate     /* figure out expanded form of current name */
6477c478bd9Sstevel@tonic-gate 
6487c478bd9Sstevel@tonic-gate     clst = strlen(current) - 1;
6497c478bd9Sstevel@tonic-gate     if (current[0] == ' ') {
6507c478bd9Sstevel@tonic-gate       strncpy(exp, current+1, sizeof(exp) - 1);
6517c478bd9Sstevel@tonic-gate       exp[sizeof(exp) - 1] = '\0';
6527c478bd9Sstevel@tonic-gate     }
6537c478bd9Sstevel@tonic-gate     else if ((current[0] == '/') && (prev[0] == '/')) {
6547c478bd9Sstevel@tonic-gate       strncpy(exp, prev, sizeof(exp) - 1);
6557c478bd9Sstevel@tonic-gate       exp[sizeof(exp) - 1] = '\0';
6567c478bd9Sstevel@tonic-gate       if (strlen(exp) + strlen(current) + 1 >= MAX_REALM_LN) {
6577c478bd9Sstevel@tonic-gate 	retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
6587c478bd9Sstevel@tonic-gate 	goto fail;
6597c478bd9Sstevel@tonic-gate       }
6607c478bd9Sstevel@tonic-gate       strncat(exp, current, sizeof(exp) - 1 - strlen(exp));
6617c478bd9Sstevel@tonic-gate     }
6627c478bd9Sstevel@tonic-gate     else if (current[clst] == '.') {
6637c478bd9Sstevel@tonic-gate       strncpy(exp, current, sizeof(exp) - 1);
6647c478bd9Sstevel@tonic-gate       exp[sizeof(exp) - 1] = '\0';
6657c478bd9Sstevel@tonic-gate       if (strlen(exp) + strlen(prev) + 1 >= MAX_REALM_LN) {
6667c478bd9Sstevel@tonic-gate 	retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
6677c478bd9Sstevel@tonic-gate 	goto fail;
6687c478bd9Sstevel@tonic-gate       }
6697c478bd9Sstevel@tonic-gate       strncat(exp, prev, sizeof(exp) - 1 - strlen(exp));
6707c478bd9Sstevel@tonic-gate     }
6717c478bd9Sstevel@tonic-gate     else {
6727c478bd9Sstevel@tonic-gate       strncpy(exp, current, sizeof(exp) - 1);
6737c478bd9Sstevel@tonic-gate       exp[sizeof(exp) - 1] = '\0';
6747c478bd9Sstevel@tonic-gate     }
6757c478bd9Sstevel@tonic-gate 
6767c478bd9Sstevel@tonic-gate     /* read field into next */
6777c478bd9Sstevel@tonic-gate     for (i = 0; *otrans != '\0';) {
67856a424ccSmp 	if (*otrans == '\\') {
67956a424ccSmp 	    if (*(++otrans) == '\0')
68056a424ccSmp 		break;
68156a424ccSmp 	    else
68256a424ccSmp 		continue;
68356a424ccSmp 	}
68456a424ccSmp 	if (*otrans == ',') {
68556a424ccSmp 	    otrans++;
68656a424ccSmp 	    break;
68756a424ccSmp 	}
68856a424ccSmp 	next[i++] = *otrans++;
68956a424ccSmp 	if (i >= MAX_REALM_LN) {
69056a424ccSmp 	    retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
69156a424ccSmp 	    goto fail;
69256a424ccSmp 	}
6937c478bd9Sstevel@tonic-gate     }
6947c478bd9Sstevel@tonic-gate     next[i] = '\0';
6957c478bd9Sstevel@tonic-gate     nlst = i - 1;
6967c478bd9Sstevel@tonic-gate 
6977c478bd9Sstevel@tonic-gate     if (!strcmp(exp, realm))  added = TRUE;
6987c478bd9Sstevel@tonic-gate 
6997c478bd9Sstevel@tonic-gate     /* If we still have to insert the new realm */
7007c478bd9Sstevel@tonic-gate 
7017c478bd9Sstevel@tonic-gate     if (!added) {
7027c478bd9Sstevel@tonic-gate 
7037c478bd9Sstevel@tonic-gate       /* Is the next field compressed?  If not, and if the new */
7047c478bd9Sstevel@tonic-gate       /* realm is a subrealm of the current realm, compress    */
7057c478bd9Sstevel@tonic-gate       /* the new realm, and insert immediately following the   */
7067c478bd9Sstevel@tonic-gate       /* current one.  Note that we can not do this if the next*/
7077c478bd9Sstevel@tonic-gate       /* field is already compressed since it would mess up    */
7087c478bd9Sstevel@tonic-gate       /* what has already been done.  In most cases, this is   */
7097c478bd9Sstevel@tonic-gate       /* not a problem because the realm to be added will be a */
7107c478bd9Sstevel@tonic-gate       /* subrealm of the next field too, and we will catch     */
7117c478bd9Sstevel@tonic-gate       /* it in a future iteration.                             */
7127c478bd9Sstevel@tonic-gate 
713159d09a2SMark Phalan 	/* Note that the second test here is an unsigned comparison,
714159d09a2SMark Phalan 	   so the first half (or a cast) is also required.  */
715159d09a2SMark Phalan       assert(nlst < 0 || nlst < sizeof(next));
716159d09a2SMark Phalan       if ((nlst < 0 || next[nlst] != '.') &&
717159d09a2SMark Phalan 	  (next[0] != '/') &&
718159d09a2SMark Phalan 	  (pl = subrealm(exp, realm))) {
7197c478bd9Sstevel@tonic-gate         added = TRUE;
7207c478bd9Sstevel@tonic-gate 	current[sizeof(current) - 1] = '\0';
7217c478bd9Sstevel@tonic-gate 	if (strlen(current) + (pl>0?pl:-pl) + 2 >= MAX_REALM_LN) {
7227c478bd9Sstevel@tonic-gate 	  retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
7237c478bd9Sstevel@tonic-gate 	  goto fail;
7247c478bd9Sstevel@tonic-gate 	}
7257c478bd9Sstevel@tonic-gate         strncat(current, ",", sizeof(current) - 1 - strlen(current));
7267c478bd9Sstevel@tonic-gate         if (pl > 0) {
72756a424ccSmp           strncat(current, realm, (unsigned) pl);
7287c478bd9Sstevel@tonic-gate         }
7297c478bd9Sstevel@tonic-gate         else {
73056a424ccSmp           strncat(current, realm+strlen(realm)+pl, (unsigned) (-pl));
7317c478bd9Sstevel@tonic-gate         }
7327c478bd9Sstevel@tonic-gate       }
7337c478bd9Sstevel@tonic-gate 
7347c478bd9Sstevel@tonic-gate       /* Whether or not the next field is compressed, if the    */
7357c478bd9Sstevel@tonic-gate       /* realm to be added is a superrealm of the current realm,*/
7367c478bd9Sstevel@tonic-gate       /* then the current realm can be compressed.  First the   */
7377c478bd9Sstevel@tonic-gate       /* realm to be added must be compressed relative to the   */
7387c478bd9Sstevel@tonic-gate       /* previous realm (if possible), and then the current     */
7397c478bd9Sstevel@tonic-gate       /* realm compressed relative to the new realm.  Note that */
7407c478bd9Sstevel@tonic-gate       /* if the realm to be added is also a superrealm of the   */
7417c478bd9Sstevel@tonic-gate       /* previous realm, it would have been added earlier, and  */
7427c478bd9Sstevel@tonic-gate       /* we would not reach this step this time around.         */
7437c478bd9Sstevel@tonic-gate 
7447c478bd9Sstevel@tonic-gate       else if ((pl = subrealm(realm, exp))) {
7457c478bd9Sstevel@tonic-gate         added      = TRUE;
7467c478bd9Sstevel@tonic-gate         current[0] = '\0';
7477c478bd9Sstevel@tonic-gate         if ((pl1 = subrealm(prev,realm))) {
7487c478bd9Sstevel@tonic-gate 	  if (strlen(current) + (pl1>0?pl1:-pl1) + 1 >= MAX_REALM_LN) {
7497c478bd9Sstevel@tonic-gate 	    retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
7507c478bd9Sstevel@tonic-gate 	    goto fail;
7517c478bd9Sstevel@tonic-gate 	  }
7527c478bd9Sstevel@tonic-gate           if (pl1 > 0) {
75356a424ccSmp             strncat(current, realm, (unsigned) pl1);
7547c478bd9Sstevel@tonic-gate           }
7557c478bd9Sstevel@tonic-gate           else {
75656a424ccSmp             strncat(current, realm+strlen(realm)+pl1, (unsigned) (-pl1));
7577c478bd9Sstevel@tonic-gate           }
7587c478bd9Sstevel@tonic-gate         }
7597c478bd9Sstevel@tonic-gate         else { /* If not a subrealm */
7607c478bd9Sstevel@tonic-gate           if ((realm[0] == '/') && prev[0]) {
7617c478bd9Sstevel@tonic-gate 	    if (strlen(current) + 2 >= MAX_REALM_LN) {
7627c478bd9Sstevel@tonic-gate 	      retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
7637c478bd9Sstevel@tonic-gate 	      goto fail;
7647c478bd9Sstevel@tonic-gate 	    }
7657c478bd9Sstevel@tonic-gate 	    strncat(current, " ", sizeof(current) - 1 - strlen(current));
7667c478bd9Sstevel@tonic-gate 	    current[sizeof(current) - 1] = '\0';
7677c478bd9Sstevel@tonic-gate           }
7687c478bd9Sstevel@tonic-gate 	  if (strlen(current) + strlen(realm) + 1 >= MAX_REALM_LN) {
7697c478bd9Sstevel@tonic-gate 	    retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
7707c478bd9Sstevel@tonic-gate 	    goto fail;
7717c478bd9Sstevel@tonic-gate 	  }
7727c478bd9Sstevel@tonic-gate           strncat(current, realm, sizeof(current) - 1 - strlen(current));
7737c478bd9Sstevel@tonic-gate 	  current[sizeof(current) - 1] = '\0';
7747c478bd9Sstevel@tonic-gate         }
7757c478bd9Sstevel@tonic-gate 	if (strlen(current) + (pl>0?pl:-pl) + 2 >= MAX_REALM_LN) {
7767c478bd9Sstevel@tonic-gate 	  retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
7777c478bd9Sstevel@tonic-gate 	  goto fail;
7787c478bd9Sstevel@tonic-gate 	}
7797c478bd9Sstevel@tonic-gate         strncat(current,",", sizeof(current) - 1 - strlen(current));
7807c478bd9Sstevel@tonic-gate 	current[sizeof(current) - 1] = '\0';
7817c478bd9Sstevel@tonic-gate         if (pl > 0) {
78256a424ccSmp           strncat(current, exp, (unsigned) pl);
7837c478bd9Sstevel@tonic-gate         }
7847c478bd9Sstevel@tonic-gate         else {
78556a424ccSmp           strncat(current, exp+strlen(exp)+pl, (unsigned)(-pl));
7867c478bd9Sstevel@tonic-gate         }
7877c478bd9Sstevel@tonic-gate       }
7887c478bd9Sstevel@tonic-gate     }
7897c478bd9Sstevel@tonic-gate 
7907c478bd9Sstevel@tonic-gate     if (new_trans->length != 0) {
7917c478bd9Sstevel@tonic-gate       if (strlen(trans) + 2 >= MAX_REALM_LN) {
7927c478bd9Sstevel@tonic-gate 	retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
7937c478bd9Sstevel@tonic-gate 	goto fail;
7947c478bd9Sstevel@tonic-gate       }
7957c478bd9Sstevel@tonic-gate       strcat(trans, ",");
7967c478bd9Sstevel@tonic-gate     }
7977c478bd9Sstevel@tonic-gate     if (strlen(trans) + strlen(current) + 1 >= MAX_REALM_LN) {
7987c478bd9Sstevel@tonic-gate       retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
7997c478bd9Sstevel@tonic-gate       goto fail;
8007c478bd9Sstevel@tonic-gate     }
8017c478bd9Sstevel@tonic-gate     strcat(trans, current);
8027c478bd9Sstevel@tonic-gate     new_trans->length = strlen(trans);
8037c478bd9Sstevel@tonic-gate 
8047c478bd9Sstevel@tonic-gate     strncpy(prev, exp, sizeof(prev) - 1);
8057c478bd9Sstevel@tonic-gate     prev[sizeof(prev) - 1] = '\0';
8067c478bd9Sstevel@tonic-gate     strncpy(current, next, sizeof(current) - 1);
8077c478bd9Sstevel@tonic-gate     current[sizeof(current) - 1] = '\0';
8087c478bd9Sstevel@tonic-gate   }
8097c478bd9Sstevel@tonic-gate 
8107c478bd9Sstevel@tonic-gate   if (!added) {
8117c478bd9Sstevel@tonic-gate     if (new_trans->length != 0) {
8127c478bd9Sstevel@tonic-gate       if (strlen(trans) + 2 >= MAX_REALM_LN) {
8137c478bd9Sstevel@tonic-gate 	retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
8147c478bd9Sstevel@tonic-gate 	goto fail;
8157c478bd9Sstevel@tonic-gate       }
8167c478bd9Sstevel@tonic-gate       strcat(trans, ",");
8177c478bd9Sstevel@tonic-gate     }
8187c478bd9Sstevel@tonic-gate     if((realm[0] == '/') && trans[0]) {
8197c478bd9Sstevel@tonic-gate       if (strlen(trans) + 2 >= MAX_REALM_LN) {
8207c478bd9Sstevel@tonic-gate 	retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
8217c478bd9Sstevel@tonic-gate 	goto fail;
8227c478bd9Sstevel@tonic-gate       }
8237c478bd9Sstevel@tonic-gate       strcat(trans, " ");
8247c478bd9Sstevel@tonic-gate     }
8257c478bd9Sstevel@tonic-gate     if (strlen(trans) + strlen(realm) + 1 >= MAX_REALM_LN) {
8267c478bd9Sstevel@tonic-gate       retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
8277c478bd9Sstevel@tonic-gate       goto fail;
8287c478bd9Sstevel@tonic-gate     }
8297c478bd9Sstevel@tonic-gate     strcat(trans, realm);
8307c478bd9Sstevel@tonic-gate     new_trans->length = strlen(trans);
8317c478bd9Sstevel@tonic-gate   }
8327c478bd9Sstevel@tonic-gate 
8337c478bd9Sstevel@tonic-gate   retval = 0;
8347c478bd9Sstevel@tonic-gate fail:
8357c478bd9Sstevel@tonic-gate   free(realm);
8367c478bd9Sstevel@tonic-gate   free(otrans_ptr);
8377c478bd9Sstevel@tonic-gate   return (retval);
8387c478bd9Sstevel@tonic-gate }
8397c478bd9Sstevel@tonic-gate 
8407c478bd9Sstevel@tonic-gate /*
8417c478bd9Sstevel@tonic-gate  * Routines that validate a AS request; checks a lot of things.  :-)
8427c478bd9Sstevel@tonic-gate  *
8437c478bd9Sstevel@tonic-gate  * Returns a Kerberos protocol error number, which is _not_ the same
8447c478bd9Sstevel@tonic-gate  * as a com_err error number!
8457c478bd9Sstevel@tonic-gate  */
8467c478bd9Sstevel@tonic-gate #define AS_INVALID_OPTIONS (KDC_OPT_FORWARDED | KDC_OPT_PROXY |\
84756a424ccSmp KDC_OPT_VALIDATE | KDC_OPT_RENEW | KDC_OPT_ENC_TKT_IN_SKEY)
8487c478bd9Sstevel@tonic-gate int
validate_as_request(register krb5_kdc_req * request,krb5_db_entry client,krb5_db_entry server,krb5_timestamp kdc_time,const char ** status)84956a424ccSmp validate_as_request(register krb5_kdc_req *request, krb5_db_entry client,
85056a424ccSmp 		    krb5_db_entry server, krb5_timestamp kdc_time,
85156a424ccSmp 		    const char **status)
8527c478bd9Sstevel@tonic-gate {
8537c478bd9Sstevel@tonic-gate     int		errcode;
854*55fea89dSDan Cross 
8557c478bd9Sstevel@tonic-gate     /*
85656a424ccSmp      * If an option is set that is only allowed in TGS requests, complain.
8577c478bd9Sstevel@tonic-gate      */
8587c478bd9Sstevel@tonic-gate     if (request->kdc_options & AS_INVALID_OPTIONS) {
8597c478bd9Sstevel@tonic-gate 	*status = "INVALID AS OPTIONS";
8607c478bd9Sstevel@tonic-gate 	return KDC_ERR_BADOPTION;
8617c478bd9Sstevel@tonic-gate     }
8627c478bd9Sstevel@tonic-gate 
8637c478bd9Sstevel@tonic-gate     /* The client's password must not be expired, unless the server is
8647c478bd9Sstevel@tonic-gate       a KRB5_KDC_PWCHANGE_SERVICE. */
8657c478bd9Sstevel@tonic-gate     if (client.pw_expiration && client.pw_expiration < kdc_time &&
8667c478bd9Sstevel@tonic-gate 	!isflagset(server.attributes, KRB5_KDB_PWCHANGE_SERVICE)) {
8677c478bd9Sstevel@tonic-gate 	*status = "CLIENT KEY EXPIRED";
8687c478bd9Sstevel@tonic-gate #ifdef KRBCONF_VAGUE_ERRORS
8697c478bd9Sstevel@tonic-gate 	return(KRB_ERR_GENERIC);
8707c478bd9Sstevel@tonic-gate #else
8717c478bd9Sstevel@tonic-gate 	return(KDC_ERR_KEY_EXP);
8727c478bd9Sstevel@tonic-gate #endif
8737c478bd9Sstevel@tonic-gate     }
8747c478bd9Sstevel@tonic-gate 
8757c478bd9Sstevel@tonic-gate     /* The client must not be expired */
8767c478bd9Sstevel@tonic-gate     if (client.expiration && client.expiration < kdc_time) {
8777c478bd9Sstevel@tonic-gate 	*status = "CLIENT EXPIRED";
8787c478bd9Sstevel@tonic-gate #ifdef KRBCONF_VAGUE_ERRORS
8797c478bd9Sstevel@tonic-gate 	return(KRB_ERR_GENERIC);
8807c478bd9Sstevel@tonic-gate #else
8817c478bd9Sstevel@tonic-gate 	return(KDC_ERR_NAME_EXP);
8827c478bd9Sstevel@tonic-gate #endif
8837c478bd9Sstevel@tonic-gate     }
8847c478bd9Sstevel@tonic-gate 
8857c478bd9Sstevel@tonic-gate     /* The server must not be expired */
8867c478bd9Sstevel@tonic-gate     if (server.expiration && server.expiration < kdc_time) {
8877c478bd9Sstevel@tonic-gate 	*status = "SERVICE EXPIRED";
8887c478bd9Sstevel@tonic-gate 	    return(KDC_ERR_SERVICE_EXP);
8897c478bd9Sstevel@tonic-gate     }
8907c478bd9Sstevel@tonic-gate 
8917c478bd9Sstevel@tonic-gate     /*
892*55fea89dSDan Cross      * If the client requires password changing, then only allow the
8937c478bd9Sstevel@tonic-gate      * pwchange service.
8947c478bd9Sstevel@tonic-gate      */
8957c478bd9Sstevel@tonic-gate     if (isflagset(client.attributes, KRB5_KDB_REQUIRES_PWCHANGE) &&
8967c478bd9Sstevel@tonic-gate 	!isflagset(server.attributes, KRB5_KDB_PWCHANGE_SERVICE)) {
8977c478bd9Sstevel@tonic-gate 	*status = "REQUIRED PWCHANGE";
8987c478bd9Sstevel@tonic-gate 	return(KDC_ERR_KEY_EXP);
8997c478bd9Sstevel@tonic-gate     }
9007c478bd9Sstevel@tonic-gate 
9017c478bd9Sstevel@tonic-gate     /* Client and server must allow postdating tickets */
9027c478bd9Sstevel@tonic-gate     if ((isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) ||
903*55fea89dSDan Cross 	 isflagset(request->kdc_options, KDC_OPT_POSTDATED)) &&
9047c478bd9Sstevel@tonic-gate 	(isflagset(client.attributes, KRB5_KDB_DISALLOW_POSTDATED) ||
9057c478bd9Sstevel@tonic-gate 	 isflagset(server.attributes, KRB5_KDB_DISALLOW_POSTDATED))) {
9067c478bd9Sstevel@tonic-gate 	*status = "POSTDATE NOT ALLOWED";
9077c478bd9Sstevel@tonic-gate 	return(KDC_ERR_CANNOT_POSTDATE);
9087c478bd9Sstevel@tonic-gate     }
909*55fea89dSDan Cross 
9107c478bd9Sstevel@tonic-gate     /* Client and server must allow forwardable tickets */
9117c478bd9Sstevel@tonic-gate     if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) &&
9127c478bd9Sstevel@tonic-gate 	(isflagset(client.attributes, KRB5_KDB_DISALLOW_FORWARDABLE) ||
9137c478bd9Sstevel@tonic-gate 	 isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))) {
9147c478bd9Sstevel@tonic-gate 	*status = "FORWARDABLE NOT ALLOWED";
9157c478bd9Sstevel@tonic-gate 	return(KDC_ERR_POLICY);
9167c478bd9Sstevel@tonic-gate     }
917*55fea89dSDan Cross 
9187c478bd9Sstevel@tonic-gate     /* Client and server must allow renewable tickets */
9197c478bd9Sstevel@tonic-gate     if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE) &&
9207c478bd9Sstevel@tonic-gate 	(isflagset(client.attributes, KRB5_KDB_DISALLOW_RENEWABLE) ||
9217c478bd9Sstevel@tonic-gate 	 isflagset(server.attributes, KRB5_KDB_DISALLOW_RENEWABLE))) {
9227c478bd9Sstevel@tonic-gate 	*status = "RENEWABLE NOT ALLOWED";
9237c478bd9Sstevel@tonic-gate 	return(KDC_ERR_POLICY);
9247c478bd9Sstevel@tonic-gate     }
925*55fea89dSDan Cross 
9267c478bd9Sstevel@tonic-gate     /* Client and server must allow proxiable tickets */
9277c478bd9Sstevel@tonic-gate     if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE) &&
9287c478bd9Sstevel@tonic-gate 	(isflagset(client.attributes, KRB5_KDB_DISALLOW_PROXIABLE) ||
9297c478bd9Sstevel@tonic-gate 	 isflagset(server.attributes, KRB5_KDB_DISALLOW_PROXIABLE))) {
9307c478bd9Sstevel@tonic-gate 	*status = "PROXIABLE NOT ALLOWED";
9317c478bd9Sstevel@tonic-gate 	return(KDC_ERR_POLICY);
9327c478bd9Sstevel@tonic-gate     }
933*55fea89dSDan Cross 
9347c478bd9Sstevel@tonic-gate     /* Check to see if client is locked out */
9357c478bd9Sstevel@tonic-gate     if (isflagset(client.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
9367c478bd9Sstevel@tonic-gate 	*status = "CLIENT LOCKED OUT";
9377c478bd9Sstevel@tonic-gate 	return(KDC_ERR_C_PRINCIPAL_UNKNOWN);
9387c478bd9Sstevel@tonic-gate     }
9397c478bd9Sstevel@tonic-gate 
9407c478bd9Sstevel@tonic-gate     /* Check to see if server is locked out */
9417c478bd9Sstevel@tonic-gate     if (isflagset(server.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
9427c478bd9Sstevel@tonic-gate 	*status = "SERVICE LOCKED OUT";
9437c478bd9Sstevel@tonic-gate 	return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
9447c478bd9Sstevel@tonic-gate     }
945*55fea89dSDan Cross 
9467c478bd9Sstevel@tonic-gate     /* Check to see if server is allowed to be a service */
9477c478bd9Sstevel@tonic-gate     if (isflagset(server.attributes, KRB5_KDB_DISALLOW_SVR)) {
9487c478bd9Sstevel@tonic-gate 	*status = "SERVICE NOT ALLOWED";
9497c478bd9Sstevel@tonic-gate 	return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
9507c478bd9Sstevel@tonic-gate     }
9517c478bd9Sstevel@tonic-gate 
9527c478bd9Sstevel@tonic-gate     /*
9537c478bd9Sstevel@tonic-gate      * Check against local policy
9547c478bd9Sstevel@tonic-gate      */
9557c478bd9Sstevel@tonic-gate     errcode = against_local_policy_as(request, server, client,
956*55fea89dSDan Cross 				      kdc_time, status);
9577c478bd9Sstevel@tonic-gate     if (errcode)
9587c478bd9Sstevel@tonic-gate 	return errcode;
9597c478bd9Sstevel@tonic-gate 
9607c478bd9Sstevel@tonic-gate     return 0;
9617c478bd9Sstevel@tonic-gate }
9627c478bd9Sstevel@tonic-gate 
9637c478bd9Sstevel@tonic-gate #define ASN1_ID_CLASS	(0xc0)
9647c478bd9Sstevel@tonic-gate #define ASN1_ID_TYPE    (0x20)
965*55fea89dSDan Cross #define ASN1_ID_TAG	(0x1f)
9667c478bd9Sstevel@tonic-gate #define ASN1_CLASS_UNIV	(0)
9677c478bd9Sstevel@tonic-gate #define ASN1_CLASS_APP	(1)
9687c478bd9Sstevel@tonic-gate #define ASN1_CLASS_CTX	(2)
9697c478bd9Sstevel@tonic-gate #define ASN1_CLASS_PRIV	(3)
9707c478bd9Sstevel@tonic-gate #define asn1_id_constructed(x) 	(x & ASN1_ID_TYPE)
9717c478bd9Sstevel@tonic-gate #define asn1_id_primitive(x) 	(!asn1_id_constructed(x))
9727c478bd9Sstevel@tonic-gate #define asn1_id_class(x)	((x & ASN1_ID_CLASS) >> 6)
9737c478bd9Sstevel@tonic-gate #define asn1_id_tag(x)		(x & ASN1_ID_TAG)
9747c478bd9Sstevel@tonic-gate 
9757c478bd9Sstevel@tonic-gate /*
9767c478bd9Sstevel@tonic-gate  * asn1length - return encoded length of value.
9777c478bd9Sstevel@tonic-gate  *
9787c478bd9Sstevel@tonic-gate  * passed a pointer into the asn.1 stream, which is updated
9797c478bd9Sstevel@tonic-gate  * to point right after the length bits.
9807c478bd9Sstevel@tonic-gate  *
9817c478bd9Sstevel@tonic-gate  * returns -1 on failure.
9827c478bd9Sstevel@tonic-gate  */
9837c478bd9Sstevel@tonic-gate static int
asn1length(unsigned char ** astream)98456a424ccSmp asn1length(unsigned char **astream)
9857c478bd9Sstevel@tonic-gate {
9867c478bd9Sstevel@tonic-gate     int length;		/* resulting length */
9877c478bd9Sstevel@tonic-gate     int sublen;		/* sublengths */
988*55fea89dSDan Cross     int blen;		/* bytes of length */
989*55fea89dSDan Cross     unsigned char *p;	/* substring searching */
9907c478bd9Sstevel@tonic-gate 
9917c478bd9Sstevel@tonic-gate     if (**astream & 0x80) {
9927c478bd9Sstevel@tonic-gate         blen = **astream & 0x7f;
9937c478bd9Sstevel@tonic-gate 	if (blen > 3) {
9947c478bd9Sstevel@tonic-gate 	   return(-1);
9957c478bd9Sstevel@tonic-gate 	}
9967c478bd9Sstevel@tonic-gate 	for (++*astream, length = 0; blen; ++*astream, blen--) {
9977c478bd9Sstevel@tonic-gate 	    length = (length << 8) | **astream;
9987c478bd9Sstevel@tonic-gate 	}
9997c478bd9Sstevel@tonic-gate 	if (length == 0) {
10007c478bd9Sstevel@tonic-gate 		/* indefinite length, figure out by hand */
10017c478bd9Sstevel@tonic-gate 	    p = *astream;
10027c478bd9Sstevel@tonic-gate 	    p++;
10037c478bd9Sstevel@tonic-gate 	    while (1) {
10047c478bd9Sstevel@tonic-gate 		/* compute value length. */
10057c478bd9Sstevel@tonic-gate 		if ((sublen = asn1length(&p)) < 0) {
10067c478bd9Sstevel@tonic-gate 		    return(-1);
10077c478bd9Sstevel@tonic-gate 		}
10087c478bd9Sstevel@tonic-gate 		p += sublen;
10097c478bd9Sstevel@tonic-gate                 /* check for termination */
10107c478bd9Sstevel@tonic-gate 		if ((!*p++) && (!*p)) {
10117c478bd9Sstevel@tonic-gate 		    p++;
10127c478bd9Sstevel@tonic-gate 		    break;
10137c478bd9Sstevel@tonic-gate 		}
10147c478bd9Sstevel@tonic-gate 	    }
1015*55fea89dSDan Cross 	    length = p - *astream;
10167c478bd9Sstevel@tonic-gate 	}
10177c478bd9Sstevel@tonic-gate     } else {
10187c478bd9Sstevel@tonic-gate 	length = **astream;
10197c478bd9Sstevel@tonic-gate 	++*astream;
1020*55fea89dSDan Cross     }
10217c478bd9Sstevel@tonic-gate    return(length);
10227c478bd9Sstevel@tonic-gate }
10237c478bd9Sstevel@tonic-gate 
10247c478bd9Sstevel@tonic-gate /*
10257c478bd9Sstevel@tonic-gate  * fetch_asn1_field - return raw asn.1 stream of subfield.
10267c478bd9Sstevel@tonic-gate  *
10277c478bd9Sstevel@tonic-gate  * this routine is passed a context-dependent tag number and "level" and returns
10287c478bd9Sstevel@tonic-gate  * the size and length of the corresponding level subfield.
10297c478bd9Sstevel@tonic-gate  *
1030*55fea89dSDan Cross  * levels and are numbered starting from 1.
10317c478bd9Sstevel@tonic-gate  *
10327c478bd9Sstevel@tonic-gate  * returns 0 on success, -1 otherwise.
10337c478bd9Sstevel@tonic-gate  */
10347c478bd9Sstevel@tonic-gate int
fetch_asn1_field(unsigned char * astream,unsigned int level,unsigned int field,krb5_data * data)103556a424ccSmp fetch_asn1_field(unsigned char *astream, unsigned int level,
103656a424ccSmp 		 unsigned int field, krb5_data *data)
10377c478bd9Sstevel@tonic-gate {
10387c478bd9Sstevel@tonic-gate     unsigned char *estream;	/* end of stream */
10397c478bd9Sstevel@tonic-gate     int classes;		/* # classes seen so far this level */
10407c478bd9Sstevel@tonic-gate     unsigned int levels = 0;		/* levels seen so far */
10417c478bd9Sstevel@tonic-gate     int lastlevel = 1000;       /* last level seen */
10427c478bd9Sstevel@tonic-gate     int length;			/* various lengths */
10437c478bd9Sstevel@tonic-gate     int tag;			/* tag number */
10447c478bd9Sstevel@tonic-gate     unsigned char savelen;      /* saved length of our field */
10457c478bd9Sstevel@tonic-gate 
10467c478bd9Sstevel@tonic-gate     classes = -1;
1047*55fea89dSDan Cross     /* we assume that the first identifier/length will tell us
10487c478bd9Sstevel@tonic-gate        how long the entire stream is. */
10497c478bd9Sstevel@tonic-gate     astream++;
10507c478bd9Sstevel@tonic-gate     estream = astream;
10517c478bd9Sstevel@tonic-gate     if ((length = asn1length(&astream)) < 0) {
10527c478bd9Sstevel@tonic-gate 	return(-1);
10537c478bd9Sstevel@tonic-gate     }
10547c478bd9Sstevel@tonic-gate     estream += length;
10557c478bd9Sstevel@tonic-gate     /* search down the stream, checking identifiers.  we process identifiers
10567c478bd9Sstevel@tonic-gate        until we hit the "level" we want, and then process that level for our
10577c478bd9Sstevel@tonic-gate        subfield, always making sure we don't go off the end of the stream.  */
10587c478bd9Sstevel@tonic-gate     while (astream < estream) {
10597c478bd9Sstevel@tonic-gate 	if (!asn1_id_constructed(*astream)) {
10607c478bd9Sstevel@tonic-gate 	    return(-1);
10617c478bd9Sstevel@tonic-gate 	}
10627c478bd9Sstevel@tonic-gate         if (asn1_id_class(*astream) == ASN1_CLASS_CTX) {
10637c478bd9Sstevel@tonic-gate             if ((tag = (int)asn1_id_tag(*astream)) <= lastlevel) {
10647c478bd9Sstevel@tonic-gate                 levels++;
10657c478bd9Sstevel@tonic-gate                 classes = -1;
10667c478bd9Sstevel@tonic-gate             }
1067*55fea89dSDan Cross             lastlevel = tag;
10687c478bd9Sstevel@tonic-gate             if (levels == level) {
10697c478bd9Sstevel@tonic-gate 	        /* in our context-dependent class, is this the one we're looking for ? */
10707c478bd9Sstevel@tonic-gate 	        if (tag == field) {
1071*55fea89dSDan Cross 		    /* return length and data */
10727c478bd9Sstevel@tonic-gate 		    astream++;
10737c478bd9Sstevel@tonic-gate 		    savelen = *astream;
10747c478bd9Sstevel@tonic-gate 		    if ((data->length = asn1length(&astream)) < 0) {
10757c478bd9Sstevel@tonic-gate 		        return(-1);
10767c478bd9Sstevel@tonic-gate 	 	    }
10777c478bd9Sstevel@tonic-gate 		    /* if the field length is indefinite, we will have to subtract two
10787c478bd9Sstevel@tonic-gate                        (terminating octets) from the length returned since we don't want
10797c478bd9Sstevel@tonic-gate                        to pass any info from the "wrapper" back.  asn1length will always return
1080*55fea89dSDan Cross                        the *total* length of the field, not just what's contained in it */
10817c478bd9Sstevel@tonic-gate 		    if ((savelen & 0xff) == 0x80) {
10827c478bd9Sstevel@tonic-gate 		      data->length -=2 ;
10837c478bd9Sstevel@tonic-gate 		    }
10847c478bd9Sstevel@tonic-gate 		    data->data = (char *)astream;
10857c478bd9Sstevel@tonic-gate 		    return(0);
10867c478bd9Sstevel@tonic-gate 	        } else if (tag <= classes) {
10877c478bd9Sstevel@tonic-gate 		    /* we've seen this class before, something must be wrong */
10887c478bd9Sstevel@tonic-gate 		    return(-1);
10897c478bd9Sstevel@tonic-gate 	        } else {
10907c478bd9Sstevel@tonic-gate 		    classes = tag;
10917c478bd9Sstevel@tonic-gate 	        }
10927c478bd9Sstevel@tonic-gate 	    }
10937c478bd9Sstevel@tonic-gate         }
10947c478bd9Sstevel@tonic-gate         /* if we're not on our level yet, process this value.  otherwise skip over it */
10957c478bd9Sstevel@tonic-gate 	astream++;
10967c478bd9Sstevel@tonic-gate 	if ((length = asn1length(&astream)) < 0) {
10977c478bd9Sstevel@tonic-gate 	    return(-1);
10987c478bd9Sstevel@tonic-gate 	}
10997c478bd9Sstevel@tonic-gate 	if (levels == level) {
11007c478bd9Sstevel@tonic-gate 	    astream += length;
11017c478bd9Sstevel@tonic-gate 	}
11027c478bd9Sstevel@tonic-gate     }
11037c478bd9Sstevel@tonic-gate     return(-1);
1104*55fea89dSDan Cross }
11057c478bd9Sstevel@tonic-gate 
11067c478bd9Sstevel@tonic-gate /*
11077c478bd9Sstevel@tonic-gate  * Routines that validate a TGS request; checks a lot of things.  :-)
11087c478bd9Sstevel@tonic-gate  *
11097c478bd9Sstevel@tonic-gate  * Returns a Kerberos protocol error number, which is _not_ the same
11107c478bd9Sstevel@tonic-gate  * as a com_err error number!
11117c478bd9Sstevel@tonic-gate  */
11127c478bd9Sstevel@tonic-gate #define TGS_OPTIONS_HANDLED (KDC_OPT_FORWARDABLE | KDC_OPT_FORWARDED | \
11137c478bd9Sstevel@tonic-gate 			     KDC_OPT_PROXIABLE | KDC_OPT_PROXY | \
11147c478bd9Sstevel@tonic-gate 			     KDC_OPT_ALLOW_POSTDATE | KDC_OPT_POSTDATED | \
11157c478bd9Sstevel@tonic-gate 			     KDC_OPT_RENEWABLE | KDC_OPT_RENEWABLE_OK | \
11167c478bd9Sstevel@tonic-gate 			     KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_RENEW | \
11177c478bd9Sstevel@tonic-gate 			     KDC_OPT_VALIDATE)
11187c478bd9Sstevel@tonic-gate 
11197c478bd9Sstevel@tonic-gate #define NO_TGT_OPTION (KDC_OPT_FORWARDED | KDC_OPT_PROXY | KDC_OPT_RENEW | \
11207c478bd9Sstevel@tonic-gate 		       KDC_OPT_VALIDATE)
11217c478bd9Sstevel@tonic-gate 
11227c478bd9Sstevel@tonic-gate int
validate_tgs_request(register krb5_kdc_req * request,krb5_db_entry server,krb5_ticket * ticket,krb5_timestamp kdc_time,const char ** status)112356a424ccSmp validate_tgs_request(register krb5_kdc_req *request, krb5_db_entry server,
112456a424ccSmp 		     krb5_ticket *ticket, krb5_timestamp kdc_time,
112556a424ccSmp 		     const char **status)
11267c478bd9Sstevel@tonic-gate {
11277c478bd9Sstevel@tonic-gate     int		errcode;
11287c478bd9Sstevel@tonic-gate     int		st_idx = 0;
11297c478bd9Sstevel@tonic-gate 
11307c478bd9Sstevel@tonic-gate     /*
11317c478bd9Sstevel@tonic-gate      * If an illegal option is set, ignore it.
11327c478bd9Sstevel@tonic-gate      */
113356a424ccSmp     request->kdc_options &= TGS_OPTIONS_HANDLED;
113456a424ccSmp 
11357c478bd9Sstevel@tonic-gate     /* Check to see if server has expired */
11367c478bd9Sstevel@tonic-gate     if (server.expiration && server.expiration < kdc_time) {
11377c478bd9Sstevel@tonic-gate 	*status = "SERVICE EXPIRED";
11387c478bd9Sstevel@tonic-gate 	return(KDC_ERR_SERVICE_EXP);
11397c478bd9Sstevel@tonic-gate     }
11407c478bd9Sstevel@tonic-gate 
11417c478bd9Sstevel@tonic-gate     /*
11427c478bd9Sstevel@tonic-gate      * Verify that the server principal in authdat->ticket is correct
11437c478bd9Sstevel@tonic-gate      * (either the ticket granting service or the service that was
11447c478bd9Sstevel@tonic-gate      * originally requested)
11457c478bd9Sstevel@tonic-gate      */
11467c478bd9Sstevel@tonic-gate     if (request->kdc_options & NO_TGT_OPTION) {
11477c478bd9Sstevel@tonic-gate 	if (!krb5_principal_compare(kdc_context, ticket->server, request->server)) {
11487c478bd9Sstevel@tonic-gate 	    *status = "SERVER DIDN'T MATCH TICKET FOR RENEW/FORWARD/ETC";
11497c478bd9Sstevel@tonic-gate 	    return(KDC_ERR_SERVER_NOMATCH);
11507c478bd9Sstevel@tonic-gate 	}
11517c478bd9Sstevel@tonic-gate     } else {
11527c478bd9Sstevel@tonic-gate 	/*
11537c478bd9Sstevel@tonic-gate 	 * OK, we need to validate the krbtgt service in the ticket.
11547c478bd9Sstevel@tonic-gate 	 *
11557c478bd9Sstevel@tonic-gate 	 * The krbtgt service is of the form:
11567c478bd9Sstevel@tonic-gate 	 * 		krbtgt/realm-A@realm-B
11577c478bd9Sstevel@tonic-gate 	 *
11587c478bd9Sstevel@tonic-gate 	 * Realm A is the "server realm"; the realm of the
11597c478bd9Sstevel@tonic-gate 	 * server of the requested ticket must match this realm.
11607c478bd9Sstevel@tonic-gate 	 * Of course, it should be a realm serviced by this KDC.
11617c478bd9Sstevel@tonic-gate 	 *
11627c478bd9Sstevel@tonic-gate 	 * Realm B is the "client realm"; this is what should be
11637c478bd9Sstevel@tonic-gate 	 * added to the transited field.  (which is done elsewhere)
11647c478bd9Sstevel@tonic-gate 	 */
11657c478bd9Sstevel@tonic-gate 
11667c478bd9Sstevel@tonic-gate 	/* Make sure there are two components... */
11677c478bd9Sstevel@tonic-gate 	if (krb5_princ_size(kdc_context, ticket->server) != 2) {
11687c478bd9Sstevel@tonic-gate 	    *status = "BAD TGS SERVER LENGTH";
11697c478bd9Sstevel@tonic-gate 	    return KRB_AP_ERR_NOT_US;
11707c478bd9Sstevel@tonic-gate 	}
11717c478bd9Sstevel@tonic-gate 	/* ...that the first component is krbtgt... */
11727c478bd9Sstevel@tonic-gate 	if (!krb5_is_tgs_principal(ticket->server)) {
11737c478bd9Sstevel@tonic-gate 	    *status = "BAD TGS SERVER NAME";
11747c478bd9Sstevel@tonic-gate 	    return KRB_AP_ERR_NOT_US;
11757c478bd9Sstevel@tonic-gate 	}
11767c478bd9Sstevel@tonic-gate 	/* ...and that the second component matches the server realm... */
117756a424ccSmp 	if ((krb5_princ_size(kdc_context, ticket->server) <= 1) ||
117856a424ccSmp 	    (krb5_princ_component(kdc_context, ticket->server, 1)->length !=
11797c478bd9Sstevel@tonic-gate 	     krb5_princ_realm(kdc_context, request->server)->length) ||
11807c478bd9Sstevel@tonic-gate 	    memcmp(krb5_princ_component(kdc_context, ticket->server, 1)->data,
11817c478bd9Sstevel@tonic-gate 		   krb5_princ_realm(kdc_context, request->server)->data,
11827c478bd9Sstevel@tonic-gate 		   krb5_princ_realm(kdc_context, request->server)->length)) {
11837c478bd9Sstevel@tonic-gate 	    *status = "BAD TGS SERVER INSTANCE";
11847c478bd9Sstevel@tonic-gate 	    return KRB_AP_ERR_NOT_US;
11857c478bd9Sstevel@tonic-gate 	}
11867c478bd9Sstevel@tonic-gate 	/* XXX add check that second component must match locally
11877c478bd9Sstevel@tonic-gate 	 * supported realm?
11887c478bd9Sstevel@tonic-gate 	 */
11897c478bd9Sstevel@tonic-gate 
11907c478bd9Sstevel@tonic-gate 	/* Server must allow TGS based issuances */
11917c478bd9Sstevel@tonic-gate 	if (isflagset(server.attributes, KRB5_KDB_DISALLOW_TGT_BASED)) {
11927c478bd9Sstevel@tonic-gate 	    *status = "TGT BASED NOT ALLOWED";
11937c478bd9Sstevel@tonic-gate 	    return(KDC_ERR_POLICY);
11947c478bd9Sstevel@tonic-gate 	}
11957c478bd9Sstevel@tonic-gate     }
1196*55fea89dSDan Cross 
11977c478bd9Sstevel@tonic-gate     /* TGS must be forwardable to get forwarded or forwardable ticket */
11987c478bd9Sstevel@tonic-gate     if ((isflagset(request->kdc_options, KDC_OPT_FORWARDED) ||
11997c478bd9Sstevel@tonic-gate 	 isflagset(request->kdc_options, KDC_OPT_FORWARDABLE)) &&
12007c478bd9Sstevel@tonic-gate 	!isflagset(ticket->enc_part2->flags, TKT_FLG_FORWARDABLE)) {
12017c478bd9Sstevel@tonic-gate 	*status = "TGT NOT FORWARDABLE";
12027c478bd9Sstevel@tonic-gate 
12037c478bd9Sstevel@tonic-gate 	return KDC_ERR_BADOPTION;
12047c478bd9Sstevel@tonic-gate     }
12057c478bd9Sstevel@tonic-gate 
1206*55fea89dSDan Cross     /* TGS must be proxiable to get proxiable ticket */
12077c478bd9Sstevel@tonic-gate     if ((isflagset(request->kdc_options, KDC_OPT_PROXY) ||
12087c478bd9Sstevel@tonic-gate 	 isflagset(request->kdc_options, KDC_OPT_PROXIABLE)) &&
12097c478bd9Sstevel@tonic-gate 	!isflagset(ticket->enc_part2->flags, TKT_FLG_PROXIABLE)) {
12107c478bd9Sstevel@tonic-gate 	*status = "TGT NOT PROXIABLE";
12117c478bd9Sstevel@tonic-gate 	return KDC_ERR_BADOPTION;
12127c478bd9Sstevel@tonic-gate     }
12137c478bd9Sstevel@tonic-gate 
12147c478bd9Sstevel@tonic-gate     /* TGS must allow postdating to get postdated ticket */
12157c478bd9Sstevel@tonic-gate     if ((isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) ||
12167c478bd9Sstevel@tonic-gate 	  isflagset(request->kdc_options, KDC_OPT_POSTDATED)) &&
12177c478bd9Sstevel@tonic-gate 	!isflagset(ticket->enc_part2->flags, TKT_FLG_MAY_POSTDATE)) {
12187c478bd9Sstevel@tonic-gate 	*status = "TGT NOT POSTDATABLE";
12197c478bd9Sstevel@tonic-gate 	return KDC_ERR_BADOPTION;
12207c478bd9Sstevel@tonic-gate     }
12217c478bd9Sstevel@tonic-gate 
12227c478bd9Sstevel@tonic-gate     /* can only validate invalid tix */
12237c478bd9Sstevel@tonic-gate     if (isflagset(request->kdc_options, KDC_OPT_VALIDATE) &&
12247c478bd9Sstevel@tonic-gate 	!isflagset(ticket->enc_part2->flags, TKT_FLG_INVALID)) {
12257c478bd9Sstevel@tonic-gate 	*status = "VALIDATE VALID TICKET";
12267c478bd9Sstevel@tonic-gate 	return KDC_ERR_BADOPTION;
12277c478bd9Sstevel@tonic-gate     }
12287c478bd9Sstevel@tonic-gate 
12297c478bd9Sstevel@tonic-gate     /* can only renew renewable tix */
12307c478bd9Sstevel@tonic-gate     if ((isflagset(request->kdc_options, KDC_OPT_RENEW) ||
12317c478bd9Sstevel@tonic-gate 	  isflagset(request->kdc_options, KDC_OPT_RENEWABLE)) &&
12327c478bd9Sstevel@tonic-gate 	!isflagset(ticket->enc_part2->flags, TKT_FLG_RENEWABLE)) {
12337c478bd9Sstevel@tonic-gate 	*status = "TICKET NOT RENEWABLE";
12347c478bd9Sstevel@tonic-gate 	return KDC_ERR_BADOPTION;
12357c478bd9Sstevel@tonic-gate     }
12367c478bd9Sstevel@tonic-gate 
12377c478bd9Sstevel@tonic-gate     /* can not proxy ticket granting tickets */
12387c478bd9Sstevel@tonic-gate     if (isflagset(request->kdc_options, KDC_OPT_PROXY) &&
12397c478bd9Sstevel@tonic-gate 	(!request->server->data ||
12407c478bd9Sstevel@tonic-gate 	 request->server->data[0].length != KRB5_TGS_NAME_SIZE ||
12417c478bd9Sstevel@tonic-gate 	 memcmp(request->server->data[0].data, KRB5_TGS_NAME,
12427c478bd9Sstevel@tonic-gate 		KRB5_TGS_NAME_SIZE))) {
12437c478bd9Sstevel@tonic-gate 	*status = "CAN'T PROXY TGT";
12447c478bd9Sstevel@tonic-gate 	return KDC_ERR_BADOPTION;
12457c478bd9Sstevel@tonic-gate     }
1246*55fea89dSDan Cross 
12477c478bd9Sstevel@tonic-gate     /* Server must allow forwardable tickets */
12487c478bd9Sstevel@tonic-gate     if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) &&
12497c478bd9Sstevel@tonic-gate 	isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE)) {
12507c478bd9Sstevel@tonic-gate 	*status = "NON-FORWARDABLE TICKET";
12517c478bd9Sstevel@tonic-gate 	return(KDC_ERR_POLICY);
12527c478bd9Sstevel@tonic-gate     }
1253*55fea89dSDan Cross 
12547c478bd9Sstevel@tonic-gate     /* Server must allow renewable tickets */
12557c478bd9Sstevel@tonic-gate     if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE) &&
12567c478bd9Sstevel@tonic-gate 	isflagset(server.attributes, KRB5_KDB_DISALLOW_RENEWABLE)) {
12577c478bd9Sstevel@tonic-gate 	*status = "NON-RENEWABLE TICKET";
12587c478bd9Sstevel@tonic-gate 	return(KDC_ERR_POLICY);
12597c478bd9Sstevel@tonic-gate     }
1260*55fea89dSDan Cross 
12617c478bd9Sstevel@tonic-gate     /* Server must allow proxiable tickets */
12627c478bd9Sstevel@tonic-gate     if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE) &&
12637c478bd9Sstevel@tonic-gate 	isflagset(server.attributes, KRB5_KDB_DISALLOW_PROXIABLE)) {
12647c478bd9Sstevel@tonic-gate 	*status = "NON-PROXIABLE TICKET";
12657c478bd9Sstevel@tonic-gate 	return(KDC_ERR_POLICY);
12667c478bd9Sstevel@tonic-gate     }
1267*55fea89dSDan Cross 
12687c478bd9Sstevel@tonic-gate     /* Server must allow postdated tickets */
12697c478bd9Sstevel@tonic-gate     if (isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) &&
12707c478bd9Sstevel@tonic-gate 	isflagset(server.attributes, KRB5_KDB_DISALLOW_POSTDATED)) {
12717c478bd9Sstevel@tonic-gate 	*status = "NON-POSTDATABLE TICKET";
12727c478bd9Sstevel@tonic-gate 	return(KDC_ERR_CANNOT_POSTDATE);
12737c478bd9Sstevel@tonic-gate     }
1274*55fea89dSDan Cross 
12757c478bd9Sstevel@tonic-gate     /* Server must allow DUP SKEY requests */
12767c478bd9Sstevel@tonic-gate     if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY) &&
12777c478bd9Sstevel@tonic-gate 	isflagset(server.attributes, KRB5_KDB_DISALLOW_DUP_SKEY)) {
12787c478bd9Sstevel@tonic-gate 	*status = "DUP_SKEY DISALLOWED";
12797c478bd9Sstevel@tonic-gate 	return(KDC_ERR_POLICY);
12807c478bd9Sstevel@tonic-gate     }
12817c478bd9Sstevel@tonic-gate 
12827c478bd9Sstevel@tonic-gate     /* Server must not be locked out */
12837c478bd9Sstevel@tonic-gate     if (isflagset(server.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
12847c478bd9Sstevel@tonic-gate 	*status = "SERVER LOCKED OUT";
12857c478bd9Sstevel@tonic-gate 	return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
12867c478bd9Sstevel@tonic-gate     }
1287*55fea89dSDan Cross 
12887c478bd9Sstevel@tonic-gate     /* Server must be allowed to be a service */
12897c478bd9Sstevel@tonic-gate     if (isflagset(server.attributes, KRB5_KDB_DISALLOW_SVR)) {
12907c478bd9Sstevel@tonic-gate 	*status = "SERVER NOT ALLOWED";
12917c478bd9Sstevel@tonic-gate 	return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
12927c478bd9Sstevel@tonic-gate     }
12937c478bd9Sstevel@tonic-gate 
12947c478bd9Sstevel@tonic-gate     /* Check the hot list */
12957c478bd9Sstevel@tonic-gate     if (check_hot_list(ticket)) {
12967c478bd9Sstevel@tonic-gate 	*status = "HOT_LIST";
12977c478bd9Sstevel@tonic-gate 	return(KRB_AP_ERR_REPEAT);
12987c478bd9Sstevel@tonic-gate     }
1299*55fea89dSDan Cross 
13007c478bd9Sstevel@tonic-gate     /* Check the start time vs. the KDC time */
13017c478bd9Sstevel@tonic-gate     if (isflagset(request->kdc_options, KDC_OPT_VALIDATE)) {
13027c478bd9Sstevel@tonic-gate 	if (ticket->enc_part2->times.starttime > kdc_time) {
13037c478bd9Sstevel@tonic-gate 	    *status = "NOT_YET_VALID";
13047c478bd9Sstevel@tonic-gate 	    return(KRB_AP_ERR_TKT_NYV);
13057c478bd9Sstevel@tonic-gate 	}
13067c478bd9Sstevel@tonic-gate     }
1307*55fea89dSDan Cross 
13087c478bd9Sstevel@tonic-gate     /*
13097c478bd9Sstevel@tonic-gate      * Check the renew_till time.  The endtime was already
13107c478bd9Sstevel@tonic-gate      * been checked in the initial authentication check.
13117c478bd9Sstevel@tonic-gate      */
13127c478bd9Sstevel@tonic-gate     if (isflagset(request->kdc_options, KDC_OPT_RENEW) &&
13137c478bd9Sstevel@tonic-gate 	(ticket->enc_part2->times.renew_till < kdc_time)) {
13147c478bd9Sstevel@tonic-gate 	*status = "TKT_EXPIRED";
13157c478bd9Sstevel@tonic-gate 	return(KRB_AP_ERR_TKT_EXPIRED);
13167c478bd9Sstevel@tonic-gate     }
1317*55fea89dSDan Cross 
13187c478bd9Sstevel@tonic-gate     /*
13197c478bd9Sstevel@tonic-gate      * Checks for ENC_TKT_IN_SKEY:
13207c478bd9Sstevel@tonic-gate      *
13217c478bd9Sstevel@tonic-gate      * (1) Make sure the second ticket exists
13227c478bd9Sstevel@tonic-gate      * (2) Make sure it is a ticket granting ticket
13237c478bd9Sstevel@tonic-gate      */
13247c478bd9Sstevel@tonic-gate     if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) {
13257c478bd9Sstevel@tonic-gate 	if (!request->second_ticket ||
13267c478bd9Sstevel@tonic-gate 	    !request->second_ticket[st_idx]) {
13277c478bd9Sstevel@tonic-gate 	    *status = "NO_2ND_TKT";
13287c478bd9Sstevel@tonic-gate 	    return(KDC_ERR_BADOPTION);
13297c478bd9Sstevel@tonic-gate 	}
13307c478bd9Sstevel@tonic-gate 	if (!krb5_principal_compare(kdc_context, request->second_ticket[st_idx]->server,
13317c478bd9Sstevel@tonic-gate 				    tgs_server)) {
13327c478bd9Sstevel@tonic-gate 		*status = "2ND_TKT_NOT_TGS";
13337c478bd9Sstevel@tonic-gate 		return(KDC_ERR_POLICY);
13347c478bd9Sstevel@tonic-gate 	}
13357c478bd9Sstevel@tonic-gate 	st_idx++;
13367c478bd9Sstevel@tonic-gate     }
13377c478bd9Sstevel@tonic-gate 
13387c478bd9Sstevel@tonic-gate     /* Check for hardware preauthentication */
13397c478bd9Sstevel@tonic-gate     if (isflagset(server.attributes, KRB5_KDB_REQUIRES_HW_AUTH) &&
13407c478bd9Sstevel@tonic-gate 	!isflagset(ticket->enc_part2->flags,TKT_FLG_HW_AUTH)) {
13417c478bd9Sstevel@tonic-gate 	*status = "NO HW PREAUTH";
13427c478bd9Sstevel@tonic-gate 	return KRB_ERR_GENERIC;
13437c478bd9Sstevel@tonic-gate     }
13447c478bd9Sstevel@tonic-gate 
13457c478bd9Sstevel@tonic-gate     /* Check for any kind of preauthentication */
13467c478bd9Sstevel@tonic-gate     if (isflagset(server.attributes, KRB5_KDB_REQUIRES_PRE_AUTH) &&
13477c478bd9Sstevel@tonic-gate 	!isflagset(ticket->enc_part2->flags, TKT_FLG_PRE_AUTH)) {
13487c478bd9Sstevel@tonic-gate 	*status = "NO PREAUTH";
13497c478bd9Sstevel@tonic-gate 	return KRB_ERR_GENERIC;
13507c478bd9Sstevel@tonic-gate     }
1351*55fea89dSDan Cross 
13527c478bd9Sstevel@tonic-gate     /*
13537c478bd9Sstevel@tonic-gate      * Check local policy
13547c478bd9Sstevel@tonic-gate      */
13557c478bd9Sstevel@tonic-gate     errcode = against_local_policy_tgs(request, server, ticket, status);
13567c478bd9Sstevel@tonic-gate     if (errcode)
13577c478bd9Sstevel@tonic-gate 	return errcode;
1358*55fea89dSDan Cross 
1359*55fea89dSDan Cross 
13607c478bd9Sstevel@tonic-gate     return 0;
13617c478bd9Sstevel@tonic-gate }
13627c478bd9Sstevel@tonic-gate 
13637c478bd9Sstevel@tonic-gate /*
13647c478bd9Sstevel@tonic-gate  * This function returns 1 if the dbentry has a key for a specified
13657c478bd9Sstevel@tonic-gate  * keytype, and 0 if not.
13667c478bd9Sstevel@tonic-gate  */
13677c478bd9Sstevel@tonic-gate int
dbentry_has_key_for_enctype(krb5_context context,krb5_db_entry * client,krb5_enctype enctype)136856a424ccSmp dbentry_has_key_for_enctype(krb5_context context, krb5_db_entry *client,
136956a424ccSmp 			    krb5_enctype enctype)
13707c478bd9Sstevel@tonic-gate {
13717c478bd9Sstevel@tonic-gate     krb5_error_code	retval;
13727c478bd9Sstevel@tonic-gate     krb5_key_data	*datap;
13737c478bd9Sstevel@tonic-gate 
13747c478bd9Sstevel@tonic-gate     retval = krb5_dbe_find_enctype(context, client, enctype,
13757c478bd9Sstevel@tonic-gate 				   -1, 0, &datap);
13767c478bd9Sstevel@tonic-gate     if (retval)
13777c478bd9Sstevel@tonic-gate 	return 0;
13787c478bd9Sstevel@tonic-gate     else
13797c478bd9Sstevel@tonic-gate 	return 1;
13807c478bd9Sstevel@tonic-gate }
13817c478bd9Sstevel@tonic-gate 
13827c478bd9Sstevel@tonic-gate /*
13837c478bd9Sstevel@tonic-gate  * This function returns 1 if the entity referenced by this
13847c478bd9Sstevel@tonic-gate  * structure can support the a particular encryption system, and 0 if
13857c478bd9Sstevel@tonic-gate  * not.
13867c478bd9Sstevel@tonic-gate  *
13877c478bd9Sstevel@tonic-gate  * XXX eventually this information should be looked up in the
13887c478bd9Sstevel@tonic-gate  * database.  Since it isn't, we use some hueristics and attribute
13897c478bd9Sstevel@tonic-gate  * options bits for now.
13907c478bd9Sstevel@tonic-gate  */
13917c478bd9Sstevel@tonic-gate int
dbentry_supports_enctype(krb5_context context,krb5_db_entry * client,krb5_enctype enctype)139256a424ccSmp dbentry_supports_enctype(krb5_context context, krb5_db_entry *client,
139356a424ccSmp 			 krb5_enctype enctype)
13947c478bd9Sstevel@tonic-gate {
13957c478bd9Sstevel@tonic-gate     /*
13967c478bd9Sstevel@tonic-gate      * If it's DES_CBC_MD5, there's a bit in the attribute mask which
13977c478bd9Sstevel@tonic-gate      * checks to see if we support it.
13987c478bd9Sstevel@tonic-gate      *
13997c478bd9Sstevel@tonic-gate      * In theory everything's supposed to support DES_CBC_MD5, but
14007c478bd9Sstevel@tonic-gate      * that's not the reality....
14017c478bd9Sstevel@tonic-gate      */
14027c478bd9Sstevel@tonic-gate 
14037c478bd9Sstevel@tonic-gate     /*
14047c478bd9Sstevel@tonic-gate      * We are assuming that all entries can support MD5; this information
14057c478bd9Sstevel@tonic-gate      * need not be kept in the database.
1406*55fea89dSDan Cross     */
1407*55fea89dSDan Cross 
1408*55fea89dSDan Cross 
14097c478bd9Sstevel@tonic-gate     if (enctype == ENCTYPE_DES_CBC_MD5)
14107c478bd9Sstevel@tonic-gate 	return 1;
14117c478bd9Sstevel@tonic-gate 
14127c478bd9Sstevel@tonic-gate     /*
14137c478bd9Sstevel@tonic-gate      * XXX we assume everything can understand DES_CBC_CRC
14147c478bd9Sstevel@tonic-gate      */
14157c478bd9Sstevel@tonic-gate     if (enctype == ENCTYPE_DES_CBC_CRC)
14167c478bd9Sstevel@tonic-gate 	return 1;
1417*55fea89dSDan Cross 
14187c478bd9Sstevel@tonic-gate     /*
14197c478bd9Sstevel@tonic-gate      * If we have a key for the encryption system, we assume it's
14207c478bd9Sstevel@tonic-gate      * supported.
14217c478bd9Sstevel@tonic-gate      */
14227c478bd9Sstevel@tonic-gate     return dbentry_has_key_for_enctype(context, client, enctype);
14237c478bd9Sstevel@tonic-gate }
14247c478bd9Sstevel@tonic-gate 
14257c478bd9Sstevel@tonic-gate /*
14267c478bd9Sstevel@tonic-gate  * This function returns the keytype which should be selected for the
14277c478bd9Sstevel@tonic-gate  * session key.  It is based on the ordered list which the user
14287c478bd9Sstevel@tonic-gate  * requested, and what the KDC and the application server can support.
14297c478bd9Sstevel@tonic-gate  */
14307c478bd9Sstevel@tonic-gate krb5_enctype
select_session_keytype(krb5_context context,krb5_db_entry * server,int nktypes,krb5_enctype * ktype)143156a424ccSmp select_session_keytype(krb5_context context, krb5_db_entry *server,
143256a424ccSmp 		       int nktypes, krb5_enctype *ktype)
14337c478bd9Sstevel@tonic-gate {
14347c478bd9Sstevel@tonic-gate     int		i;
1435*55fea89dSDan Cross 
14367c478bd9Sstevel@tonic-gate     for (i = 0; i < nktypes; i++) {
1437505d05c7Sgtb 	if (!krb5_c_valid_enctype(ktype[i]))
14387c478bd9Sstevel@tonic-gate 	    continue;
14397c478bd9Sstevel@tonic-gate 
144056a424ccSmp 	if (!krb5_is_permitted_enctype(context, ktype[i]))
144156a424ccSmp 	    continue;
144256a424ccSmp 
14437c478bd9Sstevel@tonic-gate 	if (dbentry_supports_enctype(context, server, ktype[i]))
14447c478bd9Sstevel@tonic-gate 	    return ktype[i];
14457c478bd9Sstevel@tonic-gate     }
14467c478bd9Sstevel@tonic-gate     return 0;
14477c478bd9Sstevel@tonic-gate }
14487c478bd9Sstevel@tonic-gate 
14497c478bd9Sstevel@tonic-gate /*
14507c478bd9Sstevel@tonic-gate  * This function returns salt information for a particular client_key
14517c478bd9Sstevel@tonic-gate  */
14527c478bd9Sstevel@tonic-gate krb5_error_code
get_salt_from_key(krb5_context context,krb5_principal client,krb5_key_data * client_key,krb5_data * salt)145356a424ccSmp get_salt_from_key(krb5_context context, krb5_principal client,
145456a424ccSmp 		  krb5_key_data *client_key, krb5_data *salt)
14557c478bd9Sstevel@tonic-gate {
14567c478bd9Sstevel@tonic-gate     krb5_error_code		retval;
14577c478bd9Sstevel@tonic-gate     krb5_data *			realm;
1458*55fea89dSDan Cross 
14597c478bd9Sstevel@tonic-gate     salt->data = 0;
146056a424ccSmp     salt->length = SALT_TYPE_NO_LENGTH;
1461*55fea89dSDan Cross 
14627c478bd9Sstevel@tonic-gate     if (client_key->key_data_ver == 1)
14637c478bd9Sstevel@tonic-gate 	return 0;
14647c478bd9Sstevel@tonic-gate 
14657c478bd9Sstevel@tonic-gate     switch (client_key->key_data_type[1]) {
14667c478bd9Sstevel@tonic-gate     case KRB5_KDB_SALTTYPE_NORMAL:
14677c478bd9Sstevel@tonic-gate 	break;
14687c478bd9Sstevel@tonic-gate     case KRB5_KDB_SALTTYPE_V4:
14697c478bd9Sstevel@tonic-gate 	/* send an empty (V4) salt */
14707c478bd9Sstevel@tonic-gate 	salt->data = 0;
14717c478bd9Sstevel@tonic-gate 	salt->length = 0;
14727c478bd9Sstevel@tonic-gate 	break;
14737c478bd9Sstevel@tonic-gate     case KRB5_KDB_SALTTYPE_NOREALM:
14747c478bd9Sstevel@tonic-gate 	if ((retval = krb5_principal2salt_norealm(context, client, salt)))
14757c478bd9Sstevel@tonic-gate 	    return retval;
14767c478bd9Sstevel@tonic-gate 	break;
14777c478bd9Sstevel@tonic-gate     case KRB5_KDB_SALTTYPE_AFS3:
14787c478bd9Sstevel@tonic-gate 	/* send the same salt as with onlyrealm - but with no type info,
14797c478bd9Sstevel@tonic-gate 	   we just hope they figure it out on the other end. */
14807c478bd9Sstevel@tonic-gate 	/* fall through to onlyrealm: */
14817c478bd9Sstevel@tonic-gate     case KRB5_KDB_SALTTYPE_ONLYREALM:
14827c478bd9Sstevel@tonic-gate 	realm = krb5_princ_realm(context, client);
14837c478bd9Sstevel@tonic-gate 	salt->length = realm->length;
14847c478bd9Sstevel@tonic-gate 	if ((salt->data = malloc(realm->length)) == NULL)
14857c478bd9Sstevel@tonic-gate 	    return ENOMEM;
14867c478bd9Sstevel@tonic-gate 	memcpy(salt->data, realm->data, realm->length);
14877c478bd9Sstevel@tonic-gate 	break;
14887c478bd9Sstevel@tonic-gate     case KRB5_KDB_SALTTYPE_SPECIAL:
14897c478bd9Sstevel@tonic-gate 	salt->length = client_key->key_data_length[1];
14907c478bd9Sstevel@tonic-gate 	if ((salt->data = malloc(salt->length)) == NULL)
14917c478bd9Sstevel@tonic-gate 	    return ENOMEM;
14927c478bd9Sstevel@tonic-gate 	memcpy(salt->data, client_key->key_data_contents[1], salt->length);
14937c478bd9Sstevel@tonic-gate 	break;
14947c478bd9Sstevel@tonic-gate     }
14957c478bd9Sstevel@tonic-gate     return 0;
14967c478bd9Sstevel@tonic-gate }
14977c478bd9Sstevel@tonic-gate 
14987c478bd9Sstevel@tonic-gate /*
14997c478bd9Sstevel@tonic-gate  * Limit strings to a "reasonable" length to prevent crowding out of
15007c478bd9Sstevel@tonic-gate  * other useful information in the log entry
15017c478bd9Sstevel@tonic-gate  */
15027c478bd9Sstevel@tonic-gate #define NAME_LENGTH_LIMIT 128
15037c478bd9Sstevel@tonic-gate 
limit_string(char * name)15047c478bd9Sstevel@tonic-gate void limit_string(char *name)
15057c478bd9Sstevel@tonic-gate {
15067c478bd9Sstevel@tonic-gate 	int	i;
15077c478bd9Sstevel@tonic-gate 
15087c478bd9Sstevel@tonic-gate 	if (!name)
15097c478bd9Sstevel@tonic-gate 		return;
15107c478bd9Sstevel@tonic-gate 
15117c478bd9Sstevel@tonic-gate 	if (strlen(name) < NAME_LENGTH_LIMIT)
15127c478bd9Sstevel@tonic-gate 		return;
15137c478bd9Sstevel@tonic-gate 
15147c478bd9Sstevel@tonic-gate 	i = NAME_LENGTH_LIMIT-4;
15157c478bd9Sstevel@tonic-gate 	name[i++] = '.';
15167c478bd9Sstevel@tonic-gate 	name[i++] = '.';
15177c478bd9Sstevel@tonic-gate 	name[i++] = '.';
15187c478bd9Sstevel@tonic-gate 	name[i] = '\0';
15197c478bd9Sstevel@tonic-gate 	return;
15207c478bd9Sstevel@tonic-gate }
152156a424ccSmp 
152256a424ccSmp /*
152356a424ccSmp  * L10_2 = log10(2**x), rounded up; log10(2) ~= 0.301.
152456a424ccSmp  */
152556a424ccSmp #define L10_2(x) ((int)(((x * 301) + 999) / 1000))
152656a424ccSmp 
152756a424ccSmp /*
152856a424ccSmp  * Max length of sprintf("%ld") for an int of type T; includes leading
152956a424ccSmp  * minus sign and terminating NUL.
153056a424ccSmp  */
153156a424ccSmp #define D_LEN(t) (L10_2(sizeof(t) * CHAR_BIT) + 2)
153256a424ccSmp 
153356a424ccSmp void
ktypes2str(char * s,size_t len,int nktypes,krb5_enctype * ktype)153456a424ccSmp ktypes2str(char *s, size_t len, int nktypes, krb5_enctype *ktype)
153556a424ccSmp {
153656a424ccSmp     int i;
153756a424ccSmp     char stmp[D_LEN(krb5_enctype) + 1];
153856a424ccSmp     char *p;
153956a424ccSmp 
154056a424ccSmp     if (nktypes < 0
154156a424ccSmp 	|| len < (sizeof(" etypes {...}") + D_LEN(int))) {
154256a424ccSmp 	*s = '\0';
154356a424ccSmp 	return;
154456a424ccSmp     }
154556a424ccSmp 
154656a424ccSmp     sprintf(s, "%d etypes {", nktypes);
154756a424ccSmp     for (i = 0; i < nktypes; i++) {
154856a424ccSmp 	sprintf(stmp, "%s%ld", i ? " " : "", (long)ktype[i]);
154956a424ccSmp 	if (strlen(s) + strlen(stmp) + sizeof("}") > len)
155056a424ccSmp 	    break;
155156a424ccSmp 	strcat(s, stmp);
155256a424ccSmp     }
155356a424ccSmp     if (i < nktypes) {
155456a424ccSmp 	/*
155556a424ccSmp 	 * We broke out of the loop. Try to truncate the list.
155656a424ccSmp 	 */
155756a424ccSmp 	p = s + strlen(s);
155856a424ccSmp 	while (p - s + sizeof("...}") > len) {
155956a424ccSmp 	    while (p > s && *p != ' ' && *p != '{')
156056a424ccSmp 		*p-- = '\0';
156156a424ccSmp 	    if (p > s && *p == ' ') {
156256a424ccSmp 		*p-- = '\0';
156356a424ccSmp 		continue;
156456a424ccSmp 	    }
156556a424ccSmp 	}
156656a424ccSmp 	strcat(s, "...");
156756a424ccSmp     }
156856a424ccSmp     strcat(s, "}");
156956a424ccSmp     return;
157056a424ccSmp }
157156a424ccSmp 
157256a424ccSmp void
rep_etypes2str(char * s,size_t len,krb5_kdc_rep * rep)157356a424ccSmp rep_etypes2str(char *s, size_t len, krb5_kdc_rep *rep)
157456a424ccSmp {
157556a424ccSmp     char stmp[sizeof("ses=") + D_LEN(krb5_enctype)];
157656a424ccSmp 
157756a424ccSmp     if (len < (3 * D_LEN(krb5_enctype)
157856a424ccSmp 	       + sizeof("etypes {rep= tkt= ses=}"))) {
157956a424ccSmp 	*s = '\0';
158056a424ccSmp 	return;
158156a424ccSmp     }
158256a424ccSmp 
158356a424ccSmp     sprintf(s, "etypes {rep=%ld", (long)rep->enc_part.enctype);
158456a424ccSmp 
158556a424ccSmp     if (rep->ticket != NULL) {
158656a424ccSmp 	sprintf(stmp, " tkt=%ld", (long)rep->ticket->enc_part.enctype);
158756a424ccSmp 	strcat(s, stmp);
158856a424ccSmp     }
158956a424ccSmp 
159056a424ccSmp     if (rep->ticket != NULL
159156a424ccSmp 	&& rep->ticket->enc_part2 != NULL
159256a424ccSmp 	&& rep->ticket->enc_part2->session != NULL) {
159356a424ccSmp 	sprintf(stmp, " ses=%ld",
159456a424ccSmp 		(long)rep->ticket->enc_part2->session->enctype);
159556a424ccSmp 	strcat(s, stmp);
159656a424ccSmp     }
159756a424ccSmp     strcat(s, "}");
159856a424ccSmp     return;
159956a424ccSmp }
1600