keychain_apple.mm 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. /******************************************************************************
  2. * Copyright (C) 2016 Mathias Hasselmann <mathias.hasselmann@kdab.com> *
  3. * *
  4. * This program is distributed in the hope that it will be useful, but *
  5. * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
  6. * or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution *
  7. * details, check the accompanying file 'COPYING'. *
  8. *****************************************************************************/
  9. #include "keychain_p.h"
  10. #import <Foundation/Foundation.h>
  11. #import <Security/Security.h>
  12. using namespace QKeychain;
  13. struct ErrorDescription
  14. {
  15. QKeychain::Error code;
  16. QString message;
  17. ErrorDescription(QKeychain::Error code, const QString &message)
  18. : code(code), message(message) {}
  19. static ErrorDescription fromStatus(OSStatus status)
  20. {
  21. switch(status) {
  22. case errSecSuccess:
  23. return ErrorDescription(QKeychain::NoError, Job::tr("No error"));
  24. case errSecItemNotFound:
  25. return ErrorDescription(QKeychain::EntryNotFound, Job::tr("The specified item could not be found in the keychain"));
  26. case errSecUserCanceled:
  27. return ErrorDescription(QKeychain::AccessDeniedByUser, Job::tr("User canceled the operation"));
  28. case errSecInteractionNotAllowed:
  29. return ErrorDescription(QKeychain::AccessDenied, Job::tr("User interaction is not allowed"));
  30. case errSecNotAvailable:
  31. return ErrorDescription(QKeychain::AccessDenied, Job::tr("No keychain is available. You may need to restart your computer"));
  32. case errSecAuthFailed:
  33. return ErrorDescription(QKeychain::AccessDenied, Job::tr("The user name or passphrase you entered is not correct"));
  34. case errSecVerifyFailed:
  35. return ErrorDescription(QKeychain::AccessDenied, Job::tr("A cryptographic verification failure has occurred"));
  36. case errSecUnimplemented:
  37. return ErrorDescription(QKeychain::NotImplemented, Job::tr("Function or operation not implemented"));
  38. case errSecIO:
  39. return ErrorDescription(QKeychain::OtherError, Job::tr("I/O error"));
  40. case errSecOpWr:
  41. return ErrorDescription(QKeychain::OtherError, Job::tr("Already open with with write permission"));
  42. case errSecParam:
  43. return ErrorDescription(QKeychain::OtherError, Job::tr("Invalid parameters passed to a function"));
  44. case errSecAllocate:
  45. return ErrorDescription(QKeychain::OtherError, Job::tr("Failed to allocate memory"));
  46. case errSecBadReq:
  47. return ErrorDescription(QKeychain::OtherError, Job::tr("Bad parameter or invalid state for operation"));
  48. case errSecInternalComponent:
  49. return ErrorDescription(QKeychain::OtherError, Job::tr("An internal component failed"));
  50. case errSecDuplicateItem:
  51. return ErrorDescription(QKeychain::OtherError, Job::tr("The specified item already exists in the keychain"));
  52. case errSecDecode:
  53. return ErrorDescription(QKeychain::OtherError, Job::tr("Unable to decode the provided data"));
  54. }
  55. return ErrorDescription(QKeychain::OtherError, Job::tr("Unknown error"));
  56. }
  57. };
  58. void ReadPasswordJobPrivate::scheduledStart()
  59. {
  60. NSDictionary *const query = @{
  61. (__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword,
  62. (__bridge id) kSecAttrService: (__bridge NSString *) service.toCFString(),
  63. (__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(),
  64. (__bridge id) kSecReturnData: @YES,
  65. };
  66. CFTypeRef dataRef = nil;
  67. const OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef) query, &dataRef);
  68. data.clear();
  69. mode = Binary;
  70. if (status == errSecSuccess) {
  71. if (dataRef)
  72. data = QByteArray::fromCFData((CFDataRef) dataRef);
  73. q->emitFinished();
  74. } else {
  75. const ErrorDescription error = ErrorDescription::fromStatus(status);
  76. q->emitFinishedWithError(error.code, Job::tr("Could not retrieve private key from keystore: %1").arg(error.message));
  77. }
  78. if (dataRef)
  79. [dataRef release];
  80. }
  81. void WritePasswordJobPrivate::scheduledStart()
  82. {
  83. NSDictionary *const query = @{
  84. (__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword,
  85. (__bridge id) kSecAttrService: (__bridge NSString *) service.toCFString(),
  86. (__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(),
  87. };
  88. OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef) query, nil);
  89. if (status == errSecSuccess) {
  90. NSDictionary *const update = @{
  91. (__bridge id) kSecValueData: (__bridge NSData *) data.toCFData(),
  92. };
  93. status = SecItemUpdate((__bridge CFDictionaryRef) query, (__bridge CFDictionaryRef) update);
  94. } else {
  95. NSDictionary *const insert = @{
  96. (__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword,
  97. (__bridge id) kSecAttrService: (__bridge NSString *) service.toCFString(),
  98. (__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(),
  99. (__bridge id) kSecValueData: (__bridge NSData *) data.toCFData(),
  100. };
  101. status = SecItemAdd((__bridge CFDictionaryRef) insert, nil);
  102. }
  103. if (status == errSecSuccess) {
  104. q->emitFinished();
  105. } else {
  106. const ErrorDescription error = ErrorDescription::fromStatus(status);
  107. q->emitFinishedWithError(error.code, tr("Could not store data in settings: %1").arg(error.message));
  108. }
  109. }
  110. void DeletePasswordJobPrivate::scheduledStart()
  111. {
  112. const NSDictionary *const query = @{
  113. (__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword,
  114. (__bridge id) kSecAttrService: (__bridge NSString *) service.toCFString(),
  115. (__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(),
  116. };
  117. const OSStatus status = SecItemDelete((__bridge CFDictionaryRef) query);
  118. if (status == errSecSuccess) {
  119. q->emitFinished();
  120. } else {
  121. const ErrorDescription error = ErrorDescription::fromStatus(status);
  122. q->emitFinishedWithError(error.code, Job::tr("Could not remove private key from keystore: %1").arg(error.message));
  123. }
  124. }