|
| 1 | +#include <Security/Security.h> |
1 | 2 | #include "keytar.h"
|
| 3 | +#include "credentials.h" |
2 | 4 |
|
3 |
| -#include <Security/Security.h> |
4 | 5 |
|
5 | 6 | namespace keytar {
|
6 | 7 |
|
| 8 | +/** |
| 9 | + * Converts a CFString to a std::string |
| 10 | + * |
| 11 | + * This either uses CFStringGetCStringPtr or (if that fails) |
| 12 | + * CFStringGetCString, trying to be as efficient as possible. |
| 13 | + */ |
| 14 | +const std::string CFStringToStdString(CFStringRef cfstring) { |
| 15 | + const char* cstr = CFStringGetCStringPtr(cfstring, kCFStringEncodingUTF8); |
| 16 | + |
| 17 | + if (cstr != NULL) { |
| 18 | + return std::string(cstr); |
| 19 | + } |
| 20 | + |
| 21 | + CFIndex length = CFStringGetLength(cfstring); |
| 22 | + // Worst case: 2 bytes per character + NUL |
| 23 | + CFIndex cstrPtrLen = length * 2 + 1; |
| 24 | + char* cstrPtr = static_cast<char*>(malloc(cstrPtrLen)); |
| 25 | + |
| 26 | + Boolean result = CFStringGetCString(cfstring, |
| 27 | + cstrPtr, |
| 28 | + cstrPtrLen, |
| 29 | + kCFStringEncodingUTF8); |
| 30 | + |
| 31 | + std::string stdstring; |
| 32 | + if (result) { |
| 33 | + stdstring = std::string(cstrPtr); |
| 34 | + } |
| 35 | + |
| 36 | + free(cstrPtr); |
| 37 | + |
| 38 | + return stdstring; |
| 39 | +} |
| 40 | + |
7 | 41 | const std::string errorStatusToString(OSStatus status) {
|
8 | 42 | std::string errorStr;
|
9 | 43 | CFStringRef errorMessageString = SecCopyErrorMessageString(status, NULL);
|
@@ -149,4 +183,97 @@ KEYTAR_OP_RESULT FindPassword(const std::string& service,
|
149 | 183 | return SUCCESS;
|
150 | 184 | }
|
151 | 185 |
|
| 186 | +Credentials getCredentialsForItem(CFDictionaryRef item) { |
| 187 | + CFStringRef service = (CFStringRef) CFDictionaryGetValue(item, |
| 188 | + kSecAttrService); |
| 189 | + CFStringRef account = (CFStringRef) CFDictionaryGetValue(item, |
| 190 | + kSecAttrAccount); |
| 191 | + |
| 192 | + CFMutableDictionaryRef query = CFDictionaryCreateMutable( |
| 193 | + NULL, |
| 194 | + 0, |
| 195 | + &kCFTypeDictionaryKeyCallBacks, |
| 196 | + &kCFTypeDictionaryValueCallBacks); |
| 197 | + |
| 198 | + CFDictionaryAddValue(query, kSecAttrService, service); |
| 199 | + CFDictionaryAddValue(query, kSecClass, kSecClassGenericPassword); |
| 200 | + CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne); |
| 201 | + CFDictionaryAddValue(query, kSecReturnAttributes, kCFBooleanTrue); |
| 202 | + CFDictionaryAddValue(query, kSecReturnData, kCFBooleanTrue); |
| 203 | + CFDictionaryAddValue(query, kSecAttrAccount, account); |
| 204 | + |
| 205 | + CFTypeRef result; |
| 206 | + OSStatus status = SecItemCopyMatching((CFDictionaryRef) query, &result); |
| 207 | + |
| 208 | + if (status == errSecSuccess) { |
| 209 | + CFDataRef passwordData = (CFDataRef) CFDictionaryGetValue( |
| 210 | + (CFDictionaryRef) result, |
| 211 | + CFSTR("v_Data")); |
| 212 | + CFStringRef password = CFStringCreateFromExternalRepresentation( |
| 213 | + NULL, |
| 214 | + passwordData, |
| 215 | + kCFStringEncodingUTF8); |
| 216 | + |
| 217 | + Credentials cred = Credentials( |
| 218 | + CFStringToStdString(account), |
| 219 | + CFStringToStdString(password)); |
| 220 | + CFRelease(password); |
| 221 | + |
| 222 | + return cred; |
| 223 | + } |
| 224 | + |
| 225 | + return Credentials(); |
| 226 | +} |
| 227 | + |
| 228 | +KEYTAR_OP_RESULT FindCredentials(const std::string& service, |
| 229 | + std::vector<Credentials>* credentials, |
| 230 | + std::string* error) { |
| 231 | + CFStringRef serviceStr = CFStringCreateWithCString( |
| 232 | + NULL, |
| 233 | + service.c_str(), |
| 234 | + kCFStringEncodingUTF8); |
| 235 | + |
| 236 | + CFMutableDictionaryRef query = CFDictionaryCreateMutable( |
| 237 | + NULL, |
| 238 | + 0, |
| 239 | + &kCFTypeDictionaryKeyCallBacks, |
| 240 | + &kCFTypeDictionaryValueCallBacks); |
| 241 | + CFDictionaryAddValue(query, kSecClass, kSecClassGenericPassword); |
| 242 | + CFDictionaryAddValue(query, kSecAttrService, serviceStr); |
| 243 | + CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitAll); |
| 244 | + CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue); |
| 245 | + CFDictionaryAddValue(query, kSecReturnAttributes, kCFBooleanTrue); |
| 246 | + |
| 247 | + CFTypeRef result; |
| 248 | + OSStatus status = SecItemCopyMatching((CFDictionaryRef) query, &result); |
| 249 | + |
| 250 | + if (status == errSecSuccess) { |
| 251 | + CFArrayRef resultArray = (CFArrayRef) result; |
| 252 | + int resultCount = CFArrayGetCount(resultArray); |
| 253 | + |
| 254 | + for (int idx = 0; idx < resultCount; idx++) { |
| 255 | + CFDictionaryRef item = (CFDictionaryRef) CFArrayGetValueAtIndex( |
| 256 | + resultArray, |
| 257 | + idx); |
| 258 | + |
| 259 | + Credentials cred = getCredentialsForItem(item); |
| 260 | + credentials->push_back(cred); |
| 261 | + } |
| 262 | + } else if (status == errSecItemNotFound) { |
| 263 | + return FAIL_NONFATAL; |
| 264 | + } else { |
| 265 | + *error = errorStatusToString(status); |
| 266 | + return FAIL_ERROR; |
| 267 | + } |
| 268 | + |
| 269 | + |
| 270 | + if (result != NULL) { |
| 271 | + CFRelease(result); |
| 272 | + } |
| 273 | + |
| 274 | + CFRelease(query); |
| 275 | + |
| 276 | + return SUCCESS; |
| 277 | +} |
| 278 | + |
152 | 279 | } // namespace keytar
|
0 commit comments