| /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
| |
| #include "fd-util.h" |
| #include "fs-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); |
| |
| static int open_passwd_file(const char *root, const char *fname, FILE **ret_file) { |
| _cleanup_free_ char *p = NULL; |
| _cleanup_close_ int fd = -1; |
| |
| fd = chase_symlinks_and_open(fname, root, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC, &p); |
| if (fd < 0) |
| return fd; |
| |
| FILE *f = fdopen(fd, "r"); |
| if (!f) |
| return -errno; |
| |
| TAKE_FD(fd); |
| |
| log_debug("Reading %s entries from %s...", basename(fname), p); |
| |
| *ret_file = f; |
| return 0; |
| } |
| |
| static int populate_uid_cache(const char *root, Hashmap **ret) { |
| _cleanup_(hashmap_freep) Hashmap *cache = NULL; |
| int r; |
| |
| cache = hashmap_new(&uid_gid_hash_ops); |
| if (!cache) |
| return -ENOMEM; |
| |
| /* The directory list is hardcoded here: /etc is the standard, and rpm-ostree uses /usr/lib. This |
| * could be made configurable, but I don't see the point right now. */ |
| |
| const char *fname; |
| FOREACH_STRING(fname, "/etc/passwd", "/usr/lib/passwd") { |
| _cleanup_fclose_ FILE *f = NULL; |
| |
| r = open_passwd_file(root, fname, &f); |
| if (r == -ENOENT) |
| continue; |
| if (r < 0) |
| return r; |
| |
| struct passwd *pw; |
| while ((r = fgetpwent_sane(f, &pw)) > 0) { |
| _cleanup_free_ char *n = NULL; |
| |
| n = strdup(pw->pw_name); |
| if (!n) |
| return -ENOMEM; |
| |
| r = hashmap_put(cache, n, UID_TO_PTR(pw->pw_uid)); |
| if (IN_SET(r, 0 -EEXIST)) |
| continue; |
| if (r < 0) |
| return r; |
| TAKE_PTR(n); |
| } |
| } |
| |
| *ret = TAKE_PTR(cache); |
| return 0; |
| } |
| |
| static int populate_gid_cache(const char *root, Hashmap **ret) { |
| _cleanup_(hashmap_freep) Hashmap *cache = NULL; |
| int r; |
| |
| cache = hashmap_new(&uid_gid_hash_ops); |
| if (!cache) |
| return -ENOMEM; |
| |
| const char *fname; |
| FOREACH_STRING(fname, "/etc/group", "/usr/lib/group") { |
| _cleanup_fclose_ FILE *f = NULL; |
| |
| r = open_passwd_file(root, fname, &f); |
| if (r == -ENOENT) |
| continue; |
| if (r < 0) |
| return r; |
| |
| struct group *gr; |
| while ((r = fgetgrent_sane(f, &gr)) > 0) { |
| _cleanup_free_ char *n = NULL; |
| |
| n = strdup(gr->gr_name); |
| if (!n) |
| return -ENOMEM; |
| |
| r = hashmap_put(cache, n, GID_TO_PTR(gr->gr_gid)); |
| if (IN_SET(r, 0, -EEXIST)) |
| continue; |
| if (r < 0) |
| return r; |
| TAKE_PTR(n); |
| } |
| } |
| |
| *ret = TAKE_PTR(cache); |
| return 0; |
| } |
| |
| 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) { |
| r = populate_uid_cache(root, cache); |
| if (r < 0) |
| return r; |
| } |
| |
| 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) { |
| r = populate_gid_cache(root, cache); |
| if (r < 0) |
| return r; |
| } |
| |
| found = hashmap_get(*cache, group); |
| if (!found) |
| return -ESRCH; |
| |
| *ret_gid = PTR_TO_GID(found); |
| return 0; |
| } |