Skip to content

Commit d3282a9

Browse files
authored
Merge pull request #19469 from nicolaswill/quantum-experimental
Add CodeQL Quantum models and queries (Java, C++) to experimental
2 parents 6678dc4 + 5334e90 commit d3282a9

File tree

59 files changed

+9433
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+9433
-0
lines changed

CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
# Experimental CodeQL cryptography
1919
**/experimental/quantum/ @github/ps-codeql
20+
/shared/quantum/ @github/ps-codeql
2021

2122
# CodeQL tools and associated docs
2223
/docs/codeql/codeql-cli/ @github/codeql-cli-reviewers

cpp/ql/integration-tests/query-suite/not_included_in_qls.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@ ql/cpp/ql/src/experimental/cryptography/inventory/new_models/SigningAlgorithms.q
299299
ql/cpp/ql/src/experimental/cryptography/inventory/new_models/SymmetricEncryptionAlgorithms.ql
300300
ql/cpp/ql/src/experimental/cryptography/inventory/new_models/SymmetricPaddingAlgorithms.ql
301301
ql/cpp/ql/src/experimental/cryptography/inventory/new_models/UnknownAsymmetricKeyGeneration.ql
302+
ql/cpp/ql/src/experimental/quantum/PrintCBOMGraph.ql
302303
ql/cpp/ql/src/external/examples/filters/BumpMetricBy10.ql
303304
ql/cpp/ql/src/external/examples/filters/EditDefectMessage.ql
304305
ql/cpp/ql/src/external/examples/filters/ExcludeGeneratedCode.ql
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
private import cpp as Language
2+
import semmle.code.cpp.dataflow.new.DataFlow
3+
import codeql.quantum.experimental.Model
4+
5+
module CryptoInput implements InputSig<Language::Location> {
6+
class DataFlowNode = DataFlow::Node;
7+
8+
class LocatableElement = Language::Locatable;
9+
10+
class UnknownLocation = Language::UnknownDefaultLocation;
11+
12+
LocatableElement dfn_to_element(DataFlow::Node node) {
13+
result = node.asExpr() or
14+
result = node.asParameter() or
15+
result = node.asVariable()
16+
}
17+
18+
string locationToFileBaseNameAndLineNumberString(Location location) {
19+
result = location.getFile().getBaseName() + ":" + location.getStartLine()
20+
}
21+
22+
predicate artifactOutputFlowsToGenericInput(
23+
DataFlow::Node artifactOutput, DataFlow::Node otherInput
24+
) {
25+
ArtifactFlow::flow(artifactOutput, otherInput)
26+
}
27+
}
28+
29+
module Crypto = CryptographyBase<Language::Location, CryptoInput>;
30+
31+
module ArtifactFlowConfig implements DataFlow::ConfigSig {
32+
predicate isSource(DataFlow::Node source) {
33+
source = any(Crypto::ArtifactInstance artifact).getOutputNode()
34+
}
35+
36+
predicate isSink(DataFlow::Node sink) {
37+
sink = any(Crypto::FlowAwareElement other).getInputNode()
38+
}
39+
40+
predicate isBarrierOut(DataFlow::Node node) {
41+
node = any(Crypto::FlowAwareElement element).getInputNode()
42+
}
43+
44+
predicate isBarrierIn(DataFlow::Node node) {
45+
node = any(Crypto::FlowAwareElement element).getOutputNode()
46+
}
47+
48+
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
49+
node1.(AdditionalFlowInputStep).getOutput() = node2
50+
}
51+
}
52+
53+
module ArtifactFlow = DataFlow::Global<ArtifactFlowConfig>;
54+
55+
/**
56+
* Artifact output to node input configuration
57+
*/
58+
abstract class AdditionalFlowInputStep extends DataFlow::Node {
59+
abstract DataFlow::Node getOutput();
60+
61+
final DataFlow::Node getInput() { result = this }
62+
}
63+
64+
/**
65+
* Generic data source to node input configuration
66+
*/
67+
module GenericDataSourceFlowConfig implements DataFlow::ConfigSig {
68+
predicate isSource(DataFlow::Node source) {
69+
source = any(Crypto::GenericSourceInstance i).getOutputNode()
70+
}
71+
72+
predicate isSink(DataFlow::Node sink) {
73+
sink = any(Crypto::FlowAwareElement other).getInputNode()
74+
}
75+
76+
predicate isBarrierOut(DataFlow::Node node) {
77+
node = any(Crypto::FlowAwareElement element).getInputNode()
78+
}
79+
80+
predicate isBarrierIn(DataFlow::Node node) {
81+
node = any(Crypto::FlowAwareElement element).getOutputNode()
82+
}
83+
84+
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
85+
node1.(AdditionalFlowInputStep).getOutput() = node2
86+
}
87+
}
88+
89+
module ArtifactUniversalFlowConfig implements DataFlow::ConfigSig {
90+
predicate isSource(DataFlow::Node source) {
91+
source = any(Crypto::ArtifactInstance artifact).getOutputNode()
92+
}
93+
94+
predicate isSink(DataFlow::Node sink) {
95+
sink = any(Crypto::FlowAwareElement other).getInputNode()
96+
}
97+
98+
predicate isBarrierOut(DataFlow::Node node) {
99+
node = any(Crypto::FlowAwareElement element).getInputNode()
100+
}
101+
102+
predicate isBarrierIn(DataFlow::Node node) {
103+
node = any(Crypto::FlowAwareElement element).getOutputNode()
104+
}
105+
106+
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
107+
node1.(AdditionalFlowInputStep).getOutput() = node2
108+
}
109+
}
110+
111+
module ArtifactUniversalFlow = DataFlow::Global<ArtifactUniversalFlowConfig>;
112+
113+
import OpenSSL.OpenSSL
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
import cpp
2+
import semmle.code.cpp.dataflow.new.DataFlow
3+
import experimental.quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmConstants
4+
import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers
5+
6+
/**
7+
* Traces 'known algorithms' to AVCs, specifically
8+
* algorithms that are in the set of known algorithm constants.
9+
* Padding-specific consumers exist that have their own values that
10+
* overlap with the known algorithm constants.
11+
* Padding consumers (specific padding consumers) are excluded from the set of sinks.
12+
*/
13+
module KnownOpenSSLAlgorithmToAlgorithmValueConsumerConfig implements DataFlow::ConfigSig {
14+
predicate isSource(DataFlow::Node source) {
15+
source.asExpr() instanceof KnownOpenSSLAlgorithmConstant
16+
}
17+
18+
predicate isSink(DataFlow::Node sink) {
19+
exists(OpenSSLAlgorithmValueConsumer c |
20+
c.getInputNode() = sink and
21+
not c instanceof PaddingAlgorithmValueConsumer
22+
)
23+
}
24+
25+
predicate isBarrier(DataFlow::Node node) {
26+
// False positive reducer, don't flow out through argv
27+
exists(VariableAccess va, Variable v |
28+
v.getAnAccess() = va and va = node.asExpr()
29+
or
30+
va = node.asIndirectExpr()
31+
|
32+
v.getName().matches("%argv")
33+
)
34+
}
35+
36+
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
37+
knownPassThroughStep(node1, node2)
38+
}
39+
}
40+
41+
module KnownOpenSSLAlgorithmToAlgorithmValueConsumerFlow =
42+
DataFlow::Global<KnownOpenSSLAlgorithmToAlgorithmValueConsumerConfig>;
43+
44+
module RSAPaddingAlgorithmToPaddingAlgorithmValueConsumerConfig implements DataFlow::ConfigSig {
45+
predicate isSource(DataFlow::Node source) {
46+
source.asExpr() instanceof KnownOpenSSLAlgorithmConstant
47+
}
48+
49+
predicate isSink(DataFlow::Node sink) {
50+
exists(PaddingAlgorithmValueConsumer c | c.getInputNode() = sink)
51+
}
52+
53+
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
54+
knownPassThroughStep(node1, node2)
55+
}
56+
}
57+
58+
module RSAPaddingAlgorithmToPaddingAlgorithmValueConsumerFlow =
59+
DataFlow::Global<RSAPaddingAlgorithmToPaddingAlgorithmValueConsumerConfig>;
60+
61+
class OpenSSLAlgorithmAdditionalFlowStep extends AdditionalFlowInputStep {
62+
OpenSSLAlgorithmAdditionalFlowStep() { exists(AlgorithmPassthroughCall c | c.getInNode() = this) }
63+
64+
override DataFlow::Node getOutput() {
65+
exists(AlgorithmPassthroughCall c | c.getInNode() = this and c.getOutNode() = result)
66+
}
67+
}
68+
69+
abstract class AlgorithmPassthroughCall extends Call {
70+
abstract DataFlow::Node getInNode();
71+
72+
abstract DataFlow::Node getOutNode();
73+
}
74+
75+
class CopyAndDupAlgorithmPassthroughCall extends AlgorithmPassthroughCall {
76+
DataFlow::Node inNode;
77+
DataFlow::Node outNode;
78+
79+
CopyAndDupAlgorithmPassthroughCall() {
80+
// Flow out through any return or other argument of the same type
81+
// Assume flow in and out is asIndirectExpr or asDefinitingArgument since a pointer is assumed
82+
// to be involved
83+
// NOTE: not attempting to detect openssl specific copy/dup functions, but anything suspected to be copy/dup
84+
this.getTarget().getName().toLowerCase().matches(["%_dup%", "%_copy%"]) and
85+
exists(Expr inArg, Type t |
86+
inArg = this.getAnArgument() and t = inArg.getUnspecifiedType().stripType()
87+
|
88+
inNode.asIndirectExpr() = inArg and
89+
(
90+
// Case 1: flow through another argument as an out arg of the same type
91+
exists(Expr outArg |
92+
outArg = this.getAnArgument() and
93+
outArg != inArg and
94+
outArg.getUnspecifiedType().stripType() = t
95+
|
96+
outNode.asDefiningArgument() = outArg
97+
)
98+
or
99+
// Case 2: flow through the return value if the result is the same as the intput type
100+
exists(Expr outArg | outArg = this and outArg.getUnspecifiedType().stripType() = t |
101+
outNode.asIndirectExpr() = outArg
102+
)
103+
)
104+
)
105+
}
106+
107+
override DataFlow::Node getInNode() { result = inNode }
108+
109+
override DataFlow::Node getOutNode() { result = outNode }
110+
}
111+
112+
class NIDToPointerPassthroughCall extends AlgorithmPassthroughCall {
113+
DataFlow::Node inNode;
114+
DataFlow::Node outNode;
115+
116+
NIDToPointerPassthroughCall() {
117+
this.getTarget().getName() in ["OBJ_nid2obj", "OBJ_nid2ln", "OBJ_nid2sn"] and
118+
inNode.asExpr() = this.getArgument(0) and
119+
outNode.asExpr() = this
120+
//outNode.asIndirectExpr() = this
121+
}
122+
123+
override DataFlow::Node getInNode() { result = inNode }
124+
125+
override DataFlow::Node getOutNode() { result = outNode }
126+
}
127+
128+
class PointerToPointerPassthroughCall extends AlgorithmPassthroughCall {
129+
DataFlow::Node inNode;
130+
DataFlow::Node outNode;
131+
132+
PointerToPointerPassthroughCall() {
133+
this.getTarget().getName() = "OBJ_txt2obj" and
134+
inNode.asIndirectExpr() = this.getArgument(0) and
135+
outNode.asIndirectExpr() = this
136+
or
137+
//outNode.asExpr() = this
138+
this.getTarget().getName() in ["OBJ_obj2txt", "i2t_ASN1_OBJECT"] and
139+
inNode.asIndirectExpr() = this.getArgument(2) and
140+
outNode.asDefiningArgument() = this.getArgument(0)
141+
}
142+
143+
override DataFlow::Node getInNode() { result = inNode }
144+
145+
override DataFlow::Node getOutNode() { result = outNode }
146+
}
147+
148+
class PointerToNIDPassthroughCall extends AlgorithmPassthroughCall {
149+
DataFlow::Node inNode;
150+
DataFlow::Node outNode;
151+
152+
PointerToNIDPassthroughCall() {
153+
this.getTarget().getName() in ["OBJ_obj2nid", "OBJ_ln2nid", "OBJ_sn2nid", "OBJ_txt2nid"] and
154+
(
155+
inNode.asIndirectExpr() = this.getArgument(0)
156+
or
157+
inNode.asExpr() = this.getArgument(0)
158+
) and
159+
outNode.asExpr() = this
160+
}
161+
162+
override DataFlow::Node getInNode() { result = inNode }
163+
164+
override DataFlow::Node getOutNode() { result = outNode }
165+
}
166+
167+
// TODO: pkeys pass through EVP_PKEY_CTX_new and any similar variant
168+
predicate knownPassThroughStep(DataFlow::Node node1, DataFlow::Node node2) {
169+
exists(AlgorithmPassthroughCall c | c.getInNode() = node1 and c.getOutNode() = node2)
170+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import cpp
2+
import experimental.quantum.Language
3+
import OpenSSLAlgorithmInstanceBase
4+
import experimental.quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmConstants
5+
import experimental.quantum.OpenSSL.AlgorithmValueConsumers.DirectAlgorithmValueConsumer
6+
import AlgToAVCFlow
7+
8+
/**
9+
* Given a `KnownOpenSSLBlockModeAlgorithmConstant`, converts this to a block family type.
10+
* Does not bind if there is know mapping (no mapping to 'unknown' or 'other').
11+
*/
12+
predicate knownOpenSSLConstantToBlockModeFamilyType(
13+
KnownOpenSSLBlockModeAlgorithmConstant e, Crypto::TBlockCipherModeOfOperationType type
14+
) {
15+
exists(string name |
16+
name = e.getNormalizedName() and
17+
(
18+
name.matches("CBC") and type instanceof Crypto::CBC
19+
or
20+
name.matches("CFB%") and type instanceof Crypto::CFB
21+
or
22+
name.matches("CTR") and type instanceof Crypto::CTR
23+
or
24+
name.matches("GCM") and type instanceof Crypto::GCM
25+
or
26+
name.matches("OFB") and type instanceof Crypto::OFB
27+
or
28+
name.matches("XTS") and type instanceof Crypto::XTS
29+
or
30+
name.matches("CCM") and type instanceof Crypto::CCM
31+
or
32+
name.matches("GCM") and type instanceof Crypto::GCM
33+
or
34+
name.matches("CCM") and type instanceof Crypto::CCM
35+
or
36+
name.matches("ECB") and type instanceof Crypto::ECB
37+
)
38+
)
39+
}
40+
41+
class KnownOpenSSLBlockModeConstantAlgorithmInstance extends OpenSSLAlgorithmInstance,
42+
Crypto::ModeOfOperationAlgorithmInstance instanceof KnownOpenSSLBlockModeAlgorithmConstant
43+
{
44+
OpenSSLAlgorithmValueConsumer getterCall;
45+
46+
KnownOpenSSLBlockModeConstantAlgorithmInstance() {
47+
// Two possibilities:
48+
// 1) The source is a literal and flows to a getter, then we know we have an instance
49+
// 2) The source is a KnownOpenSSLAlgorithm is call, and we know we have an instance immediately from that
50+
// Possibility 1:
51+
this instanceof Literal and
52+
exists(DataFlow::Node src, DataFlow::Node sink |
53+
// Sink is an argument to a CipherGetterCall
54+
sink = getterCall.(OpenSSLAlgorithmValueConsumer).getInputNode() and
55+
// Source is `this`
56+
src.asExpr() = this and
57+
// This traces to a getter
58+
KnownOpenSSLAlgorithmToAlgorithmValueConsumerFlow::flow(src, sink)
59+
)
60+
or
61+
// Possibility 2:
62+
this instanceof DirectAlgorithmValueConsumer and getterCall = this
63+
}
64+
65+
override Crypto::TBlockCipherModeOfOperationType getModeType() {
66+
knownOpenSSLConstantToBlockModeFamilyType(this, result)
67+
or
68+
not knownOpenSSLConstantToBlockModeFamilyType(this, _) and result = Crypto::OtherMode()
69+
}
70+
71+
// NOTE: I'm not going to attempt to parse out the mode specific part, so returning
72+
// the same as the raw name for now.
73+
override string getRawModeAlgorithmName() { result = this.(Literal).getValue().toString() }
74+
75+
override OpenSSLAlgorithmValueConsumer getAVC() { result = getterCall }
76+
}

0 commit comments

Comments
 (0)