diff --git a/java/ql/src/Likely Bugs/Arithmetic/BadAbsOfRandom.ql b/java/ql/src/Likely Bugs/Arithmetic/BadAbsOfRandom.ql index 47b82798aea..ec9c4716ea6 100644 --- a/java/ql/src/Likely Bugs/Arithmetic/BadAbsOfRandom.ql +++ b/java/ql/src/Likely Bugs/Arithmetic/BadAbsOfRandom.ql @@ -11,7 +11,7 @@ */ import java -import semmle.code.java.security.Random +import semmle.code.java.security.RandomQuery from MethodAccess ma, Method abs, Method nextIntOrLong, RandomDataSource nma where diff --git a/java/ql/src/Likely Bugs/Arithmetic/RandomUsedOnce.ql b/java/ql/src/Likely Bugs/Arithmetic/RandomUsedOnce.ql index f2b78dcaf18..f502d07440f 100644 --- a/java/ql/src/Likely Bugs/Arithmetic/RandomUsedOnce.ql +++ b/java/ql/src/Likely Bugs/Arithmetic/RandomUsedOnce.ql @@ -13,7 +13,7 @@ */ import java -import semmle.code.java.security.Random +import semmle.code.java.security.RandomQuery from RandomDataSource ma where ma.getQualifier() instanceof ClassInstanceExpr diff --git a/java/ql/src/Security/CWE/CWE-129/ArraySizing.qll b/java/ql/src/Security/CWE/CWE-129/ArraySizing.qll index e0bbbf80f27..480c23ea395 100644 --- a/java/ql/src/Security/CWE/CWE-129/ArraySizing.qll +++ b/java/ql/src/Security/CWE/CWE-129/ArraySizing.qll @@ -1,7 +1,7 @@ import java import semmle.code.java.dataflow.DataFlow import semmle.code.java.dataflow.DefUse -import semmle.code.java.security.Random +import semmle.code.java.security.RandomDataSource private import BoundingChecks /** diff --git a/java/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql b/java/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql index 9cc3dfbabeb..bd0f8b3b007 100644 --- a/java/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql +++ b/java/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql @@ -14,7 +14,7 @@ import java import semmle.code.java.dataflow.TaintTracking -import semmle.code.java.security.Random +import semmle.code.java.security.RandomQuery import semmle.code.java.security.SecurityTests import ArithmeticCommon import DataFlow::PathGraph diff --git a/java/ql/src/Security/CWE/CWE-335/PredictableSeed.ql b/java/ql/src/Security/CWE/CWE-335/PredictableSeed.ql index 9b873aa407f..cf9b5bbbb50 100644 --- a/java/ql/src/Security/CWE/CWE-335/PredictableSeed.ql +++ b/java/ql/src/Security/CWE/CWE-335/PredictableSeed.ql @@ -11,7 +11,7 @@ */ import java -import semmle.code.java.security.Random +import semmle.code.java.security.RandomQuery from GetRandomData da, RValue use, PredictableSeedExpr source where diff --git a/java/ql/src/semmle/code/java/dataflow/RangeAnalysis.qll b/java/ql/src/semmle/code/java/dataflow/RangeAnalysis.qll index 47f6fe25d73..de7aca88434 100644 --- a/java/ql/src/semmle/code/java/dataflow/RangeAnalysis.qll +++ b/java/ql/src/semmle/code/java/dataflow/RangeAnalysis.qll @@ -68,7 +68,7 @@ private import SSA private import RangeUtils private import semmle.code.java.dataflow.internal.rangeanalysis.SsaReadPositionCommon private import semmle.code.java.controlflow.internal.GuardsLogic -private import semmle.code.java.security.Random +private import semmle.code.java.security.RandomDataSource private import SignAnalysis private import ModulusAnalysis private import semmle.code.java.Reflection diff --git a/java/ql/src/semmle/code/java/security/Random.qll b/java/ql/src/semmle/code/java/security/RandomDataSource.qll similarity index 52% rename from java/ql/src/semmle/code/java/security/Random.qll rename to java/ql/src/semmle/code/java/security/RandomDataSource.qll index 75ee912a895..e52c6a3b7eb 100644 --- a/java/ql/src/semmle/code/java/security/Random.qll +++ b/java/ql/src/semmle/code/java/security/RandomDataSource.qll @@ -1,13 +1,8 @@ -import java -import semmle.code.java.dataflow.DefUse -import semmle.code.java.dataflow.DataFlow6 - /** - * The `java.security.SecureRandom` class. + * Defines classes representing random data sources. */ -class SecureRandomNumberGenerator extends RefType { - SecureRandomNumberGenerator() { this.hasQualifiedName("java.security", "SecureRandom") } -} + +import java /** * A method access that returns random data or writes random data to an argument. @@ -148,149 +143,3 @@ class ApacheCommonsRandomSource extends RandomDataSource { override Expr getOutput() { result = this } } - -/** - * 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 | hasSubtype*(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 | hasSubtype*(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)) - ) -} - -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 -} - -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) - ) - } -} - -abstract class ReturnsPredictableExpr extends Method { } - -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") - } -} diff --git a/java/ql/src/semmle/code/java/security/RandomQuery.qll b/java/ql/src/semmle/code/java/security/RandomQuery.qll new file mode 100644 index 00000000000..2e844562260 --- /dev/null +++ b/java/ql/src/semmle/code/java/security/RandomQuery.qll @@ -0,0 +1,157 @@ +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 | hasSubtype*(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 | hasSubtype*(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)) + ) +} + +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 +} + +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) + ) + } +} + +abstract class ReturnsPredictableExpr extends Method { } + +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") + } +}