Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(168)

Side by Side Diff: chrome/browser/policy/device_management_service.cc

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

Powered by Google App Engine
This is Rietveld 408576698