LCOV - code coverage report
Current view: top level - nspawn - nspawn-oci.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 0 959 0.0 %
Date: 2019-08-22 15:41:25 Functions: 0 69 0.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <linux/oom.h>
       4             : #if HAVE_SECCOMP
       5             : #include <seccomp.h>
       6             : #endif
       7             : 
       8             : #include "bus-util.h"
       9             : #include "cap-list.h"
      10             : #include "cpu-set-util.h"
      11             : #include "env-util.h"
      12             : #include "format-util.h"
      13             : #include "fs-util.h"
      14             : #include "hostname-util.h"
      15             : #include "json.h"
      16             : #include "missing_sched.h"
      17             : #include "nspawn-oci.h"
      18             : #include "path-util.h"
      19             : #include "rlimit-util.h"
      20             : #if HAVE_SECCOMP
      21             : #include "seccomp-util.h"
      22             : #endif
      23             : #include "stat-util.h"
      24             : #include "stdio-util.h"
      25             : #include "string-util.h"
      26             : #include "strv.h"
      27             : #include "user-util.h"
      28             : 
      29             : /* TODO:
      30             :  * OCI runtime tool implementation
      31             :  * hooks
      32             :  *
      33             :  * Spec issues:
      34             :  *
      35             :  * How is RLIM_INFINITY supposed to be encoded?
      36             :  * configured effective caps is bullshit, as execv() corrupts it anyway
      37             :  * pipes bind mounted is *very* different from pipes newly created, comments regarding bind mount or not are bogus
      38             :  * annotation values structured? or string?
      39             :  * configurable file system namespace path, but then also root path? wtf?
      40             :  * apply sysctl inside of the container? or outside?
      41             :  * how is unlimited pids tasks limit to be encoded?
      42             :  * what are the defaults for caps if not specified?
      43             :  * what are the default uid/gid mappings if one is missing but the other set, or when user ns is on but no namespace configured
      44             :  * the source field of "mounts" is really weird, as it cannot realistically be relative to the bundle, since we never know if that's what the fs wants
      45             :  * spec contradicts itself on the mount "type" field, as the example uses "bind" as type, but it's not listed in /proc/filesystem, and is something made up by /bin/mount
      46             :  * if type of mount is left out, what shall be assumed? "bind"?
      47             :  * readonly mounts is entirely redundant?
      48             :  * should escaping be applied when joining mount options with ","?
      49             :  * devices cgroup support is bogus, "allow" and "deny" on the kernel level is about adding/removing entries, not about access
      50             :  * spec needs to say that "rwm" devices cgroup combination can't be the empty string
      51             :  * cgrouspv1 crap: kernel, kernelTCP, swapiness, disableOOMKiller, swap, devices, leafWeight
      52             :  * general: it shouldn't leak lower level abstractions this obviously
      53             :  * unmanagable cgroups stuff: realtimeRuntime/realtimePeriod
      54             :  * needs to say what happense when some option is not specified, i.e. which defautls apply
      55             :  * no architecture? no personality?
      56             :  * seccomp example and logic is simply broken: there's no constant "SCMP_ACT_ERRNO".
      57             :  * spec should say what to do with unknown props
      58             :  * /bin/mount regarding NFS and FUSE required?
      59             :  * what does terminal=false mean?
      60             :  * sysctl inside or outside? whitelisting?
      61             :  * swapiness typo -> swappiness
      62             :  *
      63             :  * Unsupported:
      64             :  *
      65             :  * apparmorProfile
      66             :  * selinuxLabel + mountLabel
      67             :  * hugepageLimits
      68             :  * network
      69             :  * rdma
      70             :  * intelRdt
      71             :  * swappiness, disableOOMKiller, kernel, kernelTCP, leafWeight (because it's dead, cgroupsv2 can't do it and hence systemd neither)
      72             :  *
      73             :  * Non-slice cgroup paths
      74             :  * Propagation that is not slave + shared
      75             :  * more than one uid/gid mapping, mappings with a container base != 0, or non-matching uid/gid mappings
      76             :  * device cgroups access = false items that are not catchall
      77             :  * device cgroups matches where minor is specified, but major isn't. similar where major is specified but char/block is not. also, any match that only has a type set that has less than "rwm" set. also, any entry that has none of rwm set.
      78             :  *
      79             :  */
      80             : 
      81           0 : static int oci_unexpected(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
      82           0 :         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
      83             :                         "Unexpected OCI element '%s' of type '%s'.", name, json_variant_type_to_string(json_variant_type(v)));
      84             : }
      85             : 
      86           0 : static int oci_unsupported(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
      87           0 :         return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP),
      88             :                         "Unsupported OCI element '%s' of type '%s'.", name, json_variant_type_to_string(json_variant_type(v)));
      89             : }
      90             : 
      91           0 : static int oci_terminal(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
      92           0 :         Settings *s = userdata;
      93             : 
      94             :         /* If not specified, or set to true, we'll default to either an interactive or a read-only
      95             :          * console. If specified as false, we'll forcibly move to "pipe" mode though. */
      96           0 :         s->console_mode = json_variant_boolean(v) ? _CONSOLE_MODE_INVALID : CONSOLE_PIPE;
      97           0 :         return 0;
      98             : }
      99             : 
     100           0 : static int oci_console_dimension(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
     101           0 :         unsigned *u = userdata;
     102             :         uintmax_t k;
     103             : 
     104           0 :         assert(u);
     105             : 
     106           0 :         k = json_variant_unsigned(variant);
     107           0 :         if (k == 0)
     108           0 :                 return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE),
     109             :                                 "Console size field '%s' is too small.", strna(name));
     110           0 :         if (k > USHRT_MAX) /* TIOCSWINSZ's struct winsize uses "unsigned short" for width and height */
     111           0 :                 return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE),
     112             :                                 "Console size field '%s' is too large.", strna(name));
     113             : 
     114           0 :         *u = (unsigned) k;
     115           0 :         return 0;
     116             : }
     117             : 
     118           0 : static int oci_console_size(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     119             : 
     120             :         static const JsonDispatch table[] = {
     121             :                 { "height", JSON_VARIANT_UNSIGNED, oci_console_dimension, offsetof(Settings, console_height), JSON_MANDATORY },
     122             :                 { "width",  JSON_VARIANT_UNSIGNED, oci_console_dimension, offsetof(Settings, console_width),  JSON_MANDATORY },
     123             :                 {}
     124             :         };
     125             : 
     126           0 :         return json_dispatch(v, table, oci_unexpected, flags, userdata);
     127             : }
     128             : 
     129           0 : static int oci_absolute_path(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     130           0 :         char **p = userdata;
     131             :         const char *n;
     132             : 
     133           0 :         assert(p);
     134             : 
     135           0 :         n = json_variant_string(v);
     136             : 
     137           0 :         if (!path_is_absolute(n))
     138           0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     139             :                                 "Path in JSON field '%s' is not absolute: %s", strna(name), n);
     140             : 
     141           0 :         return free_and_strdup_warn(p, n);
     142             : }
     143             : 
     144           0 : static int oci_env(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     145           0 :         char ***l = userdata;
     146             :         JsonVariant *e;
     147             :         int r;
     148             : 
     149           0 :         assert(l);
     150             : 
     151           0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
     152             :                 const char *n;
     153             : 
     154           0 :                 if (!json_variant_is_string(e))
     155           0 :                         return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL),
     156             :                                         "Environment array contains non-string.");
     157             : 
     158           0 :                 assert_se(n = json_variant_string(e));
     159             : 
     160           0 :                 if (!env_assignment_is_valid(n))
     161           0 :                         return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL),
     162             :                                         "Environment assignment not valid: %s", n);
     163             : 
     164           0 :                 r = strv_extend(l, n);
     165           0 :                 if (r < 0)
     166           0 :                         return log_oom();
     167             :         }
     168             : 
     169           0 :         return 0;
     170             : }
     171             : 
     172           0 : static int oci_args(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     173           0 :         _cleanup_strv_free_ char **l = NULL;
     174           0 :         char ***value = userdata;
     175             :         JsonVariant *e;
     176             :         int r;
     177             : 
     178           0 :         assert(value);
     179             : 
     180           0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
     181             :                 const char *n;
     182             : 
     183           0 :                 if (!json_variant_is_string(e))
     184           0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     185             :                                         "Argument is not a string.");
     186             : 
     187           0 :                 assert_se(n = json_variant_string(e));
     188             : 
     189           0 :                 r = strv_extend(&l, n);
     190           0 :                 if (r < 0)
     191           0 :                         return log_oom();
     192             :         }
     193             : 
     194           0 :         if (strv_isempty(l))
     195           0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     196             :                                 "Argument list empty, refusing.");
     197             : 
     198           0 :         if (isempty(l[0]))
     199           0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     200             :                                 "Executable name is empty, refusing.");
     201             : 
     202           0 :         return strv_free_and_replace(*value, l);
     203             : }
     204             : 
     205           0 : static int oci_rlimit_type(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     206             :         const char *z;
     207           0 :         int t, *type = userdata;
     208             : 
     209           0 :         assert_se(type);
     210             : 
     211           0 :         z = startswith(json_variant_string(v), "RLIMIT_");
     212           0 :         if (!z)
     213           0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     214             :                                 "rlimit entry's name does not begin with 'RLIMIT_', refusing: %s",
     215             :                                 json_variant_string(v));
     216             : 
     217           0 :         t = rlimit_from_string(z);
     218           0 :         if (t < 0)
     219           0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     220             :                                 "rlimit name unknown: %s", json_variant_string(v));
     221             : 
     222           0 :         *type = t;
     223           0 :         return 0;
     224             : }
     225             : 
     226           0 : static int oci_rlimit_value(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     227           0 :         rlim_t z, *value = userdata;
     228             : 
     229           0 :         assert(value);
     230             : 
     231           0 :         if (json_variant_is_negative(v))
     232           0 :                 z = RLIM_INFINITY;
     233             :         else {
     234           0 :                 if (!json_variant_is_unsigned(v))
     235           0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
     236             :                                         "rlimits limit not unsigned, refusing.");
     237             : 
     238           0 :                 z = (rlim_t) json_variant_unsigned(v);
     239             : 
     240           0 :                 if ((uintmax_t) z != json_variant_unsigned(v))
     241           0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     242             :                                         "rlimits limit out of range, refusing.");
     243             :         }
     244             : 
     245           0 :         *value = z;
     246           0 :         return 0;
     247             : }
     248             : 
     249           0 : static int oci_rlimits(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     250             : 
     251           0 :         Settings *s = userdata;
     252             :         JsonVariant *e;
     253             :         int r;
     254             : 
     255           0 :         assert(s);
     256             : 
     257           0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
     258             : 
     259             :                 struct rlimit_data {
     260             :                         int type;
     261             :                         rlim_t soft;
     262             :                         rlim_t hard;
     263           0 :                 } data = {
     264             :                         .type = -1,
     265             :                         .soft = RLIM_INFINITY,
     266             :                         .hard = RLIM_INFINITY,
     267             :                 };
     268             : 
     269             :                 static const JsonDispatch table[] = {
     270             :                         { "soft", JSON_VARIANT_NUMBER, oci_rlimit_value, offsetof(struct rlimit_data, soft), JSON_MANDATORY },
     271             :                         { "hard", JSON_VARIANT_NUMBER, oci_rlimit_value, offsetof(struct rlimit_data, hard), JSON_MANDATORY },
     272             :                         { "type", JSON_VARIANT_STRING, oci_rlimit_type,  offsetof(struct rlimit_data, type), JSON_MANDATORY },
     273             :                         {}
     274             :                 };
     275             : 
     276           0 :                 r = json_dispatch(e, table, oci_unexpected, flags, &data);
     277           0 :                 if (r < 0)
     278           0 :                         return r;
     279             : 
     280           0 :                 assert(data.type >= 0);
     281           0 :                 assert(data.type < _RLIMIT_MAX);
     282             : 
     283           0 :                 if (s->rlimit[data.type])
     284           0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     285             :                                         "rlimits array contains duplicate entry, refusing.");
     286             : 
     287           0 :                 s->rlimit[data.type] = new(struct rlimit, 1);
     288           0 :                 if (!s->rlimit[data.type])
     289           0 :                         return log_oom();
     290             : 
     291           0 :                 *s->rlimit[data.type] = (struct rlimit) {
     292           0 :                         .rlim_cur = data.soft,
     293           0 :                         .rlim_max = data.hard,
     294             :                 };
     295             : 
     296             :         }
     297           0 :         return 0;
     298             : }
     299             : 
     300           0 : static int oci_capability_array(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     301           0 :         uint64_t *mask = userdata, m = 0;
     302             :         JsonVariant *e;
     303             : 
     304           0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
     305             :                 const char *n;
     306             :                 int cap;
     307             : 
     308           0 :                 if (!json_variant_is_string(e))
     309           0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     310             :                                         "Entry in capabilities array is not a string.");
     311             : 
     312           0 :                 assert_se(n = json_variant_string(e));
     313             : 
     314           0 :                 cap = capability_from_name(n);
     315           0 :                 if (cap < 0)
     316           0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     317             :                                         "Unknown capability: %s", n);
     318             : 
     319           0 :                 m |= UINT64_C(1) << cap;
     320             :         }
     321             : 
     322           0 :         if (*mask == (uint64_t) -1)
     323           0 :                 *mask = m;
     324             :         else
     325           0 :                 *mask |= m;
     326             : 
     327           0 :         return 0;
     328             : }
     329             : 
     330           0 : static int oci_capabilities(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     331             : 
     332             :         static const JsonDispatch table[] = {
     333             :                 { "effective",   JSON_VARIANT_ARRAY, oci_capability_array, offsetof(CapabilityQuintet, effective)   },
     334             :                 { "bounding",    JSON_VARIANT_ARRAY, oci_capability_array, offsetof(CapabilityQuintet, bounding)    },
     335             :                 { "inheritable", JSON_VARIANT_ARRAY, oci_capability_array, offsetof(CapabilityQuintet, inheritable) },
     336             :                 { "permitted",   JSON_VARIANT_ARRAY, oci_capability_array, offsetof(CapabilityQuintet, permitted)   },
     337             :                 { "ambient",     JSON_VARIANT_ARRAY, oci_capability_array, offsetof(CapabilityQuintet, ambient)     },
     338             :                 {}
     339             :         };
     340             : 
     341           0 :         Settings *s = userdata;
     342             :         int r;
     343             : 
     344           0 :         assert(s);
     345             : 
     346           0 :         r = json_dispatch(v, table, oci_unexpected, flags, &s->full_capabilities);
     347           0 :         if (r < 0)
     348           0 :                 return r;
     349             : 
     350           0 :         if (s->full_capabilities.bounding != (uint64_t) -1) {
     351           0 :                 s->capability = s->full_capabilities.bounding;
     352           0 :                 s->drop_capability = ~s->full_capabilities.bounding;
     353             :         }
     354             : 
     355           0 :         return 0;
     356             : }
     357             : 
     358           0 : static int oci_oom_score_adj(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     359           0 :         Settings *s = userdata;
     360             :         intmax_t k;
     361             : 
     362           0 :         assert(s);
     363             : 
     364           0 :         k = json_variant_integer(v);
     365           0 :         if (k < OOM_SCORE_ADJ_MIN || k > OOM_SCORE_ADJ_MAX)
     366           0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     367             :                                 "oomScoreAdj value out of range: %ji", k);
     368             : 
     369           0 :         s->oom_score_adjust = (int) k;
     370           0 :         s->oom_score_adjust_set = true;
     371             : 
     372           0 :         return 0;
     373             : }
     374             : 
     375           0 : static int oci_uid_gid(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     376           0 :         uid_t *uid = userdata, u;
     377             :         uintmax_t k;
     378             : 
     379           0 :         assert(uid);
     380             :         assert_cc(sizeof(uid_t) == sizeof(gid_t));
     381             : 
     382           0 :         k = json_variant_unsigned(v);
     383           0 :         u = (uid_t) k;
     384           0 :         if ((uintmax_t) u != k)
     385           0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     386             :                                 "UID/GID out of range: %ji", k);
     387             : 
     388           0 :         if (!uid_is_valid(u))
     389           0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     390             :                                 "UID/GID is not valid: " UID_FMT, u);
     391             : 
     392           0 :         *uid = u;
     393           0 :         return 0;
     394             : }
     395             : 
     396           0 : static int oci_supplementary_gids(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     397           0 :         Settings *s = userdata;
     398             :         JsonVariant *e;
     399             :         int r;
     400             : 
     401           0 :         assert(s);
     402             : 
     403           0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
     404             :                 gid_t gid, *a;
     405             : 
     406           0 :                 if (!json_variant_is_unsigned(e))
     407           0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     408             :                                         "Supplementary GID entry is not a UID.");
     409             : 
     410           0 :                 r = oci_uid_gid(name, e, flags, &gid);
     411           0 :                 if (r < 0)
     412           0 :                         return r;
     413             : 
     414           0 :                 a = reallocarray(s->supplementary_gids, s->n_supplementary_gids + 1, sizeof(gid_t));
     415           0 :                 if (!a)
     416           0 :                         return log_oom();
     417             : 
     418           0 :                 s->supplementary_gids = a;
     419           0 :                 s->supplementary_gids[s->n_supplementary_gids++] = gid;
     420             :         }
     421             : 
     422           0 :         return 0;
     423             : }
     424             : 
     425           0 : static int oci_user(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     426             :         static const JsonDispatch table[] = {
     427             :                 { "uid",            JSON_VARIANT_UNSIGNED, oci_uid_gid,            offsetof(Settings, uid), JSON_MANDATORY },
     428             :                 { "gid",            JSON_VARIANT_UNSIGNED, oci_uid_gid,            offsetof(Settings, gid), JSON_MANDATORY },
     429             :                 { "additionalGids", JSON_VARIANT_ARRAY,    oci_supplementary_gids, 0,                       0              },
     430             :                 {}
     431             :         };
     432             : 
     433           0 :         return json_dispatch(v, table, oci_unexpected, flags, userdata);
     434             : }
     435             : 
     436           0 : static int oci_process(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     437             : 
     438             :         static const JsonDispatch table[] = {
     439             :                 { "terminal",        JSON_VARIANT_BOOLEAN, oci_terminal,          0,                                     0               },
     440             :                 { "consoleSize",     JSON_VARIANT_OBJECT,  oci_console_size,      0,                                     0               },
     441             :                 { "cwd",             JSON_VARIANT_STRING,  oci_absolute_path,     offsetof(Settings, working_directory), 0               },
     442             :                 { "env",             JSON_VARIANT_ARRAY,   oci_env,               offsetof(Settings, environment),       0               },
     443             :                 { "args",            JSON_VARIANT_ARRAY,   oci_args,              offsetof(Settings, parameters),        0               },
     444             :                 { "rlimits",         JSON_VARIANT_ARRAY,   oci_rlimits,           0,                                     0               },
     445             :                 { "apparmorProfile", JSON_VARIANT_STRING,  oci_unsupported,       0,                                     JSON_PERMISSIVE },
     446             :                 { "capabilities",    JSON_VARIANT_OBJECT,  oci_capabilities,      0,                                     0               },
     447             :                 { "noNewPrivileges", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(Settings, no_new_privileges), 0               },
     448             :                 { "oomScoreAdj",     JSON_VARIANT_INTEGER, oci_oom_score_adj,     0,                                     0               },
     449             :                 { "selinuxLabel",    JSON_VARIANT_STRING,  oci_unsupported,       0,                                     JSON_PERMISSIVE },
     450             :                 { "user",            JSON_VARIANT_OBJECT,  oci_user,              0,                                     0               },
     451             :                 {}
     452             :         };
     453             : 
     454           0 :         return json_dispatch(v, table, oci_unexpected, flags, userdata);
     455             : }
     456             : 
     457           0 : static int oci_root(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     458             : 
     459             :         static const JsonDispatch table[] = {
     460             :                 { "path",     JSON_VARIANT_STRING,  json_dispatch_string,  offsetof(Settings, root)      },
     461             :                 { "readonly", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(Settings, read_only) },
     462             :                 {}
     463             :         };
     464             : 
     465           0 :         return json_dispatch(v, table, oci_unexpected, flags, userdata);
     466             : }
     467             : 
     468           0 : static int oci_hostname(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     469           0 :         Settings *s = userdata;
     470             :         const char *n;
     471             : 
     472           0 :         assert(s);
     473             : 
     474           0 :         assert_se(n = json_variant_string(v));
     475             : 
     476           0 :         if (!hostname_is_valid(n, false))
     477           0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     478             :                                 "Hostname string is not a valid hostname: %s", n);
     479             : 
     480           0 :         return free_and_strdup_warn(&s->hostname, n);
     481             : }
     482             : 
     483           0 : static bool oci_exclude_mount(const char *path) {
     484             : 
     485             :         /* Returns "true" for all mounts we insist to mount on our own, and hence ignore the OCI data. */
     486             : 
     487           0 :         if (PATH_IN_SET(path,
     488             :                         "/dev",
     489             :                         "/dev/mqueue",
     490             :                         "/dev/pts",
     491             :                         "/dev/shm",
     492             :                         "/proc",
     493             :                         "/proc/acpi",
     494             :                         "/proc/apm",
     495             :                         "/proc/asound",
     496             :                         "/proc/bus",
     497             :                         "/proc/fs",
     498             :                         "/proc/irq",
     499             :                         "/proc/kallsyms",
     500             :                         "/proc/kcore",
     501             :                         "/proc/keys",
     502             :                         "/proc/scsi",
     503             :                         "/proc/sys",
     504             :                         "/proc/sys/net",
     505             :                         "/proc/sysrq-trigger",
     506             :                         "/proc/timer_list",
     507             :                         "/run",
     508             :                         "/sys",
     509             :                         "/sys",
     510             :                         "/sys/fs/selinux",
     511             :                         "/tmp"))
     512           0 :                 return true;
     513             : 
     514             :         /* Similar, skip the whole /sys/fs/cgroups subtree */
     515           0 :         if (path_startswith(path, "/sys/fs/cgroup"))
     516           0 :                 return true;
     517             : 
     518           0 :         return false;
     519             : }
     520             : 
     521             : typedef struct oci_mount_data {
     522             :         char *destination;
     523             :         char *source;
     524             :         char *type;
     525             :         char **options;
     526             : } oci_mount_data;
     527             : 
     528           0 : static void cleanup_oci_mount_data(oci_mount_data *data) {
     529           0 :         free(data->destination);
     530           0 :         free(data->source);
     531           0 :         strv_free(data->options);
     532           0 :         free(data->type);
     533           0 : }
     534             : 
     535           0 : static int oci_mounts(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     536           0 :         Settings *s = userdata;
     537             :         JsonVariant *e;
     538             :         int r;
     539             : 
     540           0 :         assert(s);
     541             : 
     542           0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
     543             :                 static const JsonDispatch table[] = {
     544             :                         { "destination", JSON_VARIANT_STRING, oci_absolute_path,    offsetof(oci_mount_data, destination), JSON_MANDATORY },
     545             :                         { "source",      JSON_VARIANT_STRING, json_dispatch_string, offsetof(oci_mount_data, source),      0              },
     546             :                         { "options",     JSON_VARIANT_ARRAY,  json_dispatch_strv,   offsetof(oci_mount_data, options),     0,             },
     547             :                         { "type",        JSON_VARIANT_STRING, json_dispatch_string, offsetof(oci_mount_data, type),        0              },
     548             :                         {}
     549             :                 };
     550             : 
     551           0 :                 _cleanup_free_ char *joined_options = NULL;
     552             :                 CustomMount *m;
     553           0 :                 _cleanup_(cleanup_oci_mount_data) oci_mount_data data = {};
     554             : 
     555           0 :                 r = json_dispatch(e, table, oci_unexpected, flags, &data);
     556           0 :                 if (r < 0)
     557           0 :                         return r;
     558             : 
     559           0 :                 if (!path_is_absolute(data.destination))
     560           0 :                         return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL),
     561             :                                         "Mount destination not an absolute path: %s", data.destination);
     562             : 
     563           0 :                 if (oci_exclude_mount(data.destination))
     564           0 :                         continue;
     565             : 
     566           0 :                 if (data.options) {
     567           0 :                         joined_options = strv_join(data.options, ",");
     568           0 :                         if (!joined_options)
     569           0 :                                 return log_oom();
     570             :                 }
     571             : 
     572           0 :                 if (!data.type || streq(data.type, "bind")) {
     573           0 :                         if (data.source && !path_is_absolute(data.source)) {
     574             :                                 char *joined;
     575             : 
     576           0 :                                 joined = path_join(s->bundle, data.source);
     577           0 :                                 if (!joined)
     578           0 :                                         return log_oom();
     579             : 
     580           0 :                                 free_and_replace(data.source, joined);
     581             :                         }
     582             : 
     583           0 :                         data.type = mfree(data.type);
     584             : 
     585           0 :                         m = custom_mount_add(&s->custom_mounts, &s->n_custom_mounts, CUSTOM_MOUNT_BIND);
     586             :                 } else
     587           0 :                         m = custom_mount_add(&s->custom_mounts, &s->n_custom_mounts, CUSTOM_MOUNT_ARBITRARY);
     588           0 :                 if (!m)
     589           0 :                         return log_oom();
     590             : 
     591           0 :                 m->destination = TAKE_PTR(data.destination);
     592           0 :                 m->source = TAKE_PTR(data.source);
     593           0 :                 m->options = TAKE_PTR(joined_options);
     594           0 :                 m->type_argument = TAKE_PTR(data.type);
     595             :         }
     596             : 
     597           0 :         return 0;
     598             : }
     599             : 
     600           0 : static int oci_namespace_type(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     601           0 :         unsigned long *nsflags = userdata;
     602             :         const char *n;
     603             : 
     604           0 :         assert(nsflags);
     605           0 :         assert_se(n = json_variant_string(v));
     606             : 
     607             :         /* We don't use namespace_flags_from_string() here, as the OCI spec uses slightly different names than the
     608             :          * kernel here. */
     609           0 :         if (streq(n, "pid"))
     610           0 :                 *nsflags = CLONE_NEWPID;
     611           0 :         else if (streq(n, "network"))
     612           0 :                 *nsflags = CLONE_NEWNET;
     613           0 :         else if (streq(n, "mount"))
     614           0 :                 *nsflags = CLONE_NEWNS;
     615           0 :         else if (streq(n, "ipc"))
     616           0 :                 *nsflags = CLONE_NEWIPC;
     617           0 :         else if (streq(n, "uts"))
     618           0 :                 *nsflags = CLONE_NEWUTS;
     619           0 :         else if (streq(n, "user"))
     620           0 :                 *nsflags = CLONE_NEWUSER;
     621           0 :         else if (streq(n, "cgroup"))
     622           0 :                 *nsflags = CLONE_NEWCGROUP;
     623             :         else
     624           0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     625             :                                 "Unknown cgroup type, refusing: %s", n);
     626             : 
     627           0 :         return 0;
     628             : }
     629             : 
     630           0 : static int oci_namespaces(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     631           0 :         Settings *s = userdata;
     632           0 :         unsigned long n = 0;
     633             :         JsonVariant *e;
     634             :         int r;
     635             : 
     636           0 :         assert_se(s);
     637             : 
     638           0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
     639             : 
     640             :                 struct namespace_data {
     641             :                         unsigned long type;
     642             :                         char *path;
     643           0 :                 } data = {};
     644             : 
     645             :                 static const JsonDispatch table[] = {
     646             :                         { "type", JSON_VARIANT_STRING, oci_namespace_type, offsetof(struct namespace_data, type), JSON_MANDATORY },
     647             :                         { "path", JSON_VARIANT_STRING, oci_absolute_path,  offsetof(struct namespace_data, path), 0              },
     648             :                         {}
     649             :                 };
     650             : 
     651           0 :                 r = json_dispatch(e, table, oci_unexpected, flags, &data);
     652           0 :                 if (r < 0) {
     653           0 :                         free(data.path);
     654           0 :                         return r;
     655             :                 }
     656             : 
     657           0 :                 if (data.path) {
     658           0 :                         if (data.type != CLONE_NEWNET) {
     659           0 :                                 free(data.path);
     660           0 :                                 return json_log(e, flags, SYNTHETIC_ERRNO(EOPNOTSUPP),
     661             :                                                 "Specifying namespace path for non-network namespace is not supported.");
     662             :                         }
     663             : 
     664           0 :                         if (s->network_namespace_path) {
     665           0 :                                 free(data.path);
     666           0 :                                 return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL),
     667             :                                                 "Network namespace path specified more than once, refusing.");
     668             :                         }
     669             : 
     670           0 :                         free(s->network_namespace_path);
     671           0 :                         s->network_namespace_path = data.path;
     672             :                 }
     673             : 
     674           0 :                 if (FLAGS_SET(n, data.type)) {
     675           0 :                         return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL),
     676             :                                         "Duplicate namespace specification, refusing.");
     677             :                 }
     678             : 
     679           0 :                 n |= data.type;
     680             :         }
     681             : 
     682           0 :         if (!FLAGS_SET(n, CLONE_NEWNS))
     683           0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP),
     684             :                                 "Containers without file system namespace aren't supported.");
     685             : 
     686           0 :         s->private_network = FLAGS_SET(n, CLONE_NEWNET);
     687           0 :         s->userns_mode = FLAGS_SET(n, CLONE_NEWUSER) ? USER_NAMESPACE_FIXED : USER_NAMESPACE_NO;
     688           0 :         s->use_cgns = FLAGS_SET(n, CLONE_NEWCGROUP);
     689             : 
     690           0 :         s->clone_ns_flags = n & (CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS);
     691             : 
     692           0 :         return 0;
     693             : }
     694             : 
     695           0 : static int oci_uid_gid_range(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     696           0 :         uid_t *uid = userdata, u;
     697             :         uintmax_t k;
     698             : 
     699           0 :         assert(uid);
     700             :         assert_cc(sizeof(uid_t) == sizeof(gid_t));
     701             : 
     702             :         /* This is very much like oci_uid_gid(), except the checks are a bit different, as this is a UID range rather
     703             :          * than a specific UID, and hence (uid_t) -1 has no special significance. OTOH a range of zero makes no
     704             :          * sense. */
     705             : 
     706           0 :         k = json_variant_unsigned(v);
     707           0 :         u = (uid_t) k;
     708           0 :         if ((uintmax_t) u != k)
     709           0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
     710             :                                 "UID/GID out of range: %ji", k);
     711           0 :         if (u == 0)
     712           0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
     713             :                                 "UID/GID range can't be zero.");
     714             : 
     715           0 :         *uid = u;
     716           0 :         return 0;
     717             : }
     718             : 
     719           0 : static int oci_uid_gid_mappings(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     720             :         struct mapping_data {
     721             :                 uid_t host_id;
     722             :                 uid_t container_id;
     723             :                 uid_t range;
     724           0 :         } data = {
     725             :                 .host_id = UID_INVALID,
     726             :                 .container_id = UID_INVALID,
     727             :                 .range = 0,
     728             :         };
     729             : 
     730             :         static const JsonDispatch table[] = {
     731             :                 { "containerID", JSON_VARIANT_UNSIGNED, oci_uid_gid,       offsetof(struct mapping_data, container_id), JSON_MANDATORY },
     732             :                 { "hostID",      JSON_VARIANT_UNSIGNED, oci_uid_gid,       offsetof(struct mapping_data, host_id),      JSON_MANDATORY },
     733             :                 { "size",        JSON_VARIANT_UNSIGNED, oci_uid_gid_range, offsetof(struct mapping_data, range),        JSON_MANDATORY },
     734             :                 {}
     735             :         };
     736             : 
     737           0 :         Settings *s = userdata;
     738             :         JsonVariant *e;
     739             :         int r;
     740             : 
     741           0 :         assert(s);
     742             : 
     743           0 :         if (json_variant_elements(v) == 0)
     744           0 :                 return 0;
     745             : 
     746           0 :         if (json_variant_elements(v) > 1)
     747           0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP),
     748             :                                 "UID/GID mappings with more than one entry are not supported.");
     749             : 
     750           0 :         assert_se(e = json_variant_by_index(v, 0));
     751             : 
     752           0 :         r = json_dispatch(e, table, oci_unexpected, flags, &data);
     753           0 :         if (r < 0)
     754           0 :                 return r;
     755             : 
     756           0 :         if (data.host_id + data.range < data.host_id ||
     757           0 :             data.container_id + data.range < data.container_id)
     758           0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     759             :                                 "UID/GID range goes beyond UID/GID validity range, refusing.");
     760             : 
     761           0 :         if (data.container_id != 0)
     762           0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP),
     763             :                                 "UID/GID mappings with a non-zero container base are not supported.");
     764             : 
     765           0 :         if (data.range < 0x10000)
     766           0 :                 json_log(v, flags|JSON_WARNING, 0,
     767             :                          "UID/GID mapping with less than 65536 UID/GIDS set up, you are looking for trouble.");
     768             : 
     769           0 :         if (s->uid_range != UID_INVALID &&
     770           0 :             (s->uid_shift != data.host_id || s->uid_range != data.range))
     771           0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP),
     772             :                                 "Non-matching UID and GID mappings are not supported.");
     773             : 
     774           0 :         s->uid_shift = data.host_id;
     775           0 :         s->uid_range = data.range;
     776             : 
     777           0 :         return 0;
     778             : }
     779             : 
     780           0 : static int oci_device_type(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     781           0 :         mode_t *mode = userdata;
     782             :         const char *t;
     783             : 
     784           0 :         assert(mode);
     785           0 :         assert_se(t = json_variant_string(v));
     786             : 
     787           0 :         if (STR_IN_SET(t, "c", "u"))
     788           0 :                 *mode = (*mode & ~S_IFMT) | S_IFCHR;
     789           0 :         else if (streq(t, "b"))
     790           0 :                 *mode = (*mode & ~S_IFMT) | S_IFBLK;
     791           0 :         else if (streq(t, "p"))
     792           0 :                 *mode = (*mode & ~S_IFMT) | S_IFIFO;
     793             :         else
     794           0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     795             :                                 "Unknown device type: %s", t);
     796             : 
     797           0 :         return 0;
     798             : }
     799             : 
     800           0 : static int oci_device_major(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     801           0 :         unsigned *u = userdata;
     802             :         uintmax_t k;
     803             : 
     804           0 :         assert_se(u);
     805             : 
     806           0 :         k = json_variant_unsigned(v);
     807           0 :         if (!DEVICE_MAJOR_VALID(k))
     808           0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
     809             :                                 "Device major %ji out of range.", k);
     810             : 
     811           0 :         *u = (unsigned) k;
     812           0 :         return 0;
     813             : }
     814             : 
     815           0 : static int oci_device_minor(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     816           0 :         unsigned *u = userdata;
     817             :         uintmax_t k;
     818             : 
     819           0 :         assert_se(u);
     820             : 
     821           0 :         k = json_variant_unsigned(v);
     822           0 :         if (!DEVICE_MINOR_VALID(k))
     823           0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
     824             :                                 "Device minor %ji out of range.", k);
     825             : 
     826           0 :         *u = (unsigned) k;
     827           0 :         return 0;
     828             : }
     829             : 
     830           0 : static int oci_device_file_mode(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     831           0 :         mode_t *mode = userdata, m;
     832             :         uintmax_t k;
     833             : 
     834           0 :         assert(mode);
     835             : 
     836           0 :         k = json_variant_unsigned(v);
     837           0 :         m = (mode_t) k;
     838             : 
     839           0 :         if ((m & ~07777) != 0 || (uintmax_t) m != k)
     840           0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
     841             :                                 "fileMode out of range, refusing.");
     842             : 
     843           0 :         *mode = m;
     844           0 :         return 0;
     845             : }
     846             : 
     847           0 : static int oci_devices(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     848           0 :         Settings *s = userdata;
     849             :         JsonVariant *e;
     850             :         int r;
     851             : 
     852           0 :         assert(s);
     853             : 
     854           0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
     855             : 
     856             :                 static const JsonDispatch table[] = {
     857             :                         { "type",     JSON_VARIANT_STRING,   oci_device_type,      offsetof(DeviceNode, mode),  JSON_MANDATORY },
     858             :                         { "path",     JSON_VARIANT_STRING,   oci_absolute_path,    offsetof(DeviceNode, path),  JSON_MANDATORY },
     859             :                         { "major",    JSON_VARIANT_UNSIGNED, oci_device_major,     offsetof(DeviceNode, major), 0              },
     860             :                         { "minor",    JSON_VARIANT_UNSIGNED, oci_device_minor,     offsetof(DeviceNode, minor), 0              },
     861             :                         { "fileMode", JSON_VARIANT_UNSIGNED, oci_device_file_mode, offsetof(DeviceNode, mode),  0              },
     862             :                         { "uid",      JSON_VARIANT_UNSIGNED, oci_uid_gid,          offsetof(DeviceNode, uid),   0              },
     863             :                         { "gid",      JSON_VARIANT_UNSIGNED, oci_uid_gid,          offsetof(DeviceNode, gid),   0              },
     864             :                         {}
     865             :                 };
     866             : 
     867             :                 DeviceNode *node, *nodes;
     868             : 
     869           0 :                 nodes = reallocarray(s->extra_nodes, s->n_extra_nodes + 1, sizeof(DeviceNode));
     870           0 :                 if (!nodes)
     871           0 :                         return log_oom();
     872             : 
     873           0 :                 s->extra_nodes = nodes;
     874             : 
     875           0 :                 node = nodes + s->n_extra_nodes;
     876           0 :                 *node = (DeviceNode) {
     877             :                         .uid = UID_INVALID,
     878             :                         .gid = GID_INVALID,
     879             :                         .major = (unsigned) -1,
     880             :                         .minor = (unsigned) -1,
     881             :                         .mode = 0644,
     882             :                 };
     883             : 
     884           0 :                 r = json_dispatch(e, table, oci_unexpected, flags, node);
     885           0 :                 if (r < 0)
     886           0 :                         goto fail_element;
     887             : 
     888           0 :                 if (S_ISCHR(node->mode) || S_ISBLK(node->mode)) {
     889           0 :                         _cleanup_free_ char *path = NULL;
     890             : 
     891           0 :                         if (node->major == (unsigned) -1 || node->minor == (unsigned) -1) {
     892           0 :                                 r = json_log(e, flags, SYNTHETIC_ERRNO(EINVAL),
     893             :                                              "Major/minor required when device node is device node");
     894           0 :                                 goto fail_element;
     895             :                         }
     896             : 
     897             :                         /* Suppress a couple of implicit device nodes */
     898           0 :                         r = device_path_make_canonical(node->mode, makedev(node->major, node->minor), &path);
     899           0 :                         if (r < 0)
     900           0 :                                 json_log(e, flags|JSON_DEBUG, 0, "Failed to resolve device node %u:%u, ignoring: %m", node->major, node->minor);
     901             :                         else {
     902           0 :                                 if (PATH_IN_SET(path,
     903             :                                                 "/dev/null",
     904             :                                                 "/dev/zero",
     905             :                                                 "/dev/full",
     906             :                                                 "/dev/random",
     907             :                                                 "/dev/urandom",
     908             :                                                 "/dev/tty",
     909             :                                                 "/dev/net/tun",
     910             :                                                 "/dev/ptmx",
     911             :                                                 "/dev/pts/ptmx",
     912             :                                                 "/dev/console")) {
     913             : 
     914           0 :                                         json_log(e, flags|JSON_DEBUG, 0, "Ignoring devices item for device '%s', as it is implicitly created anyway.", path);
     915           0 :                                         free(node->path);
     916           0 :                                         continue;
     917             :                                 }
     918             :                         }
     919             :                 }
     920             : 
     921           0 :                 s->n_extra_nodes++;
     922           0 :                 continue;
     923             : 
     924           0 :         fail_element:
     925           0 :                 free(node->path);
     926           0 :                 return r;
     927             :         }
     928             : 
     929           0 :         return 0;
     930             : }
     931             : 
     932           0 : static int oci_cgroups_path(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     933           0 :         _cleanup_free_ char *slice = NULL, *backwards = NULL;
     934           0 :         Settings *s = userdata;
     935             :         const char *p;
     936             :         int r;
     937             : 
     938           0 :         assert(s);
     939             : 
     940           0 :         assert_se(p = json_variant_string(v));
     941             : 
     942           0 :         r = cg_path_get_slice(p, &slice);
     943           0 :         if (r < 0)
     944           0 :                 return json_log(v, flags, r, "Couldn't derive slice unit name from path '%s': %m", p);
     945             : 
     946           0 :         r = cg_slice_to_path(slice, &backwards);
     947           0 :         if (r < 0)
     948           0 :                 return json_log(v, flags, r, "Couldn't convert slice unit name '%s' back to path: %m", slice);
     949             : 
     950           0 :         if (!path_equal(backwards, p))
     951           0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     952             :                                 "Control group path '%s' does not refer to slice unit, refusing.", p);
     953             : 
     954           0 :         free_and_replace(s->slice, slice);
     955           0 :         return 0;
     956             : }
     957             : 
     958           0 : static int oci_cgroup_device_type(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     959           0 :         mode_t *mode = userdata;
     960             :         const char *n;
     961             : 
     962           0 :         assert_se(n = json_variant_string(v));
     963             : 
     964           0 :         if (streq(n, "c"))
     965           0 :                 *mode = S_IFCHR;
     966           0 :         else if (streq(n, "b"))
     967           0 :                 *mode = S_IFBLK;
     968             :         else
     969           0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
     970             :                                 "Control group device type unknown: %s", n);
     971             : 
     972           0 :         return 0;
     973             : }
     974             : 
     975             : struct device_data {
     976             :         bool allow;
     977             :         bool r;
     978             :         bool w;
     979             :         bool m;
     980             :         mode_t type;
     981             :         unsigned major;
     982             :         unsigned minor;
     983             : };
     984             : 
     985           0 : static int oci_cgroup_device_access(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
     986           0 :         struct device_data *d = userdata;
     987           0 :         bool r = false, w = false, m = false;
     988             :         const char *s;
     989             :         size_t i;
     990             : 
     991           0 :         assert_se(s = json_variant_string(v));
     992             : 
     993           0 :         for (i = 0; s[i]; i++)
     994           0 :                 if (s[i] == 'r')
     995           0 :                         r = true;
     996           0 :                 else if (s[i] == 'w')
     997           0 :                         w = true;
     998           0 :                 else if (s[i] == 'm')
     999           0 :                         m = true;
    1000             :                 else
    1001           0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
    1002             :                                         "Unknown device access character '%c'.", s[i]);
    1003             : 
    1004           0 :         d->r = r;
    1005           0 :         d->w = w;
    1006           0 :         d->m = m;
    1007             : 
    1008           0 :         return 0;
    1009             : }
    1010             : 
    1011           0 : static int oci_cgroup_devices(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1012             : 
    1013           0 :         _cleanup_free_ struct device_data *list = NULL;
    1014           0 :         Settings *s = userdata;
    1015           0 :         size_t n_list = 0, i;
    1016           0 :         bool noop = false;
    1017             :         JsonVariant *e;
    1018             :         int r;
    1019             : 
    1020           0 :         assert(s);
    1021             : 
    1022           0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
    1023             : 
    1024           0 :                 struct device_data data = {
    1025             :                         .major = (unsigned) -1,
    1026             :                         .minor = (unsigned) -1,
    1027             :                 }, *a;
    1028             : 
    1029             :                 static const JsonDispatch table[] = {
    1030             :                         { "allow",  JSON_VARIANT_BOOLEAN,  json_dispatch_boolean,    offsetof(struct device_data, allow), JSON_MANDATORY },
    1031             :                         { "type",   JSON_VARIANT_STRING,   oci_cgroup_device_type,   offsetof(struct device_data, type),  0              },
    1032             :                         { "major",  JSON_VARIANT_UNSIGNED, oci_device_major,         offsetof(struct device_data, major), 0              },
    1033             :                         { "minor",  JSON_VARIANT_UNSIGNED, oci_device_minor,         offsetof(struct device_data, minor), 0              },
    1034             :                         { "access", JSON_VARIANT_STRING,   oci_cgroup_device_access, 0,                                   0              },
    1035             :                         {}
    1036             :                 };
    1037             : 
    1038           0 :                 r = json_dispatch(e, table, oci_unexpected, flags, &data);
    1039           0 :                 if (r < 0)
    1040           0 :                         return r;
    1041             : 
    1042           0 :                 if (!data.allow) {
    1043             :                         /* The fact that OCI allows 'deny' entries makes really no sense, as 'allow' vs. 'deny' for the
    1044             :                          * devices cgroup controller is really not about whitelisting and blacklisting but about adding
    1045             :                          * and removing entries from the whitelist. Since we always start out with an empty whitelist
    1046             :                          * we hence ignore the whole thing, as removing entries which don't exist make no sense. We'll
    1047             :                          * log about this, since this is really borked in the spec, with one exception: the entry
    1048             :                          * that's supposed to drop the kernel's default we ignore silently */
    1049             : 
    1050           0 :                         if (!data.r || !data.w || !data.m || data.type != 0 || data.major != (unsigned) -1 || data.minor != (unsigned) -1)
    1051           0 :                                 json_log(v, flags|JSON_WARNING, 0, "Devices cgroup whitelist with arbitrary 'allow' entries not supported, ignoring.");
    1052             : 
    1053             :                         /* We ignore the 'deny' entry as for us that's implied */
    1054           0 :                         continue;
    1055             :                 }
    1056             : 
    1057           0 :                 if (!data.r && !data.w && !data.m) {
    1058           0 :                         json_log(v, flags|LOG_WARNING, 0, "Device cgroup whitelist entry with no effect found, ignoring.");
    1059           0 :                         continue;
    1060             :                 }
    1061             : 
    1062           0 :                 if (data.minor != (unsigned) -1 && data.major == (unsigned) -1)
    1063           0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP),
    1064             :                                         "Device cgroup whitelist entries with minors but no majors not supported.");
    1065             : 
    1066           0 :                 if (data.major != (unsigned) -1 && data.type == 0)
    1067           0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP),
    1068             :                                         "Device cgroup whitelist entries with majors but no device node type not supported.");
    1069             : 
    1070           0 :                 if (data.type == 0) {
    1071           0 :                         if (data.r && data.w && data.m) /* a catchall whitelist entry means we are looking at a noop */
    1072           0 :                                 noop = true;
    1073             :                         else
    1074           0 :                                 return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP),
    1075             :                                                 "Device cgroup whitelist entries with no type not supported.");
    1076             :                 }
    1077             : 
    1078           0 :                 a = reallocarray(list, n_list + 1, sizeof(struct device_data));
    1079           0 :                 if (!a)
    1080           0 :                         return log_oom();
    1081             : 
    1082           0 :                 list = a;
    1083           0 :                 list[n_list++] = data;
    1084             :         }
    1085             : 
    1086           0 :         if (noop)
    1087           0 :                 return 0;
    1088             : 
    1089           0 :         r = settings_allocate_properties(s);
    1090           0 :         if (r < 0)
    1091           0 :                 return r;
    1092             : 
    1093           0 :         r = sd_bus_message_open_container(s->properties, 'r', "sv");
    1094           0 :         if (r < 0)
    1095           0 :                 return bus_log_create_error(r);
    1096             : 
    1097           0 :         r = sd_bus_message_append(s->properties, "s", "DeviceAllow");
    1098           0 :         if (r < 0)
    1099           0 :                 return bus_log_create_error(r);
    1100             : 
    1101           0 :         r = sd_bus_message_open_container(s->properties, 'v', "a(ss)");
    1102           0 :         if (r < 0)
    1103           0 :                 return bus_log_create_error(r);
    1104             : 
    1105           0 :         r = sd_bus_message_open_container(s->properties, 'a', "(ss)");
    1106           0 :         if (r < 0)
    1107           0 :                 return bus_log_create_error(r);
    1108             : 
    1109           0 :         for (i = 0; i < n_list; i++) {
    1110           0 :                 _cleanup_free_ char *pattern = NULL;
    1111             :                 char access[4];
    1112           0 :                 size_t n = 0;
    1113             : 
    1114           0 :                 if (list[i].minor == (unsigned) -1) {
    1115             :                         const char *t;
    1116             : 
    1117           0 :                         if (list[i].type == S_IFBLK)
    1118           0 :                                 t = "block";
    1119             :                         else {
    1120           0 :                                 assert(list[i].type == S_IFCHR);
    1121           0 :                                 t = "char";
    1122             :                         }
    1123             : 
    1124           0 :                         if (list[i].major == (unsigned) -1) {
    1125           0 :                                 pattern = strjoin(t, "-*");
    1126           0 :                                 if (!pattern)
    1127           0 :                                         return log_oom();
    1128             :                         } else {
    1129           0 :                                 if (asprintf(&pattern, "%s-%u", t, list[i].major) < 0)
    1130           0 :                                         return log_oom();
    1131             :                         }
    1132             : 
    1133             :                 } else {
    1134           0 :                         assert(list[i].major != (unsigned) -1); /* If a minor is specified, then a major also needs to be specified */
    1135             : 
    1136           0 :                         r = device_path_make_major_minor(list[i].type, makedev(list[i].major, list[i].minor), &pattern);
    1137           0 :                         if (r < 0)
    1138           0 :                                 return log_oom();
    1139             :                 }
    1140             : 
    1141           0 :                 if (list[i].r)
    1142           0 :                         access[n++] = 'r';
    1143           0 :                 if (list[i].w)
    1144           0 :                         access[n++] = 'w';
    1145           0 :                 if (list[i].m)
    1146           0 :                         access[n++] = 'm';
    1147           0 :                 access[n] = 0;
    1148             : 
    1149           0 :                 assert(n > 0);
    1150             : 
    1151           0 :                 r = sd_bus_message_append(s->properties, "(ss)", pattern, access);
    1152           0 :                 if (r < 0)
    1153           0 :                         return bus_log_create_error(r);
    1154             :         }
    1155             : 
    1156           0 :         r = sd_bus_message_close_container(s->properties);
    1157           0 :         if (r < 0)
    1158           0 :                 return bus_log_create_error(r);
    1159             : 
    1160           0 :         r = sd_bus_message_close_container(s->properties);
    1161           0 :         if (r < 0)
    1162           0 :                 return bus_log_create_error(r);
    1163             : 
    1164           0 :         r = sd_bus_message_close_container(s->properties);
    1165           0 :         if (r < 0)
    1166           0 :                 return bus_log_create_error(r);
    1167             : 
    1168           0 :         return 0;
    1169             : }
    1170             : 
    1171           0 : static int oci_cgroup_memory_limit(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1172           0 :         uint64_t *m = userdata;
    1173             :         uintmax_t k;
    1174             : 
    1175           0 :         assert(m);
    1176             : 
    1177           0 :         if (json_variant_is_negative(v)) {
    1178           0 :                 *m = UINT64_MAX;
    1179           0 :                 return 0;
    1180             :         }
    1181             : 
    1182           0 :         if (!json_variant_is_unsigned(v))
    1183           0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
    1184             :                                 "Memory limit is not an unsigned integer");
    1185             : 
    1186           0 :         k = json_variant_unsigned(v);
    1187           0 :         if (k >= UINT64_MAX)
    1188           0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
    1189             :                                 "Memory limit too large: %ji", k);
    1190             : 
    1191           0 :         *m = (uint64_t) k;
    1192           0 :         return 0;
    1193             : }
    1194             : 
    1195           0 : static int oci_cgroup_memory(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1196             : 
    1197             :         struct memory_data {
    1198             :                 uint64_t limit;
    1199             :                 uint64_t reservation;
    1200             :                 uint64_t swap;
    1201           0 :         } data = {
    1202             :                 .limit = UINT64_MAX,
    1203             :                 .reservation = UINT64_MAX,
    1204             :                 .swap = UINT64_MAX,
    1205             :         };
    1206             : 
    1207             :         static const JsonDispatch table[] = {
    1208             :                 { "limit",            JSON_VARIANT_NUMBER, oci_cgroup_memory_limit, offsetof(struct memory_data, limit),       0               },
    1209             :                 { "reservation",      JSON_VARIANT_NUMBER, oci_cgroup_memory_limit, offsetof(struct memory_data, reservation), 0               },
    1210             :                 { "swap",             JSON_VARIANT_NUMBER, oci_cgroup_memory_limit, offsetof(struct memory_data, swap),        0               },
    1211             :                 { "kernel",           JSON_VARIANT_NUMBER, oci_unsupported,         0,                                         JSON_PERMISSIVE },
    1212             :                 { "kernelTCP",        JSON_VARIANT_NUMBER, oci_unsupported,         0,                                         JSON_PERMISSIVE },
    1213             :                 { "swapiness",        JSON_VARIANT_NUMBER, oci_unsupported,         0,                                         JSON_PERMISSIVE },
    1214             :                 { "disableOOMKiller", JSON_VARIANT_NUMBER, oci_unsupported,         0,                                         JSON_PERMISSIVE },
    1215             :                 {}
    1216             :         };
    1217             : 
    1218           0 :         Settings *s = userdata;
    1219             :         int r;
    1220             : 
    1221           0 :         r = json_dispatch(v, table, oci_unexpected, flags, &data);
    1222           0 :         if (r < 0)
    1223           0 :                 return r;
    1224             : 
    1225           0 :         if (data.swap != UINT64_MAX) {
    1226           0 :                 if (data.limit == UINT64_MAX)
    1227           0 :                         json_log(v, flags|LOG_WARNING, 0, "swap limit without memory limit is not supported, ignoring.");
    1228           0 :                 else if (data.swap < data.limit)
    1229           0 :                         json_log(v, flags|LOG_WARNING, 0, "swap limit is below memory limit, ignoring.");
    1230             :                 else {
    1231           0 :                         r = settings_allocate_properties(s);
    1232           0 :                         if (r < 0)
    1233           0 :                                 return r;
    1234             : 
    1235           0 :                         r = sd_bus_message_append(s->properties, "(sv)", "MemorySwapMax", "t", data.swap - data.limit);
    1236           0 :                         if (r < 0)
    1237           0 :                                 return bus_log_create_error(r);
    1238             :                 }
    1239             :         }
    1240             : 
    1241           0 :         if (data.limit != UINT64_MAX) {
    1242           0 :                 r = settings_allocate_properties(s);
    1243           0 :                 if (r < 0)
    1244           0 :                         return r;
    1245             : 
    1246           0 :                 r = sd_bus_message_append(s->properties, "(sv)", "MemoryMax", "t", data.limit);
    1247           0 :                 if (r < 0)
    1248           0 :                         return bus_log_create_error(r);
    1249             :         }
    1250             : 
    1251           0 :         if (data.reservation != UINT64_MAX) {
    1252           0 :                 r = settings_allocate_properties(s);
    1253           0 :                 if (r < 0)
    1254           0 :                         return r;
    1255             : 
    1256           0 :                 r = sd_bus_message_append(s->properties, "(sv)", "MemoryLow", "t", data.reservation);
    1257           0 :                 if (r < 0)
    1258           0 :                         return bus_log_create_error(r);
    1259             :         }
    1260             : 
    1261           0 :         return 0;
    1262             : }
    1263             : 
    1264             : struct cpu_data {
    1265             :         uint64_t shares;
    1266             :         uint64_t quota;
    1267             :         uint64_t period;
    1268             :         CPUSet cpu_set;
    1269             : };
    1270             : 
    1271           0 : static int oci_cgroup_cpu_shares(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1272           0 :         uint64_t *u = userdata;
    1273             :         uintmax_t k;
    1274             : 
    1275           0 :         assert(u);
    1276             : 
    1277           0 :         k = json_variant_unsigned(v);
    1278           0 :         if (k < CGROUP_CPU_SHARES_MIN || k > CGROUP_CPU_SHARES_MAX)
    1279           0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
    1280             :                                 "shares value out of range.");
    1281             : 
    1282           0 :         *u = (uint64_t) k;
    1283           0 :         return 0;
    1284             : }
    1285             : 
    1286           0 : static int oci_cgroup_cpu_quota(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1287           0 :         uint64_t *u = userdata;
    1288             :         uintmax_t k;
    1289             : 
    1290           0 :         assert(u);
    1291             : 
    1292           0 :         k = json_variant_unsigned(v);
    1293           0 :         if (k <= 0 || k >= UINT64_MAX)
    1294           0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
    1295             :                                 "period/quota value out of range.");
    1296             : 
    1297           0 :         *u = (uint64_t) k;
    1298           0 :         return 0;
    1299             : }
    1300             : 
    1301           0 : static int oci_cgroup_cpu_cpus(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1302           0 :         struct cpu_data *data = userdata;
    1303             :         CPUSet set;
    1304             :         const char *n;
    1305             :         int r;
    1306             : 
    1307           0 :         assert(data);
    1308             : 
    1309           0 :         assert_se(n = json_variant_string(v));
    1310             : 
    1311           0 :         r = parse_cpu_set(n, &set);
    1312           0 :         if (r < 0)
    1313           0 :                 return json_log(v, flags, r, "Failed to parse CPU set specification: %s", n);
    1314             : 
    1315           0 :         cpu_set_reset(&data->cpu_set);
    1316           0 :         data->cpu_set = set;
    1317             : 
    1318           0 :         return 0;
    1319             : }
    1320             : 
    1321           0 : static int oci_cgroup_cpu(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1322             : 
    1323             :         static const JsonDispatch table[] = {
    1324             :                 { "shares",          JSON_VARIANT_UNSIGNED, oci_cgroup_cpu_shares, offsetof(struct cpu_data, shares), 0 },
    1325             :                 { "quota",           JSON_VARIANT_UNSIGNED, oci_cgroup_cpu_quota,  offsetof(struct cpu_data, quota),  0 },
    1326             :                 { "period",          JSON_VARIANT_UNSIGNED, oci_cgroup_cpu_quota,  offsetof(struct cpu_data, period), 0 },
    1327             :                 { "realtimeRuntime", JSON_VARIANT_UNSIGNED, oci_unsupported,       0,                                 0 },
    1328             :                 { "realtimePeriod",  JSON_VARIANT_UNSIGNED, oci_unsupported,       0,                                 0 },
    1329             :                 { "cpus",            JSON_VARIANT_STRING,   oci_cgroup_cpu_cpus,   0,                                 0 },
    1330             :                 { "mems",            JSON_VARIANT_STRING,   oci_unsupported,       0,                                 0 },
    1331             :                 {}
    1332             :         };
    1333             : 
    1334           0 :         struct cpu_data data = {
    1335             :                 .shares = UINT64_MAX,
    1336             :                 .quota = UINT64_MAX,
    1337             :                 .period = UINT64_MAX,
    1338             :         };
    1339             : 
    1340           0 :         Settings *s = userdata;
    1341             :         int r;
    1342             : 
    1343           0 :         r = json_dispatch(v, table, oci_unexpected, flags, &data);
    1344           0 :         if (r < 0) {
    1345           0 :                 cpu_set_reset(&data.cpu_set);
    1346           0 :                 return r;
    1347             :         }
    1348             : 
    1349           0 :         cpu_set_reset(&s->cpu_set);
    1350           0 :         s->cpu_set = data.cpu_set;
    1351             : 
    1352           0 :         if (data.shares != UINT64_MAX) {
    1353           0 :                 r = settings_allocate_properties(s);
    1354           0 :                 if (r < 0)
    1355           0 :                         return r;
    1356             : 
    1357           0 :                 r = sd_bus_message_append(s->properties, "(sv)", "CPUShares", "t", data.shares);
    1358           0 :                 if (r < 0)
    1359           0 :                         return bus_log_create_error(r);
    1360             :         }
    1361             : 
    1362           0 :         if (data.quota != UINT64_MAX && data.period != UINT64_MAX) {
    1363           0 :                 r = settings_allocate_properties(s);
    1364           0 :                 if (r < 0)
    1365           0 :                         return r;
    1366             : 
    1367           0 :                 r = sd_bus_message_append(s->properties, "(sv)", "CPUQuotaPerSecUSec", "t", (uint64_t) (data.quota * USEC_PER_SEC / data.period));
    1368           0 :                 if (r < 0)
    1369           0 :                         return bus_log_create_error(r);
    1370             : 
    1371           0 :         } else if ((data.quota != UINT64_MAX) != (data.period != UINT64_MAX))
    1372           0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
    1373             :                                 "CPU quota and period not used together.");
    1374             : 
    1375           0 :         return 0;
    1376             : }
    1377             : 
    1378           0 : static int oci_cgroup_block_io_weight(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1379           0 :         Settings *s = userdata;
    1380             :         uintmax_t k;
    1381             :         int r;
    1382             : 
    1383           0 :         assert(s);
    1384             : 
    1385           0 :         k = json_variant_unsigned(v);
    1386           0 :         if (k < CGROUP_BLKIO_WEIGHT_MIN || k > CGROUP_BLKIO_WEIGHT_MAX)
    1387           0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
    1388             :                                 "Block I/O weight out of range.");
    1389             : 
    1390           0 :         r = settings_allocate_properties(s);
    1391           0 :         if (r < 0)
    1392           0 :                 return r;
    1393             : 
    1394           0 :         r = sd_bus_message_append(s->properties, "(sv)", "BlockIOWeight", "t", (uint64_t) k);
    1395           0 :         if (r < 0)
    1396           0 :                 return bus_log_create_error(r);
    1397             : 
    1398           0 :         return 0;
    1399             : }
    1400             : 
    1401           0 : static int oci_cgroup_block_io_weight_device(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1402           0 :         Settings *s = userdata;
    1403             :         JsonVariant *e;
    1404             :         int r;
    1405             : 
    1406           0 :         assert(s);
    1407             : 
    1408           0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
    1409             :                 struct device_data {
    1410             :                         unsigned major;
    1411             :                         unsigned minor;
    1412             :                         uintmax_t weight;
    1413           0 :                 } data = {
    1414             :                         .major = (unsigned) -1,
    1415             :                         .minor = (unsigned) -1,
    1416             :                         .weight = UINTMAX_MAX,
    1417             :                 };
    1418             : 
    1419             :                 static const JsonDispatch table[] =  {
    1420             :                         { "major",      JSON_VARIANT_UNSIGNED, oci_device_major,       offsetof(struct device_data, major),  JSON_MANDATORY  },
    1421             :                         { "minor",      JSON_VARIANT_UNSIGNED, oci_device_minor,       offsetof(struct device_data, minor),  JSON_MANDATORY  },
    1422             :                         { "weight",     JSON_VARIANT_UNSIGNED, json_dispatch_unsigned, offsetof(struct device_data, weight), 0               },
    1423             :                         { "leafWeight", JSON_VARIANT_INTEGER,  oci_unsupported,        0,                                    JSON_PERMISSIVE },
    1424             :                         {}
    1425             :                 };
    1426             : 
    1427           0 :                 _cleanup_free_ char *path = NULL;
    1428             : 
    1429           0 :                 r = json_dispatch(e, table, oci_unexpected, flags, &data);
    1430           0 :                 if (r < 0)
    1431           0 :                         return r;
    1432             : 
    1433           0 :                 if (data.weight == UINTMAX_MAX)
    1434           0 :                         continue;
    1435             : 
    1436           0 :                 if (data.weight < CGROUP_BLKIO_WEIGHT_MIN || data.weight > CGROUP_BLKIO_WEIGHT_MAX)
    1437           0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
    1438             :                                         "Block I/O device weight out of range.");
    1439             : 
    1440           0 :                 r = device_path_make_major_minor(S_IFBLK, makedev(data.major, data.minor), &path);
    1441           0 :                 if (r < 0)
    1442           0 :                         return json_log(v, flags, r, "Failed to build device path: %m");
    1443             : 
    1444           0 :                 r = settings_allocate_properties(s);
    1445           0 :                 if (r < 0)
    1446           0 :                         return r;
    1447             : 
    1448           0 :                 r = sd_bus_message_append(s->properties, "(sv)", "BlockIODeviceWeight", "a(st)", 1, path, (uint64_t) data.weight);
    1449           0 :                 if (r < 0)
    1450           0 :                         return bus_log_create_error(r);
    1451             :         }
    1452             : 
    1453           0 :         return 0;
    1454             : }
    1455             : 
    1456           0 : static int oci_cgroup_block_io_throttle(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1457           0 :         Settings *s = userdata;
    1458             :         const char *pname;
    1459             :         JsonVariant *e;
    1460             :         int r;
    1461             : 
    1462           0 :         assert(s);
    1463             : 
    1464           0 :         pname = streq(name, "throttleReadBpsDevice")  ? "IOReadBandwidthMax" :
    1465           0 :                 streq(name, "throttleWriteBpsDevice") ? "IOWriteBandwidthMax" :
    1466           0 :                 streq(name, "throttleReadIOPSDevice") ? "IOReadIOPSMax" :
    1467             :                                                         "IOWriteIOPSMax";
    1468             : 
    1469           0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
    1470             :                 struct device_data {
    1471             :                         unsigned major;
    1472             :                         unsigned minor;
    1473             :                         uintmax_t rate;
    1474           0 :                 } data = {
    1475             :                         .major = (unsigned) -1,
    1476             :                         .minor = (unsigned) -1,
    1477             :                 };
    1478             : 
    1479             :                 static const JsonDispatch table[] = {
    1480             :                         { "major", JSON_VARIANT_UNSIGNED, oci_device_major,       offsetof(struct device_data, major), JSON_MANDATORY },
    1481             :                         { "minor", JSON_VARIANT_UNSIGNED, oci_device_minor,       offsetof(struct device_data, minor), JSON_MANDATORY },
    1482             :                         { "rate",  JSON_VARIANT_UNSIGNED, json_dispatch_unsigned, offsetof(struct device_data, rate),  JSON_MANDATORY },
    1483             :                         {}
    1484             :                 };
    1485             : 
    1486           0 :                 _cleanup_free_ char *path = NULL;
    1487             : 
    1488           0 :                 r = json_dispatch(e, table, oci_unexpected, flags, &data);
    1489           0 :                 if (r < 0)
    1490           0 :                         return r;
    1491             : 
    1492           0 :                 if (data.rate >= UINT64_MAX)
    1493           0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
    1494             :                                         "Block I/O device rate out of range.");
    1495             : 
    1496           0 :                 r = device_path_make_major_minor(S_IFBLK, makedev(data.major, data.minor), &path);
    1497           0 :                 if (r < 0)
    1498           0 :                         return json_log(v, flags, r, "Failed to build device path: %m");
    1499             : 
    1500           0 :                 r = settings_allocate_properties(s);
    1501           0 :                 if (r < 0)
    1502           0 :                         return r;
    1503             : 
    1504           0 :                 r = sd_bus_message_append(s->properties, "(sv)", pname, "a(st)", 1, path, (uint64_t) data.rate);
    1505           0 :                 if (r < 0)
    1506           0 :                         return bus_log_create_error(r);
    1507             :         }
    1508             : 
    1509           0 :         return 0;
    1510             : }
    1511             : 
    1512           0 : static int oci_cgroup_block_io(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1513             : 
    1514             :         static const JsonDispatch table[] = {
    1515             :                 { "weight",                  JSON_VARIANT_UNSIGNED, oci_cgroup_block_io_weight,        0, 0               },
    1516             :                 { "leafWeight",              JSON_VARIANT_UNSIGNED, oci_unsupported,                   0, JSON_PERMISSIVE },
    1517             :                 { "weightDevice",            JSON_VARIANT_ARRAY,    oci_cgroup_block_io_weight_device, 0, 0               },
    1518             :                 { "throttleReadBpsDevice",   JSON_VARIANT_ARRAY,    oci_cgroup_block_io_throttle,      0, 0               },
    1519             :                 { "throttleWriteBpsDevice",  JSON_VARIANT_ARRAY,    oci_cgroup_block_io_throttle,      0, 0               },
    1520             :                 { "throttleReadIOPSDevice",  JSON_VARIANT_ARRAY,    oci_cgroup_block_io_throttle,      0, 0               },
    1521             :                 { "throttleWriteIOPSDevice", JSON_VARIANT_ARRAY,    oci_cgroup_block_io_throttle,      0, 0               },
    1522             :                 {}
    1523             :         };
    1524             : 
    1525           0 :         return json_dispatch(v, table, oci_unexpected, flags, userdata);
    1526             : }
    1527             : 
    1528           0 : static int oci_cgroup_pids(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1529             : 
    1530             :         static const JsonDispatch table[] = {
    1531             :                 { "limit", JSON_VARIANT_NUMBER, json_dispatch_variant, 0, JSON_MANDATORY },
    1532             :                 {}
    1533             :         };
    1534             : 
    1535           0 :         _cleanup_(json_variant_unrefp) JsonVariant *k = NULL;
    1536           0 :         Settings *s = userdata;
    1537             :         uint64_t m;
    1538             :         int r;
    1539             : 
    1540           0 :         assert(s);
    1541             : 
    1542           0 :         r = json_dispatch(v, table, oci_unexpected, flags, &k);
    1543           0 :         if (r < 0)
    1544           0 :                 return r;
    1545             : 
    1546           0 :         if (json_variant_is_negative(k))
    1547           0 :                 m = UINT64_MAX;
    1548             :         else {
    1549           0 :                 if (!json_variant_is_unsigned(k))
    1550           0 :                         return json_log(k, flags, SYNTHETIC_ERRNO(EINVAL),
    1551             :                                         "pids limit not unsigned integer, refusing.");
    1552             : 
    1553           0 :                 m = (uint64_t) json_variant_unsigned(k);
    1554             : 
    1555           0 :                 if ((uintmax_t) m != json_variant_unsigned(k))
    1556           0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
    1557             :                                         "pids limit out of range, refusing.");
    1558             :         }
    1559             : 
    1560           0 :         r = settings_allocate_properties(s);
    1561           0 :         if (r < 0)
    1562           0 :                 return r;
    1563             : 
    1564           0 :         r = sd_bus_message_append(s->properties, "(sv)", "TasksMax", "t", m);
    1565           0 :         if (r < 0)
    1566           0 :                 return bus_log_create_error(r);
    1567             : 
    1568           0 :         return 0;
    1569             : }
    1570             : 
    1571           0 : static int oci_resources(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1572             : 
    1573             :         static const JsonDispatch table[] = {
    1574             :                 { "devices",        JSON_VARIANT_ARRAY,  oci_cgroup_devices,  0, 0 },
    1575             :                 { "memory",         JSON_VARIANT_OBJECT, oci_cgroup_memory,   0, 0 },
    1576             :                 { "cpu",            JSON_VARIANT_OBJECT, oci_cgroup_cpu,      0, 0 },
    1577             :                 { "blockIO",        JSON_VARIANT_OBJECT, oci_cgroup_block_io, 0, 0 },
    1578             :                 { "hugepageLimits", JSON_VARIANT_ARRAY,  oci_unsupported,     0, 0 },
    1579             :                 { "network",        JSON_VARIANT_OBJECT, oci_unsupported,     0, 0 },
    1580             :                 { "pids",           JSON_VARIANT_OBJECT, oci_cgroup_pids,     0, 0 },
    1581             :                 { "rdma",           JSON_VARIANT_OBJECT, oci_unsupported,     0, 0 },
    1582             :                 {}
    1583             :         };
    1584             : 
    1585           0 :         return json_dispatch(v, table, oci_unexpected, flags, userdata);
    1586             : }
    1587             : 
    1588           0 : static bool sysctl_key_valid(const char *s) {
    1589           0 :         bool dot = true;
    1590             : 
    1591             :         /* Note that we are a bit stricter here than in systemd-sysctl, as that inherited semantics from the old sysctl
    1592             :          * tool, which were really weird (as it swaps / and . in both ways) */
    1593             : 
    1594           0 :         if (isempty(s))
    1595           0 :                 return false;
    1596             : 
    1597           0 :         for (; *s; s++) {
    1598             : 
    1599           0 :                 if (*s <= ' ' || *s >= 127)
    1600           0 :                         return false;
    1601           0 :                 if (*s == '/')
    1602           0 :                         return false;
    1603           0 :                 if (*s == '.') {
    1604             : 
    1605           0 :                         if (dot) /* Don't allow two dots next to each other (or at the beginning) */
    1606           0 :                                 return false;
    1607             : 
    1608           0 :                         dot = true;
    1609             :                 } else
    1610           0 :                         dot = false;
    1611             :         }
    1612             : 
    1613           0 :         if (dot) /* don't allow a dot at the end */
    1614           0 :                 return false;
    1615             : 
    1616           0 :         return true;
    1617             : }
    1618             : 
    1619           0 : static int oci_sysctl(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1620           0 :         Settings *s = userdata;
    1621             :         JsonVariant *w;
    1622             :         const char *k;
    1623             :         int r;
    1624             : 
    1625           0 :         assert(s);
    1626             : 
    1627           0 :         JSON_VARIANT_OBJECT_FOREACH(k, w, v) {
    1628             :                 const char *m;
    1629             : 
    1630           0 :                 if (!json_variant_is_string(w))
    1631           0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
    1632             :                                         "sysctl parameter is not a string, refusing.");
    1633             : 
    1634           0 :                 assert_se(m = json_variant_string(w));
    1635             : 
    1636           0 :                 if (sysctl_key_valid(k))
    1637           0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
    1638             :                                         "sysctl key invalid, refusing: %s", k);
    1639             : 
    1640           0 :                 r = strv_extend_strv(&s->sysctl, STRV_MAKE(k, m), false);
    1641           0 :                 if (r < 0)
    1642           0 :                         return log_oom();
    1643             :         }
    1644             : 
    1645           0 :         return 0;
    1646             : }
    1647             : 
    1648             : #if HAVE_SECCOMP
    1649           0 : static int oci_seccomp_action_from_string(const char *name, uint32_t *ret) {
    1650             : 
    1651             :         static const struct {
    1652             :                 const char *name;
    1653             :                 uint32_t action;
    1654             :         } table[] = {
    1655             :                 { "SCMP_ACT_ALLOW",         SCMP_ACT_ALLOW        },
    1656             :                 { "SCMP_ACT_ERRNO",         SCMP_ACT_ERRNO(EPERM) }, /* the OCI spec doesn't document the error, but it appears EPERM is supposed to be used */
    1657             :                 { "SCMP_ACT_KILL",          SCMP_ACT_KILL         },
    1658             : #ifdef SCMP_ACT_KILL_PROCESS
    1659             :                 { "SCMP_ACT_KILL_PROCESS",  SCMP_ACT_KILL_PROCESS },
    1660             : #endif
    1661             : #ifdef SCMP_ACT_KILL_THREAD
    1662             :                 { "SCMP_ACT_KILL_THREAD",   SCMP_ACT_KILL_THREAD  },
    1663             : #endif
    1664             : #ifdef SCMP_ACT_LOG
    1665             :                 { "SCMP_ACT_LOG",           SCMP_ACT_LOG          },
    1666             : #endif
    1667             :                 { "SCMP_ACT_TRAP",          SCMP_ACT_TRAP         },
    1668             : 
    1669             :                 /* We don't support SCMP_ACT_TRACE because that requires a tracer, and that doesn't really make sense
    1670             :                  * here */
    1671             :         };
    1672             : 
    1673             :         size_t i;
    1674             : 
    1675           0 :         for (i = 0; i < ELEMENTSOF(table); i++)
    1676           0 :                 if (streq_ptr(name, table[i].name)) {
    1677           0 :                         *ret = table[i].action;
    1678           0 :                         return 0;
    1679             :                 }
    1680             : 
    1681           0 :         return -EINVAL;
    1682             : }
    1683             : 
    1684           0 : static int oci_seccomp_arch_from_string(const char *name, uint32_t *ret) {
    1685             : 
    1686             :         static const struct {
    1687             :                 const char *name;
    1688             :                 uint32_t arch;
    1689             :         } table[] = {
    1690             :                 { "SCMP_ARCH_AARCH64",     SCMP_ARCH_AARCH64     },
    1691             :                 { "SCMP_ARCH_ARM",         SCMP_ARCH_ARM         },
    1692             :                 { "SCMP_ARCH_MIPS",        SCMP_ARCH_MIPS        },
    1693             :                 { "SCMP_ARCH_MIPS64",      SCMP_ARCH_MIPS64      },
    1694             :                 { "SCMP_ARCH_MIPS64N32",   SCMP_ARCH_MIPS64N32   },
    1695             :                 { "SCMP_ARCH_MIPSEL",      SCMP_ARCH_MIPSEL      },
    1696             :                 { "SCMP_ARCH_MIPSEL64",    SCMP_ARCH_MIPSEL64    },
    1697             :                 { "SCMP_ARCH_MIPSEL64N32", SCMP_ARCH_MIPSEL64N32 },
    1698             :                 { "SCMP_ARCH_NATIVE",      SCMP_ARCH_NATIVE      },
    1699             : #ifdef SCMP_ARCH_PARISC
    1700             :                 { "SCMP_ARCH_PARISC",      SCMP_ARCH_PARISC      },
    1701             : #endif
    1702             : #ifdef SCMP_ARCH_PARISC64
    1703             :                 { "SCMP_ARCH_PARISC64",    SCMP_ARCH_PARISC64    },
    1704             : #endif
    1705             :                 { "SCMP_ARCH_PPC",         SCMP_ARCH_PPC         },
    1706             :                 { "SCMP_ARCH_PPC64",       SCMP_ARCH_PPC64       },
    1707             :                 { "SCMP_ARCH_PPC64LE",     SCMP_ARCH_PPC64LE     },
    1708             :                 { "SCMP_ARCH_S390",        SCMP_ARCH_S390        },
    1709             :                 { "SCMP_ARCH_S390X",       SCMP_ARCH_S390X       },
    1710             :                 { "SCMP_ARCH_X32",         SCMP_ARCH_X32         },
    1711             :                 { "SCMP_ARCH_X86",         SCMP_ARCH_X86         },
    1712             :                 { "SCMP_ARCH_X86_64",      SCMP_ARCH_X86_64      },
    1713             :         };
    1714             : 
    1715             :         size_t i;
    1716             : 
    1717           0 :         for (i = 0; i < ELEMENTSOF(table); i++)
    1718           0 :                 if (streq_ptr(table[i].name, name)) {
    1719           0 :                         *ret = table[i].arch;
    1720           0 :                         return 0;
    1721             :                 }
    1722             : 
    1723           0 :         return -EINVAL;
    1724             : }
    1725             : 
    1726           0 : static int oci_seccomp_compare_from_string(const char *name, enum scmp_compare *ret) {
    1727             : 
    1728             :         static const struct {
    1729             :                 const char *name;
    1730             :                 enum scmp_compare op;
    1731             :         } table[] = {
    1732             :                 { "SCMP_CMP_NE",        SCMP_CMP_NE        },
    1733             :                 { "SCMP_CMP_LT",        SCMP_CMP_LT        },
    1734             :                 { "SCMP_CMP_LE",        SCMP_CMP_LE        },
    1735             :                 { "SCMP_CMP_EQ",        SCMP_CMP_EQ        },
    1736             :                 { "SCMP_CMP_GE",        SCMP_CMP_GE        },
    1737             :                 { "SCMP_CMP_GT",        SCMP_CMP_GT        },
    1738             :                 { "SCMP_CMP_MASKED_EQ", SCMP_CMP_MASKED_EQ },
    1739             :         };
    1740             : 
    1741             :         size_t i;
    1742             : 
    1743           0 :         for (i = 0; i < ELEMENTSOF(table); i++)
    1744           0 :                 if (streq_ptr(table[i].name, name)) {
    1745           0 :                         *ret = table[i].op;
    1746           0 :                         return 0;
    1747             :                 }
    1748             : 
    1749           0 :         return -EINVAL;
    1750             : }
    1751             : 
    1752           0 : static int oci_seccomp_archs(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1753           0 :         scmp_filter_ctx *sc = userdata;
    1754             :         JsonVariant *e;
    1755             :         int r;
    1756             : 
    1757           0 :         assert(sc);
    1758             : 
    1759           0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
    1760             :                 uint32_t a;
    1761             : 
    1762           0 :                 if (!json_variant_is_string(e))
    1763           0 :                         return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL),
    1764             :                                         "Architecture entry is not a string");
    1765             : 
    1766           0 :                 r = oci_seccomp_arch_from_string(json_variant_string(e), &a);
    1767           0 :                 if (r < 0)
    1768           0 :                         return json_log(e, flags, r, "Unknown architecture: %s", json_variant_string(e));
    1769             : 
    1770           0 :                 r = seccomp_arch_add(sc, a);
    1771           0 :                 if (r == -EEXIST)
    1772           0 :                         continue;
    1773           0 :                 if (r < 0)
    1774           0 :                         return json_log(e, flags, r, "Failed to add architecture to seccomp filter: %m");
    1775             :         }
    1776             : 
    1777           0 :         return 0;
    1778             : }
    1779             : 
    1780             : struct syscall_rule {
    1781             :         char **names;
    1782             :         uint32_t action;
    1783             :         struct scmp_arg_cmp *arguments;
    1784             :         size_t n_arguments;
    1785             : };
    1786             : 
    1787           0 : static void syscall_rule_free(struct syscall_rule *rule) {
    1788           0 :         assert(rule);
    1789             : 
    1790           0 :         strv_free(rule->names);
    1791           0 :         free(rule->arguments);
    1792           0 : };
    1793             : 
    1794           0 : static int oci_seccomp_action(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1795           0 :         uint32_t *action = userdata;
    1796             :         int r;
    1797             : 
    1798           0 :         assert(action);
    1799             : 
    1800           0 :         r = oci_seccomp_action_from_string(json_variant_string(v), action);
    1801           0 :         if (r < 0)
    1802           0 :                 return json_log(v, flags, r, "Unknown system call action '%s': %m", json_variant_string(v));
    1803             : 
    1804           0 :         return 0;
    1805             : }
    1806             : 
    1807           0 : static int oci_seccomp_op(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1808           0 :         enum scmp_compare *op = userdata;
    1809             :         int r;
    1810             : 
    1811           0 :         assert(op);
    1812             : 
    1813           0 :         r = oci_seccomp_compare_from_string(json_variant_string(v), op);
    1814           0 :         if (r < 0)
    1815           0 :                 return json_log(v, flags, r, "Unknown seccomp operator '%s': %m", json_variant_string(v));
    1816             : 
    1817           0 :         return 0;
    1818             : }
    1819             : 
    1820           0 : static int oci_seccomp_args(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1821           0 :         struct syscall_rule *rule = userdata;
    1822             :         JsonVariant *e;
    1823             :         int r;
    1824             : 
    1825           0 :         assert(rule);
    1826             : 
    1827           0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
    1828             :                 static const struct JsonDispatch table[] = {
    1829             :                         { "index",    JSON_VARIANT_UNSIGNED, json_dispatch_uint32, offsetof(struct scmp_arg_cmp, arg),     JSON_MANDATORY },
    1830             :                         { "value",    JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(struct scmp_arg_cmp, datum_a), JSON_MANDATORY },
    1831             :                         { "valueTwo", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(struct scmp_arg_cmp, datum_b), 0              },
    1832             :                         { "op",       JSON_VARIANT_STRING,   oci_seccomp_op,       offsetof(struct scmp_arg_cmp, op),      JSON_MANDATORY },
    1833             :                         {},
    1834             :                 };
    1835             : 
    1836             :                 struct scmp_arg_cmp *a, *p;
    1837             :                 int expected;
    1838             : 
    1839           0 :                 a = reallocarray(rule->arguments, rule->n_arguments + 1, sizeof(struct syscall_rule));
    1840           0 :                 if (!a)
    1841           0 :                         return log_oom();
    1842             : 
    1843           0 :                 rule->arguments = a;
    1844           0 :                 p = rule->arguments + rule->n_arguments;
    1845             : 
    1846           0 :                 *p = (struct scmp_arg_cmp) {
    1847             :                         .arg = 0,
    1848             :                         .datum_a = 0,
    1849             :                         .datum_b = 0,
    1850             :                         .op = 0,
    1851             :                 };
    1852             : 
    1853           0 :                 r = json_dispatch(e, table, oci_unexpected, flags, p);
    1854           0 :                 if (r < 0)
    1855           0 :                         return r;
    1856             : 
    1857           0 :                 expected = p->op == SCMP_CMP_MASKED_EQ ? 4 : 3;
    1858           0 :                 if (r != expected)
    1859           0 :                         json_log(e, flags|JSON_WARNING, 0, "Wrong number of system call arguments for JSON data data, ignoring.");
    1860             : 
    1861             :                 /* Note that we are a bit sloppy here and do not insist that SCMP_CMP_MASKED_EQ gets two datum values,
    1862             :                  * and the other only one. That's because buildah for example by default calls things with
    1863             :                  * SCMP_CMP_MASKED_EQ but only one argument. We use 0 when the value is not specified. */
    1864             : 
    1865           0 :                 rule->n_arguments++;
    1866             :         }
    1867             : 
    1868           0 :         return 0;
    1869             : }
    1870             : 
    1871           0 : static int oci_seccomp_syscalls(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1872           0 :         scmp_filter_ctx *sc = userdata;
    1873             :         JsonVariant *e;
    1874             :         int r;
    1875             : 
    1876           0 :         assert(sc);
    1877             : 
    1878           0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
    1879             :                 static const JsonDispatch table[] = {
    1880             :                         { "names",  JSON_VARIANT_ARRAY,  json_dispatch_strv, offsetof(struct syscall_rule, names),  JSON_MANDATORY },
    1881             :                         { "action", JSON_VARIANT_STRING, oci_seccomp_action, offsetof(struct syscall_rule, action), JSON_MANDATORY },
    1882             :                         { "args",   JSON_VARIANT_ARRAY,  oci_seccomp_args,   0,                                     0              },
    1883             :                 };
    1884           0 :                 struct syscall_rule rule = {
    1885             :                         .action = (uint32_t) -1,
    1886             :                 };
    1887             :                 char **i;
    1888             : 
    1889           0 :                 r = json_dispatch(e, table, oci_unexpected, flags, &rule);
    1890           0 :                 if (r < 0)
    1891           0 :                         goto fail_rule;
    1892             : 
    1893           0 :                 if (strv_isempty(rule.names)) {
    1894           0 :                         json_log(e, flags, 0, "System call name list is empty.");
    1895           0 :                         r = -EINVAL;
    1896           0 :                         goto fail_rule;
    1897             :                 }
    1898             : 
    1899           0 :                 STRV_FOREACH(i, rule.names) {
    1900             :                         int nr;
    1901             : 
    1902           0 :                         nr = seccomp_syscall_resolve_name(*i);
    1903           0 :                         if (nr == __NR_SCMP_ERROR) {
    1904           0 :                                 log_debug("Unknown syscall %s, skipping.", *i);
    1905           0 :                                 continue;
    1906             :                         }
    1907             : 
    1908           0 :                         r = seccomp_rule_add_array(sc, rule.action, nr, rule.n_arguments, rule.arguments);
    1909           0 :                         if (r < 0)
    1910           0 :                                 goto fail_rule;
    1911             :                 }
    1912             : 
    1913           0 :                 syscall_rule_free(&rule);
    1914           0 :                 continue;
    1915             : 
    1916           0 :         fail_rule:
    1917           0 :                 syscall_rule_free(&rule);
    1918           0 :                 return r;
    1919             :         }
    1920             : 
    1921           0 :         return 0;
    1922             : }
    1923             : #endif
    1924             : 
    1925           0 : static int oci_seccomp(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1926             : 
    1927             : #if HAVE_SECCOMP
    1928             :         static const JsonDispatch table[] = {
    1929             :                 { "defaultAction", JSON_VARIANT_STRING, NULL,                 0, JSON_MANDATORY },
    1930             :                 { "architectures", JSON_VARIANT_ARRAY,  oci_seccomp_archs,    0, 0              },
    1931             :                 { "syscalls",      JSON_VARIANT_ARRAY,  oci_seccomp_syscalls, 0, 0              },
    1932             :                 {}
    1933             :         };
    1934             : 
    1935           0 :         _cleanup_(seccomp_releasep) scmp_filter_ctx sc = NULL;
    1936           0 :         Settings *s = userdata;
    1937             :         JsonVariant *def;
    1938             :         uint32_t d;
    1939             :         int r;
    1940             : 
    1941           0 :         assert(s);
    1942             : 
    1943           0 :         def = json_variant_by_key(v, "defaultAction");
    1944           0 :         if (!def)
    1945           0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), "defaultAction element missing.");
    1946             : 
    1947           0 :         if (!json_variant_is_string(def))
    1948           0 :                 return json_log(def, flags, SYNTHETIC_ERRNO(EINVAL), "defaultAction is not a string.");
    1949             : 
    1950           0 :         r = oci_seccomp_action_from_string(json_variant_string(def), &d);
    1951           0 :         if (r < 0)
    1952           0 :                 return json_log(def, flags, r, "Unknown default action: %s", json_variant_string(def));
    1953             : 
    1954           0 :         sc = seccomp_init(d);
    1955           0 :         if (!sc)
    1956           0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(ENOMEM), "Couldn't allocate seccomp object.");
    1957             : 
    1958           0 :         r = json_dispatch(v, table, oci_unexpected, flags, sc);
    1959           0 :         if (r < 0)
    1960           0 :                 return r;
    1961             : 
    1962           0 :         seccomp_release(s->seccomp);
    1963           0 :         s->seccomp = TAKE_PTR(sc);
    1964           0 :         return 0;
    1965             : #else
    1966             :         return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP), "libseccomp support not enabled, can't parse seccomp object.");
    1967             : #endif
    1968             : }
    1969             : 
    1970           0 : static int oci_rootfs_propagation(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1971             :         const char *s;
    1972             : 
    1973           0 :         s = json_variant_string(v);
    1974             : 
    1975           0 :         if (streq(s, "shared"))
    1976           0 :                 return 0;
    1977             : 
    1978           0 :         json_log(v, flags|JSON_DEBUG, 0, "Ignoring rootfsPropagation setting '%s'.", s);
    1979           0 :         return 0;
    1980             : }
    1981             : 
    1982           0 : static int oci_masked_paths(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    1983           0 :         Settings *s = userdata;
    1984             :         JsonVariant *e;
    1985             : 
    1986           0 :         assert(s);
    1987             : 
    1988           0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
    1989           0 :                 _cleanup_free_ char *destination = NULL;
    1990             :                 CustomMount *m;
    1991             :                 const char *p;
    1992             : 
    1993           0 :                 if (!json_variant_is_string(e))
    1994           0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
    1995             :                                         "Path is not a string, refusing.");
    1996             : 
    1997           0 :                 assert_se(p = json_variant_string(e));
    1998             : 
    1999           0 :                 if (!path_is_absolute(p))
    2000           0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
    2001             :                                         "Path is not not absolute, refusing: %s", p);
    2002             : 
    2003           0 :                 if (oci_exclude_mount(p))
    2004           0 :                         continue;
    2005             : 
    2006           0 :                 destination = strdup(p);
    2007           0 :                 if (!destination)
    2008           0 :                         return log_oom();
    2009             : 
    2010           0 :                 m = custom_mount_add(&s->custom_mounts, &s->n_custom_mounts, CUSTOM_MOUNT_INACCESSIBLE);
    2011           0 :                 if (!m)
    2012           0 :                         return log_oom();
    2013             : 
    2014           0 :                 m->destination = TAKE_PTR(destination);
    2015             : 
    2016             :                 /* The spec doesn't say this, but apparently pre-existing implementations are lenient towards
    2017             :                  * non-existing paths to mask. Let's hence be too. */
    2018           0 :                 m->graceful = true;
    2019             :         }
    2020             : 
    2021           0 :         return 0;
    2022             : }
    2023             : 
    2024           0 : static int oci_readonly_paths(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    2025           0 :         Settings *s = userdata;
    2026             :         JsonVariant *e;
    2027             : 
    2028           0 :         assert(s);
    2029             : 
    2030           0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
    2031           0 :                 _cleanup_free_ char *source = NULL, *destination = NULL;
    2032             :                 CustomMount *m;
    2033             :                 const char *p;
    2034             : 
    2035           0 :                 if (!json_variant_is_string(e))
    2036           0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
    2037             :                                         "Path is not a string, refusing.");
    2038             : 
    2039           0 :                 assert_se(p = json_variant_string(e));
    2040             : 
    2041           0 :                 if (!path_is_absolute(p))
    2042           0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
    2043             :                                         "Path is not not absolute, refusing: %s", p);
    2044             : 
    2045           0 :                 if (oci_exclude_mount(p))
    2046           0 :                         continue;
    2047             : 
    2048           0 :                 source = strjoin("+", p);
    2049           0 :                 if (!source)
    2050           0 :                         return log_oom();
    2051             : 
    2052           0 :                 destination = strdup(p);
    2053           0 :                 if (!destination)
    2054           0 :                         return log_oom();
    2055             : 
    2056           0 :                 m = custom_mount_add(&s->custom_mounts, &s->n_custom_mounts, CUSTOM_MOUNT_BIND);
    2057           0 :                 if (!m)
    2058           0 :                         return log_oom();
    2059             : 
    2060           0 :                 m->source = TAKE_PTR(source);
    2061           0 :                 m->destination = TAKE_PTR(destination);
    2062           0 :                 m->read_only = true;
    2063             :         }
    2064             : 
    2065           0 :         return 0;
    2066             : }
    2067             : 
    2068           0 : static int oci_linux(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    2069             : 
    2070             :         static const JsonDispatch table[] = {
    2071             :                 { "namespaces",        JSON_VARIANT_ARRAY,  oci_namespaces,         0, 0               },
    2072             :                 { "uidMappings",       JSON_VARIANT_ARRAY,  oci_uid_gid_mappings,   0, 0               },
    2073             :                 { "gidMappings",       JSON_VARIANT_ARRAY,  oci_uid_gid_mappings,   0, 0               },
    2074             :                 { "devices",           JSON_VARIANT_ARRAY,  oci_devices,            0, 0               },
    2075             :                 { "cgroupsPath",       JSON_VARIANT_STRING, oci_cgroups_path,       0, 0               },
    2076             :                 { "resources",         JSON_VARIANT_OBJECT, oci_resources,          0, 0               },
    2077             :                 { "intelRdt",          JSON_VARIANT_OBJECT, oci_unsupported,        0, JSON_PERMISSIVE },
    2078             :                 { "sysctl",            JSON_VARIANT_OBJECT, oci_sysctl,             0, 0               },
    2079             :                 { "seccomp",           JSON_VARIANT_OBJECT, oci_seccomp,            0, 0               },
    2080             :                 { "rootfsPropagation", JSON_VARIANT_STRING, oci_rootfs_propagation, 0, 0               },
    2081             :                 { "maskedPaths",       JSON_VARIANT_ARRAY,  oci_masked_paths,       0, 0               },
    2082             :                 { "readonlyPaths",     JSON_VARIANT_ARRAY,  oci_readonly_paths,     0, 0               },
    2083             :                 { "mountLabel",        JSON_VARIANT_STRING, oci_unsupported,        0, JSON_PERMISSIVE },
    2084             :                 {}
    2085             :         };
    2086             : 
    2087           0 :         return json_dispatch(v, table, oci_unexpected, flags, userdata);
    2088             : }
    2089             : 
    2090           0 : static int oci_hook_timeout(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    2091           0 :         usec_t *u = userdata;
    2092             :         uintmax_t k;
    2093             : 
    2094           0 :         k = json_variant_unsigned(v);
    2095           0 :         if (k == 0 || k > (UINT64_MAX-1)/USEC_PER_SEC)
    2096           0 :                 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
    2097             :                                 "Hook timeout value out of range.");
    2098             : 
    2099           0 :         *u = k * USEC_PER_SEC;
    2100           0 :         return 0;
    2101             : }
    2102             : 
    2103           0 : static int oci_hooks_array(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    2104           0 :         Settings *s = userdata;
    2105             :         JsonVariant *e;
    2106             :         int r;
    2107             : 
    2108           0 :         assert(s);
    2109             : 
    2110           0 :         JSON_VARIANT_ARRAY_FOREACH(e, v) {
    2111             : 
    2112             :                 static const JsonDispatch table[] = {
    2113             :                         { "path",    JSON_VARIANT_STRING,   oci_absolute_path, offsetof(OciHook, path),    JSON_MANDATORY },
    2114             :                         { "args",    JSON_VARIANT_ARRAY,    oci_args,          offsetof(OciHook, args),    0              },
    2115             :                         { "env",     JSON_VARIANT_ARRAY,    oci_env,           offsetof(OciHook, env),     0              },
    2116             :                         { "timeout", JSON_VARIANT_UNSIGNED, oci_hook_timeout,  offsetof(OciHook, timeout), 0              },
    2117             :                         {}
    2118             :                 };
    2119             : 
    2120             :                 OciHook *a, **array, *new_item;
    2121             :                 size_t *n_array;
    2122             : 
    2123           0 :                 if (streq(name, "prestart")) {
    2124           0 :                         array = &s->oci_hooks_prestart;
    2125           0 :                         n_array = &s->n_oci_hooks_prestart;
    2126           0 :                 } else if (streq(name, "poststart")) {
    2127           0 :                         array = &s->oci_hooks_poststart;
    2128           0 :                         n_array = &s->n_oci_hooks_poststart;
    2129             :                 } else {
    2130           0 :                         assert(streq(name, "poststop"));
    2131           0 :                         array = &s->oci_hooks_poststop;
    2132           0 :                         n_array = &s->n_oci_hooks_poststop;
    2133             :                 }
    2134             : 
    2135           0 :                 a = reallocarray(*array, *n_array + 1, sizeof(OciHook));
    2136           0 :                 if (!a)
    2137           0 :                         return log_oom();
    2138             : 
    2139           0 :                 *array = a;
    2140           0 :                 new_item = a + *n_array;
    2141             : 
    2142           0 :                 *new_item = (OciHook) {
    2143             :                         .timeout = USEC_INFINITY,
    2144             :                 };
    2145             : 
    2146           0 :                 r = json_dispatch(e, table, oci_unexpected, flags, userdata);
    2147           0 :                 if (r < 0) {
    2148           0 :                         free(new_item->path);
    2149           0 :                         strv_free(new_item->args);
    2150           0 :                         strv_free(new_item->env);
    2151           0 :                         return r;
    2152             :                 }
    2153             : 
    2154           0 :                 (*n_array) ++;
    2155             :         }
    2156             : 
    2157           0 :         return 0;
    2158             : }
    2159             : 
    2160           0 : static int oci_hooks(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    2161             : 
    2162             :         static const JsonDispatch table[] = {
    2163             :                 { "prestart",  JSON_VARIANT_OBJECT, oci_hooks_array, 0, 0 },
    2164             :                 { "poststart", JSON_VARIANT_OBJECT, oci_hooks_array, 0, 0 },
    2165             :                 { "poststop",  JSON_VARIANT_OBJECT, oci_hooks_array, 0, 0 },
    2166             :                 {}
    2167             :         };
    2168             : 
    2169           0 :         return json_dispatch(v, table, oci_unexpected, flags, userdata);
    2170             : }
    2171             : 
    2172           0 : static int oci_annotations(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
    2173             :         JsonVariant *w;
    2174             :         const char *k;
    2175             : 
    2176           0 :         JSON_VARIANT_OBJECT_FOREACH(k, w, v) {
    2177             : 
    2178           0 :                 if (isempty(k))
    2179           0 :                         return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
    2180             :                                         "Annotation with empty key, refusing.");
    2181             : 
    2182           0 :                 if (!json_variant_is_string(w))
    2183           0 :                         return json_log(w, flags, SYNTHETIC_ERRNO(EINVAL),
    2184             :                                         "Annotation has non-string value, refusing.");
    2185             : 
    2186           0 :                 json_log(w, flags|JSON_DEBUG, 0, "Ignoring annotation '%s' with value '%s'.", k, json_variant_string(w));
    2187             :         }
    2188             : 
    2189           0 :         return 0;
    2190             : }
    2191             : 
    2192           0 : int oci_load(FILE *f, const char *bundle, Settings **ret) {
    2193             : 
    2194             :         static const JsonDispatch table[] = {
    2195             :                 { "ociVersion",  JSON_VARIANT_STRING, NULL,            0, JSON_MANDATORY },
    2196             :                 { "process",     JSON_VARIANT_OBJECT, oci_process,     0, 0 },
    2197             :                 { "root",        JSON_VARIANT_OBJECT, oci_root,        0, 0 },
    2198             :                 { "hostname",    JSON_VARIANT_STRING, oci_hostname,    0, 0 },
    2199             :                 { "mounts",      JSON_VARIANT_ARRAY,  oci_mounts,      0, 0 },
    2200             :                 { "linux",       JSON_VARIANT_OBJECT, oci_linux,       0, 0 },
    2201             :                 { "hooks",       JSON_VARIANT_OBJECT, oci_hooks,       0, 0 },
    2202             :                 { "annotations", JSON_VARIANT_OBJECT, oci_annotations, 0, 0 },
    2203             :                 {}
    2204             :         };
    2205             : 
    2206           0 :         _cleanup_(json_variant_unrefp) JsonVariant *oci = NULL;
    2207           0 :         _cleanup_(settings_freep) Settings *s = NULL;
    2208           0 :         unsigned line = 0, column = 0;
    2209             :         JsonVariant *v;
    2210             :         const char *path;
    2211             :         int r;
    2212             : 
    2213           0 :         assert_se(bundle);
    2214             : 
    2215           0 :         path = strjoina(bundle, "/config.json");
    2216             : 
    2217           0 :         r = json_parse_file(f, path, &oci, &line, &column);
    2218           0 :         if (r < 0) {
    2219           0 :                 if (line != 0 && column != 0)
    2220           0 :                         return log_error_errno(r, "Failed to parse '%s' at %u:%u: %m", path, line, column);
    2221             :                 else
    2222           0 :                         return log_error_errno(r, "Failed to parse '%s': %m", path);
    2223             :         }
    2224             : 
    2225           0 :         v = json_variant_by_key(oci, "ociVersion");
    2226           0 :         if (!v) {
    2227           0 :                 log_error("JSON file '%s' is not an OCI bundle configuration file. Refusing.", path);
    2228           0 :                 return -EINVAL;
    2229             :         }
    2230           0 :         if (!streq_ptr(json_variant_string(v), "1.0.0")) {
    2231           0 :                 log_error("OCI bundle version not supported: %s", strna(json_variant_string(v)));
    2232           0 :                 return -EINVAL;
    2233             :         }
    2234             : 
    2235             :         // {
    2236             :         //         _cleanup_free_ char *formatted = NULL;
    2237             :         //         assert_se(json_variant_format(oci, JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR, &formatted) >= 0);
    2238             :         //         fputs(formatted, stdout);
    2239             :         // }
    2240             : 
    2241           0 :         s = settings_new();
    2242           0 :         if (!s)
    2243           0 :                 return log_oom();
    2244             : 
    2245           0 :         s->start_mode = START_PID1;
    2246           0 :         s->resolv_conf = RESOLV_CONF_OFF;
    2247           0 :         s->link_journal = LINK_NO;
    2248           0 :         s->timezone = TIMEZONE_OFF;
    2249             : 
    2250           0 :         s->bundle = strdup(bundle);
    2251           0 :         if (!s->bundle)
    2252           0 :                 return log_oom();
    2253             : 
    2254           0 :         r = json_dispatch(oci, table, oci_unexpected, 0, s);
    2255           0 :         if (r < 0)
    2256           0 :                 return r;
    2257             : 
    2258           0 :         if (s->properties) {
    2259           0 :                 r = sd_bus_message_seal(s->properties, 0, 0);
    2260           0 :                 if (r < 0)
    2261           0 :                         return log_error_errno(r, "Cannot seal properties bus message: %m");
    2262             :         }
    2263             : 
    2264           0 :         *ret = TAKE_PTR(s);
    2265           0 :         return 0;
    2266             : }

Generated by: LCOV version 1.14