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