xref: /illumos-gate/usr/src/cmd/krb5/krb5kdc/kdc_util.c (revision cb6207858a9fcc2feaee22e626912fba281ac969)
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 	    krb5_klog_syslog(LOG_ERR,"TGS_REQ: UNKNOWN SERVER: server='%s'",
411 			     sname);
412 	    free(sname);
413 	}
414 	return(KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN);
415     }
416     retval = krb5_dbe_find_enctype(kdc_context, &server,
417 				   ticket->enc_part.enctype, -1,
418 				   ticket->enc_part.kvno, &server_key);
419     if (retval)
420 	goto errout;
421     if (!server_key) {
422 	retval = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
423 	goto errout;
424     }
425     *kvno = server_key->key_data_kvno;
426     if ((*key = (krb5_keyblock *)malloc(sizeof **key))) {
427 	retval = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock,
428 					     server_key,
429 					     *key, NULL);
430     } else
431 	retval = ENOMEM;
432 errout:
433     krb5_db_free_principal(kdc_context, &server, nprincs);
434     return retval;
435 }
436 
437 /* This probably wants to be updated if you support last_req stuff */
438 
439 static krb5_last_req_entry nolrentry = { KV5M_LAST_REQ_ENTRY, KRB5_LRQ_NONE, 0 };
440 static krb5_last_req_entry *nolrarray[] = { &nolrentry, 0 };
441 
442 krb5_error_code
443 fetch_last_req_info(krb5_db_entry *dbentry, krb5_last_req_entry ***lrentry)
444 {
445     *lrentry = nolrarray;
446     return 0;
447 }
448 
449 
450 /* XXX!  This is a temporary place-holder */
451 
452 krb5_error_code
453 check_hot_list(krb5_ticket *ticket)
454 {
455     return 0;
456 }
457 
458 
459 #define MAX_REALM_LN 500
460 
461 
462 /*
463  * subrealm - determine if r2 is a subrealm of r1
464  *
465  *            SUBREALM takes two realms, r1 and r2, and
466  *            determines if r2 is a subrealm of r1.
467  *            r2 is a subrealm of r1 if (r1 is a prefix
468  *            of r2 AND r1 and r2 begin with a /) or if
469  *            (r1 is a suffix of r2 and neither r1 nor r2
470  *            begin with a /).
471  *
472  * RETURNS:   If r2 is a subrealm, and r1 is a prefix, the number
473  *            of characters in the suffix of r2 is returned as a
474  *            negative number.
475  *
476  *            If r2 is a subrealm, and r1 is a suffix, the number
477  *            of characters in the prefix of r2 is returned as a
478  *            positive number.
479  *
480  *            If r2 is not a subrealm, SUBREALM returns 0.
481  */
482 static  int
483 subrealm(char *r1, char *r2)
484 {
485     size_t l1,l2;
486     l1 = strlen(r1);
487     l2 = strlen(r2);
488     if(l2 <= l1) return(0);
489     if((*r1 == '/') && (*r2 == '/') && (strncmp(r1,r2,l1) == 0)) return(l1-l2);
490     if((*r1 != '/') && (*r2 != '/') && (strncmp(r1,r2+l2-l1,l1) == 0))
491 	return(l2-l1);
492     return(0);
493 }
494 
495 /*
496  * add_to_transited  Adds the name of the realm which issued the
497  *                   ticket granting ticket on which the new ticket to
498  *                   be issued is based (note that this is the same as
499  *                   the realm of the server listed in the ticket
500  *                   granting ticket.
501  *
502  * ASSUMPTIONS:  This procedure assumes that the transited field from
503  *               the existing ticket granting ticket already appears
504  *               in compressed form.  It will add the new realm while
505  *               maintaining that form.   As long as each successive
506  *               realm is added using this (or a similar) routine, the
507  *               transited field will be in compressed form.  The
508  *               basis step is an empty transited field which is, by
509  *               its nature, in its most compressed form.
510  *
511  * ARGUMENTS: krb5_data *tgt_trans  Transited field from TGT
512  *            krb5_data *new_trans  The transited field for the new ticket
513  *            krb5_principal tgs    Name of ticket granting server
514  *                                  This includes the realm of the KDC
515  *                                  that issued the ticket granting
516  *                                  ticket.  This is the realm that is
517  *                                  to be added to the transited field.
518  *            krb5_principal client Name of the client
519  *            krb5_principal server The name of the requested server.
520  *                                  This may be the an intermediate
521  *                                  ticket granting server.
522  *
523  *            The last two argument are needed since they are
524  *            implicitly part of the transited field of the new ticket
525  *            even though they are not explicitly listed.
526  *
527  * RETURNS:   krb5_error_code - Success, or out of memory
528  *
529  * MODIFIES:  new_trans:  ->length will contain the length of the new
530  *                        transited field.
531  *
532  *                        If ->data was not null when this procedure
533  *                        is called, the memory referenced by ->data
534  *                        will be deallocated.
535  *
536  *                        Memory will be allocated for the new transited field
537  *                        ->data will be updated to point to the newly
538  *                        allocated memory.
539  *
540  * BUGS:  The space allocated for the new transited field is the
541  *        maximum that might be needed given the old transited field,
542  *        and the realm to be added.  This length is calculated
543  *        assuming that no compression of the new realm is possible.
544  *        This has no adverse consequences other than the allocation
545  *        of more space than required.
546  *
547  *        This procedure will not yet use the null subfield notation,
548  *        and it will get confused if it sees it.
549  *
550  *        This procedure does not check for quoted commas in realm
551  *        names.
552  */
553 
554 krb5_error_code
555 add_to_transited(krb5_data *tgt_trans, krb5_data *new_trans,
556 		 krb5_principal tgs, krb5_principal client,
557 		 krb5_principal server)
558 {
559   krb5_error_code retval;
560   char        *realm;
561   char        *trans;
562   char        *otrans, *otrans_ptr;
563 
564   /* The following are for stepping through the transited field     */
565 
566   char        prev[MAX_REALM_LN];
567   char        next[MAX_REALM_LN];
568   char        current[MAX_REALM_LN];
569   char        exp[MAX_REALM_LN];      /* Expanded current realm name     */
570 
571   int	      i;
572   int         clst, nlst;    /* count of last character in current and next */
573   int         pl, pl1;       /* prefix length                               */
574   int         added;         /* TRUE = new realm has been added             */
575 
576   if (!(realm = (char *) malloc(krb5_princ_realm(kdc_context, tgs)->length+1))) {
577     return(ENOMEM);
578   }
579   memcpy(realm, krb5_princ_realm(kdc_context, tgs)->data,
580 	 krb5_princ_realm(kdc_context, tgs)->length);
581   realm[krb5_princ_realm(kdc_context, tgs)->length] = '\0';
582 
583   if (!(otrans = (char *) malloc(tgt_trans->length+1))) {
584     free(realm);
585     return(ENOMEM);
586   }
587   memcpy(otrans, tgt_trans->data, tgt_trans->length);
588   otrans[tgt_trans->length] = '\0';
589   /* Keep track of start so we can free */
590   otrans_ptr = otrans;
591 
592   /* +1 for null,
593      +1 for extra comma which may be added between
594      +1 for potential space when leading slash in realm */
595   if (!(trans = (char *) malloc(strlen(realm) + strlen(otrans) + 3))) {
596     retval = ENOMEM;
597     goto fail;
598   }
599 
600   if (new_trans->data)  free(new_trans->data);
601   new_trans->data = trans;
602   new_trans->length = 0;
603 
604   trans[0] = '\0';
605 
606   /* For the purpose of appending, the realm preceding the first */
607   /* realm in the transited field is considered the null realm   */
608 
609   prev[0] = '\0';
610 
611   /* read field into current */
612   for (i = 0; *otrans != '\0';) {
613       if (*otrans == '\\') {
614 	  if (*(++otrans) == '\0')
615 	      break;
616 	  else
617 	      continue;
618       }
619       if (*otrans == ',') {
620 	  otrans++;
621 	  break;
622       }
623       current[i++] = *otrans++;
624       if (i >= MAX_REALM_LN) {
625 	  retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
626 	  goto fail;
627       }
628   }
629   current[i] = '\0';
630 
631   added = (krb5_princ_realm(kdc_context, client)->length == strlen(realm) &&
632            !strncmp(krb5_princ_realm(kdc_context, client)->data, realm, strlen(realm))) ||
633           (krb5_princ_realm(kdc_context, server)->length == strlen(realm) &&
634            !strncmp(krb5_princ_realm(kdc_context, server)->data, realm, strlen(realm)));
635 
636   while (current[0]) {
637 
638     /* figure out expanded form of current name */
639 
640     clst = strlen(current) - 1;
641     if (current[0] == ' ') {
642       strncpy(exp, current+1, sizeof(exp) - 1);
643       exp[sizeof(exp) - 1] = '\0';
644     }
645     else if ((current[0] == '/') && (prev[0] == '/')) {
646       strncpy(exp, prev, sizeof(exp) - 1);
647       exp[sizeof(exp) - 1] = '\0';
648       if (strlen(exp) + strlen(current) + 1 >= MAX_REALM_LN) {
649 	retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
650 	goto fail;
651       }
652       strncat(exp, current, sizeof(exp) - 1 - strlen(exp));
653     }
654     else if (current[clst] == '.') {
655       strncpy(exp, current, sizeof(exp) - 1);
656       exp[sizeof(exp) - 1] = '\0';
657       if (strlen(exp) + strlen(prev) + 1 >= MAX_REALM_LN) {
658 	retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
659 	goto fail;
660       }
661       strncat(exp, prev, sizeof(exp) - 1 - strlen(exp));
662     }
663     else {
664       strncpy(exp, current, sizeof(exp) - 1);
665       exp[sizeof(exp) - 1] = '\0';
666     }
667 
668     /* read field into next */
669     for (i = 0; *otrans != '\0';) {
670 	if (*otrans == '\\') {
671 	    if (*(++otrans) == '\0')
672 		break;
673 	    else
674 		continue;
675 	}
676 	if (*otrans == ',') {
677 	    otrans++;
678 	    break;
679 	}
680 	next[i++] = *otrans++;
681 	if (i >= MAX_REALM_LN) {
682 	    retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
683 	    goto fail;
684 	}
685     }
686     next[i] = '\0';
687     nlst = i - 1;
688 
689     if (!strcmp(exp, realm))  added = TRUE;
690 
691     /* If we still have to insert the new realm */
692 
693     if (!added) {
694 
695       /* Is the next field compressed?  If not, and if the new */
696       /* realm is a subrealm of the current realm, compress    */
697       /* the new realm, and insert immediately following the   */
698       /* current one.  Note that we can not do this if the next*/
699       /* field is already compressed since it would mess up    */
700       /* what has already been done.  In most cases, this is   */
701       /* not a problem because the realm to be added will be a */
702       /* subrealm of the next field too, and we will catch     */
703       /* it in a future iteration.                             */
704 
705       if ((next[nlst] != '.') && (next[0] != '/') &&
706           (pl = subrealm(exp, realm))) {
707         added = TRUE;
708 	current[sizeof(current) - 1] = '\0';
709 	if (strlen(current) + (pl>0?pl:-pl) + 2 >= MAX_REALM_LN) {
710 	  retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
711 	  goto fail;
712 	}
713         strncat(current, ",", sizeof(current) - 1 - strlen(current));
714         if (pl > 0) {
715           strncat(current, realm, (unsigned) pl);
716         }
717         else {
718           strncat(current, realm+strlen(realm)+pl, (unsigned) (-pl));
719         }
720       }
721 
722       /* Whether or not the next field is compressed, if the    */
723       /* realm to be added is a superrealm of the current realm,*/
724       /* then the current realm can be compressed.  First the   */
725       /* realm to be added must be compressed relative to the   */
726       /* previous realm (if possible), and then the current     */
727       /* realm compressed relative to the new realm.  Note that */
728       /* if the realm to be added is also a superrealm of the   */
729       /* previous realm, it would have been added earlier, and  */
730       /* we would not reach this step this time around.         */
731 
732       else if ((pl = subrealm(realm, exp))) {
733         added      = TRUE;
734         current[0] = '\0';
735         if ((pl1 = subrealm(prev,realm))) {
736 	  if (strlen(current) + (pl1>0?pl1:-pl1) + 1 >= MAX_REALM_LN) {
737 	    retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
738 	    goto fail;
739 	  }
740           if (pl1 > 0) {
741             strncat(current, realm, (unsigned) pl1);
742           }
743           else {
744             strncat(current, realm+strlen(realm)+pl1, (unsigned) (-pl1));
745           }
746         }
747         else { /* If not a subrealm */
748           if ((realm[0] == '/') && prev[0]) {
749 	    if (strlen(current) + 2 >= MAX_REALM_LN) {
750 	      retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
751 	      goto fail;
752 	    }
753 	    strncat(current, " ", sizeof(current) - 1 - strlen(current));
754 	    current[sizeof(current) - 1] = '\0';
755           }
756 	  if (strlen(current) + strlen(realm) + 1 >= MAX_REALM_LN) {
757 	    retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
758 	    goto fail;
759 	  }
760           strncat(current, realm, sizeof(current) - 1 - strlen(current));
761 	  current[sizeof(current) - 1] = '\0';
762         }
763 	if (strlen(current) + (pl>0?pl:-pl) + 2 >= MAX_REALM_LN) {
764 	  retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
765 	  goto fail;
766 	}
767         strncat(current,",", sizeof(current) - 1 - strlen(current));
768 	current[sizeof(current) - 1] = '\0';
769         if (pl > 0) {
770           strncat(current, exp, (unsigned) pl);
771         }
772         else {
773           strncat(current, exp+strlen(exp)+pl, (unsigned)(-pl));
774         }
775       }
776     }
777 
778     if (new_trans->length != 0) {
779       if (strlen(trans) + 2 >= MAX_REALM_LN) {
780 	retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
781 	goto fail;
782       }
783       strcat(trans, ",");
784     }
785     if (strlen(trans) + strlen(current) + 1 >= MAX_REALM_LN) {
786       retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
787       goto fail;
788     }
789     strcat(trans, current);
790     new_trans->length = strlen(trans);
791 
792     strncpy(prev, exp, sizeof(prev) - 1);
793     prev[sizeof(prev) - 1] = '\0';
794     strncpy(current, next, sizeof(current) - 1);
795     current[sizeof(current) - 1] = '\0';
796   }
797 
798   if (!added) {
799     if (new_trans->length != 0) {
800       if (strlen(trans) + 2 >= MAX_REALM_LN) {
801 	retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
802 	goto fail;
803       }
804       strcat(trans, ",");
805     }
806     if((realm[0] == '/') && trans[0]) {
807       if (strlen(trans) + 2 >= MAX_REALM_LN) {
808 	retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
809 	goto fail;
810       }
811       strcat(trans, " ");
812     }
813     if (strlen(trans) + strlen(realm) + 1 >= MAX_REALM_LN) {
814       retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
815       goto fail;
816     }
817     strcat(trans, realm);
818     new_trans->length = strlen(trans);
819   }
820 
821   retval = 0;
822 fail:
823   free(realm);
824   free(otrans_ptr);
825   return (retval);
826 }
827 
828 /*
829  * Routines that validate a AS request; checks a lot of things.  :-)
830  *
831  * Returns a Kerberos protocol error number, which is _not_ the same
832  * as a com_err error number!
833  */
834 #define AS_INVALID_OPTIONS (KDC_OPT_FORWARDED | KDC_OPT_PROXY |\
835 KDC_OPT_VALIDATE | KDC_OPT_RENEW | KDC_OPT_ENC_TKT_IN_SKEY)
836 int
837 validate_as_request(register krb5_kdc_req *request, krb5_db_entry client,
838 		    krb5_db_entry server, krb5_timestamp kdc_time,
839 		    const char **status)
840 {
841     int		errcode;
842 
843     /*
844      * If an option is set that is only allowed in TGS requests, complain.
845      */
846     if (request->kdc_options & AS_INVALID_OPTIONS) {
847 	*status = "INVALID AS OPTIONS";
848 	return KDC_ERR_BADOPTION;
849     }
850 
851     /* The client's password must not be expired, unless the server is
852       a KRB5_KDC_PWCHANGE_SERVICE. */
853     if (client.pw_expiration && client.pw_expiration < kdc_time &&
854 	!isflagset(server.attributes, KRB5_KDB_PWCHANGE_SERVICE)) {
855 	*status = "CLIENT KEY EXPIRED";
856 #ifdef KRBCONF_VAGUE_ERRORS
857 	return(KRB_ERR_GENERIC);
858 #else
859 	return(KDC_ERR_KEY_EXP);
860 #endif
861     }
862 
863     /* The client must not be expired */
864     if (client.expiration && client.expiration < kdc_time) {
865 	*status = "CLIENT EXPIRED";
866 #ifdef KRBCONF_VAGUE_ERRORS
867 	return(KRB_ERR_GENERIC);
868 #else
869 	return(KDC_ERR_NAME_EXP);
870 #endif
871     }
872 
873     /* The server must not be expired */
874     if (server.expiration && server.expiration < kdc_time) {
875 	*status = "SERVICE EXPIRED";
876 	    return(KDC_ERR_SERVICE_EXP);
877     }
878 
879     /*
880      * If the client requires password changing, then only allow the
881      * pwchange service.
882      */
883     if (isflagset(client.attributes, KRB5_KDB_REQUIRES_PWCHANGE) &&
884 	!isflagset(server.attributes, KRB5_KDB_PWCHANGE_SERVICE)) {
885 	*status = "REQUIRED PWCHANGE";
886 	return(KDC_ERR_KEY_EXP);
887     }
888 
889     /* Client and server must allow postdating tickets */
890     if ((isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) ||
891 	 isflagset(request->kdc_options, KDC_OPT_POSTDATED)) &&
892 	(isflagset(client.attributes, KRB5_KDB_DISALLOW_POSTDATED) ||
893 	 isflagset(server.attributes, KRB5_KDB_DISALLOW_POSTDATED))) {
894 	*status = "POSTDATE NOT ALLOWED";
895 	return(KDC_ERR_CANNOT_POSTDATE);
896     }
897 
898     /* Client and server must allow forwardable tickets */
899     if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) &&
900 	(isflagset(client.attributes, KRB5_KDB_DISALLOW_FORWARDABLE) ||
901 	 isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))) {
902 	*status = "FORWARDABLE NOT ALLOWED";
903 	return(KDC_ERR_POLICY);
904     }
905 
906     /* Client and server must allow renewable tickets */
907     if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE) &&
908 	(isflagset(client.attributes, KRB5_KDB_DISALLOW_RENEWABLE) ||
909 	 isflagset(server.attributes, KRB5_KDB_DISALLOW_RENEWABLE))) {
910 	*status = "RENEWABLE NOT ALLOWED";
911 	return(KDC_ERR_POLICY);
912     }
913 
914     /* Client and server must allow proxiable tickets */
915     if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE) &&
916 	(isflagset(client.attributes, KRB5_KDB_DISALLOW_PROXIABLE) ||
917 	 isflagset(server.attributes, KRB5_KDB_DISALLOW_PROXIABLE))) {
918 	*status = "PROXIABLE NOT ALLOWED";
919 	return(KDC_ERR_POLICY);
920     }
921 
922     /* Check to see if client is locked out */
923     if (isflagset(client.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
924 	*status = "CLIENT LOCKED OUT";
925 	return(KDC_ERR_C_PRINCIPAL_UNKNOWN);
926     }
927 
928     /* Check to see if server is locked out */
929     if (isflagset(server.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
930 	*status = "SERVICE LOCKED OUT";
931 	return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
932     }
933 
934     /* Check to see if server is allowed to be a service */
935     if (isflagset(server.attributes, KRB5_KDB_DISALLOW_SVR)) {
936 	*status = "SERVICE NOT ALLOWED";
937 	return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
938     }
939 
940     /*
941      * Check against local policy
942      */
943     errcode = against_local_policy_as(request, server, client,
944 				      kdc_time, status);
945     if (errcode)
946 	return errcode;
947 
948     return 0;
949 }
950 
951 #define ASN1_ID_CLASS	(0xc0)
952 #define ASN1_ID_TYPE    (0x20)
953 #define ASN1_ID_TAG	(0x1f)
954 #define ASN1_CLASS_UNIV	(0)
955 #define ASN1_CLASS_APP	(1)
956 #define ASN1_CLASS_CTX	(2)
957 #define ASN1_CLASS_PRIV	(3)
958 #define asn1_id_constructed(x) 	(x & ASN1_ID_TYPE)
959 #define asn1_id_primitive(x) 	(!asn1_id_constructed(x))
960 #define asn1_id_class(x)	((x & ASN1_ID_CLASS) >> 6)
961 #define asn1_id_tag(x)		(x & ASN1_ID_TAG)
962 
963 /*
964  * asn1length - return encoded length of value.
965  *
966  * passed a pointer into the asn.1 stream, which is updated
967  * to point right after the length bits.
968  *
969  * returns -1 on failure.
970  */
971 static int
972 asn1length(unsigned char **astream)
973 {
974     int length;		/* resulting length */
975     int sublen;		/* sublengths */
976     int blen;		/* bytes of length */
977     unsigned char *p;	/* substring searching */
978 
979     if (**astream & 0x80) {
980         blen = **astream & 0x7f;
981 	if (blen > 3) {
982 	   return(-1);
983 	}
984 	for (++*astream, length = 0; blen; ++*astream, blen--) {
985 	    length = (length << 8) | **astream;
986 	}
987 	if (length == 0) {
988 		/* indefinite length, figure out by hand */
989 	    p = *astream;
990 	    p++;
991 	    while (1) {
992 		/* compute value length. */
993 		if ((sublen = asn1length(&p)) < 0) {
994 		    return(-1);
995 		}
996 		p += sublen;
997                 /* check for termination */
998 		if ((!*p++) && (!*p)) {
999 		    p++;
1000 		    break;
1001 		}
1002 	    }
1003 	    length = p - *astream;
1004 	}
1005     } else {
1006 	length = **astream;
1007 	++*astream;
1008     }
1009    return(length);
1010 }
1011 
1012 /*
1013  * fetch_asn1_field - return raw asn.1 stream of subfield.
1014  *
1015  * this routine is passed a context-dependent tag number and "level" and returns
1016  * the size and length of the corresponding level subfield.
1017  *
1018  * levels and are numbered starting from 1.
1019  *
1020  * returns 0 on success, -1 otherwise.
1021  */
1022 int
1023 fetch_asn1_field(unsigned char *astream, unsigned int level,
1024 		 unsigned int field, krb5_data *data)
1025 {
1026     unsigned char *estream;	/* end of stream */
1027     int classes;		/* # classes seen so far this level */
1028     unsigned int levels = 0;		/* levels seen so far */
1029     int lastlevel = 1000;       /* last level seen */
1030     int length;			/* various lengths */
1031     int tag;			/* tag number */
1032     unsigned char savelen;      /* saved length of our field */
1033 
1034     classes = -1;
1035     /* we assume that the first identifier/length will tell us
1036        how long the entire stream is. */
1037     astream++;
1038     estream = astream;
1039     if ((length = asn1length(&astream)) < 0) {
1040 	return(-1);
1041     }
1042     estream += length;
1043     /* search down the stream, checking identifiers.  we process identifiers
1044        until we hit the "level" we want, and then process that level for our
1045        subfield, always making sure we don't go off the end of the stream.  */
1046     while (astream < estream) {
1047 	if (!asn1_id_constructed(*astream)) {
1048 	    return(-1);
1049 	}
1050         if (asn1_id_class(*astream) == ASN1_CLASS_CTX) {
1051             if ((tag = (int)asn1_id_tag(*astream)) <= lastlevel) {
1052                 levels++;
1053                 classes = -1;
1054             }
1055             lastlevel = tag;
1056             if (levels == level) {
1057 	        /* in our context-dependent class, is this the one we're looking for ? */
1058 	        if (tag == field) {
1059 		    /* return length and data */
1060 		    astream++;
1061 		    savelen = *astream;
1062 		    if ((data->length = asn1length(&astream)) < 0) {
1063 		        return(-1);
1064 	 	    }
1065 		    /* if the field length is indefinite, we will have to subtract two
1066                        (terminating octets) from the length returned since we don't want
1067                        to pass any info from the "wrapper" back.  asn1length will always return
1068                        the *total* length of the field, not just what's contained in it */
1069 		    if ((savelen & 0xff) == 0x80) {
1070 		      data->length -=2 ;
1071 		    }
1072 		    data->data = (char *)astream;
1073 		    return(0);
1074 	        } else if (tag <= classes) {
1075 		    /* we've seen this class before, something must be wrong */
1076 		    return(-1);
1077 	        } else {
1078 		    classes = tag;
1079 	        }
1080 	    }
1081         }
1082         /* if we're not on our level yet, process this value.  otherwise skip over it */
1083 	astream++;
1084 	if ((length = asn1length(&astream)) < 0) {
1085 	    return(-1);
1086 	}
1087 	if (levels == level) {
1088 	    astream += length;
1089 	}
1090     }
1091     return(-1);
1092 }
1093 
1094 /*
1095  * Routines that validate a TGS request; checks a lot of things.  :-)
1096  *
1097  * Returns a Kerberos protocol error number, which is _not_ the same
1098  * as a com_err error number!
1099  */
1100 #define TGS_OPTIONS_HANDLED (KDC_OPT_FORWARDABLE | KDC_OPT_FORWARDED | \
1101 			     KDC_OPT_PROXIABLE | KDC_OPT_PROXY | \
1102 			     KDC_OPT_ALLOW_POSTDATE | KDC_OPT_POSTDATED | \
1103 			     KDC_OPT_RENEWABLE | KDC_OPT_RENEWABLE_OK | \
1104 			     KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_RENEW | \
1105 			     KDC_OPT_VALIDATE)
1106 
1107 #define NO_TGT_OPTION (KDC_OPT_FORWARDED | KDC_OPT_PROXY | KDC_OPT_RENEW | \
1108 		       KDC_OPT_VALIDATE)
1109 
1110 int
1111 validate_tgs_request(register krb5_kdc_req *request, krb5_db_entry server,
1112 		     krb5_ticket *ticket, krb5_timestamp kdc_time,
1113 		     const char **status)
1114 {
1115     int		errcode;
1116     int		st_idx = 0;
1117 
1118     /*
1119      * If an illegal option is set, ignore it.
1120      */
1121     request->kdc_options &= TGS_OPTIONS_HANDLED;
1122 
1123     /* Check to see if server has expired */
1124     if (server.expiration && server.expiration < kdc_time) {
1125 	*status = "SERVICE EXPIRED";
1126 	return(KDC_ERR_SERVICE_EXP);
1127     }
1128 
1129     /*
1130      * Verify that the server principal in authdat->ticket is correct
1131      * (either the ticket granting service or the service that was
1132      * originally requested)
1133      */
1134     if (request->kdc_options & NO_TGT_OPTION) {
1135 	if (!krb5_principal_compare(kdc_context, ticket->server, request->server)) {
1136 	    *status = "SERVER DIDN'T MATCH TICKET FOR RENEW/FORWARD/ETC";
1137 	    return(KDC_ERR_SERVER_NOMATCH);
1138 	}
1139     } else {
1140 	/*
1141 	 * OK, we need to validate the krbtgt service in the ticket.
1142 	 *
1143 	 * The krbtgt service is of the form:
1144 	 * 		krbtgt/realm-A@realm-B
1145 	 *
1146 	 * Realm A is the "server realm"; the realm of the
1147 	 * server of the requested ticket must match this realm.
1148 	 * Of course, it should be a realm serviced by this KDC.
1149 	 *
1150 	 * Realm B is the "client realm"; this is what should be
1151 	 * added to the transited field.  (which is done elsewhere)
1152 	 */
1153 
1154 	/* Make sure there are two components... */
1155 	if (krb5_princ_size(kdc_context, ticket->server) != 2) {
1156 	    *status = "BAD TGS SERVER LENGTH";
1157 	    return KRB_AP_ERR_NOT_US;
1158 	}
1159 	/* ...that the first component is krbtgt... */
1160 	if (!krb5_is_tgs_principal(ticket->server)) {
1161 	    *status = "BAD TGS SERVER NAME";
1162 	    return KRB_AP_ERR_NOT_US;
1163 	}
1164 	/* ...and that the second component matches the server realm... */
1165 	if ((krb5_princ_size(kdc_context, ticket->server) <= 1) ||
1166 	    (krb5_princ_component(kdc_context, ticket->server, 1)->length !=
1167 	     krb5_princ_realm(kdc_context, request->server)->length) ||
1168 	    memcmp(krb5_princ_component(kdc_context, ticket->server, 1)->data,
1169 		   krb5_princ_realm(kdc_context, request->server)->data,
1170 		   krb5_princ_realm(kdc_context, request->server)->length)) {
1171 	    *status = "BAD TGS SERVER INSTANCE";
1172 	    return KRB_AP_ERR_NOT_US;
1173 	}
1174 	/* XXX add check that second component must match locally
1175 	 * supported realm?
1176 	 */
1177 
1178 	/* Server must allow TGS based issuances */
1179 	if (isflagset(server.attributes, KRB5_KDB_DISALLOW_TGT_BASED)) {
1180 	    *status = "TGT BASED NOT ALLOWED";
1181 	    return(KDC_ERR_POLICY);
1182 	}
1183     }
1184 
1185     /* TGS must be forwardable to get forwarded or forwardable ticket */
1186     if ((isflagset(request->kdc_options, KDC_OPT_FORWARDED) ||
1187 	 isflagset(request->kdc_options, KDC_OPT_FORWARDABLE)) &&
1188 	!isflagset(ticket->enc_part2->flags, TKT_FLG_FORWARDABLE)) {
1189 	*status = "TGT NOT FORWARDABLE";
1190 
1191 	return KDC_ERR_BADOPTION;
1192     }
1193 
1194     /* TGS must be proxiable to get proxiable ticket */
1195     if ((isflagset(request->kdc_options, KDC_OPT_PROXY) ||
1196 	 isflagset(request->kdc_options, KDC_OPT_PROXIABLE)) &&
1197 	!isflagset(ticket->enc_part2->flags, TKT_FLG_PROXIABLE)) {
1198 	*status = "TGT NOT PROXIABLE";
1199 	return KDC_ERR_BADOPTION;
1200     }
1201 
1202     /* TGS must allow postdating to get postdated ticket */
1203     if ((isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) ||
1204 	  isflagset(request->kdc_options, KDC_OPT_POSTDATED)) &&
1205 	!isflagset(ticket->enc_part2->flags, TKT_FLG_MAY_POSTDATE)) {
1206 	*status = "TGT NOT POSTDATABLE";
1207 	return KDC_ERR_BADOPTION;
1208     }
1209 
1210     /* can only validate invalid tix */
1211     if (isflagset(request->kdc_options, KDC_OPT_VALIDATE) &&
1212 	!isflagset(ticket->enc_part2->flags, TKT_FLG_INVALID)) {
1213 	*status = "VALIDATE VALID TICKET";
1214 	return KDC_ERR_BADOPTION;
1215     }
1216 
1217     /* can only renew renewable tix */
1218     if ((isflagset(request->kdc_options, KDC_OPT_RENEW) ||
1219 	  isflagset(request->kdc_options, KDC_OPT_RENEWABLE)) &&
1220 	!isflagset(ticket->enc_part2->flags, TKT_FLG_RENEWABLE)) {
1221 	*status = "TICKET NOT RENEWABLE";
1222 	return KDC_ERR_BADOPTION;
1223     }
1224 
1225     /* can not proxy ticket granting tickets */
1226     if (isflagset(request->kdc_options, KDC_OPT_PROXY) &&
1227 	(!request->server->data ||
1228 	 request->server->data[0].length != KRB5_TGS_NAME_SIZE ||
1229 	 memcmp(request->server->data[0].data, KRB5_TGS_NAME,
1230 		KRB5_TGS_NAME_SIZE))) {
1231 	*status = "CAN'T PROXY TGT";
1232 	return KDC_ERR_BADOPTION;
1233     }
1234 
1235     /* Server must allow forwardable tickets */
1236     if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) &&
1237 	isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE)) {
1238 	*status = "NON-FORWARDABLE TICKET";
1239 	return(KDC_ERR_POLICY);
1240     }
1241 
1242     /* Server must allow renewable tickets */
1243     if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE) &&
1244 	isflagset(server.attributes, KRB5_KDB_DISALLOW_RENEWABLE)) {
1245 	*status = "NON-RENEWABLE TICKET";
1246 	return(KDC_ERR_POLICY);
1247     }
1248 
1249     /* Server must allow proxiable tickets */
1250     if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE) &&
1251 	isflagset(server.attributes, KRB5_KDB_DISALLOW_PROXIABLE)) {
1252 	*status = "NON-PROXIABLE TICKET";
1253 	return(KDC_ERR_POLICY);
1254     }
1255 
1256     /* Server must allow postdated tickets */
1257     if (isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) &&
1258 	isflagset(server.attributes, KRB5_KDB_DISALLOW_POSTDATED)) {
1259 	*status = "NON-POSTDATABLE TICKET";
1260 	return(KDC_ERR_CANNOT_POSTDATE);
1261     }
1262 
1263     /* Server must allow DUP SKEY requests */
1264     if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY) &&
1265 	isflagset(server.attributes, KRB5_KDB_DISALLOW_DUP_SKEY)) {
1266 	*status = "DUP_SKEY DISALLOWED";
1267 	return(KDC_ERR_POLICY);
1268     }
1269 
1270     /* Server must not be locked out */
1271     if (isflagset(server.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
1272 	*status = "SERVER LOCKED OUT";
1273 	return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
1274     }
1275 
1276     /* Server must be allowed to be a service */
1277     if (isflagset(server.attributes, KRB5_KDB_DISALLOW_SVR)) {
1278 	*status = "SERVER NOT ALLOWED";
1279 	return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
1280     }
1281 
1282     /* Check the hot list */
1283     if (check_hot_list(ticket)) {
1284 	*status = "HOT_LIST";
1285 	return(KRB_AP_ERR_REPEAT);
1286     }
1287 
1288     /* Check the start time vs. the KDC time */
1289     if (isflagset(request->kdc_options, KDC_OPT_VALIDATE)) {
1290 	if (ticket->enc_part2->times.starttime > kdc_time) {
1291 	    *status = "NOT_YET_VALID";
1292 	    return(KRB_AP_ERR_TKT_NYV);
1293 	}
1294     }
1295 
1296     /*
1297      * Check the renew_till time.  The endtime was already
1298      * been checked in the initial authentication check.
1299      */
1300     if (isflagset(request->kdc_options, KDC_OPT_RENEW) &&
1301 	(ticket->enc_part2->times.renew_till < kdc_time)) {
1302 	*status = "TKT_EXPIRED";
1303 	return(KRB_AP_ERR_TKT_EXPIRED);
1304     }
1305 
1306     /*
1307      * Checks for ENC_TKT_IN_SKEY:
1308      *
1309      * (1) Make sure the second ticket exists
1310      * (2) Make sure it is a ticket granting ticket
1311      */
1312     if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) {
1313 	if (!request->second_ticket ||
1314 	    !request->second_ticket[st_idx]) {
1315 	    *status = "NO_2ND_TKT";
1316 	    return(KDC_ERR_BADOPTION);
1317 	}
1318 	if (!krb5_principal_compare(kdc_context, request->second_ticket[st_idx]->server,
1319 				    tgs_server)) {
1320 		*status = "2ND_TKT_NOT_TGS";
1321 		return(KDC_ERR_POLICY);
1322 	}
1323 	st_idx++;
1324     }
1325 
1326     /* Check for hardware preauthentication */
1327     if (isflagset(server.attributes, KRB5_KDB_REQUIRES_HW_AUTH) &&
1328 	!isflagset(ticket->enc_part2->flags,TKT_FLG_HW_AUTH)) {
1329 	*status = "NO HW PREAUTH";
1330 	return KRB_ERR_GENERIC;
1331     }
1332 
1333     /* Check for any kind of preauthentication */
1334     if (isflagset(server.attributes, KRB5_KDB_REQUIRES_PRE_AUTH) &&
1335 	!isflagset(ticket->enc_part2->flags, TKT_FLG_PRE_AUTH)) {
1336 	*status = "NO PREAUTH";
1337 	return KRB_ERR_GENERIC;
1338     }
1339 
1340     /*
1341      * Check local policy
1342      */
1343     errcode = against_local_policy_tgs(request, server, ticket, status);
1344     if (errcode)
1345 	return errcode;
1346 
1347 
1348     return 0;
1349 }
1350 
1351 /*
1352  * This function returns 1 if the dbentry has a key for a specified
1353  * keytype, and 0 if not.
1354  */
1355 int
1356 dbentry_has_key_for_enctype(krb5_context context, krb5_db_entry *client,
1357 			    krb5_enctype enctype)
1358 {
1359     krb5_error_code	retval;
1360     krb5_key_data	*datap;
1361 
1362     retval = krb5_dbe_find_enctype(context, client, enctype,
1363 				   -1, 0, &datap);
1364     if (retval)
1365 	return 0;
1366     else
1367 	return 1;
1368 }
1369 
1370 /*
1371  * This function returns 1 if the entity referenced by this
1372  * structure can support the a particular encryption system, and 0 if
1373  * not.
1374  *
1375  * XXX eventually this information should be looked up in the
1376  * database.  Since it isn't, we use some hueristics and attribute
1377  * options bits for now.
1378  */
1379 int
1380 dbentry_supports_enctype(krb5_context context, krb5_db_entry *client,
1381 			 krb5_enctype enctype)
1382 {
1383     /*
1384      * If it's DES_CBC_MD5, there's a bit in the attribute mask which
1385      * checks to see if we support it.
1386      *
1387      * In theory everything's supposed to support DES_CBC_MD5, but
1388      * that's not the reality....
1389      */
1390 
1391     /*
1392      * We are assuming that all entries can support MD5; this information
1393      * need not be kept in the database.
1394     */
1395 
1396 
1397     if (enctype == ENCTYPE_DES_CBC_MD5)
1398 	return 1;
1399 
1400     /*
1401      * XXX we assume everything can understand DES_CBC_CRC
1402      */
1403     if (enctype == ENCTYPE_DES_CBC_CRC)
1404 	return 1;
1405 
1406     /*
1407      * If we have a key for the encryption system, we assume it's
1408      * supported.
1409      */
1410     return dbentry_has_key_for_enctype(context, client, enctype);
1411 }
1412 
1413 /*
1414  * This function returns the keytype which should be selected for the
1415  * session key.  It is based on the ordered list which the user
1416  * requested, and what the KDC and the application server can support.
1417  */
1418 krb5_enctype
1419 select_session_keytype(krb5_context context, krb5_db_entry *server,
1420 		       int nktypes, krb5_enctype *ktype)
1421 {
1422     int		i;
1423 
1424     for (i = 0; i < nktypes; i++) {
1425 	if (!krb5_c_valid_enctype(ktype[i]))
1426 	    continue;
1427 
1428 	if (!krb5_is_permitted_enctype(context, ktype[i]))
1429 	    continue;
1430 
1431 	if (dbentry_supports_enctype(context, server, ktype[i]))
1432 	    return ktype[i];
1433     }
1434     return 0;
1435 }
1436 
1437 /*
1438  * This function returns salt information for a particular client_key
1439  */
1440 krb5_error_code
1441 get_salt_from_key(krb5_context context, krb5_principal client,
1442 		  krb5_key_data *client_key, krb5_data *salt)
1443 {
1444     krb5_error_code		retval;
1445     krb5_data *			realm;
1446 
1447     salt->data = 0;
1448     salt->length = SALT_TYPE_NO_LENGTH;
1449 
1450     if (client_key->key_data_ver == 1)
1451 	return 0;
1452 
1453     switch (client_key->key_data_type[1]) {
1454     case KRB5_KDB_SALTTYPE_NORMAL:
1455 	break;
1456     case KRB5_KDB_SALTTYPE_V4:
1457 	/* send an empty (V4) salt */
1458 	salt->data = 0;
1459 	salt->length = 0;
1460 	break;
1461     case KRB5_KDB_SALTTYPE_NOREALM:
1462 	if ((retval = krb5_principal2salt_norealm(context, client, salt)))
1463 	    return retval;
1464 	break;
1465     case KRB5_KDB_SALTTYPE_AFS3:
1466 	/* send the same salt as with onlyrealm - but with no type info,
1467 	   we just hope they figure it out on the other end. */
1468 	/* fall through to onlyrealm: */
1469     case KRB5_KDB_SALTTYPE_ONLYREALM:
1470 	realm = krb5_princ_realm(context, client);
1471 	salt->length = realm->length;
1472 	if ((salt->data = malloc(realm->length)) == NULL)
1473 	    return ENOMEM;
1474 	memcpy(salt->data, realm->data, realm->length);
1475 	break;
1476     case KRB5_KDB_SALTTYPE_SPECIAL:
1477 	salt->length = client_key->key_data_length[1];
1478 	if ((salt->data = malloc(salt->length)) == NULL)
1479 	    return ENOMEM;
1480 	memcpy(salt->data, client_key->key_data_contents[1], salt->length);
1481 	break;
1482     }
1483     return 0;
1484 }
1485 
1486 /*
1487  * Limit strings to a "reasonable" length to prevent crowding out of
1488  * other useful information in the log entry
1489  */
1490 #define NAME_LENGTH_LIMIT 128
1491 
1492 void limit_string(char *name)
1493 {
1494 	int	i;
1495 
1496 	if (!name)
1497 		return;
1498 
1499 	if (strlen(name) < NAME_LENGTH_LIMIT)
1500 		return;
1501 
1502 	i = NAME_LENGTH_LIMIT-4;
1503 	name[i++] = '.';
1504 	name[i++] = '.';
1505 	name[i++] = '.';
1506 	name[i] = '\0';
1507 	return;
1508 }
1509 
1510 /*
1511  * L10_2 = log10(2**x), rounded up; log10(2) ~= 0.301.
1512  */
1513 #define L10_2(x) ((int)(((x * 301) + 999) / 1000))
1514 
1515 /*
1516  * Max length of sprintf("%ld") for an int of type T; includes leading
1517  * minus sign and terminating NUL.
1518  */
1519 #define D_LEN(t) (L10_2(sizeof(t) * CHAR_BIT) + 2)
1520 
1521 void
1522 ktypes2str(char *s, size_t len, int nktypes, krb5_enctype *ktype)
1523 {
1524     int i;
1525     char stmp[D_LEN(krb5_enctype) + 1];
1526     char *p;
1527 
1528     if (nktypes < 0
1529 	|| len < (sizeof(" etypes {...}") + D_LEN(int))) {
1530 	*s = '\0';
1531 	return;
1532     }
1533 
1534     sprintf(s, "%d etypes {", nktypes);
1535     for (i = 0; i < nktypes; i++) {
1536 	sprintf(stmp, "%s%ld", i ? " " : "", (long)ktype[i]);
1537 	if (strlen(s) + strlen(stmp) + sizeof("}") > len)
1538 	    break;
1539 	strcat(s, stmp);
1540     }
1541     if (i < nktypes) {
1542 	/*
1543 	 * We broke out of the loop. Try to truncate the list.
1544 	 */
1545 	p = s + strlen(s);
1546 	while (p - s + sizeof("...}") > len) {
1547 	    while (p > s && *p != ' ' && *p != '{')
1548 		*p-- = '\0';
1549 	    if (p > s && *p == ' ') {
1550 		*p-- = '\0';
1551 		continue;
1552 	    }
1553 	}
1554 	strcat(s, "...");
1555     }
1556     strcat(s, "}");
1557     return;
1558 }
1559 
1560 void
1561 rep_etypes2str(char *s, size_t len, krb5_kdc_rep *rep)
1562 {
1563     char stmp[sizeof("ses=") + D_LEN(krb5_enctype)];
1564 
1565     if (len < (3 * D_LEN(krb5_enctype)
1566 	       + sizeof("etypes {rep= tkt= ses=}"))) {
1567 	*s = '\0';
1568 	return;
1569     }
1570 
1571     sprintf(s, "etypes {rep=%ld", (long)rep->enc_part.enctype);
1572 
1573     if (rep->ticket != NULL) {
1574 	sprintf(stmp, " tkt=%ld", (long)rep->ticket->enc_part.enctype);
1575 	strcat(s, stmp);
1576     }
1577 
1578     if (rep->ticket != NULL
1579 	&& rep->ticket->enc_part2 != NULL
1580 	&& rep->ticket->enc_part2->session != NULL) {
1581 	sprintf(stmp, " ses=%ld",
1582 		(long)rep->ticket->enc_part2->session->enctype);
1583 	strcat(s, stmp);
1584     }
1585     strcat(s, "}");
1586     return;
1587 }
1588