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/chromeos/login/user_manager.h"
6
7#include "base/command_line.h"
8#include "base/compiler_specific.h"
9#include "base/file_path.h"
10#include "base/file_util.h"
11#include "base/lazy_instance.h"
12#include "base/logging.h"
13#include "base/path_service.h"
14#include "base/string_util.h"
15#include "base/stringprintf.h"
16#include "base/time.h"
17#include "base/utf_string_conversions.h"
18#include "base/values.h"
19#include "crypto/nss_util.h"
20#include "chrome/browser/browser_process.h"
21#include "chrome/browser/chromeos/cros/cros_library.h"
22#include "chrome/browser/chromeos/cros/cryptohome_library.h"
23#include "chrome/browser/chromeos/cros/input_method_library.h"
24#include "chrome/browser/chromeos/login/default_user_images.h"
25#include "chrome/browser/chromeos/login/login_display.h"
26#include "chrome/browser/chromeos/login/ownership_service.h"
27#include "chrome/browser/chromeos/user_cros_settings_provider.h"
28#include "chrome/browser/chromeos/wm_ipc.h"
29#include "chrome/browser/defaults.h"
30#include "chrome/browser/prefs/pref_service.h"
31#include "chrome/browser/prefs/scoped_user_pref_update.h"
32#include "chrome/common/chrome_paths.h"
33#include "chrome/common/chrome_switches.h"
34#include "content/browser/browser_thread.h"
35#include "content/common/notification_service.h"
36#include "content/common/notification_type.h"
37#include "ui/base/resource/resource_bundle.h"
38#include "ui/gfx/codec/png_codec.h"
39
40namespace chromeos {
41
42namespace {
43
44// A vector pref of the users who have logged into the device.
45const char kLoggedInUsers[] = "LoggedInUsers";
46// A dictionary that maps usernames to file paths to their images.
47const char kUserImages[] = "UserImages";
48
49// Incognito user is represented by an empty string (since some code already
50// depends on that and it's hard to figure out what).
51const char kGuestUser[] = "";
52
53base::LazyInstance<UserManager> g_user_manager(base::LINKER_INITIALIZED);
54
55// Stores path to the image in local state. Runs on UI thread.
56void SavePathToLocalState(const std::string& username,
57                          const std::string& image_path) {
58  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
59  PrefService* local_state = g_browser_process->local_state();
60  DictionaryPrefUpdate images_update(local_state, kUserImages);
61  images_update->SetWithoutPathExpansion(username, new StringValue(image_path));
62  DVLOG(1) << "Saving path to user image in Local State.";
63  local_state->SavePersistentPrefs();
64}
65
66// Saves image to file with specified path. Runs on FILE thread.
67// Posts task for saving image path to local state on UI thread.
68void SaveImageToFile(const SkBitmap& image,
69                     const FilePath& image_path,
70                     const std::string& username) {
71  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
72  std::vector<unsigned char> encoded_image;
73  if (!gfx::PNGCodec::EncodeBGRASkBitmap(image, true, &encoded_image)) {
74    LOG(ERROR) << "Failed to PNG encode the image.";
75    return;
76  }
77
78  if (file_util::WriteFile(image_path,
79                           reinterpret_cast<char*>(&encoded_image[0]),
80                           encoded_image.size()) == -1) {
81    LOG(ERROR) << "Failed to save image to file.";
82    return;
83  }
84
85  BrowserThread::PostTask(
86      BrowserThread::UI,
87      FROM_HERE,
88      NewRunnableFunction(&SavePathToLocalState,
89                          username, image_path.value()));
90}
91
92// Deletes user's image file. Runs on FILE thread.
93void DeleteUserImage(const FilePath& image_path) {
94  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
95  if (!file_util::Delete(image_path, false)) {
96    LOG(ERROR) << "Failed to remove user image.";
97    return;
98  }
99}
100
101// Updates current user ownership on UI thread.
102void UpdateOwnership(bool is_owner) {
103  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
104
105  g_user_manager.Get().set_current_user_is_owner(is_owner);
106  NotificationService::current()->Notify(NotificationType::OWNERSHIP_CHECKED,
107                                         NotificationService::AllSources(),
108                                         NotificationService::NoDetails());
109  if (is_owner) {
110    // Also update cached value.
111    UserCrosSettingsProvider::UpdateCachedOwner(
112      g_user_manager.Get().logged_in_user().email());
113  }
114}
115
116// Checks current user's ownership on file thread.
117void CheckOwnership() {
118  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
119  bool is_owner = OwnershipService::GetSharedInstance()->CurrentUserIsOwner();
120  VLOG(1) << "Current user " << (is_owner ? "is owner" : "is not owner");
121
122  g_user_manager.Get().set_current_user_is_owner(is_owner);
123
124  // UserManager should be accessed only on UI thread.
125  BrowserThread::PostTask(
126      BrowserThread::UI,
127      FROM_HERE,
128      NewRunnableFunction(&UpdateOwnership, is_owner));
129}
130
131// Used to handle the asynchronous response of deleting a cryptohome directory.
132class RemoveAttempt : public CryptohomeLibrary::Delegate {
133 public:
134  // Creates new remove attempt for the given user. Note, |delegate| can
135  // be NULL.
136  RemoveAttempt(const std::string& user_email,
137                chromeos::RemoveUserDelegate* delegate)
138      : user_email_(user_email),
139        delegate_(delegate),
140        method_factory_(this) {
141    RemoveUser();
142  }
143
144  void RemoveUser() {
145    // Owner is not allowed to be removed from the device.
146    // Must not proceed without signature verification.
147    UserCrosSettingsProvider user_settings;
148    bool trusted_owner_available = user_settings.RequestTrustedOwner(
149        method_factory_.NewRunnableMethod(&RemoveAttempt::RemoveUser));
150    if (!trusted_owner_available) {
151      // Value of owner email is still not verified.
152      // Another attempt will be invoked after verification completion.
153      return;
154    }
155    if (user_email_ == UserCrosSettingsProvider::cached_owner()) {
156      // Owner is not allowed to be removed from the device. Probably on
157      // the stack, so deffer the deletion.
158      MessageLoop::current()->DeleteSoon(FROM_HERE, this);
159      return;
160    }
161
162    if (delegate_)
163      delegate_->OnBeforeUserRemoved(user_email_);
164
165    chromeos::UserManager::Get()->RemoveUserFromList(user_email_);
166    RemoveUserCryptohome();
167
168    if (delegate_)
169      delegate_->OnUserRemoved(user_email_);
170  }
171
172  void RemoveUserCryptohome() {
173    if (CrosLibrary::Get()->EnsureLoaded()) {
174      CrosLibrary::Get()->GetCryptohomeLibrary()->AsyncRemove(user_email_,
175                                                              this);
176    }
177  }
178
179  void OnComplete(bool success, int return_code) {
180    // Log the error, but there's not much we can do.
181    if (!success) {
182      VLOG(1) << "Removal of cryptohome for " << user_email_
183              << " failed, return code: " << return_code;
184    }
185    delete this;
186  }
187
188 private:
189  std::string user_email_;
190  chromeos::RemoveUserDelegate* delegate_;
191
192  // Factory of callbacks.
193  ScopedRunnableMethodFactory<RemoveAttempt> method_factory_;
194
195  DISALLOW_COPY_AND_ASSIGN(RemoveAttempt);
196};
197
198}  // namespace
199
200UserManager::User::User() {
201  image_ = *ResourceBundle::GetSharedInstance().GetBitmapNamed(
202      kDefaultImageResources[0]);
203}
204
205UserManager::User::~User() {}
206
207std::string UserManager::User::GetDisplayName() const {
208  size_t i = email_.find('@');
209  if (i == 0 || i == std::string::npos) {
210    return email_;
211  }
212  return email_.substr(0, i);
213}
214
215std::string UserManager::User::GetNameTooltip() const {
216  const std::string& user_email = email();
217  size_t at_pos = user_email.rfind('@');
218  if (at_pos == std::string::npos) {
219    NOTREACHED();
220    return std::string();
221  }
222  size_t domain_start = at_pos + 1;
223  std::string domain = user_email.substr(domain_start,
224                                         user_email.length() - domain_start);
225  return base::StringPrintf("%s (%s)",
226                            GetDisplayName().c_str(),
227                            domain.c_str());
228}
229
230// static
231UserManager* UserManager::Get() {
232  // Not thread-safe.
233  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
234  return &g_user_manager.Get();
235}
236
237// static
238void UserManager::RegisterPrefs(PrefService* local_state) {
239  local_state->RegisterListPref(kLoggedInUsers);
240  local_state->RegisterDictionaryPref(kUserImages);
241}
242
243std::vector<UserManager::User> UserManager::GetUsers() const {
244  std::vector<User> users;
245  if (!g_browser_process)
246    return users;
247
248  PrefService* local_state = g_browser_process->local_state();
249  const ListValue* prefs_users = local_state->GetList(kLoggedInUsers);
250  const DictionaryValue* prefs_images = local_state->GetDictionary(kUserImages);
251
252  if (prefs_users) {
253    for (ListValue::const_iterator it = prefs_users->begin();
254         it != prefs_users->end();
255         ++it) {
256      std::string email;
257      if ((*it)->GetAsString(&email)) {
258        User user;
259        user.set_email(email);
260        UserImages::const_iterator image_it = user_images_.find(email);
261        std::string image_path;
262        if (image_it == user_images_.end()) {
263          if (prefs_images &&
264              prefs_images->GetStringWithoutPathExpansion(email, &image_path)) {
265            int default_image_id = kDefaultImagesCount;
266            if (IsDefaultImagePath(image_path, &default_image_id)) {
267              DCHECK(default_image_id < kDefaultImagesCount);
268              int resource_id = kDefaultImageResources[default_image_id];
269              user.set_image(
270                  *ResourceBundle::GetSharedInstance().GetBitmapNamed(
271                      resource_id));
272              user_images_[email] = user.image();
273            } else {
274              // Insert the default image so we don't send another request if
275              // GetUsers is called twice.
276              user_images_[email] = user.image();
277              image_loader_->Start(email, image_path, false);
278            }
279          }
280        } else {
281          user.set_image(image_it->second);
282        }
283        users.push_back(user);
284      }
285    }
286  }
287  return users;
288}
289
290void UserManager::OffTheRecordUserLoggedIn() {
291  user_is_logged_in_ = true;
292  logged_in_user_ = User();
293  logged_in_user_.set_email(kGuestUser);
294  NotifyOnLogin();
295}
296
297void UserManager::UserLoggedIn(const std::string& email) {
298  if (email == kGuestUser) {
299    OffTheRecordUserLoggedIn();
300    return;
301  }
302
303  if (!IsKnownUser(email)) {
304    current_user_is_new_ = true;
305    browser_defaults::skip_restore = true;
306  }
307
308  // Get a copy of the current users.
309  std::vector<User> users = GetUsers();
310
311  // Clear the prefs view of the users.
312  PrefService* prefs = g_browser_process->local_state();
313  ListPrefUpdate prefs_users_update(prefs, kLoggedInUsers);
314  prefs_users_update->Clear();
315
316  user_is_logged_in_ = true;
317  logged_in_user_ = User();
318  logged_in_user_.set_email(email);
319
320  // Make sure this user is first.
321  prefs_users_update->Append(Value::CreateStringValue(email));
322  for (std::vector<User>::iterator it = users.begin();
323       it != users.end();
324       ++it) {
325    std::string user_email = it->email();
326    // Skip the most recent user.
327    if (email != user_email) {
328      prefs_users_update->Append(Value::CreateStringValue(user_email));
329    } else {
330      logged_in_user_ = *it;
331    }
332  }
333  prefs->SavePersistentPrefs();
334  NotifyOnLogin();
335  if (current_user_is_new_)
336    SetDefaultUserImage(email);
337}
338
339void UserManager::RemoveUser(const std::string& email,
340                             RemoveUserDelegate* delegate) {
341  // Get a copy of the current users.
342  std::vector<User> users = GetUsers();
343
344  // Sanity check: we must not remove single user. This check may seem
345  // redundant at a first sight because this single user must be an owner and
346  // we perform special check later in order not to remove an owner.  However
347  // due to non-instant nature of ownership assignment this later check may
348  // sometimes fail. See http://crosbug.com/12723
349  if (users.size() < 2)
350    return;
351
352  bool user_found = false;
353  for (size_t i = 0; !user_found && i < users.size(); ++i)
354    user_found = (email == users[i].email());
355  if (!user_found)
356    return;
357
358  // |RemoveAttempt| deletes itself when done.
359  new RemoveAttempt(email, delegate);
360}
361
362void UserManager::RemoveUserFromList(const std::string& email) {
363  // Get a copy of the current users.
364  std::vector<User> users = GetUsers();
365
366  // Clear the prefs view of the users.
367  PrefService* prefs = g_browser_process->local_state();
368  ListPrefUpdate prefs_users_update(prefs, kLoggedInUsers);
369  prefs_users_update->Clear();
370
371  for (std::vector<User>::iterator it = users.begin();
372       it != users.end();
373       ++it) {
374    std::string user_email = it->email();
375    // Skip user that we would like to delete.
376    if (email != user_email)
377      prefs_users_update->Append(Value::CreateStringValue(user_email));
378  }
379
380  DictionaryPrefUpdate prefs_images_update(prefs, kUserImages);
381  std::string image_path_string;
382  prefs_images_update->GetStringWithoutPathExpansion(email, &image_path_string);
383  prefs_images_update->RemoveWithoutPathExpansion(email, NULL);
384
385  prefs->SavePersistentPrefs();
386
387  int default_image_id = kDefaultImagesCount;
388  if (!IsDefaultImagePath(image_path_string, &default_image_id)) {
389    FilePath image_path(image_path_string);
390    BrowserThread::PostTask(
391        BrowserThread::FILE,
392        FROM_HERE,
393        NewRunnableFunction(&DeleteUserImage,
394                            image_path));
395  }
396}
397
398bool UserManager::IsKnownUser(const std::string& email) {
399  std::vector<User> users = GetUsers();
400  for (std::vector<User>::iterator it = users.begin();
401       it < users.end();
402       ++it) {
403    if (it->email() == email)
404      return true;
405  }
406
407  return false;
408}
409
410const UserManager::User& UserManager::logged_in_user() const {
411  return logged_in_user_;
412}
413
414void UserManager::SetLoggedInUserImage(const SkBitmap& image) {
415  if (logged_in_user_.email().empty())
416    return;
417  OnImageLoaded(logged_in_user_.email(), image, false);
418}
419
420void UserManager::LoadLoggedInUserImage(const FilePath& path) {
421  if (logged_in_user_.email().empty())
422    return;
423  image_loader_->Start(logged_in_user_.email(), path.value(), true);
424}
425
426void UserManager::SaveUserImage(const std::string& username,
427                                const SkBitmap& image) {
428  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
429  FilePath image_path = GetImagePathForUser(username);
430  DVLOG(1) << "Saving user image to " << image_path.value();
431
432  BrowserThread::PostTask(
433      BrowserThread::FILE,
434      FROM_HERE,
435      NewRunnableFunction(&SaveImageToFile,
436                          image, image_path, username));
437}
438
439void UserManager::SaveUserImagePath(const std::string& username,
440                                    const std::string& image_path) {
441  SavePathToLocalState(username, image_path);
442}
443
444void UserManager::SetDefaultUserImage(const std::string& username) {
445  if (!g_browser_process)
446    return;
447
448  PrefService* local_state = g_browser_process->local_state();
449  DCHECK(local_state);
450  const ListValue* prefs_users = local_state->GetList(kLoggedInUsers);
451  DCHECK(prefs_users);
452  const DictionaryValue* prefs_images =
453      local_state->GetDictionary(kUserImages);
454  DCHECK(prefs_images);
455
456  // We want to distribute default images between users uniformly so that if
457  // there're more users with red image, we won't add red one for sure.
458  // Thus we count how many default images of each color are used and choose
459  // the first color with minimal usage.
460  std::vector<int> colors_count(kDefaultImagesCount, 0);
461  for (ListValue::const_iterator it = prefs_users->begin();
462       it != prefs_users->end();
463       ++it) {
464    std::string email;
465    if ((*it)->GetAsString(&email)) {
466      std::string image_path;
467      int default_image_id = kDefaultImagesCount;
468      if (prefs_images->GetStringWithoutPathExpansion(email, &image_path) &&
469          IsDefaultImagePath(image_path, &default_image_id)) {
470        DCHECK(default_image_id < kDefaultImagesCount);
471        ++colors_count[default_image_id];
472      }
473    }
474  }
475  std::vector<int>::const_iterator min_it =
476      std::min_element(colors_count.begin(), colors_count.end());
477  int selected_id = min_it - colors_count.begin();
478  std::string user_image_path =
479      GetDefaultImagePath(selected_id);
480  int resource_id = kDefaultImageResources[selected_id];
481  SkBitmap user_image = *ResourceBundle::GetSharedInstance().GetBitmapNamed(
482      resource_id);
483
484  SavePathToLocalState(username, user_image_path);
485  SetLoggedInUserImage(user_image);
486}
487
488int UserManager::GetUserDefaultImageIndex(const std::string& username) {
489  if (!g_browser_process)
490    return -1;
491
492  PrefService* local_state = g_browser_process->local_state();
493  const DictionaryValue* prefs_images = local_state->GetDictionary(kUserImages);
494
495  if (!prefs_images)
496    return -1;
497
498  std::string image_path;
499  if (!prefs_images->GetStringWithoutPathExpansion(username, &image_path))
500    return -1;
501
502  int image_id = kDefaultImagesCount;
503  if (!IsDefaultImagePath(image_path, &image_id))
504    return -1;
505  return image_id;
506}
507
508void UserManager::OnImageLoaded(const std::string& username,
509                                const SkBitmap& image,
510                                bool should_save_image) {
511  DVLOG(1) << "Loaded image for " << username;
512  user_images_[username] = image;
513  User user;
514  user.set_email(username);
515  user.set_image(image);
516  if (logged_in_user_.email() == username)
517    logged_in_user_.set_image(image);
518  if (should_save_image)
519    SaveUserImage(username, image);
520  NotificationService::current()->Notify(
521      NotificationType::LOGIN_USER_IMAGE_CHANGED,
522      Source<UserManager>(this),
523      Details<const User>(&user));
524}
525
526bool UserManager::IsLoggedInAsGuest() const {
527  return logged_in_user().email() == kGuestUser;
528}
529
530// Private constructor and destructor. Do nothing.
531UserManager::UserManager()
532    : ALLOW_THIS_IN_INITIALIZER_LIST(image_loader_(new UserImageLoader(this))),
533      current_user_is_owner_(false),
534      current_user_is_new_(false),
535      user_is_logged_in_(false) {
536  registrar_.Add(this, NotificationType::OWNER_KEY_FETCH_ATTEMPT_SUCCEEDED,
537      NotificationService::AllSources());
538}
539
540UserManager::~UserManager() {
541  image_loader_->set_delegate(NULL);
542}
543
544FilePath UserManager::GetImagePathForUser(const std::string& username) {
545  std::string filename = username + ".png";
546  FilePath user_data_dir;
547  PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
548  return user_data_dir.AppendASCII(filename);
549}
550
551class RealTPMTokenInfoDelegate : public crypto::TPMTokenInfoDelegate {
552 public:
553  RealTPMTokenInfoDelegate();
554  virtual ~RealTPMTokenInfoDelegate();
555  virtual bool IsTokenReady() const;
556  virtual void GetTokenInfo(std::string* token_name,
557                            std::string* user_pin) const;
558};
559
560RealTPMTokenInfoDelegate::RealTPMTokenInfoDelegate() {}
561RealTPMTokenInfoDelegate::~RealTPMTokenInfoDelegate() {}
562
563bool RealTPMTokenInfoDelegate::IsTokenReady() const {
564  return CrosLibrary::Get()->GetCryptohomeLibrary()->Pkcs11IsTpmTokenReady();
565}
566
567void RealTPMTokenInfoDelegate::GetTokenInfo(std::string* token_name,
568                                            std::string* user_pin) const {
569  std::string local_token_name;
570  std::string local_user_pin;
571  CrosLibrary::Get()->GetCryptohomeLibrary()->Pkcs11GetTpmTokenInfo(
572      &local_token_name, &local_user_pin);
573  if (token_name)
574    *token_name = local_token_name;
575  if (user_pin)
576    *user_pin = local_user_pin;
577}
578
579void UserManager::NotifyOnLogin() {
580  NotificationService::current()->Notify(
581      NotificationType::LOGIN_USER_CHANGED,
582      Source<UserManager>(this),
583      Details<const User>(&logged_in_user_));
584
585  chromeos::CrosLibrary::Get()->GetInputMethodLibrary()->
586      SetDeferImeStartup(false);
587  // Shut down the IME so that it will reload the user's settings.
588  chromeos::CrosLibrary::Get()->GetInputMethodLibrary()->
589      StopInputMethodDaemon();
590  // Let the window manager know that we're logged in now.
591  WmIpc::instance()->SetLoggedInProperty(true);
592  // Ensure we've opened the real user's key/certificate database.
593  crypto::OpenPersistentNSSDB();
594
595  // Only load the Opencryptoki library into NSS if we have this switch.
596  // TODO(gspencer): Remove this switch once cryptohomed work is finished:
597  // http://crosbug.com/12295 and http://crosbug.com/12304
598  if (CommandLine::ForCurrentProcess()->HasSwitch(
599          switches::kLoadOpencryptoki)) {
600    crypto::EnableTPMTokenForNSS(new RealTPMTokenInfoDelegate());
601  }
602
603  // Schedules current user ownership check on file thread.
604  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
605      NewRunnableFunction(&CheckOwnership));
606}
607
608void UserManager::Observe(NotificationType type,
609                          const NotificationSource& source,
610                          const NotificationDetails& details) {
611  if (type == NotificationType::OWNER_KEY_FETCH_ATTEMPT_SUCCEEDED) {
612    BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
613        NewRunnableFunction(&CheckOwnership));
614  }
615}
616
617bool UserManager::current_user_is_owner() const {
618  base::AutoLock lk(current_user_is_owner_lock_);
619  return current_user_is_owner_;
620}
621
622void UserManager::set_current_user_is_owner(bool current_user_is_owner) {
623  base::AutoLock lk(current_user_is_owner_lock_);
624  current_user_is_owner_ = current_user_is_owner;
625}
626
627}  // namespace chromeos
628