master
cpp 121 lines 3.65 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 WinCredStorage.cpp
8
9 Abstract:
10
11 Windows Credential Manager credential storage implementation.
12
13 --*/
14
15 #include "precomp.h"
16 #include "WinCredStorage.h"
17 #include <wincred.h>
18
19 using wsl::shared::Localization;
20
21 using unique_credential = wil::unique_any<PCREDENTIALW, decltype(&CredFree), CredFree>;
22 using unique_credential_array = wil::unique_any<PCREDENTIALW*, decltype(&CredFree), CredFree>;
23
24 static constexpr auto WinCredPrefix = L"wslc-credential/";
25
26 namespace wsl::windows::wslc::services {
27
28 std::wstring WinCredStorage::TargetName(const std::string& serverAddress)
29 {
30 return std::wstring(WinCredPrefix) + wsl::shared::string::MultiByteToWide(serverAddress);
31 }
32
33 void WinCredStorage::Store(const std::string& serverAddress, const std::string& username, const std::string& secret)
34 {
35 auto targetName = TargetName(serverAddress);
36 auto wideUsername = wsl::shared::string::MultiByteToWide(username);
37
38 CREDENTIALW cred{};
39 cred.Type = CRED_TYPE_GENERIC;
40 cred.TargetName = const_cast<LPWSTR>(targetName.c_str());
41 cred.UserName = const_cast<LPWSTR>(wideUsername.c_str());
42 cred.CredentialBlobSize = static_cast<DWORD>(secret.size());
43 cred.CredentialBlob = reinterpret_cast<LPBYTE>(const_cast<char*>(secret.data()));
44 cred.Persist = CRED_PERSIST_LOCAL_MACHINE;
45
46 THROW_IF_WIN32_BOOL_FALSE(CredWriteW(&cred, 0));
47 }
48
49 std::pair<std::string, std::string> WinCredStorage::Get(const std::string& serverAddress)
50 {
51 auto targetName = TargetName(serverAddress);
52
53 unique_credential cred;
54 if (!CredReadW(targetName.c_str(), CRED_TYPE_GENERIC, 0, &cred))
55 {
56 auto error = GetLastError();
57
58 // Credential Manager is unavailable under network logon session (e.g. an SSH network logon),
59 // Treat it like "no credential found" so anonymous pull/push can proceed.
60 THROW_LAST_ERROR_IF(error != ERROR_NOT_FOUND && error != ERROR_NO_SUCH_LOGON_SESSION);
61 return {};
62 }
63
64 if (cred.get()->CredentialBlobSize == 0 || cred.get()->CredentialBlob == nullptr)
65 {
66 return {};
67 }
68
69 std::string username;
70 if (cred.get()->UserName)
71 {
72 username = wsl::shared::string::WideToMultiByte(cred.get()->UserName);
73 }
74
75 return {std::move(username), {reinterpret_cast<const char*>(cred.get()->CredentialBlob), cred.get()->CredentialBlobSize}};
76 }
77
78 void WinCredStorage::Erase(const std::string& serverAddress)
79 {
80 auto targetName = TargetName(serverAddress);
81
82 if (!CredDeleteW(targetName.c_str(), CRED_TYPE_GENERIC, 0))
83 {
84 auto error = GetLastError();
85 THROW_HR_WITH_USER_ERROR_IF(
86 E_NOT_SET, Localization::WSLCCLI_LogoutNotFound(wsl::shared::string::MultiByteToWide(serverAddress)), error == ERROR_NOT_FOUND);
87
88 THROW_WIN32(error);
89 }
90 }
91
92 std::vector<std::wstring> WinCredStorage::List()
93 {
94 auto prefix = std::wstring(WinCredPrefix);
95 auto filter = prefix + L"*";
96
97 DWORD count = 0;
98 unique_credential_array creds;
99 if (!CredEnumerateW(filter.c_str(), 0, &count, &creds))
100 {
101 auto error = GetLastError();
102
103 // Credential Manager is unavailable under network logon session (e.g. an SSH network logon),
104 // Treat it like "no credential found" so anonymous pull/push can proceed.
105 THROW_LAST_ERROR_IF(error != ERROR_NOT_FOUND && error != ERROR_NO_SUCH_LOGON_SESSION);
106 return {};
107 }
108
109 std::vector<std::wstring> result;
110 result.reserve(count);
111
112 for (DWORD i = 0; i < count; ++i)
113 {
114 std::wstring_view name(creds.get()[i]->TargetName);
115 result.emplace_back(name.substr(prefix.size()));
116 }
117
118 return result;
119 }
120
121 } // namespace wsl::windows::wslc::services