blob: d9c26bc63874e795b6a75dc5a9ec9ade225689ed [file] [log] [blame]
Dylan Reid6b590e62016-10-27 19:10:53 -07001// Copyright 2016 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Stephen Barber5f6dc9b2017-04-04 12:36:32 -07005#include "run_oci/container_config_parser.h"
Dylan Reid6b590e62016-10-27 19:10:53 -07006
Luis Hector Chavez6e9a5332017-10-26 14:41:49 -07007#include <linux/securebits.h>
Luis Hector Chavez8373b412017-07-10 12:51:07 -07008#include <sys/capability.h>
Luis Hector Chavez7c6fddf2017-10-24 15:39:41 -07009#include <sys/mount.h>
Dylan Reid93fa4602017-06-06 13:39:31 -070010#include <sys/resource.h>
Dylan Reid6b590e62016-10-27 19:10:53 -070011#include <unistd.h>
12
Luis Hector Chavez8e4dcc12017-06-27 12:54:47 -070013#include <map>
Ben Chanf43980b2017-03-10 11:31:46 -080014#include <regex> // NOLINT(build/c++11)
Dylan Reid6b590e62016-10-27 19:10:53 -070015#include <string>
Luis Hector Chavezf8e8f4c2017-08-01 01:09:39 -070016#include <utility>
Dylan Reid6b590e62016-10-27 19:10:53 -070017#include <vector>
18
19#include <base/json/json_reader.h>
Luis Hector Chavezf8e8f4c2017-08-01 01:09:39 -070020#include <base/strings/string_split.h>
hschamd79f5392020-11-05 17:50:07 +090021#include <base/strings/string_util.h>
Dylan Reid6b590e62016-10-27 19:10:53 -070022#include <base/values.h>
23
Stephen Barber5f6dc9b2017-04-04 12:36:32 -070024namespace run_oci {
Dylan Reid6b590e62016-10-27 19:10:53 -070025
26namespace {
27
yusukesfc412ea2018-01-10 08:27:24 -080028// Gets an integer from the given dictionary.
29template <typename T>
hschamd79f5392020-11-05 17:50:07 +090030bool ParseIntFromDict(const base::Value& dict, const char* name, T* val_out) {
31 base::Optional<double> double_val = dict.FindDoubleKey(name);
32 if (!double_val.has_value()) {
Dylan Reidb0a72772016-11-03 16:27:50 +000033 return false;
34 }
hschamd79f5392020-11-05 17:50:07 +090035 *val_out = static_cast<T>(*double_val);
Dylan Reidb0a72772016-11-03 16:27:50 +000036 return true;
37}
38
hschamd79f5392020-11-05 17:50:07 +090039// Parse a list-type Value structure as vector of integers.
Risanfd41aee2018-08-15 14:03:38 +090040template <typename T>
hschamd79f5392020-11-05 17:50:07 +090041bool ParseIntList(const base::Value& list_val, std::vector<T>* val_out) {
42 for (const base::Value& entry : list_val.GetList()) {
43 if (!entry.is_double() && !entry.is_int()) {
Risanfd41aee2018-08-15 14:03:38 +090044 return false;
45 }
hschamd79f5392020-11-05 17:50:07 +090046 val_out->emplace_back(static_cast<T>(entry.GetDouble()));
Risanfd41aee2018-08-15 14:03:38 +090047 }
48 return true;
49}
50
Dylan Reid6b590e62016-10-27 19:10:53 -070051// Parses basic platform configuration.
hschamd79f5392020-11-05 17:50:07 +090052bool ParsePlatformConfig(const base::Value& config_root_dict,
Dylan Reid6b590e62016-10-27 19:10:53 -070053 OciConfigPtr const& config_out) {
54 // |platform_dict| stays owned by |config_root_dict|
hschamd79f5392020-11-05 17:50:07 +090055 const base::Value* platform_dict = config_root_dict.FindDictKey("platform");
56 if (!platform_dict) {
Dylan Reid6b590e62016-10-27 19:10:53 -070057 LOG(ERROR) << "Fail to parse platform dictionary from config";
58 return false;
59 }
60
hschamd79f5392020-11-05 17:50:07 +090061 const std::string* os = platform_dict->FindStringKey("os");
62 if (!os) {
Dylan Reid6b590e62016-10-27 19:10:53 -070063 return false;
64 }
hschamd79f5392020-11-05 17:50:07 +090065 config_out->platform.os = *os;
Dylan Reid6b590e62016-10-27 19:10:53 -070066
hschamd79f5392020-11-05 17:50:07 +090067 const std::string* arch = platform_dict->FindStringKey("arch");
68 if (!arch) {
Dylan Reid6b590e62016-10-27 19:10:53 -070069 return false;
70 }
hschamd79f5392020-11-05 17:50:07 +090071 config_out->platform.arch = *arch;
Dylan Reid6b590e62016-10-27 19:10:53 -070072
73 return true;
74}
75
76// Parses root fs info.
hschamd79f5392020-11-05 17:50:07 +090077bool ParseRootFileSystemConfig(const base::Value& config_root_dict,
Dylan Reid6b590e62016-10-27 19:10:53 -070078 OciConfigPtr const& config_out) {
79 // |rootfs_dict| stays owned by |config_root_dict|
hschamd79f5392020-11-05 17:50:07 +090080 const base::Value* rootfs_dict = config_root_dict.FindDictKey("root");
81 if (!rootfs_dict) {
Dylan Reid6b590e62016-10-27 19:10:53 -070082 LOG(ERROR) << "Fail to parse rootfs dictionary from config";
83 return false;
84 }
hschamd79f5392020-11-05 17:50:07 +090085 const std::string* path = rootfs_dict->FindStringKey("path");
86 if (!path) {
Dylan Reid6b590e62016-10-27 19:10:53 -070087 LOG(ERROR) << "Fail to get rootfs path from config";
88 return false;
89 }
hschamd79f5392020-11-05 17:50:07 +090090 config_out->root.path = base::FilePath(*path);
91 base::Optional<bool> read_only = rootfs_dict->FindBoolKey("readonly");
92 if (read_only.has_value())
93 config_out->root.readonly = *read_only;
Dylan Reid6b590e62016-10-27 19:10:53 -070094 return true;
95}
96
Luis Hector Chavez8e4dcc12017-06-27 12:54:47 -070097// Fills |config_out| with information about the capability sets in the
98// container.
hschamd79f5392020-11-05 17:50:07 +090099bool ParseCapabilitiesConfig(const base::Value& capabilities_dict,
Luis Hector Chavez8e4dcc12017-06-27 12:54:47 -0700100 std::map<std::string, CapSet>* config_out) {
101 constexpr const char* kCapabilitySetNames[] = {
102 "effective", "bounding", "inheritable", "permitted", "ambient"};
103 const std::string kAmbientCapabilitySetName = "ambient";
104
105 CapSet caps_superset;
106 for (const char* set_name : kCapabilitySetNames) {
107 // |capset_list| stays owned by |capabilities_dict|.
hschamd79f5392020-11-05 17:50:07 +0900108 const base::Value* capset_list = capabilities_dict.FindListKey(set_name);
109 if (!capset_list)
Luis Hector Chavez8e4dcc12017-06-27 12:54:47 -0700110 continue;
111 CapSet caps;
Luis Hector Chavez8373b412017-07-10 12:51:07 -0700112 cap_value_t cap_value;
hschamd79f5392020-11-05 17:50:07 +0900113 for (const auto& cap_name_value : capset_list->GetList()) {
114 if (!cap_name_value.is_string()) {
Luis Hector Chavez8e4dcc12017-06-27 12:54:47 -0700115 LOG(ERROR) << "Capability list " << set_name
116 << " contains a non-string";
117 return false;
118 }
hschamd79f5392020-11-05 17:50:07 +0900119 std::string cap_name = cap_name_value.GetString();
Luis Hector Chavez8373b412017-07-10 12:51:07 -0700120 if (cap_from_name(cap_name.c_str(), &cap_value) == -1) {
Luis Hector Chavez8e4dcc12017-06-27 12:54:47 -0700121 LOG(ERROR) << "Unrecognized capability name: " << cap_name;
122 return false;
123 }
Luis Hector Chavez8373b412017-07-10 12:51:07 -0700124 caps[cap_value] = true;
Luis Hector Chavez8e4dcc12017-06-27 12:54:47 -0700125 }
126 (*config_out)[set_name] = caps;
127 caps_superset = caps;
128 }
129
130 // We currently only support sets that are identical, except that ambient is
131 // optional.
132 for (const char* set_name : kCapabilitySetNames) {
133 auto it = config_out->find(set_name);
134 if (it == config_out->end() && set_name == kAmbientCapabilitySetName) {
135 // Ambient capabilities are optional.
136 continue;
137 }
138 if (it == config_out->end()) {
139 LOG(ERROR)
140 << "If capabilities are set, all capability sets should be present";
141 return false;
142 }
143 if (it->second != caps_superset) {
144 LOG(ERROR)
145 << "If capabilities are set, all capability sets should be identical";
146 return false;
147 }
148 }
149
150 return true;
151}
152
Dylan Reid93fa4602017-06-06 13:39:31 -0700153const std::map<std::string, int> kRlimitMap = {
154#define RLIMIT_MAP_ENTRY(limit) \
155 { "RLIMIT_" #limit, RLIMIT_##limit }
Luis Hector Chavez9f9f66e2017-10-16 14:54:32 -0700156 RLIMIT_MAP_ENTRY(CPU), RLIMIT_MAP_ENTRY(FSIZE),
157 RLIMIT_MAP_ENTRY(DATA), RLIMIT_MAP_ENTRY(STACK),
158 RLIMIT_MAP_ENTRY(CORE), RLIMIT_MAP_ENTRY(RSS),
159 RLIMIT_MAP_ENTRY(NPROC), RLIMIT_MAP_ENTRY(NOFILE),
160 RLIMIT_MAP_ENTRY(MEMLOCK), RLIMIT_MAP_ENTRY(AS),
161 RLIMIT_MAP_ENTRY(LOCKS), RLIMIT_MAP_ENTRY(SIGPENDING),
162 RLIMIT_MAP_ENTRY(MSGQUEUE), RLIMIT_MAP_ENTRY(NICE),
163 RLIMIT_MAP_ENTRY(RTPRIO), RLIMIT_MAP_ENTRY(RTTIME),
Dylan Reid93fa4602017-06-06 13:39:31 -0700164#undef RLIMIT_MAP_ENTRY
165};
166
167// Fills |config_out| with information about the capability sets in the
168// container.
hschamd79f5392020-11-05 17:50:07 +0900169bool ParseRlimitsConfig(const base::Value& rlimits_list,
Dylan Reid93fa4602017-06-06 13:39:31 -0700170 std::vector<OciProcessRlimit>* rlimits_out) {
hschamd79f5392020-11-05 17:50:07 +0900171 size_t num_limits = rlimits_list.GetList().size();
Dylan Reid93fa4602017-06-06 13:39:31 -0700172 for (size_t i = 0; i < num_limits; ++i) {
hschamd79f5392020-11-05 17:50:07 +0900173 const base::Value& rlimits_dict = rlimits_list.GetList()[i];
174 if (!rlimits_dict.is_dict()) {
Dylan Reid93fa4602017-06-06 13:39:31 -0700175 LOG(ERROR) << "Fail to get rlimit item " << i;
176 return false;
177 }
178
hschamd79f5392020-11-05 17:50:07 +0900179 const std::string* rlimit_name = rlimits_dict.FindStringKey("type");
180 if (!rlimit_name) {
Dylan Reid93fa4602017-06-06 13:39:31 -0700181 LOG(ERROR) << "Fail to get type of rlimit " << i;
182 return false;
183 }
hschamd79f5392020-11-05 17:50:07 +0900184 const auto it = kRlimitMap.find(*rlimit_name);
Dylan Reid93fa4602017-06-06 13:39:31 -0700185 if (it == kRlimitMap.end()) {
hschamd79f5392020-11-05 17:50:07 +0900186 LOG(ERROR) << "Unrecognized rlimit name: " << *rlimit_name;
Dylan Reid93fa4602017-06-06 13:39:31 -0700187 return false;
188 }
189
190 OciProcessRlimit limit;
191 limit.type = it->second;
hschamd79f5392020-11-05 17:50:07 +0900192 if (!ParseIntFromDict(rlimits_dict, "hard", &limit.hard)) {
Dylan Reid93fa4602017-06-06 13:39:31 -0700193 LOG(ERROR) << "Fail to get hard limit of rlimit " << i;
194 return false;
195 }
hschamd79f5392020-11-05 17:50:07 +0900196 if (!ParseIntFromDict(rlimits_dict, "soft", &limit.soft)) {
Dylan Reid93fa4602017-06-06 13:39:31 -0700197 LOG(ERROR) << "Fail to get soft limit of rlimit " << i;
198 return false;
199 }
200 rlimits_out->push_back(limit);
201 }
202
203 return true;
204}
205
Dylan Reid6b590e62016-10-27 19:10:53 -0700206// Fills |config_out| with information about the main process to run in the
207// container and the user it should be run as.
hschamd79f5392020-11-05 17:50:07 +0900208bool ParseProcessConfig(const base::Value& config_root_dict,
Dylan Reid6b590e62016-10-27 19:10:53 -0700209 OciConfigPtr const& config_out) {
210 // |process_dict| stays owned by |config_root_dict|
hschamd79f5392020-11-05 17:50:07 +0900211 const base::Value* process_dict = config_root_dict.FindDictKey("process");
212 if (!process_dict) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700213 LOG(ERROR) << "Fail to get main process from config";
214 return false;
215 }
hschamd79f5392020-11-05 17:50:07 +0900216 base::Optional<bool> terminal = process_dict->FindBoolKey("terminal");
217 if (terminal.has_value())
218 config_out->process.terminal = *terminal;
Dylan Reid6b590e62016-10-27 19:10:53 -0700219 // |user_dict| stays owned by |process_dict|
hschamd79f5392020-11-05 17:50:07 +0900220 const base::Value* user_dict = process_dict->FindDictKey("user");
221 if (!user_dict) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700222 LOG(ERROR) << "Failed to get user info from config";
223 return false;
224 }
yusukesfc412ea2018-01-10 08:27:24 -0800225 if (!ParseIntFromDict(*user_dict, "uid", &config_out->process.user.uid))
Dylan Reid6b590e62016-10-27 19:10:53 -0700226 return false;
yusukesfc412ea2018-01-10 08:27:24 -0800227 if (!ParseIntFromDict(*user_dict, "gid", &config_out->process.user.gid))
Dylan Reid6b590e62016-10-27 19:10:53 -0700228 return false;
Risanfd41aee2018-08-15 14:03:38 +0900229
230 // If additionalGids field is specified, parse it as a valid list of integers.
hschamd79f5392020-11-05 17:50:07 +0900231 const base::Value* list_val = user_dict->FindListKey("additionalGids");
232 if (list_val &&
Risanfd41aee2018-08-15 14:03:38 +0900233 !ParseIntList(*list_val, &config_out->process.user.additionalGids)) {
234 LOG(ERROR) << "Invalid process.user.additionalGids";
235 return false;
236 }
237
Dylan Reid6b590e62016-10-27 19:10:53 -0700238 // |args_list| stays owned by |process_dict|
hschamd79f5392020-11-05 17:50:07 +0900239 const base::Value* args_list = process_dict->FindListKey("args");
240 if (!args_list) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700241 LOG(ERROR) << "Fail to get main process args from config";
242 return false;
243 }
hschamd79f5392020-11-05 17:50:07 +0900244 for (const auto& arg : args_list->GetList()) {
245 if (!arg.is_string()) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700246 LOG(ERROR) << "Fail to get process args from config";
247 return false;
248 }
hschamd79f5392020-11-05 17:50:07 +0900249 config_out->process.args.push_back(arg.GetString());
Dylan Reid6b590e62016-10-27 19:10:53 -0700250 }
251 // |env_list| stays owned by |process_dict|
hschamd79f5392020-11-05 17:50:07 +0900252 const base::Value* env_list = process_dict->FindListKey("env");
253 if (env_list) {
254 for (const auto& env_value : env_list->GetList()) {
255 if (!env_value.is_string()) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700256 LOG(ERROR) << "Fail to get process env from config";
257 return false;
258 }
hschamd79f5392020-11-05 17:50:07 +0900259 const std::string& env = env_value.GetString();
Luis Hector Chavez855e99e2017-10-10 10:27:33 -0700260 std::vector<std::string> kvp = base::SplitString(
261 env, "=", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
262 if (kvp.size() != 2) {
263 LOG(ERROR) << "Fail to parse env \"" << env
264 << "\". Must be in name=value format.";
265 return false;
266 }
267 config_out->process.env.insert(std::make_pair(kvp[0], kvp[1]));
Dylan Reid6b590e62016-10-27 19:10:53 -0700268 }
269 }
hschamd79f5392020-11-05 17:50:07 +0900270 const std::string* path = process_dict->FindStringKey("cwd");
271 if (!path) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700272 LOG(ERROR) << "failed to get cwd of process";
273 return false;
274 }
hschamd79f5392020-11-05 17:50:07 +0900275 config_out->process.cwd = base::FilePath(*path);
276 base::Optional<int> umask_int = process_dict->FindIntKey("umask");
277 if (umask_int.has_value())
278 config_out->process.umask = static_cast<mode_t>(*umask_int);
Luis Hector Chavezce1b8282017-10-30 10:12:49 -0700279 else
280 config_out->process.umask = 0022; // Optional
Luis Hector Chavez855e99e2017-10-10 10:27:33 -0700281
Luis Hector Chavez15e8e672017-07-20 15:13:27 -0700282 // selinuxLabel is optional.
hschamd79f5392020-11-05 17:50:07 +0900283 const std::string* selinux_label =
284 process_dict->FindStringKey("selinuxLabel");
285 if (selinux_label)
286 config_out->process.selinuxLabel = *selinux_label;
Luis Hector Chavez8e4dcc12017-06-27 12:54:47 -0700287 // |capabilities_dict| stays owned by |process_dict|
hschamd79f5392020-11-05 17:50:07 +0900288 const base::Value* capabilities_dict =
289 process_dict->FindDictKey("capabilities");
290 if (capabilities_dict) {
Luis Hector Chavez8e4dcc12017-06-27 12:54:47 -0700291 if (!ParseCapabilitiesConfig(*capabilities_dict,
292 &config_out->process.capabilities)) {
293 return false;
294 }
295 }
Dylan Reid6b590e62016-10-27 19:10:53 -0700296
Dylan Reid93fa4602017-06-06 13:39:31 -0700297 // |rlimit_list| stays owned by |process_dict|
hschamd79f5392020-11-05 17:50:07 +0900298 const base::Value* rlimits_list = process_dict->FindListKey("rlimits");
299 if (rlimits_list) {
Dylan Reid93fa4602017-06-06 13:39:31 -0700300 if (!ParseRlimitsConfig(*rlimits_list, &config_out->process.rlimits)) {
301 return false;
302 }
303 }
304
Dylan Reid6b590e62016-10-27 19:10:53 -0700305 return true;
306}
307
308// Parses the 'mounts' field. The necessary mounts for running the container
309// are specified here.
hschamd79f5392020-11-05 17:50:07 +0900310bool ParseMounts(const base::Value& config_root_dict,
Dylan Reid6b590e62016-10-27 19:10:53 -0700311 OciConfigPtr const& config_out) {
312 // |config_mounts_list| stays owned by |config_root_dict|
hschamd79f5392020-11-05 17:50:07 +0900313 const base::Value* config_mounts_list =
314 config_root_dict.FindListKey("mounts");
315 if (!config_mounts_list) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700316 LOG(ERROR) << "Fail to get mounts from config dictionary";
317 return false;
318 }
319
hschamd79f5392020-11-05 17:50:07 +0900320 for (size_t i = 0; i < config_mounts_list->GetList().size(); ++i) {
321 const base::Value& mount_dict = config_mounts_list->GetList()[i];
322 if (!mount_dict.is_dict()) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700323 LOG(ERROR) << "Fail to get mount item " << i;
324 return false;
325 }
326 OciMount mount;
hschamd79f5392020-11-05 17:50:07 +0900327 const std::string* path = mount_dict.FindStringKey("destination");
328 if (!path) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700329 LOG(ERROR) << "Fail to get mount path for mount " << i;
330 return false;
331 }
hschamd79f5392020-11-05 17:50:07 +0900332 mount.destination = base::FilePath(*path);
333 const std::string* type = mount_dict.FindStringKey("type");
334 if (!type) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700335 LOG(ERROR) << "Fail to get mount type for mount " << i;
336 return false;
337 }
hschamd79f5392020-11-05 17:50:07 +0900338 mount.type = *type;
339 const std::string* source = mount_dict.FindStringKey("source");
340 if (!source) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700341 LOG(ERROR) << "Fail to get mount source for mount " << i;
342 return false;
343 }
hschamd79f5392020-11-05 17:50:07 +0900344 mount.source = base::FilePath(*source);
345 base::Optional<bool> intermediate_namespace =
346 mount_dict.FindBoolKey("performInIntermediateNamespace");
347 mount.performInIntermediateNamespace =
348 intermediate_namespace.value_or(false);
Dylan Reid6b590e62016-10-27 19:10:53 -0700349
350 // |options| are owned by |mount_dict|
hschamd79f5392020-11-05 17:50:07 +0900351 const base::Value* options = mount_dict.FindListKey("options");
352 if (options) {
353 for (size_t j = 0; j < options->GetList().size(); ++j) {
354 const base::Value& this_opt = options->GetList()[j];
355 if (!this_opt.is_string()) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700356 LOG(ERROR) << "Fail to get option " << j << " from mount options";
357 return false;
358 }
hschamd79f5392020-11-05 17:50:07 +0900359 mount.options.push_back(this_opt.GetString());
Dylan Reid6b590e62016-10-27 19:10:53 -0700360 }
361 }
362
363 config_out->mounts.push_back(mount);
364 }
365 return true;
366}
367
Dylan Reid6985e3b2017-03-31 19:39:16 -0700368// Parses the linux resource list
hschamd79f5392020-11-05 17:50:07 +0900369bool ParseResources(const base::Value& resources_dict,
Dylan Reid6985e3b2017-03-31 19:39:16 -0700370 OciLinuxResources* resources_out) {
371 // |device_list| is owned by |resources_dict|
hschamd79f5392020-11-05 17:50:07 +0900372 const base::Value* device_list = resources_dict.FindListKey("devices");
373 if (!device_list) {
Dylan Reid6985e3b2017-03-31 19:39:16 -0700374 // The device list is optional.
375 return true;
376 }
hschamd79f5392020-11-05 17:50:07 +0900377 size_t num_devices = device_list->GetList().size();
Dylan Reid6985e3b2017-03-31 19:39:16 -0700378 for (size_t i = 0; i < num_devices; ++i) {
379 OciLinuxCgroupDevice device;
380
hschamd79f5392020-11-05 17:50:07 +0900381 const base::Value& dev = device_list->GetList()[i];
382 if (!dev.is_dict()) {
Dylan Reid6985e3b2017-03-31 19:39:16 -0700383 LOG(ERROR) << "Fail to get device " << i;
384 return false;
385 }
386
hschamd79f5392020-11-05 17:50:07 +0900387 base::Optional<bool> allow = dev.FindBoolKey("allow");
388 if (!allow.has_value()) {
Dylan Reid6985e3b2017-03-31 19:39:16 -0700389 LOG(ERROR) << "Fail to get allow value for device " << i;
390 return false;
391 }
hschamd79f5392020-11-05 17:50:07 +0900392 device.allow = *allow;
393 const std::string* access = dev.FindStringKey("access");
394 // Optional, default to all perms.
395 device.access = access ? *access : "rwm";
396 const std::string* type = dev.FindStringKey("type");
397 // Optional, default to both a means all.
398 device.type = type ? *type : "a";
399 if (!ParseIntFromDict(dev, "major", &device.major))
Dylan Reid6985e3b2017-03-31 19:39:16 -0700400 device.major = -1; // Optional, -1 will map to all devices.
hschamd79f5392020-11-05 17:50:07 +0900401 if (!ParseIntFromDict(dev, "minor", &device.minor))
Dylan Reid6985e3b2017-03-31 19:39:16 -0700402 device.minor = -1; // Optional, -1 will map to all devices.
403
404 resources_out->devices.push_back(device);
405 }
406
407 return true;
408}
409
Stephen Barber3c0a2022017-09-08 14:17:57 -0700410// Parses the list of namespaces and fills |namespaces_out| with them.
hschamd79f5392020-11-05 17:50:07 +0900411bool ParseNamespaces(const base::Value* namespaces_list,
Stephen Barber3c0a2022017-09-08 14:17:57 -0700412 std::vector<OciNamespace>* namespaces_out) {
hschamd79f5392020-11-05 17:50:07 +0900413 for (size_t i = 0; i < namespaces_list->GetList().size(); ++i) {
Stephen Barber3c0a2022017-09-08 14:17:57 -0700414 OciNamespace new_namespace;
hschamd79f5392020-11-05 17:50:07 +0900415 const base::Value& ns = namespaces_list->GetList()[i];
416 if (!ns.is_dict()) {
Stephen Barber3c0a2022017-09-08 14:17:57 -0700417 LOG(ERROR) << "Failed to get namespace " << i;
418 return false;
419 }
hschamd79f5392020-11-05 17:50:07 +0900420 const std::string* type = ns.FindStringKey("type");
421 if (!type) {
Stephen Barber3c0a2022017-09-08 14:17:57 -0700422 LOG(ERROR) << "Namespace " << i << " missing type";
423 return false;
424 }
hschamd79f5392020-11-05 17:50:07 +0900425 new_namespace.type = *type;
426 const std::string* path = ns.FindStringKey("path");
427 if (path)
428 new_namespace.path = base::FilePath(*path);
Stephen Barber3c0a2022017-09-08 14:17:57 -0700429 namespaces_out->push_back(new_namespace);
430 }
431 return true;
432}
433
Dylan Reid6b590e62016-10-27 19:10:53 -0700434// Parse the list of device nodes that the container needs to run.
hschamd79f5392020-11-05 17:50:07 +0900435bool ParseDeviceList(const base::Value& linux_dict,
Dylan Reid6b590e62016-10-27 19:10:53 -0700436 OciConfigPtr const& config_out) {
437 // |device_list| is owned by |linux_dict|
hschamd79f5392020-11-05 17:50:07 +0900438 const base::Value* device_list = linux_dict.FindListKey("devices");
439 if (!device_list) {
Dylan Reida5ed1272016-11-11 16:43:39 -0800440 // The device list is optional.
441 return true;
Dylan Reid6b590e62016-10-27 19:10:53 -0700442 }
hschamd79f5392020-11-05 17:50:07 +0900443 size_t num_devices = device_list->GetList().size();
Dylan Reid6b590e62016-10-27 19:10:53 -0700444 for (size_t i = 0; i < num_devices; ++i) {
445 OciLinuxDevice device;
446
hschamd79f5392020-11-05 17:50:07 +0900447 const base::Value& dev = device_list->GetList()[i];
448 if (!dev.is_dict()) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700449 LOG(ERROR) << "Fail to get device " << i;
450 return false;
451 }
hschamd79f5392020-11-05 17:50:07 +0900452 const std::string* path = dev.FindStringKey("path");
453 if (!path) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700454 LOG(ERROR) << "Fail to get path for dev";
455 return false;
456 }
hschamd79f5392020-11-05 17:50:07 +0900457 device.path = base::FilePath(*path);
458 const std::string* type = dev.FindStringKey("type");
459 if (!type) {
Luis Hector Chavez855e99e2017-10-10 10:27:33 -0700460 LOG(ERROR) << "Fail to get type for " << device.path.value();
Dylan Reid6b590e62016-10-27 19:10:53 -0700461 return false;
462 }
hschamd79f5392020-11-05 17:50:07 +0900463 device.type = *type;
464 base::Optional<bool> dynamic_major = dev.FindBoolKey("dynamicMajor");
465 if (dynamic_major.has_value())
466 device.dynamicMajor = *dynamic_major;
Stephen Barber7bae6642017-11-30 10:47:12 -0800467 if (device.dynamicMajor) {
hschamd79f5392020-11-05 17:50:07 +0900468 if (dev.FindKey("major")) {
Stephen Barber7bae6642017-11-30 10:47:12 -0800469 LOG(WARNING)
470 << "Ignoring \"major\" since \"dynamicMajor\" is specified for "
471 << device.path.value();
472 }
473 } else {
hschamd79f5392020-11-05 17:50:07 +0900474 if (!ParseIntFromDict(dev, "major", &device.major))
Stephen Barber7bae6642017-11-30 10:47:12 -0800475 return false;
476 }
477
hschamd79f5392020-11-05 17:50:07 +0900478 base::Optional<bool> dynamic_minor = dev.FindBoolKey("dynamicMinor");
479 if (dynamic_minor.has_value())
480 device.dynamicMinor = *dynamic_minor;
Luis Hector Chavezac20fc52017-10-10 11:08:41 -0700481 if (device.dynamicMinor) {
hschamd79f5392020-11-05 17:50:07 +0900482 if (dev.FindKey("minor")) {
Luis Hector Chavezac20fc52017-10-10 11:08:41 -0700483 LOG(WARNING)
484 << "Ignoring \"minor\" since \"dynamicMinor\" is specified for "
485 << device.path.value();
486 }
487 } else {
hschamd79f5392020-11-05 17:50:07 +0900488 if (!ParseIntFromDict(dev, "minor", &device.minor))
Luis Hector Chavezac20fc52017-10-10 11:08:41 -0700489 return false;
490 }
hschamd79f5392020-11-05 17:50:07 +0900491 if (!ParseIntFromDict(dev, "fileMode", &device.fileMode))
Dylan Reidc0c28502016-11-04 10:51:30 -0700492 return false;
hschamd79f5392020-11-05 17:50:07 +0900493 if (!ParseIntFromDict(dev, "uid", &device.uid))
Dylan Reidc0c28502016-11-04 10:51:30 -0700494 return false;
hschamd79f5392020-11-05 17:50:07 +0900495 if (!ParseIntFromDict(dev, "gid", &device.gid))
Dylan Reidc0c28502016-11-04 10:51:30 -0700496 return false;
Dylan Reid6b590e62016-10-27 19:10:53 -0700497
498 config_out->linux_config.devices.push_back(device);
499 }
500
501 return true;
502}
503
504// Parses the list of ID mappings and fills |mappings_out| with them.
hschamd79f5392020-11-05 17:50:07 +0900505bool ParseLinuxIdMappings(const base::Value* id_map_list,
Ben Chanf43980b2017-03-10 11:31:46 -0800506 std::vector<OciLinuxNamespaceMapping>* mappings_out) {
hschamd79f5392020-11-05 17:50:07 +0900507 for (size_t i = 0; i < id_map_list->GetList().size(); ++i) {
508 const base::Value& map = id_map_list->GetList()[i];
509 if (!map.is_dict()) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700510 LOG(ERROR) << "Fail to get id map " << i;
511 return false;
512 }
hschamd79f5392020-11-05 17:50:07 +0900513 OciLinuxNamespaceMapping new_map;
514 if (!ParseIntFromDict(map, "hostID", &new_map.hostID))
Dylan Reid6b590e62016-10-27 19:10:53 -0700515 return false;
hschamd79f5392020-11-05 17:50:07 +0900516 if (!ParseIntFromDict(map, "containerID", &new_map.containerID))
Dylan Reid6b590e62016-10-27 19:10:53 -0700517 return false;
hschamd79f5392020-11-05 17:50:07 +0900518 if (!ParseIntFromDict(map, "size", &new_map.size))
Dylan Reid6b590e62016-10-27 19:10:53 -0700519 return false;
Ben Chanf43980b2017-03-10 11:31:46 -0800520 mappings_out->push_back(new_map);
Dylan Reid6b590e62016-10-27 19:10:53 -0700521 }
522 return true;
523}
524
Dylan Reidb0a72772016-11-03 16:27:50 +0000525// Parses seccomp syscall args.
hschamd79f5392020-11-05 17:50:07 +0900526bool ParseSeccompArgs(const base::Value& syscall_dict,
Dylan Reidb0a72772016-11-03 16:27:50 +0000527 OciSeccompSyscall* syscall_out) {
hschamd79f5392020-11-05 17:50:07 +0900528 const base::Value* args = syscall_dict.FindListKey("args");
529 if (args) {
530 for (const auto& args_dict : args->GetList()) {
531 if (!args_dict.is_dict()) {
Dylan Reidb0a72772016-11-03 16:27:50 +0000532 LOG(ERROR) << "Failed to pars args dict for " << syscall_out->name;
533 return false;
534 }
535 OciSeccompArg this_arg;
hschamd79f5392020-11-05 17:50:07 +0900536 if (!ParseIntFromDict(args_dict, "index", &this_arg.index))
Dylan Reidb0a72772016-11-03 16:27:50 +0000537 return false;
hschamd79f5392020-11-05 17:50:07 +0900538 if (!ParseIntFromDict(args_dict, "value", &this_arg.value))
Dylan Reidb0a72772016-11-03 16:27:50 +0000539 return false;
hschamd79f5392020-11-05 17:50:07 +0900540 if (!ParseIntFromDict(args_dict, "value2", &this_arg.value2))
Dylan Reidb0a72772016-11-03 16:27:50 +0000541 return false;
hschamd79f5392020-11-05 17:50:07 +0900542 const std::string* op = args_dict.FindStringKey("op");
543 if (!op) {
Luis Hector Chavez9f9f66e2017-10-16 14:54:32 -0700544 LOG(ERROR) << "Failed to parse op for arg " << this_arg.index << " of "
545 << syscall_out->name;
Dylan Reidb0a72772016-11-03 16:27:50 +0000546 return false;
547 }
hschamd79f5392020-11-05 17:50:07 +0900548 this_arg.op = *op;
Dylan Reidb0a72772016-11-03 16:27:50 +0000549 syscall_out->args.push_back(this_arg);
550 }
551 }
552 return true;
553}
554
555// Parses the seccomp node if it is present.
hschamd79f5392020-11-05 17:50:07 +0900556bool ParseSeccompInfo(const base::Value& seccomp_dict,
Dylan Reidb0a72772016-11-03 16:27:50 +0000557 OciSeccomp* seccomp_out) {
hschamd79f5392020-11-05 17:50:07 +0900558 const std::string* default_action =
559 seccomp_dict.FindStringKey("defaultAction");
560 if (!default_action)
Dylan Reidb0a72772016-11-03 16:27:50 +0000561 return false;
hschamd79f5392020-11-05 17:50:07 +0900562 seccomp_out->defaultAction = *default_action;
Dylan Reidb0a72772016-11-03 16:27:50 +0000563 // Gets the list of architectures.
hschamd79f5392020-11-05 17:50:07 +0900564 const base::Value* architectures = seccomp_dict.FindListKey("architectures");
565 if (!architectures) {
Dylan Reidb0a72772016-11-03 16:27:50 +0000566 LOG(ERROR) << "Fail to read seccomp architectures";
567 return false;
568 }
hschamd79f5392020-11-05 17:50:07 +0900569 for (const auto& this_arch : architectures->GetList()) {
570 if (!this_arch.is_string()) {
Dylan Reidb0a72772016-11-03 16:27:50 +0000571 LOG(ERROR) << "Fail to parse seccomp architecture list";
572 return false;
573 }
hschamd79f5392020-11-05 17:50:07 +0900574 seccomp_out->architectures.push_back(this_arch.GetString());
Dylan Reidb0a72772016-11-03 16:27:50 +0000575 }
576
577 // Gets the list of syscalls.
hschamd79f5392020-11-05 17:50:07 +0900578 const base::Value* syscalls = seccomp_dict.FindListKey("syscalls");
579 if (!syscalls) {
Dylan Reidb0a72772016-11-03 16:27:50 +0000580 LOG(ERROR) << "Fail to read seccomp syscalls";
581 return false;
582 }
hschamd79f5392020-11-05 17:50:07 +0900583 for (size_t i = 0; i < syscalls->GetList().size(); ++i) {
584 const base::Value& syscall_dict = syscalls->GetList()[i];
585 if (!syscall_dict.is_dict()) {
Dylan Reidb0a72772016-11-03 16:27:50 +0000586 LOG(ERROR) << "Fail to parse seccomp syscalls list";
587 return false;
588 }
589 OciSeccompSyscall this_syscall;
hschamd79f5392020-11-05 17:50:07 +0900590 const std::string* name = syscall_dict.FindStringKey("name");
591 if (!name) {
Dylan Reidb0a72772016-11-03 16:27:50 +0000592 LOG(ERROR) << "Fail to parse syscall name " << i;
593 return false;
594 }
hschamd79f5392020-11-05 17:50:07 +0900595 this_syscall.name = *name;
596 const std::string* action = syscall_dict.FindStringKey("action");
597 if (!action) {
Dylan Reidb0a72772016-11-03 16:27:50 +0000598 LOG(ERROR) << "Fail to parse syscall action for " << this_syscall.name;
599 return false;
600 }
hschamd79f5392020-11-05 17:50:07 +0900601 this_syscall.action = *action;
602 if (!ParseSeccompArgs(syscall_dict, &this_syscall))
Dylan Reidb0a72772016-11-03 16:27:50 +0000603 return false;
604 seccomp_out->syscalls.push_back(this_syscall);
605 }
606
607 return true;
608}
609
Luis Hector Chavez7c6fddf2017-10-24 15:39:41 -0700610constexpr std::pair<const char*, int> kMountPropagationMapping[] = {
611 {"rprivate", MS_PRIVATE | MS_REC}, {"private", MS_PRIVATE},
612 {"rslave", MS_SLAVE | MS_REC}, {"slave", MS_SLAVE},
613 {"rshared", MS_SHARED | MS_REC}, {"shared", MS_SHARED},
614 {"", MS_SLAVE | MS_REC}, // Default value.
615};
616
617bool ParseMountPropagationFlags(const std::string& propagation,
618 int* propagation_flags_out) {
619 for (const auto& entry : kMountPropagationMapping) {
620 if (propagation == entry.first) {
621 *propagation_flags_out = entry.second;
622 return true;
623 }
624 }
625 LOG(ERROR) << "Unrecognized mount propagation flags: " << propagation;
626 return false;
627}
628
Luis Hector Chavez6e9a5332017-10-26 14:41:49 -0700629constexpr std::pair<const char*, uint64_t> kSecurebitsMapping[] = {
630#define SECUREBIT_MAP_ENTRY(secbit) \
631 { #secbit, SECBIT_##secbit }
Tom Hughesd49b8f62020-08-24 18:24:16 -0700632 SECUREBIT_MAP_ENTRY(NOROOT),
633 SECUREBIT_MAP_ENTRY(NOROOT_LOCKED),
Luis Hector Chavez6e9a5332017-10-26 14:41:49 -0700634 SECUREBIT_MAP_ENTRY(NO_SETUID_FIXUP),
Tom Hughesd49b8f62020-08-24 18:24:16 -0700635 SECUREBIT_MAP_ENTRY(NO_SETUID_FIXUP_LOCKED),
636 SECUREBIT_MAP_ENTRY(KEEP_CAPS),
Luis Hector Chavez6e9a5332017-10-26 14:41:49 -0700637 SECUREBIT_MAP_ENTRY(KEEP_CAPS_LOCKED),
638#if defined(SECBIT_NO_CAP_AMBIENT_RAISE)
639 // Kernels < v4.4 do not have this.
640 SECUREBIT_MAP_ENTRY(NO_CAP_AMBIENT_RAISE),
641 SECUREBIT_MAP_ENTRY(NO_CAP_AMBIENT_RAISE_LOCKED),
642#endif // SECBIT_NO_CAP_AMBIENT_RAISE
643#undef SECUREBIT_MAP_ENTRY
644};
645
646bool ParseSecurebit(const std::string& securebit_name, uint64_t* mask_out) {
647 for (const auto& entry : kSecurebitsMapping) {
648 if (securebit_name == entry.first) {
649 *mask_out = entry.second;
650 return true;
651 }
652 }
653 LOG(ERROR) << "Unrecognized securebit name: " << securebit_name;
654 return false;
655}
656
hschamd79f5392020-11-05 17:50:07 +0900657bool ParseSkipSecurebitsMask(const base::Value& skip_securebits_list,
Luis Hector Chavez6e9a5332017-10-26 14:41:49 -0700658 uint64_t* securebits_mask_out) {
hschamd79f5392020-11-05 17:50:07 +0900659 size_t num_securebits = skip_securebits_list.GetList().size();
Luis Hector Chavez6e9a5332017-10-26 14:41:49 -0700660 for (size_t i = 0; i < num_securebits; ++i) {
hschamd79f5392020-11-05 17:50:07 +0900661 const base::Value& securebit_name = skip_securebits_list.GetList()[i];
662 if (!securebit_name.is_string()) {
Luis Hector Chavez6e9a5332017-10-26 14:41:49 -0700663 LOG(ERROR) << "Fail to get securebit name " << i;
664 return false;
665 }
666 uint64_t mask = 0;
hschamd79f5392020-11-05 17:50:07 +0900667 if (!ParseSecurebit(securebit_name.GetString(), &mask))
Luis Hector Chavez6e9a5332017-10-26 14:41:49 -0700668 return false;
669 *securebits_mask_out |= mask;
670 }
671 return true;
672}
673
yusukesd9598352018-01-09 17:40:33 -0800674// Parses the cpu node if it is present.
hschamd79f5392020-11-05 17:50:07 +0900675bool ParseCpuInfo(const base::Value& cpu_dict, OciCpu* cpu_out) {
yusukesd9598352018-01-09 17:40:33 -0800676 ParseIntFromDict(cpu_dict, "shares", &cpu_out->shares);
677 ParseIntFromDict(cpu_dict, "quota", &cpu_out->quota);
678 ParseIntFromDict(cpu_dict, "period", &cpu_out->period);
679 ParseIntFromDict(cpu_dict, "realtimeRuntime", &cpu_out->realtimeRuntime);
680 ParseIntFromDict(cpu_dict, "realtimePeriod", &cpu_out->realtimePeriod);
681 return true;
682}
683
Dylan Reid6b590e62016-10-27 19:10:53 -0700684// Parses the linux node which has information about setting up a user
685// namespace, and the list of devices for the container.
hschamd79f5392020-11-05 17:50:07 +0900686bool ParseLinuxConfigDict(const base::Value& runtime_root_dict,
Dylan Reid6b590e62016-10-27 19:10:53 -0700687 OciConfigPtr const& config_out) {
688 // |linux_dict| is owned by |runtime_root_dict|
hschamd79f5392020-11-05 17:50:07 +0900689 const base::Value* linux_dict = runtime_root_dict.FindDictKey("linux");
690 if (!linux_dict) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700691 LOG(ERROR) << "Fail to get linux dictionary from the runtime dictionary";
692 return false;
693 }
694
695 // |uid_map_list| is owned by |linux_dict|
hschamd79f5392020-11-05 17:50:07 +0900696 const base::Value* uid_map_list = linux_dict->FindListKey("uidMappings");
697 if (uid_map_list)
Stephen Barber771653f2017-10-04 23:48:57 -0700698 ParseLinuxIdMappings(uid_map_list, &config_out->linux_config.uidMappings);
Dylan Reid6b590e62016-10-27 19:10:53 -0700699
700 // |gid_map_list| is owned by |linux_dict|
hschamd79f5392020-11-05 17:50:07 +0900701 const base::Value* gid_map_list = linux_dict->FindListKey("gidMappings");
702 if (gid_map_list)
Stephen Barber771653f2017-10-04 23:48:57 -0700703 ParseLinuxIdMappings(gid_map_list, &config_out->linux_config.gidMappings);
Dylan Reid6b590e62016-10-27 19:10:53 -0700704
705 if (!ParseDeviceList(*linux_dict, config_out))
706 return false;
707
hschamd79f5392020-11-05 17:50:07 +0900708 const base::Value* resources_dict = linux_dict->FindDictKey("resources");
709 if (resources_dict) {
Dylan Reid6985e3b2017-03-31 19:39:16 -0700710 if (!ParseResources(*resources_dict, &config_out->linux_config.resources))
711 return false;
712 }
713
hschamd79f5392020-11-05 17:50:07 +0900714 const base::Value* namespaces_list = linux_dict->FindListKey("namespaces");
715 if (namespaces_list) {
Stephen Barber3c0a2022017-09-08 14:17:57 -0700716 if (!ParseNamespaces(namespaces_list, &config_out->linux_config.namespaces))
717 return false;
718 }
719
hschamd79f5392020-11-05 17:50:07 +0900720 const base::Value* seccomp_dict = linux_dict->FindDictKey("seccomp");
721 if (seccomp_dict) {
Dylan Reidb0a72772016-11-03 16:27:50 +0000722 if (!ParseSeccompInfo(*seccomp_dict, &config_out->linux_config.seccomp))
723 return false;
724 }
725
hschamd79f5392020-11-05 17:50:07 +0900726 const std::string* rootfs_propagation_string =
727 linux_dict->FindStringKey("rootfsPropagation");
Luis Hector Chavez7c6fddf2017-10-24 15:39:41 -0700728 if (!ParseMountPropagationFlags(
hschamd79f5392020-11-05 17:50:07 +0900729 rootfs_propagation_string ? *rootfs_propagation_string
730 : base::EmptyString(), // Optional
Luis Hector Chavez7c6fddf2017-10-24 15:39:41 -0700731 &config_out->linux_config.rootfsPropagation)) {
732 return false;
733 }
734
hschamd79f5392020-11-05 17:50:07 +0900735 const std::string* cgroups_path_string =
736 linux_dict->FindStringKey("cgroupsPath");
737 if (cgroups_path_string)
738 config_out->linux_config.cgroupsPath = base::FilePath(*cgroups_path_string);
Luis Hector Chavez45ac1242017-10-26 13:21:16 -0700739
hschamd79f5392020-11-05 17:50:07 +0900740 const std::string* alt_syscall = linux_dict->FindStringKey("altSyscall");
741 config_out->linux_config.altSyscall =
742 alt_syscall ? *alt_syscall : base::EmptyString(); // Optional
Luis Hector Chavez0f3d7a42017-10-26 10:48:30 -0700743
hschamd79f5392020-11-05 17:50:07 +0900744 base::Optional<bool> core_sched = linux_dict->FindBoolKey("coreSched");
745 config_out->linux_config.coreSched = core_sched.value_or(false); // Optional
Ereth McKnight-MacNeil70c50282020-07-02 00:30:56 -0700746
hschamd79f5392020-11-05 17:50:07 +0900747 const base::Value* skip_securebits_list =
748 linux_dict->FindListKey("skipSecurebits");
749 if (skip_securebits_list) {
Luis Hector Chavez6e9a5332017-10-26 14:41:49 -0700750 if (!ParseSkipSecurebitsMask(*skip_securebits_list,
751 &config_out->linux_config.skipSecurebits)) {
752 return false;
753 }
754 } else {
755 config_out->linux_config.skipSecurebits = 0; // Optional
756 }
757
hschamd79f5392020-11-05 17:50:07 +0900758 const base::Value* cpu_dict = linux_dict->FindDictKey("cpu");
759 if (cpu_dict) {
yusukesd9598352018-01-09 17:40:33 -0800760 if (!ParseCpuInfo(*cpu_dict, &config_out->linux_config.cpu))
761 return false;
762 }
763
Dylan Reid6b590e62016-10-27 19:10:53 -0700764 return true;
765}
766
Dylan Reid45e34fe2016-12-02 15:11:53 -0800767bool HostnameValid(const std::string& hostname) {
768 if (hostname.length() > 255)
769 return false;
770
771 const std::regex name("^[0-9a-zA-Z]([0-9a-zA-Z-]*[0-9a-zA-Z])?$");
772 if (!std::regex_match(hostname, name))
773 return false;
774
775 const std::regex double_dash("--");
776 if (std::regex_match(hostname, double_dash))
777 return false;
778
779 return true;
780}
781
hschamd79f5392020-11-05 17:50:07 +0900782bool ParseHooksList(const base::Value& hooks_list,
Luis Hector Chavezf8e8f4c2017-08-01 01:09:39 -0700783 std::vector<OciHook>* hooks_out,
784 const std::string& hook_type) {
hschamd79f5392020-11-05 17:50:07 +0900785 size_t num_hooks = hooks_list.GetList().size();
Luis Hector Chavezf8e8f4c2017-08-01 01:09:39 -0700786 for (size_t i = 0; i < num_hooks; ++i) {
787 OciHook hook;
hschamd79f5392020-11-05 17:50:07 +0900788 const base::Value& hook_dict = hooks_list.GetList()[i];
789 if (!hook_dict.is_dict()) {
Luis Hector Chavezf8e8f4c2017-08-01 01:09:39 -0700790 LOG(ERROR) << "Fail to get " << hook_type << " hook item " << i;
791 return false;
792 }
793
hschamd79f5392020-11-05 17:50:07 +0900794 const std::string* path = hook_dict.FindStringPath("path");
795 if (!path) {
Luis Hector Chavezf8e8f4c2017-08-01 01:09:39 -0700796 LOG(ERROR) << "Fail to get path of " << hook_type << " hook " << i;
797 return false;
798 }
hschamd79f5392020-11-05 17:50:07 +0900799 hook.path = base::FilePath(*path);
Luis Hector Chavezf8e8f4c2017-08-01 01:09:39 -0700800
hschamd79f5392020-11-05 17:50:07 +0900801 const base::Value* hook_args = hook_dict.FindListKey("args");
Luis Hector Chavezf8e8f4c2017-08-01 01:09:39 -0700802 // args are optional.
hschamd79f5392020-11-05 17:50:07 +0900803 if (hook_args) {
804 size_t num_args = hook_args->GetList().size();
Luis Hector Chavezf8e8f4c2017-08-01 01:09:39 -0700805 for (size_t j = 0; j < num_args; ++j) {
hschamd79f5392020-11-05 17:50:07 +0900806 const base::Value& arg = hook_args->GetList()[j];
807 if (!arg.is_string()) {
Luis Hector Chavezf8e8f4c2017-08-01 01:09:39 -0700808 LOG(ERROR) << "Fail to get arg " << j << " of " << hook_type
809 << " hook " << i;
810 return false;
811 }
hschamd79f5392020-11-05 17:50:07 +0900812 hook.args.push_back(arg.GetString());
Luis Hector Chavezf8e8f4c2017-08-01 01:09:39 -0700813 }
814 }
815
hschamd79f5392020-11-05 17:50:07 +0900816 const base::Value* hook_envs = hook_dict.FindListKey("env");
Luis Hector Chavezf8e8f4c2017-08-01 01:09:39 -0700817 // envs are optional.
hschamd79f5392020-11-05 17:50:07 +0900818 if (hook_envs) {
819 size_t num_env = hook_envs->GetList().size();
Luis Hector Chavezf8e8f4c2017-08-01 01:09:39 -0700820 for (size_t j = 0; j < num_env; ++j) {
hschamd79f5392020-11-05 17:50:07 +0900821 const base::Value& env = hook_envs->GetList()[j];
822 if (!env.is_string()) {
Luis Hector Chavezf8e8f4c2017-08-01 01:09:39 -0700823 LOG(ERROR) << "Fail to get env " << j << " of " << hook_type
824 << " hook " << i;
825 return false;
826 }
827 std::vector<std::string> kvp = base::SplitString(
hschamd79f5392020-11-05 17:50:07 +0900828 env.GetString(), "=", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
Luis Hector Chavezf8e8f4c2017-08-01 01:09:39 -0700829 if (kvp.size() != 2) {
hschamd79f5392020-11-05 17:50:07 +0900830 LOG(ERROR) << "Fail to parse env \"" << env.GetString()
Luis Hector Chavezf8e8f4c2017-08-01 01:09:39 -0700831 << "\". Must be in name=value format.";
832 return false;
833 }
834 hook.env.insert(std::make_pair(kvp[0], kvp[1]));
835 }
836 }
837
hschamd79f5392020-11-05 17:50:07 +0900838 base::Optional<int> timeout_seconds = hook_dict.FindIntKey("timeout");
Luis Hector Chavezf8e8f4c2017-08-01 01:09:39 -0700839 // timeout is optional.
hschamd79f5392020-11-05 17:50:07 +0900840 hook.timeout = timeout_seconds.has_value()
841 ? base::TimeDelta::FromSeconds(*timeout_seconds)
842 : base::TimeDelta::Max();
Luis Hector Chavezf8e8f4c2017-08-01 01:09:39 -0700843
844 hooks_out->emplace_back(std::move(hook));
845 }
846 return true;
847}
848
hschamd79f5392020-11-05 17:50:07 +0900849bool ParseHooks(const base::Value& config_root_dict,
Luis Hector Chavezf8e8f4c2017-08-01 01:09:39 -0700850 OciConfigPtr const& config_out) {
hschamd79f5392020-11-05 17:50:07 +0900851 const base::Value* hooks_config_dict = config_root_dict.FindDictKey("hooks");
852 if (!hooks_config_dict) {
Luis Hector Chavezf8e8f4c2017-08-01 01:09:39 -0700853 // Hooks are optional.
854 return true;
855 }
856
hschamd79f5392020-11-05 17:50:07 +0900857 const base::Value* hooks_list = hooks_config_dict->FindListKey("precreate");
858 if (hooks_list) {
yusukesa7b5e942018-04-10 13:48:35 -0700859 if (!ParseHooksList(*hooks_list, &config_out->pre_create_hooks,
860 "precreate")) {
861 return false;
862 }
863 }
hschamd79f5392020-11-05 17:50:07 +0900864 hooks_list = hooks_config_dict->FindListKey("prechroot");
865 if (hooks_list) {
Luis Hector Chavezbb515a02017-09-29 15:44:35 -0700866 if (!ParseHooksList(*hooks_list, &config_out->pre_chroot_hooks,
867 "prechroot")) {
868 return false;
869 }
870 }
hschamd79f5392020-11-05 17:50:07 +0900871 hooks_list = hooks_config_dict->FindListKey("prestart");
872 if (hooks_list) {
Luis Hector Chavezf8e8f4c2017-08-01 01:09:39 -0700873 if (!ParseHooksList(*hooks_list, &config_out->pre_start_hooks, "prestart"))
874 return false;
875 }
hschamd79f5392020-11-05 17:50:07 +0900876 hooks_list = hooks_config_dict->FindListKey("poststart");
877 if (hooks_list) {
Luis Hector Chavez9f9f66e2017-10-16 14:54:32 -0700878 if (!ParseHooksList(*hooks_list, &config_out->post_start_hooks,
879 "poststart"))
Luis Hector Chavezf8e8f4c2017-08-01 01:09:39 -0700880 return false;
881 }
hschamd79f5392020-11-05 17:50:07 +0900882 hooks_list = hooks_config_dict->FindListKey("poststop");
883 if (hooks_list) {
Luis Hector Chavezf8e8f4c2017-08-01 01:09:39 -0700884 if (!ParseHooksList(*hooks_list, &config_out->post_stop_hooks, "poststop"))
885 return false;
886 }
887 return true;
888}
889
Dylan Reid6b590e62016-10-27 19:10:53 -0700890// Parses the configuration file for the container. The config file specifies
891// basic filesystem info and details about the process to be run. namespace,
892// cgroup, and syscall configurations are also specified
hschamd79f5392020-11-05 17:50:07 +0900893bool ParseConfigDict(const base::Value& config_root_dict,
Dylan Reid6b590e62016-10-27 19:10:53 -0700894 OciConfigPtr const& config_out) {
hschamd79f5392020-11-05 17:50:07 +0900895 const std::string* oci_version = config_root_dict.FindStringKey("ociVersion");
896 if (!oci_version) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700897 LOG(ERROR) << "Failed to parse ociVersion";
898 return false;
899 }
hschamd79f5392020-11-05 17:50:07 +0900900 config_out->ociVersion = *oci_version;
901 const std::string* host_name = config_root_dict.FindStringKey("hostname");
902 if (!host_name) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700903 LOG(ERROR) << "Failed to parse hostname";
904 return false;
905 }
hschamd79f5392020-11-05 17:50:07 +0900906 config_out->hostname = *host_name;
Dylan Reid45e34fe2016-12-02 15:11:53 -0800907 if (!HostnameValid(config_out->hostname)) {
908 LOG(ERROR) << "Invalid hostname " << config_out->hostname;
909 return false;
910 }
Dylan Reid6b590e62016-10-27 19:10:53 -0700911
912 // Platform info
913 if (!ParsePlatformConfig(config_root_dict, config_out)) {
914 return false;
915 }
916
917 // Root fs info
918 if (!ParseRootFileSystemConfig(config_root_dict, config_out)) {
919 return false;
920 }
921
922 // Process info
923 if (!ParseProcessConfig(config_root_dict, config_out)) {
924 return false;
925 }
926
927 // Get a list of mount points and mounts.
928 if (!ParseMounts(config_root_dict, config_out)) {
929 LOG(ERROR) << "Failed to parse mounts";
930 return false;
931 }
932
Luis Hector Chavezf8e8f4c2017-08-01 01:09:39 -0700933 // Hooks info
934 if (!ParseHooks(config_root_dict, config_out)) {
935 return false;
936 }
937
Dylan Reid6b590e62016-10-27 19:10:53 -0700938 // Parse linux node.
939 if (!ParseLinuxConfigDict(config_root_dict, config_out)) {
940 LOG(ERROR) << "Failed to parse the linux node";
941 return false;
942 }
943
944 return true;
945}
946
Ben Chanf43980b2017-03-10 11:31:46 -0800947} // anonymous namespace
Dylan Reid6b590e62016-10-27 19:10:53 -0700948
949bool ParseContainerConfig(const std::string& config_json_data,
950 OciConfigPtr const& config_out) {
hscham44828cd2020-06-17 14:36:08 +0900951 auto result = base::JSONReader::ReadAndReturnValueWithError(
952 config_json_data, base::JSON_PARSE_RFC);
hschamc62f55d2020-12-17 14:52:05 +0900953 if (!result.value) {
hscham44828cd2020-06-17 14:36:08 +0900954 LOG(ERROR) << "Fail to parse config.json: " << result.error_message;
Dylan Reid6b590e62016-10-27 19:10:53 -0700955 return false;
956 }
hschamd79f5392020-11-05 17:50:07 +0900957 if (!result.value->is_dict()) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700958 LOG(ERROR) << "Fail to parse root dictionary from config.json";
959 return false;
960 }
hschamd79f5392020-11-05 17:50:07 +0900961 if (!ParseConfigDict(*result.value, config_out)) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700962 return false;
963 }
964
965 return true;
966}
967
Stephen Barber5f6dc9b2017-04-04 12:36:32 -0700968} // namespace run_oci