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