/*
 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package sun.security.jgss.krb5;

import javax.security.auth.kerberos.KerberosTicket;
import javax.security.auth.kerberos.KerberosKey;
import javax.security.auth.kerberos.KerberosPrincipal;
import javax.security.auth.kerberos.KeyTab;
import javax.security.auth.Subject;

import sun.security.krb5.Credentials;
import sun.security.krb5.EncryptionKey;
import sun.security.krb5.KrbException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import sun.security.krb5.*;
import sun.security.krb5.internal.Krb5;

Credentials of a kerberos acceptor. A KerberosPrincipal object (kp) is the principal. It can be specified as the serverPrincipal argument in the getInstance() method, or uses only KerberosPrincipal in the subject. Otherwise, the creds object is unbound and kp is null. The class also encapsulates various secrets, which can be: 1. Some KerberosKeys (generated from password) 2. Some KeyTabs (for a typical service based on keytabs) 3. A TGT (for S4U2proxy extension or user2user) Note that some secrets can coexist. For example, a user2user service can use its keytab (or keys) if the client can successfully obtain a normal service ticket, or it can use the TGT (actually, the session key of the TGT) if the client can only acquire a service ticket of ENC-TKT-IN-SKEY style.
Since:1.8
/** * Credentials of a kerberos acceptor. A KerberosPrincipal object (kp) is * the principal. It can be specified as the serverPrincipal argument * in the getInstance() method, or uses only KerberosPrincipal in the subject. * Otherwise, the creds object is unbound and kp is null. * * The class also encapsulates various secrets, which can be: * * 1. Some KerberosKeys (generated from password) * 2. Some KeyTabs (for a typical service based on keytabs) * 3. A TGT (for S4U2proxy extension or user2user) * * Note that some secrets can coexist. For example, a user2user service * can use its keytab (or keys) if the client can successfully obtain a * normal service ticket, or it can use the TGT (actually, the session key * of the TGT) if the client can only acquire a service ticket * of ENC-TKT-IN-SKEY style. * * @since 1.8 */
public final class ServiceCreds { // The principal, or null if unbound private KerberosPrincipal kp; // All principals in the subject's princ set private Set<KerberosPrincipal> allPrincs; // All private credentials that can be used private List<KeyTab> ktabs; private List<KerberosKey> kk; private KerberosTicket tgt; private boolean destroyed; private ServiceCreds() { // Make sure this class cannot be instantiated externally. }
Creates a ServiceCreds object based on info in a Subject for a given principal name (if specified).
Returns:the object, or null if there is no private creds for it
/** * Creates a ServiceCreds object based on info in a Subject for * a given principal name (if specified). * @return the object, or null if there is no private creds for it */
public static ServiceCreds getInstance( Subject subj, String serverPrincipal) { ServiceCreds sc = new ServiceCreds(); sc.allPrincs = subj.getPrincipals(KerberosPrincipal.class); // Compatibility. A key implies its own principal for (KerberosKey key: SubjectComber.findMany( subj, serverPrincipal, null, KerberosKey.class)) { sc.allPrincs.add(key.getPrincipal()); } if (serverPrincipal != null) { // A named principal sc.kp = new KerberosPrincipal(serverPrincipal); } else { // For compatibility reason, we set the name of default principal // to the "only possible" name it can take, which means there is // only one KerberosPrincipal and there is no unbound keytabs if (sc.allPrincs.size() == 1) { boolean hasUnbound = false; for (KeyTab ktab: SubjectComber.findMany( subj, null, null, KeyTab.class)) { if (!ktab.isBound()) { hasUnbound = true; break; } } if (!hasUnbound) { sc.kp = sc.allPrincs.iterator().next(); serverPrincipal = sc.kp.getName(); } } } sc.ktabs = SubjectComber.findMany( subj, serverPrincipal, null, KeyTab.class); sc.kk = SubjectComber.findMany( subj, serverPrincipal, null, KerberosKey.class); sc.tgt = SubjectComber.find( subj, null, serverPrincipal, KerberosTicket.class); if (sc.ktabs.isEmpty() && sc.kk.isEmpty() && sc.tgt == null) { return null; } sc.destroyed = false; return sc; } // can be null public String getName() { if (destroyed) { throw new IllegalStateException("This object is destroyed"); } return kp == null ? null : kp.getName(); }
Gets keys for "someone". Used in 2 cases: 1. By TLS because it needs to get keys before client comes in. 2. As a fallback in getEKeys() below. This method can still return an empty array.
/** * Gets keys for "someone". Used in 2 cases: * 1. By TLS because it needs to get keys before client comes in. * 2. As a fallback in getEKeys() below. * This method can still return an empty array. */
public KerberosKey[] getKKeys() { if (destroyed) { throw new IllegalStateException("This object is destroyed"); } KerberosPrincipal one = kp; // named principal if (one == null && !allPrincs.isEmpty()) { // or, a known principal one = allPrincs.iterator().next(); } if (one == null) { // Or, some random one for (KeyTab ktab: ktabs) { // Must be unbound keytab, otherwise, allPrincs is not empty PrincipalName pn = Krb5Util.snapshotFromJavaxKeyTab(ktab).getOneName(); if (pn != null) { one = new KerberosPrincipal(pn.getName()); break; } } } if (one != null) { return getKKeys(one); } else { return new KerberosKey[0]; } }
Get kkeys for a principal,
Params:
  • princ – the target name initiator requests. Not null.
Returns:keys for the princ, never null, might be empty
/** * Get kkeys for a principal, * @param princ the target name initiator requests. Not null. * @return keys for the princ, never null, might be empty */
public KerberosKey[] getKKeys(KerberosPrincipal princ) { if (destroyed) { throw new IllegalStateException("This object is destroyed"); } ArrayList<KerberosKey> keys = new ArrayList<>(); if (kp != null && !princ.equals(kp)) { // named principal return new KerberosKey[0]; } for (KerberosKey k: kk) { if (k.getPrincipal().equals(princ)) { keys.add(k); } } for (KeyTab ktab: ktabs) { if (ktab.getPrincipal() == null && ktab.isBound()) { // legacy bound keytab. although we don't know who // the bound principal is, it must be in allPrincs if (!allPrincs.contains(princ)) { continue; // skip this legacy bound keytab } } for (KerberosKey k: ktab.getKeys(princ)) { keys.add(k); } } return keys.toArray(new KerberosKey[keys.size()]); }
Gets EKeys for a principal.
Params:
  • princ – the target name initiator requests. Not null.
Returns:keys for the princ, never null, might be empty
/** * Gets EKeys for a principal. * @param princ the target name initiator requests. Not null. * @return keys for the princ, never null, might be empty */
public EncryptionKey[] getEKeys(PrincipalName princ) { if (destroyed) { throw new IllegalStateException("This object is destroyed"); } KerberosKey[] kkeys = getKKeys(new KerberosPrincipal(princ.getName())); if (kkeys.length == 0) { // Fallback: old JDK does not perform real name checking. If the // acceptor has host.sun.com but initiator requests for host, // as long as their keys match (i.e. keys for one can decrypt // the other's service ticket), the authentication is OK. // There are real customers depending on this to use different // names for a single service. kkeys = getKKeys(); } EncryptionKey[] ekeys = new EncryptionKey[kkeys.length]; for (int i=0; i<ekeys.length; i++) { ekeys[i] = new EncryptionKey( kkeys[i].getEncoded(), kkeys[i].getKeyType(), new Integer(kkeys[i].getVersionNumber())); } return ekeys; } public Credentials getInitCred() { if (destroyed) { throw new IllegalStateException("This object is destroyed"); } if (tgt == null) { return null; } try { return Krb5Util.ticketToCreds(tgt); } catch (KrbException | IOException e) { return null; } } public void destroy() { // Do not wipe out real keys because they are references to the // priv creds in subject. Just make it useless. destroyed = true; kp = null; ktabs.clear(); kk.clear(); tgt = null; } }