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