Bug 1935611 - Fix libyuv/libpng link failed for loongarch64. r=glandium,tnikkel,ng
[gecko.git] / security / manager / ssl / KeychainSecret.cpp
blob3faa5dc7107c8645b0fc0336755eac7576136d70
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "KeychainSecret.h"
9 #include <Security/Security.h>
11 #include "mozilla/Logging.h"
13 // This is the implementation of KeychainSecret, an instantiation of OSKeyStore
14 // for OS X. It uses the system keychain, hence the name.
16 using namespace mozilla;
18 LazyLogModule gKeychainSecretLog("keychainsecret");
20 KeychainSecret::KeychainSecret() {}
22 KeychainSecret::~KeychainSecret() {}
24 ScopedCFType<CFStringRef> MozillaStringToCFString(const nsACString& stringIn) {
25 // https://developer.apple.com/documentation/corefoundation/1543419-cfstringcreatewithbytes
26 ScopedCFType<CFStringRef> stringOut(CFStringCreateWithBytes(
27 nullptr, reinterpret_cast<const UInt8*>(stringIn.BeginReading()),
28 stringIn.Length(), kCFStringEncodingUTF8, false));
29 return stringOut;
32 nsresult KeychainSecret::StoreSecret(const nsACString& aSecret,
33 const nsACString& aLabel) {
34 // This creates a CFDictionary of the form:
35 // { class: generic password,
36 // account: the given label,
37 // value: the given secret }
38 // "account" is the way we differentiate different secrets.
39 // By default, secrets stored by the application (Firefox) in this way are not
40 // accessible to other applications, so we shouldn't need to worry about
41 // unauthorized access or namespace collisions. This will be the case as long
42 // as we never set the kSecAttrAccessGroup attribute on the CFDictionary. The
43 // platform enforces this restriction using the application-identifier
44 // entitlement that each application bundle should have. See
45 // https://developer.apple.com/documentation/security/1401659-secitemadd?language=objc#discussion
47 // The keychain does not overwrite secrets by default (unlike other backends
48 // like libsecret and credential manager). To be consistent, we first delete
49 // any previously-stored secrets that use the given label.
50 nsresult rv = DeleteSecret(aLabel);
51 if (NS_FAILED(rv)) {
52 MOZ_LOG(gKeychainSecretLog, LogLevel::Debug,
53 ("DeleteSecret before StoreSecret failed"));
54 return rv;
56 const CFStringRef keys[] = {kSecClass, kSecAttrAccount, kSecValueData};
57 ScopedCFType<CFStringRef> label(MozillaStringToCFString(aLabel));
58 if (!label) {
59 MOZ_LOG(gKeychainSecretLog, LogLevel::Debug,
60 ("MozillaStringToCFString failed"));
61 return NS_ERROR_FAILURE;
63 ScopedCFType<CFDataRef> secret(CFDataCreate(
64 nullptr, reinterpret_cast<const UInt8*>(aSecret.BeginReading()),
65 aSecret.Length()));
66 if (!secret) {
67 MOZ_LOG(gKeychainSecretLog, LogLevel::Debug, ("CFDataCreate failed"));
68 return NS_ERROR_FAILURE;
70 const void* values[] = {kSecClassGenericPassword, label.get(), secret.get()};
71 static_assert(std::size(keys) == std::size(values),
72 "mismatched SecItemAdd key/value array sizes");
73 ScopedCFType<CFDictionaryRef> addDictionary(CFDictionaryCreate(
74 nullptr, (const void**)&keys, (const void**)&values, std::size(keys),
75 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
76 // https://developer.apple.com/documentation/security/1401659-secitemadd
77 OSStatus osrv = SecItemAdd(addDictionary.get(), nullptr);
78 if (osrv != errSecSuccess) {
79 MOZ_LOG(gKeychainSecretLog, LogLevel::Debug,
80 ("SecItemAdd failed: %d", osrv));
81 return NS_ERROR_FAILURE;
83 return NS_OK;
86 nsresult KeychainSecret::DeleteSecret(const nsACString& aLabel) {
87 // To delete a secret, we create a CFDictionary of the form:
88 // { class: generic password,
89 // account: the given label }
90 // and then call SecItemDelete.
91 const CFStringRef keys[] = {kSecClass, kSecAttrAccount};
92 ScopedCFType<CFStringRef> label(MozillaStringToCFString(aLabel));
93 if (!label) {
94 MOZ_LOG(gKeychainSecretLog, LogLevel::Debug,
95 ("MozillaStringToCFString failed"));
96 return NS_ERROR_FAILURE;
98 const void* values[] = {kSecClassGenericPassword, label.get()};
99 static_assert(std::size(keys) == std::size(values),
100 "mismatched SecItemDelete key/value array sizes");
101 ScopedCFType<CFDictionaryRef> deleteDictionary(CFDictionaryCreate(
102 nullptr, (const void**)&keys, (const void**)&values, std::size(keys),
103 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
104 // https://developer.apple.com/documentation/security/1395547-secitemdelete
105 OSStatus rv = SecItemDelete(deleteDictionary.get());
106 if (rv != errSecSuccess && rv != errSecItemNotFound) {
107 MOZ_LOG(gKeychainSecretLog, LogLevel::Debug,
108 ("SecItemDelete failed: %d", rv));
109 return NS_ERROR_FAILURE;
111 return NS_OK;
114 nsresult KeychainSecret::RetrieveSecret(const nsACString& aLabel,
115 /* out */ nsACString& aSecret) {
116 // To retrieve a secret, we create a CFDictionary of the form:
117 // { class: generic password,
118 // account: the given label,
119 // match limit: match one,
120 // return attributes: true,
121 // return data: true }
122 // This searches for and returns the attributes and data for the secret
123 // matching the given label. We then extract the data (i.e. the secret) and
124 // return it.
125 const CFStringRef keys[] = {kSecClass, kSecAttrAccount, kSecMatchLimit,
126 kSecReturnAttributes, kSecReturnData};
127 ScopedCFType<CFStringRef> label(MozillaStringToCFString(aLabel));
128 if (!label) {
129 MOZ_LOG(gKeychainSecretLog, LogLevel::Debug,
130 ("MozillaStringToCFString failed"));
131 return NS_ERROR_FAILURE;
133 const void* values[] = {kSecClassGenericPassword, label.get(),
134 kSecMatchLimitOne, kCFBooleanTrue, kCFBooleanTrue};
135 static_assert(std::size(keys) == std::size(values),
136 "mismatched SecItemCopyMatching key/value array sizes");
137 ScopedCFType<CFDictionaryRef> searchDictionary(CFDictionaryCreate(
138 nullptr, (const void**)&keys, (const void**)&values, std::size(keys),
139 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
140 CFTypeRef item;
141 // https://developer.apple.com/documentation/security/1398306-secitemcopymatching
142 OSStatus rv = SecItemCopyMatching(searchDictionary.get(), &item);
143 if (rv != errSecSuccess) {
144 MOZ_LOG(gKeychainSecretLog, LogLevel::Debug,
145 ("SecItemCopyMatching failed: %d", rv));
146 return NS_ERROR_FAILURE;
148 ScopedCFType<CFDictionaryRef> dictionary(
149 reinterpret_cast<CFDictionaryRef>(item));
150 CFDataRef secret = reinterpret_cast<CFDataRef>(
151 CFDictionaryGetValue(dictionary.get(), kSecValueData));
152 if (!secret) {
153 MOZ_LOG(gKeychainSecretLog, LogLevel::Debug,
154 ("CFDictionaryGetValue failed"));
155 return NS_ERROR_FAILURE;
157 aSecret.Assign(reinterpret_cast<const char*>(CFDataGetBytePtr(secret)),
158 CFDataGetLength(secret));
159 return NS_OK;