xref: /illumos-gate/usr/src/cmd/krb5/krb5kdc/kdc_util.c (revision ed69b65213c4951189b6e407c83cf111f35c96e7)
1 /*
2  * kdc/kdc_util.c
3  *
4  * Copyright 1990,1991 by the Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  *
26  *
27  * Utility functions for the KDC implementation.
28  */
29 
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 #include "k5-int.h"
34 #include "kdc_util.h"
35 #include "extern.h"
36 #include <stdio.h>
37 #include <ctype.h>
38 #include <syslog.h>
39 #include "adm.h"
40 #include "adm_proto.h"
41 #include <limits.h>
42 
43 #ifdef USE_RCACHE
44 static char *kdc_current_rcname = (char *) NULL;
45 krb5_deltat rc_lifetime; /* See kdc_initialize_rcache() */
46 #endif
47 
48 #ifdef USE_RCACHE
49 /*
50  * initialize the replay cache.
51  */
52 krb5_error_code
53 kdc_initialize_rcache(krb5_context kcontext, char *rcache_name)
54 {
55     krb5_error_code	retval;
56     char		*rcname;
57     char		*sname;
58 
59     rcname = (rcache_name) ? rcache_name : kdc_current_rcname;
60 
61     /* rc_lifetime used elsewhere to verify we're not */
62     /*  replaying really old data                     */
63     rc_lifetime = kcontext->clockskew;
64 
65     if (!rcname)
66 	rcname = KDCRCACHE;
67     if (!(retval = krb5_rc_resolve_full(kcontext, &kdc_rcache, rcname))) {
68 	/* Recover or initialize the replay cache */
69 	if (!(retval = krb5_rc_recover(kcontext, kdc_rcache)) ||
70 	    !(retval = krb5_rc_initialize(kcontext,
71 					  kdc_rcache,
72 					  kcontext->clockskew))
73 	    ) {
74 	    /* Expunge the replay cache */
75 	    if (!(retval = krb5_rc_expunge(kcontext, kdc_rcache))) {
76 		sname = kdc_current_rcname;
77 		kdc_current_rcname = strdup(rcname);
78 		if (sname)
79 		    free(sname);
80 	    }
81 	}
82 	if (retval)
83 	    krb5_rc_close(kcontext, kdc_rcache);
84     }
85     return(retval);
86 }
87 #endif
88 
89 /*
90  * concatenate first two authdata arrays, returning an allocated replacement.
91  * The replacement should be freed with krb5_free_authdata().
92  */
93 krb5_error_code
94 concat_authorization_data(krb5_authdata **first, krb5_authdata **second,
95 			  krb5_authdata ***output)
96 {
97     register int i, j;
98     register krb5_authdata **ptr, **retdata;
99 
100     /* count up the entries */
101     i = 0;
102     if (first)
103 	for (ptr = first; *ptr; ptr++)
104 	    i++;
105     if (second)
106 	for (ptr = second; *ptr; ptr++)
107 	    i++;
108 
109     retdata = (krb5_authdata **)malloc((i+1)*sizeof(*retdata));
110     if (!retdata)
111 	return ENOMEM;
112     retdata[i] = 0;			/* null-terminated array */
113     for (i = 0, j = 0, ptr = first; j < 2 ; ptr = second, j++)
114 	while (ptr && *ptr) {
115 	    /* now walk & copy */
116 	    retdata[i] = (krb5_authdata *)malloc(sizeof(*retdata[i]));
117 	    if (!retdata[i]) {
118 		krb5_free_authdata(kdc_context, retdata);
119 		return ENOMEM;
120 	    }
121 	    *retdata[i] = **ptr;
122 	    if (!(retdata[i]->contents =
123 		  (krb5_octet *)malloc(retdata[i]->length))) {
124 		free((char *)retdata[i]);
125 		retdata[i] = 0;
126 		krb5_free_authdata(kdc_context, retdata);
127 		return ENOMEM;
128 	    }
129 	    memcpy((char *) retdata[i]->contents,
130 		   (char *)(*ptr)->contents,
131 		   retdata[i]->length);
132 
133 	    ptr++;
134 	    i++;
135 	}
136     *output = retdata;
137     return 0;
138 }
139 
140 krb5_boolean
141 realm_compare(krb5_principal princ1, krb5_principal princ2)
142 {
143   krb5_data *realm1 = krb5_princ_realm(kdc_context, princ1);
144   krb5_data *realm2 = krb5_princ_realm(kdc_context, princ2);
145 
146   return((realm1->length == realm2->length) &&
147          !memcmp(realm1->data, realm2->data, realm1->length));
148 }
149 
150 /*
151  * Returns TRUE if the kerberos principal is the name of a Kerberos ticket
152  * service.
153  */
154 krb5_boolean krb5_is_tgs_principal(krb5_principal principal)
155 {
156 	if ((krb5_princ_size(kdc_context, principal) > 0) &&
157 	    (krb5_princ_component(kdc_context, principal, 0)->length ==
158 	     KRB5_TGS_NAME_SIZE) &&
159 	    (!memcmp(krb5_princ_component(kdc_context, principal, 0)->data,
160 		     KRB5_TGS_NAME, KRB5_TGS_NAME_SIZE)))
161 		return TRUE;
162 	return FALSE;
163 }
164 
165 /*
166  * given authentication data (provides seed for checksum), verify checksum
167  * for source data.
168  */
169 static krb5_error_code
170 comp_cksum(krb5_context kcontext, krb5_data *source, krb5_ticket *ticket,
171 	   krb5_checksum *his_cksum)
172 {
173     krb5_error_code 	  retval;
174     krb5_boolean	  valid;
175 
176     if (!krb5_c_valid_cksumtype(his_cksum->checksum_type))
177 	return KRB5KDC_ERR_SUMTYPE_NOSUPP;
178 
179     /* must be collision proof */
180     if (!krb5_c_is_coll_proof_cksum(his_cksum->checksum_type))
181 	return KRB5KRB_AP_ERR_INAPP_CKSUM;
182 
183     /* verify checksum */
184     if ((retval = krb5_c_verify_checksum(kcontext, ticket->enc_part2->session,
185 					 KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
186 					 source, his_cksum, &valid)))
187 	return(retval);
188 
189     if (!valid)
190 	return(KRB5KRB_AP_ERR_BAD_INTEGRITY);
191 
192     return(0);
193 }
194 
195 krb5_error_code
196 kdc_process_tgs_req(krb5_kdc_req *request, const krb5_fulladdr *from,
197 		    krb5_data *pkt, krb5_ticket **ticket,
198 		    krb5_keyblock **subkey)
199 {
200     krb5_pa_data       ** tmppa;
201     krb5_ap_req 	* apreq;
202     krb5_error_code 	  retval;
203     krb5_data		  scratch1;
204     krb5_data 		* scratch = NULL;
205     krb5_boolean 	  foreign_server = FALSE;
206     krb5_auth_context 	  auth_context = NULL;
207     krb5_authenticator	* authenticator = NULL;
208     krb5_checksum 	* his_cksum = NULL;
209 /*    krb5_keyblock 	* key = NULL;*/
210 /*    krb5_kvno 		  kvno = 0;*/
211 
212     if (!request->padata)
213 	return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
214     for (tmppa = request->padata; *tmppa; tmppa++) {
215 	if ((*tmppa)->pa_type == KRB5_PADATA_AP_REQ)
216 	    break;
217     }
218     if (!*tmppa)			/* cannot find any AP_REQ */
219 	return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
220 
221     scratch1.length = (*tmppa)->length;
222     scratch1.data = (char *)(*tmppa)->contents;
223     if ((retval = decode_krb5_ap_req(&scratch1, &apreq)))
224 	return retval;
225 
226     if (isflagset(apreq->ap_options, AP_OPTS_USE_SESSION_KEY) ||
227 	isflagset(apreq->ap_options, AP_OPTS_MUTUAL_REQUIRED)) {
228 	krb5_klog_syslog(LOG_INFO, "TGS_REQ: SESSION KEY or MUTUAL");
229 	retval = KRB5KDC_ERR_POLICY;
230 	goto cleanup;
231     }
232 
233     /* If the "server" principal in the ticket is not something
234        in the local realm, then we must refuse to service the request
235        if the client claims to be from the local realm.
236 
237        If we don't do this, then some other realm's nasty KDC can
238        claim to be authenticating a client from our realm, and we'll
239        give out tickets concurring with it!
240 
241        we set a flag here for checking below.
242        */
243     if ((krb5_princ_realm(kdc_context, apreq->ticket->server)->length !=
244 	 krb5_princ_realm(kdc_context, tgs_server)->length) ||
245 	memcmp(krb5_princ_realm(kdc_context, apreq->ticket->server)->data,
246 	       krb5_princ_realm(kdc_context, tgs_server)->data,
247 	       krb5_princ_realm(kdc_context, tgs_server)->length))
248 	foreign_server = TRUE;
249 
250     if ((retval = krb5_auth_con_init(kdc_context, &auth_context)))
251 	goto cleanup;
252 
253     if ((retval = krb5_auth_con_setaddrs(kdc_context, auth_context, NULL,
254 					 from->address)) )
255 	goto cleanup_auth_context;
256 #ifdef USE_RCACHE
257     if ((retval = krb5_auth_con_setrcache(kdc_context, auth_context,
258 					  kdc_rcache)))
259 	goto cleanup_auth_context;
260 #endif
261 
262 /*
263     if ((retval = kdc_get_server_key(apreq->ticket, &key, &kvno)))
264 	goto cleanup_auth_context;
265 */
266 
267     /*
268      * XXX This is currently wrong but to fix it will require making a
269      * new keytab for groveling over the kdb.
270      */
271 /*
272     retval = krb5_auth_con_setuseruserkey(kdc_context, auth_context, key);
273     krb5_free_keyblock(kdc_context, key);
274     if (retval)
275 	goto cleanup_auth_context;
276 */
277 
278     if ((retval = krb5_rd_req_decoded_anyflag(kdc_context, &auth_context, apreq,
279 				      apreq->ticket->server,
280 				      kdc_active_realm->realm_keytab,
281 				      NULL, ticket))) {
282 #ifdef USE_RCACHE
283 	/*
284 	 * I'm not so sure that this is right, but it's better than nothing
285 	 * at all.
286 	 *
287 	 * If we choke in the rd_req because of the replay cache, then attempt
288 	 * to reinitialize the replay cache because somebody could have deleted
289 	 * it from underneath us (e.g. a cron job)
290 	 */
291 	if ((retval == KRB5_RC_IO_IO) ||
292 	    (retval == KRB5_RC_IO_UNKNOWN)) {
293 	    (void) krb5_rc_close(kdc_context, kdc_rcache);
294 	    kdc_rcache = (krb5_rcache) NULL;
295 	    if (!(retval = kdc_initialize_rcache(kdc_context, (char *) NULL))) {
296 		if ((retval = krb5_auth_con_setrcache(kdc_context, auth_context,
297 						      kdc_rcache)) ||
298 		    (retval = krb5_rd_req_decoded_anyflag(kdc_context, &auth_context,
299 						  apreq, apreq->ticket->server,
300 				      		 kdc_active_realm->realm_keytab,
301 						  NULL, ticket))
302 		    )
303 		    goto cleanup_auth_context;
304 	    }
305 	} else
306 	    goto cleanup_auth_context;
307 #else
308 	goto cleanup_auth_context;
309 #endif
310     }
311 
312     /* "invalid flag" tickets can must be used to validate */
313     if (isflagset((*ticket)->enc_part2->flags, TKT_FLG_INVALID)
314 	&& !isflagset(request->kdc_options, KDC_OPT_VALIDATE)) {
315         retval = KRB5KRB_AP_ERR_TKT_INVALID;
316 	goto cleanup_auth_context;
317     }
318 
319     if ((retval = krb5_auth_con_getrecvsubkey(kdc_context,
320 					      auth_context, subkey)))
321 	goto cleanup_auth_context;
322 
323     if ((retval = krb5_auth_con_getauthenticator(kdc_context, auth_context,
324 						 &authenticator)))
325 	goto cleanup_auth_context;
326 
327     /* Check for a checksum */
328     if (!(his_cksum = authenticator->checksum)) {
329 	retval = KRB5KRB_AP_ERR_INAPP_CKSUM;
330 	goto cleanup_authenticator;
331     }
332 
333     /* make sure the client is of proper lineage (see above) */
334     if (foreign_server) {
335 	krb5_data *tkt_realm = krb5_princ_realm(kdc_context,
336 						(*ticket)->enc_part2->client);
337 	krb5_data *tgs_realm = krb5_princ_realm(kdc_context, tgs_server);
338 	if (tkt_realm->length == tgs_realm->length &&
339 	    !memcmp(tkt_realm->data, tgs_realm->data, tgs_realm->length)) {
340 	    /* someone in a foreign realm claiming to be local */
341 	    krb5_klog_syslog(LOG_INFO, "PROCESS_TGS: failed lineage check");
342 	    retval = KRB5KDC_ERR_POLICY;
343 	    goto cleanup_authenticator;
344 	}
345     }
346 
347     /*
348      * Check application checksum vs. tgs request
349      *
350      * We try checksumming the req-body two different ways: first we
351      * try reaching into the raw asn.1 stream (if available), and
352      * checksum that directly; if that fails, then we try encoding
353      * using our local asn.1 library.
354      */
355     if (pkt && (fetch_asn1_field((unsigned char *) pkt->data,
356 				 1, 4, &scratch1) >= 0)) {
357 	if (comp_cksum(kdc_context, &scratch1, *ticket, his_cksum)) {
358 	    if (!(retval = encode_krb5_kdc_req_body(request, &scratch)))
359 	        retval = comp_cksum(kdc_context, scratch, *ticket, his_cksum);
360 	    krb5_free_data(kdc_context, scratch);
361 	}
362     }
363 
364 cleanup_authenticator:
365     krb5_free_authenticator(kdc_context, authenticator);
366 
367 cleanup_auth_context:
368     /* We do not want the free of the auth_context to close the rcache */
369 #ifdef USE_RCACHE
370     (void)  krb5_auth_con_setrcache(kdc_context, auth_context, 0);
371 #endif
372     krb5_auth_con_free(kdc_context, auth_context);
373 
374 cleanup:
375     krb5_free_ap_req(kdc_context, apreq);
376     return retval;
377 }
378 
379 /* XXX This function should no longer be necessary.
380  * The KDC should take the keytab associated with the realm and pass that to
381  * the krb5_rd_req_decode(). --proven
382  *
383  * It's actually still used by do_tgs_req() for u2u auth, and not too
384  * much else. -- tlyu
385  */
386 krb5_error_code
387 kdc_get_server_key(krb5_ticket *ticket, krb5_keyblock **key, krb5_kvno *kvno)
388 {
389     krb5_error_code 	  retval;
390     krb5_db_entry 	  server;
391     krb5_boolean 	  more;
392     int	nprincs;
393     krb5_key_data	* server_key;
394 
395     nprincs = 1;
396 
397     if ((retval = krb5_db_get_principal(kdc_context, ticket->server,
398 					&server, &nprincs,
399 					&more))) {
400 	return(retval);
401     }
402     if (more) {
403 	krb5_db_free_principal(kdc_context, &server, nprincs);
404 	return(KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE);
405     } else if (nprincs != 1) {
406 	char *sname;
407 
408 	krb5_db_free_principal(kdc_context, &server, nprincs);
409 	if (!krb5_unparse_name(kdc_context, ticket->server, &sname)) {
410 	    limit_string(sname);
411 	    krb5_klog_syslog(LOG_ERR,"TGS_REQ: UNKNOWN SERVER: server='%s'",
412 			     sname);
413 	    free(sname);
414 	}
415 	return(KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN);
416     }
417     retval = krb5_dbe_find_enctype(kdc_context, &server,
418 				   ticket->enc_part.enctype, -1,
419 				   ticket->enc_part.kvno, &server_key);
420     if (retval)
421 	goto errout;
422     if (!server_key) {
423 	retval = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
424 	goto errout;
425     }
426     *kvno = server_key->key_data_kvno;
427     if ((*key = (krb5_keyblock *)malloc(sizeof **key))) {
428 	retval = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock,
429 					     server_key,
430 					     *key, NULL);
431     } else
432 	retval = ENOMEM;
433 errout:
434     krb5_db_free_principal(kdc_context, &server, nprincs);
435     return retval;
436 }
437 
438 /* This probably wants to be updated if you support last_req stuff */
439 
440 static krb5_last_req_entry nolrentry = { KV5M_LAST_REQ_ENTRY, KRB5_LRQ_NONE, 0 };
441 static krb5_last_req_entry *nolrarray[] = { &nolrentry, 0 };
442 
443 krb5_error_code
444 fetch_last_req_info(krb5_db_entry *dbentry, krb5_last_req_entry ***lrentry)
445 {
446     *lrentry = nolrarray;
447     return 0;
448 }
449 
450 
451 /* XXX!  This is a temporary place-holder */
452 
453 krb5_error_code
454 check_hot_list(krb5_ticket *ticket)
455 {
456     return 0;
457 }
458 
459 
460 #define MAX_REALM_LN 500
461 
462 
463 /*
464  * subrealm - determine if r2 is a subrealm of r1
465  *
466  *            SUBREALM takes two realms, r1 and r2, and
467  *            determines if r2 is a subrealm of r1.
468  *            r2 is a subrealm of r1 if (r1 is a prefix
469  *            of r2 AND r1 and r2 begin with a /) or if
470  *            (r1 is a suffix of r2 and neither r1 nor r2
471  *            begin with a /).
472  *
473  * RETURNS:   If r2 is a subrealm, and r1 is a prefix, the number
474  *            of characters in the suffix of r2 is returned as a
475  *            negative number.
476  *
477  *            If r2 is a subrealm, and r1 is a suffix, the number
478  *            of characters in the prefix of r2 is returned as a
479  *            positive number.
480  *
481  *            If r2 is not a subrealm, SUBREALM returns 0.
482  */
483 static  int
484 subrealm(char *r1, char *r2)
485 {
486     size_t l1,l2;
487     l1 = strlen(r1);
488     l2 = strlen(r2);
489     if(l2 <= l1) return(0);
490     if((*r1 == '/') && (*r2 == '/') && (strncmp(r1,r2,l1) == 0)) return(l1-l2);
491     if((*r1 != '/') && (*r2 != '/') && (strncmp(r1,r2+l2-l1,l1) == 0))
492 	return(l2-l1);
493     return(0);
494 }
495 
496 /*
497  * add_to_transited  Adds the name of the realm which issued the
498  *                   ticket granting ticket on which the new ticket to
499  *                   be issued is based (note that this is the same as
500  *                   the realm of the server listed in the ticket
501  *                   granting ticket.
502  *
503  * ASSUMPTIONS:  This procedure assumes that the transited field from
504  *               the existing ticket granting ticket already appears
505  *               in compressed form.  It will add the new realm while
506  *               maintaining that form.   As long as each successive
507  *               realm is added using this (or a similar) routine, the
508  *               transited field will be in compressed form.  The
509  *               basis step is an empty transited field which is, by
510  *               its nature, in its most compressed form.
511  *
512  * ARGUMENTS: krb5_data *tgt_trans  Transited field from TGT
513  *            krb5_data *new_trans  The transited field for the new ticket
514  *            krb5_principal tgs    Name of ticket granting server
515  *                                  This includes the realm of the KDC
516  *                                  that issued the ticket granting
517  *                                  ticket.  This is the realm that is
518  *                                  to be added to the transited field.
519  *            krb5_principal client Name of the client
520  *            krb5_principal server The name of the requested server.
521  *                                  This may be the an intermediate
522  *                                  ticket granting server.
523  *
524  *            The last two argument are needed since they are
525  *            implicitly part of the transited field of the new ticket
526  *            even though they are not explicitly listed.
527  *
528  * RETURNS:   krb5_error_code - Success, or out of memory
529  *
530  * MODIFIES:  new_trans:  ->length will contain the length of the new
531  *                        transited field.
532  *
533  *                        If ->data was not null when this procedure
534  *                        is called, the memory referenced by ->data
535  *                        will be deallocated.
536  *
537  *                        Memory will be allocated for the new transited field
538  *                        ->data will be updated to point to the newly
539  *                        allocated memory.
540  *
541  * BUGS:  The space allocated for the new transited field is the
542  *        maximum that might be needed given the old transited field,
543  *        and the realm to be added.  This length is calculated
544  *        assuming that no compression of the new realm is possible.
545  *        This has no adverse consequences other than the allocation
546  *        of more space than required.
547  *
548  *        This procedure will not yet use the null subfield notation,
549  *        and it will get confused if it sees it.
550  *
551  *        This procedure does not check for quoted commas in realm
552  *        names.
553  */
554 
555 krb5_error_code
556 add_to_transited(krb5_data *tgt_trans, krb5_data *new_trans,
557 		 krb5_principal tgs, krb5_principal client,
558 		 krb5_principal server)
559 {
560   krb5_error_code retval;
561   char        *realm;
562   char        *trans;
563   char        *otrans, *otrans_ptr;
564 
565   /* The following are for stepping through the transited field     */
566 
567   char        prev[MAX_REALM_LN];
568   char        next[MAX_REALM_LN];
569   char        current[MAX_REALM_LN];
570   char        exp[MAX_REALM_LN];      /* Expanded current realm name     */
571 
572   int	      i;
573   int         clst, nlst;    /* count of last character in current and next */
574   int         pl, pl1;       /* prefix length                               */
575   int         added;         /* TRUE = new realm has been added             */
576 
577   if (!(realm = (char *) malloc(krb5_princ_realm(kdc_context, tgs)->length+1))) {
578     return(ENOMEM);
579   }
580   memcpy(realm, krb5_princ_realm(kdc_context, tgs)->data,
581 	 krb5_princ_realm(kdc_context, tgs)->length);
582   realm[krb5_princ_realm(kdc_context, tgs)->length] = '\0';
583 
584   if (!(otrans = (char *) malloc(tgt_trans->length+1))) {
585     free(realm);
586     return(ENOMEM);
587   }
588   memcpy(otrans, tgt_trans->data, tgt_trans->length);
589   otrans[tgt_trans->length] = '\0';
590   /* Keep track of start so we can free */
591   otrans_ptr = otrans;
592 
593   /* +1 for null,
594      +1 for extra comma which may be added between
595      +1 for potential space when leading slash in realm */
596   if (!(trans = (char *) malloc(strlen(realm) + strlen(otrans) + 3))) {
597     retval = ENOMEM;
598     goto fail;
599   }
600 
601   if (new_trans->data)  free(new_trans->data);
602   new_trans->data = trans;
603   new_trans->length = 0;
604 
605   trans[0] = '\0';
606 
607   /* For the purpose of appending, the realm preceding the first */
608   /* realm in the transited field is considered the null realm   */
609 
610   prev[0] = '\0';
611 
612   /* read field into current */
613   for (i = 0; *otrans != '\0';) {
614       if (*otrans == '\\') {
615 	  if (*(++otrans) == '\0')
616 	      break;
617 	  else
618 	      continue;
619       }
620       if (*otrans == ',') {
621 	  otrans++;
622 	  break;
623       }
624       current[i++] = *otrans++;
625       if (i >= MAX_REALM_LN) {
626 	  retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
627 	  goto fail;
628       }
629   }
630   current[i] = '\0';
631 
632   added = (krb5_princ_realm(kdc_context, client)->length == strlen(realm) &&
633            !strncmp(krb5_princ_realm(kdc_context, client)->data, realm, strlen(realm))) ||
634           (krb5_princ_realm(kdc_context, server)->length == strlen(realm) &&
635            !strncmp(krb5_princ_realm(kdc_context, server)->data, realm, strlen(realm)));
636 
637   while (current[0]) {
638 
639     /* figure out expanded form of current name */
640 
641     clst = strlen(current) - 1;
642     if (current[0] == ' ') {
643       strncpy(exp, current+1, sizeof(exp) - 1);
644       exp[sizeof(exp) - 1] = '\0';
645     }
646     else if ((current[0] == '/') && (prev[0] == '/')) {
647       strncpy(exp, prev, sizeof(exp) - 1);
648       exp[sizeof(exp) - 1] = '\0';
649       if (strlen(exp) + strlen(current) + 1 >= MAX_REALM_LN) {
650 	retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
651 	goto fail;
652       }
653       strncat(exp, current, sizeof(exp) - 1 - strlen(exp));
654     }
655     else if (current[clst] == '.') {
656       strncpy(exp, current, sizeof(exp) - 1);
657       exp[sizeof(exp) - 1] = '\0';
658       if (strlen(exp) + strlen(prev) + 1 >= MAX_REALM_LN) {
659 	retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
660 	goto fail;
661       }
662       strncat(exp, prev, sizeof(exp) - 1 - strlen(exp));
663     }
664     else {
665       strncpy(exp, current, sizeof(exp) - 1);
666       exp[sizeof(exp) - 1] = '\0';
667     }
668 
669     /* read field into next */
670     for (i = 0; *otrans != '\0';) {
671 	if (*otrans == '\\') {
672 	    if (*(++otrans) == '\0')
673 		break;
674 	    else
675 		continue;
676 	}
677 	if (*otrans == ',') {
678 	    otrans++;
679 	    break;
680 	}
681 	next[i++] = *otrans++;
682 	if (i >= MAX_REALM_LN) {
683 	    retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
684 	    goto fail;
685 	}
686     }
687     next[i] = '\0';
688     nlst = i - 1;
689 
690     if (!strcmp(exp, realm))  added = TRUE;
691 
692     /* If we still have to insert the new realm */
693 
694     if (!added) {
695 
696       /* Is the next field compressed?  If not, and if the new */
697       /* realm is a subrealm of the current realm, compress    */
698       /* the new realm, and insert immediately following the   */
699       /* current one.  Note that we can not do this if the next*/
700       /* field is already compressed since it would mess up    */
701       /* what has already been done.  In most cases, this is   */
702       /* not a problem because the realm to be added will be a */
703       /* subrealm of the next field too, and we will catch     */
704       /* it in a future iteration.                             */
705 
706       if ((next[nlst] != '.') && (next[0] != '/') &&
707           (pl = subrealm(exp, realm))) {
708         added = TRUE;
709 	current[sizeof(current) - 1] = '\0';
710 	if (strlen(current) + (pl>0?pl:-pl) + 2 >= MAX_REALM_LN) {
711 	  retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
712 	  goto fail;
713 	}
714         strncat(current, ",", sizeof(current) - 1 - strlen(current));
715         if (pl > 0) {
716           strncat(current, realm, (unsigned) pl);
717         }
718         else {
719           strncat(current, realm+strlen(realm)+pl, (unsigned) (-pl));
720         }
721       }
722 
723       /* Whether or not the next field is compressed, if the    */
724       /* realm to be added is a superrealm of the current realm,*/
725       /* then the current realm can be compressed.  First the   */
726       /* realm to be added must be compressed relative to the   */
727       /* previous realm (if possible), and then the current     */
728       /* realm compressed relative to the new realm.  Note that */
729       /* if the realm to be added is also a superrealm of the   */
730       /* previous realm, it would have been added earlier, and  */
731       /* we would not reach this step this time around.         */
732 
733       else if ((pl = subrealm(realm, exp))) {
734         added      = TRUE;
735         current[0] = '\0';
736         if ((pl1 = subrealm(prev,realm))) {
737 	  if (strlen(current) + (pl1>0?pl1:-pl1) + 1 >= MAX_REALM_LN) {
738 	    retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
739 	    goto fail;
740 	  }
741           if (pl1 > 0) {
742             strncat(current, realm, (unsigned) pl1);
743           }
744           else {
745             strncat(current, realm+strlen(realm)+pl1, (unsigned) (-pl1));
746           }
747         }
748         else { /* If not a subrealm */
749           if ((realm[0] == '/') && prev[0]) {
750 	    if (strlen(current) + 2 >= MAX_REALM_LN) {
751 	      retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
752 	      goto fail;
753 	    }
754 	    strncat(current, " ", sizeof(current) - 1 - strlen(current));
755 	    current[sizeof(current) - 1] = '\0';
756           }
757 	  if (strlen(current) + strlen(realm) + 1 >= MAX_REALM_LN) {
758 	    retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
759 	    goto fail;
760 	  }
761           strncat(current, realm, sizeof(current) - 1 - strlen(current));
762 	  current[sizeof(current) - 1] = '\0';
763         }
764 	if (strlen(current) + (pl>0?pl:-pl) + 2 >= MAX_REALM_LN) {
765 	  retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
766 	  goto fail;
767 	}
768         strncat(current,",", sizeof(current) - 1 - strlen(current));
769 	current[sizeof(current) - 1] = '\0';
770         if (pl > 0) {
771           strncat(current, exp, (unsigned) pl);
772         }
773         else {
774           strncat(current, exp+strlen(exp)+pl, (unsigned)(-pl));
775         }
776       }
777     }
778 
779     if (new_trans->length != 0) {
780       if (strlen(trans) + 2 >= MAX_REALM_LN) {
781 	retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
782 	goto fail;
783       }
784       strcat(trans, ",");
785     }
786     if (strlen(trans) + strlen(current) + 1 >= MAX_REALM_LN) {
787       retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
788       goto fail;
789     }
790     strcat(trans, current);
791     new_trans->length = strlen(trans);
792 
793     strncpy(prev, exp, sizeof(prev) - 1);
794     prev[sizeof(prev) - 1] = '\0';
795     strncpy(current, next, sizeof(current) - 1);
796     current[sizeof(current) - 1] = '\0';
797   }
798 
799   if (!added) {
800     if (new_trans->length != 0) {
801       if (strlen(trans) + 2 >= MAX_REALM_LN) {
802 	retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
803 	goto fail;
804       }
805       strcat(trans, ",");
806     }
807     if((realm[0] == '/') && trans[0]) {
808       if (strlen(trans) + 2 >= MAX_REALM_LN) {
809 	retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
810 	goto fail;
811       }
812       strcat(trans, " ");
813     }
814     if (strlen(trans) + strlen(realm) + 1 >= MAX_REALM_LN) {
815       retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
816       goto fail;
817     }
818     strcat(trans, realm);
819     new_trans->length = strlen(trans);
820   }
821 
822   retval = 0;
823 fail:
824   free(realm);
825   free(otrans_ptr);
826   return (retval);
827 }
828 
829 /*
830  * Routines that validate a AS request; checks a lot of things.  :-)
831  *
832  * Returns a Kerberos protocol error number, which is _not_ the same
833  * as a com_err error number!
834  */
835 #define AS_INVALID_OPTIONS (KDC_OPT_FORWARDED | KDC_OPT_PROXY |\
836 KDC_OPT_VALIDATE | KDC_OPT_RENEW | KDC_OPT_ENC_TKT_IN_SKEY)
837 int
838 validate_as_request(register krb5_kdc_req *request, krb5_db_entry client,
839 		    krb5_db_entry server, krb5_timestamp kdc_time,
840 		    const char **status)
841 {
842     int		errcode;
843 
844     /*
845      * If an option is set that is only allowed in TGS requests, complain.
846      */
847     if (request->kdc_options & AS_INVALID_OPTIONS) {
848 	*status = "INVALID AS OPTIONS";
849 	return KDC_ERR_BADOPTION;
850     }
851 
852     /* The client's password must not be expired, unless the server is
853       a KRB5_KDC_PWCHANGE_SERVICE. */
854     if (client.pw_expiration && client.pw_expiration < kdc_time &&
855 	!isflagset(server.attributes, KRB5_KDB_PWCHANGE_SERVICE)) {
856 	*status = "CLIENT KEY EXPIRED";
857 #ifdef KRBCONF_VAGUE_ERRORS
858 	return(KRB_ERR_GENERIC);
859 #else
860 	return(KDC_ERR_KEY_EXP);
861 #endif
862     }
863 
864     /* The client must not be expired */
865     if (client.expiration && client.expiration < kdc_time) {
866 	*status = "CLIENT EXPIRED";
867 #ifdef KRBCONF_VAGUE_ERRORS
868 	return(KRB_ERR_GENERIC);
869 #else
870 	return(KDC_ERR_NAME_EXP);
871 #endif
872     }
873 
874     /* The server must not be expired */
875     if (server.expiration && server.expiration < kdc_time) {
876 	*status = "SERVICE EXPIRED";
877 	    return(KDC_ERR_SERVICE_EXP);
878     }
879 
880     /*
881      * If the client requires password changing, then only allow the
882      * pwchange service.
883      */
884     if (isflagset(client.attributes, KRB5_KDB_REQUIRES_PWCHANGE) &&
885 	!isflagset(server.attributes, KRB5_KDB_PWCHANGE_SERVICE)) {
886 	*status = "REQUIRED PWCHANGE";
887 	return(KDC_ERR_KEY_EXP);
888     }
889 
890     /* Client and server must allow postdating tickets */
891     if ((isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) ||
892 	 isflagset(request->kdc_options, KDC_OPT_POSTDATED)) &&
893 	(isflagset(client.attributes, KRB5_KDB_DISALLOW_POSTDATED) ||
894 	 isflagset(server.attributes, KRB5_KDB_DISALLOW_POSTDATED))) {
895 	*status = "POSTDATE NOT ALLOWED";
896 	return(KDC_ERR_CANNOT_POSTDATE);
897     }
898 
899     /* Client and server must allow forwardable tickets */
900     if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) &&
901 	(isflagset(client.attributes, KRB5_KDB_DISALLOW_FORWARDABLE) ||
902 	 isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))) {
903 	*status = "FORWARDABLE NOT ALLOWED";
904 	return(KDC_ERR_POLICY);
905     }
906 
907     /* Client and server must allow renewable tickets */
908     if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE) &&
909 	(isflagset(client.attributes, KRB5_KDB_DISALLOW_RENEWABLE) ||
910 	 isflagset(server.attributes, KRB5_KDB_DISALLOW_RENEWABLE))) {
911 	*status = "RENEWABLE NOT ALLOWED";
912 	return(KDC_ERR_POLICY);
913     }
914 
915     /* Client and server must allow proxiable tickets */
916     if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE) &&
917 	(isflagset(client.attributes, KRB5_KDB_DISALLOW_PROXIABLE) ||
918 	 isflagset(server.attributes, KRB5_KDB_DISALLOW_PROXIABLE))) {
919 	*status = "PROXIABLE NOT ALLOWED";
920 	return(KDC_ERR_POLICY);
921     }
922 
923     /* Check to see if client is locked out */
924     if (isflagset(client.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
925 	*status = "CLIENT LOCKED OUT";
926 	return(KDC_ERR_C_PRINCIPAL_UNKNOWN);
927     }
928 
929     /* Check to see if server is locked out */
930     if (isflagset(server.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
931 	*status = "SERVICE LOCKED OUT";
932 	return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
933     }
934 
935     /* Check to see if server is allowed to be a service */
936     if (isflagset(server.attributes, KRB5_KDB_DISALLOW_SVR)) {
937 	*status = "SERVICE NOT ALLOWED";
938 	return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
939     }
940 
941     /*
942      * Check against local policy
943      */
944     errcode = against_local_policy_as(request, server, client,
945 				      kdc_time, status);
946     if (errcode)
947 	return errcode;
948 
949     return 0;
950 }
951 
952 #define ASN1_ID_CLASS	(0xc0)
953 #define ASN1_ID_TYPE    (0x20)
954 #define ASN1_ID_TAG	(0x1f)
955 #define ASN1_CLASS_UNIV	(0)
956 #define ASN1_CLASS_APP	(1)
957 #define ASN1_CLASS_CTX	(2)
958 #define ASN1_CLASS_PRIV	(3)
959 #define asn1_id_constructed(x) 	(x & ASN1_ID_TYPE)
960 #define asn1_id_primitive(x) 	(!asn1_id_constructed(x))
961 #define asn1_id_class(x)	((x & ASN1_ID_CLASS) >> 6)
962 #define asn1_id_tag(x)		(x & ASN1_ID_TAG)
963 
964 /*
965  * asn1length - return encoded length of value.
966  *
967  * passed a pointer into the asn.1 stream, which is updated
968  * to point right after the length bits.
969  *
970  * returns -1 on failure.
971  */
972 static int
973 asn1length(unsigned char **astream)
974 {
975     int length;		/* resulting length */
976     int sublen;		/* sublengths */
977     int blen;		/* bytes of length */
978     unsigned char *p;	/* substring searching */
979 
980     if (**astream & 0x80) {
981         blen = **astream & 0x7f;
982 	if (blen > 3) {
983 	   return(-1);
984 	}
985 	for (++*astream, length = 0; blen; ++*astream, blen--) {
986 	    length = (length << 8) | **astream;
987 	}
988 	if (length == 0) {
989 		/* indefinite length, figure out by hand */
990 	    p = *astream;
991 	    p++;
992 	    while (1) {
993 		/* compute value length. */
994 		if ((sublen = asn1length(&p)) < 0) {
995 		    return(-1);
996 		}
997 		p += sublen;
998                 /* check for termination */
999 		if ((!*p++) && (!*p)) {
1000 		    p++;
1001 		    break;
1002 		}
1003 	    }
1004 	    length = p - *astream;
1005 	}
1006     } else {
1007 	length = **astream;
1008 	++*astream;
1009     }
1010    return(length);
1011 }
1012 
1013 /*
1014  * fetch_asn1_field - return raw asn.1 stream of subfield.
1015  *
1016  * this routine is passed a context-dependent tag number and "level" and returns
1017  * the size and length of the corresponding level subfield.
1018  *
1019  * levels and are numbered starting from 1.
1020  *
1021  * returns 0 on success, -1 otherwise.
1022  */
1023 int
1024 fetch_asn1_field(unsigned char *astream, unsigned int level,
1025 		 unsigned int field, krb5_data *data)
1026 {
1027     unsigned char *estream;	/* end of stream */
1028     int classes;		/* # classes seen so far this level */
1029     unsigned int levels = 0;		/* levels seen so far */
1030     int lastlevel = 1000;       /* last level seen */
1031     int length;			/* various lengths */
1032     int tag;			/* tag number */
1033     unsigned char savelen;      /* saved length of our field */
1034 
1035     classes = -1;
1036     /* we assume that the first identifier/length will tell us
1037        how long the entire stream is. */
1038     astream++;
1039     estream = astream;
1040     if ((length = asn1length(&astream)) < 0) {
1041 	return(-1);
1042     }
1043     estream += length;
1044     /* search down the stream, checking identifiers.  we process identifiers
1045        until we hit the "level" we want, and then process that level for our
1046        subfield, always making sure we don't go off the end of the stream.  */
1047     while (astream < estream) {
1048 	if (!asn1_id_constructed(*astream)) {
1049 	    return(-1);
1050 	}
1051         if (asn1_id_class(*astream) == ASN1_CLASS_CTX) {
1052             if ((tag = (int)asn1_id_tag(*astream)) <= lastlevel) {
1053                 levels++;
1054                 classes = -1;
1055             }
1056             lastlevel = tag;
1057             if (levels == level) {
1058 	        /* in our context-dependent class, is this the one we're looking for ? */
1059 	        if (tag == field) {
1060 		    /* return length and data */
1061 		    astream++;
1062 		    savelen = *astream;
1063 		    if ((data->length = asn1length(&astream)) < 0) {
1064 		        return(-1);
1065 	 	    }
1066 		    /* if the field length is indefinite, we will have to subtract two
1067                        (terminating octets) from the length returned since we don't want
1068                        to pass any info from the "wrapper" back.  asn1length will always return
1069                        the *total* length of the field, not just what's contained in it */
1070 		    if ((savelen & 0xff) == 0x80) {
1071 		      data->length -=2 ;
1072 		    }
1073 		    data->data = (char *)astream;
1074 		    return(0);
1075 	        } else if (tag <= classes) {
1076 		    /* we've seen this class before, something must be wrong */
1077 		    return(-1);
1078 	        } else {
1079 		    classes = tag;
1080 	        }
1081 	    }
1082         }
1083         /* if we're not on our level yet, process this value.  otherwise skip over it */
1084 	astream++;
1085 	if ((length = asn1length(&astream)) < 0) {
1086 	    return(-1);
1087 	}
1088 	if (levels == level) {
1089 	    astream += length;
1090 	}
1091     }
1092     return(-1);
1093 }
1094 
1095 /*
1096  * Routines that validate a TGS request; checks a lot of things.  :-)
1097  *
1098  * Returns a Kerberos protocol error number, which is _not_ the same
1099  * as a com_err error number!
1100  */
1101 #define TGS_OPTIONS_HANDLED (KDC_OPT_FORWARDABLE | KDC_OPT_FORWARDED | \
1102 			     KDC_OPT_PROXIABLE | KDC_OPT_PROXY | \
1103 			     KDC_OPT_ALLOW_POSTDATE | KDC_OPT_POSTDATED | \
1104 			     KDC_OPT_RENEWABLE | KDC_OPT_RENEWABLE_OK | \
1105 			     KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_RENEW | \
1106 			     KDC_OPT_VALIDATE)
1107 
1108 #define NO_TGT_OPTION (KDC_OPT_FORWARDED | KDC_OPT_PROXY | KDC_OPT_RENEW | \
1109 		       KDC_OPT_VALIDATE)
1110 
1111 int
1112 validate_tgs_request(register krb5_kdc_req *request, krb5_db_entry server,
1113 		     krb5_ticket *ticket, krb5_timestamp kdc_time,
1114 		     const char **status)
1115 {
1116     int		errcode;
1117     int		st_idx = 0;
1118 
1119     /*
1120      * If an illegal option is set, ignore it.
1121      */
1122     request->kdc_options &= TGS_OPTIONS_HANDLED;
1123 
1124     /* Check to see if server has expired */
1125     if (server.expiration && server.expiration < kdc_time) {
1126 	*status = "SERVICE EXPIRED";
1127 	return(KDC_ERR_SERVICE_EXP);
1128     }
1129 
1130     /*
1131      * Verify that the server principal in authdat->ticket is correct
1132      * (either the ticket granting service or the service that was
1133      * originally requested)
1134      */
1135     if (request->kdc_options & NO_TGT_OPTION) {
1136 	if (!krb5_principal_compare(kdc_context, ticket->server, request->server)) {
1137 	    *status = "SERVER DIDN'T MATCH TICKET FOR RENEW/FORWARD/ETC";
1138 	    return(KDC_ERR_SERVER_NOMATCH);
1139 	}
1140     } else {
1141 	/*
1142 	 * OK, we need to validate the krbtgt service in the ticket.
1143 	 *
1144 	 * The krbtgt service is of the form:
1145 	 * 		krbtgt/realm-A@realm-B
1146 	 *
1147 	 * Realm A is the "server realm"; the realm of the
1148 	 * server of the requested ticket must match this realm.
1149 	 * Of course, it should be a realm serviced by this KDC.
1150 	 *
1151 	 * Realm B is the "client realm"; this is what should be
1152 	 * added to the transited field.  (which is done elsewhere)
1153 	 */
1154 
1155 	/* Make sure there are two components... */
1156 	if (krb5_princ_size(kdc_context, ticket->server) != 2) {
1157 	    *status = "BAD TGS SERVER LENGTH";
1158 	    return KRB_AP_ERR_NOT_US;
1159 	}
1160 	/* ...that the first component is krbtgt... */
1161 	if (!krb5_is_tgs_principal(ticket->server)) {
1162 	    *status = "BAD TGS SERVER NAME";
1163 	    return KRB_AP_ERR_NOT_US;
1164 	}
1165 	/* ...and that the second component matches the server realm... */
1166 	if ((krb5_princ_size(kdc_context, ticket->server) <= 1) ||
1167 	    (krb5_princ_component(kdc_context, ticket->server, 1)->length !=
1168 	     krb5_princ_realm(kdc_context, request->server)->length) ||
1169 	    memcmp(krb5_princ_component(kdc_context, ticket->server, 1)->data,
1170 		   krb5_princ_realm(kdc_context, request->server)->data,
1171 		   krb5_princ_realm(kdc_context, request->server)->length)) {
1172 	    *status = "BAD TGS SERVER INSTANCE";
1173 	    return KRB_AP_ERR_NOT_US;
1174 	}
1175 	/* XXX add check that second component must match locally
1176 	 * supported realm?
1177 	 */
1178 
1179 	/* Server must allow TGS based issuances */
1180 	if (isflagset(server.attributes, KRB5_KDB_DISALLOW_TGT_BASED)) {
1181 	    *status = "TGT BASED NOT ALLOWED";
1182 	    return(KDC_ERR_POLICY);
1183 	}
1184     }
1185 
1186     /* TGS must be forwardable to get forwarded or forwardable ticket */
1187     if ((isflagset(request->kdc_options, KDC_OPT_FORWARDED) ||
1188 	 isflagset(request->kdc_options, KDC_OPT_FORWARDABLE)) &&
1189 	!isflagset(ticket->enc_part2->flags, TKT_FLG_FORWARDABLE)) {
1190 	*status = "TGT NOT FORWARDABLE";
1191 
1192 	return KDC_ERR_BADOPTION;
1193     }
1194 
1195     /* TGS must be proxiable to get proxiable ticket */
1196     if ((isflagset(request->kdc_options, KDC_OPT_PROXY) ||
1197 	 isflagset(request->kdc_options, KDC_OPT_PROXIABLE)) &&
1198 	!isflagset(ticket->enc_part2->flags, TKT_FLG_PROXIABLE)) {
1199 	*status = "TGT NOT PROXIABLE";
1200 	return KDC_ERR_BADOPTION;
1201     }
1202 
1203     /* TGS must allow postdating to get postdated ticket */
1204     if ((isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) ||
1205 	  isflagset(request->kdc_options, KDC_OPT_POSTDATED)) &&
1206 	!isflagset(ticket->enc_part2->flags, TKT_FLG_MAY_POSTDATE)) {
1207 	*status = "TGT NOT POSTDATABLE";
1208 	return KDC_ERR_BADOPTION;
1209     }
1210 
1211     /* can only validate invalid tix */
1212     if (isflagset(request->kdc_options, KDC_OPT_VALIDATE) &&
1213 	!isflagset(ticket->enc_part2->flags, TKT_FLG_INVALID)) {
1214 	*status = "VALIDATE VALID TICKET";
1215 	return KDC_ERR_BADOPTION;
1216     }
1217 
1218     /* can only renew renewable tix */
1219     if ((isflagset(request->kdc_options, KDC_OPT_RENEW) ||
1220 	  isflagset(request->kdc_options, KDC_OPT_RENEWABLE)) &&
1221 	!isflagset(ticket->enc_part2->flags, TKT_FLG_RENEWABLE)) {
1222 	*status = "TICKET NOT RENEWABLE";
1223 	return KDC_ERR_BADOPTION;
1224     }
1225 
1226     /* can not proxy ticket granting tickets */
1227     if (isflagset(request->kdc_options, KDC_OPT_PROXY) &&
1228 	(!request->server->data ||
1229 	 request->server->data[0].length != KRB5_TGS_NAME_SIZE ||
1230 	 memcmp(request->server->data[0].data, KRB5_TGS_NAME,
1231 		KRB5_TGS_NAME_SIZE))) {
1232 	*status = "CAN'T PROXY TGT";
1233 	return KDC_ERR_BADOPTION;
1234     }
1235 
1236     /* Server must allow forwardable tickets */
1237     if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) &&
1238 	isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE)) {
1239 	*status = "NON-FORWARDABLE TICKET";
1240 	return(KDC_ERR_POLICY);
1241     }
1242 
1243     /* Server must allow renewable tickets */
1244     if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE) &&
1245 	isflagset(server.attributes, KRB5_KDB_DISALLOW_RENEWABLE)) {
1246 	*status = "NON-RENEWABLE TICKET";
1247 	return(KDC_ERR_POLICY);
1248     }
1249 
1250     /* Server must allow proxiable tickets */
1251     if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE) &&
1252 	isflagset(server.attributes, KRB5_KDB_DISALLOW_PROXIABLE)) {
1253 	*status = "NON-PROXIABLE TICKET";
1254 	return(KDC_ERR_POLICY);
1255     }
1256 
1257     /* Server must allow postdated tickets */
1258     if (isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) &&
1259 	isflagset(server.attributes, KRB5_KDB_DISALLOW_POSTDATED)) {
1260 	*status = "NON-POSTDATABLE TICKET";
1261 	return(KDC_ERR_CANNOT_POSTDATE);
1262     }
1263 
1264     /* Server must allow DUP SKEY requests */
1265     if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY) &&
1266 	isflagset(server.attributes, KRB5_KDB_DISALLOW_DUP_SKEY)) {
1267 	*status = "DUP_SKEY DISALLOWED";
1268 	return(KDC_ERR_POLICY);
1269     }
1270 
1271     /* Server must not be locked out */
1272     if (isflagset(server.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
1273 	*status = "SERVER LOCKED OUT";
1274 	return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
1275     }
1276 
1277     /* Server must be allowed to be a service */
1278     if (isflagset(server.attributes, KRB5_KDB_DISALLOW_SVR)) {
1279 	*status = "SERVER NOT ALLOWED";
1280 	return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
1281     }
1282 
1283     /* Check the hot list */
1284     if (check_hot_list(ticket)) {
1285 	*status = "HOT_LIST";
1286 	return(KRB_AP_ERR_REPEAT);
1287     }
1288 
1289     /* Check the start time vs. the KDC time */
1290     if (isflagset(request->kdc_options, KDC_OPT_VALIDATE)) {
1291 	if (ticket->enc_part2->times.starttime > kdc_time) {
1292 	    *status = "NOT_YET_VALID";
1293 	    return(KRB_AP_ERR_TKT_NYV);
1294 	}
1295     }
1296 
1297     /*
1298      * Check the renew_till time.  The endtime was already
1299      * been checked in the initial authentication check.
1300      */
1301     if (isflagset(request->kdc_options, KDC_OPT_RENEW) &&
1302 	(ticket->enc_part2->times.renew_till < kdc_time)) {
1303 	*status = "TKT_EXPIRED";
1304 	return(KRB_AP_ERR_TKT_EXPIRED);
1305     }
1306 
1307     /*
1308      * Checks for ENC_TKT_IN_SKEY:
1309      *
1310      * (1) Make sure the second ticket exists
1311      * (2) Make sure it is a ticket granting ticket
1312      */
1313     if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) {
1314 	if (!request->second_ticket ||
1315 	    !request->second_ticket[st_idx]) {
1316 	    *status = "NO_2ND_TKT";
1317 	    return(KDC_ERR_BADOPTION);
1318 	}
1319 	if (!krb5_principal_compare(kdc_context, request->second_ticket[st_idx]->server,
1320 				    tgs_server)) {
1321 		*status = "2ND_TKT_NOT_TGS";
1322 		return(KDC_ERR_POLICY);
1323 	}
1324 	st_idx++;
1325     }
1326 
1327     /* Check for hardware preauthentication */
1328     if (isflagset(server.attributes, KRB5_KDB_REQUIRES_HW_AUTH) &&
1329 	!isflagset(ticket->enc_part2->flags,TKT_FLG_HW_AUTH)) {
1330 	*status = "NO HW PREAUTH";
1331 	return KRB_ERR_GENERIC;
1332     }
1333 
1334     /* Check for any kind of preauthentication */
1335     if (isflagset(server.attributes, KRB5_KDB_REQUIRES_PRE_AUTH) &&
1336 	!isflagset(ticket->enc_part2->flags, TKT_FLG_PRE_AUTH)) {
1337 	*status = "NO PREAUTH";
1338 	return KRB_ERR_GENERIC;
1339     }
1340 
1341     /*
1342      * Check local policy
1343      */
1344     errcode = against_local_policy_tgs(request, server, ticket, status);
1345     if (errcode)
1346 	return errcode;
1347 
1348 
1349     return 0;
1350 }
1351 
1352 /*
1353  * This function returns 1 if the dbentry has a key for a specified
1354  * keytype, and 0 if not.
1355  */
1356 int
1357 dbentry_has_key_for_enctype(krb5_context context, krb5_db_entry *client,
1358 			    krb5_enctype enctype)
1359 {
1360     krb5_error_code	retval;
1361     krb5_key_data	*datap;
1362 
1363     retval = krb5_dbe_find_enctype(context, client, enctype,
1364 				   -1, 0, &datap);
1365     if (retval)
1366 	return 0;
1367     else
1368 	return 1;
1369 }
1370 
1371 /*
1372  * This function returns 1 if the entity referenced by this
1373  * structure can support the a particular encryption system, and 0 if
1374  * not.
1375  *
1376  * XXX eventually this information should be looked up in the
1377  * database.  Since it isn't, we use some hueristics and attribute
1378  * options bits for now.
1379  */
1380 int
1381 dbentry_supports_enctype(krb5_context context, krb5_db_entry *client,
1382 			 krb5_enctype enctype)
1383 {
1384     /*
1385      * If it's DES_CBC_MD5, there's a bit in the attribute mask which
1386      * checks to see if we support it.
1387      *
1388      * In theory everything's supposed to support DES_CBC_MD5, but
1389      * that's not the reality....
1390      */
1391 
1392     /*
1393      * We are assuming that all entries can support MD5; this information
1394      * need not be kept in the database.
1395     */
1396 
1397 
1398     if (enctype == ENCTYPE_DES_CBC_MD5)
1399 	return 1;
1400 
1401     /*
1402      * XXX we assume everything can understand DES_CBC_CRC
1403      */
1404     if (enctype == ENCTYPE_DES_CBC_CRC)
1405 	return 1;
1406 
1407     /*
1408      * If we have a key for the encryption system, we assume it's
1409      * supported.
1410      */
1411     return dbentry_has_key_for_enctype(context, client, enctype);
1412 }
1413 
1414 /*
1415  * This function returns the keytype which should be selected for the
1416  * session key.  It is based on the ordered list which the user
1417  * requested, and what the KDC and the application server can support.
1418  */
1419 krb5_enctype
1420 select_session_keytype(krb5_context context, krb5_db_entry *server,
1421 		       int nktypes, krb5_enctype *ktype)
1422 {
1423     int		i;
1424 
1425     for (i = 0; i < nktypes; i++) {
1426 	if (!krb5_c_valid_enctype(ktype[i]))
1427 	    continue;
1428 
1429 	if (!krb5_is_permitted_enctype(context, ktype[i]))
1430 	    continue;
1431 
1432 	if (dbentry_supports_enctype(context, server, ktype[i]))
1433 	    return ktype[i];
1434     }
1435     return 0;
1436 }
1437 
1438 /*
1439  * This function returns salt information for a particular client_key
1440  */
1441 krb5_error_code
1442 get_salt_from_key(krb5_context context, krb5_principal client,
1443 		  krb5_key_data *client_key, krb5_data *salt)
1444 {
1445     krb5_error_code		retval;
1446     krb5_data *			realm;
1447 
1448     salt->data = 0;
1449     salt->length = SALT_TYPE_NO_LENGTH;
1450 
1451     if (client_key->key_data_ver == 1)
1452 	return 0;
1453 
1454     switch (client_key->key_data_type[1]) {
1455     case KRB5_KDB_SALTTYPE_NORMAL:
1456 	break;
1457     case KRB5_KDB_SALTTYPE_V4:
1458 	/* send an empty (V4) salt */
1459 	salt->data = 0;
1460 	salt->length = 0;
1461 	break;
1462     case KRB5_KDB_SALTTYPE_NOREALM:
1463 	if ((retval = krb5_principal2salt_norealm(context, client, salt)))
1464 	    return retval;
1465 	break;
1466     case KRB5_KDB_SALTTYPE_AFS3:
1467 	/* send the same salt as with onlyrealm - but with no type info,
1468 	   we just hope they figure it out on the other end. */
1469 	/* fall through to onlyrealm: */
1470     case KRB5_KDB_SALTTYPE_ONLYREALM:
1471 	realm = krb5_princ_realm(context, client);
1472 	salt->length = realm->length;
1473 	if ((salt->data = malloc(realm->length)) == NULL)
1474 	    return ENOMEM;
1475 	memcpy(salt->data, realm->data, realm->length);
1476 	break;
1477     case KRB5_KDB_SALTTYPE_SPECIAL:
1478 	salt->length = client_key->key_data_length[1];
1479 	if ((salt->data = malloc(salt->length)) == NULL)
1480 	    return ENOMEM;
1481 	memcpy(salt->data, client_key->key_data_contents[1], salt->length);
1482 	break;
1483     }
1484     return 0;
1485 }
1486 
1487 /*
1488  * Limit strings to a "reasonable" length to prevent crowding out of
1489  * other useful information in the log entry
1490  */
1491 #define NAME_LENGTH_LIMIT 128
1492 
1493 void limit_string(char *name)
1494 {
1495 	int	i;
1496 
1497 	if (!name)
1498 		return;
1499 
1500 	if (strlen(name) < NAME_LENGTH_LIMIT)
1501 		return;
1502 
1503 	i = NAME_LENGTH_LIMIT-4;
1504 	name[i++] = '.';
1505 	name[i++] = '.';
1506 	name[i++] = '.';
1507 	name[i] = '\0';
1508 	return;
1509 }
1510 
1511 /*
1512  * L10_2 = log10(2**x), rounded up; log10(2) ~= 0.301.
1513  */
1514 #define L10_2(x) ((int)(((x * 301) + 999) / 1000))
1515 
1516 /*
1517  * Max length of sprintf("%ld") for an int of type T; includes leading
1518  * minus sign and terminating NUL.
1519  */
1520 #define D_LEN(t) (L10_2(sizeof(t) * CHAR_BIT) + 2)
1521 
1522 void
1523 ktypes2str(char *s, size_t len, int nktypes, krb5_enctype *ktype)
1524 {
1525     int i;
1526     char stmp[D_LEN(krb5_enctype) + 1];
1527     char *p;
1528 
1529     if (nktypes < 0
1530 	|| len < (sizeof(" etypes {...}") + D_LEN(int))) {
1531 	*s = '\0';
1532 	return;
1533     }
1534 
1535     sprintf(s, "%d etypes {", nktypes);
1536     for (i = 0; i < nktypes; i++) {
1537 	sprintf(stmp, "%s%ld", i ? " " : "", (long)ktype[i]);
1538 	if (strlen(s) + strlen(stmp) + sizeof("}") > len)
1539 	    break;
1540 	strcat(s, stmp);
1541     }
1542     if (i < nktypes) {
1543 	/*
1544 	 * We broke out of the loop. Try to truncate the list.
1545 	 */
1546 	p = s + strlen(s);
1547 	while (p - s + sizeof("...}") > len) {
1548 	    while (p > s && *p != ' ' && *p != '{')
1549 		*p-- = '\0';
1550 	    if (p > s && *p == ' ') {
1551 		*p-- = '\0';
1552 		continue;
1553 	    }
1554 	}
1555 	strcat(s, "...");
1556     }
1557     strcat(s, "}");
1558     return;
1559 }
1560 
1561 void
1562 rep_etypes2str(char *s, size_t len, krb5_kdc_rep *rep)
1563 {
1564     char stmp[sizeof("ses=") + D_LEN(krb5_enctype)];
1565 
1566     if (len < (3 * D_LEN(krb5_enctype)
1567 	       + sizeof("etypes {rep= tkt= ses=}"))) {
1568 	*s = '\0';
1569 	return;
1570     }
1571 
1572     sprintf(s, "etypes {rep=%ld", (long)rep->enc_part.enctype);
1573 
1574     if (rep->ticket != NULL) {
1575 	sprintf(stmp, " tkt=%ld", (long)rep->ticket->enc_part.enctype);
1576 	strcat(s, stmp);
1577     }
1578 
1579     if (rep->ticket != NULL
1580 	&& rep->ticket->enc_part2 != NULL
1581 	&& rep->ticket->enc_part2->session != NULL) {
1582 	sprintf(stmp, " ses=%ld",
1583 		(long)rep->ticket->enc_part2->session->enctype);
1584 	strcat(s, stmp);
1585     }
1586     strcat(s, "}");
1587     return;
1588 }
1589