diff --git a/configure.ac b/configure.ac index 90fe145bbf..85db01f5e3 100644 --- a/configure.ac +++ b/configure.ac @@ -93,7 +93,7 @@ AC_MSG_RESULT([$have_cpuid]) dnl Availability of various common functions (non-fatal if missing). -AC_CHECK_FUNCS_ONCE([cfmakeraw regexec sched_getaffinity getuid getgid \ +AC_CHECK_FUNCS_ONCE([cfmakeraw regexec sched_getaffinity getuid getgid initgroups \ posix_fallocate mmap]) dnl Availability of various not common threadsafe functions diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 05c4af8ab7..a959ad92ed 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -859,6 +859,7 @@ virRun; virRunWithHook; virSetCloseExec; virSetNonBlock; +virSetUIDGID; virSkipSpaces; virStrToDouble; virStrToLong_i; diff --git a/src/util/util.c b/src/util/util.c index 7d3377b504..d6fa81b4af 100644 --- a/src/util/util.c +++ b/src/util/util.c @@ -2811,6 +2811,61 @@ int virGetGroupID(const char *name, return 0; } + +/* 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. + */ +int +virSetUIDGID(uid_t uid, gid_t gid) +{ + if (gid > 0) { + if (setregid(gid, gid) < 0) { + virReportSystemError(errno, + _("cannot change to '%d' group"), gid); + return -1; + } + } + + if (uid > 0) { +# ifdef HAVE_INITGROUPS + struct passwd pwd, *pwd_result; + char *buf = NULL; + size_t bufsize; + + bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); + if (bufsize == -1) + bufsize = 16384; + + if (VIR_ALLOC_N(buf, bufsize) < 0) { + virReportOOMError(); + return -1; + } + getpwuid_r(uid, &pwd, buf, bufsize, &pwd_result); + if (!pwd_result) { + virReportSystemError(errno, + _("cannot getpwuid_r(%d)"), uid); + VIR_FREE(buf); + return -1; + } + if (initgroups(pwd.pw_name, pwd.pw_gid) < 0) { + virReportSystemError(errno, + _("cannot initgroups(\"%s\", %d)"), + pwd.pw_name, pwd.pw_gid); + VIR_FREE(buf); + return -1; + } + VIR_FREE(buf); +# endif + if (setreuid(uid, uid) < 0) { + virReportSystemError(errno, + _("cannot change to uid to '%d'"), uid); + return -1; + } + } + return 0; +} + #else /* HAVE_GETPWUID_R */ char * @@ -2849,6 +2904,15 @@ int virGetGroupID(const char *name ATTRIBUTE_UNUSED, return 0; } + +int +virSetUIDGID(uid_t uid ATTRIBUTE_UNUSED, + gid_t gid ATTRIBUTE_UNUSED) +{ + virUtilError(VIR_ERR_INTERNAL_ERROR, + "%s", _("virSetUIDGID is not available")); + return -1; +} #endif /* HAVE_GETPWUID_R */ diff --git a/src/util/util.h b/src/util/util.h index ef4fc1349d..989962f33f 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -93,6 +93,8 @@ int virPipeReadUntilEOF(int outfd, int errfd, char **outbuf, char **errbuf); int virFork(pid_t *pid); +int virSetUIDGID(uid_t uid, gid_t gid); + int virFileReadLimFD(int fd, int maxlen, char **buf) ATTRIBUTE_RETURN_CHECK; int virFileReadAll(const char *path, int maxlen, char **buf) ATTRIBUTE_RETURN_CHECK;