keychain_win.cpp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. /******************************************************************************
  2. * Copyright (C) 2011-2015 Frank Osterfeld <frank.osterfeld@gmail.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. #include "plaintextstore_p.h"
  11. #include <windows.h>
  12. #include <wincrypt.h>
  13. #include <memory>
  14. using namespace QKeychain;
  15. #if defined(USE_CREDENTIAL_STORE)
  16. #include <wincred.h>
  17. void ReadPasswordJobPrivate::scheduledStart() {
  18. LPCWSTR name = (LPCWSTR)key.utf16();
  19. PCREDENTIALW cred;
  20. if (!CredReadW(name, CRED_TYPE_GENERIC, 0, &cred)) {
  21. Error err;
  22. QString msg;
  23. switch(GetLastError()) {
  24. case ERROR_NOT_FOUND:
  25. err = EntryNotFound;
  26. msg = tr("Password entry not found");
  27. break;
  28. default:
  29. err = OtherError;
  30. msg = tr("Could not decrypt data");
  31. break;
  32. }
  33. q->emitFinishedWithError( err, msg );
  34. return;
  35. }
  36. data = QByteArray((char*)cred->CredentialBlob, cred->CredentialBlobSize);
  37. CredFree(cred);
  38. q->emitFinished();
  39. }
  40. void WritePasswordJobPrivate::scheduledStart() {
  41. CREDENTIALW cred;
  42. char *pwd = data.data();
  43. LPWSTR name = (LPWSTR)key.utf16();
  44. memset(&cred, 0, sizeof(cred));
  45. cred.Comment = const_cast<wchar_t*>(L"QtKeychain");
  46. cred.Type = CRED_TYPE_GENERIC;
  47. cred.TargetName = name;
  48. cred.CredentialBlobSize = data.size();
  49. cred.CredentialBlob = (LPBYTE)pwd;
  50. cred.Persist = CRED_PERSIST_ENTERPRISE;
  51. if (CredWriteW(&cred, 0)) {
  52. q->emitFinished();
  53. return;
  54. }
  55. DWORD err = GetLastError();
  56. // Detect size-exceeded errors and provide nicer messages.
  57. // Unfortunately these error codes aren't documented.
  58. // Found empirically on Win10 1803 build 17134.523.
  59. if (err == RPC_X_BAD_STUB_DATA) {
  60. const size_t maxBlob = CRED_MAX_CREDENTIAL_BLOB_SIZE;
  61. if (cred.CredentialBlobSize > maxBlob) {
  62. q->emitFinishedWithError(
  63. OtherError,
  64. tr("Credential size exceeds maximum size of %1").arg(maxBlob));
  65. return;
  66. }
  67. }
  68. if (err == RPC_S_INVALID_BOUND) {
  69. const size_t maxTargetName = CRED_MAX_GENERIC_TARGET_NAME_LENGTH;
  70. if (key.size() > maxTargetName) {
  71. q->emitFinishedWithError(
  72. OtherError,
  73. tr("Credential key exceeds maximum size of %1").arg(maxTargetName));
  74. return;
  75. }
  76. }
  77. q->emitFinishedWithError( OtherError, tr("Writing credentials failed: Win32 error code %1").arg(err) );
  78. }
  79. void DeletePasswordJobPrivate::scheduledStart() {
  80. LPCWSTR name = (LPCWSTR)key.utf16();
  81. if (!CredDeleteW(name, CRED_TYPE_GENERIC, 0)) {
  82. Error err;
  83. QString msg;
  84. switch(GetLastError()) {
  85. case ERROR_NOT_FOUND:
  86. err = EntryNotFound;
  87. msg = tr("Password entry not found");
  88. break;
  89. default:
  90. err = OtherError;
  91. msg = tr("Could not decrypt data");
  92. break;
  93. }
  94. q->emitFinishedWithError( err, msg );
  95. } else {
  96. q->emitFinished();
  97. }
  98. }
  99. #else
  100. void ReadPasswordJobPrivate::scheduledStart() {
  101. PlainTextStore plainTextStore( q->service(), q->settings() );
  102. QByteArray encrypted = plainTextStore.readData( key );
  103. if ( plainTextStore.error() != NoError ) {
  104. q->emitFinishedWithError( plainTextStore.error(), plainTextStore.errorString() );
  105. return;
  106. }
  107. DATA_BLOB blob_in, blob_out;
  108. blob_in.pbData = reinterpret_cast<BYTE*>( encrypted.data() );
  109. blob_in.cbData = encrypted.size();
  110. const BOOL ret = CryptUnprotectData( &blob_in,
  111. nullptr,
  112. nullptr,
  113. nullptr,
  114. nullptr,
  115. 0,
  116. &blob_out );
  117. if ( !ret ) {
  118. q->emitFinishedWithError( OtherError, tr("Could not decrypt data") );
  119. return;
  120. }
  121. data = QByteArray( reinterpret_cast<char*>( blob_out.pbData ), blob_out.cbData );
  122. SecureZeroMemory( blob_out.pbData, blob_out.cbData );
  123. LocalFree( blob_out.pbData );
  124. q->emitFinished();
  125. }
  126. void WritePasswordJobPrivate::scheduledStart() {
  127. DATA_BLOB blob_in, blob_out;
  128. blob_in.pbData = reinterpret_cast<BYTE*>( data.data() );
  129. blob_in.cbData = data.size();
  130. const BOOL res = CryptProtectData( &blob_in,
  131. L"QKeychain-encrypted data",
  132. nullptr,
  133. nullptr,
  134. nullptr,
  135. 0,
  136. &blob_out );
  137. if ( !res ) {
  138. q->emitFinishedWithError( OtherError, tr("Encryption failed") ); //TODO more details available?
  139. return;
  140. }
  141. const QByteArray encrypted( reinterpret_cast<char*>( blob_out.pbData ), blob_out.cbData );
  142. LocalFree( blob_out.pbData );
  143. PlainTextStore plainTextStore( q->service(), q->settings() );
  144. plainTextStore.write( key, encrypted, Binary );
  145. if ( plainTextStore.error() != NoError ) {
  146. q->emitFinishedWithError( plainTextStore.error(), plainTextStore.errorString() );
  147. return;
  148. }
  149. q->emitFinished();
  150. }
  151. void DeletePasswordJobPrivate::scheduledStart() {
  152. PlainTextStore plainTextStore( q->service(), q->settings() );
  153. plainTextStore.remove( key );
  154. if ( plainTextStore.error() != NoError ) {
  155. q->emitFinishedWithError( plainTextStore.error(), plainTextStore.errorString() );
  156. } else {
  157. q->emitFinished();
  158. }
  159. }
  160. #endif