Home | History | Annotate | Download | only in policy
      1 // Copyright (c) 2012 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/policy_loader_mac.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/bind_helpers.h"
      9 #include "base/callback.h"
     10 #include "base/file_util.h"
     11 #include "base/mac/foundation_util.h"
     12 #include "base/mac/scoped_cftyperef.h"
     13 #include "base/path_service.h"
     14 #include "base/platform_file.h"
     15 #include "base/strings/sys_string_conversions.h"
     16 #include "base/values.h"
     17 #include "chrome/browser/policy/external_data_fetcher.h"
     18 #include "chrome/browser/policy/policy_bundle.h"
     19 #include "chrome/browser/policy/policy_domain_descriptor.h"
     20 #include "chrome/browser/policy/policy_load_status.h"
     21 #include "chrome/browser/policy/policy_map.h"
     22 #include "chrome/browser/policy/preferences_mac.h"
     23 #include "chrome/common/chrome_paths.h"
     24 #include "chrome/common/policy/policy_schema.h"
     25 #include "policy/policy_constants.h"
     26 
     27 using base::mac::CFCast;
     28 using base::ScopedCFTypeRef;
     29 
     30 namespace policy {
     31 
     32 namespace {
     33 
     34 base::FilePath GetManagedPolicyPath() {
     35   // This constructs the path to the plist file in which Mac OS X stores the
     36   // managed preference for the application. This is undocumented and therefore
     37   // fragile, but if it doesn't work out, AsyncPolicyLoader has a task that
     38   // polls periodically in order to reload managed preferences later even if we
     39   // missed the change.
     40   base::FilePath path;
     41   if (!PathService::Get(chrome::DIR_MANAGED_PREFS, &path))
     42     return base::FilePath();
     43 
     44   CFBundleRef bundle(CFBundleGetMainBundle());
     45   if (!bundle)
     46     return base::FilePath();
     47 
     48   CFStringRef bundle_id = CFBundleGetIdentifier(bundle);
     49   if (!bundle_id)
     50     return base::FilePath();
     51 
     52   return path.Append(base::SysCFStringRefToUTF8(bundle_id) + ".plist");
     53 }
     54 
     55 // Callback function for CFDictionaryApplyFunction. |key| and |value| are an
     56 // entry of the CFDictionary that should be converted into an equivalent entry
     57 // in the DictionaryValue in |context|.
     58 void DictionaryEntryToValue(const void* key, const void* value, void* context) {
     59   if (CFStringRef cf_key = CFCast<CFStringRef>(key)) {
     60     base::Value* converted =
     61         PolicyLoaderMac::CreateValueFromProperty(
     62             static_cast<CFPropertyListRef>(value));
     63     if (converted) {
     64       const std::string string = base::SysCFStringRefToUTF8(cf_key);
     65       static_cast<base::DictionaryValue *>(context)->Set(string, converted);
     66     }
     67   }
     68 }
     69 
     70 // Callback function for CFArrayApplyFunction. |value| is an entry of the
     71 // CFArray that should be converted into an equivalent entry in the ListValue
     72 // in |context|.
     73 void ArrayEntryToValue(const void* value, void* context) {
     74   base::Value* converted =
     75       PolicyLoaderMac::CreateValueFromProperty(
     76           static_cast<CFPropertyListRef>(value));
     77   if (converted)
     78     static_cast<base::ListValue *>(context)->Append(converted);
     79 }
     80 
     81 }  // namespace
     82 
     83 PolicyLoaderMac::PolicyLoaderMac(const PolicyDefinitionList* policy_list,
     84                                  MacPreferences* preferences)
     85     : policy_list_(policy_list),
     86       preferences_(preferences),
     87       managed_policy_path_(GetManagedPolicyPath()) {}
     88 
     89 PolicyLoaderMac::~PolicyLoaderMac() {}
     90 
     91 void PolicyLoaderMac::InitOnFile() {
     92   if (!managed_policy_path_.empty()) {
     93     watcher_.Watch(
     94         managed_policy_path_, false,
     95         base::Bind(&PolicyLoaderMac::OnFileUpdated, base::Unretained(this)));
     96   }
     97 }
     98 
     99 scoped_ptr<PolicyBundle> PolicyLoaderMac::Load() {
    100   preferences_->AppSynchronize(kCFPreferencesCurrentApplication);
    101   scoped_ptr<PolicyBundle> bundle(new PolicyBundle());
    102 
    103   // Load Chrome's policy.
    104   // TODO(joaodasilva): use a schema for Chrome once it's generated and
    105   // available from a PolicyDomainDescriptor.
    106   PolicyMap& chrome_policy =
    107       bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
    108 
    109   PolicyLoadStatusSample status;
    110   bool policy_present = false;
    111   const PolicyDefinitionList::Entry* current;
    112   for (current = policy_list_->begin; current != policy_list_->end; ++current) {
    113     base::ScopedCFTypeRef<CFStringRef> name(
    114         base::SysUTF8ToCFStringRef(current->name));
    115     base::ScopedCFTypeRef<CFPropertyListRef> value(
    116         preferences_->CopyAppValue(name, kCFPreferencesCurrentApplication));
    117     if (!value.get())
    118       continue;
    119     policy_present = true;
    120     bool forced =
    121         preferences_->AppValueIsForced(name, kCFPreferencesCurrentApplication);
    122     PolicyLevel level = forced ? POLICY_LEVEL_MANDATORY :
    123                                  POLICY_LEVEL_RECOMMENDED;
    124     // TODO(joaodasilva): figure the policy scope.
    125     base::Value* policy = CreateValueFromProperty(value);
    126     if (policy)
    127       chrome_policy.Set(current->name, level, POLICY_SCOPE_USER, policy, NULL);
    128     else
    129       status.Add(POLICY_LOAD_STATUS_PARSE_ERROR);
    130   }
    131 
    132   if (!policy_present)
    133     status.Add(POLICY_LOAD_STATUS_NO_POLICY);
    134 
    135   // Load policy for the registered components.
    136   static const struct {
    137     PolicyDomain domain;
    138     const char* domain_name;
    139   } kSupportedDomains[] = {
    140     { POLICY_DOMAIN_EXTENSIONS, "extensions" },
    141   };
    142   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kSupportedDomains); ++i) {
    143     DescriptorMap::const_iterator it =
    144         descriptor_map().find(kSupportedDomains[i].domain);
    145     if (it != descriptor_map().end()) {
    146       LoadPolicyForDomain(
    147           it->second, kSupportedDomains[i].domain_name, bundle.get());
    148     }
    149   }
    150 
    151   return bundle.Pass();
    152 }
    153 
    154 base::Time PolicyLoaderMac::LastModificationTime() {
    155   base::PlatformFileInfo file_info;
    156   if (!file_util::GetFileInfo(managed_policy_path_, &file_info) ||
    157       file_info.is_directory) {
    158     return base::Time();
    159   }
    160 
    161   return file_info.last_modified;
    162 }
    163 
    164 // static
    165 base::Value* PolicyLoaderMac::CreateValueFromProperty(
    166     CFPropertyListRef property) {
    167   if (CFCast<CFNullRef>(property))
    168     return base::Value::CreateNullValue();
    169 
    170   if (CFBooleanRef boolean = CFCast<CFBooleanRef>(property))
    171     return base::Value::CreateBooleanValue(CFBooleanGetValue(boolean));
    172 
    173   if (CFNumberRef number = CFCast<CFNumberRef>(property)) {
    174     // CFNumberGetValue() converts values implicitly when the conversion is not
    175     // lossy. Check the type before trying to convert.
    176     if (CFNumberIsFloatType(number)) {
    177       double double_value;
    178       if (CFNumberGetValue(number, kCFNumberDoubleType, &double_value))
    179         return base::Value::CreateDoubleValue(double_value);
    180     } else {
    181       int int_value;
    182       if (CFNumberGetValue(number, kCFNumberIntType, &int_value))
    183         return base::Value::CreateIntegerValue(int_value);
    184     }
    185   }
    186 
    187   if (CFStringRef string = CFCast<CFStringRef>(property))
    188     return base::Value::CreateStringValue(base::SysCFStringRefToUTF8(string));
    189 
    190   if (CFDictionaryRef dict = CFCast<CFDictionaryRef>(property)) {
    191     base::DictionaryValue* dict_value = new base::DictionaryValue();
    192     CFDictionaryApplyFunction(dict, DictionaryEntryToValue, dict_value);
    193     return dict_value;
    194   }
    195 
    196   if (CFArrayRef array = CFCast<CFArrayRef>(property)) {
    197     base::ListValue* list_value = new base::ListValue();
    198     CFArrayApplyFunction(array,
    199                          CFRangeMake(0, CFArrayGetCount(array)),
    200                          ArrayEntryToValue,
    201                          list_value);
    202     return list_value;
    203   }
    204 
    205   return NULL;
    206 }
    207 
    208 void PolicyLoaderMac::LoadPolicyForDomain(
    209     scoped_refptr<const PolicyDomainDescriptor> descriptor,
    210     const std::string& domain_name,
    211     PolicyBundle* bundle) {
    212   std::string id_prefix(base::mac::BaseBundleID());
    213   id_prefix.append(".").append(domain_name).append(".");
    214 
    215   for (PolicyDomainDescriptor::SchemaMap::const_iterator it_schema =
    216            descriptor->components().begin();
    217        it_schema != descriptor->components().end(); ++it_schema) {
    218     PolicyMap policy;
    219     LoadPolicyForComponent(
    220         id_prefix + it_schema->first, it_schema->second, &policy);
    221     if (!policy.empty()) {
    222       bundle->Get(PolicyNamespace(descriptor->domain(), it_schema->first))
    223           .Swap(&policy);
    224     }
    225   }
    226 }
    227 
    228 void PolicyLoaderMac::LoadPolicyForComponent(
    229     const std::string& bundle_id_string,
    230     const PolicySchema* schema,
    231     PolicyMap* policy) {
    232   // TODO(joaodasilva): extensions may be registered in a PolicyDomainDescriptor
    233   // without a PolicySchema, to allow a graceful update of the Legacy Browser
    234   // Support extension on Windows. Remove this temporary check once that support
    235   // is removed.
    236   if (!schema)
    237     return;
    238 
    239   base::ScopedCFTypeRef<CFStringRef> bundle_id(
    240       base::SysUTF8ToCFStringRef(bundle_id_string));
    241   preferences_->AppSynchronize(bundle_id);
    242 
    243   const PolicySchemaMap* map = schema->GetProperties();
    244   if (!map) {
    245     NOTREACHED();
    246     return;
    247   }
    248 
    249   for (PolicySchemaMap::const_iterator it = map->begin();
    250        it != map->end(); ++it) {
    251     base::ScopedCFTypeRef<CFStringRef> pref_name(
    252         base::SysUTF8ToCFStringRef(it->first));
    253     base::ScopedCFTypeRef<CFPropertyListRef> value(
    254         preferences_->CopyAppValue(pref_name, bundle_id));
    255     if (!value.get())
    256       continue;
    257     bool forced =
    258         preferences_->AppValueIsForced(pref_name, bundle_id);
    259     PolicyLevel level = forced ? POLICY_LEVEL_MANDATORY :
    260                                  POLICY_LEVEL_RECOMMENDED;
    261     scoped_ptr<base::Value> policy_value(CreateValueFromProperty(value));
    262     if (policy_value)
    263       policy->Set(it->first, level, POLICY_SCOPE_USER,
    264                   policy_value.release(), NULL);
    265   }
    266 }
    267 
    268 void PolicyLoaderMac::OnFileUpdated(const base::FilePath& path, bool error) {
    269   if (!error)
    270     Reload(false);
    271 }
    272 
    273 }  // namespace policy
    274