blob: c20d17b4afffded92bbf6a1ca972d2e2a7eb680e [file] [log] [blame] [edit]
// Copyright 2018 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 (
"bytes"
"context"
"crypto/x509"
"io/ioutil"
"path/filepath"
"chromiumos/tast/errors"
"chromiumos/tast/local/bundles/cros/session/cmp"
"chromiumos/tast/local/cryptohome"
"chromiumos/tast/local/session"
"chromiumos/tast/local/session/ownership"
"chromiumos/tast/testing"
)
func init() {
testing.AddTest(&testing.Test{
Func: OwnershipRetaken,
Desc: "Ensures that ownership is re-taken upon loss of owner's cryptohome",
Contacts: []string{
"mnissler@chromium.org", // session_manager owner
"hidehiko@chromium.org", // Tast port author
},
Data: []string{"testcert.p12"},
Attr: []string{"group:mainline"},
})
}
func OwnershipRetaken(ctx context.Context, s *testing.State) {
const (
testUser = "ownership_test@chromium.org"
testPass = "testme"
)
privKey, err := session.ExtractPrivKey(s.DataPath("testcert.p12"))
if err != nil {
s.Fatal("Failed to parse PKCS #12 file: ", err)
}
if err := session.SetUpDevice(ctx); err != nil {
s.Fatal("Failed to reset device ownership: ", err)
}
if err = cryptohome.RemoveVault(ctx, testUser); err != nil {
s.Fatal("Failed to remove vault: ", 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)
}
// Pre-configure some owner settings, including initial key.
settings := ownership.BuildTestSettings(testUser)
if err := session.StoreSettings(ctx, sm, testUser, privKey, nil, settings); err != nil {
s.Fatal("Failed to store settings: ", err)
}
// Grab key, ensure that it's the same as the known key.
verifyOwnerKey := func() (bool, error) {
path := filepath.Join(session.PolicyPath, "owner.key")
pubKey, err := ioutil.ReadFile(path)
if err != nil {
return false, errors.Wrap(err, "failed to read policy")
}
pubDer, err := x509.MarshalPKIXPublicKey(&privKey.PublicKey)
if err != nil {
return false, errors.Wrap(err, "failed to marshal public key to DER")
}
return bytes.Equal(pubKey, pubDer), nil
}
if same, err := verifyOwnerKey(); err != nil {
s.Fatal("Failed to check owner key: ", err)
} else if !same {
s.Fatal("Owner key should not have changed")
}
// Start a new session, which will trigger the re-taking of ownership.
wp, err := sm.WatchPropertyChangeComplete(ctx)
if err != nil {
s.Fatal("Failed to start watching PropertyChangeComplete signal: ", err)
}
defer wp.Close(ctx)
ws, err := sm.WatchSetOwnerKeyComplete(ctx)
if err != nil {
s.Fatal("Failed to start watching SetOwnerKeyComplete signal: ", err)
}
defer ws.Close(ctx)
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 new session for %s: %v", testUser, err)
}
select {
case <-wp.Signals:
case <-ws.Signals:
case <-ctx.Done():
s.Fatal("Timed out waiting for PropertyChangeComplete or SetOwnerKeyComplete signal: ", ctx.Err())
}
// Grab key, ensure that it's different than known key.
if same, err := verifyOwnerKey(); err != nil {
s.Fatal("Failed to check owner key: ", err)
} else if same {
s.Fatal("Owner key should have changed")
}
// Fetch the data from the session_manager.
ret, err := session.RetrieveSettings(ctx, sm)
if err != nil {
s.Fatal("Failed to retrieve settings: ", err)
}
// Verify that there's no diff between sent data and fetched data.
if diff := cmp.ProtoDiff(settings, ret); diff != "" {
const diffName = "diff.txt"
if err = ioutil.WriteFile(filepath.Join(s.OutDir(), diffName), []byte(diff), 0644); err != nil {
s.Error("Failed to write diff: ", err)
}
s.Error("Sent data and fetched data has diff, which is found in ", diffName)
}
}