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