12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "components/policy/core/common/cloud/resource_cache.h"
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/base64.h"
8f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "base/callback.h"
9eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/files/file_enumerator.h"
101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/files/file_util.h"
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/logging.h"
125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/numerics/safe_conversions.h"
13d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "base/sequenced_task_runner.h"
14868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_util.h"
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace policy {
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
18eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochnamespace {
19eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
20eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// Verifies that |value| is not empty and encodes it into base64url format,
21eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// which is safe to use as a file name on all platforms.
225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liubool Base64UrlEncode(const std::string& value, std::string* encoded) {
23eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  DCHECK(!value.empty());
24a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (value.empty())
25eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return false;
26a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::Base64Encode(value, encoded);
27a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::ReplaceChars(*encoded, "+", "-", encoded);
28a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::ReplaceChars(*encoded, "/", "_", encoded);
295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // Note: this encoding keeps the padding chars, though the "Baset64 with safe
305c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // URL alphabet" encoding trims them. See Base64UrlDecode below.
31eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return true;
32eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
33eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
349ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch// Decodes all elements of |input| from base64url format and stores the decoded
359ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch// elements in |output|.
365c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liubool Base64UrlEncode(const std::set<std::string>& input,
375c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu                     std::set<std::string>* output) {
389ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch  output->clear();
399ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch  for (std::set<std::string>::const_iterator it = input.begin();
409ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch       it != input.end(); ++it) {
419ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch    std::string encoded;
425c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    if (!Base64UrlEncode(*it, &encoded)) {
439ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch      output->clear();
449ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch      return false;
459ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch    }
469ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch    output->insert(encoded);
479ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch  }
489ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch  return true;
499ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch}
509ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch
51eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// Decodes |encoded| from base64url format and verifies that the result is not
52eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// emtpy.
535c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liubool Base64UrlDecode(const std::string& encoded, std::string* value) {
54eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  std::string buffer;
55a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::ReplaceChars(encoded, "-", "+", &buffer);
56a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::ReplaceChars(buffer, "_", "/", &buffer);
57eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return base::Base64Decode(buffer, value) && !value->empty();
58eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
59eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
60eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}  // namespace
61eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
62d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)ResourceCache::ResourceCache(
63d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    const base::FilePath& cache_dir,
64d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    scoped_refptr<base::SequencedTaskRunner> task_runner)
65d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    : cache_dir_(cache_dir),
66d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      task_runner_(task_runner) {
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ResourceCache::~ResourceCache() {
70d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DCHECK(task_runner_->RunsTasksOnCurrentThread());
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool ResourceCache::Store(const std::string& key,
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                          const std::string& subkey,
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                          const std::string& data) {
76d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DCHECK(task_runner_->RunsTasksOnCurrentThread());
77eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  base::FilePath subkey_path;
78eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Delete the file before writing to it. This ensures that the write does not
79eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // follow a symlink planted at |subkey_path|, clobbering a file outside the
80eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // cache directory. The mechanism is meant to foil file-system-level attacks
81eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // where a symlink is planted in the cache directory before Chrome has
82eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // started. An attacker controlling a process running concurrently with Chrome
83eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // would be able to race against the protection by re-creating the symlink
84eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // between these two calls. There is nothing in file_util that could be used
85eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // to protect against such races, especially as the cache is cross-platform
86eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // and therefore cannot use any POSIX-only tricks.
875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  int size = base::checked_cast<int>(data.size());
88eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return VerifyKeyPathAndGetSubkeyPath(key, true, subkey, &subkey_path) &&
897dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch         base::DeleteFile(subkey_path, false) &&
90a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)         (base::WriteFile(subkey_path, data.data(), size) == size);
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool ResourceCache::Load(const std::string& key,
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                         const std::string& subkey,
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                         std::string* data) {
96d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DCHECK(task_runner_->RunsTasksOnCurrentThread());
97eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  base::FilePath subkey_path;
98eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Only read from |subkey_path| if it is not a symlink.
99eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (!VerifyKeyPathAndGetSubkeyPath(key, false, subkey, &subkey_path) ||
100a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      base::IsLink(subkey_path)) {
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
102eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
103eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  data->clear();
10458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  return base::ReadFileToString(subkey_path, data);
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ResourceCache::LoadAllSubkeys(
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const std::string& key,
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    std::map<std::string, std::string>* contents) {
110d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DCHECK(task_runner_->RunsTasksOnCurrentThread());
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  contents->clear();
112eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  base::FilePath key_path;
113eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (!VerifyKeyPath(key, false, &key_path))
114eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return;
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
116eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  base::FileEnumerator enumerator(key_path, false, base::FileEnumerator::FILES);
117eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  for (base::FilePath path = enumerator.Next(); !path.empty();
118eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch       path = enumerator.Next()) {
119eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const std::string encoded_subkey = path.BaseName().MaybeAsASCII();
120eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    std::string subkey;
121eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    std::string data;
122eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // Only read from |subkey_path| if it is not a symlink and its name is
123eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // a base64-encoded string.
124a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (!base::IsLink(path) &&
1255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        Base64UrlDecode(encoded_subkey, &subkey) &&
12658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        base::ReadFileToString(path, &data)) {
127eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      (*contents)[subkey].swap(data);
128eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    }
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ResourceCache::Delete(const std::string& key, const std::string& subkey) {
133d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DCHECK(task_runner_->RunsTasksOnCurrentThread());
134eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  base::FilePath subkey_path;
135eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (VerifyKeyPathAndGetSubkeyPath(key, false, subkey, &subkey_path))
1367dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    base::DeleteFile(subkey_path, false);
137eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Delete() does nothing if the directory given to it is not empty. Hence, the
138eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // call below deletes the directory representing |key| if its last subkey was
139eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // just removed and does nothing otherwise.
1407dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  base::DeleteFile(subkey_path.DirName(), false);
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
143f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void ResourceCache::Clear(const std::string& key) {
144f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  DCHECK(task_runner_->RunsTasksOnCurrentThread());
145f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  base::FilePath key_path;
146f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (VerifyKeyPath(key, false, &key_path))
147f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    base::DeleteFile(key_path, true);
148f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
149f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
150f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void ResourceCache::FilterSubkeys(const std::string& key,
151f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                  const SubkeyFilter& test) {
152f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  DCHECK(task_runner_->RunsTasksOnCurrentThread());
153f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
154f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  base::FilePath key_path;
155f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!VerifyKeyPath(key, false, &key_path))
156f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return;
157f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
158f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  base::FileEnumerator enumerator(key_path, false, base::FileEnumerator::FILES);
159f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  for (base::FilePath subkey_path = enumerator.Next();
160f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)       !subkey_path.empty(); subkey_path = enumerator.Next()) {
161f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    std::string subkey;
162f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    // Delete files with invalid names, and files whose subkey doesn't pass the
163f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    // filter.
1645c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    if (!Base64UrlDecode(subkey_path.BaseName().MaybeAsASCII(), &subkey) ||
165f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        test.Run(subkey)) {
166f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      base::DeleteFile(subkey_path, true);
167f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    }
168f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
169f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
170f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // Delete() does nothing if the directory given to it is not empty. Hence, the
171f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // call below deletes the directory representing |key| if all of its subkeys
172f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // were just removed and does nothing otherwise.
173f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  base::DeleteFile(key_path, false);
174f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
175f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
1769ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdochvoid ResourceCache::PurgeOtherKeys(const std::set<std::string>& keys_to_keep) {
177d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DCHECK(task_runner_->RunsTasksOnCurrentThread());
1789ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch  std::set<std::string> encoded_keys_to_keep;
1795c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (!Base64UrlEncode(keys_to_keep, &encoded_keys_to_keep))
1809ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch    return;
1819ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch
1829ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch  base::FileEnumerator enumerator(
1839ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch      cache_dir_, false, base::FileEnumerator::DIRECTORIES);
1849ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch  for (base::FilePath path = enumerator.Next(); !path.empty();
1859ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch       path = enumerator.Next()) {
1869ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch    const std::string name(path.BaseName().MaybeAsASCII());
1879ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch    if (encoded_keys_to_keep.find(name) == encoded_keys_to_keep.end())
1889ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch      base::DeleteFile(path, true);
1899ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch  }
1909ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch}
1919ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ResourceCache::PurgeOtherSubkeys(
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const std::string& key,
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const std::set<std::string>& subkeys_to_keep) {
195d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DCHECK(task_runner_->RunsTasksOnCurrentThread());
196eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  base::FilePath key_path;
197eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (!VerifyKeyPath(key, false, &key_path))
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
200eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  std::set<std::string> encoded_subkeys_to_keep;
2015c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (!Base64UrlEncode(subkeys_to_keep, &encoded_subkeys_to_keep))
2029ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch    return;
2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
204eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  base::FileEnumerator enumerator(key_path, false, base::FileEnumerator::FILES);
205eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  for (base::FilePath path = enumerator.Next(); !path.empty();
206eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch       path = enumerator.Next()) {
207eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const std::string name(path.BaseName().MaybeAsASCII());
208eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (encoded_subkeys_to_keep.find(name) == encoded_subkeys_to_keep.end())
2097dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      base::DeleteFile(path, false);
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
211eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Delete() does nothing if the directory given to it is not empty. Hence, the
212eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // call below deletes the directory representing |key| if all of its subkeys
213eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // were just removed and does nothing otherwise.
2147dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  base::DeleteFile(key_path, false);
2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
217eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochbool ResourceCache::VerifyKeyPath(const std::string& key,
218eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                  bool allow_create,
219eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                  base::FilePath* path) {
220eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  std::string encoded;
2215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (!Base64UrlEncode(key, &encoded))
222eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return false;
223eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  *path = cache_dir_.AppendASCII(encoded);
224a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  return allow_create ? base::CreateDirectory(*path) :
2257dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                        base::DirectoryExists(*path);
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
228eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochbool ResourceCache::VerifyKeyPathAndGetSubkeyPath(const std::string& key,
229eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                                  bool allow_create_key,
230eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                                  const std::string& subkey,
231eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                                  base::FilePath* path) {
232eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  base::FilePath key_path;
233eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  std::string encoded;
234eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (!VerifyKeyPath(key, allow_create_key, &key_path) ||
2355c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      !Base64UrlEncode(subkey, &encoded)) {
236eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return false;
2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
238eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  *path = key_path.AppendASCII(encoded);
239eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return true;
2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
242eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace policy
244