diff options
author | Lennart Poettering <lennart@poettering.net> | 2017-05-29 15:18:38 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-05-29 15:18:38 +0200 |
commit | defdbbb6dc62b7985072bfb68e7f8ef3f4c75246 (patch) | |
tree | c5c1b1188bcd4e4b3fcf8862f7fbcdc1076b0e75 | |
parent | Merge pull request #6031 from teg/monitor (diff) | |
parent | core: add @system special value to ConditionUser= (diff) | |
download | systemd-defdbbb6dc62b7985072bfb68e7f8ef3f4c75246.tar.xz systemd-defdbbb6dc62b7985072bfb68e7f8ef3f4c75246.zip |
Merge pull request #5926 from fsateler/condition-uid
core: add ConditionUID and ConditionGID
-rw-r--r-- | man/systemd.unit.xml | 19 | ||||
-rw-r--r-- | src/core/load-fragment-gperf.gperf.m4 | 4 | ||||
-rw-r--r-- | src/shared/condition.c | 62 | ||||
-rw-r--r-- | src/shared/condition.h | 3 | ||||
-rw-r--r-- | src/test/test-condition.c | 151 |
5 files changed, 239 insertions, 0 deletions
diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index 59d16423be..441378936a 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -820,6 +820,8 @@ <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 @@ -1033,6 +1035,21 @@ 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 @@ -1069,6 +1086,8 @@ <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 diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 566e3f8400..c43b7885be 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -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) diff --git a/src/shared/condition.c b/src/shared/condition.c index aec7ebb9cf..1af74c61f0 100644 --- a/src/shared/condition.c +++ b/src/shared/condition.c @@ -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" }; diff --git a/src/shared/condition.h b/src/shared/condition.h index bdda04b770..d0b592bc43 100644 --- a/src/shared/condition.h +++ b/src/shared/condition.h @@ -49,6 +49,9 @@ typedef enum ConditionType { CONDITION_NULL, + CONDITION_USER, + CONDITION_GROUP, + _CONDITION_TYPE_MAX, _CONDITION_TYPE_INVALID = -1 } ConditionType; diff --git a/src/test/test-condition.c b/src/test/test-condition.c index 3bb7f92230..121345cfd1 100644 --- a/src/test/test-condition.c +++ b/src/test/test-condition.c @@ -17,6 +17,10 @@ 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; } |