dnl Availability of various common functions (non-fatal if missing),
dnl and various less common threadsafe functions
AC_CHECK_FUNCS_ONCE([cfmakeraw geteuid getgid getgrnam_r getmntent_r \
- getpwuid_r getuid initgroups kill mmap newlocale posix_fallocate \
- posix_memalign prlimit regexec sched_getaffinity setns setrlimit symlink])
+ getpwuid_r getuid kill mmap newlocale posix_fallocate posix_memalign \
+ prlimit regexec sched_getaffinity setgroups setns setrlimit symlink])
dnl Availability of pthread functions (if missing, win32 threading is
dnl assumed). Because of $LIB_PTHREAD, we cannot use AC_CHECK_FUNCS_ONCE.
/*
- * Copyright (C) 2008-2012 Red Hat, Inc.
+ * Copyright (C) 2008-2013 Red Hat, Inc.
* Copyright (C) 2008 IBM Corp.
*
* lxc_container.c: file description
*/
static int lxcContainerSetID(virDomainDefPtr def)
{
+ gid_t *groups;
+ int ngroups;
+
/* Only call virSetUIDGID when user namespace is enabled
* for this container. And user namespace is only enabled
* when nuidmap&ngidmap is not zero */
VIR_DEBUG("Set UID/GID to 0/0");
- if (def->idmap.nuidmap && virSetUIDGID(0, 0) < 0) {
+ if (def->idmap.nuidmap &&
+ ((ngroups = virGetGroupList(0, 0, &groups) < 0) ||
+ virSetUIDGID(0, 0, groups, ngroups) < 0)) {
virReportSystemError(errno, "%s",
_("setuid or setgid failed"));
+ VIR_FREE(groups);
return -1;
}
+ VIR_FREE(groups);
return 0;
}
uid_t user;
gid_t group;
virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr);
+ gid_t *groups;
+ int ngroups;
+ int ret = -1;
if (virSecurityDACGetIds(def, priv, &user, &group))
return -1;
+ ngroups = virGetGroupList(user, group, &groups);
+ if (ngroups < 0)
+ return -1;
VIR_DEBUG("Dropping privileges of DEF to %u:%u",
(unsigned int) user, (unsigned int) group);
- if (virSetUIDGID(user, group) < 0)
- return -1;
-
- return 0;
+ if (virSetUIDGID(user, group, groups, ngroups) < 0)
+ goto cleanup;
+ ret = 0;
+cleanup:
+ VIR_FREE(groups);
+ return ret;
}
const char *binary = NULL;
int forkRet, ret;
struct sigaction waxon, waxoff;
+ gid_t *groups = NULL;
+ int ngroups;
if (cmd->args[0][0] != '/') {
if (!(binary = virFindFileInPath(cmd->args[0]))) {
childerr = null;
}
+ if ((ngroups = virGetGroupList(cmd->uid, cmd->gid, &groups)) < 0)
+ goto cleanup;
+
forkRet = virFork(&pid);
if (pid < 0) {
if (binary != cmd->args[0])
VIR_FREE(binary);
+ VIR_FREE(groups);
return 0;
}
cmd->capabilities || (cmd->flags & VIR_EXEC_CLEAR_CAPS)) {
VIR_DEBUG("Setting child uid:gid to %d:%d with caps %llx",
(int)cmd->uid, (int)cmd->gid, cmd->capabilities);
- if (virSetUIDGIDWithCaps(cmd->uid, cmd->gid, cmd->capabilities,
+ if (virSetUIDGIDWithCaps(cmd->uid, cmd->gid, groups, ngroups,
+ cmd->capabilities,
!!(cmd->flags & VIR_EXEC_CLEAR_CAPS)) < 0) {
goto fork_error;
}
/* This is cleanup of parent process only - child
should never jump here on error */
+ VIR_FREE(groups);
if (binary != cmd->args[0])
VIR_FREE(binary);
pid_t pid = 0;
int status, ret = 0;
int forkRet = 0;
+ gid_t *groups;
+ int ngroups;
if (uid == getuid() &&
gid == getgid())
return access(path, mode);
+ ngroups = virGetGroupList(uid, gid, &groups);
+ if (ngroups < 0)
+ return -1;
+
forkRet = virFork(&pid);
if (pid < 0) {
+ VIR_FREE(groups);
return -1;
}
if (pid) { /* parent */
+ VIR_FREE(groups);
if (virProcessWait(pid, &status) < 0) {
/* virProcessWait() already
* reported error */
goto childerror;
}
- if (virSetUIDGID(uid, gid) < 0) {
+ if (virSetUIDGID(uid, gid, groups, ngroups) < 0) {
ret = errno;
goto childerror;
}
int fd = -1;
int pair[2] = { -1, -1 };
int forkRet;
+ gid_t *groups;
+ int ngroups;
/* parent is running as root, but caller requested that the
* file be opened as some other user and/or group). The
* following dance avoids problems caused by root-squashing
* NFS servers. */
+ ngroups = virGetGroupList(uid, gid, &groups);
+ if (ngroups < 0)
+ return -errno;
+
if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) < 0) {
ret = -errno;
virReportSystemError(errno,
_("failed to create socket needed for '%s'"),
path);
+ VIR_FREE(groups);
return ret;
}
/* set desired uid/gid, then attempt to create the file */
- if (virSetUIDGID(uid, gid) < 0) {
+ if (virSetUIDGID(uid, gid, groups, ngroups) < 0) {
ret = -errno;
goto childerror;
}
/* parent */
+ VIR_FREE(groups);
VIR_FORCE_CLOSE(pair[1]);
do {
pid_t pid;
int waitret;
int status, ret = 0;
+ gid_t *groups;
+ int ngroups;
/* allow using -1 to mean "current value" */
if (uid == (uid_t) -1)
return virDirCreateNoFork(path, mode, uid, gid, flags);
}
+ ngroups = virGetGroupList(uid, gid, &groups);
+ if (ngroups < 0)
+ return -errno;
+
int forkRet = virFork(&pid);
if (pid < 0) {
ret = -errno;
+ VIR_FREE(groups);
return ret;
}
if (pid) { /* parent */
/* wait for child to complete, and retrieve its exit code */
+ VIR_FREE(groups);
while ((waitret = waitpid(pid, &status, 0) == -1) && (errno == EINTR));
if (waitret == -1) {
ret = -errno;
/* set desired uid/gid, then attempt to create the directory */
- if (virSetUIDGID(uid, gid) < 0) {
+ if (virSetUIDGID(uid, gid, groups, ngroups) < 0) {
ret = -errno;
goto childerror;
}
}
-/* Set the real and effective uid and gid to the given values, and call
- * initgroups so that the process has all the assumed group membership of
- * that uid. return 0 on success, -1 on failure (the original system error
- * remains in errno).
+/* Set the real and effective uid and gid to the given values, as well
+ * as all the supplementary groups, so that the process has all the
+ * assumed group membership of that uid. Return 0 on success, -1 on
+ * failure (the original system error remains in errno).
*/
int
-virSetUIDGID(uid_t uid, gid_t gid)
+virSetUIDGID(uid_t uid, gid_t gid, gid_t *groups ATTRIBUTE_UNUSED,
+ int ngroups ATTRIBUTE_UNUSED)
{
- int err;
- char *buf = NULL;
-
- if (gid != (gid_t)-1) {
- if (setregid(gid, gid) < 0) {
- virReportSystemError(err = errno,
- _("cannot change to '%u' group"),
- (unsigned int) gid);
- goto error;
- }
+ if (gid != (gid_t)-1 && setregid(gid, gid) < 0) {
+ virReportSystemError(errno,
+ _("cannot change to '%u' group"),
+ (unsigned int) gid);
+ return -1;
}
- if (uid != (uid_t)-1) {
-# ifdef HAVE_INITGROUPS
- struct passwd pwd, *pwd_result;
- size_t bufsize;
- int rc;
-
- bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
- if (bufsize == -1)
- bufsize = 16384;
-
- if (VIR_ALLOC_N(buf, bufsize) < 0) {
- err = ENOMEM;
- goto error;
- }
- while ((rc = getpwuid_r(uid, &pwd, buf, bufsize,
- &pwd_result)) == ERANGE) {
- if (VIR_RESIZE_N(buf, bufsize, bufsize, bufsize) < 0) {
- err = ENOMEM;
- goto error;
- }
- }
-
- if (rc) {
- virReportSystemError(err = rc, _("cannot getpwuid_r(%u)"),
- (unsigned int) uid);
- goto error;
- }
-
- if (!pwd_result) {
- virReportError(VIR_ERR_INTERNAL_ERROR,
- _("getpwuid_r failed to retrieve data "
- "for uid '%u'"),
- (unsigned int) uid);
- err = EINVAL;
- goto error;
- }
-
- if (initgroups(pwd.pw_name, pwd.pw_gid) < 0) {
- virReportSystemError(err = errno,
- _("cannot initgroups(\"%s\", %d)"),
- pwd.pw_name, (unsigned int) pwd.pw_gid);
- goto error;
- }
+# if HAVE_SETGROUPS
+ if (ngroups && setgroups(ngroups, groups) < 0) {
+ virReportSystemError(errno, "%s",
+ _("cannot set supplemental groups"));
+ return -1;
+ }
# endif
- if (setreuid(uid, uid) < 0) {
- virReportSystemError(err = errno,
- _("cannot change to uid to '%u'"),
- (unsigned int) uid);
- goto error;
- }
+
+ if (uid != (uid_t)-1 && setreuid(uid, uid) < 0) {
+ virReportSystemError(errno,
+ _("cannot change to uid to '%u'"),
+ (unsigned int) uid);
+ return -1;
}
- VIR_FREE(buf);
return 0;
-
-error:
- VIR_FREE(buf);
- errno = err;
- return -1;
}
#else /* ! HAVE_GETPWUID_R */
int
virSetUIDGID(uid_t uid ATTRIBUTE_UNUSED,
- gid_t gid ATTRIBUTE_UNUSED)
+ gid_t gid ATTRIBUTE_UNUSED,
+ gid_t *groups ATTRIBUTE_UNUSED,
+ int ngroups ATTRIBUTE_UNUSED)
{
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("virSetUIDGID is not available"));
* errno).
*/
int
-virSetUIDGIDWithCaps(uid_t uid, gid_t gid, unsigned long long capBits,
- bool clearExistingCaps)
+virSetUIDGIDWithCaps(uid_t uid, gid_t gid, gid_t *groups, int ngroups,
+ unsigned long long capBits, bool clearExistingCaps)
{
size_t i;
int capng_ret, ret = -1;
}
}
- if (virSetUIDGID(uid, gid) < 0)
+ if (virSetUIDGID(uid, gid, groups, ngroups) < 0)
goto cleanup;
/* Tell it we are done keeping capabilities */
*/
int
-virSetUIDGIDWithCaps(uid_t uid, gid_t gid,
+virSetUIDGIDWithCaps(uid_t uid, gid_t gid, gid_t *groups, int ngroups,
unsigned long long capBits ATTRIBUTE_UNUSED,
bool clearExistingCaps ATTRIBUTE_UNUSED)
{
- return virSetUIDGID(uid, gid);
+ return virSetUIDGID(uid, gid, groups, ngroups);
}
#endif
int virPipeReadUntilEOF(int outfd, int errfd,
char **outbuf, char **errbuf);
-int virSetUIDGID(uid_t uid, gid_t gid);
-int virSetUIDGIDWithCaps(uid_t uid, gid_t gid, unsigned long long capBits,
+int virSetUIDGID(uid_t uid, gid_t gid, gid_t *groups, int ngroups);
+int virSetUIDGIDWithCaps(uid_t uid, gid_t gid, gid_t *groups, int ngroups,
+ unsigned long long capBits,
bool clearExistingCaps);
int virScaleInteger(unsigned long long *value, const char *suffix,