chiark / gitweb /
core: warn when unit files with unsupported options are parsed
[elogind.git] / src / core / load-fragment.c
index 8a42e739bedc11572fd7a261ff7bc7dfdf7b6f8c..7260d205788664167931e6f8b9724074296f605f 100644 (file)
 #include <sys/time.h>
 #include <sys/resource.h>
 
+#ifdef HAVE_SECCOMP
+#include <seccomp.h>
+#endif
+
 #include "sd-messages.h"
 #include "unit.h"
 #include "strv.h"
 #include "unit-printf.h"
 #include "utf8.h"
 #include "path-util.h"
-#include "syscall-list.h"
 #include "env-util.h"
 #include "cgroup.h"
 #include "bus-util.h"
 #include "bus-error.h"
+#include "errno-list.h"
 
-#ifndef HAVE_SYSV_COMPAT
-int config_parse_warn_compat(const char *unit,
-                             const char *filename,
-                             unsigned line,
-                             const char *section,
-                             unsigned section_line,
-                             const char *lvalue,
-                             int ltype,
-                             const char *rvalue,
-                             void *data,
-                             void *userdata) {
+#ifdef HAVE_SECCOMP
+#include "seccomp-util.h"
+#endif
+
+#if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_LIBWRAP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK)
+int config_parse_warn_compat(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
 
         log_syntax(unit, LOG_DEBUG, filename, line, EINVAL,
                    "Support for option %s= has been disabled at compile time and is ignored",
@@ -1134,6 +1143,55 @@ int config_parse_exec_mount_flags(const char *unit,
         return 0;
 }
 
+int config_parse_exec_selinux_context(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        ExecContext *c = data;
+        Unit *u = userdata;
+        bool ignore;
+        char *k;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (isempty(rvalue)) {
+                free(c->selinux_context);
+                c->selinux_context = NULL;
+                c->selinux_context_ignore = false;
+                return 0;
+        }
+
+        if (rvalue[0] == '-') {
+                ignore = true;
+                rvalue++;
+        } else
+                ignore = false;
+
+        r = unit_name_printf(u, rvalue, &k);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", strerror(-r));
+                return 0;
+        }
+
+        free(c->selinux_context);
+        c->selinux_context = k;
+        c->selinux_context_ignore = ignore;
+
+        return 0;
+}
+
 int config_parse_timer(const char *unit,
                        const char *filename,
                        unsigned line,
@@ -1457,6 +1515,51 @@ int config_parse_service_timeout(const char *unit,
         return 0;
 }
 
+int config_parse_busname_service(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+        BusName *n = data;
+        int r;
+        Unit *x;
+        _cleanup_free_ char *p = NULL;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = unit_name_printf(UNIT(n), rvalue, &p);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        if (!endswith(p, ".service")) {
+                log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unit must be of type service, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        r = manager_load_unit(UNIT(n)->manager, p, NULL, &error, &x);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
+                return 0;
+        }
+
+        unit_ref_set(&n->service, x);
+
+        return 0;
+}
+
 int config_parse_unit_env_file(const char *unit,
                                const char *filename,
                                unsigned line,
@@ -1871,33 +1974,32 @@ int config_parse_documentation(const char *unit,
         return r;
 }
 
-static void syscall_set(uint32_t *p, int nr) {
-        nr = SYSCALL_TO_INDEX(nr);
-        p[nr >> 4] |= 1 << (nr & 31);
-}
-
-static void syscall_unset(uint32_t *p, int nr) {
-        nr = SYSCALL_TO_INDEX(nr);
-        p[nr >> 4] &= ~(1 << (nr & 31));
-}
+#ifdef HAVE_SECCOMP
+int config_parse_syscall_filter(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
 
-int config_parse_syscall_filter(const char *unit,
-                                const char *filename,
-                                unsigned line,
-                                const char *section,
-                                unsigned section_line,
-                                const char *lvalue,
-                                int ltype,
-                                const char *rvalue,
-                                void *data,
-                                void *userdata) {
+        static const char default_syscalls[] =
+                "execve\0"
+                "exit\0"
+                "exit_group\0"
+                "rt_sigreturn\0"
+                "sigreturn\0";
 
         ExecContext *c = data;
         Unit *u = userdata;
         bool invert = false;
-        char *w;
+        char *w, *state;
         size_t l;
-        char *state;
+        int r;
 
         assert(filename);
         assert(lvalue);
@@ -1906,8 +2008,9 @@ int config_parse_syscall_filter(const char *unit,
 
         if (isempty(rvalue)) {
                 /* Empty assignment resets the list */
-                free(c->syscall_filter);
+                set_free(c->syscall_filter);
                 c->syscall_filter = NULL;
+                c->syscall_whitelist = false;
                 return 0;
         }
 
@@ -1917,44 +2020,61 @@ int config_parse_syscall_filter(const char *unit,
         }
 
         if (!c->syscall_filter) {
-                size_t n;
-
-                n = (syscall_max() + 31) >> 4;
-                c->syscall_filter = new(uint32_t, n);
+                c->syscall_filter = set_new(trivial_hash_func, trivial_compare_func);
                 if (!c->syscall_filter)
                         return log_oom();
 
-                memset(c->syscall_filter, invert ? 0xFF : 0, n * sizeof(uint32_t));
+                if (invert)
+                        /* Allow everything but the ones listed */
+                        c->syscall_whitelist = false;
+                else {
+                        const char *i;
 
-                /* Add these by default */
-                syscall_set(c->syscall_filter, __NR_execve);
-                syscall_set(c->syscall_filter, __NR_rt_sigreturn);
-#ifdef __NR_sigreturn
-                syscall_set(c->syscall_filter, __NR_sigreturn);
-#endif
-                syscall_set(c->syscall_filter, __NR_exit_group);
-                syscall_set(c->syscall_filter, __NR_exit);
+                        /* Allow nothing but the ones listed */
+                        c->syscall_whitelist = true;
+
+                        /* Accept default syscalls if we are on a whitelist */
+                        NULSTR_FOREACH(i, default_syscalls)  {
+                                int id;
+
+                                id = seccomp_syscall_resolve_name(i);
+                                if (id < 0)
+                                        continue;
+
+                                r = set_put(c->syscall_filter, INT_TO_PTR(id + 1));
+                                if (r == -EEXIST)
+                                        continue;
+                                if (r < 0)
+                                        return log_oom();
+                        }
+                }
         }
 
         FOREACH_WORD_QUOTED(w, l, rvalue, state) {
-                int id;
                 _cleanup_free_ char *t = NULL;
+                int id;
 
                 t = strndup(w, l);
                 if (!t)
                         return log_oom();
 
-                id = syscall_from_name(t);
+                id = seccomp_syscall_resolve_name(t);
                 if (id < 0)  {
-                        log_syntax(unit, LOG_ERR, filename, line, EINVAL,
-                                   "Failed to parse syscall, ignoring: %s", t);
+                        log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse system call, ignoring: %s", t);
                         continue;
                 }
 
-                if (invert)
-                        syscall_unset(c->syscall_filter, id);
-                else
-                        syscall_set(c->syscall_filter, id);
+                /* If we previously wanted to forbid a syscall and now
+                 * we want to allow it, then remove it from the list
+                 */
+                if (!invert == c->syscall_whitelist)  {
+                        r = set_put(c->syscall_filter, INT_TO_PTR(id + 1));
+                        if (r == -EEXIST)
+                                continue;
+                        if (r < 0)
+                                return log_oom();
+                } else
+                        set_remove(c->syscall_filter, INT_TO_PTR(id + 1));
         }
 
         c->no_new_privileges = true;
@@ -1962,6 +2082,93 @@ int config_parse_syscall_filter(const char *unit,
         return 0;
 }
 
+int config_parse_syscall_archs(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        Set **archs = data;
+        char *w, *state;
+        size_t l;
+        int r;
+
+        if (isempty(rvalue)) {
+                set_free(*archs);
+                *archs = NULL;
+                return 0;
+        }
+
+        r = set_ensure_allocated(archs, trivial_hash_func, trivial_compare_func);
+        if (r < 0)
+                return log_oom();
+
+        FOREACH_WORD_QUOTED(w, l, rvalue, state) {
+                _cleanup_free_ char *t = NULL;
+                uint32_t a;
+
+                t = strndup(w, l);
+                if (!t)
+                        return log_oom();
+
+                r = seccomp_arch_from_string(t, &a);
+                if (r < 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse system call architecture, ignoring: %s", t);
+                        continue;
+                }
+
+                r = set_put(*archs, UINT32_TO_PTR(a + 1));
+                if (r == -EEXIST)
+                        continue;
+                if (r < 0)
+                        return log_oom();
+        }
+
+        return 0;
+}
+
+int config_parse_syscall_errno(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        ExecContext *c = data;
+        int e;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+
+        if (isempty(rvalue)) {
+                /* Empty assignment resets to KILL */
+                c->syscall_errno = 0;
+                return 0;
+        }
+
+        e = errno_from_name(rvalue);
+        if (e < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse error number, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        c->syscall_errno = e;
+        return 0;
+}
+#endif
+
 int config_parse_unit_slice(
                 const char *unit,
                 const char *filename,
@@ -2386,9 +2593,9 @@ static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
                 /* Add the file name we are currently looking at to
                  * the names of this unit, but only if it is a valid
                  * unit name. */
-                name = path_get_file_name(*filename);
+                name = basename(*filename);
 
-                if (unit_name_is_valid(name, true)) {
+                if (unit_name_is_valid(name, TEMPLATE_VALID)) {
 
                         id = set_get(names, name);
                         if (!id) {
@@ -2635,31 +2842,28 @@ int unit_load_fragment(Unit *u) {
 
         /* Look for a template */
         if (u->load_state == UNIT_STUB && u->instance) {
-                char *k;
+                _cleanup_free_ char *k;
 
                 k = unit_name_template(u->id);
                 if (!k)
                         return -ENOMEM;
 
                 r = load_from_path(u, k);
-                free(k);
-
                 if (r < 0)
                         return r;
 
                 if (u->load_state == UNIT_STUB)
                         SET_FOREACH(t, u->names, i) {
+                                _cleanup_free_ char *z = NULL;
 
                                 if (t == u->id)
                                         continue;
 
-                                k = unit_name_template(t);
-                                if (!k)
+                                z = unit_name_template(t);
+                                if (!z)
                                         return -ENOMEM;
 
-                                r = load_from_path(u, k);
-                                free(k);
-
+                                r = load_from_path(u, z);
                                 if (r < 0)
                                         return r;
 
@@ -2676,6 +2880,9 @@ void unit_dump_config_items(FILE *f) {
                 const ConfigParserCallback callback;
                 const char *rvalue;
         } table[] = {
+#if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_LIBWRAP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK)
+                { config_parse_warn_compat,           "NOTSUPPORTED" },
+#endif
                 { config_parse_int,                   "INTEGER" },
                 { config_parse_unsigned,              "UNSIGNED" },
                 { config_parse_bytes_size,            "SIZE" },
@@ -2707,8 +2914,6 @@ void unit_dump_config_items(FILE *f) {
                 { config_parse_service_restart,       "SERVICERESTART" },
 #ifdef HAVE_SYSV_COMPAT
                 { config_parse_sysv_priority,         "SYSVPRIORITY" },
-#else
-                { config_parse_warn_compat,           "NOTSUPPORTED" },
 #endif
                 { config_parse_kill_mode,             "KILLMODE" },
                 { config_parse_kill_signal,           "SIGNAL" },
@@ -2736,7 +2941,11 @@ void unit_dump_config_items(FILE *f) {
                 { config_parse_set_status,            "STATUS" },
                 { config_parse_service_sockets,       "SOCKETS" },
                 { config_parse_environ,               "ENVIRON" },
-                { config_parse_syscall_filter,        "SYSCALL" },
+#ifdef HAVE_SECCOMP
+                { config_parse_syscall_filter,        "SYSCALLS" },
+                { config_parse_syscall_archs,         "ARCHS" },
+                { config_parse_syscall_errno,         "ERRNO" },
+#endif
                 { config_parse_cpu_shares,            "SHARES" },
                 { config_parse_memory_limit,          "LIMIT" },
                 { config_parse_device_allow,          "DEVICE" },
@@ -2746,6 +2955,11 @@ void unit_dump_config_items(FILE *f) {
                 { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
                 { config_parse_long,                  "LONG" },
                 { config_parse_socket_service,        "SERVICE" },
+#ifdef HAVE_SELINUX
+                { config_parse_exec_selinux_context,  "LABEL" },
+#endif
+                { config_parse_job_mode,              "MODE" },
+                { config_parse_job_mode_isolate,      "BOOLEAN" },
         };
 
         const char *prev = NULL;