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