526 lines
19 KiB
C++
526 lines
19 KiB
C++
//
|
|
// Copyright (C) 2013 The Android Open Source Project
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
|
|
#include "shill/eap_credentials.h"
|
|
|
|
#include <map>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#if defined(__ANDROID__)
|
|
#include <dbus/service_constants.h>
|
|
#else
|
|
#include <chromeos/dbus/service_constants.h>
|
|
#endif // __ANDROID__
|
|
|
|
#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/supplicant/wpa_supplicant.h"
|
|
|
|
using base::FilePath;
|
|
using std::map;
|
|
using std::string;
|
|
using std::vector;
|
|
|
|
using std::string;
|
|
|
|
namespace shill {
|
|
|
|
namespace Logging {
|
|
static auto kModuleLogScope = ScopeLogger::kService;
|
|
static string ObjectID(const EapCredentials* e) { return "(eap_credentials)"; }
|
|
}
|
|
|
|
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::kStorageEapUseProactiveKeyCaching[] =
|
|
"EAP.UseProactiveKeyCaching";
|
|
const char EapCredentials::kStorageEapUseSystemCAs[] = "EAP.UseSystemCAs";
|
|
|
|
EapCredentials::EapCredentials() : use_system_cas_(true),
|
|
use_proactive_key_caching_(false) {}
|
|
|
|
EapCredentials::~EapCredentials() {}
|
|
|
|
// static
|
|
void EapCredentials::PopulateSupplicantProperties(
|
|
CertificateFile* certificate_file, KeyValueStore* 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->SetUint(WPASupplicant::kNetworkPropertyEngine,
|
|
WPASupplicant::kDefaultEngine);
|
|
}
|
|
|
|
if (use_proactive_key_caching_) {
|
|
params->SetUint(WPASupplicant::kNetworkPropertyEapProactiveKeyCaching,
|
|
WPASupplicant::kProactiveKeyCachingEnabled);
|
|
} else {
|
|
params->SetUint(WPASupplicant::kNetworkPropertyEapProactiveKeyCaching,
|
|
WPASupplicant::kProactiveKeyCachingDisabled);
|
|
}
|
|
|
|
for (const auto& keyval : propertyvals) {
|
|
if (strlen(keyval.second) > 0) {
|
|
params->SetString(keyval.first, 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,
|
|
nullptr,
|
|
&password_);
|
|
store->RegisterString(kEapPinProperty, &pin_);
|
|
store->RegisterString(kEapPrivateKeyProperty, &private_key_);
|
|
HelpRegisterWriteOnlyDerivedString(store,
|
|
kEapPrivateKeyPasswordProperty,
|
|
&EapCredentials::SetEapPrivateKeyPassword,
|
|
nullptr,
|
|
&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(kEapUseProactiveKeyCachingProperty,
|
|
&use_proactive_key_caching_);
|
|
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(this, 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(this, 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(this, 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(this, 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(this, 2) << "Connectable. !EAP-TLS and has a password.";
|
|
return true;
|
|
}
|
|
}
|
|
|
|
SLOG(this, 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, nullptr);
|
|
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, kStorageEapUseProactiveKeyCaching,
|
|
&use_proactive_key_caching_);
|
|
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, kStorageEapUseProactiveKeyCaching,
|
|
use_proactive_key_caching_);
|
|
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;
|
|
use_proactive_key_caching_ = false;
|
|
}
|
|
|
|
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
|