blob: 81eec2020dcb4ef254574adfe266e404401003bd [file] [log] [blame]
Paul Stewartc43cbbe2013-04-11 06:29:30 -07001// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "shill/eap_credentials.h"
6
7#include <map>
8#include <string>
9#include <utility>
10#include <vector>
11
12#include <chromeos/dbus/service_constants.h>
13
14#include "shill/certificate_file.h"
Paul Stewartc43cbbe2013-04-11 06:29:30 -070015#include "shill/key_value_store.h"
Alex Vakulenkoa41ab512014-07-23 14:24:23 -070016#include "shill/logging.h"
Paul Stewartc43cbbe2013-04-11 06:29:30 -070017#include "shill/metrics.h"
Paul Stewartc43cbbe2013-04-11 06:29:30 -070018#include "shill/property_accessor.h"
19#include "shill/property_store.h"
20#include "shill/service.h"
21#include "shill/store_interface.h"
22#include "shill/wpa_supplicant.h"
23
24using base::FilePath;
25using std::map;
26using std::string;
27using std::vector;
28
29using std::string;
30
31namespace shill {
32
33const char EapCredentials::kStorageEapAnonymousIdentity[] =
34 "EAP.AnonymousIdentity";
35const char EapCredentials::kStorageEapCACert[] = "EAP.CACert";
36const char EapCredentials::kStorageEapCACertID[] = "EAP.CACertID";
37const char EapCredentials::kStorageEapCACertNSS[] = "EAP.CACertNSS";
38const char EapCredentials::kStorageEapCACertPEM[] = "EAP.CACertPEM";
39const char EapCredentials::kStorageEapCertID[] = "EAP.CertID";
40const char EapCredentials::kStorageEapClientCert[] = "EAP.ClientCert";
41const char EapCredentials::kStorageEapEap[] = "EAP.EAP";
42const char EapCredentials::kStorageEapIdentity[] = "EAP.Identity";
43const char EapCredentials::kStorageEapInnerEap[] = "EAP.InnerEAP";
44const char EapCredentials::kStorageEapKeyID[] = "EAP.KeyID";
45const char EapCredentials::kStorageEapKeyManagement[] = "EAP.KeyMgmt";
46const char EapCredentials::kStorageEapPIN[] = "EAP.PIN";
47const char EapCredentials::kStorageEapPassword[] = "EAP.Password";
48const char EapCredentials::kStorageEapPrivateKey[] = "EAP.PrivateKey";
49const char EapCredentials::kStorageEapPrivateKeyPassword[] =
50 "EAP.PrivateKeyPassword";
51const char EapCredentials::kStorageEapSubjectMatch[] =
52 "EAP.SubjectMatch";
53const char EapCredentials::kStorageEapUseSystemCAs[] = "EAP.UseSystemCAs";
54
55EapCredentials::EapCredentials() : use_system_cas_(true) {}
56
57EapCredentials::~EapCredentials() {}
58
59// static
60void EapCredentials::PopulateSupplicantProperties(
61 CertificateFile *certificate_file,
Paul Stewartc43cbbe2013-04-11 06:29:30 -070062 map<string, DBus::Variant> *params) const {
63 string ca_cert = ca_cert_;
64 if (!ca_cert_pem_.empty()) {
65 FilePath certfile =
Paul Stewartb3008ea2013-06-28 14:51:54 -070066 certificate_file->CreatePEMFromStrings(ca_cert_pem_);
Paul Stewartc43cbbe2013-04-11 06:29:30 -070067 if (certfile.empty()) {
68 LOG(ERROR) << "Unable to extract PEM certificate.";
69 } else {
70 ca_cert = certfile.value();
71 }
Paul Stewartc43cbbe2013-04-11 06:29:30 -070072 }
73
74
75 typedef std::pair<const char *, const char *> KeyVal;
76 KeyVal init_propertyvals[] = {
77 // Authentication properties.
78 KeyVal(WPASupplicant::kNetworkPropertyEapAnonymousIdentity,
79 anonymous_identity_.c_str()),
Paul Stewartc43cbbe2013-04-11 06:29:30 -070080 KeyVal(WPASupplicant::kNetworkPropertyEapClientCert,
81 client_cert_.c_str()),
82 KeyVal(WPASupplicant::kNetworkPropertyEapIdentity, identity_.c_str()),
Paul Stewartc43cbbe2013-04-11 06:29:30 -070083 KeyVal(WPASupplicant::kNetworkPropertyEapCaPassword,
84 password_.c_str()),
85 KeyVal(WPASupplicant::kNetworkPropertyEapPrivateKey,
86 private_key_.c_str()),
87 KeyVal(WPASupplicant::kNetworkPropertyEapPrivateKeyPassword,
88 private_key_password_.c_str()),
89
90 // Non-authentication properties.
91 KeyVal(WPASupplicant::kNetworkPropertyEapCaCert, ca_cert.c_str()),
92 KeyVal(WPASupplicant::kNetworkPropertyEapCaCertId,
93 ca_cert_id_.c_str()),
94 KeyVal(WPASupplicant::kNetworkPropertyEapEap, eap_.c_str()),
95 KeyVal(WPASupplicant::kNetworkPropertyEapInnerEap,
96 inner_eap_.c_str()),
97 KeyVal(WPASupplicant::kNetworkPropertyEapSubjectMatch,
98 subject_match_.c_str())
99 };
100
101 vector<KeyVal> propertyvals(init_propertyvals,
102 init_propertyvals + arraysize(init_propertyvals));
103 if (use_system_cas_) {
104 propertyvals.push_back(KeyVal(
105 WPASupplicant::kNetworkPropertyCaPath, WPASupplicant::kCaPath));
106 } else if (ca_cert.empty()) {
107 LOG(WARNING) << __func__
108 << ": No certificate authorities are configured."
109 << " Server certificates will be accepted"
110 << " unconditionally.";
111 }
112
Paul Stewart416a9812013-08-27 09:38:54 -0700113 if (ClientAuthenticationUsesCryptoToken()) {
114 propertyvals.push_back(KeyVal(
115 WPASupplicant::kNetworkPropertyEapCertId, cert_id_.c_str()));
116 propertyvals.push_back(KeyVal(
117 WPASupplicant::kNetworkPropertyEapKeyId, key_id_.c_str()));
118 }
119
120 if (ClientAuthenticationUsesCryptoToken() || !ca_cert_id_.empty()) {
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700121 propertyvals.push_back(KeyVal(
122 WPASupplicant::kNetworkPropertyEapPin, pin_.c_str()));
123 propertyvals.push_back(KeyVal(
124 WPASupplicant::kNetworkPropertyEngineId,
125 WPASupplicant::kEnginePKCS11));
126 // We can't use the propertyvals vector for this since this argument
Ben Chan7fab8972014-08-10 17:14:46 -0700127 // is a uint32_t, not a string.
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700128 (*params)[WPASupplicant::kNetworkPropertyEngine].writer().
129 append_uint32(WPASupplicant::kDefaultEngine);
130 }
131
Paul Stewart6db7b242014-05-02 15:34:21 -0700132 for (const auto &keyval : propertyvals) {
133 if (strlen(keyval.second) > 0) {
134 (*params)[keyval.first].writer().append_string(keyval.second);
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700135 }
136 }
137}
138
139// static
140void EapCredentials::PopulateWiMaxProperties(KeyValueStore *params) const {
141 if (!anonymous_identity_.empty()) {
142 params->SetString(wimax_manager::kEAPAnonymousIdentity,
143 anonymous_identity_);
144 }
145 if (!identity_.empty()) {
146 params->SetString(wimax_manager::kEAPUserIdentity, identity_);
147 }
148 if (!password_.empty()) {
149 params->SetString(wimax_manager::kEAPUserPassword, password_);
150 }
151}
152
153void EapCredentials::InitPropertyStore(PropertyStore *store) {
154 // Authentication properties.
Ben Chan783f00b2013-09-20 13:50:34 -0700155 store->RegisterString(kEapAnonymousIdentityProperty, &anonymous_identity_);
Ben Chan011e6662014-05-07 11:03:25 -0700156 store->RegisterString(kEapCertIdProperty, &cert_id_);
157 store->RegisterString(kEapClientCertProperty, &client_cert_);
Ben Chan783f00b2013-09-20 13:50:34 -0700158 store->RegisterString(kEapIdentityProperty, &identity_);
Ben Chan011e6662014-05-07 11:03:25 -0700159 store->RegisterString(kEapKeyIdProperty, &key_id_);
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700160 HelpRegisterDerivedString(store,
Ben Chan783f00b2013-09-20 13:50:34 -0700161 kEapKeyMgmtProperty,
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700162 &EapCredentials::GetKeyManagement,
163 &EapCredentials::SetKeyManagement);
164 HelpRegisterWriteOnlyDerivedString(store,
Ben Chan783f00b2013-09-20 13:50:34 -0700165 kEapPasswordProperty,
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700166 &EapCredentials::SetEapPassword,
Ben Chancc225ef2014-09-30 13:26:51 -0700167 nullptr,
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700168 &password_);
Ben Chan011e6662014-05-07 11:03:25 -0700169 store->RegisterString(kEapPinProperty, &pin_);
Ben Chan783f00b2013-09-20 13:50:34 -0700170 store->RegisterString(kEapPrivateKeyProperty, &private_key_);
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700171 HelpRegisterWriteOnlyDerivedString(store,
Ben Chan783f00b2013-09-20 13:50:34 -0700172 kEapPrivateKeyPasswordProperty,
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700173 &EapCredentials::SetEapPrivateKeyPassword,
Ben Chancc225ef2014-09-30 13:26:51 -0700174 nullptr,
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700175 &private_key_password_);
176
177 // Non-authentication properties.
Paul Stewartb3008ea2013-06-28 14:51:54 -0700178 store->RegisterStrings(kEapCaCertPemProperty, &ca_cert_pem_);
Ben Chan011e6662014-05-07 11:03:25 -0700179 store->RegisterString(kEapCaCertIdProperty, &ca_cert_id_);
Ben Chan783f00b2013-09-20 13:50:34 -0700180 store->RegisterString(kEapCaCertNssProperty, &ca_cert_nss_);
181 store->RegisterString(kEapCaCertProperty, &ca_cert_);
Ben Chan011e6662014-05-07 11:03:25 -0700182 store->RegisterString(kEapMethodProperty, &eap_);
Ben Chan783f00b2013-09-20 13:50:34 -0700183 store->RegisterString(kEapPhase2AuthProperty, &inner_eap_);
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700184 store->RegisterString(kEapSubjectMatchProperty, &subject_match_);
Ben Chan011e6662014-05-07 11:03:25 -0700185 store->RegisterBool(kEapUseSystemCasProperty, &use_system_cas_);
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700186}
187
188// static
189bool EapCredentials::IsEapAuthenticationProperty(const string property) {
190 return
Ben Chan783f00b2013-09-20 13:50:34 -0700191 property == kEapAnonymousIdentityProperty ||
Ben Chan011e6662014-05-07 11:03:25 -0700192 property == kEapCertIdProperty ||
193 property == kEapClientCertProperty ||
Ben Chan783f00b2013-09-20 13:50:34 -0700194 property == kEapIdentityProperty ||
Ben Chan011e6662014-05-07 11:03:25 -0700195 property == kEapKeyIdProperty ||
Ben Chan783f00b2013-09-20 13:50:34 -0700196 property == kEapKeyMgmtProperty ||
197 property == kEapPasswordProperty ||
Ben Chan011e6662014-05-07 11:03:25 -0700198 property == kEapPinProperty ||
Ben Chan783f00b2013-09-20 13:50:34 -0700199 property == kEapPrivateKeyProperty ||
200 property == kEapPrivateKeyPasswordProperty;
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700201}
202
203bool EapCredentials::IsConnectable() const {
204 // Identity is required.
205 if (identity_.empty()) {
206 SLOG(Service, 2) << "Not connectable: Identity is empty.";
207 return false;
208 }
209
210 if (!client_cert_.empty() || !cert_id_.empty()) {
211 // If a client certificate is being used, we must have a private key.
212 if (private_key_.empty() && key_id_.empty()) {
213 SLOG(Service, 2)
214 << "Not connectable: Client certificate but no private key.";
215 return false;
216 }
217 }
218 if (!cert_id_.empty() || !key_id_.empty() ||
219 !ca_cert_id_.empty()) {
220 // If PKCS#11 data is needed, a PIN is required.
221 if (pin_.empty()) {
222 SLOG(Service, 2) << "Not connectable: PKCS#11 data but no PIN.";
223 return false;
224 }
225 }
226
227 // For EAP-TLS, a client certificate is required.
Ben Chan783f00b2013-09-20 13:50:34 -0700228 if (eap_.empty() || eap_ == kEapMethodTLS) {
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700229 if ((!client_cert_.empty() || !cert_id_.empty()) &&
230 (!private_key_.empty() || !key_id_.empty())) {
231 SLOG(Service, 2) << "Connectable: EAP-TLS with a client cert and key.";
232 return true;
233 }
234 }
235
236 // For EAP types other than TLS (e.g. EAP-TTLS or EAP-PEAP, password is the
237 // minimum requirement), at least an identity + password is required.
Ben Chan783f00b2013-09-20 13:50:34 -0700238 if (eap_.empty() || eap_ != kEapMethodTLS) {
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700239 if (!password_.empty()) {
240 SLOG(Service, 2) << "Connectable. !EAP-TLS and has a password.";
241 return true;
242 }
243 }
244
245 SLOG(Service, 2)
246 << "Not connectable: No suitable EAP configuration was found.";
247 return false;
248}
249
250bool EapCredentials::IsConnectableUsingPassphrase() const {
251 return !identity_.empty() && !password_.empty();
252}
253
254void EapCredentials::Load(StoreInterface *storage, const string &id) {
255 // Authentication properties.
256 storage->GetCryptedString(id,
257 kStorageEapAnonymousIdentity,
258 &anonymous_identity_);
259 storage->GetString(id, kStorageEapCertID, &cert_id_);
260 storage->GetString(id, kStorageEapClientCert, &client_cert_);
261 storage->GetCryptedString(id, kStorageEapIdentity, &identity_);
262 storage->GetString(id, kStorageEapKeyID, &key_id_);
263 string key_management;
264 storage->GetString(id, kStorageEapKeyManagement, &key_management);
Ben Chancc225ef2014-09-30 13:26:51 -0700265 SetKeyManagement(key_management, nullptr);
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700266 storage->GetCryptedString(id, kStorageEapPassword, &password_);
267 storage->GetString(id, kStorageEapPIN, &pin_);
268 storage->GetString(id, kStorageEapPrivateKey, &private_key_);
269 storage->GetCryptedString(id,
270 kStorageEapPrivateKeyPassword,
271 &private_key_password_);
272
273 // Non-authentication properties.
274 storage->GetString(id, kStorageEapCACert, &ca_cert_);
275 storage->GetString(id, kStorageEapCACertID, &ca_cert_id_);
276 storage->GetString(id, kStorageEapCACertNSS, &ca_cert_nss_);
Paul Stewartb3008ea2013-06-28 14:51:54 -0700277 storage->GetStringList(id, kStorageEapCACertPEM, &ca_cert_pem_);
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700278 storage->GetString(id, kStorageEapEap, &eap_);
279 storage->GetString(id, kStorageEapInnerEap, &inner_eap_);
280 storage->GetString(id, kStorageEapSubjectMatch, &subject_match_);
281 storage->GetBool(id, kStorageEapUseSystemCAs, &use_system_cas_);
282}
283
284void EapCredentials::OutputConnectionMetrics(
285 Metrics *metrics, Technology::Identifier technology) const {
286 Metrics::EapOuterProtocol outer_protocol =
287 Metrics::EapOuterProtocolStringToEnum(eap_);
288 metrics->SendEnumToUMA(
mukesh agrawal132e96f2014-04-24 11:49:42 -0700289 metrics->GetFullMetricName(Metrics::kMetricNetworkEapOuterProtocolSuffix,
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700290 technology),
291 outer_protocol,
292 Metrics::kMetricNetworkEapOuterProtocolMax);
293
294 Metrics::EapInnerProtocol inner_protocol =
295 Metrics::EapInnerProtocolStringToEnum(inner_eap_);
296 metrics->SendEnumToUMA(
mukesh agrawal132e96f2014-04-24 11:49:42 -0700297 metrics->GetFullMetricName(Metrics::kMetricNetworkEapInnerProtocolSuffix,
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700298 technology),
299 inner_protocol,
300 Metrics::kMetricNetworkEapInnerProtocolMax);
301}
302
303void EapCredentials::Save(StoreInterface *storage, const string &id,
304 bool save_credentials) const {
305 // Authentication properties.
306 Service::SaveString(storage,
307 id,
308 kStorageEapAnonymousIdentity,
309 anonymous_identity_,
310 true,
311 save_credentials);
312 Service::SaveString(storage,
313 id,
314 kStorageEapCertID,
315 cert_id_,
316 false,
317 save_credentials);
318 Service::SaveString(storage,
319 id,
320 kStorageEapClientCert,
321 client_cert_,
322 false,
323 save_credentials);
324 Service::SaveString(storage,
325 id,
326 kStorageEapIdentity,
327 identity_,
328 true,
329 save_credentials);
330 Service::SaveString(storage,
331 id,
332 kStorageEapKeyID,
333 key_id_,
334 false,
335 save_credentials);
336 Service::SaveString(storage,
337 id,
338 kStorageEapKeyManagement,
339 key_management_,
340 false,
341 true);
342 Service::SaveString(storage,
343 id,
344 kStorageEapPassword,
345 password_,
346 true,
347 save_credentials);
348 Service::SaveString(storage,
349 id,
350 kStorageEapPIN,
351 pin_,
352 false,
353 save_credentials);
354 Service::SaveString(storage,
355 id,
356 kStorageEapPrivateKey,
357 private_key_,
358 false,
359 save_credentials);
360 Service::SaveString(storage,
361 id,
362 kStorageEapPrivateKeyPassword,
363 private_key_password_,
364 true,
365 save_credentials);
366
367 // Non-authentication properties.
368 Service::SaveString(storage, id, kStorageEapCACert, ca_cert_, false, true);
369 Service::SaveString(storage,
370 id,
371 kStorageEapCACertID,
372 ca_cert_id_,
373 false,
374 true);
375 Service::SaveString(storage,
376 id,
377 kStorageEapCACertNSS,
378 ca_cert_nss_,
379 false,
380 true);
Paul Stewartb3008ea2013-06-28 14:51:54 -0700381 if (ca_cert_pem_.empty()) {
382 storage->DeleteKey(id, kStorageEapCACertPEM);
383 } else {
384 storage->SetStringList(id, kStorageEapCACertPEM, ca_cert_pem_);
385 }
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700386 Service::SaveString(storage, id, kStorageEapEap, eap_, false, true);
387 Service::SaveString(storage,
388 id,
389 kStorageEapInnerEap,
390 inner_eap_,
391 false,
392 true);
393 Service::SaveString(storage,
394 id,
395 kStorageEapSubjectMatch,
396 subject_match_,
397 false,
398 true);
399 storage->SetBool(id, kStorageEapUseSystemCAs, use_system_cas_);
400}
401
402void EapCredentials::Reset() {
403 // Authentication properties.
404 anonymous_identity_ = "";
405 cert_id_ = "";
406 client_cert_ = "";
407 identity_ = "";
408 key_id_ = "";
Paul Stewart9956dc72013-04-23 14:06:17 -0700409 // Do not reset key_management_, since it should never be emptied.
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700410 password_ = "";
411 pin_ = "";
412 private_key_ = "";
413 private_key_password_ = "";
414
415 // Non-authentication properties.
416 ca_cert_ = "";
417 ca_cert_id_ = "";
418 ca_cert_nss_ = "";
Paul Stewartb3008ea2013-06-28 14:51:54 -0700419 ca_cert_pem_.clear();
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700420 eap_ = "";
421 inner_eap_ = "";
422 subject_match_ = "";
423 use_system_cas_ = true;
424}
425
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700426bool EapCredentials::SetEapPassword(const string &password, Error */*error*/) {
427 if (password_ == password) {
428 return false;
429 }
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700430 password_ = password;
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700431 return true;
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700432}
433
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700434bool EapCredentials::SetEapPrivateKeyPassword(const string &password,
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700435 Error */*error*/) {
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700436 if (private_key_password_ == password) {
437 return false;
438 }
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700439 private_key_password_ = password;
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700440 return true;
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700441}
442
443string EapCredentials::GetKeyManagement(Error */*error*/) {
444 return key_management_;
445}
446
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700447bool EapCredentials::SetKeyManagement(const std::string &key_management,
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700448 Error */*error*/) {
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700449 if (key_management.empty()) {
450 return false;
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700451 }
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700452 if (key_management_ == key_management) {
453 return false;
454 }
455 key_management_ = key_management;
456 return true;
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700457}
458
Paul Stewart416a9812013-08-27 09:38:54 -0700459bool EapCredentials::ClientAuthenticationUsesCryptoToken() const {
Ben Chan783f00b2013-09-20 13:50:34 -0700460 return (eap_.empty() || eap_ == kEapMethodTLS ||
461 inner_eap_ == kEapMethodTLS) &&
Paul Stewart416a9812013-08-27 09:38:54 -0700462 (!cert_id_.empty() || !key_id_.empty());
463}
464
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700465void EapCredentials::HelpRegisterDerivedString(
466 PropertyStore *store,
467 const string &name,
Alex Vakulenko8a532292014-06-16 17:18:44 -0700468 string(EapCredentials::*get)(Error *error),
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700469 bool(EapCredentials::*set)(const string&, Error *)) {
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700470 store->RegisterDerivedString(
471 name,
472 StringAccessor(new CustomAccessor<EapCredentials, string>(
473 this, get, set)));
474}
475
476void EapCredentials::HelpRegisterWriteOnlyDerivedString(
477 PropertyStore *store,
478 const string &name,
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700479 bool(EapCredentials::*set)(const string &, Error *),
Alex Vakulenko8a532292014-06-16 17:18:44 -0700480 void(EapCredentials::*clear)(Error *error),
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700481 const string *default_value) {
482 store->RegisterDerivedString(
483 name,
484 StringAccessor(
485 new CustomWriteOnlyAccessor<EapCredentials, string>(
486 this, set, clear, default_value)));
487}
488
Alex Vakulenko8a532292014-06-16 17:18:44 -0700489} // namespace shill