Path: blob/master/src/java.security.jgss/share/classes/sun/security/jgss/krb5/ServiceCreds.java
41161 views
/*1* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.jgss.krb5;2627import javax.security.auth.kerberos.KerberosTicket;28import javax.security.auth.kerberos.KerberosKey;29import javax.security.auth.kerberos.KerberosPrincipal;30import javax.security.auth.kerberos.KeyTab;31import javax.security.auth.Subject;3233import sun.security.krb5.Credentials;34import sun.security.krb5.EncryptionKey;35import sun.security.krb5.KrbException;36import java.io.IOException;37import java.util.ArrayList;38import java.util.List;39import java.util.Set;40import sun.security.krb5.*;41import sun.security.krb5.internal.Krb5;4243/**44* Credentials of a kerberos acceptor. A KerberosPrincipal object (kp) is45* the principal. It can be specified as the serverPrincipal argument46* in the getInstance() method, or uses only KerberosPrincipal in the subject.47* Otherwise, the creds object is unbound and kp is null.48*49* The class also encapsulates various secrets, which can be:50*51* 1. Some KerberosKeys (generated from password)52* 2. Some KeyTabs (for a typical service based on keytabs)53* 3. A TGT (for S4U2proxy extension or user2user)54*55* Note that some secrets can coexist. For example, a user2user service56* can use its keytab (or keys) if the client can successfully obtain a57* normal service ticket, or it can use the TGT (actually, the session key58* of the TGT) if the client can only acquire a service ticket59* of ENC-TKT-IN-SKEY style.60*61* @since 1.862*/63public final class ServiceCreds {64// The principal, or null if unbound65private KerberosPrincipal kp;6667// All principals in the subject's princ set68private Set<KerberosPrincipal> allPrincs;6970// All private credentials that can be used71private List<KeyTab> ktabs;72private List<KerberosKey> kk;73private KerberosTicket tgt;7475private boolean destroyed;7677private ServiceCreds() {78// Make sure this class cannot be instantiated externally.79}8081/**82* Creates a ServiceCreds object based on info in a Subject for83* a given principal name (if specified).84* @return the object, or null if there is no private creds for it85*/86public static ServiceCreds getInstance(87Subject subj, String serverPrincipal) {8889ServiceCreds sc = new ServiceCreds();9091sc.allPrincs =92subj.getPrincipals(KerberosPrincipal.class);9394// Compatibility. A key implies its own principal95for (KerberosKey key: SubjectComber.findMany(96subj, serverPrincipal, null, KerberosKey.class)) {97sc.allPrincs.add(key.getPrincipal());98}99100if (serverPrincipal != null) { // A named principal101sc.kp = new KerberosPrincipal(serverPrincipal);102} else {103// For compatibility reason, we set the name of default principal104// to the "only possible" name it can take, which means there is105// only one KerberosPrincipal and there is no unbound keytabs106if (sc.allPrincs.size() == 1) {107boolean hasUnbound = false;108for (KeyTab ktab: SubjectComber.findMany(109subj, null, null, KeyTab.class)) {110if (!ktab.isBound()) {111hasUnbound = true;112break;113}114}115if (!hasUnbound) {116sc.kp = sc.allPrincs.iterator().next();117serverPrincipal = sc.kp.getName();118}119}120}121122sc.ktabs = SubjectComber.findMany(123subj, serverPrincipal, null, KeyTab.class);124sc.kk = SubjectComber.findMany(125subj, serverPrincipal, null, KerberosKey.class);126sc.tgt = SubjectComber.find(127subj, null, serverPrincipal, KerberosTicket.class);128if (sc.ktabs.isEmpty() && sc.kk.isEmpty() && sc.tgt == null) {129return null;130}131132sc.destroyed = false;133134return sc;135}136137// can be null138public String getName() {139if (destroyed) {140throw new IllegalStateException("This object is destroyed");141}142return kp == null ? null : kp.getName();143}144145/**146* Gets keys for "someone". Used in 2 cases:147* 1. By TLS because it needs to get keys before client comes in.148* 2. As a fallback in getEKeys() below.149* This method can still return an empty array.150*/151public KerberosKey[] getKKeys() {152if (destroyed) {153throw new IllegalStateException("This object is destroyed");154}155KerberosPrincipal one = kp; // named principal156if (one == null && !allPrincs.isEmpty()) { // or, a known principal157one = allPrincs.iterator().next();158}159if (one == null) { // Or, some random one160for (KeyTab ktab: ktabs) {161// Must be unbound keytab, otherwise, allPrincs is not empty162PrincipalName pn =163Krb5Util.snapshotFromJavaxKeyTab(ktab).getOneName();164if (pn != null) {165one = new KerberosPrincipal(pn.getName());166break;167}168}169}170if (one != null) {171return getKKeys(one);172} else {173return new KerberosKey[0];174}175}176177/**178* Get kkeys for a principal,179* @param princ the target name initiator requests. Not null.180* @return keys for the princ, never null, might be empty181*/182public KerberosKey[] getKKeys(KerberosPrincipal princ) {183if (destroyed) {184throw new IllegalStateException("This object is destroyed");185}186ArrayList<KerberosKey> keys = new ArrayList<>();187if (kp != null && !princ.equals(kp)) { // named principal188return new KerberosKey[0];189}190for (KerberosKey k: kk) {191if (k.getPrincipal().equals(princ)) {192keys.add(k);193}194}195for (KeyTab ktab: ktabs) {196if (ktab.getPrincipal() == null && ktab.isBound()) {197// legacy bound keytab. although we don't know who198// the bound principal is, it must be in allPrincs199if (!allPrincs.contains(princ)) {200continue; // skip this legacy bound keytab201}202}203for (KerberosKey k: ktab.getKeys(princ)) {204keys.add(k);205}206}207return keys.toArray(new KerberosKey[keys.size()]);208}209210/**211* Gets EKeys for a principal.212* @param princ the target name initiator requests. Not null.213* @return keys for the princ, never null, might be empty214*/215public EncryptionKey[] getEKeys(PrincipalName princ) {216if (destroyed) {217throw new IllegalStateException("This object is destroyed");218}219KerberosKey[] kkeys = getKKeys(new KerberosPrincipal(princ.getName()));220if (kkeys.length == 0) {221// Fallback: old JDK does not perform real name checking. If the222// acceptor has host.sun.com but initiator requests for host,223// as long as their keys match (i.e. keys for one can decrypt224// the other's service ticket), the authentication is OK.225// There are real customers depending on this to use different226// names for a single service.227kkeys = getKKeys();228}229EncryptionKey[] ekeys = new EncryptionKey[kkeys.length];230for (int i=0; i<ekeys.length; i++) {231ekeys[i] = new EncryptionKey(232kkeys[i].getEncoded(), kkeys[i].getKeyType(),233kkeys[i].getVersionNumber());234}235return ekeys;236}237238public Credentials getInitCred() {239if (destroyed) {240throw new IllegalStateException("This object is destroyed");241}242if (tgt == null) {243return null;244}245try {246return Krb5Util.ticketToCreds(tgt);247} catch (KrbException | IOException e) {248return null;249}250}251252public void destroy() {253// Do not wipe out real keys because they are references to the254// priv creds in subject. Just make it useless.255destroyed = true;256kp = null;257ktabs.clear();258kk.clear();259tgt = null;260}261}262263264