blob: 01993f875eee7bcfde86e4b2e44941fae22f565c [file] [log] [blame]
// 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 security
import (
"context"
"fmt"
"os"
"os/user"
"path/filepath"
"strings"
chk "chromiumos/tast/local/bundles/cros/security/filecheck"
"chromiumos/tast/local/moblab"
"chromiumos/tast/testing"
)
func init() {
testing.AddTest(&testing.Test{
Func: StatefulFiles,
Desc: "Checks ownership and permissions of files on the stateful partition",
Contacts: []string{
"jorgelo@chromium.org", // Security team
"chromeos-security@google.com",
},
Attr: []string{"group:mainline"},
})
}
func StatefulFiles(ctx context.Context, s *testing.State) {
const (
root = "/mnt/stateful_partition"
errorFile = "errors.txt"
maxErrors = 5 // max to print
)
// The basic approach here is to specify patterns for paths within a top-level directory, and then add a catch-all
// Tree pattern that checks anything in the directory that wasn't already explicitly checked or skipped.
// Any top-level directories not explicitly handled are matched by the final AllPaths pattern.
patterns := []*chk.Pattern{
chk.NewPattern(chk.Path("dev_image"), chk.SkipChildren()), // only exists for dev images
chk.NewPattern(chk.Path("dev_image_old"), chk.SkipChildren()), // only exists for dev images
chk.NewPattern(chk.Path("dev_image_new"), chk.SkipChildren()), // only exists for dev images
chk.NewPattern(chk.Path("encrypted/chronos"), chk.Users("chronos"), chk.Groups("chronos"), chk.Mode(0755), chk.SkipChildren()), // contents checked by security.UserFiles*
chk.NewPattern(chk.Path("encrypted/var/cache/app_pack"), chk.Users("chronos"), chk.Groups("chronos"), chk.Mode(0700), chk.SkipChildren()),
// TODO(crbug.com/905719): Check for a specific user:group and mode.
chk.NewPattern(chk.Path("encrypted/var/cache/camera"), chk.Users("chronos", "root"), chk.NotMode(02), chk.SkipChildren()),
chk.NewPattern(chk.Path("encrypted/var/cache/device_local_account_component_policy"), chk.Users("chronos"), chk.Groups("chronos"), chk.Mode(0700), chk.SkipChildren()),
chk.NewPattern(chk.Path("encrypted/var/cache/device_local_account_extensions"), chk.Users("chronos"), chk.Groups("chronos"), chk.Mode(0700), chk.SkipChildren()),
chk.NewPattern(chk.Path("encrypted/var/cache/device_local_account_external_policy_data"), chk.Users("chronos"), chk.Groups("chronos"), chk.Mode(0700), chk.SkipChildren()),
chk.NewPattern(chk.Path("encrypted/var/cache/device_policy_external_data"), chk.Users("chronos"), chk.Groups("chronos"), chk.Mode(0700), chk.SkipChildren()),
chk.NewPattern(chk.Path("encrypted/var/cache/display_profiles"), chk.Users("chronos"), chk.Groups("chronos"), chk.Mode(0700), chk.SkipChildren()),
chk.NewPattern(chk.Path("encrypted/var/cache/edb"), chk.Users("root"), chk.Groups("portage"), chk.Mode(0755), chk.SkipChildren()),
chk.NewPattern(chk.Tree("encrypted/var/cache/echo"), chk.Users("root"), chk.NotMode(022)),
// Temporary directory to create external_cache. Most of time, it's owned by root, but switched to chronos just before its renaming.
// See also crx-import.sh for details.
chk.NewPattern(chk.Path("encrypted/var/cache/external_cache.tmp"), chk.Users("chronos", "root"), chk.Groups("chronos", "root"), chk.Mode(0700), chk.SkipChildren()),
chk.NewPattern(chk.Path("encrypted/var/cache/external_cache"), chk.Users("chronos"), chk.Groups("chronos"), chk.Mode(0700), chk.SkipChildren()),
chk.NewPattern(chk.Tree("encrypted/var/cache/hermes"), chk.Users("modem"), chk.Groups("modem"), chk.NotMode(022)),
chk.NewPattern(chk.Tree("encrypted/var/cache/ldconfig"), chk.Users("root"), chk.Groups("root"), chk.NotMode(077)),
chk.NewPattern(chk.Tree("encrypted/var/cache/modemfwd"), chk.Users("modem"), chk.Groups("modem"), chk.NotMode(022)),
chk.NewPattern(chk.Tree("encrypted/var/cache/modem-utilities"), chk.Users("shill-scripts"), chk.Groups("shill-scripts"), chk.Mode(0664)),
chk.NewPattern(chk.Path("encrypted/var/cache/shared_extensions"), chk.Users("chronos"), chk.Groups("chronos"), chk.Mode(0700), chk.SkipChildren()),
chk.NewPattern(chk.Tree("encrypted/var/cache/shill"), chk.Users("shill"), chk.Groups("shill"), chk.NotMode(022)),
chk.NewPattern(chk.Path("encrypted/var/cache/signin_profile_component_policy"), chk.Users("chronos"), chk.Groups("chronos"), chk.Mode(0700), chk.SkipChildren()),
chk.NewPattern(chk.Path("encrypted/var/cache/signin_profile_extensions"), chk.Users("chronos"), chk.Groups("chronos"), chk.Mode(0700), chk.SkipChildren()),
chk.NewPattern(chk.Tree("encrypted/var/cache"), chk.Users("root"), chk.Groups("root"), chk.NotMode(022)),
chk.NewPattern(chk.Tree("encrypted/var/coredumps"), chk.Users("chronos"), chk.Groups("chronos"), chk.NotMode(077)),
// TODO(b/205582301) - Some Bluetooth config files are initialized with group write permission. Temporarily don't check the group settings.
chk.NewPattern(chk.Tree("encrypted/var/lib/bluetooth"), chk.Users("bluetooth"), chk.NotMode(007)),
chk.NewPattern(chk.Tree("encrypted/var/lib/bootlockbox"), chk.Users("bootlockboxd"), chk.Groups("bootlockboxd"), chk.NotMode(022)),
chk.NewPattern(chk.Tree("encrypted/var/lib/chaps"), chk.Users("chaps"), chk.Groups("chronos-access"), chk.NotMode(022)),
chk.NewPattern(chk.Path("encrypted/var/lib/cras"), chk.Users("cras"), chk.Groups("cras"), chk.Mode(0755)), // directory itself
chk.NewPattern(chk.Tree("encrypted/var/lib/cras"), chk.Users("cras"), chk.Groups("cras"), chk.Mode(0644), chk.SkipChildren()), // children
chk.NewPattern(chk.Tree("encrypted/var/lib/chaps/database"), chk.Users("chaps"), chk.Groups("chronos-access"), chk.NotMode(027)),
chk.NewPattern(chk.Tree("encrypted/var/lib/dhcpcd"), chk.Users("dhcp"), chk.Groups("dhcp"), chk.NotMode(022)),
chk.NewPattern(chk.Path("encrypted/var/lib/gentoo"), chk.Users("root"), chk.NotMode(022), chk.SkipChildren()),
chk.NewPattern(chk.Tree("encrypted/var/lib/imageloader"), chk.Users("imageloaderd"), chk.Groups("imageloaderd"), chk.NotMode(022)),
// TODO(chromium:1197973): Re-add permissions checks for /var/lib/metrics
chk.NewPattern(chk.Tree("encrypted/var/lib/metrics"), chk.SkipChildren()),
chk.NewPattern(chk.Tree("encrypted/var/lib/ml_service"), chk.Users("ml-service"), chk.Groups("ml-service"), chk.NotMode(02)),
chk.NewPattern(chk.Tree("encrypted/var/lib/modemfwd"), chk.Users("modem"), chk.Groups("modem"), chk.NotMode(022)),
chk.NewPattern(chk.Tree("encrypted/var/lib/oobe_config_restore"), chk.Users("oobe_config_restore"), chk.Groups("oobe_config_restore"), chk.NotMode(022)),
chk.NewPattern(chk.Tree("encrypted/var/lib/oobe_config_save"), chk.Users("oobe_config_save"), chk.Groups("oobe_config_save"), chk.NotMode(022)),
chk.NewPattern(chk.Tree("encrypted/var/lib/power_manager"), chk.Users("power"), chk.Groups("power"), chk.NotMode(022)),
chk.NewPattern(chk.Tree("encrypted/var/lib/shill"), chk.Users("shill"), chk.Groups("shill"), chk.NotMode(022)),
chk.NewPattern(chk.Tree("encrypted/var/lib/timezone"), chk.Users("chronos", "root"), chk.NotMode(022)),
chk.NewPattern(chk.Tree("encrypted/var/lib/tpm"), chk.Users("root"), chk.Groups("root"), chk.NotMode(077)),
chk.NewPattern(chk.Path("encrypted/var/lib/whitelist"), chk.Users("root"), chk.Groups("policy-readers"), chk.Mode(0750)), // directory itself
chk.NewPattern(chk.Tree("encrypted/var/lib/whitelist"), chk.Users("root"), chk.Groups("root"), chk.NotMode(022)), // children
chk.NewPattern(chk.Path("encrypted/var/lib/devicesettings"), chk.Users("root"), chk.Groups("policy-readers"), chk.Mode(0750)), // directory itself
chk.NewPattern(chk.Tree("encrypted/var/lib/devicesettings"), chk.Users("root"), chk.Groups("root"), chk.NotMode(022)), // children
chk.NewPattern(chk.Tree("encrypted/var/lib"), chk.Users("root"), chk.Groups("root"), chk.NotMode(022)),
chk.NewPattern(chk.Tree("encrypted/var/log/asan"), chk.Users("root"), chk.Groups("root"), chk.Mode(0777|os.ModeSticky)),
chk.NewPattern(chk.Tree("encrypted/var/log/chrome/Crash Reports/uploads.log"), chk.Users("root"), chk.Groups("root"), chk.Mode(0644)),
chk.NewPattern(chk.Tree("encrypted/var/log/chrome/Crash Reports"), chk.Users("chronos"), chk.Groups("chronos"), chk.NotMode(077)),
chk.NewPattern(chk.Tree("encrypted/var/log/chrome"), chk.Users("chronos"), chk.Groups("chronos"), chk.NotMode(022)),
chk.NewPattern(chk.Path("encrypted/var/log/emerge.log"), chk.Users("portage"), chk.Groups("portage"), chk.Mode(0660)),
chk.NewPattern(chk.Tree("encrypted/var/log/metrics"), chk.Users("root", "chronos", "metrics", "shill"), chk.NotMode(022)),
chk.NewPattern(chk.Tree("encrypted/var/log/modemfwd"), chk.Users("modem"), chk.Groups("modem"), chk.NotMode(022)),
chk.NewPattern(chk.Tree("encrypted/var/log/power_manager"), chk.Users("power"), chk.Groups("power"), chk.NotMode(022)),
chk.NewPattern(chk.Tree("encrypted/var/log/tcsd"), chk.Users("tss"), chk.Groups("tss"), chk.NotMode(022)),
chk.NewPattern(chk.Path("encrypted/var/log/usbmon"), chk.Users("root", "tcpdump"), chk.Groups("root", "tcpdump"), chk.SkipChildren()), // only created by tests
chk.NewPattern(chk.Path("encrypted/var/log/vmlog"), chk.Users("metrics"), chk.Groups("metrics"), chk.Mode(0755)), // directory itself
chk.NewPattern(chk.Tree("encrypted/var/log/vmlog"), chk.Users("metrics"), chk.Groups("metrics"), chk.Mode(0644)), // children
chk.NewPattern(chk.Path("encrypted/var/log"), chk.Users("root"), chk.Groups("syslog"), chk.Mode(0775|os.ModeSticky)), // directory itself
chk.NewPattern(chk.Tree("encrypted/var/log"), chk.Users("syslog", "root"), chk.Groups("syslog", "root"), chk.NotMode(022)), // children
chk.NewPattern(chk.Path("encrypted/var/spool/crash"), chk.Users("root"), chk.Groups("crash-access"), chk.Mode(0770|os.ModeSetgid)), // directory itself
chk.NewPattern(chk.Tree("encrypted/var/spool/crash"), chk.Users("root"), chk.Groups("crash-access"), chk.NotMode(002)), // children
chk.NewPattern(chk.Path("encrypted/var/tmp"), chk.Users("root"), chk.Groups("root"), chk.Mode(0777|os.ModeSticky), chk.SkipChildren()),
chk.NewPattern(chk.Tree("encrypted"), chk.Users("root"), chk.NotMode(022)),
chk.NewPattern(chk.PathRegexp(`^encrypted\.`), chk.Users("root"), chk.Groups("root"), chk.Mode(0600)),
chk.NewPattern(chk.Tree("etc"), chk.Users("root"), chk.NotMode(022)),
chk.NewPattern(chk.Path("home/.shadow"), chk.Users("root"), chk.Groups("root"), chk.Mode(0700), chk.SkipChildren()),
chk.NewPattern(chk.Path("home/root"), chk.Users("root"), chk.Groups("root"), chk.Mode(0751|os.ModeSticky)), // directory itself
chk.NewPattern(chk.Tree("home/root"), chk.Users("root"), chk.Groups("root"), chk.Mode(0700), chk.SkipChildren()), // top-level children
chk.NewPattern(chk.Path("home/user"), chk.Users("root"), chk.Groups("root"), chk.Mode(0755)), // directory itself
chk.NewPattern(chk.Tree("home/user"), chk.Users("chronos"), chk.Groups("chronos-access"), chk.Mode(0750), chk.SkipChildren()), // top-level children
chk.NewPattern(chk.Tree("home"), chk.Users("root"), chk.Groups("root"), chk.NotMode(022)),
chk.NewPattern(chk.Path("unencrypted/apkcache"), chk.Mode(0700), chk.SkipChildren()),
chk.NewPattern(chk.Tree("unencrypted/attestation"), chk.Users("attestation", "root"), chk.NotMode(022)),
chk.NewPattern(chk.Path("unencrypted/preserve"), chk.Users("root"), chk.NotMode(02)), // directory itself
chk.NewPattern(chk.Path("unencrypted/preserve/cros-update"), chk.SkipChildren()), // only exists for testing
chk.NewPattern(chk.Path("unencrypted/preserve/log"), chk.SkipChildren()), // only exists for testing
chk.NewPattern(chk.Tree("unencrypted/preserve"), chk.Users("attestation", "root"), chk.NotMode(022)), // other children
chk.NewPattern(chk.Path("unencrypted/userspace_swap.tmp"), chk.Users("chronos"), chk.SkipChildren()),
chk.NewPattern(chk.Tree("unencrypted"), chk.Users("root"), chk.NotMode(022)),
chk.NewPattern(chk.Path("var_overlay"), chk.SkipChildren()), // only exists for dev images
// This file can be created by
// https://source.corp.google.com/chromeos_public/src/platform/factory/py/gooftool/wipe.py.
// TODO(crbug.com/1083285): Avoid creating this file with 0666 permissions.
chk.NewPattern(chk.Path("wipe_mark_file"), chk.Mode(0666)),
chk.NewPattern(chk.Root(), chk.Users("root"), chk.Groups("root"), chk.Mode(0755)), // stateful_partition directory itself
chk.NewPattern(chk.AllPaths(), chk.Users("root"), chk.NotMode(022)), // everything else not already matched
}
// prependPatterns prepends the supplied patterns to the main patterns slice.
prependPatterns := func(newPatterns ...*chk.Pattern) { patterns = append(newPatterns, patterns...) }
if _, err := user.Lookup("tss"); err == nil {
prependPatterns(chk.NewPattern(chk.Tree("var-overlay/lib/tpm"), chk.Users("tss"), chk.NotMode(022)))
}
if _, err := user.Lookup("tpm_manager"); err == nil {
prependPatterns(
chk.NewPattern(chk.Path("encrypted/var/lib/tpm_manager"), chk.Users("tpm_manager"), chk.Groups("tpm_manager"), chk.NotMode(022)),
chk.NewPattern(chk.Path("encrypted/var/lib/tpm_manager/local_tpm_data"), chk.Users("root"), chk.Groups("root"), chk.NotMode(077)))
}
if _, err := user.Lookup("tpm2-simulator"); err == nil {
prependPatterns(
chk.NewPattern(chk.Path("unencrypted/tpm2-simulator"), chk.Users("tpm2-simulator"), chk.Groups("tpm2-simulator"), chk.NotMode(022)),
chk.NewPattern(chk.Path("unencrypted/tpm2-simulator/NVChip"), chk.Users("tpm2-simulator"), chk.Groups("tpm2-simulator"), chk.NotMode(022)),
chk.NewPattern(chk.Path("unencrypted/tpm2-simulator/NVChip_mount"), chk.Users("tpm2-simulator"), chk.Groups("tpm2-simulator"), chk.NotMode(022)))
}
if _, err := user.Lookup("trunks"); err == nil {
prependPatterns(
chk.NewPattern(chk.Path("encrypted/var/lib/trunks"), chk.Users("trunks"), chk.Groups("trunks"), chk.NotMode(022)))
}
if _, err := user.Lookup("biod"); err == nil {
prependPatterns(
chk.NewPattern(chk.Tree("encrypted/var/log/bio_crypto_init"), chk.Users("biod", "root"), chk.Groups("biod", "root"), chk.NotMode(022)),
chk.NewPattern(chk.Tree("encrypted/var/log/biod"), chk.Users("biod", "root"), chk.Groups("biod", "root"), chk.NotMode(022)))
}
if _, err := user.Lookup("buffet"); err == nil {
prependPatterns(chk.NewPattern(chk.Tree("encrypted/var/lib/buffet"), chk.Users("buffet"), chk.Groups("buffet"), chk.NotMode(02)))
}
if _, err := user.Lookup("cups"); err == nil {
prependPatterns(
chk.NewPattern(chk.Tree("encrypted/var/cache/cups"), chk.Users("cups"), chk.Groups("cups", "nobody"), chk.NotMode(02)),
chk.NewPattern(chk.Tree("encrypted/var/spool/cups"), chk.Users("cups"), chk.Groups("cups", "nobody"), chk.NotMode(02)))
}
if _, err := user.Lookup("android-root"); err == nil {
prependPatterns(
chk.NewPattern(chk.Tree("unencrypted/art-data"), chk.Users("android-root", "root"), chk.NotMode(022)))
}
if _, err := user.Lookup("cdm-oemcrypto"); err == nil {
prependPatterns(chk.NewPattern(chk.Path("encrypted/var/lib/oemcrypto"), chk.Users("cdm-oemcrypto"), chk.Groups("cdm-oemcrypto"), chk.Mode(0700), chk.SkipChildren()))
}
if _, err := user.Lookup("fwupd"); err == nil {
prependPatterns(
chk.NewPattern(chk.Path("encrypted/var/cache/fwupd"), chk.Users("fwupd"), chk.Groups("fwupd"), chk.SkipChildren()),
chk.NewPattern(chk.Path("encrypted/var/lib/fwupd"), chk.Users("fwupd"), chk.Groups("fwupd"), chk.SkipChildren()))
}
if _, err := user.Lookup("dlcservice"); err == nil {
prependPatterns(chk.NewPattern(chk.Tree("encrypted/var/cache/dlc"), chk.Users("dlcservice"), chk.Groups("dlcservice"), chk.NotMode(022)))
// encrypted dlc-images is created by dev_utils.sh script prior to bind
// mounting unencrypted dlc-images directory.
prependPatterns(chk.NewPattern(chk.Tree("encrypted/var/cache/dlc-images"), chk.Users("root"), chk.Groups("root"), chk.NotMode(022)))
prependPatterns(chk.NewPattern(chk.Tree("encrypted/var/lib/dlcservice"), chk.Users("dlcservice"), chk.Groups("dlcservice"), chk.NotMode(022)))
prependPatterns(chk.NewPattern(chk.Tree("unencrypted/dlc-factory-images"), chk.Users("dlcservice"), chk.Groups("dlcservice"), chk.NotMode(022)))
}
if _, err := user.Lookup("wilco_dtc"); err == nil {
prependPatterns(chk.NewPattern(chk.Path("encrypted/var/lib/wilco/storage.img"), chk.Users("wilco_dtc"), chk.Groups("wilco_dtc"), chk.NotMode(022)))
}
if _, err := user.Lookup("cros_healthd"); err == nil {
prependPatterns(chk.NewPattern(chk.Path("encrypted/var/cache/diagnostics"), chk.Users("cros_healthd"), chk.Groups("cros_healthd"), chk.SkipChildren()))
}
if _, err := user.Lookup("missived"); err == nil {
prependPatterns(chk.NewPattern(chk.Tree("encrypted/var/cache/reporting"), chk.Users("missived"), chk.Groups("missived"), chk.NotMode(022)))
}
if _, err := user.Lookup("displaylink"); err == nil {
prependPatterns(chk.NewPattern(chk.Tree("encrypted/var/log/displaylink"), chk.Users("displaylink"), chk.Groups("displaylink"), chk.NotMode(022), chk.SkipChildren()))
}
if _, err := user.Lookup("sound_card_init"); err == nil {
prependPatterns(chk.NewPattern(chk.Tree("encrypted/var/lib/sound_card_init"), chk.Users("sound_card_init"), chk.Groups("sound_card_init"), chk.NotMode(022)))
}
if _, err := user.Lookup("rmtfs"); err == nil {
prependPatterns(chk.NewPattern(chk.Tree("encrypted/var/lib/rmtfs"), chk.Users("rmtfs"), chk.NotMode(022)))
}
if _, err := user.Lookup("rmad"); err == nil {
prependPatterns(chk.NewPattern(chk.Tree("encrypted/var/lib/rmad"), chk.Users("rmad"), chk.Groups("rmad"), chk.NotMode(022)))
prependPatterns(chk.NewPattern(chk.Tree("unencrypted/rma-data"), chk.Users("rmad"), chk.Groups("rmad"), chk.NotMode(022)))
}
if moblab.IsMoblab() {
// On moblab devices, there are additional user dirs and tons of stuff (MySQL, etc.) in /var.
prependPatterns(
chk.NewPattern(chk.Tree("home/chronos"), chk.Users("chronos", "root")),
chk.NewPattern(chk.Tree("home/moblab"), chk.Users("moblab", "root")),
chk.NewPattern(chk.Tree("var"), chk.SkipChildren()))
}
s.Log("Checking ", root)
problems, numPaths, err := chk.Check(ctx, root, patterns)
s.Logf("Scanned %d path(s)", numPaths)
if err != nil {
s.Errorf("Failed to check %v: %v", root, err)
}
f, err := os.Create(filepath.Join(s.OutDir(), errorFile))
if err != nil {
s.Error("Failed to create error file: ", err)
} else {
defer f.Close()
for path, msgs := range problems {
if _, err := fmt.Fprintf(f, "%v: %v\n", path, strings.Join(msgs, ", ")); err != nil {
s.Error("Failed to write error file: ", err)
break
}
}
}
numErrors := 0
for path, msgs := range problems {
numErrors++
if numErrors > maxErrors {
s.Logf("Too many errors; aborting (see %v)", errorFile)
break
}
s.Errorf("%v: %v", path, strings.Join(msgs, ", "))
}
}