#include "net/cert/cert_database.h"
#include <Security/Security.h>
#include "base/check.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/mac/mac_logging.h"
#include "base/memory/raw_ptr.h"
#include "base/notreached.h"
#include "base/process/process_handle.h"
#include "base/synchronization/lock.h"
#include "base/task/current_thread.h"
#include "base/task/single_thread_task_runner.h"
#include "crypto/mac_security_services_lock.h"
#include "net/base/net_errors.h"
#include "net/cert/x509_certificate.h"
namespace net {
class CertDatabase::Notifier {
public:
Notifier(CertDatabase* cert_db,
scoped_refptr<base::SingleThreadTaskRunner> task_runner)
: cert_db_(cert_db), task_runner_(std::move(task_runner)) {
DCHECK(base::CurrentUIThread::IsSet());
DCHECK(task_runner_->BelongsToCurrentThread());
task_runner_->PostTask(
FROM_HERE, base::BindOnce(&Notifier::Init, base::Unretained(this)));
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
~Notifier() {
DCHECK(called_shutdown_);
if (registered_ && task_runner_->RunsTasksInCurrentSequence())
SecKeychainRemoveCallback(&Notifier::KeychainCallback);
}
#pragma clang diagnostic pop
void Shutdown() {
called_shutdown_ = true;
if (!task_runner_->DeleteSoon(FROM_HERE, this)) {
delete this;
}
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
private:
void Init() {
SecKeychainEventMask event_mask =
kSecKeychainListChangedMask | kSecTrustSettingsChangedEventMask;
OSStatus status = SecKeychainAddCallback(&Notifier::KeychainCallback,
event_mask, this);
if (status == noErr)
registered_ = true;
}
#pragma clang diagnostic pop
static OSStatus KeychainCallback(SecKeychainEvent keychain_event,
SecKeychainCallbackInfo* info,
void* context);
const raw_ptr<CertDatabase> cert_db_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
bool registered_ = false;
bool called_shutdown_ = false;
};
OSStatus CertDatabase::Notifier::KeychainCallback(
SecKeychainEvent keychain_event,
SecKeychainCallbackInfo* info,
void* context) {
Notifier* that = reinterpret_cast<Notifier*>(context);
if (info->version > SEC_KEYCHAIN_SETTINGS_VERS1) {
NOTREACHED();
return errSecWrongSecVersion;
}
if (info->pid == base::GetCurrentProcId()) {
return errSecSuccess;
}
switch (keychain_event) {
case kSecKeychainListChangedEvent:
case kSecTrustSettingsChangedEvent:
that->cert_db_->NotifyObserversCertDBChanged();
break;
default:
break;
}
return errSecSuccess;
}
void CertDatabase::StartListeningForKeychainEvents() {
ReleaseNotifier();
notifier_ =
new Notifier(this, base::SingleThreadTaskRunner::GetCurrentDefault());
}
void CertDatabase::ReleaseNotifier() {
if (notifier_) {
notifier_->Shutdown();
notifier_ = nullptr;
}
}
}