tpm: Store TPM state under /run/vm

When running in multiprocess mode, such as on a device, TPM state gets
placed in /run/vm/tpm.{pid} (e.g. /run/vm/tpm.22726) where pid is the
pid of the original crosvm process. The TPM simulator will write a
single file called NVChip of size 16384 bytes into this directory. The
directory and NVChip file will have uid and pid set to crosvm.

When running without multiprocess mode / without minijail / probably in
cros_sdk, TPM state is placed in /tmp/tpm-simulator as before. The
/run/vm directory is not present under cros_sdk.

Will follow up with a separate CL to remove the TPM state directory at
crosvm exit.

Tested by running the following on a grunt board (Barla) in dev mode:

    sudo crosvm run \
        --root rootfs.ext4 \
        --socket crosvm.sock \
        --seccomp-policy-dir seccomp \
        --software-tpm \
        -p init=/bin/bash \
        -p panic=-1 \
        vmlinux.bin

and confirming that /dev/tpm0 and /dev/tpmrm0 are present in the VM.

BUG=chromium:921841
TEST=manual testing on grunt

Change-Id: I1868896b9eb6f510d8b97022ba950b3604d9d40b
Reviewed-on: https://chromium-review.googlesource.com/1496910
Commit-Ready: David Tolnay <dtolnay@chromium.org>
Tested-by: David Tolnay <dtolnay@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Dylan Reid <dgreid@chromium.org>
diff --git a/src/linux.rs b/src/linux.rs
index 6d09cbf..a8f7ae9 100644
--- a/src/linux.rs
+++ b/src/linux.rs
@@ -19,7 +19,7 @@
 use std::thread::JoinHandle;
 use std::time::{Duration, SystemTime, UNIX_EPOCH};
 
-use libc::{self, c_int};
+use libc::{self, c_int, gid_t, uid_t};
 
 use audio_streams::DummyStreamSource;
 use byteorder::{ByteOrder, LittleEndian};
@@ -312,11 +312,48 @@
 
     #[cfg(feature = "tpm")]
     {
+        use std::ffi::CString;
+        use std::fs;
+        use std::process;
+        use sys_util::chown;
+
         if cfg.software_tpm {
-            let tpm_box = Box::new(devices::virtio::Tpm::new());
+            let tpm_storage: PathBuf;
+            let mut tpm_jail = simple_jail(&cfg, "tpm_device.policy")?;
+
+            match &mut tpm_jail {
+                Some(jail) => {
+                    // Create a tmpfs in the device's root directory for tpm
+                    // simulator storage. The size is 20*1024, or 20 KB.
+                    jail.mount_with_data(
+                        Path::new("none"),
+                        Path::new("/"),
+                        "tmpfs",
+                        (libc::MS_NOSUID | libc::MS_NODEV | libc::MS_NOEXEC) as usize,
+                        "size=20480",
+                    )?;
+
+                    let crosvm_ids = add_crosvm_user_to_jail(jail, "tpm")?;
+
+                    let pid = process::id();
+                    let tpm_pid_dir = format!("/run/vm/tpm.{}", pid);
+                    tpm_storage = Path::new(&tpm_pid_dir).to_owned();
+                    fs::create_dir_all(&tpm_storage)?;
+                    let tpm_pid_dir_c = CString::new(tpm_pid_dir).expect("no nul bytes");
+                    chown(&tpm_pid_dir_c, crosvm_ids.uid, crosvm_ids.gid)?;
+
+                    jail.mount_bind(&tpm_storage, &tpm_storage, true)?;
+                }
+                None => {
+                    // Path used inside cros_sdk which does not have /run/vm.
+                    tpm_storage = Path::new("/tmp/tpm-simulator").to_owned();
+                }
+            }
+
+            let tpm = devices::virtio::Tpm::new(tpm_storage);
             devs.push(VirtioDeviceStub {
-                dev: tpm_box,
-                jail: simple_jail(&cfg, "tpm_device.policy")?,
+                dev: Box::new(tpm),
+                jail: tpm_jail,
             });
         }
     }
@@ -665,12 +702,17 @@
     Ok(pci_devices)
 }
 
+struct Ids {
+    uid: uid_t,
+    gid: gid_t,
+}
+
 // Set the uid/gid for the jailed process and give a basic id map. This is
 // required for bind mounts to work.
 fn add_crosvm_user_to_jail(
     jail: &mut Minijail,
     feature: &str,
-) -> std::result::Result<(), Box<Error>> {
+) -> std::result::Result<Ids, Box<Error>> {
     let crosvm_user_group = CStr::from_bytes_with_nul(b"crosvm\0").unwrap();
 
     let crosvm_uid = match get_user_id(&crosvm_user_group) {
@@ -696,7 +738,10 @@
     jail.gidmap(&format!("{0} {0} 1", crosvm_gid))
         .map_err(Error::SettingGidMap)?;
 
-    Ok(())
+    Ok(Ids {
+        uid: crosvm_uid,
+        gid: crosvm_gid,
+    })
 }
 
 fn raw_fd_from_path(path: &PathBuf) -> std::result::Result<RawFd, Box<Error>> {