1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* kdc/do_tgs_req.c - KDC Routines to deal with TGS_REQ's */
3 /*
4  * Copyright 1990, 1991, 2001, 2007, 2008, 2009, 2013, 2014 by the
5  * Massachusetts Institute of Technology.  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  * Copyright (c) 2006-2008, Novell, Inc.
28  * All rights reserved.
29  *
30  * Redistribution and use in source and binary forms, with or without
31  * modification, are permitted provided that the following conditions are met:
32  *
33  *   * Redistributions of source code must retain the above copyright notice,
34  *       this list of conditions and the following disclaimer.
35  *   * Redistributions in binary form must reproduce the above copyright
36  *       notice, this list of conditions and the following disclaimer in the
37  *       documentation and/or other materials provided with the distribution.
38  *   * The copyright holder's name is not used to endorse or promote products
39  *       derived from this software without specific prior written permission.
40  *
41  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
42  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
45  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
46  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
47  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
48  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
49  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
50  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
51  * POSSIBILITY OF SUCH DAMAGE.
52  */
53 
54 #include "k5-int.h"
55 
56 #include <syslog.h>
57 #ifdef HAVE_NETINET_IN_H
58 #include <sys/types.h>
59 #include <netinet/in.h>
60 #ifndef hpux
61 #include <arpa/inet.h>
62 #endif
63 #endif
64 
65 #include "kdc_util.h"
66 #include "kdc_audit.h"
67 #include "policy.h"
68 #include "extern.h"
69 #include "adm_proto.h"
70 #include <ctype.h>
71 
72 static krb5_error_code
73 find_alternate_tgs(kdc_realm_t *, krb5_principal, krb5_db_entry **,
74                    const char**);
75 
76 static krb5_error_code
77 prepare_error_tgs(struct kdc_request_state *, krb5_kdc_req *,krb5_ticket *,int,
78                   krb5_principal,krb5_data **,const char *, krb5_pa_data **);
79 
80 static krb5_error_code
81 decrypt_2ndtkt(kdc_realm_t *, krb5_kdc_req *, krb5_flags, const krb5_ticket **,
82                krb5_db_entry **, krb5_keyblock **, const char **);
83 
84 static krb5_error_code
85 gen_session_key(kdc_realm_t *, krb5_kdc_req *, krb5_db_entry *,
86                 krb5_keyblock *, const char **);
87 
88 static krb5_int32
89 find_referral_tgs(kdc_realm_t *, krb5_kdc_req *, krb5_principal *);
90 
91 static krb5_error_code
92 db_get_svc_princ(krb5_context, krb5_principal, krb5_flags,
93                  krb5_db_entry **, const char **);
94 
95 static krb5_error_code
96 search_sprinc(kdc_realm_t *, krb5_kdc_req *, krb5_flags,
97               krb5_db_entry **, const char **);
98 
99 /*ARGSUSED*/
100 krb5_error_code
process_tgs_req(krb5_kdc_req * request,krb5_data * pkt,const krb5_fulladdr * from,kdc_realm_t * kdc_active_realm,krb5_data ** response)101 process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
102                 const krb5_fulladdr *from, kdc_realm_t *kdc_active_realm,
103                 krb5_data **response)
104 {
105     krb5_keyblock * subkey = 0;
106     krb5_keyblock *header_key = NULL;
107     krb5_keyblock *stkt_server_key = NULL;
108     krb5_keyblock *subject_key;
109     krb5_db_entry *server = NULL;
110     krb5_db_entry *stkt_server = NULL;
111     krb5_db_entry *subject_server;
112     krb5_kdc_rep reply;
113     krb5_enc_kdc_rep_part reply_encpart;
114     krb5_ticket ticket_reply, *header_ticket = 0;
115     const krb5_ticket *stkt = NULL;
116     krb5_enc_tkt_part enc_tkt_reply;
117     int newtransited = 0;
118     krb5_error_code retval = 0;
119     krb5_keyblock server_keyblock, *encrypting_key;
120     krb5_timestamp kdc_time, authtime = 0;
121     krb5_keyblock session_key, local_tgt_key;
122     krb5_keyblock *reply_key = NULL;
123     krb5_principal cprinc = NULL, sprinc = NULL, altcprinc = NULL;
124     krb5_const_principal authdata_client;
125     krb5_principal stkt_authdata_client = NULL;
126     krb5_last_req_entry *nolrarray[2], nolrentry;
127     int errcode;
128     const char        *status = 0;
129     krb5_enc_tkt_part *header_enc_tkt = NULL; /* TGT */
130     krb5_enc_tkt_part *subject_tkt = NULL; /* TGT or evidence ticket */
131     krb5_db_entry *client = NULL, *header_server = NULL;
132     krb5_db_entry *local_tgt, *local_tgt_storage = NULL;
133     krb5_pa_s4u_x509_user *s4u_x509_user = NULL; /* protocol transition request */
134     krb5_authdata **kdc_issued_auth_data = NULL; /* auth data issued by KDC */
135     unsigned int c_flags = 0, s_flags = 0;       /* client/server KDB flags */
136     krb5_boolean is_referral, is_crossrealm;
137     const char *emsg = NULL;
138     krb5_kvno ticket_kvno = 0;
139     struct kdc_request_state *state = NULL;
140     krb5_pa_data *pa_tgs_req; /*points into request*/
141     krb5_data scratch;
142     krb5_pa_data **e_data = NULL;
143     krb5_audit_state *au_state = NULL;
144     krb5_data **auth_indicators = NULL;
145     void *ad_info = NULL, *stkt_ad_info = NULL;
146 
147     memset(&reply, 0, sizeof(reply));
148     memset(&reply_encpart, 0, sizeof(reply_encpart));
149     memset(&ticket_reply, 0, sizeof(ticket_reply));
150     memset(&enc_tkt_reply, 0, sizeof(enc_tkt_reply));
151     memset(&server_keyblock, 0, sizeof(server_keyblock));
152     memset(&local_tgt_key, 0, sizeof(local_tgt_key));
153     session_key.contents = NULL;
154 
155     /* Save pointer to client-requested service principal, in case of
156      * errors before a successful call to search_sprinc(). */
157     sprinc = request->server;
158 
159     if (request->msg_type != KRB5_TGS_REQ) {
160         krb5_free_kdc_req(kdc_context, request);
161         return KRB5_BADMSGTYPE;
162     }
163 
164     errcode = kdc_make_rstate(kdc_active_realm, &state);
165     if (errcode !=0) {
166         krb5_free_kdc_req(kdc_context, request);
167         return errcode;
168     }
169 
170     /* Initialize audit state. */
171     errcode = kau_init_kdc_req(kdc_context, request, from, &au_state);
172     if (errcode) {
173         krb5_free_kdc_req(kdc_context, request);
174         return errcode;
175     }
176     /* Seed the audit trail with the request ID and basic information. */
177     kau_tgs_req(kdc_context, TRUE, au_state);
178 
179     errcode = kdc_process_tgs_req(kdc_active_realm,
180                                   request, from, pkt, &header_ticket,
181                                   &header_server, &header_key, &subkey,
182                                   &pa_tgs_req);
183     if (header_ticket && header_ticket->enc_part2)
184         cprinc = header_ticket->enc_part2->client;
185 
186     if (errcode) {
187         status = "PROCESS_TGS";
188         goto cleanup;
189     }
190 
191     if (!header_ticket) {
192         errcode = KRB5_NO_TKT_SUPPLIED;        /* XXX? */
193         goto cleanup;
194     }
195     errcode = kau_make_tkt_id(kdc_context, header_ticket,
196                               &au_state->tkt_in_id);
197     if (errcode)
198         goto cleanup;
199 
200     scratch.length = pa_tgs_req->length;
201     scratch.data = (char *) pa_tgs_req->contents;
202     errcode = kdc_find_fast(&request, &scratch, subkey,
203                             header_ticket->enc_part2->session, state, NULL);
204     /* Reset sprinc because kdc_find_fast() can replace request. */
205     sprinc = request->server;
206     if (errcode !=0) {
207         status = "FIND_FAST";
208         goto cleanup;
209     }
210 
211     errcode = get_local_tgt(kdc_context, &sprinc->realm, header_server,
212                             &local_tgt, &local_tgt_storage, &local_tgt_key);
213     if (errcode) {
214         status = "GET_LOCAL_TGT";
215         goto cleanup;
216     }
217 
218     /* Ignore (for now) the request modification due to FAST processing. */
219     au_state->request = request;
220 
221     /*
222      * Pointer to the encrypted part of the header ticket, which may be
223      * replaced to point to the encrypted part of the evidence ticket
224      * if constrained delegation is used. This simplifies the number of
225      * special cases for constrained delegation.
226      */
227     header_enc_tkt = header_ticket->enc_part2;
228 
229     /*
230      * We've already dealt with the AP_REQ authentication, so we can
231      * use header_ticket freely.  The encrypted part (if any) has been
232      * decrypted with the session key.
233      */
234 
235     au_state->stage = SRVC_PRINC;
236 
237     /* XXX make sure server here has the proper realm...taken from AP_REQ
238        header? */
239 
240     if (isflagset(request->kdc_options, KDC_OPT_CANONICALIZE)) {
241         setflag(c_flags, KRB5_KDB_FLAG_CANONICALIZE);
242         setflag(s_flags, KRB5_KDB_FLAG_CANONICALIZE);
243     }
244 
245     errcode = search_sprinc(kdc_active_realm, request, s_flags, &server,
246                             &status);
247     if (errcode != 0)
248         goto cleanup;
249     sprinc = server->princ;
250 
251     /* If we got a cross-realm TGS which is not the requested server, we are
252      * issuing a referral (or alternate TGT, which we treat similarly). */
253     is_referral = is_cross_tgs_principal(server->princ) &&
254         !krb5_principal_compare(kdc_context, request->server, server->princ);
255 
256     au_state->stage = VALIDATE_POL;
257 
258     if ((errcode = krb5_timeofday(kdc_context, &kdc_time)))
259         goto cleanup;
260 
261     is_crossrealm = !data_eq(header_server->princ->realm, sprinc->realm);
262     if (is_crossrealm)
263         setflag(c_flags, KRB5_KDB_FLAG_CROSS_REALM);
264     if (is_referral)
265         setflag(c_flags, KRB5_KDB_FLAG_ISSUING_REFERRAL);
266 
267     /* Check for protocol transition */
268     errcode = kdc_process_s4u2self_req(kdc_active_realm, request, server,
269                                        subkey, header_enc_tkt->session,
270                                        &s4u_x509_user, &client, &status);
271     if (s4u_x509_user != NULL || errcode != 0) {
272         if (s4u_x509_user != NULL)
273             au_state->s4u2self_user = s4u_x509_user->user_id.user;
274         au_state->status = status;
275         kau_s4u2self(kdc_context, errcode ? FALSE : TRUE, au_state);
276         au_state->s4u2self_user = NULL;
277     }
278 
279     /* For user-to-user and S4U2Proxy requests, decrypt the second ticket. */
280     errcode = decrypt_2ndtkt(kdc_active_realm, request, c_flags,
281                              &stkt, &stkt_server, &stkt_server_key, &status);
282     if (errcode)
283         goto cleanup;
284 
285     retval = validate_tgs_request(kdc_active_realm, request, server,
286                                   header_ticket, stkt, stkt_server, kdc_time,
287                                   s4u_x509_user, client, is_crossrealm,
288                                   is_referral, &status, &e_data);
289     if (retval) {
290         if (retval == KDC_ERR_POLICY || retval == KDC_ERR_BADOPTION)
291             au_state->violation = PROT_CONSTRAINT;
292         errcode = retval + ERROR_TABLE_BASE_krb5;
293         goto cleanup;
294     }
295 
296     if (errcode)
297         goto cleanup;
298 
299     if (s4u_x509_user != NULL && client == NULL) {
300         /*
301          * For an S4U2Self referral request (the requesting service is
302          * following a referral back to its own realm), the authdata in the
303          * header ticket should be for the requested client.
304          */
305         setflag(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION);
306         authdata_client = s4u_x509_user->user_id.user;
307     } else {
308         /* Otherwise (including for initial S4U2Self requests), the authdata
309          * should be for the header ticket client. */
310         authdata_client = header_enc_tkt->client;
311     }
312     errcode = krb5_db_get_authdata_info(kdc_context, c_flags,
313                                         header_enc_tkt->authorization_data,
314                                         authdata_client, request->server,
315                                         header_key, &local_tgt_key, local_tgt,
316                                         header_enc_tkt->times.authtime,
317                                         &ad_info, NULL);
318     if (errcode && errcode != KRB5_PLUGIN_OP_NOTSUPP)
319         goto cleanup;
320 
321     /* Flag all S4U2Self requests now that we have checked the authdata. */
322     if (s4u_x509_user != NULL)
323         setflag(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION);
324 
325     if (isflagset(request->kdc_options, KDC_OPT_CNAME_IN_ADDL_TKT)) {
326         /* Do constrained delegation protocol and authorization checks. */
327         setflag(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION);
328 
329         errcode = kdc_process_s4u2proxy_req(kdc_active_realm, c_flags, request,
330                                             stkt->enc_part2, local_tgt,
331                                             &local_tgt_key, stkt_server,
332                                             stkt_server_key,
333                                             header_ticket->enc_part2->client,
334                                             server, request->server, ad_info,
335                                             &stkt_ad_info,
336                                             &stkt_authdata_client,
337                                             &status);
338         if (errcode == KDC_ERR_POLICY || errcode == KDC_ERR_BADOPTION)
339             au_state->violation = PROT_CONSTRAINT;
340         else if (errcode)
341             au_state->violation = LOCAL_POLICY;
342         au_state->status = status;
343         retval = kau_make_tkt_id(kdc_context, stkt, &au_state->evid_tkt_id);
344         if (retval) {
345             errcode = retval;
346             goto cleanup;
347         }
348         kau_s4u2proxy(kdc_context, errcode ? FALSE : TRUE, au_state);
349         if (errcode)
350             goto cleanup;
351 
352         assert(krb5_is_tgs_principal(header_ticket->server));
353 
354         /* Use the parsed authdata from the second ticket during authdata
355          * handling. */
356         krb5_db_free_authdata_info(kdc_context, ad_info);
357         ad_info = stkt_ad_info;
358         stkt_ad_info = NULL;
359     }
360 
361     au_state->stage = ISSUE_TKT;
362 
363     errcode = gen_session_key(kdc_active_realm, request, server, &session_key,
364                               &status);
365     if (errcode)
366         goto cleanup;
367 
368     /*
369      * subject_tkt will refer to the evidence ticket (for constrained
370      * delegation) or the TGT. The distinction from header_enc_tkt is
371      * necessary because the TGS signature only protects some fields:
372      * the others could be forged by a malicious server.
373      */
374 
375     if (isflagset(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION)) {
376         subject_tkt = stkt->enc_part2;
377         subject_server = stkt_server;
378         subject_key = stkt_server_key;
379     } else {
380         subject_tkt = header_enc_tkt;
381         subject_server = header_server;
382         subject_key = header_key;
383     }
384     authtime = subject_tkt->times.authtime;
385 
386     /* Extract and check auth indicators from the subject ticket, except for
387      * S4U2Self requests (where the client didn't authenticate). */
388     if (s4u_x509_user == NULL) {
389         errcode = get_auth_indicators(kdc_context, subject_tkt, local_tgt,
390                                       &local_tgt_key, &auth_indicators);
391         if (errcode) {
392             status = "GET_AUTH_INDICATORS";
393             goto cleanup;
394         }
395 
396         errcode = check_indicators(kdc_context, server, auth_indicators);
397         if (errcode) {
398             status = "HIGHER_AUTHENTICATION_REQUIRED";
399             goto cleanup;
400         }
401     }
402 
403     if (is_referral)
404         ticket_reply.server = server->princ;
405     else
406         ticket_reply.server = request->server; /* XXX careful for realm... */
407 
408     enc_tkt_reply.flags = get_ticket_flags(request->kdc_options, client,
409                                            server, header_enc_tkt);
410     enc_tkt_reply.times.starttime = 0;
411 
412     /* OK_TO_AUTH_AS_DELEGATE must be set on the service requesting S4U2Self
413      * for forwardable tickets to be issued. */
414     if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION) &&
415         !is_referral &&
416         !isflagset(server->attributes, KRB5_KDB_OK_TO_AUTH_AS_DELEGATE))
417         clear(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE);
418 
419     /* don't use new addresses unless forwarded, see below */
420 
421     enc_tkt_reply.caddrs = header_enc_tkt->caddrs;
422     /* noaddrarray[0] = 0; */
423     reply_encpart.caddrs = 0;/* optional...don't put it in */
424     reply_encpart.enc_padata = NULL;
425 
426     /*
427      * It should be noted that local policy may affect the
428      * processing of any of these flags.  For example, some
429      * realms may refuse to issue renewable tickets
430      */
431 
432     if (isflagset(request->kdc_options, KDC_OPT_FORWARDED) ||
433         isflagset(request->kdc_options, KDC_OPT_PROXY)) {
434 
435         /* include new addresses in ticket & reply */
436 
437         enc_tkt_reply.caddrs = request->addresses;
438         reply_encpart.caddrs = request->addresses;
439     }
440 
441     if (isflagset(request->kdc_options, KDC_OPT_POSTDATED))
442         enc_tkt_reply.times.starttime = request->from;
443     else
444         enc_tkt_reply.times.starttime = kdc_time;
445 
446     if (isflagset(request->kdc_options, KDC_OPT_VALIDATE)) {
447         assert(isflagset(c_flags, KRB5_KDB_FLAGS_S4U) == 0);
448         /* BEWARE of allocation hanging off of ticket & enc_part2, it belongs
449            to the caller */
450         ticket_reply = *(header_ticket);
451         enc_tkt_reply = *(header_ticket->enc_part2);
452         enc_tkt_reply.authorization_data = NULL;
453         clear(enc_tkt_reply.flags, TKT_FLG_INVALID);
454     }
455 
456     if (isflagset(request->kdc_options, KDC_OPT_RENEW)) {
457         krb5_timestamp old_starttime;
458         krb5_deltat old_life;
459 
460         assert(isflagset(c_flags, KRB5_KDB_FLAGS_S4U) == 0);
461         /* BEWARE of allocation hanging off of ticket & enc_part2, it belongs
462            to the caller */
463         ticket_reply = *(header_ticket);
464         enc_tkt_reply = *(header_ticket->enc_part2);
465         enc_tkt_reply.authorization_data = NULL;
466 
467         old_starttime = enc_tkt_reply.times.starttime ?
468             enc_tkt_reply.times.starttime : enc_tkt_reply.times.authtime;
469         old_life = ts_delta(enc_tkt_reply.times.endtime, old_starttime);
470 
471         enc_tkt_reply.times.starttime = kdc_time;
472         enc_tkt_reply.times.endtime =
473             ts_min(header_ticket->enc_part2->times.renew_till,
474                    ts_incr(kdc_time, old_life));
475     } else {
476         /* not a renew request */
477         enc_tkt_reply.times.starttime = kdc_time;
478 
479         kdc_get_ticket_endtime(kdc_active_realm, enc_tkt_reply.times.starttime,
480                                header_enc_tkt->times.endtime, request->till,
481                                client, server, &enc_tkt_reply.times.endtime);
482     }
483 
484     kdc_get_ticket_renewtime(kdc_active_realm, request, header_enc_tkt, client,
485                              server, &enc_tkt_reply);
486 
487     errcode = check_kdcpolicy_tgs(kdc_context, request, server, header_ticket,
488                                   auth_indicators, kdc_time,
489                                   &enc_tkt_reply.times, &status);
490     if (errcode)
491         goto cleanup;
492 
493     /*
494      * Set authtime to be the same as header or evidence ticket's
495      */
496     enc_tkt_reply.times.authtime = authtime;
497 
498     /* starttime is optional, and treated as authtime if not present.
499        so we can nuke it if it matches */
500     if (enc_tkt_reply.times.starttime == enc_tkt_reply.times.authtime)
501         enc_tkt_reply.times.starttime = 0;
502 
503     if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION)) {
504         altcprinc = s4u_x509_user->user_id.user;
505     } else if (isflagset(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION)) {
506         /* kdc_process_s4u2proxy_req() only allows cross-realm requests if
507          * stkt_authdata_client is set. */
508         altcprinc = is_crossrealm ? stkt_authdata_client : subject_tkt->client;
509     } else {
510         altcprinc = NULL;
511     }
512     if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) {
513         encrypting_key = stkt->enc_part2->session;
514     } else {
515         errcode = get_first_current_key(kdc_context, server, &server_keyblock);
516         if (errcode) {
517             status = "FINDING_SERVER_KEY";
518             goto cleanup;
519         }
520         encrypting_key = &server_keyblock;
521     }
522 
523     if (isflagset(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION)) {
524         /*
525          * Don't allow authorization data to be disabled if constrained
526          * delegation is requested. We don't want to deny the server
527          * the ability to validate that delegation was used.
528          */
529         clear(server->attributes, KRB5_KDB_NO_AUTH_DATA_REQUIRED);
530     }
531     if (isflagset(server->attributes, KRB5_KDB_NO_AUTH_DATA_REQUIRED) == 0) {
532         /* If we are not doing protocol transition, try to look up the subject
533          * principal so that KDB modules can add additional authdata. */
534         if (!isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION)) {
535             /* Generate authorization data so we can include it in ticket */
536             setflag(c_flags, KRB5_KDB_FLAG_INCLUDE_PAC);
537             /* Map principals from foreign (possibly non-AD) realms */
538             setflag(c_flags, KRB5_KDB_FLAG_MAP_PRINCIPALS);
539 
540             assert(client == NULL); /* should not have been set already */
541 
542             errcode = krb5_db_get_principal(kdc_context, subject_tkt->client,
543                                             c_flags, &client);
544         }
545     }
546 
547     if (isflagset(c_flags, KRB5_KDB_FLAGS_S4U) && !is_referral)
548         enc_tkt_reply.client = altcprinc;
549     else
550         enc_tkt_reply.client = header_enc_tkt->client;
551 
552     enc_tkt_reply.session = &session_key;
553     enc_tkt_reply.transited.tr_type = KRB5_DOMAIN_X500_COMPRESS;
554     enc_tkt_reply.transited.tr_contents = empty_string; /* equivalent of "" */
555 
556     /*
557      * Only add the realm of the presented tgt to the transited list if
558      * it is different than the server realm (cross-realm) and it is different
559      * than the realm of the client (since the realm of the client is already
560      * implicitly part of the transited list and should not be explicitly
561      * listed).
562      */
563     if (!is_crossrealm ||
564         krb5_realm_compare(kdc_context, header_ticket->server,
565                            enc_tkt_reply.client)) {
566         /* tgt issued by local realm or issued by realm of client */
567         enc_tkt_reply.transited = header_enc_tkt->transited;
568     } else {
569         /* tgt issued by some other realm and not the realm of the client */
570         /* assemble new transited field into allocated storage */
571         if (header_enc_tkt->transited.tr_type !=
572             KRB5_DOMAIN_X500_COMPRESS) {
573             status = "VALIDATE_TRANSIT_TYPE";
574             errcode = KRB5KDC_ERR_TRTYPE_NOSUPP;
575             goto cleanup;
576         }
577         memset(&enc_tkt_reply.transited, 0, sizeof(enc_tkt_reply.transited));
578         enc_tkt_reply.transited.tr_type = KRB5_DOMAIN_X500_COMPRESS;
579         if ((errcode =
580              add_to_transited(&header_enc_tkt->transited.tr_contents,
581                               &enc_tkt_reply.transited.tr_contents,
582                               header_ticket->server,
583                               enc_tkt_reply.client,
584                               request->server))) {
585             status = "ADD_TO_TRANSITED_LIST";
586             goto cleanup;
587         }
588         newtransited = 1;
589     }
590     if (!isflagset (request->kdc_options, KDC_OPT_DISABLE_TRANSITED_CHECK)) {
591         errcode = kdc_check_transited_list (kdc_active_realm,
592                                             &enc_tkt_reply.transited.tr_contents,
593                                             krb5_princ_realm (kdc_context, header_enc_tkt->client),
594                                             krb5_princ_realm (kdc_context, request->server));
595         if (errcode == 0) {
596             setflag (enc_tkt_reply.flags, TKT_FLG_TRANSIT_POLICY_CHECKED);
597         } else {
598             log_tgs_badtrans(kdc_context, cprinc, sprinc,
599                              &enc_tkt_reply.transited.tr_contents, errcode);
600         }
601     } else
602         krb5_klog_syslog(LOG_INFO, _("not checking transit path"));
603     if (kdc_active_realm->realm_reject_bad_transit &&
604         !isflagset(enc_tkt_reply.flags, TKT_FLG_TRANSIT_POLICY_CHECKED)) {
605         errcode = KRB5KDC_ERR_POLICY;
606         status = "BAD_TRANSIT";
607         au_state->violation = LOCAL_POLICY;
608         goto cleanup;
609     }
610 
611     errcode = handle_authdata(kdc_context, c_flags, client, server,
612                               subject_server, local_tgt, &local_tgt_key,
613                               subkey != NULL ? subkey :
614                               header_ticket->enc_part2->session,
615                               encrypting_key, subject_key, pkt, request,
616                               altcprinc, ad_info, subject_tkt,
617                               &auth_indicators, &enc_tkt_reply);
618     if (errcode) {
619         krb5_klog_syslog(LOG_INFO, _("TGS_REQ : handle_authdata (%d)"),
620                          errcode);
621         status = "HANDLE_AUTHDATA";
622         goto cleanup;
623     }
624 
625     ticket_reply.enc_part2 = &enc_tkt_reply;
626 
627     /* If we are doing user-to-user authentication, encrypt the ticket using
628      * the session key of the second ticket. */
629     if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) {
630         ticket_kvno = 0;
631         ticket_reply.enc_part.enctype = stkt->enc_part2->session->enctype;
632         kau_u2u(kdc_context, TRUE, au_state);
633     } else {
634         ticket_kvno = current_kvno(server);
635     }
636 
637     errcode = krb5_encrypt_tkt_part(kdc_context, encrypting_key,
638                                     &ticket_reply);
639     if (errcode)
640         goto cleanup;
641     ticket_reply.enc_part.kvno = ticket_kvno;
642     /* Start assembling the response */
643     au_state->stage = ENCR_REP;
644     reply.msg_type = KRB5_TGS_REP;
645     if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION) &&
646         krb5int_find_pa_data(kdc_context, request->padata,
647                              KRB5_PADATA_S4U_X509_USER) != NULL) {
648         errcode = kdc_make_s4u2self_rep(kdc_context,
649                                         subkey,
650                                         header_ticket->enc_part2->session,
651                                         s4u_x509_user,
652                                         &reply,
653                                         &reply_encpart);
654         if (errcode)
655             au_state->status = status;
656         kau_s4u2self(kdc_context, errcode ? FALSE : TRUE, au_state);
657         if (errcode)
658             goto cleanup;
659     }
660 
661     reply.client = enc_tkt_reply.client;
662     reply.enc_part.kvno = 0;/* We are using the session key */
663     reply.ticket = &ticket_reply;
664 
665     reply_encpart.session = &session_key;
666     reply_encpart.nonce = request->nonce;
667 
668     /* copy the time fields */
669     reply_encpart.times = enc_tkt_reply.times;
670 
671     nolrentry.lr_type = KRB5_LRQ_NONE;
672     nolrentry.value = 0;
673     nolrentry.magic = 0;
674     nolrarray[0] = &nolrentry;
675     nolrarray[1] = 0;
676     reply_encpart.last_req = nolrarray;        /* not available for TGS reqs */
677     reply_encpart.key_exp = 0;/* ditto */
678     reply_encpart.flags = enc_tkt_reply.flags;
679     reply_encpart.server = ticket_reply.server;
680 
681     /* use the session key in the ticket, unless there's a subsession key
682        in the AP_REQ */
683     reply.enc_part.enctype = subkey ? subkey->enctype :
684         header_ticket->enc_part2->session->enctype;
685     errcode  = kdc_fast_response_handle_padata(state, request, &reply,
686                                                subkey ? subkey->enctype : header_ticket->enc_part2->session->enctype);
687     if (errcode)
688         goto cleanup;
689     errcode =kdc_fast_handle_reply_key(state,
690                                        subkey?subkey:header_ticket->enc_part2->session, &reply_key);
691     if (errcode)
692         goto cleanup;
693     errcode = return_enc_padata(kdc_context, pkt, request,
694                                 reply_key, server, &reply_encpart,
695                                 is_referral &&
696                                 isflagset(s_flags,
697                                           KRB5_KDB_FLAG_CANONICALIZE));
698     if (errcode) {
699         status = "KDC_RETURN_ENC_PADATA";
700         goto cleanup;
701     }
702 
703     errcode = kau_make_tkt_id(kdc_context, &ticket_reply, &au_state->tkt_out_id);
704     if (errcode)
705         goto cleanup;
706 
707     if (kdc_fast_hide_client(state))
708         reply.client = (krb5_principal)krb5_anonymous_principal();
709     errcode = krb5_encode_kdc_rep(kdc_context, KRB5_TGS_REP, &reply_encpart,
710                                   subkey ? 1 : 0,
711                                   reply_key,
712                                   &reply, response);
713     if (!errcode)
714         status = "ISSUE";
715 
716     memset(ticket_reply.enc_part.ciphertext.data, 0,
717            ticket_reply.enc_part.ciphertext.length);
718     free(ticket_reply.enc_part.ciphertext.data);
719     /* these parts are left on as a courtesy from krb5_encode_kdc_rep so we
720        can use them in raw form if needed.  But, we don't... */
721     memset(reply.enc_part.ciphertext.data, 0,
722            reply.enc_part.ciphertext.length);
723     free(reply.enc_part.ciphertext.data);
724 
725 cleanup:
726     if (status == NULL)
727         status = "UNKNOWN_REASON";
728     krb5_free_keyblock_contents(kdc_context, &server_keyblock);
729     if (reply_key)
730         krb5_free_keyblock(kdc_context, reply_key);
731     if (stkt_server_key)
732         krb5_free_keyblock(kdc_context, stkt_server_key);
733     if (errcode)
734         emsg = krb5_get_error_message (kdc_context, errcode);
735 
736     au_state->status = status;
737     if (!errcode)
738         au_state->reply = &reply;
739     kau_tgs_req(kdc_context, errcode ? FALSE : TRUE, au_state);
740     kau_free_kdc_req(au_state);
741 
742     log_tgs_req(kdc_context, from, request, &reply, cprinc,
743                 sprinc, altcprinc, authtime,
744                 c_flags, status, errcode, emsg);
745     if (errcode) {
746         krb5_free_error_message (kdc_context, emsg);
747         emsg = NULL;
748     }
749 
750     if (errcode) {
751         int got_err = 0;
752         if (status == 0) {
753             status = krb5_get_error_message (kdc_context, errcode);
754             got_err = 1;
755         }
756         errcode -= ERROR_TABLE_BASE_krb5;
757         if (errcode < 0 || errcode > KRB_ERR_MAX)
758             errcode = KRB_ERR_GENERIC;
759 
760         retval = prepare_error_tgs(state, request, header_ticket, errcode,
761                                    (server != NULL) ? server->princ : NULL,
762                                    response, status, e_data);
763         if (got_err) {
764             krb5_free_error_message (kdc_context, status);
765             status = 0;
766         }
767     }
768 
769     if (header_ticket != NULL)
770         krb5_free_ticket(kdc_context, header_ticket);
771     if (request != NULL)
772         krb5_free_kdc_req(kdc_context, request);
773     if (state)
774         kdc_free_rstate(state);
775     krb5_db_free_principal(kdc_context, server);
776     krb5_db_free_principal(kdc_context, stkt_server);
777     krb5_db_free_principal(kdc_context, header_server);
778     krb5_db_free_principal(kdc_context, client);
779     krb5_db_free_principal(kdc_context, local_tgt_storage);
780     if (local_tgt_key.contents != NULL)
781         krb5_free_keyblock_contents(kdc_context, &local_tgt_key);
782     if (session_key.contents != NULL)
783         krb5_free_keyblock_contents(kdc_context, &session_key);
784     if (newtransited)
785         free(enc_tkt_reply.transited.tr_contents.data);
786     if (s4u_x509_user != NULL)
787         krb5_free_pa_s4u_x509_user(kdc_context, s4u_x509_user);
788     if (kdc_issued_auth_data != NULL)
789         krb5_free_authdata(kdc_context, kdc_issued_auth_data);
790     if (subkey != NULL)
791         krb5_free_keyblock(kdc_context, subkey);
792     if (header_key != NULL)
793         krb5_free_keyblock(kdc_context, header_key);
794     if (reply.padata)
795         krb5_free_pa_data(kdc_context, reply.padata);
796     if (reply_encpart.enc_padata)
797         krb5_free_pa_data(kdc_context, reply_encpart.enc_padata);
798     if (enc_tkt_reply.authorization_data != NULL)
799         krb5_free_authdata(kdc_context, enc_tkt_reply.authorization_data);
800     krb5_free_pa_data(kdc_context, e_data);
801     k5_free_data_ptr_list(auth_indicators);
802     krb5_db_free_authdata_info(kdc_context, ad_info);
803     krb5_db_free_authdata_info(kdc_context, stkt_ad_info);
804     krb5_free_principal(kdc_context, stkt_authdata_client);
805 
806     return retval;
807 }
808 
809 static krb5_error_code
prepare_error_tgs(struct kdc_request_state * state,krb5_kdc_req * request,krb5_ticket * ticket,int error,krb5_principal canon_server,krb5_data ** response,const char * status,krb5_pa_data ** e_data)810 prepare_error_tgs (struct kdc_request_state *state,
811                    krb5_kdc_req *request, krb5_ticket *ticket, int error,
812                    krb5_principal canon_server,
813                    krb5_data **response, const char *status,
814                    krb5_pa_data **e_data)
815 {
816     krb5_error errpkt;
817     krb5_error_code retval = 0;
818     krb5_data *scratch, *e_data_asn1 = NULL, *fast_edata = NULL;
819     kdc_realm_t *kdc_active_realm = state->realm_data;
820 
821     errpkt.magic = KV5M_ERROR;
822     errpkt.ctime = 0;
823     errpkt.cusec = 0;
824 
825     if ((retval = krb5_us_timeofday(kdc_context, &errpkt.stime,
826                                     &errpkt.susec)))
827         return(retval);
828     errpkt.error = error;
829     errpkt.server = request->server;
830     if (ticket && ticket->enc_part2)
831         errpkt.client = ticket->enc_part2->client;
832     else
833         errpkt.client = NULL;
834     errpkt.text.length = strlen(status);
835     if (!(errpkt.text.data = strdup(status)))
836         return ENOMEM;
837 
838     if (!(scratch = (krb5_data *)malloc(sizeof(*scratch)))) {
839         free(errpkt.text.data);
840         return ENOMEM;
841     }
842 
843     if (e_data != NULL) {
844         retval = encode_krb5_padata_sequence(e_data, &e_data_asn1);
845         if (retval) {
846             free(scratch);
847             free(errpkt.text.data);
848             return retval;
849         }
850         errpkt.e_data = *e_data_asn1;
851     } else
852         errpkt.e_data = empty_data();
853 
854     retval = kdc_fast_handle_error(kdc_context, state, request, e_data,
855                                    &errpkt, &fast_edata);
856     if (retval) {
857         free(scratch);
858         free(errpkt.text.data);
859         krb5_free_data(kdc_context, e_data_asn1);
860         return retval;
861     }
862     if (fast_edata)
863         errpkt.e_data = *fast_edata;
864     if (kdc_fast_hide_client(state) && errpkt.client != NULL)
865         errpkt.client = (krb5_principal)krb5_anonymous_principal();
866     retval = krb5_mk_error(kdc_context, &errpkt, scratch);
867     free(errpkt.text.data);
868     krb5_free_data(kdc_context, e_data_asn1);
869     krb5_free_data(kdc_context, fast_edata);
870     if (retval)
871         free(scratch);
872     else
873         *response = scratch;
874 
875     return retval;
876 }
877 
878 /* KDC options that require a second ticket */
879 #define STKT_OPTIONS (KDC_OPT_CNAME_IN_ADDL_TKT | KDC_OPT_ENC_TKT_IN_SKEY)
880 /*
881  * If req is a second-ticket request and a second ticket is present, decrypt
882  * it.  Set *stkt_out to an alias to the ticket with populated enc_part2.  Set
883  * *server_out to the server DB entry and *key_out to the ticket decryption
884  * key.
885  */
886 static krb5_error_code
decrypt_2ndtkt(kdc_realm_t * kdc_active_realm,krb5_kdc_req * req,krb5_flags flags,const krb5_ticket ** stkt_out,krb5_db_entry ** server_out,krb5_keyblock ** key_out,const char ** status)887 decrypt_2ndtkt(kdc_realm_t *kdc_active_realm, krb5_kdc_req *req,
888                krb5_flags flags, const krb5_ticket **stkt_out,
889                krb5_db_entry **server_out, krb5_keyblock **key_out,
890                const char **status)
891 {
892     krb5_error_code retval;
893     krb5_db_entry *server = NULL;
894     krb5_keyblock *key = NULL;
895     krb5_kvno kvno;
896     krb5_ticket *stkt;
897 
898     *stkt_out = NULL;
899     *server_out = NULL;
900     *key_out = NULL;
901 
902     if (!(req->kdc_options & STKT_OPTIONS) || req->second_ticket == NULL ||
903         req->second_ticket[0] == NULL)
904         return 0;
905 
906     stkt = req->second_ticket[0];
907     retval = kdc_get_server_key(kdc_context, stkt, flags, TRUE,
908                                 &server, &key, &kvno);
909     if (retval != 0) {
910         *status = "2ND_TKT_SERVER";
911         goto cleanup;
912     }
913     retval = krb5_decrypt_tkt_part(kdc_context, key, stkt);
914     if (retval != 0) {
915         *status = "2ND_TKT_DECRYPT";
916         goto cleanup;
917     }
918     *stkt_out = stkt;
919     *server_out = server;
920     *key_out = key;
921     server = NULL;
922     key = NULL;
923 
924 cleanup:
925     krb5_db_free_principal(kdc_context, server);
926     krb5_free_keyblock(kdc_context, key);
927     return retval;
928 }
929 
930 static krb5_error_code
get_2ndtkt_enctype(kdc_realm_t * kdc_active_realm,krb5_kdc_req * req,krb5_enctype * useenctype,const char ** status)931 get_2ndtkt_enctype(kdc_realm_t *kdc_active_realm, krb5_kdc_req *req,
932                    krb5_enctype *useenctype, const char **status)
933 {
934     krb5_enctype etype;
935     krb5_ticket *stkt = req->second_ticket[0];
936     int i;
937 
938     etype = stkt->enc_part2->session->enctype;
939     if (!krb5_c_valid_enctype(etype)) {
940         *status = "BAD_ETYPE_IN_2ND_TKT";
941         return KRB5KDC_ERR_ETYPE_NOSUPP;
942     }
943     for (i = 0; i < req->nktypes; i++) {
944         if (req->ktype[i] == etype) {
945             *useenctype = etype;
946             break;
947         }
948     }
949     return 0;
950 }
951 
952 static krb5_error_code
gen_session_key(kdc_realm_t * kdc_active_realm,krb5_kdc_req * req,krb5_db_entry * server,krb5_keyblock * skey,const char ** status)953 gen_session_key(kdc_realm_t *kdc_active_realm, krb5_kdc_req *req,
954                 krb5_db_entry *server, krb5_keyblock *skey,
955                 const char **status)
956 {
957     krb5_error_code retval;
958     krb5_enctype useenctype = 0;
959 
960     /*
961      * Some special care needs to be taken in the user-to-user
962      * case, since we don't know what keytypes the application server
963      * which is doing user-to-user authentication can support.  We
964      * know that it at least must be able to support the encryption
965      * type of the session key in the TGT, since otherwise it won't be
966      * able to decrypt the U2U ticket!  So we use that in preference
967      * to anything else.
968      */
969     if (req->kdc_options & KDC_OPT_ENC_TKT_IN_SKEY) {
970         retval = get_2ndtkt_enctype(kdc_active_realm, req, &useenctype,
971                                     status);
972         if (retval != 0)
973             return retval;
974     }
975     if (useenctype == 0) {
976         useenctype = select_session_keytype(kdc_active_realm, server,
977                                             req->nktypes,
978                                             req->ktype);
979     }
980     if (useenctype == 0) {
981         /* unsupported ktype */
982         *status = "BAD_ENCRYPTION_TYPE";
983         return KRB5KDC_ERR_ETYPE_NOSUPP;
984     }
985 
986     return krb5_c_make_random_key(kdc_context, useenctype, skey);
987 }
988 
989 /*
990  * The request seems to be for a ticket-granting service somewhere else,
991  * but we don't have a ticket for the final TGS.  Try to give the requestor
992  * some intermediate realm.
993  */
994 static krb5_error_code
find_alternate_tgs(kdc_realm_t * kdc_active_realm,krb5_principal princ,krb5_db_entry ** server_ptr,const char ** status)995 find_alternate_tgs(kdc_realm_t *kdc_active_realm, krb5_principal princ,
996                    krb5_db_entry **server_ptr, const char **status)
997 {
998     krb5_error_code retval;
999     krb5_principal *plist = NULL, *pl2;
1000     krb5_data tmp;
1001     krb5_db_entry *server = NULL;
1002 
1003     *server_ptr = NULL;
1004     assert(is_cross_tgs_principal(princ));
1005     if ((retval = krb5_walk_realm_tree(kdc_context,
1006                                        krb5_princ_realm(kdc_context, princ),
1007                                        krb5_princ_component(kdc_context, princ, 1),
1008                                        &plist, KRB5_REALM_BRANCH_CHAR))) {
1009         goto cleanup;
1010     }
1011     /* move to the end */
1012     for (pl2 = plist; *pl2; pl2++);
1013 
1014     /* the first entry in this array is for krbtgt/local@local, so we
1015        ignore it */
1016     while (--pl2 > plist) {
1017         tmp = *krb5_princ_realm(kdc_context, *pl2);
1018         krb5_princ_set_realm(kdc_context, *pl2,
1019                              krb5_princ_realm(kdc_context, princ));
1020         retval = db_get_svc_princ(kdc_context, *pl2, 0, &server, status);
1021         krb5_princ_set_realm(kdc_context, *pl2, &tmp);
1022         if (retval == KRB5_KDB_NOENTRY)
1023             continue;
1024         else if (retval)
1025             goto cleanup;
1026 
1027         log_tgs_alt_tgt(kdc_context, server->princ);
1028         *server_ptr = server;
1029         server = NULL;
1030         goto cleanup;
1031     }
1032 cleanup:
1033     if (retval == 0 && *server_ptr == NULL)
1034         retval = KRB5_KDB_NOENTRY;
1035     if (retval != 0)
1036         *status = "UNKNOWN_SERVER";
1037 
1038     krb5_free_realm_tree(kdc_context, plist);
1039     krb5_db_free_principal(kdc_context, server);
1040     return retval;
1041 }
1042 
1043 /* Return true if item is an element of the space/comma-separated list. */
1044 static krb5_boolean
in_list(const char * list,const char * item)1045 in_list(const char *list, const char *item)
1046 {
1047     const char *p;
1048     int len = strlen(item);
1049 
1050     if (list == NULL)
1051         return FALSE;
1052     for (p = strstr(list, item); p != NULL; p = strstr(p + 1, item)) {
1053         if ((p == list || isspace((unsigned char)p[-1]) || p[-1] == ',') &&
1054             (p[len] == '\0' || isspace((unsigned char)p[len]) ||
1055              p[len] == ','))
1056                 return TRUE;
1057     }
1058     return FALSE;
1059 }
1060 
1061 /*
1062  * Check whether the request satisfies the conditions for generating a referral
1063  * TGT.  The caller checks whether the hostname component looks like a FQDN.
1064  */
1065 static krb5_boolean
is_referral_req(kdc_realm_t * kdc_active_realm,krb5_kdc_req * request)1066 is_referral_req(kdc_realm_t *kdc_active_realm, krb5_kdc_req *request)
1067 {
1068     krb5_boolean ret = FALSE;
1069     char *stype = NULL;
1070     char *hostbased = kdc_active_realm->realm_hostbased;
1071     char *no_referral = kdc_active_realm->realm_no_referral;
1072 
1073     if (!(request->kdc_options & KDC_OPT_CANONICALIZE))
1074         return FALSE;
1075 
1076     if (request->kdc_options & KDC_OPT_ENC_TKT_IN_SKEY)
1077         return FALSE;
1078 
1079     if (krb5_princ_size(kdc_context, request->server) != 2)
1080         return FALSE;
1081 
1082     stype = data2string(krb5_princ_component(kdc_context, request->server, 0));
1083     if (stype == NULL)
1084         return FALSE;
1085     switch (krb5_princ_type(kdc_context, request->server)) {
1086     case KRB5_NT_UNKNOWN:
1087         /* Allow referrals for NT-UNKNOWN principals, if configured. */
1088         if (!in_list(hostbased, stype) && !in_list(hostbased, "*"))
1089             goto cleanup;
1090         /* FALLTHROUGH */
1091     case KRB5_NT_SRV_HST:
1092     case KRB5_NT_SRV_INST:
1093         /* Deny referrals for specific service types, if configured. */
1094         if (in_list(no_referral, stype) || in_list(no_referral, "*"))
1095             goto cleanup;
1096         ret = TRUE;
1097         break;
1098     default:
1099         goto cleanup;
1100     }
1101 cleanup:
1102     free(stype);
1103     return ret;
1104 }
1105 
1106 /*
1107  * Find a remote realm TGS principal for an unknown host-based service
1108  * principal.
1109  */
1110 static krb5_int32
find_referral_tgs(kdc_realm_t * kdc_active_realm,krb5_kdc_req * request,krb5_principal * krbtgt_princ)1111 find_referral_tgs(kdc_realm_t *kdc_active_realm, krb5_kdc_req *request,
1112                   krb5_principal *krbtgt_princ)
1113 {
1114     krb5_error_code retval = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1115     char **realms = NULL, *hostname = NULL;
1116     krb5_data srealm = request->server->realm;
1117 
1118     if (!is_referral_req(kdc_active_realm, request))
1119         goto cleanup;
1120 
1121     hostname = data2string(krb5_princ_component(kdc_context,
1122                                                 request->server, 1));
1123     if (hostname == NULL) {
1124         retval = ENOMEM;
1125         goto cleanup;
1126     }
1127     /* If the hostname doesn't contain a '.', it's not a FQDN. */
1128     if (strchr(hostname, '.') == NULL)
1129         goto cleanup;
1130     retval = krb5_get_host_realm(kdc_context, hostname, &realms);
1131     if (retval) {
1132         /* no match found */
1133         kdc_err(kdc_context, retval, "unable to find realm of host");
1134         goto cleanup;
1135     }
1136     /* Don't return a referral to the empty realm or the service realm. */
1137     if (realms == NULL || realms[0] == NULL || *realms[0] == '\0' ||
1138         data_eq_string(srealm, realms[0])) {
1139         retval = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1140         goto cleanup;
1141     }
1142     retval = krb5_build_principal(kdc_context, krbtgt_princ,
1143                                   srealm.length, srealm.data,
1144                                   "krbtgt", realms[0], (char *)0);
1145 cleanup:
1146     krb5_free_host_realm(kdc_context, realms);
1147     free(hostname);
1148 
1149     return retval;
1150 }
1151 
1152 static krb5_error_code
db_get_svc_princ(krb5_context ctx,krb5_principal princ,krb5_flags flags,krb5_db_entry ** server,const char ** status)1153 db_get_svc_princ(krb5_context ctx, krb5_principal princ,
1154                  krb5_flags flags, krb5_db_entry **server,
1155                  const char **status)
1156 {
1157     krb5_error_code ret;
1158 
1159     ret = krb5_db_get_principal(ctx, princ, flags, server);
1160     if (ret == KRB5_KDB_CANTLOCK_DB)
1161         ret = KRB5KDC_ERR_SVC_UNAVAILABLE;
1162     if (ret != 0) {
1163         *status = "LOOKING_UP_SERVER";
1164     }
1165     return ret;
1166 }
1167 
1168 static krb5_error_code
search_sprinc(kdc_realm_t * kdc_active_realm,krb5_kdc_req * req,krb5_flags flags,krb5_db_entry ** server,const char ** status)1169 search_sprinc(kdc_realm_t *kdc_active_realm, krb5_kdc_req *req,
1170               krb5_flags flags, krb5_db_entry **server, const char **status)
1171 {
1172     krb5_error_code ret;
1173     krb5_principal princ = req->server;
1174     krb5_principal reftgs = NULL;
1175     krb5_boolean allow_referral;
1176 
1177     /* Do not allow referrals for u2u or ticket modification requests, because
1178      * the server is supposed to match an already-issued ticket. */
1179     allow_referral = !(req->kdc_options & NO_REFERRAL_OPTION);
1180     if (!allow_referral)
1181         flags &= ~KRB5_KDB_FLAG_CANONICALIZE;
1182 
1183     ret = db_get_svc_princ(kdc_context, princ, flags, server, status);
1184     if (ret == 0 || ret != KRB5_KDB_NOENTRY || !allow_referral)
1185         goto cleanup;
1186 
1187     if (!is_cross_tgs_principal(req->server)) {
1188         ret = find_referral_tgs(kdc_active_realm, req, &reftgs);
1189         if (ret != 0)
1190             goto cleanup;
1191         ret = db_get_svc_princ(kdc_context, reftgs, flags, server, status);
1192         if (ret == 0 || ret != KRB5_KDB_NOENTRY)
1193             goto cleanup;
1194 
1195         princ = reftgs;
1196     }
1197     ret = find_alternate_tgs(kdc_active_realm, princ, server, status);
1198 
1199 cleanup:
1200     if (ret != 0 && ret != KRB5KDC_ERR_SVC_UNAVAILABLE) {
1201         ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1202         if (*status == NULL)
1203             *status = "LOOKING_UP_SERVER";
1204     }
1205     krb5_free_principal(kdc_context, reftgs);
1206     return ret;
1207 }
1208