mirror of
https://github.com/systemd/systemd.git
synced 2024-11-27 12:13:33 +08:00
Merge pull request #15718 from poettering/tmpfiles-offline
tmpfiles: read /etc/passwd + /etc/group with fgetpwent()/fgetgrent() if --root= is specified
This commit is contained in:
commit
165bda37ba
@ -161,10 +161,10 @@
|
||||
<listitem><para>Takes a directory path as an argument. All paths will be prefixed with the given alternate
|
||||
<replaceable>root</replaceable> path, including config search paths.</para>
|
||||
|
||||
<para>Note that this option does not alter how the users and groups specified in the configuration files are
|
||||
resolved. With or without this option, users and groups are always resolved according to the host's user and
|
||||
group databases, any such databases stored under the specified root directories are not
|
||||
consulted.</para></listitem>
|
||||
<para>When this option is used, the libc Name Service Switch (NSS) is bypassed for resolving users
|
||||
and groups. Instead the files <filename>/etc/passwd</filename> and <filename>/etc/group</filename>
|
||||
inside the alternate root are read directly. This means that users/groups not listed in these files
|
||||
will not be resolved, i.e. LDAP NIS and other complex databases are not considered.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
|
@ -2891,6 +2891,8 @@ if conf.get('ENABLE_TMPFILES') == 1
|
||||
exe = executable(
|
||||
'systemd-tmpfiles',
|
||||
'src/tmpfiles/tmpfiles.c',
|
||||
'src/tmpfiles/offline-passwd.c',
|
||||
'src/tmpfiles/offline-passwd.h',
|
||||
include_directories : includes,
|
||||
link_with : [libshared],
|
||||
dependencies : [libacl],
|
||||
|
@ -1814,7 +1814,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;
|
||||
|
122
src/tmpfiles/offline-passwd.c
Normal file
122
src/tmpfiles/offline-passwd.c
Normal file
@ -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;
|
||||
}
|
9
src/tmpfiles/offline-passwd.h
Normal file
9
src/tmpfiles/offline-passwd.h
Normal file
@ -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);
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user