1 // Copyright (c) 2013 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/test/local_policy_test_server.h" 6 7 #include <ctype.h> 8 9 #include <algorithm> 10 #include <vector> 11 12 #include "base/base_paths.h" 13 #include "base/files/file_util.h" 14 #include "base/json/json_writer.h" 15 #include "base/path_service.h" 16 #include "base/stl_util.h" 17 #include "base/strings/stringprintf.h" 18 #include "components/policy/core/common/cloud/cloud_policy_constants.h" 19 #include "crypto/rsa_private_key.h" 20 #include "net/test/python_utils.h" 21 #include "net/test/spawned_test_server/base_test_server.h" 22 23 namespace policy { 24 25 namespace { 26 27 // Filename in the temporary directory storing the policy data. 28 const base::FilePath::CharType kPolicyFileName[] = FILE_PATH_LITERAL("policy"); 29 30 // Private signing key file within the temporary directory. 31 const base::FilePath::CharType kSigningKeyFileName[] = 32 FILE_PATH_LITERAL("signing_key"); 33 34 // Private signing key signature file within the temporary directory. 35 const base::FilePath::CharType kSigningKeySignatureFileName[] = 36 FILE_PATH_LITERAL("signing_key.sig"); 37 38 // The file containing client definitions to be passed to the server. 39 const base::FilePath::CharType kClientStateFileName[] = 40 FILE_PATH_LITERAL("clients"); 41 42 // Dictionary keys for the client state file. Needs to be kept in sync with 43 // policy_testserver.py. 44 const char kClientStateKeyAllowedPolicyTypes[] = "allowed_policy_types"; 45 const char kClientStateKeyDeviceId[] = "device_id"; 46 const char kClientStateKeyDeviceToken[] = "device_token"; 47 const char kClientStateKeyMachineName[] = "machine_name"; 48 const char kClientStateKeyMachineId[] = "machine_id"; 49 50 // Checks whether a given character should be replaced when constructing a file 51 // name. To keep things simple, this is a bit over-aggressive. Needs to be kept 52 // in sync with policy_testserver.py. 53 bool IsUnsafeCharacter(char c) { 54 return !(isalnum(c) || c == '.' || c == '@' || c == '-'); 55 } 56 57 } // namespace 58 59 LocalPolicyTestServer::LocalPolicyTestServer() 60 : net::LocalTestServer(net::BaseTestServer::TYPE_HTTP, 61 net::BaseTestServer::kLocalhost, 62 base::FilePath()) { 63 CHECK(server_data_dir_.CreateUniqueTempDir()); 64 config_file_ = server_data_dir_.path().Append(kPolicyFileName); 65 } 66 67 LocalPolicyTestServer::LocalPolicyTestServer(const base::FilePath& config_file) 68 : net::LocalTestServer(net::BaseTestServer::TYPE_HTTP, 69 net::BaseTestServer::kLocalhost, 70 base::FilePath()), 71 config_file_(config_file) {} 72 73 LocalPolicyTestServer::LocalPolicyTestServer(const std::string& test_name) 74 : net::LocalTestServer(net::BaseTestServer::TYPE_HTTP, 75 net::BaseTestServer::kLocalhost, 76 base::FilePath()) { 77 // Read configuration from a file in chrome/test/data/policy. 78 base::FilePath source_root; 79 CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &source_root)); 80 config_file_ = source_root 81 .AppendASCII("chrome") 82 .AppendASCII("test") 83 .AppendASCII("data") 84 .AppendASCII("policy") 85 .AppendASCII(base::StringPrintf("policy_%s.json", test_name.c_str())); 86 } 87 88 LocalPolicyTestServer::~LocalPolicyTestServer() {} 89 90 bool LocalPolicyTestServer::SetSigningKeyAndSignature( 91 const crypto::RSAPrivateKey* key, const std::string& signature) { 92 CHECK(server_data_dir_.IsValid()); 93 94 std::vector<uint8> signing_key_bits; 95 if (!key->ExportPrivateKey(&signing_key_bits)) 96 return false; 97 98 policy_key_ = server_data_dir_.path().Append(kSigningKeyFileName); 99 int bytes_written = base::WriteFile( 100 policy_key_, 101 reinterpret_cast<const char*>(vector_as_array(&signing_key_bits)), 102 signing_key_bits.size()); 103 104 if (bytes_written != static_cast<int>(signing_key_bits.size())) 105 return false; 106 107 // Write the signature data. 108 base::FilePath signature_file = server_data_dir_.path().Append( 109 kSigningKeySignatureFileName); 110 bytes_written = base::WriteFile( 111 signature_file, 112 signature.c_str(), 113 signature.size()); 114 115 return bytes_written == static_cast<int>(signature.size()); 116 } 117 118 void LocalPolicyTestServer::RegisterClient(const std::string& dm_token, 119 const std::string& device_id) { 120 CHECK(server_data_dir_.IsValid()); 121 122 scoped_ptr<base::DictionaryValue> client_dict(new base::DictionaryValue()); 123 client_dict->SetString(kClientStateKeyDeviceId, device_id); 124 client_dict->SetString(kClientStateKeyDeviceToken, dm_token); 125 client_dict->SetString(kClientStateKeyMachineName, std::string()); 126 client_dict->SetString(kClientStateKeyMachineId, std::string()); 127 128 // Allow all policy types for now. 129 scoped_ptr<base::ListValue> types(new base::ListValue()); 130 types->AppendString(dm_protocol::kChromeDevicePolicyType); 131 types->AppendString(dm_protocol::kChromeUserPolicyType); 132 types->AppendString(dm_protocol::kChromePublicAccountPolicyType); 133 types->AppendString(dm_protocol::kChromeExtensionPolicyType); 134 135 client_dict->Set(kClientStateKeyAllowedPolicyTypes, types.release()); 136 clients_.Set(dm_token, client_dict.release()); 137 } 138 139 bool LocalPolicyTestServer::UpdatePolicy(const std::string& type, 140 const std::string& entity_id, 141 const std::string& policy) { 142 CHECK(server_data_dir_.IsValid()); 143 144 std::string selector = GetSelector(type, entity_id); 145 base::FilePath policy_file = server_data_dir_.path().AppendASCII( 146 base::StringPrintf("policy_%s.bin", selector.c_str())); 147 148 return base::WriteFile(policy_file, policy.c_str(), policy.size()) == 149 static_cast<int>(policy.size()); 150 } 151 152 bool LocalPolicyTestServer::UpdatePolicyData(const std::string& type, 153 const std::string& entity_id, 154 const std::string& data) { 155 CHECK(server_data_dir_.IsValid()); 156 157 std::string selector = GetSelector(type, entity_id); 158 base::FilePath data_file = server_data_dir_.path().AppendASCII( 159 base::StringPrintf("policy_%s.data", selector.c_str())); 160 161 return base::WriteFile(data_file, data.c_str(), data.size()) == 162 static_cast<int>(data.size()); 163 } 164 165 GURL LocalPolicyTestServer::GetServiceURL() const { 166 return GetURL("device_management"); 167 } 168 169 bool LocalPolicyTestServer::SetPythonPath() const { 170 if (!net::LocalTestServer::SetPythonPath()) 171 return false; 172 173 // Add the net/tools/testserver directory to the path. 174 base::FilePath net_testserver_path; 175 if (!LocalTestServer::GetTestServerPath(&net_testserver_path)) { 176 LOG(ERROR) << "Failed to get net testserver path."; 177 return false; 178 } 179 AppendToPythonPath(net_testserver_path.DirName()); 180 181 // We need protobuf python bindings. 182 base::FilePath third_party_dir; 183 if (!PathService::Get(base::DIR_SOURCE_ROOT, &third_party_dir)) { 184 LOG(ERROR) << "Failed to get DIR_SOURCE_ROOT"; 185 return false; 186 } 187 AppendToPythonPath(third_party_dir 188 .AppendASCII("third_party") 189 .AppendASCII("protobuf") 190 .AppendASCII("python")); 191 192 // Add the generated python protocol buffer bindings. 193 base::FilePath pyproto_dir; 194 if (!GetPyProtoPath(&pyproto_dir)) { 195 LOG(ERROR) << "Cannot find pyproto dir for generated code."; 196 return false; 197 } 198 199 AppendToPythonPath(pyproto_dir 200 .AppendASCII("chrome") 201 .AppendASCII("browser") 202 .AppendASCII("policy") 203 .AppendASCII("proto") 204 .AppendASCII("cloud")); 205 AppendToPythonPath(pyproto_dir 206 .AppendASCII("policy") 207 .AppendASCII("proto")); 208 #if defined(OS_CHROMEOS) 209 AppendToPythonPath(pyproto_dir 210 .AppendASCII("chrome") 211 .AppendASCII("browser") 212 .AppendASCII("policy") 213 .AppendASCII("proto") 214 .AppendASCII("chromeos")); 215 #endif 216 217 return true; 218 } 219 220 bool LocalPolicyTestServer::GetTestServerPath( 221 base::FilePath* testserver_path) const { 222 base::FilePath source_root; 223 if (!PathService::Get(base::DIR_SOURCE_ROOT, &source_root)) { 224 LOG(ERROR) << "Failed to get DIR_SOURCE_ROOT"; 225 return false; 226 } 227 *testserver_path = source_root 228 .AppendASCII("chrome") 229 .AppendASCII("browser") 230 .AppendASCII("policy") 231 .AppendASCII("test") 232 .AppendASCII("policy_testserver.py"); 233 return true; 234 } 235 236 bool LocalPolicyTestServer::GenerateAdditionalArguments( 237 base::DictionaryValue* arguments) const { 238 if (!net::LocalTestServer::GenerateAdditionalArguments(arguments)) 239 return false; 240 241 arguments->SetString("config-file", config_file_.AsUTF8Unsafe()); 242 if (!policy_key_.empty()) 243 arguments->SetString("policy-key", policy_key_.AsUTF8Unsafe()); 244 if (server_data_dir_.IsValid()) { 245 arguments->SetString("data-dir", server_data_dir_.path().AsUTF8Unsafe()); 246 247 if (!clients_.empty()) { 248 std::string json; 249 base::JSONWriter::Write(&clients_, &json); 250 base::FilePath client_state_file = 251 server_data_dir_.path().Append(kClientStateFileName); 252 if (base::WriteFile(client_state_file, json.c_str(), json.size()) != 253 static_cast<int>(json.size())) { 254 return false; 255 } 256 arguments->SetString("client-state", client_state_file.AsUTF8Unsafe()); 257 } 258 } 259 260 return true; 261 } 262 263 std::string LocalPolicyTestServer::GetSelector(const std::string& type, 264 const std::string& entity_id) { 265 std::string selector = type; 266 if (!entity_id.empty()) 267 selector = base::StringPrintf("%s/%s", type.c_str(), entity_id.c_str()); 268 std::replace_if(selector.begin(), selector.end(), IsUnsafeCharacter, '_'); 269 return selector; 270 } 271 272 } // namespace policy 273