enterprise_platform_keys_apitest_nss.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
1// Copyright 2014 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 <cryptohi.h>
6
7#include "base/macros.h"
8#include "base/strings/stringprintf.h"
9#include "chrome/browser/chromeos/policy/device_policy_cros_browser_test.h"
10#include "chrome/browser/extensions/extension_apitest.h"
11#include "chrome/browser/extensions/extension_service.h"
12#include "chrome/browser/net/nss_context.h"
13#include "chrome/browser/net/url_request_mock_util.h"
14#include "chromeos/chromeos_switches.h"
15#include "chromeos/login/user_names.h"
16#include "components/policy/core/browser/browser_policy_connector.h"
17#include "components/policy/core/common/mock_configuration_policy_provider.h"
18#include "components/policy/core/common/policy_map.h"
19#include "content/public/browser/notification_service.h"
20#include "content/public/common/content_switches.h"
21#include "content/public/test/test_utils.h"
22#include "content/test/net/url_request_mock_http_job.h"
23#include "crypto/nss_util_internal.h"
24#include "crypto/scoped_test_system_nss_key_slot.h"
25#include "extensions/browser/notification_types.h"
26#include "net/base/net_errors.h"
27#include "net/cert/nss_cert_database.h"
28#include "policy/policy_constants.h"
29#include "testing/gmock/include/gmock/gmock.h"
30
31namespace {
32
33// The test extension has a certificate referencing this private key which will
34// be stored in the user's token in the test setup.
35//
36// openssl genrsa > privkey.pem
37// openssl pkcs8 -inform pem -in privkey.pem -topk8
38//   -outform der -out privkey8.der -nocrypt
39// xxd -i privkey8.der
40const unsigned char privateKeyPkcs8User[] = {
41    0x30, 0x82, 0x01, 0x55, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a,
42    0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82,
43    0x01, 0x3f, 0x30, 0x82, 0x01, 0x3b, 0x02, 0x01, 0x00, 0x02, 0x41, 0x00,
44    0xc7, 0xc1, 0x4d, 0xd5, 0xdc, 0x3a, 0x2e, 0x1f, 0x42, 0x30, 0x3d, 0x21,
45    0x1e, 0xa2, 0x1f, 0x60, 0xcb, 0x71, 0x11, 0x53, 0xb0, 0x75, 0xa0, 0x62,
46    0xfe, 0x5e, 0x0a, 0xde, 0xb0, 0x0f, 0x48, 0x97, 0x5e, 0x42, 0xa7, 0x3a,
47    0xd1, 0xca, 0x4c, 0xe3, 0xdb, 0x5f, 0x31, 0xc2, 0x99, 0x08, 0x89, 0xcd,
48    0x6d, 0x20, 0xaa, 0x75, 0xe6, 0x2b, 0x98, 0xd2, 0xf3, 0x7b, 0x4b, 0xe5,
49    0x9b, 0xfe, 0xe2, 0x6d, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x40, 0x4a,
50    0xf5, 0x76, 0x10, 0xe7, 0xb8, 0x89, 0x70, 0x3f, 0x75, 0x3c, 0xab, 0x3e,
51    0x04, 0x96, 0x83, 0xcb, 0x34, 0x1d, 0xcd, 0x6a, 0xed, 0x69, 0x07, 0x5c,
52    0xee, 0xcb, 0x63, 0x6f, 0x6b, 0xfc, 0xcf, 0xee, 0xa2, 0xc4, 0x67, 0x05,
53    0x68, 0x4d, 0x21, 0x7e, 0x3e, 0xde, 0x74, 0x72, 0xf8, 0x04, 0x35, 0x66,
54    0x1e, 0x6b, 0x1d, 0xef, 0x77, 0xf7, 0x33, 0xf0, 0x35, 0xcf, 0x35, 0x6e,
55    0x53, 0x3f, 0x9d, 0x02, 0x21, 0x00, 0xee, 0x48, 0x67, 0x1b, 0x24, 0x6e,
56    0x3d, 0x7b, 0xa0, 0xc3, 0xee, 0x8a, 0x2e, 0xc7, 0xd0, 0xa1, 0xdb, 0x25,
57    0x31, 0x12, 0x99, 0x43, 0x06, 0x3c, 0xb0, 0x80, 0x35, 0x2b, 0xf4, 0xc5,
58    0xa2, 0xd3, 0x02, 0x21, 0x00, 0xd6, 0x9b, 0x8b, 0x75, 0x91, 0x52, 0xd4,
59    0xf0, 0x76, 0xcf, 0xa2, 0xbe, 0xa6, 0xaf, 0x72, 0x6c, 0x52, 0xf9, 0xc9,
60    0x0e, 0xea, 0x4a, 0x4c, 0xd2, 0xdf, 0x25, 0x70, 0xc6, 0x66, 0x35, 0x9d,
61    0xbf, 0x02, 0x21, 0x00, 0xe8, 0x9e, 0x40, 0x21, 0xcc, 0x37, 0xde, 0xc7,
62    0xd1, 0x13, 0x55, 0xcd, 0x0a, 0x8c, 0x40, 0xcd, 0xb1, 0xed, 0xa5, 0xf1,
63    0x7d, 0x33, 0x64, 0x64, 0x5c, 0xfe, 0x5c, 0x6a, 0x34, 0x03, 0xb8, 0xc7,
64    0x02, 0x20, 0x17, 0xe1, 0xb5, 0x52, 0x3e, 0xfa, 0xc5, 0xc1, 0x80, 0xa7,
65    0x38, 0x88, 0x18, 0xca, 0x7b, 0x64, 0x3c, 0x93, 0x99, 0x61, 0x34, 0x87,
66    0x52, 0x27, 0x41, 0x37, 0xcc, 0x65, 0xf7, 0xa7, 0xcd, 0xc7, 0x02, 0x21,
67    0x00, 0x8a, 0x17, 0x7f, 0xf9, 0x45, 0xf3, 0xfd, 0xf7, 0x96, 0x62, 0xf3,
68    0x7a, 0x09, 0xfb, 0xe9, 0x9e, 0xc7, 0x7a, 0x1f, 0x53, 0x1a, 0xb8, 0xd5,
69    0x88, 0x9d, 0xd4, 0x79, 0x57, 0x88, 0x68, 0x72, 0x6f};
70
71// The test extension has a certificate referencing this private key which will
72// be stored in the system token in the test setup.
73const unsigned char privateKeyPkcs8System[] = {
74    0x30, 0x82, 0x01, 0x54, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a,
75    0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82,
76    0x01, 0x3e, 0x30, 0x82, 0x01, 0x3a, 0x02, 0x01, 0x00, 0x02, 0x41, 0x00,
77    0xe8, 0xb3, 0x04, 0xb1, 0xad, 0xef, 0x6b, 0xe5, 0xbe, 0xc9, 0x05, 0x75,
78    0x07, 0x41, 0xf5, 0x70, 0x50, 0xc2, 0xe8, 0xee, 0xeb, 0x09, 0x9d, 0x49,
79    0x64, 0x4c, 0x60, 0x61, 0x80, 0xbe, 0xc5, 0x41, 0xf3, 0x8c, 0x57, 0x90,
80    0x3a, 0x44, 0x62, 0x6d, 0x51, 0xb8, 0xbb, 0xc6, 0x9a, 0x16, 0xdf, 0xf9,
81    0xce, 0xe3, 0xb8, 0x8c, 0x2e, 0xa2, 0x16, 0xc8, 0xed, 0xc7, 0xf8, 0x4f,
82    0xbd, 0xd3, 0x6e, 0x63, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x40, 0x76,
83    0xc9, 0x83, 0xf8, 0xeb, 0xd0, 0x8f, 0xa4, 0xdd, 0x4a, 0xa2, 0xe5, 0x85,
84    0xc9, 0xee, 0xef, 0xe1, 0xda, 0x4d, 0xac, 0x41, 0x01, 0x4c, 0x70, 0x7d,
85    0xa9, 0xdb, 0x7d, 0x8a, 0x8a, 0x58, 0x09, 0x04, 0x45, 0x43, 0xa4, 0xf3,
86    0xb4, 0x98, 0xf6, 0x34, 0x68, 0x5f, 0xc1, 0xc2, 0xa7, 0x86, 0x3e, 0xec,
87    0x84, 0x0b, 0x18, 0xbc, 0xb1, 0xee, 0x6f, 0x3f, 0xb1, 0x6d, 0xbc, 0x3e,
88    0xbf, 0x6d, 0x31, 0x02, 0x21, 0x00, 0xff, 0x9d, 0x90, 0x4f, 0x0e, 0xe8,
89    0x7e, 0xf3, 0x38, 0xa7, 0xec, 0x73, 0x80, 0xf9, 0x39, 0x2c, 0xaa, 0x33,
90    0x91, 0x72, 0x10, 0x7c, 0x8b, 0xc3, 0x61, 0x6d, 0x40, 0x96, 0xac, 0xb3,
91    0x5e, 0xc9, 0x02, 0x21, 0x00, 0xe9, 0x0c, 0xa1, 0x34, 0xf2, 0x43, 0x3c,
92    0x74, 0xec, 0x1a, 0xf6, 0x80, 0x8e, 0x50, 0x10, 0x6d, 0x55, 0x64, 0xce,
93    0x47, 0x4a, 0x1e, 0x34, 0x27, 0x6c, 0x49, 0x79, 0x6a, 0x23, 0xc6, 0x9d,
94    0xcb, 0x02, 0x20, 0x48, 0xda, 0xa8, 0xc1, 0xcf, 0xb6, 0xf6, 0x4f, 0xee,
95    0x4a, 0xf6, 0x3a, 0xa9, 0x7c, 0xdf, 0x0d, 0xda, 0xe8, 0xdd, 0xc0, 0x8b,
96    0xf0, 0x63, 0x89, 0x69, 0x60, 0x51, 0x33, 0x60, 0xbf, 0xb2, 0xf9, 0x02,
97    0x21, 0x00, 0xb4, 0x77, 0x81, 0x46, 0x7c, 0xec, 0x30, 0x1e, 0xe2, 0xcf,
98    0x26, 0x5f, 0xfa, 0xd4, 0x69, 0x44, 0x21, 0x42, 0x84, 0xb2, 0x93, 0xe4,
99    0xbb, 0xc2, 0x63, 0x8a, 0xaa, 0x28, 0xd5, 0x37, 0x72, 0xed, 0x02, 0x20,
100    0x16, 0xde, 0x3d, 0x57, 0xc5, 0xd5, 0x3d, 0x90, 0x8b, 0xfd, 0x90, 0x3b,
101    0xd8, 0x71, 0x69, 0x5e, 0x8d, 0xb4, 0x48, 0x1c, 0xa4, 0x01, 0xce, 0xc1,
102    0xb5, 0x6f, 0xe9, 0x1b, 0x32, 0x91, 0x34, 0x38
103};
104
105const base::FilePath::CharType kTestExtensionDir[] =
106    FILE_PATH_LITERAL("extensions/api_test/enterprise_platform_keys");
107const base::FilePath::CharType kUpdateManifestFileName[] =
108    FILE_PATH_LITERAL("update_manifest.xml");
109
110void ImportPrivateKeyPKCS8ToSlot(const unsigned char* pkcs8_der,
111                                 size_t pkcs8_der_size,
112                                 PK11SlotInfo* slot) {
113  SECItem pki_der_user = {
114      siBuffer,
115      // NSS requires non-const data even though it is just for input.
116      const_cast<unsigned char*>(pkcs8_der),
117      pkcs8_der_size};
118
119  SECKEYPrivateKey* seckey = NULL;
120  ASSERT_EQ(SECSuccess,
121            PK11_ImportDERPrivateKeyInfoAndReturnKey(slot,
122                                                     &pki_der_user,
123                                                     NULL,    // nickname
124                                                     NULL,    // publicValue
125                                                     true,    // isPerm
126                                                     true,    // isPrivate
127                                                     KU_ALL,  // usage
128                                                     &seckey,
129                                                     NULL));
130}
131
132// The managed_storage extension has a key defined in its manifest, so that
133// its extension ID is well-known and the policy system can push policies for
134// the extension.
135const char kTestExtensionID[] = "aecpbnckhoppanpmefllkdkohionpmig";
136
137class EnterprisePlatformKeysTest : public ExtensionApiTest {
138 public:
139  EnterprisePlatformKeysTest() : nss_db_(NULL) {}
140
141  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
142    ExtensionApiTest::SetUpCommandLine(command_line);
143
144    // Enable the WebCrypto API.
145    command_line->AppendSwitch(
146        switches::kEnableExperimentalWebPlatformFeatures);
147
148    command_line->AppendSwitchASCII(chromeos::switches::kLoginUser,
149                                    chromeos::login::kStubUser);
150  }
151
152  virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
153    ExtensionApiTest::SetUpInProcessBrowserTestFixture();
154
155    device_policy_test_helper_.device_policy()->policy_data().set_username(
156        chromeos::login::kStubUser);
157
158    device_policy_test_helper_.device_policy()->Build();
159    device_policy_test_helper_.MarkAsEnterpriseOwned();
160
161    EXPECT_CALL(policy_provider_, IsInitializationComplete(testing::_))
162        .WillRepeatedly(testing::Return(true));
163    policy_provider_.SetAutoRefresh();
164    policy::BrowserPolicyConnector::SetPolicyProviderForTesting(
165        &policy_provider_);
166  }
167
168  virtual void SetUpOnMainThread() OVERRIDE {
169    ExtensionApiTest::SetUpOnMainThread();
170
171    // Enable the URLRequestMock, which is required for force-installing the
172    // test extension through policy.
173    content::BrowserThread::PostTask(
174        content::BrowserThread::IO,
175        FROM_HERE,
176        base::Bind(chrome_browser_net::SetUrlRequestMocksEnabled, true));
177
178    {
179      base::RunLoop loop;
180      GetNSSCertDatabaseForProfile(
181          browser()->profile(),
182          base::Bind(&EnterprisePlatformKeysTest::DidGetCertDatabase,
183                     base::Unretained(this),
184                     loop.QuitClosure()));
185      loop.Run();
186    }
187
188    SetPolicy();
189  }
190
191  void SetUpTestSystemSlot() {
192    base::RunLoop loop;
193    content::BrowserThread::PostTask(
194        content::BrowserThread::IO,
195        FROM_HERE,
196        base::Bind(&EnterprisePlatformKeysTest::SetUpTestSystemSlotOnIO,
197                   base::Unretained(this),
198                   browser()->profile()->GetResourceContext(),
199                   loop.QuitClosure()));
200    loop.Run();
201  }
202
203  void TearDownTestSystemSlot() {
204    base::RunLoop loop;
205    content::BrowserThread::PostTask(
206        content::BrowserThread::IO,
207        FROM_HERE,
208        base::Bind(&EnterprisePlatformKeysTest::TearDownTestSystemSlotOnIO,
209                   base::Unretained(this),
210                   loop.QuitClosure()));
211    loop.Run();
212  }
213
214 private:
215  void DidGetCertDatabase(const base::Closure& done_callback,
216                          net::NSSCertDatabase* cert_db) {
217    nss_db_ = cert_db;
218
219    // In order to use a prepared certificate, import a private key to the
220    // user's token for which the Javscript test will import the certificate.
221    ImportPrivateKeyPKCS8ToSlot(privateKeyPkcs8User,
222                                arraysize(privateKeyPkcs8User),
223                                cert_db->GetPrivateSlot().get());
224    done_callback.Run();
225  }
226
227  void SetUpTestSystemSlotOnIO(content::ResourceContext* context,
228                           const base::Closure& done_callback) {
229    test_system_slot_.reset(new crypto::ScopedTestSystemNSSKeySlot());
230    ASSERT_TRUE(test_system_slot_->ConstructedSuccessfully());
231
232    // Import a private key to the system slot.  The Javascript part of this
233    // test has a prepared certificate for this key.
234    ImportPrivateKeyPKCS8ToSlot(privateKeyPkcs8System,
235                                arraysize(privateKeyPkcs8System),
236                                test_system_slot_->slot());
237
238    content::BrowserThread::PostTask(
239        content::BrowserThread::UI, FROM_HERE, done_callback);
240  }
241
242  void TearDownTestSystemSlotOnIO(const base::Closure& done_callback) {
243    test_system_slot_.reset();
244
245    content::BrowserThread::PostTask(
246        content::BrowserThread::UI, FROM_HERE, done_callback);
247  }
248
249  void SetPolicy() {
250    // Extensions that are force-installed come from an update URL, which
251    // defaults to the webstore. Use a mock URL for this test with an update
252    // manifest that includes the crx file of the test extension.
253    base::FilePath update_manifest_path =
254        base::FilePath(kTestExtensionDir).Append(kUpdateManifestFileName);
255    GURL update_manifest_url(
256        content::URLRequestMockHTTPJob::GetMockUrl(update_manifest_path));
257
258    scoped_ptr<base::ListValue> forcelist(new base::ListValue);
259    forcelist->AppendString(base::StringPrintf(
260        "%s;%s", kTestExtensionID, update_manifest_url.spec().c_str()));
261
262    policy::PolicyMap policy;
263    policy.Set(policy::key::kExtensionInstallForcelist,
264               policy::POLICY_LEVEL_MANDATORY,
265               policy::POLICY_SCOPE_MACHINE,
266               forcelist.release(),
267               NULL);
268
269    // Set the policy and wait until the extension is installed.
270    content::WindowedNotificationObserver observer(
271        extensions::NOTIFICATION_EXTENSION_WILL_BE_INSTALLED_DEPRECATED,
272        content::NotificationService::AllSources());
273    policy_provider_.UpdateChromePolicy(policy);
274    observer.Wait();
275  }
276
277  net::NSSCertDatabase* nss_db_;
278  policy::DevicePolicyCrosTestHelper device_policy_test_helper_;
279  scoped_ptr<crypto::ScopedTestSystemNSSKeySlot> test_system_slot_;
280  policy::MockConfigurationPolicyProvider policy_provider_;
281};
282
283}  // namespace
284
285IN_PROC_BROWSER_TEST_F(EnterprisePlatformKeysTest, SystemTokenEnabled) {
286  SetUpTestSystemSlot();
287  ASSERT_TRUE(RunExtensionSubtest(
288      "",
289      base::StringPrintf("chrome-extension://%s/basic.html?systemTokenEnabled",
290                         kTestExtensionID)))
291      << message_;
292  TearDownTestSystemSlot();
293}
294
295IN_PROC_BROWSER_TEST_F(EnterprisePlatformKeysTest, SystemTokenDisabled) {
296  ASSERT_TRUE(RunExtensionSubtest(
297      "",
298      base::StringPrintf("chrome-extension://%s/basic.html", kTestExtensionID)))
299      << message_;
300}
301
302// Ensure that extensions that are not pre-installed by policy throw an install
303// warning if they request the enterprise.platformKeys permission in the
304// manifest and that such extensions don't see the
305// chrome.enterprise.platformKeys namespace.
306IN_PROC_BROWSER_TEST_F(ExtensionApiTest,
307                       EnterprisePlatformKeysIsRestrictedToPolicyExtension) {
308  ASSERT_TRUE(RunExtensionSubtest("enterprise_platform_keys",
309                                  "api_not_available.html",
310                                  kFlagIgnoreManifestWarnings));
311
312  base::FilePath extension_path =
313      test_data_dir_.AppendASCII("enterprise_platform_keys");
314  ExtensionService* service = extensions::ExtensionSystem::Get(
315      profile())->extension_service();
316  const extensions::Extension* extension =
317      GetExtensionByPath(service->extensions(), extension_path);
318  ASSERT_FALSE(extension->install_warnings().empty());
319  EXPECT_EQ(
320      "'enterprise.platformKeys' is not allowed for specified install "
321      "location.",
322      extension->install_warnings()[0].message);
323}
324