blob: aab82d8cbd3f35f79572b1921e45f401d2af2e86 [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package hwsec
import (
"context"
"strconv"
"strings"
"time"
"chromiumos/tast/common/hwsec"
"chromiumos/tast/common/storage/files"
"chromiumos/tast/errors"
"chromiumos/tast/remote/bundles/cros/hwsec/util"
hwsecremote "chromiumos/tast/remote/hwsec"
"chromiumos/tast/testing"
)
const (
// waitForCryptohomedTimeout is the timeout waiting for cryptohomed to respawn.
waitForCryptohomedTimeout = 30 * time.Second
)
func init() {
testing.AddTest(&testing.Test{
Func: CryptohomeCrashDataSurvival,
Desc: "Checks when cryptohome crashed or is forcefully killed, user's data are not lost",
Contacts: []string{
"zuan@chromium.org", // Test author
"cros-hwsec@google.com",
},
Attr: []string{"group:mainline", "informational"},
SoftwareDeps: []string{"tpm"},
})
}
// getCryptohomedPID query the cryptohomed's PID.
func getCryptohomedPID(ctx context.Context, r hwsec.CmdRunner) (int, error) {
raw, err := r.Run(ctx, "pidof", "cryptohomed")
if err != nil {
return -1, errors.Wrap(err, "failed to run pidof to get cryptohomed pid")
}
out := strings.TrimSpace(string(raw))
pid, err := strconv.Atoi(out)
if err != nil {
return -1, errors.Wrapf(err, "failed to parse pid from str %q", out)
}
return pid, nil
}
func CryptohomeCrashDataSurvival(ctx context.Context, s *testing.State) {
r := hwsecremote.NewCmdRunner(s.DUT())
helper, err := hwsecremote.NewHelper(r, s.DUT())
if err != nil {
s.Fatal("Helper creation error: ", err)
}
utility := helper.CryptohomeClient()
dc := hwsec.NewDaemonController(r)
// Clear any remnant data on the DUT.
utility.UnmountAndRemoveVault(ctx, util.FirstUsername)
// Create a user vault for testing.
if err := utility.MountVault(ctx, util.Password1Label, hwsec.NewPassAuthConfig(util.FirstUsername, util.FirstPassword1), true, hwsec.NewVaultConfig()); err != nil {
s.Fatal("Failed to create user: ", err)
}
defer func() {
if err := utility.UnmountAndRemoveVault(ctx, util.FirstUsername); err != nil {
s.Error("Failed to remove user vault: ", err)
}
}()
// Create test files
hf, err := files.NewHomedirFiles(ctx, utility, r, util.FirstUsername)
if err != nil {
s.Fatal("Failed to create HomedirFiles for testing files in user's home directory: ", err)
}
if err = hf.Clear(ctx); err != nil {
s.Fatal("Failed to clear test files in the user's home directory: ", err)
}
if err = hf.StepAll(ctx); err != nil {
s.Fatal("Failed to initialize the test files in the user's home directory: ", err)
}
// Get cryptohomed's current pid.
lastPid, err := getCryptohomedPID(ctx, r)
if err != nil {
s.Fatal("Failed to get cryptohomed's pid before kill: ", err)
}
// Kill cryptohomed and wait for it to restart
if _, err = r.Run(ctx, "killall", "-SIGKILL", "cryptohomed"); err != nil {
s.Fatal("Failed to kill cryptohomed")
}
// We need to wait for the new pid to appear, because it takes dbus some time to realize that
// the cryptohome dbus service is gone.
if err := testing.Poll(ctx, func(ctx context.Context) error {
pid, err := getCryptohomedPID(ctx, r)
if err != nil {
return errors.Wrap(err, "cryptohomed pid unavailable")
}
if pid == lastPid {
return errors.Errorf("cryptohomed pid %d did not change", pid)
}
return nil
}, &testing.PollOptions{Timeout: waitForCryptohomedTimeout}); err != nil {
s.Fatal("Failed to wait for cryptohomed to come back: ", err)
}
// Wait for Cryptohomed service to come back.
if err := dc.WaitForAllDBusServices(ctx); err != nil {
s.Fatal("DBus services did not return: ", err)
}
// Cryptohome is back
// Unmount and check that the files are no longer accessible.
if err = utility.UnmountAll(ctx); err != nil {
s.Fatal("Failed to unmount all: ", err)
}
if err = hf.VerifyAll(ctx); err == nil {
s.Error("Files still visible after Unmount() post crash")
}
// Mount again and things should be fine.
if err = utility.MountVault(ctx, util.Password1Label, hwsec.NewPassAuthConfig(util.FirstUsername, util.FirstPassword1), false, hwsec.NewVaultConfig()); err != nil {
s.Fatal("Failed to mount user post crash: ", err)
}
if err = hf.VerifyAll(ctx); err != nil {
s.Fatal("Files invalid after remount post crash: ", err)
}
if err = hf.StepAll(ctx); err != nil {
s.Fatal("Unable to write files after remount post crash: ", err)
}
// Unmount before restart.
// Note that if we unmount here, we'll not test the case of restarting cryptohome when a vault is mounted.
// TODO(b/205502383): Add testing of unclean cryptohome shutdown and subsequent mount.
if err = utility.UnmountAll(ctx); err != nil {
s.Fatal("Failed to unmount all: ", err)
}
// Restart all daemons to simulate a reboot.
if err := dc.RestartTPMDaemons(ctx); err != nil {
s.Fatal("Failed to restart TPM daemons: ", err)
}
if err = utility.MountVault(ctx, util.Password1Label, hwsec.NewPassAuthConfig(util.FirstUsername, util.FirstPassword1), false, hwsec.NewVaultConfig()); err != nil {
s.Fatal("Failed to mount user post restart: ", err)
}
if err = hf.VerifyAll(ctx); err != nil {
s.Fatal("Files invalid after mount post restart: ", err)
}
if err = hf.StepAll(ctx); err != nil {
s.Fatal("Unable to write files after mount post restart: ", err)
}
}