Enforce block_devmode flag in chromeos_startup.

When the block_devmode crossystem flag has been set by the device
owner, we should block boots in developer mode.

BUG=chromium:375775
TEST=Manual.
CQ-DEPEND=CL:201640

Change-Id: I6ec90bf6377794f22ed08b40b750c3329eb183e6
Reviewed-on: https://chromium-review.googlesource.com/201650
Reviewed-by: Mattias Nissler <mnissler@chromium.org>
Tested-by: Mattias Nissler <mnissler@chromium.org>
Commit-Queue: Julian Pastarmov <pastarmovj@chromium.org>
diff --git a/chromeos-boot-alert b/chromeos-boot-alert
index 9d8b8dc..a2e5bdf 100755
--- a/chromeos-boot-alert
+++ b/chromeos-boot-alert
@@ -39,6 +39,10 @@
 
   dev_fwcheck: Message when staring with developer firmware, to check if there
                is available updates.
+
+  block_devmode: Message shown to indicate that dev mode is blocked by request
+                 of the device owner.
+                 Arg: none
 "
 }
 
@@ -177,8 +181,8 @@
     "20")  # Spacebar
       crossystem recovery_request=1
       reboot
-      # To prevent system continue booting
-      sleep 1d
+      # To prevent the system from continuing to boot.
+      sleep infinity
       ;;
   esac
   return 0
@@ -360,6 +364,59 @@
   fi
 }
 
+# Prints a message telling the user that developer mode has been disabled for
+# the device upon request by the device owner. Depending on hardware support,
+# the system switches back to verified boot mode automatically or prompts the
+# user to do so. The system reboots after the user confirms by pressing space or
+# after timeout.
+mode_block_devmode() {
+  local delay_secs=30
+
+  if has_virtual_dev_switch; then
+    show_assets_message_image "block_devmode_virtual" ||
+    # The text by echo below is only fallback of localized assets message.
+    # Please sync with text there if you want to change the message.
+    echo "
+      The device owner has disabled Developer Mode for this device. Please ask
+      the device owner to remove the restriction if you want to use Developer
+      Mode.
+
+      The system will reboot in Verified Mode.
+    " >"$STDOUT"
+  else
+    # Leave the notification on the screen for 5 minutes to increase chances
+    # the user actually sees it before shutting down the device.
+    delay_secs=300
+    show_assets_message_image "block_devmode" ||
+    # The text by echo below is only fallback of localized assets message.
+    # Please sync with text there if you want to change the message.
+    echo "
+      The device owner has disabled Developer Mode for this device. Please ask
+      the device owner to remove the restriction if you want to use Developer
+      Mode.
+
+      Please toggle the Developer Switch and reboot the device to return to
+      Verified Mode.
+    " >"$STDOUT"
+  fi
+
+  # Read a space bar or timeout.
+  local input=$(match_char_timeout "$delay_secs" 20 < "$STDOUT")
+  tput clear > "$STDOUT"
+
+  if has_virtual_dev_switch; then
+    # Return to verified mode.
+    crossystem disable_dev_request=1
+    reboot
+  else
+    # Shut down instead of rebooting in a loop.
+    halt
+  fi
+
+  # To prevent the system from continuing to boot.
+  sleep infinity
+}
+
 # Main initialization and dispatcher
 main() {
   # process args
@@ -387,7 +444,8 @@
 
   case "$mode" in
     "warn_dev" | "enter_dev" | "leave_dev" | "dev_fwcheck" | \
-      "update_firmware" | "wipe" | "power_wash" | "self_repair" )
+      "update_firmware" | "wipe" | "power_wash" | "self_repair" | \
+      "block_devmode" )
       mode_"$mode" "$@"
       ;;
     * )
diff --git a/chromeos_startup b/chromeos_startup
index db98729..22e762e 100755
--- a/chromeos_startup
+++ b/chromeos_startup
@@ -60,6 +60,25 @@
 # processes.
 sysctl -q -p /etc/sysctl.conf
 
+# CROS_DEBUG equals one if we've booted in developer mode or we've
+# booted a developer image.
+crossystem "cros_debug?1"
+CROS_DEBUG=$((! $?))
+
+# Check whether the device is allowed to boot in dev mode. If firmware write
+# protect is off or a debug build is already installed on the system, ignore
+# block_devmode. It is pointless in these cases, as the device is already in a
+# state where the local user has full control.
+#
+# The up-front CROS_DEBUG check avoids forking a crossystem process in verified
+# mode, thus keeping the check as lightweight as possible for normal boot.
+if [ $CROS_DEBUG -eq 1 ]; then
+  if crossystem "block_devmode?1" "wpsw_boot?1" "debug_build?0" \
+                "devsw_boot?1"; then
+    chromeos-boot-alert block_devmode /dev/tty1
+  fi
+fi
+
 # Prepare to mount stateful partition
 ROOT_DEV=$(rootdev -s)
 ROOTDEV_RET_CODE=$?
@@ -150,11 +169,6 @@
   FACTORY_MODE=factory
 fi
 
-# CROS_DEBUG equals one if we've booted in developer mode or we've
-# booted a developer image.
-crossystem "cros_debug?1"
-CROS_DEBUG=$((! $?))
-
 # Check if we need to perform firmware update: only if fwupdate_tries is
 # non-zero or if not using normal (ex, developer or recovery) firmware.
 FIRMWARE_UPDATE_SCRIPT=/usr/sbin/chromeos-firmwareupdate