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