@@ -7,6 +7,7 @@ import codeql.util.Option
77 * that flows to the input artifact of a mac operation.
88 */
99predicate isDecryptToMacFlow ( ArtifactFlow:: PathNode src , ArtifactFlow:: PathNode sink ) {
10+ // Simply flow from decrypt output to a mac input
1011 ArtifactFlow:: flowPath ( src , sink ) and
1112 exists ( Crypto:: CipherOperationNode cipherOp |
1213 cipherOp .getKeyOperationSubtype ( ) = Crypto:: TDecryptMode ( ) and
@@ -17,30 +18,163 @@ predicate isDecryptToMacFlow(ArtifactFlow::PathNode src, ArtifactFlow::PathNode
1718 )
1819}
1920
21+ /**
22+ * Experimental interface for graph generation, supply the
23+ * node to determine if a issue exists, and if so
24+ * the graph can add a property on the node.
25+ */
2026predicate isDecryptToMacNode ( Crypto:: ArtifactNode node ) {
2127 exists ( ArtifactFlow:: PathNode src |
2228 isDecryptToMacFlow ( src , _) and
2329 node .asElement ( ) = src .getNode ( ) .asExpr ( )
2430 )
2531}
2632
33+ predicate isDecryptThenMacFlow ( DecryptThenMacFlow:: PathNode src , DecryptThenMacFlow:: PathNode sink ) {
34+ DecryptThenMacFlow:: flowPath ( src , sink )
35+ }
36+
2737/**
2838 * Holds when the src node is used as plaintext input to both
2939 * an encryption operation and a mac operation, via the
3040 * argument represented by InterimArg.
3141 */
3242predicate isPlaintextInEncryptionAndMac (
3343 PlaintextUseAsMacAndCipherInputFlow:: PathNode src ,
34- PlaintextUseAsMacAndCipherInputFlow:: PathNode sink , InterimArg arg
44+ PlaintextUseAsMacAndCipherInputFlow:: PathNode sink , EncryptOrMacCallArg arg
3545) {
3646 PlaintextUseAsMacAndCipherInputFlow:: flowPath ( src , sink ) and
37- arg = sink .getState ( ) .asSome ( )
47+ arg = sink .getState ( ) .asSome ( ) and
48+ // the above pathing adds flow steps that may not have consideration for the calling context
49+ // TODO: this is something we want to look into to improving, but for now
50+ // we can filter bad flows with one additional flow check, that the source goes to both
51+ // src and sink through a generic flow
52+ // note that the flow path above ensures src gets to the interim arg, so we just need to
53+ // verify the source to sink.
54+ // TODO: having to copy the generic data flow config into a use-use variant
55+ // should we fix this at the language level to allow use use more intuitively?
56+ // Seems to be a common issue.
57+ GenericDataSourceFlowUseUseFlow:: flow ( src .getNode ( ) , sink .getNode ( ) )
3858}
3959
40- module ArgToSinkConfig implements DataFlow:: ConfigSig {
60+ /**
61+ * A copy of GenericDataSourceFlow but with use-use flows enabled by removing the barrier out
62+ */
63+ module GenericDataSourceFlowUseUseConfig implements DataFlow:: ConfigSig {
64+ predicate isSource ( DataFlow:: Node source ) {
65+ source = any ( Crypto:: GenericSourceInstance i ) .getOutputNode ( )
66+ }
67+
68+ predicate isSink ( DataFlow:: Node sink ) {
69+ sink = any ( Crypto:: FlowAwareElement other ) .getInputNode ( )
70+ }
71+
72+ predicate isBarrierIn ( DataFlow:: Node node ) {
73+ node = any ( Crypto:: FlowAwareElement element ) .getOutputNode ( )
74+ }
75+
76+ predicate isAdditionalFlowStep ( DataFlow:: Node node1 , DataFlow:: Node node2 ) {
77+ node1 .( AdditionalFlowInputStep ) .getOutput ( ) = node2
78+ or
79+ exists ( MethodCall m |
80+ m .getMethod ( ) .hasQualifiedName ( "java.lang" , "String" , "getBytes" ) and
81+ node1 .asExpr ( ) = m .getQualifier ( ) and
82+ node2 .asExpr ( ) = m
83+ )
84+ }
85+ }
86+
87+ module GenericDataSourceFlowUseUseFlow = TaintTracking:: Global< GenericDataSourceFlowUseUseConfig > ;
88+
89+ module WrapperArgFlowConfig implements DataFlow:: ConfigSig {
90+ predicate isSource ( DataFlow:: Node source ) {
91+ // Start from a parameter and not a call to avoid flow going out of
92+ // the call. We want to flow down a call, so start from a parameter
93+ // and barrier flows through returns
94+ exists ( Method m | m .getParameter ( _) = source .asParameter ( ) )
95+ }
96+
97+ predicate isSink ( DataFlow:: Node sink ) {
98+ exists ( Crypto:: CipherOperationNode cipherOp |
99+ cipherOp .getAnInputArtifact ( ) .asElement ( ) = sink .asExpr ( )
100+ )
101+ or
102+ exists ( Crypto:: MacOperationNode macOp | macOp .getAnInputArtifact ( ) .asElement ( ) = sink .asExpr ( ) )
103+ }
104+
105+ predicate isBarrierIn ( DataFlow:: Node node ) {
106+ node = any ( Crypto:: FlowAwareElement element ) .getOutputNode ( )
107+ }
108+
109+ predicate isBarrierOut ( DataFlow:: Node node ) {
110+ // stop all flow out of a call return
111+ // TODO: this might be too strict and remove taint flow, need to reassess
112+ node .asExpr ( ) instanceof Call or
113+ node = any ( Crypto:: FlowAwareElement element ) .getInputNode ( )
114+ }
115+
116+ predicate isAdditionalFlowStep ( DataFlow:: Node node1 , DataFlow:: Node node2 ) {
117+ node1 .( AdditionalFlowInputStep ) .getOutput ( ) = node2
118+ or
119+ exists ( MethodCall m |
120+ m .getMethod ( ) .hasQualifiedName ( "java.lang" , "String" , "getBytes" ) and
121+ node1 .asExpr ( ) = m .getQualifier ( ) and
122+ node2 .asExpr ( ) = m
123+ )
124+ }
125+ }
126+
127+ module WrapperArgFlow = TaintTracking:: Global< WrapperArgFlowConfig > ;
128+
129+ predicate encryptWrapperArg ( DataFlow:: Node n , DataFlow:: Node sink ) {
130+ exists ( Crypto:: CipherOperationNode cipherOp |
131+ cipherOp .getKeyOperationSubtype ( ) = Crypto:: TEncryptMode ( ) and
132+ cipherOp .getAnInputArtifact ( ) .asElement ( ) = sink .asExpr ( )
133+ ) and
134+ (
135+ exists ( Parameter p , DataFlow:: Node src |
136+ p = src .asParameter ( ) and
137+ WrapperArgFlow:: flow ( src , sink ) and
138+ n .asExpr ( ) = p .getAnArgument ( )
139+ )
140+ or
141+ n = sink // the call the target operation is considered a wrapper arg to itself
142+ )
143+ }
144+
145+ predicate decryptWrapperArg ( DataFlow:: Node n , DataFlow:: Node sink ) {
146+ exists ( Crypto:: CipherOperationNode cipherOp |
147+ cipherOp .getKeyOperationSubtype ( ) = Crypto:: TDecryptMode ( ) and
148+ cipherOp .getAnInputArtifact ( ) .asElement ( ) = sink .asExpr ( )
149+ ) and
150+ (
151+ exists ( Parameter p , DataFlow:: Node src |
152+ p = src .asParameter ( ) and
153+ WrapperArgFlow:: flow ( src , sink ) and
154+ n .asExpr ( ) = p .getAnArgument ( )
155+ )
156+ or
157+ n = sink // the call the target operation is considered a wrapper arg to itself
158+ )
159+ }
160+
161+ predicate macWrapperArg ( DataFlow:: Node n , DataFlow:: Node sink ) {
162+ exists ( Crypto:: MacOperationNode macOp | macOp .getAnInputArtifact ( ) .asElement ( ) = sink .asExpr ( ) ) and
163+ (
164+ exists ( Parameter p , DataFlow:: Node src |
165+ p = src .asParameter ( ) and
166+ WrapperArgFlow:: flow ( src , sink ) and
167+ n .asExpr ( ) = p .getAnArgument ( )
168+ )
169+ or
170+ n = sink // the call the target operation is considered a wrapper arg to itself
171+ )
172+ }
173+
174+ module ArgToEncryptOrMacConfig implements DataFlow:: ConfigSig {
41175 predicate isSource ( DataFlow:: Node source ) { exists ( Call c | c .getAnArgument ( ) = source .asExpr ( ) ) }
42176
43- predicate isSink ( DataFlow:: Node sink ) { targetSinks ( sink ) }
177+ predicate isSink ( DataFlow:: Node sink ) { encryptOrMacSink ( sink ) }
44178
45179 // Don't go in to a known out node, this will prevent the plaintext
46180 // from tracing out of cipher operations for example, we just want to trace
@@ -62,12 +196,12 @@ module ArgToSinkConfig implements DataFlow::ConfigSig {
62196 }
63197}
64198
65- module ArgToSinkFlow = TaintTracking:: Global< ArgToSinkConfig > ;
199+ module ArgToEncryptOrMacFlow = TaintTracking:: Global< ArgToEncryptOrMacConfig > ;
66200
67201/**
68202 * Target sinks for this query are either encryption operations or mac operation message inputs
69203 */
70- predicate targetSinks ( DataFlow:: Node n ) {
204+ predicate encryptOrMacSink ( DataFlow:: Node n ) {
71205 exists ( Crypto:: CipherOperationNode cipherOp |
72206 cipherOp .getKeyOperationSubtype ( ) = Crypto:: TEncryptMode ( ) and
73207 cipherOp .getAnInputArtifact ( ) .asElement ( ) = n .asExpr ( )
@@ -77,44 +211,36 @@ predicate targetSinks(DataFlow::Node n) {
77211}
78212
79213/**
80- * An argument of a target sink or a parent call whose parameter flows to a target sink
214+ * Target sinks for decryption operations
81215 */
82- class InterimArg extends DataFlow:: Node {
83- DataFlow:: Node targetSink ;
84-
85- InterimArg ( ) {
86- targetSinks ( targetSink ) and
87- (
88- this = targetSink
89- or
90- ArgToSinkFlow:: flow ( this , targetSink ) and
91- this .getEnclosingCallable ( ) .calls + ( targetSink .getEnclosingCallable ( ) )
92- )
93- }
94-
95- DataFlow:: Node getTargetSink ( ) { result = targetSink }
216+ predicate decryptSink ( DataFlow:: Node n ) {
217+ exists ( Crypto:: CipherOperationNode cipherOp |
218+ cipherOp .getKeyOperationSubtype ( ) = Crypto:: TDecryptMode ( ) and
219+ cipherOp .getAnInputArtifact ( ) .asElement ( ) = n .asExpr ( )
220+ )
96221}
97222
98- /**
99- * A wrapper class to represent a target argument dataflow node.
100- */
101- class TargetArg extends DataFlow:: Node {
102- TargetArg ( ) { targetSinks ( this ) }
223+ class EncryptOrMacCallArg extends DataFlow:: Node {
224+ boolean isEncryption ;
103225
104- predicate isCipher ( ) {
226+ EncryptOrMacCallArg ( ) {
105227 exists ( Crypto:: CipherOperationNode cipherOp |
106228 cipherOp .getKeyOperationSubtype ( ) = Crypto:: TEncryptMode ( ) and
107229 cipherOp .getAnInputArtifact ( ) .asElement ( ) = this .asExpr ( )
108- )
230+ ) and
231+ isEncryption = true
232+ or
233+ exists ( Crypto:: MacOperationNode macOp | macOp .getAnInputArtifact ( ) .asElement ( ) = this .asExpr ( ) ) and
234+ isEncryption = false
109235 }
110236
111- predicate isMac ( ) {
112- exists ( Crypto :: MacOperationNode macOp | macOp . getAnInputArtifact ( ) . asElement ( ) = this . asExpr ( ) )
113- }
237+ predicate isEncryption ( ) { isEncryption = true }
238+
239+ predicate isMac ( ) { isEncryption = false }
114240}
115241
116242module PlaintextUseAsMacAndCipherInputConfig implements DataFlow:: StateConfigSig {
117- class FlowState = Option< TargetArg > :: Option ;
243+ class FlowState = Option< EncryptOrMacCallArg > :: Option ;
118244
119245 // TODO: can we approximate a message source better?
120246 predicate isSource ( DataFlow:: Node source , FlowState state ) {
@@ -128,12 +254,9 @@ module PlaintextUseAsMacAndCipherInputConfig implements DataFlow::StateConfigSig
128254 }
129255
130256 predicate isSink ( DataFlow:: Node sink , FlowState state ) {
131- sink instanceof TargetArg and
132- (
133- sink .( TargetArg ) .isMac ( ) and state .asSome ( ) .isCipher ( )
134- or
135- sink .( TargetArg ) .isCipher ( ) and state .asSome ( ) .isMac ( )
136- )
257+ sink .( EncryptOrMacCallArg ) .isMac ( ) and state .asSome ( ) .isEncryption ( )
258+ or
259+ sink .( EncryptOrMacCallArg ) .isEncryption ( ) and state .asSome ( ) .isMac ( )
137260 }
138261
139262 predicate isBarrierOut ( DataFlow:: Node node , FlowState state ) {
@@ -163,16 +286,51 @@ module PlaintextUseAsMacAndCipherInputConfig implements DataFlow::StateConfigSig
163286 predicate isAdditionalFlowStep (
164287 DataFlow:: Node node1 , FlowState state1 , DataFlow:: Node node2 , FlowState state2
165288 ) {
166- ( exists ( state1 . asSome ( ) ) or state1 . isNone ( ) ) and
167- targetSinks ( node1 ) and
168- node1 instanceof TargetArg and
169- //use-use flow, either flow directly from the node1 use
170- //or find a parent call in the call in the call stack
171- //and continue flow from that parameter
172- node2 . ( InterimArg ) . getTargetSink ( ) = node1 and
289+ // TODO: should we consider isSome cases?
290+ state1 . isNone ( ) and
291+ (
292+ encryptWrapperArg ( node2 , node1 )
293+ or
294+ macWrapperArg ( node2 , node1 )
295+ ) and
173296 state2 .asSome ( ) = node1
174297 }
175298}
176299
177300module PlaintextUseAsMacAndCipherInputFlow =
178301 TaintTracking:: GlobalWithState< PlaintextUseAsMacAndCipherInputConfig > ;
302+
303+ module DecryptThenMacConfig implements DataFlow:: ConfigSig {
304+ predicate isSource ( DataFlow:: Node source ) {
305+ exists ( Crypto:: CipherOperationNode cipherOp |
306+ cipherOp .getKeyOperationSubtype ( ) = Crypto:: TDecryptMode ( ) and
307+ cipherOp .getAnInputArtifact ( ) .asElement ( ) = source .asExpr ( )
308+ )
309+ }
310+
311+ predicate isSink ( DataFlow:: Node sink ) {
312+ exists ( Crypto:: MacOperationNode macOp | macOp .getAnInputArtifact ( ) .asElement ( ) = sink .asExpr ( ) )
313+ }
314+
315+ // Don't go in to a known out node, prevents
316+ // from tracing out of an operation
317+ // NOTE: we are not using a barrier out on input nodes, because
318+ // that would remove 'use-use' flows, which we need
319+ predicate isBarrierIn ( DataFlow:: Node node ) {
320+ node = any ( Crypto:: FlowAwareElement element ) .getOutputNode ( )
321+ }
322+
323+ predicate isAdditionalFlowStep ( DataFlow:: Node node1 , DataFlow:: Node node2 ) {
324+ node1 .( AdditionalFlowInputStep ) .getOutput ( ) = node2
325+ or
326+ exists ( MethodCall m |
327+ m .getMethod ( ) .hasQualifiedName ( "java.lang" , "String" , "getBytes" ) and
328+ node1 .asExpr ( ) = m .getQualifier ( ) and
329+ node2 .asExpr ( ) = m
330+ )
331+ or
332+ decryptWrapperArg ( node2 , node1 )
333+ }
334+ }
335+
336+ module DecryptThenMacFlow = TaintTracking:: Global< DecryptThenMacConfig > ;
0 commit comments