| // Copyright 2016 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 "authpolicy/authpolicy.h" |
| |
| #include <utility> |
| #include <vector> |
| |
| #include <base/strings/stringprintf.h> |
| #include <base/threading/thread_task_runner_handle.h> |
| #include <brillo/dbus/dbus_method_invoker.h> |
| #include <dbus/authpolicy/dbus-constants.h> |
| #include <dbus/login_manager/dbus-constants.h> |
| |
| #include "authpolicy/authpolicy_metrics.h" |
| #include "authpolicy/path_service.h" |
| #include "authpolicy/proto_bindings/active_directory_info.pb.h" |
| #include "authpolicy/samba_interface.h" |
| #include "bindings/device_management_backend.pb.h" |
| |
| namespace em = enterprise_management; |
| |
| using brillo::dbus_utils::DBusObject; |
| using brillo::dbus_utils::ExtractMethodCallResults; |
| |
| namespace authpolicy { |
| |
| const char kChromeUserPolicyType[] = "google/chromeos/user"; |
| const char kChromeDevicePolicyType[] = "google/chromeos/device"; |
| |
| namespace { |
| |
| void PrintError(const char* msg, ErrorType error) { |
| if (error == ERROR_NONE) |
| LOG(INFO) << msg << " succeeded"; |
| else |
| LOG(INFO) << msg << " failed with code " << error; |
| } |
| |
| const char* GetSessionManagerStoreMethod(bool is_user_policy) { |
| return is_user_policy |
| ? login_manager::kSessionManagerStoreUnsignedPolicyForUser |
| : login_manager::kSessionManagerStoreUnsignedPolicy; |
| } |
| |
| DBusCallType GetPolicyDBusCallType(bool is_user_policy) { |
| return is_user_policy ? DBUS_CALL_REFRESH_USER_POLICY |
| : DBUS_CALL_REFRESH_DEVICE_POLICY; |
| } |
| |
| // Serializes |proto| to the byte array |proto_blob|. Returns ERROR_NONE on |
| // success and ERROR_PARSE_FAILED otherwise. |
| template <typename ProtoType> |
| ErrorType SerializeProto(ProtoType proto, std::vector<uint8_t>* proto_blob) { |
| std::string buffer; |
| if (!proto.SerializeToString(&buffer)) { |
| LOG(ERROR) << "Failed to serialize proto"; |
| return ERROR_PARSE_FAILED; |
| } |
| proto_blob->assign(buffer.begin(), buffer.end()); |
| return ERROR_NONE; |
| } |
| |
| } // namespace |
| |
| // static |
| std::unique_ptr<DBusObject> AuthPolicy::GetDBusObject( |
| brillo::dbus_utils::ExportedObjectManager* object_manager) { |
| return std::make_unique<DBusObject>( |
| object_manager, |
| object_manager->GetBus(), |
| org::chromium::AuthPolicyAdaptor::GetObjectPath()); |
| } |
| |
| AuthPolicy::AuthPolicy(AuthPolicyMetrics* metrics, |
| const PathService* path_service) |
| : org::chromium::AuthPolicyAdaptor(this), |
| metrics_(metrics), |
| samba_(base::ThreadTaskRunnerHandle::Get(), |
| metrics, |
| path_service, |
| base::Bind(&AuthPolicy::OnUserKerberosFilesChanged, |
| base::Unretained(this))), |
| weak_ptr_factory_(this) {} |
| |
| ErrorType AuthPolicy::Initialize(bool expect_config) { |
| return samba_.Initialize(expect_config); |
| } |
| |
| void AuthPolicy::RegisterAsync( |
| std::unique_ptr<brillo::dbus_utils::DBusObject> dbus_object, |
| const AsyncEventSequencer::CompletionAction& completion_callback) { |
| DCHECK(!dbus_object_); |
| dbus_object_ = std::move(dbus_object); |
| // Make sure the task runner passed to |samba_| in the constructor is actually |
| // the D-Bus task runner. This guarantees that automatic TGT renewal won't |
| // interfere with D-Bus calls. Note that |GetDBusTaskRunner()| returns a |
| // TaskRunner, which is a base class of SingleThreadTaskRunner accepted by |
| // |samba_|. |
| CHECK_EQ(base::ThreadTaskRunnerHandle::Get(), |
| dbus_object_->GetBus()->GetDBusTaskRunner()); |
| RegisterWithDBusObject(dbus_object_.get()); |
| dbus_object_->RegisterAsync(completion_callback); |
| session_manager_proxy_ = dbus_object_->GetBus()->GetObjectProxy( |
| login_manager::kSessionManagerServiceName, |
| dbus::ObjectPath(login_manager::kSessionManagerServicePath)); |
| DCHECK(session_manager_proxy_); |
| } |
| |
| void AuthPolicy::AuthenticateUser(const std::string& user_principal_name, |
| const std::string& account_id, |
| const dbus::FileDescriptor& password_fd, |
| int32_t* int_error, |
| std::vector<uint8_t>* account_info_blob) { |
| LOG(INFO) << "Received 'AuthenticateUser' request"; |
| ScopedTimerReporter timer(TIMER_AUTHENTICATE_USER); |
| |
| authpolicy::ActiveDirectoryAccountInfo account_info; |
| ErrorType error = samba_.AuthenticateUser( |
| user_principal_name, account_id, password_fd.value(), &account_info); |
| if (error == ERROR_NONE) |
| error = SerializeProto(account_info, account_info_blob); |
| PrintError("AuthenticateUser", error); |
| metrics_->ReportDBusResult(DBUS_CALL_AUTHENTICATE_USER, error); |
| *int_error = static_cast<int>(error); |
| } |
| |
| void AuthPolicy::GetUserStatus(const std::string& account_id, |
| int32_t* int_error, |
| std::vector<uint8_t>* user_status_blob) { |
| LOG(INFO) << "Received 'GetUserStatus' request"; |
| ScopedTimerReporter timer(TIMER_GET_USER_STATUS); |
| |
| authpolicy::ActiveDirectoryUserStatus user_status; |
| ErrorType error = samba_.GetUserStatus(account_id, &user_status); |
| if (error == ERROR_NONE) |
| error = SerializeProto(user_status, user_status_blob); |
| PrintError("GetUserStatus", error); |
| metrics_->ReportDBusResult(DBUS_CALL_GET_USER_STATUS, error); |
| *int_error = static_cast<int>(error); |
| } |
| |
| void AuthPolicy::GetUserKerberosFiles( |
| const std::string& account_id, |
| int32_t* int_error, |
| std::vector<uint8_t>* kerberos_files_blob) { |
| LOG(INFO) << "Received 'GetUserKerberosFiles' request"; |
| ScopedTimerReporter timer(TIMER_GET_USER_KERBEROS_FILES); |
| |
| authpolicy::KerberosFiles kerberos_files; |
| ErrorType error = samba_.GetUserKerberosFiles(account_id, &kerberos_files); |
| if (error == ERROR_NONE) |
| error = SerializeProto(kerberos_files, kerberos_files_blob); |
| PrintError("GetUserKerberosFiles", error); |
| metrics_->ReportDBusResult(DBUS_CALL_GET_USER_KERBEROS_FILES, error); |
| *int_error = static_cast<int>(error); |
| } |
| |
| int32_t AuthPolicy::JoinADDomain(const std::string& machine_name, |
| const std::string& user_principal_name, |
| const dbus::FileDescriptor& password_fd) { |
| LOG(INFO) << "Received 'JoinADDomain' request"; |
| ScopedTimerReporter timer(TIMER_JOIN_AD_DOMAIN); |
| |
| ErrorType error = samba_.JoinMachine( |
| machine_name, user_principal_name, password_fd.value()); |
| PrintError("JoinADDomain", error); |
| metrics_->ReportDBusResult(DBUS_CALL_JOIN_AD_DOMAIN, error); |
| return error; |
| } |
| |
| void AuthPolicy::RefreshUserPolicy(PolicyResponseCallback callback, |
| const std::string& account_id_key) { |
| LOG(INFO) << "Received 'RefreshUserPolicy' request"; |
| auto timer = std::make_unique<ScopedTimerReporter>(TIMER_REFRESH_USER_POLICY); |
| |
| // Fetch GPOs for the current user. |
| protos::GpoPolicyData gpo_policy_data; |
| ErrorType error = samba_.FetchUserGpos(account_id_key, &gpo_policy_data); |
| PrintError("User policy fetch and parsing", error); |
| |
| // Return immediately on error. |
| if (error != ERROR_NONE) { |
| metrics_->ReportDBusResult(DBUS_CALL_REFRESH_USER_POLICY, error); |
| callback->Return(error); |
| return; |
| } |
| |
| // Send policy to Session Manager. |
| StorePolicy( |
| gpo_policy_data, &account_id_key, std::move(timer), std::move(callback)); |
| } |
| |
| void AuthPolicy::RefreshDevicePolicy(PolicyResponseCallback callback) { |
| LOG(INFO) << "Received 'RefreshDevicePolicy' request"; |
| auto timer = |
| std::make_unique<ScopedTimerReporter>(TIMER_REFRESH_DEVICE_POLICY); |
| |
| // Fetch GPOs for the device. |
| protos::GpoPolicyData gpo_policy_data; |
| ErrorType error = samba_.FetchDeviceGpos(&gpo_policy_data); |
| PrintError("Device policy fetch and parsing", error); |
| |
| // Return immediately on error. |
| if (error != ERROR_NONE) { |
| metrics_->ReportDBusResult(DBUS_CALL_REFRESH_DEVICE_POLICY, error); |
| callback->Return(error); |
| return; |
| } |
| |
| // Send policy to Session Manager. |
| StorePolicy(gpo_policy_data, nullptr, std::move(timer), std::move(callback)); |
| } |
| |
| std::string AuthPolicy::SetDefaultLogLevel(int32_t level) { |
| LOG(INFO) << "Received 'SetDefaultLogLevel' request"; |
| if (level < AuthPolicyFlags::kMinLevel || |
| level > AuthPolicyFlags::kMaxLevel) { |
| std::string message = base::StringPrintf("Level must be between %i and %i.", |
| AuthPolicyFlags::kMinLevel, |
| AuthPolicyFlags::kMaxLevel); |
| LOG(ERROR) << message; |
| return message; |
| } |
| samba_.SetDefaultLogLevel(static_cast<AuthPolicyFlags::DefaultLevel>(level)); |
| return std::string(); |
| } |
| |
| void AuthPolicy::OnUserKerberosFilesChanged() { |
| LOG(INFO) << "Firing signal UserKerberosFilesChanged"; |
| SendUserKerberosFilesChangedSignal(); |
| } |
| |
| void AuthPolicy::StorePolicy(const protos::GpoPolicyData& gpo_policy_data, |
| const std::string* account_id_key, |
| std::unique_ptr<ScopedTimerReporter> timer, |
| PolicyResponseCallback callback) { |
| // Note: Only policy_value required here, the other data only impacts |
| // signature, but since we don't sign, we don't need it. |
| const bool is_user_policy = account_id_key != nullptr; |
| const char* const policy_type = |
| is_user_policy ? kChromeUserPolicyType : kChromeDevicePolicyType; |
| |
| em::PolicyData em_policy_data; |
| em_policy_data.set_policy_value(gpo_policy_data.user_or_device_policy()); |
| em_policy_data.set_policy_type(policy_type); |
| em_policy_data.set_management_mode(em::PolicyData::ENTERPRISE_MANAGED); |
| // Note: No signature required here, Active Directory policy is unsigned! |
| |
| em::PolicyFetchResponse policy_response; |
| std::string response_blob; |
| if (!em_policy_data.SerializeToString( |
| policy_response.mutable_policy_data()) || |
| !policy_response.SerializeToString(&response_blob)) { |
| LOG(ERROR) << "Failed to serialize policy data"; |
| const DBusCallType call_type = GetPolicyDBusCallType(is_user_policy); |
| metrics_->ReportDBusResult(call_type, ERROR_STORE_POLICY_FAILED); |
| callback->Return(ERROR_STORE_POLICY_FAILED); |
| return; |
| } |
| |
| const char* const method = GetSessionManagerStoreMethod(is_user_policy); |
| dbus::MethodCall method_call(login_manager::kSessionManagerInterface, method); |
| dbus::MessageWriter writer(&method_call); |
| if (account_id_key) |
| writer.AppendString(*account_id_key); |
| writer.AppendArrayOfBytes( |
| reinterpret_cast<const uint8_t*>(response_blob.data()), |
| response_blob.size()); |
| session_manager_proxy_->CallMethod(&method_call, |
| dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, |
| base::Bind(&AuthPolicy::OnPolicyStored, |
| weak_ptr_factory_.GetWeakPtr(), |
| is_user_policy, |
| base::Passed(&timer), |
| base::Passed(&callback))); |
| } |
| |
| void AuthPolicy::OnPolicyStored( |
| bool is_user_policy, |
| std::unique_ptr<ScopedTimerReporter> /* timer */, |
| PolicyResponseCallback callback, |
| dbus::Response* response) { |
| const char* const method = GetSessionManagerStoreMethod(is_user_policy); |
| brillo::ErrorPtr brillo_error; |
| std::string msg; |
| if (!response) { |
| // In case of error, session_manager_proxy_ prints out the error string and |
| // response is empty. |
| msg = |
| base::StringPrintf("Call to %s failed. No response or error.", method); |
| } else if (!ExtractMethodCallResults(response, &brillo_error)) { |
| // Response is expected have no call results. |
| msg = base::StringPrintf( |
| "Call to %s failed. %s", |
| method, |
| brillo_error ? brillo_error->GetMessage().c_str() : "Unknown error."); |
| } |
| |
| ErrorType error; |
| if (!msg.empty()) { |
| LOG(ERROR) << msg; |
| error = ERROR_STORE_POLICY_FAILED; |
| } else { |
| LOG(INFO) << "Call to " << method << " succeeded."; |
| error = ERROR_NONE; |
| } |
| const DBusCallType call_type = GetPolicyDBusCallType(is_user_policy); |
| metrics_->ReportDBusResult(call_type, error); |
| callback->Return(error); |
| } |
| |
| } // namespace authpolicy |