You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

304 lines
12 KiB

(*
* Copyright (c) 2016 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*)
open! IStd
module F = Format
module L = Logging
module SourceKind = struct
type t =
| Intent (** external Intent or a value read from one *)
| Other (** for testing or uncategorized sources *)
| PrivateData (** private user or device-specific data *)
| WebviewUrl (** external URL passed to a WebView *)
| Unknown
[@@deriving compare]
let unknown = Unknown
let of_string = function
| "Intent" -> Intent
| "PrivateData" -> PrivateData
| "WebviewUrl" -> WebviewUrl
| _ -> Other
let external_sources = QuandaryConfig.Source.of_json Config.quandary_sources
let get pname tenv = match pname with
| Typ.Procname.Java pname ->
begin
match Typ.Procname.java_get_class_name pname, Typ.Procname.java_get_method pname with
| "android.location.Location",
("getAltitude" | "getBearing" | "getLatitude" | "getLongitude" | "getSpeed") ->
Some PrivateData
| "android.telephony.TelephonyManager",
("getDeviceId" |
"getLine1Number" |
"getSimSerialNumber" |
"getSubscriberId" |
"getVoiceMailNumber") ->
Some PrivateData
| "com.facebook.infer.builtins.InferTaint", "inferSecretSource" ->
Some Other
| class_name, method_name ->
let taint_matching_supertype typename _ =
match Typ.Name.name typename, method_name with
| "android.app.Activity", "getIntent" ->
Some Intent
| "android.content.Intent", "getStringExtra" ->
Some Intent
| "android.content.SharedPreferences", "getString" ->
Some PrivateData
| _ ->
None in
let kind_opt =
PatternMatch.supertype_find_map_opt
tenv
taint_matching_supertype
(Typ.Name.Java.from_string class_name) in
begin
match kind_opt with
| Some _ -> kind_opt
| None ->
(* check the list of externally specified sources *)
let procedure = class_name ^ "." ^ method_name in
List.find_map
~f:(fun (source_spec : QuandaryConfig.Source.t) ->
if Str.string_match source_spec.procedure procedure 0
then Some (of_string source_spec.kind)
else None)
external_sources
end
end
| pname when BuiltinDecl.is_declared pname -> None
| pname -> failwithf "Non-Java procname %a in Java analysis@." Typ.Procname.pp pname
let get_tainted_formals pdesc tenv =
let make_untainted (name, typ) =
name, typ, None in
let taint_formals_with_types type_strs kind formals =
let taint_formal_with_types ((formal_name, formal_typ) as formal) =
let matches_classname = match formal_typ with
| Typ.Tptr (Tstruct typename, _) ->
List.mem ~equal:String.equal type_strs (Typ.Name.name typename)
| _ ->
false in
if matches_classname
then
formal_name, formal_typ, Some kind
else
make_untainted formal in
List.map ~f:taint_formal_with_types formals in
let formals = Procdesc.get_formals pdesc in
match Procdesc.get_proc_name pdesc with
| Typ.Procname.Java java_pname ->
begin
match Typ.Procname.java_get_class_name java_pname,
Typ.Procname.java_get_method java_pname with
| "codetoanalyze.java.quandary.TaintedFormals", "taintedContextBad" ->
taint_formals_with_types ["java.lang.Integer"; "java.lang.String"] Other formals
| class_name, method_name ->
let taint_matching_supertype typename _ =
match Typ.Name.name typename, method_name with
| "android.app.Activity", ("onActivityResult" | "onNewIntent") ->
Some (taint_formals_with_types ["android.content.Intent"] Intent formals)
| "android.webkit.WebViewClient",
("onLoadResource" | "shouldInterceptRequest" | "shouldOverrideUrlLoading") ->
Some
(taint_formals_with_types
["android.webkit.WebResourceRequest"; "java.lang.String"]
WebviewUrl
formals)
| "android.webkit.WebChromeClient",
("onJsAlert" | "onJsBeforeUnload" | "onJsConfirm" | "onJsPrompt") ->
Some (taint_formals_with_types ["java.lang.String"] WebviewUrl formals)
| _ ->
None in
begin
match
PatternMatch.supertype_find_map_opt
tenv
taint_matching_supertype
(Typ.Name.Java.from_string class_name) with
| Some tainted_formals -> tainted_formals
| None -> Source.all_formals_untainted pdesc
end
end
| procname ->
failwithf
"Non-Java procedure %a where only Java procedures are expected"
Typ.Procname.pp procname
let pp fmt kind =
F.fprintf fmt
(match kind with
| Intent -> "Intent"
| WebviewUrl -> "WebviewUrl"
| PrivateData -> "PrivateData"
| Other -> "Other"
| Unknown -> "Unknown")
end
module JavaSource = Source.Make(SourceKind)
module SinkKind = struct
type t =
| CreateIntent (** sink that creates an Intent *)
| JavaScript (** sink that passes its arguments to untrusted JS code *)
| Logging (** sink that logs one or more of its arguments *)
| StartComponent (** sink that launches an Activity, Service, etc. *)
| Other (** for testing or uncategorized sinks *)
[@@deriving compare]
let of_string = function
| "JavaScript" -> JavaScript
| "Logging" -> Logging
| "StartComponent" -> StartComponent
| _ -> Other
let external_sinks = QuandaryConfig.Sink.of_json Config.quandary_sinks
let get pname actuals tenv =
(* taint all the inputs of [pname]. for non-static procedures, taints the "this" parameter only
if [taint_this] is true. *)
let taint_all ?(taint_this=false) kind ~report_reachable =
let actuals_to_taint, offset =
if Typ.Procname.java_is_static pname || taint_this
then actuals, 0
else List.tl_exn actuals, 1 in
List.mapi
~f:(fun param_num _ -> kind, param_num + offset, report_reachable)
actuals_to_taint in
(* taint the nth non-"this" parameter (0-indexed) *)
let taint_nth n kind ~report_reachable =
let first_index = if Typ.Procname.java_is_static pname then n else n + 1 in
[kind, first_index, report_reachable] in
match pname with
| Typ.Procname.Java java_pname ->
begin
match Typ.Procname.java_get_class_name java_pname, Typ.Procname.java_get_method java_pname with
| "android.util.Log", ("e" | "println" | "w" | "wtf") ->
taint_all Logging ~report_reachable:true
| "com.facebook.infer.builtins.InferTaint", "inferSensitiveSink" ->
[Other, 0, false]
| class_name, method_name ->
let taint_matching_supertype typename _ =
match Typ.Name.name typename, method_name with
| "android.app.Activity",
("startActivityFromChild" | "startActivityFromFragment") ->
Some (taint_nth 1 StartComponent ~report_reachable:true)
| "android.app.Activity", "startIntentSenderForResult" ->
Some (taint_nth 2 StartComponent ~report_reachable:true)
| "android.app.Activity", "startIntentSenderFromChild" ->
Some (taint_nth 3 StartComponent ~report_reachable:true)
| "android.content.Context",
("bindService" |
"sendBroadcast" |
"sendBroadcastAsUser" |
"sendOrderedBroadcast" |
"sendOrderedBroadcastAsUser" |
"sendStickyBroadcast" |
"sendStickyBroadcastAsUser" |
"sendStickyOrderedBroadcast" |
"sendStickyOrderedBroadcastAsUser" |
"startActivities" |
"startActivity" |
"startActivityForResult" |
"startActivityIfNeeded" |
"startNextMatchingActivity" |
"startService" |
"stopService") ->
Some (taint_nth 0 StartComponent ~report_reachable:true)
| "android.content.Context", "startIntentSender" ->
Some (taint_nth 1 StartComponent ~report_reachable:true)
| "android.content.Intent",
("parseUri" |
"getIntent" |
"getIntentOld" |
"setComponent" |
"setData" |
"setDataAndNormalize" |
"setDataAndType" |
"setDataAndTypeAndNormalize" |
"setPackage") ->
Some (taint_nth 0 CreateIntent ~report_reachable:true)
| "android.content.Intent", "setClassName" ->
Some (taint_all CreateIntent ~report_reachable:true)
| "android.webkit.WebView",
("evaluateJavascript" |
"loadData" |
"loadDataWithBaseURL" |
"loadUrl" |
"postWebMessage") ->
Some (taint_all JavaScript ~report_reachable:true)
| class_name, method_name ->
(* check the list of externally specified sinks *)
let procedure = class_name ^ "." ^ method_name in
List.find_map
~f:(fun (sink_spec : QuandaryConfig.Sink.t) ->
if Str.string_match sink_spec.procedure procedure 0
then
let kind = of_string sink_spec.kind in
try
let n = int_of_string sink_spec.index in
Some (taint_nth n kind ~report_reachable:true)
with Failure _ ->
(* couldn't parse the index, just taint everything *)
Some (taint_all kind ~report_reachable:true)
else
None)
external_sinks in
begin
match
PatternMatch.supertype_find_map_opt
tenv
taint_matching_supertype
(Typ.Name.Java.from_string class_name) with
| Some sinks -> sinks
| None -> []
end
end
| pname when BuiltinDecl.is_declared pname -> []
| pname -> failwithf "Non-Java procname %a in Java analysis@." Typ.Procname.pp pname
let pp fmt kind =
F.fprintf fmt
(match kind with
| CreateIntent -> "CreateIntent"
| JavaScript -> "JavaScript"
| Logging -> "Logging"
| StartComponent -> "StartComponent"
| Other -> "Other")
end
module JavaSink = Sink.Make(SinkKind)
include
Trace.Make(struct
module Source = JavaSource
module Sink = JavaSink
let should_report source sink =
match Source.kind source, Sink.kind sink with
| PrivateData, Logging (* logging private data issue *)
| Intent, StartComponent (* intent reuse issue *)
| Intent, CreateIntent (* intent configured with external values issue *)
| Intent, JavaScript (* external data flows into JS: remote code execution risk *)
| WebviewUrl, (CreateIntent | StartComponent)
(* create intent/launch component from external URL *)
| PrivateData, JavaScript (* leaking private data into JS *)
| Other, _ | _, Other -> (* for testing purposes, Other matches everything *)
true
| _ ->
false
end)