[systemd-commits] 2 commits - .gitignore Makefile.am man/systemd.socket.xml man/sysusers.d.xml src/core src/shared src/sysusers src/test
Lennart Poettering
lennart at kemper.freedesktop.org
Tue Aug 19 10:16:57 PDT 2014
.gitignore | 1
Makefile.am | 11 +
man/systemd.socket.xml | 11 -
man/sysusers.d.xml | 31 +++++
src/core/execute.c | 23 ---
src/core/execute.h | 1
src/core/load-fragment-gperf.gperf.m4 | 3
src/core/socket.c | 22 ---
src/core/socket.h | 2
src/shared/label.c | 69 -----------
src/shared/label.h | 1
src/shared/uid-range.c | 205 +++++++++++++++++++++++++++++++++
src/shared/uid-range.h | 34 +++++
src/sysusers/sysusers.c | 207 ++++++++++++++++++++++------------
src/test/test-uid-range.c | 91 ++++++++++++++
15 files changed, 512 insertions(+), 200 deletions(-)
New commits:
commit 3bb07b7680c543c982077ac075abe8badeb46ca1
Author: Lennart Poettering <lennart at poettering.net>
Date: Tue Aug 19 19:16:08 2014 +0200
Revert "socket: introduce SELinuxLabelViaNet option"
This reverts commit cf8bd44339b00330fdbc91041d6731ba8aba9fec.
Needs more discussion on the mailing list.
diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml
index f376f72..4483905 100644
--- a/man/systemd.socket.xml
+++ b/man/systemd.socket.xml
@@ -676,17 +676,6 @@
</varlistentry>
<varlistentry>
- <term><varname>SELinuxLabelViaNet=</varname></term>
- <listitem><para>Takes a boolean
- value. Controls whether systemd attempts to figure out
- SELinux label used for instantiated service from
- information handed by peer over the
- network. Configuration option has effect only
- on sockets with <literal>Accept=</literal>
- mode set to <literal>yes</literal>.</para></listitem>
- </varlistentry>
-
- <varlistentry>
<term><varname>PipeSize=</varname></term>
<listitem><para>Takes a size in
bytes. Controls the pipe buffer size
diff --git a/src/core/execute.c b/src/core/execute.c
index 1297912..d8452a6 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -83,7 +83,6 @@
#include "af-list.h"
#include "mkdir.h"
#include "apparmor-util.h"
-#include "label.h"
#ifdef HAVE_SECCOMP
#include "seccomp-util.h"
@@ -1730,22 +1729,6 @@ int exec_spawn(ExecCommand *command,
goto fail_child;
}
}
-
- if (context->selinux_label_via_net && use_selinux()) {
- _cleanup_free_ char *label = NULL;
-
- err = label_get_child_label(socket_fd, command->path, &label);
- if (err < 0) {
- r = EXIT_SELINUX_CONTEXT;
- goto fail_child;
- }
-
- err = setexeccon(label);
- if (err < 0) {
- r = EXIT_SELINUX_CONTEXT;
- goto fail_child;
- }
- }
#endif
#ifdef HAVE_APPARMOR
@@ -2129,8 +2112,7 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
"%sPrivateDevices: %s\n"
"%sProtectHome: %s\n"
"%sProtectSystem: %s\n"
- "%sIgnoreSIGPIPE: %s\n"
- "%sSELinuxLabelViaNet: %s\n",
+ "%sIgnoreSIGPIPE: %s\n",
prefix, c->umask,
prefix, c->working_directory ? c->working_directory : "/",
prefix, c->root_directory ? c->root_directory : "/",
@@ -2140,8 +2122,7 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
prefix, yes_no(c->private_devices),
prefix, protect_home_to_string(c->protect_home),
prefix, protect_system_to_string(c->protect_system),
- prefix, yes_no(c->ignore_sigpipe),
- prefix, yes_no(c->selinux_label_via_net));
+ prefix, yes_no(c->ignore_sigpipe));
STRV_FOREACH(e, c->environment)
fprintf(f, "%sEnvironment: %s\n", prefix, *e);
diff --git a/src/core/execute.h b/src/core/execute.h
index d23a980..9d05d3a 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -136,7 +136,6 @@ struct ExecContext {
bool selinux_context_ignore;
char *selinux_context;
- bool selinux_label_via_net;
bool apparmor_profile_ignore;
char *apparmor_profile;
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index d5ff848..b4e2b25 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -262,9 +262,6 @@ Socket.SmackLabelIPOut, config_parse_string, 0,
`Socket.SmackLabel, config_parse_warn_compat, 0, 0
Socket.SmackLabelIPIn, config_parse_warn_compat, 0, 0
Socket.SmackLabelIPOut, config_parse_warn_compat, 0, 0')
-m4_ifdef(`HAVE_SELINUX',
-`Socket.SELinuxLabelViaNet, config_parse_bool, 0, offsetof(Socket, selinux_label_via_net)',
-`Socket.SELinuxLabelViaNet, config_parse_warn_compat, 0, 0')
EXEC_CONTEXT_CONFIG_ITEMS(Socket)m4_dnl
CGROUP_CONTEXT_CONFIG_ITEMS(Socket)m4_dnl
KILL_CONTEXT_CONFIG_ITEMS(Socket)m4_dnl
diff --git a/src/core/socket.c b/src/core/socket.c
index 34ce1b1..a16b20d 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -31,10 +31,6 @@
#include <mqueue.h>
#include <sys/xattr.h>
-#ifdef HAVE_SELINUX
-#include <selinux/selinux.h>
-#endif
-
#include "sd-event.h"
#include "log.h"
#include "load-dropin.h"
@@ -492,8 +488,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
"%sPassCredentials: %s\n"
"%sPassSecurity: %s\n"
"%sTCPCongestion: %s\n"
- "%sRemoveOnStop: %s\n"
- "%sSELinuxLabelViaNet: %s\n",
+ "%sRemoveOnStop: %s\n",
prefix, socket_state_to_string(s->state),
prefix, socket_result_to_string(s->result),
prefix, socket_address_bind_ipv6_only_to_string(s->bind_ipv6_only),
@@ -508,8 +503,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
prefix, yes_no(s->pass_cred),
prefix, yes_no(s->pass_sec),
prefix, strna(s->tcp_congestion),
- prefix, yes_no(s->remove_on_stop),
- prefix, yes_no(s->selinux_label_via_net));
+ prefix, yes_no(s->remove_on_stop));
if (s->control_pid > 0)
fprintf(f,
@@ -1136,14 +1130,7 @@ static int socket_open_fds(Socket *s) {
continue;
if (p->type == SOCKET_SOCKET) {
-#ifdef HAVE_SELINUX
- if (!know_label && s->selinux_label_via_net) {
- r = getcon(&label);
- if (r < 0)
- return r;
- know_label = true;
- }
-#endif
+
if (!know_label) {
r = socket_instantiate_service(s);
@@ -1842,9 +1829,6 @@ static void socket_enter_running(Socket *s, int cfd) {
cfd = -1;
s->n_connections ++;
- if (s->selinux_label_via_net)
- service->exec_context.selinux_label_via_net = true;
-
r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, true, &error, NULL);
if (r < 0)
goto fail;
diff --git a/src/core/socket.h b/src/core/socket.h
index ab342c3..eede705 100644
--- a/src/core/socket.h
+++ b/src/core/socket.h
@@ -165,8 +165,6 @@ struct Socket {
char *smack_ip_in;
char *smack_ip_out;
- bool selinux_label_via_net;
-
char *user, *group;
};
diff --git a/src/shared/label.c b/src/shared/label.c
index dd89bec..25a8b36 100644
--- a/src/shared/label.c
+++ b/src/shared/label.c
@@ -31,7 +31,6 @@
#ifdef HAVE_SELINUX
#include <selinux/selinux.h>
#include <selinux/label.h>
-#include <selinux/context.h>
#endif
#include "label.h"
@@ -244,74 +243,6 @@ fail:
return r;
}
-int label_get_child_label(int socket_fd, const char *exe, char **label) {
- int r = 0;
-
-#ifdef HAVE_SELINUX
-
- security_context_t mycon = NULL, peercon = NULL, fcon = NULL, ret = NULL;
- security_class_t sclass;
- context_t pcon = NULL, bcon = NULL;
- const char *range = NULL;
-
- assert(socket_fd >= 0);
- assert(exe);
- assert(label);
-
- r = getcon(&mycon);
- if (r < 0)
- goto out;
-
- r = getpeercon(socket_fd, &peercon);
- if (r < 0)
- goto out;
-
- r = getfilecon(exe, &fcon);
- if (r < 0)
- goto out;
-
- bcon = context_new(mycon);
- if (!bcon)
- goto out;
-
- pcon = context_new(peercon);
- if (!pcon)
- goto out;
-
- range = context_range_get(pcon);
- if (!range)
- goto out;
-
- r = context_range_set(bcon, range);
- if (r)
- goto out;
-
- freecon(mycon);
- mycon = context_str(bcon);
- if (!mycon)
- goto out;
-
- sclass = string_to_security_class("process");
- r = security_compute_create(mycon, fcon, sclass, &ret);
- if (r < 0)
- goto out;
-
- *label = ret;
-
-out:
- if (r && security_getenforce() == 1)
- r = -errno;
-
- freecon(mycon);
- freecon(peercon);
- freecon(fcon);
- context_free(pcon);
- context_free(bcon);
-
-#endif
- return r;
-}
-
int label_context_set(const char *path, mode_t mode) {
int r = 0;
diff --git a/src/shared/label.h b/src/shared/label.h
index 4163f7f..7294820 100644
--- a/src/shared/label.h
+++ b/src/shared/label.h
@@ -39,7 +39,6 @@ void label_context_clear(void);
void label_free(const char *label);
int label_get_create_label_from_exe(const char *exe, char **label);
-int label_get_child_label(int socket_fd, const char *exec, char **label);
int label_mkdir(const char *path, mode_t mode);
commit 8530dc4467691a893aa2e07319b18a84fec96cad
Author: Lennart Poettering <lennart at poettering.net>
Date: Tue Aug 19 19:05:11 2014 +0200
tmpfiles: add new 'r' line type to add UIDs/GIDs to the pool to allocate UIDs/GIDs from
This way we can guarantee a limited amount of compatibility with
login.defs, by generate an appopriate "r" line out of it, on package
installation.
diff --git a/.gitignore b/.gitignore
index a3d8c4e..8189da7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -228,6 +228,7 @@
/test-time
/test-tmpfiles
/test-udev
+/test-uid-range
/test-unifont
/test-unit-file
/test-unit-name
diff --git a/Makefile.am b/Makefile.am
index 3ef9c24..d4d5775 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -862,6 +862,8 @@ libsystemd_shared_la_SOURCES = \
src/shared/base-filesystem.h \
src/shared/memfd.c \
src/shared/memfd.h \
+ src/shared/uid-range.c \
+ src/shared/uid-range.h \
src/shared/nss-util.h
nodist_libsystemd_shared_la_SOURCES = \
@@ -1322,7 +1324,8 @@ tests += \
test-capability \
test-async \
test-ratelimit \
- test-condition-util
+ test-condition-util \
+ test-uid-range
EXTRA_DIST += \
test/a.service \
@@ -1487,6 +1490,12 @@ test_util_SOURCES = \
test_util_LDADD = \
libsystemd-core.la
+test_uid_range_SOURCES = \
+ src/test/test-uid-range.c
+
+test_uid_range_LDADD = \
+ libsystemd-core.la
+
test_socket_util_SOURCES = \
src/test/test-socket-util.c
diff --git a/man/sysusers.d.xml b/man/sysusers.d.xml
index 1832ecf..18c71db 100644
--- a/man/sysusers.d.xml
+++ b/man/sysusers.d.xml
@@ -134,6 +134,25 @@ u root 0 "Superuser" /root</programlisting>
will be implicitly
created.</para></listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>r</varname></term>
+ <listitem><para>Add a range of
+ numeric UIDs/GIDs to the pool
+ to allocate new UIDs and GIDs
+ from. If no line of this type
+ is specified the range of
+ UIDs/GIDs is set to some
+ compiled-in default. Note that
+ both UIDs and GIDs are
+ allocated from the same pool,
+ in order to ensure that users
+ and groups of the same name
+ are likely to carry the same
+ numeric UID and
+ GID.</para></listitem>
+ </varlistentry>
+
</variablelist>
</refsect2>
@@ -154,6 +173,10 @@ u root 0 "Superuser" /root</programlisting>
<para>For <varname>m</varname> lines this
field should contain the user name to add to a
group.</para>
+
+ <para>For lines of type <varname>r</varname>
+ this field should be set to
+ <literal>-</literal>.</para>
</refsect2>
<refsect2>
@@ -175,6 +198,14 @@ u root 0 "Superuser" /root</programlisting>
<para>For <varname>m</varname> lines this
field should contain the group name to add to
a user to.</para>
+
+ <para>For lines of type <varname>r</varname>
+ this field should be set to a UID/GID range in
+ the format <literal>FROM-TO</literal> where
+ both values are formatted as decimal ASCII
+ numbers. Alternatively, a single UID/GID may
+ be specified formatted as decimal ASCII
+ numbers.</para>
</refsect2>
<refsect2>
diff --git a/src/shared/uid-range.c b/src/shared/uid-range.c
new file mode 100644
index 0000000..74c3be4
--- /dev/null
+++ b/src/shared/uid-range.c
@@ -0,0 +1,205 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "util.h"
+#include "uid-range.h"
+
+static bool uid_range_intersect(UidRange *range, uid_t start, uid_t nr) {
+ assert(range);
+
+ return range->start <= start + nr &&
+ range->start + range->nr >= start;
+}
+
+static void uid_range_coalesce(UidRange **p, unsigned *n) {
+ unsigned i, j;
+
+ assert(p);
+ assert(n);
+
+ for (i = 0; i < *n; i++) {
+ for (j = i + 1; j < *n; j++) {
+ UidRange *x = (*p)+i, *y = (*p)+j;
+
+ if (uid_range_intersect(x, y->start, y->nr)) {
+ uid_t begin, end;
+
+ begin = MIN(x->start, y->start);
+ end = MAX(x->start + x->nr, y->start + y->nr);
+
+ x->start = begin;
+ x->nr = end - begin;
+
+ if (*n > j+1)
+ memmove(y, y+1, sizeof(UidRange) * (*n - j -1));
+
+ (*n) --;
+ j--;
+ }
+ }
+ }
+
+}
+
+static int uid_range_compare(const void *a, const void *b) {
+ const UidRange *x = a, *y = b;
+
+ if (x->start < y->start)
+ return -1;
+ if (x->start > y->start)
+ return 1;
+
+ if (x->nr < y->nr)
+ return -1;
+ if (x->nr > y->nr)
+ return 1;
+
+ return 0;
+}
+
+int uid_range_add(UidRange **p, unsigned *n, uid_t start, uid_t nr) {
+ bool found = false;
+ UidRange *x;
+ unsigned i;
+
+ assert(p);
+ assert(n);
+
+ if (nr <= 0)
+ return 0;
+
+ for (i = 0; i < *n; i++) {
+ x = (*p) + i;
+ if (uid_range_intersect(x, start, nr)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ uid_t begin, end;
+
+ begin = MIN(x->start, start);
+ end = MAX(x->start + x->nr, start + nr);
+
+ x->start = begin;
+ x->nr = end - begin;
+ } else {
+ UidRange *t;
+
+ t = realloc(*p, sizeof(UidRange) * (*n + 1));
+ if (!t)
+ return -ENOMEM;
+
+ *p = t;
+ x = t + ((*n) ++);
+
+ x->start = start;
+ x->nr = nr;
+ }
+
+ qsort(*p, *n, sizeof(UidRange), uid_range_compare);
+ uid_range_coalesce(p, n);
+
+ return *n;
+}
+
+int uid_range_add_str(UidRange **p, unsigned *n, const char *s) {
+ uid_t start, nr;
+ const char *t;
+ int r;
+
+ assert(p);
+ assert(n);
+ assert(s);
+
+ t = strchr(s, '-');
+ if (t) {
+ char *b;
+ uid_t end;
+
+ b = strndupa(s, t - s);
+ r = parse_uid(b, &start);
+ if (r < 0)
+ return r;
+
+ r = parse_uid(t+1, &end);
+ if (r < 0)
+ return r;
+
+ if (end < start)
+ return -EINVAL;
+
+ nr = end - start + 1;
+ } else {
+ r = parse_uid(s, &start);
+ if (r < 0)
+ return r;
+
+ nr = 1;
+ }
+
+ return uid_range_add(p, n, start, nr);
+}
+
+int uid_range_next_lower(const UidRange *p, unsigned n, uid_t *uid) {
+ uid_t closest = (uid_t) -1, candidate;
+ unsigned i;
+
+ assert(p);
+ assert(uid);
+
+ candidate = *uid - 1;
+
+ for (i = 0; i < n; i++) {
+ uid_t begin, end;
+
+ begin = p[i].start;
+ end = p[i].start + p[i].nr - 1;
+
+ if (candidate >= begin && candidate <= end) {
+ *uid = candidate;
+ return 1;
+ }
+
+ if (end < candidate)
+ closest = end;
+ }
+
+ if (closest == (uid_t) -1)
+ return -EBUSY;
+
+ *uid = closest;
+ return 1;
+}
+
+bool uid_range_contains(const UidRange *p, unsigned n, uid_t uid) {
+ unsigned i;
+
+ assert(p);
+ assert(uid);
+
+ for (i = 0; i < n; i++)
+ if (uid >= p[i].start && uid < p[i].start + p[i].nr)
+ return true;
+
+ return false;
+}
diff --git a/src/shared/uid-range.h b/src/shared/uid-range.h
new file mode 100644
index 0000000..d3dac8d
--- /dev/null
+++ b/src/shared/uid-range.h
@@ -0,0 +1,34 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+
+typedef struct UidRange {
+ uid_t start, nr;
+} UidRange;
+
+int uid_range_add(UidRange **p, unsigned *n, uid_t start, uid_t nr);
+int uid_range_add_str(UidRange **p, unsigned *n, const char *s);
+
+int uid_range_next_lower(const UidRange *p, unsigned n, uid_t *uid);
+bool uid_range_contains(const UidRange *p, unsigned n, uid_t uid);
diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c
index eedc1e0..1ff0e21 100644
--- a/src/sysusers/sysusers.c
+++ b/src/sysusers/sysusers.c
@@ -38,11 +38,13 @@
#include "utf8.h"
#include "label.h"
#include "fileio-label.h"
+#include "uid-range.h"
typedef enum ItemType {
ADD_USER = 'u',
ADD_GROUP = 'g',
ADD_MEMBER = 'm',
+ ADD_RANGE = 'r',
} ItemType;
typedef struct Item {
ItemType type;
@@ -82,8 +84,9 @@ static Hashmap *members = NULL;
static Hashmap *database_uid = NULL, *database_user = NULL;
static Hashmap *database_gid = NULL, *database_group = NULL;
-static uid_t search_uid = SYSTEM_UID_MAX;
-static gid_t search_gid = SYSTEM_GID_MAX;
+static uid_t search_uid = (uid_t) -1;
+static UidRange *uid_range = NULL;
+static unsigned n_uid_range = 0;
#define UID_TO_PTR(u) (ULONG_TO_PTR(u+1))
#define PTR_TO_UID(u) ((uid_t) (PTR_TO_ULONG(u)-1))
@@ -916,7 +919,7 @@ static int add_user(Item *i) {
if (read_id_from_file(i, &c, NULL) > 0) {
- if (c <= 0 || c > SYSTEM_UID_MAX)
+ if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c))
log_debug("User ID " UID_FMT " of file not suitable for %s.", c, i->name);
else {
r = uid_is_ok(c, i->name);
@@ -947,7 +950,12 @@ static int add_user(Item *i) {
/* And if that didn't work either, let's try to find a free one */
if (!i->uid_set) {
- for (; search_uid > 0; search_uid--) {
+ for (;;) {
+ r = uid_range_next_lower(uid_range, n_uid_range, &search_uid);
+ if (r < 0) {
+ log_error("No free user ID available for %s.", i->name);
+ return r;
+ }
r = uid_is_ok(search_uid, i->name);
if (r < 0) {
@@ -957,15 +965,8 @@ static int add_user(Item *i) {
break;
}
- if (search_uid <= 0) {
- log_error("No free user ID available for %s.", i->name);
- return -E2BIG;
- }
-
i->uid_set = true;
i->uid = search_uid;
-
- search_uid--;
}
r = hashmap_ensure_allocated(&todo_uids, trivial_hash_func, trivial_compare_func);
@@ -1083,7 +1084,7 @@ static int add_group(Item *i) {
if (read_id_from_file(i, NULL, &c) > 0) {
- if (c <= 0 || c > SYSTEM_GID_MAX)
+ if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c))
log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name);
else {
r = gid_is_ok(c);
@@ -1101,8 +1102,15 @@ static int add_group(Item *i) {
/* And if that didn't work either, let's try to find a free one */
if (!i->gid_set) {
- for (; search_gid > 0; search_gid--) {
- r = gid_is_ok(search_gid);
+ for (;;) {
+ /* We look for new GIDs in the UID pool! */
+ r = uid_range_next_lower(uid_range, n_uid_range, &search_uid);
+ if (r < 0) {
+ log_error("No free group ID available for %s.", i->name);
+ return r;
+ }
+
+ r = gid_is_ok(search_uid);
if (r < 0) {
log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
return r;
@@ -1110,15 +1118,8 @@ static int add_group(Item *i) {
break;
}
- if (search_gid <= 0) {
- log_error("No free group ID available for %s.", i->name);
- return -E2BIG;
- }
-
i->gid_set = true;
- i->gid = search_gid;
-
- search_gid--;
+ i->gid = search_uid;
}
r = hashmap_ensure_allocated(&todo_gids, trivial_hash_func, trivial_compare_func);
@@ -1384,7 +1385,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
{}
};
- _cleanup_free_ char *action = NULL, *name = NULL, *id = NULL, *resolved_name = NULL, *description = NULL, *home = NULL;
+ _cleanup_free_ char *action = NULL, *name = NULL, *id = NULL, *resolved_name = NULL, *resolved_id = NULL, *description = NULL, *home = NULL;
_cleanup_(item_freep) Item *i = NULL;
Item *existing;
Hashmap *h;
@@ -1417,66 +1418,113 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
return -EINVAL;
}
- if (!IN_SET(action[0], ADD_USER, ADD_GROUP, ADD_MEMBER)) {
+ if (!IN_SET(action[0], ADD_USER, ADD_GROUP, ADD_MEMBER, ADD_RANGE)) {
log_error("[%s:%u] Unknown command command type '%c'.", fname, line, action[0]);
return -EBADMSG;
}
/* Verify name */
- r = specifier_printf(name, specifier_table, NULL, &resolved_name);
- if (r < 0) {
- log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
- return r;
+ if (isempty(name) || streq(name, "-")) {
+ free(name);
+ name = NULL;
}
- if (!valid_user_group_name(resolved_name)) {
- log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_name);
- return -EINVAL;
+ if (name) {
+ r = specifier_printf(name, specifier_table, NULL, &resolved_name);
+ if (r < 0) {
+ log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
+ return r;
+ }
+
+ if (!valid_user_group_name(resolved_name)) {
+ log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_name);
+ return -EINVAL;
+ }
}
- /* Simplify remaining columns */
+ /* Verify id */
if (isempty(id) || streq(id, "-")) {
free(id);
id = NULL;
}
+ if (id) {
+ r = specifier_printf(id, specifier_table, NULL, &resolved_id);
+ if (r < 0) {
+ log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
+ return r;
+ }
+ }
+
+ /* Verify description */
if (isempty(description) || streq(description, "-")) {
free(description);
description = NULL;
}
+ if (description) {
+ if (!valid_gecos(description)) {
+ log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, description);
+ return -EINVAL;
+ }
+ }
+
+ /* Verify home */
if (isempty(home) || streq(home, "-")) {
free(home);
home = NULL;
}
+ if (home) {
+ if (!valid_home(home)) {
+ log_error("[%s:%u] '%s' is not a valid home directory field.", fname, line, home);
+ return -EINVAL;
+ }
+ }
+
switch (action[0]) {
- case ADD_MEMBER: {
- _cleanup_free_ char *resolved_id = NULL;
- char **l;
+ case ADD_RANGE:
+ if (resolved_name) {
+ log_error("[%s:%u] Lines of type 'r' don't take a name field.", fname, line);
+ return -EINVAL;
+ }
- /* Try to extend an existing member or group item */
+ if (!resolved_id) {
+ log_error("[%s:%u] Lines of type 'r' require a ID range in the third field.", fname, line);
+ return -EINVAL;
+ }
if (description) {
- log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname, line);
+ log_error("[%s:%u] Lines of type 'r' don't take a GECOS field.", fname, line);
return -EINVAL;
}
if (home) {
- log_error("[%s:%u] Lines of type 'm' don't take a home directory field.", fname, line);
+ log_error("[%s:%u] Lines of type 'r' don't take a home directory field.", fname, line);
return -EINVAL;
}
- if (!id) {
- log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname, line);
+ r = uid_range_add_str(&uid_range, &n_uid_range, resolved_id);
+ if (r < 0) {
+ log_error("[%s:%u] Invalid UID range %s.", fname, line, resolved_id);
return -EINVAL;
}
- r = specifier_printf(id, specifier_table, NULL, &resolved_id);
- if (r < 0) {
- log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
- return r;
+ return 0;
+
+ case ADD_MEMBER: {
+ char **l;
+
+ /* Try to extend an existing member or group item */
+ if (!name) {
+ log_error("[%s:%u] Lines of type 'm' require a user name in the second field.", fname, line);
+ return -EINVAL;
+ }
+
+ if (!resolved_id) {
+ log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname, line);
+ return -EINVAL;
}
if (!valid_user_group_name(resolved_id)) {
@@ -1484,6 +1532,16 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
return -EINVAL;
}
+ if (description) {
+ log_error("[%s:%u] Lines of type 'm' don't take a GECOS field.", fname, line);
+ return -EINVAL;
+ }
+
+ if (home) {
+ log_error("[%s:%u] Lines of type 'm' don't take a home directory field.", fname, line);
+ return -EINVAL;
+ }
+
r = hashmap_ensure_allocated(&members, string_hash_func, string_compare_func);
if (r < 0)
return log_oom();
@@ -1521,6 +1579,11 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
}
case ADD_USER:
+ if (!name) {
+ log_error("[%s:%u] Lines of type 'u' require a user name in the second field.", fname, line);
+ return -EINVAL;
+ }
+
r = hashmap_ensure_allocated(&users, string_hash_func, string_compare_func);
if (r < 0)
return log_oom();
@@ -1529,14 +1592,14 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
if (!i)
return log_oom();
- if (id) {
- if (path_is_absolute(id)) {
- i->uid_path = id;
- id = NULL;
+ if (resolved_id) {
+ if (path_is_absolute(resolved_id)) {
+ i->uid_path = resolved_id;
+ resolved_id = NULL;
path_kill_slashes(i->uid_path);
} else {
- r = parse_uid(id, &i->uid);
+ r = parse_uid(resolved_id, &i->uid);
if (r < 0) {
log_error("Failed to parse UID: %s", id);
return -EBADMSG;
@@ -1546,30 +1609,20 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
}
}
- if (description) {
- if (!valid_gecos(description)) {
- log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, description);
- return -EINVAL;
- }
-
- i->description = description;
- description = NULL;
- }
-
- if (home) {
- if (!valid_home(home)) {
- log_error("[%s:%u] '%s' is not a valid home directory field.", fname, line, home);
- return -EINVAL;
- }
+ i->description = description;
+ description = NULL;
- i->home = home;
- home = NULL;
- }
+ i->home = home;
+ home = NULL;
h = users;
break;
case ADD_GROUP:
+ if (!name) {
+ log_error("[%s:%u] Lines of type 'g' require a user name in the second field.", fname, line);
+ return -EINVAL;
+ }
if (description) {
log_error("[%s:%u] Lines of type 'g' don't take a GECOS field.", fname, line);
@@ -1589,14 +1642,14 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
if (!i)
return log_oom();
- if (id) {
- if (path_is_absolute(id)) {
- i->gid_path = id;
- id = NULL;
+ if (resolved_id) {
+ if (path_is_absolute(resolved_id)) {
+ i->gid_path = resolved_id;
+ resolved_id = NULL;
path_kill_slashes(i->gid_path);
} else {
- r = parse_gid(id, &i->gid);
+ r = parse_gid(resolved_id, &i->gid);
if (r < 0) {
log_error("Failed to parse GID: %s", id);
return -EBADMSG;
@@ -1608,6 +1661,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
h = groups;
break;
+
default:
return -EBADMSG;
}
@@ -1812,6 +1866,15 @@ int main(int argc, char *argv[]) {
}
}
+ if (!uid_range) {
+ /* Default to default range of 1..SYSTEMD_UID_MAX */
+ r = uid_range_add(&uid_range, &n_uid_range, 1, SYSTEM_UID_MAX);
+ if (r < 0) {
+ log_oom();
+ goto finish;
+ }
+ }
+
r = add_implicit();
if (r < 0)
goto finish;
diff --git a/src/test/test-uid-range.c b/src/test/test-uid-range.c
new file mode 100644
index 0000000..5f3d871
--- /dev/null
+++ b/src/test/test-uid-range.c
@@ -0,0 +1,91 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stddef.h>
+
+#include "util.h"
+#include "uid-range.h"
+
+int main(int argc, char *argv[]) {
+ _cleanup_free_ UidRange *p = NULL;
+ unsigned n = 0;
+ uid_t search;
+
+ assert_se(uid_range_add_str(&p, &n, "500-999") >= 0);
+ assert_se(n == 1);
+ assert_se(p[0].start == 500);
+ assert_se(p[0].nr = 500);
+
+ assert_se(!uid_range_contains(p, n, 499));
+ assert_se(uid_range_contains(p, n, 500));
+ assert_se(uid_range_contains(p, n, 999));
+ assert_se(!uid_range_contains(p, n, 1000));
+
+ search = (uid_t) -1;
+ assert_se(uid_range_next_lower(p, n, &search));
+ assert_se(search == 999);
+ assert_se(uid_range_next_lower(p, n, &search));
+ assert_se(search == 998);
+ search = 501;
+ assert_se(uid_range_next_lower(p, n, &search));
+ assert_se(search == 500);
+ assert_se(uid_range_next_lower(p, n, &search) == -EBUSY);
+
+ assert_se(uid_range_add_str(&p, &n, "1000") >= 0);
+ assert_se(n == 1);
+ assert_se(p[0].start == 500);
+ assert_se(p[0].nr = 501);
+
+ assert_se(uid_range_add_str(&p, &n, "30-40") >= 0);
+ assert_se(n == 2);
+ assert_se(p[0].start == 30);
+ assert_se(p[0].nr = 11);
+ assert_se(p[1].start == 500);
+ assert_se(p[1].nr = 501);
+
+ assert_se(uid_range_add_str(&p, &n, "60-70") >= 0);
+ assert_se(n == 3);
+ assert_se(p[0].start == 30);
+ assert_se(p[0].nr = 11);
+ assert_se(p[1].start == 60);
+ assert_se(p[1].nr = 11);
+ assert_se(p[2].start == 500);
+ assert_se(p[2].nr = 501);
+
+ assert_se(uid_range_add_str(&p, &n, "20-2000") >= 0);
+ assert_se(n == 1);
+ assert_se(p[0].start == 20);
+ assert_se(p[0].nr = 1981);
+
+ assert_se(uid_range_add_str(&p, &n, "2002") >= 0);
+ assert_se(n == 2);
+ assert_se(p[0].start == 20);
+ assert_se(p[0].nr = 1981);
+ assert_se(p[1].start == 2002);
+ assert_se(p[1].nr = 1);
+
+ assert_se(uid_range_add_str(&p, &n, "2001") >= 0);
+ assert_se(n == 1);
+ assert_se(p[0].start == 20);
+ assert_se(p[0].nr = 1983);
+
+ return 0;
+}
More information about the systemd-commits
mailing list