@@ -2,9 +2,19 @@ import java
22import semmle.code.java.dataflow.FlowSources
33import DataFlow
44
5+ /** The interface `javax.naming.directory.DirContext`. */
6+ class TypeDirContext extends Interface {
7+ TypeDirContext ( ) { this .hasQualifiedName ( "javax.naming.directory" , "DirContext" ) }
8+ }
9+
10+ /** The interface `javax.naming.ldap.LdapContext`. */
11+ class TypeLdapContext extends Interface {
12+ TypeLdapContext ( ) { this .hasQualifiedName ( "javax.naming.ldap" , "LdapContext" ) }
13+ }
14+
515/** The class `com.unboundid.ldap.sdk.SearchRequest`. */
6- class TypeSearchRequest extends Class {
7- TypeSearchRequest ( ) { this .hasQualifiedName ( "com.unboundid.ldap.sdk" , "SearchRequest" ) }
16+ class TypeUnboundIdSearchRequest extends Class {
17+ TypeUnboundIdSearchRequest ( ) { this .hasQualifiedName ( "com.unboundid.ldap.sdk" , "SearchRequest" ) }
818}
919
1020/** The interface `com.unboundid.ldap.sdk.ReadOnlySearchRequest`. */
@@ -53,6 +63,20 @@ class TypeSpringLdapFilter extends Interface {
5363 TypeSpringLdapFilter ( ) { this .hasQualifiedName ( "org.springframework.ldap.filter" , "Filter" ) }
5464}
5565
66+ /** The interface `org.apache.directory.ldap.client.api.LdapConnection`. */
67+ class TypeLdapConnection extends Interface {
68+ TypeLdapConnection ( ) {
69+ this .hasQualifiedName ( "org.apache.directory.ldap.client.api" , "LdapConnection" )
70+ }
71+ }
72+
73+ /** The interface `org.apache.directory.api.ldap.model.message.SearchRequest`. */
74+ class TypeApacheSearchRequest extends Interface {
75+ TypeApacheSearchRequest ( ) {
76+ this .hasQualifiedName ( "org.apache.directory.api.ldap.model.message" , "SearchRequest" )
77+ }
78+ }
79+
5680/** The class `org.springframework.ldap.support.LdapEncoder`. */
5781class TypeLdapEncoder extends Class {
5882 TypeLdapEncoder ( ) { this .hasQualifiedName ( "org.springframework.ldap.support" , "LdapEncoder" ) }
@@ -87,9 +111,10 @@ class LdapInjectionFlowConfig extends TaintTracking::Configuration {
87111
88112 override predicate isAdditionalTaintStep ( DataFlow:: Node node1 , DataFlow:: Node node2 ) {
89113 filterStep ( node1 , node2 ) or
90- searchRequestStep ( node1 , node2 ) or
114+ unboundIdSearchRequestStep ( node1 , node2 ) or
91115 ldapQueryStep ( node1 , node2 ) or
92- hardcodedFilterStep ( node1 , node2 )
116+ hardcodedFilterStep ( node1 , node2 ) or
117+ apacheSearchRequestStep ( node1 , node2 )
93118 }
94119}
95120
@@ -103,30 +128,6 @@ class LocalSource extends LdapInjectionSource {
103128 LocalSource ( ) { this instanceof LocalUserInput }
104129}
105130
106- abstract class Context extends RefType { }
107-
108- /**
109- * The interface `javax.naming.directory.DirContext` or
110- * the class `javax.naming.directory.InitialDirContext`.
111- */
112- class DirContext extends Context {
113- DirContext ( ) {
114- this .hasQualifiedName ( "javax.naming.directory" , "DirContext" ) or
115- this .hasQualifiedName ( "javax.naming.directory" , "InitialDirContext" )
116- }
117- }
118-
119- /**
120- * The interface `javax.naming.ldap.LdapContext` or
121- * the class `javax.naming.ldap.InitialLdapContext`.
122- */
123- class LdapContext extends Context {
124- LdapContext ( ) {
125- this .hasQualifiedName ( "javax.naming.ldap" , "LdapContext" ) or
126- this .hasQualifiedName ( "javax.naming.ldap" , "InitialLdapContext" )
127- }
128- }
129-
130131/**
131132 * JNDI sink for LDAP injection vulnerabilities, i.e. 2nd argument to search method from
132133 * DirContext, InitialDirContext, LdapContext or InitialLdapContext.
@@ -137,7 +138,12 @@ class JndiLdapInjectionSink extends LdapInjectionSink {
137138 ma .getMethod ( ) = m and
138139 ma .getArgument ( index ) = this .getExpr ( )
139140 |
140- m .getDeclaringType ( ) instanceof Context and m .hasName ( "search" ) and index = 1
141+ (
142+ m .getDeclaringType ( ) .getAnAncestor ( ) instanceof TypeDirContext or
143+ m .getDeclaringType ( ) .getAnAncestor ( ) instanceof TypeLdapContext
144+ ) and
145+ m .hasName ( "search" ) and
146+ index = 1
141147 )
142148 }
143149}
@@ -161,7 +167,7 @@ class UnboundIdLdapInjectionSink extends LdapInjectionSink {
161167 (
162168 // Parameter type is SearchRequest or ReadOnlySearchRequest
163169 param .getType ( ) instanceof TypeReadOnlySearchRequest or
164- param .getType ( ) instanceof TypeSearchRequest or
170+ param .getType ( ) instanceof TypeUnboundIdSearchRequest or
165171 // Or parameter index is 2, 3, 5, 6 or 7 (this is where filter parameter is)
166172 index = any ( int i | i = [ 2 ..3 ] or i = [ 5 ..7 ] )
167173 )
@@ -201,6 +207,27 @@ class SpringLdapInjectionSink extends LdapInjectionSink {
201207 }
202208}
203209
210+ /** Apache LDAP API sink for LDAP injection vulnerabilities, i.e. LdapConnection.search method. */
211+ class ApacheLdapInjectionSink extends LdapInjectionSink {
212+ ApacheLdapInjectionSink ( ) {
213+ exists ( MethodAccess ma , Method m , int index , RefType paramType |
214+ ma .getMethod ( ) = m and
215+ ma .getArgument ( index ) = this .getExpr ( ) and
216+ m .getParameterType ( index ) = paramType
217+ |
218+ // LdapConnection.search method
219+ m .getDeclaringType ( ) .getAnAncestor ( ) instanceof TypeLdapConnection and
220+ m .hasName ( "search" ) and
221+ (
222+ // Parameter type is SearchRequest
223+ paramType instanceof TypeApacheSearchRequest or
224+ // Or parameter index is 1 (this is where filter parameter is)
225+ index = 1
226+ )
227+ )
228+ }
229+ }
230+
204231/** An expression node with a primitive type. */
205232class PrimitiveTypeSanitizer extends LdapInjectionSanitizer {
206233 PrimitiveTypeSanitizer ( ) { this .getType ( ) instanceof PrimitiveType }
@@ -240,7 +267,6 @@ class UnboundIdSanitizer extends LdapInjectionSanitizer {
240267 */
241268predicate filterStep ( ExprNode n1 , ExprNode n2 ) {
242269 exists ( MethodAccess ma , Method m |
243- n1 .asExpr ( ) = ma .getQualifier ( ) or
244270 n1 .asExpr ( ) = ma .getAnArgument ( )
245271 |
246272 n2 .asExpr ( ) = ma and
@@ -255,9 +281,9 @@ predicate filterStep(ExprNode n1, ExprNode n2) {
255281 * `SearchRequest`, i.e. `new SearchRequest([...], tainted, [...])`, where `tainted` is
256282 * parameter number 3, 4, 7, 8 or 9, but is not varargs.
257283 */
258- predicate searchRequestStep ( ExprNode n1 , ExprNode n2 ) {
284+ predicate unboundIdSearchRequestStep ( ExprNode n1 , ExprNode n2 ) {
259285 exists ( ConstructorCall cc , Constructor c , int index |
260- cc .getConstructedType ( ) instanceof TypeSearchRequest
286+ cc .getConstructedType ( ) instanceof TypeUnboundIdSearchRequest
261287 |
262288 n1 .asExpr ( ) = cc .getArgument ( index ) and
263289 n2 .asExpr ( ) = cc and
@@ -274,7 +300,6 @@ predicate searchRequestStep(ExprNode n1, ExprNode n2) {
274300 */
275301predicate ldapQueryStep ( ExprNode n1 , ExprNode n2 ) {
276302 exists ( MethodAccess ma , Method m , int index |
277- n1 .asExpr ( ) = ma .getQualifier ( ) or
278303 n1 .asExpr ( ) = ma .getArgument ( index )
279304 |
280305 n2 .asExpr ( ) = ma and
@@ -294,4 +319,19 @@ predicate hardcodedFilterStep(ExprNode n1, ExprNode n2) {
294319 n1 .asExpr ( ) = cc .getAnArgument ( ) and
295320 n2 .asExpr ( ) = cc
296321 )
322+ }
323+
324+ /**
325+ * Holds if `n1` to `n2` is a dataflow step that converts between `String` and Apache LDAP API
326+ * `SearchRequest`, i.e. `SearchRequest s = new SearchRequestImpl(); s.setFilter(tainted");`.
327+ */
328+ predicate apacheSearchRequestStep ( ExprNode n1 , ExprNode n2 ) {
329+ exists ( MethodAccess ma , Method m |
330+ n1 .asExpr ( ) = ma .getAnArgument ( )
331+ |
332+ n2 .asExpr ( ) = ma .getQualifier ( ) and
333+ ma .getMethod ( ) = m and
334+ m .getDeclaringType ( ) .getAnAncestor ( ) instanceof TypeApacheSearchRequest and
335+ m .hasName ( "setFilter" )
336+ )
297337}
0 commit comments