xref: /illumos-gate/usr/src/cmd/krb5/krb5kdc/kdc_util.c (revision 55fea89d)
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
43 static char *kdc_current_rcname = (char *) NULL;
44 krb5_deltat rc_lifetime; /* See kdc_initialize_rcache() */
45 #endif
46 
47 #ifdef USE_RCACHE
48 /*
49  * initialize the replay cache.
50  */
51 krb5_error_code
kdc_initialize_rcache(krb5_context kcontext,char * rcache_name)52 kdc_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  */
92 krb5_error_code
concat_authorization_data(krb5_authdata ** first,krb5_authdata ** second,krb5_authdata *** output)93 concat_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 
139 krb5_boolean
realm_compare(krb5_principal princ1,krb5_principal princ2)140 realm_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  */
krb5_is_tgs_principal(krb5_principal principal)153 krb5_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  */
168 static krb5_error_code
comp_cksum(krb5_context kcontext,krb5_data * source,krb5_ticket * ticket,krb5_checksum * his_cksum)169 comp_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 
194 krb5_error_code
kdc_process_tgs_req(krb5_kdc_req * request,const krb5_fulladdr * from,krb5_data * pkt,krb5_ticket ** ticket,krb5_keyblock ** subkey)195 kdc_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 
363 cleanup_authenticator:
364     krb5_free_authenticator(kdc_context, authenticator);
365 
366 cleanup_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 
373 cleanup:
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  */
385 krb5_error_code
kdc_get_server_key(krb5_ticket * ticket,krb5_keyblock ** key,krb5_kvno * kvno)386 kdc_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;
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
fetch_last_req_info(krb5_db_entry * dbentry,krb5_last_req_entry *** lrentry)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
check_hot_list(krb5_ticket * ticket)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
subrealm(char * r1,char * r2)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 static char *
data2string(krb5_data * d)555 data2string (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 
566 krb5_error_code
add_to_transited(krb5_data * tgt_trans,krb5_data * new_trans,krb5_principal tgs,krb5_principal client,krb5_principal server)567 add_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;
834 fail:
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 |\
847 KDC_OPT_VALIDATE | KDC_OPT_RENEW | KDC_OPT_ENC_TKT_IN_SKEY)
848 int
validate_as_request(register krb5_kdc_req * request,krb5_db_entry client,krb5_db_entry server,krb5_timestamp kdc_time,const char ** status)849 validate_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  */
983 static int
asn1length(unsigned char ** astream)984 asn1length(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  */
1034 int
fetch_asn1_field(unsigned char * astream,unsigned int level,unsigned int field,krb5_data * data)1035 fetch_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 
1122 int
validate_tgs_request(register krb5_kdc_req * request,krb5_db_entry server,krb5_ticket * ticket,krb5_timestamp kdc_time,const char ** status)1123 validate_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  */
1367 int
dbentry_has_key_for_enctype(krb5_context context,krb5_db_entry * client,krb5_enctype enctype)1368 dbentry_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  */
1391 int
dbentry_supports_enctype(krb5_context context,krb5_db_entry * client,krb5_enctype enctype)1392 dbentry_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  */
1430 krb5_enctype
select_session_keytype(krb5_context context,krb5_db_entry * server,int nktypes,krb5_enctype * ktype)1431 select_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  */
1452 krb5_error_code
get_salt_from_key(krb5_context context,krb5_principal client,krb5_key_data * client_key,krb5_data * salt)1453 get_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 
limit_string(char * name)1504 void 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 
1533 void
ktypes2str(char * s,size_t len,int nktypes,krb5_enctype * ktype)1534 ktypes2str(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 
1572 void
rep_etypes2str(char * s,size_t len,krb5_kdc_rep * rep)1573 rep_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