sandbox_policy_diagnostic.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. // Copyright 2019 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "sandbox/win/src/sandbox_policy_diagnostic.h"
  5. #include <Windows.h>
  6. #include <stddef.h>
  7. #include <cinttypes>
  8. #include <memory>
  9. #include <string>
  10. #include <utility>
  11. #include <vector>
  12. #include "base/check.h"
  13. #include "base/json/json_string_value_serializer.h"
  14. #include "base/numerics/safe_conversions.h"
  15. #include "base/strings/string_util.h"
  16. #include "base/strings/stringprintf.h"
  17. #include "base/strings/utf_string_conversions.h"
  18. #include "base/values.h"
  19. #include "sandbox/win/src/ipc_tags.h"
  20. #include "sandbox/win/src/policy_engine_opcodes.h"
  21. #include "sandbox/win/src/sandbox_policy_base.h"
  22. #include "sandbox/win/src/target_process.h"
  23. #include "sandbox/win/src/win_utils.h"
  24. namespace sandbox {
  25. namespace {
  26. // Keys in base::Value snapshots of Policies for chrome://sandbox.
  27. const char kAppContainerCapabilities[] = "appContainerCapabilities";
  28. const char kAppContainerInitialCapabilities[] =
  29. "appContainerInitialCapabilities";
  30. const char kAppContainerSid[] = "appContainerSid";
  31. const char kComponentFilters[] = "componentFilters";
  32. const char kDesiredIntegrityLevel[] = "desiredIntegrityLevel";
  33. const char kDesiredMitigations[] = "desiredMitigations";
  34. const char kDisconnectCsrss[] = "disconnectCsrss";
  35. const char kHandlesToClose[] = "handlesToClose";
  36. const char kJobLevel[] = "jobLevel";
  37. const char kLockdownLevel[] = "lockdownLevel";
  38. const char kLowboxSid[] = "lowboxSid";
  39. const char kPlatformMitigations[] = "platformMitigations";
  40. const char kPolicyRules[] = "policyRules";
  41. const char kProcessId[] = "processId";
  42. const char kTag[] = "tag";
  43. // Values in snapshots of Policies.
  44. const char kDisabled[] = "disabled";
  45. const char kEnabled[] = "enabled";
  46. std::string GetTokenLevelInEnglish(TokenLevel token) {
  47. switch (token) {
  48. case USER_LOCKDOWN:
  49. return "Lockdown";
  50. case USER_RESTRICTED:
  51. return "Restricted";
  52. case USER_LIMITED:
  53. return "Limited";
  54. case USER_INTERACTIVE:
  55. return "Interactive";
  56. case USER_RESTRICTED_SAME_ACCESS:
  57. return "Restricted Same Access";
  58. case USER_UNPROTECTED:
  59. return "None";
  60. case USER_RESTRICTED_NON_ADMIN:
  61. return "Restricted Non Admin";
  62. case USER_LAST:
  63. DCHECK(false) << "Unknown TokenType";
  64. return "Unknown";
  65. }
  66. }
  67. std::string GetJobLevelInEnglish(JobLevel job) {
  68. switch (job) {
  69. case JobLevel::kLockdown:
  70. return "Lockdown";
  71. case JobLevel::kLimitedUser:
  72. return "Limited User";
  73. case JobLevel::kInteractive:
  74. return "Interactive";
  75. case JobLevel::kUnprotected:
  76. return "Unprotected";
  77. case JobLevel::kNone:
  78. return "None";
  79. }
  80. }
  81. std::string GetIntegrityLevelInEnglish(IntegrityLevel integrity) {
  82. switch (integrity) {
  83. case INTEGRITY_LEVEL_SYSTEM:
  84. return "S-1-16-16384 System";
  85. case INTEGRITY_LEVEL_HIGH:
  86. return "S-1-16-12288 High";
  87. case INTEGRITY_LEVEL_MEDIUM:
  88. return "S-1-16-8192 Medium";
  89. case INTEGRITY_LEVEL_MEDIUM_LOW:
  90. return "S-1-16-6144 Medium Low";
  91. case INTEGRITY_LEVEL_LOW:
  92. return "S-1-16-4096 Low";
  93. case INTEGRITY_LEVEL_BELOW_LOW:
  94. return "S-1-16-2048 Below Low";
  95. case INTEGRITY_LEVEL_UNTRUSTED:
  96. return "S-1-16-0 Untrusted";
  97. case INTEGRITY_LEVEL_LAST:
  98. return "Default";
  99. }
  100. }
  101. std::wstring GetSidAsString(const base::win::Sid& sid) {
  102. absl::optional<std::wstring> result = sid.ToSddlString();
  103. if (!result) {
  104. DCHECK(false) << "Failed to make sddl string";
  105. return L"";
  106. }
  107. return *result;
  108. }
  109. std::string GetMitigationsAsHex(MitigationFlags mitigations) {
  110. return base::StringPrintf("%016" PRIx64,
  111. base::checked_cast<uint64_t>(mitigations));
  112. }
  113. std::string GetPlatformMitigationsAsHex(MitigationFlags mitigations) {
  114. DWORD64 platform_flags[2] = {0};
  115. size_t flags_size = 0;
  116. sandbox::ConvertProcessMitigationsToPolicy(mitigations, &(platform_flags[0]),
  117. &flags_size);
  118. DCHECK(flags_size / sizeof(DWORD64) <= 2)
  119. << "Unexpected mitigation flags size";
  120. if (flags_size == 2 * sizeof(DWORD64))
  121. return base::StringPrintf("%016" PRIx64 "%016" PRIx64, platform_flags[0],
  122. platform_flags[1]);
  123. return base::StringPrintf("%016" PRIx64, platform_flags[0]);
  124. }
  125. std::string GetComponentFilterAsHex(MitigationFlags mitigations) {
  126. COMPONENT_FILTER filter;
  127. sandbox::ConvertProcessMitigationsToComponentFilter(mitigations, &filter);
  128. return base::StringPrintf("%08lx", filter.ComponentFlags);
  129. }
  130. std::string GetIpcTagAsString(IpcTag service) {
  131. switch (service) {
  132. case IpcTag::UNUSED:
  133. DCHECK(false) << "Unused IpcTag";
  134. return "Unused";
  135. case IpcTag::PING1:
  136. return "Ping1";
  137. case IpcTag::PING2:
  138. return "Ping2";
  139. case IpcTag::NTCREATEFILE:
  140. return "NtCreateFile";
  141. case IpcTag::NTOPENFILE:
  142. return "NtOpenFile";
  143. case IpcTag::NTQUERYATTRIBUTESFILE:
  144. return "NtQueryAttributesFile";
  145. case IpcTag::NTQUERYFULLATTRIBUTESFILE:
  146. return "NtQueryFullAttributesFile";
  147. case IpcTag::NTSETINFO_RENAME:
  148. return "NtSetInfoRename";
  149. case IpcTag::CREATENAMEDPIPEW:
  150. return "CreateNamedPipeW";
  151. case IpcTag::NTOPENTHREAD:
  152. return "NtOpenThread";
  153. case IpcTag::NTOPENPROCESS:
  154. return "NtOpenProcess";
  155. case IpcTag::NTOPENPROCESSTOKEN:
  156. return "NtOpenProcessToken";
  157. case IpcTag::NTOPENPROCESSTOKENEX:
  158. return "NtOpenProcessTokenEx";
  159. case IpcTag::GDI_GDIDLLINITIALIZE:
  160. return "GdiDllInitialize";
  161. case IpcTag::GDI_GETSTOCKOBJECT:
  162. return "GetStockObject";
  163. case IpcTag::USER_REGISTERCLASSW:
  164. return "RegisterClassW";
  165. case IpcTag::CREATETHREAD:
  166. return "CreateThread";
  167. case IpcTag::NTCREATESECTION:
  168. return "NtCreateSection";
  169. case IpcTag::WS2SOCKET:
  170. return "WSA_Socket";
  171. case IpcTag::LAST:
  172. DCHECK(false) << "Unknown IpcTag";
  173. return "Unknown";
  174. }
  175. }
  176. std::string GetOpcodeAction(EvalResult action) {
  177. switch (action) {
  178. case EVAL_TRUE:
  179. return "true";
  180. case EVAL_FALSE:
  181. return "false";
  182. case EVAL_ERROR:
  183. return "error";
  184. case ASK_BROKER:
  185. return "askBroker";
  186. case DENY_ACCESS:
  187. return "deny";
  188. case GIVE_READONLY:
  189. return "readonly";
  190. case GIVE_ALLACCESS:
  191. return "allaccess";
  192. case GIVE_CACHED:
  193. return "cached";
  194. case GIVE_FIRST:
  195. return "first";
  196. case SIGNAL_ALARM:
  197. return "alarm";
  198. case FAKE_SUCCESS:
  199. return "fakeSuccess";
  200. case FAKE_ACCESS_DENIED:
  201. return "fakeDenied";
  202. case TERMINATE_PROCESS:
  203. return "terminate";
  204. }
  205. }
  206. std::string GetStringMatchOperation(int pos, uint32_t options) {
  207. if (pos == 0) {
  208. if (options & EXACT_LENGTH)
  209. return "exact";
  210. else
  211. return "prefix";
  212. } else if (pos < 0) {
  213. return "scan";
  214. } else if (pos == kSeekToEnd) {
  215. return "ends";
  216. } else {
  217. DCHECK(false) << "Invalid pos (" << pos << ")";
  218. return "unknown";
  219. }
  220. }
  221. std::string GetPolicyOpcode(const PolicyOpcode* opcode, bool continuation) {
  222. // See |policy_engine_opcodes.cc|.
  223. uint32_t args[4];
  224. auto options = opcode->GetOptions();
  225. auto param = opcode->GetParameter();
  226. std::string condition;
  227. if (options & kPolNegateEval)
  228. condition += "!(";
  229. switch (opcode->GetID()) {
  230. case OP_ALWAYS_FALSE:
  231. condition += "false";
  232. break;
  233. case OP_ALWAYS_TRUE:
  234. condition += "true";
  235. break;
  236. case OP_NUMBER_MATCH:
  237. opcode->GetArgument(1, &args[1]);
  238. if (args[1] == UINT32_TYPE) {
  239. opcode->GetArgument(0, &args[0]);
  240. condition += base::StringPrintf("p[%d] == %x", param, args[0]);
  241. } else {
  242. const void* match_ptr = nullptr;
  243. opcode->GetArgument(0, &match_ptr);
  244. condition += base::StringPrintf("p[%d] == %p", param, match_ptr);
  245. }
  246. break;
  247. case OP_NUMBER_MATCH_RANGE:
  248. opcode->GetArgument(0, &args[0]);
  249. opcode->GetArgument(1, &args[1]);
  250. condition +=
  251. base::StringPrintf("%x <= p[%d] <= %x", args[0], param, args[1]);
  252. break;
  253. case OP_NUMBER_AND_MATCH:
  254. opcode->GetArgument(0, &args[0]);
  255. condition += base::StringPrintf("p[%d] & %x", param, args[0]);
  256. break;
  257. case OP_WSTRING_MATCH: {
  258. int pos;
  259. opcode->GetArgument(1, &args[1]); // Length.
  260. opcode->GetArgument(2, &pos); // Position.
  261. opcode->GetArgument(3, &args[3]); // Options.
  262. // These are not nul-terminated so we have to wrap them here.
  263. auto match_string = std::wstring(opcode->GetRelativeString(0), 0,
  264. static_cast<size_t>(args[1]));
  265. condition += GetStringMatchOperation(pos, args[3]);
  266. if (args[3] & CASE_INSENSITIVE)
  267. condition += "_i";
  268. condition +=
  269. base::StringPrintf("(p[%d], '%S')", param, match_string.c_str());
  270. } break;
  271. case OP_ACTION:
  272. opcode->GetArgument(0, &args[0]);
  273. condition += GetOpcodeAction(static_cast<EvalResult>(args[0]));
  274. break;
  275. default:
  276. DCHECK(false) << "Unknown Opcode";
  277. return "Unknown";
  278. }
  279. if (options & kPolNegateEval)
  280. condition += ")";
  281. // If there is another rule add a joining token.
  282. if (continuation) {
  283. if (options & kPolUseOREval)
  284. condition += " || ";
  285. else
  286. condition += " && ";
  287. }
  288. return condition;
  289. }
  290. // Uses |service| to index into |policy_rules| returning a list of opcodes.
  291. base::Value::List GetPolicyOpcodes(const PolicyGlobal* policy_rules,
  292. IpcTag service) {
  293. base::Value::List entry;
  294. PolicyBuffer* policy_buffer =
  295. policy_rules->entry[static_cast<size_t>(service)];
  296. // Build up rules and emit when we hit an action.
  297. std::string cur_rule;
  298. for (size_t i = 0; i < policy_buffer->opcode_count; i++) {
  299. const PolicyOpcode* opcode = &policy_buffer->opcodes[i];
  300. if (opcode->GetID() != OP_ACTION) {
  301. DCHECK(i + 1 < policy_buffer->opcode_count)
  302. << "Non-actions should not terminate rules";
  303. bool peak = policy_buffer->opcodes[i + 1].GetID() != OP_ACTION;
  304. cur_rule += GetPolicyOpcode(opcode, peak);
  305. } else {
  306. cur_rule += " -> ";
  307. cur_rule += GetPolicyOpcode(opcode, false);
  308. entry.Append(cur_rule);
  309. cur_rule.clear();
  310. }
  311. }
  312. return entry;
  313. }
  314. base::Value::Dict GetPolicyRules(const PolicyGlobal* policy_rules) {
  315. DCHECK(policy_rules);
  316. base::Value::Dict results;
  317. for (size_t i = 0; i < kMaxServiceCount; i++) {
  318. if (!policy_rules->entry[i])
  319. continue;
  320. IpcTag service = static_cast<IpcTag>(i);
  321. results.Set(GetIpcTagAsString(service),
  322. GetPolicyOpcodes(policy_rules, service));
  323. }
  324. return results;
  325. }
  326. // HandleMap is just wstrings, nested sets could be empty.
  327. base::Value::Dict GetHandlesToClose(const HandleMap& handle_map) {
  328. base::Value::Dict results;
  329. for (const auto& kv : handle_map) {
  330. base::Value::List entries;
  331. // kv.second may be an empty map.
  332. for (const auto& entry : kv.second) {
  333. entries.Append(base::AsStringPiece16(entry));
  334. }
  335. results.Set(base::WideToUTF8(kv.first), std::move(entries));
  336. }
  337. return results;
  338. }
  339. } // namespace
  340. // We are a friend of PolicyBase so that we can steal its private members
  341. // quickly in the BrokerServices tracker thread.
  342. PolicyDiagnostic::PolicyDiagnostic(PolicyBase* policy) {
  343. DCHECK(policy);
  344. ConfigBase* config = policy->config();
  345. // TODO(crbug/997273) Add more fields once webui plumbing is complete.
  346. process_id_ = base::strict_cast<uint32_t>(policy->target_->ProcessId());
  347. lockdown_level_ = policy->lockdown_level_;
  348. job_level_ = policy->job_level_;
  349. tag_ = policy->tag_;
  350. // Select the final integrity level.
  351. if (policy->delayed_integrity_level_ == INTEGRITY_LEVEL_LAST)
  352. desired_integrity_level_ = policy->integrity_level_;
  353. else
  354. desired_integrity_level_ = policy->delayed_integrity_level_;
  355. desired_mitigations_ = policy->mitigations_ | policy->delayed_mitigations_;
  356. if (policy->app_container_) {
  357. app_container_sid_.emplace(policy->app_container_->GetPackageSid().Clone());
  358. for (const auto& sid : policy->app_container_->GetCapabilities()) {
  359. capabilities_.push_back(sid.Clone());
  360. }
  361. for (const auto& sid :
  362. policy->app_container_->GetImpersonationCapabilities()) {
  363. initial_capabilities_.push_back(sid.Clone());
  364. }
  365. app_container_type_ = policy->app_container_->GetAppContainerType();
  366. }
  367. if (config->policy_) {
  368. PolicyGlobal* original_rules = config->policy_;
  369. size_t policy_mem_size = original_rules->data_size + sizeof(PolicyGlobal);
  370. policy_rules_.reset(
  371. static_cast<sandbox::PolicyGlobal*>(::operator new(policy_mem_size)));
  372. memcpy(policy_rules_.get(), original_rules, policy_mem_size);
  373. // Fixup pointers (see |PolicyGlobal| in policy_low_level.h).
  374. PolicyBuffer** original_entries = original_rules->entry;
  375. PolicyBuffer** copy_base = policy_rules_->entry;
  376. for (size_t i = 0; i < kMaxServiceCount; i++) {
  377. if (policy_rules_->entry[i]) {
  378. policy_rules_->entry[i] = reinterpret_cast<PolicyBuffer*>(
  379. reinterpret_cast<char*>(copy_base) +
  380. (reinterpret_cast<char*>(original_entries[i]) -
  381. reinterpret_cast<char*>(original_entries)));
  382. }
  383. }
  384. }
  385. is_csrss_connected_ = policy->is_csrss_connected_;
  386. handles_to_close_.insert(policy->handle_closer_.handles_to_close_.begin(),
  387. policy->handle_closer_.handles_to_close_.end());
  388. }
  389. PolicyDiagnostic::~PolicyDiagnostic() = default;
  390. const char* PolicyDiagnostic::JsonString() {
  391. // Lazily constructs json_string_.
  392. if (json_string_)
  393. return json_string_->c_str();
  394. base::Value::Dict dict;
  395. dict.Set(kProcessId, base::strict_cast<double>(process_id_));
  396. dict.Set(kTag, base::Value(tag_));
  397. dict.Set(kLockdownLevel, GetTokenLevelInEnglish(lockdown_level_));
  398. dict.Set(kJobLevel, GetJobLevelInEnglish(job_level_));
  399. dict.Set(kDesiredIntegrityLevel,
  400. GetIntegrityLevelInEnglish(desired_integrity_level_));
  401. dict.Set(kDesiredMitigations, GetMitigationsAsHex(desired_mitigations_));
  402. dict.Set(kPlatformMitigations,
  403. GetPlatformMitigationsAsHex(desired_mitigations_));
  404. dict.Set(kComponentFilters, GetComponentFilterAsHex(desired_mitigations_));
  405. if (app_container_sid_) {
  406. dict.Set(kAppContainerSid,
  407. base::AsStringPiece16(GetSidAsString(*app_container_sid_)));
  408. base::Value::List caps;
  409. for (const auto& sid : capabilities_) {
  410. auto sid_value = base::Value(base::AsStringPiece16(GetSidAsString(sid)));
  411. caps.Append(std::move(sid_value));
  412. }
  413. if (!caps.empty()) {
  414. dict.Set(kAppContainerCapabilities, std::move(caps));
  415. }
  416. base::Value::List imp_caps;
  417. for (const auto& sid : initial_capabilities_) {
  418. auto sid_value = base::Value(base::AsStringPiece16(GetSidAsString(sid)));
  419. imp_caps.Append(std::move(sid_value));
  420. }
  421. if (!imp_caps.empty()) {
  422. dict.Set(kAppContainerInitialCapabilities, std::move(imp_caps));
  423. }
  424. if (app_container_type_ == AppContainerType::kLowbox)
  425. dict.Set(kLowboxSid,
  426. base::AsStringPiece16(GetSidAsString(*app_container_sid_)));
  427. }
  428. if (policy_rules_)
  429. dict.Set(kPolicyRules, GetPolicyRules(policy_rules_.get()));
  430. dict.Set(kDisconnectCsrss, is_csrss_connected_ ? kDisabled : kEnabled);
  431. if (!handles_to_close_.empty())
  432. dict.Set(kHandlesToClose, GetHandlesToClose(handles_to_close_));
  433. auto json_string = std::make_unique<std::string>();
  434. JSONStringValueSerializer to_json(json_string.get());
  435. CHECK(to_json.Serialize(dict));
  436. json_string_ = std::move(json_string);
  437. return json_string_->c_str();
  438. }
  439. } // namespace sandbox