]>
Commit | Line | Data |
---|---|---|
52ad194e | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
8a584da2 MP |
2 | |
3 | #include <nss.h> | |
b012e921 | 4 | #include <pthread.h> |
8a584da2 MP |
5 | |
6 | #include "sd-bus.h" | |
7 | ||
8 | #include "alloc-util.h" | |
9 | #include "bus-common-errors.h" | |
b012e921 | 10 | #include "dirent-util.h" |
8a584da2 | 11 | #include "env-util.h" |
b012e921 | 12 | #include "fd-util.h" |
8a584da2 | 13 | #include "fs-util.h" |
b012e921 | 14 | #include "list.h" |
8a584da2 MP |
15 | #include "macro.h" |
16 | #include "nss-util.h" | |
17 | #include "signal-util.h" | |
18 | #include "stdio-util.h" | |
19 | #include "string-util.h" | |
20 | #include "user-util.h" | |
21 | #include "util.h" | |
22 | ||
b012e921 MB |
23 | #define DYNAMIC_USER_GECOS "Dynamic User" |
24 | #define DYNAMIC_USER_PASSWD "*" /* locked */ | |
25 | #define DYNAMIC_USER_DIR "/" | |
26 | #define DYNAMIC_USER_SHELL "/sbin/nologin" | |
27 | ||
8a584da2 MP |
28 | static const struct passwd root_passwd = { |
29 | .pw_name = (char*) "root", | |
30 | .pw_passwd = (char*) "x", /* see shadow file */ | |
31 | .pw_uid = 0, | |
32 | .pw_gid = 0, | |
33 | .pw_gecos = (char*) "Super User", | |
34 | .pw_dir = (char*) "/root", | |
35 | .pw_shell = (char*) "/bin/sh", | |
36 | }; | |
37 | ||
38 | static const struct passwd nobody_passwd = { | |
39 | .pw_name = (char*) NOBODY_USER_NAME, | |
40 | .pw_passwd = (char*) "*", /* locked */ | |
52ad194e MB |
41 | .pw_uid = UID_NOBODY, |
42 | .pw_gid = GID_NOBODY, | |
8a584da2 MP |
43 | .pw_gecos = (char*) "User Nobody", |
44 | .pw_dir = (char*) "/", | |
45 | .pw_shell = (char*) "/sbin/nologin", | |
46 | }; | |
47 | ||
48 | static const struct group root_group = { | |
49 | .gr_name = (char*) "root", | |
50 | .gr_gid = 0, | |
51 | .gr_passwd = (char*) "x", /* see shadow file */ | |
52 | .gr_mem = (char*[]) { NULL }, | |
53 | }; | |
54 | ||
55 | static const struct group nobody_group = { | |
56 | .gr_name = (char*) NOBODY_GROUP_NAME, | |
52ad194e | 57 | .gr_gid = GID_NOBODY, |
8a584da2 MP |
58 | .gr_passwd = (char*) "*", /* locked */ |
59 | .gr_mem = (char*[]) { NULL }, | |
60 | }; | |
61 | ||
b012e921 MB |
62 | typedef struct UserEntry UserEntry; |
63 | typedef struct GetentData GetentData; | |
64 | ||
65 | struct UserEntry { | |
66 | uid_t id; | |
67 | char *name; | |
68 | ||
69 | GetentData *data; | |
70 | LIST_FIELDS(UserEntry, entries); | |
71 | }; | |
72 | ||
73 | struct GetentData { | |
74 | /* As explained in NOTES section of getpwent_r(3) as 'getpwent_r() is not really | |
75 | * reentrant since it shares the reading position in the stream with all other threads', | |
76 | * we need to protect the data in UserEntry from multithreaded programs which may call | |
77 | * setpwent(), getpwent_r(), or endpwent() simultaneously. So, each function locks the | |
78 | * data by using the mutex below. */ | |
79 | pthread_mutex_t mutex; | |
80 | ||
81 | UserEntry *position; | |
82 | LIST_HEAD(UserEntry, entries); | |
83 | }; | |
84 | ||
85 | static GetentData getpwent_data = { PTHREAD_MUTEX_INITIALIZER, NULL, NULL }; | |
86 | static GetentData getgrent_data = { PTHREAD_MUTEX_INITIALIZER, NULL, NULL }; | |
87 | ||
8a584da2 MP |
88 | NSS_GETPW_PROTOTYPES(systemd); |
89 | NSS_GETGR_PROTOTYPES(systemd); | |
b012e921 MB |
90 | enum nss_status _nss_systemd_endpwent(void) _public_; |
91 | enum nss_status _nss_systemd_setpwent(int stayopen) _public_; | |
92 | enum nss_status _nss_systemd_getpwent_r(struct passwd *result, char *buffer, size_t buflen, int *errnop) _public_; | |
93 | enum nss_status _nss_systemd_endgrent(void) _public_; | |
94 | enum nss_status _nss_systemd_setgrent(int stayopen) _public_; | |
95 | enum nss_status _nss_systemd_getgrent_r(struct group *result, char *buffer, size_t buflen, int *errnop) _public_; | |
8a584da2 MP |
96 | |
97 | static int direct_lookup_name(const char *name, uid_t *ret) { | |
98 | _cleanup_free_ char *s = NULL; | |
99 | const char *path; | |
100 | int r; | |
101 | ||
102 | assert(name); | |
103 | ||
104 | /* Normally, we go via the bus to resolve names. That has the benefit that it is available from any mount | |
105 | * namespace and subject to proper authentication. However, there's one problem: if our module is called from | |
106 | * dbus-daemon itself we really can't use D-Bus to communicate. In this case, resort to a client-side hack, | |
107 | * and look for the dynamic names directly. This is pretty ugly, but breaks the cyclic dependency. */ | |
108 | ||
109 | path = strjoina("/run/systemd/dynamic-uid/direct:", name); | |
110 | r = readlink_malloc(path, &s); | |
111 | if (r < 0) | |
112 | return r; | |
113 | ||
114 | return parse_uid(s, ret); | |
115 | } | |
116 | ||
117 | static int direct_lookup_uid(uid_t uid, char **ret) { | |
52ad194e | 118 | char path[STRLEN("/run/systemd/dynamic-uid/direct:") + DECIMAL_STR_MAX(uid_t) + 1], *s; |
8a584da2 MP |
119 | int r; |
120 | ||
121 | xsprintf(path, "/run/systemd/dynamic-uid/direct:" UID_FMT, uid); | |
122 | ||
123 | r = readlink_malloc(path, &s); | |
124 | if (r < 0) | |
125 | return r; | |
126 | if (!valid_user_group_name(s)) { /* extra safety check */ | |
127 | free(s); | |
128 | return -EINVAL; | |
129 | } | |
130 | ||
131 | *ret = s; | |
132 | return 0; | |
133 | } | |
134 | ||
135 | enum nss_status _nss_systemd_getpwnam_r( | |
136 | const char *name, | |
137 | struct passwd *pwd, | |
138 | char *buffer, size_t buflen, | |
139 | int *errnop) { | |
140 | ||
b012e921 MB |
141 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; |
142 | _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL; | |
143 | _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; | |
8a584da2 MP |
144 | uint32_t translated; |
145 | size_t l; | |
f5e65279 | 146 | int bypass, r; |
8a584da2 | 147 | |
6e866b33 | 148 | PROTECT_ERRNO; |
8a584da2 MP |
149 | BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); |
150 | ||
151 | assert(name); | |
152 | assert(pwd); | |
153 | ||
2897b343 MP |
154 | /* If the username is not valid, then we don't know it. Ideally libc would filter these for us anyway. We don't |
155 | * generate EINVAL here, because it isn't really out business to complain about invalid user names. */ | |
156 | if (!valid_user_group_name(name)) | |
6e866b33 | 157 | return NSS_STATUS_NOTFOUND; |
8a584da2 MP |
158 | |
159 | /* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */ | |
f5e65279 MB |
160 | if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) { |
161 | if (streq(name, root_passwd.pw_name)) { | |
162 | *pwd = root_passwd; | |
f5e65279 MB |
163 | return NSS_STATUS_SUCCESS; |
164 | } | |
1d42b86d MB |
165 | if (synthesize_nobody() && |
166 | streq(name, nobody_passwd.pw_name)) { | |
f5e65279 | 167 | *pwd = nobody_passwd; |
f5e65279 MB |
168 | return NSS_STATUS_SUCCESS; |
169 | } | |
8a584da2 MP |
170 | } |
171 | ||
172 | /* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */ | |
f5e65279 | 173 | if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) |
6e866b33 | 174 | return NSS_STATUS_NOTFOUND; |
8a584da2 | 175 | |
f5e65279 MB |
176 | bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS"); |
177 | if (bypass <= 0) { | |
8a584da2 | 178 | r = sd_bus_open_system(&bus); |
b012e921 | 179 | if (r < 0) |
f5e65279 | 180 | bypass = 1; |
b012e921 | 181 | } |
8a584da2 | 182 | |
b012e921 MB |
183 | if (bypass > 0) { |
184 | r = direct_lookup_name(name, (uid_t*) &translated); | |
185 | if (r == -ENOENT) | |
6e866b33 | 186 | return NSS_STATUS_NOTFOUND; |
b012e921 MB |
187 | if (r < 0) |
188 | goto fail; | |
189 | } else { | |
8a584da2 MP |
190 | r = sd_bus_call_method(bus, |
191 | "org.freedesktop.systemd1", | |
192 | "/org/freedesktop/systemd1", | |
193 | "org.freedesktop.systemd1.Manager", | |
194 | "LookupDynamicUserByName", | |
195 | &error, | |
196 | &reply, | |
197 | "s", | |
198 | name); | |
199 | if (r < 0) { | |
200 | if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER)) | |
6e866b33 | 201 | return NSS_STATUS_NOTFOUND; |
8a584da2 MP |
202 | |
203 | goto fail; | |
204 | } | |
205 | ||
206 | r = sd_bus_message_read(reply, "u", &translated); | |
207 | if (r < 0) | |
208 | goto fail; | |
209 | } | |
210 | ||
211 | l = strlen(name); | |
212 | if (buflen < l+1) { | |
2897b343 | 213 | *errnop = ERANGE; |
8a584da2 MP |
214 | return NSS_STATUS_TRYAGAIN; |
215 | } | |
216 | ||
217 | memcpy(buffer, name, l+1); | |
218 | ||
219 | pwd->pw_name = buffer; | |
220 | pwd->pw_uid = (uid_t) translated; | |
221 | pwd->pw_gid = (uid_t) translated; | |
b012e921 MB |
222 | pwd->pw_gecos = (char*) DYNAMIC_USER_GECOS; |
223 | pwd->pw_passwd = (char*) DYNAMIC_USER_PASSWD; | |
224 | pwd->pw_dir = (char*) DYNAMIC_USER_DIR; | |
225 | pwd->pw_shell = (char*) DYNAMIC_USER_SHELL; | |
8a584da2 | 226 | |
8a584da2 MP |
227 | return NSS_STATUS_SUCCESS; |
228 | ||
8a584da2 MP |
229 | fail: |
230 | *errnop = -r; | |
231 | return NSS_STATUS_UNAVAIL; | |
232 | } | |
233 | ||
234 | enum nss_status _nss_systemd_getpwuid_r( | |
235 | uid_t uid, | |
236 | struct passwd *pwd, | |
237 | char *buffer, size_t buflen, | |
238 | int *errnop) { | |
239 | ||
240 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; | |
241 | _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL; | |
242 | _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; | |
243 | _cleanup_free_ char *direct = NULL; | |
244 | const char *translated; | |
245 | size_t l; | |
f5e65279 | 246 | int bypass, r; |
8a584da2 | 247 | |
6e866b33 | 248 | PROTECT_ERRNO; |
8a584da2 MP |
249 | BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); |
250 | ||
2897b343 | 251 | if (!uid_is_valid(uid)) |
6e866b33 | 252 | return NSS_STATUS_NOTFOUND; |
8a584da2 MP |
253 | |
254 | /* Synthesize data for the root user and for nobody in case they are missing from /etc/passwd */ | |
f5e65279 MB |
255 | if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) { |
256 | if (uid == root_passwd.pw_uid) { | |
257 | *pwd = root_passwd; | |
f5e65279 MB |
258 | return NSS_STATUS_SUCCESS; |
259 | } | |
1d42b86d MB |
260 | if (synthesize_nobody() && |
261 | uid == nobody_passwd.pw_uid) { | |
f5e65279 | 262 | *pwd = nobody_passwd; |
f5e65279 MB |
263 | return NSS_STATUS_SUCCESS; |
264 | } | |
8a584da2 MP |
265 | } |
266 | ||
52ad194e | 267 | if (!uid_is_dynamic(uid)) |
6e866b33 | 268 | return NSS_STATUS_NOTFOUND; |
8a584da2 | 269 | |
f5e65279 | 270 | if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) |
6e866b33 | 271 | return NSS_STATUS_NOTFOUND; |
8a584da2 | 272 | |
f5e65279 MB |
273 | bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS"); |
274 | if (bypass <= 0) { | |
8a584da2 | 275 | r = sd_bus_open_system(&bus); |
b012e921 | 276 | if (r < 0) |
f5e65279 | 277 | bypass = 1; |
b012e921 | 278 | } |
8a584da2 | 279 | |
b012e921 MB |
280 | if (bypass > 0) { |
281 | r = direct_lookup_uid(uid, &direct); | |
282 | if (r == -ENOENT) | |
6e866b33 | 283 | return NSS_STATUS_NOTFOUND; |
b012e921 MB |
284 | if (r < 0) |
285 | goto fail; | |
286 | ||
287 | translated = direct; | |
288 | ||
289 | } else { | |
8a584da2 MP |
290 | r = sd_bus_call_method(bus, |
291 | "org.freedesktop.systemd1", | |
292 | "/org/freedesktop/systemd1", | |
293 | "org.freedesktop.systemd1.Manager", | |
294 | "LookupDynamicUserByUID", | |
295 | &error, | |
296 | &reply, | |
297 | "u", | |
298 | (uint32_t) uid); | |
299 | if (r < 0) { | |
300 | if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER)) | |
6e866b33 | 301 | return NSS_STATUS_NOTFOUND; |
8a584da2 MP |
302 | |
303 | goto fail; | |
304 | } | |
305 | ||
306 | r = sd_bus_message_read(reply, "s", &translated); | |
307 | if (r < 0) | |
308 | goto fail; | |
309 | } | |
310 | ||
311 | l = strlen(translated) + 1; | |
312 | if (buflen < l) { | |
2897b343 | 313 | *errnop = ERANGE; |
8a584da2 MP |
314 | return NSS_STATUS_TRYAGAIN; |
315 | } | |
316 | ||
317 | memcpy(buffer, translated, l); | |
318 | ||
319 | pwd->pw_name = buffer; | |
320 | pwd->pw_uid = uid; | |
321 | pwd->pw_gid = uid; | |
b012e921 MB |
322 | pwd->pw_gecos = (char*) DYNAMIC_USER_GECOS; |
323 | pwd->pw_passwd = (char*) DYNAMIC_USER_PASSWD; | |
324 | pwd->pw_dir = (char*) DYNAMIC_USER_DIR; | |
325 | pwd->pw_shell = (char*) DYNAMIC_USER_SHELL; | |
8a584da2 | 326 | |
8a584da2 MP |
327 | return NSS_STATUS_SUCCESS; |
328 | ||
8a584da2 MP |
329 | fail: |
330 | *errnop = -r; | |
331 | return NSS_STATUS_UNAVAIL; | |
332 | } | |
333 | ||
98393f85 MB |
334 | #pragma GCC diagnostic ignored "-Wsizeof-pointer-memaccess" |
335 | ||
8a584da2 MP |
336 | enum nss_status _nss_systemd_getgrnam_r( |
337 | const char *name, | |
338 | struct group *gr, | |
339 | char *buffer, size_t buflen, | |
340 | int *errnop) { | |
341 | ||
b012e921 MB |
342 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; |
343 | _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL; | |
344 | _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; | |
8a584da2 MP |
345 | uint32_t translated; |
346 | size_t l; | |
f5e65279 | 347 | int bypass, r; |
8a584da2 | 348 | |
6e866b33 | 349 | PROTECT_ERRNO; |
8a584da2 MP |
350 | BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); |
351 | ||
352 | assert(name); | |
353 | assert(gr); | |
354 | ||
2897b343 | 355 | if (!valid_user_group_name(name)) |
6e866b33 | 356 | return NSS_STATUS_NOTFOUND; |
8a584da2 MP |
357 | |
358 | /* Synthesize records for root and nobody, in case they are missing form /etc/group */ | |
f5e65279 MB |
359 | if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) { |
360 | if (streq(name, root_group.gr_name)) { | |
361 | *gr = root_group; | |
f5e65279 MB |
362 | return NSS_STATUS_SUCCESS; |
363 | } | |
1d42b86d MB |
364 | if (synthesize_nobody() && |
365 | streq(name, nobody_group.gr_name)) { | |
f5e65279 | 366 | *gr = nobody_group; |
f5e65279 MB |
367 | return NSS_STATUS_SUCCESS; |
368 | } | |
8a584da2 MP |
369 | } |
370 | ||
f5e65279 | 371 | if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) |
6e866b33 | 372 | return NSS_STATUS_NOTFOUND; |
8a584da2 | 373 | |
f5e65279 MB |
374 | bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS"); |
375 | if (bypass <= 0) { | |
8a584da2 | 376 | r = sd_bus_open_system(&bus); |
b012e921 | 377 | if (r < 0) |
f5e65279 | 378 | bypass = 1; |
b012e921 | 379 | } |
8a584da2 | 380 | |
b012e921 MB |
381 | if (bypass > 0) { |
382 | r = direct_lookup_name(name, (uid_t*) &translated); | |
383 | if (r == -ENOENT) | |
6e866b33 | 384 | return NSS_STATUS_NOTFOUND; |
b012e921 MB |
385 | if (r < 0) |
386 | goto fail; | |
387 | } else { | |
8a584da2 MP |
388 | r = sd_bus_call_method(bus, |
389 | "org.freedesktop.systemd1", | |
390 | "/org/freedesktop/systemd1", | |
391 | "org.freedesktop.systemd1.Manager", | |
392 | "LookupDynamicUserByName", | |
393 | &error, | |
394 | &reply, | |
395 | "s", | |
396 | name); | |
397 | if (r < 0) { | |
398 | if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER)) | |
6e866b33 | 399 | return NSS_STATUS_NOTFOUND; |
8a584da2 MP |
400 | |
401 | goto fail; | |
402 | } | |
403 | ||
404 | r = sd_bus_message_read(reply, "u", &translated); | |
405 | if (r < 0) | |
406 | goto fail; | |
407 | } | |
408 | ||
409 | l = sizeof(char*) + strlen(name) + 1; | |
410 | if (buflen < l) { | |
2897b343 | 411 | *errnop = ERANGE; |
8a584da2 MP |
412 | return NSS_STATUS_TRYAGAIN; |
413 | } | |
414 | ||
415 | memzero(buffer, sizeof(char*)); | |
416 | strcpy(buffer + sizeof(char*), name); | |
417 | ||
418 | gr->gr_name = buffer + sizeof(char*); | |
419 | gr->gr_gid = (gid_t) translated; | |
b012e921 | 420 | gr->gr_passwd = (char*) DYNAMIC_USER_PASSWD; |
8a584da2 MP |
421 | gr->gr_mem = (char**) buffer; |
422 | ||
8a584da2 MP |
423 | return NSS_STATUS_SUCCESS; |
424 | ||
8a584da2 MP |
425 | fail: |
426 | *errnop = -r; | |
427 | return NSS_STATUS_UNAVAIL; | |
428 | } | |
429 | ||
430 | enum nss_status _nss_systemd_getgrgid_r( | |
431 | gid_t gid, | |
432 | struct group *gr, | |
433 | char *buffer, size_t buflen, | |
434 | int *errnop) { | |
435 | ||
436 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; | |
437 | _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL; | |
438 | _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; | |
439 | _cleanup_free_ char *direct = NULL; | |
440 | const char *translated; | |
441 | size_t l; | |
f5e65279 | 442 | int bypass, r; |
8a584da2 | 443 | |
6e866b33 | 444 | PROTECT_ERRNO; |
8a584da2 MP |
445 | BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); |
446 | ||
2897b343 | 447 | if (!gid_is_valid(gid)) |
6e866b33 | 448 | return NSS_STATUS_NOTFOUND; |
8a584da2 MP |
449 | |
450 | /* Synthesize records for root and nobody, in case they are missing from /etc/group */ | |
f5e65279 MB |
451 | if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) { |
452 | if (gid == root_group.gr_gid) { | |
453 | *gr = root_group; | |
f5e65279 MB |
454 | return NSS_STATUS_SUCCESS; |
455 | } | |
1d42b86d MB |
456 | if (synthesize_nobody() && |
457 | gid == nobody_group.gr_gid) { | |
f5e65279 | 458 | *gr = nobody_group; |
f5e65279 MB |
459 | return NSS_STATUS_SUCCESS; |
460 | } | |
8a584da2 MP |
461 | } |
462 | ||
52ad194e | 463 | if (!gid_is_dynamic(gid)) |
6e866b33 | 464 | return NSS_STATUS_NOTFOUND; |
8a584da2 | 465 | |
f5e65279 | 466 | if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) |
6e866b33 | 467 | return NSS_STATUS_NOTFOUND; |
8a584da2 | 468 | |
f5e65279 MB |
469 | bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS"); |
470 | if (bypass <= 0) { | |
8a584da2 | 471 | r = sd_bus_open_system(&bus); |
b012e921 | 472 | if (r < 0) |
f5e65279 | 473 | bypass = 1; |
b012e921 MB |
474 | } |
475 | ||
476 | if (bypass > 0) { | |
477 | r = direct_lookup_uid(gid, &direct); | |
478 | if (r == -ENOENT) | |
6e866b33 | 479 | return NSS_STATUS_NOTFOUND; |
b012e921 MB |
480 | if (r < 0) |
481 | goto fail; | |
8a584da2 | 482 | |
b012e921 MB |
483 | translated = direct; |
484 | ||
485 | } else { | |
8a584da2 MP |
486 | r = sd_bus_call_method(bus, |
487 | "org.freedesktop.systemd1", | |
488 | "/org/freedesktop/systemd1", | |
489 | "org.freedesktop.systemd1.Manager", | |
490 | "LookupDynamicUserByUID", | |
491 | &error, | |
492 | &reply, | |
493 | "u", | |
494 | (uint32_t) gid); | |
495 | if (r < 0) { | |
496 | if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER)) | |
6e866b33 | 497 | return NSS_STATUS_NOTFOUND; |
8a584da2 MP |
498 | |
499 | goto fail; | |
500 | } | |
501 | ||
502 | r = sd_bus_message_read(reply, "s", &translated); | |
503 | if (r < 0) | |
504 | goto fail; | |
505 | } | |
506 | ||
507 | l = sizeof(char*) + strlen(translated) + 1; | |
508 | if (buflen < l) { | |
2897b343 | 509 | *errnop = ERANGE; |
8a584da2 MP |
510 | return NSS_STATUS_TRYAGAIN; |
511 | } | |
512 | ||
513 | memzero(buffer, sizeof(char*)); | |
514 | strcpy(buffer + sizeof(char*), translated); | |
515 | ||
516 | gr->gr_name = buffer + sizeof(char*); | |
517 | gr->gr_gid = gid; | |
b012e921 | 518 | gr->gr_passwd = (char*) DYNAMIC_USER_PASSWD; |
8a584da2 MP |
519 | gr->gr_mem = (char**) buffer; |
520 | ||
8a584da2 MP |
521 | return NSS_STATUS_SUCCESS; |
522 | ||
8a584da2 MP |
523 | fail: |
524 | *errnop = -r; | |
525 | return NSS_STATUS_UNAVAIL; | |
526 | } | |
b012e921 MB |
527 | |
528 | static void user_entry_free(UserEntry *p) { | |
529 | if (!p) | |
530 | return; | |
531 | ||
532 | if (p->data) | |
533 | LIST_REMOVE(entries, p->data->entries, p); | |
534 | ||
535 | free(p->name); | |
536 | free(p); | |
537 | } | |
538 | ||
539 | static int user_entry_add(GetentData *data, const char *name, uid_t id) { | |
540 | UserEntry *p; | |
541 | ||
542 | assert(data); | |
543 | ||
544 | /* This happens when User= or Group= already exists statically. */ | |
545 | if (!uid_is_dynamic(id)) | |
546 | return -EINVAL; | |
547 | ||
548 | p = new0(UserEntry, 1); | |
549 | if (!p) | |
550 | return -ENOMEM; | |
551 | ||
552 | p->name = strdup(name); | |
553 | if (!p->name) { | |
554 | free(p); | |
555 | return -ENOMEM; | |
556 | } | |
557 | p->id = id; | |
558 | p->data = data; | |
559 | ||
560 | LIST_PREPEND(entries, data->entries, p); | |
561 | ||
562 | return 0; | |
563 | } | |
564 | ||
565 | static void systemd_endent(GetentData *data) { | |
566 | UserEntry *p; | |
567 | ||
568 | assert(data); | |
569 | ||
570 | while ((p = data->entries)) | |
571 | user_entry_free(p); | |
572 | ||
573 | data->position = NULL; | |
574 | } | |
575 | ||
576 | static enum nss_status nss_systemd_endent(GetentData *p) { | |
6e866b33 | 577 | PROTECT_ERRNO; |
b012e921 MB |
578 | BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); |
579 | ||
580 | assert_se(pthread_mutex_lock(&p->mutex) == 0); | |
581 | systemd_endent(p); | |
582 | assert_se(pthread_mutex_unlock(&p->mutex) == 0); | |
583 | ||
584 | return NSS_STATUS_SUCCESS; | |
585 | } | |
586 | ||
587 | enum nss_status _nss_systemd_endpwent(void) { | |
588 | return nss_systemd_endent(&getpwent_data); | |
589 | } | |
590 | ||
591 | enum nss_status _nss_systemd_endgrent(void) { | |
592 | return nss_systemd_endent(&getgrent_data); | |
593 | } | |
594 | ||
595 | static int direct_enumeration(GetentData *p) { | |
596 | _cleanup_closedir_ DIR *d = NULL; | |
597 | struct dirent *de; | |
598 | int r; | |
599 | ||
600 | assert(p); | |
601 | ||
602 | d = opendir("/run/systemd/dynamic-uid/"); | |
603 | if (!d) | |
604 | return -errno; | |
605 | ||
606 | FOREACH_DIRENT(de, d, return -errno) { | |
607 | _cleanup_free_ char *name = NULL; | |
608 | uid_t uid, verified; | |
609 | ||
610 | if (!dirent_is_file(de)) | |
611 | continue; | |
612 | ||
613 | r = parse_uid(de->d_name, &uid); | |
614 | if (r < 0) | |
615 | continue; | |
616 | ||
617 | r = direct_lookup_uid(uid, &name); | |
618 | if (r == -ENOMEM) | |
619 | return r; | |
620 | if (r < 0) | |
621 | continue; | |
622 | ||
623 | r = direct_lookup_name(name, &verified); | |
624 | if (r < 0) | |
625 | continue; | |
626 | ||
627 | if (uid != verified) | |
628 | continue; | |
629 | ||
630 | r = user_entry_add(p, name, uid); | |
631 | if (r == -ENOMEM) | |
632 | return r; | |
633 | if (r < 0) | |
634 | continue; | |
635 | } | |
636 | ||
637 | return 0; | |
638 | } | |
639 | ||
640 | static enum nss_status systemd_setent(GetentData *p) { | |
641 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; | |
642 | _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL; | |
643 | _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; | |
644 | const char *name; | |
645 | uid_t id; | |
646 | int bypass, r; | |
647 | ||
6e866b33 | 648 | PROTECT_ERRNO; |
b012e921 MB |
649 | BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); |
650 | ||
651 | assert(p); | |
652 | ||
653 | assert_se(pthread_mutex_lock(&p->mutex) == 0); | |
654 | ||
655 | systemd_endent(p); | |
656 | ||
657 | if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) | |
658 | goto finish; | |
659 | ||
660 | bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS"); | |
661 | ||
662 | if (bypass <= 0) { | |
663 | r = sd_bus_open_system(&bus); | |
664 | if (r < 0) | |
665 | bypass = 1; | |
666 | } | |
667 | ||
668 | if (bypass > 0) { | |
669 | r = direct_enumeration(p); | |
670 | if (r < 0) | |
671 | goto fail; | |
672 | ||
673 | goto finish; | |
674 | } | |
675 | ||
676 | r = sd_bus_call_method(bus, | |
677 | "org.freedesktop.systemd1", | |
678 | "/org/freedesktop/systemd1", | |
679 | "org.freedesktop.systemd1.Manager", | |
680 | "GetDynamicUsers", | |
681 | &error, | |
682 | &reply, | |
683 | NULL); | |
684 | if (r < 0) | |
685 | goto fail; | |
686 | ||
687 | r = sd_bus_message_enter_container(reply, 'a', "(us)"); | |
688 | if (r < 0) | |
689 | goto fail; | |
690 | ||
691 | while ((r = sd_bus_message_read(reply, "(us)", &id, &name)) > 0) { | |
692 | r = user_entry_add(p, name, id); | |
693 | if (r == -ENOMEM) | |
694 | goto fail; | |
695 | if (r < 0) | |
696 | continue; | |
697 | } | |
698 | if (r < 0) | |
699 | goto fail; | |
700 | ||
701 | r = sd_bus_message_exit_container(reply); | |
702 | if (r < 0) | |
703 | goto fail; | |
704 | ||
705 | finish: | |
706 | p->position = p->entries; | |
707 | assert_se(pthread_mutex_unlock(&p->mutex) == 0); | |
708 | ||
709 | return NSS_STATUS_SUCCESS; | |
710 | ||
711 | fail: | |
712 | systemd_endent(p); | |
713 | assert_se(pthread_mutex_unlock(&p->mutex) == 0); | |
714 | ||
715 | return NSS_STATUS_UNAVAIL; | |
716 | } | |
717 | ||
718 | enum nss_status _nss_systemd_setpwent(int stayopen) { | |
719 | return systemd_setent(&getpwent_data); | |
720 | } | |
721 | ||
722 | enum nss_status _nss_systemd_setgrent(int stayopen) { | |
723 | return systemd_setent(&getgrent_data); | |
724 | } | |
725 | ||
726 | enum nss_status _nss_systemd_getpwent_r(struct passwd *result, char *buffer, size_t buflen, int *errnop) { | |
727 | enum nss_status ret; | |
728 | UserEntry *p; | |
729 | size_t len; | |
730 | ||
6e866b33 | 731 | PROTECT_ERRNO; |
b012e921 MB |
732 | BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); |
733 | ||
734 | assert(result); | |
735 | assert(buffer); | |
736 | assert(errnop); | |
737 | ||
738 | assert_se(pthread_mutex_lock(&getpwent_data.mutex) == 0); | |
739 | ||
740 | LIST_FOREACH(entries, p, getpwent_data.position) { | |
741 | len = strlen(p->name) + 1; | |
742 | if (buflen < len) { | |
743 | *errnop = ERANGE; | |
744 | ret = NSS_STATUS_TRYAGAIN; | |
745 | goto finalize; | |
746 | } | |
747 | ||
748 | memcpy(buffer, p->name, len); | |
749 | ||
750 | result->pw_name = buffer; | |
751 | result->pw_uid = p->id; | |
752 | result->pw_gid = p->id; | |
753 | result->pw_gecos = (char*) DYNAMIC_USER_GECOS; | |
754 | result->pw_passwd = (char*) DYNAMIC_USER_PASSWD; | |
755 | result->pw_dir = (char*) DYNAMIC_USER_DIR; | |
756 | result->pw_shell = (char*) DYNAMIC_USER_SHELL; | |
757 | break; | |
758 | } | |
759 | if (!p) { | |
b012e921 MB |
760 | ret = NSS_STATUS_NOTFOUND; |
761 | goto finalize; | |
762 | } | |
763 | ||
764 | /* On success, step to the next entry. */ | |
765 | p = p->entries_next; | |
766 | ret = NSS_STATUS_SUCCESS; | |
767 | ||
768 | finalize: | |
769 | /* Save position for the next call. */ | |
770 | getpwent_data.position = p; | |
771 | ||
772 | assert_se(pthread_mutex_unlock(&getpwent_data.mutex) == 0); | |
773 | ||
774 | return ret; | |
775 | } | |
776 | ||
777 | enum nss_status _nss_systemd_getgrent_r(struct group *result, char *buffer, size_t buflen, int *errnop) { | |
778 | enum nss_status ret; | |
779 | UserEntry *p; | |
780 | size_t len; | |
781 | ||
6e866b33 | 782 | PROTECT_ERRNO; |
b012e921 MB |
783 | BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); |
784 | ||
785 | assert(result); | |
786 | assert(buffer); | |
787 | assert(errnop); | |
788 | ||
789 | assert_se(pthread_mutex_lock(&getgrent_data.mutex) == 0); | |
790 | ||
791 | LIST_FOREACH(entries, p, getgrent_data.position) { | |
792 | len = sizeof(char*) + strlen(p->name) + 1; | |
793 | if (buflen < len) { | |
794 | *errnop = ERANGE; | |
795 | ret = NSS_STATUS_TRYAGAIN; | |
796 | goto finalize; | |
797 | } | |
798 | ||
799 | memzero(buffer, sizeof(char*)); | |
800 | strcpy(buffer + sizeof(char*), p->name); | |
801 | ||
802 | result->gr_name = buffer + sizeof(char*); | |
803 | result->gr_gid = p->id; | |
804 | result->gr_passwd = (char*) DYNAMIC_USER_PASSWD; | |
805 | result->gr_mem = (char**) buffer; | |
806 | break; | |
807 | } | |
808 | if (!p) { | |
b012e921 MB |
809 | ret = NSS_STATUS_NOTFOUND; |
810 | goto finalize; | |
811 | } | |
812 | ||
813 | /* On success, step to the next entry. */ | |
814 | p = p->entries_next; | |
815 | ret = NSS_STATUS_SUCCESS; | |
816 | ||
817 | finalize: | |
818 | /* Save position for the next call. */ | |
819 | getgrent_data.position = p; | |
820 | ||
821 | assert_se(pthread_mutex_unlock(&getgrent_data.mutex) == 0); | |
822 | ||
823 | return ret; | |
824 | } |