13
13
14
14
import java
15
15
import semmle.code.java.dataflow.TaintTracking
16
+ import semmle.code.java.dataflow.TaintTracking2
17
+ import semmle.code.java.dataflow.FlowSources
16
18
import DataFlow:: PathGraph
17
19
20
+ private class UserInputInCryptoOperationConfig extends TaintTracking2:: Configuration {
21
+ UserInputInCryptoOperationConfig ( ) { this = "UserInputInCryptoOperationConfig" }
22
+
23
+ override predicate isSource ( DataFlow:: Node source ) { source instanceof RemoteFlowSource }
24
+
25
+ override predicate isSink ( DataFlow:: Node sink ) {
26
+ exists ( MethodAccess ma , Method m | m = ma .getMethod ( ) |
27
+ (
28
+ (
29
+ m .hasQualifiedName ( "javax.crypto" , [ "Mac" , "Cipher" ] , "doFinal" ) or
30
+ m .hasQualifiedName ( "java.security" , "Signature" , "sign" )
31
+ ) and
32
+ ma .getQualifier ( ) = sink .asExpr ( )
33
+ )
34
+ )
35
+ }
36
+
37
+ override predicate isAdditionalTaintStep ( DataFlow2:: Node fromNode , DataFlow2:: Node toNode ) {
38
+ exists ( MethodAccess ma , Method m | m = ma .getMethod ( ) |
39
+ (
40
+ (
41
+ m .hasQualifiedName ( "javax.crypto" , [ "Mac" , "Cipher" ] , [ "doFinal" , "update" ] ) or
42
+ m .hasQualifiedName ( "java.security" , "Signature" , [ "sign" , "update" ] )
43
+ ) and
44
+ ma .getAnArgument ( ) = fromNode .asExpr ( ) and
45
+ ma .getQualifier ( ) = toNode .asExpr ( )
46
+ )
47
+ )
48
+ }
49
+ }
50
+
51
+ abstract private class CryptoOperationSource extends DataFlow:: Node {
52
+ Expr cryptoOperation ;
53
+
54
+ predicate includesUserInput ( ) {
55
+ exists (
56
+ DataFlow2:: PathNode source , DataFlow2:: PathNode sink , UserInputInCryptoOperationConfig config
57
+ |
58
+ config .hasFlowPath ( source , sink )
59
+ |
60
+ sink .getNode ( ) .asExpr ( ) = cryptoOperation
61
+ )
62
+ }
63
+ }
64
+
18
65
/**
19
66
* A source that produces a MAC.
20
67
*/
21
- private class MacSource extends DataFlow :: Node {
68
+ private class MacSource extends CryptoOperationSource {
22
69
MacSource ( ) {
23
70
exists ( MethodAccess ma , Method m | m = ma .getMethod ( ) |
24
71
m .hasQualifiedName ( "javax.crypto" , "Mac" , "doFinal" ) and
25
72
(
26
73
m .getReturnType ( ) instanceof Array and ma = this .asExpr ( )
27
74
or
28
75
m .getParameterType ( 0 ) instanceof Array and ma .getArgument ( 0 ) = this .asExpr ( )
29
- )
76
+ ) and
77
+ cryptoOperation = ma .getQualifier ( )
30
78
)
31
79
}
32
80
}
33
81
34
82
/**
35
83
* A source that produces a signature.
36
84
*/
37
- private class SignatureSource extends DataFlow :: Node {
85
+ private class SignatureSource extends CryptoOperationSource {
38
86
SignatureSource ( ) {
39
87
exists ( MethodAccess ma , Method m | m = ma .getMethod ( ) |
40
88
m .hasQualifiedName ( "java.security" , "Signature" , "sign" ) and
41
89
(
42
90
m .getReturnType ( ) instanceof Array and ma = this .asExpr ( )
43
91
or
44
92
m .getParameterType ( 0 ) instanceof Array and ma .getArgument ( 0 ) = this .asExpr ( )
45
- )
93
+ ) and
94
+ cryptoOperation = ma .getQualifier ( )
46
95
)
47
96
}
48
97
}
49
98
50
99
/**
51
100
* A source that produces a ciphertext.
52
101
*/
53
- private class CiphertextSource extends DataFlow :: Node {
102
+ private class CiphertextSource extends CryptoOperationSource {
54
103
CiphertextSource ( ) {
55
104
exists ( MethodAccess ma , Method m | m = ma .getMethod ( ) |
56
105
m .hasQualifiedName ( "javax.crypto" , "Cipher" , "doFinal" ) and
@@ -61,7 +110,28 @@ private class CiphertextSource extends DataFlow::Node {
61
110
or
62
111
m .getParameterType ( 1 ) .( RefType ) .hasQualifiedName ( "java.nio" , "ByteBuffer" ) and
63
112
ma .getArgument ( 1 ) = this .asExpr ( )
64
- )
113
+ ) and
114
+ cryptoOperation = ma .getQualifier ( )
115
+ )
116
+ }
117
+ }
118
+
119
+ private class UserInputComparisonConfig extends TaintTracking2:: Configuration {
120
+ UserInputComparisonConfig ( ) { this = "UserInputComparisonConfig" }
121
+
122
+ override predicate isSource ( DataFlow:: Node source ) { source instanceof RemoteFlowSource }
123
+
124
+ override predicate isSink ( DataFlow:: Node sink ) {
125
+ exists ( MethodAccess ma , Method m | m = ma .getMethod ( ) |
126
+ (
127
+ m .hasQualifiedName ( "java.lang" , "String" , [ "equals" , "contentEquals" , "equalsIgnoreCase" ] ) or
128
+ m .hasQualifiedName ( "java.nio" , "ByteBuffer" , [ "equals" , "compareTo" ] ) or
129
+ m .hasQualifiedName ( "java.util" , "Arrays" , [ "equals" , "deepEquals" ] ) or
130
+ m .hasQualifiedName ( "java.util" , "Objects" , "deepEquals" ) or
131
+ m .hasQualifiedName ( "org.apache.commons.lang3" , "StringUtils" ,
132
+ [ "equals" , "equalsAny" , "equalsAnyIgnoreCase" , "equalsIgnoreCase" ] )
133
+ ) and
134
+ [ ma .getAnArgument ( ) , ma .getQualifier ( ) ] = sink .asExpr ( )
65
135
)
66
136
}
67
137
}
@@ -70,41 +140,46 @@ private class CiphertextSource extends DataFlow::Node {
70
140
* A sink that compares input using a non-constant time algorithm.
71
141
*/
72
142
private class KnownNonConstantTimeComparisonSink extends DataFlow:: Node {
143
+ Expr anotherParameter ;
144
+
73
145
KnownNonConstantTimeComparisonSink ( ) {
74
146
exists ( MethodAccess ma , Method m | m = ma .getMethod ( ) |
75
- m .hasQualifiedName ( "java.lang" , "String" , [ "equals" , "contentEquals" , "equalsIgnoreCase" ] ) and
76
- this .asExpr ( ) = [ ma .getQualifier ( ) , ma .getAnArgument ( ) ]
77
- or
78
- m .hasQualifiedName ( "java.nio" , "ByteBuffer" , [ "equals" , "compareTo" ] ) and
79
- this .asExpr ( ) = [ ma .getQualifier ( ) , ma .getAnArgument ( ) ]
80
- or
81
- m .hasQualifiedName ( "java.util" , "Arrays" , [ "equals" , "deepEquals" ] ) and
82
- ma .getAnArgument ( ) = this .asExpr ( )
83
- or
84
- m .hasQualifiedName ( "java.util" , "Objects" , "deepEquals" ) and
85
- ma .getAnArgument ( ) = this .asExpr ( )
86
- or
87
- m .hasQualifiedName ( "org.apache.commons.lang3" , "StringUtils" ,
88
- [ "equals" , "equalsAny" , "equalsAnyIgnoreCase" , "equalsIgnoreCase" ] ) and
89
- ma .getAnArgument ( ) = this .asExpr ( )
147
+ (
148
+ m .hasQualifiedName ( "java.lang" , "String" , [ "equals" , "contentEquals" , "equalsIgnoreCase" ] ) or
149
+ m .hasQualifiedName ( "java.nio" , "ByteBuffer" , [ "equals" , "compareTo" ] )
150
+ ) and
151
+ (
152
+ this .asExpr ( ) = ma .getQualifier ( ) and
153
+ anotherParameter = ma .getAnArgument ( ) and
154
+ not ma .getAnArgument ( ) .isCompileTimeConstant ( )
155
+ or
156
+ this .asExpr ( ) = ma .getAnArgument ( ) and
157
+ anotherParameter = ma .getQualifier ( ) and
158
+ not ma .getQualifier ( ) .isCompileTimeConstant ( )
159
+ )
160
+ )
161
+ or
162
+ exists ( StaticMethodAccess ma , Method m | m = ma .getMethod ( ) |
163
+ (
164
+ m .hasQualifiedName ( "java.util" , "Arrays" , [ "equals" , "deepEquals" ] ) or
165
+ m .hasQualifiedName ( "java.util" , "Objects" , "deepEquals" ) or
166
+ m .hasQualifiedName ( "org.apache.commons.lang3" , "StringUtils" ,
167
+ [ "equals" , "equalsAny" , "equalsAnyIgnoreCase" , "equalsIgnoreCase" ] )
168
+ ) and
169
+ ma .getAnArgument ( ) = this .asExpr ( ) and
170
+ (
171
+ this .asExpr ( ) = ma .getArgument ( 0 ) and anotherParameter = ma .getArgument ( 1 )
172
+ or
173
+ this .asExpr ( ) = ma .getArgument ( 1 ) and anotherParameter = ma .getArgument ( 0 )
174
+ )
90
175
)
91
176
}
92
- }
93
177
94
- /**
95
- * Holds if `fromNode` to `toNode` is a dataflow step
96
- * that converts a `ByteBuffer` to a byte array and vice versa.
97
- */
98
- private predicate convertByteBufferStep ( DataFlow:: Node fromNode , DataFlow:: Node toNode ) {
99
- exists ( MethodAccess ma , Method m | m = ma .getMethod ( ) |
100
- m .hasQualifiedName ( "java.nio" , "ByteBuffer" , "array" ) and
101
- ma .getQualifier ( ) = fromNode .asExpr ( ) and
102
- ma = toNode .asExpr ( )
103
- or
104
- m .hasQualifiedName ( "java.nio" , "ByteBuffer" , "wrap" ) and
105
- ma .getAnArgument ( ) = fromNode .asExpr ( ) and
106
- ma = toNode .asExpr ( )
107
- )
178
+ predicate includesUserInput ( ) {
179
+ exists ( UserInputComparisonConfig config |
180
+ config .hasFlowTo ( DataFlow2:: exprNode ( anotherParameter ) )
181
+ )
182
+ }
108
183
}
109
184
110
185
/**
@@ -114,24 +189,19 @@ private predicate convertByteBufferStep(DataFlow::Node fromNode, DataFlow::Node
114
189
private class NonConstantTimeCryptoComparisonConfig extends TaintTracking:: Configuration {
115
190
NonConstantTimeCryptoComparisonConfig ( ) { this = "NonConstantTimeCryptoComparisonConfig" }
116
191
117
- override predicate isSource ( DataFlow:: Node source ) {
118
- source instanceof MacSource
119
- or
120
- source instanceof SignatureSource
121
- or
122
- source instanceof CiphertextSource
123
- }
192
+ override predicate isSource ( DataFlow:: Node source ) { source instanceof CryptoOperationSource }
124
193
125
194
override predicate isSink ( DataFlow:: Node sink ) {
126
195
sink instanceof KnownNonConstantTimeComparisonSink
127
196
}
128
-
129
- override predicate isAdditionalTaintStep ( DataFlow:: Node fromNode , DataFlow:: Node toNode ) {
130
- convertByteBufferStep ( fromNode , toNode )
131
- }
132
197
}
133
198
134
199
from DataFlow:: PathNode source , DataFlow:: PathNode sink , NonConstantTimeCryptoComparisonConfig conf
135
- where conf .hasFlowPath ( source , sink )
200
+ where
201
+ conf .hasFlowPath ( source , sink ) and
202
+ (
203
+ source .getNode ( ) .( CryptoOperationSource ) .includesUserInput ( ) or
204
+ sink .getNode ( ) .( KnownNonConstantTimeComparisonSink ) .includesUserInput ( )
205
+ )
136
206
select sink .getNode ( ) , source , sink ,
137
207
"Using a non-constant time algorithm for comparing results of a cryptographic operation."
0 commit comments