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