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

Extend RestrictedSecurity constraints #872

Merged
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
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* ===========================================================================
* (c) Copyright IBM Corp. 2022, 2024 All Rights Reserved
* (c) Copyright IBM Corp. 2022, 2025 All Rights Reserved
* ===========================================================================
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -248,14 +248,27 @@ public static boolean isFIPSEnabled() {
}

/**
* Check if the service is allowed in restricted security mode.
* Check if the service is allowed to be used in restricted security mode.
*
* @param service the service to check
* @return true if the service is allowed
* @return true if the service is allowed to be used
*/
public static boolean isServiceAllowed(Service service) {
if (securityEnabled) {
return restricts.isRestrictedServiceAllowed(service);
return restricts.isRestrictedServiceAllowed(service, true);
}
return true;
}

/**
* Check if the service is allowed to be registered in restricted security mode.
*
* @param service the service to check
* @return true if the service is allowed to be registered
*/
public static boolean canServiceBeRegistered(Service service) {
if (securityEnabled) {
return restricts.isRestrictedServiceAllowed(service, false);
}
return true;
}
Expand Down Expand Up @@ -758,10 +771,11 @@ private RestrictedSecurityProperties(String profileID, ProfileParser parser) {
/**
* Check if the Service is allowed in restricted security mode.
*
* @param service the Service to check
* @param service the Service to check
* @param checkUse should its attempted use be checked against the accepted
* @return true if the Service is allowed
*/
boolean isRestrictedServiceAllowed(Service service) {
boolean isRestrictedServiceAllowed(Service service, boolean checkUse) {
Provider provider = service.getProvider();
String providerClassName = provider.getClass().getName();

Expand Down Expand Up @@ -797,11 +811,13 @@ boolean isRestrictedServiceAllowed(Service service) {
String cType = constraint.type;
String cAlgorithm = constraint.algorithm;
String cAttribute = constraint.attributes;
String cAcceptedUses = constraint.acceptedUses;
if (debug != null) {
debug.println("Checking provider constraint:"
+ "\n\tService type: " + cType
+ "\n\tAlgorithm: " + cAlgorithm
+ "\n\tAttributes: " + cAttribute);
+ "\n\tAttributes: " + cAttribute
+ "\n\tAccepted uses: " + cAcceptedUses);
}

if (!isAsterisk(cType) && !type.equals(cType)) {
Expand All @@ -819,56 +835,114 @@ boolean isRestrictedServiceAllowed(Service service) {
continue;
}

// For type and algorithm match, and attribute is *.
if (isAsterisk(cAttribute)) {
if (debug != null) {
debug.println("The following service:"
+ "\n\tService type: " + type
+ "\n\tAlgorithm: " + algorithm
+ "\nis allowed in provider: " + providerClassName);
}
return true;
}

// For type and algorithm match, and attribute is not *.
// Then continue checking attributes.
String[] cAttributeArray = cAttribute.split(":");
if (!isAsterisk(cAttribute)) {
String[] cAttributeArray = cAttribute.split(":");

// For each attribute, must be all matched for return allowed.
for (String attribute : cAttributeArray) {
String[] input = attribute.split("=", 2);
// For each attribute, must be all matched for return allowed.
for (String attribute : cAttributeArray) {
String[] input = attribute.split("=", 2);

String cName = input[0].trim();
String cValue = input[1].trim();
String sValue = service.getAttribute(cName);
if (debug != null) {
debug.println("Checking specific attribute with:"
+ "\n\tName: " + cName
+ "\n\tValue: " + cValue
+ "\nagainst the service attribute value: " + sValue);
String cName = input[0].trim();
String cValue = input[1].trim();
String sValue = service.getAttribute(cName);
if (debug != null) {
debug.println("Checking specific attribute with:"
+ "\n\tName: " + cName
+ "\n\tValue: " + cValue
+ "\nagainst the service attribute value: " + sValue);
}
if ((sValue == null) || !cValue.equalsIgnoreCase(sValue)) {
// If any attribute doesn't match, return service is not allowed.
if (debug != null) {
debug.println("Attributes don't match!");
debug.println("The following service:"
+ "\n\tService type: " + type
+ "\n\tAlgorithm: " + algorithm
+ "\n\tAttribute: " + cAttribute
+ "\nis NOT allowed in provider: " + providerClassName);
}
return false;
}
if (debug != null) {
debug.println("Attributes match!");
}
}
if ((sValue == null) || !cValue.equalsIgnoreCase(sValue)) {
// If any attribute doesn't match, return service is not allowed.
}

// See if accepted uses have been specified and apply
// them to the call stack.
if (checkUse && (cAcceptedUses != null)) {
String[] optionAndValue = cAcceptedUses.split(":");
String option = optionAndValue[0];
String value = optionAndValue[1];
StackTraceElement[] stackElements = Thread.currentThread().getStackTrace();
boolean found = false;
for (StackTraceElement stackElement : stackElements) {
if (debug != null) {
debug.println("Attributes don't match!");
debug.println("Attempting to match " + stackElement + " with: " + option + " : " + value);
}
String stackElemModule = stackElement.getModuleName();
String stackElemFullClassName = stackElement.getClassName();
int stackElemEnd = stackElemFullClassName.lastIndexOf('.');
String stackElemPackage = null;
if (stackElemEnd != -1) {
stackElemPackage = stackElemFullClassName.substring(0, stackElemEnd);
}
String module;
switch (option) {
case "ModuleAndFullClassName":
String[] moduleAndFullClassName = value.split("/");
module = moduleAndFullClassName[0];
String fullClassName = moduleAndFullClassName[1];
found = module.equals(stackElemModule) && stackElemFullClassName.equals(fullClassName);
break;
case "ModuleAndPackage":
String[] moduleAndPackage = value.split("/");
module = moduleAndPackage[0];
String packageValue = moduleAndPackage[1];
found = module.equals(stackElemModule) && packageValue.equals(stackElemPackage);
break;
case "FullClassName":
found = stackElemFullClassName.equals(value);
break;
case "Package":
found = value.equals(stackElemPackage);
break;
default:
printStackTraceAndExit("Incorrect option to match in constraint: " + constraint);
break;
}

if (found) {
break;
}
}

// If nothing matching the accepted uses is found in the call stack,
// this service is not allowed.
if (!found) {
if (debug != null) {
debug.println("Classes in call stack are not part of accepted uses!");
debug.println("The following service:"
+ "\n\tService type: " + type
+ "\n\tAlgorithm: " + algorithm
+ "\n\tAttribute: " + cAttribute
+ "\n\tAccepted uses: " + cAcceptedUses
+ "\nis NOT allowed in provider: " + providerClassName);
}
return false;
}
if (debug != null) {
debug.println("Attributes match!");
}
}

if (debug != null) {
debug.println("All attributes matched!");
debug.println("The following service:"
+ "\n\tService type: " + type
+ "\n\tAlgorithm: " + algorithm
+ "\n\tAttribute: " + cAttribute
+ "\n\tAccepted uses: " + cAcceptedUses
+ "\nis allowed in provider: " + providerClassName);
}
return true;
Expand Down Expand Up @@ -1458,7 +1532,8 @@ private void setConstraints(String providerName, String providerInfo, boolean pr
final String typeRE = "\\w+";
final String algoRE = "[A-Za-z0-9./_-]+";
final String attrRE = "[A-Za-z0-9=*|.:]+";
final String consRE = "\\{(" + typeRE + "),(" + algoRE + "),(" + attrRE + ")\\}";
final String usesRE = "[A-Za-z0-9._:/$]+";
final String consRE = "\\{(" + typeRE + "),(" + algoRE + "),(" + attrRE + ")(," + usesRE + ")?\\}";
p = Pattern.compile(
"\\["
+ "([+-]?)" // option to append or remove
Expand All @@ -1481,6 +1556,42 @@ private void setConstraints(String providerName, String providerInfo, boolean pr
String inType = m.group(1);
String inAlgorithm = m.group(2);
String inAttributes = m.group(3);
String inAcceptedUses = m.group(4);

if (inAcceptedUses != null) {
inAcceptedUses = inAcceptedUses.substring(1);
boolean isSpecIncorrect = false;
String[] optionAndValue = inAcceptedUses.split(":");
if (optionAndValue.length != 2) {
isSpecIncorrect = true;
} else {
String option = optionAndValue[0];
String value = optionAndValue[1];
switch (option) {
case "ModuleAndFullClassName":
if (value.split("/").length != 2) {
isSpecIncorrect = true;
}
break;
case "ModuleAndPackage":
if (value.split("/").length != 2) {
isSpecIncorrect = true;
}
break;
case "FullClassName":
case "Package":
// Nothing further to check in those options.
break;
default:
isSpecIncorrect = true;
break;
}
}
if (isSpecIncorrect) {
printStackTraceAndExit("Incorrect specification of accepted uses in constraint for "
+ inType + ", " + inAlgorithm + ": " + inAcceptedUses);
}
}

// Each attribute must includes 2 fields (key and value) or *.
if (!isAsterisk(inAttributes)) {
Expand All @@ -1493,7 +1604,8 @@ private void setConstraints(String providerName, String providerInfo, boolean pr
}
}
}
Constraint constraint = new Constraint(inType, inAlgorithm, inAttributes);

Constraint constraint = new Constraint(inType, inAlgorithm, inAttributes, inAcceptedUses);
constraints.add(constraint);
}

Expand Down Expand Up @@ -1765,7 +1877,7 @@ private static void checkProviderFormat(String providerInfo, boolean update) {
+ "(\\[" // constraints [optional]
+ "\\s*"
+ "([+-])?" // action [optional]
+ "[A-Za-z0-9{}.=*|:,/_\\s-]+" // constraint definition
+ "[A-Za-z0-9{}.=*|:$,/_\\s-]+" // constraint definition
+ "\\])?"
+ "\\s*"
+ "$");
Expand Down Expand Up @@ -1828,17 +1940,27 @@ private static final class Constraint {
final String type;
final String algorithm;
final String attributes;
final String acceptedUses;

Constraint(String type, String algorithm, String attributes) {
Constraint(String type, String algorithm, String attributes, String acceptedUses) {
super();
this.type = type;
this.algorithm = algorithm;
this.attributes = attributes;
this.acceptedUses = acceptedUses;
}

@Override
public String toString() {
return "{" + type + ", " + algorithm + ", " + attributes + "}";
StringBuilder buffer = new StringBuilder();
buffer.append("{").append(type);
buffer.append(", ").append(algorithm);
buffer.append(", ").append(attributes);
if (acceptedUses != null) {
buffer.append(", ").append(acceptedUses);
}
buffer.append("}");
return buffer.toString();
}

@Override
Expand All @@ -1850,14 +1972,15 @@ public boolean equals(Object obj) {
Constraint other = (Constraint) obj;
return Objects.equals(type, other.type)
&& Objects.equals(algorithm, other.algorithm)
&& Objects.equals(attributes, other.attributes);
&& Objects.equals(attributes, other.attributes)
&& Objects.equals(acceptedUses, other.acceptedUses);
}
return false;
}

@Override
public int hashCode() {
return Objects.hash(type, algorithm, attributes);
return Objects.hash(type, algorithm, attributes, acceptedUses);
}
}
}
Loading