mirror of
https://github.com/github/codeql.git
synced 2026-01-12 14:10:21 +01:00
Merge pull request #12428 from yoff/python/rewrite-InsecureContextConfiguration
Python: Clean up insecure context query
This commit is contained in:
@@ -4,23 +4,18 @@ import TlsLibraryModel
|
||||
|
||||
/**
|
||||
* Configuration to determine the state of a context being used to create
|
||||
* a connection. There is one configuration for each pair of `TlsLibrary` and `ProtocolVersion`,
|
||||
* such that a single configuration only tracks contexts where a specific `ProtocolVersion` is allowed.
|
||||
* a connection. The configuration uses a flow state to track the `TlsLibrary`
|
||||
* and the insecure `ProtocolVersion`s that are allowed.
|
||||
*
|
||||
* The state is in terms of whether a specific protocol is allowed. This is
|
||||
* either true or false when the context is created and can then be modified
|
||||
* later by either restricting or unrestricting the protocol (see the predicates
|
||||
* `isRestriction` and `isUnrestriction`).
|
||||
* later by either restricting or unrestricting the protocol (see the predicate
|
||||
* `isAdditionalFlowStep`).
|
||||
*
|
||||
* Since we are interested in the final state, we want the flow to start from
|
||||
* the last unrestriction, so we disallow flow into unrestrictions. We also
|
||||
* model the creation as an unrestriction of everything it allows, to account
|
||||
* for the common case where the creation plays the role of "last unrestriction".
|
||||
*
|
||||
* Since we really want "the last unrestriction, not nullified by a restriction",
|
||||
* we also disallow flow into restrictions.
|
||||
* The state is represented as a bit vector, where each bit corresponds to a
|
||||
* protocol version. The bit is set if the protocol is allowed.
|
||||
*/
|
||||
module InsecureContextConfiguration2 implements DataFlow::StateConfigSig {
|
||||
module InsecureContextConfiguration implements DataFlow::StateConfigSig {
|
||||
private newtype TFlowState =
|
||||
TMkFlowState(TlsLibrary library, int bits) {
|
||||
bits in [0 .. max(any(ProtocolVersion v).getBit()) * 2 - 1]
|
||||
@@ -61,9 +56,14 @@ module InsecureContextConfiguration2 implements DataFlow::StateConfigSig {
|
||||
}
|
||||
|
||||
predicate isSource(DataFlow::Node source, FlowState state) {
|
||||
exists(ProtocolFamily family |
|
||||
source = state.getLibrary().unspecific_context_creation(family) and
|
||||
state.getBits() = family.getBits()
|
||||
exists(ContextCreation creation | source = creation |
|
||||
creation = state.getLibrary().unspecific_context_creation() and
|
||||
state.getBits() =
|
||||
sum(ProtocolVersion version |
|
||||
version = creation.getProtocol() and version.isInsecure()
|
||||
|
|
||||
version.getBit()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ module InsecureContextConfiguration2 implements DataFlow::StateConfigSig {
|
||||
}
|
||||
}
|
||||
|
||||
private module InsecureContextFlow = DataFlow::GlobalWithState<InsecureContextConfiguration2>;
|
||||
private module InsecureContextFlow = DataFlow::GlobalWithState<InsecureContextConfiguration>;
|
||||
|
||||
/**
|
||||
* Holds if `conectionCreation` marks the creation of a connection based on the contex
|
||||
|
||||
@@ -12,14 +12,14 @@ class PyOpenSslContextCreation extends ContextCreation, DataFlow::CallCfgNode {
|
||||
this = API::moduleImport("OpenSSL").getMember("SSL").getMember("Context").getACall()
|
||||
}
|
||||
|
||||
override string getProtocol() {
|
||||
override ProtocolVersion getProtocol() {
|
||||
exists(DataFlow::Node protocolArg, PyOpenSsl pyo |
|
||||
protocolArg in [this.getArg(0), this.getArgByName("method")]
|
||||
|
|
||||
protocolArg in [
|
||||
pyo.specific_version(result).getAValueReachableFromSource(),
|
||||
pyo.unspecific_version(result).getAValueReachableFromSource()
|
||||
]
|
||||
protocolArg = pyo.specific_version(result).getAValueReachableFromSource()
|
||||
or
|
||||
protocolArg = pyo.unspecific_version().getAValueReachableFromSource() and
|
||||
result = any(ProtocolVersion pv)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -51,19 +51,23 @@ class SetOptionsCall extends ProtocolRestriction, DataFlow::CallCfgNode {
|
||||
}
|
||||
}
|
||||
|
||||
class UnspecificPyOpenSslContextCreation extends PyOpenSslContextCreation, UnspecificContextCreation
|
||||
{
|
||||
// UnspecificPyOpenSslContextCreation() { library instanceof PyOpenSsl }
|
||||
}
|
||||
|
||||
class PyOpenSsl extends TlsLibrary {
|
||||
PyOpenSsl() { this = "pyOpenSSL" }
|
||||
|
||||
override string specific_version_name(ProtocolVersion version) { result = version + "_METHOD" }
|
||||
|
||||
override string unspecific_version_name(ProtocolFamily family) {
|
||||
// `"TLS_METHOD"` is not actually available in pyOpenSSL yet, but should be coming soon..
|
||||
result = family + "_METHOD"
|
||||
override string unspecific_version_name() {
|
||||
// See
|
||||
// - https://www.pyopenssl.org/en/23.0.0/api/ssl.html#module-OpenSSL.SSL
|
||||
// - https://www.openssl.org/docs/manmaster/man3/DTLS_server_method.html#NOTES
|
||||
//
|
||||
// PyOpenSSL also allows DTLS
|
||||
// see https://www.pyopenssl.org/en/stable/api/ssl.html#OpenSSL.SSL.Context
|
||||
// although they are not mentioned here:
|
||||
// https://www.pyopenssl.org/en/stable/api/ssl.html#OpenSSL.SSL.TLS_METHOD
|
||||
result = ["TLS", "SSLv23"] + "_METHOD"
|
||||
or
|
||||
result = "TLS_" + ["CLIENT", "SERVER"] + "_METHOD"
|
||||
}
|
||||
|
||||
override API::Node version_constants() { result = API::moduleImport("OpenSSL").getMember("SSL") }
|
||||
@@ -80,7 +84,5 @@ class PyOpenSsl extends TlsLibrary {
|
||||
|
||||
override ProtocolRestriction protocol_restriction() { result instanceof SetOptionsCall }
|
||||
|
||||
override ProtocolUnrestriction protocol_unrestriction() {
|
||||
result instanceof UnspecificPyOpenSslContextCreation
|
||||
}
|
||||
override ProtocolUnrestriction protocol_unrestriction() { none() }
|
||||
}
|
||||
|
||||
@@ -10,20 +10,21 @@ import TlsLibraryModel
|
||||
class SslContextCreation extends ContextCreation, DataFlow::CallCfgNode {
|
||||
SslContextCreation() { this = API::moduleImport("ssl").getMember("SSLContext").getACall() }
|
||||
|
||||
override string getProtocol() {
|
||||
override ProtocolVersion getProtocol() {
|
||||
exists(DataFlow::Node protocolArg, Ssl ssl |
|
||||
protocolArg in [this.getArg(0), this.getArgByName("protocol")]
|
||||
|
|
||||
protocolArg =
|
||||
[
|
||||
ssl.specific_version(result).getAValueReachableFromSource(),
|
||||
ssl.unspecific_version(result).getAValueReachableFromSource()
|
||||
]
|
||||
protocolArg = ssl.specific_version(result).getAValueReachableFromSource()
|
||||
or
|
||||
protocolArg = ssl.unspecific_version().getAValueReachableFromSource() and
|
||||
// see https://docs.python.org/3/library/ssl.html#id7
|
||||
result in ["TLSv1", "TLSv1_1", "TLSv1_2", "TLSv1_3"]
|
||||
)
|
||||
or
|
||||
not exists(this.getArg(_)) and
|
||||
not exists(this.getArgByName(_)) and
|
||||
result = "TLS"
|
||||
// see https://docs.python.org/3/library/ssl.html#id7
|
||||
result in ["TLSv1", "TLSv1_1", "TLSv1_2", "TLSv1_3"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +35,7 @@ class SslDefaultContextCreation extends ContextCreation {
|
||||
|
||||
// Allowed insecure versions are "TLSv1" and "TLSv1_1"
|
||||
// see https://docs.python.org/3/library/ssl.html#context-creation
|
||||
override string getProtocol() { result = "TLS" }
|
||||
override ProtocolVersion getProtocol() { result in ["TLSv1", "TLSv1_1", "TLSv1_2", "TLSv1_3"] }
|
||||
}
|
||||
|
||||
/** Gets a reference to an `ssl.Context` instance. */
|
||||
@@ -161,33 +162,29 @@ class ContextSetVersion extends ProtocolRestriction, ProtocolUnrestriction, Data
|
||||
}
|
||||
}
|
||||
|
||||
class UnspecificSslContextCreation extends SslContextCreation, UnspecificContextCreation {
|
||||
// UnspecificSslContextCreation() { library instanceof Ssl }
|
||||
// override ProtocolVersion getUnrestriction() {
|
||||
// result = UnspecificContextCreation.super.getUnrestriction() and
|
||||
// // These are turned off by default since Python 3.6
|
||||
// // see https://docs.python.org/3.6/library/ssl.html#ssl.SSLContext
|
||||
// not result in ["SSLv2", "SSLv3"]
|
||||
// }
|
||||
}
|
||||
|
||||
class UnspecificSslDefaultContextCreation extends SslDefaultContextCreation {
|
||||
// override DataFlow::Node getContext() { result = this }
|
||||
// // see https://docs.python.org/3/library/ssl.html#ssl.create_default_context
|
||||
// override ProtocolVersion getUnrestriction() {
|
||||
// result in ["TLSv1", "TLSv1_1", "TLSv1_2", "TLSv1_3"]
|
||||
// }
|
||||
}
|
||||
|
||||
// class UnspecificSslContextCreation extends SslContextCreation, UnspecificContextCreation {
|
||||
// // UnspecificSslContextCreation() { library instanceof Ssl }
|
||||
// override ProtocolVersion getProtocol() {
|
||||
// result = UnspecificContextCreation.super.getProtocol() and
|
||||
// // These are turned off by default since Python 3.6
|
||||
// // see https://docs.python.org/3.6/library/ssl.html#ssl.SSLContext
|
||||
// not result in ["SSLv2", "SSLv3"]
|
||||
// }
|
||||
// }
|
||||
// class UnspecificSslDefaultContextCreation extends SslDefaultContextCreation {
|
||||
// // override DataFlow::Node getContext() { result = this }
|
||||
// // see https://docs.python.org/3/library/ssl.html#ssl.create_default_context
|
||||
// override ProtocolVersion getProtocol() { result in ["TLSv1", "TLSv1_1", "TLSv1_2", "TLSv1_3"] }
|
||||
// }
|
||||
class Ssl extends TlsLibrary {
|
||||
Ssl() { this = "ssl" }
|
||||
|
||||
override string specific_version_name(ProtocolVersion version) { result = "PROTOCOL_" + version }
|
||||
|
||||
override string unspecific_version_name(ProtocolFamily family) {
|
||||
family = "SSLv23" and result = "PROTOCOL_" + family
|
||||
override string unspecific_version_name() {
|
||||
result = "PROTOCOL_SSLv23"
|
||||
or
|
||||
family = "TLS" and result = "PROTOCOL_" + family + ["", "_CLIENT", "_SERVER"]
|
||||
result = "PROTOCOL_TLS" + ["", "_CLIENT", "_SERVER"]
|
||||
}
|
||||
|
||||
override API::Node version_constants() { result = API::moduleImport("ssl") }
|
||||
@@ -217,9 +214,5 @@ class Ssl extends TlsLibrary {
|
||||
result instanceof OptionsAugAndNot
|
||||
or
|
||||
result instanceof ContextSetVersion
|
||||
or
|
||||
result instanceof UnspecificSslContextCreation
|
||||
or
|
||||
result instanceof UnspecificSslDefaultContextCreation
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,29 +37,23 @@ class ProtocolVersion extends string {
|
||||
or
|
||||
this = "TLSv1_3" and result = 32
|
||||
}
|
||||
|
||||
/** Gets the protocol family for this protocol version. */
|
||||
ProtocolFamily getFamily() {
|
||||
result = "SSLv23" and this in ["SSLv2", "SSLv3"]
|
||||
or
|
||||
result = "TLS" and this in ["TLSv1", "TLSv1_1", "TLSv1_2", "TLSv1_3"]
|
||||
}
|
||||
}
|
||||
|
||||
/** An unspecific protocol version */
|
||||
class ProtocolFamily extends string {
|
||||
ProtocolFamily() { this in ["SSLv23", "TLS"] }
|
||||
|
||||
/** Gets the bit mask for this protocol family. */
|
||||
int getBits() {
|
||||
result = sum(ProtocolVersion version | version.getFamily() = this | version.getBit())
|
||||
}
|
||||
}
|
||||
|
||||
/** The creation of a context. */
|
||||
abstract class ContextCreation extends DataFlow::Node {
|
||||
/** Gets the protocol version or family for this context. */
|
||||
abstract string getProtocol();
|
||||
/**
|
||||
* Gets the protocol version for this context.
|
||||
* There can be multiple values if the context was created
|
||||
* using a non-specific version such as `TLS`.
|
||||
*/
|
||||
abstract ProtocolVersion getProtocol();
|
||||
|
||||
/**
|
||||
* Holds if the context was created with a specific version
|
||||
* rather than with a version flexible method, see:
|
||||
* https://www.openssl.org/docs/manmaster/man3/DTLS_server_method.html#NOTES
|
||||
*/
|
||||
predicate specificVersion() { count(this.getProtocol()) = 1 }
|
||||
}
|
||||
|
||||
/** The creation of a connection from a context. */
|
||||
@@ -91,13 +85,12 @@ abstract class ProtocolUnrestriction extends DataFlow::Node {
|
||||
* This also serves as unrestricting these protocols.
|
||||
*/
|
||||
abstract class UnspecificContextCreation extends ContextCreation {
|
||||
// override ProtocolVersion getUnrestriction() {
|
||||
// // There is only one family, the two names are aliases in OpenSSL.
|
||||
// // see https://github.com/openssl/openssl/blob/13888e797c5a3193e91d71e5f5a196a2d68d266f/include/openssl/ssl.h.in#L1953-L1955
|
||||
// family in ["SSLv23", "TLS"] and
|
||||
// // see https://docs.python.org/3/library/ssl.html#ssl-contexts
|
||||
// result in ["SSLv2", "SSLv3", "TLSv1", "TLSv1_1", "TLSv1_2", "TLSv1_3"]
|
||||
// }
|
||||
override ProtocolVersion getProtocol() {
|
||||
// There is only one family, the two names are aliases in OpenSSL.
|
||||
// see https://github.com/openssl/openssl/blob/13888e797c5a3193e91d71e5f5a196a2d68d266f/include/openssl/ssl.h.in#L1953-L1955
|
||||
// see https://docs.python.org/3/library/ssl.html#ssl-contexts
|
||||
result in ["SSLv2", "SSLv3", "TLSv1", "TLSv1_1", "TLSv1_2", "TLSv1_3"]
|
||||
}
|
||||
}
|
||||
|
||||
/** A model of a SSL/TLS library. */
|
||||
@@ -108,8 +101,8 @@ abstract class TlsLibrary extends string {
|
||||
/** Gets the name of a specific protocol version. */
|
||||
abstract string specific_version_name(ProtocolVersion version);
|
||||
|
||||
/** Gets a name, which is a member of `version_constants`, that can be used to specify the protocol family `family`. */
|
||||
abstract string unspecific_version_name(ProtocolFamily family);
|
||||
/** Gets a name, which is a member of `version_constants`, that can be used to specify the entire protocol family. */
|
||||
abstract string unspecific_version_name();
|
||||
|
||||
/** Gets an API node representing the module or class holding the version constants. */
|
||||
abstract API::Node version_constants();
|
||||
@@ -119,9 +112,9 @@ abstract class TlsLibrary extends string {
|
||||
result = this.version_constants().getMember(this.specific_version_name(version))
|
||||
}
|
||||
|
||||
/** Gets an API node representing the protocol family `family`. */
|
||||
API::Node unspecific_version(ProtocolFamily family) {
|
||||
result = this.version_constants().getMember(this.unspecific_version_name(family))
|
||||
/** Gets an API node representing the protocol entire family. */
|
||||
API::Node unspecific_version() {
|
||||
result = this.version_constants().getMember(this.unspecific_version_name())
|
||||
}
|
||||
|
||||
/** Gets a creation of a context with a default protocol. */
|
||||
@@ -133,14 +126,15 @@ abstract class TlsLibrary extends string {
|
||||
/** Gets a creation of a context with a specific protocol version, known to be insecure. */
|
||||
ContextCreation insecure_context_creation(ProtocolVersion version) {
|
||||
result in [this.specific_context_creation(), this.default_context_creation()] and
|
||||
result.specificVersion() and
|
||||
result.getProtocol() = version and
|
||||
version.isInsecure()
|
||||
}
|
||||
|
||||
/** Gets a context that was created using `family`, known to have insecure instances. */
|
||||
ContextCreation unspecific_context_creation(ProtocolFamily family) {
|
||||
ContextCreation unspecific_context_creation() {
|
||||
result in [this.specific_context_creation(), this.default_context_creation()] and
|
||||
result.getProtocol() = family
|
||||
not result.specificVersion()
|
||||
}
|
||||
|
||||
/** Gets a dataflow node representing a connection being created in an insecure manner, not from a context. */
|
||||
|
||||
@@ -18,15 +18,18 @@
|
||||
| import_use.py:17:14:17:34 | ControlFlowNode for also_insecure_context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | import_def.py:10:25:10:56 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
|
||||
| pyOpenSSL_fluent.py:8:27:8:33 | ControlFlowNode for context | Insecure SSL/TLS protocol version SSLv2 allowed by $@. | pyOpenSSL_fluent.py:6:15:6:44 | ControlFlowNode for Attribute() | call to SSL.Context |
|
||||
| pyOpenSSL_fluent.py:8:27:8:33 | ControlFlowNode for context | Insecure SSL/TLS protocol version SSLv3 allowed by $@. | pyOpenSSL_fluent.py:6:15:6:44 | ControlFlowNode for Attribute() | call to SSL.Context |
|
||||
| pyOpenSSL_fluent.py:8:27:8:33 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1 allowed by $@. | pyOpenSSL_fluent.py:6:15:6:44 | ControlFlowNode for Attribute() | call to SSL.Context |
|
||||
| pyOpenSSL_fluent.py:8:27:8:33 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | pyOpenSSL_fluent.py:6:15:6:44 | ControlFlowNode for Attribute() | call to SSL.Context |
|
||||
| pyOpenSSL_fluent.py:18:27:18:33 | ControlFlowNode for context | Insecure SSL/TLS protocol version SSLv2 allowed by $@. | pyOpenSSL_fluent.py:15:15:15:44 | ControlFlowNode for Attribute() | call to SSL.Context |
|
||||
| pyOpenSSL_fluent.py:18:27:18:33 | ControlFlowNode for context | Insecure SSL/TLS protocol version SSLv3 allowed by $@. | pyOpenSSL_fluent.py:15:15:15:44 | ControlFlowNode for Attribute() | call to SSL.Context |
|
||||
| pyOpenSSL_fluent.py:18:27:18:33 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | pyOpenSSL_fluent.py:15:15:15:44 | ControlFlowNode for Attribute() | call to SSL.Context |
|
||||
| ssl_fluent.py:9:14:9:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1 allowed by $@. | ssl_fluent.py:6:15:6:46 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
|
||||
| ssl_fluent.py:9:14:9:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:6:15:6:46 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
|
||||
| ssl_fluent.py:19:14:19:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:15:15:15:46 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
|
||||
| ssl_fluent.py:28:14:28:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:24:15:24:53 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
|
||||
| ssl_fluent.py:37:14:37:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:33:15:33:53 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
|
||||
| ssl_fluent.py:57:14:57:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version SSLv2 allowed by $@. | ssl_fluent.py:54:15:54:49 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
|
||||
| ssl_fluent.py:57:14:57:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version SSLv3 allowed by $@. | ssl_fluent.py:54:15:54:49 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
|
||||
| ssl_fluent.py:57:14:57:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1 allowed by $@. | ssl_fluent.py:54:15:54:49 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
|
||||
| ssl_fluent.py:57:14:57:20 | ControlFlowNode for context | Insecure SSL/TLS protocol version TLSv1_1 allowed by $@. | ssl_fluent.py:54:15:54:49 | ControlFlowNode for Attribute() | call to ssl.SSLContext |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
|
||||
Reference in New Issue
Block a user