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