1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "chase-symlinks.h"
4 #include "fd-util.h"
5 #include "offline-passwd.h"
6 #include "path-util.h"
7 #include "user-util.h"
8
9 DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(uid_gid_hash_ops, char, string_hash_func, string_compare_func, free);
10
open_passwd_file(const char * root,const char * fname,FILE ** ret_file)11 static int open_passwd_file(const char *root, const char *fname, FILE **ret_file) {
12 _cleanup_free_ char *p = NULL;
13 _cleanup_close_ int fd = -1;
14
15 fd = chase_symlinks_and_open(fname, root, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC, &p);
16 if (fd < 0)
17 return fd;
18
19 FILE *f = fdopen(fd, "r");
20 if (!f)
21 return -errno;
22
23 TAKE_FD(fd);
24
25 log_debug("Reading %s entries from %s...", basename(fname), p);
26
27 *ret_file = f;
28 return 0;
29 }
30
populate_uid_cache(const char * root,Hashmap ** ret)31 static int populate_uid_cache(const char *root, Hashmap **ret) {
32 _cleanup_(hashmap_freep) Hashmap *cache = NULL;
33 int r;
34
35 cache = hashmap_new(&uid_gid_hash_ops);
36 if (!cache)
37 return -ENOMEM;
38
39 /* The directory list is hardcoded here: /etc is the standard, and rpm-ostree uses /usr/lib. This
40 * could be made configurable, but I don't see the point right now. */
41
42 FOREACH_STRING(fname, "/etc/passwd", "/usr/lib/passwd") {
43 _cleanup_fclose_ FILE *f = NULL;
44
45 r = open_passwd_file(root, fname, &f);
46 if (r == -ENOENT)
47 continue;
48 if (r < 0)
49 return r;
50
51 struct passwd *pw;
52 while ((r = fgetpwent_sane(f, &pw)) > 0) {
53 _cleanup_free_ char *n = NULL;
54
55 n = strdup(pw->pw_name);
56 if (!n)
57 return -ENOMEM;
58
59 r = hashmap_put(cache, n, UID_TO_PTR(pw->pw_uid));
60 if (IN_SET(r, 0 -EEXIST))
61 continue;
62 if (r < 0)
63 return r;
64 TAKE_PTR(n);
65 }
66 }
67
68 *ret = TAKE_PTR(cache);
69 return 0;
70 }
71
populate_gid_cache(const char * root,Hashmap ** ret)72 static int populate_gid_cache(const char *root, Hashmap **ret) {
73 _cleanup_(hashmap_freep) Hashmap *cache = NULL;
74 int r;
75
76 cache = hashmap_new(&uid_gid_hash_ops);
77 if (!cache)
78 return -ENOMEM;
79
80 FOREACH_STRING(fname, "/etc/group", "/usr/lib/group") {
81 _cleanup_fclose_ FILE *f = NULL;
82
83 r = open_passwd_file(root, fname, &f);
84 if (r == -ENOENT)
85 continue;
86 if (r < 0)
87 return r;
88
89 struct group *gr;
90 while ((r = fgetgrent_sane(f, &gr)) > 0) {
91 _cleanup_free_ char *n = NULL;
92
93 n = strdup(gr->gr_name);
94 if (!n)
95 return -ENOMEM;
96
97 r = hashmap_put(cache, n, GID_TO_PTR(gr->gr_gid));
98 if (IN_SET(r, 0, -EEXIST))
99 continue;
100 if (r < 0)
101 return r;
102 TAKE_PTR(n);
103 }
104 }
105
106 *ret = TAKE_PTR(cache);
107 return 0;
108 }
109
name_to_uid_offline(const char * root,const char * user,uid_t * ret_uid,Hashmap ** cache)110 int name_to_uid_offline(
111 const char *root,
112 const char *user,
113 uid_t *ret_uid,
114 Hashmap **cache) {
115
116 void *found;
117 int r;
118
119 assert(user);
120 assert(ret_uid);
121 assert(cache);
122
123 if (!*cache) {
124 r = populate_uid_cache(root, cache);
125 if (r < 0)
126 return r;
127 }
128
129 found = hashmap_get(*cache, user);
130 if (!found)
131 return -ESRCH;
132
133 *ret_uid = PTR_TO_UID(found);
134 return 0;
135 }
136
name_to_gid_offline(const char * root,const char * group,gid_t * ret_gid,Hashmap ** cache)137 int name_to_gid_offline(
138 const char *root,
139 const char *group,
140 gid_t *ret_gid,
141 Hashmap **cache) {
142
143 void *found;
144 int r;
145
146 assert(group);
147 assert(ret_gid);
148 assert(cache);
149
150 if (!*cache) {
151 r = populate_gid_cache(root, cache);
152 if (r < 0)
153 return r;
154 }
155
156 found = hashmap_get(*cache, group);
157 if (!found)
158 return -ESRCH;
159
160 *ret_gid = PTR_TO_GID(found);
161 return 0;
162 }
163