Home | History | Annotate | Download | only in policy
      1 // Copyright (c) 2011 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 
      5 #include "chrome/browser/policy/user_policy_cache.h"
      6 
      7 #include <limits>
      8 #include <string>
      9 
     10 #include "base/file_util.h"
     11 #include "base/logging.h"
     12 #include "base/task.h"
     13 #include "base/values.h"
     14 #include "chrome/browser/policy/configuration_policy_pref_store.h"
     15 #include "chrome/browser/policy/policy_map.h"
     16 #include "chrome/browser/policy/proto/cloud_policy.pb.h"
     17 #include "chrome/browser/policy/proto/device_management_local.pb.h"
     18 #include "chrome/browser/policy/proto/old_generic_format.pb.h"
     19 #include "content/browser/browser_thread.h"
     20 #include "policy/configuration_policy_type.h"
     21 
     22 namespace policy {
     23 
     24 // Decodes a CloudPolicySettings object into two maps with mandatory and
     25 // recommended settings, respectively. The implementation is generated code
     26 // in policy/cloud_policy_generated.cc.
     27 void DecodePolicy(const em::CloudPolicySettings& policy,
     28                   PolicyMap* mandatory, PolicyMap* recommended);
     29 
     30 // Saves policy information to a file.
     31 class PersistPolicyTask : public Task {
     32  public:
     33   PersistPolicyTask(const FilePath& path,
     34                     const em::PolicyFetchResponse* cloud_policy_response,
     35                     const bool is_unmanaged)
     36       : path_(path),
     37         cloud_policy_response_(cloud_policy_response),
     38         is_unmanaged_(is_unmanaged) {}
     39 
     40  private:
     41   // Task override.
     42   virtual void Run();
     43 
     44   const FilePath path_;
     45   scoped_ptr<const em::PolicyFetchResponse> cloud_policy_response_;
     46   const bool is_unmanaged_;
     47 };
     48 
     49 void PersistPolicyTask::Run() {
     50   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
     51   std::string data;
     52   em::CachedCloudPolicyResponse cached_policy;
     53   if (cloud_policy_response_.get()) {
     54     cached_policy.mutable_cloud_policy()->CopyFrom(*cloud_policy_response_);
     55   }
     56   if (is_unmanaged_) {
     57     cached_policy.set_unmanaged(true);
     58     cached_policy.set_timestamp(base::Time::NowFromSystemTime().ToTimeT());
     59   }
     60   if (!cached_policy.SerializeToString(&data)) {
     61     LOG(WARNING) << "Failed to serialize policy data";
     62     return;
     63   }
     64 
     65   int size = data.size();
     66   if (file_util::WriteFile(path_, data.c_str(), size) != size) {
     67     LOG(WARNING) << "Failed to write " << path_.value();
     68     return;
     69   }
     70 }
     71 
     72 UserPolicyCache::UserPolicyCache(const FilePath& backing_file_path)
     73     : backing_file_path_(backing_file_path) {
     74 }
     75 
     76 UserPolicyCache::~UserPolicyCache() {
     77 }
     78 
     79 void UserPolicyCache::Load() {
     80   // TODO(jkummerow): This method is doing file IO during browser startup. In
     81   // the long run it would be better to delay this until the FILE thread exists.
     82   if (!file_util::PathExists(backing_file_path_) || initialization_complete()) {
     83     return;
     84   }
     85 
     86   // Read the protobuf from the file.
     87   std::string data;
     88   if (!file_util::ReadFileToString(backing_file_path_, &data)) {
     89     LOG(WARNING) << "Failed to read policy data from "
     90                  << backing_file_path_.value();
     91     return;
     92   }
     93 
     94   em::CachedCloudPolicyResponse cached_response;
     95   if (!cached_response.ParseFromArray(data.c_str(), data.size())) {
     96     LOG(WARNING) << "Failed to parse policy data read from "
     97                  << backing_file_path_.value();
     98     return;
     99   }
    100 
    101   if (cached_response.unmanaged()) {
    102     SetUnmanagedInternal(base::Time::FromTimeT(cached_response.timestamp()));
    103   } else if (cached_response.has_cloud_policy()) {
    104     base::Time timestamp;
    105     if (SetPolicyInternal(cached_response.cloud_policy(), &timestamp, true))
    106       set_last_policy_refresh_time(timestamp);
    107   }
    108 }
    109 
    110 void UserPolicyCache::SetPolicy(const em::PolicyFetchResponse& policy) {
    111   base::Time now = base::Time::NowFromSystemTime();
    112   set_last_policy_refresh_time(now);
    113   bool ok = SetPolicyInternal(policy, NULL, false);
    114   if (ok)
    115     PersistPolicy(policy, now);
    116 }
    117 
    118 void UserPolicyCache::SetUnmanaged() {
    119   DCHECK(CalledOnValidThread());
    120   SetUnmanagedInternal(base::Time::NowFromSystemTime());
    121   BrowserThread::PostTask(
    122       BrowserThread::FILE,
    123       FROM_HERE,
    124       new PersistPolicyTask(backing_file_path_, NULL, true));
    125 }
    126 
    127 void UserPolicyCache::PersistPolicy(const em::PolicyFetchResponse& policy,
    128                                     const base::Time& timestamp) {
    129   if (timestamp > base::Time::NowFromSystemTime() +
    130                   base::TimeDelta::FromMinutes(1)) {
    131     LOG(WARNING) << "Server returned policy with timestamp from the future, "
    132                     "not persisting to disk.";
    133   } else {
    134     em::PolicyFetchResponse* policy_copy = new em::PolicyFetchResponse;
    135     policy_copy->CopyFrom(policy);
    136     BrowserThread::PostTask(
    137         BrowserThread::FILE,
    138         FROM_HERE,
    139         new PersistPolicyTask(backing_file_path_, policy_copy, false));
    140   }
    141 }
    142 
    143 bool UserPolicyCache::DecodePolicyData(const em::PolicyData& policy_data,
    144                                        PolicyMap* mandatory,
    145                                        PolicyMap* recommended) {
    146   // TODO(jkummerow): Verify policy_data.device_token(). Needs final
    147   // specification which token we're actually sending / expecting to get back.
    148   em::CloudPolicySettings policy;
    149   if (!policy.ParseFromString(policy_data.policy_value())) {
    150     LOG(WARNING) << "Failed to parse CloudPolicySettings protobuf.";
    151     return false;
    152   }
    153   DecodePolicy(policy, mandatory, recommended);
    154   MaybeDecodeOldstylePolicy(policy_data.policy_value(), mandatory, recommended);
    155   return true;
    156 }
    157 
    158 // Everything below is only needed for supporting old-style GenericNamedValue
    159 // based policy data and can be removed once this support is no longer needed.
    160 
    161 using google::protobuf::RepeatedField;
    162 using google::protobuf::RepeatedPtrField;
    163 
    164 class PolicyMapProxy : public ConfigurationPolicyStoreInterface {
    165  public:
    166   // Does not take ownership of |policy_map|, and callers need to make sure
    167   // that |policy_map| outlives this PolicyMapProxy.
    168   explicit PolicyMapProxy(PolicyMap* policy_map)
    169       : policy_map_(policy_map) {}
    170   virtual ~PolicyMapProxy() {}
    171   virtual void Apply(ConfigurationPolicyType policy, Value* value) {
    172     policy_map_->Set(policy, value);
    173   }
    174 
    175  private:
    176   PolicyMap* policy_map_;
    177 
    178   DISALLOW_COPY_AND_ASSIGN(PolicyMapProxy);
    179 };
    180 
    181 void UserPolicyCache::MaybeDecodeOldstylePolicy(
    182     const std::string& policy_data,
    183     PolicyMap* mandatory,
    184     PolicyMap* recommended) {
    185   // Return immediately if we already have policy information in the maps.
    186   if (!mandatory->empty() || !recommended->empty())
    187     return;
    188   em::LegacyChromeSettingsProto policy;
    189   // Return if the input string doesn't match the protobuf definition.
    190   if (!policy.ParseFromString(policy_data))
    191     return;
    192   // Return if there's no old-style policy to decode.
    193   if (policy.named_value_size() == 0)
    194     return;
    195 
    196   // Inspect GenericNamedValues and decode them.
    197   DictionaryValue result;
    198   RepeatedPtrField<em::GenericNamedValue>::const_iterator named_value;
    199   for (named_value = policy.named_value().begin();
    200        named_value != policy.named_value().end();
    201        ++named_value) {
    202     if (named_value->has_value()) {
    203       Value* decoded_value = DecodeValue(named_value->value());
    204       if (decoded_value)
    205         result.Set(named_value->name(), decoded_value);
    206     }
    207   }
    208   // Hack: Let one of the providers do the transformation from DictionaryValue
    209   // to PolicyMap, since they have the required code anyway.
    210   PolicyMapProxy map_proxy(mandatory);
    211   GetManagedPolicyProvider()->ApplyPolicyValueTree(&result, &map_proxy);
    212 }
    213 
    214 Value* UserPolicyCache::DecodeIntegerValue(
    215     google::protobuf::int64 value) const {
    216   if (value < std::numeric_limits<int>::min() ||
    217       value > std::numeric_limits<int>::max()) {
    218     LOG(WARNING) << "Integer value " << value
    219                  << " out of numeric limits, ignoring.";
    220     return NULL;
    221   }
    222 
    223   return Value::CreateIntegerValue(static_cast<int>(value));
    224 }
    225 
    226 Value* UserPolicyCache::DecodeValue(const em::GenericValue& value) const {
    227   if (!value.has_value_type())
    228     return NULL;
    229 
    230   switch (value.value_type()) {
    231     case em::GenericValue::VALUE_TYPE_BOOL:
    232       if (value.has_bool_value())
    233         return Value::CreateBooleanValue(value.bool_value());
    234       return NULL;
    235     case em::GenericValue::VALUE_TYPE_INT64:
    236       if (value.has_int64_value())
    237         return DecodeIntegerValue(value.int64_value());
    238       return NULL;
    239     case em::GenericValue::VALUE_TYPE_STRING:
    240       if (value.has_string_value())
    241         return Value::CreateStringValue(value.string_value());
    242       return NULL;
    243     case em::GenericValue::VALUE_TYPE_DOUBLE:
    244       if (value.has_double_value())
    245         return Value::CreateDoubleValue(value.double_value());
    246       return NULL;
    247     case em::GenericValue::VALUE_TYPE_BYTES:
    248       if (value.has_bytes_value()) {
    249         std::string bytes = value.bytes_value();
    250         return BinaryValue::CreateWithCopiedBuffer(bytes.c_str(), bytes.size());
    251       }
    252       return NULL;
    253     case em::GenericValue::VALUE_TYPE_BOOL_ARRAY: {
    254       ListValue* list = new ListValue;
    255       RepeatedField<bool>::const_iterator i;
    256       for (i = value.bool_array().begin(); i != value.bool_array().end(); ++i)
    257         list->Append(Value::CreateBooleanValue(*i));
    258       return list;
    259     }
    260     case em::GenericValue::VALUE_TYPE_INT64_ARRAY: {
    261       ListValue* list = new ListValue;
    262       RepeatedField<google::protobuf::int64>::const_iterator i;
    263       for (i = value.int64_array().begin();
    264            i != value.int64_array().end(); ++i) {
    265         Value* int_value = DecodeIntegerValue(*i);
    266         if (int_value)
    267           list->Append(int_value);
    268       }
    269       return list;
    270     }
    271     case em::GenericValue::VALUE_TYPE_STRING_ARRAY: {
    272       ListValue* list = new ListValue;
    273       RepeatedPtrField<std::string>::const_iterator i;
    274       for (i = value.string_array().begin();
    275            i != value.string_array().end(); ++i)
    276         list->Append(Value::CreateStringValue(*i));
    277       return list;
    278     }
    279     case em::GenericValue::VALUE_TYPE_DOUBLE_ARRAY: {
    280       ListValue* list = new ListValue;
    281       RepeatedField<double>::const_iterator i;
    282       for (i = value.double_array().begin();
    283            i != value.double_array().end(); ++i)
    284         list->Append(Value::CreateDoubleValue(*i));
    285       return list;
    286     }
    287     default:
    288       NOTREACHED() << "Unhandled value type";
    289   }
    290 
    291   return NULL;
    292 }
    293 
    294 }  // namespace policy
    295