Merge pull request #5284 from yoff/python-port-insecure-protocol

Python: port py/insecure-protocol
This commit is contained in:
Rasmus Wriedt Larsen
2021-04-27 09:30:18 +02:00
committed by GitHub
12 changed files with 911 additions and 125 deletions

View File

@@ -0,0 +1,103 @@
private import python
private import semmle.python.dataflow.new.DataFlow
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.
*
* 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`).
*
* 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.
*/
class InsecureContextConfiguration extends DataFlow::Configuration {
TlsLibrary library;
ProtocolVersion tracked_version;
InsecureContextConfiguration() {
this = library + "Allows" + tracked_version and
tracked_version.isInsecure()
}
ProtocolVersion getTrackedVersion() { result = tracked_version }
override predicate isSource(DataFlow::Node source) { this.isUnrestriction(source) }
override predicate isSink(DataFlow::Node sink) {
sink = library.connection_creation().getContext()
}
override predicate isBarrierIn(DataFlow::Node node) {
this.isRestriction(node)
or
this.isUnrestriction(node)
}
private predicate isRestriction(DataFlow::Node node) {
exists(ProtocolRestriction r |
r = library.protocol_restriction() and
r.getRestriction() = tracked_version
|
node = r.getContext()
)
}
private predicate isUnrestriction(DataFlow::Node node) {
exists(ProtocolUnrestriction pu |
pu = library.protocol_unrestriction() and
pu.getUnrestriction() = tracked_version
|
node = pu.getContext()
)
}
}
/**
* Holds if `conectionCreation` marks the creation of a connetion based on the contex
* found at `contextOrigin` and allowing `insecure_version`.
*
* `specific` is true iff the context is configured for a specific protocol version (`ssl.PROTOCOL_TLSv1_2`) rather
* than for a family of protocols (`ssl.PROTOCOL_TLS`).
*/
predicate unsafe_connection_creation_with_context(
DataFlow::Node connectionCreation, ProtocolVersion insecure_version, DataFlow::Node contextOrigin,
boolean specific
) {
// Connection created from a context allowing `insecure_version`.
exists(InsecureContextConfiguration c | c.hasFlow(contextOrigin, connectionCreation) |
insecure_version = c.getTrackedVersion() and
specific = false
)
or
// Connection created from a context specifying `insecure_version`.
exists(TlsLibrary l |
connectionCreation = l.insecure_connection_creation(insecure_version) and
contextOrigin = connectionCreation and
specific = true
)
}
/**
* Holds if `conectionCreation` marks the creation of a connetion witout reference to a context
* and allowing `insecure_version`.
*/
predicate unsafe_connection_creation_without_context(
DataFlow::CallCfgNode connectionCreation, string insecure_version
) {
exists(TlsLibrary l | connectionCreation = l.insecure_connection_creation(insecure_version))
}
/** Holds if `contextCreation` is creating a context tied to a specific insecure version. */
predicate unsafe_context_creation(DataFlow::CallCfgNode contextCreation, string insecure_version) {
exists(TlsLibrary l | contextCreation = l.insecure_context_creation(insecure_version))
}

View File

@@ -13,8 +13,8 @@
<p>
Ensure that a modern, strong protocol is used. All versions of SSL,
and TLS 1.0 are known to be vulnerable to attacks. Using TLS 1.1 or
above is strongly recommended.
and TLS versions 1.0 and 1.1 are known to be vulnerable to attacks.
Using TLS 1.2 or above is strongly recommended.
</p>
</recommendation>
@@ -30,20 +30,35 @@
<p>
All cases should be updated to use a secure protocol, such as
<code>PROTOCOL_TLSv1_1</code>.
<code>PROTOCOL_TLSv1_2</code>.
</p>
<p>
Note that <code>ssl.wrap_socket</code> has been deprecated in
Python 3.7. A preferred alternative is to use
<code>ssl.SSLContext</code>, which is supported in Python 2.7.9 and
3.2 and later versions.
Python 3.7. The recommended alternatives are:
</p>
<ul>
<li><code>ssl.SSLContext</code> - supported in Python 2.7.9,
3.2, and later versions</li>
<li><code>ssl.create_default_context</code> - a convenience function,
supported in Python 3.4 and later versions.</li>
</ul>
<p>
Even when you use these alternatives, you should
ensure that a safe protocol is used. The following code illustrates
how to use flags (available since Python 3.2) or the `minimum_version`
field (favored since Python 3.7) to restrict the protocols accepted when
creating a connection.
</p>
<sample src="examples/secure_default_protocol.py" />
</example>
<references>
<li>Wikipedia: <a href="https://en.wikipedia.org/wiki/Transport_Layer_Security"> Transport Layer Security</a>.</li>
<li>Python 3 documentation: <a href="https://docs.python.org/3/library/ssl.html#ssl.SSLContext"> class ssl.SSLContext</a>.</li>
<li>Python 3 documentation: <a href="https://docs.python.org/3/library/ssl.html#ssl.wrap_socket"> ssl.wrap_socket</a>.</li>
<li>Python 3 documentation: <a href="https://docs.python.org/3/library/ssl.html#functions-constants-and-exceptions"> notes on context creation</a>.</li>
<li>Python 3 documentation: <a href="https://docs.python.org/3/library/ssl.html#ssl-security"> notes on security considerations</a>.</li>
<li>pyOpenSSL documentation: <a href="https://pyopenssl.org/en/stable/api/ssl.html"> An interface to the SSL-specific parts of OpenSSL</a>.</li>
</references>

View File

@@ -10,86 +10,77 @@
*/
import python
import semmle.python.dataflow.new.DataFlow
import FluentApiModel
private ModuleValue the_ssl_module() { result = Module::named("ssl") }
FunctionValue ssl_wrap_socket() { result = the_ssl_module().attr("wrap_socket") }
ClassValue ssl_Context_class() { result = the_ssl_module().attr("SSLContext") }
private ModuleValue the_pyOpenSSL_module() { result = Value::named("pyOpenSSL.SSL") }
ClassValue the_pyOpenSSL_Context_class() { result = Value::named("pyOpenSSL.SSL.Context") }
string insecure_version_name() {
// For `pyOpenSSL.SSL`
result = "SSLv2_METHOD" or
result = "SSLv23_METHOD" or
result = "SSLv3_METHOD" or
result = "TLSv1_METHOD" or
// For the `ssl` module
result = "PROTOCOL_SSLv2" or
result = "PROTOCOL_SSLv3" or
result = "PROTOCOL_SSLv23" or
result = "PROTOCOL_TLS" or
result = "PROTOCOL_TLSv1"
}
/*
* A syntactic check for cases where points-to analysis cannot infer the presence of
* a protocol constant, e.g. if it has been removed in later versions of the `ssl`
* library.
*/
bindingset[named_argument]
predicate probable_insecure_ssl_constant(
CallNode call, string insecure_version, string named_argument
) {
exists(ControlFlowNode arg |
arg = call.getArgByName(named_argument) or
arg = call.getArg(0)
|
arg.(AttrNode).getObject(insecure_version).pointsTo(the_ssl_module())
// Helper for pretty printer `configName`.
// This is a consequence of missing pretty priting.
// We do not want to evaluate our bespoke pretty printer
// for all `DataFlow::Node`s so we define a sub class of interesting ones.
class ProtocolConfiguration extends DataFlow::Node {
ProtocolConfiguration() {
unsafe_connection_creation_with_context(_, _, this, _)
or
arg.(NameNode).getId() = insecure_version and
exists(Import imp |
imp.getAnImportedModuleName() = "ssl" and
imp.getAName().getAsname().(Name).getId() = insecure_version
)
)
}
predicate unsafe_ssl_wrap_socket_call(
CallNode call, string method_name, string insecure_version, string named_argument
) {
(
call = ssl_wrap_socket().getACall() and
method_name = "deprecated method ssl.wrap_socket" and
named_argument = "ssl_version"
unsafe_connection_creation_without_context(this, _)
or
call = ssl_Context_class().getACall() and
named_argument = "protocol" and
method_name = "ssl.SSLContext"
) and
insecure_version = insecure_version_name() and
(
call.getArgByName(named_argument).pointsTo(the_ssl_module().attr(insecure_version))
unsafe_context_creation(this, _)
}
AstNode getNode() { result = this.asCfgNode().(CallNode).getFunction().getNode() }
}
// Helper for pretty printer `callName`.
// This is a consequence of missing pretty priting.
// We do not want to evaluate our bespoke pretty printer
// for all `AstNode`s so we define a sub class of interesting ones.
//
// Note that AstNode is abstract and AstNode_ is a library class, so
// we have to extend @py_ast_node.
class Nameable extends @py_ast_node {
Nameable() {
this = any(ProtocolConfiguration pc).getNode()
or
probable_insecure_ssl_constant(call, insecure_version, named_argument)
)
exists(Nameable attr | this = attr.(Attribute).getObject())
}
string toString() { result = "AstNode" }
}
predicate unsafe_pyOpenSSL_Context_call(CallNode call, string insecure_version) {
call = the_pyOpenSSL_Context_class().getACall() and
insecure_version = insecure_version_name() and
call.getArg(0).pointsTo(the_pyOpenSSL_module().attr(insecure_version))
}
from CallNode call, string method_name, string insecure_version
where
unsafe_ssl_wrap_socket_call(call, method_name, insecure_version, _)
string callName(Nameable call) {
result = call.(Name).getId()
or
unsafe_pyOpenSSL_Context_call(call, insecure_version) and method_name = "pyOpenSSL.SSL.Context"
select call,
"Insecure SSL/TLS protocol version " + insecure_version + " specified in call to " + method_name +
"."
exists(Attribute a | a = call | result = callName(a.getObject()) + "." + a.getName())
}
string configName(ProtocolConfiguration protocolConfiguration) {
result =
"call to " + callName(protocolConfiguration.asCfgNode().(CallNode).getFunction().getNode())
or
not protocolConfiguration.asCfgNode() instanceof CallNode and
not protocolConfiguration instanceof ContextCreation and
result = "context modification"
}
string verb(boolean specific) {
specific = true and result = "specified"
or
specific = false and result = "allowed"
}
from
DataFlow::Node connectionCreation, string insecure_version, DataFlow::Node protocolConfiguration,
boolean specific
where
unsafe_connection_creation_with_context(connectionCreation, insecure_version,
protocolConfiguration, specific)
or
unsafe_connection_creation_without_context(connectionCreation, insecure_version) and
protocolConfiguration = connectionCreation and
specific = true
or
unsafe_context_creation(protocolConfiguration, insecure_version) and
connectionCreation = protocolConfiguration and
specific = true
select connectionCreation,
"Insecure SSL/TLS protocol version " + insecure_version + " " + verb(specific) + " by $@ ",
protocolConfiguration, configName(protocolConfiguration)

View File

@@ -0,0 +1,83 @@
/**
* Provides modeling of SSL/TLS functionality of the `OpenSSL` module from the `pyOpenSSL` PyPI package.
* See https://www.pyopenssl.org/en/stable/
*/
private import python
private import semmle.python.ApiGraphs
import TlsLibraryModel
class PyOpenSSLContextCreation extends ContextCreation, DataFlow::CallCfgNode {
PyOpenSSLContextCreation() {
this = API::moduleImport("OpenSSL").getMember("SSL").getMember("Context").getACall()
}
override string getProtocol() {
exists(ControlFlowNode protocolArg, PyOpenSSL pyo |
protocolArg in [node.getArg(0), node.getArgByName("method")]
|
protocolArg =
[pyo.specific_version(result).getAUse(), pyo.unspecific_version(result).getAUse()]
.asCfgNode()
)
}
}
class ConnectionCall extends ConnectionCreation, DataFlow::CallCfgNode {
ConnectionCall() {
this = API::moduleImport("OpenSSL").getMember("SSL").getMember("Connection").getACall()
}
override DataFlow::CfgNode getContext() {
result.getNode() in [node.getArg(0), node.getArgByName("context")]
}
}
// This cannot be used to unrestrict,
// see https://www.pyopenssl.org/en/stable/api/ssl.html#OpenSSL.SSL.Context.set_options
class SetOptionsCall extends ProtocolRestriction, DataFlow::CallCfgNode {
SetOptionsCall() { node.getFunction().(AttrNode).getName() = "set_options" }
override DataFlow::CfgNode getContext() {
result.getNode() = node.getFunction().(AttrNode).getObject()
}
override ProtocolVersion getRestriction() {
API::moduleImport("OpenSSL").getMember("SSL").getMember("OP_NO_" + result).getAUse().asCfgNode() in [
node.getArg(0), node.getArgByName("options")
]
}
}
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 API::Node version_constants() { result = API::moduleImport("OpenSSL").getMember("SSL") }
override ContextCreation default_context_creation() { none() }
override ContextCreation specific_context_creation() {
result instanceof PyOpenSSLContextCreation
}
override DataFlow::Node insecure_connection_creation(ProtocolVersion version) { none() }
override ConnectionCreation connection_creation() { result instanceof ConnectionCall }
override ProtocolRestriction protocol_restriction() { result instanceof SetOptionsCall }
override ProtocolUnrestriction protocol_unrestriction() {
result instanceof UnspecificPyOpenSSLContextCreation
}
}

View File

@@ -0,0 +1,24 @@
# Current status (Feb 2021)
This should be kept up to date; the world is moving fast and protocols are being broken.
## Protocols
- All versions of SSL are insecure
- TLS 1.0 and TLS 1.1 are insecure
- TLS 1.2 have some issues. but TLS 1.3 is not widely supported
## Conection methods
- `ssl.wrap_socket` is creating insecure connections, use `SSLContext.wrap_socket` instead. [link](https://docs.python.org/3/library/ssl.html#ssl.wrap_socket)
> Deprecated since version 3.7: Since Python 3.2 and 2.7.9, it is recommended to use the `SSLContext.wrap_socket()` instead of `wrap_socket()`. The top-level function is limited and creates an insecure client socket without server name indication or hostname matching.
- Default constructors are fine, a fluent API is used to constrain possible protocols later.
## Current recomendation
TLS 1.2 or TLS 1.3
## Queries
- `InsecureProtocol` detects uses of insecure protocols.
- `InsecureDefaultProtocol` detect default constructions, this is no longer unsafe.

View File

@@ -0,0 +1,214 @@
/**
* Provides modeling of SSL/TLS functionality of the `ssl` module from the standard library.
* See https://docs.python.org/3.9/library/ssl.html
*/
private import python
private import semmle.python.ApiGraphs
import TlsLibraryModel
class SSLContextCreation extends ContextCreation, DataFlow::CallCfgNode {
SSLContextCreation() { this = API::moduleImport("ssl").getMember("SSLContext").getACall() }
override string getProtocol() {
exists(ControlFlowNode protocolArg, Ssl ssl |
protocolArg in [node.getArg(0), node.getArgByName("protocol")]
|
protocolArg =
[ssl.specific_version(result).getAUse(), ssl.unspecific_version(result).getAUse()]
.asCfgNode()
)
or
not exists(node.getAnArg()) and
result = "TLS"
}
}
class SSLDefaultContextCreation extends ContextCreation {
SSLDefaultContextCreation() {
this = API::moduleImport("ssl").getMember("create_default_context").getACall()
}
// Allowed insecure versions are "TLSv1" and "TLSv1_1"
// see https://docs.python.org/3/library/ssl.html#context-creation
override string getProtocol() { result = "TLS" }
}
/** Gets a reference to an `ssl.Context` instance. */
API::Node sslContextInstance() {
result = API::moduleImport("ssl").getMember(["SSLContext", "create_default_context"]).getReturn()
}
class WrapSocketCall extends ConnectionCreation, DataFlow::CallCfgNode {
WrapSocketCall() { this = sslContextInstance().getMember("wrap_socket").getACall() }
override DataFlow::Node getContext() {
result = this.getFunction().(DataFlow::AttrRead).getObject()
}
}
class OptionsAugOr extends ProtocolRestriction, DataFlow::CfgNode {
ProtocolVersion restriction;
OptionsAugOr() {
exists(AugAssign aa, AttrNode attr, Expr flag |
aa.getOperation().getOp() instanceof BitOr and
aa.getTarget() = attr.getNode() and
attr.getName() = "options" and
attr.getObject() = node and
flag = API::moduleImport("ssl").getMember("OP_NO_" + restriction).getAUse().asExpr() and
(
aa.getValue() = flag
or
impliesBitSet(aa.getValue(), flag, false, false)
)
)
}
override DataFlow::Node getContext() { result = this }
override ProtocolVersion getRestriction() { result = restriction }
}
class OptionsAugAndNot extends ProtocolUnrestriction, DataFlow::CfgNode {
ProtocolVersion restriction;
OptionsAugAndNot() {
exists(AugAssign aa, AttrNode attr, Expr flag, UnaryExpr notFlag |
aa.getOperation().getOp() instanceof BitAnd and
aa.getTarget() = attr.getNode() and
attr.getName() = "options" and
attr.getObject() = node and
notFlag.getOp() instanceof Invert and
notFlag.getOperand() = flag and
flag = API::moduleImport("ssl").getMember("OP_NO_" + restriction).getAUse().asExpr() and
(
aa.getValue() = notFlag
or
impliesBitSet(aa.getValue(), notFlag, true, true)
)
)
}
override DataFlow::Node getContext() { result = this }
override ProtocolVersion getUnrestriction() { result = restriction }
}
/**
* Holds if
* for every bit, _b_:
* `wholeHasBitSet` represents that _b_ is set in `whole`
* implies
* `partHasBitSet` represents that _b_ is set in `part`
*
* As an example take `whole` = `part1 & part2`. Then
* `impliesBitSet(whole, part1, true, true)` holds
* because for any bit in `whole`, if that bit is set it must also be set in `part1`.
*
* Similarly for `whole` = `part1 | part2`. Here
* `impliesBitSet(whole, part1, false, false)` holds
* because for any bit in `whole`, if that bit is not set, it cannot be set in `part1`.
*/
predicate impliesBitSet(BinaryExpr whole, Expr part, boolean partHasBitSet, boolean wholeHasBitSet) {
whole.getOp() instanceof BitAnd and
(
wholeHasBitSet = true and partHasBitSet = true and part in [whole.getLeft(), whole.getRight()]
or
wholeHasBitSet = true and
impliesBitSet([whole.getLeft(), whole.getRight()], part, partHasBitSet, wholeHasBitSet)
)
or
whole.getOp() instanceof BitOr and
(
wholeHasBitSet = false and partHasBitSet = false and part in [whole.getLeft(), whole.getRight()]
or
wholeHasBitSet = false and
impliesBitSet([whole.getLeft(), whole.getRight()], part, partHasBitSet, wholeHasBitSet)
)
}
class ContextSetVersion extends ProtocolRestriction, ProtocolUnrestriction, DataFlow::CfgNode {
ProtocolVersion restriction;
ContextSetVersion() {
exists(DataFlow::AttrWrite aw |
aw.getObject().asCfgNode() = node and
aw.getAttributeName() = "minimum_version" and
aw.getValue() =
API::moduleImport("ssl").getMember("TLSVersion").getMember(restriction).getAUse()
)
}
override DataFlow::Node getContext() { result = this }
override ProtocolVersion getRestriction() { result.lessThan(restriction) }
override ProtocolVersion getUnrestriction() {
restriction = result or restriction.lessThan(result)
}
}
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, ProtocolUnrestriction {
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 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
or
family = "TLS" and result = "PROTOCOL_" + family + ["", "_CLIENT", "_SERVER"]
}
override API::Node version_constants() { result = API::moduleImport("ssl") }
override ContextCreation default_context_creation() {
result instanceof SSLDefaultContextCreation
}
override ContextCreation specific_context_creation() { result instanceof SSLContextCreation }
override DataFlow::CallCfgNode insecure_connection_creation(ProtocolVersion version) {
result = API::moduleImport("ssl").getMember("wrap_socket").getACall() and
this.specific_version(version).getAUse() = result.getArgByName("ssl_version") and
version.isInsecure()
}
override ConnectionCreation connection_creation() { result instanceof WrapSocketCall }
override ProtocolRestriction protocol_restriction() {
result instanceof OptionsAugOr
or
result instanceof ContextSetVersion
}
override ProtocolUnrestriction protocol_unrestriction() {
result instanceof OptionsAugAndNot
or
result instanceof ContextSetVersion
or
result instanceof UnspecificSSLContextCreation
or
result instanceof UnspecificSSLDefaultContextCreation
}
}

View File

@@ -0,0 +1,137 @@
private import python
private import semmle.python.ApiGraphs
import Ssl
import PyOpenSSL
/**
* A specific protocol version of SSL or TLS.
*/
class ProtocolVersion extends string {
ProtocolVersion() { this in ["SSLv2", "SSLv3", "TLSv1", "TLSv1_1", "TLSv1_2", "TLSv1_3"] }
/** Gets a `ProtocolVersion` that is less than this `ProtocolVersion`, if any. */
predicate lessThan(ProtocolVersion version) {
this = "SSLv2" and version = "SSLv3"
or
this = "TLSv1" and version = ["TLSv1_1", "TLSv1_2", "TLSv1_3"]
or
this = ["TLSv1", "TLSv1_1"] and version = ["TLSv1_2", "TLSv1_3"]
or
this = ["TLSv1", "TLSv1_1", "TLSv1_2"] and version = "TLSv1_3"
}
/** Holds if this protocol version is known to be insecure. */
predicate isInsecure() { this in ["SSLv2", "SSLv3", "TLSv1", "TLSv1_1"] }
}
/** An unspecific protocol version */
class ProtocolFamily extends string {
ProtocolFamily() { this in ["SSLv23", "TLS"] }
}
/** The creation of a context. */
abstract class ContextCreation extends DataFlow::Node {
/** Gets the protocol version or family for this context. */
abstract string getProtocol();
}
/** The creation of a connection from a context. */
abstract class ConnectionCreation extends DataFlow::Node {
/** Gets the context used to create the connection. */
abstract DataFlow::Node getContext();
}
/** A context is being restricted on which protocols it can accepts. */
abstract class ProtocolRestriction extends DataFlow::Node {
/** Gets the context being restricted. */
abstract DataFlow::Node getContext();
/** Gets the protocol version being disallowed. */
abstract ProtocolVersion getRestriction();
}
/** A context is being relaxed on which protocols it can accepts. */
abstract class ProtocolUnrestriction extends DataFlow::Node {
/** Gets the context being relaxed. */
abstract DataFlow::Node getContext();
/** Gets the protocol version being allowed. */
abstract ProtocolVersion getUnrestriction();
}
/**
* A context is being created with a range of allowed protocols.
* This also serves as unrestricting these protocols.
*/
abstract class UnspecificContextCreation extends ContextCreation, ProtocolUnrestriction {
TlsLibrary library;
ProtocolFamily family;
UnspecificContextCreation() { this.getProtocol() = family }
override DataFlow::CfgNode getContext() { result = this }
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"]
}
}
/** A model of a SSL/TLS library. */
abstract class TlsLibrary extends string {
bindingset[this]
TlsLibrary() { any() }
/** 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 an API node representing the module or class holding the version constants. */
abstract API::Node version_constants();
/** Gets an API node representing a specific protocol version. */
API::Node specific_version(ProtocolVersion version) {
result = version_constants().getMember(specific_version_name(version))
}
/** Gets an API node representing the protocol family `family`. */
API::Node unspecific_version(ProtocolFamily family) {
result = version_constants().getMember(unspecific_version_name(family))
}
/** Gets a creation of a context with a default protocol. */
abstract ContextCreation default_context_creation();
/** Gets a creation of a context with a specific protocol. */
abstract ContextCreation specific_context_creation();
/** Gets a creation of a context with a specific protocol version, known to be insecure. */
ContextCreation insecure_context_creation(ProtocolVersion version) {
result in [specific_context_creation(), default_context_creation()] 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) {
result in [specific_context_creation(), default_context_creation()] and
result.getProtocol() = family
}
/** Gets a dataflow node representing a connection being created in an insecure manner, not from a context. */
abstract DataFlow::Node insecure_connection_creation(ProtocolVersion version);
/** Gets a dataflow node representing a connection being created from a context. */
abstract ConnectionCreation connection_creation();
/** Gets a dataflow node representing a context being restricted on which protocols it can accepts. */
abstract ProtocolRestriction protocol_restriction();
/** Gets a dataflow node representing a context being relaxed on which protocols it can accepts. */
abstract ProtocolUnrestriction protocol_unrestriction();
}