관리-도구
편집 파일: UserSwitchingRules.h
/* * Phusion Passenger - https://www.phusionpassenger.com/ * Copyright (c) 2011-2018 Phusion Holding B.V. * * "Passenger", "Phusion Passenger" and "Union Station" are registered * trademarks of Phusion Holding B.V. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #ifndef _PASSENGER_SPAWNING_KIT_USER_SWITCHING_RULES_H_ #define _PASSENGER_SPAWNING_KIT_USER_SWITCHING_RULES_H_ #include <sys/types.h> #include <pwd.h> #include <grp.h> #include <unistd.h> #include <string> #include <algorithm> #include <boost/shared_array.hpp> #include <oxt/backtrace.hpp> #include <oxt/system_calls.hpp> #include <WrapperRegistry/Registry.h> #include <Exceptions.h> #include <Utils.h> #include <Core/SpawningKit/Context.h> #include <FileTools/PathManip.h> #include <SystemTools/UserDatabase.h> namespace Passenger { namespace SpawningKit { using namespace std; using namespace boost; using namespace oxt; struct UserSwitchingInfo { bool enabled; string username; string groupname; uid_t uid; gid_t gid; struct passwd lveUserPwd, *lveUserPwdComplete; boost::shared_array<char> lveUserPwdStrBuf; }; inline UserSwitchingInfo prepareUserSwitching(const AppPoolOptions &options, const WrapperRegistry::Registry &wrapperRegistry) { TRACE_POINT(); UserSwitchingInfo info; if (geteuid() != 0) { struct passwd &pwd = info.lveUserPwd; boost::shared_array<char> &strings = info.lveUserPwdStrBuf; struct passwd *userInfo; long bufSize; // _SC_GETPW_R_SIZE_MAX is not a maximum: // http://tomlee.co/2012/10/problems-with-large-linux-unix-groups-and-getgrgid_r-getgrnam_r/ bufSize = std::max<long>(1024 * 128, sysconf(_SC_GETPW_R_SIZE_MAX)); strings.reset(new char[bufSize]); userInfo = (struct passwd *) NULL; if (getpwuid_r(geteuid(), &pwd, strings.get(), bufSize, &userInfo) != 0 || userInfo == (struct passwd *) NULL) { throw RuntimeException("Cannot get user database entry for user " + lookupSystemUsernameByUid(geteuid()) + "; it looks like your system's " + "user database is broken, please fix it."); } info.enabled = false; info.username = userInfo->pw_name; info.groupname = lookupSystemGroupnameByGid(userInfo->pw_gid, true); info.uid = geteuid(); info.gid = getegid(); return info; } UPDATE_TRACE_POINT(); string defaultGroup; // This is the file that determines what user we lower privilege to. string referenceFile; struct passwd &pwd = info.lveUserPwd; boost::shared_array<char> &pwdBuf = info.lveUserPwdStrBuf; struct passwd *userInfo; struct group grp; gid_t groupId = (gid_t) -1; long pwdBufSize, grpBufSize; boost::shared_array<char> grpBuf; int ret; if (options.appType.empty()) { referenceFile = absolutizePath(options.appRoot); } else { referenceFile = absolutizePath(options.getStartupFile(wrapperRegistry), absolutizePath(options.appRoot)); } // _SC_GETPW_R_SIZE_MAX/_SC_GETGR_R_SIZE_MAX are not maximums: // http://tomlee.co/2012/10/problems-with-large-linux-unix-groups-and-getgrgid_r-getgrnam_r/ pwdBufSize = std::max<long>(1024 * 128, sysconf(_SC_GETPW_R_SIZE_MAX)); pwdBuf.reset(new char[pwdBufSize]); grpBufSize = std::max<long>(1024 * 128, sysconf(_SC_GETGR_R_SIZE_MAX)); grpBuf.reset(new char[grpBufSize]); if (options.defaultGroup.empty()) { struct passwd *info; struct group *group; info = (struct passwd *) NULL; ret = getpwnam_r(options.defaultUser.c_str(), &pwd, pwdBuf.get(), pwdBufSize, &info); if (ret != 0) { info = (struct passwd *) NULL; } if (info == (struct passwd *) NULL) { throw RuntimeException("Cannot get user database entry for username '" + options.defaultUser + "'"); } group = (struct group *) NULL; ret = getgrgid_r(info->pw_gid, &grp, grpBuf.get(), grpBufSize, &group); if (ret != 0) { group = (struct group *) NULL; } if (group == (struct group *) NULL) { throw RuntimeException(string("Cannot get group database entry for ") + "the default group belonging to username '" + options.defaultUser + "'"); } defaultGroup = group->gr_name; } else { defaultGroup = options.defaultGroup; } UPDATE_TRACE_POINT(); userInfo = (struct passwd *) NULL; if (!options.userSwitching) { // Keep userInfo at NULL so that it's set to defaultUser's UID. } else if (!options.user.empty()) { ret = getpwnam_r(options.user.c_str(), &pwd, pwdBuf.get(), pwdBufSize, &userInfo); if (ret != 0) { userInfo = (struct passwd *) NULL; } } else { struct stat buf; if (syscalls::lstat(referenceFile.c_str(), &buf) == -1) { int e = errno; throw SystemException("Cannot lstat(\"" + referenceFile + "\")", e); } ret = getpwuid_r(buf.st_uid, &pwd, pwdBuf.get(), pwdBufSize, &userInfo); if (ret != 0) { userInfo = (struct passwd *) NULL; } } if (userInfo == (struct passwd *) NULL || userInfo->pw_uid == 0) { userInfo = (struct passwd *) NULL; ret = getpwnam_r(options.defaultUser.c_str(), &pwd, pwdBuf.get(), pwdBufSize, &userInfo); if (ret != 0) { userInfo = (struct passwd *) NULL; } } UPDATE_TRACE_POINT(); if (!options.userSwitching) { // Keep groupId at -1 so that it's set to defaultGroup's GID. } else if (!options.group.empty()) { struct group *groupInfo = (struct group *) NULL; if (options.group == "!STARTUP_FILE!") { struct stat buf; if (syscalls::lstat(referenceFile.c_str(), &buf) == -1) { int e = errno; throw SystemException("Cannot lstat(\"" + referenceFile + "\")", e); } ret = getgrgid_r(buf.st_gid, &grp, grpBuf.get(), grpBufSize, &groupInfo); if (ret != 0) { groupInfo = (struct group *) NULL; } if (groupInfo != NULL) { groupId = buf.st_gid; } else { groupId = (gid_t) -1; } } else { ret = getgrnam_r(options.group.c_str(), &grp, grpBuf.get(), grpBufSize, &groupInfo); if (ret != 0) { groupInfo = (struct group *) NULL; } if (groupInfo != NULL) { groupId = groupInfo->gr_gid; } else { groupId = (gid_t) -1; } } } else if (userInfo != (struct passwd *) NULL) { groupId = userInfo->pw_gid; } if (groupId == 0 || groupId == (gid_t) -1) { OsGroup osGroup; if (lookupSystemGroupByName(defaultGroup, osGroup)) { groupId = osGroup.grp.gr_gid; } else if (looksLikePositiveNumber(defaultGroup)) { groupId = atoi(defaultGroup); } else { groupId = -1; } } UPDATE_TRACE_POINT(); if (userInfo == (struct passwd *) NULL) { throw RuntimeException("Cannot determine a user to lower privilege to"); } if (groupId == (gid_t) -1) { throw RuntimeException("Cannot determine a group to lower privilege to"); } UPDATE_TRACE_POINT(); info.enabled = true; info.username = userInfo->pw_name; info.groupname = lookupSystemGroupnameByGid(groupId, true); info.uid = userInfo->pw_uid; info.gid = groupId; return info; } } // namespace SpawningKit } // namespace Passenger #endif /* _PASSENGER_SPAWNING_KIT_USER_SWITCHING_RULES_H_ */