1 /*
2 * Copyright (C) Igor Sysoev
3 * Copyright (C) NGINX, Inc.
4 */
5
6 #include <nxt_main.h>
7
8
9 static nxt_int_t nxt_credential_groups_get(nxt_task_t *task, nxt_mp_t *mp,
10 nxt_credential_t *uc);
11
12
13 nxt_int_t
nxt_credential_get(nxt_task_t * task,nxt_mp_t * mp,nxt_credential_t * uc,const char * group)14 nxt_credential_get(nxt_task_t *task, nxt_mp_t *mp, nxt_credential_t *uc,
15 const char *group)
16 {
17 struct group *grp;
18 struct passwd *pwd;
19
20 nxt_errno = 0;
21
22 pwd = getpwnam(uc->user);
23
24 if (nxt_slow_path(pwd == NULL)) {
25
26 if (nxt_errno == 0) {
27 nxt_alert(task, "getpwnam(\"%s\") failed, user \"%s\" not found",
28 uc->user, uc->user);
29 } else {
30 nxt_alert(task, "getpwnam(\"%s\") failed %E", uc->user, nxt_errno);
31 }
32
33 return NXT_ERROR;
34 }
35
36 uc->uid = pwd->pw_uid;
37 uc->base_gid = pwd->pw_gid;
38
39 if (group != NULL && group[0] != '\0') {
40 nxt_errno = 0;
41
42 grp = getgrnam(group);
43
44 if (nxt_slow_path(grp == NULL)) {
45
46 if (nxt_errno == 0) {
47 nxt_alert(task,
48 "getgrnam(\"%s\") failed, group \"%s\" not found",
49 group, group);
50 } else {
51 nxt_alert(task, "getgrnam(\"%s\") failed %E", group, nxt_errno);
52 }
53
54 return NXT_ERROR;
55 }
56
57 uc->base_gid = grp->gr_gid;
58 }
59
60 nxt_debug(task, "about to get \"%s\" groups (uid:%d, base gid:%d)",
61 uc->user, uc->uid, uc->base_gid);
62
63 if (nxt_credential_groups_get(task, mp, uc) != NXT_OK) {
64 return NXT_ERROR;
65 }
66
67 #if (NXT_DEBUG)
68 {
69 u_char *p, *end;
70 nxt_uint_t i;
71 u_char msg[NXT_MAX_ERROR_STR];
72
73 p = msg;
74 end = msg + NXT_MAX_ERROR_STR;
75
76 for (i = 0; i < uc->ngroups; i++) {
77 p = nxt_sprintf(p, end, "%d,", uc->gids[i]);
78 }
79
80 if (uc->ngroups > 0) {
81 p--;
82 }
83
84 nxt_debug(task, "user \"%s\" has gids:%*s", uc->user, p - msg, msg);
85 }
86 #endif
87
88 return NXT_OK;
89 }
90
91
92 #if (NXT_HAVE_GETGROUPLIST && !NXT_MACOSX)
93
94 #define NXT_NGROUPS nxt_min(256, NGROUPS_MAX)
95
96
97 static nxt_int_t
nxt_credential_groups_get(nxt_task_t * task,nxt_mp_t * mp,nxt_credential_t * uc)98 nxt_credential_groups_get(nxt_task_t *task, nxt_mp_t *mp,
99 nxt_credential_t *uc)
100 {
101 int ngroups;
102 gid_t groups[NXT_NGROUPS];
103
104 ngroups = NXT_NGROUPS;
105
106 if (getgrouplist(uc->user, uc->base_gid, groups, &ngroups) < 0) {
107 if (nxt_slow_path(ngroups <= NXT_NGROUPS)) {
108 nxt_alert(task, "getgrouplist(\"%s\", %d, ...) failed %E", uc->user,
109 uc->base_gid, nxt_errno);
110
111 return NXT_ERROR;
112 }
113 }
114
115 if (ngroups > NXT_NGROUPS) {
116 if (ngroups > NGROUPS_MAX) {
117 ngroups = NGROUPS_MAX;
118 }
119
120 uc->ngroups = ngroups;
121
122 uc->gids = nxt_mp_alloc(mp, ngroups * sizeof(gid_t));
123 if (nxt_slow_path(uc->gids == NULL)) {
124 return NXT_ERROR;
125 }
126
127 if (nxt_slow_path(getgrouplist(uc->user, uc->base_gid, uc->gids,
128 &ngroups) < 0)) {
129
130 nxt_alert(task, "getgrouplist(\"%s\", %d) failed %E", uc->user,
131 uc->base_gid, nxt_errno);
132
133 return NXT_ERROR;
134 }
135
136 return NXT_OK;
137 }
138
139 uc->ngroups = ngroups;
140
141 uc->gids = nxt_mp_alloc(mp, ngroups * sizeof(gid_t));
142 if (nxt_slow_path(uc->gids == NULL)) {
143 return NXT_ERROR;
144 }
145
146 nxt_memcpy(uc->gids, groups, ngroups * sizeof(gid_t));
147
148 return NXT_OK;
149 }
150
151
152 #else
153
154 /*
155 * For operating systems that lack getgrouplist(3) or it's buggy (MacOS),
156 * nxt_credential_groups_get() stores an array of groups IDs which should be
157 * set by the setgroups() function for a given user. The initgroups()
158 * may block a just forked worker process for some time if LDAP or NDIS+
159 * is used, so nxt_credential_groups_get() allows to get worker user groups in
160 * main process. In a nutshell the initgroups() calls getgrouplist()
161 * followed by setgroups(). However older Solaris lacks the getgrouplist().
162 * Besides getgrouplist() does not allow to query the exact number of
163 * groups in some platforms, while NGROUPS_MAX can be quite large (e.g.
164 * 65536 on Linux).
165 * So nxt_credential_groups_get() emulates getgrouplist(): at first the
166 * function saves the super-user groups IDs, then calls initgroups() and saves
167 * the specified user groups IDs, and then restores the super-user groups IDs.
168 * This works at least on Linux, FreeBSD, and Solaris, but does not work
169 * on MacOSX, getgroups(2):
170 *
171 * To provide compatibility with applications that use getgroups() in
172 * environments where users may be in more than {NGROUPS_MAX} groups,
173 * a variant of getgroups(), obtained when compiling with either the
174 * macros _DARWIN_UNLIMITED_GETGROUPS or _DARWIN_C_SOURCE defined, can
175 * be used that is not limited to {NGROUPS_MAX} groups. However, this
176 * variant only returns the user's default group access list and not
177 * the group list modified by a call to setgroups(2).
178 *
179 * For such cases initgroups() is used in worker process as fallback.
180 */
181
182 static nxt_int_t
nxt_credential_groups_get(nxt_task_t * task,nxt_mp_t * mp,nxt_credential_t * uc)183 nxt_credential_groups_get(nxt_task_t *task, nxt_mp_t *mp, nxt_credential_t *uc)
184 {
185 int nsaved, ngroups;
186 nxt_int_t ret;
187 nxt_gid_t *saved;
188
189 nsaved = getgroups(0, NULL);
190
191 if (nxt_slow_path(nsaved == -1)) {
192 nxt_alert(task, "getgroups(0, NULL) failed %E", nxt_errno);
193 return NXT_ERROR;
194 }
195
196 nxt_debug(task, "getgroups(0, NULL): %d", nsaved);
197
198 if (nsaved > NGROUPS_MAX) {
199 /* MacOSX case. */
200
201 uc->gids = NULL;
202 uc->ngroups = 0;
203
204 return NXT_OK;
205 }
206
207 saved = nxt_mp_alloc(mp, nsaved * sizeof(nxt_gid_t));
208
209 if (nxt_slow_path(saved == NULL)) {
210 return NXT_ERROR;
211 }
212
213 ret = NXT_ERROR;
214
215 nsaved = getgroups(nsaved, saved);
216
217 if (nxt_slow_path(nsaved == -1)) {
218 nxt_alert(task, "getgroups(%d) failed %E", nsaved, nxt_errno);
219 goto free;
220 }
221
222 nxt_debug(task, "getgroups(): %d", nsaved);
223
224 if (initgroups(uc->user, uc->base_gid) != 0) {
225 if (nxt_errno == NXT_EPERM) {
226 nxt_log(task, NXT_LOG_NOTICE,
227 "initgroups(%s, %d) failed %E, ignored",
228 uc->user, uc->base_gid, nxt_errno);
229
230 ret = NXT_OK;
231
232 goto free;
233
234 } else {
235 nxt_alert(task, "initgroups(%s, %d) failed %E",
236 uc->user, uc->base_gid, nxt_errno);
237 goto restore;
238 }
239 }
240
241 ngroups = getgroups(0, NULL);
242
243 if (nxt_slow_path(ngroups == -1)) {
244 nxt_alert(task, "getgroups(0, NULL) failed %E", nxt_errno);
245 goto restore;
246 }
247
248 nxt_debug(task, "getgroups(0, NULL): %d", ngroups);
249
250 uc->gids = nxt_mp_alloc(mp, ngroups * sizeof(nxt_gid_t));
251
252 if (nxt_slow_path(uc->gids == NULL)) {
253 goto restore;
254 }
255
256 ngroups = getgroups(ngroups, uc->gids);
257
258 if (nxt_slow_path(ngroups == -1)) {
259 nxt_alert(task, "getgroups(%d) failed %E", ngroups, nxt_errno);
260 goto restore;
261 }
262
263 uc->ngroups = ngroups;
264
265 ret = NXT_OK;
266
267 restore:
268
269 if (nxt_slow_path(setgroups(nsaved, saved) != 0)) {
270 nxt_alert(task, "setgroups(%d) failed %E", nsaved, nxt_errno);
271 ret = NXT_ERROR;
272 }
273
274 free:
275
276 nxt_mp_free(mp, saved);
277
278 return ret;
279 }
280
281
282 #endif
283
284
285 nxt_int_t
nxt_credential_setuid(nxt_task_t * task,nxt_credential_t * uc)286 nxt_credential_setuid(nxt_task_t *task, nxt_credential_t *uc)
287 {
288 nxt_debug(task, "user cred set: \"%s\" uid:%d", uc->user, uc->uid);
289
290 if (setuid(uc->uid) != 0) {
291
292 #if (NXT_HAVE_LINUX_NS)
293 if (nxt_errno == EINVAL) {
294 nxt_log(task, NXT_LOG_ERR, "The uid %d (user \"%s\") isn't "
295 "valid in the application namespace.", uc->uid, uc->user);
296 return NXT_ERROR;
297 }
298 #endif
299
300 nxt_alert(task, "setuid(%d) failed %E", uc->uid, nxt_errno);
301 return NXT_ERROR;
302 }
303
304 return NXT_OK;
305 }
306
307
308 nxt_int_t
nxt_credential_setgids(nxt_task_t * task,nxt_credential_t * uc)309 nxt_credential_setgids(nxt_task_t *task, nxt_credential_t *uc)
310 {
311 nxt_runtime_t *rt;
312
313 nxt_debug(task, "user cred set gids: base gid:%d, ngroups: %d",
314 uc->base_gid, uc->ngroups);
315
316 rt = task->thread->runtime;
317
318 if (setgid(uc->base_gid) != 0) {
319
320 #if (NXT_HAVE_LINUX_NS)
321 if (nxt_errno == EINVAL) {
322 nxt_log(task, NXT_LOG_ERR, "The gid %d isn't valid in the "
323 "application namespace.", uc->base_gid);
324 return NXT_ERROR;
325 }
326 #endif
327
328 nxt_alert(task, "setgid(%d) failed %E", uc->base_gid, nxt_errno);
329 return NXT_ERROR;
330 }
331
332 if (!rt->capabilities.setid) {
333 return NXT_OK;
334 }
335
336 if (nxt_slow_path(uc->ngroups > 0
337 && setgroups(uc->ngroups, uc->gids) != 0)) {
338
339 #if (NXT_HAVE_LINUX_NS)
340 if (nxt_errno == EINVAL) {
341 nxt_log(task, NXT_LOG_ERR, "The user \"%s\" (uid: %d) has "
342 "supplementary group ids not valid in the application "
343 "namespace.", uc->user, uc->uid);
344 return NXT_ERROR;
345 }
346 #endif
347
348 nxt_alert(task, "setgroups(%i) failed %E", uc->ngroups, nxt_errno);
349 return NXT_ERROR;
350 }
351
352 return NXT_OK;
353 }
354