[login_manager] Fix race condition that caused ownership to never work

There's a race to create the user's NSSDB between chrome and the
session manager.  If the session manager wins, that directory and the
files within are owned by root, which is bad.

BUG=14008
TEST=unit, and suite_Smoke (incl login_OwnershipTaken)

Change-Id: I58080f281408c1f8e3d6692f5759148667a7bdff

Review URL: http://codereview.chromium.org/6820024
diff --git a/mock_nss_util.h b/mock_nss_util.h
index c958ec2..b3de13f 100644
--- a/mock_nss_util.h
+++ b/mock_nss_util.h
@@ -26,6 +26,7 @@
   MockNssUtil() {}
   virtual ~MockNssUtil() {}
 
+  MOCK_METHOD0(MightHaveKeys, bool());
   MOCK_METHOD0(OpenUserDB, bool());
   MOCK_METHOD1(GetPrivateKey, base::RSAPrivateKey*(const std::vector<uint8>&));
   MOCK_METHOD0(GenerateKeyPair, base::RSAPrivateKey*());
@@ -39,8 +40,7 @@
                           base::RSAPrivateKey* key));
  protected:
   void ExpectGetOwnerKeyFilePath() {
-    EXPECT_CALL(*this, GetOwnerKeyFilePath())
-        .WillOnce(Return(FilePath("")));
+    EXPECT_CALL(*this, GetOwnerKeyFilePath()).WillOnce(Return(FilePath("")));
   }
 };
 
@@ -60,8 +60,8 @@
  public:
   KeyCheckUtil() {
     ExpectGetOwnerKeyFilePath();
-    EXPECT_CALL(*this, OpenUserDB())
-        .WillOnce(Return(true));
+    EXPECT_CALL(*this, MightHaveKeys()).WillOnce(Return(true));
+    EXPECT_CALL(*this, OpenUserDB()).WillOnce(Return(true));
     EXPECT_CALL(*this, GetPrivateKey(_))
         .WillOnce(Return(reinterpret_cast<base::RSAPrivateKey*>(7)));
   }
@@ -72,8 +72,8 @@
  public:
   KeyFailUtil() {
     ExpectGetOwnerKeyFilePath();
-    EXPECT_CALL(*this, OpenUserDB())
-        .WillOnce(Return(true));
+    EXPECT_CALL(*this, MightHaveKeys()).WillOnce(Return(true));
+    EXPECT_CALL(*this, OpenUserDB()).WillOnce(Return(true));
     EXPECT_CALL(*this, GetPrivateKey(_))
         .WillOnce(Return(reinterpret_cast<base::RSAPrivateKey*>(NULL)));
   }
@@ -84,19 +84,27 @@
  public:
   SadNssUtil() {
     ExpectGetOwnerKeyFilePath();
-    EXPECT_CALL(*this, OpenUserDB())
-        .WillOnce(Return(false));
+    EXPECT_CALL(*this, MightHaveKeys()).WillOnce(Return(true));
+    EXPECT_CALL(*this, OpenUserDB()).WillOnce(Return(false));
   }
   virtual ~SadNssUtil() {}
 };
 
+class EmptyNssUtil : public MockNssUtil {
+ public:
+  EmptyNssUtil() {
+    ExpectGetOwnerKeyFilePath();
+    EXPECT_CALL(*this, MightHaveKeys()).WillOnce(Return(false));
+  }
+  virtual ~EmptyNssUtil() {}
+};
+
 class ShortKeyGenerator : public MockNssUtil {
  public:
   ShortKeyGenerator() {
     base::EnsureNSSInit();
     base::OpenPersistentNSSDB();
-    ON_CALL(*this, GenerateKeyPair())
-        .WillByDefault(Invoke(CreateFake));
+    ON_CALL(*this, GenerateKeyPair()).WillByDefault(Invoke(CreateFake));
   }
   virtual ~ShortKeyGenerator() {}
 
@@ -111,10 +119,9 @@
  public:
   ShortKeyUtil() {
     ExpectGetOwnerKeyFilePath();
-    EXPECT_CALL(*this, OpenUserDB())
-        .WillOnce(Return(true));
-    EXPECT_CALL(*this, GenerateKeyPair())
-        .Times(1);
+    EXPECT_CALL(*this, MightHaveKeys()).WillOnce(Return(true));
+    EXPECT_CALL(*this, OpenUserDB()).WillOnce(Return(true));
+    EXPECT_CALL(*this, GenerateKeyPair()).Times(1);
   }
   virtual ~ShortKeyUtil() {}
 };
diff --git a/nss_util.cc b/nss_util.cc
index d87e9b2..a55e6d2 100644
--- a/nss_util.cc
+++ b/nss_util.cc
@@ -9,6 +9,7 @@
 #include <base/crypto/signature_creator.h>
 #include <base/crypto/signature_verifier.h>
 #include <base/file_path.h>
+#include <base/file_util.h>
 #include <base/logging.h>
 #include <base/nss_util.h>
 #include <base/scoped_ptr.h>
@@ -33,6 +34,8 @@
   NssUtilImpl();
   virtual ~NssUtilImpl();
 
+  bool MightHaveKeys();
+
   bool OpenUserDB();
 
   base::RSAPrivateKey* GetPrivateKey(const std::vector<uint8>& public_key_der);
@@ -51,6 +54,10 @@
             base::RSAPrivateKey* key);
  private:
   static const uint16 kKeySizeInBits;
+  // Hardcoded path of the user's NSS key database.
+  // TODO(cmasone): get rid of this once http://crosbug.com/14007 is fixed.
+  static const char kUserDbPath[];
+
   DISALLOW_COPY_AND_ASSIGN(NssUtilImpl);
 };
 
@@ -76,11 +83,17 @@
 // We're generating and using 2048-bit RSA keys.
 // static
 const uint16 NssUtilImpl::kKeySizeInBits = 2048;
+// static
+const char NssUtilImpl::kUserDbPath[] = "/home/chronos/user/.pki/nssdb/key4.db";
 
 NssUtilImpl::NssUtilImpl() {}
 
 NssUtilImpl::~NssUtilImpl() {}
 
+bool NssUtilImpl::MightHaveKeys() {
+  return file_util::PathExists(FilePath(NssUtilImpl::kUserDbPath));
+}
+
 bool NssUtilImpl::OpenUserDB() {
   // TODO(cmasone): If we ever try to keep the session_manager alive across
   // user sessions, we'll need to deal with the fact that we have no way to
diff --git a/nss_util.h b/nss_util.h
index bd53777..2ecc611 100644
--- a/nss_util.h
+++ b/nss_util.h
@@ -42,6 +42,8 @@
 
   static void BlobFromBuffer(const std::string& buf, std::vector<uint8>* out);
 
+  virtual bool MightHaveKeys() = 0;
+
   virtual bool OpenUserDB() = 0;
 
   // Caller takes ownership of returned key.
diff --git a/session_manager_service.cc b/session_manager_service.cc
index e02b5f5..8f5ca0a 100644
--- a/session_manager_service.cc
+++ b/session_manager_service.cc
@@ -1139,6 +1139,8 @@
 gboolean SessionManagerService::CurrentUserHasOwnerKey(
     const std::vector<uint8>& pub_key,
     GError** error) {
+  if (!nss_->MightHaveKeys())
+    return FALSE;
   if (!nss_->OpenUserDB()) {
     const char msg[] = "Could not open the current user's NSS database.";
     LOG(ERROR) << msg;