blob: 48ff9f2e91005a0dca50bbe52ed9193a3fb11992 [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"
"io/ioutil"
"path/filepath"
"strings"
"time"
"chromiumos/tast/errors"
"chromiumos/tast/local/arc"
"chromiumos/tast/local/bundles/cros/security/selinux"
"chromiumos/tast/local/chrome"
"chromiumos/tast/testing"
)
func init() {
testing.AddTest(&testing.Test{
Func: SELinuxFilesARC,
LacrosStatus: testing.LacrosVariantUnknown,
Desc: "Checks SELinux labels on ARC-specific files on devices that support ARC",
Contacts: []string{"niwa@chromium.org", "fqj@chromium.org", "jorgelo@chromium.org", "chromeos-security@google.com"},
SoftwareDeps: []string{"selinux", "chrome"},
Attr: []string{"group:mainline"},
Timeout: 5 * time.Minute,
Params: []testing.Param{
{
ExtraSoftwareDeps: []string{"android_p"},
}, {
Name: "vm",
ExtraSoftwareDeps: []string{"android_vm"},
},
},
})
}
type arcFileTestCase struct {
path string
isAndroidPath bool
context string
recursive bool
filter selinux.FileLabelCheckFilter // nil is selinux.CheckAll
ignoreErrors bool
}
func SELinuxFilesARC(ctx context.Context, s *testing.State) {
// Side effect of other tests in the same arc.Booted() may cause this
// test more flaky.
cr, err := chrome.New(ctx, chrome.ARCEnabled())
if err != nil {
s.Fatal("Chrome login failed: ", err)
}
defer cr.Close(ctx)
a, err := arc.New(ctx, s.OutDir())
if err != nil {
s.Fatal("Failed to start ARC: ", err)
}
defer a.Close(ctx)
vmEnabled, err := arc.VMEnabled()
if err != nil {
s.Fatal("Failed to check whether ARCVM is enabled: ", err)
}
var androidContainerRoot string
if !vmEnabled {
androidContainerRoot, err = androidContainerRootPath()
if err != nil {
s.Fatal("Failed to get Android container root path: ", err)
}
}
var testArgs []arcFileTestCase
// Append container-only test cases.
if !vmEnabled {
gpuDevices, err := selinux.GpuDevices()
if err != nil {
// Error instead of Fatal to continue test other testcases .
// We don't want to "hide" other failures since SELinuxFiles tests are mostly independent test cases.
s.Error("Failed to enumerate gpu devices: ", err)
}
for _, gpuDevice := range gpuDevices {
testArgs = append(testArgs,
[]arcFileTestCase{
{path: filepath.Join(gpuDevice, "config"), context: "gpu_device", ignoreErrors: true},
{path: filepath.Join(gpuDevice, "device"), context: "gpu_device", ignoreErrors: true},
{path: filepath.Join(gpuDevice, "drm"), context: "gpu_device", ignoreErrors: true},
{path: filepath.Join(gpuDevice, "subsystem_device"), context: "gpu_device", ignoreErrors: true},
{path: filepath.Join(gpuDevice, "subsystem_vendor"), context: "gpu_device", ignoreErrors: true},
{path: filepath.Join(gpuDevice, "uevent"), context: "gpu_device"},
{path: filepath.Join(gpuDevice, "vendor"), context: "gpu_device", ignoreErrors: true},
{path: gpuDevice, context: "sysfs", recursive: true, filter: selinux.IgnorePaths([]string{
filepath.Join(gpuDevice, "config"),
filepath.Join(gpuDevice, "device"),
filepath.Join(gpuDevice, "drm"),
filepath.Join(gpuDevice, "subsystem_device"),
filepath.Join(gpuDevice, "subsystem_vendor"),
filepath.Join(gpuDevice, "uevent"),
filepath.Join(gpuDevice, "vendor"),
})},
}...,
)
}
iioDevices, err := selinux.IIOSensorDevices()
if err != nil {
s.Error("Failed to enumerate iio devices: ", err)
}
for _, iioDevice := range iioDevices {
testArgs = append(
testArgs,
[]arcFileTestCase{
// TODO(b:190243278): When iioservice is enabled everywhere, remove cros_sensor_hal_sysfs.
// cros_sensor_hal_sysfs is needed when iioservice is not used and direct access is required from ARC.
{path: iioDevice, context: "cros_sensor_hal_sysfs|sysfs", recursive: true, filter: selinux.IIOSensorFilter},
{path: iioDevice, context: "sysfs", recursive: true, filter: selinux.InvertFilterSkipFile(selinux.IIOSensorFilter)},
}...)
}
testArgs = append(testArgs, []arcFileTestCase{
{path: "/mnt/stateful_partition/unencrypted/art-data/dalvik-cache/", context: "dalvikcache_data_file", recursive: true},
{path: "/run/arc/adbd", context: "(tmpfs|device)"},
{path: "/run/arc/cmdline.android", context: "(proc_cmdline|proc)"}, // N or below is proc
{path: "/run/arc/debugfs", context: "(debugfs|tmpfs)"},
{path: "/run/arc/fake_kptr_restrict", context: "proc_security"},
{path: "/run/arc/fake_mmap_rnd_bits", context: "proc_security"},
{path: "/run/arc/fake_mmap_rnd_compat_bits", context: "proc_security"},
{path: "/run/arc/media", context: "tmpfs"},
{path: "/run/arc/obb", context: "tmpfs"},
{path: "/run/arc/oem/etc", context: "(tmpfs|oemfs)", recursive: true},
{path: "/run/arc/host_generated/build.prop", context: "system_file"}, // Android labels, bind-mount into ARC
{path: "/run/arc/host_generated/default.prop", context: "rootfs", ignoreErrors: true}, // Android labels, bind-mount into ARC.
{path: "/run/arc/sdcard", context: "(tmpfs|storage_file)"},
{path: "/run/arc/shared_mounts", context: "tmpfs"},
{path: "/run/chrome/arc_bridge.sock", context: "arc_bridge_socket"},
{path: "/usr/sbin/arc-setup", context: "cros_arc_setup_exec"},
{path: "dev/ptmx", isAndroidPath: true, context: "ptmx_device"},
{path: "dev/random", isAndroidPath: true, context: "random_device"},
{path: "dev/urandom", isAndroidPath: true, context: "u?random_device"},
{path: "oem", isAndroidPath: true, context: "oemfs"},
{path: "sys/kernel/debug/sync", isAndroidPath: true, context: "tmpfs|debugfs_sync"}, // pre-3.18 doesn't have debugfs/sync, thus ARC container has a tmpfs fake.
}...)
}
// Append common test cases.
testArgs = append(testArgs, []arcFileTestCase{
{path: "/mnt/stateful_partition/unencrypted/apkcache", context: "apkcache_file"},
{path: "/opt/google/chrome/chrome", context: "chrome_browser_exec"},
{path: "/run/arcvm", context: "cros_run_arcvm", ignoreErrors: true},
{path: "/run/arcvm/android-data", context: "(system_data_file|system_data_root_file)", ignoreErrors: true}, // Android label
{path: "/run/camera", context: "(camera_dir|camera_socket)", ignoreErrors: true}, // N or below is camera_socket
{path: "/run/camera/camera.sock", context: "camera_socket", ignoreErrors: true},
{path: "/run/camera/camera3.sock", context: "camera_socket", ignoreErrors: true},
{path: "/run/chrome/wayland-0", context: "wayland_socket"},
{path: "/run/cras", context: "cras_socket", recursive: true},
{path: "/run/session_manager", context: "cros_run_session_manager", recursive: true},
{path: "/sys/kernel/debug/sync/sw_sync", context: "debugfs_sw_sync", ignoreErrors: true},
{path: "/var/log/chrome", context: "cros_var_log_chrome", recursive: true},
}...)
for _, testArg := range testArgs {
filter := testArg.filter
if filter == nil {
filter = selinux.CheckAll
}
path := testArg.path
if testArg.isAndroidPath {
path = filepath.Join(androidContainerRoot, path)
}
expected, err := selinux.FileContextRegexp(testArg.context)
if err != nil {
s.Errorf("Failed to compile expected context %q: %v", testArg.context, err)
continue
}
selinux.CheckContext(ctx, s, &selinux.CheckContextReq{
Path: path,
Expected: expected,
Recursive: testArg.recursive,
Filter: filter,
IgnoreErrors: testArg.ignoreErrors,
Log: false,
})
}
selinux.CheckHomeDirectory(ctx, s)
}
// androidContainerRootPath returns /proc/<android-container-pid>/root.
func androidContainerRootPath() (string, error) {
containerPIDFiles, err := filepath.Glob("/run/containers/android*/container.pid")
if err != nil {
return "", errors.Wrap(err, "failed to find container.pid file")
}
if len(containerPIDFiles) != 1 {
return "", errors.Errorf("expected to find one container.pid file; got %d", len(containerPIDFiles))
}
containerPIDFileName := containerPIDFiles[0]
b, err := ioutil.ReadFile(containerPIDFileName)
if err != nil {
return "", errors.Wrap(err, "failed to read container.pid")
}
androidContainerRoot := fmt.Sprintf("/proc/%s/root", strings.TrimSpace(string(b)))
return androidContainerRoot, nil
}