blob: 19625d1b3d6d4f3bf69e271f694a8dc0c7ba4948 [file] [log] [blame] [edit]
// Copyright 2019 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 session
import (
"context"
"os"
"path/filepath"
"syscall"
"github.com/golang/protobuf/proto"
"chromiumos/policy/enterprise_management"
"chromiumos/tast/errors"
"chromiumos/tast/local/chrome/chromeproc"
"chromiumos/tast/local/cryptohome"
"chromiumos/tast/local/session"
"chromiumos/tast/local/session/ownership"
"chromiumos/tast/local/sysutil"
"chromiumos/tast/local/upstart"
"chromiumos/tast/testing"
)
func init() {
testing.AddTest(&testing.Test{
Func: UserPolicyKeys,
Desc: "Verifies that, after policy is pushed, the user policy key winds up stored in the right place",
Contacts: []string{
"mnissler@chromium.org", // session_manager owner
"hidehiko@chromium.org", // Tast port author
},
SoftwareDeps: []string{"chrome"},
Data: []string{"testcert.p12"},
Attr: []string{"group:mainline"},
})
}
func UserPolicyKeys(ctx context.Context, s *testing.State) {
const (
testUser = "test@foo.com"
testPass = "test_password"
)
privKey, err := session.ExtractPrivKey(s.DataPath("testcert.p12"))
if err != nil {
s.Fatal("Failed to parse PKCS #12 file: ", err)
}
testDesc := ownership.UserPolicyDescriptor(testUser)
userHash, err := cryptohome.UserHash(ctx, testUser)
if err != nil {
s.Fatalf("Failed to find user hash for %s: %v", testUser, err)
}
keyFile := filepath.Join("/run/user_policy", userHash, "policy.pub")
readable := func(fi os.FileInfo, uid, gid uint32) (bool, error) {
perm := fi.Mode().Perm()
st, ok := fi.Sys().(*syscall.Stat_t)
if st == nil || !ok {
return false, errors.New("failed to find uid/gid")
}
if st.Uid == uid {
return (perm & 0400) != 0, nil
}
if st.Gid == gid {
return (perm & 0040) != 0, nil
}
return (perm & 0004) != 0, nil
}
executable := func(fi os.FileInfo, uid, gid uint32) (bool, error) {
perm := fi.Mode().Perm()
st, ok := fi.Sys().(*syscall.Stat_t)
if st == nil || !ok {
return false, errors.New("failed to find uid/gid")
}
if st.Uid == uid {
return (perm & 0100) != 0, nil
}
if st.Gid == gid {
return (perm & 0010) != 0, nil
}
return (perm & 0001) != 0, nil
}
verifyKeyFile := func(p string) error {
fi, err := os.Stat(p)
if err != nil {
return errors.Wrapf(err, "failed to stat %s", p)
}
if !fi.Mode().IsRegular() {
return errors.Errorf("%s is not a regular file", p)
}
if ok, err := readable(fi, sysutil.ChronosUID, sysutil.ChronosGID); err != nil {
return errors.Wrapf(err, "failed to check readability of %s", p)
} else if !ok {
return errors.Errorf("%s is unreadable by chronos", p)
}
// Ensure parent directories are executable by chronos.
for {
p = filepath.Dir(p)
di, err := os.Stat(p)
if err != nil {
return errors.Wrapf(err, "failed to stat %s", p)
}
if ok, err := executable(di, sysutil.ChronosUID, sysutil.ChronosGID); err != nil {
return errors.Wrapf(err, "failed to check executability of %s", p)
} else if !ok {
return errors.Errorf("%s is unexecutable by chronos", p)
}
if p == "/" {
break
}
}
return nil
}
policy, err := session.BuildPolicy("", privKey, nil, &enterprise_management.ChromeDeviceSettingsProto{})
if err != nil {
s.Fatal("Failed to build test policy data: ", err)
}
if err := session.SetUpDevice(ctx); err != nil {
s.Fatal("Failed to reset device ownership: ", err)
}
sm, err := session.NewSessionManager(ctx)
if err != nil {
s.Fatal("Failed to create session_manager binding: ", err)
}
if err := session.PrepareChromeForPolicyTesting(ctx, sm); err != nil {
s.Fatal("Failed to prepare Chrome for testing: ", err)
}
// Create clean vault for the test user, and start the session.
if err = cryptohome.RemoveVault(ctx, testUser); err != nil {
s.Fatal("Failed to remove vault: ", err)
}
if err = cryptohome.CreateVault(ctx, testUser, testPass); err != nil {
s.Fatal("Failed to create vault: ", err)
}
if err := sm.StartSession(ctx, testUser, ""); err != nil {
s.Fatalf("Failed to start session for %s: %v", testUser, err)
}
// No policy stored yet.
if ret, err := sm.RetrievePolicyEx(ctx, testDesc); err != nil {
s.Fatalf("Failed to retrieve policy for %s: %v", testUser, err)
} else if !proto.Equal(ret, &enterprise_management.PolicyFetchResponse{}) {
s.Fatal("Unexpected policy is fetched for ", testUser)
}
if _, err := os.Stat(keyFile); err == nil {
s.Fatalf("%s exists unexpectedly", keyFile)
} else if !os.IsNotExist(err) {
s.Fatalf("Failed to stat %s: %v", keyFile, err)
}
// Now store a policy.
if err := sm.StorePolicyEx(ctx, testDesc, policy); err != nil {
s.Fatal("Failed to store user policy: ", err)
}
// The policy key file should have been created now.
if err := verifyKeyFile(keyFile); err != nil {
s.Fatal("Failed to verify created key: ", err)
}
// Restart the ui, which should delete the key.
if err := cryptohome.UnmountVault(ctx, testUser); err != nil {
s.Fatal("Failed to unmount user vault: ", err)
}
chromePID, err := chromeproc.GetRootPID()
if err != nil {
s.Fatal("Failed to find Chrome: ", err)
}
if err := upstart.RestartJob(ctx, "ui"); err != nil {
s.Fatal("Failed to restart ui: ", err)
}
// The actual deletion is done in the session_manager's Chrome setup
// code. Thus wait for the Chrome reboot, which should be after the
// setup.
if err := testing.Poll(ctx, func(ctx context.Context) error {
if newPID, err := chromeproc.GetRootPID(); err != nil {
return err
} else if chromePID == newPID {
return errors.Errorf("Chrome PID is not yet changed: %d", chromePID)
}
return nil
}, nil); err != nil {
s.Fatal("Restarted Chrome is not found: ", err)
}
if _, err := os.Stat(keyFile); err == nil {
s.Fatalf("%s exists unexpectedly", keyFile)
} else if !os.IsNotExist(err) {
s.Fatalf("Failed to stat %s: %v", keyFile, err)
}
// Starting a new session will restore the key that was previously
// stored. Reconnect to the session_manager, because the restart
// killed it.
if err := cryptohome.CreateVault(ctx, testUser, testPass); err != nil {
s.Fatal("Failed to mount vault: ", err)
}
if err := sm.StartSession(ctx, testUser, ""); err != nil {
s.Fatalf("Failed to start session for %s: %v", testUser, err)
}
// The policy key file should have been restored now.
if err := verifyKeyFile(keyFile); err != nil {
s.Fatal("Failed to verify restored key: ", err)
}
}