1 /*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "host/commands/cvd/selector/selector_constants.h"
18
19 #include <sys/stat.h>
20 #include <unistd.h>
21
22 #include <deque>
23 #include <sstream>
24
25 #include "common/libs/fs/shared_buf.h"
26 #include "common/libs/fs/shared_fd.h"
27 #include "common/libs/utils/environment.h"
28 #include "common/libs/utils/files.h"
29 #include "common/libs/utils/users.h"
30
31 namespace cuttlefish {
32 namespace selector {
33
34 enum class OwnershipType { kUser, kGroup, kOthers };
35
GetOwnershipType(const struct stat & file_stat,const uid_t uid,const gid_t gid)36 static OwnershipType GetOwnershipType(const struct stat& file_stat,
37 const uid_t uid, const gid_t gid) {
38 if (file_stat.st_uid == uid) {
39 return OwnershipType::kUser;
40 }
41 if (file_stat.st_gid == gid) {
42 return OwnershipType::kGroup;
43 }
44 return OwnershipType::kOthers;
45 }
46
47 struct RequirePermission {
48 const bool needs_read_permission;
49 const bool needs_write_permission;
50 const bool needs_exec_permission;
51 };
52
CheckPermission(const OwnershipType ownership_type,const struct stat & file_stat,const RequirePermission & perm)53 static Result<void> CheckPermission(const OwnershipType ownership_type,
54 const struct stat& file_stat,
55 const RequirePermission& perm) {
56 const auto perm_bits = file_stat.st_mode;
57
58 switch (ownership_type) {
59 case OwnershipType::kUser: {
60 CF_EXPECT(!perm.needs_read_permission || (perm_bits & S_IRUSR));
61 CF_EXPECT(!perm.needs_write_permission || (perm_bits & S_IWUSR));
62 CF_EXPECT(!perm.needs_exec_permission || (perm_bits & S_IXUSR));
63 return {};
64 }
65 case OwnershipType::kGroup: {
66 CF_EXPECT(!perm.needs_read_permission || (perm_bits & S_IRGRP));
67 CF_EXPECT(!perm.needs_write_permission || (perm_bits & S_IWGRP));
68 CF_EXPECT(!perm.needs_exec_permission || (perm_bits & S_IXGRP));
69 return {};
70 }
71 case OwnershipType::kOthers:
72 break;
73 }
74 CF_EXPECT(!perm.needs_read_permission || (perm_bits & S_IROTH));
75 CF_EXPECT(!perm.needs_write_permission || (perm_bits & S_IWOTH));
76 CF_EXPECT(!perm.needs_exec_permission || (perm_bits & S_IXOTH));
77 return {};
78 }
79
CheckPermission(const std::string & dir,const uid_t client_uid,const gid_t client_gid)80 static Result<void> CheckPermission(const std::string& dir,
81 const uid_t client_uid,
82 const gid_t client_gid) {
83 CF_EXPECT(!dir.empty() && DirectoryExists(dir));
84 struct stat dir_stat;
85 CF_EXPECT_EQ(stat(dir.c_str(), std::addressof(dir_stat)), 0);
86
87 const auto server_ownership = GetOwnershipType(dir_stat, getuid(), getgid());
88 CF_EXPECT(CheckPermission(server_ownership, dir_stat,
89 RequirePermission{.needs_read_permission = true,
90 .needs_write_permission = true,
91 .needs_exec_permission = true}));
92 const auto client_ownership =
93 GetOwnershipType(dir_stat, client_uid, client_gid);
94 CF_EXPECT(CheckPermission(client_ownership, dir_stat,
95 RequirePermission{.needs_read_permission = true,
96 .needs_write_permission = true,
97 .needs_exec_permission = true}));
98 return {};
99 }
100
ParentOfAutogeneratedHomes(const uid_t client_uid,const gid_t client_gid)101 Result<std::string> ParentOfAutogeneratedHomes(const uid_t client_uid,
102 const gid_t client_gid) {
103 std::deque<std::string> try_dirs = {
104 StringFromEnv("TMPDIR", ""),
105 StringFromEnv("TEMP", ""),
106 StringFromEnv("TMP", ""),
107 "/tmp",
108 "/var/tmp",
109 "/usr/tmp",
110 };
111
112 auto system_wide_home = SystemWideUserHome(client_uid);
113 if (system_wide_home.ok()) {
114 try_dirs.emplace_back(*system_wide_home);
115 }
116 try_dirs.emplace_back(AbsolutePath("."));
117 while (!try_dirs.empty()) {
118 const auto candidate = std::move(try_dirs.front());
119 try_dirs.pop_front();
120 if (candidate.empty() || !EnsureDirectoryExists(candidate).ok()) {
121 continue;
122 }
123 CF_EXPECT(CheckPermission(candidate, client_uid, client_gid));
124 return AbsolutePath(candidate);
125 }
126 return CF_ERR("Tried all candidate directories but none was read-writable.");
127 }
128
GroupNameFlag(const std::string & name)129 CvdFlag<std::string> SelectorFlags::GroupNameFlag(const std::string& name) {
130 CvdFlag<std::string> group_name{name};
131 std::stringstream group_name_help;
132 group_name_help << "--" << name << "=<"
133 << "name of the instance group>";
134 group_name.SetHelpMessage(group_name_help.str());
135 return group_name;
136 }
137
InstanceNameFlag(const std::string & name)138 CvdFlag<std::string> SelectorFlags::InstanceNameFlag(const std::string& name) {
139 CvdFlag<std::string> instance_name{name};
140 std::stringstream instance_name_help;
141 instance_name_help << "--" << name << "=<"
142 << "comma-separated names of the instances>";
143 instance_name.SetHelpMessage(instance_name_help.str());
144 return instance_name;
145 }
146
DisableDefaultGroupFlag(const std::string & name,const bool default_val)147 CvdFlag<bool> SelectorFlags::DisableDefaultGroupFlag(const std::string& name,
148 const bool default_val) {
149 CvdFlag<bool> disable_default_group(name, default_val);
150 std::stringstream help;
151 help << "--" << name << "=true not to create the default instance group.";
152 disable_default_group.SetHelpMessage(help.str());
153 return disable_default_group;
154 }
155
AcquireFileLockFlag(const std::string & name,const bool default_val)156 CvdFlag<bool> SelectorFlags::AcquireFileLockFlag(const std::string& name,
157 const bool default_val) {
158 CvdFlag<bool> acquire_file_lock(name, default_val);
159 std::stringstream help;
160 help << "--" << name
161 << "=false for cvd server not to acquire lock file locks.";
162 acquire_file_lock.SetHelpMessage(help.str());
163 return acquire_file_lock;
164 }
165
Get()166 const SelectorFlags& SelectorFlags::Get() {
167 static SelectorFlags singleton_selector_flags;
168 return singleton_selector_flags;
169 }
170
New()171 const SelectorFlags SelectorFlags::New() {
172 SelectorFlags selector_flags;
173 return selector_flags;
174 }
175
176 } // namespace selector
177 } // namespace cuttlefish
178