reapply memchr patch
[krb5.git] / src / kdc / do_tgs_req.c
1 /*
2  * kdc/do_tgs_req.c
3  *
4  * Copyright 1990,1991,2001,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  * KDC Routines to deal with TGS_REQ's
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 "com_err.h"
59
60 #include <syslog.h>
61 #ifdef HAVE_NETINET_IN_H
62 #include <sys/types.h>
63 #include <netinet/in.h>
64 #ifndef hpux
65 #include <arpa/inet.h>
66 #endif
67 #endif
68
69 #include "kdc_util.h"
70 #include "policy.h"
71 #include "extern.h"
72 #include "adm_proto.h"
73 #include <ctype.h>
74
75 static void 
76 find_alternate_tgs(krb5_kdc_req *,krb5_db_entry *,
77                    krb5_boolean *,int *);
78
79 static krb5_error_code 
80 prepare_error_tgs(krb5_kdc_req *,krb5_ticket *,int,
81                   krb5_principal,krb5_data **,const char *);
82
83 static krb5_int32
84 prep_reprocess_req(krb5_kdc_req *,krb5_principal *);
85
86 /*ARGSUSED*/
87 krb5_error_code
88 process_tgs_req(krb5_data *pkt, const krb5_fulladdr *from,
89                 krb5_data **response)
90 {
91     krb5_keyblock * subkey = 0;
92     krb5_kdc_req *request = 0;
93     krb5_db_entry server;
94     krb5_kdc_rep reply;
95     krb5_enc_kdc_rep_part reply_encpart;
96     krb5_ticket ticket_reply, *header_ticket = 0;
97     int st_idx = 0;
98     krb5_enc_tkt_part enc_tkt_reply;
99     krb5_transited enc_tkt_transited;
100     int newtransited = 0;
101     krb5_error_code retval = 0;
102     int nprincs = 0;
103     krb5_boolean more;
104     krb5_timestamp kdc_time, authtime=0;
105     krb5_keyblock session_key;
106     krb5_timestamp until, rtime;
107     krb5_keyblock encrypting_key;
108     krb5_keyblock *mkey_ptr;
109     krb5_key_data  *server_key;
110     char *cname = 0, *sname = 0, *altcname = 0;
111     krb5_last_req_entry *nolrarray[2], nolrentry;
112     krb5_enctype useenctype;
113     int errcode, errcode2;
114     register int i;
115     int firstpass = 1;
116     const char        *status = 0;
117     krb5_enc_tkt_part *header_enc_tkt = NULL; /* ticket granting or evidence ticket */
118     krb5_db_entry client, krbtgt;
119     int c_nprincs = 0, k_nprincs = 0;
120     krb5_pa_for_user *for_user = NULL;           /* protocol transition request */
121     krb5_authdata **kdc_issued_auth_data = NULL; /* auth data issued by KDC */
122     unsigned int c_flags = 0, s_flags = 0;       /* client/server KDB flags */
123     char *s4u_name = NULL;
124     krb5_boolean is_referral, db_ref_done = FALSE;
125     const char *emsg = NULL;
126     krb5_data *tgs_1 =NULL, *server_1 = NULL;
127     krb5_principal krbtgt_princ;
128     krb5_kvno ticket_kvno = 0;
129
130     session_key.contents = NULL;
131     
132     retval = decode_krb5_tgs_req(pkt, &request);
133     if (retval)
134         return retval;
135
136     /*
137      * setup_server_realm() sets up the global realm-specific data pointer.
138      */
139     if ((retval = setup_server_realm(request->server))) {
140         krb5_free_kdc_req(kdc_context, request);
141         return retval;
142     }
143     errcode = kdc_process_tgs_req(request, from, pkt, &header_ticket,
144                                   &krbtgt, &k_nprincs, &subkey);
145     if (header_ticket && header_ticket->enc_part2 &&
146         (errcode2 = krb5_unparse_name(kdc_context, 
147                                       header_ticket->enc_part2->client,
148                                       &cname))) {
149         status = "UNPARSING CLIENT";
150         errcode = errcode2;
151         goto cleanup;
152     }
153     limit_string(cname);
154     
155     if (errcode) {
156         status = "PROCESS_TGS";
157         goto cleanup;
158     }
159
160     if (!header_ticket) {
161         errcode = KRB5_NO_TKT_SUPPLIED;        /* XXX? */
162         status="UNEXPECTED NULL in header_ticket";
163         goto cleanup;
164     }
165
166     /*
167      * Pointer to the encrypted part of the header ticket, which may be
168      * replaced to point to the encrypted part of the evidence ticket
169      * if constrained delegation is used. This simplifies the number of
170      * special cases for constrained delegation.
171      */
172     header_enc_tkt = header_ticket->enc_part2;
173     
174     /*
175      * We've already dealt with the AP_REQ authentication, so we can
176      * use header_ticket freely.  The encrypted part (if any) has been
177      * decrypted with the session key.
178      */
179
180     /* XXX make sure server here has the proper realm...taken from AP_REQ
181        header? */
182
183     if (isflagset(request->kdc_options, KDC_OPT_CANONICALIZE)) {
184         setflag(c_flags, KRB5_KDB_FLAG_CANONICALIZE);
185         setflag(s_flags, KRB5_KDB_FLAG_CANONICALIZE);
186     }
187
188     db_ref_done = FALSE;
189 ref_tgt_again:
190     nprincs = 1;
191     if ((errcode = krb5_unparse_name(kdc_context, request->server, &sname))) {
192         status = "UNPARSING SERVER";
193         goto cleanup;
194     }
195     limit_string(sname);
196
197     errcode = krb5_db_get_principal_ext(kdc_context,
198                                         request->server,
199                                         s_flags,
200                                         &server,
201                                         &nprincs,
202                                         &more);
203     if (errcode) {
204         status = "LOOKING_UP_SERVER";
205         nprincs = 0;
206         goto cleanup;
207     }
208 tgt_again:
209     if (more) {
210         status = "NON_UNIQUE_PRINCIPAL";
211         errcode = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE;
212         goto cleanup;
213     } else if (nprincs != 1) {
214         /*
215          * might be a request for a TGT for some other realm; we
216          * should do our best to find such a TGS in this db
217          */
218         if (firstpass ) {
219
220             if ( krb5_is_tgs_principal(request->server) == TRUE) { /* Principal is a name of krb ticket service */
221                 if (krb5_princ_size(kdc_context, request->server) == 2) { 
222                                           
223                     server_1 = krb5_princ_component(kdc_context, request->server, 1);
224                     tgs_1 = krb5_princ_component(kdc_context, tgs_server, 1);
225
226                     if (!tgs_1 || !data_eq(*server_1, *tgs_1)) {
227                         krb5_db_free_principal(kdc_context, &server, nprincs);
228                         find_alternate_tgs(request, &server, &more, &nprincs);
229                         firstpass = 0;
230                         goto tgt_again;
231                     }
232                 }  
233                 krb5_db_free_principal(kdc_context, &server, nprincs);
234                 status = "UNKNOWN_SERVER";
235                 errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
236                 goto cleanup;
237
238             } else if ( db_ref_done == FALSE) {
239                 retval = prep_reprocess_req(request, &krbtgt_princ);
240                 if (!retval) {
241                     krb5_free_principal(kdc_context, request->server);
242                     retval = krb5_copy_principal(kdc_context, krbtgt_princ, &(request->server));
243                     if (!retval) {
244                         db_ref_done = TRUE;
245                         if (sname != NULL) 
246                             free(sname);
247                         goto ref_tgt_again;
248                     }
249                 }
250             }
251         }
252
253         krb5_db_free_principal(kdc_context, &server, nprincs);
254         status = "UNKNOWN_SERVER";
255         errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
256         goto cleanup;
257     }
258
259     if ((errcode = krb5_timeofday(kdc_context, &kdc_time))) {
260         status = "TIME_OF_DAY";
261         goto cleanup;
262     }
263     
264     if ((retval = validate_tgs_request(request, server, header_ticket,
265                                        kdc_time, &status))) {
266     if (!status)
267         status = "UNKNOWN_REASON";
268         errcode = retval + ERROR_TABLE_BASE_krb5;
269         goto cleanup;
270     }
271
272     if (!is_local_principal(header_enc_tkt->client))
273         setflag(c_flags, KRB5_KDB_FLAG_CROSS_REALM);
274
275     is_referral = krb5_is_tgs_principal(server.princ) &&
276         !krb5_principal_compare(kdc_context, tgs_server, server.princ);
277
278     /* Check for protocol transition */
279     errcode = kdc_process_s4u2self_req(kdc_context, request, header_enc_tkt->client,
280                                        &server, header_enc_tkt->session, kdc_time,
281                                        &for_user, &client, &c_nprincs, &status);
282     if (errcode)
283         goto cleanup;
284     if (for_user != NULL)
285         setflag(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION);
286
287     /*
288      * We pick the session keytype here....
289      * 
290      * Some special care needs to be taken in the user-to-user
291      * case, since we don't know what keytypes the application server
292      * which is doing user-to-user authentication can support.  We
293      * know that it at least must be able to support the encryption
294      * type of the session key in the TGT, since otherwise it won't be
295      * able to decrypt the U2U ticket!  So we use that in preference
296      * to anything else.
297      */
298     useenctype = 0;
299     if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY |
300                         KDC_OPT_CNAME_IN_ADDL_TKT)) {
301         krb5_keyblock *        st_sealing_key;
302         krb5_kvno       st_srv_kvno;
303         krb5_enctype    etype;
304         krb5_db_entry    st_client;
305         int             st_nprincs = 0;
306
307         /*
308          * Get the key for the second ticket, and decrypt it.
309          */
310         if ((errcode = kdc_get_server_key(request->second_ticket[st_idx],
311                                           c_flags,
312                                           TRUE, /* match_enctype */
313                                           &st_client,
314                                           &st_nprincs,
315                                           &st_sealing_key,
316                                           &st_srv_kvno))) {
317             status = "2ND_TKT_SERVER";
318             goto cleanup;
319         }
320         errcode = krb5_decrypt_tkt_part(kdc_context, st_sealing_key,
321                                     request->second_ticket[st_idx]);
322         krb5_free_keyblock(kdc_context, st_sealing_key);
323         if (errcode) {
324             status = "2ND_TKT_DECRYPT";
325             krb5_db_free_principal(kdc_context, &st_client, st_nprincs);
326             goto cleanup;
327         }
328         
329         etype = request->second_ticket[st_idx]->enc_part2->session->enctype;
330         if (!krb5_c_valid_enctype(etype)) {
331             status = "BAD_ETYPE_IN_2ND_TKT";
332             errcode = KRB5KDC_ERR_ETYPE_NOSUPP;
333             krb5_db_free_principal(kdc_context, &st_client, st_nprincs);
334             goto cleanup;
335         }
336         
337         for (i = 0; i < request->nktypes; i++) {
338             if (request->ktype[i] == etype) {
339                 useenctype = etype;
340                 break;
341             }
342         }
343
344         if (isflagset(request->kdc_options, KDC_OPT_CNAME_IN_ADDL_TKT)) {
345             /* Do constrained delegation protocol and authorization checks */
346             errcode = kdc_process_s4u2proxy_req(kdc_context,
347                                                 request,
348                                                 request->second_ticket[st_idx]->enc_part2,
349                                                 &st_client,
350                                                 header_ticket->enc_part2->client,
351                                                 request->server,
352                                                 &status);
353             if (errcode)
354                 goto cleanup;
355
356             setflag(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION);
357
358             assert(krb5_is_tgs_principal(header_ticket->server));
359  
360             /* From now on, use evidence ticket as header ticket */
361             header_enc_tkt = request->second_ticket[st_idx]->enc_part2;
362
363             assert(c_nprincs == 0); /* assured by kdc_process_s4u2self_req() */
364
365             client = st_client;
366             c_nprincs = st_nprincs;
367         } else {
368             /* "client" is not used for user2user */
369             krb5_db_free_principal(kdc_context, &st_client, st_nprincs);
370         }
371     }
372
373     /*
374      * Select the keytype for the ticket session key.
375      */
376     if ((useenctype == 0) &&
377         (useenctype = select_session_keytype(kdc_context, &server,
378         request->nktypes,
379         request->ktype)) == 0) {
380         /* unsupported ktype */
381         status = "BAD_ENCRYPTION_TYPE";
382         errcode = KRB5KDC_ERR_ETYPE_NOSUPP;
383         goto cleanup;
384     }
385     
386     errcode = krb5_c_make_random_key(kdc_context, useenctype, &session_key);
387
388     if (errcode) {
389         /* random key failed */
390         status = "RANDOM_KEY_FAILED";
391         goto cleanup;
392     }
393
394     authtime = header_enc_tkt->times.authtime;
395
396     if (is_referral)
397         ticket_reply.server = server.princ;
398     else
399         ticket_reply.server = request->server; /* XXX careful for realm... */
400
401     enc_tkt_reply.flags = 0;
402     enc_tkt_reply.times.starttime = 0;
403
404     if (isflagset(server.attributes, KRB5_KDB_OK_AS_DELEGATE) &&
405         !is_referral) {
406         /* Ensure that we are not returning a referral */
407         setflag(enc_tkt_reply.flags, TKT_FLG_OK_AS_DELEGATE);
408     }
409
410     /*
411      * Fix header_ticket's starttime; if it's zero, fill in the
412      * authtime's value.
413      */
414     if (!(header_enc_tkt->times.starttime))
415         header_enc_tkt->times.starttime = header_enc_tkt->times.authtime;
416
417     /* don't use new addresses unless forwarded, see below */
418
419     enc_tkt_reply.caddrs = header_enc_tkt->caddrs;
420     /* noaddrarray[0] = 0; */
421     reply_encpart.caddrs = 0;/* optional...don't put it in */
422     reply_encpart.enc_padata = NULL;
423
424     /* It should be noted that local policy may affect the  */
425     /* processing of any of these flags.  For example, some */
426     /* realms may refuse to issue renewable tickets         */
427
428     if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE))
429         setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE);
430     if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION)) {
431         if (!krb5_is_tgs_principal(server.princ) &&
432             is_local_principal(server.princ)) {
433             if (isflagset(server.attributes, KRB5_KDB_OK_TO_AUTH_AS_DELEGATE))
434                 setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE);
435             else
436                 clear(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE);
437         }
438         if (isflagset(client.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))
439             clear(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE);
440     }
441     if (isflagset(request->kdc_options, KDC_OPT_FORWARDED)) {
442         setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDED);
443
444         /* include new addresses in ticket & reply */
445
446         enc_tkt_reply.caddrs = request->addresses;
447         reply_encpart.caddrs = request->addresses;
448     }        
449     if (isflagset(header_enc_tkt->flags, TKT_FLG_FORWARDED))
450         setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDED);
451
452     if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE))
453         setflag(enc_tkt_reply.flags, TKT_FLG_PROXIABLE);
454
455     if (isflagset(request->kdc_options, KDC_OPT_PROXY)) {
456         setflag(enc_tkt_reply.flags, TKT_FLG_PROXY);
457
458         /* include new addresses in ticket & reply */
459
460         enc_tkt_reply.caddrs = request->addresses;
461         reply_encpart.caddrs = request->addresses;
462     }
463
464     if (isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE))
465         setflag(enc_tkt_reply.flags, TKT_FLG_MAY_POSTDATE);
466
467     if (isflagset(request->kdc_options, KDC_OPT_POSTDATED)) {
468         setflag(enc_tkt_reply.flags, TKT_FLG_POSTDATED);
469         setflag(enc_tkt_reply.flags, TKT_FLG_INVALID);
470         enc_tkt_reply.times.starttime = request->from;
471     } else
472         enc_tkt_reply.times.starttime = kdc_time;
473
474     if (isflagset(request->kdc_options, KDC_OPT_VALIDATE)) {
475         assert(isflagset(c_flags, KRB5_KDB_FLAGS_S4U) == 0);
476         /* BEWARE of allocation hanging off of ticket & enc_part2, it belongs
477            to the caller */
478         ticket_reply = *(header_ticket);
479         enc_tkt_reply = *(header_ticket->enc_part2);
480         clear(enc_tkt_reply.flags, TKT_FLG_INVALID);
481     }
482
483     if (isflagset(request->kdc_options, KDC_OPT_RENEW)) {
484         krb5_deltat old_life;
485
486         assert(isflagset(c_flags, KRB5_KDB_FLAGS_S4U) == 0);
487         /* BEWARE of allocation hanging off of ticket & enc_part2, it belongs
488            to the caller */
489         ticket_reply = *(header_ticket);
490         enc_tkt_reply = *(header_ticket->enc_part2);
491
492         old_life = enc_tkt_reply.times.endtime - enc_tkt_reply.times.starttime;
493
494         enc_tkt_reply.times.starttime = kdc_time;
495         enc_tkt_reply.times.endtime =
496             min(header_ticket->enc_part2->times.renew_till,
497                 kdc_time + old_life);
498     } else {
499         /* not a renew request */
500         enc_tkt_reply.times.starttime = kdc_time;
501         until = (request->till == 0) ? kdc_infinity : request->till;
502         enc_tkt_reply.times.endtime =
503             min(until, min(enc_tkt_reply.times.starttime + server.max_life,
504                min(enc_tkt_reply.times.starttime + max_life_for_realm,
505                    header_enc_tkt->times.endtime)));
506         if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE_OK) &&
507             (enc_tkt_reply.times.endtime < request->till) &&
508             isflagset(header_enc_tkt->flags, TKT_FLG_RENEWABLE)) {
509             setflag(request->kdc_options, KDC_OPT_RENEWABLE);
510             request->rtime =
511                 min(request->till, header_enc_tkt->times.renew_till);
512         }
513     }
514     rtime = (request->rtime == 0) ? kdc_infinity : request->rtime;
515
516     if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE)) {
517         /* already checked above in policy check to reject request for a
518            renewable ticket using a non-renewable ticket */
519         setflag(enc_tkt_reply.flags, TKT_FLG_RENEWABLE);
520         enc_tkt_reply.times.renew_till =
521                         min(rtime,
522                             min(header_enc_tkt->times.renew_till,
523                                 enc_tkt_reply.times.starttime +
524                                 min(server.max_renewable_life,
525                                 max_renewable_life_for_realm)));
526     } else {
527         enc_tkt_reply.times.renew_till = 0;
528     }
529     
530     /*
531      * Set authtime to be the same as header_ticket's
532      */
533     enc_tkt_reply.times.authtime = header_enc_tkt->times.authtime;
534     
535     /*
536      * Propagate the preauthentication flags through to the returned ticket.
537      */
538     if (isflagset(header_enc_tkt->flags, TKT_FLG_PRE_AUTH))
539         setflag(enc_tkt_reply.flags, TKT_FLG_PRE_AUTH);
540
541     if (isflagset(header_enc_tkt->flags, TKT_FLG_HW_AUTH))
542         setflag(enc_tkt_reply.flags, TKT_FLG_HW_AUTH);
543     
544     /* starttime is optional, and treated as authtime if not present.
545        so we can nuke it if it matches */
546     if (enc_tkt_reply.times.starttime == enc_tkt_reply.times.authtime)
547         enc_tkt_reply.times.starttime = 0;
548
549     if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION)) {
550         errcode = krb5_unparse_name(kdc_context, for_user->user, &s4u_name);
551     } else if (isflagset(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION)) {
552         errcode = krb5_unparse_name(kdc_context, header_enc_tkt->client, &s4u_name);
553     } else {
554         errcode = 0;
555     }
556     if (errcode) {
557         status = "UNPARSING S4U CLIENT";
558         goto cleanup;
559     }
560
561     if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) {
562         krb5_enc_tkt_part *t2enc = request->second_ticket[st_idx]->enc_part2;
563         encrypting_key = *(t2enc->session);
564     } else {
565         /*
566          * Find the server key
567          */
568         if ((errcode = krb5_dbe_find_enctype(kdc_context, &server,
569                              -1, /* ignore keytype */
570                                              -1, /* Ignore salttype */
571                                              0,/* Get highest kvno */
572                                              &server_key))) {
573             status = "FINDING_SERVER_KEY";
574             goto cleanup;
575         }
576
577         if ((errcode = krb5_dbe_find_mkey(kdc_context, master_keylist, &server,
578                                           &mkey_ptr))) {
579             krb5_keylist_node *tmp_mkey_list;
580             /* try refreshing master key list */
581             /* XXX it would nice if we had the mkvno here for optimization */
582             if (krb5_db_fetch_mkey_list(kdc_context, master_princ,
583                                         &master_keyblock, 0, &tmp_mkey_list) == 0) {
584                 krb5_dbe_free_key_list(kdc_context, master_keylist);
585                 master_keylist = tmp_mkey_list;
586                 if ((errcode = krb5_dbe_find_mkey(kdc_context, master_keylist,
587                                                   &server, &mkey_ptr))) {
588                     status = "FINDING_MASTER_KEY";
589                     goto cleanup;
590                 }
591             } else {
592                 status = "FINDING_MASTER_KEY";
593                 goto cleanup;
594             }
595         }
596
597         /* convert server.key into a real key (it may be encrypted
598          *        in the database) */
599         if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context,
600                                                    mkey_ptr, 
601                                                    server_key, &encrypting_key,
602                                                    NULL))) {
603             status = "DECRYPT_SERVER_KEY";
604             goto cleanup;
605         }
606     }
607
608     if (isflagset(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION)) {
609         /*
610          * Don't allow authorization data to be disabled if constrained
611          * delegation is requested. We don't want to deny the server
612          * the ability to validate that delegation was used.
613          */
614         clear(server.attributes, KRB5_KDB_NO_AUTH_DATA_REQUIRED);
615     }
616     if (isflagset(server.attributes, KRB5_KDB_NO_AUTH_DATA_REQUIRED) == 0) {
617         /*
618          * If we are not doing protocol transition/constrained delegation
619          * and there was no authorization data included, try to lookup
620          * the client principal as it may be mapped to a local account.
621          *
622          * Always validate authorization data for constrained delegation
623          * because we must validate the KDC signatures.
624          */
625         if (!isflagset(c_flags, KRB5_KDB_FLAGS_S4U) &&
626             header_enc_tkt->authorization_data == NULL) {
627
628             /* Generate authorization data so we can include it in ticket */
629             setflag(c_flags, KRB5_KDB_FLAG_INCLUDE_PAC);
630             /* Map principals from foreign (possibly non-AD) realms */
631             setflag(c_flags, KRB5_KDB_FLAG_MAP_PRINCIPALS);
632
633             assert(c_nprincs == 0); /* should not have been looked up already */
634
635             c_nprincs = 1;
636             errcode = krb5_db_get_principal_ext(kdc_context,
637                                                 header_enc_tkt->client,
638                                                 c_flags,
639                                                 &client,
640                                                 &c_nprincs,
641                                                 &more);
642             /*
643              * We can ignore errors because the principal may be a
644              * valid cross-realm principal for which we have no local
645              * mapping. But we do want to check that at most one entry
646              * was returned.
647              */
648             if (errcode == 0 && (more || c_nprincs > 1)) {
649                 errcode = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE;
650                 goto cleanup;
651             } else if (errcode) {
652                 c_nprincs = 0;
653             }
654         }
655     }
656
657     enc_tkt_reply.authorization_data = NULL;
658
659     if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION) &&
660         is_local_principal(header_enc_tkt->client))
661         enc_tkt_reply.client = for_user->user;
662     else
663         enc_tkt_reply.client = header_enc_tkt->client;
664
665     errcode = handle_authdata(kdc_context,
666                               c_flags,
667                               (c_nprincs != 0) ? &client : NULL,
668                               &server,
669                               (k_nprincs != 0) ? &krbtgt : NULL,
670                               subkey != NULL ? subkey :
671                               header_ticket->enc_part2->session,
672                               &encrypting_key, /* U2U or server key */
673                               pkt,
674                               request,
675                               for_user ? for_user->user : NULL,
676                               header_enc_tkt,
677                               &enc_tkt_reply);
678     if (errcode) {
679         krb5_klog_syslog(LOG_INFO, "TGS_REQ : handle_authdata (%d)", errcode);
680         status = "HANDLE_AUTHDATA";
681         goto cleanup;
682     }
683
684     if (is_referral && isflagset(s_flags, KRB5_KDB_FLAG_CANONICALIZE)) {
685         errcode = return_svr_referral_data(kdc_context,
686                                            &server, &reply_encpart);
687         if (errcode) {
688             status = "KDC_RETURN_ENC_PADATA";
689             goto cleanup;
690         }
691     }
692
693     enc_tkt_reply.session = &session_key;
694     enc_tkt_reply.transited.tr_type = KRB5_DOMAIN_X500_COMPRESS;
695     enc_tkt_reply.transited.tr_contents = empty_string; /* equivalent of "" */
696
697     /*
698      * Only add the realm of the presented tgt to the transited list if 
699      * it is different than the local realm (cross-realm) and it is different
700      * than the realm of the client (since the realm of the client is already
701      * implicitly part of the transited list and should not be explicitly
702      * listed).
703      */
704
705     /* realm compare is like strcmp, but knows how to deal with these args */
706     if (realm_compare(header_ticket->server, tgs_server) ||
707         realm_compare(header_ticket->server, enc_tkt_reply.client)) {
708         /* tgt issued by local realm or issued by realm of client */
709         enc_tkt_reply.transited = header_enc_tkt->transited;
710     } else {
711         /* tgt issued by some other realm and not the realm of the client */
712         /* assemble new transited field into allocated storage */
713         if (header_enc_tkt->transited.tr_type !=
714             KRB5_DOMAIN_X500_COMPRESS) {
715             status = "BAD_TRTYPE";
716             errcode = KRB5KDC_ERR_TRTYPE_NOSUPP;
717             goto cleanup;
718         }
719         enc_tkt_transited.tr_type = KRB5_DOMAIN_X500_COMPRESS;
720         enc_tkt_transited.magic = 0;
721         enc_tkt_transited.tr_contents.magic = 0;
722         enc_tkt_transited.tr_contents.data = 0;
723         enc_tkt_transited.tr_contents.length = 0;
724         enc_tkt_reply.transited = enc_tkt_transited;
725         if ((errcode =
726             add_to_transited(&header_enc_tkt->transited.tr_contents,
727                              &enc_tkt_reply.transited.tr_contents,
728                              header_ticket->server,
729                              enc_tkt_reply.client,
730                              request->server))) {
731                                  status = "ADD_TR_FAIL";
732                                  goto cleanup;
733         }
734         newtransited = 1;
735     }
736     if (isflagset(c_flags, KRB5_KDB_FLAG_CROSS_REALM)) {
737         errcode = validate_transit_path(kdc_context, header_enc_tkt->client,
738         &server,
739         (k_nprincs != 0) ? &krbtgt : NULL);
740         if (errcode) {
741             status = "NON_TRANSITIVE";
742             goto cleanup;
743         }
744     }
745     if (!isflagset (request->kdc_options, KDC_OPT_DISABLE_TRANSITED_CHECK)) {
746         unsigned int tlen;
747         char *tdots;
748
749         errcode = kdc_check_transited_list (kdc_context,
750                                             &enc_tkt_reply.transited.tr_contents,
751                                             krb5_princ_realm (kdc_context, header_enc_tkt->client),
752                                             krb5_princ_realm (kdc_context, request->server));
753         tlen = enc_tkt_reply.transited.tr_contents.length;
754         tdots = tlen > 125 ? "..." : "";
755         tlen = tlen > 125 ? 125 : tlen;
756
757         if (errcode == 0) {
758             setflag (enc_tkt_reply.flags, TKT_FLG_TRANSIT_POLICY_CHECKED);
759         } else if (errcode == KRB5KRB_AP_ERR_ILL_CR_TKT)
760             krb5_klog_syslog (LOG_INFO,
761                               "bad realm transit path from '%s' to '%s' "
762                               "via '%.*s%s'",
763                               cname ? cname : "<unknown client>",
764                               sname ? sname : "<unknown server>",
765                               tlen,
766                               enc_tkt_reply.transited.tr_contents.data,
767                               tdots);
768         else {
769             emsg = krb5_get_error_message(kdc_context, errcode);
770             krb5_klog_syslog (LOG_ERR,
771                               "unexpected error checking transit from "
772                               "'%s' to '%s' via '%.*s%s': %s",
773                               cname ? cname : "<unknown client>",
774                               sname ? sname : "<unknown server>",
775                               tlen,
776                               enc_tkt_reply.transited.tr_contents.data,
777                               tdots, emsg);
778             krb5_free_error_message(kdc_context, emsg);
779             emsg = NULL;
780         }
781     } else
782         krb5_klog_syslog (LOG_INFO, "not checking transit path");
783     if (reject_bad_transit
784         && !isflagset (enc_tkt_reply.flags, TKT_FLG_TRANSIT_POLICY_CHECKED)) {
785         errcode = KRB5KDC_ERR_POLICY;
786         status = "BAD_TRANSIT";
787         goto cleanup;
788     }
789
790     ticket_reply.enc_part2 = &enc_tkt_reply;
791
792     /*
793      * If we are doing user-to-user authentication, then make sure
794      * that the client for the second ticket matches the request
795      * server, and then encrypt the ticket using the session key of
796      * the second ticket.
797      */
798     if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) {
799         /*
800          * Make sure the client for the second ticket matches
801          * requested server.
802          */
803         krb5_enc_tkt_part *t2enc = request->second_ticket[st_idx]->enc_part2;
804         krb5_principal client2 = t2enc->client;
805         if (!krb5_principal_compare(kdc_context, request->server, client2)) {
806             if ((errcode = krb5_unparse_name(kdc_context, client2, &altcname)))
807                 altcname = 0;
808             if (altcname != NULL)
809                 limit_string(altcname);
810
811             errcode = KRB5KDC_ERR_SERVER_NOMATCH;
812             status = "2ND_TKT_MISMATCH";
813             goto cleanup;
814         }
815             
816         ticket_kvno = 0;
817         ticket_reply.enc_part.enctype = t2enc->session->enctype;
818         st_idx++;
819     } else {
820         ticket_kvno = server_key->key_data_kvno;
821     }
822
823     errcode = krb5_encrypt_tkt_part(kdc_context, &encrypting_key,
824                     &ticket_reply);
825     if (!isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY))
826         krb5_free_keyblock_contents(kdc_context, &encrypting_key);
827     if (errcode) {
828         status = "TKT_ENCRYPT";
829         goto cleanup;
830     }
831     ticket_reply.enc_part.kvno = ticket_kvno;
832     /* Start assembling the response */
833     reply.msg_type = KRB5_TGS_REP;
834     reply.padata = 0;/* always */
835     reply.client = enc_tkt_reply.client;
836     reply.enc_part.kvno = 0;/* We are using the session key */
837     reply.ticket = &ticket_reply;
838
839     reply_encpart.session = &session_key;
840     reply_encpart.nonce = request->nonce;
841
842     /* copy the time fields EXCEPT for authtime; its location
843        is used for ktime */
844     reply_encpart.times = enc_tkt_reply.times;
845     reply_encpart.times.authtime = header_enc_tkt->times.authtime;
846
847     /* starttime is optional, and treated as authtime if not present.
848        so we can nuke it if it matches */
849     if (enc_tkt_reply.times.starttime == enc_tkt_reply.times.authtime)
850         enc_tkt_reply.times.starttime = 0;
851
852     nolrentry.lr_type = KRB5_LRQ_NONE;
853     nolrentry.value = 0;
854     nolrarray[0] = &nolrentry;
855     nolrarray[1] = 0;
856     reply_encpart.last_req = nolrarray;        /* not available for TGS reqs */
857     reply_encpart.key_exp = 0;/* ditto */
858     reply_encpart.flags = enc_tkt_reply.flags;
859     reply_encpart.server = ticket_reply.server;
860     
861     /* use the session key in the ticket, unless there's a subsession key
862        in the AP_REQ */
863
864     reply.enc_part.enctype = subkey ? subkey->enctype :
865     header_ticket->enc_part2->session->enctype;
866     errcode = krb5_encode_kdc_rep(kdc_context, KRB5_TGS_REP, &reply_encpart, 
867                   subkey ? 1 : 0,
868                   subkey ? subkey :
869                   header_ticket->enc_part2->session,
870                   &reply, response);
871     if (errcode) {
872         status = "ENCODE_KDC_REP";
873     } else {
874         status = "ISSUE";
875     }
876
877     memset(ticket_reply.enc_part.ciphertext.data, 0,
878            ticket_reply.enc_part.ciphertext.length);
879     free(ticket_reply.enc_part.ciphertext.data);
880     /* these parts are left on as a courtesy from krb5_encode_kdc_rep so we
881        can use them in raw form if needed.  But, we don't... */
882     memset(reply.enc_part.ciphertext.data, 0,
883            reply.enc_part.ciphertext.length);
884     free(reply.enc_part.ciphertext.data);
885     
886 cleanup:
887     assert(status != NULL);
888     if (errcode) 
889         emsg = krb5_get_error_message (kdc_context, errcode);
890     log_tgs_req(from, request, &reply, cname, sname, altcname, authtime,
891                 c_flags, s4u_name, status, errcode, emsg);
892     if (errcode) {
893         krb5_free_error_message (kdc_context, emsg);
894         emsg = NULL;
895     }
896
897     if (errcode) {
898         int got_err = 0;
899         if (status == 0) {
900             status = krb5_get_error_message (kdc_context, errcode);
901             got_err = 1;
902         }
903         errcode -= ERROR_TABLE_BASE_krb5;
904         if (errcode < 0 || errcode > 128)
905             errcode = KRB_ERR_GENERIC;
906             
907         retval = prepare_error_tgs(request, header_ticket, errcode,
908         nprincs ? server.princ : NULL,
909                    response, status);
910         if (got_err) {
911             krb5_free_error_message (kdc_context, status);
912             status = 0;
913         }
914     }
915     
916     if (header_ticket != NULL)
917         krb5_free_ticket(kdc_context, header_ticket);
918     if (request != NULL)
919         krb5_free_kdc_req(kdc_context, request);
920     if (cname != NULL)
921         free(cname);
922     if (sname != NULL)
923         free(sname);
924     if (nprincs != 0)
925         krb5_db_free_principal(kdc_context, &server, 1);
926     if (session_key.contents != NULL)
927         krb5_free_keyblock_contents(kdc_context, &session_key);
928     if (newtransited)
929         free(enc_tkt_reply.transited.tr_contents.data);
930     if (k_nprincs)
931         krb5_db_free_principal(kdc_context, &krbtgt, k_nprincs);
932     if (c_nprincs)
933         krb5_db_free_principal(kdc_context, &client, c_nprincs);
934     if (for_user != NULL)
935         krb5_free_pa_for_user(kdc_context, for_user);
936     if (kdc_issued_auth_data != NULL)
937         krb5_free_authdata(kdc_context, kdc_issued_auth_data);
938     if (s4u_name != NULL)
939         free(s4u_name);
940     if (subkey != NULL)
941         krb5_free_keyblock(kdc_context, subkey);
942
943     return retval;
944 }
945
946 static krb5_error_code
947 prepare_error_tgs (krb5_kdc_req *request, krb5_ticket *ticket, int error,
948                    krb5_principal canon_server,
949                    krb5_data **response, const char *status)
950 {
951     krb5_error errpkt;
952     krb5_error_code retval;
953     krb5_data *scratch;
954
955     errpkt.ctime = request->nonce;
956     errpkt.cusec = 0;
957
958     if ((retval = krb5_us_timeofday(kdc_context, &errpkt.stime,
959                     &errpkt.susec)))
960         return(retval);
961     errpkt.error = error;
962     errpkt.server = request->server;
963     if (ticket && ticket->enc_part2)
964         errpkt.client = ticket->enc_part2->client;
965     else
966         errpkt.client = NULL;
967     errpkt.text.length = strlen(status) + 1;
968     if (!(errpkt.text.data = strdup(status)))
969         return ENOMEM;
970
971     if (!(scratch = (krb5_data *)malloc(sizeof(*scratch)))) {
972         free(errpkt.text.data);
973         return ENOMEM;
974     }
975     errpkt.e_data.length = 0;
976     errpkt.e_data.data = NULL;
977
978     retval = krb5_mk_error(kdc_context, &errpkt, scratch);
979     free(errpkt.text.data);
980     if (retval)
981         free(scratch);
982     else
983         *response = scratch;
984
985     return retval;
986 }
987
988 /*
989  * The request seems to be for a ticket-granting service somewhere else,
990  * but we don't have a ticket for the final TGS.  Try to give the requestor
991  * some intermediate realm.
992  */
993 static void
994 find_alternate_tgs(krb5_kdc_req *request, krb5_db_entry *server,
995                    krb5_boolean *more, int *nprincs)
996 {
997     krb5_error_code retval;
998     krb5_principal *plist, *pl2;
999     krb5_data tmp;
1000
1001     *nprincs = 0;
1002     *more = FALSE;
1003
1004     /*
1005      * Call to krb5_princ_component is normally not safe but is so
1006      * here only because find_alternate_tgs() is only called from
1007      * somewhere that has already checked the number of components in
1008      * the principal.
1009      */
1010     if ((retval = krb5_walk_realm_tree(kdc_context, 
1011       krb5_princ_realm(kdc_context, request->server),
1012       krb5_princ_component(kdc_context, request->server, 1),
1013                       &plist, KRB5_REALM_BRANCH_CHAR)))
1014         return;
1015
1016     /* move to the end */
1017     for (pl2 = plist; *pl2; pl2++);
1018
1019     /* the first entry in this array is for krbtgt/local@local, so we
1020        ignore it */
1021     while (--pl2 > plist) {
1022         *nprincs = 1;
1023         tmp = *krb5_princ_realm(kdc_context, *pl2);
1024         krb5_princ_set_realm(kdc_context, *pl2, 
1025              krb5_princ_realm(kdc_context, tgs_server));
1026         retval = get_principal(kdc_context, *pl2, server, nprincs, more);
1027         krb5_princ_set_realm(kdc_context, *pl2, &tmp);
1028         if (retval) {
1029             *nprincs = 0;
1030             *more = FALSE;
1031             krb5_free_realm_tree(kdc_context, plist);
1032             return;
1033         }
1034         if (*more) {
1035             krb5_db_free_principal(kdc_context, server, *nprincs);
1036             continue;
1037         } else if (*nprincs == 1) {
1038             /* Found it! */
1039             krb5_principal tmpprinc;
1040
1041             tmp = *krb5_princ_realm(kdc_context, *pl2);
1042             krb5_princ_set_realm(kdc_context, *pl2, 
1043                  krb5_princ_realm(kdc_context, tgs_server));
1044             if ((retval = krb5_copy_principal(kdc_context, *pl2, &tmpprinc))) {
1045                                               krb5_db_free_principal(kdc_context, server, *nprincs);
1046                                               krb5_princ_set_realm(kdc_context, *pl2, &tmp);
1047                                               continue;
1048             }
1049             krb5_princ_set_realm(kdc_context, *pl2, &tmp);
1050
1051             krb5_free_principal(kdc_context, request->server);
1052             request->server = tmpprinc;
1053             log_tgs_alt_tgt(request->server);
1054             krb5_free_realm_tree(kdc_context, plist);
1055             return;
1056         }
1057         krb5_db_free_principal(kdc_context, server, *nprincs);
1058         continue;
1059     }
1060
1061     *nprincs = 0;
1062     *more = FALSE;
1063     krb5_free_realm_tree(kdc_context, plist);
1064     return;
1065 }
1066
1067 static krb5_int32
1068 prep_reprocess_req(krb5_kdc_req *request, krb5_principal *krbtgt_princ) 
1069 {
1070     krb5_error_code retval = KRB5KRB_AP_ERR_BADMATCH;
1071     char **realms, **cpp, *temp_buf=NULL;
1072     krb5_data *comp1 = NULL, *comp2 = NULL; 
1073     char *comp1_str = NULL; 
1074
1075     /* By now we know that server principal name is unknown.
1076      * If CANONICALIZE flag is set in the request                                 
1077      * If req is not U2U authn. req                                               
1078      * the requested server princ. has exactly two components                     
1079      * either 
1080      *      the name type is NT-SRV-HST                                           
1081      *      or name type is NT-UNKNOWN and 
1082      *         the 1st component is listed in conf file under host_based_services 
1083      * the 1st component is not in a list in conf under "no_host_referral"        
1084      * the 2d component looks like fully-qualified domain name (FQDN)              
1085      * If all of these conditions are satisfied - try mapping the FQDN and 
1086      * re-process the request as if client had asked for cross-realm TGT.
1087      */
1088
1089     if (isflagset(request->kdc_options, KDC_OPT_CANONICALIZE) == TRUE &&   
1090         !isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY) && 
1091         krb5_princ_size(kdc_context, request->server) == 2) {             
1092
1093         comp1 = krb5_princ_component(kdc_context, request->server, 0);
1094         comp2 = krb5_princ_component(kdc_context, request->server, 1);
1095
1096         comp1_str = calloc(1,comp1->length+1);
1097         if (!comp1_str) {
1098             retval = ENOMEM; 
1099             goto cleanup; 
1100          }
1101         strlcpy(comp1_str,comp1->data,comp1->length+1);
1102
1103         if ((krb5_princ_type(kdc_context, request->server) == KRB5_NT_SRV_HST || 
1104             (krb5_princ_type(kdc_context, request->server) == KRB5_NT_UNKNOWN &&   
1105             kdc_active_realm->realm_host_based_services != NULL &&
1106             (krb5_match_config_pattern(kdc_active_realm->realm_host_based_services, comp1_str) == TRUE ||
1107              krb5_match_config_pattern(kdc_active_realm->realm_host_based_services, "*") == TRUE))) &&
1108             (kdc_active_realm->realm_no_host_referral == NULL || 
1109             (krb5_match_config_pattern(kdc_active_realm->realm_no_host_referral, "*") == FALSE &&
1110              krb5_match_config_pattern(kdc_active_realm->realm_no_host_referral, comp1_str) == FALSE))) { 
1111
1112             if (memchr(comp2->data, '.', comp2->length) == NULL)
1113                 goto cleanup;
1114             temp_buf = calloc(1, comp2->length+1);
1115             if (!temp_buf){
1116                 retval = ENOMEM; 
1117                 goto cleanup;
1118             }
1119             strlcpy(temp_buf, comp2->data,comp2->length+1);
1120             retval = krb5int_get_domain_realm_mapping(kdc_context, temp_buf, &realms);
1121             free(temp_buf);
1122             if (retval) {
1123                 /* no match found */
1124                 com_err("krb5_get_domain_realm_mapping", retval, 0);
1125                 goto cleanup;
1126             }
1127             if (realms == 0) {
1128                 printf(" (null)\n");
1129                 goto cleanup;
1130             }
1131             if (realms[0] == 0) {
1132                 printf(" (none)\n");
1133                 free(realms);
1134                 goto cleanup;
1135             }
1136             /* Modify request. 
1137              * Construct cross-realm tgt :  krbtgt/REMOTE_REALM@LOCAL_REALM 
1138              * and use it as a principal in this req. 
1139              */
1140             retval = krb5_build_principal(kdc_context, krbtgt_princ, 
1141                                           (*request->server).realm.length, 
1142                                           (*request->server).realm.data, 
1143                                           "krbtgt", realms[0], (char *)0);
1144                          
1145             for (cpp = realms; *cpp; cpp++)  
1146                    free(*cpp);
1147         }
1148     }
1149 cleanup:
1150     free(comp1_str);
1151     return retval;
1152 }
1153
1154