Path: blob/master/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KeyTab.java
41161 views
/*1* Copyright (c) 2011, 2021, 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 javax.security.auth.kerberos;2627import java.io.File;28import java.security.AccessControlException;29import java.util.Objects;30import sun.security.krb5.EncryptionKey;31import sun.security.krb5.KerberosSecrets;32import sun.security.krb5.PrincipalName;33import sun.security.krb5.RealmException;3435/**36* This class encapsulates a keytab file.37* <p>38* A Kerberos JAAS login module that obtains long term secret keys from a39* keytab file should use this class. The login module will store40* an instance of this class in the private credential set of a41* {@link javax.security.auth.Subject Subject} during the commit phase of the42* authentication process.43* <p>44* If a {@code KeyTab} object is obtained from {@link #getUnboundInstance()}45* or {@link #getUnboundInstance(java.io.File)}, it is unbound and thus can be46* used by any service principal. Otherwise, if it's obtained from47* {@link #getInstance(KerberosPrincipal)} or48* {@link #getInstance(KerberosPrincipal, java.io.File)}, it is bound to the49* specific service principal and can only be used by it.50* <p>51* Please note the constructors {@link #getInstance()} and52* {@link #getInstance(java.io.File)} were created when there was no support53* for unbound keytabs. These methods should not be used anymore. An object54* created with either of these methods are considered to be bound to an55* unknown principal, which means, its {@link #isBound()} returns true and56* {@link #getPrincipal()} returns null.57* <p>58* It might be necessary for the application to be granted a59* {@link javax.security.auth.PrivateCredentialPermission60* PrivateCredentialPermission} if it needs to access the {@code KeyTab}61* instance from a {@code Subject}. This permission is not needed when the62* application depends on the default JGSS Kerberos mechanism to access the63* {@code KeyTab}. In that case, however, the application will need an appropriate64* {@link javax.security.auth.kerberos.ServicePermission ServicePermission}.65* <p>66* The keytab file format is described at67* <a href="http://www.ioplex.com/utilities/keytab.txt">68* http://www.ioplex.com/utilities/keytab.txt</a>.69*70* @since 1.771*/72public final class KeyTab {7374/*75* Impl notes:76*77* This class is only a name, a permanent link to the keytab source78* (can be missing). Itself has no content. In order to read content,79* take a snapshot and read from it.80*81* The snapshot is of type sun.security.krb5.internal.ktab.KeyTab, which82* contains the content of the keytab file when the snapshot is taken.83* Itself has no refresh function and mostly an immutable class (except84* for the create/add/save methods only used by the ktab command).85*/8687// Source, null if using the default one. Note that the default name88// is maintained in snapshot, this field is never "resolved".89private final File file;9091// Bound user: normally from the "principal" value in a JAAS krb592// login conf. Will be null if it's "*".93private final KerberosPrincipal princ;9495private final boolean bound;9697// Set up JavaxSecurityAuthKerberosAccess in KerberosSecrets98static {99KerberosSecrets.setJavaxSecurityAuthKerberosAccess(100new JavaxSecurityAuthKerberosAccessImpl());101}102103private KeyTab(KerberosPrincipal princ, File file, boolean bound) {104this.princ = princ;105this.file = file;106this.bound = bound;107}108109/**110* Returns a {@code KeyTab} instance from a {@code File} object111* that is bound to an unknown service principal.112* <p>113* The result of this method is never null. This method only associates114* the returned {@code KeyTab} object with the file and does not read it.115* <p>116* Developers should call {@link #getInstance(KerberosPrincipal,File)}117* when the bound service principal is known.118* @param file the keytab {@code File} object, must not be null119* @return the keytab instance120* @throws NullPointerException if the {@code file} argument is null121*/122public static KeyTab getInstance(File file) {123if (file == null) {124throw new NullPointerException("file must be non null");125}126return new KeyTab(null, file, true);127}128129/**130* Returns an unbound {@code KeyTab} instance from a {@code File}131* object.132* <p>133* The result of this method is never null. This method only associates134* the returned {@code KeyTab} object with the file and does not read it.135* @param file the keytab {@code File} object, must not be null136* @return the keytab instance137* @throws NullPointerException if the file argument is null138* @since 1.8139*/140public static KeyTab getUnboundInstance(File file) {141if (file == null) {142throw new NullPointerException("file must be non null");143}144return new KeyTab(null, file, false);145}146147/**148* Returns a {@code KeyTab} instance from a {@code File} object149* that is bound to the specified service principal.150* <p>151* The result of this method is never null. This method only associates152* the returned {@code KeyTab} object with the file and does not read it.153* @param princ the bound service principal, must not be null154* @param file the keytab {@code File} object, must not be null155* @return the keytab instance156* @throws NullPointerException if either of the arguments is null157* @since 1.8158*/159public static KeyTab getInstance(KerberosPrincipal princ, File file) {160if (princ == null) {161throw new NullPointerException("princ must be non null");162}163if (file == null) {164throw new NullPointerException("file must be non null");165}166return new KeyTab(princ, file, true);167}168169/**170* Returns the default {@code KeyTab} instance that is bound171* to an unknown service principal.172* <p>173* The result of this method is never null. This method only associates174* the returned {@code KeyTab} object with the default keytab file and175* does not read it.176* <p>177* Developers should call {@link #getInstance(KerberosPrincipal)}178* when the bound service principal is known.179* @return the default keytab instance.180*/181public static KeyTab getInstance() {182return new KeyTab(null, null, true);183}184185/**186* Returns the default unbound {@code KeyTab} instance.187* <p>188* The result of this method is never null. This method only associates189* the returned {@code KeyTab} object with the default keytab file and190* does not read it.191* @return the default keytab instance192* @since 1.8193*/194public static KeyTab getUnboundInstance() {195return new KeyTab(null, null, false);196}197198/**199* Returns the default {@code KeyTab} instance that is bound200* to the specified service principal.201* <p>202* The result of this method is never null. This method only associates203* the returned {@code KeyTab} object with the default keytab file and204* does not read it.205* @param princ the bound service principal, must not be null206* @return the default keytab instance207* @throws NullPointerException if {@code princ} is null208* @since 1.8209*/210public static KeyTab getInstance(KerberosPrincipal princ) {211if (princ == null) {212throw new NullPointerException("princ must be non null");213}214return new KeyTab(princ, null, true);215}216217// Takes a snapshot of the keytab content. This method is called by218// JavaxSecurityAuthKerberosAccessImpl so no more private219sun.security.krb5.internal.ktab.KeyTab takeSnapshot() {220try {221return sun.security.krb5.internal.ktab.KeyTab.getInstance(file);222} catch (@SuppressWarnings("removal") AccessControlException ace) {223if (file != null) {224// It's OK to show the name if caller specified it225throw ace;226} else {227@SuppressWarnings("removal")228AccessControlException ace2 = new AccessControlException(229"Access to default keytab denied (modified exception)");230ace2.setStackTrace(ace.getStackTrace());231throw ace2;232}233}234}235236/**237* Returns fresh keys for the given Kerberos principal.238* <p>239* Implementation of this method should make sure the returned keys match240* the latest content of the keytab file. The result is a newly created241* copy that can be modified by the caller without modifying the keytab242* object. The caller should {@link KerberosKey#destroy() destroy} the243* result keys after they are used.244* <p>245* Please note that the keytab file can be created after the246* {@code KeyTab} object is instantiated and its content may change over247* time. Therefore, an application should call this method only when it248* needs to use the keys. Any previous result from an earlier invocation249* could potentially be expired.250* <p>251* If there is any error (say, I/O error or format error)252* during the reading process of the keytab file, a saved result should be253* returned. If there is no saved result (say, this is the first time this254* method is called, or, all previous read attempts failed), an empty array255* should be returned. This can make sure the result is not drastically256* changed during the (probably slow) update of the keytab file.257* <p>258* Each time this method is called and the reading of the file succeeds259* with no exception (say, I/O error or file format error),260* the result should be saved for {@code principal}. The implementation can261* also save keys for other principals having keys in the same keytab object262* if convenient.263* <p>264* Any unsupported key read from the keytab is ignored and not included265* in the result.266* <p>267* If this keytab is bound to a specific principal, calling this method on268* another principal will return an empty array.269*270* @param principal the Kerberos principal, must not be null.271* @return the keys (never null, may be empty)272* @throws NullPointerException if the {@code principal}273* argument is null274* @throws SecurityException if a security manager exists and the read275* access to the keytab file is not permitted276*/277public KerberosKey[] getKeys(KerberosPrincipal principal) {278try {279if (princ != null && !principal.equals(princ)) {280return new KerberosKey[0];281}282PrincipalName pn = new PrincipalName(principal.getName());283EncryptionKey[] keys = takeSnapshot().readServiceKeys(pn);284KerberosKey[] kks = new KerberosKey[keys.length];285for (int i=0; i<kks.length; i++) {286Integer tmp = keys[i].getKeyVersionNumber();287kks[i] = new KerberosKey(288principal,289keys[i].getBytes(),290keys[i].getEType(),291tmp == null ? 0 : tmp.intValue());292keys[i].destroy();293}294return kks;295} catch (RealmException re) {296return new KerberosKey[0];297}298}299300EncryptionKey[] getEncryptionKeys(PrincipalName principal) {301return takeSnapshot().readServiceKeys(principal);302}303304/**305* Checks if the keytab file exists. Implementation of this method306* should make sure that the result matches the latest status of the307* keytab file.308*309* @return true if the keytab file exists; false otherwise.310* @throws SecurityException if a security manager exists and the read311* access to the keytab file is not permitted312*/313public boolean exists() {314return !takeSnapshot().isMissing();315}316317/**318* Returns an informative textual representation of this {@code KeyTab}.319*320* @return an informative textual representation of this {@code KeyTab}.321*/322public String toString() {323String s = (file == null) ? "Default keytab" : file.toString();324if (!bound) return s;325else if (princ == null) return s + " for someone";326else return s + " for " + princ;327}328329/**330* Returns a hash code for this {@code KeyTab}.331*332* @return a hash code for this {@code KeyTab}.333*/334public int hashCode() {335return Objects.hash(file, princ, bound);336}337338/**339* Compares the specified object with this {@code KeyTab} for equality.340* Returns true if the given object is also a341* {@code KeyTab} and the two342* {@code KeyTab} instances are equivalent.343*344* @param other the object to compare to345* @return true if the specified object is equal to this {@code KeyTab}346*/347public boolean equals(Object other) {348if (other == this)349return true;350351if (! (other instanceof KeyTab)) {352return false;353}354355KeyTab otherKtab = (KeyTab) other;356return Objects.equals(otherKtab.princ, princ) &&357Objects.equals(otherKtab.file, file) &&358bound == otherKtab.bound;359}360361/**362* Returns the service principal this {@code KeyTab} object363* is bound to. Returns {@code null} if it's not bound.364* <p>365* Please note the deprecated constructors create a {@code KeyTab} object366* bound for some unknown principal. In this case, this method also returns367* null. User can call {@link #isBound()} to verify this case.368* @return the service principal369* @since 1.8370*/371public KerberosPrincipal getPrincipal() {372return princ;373}374375/**376* Returns if the keytab is bound to a principal377* @return if the keytab is bound to a principal378* @since 1.8379*/380public boolean isBound() {381return bound;382}383}384385386