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