1 // Copyright 2013 The Chromium Authors
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 "net/cert/nss_cert_database_chromeos.h"
6
7 #include <cert.h>
8 #include <pk11pub.h>
9
10 #include <algorithm>
11 #include <memory>
12 #include <utility>
13 #include <vector>
14
15 #include "base/functional/bind.h"
16 #include "base/functional/callback.h"
17 #include "base/location.h"
18 #include "base/task/thread_pool.h"
19 #include "base/threading/scoped_blocking_call.h"
20 #include "net/cert/nss_cert_database.h"
21
22 namespace net {
23
NSSCertDatabaseChromeOS(crypto::ScopedPK11Slot public_slot,crypto::ScopedPK11Slot private_slot)24 NSSCertDatabaseChromeOS::NSSCertDatabaseChromeOS(
25 crypto::ScopedPK11Slot public_slot,
26 crypto::ScopedPK11Slot private_slot)
27 : NSSCertDatabase(std::move(public_slot), std::move(private_slot)) {
28 // By default, don't use a system slot. Only if explicitly set by
29 // SetSystemSlot, the system slot will be used.
30 profile_filter_.Init(GetPublicSlot(),
31 GetPrivateSlot(),
32 crypto::ScopedPK11Slot() /* no system slot */);
33 }
34
35 NSSCertDatabaseChromeOS::~NSSCertDatabaseChromeOS() = default;
36
SetSystemSlot(crypto::ScopedPK11Slot system_slot)37 void NSSCertDatabaseChromeOS::SetSystemSlot(
38 crypto::ScopedPK11Slot system_slot) {
39 system_slot_ = std::move(system_slot);
40 profile_filter_.Init(GetPublicSlot(), GetPrivateSlot(), GetSystemSlot());
41 }
42
ListCerts(NSSCertDatabase::ListCertsCallback callback)43 void NSSCertDatabaseChromeOS::ListCerts(
44 NSSCertDatabase::ListCertsCallback callback) {
45 base::ThreadPool::PostTaskAndReplyWithResult(
46 FROM_HERE,
47 {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
48 base::BindOnce(&NSSCertDatabaseChromeOS::ListCertsImpl, profile_filter_),
49 std::move(callback));
50 }
51
ListCertsInfo(ListCertsInfoCallback callback,NSSRootsHandling nss_roots_handling)52 void NSSCertDatabaseChromeOS::ListCertsInfo(
53 ListCertsInfoCallback callback,
54 NSSRootsHandling nss_roots_handling) {
55 base::ThreadPool::PostTaskAndReplyWithResult(
56 FROM_HERE,
57 {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
58 base::BindOnce(&NSSCertDatabaseChromeOS::ListCertsInfoImpl,
59 profile_filter_, /*slot=*/GetSystemSlot(),
60 /*add_certs_info=*/true, nss_roots_handling),
61 std::move(callback));
62 }
63
GetSystemSlot() const64 crypto::ScopedPK11Slot NSSCertDatabaseChromeOS::GetSystemSlot() const {
65 if (system_slot_)
66 return crypto::ScopedPK11Slot(PK11_ReferenceSlot(system_slot_.get()));
67 return crypto::ScopedPK11Slot();
68 }
69
ListModules(std::vector<crypto::ScopedPK11Slot> * modules,bool need_rw) const70 void NSSCertDatabaseChromeOS::ListModules(
71 std::vector<crypto::ScopedPK11Slot>* modules,
72 bool need_rw) const {
73 NSSCertDatabase::ListModules(modules, need_rw);
74
75 const NSSProfileFilterChromeOS& profile_filter = profile_filter_;
76 std::erase_if(*modules, [&profile_filter](crypto::ScopedPK11Slot& module) {
77 return !profile_filter.IsModuleAllowed(module.get());
78 });
79 }
80
SetCertTrust(CERTCertificate * cert,CertType type,TrustBits trust_bits)81 bool NSSCertDatabaseChromeOS::SetCertTrust(CERTCertificate* cert,
82 CertType type,
83 TrustBits trust_bits) {
84 crypto::ScopedPK11Slot public_slot = GetPublicSlot();
85
86 // Ensure that the certificate exists on the public slot so NSS puts the trust
87 // settings there (https://crbug.com/1132030).
88 if (public_slot == GetSystemSlot()) {
89 // Never attempt to store trust setting on the system slot.
90 return false;
91 }
92
93 if (!IsCertificateOnSlot(cert, public_slot.get())) {
94 // Copy the certificate to the public slot.
95 SECStatus srv =
96 PK11_ImportCert(public_slot.get(), cert, CK_INVALID_HANDLE,
97 cert->nickname, PR_FALSE /* includeTrust (unused) */);
98 if (srv != SECSuccess) {
99 LOG(ERROR) << "Failed to import certificate onto public slot.";
100 return false;
101 }
102 }
103 return NSSCertDatabase::SetCertTrust(cert, type, trust_bits);
104 }
105
106 // static
ListCertsImpl(const NSSProfileFilterChromeOS & profile_filter)107 ScopedCERTCertificateList NSSCertDatabaseChromeOS::ListCertsImpl(
108 const NSSProfileFilterChromeOS& profile_filter) {
109 CertInfoList certs_info =
110 ListCertsInfoImpl(profile_filter, crypto::ScopedPK11Slot(),
111 /*add_certs_info=*/false, NSSRootsHandling::kInclude);
112
113 return ExtractCertificates(std::move(certs_info));
114 }
115
116 // static
ListCertsInfoImpl(const NSSProfileFilterChromeOS & profile_filter,crypto::ScopedPK11Slot system_slot,bool add_certs_info,NSSRootsHandling nss_roots_handling)117 NSSCertDatabase::CertInfoList NSSCertDatabaseChromeOS::ListCertsInfoImpl(
118 const NSSProfileFilterChromeOS& profile_filter,
119 crypto::ScopedPK11Slot system_slot,
120 bool add_certs_info,
121 NSSRootsHandling nss_roots_handling) {
122 // This method may acquire the NSS lock or reenter this code via extension
123 // hooks (such as smart card UI). To ensure threads are not starved or
124 // deadlocked, the base::ScopedBlockingCall below increments the thread pool
125 // capacity if this method takes too much time to run.
126 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
127 base::BlockingType::MAY_BLOCK);
128
129 CertInfoList certs_info(NSSCertDatabase::ListCertsInfoImpl(
130 crypto::ScopedPK11Slot(), add_certs_info, nss_roots_handling));
131
132 // Filter certificate information according to user profile.
133 std::erase_if(certs_info, [&profile_filter](CertInfo& cert_info) {
134 return !profile_filter.IsCertAllowed(cert_info.cert.get());
135 });
136
137 if (add_certs_info) {
138 // Add Chrome OS specific information.
139 for (auto& cert_info : certs_info) {
140 cert_info.device_wide =
141 IsCertificateOnSlot(cert_info.cert.get(), system_slot.get());
142 cert_info.hardware_backed = IsHardwareBacked(cert_info.cert.get());
143 }
144 }
145
146 return certs_info;
147 }
148
149 } // namespace net
150