Files
codeql/java/ql/lib/semmle/code/java/security/RandomQuery.qll
2022-02-21 17:05:00 +00:00

174 lines
5.1 KiB
Plaintext

/** Provides classes and methods shared by randomness-related queries. */
import java
import semmle.code.java.dataflow.DefUse
import semmle.code.java.dataflow.DataFlow6
import RandomDataSource
/**
* The `java.security.SecureRandom` class.
*/
class SecureRandomNumberGenerator extends RefType {
SecureRandomNumberGenerator() { this.hasQualifiedName("java.security", "SecureRandom") }
}
/**
* A method access calling a method declared on `java.security.SecureRandom`
* that returns random data or writes random data to an argument.
*/
class GetRandomData extends StdlibRandomSource {
GetRandomData() { this.getQualifier().getType() instanceof SecureRandomNumberGenerator }
}
private predicate isSeeded(RValue use) {
isSeeding(_, use)
or
exists(GetRandomData da, RValue seeduse |
da.getQualifier() = seeduse and
useUsePair(seeduse, use)
)
}
private class PredictableSeedFlowConfiguration extends DataFlow6::Configuration {
PredictableSeedFlowConfiguration() { this = "Random::PredictableSeedFlowConfiguration" }
override predicate isSource(DataFlow6::Node source) {
source.asExpr() instanceof PredictableSeedExpr
}
override predicate isSink(DataFlow6::Node sink) { isSeeding(sink.asExpr(), _) }
override predicate isAdditionalFlowStep(DataFlow6::Node node1, DataFlow6::Node node2) {
predictableCalcStep(node1.asExpr(), node2.asExpr())
}
}
private predicate predictableCalcStep(Expr e1, Expr e2) {
e2.(BinaryExpr).hasOperands(e1, any(PredictableSeedExpr p))
or
exists(AssignOp a | a = e2 | e1 = a.getDest() and a.getRhs() instanceof PredictableSeedExpr)
or
exists(ConstructorCall cc, TypeNumber t | cc = e2 |
cc.getArgument(0) = e1 and
t.hasSubtype*(cc.getConstructedType())
)
or
exists(Method m, MethodAccess ma |
ma = e2 and
e1 = ma.getQualifier() and
m = ma.getMethod() and
exists(TypeNumber t | hasDescendant(t, m.getDeclaringType())) and
(
m.getName().matches("to%String") or
m.getName() = "toByteArray" or
m.getName().matches("%Value")
)
)
or
exists(Method m, MethodAccess ma |
ma = e2 and
e1 = ma.getArgument(0) and
m = ma.getMethod() and
exists(TypeNumber t | hasDescendant(t, m.getDeclaringType())) and
(
m.getName().matches("parse%") or
m.getName().matches("valueOf%") or
m.getName().matches("to%String")
)
)
}
private predicate safelySeeded(RValue use) {
exists(Expr arg |
isSeeding(arg, use) and
not exists(PredictableSeedFlowConfiguration conf | conf.hasFlowToExpr(arg))
)
or
exists(GetRandomData da, RValue seeduse |
da.getQualifier() = seeduse and useUsePair(seeduse, use)
|
not exists(RValue prior | useUsePair(prior, seeduse) | isSeeded(prior))
)
}
/**
* Holds if predictable seed `source` is used to initialise a random-number generator
* used at `use`.
*/
predicate unsafelySeeded(RValue use, PredictableSeedExpr source) {
isSeedingSource(_, use, source) and
not safelySeeded(use)
}
private predicate isSeeding(Expr arg, RValue use) {
exists(Expr e, VariableAssign def |
def.getSource() = e and
isSeedingConstruction(e, arg)
|
defUsePair(def, use) or
def.getDestVar().(Field).getAnAccess() = use
)
or
exists(Expr e, RValue seeduse |
e.(MethodAccess).getQualifier() = seeduse and
isRandomSeeding(e, arg) and
useUsePair(seeduse, use)
)
}
private predicate isSeedingSource(Expr arg, RValue use, Expr source) {
isSeeding(arg, use) and
exists(PredictableSeedFlowConfiguration conf |
conf.hasFlow(DataFlow6::exprNode(source), DataFlow6::exprNode(arg))
)
}
private predicate isRandomSeeding(MethodAccess m, Expr arg) {
exists(Method def | m.getMethod() = def |
def.getDeclaringType() instanceof SecureRandomNumberGenerator and
def.getName() = "setSeed" and
arg = m.getArgument(0)
)
}
private predicate isSeedingConstruction(ClassInstanceExpr c, Expr arg) {
c.getConstructedType() instanceof SecureRandomNumberGenerator and
c.getNumArgument() = 1 and
c.getArgument(0) = arg
}
/**
* A constant, call to a `ReturnsPredictableExpr` method, or an array initialiser
* consisting entirely of the same.
*/
class PredictableSeedExpr extends Expr {
PredictableSeedExpr() {
this.(MethodAccess).getCallee() instanceof ReturnsPredictableExpr
or
this instanceof CompileTimeConstantExpr
or
this.(ArrayCreationExpr).getInit() instanceof PredictableSeedExpr
or
exists(ArrayInit init | init = this |
forall(Expr e | e = init.getAnInit() | e instanceof PredictableSeedExpr)
)
}
}
/**
* A method whose return value is predictable (not necessarily constant).
*
* Extend this class in order that all randomness-related queries should consider the result
* of a particular method predictable when noting bad RNG seeding and related issues.
*/
abstract class ReturnsPredictableExpr extends Method { }
private class ReturnsSystemTime extends ReturnsPredictableExpr {
ReturnsSystemTime() {
this.getDeclaringType().hasQualifiedName("java.lang", "System") and
this.hasName("currentTimeMillis")
or
this.getDeclaringType().hasQualifiedName("java.lang", "System") and this.hasName("nanoTime")
}
}