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