1/*
2 * Copyright (c) 2000, 2013, 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
32package sun.security.krb5;
33
34import sun.security.krb5.internal.*;
35import sun.security.krb5.internal.ccache.CredentialsCache;
36import sun.security.krb5.internal.crypto.EType;
37import java.io.IOException;
38import java.util.Date;
39import java.util.Locale;
40import java.net.InetAddress;
41
42/**
43 * This class encapsulates the concept of a Kerberos service
44 * credential. That includes a Kerberos ticket and an associated
45 * session key.
46 */
47public class Credentials {
48
49    Ticket ticket;
50    PrincipalName client;
51    PrincipalName server;
52    EncryptionKey key;
53    TicketFlags flags;
54    KerberosTime authTime;
55    KerberosTime startTime;
56    KerberosTime endTime;
57    KerberosTime renewTill;
58    HostAddresses cAddr;
59    EncryptionKey serviceKey;
60    AuthorizationData authzData;
61    private static boolean DEBUG = Krb5.DEBUG;
62    private static CredentialsCache cache;
63    static boolean alreadyLoaded = false;
64    private static boolean alreadyTried = false;
65
66    // Read native ticket with session key type in the given list
67    private static native Credentials acquireDefaultNativeCreds(int[] eTypes);
68
69    public Credentials(Ticket new_ticket,
70                       PrincipalName new_client,
71                       PrincipalName new_server,
72                       EncryptionKey new_key,
73                       TicketFlags new_flags,
74                       KerberosTime authTime,
75                       KerberosTime new_startTime,
76                       KerberosTime new_endTime,
77                       KerberosTime renewTill,
78                       HostAddresses cAddr,
79                       AuthorizationData authzData) {
80        this(new_ticket, new_client, new_server, new_key, new_flags,
81                authTime, new_startTime, new_endTime, renewTill, cAddr);
82        this.authzData = authzData;
83    }
84
85    public Credentials(Ticket new_ticket,
86                       PrincipalName new_client,
87                       PrincipalName new_server,
88                       EncryptionKey new_key,
89                       TicketFlags new_flags,
90                       KerberosTime authTime,
91                       KerberosTime new_startTime,
92                       KerberosTime new_endTime,
93                       KerberosTime renewTill,
94                       HostAddresses cAddr) {
95        ticket = new_ticket;
96        client = new_client;
97        server = new_server;
98        key = new_key;
99        flags = new_flags;
100        this.authTime = authTime;
101        startTime = new_startTime;
102        endTime = new_endTime;
103        this.renewTill = renewTill;
104        this.cAddr = cAddr;
105    }
106
107    public Credentials(byte[] encoding,
108                       String client,
109                       String server,
110                       byte[] keyBytes,
111                       int keyType,
112                       boolean[] flags,
113                       Date authTime,
114                       Date startTime,
115                       Date endTime,
116                       Date renewTill,
117                       InetAddress[] cAddrs) throws KrbException, IOException {
118        this(new Ticket(encoding),
119             new PrincipalName(client, PrincipalName.KRB_NT_PRINCIPAL),
120             new PrincipalName(server, PrincipalName.KRB_NT_SRV_INST),
121             new EncryptionKey(keyType, keyBytes),
122             (flags == null? null: new TicketFlags(flags)),
123             (authTime == null? null: new KerberosTime(authTime)),
124             (startTime == null? null: new KerberosTime(startTime)),
125             (endTime == null? null: new KerberosTime(endTime)),
126             (renewTill == null? null: new KerberosTime(renewTill)),
127             null); // caddrs are in the encoding at this point
128    }
129
130    /**
131     * Acquires a service ticket for the specified service
132     * principal. If the service ticket is not already available, it
133     * obtains a new one from the KDC.
134     */
135    /*
136    public Credentials(Credentials tgt, PrincipalName service)
137        throws KrbException {
138    }
139    */
140
141    public final PrincipalName getClient() {
142        return client;
143    }
144
145    public final PrincipalName getServer() {
146        return server;
147    }
148
149    public final EncryptionKey getSessionKey() {
150        return key;
151    }
152
153    public final Date getAuthTime() {
154        if (authTime != null) {
155            return authTime.toDate();
156        } else {
157            return null;
158        }
159    }
160
161    public final Date getStartTime() {
162        if (startTime != null)
163            {
164                return startTime.toDate();
165            }
166        return null;
167    }
168
169    public final Date getEndTime() {
170        if (endTime != null)
171            {
172                return endTime.toDate();
173            }
174        return null;
175    }
176
177    public final Date getRenewTill() {
178        if (renewTill != null)
179            {
180                return renewTill.toDate();
181            }
182        return null;
183    }
184
185    public final boolean[] getFlags() {
186        if (flags == null) // Can be in a KRB-CRED
187        return null;
188        return flags.toBooleanArray();
189    }
190
191    public final InetAddress[] getClientAddresses() {
192
193        if (cAddr == null)
194        return null;
195
196        return cAddr.getInetAddresses();
197    }
198
199    public final byte[] getEncoded() {
200        byte[] retVal = null;
201        try {
202            retVal = ticket.asn1Encode();
203        } catch (Asn1Exception e) {
204            if (DEBUG)
205            System.out.println(e);
206        } catch (IOException ioe) {
207            if (DEBUG)
208            System.out.println(ioe);
209        }
210        return retVal;
211    }
212
213    public boolean isForwardable() {
214        return flags.get(Krb5.TKT_OPTS_FORWARDABLE);
215    }
216
217    public boolean isRenewable() {
218        return flags.get(Krb5.TKT_OPTS_RENEWABLE);
219    }
220
221    public Ticket getTicket() {
222        return ticket;
223    }
224
225    public TicketFlags getTicketFlags() {
226        return flags;
227    }
228
229    public AuthorizationData getAuthzData() {
230        return authzData;
231    }
232    /**
233     * Checks if the service ticket returned by the KDC has the OK-AS-DELEGATE
234     * flag set
235     * @return true if OK-AS_DELEGATE flag is set, otherwise, return false.
236     */
237    public boolean checkDelegate() {
238        return flags.get(Krb5.TKT_OPTS_DELEGATE);
239    }
240
241    /**
242     * Reset TKT_OPTS_DELEGATE to false, called at credentials acquirement
243     * when one of the cross-realm TGTs does not have the OK-AS-DELEGATE
244     * flag set. This info must be preservable and restorable through
245     * the Krb5Util.credsToTicket/ticketToCreds() methods so that even if
246     * the service ticket is cached it still remembers the cross-realm
247     * authentication result.
248     */
249    public void resetDelegate() {
250        flags.set(Krb5.TKT_OPTS_DELEGATE, false);
251    }
252
253    public Credentials renew() throws KrbException, IOException {
254        KDCOptions options = new KDCOptions();
255        options.set(KDCOptions.RENEW, true);
256        /*
257         * Added here to pass KrbKdcRep.check:73
258         */
259        options.set(KDCOptions.RENEWABLE, true);
260
261        return new KrbTgsReq(options,
262                             this,
263                             server,
264                             null, // from
265                             null, // till
266                             null, // rtime
267                             null, // eTypes
268                             cAddr,
269                             null,
270                             null,
271                             null).sendAndGetCreds();
272    }
273
274    /**
275     * Returns a TGT for the given client principal from a ticket cache.
276     *
277     * @param princ the client principal. A value of null means that the
278     * default principal name in the credentials cache will be used.
279     * @param ticketCache the path to the tickets file. A value
280     * of null will be accepted to indicate that the default
281     * path should be searched
282     * @return the TGT credentials or null if none were found. If the tgt
283     * expired, it is the responsibility of the caller to determine this.
284     */
285    public static Credentials acquireTGTFromCache(PrincipalName princ,
286                                                  String ticketCache)
287        throws KrbException, IOException {
288
289        if (ticketCache == null) {
290            // The default ticket cache on Windows and Mac is not a file.
291            String os = java.security.AccessController.doPrivileged(
292                        new sun.security.action.GetPropertyAction("os.name"));
293            if (os.toUpperCase(Locale.ENGLISH).startsWith("WINDOWS") ||
294                    os.toUpperCase(Locale.ENGLISH).contains("OS X")) {
295                Credentials creds = acquireDefaultCreds();
296                if (creds == null) {
297                    if (DEBUG) {
298                        System.out.println(">>> Found no TGT's in LSA");
299                    }
300                    return null;
301                }
302                if (princ != null) {
303                    if (creds.getClient().equals(princ)) {
304                        if (DEBUG) {
305                            System.out.println(">>> Obtained TGT from LSA: "
306                                               + creds);
307                        }
308                        return creds;
309                    } else {
310                        if (DEBUG) {
311                            System.out.println(">>> LSA contains TGT for "
312                                               + creds.getClient()
313                                               + " not "
314                                               + princ);
315                        }
316                        return null;
317                    }
318                } else {
319                    if (DEBUG) {
320                        System.out.println(">>> Obtained TGT from LSA: "
321                                           + creds);
322                    }
323                    return creds;
324                }
325            }
326        }
327
328        /*
329         * Returns the appropriate cache. If ticketCache is null, it is the
330         * default cache otherwise it is the cache filename contained in it.
331         */
332        CredentialsCache ccache =
333            CredentialsCache.getInstance(princ, ticketCache);
334
335        if (ccache == null) {
336            return null;
337        }
338
339        sun.security.krb5.internal.ccache.Credentials tgtCred  =
340            ccache.getDefaultCreds();
341
342        if (tgtCred == null) {
343            return null;
344        }
345
346        if (EType.isSupported(tgtCred.getEType())) {
347            return tgtCred.setKrbCreds();
348        } else {
349            if (DEBUG) {
350                System.out.println(
351                    ">>> unsupported key type found the default TGT: " +
352                    tgtCred.getEType());
353            }
354            return null;
355        }
356    }
357
358    /**
359     * Acquires default credentials.
360     * <br>The possible locations for default credentials cache is searched in
361     * the following order:
362     * <ol>
363     * <li> The directory and cache file name specified by "KRB5CCNAME" system.
364     * property.
365     * <li> The directory and cache file name specified by "KRB5CCNAME"
366     * environment variable.
367     * <li> A cache file named krb5cc_{user.name} at {user.home} directory.
368     * </ol>
369     * @return a <code>KrbCreds</code> object if the credential is found,
370     * otherwise return null.
371     */
372
373    // this method is intentionally changed to not check if the caller's
374    // principal name matches cache file's principal name.
375    // It assumes that the GSS call has
376    // the privilege to access the default cache file.
377
378    // This method is only called on Windows and Mac OS X, the native
379    // acquireDefaultNativeCreds is also available on these platforms.
380    public static synchronized Credentials acquireDefaultCreds() {
381        Credentials result = null;
382
383        if (cache == null) {
384            cache = CredentialsCache.getInstance();
385        }
386        if (cache != null) {
387            sun.security.krb5.internal.ccache.Credentials temp =
388                cache.getDefaultCreds();
389            if (temp != null) {
390                if (DEBUG) {
391                    System.out.println(">>> KrbCreds found the default ticket"
392                            + " granting ticket in credential cache.");
393                }
394                if (EType.isSupported(temp.getEType())) {
395                    result = temp.setKrbCreds();
396                } else {
397                    if (DEBUG) {
398                        System.out.println(
399                            ">>> unsupported key type found the default TGT: " +
400                            temp.getEType());
401                    }
402                }
403            }
404        }
405        if (result == null) {
406            // Doesn't seem to be a default cache on this system or
407            // TGT has unsupported encryption type
408
409            if (!alreadyTried) {
410                // See if there's any native code to load
411                try {
412                    ensureLoaded();
413                } catch (Exception e) {
414                    if (DEBUG) {
415                        System.out.println("Can not load credentials cache");
416                        e.printStackTrace();
417                    }
418                    alreadyTried = true;
419                }
420            }
421            if (alreadyLoaded) {
422                // There is some native code
423                if (DEBUG) {
424                    System.out.println(">> Acquire default native Credentials");
425                }
426                try {
427                    result = acquireDefaultNativeCreds(
428                            EType.getDefaults("default_tkt_enctypes"));
429                } catch (KrbException ke) {
430                    // when there is no default_tkt_enctypes.
431                }
432            }
433        }
434        return result;
435    }
436
437    /**
438     * Acquires credentials for a specified service using initial credential.
439     * When the service has a different realm
440     * from the initial credential, we do cross-realm authentication
441     * - first, we use the current credential to get
442     * a cross-realm credential from the local KDC, then use that
443     * cross-realm credential to request service credential
444     * from the foreigh KDC.
445     *
446     * @param service the name of service principal using format
447     * components@realm
448     * @param ccreds client's initial credential.
449     * @exception IOException if an error occurs in reading the credentials
450     * cache
451     * @exception KrbException if an error occurs specific to Kerberos
452     * @return a <code>Credentials</code> object.
453     */
454
455    public static Credentials acquireServiceCreds(String service,
456                                                  Credentials ccreds)
457        throws KrbException, IOException {
458        return CredentialsUtil.acquireServiceCreds(service, ccreds);
459    }
460
461    public static Credentials acquireS4U2selfCreds(PrincipalName user,
462            Credentials ccreds) throws KrbException, IOException {
463        return CredentialsUtil.acquireS4U2selfCreds(user, ccreds);
464    }
465
466    public static Credentials acquireS4U2proxyCreds(String service,
467            Ticket second, PrincipalName client, Credentials ccreds)
468        throws KrbException, IOException {
469        return CredentialsUtil.acquireS4U2proxyCreds(
470                service, second, client, ccreds);
471    }
472
473    public CredentialsCache getCache() {
474        return cache;
475    }
476
477    public EncryptionKey getServiceKey() {
478        return serviceKey;
479    }
480
481    /*
482     * Prints out debug info.
483     */
484    public static void printDebug(Credentials c) {
485        System.out.println(">>> DEBUG: ----Credentials----");
486        System.out.println("\tclient: " + c.client.toString());
487        System.out.println("\tserver: " + c.server.toString());
488        System.out.println("\tticket: sname: " + c.ticket.sname.toString());
489        if (c.startTime != null) {
490            System.out.println("\tstartTime: " + c.startTime.getTime());
491        }
492        System.out.println("\tendTime: " + c.endTime.getTime());
493        System.out.println("        ----Credentials end----");
494    }
495
496
497    static void ensureLoaded() {
498        java.security.AccessController.doPrivileged(
499                new java.security.PrivilegedAction<Void> () {
500                        public Void run() {
501                                if (System.getProperty("os.name").contains("OS X")) {
502                                    System.loadLibrary("osxkrb5");
503                                } else {
504                                    System.loadLibrary("w2k_lsa_auth");
505                                }
506                                return null;
507                        }
508                });
509        alreadyLoaded = true;
510    }
511
512    public String toString() {
513        StringBuilder sb = new StringBuilder("Credentials:");
514        sb.append(    "\n      client=").append(client);
515        sb.append(    "\n      server=").append(server);
516        if (authTime != null) {
517            sb.append("\n    authTime=").append(authTime);
518        }
519        if (startTime != null) {
520            sb.append("\n   startTime=").append(startTime);
521        }
522        sb.append(    "\n     endTime=").append(endTime);
523        sb.append(    "\n   renewTill=").append(renewTill);
524        sb.append(    "\n       flags=").append(flags);
525        sb.append(    "\nEType (skey)=").append(key.getEType());
526        sb.append(    "\n   (tkt key)=").append(ticket.encPart.eType);
527        return sb.toString();
528    }
529
530    public sun.security.krb5.internal.ccache.Credentials toCCacheCreds() {
531        return new sun.security.krb5.internal.ccache.Credentials(
532                getClient(), getServer(),
533                getSessionKey(),
534                date2kt(getAuthTime()),
535                date2kt(getStartTime()),
536                date2kt(getEndTime()),
537                date2kt(getRenewTill()),
538                false,
539                flags,
540                new HostAddresses(getClientAddresses()),
541                getAuthzData(),
542                getTicket(),
543                null);
544    }
545
546    private static KerberosTime date2kt(Date d) {
547        return d == null ? null : new KerberosTime(d);
548    }
549}
550