Fix inter-realm handling in KDC so that an intermediate realm is returned
[krb5.git] / src / kdc / do_tgs_req.c
1 /*
2  * $Source$
3  * $Author$
4  *
5  * Copyright 1990,1991 by the Massachusetts Institute of Technology.
6  * All Rights Reserved.
7  *
8  * Export of this software from the United States of America may
9  *   require a specific license from the United States Government.
10  *   It is the responsibility of any person or organization contemplating
11  *   export to obtain such a license before exporting.
12  * 
13  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
14  * distribute this software and its documentation for any purpose and
15  * without fee is hereby granted, provided that the above copyright
16  * notice appear in all copies and that both that copyright notice and
17  * this permission notice appear in supporting documentation, and that
18  * the name of M.I.T. not be used in advertising or publicity pertaining
19  * to distribution of the software without specific, written prior
20  * permission.  M.I.T. makes no representations about the suitability of
21  * this software for any purpose.  It is provided "as is" without express
22  * or implied warranty.
23  * 
24  *
25  * KDC Routines to deal with TGS_REQ's
26  */
27
28 #if !defined(lint) && !defined(SABER)
29 static char rcsid_do_tgs_req_c[] =
30 "$Id$";
31 #endif  /* !lint & !SABER */
32
33 #include <krb5/krb5.h>
34 #include <krb5/kdb.h>
35 #include <krb5/los-proto.h>
36 #include <krb5/asn1.h>
37 #include <krb5/osconf.h>
38 #include <krb5/ext-proto.h>
39 #include <com_err.h>
40
41 #include <syslog.h>
42 #ifdef KRB5_USE_INET
43 #include <sys/types.h>
44 #include <netinet/in.h>
45 #ifndef hpux
46 #include <arpa/inet.h>
47 #endif
48 #endif
49
50 #include "kdc_util.h"
51 #include "policy.h"
52 #include "extern.h"
53
54
55 static void find_alternate_tgs PROTOTYPE((krb5_kdc_req *,
56                                           krb5_db_entry *,
57                                           krb5_boolean *,
58                                           int *));
59
60 static krb5_error_code prepare_error_tgs PROTOTYPE((krb5_kdc_req *,
61                                                     krb5_ticket *,
62                                                     int,
63                                                     const char *,
64                                                     krb5_data **));
65
66 /*ARGSUSED*/
67 krb5_error_code
68 process_tgs_req(pkt, from, is_secondary, response)
69 krb5_data *pkt;
70 const krb5_fulladdr *from;              /* who sent it ? */
71 int     is_secondary;
72 krb5_data **response;                   /* filled in with a response packet */
73 {
74     krb5_kdc_req *request = 0;
75     krb5_db_entry server;
76     krb5_kdc_rep reply;
77     krb5_enc_kdc_rep_part reply_encpart;
78     krb5_ticket ticket_reply, *header_ticket = 0;
79     krb5_tkt_authent *req_authdat = 0;
80     int st_idx = 0;
81     krb5_enc_tkt_part enc_tkt_reply;
82     krb5_transited enc_tkt_transited;
83     int newtransited = 0;
84     krb5_error_code retval = 0;
85     int nprincs = 0;
86     krb5_boolean more;
87     krb5_timestamp kdc_time, authtime;
88     krb5_keyblock *session_key = 0;
89     krb5_timestamp until, rtime;
90     krb5_keyblock encrypting_key;
91     char *cname = 0, *sname = 0, *tmp = 0, *fromstring = 0;
92     krb5_last_req_entry *nolrarray[2], nolrentry;
93 /*    krb5_address *noaddrarray[1]; */
94     krb5_enctype useetype;
95     int errcode, errcode2;
96     register int i;
97     int firstpass = 1;
98     char        *status = 0;
99     char        secondary_ch;
100     
101     if (is_secondary)
102         secondary_ch = ';';
103     else
104         secondary_ch = ':';
105     
106     retval = decode_krb5_tgs_req(pkt, &request);
107     if (retval)
108         return retval;
109
110 #ifdef KRB5_USE_INET
111     if (from->address->addrtype == ADDRTYPE_INET)
112         fromstring =
113             (char *) inet_ntoa(*(struct in_addr *)from->address->contents);
114 #endif
115     if (!fromstring)
116         fromstring = "<unknown>";
117
118     if (errcode = krb5_unparse_name(request->server, &sname)) {
119         status = "UNPARSING SERVER";
120         goto cleanup;
121     }
122
123     errcode = kdc_process_tgs_req(request, from, pkt, &req_authdat);
124     if (req_authdat)
125         header_ticket = req_authdat->ticket;
126
127     if (header_ticket && header_ticket->enc_part2 &&
128         (errcode2 = krb5_unparse_name(header_ticket->enc_part2->client,
129                                       &cname))) {
130         status = "UNPARSING CLIENT";
131         errcode = errcode2;
132         goto cleanup;
133     }
134
135     if (errcode) {
136         status = "PROCESS_TGS";
137         goto cleanup;
138     }
139
140     if (!header_ticket) {
141         status="UNEXPECTED NULL in header_ticket";
142         goto cleanup;
143     }
144     
145     /*
146      * We've already dealt with the AP_REQ authentication, so we can
147      * use header_ticket freely.  The encrypted part (if any) has been
148      * decrypted with the session key.
149      */
150
151     authtime = header_ticket->enc_part2->times.authtime;
152
153     /* XXX make sure server here has the proper realm...taken from AP_REQ
154        header? */
155
156     nprincs = 1;
157     if (retval = krb5_db_get_principal(request->server, &server, &nprincs,
158                                        &more)) {
159         syslog(LOG_INFO,
160                "TGS_REQ: GET_PRINCIPAL: authtime %d, host %s, %s for %s (%s)",
161                authtime, fromstring, cname, sname, error_message(retval));
162         nprincs = 0;
163         goto cleanup;
164     }
165 tgt_again:
166     if (more) {
167         status = "NON_UNIQUE_PRINCIPAL";
168         errcode = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE;
169         goto cleanup;
170     } else if (nprincs != 1) {
171         /*
172          * might be a request for a TGT for some other realm; we
173          * should do our best to find such a TGS in this db
174          */
175         if (firstpass && krb5_princ_size(request->server) == 2) {
176             krb5_data *server_1 = krb5_princ_component(request->server, 1);
177             krb5_data *tgs_1 = krb5_princ_component(tgs_server, 1);
178
179             if (server_1->length != tgs_1->length ||
180                 memcmp(server_1->data, tgs_1->data, tgs_1->length)) {
181                 krb5_db_free_principal(&server, nprincs);
182                 find_alternate_tgs(request, &server, &more, &nprincs);
183                 firstpass = 0;
184                 goto tgt_again;
185             }
186         }
187         krb5_db_free_principal(&server, nprincs);
188         status = "UNKNOWN_SERVER";
189         errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
190         goto cleanup;
191     }
192
193     if (retval = krb5_timeofday(&kdc_time)) {
194         status = "TIME_OF_DAY";
195         goto cleanup;
196     }
197     
198     if (retval = validate_tgs_request(request, server, header_ticket,
199                                       kdc_time, &status)) {
200         if (!status)
201             status = "UNKNOWN_REASON";
202         errcode = retval + ERROR_TABLE_BASE_krb5;
203         goto cleanup;
204     }
205
206     for (i = 0; i < request->netypes; i++)
207         if (valid_etype(request->etype[i]))
208             break;
209     if (i == request->netypes) {
210         /* unsupported etype */
211         status = "BAD_ENCRYPTION_TYPE";
212         errcode = KRB5KDC_ERR_ETYPE_NOSUPP;
213         goto cleanup;
214     }
215     useetype = request->etype[i];
216
217     if (retval = (*(krb5_csarray[useetype]->system->random_key))(krb5_csarray[useetype]->random_sequence, &session_key)) {
218         /* random key failed */
219         status = "RANDOM_KEY_FAILED";
220         goto cleanup;
221     }
222
223     ticket_reply.server = request->server; /* XXX careful for realm... */
224     ticket_reply.enc_part.etype = useetype;
225     ticket_reply.enc_part.kvno = server.kvno;
226
227     enc_tkt_reply.flags = 0;
228     enc_tkt_reply.times.starttime = 0;
229
230     /*
231      * Fix header_ticket's starttime; if it's zero, fill in the
232      * authtime's value.
233      */
234     if (!(header_ticket->enc_part2->times.starttime))
235         header_ticket->enc_part2->times.starttime =
236             header_ticket->enc_part2->times.authtime;
237
238     /* don't use new addresses unless forwarded, see below */
239
240     enc_tkt_reply.caddrs = header_ticket->enc_part2->caddrs;
241     /* noaddrarray[0] = 0; */
242     reply_encpart.caddrs = 0;           /* optional...don't put it in */
243
244     /* It should be noted that local policy may affect the  */
245     /* processing of any of these flags.  For example, some */
246     /* realms may refuse to issue renewable tickets         */
247
248     if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE))
249         setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE);
250
251     if (isflagset(request->kdc_options, KDC_OPT_FORWARDED)) {
252         setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDED);
253
254         /* include new addresses in ticket & reply */
255
256         enc_tkt_reply.caddrs = request->addresses;
257         reply_encpart.caddrs = request->addresses;
258     }   
259     if (isflagset(header_ticket->enc_part2->flags, TKT_FLG_FORWARDED))
260         setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDED);
261
262     if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE))
263         setflag(enc_tkt_reply.flags, TKT_FLG_PROXIABLE);
264
265     if (isflagset(request->kdc_options, KDC_OPT_PROXY)) {
266         setflag(enc_tkt_reply.flags, TKT_FLG_PROXY);
267
268         /* include new addresses in ticket & reply */
269
270         enc_tkt_reply.caddrs = request->addresses;
271         reply_encpart.caddrs = request->addresses;
272     }
273
274     if (isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE))
275         setflag(enc_tkt_reply.flags, TKT_FLG_MAY_POSTDATE);
276
277     if (isflagset(request->kdc_options, KDC_OPT_POSTDATED)) {
278         setflag(enc_tkt_reply.flags, TKT_FLG_POSTDATED);
279         setflag(enc_tkt_reply.flags, TKT_FLG_INVALID);
280         enc_tkt_reply.times.starttime = request->from;
281     } else
282         enc_tkt_reply.times.starttime = kdc_time;
283
284     if (isflagset(request->kdc_options, KDC_OPT_VALIDATE)) {
285         /* BEWARE of allocation hanging off of ticket & enc_part2, it belongs
286            to the caller */
287         ticket_reply = *(header_ticket);
288         enc_tkt_reply = *(header_ticket->enc_part2);
289         clear(enc_tkt_reply.flags, TKT_FLG_INVALID);
290     }
291
292     if (isflagset(request->kdc_options, KDC_OPT_RENEW)) {
293         krb5_deltat old_life;
294
295         /* BEWARE of allocation hanging off of ticket & enc_part2, it belongs
296            to the caller */
297         ticket_reply = *(header_ticket);
298         enc_tkt_reply = *(header_ticket->enc_part2);
299
300         old_life = enc_tkt_reply.times.endtime - enc_tkt_reply.times.starttime;
301
302         enc_tkt_reply.times.starttime = kdc_time;
303         enc_tkt_reply.times.endtime =
304             min(header_ticket->enc_part2->times.renew_till,
305                 kdc_time + old_life);
306     } else {
307         /* not a renew request */
308         enc_tkt_reply.times.starttime = kdc_time;
309         until = (request->till == 0) ? kdc_infinity : request->till;
310         enc_tkt_reply.times.endtime =
311             min(until, min(enc_tkt_reply.times.starttime + server.max_life,
312                            min(enc_tkt_reply.times.starttime + max_life_for_realm,
313                                header_ticket->enc_part2->times.endtime)));
314         if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE_OK) &&
315             (enc_tkt_reply.times.endtime < request->till) &&
316             isflagset(header_ticket->enc_part2->flags,
317                   TKT_FLG_RENEWABLE)) {
318             setflag(request->kdc_options, KDC_OPT_RENEWABLE);
319             request->rtime =
320                 min(request->till,
321                     header_ticket->enc_part2->times.renew_till);
322         }
323     }
324     rtime = (request->rtime == 0) ? kdc_infinity : request->rtime;
325
326     if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE)) {
327         /* already checked above in policy check to reject request for a
328            renewable ticket using a non-renewable ticket */
329         setflag(enc_tkt_reply.flags, TKT_FLG_RENEWABLE);
330         enc_tkt_reply.times.renew_till =
331             min(rtime,
332                 min(header_ticket->enc_part2->times.renew_till,
333                     enc_tkt_reply.times.starttime +
334                     min(server.max_renewable_life,
335                         max_renewable_life_for_realm)));
336     } else {
337         enc_tkt_reply.times.renew_till = 0;
338     }
339     
340     /*
341      * Set authtime to be the same as header_ticket's
342      */
343     enc_tkt_reply.times.authtime = header_ticket->enc_part2->times.authtime;
344     
345     /*
346      * Propagate the preauthentication flags through to the returned ticket.
347      */
348     if (isflagset(header_ticket->enc_part2->flags, TKT_FLG_PRE_AUTH))
349         setflag(enc_tkt_reply.flags, TKT_FLG_PRE_AUTH);
350
351     if (isflagset(header_ticket->enc_part2->flags, TKT_FLG_HW_AUTH))
352         setflag(enc_tkt_reply.flags, TKT_FLG_HW_AUTH);
353     
354     /* starttime is optional, and treated as authtime if not present.
355        so we can nuke it if it matches */
356     if (enc_tkt_reply.times.starttime == enc_tkt_reply.times.authtime)
357         enc_tkt_reply.times.starttime = 0;
358
359     /* assemble any authorization data */
360     if (request->authorization_data.ciphertext.data) {
361         krb5_encrypt_block eblock;
362         krb5_data scratch;
363
364         /* decrypt the authdata in the request */
365         if (!valid_etype(request->authorization_data.etype)) {
366             status = "BAD_AUTH_ETYPE";
367             errcode = KRB5KDC_ERR_ETYPE_NOSUPP;
368             goto cleanup;
369         }
370         /* put together an eblock for this encryption */
371
372         krb5_use_cstype(&eblock, request->authorization_data.etype);
373
374         scratch.length = request->authorization_data.ciphertext.length;
375         if (!(scratch.data =
376               malloc(request->authorization_data.ciphertext.length))) {
377             status = "AUTH_NOMEM";
378             goto cleanup;
379         }
380         /* do any necessary key pre-processing */
381         if (retval = krb5_process_key(&eblock,
382                                       header_ticket->enc_part2->session)) {
383             status = "AUTH_PROCESS_KEY";
384             free(scratch.data);
385             goto cleanup;
386         }
387
388         /* call the encryption routine */
389         if (retval = krb5_decrypt((krb5_pointer) request->authorization_data.ciphertext.data,
390                                   (krb5_pointer) scratch.data,
391                                   scratch.length, &eblock, 0)) {
392             status = "AUTH_ENCRYPT_FAIL";
393             (void) krb5_finish_key(&eblock);
394             free(scratch.data);
395             goto cleanup;
396         }
397         if (retval = krb5_finish_key(&eblock)) {
398             status = "AUTH_FINISH_KEY";
399             free(scratch.data);
400             goto cleanup;
401         }
402         /* scratch now has the authorization data, so we decode it */
403         retval = decode_krb5_authdata(&scratch, request->unenc_authdata);
404         free(scratch.data);
405         if (retval) {
406             status = "AUTH_DECODE";
407             goto cleanup;
408         }
409
410         if (retval =
411             concat_authorization_data(request->unenc_authdata,
412                                       header_ticket->enc_part2->authorization_data, 
413                                       &enc_tkt_reply.authorization_data)) {
414             status = "CONCAT_AUTH";
415             goto cleanup;
416         }
417     } else
418         enc_tkt_reply.authorization_data =
419             header_ticket->enc_part2->authorization_data;
420
421     enc_tkt_reply.session = session_key;
422     enc_tkt_reply.client = header_ticket->enc_part2->client;
423     enc_tkt_reply.transited.tr_type = KRB5_DOMAIN_X500_COMPRESS;
424     enc_tkt_reply.transited.tr_contents = empty_string; /* equivalent of "" */
425
426     /* realm compare is like strcmp, but knows how to deal with these args */
427     if (realm_compare(realm_of_tgt(header_ticket),
428                        header_ticket->server)) {
429         /* tgt issued by local realm */
430         enc_tkt_reply.transited = header_ticket->enc_part2->transited;
431     } else {
432         /* assemble new transited field into allocated storage */
433         if (header_ticket->enc_part2->transited.tr_type !=
434             KRB5_DOMAIN_X500_COMPRESS) {
435             status = "BAD_TRTYPE";
436             errcode = KRB5KDC_ERR_TRTYPE_NOSUPP;
437             goto cleanup;
438         }
439         enc_tkt_transited.tr_type = KRB5_DOMAIN_X500_COMPRESS;
440         enc_tkt_transited.tr_contents.data = 0;
441         enc_tkt_transited.tr_contents.length = 0;
442         enc_tkt_reply.transited = enc_tkt_transited;
443         if (retval =
444             add_to_transited(&header_ticket->enc_part2->transited.tr_contents,
445                                &enc_tkt_reply.transited.tr_contents,
446                                header_ticket->server,
447                                enc_tkt_reply.client,
448                                request->server)) {
449             status = "ADD_TR_FAIL";
450             goto cleanup;
451         }
452         newtransited = 1;
453     }
454
455     ticket_reply.enc_part2 = &enc_tkt_reply;
456
457     if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) {
458         krb5_keyblock *st_sealing_key;
459         krb5_kvno st_srv_kvno;
460
461         if (retval = kdc_get_server_key(request->second_ticket[st_idx],
462                                         &st_sealing_key,
463                                         &st_srv_kvno)) {
464             status = "2ND_TKT_SERVER";
465             goto cleanup;
466         }
467
468         /* decrypt the ticket */
469         retval = krb5_decrypt_tkt_part(st_sealing_key,
470                                        request->second_ticket[st_idx]);
471         krb5_free_keyblock(st_sealing_key);
472         if (retval) {
473             status = "2ND_TKT_DECRYPT";
474             goto cleanup;
475         }
476
477         /*
478          * Make sure the client for the second ticket matches
479          * requested server.
480          */
481         if (!krb5_principal_compare(request->server,
482                                     request->second_ticket[st_idx]->enc_part2->client)) {
483                 if (retval = krb5_unparse_name(request->second_ticket[st_idx]->enc_part2->client, &tmp))
484                         tmp = 0;
485                 syslog(LOG_INFO, "TGS_REQ: 2ND_TKT_MISMATCH: authtime %d, host %s, %s for %s, 2nd tkt client %s",
486                        authtime, fromstring, cname, sname,
487                        tmp ? tmp : "<unknown>");
488                 goto cleanup;
489         }
490             
491         if (retval = krb5_encrypt_tkt_part(request->second_ticket[st_idx]->enc_part2->session,
492                                            &ticket_reply)) {
493             status = "2ND_TKT_ENCRYPT";
494             goto cleanup;
495         }
496         st_idx++;
497     } else {
498         /* convert server.key into a real key (it may be encrypted
499            in the database) */
500         if (retval = KDB_CONVERT_KEY_OUTOF_DB(&server.key, &encrypting_key)) {
501             status = "CONV_KEY";
502             goto cleanup;
503         }
504
505         retval = krb5_encrypt_tkt_part(&encrypting_key, &ticket_reply);
506
507         memset((char *)encrypting_key.contents, 0, encrypting_key.length);
508         krb5_xfree(encrypting_key.contents);
509
510         if (retval) {
511             status = "TKT_ENCRYPT";
512             goto cleanup;
513         }
514     }
515
516     /* Start assembling the response */
517     reply.msg_type = KRB5_TGS_REP;
518     reply.padata = 0;           /* always */
519     reply.client = header_ticket->enc_part2->client;
520     reply.enc_part.etype = useetype;
521     reply.enc_part.kvno = 0;            /* We are using the session key */
522     reply.ticket = &ticket_reply;
523
524     reply_encpart.session = session_key;
525     reply_encpart.nonce = request->nonce;
526
527     /* copy the time fields EXCEPT for authtime; its location
528        is used for ktime */
529     reply_encpart.times = enc_tkt_reply.times;
530     reply_encpart.times.authtime = header_ticket->enc_part2->times.authtime;
531
532     /* starttime is optional, and treated as authtime if not present.
533        so we can nuke it if it matches */
534     if (enc_tkt_reply.times.starttime == enc_tkt_reply.times.authtime)
535         enc_tkt_reply.times.starttime = 0;
536
537     nolrentry.lr_type = KRB5_LRQ_NONE;
538     nolrentry.value = 0;
539     nolrarray[0] = &nolrentry;
540     nolrarray[1] = 0;
541     reply_encpart.last_req = nolrarray; /* not available for TGS reqs */
542     reply_encpart.key_exp = 0;          /* ditto */
543     reply_encpart.flags = enc_tkt_reply.flags;
544     reply_encpart.server = ticket_reply.server;
545     
546     /* use the session key in the ticket, unless there's a subsession key
547        in the AP_REQ */
548
549     retval = krb5_encode_kdc_rep(KRB5_TGS_REP, &reply_encpart,
550                                  req_authdat->authenticator->subkey ?
551                                  req_authdat->authenticator->subkey :
552                                  header_ticket->enc_part2->session,
553                                  &reply, response);
554     if (retval) {
555         status = "ENCODE_KDC_REP";
556     } else {
557         status = "ISSUE";
558     }
559
560     memset(ticket_reply.enc_part.ciphertext.data, 0,
561            ticket_reply.enc_part.ciphertext.length);
562     free(ticket_reply.enc_part.ciphertext.data);
563     /* these parts are left on as a courtesy from krb5_encode_kdc_rep so we
564        can use them in raw form if needed.  But, we don't... */
565     memset(reply.enc_part.ciphertext.data, 0,
566            reply.enc_part.ciphertext.length);
567     free(reply.enc_part.ciphertext.data);
568     
569 cleanup:
570     if (status)
571         syslog(LOG_INFO, "TGS_REQ%c %s: authtime %d, host %s, %s for %s%s%s",
572                secondary_ch, status, authtime, fromstring,
573                cname ? cname : "<unknown client>",
574                sname ? sname : "<unknown server>",
575                errcode ? ", " : "",
576                errcode ? error_message(errcode) : "");
577     if (errcode) {
578         errcode -= ERROR_TABLE_BASE_krb5;
579         if (errcode < 0 || errcode > 128)
580             errcode = KRB_ERR_GENERIC;
581             
582         retval = prepare_error_tgs(request, header_ticket, errcode,
583                                    fromstring, response);
584     }
585     
586     if (request)
587         krb5_free_kdc_req(request);
588     if (req_authdat)
589         krb5_free_tkt_authent(req_authdat);
590     if (cname)
591         free(cname);
592     if (sname)
593         free(sname);
594     if (nprincs)
595         krb5_db_free_principal(&server, 1);
596     if (session_key)
597         krb5_free_keyblock(session_key);
598     if (newtransited)
599         free(enc_tkt_reply.transited.tr_contents.data); 
600
601     return retval;
602 }
603
604 static krb5_error_code
605 prepare_error_tgs (request, ticket, error, ident, response)
606 register krb5_kdc_req *request;
607 krb5_ticket *ticket;
608 int error;
609 const char *ident;
610 krb5_data **response;
611 {
612     krb5_error errpkt;
613     krb5_error_code retval;
614     krb5_data *scratch;
615
616     errpkt.ctime = request->nonce;
617     errpkt.cusec = 0;
618
619     if (retval = krb5_us_timeofday(&errpkt.stime, &errpkt.susec)) {
620         if (ticket)
621             krb5_free_ticket(ticket);
622         return(retval);
623     }
624     errpkt.error = error;
625     errpkt.server = request->server;
626     if (ticket && ticket->enc_part2)
627         errpkt.client = ticket->enc_part2->client;
628     else
629         errpkt.client = 0;
630     errpkt.text.length = strlen(error_message(error+KRB5KDC_ERR_NONE))+1;
631     if (!(errpkt.text.data = malloc(errpkt.text.length))) {
632         if (ticket)
633             krb5_free_ticket(ticket);
634         return ENOMEM;
635     }
636     (void) strcpy(errpkt.text.data, error_message(error+KRB5KDC_ERR_NONE));
637
638     if (!(scratch = (krb5_data *)malloc(sizeof(*scratch)))) {
639         free(errpkt.text.data);
640         if (ticket)
641             krb5_free_ticket(ticket);
642         return ENOMEM;
643     }
644     errpkt.e_data.length = 0;
645     errpkt.e_data.data = 0;
646
647     retval = krb5_mk_error(&errpkt, scratch);
648     free(errpkt.text.data);
649     *response = scratch;
650     if (ticket)
651         krb5_free_ticket(ticket);
652     return retval;
653 }
654
655 /*
656  * The request seems to be for a ticket-granting service somewhere else,
657  * but we don't have a ticket for the final TGS.  Try to give the requestor
658  * some intermediate realm.
659  */
660 static void
661 find_alternate_tgs(request, server, more, nprincs)
662 krb5_kdc_req *request;
663 krb5_db_entry *server;
664 krb5_boolean *more;
665 int *nprincs;
666 {
667     krb5_error_code retval;
668     krb5_principal *plist, *pl2;
669     krb5_data *tmp;
670
671     *nprincs = 0;
672     *more = FALSE;
673
674     if (retval = krb5_walk_realm_tree(krb5_princ_realm(request->server),
675                                       krb5_princ_component(request->server, 1),
676                                       &plist, KRB5_REALM_BRANCH_CHAR))
677         return;
678
679     /* move to the end */
680     for (pl2 = plist; *pl2; pl2++);
681
682     /* the first entry in this array is for krbtgt/local@local, so we
683        ignore it */
684     while (--pl2 > plist) {
685         *nprincs = 1;
686         tmp = krb5_princ_realm(*pl2);
687         krb5_princ_set_realm(*pl2, krb5_princ_realm(tgs_server));
688         retval = krb5_db_get_principal(*pl2, server, nprincs, more);
689         krb5_princ_set_realm(*pl2, tmp);
690         if (retval) {
691             *nprincs = 0;
692             *more = FALSE;
693             krb5_free_realm_tree(plist);
694             return;
695         }
696         if (*more) {
697             krb5_db_free_principal(server, *nprincs);
698             continue;
699         } else if (*nprincs == 1) {
700             /* Found it! */
701             krb5_principal tmpprinc;
702             char *sname;
703
704             tmp = krb5_princ_realm(*pl2);
705             krb5_princ_set_realm(*pl2, krb5_princ_realm(tgs_server));
706             if (retval = krb5_copy_principal(*pl2, &tmpprinc)) {
707                 krb5_db_free_principal(server, *nprincs);
708                 krb5_princ_set_realm(*pl2, tmp);
709                 continue;
710             }
711             krb5_princ_set_realm(*pl2, tmp);
712
713             krb5_free_principal(request->server);
714             request->server = tmpprinc;
715             if (krb5_unparse_name(request->server, &sname)) {
716                 syslog(LOG_INFO,
717                        "TGS_REQ: issuing alternate <un-unparseable> TGT");
718             } else {
719                 syslog(LOG_INFO,
720                        "TGS_REQ: issuing TGT %s", sname);
721                 free(sname);
722             }
723             return;
724         }
725         krb5_db_free_principal(server, *nprincs);
726         continue;
727     }
728
729     *nprincs = 0;
730     *more = FALSE;
731     krb5_free_realm_tree(plist);
732     return;
733 }
734