8215032: Support Kerberos cross-realm referrals (RFC 6806)
Reviewed-by: weijun
This commit is contained in:
parent
8ee8c48696
commit
5aae9ef0db
@ -474,6 +474,31 @@ networkaddress.cache.negative.ttl=10
|
||||
#
|
||||
krb5.kdc.bad.policy = tryLast
|
||||
|
||||
#
|
||||
# Kerberos cross-realm referrals (RFC 6806)
|
||||
#
|
||||
# OpenJDK's Kerberos client supports cross-realm referrals as defined in
|
||||
# RFC 6806. This allows to setup more dynamic environments in which clients
|
||||
# do not need to know in advance how to reach the realm of a target principal
|
||||
# (either a user or service).
|
||||
#
|
||||
# When a client issues an AS or a TGS request, the "canonicalize" option
|
||||
# is set to announce support of this feature. A KDC server may fulfill the
|
||||
# request or reply referring the client to a different one. If referred,
|
||||
# the client will issue a new request and the cycle repeats.
|
||||
#
|
||||
# In addition to referrals, the "canonicalize" option allows the KDC server
|
||||
# to change the client name in response to an AS request. For security reasons,
|
||||
# RFC 6806 (section 11) FAST scheme is enforced.
|
||||
#
|
||||
# Disable Kerberos cross-realm referrals. Value may be overwritten with a
|
||||
# System property (-Dsun.security.krb5.disableReferrals).
|
||||
sun.security.krb5.disableReferrals=false
|
||||
|
||||
# Maximum number of AS or TGS referrals to avoid infinite loops. Value may
|
||||
# be overwritten with a System property (-Dsun.security.krb5.maxReferrals).
|
||||
sun.security.krb5.maxReferrals=5
|
||||
|
||||
#
|
||||
# Algorithm restrictions for certification path (CertPath) processing
|
||||
#
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2019, 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
|
||||
@ -80,6 +80,11 @@ public final class KerberosPrincipal
|
||||
|
||||
public static final int KRB_NT_UID = 5;
|
||||
|
||||
/**
|
||||
* Enterprise name (alias)
|
||||
*/
|
||||
public static final int KRB_NT_ENTERPRISE = 10;
|
||||
|
||||
private transient String fullName;
|
||||
|
||||
private transient String realm;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2019, 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
|
||||
@ -218,7 +218,7 @@ public class Checksum {
|
||||
* @exception IOException if an I/O error occurs while reading encoded data.
|
||||
*
|
||||
*/
|
||||
private Checksum(DerValue encoding) throws Asn1Exception, IOException {
|
||||
public Checksum(DerValue encoding) throws Asn1Exception, IOException {
|
||||
DerValue der;
|
||||
if (encoding.getTag() != DerValue.tag_Sequence) {
|
||||
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2019, 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
|
||||
@ -48,6 +48,7 @@ import sun.net.dns.ResolverConfiguration;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
import sun.security.krb5.internal.crypto.EType;
|
||||
import sun.security.krb5.internal.Krb5;
|
||||
import sun.security.util.SecurityProperties;
|
||||
|
||||
/**
|
||||
* This class maintains key-value pairs of Kerberos configurable constants
|
||||
@ -56,6 +57,41 @@ import sun.security.krb5.internal.Krb5;
|
||||
|
||||
public class Config {
|
||||
|
||||
/**
|
||||
* {@systemProperty sun.security.krb5.disableReferrals} property
|
||||
* indicating whether or not cross-realm referrals (RFC 6806) are
|
||||
* enabled.
|
||||
*/
|
||||
public static final boolean DISABLE_REFERRALS;
|
||||
|
||||
/**
|
||||
* {@systemProperty sun.security.krb5.maxReferrals} property
|
||||
* indicating the maximum number of cross-realm referral
|
||||
* hops allowed.
|
||||
*/
|
||||
public static final int MAX_REFERRALS;
|
||||
|
||||
static {
|
||||
String disableReferralsProp =
|
||||
SecurityProperties.privilegedGetOverridable(
|
||||
"sun.security.krb5.disableReferrals");
|
||||
if (disableReferralsProp != null) {
|
||||
DISABLE_REFERRALS = "true".equalsIgnoreCase(disableReferralsProp);
|
||||
} else {
|
||||
DISABLE_REFERRALS = false;
|
||||
}
|
||||
|
||||
int maxReferralsValue = 5;
|
||||
String maxReferralsProp =
|
||||
SecurityProperties.privilegedGetOverridable(
|
||||
"sun.security.krb5.maxReferrals");
|
||||
try {
|
||||
maxReferralsValue = Integer.parseInt(maxReferralsProp);
|
||||
} catch (NumberFormatException e) {
|
||||
}
|
||||
MAX_REFERRALS = maxReferralsValue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Only allow a single instance of Config.
|
||||
*/
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2019, 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
|
||||
@ -155,11 +155,11 @@ class KrbAsRep extends KrbKdcRep {
|
||||
rep.encKDCRepPart = enc_part;
|
||||
|
||||
ASReq req = asReq.getMessage();
|
||||
check(true, req, rep);
|
||||
check(true, req, rep, dkey);
|
||||
|
||||
creds = new Credentials(
|
||||
rep.ticket,
|
||||
req.reqBody.cname,
|
||||
rep.cname,
|
||||
enc_part.sname,
|
||||
enc_part.key,
|
||||
enc_part.flags,
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2019, 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
|
||||
@ -36,6 +36,7 @@ import sun.security.krb5.internal.crypto.Nonce;
|
||||
import sun.security.krb5.internal.crypto.KeyUsage;
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* This class encapsulates the KRB-AS-REQ message that the client
|
||||
@ -58,7 +59,8 @@ public class KrbAsReq {
|
||||
KerberosTime till, // ok, will use
|
||||
KerberosTime rtime, // ok
|
||||
int[] eTypes, // NO
|
||||
HostAddresses addresses // ok
|
||||
HostAddresses addresses, // ok
|
||||
PAData[] extraPAs // ok
|
||||
)
|
||||
throws KrbException, IOException {
|
||||
|
||||
@ -93,6 +95,15 @@ public class KrbAsReq {
|
||||
paData[0] = new PAData( Krb5.PA_ENC_TIMESTAMP,
|
||||
encTs.asn1Encode());
|
||||
}
|
||||
if (extraPAs != null && extraPAs.length > 0) {
|
||||
if (paData == null) {
|
||||
paData = new PAData[extraPAs.length];
|
||||
} else {
|
||||
paData = Arrays.copyOf(paData, paData.length + extraPAs.length);
|
||||
}
|
||||
System.arraycopy(extraPAs, 0, paData,
|
||||
paData.length - extraPAs.length, extraPAs.length);
|
||||
}
|
||||
|
||||
if (cname.getRealm() == null) {
|
||||
throw new RealmException(Krb5.REALM_NULL,
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2010, 2019, 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
|
||||
@ -262,7 +262,9 @@ public final class KrbAsReqBuilder {
|
||||
* @throws KrbException
|
||||
* @throws IOException
|
||||
*/
|
||||
private KrbAsReq build(EncryptionKey key) throws KrbException, IOException {
|
||||
private KrbAsReq build(EncryptionKey key, ReferralsState referralsState)
|
||||
throws KrbException, IOException {
|
||||
PAData[] extraPAs = null;
|
||||
int[] eTypes;
|
||||
if (password != null) {
|
||||
eTypes = EType.getDefaults("default_tkt_enctypes");
|
||||
@ -272,6 +274,14 @@ public final class KrbAsReqBuilder {
|
||||
ks);
|
||||
for (EncryptionKey k: ks) k.destroy();
|
||||
}
|
||||
options = (options == null) ? new KDCOptions() : options;
|
||||
if (referralsState.isEnabled()) {
|
||||
options.set(KDCOptions.CANONICALIZE, true);
|
||||
extraPAs = new PAData[]{ new PAData(Krb5.PA_REQ_ENC_PA_REP,
|
||||
new byte[]{}) };
|
||||
} else {
|
||||
options.set(KDCOptions.CANONICALIZE, false);
|
||||
}
|
||||
return new KrbAsReq(key,
|
||||
options,
|
||||
cname,
|
||||
@ -280,7 +290,8 @@ public final class KrbAsReqBuilder {
|
||||
till,
|
||||
rtime,
|
||||
eTypes,
|
||||
addresses);
|
||||
addresses,
|
||||
extraPAs);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -318,11 +329,15 @@ public final class KrbAsReqBuilder {
|
||||
*/
|
||||
private KrbAsReqBuilder send() throws KrbException, IOException {
|
||||
boolean preAuthFailedOnce = false;
|
||||
KdcComm comm = new KdcComm(cname.getRealmAsString());
|
||||
KdcComm comm = null;
|
||||
EncryptionKey pakey = null;
|
||||
ReferralsState referralsState = new ReferralsState();
|
||||
while (true) {
|
||||
if (referralsState.refreshComm()) {
|
||||
comm = new KdcComm(cname.getRealmAsString());
|
||||
}
|
||||
try {
|
||||
req = build(pakey);
|
||||
req = build(pakey, referralsState);
|
||||
rep = new KrbAsRep(comm.send(req.encoding()));
|
||||
return this;
|
||||
} catch (KrbException ke) {
|
||||
@ -351,12 +366,69 @@ public final class KrbAsReqBuilder {
|
||||
}
|
||||
paList = kerr.getPA(); // Update current paList
|
||||
} else {
|
||||
if (referralsState.handleError(ke)) {
|
||||
continue;
|
||||
}
|
||||
throw ke;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class ReferralsState {
|
||||
private boolean enabled;
|
||||
private int count;
|
||||
private boolean refreshComm;
|
||||
|
||||
ReferralsState() throws KrbException {
|
||||
if (Config.DISABLE_REFERRALS) {
|
||||
if (cname.getNameType() == PrincipalName.KRB_NT_ENTERPRISE) {
|
||||
throw new KrbException("NT-ENTERPRISE principals only allowed" +
|
||||
" when referrals are enabled.");
|
||||
}
|
||||
enabled = false;
|
||||
} else {
|
||||
enabled = true;
|
||||
}
|
||||
refreshComm = true;
|
||||
}
|
||||
|
||||
boolean handleError(KrbException ke) throws RealmException {
|
||||
if (enabled) {
|
||||
if (ke.returnCode() == Krb5.KRB_ERR_WRONG_REALM) {
|
||||
Realm referredRealm = ke.getError().getClientRealm();
|
||||
if (req.getMessage().reqBody.kdcOptions.get(KDCOptions.CANONICALIZE) &&
|
||||
referredRealm != null && referredRealm.toString().length() > 0 &&
|
||||
count < Config.MAX_REFERRALS) {
|
||||
cname = new PrincipalName(cname.getNameType(),
|
||||
cname.getNameStrings(), referredRealm);
|
||||
refreshComm = true;
|
||||
count++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (count < Config.MAX_REFERRALS &&
|
||||
cname.getNameType() != PrincipalName.KRB_NT_ENTERPRISE) {
|
||||
// Server may raise an error if CANONICALIZE is true.
|
||||
// Try CANONICALIZE false.
|
||||
enabled = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean refreshComm() {
|
||||
boolean retRefreshComm = refreshComm;
|
||||
refreshComm = false;
|
||||
return retRefreshComm;
|
||||
}
|
||||
|
||||
boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs AS-REQ send and AS-REP receive.
|
||||
* Maybe a state is needed here, to divide prepare process and getCreds.
|
||||
|
@ -31,23 +31,41 @@
|
||||
package sun.security.krb5;
|
||||
|
||||
import sun.security.krb5.internal.*;
|
||||
import sun.security.krb5.internal.crypto.KeyUsage;
|
||||
import sun.security.util.DerInputStream;
|
||||
|
||||
abstract class KrbKdcRep {
|
||||
|
||||
static void check(
|
||||
boolean isAsReq,
|
||||
KDCReq req,
|
||||
KDCRep rep
|
||||
KDCRep rep,
|
||||
EncryptionKey replyKey
|
||||
) throws KrbApErrException {
|
||||
|
||||
if (isAsReq && !req.reqBody.cname.equals(rep.cname)) {
|
||||
// cname change in AS-REP is allowed only if the client
|
||||
// sent CANONICALIZE and the server supports RFC 6806 - Section 11
|
||||
// FAST scheme (ENC-PA-REP flag).
|
||||
if (isAsReq && !req.reqBody.cname.equals(rep.cname) &&
|
||||
(!req.reqBody.kdcOptions.get(KDCOptions.CANONICALIZE) ||
|
||||
!rep.encKDCRepPart.flags.get(Krb5.TKT_OPTS_ENC_PA_REP))) {
|
||||
rep.encKDCRepPart.key.destroy();
|
||||
throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
|
||||
}
|
||||
|
||||
// sname change in TGS-REP is allowed only if client
|
||||
// sent CANONICALIZE and new sname is a referral of
|
||||
// the form krbtgt/TO-REALM.COM@FROM-REALM.COM.
|
||||
if (!req.reqBody.sname.equals(rep.encKDCRepPart.sname)) {
|
||||
rep.encKDCRepPart.key.destroy();
|
||||
throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
|
||||
String[] snameStrings = rep.encKDCRepPart.sname.getNameStrings();
|
||||
if (isAsReq || !req.reqBody.kdcOptions.get(KDCOptions.CANONICALIZE) ||
|
||||
snameStrings == null || snameStrings.length != 2 ||
|
||||
!snameStrings[0].equals(PrincipalName.TGS_DEFAULT_SRV_NAME) ||
|
||||
!rep.encKDCRepPart.sname.getRealmString().equals(
|
||||
req.reqBody.sname.getRealmString())) {
|
||||
rep.encKDCRepPart.key.destroy();
|
||||
throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
|
||||
}
|
||||
}
|
||||
|
||||
if (req.reqBody.getNonce() != rep.encKDCRepPart.nonce) {
|
||||
@ -118,5 +136,45 @@ abstract class KrbKdcRep {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RFC 6806 - Section 11 mechanism check
|
||||
if (rep.encKDCRepPart.flags.get(Krb5.TKT_OPTS_ENC_PA_REP) &&
|
||||
req.reqBody.kdcOptions.get(KDCOptions.CANONICALIZE)) {
|
||||
boolean reqPaReqEncPaRep = false;
|
||||
boolean repPaReqEncPaRepValid = false;
|
||||
|
||||
// PA_REQ_ENC_PA_REP only required for AS requests
|
||||
for (PAData pa : req.pAData) {
|
||||
if (pa.getType() == Krb5.PA_REQ_ENC_PA_REP) {
|
||||
reqPaReqEncPaRep = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rep.encKDCRepPart.pAData != null) {
|
||||
for (PAData pa : rep.encKDCRepPart.pAData) {
|
||||
if (pa.getType() == Krb5.PA_REQ_ENC_PA_REP) {
|
||||
try {
|
||||
Checksum repCksum = new Checksum(
|
||||
new DerInputStream(
|
||||
pa.getValue()).getDerValue());
|
||||
repPaReqEncPaRepValid =
|
||||
repCksum.verifyKeyedChecksum(
|
||||
req.asn1Encode(), replyKey,
|
||||
KeyUsage.KU_AS_REQ);
|
||||
} catch (Exception e) {
|
||||
if (Krb5.DEBUG) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (reqPaReqEncPaRep && !repPaReqEncPaRepValid) {
|
||||
throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2019, 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
|
||||
@ -84,7 +84,7 @@ public class KrbTgsRep extends KrbKdcRep {
|
||||
EncTGSRepPart enc_part = new EncTGSRepPart(ref);
|
||||
rep.encKDCRepPart = enc_part;
|
||||
|
||||
check(false, req, rep);
|
||||
check(false, req, rep, tgsReq.tgsReqKey);
|
||||
|
||||
this.creds = new Credentials(rep.ticket,
|
||||
rep.cname,
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2019, 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
|
||||
@ -36,6 +36,7 @@ import sun.security.krb5.internal.crypto.*;
|
||||
import java.io.IOException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* This class encapsulates a Kerberos TGS-REQ that is sent from the
|
||||
@ -57,59 +58,23 @@ public class KrbTgsReq {
|
||||
private byte[] ibuf;
|
||||
|
||||
// Used in CredentialsUtil
|
||||
public KrbTgsReq(Credentials asCreds,
|
||||
PrincipalName sname)
|
||||
public KrbTgsReq(KDCOptions options, Credentials asCreds,
|
||||
PrincipalName cname, PrincipalName sname,
|
||||
Ticket[] additionalTickets, PAData[] extraPAs)
|
||||
throws KrbException, IOException {
|
||||
this(new KDCOptions(),
|
||||
asCreds,
|
||||
sname,
|
||||
null, // KerberosTime from
|
||||
null, // KerberosTime till
|
||||
null, // KerberosTime rtime
|
||||
null, // eTypes, // null, // int[] eTypes
|
||||
null, // HostAddresses addresses
|
||||
null, // AuthorizationData authorizationData
|
||||
null, // Ticket[] additionalTickets
|
||||
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
|
||||
this(options,
|
||||
asCreds,
|
||||
cname,
|
||||
sname,
|
||||
null, // KerberosTime from
|
||||
null, // KerberosTime till
|
||||
null, // KerberosTime rtime
|
||||
null, // int[] eTypes
|
||||
null, // HostAddresses addresses
|
||||
null, // AuthorizationData authorizationData
|
||||
additionalTickets,
|
||||
null, // EncryptionKey subKey
|
||||
extraPAs);
|
||||
}
|
||||
|
||||
// Called by Credentials, KrbCred
|
||||
@ -143,7 +108,7 @@ public class KrbTgsReq {
|
||||
AuthorizationData authorizationData,
|
||||
Ticket[] additionalTickets,
|
||||
EncryptionKey subKey,
|
||||
PAData extraPA) throws KrbException, IOException {
|
||||
PAData[] extraPAs) throws KrbException, IOException {
|
||||
|
||||
princName = cname;
|
||||
servName = sname;
|
||||
@ -216,7 +181,7 @@ public class KrbTgsReq {
|
||||
authorizationData,
|
||||
additionalTickets,
|
||||
subKey,
|
||||
extraPA);
|
||||
extraPAs);
|
||||
obuf = tgsReqMessg.asn1Encode();
|
||||
|
||||
// XXX We need to revisit this to see if can't move it
|
||||
@ -282,7 +247,7 @@ public class KrbTgsReq {
|
||||
AuthorizationData authorizationData,
|
||||
Ticket[] additionalTickets,
|
||||
EncryptionKey subKey,
|
||||
PAData extraPA)
|
||||
PAData[] extraPAs)
|
||||
throws IOException, KrbException, UnknownHostException {
|
||||
KerberosTime req_till = null;
|
||||
if (till == null) {
|
||||
@ -382,11 +347,14 @@ public class KrbTgsReq {
|
||||
null).getMessage();
|
||||
|
||||
PAData tgsPAData = new PAData(Krb5.PA_TGS_REQ, tgs_ap_req);
|
||||
return new TGSReq(
|
||||
extraPA != null ?
|
||||
new PAData[] {extraPA, tgsPAData } :
|
||||
new PAData[] {tgsPAData},
|
||||
reqBody);
|
||||
PAData[] pa;
|
||||
if (extraPAs != null) {
|
||||
pa = Arrays.copyOf(extraPAs, extraPAs.length + 1);
|
||||
pa[extraPAs.length] = tgsPAData;
|
||||
} else {
|
||||
pa = new PAData[] {tgsPAData};
|
||||
}
|
||||
return new TGSReq(pa, reqBody);
|
||||
}
|
||||
|
||||
TGSReq getMessage() {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2019, 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
|
||||
@ -90,6 +90,11 @@ public class PrincipalName implements Cloneable {
|
||||
*/
|
||||
public static final int KRB_NT_UID = 5;
|
||||
|
||||
/**
|
||||
* Enterprise name (alias)
|
||||
*/
|
||||
public static final int KRB_NT_ENTERPRISE = 10;
|
||||
|
||||
/**
|
||||
* TGS Name
|
||||
*/
|
||||
@ -465,6 +470,7 @@ public class PrincipalName implements Cloneable {
|
||||
case KRB_NT_SRV_INST:
|
||||
case KRB_NT_SRV_XHST:
|
||||
case KRB_NT_UID:
|
||||
case KRB_NT_ENTERPRISE:
|
||||
nameStrings = nameParts;
|
||||
nameType = type;
|
||||
if (realm != null) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2019, 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,6 +33,8 @@ package sun.security.krb5.internal;
|
||||
|
||||
import sun.security.krb5.*;
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This class is a utility that contains much of the TGS-Exchange
|
||||
@ -61,13 +63,11 @@ public class CredentialsUtil {
|
||||
if (!ccreds.isForwardable()) {
|
||||
throw new KrbException("S4U2self needs a FORWARDABLE ticket");
|
||||
}
|
||||
KrbTgsReq req = new KrbTgsReq(
|
||||
ccreds,
|
||||
ccreds.getClient(),
|
||||
new PAData(Krb5.PA_FOR_USER,
|
||||
new PAForUserEnc(client,
|
||||
ccreds.getSessionKey()).asn1Encode()));
|
||||
Credentials creds = req.sendAndGetCreds();
|
||||
Credentials creds = serviceCreds(KDCOptions.with(KDCOptions.FORWARDABLE),
|
||||
ccreds, ccreds.getClient(), ccreds.getClient(), null,
|
||||
new PAData[] {new PAData(Krb5.PA_FOR_USER,
|
||||
new PAForUserEnc(client,
|
||||
ccreds.getSessionKey()).asn1Encode())});
|
||||
if (!creds.getClient().equals(client)) {
|
||||
throw new KrbException("S4U2self request not honored by KDC");
|
||||
}
|
||||
@ -89,11 +89,10 @@ public class CredentialsUtil {
|
||||
String backend, Ticket second,
|
||||
PrincipalName client, Credentials ccreds)
|
||||
throws KrbException, IOException {
|
||||
KrbTgsReq req = new KrbTgsReq(
|
||||
ccreds,
|
||||
second,
|
||||
new PrincipalName(backend));
|
||||
Credentials creds = req.sendAndGetCreds();
|
||||
Credentials creds = serviceCreds(KDCOptions.with(
|
||||
KDCOptions.CNAME_IN_ADDL_TKT, KDCOptions.FORWARDABLE),
|
||||
ccreds, ccreds.getClient(), new PrincipalName(backend),
|
||||
new Ticket[] {second}, null);
|
||||
if (!creds.getClient().equals(client)) {
|
||||
throw new KrbException("S4U2proxy request not honored by KDC");
|
||||
}
|
||||
@ -114,53 +113,9 @@ public class CredentialsUtil {
|
||||
public static Credentials acquireServiceCreds(
|
||||
String service, Credentials ccreds)
|
||||
throws KrbException, IOException {
|
||||
PrincipalName sname = new PrincipalName(service);
|
||||
String serviceRealm = sname.getRealmString();
|
||||
String localRealm = ccreds.getClient().getRealmString();
|
||||
|
||||
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");
|
||||
PrincipalName sname = new PrincipalName(service,
|
||||
PrincipalName.KRB_NT_SRV_HST);
|
||||
return serviceCreds(sname, ccreds);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -305,6 +260,148 @@ public class CredentialsUtil {
|
||||
private static Credentials serviceCreds(
|
||||
PrincipalName service, Credentials ccreds)
|
||||
throws KrbException, IOException {
|
||||
return new KrbTgsReq(ccreds, service).sendAndGetCreds();
|
||||
return serviceCreds(new KDCOptions(), ccreds,
|
||||
ccreds.getClient(), service, null, null);
|
||||
}
|
||||
|
||||
/*
|
||||
* Obtains credentials for a service (TGS).
|
||||
* Cross-realm referrals are handled if enabled. A fallback scheme
|
||||
* without cross-realm referrals supports is used in case of server
|
||||
* error to maintain backward compatibility.
|
||||
*/
|
||||
private static Credentials serviceCreds(
|
||||
KDCOptions options, Credentials asCreds,
|
||||
PrincipalName cname, PrincipalName sname,
|
||||
Ticket[] additionalTickets, PAData[] extraPAs)
|
||||
throws KrbException, IOException {
|
||||
if (!Config.DISABLE_REFERRALS) {
|
||||
try {
|
||||
return serviceCredsReferrals(options, asCreds,
|
||||
cname, sname, additionalTickets, extraPAs);
|
||||
} catch (KrbException e) {
|
||||
// Server may raise an error if CANONICALIZE is true.
|
||||
// Try CANONICALIZE false.
|
||||
}
|
||||
}
|
||||
return serviceCredsSingle(options, asCreds,
|
||||
cname, sname, additionalTickets, extraPAs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Obtains credentials for a service (TGS).
|
||||
* May handle and follow cross-realm referrals as defined by RFC 6806.
|
||||
*/
|
||||
private static Credentials serviceCredsReferrals(
|
||||
KDCOptions options, Credentials asCreds,
|
||||
PrincipalName cname, PrincipalName sname,
|
||||
Ticket[] additionalTickets, PAData[] extraPAs)
|
||||
throws KrbException, IOException {
|
||||
options = new KDCOptions(options.toBooleanArray());
|
||||
options.set(KDCOptions.CANONICALIZE, true);
|
||||
PrincipalName cSname = sname;
|
||||
Credentials creds = null;
|
||||
boolean isReferral = false;
|
||||
List<String> referrals = new LinkedList<>();
|
||||
while (referrals.size() <= Config.MAX_REFERRALS) {
|
||||
ReferralsCache.ReferralCacheEntry ref =
|
||||
ReferralsCache.get(sname, cSname.getRealmString());
|
||||
String toRealm = null;
|
||||
if (ref == null) {
|
||||
creds = serviceCredsSingle(options, asCreds,
|
||||
cname, cSname, additionalTickets, extraPAs);
|
||||
PrincipalName server = creds.getServer();
|
||||
if (!cSname.equals(server)) {
|
||||
String[] serverNameStrings = server.getNameStrings();
|
||||
if (serverNameStrings.length == 2 &&
|
||||
serverNameStrings[0].equals(
|
||||
PrincipalName.TGS_DEFAULT_SRV_NAME) &&
|
||||
!cSname.getRealmAsString().equals(serverNameStrings[1])) {
|
||||
// Server Name (sname) has the following format:
|
||||
// krbtgt/TO-REALM.COM@FROM-REALM.COM
|
||||
ReferralsCache.put(sname, server.getRealmString(),
|
||||
serverNameStrings[1], creds);
|
||||
toRealm = serverNameStrings[1];
|
||||
isReferral = true;
|
||||
asCreds = creds;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
toRealm = ref.getToRealm();
|
||||
asCreds = ref.getCreds();
|
||||
isReferral = true;
|
||||
}
|
||||
if (isReferral) {
|
||||
if (referrals.contains(toRealm)) {
|
||||
// Referrals loop detected
|
||||
return null;
|
||||
}
|
||||
cSname = new PrincipalName(cSname.getNameString(),
|
||||
cSname.getNameType(), toRealm);
|
||||
referrals.add(toRealm);
|
||||
isReferral = false;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return creds;
|
||||
}
|
||||
|
||||
/*
|
||||
* Obtains credentials for a service (TGS).
|
||||
* If the service realm is different than the one in the TGT, a new TGT for
|
||||
* the service realm is obtained first (see getTGTforRealm call). This is
|
||||
* not expected when following cross-realm referrals because the referral
|
||||
* TGT realm matches the service realm.
|
||||
*/
|
||||
private static Credentials serviceCredsSingle(
|
||||
KDCOptions options, Credentials asCreds,
|
||||
PrincipalName cname, PrincipalName sname,
|
||||
Ticket[] additionalTickets, PAData[] extraPAs)
|
||||
throws KrbException, IOException {
|
||||
Credentials theCreds = null;
|
||||
boolean[] okAsDelegate = new boolean[]{true};
|
||||
String[] serverAsCredsNames = asCreds.getServer().getNameStrings();
|
||||
String tgtRealm = serverAsCredsNames[1];
|
||||
String serviceRealm = sname.getRealmString();
|
||||
if (!serviceRealm.equals(tgtRealm)) {
|
||||
// This is a cross-realm service request
|
||||
if (DEBUG) {
|
||||
System.out.println(">>> serviceCredsSingle:" +
|
||||
" cross-realm authentication");
|
||||
System.out.println(">>> serviceCredsSingle:" +
|
||||
" obtaining credentials from " + tgtRealm +
|
||||
" to " + serviceRealm);
|
||||
}
|
||||
Credentials newTgt = getTGTforRealm(tgtRealm, serviceRealm,
|
||||
asCreds, okAsDelegate);
|
||||
if (newTgt == null) {
|
||||
throw new KrbApErrException(Krb5.KRB_AP_ERR_GEN_CRED,
|
||||
"No service creds");
|
||||
}
|
||||
if (DEBUG) {
|
||||
System.out.println(">>> Cross-realm TGT Credentials" +
|
||||
" serviceCredsSingle: ");
|
||||
Credentials.printDebug(newTgt);
|
||||
}
|
||||
asCreds = newTgt;
|
||||
cname = asCreds.getClient();
|
||||
} else if (DEBUG) {
|
||||
System.out.println(">>> Credentials serviceCredsSingle:" +
|
||||
" same realm");
|
||||
}
|
||||
KrbTgsReq req = new KrbTgsReq(options, asCreds,
|
||||
cname, sname, additionalTickets, extraPAs);
|
||||
theCreds = req.sendAndGetCreds();
|
||||
if (theCreds != null) {
|
||||
if (DEBUG) {
|
||||
System.out.println(">>> TGS credentials serviceCredsSingle:");
|
||||
Credentials.printDebug(theCreds);
|
||||
}
|
||||
if (!okAsDelegate[0]) {
|
||||
theCreds.resetDelegate();
|
||||
}
|
||||
}
|
||||
return theCreds;
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,8 @@ public class EncASRepPart extends EncKDCRepPart {
|
||||
KerberosTime new_endtime,
|
||||
KerberosTime new_renewTill,
|
||||
PrincipalName new_sname,
|
||||
HostAddresses new_caddr) {
|
||||
HostAddresses new_caddr,
|
||||
PAData[] new_pAData) {
|
||||
super(
|
||||
new_key,
|
||||
new_lastReq,
|
||||
@ -60,6 +61,7 @@ public class EncASRepPart extends EncKDCRepPart {
|
||||
new_renewTill,
|
||||
new_sname,
|
||||
new_caddr,
|
||||
new_pAData,
|
||||
Krb5.KRB_ENC_AS_REP_PART
|
||||
);
|
||||
//may need to use Krb5.KRB_ENC_TGS_REP_PART to mimic
|
||||
|
@ -31,7 +31,6 @@
|
||||
package sun.security.krb5.internal;
|
||||
|
||||
import sun.security.krb5.*;
|
||||
import sun.security.krb5.EncryptionKey;
|
||||
import sun.security.util.*;
|
||||
import java.util.Vector;
|
||||
import java.io.IOException;
|
||||
@ -41,19 +40,20 @@ import java.math.BigInteger;
|
||||
* Implements the ASN.1 EncKDCRepPart type.
|
||||
*
|
||||
* <pre>{@code
|
||||
* EncKDCRepPart ::= SEQUENCE {
|
||||
* key [0] EncryptionKey,
|
||||
* last-req [1] LastReq,
|
||||
* nonce [2] UInt32,
|
||||
* key-expiration [3] KerberosTime OPTIONAL,
|
||||
* flags [4] TicketFlags,
|
||||
* authtime [5] KerberosTime,
|
||||
* starttime [6] KerberosTime OPTIONAL,
|
||||
* endtime [7] KerberosTime,
|
||||
* renew-till [8] KerberosTime OPTIONAL,
|
||||
* srealm [9] Realm,
|
||||
* sname [10] PrincipalName,
|
||||
* caddr [11] HostAddresses OPTIONAL
|
||||
* EncKDCRepPart ::= SEQUENCE {
|
||||
* key [0] EncryptionKey,
|
||||
* last-req [1] LastReq,
|
||||
* nonce [2] UInt32,
|
||||
* key-expiration [3] KerberosTime OPTIONAL,
|
||||
* flags [4] TicketFlags,
|
||||
* authtime [5] KerberosTime,
|
||||
* starttime [6] KerberosTime OPTIONAL,
|
||||
* endtime [7] KerberosTime,
|
||||
* renew-till [8] KerberosTime OPTIONAL,
|
||||
* srealm [9] Realm,
|
||||
* sname [10] PrincipalName,
|
||||
* caddr [11] HostAddresses OPTIONAL,
|
||||
* encrypted-pa-data [12] SEQUENCE OF PA-DATA OPTIONAL
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
@ -76,6 +76,7 @@ public class EncKDCRepPart {
|
||||
public KerberosTime renewTill; //optional
|
||||
public PrincipalName sname;
|
||||
public HostAddresses caddr; //optional
|
||||
public PAData[] pAData; //optional
|
||||
public int msgType; //not included in sequence
|
||||
|
||||
public EncKDCRepPart(
|
||||
@ -90,6 +91,7 @@ public class EncKDCRepPart {
|
||||
KerberosTime new_renewTill,
|
||||
PrincipalName new_sname,
|
||||
HostAddresses new_caddr,
|
||||
PAData[] new_pAData,
|
||||
int new_msgType) {
|
||||
key = new_key;
|
||||
lastReq = new_lastReq;
|
||||
@ -102,6 +104,7 @@ public class EncKDCRepPart {
|
||||
renewTill = new_renewTill;
|
||||
sname = new_sname;
|
||||
caddr = new_caddr;
|
||||
pAData = new_pAData;
|
||||
msgType = new_msgType;
|
||||
}
|
||||
|
||||
@ -160,6 +163,9 @@ public class EncKDCRepPart {
|
||||
if (der.getData().available() > 0) {
|
||||
caddr = HostAddresses.parse(der.getData(), (byte) 0x0B, true);
|
||||
}
|
||||
if (der.getData().available() > 0) {
|
||||
pAData = PAData.parseSequence(der.getData(), (byte) 0x0C, true);
|
||||
}
|
||||
// We observe extra data from MSAD
|
||||
/*if (der.getData().available() > 0) {
|
||||
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
|
||||
@ -175,47 +181,58 @@ public class EncKDCRepPart {
|
||||
*/
|
||||
public byte[] asn1Encode(int rep_type) throws Asn1Exception,
|
||||
IOException {
|
||||
DerOutputStream bytes;
|
||||
DerOutputStream temp = new DerOutputStream();
|
||||
DerOutputStream bytes = new DerOutputStream();
|
||||
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
|
||||
DerOutputStream out = new DerOutputStream();
|
||||
out.write(DerValue.createTag(DerValue.TAG_CONTEXT,
|
||||
true, (byte) 0x00), key.asn1Encode());
|
||||
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
|
||||
out.write(DerValue.createTag(DerValue.TAG_CONTEXT,
|
||||
true, (byte) 0x01), lastReq.asn1Encode());
|
||||
temp.putInteger(BigInteger.valueOf(nonce));
|
||||
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
|
||||
out.write(DerValue.createTag(DerValue.TAG_CONTEXT,
|
||||
true, (byte) 0x02), temp);
|
||||
|
||||
if (keyExpiration != null) {
|
||||
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
|
||||
out.write(DerValue.createTag(DerValue.TAG_CONTEXT,
|
||||
true, (byte) 0x03), keyExpiration.asn1Encode());
|
||||
}
|
||||
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
|
||||
out.write(DerValue.createTag(DerValue.TAG_CONTEXT,
|
||||
true, (byte) 0x04), flags.asn1Encode());
|
||||
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
|
||||
out.write(DerValue.createTag(DerValue.TAG_CONTEXT,
|
||||
true, (byte) 0x05), authtime.asn1Encode());
|
||||
if (starttime != null) {
|
||||
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
|
||||
out.write(DerValue.createTag(DerValue.TAG_CONTEXT,
|
||||
true, (byte) 0x06), starttime.asn1Encode());
|
||||
}
|
||||
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
|
||||
out.write(DerValue.createTag(DerValue.TAG_CONTEXT,
|
||||
true, (byte) 0x07), endtime.asn1Encode());
|
||||
if (renewTill != null) {
|
||||
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
|
||||
out.write(DerValue.createTag(DerValue.TAG_CONTEXT,
|
||||
true, (byte) 0x08), renewTill.asn1Encode());
|
||||
}
|
||||
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
|
||||
out.write(DerValue.createTag(DerValue.TAG_CONTEXT,
|
||||
true, (byte) 0x09), sname.getRealm().asn1Encode());
|
||||
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
|
||||
out.write(DerValue.createTag(DerValue.TAG_CONTEXT,
|
||||
true, (byte) 0x0A), sname.asn1Encode());
|
||||
if (caddr != null) {
|
||||
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT,
|
||||
out.write(DerValue.createTag(DerValue.TAG_CONTEXT,
|
||||
true, (byte) 0x0B), caddr.asn1Encode());
|
||||
}
|
||||
if (pAData != null && pAData.length > 0) {
|
||||
temp = new DerOutputStream();
|
||||
for (int i = 0; i < pAData.length; i++) {
|
||||
temp.write(pAData[i].asn1Encode());
|
||||
}
|
||||
bytes = new DerOutputStream();
|
||||
bytes.write(DerValue.tag_SequenceOf, temp);
|
||||
out.write(DerValue.createTag(DerValue.TAG_CONTEXT,
|
||||
true, (byte) 0x0C), bytes);
|
||||
}
|
||||
//should use the rep_type to build the encoding
|
||||
//but other implementations do not; it is ignored and
|
||||
//the cached msgType is used instead
|
||||
temp = new DerOutputStream();
|
||||
temp.write(DerValue.tag_Sequence, bytes);
|
||||
temp.write(DerValue.tag_Sequence, out);
|
||||
bytes = new DerOutputStream();
|
||||
bytes.write(DerValue.createTag(DerValue.TAG_APPLICATION,
|
||||
true, (byte) msgType), temp);
|
||||
|
@ -46,7 +46,8 @@ public class EncTGSRepPart extends EncKDCRepPart {
|
||||
KerberosTime new_endtime,
|
||||
KerberosTime new_renewTill,
|
||||
PrincipalName new_sname,
|
||||
HostAddresses new_caddr) {
|
||||
HostAddresses new_caddr,
|
||||
PAData[] new_pAData) {
|
||||
super(
|
||||
new_key,
|
||||
new_lastReq,
|
||||
@ -59,6 +60,7 @@ public class EncTGSRepPart extends EncKDCRepPart {
|
||||
new_renewTill,
|
||||
new_sname,
|
||||
new_caddr,
|
||||
new_pAData,
|
||||
Krb5.KRB_ENC_TGS_REP_PART);
|
||||
}
|
||||
|
||||
|
@ -140,6 +140,7 @@ public class KDCOptions extends KerberosFlags {
|
||||
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 CANONICALIZE = 15;
|
||||
public static final int RENEWABLE_OK = 27;
|
||||
public static final int ENC_TKT_IN_SKEY = 28;
|
||||
public static final int RENEW = 30;
|
||||
@ -160,7 +161,8 @@ public class KDCOptions extends KerberosFlags {
|
||||
"UNUSED11", //11;
|
||||
null,null,
|
||||
"CNAME_IN_ADDL_TKT",//14;
|
||||
null,null,null,null,null,null,null,null,null,null,null,null,
|
||||
"CANONICALIZE", //15;
|
||||
null,null,null,null,null,null,null,null,null,null,null,
|
||||
"RENEWABLE_OK", //27;
|
||||
"ENC_TKT_IN_SKEY", //28;
|
||||
null,
|
||||
|
@ -59,9 +59,9 @@ import java.math.BigInteger;
|
||||
public class KDCReq {
|
||||
|
||||
public KDCReqBody reqBody;
|
||||
public PAData[] pAData = null; //optional
|
||||
private int pvno;
|
||||
private int msgType;
|
||||
private PAData[] pAData = null; //optional
|
||||
|
||||
public KDCReq(PAData[] new_pAData, KDCReqBody new_reqBody,
|
||||
int req_type) throws IOException {
|
||||
@ -144,23 +144,7 @@ public class KDCReq {
|
||||
} else {
|
||||
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
|
||||
}
|
||||
if ((der.getData().peekByte() & 0x1F) == 0x03) {
|
||||
subDer = der.getData().getDerValue();
|
||||
DerValue subsubDer = subDer.getData().getDerValue();
|
||||
if (subsubDer.getTag() != DerValue.tag_SequenceOf) {
|
||||
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
|
||||
}
|
||||
Vector<PAData> v = new Vector<>();
|
||||
while (subsubDer.getData().available() > 0) {
|
||||
v.addElement(new PAData(subsubDer.getData().getDerValue()));
|
||||
}
|
||||
if (v.size() > 0) {
|
||||
pAData = new PAData[v.size()];
|
||||
v.copyInto(pAData);
|
||||
}
|
||||
} else {
|
||||
pAData = null;
|
||||
}
|
||||
pAData = PAData.parseSequence(der.getData(), (byte) 0x03, true);
|
||||
subDer = der.getData().getDerValue();
|
||||
if ((subDer.getTag() & 0x01F) == 0x04) {
|
||||
DerValue subsubDer = subDer.getData().getDerValue();
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2019, 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
|
||||
@ -90,6 +90,7 @@ public class KRBError implements java.io.Serializable {
|
||||
private KerberosTime sTime;
|
||||
private Integer suSec;
|
||||
private int errorCode;
|
||||
private Realm crealm; //optional
|
||||
private PrincipalName cname; //optional
|
||||
private PrincipalName sname;
|
||||
private String eText; //optional
|
||||
@ -138,6 +139,7 @@ public class KRBError implements java.io.Serializable {
|
||||
sTime = new_sTime;
|
||||
suSec = new_suSec;
|
||||
errorCode = new_errorCode;
|
||||
crealm = new_cname.getRealm();
|
||||
cname = new_cname;
|
||||
sname = new_sname;
|
||||
eText = new_eText;
|
||||
@ -166,6 +168,7 @@ public class KRBError implements java.io.Serializable {
|
||||
sTime = new_sTime;
|
||||
suSec = new_suSec;
|
||||
errorCode = new_errorCode;
|
||||
crealm = new_cname.getRealm();
|
||||
cname = new_cname;
|
||||
sname = new_sname;
|
||||
eText = new_eText;
|
||||
@ -262,6 +265,10 @@ public class KRBError implements java.io.Serializable {
|
||||
pa = paList.toArray(new PAData[paList.size()]);
|
||||
}
|
||||
|
||||
public final Realm getClientRealm() {
|
||||
return crealm;
|
||||
}
|
||||
|
||||
public final KerberosTime getServerTime() {
|
||||
return sTime;
|
||||
}
|
||||
@ -349,7 +356,7 @@ public class KRBError implements java.io.Serializable {
|
||||
errorCode = subDer.getData().getBigInteger().intValue();
|
||||
}
|
||||
else throw new Asn1Exception(Krb5.ASN1_BAD_ID);
|
||||
Realm crealm = Realm.parse(der.getData(), (byte)0x07, true);
|
||||
crealm = Realm.parse(der.getData(), (byte)0x07, true);
|
||||
cname = PrincipalName.parse(der.getData(), (byte)0x08, true, crealm);
|
||||
Realm realm = Realm.parse(der.getData(), (byte)0x09, false);
|
||||
sname = PrincipalName.parse(der.getData(), (byte)0x0A, false, realm);
|
||||
@ -393,6 +400,9 @@ public class KRBError implements java.io.Serializable {
|
||||
System.out.println("\t suSec is " + suSec);
|
||||
System.out.println("\t error code is " + errorCode);
|
||||
System.out.println("\t error Message is " + Krb5.getErrorMessage(errorCode));
|
||||
if (crealm != null) {
|
||||
System.out.println("\t crealm is " + crealm.toString());
|
||||
}
|
||||
if (cname != null) {
|
||||
System.out.println("\t cname is " + cname.toString());
|
||||
}
|
||||
@ -442,8 +452,10 @@ public class KRBError implements java.io.Serializable {
|
||||
temp.putInteger(BigInteger.valueOf(errorCode));
|
||||
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x06), temp);
|
||||
|
||||
if (crealm != null) {
|
||||
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x07), crealm.asn1Encode());
|
||||
}
|
||||
if (cname != null) {
|
||||
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x07), cname.getRealm().asn1Encode());
|
||||
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x08), cname.asn1Encode());
|
||||
}
|
||||
|
||||
@ -488,6 +500,7 @@ public class KRBError implements java.io.Serializable {
|
||||
isEqual(sTime, other.sTime) &&
|
||||
isEqual(suSec, other.suSec) &&
|
||||
errorCode == other.errorCode &&
|
||||
isEqual(crealm, other.crealm) &&
|
||||
isEqual(cname, other.cname) &&
|
||||
isEqual(sname, other.sname) &&
|
||||
isEqual(eText, other.eText) &&
|
||||
@ -508,6 +521,7 @@ public class KRBError implements java.io.Serializable {
|
||||
if (sTime != null) result = 37 * result + sTime.hashCode();
|
||||
if (suSec != null) result = 37 * result + suSec.hashCode();
|
||||
result = 37 * result + errorCode;
|
||||
if (crealm != null) result = 37 * result + crealm.hashCode();
|
||||
if (cname != null) result = 37 * result + cname.hashCode();
|
||||
if (sname != null) result = 37 * result + sname.hashCode();
|
||||
if (eText != null) result = 37 * result + eText.hashCode();
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2019, 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
|
||||
@ -72,6 +72,7 @@ public class Krb5 {
|
||||
public static final int TKT_OPTS_PRE_AUTHENT = 10;
|
||||
public static final int TKT_OPTS_HW_AUTHENT = 11;
|
||||
public static final int TKT_OPTS_DELEGATE = 13;
|
||||
public static final int TKT_OPTS_ENC_PA_REP = 15;
|
||||
public static final int TKT_OPTS_MAX = 31;
|
||||
|
||||
// KDC Options
|
||||
@ -165,6 +166,9 @@ public class Krb5 {
|
||||
// S4U2user info
|
||||
public static final int PA_FOR_USER = 129;
|
||||
|
||||
// FAST (RFC 6806)
|
||||
public static final int PA_REQ_ENC_PA_REP = 149;
|
||||
|
||||
//-------------------------------+-------------
|
||||
//authorization data type |ad-type value
|
||||
//-------------------------------+-------------
|
||||
@ -267,6 +271,7 @@ public class Krb5 {
|
||||
public static final int KRB_ERR_RESPONSE_TOO_BIG = 52; //Response too big for UDP, retry with TCP
|
||||
public static final int KRB_ERR_GENERIC = 60; //Generic error (description in e-text)
|
||||
public static final int KRB_ERR_FIELD_TOOLONG = 61; //Field is too long for this implementation
|
||||
public static final int KRB_ERR_WRONG_REALM = 68; //Wrong realm
|
||||
public static final int KRB_CRYPTO_NOT_SUPPORT = 100; //Client does not support this crypto type
|
||||
public static final int KRB_AP_ERR_NOREALM = 62;
|
||||
public static final int KRB_AP_ERR_GEN_CRED = 63;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2017, 2019 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
|
||||
@ -35,6 +35,8 @@ import sun.security.krb5.internal.crypto.EType;
|
||||
import sun.security.util.*;
|
||||
import sun.security.krb5.Asn1Exception;
|
||||
import java.io.IOException;
|
||||
import java.util.Vector;
|
||||
|
||||
import sun.security.krb5.internal.util.KerberosString;
|
||||
|
||||
/**
|
||||
@ -139,6 +141,41 @@ public class PAData {
|
||||
return ((pADataValue == null) ? null : pADataValue.clone());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse (unmarshal) a PAData from a DER input stream. This form
|
||||
* parsing might be used when expanding a value which is part of
|
||||
* a constructed sequence and uses explicitly tagged type.
|
||||
*
|
||||
* @exception Asn1Exception if an Asn1Exception occurs.
|
||||
* @param data the Der input stream value, which contains one or more
|
||||
* marshaled values.
|
||||
* @param explicitTag tag number.
|
||||
* @param optional indicates if this data field is optional.
|
||||
* @return an array of PAData.
|
||||
*/
|
||||
public static PAData[] parseSequence(DerInputStream data,
|
||||
byte explicitTag, boolean optional)
|
||||
throws Asn1Exception, IOException {
|
||||
if ((optional) &&
|
||||
(((byte)data.peekByte() & (byte)0x1F) != explicitTag))
|
||||
return null;
|
||||
DerValue subDer = data.getDerValue();
|
||||
DerValue subsubDer = subDer.getData().getDerValue();
|
||||
if (subsubDer.getTag() != DerValue.tag_SequenceOf) {
|
||||
throw new Asn1Exception(Krb5.ASN1_BAD_ID);
|
||||
}
|
||||
Vector<PAData> v = new Vector<>();
|
||||
while (subsubDer.getData().available() > 0) {
|
||||
v.addElement(new PAData(subsubDer.getData().getDerValue()));
|
||||
}
|
||||
if (v.size() > 0) {
|
||||
PAData[] pas = new PAData[v.size()];
|
||||
v.copyInto(pas);
|
||||
return pas;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the preferred etype from the PAData array.
|
||||
* <ol>
|
||||
|
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Red Hat, Inc.
|
||||
* 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.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import sun.security.krb5.Credentials;
|
||||
import sun.security.krb5.PrincipalName;
|
||||
|
||||
/*
|
||||
* ReferralsCache class implements a cache scheme for referral TGTs as
|
||||
* described in RFC 6806 - 10. Caching Information. The goal is to optimize
|
||||
* resources (such as network traffic) when a client requests credentials for a
|
||||
* service principal to a given KDC. If a referral TGT was previously received,
|
||||
* cached information is used instead of issuing a new query. Once a referral
|
||||
* TGT expires, the corresponding referral entry in the cache is removed.
|
||||
*/
|
||||
final class ReferralsCache {
|
||||
|
||||
private static Map<PrincipalName, Map<String, ReferralCacheEntry>> referralsMap =
|
||||
new HashMap<>();
|
||||
|
||||
static final class ReferralCacheEntry {
|
||||
private final Credentials creds;
|
||||
private final String toRealm;
|
||||
ReferralCacheEntry(Credentials creds, String toRealm) {
|
||||
this.creds = creds;
|
||||
this.toRealm = toRealm;
|
||||
}
|
||||
Credentials getCreds() {
|
||||
return creds;
|
||||
}
|
||||
String getToRealm() {
|
||||
return toRealm;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a new referral entry to the cache, including: service principal,
|
||||
* source KDC realm, destination KDC realm and referral TGT.
|
||||
*
|
||||
* If a loop is generated when adding the new referral, the first hop is
|
||||
* automatically removed. For example, let's assume that adding a
|
||||
* REALM-3.COM -> REALM-1.COM referral generates the following loop:
|
||||
* REALM-1.COM -> REALM-2.COM -> REALM-3.COM -> REALM-1.COM. Then,
|
||||
* REALM-1.COM -> REALM-2.COM referral entry is removed from the cache.
|
||||
*/
|
||||
static synchronized void put(PrincipalName service,
|
||||
String fromRealm, String toRealm, Credentials creds) {
|
||||
pruneExpired(service);
|
||||
if (creds.getEndTime().before(new Date())) {
|
||||
return;
|
||||
}
|
||||
Map<String, ReferralCacheEntry> entries = referralsMap.get(service);
|
||||
if (entries == null) {
|
||||
entries = new HashMap<String, ReferralCacheEntry>();
|
||||
referralsMap.put(service, entries);
|
||||
}
|
||||
entries.remove(fromRealm);
|
||||
ReferralCacheEntry newEntry = new ReferralCacheEntry(creds, toRealm);
|
||||
entries.put(fromRealm, newEntry);
|
||||
|
||||
// Remove loops within the cache
|
||||
ReferralCacheEntry current = newEntry;
|
||||
List<ReferralCacheEntry> seen = new LinkedList<>();
|
||||
while (current != null) {
|
||||
if (seen.contains(current)) {
|
||||
// Loop found. Remove the first referral to cut the loop.
|
||||
entries.remove(newEntry.getToRealm());
|
||||
break;
|
||||
}
|
||||
seen.add(current);
|
||||
current = entries.get(current.getToRealm());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Obtain a referral entry from the cache given a service principal and a
|
||||
* source KDC realm.
|
||||
*/
|
||||
static synchronized ReferralCacheEntry get(PrincipalName service,
|
||||
String fromRealm) {
|
||||
pruneExpired(service);
|
||||
Map<String, ReferralCacheEntry> entries = referralsMap.get(service);
|
||||
if (entries != null) {
|
||||
ReferralCacheEntry toRef = entries.get(fromRealm);
|
||||
if (toRef != null) {
|
||||
return toRef;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove referral entries from the cache when referral TGTs expire.
|
||||
*/
|
||||
private static void pruneExpired(PrincipalName service) {
|
||||
Date now = new Date();
|
||||
Map<String, ReferralCacheEntry> entries = referralsMap.get(service);
|
||||
if (entries != null) {
|
||||
for (Entry<String, ReferralCacheEntry> mapEntry :
|
||||
entries.entrySet()) {
|
||||
if (mapEntry.getValue().getCreds().getEndTime().before(now)) {
|
||||
entries.remove(mapEntry.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -51,7 +51,8 @@ import java.io.IOException;
|
||||
* renewable(8),
|
||||
* initial(9),
|
||||
* pre-authent(10),
|
||||
* hw-authent(11)
|
||||
* hw-authent(11),
|
||||
* enc-pa-rep(15)
|
||||
* }
|
||||
*/
|
||||
public class TicketFlags extends KerberosFlags {
|
||||
@ -178,6 +179,9 @@ public class TicketFlags extends KerberosFlags {
|
||||
case 11:
|
||||
sb.append("HW-AUTHENT;");
|
||||
break;
|
||||
case 15:
|
||||
sb.append("ENC-PA-REP;");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2004, 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2004, 2019, 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
|
||||
@ -56,6 +56,7 @@ public class KeyUsage {
|
||||
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 int KU_AS_REQ = 56;
|
||||
|
||||
public static final boolean isValid(int usage) {
|
||||
return usage >= 0;
|
||||
|
@ -165,6 +165,14 @@ public class KDC {
|
||||
private TreeMap<String,byte[]> s2kparamses = new TreeMap<>
|
||||
(String.CASE_INSENSITIVE_ORDER);
|
||||
|
||||
// Alias for referrals.
|
||||
private TreeMap<String,KDC> aliasReferrals = new TreeMap<>
|
||||
(String.CASE_INSENSITIVE_ORDER);
|
||||
|
||||
// Alias for local resolution.
|
||||
private TreeMap<String,PrincipalName> alias2Principals = new TreeMap<>
|
||||
(String.CASE_INSENSITIVE_ORDER);
|
||||
|
||||
// Realm name
|
||||
private String realm;
|
||||
// KDC
|
||||
@ -553,6 +561,29 @@ public class KDC {
|
||||
return port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an alias name to be referred to a different KDC for
|
||||
* resolution, according to RFC 6806.
|
||||
* @param alias Alias name (i.e. user@REALM.COM).
|
||||
* @param referredKDC KDC to which the alias is referred for resolution.
|
||||
*/
|
||||
public void registerAlias(String alias, KDC referredKDC) {
|
||||
aliasReferrals.remove(alias);
|
||||
aliasReferrals.put(alias, referredKDC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an alias to be resolved to a Principal Name locally,
|
||||
* according to RFC 6806.
|
||||
* @param alias Alias name (i.e. user@REALM.COM).
|
||||
* @param user Principal Name to which the alias is resolved.
|
||||
*/
|
||||
public void registerAlias(String alias, String user)
|
||||
throws RealmException {
|
||||
alias2Principals.remove(alias);
|
||||
alias2Principals.put(alias, new PrincipalName(user));
|
||||
}
|
||||
|
||||
// Private helper methods
|
||||
|
||||
/**
|
||||
@ -778,6 +809,17 @@ public class KDC {
|
||||
PrincipalName cname = null;
|
||||
boolean allowForwardable = true;
|
||||
|
||||
if (body.kdcOptions.get(KDCOptions.CANONICALIZE)) {
|
||||
KDC referral = aliasReferrals.get(body.sname.getNameString());
|
||||
if (referral != null) {
|
||||
service = new PrincipalName(
|
||||
PrincipalName.TGS_DEFAULT_SRV_NAME +
|
||||
PrincipalName.NAME_COMPONENT_SEPARATOR_STR +
|
||||
referral.getRealm(), PrincipalName.KRB_NT_SRV_INST,
|
||||
this.getRealm());
|
||||
}
|
||||
}
|
||||
|
||||
if (pas == null || pas.length == 0) {
|
||||
throw new KrbException(Krb5.KDC_ERR_PADATA_TYPE_NOSUPP);
|
||||
} else {
|
||||
@ -964,7 +1006,8 @@ public class KDC {
|
||||
from,
|
||||
till, renewTill,
|
||||
service,
|
||||
body.addresses
|
||||
body.addresses,
|
||||
null
|
||||
);
|
||||
EncryptedData edata = new EncryptedData(ckey, enc_part.asn1Encode(),
|
||||
KeyUsage.KU_ENC_TGS_REP_PART_SESSKEY);
|
||||
@ -1008,6 +1051,7 @@ public class KDC {
|
||||
*/
|
||||
protected byte[] processAsReq(byte[] in) throws Exception {
|
||||
ASReq asReq = new ASReq(in);
|
||||
byte[] asReqbytes = asReq.asn1Encode();
|
||||
int[] eTypes = null;
|
||||
List<PAData> outPAs = new ArrayList<>();
|
||||
|
||||
@ -1030,6 +1074,24 @@ public class KDC {
|
||||
}
|
||||
int eType = eTypes[0];
|
||||
|
||||
if (body.kdcOptions.get(KDCOptions.CANONICALIZE) &&
|
||||
body.cname.getNameType() == PrincipalName.KRB_NT_ENTERPRISE) {
|
||||
PrincipalName principal = alias2Principals.get(
|
||||
body.cname.getNameString());
|
||||
if (principal != null) {
|
||||
body.cname = principal;
|
||||
} else {
|
||||
KDC referral = aliasReferrals.get(body.cname.getNameString());
|
||||
if (referral != null) {
|
||||
body.cname = new PrincipalName(
|
||||
PrincipalName.TGS_DEFAULT_SRV_NAME,
|
||||
PrincipalName.KRB_NT_SRV_INST,
|
||||
referral.getRealm());
|
||||
throw new KrbException(Krb5.KRB_ERR_WRONG_REALM);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EncryptionKey ckey = keyForUser(body.cname, eType, false);
|
||||
EncryptionKey skey = keyForUser(service, eType, true);
|
||||
|
||||
@ -1211,17 +1273,18 @@ public class KDC {
|
||||
}
|
||||
|
||||
PAData[] inPAs = KDCReqDotPAData(asReq);
|
||||
List<PAData> enc_outPAs = new ArrayList<>();
|
||||
if (inPAs == null || inPAs.length == 0) {
|
||||
Object preauth = options.get(Option.PREAUTH_REQUIRED);
|
||||
if (preauth == null || preauth.equals(Boolean.TRUE)) {
|
||||
throw new KrbException(Krb5.KDC_ERR_PREAUTH_REQUIRED);
|
||||
}
|
||||
} else {
|
||||
EncryptionKey pakey = null;
|
||||
try {
|
||||
EncryptedData data = newEncryptedData(
|
||||
new DerValue(inPAs[0].getValue()));
|
||||
EncryptionKey pakey
|
||||
= keyForUser(body.cname, data.getEType(), false);
|
||||
pakey = keyForUser(body.cname, data.getEType(), false);
|
||||
data.decrypt(pakey, KeyUsage.KU_PA_ENC_TS);
|
||||
} catch (Exception e) {
|
||||
KrbException ke = new KrbException(Krb5.KDC_ERR_PREAUTH_FAILED);
|
||||
@ -1229,6 +1292,17 @@ public class KDC {
|
||||
throw ke;
|
||||
}
|
||||
bFlags[Krb5.TKT_OPTS_PRE_AUTHENT] = true;
|
||||
for (PAData pa : inPAs) {
|
||||
if (pa.getType() == Krb5.PA_REQ_ENC_PA_REP) {
|
||||
Checksum ckSum = new Checksum(
|
||||
Checksum.CKSUMTYPE_HMAC_SHA1_96_AES128,
|
||||
asReqbytes, ckey, KeyUsage.KU_AS_REQ);
|
||||
enc_outPAs.add(new PAData(Krb5.PA_REQ_ENC_PA_REP,
|
||||
ckSum.asn1Encode()));
|
||||
bFlags[Krb5.TKT_OPTS_ENC_PA_REP] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TicketFlags tFlags = new TicketFlags(bFlags);
|
||||
@ -1259,7 +1333,8 @@ public class KDC {
|
||||
from,
|
||||
till, rtime,
|
||||
service,
|
||||
body.addresses
|
||||
body.addresses,
|
||||
enc_outPAs.toArray(new PAData[enc_outPAs.size()])
|
||||
);
|
||||
EncryptedData edata = new EncryptedData(ckey, enc_part.asn1Encode(),
|
||||
KeyUsage.KU_ENC_AS_REP_PART);
|
||||
@ -1307,8 +1382,10 @@ public class KDC {
|
||||
if (kerr == null) {
|
||||
if (ke.returnCode() == Krb5.KDC_ERR_PREAUTH_REQUIRED ||
|
||||
ke.returnCode() == Krb5.KDC_ERR_PREAUTH_FAILED) {
|
||||
outPAs.add(new PAData(Krb5.PA_ENC_TIMESTAMP, new byte[0]));
|
||||
}
|
||||
if (outPAs.size() > 0) {
|
||||
DerOutputStream bytes = new DerOutputStream();
|
||||
bytes.write(new PAData(Krb5.PA_ENC_TIMESTAMP, new byte[0]).asn1Encode());
|
||||
for (PAData p: outPAs) {
|
||||
bytes.write(p.asn1Encode());
|
||||
}
|
||||
|
169
test/jdk/sun/security/krb5/auto/ReferralsTest.java
Normal file
169
test/jdk/sun/security/krb5/auto/ReferralsTest.java
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Red Hat, Inc.
|
||||
* 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 8215032
|
||||
* @library /test/lib
|
||||
* @run main/othervm/timeout=120 -Dsun.security.krb5.debug=true ReferralsTest
|
||||
* @summary Test Kerberos cross-realm referrals (RFC 6806)
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import sun.security.krb5.Credentials;
|
||||
import sun.security.krb5.internal.CredentialsUtil;
|
||||
import sun.security.krb5.KrbAsReqBuilder;
|
||||
import sun.security.krb5.PrincipalName;
|
||||
|
||||
public class ReferralsTest {
|
||||
private static final boolean DEBUG = true;
|
||||
private static final String krbConfigName = "krb5-localkdc.conf";
|
||||
private static final String realmKDC1 = "RABBIT.HOLE";
|
||||
private static final String realmKDC2 = "DEV.RABBIT.HOLE";
|
||||
private static final char[] password = "123qwe@Z".toCharArray();
|
||||
private static final String clientName = "test";
|
||||
|
||||
private static final String clientAlias = clientName +
|
||||
PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC1;
|
||||
|
||||
private static final String clientKDC1QueryName = clientAlias.replaceAll(
|
||||
PrincipalName.NAME_REALM_SEPARATOR_STR, "\\\\" +
|
||||
PrincipalName.NAME_REALM_SEPARATOR_STR) +
|
||||
PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC1;
|
||||
private static PrincipalName clientKDC1QueryPrincipal = null;
|
||||
static {
|
||||
try {
|
||||
clientKDC1QueryPrincipal = new PrincipalName(
|
||||
clientKDC1QueryName, PrincipalName.KRB_NT_ENTERPRISE,
|
||||
null);
|
||||
} catch (Throwable t) {}
|
||||
}
|
||||
|
||||
private static final String clientKDC2Name = clientName +
|
||||
PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC2;
|
||||
|
||||
private static final String serviceName = "http" +
|
||||
PrincipalName.NAME_COMPONENT_SEPARATOR_STR +
|
||||
"server.dev.rabbit.hole";
|
||||
|
||||
private static Credentials tgt;
|
||||
private static Credentials tgs;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
try {
|
||||
initializeKDCs();
|
||||
getTGT();
|
||||
getTGS();
|
||||
} finally {
|
||||
cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
private static void initializeKDCs() throws Exception {
|
||||
KDC kdc1 = KDC.create(realmKDC1, "localhost", 0, true);
|
||||
kdc1.addPrincipalRandKey(PrincipalName.TGS_DEFAULT_SRV_NAME +
|
||||
PrincipalName.NAME_COMPONENT_SEPARATOR_STR + realmKDC1);
|
||||
kdc1.addPrincipal(PrincipalName.TGS_DEFAULT_SRV_NAME +
|
||||
PrincipalName.NAME_COMPONENT_SEPARATOR_STR + realmKDC1 +
|
||||
PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC2,
|
||||
password);
|
||||
kdc1.addPrincipal(PrincipalName.TGS_DEFAULT_SRV_NAME +
|
||||
PrincipalName.NAME_COMPONENT_SEPARATOR_STR + realmKDC2,
|
||||
password);
|
||||
|
||||
KDC kdc2 = KDC.create(realmKDC2, "localhost", 0, true);
|
||||
kdc2.addPrincipalRandKey(PrincipalName.TGS_DEFAULT_SRV_NAME +
|
||||
PrincipalName.NAME_COMPONENT_SEPARATOR_STR + realmKDC2);
|
||||
kdc2.addPrincipal(clientKDC2Name, password);
|
||||
kdc2.addPrincipal(serviceName, password);
|
||||
kdc2.addPrincipal(PrincipalName.TGS_DEFAULT_SRV_NAME +
|
||||
PrincipalName.NAME_COMPONENT_SEPARATOR_STR + realmKDC1,
|
||||
password);
|
||||
kdc2.addPrincipal(PrincipalName.TGS_DEFAULT_SRV_NAME +
|
||||
PrincipalName.NAME_COMPONENT_SEPARATOR_STR + realmKDC2 +
|
||||
PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC1,
|
||||
password);
|
||||
|
||||
kdc1.registerAlias(clientAlias, kdc2);
|
||||
kdc1.registerAlias(serviceName, kdc2);
|
||||
kdc2.registerAlias(clientAlias, clientKDC2Name);
|
||||
|
||||
KDC.saveConfig(krbConfigName, kdc1, kdc2,
|
||||
"forwardable=true");
|
||||
System.setProperty("java.security.krb5.conf", krbConfigName);
|
||||
}
|
||||
|
||||
private static void cleanup() {
|
||||
File f = new File(krbConfigName);
|
||||
if (f.exists()) {
|
||||
f.delete();
|
||||
}
|
||||
}
|
||||
|
||||
private static void getTGT() throws Exception {
|
||||
KrbAsReqBuilder builder = new KrbAsReqBuilder(clientKDC1QueryPrincipal,
|
||||
password);
|
||||
tgt = builder.action().getCreds();
|
||||
builder.destroy();
|
||||
if (DEBUG) {
|
||||
System.out.println("TGT");
|
||||
System.out.println("----------------------");
|
||||
System.out.println(tgt);
|
||||
System.out.println("----------------------");
|
||||
}
|
||||
if (tgt == null) {
|
||||
throw new Exception("TGT is null");
|
||||
}
|
||||
if (!tgt.getClient().getName().equals(clientKDC2Name)) {
|
||||
throw new Exception("Unexpected TGT client");
|
||||
}
|
||||
String[] tgtServerNames = tgt.getServer().getNameStrings();
|
||||
if (tgtServerNames.length != 2 || !tgtServerNames[0].equals(
|
||||
PrincipalName.TGS_DEFAULT_SRV_NAME) ||
|
||||
!tgtServerNames[1].equals(realmKDC2) ||
|
||||
!tgt.getServer().getRealmString().equals(realmKDC2)) {
|
||||
throw new Exception("Unexpected TGT server");
|
||||
}
|
||||
}
|
||||
|
||||
private static void getTGS() throws Exception {
|
||||
tgs = CredentialsUtil.acquireServiceCreds(serviceName +
|
||||
PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC1, tgt);
|
||||
if (DEBUG) {
|
||||
System.out.println("TGS");
|
||||
System.out.println("----------------------");
|
||||
System.out.println(tgs);
|
||||
System.out.println("----------------------");
|
||||
}
|
||||
if (tgs == null) {
|
||||
throw new Exception("TGS is null");
|
||||
}
|
||||
if (!tgs.getClient().getName().equals(clientKDC2Name)) {
|
||||
throw new Exception("Unexpected TGS client");
|
||||
}
|
||||
if (!tgs.getServer().getNameString().equals(serviceName) ||
|
||||
!tgs.getServer().getRealmString().equals(realmKDC2)) {
|
||||
throw new Exception("Unexpected TGS server");
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user