summaryrefslogtreecommitdiffhomepage
path: root/src/nxt_process.c
diff options
context:
space:
mode:
authorTiago Natel <t.nateldemoura@f5.com>2019-11-26 16:15:23 +0000
committerTiago Natel <t.nateldemoura@f5.com>2019-11-26 16:15:23 +0000
commit2f23923e44d4528a547d2a29212ac93c3f0e25de (patch)
tree5164e0d914be7b2fc4ea676ac6defa6cf90b3b83 /src/nxt_process.c
parent224787bbaccfc2d065cbc6fb507820fe8d30cf61 (diff)
downloadunit-2f23923e44d4528a547d2a29212ac93c3f0e25de.tar.gz
unit-2f23923e44d4528a547d2a29212ac93c3f0e25de.tar.bz2
Changed the group listing to run unprivileged when possible.
Now the nxt_user_groups_get() function uses getgrouplist(3) when available (except MacOS, see below). For some platforms, getgrouplist() supports a method of probing how much groups the user has but the behavior is not consistent. The method used here consists of optimistically trying to get up to min(256, NGROUPS_MAX) groups; only if ngroups returned exceeds the original value, we do a second call. This method can block main's process if LDAP/NDIS+ is in use. MacOS has getgrouplist(3) but it's buggy. It doesn't update ngroups if the value passed is smaller than the number of groups the user has. Some projects (like Go stdlib) call getgrouplist() in a loop, increasing ngroups until it exceeds the number of groups user belongs to or fail when a limit is reached. For performance reasons, this is to be avoided and MacOS is handled in the fallback implementation. The fallback implementation is the old Unit approach. It saves main's user groups (getgroups(2)) and then calls initgroups(3) to load application's groups in main, then does a second getgroups(2) to store the gids and restore main's groups in the end. Because of initgroups(3)' call to setgroups(2), this method requires root capabilities. In the case of OSX, which has small NGROUPS_MAX by default (16), it's not possible to restore main's groups if it's large; if so, this method fallbacks again: user_cred gids aren't stored, and the worker process calls initgroups() itself and may block for some time if LDAP/NDIS+ is in use.
Diffstat (limited to '')
-rw-r--r--src/nxt_process.c134
1 files changed, 104 insertions, 30 deletions
diff --git a/src/nxt_process.c b/src/nxt_process.c
index b246a58c..64356d64 100644
--- a/src/nxt_process.c
+++ b/src/nxt_process.c
@@ -571,19 +571,109 @@ nxt_user_cred_get(nxt_task_t *task, nxt_user_cred_t *uc, const char *group)
uc->base_gid = grp->gr_gid;
}
- return nxt_user_groups_get(task, uc);
+ nxt_debug(task, "about to get \"%s\" groups (uid:%d, base gid:%d)",
+ uc->user, uc->uid, uc->base_gid);
+
+ if (nxt_user_groups_get(task, uc) != NXT_OK) {
+ return NXT_ERROR;
+ }
+
+#if (NXT_DEBUG)
+ {
+ u_char *p, *end;
+ nxt_uint_t i;
+ u_char msg[NXT_MAX_ERROR_STR];
+
+ p = msg;
+ end = msg + NXT_MAX_ERROR_STR;
+
+ for (i = 0; i < uc->ngroups; i++) {
+ p = nxt_sprintf(p, end, "%d%c", uc->gids[i],
+ i+1 < uc->ngroups ? ',' : '\0');
+ }
+
+ nxt_debug(task, "user \"%s\" has gids:%*s", uc->user, p - msg, msg);
+ }
+#endif
+
+ return NXT_OK;
}
+#if (NXT_HAVE_GETGROUPLIST && !NXT_MACOSX)
+
+#define NXT_NGROUPS nxt_min(256, NGROUPS_MAX)
+
+
+static nxt_int_t
+nxt_user_groups_get(nxt_task_t *task, nxt_user_cred_t *uc)
+{
+ int ngroups;
+ gid_t groups[NXT_NGROUPS];
+
+ ngroups = NXT_NGROUPS;
+
+ if (getgrouplist(uc->user, uc->base_gid, groups, &ngroups) < 0) {
+ if (nxt_slow_path(ngroups <= NXT_NGROUPS)) {
+ nxt_alert(task, "getgrouplist(\"%s\", %d, ...) failed %E", uc->user,
+ uc->base_gid, nxt_errno);
+
+ return NXT_ERROR;
+ }
+ }
+
+ if (ngroups > NXT_NGROUPS) {
+ if (ngroups > NGROUPS_MAX) {
+ ngroups = NGROUPS_MAX;
+ }
+
+ uc->ngroups = ngroups;
+
+ uc->gids = nxt_malloc(ngroups * sizeof(gid_t));
+ if (nxt_slow_path(uc->gids == NULL)) {
+ return NXT_ERROR;
+ }
+
+ if (nxt_slow_path(getgrouplist(uc->user, uc->base_gid, uc->gids,
+ &ngroups) < 0)) {
+
+ nxt_alert(task, "getgrouplist(\"%s\", %d) failed %E", uc->user,
+ uc->base_gid, nxt_errno);
+
+ nxt_free(uc->gids);
+
+ return NXT_ERROR;
+ }
+
+ return NXT_OK;
+ }
+
+ uc->ngroups = ngroups;
+
+ uc->gids = nxt_malloc(ngroups * sizeof(gid_t));
+ if (nxt_slow_path(uc->gids == NULL)) {
+ return NXT_ERROR;
+ }
+
+ nxt_memcpy(uc->gids, groups, ngroups * sizeof(gid_t));
+
+ return NXT_OK;
+}
+
+
+#else
+
/*
+ * For operating systems that lack getgrouplist(3) or it's buggy (MacOS),
* nxt_user_groups_get() stores an array of groups IDs which should be
- * set by the initgroups() function for a given user. The initgroups()
+ * set by the setgroups() function for a given user. The initgroups()
* may block a just forked worker process for some time if LDAP or NDIS+
* is used, so nxt_user_groups_get() allows to get worker user groups in
* main process. In a nutshell the initgroups() calls getgrouplist()
- * followed by setgroups(). However Solaris lacks the getgrouplist().
+ * followed by setgroups(). However older Solaris lacks the getgrouplist().
* Besides getgrouplist() does not allow to query the exact number of
- * groups while NGROUPS_MAX can be quite large (e.g. 65536 on Linux).
+ * groups in some platforms, while NGROUPS_MAX can be quite large (e.g.
+ * 65536 on Linux).
* So nxt_user_groups_get() emulates getgrouplist(): at first the function
* saves the super-user groups IDs, then calls initgroups() and saves the
* specified user groups IDs, and then restores the super-user groups IDs.
@@ -610,7 +700,7 @@ nxt_user_groups_get(nxt_task_t *task, nxt_user_cred_t *uc)
nsaved = getgroups(0, NULL);
- if (nsaved == -1) {
+ if (nxt_slow_path(nsaved == -1)) {
nxt_alert(task, "getgroups(0, NULL) failed %E", nxt_errno);
return NXT_ERROR;
}
@@ -628,7 +718,7 @@ nxt_user_groups_get(nxt_task_t *task, nxt_user_cred_t *uc)
saved = nxt_malloc(nsaved * sizeof(nxt_gid_t));
- if (saved == NULL) {
+ if (nxt_slow_path(saved == NULL)) {
return NXT_ERROR;
}
@@ -636,7 +726,7 @@ nxt_user_groups_get(nxt_task_t *task, nxt_user_cred_t *uc)
nsaved = getgroups(nsaved, saved);
- if (nsaved == -1) {
+ if (nxt_slow_path(nsaved == -1)) {
nxt_alert(task, "getgroups(%d) failed %E", nsaved, nxt_errno);
goto free;
}
@@ -662,7 +752,7 @@ nxt_user_groups_get(nxt_task_t *task, nxt_user_cred_t *uc)
ngroups = getgroups(0, NULL);
- if (ngroups == -1) {
+ if (nxt_slow_path(ngroups == -1)) {
nxt_alert(task, "getgroups(0, NULL) failed %E", nxt_errno);
goto restore;
}
@@ -671,43 +761,24 @@ nxt_user_groups_get(nxt_task_t *task, nxt_user_cred_t *uc)
uc->gids = nxt_malloc(ngroups * sizeof(nxt_gid_t));
- if (uc->gids == NULL) {
+ if (nxt_slow_path(uc->gids == NULL)) {
goto restore;
}
ngroups = getgroups(ngroups, uc->gids);
- if (ngroups == -1) {
+ if (nxt_slow_path(ngroups == -1)) {
nxt_alert(task, "getgroups(%d) failed %E", ngroups, nxt_errno);
goto restore;
}
uc->ngroups = ngroups;
-#if (NXT_DEBUG)
- {
- u_char *p, *end;
- nxt_uint_t i;
- u_char msg[NXT_MAX_ERROR_STR];
-
- p = msg;
- end = msg + NXT_MAX_ERROR_STR;
-
- for (i = 0; i < uc->ngroups; i++) {
- p = nxt_sprintf(p, end, "%uL:", (uint64_t) uc->gids[i]);
- }
-
- nxt_debug(task, "user \"%s\" cred: uid:%uL base gid:%uL, gids:%*s",
- uc->user, (uint64_t) uc->uid, (uint64_t) uc->base_gid,
- p - msg, msg);
- }
-#endif
-
ret = NXT_OK;
restore:
- if (setgroups(nsaved, saved) != 0) {
+ if (nxt_slow_path(setgroups(nsaved, saved) != 0)) {
nxt_alert(task, "setgroups(%d) failed %E", nsaved, nxt_errno);
ret = NXT_ERROR;
}
@@ -720,6 +791,9 @@ free:
}
+#endif
+
+
nxt_int_t
nxt_user_cred_set(nxt_task_t *task, nxt_user_cred_t *uc)
{