File: security_files_integration_test.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (132 lines) | stat: -rw-r--r-- 4,669 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <grp.h>
#include <pwd.h>
#include <sys/stat.h>

#include <string>
#include <vector>

#include "base/command_line.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/ash/login/test/session_manager_state_waiter.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/chromeos/crosier/ash_integration_test.h"
#include "chrome/test/base/chromeos/crosier/chromeos_integration_login_mixin.h"

namespace {

// Returns the Posix file permissions for a file at `path`. The file is assumed
// to exist.
int GetFilePermissions(const base::FilePath& path) {
  int mode = 0;
  CHECK(base::GetPosixFilePermissions(path, &mode));
  return mode;
}

// Returns the owner of the file at `path`. The file is assumed to exist.
std::string GetFileOwner(const base::FilePath& path) {
  struct stat info;
  int rv = stat(path.MaybeAsASCII().c_str(), &info);
  CHECK_EQ(rv, 0);
  // Look up the owner in the passwd database.
  struct passwd* file_owner = getpwuid(info.st_uid);
  CHECK(file_owner);
  return file_owner->pw_name;
}

// Returns the group of the file at `path`. The file is assumed to exist.
std::string GetFileGroup(const base::FilePath& path) {
  struct stat info;
  int rv = stat(path.MaybeAsASCII().c_str(), &info);
  CHECK_EQ(rv, 0);
  // Look up the file's group in the group database.
  struct group* file_group = getgrgid(info.st_gid);
  CHECK(file_group);
  return file_group->gr_name;
}

class SecurityFilesIntegrationTest : public AshIntegrationTest {
 public:
  SecurityFilesIntegrationTest() {
    // Keep test running after dismissing login screen.
    set_exit_when_last_browser_closes(false);

    login_mixin().SetMode(ChromeOSIntegrationLoginMixin::Mode::kTestLogin);
  }

  ~SecurityFilesIntegrationTest() override = default;

  // AshIntegrationTest:
  void SetUpCommandLine(base::CommandLine* command_line) override {
    AshIntegrationTest::SetUpCommandLine(command_line);
    base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
        switches::kUserDataDir, "/home/chronos");
  }
};

IN_PROC_BROWSER_TEST_F(SecurityFilesIntegrationTest, UserFilesLoggedIn) {
  login_mixin().Login();

  ash::test::WaitForPrimaryUserSessionStart();
  EXPECT_TRUE(login_mixin().IsCryptohomeMounted());

  base::ScopedAllowBlockingForTesting allow_blocking;

  // Validate /home/chronos.
  base::FilePath home_chronos("/home/chronos");
  ASSERT_TRUE(base::PathExists(home_chronos));
  EXPECT_EQ(GetFilePermissions(home_chronos), 0755);
  EXPECT_EQ(GetFileOwner(home_chronos), "chronos");

  // Validate /home/chronos/user.
  base::FilePath home_chronos_user("/home/chronos/user");
  ASSERT_TRUE(base::PathExists(home_chronos_user));
  EXPECT_FALSE(GetFilePermissions(home_chronos_user) &
               base::FILE_PERMISSION_WRITE_BY_OTHERS);
  EXPECT_EQ(GetFileOwner(home_chronos_user), "chronos");

  // There is at least one directory matching "u-*".
  base::FileEnumerator enumerator(home_chronos, /*recursive=*/false,
                                  base::FileEnumerator::DIRECTORIES, "u-*");
  int count = 0;
  enumerator.ForEach([&count](const base::FilePath& item) {
    count++;
    EXPECT_FALSE(GetFilePermissions(item) &
                 base::FILE_PERMISSION_WRITE_BY_OTHERS);
  });
  EXPECT_GT(count, 0);

  // Validate contents of /home/chronos.
  base::FileEnumerator enumerator2(
      home_chronos, /*recursive=*/false,
      base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES, "*");
  enumerator2.ForEach([](const base::FilePath& item) {
    // /home/chronos/crash is not other writable.
    if (item == base::FilePath("/home/chronos/crash")) {
      EXPECT_FALSE(GetFilePermissions(item) &
                   base::FILE_PERMISSION_WRITE_BY_OTHERS);
      return;
    }
    // All other subdirectories and files are not group writable and not other
    // writable.
    constexpr int banned = base::FILE_PERMISSION_WRITE_BY_GROUP |
                           base::FILE_PERMISSION_WRITE_BY_OTHERS;
    EXPECT_FALSE(GetFilePermissions(item) & banned) << item.MaybeAsASCII();
  });

  // Validate Downloads.
  base::FilePath downloads("/home/chronos/user/MyFiles/Downloads");
  ASSERT_TRUE(base::PathExists(downloads));
  EXPECT_EQ(GetFilePermissions(downloads), 0750);
  EXPECT_EQ(GetFileOwner(downloads), "chronos");
  EXPECT_EQ(GetFileGroup(downloads), "chronos-access");
}

}  // namespace