8272162: S4U2Self ticket without forwardable flag
Reviewed-by: valeriep
This commit is contained in:
parent
dd73e3cea2
commit
ab867f6c7c
src
java.base/share/conf/security
java.security.jgss/share/classes/sun/security
test/jdk/sun/security/krb5/auto
@ -1365,3 +1365,29 @@ jdk.tls.alpnCharset=ISO_8859_1
|
||||
# The default pattern value allows any object factory class specified by the reference
|
||||
# instance to recreate the referenced object.
|
||||
#jdk.jndi.object.factoriesFilter=*
|
||||
|
||||
#
|
||||
# Policy for non-forwardable service ticket in a S4U2proxy request
|
||||
#
|
||||
# The Service for User to Proxy (S4U2proxy) Kerberos extension enables a middle service
|
||||
# to obtain a service ticket to another service on behalf of a user. It requires that
|
||||
# the user's service ticket to the first service has the forwardable flag set [1].
|
||||
# However, some KDC implementations ignore this requirement and accept service tickets
|
||||
# with the flag unset.
|
||||
#
|
||||
# If this security property is set to "true", then
|
||||
#
|
||||
# 1) The user service ticket, when obtained by the middle service after a S4U2self
|
||||
# impersonation, is not required to have the forwardable flag set; and,
|
||||
#
|
||||
# 2) If a S4U2proxy request receives a KRB_ERROR of the KDC_ERR_BADOPTION error code
|
||||
# and the ticket to the middle service is not forwardable, OpenJDK will try the same
|
||||
# request with another KDC instead of treating it as a fatal failure.
|
||||
#
|
||||
# The default value is "false".
|
||||
#
|
||||
# If a system property of the same name is also specified, it supersedes the
|
||||
# security property value defined here.
|
||||
#
|
||||
# [1] https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-sfu/bde93b0e-f3c9-4ddf-9f44-e1453be7af5a
|
||||
#jdk.security.krb5.s4u2proxy.acceptNonForwardableServiceTicket=false
|
||||
|
@ -45,7 +45,6 @@ import javax.security.auth.kerberos.ServicePermission;
|
||||
import javax.security.auth.kerberos.KerberosCredMessage;
|
||||
import javax.security.auth.kerberos.KerberosPrincipal;
|
||||
import javax.security.auth.kerberos.KerberosTicket;
|
||||
import sun.security.krb5.internal.Ticket;
|
||||
import sun.security.krb5.internal.AuthorizationData;
|
||||
|
||||
/**
|
||||
@ -120,9 +119,12 @@ class Krb5Context implements GSSContextSpi {
|
||||
// XXX See if the required info from these can be extracted and
|
||||
// stored elsewhere
|
||||
private Credentials tgt;
|
||||
|
||||
// On the Initiator side, contains the final TGS to a service on both
|
||||
// delegation and no-delegation scenarios.
|
||||
// On the Acceptor side, contains a user TGS usable for delegation.
|
||||
private Credentials serviceCreds;
|
||||
private KrbApReq apReq;
|
||||
Ticket serviceTicket;
|
||||
private final GSSCaller caller;
|
||||
private static final boolean DEBUG = Krb5Util.DEBUG;
|
||||
|
||||
@ -548,7 +550,7 @@ class Krb5Context implements GSSContextSpi {
|
||||
delegatedCred = new Krb5ProxyCredential(
|
||||
Krb5InitCredential.getInstance(
|
||||
GSSCaller.CALLER_ACCEPT, myName, lifetime),
|
||||
peerName, serviceTicket);
|
||||
peerName, serviceCreds);
|
||||
} catch (GSSException gsse) {
|
||||
// OK, delegatedCred is null then
|
||||
}
|
||||
@ -623,13 +625,13 @@ class Krb5Context implements GSSContextSpi {
|
||||
"No TGT available");
|
||||
}
|
||||
myName = (Krb5NameElement) myCred.getName();
|
||||
final Krb5ProxyCredential second;
|
||||
final Krb5ProxyCredential proxyCreds;
|
||||
if (myCred instanceof Krb5InitCredential) {
|
||||
second = null;
|
||||
proxyCreds = null;
|
||||
tgt = ((Krb5InitCredential) myCred).getKrb5Credentials();
|
||||
} else {
|
||||
second = (Krb5ProxyCredential) myCred;
|
||||
tgt = second.self.getKrb5Credentials();
|
||||
proxyCreds = (Krb5ProxyCredential) myCred;
|
||||
tgt = proxyCreds.self.getKrb5Credentials();
|
||||
}
|
||||
|
||||
checkPermission(peerName.getKrb5PrincipalName().getName(),
|
||||
@ -657,9 +659,9 @@ class Krb5Context implements GSSContextSpi {
|
||||
GSSCaller.CALLER_UNKNOWN,
|
||||
// since it's useSubjectCredsOnly here,
|
||||
// don't worry about the null
|
||||
second == null ?
|
||||
proxyCreds == null ?
|
||||
myName.getKrb5PrincipalName().getName():
|
||||
second.getName().getKrb5PrincipalName().getName(),
|
||||
proxyCreds.getName().getKrb5PrincipalName().getName(),
|
||||
peerName.getKrb5PrincipalName().getName());
|
||||
}});
|
||||
kerbTicket = tmp;
|
||||
@ -690,15 +692,15 @@ class Krb5Context implements GSSContextSpi {
|
||||
"the subject");
|
||||
}
|
||||
// Get Service ticket using the Kerberos protocols
|
||||
if (second == null) {
|
||||
if (proxyCreds == null) {
|
||||
serviceCreds = Credentials.acquireServiceCreds(
|
||||
peerName.getKrb5PrincipalName().getName(),
|
||||
tgt);
|
||||
} else {
|
||||
serviceCreds = Credentials.acquireS4U2proxyCreds(
|
||||
peerName.getKrb5PrincipalName().getName(),
|
||||
second.tkt,
|
||||
second.getName().getKrb5PrincipalName(),
|
||||
proxyCreds.userCreds,
|
||||
proxyCreds.getName().getKrb5PrincipalName(),
|
||||
tgt);
|
||||
}
|
||||
if (GSSUtil.useSubjectCredsOnly(caller)) {
|
||||
@ -844,7 +846,7 @@ class Krb5Context implements GSSContextSpi {
|
||||
retVal = new AcceptSecContextToken(this,
|
||||
token.getKrbApReq()).encode();
|
||||
}
|
||||
serviceTicket = token.getKrbApReq().getCreds().getTicket();
|
||||
serviceCreds = token.getKrbApReq().getCreds();
|
||||
myCred = null;
|
||||
state = STATE_DONE;
|
||||
} else {
|
||||
|
@ -392,7 +392,7 @@ public class Krb5InitCredential
|
||||
Krb5NameElement kname = (Krb5NameElement)name;
|
||||
Credentials newCred = Credentials.acquireS4U2selfCreds(
|
||||
kname.getKrb5PrincipalName(), krb5Credentials);
|
||||
return new Krb5ProxyCredential(this, kname, newCred.getTicket());
|
||||
return new Krb5ProxyCredential(this, kname, newCred);
|
||||
} catch (IOException | KrbException ke) {
|
||||
GSSException ge =
|
||||
new GSSException(GSSException.FAILURE, -1,
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2021, 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
|
||||
@ -33,7 +33,6 @@ import java.io.IOException;
|
||||
|
||||
import sun.security.krb5.Credentials;
|
||||
import sun.security.krb5.KrbException;
|
||||
import sun.security.krb5.internal.Ticket;
|
||||
|
||||
import javax.security.auth.kerberos.KerberosTicket;
|
||||
|
||||
@ -50,23 +49,23 @@ public class Krb5ProxyCredential
|
||||
implements Krb5CredElement {
|
||||
|
||||
public final Krb5InitCredential self; // the middle server
|
||||
private final Krb5NameElement client; // the client
|
||||
private final Krb5NameElement user; // the user
|
||||
|
||||
// The ticket with cname=client and sname=self. This can be a normal
|
||||
// service ticket or an S4U2self ticket.
|
||||
public final Ticket tkt;
|
||||
// The creds with cname=user and sname=self. The ticket inside can
|
||||
// be either a normal service ticket or an S4U2self ticket.
|
||||
public final Credentials userCreds;
|
||||
|
||||
Krb5ProxyCredential(Krb5InitCredential self, Krb5NameElement client,
|
||||
Ticket tkt) {
|
||||
Krb5ProxyCredential(Krb5InitCredential self, Krb5NameElement user,
|
||||
Credentials userCreds) {
|
||||
this.self = self;
|
||||
this.tkt = tkt;
|
||||
this.client = client;
|
||||
this.userCreds = userCreds;
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
// The client name behind the proxy
|
||||
// The user name behind the proxy
|
||||
@Override
|
||||
public final Krb5NameElement getName() throws GSSException {
|
||||
return client;
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -130,7 +129,7 @@ public class Krb5ProxyCredential
|
||||
Credentials proxyCreds = Krb5Util.ticketToCreds(proxy);
|
||||
return new Krb5ProxyCredential(initiator,
|
||||
Krb5NameElement.getInstance(proxyCreds.getClient()),
|
||||
proxyCreds.getTicket());
|
||||
proxyCreds);
|
||||
} else {
|
||||
return initiator;
|
||||
}
|
||||
|
@ -35,6 +35,8 @@ import sun.security.action.GetPropertyAction;
|
||||
import sun.security.krb5.internal.*;
|
||||
import sun.security.krb5.internal.ccache.CredentialsCache;
|
||||
import sun.security.krb5.internal.crypto.EType;
|
||||
import sun.security.util.SecurityProperties;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
@ -64,6 +66,10 @@ public class Credentials {
|
||||
static boolean alreadyLoaded = false;
|
||||
private static boolean alreadyTried = false;
|
||||
|
||||
public static final boolean S4U2PROXY_ACCEPT_NON_FORWARDABLE
|
||||
= "true".equalsIgnoreCase(SecurityProperties.privilegedGetOverridable(
|
||||
"jdk.security.krb5.s4u2proxy.acceptNonForwardableServiceTicket"));
|
||||
|
||||
private Credentials proxy = null;
|
||||
|
||||
public Credentials getProxy() {
|
||||
@ -97,7 +103,7 @@ public class Credentials {
|
||||
this.authzData = authzData;
|
||||
}
|
||||
|
||||
// Warning: called by NativeCreds.c and nativeccache.c
|
||||
// Warning: also called by NativeCreds.c and nativeccache.c
|
||||
public Credentials(Ticket new_ticket,
|
||||
PrincipalName new_client,
|
||||
PrincipalName new_client_alias,
|
||||
@ -478,7 +484,7 @@ public class Credentials {
|
||||
*
|
||||
* @param service the name of service principal using format
|
||||
* components@realm
|
||||
* @param ccreds client's initial credential.
|
||||
* @param initCreds client's initial credential.
|
||||
* @exception IOException if an error occurs in reading the credentials
|
||||
* cache
|
||||
* @exception KrbException if an error occurs specific to Kerberos
|
||||
@ -486,21 +492,21 @@ public class Credentials {
|
||||
*/
|
||||
|
||||
public static Credentials acquireServiceCreds(String service,
|
||||
Credentials ccreds)
|
||||
Credentials initCreds)
|
||||
throws KrbException, IOException {
|
||||
return CredentialsUtil.acquireServiceCreds(service, ccreds);
|
||||
return CredentialsUtil.acquireServiceCreds(service, initCreds);
|
||||
}
|
||||
|
||||
public static Credentials acquireS4U2selfCreds(PrincipalName user,
|
||||
Credentials ccreds) throws KrbException, IOException {
|
||||
return CredentialsUtil.acquireS4U2selfCreds(user, ccreds);
|
||||
Credentials middleTGT) throws KrbException, IOException {
|
||||
return CredentialsUtil.acquireS4U2selfCreds(user, middleTGT);
|
||||
}
|
||||
|
||||
public static Credentials acquireS4U2proxyCreds(String service,
|
||||
Ticket second, PrincipalName client, Credentials ccreds)
|
||||
Credentials userCreds, PrincipalName client, Credentials middleTGT)
|
||||
throws KrbException, IOException {
|
||||
return CredentialsUtil.acquireS4U2proxyCreds(
|
||||
service, second, client, ccreds);
|
||||
service, userCreds, client, middleTGT);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -188,21 +188,22 @@ public final class KdcComm {
|
||||
this.realm = realm;
|
||||
}
|
||||
|
||||
public byte[] send(byte[] obuf)
|
||||
public byte[] send(KrbKdcReq req)
|
||||
throws IOException, KrbException {
|
||||
int udpPrefLimit = getRealmSpecificValue(
|
||||
realm, "udp_preference_limit", defaultUdpPrefLimit);
|
||||
|
||||
byte[] obuf = req.encoding();
|
||||
boolean useTCP = (udpPrefLimit > 0 &&
|
||||
(obuf != null && obuf.length > udpPrefLimit));
|
||||
|
||||
return send(obuf, useTCP);
|
||||
return send(req, useTCP);
|
||||
}
|
||||
|
||||
private byte[] send(byte[] obuf, boolean useTCP)
|
||||
private byte[] send(KrbKdcReq req, boolean useTCP)
|
||||
throws IOException, KrbException {
|
||||
|
||||
if (obuf == null)
|
||||
if (req == null)
|
||||
return null;
|
||||
Config cfg = Config.getInstance();
|
||||
|
||||
@ -225,12 +226,12 @@ public final class KdcComm {
|
||||
}
|
||||
byte[] ibuf = null;
|
||||
try {
|
||||
ibuf = sendIfPossible(obuf, tempKdc.next(), useTCP);
|
||||
ibuf = sendIfPossible(req, tempKdc.next(), useTCP);
|
||||
} catch(Exception first) {
|
||||
boolean ok = false;
|
||||
while(tempKdc.hasNext()) {
|
||||
try {
|
||||
ibuf = sendIfPossible(obuf, tempKdc.next(), useTCP);
|
||||
ibuf = sendIfPossible(req, tempKdc.next(), useTCP);
|
||||
ok = true;
|
||||
break;
|
||||
} catch(Exception ignore) {}
|
||||
@ -243,13 +244,13 @@ public final class KdcComm {
|
||||
return ibuf;
|
||||
}
|
||||
|
||||
// send the AS Request to the specified KDC
|
||||
// send the KDC Request to the specified KDC
|
||||
// failover to using TCP if useTCP is not set and response is too big
|
||||
private byte[] sendIfPossible(byte[] obuf, String tempKdc, boolean useTCP)
|
||||
private byte[] sendIfPossible(KrbKdcReq req, String tempKdc, boolean useTCP)
|
||||
throws IOException, KrbException {
|
||||
|
||||
try {
|
||||
byte[] ibuf = send(obuf, tempKdc, useTCP);
|
||||
byte[] ibuf = send(req, tempKdc, useTCP);
|
||||
KRBError ke = null;
|
||||
try {
|
||||
ke = new KRBError(ibuf);
|
||||
@ -259,10 +260,17 @@ public final class KdcComm {
|
||||
if (ke != null) {
|
||||
if (ke.getErrorCode() ==
|
||||
Krb5.KRB_ERR_RESPONSE_TOO_BIG) {
|
||||
ibuf = send(obuf, tempKdc, true);
|
||||
ibuf = send(req, tempKdc, true);
|
||||
} else if (ke.getErrorCode() ==
|
||||
Krb5.KDC_ERR_SVC_UNAVAILABLE) {
|
||||
throw new KrbException("A service is not available");
|
||||
} else if (ke.getErrorCode() == Krb5.KDC_ERR_BADOPTION
|
||||
&& Credentials.S4U2PROXY_ACCEPT_NON_FORWARDABLE
|
||||
&& req instanceof KrbTgsReq tgsReq) {
|
||||
Credentials extra = tgsReq.getAdditionalCreds();
|
||||
if (extra != null && !extra.isForwardable()) {
|
||||
throw new KrbException("S4U2Proxy with non-forwardable ticket");
|
||||
}
|
||||
}
|
||||
}
|
||||
KdcAccessibility.removeBad(tempKdc);
|
||||
@ -278,12 +286,12 @@ public final class KdcComm {
|
||||
}
|
||||
}
|
||||
|
||||
// send the AS Request to the specified KDC
|
||||
// send the KDC Request to the specified KDC
|
||||
|
||||
private byte[] send(byte[] obuf, String tempKdc, boolean useTCP)
|
||||
private byte[] send(KrbKdcReq req, String tempKdc, boolean useTCP)
|
||||
throws IOException, KrbException {
|
||||
|
||||
if (obuf == null)
|
||||
if (req == null)
|
||||
return null;
|
||||
|
||||
int port = Krb5.KDC_INET_DEFAULT_PORT;
|
||||
@ -336,6 +344,7 @@ public final class KdcComm {
|
||||
port = tempPort;
|
||||
}
|
||||
|
||||
byte[] obuf = req.encoding();
|
||||
if (DEBUG) {
|
||||
System.out.println(">>> KrbKdcReq send: kdc=" + kdc
|
||||
+ (useTCP ? " TCP:":" UDP:")
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2021, 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
|
||||
@ -42,7 +42,7 @@ import java.util.Arrays;
|
||||
* This class encapsulates the KRB-AS-REQ message that the client
|
||||
* sends to the KDC.
|
||||
*/
|
||||
public class KrbAsReq {
|
||||
public class KrbAsReq extends KrbKdcReq {
|
||||
private ASReq asReqMessg;
|
||||
|
||||
private boolean DEBUG = Krb5.DEBUG;
|
||||
@ -165,10 +165,7 @@ public class KrbAsReq {
|
||||
asReqMessg = new ASReq(
|
||||
paData,
|
||||
kdc_req_body);
|
||||
}
|
||||
|
||||
byte[] encoding() throws IOException, Asn1Exception {
|
||||
return asReqMessg.asn1Encode();
|
||||
obuf = asReqMessg.asn1Encode();
|
||||
}
|
||||
|
||||
// Used by KrbAsRep to validate AS-REP
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2010, 2021, 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
|
||||
@ -342,7 +342,7 @@ public final class KrbAsReqBuilder {
|
||||
}
|
||||
try {
|
||||
req = build(pakey, referralsState);
|
||||
rep = new KrbAsRep(comm.send(req.encoding()));
|
||||
rep = new KrbAsRep(comm.send(req));
|
||||
return this;
|
||||
} catch (KrbException ke) {
|
||||
if (!preAuthFailedOnce && (
|
||||
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2021, 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;
|
||||
|
||||
/**
|
||||
* Parent class for KrbAsReq and KrbTgsReq.
|
||||
*/
|
||||
abstract class KrbKdcReq {
|
||||
|
||||
protected byte[] obuf;
|
||||
|
||||
public byte[] encoding() {
|
||||
return obuf;
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2021, 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
|
||||
@ -43,7 +43,7 @@ import java.io.IOException;
|
||||
public class KrbTgsRep extends KrbKdcRep {
|
||||
private TGSRep rep;
|
||||
private Credentials creds;
|
||||
private Ticket secondTicket;
|
||||
private Credentials additionalCreds;
|
||||
|
||||
KrbTgsRep(byte[] ibuf, KrbTgsReq tgsReq)
|
||||
throws KrbException, IOException {
|
||||
@ -115,7 +115,7 @@ public class KrbTgsRep extends KrbKdcRep {
|
||||
enc_part.caddr
|
||||
);
|
||||
this.rep = rep;
|
||||
this.secondTicket = tgsReq.getSecondTicket();
|
||||
this.additionalCreds = tgsReq.getAdditionalCreds();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -126,7 +126,8 @@ public class KrbTgsRep extends KrbKdcRep {
|
||||
}
|
||||
|
||||
sun.security.krb5.internal.ccache.Credentials setCredentials() {
|
||||
return new sun.security.krb5.internal.ccache.Credentials(rep, secondTicket);
|
||||
return new sun.security.krb5.internal.ccache.Credentials(
|
||||
rep, additionalCreds == null ? null : additionalCreds.ticket);
|
||||
}
|
||||
|
||||
private static boolean isReferralSname(PrincipalName sname) {
|
||||
|
@ -42,7 +42,7 @@ import java.util.Arrays;
|
||||
* This class encapsulates a Kerberos TGS-REQ that is sent from the
|
||||
* client to the KDC.
|
||||
*/
|
||||
public class KrbTgsReq {
|
||||
public class KrbTgsReq extends KrbKdcReq {
|
||||
|
||||
private PrincipalName princName;
|
||||
private PrincipalName clientAlias;
|
||||
@ -50,34 +50,31 @@ public class KrbTgsReq {
|
||||
private PrincipalName serverAlias;
|
||||
private TGSReq tgsReqMessg;
|
||||
private KerberosTime ctime;
|
||||
private Ticket secondTicket = null;
|
||||
private Credentials additionalCreds = null;
|
||||
private boolean useSubkey = false;
|
||||
EncryptionKey tgsReqKey;
|
||||
|
||||
private byte[] obuf;
|
||||
private byte[] ibuf;
|
||||
|
||||
// Used in CredentialsUtil
|
||||
public KrbTgsReq(KDCOptions options, Credentials asCreds,
|
||||
PrincipalName cname, PrincipalName clientAlias,
|
||||
PrincipalName sname, PrincipalName serverAlias,
|
||||
Ticket[] additionalTickets, PAData[] extraPAs)
|
||||
throws KrbException, IOException {
|
||||
Credentials additionalCreds, PAData[] extraPAs)
|
||||
throws KrbException, IOException {
|
||||
this(options,
|
||||
asCreds,
|
||||
cname,
|
||||
clientAlias,
|
||||
sname,
|
||||
serverAlias,
|
||||
null, // KerberosTime from
|
||||
null, // KerberosTime till
|
||||
null, // KerberosTime rtime
|
||||
null, // int[] eTypes
|
||||
null, // HostAddresses addresses
|
||||
null, // AuthorizationData authorizationData
|
||||
additionalTickets,
|
||||
null, // EncryptionKey subKey
|
||||
extraPAs);
|
||||
asCreds,
|
||||
cname,
|
||||
clientAlias,
|
||||
sname,
|
||||
serverAlias,
|
||||
null, // KerberosTime from
|
||||
null, // KerberosTime till
|
||||
null, // KerberosTime rtime
|
||||
null, // int[] eTypes
|
||||
null, // HostAddresses addresses
|
||||
null, // AuthorizationData authorizationData
|
||||
additionalCreds,
|
||||
null, // EncryptionKey subKey
|
||||
extraPAs);
|
||||
}
|
||||
|
||||
// Called by Credentials, KrbCred
|
||||
@ -92,11 +89,11 @@ public class KrbTgsReq {
|
||||
int[] eTypes,
|
||||
HostAddresses addresses,
|
||||
AuthorizationData authorizationData,
|
||||
Ticket[] additionalTickets,
|
||||
Credentials additionalCreds,
|
||||
EncryptionKey subKey) throws KrbException, IOException {
|
||||
this(options, asCreds, asCreds.getClient(), asCreds.getClientAlias(),
|
||||
sname, serverAlias, from, till, rtime, eTypes,
|
||||
addresses, authorizationData, additionalTickets, subKey, null);
|
||||
addresses, authorizationData, additionalCreds, subKey, null);
|
||||
}
|
||||
|
||||
private KrbTgsReq(
|
||||
@ -112,7 +109,7 @@ public class KrbTgsReq {
|
||||
int[] eTypes,
|
||||
HostAddresses addresses,
|
||||
AuthorizationData authorizationData,
|
||||
Ticket[] additionalTickets,
|
||||
Credentials additionalCreds,
|
||||
EncryptionKey subKey,
|
||||
PAData[] extraPAs) throws KrbException, IOException {
|
||||
|
||||
@ -154,24 +151,24 @@ public class KrbTgsReq {
|
||||
if (!(asCreds.flags.get(KDCOptions.POSTDATED)))
|
||||
throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS);
|
||||
} else {
|
||||
if (from != null) from = null;
|
||||
if (from != null) from = null;
|
||||
}
|
||||
if (options.get(KDCOptions.RENEWABLE)) {
|
||||
if (!(asCreds.flags.get(KDCOptions.RENEWABLE)))
|
||||
throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS);
|
||||
} else {
|
||||
if (rtime != null) rtime = null;
|
||||
if (rtime != null) rtime = null;
|
||||
}
|
||||
if (options.get(KDCOptions.ENC_TKT_IN_SKEY) || options.get(KDCOptions.CNAME_IN_ADDL_TKT)) {
|
||||
if (additionalTickets == null)
|
||||
if (additionalCreds == 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];
|
||||
this.additionalCreds = additionalCreds;
|
||||
} else {
|
||||
if (additionalTickets != null)
|
||||
additionalTickets = null;
|
||||
if (additionalCreds != null)
|
||||
additionalCreds = null;
|
||||
}
|
||||
|
||||
tgsReqMessg = createRequest(
|
||||
@ -187,7 +184,7 @@ public class KrbTgsReq {
|
||||
eTypes,
|
||||
addresses,
|
||||
authorizationData,
|
||||
additionalTickets,
|
||||
additionalCreds,
|
||||
subKey,
|
||||
extraPAs);
|
||||
obuf = tgsReqMessg.asn1Encode();
|
||||
@ -206,34 +203,16 @@ public class KrbTgsReq {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a TGS request to the realm of the target.
|
||||
* @throws KrbException
|
||||
* @throws IOException
|
||||
*/
|
||||
public void send() throws IOException, KrbException {
|
||||
String realmStr = null;
|
||||
if (servName != null)
|
||||
realmStr = servName.getRealmString();
|
||||
KdcComm comm = new KdcComm(realmStr);
|
||||
ibuf = comm.send(obuf);
|
||||
}
|
||||
|
||||
public KrbTgsRep getReply()
|
||||
throws KrbException, IOException {
|
||||
return new KrbTgsRep(ibuf, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the request, waits for a reply, and returns the Credentials.
|
||||
* Used in Credentials, KrbCred, and internal/CredentialsUtil.
|
||||
*/
|
||||
public Credentials sendAndGetCreds() throws IOException, KrbException {
|
||||
KrbTgsRep tgs_rep = null;
|
||||
String kdc = null;
|
||||
send();
|
||||
tgs_rep = getReply();
|
||||
return tgs_rep.getCreds();
|
||||
String realmStr = servName != null
|
||||
? servName.getRealmString()
|
||||
: null;
|
||||
KdcComm comm = new KdcComm(realmStr);
|
||||
return new KrbTgsRep(comm.send(this), this).getCreds();
|
||||
}
|
||||
|
||||
KerberosTime getCtime() {
|
||||
@ -253,7 +232,7 @@ public class KrbTgsReq {
|
||||
int[] eTypes,
|
||||
HostAddresses addresses,
|
||||
AuthorizationData authorizationData,
|
||||
Ticket[] additionalTickets,
|
||||
Credentials additionalCreds,
|
||||
EncryptionKey subKey,
|
||||
PAData[] extraPAs)
|
||||
throws IOException, KrbException, UnknownHostException {
|
||||
@ -302,6 +281,8 @@ public class KrbTgsReq {
|
||||
KeyUsage.KU_TGS_REQ_AUTH_DATA_SESSKEY);
|
||||
}
|
||||
|
||||
Ticket[] additionalTickets = additionalCreds == null ? null
|
||||
: new Ticket[] { additionalCreds.getTicket() };
|
||||
KDCReqBody reqBody = new KDCReqBody(
|
||||
kdc_options,
|
||||
cname,
|
||||
@ -347,8 +328,8 @@ public class KrbTgsReq {
|
||||
return tgsReqMessg;
|
||||
}
|
||||
|
||||
Ticket getSecondTicket() {
|
||||
return secondTicket;
|
||||
Credentials getAdditionalCreds() {
|
||||
return additionalCreds;
|
||||
}
|
||||
|
||||
PrincipalName getClientAlias() {
|
||||
|
@ -32,7 +32,6 @@
|
||||
package sun.security.krb5.internal;
|
||||
|
||||
import sun.security.krb5.*;
|
||||
import sun.security.util.DerValue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
@ -55,17 +54,14 @@ public class CredentialsUtil {
|
||||
* Used by a middle server to acquire credentials on behalf of a
|
||||
* user to itself using the S4U2self extension.
|
||||
* @param user the user to impersonate
|
||||
* @param ccreds the TGT of the middle service
|
||||
* @param middleTGT the TGT of the middle service
|
||||
* @return the new creds (cname=user, sname=middle)
|
||||
*/
|
||||
public static Credentials acquireS4U2selfCreds(PrincipalName user,
|
||||
Credentials ccreds) throws KrbException, IOException {
|
||||
if (!ccreds.isForwardable()) {
|
||||
throw new KrbException("S4U2self needs a FORWARDABLE ticket");
|
||||
}
|
||||
PrincipalName sname = ccreds.getClient();
|
||||
Credentials middleTGT) throws KrbException, IOException {
|
||||
PrincipalName sname = middleTGT.getClient();
|
||||
String uRealm = user.getRealmString();
|
||||
String localRealm = ccreds.getClient().getRealmString();
|
||||
String localRealm = sname.getRealmString();
|
||||
if (!uRealm.equals(localRealm)) {
|
||||
// Referrals will be required because the middle service
|
||||
// and the user impersonated are on different realms.
|
||||
@ -73,25 +69,25 @@ public class CredentialsUtil {
|
||||
throw new KrbException("Cross-realm S4U2Self request not" +
|
||||
" possible when referrals are disabled.");
|
||||
}
|
||||
if (ccreds.getClientAlias() != null) {
|
||||
if (middleTGT.getClientAlias() != null) {
|
||||
// If the name was canonicalized, the user pick
|
||||
// has preference. This gives the possibility of
|
||||
// using FQDNs that KDCs may use to return referrals.
|
||||
// I.e.: a SVC/host.realm-2.com@REALM-1.COM name
|
||||
// may be used by REALM-1.COM KDC to return a
|
||||
// referral to REALM-2.COM.
|
||||
sname = ccreds.getClientAlias();
|
||||
sname = middleTGT.getClientAlias();
|
||||
}
|
||||
sname = new PrincipalName(sname.getNameType(),
|
||||
sname.getNameStrings(), new Realm(uRealm));
|
||||
}
|
||||
Credentials creds = serviceCreds(
|
||||
KDCOptions.with(KDCOptions.FORWARDABLE),
|
||||
ccreds, ccreds.getClient(), sname, user,
|
||||
middleTGT, middleTGT.getClient(), sname, user,
|
||||
null, new PAData[] {
|
||||
new PAData(Krb5.PA_FOR_USER,
|
||||
new PAForUserEnc(user,
|
||||
ccreds.getSessionKey()).asn1Encode()),
|
||||
middleTGT.getSessionKey()).asn1Encode()),
|
||||
new PAData(Krb5.PA_PAC_OPTIONS,
|
||||
new PaPacOptions()
|
||||
.setResourceBasedConstrainedDelegation(true)
|
||||
@ -101,7 +97,7 @@ public class CredentialsUtil {
|
||||
if (!creds.getClient().equals(user)) {
|
||||
throw new KrbException("S4U2self request not honored by KDC");
|
||||
}
|
||||
if (!creds.isForwardable()) {
|
||||
if (!creds.isForwardable() && !Credentials.S4U2PROXY_ACCEPT_NON_FORWARDABLE) {
|
||||
throw new KrbException("S4U2self ticket must be FORWARDABLE");
|
||||
}
|
||||
return creds;
|
||||
@ -111,17 +107,17 @@ public class CredentialsUtil {
|
||||
* 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)
|
||||
* @param userCreds containing the user's service ticket to the middle server
|
||||
* @param middleTGT the TGT of the middle server
|
||||
* @return the creds (cname=user, sname=backend)
|
||||
*/
|
||||
public static Credentials acquireS4U2proxyCreds(
|
||||
String backend, Ticket second,
|
||||
PrincipalName client, Credentials ccreds)
|
||||
String backend, Credentials userCreds,
|
||||
PrincipalName client, Credentials middleTGT)
|
||||
throws KrbException, IOException {
|
||||
PrincipalName backendPrincipal = new PrincipalName(backend);
|
||||
String backendRealm = backendPrincipal.getRealmString();
|
||||
String localRealm = ccreds.getClient().getRealmString();
|
||||
String localRealm = middleTGT.getClient().getRealmString();
|
||||
if (!backendRealm.equals(localRealm)) {
|
||||
// The middle service and the backend service are on
|
||||
// different realms, so referrals will be required.
|
||||
@ -136,8 +132,8 @@ public class CredentialsUtil {
|
||||
}
|
||||
Credentials creds = serviceCreds(KDCOptions.with(
|
||||
KDCOptions.CNAME_IN_ADDL_TKT, KDCOptions.FORWARDABLE),
|
||||
ccreds, ccreds.getClient(), backendPrincipal, null,
|
||||
new Ticket[] {second}, new PAData[] {
|
||||
middleTGT, middleTGT.getClient(), backendPrincipal, null,
|
||||
userCreds, new PAData[] {
|
||||
new PAData(Krb5.PA_PAC_OPTIONS,
|
||||
new PaPacOptions()
|
||||
.setResourceBasedConstrainedDelegation(true)
|
||||
@ -159,28 +155,30 @@ public class CredentialsUtil {
|
||||
* from the foreign KDC.
|
||||
*
|
||||
* @param service the name of service principal
|
||||
* @param ccreds client's initial credential
|
||||
* @param initCreds client's initial credential
|
||||
*/
|
||||
public static Credentials acquireServiceCreds(
|
||||
String service, Credentials ccreds)
|
||||
String service, Credentials initCreds)
|
||||
throws KrbException, IOException {
|
||||
PrincipalName sname = new PrincipalName(service,
|
||||
PrincipalName.KRB_NT_UNKNOWN);
|
||||
return serviceCreds(sname, ccreds);
|
||||
return serviceCreds(new KDCOptions(), initCreds,
|
||||
initCreds.getClient(),
|
||||
new PrincipalName(service, PrincipalName.KRB_NT_UNKNOWN),
|
||||
null, null,
|
||||
null, S4U2Type.NONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a TGT to another realm
|
||||
* @param localRealm this realm
|
||||
* @param serviceRealm the other realm, cannot equals to localRealm
|
||||
* @param ccreds TGT in this realm
|
||||
* @param localTGT 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)
|
||||
String serviceRealm, Credentials localTGT, boolean[] okAsDelegate)
|
||||
throws KrbException {
|
||||
|
||||
// Get a list of realms to traverse
|
||||
@ -192,7 +190,7 @@ public class CredentialsUtil {
|
||||
String newTgtRealm = null;
|
||||
|
||||
okAsDelegate[0] = true;
|
||||
for (cTgt = ccreds, i = 0; i < realms.length;) {
|
||||
for (cTgt = localTGT, i = 0; i < realms.length;) {
|
||||
tempService = PrincipalName.tgsService(serviceRealm, realms[i]);
|
||||
|
||||
if (DEBUG) {
|
||||
@ -309,10 +307,10 @@ public class CredentialsUtil {
|
||||
* This method does the real job to request the service credential.
|
||||
*/
|
||||
private static Credentials serviceCreds(
|
||||
PrincipalName service, Credentials ccreds)
|
||||
PrincipalName service, Credentials initCreds)
|
||||
throws KrbException, IOException {
|
||||
return serviceCreds(new KDCOptions(), ccreds,
|
||||
ccreds.getClient(), service, null, null,
|
||||
return serviceCreds(new KDCOptions(), initCreds,
|
||||
initCreds.getClient(), service, null, null,
|
||||
null, S4U2Type.NONE);
|
||||
}
|
||||
|
||||
@ -325,13 +323,13 @@ public class CredentialsUtil {
|
||||
private static Credentials serviceCreds(
|
||||
KDCOptions options, Credentials asCreds,
|
||||
PrincipalName cname, PrincipalName sname,
|
||||
PrincipalName user, Ticket[] additionalTickets,
|
||||
PrincipalName user, Credentials additionalCreds,
|
||||
PAData[] extraPAs, S4U2Type s4u2Type)
|
||||
throws KrbException, IOException {
|
||||
if (!Config.DISABLE_REFERRALS) {
|
||||
try {
|
||||
return serviceCredsReferrals(options, asCreds, cname, sname,
|
||||
s4u2Type, user, additionalTickets, extraPAs);
|
||||
s4u2Type, user, additionalCreds, extraPAs);
|
||||
} catch (KrbException e) {
|
||||
// Server may raise an error if CANONICALIZE is true.
|
||||
// Try CANONICALIZE false.
|
||||
@ -339,7 +337,7 @@ public class CredentialsUtil {
|
||||
}
|
||||
return serviceCredsSingle(options, asCreds, cname,
|
||||
asCreds.getClientAlias(), sname, sname, s4u2Type,
|
||||
user, additionalTickets, extraPAs);
|
||||
user, additionalCreds, extraPAs);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -350,7 +348,7 @@ public class CredentialsUtil {
|
||||
KDCOptions options, Credentials asCreds,
|
||||
PrincipalName cname, PrincipalName sname,
|
||||
S4U2Type s4u2Type, PrincipalName user,
|
||||
Ticket[] additionalTickets, PAData[] extraPAs)
|
||||
Credentials additionalCreds, PAData[] extraPAs)
|
||||
throws KrbException, IOException {
|
||||
options = new KDCOptions(options.toBooleanArray());
|
||||
options.set(KDCOptions.CANONICALIZE, true);
|
||||
@ -363,12 +361,12 @@ public class CredentialsUtil {
|
||||
while (referrals.size() <= Config.MAX_REFERRALS) {
|
||||
ReferralsCache.ReferralCacheEntry ref =
|
||||
ReferralsCache.get(cname, sname, user,
|
||||
additionalTickets, refSname.getRealmString());
|
||||
additionalCreds, refSname.getRealmString());
|
||||
String toRealm = null;
|
||||
if (ref == null) {
|
||||
creds = serviceCredsSingle(options, asCreds, cname,
|
||||
clientAlias, refSname, cSname, s4u2Type,
|
||||
user, additionalTickets, extraPAs);
|
||||
user, additionalCreds, extraPAs);
|
||||
PrincipalName server = creds.getServer();
|
||||
if (!refSname.equals(server)) {
|
||||
String[] serverNameStrings = server.getNameStrings();
|
||||
@ -380,7 +378,7 @@ public class CredentialsUtil {
|
||||
// Server Name (sname) has the following format:
|
||||
// krbtgt/TO-REALM.COM@FROM-REALM.COM
|
||||
ReferralsCache.put(cname, sname, user,
|
||||
additionalTickets, server.getRealmString(),
|
||||
additionalCreds, server.getRealmString(),
|
||||
serverNameStrings[1], creds);
|
||||
toRealm = serverNameStrings[1];
|
||||
isReferral = true;
|
||||
@ -398,13 +396,11 @@ public class CredentialsUtil {
|
||||
toRealm = handleS4U2ProxyReferral(asCreds,
|
||||
credsInOut, sname);
|
||||
creds = credsInOut[0];
|
||||
if (additionalTickets == null ||
|
||||
additionalTickets.length == 0 ||
|
||||
credsInOut[1] == null) {
|
||||
if (additionalCreds == null || credsInOut[1] == null) {
|
||||
throw new KrbException("Additional tickets expected" +
|
||||
" for S4U2Proxy.");
|
||||
}
|
||||
additionalTickets[0] = credsInOut[1].getTicket();
|
||||
additionalCreds = credsInOut[1];
|
||||
} else if (s4u2Type == S4U2Type.SELF) {
|
||||
handleS4U2SelfReferral(extraPAs, user, creds);
|
||||
}
|
||||
@ -436,7 +432,7 @@ public class CredentialsUtil {
|
||||
PrincipalName cname, PrincipalName clientAlias,
|
||||
PrincipalName refSname, PrincipalName sname,
|
||||
S4U2Type s4u2Type, PrincipalName user,
|
||||
Ticket[] additionalTickets, PAData[] extraPAs)
|
||||
Credentials additionalCreds, PAData[] extraPAs)
|
||||
throws KrbException, IOException {
|
||||
Credentials theCreds = null;
|
||||
boolean[] okAsDelegate = new boolean[]{true};
|
||||
@ -473,7 +469,7 @@ public class CredentialsUtil {
|
||||
" same realm");
|
||||
}
|
||||
KrbTgsReq req = new KrbTgsReq(options, asCreds, cname, clientAlias,
|
||||
refSname, sname, additionalTickets, extraPAs);
|
||||
refSname, sname, additionalCreds, extraPAs);
|
||||
theCreds = req.sendAndGetCreds();
|
||||
if (theCreds != null) {
|
||||
if (DEBUG) {
|
||||
|
@ -31,7 +31,6 @@
|
||||
package sun.security.krb5.internal;
|
||||
|
||||
import sun.security.krb5.*;
|
||||
import java.util.Vector;
|
||||
import sun.security.util.*;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
@ -192,8 +191,4 @@ public class KDCReq {
|
||||
true, (byte) msgType), bytes);
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
public byte[] asn1EncodeReqBody() throws Asn1Exception, IOException {
|
||||
return reqBody.asn1Encode(msgType);
|
||||
}
|
||||
}
|
||||
|
@ -112,10 +112,10 @@ final class ReferralsCache {
|
||||
* REALM-1.COM -> REALM-2.COM referral entry is removed from the cache.
|
||||
*/
|
||||
static synchronized void put(PrincipalName cname, PrincipalName service,
|
||||
PrincipalName user, Ticket[] userSvcTickets, String fromRealm,
|
||||
PrincipalName user, Credentials additionalCreds, String fromRealm,
|
||||
String toRealm, Credentials creds) {
|
||||
Ticket userSvcTicket = (userSvcTickets != null ?
|
||||
userSvcTickets[0] : null);
|
||||
Ticket userSvcTicket = (additionalCreds != null ?
|
||||
additionalCreds.getTicket() : null);
|
||||
ReferralCacheKey k = new ReferralCacheKey(cname, service,
|
||||
user, userSvcTicket);
|
||||
pruneExpired(k);
|
||||
@ -152,9 +152,9 @@ final class ReferralsCache {
|
||||
*/
|
||||
static synchronized ReferralCacheEntry get(PrincipalName cname,
|
||||
PrincipalName service, PrincipalName user,
|
||||
Ticket[] userSvcTickets, String fromRealm) {
|
||||
Ticket userSvcTicket = (userSvcTickets != null ?
|
||||
userSvcTickets[0] : null);
|
||||
Credentials additionalCreds, String fromRealm) {
|
||||
Ticket userSvcTicket = (additionalCreds != null ?
|
||||
additionalCreds.getTicket() : null);
|
||||
ReferralCacheKey k = new ReferralCacheKey(cname, service,
|
||||
user, userSvcTicket);
|
||||
pruneExpired(k);
|
||||
|
@ -85,12 +85,12 @@ public class Ticket implements Cloneable {
|
||||
|
||||
// Warning: called by NativeCreds.c and nativeccache.c
|
||||
public Ticket(byte[] data) throws Asn1Exception,
|
||||
RealmException, KrbApErrException, IOException {
|
||||
RealmException, KrbApErrException, IOException {
|
||||
init(new DerValue(data));
|
||||
}
|
||||
|
||||
public Ticket(DerValue encoding) throws Asn1Exception,
|
||||
RealmException, KrbApErrException, IOException {
|
||||
RealmException, KrbApErrException, IOException {
|
||||
init(encoding);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2008, 2021, 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
|
||||
@ -29,8 +29,11 @@ import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.*;
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.stream.Collectors;
|
||||
@ -249,6 +252,14 @@ public class KDC {
|
||||
* If true, will check if TGS-REQ contains a non-null addresses field.
|
||||
*/
|
||||
CHECK_ADDRESSES,
|
||||
/**
|
||||
* If true, S4U2self ticket is not set forwardable.
|
||||
*/
|
||||
S4U2SELF_NOT_FORWARDABLE,
|
||||
/**
|
||||
* If true, allow S4U2self ticket not forwardable.
|
||||
*/
|
||||
S4U2SELF_ALLOW_NOT_FORWARDABLE,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -441,12 +452,12 @@ public class KDC {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new principal to this realm with a random password
|
||||
* Adds a new principal to this realm with a generated password
|
||||
* @param user the principal's name. For a service principal, use the
|
||||
* form of host/f.q.d.n
|
||||
*/
|
||||
public void addPrincipalRandKey(String user) {
|
||||
addPrincipal(user, randomPassword());
|
||||
addPrincipal(user, randomPassword(user + "@" + realm));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -607,14 +618,29 @@ public class KDC {
|
||||
startServer(port, asDaemon);
|
||||
}
|
||||
/**
|
||||
* Generates a 32-char random password
|
||||
* Generates a 32-char password
|
||||
* @param user the string to generate from, random is null
|
||||
* @return the password
|
||||
*/
|
||||
private static char[] randomPassword() {
|
||||
char[] pass = new char[32];
|
||||
Random r = new Random();
|
||||
for (int i=0; i<31; i++)
|
||||
pass[i] = (char)('a' + r.nextInt(26));
|
||||
private static char[] randomPassword(String user) {
|
||||
char[] pass;
|
||||
if (user == null) {
|
||||
pass = new char[32];
|
||||
Random r = new Random();
|
||||
for (int i = 0; i < 31; i++) {
|
||||
pass[i] = (char) ('a' + r.nextInt(26));
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
pass = Base64.getEncoder().encodeToString(
|
||||
MessageDigest.getInstance("SHA-256").digest((user)
|
||||
.getBytes(StandardCharsets.UTF_8)))
|
||||
.substring(0, 32)
|
||||
.toCharArray();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
// The last char cannot be a number, otherwise, keyForUser()
|
||||
// believes it's a sign of kvno
|
||||
pass[31] = 'Z';
|
||||
@ -629,7 +655,7 @@ public class KDC {
|
||||
*/
|
||||
private static EncryptionKey generateRandomKey(int eType)
|
||||
throws KrbException {
|
||||
return genKey0(randomPassword(), "NOTHING", null, eType, null);
|
||||
return genKey0(randomPassword(null), "NOTHING", null, eType, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -790,7 +816,7 @@ public class KDC {
|
||||
service.getNameStrings(), service.getRealm());
|
||||
}
|
||||
try {
|
||||
System.out.println(realm + "> " + tgsReq.reqBody.cname +
|
||||
log(tgsReq.reqBody.cname +
|
||||
" sends TGS-REQ for " +
|
||||
service + ", " + tgsReq.reqBody.kdcOptions);
|
||||
KDCReqBody body = tgsReq.reqBody;
|
||||
@ -810,7 +836,7 @@ public class KDC {
|
||||
boolean allowForwardable = true;
|
||||
boolean isReferral = false;
|
||||
if (body.kdcOptions.get(KDCOptions.CANONICALIZE)) {
|
||||
System.out.println(realm + "> verifying referral for " +
|
||||
log("verifying referral for " +
|
||||
body.sname.getNameString());
|
||||
KDC referral = aliasReferrals.get(body.sname.getNameString());
|
||||
if (referral != null) {
|
||||
@ -819,7 +845,7 @@ public class KDC {
|
||||
PrincipalName.NAME_COMPONENT_SEPARATOR_STR +
|
||||
referral.getRealm(), PrincipalName.KRB_NT_SRV_INST,
|
||||
this.getRealm());
|
||||
System.out.println(realm + "> referral to " +
|
||||
log("referral to " +
|
||||
referral.getRealm());
|
||||
isReferral = true;
|
||||
}
|
||||
@ -842,14 +868,14 @@ public class KDC {
|
||||
// Finally, cname will be overwritten by PA-FOR-USER
|
||||
// if it exists.
|
||||
cname = etp.cname;
|
||||
System.out.println(realm + "> presenting a ticket of "
|
||||
log("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 + "> See PA_FOR_USER "
|
||||
log("See PA_FOR_USER "
|
||||
+ " in the name of " + p4u.name);
|
||||
}
|
||||
}
|
||||
@ -862,6 +888,9 @@ public class KDC {
|
||||
// allowed to send S4U2self, do not send an error.
|
||||
// Instead, send a ticket which is useless later.
|
||||
allowForwardable = false;
|
||||
} else if (options.get(Option.S4U2SELF_NOT_FORWARDABLE) == Boolean.TRUE) {
|
||||
// Requsted not forwardable
|
||||
allowForwardable = false;
|
||||
}
|
||||
cname = forUserCName;
|
||||
}
|
||||
@ -936,15 +965,16 @@ public class KDC {
|
||||
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);
|
||||
if (!tktEncPart.flags.get(Krb5.TKT_OPTS_FORWARDABLE)
|
||||
&& options.get(Option.S4U2SELF_ALLOW_NOT_FORWARDABLE) != Boolean.TRUE) {
|
||||
throw new KrbException(Krb5.KDC_ERR_BADOPTION);
|
||||
}
|
||||
PrincipalName client = tktEncPart.cname;
|
||||
System.out.println(realm + "> and an additional ticket of "
|
||||
log("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");
|
||||
log("S4U2proxy OK");
|
||||
} else {
|
||||
throw new KrbException(Krb5.KDC_ERR_BADOPTION);
|
||||
}
|
||||
@ -1066,7 +1096,7 @@ public class KDC {
|
||||
Realm.getDefault());
|
||||
}
|
||||
try {
|
||||
System.out.println(realm + "> " + asReq.reqBody.cname +
|
||||
log(asReq.reqBody.cname +
|
||||
" sends AS-REQ for " +
|
||||
service + ", " + asReq.reqBody.kdcOptions);
|
||||
|
||||
@ -1601,6 +1631,10 @@ public class KDC {
|
||||
return udpConsumerReady && tcpConsumerReady && dispatcherReady;
|
||||
}
|
||||
|
||||
void log(String s) {
|
||||
System.out.println(realm + ":" + port + "> " + s);
|
||||
}
|
||||
|
||||
public void terminate() {
|
||||
if (nativeKdc != null) {
|
||||
System.out.println("Killing kdc...");
|
||||
|
96
test/jdk/sun/security/krb5/auto/S4U2selfNotF.java
Normal file
96
test/jdk/sun/security/krb5/auto/S4U2selfNotF.java
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (c) 2021, 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 8272162
|
||||
* @summary S4U2Self ticket without forwardable flag
|
||||
* @library /test/lib
|
||||
* @compile -XDignore.symbol.file S4U2selfNotF.java
|
||||
* @run main jdk.test.lib.FileInstaller TestHosts TestHosts
|
||||
* @run main/othervm -Djdk.net.hosts.file=TestHosts
|
||||
* -Djdk.security.krb5.s4u2proxy.acceptNonForwardableServiceTicket=true
|
||||
* S4U2selfNotF
|
||||
*/
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import jdk.test.lib.Asserts;
|
||||
import sun.security.jgss.GSSUtil;
|
||||
import sun.security.krb5.Config;
|
||||
|
||||
public class S4U2selfNotF {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
// Create 2 KDCs that has almost the same settings
|
||||
OneKDC[] kdcs = new OneKDC[2];
|
||||
boolean[] touched = new boolean[2];
|
||||
for (int i = 0; i < 2; i++) {
|
||||
final int pos = i;
|
||||
kdcs[i] = new OneKDC(null) {
|
||||
protected byte[] processTgsReq(byte[] in) throws Exception {
|
||||
touched[pos] = true;
|
||||
return super.processTgsReq(in);
|
||||
}
|
||||
};
|
||||
kdcs[i].setOption(KDC.Option.ALLOW_S4U2SELF,
|
||||
List.of(OneKDC.USER + "@" + OneKDC.REALM));
|
||||
kdcs[i].setOption(KDC.Option.ALLOW_S4U2PROXY, Map.of(
|
||||
OneKDC.USER + "@" + OneKDC.REALM,
|
||||
List.of(OneKDC.BACKEND + "@" + OneKDC.REALM)));
|
||||
}
|
||||
kdcs[0].writeJAASConf();
|
||||
|
||||
// except that the 1st issues a non-forwardable S4U2self
|
||||
// ticket and only the 2nd accepts it
|
||||
kdcs[0].setOption(KDC.Option.S4U2SELF_NOT_FORWARDABLE, true);
|
||||
kdcs[1].setOption(KDC.Option.S4U2SELF_ALLOW_NOT_FORWARDABLE, true);
|
||||
|
||||
Files.write(Path.of(OneKDC.KRB5_CONF), String.format("""
|
||||
[libdefaults]
|
||||
default_realm = RABBIT.HOLE
|
||||
forwardable = true
|
||||
default_keytab_name = localkdc.ktab
|
||||
|
||||
[realms]
|
||||
RABBIT.HOLE = {
|
||||
kdc = kdc.rabbit.hole:%d kdc.rabbit.hole:%d
|
||||
}
|
||||
""", kdcs[0].getPort(), kdcs[1].getPort())
|
||||
.getBytes(StandardCharsets.UTF_8));
|
||||
Config.refresh();
|
||||
|
||||
Context c = Context.fromJAAS("client");
|
||||
c = c.impersonate(OneKDC.USER2);
|
||||
c.startAsClient(OneKDC.BACKEND, GSSUtil.GSS_KRB5_MECH_OID);
|
||||
c.take(new byte[0]);
|
||||
|
||||
Asserts.assertTrue(touched[0]); // get S4U2self from 1st one
|
||||
Asserts.assertTrue(touched[1]); // get S4U2proxy from 2nd one
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user