提交 c35ebc1a 编写于 作者: W weijun

6355584: Introduce constrained Kerberos delegation

Reviewed-by: valeriep
上级 b1018cd9
/*
* Copyright (c) 2012, 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 com.sun.security.jgss;
import org.ietf.jgss.*;
/**
* The extended GSSCredential interface for supporting additional
* functionalities not defined by {@code org.ietf.jgss.GSSCredential}.
* @since 1.8
*/
public interface ExtendedGSSCredential extends GSSCredential {
/**
* Impersonates a principal. In Kerberos, this can be implemented
* using the Microsoft S4U2self extension.
* <p>
* A {@link GSSException#NO_CRED GSSException.NO_CRED} will be thrown if the
* impersonation fails. A {@link GSSException#FAILURE GSSException.FAILURE}
* will be thrown if the impersonation method is not available to this
* credential object.
* @param name the name of the principal to impersonate
* @return a credential for that principal
* @throws GSSException containing the following
* major error codes:
* {@link GSSException#NO_CRED GSSException.NO_CRED}
* {@link GSSException#FAILURE GSSException.FAILURE}
*/
public GSSCredential impersonate(GSSName name) throws GSSException;
}
......@@ -31,10 +31,19 @@ package sun.security.jgss;
* different callers.
*/
public class GSSCaller {
public static final GSSCaller CALLER_UNKNOWN = new GSSCaller();
public static final GSSCaller CALLER_INITIATE = new GSSCaller();
public static final GSSCaller CALLER_ACCEPT = new GSSCaller();
public static final GSSCaller CALLER_SSL_CLIENT = new GSSCaller();
public static final GSSCaller CALLER_SSL_SERVER = new GSSCaller();
public static final GSSCaller CALLER_UNKNOWN = new GSSCaller("UNKNOWN");
public static final GSSCaller CALLER_INITIATE = new GSSCaller("INITIATE");
public static final GSSCaller CALLER_ACCEPT = new GSSCaller("ACCEPT");
public static final GSSCaller CALLER_SSL_CLIENT = new GSSCaller("SSL_CLIENT");
public static final GSSCaller CALLER_SSL_SERVER = new GSSCaller("SSL_SERVER");
private String name;
GSSCaller(String s) {
name = s;
}
@Override
public String toString() {
return "GSSCaller{" + name + '}';
}
}
......@@ -28,8 +28,9 @@ package sun.security.jgss;
import org.ietf.jgss.*;
import sun.security.jgss.spi.*;
import java.util.*;
import com.sun.security.jgss.*;
public class GSSCredentialImpl implements GSSCredential {
public class GSSCredentialImpl implements ExtendedGSSCredential {
private GSSManagerImpl gssManager = null;
private boolean destroyed = false;
......@@ -122,6 +123,19 @@ public class GSSCredentialImpl implements GSSCredential {
}
}
public GSSCredential impersonate(GSSName name) throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
"no longer valid");
}
Oid mech = tempCred.getMechanism();
GSSNameSpi nameElement = (name == null ? null :
((GSSNameImpl)name).getElement(mech));
GSSCredentialSpi cred = tempCred.impersonate(nameElement);
return (cred == null ?
null : new GSSCredentialImpl(gssManager, cred));
}
public GSSName getName() throws GSSException {
if (destroyed) {
throw new IllegalStateException("This credential is " +
......
......@@ -35,6 +35,7 @@ public class HttpCaller extends GSSCaller {
final private HttpCallerInfo hci;
public HttpCaller(HttpCallerInfo hci) {
super("HTTP_CLIENT");
this.hci = hci;
}
......
......@@ -25,6 +25,7 @@
package sun.security.jgss.krb5;
import java.io.IOException;
import org.ietf.jgss.*;
import sun.security.jgss.GSSCaller;
import sun.security.jgss.spi.*;
......@@ -177,4 +178,21 @@ public class Krb5AcceptCredential
public void destroy() throws DestroyFailedException {
screds.destroy();
}
/**
* Impersonation is only available on the initiator side. The
* service must starts as an initiator to get an initial TGT to complete
* the S4U2self protocol.
*/
@Override
public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException {
Credentials cred = screds.getInitCred();
if (cred != null) {
return Krb5InitCredential.getInstance(this.name, cred)
.impersonate(name);
} else {
throw new GSSException(GSSException.FAILURE, -1,
"Only an initiate credentials can impersonate");
}
}
}
......@@ -45,6 +45,7 @@ import java.security.PrivilegedActionException;
import javax.crypto.Cipher;
import javax.security.auth.Subject;
import javax.security.auth.kerberos.*;
import sun.security.krb5.internal.Ticket;
/**
* Implements the mechanism specific context class for the Kerberos v5
......@@ -76,7 +77,7 @@ class Krb5Context implements GSSContextSpi {
* values.
*/
private boolean credDelegState = false;
private boolean credDelegState = false; // now only useful at client
private boolean mutualAuthState = true;
private boolean replayDetState = true;
private boolean sequenceDetState = true;
......@@ -84,6 +85,8 @@ class Krb5Context implements GSSContextSpi {
private boolean integState = true;
private boolean delegPolicyState = false;
private boolean isConstrainedDelegationTried = false;
private int mySeqNumber;
private int peerSeqNumber;
private int keySrc;
......@@ -113,13 +116,11 @@ class Krb5Context implements GSSContextSpi {
private Krb5CredElement myCred;
private Krb5CredElement delegatedCred; // Set only on acceptor side
/* DESCipher instance used by the corresponding GSSContext */
private Cipher desCipher = null;
// XXX See if the required info from these can be extracted and
// stored elsewhere
private Credentials serviceCreds;
private KrbApReq apReq;
Ticket serviceTicket;
final private GSSCaller caller;
private static final boolean DEBUG = Krb5Util.DEBUG;
......@@ -248,7 +249,14 @@ class Krb5Context implements GSSContextSpi {
* Is credential delegation enabled?
*/
public final boolean getCredDelegState() {
return credDelegState;
if (isInitiator()) {
return credDelegState;
} else {
// Server side deleg state is not flagged by credDelegState.
// It can use constrained delegation.
tryConstrainedDelegation();
return delegatedCred != null;
}
}
/**
......@@ -498,7 +506,8 @@ class Krb5Context implements GSSContextSpi {
* Returns the delegated credential for the context. This
* is an optional feature of contexts which not all
* mechanisms will support. A context can be requested to
* support credential delegation by using the <b>CRED_DELEG</b>.
* support credential delegation by using the <b>CRED_DELEG</b>,
* or it can request for a constrained delegation.
* This is only valid on the acceptor side of the context.
* @return GSSCredentialSpi object for the delegated credential
* @exception GSSException
......@@ -507,11 +516,41 @@ class Krb5Context implements GSSContextSpi {
public final GSSCredentialSpi getDelegCred() throws GSSException {
if (state != STATE_IN_PROCESS && state != STATE_DONE)
throw new GSSException(GSSException.NO_CONTEXT);
if (delegatedCred == null)
if (isInitiator()) {
throw new GSSException(GSSException.NO_CRED);
}
tryConstrainedDelegation();
if (delegatedCred == null) {
throw new GSSException(GSSException.NO_CRED);
}
return delegatedCred;
}
private void tryConstrainedDelegation() {
if (state != STATE_IN_PROCESS && state != STATE_DONE) {
return;
}
// We will only try constrained delegation once (if necessary).
if (!isConstrainedDelegationTried) {
if (delegatedCred == null) {
if (DEBUG) {
System.out.println(">>> Constrained deleg from " + caller);
}
// The constrained delegation part. The acceptor needs to have
// isInitiator=true in order to get a TGT, either earlier at
// logon stage, if useSubjectCredsOnly, or now.
try {
delegatedCred = new Krb5ProxyCredential(
Krb5InitCredential.getInstance(
GSSCaller.CALLER_ACCEPT, myName, lifetime),
peerName, serviceTicket);
} catch (GSSException gsse) {
// OK, delegatedCred is null then
}
}
isConstrainedDelegationTried = true;
}
}
/**
* Tests if this is the initiator side of the context.
*
......@@ -577,8 +616,15 @@ class Krb5Context implements GSSContextSpi {
"No TGT available");
}
myName = (Krb5NameElement) myCred.getName();
Credentials tgt =
((Krb5InitCredential) myCred).getKrb5Credentials();
Credentials tgt;
final Krb5ProxyCredential second;
if (myCred instanceof Krb5InitCredential) {
second = null;
tgt = ((Krb5InitCredential) myCred).getKrb5Credentials();
} else {
second = (Krb5ProxyCredential) myCred;
tgt = second.self.getKrb5Credentials();
}
checkPermission(peerName.getKrb5PrincipalName().getName(),
"initiate");
......@@ -607,7 +653,9 @@ class Krb5Context implements GSSContextSpi {
GSSCaller.CALLER_UNKNOWN,
// since it's useSubjectCredsOnly here,
// don't worry about the null
myName.getKrb5PrincipalName().getName(),
second == null ?
myName.getKrb5PrincipalName().getName():
second.getName().getKrb5PrincipalName().getName(),
peerName.getKrb5PrincipalName().getName(),
acc);
}});
......@@ -638,9 +686,17 @@ class Krb5Context implements GSSContextSpi {
"the subject");
}
// Get Service ticket using the Kerberos protocols
serviceCreds = Credentials.acquireServiceCreds(
if (second == null) {
serviceCreds = Credentials.acquireServiceCreds(
peerName.getKrb5PrincipalName().getName(),
tgt);
} else {
serviceCreds = Credentials.acquireS4U2proxyCreds(
peerName.getKrb5PrincipalName().getName(),
second.tkt,
second.getName().getKrb5PrincipalName(),
tgt);
}
if (GSSUtil.useSubjectCredsOnly(caller)) {
final Subject subject =
AccessController.doPrivileged(
......@@ -776,6 +832,7 @@ class Krb5Context implements GSSContextSpi {
retVal = new AcceptSecContextToken(this,
token.getKrbApReq()).encode();
}
serviceTicket = token.getKrbApReq().getCreds().getTicket();
myCred = null;
state = STATE_DONE;
} else {
......@@ -802,8 +859,6 @@ class Krb5Context implements GSSContextSpi {
return retVal;
}
/**
* Queries the context for largest data size to accomodate
* the specified protection and be <= maxTokSize.
......
......@@ -309,8 +309,7 @@ public class Krb5InitCredential
int initLifetime)
throws GSSException {
String realm = null;
final String clientPrincipal, tgsPrincipal = null;
final String clientPrincipal;
/*
* Find the TGT for the realm that the client is in. If the client
......@@ -318,20 +317,8 @@ public class Krb5InitCredential
*/
if (name != null) {
clientPrincipal = (name.getKrb5PrincipalName()).getName();
realm = (name.getKrb5PrincipalName()).getRealmAsString();
} else {
clientPrincipal = null;
try {
Config config = Config.getInstance();
realm = config.getDefaultRealm();
} catch (KrbException e) {
GSSException ge =
new GSSException(GSSException.NO_CRED, -1,
"Attempt to obtain INITIATE credentials failed!" +
" (" + e.getMessage() + ")");
ge.initCause(e);
throw ge;
}
}
final AccessControlContext acc = AccessController.getContext();
......@@ -343,9 +330,11 @@ public class Krb5InitCredential
return AccessController.doPrivileged(
new PrivilegedExceptionAction<KerberosTicket>() {
public KerberosTicket run() throws Exception {
// It's OK to use null as serverPrincipal. TGT is almost
// the first ticket for a principal and we use list.
return Krb5Util.getTicket(
realCaller,
clientPrincipal, tgsPrincipal, acc);
clientPrincipal, null, acc);
}});
} catch (PrivilegedActionException e) {
GSSException ge =
......@@ -356,4 +345,20 @@ public class Krb5InitCredential
throw ge;
}
}
@Override
public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException {
try {
Krb5NameElement kname = (Krb5NameElement)name;
Credentials newCred = Credentials.acquireS4U2selfCreds(
kname.getKrb5PrincipalName(), krb5Credentials);
return new Krb5ProxyCredential(this, kname, newCred.getTicket());
} catch (IOException | KrbException ke) {
GSSException ge =
new GSSException(GSSException.FAILURE, -1,
"Attempt to obtain S4U2self credentials failed!");
ge.initCause(ke);
throw ge;
}
}
}
/*
* Copyright (c) 2012, 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 org.ietf.jgss.*;
import sun.security.jgss.spi.*;
import java.util.Date;
import sun.security.krb5.internal.Ticket;
/**
* Implements the krb5 proxy credential element used in constrained
* delegation. It is used in both impersonation (where there is no Kerberos 5
* communication between the middle server and the client) and normal
* constrained delegation (where there is, but client has not called
* requestCredDeleg(true)).
* @since 1.8
*/
public class Krb5ProxyCredential
implements Krb5CredElement {
public final Krb5InitCredential self; // the middle server
private final Krb5NameElement client; // the client
// The ticket with cname=client and sname=self. This can be a normal
// service ticket or an S4U2self ticket.
public final Ticket tkt;
Krb5ProxyCredential(Krb5InitCredential self, Krb5NameElement client,
Ticket tkt) {
this.self = self;
this.tkt = tkt;
this.client = client;
}
// The client name behind the proxy
@Override
public final Krb5NameElement getName() throws GSSException {
return client;
}
@Override
public int getInitLifetime() throws GSSException {
// endTime of tkt is not used by KDC, and it's also not
// available in the case of kerberos constr deleg
return self.getInitLifetime();
}
@Override
public int getAcceptLifetime() throws GSSException {
return 0;
}
@Override
public boolean isInitiatorCredential() throws GSSException {
return true;
}
@Override
public boolean isAcceptorCredential() throws GSSException {
return false;
}
@Override
public final Oid getMechanism() {
return Krb5MechFactory.GSS_KRB5_MECH_OID;
}
@Override
public final java.security.Provider getProvider() {
return Krb5MechFactory.PROVIDER;
}
@Override
public void dispose() throws GSSException {
try {
self.destroy();
} catch (javax.security.auth.DestroyFailedException e) {
GSSException gssException =
new GSSException(GSSException.FAILURE, -1,
"Could not destroy credentials - " + e.getMessage());
gssException.initCause(e);
}
}
@Override
public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException {
// Cannot impersonate multiple levels without the impersonatee's TGT.
throw new GSSException(GSSException.FAILURE, -1,
"Only an initiate credentials can impersonate");
}
}
......@@ -206,7 +206,7 @@ public class Krb5Util {
* identity, which can be:
* 1. Some KerberosKeys (generated from password)
* 2. A KeyTab (for a typical service)
* 3. A TGT (for a user2user service. Not supported yet)
* 3. A TGT (for S4U2proxy extension)
*
* Note that some creds can coexist. For example, a user2user service
* can use its keytab (or keys) if the client can successfully obtain a
......@@ -219,7 +219,7 @@ public class Krb5Util {
private List<KeyTab> ktabs;
private List<KerberosKey> kk;
private Subject subj;
//private KerberosTicket tgt; // user2user, not supported yet
private KerberosTicket tgt;
private static ServiceCreds getInstance(
Subject subj, String serverPrincipal) {
......@@ -255,6 +255,8 @@ public class Krb5Util {
subj, null, null, KeyTab.class);
sc.kk = SubjectComber.findMany(
subj, serverPrincipal, null, KerberosKey.class);
sc.tgt = SubjectComber.find(subj, null, null, KerberosTicket.class);
if (sc.ktabs.isEmpty() && sc.kk.isEmpty()) {
return null;
}
......@@ -310,10 +312,22 @@ public class Krb5Util {
return ekeys;
}
public Credentials getInitCred() {
if (tgt == null) {
return null;
}
try {
return ticketToCreds(tgt);
} catch (KrbException | IOException e) {
return null;
}
}
public void destroy() {
kp = null;
ktabs = null;
kk = null;
tgt = null;
}
}
/**
......@@ -357,7 +371,7 @@ public class Krb5Util {
};
public static Credentials ticketToCreds(KerberosTicket kerbTicket)
throws KrbException, IOException {
throws KrbException, IOException {
return new Credentials(
kerbTicket.getEncoded(),
kerbTicket.getClient().getName(),
......
......@@ -96,4 +96,13 @@ public interface GSSCredentialSpi {
* @exception GSSException may be thrown
*/
public Oid getMechanism();
/**
* Impersonates another client.
*
* @param name the client to impersonate
* @return the new credential
* @exception GSSException may be thrown
*/
public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException;
}
......@@ -1059,6 +1059,9 @@ public class SpNegoContext implements GSSContextSpi {
if (mechContext != null) {
GSSCredentialImpl delegCred =
(GSSCredentialImpl)mechContext.getDelegCred();
if (delegCred == null) {
return null;
}
// determine delegated cred element usage
boolean initiate = false;
if (delegCred.getUsage() == GSSCredential.INITIATE_ONLY) {
......
......@@ -88,4 +88,9 @@ public class SpNegoCredElement implements GSSCredentialSpi {
public Oid getMechanism() {
return GSSUtil.GSS_SPNEGO_MECH_OID;
}
@Override
public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException {
return cred.impersonate(name);
}
}
......@@ -28,6 +28,7 @@ import org.ietf.jgss.*;
import java.security.Provider;
import sun.security.jgss.GSSUtil;
import sun.security.jgss.spi.GSSCredentialSpi;
import sun.security.jgss.spi.GSSNameSpi;
/**
* This class is essentially a wrapper class for the gss_cred_id_t
......@@ -132,4 +133,10 @@ public class GSSCredElement implements GSSCredentialSpi {
protected void finalize() throws Throwable {
dispose();
}
@Override
public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException {
throw new GSSException(GSSException.FAILURE, -1,
"Not supported yet");
}
}
......@@ -449,6 +449,18 @@ public class Credentials {
return CredentialsUtil.acquireServiceCreds(service, ccreds);
}
public static Credentials acquireS4U2selfCreds(PrincipalName user,
Credentials ccreds) throws KrbException, IOException {
return CredentialsUtil.acquireS4U2selfCreds(user, ccreds);
}
public static Credentials acquireS4U2proxyCreds(String service,
Ticket second, PrincipalName client, Credentials ccreds)
throws KrbException, IOException {
return CredentialsUtil.acquireS4U2proxyCreds(
service, second, client, ccreds);
}
public CredentialsCache getCache() {
return cache;
}
......@@ -490,18 +502,19 @@ public class Credentials {
public String toString() {
StringBuffer buffer = new StringBuffer("Credentials:");
buffer.append("\nclient=").append(client);
buffer.append("\nserver=").append(server);
buffer.append( "\n client=").append(client);
buffer.append( "\n server=").append(server);
if (authTime != null) {
buffer.append("\nauthTime=").append(authTime);
buffer.append("\n authTime=").append(authTime);
}
if (startTime != null) {
buffer.append("\nstartTime=").append(startTime);
buffer.append("\n startTime=").append(startTime);
}
buffer.append("\nendTime=").append(endTime);
buffer.append("\nrenewTill=").append(renewTill);
buffer.append("\nflags: ").append(flags);
buffer.append("\nEType (int): ").append(key.getEType());
buffer.append( "\n endTime=").append(endTime);
buffer.append( "\n renewTill=").append(renewTill);
buffer.append( "\n flags=").append(flags);
buffer.append( "\nEType (skey)=").append(key.getEType());
buffer.append( "\n (tkt key)=").append(ticket.encPart.eType);
return buffer.toString();
}
......
......@@ -160,8 +160,6 @@ public class EncryptedData implements Cloneable {
kvno = key.getKeyVersionNumber();
}
*/
// currently destructive on cipher
public byte[] decrypt(
EncryptionKey key, int usage)
throws KdcErrException, KrbApErrException, KrbCryptoException {
......@@ -175,7 +173,9 @@ public class EncryptedData implements Cloneable {
EType etypeEngine = EType.getInstance(eType);
plain = etypeEngine.decrypt(cipher, key.getBytes(), usage);
cipher = null;
// The service ticket will be used in S4U2proxy request. Therefore
// the raw ticket is still needed.
//cipher = null;
return etypeEngine.decryptedData(plain);
}
......
......@@ -287,8 +287,9 @@ public class KrbApReq {
cusec = authenticator.cusec;
authenticator.ctime.setMicroSeconds(authenticator.cusec);
if (!authenticator.cname.equals(enc_ticketPart.cname))
if (!authenticator.cname.equals(enc_ticketPart.cname)) {
throw new KrbApErrException(Krb5.KRB_AP_ERR_BADMATCH);
}
KerberosTime currTime = new KerberosTime(KerberosTime.NOW);
if (!authenticator.ctime.inClockSkew(currTime))
......
......@@ -64,7 +64,12 @@ abstract class KrbKdcRep {
for (int i = 1; i < 6; i++) {
if (req.reqBody.kdcOptions.get(i) !=
rep.encKDCRepPart.flags.get(i)) {
rep.encKDCRepPart.flags.get(i)) {
if (Krb5.DEBUG) {
System.out.println("> KrbKdcRep.check: at #" + i
+ ". request for " + req.reqBody.kdcOptions.get(i)
+ ", received " + rep.encKDCRepPart.flags.get(i));
}
throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
}
}
......
......@@ -87,7 +87,7 @@ public class KrbTgsRep extends KrbKdcRep {
check(false, req, rep);
this.creds = new Credentials(rep.ticket,
req.reqBody.cname,
rep.cname,
rep.ticket.sname,
enc_part.key,
enc_part.flags,
......
......@@ -35,6 +35,7 @@ import sun.security.krb5.internal.*;
import sun.security.krb5.internal.crypto.*;
import java.io.IOException;
import java.net.UnknownHostException;
import java.util.Arrays;
/**
* This class encapsulates a Kerberos TGS-REQ that is sent from the
......@@ -55,7 +56,7 @@ public class KrbTgsReq {
private byte[] obuf;
private byte[] ibuf;
// Used in CredentialsUtil
// Used in CredentialsUtil
public KrbTgsReq(Credentials asCreds,
PrincipalName sname)
throws KrbException, IOException {
......@@ -72,6 +73,45 @@ public class KrbTgsReq {
null); // EncryptionKey subSessionKey
}
// S4U2proxy
public KrbTgsReq(Credentials asCreds,
Ticket second,
PrincipalName sname)
throws KrbException, IOException {
this(KDCOptions.with(KDCOptions.CNAME_IN_ADDL_TKT,
KDCOptions.FORWARDABLE),
asCreds,
sname,
null,
null,
null,
null,
null,
null,
new Ticket[] {second}, // the service ticket
null);
}
// S4U2user
public KrbTgsReq(Credentials asCreds,
PrincipalName sname,
PAData extraPA)
throws KrbException, IOException {
this(KDCOptions.with(KDCOptions.FORWARDABLE),
asCreds,
asCreds.getClient(),
sname,
null,
null,
null,
null,
null,
null,
null,
null,
extraPA); // the PA-FOR-USER
}
// Called by Credentials, KrbCred
KrbTgsReq(
KDCOptions options,
......@@ -85,14 +125,42 @@ public class KrbTgsReq {
AuthorizationData authorizationData,
Ticket[] additionalTickets,
EncryptionKey subKey) throws KrbException, IOException {
this(options, asCreds, asCreds.getClient(), sname,
from, till, rtime, eTypes, addresses,
authorizationData, additionalTickets, subKey, null);
}
princName = asCreds.client;
private 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) throws KrbException, IOException {
princName = cname;
servName = sname;
ctime = new KerberosTime(KerberosTime.NOW);
// check if they are valid arguments. The optional fields
// should be consistent with settings in KDCOptions.
// TODO: Is this necessary? If the TGT is not FORWARDABLE,
// you can still request for a FORWARDABLE ticket, just the
// KDC will give you a non-FORWARDABLE one. Even if you
// cannot use the ticket expected, it still contains info.
// This means there will be problem later. We already have
// flags check in KrbTgsRep. Of course, sometimes the KDC
// will not issue the ticket at all.
if (options.get(KDCOptions.FORWARDABLE) &&
(!(asCreds.flags.get(Krb5.TKT_OPTS_FORWARDABLE)))) {
throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS);
......@@ -130,13 +198,13 @@ public class KrbTgsReq {
} else {
if (rtime != null) rtime = null;
}
if (options.get(KDCOptions.ENC_TKT_IN_SKEY)) {
if (options.get(KDCOptions.ENC_TKT_IN_SKEY) || options.get(KDCOptions.CNAME_IN_ADDL_TKT)) {
if (additionalTickets == null)
throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS);
// in TGS_REQ there could be more than one additional
// tickets, but in file-based credential cache,
// there is only one additional ticket field.
secondTicket = additionalTickets[0];
secondTicket = additionalTickets[0];
} else {
if (additionalTickets != null)
additionalTickets = null;
......@@ -156,7 +224,8 @@ public class KrbTgsReq {
addresses,
authorizationData,
additionalTickets,
subKey);
subKey,
extraPA);
obuf = tgsReqMessg.asn1Encode();
// XXX We need to revisit this to see if can't move it
......@@ -221,7 +290,8 @@ public class KrbTgsReq {
HostAddresses addresses,
AuthorizationData authorizationData,
Ticket[] additionalTickets,
EncryptionKey subKey)
EncryptionKey subKey,
PAData extraPA)
throws Asn1Exception, IOException, KdcErrException, KrbApErrException,
UnknownHostException, KrbCryptoException {
KerberosTime req_till = null;
......@@ -318,10 +388,12 @@ public class KrbTgsReq {
null,
null).getMessage();
PAData[] tgsPAData = new PAData[1];
tgsPAData[0] = new PAData(Krb5.PA_TGS_REQ, tgs_ap_req);
return new TGSReq(tgsPAData, reqBody);
PAData tgsPAData = new PAData(Krb5.PA_TGS_REQ, tgs_ap_req);
return new TGSReq(
extraPA != null ?
new PAData[] {extraPA, tgsPAData } :
new PAData[] {tgsPAData},
reqBody);
}
TGSReq getMessage() {
......
......@@ -32,17 +32,7 @@
package sun.security.krb5.internal;
import sun.security.krb5.*;
import sun.security.krb5.internal.ccache.CredentialsCache;
import java.util.StringTokenizer;
import sun.security.krb5.internal.ktab.*;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.Vector;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
/**
* This class is a utility that contains much of the TGS-Exchange
......@@ -53,77 +43,158 @@ public class CredentialsUtil {
private static boolean DEBUG = sun.security.krb5.internal.Krb5.DEBUG;
/**
* Acquires credentials for a specified service using initial credential. Wh
en the service has a different realm
* from the initial credential, we do cross-realm authentication - first, we
use the current credential to get
* a cross-realm credential from the local KDC, then use that cross-realm cr
edential to request service credential
* from the foreigh KDC.
*
* @param service the name of service principal using format components@real
m
* @param ccreds client's initial credential.
* @exception Exception general exception will be thrown when any error occu
rs.
* @return a <code>Credentials</code> object.
*/
/**
* Used by a middle server to acquire credentials on behalf of a
* client to itself using the S4U2self extension.
* @param client the client to impersonate
* @param ccreds the TGT of the middle service
* @return the new creds (cname=client, sname=middle)
*/
public static Credentials acquireS4U2selfCreds(PrincipalName client,
Credentials ccreds) throws KrbException, IOException {
String uRealm = client.getRealmString();
String localRealm = ccreds.getClient().getRealmString();
if (!uRealm.equals(localRealm)) {
// TODO: we do not support kerberos referral now
throw new KrbException("Cross realm impersonation not supported");
}
KrbTgsReq req = new KrbTgsReq(
ccreds,
ccreds.getClient(),
new PAData(Krb5.PA_FOR_USER,
new PAForUserEnc(client,
ccreds.getSessionKey()).asn1Encode()));
Credentials creds = req.sendAndGetCreds();
if (!creds.getClient().equals(client)) {
throw new KrbException("S4U2self request not honored by KDC");
}
return creds;
}
/**
* Used by a middle server to acquire a service ticket to a backend
* server using the S4U2proxy extension.
* @param backend the name of the backend service
* @param second the client's service ticket to the middle server
* @param ccreds the TGT of the middle server
* @return the creds (cname=client, sname=backend)
*/
public static Credentials acquireS4U2proxyCreds(
String backend, Ticket second,
PrincipalName client, Credentials ccreds)
throws KrbException, IOException {
KrbTgsReq req = new KrbTgsReq(
ccreds,
second,
new PrincipalName(backend));
Credentials creds = req.sendAndGetCreds();
if (!creds.getClient().equals(client)) {
throw new KrbException("S4U2proxy request not honored by KDC");
}
return creds;
}
/**
* Acquires credentials for a specified service using initial
* credential. When the service has a different realm from the initial
* credential, we do cross-realm authentication - first, we use the
* current credential to get a cross-realm credential from the local KDC,
* then use that cross-realm credential to request service credential
* from the foreign KDC.
*
* @param service the name of service principal
* @param ccreds client's initial credential
*/
public static Credentials acquireServiceCreds(
String service, Credentials ccreds)
throws KrbException, IOException {
throws KrbException, IOException {
PrincipalName sname = new PrincipalName(service);
String serviceRealm = sname.getRealmString();
String localRealm = ccreds.getClient().getRealmString();
/*
if (!localRealm.equalsIgnoreCase(serviceRealm)) { //do cross-realm auth entication
if (DEBUG) {
System.out.println(">>>DEBUG: Credentails request cross realm ticket for " + "krbtgt/" + serviceRealm + "@" + localRealm);
}
Credentials crossCreds = serviceCreds(new ServiceName("krbtgt/" + serviceRealm + "@" + localRealm), ccreds);
if (DEBUG) {
printDebug(crossCreds);
}
Credentials result = serviceCreds(sname, crossCreds);
if (DEBUG) {
printDebug(result);
}
return result;
}
else return serviceCreds(sname, ccreds);
*/
if (localRealm.equals(serviceRealm))
{
if (DEBUG)
System.out.println(">>> Credentials acquireServiceCreds: same realm");
if (localRealm.equals(serviceRealm)) {
if (DEBUG) {
System.out.println(
">>> Credentials acquireServiceCreds: same realm");
}
return serviceCreds(sname, ccreds);
}
Credentials theCreds = null;
boolean[] okAsDelegate = new boolean[1];
Credentials theTgt = getTGTforRealm(localRealm, serviceRealm,
ccreds, okAsDelegate);
if (theTgt != null) {
if (DEBUG) {
System.out.println(">>> Credentials acquireServiceCreds: "
+ "got right tgt");
System.out.println(">>> Credentials acquireServiceCreds: "
+ "obtaining service creds for " + sname);
}
try {
theCreds = serviceCreds(sname, theTgt);
} catch (Exception exc) {
if (DEBUG) {
System.out.println(exc);
}
theCreds = null;
}
}
if (theCreds != null) {
if (DEBUG) {
System.out.println(">>> Credentials acquireServiceCreds: "
+ "returning creds:");
Credentials.printDebug(theCreds);
}
if (!okAsDelegate[0]) {
theCreds.resetDelegate();
}
return theCreds;
}
throw new KrbApErrException(Krb5.KRB_AP_ERR_GEN_CRED,
"No service creds");
}
/**
* Gets a TGT to another realm
* @param localRealm this realm
* @param serviceRealm the other realm
* @param ccreds TGT in this realm
* @param okAsDelegate an [out] argument to receive the okAsDelegate
* property. True only if all realms allow delegation.
* @return the TGT for the other realm, null if cannot find a path
* @throws KrbException if something goes wrong
*/
private static Credentials getTGTforRealm(String localRealm,
String serviceRealm, Credentials ccreds, boolean[] okAsDelegate)
throws KrbException {
// Get a list of realms to traverse
String[] realms = Realm.getRealmsList(localRealm, serviceRealm);
boolean okAsDelegate = true;
if (realms == null || realms.length == 0)
{
if (DEBUG)
System.out.println(">>> Credentials acquireServiceCreds: no realms list");
if (realms == null || realms.length == 0) {
if (DEBUG) {
System.out.println(
">>> Credentials acquireServiceCreds: no realms list");
}
return null;
}
int i = 0, k = 0;
Credentials cTgt = null, newTgt = null, theTgt = null;
PrincipalName tempService = null;
String realm = null, newTgtRealm = null, theTgtRealm = null;
String newTgtRealm = null;
for (cTgt = ccreds, i = 0; i < realms.length;)
{
okAsDelegate[0] = true;
for (cTgt = ccreds, i = 0; i < realms.length;) {
tempService = PrincipalName.tgsService(serviceRealm, realms[i]);
if (DEBUG)
{
System.out.println(">>> Credentials acquireServiceCreds: main loop: [" + i +"] tempService=" + tempService);
if (DEBUG) {
System.out.println(
">>> Credentials acquireServiceCreds: main loop: ["
+ i +"] tempService=" + tempService);
}
try {
......@@ -132,11 +203,10 @@ rs.
newTgt = null;
}
if (newTgt == null)
{
if (DEBUG)
{
System.out.println(">>> Credentials acquireServiceCreds: no tgt; searching backwards");
if (newTgt == null) {
if (DEBUG) {
System.out.println(">>> Credentials acquireServiceCreds: "
+ "no tgt; searching backwards");
}
/*
......@@ -144,17 +214,15 @@ rs.
* realm as close to the target as possible.
* That means traversing the realms list backwards.
*/
for (newTgt = null, k = realms.length - 1;
newTgt == null && k > i; k--)
{
newTgt == null && k > i; k--) {
tempService = PrincipalName.tgsService(realms[k], realms[i]);
if (DEBUG)
{
System.out.println(">>> Credentials acquireServiceCreds: inner loop: [" + k +"] tempService=" + tempService);
if (DEBUG) {
System.out.println(
">>> Credentials acquireServiceCreds: "
+ "inner loop: [" + k
+ "] tempService=" + tempService);
}
try {
newTgt = serviceCreds(tempService, cTgt);
} catch (Exception exc) {
......@@ -163,11 +231,10 @@ rs.
}
} // Ends 'if (newTgt == null)'
if (newTgt == null)
{
if (DEBUG)
{
System.out.println(">>> Credentials acquireServiceCreds: no tgt; cannot get creds");
if (newTgt == null) {
if (DEBUG) {
System.out.println(">>> Credentials acquireServiceCreds: "
+ "no tgt; cannot get creds");
}
break;
}
......@@ -176,29 +243,24 @@ rs.
* We have a tgt. It may or may not be for the target.
* If it's for the target realm, we're done looking for a tgt.
*/
newTgtRealm = newTgt.getServer().getInstanceComponent();
if (okAsDelegate && !newTgt.checkDelegate()) {
if (DEBUG)
{
if (okAsDelegate[0] && !newTgt.checkDelegate()) {
if (DEBUG) {
System.out.println(">>> Credentials acquireServiceCreds: " +
"global OK-AS-DELEGATE turned off at " +
newTgt.getServer());
}
okAsDelegate = false;
okAsDelegate[0] = false;
}
if (DEBUG)
{
System.out.println(">>> Credentials acquireServiceCreds: got tgt");
//printDebug(newTgt);
if (DEBUG) {
System.out.println(">>> Credentials acquireServiceCreds: "
+ "got tgt");
}
if (newTgtRealm.equals(serviceRealm))
{
if (newTgtRealm.equals(serviceRealm)) {
/* We got the right tgt */
theTgt = newTgt;
theTgtRealm = newTgtRealm;
break;
}
......@@ -207,17 +269,13 @@ rs.
* See if the realm of the new tgt is in the list of realms
* and continue looking from there.
*/
for (k = i+1; k < realms.length; k++)
{
if (newTgtRealm.equals(realms[k]))
{
for (k = i+1; k < realms.length; k++) {
if (newTgtRealm.equals(realms[k])) {
break;
}
}
if (k < realms.length)
{
if (k < realms.length) {
/*
* (re)set the counter so we start looking
* from the realm we just obtained a tgt for.
......@@ -225,64 +283,24 @@ rs.
i = k;
cTgt = newTgt;
if (DEBUG)
{
System.out.println(">>> Credentials acquireServiceCreds: continuing with main loop counter reset to " + i);
if (DEBUG) {
System.out.println(">>> Credentials acquireServiceCreds: "
+ "continuing with main loop counter reset to " + i);
}
continue;
}
else
{
else {
/*
* The new tgt's realm is not in the heirarchy of realms.
* It's probably not safe to get a tgt from
* a tgs that is outside the known list of realms.
* Give up now.
*/
break;
}
} // Ends outermost/main 'for' loop
Credentials theCreds = null;
if (theTgt != null)
{
/* We have the right tgt. Let's get the service creds */
if (DEBUG)
{
System.out.println(">>> Credentials acquireServiceCreds: got right tgt");
//printDebug(theTgt);
System.out.println(">>> Credentials acquireServiceCreds: obtaining service creds for " + sname);
}
try {
theCreds = serviceCreds(sname, theTgt);
} catch (Exception exc) {
if (DEBUG)
System.out.println(exc);
theCreds = null;
}
}
if (theCreds != null)
{
if (DEBUG)
{
System.out.println(">>> Credentials acquireServiceCreds: returning creds:");
Credentials.printDebug(theCreds);
}
if (!okAsDelegate) {
theCreds.resetDelegate();
}
return theCreds;
}
throw new KrbApErrException(Krb5.KRB_AP_ERR_GEN_CRED,
"No service creds");
return theTgt;
}
/*
......
......@@ -160,9 +160,10 @@ public class EncKDCRepPart {
if (der.getData().available() > 0) {
caddr = HostAddresses.parse(der.getData(), (byte) 0x0B, true);
}
if (der.getData().available() > 0) {
// We observe extra data from MSAD
/*if (der.getData().available() > 0) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
}*/
}
/**
......
......@@ -139,13 +139,45 @@ public class KDCOptions extends KerberosFlags {
public static final int UNUSED9 = 9;
public static final int UNUSED10 = 10;
public static final int UNUSED11 = 11;
public static final int CNAME_IN_ADDL_TKT = 14;
public static final int RENEWABLE_OK = 27;
public static final int ENC_TKT_IN_SKEY = 28;
public static final int RENEW = 30;
public static final int VALIDATE = 31;
private static final String[] names = {
"RESERVED", //0
"FORWARDABLE", //1;
"FORWARDED", //2;
"PROXIABLE", //3;
"PROXY", //4;
"ALLOW_POSTDATE", //5;
"POSTDATED", //6;
"UNUSED7", //7;
"RENEWABLE", //8;
"UNUSED9", //9;
"UNUSED10", //10;
"UNUSED11", //11;
null,null,
"CNAME_IN_ADDL_TKT",//14;
null,null,null,null,null,null,null,null,null,null,null,null,
"RENEWABLE_OK", //27;
"ENC_TKT_IN_SKEY", //28;
null,
"RENEW", //30;
"VALIDATE", //31;
};
private boolean DEBUG = Krb5.DEBUG;
public static KDCOptions with(int... flags) {
KDCOptions options = new KDCOptions();
for (int flag: flags) {
options.set(flag, true);
}
return options;
}
public KDCOptions() {
super(Krb5.KDC_OPTS_MAX + 1);
setDefault();
......@@ -238,6 +270,20 @@ public class KDCOptions extends KerberosFlags {
return super.get(option);
}
@Override public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("KDCOptions: ");
for (int i=0; i<Krb5.KDC_OPTS_MAX+1; i++) {
if (get(i)) {
if (names[i] != null) {
sb.append(names[i]).append(",");
} else {
sb.append(i).append(",");
}
}
}
return sb.toString();
}
private void setDefault() {
try {
......
......@@ -158,6 +158,9 @@ public class Krb5 {
public static final int PA_ETYPE_INFO = 11;
public static final int PA_ETYPE_INFO2 = 19;
// S4U2user info
public static final int PA_FOR_USER = 129;
//-------------------------------+-------------
//authorization data type |ad-type value
//-------------------------------+-------------
......
......@@ -312,6 +312,9 @@ public class PAData {
}
}
break;
case Krb5.PA_FOR_USER:
sb.append("\t PA-FOR-USER\n");
break;
default:
// Unknown Pre-auth type
break;
......
/*
* Copyright (c) 2012, 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.krb5.internal;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import sun.security.krb5.*;
import sun.security.krb5.internal.crypto.KeyUsage;
import sun.security.krb5.internal.util.KerberosString;
import sun.security.util.DerOutputStream;
import sun.security.util.DerValue;
/**
* Implements the ASN.1 PA-FOR-USER type.
*
* <xmp>
* padata-type ::= PA-FOR-USER
* -- value 129
* padata-value ::= EncryptedData
* -- PA-FOR-USER-ENC
* PA-FOR-USER-ENC ::= SEQUENCE {
* userName[0] PrincipalName,
* userRealm[1] Realm,
* cksum[2] Checksum,
* auth-package[3] KerberosString
* }
* </xmp>
*
* <p>
* This definition reflects MS-SFU.
*/
public class PAForUserEnc {
final public PrincipalName name;
final private EncryptionKey key;
final public static String AUTH_PACKAGE = "Kerberos";
public PAForUserEnc(PrincipalName name, EncryptionKey key) {
this.name = name;
this.key = key;
}
/**
* Constructs a PA-FOR-USER object from a DER encoding.
* @param encoding the input object
* @param key the key to verify the checksum inside encoding
* @throws KrbException if the verification fails.
* Note: this method is now only used by test KDC, therefore
* the verification is ignored (at the moment).
*/
public PAForUserEnc(DerValue encoding, EncryptionKey key)
throws Asn1Exception, KrbException, IOException {
DerValue der = null;
this.key = key;
if (encoding.getTag() != DerValue.tag_Sequence) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
// Realm after name? Quite abnormal.
PrincipalName tmpName = null;
der = encoding.getData().getDerValue();
if ((der.getTag() & 0x1F) == 0x00) {
try {
tmpName = new PrincipalName(der.getData().getDerValue(),
new Realm("PLACEHOLDER"));
} catch (RealmException re) {
// Impossible
}
} else {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
der = encoding.getData().getDerValue();
if ((der.getTag() & 0x1F) == 0x01) {
try {
Realm realm = new Realm(der.getData().getDerValue());
name = new PrincipalName(
tmpName.getNameType(), tmpName.getNameStrings(), realm);
} catch (RealmException re) {
throw new IOException(re);
}
} else {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
der = encoding.getData().getDerValue();
if ((der.getTag() & 0x1F) == 0x02) {
// Deal with the checksum
} else {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
der = encoding.getData().getDerValue();
if ((der.getTag() & 0x1F) == 0x03) {
String authPackage = new KerberosString(der.getData().getDerValue()).toString();
if (!authPackage.equalsIgnoreCase(AUTH_PACKAGE)) {
throw new IOException("Incorrect auth-package");
}
} else {
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
if (encoding.getData().available() > 0)
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
}
public byte[] asn1Encode() throws Asn1Exception, IOException {
DerOutputStream bytes = new DerOutputStream();
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x00), name.asn1Encode());
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x01), name.getRealm().asn1Encode());
try {
Checksum cks = new Checksum(
Checksum.CKSUMTYPE_HMAC_MD5_ARCFOUR,
getS4UByteArray(),
key,
KeyUsage.KU_PA_FOR_USER_ENC_CKSUM);
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x02), cks.asn1Encode());
} catch (KrbException ke) {
throw new IOException(ke);
}
DerOutputStream temp = new DerOutputStream();
temp.putDerValue(new KerberosString(AUTH_PACKAGE).toDerValue());
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x03), temp);
temp = new DerOutputStream();
temp.write(DerValue.tag_Sequence, bytes);
return temp.toByteArray();
}
/**
* Returns S4UByteArray, the block to calculate checksum inside a
* PA-FOR-USER-ENC data structure. It includes:
* 1. userName.name-type encoded as a 4-byte integer in little endian
* byte order
* 2. all string values in the sequence of strings contained in the
* userName.name-string field
* 3. the string value of the userRealm field
* 4. the string value of auth-package field
*/
public byte[] getS4UByteArray() {
try {
ByteArrayOutputStream ba = new ByteArrayOutputStream();
ba.write(new byte[4]);
for (String s: name.getNameStrings()) {
ba.write(s.getBytes("UTF-8"));
}
ba.write(name.getRealm().toString().getBytes("UTF-8"));
ba.write(AUTH_PACKAGE.getBytes("UTF-8"));
byte[] output = ba.toByteArray();
int pnType = name.getNameType();
output[0] = (byte)(pnType & 0xff);
output[1] = (byte)((pnType>>8) & 0xff);
output[2] = (byte)((pnType>>16) & 0xff);
output[3] = (byte)((pnType>>24) & 0xff);
return output;
} catch (IOException ioe) {
// not possible
throw new AssertionError("Cannot write ByteArrayOutputStream", ioe);
}
}
public String toString() {
return "PA-FOR-USER: " + name;
}
}
......@@ -54,6 +54,7 @@ public class KeyUsage {
public static final int KU_ENC_KRB_PRIV_PART = 13; // KrbPriv
public static final int KU_ENC_KRB_CRED_PART = 14; // KrbCred
public static final int KU_KRB_SAFE_CKSUM = 15; // KrbSafe
public static final int KU_PA_FOR_USER_ENC_CKSUM = 17; // S4U2user
public static final int KU_AD_KDC_ISSUED_CKSUM = 19;
public static final boolean isValid(int usage) {
......
......@@ -279,6 +279,9 @@ public class KeyTab implements KeyTabConstants {
EncryptionKey key;
int size = entries.size();
ArrayList<EncryptionKey> keys = new ArrayList<>(size);
if (DEBUG) {
System.out.println("Looking for keys for: " + service);
}
for (int i = size-1; i >= 0; i--) {
entry = entries.elementAt(i);
if (entry.service.match(service)) {
......
......@@ -38,11 +38,13 @@ public class Basic {
new OneKDC(null).writeJAASConf();
Context c, s;
Context c, s, s2, b;
c = Context.fromJAAS("client");
s = Context.fromJAAS("server");
b = Context.fromJAAS("backend");
c.startAsClient(OneKDC.SERVER, GSSUtil.GSS_KRB5_MECH_OID);
c.x().requestCredDeleg(true);
s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID);
Context.handshake(c, s);
......@@ -50,7 +52,13 @@ public class Basic {
Context.transmit("i say high --", c, s);
Context.transmit(" you say low", s, c);
s2 = s.delegated();
s.dispose();
c.dispose();
s2.startAsClient(OneKDC.BACKEND, GSSUtil.GSS_KRB5_MECH_OID);
b.startAsServer(GSSUtil.GSS_KRB5_MECH_OID);
Context.handshake(s2, b);
}
}
......@@ -42,9 +42,10 @@ import org.ietf.jgss.Oid;
import com.sun.security.jgss.ExtendedGSSContext;
import com.sun.security.jgss.InquireType;
import com.sun.security.jgss.AuthorizationDataEntry;
import com.sun.security.jgss.ExtendedGSSCredential;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import javax.security.auth.kerberos.KeyTab;
import java.security.Principal;
/**
* Context of a JGSS subject, encapsulating Subject and GSSContext.
......@@ -90,7 +91,21 @@ public class Context {
public Context delegated() throws Exception {
Context out = new Context();
out.s = s;
out.cred = x.getDelegCred();
try {
out.cred = Subject.doAs(s, new PrivilegedExceptionAction<GSSCredential>() {
@Override
public GSSCredential run() throws Exception {
GSSCredential cred = x.getDelegCred();
if (cred == null && x.getCredDelegState() ||
cred != null && !x.getCredDelegState()) {
throw new Exception("getCredDelegState not match");
}
return cred;
}
});
} catch (PrivilegedActionException pae) {
throw pae.getException();
}
out.name = name + " as " + out.cred.getName().toString();
return out;
}
......@@ -212,28 +227,34 @@ public class Context {
* @throws java.lang.Exception
*/
public void startAsServer(final Oid mech) throws Exception {
startAsServer(null, mech);
startAsServer(null, mech, false);
}
public void startAsServer(final String name, final Oid mech) throws Exception {
startAsServer(name, mech, false);
}
/**
* Starts as a server with the specified service name
* @param name the service name
* @param mech GSS mech
* @throws java.lang.Exception
*/
public void startAsServer(final String name, final Oid mech) throws Exception {
public void startAsServer(final String name, final Oid mech, final boolean asInitiator) throws Exception {
doAs(new Action() {
@Override
public byte[] run(Context me, byte[] dummy) throws Exception {
GSSManager m = GSSManager.getInstance();
me.x = (ExtendedGSSContext)m.createContext(m.createCredential(
me.cred = m.createCredential(
name == null ? null :
(name.indexOf('@') < 0 ?
m.createName(name, null) :
m.createName(name, GSSName.NT_HOSTBASED_SERVICE)),
GSSCredential.INDEFINITE_LIFETIME,
mech,
GSSCredential.ACCEPT_ONLY));
asInitiator?
GSSCredential.INITIATE_AND_ACCEPT:
GSSCredential.ACCEPT_ONLY);
me.x = (ExtendedGSSContext)m.createContext(me.cred);
return null;
}
}, null);
......@@ -331,27 +352,38 @@ public class Context {
} catch (Exception e) {
;// Don't care
}
System.out.println("====== Private Credentials Set ======");
for (Object o : s.getPrivateCredentials()) {
System.out.println(" " + o.getClass());
if (o instanceof KerberosTicket) {
KerberosTicket kt = (KerberosTicket) o;
System.out.println(" " + kt.getServer() + " for " + kt.getClient());
} else if (o instanceof KerberosKey) {
KerberosKey kk = (KerberosKey) o;
System.out.print(" " + kk.getKeyType() + " " + kk.getVersionNumber() + " " + kk.getAlgorithm() + " ");
for (byte b : kk.getEncoded()) {
System.out.printf("%02X", b & 0xff);
}
System.out.println();
} else if (o instanceof Map) {
Map map = (Map) o;
for (Object k : map.keySet()) {
System.out.println(" " + k + ": " + map.get(k));
}
} else {
if (s != null) {
System.out.println("====== START SUBJECT CONTENT =====");
for (Principal p: s.getPrincipals()) {
System.out.println(" Principal: " + p);
}
for (Object o : s.getPublicCredentials()) {
System.out.println(" " + o.getClass());
System.out.println(" " + o);
}
System.out.println("====== Private Credentials Set ======");
for (Object o : s.getPrivateCredentials()) {
System.out.println(" " + o.getClass());
if (o instanceof KerberosTicket) {
KerberosTicket kt = (KerberosTicket) o;
System.out.println(" " + kt.getServer() + " for " + kt.getClient());
} else if (o instanceof KerberosKey) {
KerberosKey kk = (KerberosKey) o;
System.out.print(" " + kk.getKeyType() + " " + kk.getVersionNumber() + " " + kk.getAlgorithm() + " ");
for (byte b : kk.getEncoded()) {
System.out.printf("%02X", b & 0xff);
}
System.out.println();
} else if (o instanceof Map) {
Map map = (Map) o;
for (Object k : map.keySet()) {
System.out.println(" " + k + ": " + map.get(k));
}
} else {
System.out.println(" " + o);
}
}
System.out.println("====== END SUBJECT CONTENT =====");
}
if (x != null && x instanceof ExtendedGSSContext) {
if (x.isEstablished()) {
......@@ -510,6 +542,29 @@ public class Context {
return sb.toString();
}
public Context impersonate(final String someone) throws Exception {
try {
GSSCredential creds = Subject.doAs(s, new PrivilegedExceptionAction<GSSCredential>() {
@Override
public GSSCredential run() throws Exception {
GSSManager m = GSSManager.getInstance();
GSSName other = m.createName(someone, GSSName.NT_USER_NAME);
if (Context.this.cred == null) {
Context.this.cred = m.createCredential(GSSCredential.INITIATE_ONLY);
}
return ((ExtendedGSSCredential)Context.this.cred).impersonate(other);
}
});
Context out = new Context();
out.s = s;
out.cred = creds;
out.name = name + " as " + out.cred.getName().toString();
return out;
} catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
public byte[] take(final byte[] in) throws Exception {
return doAs(new Action() {
@Override
......@@ -522,10 +577,11 @@ public class Context {
}
return null;
} else {
System.out.println(name + " call initSecContext");
if (me.x.isInitiator()) {
System.out.println(name + " call initSecContext");
return me.x.initSecContext(input, 0, input.length);
} else {
System.out.println(name + " call acceptSecContext");
return me.x.acceptSecContext(input, 0, input.length);
}
}
......
......@@ -24,6 +24,7 @@
/*
* @test
* @bug 6706974
* @compile -XDignore.symbol.file CrossRealm.java
* @run main/othervm CrossRealm
* @summary Add krb5 test infrastructure
*/
......
......@@ -178,6 +178,20 @@ public class KDC {
* What backend server can be delegated to
*/
OK_AS_DELEGATE,
/**
* Allow S4U2self, List<String> of middle servers.
* If not set, means KDC does not understand S4U2self at all, therefore
* would ignore any PA-FOR-USER request and send a ticket using the
* cname of teh requestor. If set, it returns FORWARDABLE tickets to
* a server with its name in the list
*/
ALLOW_S4U2SELF,
/**
* Allow S4U2proxy, Map<String,List<String>> of middle servers to
* backends. If not set or a backend not in a server's list,
* Krb5.KDC_ERR_POLICY will be send for S4U2proxy request.
*/
ALLOW_S4U2PROXY,
};
static {
......@@ -618,13 +632,18 @@ public class KDC {
int e2 = eTypes[0]; // etype for outgoing session key
int e3 = eTypes[0]; // etype for outgoing ticket
PAData[] pas = kDCReqDotPAData(tgsReq);
PAData[] pas = KDCReqDotPAData(tgsReq);
Ticket tkt = null;
EncTicketPart etp = null;
PrincipalName cname = null;
boolean allowForwardable = true;
if (pas == null || pas.length == 0) {
throw new KrbException(Krb5.KDC_ERR_PADATA_TYPE_NOSUPP);
} else {
PrincipalName forUserCName = null;
for (PAData pa: pas) {
if (pa.getType() == Krb5.PA_TGS_REQ) {
APReq apReq = new APReq(pa.getValue());
......@@ -636,7 +655,30 @@ public class KDC {
DerInputStream derIn = new DerInputStream(bb);
DerValue der = derIn.getDerValue();
etp = new EncTicketPart(der.toByteArray());
// Finally, cname will be overwritten by PA-FOR-USER
// if it exists.
cname = etp.cname;
System.out.println(realm + "> presenting a ticket of "
+ etp.cname + " to " + tkt.sname);
} else if (pa.getType() == Krb5.PA_FOR_USER) {
if (options.containsKey(Option.ALLOW_S4U2SELF)) {
PAForUserEnc p4u = new PAForUserEnc(
new DerValue(pa.getValue()), null);
forUserCName = p4u.name;
System.out.println(realm + "> presenting a PA_FOR_USER "
+ " in the name of " + p4u.name);
}
}
}
if (forUserCName != null) {
List<String> names = (List<String>)options.get(Option.ALLOW_S4U2SELF);
if (!names.contains(cname.toString())) {
// Mimic the normal KDC behavior. When a server is not
// allowed to send S4U2self, do not send an error.
// Instead, send a ticket which is useless later.
allowForwardable = false;
}
cname = forUserCName;
}
if (tkt == null) {
throw new KrbException(Krb5.KDC_ERR_PADATA_TYPE_NOSUPP);
......@@ -658,7 +700,8 @@ public class KDC {
}
boolean[] bFlags = new boolean[Krb5.TKT_OPTS_MAX+1];
if (body.kdcOptions.get(KDCOptions.FORWARDABLE)) {
if (body.kdcOptions.get(KDCOptions.FORWARDABLE)
&& allowForwardable) {
bFlags[Krb5.TKT_OPTS_FORWARDABLE] = true;
}
if (body.kdcOptions.get(KDCOptions.FORWARDED) ||
......@@ -678,6 +721,37 @@ public class KDC {
if (body.kdcOptions.get(KDCOptions.ALLOW_POSTDATE)) {
bFlags[Krb5.TKT_OPTS_MAY_POSTDATE] = true;
}
if (body.kdcOptions.get(KDCOptions.CNAME_IN_ADDL_TKT)) {
if (!options.containsKey(Option.ALLOW_S4U2PROXY)) {
// Don't understand CNAME_IN_ADDL_TKT
throw new KrbException(Krb5.KDC_ERR_BADOPTION);
} else {
Map<String,List<String>> map = (Map<String,List<String>>)
options.get(Option.ALLOW_S4U2PROXY);
Ticket second = KDCReqBodyDotFirstAdditionalTicket(body);
EncryptionKey key2 = keyForUser(second.sname, second.encPart.getEType(), true);
byte[] bb = second.encPart.decrypt(key2, KeyUsage.KU_TICKET);
DerInputStream derIn = new DerInputStream(bb);
DerValue der = derIn.getDerValue();
EncTicketPart tktEncPart = new EncTicketPart(der.toByteArray());
if (!tktEncPart.flags.get(Krb5.TKT_OPTS_FORWARDABLE)) {
//throw new KrbException(Krb5.KDC_ERR_BADOPTION);
}
PrincipalName client = tktEncPart.cname;
System.out.println(realm + "> and an additional ticket of "
+ client + " to " + second.sname);
if (map.containsKey(cname.toString())) {
if (map.get(cname.toString()).contains(service.toString())) {
System.out.println(realm + "> S4U2proxy OK");
} else {
throw new KrbException(Krb5.KDC_ERR_BADOPTION);
}
} else {
throw new KrbException(Krb5.KDC_ERR_BADOPTION);
}
cname = client;
}
}
String okAsDelegate = (String)options.get(Option.OK_AS_DELEGATE);
if (okAsDelegate != null && (
......@@ -691,7 +765,7 @@ public class KDC {
EncTicketPart enc = new EncTicketPart(
tFlags,
key,
etp.cname,
cname,
new TransitedEncoding(1, new byte[0]), // TODO
new KerberosTime(new Date()),
body.from,
......@@ -729,7 +803,7 @@ public class KDC {
);
EncryptedData edata = new EncryptedData(ckey, enc_part.asn1Encode(), KeyUsage.KU_ENC_TGS_REP_PART_SESSKEY);
TGSRep tgsRep = new TGSRep(null,
etp.cname,
cname,
t,
edata);
System.out.println(" Return " + tgsRep.cname
......@@ -942,7 +1016,7 @@ public class KDC {
outPAs.add(new PAData(Krb5.PA_ETYPE_INFO, eid.toByteArray()));
}
PAData[] inPAs = kDCReqDotPAData(asReq);
PAData[] inPAs = KDCReqDotPAData(asReq);
if (inPAs == null || inPAs.length == 0) {
Object preauth = options.get(Option.PREAUTH_REQUIRED);
if (preauth == null || preauth.equals(Boolean.TRUE)) {
......@@ -1252,6 +1326,7 @@ public class KDC {
private static final Field getEType;
private static final Constructor<EncryptedData> ctorEncryptedData;
private static final Method stringToKey;
private static final Field getAddlTkt;
static {
try {
......@@ -1265,6 +1340,8 @@ public class KDC {
"stringToKey",
char[].class, String.class, byte[].class, Integer.TYPE);
stringToKey.setAccessible(true);
getAddlTkt = KDCReqBody.class.getDeclaredField("additionalTickets");
getAddlTkt.setAccessible(true);
} catch (NoSuchFieldException nsfe) {
throw new AssertionError(nsfe);
} catch (NoSuchMethodException nsme) {
......@@ -1278,7 +1355,7 @@ public class KDC {
throw new AssertionError(e);
}
}
private static PAData[] kDCReqDotPAData(KDCReq req) {
private static PAData[] KDCReqDotPAData(KDCReq req) {
try {
return (PAData[])getPADataField.get(req);
} catch (Exception e) {
......@@ -1303,4 +1380,11 @@ public class KDC {
throw new AssertionError(e);
}
}
private static Ticket KDCReqBodyDotFirstAdditionalTicket(KDCReqBody body) {
try {
return ((Ticket[])getAddlTkt.get(body))[0];
} catch (Exception e) {
throw new AssertionError(e);
}
}
}
......@@ -91,7 +91,7 @@ public class OkAsDelegate {
Context c, s;
c = Context.fromJAAS("client");
s = Context.fromJAAS("server");
s = Context.fromJAAS("com.sun.security.jgss.krb5.accept");
Oid mech = GSSUtil.GSS_KRB5_MECH_OID;
if (System.getProperty("test.spnego") != null) {
......
/*
* Copyright (c) 2012, 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.
*
* 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.
*/
/*
* @test
* @bug 6355584
* @summary Introduce constrained Kerberos delegation
* @compile -XDignore.symbol.file S4U2proxy.java
* @run main/othervm S4U2proxy krb5
* @run main/othervm S4U2proxy spnego
*/
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.ietf.jgss.Oid;
import sun.security.jgss.GSSUtil;
public class S4U2proxy {
public static void main(String[] args) throws Exception {
Oid mech;
if (args[0].equals("spnego")) {
mech = GSSUtil.GSS_SPNEGO_MECH_OID;
} else if (args[0].contains("krb5")) {
mech = GSSUtil.GSS_KRB5_MECH_OID;
} else {
throw new Exception("Unknown mech");
}
OneKDC kdc = new OneKDC(null);
kdc.writeJAASConf();
kdc.setOption(KDC.Option.PREAUTH_REQUIRED, false);
Map<String,List<String>> map = new HashMap<>();
map.put(OneKDC.SERVER + "@" + OneKDC.REALM, Arrays.asList(
new String[]{OneKDC.BACKEND + "@" + OneKDC.REALM}));
kdc.setOption(KDC.Option.ALLOW_S4U2PROXY, map);
Context c, s, b;
c = Context.fromJAAS("client");
s = Context.fromJAAS("server");
b = Context.fromJAAS("backend");
c.startAsClient(OneKDC.SERVER, mech);
s.startAsServer(null, mech, false);
Context.handshake(c, s);
Context p = s.delegated();
p.startAsClient(OneKDC.BACKEND, mech);
b.startAsServer(mech);
Context.handshake(p, b);
p.startAsClient(OneKDC.BACKEND, mech);
b.startAsServer(mech);
Context.handshake(p, b);
}
}
/*
* Copyright (c) 2012, 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.
*
* 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.
*/
/*
* @test
* @bug 6355584
* @summary Introduce constrained Kerberos delegation
* @compile -XDignore.symbol.file S4U2proxyGSS.java
* @run main/othervm -Djavax.security.auth.useSubjectCredsOnly=false S4U2proxyGSS krb5
* @run main/othervm -Djavax.security.auth.useSubjectCredsOnly=false S4U2proxyGSS spnego
*/
import java.io.File;
import java.io.FileOutputStream;
import java.security.Security;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.ietf.jgss.Oid;
import sun.security.jgss.GSSUtil;
public class S4U2proxyGSS {
public static void main(String[] args) throws Exception {
Oid mech;
if (args[0].equals("spnego")) {
mech = GSSUtil.GSS_SPNEGO_MECH_OID;
} else if (args[0].contains("krb5")) {
mech = GSSUtil.GSS_KRB5_MECH_OID;
} else {
throw new Exception("Unknown mech");
}
OneKDC kdc = new OneKDC(null);
kdc.writeJAASConf();
kdc.setOption(KDC.Option.PREAUTH_REQUIRED, false);
Map<String,List<String>> map = new HashMap<>();
map.put(OneKDC.SERVER + "@" + OneKDC.REALM, Arrays.asList(
new String[]{OneKDC.SERVER + "@" + OneKDC.REALM}));
kdc.setOption(KDC.Option.ALLOW_S4U2PROXY, map);
Context c, s, b;
System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");
System.setProperty("java.security.auth.login.config", OneKDC.JAAS_CONF);
File f = new File(OneKDC.JAAS_CONF);
FileOutputStream fos = new FileOutputStream(f);
fos.write((
"com.sun.security.jgss.krb5.initiate {\n" +
" com.sun.security.auth.module.Krb5LoginModule required;\n};\n" +
"com.sun.security.jgss.krb5.accept {\n" +
" com.sun.security.auth.module.Krb5LoginModule required\n" +
" principal=\"" + OneKDC.SERVER + "\"\n" +
" useKeyTab=true\n" +
" storeKey=true;\n};\n"
).getBytes());
fos.close();
Security.setProperty("auth.login.defaultCallbackHandler", "OneKDC$CallbackForClient");
c = Context.fromThinAir();
s = Context.fromThinAir();
b = Context.fromThinAir();
c.startAsClient(OneKDC.SERVER, mech);
c.x().requestCredDeleg(false);
s.startAsServer(mech);
Context.handshake(c, s);
Context p = s.delegated();
p.startAsClient(OneKDC.SERVER, mech);
b.startAsServer(mech);
Context.handshake(p, b);
String n1 = p.x().getSrcName().toString().split("@")[0];
String n2 = b.x().getSrcName().toString().split("@")[0];
if (!n1.equals(OneKDC.USER) || !n2.equals(OneKDC.USER)) {
throw new Exception("Delegation failed");
}
}
}
/*
* Copyright (c) 2012, 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.
*
* 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.
*/
/*
* @test
* @bug 6355584
* @summary Introduce constrained Kerberos delegation
* @compile -XDignore.symbol.file S4U2self.java
* @run main/othervm -Dsun.security.krb5.debug=false S4U2self krb5 0
* @run main/othervm/fail -Dsun.security.krb5.debug=false S4U2self krb5 1
* @run main/othervm/fail -Dsun.security.krb5.debug=false S4U2self krb5 2
* @run main/othervm/fail -Dsun.security.krb5.debug=false S4U2self krb5 3
* @run main/othervm/fail -Dsun.security.krb5.debug=false S4U2self krb5 4
* @run main/othervm/fail -Dsun.security.krb5.debug=false S4U2self krb5 5
* @run main/othervm -Dsun.security.krb5.debug=false S4U2self spnego
*/
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.ietf.jgss.Oid;
import sun.security.jgss.GSSUtil;
public class S4U2self {
public static void main(String[] args) throws Exception {
// Test case, different policy settings in KDC:
// | ALLOW_S4U2SELF on
// | USER USER2 none
// ALLOW_S4U2PORXY |-------------------------
// USER to BACKEND | 0 1 2
// USER2 to BACKEND | 3
// USER to SERVER | 4
// none | 5
//
// 0 should succeed, all other fail
int test = 0;
Oid mech;
if (args[0].equals("spnego")) {
mech = GSSUtil.GSS_SPNEGO_MECH_OID;
} else if (args[0].contains("krb5")) {
mech = GSSUtil.GSS_KRB5_MECH_OID;
test = Integer.parseInt(args[1]);
} else {
throw new Exception("Unknown mech");
}
OneKDC kdc = new OneKDC(null);
kdc.writeJAASConf();
switch (test) {
case 1:
kdc.setOption(KDC.Option.ALLOW_S4U2SELF, Arrays.asList(
new String[]{OneKDC.USER2 + "@" + OneKDC.REALM}));
break;
case 2:
// No S4U2self
break;
default:
kdc.setOption(KDC.Option.ALLOW_S4U2SELF, Arrays.asList(
new String[]{OneKDC.USER + "@" + OneKDC.REALM}));
break;
}
Map<String,List<String>> map = new HashMap<>();
switch (test) {
case 3:
map.put(OneKDC.USER2 + "@" + OneKDC.REALM, Arrays.asList(
new String[]{OneKDC.BACKEND + "@" + OneKDC.REALM}));
kdc.setOption(KDC.Option.ALLOW_S4U2PROXY, map);
break;
case 4:
map.put(OneKDC.USER + "@" + OneKDC.REALM, Arrays.asList(
new String[]{OneKDC.SERVER + "@" + OneKDC.REALM}));
kdc.setOption(KDC.Option.ALLOW_S4U2PROXY, map);
break;
case 5:
// No S4U2proxy set
break;
default:
map.put(OneKDC.USER + "@" + OneKDC.REALM, Arrays.asList(
new String[]{OneKDC.BACKEND + "@" + OneKDC.REALM}));
kdc.setOption(KDC.Option.ALLOW_S4U2PROXY, map);
break;
}
Context c, s;
c = Context.fromJAAS("client");
c = c.impersonate(OneKDC.USER2);
c.status();
c.startAsClient(OneKDC.BACKEND, mech);
s = Context.fromJAAS("backend");
s.startAsServer(mech);
Context.handshake(c, s);
Context.transmit("i say high --", c, s);
Context.transmit(" you say low", s, c);
c.status();
s.status();
String n1 = c.x().getSrcName().toString().split("@")[0];
String n2 = s.x().getSrcName().toString().split("@")[0];
if (!n1.equals(OneKDC.USER2) || !n2.equals(OneKDC.USER2)) {
throw new Exception("Impersonate failed");
}
s.dispose();
c.dispose();
}
}
/*
* Copyright (c) 2012, 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.
*
* 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.
*/
/*
* @test
* @bug 6355584
* @summary Introduce constrained Kerberos delegation
* @compile -XDignore.symbol.file S4U2selfAsServer.java
* @run main/othervm S4U2selfAsServer krb5
* @run main/othervm S4U2selfAsServer spnego
*/
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.ietf.jgss.Oid;
import sun.security.jgss.GSSUtil;
public class S4U2selfAsServer {
public static void main(String[] args) throws Exception {
Oid mech;
if (args[0].equals("spnego")) {
mech = GSSUtil.GSS_SPNEGO_MECH_OID;
} else if (args[0].contains("krb5")) {
mech = GSSUtil.GSS_KRB5_MECH_OID;
} else {
throw new Exception("Unknown mech");
}
OneKDC kdc = new OneKDC(null);
kdc.writeJAASConf();
kdc.setOption(KDC.Option.PREAUTH_REQUIRED, false);
Map<String,List<String>> map = new HashMap<>();
map.put(OneKDC.SERVER + "@" + OneKDC.REALM, Arrays.asList(
new String[]{OneKDC.BACKEND + "@" + OneKDC.REALM}));
kdc.setOption(KDC.Option.ALLOW_S4U2PROXY, map);
kdc.setOption(KDC.Option.ALLOW_S4U2SELF, Arrays.asList(
new String[]{OneKDC.SERVER + "@" + OneKDC.REALM}));
Context s, b;
s = Context.fromJAAS("server");
b = Context.fromJAAS("backend");
s.startAsServer(null, mech, false);
Context p = s.impersonate(OneKDC.USER);
p.startAsClient(OneKDC.BACKEND, mech);
b.startAsServer(mech);
Context.handshake(p, b);
p.startAsClient(OneKDC.BACKEND, mech);
b.startAsServer(mech);
Context.handshake(p, b);
}
}
/*
* Copyright (c) 2012, 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.
*
* 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.
*/
/*
* @test
* @bug 6355584
* @summary Introduce constrained Kerberos delegation
* @compile -XDignore.symbol.file S4U2selfAsServerGSS.java
* @run main/othervm -Djavax.security.auth.useSubjectCredsOnly=false S4U2selfAsServerGSS krb5
* @run main/othervm -Djavax.security.auth.useSubjectCredsOnly=false S4U2selfAsServerGSS spnego
*/
import java.io.File;
import java.io.FileOutputStream;
import java.security.Security;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.ietf.jgss.Oid;
import sun.security.jgss.GSSUtil;
public class S4U2selfAsServerGSS {
public static void main(String[] args) throws Exception {
Oid mech;
if (args[0].equals("spnego")) {
mech = GSSUtil.GSS_SPNEGO_MECH_OID;
} else if (args[0].contains("krb5")) {
mech = GSSUtil.GSS_KRB5_MECH_OID;
} else {
throw new Exception("Unknown mech");
}
OneKDC kdc = new OneKDC(null);
kdc.writeJAASConf();
kdc.setOption(KDC.Option.PREAUTH_REQUIRED, false);
Map<String,List<String>> map = new HashMap<>();
map.put(OneKDC.SERVER + "@" + OneKDC.REALM, Arrays.asList(
new String[]{OneKDC.SERVER + "@" + OneKDC.REALM}));
kdc.setOption(KDC.Option.ALLOW_S4U2PROXY, map);
kdc.setOption(KDC.Option.ALLOW_S4U2SELF, Arrays.asList(
new String[]{OneKDC.SERVER + "@" + OneKDC.REALM}));
Context s, b;
System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");
System.setProperty("java.security.auth.login.config", OneKDC.JAAS_CONF);
File f = new File(OneKDC.JAAS_CONF);
FileOutputStream fos = new FileOutputStream(f);
fos.write((
"com.sun.security.jgss.krb5.accept {\n" +
" com.sun.security.auth.module.Krb5LoginModule required\n" +
" principal=\"" + OneKDC.SERVER + "\"\n" +
" useKeyTab=true\n" +
" storeKey=true;\n};\n"
).getBytes());
fos.close();
Security.setProperty("auth.login.defaultCallbackHandler", "OneKDC$CallbackForClient");
s = Context.fromThinAir();
b = Context.fromThinAir();
s.startAsServer(mech);
Context p = s.impersonate(OneKDC.USER);
p.startAsClient(OneKDC.SERVER, mech);
b.startAsServer(mech);
Context.handshake(p, b);
String n1 = p.x().getSrcName().toString().split("@")[0];
String n2 = b.x().getSrcName().toString().split("@")[0];
if (!n1.equals(OneKDC.USER) || !n2.equals(OneKDC.USER)) {
throw new Exception("Delegation failed");
}
}
}
/*
* Copyright (c) 2012, 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.
*
* 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.
*/
/*
* @test
* @bug 6355584
* @summary Introduce constrained Kerberos delegation
* @compile -XDignore.symbol.file S4U2selfGSS.java
* @run main/othervm -Dsun.security.krb5.debug=false S4U2selfGSS krb5
* @run main/othervm -Dsun.security.krb5.debug=false S4U2selfGSS spnego
*/
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.ietf.jgss.Oid;
import sun.security.jgss.GSSUtil;
public class S4U2selfGSS {
public static void main(String[] args) throws Exception {
Oid mech;
if (args[0].equals("spnego")) {
mech = GSSUtil.GSS_SPNEGO_MECH_OID;
} else if (args[0].contains("krb5")) {
mech = GSSUtil.GSS_KRB5_MECH_OID;
} else {
throw new Exception("Unknown mech");
}
OneKDC kdc = new OneKDC(null);
kdc.writeJAASConf();
kdc.setOption(KDC.Option.ALLOW_S4U2SELF, Arrays.asList(
new String[]{OneKDC.USER + "@" + OneKDC.REALM}));
Map<String,List<String>> map = new HashMap<>();
map.put(OneKDC.USER + "@" + OneKDC.REALM, Arrays.asList(
new String[]{OneKDC.SERVER + "@" + OneKDC.REALM}));
kdc.setOption(KDC.Option.ALLOW_S4U2PROXY, map);
Context c, s;
System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");
c = Context.fromThinAir();
s = Context.fromThinAir();
c = c.impersonate(OneKDC.USER2);
c.startAsClient(OneKDC.SERVER, mech);
s.startAsServer(mech);
Context.handshake(c, s);
String n1 = c.x().getSrcName().toString().split("@")[0];
String n2 = s.x().getSrcName().toString().split("@")[0];
if (!n1.equals(OneKDC.USER2) || !n2.equals(OneKDC.USER2)) {
throw new Exception("Impersonate failed");
}
s.dispose();
c.dispose();
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册