-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpassword_provider.cc
190 lines (154 loc) · 5.74 KB
/
password_provider.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
// Copyright 2017 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "libpasswordprovider/password_provider.h"
#include <grp.h>
#include <keyutils.h>
#include <unistd.h>
#include <vector>
#include "base/logging.h"
#include "libpasswordprovider/password.h"
#include <base/check.h>
#include <base/check_op.h>
namespace password_provider {
namespace {
constexpr int kDefaultGroupStringsLength = 1024;
constexpr char kKeyringDescription[] = "password keyring";
constexpr char kKeyringKeyType[] = "keyring";
constexpr char kPasswordKeyDescription[] = "password";
constexpr char kPasswordKeyType[] = "user";
constexpr char kPasswordViewersGroupName[] = "password-viewers";
key_serial_t RequestKey(const char* type, const char* description) {
key_serial_t keyring_serial =
find_key_by_type_and_desc(kKeyringKeyType, kKeyringDescription, 0);
if (keyring_serial == -1) {
// This is also called in cases where keys might not exist (e.g., cleaning
// up on logout) so not finding any keys is not an error.
VLOG(1) << "Error finding keyring. errno: " << errno;
return keyring_serial;
}
return keyctl_search(keyring_serial, type, description, 0);
}
int RevokeKey(const char* type, const char* description) {
key_serial_t key_serial = RequestKey(type, description);
if (key_serial == -1) {
return errno;
}
int result = keyctl_revoke(key_serial);
if (result == -1) {
return errno;
}
return result;
}
void HandleKeyError(const char* type, const char* description) {
int result = RevokeKey(type, description);
if (result != 0) {
PLOG(ERROR) << "Error revoking key: " << description;
}
}
} // namespace
PasswordProvider::PasswordProvider() {}
bool PasswordProvider::SavePassword(const Password& password) const {
DCHECK_GT(password.size(), 0);
DCHECK(password.GetRaw());
// Get the group ID for password-viewers
long group_name_length = sysconf(_SC_GETGR_R_SIZE_MAX); // NOLINT long
if (group_name_length == -1) {
group_name_length = kDefaultGroupStringsLength;
}
struct group group_info, *group_infop;
std::vector<char> group_name_buf(group_name_length);
int result =
getgrnam_r(kPasswordViewersGroupName, &group_info, group_name_buf.data(),
group_name_length, &group_infop);
if (result) {
LOG(WARNING) << "Error retrieving group ID for "
<< kPasswordViewersGroupName << " error: " << result;
group_info.gr_gid = -1;
} else if (group_infop == NULL) {
LOG(WARNING) << "Could not find group ID for " << kPasswordViewersGroupName;
group_info.gr_gid = -1;
}
key_serial_t keyring_id = add_key(kKeyringKeyType, kKeyringDescription, NULL,
0, KEY_SPEC_PROCESS_KEYRING);
if (keyring_id == -1) {
PLOG(ERROR) << "Error creating keyring.";
return false;
}
result = keyctl_chown(keyring_id, -1, group_info.gr_gid);
if (result == -1) {
// Don't return false here. Failing to change the group means that the key
// can't be retrieved by the users in the specified group. The security of
// the key is not compromised. Unit tests are not run as a superuser, and so
// can't chown the key and this call will always fail.
PLOG(ERROR) << "Could not change keyring group.";
}
result =
keyctl_setperm(keyring_id, KEY_POS_ALL | KEY_GRP_VIEW | KEY_GRP_READ |
KEY_GRP_SEARCH | KEY_GRP_WRITE);
if (result == -1) {
PLOG(ERROR) << "Error setting permissions on keyring. ";
return false;
}
key_serial_t key_serial =
add_key(kPasswordKeyType, kPasswordKeyDescription, password.GetRaw(),
password.size(), keyring_id);
if (key_serial == -1) {
PLOG(ERROR) << "Error adding key to keyring.";
return false;
}
result = keyctl_chown(key_serial, -1, group_info.gr_gid);
if (result == -1) {
// Don't return false here. Failing to change the group means that the key
// can't be retrieved by the users in the specified group. The security of
// the key is not compromised. Unit tests are not run as a superuser, and so
// can't chown the key and this call will always fail.
PLOG(ERROR) << "Could not change key group.";
}
result =
keyctl_setperm(key_serial, KEY_POS_ALL | KEY_GRP_VIEW | KEY_GRP_READ |
KEY_GRP_SEARCH | KEY_GRP_WRITE);
if (result == -1) {
PLOG(ERROR) << "Error setting permissions on key. ";
HandleKeyError(kPasswordKeyType, kPasswordKeyDescription);
return false;
}
return true;
}
std::unique_ptr<Password> PasswordProvider::GetPassword() const {
key_serial_t key_serial =
RequestKey(kPasswordKeyType, kPasswordKeyDescription);
if (key_serial == -1) {
PLOG(WARNING) << "Could not find key.";
return nullptr;
}
auto password = std::make_unique<Password>();
if (!password->Init()) {
LOG(ERROR) << "Error allocating buffer for password";
return nullptr;
}
int result =
keyctl_read(key_serial, password->GetMutableRaw(), password->max_size());
if (result > password->max_size()) {
LOG(ERROR) << "Password too large for buffer. Max size: "
<< password->max_size();
return nullptr;
}
if (result == -1) {
PLOG(ERROR) << "Error reading key.";
return nullptr;
}
password->SetSize(result);
return password;
}
bool PasswordProvider::DiscardPassword() const {
int result = RevokeKey(kPasswordKeyType, kPasswordKeyDescription);
if (result != 0) {
// This is also called in cases where keys might not exist (e.g., cleaning
// up on logout) so not finding any keys is not an error.
VLOG(1) << "Error revoking key. errno: " << errno;
return false;
}
return true;
}
} // namespace password_provider