-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathu2f_daemon.cc
414 lines (324 loc) · 12.8 KB
/
u2f_daemon.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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
// Copyright 2019 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "u2fd/u2f_daemon.h"
#include <functional>
#include <string>
#include <sysexits.h>
#include <utility>
#include <vector>
#include <attestation/proto_bindings/interface.pb.h>
#include <attestation-client/attestation/dbus-constants.h>
#include <base/bind.h>
#include <base/optional.h>
#include <base/synchronization/waitable_event.h>
#include <bindings/chrome_device_policy.pb.h>
#include <dbus/u2f/dbus-constants.h>
#include <policy/device_policy.h>
#include <policy/libpolicy.h>
#include <trunks/cr50_headers/virtual_nvmem.h>
#include "u2fd/u2fhid.h"
#include "u2fd/uhid_device.h"
namespace u2f {
namespace em = enterprise_management;
enum class U2fMode : uint8_t {
kUnset = em::DeviceSecondFactorAuthenticationProto_U2fMode_UNSET,
kDisabled = em::DeviceSecondFactorAuthenticationProto_U2fMode_DISABLED,
kU2f = em::DeviceSecondFactorAuthenticationProto_U2fMode_U2F,
kU2fExtended = em::DeviceSecondFactorAuthenticationProto_U2fMode_U2F_EXTENDED,
};
namespace {
constexpr char kDeviceName[] = "Integrated U2F";
constexpr int kWinkSignalMinIntervalMs = 1000;
// The U2F counter stored in cr50 is stored in a format resistant to rollbacks,
// and that guarantees monotonicity even in the presence of partial writes.
// See //platform/ec/include/nvcounter.h
//
// The counter is stored across 2 pages of flash - a high page and a low page,
// with each page containing 512 4-byte words. The counter increments using
// 'strikes', with each strike occupying 4 bits. The high page can represent
// numbers 0-2048, and the low page can represent numbers 0-4096.
// The pages are interpreted as two digits of a base-4097 number, giving us
// the maximum value below.
// See //platform/ec/common/nvcounter.c for more details.
constexpr uint32_t kMaxCr50U2fCounterValue = (2048 * 4097) + 4096;
// If we are supporting legacy key handles, we initialize the counter such that
// it is always larger than the maximum possible value cr50 could have returned,
// and therefore guarantee that we provide a monotonically increasing counter
// value for migrated key handles.
constexpr uint32_t kLegacyKhCounterMin = kMaxCr50U2fCounterValue + 1;
bool U2fPolicyReady() {
policy::PolicyProvider policy_provider;
return policy_provider.Reload();
}
U2fMode ReadU2fPolicy() {
policy::PolicyProvider policy_provider;
if (!policy_provider.Reload()) {
LOG(DFATAL) << "Failed to load device policy";
}
int mode = 0;
const policy::DevicePolicy* policy = &policy_provider.GetDevicePolicy();
if (!policy->GetSecondFactorAuthenticationMode(&mode))
return U2fMode::kUnset;
return static_cast<U2fMode>(mode);
}
const char* U2fModeToString(U2fMode mode) {
switch (mode) {
case U2fMode::kUnset:
return "unset";
case U2fMode::kDisabled:
return "disabled";
case U2fMode::kU2f:
return "U2F";
case U2fMode::kU2fExtended:
return "U2F+extensions";
}
return "unknown";
}
U2fMode GetU2fMode(bool force_u2f, bool force_g2f) {
U2fMode policy_mode = ReadU2fPolicy();
LOG(INFO) << "Requested Mode: Policy[" << U2fModeToString(policy_mode)
<< "], force_u2f[" << force_u2f << "], force_g2f[" << force_g2f
<< "]";
// Always honor the administrator request to disable even if given
// contradictory override flags.
if (policy_mode == U2fMode::kDisabled) {
LOG(INFO) << "Mode: Disabled (explicitly by policy)";
return U2fMode::kDisabled;
}
if (force_g2f || policy_mode == U2fMode::kU2fExtended) {
LOG(INFO) << "Mode: U2F+extensions";
return U2fMode::kU2fExtended;
}
if (force_u2f || policy_mode == U2fMode::kU2f) {
LOG(INFO) << "Mode:U2F";
return U2fMode::kU2f;
}
LOG(INFO) << "Mode: Disabled";
return U2fMode::kDisabled;
}
void OnPolicySignalConnected(const std::string& interface,
const std::string& signal,
bool success) {
if (!success) {
LOG(FATAL) << "Could not connect to signal " << signal << " on interface "
<< interface;
}
}
} // namespace
U2fDaemon::U2fDaemon(bool force_u2f,
bool force_g2f,
bool g2f_allowlist_data,
bool legacy_kh_fallback,
uint32_t vendor_id,
uint32_t product_id)
: brillo::DBusServiceDaemon(u2f::kU2FServiceName),
force_u2f_(force_u2f),
force_g2f_(force_g2f),
g2f_allowlist_data_(g2f_allowlist_data),
legacy_kh_fallback_(legacy_kh_fallback),
vendor_id_(vendor_id),
product_id_(product_id) {}
int U2fDaemon::OnInit() {
int rc = brillo::DBusServiceDaemon::OnInit();
if (rc != EX_OK)
return rc;
if (!InitializeDBusProxies()) {
return EX_IOERR;
}
user_state_ = std::make_unique<u2f::UserState>(
sm_proxy_.get(), legacy_kh_fallback_ ? kLegacyKhCounterMin : 0);
sm_proxy_->RegisterPropertyChangeCompleteSignalHandler(
base::Bind(&U2fDaemon::TryStartService, base::Unretained(this)),
base::Bind(&OnPolicySignalConnected));
bool policy_ready = U2fPolicyReady();
if (policy_ready) {
int status = StartService();
// If U2F is not currently enabled, we'll wait for policy updates
// that may enable it. We don't ever disable U2F on policy updates.
// TODO(louiscollard): Fix the above.
if (status != EX_CONFIG)
return status;
}
if (policy_ready) {
LOG(INFO) << "U2F currently disabled, waiting for policy updates...";
} else {
LOG(INFO) << "Policy not available, waiting...";
}
return EX_OK;
}
void U2fDaemon::TryStartService(
const std::string& /* unused dbus signal status */) {
// If there's u2fhid_ then we have already started service.
if (u2fhid_)
return;
if (!U2fPolicyReady())
return;
int status = StartService();
if (status != EX_OK && status != EX_CONFIG) {
// Something went wrong.
exit(status);
}
}
int U2fDaemon::StartService() {
// Start U2fHid service before WebAuthn because WebAuthn initialization can
// be slow.
int status = StartU2fHidService();
U2fMode u2f_mode = GetU2fMode(force_u2f_, force_g2f_);
if (u2f_mode != U2fMode::kU2f && u2f_mode != U2fMode::kU2fExtended) {
LOG(INFO) << "Initializing WebAuthn handler.";
InitializeWebAuthnHandler();
}
return status;
}
int U2fDaemon::StartU2fHidService() {
if (u2fhid_) {
// Any failures in previous calls to this function would have caused the
// program to terminate, so we can assume we have successfully started.
return EX_OK;
}
U2fMode u2f_mode = GetU2fMode(force_u2f_, force_g2f_);
if (u2f_mode == U2fMode::kDisabled) {
return EX_CONFIG;
}
LOG(INFO) << "Starting U2fHid service.";
// If g2f is enabled by policy, we always include allowlisting data.
bool include_g2f_allowlist_data =
g2f_allowlist_data_ || (ReadU2fPolicy() == U2fMode::kU2fExtended);
CreateU2fMsgHandler(
u2f_mode == U2fMode::kU2fExtended /* Allow G2F Attestation */,
include_g2f_allowlist_data);
CreateU2fHid();
return u2fhid_->Init() ? EX_OK : EX_PROTOCOL;
}
bool U2fDaemon::InitializeDBusProxies() {
if (!tpm_proxy_.Init()) {
LOG(ERROR) << "Failed to initialize trunksd DBus proxy";
return false;
}
attestation_proxy_ = bus_->GetObjectProxy(
attestation::kAttestationServiceName,
dbus::ObjectPath(attestation::kAttestationServicePath));
if (!attestation_proxy_) {
LOG(ERROR) << "Failed to initialize attestationd DBus proxy";
return false;
}
pm_proxy_ = std::make_unique<org::chromium::PowerManagerProxy>(bus_.get());
sm_proxy_ =
std::make_unique<org::chromium::SessionManagerInterfaceProxy>(bus_.get());
return true;
}
void U2fDaemon::RegisterDBusObjectsAsync(
brillo::dbus_utils::AsyncEventSequencer* sequencer) {
dbus_object_.reset(new brillo::dbus_utils::DBusObject(
nullptr, bus_, dbus::ObjectPath(u2f::kU2FServicePath)));
auto u2f_interface = dbus_object_->AddOrGetInterface(u2f::kU2FInterface);
wink_signal_ = u2f_interface->RegisterSignal<u2f::UserNotification>(
u2f::kU2FUserNotificationSignal);
// Handlers for the WebAuthn DBus API.
u2f_interface->AddMethodHandler(kU2FMakeCredential,
base::Unretained(&webauthn_handler_),
&WebAuthnHandler::MakeCredential);
u2f_interface->AddMethodHandler(kU2FGetAssertion,
base::Unretained(&webauthn_handler_),
&WebAuthnHandler::GetAssertion);
u2f_interface->AddSimpleMethodHandler(kU2FHasCredentials,
base::Unretained(&webauthn_handler_),
&WebAuthnHandler::HasCredentials);
u2f_interface->AddSimpleMethodHandler(kU2FCancelWebAuthnFlow,
base::Unretained(&webauthn_handler_),
&WebAuthnHandler::Cancel);
u2f_interface->AddMethodHandler(kU2FIsUvpaa,
base::Unretained(&webauthn_handler_),
&WebAuthnHandler::IsUvpaa);
dbus_object_->RegisterAsync(
sequencer->GetHandler("Failed to register DBus Interface.", true));
}
void U2fDaemon::CreateU2fMsgHandler(bool allow_g2f_attestation,
bool include_g2f_allowlisting_data) {
auto user_state = std::make_unique<u2f::UserState>(
sm_proxy_.get(), legacy_kh_fallback_ ? kLegacyKhCounterMin : 0);
std::function<void()> request_presence = [this]() {
IgnorePowerButtonPress();
SendWinkSignal();
};
auto allowlisting_util =
include_g2f_allowlisting_data
? std::make_unique<u2f::AllowlistingUtil>([this](int cert_size) {
return GetCertifiedG2fCert(cert_size);
})
: std::unique_ptr<u2f::AllowlistingUtil>(nullptr);
u2f_msg_handler_ = std::make_unique<u2f::U2fMessageHandler>(
std::move(allowlisting_util), request_presence, user_state_.get(),
&tpm_proxy_, &metrics_library_, legacy_kh_fallback_,
allow_g2f_attestation);
}
void U2fDaemon::CreateU2fHid() {
u2fhid_ = std::make_unique<u2f::U2fHid>(
std::make_unique<u2f::UHidDevice>(vendor_id_, product_id_, kDeviceName,
"u2fd-tpm-cr50"),
u2f_msg_handler_.get());
}
void U2fDaemon::InitializeWebAuthnHandler() {
std::function<void()> request_presence = [this]() {
IgnorePowerButtonPress();
SendWinkSignal();
};
webauthn_handler_.Initialize(bus_.get(), &tpm_proxy_, user_state_.get(),
request_presence);
}
void U2fDaemon::SendWinkSignal() {
static base::TimeTicks last_sent;
base::TimeDelta elapsed = base::TimeTicks::Now() - last_sent;
if (elapsed.InMilliseconds() > kWinkSignalMinIntervalMs) {
u2f::UserNotification notification;
notification.set_event_type(u2f::UserNotification::TOUCH_NEEDED);
wink_signal_.lock()->Send(notification);
last_sent = base::TimeTicks::Now();
}
}
void U2fDaemon::IgnorePowerButtonPress() {
// Duration of the user presence persistence on the firmware side.
const base::TimeDelta kPresenceTimeout = base::TimeDelta::FromSeconds(10);
brillo::ErrorPtr err;
// Mask the next power button press for the UI
pm_proxy_->IgnoreNextPowerButtonPress(kPresenceTimeout.ToInternalValue(),
&err, -1);
}
namespace {
constexpr char kKeyLabelEmk[] = "attest-ent-machine";
} // namespace
base::Optional<attestation::GetCertifiedNvIndexReply>
U2fDaemon::GetCertifiedG2fCert(int g2f_cert_size) {
if (g2f_cert_size < 1 || g2f_cert_size > VIRTUAL_NV_INDEX_G2F_CERT_SIZE) {
LOG(ERROR)
<< "Invalid G2F cert size specified for whitelisting data request";
return base::nullopt;
}
attestation::GetCertifiedNvIndexRequest request;
request.set_nv_index(VIRTUAL_NV_INDEX_G2F_CERT);
request.set_nv_size(g2f_cert_size);
request.set_key_label(kKeyLabelEmk);
brillo::ErrorPtr error;
std::unique_ptr<dbus::Response> dbus_response =
brillo::dbus_utils::CallMethodAndBlock(
attestation_proxy_, attestation::kAttestationInterface,
attestation::kGetCertifiedNvIndex, &error, request);
if (!dbus_response) {
LOG(ERROR) << "Failed to retrieve certified G2F cert from attestationd";
return base::nullopt;
}
attestation::GetCertifiedNvIndexReply reply;
dbus::MessageReader reader(dbus_response.get());
if (!reader.PopArrayOfBytesAsProto(&reply)) {
LOG(ERROR) << "Failed to parse GetCertifiedNvIndexReply";
return base::nullopt;
}
if (reply.status() != attestation::AttestationStatus::STATUS_SUCCESS) {
LOG(ERROR) << "Call get GetCertifiedNvIndex failed, status: "
<< reply.status();
return base::nullopt;
}
return reply;
}
} // namespace u2f