Skip to content

Commit 3936aea

Browse files
committed
Split Ldap query file into libraries
1 parent 9275b54 commit 3936aea

File tree

3 files changed

+207
-198
lines changed

3 files changed

+207
-198
lines changed
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/** Provides classes to reason about insecure LDAP authentication. */
2+
3+
import java
4+
import semmle.code.java.frameworks.Networking
5+
import semmle.code.java.frameworks.Jndi
6+
7+
/**
8+
* Insecure (non-SSL, non-private) LDAP URL string literal.
9+
*/
10+
class InsecureLdapUrlLiteral extends StringLiteral {
11+
InsecureLdapUrlLiteral() {
12+
// Match connection strings with the LDAP protocol and without private IP addresses to reduce false positives.
13+
exists(string s | this.getValue() = s |
14+
s.regexpMatch("(?i)ldap://[\\[a-zA-Z0-9].*") and
15+
not s.substring(7, s.length()) instanceof PrivateHostName
16+
)
17+
}
18+
}
19+
20+
/** The class `java.util.Hashtable`. */
21+
class TypeHashtable extends Class {
22+
TypeHashtable() { this.getSourceDeclaration().hasQualifiedName("java.util", "Hashtable") }
23+
}
24+
25+
string getHostname(Expr expr) {
26+
result = expr.(CompileTimeConstantExpr).getStringValue() or
27+
result =
28+
expr.(VarAccess).getVariable().getAnAssignedValue().(CompileTimeConstantExpr).getStringValue()
29+
}
30+
31+
/**
32+
* Holds if a non-private LDAP string is concatenated from both protocol and host.
33+
*/
34+
predicate concatInsecureLdapString(CompileTimeConstantExpr protocol, Expr host) {
35+
protocol.getStringValue() = "ldap://" and
36+
not exists(string hostString | hostString = getHostname(host) |
37+
hostString.length() = 0 or // Empty host is loopback address
38+
hostString instanceof PrivateHostName
39+
)
40+
}
41+
42+
// Expr getLeftmostConcatOperand(Expr expr) {
43+
// if expr instanceof AddExpr
44+
// then
45+
// result = expr.(AddExpr).getLeftOperand() and
46+
// not result instanceof AddExpr
47+
// else result = expr
48+
// }
49+
/**
50+
* String concatenated with `InsecureLdapUrlLiteral`.
51+
*/
52+
class InsecureLdapUrl extends Expr {
53+
InsecureLdapUrl() {
54+
this instanceof InsecureLdapUrlLiteral
55+
or
56+
// protocol + host + ...
57+
exists(AddExpr e, CompileTimeConstantExpr protocol, Expr rest, Expr host |
58+
e = this and
59+
protocol = e.getLeftOperand() and
60+
rest = e.getRightOperand() and
61+
if rest instanceof AddExpr then host = rest.(AddExpr).getLeftOperand() else host = rest
62+
|
63+
protocol.getStringValue() = "ldap://" and
64+
concatInsecureLdapString(protocol, host)
65+
)
66+
}
67+
}
68+
69+
/**
70+
* Holds if `ma` writes the `java.naming.provider.url` (also known as `Context.PROVIDER_URL`) key of a `Hashtable`.
71+
*/
72+
predicate isProviderUrlSetter(MethodAccess ma) {
73+
ma.getMethod().getDeclaringType().getAnAncestor() instanceof TypeHashtable and
74+
ma.getMethod().hasName(["put", "setProperty"]) and
75+
(
76+
ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = "java.naming.provider.url"
77+
or
78+
exists(Field f |
79+
ma.getArgument(0) = f.getAnAccess() and
80+
f.hasName("PROVIDER_URL") and
81+
f.getDeclaringType() instanceof TypeNamingContext
82+
)
83+
)
84+
}
85+
86+
/**
87+
* Holds if `ma` sets `fieldValue` to `envValue` in some `Hashtable`.
88+
*/
89+
bindingset[fieldValue, envValue]
90+
predicate hasFieldValueEnv(MethodAccess ma, string fieldValue, string envValue) {
91+
// environment.put("java.naming.security.authentication", "simple")
92+
ma.getMethod().getDeclaringType().getAnAncestor() instanceof TypeHashtable and
93+
ma.getMethod().hasName(["put", "setProperty"]) and
94+
ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = fieldValue and
95+
ma.getArgument(1).(CompileTimeConstantExpr).getStringValue() = envValue
96+
}
97+
98+
/**
99+
* Holds if `ma` sets attribute name `fieldName` to `envValue` in some `Hashtable`.
100+
*/
101+
bindingset[fieldName, envValue]
102+
predicate hasFieldNameEnv(MethodAccess ma, string fieldName, string envValue) {
103+
// environment.put(Context.SECURITY_AUTHENTICATION, "simple")
104+
ma.getMethod().getDeclaringType().getAnAncestor() instanceof TypeHashtable and
105+
ma.getMethod().hasName(["put", "setProperty"]) and
106+
exists(Field f |
107+
ma.getArgument(0) = f.getAnAccess() and
108+
f.hasName(fieldName) and
109+
f.getDeclaringType() instanceof TypeNamingContext
110+
) and
111+
ma.getArgument(1).(CompileTimeConstantExpr).getStringValue() = envValue
112+
}
113+
114+
/**
115+
* Holds if `ma` sets `java.naming.security.authentication` (also known as `Context.SECURITY_AUTHENTICATION`) to `simple` in some `Hashtable`.
116+
*/
117+
predicate isBasicAuthEnv(MethodAccess ma) {
118+
hasFieldValueEnv(ma, "java.naming.security.authentication", "simple") or
119+
hasFieldNameEnv(ma, "SECURITY_AUTHENTICATION", "simple")
120+
}
121+
122+
/**
123+
* Holds if `ma` sets `java.naming.security.protocol` (also known as `Context.SECURITY_PROTOCOL`) to `ssl` in some `Hashtable`.
124+
*/
125+
predicate isSslEnv(MethodAccess ma) {
126+
hasFieldValueEnv(ma, "java.naming.security.protocol", "ssl") or
127+
hasFieldNameEnv(ma, "SECURITY_PROTOCOL", "ssl")
128+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/** Provides dataflow configurations to reason about insecure LDAP authentication. */
2+
3+
import java
4+
import DataFlow
5+
import semmle.code.java.dataflow.DataFlow
6+
import semmle.code.java.dataflow.TaintTracking
7+
import semmle.code.java.security.InsecureLdapAuth
8+
9+
/**
10+
* A taint-tracking configuration for `ldap://` URL in LDAP authentication.
11+
*/
12+
class InsecureUrlFlowConfig extends TaintTracking::Configuration {
13+
InsecureUrlFlowConfig() { this = "InsecureLdapAuth:InsecureUrlFlowConfig" }
14+
15+
/** Source of `ldap://` connection string. */
16+
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof InsecureLdapUrl }
17+
18+
/** Sink of directory context creation. */
19+
override predicate isSink(DataFlow::Node sink) {
20+
exists(ConstructorCall cc |
21+
cc.getConstructedType().getAnAncestor() instanceof TypeDirContext and
22+
sink.asExpr() = cc.getArgument(0)
23+
)
24+
}
25+
26+
/** Method call of `env.put()`. */
27+
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
28+
exists(MethodAccess ma |
29+
pred.asExpr() = ma.getArgument(1) and
30+
isProviderUrlSetter(ma) and
31+
succ.asExpr() = ma.getQualifier()
32+
)
33+
}
34+
}
35+
36+
/**
37+
* A taint-tracking configuration for `simple` basic-authentication in LDAP configuration.
38+
*/
39+
class BasicAuthFlowConfig extends DataFlow::Configuration {
40+
BasicAuthFlowConfig() { this = "InsecureLdapAuth:BasicAuthFlowConfig" }
41+
42+
/** Source of `simple` configuration. */
43+
override predicate isSource(DataFlow::Node src) {
44+
exists(MethodAccess ma |
45+
isBasicAuthEnv(ma) and ma.getQualifier() = src.(PostUpdateNode).getPreUpdateNode().asExpr()
46+
)
47+
}
48+
49+
/** Sink of directory context creation. */
50+
override predicate isSink(DataFlow::Node sink) {
51+
exists(ConstructorCall cc |
52+
cc.getConstructedType().getAnAncestor() instanceof TypeDirContext and
53+
sink.asExpr() = cc.getArgument(0)
54+
)
55+
}
56+
}
57+
58+
/**
59+
* A taint-tracking configuration for `ssl` configuration in LDAP authentication.
60+
*/
61+
class SslFlowConfig extends DataFlow::Configuration {
62+
SslFlowConfig() { this = "InsecureLdapAuth:SSLFlowConfig" }
63+
64+
/** Source of `ssl` configuration. */
65+
override predicate isSource(DataFlow::Node src) {
66+
exists(MethodAccess ma |
67+
isSslEnv(ma) and ma.getQualifier() = src.(PostUpdateNode).getPreUpdateNode().asExpr()
68+
)
69+
}
70+
71+
/** Sink of directory context creation. */
72+
override predicate isSink(DataFlow::Node sink) {
73+
exists(ConstructorCall cc |
74+
cc.getConstructedType().getAnAncestor() instanceof TypeDirContext and
75+
sink.asExpr() = cc.getArgument(0)
76+
)
77+
}
78+
}

0 commit comments

Comments
 (0)