| // Copyright (c) 2013 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 "shill/eap_credentials.h" |
| |
| #include <map> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include <chromeos/dbus/service_constants.h> |
| |
| #include "shill/certificate_file.h" |
| #include "shill/key_value_store.h" |
| #include "shill/logging.h" |
| #include "shill/metrics.h" |
| #include "shill/property_accessor.h" |
| #include "shill/property_store.h" |
| #include "shill/service.h" |
| #include "shill/store_interface.h" |
| #include "shill/wpa_supplicant.h" |
| |
| using base::FilePath; |
| using std::map; |
| using std::string; |
| using std::vector; |
| |
| using std::string; |
| |
| namespace shill { |
| |
| const char EapCredentials::kStorageEapAnonymousIdentity[] = |
| "EAP.AnonymousIdentity"; |
| const char EapCredentials::kStorageEapCACert[] = "EAP.CACert"; |
| const char EapCredentials::kStorageEapCACertID[] = "EAP.CACertID"; |
| const char EapCredentials::kStorageEapCACertNSS[] = "EAP.CACertNSS"; |
| const char EapCredentials::kStorageEapCACertPEM[] = "EAP.CACertPEM"; |
| const char EapCredentials::kStorageEapCertID[] = "EAP.CertID"; |
| const char EapCredentials::kStorageEapClientCert[] = "EAP.ClientCert"; |
| const char EapCredentials::kStorageEapEap[] = "EAP.EAP"; |
| const char EapCredentials::kStorageEapIdentity[] = "EAP.Identity"; |
| const char EapCredentials::kStorageEapInnerEap[] = "EAP.InnerEAP"; |
| const char EapCredentials::kStorageEapKeyID[] = "EAP.KeyID"; |
| const char EapCredentials::kStorageEapKeyManagement[] = "EAP.KeyMgmt"; |
| const char EapCredentials::kStorageEapPIN[] = "EAP.PIN"; |
| const char EapCredentials::kStorageEapPassword[] = "EAP.Password"; |
| const char EapCredentials::kStorageEapPrivateKey[] = "EAP.PrivateKey"; |
| const char EapCredentials::kStorageEapPrivateKeyPassword[] = |
| "EAP.PrivateKeyPassword"; |
| const char EapCredentials::kStorageEapSubjectMatch[] = |
| "EAP.SubjectMatch"; |
| const char EapCredentials::kStorageEapUseSystemCAs[] = "EAP.UseSystemCAs"; |
| |
| EapCredentials::EapCredentials() : use_system_cas_(true) {} |
| |
| EapCredentials::~EapCredentials() {} |
| |
| // static |
| void EapCredentials::PopulateSupplicantProperties( |
| CertificateFile *certificate_file, |
| map<string, DBus::Variant> *params) const { |
| string ca_cert = ca_cert_; |
| if (!ca_cert_pem_.empty()) { |
| FilePath certfile = |
| certificate_file->CreatePEMFromStrings(ca_cert_pem_); |
| if (certfile.empty()) { |
| LOG(ERROR) << "Unable to extract PEM certificate."; |
| } else { |
| ca_cert = certfile.value(); |
| } |
| } |
| |
| |
| typedef std::pair<const char *, const char *> KeyVal; |
| KeyVal init_propertyvals[] = { |
| // Authentication properties. |
| KeyVal(WPASupplicant::kNetworkPropertyEapAnonymousIdentity, |
| anonymous_identity_.c_str()), |
| KeyVal(WPASupplicant::kNetworkPropertyEapClientCert, |
| client_cert_.c_str()), |
| KeyVal(WPASupplicant::kNetworkPropertyEapIdentity, identity_.c_str()), |
| KeyVal(WPASupplicant::kNetworkPropertyEapCaPassword, |
| password_.c_str()), |
| KeyVal(WPASupplicant::kNetworkPropertyEapPrivateKey, |
| private_key_.c_str()), |
| KeyVal(WPASupplicant::kNetworkPropertyEapPrivateKeyPassword, |
| private_key_password_.c_str()), |
| |
| // Non-authentication properties. |
| KeyVal(WPASupplicant::kNetworkPropertyEapCaCert, ca_cert.c_str()), |
| KeyVal(WPASupplicant::kNetworkPropertyEapCaCertId, |
| ca_cert_id_.c_str()), |
| KeyVal(WPASupplicant::kNetworkPropertyEapEap, eap_.c_str()), |
| KeyVal(WPASupplicant::kNetworkPropertyEapInnerEap, |
| inner_eap_.c_str()), |
| KeyVal(WPASupplicant::kNetworkPropertyEapSubjectMatch, |
| subject_match_.c_str()) |
| }; |
| |
| vector<KeyVal> propertyvals(init_propertyvals, |
| init_propertyvals + arraysize(init_propertyvals)); |
| if (use_system_cas_) { |
| propertyvals.push_back(KeyVal( |
| WPASupplicant::kNetworkPropertyCaPath, WPASupplicant::kCaPath)); |
| } else if (ca_cert.empty()) { |
| LOG(WARNING) << __func__ |
| << ": No certificate authorities are configured." |
| << " Server certificates will be accepted" |
| << " unconditionally."; |
| } |
| |
| if (ClientAuthenticationUsesCryptoToken()) { |
| propertyvals.push_back(KeyVal( |
| WPASupplicant::kNetworkPropertyEapCertId, cert_id_.c_str())); |
| propertyvals.push_back(KeyVal( |
| WPASupplicant::kNetworkPropertyEapKeyId, key_id_.c_str())); |
| } |
| |
| if (ClientAuthenticationUsesCryptoToken() || !ca_cert_id_.empty()) { |
| propertyvals.push_back(KeyVal( |
| WPASupplicant::kNetworkPropertyEapPin, pin_.c_str())); |
| propertyvals.push_back(KeyVal( |
| WPASupplicant::kNetworkPropertyEngineId, |
| WPASupplicant::kEnginePKCS11)); |
| // We can't use the propertyvals vector for this since this argument |
| // is a uint32_t, not a string. |
| (*params)[WPASupplicant::kNetworkPropertyEngine].writer(). |
| append_uint32(WPASupplicant::kDefaultEngine); |
| } |
| |
| for (const auto &keyval : propertyvals) { |
| if (strlen(keyval.second) > 0) { |
| (*params)[keyval.first].writer().append_string(keyval.second); |
| } |
| } |
| } |
| |
| // static |
| void EapCredentials::PopulateWiMaxProperties(KeyValueStore *params) const { |
| if (!anonymous_identity_.empty()) { |
| params->SetString(wimax_manager::kEAPAnonymousIdentity, |
| anonymous_identity_); |
| } |
| if (!identity_.empty()) { |
| params->SetString(wimax_manager::kEAPUserIdentity, identity_); |
| } |
| if (!password_.empty()) { |
| params->SetString(wimax_manager::kEAPUserPassword, password_); |
| } |
| } |
| |
| void EapCredentials::InitPropertyStore(PropertyStore *store) { |
| // Authentication properties. |
| store->RegisterString(kEapAnonymousIdentityProperty, &anonymous_identity_); |
| store->RegisterString(kEapCertIdProperty, &cert_id_); |
| store->RegisterString(kEapClientCertProperty, &client_cert_); |
| store->RegisterString(kEapIdentityProperty, &identity_); |
| store->RegisterString(kEapKeyIdProperty, &key_id_); |
| HelpRegisterDerivedString(store, |
| kEapKeyMgmtProperty, |
| &EapCredentials::GetKeyManagement, |
| &EapCredentials::SetKeyManagement); |
| HelpRegisterWriteOnlyDerivedString(store, |
| kEapPasswordProperty, |
| &EapCredentials::SetEapPassword, |
| NULL, |
| &password_); |
| store->RegisterString(kEapPinProperty, &pin_); |
| store->RegisterString(kEapPrivateKeyProperty, &private_key_); |
| HelpRegisterWriteOnlyDerivedString(store, |
| kEapPrivateKeyPasswordProperty, |
| &EapCredentials::SetEapPrivateKeyPassword, |
| NULL, |
| &private_key_password_); |
| |
| // Non-authentication properties. |
| store->RegisterStrings(kEapCaCertPemProperty, &ca_cert_pem_); |
| store->RegisterString(kEapCaCertIdProperty, &ca_cert_id_); |
| store->RegisterString(kEapCaCertNssProperty, &ca_cert_nss_); |
| store->RegisterString(kEapCaCertProperty, &ca_cert_); |
| store->RegisterString(kEapMethodProperty, &eap_); |
| store->RegisterString(kEapPhase2AuthProperty, &inner_eap_); |
| store->RegisterString(kEapSubjectMatchProperty, &subject_match_); |
| store->RegisterBool(kEapUseSystemCasProperty, &use_system_cas_); |
| } |
| |
| // static |
| bool EapCredentials::IsEapAuthenticationProperty(const string property) { |
| return |
| property == kEapAnonymousIdentityProperty || |
| property == kEapCertIdProperty || |
| property == kEapClientCertProperty || |
| property == kEapIdentityProperty || |
| property == kEapKeyIdProperty || |
| property == kEapKeyMgmtProperty || |
| property == kEapPasswordProperty || |
| property == kEapPinProperty || |
| property == kEapPrivateKeyProperty || |
| property == kEapPrivateKeyPasswordProperty; |
| } |
| |
| bool EapCredentials::IsConnectable() const { |
| // Identity is required. |
| if (identity_.empty()) { |
| SLOG(Service, 2) << "Not connectable: Identity is empty."; |
| return false; |
| } |
| |
| if (!client_cert_.empty() || !cert_id_.empty()) { |
| // If a client certificate is being used, we must have a private key. |
| if (private_key_.empty() && key_id_.empty()) { |
| SLOG(Service, 2) |
| << "Not connectable: Client certificate but no private key."; |
| return false; |
| } |
| } |
| if (!cert_id_.empty() || !key_id_.empty() || |
| !ca_cert_id_.empty()) { |
| // If PKCS#11 data is needed, a PIN is required. |
| if (pin_.empty()) { |
| SLOG(Service, 2) << "Not connectable: PKCS#11 data but no PIN."; |
| return false; |
| } |
| } |
| |
| // For EAP-TLS, a client certificate is required. |
| if (eap_.empty() || eap_ == kEapMethodTLS) { |
| if ((!client_cert_.empty() || !cert_id_.empty()) && |
| (!private_key_.empty() || !key_id_.empty())) { |
| SLOG(Service, 2) << "Connectable: EAP-TLS with a client cert and key."; |
| return true; |
| } |
| } |
| |
| // For EAP types other than TLS (e.g. EAP-TTLS or EAP-PEAP, password is the |
| // minimum requirement), at least an identity + password is required. |
| if (eap_.empty() || eap_ != kEapMethodTLS) { |
| if (!password_.empty()) { |
| SLOG(Service, 2) << "Connectable. !EAP-TLS and has a password."; |
| return true; |
| } |
| } |
| |
| SLOG(Service, 2) |
| << "Not connectable: No suitable EAP configuration was found."; |
| return false; |
| } |
| |
| bool EapCredentials::IsConnectableUsingPassphrase() const { |
| return !identity_.empty() && !password_.empty(); |
| } |
| |
| void EapCredentials::Load(StoreInterface *storage, const string &id) { |
| // Authentication properties. |
| storage->GetCryptedString(id, |
| kStorageEapAnonymousIdentity, |
| &anonymous_identity_); |
| storage->GetString(id, kStorageEapCertID, &cert_id_); |
| storage->GetString(id, kStorageEapClientCert, &client_cert_); |
| storage->GetCryptedString(id, kStorageEapIdentity, &identity_); |
| storage->GetString(id, kStorageEapKeyID, &key_id_); |
| string key_management; |
| storage->GetString(id, kStorageEapKeyManagement, &key_management); |
| SetKeyManagement(key_management, NULL); |
| storage->GetCryptedString(id, kStorageEapPassword, &password_); |
| storage->GetString(id, kStorageEapPIN, &pin_); |
| storage->GetString(id, kStorageEapPrivateKey, &private_key_); |
| storage->GetCryptedString(id, |
| kStorageEapPrivateKeyPassword, |
| &private_key_password_); |
| |
| // Non-authentication properties. |
| storage->GetString(id, kStorageEapCACert, &ca_cert_); |
| storage->GetString(id, kStorageEapCACertID, &ca_cert_id_); |
| storage->GetString(id, kStorageEapCACertNSS, &ca_cert_nss_); |
| storage->GetStringList(id, kStorageEapCACertPEM, &ca_cert_pem_); |
| storage->GetString(id, kStorageEapEap, &eap_); |
| storage->GetString(id, kStorageEapInnerEap, &inner_eap_); |
| storage->GetString(id, kStorageEapSubjectMatch, &subject_match_); |
| storage->GetBool(id, kStorageEapUseSystemCAs, &use_system_cas_); |
| } |
| |
| void EapCredentials::OutputConnectionMetrics( |
| Metrics *metrics, Technology::Identifier technology) const { |
| Metrics::EapOuterProtocol outer_protocol = |
| Metrics::EapOuterProtocolStringToEnum(eap_); |
| metrics->SendEnumToUMA( |
| metrics->GetFullMetricName(Metrics::kMetricNetworkEapOuterProtocolSuffix, |
| technology), |
| outer_protocol, |
| Metrics::kMetricNetworkEapOuterProtocolMax); |
| |
| Metrics::EapInnerProtocol inner_protocol = |
| Metrics::EapInnerProtocolStringToEnum(inner_eap_); |
| metrics->SendEnumToUMA( |
| metrics->GetFullMetricName(Metrics::kMetricNetworkEapInnerProtocolSuffix, |
| technology), |
| inner_protocol, |
| Metrics::kMetricNetworkEapInnerProtocolMax); |
| } |
| |
| void EapCredentials::Save(StoreInterface *storage, const string &id, |
| bool save_credentials) const { |
| // Authentication properties. |
| Service::SaveString(storage, |
| id, |
| kStorageEapAnonymousIdentity, |
| anonymous_identity_, |
| true, |
| save_credentials); |
| Service::SaveString(storage, |
| id, |
| kStorageEapCertID, |
| cert_id_, |
| false, |
| save_credentials); |
| Service::SaveString(storage, |
| id, |
| kStorageEapClientCert, |
| client_cert_, |
| false, |
| save_credentials); |
| Service::SaveString(storage, |
| id, |
| kStorageEapIdentity, |
| identity_, |
| true, |
| save_credentials); |
| Service::SaveString(storage, |
| id, |
| kStorageEapKeyID, |
| key_id_, |
| false, |
| save_credentials); |
| Service::SaveString(storage, |
| id, |
| kStorageEapKeyManagement, |
| key_management_, |
| false, |
| true); |
| Service::SaveString(storage, |
| id, |
| kStorageEapPassword, |
| password_, |
| true, |
| save_credentials); |
| Service::SaveString(storage, |
| id, |
| kStorageEapPIN, |
| pin_, |
| false, |
| save_credentials); |
| Service::SaveString(storage, |
| id, |
| kStorageEapPrivateKey, |
| private_key_, |
| false, |
| save_credentials); |
| Service::SaveString(storage, |
| id, |
| kStorageEapPrivateKeyPassword, |
| private_key_password_, |
| true, |
| save_credentials); |
| |
| // Non-authentication properties. |
| Service::SaveString(storage, id, kStorageEapCACert, ca_cert_, false, true); |
| Service::SaveString(storage, |
| id, |
| kStorageEapCACertID, |
| ca_cert_id_, |
| false, |
| true); |
| Service::SaveString(storage, |
| id, |
| kStorageEapCACertNSS, |
| ca_cert_nss_, |
| false, |
| true); |
| if (ca_cert_pem_.empty()) { |
| storage->DeleteKey(id, kStorageEapCACertPEM); |
| } else { |
| storage->SetStringList(id, kStorageEapCACertPEM, ca_cert_pem_); |
| } |
| Service::SaveString(storage, id, kStorageEapEap, eap_, false, true); |
| Service::SaveString(storage, |
| id, |
| kStorageEapInnerEap, |
| inner_eap_, |
| false, |
| true); |
| Service::SaveString(storage, |
| id, |
| kStorageEapSubjectMatch, |
| subject_match_, |
| false, |
| true); |
| storage->SetBool(id, kStorageEapUseSystemCAs, use_system_cas_); |
| } |
| |
| void EapCredentials::Reset() { |
| // Authentication properties. |
| anonymous_identity_ = ""; |
| cert_id_ = ""; |
| client_cert_ = ""; |
| identity_ = ""; |
| key_id_ = ""; |
| // Do not reset key_management_, since it should never be emptied. |
| password_ = ""; |
| pin_ = ""; |
| private_key_ = ""; |
| private_key_password_ = ""; |
| |
| // Non-authentication properties. |
| ca_cert_ = ""; |
| ca_cert_id_ = ""; |
| ca_cert_nss_ = ""; |
| ca_cert_pem_.clear(); |
| eap_ = ""; |
| inner_eap_ = ""; |
| subject_match_ = ""; |
| use_system_cas_ = true; |
| } |
| |
| bool EapCredentials::SetEapPassword(const string &password, Error */*error*/) { |
| if (password_ == password) { |
| return false; |
| } |
| password_ = password; |
| return true; |
| } |
| |
| bool EapCredentials::SetEapPrivateKeyPassword(const string &password, |
| Error */*error*/) { |
| if (private_key_password_ == password) { |
| return false; |
| } |
| private_key_password_ = password; |
| return true; |
| } |
| |
| string EapCredentials::GetKeyManagement(Error */*error*/) { |
| return key_management_; |
| } |
| |
| bool EapCredentials::SetKeyManagement(const std::string &key_management, |
| Error */*error*/) { |
| if (key_management.empty()) { |
| return false; |
| } |
| if (key_management_ == key_management) { |
| return false; |
| } |
| key_management_ = key_management; |
| return true; |
| } |
| |
| bool EapCredentials::ClientAuthenticationUsesCryptoToken() const { |
| return (eap_.empty() || eap_ == kEapMethodTLS || |
| inner_eap_ == kEapMethodTLS) && |
| (!cert_id_.empty() || !key_id_.empty()); |
| } |
| |
| void EapCredentials::HelpRegisterDerivedString( |
| PropertyStore *store, |
| const string &name, |
| string(EapCredentials::*get)(Error *error), |
| bool(EapCredentials::*set)(const string&, Error *)) { |
| store->RegisterDerivedString( |
| name, |
| StringAccessor(new CustomAccessor<EapCredentials, string>( |
| this, get, set))); |
| } |
| |
| void EapCredentials::HelpRegisterWriteOnlyDerivedString( |
| PropertyStore *store, |
| const string &name, |
| bool(EapCredentials::*set)(const string &, Error *), |
| void(EapCredentials::*clear)(Error *error), |
| const string *default_value) { |
| store->RegisterDerivedString( |
| name, |
| StringAccessor( |
| new CustomWriteOnlyAccessor<EapCredentials, string>( |
| this, set, clear, default_value))); |
| } |
| |
| } // namespace shill |