1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
|
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ash/arc/net/cert_manager_impl.h"
#include <pk11priv.h>
#include <pk11pub.h>
#include <optional>
#include <utility>
#include "base/strings/string_number_conversions.h"
#include "base/task/bind_post_task.h"
#include "base/task/sequenced_task_runner.h"
#include "chrome/browser/net/nss_service.h"
#include "chrome/browser/net/nss_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "components/device_event_log/device_event_log.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "crypto/nss_key_util.h"
#include "net/cert/nss_cert_database.h"
#include "net/cert/scoped_nss_types.h"
#include "net/cert/x509_util_nss.h"
#include "third_party/boringssl/src/pki/pem.h"
namespace {
void GetCertDBOnIOThread(
NssCertDatabaseGetter database_getter,
base::OnceCallback<void(net::NSSCertDatabase*)> callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
auto split_callback = base::SplitOnceCallback(std::move(callback));
net::NSSCertDatabase* cert_db =
std::move(database_getter).Run(std::move(split_callback.first));
// If the NSS database was already available, |cert_db| is non-null and
// the callback has not been called. Explicitly call the callback.
if (cert_db) {
std::move(split_callback.second).Run(cert_db);
}
}
net::ScopedCERTCertificate TranslatePEMToCert(const std::string& cert_pem) {
bssl::PEMTokenizer tokenizer(cert_pem, {arc::kCertificatePEMHeader});
if (!tokenizer.GetNext()) {
NET_LOG(ERROR) << "Failed to get certificate data";
return nullptr;
}
std::vector<uint8_t> cert_der(tokenizer.data().begin(),
tokenizer.data().end());
return net::x509_util::CreateCERTCertificateFromBytes(cert_der);
}
// Check the status of delete operation for cert and key, then runs |callback|.
void DeleteCertAndKeyCallback(arc::CertManagerImpl::DeleteCertCallback callback,
bool status) {
if (!status) {
NET_LOG(ERROR) << "Failed to delete certificate";
}
// Run the callback regardless of the certificate and key deletion status.
std::move(callback).Run();
}
} // namespace
namespace arc {
CertManagerImpl::CertManagerImpl(Profile* profile) : profile_(profile) {}
CertManagerImpl::~CertManagerImpl() = default;
std::string CertManagerImpl::ImportPrivateKey(const std::string& key_pem,
net::NSSCertDatabase* database) {
if (!database) {
NET_LOG(ERROR) << "Certificate database is not initialized";
return std::string();
}
bssl::PEMTokenizer tokenizer(key_pem, {kPrivateKeyPEMHeader});
if (!tokenizer.GetNext()) {
NET_LOG(ERROR) << "Failed to get private key data";
return std::string();
}
std::vector<uint8_t> key_der(tokenizer.data().begin(),
tokenizer.data().end());
crypto::ScopedPK11Slot private_slot = database->GetPrivateSlot();
if (!private_slot) {
NET_LOG(ERROR) << "Failed to get PK11 slot";
return std::string();
}
crypto::ScopedSECKEYPrivateKey key(crypto::ImportNSSKeyFromPrivateKeyInfo(
private_slot.get(), key_der, true /* permanent */));
if (!key) {
NET_LOG(ERROR) << "Failed to import private key";
return std::string();
}
crypto::ScopedSECItem sec_item(PK11_GetLowLevelKeyIDForPrivateKey(key.get()));
if (!sec_item) {
NET_LOG(ERROR) << "Failed to get private key ID";
return std::string();
}
return base::HexEncode(sec_item->data, sec_item->len);
}
std::string CertManagerImpl::ImportUserCert(const std::string& cert_pem,
net::NSSCertDatabase* database) {
if (!database) {
NET_LOG(ERROR) << "Certificate database is not initialized";
return std::string();
}
net::ScopedCERTCertificate cert = TranslatePEMToCert(cert_pem);
if (!cert) {
NET_LOG(ERROR) << "Failed to translate PEM to certificate object";
return std::string();
}
int status = database->ImportUserCert(cert.get());
if (status != net::OK) {
NET_LOG(ERROR) << "Failed to import user certificate with status code "
<< status;
return std::string();
}
crypto::ScopedSECItem sec_item(
PK11_GetLowLevelKeyIDForCert(nullptr, cert.get(), nullptr));
if (!sec_item) {
NET_LOG(ERROR) << "Failed to get certificate ID";
return std::string();
}
return base::HexEncode(sec_item->data, sec_item->len);
}
void CertManagerImpl::DeleteCertAndKeyAsync(const std::string& cert_pem,
net::NSSCertDatabase* database,
DeleteCertCallback callback) {
if (!database) {
NET_LOG(ERROR) << "Certificate database is not initialized";
return;
}
net::ScopedCERTCertificate cert = TranslatePEMToCert(cert_pem);
if (!cert) {
NET_LOG(ERROR) << "Failed to translate PEM to certificate object";
return;
}
database->DeleteCertAndKeyAsync(
std::move(cert),
base::BindOnce(&DeleteCertAndKeyCallback, std::move(callback)));
}
int CertManagerImpl::GetSlotID(net::NSSCertDatabase* database) {
if (!database) {
NET_LOG(ERROR) << "Certificate database is not initialized";
return -1;
}
crypto::ScopedPK11Slot private_slot = database->GetPrivateSlot();
if (!private_slot) {
NET_LOG(ERROR) << "Failed to get PK11 slot";
return -1;
}
return PK11_GetSlotID(private_slot.get());
}
void CertManagerImpl::DeleteAndImportPrivateKeyAndCertWithDB(
const std::string& key_pem,
const std::string& cert_pem,
ImportPrivateKeyAndCertCallback callback,
net::NSSCertDatabase* database) {
// Attempt to delete the key and certificate first. This is important for an
// edge case below.
// 1. The user removes a set of passpoint credentials. This causes shill to
// delete the key and certificate from chaps.
// 2. The user re-adds passpoint credentials with the same key and
// certificate.
// If there is no Chrome restart between (1) and (2), NSS caches are not
// updated with the result of (1), making (2) fail.
// Deleting the key from NSS ensures that (2) succeeds even in this case.
DeleteCertAndKeyAsync(
cert_pem, database,
base::BindOnce(&CertManagerImpl::ImportPrivateKeyAndCertWithDB,
weak_factory_.GetWeakPtr(), key_pem, cert_pem,
std::move(callback), database));
}
void CertManagerImpl::ImportPrivateKeyAndCertWithDB(
const std::string& key_pem,
const std::string& cert_pem,
ImportPrivateKeyAndCertCallback callback,
net::NSSCertDatabase* database) {
std::string key_id = ImportPrivateKey(key_pem, database);
if (key_id.empty()) {
NET_LOG(ERROR) << "Failed to import private key";
std::move(callback).Run(/*cert_id=*/std::nullopt,
/*slot_id=*/std::nullopt);
return;
}
// Both DeleteCertAndKey parse the passed certificate into a CERTCertificate.
// This is unfortunate but reusing the same CERTCertificate resulted in
// PK11_GetLowLevelKeyIDForCert failing after the DeleteCertAndKey call.
std::string cert_id = ImportUserCert(cert_pem, database);
if (cert_id.empty()) {
NET_LOG(ERROR) << "Failed to import client certificate";
std::move(callback).Run(/*cert_id=*/std::nullopt,
/*slot_id=*/std::nullopt);
return;
}
int slot_id = GetSlotID(database);
// The ID of imported user certificate and private key is the same, use one
// of them.
DCHECK(key_id == cert_id);
std::move(callback).Run(cert_id, slot_id);
}
void CertManagerImpl::ImportPrivateKeyAndCert(
const std::string& key_pem,
const std::string& cert_pem,
ImportPrivateKeyAndCertCallback callback) {
content::GetIOThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(
&GetCertDBOnIOThread,
NssServiceFactory::GetForContext(profile_)
->CreateNSSCertDatabaseGetterForIOThread(),
base::BindPostTaskToCurrentDefault(base::BindOnce(
&CertManagerImpl::DeleteAndImportPrivateKeyAndCertWithDB,
weak_factory_.GetWeakPtr(), key_pem, cert_pem,
std::move(callback)))));
}
} // namespace arc
|