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