Skip to content

Commit 8fffaa4

Browse files
jonsimantova-maurice
authored andcommitted
Added generic UserSecureInternal test and Windows implementation using CredWrite/CredRead.
PiperOrigin-RevId: 245145981
1 parent cbfe192 commit 8fffaa4

File tree

3 files changed

+234
-6
lines changed

3 files changed

+234
-6
lines changed

auth/src/desktop/secure/user_secure_internal.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
12
// Copyright 2019 Google LLC
23
//
34
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,26 +19,24 @@
1819
#include <string>
1920

2021
#include "app/src/scheduler.h"
21-
#include "auth/src/desktop/secure/user_secure_data_handle.h"
2222

2323
namespace firebase {
2424
namespace auth {
2525
namespace secure {
2626

2727
class UserSecureInternal {
2828
public:
29-
UserSecureInternal() = default;
3029
virtual ~UserSecureInternal() = default;
3130

3231
// Load persisted user data for given app name.
33-
virtual std::string LoadUserData(const std::string appName) = 0;
32+
virtual std::string LoadUserData(const std::string& app_name) = 0;
3433

3534
// Save user data under the key of given app name.
36-
virtual void SaveUserData(const std::string appName,
37-
const std::string userData) = 0;
35+
virtual void SaveUserData(const std::string& app_name,
36+
const std::string& user_data) = 0;
3837

3938
// Delete user data under the given app name.
40-
virtual void DeleteUserData(const std::string appName) = 0;
39+
virtual void DeleteUserData(const std::string& app_name) = 0;
4140

4241
// Delete all user data.
4342
virtual void DeleteAllData() = 0;
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
// Copyright 2019 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "auth/src/desktop/secure/user_secure_windows_internal.h"
16+
17+
#define NOMINMAX
18+
#include <wincred.h>
19+
20+
namespace firebase {
21+
namespace auth {
22+
namespace secure {
23+
24+
static const char kNamespacePrefix[] = "firebase.auth.cpp.cred/";
25+
26+
UserSecureWindowsInternal::UserSecureWindowsInternal(
27+
const char* key_namespace) {
28+
namespace_ = std::string(kNamespacePrefix) + key_namespace;
29+
}
30+
31+
UserSecureWindowsInternal::~UserSecureWindowsInternal() {}
32+
33+
std::string UserSecureWindowsInternal::GetTargetName(
34+
const std::string& app_name) {
35+
return namespace_ + "/" + app_name;
36+
}
37+
38+
std::string UserSecureWindowsInternal::GetTargetName(
39+
const std::string& app_name, size_t idx) {
40+
return GetTargetName(app_name) + "[" + std::to_string(idx) + "]";
41+
}
42+
43+
// Returns true if an actual error occurred, false if not.
44+
static bool LogCredentialError(DWORD error, const char* func,
45+
const char* target) {
46+
if (error == ERROR_NOT_FOUND) {
47+
// If error is ERROR_NOT_FOUND, don't report an error, all is fine.
48+
LogDebug("%s: Credential %s not found", func, target);
49+
return false;
50+
} else if (error == ERROR_NO_SUCH_LOGON_SESSION) {
51+
LogWarning("%s: No logon session for credential %s", func, target);
52+
return true;
53+
} else if (error == ERROR_INVALID_FLAGS) {
54+
LogAssert("%s: Invalid flags for credential %s", func, target);
55+
return true;
56+
} else if (error == ERROR_INVALID_PARAMETER) {
57+
LogAssert("%s: Invalid paremeter for credential %s", func, target);
58+
return true;
59+
} else {
60+
// Unknown error occurred, print it out as a warning.
61+
LogWarning("%s: Operation on credential %s failed with error %d", func,
62+
target, error);
63+
return true;
64+
}
65+
}
66+
67+
std::string UserSecureWindowsInternal::LoadUserData(
68+
const std::string& app_name) {
69+
std::string output;
70+
int idx = 0;
71+
// Data comes in chunks, read a chunk at a time until we get a NOT_FOUND
72+
// error.
73+
for (;; ++idx) {
74+
std::string target = GetTargetName(app_name, idx);
75+
PCREDENTIAL credential = nullptr;
76+
BOOL success = CredRead(target.c_str(), CRED_TYPE_GENERIC, 0, &credential);
77+
if (!success) {
78+
DWORD error = GetLastError();
79+
if (credential) CredFree(credential);
80+
if (error == ERROR_NOT_FOUND && idx > 0) {
81+
// Reached the end of our data, return it.
82+
break;
83+
}
84+
LogCredentialError(error, "LoadUserData", target.c_str());
85+
return "";
86+
}
87+
std::string value(reinterpret_cast<const char*>(credential->CredentialBlob),
88+
static_cast<size_t>(credential->CredentialBlobSize));
89+
CredFree(credential);
90+
output = output + value;
91+
}
92+
return output;
93+
}
94+
95+
void UserSecureWindowsInternal::SaveUserData(const std::string& app_name,
96+
const std::string& user_data) {
97+
// First delete any existing data, so we don't have stale chunks.
98+
DeleteUserData(app_name);
99+
size_t user_data_size = user_data.length();
100+
for (size_t user_data_offset = 0; user_data_offset < user_data_size;
101+
user_data_offset += CRED_MAX_CREDENTIAL_BLOB_SIZE) {
102+
const char* chunk_data = user_data.c_str() + user_data_offset;
103+
size_t chunk_size =
104+
std::min(user_data_size - user_data_offset,
105+
static_cast<size_t>(CRED_MAX_CREDENTIAL_BLOB_SIZE));
106+
size_t chunk_number = user_data_offset / CRED_MAX_CREDENTIAL_BLOB_SIZE;
107+
std::string target = GetTargetName(app_name, chunk_number);
108+
109+
CREDENTIAL credential;
110+
memset(&credential, 0, sizeof(credential));
111+
credential.Flags = 0;
112+
credential.Type = CRED_TYPE_GENERIC;
113+
credential.TargetName = const_cast<LPSTR>(target.c_str());
114+
std::string comment =
115+
std::string("Firebase Auth persistent user data for ") + target;
116+
credential.Comment = const_cast<LPSTR>(comment.c_str());
117+
credential.CredentialBlobSize = chunk_size;
118+
credential.CredentialBlob =
119+
reinterpret_cast<LPBYTE>(const_cast<char*>(chunk_data));
120+
credential.Persist = CRED_PERSIST_LOCAL_MACHINE;
121+
BOOL success = CredWrite(&credential, 0);
122+
if (!success) {
123+
DWORD error = GetLastError();
124+
if (LogCredentialError(error, "SaveUserData", target.c_str()) &&
125+
chunk_number > 0) {
126+
// Delete partially written data before returning.
127+
DeleteUserData(app_name);
128+
}
129+
return;
130+
}
131+
}
132+
}
133+
134+
void UserSecureWindowsInternal::DeleteUserData(const std::string& app_name) {
135+
int idx = 0;
136+
for (;; ++idx) {
137+
std::string target = GetTargetName(app_name, idx);
138+
BOOL success = CredDelete(target.c_str(), CRED_TYPE_GENERIC, 0);
139+
if (!success) {
140+
DWORD error = GetLastError();
141+
if (error == ERROR_NOT_FOUND && idx > 0) {
142+
// Reached the end of our data, no error.
143+
break;
144+
}
145+
LogCredentialError(error, "DeleteUserData", target.c_str());
146+
return;
147+
}
148+
}
149+
}
150+
151+
void UserSecureWindowsInternal::DeleteAllData() {
152+
// Enumerate all credentials and delete them.
153+
std::string wildcard = "*";
154+
std::string target_glob = GetTargetName(wildcard);
155+
DWORD count = 0;
156+
PCREDENTIAL* credentials = nullptr;
157+
BOOL success = CredEnumerateA(target_glob.c_str(), 0, &count, &credentials);
158+
if (!success) {
159+
DWORD error = GetLastError();
160+
LogCredentialError(error, "DeleteAllData", target_glob.c_str());
161+
if (credentials) CredFree(credentials);
162+
return;
163+
}
164+
for (DWORD i = 0; i < count; ++i) {
165+
success = CredDelete(credentials[i]->TargetName, credentials[i]->Type, 0);
166+
if (!success) {
167+
DWORD error = GetLastError();
168+
LogCredentialError(error, "DeleteAllData", credentials[i]->TargetName);
169+
}
170+
}
171+
CredFree(credentials);
172+
}
173+
174+
} // namespace secure
175+
} // namespace auth
176+
} // namespace firebase
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Copyright 2019 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef FIREBASE_AUTH_CLIENT_CPP_SRC_DESKTOP_SECURE_USER_SECURE_WINDOWS_INTERNAL_H_
16+
#define FIREBASE_AUTH_CLIENT_CPP_SRC_DESKTOP_SECURE_USER_SECURE_WINDOWS_INTERNAL_H_
17+
18+
#include <string>
19+
20+
#include "auth/src/desktop/secure/user_secure_internal.h"
21+
22+
namespace firebase {
23+
namespace auth {
24+
namespace secure {
25+
26+
// Windows specific implementation for the secure manager of user data.
27+
class UserSecureWindowsInternal : public UserSecureInternal {
28+
public:
29+
explicit UserSecureWindowsInternal(const char* key_namespace);
30+
31+
~UserSecureWindowsInternal() override;
32+
33+
std::string LoadUserData(const std::string& app_name) override;
34+
35+
void SaveUserData(const std::string& app_name,
36+
const std::string& user_data) override;
37+
38+
void DeleteUserData(const std::string& app_name) override;
39+
40+
void DeleteAllData() override;
41+
42+
private:
43+
std::string GetTargetName(const std::string& app_name);
44+
std::string GetTargetName(const std::string& app_name, size_t idx);
45+
46+
std::string namespace_;
47+
};
48+
49+
} // namespace secure
50+
} // namespace auth
51+
} // namespace firebase
52+
53+
#endif // FIREBASE_AUTH_CLIENT_CPP_SRC_DESKTOP_SECURE_USER_SECURE_WINDOWS_INTERNAL_H_

0 commit comments

Comments
 (0)