Files
codeql/cpp/ql/lib/experimental/Quantum/OpenSSL/OpenSSLAlgorithmGetter.qll
2025-03-11 14:46:36 -04:00

534 lines
19 KiB
Plaintext

import cpp
import semmle.code.cpp.dataflow.new.DataFlow
import LibraryDetector
import OpenSSLKnownAlgorithmConstants
import experimental.Quantum.Language
class OpenSSLAlgorithmAdditionalFlowStep extends AdditionalFlowInputStep {
OpenSSLAlgorithmAdditionalFlowStep() { exists(AlgorithmPassthroughCall c | c.getInNode() = this) }
override DataFlow::Node getOutput() {
exists(AlgorithmPassthroughCall c | c.getInNode() = this and c.getOutNode() = result)
}
}
module AlgGetterToAlgConsumerConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
exists(OpenSSLAlgorithmGetterCall c | c.getResultNode() = source)
}
predicate isSink(DataFlow::Node sink) {
exists(Crypto::AlgorithmConsumer c | c.getInputNode() = sink)
}
}
module AlgGetterToAlgConsumerFlow = DataFlow::Global<AlgGetterToAlgConsumerConfig>;
abstract class AlgorithmPassthroughCall extends Call {
abstract DataFlow::Node getInNode();
abstract DataFlow::Node getOutNode();
}
class CopyAndDupAlgorithmPassthroughCall extends AlgorithmPassthroughCall {
DataFlow::Node inNode;
DataFlow::Node outNode;
CopyAndDupAlgorithmPassthroughCall() {
// Flow out through any return or other argument of the same type
// Assume flow in and out is asIndirectExpr or asDefinitingArgument since a pointer is assumed
// to be involved
// NOTE: not attempting to detect openssl specific copy/dup functions, but anything suspected to be copy/dup
this.getTarget().getName().toLowerCase().matches(["%_dup%", "%_copy%"]) and
exists(Expr inArg, Type t |
inArg = this.getAnArgument() and t = inArg.getUnspecifiedType().stripType()
|
inNode.asIndirectExpr() = inArg and
(
// Case 1: flow through another argument as an out arg of the same type
exists(Expr outArg |
outArg = this.getAnArgument() and
outArg != inArg and
outArg.getUnspecifiedType().stripType() = t
|
outNode.asDefiningArgument() = outArg
)
or
// Case 2: flow through the return value if the result is the same as the intput type
exists(Expr outArg | outArg = this and outArg.getUnspecifiedType().stripType() = t |
outNode.asIndirectExpr() = outArg
)
)
)
}
override DataFlow::Node getInNode() { result = inNode }
override DataFlow::Node getOutNode() { result = outNode }
}
class NIDToPointerPassthroughCall extends AlgorithmPassthroughCall {
DataFlow::Node inNode;
DataFlow::Node outNode;
NIDToPointerPassthroughCall() {
this.getTarget().getName() in ["OBJ_nid2obj", "OBJ_nid2ln", "OBJ_nid2sn"] and
inNode.asExpr() = this.getArgument(0) and
outNode.asExpr() = this
//outNode.asIndirectExpr() = this
}
override DataFlow::Node getInNode() { result = inNode }
override DataFlow::Node getOutNode() { result = outNode }
}
class PointerToPointerPassthroughCall extends AlgorithmPassthroughCall {
DataFlow::Node inNode;
DataFlow::Node outNode;
PointerToPointerPassthroughCall() {
this.getTarget().getName() = "OBJ_txt2obj" and
inNode.asIndirectExpr() = this.getArgument(0) and
outNode.asIndirectExpr() = this
or
//outNode.asExpr() = this
this.getTarget().getName() in ["OBJ_obj2txt", "i2t_ASN1_OBJECT"] and
inNode.asIndirectExpr() = this.getArgument(2) and
outNode.asDefiningArgument() = this.getArgument(0)
}
override DataFlow::Node getInNode() { result = inNode }
override DataFlow::Node getOutNode() { result = outNode }
}
class PointerToNIDPassthroughCall extends AlgorithmPassthroughCall {
DataFlow::Node inNode;
DataFlow::Node outNode;
PointerToNIDPassthroughCall() {
this.getTarget().getName() in ["OBJ_obj2nid", "OBJ_ln2nid", "OBJ_sn2nid", "OBJ_txt2nid"] and
(
inNode.asIndirectExpr() = this.getArgument(0)
or
inNode.asExpr() = this.getArgument(0)
) and
outNode.asExpr() = this
}
override DataFlow::Node getInNode() { result = inNode }
override DataFlow::Node getOutNode() { result = outNode }
}
predicate knownPassThroughStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(AlgorithmPassthroughCall c | c.getInNode() = node1 and c.getOutNode() = node2)
}
abstract class OpenSSLAlgorithmGetterCall extends Call {
abstract DataFlow::Node getValueArgNode();
abstract DataFlow::Node getResultNode();
abstract Expr getValueArgExpr();
abstract Expr getResultExpr();
}
module KnownOpenSSLAlgorithmToAlgorithmGetterConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
source.asExpr() instanceof KnownOpenSSLAlgorithmConstant
}
predicate isSink(DataFlow::Node sink) {
exists(OpenSSLAlgorithmGetterCall c | c.getValueArgNode() = sink)
}
predicate isBarrier(DataFlow::Node node) {
// False positive reducer, don't flow out through argv
exists(VariableAccess va, Variable v |
v.getAnAccess() = va and va = node.asExpr()
or
va = node.asIndirectExpr()
|
v.getName().matches("%argv")
)
}
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
knownPassThroughStep(node1, node2)
}
}
module KnownOpenSSLAlgorithmToAlgorithmGetterFlow =
DataFlow::Global<KnownOpenSSLAlgorithmToAlgorithmGetterConfig>;
/**
* Cases like EVP_MD5(),
* there is no input, rather it directly gets an algorithm
* and returns it.
*/
class DirectGetterCall extends OpenSSLAlgorithmGetterCall {
DataFlow::Node resultNode;
Expr resultExpr;
DirectGetterCall() {
this instanceof KnownOpenSSLAlgorithmConstant and
this instanceof Call and
resultExpr = this and
resultNode.asExpr() = resultExpr
}
override DataFlow::Node getValueArgNode() { none() }
override DataFlow::Node getResultNode() { result = resultNode }
override Expr getValueArgExpr() { none() }
override Expr getResultExpr() { result = resultExpr }
}
// https://www.openssl.org/docs/manmaster/man3/EVP_CIPHER_fetch.html
class EVPCipherGetterCall extends OpenSSLAlgorithmGetterCall {
DataFlow::Node valueArgNode;
DataFlow::Node resultNode;
Expr valueArgExpr;
Expr resultExpr;
EVPCipherGetterCall() {
resultExpr = this and
resultNode.asExpr() = this and
isPossibleOpenSSLFunction(this.getTarget()) and
(
this.getTarget().getName() in ["EVP_get_cipherbyname", "EVP_get_cipherbyobj"] and
valueArgExpr = this.getArgument(0) and
valueArgNode.asExpr() = valueArgExpr
or
this.getTarget().getName() = "EVP_CIPHER_fetch" and
valueArgExpr = this.getArgument(1) and
valueArgNode.asExpr() = valueArgExpr
or
this.getTarget().getName() = "EVP_get_cipherbynid" and
valueArgExpr = this.getArgument(0) and
valueArgNode.asExpr() = valueArgExpr
)
}
override DataFlow::Node getValueArgNode() { result = valueArgNode }
override DataFlow::Node getResultNode() { result = resultNode }
override Expr getValueArgExpr() { result = valueArgExpr }
override Expr getResultExpr() { result = resultExpr }
}
class EVPDigestGetterCall extends OpenSSLAlgorithmGetterCall {
DataFlow::Node valueArgNode;
DataFlow::Node resultNode;
Expr valueArgExpr;
Expr resultExpr;
EVPDigestGetterCall() {
resultExpr = this and
resultNode.asExpr() = this and
isPossibleOpenSSLFunction(this.getTarget()) and
(
this.getTarget().getName() in [
"EVP_get_digestbyname", "EVP_get_digestbyobj", "EVP_get_digestbynid"
] and
valueArgExpr = this.getArgument(0) and
valueArgNode.asExpr() = valueArgExpr
or
this.getTarget().getName() = "EVP_MD_fetch" and
valueArgExpr = this.getArgument(1) and
valueArgNode.asExpr() = valueArgExpr
)
}
override DataFlow::Node getValueArgNode() { result = valueArgNode }
override DataFlow::Node getResultNode() { result = resultNode }
override Expr getValueArgExpr() { result = valueArgExpr }
override Expr getResultExpr() { result = resultExpr }
}
class EVPKDFFetch extends OpenSSLAlgorithmGetterCall {
DataFlow::Node valueArgNode;
DataFlow::Node resultNode;
Expr valueArgExpr;
Expr resultExpr;
EVPKDFFetch() {
resultExpr = this and
resultNode.asExpr() = this and
isPossibleOpenSSLFunction(this.getTarget()) and
this.getTarget().getName() in ["EVP_KDF_fetch"] and
valueArgExpr = this.getArgument(1) and
valueArgNode.asExpr() = valueArgExpr
}
override DataFlow::Node getValueArgNode() { result = valueArgNode }
override DataFlow::Node getResultNode() { result = resultNode }
override Expr getValueArgExpr() { result = valueArgExpr }
override Expr getResultExpr() { result = resultExpr }
}
// /**
// * Predicates/classes for identifying algorithm sinks.
// * An Algorithm Sink is a function that takes an algorithm as an argument.
// * In particular, any function that takes in an algorithm that until the call
// * the algorithm is not definitely known to be an algorithm (e.g., an integer used as an identifier to fetch an algorithm)
// */
// //TODO: enforce a hierarchy of AlgorithmSinkArgument, e.g., so I can get all Asymmetric SinkArguments that includes all the strictly RSA etc.
// import cpp
// // import experimental.cryptography.utils.OpenSSL.LibraryFunction
// // import experimental.cryptography.CryptoAlgorithmNames
// predicate isAlgorithmSink(AlgorithmSinkArgument arg, string algType) { arg.algType() = algType }
// abstract class AlgorithmSinkArgument extends Expr {
// AlgorithmSinkArgument() {
// exists(Call c | c.getAnArgument() = this and openSSLLibraryFunc(c.getTarget()))
// }
// /**
// * Gets the function call in which the argument exists
// */
// Call getSinkCall() { result.getAnArgument() = this }
// abstract string algType();
// }
// // https://www.openssl.org/docs/manmaster/man3/EVP_CIPHER_fetch.html
// predicate cipherAlgorithmSink(string funcName, int argInd) {
// funcName in ["EVP_get_cipherbyname", "EVP_get_cipherbynid", "EVP_get_cipherbyobj"] and argInd = 0
// or
// funcName = "EVP_CIPHER_fetch" and argInd = 1
// }
// class CipherAlgorithmSink extends AlgorithmSinkArgument {
// CipherAlgorithmSink() {
// exists(Call c, string funcName, int argInd |
// funcName = c.getTarget().getName() and this = c.getArgument(argInd)
// |
// cipherAlgorithmSink(funcName, argInd)
// )
// }
// override string algType() { result = getSymmetricEncryptionType() }
// }
// // https://www.openssl.org/docs/manmaster/man3/EVP_MAC_fetch
// predicate macAlgorithmSink(string funcName, int argInd) {
// (funcName = "EVP_MAC_fetch" and argInd = 1)
// }
// class MACAlgorithmSink extends AlgorithmSinkArgument {
// MACAlgorithmSink() {
// exists(Call c, string funcName, int argInd |
// funcName = c.getTarget().getName() and this = c.getArgument(argInd)
// |
// macAlgorithmSink(funcName, argInd)
// )
// }
// override string algType() { result = "TBD" }
// }
// // https://www.openssl.org/docs/manmaster/man3/EVP_MD_fetch
// predicate messageDigestAlgorithmSink(string funcName, int argInd) {
// funcName in ["EVP_get_digestbyname", "EVP_get_digestbynid", "EVP_get_digestbyobj"] and argInd = 0
// or
// funcName = "EVP_MD_fetch" and argInd = 1
// }
// class MessageDigestAlgorithmSink extends AlgorithmSinkArgument {
// MessageDigestAlgorithmSink() {
// exists(Call c, string funcName, int argInd |
// funcName = c.getTarget().getName() and this = c.getArgument(argInd)
// |
// messageDigestAlgorithmSink(funcName, argInd)
// )
// }
// override string algType() { result = getHashType() }
// }
// // https://www.openssl.org/docs/manmaster/man3/EVP_KEYEXCH_fetch
// // https://www.openssl.org/docs/manmaster/man3/EVP_KEM_fetch
// predicate keyExchangeAlgorithmSink(string funcName, int argInd) {
// funcName = "EVP_KEYEXCH_fetch" and argInd = 1
// or
// funcName = "EVP_KEM_fetch" and argInd = 1
// }
// class KeyExchangeAlgorithmSink extends AlgorithmSinkArgument {
// KeyExchangeAlgorithmSink() {
// exists(Call c, string funcName, int argInd |
// funcName = c.getTarget().getName() and this = c.getArgument(argInd)
// |
// keyExchangeAlgorithmSink(funcName, argInd)
// )
// }
// override string algType() { result = getKeyExchangeType() }
// }
// // https://www.openssl.org/docs/manmaster/man3/EVP_KEYMGMT_fetch
// predicate keyManagementAlgorithmSink(string funcName, int argInd) {
// funcName = "EVP_KEYMGMT_fetch" and argInd = 1
// }
// class KeyManagementAlgorithmSink extends AlgorithmSinkArgument {
// KeyManagementAlgorithmSink() {
// exists(Call c, string funcName, int argInd |
// funcName = c.getTarget().getName() and this = c.getArgument(argInd)
// |
// keyManagementAlgorithmSink(funcName, argInd)
// )
// }
// override string algType() { result = "TBD" }
// }
// // https://www.openssl.org/docs/manmaster/man3/EVP_KDF
// predicate keyDerivationAlgorithmSink(string funcName, int argInd) {
// funcName = "EVP_KDF_fetch" and argInd = 1
// }
// class KeyDerivationAlgorithmSink extends AlgorithmSinkArgument {
// KeyDerivationAlgorithmSink() {
// exists(Call c, string funcName, int argInd |
// funcName = c.getTarget().getName() and this = c.getArgument(argInd)
// |
// keyDerivationAlgorithmSink(funcName, argInd)
// )
// }
// override string algType() { result = getKeyDerivationType() }
// }
// // https://www.openssl.org/docs/manmaster/man3/EVP_ASYM_CIPHER_fetch
// // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_new_id
// // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_new_CMAC_key.html
// predicate asymmetricCipherAlgorithmSink(string funcName, int argInd) {
// funcName = "EVP_ASYM_CIPHER_fetch" and argInd = 1
// or
// funcName = "EVP_PKEY_new_CMAC_key" and argInd = 3
// // NOTE: other cases are handled by AsymmetricAlgorithmSink
// }
// class AsymmetricCipherAlgorithmSink extends AlgorithmSinkArgument {
// AsymmetricCipherAlgorithmSink() {
// exists(Call c, string funcName, int argInd |
// funcName = c.getTarget().getName() and this = c.getArgument(argInd)
// |
// asymmetricCipherAlgorithmSink(funcName, argInd)
// )
// }
// override string algType() { result = "ASYMMETRIC_ENCRYPTION" }
// }
// class AsymmetricCipherAlgorithmSink_EVP_PKEY_Q_keygen extends AlgorithmSinkArgument {
// AsymmetricCipherAlgorithmSink_EVP_PKEY_Q_keygen() {
// exists(Call c, string funcName |
// funcName = c.getTarget().getName() and
// this = c.getArgument(3)
// |
// funcName = "EVP_PKEY_Q_keygen" and
// c.getArgument(3).getType().getUnderlyingType() instanceof IntegralType
// )
// }
// override string algType() { result = "ASYMMETRIC_ENCRYPTION" }
// }
// // https://www.openssl.org/docs/manmaster/man3/EVP_RAND_fetch
// predicate randomAlgorithmSink(string funcName, int argInd) {
// funcName = "EVP_RAND_fetch" and argInd = 1
// }
// class RandomAlgorithmSink extends AlgorithmSinkArgument {
// RandomAlgorithmSink() {
// exists(Call c, string funcName, int argInd |
// funcName = c.getTarget().getName() and this = c.getArgument(argInd)
// |
// randomAlgorithmSink(funcName, argInd)
// )
// }
// override string algType() { result = "TBD" }
// }
// // https://www.openssl.org/docs/manmaster/man3/EVP_SIGNATURE_fetch
// predicate signatureAlgorithmSink(string funcName, int argInd) {
// funcName = "EVP_SIGNATURE_fetch" and argInd = 1
// }
// class SignatureAlgorithmSink extends AlgorithmSinkArgument {
// SignatureAlgorithmSink() {
// exists(Call c, string funcName, int argInd |
// funcName = c.getTarget().getName() and this = c.getArgument(argInd)
// |
// signatureAlgorithmSink(funcName, argInd)
// )
// }
// override string algType() { result = getSignatureType() }
// }
// // https://www.openssl.org/docs/manmaster/man3/EC_KEY_new_by_curve_name.html
// // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_set_ec_paramgen_curve_nid.html
// predicate ellipticCurveAlgorithmSink(string funcName, int argInd) {
// funcName in ["EC_KEY_new_by_curve_name", "EVP_EC_gen"] and argInd = 0
// or
// funcName = "EC_KEY_new_by_curve_name_ex" and argInd = 2
// or
// funcName in ["EVP_PKEY_CTX_set_ec_paramgen_curve_nid"] and argInd = 1
// }
// class EllipticCurveAlgorithmSink extends AlgorithmSinkArgument {
// EllipticCurveAlgorithmSink() {
// exists(Call c, string funcName, int argInd |
// funcName = c.getTarget().getName() and this = c.getArgument(argInd)
// |
// ellipticCurveAlgorithmSink(funcName, argInd)
// )
// }
// override string algType() { result = getEllipticCurveType() }
// }
// /**
// * Special cased to address the fact that arg index 3 (zero offset based) is the curve name.
// * ASSUMPTION: if the arg ind 3 is a char* assume it is an elliptic curve
// */
// class EllipticCurveAlgorithmSink_EVP_PKEY_Q_keygen extends AlgorithmSinkArgument {
// EllipticCurveAlgorithmSink_EVP_PKEY_Q_keygen() {
// exists(Call c, string funcName |
// funcName = c.getTarget().getName() and
// this = c.getArgument(3)
// |
// funcName = "EVP_PKEY_Q_keygen" and
// c.getArgument(3).getType().getUnderlyingType() instanceof PointerType and
// c.getArgument(3).getType().getUnderlyingType().stripType() instanceof CharType
// )
// }
// override string algType() { result = getEllipticCurveType() }
// }
// // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_new_id.html
// // https://www.openssl.org/docs/man1.1.1/man3/EVP_PKEY_new_raw_private_key.html
// // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_new.html
// // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_ctrl.html
// // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_Q_keygen.html
// // https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_ctrl.html
// predicate asymmetricAlgorithmSink(string funcName, int argInd) {
// funcName = "EVP_PKEY_CTX_new_id" and argInd = 0
// or
// funcName = "EVP_PKEY_CTX_new_from_name" and argInd = 1
// or
// funcName in [
// "EVP_PKEY_new_raw_private_key", "EVP_PKEY_new_raw_public_key", "EVP_PKEY_new_mac_key"
// ] and
// argInd = 0
// or
// funcName in ["EVP_PKEY_new_raw_private_key_ex", "EVP_PKEY_new_raw_public_key_ex"] and argInd = 1
// or
// // special casing this as arg index 3 must be specified depending on if RSA or ECC, and otherwise not specified for other algs
// // funcName = "EVP_PKEY_Q_keygen" and argInd = 2
// funcName in ["EVP_PKEY_CTX_ctrl", "EVP_PKEY_CTX_set_group_name"] and argInd = 1
// // TODO consider void cases EVP_PKEY_new
// }
// class AsymmetricAlgorithmSink extends AlgorithmSinkArgument {
// AsymmetricAlgorithmSink() {
// exists(Call c, string funcName, int argInd |
// funcName = c.getTarget().getName() and this = c.getArgument(argInd)
// |
// asymmetricAlgorithmSink(funcName, argInd)
// )
// }
// override string algType() { result = getAsymmetricType() }
// }
// class AsymmetricAlgorithmSink_EVP_PKEY_Q_keygen extends AlgorithmSinkArgument {
// AsymmetricAlgorithmSink_EVP_PKEY_Q_keygen() {
// exists(Call c, string funcName |
// funcName = c.getTarget().getName() and
// this = c.getArgument(2)
// |
// funcName = "EVP_PKEY_Q_keygen" and
// not exists(c.getArgument(3))
// )
// }
// override string algType() { result = getAsymmetricType() }
// }