Patch from Luke Howard:
[krb5.git] / src / kdc / kdc_util.c
1 /*
2  * kdc/kdc_util.c
3  *
4  * Copyright 1990,1991,2007,2008 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  * Copyright (c) 2006-2008, Novell, Inc.
31  * All rights reserved.
32  *
33  * Redistribution and use in source and binary forms, with or without
34  * modification, are permitted provided that the following conditions are met:
35  *
36  *   * Redistributions of source code must retain the above copyright notice,
37  *       this list of conditions and the following disclaimer.
38  *   * Redistributions in binary form must reproduce the above copyright
39  *       notice, this list of conditions and the following disclaimer in the
40  *       documentation and/or other materials provided with the distribution.
41  *   * The copyright holder's name is not used to endorse or promote products
42  *       derived from this software without specific prior written permission.
43  *
44  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
45  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
46  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
47  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
48  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
49  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
50  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
51  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
52  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
53  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
54  * POSSIBILITY OF SUCH DAMAGE.
55  */
56
57 #include "k5-int.h"
58 #include "kdc_util.h"
59 #include "extern.h"
60 #include <stdio.h>
61 #include <ctype.h>
62 #include <syslog.h>
63 #include "adm.h"
64 #include "adm_proto.h"
65 #include <limits.h>
66
67 #ifdef USE_RCACHE
68 static char *kdc_current_rcname = (char *) NULL;
69 krb5_deltat rc_lifetime; /* See kdc_initialize_rcache() */
70 #endif
71
72 #ifdef USE_RCACHE
73 /*
74  * initialize the replay cache.
75  */
76 krb5_error_code
77 kdc_initialize_rcache(krb5_context kcontext, char *rcache_name)
78 {
79     krb5_error_code     retval;
80     char                *rcname;
81     char                *sname;
82
83     rcname = (rcache_name) ? rcache_name : kdc_current_rcname;
84
85     /* rc_lifetime used elsewhere to verify we're not */
86     /*  replaying really old data                     */
87     rc_lifetime = kcontext->clockskew;
88
89     if (!rcname)
90         rcname = KDCRCACHE;
91     if (!(retval = krb5_rc_resolve_full(kcontext, &kdc_rcache, rcname))) {
92         /* Recover or initialize the replay cache */
93         if (!(retval = krb5_rc_recover(kcontext, kdc_rcache)) ||
94             !(retval = krb5_rc_initialize(kcontext,
95                                           kdc_rcache,
96                                           kcontext->clockskew))
97             ) {
98             /* Expunge the replay cache */
99             if (!(retval = krb5_rc_expunge(kcontext, kdc_rcache))) {
100                 sname = kdc_current_rcname;
101                 kdc_current_rcname = strdup(rcname);
102                 if (sname)
103                     free(sname);
104             }
105         }
106         if (retval)
107             krb5_rc_close(kcontext, kdc_rcache);
108     }
109     return(retval);
110 }
111 #endif
112
113 /*
114  * concatenate first two authdata arrays, returning an allocated replacement.
115  * The replacement should be freed with krb5_free_authdata().
116  */
117 krb5_error_code
118 concat_authorization_data(krb5_authdata **first, krb5_authdata **second,
119                           krb5_authdata ***output)
120 {
121     register int i, j;
122     register krb5_authdata **ptr, **retdata;
123
124     /* count up the entries */
125     i = 0;
126     if (first)
127         for (ptr = first; *ptr; ptr++)
128             i++;
129     if (second)
130         for (ptr = second; *ptr; ptr++)
131             i++;
132     
133     retdata = (krb5_authdata **)malloc((i+1)*sizeof(*retdata));
134     if (!retdata)
135         return ENOMEM;
136     retdata[i] = 0;                     /* null-terminated array */
137     for (i = 0, j = 0, ptr = first; j < 2 ; ptr = second, j++)
138         while (ptr && *ptr) {
139             /* now walk & copy */
140             retdata[i] = (krb5_authdata *)malloc(sizeof(*retdata[i]));
141             if (!retdata[i]) {
142                 krb5_free_authdata(kdc_context, retdata);
143                 return ENOMEM;
144             }
145             *retdata[i] = **ptr;
146             if (!(retdata[i]->contents =
147                   (krb5_octet *)malloc(retdata[i]->length))) {
148                 free((char *)retdata[i]);
149                 retdata[i] = 0;
150                 krb5_free_authdata(kdc_context, retdata);
151                 return ENOMEM;
152             }
153             memcpy((char *) retdata[i]->contents,
154                    (char *)(*ptr)->contents,
155                    retdata[i]->length);
156
157             ptr++;
158             i++;
159         }
160     *output = retdata;
161     return 0;
162 }
163
164 krb5_boolean
165 realm_compare(krb5_const_principal princ1, krb5_const_principal princ2)
166 {
167     return krb5_realm_compare(kdc_context, princ1, princ2);
168 }
169
170 krb5_boolean
171 is_local_principal(krb5_const_principal princ1)
172 {
173     return krb5_realm_compare(kdc_context, princ1, tgs_server);
174 }
175
176 /*
177  * Returns TRUE if the kerberos principal is the name of a Kerberos ticket
178  * service.
179  */
180 krb5_boolean krb5_is_tgs_principal(krb5_const_principal principal)
181 {
182     if ((krb5_princ_size(kdc_context, principal) > 0) &&
183         data_eq_string (*krb5_princ_component(kdc_context, principal, 0),
184                         KRB5_TGS_NAME))
185         return TRUE;
186     return FALSE;
187 }
188
189 /*
190  * given authentication data (provides seed for checksum), verify checksum
191  * for source data.
192  */
193 static krb5_error_code
194 comp_cksum(krb5_context kcontext, krb5_data *source, krb5_ticket *ticket,
195            krb5_checksum *his_cksum)
196 {
197     krb5_error_code       retval;
198     krb5_boolean          valid;
199
200     if (!krb5_c_valid_cksumtype(his_cksum->checksum_type)) 
201         return KRB5KDC_ERR_SUMTYPE_NOSUPP;
202
203     /* must be collision proof */
204     if (!krb5_c_is_coll_proof_cksum(his_cksum->checksum_type))
205         return KRB5KRB_AP_ERR_INAPP_CKSUM;
206
207     /* verify checksum */
208     if ((retval = krb5_c_verify_checksum(kcontext, ticket->enc_part2->session,
209                                          KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
210                                          source, his_cksum, &valid)))
211         return(retval);
212
213     if (!valid)
214         return(KRB5KRB_AP_ERR_BAD_INTEGRITY);
215
216     return(0);
217 }
218
219 krb5_pa_data *
220 find_pa_data(krb5_pa_data **padata, krb5_preauthtype pa_type)
221 {
222     krb5_pa_data **tmppa;
223
224     if (padata == NULL)
225         return NULL;
226
227     for (tmppa = padata; *tmppa != NULL; tmppa++) {
228         if ((*tmppa)->pa_type == pa_type)
229             break;
230     }
231
232     return *tmppa;
233 }
234
235 krb5_error_code 
236 kdc_process_tgs_req(krb5_kdc_req *request, const krb5_fulladdr *from,
237                     krb5_data *pkt, krb5_ticket **ticket,
238                     krb5_db_entry *krbtgt, int *nprincs,
239                     krb5_keyblock **subkey)
240 {
241     krb5_pa_data        * tmppa;
242     krb5_ap_req         * apreq;
243     krb5_error_code       retval;
244     krb5_data             scratch1;
245     krb5_data           * scratch = NULL;
246     krb5_boolean          foreign_server = FALSE;
247     krb5_auth_context     auth_context = NULL;
248     krb5_authenticator  * authenticator = NULL;
249     krb5_checksum       * his_cksum = NULL;
250     krb5_keyblock       * key = NULL;
251     krb5_kvno             kvno = 0;
252
253     *nprincs = 0;
254
255     tmppa = find_pa_data(request->padata, KRB5_PADATA_AP_REQ);
256     if (!tmppa)
257         return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
258
259     scratch1.length = tmppa->length;
260     scratch1.data = (char *)tmppa->contents;
261     if ((retval = decode_krb5_ap_req(&scratch1, &apreq)))
262         return retval;
263
264     if (isflagset(apreq->ap_options, AP_OPTS_USE_SESSION_KEY) ||
265         isflagset(apreq->ap_options, AP_OPTS_MUTUAL_REQUIRED)) {
266         krb5_klog_syslog(LOG_INFO, "TGS_REQ: SESSION KEY or MUTUAL");
267         retval = KRB5KDC_ERR_POLICY;
268         goto cleanup;
269     }
270
271     /* If the "server" principal in the ticket is not something
272        in the local realm, then we must refuse to service the request
273        if the client claims to be from the local realm.
274        
275        If we don't do this, then some other realm's nasty KDC can
276        claim to be authenticating a client from our realm, and we'll
277        give out tickets concurring with it!
278        
279        we set a flag here for checking below.
280        */
281     foreign_server = !is_local_principal(apreq->ticket->server);
282
283     if ((retval = krb5_auth_con_init(kdc_context, &auth_context)))
284         goto cleanup;
285
286     if ((retval = krb5_auth_con_setaddrs(kdc_context, auth_context, NULL,
287                                          from->address)) )
288         goto cleanup_auth_context;
289 #ifdef USE_RCACHE
290     if ((retval = krb5_auth_con_setrcache(kdc_context, auth_context,
291                                           kdc_rcache)))
292         goto cleanup_auth_context;
293 #endif
294
295     if ((retval = kdc_get_server_key(apreq->ticket, 0, foreign_server,
296                                      krbtgt, nprincs, &key, &kvno)))
297         goto cleanup_auth_context;
298     /*
299      * We do not use the KDB keytab because other parts of the TGS need the TGT key.
300      */
301     retval = krb5_auth_con_setuseruserkey(kdc_context, auth_context, key);
302     krb5_free_keyblock(kdc_context, key);
303     if (retval) 
304         goto cleanup_auth_context;
305
306     if ((retval = krb5_rd_req_decoded_anyflag(kdc_context, &auth_context, apreq, 
307                                       apreq->ticket->server, 
308                                       kdc_active_realm->realm_keytab,
309                                       NULL, ticket))) {
310 #ifdef USE_RCACHE
311         /*
312          * I'm not so sure that this is right, but it's better than nothing
313          * at all.
314          *
315          * If we choke in the rd_req because of the replay cache, then attempt
316          * to reinitialize the replay cache because somebody could have deleted
317          * it from underneath us (e.g. a cron job)
318          */
319         if ((retval == KRB5_RC_IO_IO) ||
320             (retval == KRB5_RC_IO_UNKNOWN)) {
321             (void) krb5_rc_close(kdc_context, kdc_rcache);
322             kdc_rcache = (krb5_rcache) NULL;
323             if (!(retval = kdc_initialize_rcache(kdc_context, (char *) NULL))) {
324                 if ((retval = krb5_auth_con_setrcache(kdc_context, auth_context,
325                                                       kdc_rcache)) ||
326                     (retval = krb5_rd_req_decoded_anyflag(kdc_context, &auth_context,
327                                                   apreq, apreq->ticket->server,
328                                                  kdc_active_realm->realm_keytab,
329                                                   NULL, ticket))
330                     )
331                     goto cleanup_auth_context;
332             }
333         } else
334             goto cleanup_auth_context; 
335 #else
336         goto cleanup_auth_context; 
337 #endif
338     }
339
340     /* "invalid flag" tickets can must be used to validate */
341     if (isflagset((*ticket)->enc_part2->flags, TKT_FLG_INVALID)
342         && !isflagset(request->kdc_options, KDC_OPT_VALIDATE)) {
343         retval = KRB5KRB_AP_ERR_TKT_INVALID;
344         goto cleanup_auth_context;
345     }
346
347     if ((retval = krb5_auth_con_getrecvsubkey(kdc_context,
348                                               auth_context, subkey)))
349         goto cleanup_auth_context;
350
351     if ((retval = krb5_auth_con_getauthenticator(kdc_context, auth_context,
352                                                  &authenticator)))
353         goto cleanup_auth_context;
354
355     /* Check for a checksum */
356     if (!(his_cksum = authenticator->checksum)) {
357         retval = KRB5KRB_AP_ERR_INAPP_CKSUM; 
358         goto cleanup_authenticator;
359     }
360
361     /* make sure the client is of proper lineage (see above) */
362     if (foreign_server && !find_pa_data(request->padata, KRB5_PADATA_FOR_USER)) {
363         if (is_local_principal((*ticket)->enc_part2->client)) {
364             /* someone in a foreign realm claiming to be local */
365             krb5_klog_syslog(LOG_INFO, "PROCESS_TGS: failed lineage check");
366             retval = KRB5KDC_ERR_POLICY;
367             goto cleanup_authenticator;
368         }
369     }
370
371     /*
372      * Check application checksum vs. tgs request
373      *   
374      * We try checksumming the req-body two different ways: first we
375      * try reaching into the raw asn.1 stream (if available), and
376      * checksum that directly; if that fails, then we try encoding
377      * using our local asn.1 library.
378      */
379     if (pkt && (fetch_asn1_field((unsigned char *) pkt->data,
380                                  1, 4, &scratch1) >= 0)) {
381         if (comp_cksum(kdc_context, &scratch1, *ticket, his_cksum)) {
382             if (!(retval = encode_krb5_kdc_req_body(request, &scratch))) 
383                 retval = comp_cksum(kdc_context, scratch, *ticket, his_cksum);
384             krb5_free_data(kdc_context, scratch);
385         }
386     }
387
388 cleanup_authenticator:
389     krb5_free_authenticator(kdc_context, authenticator);
390
391 cleanup_auth_context:
392     /* We do not want the free of the auth_context to close the rcache */
393 #ifdef USE_RCACHE
394     (void)  krb5_auth_con_setrcache(kdc_context, auth_context, 0);
395 #endif
396     krb5_auth_con_free(kdc_context, auth_context);
397
398 cleanup:
399     krb5_free_ap_req(kdc_context, apreq);
400     return retval;
401 }
402
403 /* XXX This function should no longer be necessary. 
404  * The KDC should take the keytab associated with the realm and pass that to 
405  * the krb5_rd_req_decode(). --proven
406  *
407  * It's actually still used by do_tgs_req() for u2u auth, and not too
408  * much else. -- tlyu
409  */
410 krb5_error_code
411 kdc_get_server_key(krb5_ticket *ticket, unsigned int flags,
412                    krb5_boolean match_enctype, krb5_db_entry *server,
413                    int *nprincs, krb5_keyblock **key, krb5_kvno *kvno)
414 {
415     krb5_error_code       retval;
416     krb5_boolean          more, similar;
417     krb5_key_data       * server_key;
418
419     *nprincs = 1;
420
421     retval = krb5_db_get_principal_ext(kdc_context,
422                                        ticket->server,
423                                        flags,
424                                        server,
425                                        nprincs,
426                                        &more);
427     if (retval) {
428         return(retval);
429     }
430     if (more) {
431         return(KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE);
432     } else if (*nprincs != 1) {
433         char *sname;
434
435         if (!krb5_unparse_name(kdc_context, ticket->server, &sname)) {
436             krb5_klog_syslog(LOG_ERR,"TGS_REQ: UNKNOWN SERVER: server='%s'",
437                              sname);
438             free(sname);
439         }
440         return(KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN);
441     }
442     if (server->attributes & KRB5_KDB_DISALLOW_SVR ||
443         server->attributes & KRB5_KDB_DISALLOW_ALL_TIX) {
444         retval = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
445         goto errout;
446     }
447     retval = krb5_dbe_find_enctype(kdc_context, server,
448                                    match_enctype ? ticket->enc_part.enctype : -1,
449                                    -1, (krb5_int32)ticket->enc_part.kvno,
450                                    &server_key);
451     if (retval)
452         goto errout;
453     if (!server_key) {
454         retval = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
455         goto errout;
456     }
457     if ((*key = (krb5_keyblock *)malloc(sizeof **key))) {
458         retval = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock,
459                                              server_key,
460                                              *key, NULL);
461     } else
462         retval = ENOMEM;
463     retval = krb5_c_enctype_compare(kdc_context, ticket->enc_part.enctype,
464                                     (*key)->enctype, &similar);
465     if (retval)
466         goto errout;
467     if (!similar) {
468         retval = KRB5_KDB_NO_PERMITTED_KEY;
469         goto errout;
470     }
471     (*key)->enctype = ticket->enc_part.enctype;
472     *kvno = server_key->key_data_kvno;
473 errout:
474     if (retval != 0) {
475         krb5_db_free_principal(kdc_context, server, *nprincs);
476         *nprincs = 0;
477     }
478
479     return retval;
480 }
481
482 /* This probably wants to be updated if you support last_req stuff */
483
484 static krb5_last_req_entry nolrentry = { KV5M_LAST_REQ_ENTRY, KRB5_LRQ_NONE, 0 };
485 static krb5_last_req_entry *nolrarray[] = { &nolrentry, 0 };
486
487 krb5_error_code
488 fetch_last_req_info(krb5_db_entry *dbentry, krb5_last_req_entry ***lrentry)
489 {
490     *lrentry = nolrarray;
491     return 0;
492 }
493
494
495 /* XXX!  This is a temporary place-holder */
496
497 krb5_error_code
498 check_hot_list(krb5_ticket *ticket)
499 {
500     return 0;
501 }
502
503
504 #define MAX_REALM_LN 500
505
506
507 /* 
508  * subrealm - determine if r2 is a subrealm of r1
509  *
510  *            SUBREALM takes two realms, r1 and r2, and 
511  *            determines if r2 is a subrealm of r1.   
512  *            r2 is a subrealm of r1 if (r1 is a prefix
513  *            of r2 AND r1 and r2 begin with a /) or if 
514  *            (r1 is a suffix of r2 and neither r1 nor r2
515  *            begin with a /).
516  *
517  * RETURNS:   If r2 is a subrealm, and r1 is a prefix, the number
518  *            of characters in the suffix of r2 is returned as a
519  *            negative number.
520  *
521  *            If r2 is a subrealm, and r1 is a suffix, the number
522  *            of characters in the prefix of r2 is returned as a
523  *            positive number.
524  *
525  *            If r2 is not a subrealm, SUBREALM returns 0.
526  */
527 static  int
528 subrealm(char *r1, char *r2)
529 {
530     size_t l1,l2;
531     l1 = strlen(r1);
532     l2 = strlen(r2);
533     if(l2 <= l1) return(0);
534     if((*r1 == '/') && (*r2 == '/') && (strncmp(r1,r2,l1) == 0)) return(l1-l2);
535     if((*r1 != '/') && (*r2 != '/') && (strncmp(r1,r2+l2-l1,l1) == 0))
536         return(l2-l1);
537     return(0);
538 }
539
540 /*
541  * add_to_transited  Adds the name of the realm which issued the
542  *                   ticket granting ticket on which the new ticket to
543  *                   be issued is based (note that this is the same as
544  *                   the realm of the server listed in the ticket
545  *                   granting ticket. 
546  *
547  * ASSUMPTIONS:  This procedure assumes that the transited field from
548  *               the existing ticket granting ticket already appears
549  *               in compressed form.  It will add the new realm while
550  *               maintaining that form.   As long as each successive
551  *               realm is added using this (or a similar) routine, the
552  *               transited field will be in compressed form.  The
553  *               basis step is an empty transited field which is, by
554  *               its nature, in its most compressed form.
555  *
556  * ARGUMENTS: krb5_data *tgt_trans  Transited field from TGT
557  *            krb5_data *new_trans  The transited field for the new ticket
558  *            krb5_principal tgs    Name of ticket granting server
559  *                                  This includes the realm of the KDC
560  *                                  that issued the ticket granting
561  *                                  ticket.  This is the realm that is
562  *                                  to be added to the transited field.
563  *            krb5_principal client Name of the client
564  *            krb5_principal server The name of the requested server.
565  *                                  This may be the an intermediate
566  *                                  ticket granting server.
567  *
568  *            The last two argument are needed since they are
569  *            implicitly part of the transited field of the new ticket
570  *            even though they are not explicitly listed.
571  *
572  * RETURNS:   krb5_error_code - Success, or out of memory
573  *
574  * MODIFIES:  new_trans:  ->length will contain the length of the new
575  *                        transited field.
576  * 
577  *                        If ->data was not null when this procedure
578  *                        is called, the memory referenced by ->data
579  *                        will be deallocated. 
580  *
581  *                        Memory will be allocated for the new transited field
582  *                        ->data will be updated to point to the newly
583  *                        allocated memory.  
584  *
585  * BUGS:  The space allocated for the new transited field is the
586  *        maximum that might be needed given the old transited field,
587  *        and the realm to be added.  This length is calculated
588  *        assuming that no compression of the new realm is possible.
589  *        This has no adverse consequences other than the allocation
590  *        of more space than required.  
591  *
592  *        This procedure will not yet use the null subfield notation,
593  *        and it will get confused if it sees it.
594  *
595  *        This procedure does not check for quoted commas in realm
596  *        names.
597  */
598
599 static char *
600 data2string (krb5_data *d)
601 {
602     char *s;
603     s = malloc(d->length + 1);
604     if (s) {
605         memcpy(s, d->data, d->length);
606         s[d->length] = 0;
607     }
608     return s;
609 }
610
611 krb5_error_code 
612 add_to_transited(krb5_data *tgt_trans, krb5_data *new_trans,
613                  krb5_principal tgs, krb5_principal client,
614                  krb5_principal server)
615 {
616   krb5_error_code retval;
617   char        *realm;
618   char        *trans;
619   char        *otrans, *otrans_ptr;
620   size_t       bufsize;
621
622   /* The following are for stepping through the transited field     */
623
624   char        prev[MAX_REALM_LN];
625   char        next[MAX_REALM_LN];
626   char        current[MAX_REALM_LN];
627   char        exp[MAX_REALM_LN];      /* Expanded current realm name     */
628
629   int         i;
630   int         clst, nlst;    /* count of last character in current and next */
631   int         pl, pl1;       /* prefix length                               */
632   int         added;         /* TRUE = new realm has been added             */
633
634   realm = data2string(krb5_princ_realm(kdc_context, tgs));
635   if (realm == NULL)
636       return(ENOMEM);
637
638   otrans = data2string(tgt_trans);
639   if (otrans == NULL) {
640       free(realm);
641       return(ENOMEM);
642   }
643   /* Keep track of start so we can free */
644   otrans_ptr = otrans;
645
646   /* +1 for null, 
647      +1 for extra comma which may be added between
648      +1 for potential space when leading slash in realm */
649   bufsize = strlen(realm) + strlen(otrans) + 3;
650   if (bufsize > MAX_REALM_LN)
651     bufsize = MAX_REALM_LN;
652   if (!(trans = (char *) malloc(bufsize))) {
653     retval = ENOMEM;
654     goto fail;
655   }
656
657   if (new_trans->data)  free(new_trans->data);
658   new_trans->data = trans;
659   new_trans->length = 0;
660
661   trans[0] = '\0';
662
663   /* For the purpose of appending, the realm preceding the first */
664   /* realm in the transited field is considered the null realm   */
665
666   prev[0] = '\0';
667
668   /* read field into current */
669   for (i = 0; *otrans != '\0';) {
670       if (*otrans == '\\') {
671           if (*(++otrans) == '\0')
672               break;
673           else
674               continue;
675       }
676       if (*otrans == ',') {
677           otrans++;
678           break;
679       }
680       current[i++] = *otrans++;
681       if (i >= MAX_REALM_LN) {
682           retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
683           goto fail;
684       }
685   }
686   current[i] = '\0';
687
688   added = (krb5_princ_realm(kdc_context, client)->length == strlen(realm) &&
689            !strncmp(krb5_princ_realm(kdc_context, client)->data, realm, strlen(realm))) ||
690           (krb5_princ_realm(kdc_context, server)->length == strlen(realm) &&
691            !strncmp(krb5_princ_realm(kdc_context, server)->data, realm, strlen(realm)));
692
693   while (current[0]) {
694
695     /* figure out expanded form of current name */
696
697     clst = strlen(current) - 1;
698     if (current[0] == ' ') {
699       strncpy(exp, current+1, sizeof(exp) - 1);
700       exp[sizeof(exp) - 1] = '\0';
701     }
702     else if ((current[0] == '/') && (prev[0] == '/')) {
703       strncpy(exp, prev, sizeof(exp) - 1);
704       exp[sizeof(exp) - 1] = '\0';
705       if (strlen(exp) + strlen(current) + 1 >= MAX_REALM_LN) {
706         retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
707         goto fail;
708       }
709       strncat(exp, current, sizeof(exp) - 1 - strlen(exp));
710     }
711     else if (current[clst] == '.') {
712       strncpy(exp, current, sizeof(exp) - 1);
713       exp[sizeof(exp) - 1] = '\0';
714       if (strlen(exp) + strlen(prev) + 1 >= MAX_REALM_LN) {
715         retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
716         goto fail;
717       }
718       strncat(exp, prev, sizeof(exp) - 1 - strlen(exp));
719     }
720     else {
721       strncpy(exp, current, sizeof(exp) - 1);
722       exp[sizeof(exp) - 1] = '\0';
723     }
724
725     /* read field into next */
726     for (i = 0; *otrans != '\0';) {
727         if (*otrans == '\\') {
728             if (*(++otrans) == '\0')
729                 break;
730             else
731                 continue;
732         }
733         if (*otrans == ',') {
734             otrans++;
735             break;
736         }
737         next[i++] = *otrans++;
738         if (i >= MAX_REALM_LN) {
739             retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
740             goto fail;
741         }
742     }
743     next[i] = '\0';
744     nlst = i - 1;
745
746     if (!strcmp(exp, realm))  added = TRUE;
747
748     /* If we still have to insert the new realm */
749
750     if (!added) {
751
752       /* Is the next field compressed?  If not, and if the new */
753       /* realm is a subrealm of the current realm, compress    */
754       /* the new realm, and insert immediately following the   */
755       /* current one.  Note that we can not do this if the next*/
756       /* field is already compressed since it would mess up    */
757       /* what has already been done.  In most cases, this is   */
758       /* not a problem because the realm to be added will be a */
759       /* subrealm of the next field too, and we will catch     */
760       /* it in a future iteration.                             */
761
762         /* Note that the second test here is an unsigned comparison,
763            so the first half (or a cast) is also required.  */
764       assert(nlst < 0 || nlst < (int)sizeof(next));
765       if ((nlst < 0 || next[nlst] != '.') &&
766           (next[0] != '/') &&
767           (pl = subrealm(exp, realm))) {
768         added = TRUE;
769         current[sizeof(current) - 1] = '\0';
770         if (strlen(current) + (pl>0?pl:-pl) + 2 >= MAX_REALM_LN) {
771           retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
772           goto fail;
773         }
774         strncat(current, ",", sizeof(current) - 1 - strlen(current));
775         if (pl > 0) {
776           strncat(current, realm, (unsigned) pl);
777         }
778         else {
779           strncat(current, realm+strlen(realm)+pl, (unsigned) (-pl));
780         }
781       }
782
783       /* Whether or not the next field is compressed, if the    */
784       /* realm to be added is a superrealm of the current realm,*/
785       /* then the current realm can be compressed.  First the   */
786       /* realm to be added must be compressed relative to the   */
787       /* previous realm (if possible), and then the current     */
788       /* realm compressed relative to the new realm.  Note that */
789       /* if the realm to be added is also a superrealm of the   */
790       /* previous realm, it would have been added earlier, and  */
791       /* we would not reach this step this time around.         */
792
793       else if ((pl = subrealm(realm, exp))) {
794         added      = TRUE;
795         current[0] = '\0';
796         if ((pl1 = subrealm(prev,realm))) {
797           if (strlen(current) + (pl1>0?pl1:-pl1) + 1 >= MAX_REALM_LN) {
798             retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
799             goto fail;
800           }
801           if (pl1 > 0) {
802             strncat(current, realm, (unsigned) pl1);
803           }
804           else {
805             strncat(current, realm+strlen(realm)+pl1, (unsigned) (-pl1));
806           }
807         }
808         else { /* If not a subrealm */
809           if ((realm[0] == '/') && prev[0]) {
810             if (strlen(current) + 2 >= MAX_REALM_LN) {
811               retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
812               goto fail;
813             }
814             strncat(current, " ", sizeof(current) - 1 - strlen(current));
815             current[sizeof(current) - 1] = '\0';
816           }
817           if (strlen(current) + strlen(realm) + 1 >= MAX_REALM_LN) {
818             retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
819             goto fail;
820           }
821           strncat(current, realm, sizeof(current) - 1 - strlen(current));
822           current[sizeof(current) - 1] = '\0';
823         }
824         if (strlen(current) + (pl>0?pl:-pl) + 2 >= MAX_REALM_LN) {
825           retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
826           goto fail;
827         }
828         strncat(current,",", sizeof(current) - 1 - strlen(current));
829         current[sizeof(current) - 1] = '\0';
830         if (pl > 0) {
831           strncat(current, exp, (unsigned) pl);
832         }
833         else {
834           strncat(current, exp+strlen(exp)+pl, (unsigned)(-pl));
835         }
836       }
837     }
838
839     if (new_trans->length != 0) {
840       if (strlcat(trans, ",", bufsize) >= bufsize) {
841         retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
842         goto fail;
843       }
844     }
845     if (strlcat(trans, current, bufsize) >= bufsize) {
846       retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
847       goto fail;
848     }
849     new_trans->length = strlen(trans);
850
851     strncpy(prev, exp, sizeof(prev) - 1);
852     prev[sizeof(prev) - 1] = '\0';
853     strncpy(current, next, sizeof(current) - 1);
854     current[sizeof(current) - 1] = '\0';
855   }
856
857   if (!added) {
858     if (new_trans->length != 0) {
859       if (strlcat(trans, ",", bufsize) >= bufsize) {
860         retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
861         goto fail;
862       }
863     }
864     if((realm[0] == '/') && trans[0]) {
865       if (strlcat(trans, " ", bufsize) >= bufsize) {
866         retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
867         goto fail;
868       }
869     }
870     if (strlcat(trans, realm, bufsize) >= bufsize) {
871       retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
872       goto fail;
873     }
874     new_trans->length = strlen(trans);
875   }
876
877   retval = 0;
878 fail:
879   free(realm);
880   free(otrans_ptr);
881   return (retval);
882 }
883
884 /*
885  * Routines that validate a AS request; checks a lot of things.  :-)
886  *
887  * Returns a Kerberos protocol error number, which is _not_ the same
888  * as a com_err error number!
889  */
890 #define AS_INVALID_OPTIONS (KDC_OPT_FORWARDED | KDC_OPT_PROXY |\
891 KDC_OPT_VALIDATE | KDC_OPT_RENEW | KDC_OPT_ENC_TKT_IN_SKEY)
892 int
893 validate_as_request(register krb5_kdc_req *request, krb5_db_entry client,
894                     krb5_db_entry server, krb5_timestamp kdc_time,
895                     const char **status)
896 {
897     int         errcode;
898     
899     /*
900      * If an option is set that is only allowed in TGS requests, complain.
901      */
902     if (request->kdc_options & AS_INVALID_OPTIONS) {
903         *status = "INVALID AS OPTIONS";
904         return KDC_ERR_BADOPTION;
905     }
906
907     /* The client's password must not be expired, unless the server is
908       a KRB5_KDC_PWCHANGE_SERVICE. */
909     if (client.pw_expiration && client.pw_expiration < kdc_time &&
910         !isflagset(server.attributes, KRB5_KDB_PWCHANGE_SERVICE)) {
911         *status = "CLIENT KEY EXPIRED";
912 #ifdef KRBCONF_VAGUE_ERRORS
913         return(KRB_ERR_GENERIC);
914 #else
915         return(KDC_ERR_KEY_EXP);
916 #endif
917     }
918
919     /* The client must not be expired */
920     if (client.expiration && client.expiration < kdc_time) {
921         *status = "CLIENT EXPIRED";
922 #ifdef KRBCONF_VAGUE_ERRORS
923         return(KRB_ERR_GENERIC);
924 #else
925         return(KDC_ERR_NAME_EXP);
926 #endif
927     }
928
929     /* The server must not be expired */
930     if (server.expiration && server.expiration < kdc_time) {
931         *status = "SERVICE EXPIRED";
932             return(KDC_ERR_SERVICE_EXP);
933     }
934
935     /*
936      * If the client requires password changing, then only allow the 
937      * pwchange service.
938      */
939     if (isflagset(client.attributes, KRB5_KDB_REQUIRES_PWCHANGE) &&
940         !isflagset(server.attributes, KRB5_KDB_PWCHANGE_SERVICE)) {
941         *status = "REQUIRED PWCHANGE";
942         return(KDC_ERR_KEY_EXP);
943     }
944
945     /* Client and server must allow postdating tickets */
946     if ((isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) ||
947          isflagset(request->kdc_options, KDC_OPT_POSTDATED)) && 
948         (isflagset(client.attributes, KRB5_KDB_DISALLOW_POSTDATED) ||
949          isflagset(server.attributes, KRB5_KDB_DISALLOW_POSTDATED))) {
950         *status = "POSTDATE NOT ALLOWED";
951         return(KDC_ERR_CANNOT_POSTDATE);
952     }
953
954     /*
955      * A Windows KDC will return KDC_ERR_PREAUTH_REQUIRED instead of
956      * KDC_ERR_POLICY in the following case:
957      *
958      *   - KDC_OPT_FORWARDABLE is set in KDCOptions but local
959      *     policy has KRB5_KDB_DISALLOW_FORWARDABLE set for the
960      *     client, and;
961      *   - KRB5_KDB_REQUIRES_PRE_AUTH is set for the client but
962      *     preauthentication data is absent in the request.
963      *
964      * Hence, this check most be done after the check for preauth
965      * data, and is now performed by validate_forwardable().
966      */
967 #if 0
968     /* Client and server must allow forwardable tickets */
969     if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) &&
970         (isflagset(client.attributes, KRB5_KDB_DISALLOW_FORWARDABLE) ||
971          isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))) {
972         *status = "FORWARDABLE NOT ALLOWED";
973         return(KDC_ERR_POLICY);
974     }
975 #endif
976     
977     /* Client and server must allow renewable tickets */
978     if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE) &&
979         (isflagset(client.attributes, KRB5_KDB_DISALLOW_RENEWABLE) ||
980          isflagset(server.attributes, KRB5_KDB_DISALLOW_RENEWABLE))) {
981         *status = "RENEWABLE NOT ALLOWED";
982         return(KDC_ERR_POLICY);
983     }
984     
985     /* Client and server must allow proxiable tickets */
986     if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE) &&
987         (isflagset(client.attributes, KRB5_KDB_DISALLOW_PROXIABLE) ||
988          isflagset(server.attributes, KRB5_KDB_DISALLOW_PROXIABLE))) {
989         *status = "PROXIABLE NOT ALLOWED";
990         return(KDC_ERR_POLICY);
991     }
992     
993     /* Check to see if client is locked out */
994     if (isflagset(client.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
995         *status = "CLIENT LOCKED OUT";
996         return(KDC_ERR_CLIENT_REVOKED);
997     }
998
999     /* Check to see if server is locked out */
1000     if (isflagset(server.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
1001         *status = "SERVICE LOCKED OUT";
1002         return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
1003     }
1004         
1005     /* Check to see if server is allowed to be a service */
1006     if (isflagset(server.attributes, KRB5_KDB_DISALLOW_SVR)) {
1007         *status = "SERVICE NOT ALLOWED";
1008         return(KDC_ERR_MUST_USE_USER2USER);
1009     }
1010
1011     /*
1012      * Check against local policy
1013      */
1014     errcode = against_local_policy_as(request, client, server,
1015                                       kdc_time, status); 
1016     if (errcode)
1017         return errcode;
1018
1019     return 0;
1020 }
1021
1022 int
1023 validate_forwardable(krb5_kdc_req *request, krb5_db_entry client,
1024                      krb5_db_entry server, krb5_timestamp kdc_time,
1025                      const char **status)
1026 {
1027     *status = NULL;
1028     if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) &&
1029         (isflagset(client.attributes, KRB5_KDB_DISALLOW_FORWARDABLE) ||
1030          isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))) {
1031         *status = "FORWARDABLE NOT ALLOWED";
1032         return(KDC_ERR_POLICY);
1033     } else
1034         return 0;
1035 }
1036
1037 #define ASN1_ID_CLASS   (0xc0)
1038 #define ASN1_ID_TYPE    (0x20)
1039 #define ASN1_ID_TAG     (0x1f)  
1040 #define ASN1_CLASS_UNIV (0)
1041 #define ASN1_CLASS_APP  (1)
1042 #define ASN1_CLASS_CTX  (2)
1043 #define ASN1_CLASS_PRIV (3)
1044 #define asn1_id_constructed(x)  (x & ASN1_ID_TYPE)
1045 #define asn1_id_primitive(x)    (!asn1_id_constructed(x))
1046 #define asn1_id_class(x)        ((x & ASN1_ID_CLASS) >> 6)
1047 #define asn1_id_tag(x)          (x & ASN1_ID_TAG)
1048
1049 /*
1050  * asn1length - return encoded length of value.
1051  *
1052  * passed a pointer into the asn.1 stream, which is updated
1053  * to point right after the length bits.
1054  *
1055  * returns -1 on failure.
1056  */
1057 static int
1058 asn1length(unsigned char **astream)
1059 {
1060     int length;         /* resulting length */
1061     int sublen;         /* sublengths */
1062     int blen;           /* bytes of length */ 
1063     unsigned char *p;   /* substring searching */       
1064
1065     if (**astream & 0x80) {
1066         blen = **astream & 0x7f;
1067         if (blen > 3) {
1068            return(-1);
1069         }
1070         for (++*astream, length = 0; blen; ++*astream, blen--) {
1071             length = (length << 8) | **astream;
1072         }
1073         if (length == 0) {
1074                 /* indefinite length, figure out by hand */
1075             p = *astream;
1076             p++;
1077             while (1) {
1078                 /* compute value length. */
1079                 if ((sublen = asn1length(&p)) < 0) {
1080                     return(-1);
1081                 }
1082                 p += sublen;
1083                 /* check for termination */
1084                 if ((!*p++) && (!*p)) {
1085                     p++;
1086                     break;
1087                 }
1088             }
1089             length = p - *astream;       
1090         }
1091     } else {
1092         length = **astream;
1093         ++*astream;
1094     } 
1095    return(length);
1096 }
1097
1098 /*
1099  * fetch_asn1_field - return raw asn.1 stream of subfield.
1100  *
1101  * this routine is passed a context-dependent tag number and "level" and returns
1102  * the size and length of the corresponding level subfield.
1103  *
1104  * levels and are numbered starting from 1.  
1105  *
1106  * returns 0 on success, -1 otherwise.
1107  */
1108 int
1109 fetch_asn1_field(unsigned char *astream, unsigned int level,
1110                  unsigned int field, krb5_data *data)
1111 {
1112     unsigned char *estream;     /* end of stream */
1113     int classes;                /* # classes seen so far this level */
1114     unsigned int levels = 0;            /* levels seen so far */
1115     int lastlevel = 1000;       /* last level seen */
1116     int length;                 /* various lengths */
1117     int tag;                    /* tag number */
1118     unsigned char savelen;      /* saved length of our field */
1119
1120     classes = -1;
1121     /* we assume that the first identifier/length will tell us 
1122        how long the entire stream is. */
1123     astream++;
1124     estream = astream;
1125     if ((length = asn1length(&astream)) < 0) {
1126         return(-1);
1127     }
1128     estream += length;
1129     /* search down the stream, checking identifiers.  we process identifiers
1130        until we hit the "level" we want, and then process that level for our
1131        subfield, always making sure we don't go off the end of the stream.  */
1132     while (astream < estream) {
1133         if (!asn1_id_constructed(*astream)) {
1134             return(-1);
1135         }
1136         if (asn1_id_class(*astream) == ASN1_CLASS_CTX) {
1137             if ((tag = (int)asn1_id_tag(*astream)) <= lastlevel) {
1138                 levels++;
1139                 classes = -1;
1140             }
1141             lastlevel = tag; 
1142             if (levels == level) {
1143                 /* in our context-dependent class, is this the one we're looking for ? */
1144                 if (tag == (int)field) {
1145                     /* return length and data */ 
1146                     astream++;
1147                     savelen = *astream;
1148                     if ((data->length = asn1length(&astream)) < 0) {
1149                         return(-1);
1150                     }
1151                     /* if the field length is indefinite, we will have to subtract two
1152                        (terminating octets) from the length returned since we don't want
1153                        to pass any info from the "wrapper" back.  asn1length will always return
1154                        the *total* length of the field, not just what's contained in it */ 
1155                     if ((savelen & 0xff) == 0x80) {
1156                       data->length -=2 ;
1157                     }
1158                     data->data = (char *)astream;
1159                     return(0);
1160                 } else if (tag <= classes) {
1161                     /* we've seen this class before, something must be wrong */
1162                     return(-1);
1163                 } else {
1164                     classes = tag;
1165                 }
1166             }
1167         }
1168         /* if we're not on our level yet, process this value.  otherwise skip over it */
1169         astream++;
1170         if ((length = asn1length(&astream)) < 0) {
1171             return(-1);
1172         }
1173         if (levels == level) {
1174             astream += length;
1175         }
1176     }
1177     return(-1);
1178 }       
1179
1180 /*
1181  * Routines that validate a TGS request; checks a lot of things.  :-)
1182  *
1183  * Returns a Kerberos protocol error number, which is _not_ the same
1184  * as a com_err error number!
1185  */
1186 #define TGS_OPTIONS_HANDLED (KDC_OPT_FORWARDABLE | KDC_OPT_FORWARDED | \
1187                              KDC_OPT_PROXIABLE | KDC_OPT_PROXY | \
1188                              KDC_OPT_ALLOW_POSTDATE | KDC_OPT_POSTDATED | \
1189                              KDC_OPT_RENEWABLE | KDC_OPT_RENEWABLE_OK | \
1190                              KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_RENEW | \
1191                              KDC_OPT_VALIDATE | KDC_OPT_CANONICALIZE | KDC_OPT_CNAME_IN_ADDL_TKT)
1192 #define NO_TGT_OPTION (KDC_OPT_FORWARDED | KDC_OPT_PROXY | KDC_OPT_RENEW | \
1193                        KDC_OPT_VALIDATE)
1194
1195 int
1196 validate_tgs_request(register krb5_kdc_req *request, krb5_db_entry server,
1197                      krb5_ticket *ticket, krb5_timestamp kdc_time,
1198                      const char **status)
1199 {
1200     int         errcode;
1201     int         st_idx = 0;
1202
1203     /*
1204      * If an illegal option is set, ignore it.
1205      */
1206     request->kdc_options &= TGS_OPTIONS_HANDLED;
1207
1208     /* Check to see if server has expired */
1209     if (server.expiration && server.expiration < kdc_time) {
1210         *status = "SERVICE EXPIRED";
1211         return(KDC_ERR_SERVICE_EXP);
1212     }
1213
1214     /*
1215      * Verify that the server principal in authdat->ticket is correct
1216      * (either the ticket granting service or the service that was
1217      * originally requested)
1218      */
1219     if (request->kdc_options & NO_TGT_OPTION) {
1220         if (!krb5_principal_compare(kdc_context, ticket->server, request->server)) {
1221             *status = "SERVER DIDN'T MATCH TICKET FOR RENEW/FORWARD/ETC";
1222             return(KDC_ERR_SERVER_NOMATCH);
1223         }
1224     } else {
1225         /*
1226          * OK, we need to validate the krbtgt service in the ticket.
1227          *
1228          * The krbtgt service is of the form:
1229          *              krbtgt/realm-A@realm-B
1230          *
1231          * Realm A is the "server realm"; the realm of the
1232          * server of the requested ticket must match this realm.
1233          * Of course, it should be a realm serviced by this KDC.
1234          *
1235          * Realm B is the "client realm"; this is what should be
1236          * added to the transited field.  (which is done elsewhere)
1237          */
1238
1239         /* Make sure there are two components... */
1240         if (krb5_princ_size(kdc_context, ticket->server) != 2) {
1241             *status = "BAD TGS SERVER LENGTH";
1242             return KRB_AP_ERR_NOT_US;
1243         }
1244         /* ...that the first component is krbtgt... */
1245         if (!krb5_is_tgs_principal(ticket->server)) {
1246             *status = "BAD TGS SERVER NAME";
1247             return KRB_AP_ERR_NOT_US;
1248         }
1249         /* ...and that the second component matches the server realm... */
1250         if ((krb5_princ_size(kdc_context, ticket->server) <= 1) ||
1251             !data_eq(*krb5_princ_component(kdc_context, ticket->server, 1),
1252                      *krb5_princ_realm(kdc_context, request->server))) {
1253             *status = "BAD TGS SERVER INSTANCE";
1254             return KRB_AP_ERR_NOT_US;
1255         }
1256         /* XXX add check that second component must match locally
1257          * supported realm?
1258          */
1259
1260         /* Server must allow TGS based issuances */
1261         if (isflagset(server.attributes, KRB5_KDB_DISALLOW_TGT_BASED)) {
1262             *status = "TGT BASED NOT ALLOWED";
1263             return(KDC_ERR_POLICY);
1264         }
1265     }
1266             
1267     /* TGS must be forwardable to get forwarded or forwardable ticket */
1268     if ((isflagset(request->kdc_options, KDC_OPT_FORWARDED) ||
1269          isflagset(request->kdc_options, KDC_OPT_FORWARDABLE)) &&
1270         !isflagset(ticket->enc_part2->flags, TKT_FLG_FORWARDABLE)) {
1271         *status = "TGT NOT FORWARDABLE";
1272
1273         return KDC_ERR_BADOPTION;
1274     }
1275
1276     /* TGS must be proxiable to get proxiable ticket */    
1277     if ((isflagset(request->kdc_options, KDC_OPT_PROXY) ||
1278          isflagset(request->kdc_options, KDC_OPT_PROXIABLE)) &&
1279         !isflagset(ticket->enc_part2->flags, TKT_FLG_PROXIABLE)) {
1280         *status = "TGT NOT PROXIABLE";
1281         return KDC_ERR_BADOPTION;
1282     }
1283
1284     /* TGS must allow postdating to get postdated ticket */
1285     if ((isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) ||
1286           isflagset(request->kdc_options, KDC_OPT_POSTDATED)) &&
1287         !isflagset(ticket->enc_part2->flags, TKT_FLG_MAY_POSTDATE)) {
1288         *status = "TGT NOT POSTDATABLE";
1289         return KDC_ERR_BADOPTION;
1290     }
1291
1292     /* can only validate invalid tix */
1293     if (isflagset(request->kdc_options, KDC_OPT_VALIDATE) &&
1294         !isflagset(ticket->enc_part2->flags, TKT_FLG_INVALID)) {
1295         *status = "VALIDATE VALID TICKET";
1296         return KDC_ERR_BADOPTION;
1297     }
1298
1299     /* can only renew renewable tix */
1300     if ((isflagset(request->kdc_options, KDC_OPT_RENEW) ||
1301           isflagset(request->kdc_options, KDC_OPT_RENEWABLE)) &&
1302         !isflagset(ticket->enc_part2->flags, TKT_FLG_RENEWABLE)) {
1303         *status = "TICKET NOT RENEWABLE";
1304         return KDC_ERR_BADOPTION;
1305     }
1306
1307     /* can not proxy ticket granting tickets */
1308     if (isflagset(request->kdc_options, KDC_OPT_PROXY) &&
1309         (!request->server->data ||
1310          !data_eq_string(request->server->data[0], KRB5_TGS_NAME))) {
1311         *status = "CAN'T PROXY TGT";
1312         return KDC_ERR_BADOPTION;
1313     }
1314     
1315     /* Server must allow forwardable tickets */
1316     if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) &&
1317         isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE)) {
1318         *status = "NON-FORWARDABLE TICKET";
1319         return(KDC_ERR_POLICY);
1320     }
1321     
1322     /* Server must allow renewable tickets */
1323     if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE) &&
1324         isflagset(server.attributes, KRB5_KDB_DISALLOW_RENEWABLE)) {
1325         *status = "NON-RENEWABLE TICKET";
1326         return(KDC_ERR_POLICY);
1327     }
1328     
1329     /* Server must allow proxiable tickets */
1330     if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE) &&
1331         isflagset(server.attributes, KRB5_KDB_DISALLOW_PROXIABLE)) {
1332         *status = "NON-PROXIABLE TICKET";
1333         return(KDC_ERR_POLICY);
1334     }
1335     
1336     /* Server must allow postdated tickets */
1337     if (isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) &&
1338         isflagset(server.attributes, KRB5_KDB_DISALLOW_POSTDATED)) {
1339         *status = "NON-POSTDATABLE TICKET";
1340         return(KDC_ERR_CANNOT_POSTDATE);
1341     }
1342     
1343     /* Server must allow DUP SKEY requests */
1344     if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY) &&
1345         isflagset(server.attributes, KRB5_KDB_DISALLOW_DUP_SKEY)) {
1346         *status = "DUP_SKEY DISALLOWED";
1347         return(KDC_ERR_POLICY);
1348     }
1349
1350     /* Server must not be locked out */
1351     if (isflagset(server.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
1352         *status = "SERVER LOCKED OUT";
1353         return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
1354     }
1355         
1356     /* Server must be allowed to be a service */
1357     if (isflagset(server.attributes, KRB5_KDB_DISALLOW_SVR)) {
1358         *status = "SERVER NOT ALLOWED";
1359         return(KDC_ERR_MUST_USE_USER2USER);
1360     }
1361
1362     /* Check the hot list */
1363     if (check_hot_list(ticket)) {
1364         *status = "HOT_LIST";
1365         return(KRB_AP_ERR_REPEAT);
1366     }
1367     
1368     /* Check the start time vs. the KDC time */
1369     if (isflagset(request->kdc_options, KDC_OPT_VALIDATE)) {
1370         if (ticket->enc_part2->times.starttime > kdc_time) {
1371             *status = "NOT_YET_VALID";
1372             return(KRB_AP_ERR_TKT_NYV);
1373         }
1374     }
1375     
1376     /*
1377      * Check the renew_till time.  The endtime was already
1378      * been checked in the initial authentication check.
1379      */
1380     if (isflagset(request->kdc_options, KDC_OPT_RENEW) &&
1381         (ticket->enc_part2->times.renew_till < kdc_time)) {
1382         *status = "TKT_EXPIRED";
1383         return(KRB_AP_ERR_TKT_EXPIRED);
1384     }
1385     
1386     /*
1387      * Checks for ENC_TKT_IN_SKEY:
1388      *
1389      * (1) Make sure the second ticket exists
1390      * (2) Make sure it is a ticket granting ticket
1391      */
1392     if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) {
1393         if (!request->second_ticket ||
1394             !request->second_ticket[st_idx]) {
1395             *status = "NO_2ND_TKT";
1396             return(KDC_ERR_BADOPTION);
1397         }
1398         if (!krb5_principal_compare(kdc_context, request->second_ticket[st_idx]->server,
1399                                     tgs_server)) {
1400                 *status = "2ND_TKT_NOT_TGS";
1401                 return(KDC_ERR_POLICY);
1402         }
1403         st_idx++;
1404     }
1405     if (isflagset(request->kdc_options, KDC_OPT_CNAME_IN_ADDL_TKT)) {
1406         if (!request->second_ticket ||
1407             !request->second_ticket[st_idx]) {
1408             *status = "NO_2ND_TKT";
1409             return(KDC_ERR_BADOPTION);
1410         }
1411         st_idx++;
1412     }
1413
1414     /* Check for hardware preauthentication */
1415     if (isflagset(server.attributes, KRB5_KDB_REQUIRES_HW_AUTH) &&
1416         !isflagset(ticket->enc_part2->flags,TKT_FLG_HW_AUTH)) {
1417         *status = "NO HW PREAUTH";
1418         return KRB_ERR_GENERIC;
1419     }
1420
1421     /* Check for any kind of preauthentication */
1422     if (isflagset(server.attributes, KRB5_KDB_REQUIRES_PRE_AUTH) &&
1423         !isflagset(ticket->enc_part2->flags, TKT_FLG_PRE_AUTH)) {
1424         *status = "NO PREAUTH";
1425         return KRB_ERR_GENERIC;
1426     }
1427     
1428     /*
1429      * Check local policy
1430      */
1431     errcode = against_local_policy_tgs(request, server, ticket, status);
1432     if (errcode)
1433         return errcode;
1434     
1435     
1436     return 0;
1437 }
1438
1439 /*
1440  * This function returns 1 if the dbentry has a key for a specified
1441  * keytype, and 0 if not.
1442  */
1443 int
1444 dbentry_has_key_for_enctype(krb5_context context, krb5_db_entry *client,
1445                             krb5_enctype enctype)
1446 {
1447     krb5_error_code     retval;
1448     krb5_key_data       *datap;
1449
1450     retval = krb5_dbe_find_enctype(context, client, enctype,
1451                                    -1, 0, &datap);
1452     if (retval)
1453         return 0;
1454     else
1455         return 1;
1456 }
1457
1458 /*
1459  * This function returns 1 if the entity referenced by this
1460  * structure can support the a particular encryption system, and 0 if
1461  * not.
1462  *
1463  * XXX eventually this information should be looked up in the
1464  * database.  Since it isn't, we use some hueristics and attribute
1465  * options bits for now.
1466  */
1467 int
1468 dbentry_supports_enctype(krb5_context context, krb5_db_entry *client,
1469                          krb5_enctype enctype)
1470 {
1471     /*
1472      * If it's DES_CBC_MD5, there's a bit in the attribute mask which
1473      * checks to see if we support it.  For now, treat it as always
1474      * clear.
1475      *
1476      * In theory everything's supposed to support DES_CBC_MD5, but
1477      * that's not the reality....
1478      */
1479     if (enctype == ENCTYPE_DES_CBC_MD5)
1480         return 0;
1481
1482     /*
1483      * XXX we assume everything can understand DES_CBC_CRC
1484      */
1485     if (enctype == ENCTYPE_DES_CBC_CRC)
1486         return 1;
1487     
1488     /*
1489      * If we have a key for the encryption system, we assume it's
1490      * supported.
1491      */
1492     return dbentry_has_key_for_enctype(context, client, enctype);
1493 }
1494
1495 /*
1496  * This function returns the keytype which should be selected for the
1497  * session key.  It is based on the ordered list which the user
1498  * requested, and what the KDC and the application server can support.
1499  */
1500 krb5_enctype
1501 select_session_keytype(krb5_context context, krb5_db_entry *server,
1502                        int nktypes, krb5_enctype *ktype)
1503 {
1504     int         i;
1505     
1506     for (i = 0; i < nktypes; i++) {
1507         if (!krb5_c_valid_enctype(ktype[i]))
1508             continue;
1509
1510         if (!krb5_is_permitted_enctype(context, ktype[i]))
1511             continue;
1512
1513         if (dbentry_supports_enctype(context, server, ktype[i]))
1514             return ktype[i];
1515     }
1516     return 0;
1517 }
1518
1519 /*
1520  * This function returns salt information for a particular client_key
1521  */
1522 krb5_error_code
1523 get_salt_from_key(krb5_context context, krb5_principal client,
1524                   krb5_key_data *client_key, krb5_data *salt)
1525 {
1526     krb5_error_code             retval;
1527     krb5_data *                 realm;
1528     
1529     salt->data = 0;
1530     salt->length = SALT_TYPE_NO_LENGTH;
1531     
1532     if (client_key->key_data_ver == 1)
1533         return 0;
1534
1535     switch (client_key->key_data_type[1]) {
1536     case KRB5_KDB_SALTTYPE_NORMAL:
1537         break;
1538     case KRB5_KDB_SALTTYPE_V4:
1539         /* send an empty (V4) salt */
1540         salt->data = 0;
1541         salt->length = 0;
1542         break;
1543     case KRB5_KDB_SALTTYPE_NOREALM:
1544         if ((retval = krb5_principal2salt_norealm(context, client, salt)))
1545             return retval;
1546         break;
1547     case KRB5_KDB_SALTTYPE_AFS3:
1548         /* send the same salt as with onlyrealm - but with no type info,
1549            we just hope they figure it out on the other end. */
1550         /* fall through to onlyrealm: */
1551     case KRB5_KDB_SALTTYPE_ONLYREALM:
1552         realm = krb5_princ_realm(context, client);
1553         salt->length = realm->length;
1554         if ((salt->data = malloc(realm->length)) == NULL)
1555             return ENOMEM;
1556         memcpy(salt->data, realm->data, realm->length);
1557         break;
1558     case KRB5_KDB_SALTTYPE_SPECIAL:
1559         salt->length = client_key->key_data_length[1];
1560         if ((salt->data = malloc(salt->length)) == NULL)
1561             return ENOMEM;
1562         memcpy(salt->data, client_key->key_data_contents[1], salt->length);
1563         break;
1564     }
1565     return 0;
1566 }
1567
1568 /*
1569  * Limit strings to a "reasonable" length to prevent crowding out of
1570  * other useful information in the log entry
1571  */
1572 #define NAME_LENGTH_LIMIT 128
1573
1574 void limit_string(char *name)
1575 {
1576         int     i;
1577
1578         if (!name)
1579                 return;
1580
1581         if (strlen(name) < NAME_LENGTH_LIMIT)
1582                 return;
1583
1584         i = NAME_LENGTH_LIMIT-4;
1585         name[i++] = '.';
1586         name[i++] = '.';
1587         name[i++] = '.';
1588         name[i] = '\0';
1589         return;
1590 }
1591
1592 /*
1593  * L10_2 = log10(2**x), rounded up; log10(2) ~= 0.301.
1594  */
1595 #define L10_2(x) ((int)(((x * 301) + 999) / 1000))
1596
1597 /*
1598  * Max length of sprintf("%ld") for an int of type T; includes leading
1599  * minus sign and terminating NUL.
1600  */
1601 #define D_LEN(t) (L10_2(sizeof(t) * CHAR_BIT) + 2)
1602
1603 void
1604 ktypes2str(char *s, size_t len, int nktypes, krb5_enctype *ktype)
1605 {
1606     int i;
1607     char stmp[D_LEN(krb5_enctype) + 1];
1608     char *p;
1609
1610     if (nktypes < 0
1611         || len < (sizeof(" etypes {...}") + D_LEN(int))) {
1612         *s = '\0';
1613         return;
1614     }
1615
1616     snprintf(s, len, "%d etypes {", nktypes);
1617     for (i = 0; i < nktypes; i++) {
1618         snprintf(stmp, sizeof(stmp), "%s%ld", i ? " " : "", (long)ktype[i]);
1619         if (strlen(s) + strlen(stmp) + sizeof("}") > len)
1620             break;
1621         strlcat(s, stmp, len);
1622     }
1623     if (i < nktypes) {
1624         /*
1625          * We broke out of the loop. Try to truncate the list.
1626          */
1627         p = s + strlen(s);
1628         while (p - s + sizeof("...}") > len) {
1629             while (p > s && *p != ' ' && *p != '{')
1630                 *p-- = '\0';
1631             if (p > s && *p == ' ') {
1632                 *p-- = '\0';
1633                 continue;
1634             }
1635         }
1636         strlcat(s, "...", len);
1637     }
1638     strlcat(s, "}", len);
1639     return;
1640 }
1641
1642 void
1643 rep_etypes2str(char *s, size_t len, krb5_kdc_rep *rep)
1644 {
1645     char stmp[sizeof("ses=") + D_LEN(krb5_enctype)];
1646
1647     if (len < (3 * D_LEN(krb5_enctype)
1648                + sizeof("etypes {rep= tkt= ses=}"))) {
1649         *s = '\0';
1650         return;
1651     }
1652
1653     snprintf(s, len, "etypes {rep=%ld", (long)rep->enc_part.enctype);
1654
1655     if (rep->ticket != NULL) {
1656         snprintf(stmp, sizeof(stmp),
1657                  " tkt=%ld", (long)rep->ticket->enc_part.enctype);
1658         strlcat(s, stmp, len);
1659     }
1660
1661     if (rep->ticket != NULL
1662         && rep->ticket->enc_part2 != NULL
1663         && rep->ticket->enc_part2->session != NULL) {
1664         snprintf(stmp, sizeof(stmp), " ses=%ld",
1665                  (long)rep->ticket->enc_part2->session->enctype);
1666         strlcat(s, stmp, len);
1667     }
1668     strlcat(s, "}", len);
1669     return;
1670 }
1671
1672 krb5_error_code
1673 get_principal_locked (krb5_context kcontext,
1674                       krb5_const_principal search_for,
1675                       krb5_db_entry *entries, int *nentries,
1676                       krb5_boolean *more)
1677 {
1678     return krb5_db_get_principal (kcontext, search_for, entries, nentries,
1679                                   more);
1680 }
1681
1682 krb5_error_code
1683 get_principal (krb5_context kcontext,
1684                krb5_const_principal search_for,
1685                krb5_db_entry *entries, int *nentries, krb5_boolean *more)
1686 {
1687     /* Eventually this will be used to manage locking while looking up
1688        principals in the database.  */
1689     return get_principal_locked (kcontext, search_for, entries, nentries,
1690                                  more);
1691 }
1692
1693
1694 krb5_error_code
1695 sign_db_authdata (krb5_context context,
1696                   unsigned int flags,
1697                   krb5_const_principal client_princ,
1698                   krb5_db_entry *client,
1699                   krb5_db_entry *server,
1700                   krb5_db_entry *krbtgt,
1701                   krb5_keyblock *client_key,
1702                   krb5_keyblock *server_key,
1703                   krb5_timestamp authtime,
1704                   krb5_authdata **tgs_authdata,
1705                   krb5_authdata ***ret_authdata,
1706                   krb5_db_entry *ad_entry,
1707                   int *ad_nprincs)
1708 {
1709     krb5_error_code code;
1710     kdb_sign_auth_data_req req;
1711     kdb_sign_auth_data_rep rep;
1712     krb5_data req_data;
1713     krb5_data rep_data;
1714
1715     *ret_authdata = NULL;
1716     memset(ad_entry, 0, sizeof(*ad_entry));
1717     *ad_nprincs = 0;
1718
1719     memset(&req, 0, sizeof(req));
1720     memset(&rep, 0, sizeof(rep));
1721
1722     req.flags                   = flags;
1723     req.client_princ            = client_princ;
1724     req.client                  = client;
1725     req.server                  = server;
1726     req.krbtgt                  = krbtgt;
1727     req.client_key              = client_key;
1728     req.server_key              = server_key;
1729     req.authtime                = authtime;
1730     req.auth_data               = tgs_authdata;
1731
1732     rep.entry                   = ad_entry;
1733     rep.nprincs                 = 0;
1734
1735     req_data.data = (void *)&req;
1736     req_data.length = sizeof(req);
1737
1738     rep_data.data = (void *)&rep;
1739     rep_data.length = sizeof(rep);
1740
1741     code = krb5_db_invoke(context,
1742                           KRB5_KDB_METHOD_SIGN_AUTH_DATA,
1743                           &req_data,
1744                           &rep_data);
1745
1746     *ret_authdata = rep.auth_data;
1747     *ad_nprincs = rep.nprincs;
1748  
1749     return code;
1750 }
1751
1752 static krb5_error_code
1753 verify_s4u2self_checksum(krb5_context context,
1754                          krb5_keyblock *key,
1755                          krb5_pa_for_user *req)
1756 {
1757     krb5_error_code             code;
1758     int                         i;
1759     krb5_int32                  name_type;
1760     char                        *p;
1761     krb5_data                   data;
1762     krb5_boolean                valid = FALSE;
1763
1764     if (!krb5_c_is_keyed_cksum(req->cksum.checksum_type)) {
1765         return KRB5KRB_AP_ERR_INAPP_CKSUM;
1766     }
1767
1768     /*
1769      * Checksum is over name type and string components of
1770      * client principal name and auth_package.
1771      */
1772     data.length = 4;
1773     for (i = 0; i < krb5_princ_size(context, req->user); i++) {
1774         data.length += krb5_princ_component(context, req->user, i)->length;
1775     }
1776     data.length += krb5_princ_realm(context, req->user)->length;
1777     data.length += req->auth_package.length;
1778
1779     p = data.data = malloc(data.length);
1780     if (data.data == NULL) {
1781         return ENOMEM;
1782     }
1783
1784     name_type = krb5_princ_type(context, req->user);
1785     p[0] = (name_type >> 0 ) & 0xFF;
1786     p[1] = (name_type >> 8 ) & 0xFF;
1787     p[2] = (name_type >> 16) & 0xFF;
1788     p[3] = (name_type >> 24) & 0xFF;
1789     p += 4;
1790
1791     for (i = 0; i < krb5_princ_size(context, req->user); i++) {
1792         memcpy(p, krb5_princ_component(context, req->user, i)->data,
1793                krb5_princ_component(context, req->user, i)->length);
1794         p += krb5_princ_component(context, req->user, i)->length;
1795     }
1796
1797     memcpy(p, krb5_princ_realm(context, req->user)->data,
1798            krb5_princ_realm(context, req->user)->length);
1799     p += krb5_princ_realm(context, req->user)->length;
1800
1801     memcpy(p, req->auth_package.data, req->auth_package.length);
1802     p += req->auth_package.length;
1803
1804     code = krb5_c_verify_checksum(context,
1805                                   key,
1806                                   KRB5_KEYUSAGE_APP_DATA_CKSUM,
1807                                   &data,
1808                                   &req->cksum,
1809                                   &valid);
1810
1811     if (code == 0 && valid == FALSE)
1812         code = KRB5KRB_AP_ERR_BAD_INTEGRITY;
1813
1814     free(data.data);
1815
1816     return code;
1817 }
1818
1819 /*
1820  * Protocol transition validation code based on AS-REQ
1821  * validation code
1822  */
1823 static int
1824 validate_s4u2self_request(krb5_kdc_req *request,
1825                           const krb5_db_entry *client,
1826                           krb5_timestamp kdc_time,
1827                           const char **status)
1828 {
1829     int                         errcode;
1830     krb5_db_entry               server = { 0 };
1831  
1832     /* The client's password must not be expired, unless the server is
1833       a KRB5_KDC_PWCHANGE_SERVICE. */
1834     if (client->pw_expiration && client->pw_expiration < kdc_time) {
1835         *status = "CLIENT KEY EXPIRED";
1836         return KDC_ERR_KEY_EXP;
1837     }
1838
1839     /* The client must not be expired */
1840     if (client->expiration && client->expiration < kdc_time) {
1841         *status = "CLIENT EXPIRED";
1842         return KDC_ERR_NAME_EXP;
1843     }
1844
1845     /*
1846      * If the client requires password changing, then return an
1847      * error; S4U2Self cannot be used to change a password.
1848      */
1849     if (isflagset(client->attributes, KRB5_KDB_REQUIRES_PWCHANGE)) {
1850         *status = "REQUIRED PWCHANGE";
1851         return KDC_ERR_KEY_EXP;
1852     }
1853
1854     /* Check to see if client is locked out */
1855     if (isflagset(client->attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
1856         *status = "CLIENT LOCKED OUT";
1857         return KDC_ERR_C_PRINCIPAL_UNKNOWN;
1858     }
1859
1860     /*
1861      * Check against local policy
1862      */
1863     errcode = against_local_policy_as(request, *client, server,
1864                                       kdc_time, status); 
1865     if (errcode)
1866         return errcode;
1867
1868     return 0;
1869 }
1870
1871 /*
1872  * Protocol transition (S4U2Self)
1873  */
1874 krb5_error_code
1875 kdc_process_s4u2self_req(krb5_context context,
1876                          krb5_kdc_req *request,
1877                          krb5_const_principal client_princ,
1878                          const krb5_db_entry *server,
1879                          krb5_keyblock *subkey,
1880                          krb5_timestamp kdc_time,
1881                          krb5_pa_for_user **for_user,
1882                          krb5_db_entry *princ,
1883                          int *nprincs,
1884                          const char **status)
1885 {
1886     krb5_error_code             code;
1887     krb5_pa_data                **pa_data;
1888     krb5_data                   req_data;
1889     krb5_boolean                more;
1890
1891     *nprincs = 0;
1892     memset(princ, 0, sizeof(*princ));
1893
1894     if (request->padata == NULL) {
1895         return 0;
1896     }
1897
1898     for (pa_data = request->padata; *pa_data != NULL; pa_data++) {
1899         if ((*pa_data)->pa_type == KRB5_PADATA_FOR_USER)
1900             break;
1901     }
1902     if (*pa_data == NULL) {
1903         return 0;
1904     }
1905
1906 #if 0
1907     /*
1908      * Ignore request if the server principal is a TGS, not so much
1909      * to avoid unconstrained tickets being issued (as that would
1910      * require knowing the TGS key anyway) but so that we do not
1911      * block the server referral path.
1912      */
1913     if (krb5_is_tgs_principal(server->princ)) {
1914         return 0;
1915     }
1916 #endif
1917
1918     *status = "PROCESS_S4U2SELF_REQUEST";
1919
1920     req_data.length = (*pa_data)->length;
1921     req_data.data = (char *)(*pa_data)->contents;
1922
1923     code = decode_krb5_pa_for_user(&req_data, for_user);
1924     if (code) {
1925         return code;
1926     }
1927
1928     if (krb5_princ_type(context, (*for_user)->user) !=
1929         KRB5_NT_ENTERPRISE_PRINCIPAL) {
1930         *status = "INVALID_S4U2SELF_REQUEST";
1931         return KRB5KDC_ERR_POLICY;
1932     }
1933
1934     code = verify_s4u2self_checksum(context, subkey, *for_user);
1935     if (code) {
1936         *status = "INVALID_S4U2SELF_CHECKSUM";
1937         krb5_free_pa_for_user(kdc_context, *for_user);
1938         *for_user = NULL;
1939         return code;
1940     }
1941     if (!krb5_principal_compare_flags(context, request->server, client_princ,
1942                                       KRB5_PRINCIPAL_COMPARE_ENTERPRISE)) {
1943         *status = "INVALID_S4U2SELF_REQUEST";
1944         return KRB5KDC_ERR_POLICY;
1945     }
1946
1947     /*
1948      * Protocol transition is mutually exclusive with renew/forward/etc
1949      * as well as user-to-user and constrained delegation.
1950      *
1951      * We can assert from this check that the header ticket was a TGT, as
1952      * that is validated previously in validate_tgs_request().
1953      */
1954     if (request->kdc_options & (NO_TGT_OPTION | KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_CNAME_IN_ADDL_TKT)) {
1955         return KRB5KDC_ERR_BADOPTION;
1956     }
1957
1958     /*
1959      * Do not attempt to lookup principals in foreign realms.
1960      */
1961     if (is_local_principal((*for_user)->user)) {
1962         *nprincs = 1;
1963         code = krb5_db_get_principal_ext(kdc_context,
1964                                          (*for_user)->user,
1965                                          KRB5_KDB_FLAG_INCLUDE_PAC,
1966                                          princ, nprincs, &more);
1967         if (code) {
1968             *status = "LOOKING_UP_S4U2SELF_PRINCIPAL";
1969             *nprincs = 0;
1970             return code; /* caller can free for_user */
1971         }
1972
1973         if (more) {
1974             *status = "NON_UNIQUE_S4U2SELF_PRINCIPAL";
1975             return KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE;
1976         } else if (*nprincs != 1) {
1977             *status = "UNKNOWN_S4U2SELF_PRINCIPAL";
1978             return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
1979         }
1980
1981         code = validate_s4u2self_request(request, princ, kdc_time, status);
1982         if (code) {
1983             return code;
1984         }
1985     }
1986
1987     *status = NULL;
1988
1989     return 0;
1990 }
1991
1992 static krb5_error_code
1993 check_allowed_to_delegate_to(krb5_context context,
1994                              const krb5_db_entry *server,
1995                              krb5_const_principal proxy)
1996 {
1997     kdb_check_allowed_to_delegate_req   req;
1998     krb5_data                   req_data;
1999     krb5_data                   rep_data;
2000     krb5_error_code             code;
2001
2002     /* Can't get a TGT (otherwise it would be unconstrained delegation) */
2003     if (krb5_is_tgs_principal(proxy)) {
2004         return KRB5KDC_ERR_POLICY;
2005     }
2006
2007     /* Must be in same realm */
2008     if (!krb5_realm_compare(context, server->princ, proxy)) {
2009         return KRB5KDC_ERR_BADOPTION;
2010     }
2011
2012     req.server = server;
2013     req.proxy = proxy;
2014
2015     req_data.data = (void *)&req;
2016     req_data.length = sizeof(req);
2017
2018     rep_data.data = NULL;
2019     rep_data.length = 0;
2020
2021     code = krb5_db_invoke(context,
2022                           KRB5_KDB_METHOD_CHECK_ALLOWED_TO_DELEGATE,
2023                           &req_data,
2024                           &rep_data);
2025     if (code == KRB5_KDB_DBTYPE_NOSUP) {
2026         code = KRB5KDC_ERR_POLICY;
2027     }
2028
2029     assert(rep_data.length == 0);
2030
2031     return code;
2032 }
2033
2034 krb5_error_code
2035 kdc_process_s4u2proxy_req(krb5_context context,
2036                           krb5_kdc_req *request,
2037                           const krb5_enc_tkt_part *t2enc,
2038                           const krb5_db_entry *server,
2039                           krb5_const_principal server_princ,
2040                           krb5_const_principal proxy_princ,
2041                           const char **status)
2042 {
2043     krb5_error_code errcode;
2044
2045     /*
2046      * Constrained delegation is mutually exclusive with renew/forward/etc.
2047      * We can assert from this check that the header ticket was a TGT, as
2048      * that is validated previously in validate_tgs_request().
2049      */
2050     if (request->kdc_options & (NO_TGT_OPTION | KDC_OPT_ENC_TKT_IN_SKEY)) {
2051         return KRB5KDC_ERR_BADOPTION;
2052     }
2053
2054     /* Ensure that evidence ticket server matches TGT client */
2055     if (!krb5_principal_compare(kdc_context,
2056                                 server->princ, /* after canon */
2057                                 server_princ)) {
2058         return KRB5KDC_ERR_SERVER_NOMATCH;
2059     }
2060
2061     if (!isflagset(t2enc->flags, TKT_FLG_FORWARDABLE)) {
2062         *status = "EVIDENCE_TKT_NOT_FORWARDABLE";
2063         return KRB5_TKT_NOT_FORWARDABLE;
2064     }
2065
2066     /* Backend policy check */
2067     errcode = check_allowed_to_delegate_to(kdc_context,
2068                                            server, proxy_princ);
2069     if (errcode) {
2070         *status = "NOT_ALLOWED_TO_DELEGATE";
2071         return errcode;
2072     }
2073
2074     return 0;
2075 }
2076
2077 krb5_error_code
2078 kdc_check_transited_list(krb5_context context,
2079                          const krb5_data *trans,
2080                          const krb5_data *realm1,
2081                          const krb5_data *realm2)
2082 {
2083     krb5_error_code             code;
2084     kdb_check_transited_realms_req      req;
2085     krb5_data                   req_data;
2086     krb5_data                   rep_data;
2087
2088     /* First check using krb5.conf */
2089     code = krb5_check_transited_list(kdc_context, trans, realm1, realm2);
2090     if (code)
2091         return code;
2092
2093     memset(&req, 0, sizeof(req));
2094
2095     req.tr_contents             = trans;
2096     req.client_realm            = realm1;
2097     req.server_realm            = realm2;
2098
2099     req_data.data = (void *)&req;
2100     req_data.length = sizeof(req);
2101
2102     rep_data.data = NULL;
2103     rep_data.length = 0;
2104
2105     code = krb5_db_invoke(context,
2106                           KRB5_KDB_METHOD_CHECK_TRANSITED_REALMS,
2107                           &req_data,
2108                           &rep_data);
2109     if (code == KRB5_KDB_DBTYPE_NOSUP) {
2110         code = 0;
2111     }
2112
2113     assert(rep_data.length == 0);
2114
2115     return code;
2116 }
2117
2118 krb5_error_code
2119 audit_as_request(krb5_kdc_req *request,
2120                  krb5_db_entry *client,
2121                  krb5_db_entry *server,
2122                  krb5_timestamp authtime,
2123                  krb5_error_code errcode)
2124 {
2125     krb5_error_code             code;
2126     kdb_audit_as_req            req;
2127     krb5_data                   req_data;
2128     krb5_data                   rep_data;
2129
2130     memset(&req, 0, sizeof(req));
2131
2132     req.request                 = request;
2133     req.client                  = client;
2134     req.server                  = server;
2135     req.authtime                = authtime;
2136     req.error_code              = errcode;
2137
2138     req_data.data = (void *)&req;
2139     req_data.length = sizeof(req);
2140
2141     rep_data.data = NULL;
2142     rep_data.length = 0;
2143
2144     code = krb5_db_invoke(kdc_context,
2145                           KRB5_KDB_METHOD_AUDIT_AS,
2146                           &req_data,
2147                           &rep_data);
2148     if (code == KRB5_KDB_DBTYPE_NOSUP) {
2149         return 0;
2150     }
2151
2152     assert(rep_data.length == 0);
2153
2154     return code;
2155 }
2156
2157 krb5_error_code
2158 audit_tgs_request(krb5_kdc_req *request,
2159                   krb5_const_principal client,
2160                   krb5_db_entry *server,
2161                   krb5_timestamp authtime,
2162                   krb5_error_code errcode)
2163 {
2164     krb5_error_code             code;
2165     kdb_audit_tgs_req           req;
2166     krb5_data                   req_data;
2167     krb5_data                   rep_data;
2168
2169     memset(&req, 0, sizeof(req));
2170
2171     req.request                 = request;
2172     req.client                  = client;
2173     req.server                  = server;
2174     req.authtime                = authtime;
2175     req.error_code              = errcode;
2176
2177     req_data.data = (void *)&req;
2178     req_data.length = sizeof(req);
2179
2180     rep_data.data = NULL;
2181     rep_data.length = 0;
2182
2183     code = krb5_db_invoke(kdc_context,
2184                           KRB5_KDB_METHOD_AUDIT_TGS,
2185                           &req_data,
2186                           &rep_data);
2187     if (code == KRB5_KDB_DBTYPE_NOSUP) {
2188         return 0;
2189     }
2190
2191     assert(rep_data.length == 0);
2192
2193     return code;
2194 }
2195
2196 krb5_error_code
2197 validate_transit_path(krb5_context context,
2198                       krb5_const_principal client,
2199                       krb5_db_entry *server,
2200                       krb5_db_entry *krbtgt)
2201 {
2202     /* Incoming */
2203     if (isflagset(server->attributes, KRB5_KDB_XREALM_NON_TRANSITIVE)) {
2204         return KRB5KDC_ERR_PATH_NOT_ACCEPTED;
2205     }
2206
2207     /* Outgoing */
2208     if (isflagset(krbtgt->attributes, KRB5_KDB_XREALM_NON_TRANSITIVE) &&
2209         (!krb5_principal_compare(context, server->princ, krbtgt->princ) ||
2210          !krb5_realm_compare(context, client, krbtgt->princ))) {
2211         return KRB5KDC_ERR_PATH_NOT_ACCEPTED;
2212     }
2213
2214     return 0;
2215 }
2216
2217
2218 /* Main logging routines for ticket requests.
2219
2220    There are a few simple cases -- unparseable requests mainly --
2221    where messages are logged otherwise, but once a ticket request can
2222    be decoded in some basic way, these routines are used for logging
2223    the details.  */
2224
2225 /* "status" is null to indicate success.  */
2226 /* Someday, pass local address/port as well.  */
2227 void
2228 log_as_req(const krb5_fulladdr *from,
2229            krb5_kdc_req *request, krb5_kdc_rep *reply,
2230            const char *cname, const char *sname,
2231            krb5_timestamp authtime,
2232            const char *status, krb5_error_code errcode, const char *emsg)
2233 {
2234     const char *fromstring = 0;
2235     char fromstringbuf[70];
2236     char ktypestr[128];
2237     const char *cname2 = cname ? cname : "<unknown client>";
2238     const char *sname2 = sname ? sname : "<unknown server>";
2239
2240     fromstring = inet_ntop(ADDRTYPE2FAMILY (from->address->addrtype),
2241                            from->address->contents,
2242                            fromstringbuf, sizeof(fromstringbuf));
2243     if (!fromstring)
2244         fromstring = "<unknown>";
2245     ktypes2str(ktypestr, sizeof(ktypestr),
2246                request->nktypes, request->ktype);
2247
2248     if (status == NULL) {
2249         /* success */
2250         char rep_etypestr[128];
2251         rep_etypes2str(rep_etypestr, sizeof(rep_etypestr), reply);
2252         krb5_klog_syslog(LOG_INFO,
2253                          "AS_REQ (%s) %s: ISSUE: authtime %d, %s, %s for %s",
2254                          ktypestr, fromstring, authtime,
2255                          rep_etypestr, cname2, sname2);
2256     } else {
2257         /* fail */
2258         krb5_klog_syslog(LOG_INFO, "AS_REQ (%s) %s: %s: %s for %s%s%s",
2259                          ktypestr, fromstring, status, 
2260                          cname2, sname2, emsg ? ", " : "", emsg ? emsg : "");
2261     }
2262 #if 0
2263     /* Sun (OpenSolaris) version would probably something like this.
2264        The client and server names passed can be null, unlike in the
2265        logging routines used above.  Note that a struct in_addr is
2266        used, but the real address could be an IPv6 address.  */
2267     audit_krb5kdc_as_req(some in_addr *, (in_port_t)from->port, 0,
2268                          cname, sname, errcode);
2269 #endif
2270 }
2271
2272 /* Here "status" must be non-null.  Error code
2273    KRB5KDC_ERR_SERVER_NOMATCH is handled specially.  */
2274 void
2275 log_tgs_req(const krb5_fulladdr *from,
2276             krb5_kdc_req *request, krb5_kdc_rep *reply,
2277             const char *cname, const char *sname, const char *altcname,
2278             krb5_timestamp authtime,
2279             const char *status, krb5_error_code errcode, const char *emsg)
2280 {
2281     char ktypestr[128];
2282     const char *fromstring = 0;
2283     char fromstringbuf[70];
2284     char rep_etypestr[128];
2285
2286     fromstring = inet_ntop(ADDRTYPE2FAMILY(from->address->addrtype),
2287                            from->address->contents,
2288                            fromstringbuf, sizeof(fromstringbuf));
2289     if (!fromstring)
2290         fromstring = "<unknown>";
2291     ktypes2str(ktypestr, sizeof(ktypestr), request->nktypes, request->ktype);
2292     if (!errcode)
2293         rep_etypes2str(rep_etypestr, sizeof(rep_etypestr), reply);
2294     else
2295         rep_etypestr[0] = 0;
2296
2297     /* Differences: server-nomatch message logs 2nd ticket's client
2298        name (useful), and doesn't log ktypestr (probably not
2299        important).  */
2300     if (errcode != KRB5KDC_ERR_SERVER_NOMATCH)
2301         krb5_klog_syslog(LOG_INFO,
2302                          "TGS_REQ (%s) %s: %s: authtime %d, %s%s %s for %s%s%s",
2303                          ktypestr,
2304                          fromstring, status, authtime,
2305                          rep_etypestr,
2306                          !errcode ? "," : "",
2307                          cname ? cname : "<unknown client>",
2308                          sname ? sname : "<unknown server>",
2309                          errcode ? ", " : "",
2310                          errcode ? emsg : "");
2311     else
2312         krb5_klog_syslog(LOG_INFO,
2313                          "TGS_REQ %s: %s: authtime %d, %s for %s, 2nd tkt client %s",
2314                          fromstring, status, authtime,
2315                          cname ? cname : "<unknown client>",
2316                          sname ? sname : "<unknown server>",
2317                          altcname ? altcname : "<unknown>");
2318
2319     /* OpenSolaris: audit_krb5kdc_tgs_req(...)  or
2320        audit_krb5kdc_tgs_req_2ndtktmm(...) */
2321 }
2322
2323 void
2324 log_tgs_alt_tgt(krb5_principal p)
2325 {
2326     char *sname;
2327     if (krb5_unparse_name(kdc_context, p, &sname)) {
2328         krb5_klog_syslog(LOG_INFO,
2329                          "TGS_REQ: issuing alternate <un-unparseable> TGT");
2330     } else {
2331         limit_string(sname);
2332         krb5_klog_syslog(LOG_INFO, "TGS_REQ: issuing TGT %s", sname);
2333         free(sname);
2334     }
2335     /* OpenSolaris: audit_krb5kdc_tgs_req_alt_tgt(...) */
2336 }
2337