Merge pull request #12458 from egregius313/egregius313/promote-insecure-ldap-authentication

Java: Promote LDAP Authentication Query
This commit is contained in:
Edward Minnix III
2023-03-28 10:39:17 -04:00
committed by GitHub
16 changed files with 330 additions and 390 deletions

View File

@@ -0,0 +1,128 @@
/** Provides classes to reason about insecure LDAP authentication. */
import java
private import semmle.code.java.dataflow.DataFlow
private import semmle.code.java.frameworks.Networking
private import semmle.code.java.frameworks.Jndi
/**
* An expression that represents an insecure (non-SSL, non-private) LDAP URL.
*/
class InsecureLdapUrl extends Expr {
InsecureLdapUrl() {
this instanceof InsecureLdapUrlLiteral
or
// Concatentation of insecure protcol and non-private host:
// protocol + host + ...
exists(AddExpr e, CompileTimeConstantExpr protocol, Expr rest, Expr host |
e = this and
protocol = e.getLeftOperand() and
rest = e.getRightOperand() and
if rest instanceof AddExpr then host = rest.(AddExpr).getLeftOperand() else host = rest
|
protocol.getStringValue() = "ldap://" and
not exists(string hostString | hostString = getHostname(host) |
hostString.length() = 0 or // Empty host is loopback address
hostString instanceof PrivateHostName
)
)
}
}
/**
* A sink representing the construction of a `DirContextEnvironment`.
*/
class InsecureLdapUrlSink extends DataFlow::Node {
InsecureLdapUrlSink() {
exists(ConstructorCall cc |
cc.getConstructedType().getAnAncestor() instanceof TypeDirContext and
this.asExpr() = cc.getArgument(0)
)
}
}
/**
* Holds if `ma` sets `java.naming.security.authentication` (also known as `Context.SECURITY_AUTHENTICATION`) to `simple` in some `Hashtable`.
*/
predicate isBasicAuthEnv(MethodAccess ma) {
hasFieldValueEnv(ma, "java.naming.security.authentication", "simple") or
hasFieldNameEnv(ma, "SECURITY_AUTHENTICATION", "simple")
}
/**
* Holds if `ma` sets `java.naming.security.protocol` (also known as `Context.SECURITY_PROTOCOL`) to `ssl` in some `Hashtable`.
*/
predicate isSslEnv(MethodAccess ma) {
hasFieldValueEnv(ma, "java.naming.security.protocol", "ssl") or
hasFieldNameEnv(ma, "SECURITY_PROTOCOL", "ssl")
}
/**
* Holds if `ma` writes the `java.naming.provider.url` (also known as `Context.PROVIDER_URL`) key of a `Hashtable`.
*/
predicate isProviderUrlSetter(MethodAccess ma) {
ma.getMethod().getDeclaringType().getAnAncestor() instanceof TypeHashtable and
ma.getMethod().hasName(["put", "setProperty"]) and
(
ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = "java.naming.provider.url"
or
exists(Field f |
ma.getArgument(0) = f.getAnAccess() and
f.hasName("PROVIDER_URL") and
f.getDeclaringType() instanceof TypeNamingContext
)
)
}
/**
* An insecure (non-SSL, non-private) LDAP URL string literal.
*/
private class InsecureLdapUrlLiteral extends StringLiteral {
InsecureLdapUrlLiteral() {
// Match connection strings with the LDAP protocol and without private IP addresses to reduce false positives.
exists(string s | this.getValue() = s |
s.regexpMatch("(?i)ldap://[\\[a-zA-Z0-9].*") and
not s.substring(7, s.length()) instanceof PrivateHostName
)
}
}
/** The class `java.util.Hashtable`. */
private class TypeHashtable extends Class {
TypeHashtable() { this.getSourceDeclaration().hasQualifiedName("java.util", "Hashtable") }
}
/** Get the string value of an expression representing a hostname. */
private string getHostname(Expr expr) {
result = expr.(CompileTimeConstantExpr).getStringValue() or
result =
expr.(VarAccess).getVariable().getAnAssignedValue().(CompileTimeConstantExpr).getStringValue()
}
/**
* Holds if `ma` sets `fieldValue` to `envValue` in some `Hashtable`.
*/
bindingset[fieldValue, envValue]
private predicate hasFieldValueEnv(MethodAccess ma, string fieldValue, string envValue) {
// environment.put("java.naming.security.authentication", "simple")
ma.getMethod().getDeclaringType().getAnAncestor() instanceof TypeHashtable and
ma.getMethod().hasName(["put", "setProperty"]) and
ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = fieldValue and
ma.getArgument(1).(CompileTimeConstantExpr).getStringValue() = envValue
}
/**
* Holds if `ma` sets attribute name `fieldName` to `envValue` in some `Hashtable`.
*/
bindingset[fieldName, envValue]
private predicate hasFieldNameEnv(MethodAccess ma, string fieldName, string envValue) {
// environment.put(Context.SECURITY_AUTHENTICATION, "simple")
ma.getMethod().getDeclaringType().getAnAncestor() instanceof TypeHashtable and
ma.getMethod().hasName(["put", "setProperty"]) and
exists(Field f |
ma.getArgument(0) = f.getAnAccess() and
f.hasName(fieldName) and
f.getDeclaringType() instanceof TypeNamingContext
) and
ma.getArgument(1).(CompileTimeConstantExpr).getStringValue() = envValue
}

View File

@@ -0,0 +1,59 @@
/** Provides dataflow configurations to reason about insecure LDAP authentication. */
import java
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.frameworks.Jndi
import semmle.code.java.security.InsecureLdapAuth
/**
* A taint-tracking configuration for `ldap://` URL in LDAP authentication.
*/
module InsecureLdapUrlConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof InsecureLdapUrl }
predicate isSink(DataFlow::Node sink) { sink instanceof InsecureLdapUrlSink }
/** Method call of `env.put()`. */
predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(MethodAccess ma |
pred.asExpr() = ma.getArgument(1) and
isProviderUrlSetter(ma) and
succ.asExpr() = ma.getQualifier()
)
}
}
module InsecureLdapUrlFlow = TaintTracking::Global<InsecureLdapUrlConfig>;
/**
* A taint-tracking configuration for `simple` basic-authentication in LDAP configuration.
*/
private module BasicAuthConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node src) {
exists(MethodAccess ma |
isBasicAuthEnv(ma) and
ma.getQualifier() = src.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr()
)
}
predicate isSink(DataFlow::Node sink) { sink instanceof InsecureLdapUrlSink }
}
module BasicAuthFlow = DataFlow::Global<BasicAuthConfig>;
/**
* A taint-tracking configuration for `ssl` configuration in LDAP authentication.
*/
private module RequiresSslConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node src) {
exists(MethodAccess ma |
isSslEnv(ma) and
ma.getQualifier() = src.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr()
)
}
predicate isSink(DataFlow::Node sink) { sink instanceof InsecureLdapUrlSink }
}
module RequiresSslFlow = DataFlow::Global<RequiresSslConfig>;