diff options
author | Tiago Natel <t.nateldemoura@f5.com> | 2019-12-06 13:28:05 +0000 |
---|---|---|
committer | Tiago Natel <t.nateldemoura@f5.com> | 2019-12-06 13:28:05 +0000 |
commit | ed2492a66afdf578d1e4f99dc098ab685607b3ba (patch) | |
tree | 188ac507ad6efdd02e8aae6adf1dbb5cd406fdbc /src/nxt_credential.c | |
parent | c6a11260e8ad5fa64cb025f72aab343aea9f1d63 (diff) | |
download | unit-ed2492a66afdf578d1e4f99dc098ab685607b3ba.tar.gz unit-ed2492a66afdf578d1e4f99dc098ab685607b3ba.tar.bz2 |
Moved credential-related code to nxt_credential.c.
This is required to avoid include cycles, as some nxt_clone_* functions
depend on the credential structures, but nxt_process depends on clone
structures.
Diffstat (limited to '')
-rw-r--r-- | src/nxt_credential.c | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/src/nxt_credential.c b/src/nxt_credential.c new file mode 100644 index 00000000..9f275b7d --- /dev/null +++ b/src/nxt_credential.c @@ -0,0 +1,342 @@ +/* + * Copyright (C) Igor Sysoev + * Copyright (C) NGINX, Inc. + */ + +#include <nxt_main.h> + + +static nxt_int_t nxt_credential_groups_get(nxt_task_t *task, nxt_mp_t *mp, + nxt_credential_t *uc); + + +nxt_int_t +nxt_credential_get(nxt_task_t *task, nxt_mp_t *mp, nxt_credential_t *uc, + const char *group) +{ + struct group *grp; + struct passwd *pwd; + + nxt_errno = 0; + + pwd = getpwnam(uc->user); + + if (nxt_slow_path(pwd == NULL)) { + + if (nxt_errno == 0) { + nxt_alert(task, "getpwnam(\"%s\") failed, user \"%s\" not found", + uc->user, uc->user); + } else { + nxt_alert(task, "getpwnam(\"%s\") failed %E", uc->user, nxt_errno); + } + + return NXT_ERROR; + } + + uc->uid = pwd->pw_uid; + uc->base_gid = pwd->pw_gid; + + if (group != NULL && group[0] != '\0') { + nxt_errno = 0; + + grp = getgrnam(group); + + if (nxt_slow_path(grp == NULL)) { + + if (nxt_errno == 0) { + nxt_alert(task, + "getgrnam(\"%s\") failed, group \"%s\" not found", + group, group); + } else { + nxt_alert(task, "getgrnam(\"%s\") failed %E", group, nxt_errno); + } + + return NXT_ERROR; + } + + uc->base_gid = grp->gr_gid; + } + + nxt_debug(task, "about to get \"%s\" groups (uid:%d, base gid:%d)", + uc->user, uc->uid, uc->base_gid); + + if (nxt_credential_groups_get(task, mp, 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_credential_groups_get(nxt_task_t *task, nxt_mp_t *mp, + nxt_credential_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_mp_alloc(mp, 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); + + return NXT_ERROR; + } + + return NXT_OK; + } + + uc->ngroups = ngroups; + + uc->gids = nxt_mp_alloc(mp, 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_credential_groups_get() stores an array of groups IDs which should be + * 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_credential_groups_get() allows to get worker user groups in + * main process. In a nutshell the initgroups() calls getgrouplist() + * followed by setgroups(). However older Solaris lacks the getgrouplist(). + * Besides getgrouplist() does not allow to query the exact number of + * groups in some platforms, while NGROUPS_MAX can be quite large (e.g. + * 65536 on Linux). + * So nxt_credential_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. + * This works at least on Linux, FreeBSD, and Solaris, but does not work + * on MacOSX, getgroups(2): + * + * To provide compatibility with applications that use getgroups() in + * environments where users may be in more than {NGROUPS_MAX} groups, + * a variant of getgroups(), obtained when compiling with either the + * macros _DARWIN_UNLIMITED_GETGROUPS or _DARWIN_C_SOURCE defined, can + * be used that is not limited to {NGROUPS_MAX} groups. However, this + * variant only returns the user's default group access list and not + * the group list modified by a call to setgroups(2). + * + * For such cases initgroups() is used in worker process as fallback. + */ + +static nxt_int_t +nxt_credential_groups_get(nxt_task_t *task, nxt_mp_t *mp, nxt_credential_t *uc) +{ + int nsaved, ngroups; + nxt_int_t ret; + nxt_gid_t *saved; + + nsaved = getgroups(0, NULL); + + if (nxt_slow_path(nsaved == -1)) { + nxt_alert(task, "getgroups(0, NULL) failed %E", nxt_errno); + return NXT_ERROR; + } + + nxt_debug(task, "getgroups(0, NULL): %d", nsaved); + + if (nsaved > NGROUPS_MAX) { + /* MacOSX case. */ + + uc->gids = NULL; + uc->ngroups = 0; + + return NXT_OK; + } + + saved = nxt_mp_alloc(mp, nsaved * sizeof(nxt_gid_t)); + + if (nxt_slow_path(saved == NULL)) { + return NXT_ERROR; + } + + ret = NXT_ERROR; + + nsaved = getgroups(nsaved, saved); + + if (nxt_slow_path(nsaved == -1)) { + nxt_alert(task, "getgroups(%d) failed %E", nsaved, nxt_errno); + goto free; + } + + nxt_debug(task, "getgroups(): %d", nsaved); + + if (initgroups(uc->user, uc->base_gid) != 0) { + if (nxt_errno == NXT_EPERM) { + nxt_log(task, NXT_LOG_NOTICE, + "initgroups(%s, %d) failed %E, ignored", + uc->user, uc->base_gid, nxt_errno); + + ret = NXT_OK; + + goto free; + + } else { + nxt_alert(task, "initgroups(%s, %d) failed %E", + uc->user, uc->base_gid, nxt_errno); + goto restore; + } + } + + ngroups = getgroups(0, NULL); + + if (nxt_slow_path(ngroups == -1)) { + nxt_alert(task, "getgroups(0, NULL) failed %E", nxt_errno); + goto restore; + } + + nxt_debug(task, "getgroups(0, NULL): %d", ngroups); + + uc->gids = nxt_mp_alloc(mp, ngroups * sizeof(nxt_gid_t)); + + if (nxt_slow_path(uc->gids == NULL)) { + goto restore; + } + + ngroups = getgroups(ngroups, uc->gids); + + if (nxt_slow_path(ngroups == -1)) { + nxt_alert(task, "getgroups(%d) failed %E", ngroups, nxt_errno); + goto restore; + } + + uc->ngroups = ngroups; + + ret = NXT_OK; + +restore: + + if (nxt_slow_path(setgroups(nsaved, saved) != 0)) { + nxt_alert(task, "setgroups(%d) failed %E", nsaved, nxt_errno); + ret = NXT_ERROR; + } + +free: + + nxt_mp_free(mp, saved); + + return ret; +} + + +#endif + + +nxt_int_t +nxt_credential_set(nxt_task_t *task, nxt_credential_t *uc) +{ + nxt_debug(task, "user cred set: \"%s\" uid:%d base gid:%d", + uc->user, uc->uid, uc->base_gid); + + if (setgid(uc->base_gid) != 0) { + +#if (NXT_HAVE_CLONE) + if (nxt_errno == EINVAL) { + nxt_log(task, NXT_LOG_ERR, "The gid %d isn't valid in the " + "application namespace.", uc->base_gid); + return NXT_ERROR; + } +#endif + + nxt_alert(task, "setgid(%d) failed %E", uc->base_gid, nxt_errno); + return NXT_ERROR; + } + + if (uc->gids != NULL) { + if (setgroups(uc->ngroups, uc->gids) != 0) { + +#if (NXT_HAVE_CLONE) + if (nxt_errno == EINVAL) { + nxt_log(task, NXT_LOG_ERR, "The user \"%s\" (uid: %d) has " + "supplementary group ids not valid in the application " + "namespace.", uc->user, uc->uid); + return NXT_ERROR; + } +#endif + + nxt_alert(task, "setgroups(%i) failed %E", uc->ngroups, nxt_errno); + return NXT_ERROR; + } + + } else { + /* MacOSX fallback. */ + if (initgroups(uc->user, uc->base_gid) != 0) { + nxt_alert(task, "initgroups(%s, %d) failed %E", + uc->user, uc->base_gid, nxt_errno); + return NXT_ERROR; + } + } + + if (setuid(uc->uid) != 0) { + +#if (NXT_HAVE_CLONE) + if (nxt_errno == EINVAL) { + nxt_log(task, NXT_LOG_ERR, "The uid %d (user \"%s\") isn't " + "valid in the application namespace.", uc->uid, uc->user); + return NXT_ERROR; + } +#endif + + nxt_alert(task, "setuid(%d) failed %E", uc->uid, nxt_errno); + return NXT_ERROR; + } + + return NXT_OK; +} |