]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #5926 from fsateler/condition-uid
authorLennart Poettering <lennart@poettering.net>
Mon, 29 May 2017 13:18:38 +0000 (15:18 +0200)
committerGitHub <noreply@github.com>
Mon, 29 May 2017 13:18:38 +0000 (15:18 +0200)
core: add ConditionUID and ConditionGID

man/systemd.unit.xml
src/core/load-fragment-gperf.gperf.m4
src/shared/condition.c
src/shared/condition.h
src/test/test-condition.c

index 59d16423be965b5509f6fc73e05eb1b404692ff3..441378936a73f633dae027f5be0ab2d739c7880d 100644 (file)
         <term><varname>ConditionDirectoryNotEmpty=</varname></term>
         <term><varname>ConditionFileNotEmpty=</varname></term>
         <term><varname>ConditionFileIsExecutable=</varname></term>
+        <term><varname>ConditionUser=</varname></term>
+        <term><varname>ConditionGroup=</varname></term>
 
         <!-- We do not document ConditionNull=
              here, as it is not particularly
         whether a certain path exists, is a regular file and marked
         executable.</para>
 
+        <para><varname>ConditionUser=</varname> takes a numeric
+        <literal>UID</literal>, a UNIX user name, or the special value
+        <literal>@system</literal>. This condition may be used to check
+        whether the service manager is running as the given user. The
+        special value <literal>@system</literal> can be used to check
+        if the user id is within the system user range. This option is not
+        useful for system services, as the system manager exclusively
+        runs as the root user, and thus the test result is constant.</para>
+
+        <para><varname>ConditionGroup=</varname> is similar
+        to <varname>ConditionUser=</varname> but verifies that the
+        service manager's real or effective group, or any of its
+        auxiliary groups match the specified group or GID. This setting
+        does not have a special value <literal>@system</literal>.</para>
+
         <para>If multiple conditions are specified, the unit will be
         executed if all of them apply (i.e. a logical AND is applied).
         Condition checks can be prefixed with a pipe symbol (|) in
         <term><varname>AssertDirectoryNotEmpty=</varname></term>
         <term><varname>AssertFileNotEmpty=</varname></term>
         <term><varname>AssertFileIsExecutable=</varname></term>
+        <term><varname>AssertUser=</varname></term>
+        <term><varname>AssertGroup=</varname></term>
 
         <listitem><para>Similar to the <varname>ConditionArchitecture=</varname>,
         <varname>ConditionVirtualization=</varname>, …, condition settings described above, these settings add
index 566e3f8400b1accbe0bd2598a646067968062ac8..c43b7885be71f7df8d3d0c4aa92e07b1b1390ada 100644 (file)
@@ -221,6 +221,8 @@ Unit.ConditionSecurity,          config_parse_unit_condition_string, CONDITION_S
 Unit.ConditionCapability,        config_parse_unit_condition_string, CONDITION_CAPABILITY,          offsetof(Unit, conditions)
 Unit.ConditionHost,              config_parse_unit_condition_string, CONDITION_HOST,                offsetof(Unit, conditions)
 Unit.ConditionACPower,           config_parse_unit_condition_string, CONDITION_AC_POWER,            offsetof(Unit, conditions)
+Unit.ConditionUser,              config_parse_unit_condition_string, CONDITION_USER,                offsetof(Unit, conditions)
+Unit.ConditionGroup,             config_parse_unit_condition_string, CONDITION_GROUP,               offsetof(Unit, conditions)
 Unit.ConditionNull,              config_parse_unit_condition_null,   0,                             offsetof(Unit, conditions)
 Unit.AssertPathExists,           config_parse_unit_condition_path,   CONDITION_PATH_EXISTS,         offsetof(Unit, asserts)
 Unit.AssertPathExistsGlob,       config_parse_unit_condition_path,   CONDITION_PATH_EXISTS_GLOB,    offsetof(Unit, asserts)
@@ -240,6 +242,8 @@ Unit.AssertSecurity,             config_parse_unit_condition_string, CONDITION_S
 Unit.AssertCapability,           config_parse_unit_condition_string, CONDITION_CAPABILITY,          offsetof(Unit, asserts)
 Unit.AssertHost,                 config_parse_unit_condition_string, CONDITION_HOST,                offsetof(Unit, asserts)
 Unit.AssertACPower,              config_parse_unit_condition_string, CONDITION_AC_POWER,            offsetof(Unit, asserts)
+Unit.AssertUser,                 config_parse_unit_condition_string, CONDITION_USER,                offsetof(Unit, asserts)
+Unit.AssertGroup,                config_parse_unit_condition_string, CONDITION_GROUP,               offsetof(Unit, asserts)
 Unit.AssertNull,                 config_parse_unit_condition_null,   0,                             offsetof(Unit, asserts)
 m4_dnl
 Service.PIDFile,                 config_parse_unit_path_printf,      0,                             offsetof(Service, pid_file)
index aec7ebb9cfef8f35f09c7d7b183f97c822d235b7..1af74c61f00bd76c197f06570ab2e259e3ffeeef 100644 (file)
@@ -24,6 +24,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
+#include <sys/types.h>
 #include <time.h>
 #include <unistd.h>
 
@@ -52,6 +53,7 @@
 #include "stat-util.h"
 #include "string-table.h"
 #include "string-util.h"
+#include "user-util.h"
 #include "util.h"
 #include "virt.h"
 
@@ -138,6 +140,60 @@ static int condition_test_kernel_command_line(Condition *c) {
         return false;
 }
 
+static int condition_test_user(Condition *c) {
+        uid_t id;
+        int r;
+        _cleanup_free_ char *username = NULL;
+        const char *u;
+
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_USER);
+
+        r = parse_uid(c->parameter, &id);
+        if (r >= 0)
+                return id == getuid() || id == geteuid();
+
+        if (streq("@system", c->parameter))
+                return getuid() <= SYSTEM_UID_MAX || geteuid() <= SYSTEM_UID_MAX;
+
+        username = getusername_malloc();
+        if (!username)
+                return -ENOMEM;
+
+        if (streq(username, c->parameter))
+                return 1;
+
+        if (getpid() == 1)
+                return streq(c->parameter, "root");
+
+        u = c->parameter;
+        r = get_user_creds(&u, &id, NULL, NULL, NULL);
+        if (r < 0)
+                return 0;
+
+        return id == getuid() || id == geteuid();
+}
+
+static int condition_test_group(Condition *c) {
+        gid_t id;
+        int r;
+
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_GROUP);
+
+        r = parse_gid(c->parameter, &id);
+        if (r >= 0)
+                return in_gid(id);
+
+        /* Avoid any NSS lookups if we are PID1 */
+        if (getpid() == 1)
+                return streq(c->parameter, "root");
+
+        return in_group(c->parameter) > 0;
+}
+
 static int condition_test_virtualization(Condition *c) {
         int b, v;
 
@@ -475,6 +531,8 @@ int condition_test(Condition *c) {
                 [CONDITION_ARCHITECTURE] = condition_test_architecture,
                 [CONDITION_NEEDS_UPDATE] = condition_test_needs_update,
                 [CONDITION_FIRST_BOOT] = condition_test_first_boot,
+                [CONDITION_USER] = condition_test_user,
+                [CONDITION_GROUP] = condition_test_group,
                 [CONDITION_NULL] = condition_test_null,
         };
 
@@ -538,6 +596,8 @@ static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
         [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
         [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
         [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
+        [CONDITION_USER] = "ConditionUser",
+        [CONDITION_GROUP] = "ConditionGroup",
         [CONDITION_NULL] = "ConditionNull"
 };
 
@@ -562,6 +622,8 @@ static const char* const assert_type_table[_CONDITION_TYPE_MAX] = {
         [CONDITION_DIRECTORY_NOT_EMPTY] = "AssertDirectoryNotEmpty",
         [CONDITION_FILE_NOT_EMPTY] = "AssertFileNotEmpty",
         [CONDITION_FILE_IS_EXECUTABLE] = "AssertFileIsExecutable",
+        [CONDITION_USER] = "AssertUser",
+        [CONDITION_GROUP] = "AssertGroup",
         [CONDITION_NULL] = "AssertNull"
 };
 
index bdda04b7702b0a593cf606a9a401a28bd35165e8..d0b592bc433d43e4d04df89f89d36e173759da8c 100644 (file)
@@ -49,6 +49,9 @@ typedef enum ConditionType {
 
         CONDITION_NULL,
 
+        CONDITION_USER,
+        CONDITION_GROUP,
+
         _CONDITION_TYPE_MAX,
         _CONDITION_TYPE_INVALID = -1
 } ConditionType;
index 3bb7f9223041660407a74d284e3f708ac543acdc..121345cfd1c72a4a666f812fd3c954ff814af8b8 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+
 #include "sd-id128.h"
 
 #include "alloc-util.h"
@@ -34,6 +38,7 @@
 #include "strv.h"
 #include "virt.h"
 #include "util.h"
+#include "user-util.h"
 
 static void test_condition_test_path(void) {
         Condition *condition;
@@ -323,6 +328,150 @@ static void test_condition_test_virtualization(void) {
         }
 }
 
+static void test_condition_test_user(void) {
+        Condition *condition;
+        char* uid;
+        char* username;
+        int r;
+
+        condition = condition_new(CONDITION_USER, "garbage oifdsjfoidsjoj", false, false);
+        assert_se(condition);
+        r = condition_test(condition);
+        log_info("ConditionUser=garbage → %i", r);
+        assert_se(r == 0);
+        condition_free(condition);
+
+        assert_se(asprintf(&uid, "%"PRIu32, UINT32_C(0xFFFF)) > 0);
+        condition = condition_new(CONDITION_USER, uid, false, false);
+        assert_se(condition);
+        r = condition_test(condition);
+        log_info("ConditionUser=%s → %i", uid, r);
+        assert_se(r == 0);
+        condition_free(condition);
+        free(uid);
+
+        assert_se(asprintf(&uid, "%u", (unsigned)getuid()) > 0);
+        condition = condition_new(CONDITION_USER, uid, false, false);
+        assert_se(condition);
+        r = condition_test(condition);
+        log_info("ConditionUser=%s → %i", uid, r);
+        assert_se(r > 0);
+        condition_free(condition);
+        free(uid);
+
+        assert_se(asprintf(&uid, "%u", (unsigned)getuid()+1) > 0);
+        condition = condition_new(CONDITION_USER, uid, false, false);
+        assert_se(condition);
+        r = condition_test(condition);
+        log_info("ConditionUser=%s → %i", uid, r);
+        assert_se(r == 0);
+        condition_free(condition);
+        free(uid);
+
+        username = getusername_malloc();
+        assert_se(username);
+        condition = condition_new(CONDITION_USER, username, false, false);
+        assert_se(condition);
+        r = condition_test(condition);
+        log_info("ConditionUser=%s → %i", username, r);
+        assert_se(r > 0);
+        condition_free(condition);
+        free(username);
+
+        username = (char*)(geteuid() == 0 ? NOBODY_USER_NAME : "root");
+        condition = condition_new(CONDITION_USER, username, false, false);
+        assert_se(condition);
+        r = condition_test(condition);
+        log_info("ConditionUser=%s → %i", username, r);
+        assert_se(r == 0);
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_USER, "@system", false, false);
+        assert_se(condition);
+        r = condition_test(condition);
+        log_info("ConditionUser=@system → %i", r);
+        if (geteuid() == 0)
+                assert_se(r > 0);
+        else
+                assert_se(r == 0);
+        condition_free(condition);
+}
+
+static void test_condition_test_group(void) {
+        Condition *condition;
+        char* gid;
+        char* groupname;
+        gid_t *gids, max_gid;
+        int ngroups_max, r, i;
+
+        assert_se(0 < asprintf(&gid, "%u", UINT32_C(0xFFFF)));
+        condition = condition_new(CONDITION_GROUP, gid, false, false);
+        assert_se(condition);
+        r = condition_test(condition);
+        log_info("ConditionGroup=%s → %i", gid, r);
+        assert_se(r == 0);
+        condition_free(condition);
+        free(gid);
+
+        assert_se(0 < asprintf(&gid, "%u", getgid()));
+        condition = condition_new(CONDITION_GROUP, gid, false, false);
+        assert_se(condition);
+        r = condition_test(condition);
+        log_info("ConditionGroup=%s → %i", gid, r);
+        assert_se(r > 0);
+        condition_free(condition);
+        free(gid);
+
+        ngroups_max = sysconf(_SC_NGROUPS_MAX);
+        assert(ngroups_max > 0);
+
+        gids = alloca(sizeof(gid_t) * ngroups_max);
+
+        r = getgroups(ngroups_max, gids);
+        assert(r >= 0);
+
+        max_gid = getgid();
+        for (i = 0; i < r; i++) {
+                assert_se(0 < asprintf(&gid, "%u", gids[i]));
+                condition = condition_new(CONDITION_GROUP, gid, false, false);
+                assert_se(condition);
+                r = condition_test(condition);
+                log_info("ConditionGroup=%s → %i", gid, r);
+                assert_se(r > 0);
+                condition_free(condition);
+                free(gid);
+                max_gid = gids[i] > max_gid ? gids[i] : max_gid;
+
+                groupname = gid_to_name(gids[i]);
+                assert_se(groupname);
+                condition = condition_new(CONDITION_GROUP, groupname, false, false);
+                assert_se(condition);
+                r = condition_test(condition);
+                log_info("ConditionGroup=%s → %i", groupname, r);
+                assert_se(r > 0);
+                condition_free(condition);
+                free(groupname);
+                max_gid = gids[i] > max_gid ? gids[i] : max_gid;
+        }
+
+        assert_se(0 < asprintf(&gid, "%u", max_gid+1));
+        condition = condition_new(CONDITION_GROUP, gid, false, false);
+        assert_se(condition);
+        r = condition_test(condition);
+        log_info("ConditionGroup=%s → %i", gid, r);
+        assert_se(r == 0);
+        condition_free(condition);
+        free(gid);
+
+        groupname = (char*)(geteuid() == 0 ? NOBODY_GROUP_NAME : "root");
+        condition = condition_new(CONDITION_GROUP, groupname, false, false);
+        assert_se(condition);
+        r = condition_test(condition);
+        log_info("ConditionGroup=%s → %i", groupname, r);
+        assert_se(r == 0);
+        condition_free(condition);
+}
+
 int main(int argc, char *argv[]) {
         log_set_max_level(LOG_DEBUG);
         log_parse_environment();
@@ -336,6 +485,8 @@ int main(int argc, char *argv[]) {
         test_condition_test_null();
         test_condition_test_security();
         test_condition_test_virtualization();
+        test_condition_test_user();
+        test_condition_test_group();
 
         return 0;
 }