Skip to content

Instantly share code, notes, and snippets.

@CamJN
Created May 10, 2018 17:12
Show Gist options
  • Save CamJN/9f7e850dd7e946146c4873db9b5381c0 to your computer and use it in GitHub Desktop.
Save CamJN/9f7e850dd7e946146c4873db9b5381c0 to your computer and use it in GitHub Desktop.
/*
* Phusion Passenger - https://www.phusionpassenger.com/
* Copyright (c) 2012-2017 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.
*/
#include <cassert>
#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>
#include <grp.h>
#include <limits.h>
#include <pwd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
using namespace std;
template<typename T> string
toString(T something) {
stringstream s;
s << something;
return s.str();
}
bool
looksLikePositiveNumber(const string &str) {
if (str.empty()) {
return false;
} else {
bool result = true;
const char *data = str.data();
const char *end = str.data() + str.size();
while (result && data != end) {
result = result && (*data >= '0' && *data <= '9');
data++;
}
return result;
}
}
static void
lookupUserGroup(uid_t *uid, struct passwd **userInfo, gid_t *gid, char* user, char* group)
{
errno = 0;
*userInfo = getpwnam(user);
if (*userInfo == NULL) {
int e = errno;
if (looksLikePositiveNumber(string(user))) {
fprintf(stderr,
"Warning: error looking up system user database"
" entry for user '%s': %s (errno=%d)\n",
user, strerror(e), e);
*uid = (uid_t) atoi(user);
} else {
fprintf(stderr, "user is not a positive number\n");
exit(1);
}
} else {
*uid = (*userInfo)->pw_uid;
}
errno = 0;
struct group *groupInfo = getgrnam(group);
if (groupInfo == NULL) {
int e = errno;
if (looksLikePositiveNumber(string(group))) {
fprintf(stderr,
"Warning: error looking up system group database entry for group '%s':"
" %s (errno=%d)\n",
group, strerror(e), e);
*gid = (gid_t) atoi(group);
} else {
fprintf(stderr, "group is not a positive number\n");
exit(1);
}
} else {
*gid = groupInfo->gr_gid;
}
}
static void
switchGroup(uid_t uid, const struct passwd *userInfo, gid_t gid) {
if (userInfo != NULL) {
bool setgroupsCalled = false;
#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
#ifdef __APPLE__
int groups[1024];
int ngroups = sizeof(groups) / sizeof(int);
#else
gid_t groups[1024];
int ngroups = sizeof(groups) / sizeof(gid_t);
#endif
int ret = getgrouplist(userInfo->pw_name, gid,
groups, &ngroups);
if (ret == -1) {
int e = errno;
fprintf(stderr, "could not get group list\n");
exit(1);
}
if (ngroups <= NGROUPS_MAX) {
setgroupsCalled = true;
gid_t* gidset = new gid_t[ngroups];
if (setgroups(ngroups, gidset) == -1) {
int e = errno;
fprintf(stderr, "could not set groups\n");
exit(1);
}
}
#endif
if (!setgroupsCalled && initgroups(userInfo->pw_name, gid) == -1) {
int e = errno;
fprintf(stderr, "could not init groups\n");
exit(1);
}
}
if (setgid(gid) == -1) {
int e = errno;
fprintf(stderr, "could not set gid\n");
exit(1);
}
}
static void
switchUser(uid_t uid, const struct passwd *userInfo) {
if (setuid(uid) == -1) {
int e = errno;
fprintf(stderr, "could not set uid\n");
exit(1);
}
if (userInfo != NULL) {
setenv("USER", userInfo->pw_name, 1);
setenv("LOGNAME", userInfo->pw_name, 1);
setenv("SHELL", userInfo->pw_shell, 1);
setenv("HOME", userInfo->pw_dir, 1);
} else {
unsetenv("USER");
unsetenv("LOGNAME");
unsetenv("SHELL");
unsetenv("HOME");
}
}
static void
dumpUserInfo() {
system("id");
}
int
main(int argc, char *argv[]) {
if (argc != 3) {
fprintf(stderr, "please call as %s username groupname\n",argv[0]);
exit(1);
}
if (geteuid() != 0) {
fprintf(stderr, "please run as root\n");
exit(1);
}
struct passwd *userInfo = NULL;
uid_t uid;
gid_t gid;
lookupUserGroup(&uid, &userInfo, &gid, argv[1], argv[2]);
fprintf(stdout, "Before any changes\n");
dumpUserInfo();
switchGroup(uid, userInfo, gid);
fprintf(stdout, "\n\n\nAfter switching groups\n");
dumpUserInfo();
switchUser(uid, userInfo);
fprintf(stdout, "\n\n\nAfter switching users\n");
dumpUserInfo();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment