1 /*
2  * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 /*
27  *
28  *  (C) Copyright IBM Corp. 1999 All Rights Reserved.
29  *  Copyright 1997 The Open Group Research Institute.  All rights reserved.
30  */
31 
32 package sun.security.krb5;
33 
34 import sun.security.krb5.internal.*;
35 import sun.security.krb5.internal.crypto.*;
36 import java.io.IOException;
37 import java.net.UnknownHostException;
38 import java.time.Instant;
39 
40 /**
41  * This class encapsulates a Kerberos TGS-REQ that is sent from the
42  * client to the KDC.
43  */
44 public class KrbTgsReq {
45 
46     private PrincipalName princName;
47     private PrincipalName servName;
48     private TGSReq tgsReqMessg;
49     private KerberosTime ctime;
50     private Ticket secondTicket = null;
51     private boolean useSubkey = false;
52     EncryptionKey tgsReqKey;
53 
54     private static final boolean DEBUG = Krb5.DEBUG;
55 
56     private byte[] obuf;
57     private byte[] ibuf;
58 
59     // Used in CredentialsUtil
KrbTgsReq(Credentials asCreds, PrincipalName sname)60     public KrbTgsReq(Credentials asCreds,
61                      PrincipalName sname)
62         throws KrbException, IOException {
63         this(new KDCOptions(),
64             asCreds,
65             sname,
66             null, // KerberosTime from
67             null, // KerberosTime till
68             null, // KerberosTime rtime
69             null, // eTypes, // null, // int[] eTypes
70             null, // HostAddresses addresses
71             null, // AuthorizationData authorizationData
72             null, // Ticket[] additionalTickets
73             null); // EncryptionKey subSessionKey
74     }
75 
76     // S4U2proxy
KrbTgsReq(Credentials asCreds, Ticket second, PrincipalName sname)77     public KrbTgsReq(Credentials asCreds,
78                      Ticket second,
79                      PrincipalName sname)
80             throws KrbException, IOException {
81         this(KDCOptions.with(KDCOptions.CNAME_IN_ADDL_TKT,
82                 KDCOptions.FORWARDABLE),
83             asCreds,
84             sname,
85             null,
86             null,
87             null,
88             null,
89             null,
90             null,
91             new Ticket[] {second}, // the service ticket
92             null);
93     }
94 
95     // S4U2user
KrbTgsReq(Credentials asCreds, PrincipalName sname, PAData extraPA)96     public KrbTgsReq(Credentials asCreds,
97                      PrincipalName sname,
98                      PAData extraPA)
99         throws KrbException, IOException {
100         this(KDCOptions.with(KDCOptions.FORWARDABLE),
101             asCreds,
102             asCreds.getClient(),
103             sname,
104             null,
105             null,
106             null,
107             null,
108             null,
109             null,
110             null,
111             null,
112             extraPA); // the PA-FOR-USER
113     }
114 
115     // Called by Credentials, KrbCred
KrbTgsReq( KDCOptions options, Credentials asCreds, PrincipalName sname, KerberosTime from, KerberosTime till, KerberosTime rtime, int[] eTypes, HostAddresses addresses, AuthorizationData authorizationData, Ticket[] additionalTickets, EncryptionKey subKey)116     KrbTgsReq(
117             KDCOptions options,
118             Credentials asCreds,
119             PrincipalName sname,
120             KerberosTime from,
121             KerberosTime till,
122             KerberosTime rtime,
123             int[] eTypes,
124             HostAddresses addresses,
125             AuthorizationData authorizationData,
126             Ticket[] additionalTickets,
127             EncryptionKey subKey) throws KrbException, IOException {
128         this(options, asCreds, asCreds.getClient(), sname,
129                 from, till, rtime, eTypes, addresses,
130                 authorizationData, additionalTickets, subKey, null);
131     }
132 
KrbTgsReq( KDCOptions options, Credentials asCreds, PrincipalName cname, PrincipalName sname, KerberosTime from, KerberosTime till, KerberosTime rtime, int[] eTypes, HostAddresses addresses, AuthorizationData authorizationData, Ticket[] additionalTickets, EncryptionKey subKey, PAData extraPA)133     private KrbTgsReq(
134             KDCOptions options,
135             Credentials asCreds,
136             PrincipalName cname,
137             PrincipalName sname,
138             KerberosTime from,
139             KerberosTime till,
140             KerberosTime rtime,
141             int[] eTypes,
142             HostAddresses addresses,
143             AuthorizationData authorizationData,
144             Ticket[] additionalTickets,
145             EncryptionKey subKey,
146             PAData extraPA) throws KrbException, IOException {
147 
148         princName = cname;
149         servName = sname;
150         ctime = KerberosTime.now();
151 
152         // check if they are valid arguments. The optional fields
153         // should be consistent with settings in KDCOptions.
154 
155         if (options.get(KDCOptions.FORWARDABLE) &&
156                 (!(asCreds.flags.get(Krb5.TKT_OPTS_FORWARDABLE)))) {
157             options.set(KDCOptions.FORWARDABLE, false);
158         }
159         if (options.get(KDCOptions.FORWARDED)) {
160             if (!(asCreds.flags.get(KDCOptions.FORWARDABLE)))
161                 throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS);
162         }
163         if (options.get(KDCOptions.PROXIABLE) &&
164                 (!(asCreds.flags.get(Krb5.TKT_OPTS_PROXIABLE)))) {
165             throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS);
166         }
167         if (options.get(KDCOptions.PROXY)) {
168             if (!(asCreds.flags.get(KDCOptions.PROXIABLE)))
169                 throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS);
170         }
171         if (options.get(KDCOptions.ALLOW_POSTDATE) &&
172                 (!(asCreds.flags.get(Krb5.TKT_OPTS_MAY_POSTDATE)))) {
173             throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS);
174         }
175         if (options.get(KDCOptions.RENEWABLE) &&
176                 (!(asCreds.flags.get(Krb5.TKT_OPTS_RENEWABLE)))) {
177             throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS);
178         }
179 
180         if (options.get(KDCOptions.POSTDATED)) {
181             if (!(asCreds.flags.get(KDCOptions.POSTDATED)))
182                 throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS);
183         } else {
184             if (from != null)  from = null;
185         }
186         if (options.get(KDCOptions.RENEWABLE)) {
187             if (!(asCreds.flags.get(KDCOptions.RENEWABLE)))
188                 throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS);
189         } else {
190             if (rtime != null)  rtime = null;
191         }
192         if (options.get(KDCOptions.ENC_TKT_IN_SKEY) || options.get(KDCOptions.CNAME_IN_ADDL_TKT)) {
193             if (additionalTickets == null)
194                 throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS);
195             // in TGS_REQ there could be more than one additional
196             // tickets,  but in file-based credential cache,
197             // there is only one additional ticket field.
198             secondTicket = additionalTickets[0];
199         } else {
200             if (additionalTickets != null)
201                 additionalTickets = null;
202         }
203 
204         tgsReqMessg = createRequest(
205                 options,
206                 asCreds.ticket,
207                 asCreds.key,
208                 ctime,
209                 princName,
210                 servName,
211                 from,
212                 till,
213                 rtime,
214                 eTypes,
215                 addresses,
216                 authorizationData,
217                 additionalTickets,
218                 subKey,
219                 extraPA);
220         obuf = tgsReqMessg.asn1Encode();
221 
222         // XXX We need to revisit this to see if can't move it
223         // up such that FORWARDED flag set in the options
224         // is included in the marshaled request.
225         /*
226          * If this is based on a forwarded ticket, record that in the
227          * options, because the returned TgsRep will contain the
228          * FORWARDED flag set.
229          */
230         if (asCreds.flags.get(KDCOptions.FORWARDED))
231             options.set(KDCOptions.FORWARDED, true);
232 
233 
234     }
235 
236     /**
237      * Sends a TGS request to the realm of the target.
238      * @throws KrbException
239      * @throws IOException
240      */
send()241     public void send() throws IOException, KrbException {
242         String realmStr = null;
243         if (servName != null)
244             realmStr = servName.getRealmString();
245         KdcComm comm = new KdcComm(realmStr);
246         ibuf = comm.send(obuf);
247     }
248 
getReply()249     public KrbTgsRep getReply()
250         throws KrbException, IOException {
251         return new KrbTgsRep(ibuf, this);
252     }
253 
254     /**
255      * Sends the request, waits for a reply, and returns the Credentials.
256      * Used in Credentials, KrbCred, and internal/CredentialsUtil.
257      */
sendAndGetCreds()258     public Credentials sendAndGetCreds() throws IOException, KrbException {
259         KrbTgsRep tgs_rep = null;
260         String kdc = null;
261         send();
262         tgs_rep = getReply();
263         return tgs_rep.getCreds();
264     }
265 
getCtime()266     KerberosTime getCtime() {
267         return ctime;
268     }
269 
createRequest( KDCOptions kdc_options, Ticket ticket, EncryptionKey key, KerberosTime ctime, PrincipalName cname, PrincipalName sname, KerberosTime from, KerberosTime till, KerberosTime rtime, int[] eTypes, HostAddresses addresses, AuthorizationData authorizationData, Ticket[] additionalTickets, EncryptionKey subKey, PAData extraPA)270     private TGSReq createRequest(
271                          KDCOptions kdc_options,
272                          Ticket ticket,
273                          EncryptionKey key,
274                          KerberosTime ctime,
275                          PrincipalName cname,
276                          PrincipalName sname,
277                          KerberosTime from,
278                          KerberosTime till,
279                          KerberosTime rtime,
280                          int[] eTypes,
281                          HostAddresses addresses,
282                          AuthorizationData authorizationData,
283                          Ticket[] additionalTickets,
284                          EncryptionKey subKey,
285                          PAData extraPA)
286         throws IOException, KrbException, UnknownHostException {
287         KerberosTime req_till = null;
288         if (till == null) {
289             String d = Config.getInstance().get("libdefaults", "ticket_lifetime");
290             if (d != null) {
291                 req_till = new KerberosTime(Instant.now().plusSeconds(Config.duration(d)));
292             } else {
293                 req_till = new KerberosTime(0); // Choose KDC maximum allowed
294             }
295         } else {
296             req_till = till;
297         }
298 
299         /*
300          * RFC 4120, Section 5.4.2.
301          * For KRB_TGS_REP, the ciphertext is encrypted in the
302          * sub-session key from the Authenticator, or if absent,
303          * the session key from the ticket-granting ticket used
304          * in the request.
305          *
306          * To support this, use tgsReqKey to remember which key to use.
307          */
308         tgsReqKey = key;
309 
310         int[] req_eTypes = null;
311         if (eTypes == null) {
312             req_eTypes = EType.getDefaults("default_tgs_enctypes");
313         } else {
314             req_eTypes = eTypes;
315         }
316 
317         EncryptionKey reqKey = null;
318         EncryptedData encAuthorizationData = null;
319         if (authorizationData != null) {
320             byte[] ad = authorizationData.asn1Encode();
321             if (subKey != null) {
322                 reqKey = subKey;
323                 tgsReqKey = subKey;    // Key to use to decrypt reply
324                 useSubkey = true;
325                 encAuthorizationData = new EncryptedData(reqKey, ad,
326                     KeyUsage.KU_TGS_REQ_AUTH_DATA_SUBKEY);
327             } else
328                 encAuthorizationData = new EncryptedData(key, ad,
329                     KeyUsage.KU_TGS_REQ_AUTH_DATA_SESSKEY);
330         }
331 
332         KDCReqBody reqBody = new KDCReqBody(
333                                             kdc_options,
334                                             cname,
335                                             sname,
336                                             from,
337                                             req_till,
338                                             rtime,
339                                             Nonce.value(),
340                                             req_eTypes,
341                                             addresses,
342                                             encAuthorizationData,
343                                             additionalTickets);
344 
345         byte[] temp = reqBody.asn1Encode(Krb5.KRB_TGS_REQ);
346         // if the checksum type is one of the keyed checksum types,
347         // use session key.
348         Checksum cksum;
349         switch (Checksum.CKSUMTYPE_DEFAULT) {
350         case Checksum.CKSUMTYPE_RSA_MD4_DES:
351         case Checksum.CKSUMTYPE_DES_MAC:
352         case Checksum.CKSUMTYPE_DES_MAC_K:
353         case Checksum.CKSUMTYPE_RSA_MD4_DES_K:
354         case Checksum.CKSUMTYPE_RSA_MD5_DES:
355         case Checksum.CKSUMTYPE_HMAC_SHA1_DES3_KD:
356         case Checksum.CKSUMTYPE_HMAC_MD5_ARCFOUR:
357         case Checksum.CKSUMTYPE_HMAC_SHA1_96_AES128:
358         case Checksum.CKSUMTYPE_HMAC_SHA1_96_AES256:
359         case Checksum.CKSUMTYPE_HMAC_SHA256_128_AES128:
360         case Checksum.CKSUMTYPE_HMAC_SHA384_192_AES256:
361             cksum = new Checksum(Checksum.CKSUMTYPE_DEFAULT, temp, key,
362                 KeyUsage.KU_PA_TGS_REQ_CKSUM);
363             break;
364         case Checksum.CKSUMTYPE_CRC32:
365         case Checksum.CKSUMTYPE_RSA_MD4:
366         case Checksum.CKSUMTYPE_RSA_MD5:
367         default:
368             cksum = new Checksum(Checksum.CKSUMTYPE_DEFAULT, temp);
369         }
370 
371         // Usage will be KeyUsage.KU_PA_TGS_REQ_AUTHENTICATOR
372 
373         byte[] tgs_ap_req = new KrbApReq(
374                                          new APOptions(),
375                                          ticket,
376                                          key,
377                                          cname,
378                                          cksum,
379                                          ctime,
380                                          reqKey,
381                                          null,
382                                          null).getMessage();
383 
384         PAData tgsPAData = new PAData(Krb5.PA_TGS_REQ, tgs_ap_req);
385         return new TGSReq(
386                 extraPA != null ?
387                     new PAData[] {extraPA, tgsPAData } :
388                     new PAData[] {tgsPAData},
389                 reqBody);
390     }
391 
getMessage()392     TGSReq getMessage() {
393         return tgsReqMessg;
394     }
395 
getSecondTicket()396     Ticket getSecondTicket() {
397         return secondTicket;
398     }
399 
debug(String message)400     private static void debug(String message) {
401         //      System.err.println(">>> KrbTgsReq: " + message);
402     }
403 
usedSubkey()404     boolean usedSubkey() {
405         return useSubkey;
406     }
407 
408 }
409