xref: /unit/src/nxt_credential.c (revision 1306)
11305St.nateldemoura@f5.com /*
21305St.nateldemoura@f5.com  * Copyright (C) Igor Sysoev
31305St.nateldemoura@f5.com  * Copyright (C) NGINX, Inc.
41305St.nateldemoura@f5.com  */
51305St.nateldemoura@f5.com 
61305St.nateldemoura@f5.com #include <nxt_main.h>
71305St.nateldemoura@f5.com 
81305St.nateldemoura@f5.com 
91305St.nateldemoura@f5.com static nxt_int_t nxt_credential_groups_get(nxt_task_t *task, nxt_mp_t *mp,
101305St.nateldemoura@f5.com     nxt_credential_t *uc);
111305St.nateldemoura@f5.com 
121305St.nateldemoura@f5.com 
131305St.nateldemoura@f5.com nxt_int_t
141305St.nateldemoura@f5.com nxt_credential_get(nxt_task_t *task, nxt_mp_t *mp, nxt_credential_t *uc,
151305St.nateldemoura@f5.com     const char *group)
161305St.nateldemoura@f5.com {
171305St.nateldemoura@f5.com     struct group   *grp;
181305St.nateldemoura@f5.com     struct passwd  *pwd;
191305St.nateldemoura@f5.com 
201305St.nateldemoura@f5.com     nxt_errno = 0;
211305St.nateldemoura@f5.com 
221305St.nateldemoura@f5.com     pwd = getpwnam(uc->user);
231305St.nateldemoura@f5.com 
241305St.nateldemoura@f5.com     if (nxt_slow_path(pwd == NULL)) {
251305St.nateldemoura@f5.com 
261305St.nateldemoura@f5.com         if (nxt_errno == 0) {
271305St.nateldemoura@f5.com             nxt_alert(task, "getpwnam(\"%s\") failed, user \"%s\" not found",
281305St.nateldemoura@f5.com                       uc->user, uc->user);
291305St.nateldemoura@f5.com         } else {
301305St.nateldemoura@f5.com             nxt_alert(task, "getpwnam(\"%s\") failed %E", uc->user, nxt_errno);
311305St.nateldemoura@f5.com         }
321305St.nateldemoura@f5.com 
331305St.nateldemoura@f5.com         return NXT_ERROR;
341305St.nateldemoura@f5.com     }
351305St.nateldemoura@f5.com 
361305St.nateldemoura@f5.com     uc->uid = pwd->pw_uid;
371305St.nateldemoura@f5.com     uc->base_gid = pwd->pw_gid;
381305St.nateldemoura@f5.com 
391305St.nateldemoura@f5.com     if (group != NULL && group[0] != '\0') {
401305St.nateldemoura@f5.com         nxt_errno = 0;
411305St.nateldemoura@f5.com 
421305St.nateldemoura@f5.com         grp = getgrnam(group);
431305St.nateldemoura@f5.com 
441305St.nateldemoura@f5.com         if (nxt_slow_path(grp == NULL)) {
451305St.nateldemoura@f5.com 
461305St.nateldemoura@f5.com             if (nxt_errno == 0) {
471305St.nateldemoura@f5.com                 nxt_alert(task,
481305St.nateldemoura@f5.com                           "getgrnam(\"%s\") failed, group \"%s\" not found",
491305St.nateldemoura@f5.com                           group, group);
501305St.nateldemoura@f5.com             } else {
511305St.nateldemoura@f5.com                 nxt_alert(task, "getgrnam(\"%s\") failed %E", group, nxt_errno);
521305St.nateldemoura@f5.com             }
531305St.nateldemoura@f5.com 
541305St.nateldemoura@f5.com             return NXT_ERROR;
551305St.nateldemoura@f5.com         }
561305St.nateldemoura@f5.com 
571305St.nateldemoura@f5.com         uc->base_gid = grp->gr_gid;
581305St.nateldemoura@f5.com     }
591305St.nateldemoura@f5.com 
601305St.nateldemoura@f5.com     nxt_debug(task, "about to get \"%s\" groups (uid:%d, base gid:%d)",
611305St.nateldemoura@f5.com               uc->user, uc->uid, uc->base_gid);
621305St.nateldemoura@f5.com 
631305St.nateldemoura@f5.com     if (nxt_credential_groups_get(task, mp, uc) != NXT_OK) {
641305St.nateldemoura@f5.com         return NXT_ERROR;
651305St.nateldemoura@f5.com     }
661305St.nateldemoura@f5.com 
671305St.nateldemoura@f5.com #if (NXT_DEBUG)
681305St.nateldemoura@f5.com     {
691305St.nateldemoura@f5.com         u_char      *p, *end;
701305St.nateldemoura@f5.com         nxt_uint_t  i;
711305St.nateldemoura@f5.com         u_char      msg[NXT_MAX_ERROR_STR];
721305St.nateldemoura@f5.com 
731305St.nateldemoura@f5.com         p = msg;
741305St.nateldemoura@f5.com         end = msg + NXT_MAX_ERROR_STR;
751305St.nateldemoura@f5.com 
761305St.nateldemoura@f5.com         for (i = 0; i < uc->ngroups; i++) {
771305St.nateldemoura@f5.com             p = nxt_sprintf(p, end, "%d%c", uc->gids[i],
781305St.nateldemoura@f5.com                             i+1 < uc->ngroups ? ',' : '\0');
791305St.nateldemoura@f5.com         }
801305St.nateldemoura@f5.com 
811305St.nateldemoura@f5.com         nxt_debug(task, "user \"%s\" has gids:%*s", uc->user, p - msg, msg);
821305St.nateldemoura@f5.com     }
831305St.nateldemoura@f5.com #endif
841305St.nateldemoura@f5.com 
851305St.nateldemoura@f5.com     return NXT_OK;
861305St.nateldemoura@f5.com }
871305St.nateldemoura@f5.com 
881305St.nateldemoura@f5.com 
891305St.nateldemoura@f5.com #if (NXT_HAVE_GETGROUPLIST && !NXT_MACOSX)
901305St.nateldemoura@f5.com 
911305St.nateldemoura@f5.com #define NXT_NGROUPS nxt_min(256, NGROUPS_MAX)
921305St.nateldemoura@f5.com 
931305St.nateldemoura@f5.com 
941305St.nateldemoura@f5.com static nxt_int_t
951305St.nateldemoura@f5.com nxt_credential_groups_get(nxt_task_t *task, nxt_mp_t *mp,
961305St.nateldemoura@f5.com     nxt_credential_t *uc)
971305St.nateldemoura@f5.com {
981305St.nateldemoura@f5.com     int    ngroups;
991305St.nateldemoura@f5.com     gid_t  groups[NXT_NGROUPS];
1001305St.nateldemoura@f5.com 
1011305St.nateldemoura@f5.com     ngroups = NXT_NGROUPS;
1021305St.nateldemoura@f5.com 
1031305St.nateldemoura@f5.com     if (getgrouplist(uc->user, uc->base_gid, groups, &ngroups) < 0) {
1041305St.nateldemoura@f5.com         if (nxt_slow_path(ngroups <= NXT_NGROUPS)) {
1051305St.nateldemoura@f5.com             nxt_alert(task, "getgrouplist(\"%s\", %d, ...) failed %E", uc->user,
1061305St.nateldemoura@f5.com                       uc->base_gid, nxt_errno);
1071305St.nateldemoura@f5.com 
1081305St.nateldemoura@f5.com             return NXT_ERROR;
1091305St.nateldemoura@f5.com         }
1101305St.nateldemoura@f5.com     }
1111305St.nateldemoura@f5.com 
1121305St.nateldemoura@f5.com     if (ngroups > NXT_NGROUPS) {
1131305St.nateldemoura@f5.com         if (ngroups > NGROUPS_MAX) {
1141305St.nateldemoura@f5.com             ngroups = NGROUPS_MAX;
1151305St.nateldemoura@f5.com         }
1161305St.nateldemoura@f5.com 
1171305St.nateldemoura@f5.com         uc->ngroups = ngroups;
1181305St.nateldemoura@f5.com 
1191305St.nateldemoura@f5.com         uc->gids = nxt_mp_alloc(mp, ngroups * sizeof(gid_t));
1201305St.nateldemoura@f5.com         if (nxt_slow_path(uc->gids == NULL)) {
1211305St.nateldemoura@f5.com             return NXT_ERROR;
1221305St.nateldemoura@f5.com         }
1231305St.nateldemoura@f5.com 
1241305St.nateldemoura@f5.com         if (nxt_slow_path(getgrouplist(uc->user, uc->base_gid, uc->gids,
1251305St.nateldemoura@f5.com                                        &ngroups) < 0)) {
1261305St.nateldemoura@f5.com 
1271305St.nateldemoura@f5.com             nxt_alert(task, "getgrouplist(\"%s\", %d) failed %E", uc->user,
1281305St.nateldemoura@f5.com                       uc->base_gid, nxt_errno);
1291305St.nateldemoura@f5.com 
1301305St.nateldemoura@f5.com             return NXT_ERROR;
1311305St.nateldemoura@f5.com         }
1321305St.nateldemoura@f5.com 
1331305St.nateldemoura@f5.com         return NXT_OK;
1341305St.nateldemoura@f5.com     }
1351305St.nateldemoura@f5.com 
1361305St.nateldemoura@f5.com     uc->ngroups = ngroups;
1371305St.nateldemoura@f5.com 
1381305St.nateldemoura@f5.com     uc->gids = nxt_mp_alloc(mp, ngroups * sizeof(gid_t));
1391305St.nateldemoura@f5.com     if (nxt_slow_path(uc->gids == NULL)) {
1401305St.nateldemoura@f5.com         return NXT_ERROR;
1411305St.nateldemoura@f5.com     }
1421305St.nateldemoura@f5.com 
1431305St.nateldemoura@f5.com     nxt_memcpy(uc->gids, groups, ngroups * sizeof(gid_t));
1441305St.nateldemoura@f5.com 
1451305St.nateldemoura@f5.com     return NXT_OK;
1461305St.nateldemoura@f5.com }
1471305St.nateldemoura@f5.com 
1481305St.nateldemoura@f5.com 
1491305St.nateldemoura@f5.com #else
1501305St.nateldemoura@f5.com 
1511305St.nateldemoura@f5.com /*
1521305St.nateldemoura@f5.com  * For operating systems that lack getgrouplist(3) or it's buggy (MacOS),
1531305St.nateldemoura@f5.com  * nxt_credential_groups_get() stores an array of groups IDs which should be
1541305St.nateldemoura@f5.com  * set by the setgroups() function for a given user.  The initgroups()
1551305St.nateldemoura@f5.com  * may block a just forked worker process for some time if LDAP or NDIS+
1561305St.nateldemoura@f5.com  * is used, so nxt_credential_groups_get() allows to get worker user groups in
1571305St.nateldemoura@f5.com  * main process.  In a nutshell the initgroups() calls getgrouplist()
1581305St.nateldemoura@f5.com  * followed by setgroups().  However older Solaris lacks the getgrouplist().
1591305St.nateldemoura@f5.com  * Besides getgrouplist() does not allow to query the exact number of
1601305St.nateldemoura@f5.com  * groups in some platforms, while NGROUPS_MAX can be quite large (e.g.
1611305St.nateldemoura@f5.com  * 65536 on Linux).
1621305St.nateldemoura@f5.com  * So nxt_credential_groups_get() emulates getgrouplist(): at first the
1631305St.nateldemoura@f5.com  * function saves the super-user groups IDs, then calls initgroups() and saves
1641305St.nateldemoura@f5.com  * the specified user groups IDs, and then restores the super-user groups IDs.
1651305St.nateldemoura@f5.com  * This works at least on Linux, FreeBSD, and Solaris, but does not work
1661305St.nateldemoura@f5.com  * on MacOSX, getgroups(2):
1671305St.nateldemoura@f5.com  *
1681305St.nateldemoura@f5.com  *   To provide compatibility with applications that use getgroups() in
1691305St.nateldemoura@f5.com  *   environments where users may be in more than {NGROUPS_MAX} groups,
1701305St.nateldemoura@f5.com  *   a variant of getgroups(), obtained when compiling with either the
1711305St.nateldemoura@f5.com  *   macros _DARWIN_UNLIMITED_GETGROUPS or _DARWIN_C_SOURCE defined, can
1721305St.nateldemoura@f5.com  *   be used that is not limited to {NGROUPS_MAX} groups.  However, this
1731305St.nateldemoura@f5.com  *   variant only returns the user's default group access list and not
1741305St.nateldemoura@f5.com  *   the group list modified by a call to setgroups(2).
1751305St.nateldemoura@f5.com  *
1761305St.nateldemoura@f5.com  * For such cases initgroups() is used in worker process as fallback.
1771305St.nateldemoura@f5.com  */
1781305St.nateldemoura@f5.com 
1791305St.nateldemoura@f5.com static nxt_int_t
1801305St.nateldemoura@f5.com nxt_credential_groups_get(nxt_task_t *task, nxt_mp_t *mp, nxt_credential_t *uc)
1811305St.nateldemoura@f5.com {
1821305St.nateldemoura@f5.com     int        nsaved, ngroups;
1831305St.nateldemoura@f5.com     nxt_int_t  ret;
1841305St.nateldemoura@f5.com     nxt_gid_t  *saved;
1851305St.nateldemoura@f5.com 
1861305St.nateldemoura@f5.com     nsaved = getgroups(0, NULL);
1871305St.nateldemoura@f5.com 
1881305St.nateldemoura@f5.com     if (nxt_slow_path(nsaved == -1)) {
1891305St.nateldemoura@f5.com         nxt_alert(task, "getgroups(0, NULL) failed %E", nxt_errno);
1901305St.nateldemoura@f5.com         return NXT_ERROR;
1911305St.nateldemoura@f5.com     }
1921305St.nateldemoura@f5.com 
1931305St.nateldemoura@f5.com     nxt_debug(task, "getgroups(0, NULL): %d", nsaved);
1941305St.nateldemoura@f5.com 
1951305St.nateldemoura@f5.com     if (nsaved > NGROUPS_MAX) {
1961305St.nateldemoura@f5.com         /* MacOSX case. */
1971305St.nateldemoura@f5.com 
1981305St.nateldemoura@f5.com         uc->gids = NULL;
1991305St.nateldemoura@f5.com         uc->ngroups = 0;
2001305St.nateldemoura@f5.com 
2011305St.nateldemoura@f5.com         return NXT_OK;
2021305St.nateldemoura@f5.com     }
2031305St.nateldemoura@f5.com 
2041305St.nateldemoura@f5.com     saved = nxt_mp_alloc(mp, nsaved * sizeof(nxt_gid_t));
2051305St.nateldemoura@f5.com 
2061305St.nateldemoura@f5.com     if (nxt_slow_path(saved == NULL)) {
2071305St.nateldemoura@f5.com         return NXT_ERROR;
2081305St.nateldemoura@f5.com     }
2091305St.nateldemoura@f5.com 
2101305St.nateldemoura@f5.com     ret = NXT_ERROR;
2111305St.nateldemoura@f5.com 
2121305St.nateldemoura@f5.com     nsaved = getgroups(nsaved, saved);
2131305St.nateldemoura@f5.com 
2141305St.nateldemoura@f5.com     if (nxt_slow_path(nsaved == -1)) {
2151305St.nateldemoura@f5.com         nxt_alert(task, "getgroups(%d) failed %E", nsaved, nxt_errno);
2161305St.nateldemoura@f5.com         goto free;
2171305St.nateldemoura@f5.com     }
2181305St.nateldemoura@f5.com 
2191305St.nateldemoura@f5.com     nxt_debug(task, "getgroups(): %d", nsaved);
2201305St.nateldemoura@f5.com 
2211305St.nateldemoura@f5.com     if (initgroups(uc->user, uc->base_gid) != 0) {
2221305St.nateldemoura@f5.com         if (nxt_errno == NXT_EPERM) {
2231305St.nateldemoura@f5.com             nxt_log(task, NXT_LOG_NOTICE,
2241305St.nateldemoura@f5.com                     "initgroups(%s, %d) failed %E, ignored",
2251305St.nateldemoura@f5.com                     uc->user, uc->base_gid, nxt_errno);
2261305St.nateldemoura@f5.com 
2271305St.nateldemoura@f5.com             ret = NXT_OK;
2281305St.nateldemoura@f5.com 
2291305St.nateldemoura@f5.com             goto free;
2301305St.nateldemoura@f5.com 
2311305St.nateldemoura@f5.com         } else {
2321305St.nateldemoura@f5.com             nxt_alert(task, "initgroups(%s, %d) failed %E",
2331305St.nateldemoura@f5.com                       uc->user, uc->base_gid, nxt_errno);
2341305St.nateldemoura@f5.com             goto restore;
2351305St.nateldemoura@f5.com         }
2361305St.nateldemoura@f5.com     }
2371305St.nateldemoura@f5.com 
2381305St.nateldemoura@f5.com     ngroups = getgroups(0, NULL);
2391305St.nateldemoura@f5.com 
2401305St.nateldemoura@f5.com     if (nxt_slow_path(ngroups == -1)) {
2411305St.nateldemoura@f5.com         nxt_alert(task, "getgroups(0, NULL) failed %E", nxt_errno);
2421305St.nateldemoura@f5.com         goto restore;
2431305St.nateldemoura@f5.com     }
2441305St.nateldemoura@f5.com 
2451305St.nateldemoura@f5.com     nxt_debug(task, "getgroups(0, NULL): %d", ngroups);
2461305St.nateldemoura@f5.com 
2471305St.nateldemoura@f5.com     uc->gids = nxt_mp_alloc(mp, ngroups * sizeof(nxt_gid_t));
2481305St.nateldemoura@f5.com 
2491305St.nateldemoura@f5.com     if (nxt_slow_path(uc->gids == NULL)) {
2501305St.nateldemoura@f5.com         goto restore;
2511305St.nateldemoura@f5.com     }
2521305St.nateldemoura@f5.com 
2531305St.nateldemoura@f5.com     ngroups = getgroups(ngroups, uc->gids);
2541305St.nateldemoura@f5.com 
2551305St.nateldemoura@f5.com     if (nxt_slow_path(ngroups == -1)) {
2561305St.nateldemoura@f5.com         nxt_alert(task, "getgroups(%d) failed %E", ngroups, nxt_errno);
2571305St.nateldemoura@f5.com         goto restore;
2581305St.nateldemoura@f5.com     }
2591305St.nateldemoura@f5.com 
2601305St.nateldemoura@f5.com     uc->ngroups = ngroups;
2611305St.nateldemoura@f5.com 
2621305St.nateldemoura@f5.com     ret = NXT_OK;
2631305St.nateldemoura@f5.com 
2641305St.nateldemoura@f5.com restore:
2651305St.nateldemoura@f5.com 
2661305St.nateldemoura@f5.com     if (nxt_slow_path(setgroups(nsaved, saved) != 0)) {
2671305St.nateldemoura@f5.com         nxt_alert(task, "setgroups(%d) failed %E", nsaved, nxt_errno);
2681305St.nateldemoura@f5.com         ret = NXT_ERROR;
2691305St.nateldemoura@f5.com     }
2701305St.nateldemoura@f5.com 
2711305St.nateldemoura@f5.com free:
2721305St.nateldemoura@f5.com 
2731305St.nateldemoura@f5.com     nxt_mp_free(mp, saved);
2741305St.nateldemoura@f5.com 
2751305St.nateldemoura@f5.com     return ret;
2761305St.nateldemoura@f5.com }
2771305St.nateldemoura@f5.com 
2781305St.nateldemoura@f5.com 
2791305St.nateldemoura@f5.com #endif
2801305St.nateldemoura@f5.com 
2811305St.nateldemoura@f5.com 
2821305St.nateldemoura@f5.com nxt_int_t
283*1306St.nateldemoura@f5.com nxt_credential_setuid(nxt_task_t *task, nxt_credential_t *uc)
2841305St.nateldemoura@f5.com {
285*1306St.nateldemoura@f5.com     nxt_debug(task, "user cred set: \"%s\" uid:%d", uc->user, uc->uid);
2861305St.nateldemoura@f5.com 
2871305St.nateldemoura@f5.com     if (setuid(uc->uid) != 0) {
2881305St.nateldemoura@f5.com 
2891305St.nateldemoura@f5.com #if (NXT_HAVE_CLONE)
2901305St.nateldemoura@f5.com         if (nxt_errno == EINVAL) {
2911305St.nateldemoura@f5.com             nxt_log(task, NXT_LOG_ERR, "The uid %d (user \"%s\") isn't "
2921305St.nateldemoura@f5.com                     "valid in the application namespace.", uc->uid, uc->user);
2931305St.nateldemoura@f5.com             return NXT_ERROR;
2941305St.nateldemoura@f5.com         }
2951305St.nateldemoura@f5.com #endif
2961305St.nateldemoura@f5.com 
2971305St.nateldemoura@f5.com         nxt_alert(task, "setuid(%d) failed %E", uc->uid, nxt_errno);
2981305St.nateldemoura@f5.com         return NXT_ERROR;
2991305St.nateldemoura@f5.com     }
3001305St.nateldemoura@f5.com 
3011305St.nateldemoura@f5.com     return NXT_OK;
3021305St.nateldemoura@f5.com }
303*1306St.nateldemoura@f5.com 
304*1306St.nateldemoura@f5.com 
305*1306St.nateldemoura@f5.com nxt_int_t
306*1306St.nateldemoura@f5.com nxt_credential_setgids(nxt_task_t *task, nxt_credential_t *uc)
307*1306St.nateldemoura@f5.com {
308*1306St.nateldemoura@f5.com     nxt_runtime_t  *rt;
309*1306St.nateldemoura@f5.com 
310*1306St.nateldemoura@f5.com     nxt_debug(task, "user cred set gids: base gid:%d, ngroups: %d",
311*1306St.nateldemoura@f5.com               uc->base_gid, uc->ngroups);
312*1306St.nateldemoura@f5.com 
313*1306St.nateldemoura@f5.com     rt = task->thread->runtime;
314*1306St.nateldemoura@f5.com 
315*1306St.nateldemoura@f5.com     if (setgid(uc->base_gid) != 0) {
316*1306St.nateldemoura@f5.com 
317*1306St.nateldemoura@f5.com #if (NXT_HAVE_CLONE)
318*1306St.nateldemoura@f5.com         if (nxt_errno == EINVAL) {
319*1306St.nateldemoura@f5.com             nxt_log(task, NXT_LOG_ERR, "The gid %d isn't valid in the "
320*1306St.nateldemoura@f5.com                     "application namespace.", uc->base_gid);
321*1306St.nateldemoura@f5.com             return NXT_ERROR;
322*1306St.nateldemoura@f5.com         }
323*1306St.nateldemoura@f5.com #endif
324*1306St.nateldemoura@f5.com 
325*1306St.nateldemoura@f5.com         nxt_alert(task, "setgid(%d) failed %E", uc->base_gid, nxt_errno);
326*1306St.nateldemoura@f5.com         return NXT_ERROR;
327*1306St.nateldemoura@f5.com     }
328*1306St.nateldemoura@f5.com 
329*1306St.nateldemoura@f5.com     if (!rt->capabilities.setid) {
330*1306St.nateldemoura@f5.com         return NXT_OK;
331*1306St.nateldemoura@f5.com     }
332*1306St.nateldemoura@f5.com 
333*1306St.nateldemoura@f5.com     if (nxt_slow_path(uc->ngroups > 0
334*1306St.nateldemoura@f5.com                       && setgroups(uc->ngroups, uc->gids) != 0)) {
335*1306St.nateldemoura@f5.com 
336*1306St.nateldemoura@f5.com #if (NXT_HAVE_CLONE)
337*1306St.nateldemoura@f5.com         if (nxt_errno == EINVAL) {
338*1306St.nateldemoura@f5.com             nxt_log(task, NXT_LOG_ERR, "The user \"%s\" (uid: %d) has "
339*1306St.nateldemoura@f5.com                     "supplementary group ids not valid in the application "
340*1306St.nateldemoura@f5.com                     "namespace.", uc->user, uc->uid);
341*1306St.nateldemoura@f5.com             return NXT_ERROR;
342*1306St.nateldemoura@f5.com         }
343*1306St.nateldemoura@f5.com #endif
344*1306St.nateldemoura@f5.com 
345*1306St.nateldemoura@f5.com         nxt_alert(task, "setgroups(%i) failed %E", uc->ngroups, nxt_errno);
346*1306St.nateldemoura@f5.com         return NXT_ERROR;
347*1306St.nateldemoura@f5.com     }
348*1306St.nateldemoura@f5.com 
349*1306St.nateldemoura@f5.com     return NXT_OK;
350*1306St.nateldemoura@f5.com }
351