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