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/cloud_policy_client.h"
6
7#include "base/bind.h"
8#include "base/guid.h"
9#include "base/logging.h"
10#include "base/stl_util.h"
11#include "components/policy/core/common/cloud/device_management_service.h"
12#include "google_apis/gaia/gaia_constants.h"
13#include "google_apis/gaia/gaia_urls.h"
14#include "net/url_request/url_request_context_getter.h"
15
16namespace em = enterprise_management;
17
18namespace policy {
19
20namespace {
21
22// Translates the DeviceRegisterResponse::DeviceMode |mode| to the enum used
23// internally to represent different device modes.
24DeviceMode TranslateProtobufDeviceMode(
25    em::DeviceRegisterResponse::DeviceMode mode) {
26  switch (mode) {
27    case em::DeviceRegisterResponse::ENTERPRISE:
28      return DEVICE_MODE_ENTERPRISE;
29    case em::DeviceRegisterResponse::RETAIL:
30      return DEVICE_MODE_RETAIL_KIOSK;
31  }
32  LOG(ERROR) << "Unknown enrollment mode in registration response: " << mode;
33  return DEVICE_MODE_NOT_SET;
34}
35
36bool IsChromePolicy(const std::string& type) {
37  return type == dm_protocol::kChromeDevicePolicyType ||
38         type == GetChromeUserPolicyType();
39}
40
41}  // namespace
42
43CloudPolicyClient::Observer::~Observer() {}
44
45void CloudPolicyClient::Observer::OnRobotAuthCodesFetched(
46    CloudPolicyClient* client) {}
47
48CloudPolicyClient::StatusProvider::~StatusProvider() {}
49
50CloudPolicyClient::CloudPolicyClient(
51    const std::string& machine_id,
52    const std::string& machine_model,
53    const std::string& verification_key_hash,
54    UserAffiliation user_affiliation,
55    StatusProvider* status_provider,
56    DeviceManagementService* service,
57    scoped_refptr<net::URLRequestContextGetter> request_context)
58    : machine_id_(machine_id),
59      machine_model_(machine_model),
60      verification_key_hash_(verification_key_hash),
61      user_affiliation_(user_affiliation),
62      device_mode_(DEVICE_MODE_NOT_SET),
63      submit_machine_id_(false),
64      public_key_version_(-1),
65      public_key_version_valid_(false),
66      invalidation_version_(0),
67      fetched_invalidation_version_(0),
68      service_(service),                  // Can be NULL for unit tests.
69      status_provider_(status_provider),  // Can be NULL for unit tests.
70      status_(DM_STATUS_SUCCESS),
71      request_context_(request_context) {
72}
73
74CloudPolicyClient::~CloudPolicyClient() {
75  STLDeleteValues(&responses_);
76}
77
78void CloudPolicyClient::SetupRegistration(const std::string& dm_token,
79                                          const std::string& client_id) {
80  DCHECK(!dm_token.empty());
81  DCHECK(!client_id.empty());
82  DCHECK(!is_registered());
83
84  dm_token_ = dm_token;
85  client_id_ = client_id;
86  request_job_.reset();
87  STLDeleteValues(&responses_);
88
89  NotifyRegistrationStateChanged();
90}
91
92void CloudPolicyClient::Register(em::DeviceRegisterRequest::Type type,
93                                 const std::string& auth_token,
94                                 const std::string& client_id,
95                                 bool is_auto_enrollement,
96                                 const std::string& requisition,
97                                 const std::string& current_state_key) {
98  DCHECK(service_);
99  DCHECK(!auth_token.empty());
100  DCHECK(!is_registered());
101
102  if (client_id.empty()) {
103    // Generate a new client ID. This is intentionally done on each new
104    // registration request in order to preserve privacy. Reusing IDs would mean
105    // the server could track clients by their registration attempts.
106    client_id_ = base::GenerateGUID();
107  } else {
108    client_id_ = client_id;
109  }
110
111  request_job_.reset(
112      service_->CreateJob(DeviceManagementRequestJob::TYPE_REGISTRATION,
113                          GetRequestContext()));
114  request_job_->SetOAuthToken(auth_token);
115  request_job_->SetClientID(client_id_);
116
117  em::DeviceRegisterRequest* request =
118      request_job_->GetRequest()->mutable_register_request();
119  if (!client_id.empty())
120    request->set_reregister(true);
121  request->set_type(type);
122  if (!machine_id_.empty())
123    request->set_machine_id(machine_id_);
124  if (!machine_model_.empty())
125    request->set_machine_model(machine_model_);
126  if (is_auto_enrollement)
127    request->set_auto_enrolled(true);
128  if (!requisition.empty())
129    request->set_requisition(requisition);
130  if (!current_state_key.empty())
131    request->set_server_backed_state_key(current_state_key);
132
133  request_job_->SetRetryCallback(
134      base::Bind(&CloudPolicyClient::OnRetryRegister, base::Unretained(this)));
135
136  request_job_->Start(base::Bind(&CloudPolicyClient::OnRegisterCompleted,
137                                 base::Unretained(this)));
138}
139
140void CloudPolicyClient::SetInvalidationInfo(
141    int64 version,
142    const std::string& payload) {
143  invalidation_version_ = version;
144  invalidation_payload_ = payload;
145}
146
147void CloudPolicyClient::FetchPolicy() {
148  CHECK(is_registered());
149  CHECK(!namespaces_to_fetch_.empty());
150
151  request_job_.reset(
152      service_->CreateJob(DeviceManagementRequestJob::TYPE_POLICY_FETCH,
153                          GetRequestContext()));
154  request_job_->SetDMToken(dm_token_);
155  request_job_->SetClientID(client_id_);
156  request_job_->SetUserAffiliation(user_affiliation_);
157
158  em::DeviceManagementRequest* request = request_job_->GetRequest();
159
160  // Build policy fetch requests.
161  em::DevicePolicyRequest* policy_request = request->mutable_policy_request();
162  for (NamespaceSet::iterator it = namespaces_to_fetch_.begin();
163       it != namespaces_to_fetch_.end(); ++it) {
164    em::PolicyFetchRequest* fetch_request = policy_request->add_request();
165    fetch_request->set_policy_type(it->first);
166    if (!it->second.empty())
167      fetch_request->set_settings_entity_id(it->second);
168
169    // Request signed policy blobs to help prevent tampering on the client.
170    fetch_request->set_signature_type(em::PolicyFetchRequest::SHA1_RSA);
171    if (public_key_version_valid_)
172      fetch_request->set_public_key_version(public_key_version_);
173
174    if (!verification_key_hash_.empty())
175      fetch_request->set_verification_key_hash(verification_key_hash_);
176
177    // These fields are included only in requests for chrome policy.
178    if (IsChromePolicy(it->first)) {
179      if (submit_machine_id_ && !machine_id_.empty())
180        fetch_request->set_machine_id(machine_id_);
181      if (!last_policy_timestamp_.is_null()) {
182        base::TimeDelta timestamp(
183            last_policy_timestamp_ - base::Time::UnixEpoch());
184        fetch_request->set_timestamp(timestamp.InMilliseconds());
185      }
186      if (!invalidation_payload_.empty()) {
187        fetch_request->set_invalidation_version(invalidation_version_);
188        fetch_request->set_invalidation_payload(invalidation_payload_);
189      }
190    }
191  }
192
193  // Add status data.
194  if (status_provider_) {
195    if (!status_provider_->GetDeviceStatus(
196            request->mutable_device_status_report_request())) {
197      request->clear_device_status_report_request();
198    }
199    if (!status_provider_->GetSessionStatus(
200            request->mutable_session_status_report_request())) {
201      request->clear_session_status_report_request();
202    }
203  }
204
205  // Add device state keys.
206  if (!state_keys_to_upload_.empty()) {
207    em::DeviceStateKeyUpdateRequest* key_update_request =
208        request->mutable_device_state_key_update_request();
209    for (std::vector<std::string>::const_iterator key(
210             state_keys_to_upload_.begin());
211         key != state_keys_to_upload_.end();
212         ++key) {
213      key_update_request->add_server_backed_state_key(*key);
214    }
215  }
216
217  // Set the fetched invalidation version to the latest invalidation version
218  // since it is now the invalidation version used for the latest fetch.
219  fetched_invalidation_version_ = invalidation_version_;
220
221  // Fire the job.
222  request_job_->Start(base::Bind(&CloudPolicyClient::OnPolicyFetchCompleted,
223                                 base::Unretained(this)));
224}
225
226void CloudPolicyClient::FetchRobotAuthCodes(const std::string& auth_token) {
227  CHECK(is_registered());
228  DCHECK(!auth_token.empty());
229
230  request_job_.reset(service_->CreateJob(
231      DeviceManagementRequestJob::TYPE_API_AUTH_CODE_FETCH,
232      GetRequestContext()));
233  // The credentials of a domain user are needed in order to mint a new OAuth2
234  // authorization token for the robot account.
235  request_job_->SetOAuthToken(auth_token);
236  request_job_->SetDMToken(dm_token_);
237  request_job_->SetClientID(client_id_);
238
239  em::DeviceServiceApiAccessRequest* request =
240      request_job_->GetRequest()->mutable_service_api_access_request();
241  request->set_oauth2_client_id(
242      GaiaUrls::GetInstance()->oauth2_chrome_client_id());
243  request->add_auth_scope(GaiaConstants::kAnyApiOAuth2Scope);
244
245  request_job_->Start(
246      base::Bind(&CloudPolicyClient::OnFetchRobotAuthCodesCompleted,
247                 base::Unretained(this)));
248}
249
250void CloudPolicyClient::Unregister() {
251  DCHECK(service_);
252  request_job_.reset(
253      service_->CreateJob(DeviceManagementRequestJob::TYPE_UNREGISTRATION,
254                          GetRequestContext()));
255  request_job_->SetDMToken(dm_token_);
256  request_job_->SetClientID(client_id_);
257  request_job_->GetRequest()->mutable_unregister_request();
258  request_job_->Start(base::Bind(&CloudPolicyClient::OnUnregisterCompleted,
259                                 base::Unretained(this)));
260}
261
262void CloudPolicyClient::UploadCertificate(
263    const std::string& certificate_data,
264    const CloudPolicyClient::StatusCallback& callback) {
265  CHECK(is_registered());
266  request_job_.reset(
267      service_->CreateJob(DeviceManagementRequestJob::TYPE_UPLOAD_CERTIFICATE,
268                          GetRequestContext()));
269  request_job_->SetDMToken(dm_token_);
270  request_job_->SetClientID(client_id_);
271
272  em::DeviceManagementRequest* request = request_job_->GetRequest();
273  request->mutable_cert_upload_request()->set_device_certificate(
274      certificate_data);
275
276  DeviceManagementRequestJob::Callback job_callback = base::Bind(
277      &CloudPolicyClient::OnCertificateUploadCompleted,
278      base::Unretained(this),
279      callback);
280  request_job_->Start(job_callback);
281}
282
283void CloudPolicyClient::AddObserver(Observer* observer) {
284  observers_.AddObserver(observer);
285}
286
287void CloudPolicyClient::RemoveObserver(Observer* observer) {
288  observers_.RemoveObserver(observer);
289}
290
291void CloudPolicyClient::AddNamespaceToFetch(const PolicyNamespaceKey& key) {
292  namespaces_to_fetch_.insert(key);
293}
294
295void CloudPolicyClient::RemoveNamespaceToFetch(const PolicyNamespaceKey& key) {
296  namespaces_to_fetch_.erase(key);
297}
298
299void CloudPolicyClient::SetStateKeysToUpload(
300    const std::vector<std::string>& keys) {
301  state_keys_to_upload_ = keys;
302}
303
304const em::PolicyFetchResponse* CloudPolicyClient::GetPolicyFor(
305    const PolicyNamespaceKey& key) const {
306  ResponseMap::const_iterator it = responses_.find(key);
307  return it == responses_.end() ? NULL : it->second;
308}
309
310scoped_refptr<net::URLRequestContextGetter>
311CloudPolicyClient::GetRequestContext() {
312  return request_context_;
313}
314
315void CloudPolicyClient::OnRetryRegister(DeviceManagementRequestJob* job) {
316  DCHECK_EQ(request_job_.get(), job);
317  // If the initial request managed to get to the server but the response didn't
318  // arrive at the client then retrying with the same client ID will fail.
319  // Set the re-registration flag so that the server accepts it.
320  // If the server hasn't seen the client ID before then it will also accept
321  // the re-registration.
322  job->GetRequest()->mutable_register_request()->set_reregister(true);
323}
324
325void CloudPolicyClient::OnRegisterCompleted(
326    DeviceManagementStatus status,
327    int net_error,
328    const em::DeviceManagementResponse& response) {
329  if (status == DM_STATUS_SUCCESS &&
330      (!response.has_register_response() ||
331       !response.register_response().has_device_management_token())) {
332    LOG(WARNING) << "Invalid registration response.";
333    status = DM_STATUS_RESPONSE_DECODING_ERROR;
334  }
335
336  status_ = status;
337  if (status == DM_STATUS_SUCCESS) {
338    dm_token_ = response.register_response().device_management_token();
339    DVLOG(1) << "Client registration complete - DMToken = " << dm_token_;
340
341    // Device mode is only relevant for device policy really, it's the
342    // responsibility of the consumer of the field to check validity.
343    device_mode_ = DEVICE_MODE_NOT_SET;
344    if (response.register_response().has_enrollment_type()) {
345      device_mode_ = TranslateProtobufDeviceMode(
346          response.register_response().enrollment_type());
347    }
348
349    NotifyRegistrationStateChanged();
350  } else {
351    NotifyClientError();
352  }
353}
354
355void CloudPolicyClient::OnFetchRobotAuthCodesCompleted(
356    DeviceManagementStatus status,
357    int net_error,
358    const em::DeviceManagementResponse& response) {
359  if (status == DM_STATUS_SUCCESS &&
360      (!response.has_service_api_access_response() ||
361       response.service_api_access_response().auth_code().empty())) {
362    LOG(WARNING) << "Invalid service api access response.";
363    status = DM_STATUS_RESPONSE_DECODING_ERROR;
364  }
365
366  status_ = status;
367  if (status == DM_STATUS_SUCCESS) {
368    robot_api_auth_code_ = response.service_api_access_response().auth_code();
369    DVLOG(1) << "Device robot account auth code fetch complete - code = "
370             << robot_api_auth_code_;
371
372    NotifyRobotAuthCodesFetched();
373  } else {
374    NotifyClientError();
375  }
376}
377
378void CloudPolicyClient::OnPolicyFetchCompleted(
379    DeviceManagementStatus status,
380    int net_error,
381    const em::DeviceManagementResponse& response) {
382  if (status == DM_STATUS_SUCCESS) {
383    if (!response.has_policy_response() ||
384        response.policy_response().response_size() == 0) {
385      LOG(WARNING) << "Empty policy response.";
386      status = DM_STATUS_RESPONSE_DECODING_ERROR;
387    }
388  }
389
390  status_ = status;
391  if (status == DM_STATUS_SUCCESS) {
392    const em::DevicePolicyResponse& policy_response =
393        response.policy_response();
394    STLDeleteValues(&responses_);
395    for (int i = 0; i < policy_response.response_size(); ++i) {
396      const em::PolicyFetchResponse& response = policy_response.response(i);
397      em::PolicyData policy_data;
398      if (!policy_data.ParseFromString(response.policy_data()) ||
399          !policy_data.IsInitialized() ||
400          !policy_data.has_policy_type()) {
401        LOG(WARNING) << "Invalid PolicyData received, ignoring";
402        continue;
403      }
404      const std::string& type = policy_data.policy_type();
405      std::string entity_id;
406      if (policy_data.has_settings_entity_id())
407        entity_id = policy_data.settings_entity_id();
408      PolicyNamespaceKey key(type, entity_id);
409      if (ContainsKey(responses_, key)) {
410        LOG(WARNING) << "Duplicate PolicyFetchResponse for type: "
411            << type << ", entity: " << entity_id << ", ignoring";
412        continue;
413      }
414      responses_[key] = new em::PolicyFetchResponse(response);
415    }
416    if (status_provider_)
417      status_provider_->OnSubmittedSuccessfully();
418    state_keys_to_upload_.clear();
419    NotifyPolicyFetched();
420  } else {
421    NotifyClientError();
422  }
423}
424
425void CloudPolicyClient::OnUnregisterCompleted(
426    DeviceManagementStatus status,
427    int net_error,
428    const em::DeviceManagementResponse& response) {
429  if (status == DM_STATUS_SUCCESS && !response.has_unregister_response()) {
430    // Assume unregistration has succeeded either way.
431    LOG(WARNING) << "Empty unregistration response.";
432  }
433
434  status_ = status;
435  if (status == DM_STATUS_SUCCESS) {
436    dm_token_.clear();
437    NotifyRegistrationStateChanged();
438  } else {
439    NotifyClientError();
440  }
441}
442
443void CloudPolicyClient::OnCertificateUploadCompleted(
444    const CloudPolicyClient::StatusCallback& callback,
445    DeviceManagementStatus status,
446    int net_error,
447    const enterprise_management::DeviceManagementResponse& response) {
448  if (status == DM_STATUS_SUCCESS && !response.has_cert_upload_response()) {
449    LOG(WARNING) << "Empty upload certificate response.";
450    callback.Run(false);
451    return;
452  }
453
454  status_ = status;
455  if (status != DM_STATUS_SUCCESS) {
456    NotifyClientError();
457    callback.Run(false);
458    return;
459  }
460  callback.Run(true);
461}
462
463void CloudPolicyClient::NotifyPolicyFetched() {
464  FOR_EACH_OBSERVER(Observer, observers_, OnPolicyFetched(this));
465}
466
467void CloudPolicyClient::NotifyRegistrationStateChanged() {
468  FOR_EACH_OBSERVER(Observer, observers_, OnRegistrationStateChanged(this));
469}
470
471void CloudPolicyClient::NotifyRobotAuthCodesFetched() {
472  FOR_EACH_OBSERVER(Observer, observers_, OnRobotAuthCodesFetched(this));
473}
474
475void CloudPolicyClient::NotifyClientError() {
476  FOR_EACH_OBSERVER(Observer, observers_, OnClientError(this));
477}
478
479}  // namespace policy
480