entd: move opencryptoki initialization out of entd

Look for a file /home/chronos/.cryptohome-init-pkcs11 and expect cryptohome to initialize the PKCS#11 token, disabling that interface in newer extensions.

Change-Id: I363ec0ef093d58d793a3d75e22dee96b58bdacd3

R=rginda@chromium.org
BUG=chromium-os:12304
TEST=
old extension / new entd (w/ and w/o flag)
new extension / old entd:
new extension / new entd (w/ and w/o flag):

When flag is set with new entd, Initialize button disappears and we wait for cryptohomed to signal the TPM token is ready.

Review URL: http://codereview.chromium.org/6821025
diff --git a/base_policy/policy-utils.js b/base_policy/policy-utils.js
index 03d9964..85b5587 100644
--- a/base_policy/policy-utils.js
+++ b/base_policy/policy-utils.js
@@ -1241,7 +1241,7 @@
  */
 Policy.Callbacks.prototype['cb:info'] =
 function cb_info() {
-  return Policy.CallbackSuccess({
+  var callback_data = {
    description: this.policy.manifest.description,
    version: this.policy.manifest.version,
    username: entd.username,
@@ -1253,13 +1253,19 @@
      isOwned: (entd.isLibcrosLoaded ? entd.tpm.isOwned : true),
      isBeingOwned: (entd.isLibcrosLoaded ? entd.tpm.isBeingOwned : false),
      statusString: (entd.isLibcrosLoaded ? entd.tpm.statusString :
-                    "libcros not loaded"),
+                    "libcros not loaded")
    },
    pkcs11: {
      state: this.policy.pkcs11.state,
      log: this.policy.getLog(this.policy.pkcs11)
    }
-  });
+  }
+  if ('isTokenReady' in entd.tpm) {
+    callback_data.pkcs11.isTokenReady =
+      (entd.isLibcrosLoaded ? entd.tpm.isTokenReady : false);
+  }
+
+  return Policy.CallbackSuccess(callback_data);
 };
 
 /**
@@ -1281,6 +1287,7 @@
  * if the operation completes successfully but the token is not ready due to
  * some unexpected condition, it will become 'stop:user-pin'.  On error it will
  * become 'stop:error'.
+ * TODO(crosbug.com/14277): Remove SetPIN functions.
  */
 Policy.Callbacks.prototype['cb:setUserPin'] =
 function cb_setUserPin(arg) {
@@ -1413,6 +1420,7 @@
  * initialization is in progress the token state will be 'start:init'.  If the
  * operation completes successfully the token state will become 'stop:init'.
  * On error it will become 'stop:error'.
+ * TODO(crosbug.com/14277): Remove initToken function.
  */
 Policy.Callbacks.prototype['cb:initToken'] =
 function cb_initToken(arg) {
@@ -1668,6 +1676,8 @@
  */
 Policy.CallbackError =
 function CallbackError(data) {
+  // Log the error to syslogs for further diagnosis.
+  entd.syslog.error(data);
   return { status: 'error', data: data };
 };
 
diff --git a/bin/entdwife.sh b/bin/entdwife.sh
index b602e5a..dad3a05 100755
--- a/bin/entdwife.sh
+++ b/bin/entdwife.sh
@@ -292,12 +292,16 @@
 
   local extid="$(basename $(dirname "$extension"))"
 
+  local cryptohome_flag=""
+  if [ -r "/home/chronos/.cryptohome-init-pkcs11" ]; then
+    cryptohome_flag="--cryptohome-init-pkcs11"
+  fi
   # Run entd in the background and wait on it - this allows the
   # shell interpreter to catch TERM signal and clean up session_path.
   "$FLAGS_entd" --utility="$FLAGS_utility" "$root_ca_option" \
     --policy="$extension/policy.js" --manifest="$extension/manifest.json" \
     --username="$FLAGS_username" --callback-origin=chrome-extension://"$extid" \
-    --session-id="$session_id" &
+    --session-id="$session_id" $cryptohome_flag &
   local pid=$!
   wait $pid
 }
diff --git a/main.cc b/main.cc
index 299ece7..caa1784 100644
--- a/main.cc
+++ b/main.cc
@@ -15,6 +15,7 @@
 #include "entd/callback_server.h"
 #include "entd/http.h"
 #include "entd/pkcs11.h"
+#include "entd/tpm.h"
 #include "entd/utils.h"
 
 namespace switches {
@@ -58,6 +59,10 @@
 
 static const char *kSessionId = "session-id";
 
+// TODO(crosbug.com/14277): Remove option and assume it is true.
+static const char *kCryptohomeInitPkcs11 =
+    "cryptohome-init-pkcs11";
+
 }  // namespace switches
 
 // Return values:
@@ -139,6 +144,11 @@
     LOG(INFO) << "Setting libcros location: " << entd::Entd::libcros_location;
   }
 
+  if (cl->HasSwitch(switches::kCryptohomeInitPkcs11)) {
+    LOG(INFO) << "Expecting cryptohome to initialize the TPM token";
+    entd::Tpm::cryptohome_init_pkcs11 = true;
+  }
+
   if (cl->HasSwitch(switches::kAllowFileIO)) {
     LOG(INFO) << "Allowing File IO.";
     entd::Entd::allow_file_io = true;
diff --git a/reference_extension/client.js b/reference_extension/client.js
index 8bfdd17..80b677c 100644
--- a/reference_extension/client.js
+++ b/reference_extension/client.js
@@ -12,6 +12,8 @@
  */
 client.policyCallbackPort = 5199;
 
+client.cryptohome_init_pkcs11 = false;
+
 /**
  * Initialize the client.
  */
@@ -172,6 +174,12 @@
           text('Ready').
           attr('status', 'green');
 
+        // Use presence of isTokenReady to determine if
+        // cryptohome_init_pkcs11 is true.
+        // TODO(crosbug.com/14277): Remove this conditional and code
+        // to recognize if TPM has been initialized (only check token).
+        client.cryptohome_init_pkcs11 = 'isTokenReady' in pkcs11;
+
         if (retval.data.isLibcrosLoaded && !retval.data.tpm.isEnabled) {
           if (!tpmError) {
             client.showError("Your TPM is not enabled.  Please enable " +
@@ -202,6 +210,19 @@
               attr('status', 'red');
             tpmError = true;
           }
+        } else if (retval.data.isLibcrosLoaded &&
+                   client.cryptohome_init_pkcs11 &&
+                   !pkcs11.isTokenReady) {
+          if (!tpmError) {
+            client.showAlert('Please wait while your TPM Token is being ' +
+                             'created.  This dialog should go away on its ' +
+                             'own when the process completes.', 'Alert',
+                             options);
+            $('#entd-message').
+              text('Waiting for TPM Token.').
+              attr('status', 'red');
+            tpmError = true;
+          }
         } else {
           ready = true;
         }
@@ -387,6 +408,7 @@
  *
  * This causes the token initialization progress dialog to be shown, and manages
  * the asynchronous initialization of a token.
+ * TODO(crosbug.com/14277): Remove token initialization UI.
  */
 client.initToken =
 function initToken(token, force) {
@@ -957,8 +979,8 @@
   $(li).html(
       '<table width="100%">' +
       '<tr><td><span class="desc"></span> (<span class="label"></span>)</td>' +
-      '<td rowspan="2" width="1%"><button>Initialize</button></td></tr>' +
-      '<tr><td class="status"></td></tr></table>');
+      '<td rowspan="2" width="1%"><button class="init-button">Initialize' +
+      '</button></td></tr><tr><td class="status"></td></tr></table>');
 
   $(li).find('button').click(function () {
       client.onTokenClick_(client.tokens[token.slotId]);
@@ -1000,7 +1022,13 @@
   $('.label', li).text(token.label || 'Unlabeled');
   $('.status', li).attr('status', color);
   $('.status', li).text(status);
-  $('button', li).text(color == 'red' ? 'Initialize' : 'Reinitialize');
+  if (client.cryptohome_init_pkcs11) {
+    // If automatic initialization is enabled, do not give the user
+    // the option to initialize.
+    $('.init-button', li).css('display', 'none');
+  } else {
+    $('button', li).text(color == 'red' ? 'Initialize' : 'Reinitialize');
+  }
 }
 
 /**
diff --git a/reference_extension/options.html b/reference_extension/options.html
index f4a6d50..868eb20 100644
--- a/reference_extension/options.html
+++ b/reference_extension/options.html
@@ -210,6 +210,7 @@
           </form>
         </div>
 
+        <!-- TODO(crosbug.com/14277): Remove initialization UI. -->
         <div class="modal-dialog" id="token-status">
           <b>Initializing PKCS#11 Token</b>
           <hr>
diff --git a/tpm.cc b/tpm.cc
index 95b7e8a..04d3f5a 100644
--- a/tpm.cc
+++ b/tpm.cc
@@ -9,6 +9,8 @@
 
 namespace entd {
 
+bool Tpm::cryptohome_init_pkcs11 = false;
+
 bool Tpm::Initialize() {
   return true;
 }
@@ -52,6 +54,14 @@
                           v8::Handle<v8::Value>(),  // Don't need any data.
                           v8::DEFAULT,  // DEFAULT AccessControl
                           v8::DontDelete);
+  if (cryptohome_init_pkcs11) {
+    instance_t->SetAccessor(v8::String::New("isTokenReady"),
+                            Tpm::IsTokenReady,
+                            0,  // readonly, so setter is NULL
+                            v8::Handle<v8::Value>(),  // Don't need any data.
+                            v8::DEFAULT,  // DEFAULT AccessControl
+                            v8::DontDelete);
+  }
 
   return true;
 }
@@ -103,4 +113,10 @@
   return v8::String::New(status.c_str());
 }
 
+// static
+v8::Handle<v8::Value> Tpm::IsTokenReady(v8::Local<v8::String> property,
+                                        const v8::AccessorInfo& info) {
+  return v8::Boolean::New(chromeos::CryptohomePkcs11IsTpmTokenReady());
+}
+
 }  // namespace entd
diff --git a/tpm.h b/tpm.h
index 3c5ef51..31b9eae 100644
--- a/tpm.h
+++ b/tpm.h
@@ -30,6 +30,10 @@
                                             const v8::AccessorInfo& info);
   static v8::Handle<v8::Value> GetStatusString(v8::Local<v8::String> property,
                                                const v8::AccessorInfo& info);
+  static v8::Handle<v8::Value> IsTokenReady(v8::Local<v8::String> property,
+                                            const v8::AccessorInfo& info);
+
+  static bool cryptohome_init_pkcs11;
 };
 
 }  // namespace entd