| // Copyright (c) 2011 The Chromium 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 "chrome/browser/policy/device_management_backend_impl.h" |
| |
| #include <utility> |
| #include <vector> |
| |
| #if defined(OS_POSIX) && !defined(OS_MACOSX) |
| #include <sys/utsname.h> |
| #endif |
| |
| #include "base/stringprintf.h" |
| #include "base/sys_info.h" |
| #include "chrome/browser/policy/device_management_service.h" |
| #include "chrome/common/chrome_version_info.h" |
| #include "net/base/escape.h" |
| #include "net/url_request/url_request_status.h" |
| |
| #if defined(OS_CHROMEOS) |
| #include "chrome/browser/chromeos/system_access.h" |
| #endif |
| |
| namespace policy { |
| |
| // Name constants for URL query parameters. |
| const char DeviceManagementBackendImpl::kParamRequest[] = "request"; |
| const char DeviceManagementBackendImpl::kParamDeviceType[] = "devicetype"; |
| const char DeviceManagementBackendImpl::kParamAppType[] = "apptype"; |
| const char DeviceManagementBackendImpl::kParamDeviceID[] = "deviceid"; |
| const char DeviceManagementBackendImpl::kParamAgent[] = "agent"; |
| const char DeviceManagementBackendImpl::kParamPlatform[] = "platform"; |
| |
| // String constants for the device and app type we report to the server. |
| const char DeviceManagementBackendImpl::kValueRequestRegister[] = "register"; |
| const char DeviceManagementBackendImpl::kValueRequestUnregister[] = |
| "unregister"; |
| const char DeviceManagementBackendImpl::kValueRequestPolicy[] = "policy"; |
| const char DeviceManagementBackendImpl::kValueDeviceType[] = "2"; |
| const char DeviceManagementBackendImpl::kValueAppType[] = "Chrome"; |
| |
| namespace { |
| |
| const char kValueAgent[] = "%s %s(%s)"; |
| const char kValuePlatform[] = "%s|%s|%s"; |
| |
| const char kPostContentType[] = "application/protobuf"; |
| |
| const char kServiceTokenAuthHeader[] = "Authorization: GoogleLogin auth="; |
| const char kDMTokenAuthHeader[] = "Authorization: GoogleDMToken token="; |
| |
| // HTTP Error Codes of the DM Server with their concrete meinings in the context |
| // of the DM Server communication. |
| const int kSuccess = 200; |
| const int kInvalidArgument = 400; |
| const int kInvalidAuthCookieOrDMToken = 401; |
| const int kDeviceManagementNotAllowed = 403; |
| const int kInvalidURL = 404; // This error is not coming from the GFE. |
| const int kPendingApproval = 491; |
| const int kInternalServerError = 500; |
| const int kServiceUnavailable = 503; |
| const int kDeviceNotFound = 901; |
| const int kPolicyNotFound = 902; // This error is not sent as HTTP status code. |
| |
| #if defined(OS_CHROMEOS) |
| // Machine info keys. |
| const char kMachineInfoHWClass[] = "hardware_class"; |
| const char kMachineInfoBoard[] = "CHROMEOS_RELEASE_BOARD"; |
| #endif |
| |
| } // namespace |
| |
| // Helper class for URL query parameter encoding/decoding. |
| class URLQueryParameters { |
| public: |
| URLQueryParameters() {} |
| |
| // Add a query parameter. |
| void Put(const std::string& name, const std::string& value); |
| |
| // Produce the query string, taking care of properly encoding and assembling |
| // the names and values. |
| std::string Encode(); |
| |
| private: |
| typedef std::vector<std::pair<std::string, std::string> > ParameterMap; |
| ParameterMap params_; |
| |
| DISALLOW_COPY_AND_ASSIGN(URLQueryParameters); |
| }; |
| |
| void URLQueryParameters::Put(const std::string& name, |
| const std::string& value) { |
| params_.push_back(std::make_pair(name, value)); |
| } |
| |
| std::string URLQueryParameters::Encode() { |
| std::string result; |
| for (ParameterMap::const_iterator entry(params_.begin()); |
| entry != params_.end(); |
| ++entry) { |
| if (entry != params_.begin()) |
| result += '&'; |
| result += EscapeQueryParamValue(entry->first, true); |
| result += '='; |
| result += EscapeQueryParamValue(entry->second, true); |
| } |
| return result; |
| } |
| |
| // A base class containing the common code for the jobs created by the backend |
| // implementation. Subclasses provide custom code for handling actual register, |
| // unregister, and policy jobs. |
| class DeviceManagementJobBase |
| : public DeviceManagementService::DeviceManagementJob { |
| public: |
| virtual ~DeviceManagementJobBase() {} |
| |
| // DeviceManagementJob overrides: |
| virtual void HandleResponse(const net::URLRequestStatus& status, |
| int response_code, |
| const ResponseCookies& cookies, |
| const std::string& data); |
| virtual GURL GetURL(const std::string& server_url); |
| virtual void ConfigureRequest(URLFetcher* fetcher); |
| |
| protected: |
| // Constructs a device management job running for the given backend. |
| DeviceManagementJobBase(DeviceManagementBackendImpl* backend_impl, |
| const std::string& request_type, |
| const std::string& device_id) |
| : backend_impl_(backend_impl) { |
| query_params_.Put(DeviceManagementBackendImpl::kParamRequest, request_type); |
| query_params_.Put(DeviceManagementBackendImpl::kParamDeviceType, |
| DeviceManagementBackendImpl::kValueDeviceType); |
| query_params_.Put(DeviceManagementBackendImpl::kParamAppType, |
| DeviceManagementBackendImpl::kValueAppType); |
| query_params_.Put(DeviceManagementBackendImpl::kParamDeviceID, device_id); |
| query_params_.Put(DeviceManagementBackendImpl::kParamAgent, |
| DeviceManagementBackendImpl::GetAgentString()); |
| query_params_.Put(DeviceManagementBackendImpl::kParamPlatform, |
| DeviceManagementBackendImpl::GetPlatformString()); |
| } |
| |
| void SetQueryParam(const std::string& name, const std::string& value) { |
| query_params_.Put(name, value); |
| } |
| |
| void SetAuthToken(const std::string& auth_token) { |
| auth_token_ = auth_token; |
| } |
| |
| void SetDeviceManagementToken(const std::string& device_management_token) { |
| device_management_token_ = device_management_token; |
| } |
| |
| void SetPayload(const em::DeviceManagementRequest& request) { |
| if (!request.SerializeToString(&payload_)) { |
| NOTREACHED(); |
| LOG(ERROR) << "Failed to serialize request."; |
| } |
| } |
| |
| private: |
| // Implemented by subclasses to handle decoded responses and errors. |
| virtual void OnResponse( |
| const em::DeviceManagementResponse& response) = 0; |
| virtual void OnError(DeviceManagementBackend::ErrorCode error) = 0; |
| |
| // The backend this job is handling a request for. |
| DeviceManagementBackendImpl* backend_impl_; |
| |
| // Query parameters. |
| URLQueryParameters query_params_; |
| |
| // Auth token (if applicaple). |
| std::string auth_token_; |
| |
| // Device management token (if applicable). |
| std::string device_management_token_; |
| |
| // The payload. |
| std::string payload_; |
| |
| DISALLOW_COPY_AND_ASSIGN(DeviceManagementJobBase); |
| }; |
| |
| void DeviceManagementJobBase::HandleResponse( |
| const net::URLRequestStatus& status, |
| int response_code, |
| const ResponseCookies& cookies, |
| const std::string& data) { |
| // Delete ourselves when this is done. |
| scoped_ptr<DeviceManagementJob> scoped_killer(this); |
| backend_impl_->JobDone(this); |
| backend_impl_ = NULL; |
| |
| if (status.status() != net::URLRequestStatus::SUCCESS) { |
| OnError(DeviceManagementBackend::kErrorRequestFailed); |
| return; |
| } |
| |
| switch (response_code) { |
| case kSuccess: { |
| em::DeviceManagementResponse response; |
| if (!response.ParseFromString(data)) { |
| OnError(DeviceManagementBackend::kErrorResponseDecoding); |
| return; |
| } |
| OnResponse(response); |
| return; |
| } |
| case kInvalidArgument: { |
| OnError(DeviceManagementBackend::kErrorRequestInvalid); |
| return; |
| } |
| case kInvalidAuthCookieOrDMToken: { |
| OnError(DeviceManagementBackend::kErrorServiceManagementTokenInvalid); |
| return; |
| } |
| case kDeviceManagementNotAllowed: { |
| OnError(DeviceManagementBackend::kErrorServiceManagementNotSupported); |
| return; |
| } |
| case kPendingApproval: { |
| OnError(DeviceManagementBackend::kErrorServiceActivationPending); |
| return; |
| } |
| case kInvalidURL: |
| case kInternalServerError: |
| case kServiceUnavailable: { |
| OnError(DeviceManagementBackend::kErrorTemporaryUnavailable); |
| return; |
| } |
| case kDeviceNotFound: { |
| OnError(DeviceManagementBackend::kErrorServiceDeviceNotFound); |
| return; |
| } |
| case kPolicyNotFound: { |
| OnError(DeviceManagementBackend::kErrorServicePolicyNotFound); |
| break; |
| } |
| default: { |
| VLOG(1) << "Unexpected HTTP status in response from DMServer : " |
| << response_code << "."; |
| // Handle all unknown 5xx HTTP error codes as temporary and any other |
| // unknown error as one that needs more time to recover. |
| if (response_code >= 500 && response_code <= 599) |
| OnError(DeviceManagementBackend::kErrorTemporaryUnavailable); |
| else |
| OnError(DeviceManagementBackend::kErrorHttpStatus); |
| return; |
| } |
| } |
| } |
| |
| GURL DeviceManagementJobBase::GetURL( |
| const std::string& server_url) { |
| return GURL(server_url + '?' + query_params_.Encode()); |
| } |
| |
| void DeviceManagementJobBase::ConfigureRequest(URLFetcher* fetcher) { |
| fetcher->set_upload_data(kPostContentType, payload_); |
| std::string extra_headers; |
| if (!auth_token_.empty()) |
| extra_headers += kServiceTokenAuthHeader + auth_token_ + "\n"; |
| if (!device_management_token_.empty()) |
| extra_headers += kDMTokenAuthHeader + device_management_token_ + "\n"; |
| fetcher->set_extra_request_headers(extra_headers); |
| } |
| |
| // Handles device registration jobs. |
| class DeviceManagementRegisterJob : public DeviceManagementJobBase { |
| public: |
| DeviceManagementRegisterJob( |
| DeviceManagementBackendImpl* backend_impl, |
| const std::string& auth_token, |
| const std::string& device_id, |
| const em::DeviceRegisterRequest& request, |
| DeviceManagementBackend::DeviceRegisterResponseDelegate* delegate) |
| : DeviceManagementJobBase( |
| backend_impl, |
| DeviceManagementBackendImpl::kValueRequestRegister, |
| device_id), |
| delegate_(delegate) { |
| SetAuthToken(auth_token); |
| em::DeviceManagementRequest request_wrapper; |
| request_wrapper.mutable_register_request()->CopyFrom(request); |
| SetPayload(request_wrapper); |
| } |
| virtual ~DeviceManagementRegisterJob() {} |
| |
| private: |
| // DeviceManagementJobBase overrides. |
| virtual void OnError(DeviceManagementBackend::ErrorCode error) { |
| delegate_->OnError(error); |
| } |
| virtual void OnResponse(const em::DeviceManagementResponse& response) { |
| delegate_->HandleRegisterResponse(response.register_response()); |
| } |
| |
| DeviceManagementBackend::DeviceRegisterResponseDelegate* delegate_; |
| |
| DISALLOW_COPY_AND_ASSIGN(DeviceManagementRegisterJob); |
| }; |
| |
| // Handles device unregistration jobs. |
| class DeviceManagementUnregisterJob : public DeviceManagementJobBase { |
| public: |
| DeviceManagementUnregisterJob( |
| DeviceManagementBackendImpl* backend_impl, |
| const std::string& device_management_token, |
| const std::string& device_id, |
| const em::DeviceUnregisterRequest& request, |
| DeviceManagementBackend::DeviceUnregisterResponseDelegate* delegate) |
| : DeviceManagementJobBase( |
| backend_impl, |
| DeviceManagementBackendImpl::kValueRequestUnregister, |
| device_id), |
| delegate_(delegate) { |
| SetDeviceManagementToken(device_management_token); |
| em::DeviceManagementRequest request_wrapper; |
| request_wrapper.mutable_unregister_request()->CopyFrom(request); |
| SetPayload(request_wrapper); |
| } |
| virtual ~DeviceManagementUnregisterJob() {} |
| |
| private: |
| // DeviceManagementJobBase overrides. |
| virtual void OnError(DeviceManagementBackend::ErrorCode error) { |
| delegate_->OnError(error); |
| } |
| virtual void OnResponse(const em::DeviceManagementResponse& response) { |
| delegate_->HandleUnregisterResponse(response.unregister_response()); |
| } |
| |
| DeviceManagementBackend::DeviceUnregisterResponseDelegate* delegate_; |
| |
| DISALLOW_COPY_AND_ASSIGN(DeviceManagementUnregisterJob); |
| }; |
| |
| // Handles policy request jobs. |
| class DeviceManagementPolicyJob : public DeviceManagementJobBase { |
| public: |
| DeviceManagementPolicyJob( |
| DeviceManagementBackendImpl* backend_impl, |
| const std::string& device_management_token, |
| const std::string& device_id, |
| const em::DevicePolicyRequest& request, |
| DeviceManagementBackend::DevicePolicyResponseDelegate* delegate) |
| : DeviceManagementJobBase( |
| backend_impl, |
| DeviceManagementBackendImpl::kValueRequestPolicy, |
| device_id), |
| delegate_(delegate) { |
| SetDeviceManagementToken(device_management_token); |
| em::DeviceManagementRequest request_wrapper; |
| request_wrapper.mutable_policy_request()->CopyFrom(request); |
| SetPayload(request_wrapper); |
| } |
| virtual ~DeviceManagementPolicyJob() {} |
| |
| private: |
| // DeviceManagementJobBase overrides. |
| virtual void OnError(DeviceManagementBackend::ErrorCode error) { |
| delegate_->OnError(error); |
| } |
| virtual void OnResponse(const em::DeviceManagementResponse& response) { |
| delegate_->HandlePolicyResponse(response.policy_response()); |
| } |
| |
| DeviceManagementBackend::DevicePolicyResponseDelegate* delegate_; |
| |
| DISALLOW_COPY_AND_ASSIGN(DeviceManagementPolicyJob); |
| }; |
| |
| DeviceManagementBackendImpl::DeviceManagementBackendImpl( |
| DeviceManagementService* service) |
| : service_(service) { |
| } |
| |
| DeviceManagementBackendImpl::~DeviceManagementBackendImpl() { |
| for (JobSet::iterator job(pending_jobs_.begin()); |
| job != pending_jobs_.end(); |
| ++job) { |
| service_->RemoveJob(*job); |
| delete *job; |
| } |
| pending_jobs_.clear(); |
| } |
| |
| std::string DeviceManagementBackendImpl::GetAgentString() { |
| static std::string agent; |
| if (!agent.empty()) |
| return agent; |
| |
| chrome::VersionInfo version_info; |
| agent = base::StringPrintf(kValueAgent, |
| version_info.Name().c_str(), |
| version_info.Version().c_str(), |
| version_info.LastChange().c_str()); |
| return agent; |
| } |
| |
| std::string DeviceManagementBackendImpl::GetPlatformString() { |
| static std::string platform; |
| if (!platform.empty()) |
| return platform; |
| |
| std::string os_name(base::SysInfo::OperatingSystemName()); |
| std::string os_hardware(base::SysInfo::CPUArchitecture()); |
| |
| #if defined(OS_CHROMEOS) |
| chromeos::SystemAccess* sys_lib = chromeos::SystemAccess::GetInstance(); |
| |
| std::string hwclass; |
| std::string board; |
| if (!sys_lib->GetMachineStatistic(kMachineInfoHWClass, &hwclass) || |
| !sys_lib->GetMachineStatistic(kMachineInfoBoard, &board)) { |
| LOG(ERROR) << "Failed to get machine information"; |
| } |
| os_name += ",CrOS," + board; |
| os_hardware += "," + hwclass; |
| #endif |
| |
| std::string os_version("-"); |
| #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS) |
| int32 os_major_version = 0; |
| int32 os_minor_version = 0; |
| int32 os_bugfix_version = 0; |
| base::SysInfo::OperatingSystemVersionNumbers(&os_major_version, |
| &os_minor_version, |
| &os_bugfix_version); |
| os_version = base::StringPrintf("%d.%d.%d", |
| os_major_version, |
| os_minor_version, |
| os_bugfix_version); |
| #endif |
| |
| platform = base::StringPrintf(kValuePlatform, |
| os_name.c_str(), |
| os_hardware.c_str(), |
| os_version.c_str()); |
| return platform; |
| } |
| |
| void DeviceManagementBackendImpl::JobDone(DeviceManagementJobBase* job) { |
| pending_jobs_.erase(job); |
| } |
| |
| void DeviceManagementBackendImpl::AddJob(DeviceManagementJobBase* job) { |
| pending_jobs_.insert(job); |
| service_->AddJob(job); |
| } |
| |
| void DeviceManagementBackendImpl::ProcessRegisterRequest( |
| const std::string& auth_token, |
| const std::string& device_id, |
| const em::DeviceRegisterRequest& request, |
| DeviceRegisterResponseDelegate* delegate) { |
| AddJob(new DeviceManagementRegisterJob(this, auth_token, device_id, request, |
| delegate)); |
| } |
| |
| void DeviceManagementBackendImpl::ProcessUnregisterRequest( |
| const std::string& device_management_token, |
| const std::string& device_id, |
| const em::DeviceUnregisterRequest& request, |
| DeviceUnregisterResponseDelegate* delegate) { |
| AddJob(new DeviceManagementUnregisterJob(this, device_management_token, |
| device_id, request, delegate)); |
| } |
| |
| void DeviceManagementBackendImpl::ProcessPolicyRequest( |
| const std::string& device_management_token, |
| const std::string& device_id, |
| const em::DevicePolicyRequest& request, |
| DevicePolicyResponseDelegate* delegate) { |
| AddJob(new DeviceManagementPolicyJob(this, device_management_token, device_id, |
| request, delegate)); |
| } |
| |
| } // namespace policy |