Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

option to validate subject ou fields in cert requests #574

Merged
merged 1 commit into from
Oct 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions servers/zts/conf/zts.properties
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,25 @@ athenz.zts.cert_signer_factory_class=com.yahoo.athenz.zts.cert.impl.SelfCertSign
# the property is not set, then no validation is carried out.
#athenz.zts.cert_allowed_o_values=

# If enabled, ZTS server will validate the OU field in
# any certificate request if one is specified. If the
# certificate is requested from a Copper Argos provider, the provider
# service name is automatically allowed as one of the valid OU
# values. Otherwise, the list of values can be configured using
# the athenz.zts.cert_allowed_ou_values property.
#athenz.zts.cert_request_verify_subject_ou=false

# List of valid values separated by | that a certificate
# request can include in the Subject OU field. For example, if
# you allow to create certs with c=US,o=Company,OU=Athenz,cn=athenz.api
# and c=US,o=Company Inc.,ou=Yahoo,cn=athenz.api, then the value for
# this property would be set to "Athenz|Yahoo". In case the
# certificate is requested from a Copper Argos provider, the provider
# service name is automatically allowed as one of the valid OU
# values. The validation is carried out only if the
# setting is enabled (set to true).
#athenz.zts.cert_allowed_ou_values=

# During certificate refresh operation, the server retrieves the
# certificate that was used for authentication and verifies that
# the dns names in the certificate match to the values specified
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public final class ZTSConsts {

public static final String ZTS_PROP_CERT_REFRESH_IP_FNAME = "athenz.zts.cert_refresh_ip_fname";
public static final String ZTS_PROP_CERT_ALLOWED_O_VALUES = "athenz.zts.cert_allowed_o_values";
public static final String ZTS_PROP_CERT_ALLOWED_OU_VALUES = "athenz.zts.cert_allowed_ou_values";
public static final String ZTS_PROP_INSTANCE_CERT_IP_FNAME = "athenz.zts.instance_cert_ip_fname";

public static final String ZTS_PROP_CERTSIGN_BASE_URI = "athenz.zts.certsign_base_uri";
Expand All @@ -78,6 +79,7 @@ public final class ZTSConsts {
public static final String ZTS_PROP_CERT_REFRESH_VERIFY_HOSTNAMES = "athenz.zts.cert_refresh_verify_hostnames";
public static final String ZTS_PROP_CERT_REFRESH_RESET_TIME = "athenz.zts.cert_refresh_reset_time";
public static final String ZTS_PROP_CERT_REQUEST_VERIFY_IP = "athenz.zts.cert_request_verify_ip";
public static final String ZTS_PROP_CERT_REQUEST_VERIFY_SUBJECT_OU = "athenz.zts.cert_request_verify_subject_ou";

public static final String ZTS_PROP_CERT_JDBC_STORE = "athenz.zts.cert_jdbc_store";
public static final String ZTS_PROP_CERT_JDBC_USER = "athenz.zts.cert_jdbc_user";
Expand Down Expand Up @@ -126,7 +128,8 @@ public final class ZTSConsts {
public static final String ZTS_CERT_USAGE = "certUsage";
public static final String ZTS_CERT_EXPIRY_TIME = "certExpiryTime";
public static final String ZTS_CERT_REFRESH = "certRefresh";

public static final String ZTS_CERT_SUBJECT_OU = "certSubjectOU";

public static final String ZTS_CERT_USAGE_CLIENT = "client";
public static final String ZTS_CERT_USAGE_SERVER = "server";

Expand Down
59 changes: 53 additions & 6 deletions servers/zts/src/main/java/com/yahoo/athenz/zts/ZTSImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ public class ZTSImpl implements KeyStore, ZTSHandler {
protected boolean leastPrivilegePrincipal = false;
protected Set<String> authorizedProxyUsers = null;
protected Set<String> validCertSubjectOrgValues = null;
protected Set<String> validCertSubjectOrgUnitValues = null;
protected boolean secureRequestsOnly = true;
protected int svcTokenTimeout = 86400;
protected Set<String> authFreeUriSet = null;
Expand All @@ -115,6 +116,7 @@ public class ZTSImpl implements KeyStore, ZTSHandler {
protected boolean readOnlyMode = false;
protected boolean verifyCertRefreshHostnames = true;
protected boolean verifyCertRequestIP = false;
protected boolean verifyCertSubjectOU = false;

private static final String TYPE_DOMAIN_NAME = "DomainName";
private static final String TYPE_SIMPLE_NAME = "SimpleName";
Expand Down Expand Up @@ -408,17 +410,28 @@ void loadConfigurationSettings() {
verifyCertRequestIP = Boolean.parseBoolean(
System.getProperty(ZTSConsts.ZTS_PROP_CERT_REQUEST_VERIFY_IP, "false"));

// configure if we should validate subject ou fields to match
// provider names

verifyCertSubjectOU = Boolean.parseBoolean(
System.getProperty(ZTSConsts.ZTS_PROP_CERT_REQUEST_VERIFY_SUBJECT_OU, "false"));

// x509 certificate issue reset time if configured

x509CertRefreshResetTime = Long.parseLong(
System.getProperty(ZTSConsts.ZTS_PROP_CERT_REFRESH_RESET_TIME, "0"));

// list of valid O values for any certificate request
// list of valid O and OU values for any certificate request

final String validCertSubjectOrgValueList = System.getProperty(ZTSConsts.ZTS_PROP_CERT_ALLOWED_O_VALUES);
if (validCertSubjectOrgValueList != null) {
validCertSubjectOrgValues = new HashSet<>(Arrays.asList(validCertSubjectOrgValueList.split("\\|")));
}

final String validCertSubjectOrgUnitValueList = System.getProperty(ZTSConsts.ZTS_PROP_CERT_ALLOWED_OU_VALUES);
if (validCertSubjectOrgUnitValueList != null) {
validCertSubjectOrgUnitValues = new HashSet<>(Arrays.asList(validCertSubjectOrgUnitValueList.split("\\|")));
}
}

static String getServerHostName() {
Expand Down Expand Up @@ -1476,7 +1489,7 @@ public RoleToken postRoleCertificateRequest(ResourceContext ctx, String domainNa
final String ipAddress = ServletRequestUtil.getRemoteAddress(ctx.request());

if (!validateRoleCertificateRequest(req.getCsr(), domainName, roles, principalName,
cert, ipAddress, validCertSubjectOrgValues)) {
cert, ipAddress)) {
throw requestError("postRoleCertificateRequest: Unable to validate cert request",
caller, domainName);
}
Expand All @@ -1495,7 +1508,7 @@ public RoleToken postRoleCertificateRequest(ResourceContext ctx, String domainNa

boolean validateRoleCertificateRequest(final String csr, final String domainName,
Set<String> roles, final String principal, X509Certificate cert,
final String ip, Set<String> validOrgValues) {
final String ip) {

X509RoleCertRequest certReq;
try {
Expand All @@ -1505,7 +1518,14 @@ boolean validateRoleCertificateRequest(final String csr, final String domainName
return false;
}

if (!certReq.validate(roles, domainName, principal, validOrgValues)) {
if (!certReq.validate(roles, domainName, principal, validCertSubjectOrgValues)) {
return false;
}

// validate the CSR subject ou field

if (verifyCertSubjectOU && !certReq.validateSubjectOUField(null, null,
validCertSubjectOrgUnitValues)) {
return false;
}

Expand Down Expand Up @@ -1772,6 +1792,7 @@ public Response postInstanceRegisterInformation(ResourceContext ctx, InstanceReg
// request and override it with its own value.

String certUsage = null;
String certSubjectOU = null;
int certExpiryTime = 0;
boolean certRefreshAllowed = true;

Expand All @@ -1784,8 +1805,17 @@ public Response postInstanceRegisterInformation(ResourceContext ctx, InstanceReg
if (certRefreshState != null && !certRefreshState.isEmpty()) {
certRefreshAllowed = Boolean.parseBoolean(certRefreshState);
}
certSubjectOU = instanceAttrs.remove(ZTSConsts.ZTS_CERT_SUBJECT_OU);
}


// validate the CSR subject ou field. We're doing this check here
// because the provider can tell us what the ou field should be

if (verifyCertSubjectOU && !certReq.validateSubjectOUField(provider, certSubjectOU,
validCertSubjectOrgUnitValues)) {
throw requestError("CSR Subject OrgUnit validation failed", caller, domain);
}

// generate certificate for the instance

Object timerX509CertMetric = metric.startTiming("certsignx509_timing", null);
Expand Down Expand Up @@ -2084,14 +2114,24 @@ InstanceIdentity processProviderX509RefreshRequest(ResourceContext ctx, final Pr
// request and override it with its own value.

String certUsage = null;
String certSubjectOU = null;
int certExpiryTime = 0;
Map<String, String> instanceAttrs = instance.getAttributes();
if (instanceAttrs != null) {
certUsage = instanceAttrs.remove(ZTSConsts.ZTS_CERT_USAGE);
final String expiryTime = instanceAttrs.remove(ZTSConsts.ZTS_CERT_EXPIRY_TIME);
certExpiryTime = ZTSUtils.parseInt(expiryTime);
certSubjectOU = instanceAttrs.remove(ZTSConsts.ZTS_CERT_SUBJECT_OU);
}


// validate the CSR subject ou field. We're doing this check here
// because the provider can tell us what the ou field should be

if (verifyCertSubjectOU && !certReq.validateSubjectOUField(provider, certSubjectOU,
validCertSubjectOrgUnitValues)) {
throw requestError("CSR Subject OrgUnit validation failed", caller, domain);
}

// validate that the tenant domain/service matches to the values
// in the cert record when it was initially issued

Expand Down Expand Up @@ -2406,6 +2446,13 @@ public Identity postInstanceRefreshRequest(ResourceContext ctx, String domain,
throw requestError("Invalid CSR - invalid Subject O field", caller, domain);
}

// validate the CSR subject ou field

if (verifyCertSubjectOU && !x509CertReq.validateSubjectOUField(null, null,
validCertSubjectOrgUnitValues)) {
throw requestError("Invalid CSR - invalid Subject OU field", caller, domain);
}

// verify that the public key in the csr matches to the service
// public key registered in Athenz

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,36 @@ public boolean validateSubjectOField(Set<String> validValues) {
return false;
}
}


public boolean validateSubjectOUField(final String provider, final String certSubjectOU,
Set<String> validValues) {

try {
final String value = Crypto.extractX509CSRSubjectOUField(certReq);
if (value == null) {
return true;
}
// we have three values that we want to possible match against
// a) provider callback specified value
// b) provider name
// c) configured set of valid ou names

if (value.equalsIgnoreCase(certSubjectOU)) {
return true;
} else if (value.equalsIgnoreCase(provider)) {
return true;
} else if (validValues != null && !validValues.isEmpty() && validValues.contains(value)) {
return true;
} else {
LOGGER.error("Failed to validate Subject OU Field {}", value);
}
return false;
} catch (CryptoException ex) {
LOGGER.error("Unable to extract Subject OU Field: {}", ex.getMessage());
return false;
}
}

boolean extractCsrPublicKey() {

// if we have already extracted our public key
Expand Down
Loading