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
25namespace policy {
26
27// Name constants for URL query parameters.
28const char DeviceManagementBackendImpl::kParamRequest[] = "request";
29const char DeviceManagementBackendImpl::kParamDeviceType[] = "devicetype";
30const char DeviceManagementBackendImpl::kParamAppType[] = "apptype";
31const char DeviceManagementBackendImpl::kParamDeviceID[] = "deviceid";
32const char DeviceManagementBackendImpl::kParamAgent[] = "agent";
33const char DeviceManagementBackendImpl::kParamPlatform[] = "platform";
34
35// String constants for the device and app type we report to the server.
36const char DeviceManagementBackendImpl::kValueRequestRegister[] = "register";
37const char DeviceManagementBackendImpl::kValueRequestUnregister[] =
38    "unregister";
39const char DeviceManagementBackendImpl::kValueRequestPolicy[] = "policy";
40const char DeviceManagementBackendImpl::kValueDeviceType[] = "2";
41const char DeviceManagementBackendImpl::kValueAppType[] = "Chrome";
42
43namespace {
44
45const char kValueAgent[] = "%s %s(%s)";
46const char kValuePlatform[] = "%s|%s|%s";
47
48const char kPostContentType[] = "application/protobuf";
49
50const char kServiceTokenAuthHeader[] = "Authorization: GoogleLogin auth=";
51const 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.
55const int kSuccess = 200;
56const int kInvalidArgument = 400;
57const int kInvalidAuthCookieOrDMToken = 401;
58const int kDeviceManagementNotAllowed = 403;
59const int kInvalidURL = 404; // This error is not coming from the GFE.
60const int kPendingApproval = 491;
61const int kInternalServerError = 500;
62const int kServiceUnavailable = 503;
63const int kDeviceNotFound = 901;
64const int kPolicyNotFound = 902; // This error is not sent as HTTP status code.
65
66#if defined(OS_CHROMEOS)
67// Machine info keys.
68const char kMachineInfoHWClass[] = "hardware_class";
69const char kMachineInfoBoard[] = "CHROMEOS_RELEASE_BOARD";
70#endif
71
72}  // namespace
73
74// Helper class for URL query parameter encoding/decoding.
75class URLQueryParameters {
76 public:
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
93void URLQueryParameters::Put(const std::string& name,
94                             const std::string& value) {
95  params_.push_back(std::make_pair(name, value));
96}
97
98std::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.
115class DeviceManagementJobBase
116    : public DeviceManagementService::DeviceManagementJob {
117 public:
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.
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
146  void SetQueryParam(const std::string& name, const std::string& value) {
147    query_params_.Put(name, value);
148  }
149
150  void SetAuthToken(const std::string& auth_token) {
151    auth_token_ = auth_token;
152  }
153
154  void SetDeviceManagementToken(const std::string& device_management_token) {
155    device_management_token_ = device_management_token;
156  }
157
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
189void 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
258GURL DeviceManagementJobBase::GetURL(
259    const std::string& server_url) {
260  return GURL(server_url + '?' + query_params_.Encode());
261}
262
263void 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.
274class DeviceManagementRegisterJob : public DeviceManagementJobBase {
275 public:
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  }
292  virtual ~DeviceManagementRegisterJob() {}
293
294 private:
295  // DeviceManagementJobBase overrides.
296  virtual void OnError(DeviceManagementBackend::ErrorCode error) {
297    delegate_->OnError(error);
298  }
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.
309class DeviceManagementUnregisterJob : public DeviceManagementJobBase {
310 public:
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  }
327  virtual ~DeviceManagementUnregisterJob() {}
328
329 private:
330  // DeviceManagementJobBase overrides.
331  virtual void OnError(DeviceManagementBackend::ErrorCode error) {
332    delegate_->OnError(error);
333  }
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.
344class DeviceManagementPolicyJob : public DeviceManagementJobBase {
345 public:
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  }
362  virtual ~DeviceManagementPolicyJob() {}
363
364 private:
365  // DeviceManagementJobBase overrides.
366  virtual void OnError(DeviceManagementBackend::ErrorCode error) {
367    delegate_->OnError(error);
368  }
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
378DeviceManagementBackendImpl::DeviceManagementBackendImpl(
379    DeviceManagementService* service)
380    : service_(service) {
381}
382
383DeviceManagementBackendImpl::~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
393std::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
406std::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
448void DeviceManagementBackendImpl::JobDone(DeviceManagementJobBase* job) {
449  pending_jobs_.erase(job);
450}
451
452void DeviceManagementBackendImpl::AddJob(DeviceManagementJobBase* job) {
453  pending_jobs_.insert(job);
454  service_->AddJob(job);
455}
456
457void 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
466void 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
475void 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