Home | History | Annotate | Download | only in cloud
      1 // Copyright (c) 2012 The Chromium 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 "components/policy/core/common/cloud/device_management_service.h"
      6 
      7 #include <utility>
      8 
      9 #include "base/bind.h"
     10 #include "base/compiler_specific.h"
     11 #include "base/message_loop/message_loop.h"
     12 #include "base/message_loop/message_loop_proxy.h"
     13 #include "net/base/escape.h"
     14 #include "net/base/load_flags.h"
     15 #include "net/base/net_errors.h"
     16 #include "net/http/http_response_headers.h"
     17 #include "net/url_request/url_fetcher.h"
     18 #include "net/url_request/url_request_status.h"
     19 #include "url/gurl.h"
     20 
     21 namespace em = enterprise_management;
     22 
     23 namespace policy {
     24 
     25 namespace {
     26 
     27 const char kPostContentType[] = "application/protobuf";
     28 
     29 const char kServiceTokenAuthHeader[] = "Authorization: GoogleLogin auth=";
     30 const char kDMTokenAuthHeader[] = "Authorization: GoogleDMToken token=";
     31 
     32 // Number of times to retry on ERR_NETWORK_CHANGED errors.
     33 const int kMaxNetworkChangedRetries = 3;
     34 
     35 // HTTP Error Codes of the DM Server with their concrete meanings in the context
     36 // of the DM Server communication.
     37 const int kSuccess = 200;
     38 const int kInvalidArgument = 400;
     39 const int kInvalidAuthCookieOrDMToken = 401;
     40 const int kMissingLicenses = 402;
     41 const int kDeviceManagementNotAllowed = 403;
     42 const int kInvalidURL = 404;  // This error is not coming from the GFE.
     43 const int kInvalidSerialNumber = 405;
     44 const int kDeviceIdConflict = 409;
     45 const int kDeviceNotFound = 410;
     46 const int kPendingApproval = 412;
     47 const int kInternalServerError = 500;
     48 const int kServiceUnavailable = 503;
     49 const int kPolicyNotFound = 902;
     50 const int kDeprovisioned = 903;
     51 
     52 bool IsProxyError(const net::URLRequestStatus status) {
     53   switch (status.error()) {
     54     case net::ERR_PROXY_CONNECTION_FAILED:
     55     case net::ERR_TUNNEL_CONNECTION_FAILED:
     56     case net::ERR_PROXY_AUTH_UNSUPPORTED:
     57     case net::ERR_HTTPS_PROXY_TUNNEL_RESPONSE:
     58     case net::ERR_MANDATORY_PROXY_CONFIGURATION_FAILED:
     59     case net::ERR_PROXY_CERTIFICATE_INVALID:
     60     case net::ERR_SOCKS_CONNECTION_FAILED:
     61     case net::ERR_SOCKS_CONNECTION_HOST_UNREACHABLE:
     62       return true;
     63   }
     64   return false;
     65 }
     66 
     67 bool IsProtobufMimeType(const net::URLFetcher* fetcher) {
     68   return fetcher->GetResponseHeaders()->HasHeaderValue(
     69       "content-type", "application/x-protobuffer");
     70 }
     71 
     72 bool FailedWithProxy(const net::URLFetcher* fetcher) {
     73   if ((fetcher->GetLoadFlags() & net::LOAD_BYPASS_PROXY) != 0) {
     74     // The request didn't use a proxy.
     75     return false;
     76   }
     77 
     78   if (!fetcher->GetStatus().is_success() &&
     79       IsProxyError(fetcher->GetStatus())) {
     80     LOG(WARNING) << "Proxy failed while contacting dmserver.";
     81     return true;
     82   }
     83 
     84   if (fetcher->GetStatus().is_success() &&
     85       fetcher->GetResponseCode() == kSuccess &&
     86       fetcher->WasFetchedViaProxy() &&
     87       !IsProtobufMimeType(fetcher)) {
     88     // The proxy server can be misconfigured but pointing to an existing
     89     // server that replies to requests. Try to recover if a successful
     90     // request that went through a proxy returns an unexpected mime type.
     91     LOG(WARNING) << "Got bad mime-type in response from dmserver that was "
     92                  << "fetched via a proxy.";
     93     return true;
     94   }
     95 
     96   return false;
     97 }
     98 
     99 const char* UserAffiliationToString(UserAffiliation affiliation) {
    100   switch (affiliation) {
    101     case USER_AFFILIATION_MANAGED:
    102       return dm_protocol::kValueUserAffiliationManaged;
    103     case USER_AFFILIATION_NONE:
    104       return dm_protocol::kValueUserAffiliationNone;
    105   }
    106   NOTREACHED() << "Invalid user affiliation " << affiliation;
    107   return dm_protocol::kValueUserAffiliationNone;
    108 }
    109 
    110 const char* JobTypeToRequestType(DeviceManagementRequestJob::JobType type) {
    111   switch (type) {
    112     case DeviceManagementRequestJob::TYPE_AUTO_ENROLLMENT:
    113       return dm_protocol::kValueRequestAutoEnrollment;
    114     case DeviceManagementRequestJob::TYPE_REGISTRATION:
    115       return dm_protocol::kValueRequestRegister;
    116     case DeviceManagementRequestJob::TYPE_POLICY_FETCH:
    117       return dm_protocol::kValueRequestPolicy;
    118     case DeviceManagementRequestJob::TYPE_API_AUTH_CODE_FETCH:
    119       return dm_protocol::kValueRequestApiAuthorization;
    120     case DeviceManagementRequestJob::TYPE_UNREGISTRATION:
    121       return dm_protocol::kValueRequestUnregister;
    122     case DeviceManagementRequestJob::TYPE_UPLOAD_CERTIFICATE:
    123       return dm_protocol::kValueRequestUploadCertificate;
    124   }
    125   NOTREACHED() << "Invalid job type " << type;
    126   return "";
    127 }
    128 
    129 }  // namespace
    130 
    131 // Request job implementation used with DeviceManagementService.
    132 class DeviceManagementRequestJobImpl : public DeviceManagementRequestJob {
    133  public:
    134   DeviceManagementRequestJobImpl(
    135       JobType type,
    136       const std::string& agent_parameter,
    137       const std::string& platform_parameter,
    138       DeviceManagementService* service,
    139       net::URLRequestContextGetter* request_context);
    140   virtual ~DeviceManagementRequestJobImpl();
    141 
    142   // Handles the URL request response.
    143   void HandleResponse(const net::URLRequestStatus& status,
    144                       int response_code,
    145                       const net::ResponseCookies& cookies,
    146                       const std::string& data);
    147 
    148   // Gets the URL to contact.
    149   GURL GetURL(const std::string& server_url);
    150 
    151   // Configures the fetcher, setting up payload and headers.
    152   void ConfigureRequest(net::URLFetcher* fetcher);
    153 
    154   // Returns true if this job should be retried. |fetcher| has just completed,
    155   // and can be inspected to determine if the request failed and should be
    156   // retried.
    157   bool ShouldRetry(const net::URLFetcher* fetcher);
    158 
    159   // Invoked right before retrying this job.
    160   void PrepareRetry();
    161 
    162  protected:
    163   // DeviceManagementRequestJob:
    164   virtual void Run() OVERRIDE;
    165 
    166  private:
    167   // Invokes the callback with the given error code.
    168   void ReportError(DeviceManagementStatus code);
    169 
    170   // Pointer to the service this job is associated with.
    171   DeviceManagementService* service_;
    172 
    173   // Whether the BYPASS_PROXY flag should be set by ConfigureRequest().
    174   bool bypass_proxy_;
    175 
    176   // Number of times that this job has been retried due to ERR_NETWORK_CHANGED.
    177   int retries_count_;
    178 
    179   // The request context to use for this job.
    180   net::URLRequestContextGetter* request_context_;
    181 
    182   DISALLOW_COPY_AND_ASSIGN(DeviceManagementRequestJobImpl);
    183 };
    184 
    185 DeviceManagementRequestJobImpl::DeviceManagementRequestJobImpl(
    186     JobType type,
    187     const std::string& agent_parameter,
    188     const std::string& platform_parameter,
    189     DeviceManagementService* service,
    190     net::URLRequestContextGetter* request_context)
    191     : DeviceManagementRequestJob(type, agent_parameter, platform_parameter),
    192       service_(service),
    193       bypass_proxy_(false),
    194       retries_count_(0),
    195       request_context_(request_context) {}
    196 
    197 DeviceManagementRequestJobImpl::~DeviceManagementRequestJobImpl() {
    198   service_->RemoveJob(this);
    199 }
    200 
    201 void DeviceManagementRequestJobImpl::Run() {
    202   service_->AddJob(this);
    203 }
    204 
    205 void DeviceManagementRequestJobImpl::HandleResponse(
    206     const net::URLRequestStatus& status,
    207     int response_code,
    208     const net::ResponseCookies& cookies,
    209     const std::string& data) {
    210   if (status.status() != net::URLRequestStatus::SUCCESS) {
    211     LOG(WARNING) << "DMServer request failed, status: " << status.status()
    212                  << ", error: " << status.error();
    213     em::DeviceManagementResponse dummy_response;
    214     callback_.Run(DM_STATUS_REQUEST_FAILED, status.error(), dummy_response);
    215     return;
    216   }
    217 
    218   if (response_code != kSuccess)
    219     LOG(WARNING) << "DMServer sent an error response: " << response_code;
    220 
    221   switch (response_code) {
    222     case kSuccess: {
    223       em::DeviceManagementResponse response;
    224       if (!response.ParseFromString(data)) {
    225         ReportError(DM_STATUS_RESPONSE_DECODING_ERROR);
    226         return;
    227       }
    228       callback_.Run(DM_STATUS_SUCCESS, net::OK, response);
    229       return;
    230     }
    231     case kInvalidArgument:
    232       ReportError(DM_STATUS_REQUEST_INVALID);
    233       return;
    234     case kInvalidAuthCookieOrDMToken:
    235       ReportError(DM_STATUS_SERVICE_MANAGEMENT_TOKEN_INVALID);
    236       return;
    237     case kMissingLicenses:
    238       ReportError(DM_STATUS_SERVICE_MISSING_LICENSES);
    239       return;
    240     case kDeviceManagementNotAllowed:
    241       ReportError(DM_STATUS_SERVICE_MANAGEMENT_NOT_SUPPORTED);
    242       return;
    243     case kPendingApproval:
    244       ReportError(DM_STATUS_SERVICE_ACTIVATION_PENDING);
    245       return;
    246     case kInvalidURL:
    247     case kInternalServerError:
    248     case kServiceUnavailable:
    249       ReportError(DM_STATUS_TEMPORARY_UNAVAILABLE);
    250       return;
    251     case kDeviceNotFound:
    252       ReportError(DM_STATUS_SERVICE_DEVICE_NOT_FOUND);
    253       return;
    254     case kPolicyNotFound:
    255       ReportError(DM_STATUS_SERVICE_POLICY_NOT_FOUND);
    256       return;
    257     case kInvalidSerialNumber:
    258       ReportError(DM_STATUS_SERVICE_INVALID_SERIAL_NUMBER);
    259       return;
    260     case kDeprovisioned:
    261       ReportError(DM_STATUS_SERVICE_DEPROVISIONED);
    262       return;
    263     case kDeviceIdConflict:
    264       ReportError(DM_STATUS_SERVICE_DEVICE_ID_CONFLICT);
    265       return;
    266     default:
    267       // Handle all unknown 5xx HTTP error codes as temporary and any other
    268       // unknown error as one that needs more time to recover.
    269       if (response_code >= 500 && response_code <= 599)
    270         ReportError(DM_STATUS_TEMPORARY_UNAVAILABLE);
    271       else
    272         ReportError(DM_STATUS_HTTP_STATUS_ERROR);
    273       return;
    274   }
    275 }
    276 
    277 GURL DeviceManagementRequestJobImpl::GetURL(
    278     const std::string& server_url) {
    279   std::string result(server_url);
    280   result += '?';
    281   for (ParameterMap::const_iterator entry(query_params_.begin());
    282        entry != query_params_.end();
    283        ++entry) {
    284     if (entry != query_params_.begin())
    285       result += '&';
    286     result += net::EscapeQueryParamValue(entry->first, true);
    287     result += '=';
    288     result += net::EscapeQueryParamValue(entry->second, true);
    289   }
    290   return GURL(result);
    291 }
    292 
    293 void DeviceManagementRequestJobImpl::ConfigureRequest(
    294     net::URLFetcher* fetcher) {
    295   fetcher->SetRequestContext(request_context_);
    296   fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
    297                         net::LOAD_DO_NOT_SAVE_COOKIES |
    298                         net::LOAD_DISABLE_CACHE |
    299                         (bypass_proxy_ ? net::LOAD_BYPASS_PROXY : 0));
    300   std::string payload;
    301   CHECK(request_.SerializeToString(&payload));
    302   fetcher->SetUploadData(kPostContentType, payload);
    303   std::string extra_headers;
    304   if (!gaia_token_.empty())
    305     extra_headers += kServiceTokenAuthHeader + gaia_token_ + "\n";
    306   if (!dm_token_.empty())
    307     extra_headers += kDMTokenAuthHeader + dm_token_ + "\n";
    308   fetcher->SetExtraRequestHeaders(extra_headers);
    309 }
    310 
    311 bool DeviceManagementRequestJobImpl::ShouldRetry(
    312     const net::URLFetcher* fetcher) {
    313   if (FailedWithProxy(fetcher) && !bypass_proxy_) {
    314     // Retry the job if it failed due to a broken proxy, by bypassing the
    315     // proxy on the next try.
    316     bypass_proxy_ = true;
    317     return true;
    318   }
    319 
    320   // Early device policy fetches on ChromeOS and Auto-Enrollment checks are
    321   // often interrupted during ChromeOS startup when network change notifications
    322   // are sent. Allowing the fetcher to retry once after that is enough to
    323   // recover; allow it to retry up to 3 times just in case.
    324   if (fetcher->GetStatus().error() == net::ERR_NETWORK_CHANGED &&
    325       retries_count_ < kMaxNetworkChangedRetries) {
    326     ++retries_count_;
    327     return true;
    328   }
    329 
    330   // The request didn't fail, or the limit of retry attempts has been reached;
    331   // forward the result to the job owner.
    332   return false;
    333 }
    334 
    335 void DeviceManagementRequestJobImpl::PrepareRetry() {
    336   if (!retry_callback_.is_null())
    337     retry_callback_.Run(this);
    338 }
    339 
    340 void DeviceManagementRequestJobImpl::ReportError(DeviceManagementStatus code) {
    341   em::DeviceManagementResponse dummy_response;
    342   callback_.Run(code, net::OK, dummy_response);
    343 }
    344 
    345 DeviceManagementRequestJob::~DeviceManagementRequestJob() {}
    346 
    347 void DeviceManagementRequestJob::SetGaiaToken(const std::string& gaia_token) {
    348   gaia_token_ = gaia_token;
    349 }
    350 
    351 void DeviceManagementRequestJob::SetOAuthToken(const std::string& oauth_token) {
    352   AddParameter(dm_protocol::kParamOAuthToken, oauth_token);
    353 }
    354 
    355 void DeviceManagementRequestJob::SetUserAffiliation(
    356     UserAffiliation user_affiliation) {
    357   AddParameter(dm_protocol::kParamUserAffiliation,
    358                UserAffiliationToString(user_affiliation));
    359 }
    360 
    361 void DeviceManagementRequestJob::SetDMToken(const std::string& dm_token) {
    362   dm_token_ = dm_token;
    363 }
    364 
    365 void DeviceManagementRequestJob::SetClientID(const std::string& client_id) {
    366   AddParameter(dm_protocol::kParamDeviceID, client_id);
    367 }
    368 
    369 em::DeviceManagementRequest* DeviceManagementRequestJob::GetRequest() {
    370   return &request_;
    371 }
    372 
    373 DeviceManagementRequestJob::DeviceManagementRequestJob(
    374     JobType type,
    375     const std::string& agent_parameter,
    376     const std::string& platform_parameter) {
    377   AddParameter(dm_protocol::kParamRequest, JobTypeToRequestType(type));
    378   AddParameter(dm_protocol::kParamDeviceType, dm_protocol::kValueDeviceType);
    379   AddParameter(dm_protocol::kParamAppType, dm_protocol::kValueAppType);
    380   AddParameter(dm_protocol::kParamAgent, agent_parameter);
    381   AddParameter(dm_protocol::kParamPlatform, platform_parameter);
    382 }
    383 
    384 void DeviceManagementRequestJob::SetRetryCallback(
    385     const RetryCallback& retry_callback) {
    386   retry_callback_ = retry_callback;
    387 }
    388 
    389 void DeviceManagementRequestJob::Start(const Callback& callback) {
    390   callback_ = callback;
    391   Run();
    392 }
    393 
    394 void DeviceManagementRequestJob::AddParameter(const std::string& name,
    395                                               const std::string& value) {
    396   query_params_.push_back(std::make_pair(name, value));
    397 }
    398 
    399 // A random value that other fetchers won't likely use.
    400 const int DeviceManagementService::kURLFetcherID = 0xde71ce1d;
    401 
    402 DeviceManagementService::~DeviceManagementService() {
    403   // All running jobs should have been cancelled by now.
    404   DCHECK(pending_jobs_.empty());
    405   DCHECK(queued_jobs_.empty());
    406 }
    407 
    408 DeviceManagementRequestJob* DeviceManagementService::CreateJob(
    409     DeviceManagementRequestJob::JobType type,
    410     net::URLRequestContextGetter* request_context) {
    411   return new DeviceManagementRequestJobImpl(
    412       type,
    413       configuration_->GetAgentParameter(),
    414       configuration_->GetPlatformParameter(),
    415       this,
    416       request_context);
    417 }
    418 
    419 void DeviceManagementService::ScheduleInitialization(int64 delay_milliseconds) {
    420   if (initialized_)
    421     return;
    422   base::MessageLoop::current()->PostDelayedTask(
    423       FROM_HERE,
    424       base::Bind(&DeviceManagementService::Initialize,
    425                  weak_ptr_factory_.GetWeakPtr()),
    426       base::TimeDelta::FromMilliseconds(delay_milliseconds));
    427 }
    428 
    429 void DeviceManagementService::Initialize() {
    430   if (initialized_)
    431     return;
    432   initialized_ = true;
    433 
    434   while (!queued_jobs_.empty()) {
    435     StartJob(queued_jobs_.front());
    436     queued_jobs_.pop_front();
    437   }
    438 }
    439 
    440 void DeviceManagementService::Shutdown() {
    441   for (JobFetcherMap::iterator job(pending_jobs_.begin());
    442        job != pending_jobs_.end();
    443        ++job) {
    444     delete job->first;
    445     queued_jobs_.push_back(job->second);
    446   }
    447   pending_jobs_.clear();
    448 }
    449 
    450 DeviceManagementService::DeviceManagementService(
    451     scoped_ptr<Configuration> configuration)
    452     : configuration_(configuration.Pass()),
    453       initialized_(false),
    454       weak_ptr_factory_(this) {
    455   DCHECK(configuration_);
    456 }
    457 
    458 void DeviceManagementService::StartJob(DeviceManagementRequestJobImpl* job) {
    459   std::string server_url = GetServerUrl();
    460   net::URLFetcher* fetcher = net::URLFetcher::Create(
    461       kURLFetcherID, job->GetURL(server_url), net::URLFetcher::POST, this);
    462   job->ConfigureRequest(fetcher);
    463   pending_jobs_[fetcher] = job;
    464   fetcher->Start();
    465 }
    466 
    467 std::string DeviceManagementService::GetServerUrl() {
    468   return configuration_->GetServerUrl();
    469 }
    470 
    471 void DeviceManagementService::OnURLFetchComplete(
    472     const net::URLFetcher* source) {
    473   JobFetcherMap::iterator entry(pending_jobs_.find(source));
    474   if (entry == pending_jobs_.end()) {
    475     NOTREACHED() << "Callback from foreign URL fetcher";
    476     return;
    477   }
    478 
    479   DeviceManagementRequestJobImpl* job = entry->second;
    480   pending_jobs_.erase(entry);
    481 
    482   if (job->ShouldRetry(source)) {
    483     VLOG(1) << "Retrying dmserver request.";
    484     job->PrepareRetry();
    485     StartJob(job);
    486   } else {
    487     std::string data;
    488     source->GetResponseAsString(&data);
    489     job->HandleResponse(source->GetStatus(), source->GetResponseCode(),
    490                         source->GetCookies(), data);
    491   }
    492   delete source;
    493 }
    494 
    495 void DeviceManagementService::AddJob(DeviceManagementRequestJobImpl* job) {
    496   if (initialized_)
    497     StartJob(job);
    498   else
    499     queued_jobs_.push_back(job);
    500 }
    501 
    502 void DeviceManagementService::RemoveJob(DeviceManagementRequestJobImpl* job) {
    503   for (JobFetcherMap::iterator entry(pending_jobs_.begin());
    504        entry != pending_jobs_.end();
    505        ++entry) {
    506     if (entry->second == job) {
    507       delete entry->first;
    508       pending_jobs_.erase(entry);
    509       return;
    510     }
    511   }
    512 
    513   const JobQueue::iterator elem =
    514       std::find(queued_jobs_.begin(), queued_jobs_.end(), job);
    515   if (elem != queued_jobs_.end())
    516     queued_jobs_.erase(elem);
    517 }
    518 
    519 }  // namespace policy
    520