blob: 3d421ef56f0a37c63af40523229a3ec73e514688 [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 Chavez8373b412017-07-10 12:51:07 -07007#include <sys/capability.h>
Dylan Reid93fa4602017-06-06 13:39:31 -07008#include <sys/resource.h>
Dylan Reid6b590e62016-10-27 19:10:53 -07009#include <unistd.h>
10
Luis Hector Chavez8e4dcc12017-06-27 12:54:47 -070011#include <map>
Ben Chanf43980b2017-03-10 11:31:46 -080012#include <regex> // NOLINT(build/c++11)
Dylan Reid6b590e62016-10-27 19:10:53 -070013#include <string>
14#include <vector>
15
16#include <base/json/json_reader.h>
17#include <base/values.h>
18
Stephen Barber5f6dc9b2017-04-04 12:36:32 -070019namespace run_oci {
Dylan Reid6b590e62016-10-27 19:10:53 -070020
21namespace {
22
Dylan Reidc0c28502016-11-04 10:51:30 -070023// Gets a uint32 from the given dictionary.
24bool ParseUint32FromDict(const base::DictionaryValue& dict, const char *name,
25 uint32_t* val_out) {
26 double double_val;
27 if (!dict.GetDouble(name, &double_val)) {
Dylan Reidc0c28502016-11-04 10:51:30 -070028 return false;
29 }
30 *val_out = double_val;
31 return true;
32}
33
Dylan Reidb0a72772016-11-03 16:27:50 +000034// Gets a uint64 from the given dictionary.
35bool ParseUint64FromDict(const base::DictionaryValue& dict, const char *name,
36 uint64_t* val_out) {
37 double double_val;
38 if (!dict.GetDouble(name, &double_val)) {
Dylan Reidb0a72772016-11-03 16:27:50 +000039 return false;
40 }
41 *val_out = double_val;
42 return true;
43}
44
Dylan Reid6b590e62016-10-27 19:10:53 -070045// Parses basic platform configuration.
46bool ParsePlatformConfig(const base::DictionaryValue& config_root_dict,
47 OciConfigPtr const& config_out) {
48 // |platform_dict| stays owned by |config_root_dict|
49 const base::DictionaryValue* platform_dict = nullptr;
50 if (!config_root_dict.GetDictionary("platform", &platform_dict)) {
51 LOG(ERROR) << "Fail to parse platform dictionary from config";
52 return false;
53 }
54
55 if (!platform_dict->GetString("os", &config_out->platform.os)) {
56 return false;
57 }
58
59 if (!platform_dict->GetString("arch", &config_out->platform.arch)) {
60 return false;
61 }
62
63 return true;
64}
65
66// Parses root fs info.
67bool ParseRootFileSystemConfig(const base::DictionaryValue& config_root_dict,
68 OciConfigPtr const& config_out) {
69 // |rootfs_dict| stays owned by |config_root_dict|
70 const base::DictionaryValue* rootfs_dict = nullptr;
71 if (!config_root_dict.GetDictionary("root", &rootfs_dict)) {
72 LOG(ERROR) << "Fail to parse rootfs dictionary from config";
73 return false;
74 }
75 if (!rootfs_dict->GetString("path", &config_out->root.path)) {
76 LOG(ERROR) << "Fail to get rootfs path from config";
77 return false;
78 }
Dylan Reid6d650742016-11-02 23:10:38 -070079 rootfs_dict->GetBoolean("readonly", &config_out->root.readonly);
Dylan Reid6b590e62016-10-27 19:10:53 -070080 return true;
81}
82
Luis Hector Chavez8e4dcc12017-06-27 12:54:47 -070083// Fills |config_out| with information about the capability sets in the
84// container.
85bool ParseCapabilitiesConfig(const base::DictionaryValue& capabilities_dict,
86 std::map<std::string, CapSet>* config_out) {
87 constexpr const char* kCapabilitySetNames[] = {
88 "effective", "bounding", "inheritable", "permitted", "ambient"};
89 const std::string kAmbientCapabilitySetName = "ambient";
90
91 CapSet caps_superset;
92 for (const char* set_name : kCapabilitySetNames) {
93 // |capset_list| stays owned by |capabilities_dict|.
94 const base::ListValue* capset_list = nullptr;
95 if (!capabilities_dict.GetList(set_name, &capset_list))
96 continue;
97 CapSet caps;
Luis Hector Chavez8373b412017-07-10 12:51:07 -070098 cap_value_t cap_value;
Luis Hector Chavez8e4dcc12017-06-27 12:54:47 -070099 for (const auto* cap_name_value : *capset_list) {
100 std::string cap_name;
101 if (!cap_name_value->GetAsString(&cap_name)) {
102 LOG(ERROR) << "Capability list " << set_name
103 << " contains a non-string";
104 return false;
105 }
Luis Hector Chavez8373b412017-07-10 12:51:07 -0700106 if (cap_from_name(cap_name.c_str(), &cap_value) == -1) {
Luis Hector Chavez8e4dcc12017-06-27 12:54:47 -0700107 LOG(ERROR) << "Unrecognized capability name: " << cap_name;
108 return false;
109 }
Luis Hector Chavez8373b412017-07-10 12:51:07 -0700110 caps[cap_value] = true;
Luis Hector Chavez8e4dcc12017-06-27 12:54:47 -0700111 }
112 (*config_out)[set_name] = caps;
113 caps_superset = caps;
114 }
115
116 // We currently only support sets that are identical, except that ambient is
117 // optional.
118 for (const char* set_name : kCapabilitySetNames) {
119 auto it = config_out->find(set_name);
120 if (it == config_out->end() && set_name == kAmbientCapabilitySetName) {
121 // Ambient capabilities are optional.
122 continue;
123 }
124 if (it == config_out->end()) {
125 LOG(ERROR)
126 << "If capabilities are set, all capability sets should be present";
127 return false;
128 }
129 if (it->second != caps_superset) {
130 LOG(ERROR)
131 << "If capabilities are set, all capability sets should be identical";
132 return false;
133 }
134 }
135
136 return true;
137}
138
Dylan Reid93fa4602017-06-06 13:39:31 -0700139const std::map<std::string, int> kRlimitMap = {
140#define RLIMIT_MAP_ENTRY(limit) \
141 { "RLIMIT_" #limit, RLIMIT_##limit }
142 RLIMIT_MAP_ENTRY(CPU),
143 RLIMIT_MAP_ENTRY(FSIZE),
144 RLIMIT_MAP_ENTRY(DATA),
145 RLIMIT_MAP_ENTRY(STACK),
146 RLIMIT_MAP_ENTRY(CORE),
147 RLIMIT_MAP_ENTRY(RSS),
148 RLIMIT_MAP_ENTRY(NPROC),
149 RLIMIT_MAP_ENTRY(NOFILE),
150 RLIMIT_MAP_ENTRY(MEMLOCK),
151 RLIMIT_MAP_ENTRY(AS),
152 RLIMIT_MAP_ENTRY(LOCKS),
153 RLIMIT_MAP_ENTRY(SIGPENDING),
154 RLIMIT_MAP_ENTRY(MSGQUEUE),
155 RLIMIT_MAP_ENTRY(NICE),
156 RLIMIT_MAP_ENTRY(RTPRIO),
157 RLIMIT_MAP_ENTRY(RTTIME),
158#undef RLIMIT_MAP_ENTRY
159};
160
161// Fills |config_out| with information about the capability sets in the
162// container.
163bool ParseRlimitsConfig(const base::ListValue& rlimits_list,
164 std::vector<OciProcessRlimit>* rlimits_out) {
165 size_t num_limits = rlimits_list.GetSize();
166 for (size_t i = 0; i < num_limits; ++i) {
167 const base::DictionaryValue* rlimits_dict;
168 if (!rlimits_list.GetDictionary(i, &rlimits_dict)) {
169 LOG(ERROR) << "Fail to get rlimit item " << i;
170 return false;
171 }
172
173 std::string rlimit_name;
174 if (!rlimits_dict->GetString("type", &rlimit_name)) {
175 LOG(ERROR) << "Fail to get type of rlimit " << i;
176 return false;
177 }
178 const auto it = kRlimitMap.find(rlimit_name);
179 if (it == kRlimitMap.end()) {
180 LOG(ERROR) << "Unrecognized rlimit name: " << rlimit_name;
181 return false;
182 }
183
184 OciProcessRlimit limit;
185 limit.type = it->second;
186 if (!ParseUint32FromDict(*rlimits_dict, "hard", &limit.hard)) {
187 LOG(ERROR) << "Fail to get hard limit of rlimit " << i;
188 return false;
189 }
190 if (!ParseUint32FromDict(*rlimits_dict, "soft", &limit.soft)) {
191 LOG(ERROR) << "Fail to get soft limit of rlimit " << i;
192 return false;
193 }
194 rlimits_out->push_back(limit);
195 }
196
197 return true;
198}
199
Dylan Reid6b590e62016-10-27 19:10:53 -0700200// Fills |config_out| with information about the main process to run in the
201// container and the user it should be run as.
202bool ParseProcessConfig(const base::DictionaryValue& config_root_dict,
203 OciConfigPtr const& config_out) {
204 // |process_dict| stays owned by |config_root_dict|
205 const base::DictionaryValue* process_dict = nullptr;
206 if (!config_root_dict.GetDictionary("process", &process_dict)) {
207 LOG(ERROR) << "Fail to get main process from config";
208 return false;
209 }
210 process_dict->GetBoolean("terminal", &config_out->process.terminal);
211 // |user_dict| stays owned by |process_dict|
212 const base::DictionaryValue* user_dict = nullptr;
213 if (!process_dict->GetDictionary("user", &user_dict)) {
214 LOG(ERROR) << "Failed to get user info from config";
215 return false;
216 }
Dylan Reidc0c28502016-11-04 10:51:30 -0700217 if (!ParseUint32FromDict(*user_dict, "uid", &config_out->process.user.uid))
Dylan Reid6b590e62016-10-27 19:10:53 -0700218 return false;
Dylan Reidc0c28502016-11-04 10:51:30 -0700219 if (!ParseUint32FromDict(*user_dict, "gid", &config_out->process.user.gid))
Dylan Reid6b590e62016-10-27 19:10:53 -0700220 return false;
Dylan Reid6b590e62016-10-27 19:10:53 -0700221 // |args_list| stays owned by |process_dict|
222 const base::ListValue* args_list = nullptr;
223 if (!process_dict->GetList("args", &args_list)) {
224 LOG(ERROR) << "Fail to get main process args from config";
225 return false;
226 }
227 size_t num_args = args_list->GetSize();
228 for (size_t i = 0; i < num_args; ++i) {
229 std::string arg;
230 if (!args_list->GetString(i, &arg)) {
231 LOG(ERROR) << "Fail to get process args from config";
232 return false;
233 }
234 config_out->process.args.push_back(arg);
235 }
236 // |env_list| stays owned by |process_dict|
237 const base::ListValue* env_list = nullptr;
238 if (process_dict->GetList("env", &env_list)) {
239 size_t num_env = env_list->GetSize();
240 for (size_t i = 0; i < num_env; ++i) {
241 std::string env;
242 if (!env_list->GetString(i, &env)) {
243 LOG(ERROR) << "Fail to get process env from config";
244 return false;
245 }
246 config_out->process.env.push_back(env);
247 }
248 }
249 if (!process_dict->GetString("cwd", &config_out->process.cwd)) {
250 LOG(ERROR) << "failed to get cwd of process";
251 return false;
252 }
Luis Hector Chavez15e8e672017-07-20 15:13:27 -0700253 // selinuxLabel is optional.
254 process_dict->GetString("selinuxLabel", &config_out->process.selinuxLabel);
Luis Hector Chavez8e4dcc12017-06-27 12:54:47 -0700255 // |capabilities_dict| stays owned by |process_dict|
256 const base::DictionaryValue* capabilities_dict = nullptr;
257 if (process_dict->GetDictionary("capabilities", &capabilities_dict)) {
258 if (!ParseCapabilitiesConfig(*capabilities_dict,
259 &config_out->process.capabilities)) {
260 return false;
261 }
262 }
Dylan Reid6b590e62016-10-27 19:10:53 -0700263
Dylan Reid93fa4602017-06-06 13:39:31 -0700264 // |rlimit_list| stays owned by |process_dict|
265 const base::ListValue* rlimits_list = nullptr;
266 if (process_dict->GetList("rlimits", &rlimits_list)) {
267 if (!ParseRlimitsConfig(*rlimits_list, &config_out->process.rlimits)) {
268 return false;
269 }
270 }
271
Dylan Reid6b590e62016-10-27 19:10:53 -0700272 return true;
273}
274
275// Parses the 'mounts' field. The necessary mounts for running the container
276// are specified here.
277bool ParseMounts(const base::DictionaryValue& config_root_dict,
278 OciConfigPtr const& config_out) {
279 // |config_mounts_list| stays owned by |config_root_dict|
280 const base::ListValue* config_mounts_list = nullptr;
281 if (!config_root_dict.GetList("mounts", &config_mounts_list)) {
282 LOG(ERROR) << "Fail to get mounts from config dictionary";
283 return false;
284 }
285
286 for (size_t i = 0; i < config_mounts_list->GetSize(); ++i) {
287 const base::DictionaryValue* mount_dict;
288 if (!config_mounts_list->GetDictionary(i, &mount_dict)) {
289 LOG(ERROR) << "Fail to get mount item " << i;
290 return false;
291 }
292 OciMount mount;
293 if (!mount_dict->GetString("destination", &mount.destination)) {
294 LOG(ERROR) << "Fail to get mount path for mount " << i;
295 return false;
296 }
Dylan Reid6d650742016-11-02 23:10:38 -0700297 if (!mount_dict->GetString("type", &mount.type)) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700298 LOG(ERROR) << "Fail to get mount type for mount " << i;
299 return false;
300 }
301 if (!mount_dict->GetString("source", &mount.source)) {
302 LOG(ERROR) << "Fail to get mount source for mount " << i;
303 return false;
304 }
305
306 // |options| are owned by |mount_dict|
307 const base::ListValue* options = nullptr;
308 if (mount_dict->GetList("options", &options)) {
309 for (size_t j = 0; j < options->GetSize(); ++j) {
310 std::string this_opt;
311 if (!options->GetString(j, &this_opt)) {
312 LOG(ERROR) << "Fail to get option " << j << " from mount options";
313 return false;
314 }
315 mount.options.push_back(this_opt);
316 }
317 }
318
319 config_out->mounts.push_back(mount);
320 }
321 return true;
322}
323
Dylan Reid6985e3b2017-03-31 19:39:16 -0700324// Parses the linux resource list
325bool ParseResources(const base::DictionaryValue& resources_dict,
326 OciLinuxResources* resources_out) {
327 // |device_list| is owned by |resources_dict|
328 const base::ListValue* device_list = nullptr;
329 if (!resources_dict.GetList("devices", &device_list)) {
330 // The device list is optional.
331 return true;
332 }
333 size_t num_devices = device_list->GetSize();
334 for (size_t i = 0; i < num_devices; ++i) {
335 OciLinuxCgroupDevice device;
336
337 const base::DictionaryValue* dev;
338 if (!device_list->GetDictionary(i, &dev)) {
339 LOG(ERROR) << "Fail to get device " << i;
340 return false;
341 }
342
343 if (!dev->GetBoolean("allow", &device.allow)) {
344 LOG(ERROR) << "Fail to get allow value for device " << i;
345 return false;
346 }
347 if (!dev->GetString("access", &device.access))
348 device.access = "rwm"; // Optional, default to all perms.
349 if (!dev->GetString("type", &device.type))
350 device.type = "a"; // Optional, default to both a means all.
351 if (!ParseUint32FromDict(*dev, "major", &device.major))
352 device.major = -1; // Optional, -1 will map to all devices.
353 if (!ParseUint32FromDict(*dev, "minor", &device.minor))
354 device.minor = -1; // Optional, -1 will map to all devices.
355
356 resources_out->devices.push_back(device);
357 }
358
359 return true;
360}
361
Dylan Reid6b590e62016-10-27 19:10:53 -0700362// Parse the list of device nodes that the container needs to run.
363bool ParseDeviceList(const base::DictionaryValue& linux_dict,
364 OciConfigPtr const& config_out) {
365 // |device_list| is owned by |linux_dict|
366 const base::ListValue* device_list = nullptr;
367 if (!linux_dict.GetList("devices", &device_list)) {
Dylan Reida5ed1272016-11-11 16:43:39 -0800368 // The device list is optional.
369 return true;
Dylan Reid6b590e62016-10-27 19:10:53 -0700370 }
371 size_t num_devices = device_list->GetSize();
372 for (size_t i = 0; i < num_devices; ++i) {
373 OciLinuxDevice device;
374
375 const base::DictionaryValue* dev;
376 if (!device_list->GetDictionary(i, &dev)) {
377 LOG(ERROR) << "Fail to get device " << i;
378 return false;
379 }
380 std::string path;
381 if (!dev->GetString("path", &device.path)) {
382 LOG(ERROR) << "Fail to get path for dev";
383 return false;
384 }
Dylan Reid6d650742016-11-02 23:10:38 -0700385 if (!dev->GetString("type", &device.type)) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700386 LOG(ERROR) << "Fail to get type for " << device.path;
387 return false;
388 }
Dylan Reidc0c28502016-11-04 10:51:30 -0700389 if (!ParseUint32FromDict(*dev, "major", &device.major))
390 return false;
391 if (!ParseUint32FromDict(*dev, "minor", &device.minor))
392 return false;
393 if (!ParseUint32FromDict(*dev, "fileMode", &device.fileMode))
394 return false;
395 if (!ParseUint32FromDict(*dev, "uid", &device.uid))
396 return false;
397 if (!ParseUint32FromDict(*dev, "gid", &device.gid))
398 return false;
Dylan Reid6b590e62016-10-27 19:10:53 -0700399
400 config_out->linux_config.devices.push_back(device);
401 }
402
403 return true;
404}
405
406// Parses the list of ID mappings and fills |mappings_out| with them.
407bool ParseLinuxIdMappings(const base::ListValue* id_map_list,
Ben Chanf43980b2017-03-10 11:31:46 -0800408 std::vector<OciLinuxNamespaceMapping>* mappings_out) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700409 for (size_t i = 0; i < id_map_list->GetSize(); ++i) {
410 OciLinuxNamespaceMapping new_map;
411 const base::DictionaryValue* map;
412 if (!id_map_list->GetDictionary(i, &map)) {
413 LOG(ERROR) << "Fail to get id map " << i;
414 return false;
415 }
Dylan Reidc0c28502016-11-04 10:51:30 -0700416 if (!ParseUint32FromDict(*map, "hostID", &new_map.hostID))
Dylan Reid6b590e62016-10-27 19:10:53 -0700417 return false;
Dylan Reidc0c28502016-11-04 10:51:30 -0700418 if (!ParseUint32FromDict(*map, "containerID", &new_map.containerID))
Dylan Reid6b590e62016-10-27 19:10:53 -0700419 return false;
Dylan Reidc0c28502016-11-04 10:51:30 -0700420 if (!ParseUint32FromDict(*map, "size", &new_map.size))
Dylan Reid6b590e62016-10-27 19:10:53 -0700421 return false;
Ben Chanf43980b2017-03-10 11:31:46 -0800422 mappings_out->push_back(new_map);
Dylan Reid6b590e62016-10-27 19:10:53 -0700423 }
424 return true;
425}
426
Dylan Reidb0a72772016-11-03 16:27:50 +0000427// Parses seccomp syscall args.
428bool ParseSeccompArgs(const base::DictionaryValue& syscall_dict,
429 OciSeccompSyscall* syscall_out) {
430 const base::ListValue* args = nullptr;
431 if (syscall_dict.GetList("args", &args)) {
432 for (size_t i = 0; i < args->GetSize(); ++i) {
433 const base::DictionaryValue* args_dict = nullptr;
434 if (!args->GetDictionary(i, &args_dict)) {
435 LOG(ERROR) << "Failed to pars args dict for " << syscall_out->name;
436 return false;
437 }
438 OciSeccompArg this_arg;
439 if (!ParseUint32FromDict(*args_dict, "index", &this_arg.index))
440 return false;
441 if (!ParseUint64FromDict(*args_dict, "value", &this_arg.value))
442 return false;
443 if (!ParseUint64FromDict(*args_dict, "value2", &this_arg.value2))
444 return false;
445 if (!args_dict->GetString("op", &this_arg.op)) {
446 LOG(ERROR) << "Failed to parse op for arg " << this_arg.index
447 << " of " << syscall_out->name;
448 return false;
449 }
450 syscall_out->args.push_back(this_arg);
451 }
452 }
453 return true;
454}
455
456// Parses the seccomp node if it is present.
457bool ParseSeccompInfo(const base::DictionaryValue& seccomp_dict,
458 OciSeccomp* seccomp_out) {
459 if (!seccomp_dict.GetString("defaultAction",
460 &seccomp_out->defaultAction))
461 return false;
462
463 // Gets the list of architectures.
464 const base::ListValue* architectures = nullptr;
465 if (!seccomp_dict.GetList("architectures", &architectures)) {
466 LOG(ERROR) << "Fail to read seccomp architectures";
467 return false;
468 }
469 for (size_t i = 0; i < architectures->GetSize(); ++i) {
470 std::string this_arch;
471 if (!architectures->GetString(i, &this_arch)) {
472 LOG(ERROR) << "Fail to parse seccomp architecture list";
473 return false;
474 }
475 seccomp_out->architectures.push_back(this_arch);
476 }
477
478 // Gets the list of syscalls.
479 const base::ListValue* syscalls = nullptr;
480 if (!seccomp_dict.GetList("syscalls", &syscalls)) {
481 LOG(ERROR) << "Fail to read seccomp syscalls";
482 return false;
483 }
484 for (size_t i = 0; i < syscalls->GetSize(); ++i) {
485 const base::DictionaryValue* syscall_dict = nullptr;
486 if (!syscalls->GetDictionary(i, &syscall_dict)) {
487 LOG(ERROR) << "Fail to parse seccomp syscalls list";
488 return false;
489 }
490 OciSeccompSyscall this_syscall;
491 if (!syscall_dict->GetString("name", &this_syscall.name)) {
492 LOG(ERROR) << "Fail to parse syscall name " << i;
493 return false;
494 }
495 if (!syscall_dict->GetString("action", &this_syscall.action)) {
496 LOG(ERROR) << "Fail to parse syscall action for " << this_syscall.name;
497 return false;
498 }
499 if (!ParseSeccompArgs(*syscall_dict, &this_syscall))
500 return false;
501 seccomp_out->syscalls.push_back(this_syscall);
502 }
503
504 return true;
505}
506
Dylan Reid6b590e62016-10-27 19:10:53 -0700507// Parses the linux node which has information about setting up a user
508// namespace, and the list of devices for the container.
509bool ParseLinuxConfigDict(const base::DictionaryValue& runtime_root_dict,
510 OciConfigPtr const& config_out) {
511 // |linux_dict| is owned by |runtime_root_dict|
512 const base::DictionaryValue* linux_dict = nullptr;
513 if (!runtime_root_dict.GetDictionary("linux", &linux_dict)) {
514 LOG(ERROR) << "Fail to get linux dictionary from the runtime dictionary";
515 return false;
516 }
517
518 // |uid_map_list| is owned by |linux_dict|
519 const base::ListValue* uid_map_list = nullptr;
520 if (!linux_dict->GetList("uidMappings", &uid_map_list)) {
521 LOG(ERROR) << "Fail to get uid mappings list";
522 return false;
523 }
Ben Chanf43980b2017-03-10 11:31:46 -0800524 ParseLinuxIdMappings(uid_map_list, &config_out->linux_config.uidMappings);
Dylan Reid6b590e62016-10-27 19:10:53 -0700525
526 // |gid_map_list| is owned by |linux_dict|
527 const base::ListValue* gid_map_list = nullptr;
528 if (!linux_dict->GetList("gidMappings", &gid_map_list)) {
529 LOG(ERROR) << "Fail to get gid mappings list";
530 return false;
531 }
Ben Chanf43980b2017-03-10 11:31:46 -0800532 ParseLinuxIdMappings(gid_map_list, &config_out->linux_config.gidMappings);
Dylan Reid6b590e62016-10-27 19:10:53 -0700533
534 if (!ParseDeviceList(*linux_dict, config_out))
535 return false;
536
Dylan Reid6985e3b2017-03-31 19:39:16 -0700537 const base::DictionaryValue* resources_dict = nullptr;
538 if (linux_dict->GetDictionary("resources", &resources_dict)) {
539 if (!ParseResources(*resources_dict, &config_out->linux_config.resources))
540 return false;
541 }
542
Dylan Reidb0a72772016-11-03 16:27:50 +0000543 const base::DictionaryValue* seccomp_dict = nullptr;
544 if (linux_dict->GetDictionary("seccomp", &seccomp_dict)) {
545 if (!ParseSeccompInfo(*seccomp_dict, &config_out->linux_config.seccomp))
546 return false;
547 }
548
Dylan Reid6b590e62016-10-27 19:10:53 -0700549 return true;
550}
551
Dylan Reid45e34fe2016-12-02 15:11:53 -0800552bool HostnameValid(const std::string& hostname) {
553 if (hostname.length() > 255)
554 return false;
555
556 const std::regex name("^[0-9a-zA-Z]([0-9a-zA-Z-]*[0-9a-zA-Z])?$");
557 if (!std::regex_match(hostname, name))
558 return false;
559
560 const std::regex double_dash("--");
561 if (std::regex_match(hostname, double_dash))
562 return false;
563
564 return true;
565}
566
Dylan Reid6b590e62016-10-27 19:10:53 -0700567// Parses the configuration file for the container. The config file specifies
568// basic filesystem info and details about the process to be run. namespace,
569// cgroup, and syscall configurations are also specified
570bool ParseConfigDict(const base::DictionaryValue& config_root_dict,
571 OciConfigPtr const& config_out) {
Dylan Reid6d650742016-11-02 23:10:38 -0700572 if (!config_root_dict.GetString("ociVersion", &config_out->ociVersion)) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700573 LOG(ERROR) << "Failed to parse ociVersion";
574 return false;
575 }
576 if (!config_root_dict.GetString("hostname", &config_out->hostname)) {
577 LOG(ERROR) << "Failed to parse hostname";
578 return false;
579 }
Dylan Reid45e34fe2016-12-02 15:11:53 -0800580 if (!HostnameValid(config_out->hostname)) {
581 LOG(ERROR) << "Invalid hostname " << config_out->hostname;
582 return false;
583 }
Dylan Reid6b590e62016-10-27 19:10:53 -0700584
585 // Platform info
586 if (!ParsePlatformConfig(config_root_dict, config_out)) {
587 return false;
588 }
589
590 // Root fs info
591 if (!ParseRootFileSystemConfig(config_root_dict, config_out)) {
592 return false;
593 }
594
595 // Process info
596 if (!ParseProcessConfig(config_root_dict, config_out)) {
597 return false;
598 }
599
600 // Get a list of mount points and mounts.
601 if (!ParseMounts(config_root_dict, config_out)) {
602 LOG(ERROR) << "Failed to parse mounts";
603 return false;
604 }
605
606 // Parse linux node.
607 if (!ParseLinuxConfigDict(config_root_dict, config_out)) {
608 LOG(ERROR) << "Failed to parse the linux node";
609 return false;
610 }
611
612 return true;
613}
614
Ben Chanf43980b2017-03-10 11:31:46 -0800615} // anonymous namespace
Dylan Reid6b590e62016-10-27 19:10:53 -0700616
617bool ParseContainerConfig(const std::string& config_json_data,
618 OciConfigPtr const& config_out) {
Luis Hector Chavezd63f1ba2017-06-28 15:58:12 -0700619 std::string error_msg;
Dylan Reid6b590e62016-10-27 19:10:53 -0700620 std::unique_ptr<const base::Value> config_root_val =
Luis Hector Chavezd63f1ba2017-06-28 15:58:12 -0700621 base::JSONReader::ReadAndReturnError(config_json_data,
622 base::JSON_PARSE_RFC,
623 nullptr /* error_code_out */,
624 &error_msg,
625 nullptr /* error_line_out */,
626 nullptr /* error_column_out */);
Dylan Reid6b590e62016-10-27 19:10:53 -0700627 if (!config_root_val) {
Luis Hector Chavezd63f1ba2017-06-28 15:58:12 -0700628 LOG(ERROR) << "Fail to parse config.json: " << error_msg;
Dylan Reid6b590e62016-10-27 19:10:53 -0700629 return false;
630 }
631 const base::DictionaryValue* config_dict = nullptr;
632 if (!config_root_val->GetAsDictionary(&config_dict)) {
633 LOG(ERROR) << "Fail to parse root dictionary from config.json";
634 return false;
635 }
636 if (!ParseConfigDict(*config_dict, config_out)) {
637 return false;
638 }
639
640 return true;
641}
642
Stephen Barber5f6dc9b2017-04-04 12:36:32 -0700643} // namespace run_oci