Skip to content

Commit 072df5d

Browse files
committed
python: remove protocol family
this concept was due to my confusion between TLS and SSL23, but they are aliases. We might want to bring back the concept if we model DTLS. Also, model what exactly creations allow, bring this back from the unrestrictions they used to be. We accept the changes regarding sources being reported differently.
1 parent 8160f74 commit 072df5d

File tree

5 files changed

+85
-82
lines changed

5 files changed

+85
-82
lines changed

python/ql/src/Security/CWE-327/FluentApiModel.qll

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,14 @@ module InsecureContextConfiguration implements DataFlow::StateConfigSig {
6161
}
6262

6363
predicate isSource(DataFlow::Node source, FlowState state) {
64-
exists(ProtocolFamily family |
65-
source = state.getLibrary().unspecific_context_creation(family) and
66-
state.getBits() = family.getBits()
64+
exists(ContextCreation creation | source = creation |
65+
creation = state.getLibrary().unspecific_context_creation() and
66+
state.getBits() =
67+
sum(ProtocolVersion version |
68+
version = creation.getProtocol() and version.isInsecure()
69+
|
70+
version.getBit()
71+
)
6772
)
6873
}
6974

python/ql/src/Security/CWE-327/PyOpenSSL.qll

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,18 @@ class PyOpenSslContextCreation extends ContextCreation, DataFlow::CallCfgNode {
1212
this = API::moduleImport("OpenSSL").getMember("SSL").getMember("Context").getACall()
1313
}
1414

15-
override string getProtocol() {
15+
override ProtocolVersion getProtocol() {
1616
exists(DataFlow::Node protocolArg, PyOpenSsl pyo |
1717
protocolArg in [this.getArg(0), this.getArgByName("method")]
1818
|
19-
protocolArg in [
20-
pyo.specific_version(result).getAValueReachableFromSource(),
21-
pyo.unspecific_version(result).getAValueReachableFromSource()
22-
]
19+
protocolArg = pyo.specific_version(result).getAValueReachableFromSource()
20+
or
21+
protocolArg = pyo.unspecific_version().getAValueReachableFromSource() and
22+
// PyOpenSSL also allows DTLS
23+
// see https://www.pyopenssl.org/en/stable/api/ssl.html#OpenSSL.SSL.Context
24+
// although they are not mentioned here:
25+
// https://www.pyopenssl.org/en/stable/api/ssl.html#OpenSSL.SSL.TLS_METHOD
26+
result = any(ProtocolVersion pv)
2327
)
2428
}
2529
}
@@ -51,18 +55,21 @@ class SetOptionsCall extends ProtocolRestriction, DataFlow::CallCfgNode {
5155
}
5256
}
5357

54-
class UnspecificPyOpenSslContextCreation extends PyOpenSslContextCreation, UnspecificContextCreation {
55-
// UnspecificPyOpenSslContextCreation() { library instanceof PyOpenSsl }
56-
}
57-
58+
// class UnspecificPyOpenSslContextCreation extends PyOpenSslContextCreation, UnspecificContextCreation {
59+
// override ProtocolVersion getProtocol() { result = PyOpenSslContextCreation.super.getProtocol() }
60+
// }
5861
class PyOpenSsl extends TlsLibrary {
5962
PyOpenSsl() { this = "pyOpenSSL" }
6063

6164
override string specific_version_name(ProtocolVersion version) { result = version + "_METHOD" }
6265

63-
override string unspecific_version_name(ProtocolFamily family) {
64-
// `"TLS_METHOD"` is not actually available in pyOpenSSL yet, but should be coming soon..
65-
result = family + "_METHOD"
66+
override string unspecific_version_name() {
67+
// See
68+
// - https://www.pyopenssl.org/en/23.0.0/api/ssl.html#module-OpenSSL.SSL
69+
// - https://www.openssl.org/docs/manmaster/man3/DTLS_server_method.html#NOTES
70+
result = ["TLS", "SSLv23"] + "_METHOD"
71+
or
72+
result = "TLS_" + ["CLIENT", "SERVER"] + "_METHOD"
6673
}
6774

6875
override API::Node version_constants() { result = API::moduleImport("OpenSSL").getMember("SSL") }

python/ql/src/Security/CWE-327/Ssl.qll

Lines changed: 26 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,21 @@ import TlsLibraryModel
1010
class SslContextCreation extends ContextCreation, DataFlow::CallCfgNode {
1111
SslContextCreation() { this = API::moduleImport("ssl").getMember("SSLContext").getACall() }
1212

13-
override string getProtocol() {
13+
override ProtocolVersion getProtocol() {
1414
exists(DataFlow::Node protocolArg, Ssl ssl |
1515
protocolArg in [this.getArg(0), this.getArgByName("protocol")]
1616
|
17-
protocolArg =
18-
[
19-
ssl.specific_version(result).getAValueReachableFromSource(),
20-
ssl.unspecific_version(result).getAValueReachableFromSource()
21-
]
17+
protocolArg = ssl.specific_version(result).getAValueReachableFromSource()
18+
or
19+
protocolArg = ssl.unspecific_version().getAValueReachableFromSource() and
20+
// see https://docs.python.org/3/library/ssl.html#id7
21+
result in ["TLSv1", "TLSv1_1", "TLSv1_2", "TLSv1_3"]
2222
)
2323
or
2424
not exists(this.getArg(_)) and
2525
not exists(this.getArgByName(_)) and
26-
result = "TLS"
26+
// see https://docs.python.org/3/library/ssl.html#id7
27+
result in ["TLSv1", "TLSv1_1", "TLSv1_2", "TLSv1_3"]
2728
}
2829
}
2930

@@ -34,7 +35,7 @@ class SslDefaultContextCreation extends ContextCreation {
3435

3536
// Allowed insecure versions are "TLSv1" and "TLSv1_1"
3637
// see https://docs.python.org/3/library/ssl.html#context-creation
37-
override string getProtocol() { result = "TLS" }
38+
override ProtocolVersion getProtocol() { result in ["TLSv1", "TLSv1_1", "TLSv1_2", "TLSv1_3"] }
3839
}
3940

4041
/** Gets a reference to an `ssl.Context` instance. */
@@ -161,33 +162,29 @@ class ContextSetVersion extends ProtocolRestriction, ProtocolUnrestriction, Data
161162
}
162163
}
163164

164-
class UnspecificSslContextCreation extends SslContextCreation, UnspecificContextCreation {
165-
// UnspecificSslContextCreation() { library instanceof Ssl }
166-
// override ProtocolVersion getUnrestriction() {
167-
// result = UnspecificContextCreation.super.getUnrestriction() and
168-
// // These are turned off by default since Python 3.6
169-
// // see https://docs.python.org/3.6/library/ssl.html#ssl.SSLContext
170-
// not result in ["SSLv2", "SSLv3"]
171-
// }
172-
}
173-
174-
class UnspecificSslDefaultContextCreation extends SslDefaultContextCreation {
175-
// override DataFlow::Node getContext() { result = this }
176-
// // see https://docs.python.org/3/library/ssl.html#ssl.create_default_context
177-
// override ProtocolVersion getUnrestriction() {
178-
// result in ["TLSv1", "TLSv1_1", "TLSv1_2", "TLSv1_3"]
179-
// }
180-
}
181-
165+
// class UnspecificSslContextCreation extends SslContextCreation, UnspecificContextCreation {
166+
// // UnspecificSslContextCreation() { library instanceof Ssl }
167+
// override ProtocolVersion getProtocol() {
168+
// result = UnspecificContextCreation.super.getProtocol() and
169+
// // These are turned off by default since Python 3.6
170+
// // see https://docs.python.org/3.6/library/ssl.html#ssl.SSLContext
171+
// not result in ["SSLv2", "SSLv3"]
172+
// }
173+
// }
174+
// class UnspecificSslDefaultContextCreation extends SslDefaultContextCreation {
175+
// // override DataFlow::Node getContext() { result = this }
176+
// // see https://docs.python.org/3/library/ssl.html#ssl.create_default_context
177+
// override ProtocolVersion getProtocol() { result in ["TLSv1", "TLSv1_1", "TLSv1_2", "TLSv1_3"] }
178+
// }
182179
class Ssl extends TlsLibrary {
183180
Ssl() { this = "ssl" }
184181

185182
override string specific_version_name(ProtocolVersion version) { result = "PROTOCOL_" + version }
186183

187-
override string unspecific_version_name(ProtocolFamily family) {
188-
family = "SSLv23" and result = "PROTOCOL_" + family
184+
override string unspecific_version_name() {
185+
result = "PROTOCOL_SSLv23"
189186
or
190-
family = "TLS" and result = "PROTOCOL_" + family + ["", "_CLIENT", "_SERVER"]
187+
result = "PROTOCOL_TLS" + ["", "_CLIENT", "_SERVER"]
191188
}
192189

193190
override API::Node version_constants() { result = API::moduleImport("ssl") }

python/ql/src/Security/CWE-327/TlsLibraryModel.qll

Lines changed: 27 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -37,29 +37,23 @@ class ProtocolVersion extends string {
3737
or
3838
this = "TLSv1_3" and result = 32
3939
}
40-
41-
/** Gets the protocol family for this protocol version. */
42-
ProtocolFamily getFamily() {
43-
result = "SSLv23" and this in ["SSLv2", "SSLv3"]
44-
or
45-
result = "TLS" and this in ["TLSv1", "TLSv1_1", "TLSv1_2", "TLSv1_3"]
46-
}
47-
}
48-
49-
/** An unspecific protocol version */
50-
class ProtocolFamily extends string {
51-
ProtocolFamily() { this in ["SSLv23", "TLS"] }
52-
53-
/** Gets the bit mask for this protocol family. */
54-
int getBits() {
55-
result = sum(ProtocolVersion version | version.getFamily() = this | version.getBit())
56-
}
5740
}
5841

5942
/** The creation of a context. */
6043
abstract class ContextCreation extends DataFlow::Node {
61-
/** Gets the protocol version or family for this context. */
62-
abstract string getProtocol();
44+
/**
45+
* Gets the protocol version for this context.
46+
* There can be multiple values if the context was created
47+
* using a non-specific version such as `TLS`.
48+
*/
49+
abstract ProtocolVersion getProtocol();
50+
51+
/**
52+
* Holds if the context was created with a specific version
53+
* rather than with a version flexible method, see:
54+
* https://www.openssl.org/docs/manmaster/man3/DTLS_server_method.html#NOTES
55+
*/
56+
predicate specificVersion() { count(this.getProtocol()) = 1 }
6357
}
6458

6559
/** The creation of a connection from a context. */
@@ -91,13 +85,12 @@ abstract class ProtocolUnrestriction extends DataFlow::Node {
9185
* This also serves as unrestricting these protocols.
9286
*/
9387
abstract class UnspecificContextCreation extends ContextCreation {
94-
// override ProtocolVersion getUnrestriction() {
95-
// // There is only one family, the two names are aliases in OpenSSL.
96-
// // see https://github.com/openssl/openssl/blob/13888e797c5a3193e91d71e5f5a196a2d68d266f/include/openssl/ssl.h.in#L1953-L1955
97-
// family in ["SSLv23", "TLS"] and
98-
// // see https://docs.python.org/3/library/ssl.html#ssl-contexts
99-
// result in ["SSLv2", "SSLv3", "TLSv1", "TLSv1_1", "TLSv1_2", "TLSv1_3"]
100-
// }
88+
override ProtocolVersion getProtocol() {
89+
// There is only one family, the two names are aliases in OpenSSL.
90+
// see https://github.com/openssl/openssl/blob/13888e797c5a3193e91d71e5f5a196a2d68d266f/include/openssl/ssl.h.in#L1953-L1955
91+
// see https://docs.python.org/3/library/ssl.html#ssl-contexts
92+
result in ["SSLv2", "SSLv3", "TLSv1", "TLSv1_1", "TLSv1_2", "TLSv1_3"]
93+
}
10194
}
10295

10396
/** A model of a SSL/TLS library. */
@@ -108,8 +101,8 @@ abstract class TlsLibrary extends string {
108101
/** Gets the name of a specific protocol version. */
109102
abstract string specific_version_name(ProtocolVersion version);
110103

111-
/** Gets a name, which is a member of `version_constants`, that can be used to specify the protocol family `family`. */
112-
abstract string unspecific_version_name(ProtocolFamily family);
104+
/** Gets a name, which is a member of `version_constants`, that can be used to specify the entire protocol family. */
105+
abstract string unspecific_version_name();
113106

114107
/** Gets an API node representing the module or class holding the version constants. */
115108
abstract API::Node version_constants();
@@ -119,9 +112,9 @@ abstract class TlsLibrary extends string {
119112
result = this.version_constants().getMember(this.specific_version_name(version))
120113
}
121114

122-
/** Gets an API node representing the protocol family `family`. */
123-
API::Node unspecific_version(ProtocolFamily family) {
124-
result = this.version_constants().getMember(this.unspecific_version_name(family))
115+
/** Gets an API node representing the protocol entire family. */
116+
API::Node unspecific_version() {
117+
result = this.version_constants().getMember(this.unspecific_version_name())
125118
}
126119

127120
/** Gets a creation of a context with a default protocol. */
@@ -133,14 +126,15 @@ abstract class TlsLibrary extends string {
133126
/** Gets a creation of a context with a specific protocol version, known to be insecure. */
134127
ContextCreation insecure_context_creation(ProtocolVersion version) {
135128
result in [this.specific_context_creation(), this.default_context_creation()] and
129+
result.specificVersion() and
136130
result.getProtocol() = version and
137131
version.isInsecure()
138132
}
139133

140134
/** Gets a context that was created using `family`, known to have insecure instances. */
141-
ContextCreation unspecific_context_creation(ProtocolFamily family) {
135+
ContextCreation unspecific_context_creation() {
142136
result in [this.specific_context_creation(), this.default_context_creation()] and
143-
result.getProtocol() = family
137+
not result.specificVersion()
144138
}
145139

146140
/** Gets a dataflow node representing a connection being created in an insecure manner, not from a context. */

python/ql/test/query-tests/Security/CWE-327-InsecureProtocol/InsecureProtocol.expected

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@
2727
| ssl_fluent.py:71:14:71:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1 allowed by $@. | ssl_fluent.py:62:12:62:43 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
2828
| ssl_fluent.py:71:14:71:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1 allowed by $@. | ssl_fluent.py:101:15:101:46 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
2929
| ssl_fluent.py:71:14:71:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:62:12:62:43 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
30+
| ssl_fluent.py:71:14:71:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:65:15:65:46 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
3031
| ssl_fluent.py:71:14:71:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:101:15:101:46 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
31-
| ssl_fluent.py:71:14:71:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:117:5:117:11 | ControlFlowNode for context | context modification |
32-
| ssl_fluent.py:71:14:71:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:135:5:135:11 | ControlFlowNode for context | context modification |
32+
| ssl_fluent.py:71:14:71:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:115:15:115:46 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
3333
| ssl_fluent.py:77:14:77:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1 allowed by $@. | ssl_fluent.py:62:12:62:43 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
3434
| ssl_fluent.py:77:14:77:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:62:12:62:43 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
35-
| ssl_fluent.py:97:14:97:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:95:5:95:11 | ControlFlowNode for context | context modification |
36-
| ssl_fluent.py:146:14:146:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:143:5:143:11 | ControlFlowNode for context | context modification |
37-
| ssl_fluent.py:165:14:165:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version SSLv3 allowed by $@. | ssl_fluent.py:162:5:162:11 | ControlFlowNode for context | context modification |
35+
| ssl_fluent.py:97:14:97:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:65:15:65:46 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
36+
| ssl_fluent.py:146:14:146:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:142:15:142:46 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
37+
| ssl_fluent.py:165:14:165:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version SSLv3 allowed by $@. | ssl_fluent.py:161:15:161:65 | ControlFlowNode for Attribute() | call to ssl.create_default_context |
3838
| ssl_fluent.py:165:14:165:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1 allowed by $@. | ssl_fluent.py:161:15:161:65 | ControlFlowNode for Attribute() | call to ssl.create_default_context |
3939
| ssl_fluent.py:165:14:165:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:161:15:161:65 | ControlFlowNode for Attribute() | call to ssl.create_default_context |

0 commit comments

Comments
 (0)