summaryrefslogtreecommitdiff
path: root/src/tmpfiles
diff options
context:
space:
mode:
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2020-05-08 11:22:19 +0200
committerGitHub <noreply@github.com>2020-05-08 11:22:19 +0200
commit165bda37badafcb5923dff2c7b028cb82c91dd69 (patch)
tree52b5b82a1b9655997471c0fc95e34360623b00e4 /src/tmpfiles
parent8acb7780df8da3e68bc62d913e7594ac40234f75 (diff)
parent77a3cec0c715b6b08f9c9aafbd16b52bc7bb3a80 (diff)
downloadsystemd-165bda37badafcb5923dff2c7b028cb82c91dd69.tar.gz
Merge pull request #15718 from poettering/tmpfiles-offline
tmpfiles: read /etc/passwd + /etc/group with fgetpwent()/fgetgrent() if --root= is specified
Diffstat (limited to 'src/tmpfiles')
-rw-r--r--src/tmpfiles/offline-passwd.c122
-rw-r--r--src/tmpfiles/offline-passwd.h9
-rw-r--r--src/tmpfiles/tmpfiles.c80
3 files changed, 199 insertions, 12 deletions
diff --git a/src/tmpfiles/offline-passwd.c b/src/tmpfiles/offline-passwd.c
new file mode 100644
index 0000000000..8ac5431c66
--- /dev/null
+++ b/src/tmpfiles/offline-passwd.c
@@ -0,0 +1,122 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "fd-util.h"
+#include "offline-passwd.h"
+#include "path-util.h"
+#include "user-util.h"
+
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(uid_gid_hash_ops, char, string_hash_func, string_compare_func, free);
+
+int name_to_uid_offline(
+ const char *root,
+ const char *user,
+ uid_t *ret_uid,
+ Hashmap **cache) {
+
+ void *found;
+ int r;
+
+ assert(user);
+ assert(ret_uid);
+ assert(cache);
+
+ if (!*cache) {
+ _cleanup_(hashmap_freep) Hashmap *uid_by_name = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ struct passwd *pw;
+ const char *passwd_path;
+
+ passwd_path = prefix_roota(root, "/etc/passwd");
+ f = fopen(passwd_path, "re");
+ if (!f)
+ return errno == ENOENT ? -ESRCH : -errno;
+
+ uid_by_name = hashmap_new(&uid_gid_hash_ops);
+ if (!uid_by_name)
+ return -ENOMEM;
+
+ while ((r = fgetpwent_sane(f, &pw)) > 0) {
+ _cleanup_free_ char *n = NULL;
+
+ n = strdup(pw->pw_name);
+ if (!n)
+ return -ENOMEM;
+
+ r = hashmap_put(uid_by_name, n, UID_TO_PTR(pw->pw_uid));
+ if (r == -EEXIST) {
+ log_warning_errno(r, "Duplicate entry in %s for %s: %m", passwd_path, pw->pw_name);
+ continue;
+ }
+ if (r < 0)
+ return r;
+
+ TAKE_PTR(n);
+ }
+
+ *cache = TAKE_PTR(uid_by_name);
+ }
+
+ found = hashmap_get(*cache, user);
+ if (!found)
+ return -ESRCH;
+
+ *ret_uid = PTR_TO_UID(found);
+ return 0;
+}
+
+int name_to_gid_offline(
+ const char *root,
+ const char *group,
+ gid_t *ret_gid,
+ Hashmap **cache) {
+
+ void *found;
+ int r;
+
+ assert(group);
+ assert(ret_gid);
+ assert(cache);
+
+ if (!*cache) {
+ _cleanup_(hashmap_freep) Hashmap *gid_by_name = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ struct group *gr;
+ const char *group_path;
+
+ group_path = prefix_roota(root, "/etc/group");
+ f = fopen(group_path, "re");
+ if (!f)
+ return errno == ENOENT ? -ESRCH : -errno;
+
+ gid_by_name = hashmap_new(&uid_gid_hash_ops);
+ if (!gid_by_name)
+ return -ENOMEM;
+
+ while ((r = fgetgrent_sane(f, &gr)) > 0) {
+ _cleanup_free_ char *n = NULL;
+
+ n = strdup(gr->gr_name);
+ if (!n)
+ return -ENOMEM;
+
+ r = hashmap_put(gid_by_name, n, GID_TO_PTR(gr->gr_gid));
+ if (r == -EEXIST) {
+ log_warning_errno(r, "Duplicate entry in %s for %s: %m", group_path, gr->gr_name);
+ continue;
+ }
+ if (r < 0)
+ return r;
+
+ TAKE_PTR(n);
+ }
+
+ *cache = TAKE_PTR(gid_by_name);
+ }
+
+ found = hashmap_get(*cache, group);
+ if (!found)
+ return -ESRCH;
+
+ *ret_gid = PTR_TO_GID(found);
+ return 0;
+}
diff --git a/src/tmpfiles/offline-passwd.h b/src/tmpfiles/offline-passwd.h
new file mode 100644
index 0000000000..90bdfc79be
--- /dev/null
+++ b/src/tmpfiles/offline-passwd.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <sys/types.h>
+
+#include "hashmap.h"
+
+int name_to_uid_offline(const char *root, const char *user, uid_t *ret_uid, Hashmap **cache);
+int name_to_gid_offline(const char *root, const char *group, gid_t *ret_gid, Hashmap **cache);
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index 2702b36bdd..e9453d3935 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -39,6 +39,7 @@
#include "main-func.h"
#include "mkdir.h"
#include "mountpoint-util.h"
+#include "offline-passwd.h"
#include "pager.h"
#include "parse-util.h"
#include "path-lookup.h"
@@ -2488,7 +2489,63 @@ static int patch_var_run(const char *fname, unsigned line, char **path) {
return 0;
}
-static int parse_line(const char *fname, unsigned line, const char *buffer, bool *invalid_config) {
+static int find_uid(const char *user, uid_t *ret_uid, Hashmap **cache) {
+ int r;
+
+ assert(user);
+ assert(ret_uid);
+
+ /* First: parse as numeric UID string */
+ r = parse_uid(user, ret_uid);
+ if (r >= 0)
+ return r;
+
+ /* Second: pass to NSS if we are running "online" */
+ if (!arg_root)
+ return get_user_creds(&user, ret_uid, NULL, NULL, NULL, 0);
+
+ /* Third, synthesize "root" unconditionally */
+ if (streq(user, "root")) {
+ *ret_uid = 0;
+ return 0;
+ }
+
+ /* Fourth: use fgetpwent() to read /etc/passwd directly, if we are "offline" */
+ return name_to_uid_offline(arg_root, user, ret_uid, cache);
+}
+
+static int find_gid(const char *group, gid_t *ret_gid, Hashmap **cache) {
+ int r;
+
+ assert(group);
+ assert(ret_gid);
+
+ /* First: parse as numeric GID string */
+ r = parse_gid(group, ret_gid);
+ if (r >= 0)
+ return r;
+
+ /* Second: pass to NSS if we are running "online" */
+ if (!arg_root)
+ return get_group_creds(&group, ret_gid, 0);
+
+ /* Third, synthesize "root" unconditionally */
+ if (streq(group, "root")) {
+ *ret_gid = 0;
+ return 0;
+ }
+
+ /* Fourth: use fgetgrent() to read /etc/group directly, if we are "offline" */
+ return name_to_gid_offline(arg_root, group, ret_gid, cache);
+}
+
+static int parse_line(
+ const char *fname,
+ unsigned line,
+ const char *buffer,
+ bool *invalid_config,
+ Hashmap **uid_cache,
+ Hashmap **gid_cache) {
_cleanup_free_ char *action = NULL, *mode = NULL, *user = NULL, *group = NULL, *age = NULL, *path = NULL;
_cleanup_(item_free_contents) Item i = {};
@@ -2610,7 +2667,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
case COPY_FILES:
if (!i.argument) {
- i.argument = path_join(arg_root, "/usr/share/factory", i.path);
+ i.argument = path_join("/usr/share/factory", i.path);
if (!i.argument)
return log_oom();
@@ -2618,7 +2675,9 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
*invalid_config = true;
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Source path '%s' is not absolute.", i.argument);
- } else if (arg_root) {
+ }
+
+ if (!empty_or_root(arg_root)) {
char *p;
p = path_join(arg_root, i.argument);
@@ -2709,7 +2768,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to substitute specifiers in argument: %m");
}
- if (arg_root) {
+ if (!empty_or_root(arg_root)) {
char *p;
p = path_join(arg_root, i.path);
@@ -2719,9 +2778,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
}
if (!empty_or_dash(user)) {
- const char *u = user;
-
- r = get_user_creds(&u, &i.uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
+ r = find_uid(user, &i.uid, uid_cache);
if (r < 0) {
*invalid_config = true;
return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to resolve user '%s': %m", user);
@@ -2731,9 +2788,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
}
if (!empty_or_dash(group)) {
- const char *g = group;
-
- r = get_group_creds(&g, &i.gid, USER_CREDS_ALLOW_MISSING);
+ r = find_gid(group, &i.gid, gid_cache);
if (r < 0) {
*invalid_config = true;
return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to resolve group '%s'.", group);
@@ -2941,7 +2996,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_ROOT:
- r = parse_path_argument_and_warn(optarg, true, &arg_root);
+ r = parse_path_argument_and_warn(optarg, /* suppress_root= */ false, &arg_root);
if (r < 0)
return r;
break;
@@ -2982,6 +3037,7 @@ static int parse_argv(int argc, char *argv[]) {
}
static int read_config_file(char **config_dirs, const char *fn, bool ignore_enoent, bool *invalid_config) {
+ _cleanup_(hashmap_freep) Hashmap *uid_cache = NULL, *gid_cache = NULL;
_cleanup_fclose_ FILE *_f = NULL;
Iterator iterator;
unsigned v = 0;
@@ -3027,7 +3083,7 @@ static int read_config_file(char **config_dirs, const char *fn, bool ignore_enoe
if (IN_SET(*l, 0, '#'))
continue;
- k = parse_line(fn, v, l, &invalid_line);
+ k = parse_line(fn, v, l, &invalid_line, &uid_cache, &gid_cache);
if (k < 0) {
if (invalid_line)
/* Allow reporting with a special code if the caller requested this */