]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nss-systemd/userdb-glue.c
Merge pull request #15314 from keszybz/network-server-access-functions
[thirdparty/systemd.git] / src / nss-systemd / userdb-glue.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include "env-util.h"
4 #include "fd-util.h"
5 #include "group-record-nss.h"
6 #include "strv.h"
7 #include "user-record.h"
8 #include "userdb-glue.h"
9 #include "userdb.h"
10
11 UserDBFlags nss_glue_userdb_flags(void) {
12 UserDBFlags flags = USERDB_AVOID_NSS;
13
14 /* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */
15 if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
16 flags |= USERDB_AVOID_DYNAMIC_USER;
17
18 return flags;
19 }
20
21 int nss_pack_user_record(
22 UserRecord *hr,
23 struct passwd *pwd,
24 char *buffer,
25 size_t buflen) {
26
27 const char *rn, *hd, *shell;
28 size_t required;
29
30 assert(hr);
31 assert(pwd);
32
33 assert_se(hr->user_name);
34 required = strlen(hr->user_name) + 1;
35
36 assert_se(rn = user_record_real_name(hr));
37 required += strlen(rn) + 1;
38
39 assert_se(hd = user_record_home_directory(hr));
40 required += strlen(hd) + 1;
41
42 assert_se(shell = user_record_shell(hr));
43 required += strlen(shell) + 1;
44
45 if (buflen < required)
46 return -ERANGE;
47
48 *pwd = (struct passwd) {
49 .pw_name = buffer,
50 .pw_uid = hr->uid,
51 .pw_gid = user_record_gid(hr),
52 .pw_passwd = (char*) "x", /* means: see shadow file */
53 };
54
55 assert(buffer);
56
57 pwd->pw_gecos = stpcpy(pwd->pw_name, hr->user_name) + 1;
58 pwd->pw_dir = stpcpy(pwd->pw_gecos, rn) + 1;
59 pwd->pw_shell = stpcpy(pwd->pw_dir, hd) + 1;
60 strcpy(pwd->pw_shell, shell);
61
62 return 0;
63 }
64
65 enum nss_status userdb_getpwnam(
66 const char *name,
67 struct passwd *pwd,
68 char *buffer, size_t buflen,
69 int *errnop) {
70
71 _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
72 int r;
73
74 assert(pwd);
75 assert(errnop);
76
77 r = userdb_nss_compat_is_enabled();
78 if (r < 0) {
79 *errnop = -r;
80 return NSS_STATUS_UNAVAIL;
81 }
82 if (!r)
83 return NSS_STATUS_NOTFOUND;
84
85 r = userdb_by_name(name, nss_glue_userdb_flags(), &hr);
86 if (r == -ESRCH)
87 return NSS_STATUS_NOTFOUND;
88 if (r < 0) {
89 *errnop = -r;
90 return NSS_STATUS_UNAVAIL;
91 }
92
93 r = nss_pack_user_record(hr, pwd, buffer, buflen);
94 if (r < 0) {
95 *errnop = -r;
96 return NSS_STATUS_TRYAGAIN;
97 }
98
99 return NSS_STATUS_SUCCESS;
100 }
101
102 enum nss_status userdb_getpwuid(
103 uid_t uid,
104 struct passwd *pwd,
105 char *buffer,
106 size_t buflen,
107 int *errnop) {
108
109 _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
110 int r;
111
112 assert(pwd);
113 assert(errnop);
114
115 r = userdb_nss_compat_is_enabled();
116 if (r < 0) {
117 *errnop = -r;
118 return NSS_STATUS_UNAVAIL;
119 }
120 if (!r)
121 return NSS_STATUS_NOTFOUND;
122
123 r = userdb_by_uid(uid, nss_glue_userdb_flags(), &hr);
124 if (r == -ESRCH)
125 return NSS_STATUS_NOTFOUND;
126 if (r < 0) {
127 *errnop = -r;
128 return NSS_STATUS_UNAVAIL;
129 }
130
131 r = nss_pack_user_record(hr, pwd, buffer, buflen);
132 if (r < 0) {
133 *errnop = -r;
134 return NSS_STATUS_TRYAGAIN;
135 }
136
137 return NSS_STATUS_SUCCESS;
138 }
139
140 int nss_pack_group_record(
141 GroupRecord *g,
142 char **extra_members,
143 struct group *gr,
144 char *buffer,
145 size_t buflen) {
146
147 char **array = NULL, *p, **m;
148 size_t required, n = 0, i = 0;
149
150 assert(g);
151 assert(gr);
152
153 assert_se(g->group_name);
154 required = strlen(g->group_name) + 1;
155
156 STRV_FOREACH(m, g->members) {
157 required += sizeof(char*); /* space for ptr array entry */
158 required += strlen(*m) + 1;
159 n++;
160 }
161 STRV_FOREACH(m, extra_members) {
162 if (strv_contains(g->members, *m))
163 continue;
164
165 required += sizeof(char*);
166 required += strlen(*m) + 1;
167 n++;
168 }
169
170 required += sizeof(char*); /* trailing NULL in ptr array entry */
171
172 if (buflen < required)
173 return -ERANGE;
174
175 array = (char**) buffer; /* place ptr array at beginning of buffer, under assumption buffer is aligned */
176 p = buffer + sizeof(void*) * (n + 1); /* place member strings right after the ptr array */
177
178 STRV_FOREACH(m, g->members) {
179 array[i++] = p;
180 p = stpcpy(p, *m) + 1;
181 }
182 STRV_FOREACH(m, extra_members) {
183 if (strv_contains(g->members, *m))
184 continue;
185
186 array[i++] = p;
187 p = stpcpy(p, *m) + 1;
188 }
189
190 assert_se(i == n);
191 array[n] = NULL;
192
193 *gr = (struct group) {
194 .gr_name = strcpy(p, g->group_name),
195 .gr_gid = g->gid,
196 .gr_passwd = (char*) "x", /* means: see shadow file */
197 .gr_mem = array,
198 };
199
200 return 0;
201 }
202
203 enum nss_status userdb_getgrnam(
204 const char *name,
205 struct group *gr,
206 char *buffer,
207 size_t buflen,
208 int *errnop) {
209
210 _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
211 _cleanup_strv_free_ char **members = NULL;
212 int r;
213
214 assert(gr);
215 assert(errnop);
216
217 r = userdb_nss_compat_is_enabled();
218 if (r < 0) {
219 *errnop = -r;
220 return NSS_STATUS_UNAVAIL;
221 }
222 if (!r)
223 return NSS_STATUS_NOTFOUND;
224
225 r = groupdb_by_name(name, nss_glue_userdb_flags(), &g);
226 if (r < 0 && r != -ESRCH) {
227 *errnop = -r;
228 return NSS_STATUS_UNAVAIL;
229 }
230
231 r = membershipdb_by_group_strv(name, nss_glue_userdb_flags(), &members);
232 if (r < 0) {
233 *errnop = -r;
234 return NSS_STATUS_UNAVAIL;
235 }
236
237 if (!g) {
238 _cleanup_close_ int lock_fd = -1;
239
240 if (strv_isempty(members))
241 return NSS_STATUS_NOTFOUND;
242
243 /* Grmbl, so we are supposed to extend a group entry, but the group entry itself is not
244 * accessible via non-NSS. Hence let's do what we have to do, and query NSS after all to
245 * acquire it, so that we can extend it (that's because glibc's group merging feature will
246 * merge groups only if both GID and name match and thus we need to have both first). It
247 * sucks behaving recursively likely this, but it's apparently what everybody does. We break
248 * the recursion for ourselves via the userdb_nss_compat_disable() lock. */
249
250 lock_fd = userdb_nss_compat_disable();
251 if (lock_fd < 0 && lock_fd != -EBUSY)
252 return lock_fd;
253
254 r = nss_group_record_by_name(name, false, &g);
255 if (r == -ESRCH)
256 return NSS_STATUS_NOTFOUND;
257 if (r < 0) {
258 *errnop = -r;
259 return NSS_STATUS_UNAVAIL;
260 }
261 }
262
263 r = nss_pack_group_record(g, members, gr, buffer, buflen);
264 if (r < 0) {
265 *errnop = -r;
266 return NSS_STATUS_TRYAGAIN;
267 }
268
269 return NSS_STATUS_SUCCESS;
270 }
271
272 enum nss_status userdb_getgrgid(
273 gid_t gid,
274 struct group *gr,
275 char *buffer,
276 size_t buflen,
277 int *errnop) {
278
279
280 _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
281 _cleanup_strv_free_ char **members = NULL;
282 bool from_nss;
283 int r;
284
285 assert(gr);
286 assert(errnop);
287
288 r = userdb_nss_compat_is_enabled();
289 if (r < 0) {
290 *errnop = -r;
291 return NSS_STATUS_UNAVAIL;
292 }
293 if (!r)
294 return NSS_STATUS_NOTFOUND;
295
296 r = groupdb_by_gid(gid, nss_glue_userdb_flags(), &g);
297 if (r < 0 && r != -ESRCH) {
298 *errnop = -r;
299 return NSS_STATUS_UNAVAIL;
300 }
301
302 if (!g) {
303 _cleanup_close_ int lock_fd = -1;
304
305 /* So, quite possibly we have to extend an existing group record with additional members. But
306 * to do this we need to know the group name first. The group didn't exist via non-NSS
307 * queries though, hence let's try to acquire it here recursively via NSS. */
308
309 lock_fd = userdb_nss_compat_disable();
310 if (lock_fd < 0 && lock_fd != -EBUSY)
311 return lock_fd;
312
313 r = nss_group_record_by_gid(gid, false, &g);
314 if (r == -ESRCH)
315 return NSS_STATUS_NOTFOUND;
316
317 if (r < 0) {
318 *errnop = -r;
319 return NSS_STATUS_UNAVAIL;
320 }
321
322 from_nss = true;
323 } else
324 from_nss = false;
325
326 r = membershipdb_by_group_strv(g->group_name, nss_glue_userdb_flags(), &members);
327 if (r < 0) {
328 *errnop = -r;
329 return NSS_STATUS_UNAVAIL;
330 }
331
332 /* If we acquired the record via NSS then there's no reason to respond unless we have to augment the
333 * list of members of the group */
334 if (from_nss && strv_isempty(members))
335 return NSS_STATUS_NOTFOUND;
336
337 r = nss_pack_group_record(g, members, gr, buffer, buflen);
338 if (r < 0) {
339 *errnop = -r;
340 return NSS_STATUS_TRYAGAIN;
341 }
342
343 return NSS_STATUS_SUCCESS;
344 }