1 // Copyright (c) 2011 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_backend_impl.h"
6
7 #include <utility>
8 #include <vector>
9
10 #if defined(OS_POSIX) && !defined(OS_MACOSX)
11 #include <sys/utsname.h>
12 #endif
13
14 #include "base/stringprintf.h"
15 #include "base/sys_info.h"
16 #include "chrome/browser/policy/device_management_service.h"
17 #include "chrome/common/chrome_version_info.h"
18 #include "net/base/escape.h"
19 #include "net/url_request/url_request_status.h"
20
21 #if defined(OS_CHROMEOS)
22 #include "chrome/browser/chromeos/system_access.h"
23 #endif
24
25 namespace policy {
26
27 // Name constants for URL query parameters.
28 const char DeviceManagementBackendImpl::kParamRequest[] = "request";
29 const char DeviceManagementBackendImpl::kParamDeviceType[] = "devicetype";
30 const char DeviceManagementBackendImpl::kParamAppType[] = "apptype";
31 const char DeviceManagementBackendImpl::kParamDeviceID[] = "deviceid";
32 const char DeviceManagementBackendImpl::kParamAgent[] = "agent";
33 const char DeviceManagementBackendImpl::kParamPlatform[] = "platform";
34
35 // String constants for the device and app type we report to the server.
36 const char DeviceManagementBackendImpl::kValueRequestRegister[] = "register";
37 const char DeviceManagementBackendImpl::kValueRequestUnregister[] =
38 "unregister";
39 const char DeviceManagementBackendImpl::kValueRequestPolicy[] = "policy";
40 const char DeviceManagementBackendImpl::kValueDeviceType[] = "2";
41 const char DeviceManagementBackendImpl::kValueAppType[] = "Chrome";
42
43 namespace {
44
45 const char kValueAgent[] = "%s %s(%s)";
46 const char kValuePlatform[] = "%s|%s|%s";
47
48 const char kPostContentType[] = "application/protobuf";
49
50 const char kServiceTokenAuthHeader[] = "Authorization: GoogleLogin auth=";
51 const char kDMTokenAuthHeader[] = "Authorization: GoogleDMToken token=";
52
53 // HTTP Error Codes of the DM Server with their concrete meinings in the context
54 // of the DM Server communication.
55 const int kSuccess = 200;
56 const int kInvalidArgument = 400;
57 const int kInvalidAuthCookieOrDMToken = 401;
58 const int kDeviceManagementNotAllowed = 403;
59 const int kInvalidURL = 404; // This error is not coming from the GFE.
60 const int kPendingApproval = 491;
61 const int kInternalServerError = 500;
62 const int kServiceUnavailable = 503;
63 const int kDeviceNotFound = 901;
64 const int kPolicyNotFound = 902; // This error is not sent as HTTP status code.
65
66 #if defined(OS_CHROMEOS)
67 // Machine info keys.
68 const char kMachineInfoHWClass[] = "hardware_class";
69 const char kMachineInfoBoard[] = "CHROMEOS_RELEASE_BOARD";
70 #endif
71
72 } // namespace
73
74 // Helper class for URL query parameter encoding/decoding.
75 class URLQueryParameters {
76 public:
URLQueryParameters()77 URLQueryParameters() {}
78
79 // Add a query parameter.
80 void Put(const std::string& name, const std::string& value);
81
82 // Produce the query string, taking care of properly encoding and assembling
83 // the names and values.
84 std::string Encode();
85
86 private:
87 typedef std::vector<std::pair<std::string, std::string> > ParameterMap;
88 ParameterMap params_;
89
90 DISALLOW_COPY_AND_ASSIGN(URLQueryParameters);
91 };
92
Put(const std::string & name,const std::string & value)93 void URLQueryParameters::Put(const std::string& name,
94 const std::string& value) {
95 params_.push_back(std::make_pair(name, value));
96 }
97
Encode()98 std::string URLQueryParameters::Encode() {
99 std::string result;
100 for (ParameterMap::const_iterator entry(params_.begin());
101 entry != params_.end();
102 ++entry) {
103 if (entry != params_.begin())
104 result += '&';
105 result += EscapeQueryParamValue(entry->first, true);
106 result += '=';
107 result += EscapeQueryParamValue(entry->second, true);
108 }
109 return result;
110 }
111
112 // A base class containing the common code for the jobs created by the backend
113 // implementation. Subclasses provide custom code for handling actual register,
114 // unregister, and policy jobs.
115 class DeviceManagementJobBase
116 : public DeviceManagementService::DeviceManagementJob {
117 public:
~DeviceManagementJobBase()118 virtual ~DeviceManagementJobBase() {}
119
120 // DeviceManagementJob overrides:
121 virtual void HandleResponse(const net::URLRequestStatus& status,
122 int response_code,
123 const ResponseCookies& cookies,
124 const std::string& data);
125 virtual GURL GetURL(const std::string& server_url);
126 virtual void ConfigureRequest(URLFetcher* fetcher);
127
128 protected:
129 // Constructs a device management job running for the given backend.
DeviceManagementJobBase(DeviceManagementBackendImpl * backend_impl,const std::string & request_type,const std::string & device_id)130 DeviceManagementJobBase(DeviceManagementBackendImpl* backend_impl,
131 const std::string& request_type,
132 const std::string& device_id)
133 : backend_impl_(backend_impl) {
134 query_params_.Put(DeviceManagementBackendImpl::kParamRequest, request_type);
135 query_params_.Put(DeviceManagementBackendImpl::kParamDeviceType,
136 DeviceManagementBackendImpl::kValueDeviceType);
137 query_params_.Put(DeviceManagementBackendImpl::kParamAppType,
138 DeviceManagementBackendImpl::kValueAppType);
139 query_params_.Put(DeviceManagementBackendImpl::kParamDeviceID, device_id);
140 query_params_.Put(DeviceManagementBackendImpl::kParamAgent,
141 DeviceManagementBackendImpl::GetAgentString());
142 query_params_.Put(DeviceManagementBackendImpl::kParamPlatform,
143 DeviceManagementBackendImpl::GetPlatformString());
144 }
145
SetQueryParam(const std::string & name,const std::string & value)146 void SetQueryParam(const std::string& name, const std::string& value) {
147 query_params_.Put(name, value);
148 }
149
SetAuthToken(const std::string & auth_token)150 void SetAuthToken(const std::string& auth_token) {
151 auth_token_ = auth_token;
152 }
153
SetDeviceManagementToken(const std::string & device_management_token)154 void SetDeviceManagementToken(const std::string& device_management_token) {
155 device_management_token_ = device_management_token;
156 }
157
SetPayload(const em::DeviceManagementRequest & request)158 void SetPayload(const em::DeviceManagementRequest& request) {
159 if (!request.SerializeToString(&payload_)) {
160 NOTREACHED();
161 LOG(ERROR) << "Failed to serialize request.";
162 }
163 }
164
165 private:
166 // Implemented by subclasses to handle decoded responses and errors.
167 virtual void OnResponse(
168 const em::DeviceManagementResponse& response) = 0;
169 virtual void OnError(DeviceManagementBackend::ErrorCode error) = 0;
170
171 // The backend this job is handling a request for.
172 DeviceManagementBackendImpl* backend_impl_;
173
174 // Query parameters.
175 URLQueryParameters query_params_;
176
177 // Auth token (if applicaple).
178 std::string auth_token_;
179
180 // Device management token (if applicable).
181 std::string device_management_token_;
182
183 // The payload.
184 std::string payload_;
185
186 DISALLOW_COPY_AND_ASSIGN(DeviceManagementJobBase);
187 };
188
HandleResponse(const net::URLRequestStatus & status,int response_code,const ResponseCookies & cookies,const std::string & data)189 void DeviceManagementJobBase::HandleResponse(
190 const net::URLRequestStatus& status,
191 int response_code,
192 const ResponseCookies& cookies,
193 const std::string& data) {
194 // Delete ourselves when this is done.
195 scoped_ptr<DeviceManagementJob> scoped_killer(this);
196 backend_impl_->JobDone(this);
197 backend_impl_ = NULL;
198
199 if (status.status() != net::URLRequestStatus::SUCCESS) {
200 OnError(DeviceManagementBackend::kErrorRequestFailed);
201 return;
202 }
203
204 switch (response_code) {
205 case kSuccess: {
206 em::DeviceManagementResponse response;
207 if (!response.ParseFromString(data)) {
208 OnError(DeviceManagementBackend::kErrorResponseDecoding);
209 return;
210 }
211 OnResponse(response);
212 return;
213 }
214 case kInvalidArgument: {
215 OnError(DeviceManagementBackend::kErrorRequestInvalid);
216 return;
217 }
218 case kInvalidAuthCookieOrDMToken: {
219 OnError(DeviceManagementBackend::kErrorServiceManagementTokenInvalid);
220 return;
221 }
222 case kDeviceManagementNotAllowed: {
223 OnError(DeviceManagementBackend::kErrorServiceManagementNotSupported);
224 return;
225 }
226 case kPendingApproval: {
227 OnError(DeviceManagementBackend::kErrorServiceActivationPending);
228 return;
229 }
230 case kInvalidURL:
231 case kInternalServerError:
232 case kServiceUnavailable: {
233 OnError(DeviceManagementBackend::kErrorTemporaryUnavailable);
234 return;
235 }
236 case kDeviceNotFound: {
237 OnError(DeviceManagementBackend::kErrorServiceDeviceNotFound);
238 return;
239 }
240 case kPolicyNotFound: {
241 OnError(DeviceManagementBackend::kErrorServicePolicyNotFound);
242 break;
243 }
244 default: {
245 VLOG(1) << "Unexpected HTTP status in response from DMServer : "
246 << response_code << ".";
247 // Handle all unknown 5xx HTTP error codes as temporary and any other
248 // unknown error as one that needs more time to recover.
249 if (response_code >= 500 && response_code <= 599)
250 OnError(DeviceManagementBackend::kErrorTemporaryUnavailable);
251 else
252 OnError(DeviceManagementBackend::kErrorHttpStatus);
253 return;
254 }
255 }
256 }
257
GetURL(const std::string & server_url)258 GURL DeviceManagementJobBase::GetURL(
259 const std::string& server_url) {
260 return GURL(server_url + '?' + query_params_.Encode());
261 }
262
ConfigureRequest(URLFetcher * fetcher)263 void DeviceManagementJobBase::ConfigureRequest(URLFetcher* fetcher) {
264 fetcher->set_upload_data(kPostContentType, payload_);
265 std::string extra_headers;
266 if (!auth_token_.empty())
267 extra_headers += kServiceTokenAuthHeader + auth_token_ + "\n";
268 if (!device_management_token_.empty())
269 extra_headers += kDMTokenAuthHeader + device_management_token_ + "\n";
270 fetcher->set_extra_request_headers(extra_headers);
271 }
272
273 // Handles device registration jobs.
274 class DeviceManagementRegisterJob : public DeviceManagementJobBase {
275 public:
DeviceManagementRegisterJob(DeviceManagementBackendImpl * backend_impl,const std::string & auth_token,const std::string & device_id,const em::DeviceRegisterRequest & request,DeviceManagementBackend::DeviceRegisterResponseDelegate * delegate)276 DeviceManagementRegisterJob(
277 DeviceManagementBackendImpl* backend_impl,
278 const std::string& auth_token,
279 const std::string& device_id,
280 const em::DeviceRegisterRequest& request,
281 DeviceManagementBackend::DeviceRegisterResponseDelegate* delegate)
282 : DeviceManagementJobBase(
283 backend_impl,
284 DeviceManagementBackendImpl::kValueRequestRegister,
285 device_id),
286 delegate_(delegate) {
287 SetAuthToken(auth_token);
288 em::DeviceManagementRequest request_wrapper;
289 request_wrapper.mutable_register_request()->CopyFrom(request);
290 SetPayload(request_wrapper);
291 }
~DeviceManagementRegisterJob()292 virtual ~DeviceManagementRegisterJob() {}
293
294 private:
295 // DeviceManagementJobBase overrides.
OnError(DeviceManagementBackend::ErrorCode error)296 virtual void OnError(DeviceManagementBackend::ErrorCode error) {
297 delegate_->OnError(error);
298 }
OnResponse(const em::DeviceManagementResponse & response)299 virtual void OnResponse(const em::DeviceManagementResponse& response) {
300 delegate_->HandleRegisterResponse(response.register_response());
301 }
302
303 DeviceManagementBackend::DeviceRegisterResponseDelegate* delegate_;
304
305 DISALLOW_COPY_AND_ASSIGN(DeviceManagementRegisterJob);
306 };
307
308 // Handles device unregistration jobs.
309 class DeviceManagementUnregisterJob : public DeviceManagementJobBase {
310 public:
DeviceManagementUnregisterJob(DeviceManagementBackendImpl * backend_impl,const std::string & device_management_token,const std::string & device_id,const em::DeviceUnregisterRequest & request,DeviceManagementBackend::DeviceUnregisterResponseDelegate * delegate)311 DeviceManagementUnregisterJob(
312 DeviceManagementBackendImpl* backend_impl,
313 const std::string& device_management_token,
314 const std::string& device_id,
315 const em::DeviceUnregisterRequest& request,
316 DeviceManagementBackend::DeviceUnregisterResponseDelegate* delegate)
317 : DeviceManagementJobBase(
318 backend_impl,
319 DeviceManagementBackendImpl::kValueRequestUnregister,
320 device_id),
321 delegate_(delegate) {
322 SetDeviceManagementToken(device_management_token);
323 em::DeviceManagementRequest request_wrapper;
324 request_wrapper.mutable_unregister_request()->CopyFrom(request);
325 SetPayload(request_wrapper);
326 }
~DeviceManagementUnregisterJob()327 virtual ~DeviceManagementUnregisterJob() {}
328
329 private:
330 // DeviceManagementJobBase overrides.
OnError(DeviceManagementBackend::ErrorCode error)331 virtual void OnError(DeviceManagementBackend::ErrorCode error) {
332 delegate_->OnError(error);
333 }
OnResponse(const em::DeviceManagementResponse & response)334 virtual void OnResponse(const em::DeviceManagementResponse& response) {
335 delegate_->HandleUnregisterResponse(response.unregister_response());
336 }
337
338 DeviceManagementBackend::DeviceUnregisterResponseDelegate* delegate_;
339
340 DISALLOW_COPY_AND_ASSIGN(DeviceManagementUnregisterJob);
341 };
342
343 // Handles policy request jobs.
344 class DeviceManagementPolicyJob : public DeviceManagementJobBase {
345 public:
DeviceManagementPolicyJob(DeviceManagementBackendImpl * backend_impl,const std::string & device_management_token,const std::string & device_id,const em::DevicePolicyRequest & request,DeviceManagementBackend::DevicePolicyResponseDelegate * delegate)346 DeviceManagementPolicyJob(
347 DeviceManagementBackendImpl* backend_impl,
348 const std::string& device_management_token,
349 const std::string& device_id,
350 const em::DevicePolicyRequest& request,
351 DeviceManagementBackend::DevicePolicyResponseDelegate* delegate)
352 : DeviceManagementJobBase(
353 backend_impl,
354 DeviceManagementBackendImpl::kValueRequestPolicy,
355 device_id),
356 delegate_(delegate) {
357 SetDeviceManagementToken(device_management_token);
358 em::DeviceManagementRequest request_wrapper;
359 request_wrapper.mutable_policy_request()->CopyFrom(request);
360 SetPayload(request_wrapper);
361 }
~DeviceManagementPolicyJob()362 virtual ~DeviceManagementPolicyJob() {}
363
364 private:
365 // DeviceManagementJobBase overrides.
OnError(DeviceManagementBackend::ErrorCode error)366 virtual void OnError(DeviceManagementBackend::ErrorCode error) {
367 delegate_->OnError(error);
368 }
OnResponse(const em::DeviceManagementResponse & response)369 virtual void OnResponse(const em::DeviceManagementResponse& response) {
370 delegate_->HandlePolicyResponse(response.policy_response());
371 }
372
373 DeviceManagementBackend::DevicePolicyResponseDelegate* delegate_;
374
375 DISALLOW_COPY_AND_ASSIGN(DeviceManagementPolicyJob);
376 };
377
DeviceManagementBackendImpl(DeviceManagementService * service)378 DeviceManagementBackendImpl::DeviceManagementBackendImpl(
379 DeviceManagementService* service)
380 : service_(service) {
381 }
382
~DeviceManagementBackendImpl()383 DeviceManagementBackendImpl::~DeviceManagementBackendImpl() {
384 for (JobSet::iterator job(pending_jobs_.begin());
385 job != pending_jobs_.end();
386 ++job) {
387 service_->RemoveJob(*job);
388 delete *job;
389 }
390 pending_jobs_.clear();
391 }
392
GetAgentString()393 std::string DeviceManagementBackendImpl::GetAgentString() {
394 static std::string agent;
395 if (!agent.empty())
396 return agent;
397
398 chrome::VersionInfo version_info;
399 agent = base::StringPrintf(kValueAgent,
400 version_info.Name().c_str(),
401 version_info.Version().c_str(),
402 version_info.LastChange().c_str());
403 return agent;
404 }
405
GetPlatformString()406 std::string DeviceManagementBackendImpl::GetPlatformString() {
407 static std::string platform;
408 if (!platform.empty())
409 return platform;
410
411 std::string os_name(base::SysInfo::OperatingSystemName());
412 std::string os_hardware(base::SysInfo::CPUArchitecture());
413
414 #if defined(OS_CHROMEOS)
415 chromeos::SystemAccess* sys_lib = chromeos::SystemAccess::GetInstance();
416
417 std::string hwclass;
418 std::string board;
419 if (!sys_lib->GetMachineStatistic(kMachineInfoHWClass, &hwclass) ||
420 !sys_lib->GetMachineStatistic(kMachineInfoBoard, &board)) {
421 LOG(ERROR) << "Failed to get machine information";
422 }
423 os_name += ",CrOS," + board;
424 os_hardware += "," + hwclass;
425 #endif
426
427 std::string os_version("-");
428 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
429 int32 os_major_version = 0;
430 int32 os_minor_version = 0;
431 int32 os_bugfix_version = 0;
432 base::SysInfo::OperatingSystemVersionNumbers(&os_major_version,
433 &os_minor_version,
434 &os_bugfix_version);
435 os_version = base::StringPrintf("%d.%d.%d",
436 os_major_version,
437 os_minor_version,
438 os_bugfix_version);
439 #endif
440
441 platform = base::StringPrintf(kValuePlatform,
442 os_name.c_str(),
443 os_hardware.c_str(),
444 os_version.c_str());
445 return platform;
446 }
447
JobDone(DeviceManagementJobBase * job)448 void DeviceManagementBackendImpl::JobDone(DeviceManagementJobBase* job) {
449 pending_jobs_.erase(job);
450 }
451
AddJob(DeviceManagementJobBase * job)452 void DeviceManagementBackendImpl::AddJob(DeviceManagementJobBase* job) {
453 pending_jobs_.insert(job);
454 service_->AddJob(job);
455 }
456
ProcessRegisterRequest(const std::string & auth_token,const std::string & device_id,const em::DeviceRegisterRequest & request,DeviceRegisterResponseDelegate * delegate)457 void DeviceManagementBackendImpl::ProcessRegisterRequest(
458 const std::string& auth_token,
459 const std::string& device_id,
460 const em::DeviceRegisterRequest& request,
461 DeviceRegisterResponseDelegate* delegate) {
462 AddJob(new DeviceManagementRegisterJob(this, auth_token, device_id, request,
463 delegate));
464 }
465
ProcessUnregisterRequest(const std::string & device_management_token,const std::string & device_id,const em::DeviceUnregisterRequest & request,DeviceUnregisterResponseDelegate * delegate)466 void DeviceManagementBackendImpl::ProcessUnregisterRequest(
467 const std::string& device_management_token,
468 const std::string& device_id,
469 const em::DeviceUnregisterRequest& request,
470 DeviceUnregisterResponseDelegate* delegate) {
471 AddJob(new DeviceManagementUnregisterJob(this, device_management_token,
472 device_id, request, delegate));
473 }
474
ProcessPolicyRequest(const std::string & device_management_token,const std::string & device_id,const em::DevicePolicyRequest & request,DevicePolicyResponseDelegate * delegate)475 void DeviceManagementBackendImpl::ProcessPolicyRequest(
476 const std::string& device_management_token,
477 const std::string& device_id,
478 const em::DevicePolicyRequest& request,
479 DevicePolicyResponseDelegate* delegate) {
480 AddJob(new DeviceManagementPolicyJob(this, device_management_token, device_id,
481 request, delegate));
482 }
483
484 } // namespace policy
485