blob: 5b13263cac69c5e8fc4792d4f7840f9152514d84 [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
5#include "container_utils/container_config_parser.h"
6
7#include <unistd.h>
8
9#include <string>
10#include <vector>
11
12#include <base/json/json_reader.h>
13#include <base/values.h>
14
15namespace container_utils {
16
17namespace {
18
Dylan Reidc0c28502016-11-04 10:51:30 -070019// Gets a uint32 from the given dictionary.
20bool ParseUint32FromDict(const base::DictionaryValue& dict, const char *name,
21 uint32_t* val_out) {
22 double double_val;
23 if (!dict.GetDouble(name, &double_val)) {
24 LOG(ERROR) << "Failed to get " << name << " uint32_t value from config";
25 return false;
26 }
27 *val_out = double_val;
28 return true;
29}
30
Dylan Reidb0a72772016-11-03 16:27:50 +000031// Gets a uint64 from the given dictionary.
32bool ParseUint64FromDict(const base::DictionaryValue& dict, const char *name,
33 uint64_t* val_out) {
34 double double_val;
35 if (!dict.GetDouble(name, &double_val)) {
36 LOG(ERROR) << "Failed to get " << name << " uid info from config";
37 return false;
38 }
39 *val_out = double_val;
40 return true;
41}
42
Dylan Reid6b590e62016-10-27 19:10:53 -070043// Parses basic platform configuration.
44bool ParsePlatformConfig(const base::DictionaryValue& config_root_dict,
45 OciConfigPtr const& config_out) {
46 // |platform_dict| stays owned by |config_root_dict|
47 const base::DictionaryValue* platform_dict = nullptr;
48 if (!config_root_dict.GetDictionary("platform", &platform_dict)) {
49 LOG(ERROR) << "Fail to parse platform dictionary from config";
50 return false;
51 }
52
53 if (!platform_dict->GetString("os", &config_out->platform.os)) {
54 return false;
55 }
56
57 if (!platform_dict->GetString("arch", &config_out->platform.arch)) {
58 return false;
59 }
60
61 return true;
62}
63
64// Parses root fs info.
65bool ParseRootFileSystemConfig(const base::DictionaryValue& config_root_dict,
66 OciConfigPtr const& config_out) {
67 // |rootfs_dict| stays owned by |config_root_dict|
68 const base::DictionaryValue* rootfs_dict = nullptr;
69 if (!config_root_dict.GetDictionary("root", &rootfs_dict)) {
70 LOG(ERROR) << "Fail to parse rootfs dictionary from config";
71 return false;
72 }
73 if (!rootfs_dict->GetString("path", &config_out->root.path)) {
74 LOG(ERROR) << "Fail to get rootfs path from config";
75 return false;
76 }
Dylan Reid6d650742016-11-02 23:10:38 -070077 rootfs_dict->GetBoolean("readonly", &config_out->root.readonly);
Dylan Reid6b590e62016-10-27 19:10:53 -070078 return true;
79}
80
81// Fills |config_out| with information about the main process to run in the
82// container and the user it should be run as.
83bool ParseProcessConfig(const base::DictionaryValue& config_root_dict,
84 OciConfigPtr const& config_out) {
85 // |process_dict| stays owned by |config_root_dict|
86 const base::DictionaryValue* process_dict = nullptr;
87 if (!config_root_dict.GetDictionary("process", &process_dict)) {
88 LOG(ERROR) << "Fail to get main process from config";
89 return false;
90 }
91 process_dict->GetBoolean("terminal", &config_out->process.terminal);
92 // |user_dict| stays owned by |process_dict|
93 const base::DictionaryValue* user_dict = nullptr;
94 if (!process_dict->GetDictionary("user", &user_dict)) {
95 LOG(ERROR) << "Failed to get user info from config";
96 return false;
97 }
Dylan Reidc0c28502016-11-04 10:51:30 -070098 if (!ParseUint32FromDict(*user_dict, "uid", &config_out->process.user.uid))
Dylan Reid6b590e62016-10-27 19:10:53 -070099 return false;
Dylan Reidc0c28502016-11-04 10:51:30 -0700100 if (!ParseUint32FromDict(*user_dict, "gid", &config_out->process.user.gid))
Dylan Reid6b590e62016-10-27 19:10:53 -0700101 return false;
Dylan Reid6b590e62016-10-27 19:10:53 -0700102 // |args_list| stays owned by |process_dict|
103 const base::ListValue* args_list = nullptr;
104 if (!process_dict->GetList("args", &args_list)) {
105 LOG(ERROR) << "Fail to get main process args from config";
106 return false;
107 }
108 size_t num_args = args_list->GetSize();
109 for (size_t i = 0; i < num_args; ++i) {
110 std::string arg;
111 if (!args_list->GetString(i, &arg)) {
112 LOG(ERROR) << "Fail to get process args from config";
113 return false;
114 }
115 config_out->process.args.push_back(arg);
116 }
117 // |env_list| stays owned by |process_dict|
118 const base::ListValue* env_list = nullptr;
119 if (process_dict->GetList("env", &env_list)) {
120 size_t num_env = env_list->GetSize();
121 for (size_t i = 0; i < num_env; ++i) {
122 std::string env;
123 if (!env_list->GetString(i, &env)) {
124 LOG(ERROR) << "Fail to get process env from config";
125 return false;
126 }
127 config_out->process.env.push_back(env);
128 }
129 }
130 if (!process_dict->GetString("cwd", &config_out->process.cwd)) {
131 LOG(ERROR) << "failed to get cwd of process";
132 return false;
133 }
134
135 return true;
136}
137
138// Parses the 'mounts' field. The necessary mounts for running the container
139// are specified here.
140bool ParseMounts(const base::DictionaryValue& config_root_dict,
141 OciConfigPtr const& config_out) {
142 // |config_mounts_list| stays owned by |config_root_dict|
143 const base::ListValue* config_mounts_list = nullptr;
144 if (!config_root_dict.GetList("mounts", &config_mounts_list)) {
145 LOG(ERROR) << "Fail to get mounts from config dictionary";
146 return false;
147 }
148
149 for (size_t i = 0; i < config_mounts_list->GetSize(); ++i) {
150 const base::DictionaryValue* mount_dict;
151 if (!config_mounts_list->GetDictionary(i, &mount_dict)) {
152 LOG(ERROR) << "Fail to get mount item " << i;
153 return false;
154 }
155 OciMount mount;
156 if (!mount_dict->GetString("destination", &mount.destination)) {
157 LOG(ERROR) << "Fail to get mount path for mount " << i;
158 return false;
159 }
Dylan Reid6d650742016-11-02 23:10:38 -0700160 if (!mount_dict->GetString("type", &mount.type)) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700161 LOG(ERROR) << "Fail to get mount type for mount " << i;
162 return false;
163 }
164 if (!mount_dict->GetString("source", &mount.source)) {
165 LOG(ERROR) << "Fail to get mount source for mount " << i;
166 return false;
167 }
168
169 // |options| are owned by |mount_dict|
170 const base::ListValue* options = nullptr;
171 if (mount_dict->GetList("options", &options)) {
172 for (size_t j = 0; j < options->GetSize(); ++j) {
173 std::string this_opt;
174 if (!options->GetString(j, &this_opt)) {
175 LOG(ERROR) << "Fail to get option " << j << " from mount options";
176 return false;
177 }
178 mount.options.push_back(this_opt);
179 }
180 }
181
182 config_out->mounts.push_back(mount);
183 }
184 return true;
185}
186
187// Parse the list of device nodes that the container needs to run.
188bool ParseDeviceList(const base::DictionaryValue& linux_dict,
189 OciConfigPtr const& config_out) {
190 // |device_list| is owned by |linux_dict|
191 const base::ListValue* device_list = nullptr;
192 if (!linux_dict.GetList("devices", &device_list)) {
193 LOG(ERROR) << "Fail to get device list";
194 return false;
195 }
196 size_t num_devices = device_list->GetSize();
197 for (size_t i = 0; i < num_devices; ++i) {
198 OciLinuxDevice device;
199
200 const base::DictionaryValue* dev;
201 if (!device_list->GetDictionary(i, &dev)) {
202 LOG(ERROR) << "Fail to get device " << i;
203 return false;
204 }
205 std::string path;
206 if (!dev->GetString("path", &device.path)) {
207 LOG(ERROR) << "Fail to get path for dev";
208 return false;
209 }
Dylan Reid6d650742016-11-02 23:10:38 -0700210 if (!dev->GetString("type", &device.type)) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700211 LOG(ERROR) << "Fail to get type for " << device.path;
212 return false;
213 }
Dylan Reidc0c28502016-11-04 10:51:30 -0700214 if (!ParseUint32FromDict(*dev, "major", &device.major))
215 return false;
216 if (!ParseUint32FromDict(*dev, "minor", &device.minor))
217 return false;
218 if (!ParseUint32FromDict(*dev, "fileMode", &device.fileMode))
219 return false;
220 if (!ParseUint32FromDict(*dev, "uid", &device.uid))
221 return false;
222 if (!ParseUint32FromDict(*dev, "gid", &device.gid))
223 return false;
Dylan Reid6b590e62016-10-27 19:10:53 -0700224
225 config_out->linux_config.devices.push_back(device);
226 }
227
228 return true;
229}
230
231// Parses the list of ID mappings and fills |mappings_out| with them.
232bool ParseLinuxIdMappings(const base::ListValue* id_map_list,
233 std::vector<OciLinuxNamespaceMapping>& mappings_out) {
234 for (size_t i = 0; i < id_map_list->GetSize(); ++i) {
235 OciLinuxNamespaceMapping new_map;
236 const base::DictionaryValue* map;
237 if (!id_map_list->GetDictionary(i, &map)) {
238 LOG(ERROR) << "Fail to get id map " << i;
239 return false;
240 }
Dylan Reidc0c28502016-11-04 10:51:30 -0700241 if (!ParseUint32FromDict(*map, "hostID", &new_map.hostID))
Dylan Reid6b590e62016-10-27 19:10:53 -0700242 return false;
Dylan Reidc0c28502016-11-04 10:51:30 -0700243 if (!ParseUint32FromDict(*map, "containerID", &new_map.containerID))
Dylan Reid6b590e62016-10-27 19:10:53 -0700244 return false;
Dylan Reidc0c28502016-11-04 10:51:30 -0700245 if (!ParseUint32FromDict(*map, "size", &new_map.size))
Dylan Reid6b590e62016-10-27 19:10:53 -0700246 return false;
Dylan Reid6b590e62016-10-27 19:10:53 -0700247 mappings_out.push_back(new_map);
248 }
249 return true;
250}
251
Dylan Reidb0a72772016-11-03 16:27:50 +0000252// Parses seccomp syscall args.
253bool ParseSeccompArgs(const base::DictionaryValue& syscall_dict,
254 OciSeccompSyscall* syscall_out) {
255 const base::ListValue* args = nullptr;
256 if (syscall_dict.GetList("args", &args)) {
257 for (size_t i = 0; i < args->GetSize(); ++i) {
258 const base::DictionaryValue* args_dict = nullptr;
259 if (!args->GetDictionary(i, &args_dict)) {
260 LOG(ERROR) << "Failed to pars args dict for " << syscall_out->name;
261 return false;
262 }
263 OciSeccompArg this_arg;
264 if (!ParseUint32FromDict(*args_dict, "index", &this_arg.index))
265 return false;
266 if (!ParseUint64FromDict(*args_dict, "value", &this_arg.value))
267 return false;
268 if (!ParseUint64FromDict(*args_dict, "value2", &this_arg.value2))
269 return false;
270 if (!args_dict->GetString("op", &this_arg.op)) {
271 LOG(ERROR) << "Failed to parse op for arg " << this_arg.index
272 << " of " << syscall_out->name;
273 return false;
274 }
275 syscall_out->args.push_back(this_arg);
276 }
277 }
278 return true;
279}
280
281// Parses the seccomp node if it is present.
282bool ParseSeccompInfo(const base::DictionaryValue& seccomp_dict,
283 OciSeccomp* seccomp_out) {
284 if (!seccomp_dict.GetString("defaultAction",
285 &seccomp_out->defaultAction))
286 return false;
287
288 // Gets the list of architectures.
289 const base::ListValue* architectures = nullptr;
290 if (!seccomp_dict.GetList("architectures", &architectures)) {
291 LOG(ERROR) << "Fail to read seccomp architectures";
292 return false;
293 }
294 for (size_t i = 0; i < architectures->GetSize(); ++i) {
295 std::string this_arch;
296 if (!architectures->GetString(i, &this_arch)) {
297 LOG(ERROR) << "Fail to parse seccomp architecture list";
298 return false;
299 }
300 seccomp_out->architectures.push_back(this_arch);
301 }
302
303 // Gets the list of syscalls.
304 const base::ListValue* syscalls = nullptr;
305 if (!seccomp_dict.GetList("syscalls", &syscalls)) {
306 LOG(ERROR) << "Fail to read seccomp syscalls";
307 return false;
308 }
309 for (size_t i = 0; i < syscalls->GetSize(); ++i) {
310 const base::DictionaryValue* syscall_dict = nullptr;
311 if (!syscalls->GetDictionary(i, &syscall_dict)) {
312 LOG(ERROR) << "Fail to parse seccomp syscalls list";
313 return false;
314 }
315 OciSeccompSyscall this_syscall;
316 if (!syscall_dict->GetString("name", &this_syscall.name)) {
317 LOG(ERROR) << "Fail to parse syscall name " << i;
318 return false;
319 }
320 if (!syscall_dict->GetString("action", &this_syscall.action)) {
321 LOG(ERROR) << "Fail to parse syscall action for " << this_syscall.name;
322 return false;
323 }
324 if (!ParseSeccompArgs(*syscall_dict, &this_syscall))
325 return false;
326 seccomp_out->syscalls.push_back(this_syscall);
327 }
328
329 return true;
330}
331
Dylan Reid6b590e62016-10-27 19:10:53 -0700332// Parses the linux node which has information about setting up a user
333// namespace, and the list of devices for the container.
334bool ParseLinuxConfigDict(const base::DictionaryValue& runtime_root_dict,
335 OciConfigPtr const& config_out) {
336 // |linux_dict| is owned by |runtime_root_dict|
337 const base::DictionaryValue* linux_dict = nullptr;
338 if (!runtime_root_dict.GetDictionary("linux", &linux_dict)) {
339 LOG(ERROR) << "Fail to get linux dictionary from the runtime dictionary";
340 return false;
341 }
342
343 // |uid_map_list| is owned by |linux_dict|
344 const base::ListValue* uid_map_list = nullptr;
345 if (!linux_dict->GetList("uidMappings", &uid_map_list)) {
346 LOG(ERROR) << "Fail to get uid mappings list";
347 return false;
348 }
Dylan Reid6d650742016-11-02 23:10:38 -0700349 ParseLinuxIdMappings(uid_map_list, config_out->linux_config.uidMappings);
Dylan Reid6b590e62016-10-27 19:10:53 -0700350
351 // |gid_map_list| is owned by |linux_dict|
352 const base::ListValue* gid_map_list = nullptr;
353 if (!linux_dict->GetList("gidMappings", &gid_map_list)) {
354 LOG(ERROR) << "Fail to get gid mappings list";
355 return false;
356 }
Dylan Reid6d650742016-11-02 23:10:38 -0700357 ParseLinuxIdMappings(gid_map_list, config_out->linux_config.gidMappings);
Dylan Reid6b590e62016-10-27 19:10:53 -0700358
359 if (!ParseDeviceList(*linux_dict, config_out))
360 return false;
361
Dylan Reidb0a72772016-11-03 16:27:50 +0000362 const base::DictionaryValue* seccomp_dict = nullptr;
363 if (linux_dict->GetDictionary("seccomp", &seccomp_dict)) {
364 if (!ParseSeccompInfo(*seccomp_dict, &config_out->linux_config.seccomp))
365 return false;
366 }
367
Dylan Reid6b590e62016-10-27 19:10:53 -0700368 return true;
369}
370
371// Parses the configuration file for the container. The config file specifies
372// basic filesystem info and details about the process to be run. namespace,
373// cgroup, and syscall configurations are also specified
374bool ParseConfigDict(const base::DictionaryValue& config_root_dict,
375 OciConfigPtr const& config_out) {
Dylan Reid6d650742016-11-02 23:10:38 -0700376 if (!config_root_dict.GetString("ociVersion", &config_out->ociVersion)) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700377 LOG(ERROR) << "Failed to parse ociVersion";
378 return false;
379 }
380 if (!config_root_dict.GetString("hostname", &config_out->hostname)) {
381 LOG(ERROR) << "Failed to parse hostname";
382 return false;
383 }
384
385 // Platform info
386 if (!ParsePlatformConfig(config_root_dict, config_out)) {
387 return false;
388 }
389
390 // Root fs info
391 if (!ParseRootFileSystemConfig(config_root_dict, config_out)) {
392 return false;
393 }
394
395 // Process info
396 if (!ParseProcessConfig(config_root_dict, config_out)) {
397 return false;
398 }
399
400 // Get a list of mount points and mounts.
401 if (!ParseMounts(config_root_dict, config_out)) {
402 LOG(ERROR) << "Failed to parse mounts";
403 return false;
404 }
405
406 // Parse linux node.
407 if (!ParseLinuxConfigDict(config_root_dict, config_out)) {
408 LOG(ERROR) << "Failed to parse the linux node";
409 return false;
410 }
411
412 return true;
413}
414
415} // anonymous namespace
416
417bool ParseContainerConfig(const std::string& config_json_data,
418 OciConfigPtr const& config_out) {
419 std::unique_ptr<const base::Value> config_root_val =
420 base::JSONReader::Read(config_json_data);
421 if (!config_root_val) {
422 LOG(ERROR) << "Fail to parse config.json";
423 return false;
424 }
425 const base::DictionaryValue* config_dict = nullptr;
426 if (!config_root_val->GetAsDictionary(&config_dict)) {
427 LOG(ERROR) << "Fail to parse root dictionary from config.json";
428 return false;
429 }
430 if (!ParseConfigDict(*config_dict, config_out)) {
431 return false;
432 }
433
434 return true;
435}
436
437} // namespace container_utils