提交 7da40a32 编写于 作者: M mbalao

8215032: Support Kerberos cross-realm referrals (RFC 6806)

Reviewed-by: weijun, andrew
上级 9753729b
/* /*
* Copyright (c) 2000, 2013, 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -80,6 +80,11 @@ public final class KerberosPrincipal ...@@ -80,6 +80,11 @@ public final class KerberosPrincipal
public static final int KRB_NT_UID = 5; 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 fullName;
private transient String realm; private transient String realm;
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -214,7 +214,7 @@ public class Checksum { ...@@ -214,7 +214,7 @@ public class Checksum {
* @exception IOException if an I/O error occurs while reading encoded data. * @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; DerValue der;
if (encoding.getTag() != DerValue.tag_Sequence) { if (encoding.getTag() != DerValue.tag_Sequence) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID); throw new Asn1Exception(Krb5.ASN1_BAD_ID);
......
/* /*
* Copyright (c) 2000, 2013, 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -49,6 +49,7 @@ import java.util.Locale; ...@@ -49,6 +49,7 @@ import java.util.Locale;
import sun.net.dns.ResolverConfiguration; import sun.net.dns.ResolverConfiguration;
import sun.security.krb5.internal.crypto.EType; import sun.security.krb5.internal.crypto.EType;
import sun.security.krb5.internal.Krb5; import sun.security.krb5.internal.Krb5;
import sun.security.util.SecurityProperties;
/** /**
* This class maintains key-value pairs of Kerberos configurable constants * This class maintains key-value pairs of Kerberos configurable constants
...@@ -57,6 +58,41 @@ import sun.security.krb5.internal.Krb5; ...@@ -57,6 +58,41 @@ import sun.security.krb5.internal.Krb5;
public class Config { 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. * Only allow a single instance of Config.
*/ */
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -155,11 +155,11 @@ class KrbAsRep extends KrbKdcRep { ...@@ -155,11 +155,11 @@ class KrbAsRep extends KrbKdcRep {
rep.encKDCRepPart = enc_part; rep.encKDCRepPart = enc_part;
ASReq req = asReq.getMessage(); ASReq req = asReq.getMessage();
check(true, req, rep); check(true, req, rep, dkey);
creds = new Credentials( creds = new Credentials(
rep.ticket, rep.ticket,
req.reqBody.cname, rep.cname,
enc_part.sname, enc_part.sname,
enc_part.key, enc_part.key,
enc_part.flags, enc_part.flags,
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -35,6 +35,7 @@ import sun.security.krb5.internal.*; ...@@ -35,6 +35,7 @@ import sun.security.krb5.internal.*;
import sun.security.krb5.internal.crypto.Nonce; import sun.security.krb5.internal.crypto.Nonce;
import sun.security.krb5.internal.crypto.KeyUsage; import sun.security.krb5.internal.crypto.KeyUsage;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays;
/** /**
* This class encapsulates the KRB-AS-REQ message that the client * This class encapsulates the KRB-AS-REQ message that the client
...@@ -57,7 +58,8 @@ public class KrbAsReq { ...@@ -57,7 +58,8 @@ public class KrbAsReq {
KerberosTime till, // ok, will use KerberosTime till, // ok, will use
KerberosTime rtime, // ok KerberosTime rtime, // ok
int[] eTypes, // NO int[] eTypes, // NO
HostAddresses addresses // ok HostAddresses addresses, // ok
PAData[] extraPAs // ok
) )
throws KrbException, IOException { throws KrbException, IOException {
...@@ -99,6 +101,15 @@ public class KrbAsReq { ...@@ -99,6 +101,15 @@ public class KrbAsReq {
paData[0] = new PAData( Krb5.PA_ENC_TIMESTAMP, paData[0] = new PAData( Krb5.PA_ENC_TIMESTAMP,
encTs.asn1Encode()); 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) { if (cname.getRealm() == null) {
throw new RealmException(Krb5.REALM_NULL, throw new RealmException(Krb5.REALM_NULL,
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -252,7 +252,9 @@ public final class KrbAsReqBuilder { ...@@ -252,7 +252,9 @@ public final class KrbAsReqBuilder {
* @throws KrbException * @throws KrbException
* @throws IOException * @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; int[] eTypes;
if (password != null) { if (password != null) {
eTypes = EType.getDefaults("default_tkt_enctypes"); eTypes = EType.getDefaults("default_tkt_enctypes");
...@@ -262,6 +264,14 @@ public final class KrbAsReqBuilder { ...@@ -262,6 +264,14 @@ public final class KrbAsReqBuilder {
ks); ks);
for (EncryptionKey k: ks) k.destroy(); 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, return new KrbAsReq(key,
options, options,
cname, cname,
...@@ -270,7 +280,8 @@ public final class KrbAsReqBuilder { ...@@ -270,7 +280,8 @@ public final class KrbAsReqBuilder {
till, till,
rtime, rtime,
eTypes, eTypes,
addresses); addresses,
extraPAs);
} }
/** /**
...@@ -308,11 +319,15 @@ public final class KrbAsReqBuilder { ...@@ -308,11 +319,15 @@ public final class KrbAsReqBuilder {
*/ */
private KrbAsReqBuilder send() throws KrbException, IOException { private KrbAsReqBuilder send() throws KrbException, IOException {
boolean preAuthFailedOnce = false; boolean preAuthFailedOnce = false;
KdcComm comm = new KdcComm(cname.getRealmAsString()); KdcComm comm = null;
EncryptionKey pakey = null; EncryptionKey pakey = null;
ReferralsState referralsState = new ReferralsState();
while (true) { while (true) {
if (referralsState.refreshComm()) {
comm = new KdcComm(cname.getRealmAsString());
}
try { try {
req = build(pakey); req = build(pakey, referralsState);
rep = new KrbAsRep(comm.send(req.encoding())); rep = new KrbAsRep(comm.send(req.encoding()));
return this; return this;
} catch (KrbException ke) { } catch (KrbException ke) {
...@@ -341,12 +356,69 @@ public final class KrbAsReqBuilder { ...@@ -341,12 +356,69 @@ public final class KrbAsReqBuilder {
} }
paList = kerr.getPA(); // Update current paList paList = kerr.getPA(); // Update current paList
} else { } else {
if (referralsState.handleError(ke)) {
continue;
}
throw ke; 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. * Performs AS-REQ send and AS-REP receive.
* Maybe a state is needed here, to divide prepare process and getCreds. * Maybe a state is needed here, to divide prepare process and getCreds.
......
...@@ -31,23 +31,41 @@ ...@@ -31,23 +31,41 @@
package sun.security.krb5; package sun.security.krb5;
import sun.security.krb5.internal.*; import sun.security.krb5.internal.*;
import sun.security.krb5.internal.crypto.KeyUsage;
import sun.security.util.DerInputStream;
abstract class KrbKdcRep { abstract class KrbKdcRep {
static void check( static void check(
boolean isAsReq, boolean isAsReq,
KDCReq req, KDCReq req,
KDCRep rep KDCRep rep,
EncryptionKey replyKey
) throws KrbApErrException { ) 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(); rep.encKDCRepPart.key.destroy();
throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED); 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)) { if (!req.reqBody.sname.equals(rep.encKDCRepPart.sname)) {
rep.encKDCRepPart.key.destroy(); String[] snameStrings = rep.encKDCRepPart.sname.getNameStrings();
throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED); 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) { if (req.reqBody.getNonce() != rep.encKDCRepPart.nonce) {
...@@ -104,6 +122,46 @@ abstract class KrbKdcRep { ...@@ -104,6 +122,46 @@ abstract class KrbKdcRep {
throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED); throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED);
} }
// 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);
}
}
if (req.reqBody.kdcOptions.get(KDCOptions.RENEWABLE)) if (req.reqBody.kdcOptions.get(KDCOptions.RENEWABLE))
if (req.reqBody.rtime != null && !req.reqBody.rtime.isZero()) if (req.reqBody.rtime != null && !req.reqBody.rtime.isZero())
// verify this is required // verify this is required
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -84,7 +84,7 @@ public class KrbTgsRep extends KrbKdcRep { ...@@ -84,7 +84,7 @@ public class KrbTgsRep extends KrbKdcRep {
EncTGSRepPart enc_part = new EncTGSRepPart(ref); EncTGSRepPart enc_part = new EncTGSRepPart(ref);
rep.encKDCRepPart = enc_part; rep.encKDCRepPart = enc_part;
check(false, req, rep); check(false, req, rep, tgsReq.tgsReqKey);
this.creds = new Credentials(rep.ticket, this.creds = new Credentials(rep.ticket,
rep.cname, rep.cname,
......
/* /*
* Copyright (c) 2000, 2013, 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -57,59 +57,23 @@ public class KrbTgsReq { ...@@ -57,59 +57,23 @@ public class KrbTgsReq {
private byte[] ibuf; private byte[] ibuf;
// Used in CredentialsUtil // Used in CredentialsUtil
public KrbTgsReq(Credentials asCreds, public KrbTgsReq(KDCOptions options, Credentials asCreds,
PrincipalName sname) PrincipalName cname, PrincipalName sname,
Ticket[] additionalTickets, PAData[] extraPAs)
throws KrbException, IOException { throws KrbException, IOException {
this(new KDCOptions(), this(options,
asCreds, asCreds,
sname, cname,
null, // KerberosTime from sname,
null, // KerberosTime till null, // KerberosTime from
null, // KerberosTime rtime null, // KerberosTime till
null, // eTypes, // null, // int[] eTypes null, // KerberosTime rtime
null, // HostAddresses addresses null, // int[] eTypes
null, // AuthorizationData authorizationData null, // HostAddresses addresses
null, // Ticket[] additionalTickets null, // AuthorizationData authorizationData
null); // EncryptionKey subSessionKey additionalTickets,
} null, // EncryptionKey subKey
extraPAs);
// S4U2proxy
public KrbTgsReq(Credentials asCreds,
Ticket second,
PrincipalName sname)
throws KrbException, IOException {
this(KDCOptions.with(KDCOptions.CNAME_IN_ADDL_TKT,
KDCOptions.FORWARDABLE),
asCreds,
sname,
null,
null,
null,
null,
null,
null,
new Ticket[] {second}, // the service ticket
null);
}
// S4U2user
public KrbTgsReq(Credentials asCreds,
PrincipalName sname,
PAData extraPA)
throws KrbException, IOException {
this(KDCOptions.with(KDCOptions.FORWARDABLE),
asCreds,
asCreds.getClient(),
sname,
null,
null,
null,
null,
null,
null,
null,
null,
extraPA); // the PA-FOR-USER
} }
// Called by Credentials, KrbCred // Called by Credentials, KrbCred
...@@ -143,7 +107,7 @@ public class KrbTgsReq { ...@@ -143,7 +107,7 @@ public class KrbTgsReq {
AuthorizationData authorizationData, AuthorizationData authorizationData,
Ticket[] additionalTickets, Ticket[] additionalTickets,
EncryptionKey subKey, EncryptionKey subKey,
PAData extraPA) throws KrbException, IOException { PAData[] extraPAs) throws KrbException, IOException {
princName = cname; princName = cname;
servName = sname; servName = sname;
...@@ -216,7 +180,7 @@ public class KrbTgsReq { ...@@ -216,7 +180,7 @@ public class KrbTgsReq {
authorizationData, authorizationData,
additionalTickets, additionalTickets,
subKey, subKey,
extraPA); extraPAs);
obuf = tgsReqMessg.asn1Encode(); obuf = tgsReqMessg.asn1Encode();
// XXX We need to revisit this to see if can't move it // XXX We need to revisit this to see if can't move it
...@@ -282,7 +246,7 @@ public class KrbTgsReq { ...@@ -282,7 +246,7 @@ public class KrbTgsReq {
AuthorizationData authorizationData, AuthorizationData authorizationData,
Ticket[] additionalTickets, Ticket[] additionalTickets,
EncryptionKey subKey, EncryptionKey subKey,
PAData extraPA) PAData[] extraPAs)
throws IOException, KrbException, UnknownHostException { throws IOException, KrbException, UnknownHostException {
KerberosTime req_till = null; KerberosTime req_till = null;
if (till == null) { if (till == null) {
...@@ -375,11 +339,14 @@ public class KrbTgsReq { ...@@ -375,11 +339,14 @@ public class KrbTgsReq {
null).getMessage(); null).getMessage();
PAData tgsPAData = new PAData(Krb5.PA_TGS_REQ, tgs_ap_req); PAData tgsPAData = new PAData(Krb5.PA_TGS_REQ, tgs_ap_req);
return new TGSReq( PAData[] pa;
extraPA != null ? if (extraPAs != null) {
new PAData[] {extraPA, tgsPAData } : pa = Arrays.copyOf(extraPAs, extraPAs.length + 1);
new PAData[] {tgsPAData}, pa[extraPAs.length] = tgsPAData;
reqBody); } else {
pa = new PAData[] {tgsPAData};
}
return new TGSReq(pa, reqBody);
} }
TGSReq getMessage() { TGSReq getMessage() {
......
/* /*
* Copyright (c) 2000, 2013, 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -90,6 +90,11 @@ public class PrincipalName implements Cloneable { ...@@ -90,6 +90,11 @@ public class PrincipalName implements Cloneable {
*/ */
public static final int KRB_NT_UID = 5; public static final int KRB_NT_UID = 5;
/**
* Enterprise name (alias)
*/
public static final int KRB_NT_ENTERPRISE = 10;
/** /**
* TGS Name * TGS Name
*/ */
...@@ -454,6 +459,7 @@ public class PrincipalName implements Cloneable { ...@@ -454,6 +459,7 @@ public class PrincipalName implements Cloneable {
case KRB_NT_SRV_INST: case KRB_NT_SRV_INST:
case KRB_NT_SRV_XHST: case KRB_NT_SRV_XHST:
case KRB_NT_UID: case KRB_NT_UID:
case KRB_NT_ENTERPRISE:
nameStrings = nameParts; nameStrings = nameParts;
nameType = type; nameType = type;
if (realm != null) { if (realm != null) {
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -33,6 +33,8 @@ package sun.security.krb5.internal; ...@@ -33,6 +33,8 @@ package sun.security.krb5.internal;
import sun.security.krb5.*; import sun.security.krb5.*;
import java.io.IOException; import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
/** /**
* This class is a utility that contains much of the TGS-Exchange * This class is a utility that contains much of the TGS-Exchange
...@@ -61,13 +63,11 @@ public class CredentialsUtil { ...@@ -61,13 +63,11 @@ public class CredentialsUtil {
if (!ccreds.isForwardable()) { if (!ccreds.isForwardable()) {
throw new KrbException("S4U2self needs a FORWARDABLE ticket"); throw new KrbException("S4U2self needs a FORWARDABLE ticket");
} }
KrbTgsReq req = new KrbTgsReq( Credentials creds = serviceCreds(KDCOptions.with(KDCOptions.FORWARDABLE),
ccreds, ccreds, ccreds.getClient(), ccreds.getClient(), null,
ccreds.getClient(), new PAData[] {new PAData(Krb5.PA_FOR_USER,
new PAData(Krb5.PA_FOR_USER, new PAForUserEnc(client,
new PAForUserEnc(client, ccreds.getSessionKey()).asn1Encode())});
ccreds.getSessionKey()).asn1Encode()));
Credentials creds = req.sendAndGetCreds();
if (!creds.getClient().equals(client)) { if (!creds.getClient().equals(client)) {
throw new KrbException("S4U2self request not honored by KDC"); throw new KrbException("S4U2self request not honored by KDC");
} }
...@@ -89,11 +89,10 @@ public class CredentialsUtil { ...@@ -89,11 +89,10 @@ public class CredentialsUtil {
String backend, Ticket second, String backend, Ticket second,
PrincipalName client, Credentials ccreds) PrincipalName client, Credentials ccreds)
throws KrbException, IOException { throws KrbException, IOException {
KrbTgsReq req = new KrbTgsReq( Credentials creds = serviceCreds(KDCOptions.with(
ccreds, KDCOptions.CNAME_IN_ADDL_TKT, KDCOptions.FORWARDABLE),
second, ccreds, ccreds.getClient(), new PrincipalName(backend),
new PrincipalName(backend)); new Ticket[] {second}, null);
Credentials creds = req.sendAndGetCreds();
if (!creds.getClient().equals(client)) { if (!creds.getClient().equals(client)) {
throw new KrbException("S4U2proxy request not honored by KDC"); throw new KrbException("S4U2proxy request not honored by KDC");
} }
...@@ -114,53 +113,9 @@ public class CredentialsUtil { ...@@ -114,53 +113,9 @@ public class CredentialsUtil {
public static Credentials acquireServiceCreds( public static Credentials acquireServiceCreds(
String service, Credentials ccreds) String service, Credentials ccreds)
throws KrbException, IOException { throws KrbException, IOException {
PrincipalName sname = new PrincipalName(service); PrincipalName sname = new PrincipalName(service,
String serviceRealm = sname.getRealmString(); PrincipalName.KRB_NT_SRV_HST);
String localRealm = ccreds.getClient().getRealmString(); return serviceCreds(sname, ccreds);
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");
} }
/** /**
...@@ -305,6 +260,148 @@ public class CredentialsUtil { ...@@ -305,6 +260,148 @@ public class CredentialsUtil {
private static Credentials serviceCreds( private static Credentials serviceCreds(
PrincipalName service, Credentials ccreds) PrincipalName service, Credentials ccreds)
throws KrbException, IOException { 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 { ...@@ -47,7 +47,8 @@ public class EncASRepPart extends EncKDCRepPart {
KerberosTime new_endtime, KerberosTime new_endtime,
KerberosTime new_renewTill, KerberosTime new_renewTill,
PrincipalName new_sname, PrincipalName new_sname,
HostAddresses new_caddr) { HostAddresses new_caddr,
PAData[] new_pAData) {
super( super(
new_key, new_key,
new_lastReq, new_lastReq,
...@@ -60,6 +61,7 @@ public class EncASRepPart extends EncKDCRepPart { ...@@ -60,6 +61,7 @@ public class EncASRepPart extends EncKDCRepPart {
new_renewTill, new_renewTill,
new_sname, new_sname,
new_caddr, new_caddr,
new_pAData,
Krb5.KRB_ENC_AS_REP_PART Krb5.KRB_ENC_AS_REP_PART
); );
//may need to use Krb5.KRB_ENC_TGS_REP_PART to mimic //may need to use Krb5.KRB_ENC_TGS_REP_PART to mimic
......
...@@ -31,7 +31,6 @@ ...@@ -31,7 +31,6 @@
package sun.security.krb5.internal; package sun.security.krb5.internal;
import sun.security.krb5.*; import sun.security.krb5.*;
import sun.security.krb5.EncryptionKey;
import sun.security.util.*; import sun.security.util.*;
import java.util.Vector; import java.util.Vector;
import java.io.IOException; import java.io.IOException;
...@@ -41,19 +40,20 @@ import java.math.BigInteger; ...@@ -41,19 +40,20 @@ import java.math.BigInteger;
* Implements the ASN.1 EncKDCRepPart type. * Implements the ASN.1 EncKDCRepPart type.
* *
* <xmp> * <xmp>
* EncKDCRepPart ::= SEQUENCE { * EncKDCRepPart ::= SEQUENCE {
* key [0] EncryptionKey, * key [0] EncryptionKey,
* last-req [1] LastReq, * last-req [1] LastReq,
* nonce [2] UInt32, * nonce [2] UInt32,
* key-expiration [3] KerberosTime OPTIONAL, * key-expiration [3] KerberosTime OPTIONAL,
* flags [4] TicketFlags, * flags [4] TicketFlags,
* authtime [5] KerberosTime, * authtime [5] KerberosTime,
* starttime [6] KerberosTime OPTIONAL, * starttime [6] KerberosTime OPTIONAL,
* endtime [7] KerberosTime, * endtime [7] KerberosTime,
* renew-till [8] KerberosTime OPTIONAL, * renew-till [8] KerberosTime OPTIONAL,
* srealm [9] Realm, * srealm [9] Realm,
* sname [10] PrincipalName, * sname [10] PrincipalName,
* caddr [11] HostAddresses OPTIONAL * caddr [11] HostAddresses OPTIONAL,
* encrypted-pa-data [12] SEQUENCE OF PA-DATA OPTIONAL
* } * }
* </xmp> * </xmp>
* *
...@@ -76,6 +76,7 @@ public class EncKDCRepPart { ...@@ -76,6 +76,7 @@ public class EncKDCRepPart {
public KerberosTime renewTill; //optional public KerberosTime renewTill; //optional
public PrincipalName sname; public PrincipalName sname;
public HostAddresses caddr; //optional public HostAddresses caddr; //optional
public PAData[] pAData; //optional
public int msgType; //not included in sequence public int msgType; //not included in sequence
public EncKDCRepPart( public EncKDCRepPart(
...@@ -90,6 +91,7 @@ public class EncKDCRepPart { ...@@ -90,6 +91,7 @@ public class EncKDCRepPart {
KerberosTime new_renewTill, KerberosTime new_renewTill,
PrincipalName new_sname, PrincipalName new_sname,
HostAddresses new_caddr, HostAddresses new_caddr,
PAData[] new_pAData,
int new_msgType) { int new_msgType) {
key = new_key; key = new_key;
lastReq = new_lastReq; lastReq = new_lastReq;
...@@ -102,6 +104,7 @@ public class EncKDCRepPart { ...@@ -102,6 +104,7 @@ public class EncKDCRepPart {
renewTill = new_renewTill; renewTill = new_renewTill;
sname = new_sname; sname = new_sname;
caddr = new_caddr; caddr = new_caddr;
pAData = new_pAData;
msgType = new_msgType; msgType = new_msgType;
} }
...@@ -160,6 +163,9 @@ public class EncKDCRepPart { ...@@ -160,6 +163,9 @@ public class EncKDCRepPart {
if (der.getData().available() > 0) { if (der.getData().available() > 0) {
caddr = HostAddresses.parse(der.getData(), (byte) 0x0B, true); 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 // We observe extra data from MSAD
/*if (der.getData().available() > 0) { /*if (der.getData().available() > 0) {
throw new Asn1Exception(Krb5.ASN1_BAD_ID); throw new Asn1Exception(Krb5.ASN1_BAD_ID);
...@@ -175,47 +181,58 @@ public class EncKDCRepPart { ...@@ -175,47 +181,58 @@ public class EncKDCRepPart {
*/ */
public byte[] asn1Encode(int rep_type) throws Asn1Exception, public byte[] asn1Encode(int rep_type) throws Asn1Exception,
IOException { IOException {
DerOutputStream bytes;
DerOutputStream temp = new DerOutputStream(); DerOutputStream temp = new DerOutputStream();
DerOutputStream bytes = new DerOutputStream(); DerOutputStream out = new DerOutputStream();
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, out.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x00), key.asn1Encode()); true, (byte) 0x00), key.asn1Encode());
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, out.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x01), lastReq.asn1Encode()); true, (byte) 0x01), lastReq.asn1Encode());
temp.putInteger(BigInteger.valueOf(nonce)); temp.putInteger(BigInteger.valueOf(nonce));
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, out.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x02), temp); true, (byte) 0x02), temp);
if (keyExpiration != null) { if (keyExpiration != null) {
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, out.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x03), keyExpiration.asn1Encode()); true, (byte) 0x03), keyExpiration.asn1Encode());
} }
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, out.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x04), flags.asn1Encode()); true, (byte) 0x04), flags.asn1Encode());
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, out.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x05), authtime.asn1Encode()); true, (byte) 0x05), authtime.asn1Encode());
if (starttime != null) { if (starttime != null) {
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, out.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x06), starttime.asn1Encode()); true, (byte) 0x06), starttime.asn1Encode());
} }
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, out.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x07), endtime.asn1Encode()); true, (byte) 0x07), endtime.asn1Encode());
if (renewTill != null) { if (renewTill != null) {
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, out.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x08), renewTill.asn1Encode()); 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()); 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()); true, (byte) 0x0A), sname.asn1Encode());
if (caddr != null) { if (caddr != null) {
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, out.write(DerValue.createTag(DerValue.TAG_CONTEXT,
true, (byte) 0x0B), caddr.asn1Encode()); 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 //should use the rep_type to build the encoding
//but other implementations do not; it is ignored and //but other implementations do not; it is ignored and
//the cached msgType is used instead //the cached msgType is used instead
temp = new DerOutputStream(); temp = new DerOutputStream();
temp.write(DerValue.tag_Sequence, bytes); temp.write(DerValue.tag_Sequence, out);
bytes = new DerOutputStream(); bytes = new DerOutputStream();
bytes.write(DerValue.createTag(DerValue.TAG_APPLICATION, bytes.write(DerValue.createTag(DerValue.TAG_APPLICATION,
true, (byte) msgType), temp); true, (byte) msgType), temp);
......
...@@ -46,7 +46,8 @@ public class EncTGSRepPart extends EncKDCRepPart { ...@@ -46,7 +46,8 @@ public class EncTGSRepPart extends EncKDCRepPart {
KerberosTime new_endtime, KerberosTime new_endtime,
KerberosTime new_renewTill, KerberosTime new_renewTill,
PrincipalName new_sname, PrincipalName new_sname,
HostAddresses new_caddr) { HostAddresses new_caddr,
PAData[] new_pAData) {
super( super(
new_key, new_key,
new_lastReq, new_lastReq,
...@@ -59,6 +60,7 @@ public class EncTGSRepPart extends EncKDCRepPart { ...@@ -59,6 +60,7 @@ public class EncTGSRepPart extends EncKDCRepPart {
new_renewTill, new_renewTill,
new_sname, new_sname,
new_caddr, new_caddr,
new_pAData,
Krb5.KRB_ENC_TGS_REP_PART); Krb5.KRB_ENC_TGS_REP_PART);
} }
......
...@@ -140,6 +140,7 @@ public class KDCOptions extends KerberosFlags { ...@@ -140,6 +140,7 @@ public class KDCOptions extends KerberosFlags {
public static final int UNUSED10 = 10; public static final int UNUSED10 = 10;
public static final int UNUSED11 = 11; public static final int UNUSED11 = 11;
public static final int CNAME_IN_ADDL_TKT = 14; 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 RENEWABLE_OK = 27;
public static final int ENC_TKT_IN_SKEY = 28; public static final int ENC_TKT_IN_SKEY = 28;
public static final int RENEW = 30; public static final int RENEW = 30;
...@@ -160,7 +161,8 @@ public class KDCOptions extends KerberosFlags { ...@@ -160,7 +161,8 @@ public class KDCOptions extends KerberosFlags {
"UNUSED11", //11; "UNUSED11", //11;
null,null, null,null,
"CNAME_IN_ADDL_TKT",//14; "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; "RENEWABLE_OK", //27;
"ENC_TKT_IN_SKEY", //28; "ENC_TKT_IN_SKEY", //28;
null, null,
......
...@@ -59,9 +59,9 @@ import java.math.BigInteger; ...@@ -59,9 +59,9 @@ import java.math.BigInteger;
public class KDCReq { public class KDCReq {
public KDCReqBody reqBody; public KDCReqBody reqBody;
public PAData[] pAData = null; //optional
private int pvno; private int pvno;
private int msgType; private int msgType;
private PAData[] pAData = null; //optional
public KDCReq(PAData[] new_pAData, KDCReqBody new_reqBody, public KDCReq(PAData[] new_pAData, KDCReqBody new_reqBody,
int req_type) throws IOException { int req_type) throws IOException {
...@@ -144,23 +144,7 @@ public class KDCReq { ...@@ -144,23 +144,7 @@ public class KDCReq {
} else { } else {
throw new Asn1Exception(Krb5.ASN1_BAD_ID); throw new Asn1Exception(Krb5.ASN1_BAD_ID);
} }
if ((der.getData().peekByte() & 0x1F) == 0x03) { pAData = PAData.parseSequence(der.getData(), (byte) 0x03, true);
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;
}
subDer = der.getData().getDerValue(); subDer = der.getData().getDerValue();
if ((subDer.getTag() & 0x01F) == 0x04) { if ((subDer.getTag() & 0x01F) == 0x04) {
DerValue subsubDer = subDer.getData().getDerValue(); DerValue subsubDer = subDer.getData().getDerValue();
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -90,6 +90,7 @@ public class KRBError implements java.io.Serializable { ...@@ -90,6 +90,7 @@ public class KRBError implements java.io.Serializable {
private KerberosTime sTime; private KerberosTime sTime;
private Integer suSec; private Integer suSec;
private int errorCode; private int errorCode;
private Realm crealm; //optional
private PrincipalName cname; //optional private PrincipalName cname; //optional
private PrincipalName sname; private PrincipalName sname;
private String eText; //optional private String eText; //optional
...@@ -138,6 +139,7 @@ public class KRBError implements java.io.Serializable { ...@@ -138,6 +139,7 @@ public class KRBError implements java.io.Serializable {
sTime = new_sTime; sTime = new_sTime;
suSec = new_suSec; suSec = new_suSec;
errorCode = new_errorCode; errorCode = new_errorCode;
crealm = new_cname.getRealm();
cname = new_cname; cname = new_cname;
sname = new_sname; sname = new_sname;
eText = new_eText; eText = new_eText;
...@@ -166,6 +168,7 @@ public class KRBError implements java.io.Serializable { ...@@ -166,6 +168,7 @@ public class KRBError implements java.io.Serializable {
sTime = new_sTime; sTime = new_sTime;
suSec = new_suSec; suSec = new_suSec;
errorCode = new_errorCode; errorCode = new_errorCode;
crealm = new_cname.getRealm();
cname = new_cname; cname = new_cname;
sname = new_sname; sname = new_sname;
eText = new_eText; eText = new_eText;
...@@ -262,6 +265,10 @@ public class KRBError implements java.io.Serializable { ...@@ -262,6 +265,10 @@ public class KRBError implements java.io.Serializable {
pa = paList.toArray(new PAData[paList.size()]); pa = paList.toArray(new PAData[paList.size()]);
} }
public final Realm getClientRealm() {
return crealm;
}
public final KerberosTime getServerTime() { public final KerberosTime getServerTime() {
return sTime; return sTime;
} }
...@@ -349,7 +356,7 @@ public class KRBError implements java.io.Serializable { ...@@ -349,7 +356,7 @@ public class KRBError implements java.io.Serializable {
errorCode = subDer.getData().getBigInteger().intValue(); errorCode = subDer.getData().getBigInteger().intValue();
} }
else throw new Asn1Exception(Krb5.ASN1_BAD_ID); 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); cname = PrincipalName.parse(der.getData(), (byte)0x08, true, crealm);
Realm realm = Realm.parse(der.getData(), (byte)0x09, false); Realm realm = Realm.parse(der.getData(), (byte)0x09, false);
sname = PrincipalName.parse(der.getData(), (byte)0x0A, false, realm); sname = PrincipalName.parse(der.getData(), (byte)0x0A, false, realm);
...@@ -393,6 +400,9 @@ public class KRBError implements java.io.Serializable { ...@@ -393,6 +400,9 @@ public class KRBError implements java.io.Serializable {
System.out.println("\t suSec is " + suSec); System.out.println("\t suSec is " + suSec);
System.out.println("\t error code is " + errorCode); System.out.println("\t error code is " + errorCode);
System.out.println("\t error Message is " + Krb5.getErrorMessage(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) { if (cname != null) {
System.out.println("\t cname is " + cname.toString()); System.out.println("\t cname is " + cname.toString());
} }
...@@ -442,8 +452,10 @@ public class KRBError implements java.io.Serializable { ...@@ -442,8 +452,10 @@ public class KRBError implements java.io.Serializable {
temp.putInteger(BigInteger.valueOf(errorCode)); temp.putInteger(BigInteger.valueOf(errorCode));
bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x06), temp); 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) { 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()); bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x08), cname.asn1Encode());
} }
...@@ -488,6 +500,7 @@ public class KRBError implements java.io.Serializable { ...@@ -488,6 +500,7 @@ public class KRBError implements java.io.Serializable {
isEqual(sTime, other.sTime) && isEqual(sTime, other.sTime) &&
isEqual(suSec, other.suSec) && isEqual(suSec, other.suSec) &&
errorCode == other.errorCode && errorCode == other.errorCode &&
isEqual(crealm, other.crealm) &&
isEqual(cname, other.cname) && isEqual(cname, other.cname) &&
isEqual(sname, other.sname) && isEqual(sname, other.sname) &&
isEqual(eText, other.eText) && isEqual(eText, other.eText) &&
...@@ -508,6 +521,7 @@ public class KRBError implements java.io.Serializable { ...@@ -508,6 +521,7 @@ public class KRBError implements java.io.Serializable {
if (sTime != null) result = 37 * result + sTime.hashCode(); if (sTime != null) result = 37 * result + sTime.hashCode();
if (suSec != null) result = 37 * result + suSec.hashCode(); if (suSec != null) result = 37 * result + suSec.hashCode();
result = 37 * result + errorCode; result = 37 * result + errorCode;
if (crealm != null) result = 37 * result + crealm.hashCode();
if (cname != null) result = 37 * result + cname.hashCode(); if (cname != null) result = 37 * result + cname.hashCode();
if (sname != null) result = 37 * result + sname.hashCode(); if (sname != null) result = 37 * result + sname.hashCode();
if (eText != null) result = 37 * result + eText.hashCode(); if (eText != null) result = 37 * result + eText.hashCode();
......
/* /*
* Copyright (c) 2000, 2013, 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -70,6 +70,7 @@ public class Krb5 { ...@@ -70,6 +70,7 @@ public class Krb5 {
public static final int TKT_OPTS_PRE_AUTHENT = 10; public static final int TKT_OPTS_PRE_AUTHENT = 10;
public static final int TKT_OPTS_HW_AUTHENT = 11; public static final int TKT_OPTS_HW_AUTHENT = 11;
public static final int TKT_OPTS_DELEGATE = 13; 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; public static final int TKT_OPTS_MAX = 31;
// KDC Options // KDC Options
...@@ -163,6 +164,9 @@ public class Krb5 { ...@@ -163,6 +164,9 @@ public class Krb5 {
// S4U2user info // S4U2user info
public static final int PA_FOR_USER = 129; 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 //authorization data type |ad-type value
//-------------------------------+------------- //-------------------------------+-------------
...@@ -265,6 +269,7 @@ public class Krb5 { ...@@ -265,6 +269,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_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_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_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_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_NOREALM = 62;
public static final int KRB_AP_ERR_GEN_CRED = 63; public static final int KRB_AP_ERR_GEN_CRED = 63;
......
/* /*
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -34,6 +35,8 @@ import sun.security.krb5.KrbException; ...@@ -34,6 +35,8 @@ import sun.security.krb5.KrbException;
import sun.security.util.*; import sun.security.util.*;
import sun.security.krb5.Asn1Exception; import sun.security.krb5.Asn1Exception;
import java.io.IOException; import java.io.IOException;
import java.util.Vector;
import sun.security.krb5.internal.util.KerberosString; import sun.security.krb5.internal.util.KerberosString;
/** /**
...@@ -138,6 +141,41 @@ public class PAData { ...@@ -138,6 +141,41 @@ public class PAData {
return ((pADataValue == null) ? null : pADataValue.clone()); 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. * Gets the preferred etype from the PAData array.
* 1. ETYPE-INFO2-ENTRY with unknown s2kparams ignored * 1. ETYPE-INFO2-ENTRY with unknown s2kparams ignored
......
/*
* 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; ...@@ -51,7 +51,8 @@ import java.io.IOException;
* renewable(8), * renewable(8),
* initial(9), * initial(9),
* pre-authent(10), * pre-authent(10),
* hw-authent(11) * hw-authent(11),
* enc-pa-rep(15)
* } * }
*/ */
public class TicketFlags extends KerberosFlags { public class TicketFlags extends KerberosFlags {
...@@ -178,6 +179,9 @@ public class TicketFlags extends KerberosFlags { ...@@ -178,6 +179,9 @@ public class TicketFlags extends KerberosFlags {
case 11: case 11:
sb.append("HW-AUTHENT;"); sb.append("HW-AUTHENT;");
break; break;
case 15:
sb.append("ENC-PA-REP;");
break;
} }
} }
} }
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -56,6 +56,7 @@ public class KeyUsage { ...@@ -56,6 +56,7 @@ public class KeyUsage {
public static final int KU_KRB_SAFE_CKSUM = 15; // KrbSafe 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_PA_FOR_USER_ENC_CKSUM = 17; // S4U2user
public static final int KU_AD_KDC_ISSUED_CKSUM = 19; 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) { public static final boolean isValid(int usage) {
return usage >= 0; return usage >= 0;
......
...@@ -420,6 +420,32 @@ networkaddress.cache.negative.ttl=10 ...@@ -420,6 +420,32 @@ networkaddress.cache.negative.ttl=10
# krb5.kdc.bad.policy = tryLess:2,2000 # krb5.kdc.bad.policy = tryLess:2,2000
krb5.kdc.bad.policy = tryLast 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 # Algorithm restrictions for certification path (CertPath) processing
# #
# In some environments, certain algorithms or key lengths may be undesirable # In some environments, certain algorithms or key lengths may be undesirable
......
...@@ -420,6 +420,32 @@ networkaddress.cache.negative.ttl=10 ...@@ -420,6 +420,32 @@ networkaddress.cache.negative.ttl=10
# krb5.kdc.bad.policy = tryLess:2,2000 # krb5.kdc.bad.policy = tryLess:2,2000
krb5.kdc.bad.policy = tryLast 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 # Algorithm restrictions for certification path (CertPath) processing
# #
# In some environments, certain algorithms or key lengths may be undesirable # In some environments, certain algorithms or key lengths may be undesirable
......
...@@ -423,6 +423,32 @@ networkaddress.cache.negative.ttl=10 ...@@ -423,6 +423,32 @@ networkaddress.cache.negative.ttl=10
# krb5.kdc.bad.policy = tryLess:2,2000 # krb5.kdc.bad.policy = tryLess:2,2000
krb5.kdc.bad.policy = tryLast 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 # Algorithm restrictions for certification path (CertPath) processing
# #
# In some environments, certain algorithms or key lengths may be undesirable # In some environments, certain algorithms or key lengths may be undesirable
......
...@@ -422,6 +422,32 @@ networkaddress.cache.negative.ttl=10 ...@@ -422,6 +422,32 @@ networkaddress.cache.negative.ttl=10
# krb5.kdc.bad.policy = tryLess:2,2000 # krb5.kdc.bad.policy = tryLess:2,2000
krb5.kdc.bad.policy = tryLast 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 # Algorithm restrictions for certification path (CertPath) processing
# #
# In some environments, certain algorithms or key lengths may be undesirable # In some environments, certain algorithms or key lengths may be undesirable
......
...@@ -423,6 +423,32 @@ networkaddress.cache.negative.ttl=10 ...@@ -423,6 +423,32 @@ networkaddress.cache.negative.ttl=10
# krb5.kdc.bad.policy = tryLess:2,2000 # krb5.kdc.bad.policy = tryLess:2,2000
krb5.kdc.bad.policy = tryLast 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 # Algorithm restrictions for certification path (CertPath) processing
# #
# In some environments, certain algorithms or key lengths may be undesirable # In some environments, certain algorithms or key lengths may be undesirable
......
...@@ -141,6 +141,14 @@ public class KDC { ...@@ -141,6 +141,14 @@ public class KDC {
private TreeMap<String,char[]> passwords = new TreeMap<> private TreeMap<String,char[]> passwords = new TreeMap<>
(String.CASE_INSENSITIVE_ORDER); (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 // Realm name
private String realm; private String realm;
// KDC // KDC
...@@ -492,6 +500,29 @@ public class KDC { ...@@ -492,6 +500,29 @@ public class KDC {
return port; 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 // Private helper methods
/** /**
...@@ -680,6 +711,17 @@ public class KDC { ...@@ -680,6 +711,17 @@ public class KDC {
PrincipalName cname = null; PrincipalName cname = null;
boolean allowForwardable = true; 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) { if (pas == null || pas.length == 0) {
throw new KrbException(Krb5.KDC_ERR_PADATA_TYPE_NOSUPP); throw new KrbException(Krb5.KDC_ERR_PADATA_TYPE_NOSUPP);
} else { } else {
...@@ -847,7 +889,8 @@ public class KDC { ...@@ -847,7 +889,8 @@ public class KDC {
body.addresses != null // always set caddr body.addresses != null // always set caddr
? body.addresses ? body.addresses
: new HostAddresses( : new HostAddresses(
new InetAddress[]{InetAddress.getLocalHost()}) new InetAddress[]{InetAddress.getLocalHost()}),
null
); );
EncryptedData edata = new EncryptedData(ckey, enc_part.asn1Encode(), KeyUsage.KU_ENC_TGS_REP_PART_SESSKEY); EncryptedData edata = new EncryptedData(ckey, enc_part.asn1Encode(), KeyUsage.KU_ENC_TGS_REP_PART_SESSKEY);
TGSRep tgsRep = new TGSRep(null, TGSRep tgsRep = new TGSRep(null,
...@@ -890,6 +933,7 @@ public class KDC { ...@@ -890,6 +933,7 @@ public class KDC {
*/ */
protected byte[] processAsReq(byte[] in) throws Exception { protected byte[] processAsReq(byte[] in) throws Exception {
ASReq asReq = new ASReq(in); ASReq asReq = new ASReq(in);
byte[] asReqbytes = asReq.asn1Encode();
int[] eTypes = null; int[] eTypes = null;
List<PAData> outPAs = new ArrayList<>(); List<PAData> outPAs = new ArrayList<>();
...@@ -909,6 +953,24 @@ public class KDC { ...@@ -909,6 +953,24 @@ public class KDC {
eTypes = KDCReqBodyDotEType(body); eTypes = KDCReqBodyDotEType(body);
int eType = eTypes[0]; 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 ckey = keyForUser(body.cname, eType, false);
EncryptionKey skey = keyForUser(service, eType, true); EncryptionKey skey = keyForUser(service, eType, true);
...@@ -1089,20 +1151,33 @@ public class KDC { ...@@ -1089,20 +1151,33 @@ public class KDC {
} }
PAData[] inPAs = KDCReqDotPAData(asReq); PAData[] inPAs = KDCReqDotPAData(asReq);
List<PAData> enc_outPAs = new ArrayList<>();
if (inPAs == null || inPAs.length == 0) { if (inPAs == null || inPAs.length == 0) {
Object preauth = options.get(Option.PREAUTH_REQUIRED); Object preauth = options.get(Option.PREAUTH_REQUIRED);
if (preauth == null || preauth.equals(Boolean.TRUE)) { if (preauth == null || preauth.equals(Boolean.TRUE)) {
throw new KrbException(Krb5.KDC_ERR_PREAUTH_REQUIRED); throw new KrbException(Krb5.KDC_ERR_PREAUTH_REQUIRED);
} }
} else { } else {
EncryptionKey pakey = null;
try { try {
EncryptedData data = newEncryptedData(new DerValue(inPAs[0].getValue())); 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); data.decrypt(pakey, KeyUsage.KU_PA_ENC_TS);
} catch (Exception e) { } catch (Exception e) {
throw new KrbException(Krb5.KDC_ERR_PREAUTH_FAILED); throw new KrbException(Krb5.KDC_ERR_PREAUTH_FAILED);
} }
bFlags[Krb5.TKT_OPTS_PRE_AUTHENT] = true; 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); TicketFlags tFlags = new TicketFlags(bFlags);
...@@ -1133,7 +1208,8 @@ public class KDC { ...@@ -1133,7 +1208,8 @@ public class KDC {
body.from, body.from,
till, rtime, till, rtime,
service, 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); EncryptedData edata = new EncryptedData(ckey, enc_part.asn1Encode(), KeyUsage.KU_ENC_AS_REP_PART);
ASRep asRep = new ASRep( ASRep asRep = new ASRep(
...@@ -1180,8 +1256,10 @@ public class KDC { ...@@ -1180,8 +1256,10 @@ public class KDC {
if (kerr == null) { if (kerr == null) {
if (ke.returnCode() == Krb5.KDC_ERR_PREAUTH_REQUIRED || if (ke.returnCode() == Krb5.KDC_ERR_PREAUTH_REQUIRED ||
ke.returnCode() == Krb5.KDC_ERR_PREAUTH_FAILED) { 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(); DerOutputStream bytes = new DerOutputStream();
bytes.write(new PAData(Krb5.PA_ENC_TIMESTAMP, new byte[0]).asn1Encode());
for (PAData p: outPAs) { for (PAData p: outPAs) {
bytes.write(p.asn1Encode()); bytes.write(p.asn1Encode());
} }
......
/*
* 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
* @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");
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册