UserLock: Make more RAII-ish
This commit is contained in:
		
							parent
							
								
									a529c740d2
								
							
						
					
					
						commit
						c0f2f4eeef
					
				
					 1 changed files with 38 additions and 53 deletions
				
			
		|  | @ -440,16 +440,14 @@ private: | |||
|     AutoCloseFD fdUserLock; | ||||
| 
 | ||||
|     string user; | ||||
|     uid_t uid = 0; | ||||
|     gid_t gid = 0; | ||||
|     uid_t uid; | ||||
|     gid_t gid; | ||||
|     std::vector<gid_t> supplementaryGIDs; | ||||
| 
 | ||||
| public: | ||||
|     UserLock(); | ||||
|     ~UserLock(); | ||||
| 
 | ||||
|     void acquire(); | ||||
|     void release(); | ||||
| 
 | ||||
|     void kill(); | ||||
| 
 | ||||
|     string getUser() { return user; } | ||||
|  | @ -465,16 +463,8 @@ public: | |||
| PathSet UserLock::lockedPaths; | ||||
| 
 | ||||
| 
 | ||||
| UserLock::~UserLock() | ||||
| UserLock::UserLock() | ||||
| { | ||||
|     release(); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void UserLock::acquire() | ||||
| { | ||||
|     assert(uid == 0); | ||||
| 
 | ||||
|     assert(settings.buildUsersGroup != ""); | ||||
| 
 | ||||
|     /* Get the members of the build-users-group. */ | ||||
|  | @ -551,20 +541,15 @@ void UserLock::acquire() | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| void UserLock::release() | ||||
| UserLock::~UserLock() | ||||
| { | ||||
|     if (uid == 0) return; | ||||
|     fdUserLock = -1; /* releases lock */ | ||||
|     assert(lockedPaths.find(fnUserLock) != lockedPaths.end()); | ||||
|     assert(lockedPaths.count(fnUserLock)); | ||||
|     lockedPaths.erase(fnUserLock); | ||||
|     fnUserLock = ""; | ||||
|     uid = 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void UserLock::kill() | ||||
| { | ||||
|     assert(enabled()); | ||||
|     killUser(uid); | ||||
| } | ||||
| 
 | ||||
|  | @ -720,7 +705,7 @@ private: | |||
|     PathSet missingPaths; | ||||
| 
 | ||||
|     /* User selected for running the builder. */ | ||||
|     UserLock buildUser; | ||||
|     std::unique_ptr<UserLock> buildUser; | ||||
| 
 | ||||
|     /* The process ID of the builder. */ | ||||
|     Pid pid; | ||||
|  | @ -966,7 +951,7 @@ void DerivationGoal::killChild() | |||
|     if (pid != -1) { | ||||
|         worker.childTerminated(this); | ||||
| 
 | ||||
|         if (buildUser.enabled()) { | ||||
|         if (buildUser) { | ||||
|             /* If we're using a build user, then there is a tricky
 | ||||
|                race condition: if we kill the build user before the | ||||
|                child has done its setuid() to the build user uid, then | ||||
|  | @ -974,7 +959,7 @@ void DerivationGoal::killChild() | |||
|                pid.wait().  So also send a conventional kill to the | ||||
|                child. */ | ||||
|             ::kill(-pid, SIGKILL); /* ignore the result */ | ||||
|             buildUser.kill(); | ||||
|             buildUser->kill(); | ||||
|             pid.wait(); | ||||
|         } else | ||||
|             pid.kill(); | ||||
|  | @ -1395,7 +1380,7 @@ void DerivationGoal::tryToBuild() | |||
|     } catch (BuildError & e) { | ||||
|         printError(e.msg()); | ||||
|         outputLocks.unlock(); | ||||
|         buildUser.release(); | ||||
|         buildUser.reset(); | ||||
|         worker.permanentFailure = true; | ||||
|         done(BuildResult::InputRejected, e.msg()); | ||||
|         return; | ||||
|  | @ -1429,6 +1414,11 @@ void DerivationGoal::buildDone() | |||
| { | ||||
|     trace("build done"); | ||||
| 
 | ||||
|     /* Release the build user at the end of this function. We don't do
 | ||||
|        it right away because we don't want another build grabbing this | ||||
|        uid and then messing around with our output. */ | ||||
|     Finally releaseBuildUser([&]() { buildUser.reset(); }); | ||||
| 
 | ||||
|     /* Since we got an EOF on the logger pipe, the builder is presumed
 | ||||
|        to have terminated.  In fact, the builder could also have | ||||
|        simply have closed its end of the pipe, so just to be sure, | ||||
|  | @ -1458,7 +1448,7 @@ void DerivationGoal::buildDone() | |||
|        malicious user from leaving behind a process that keeps files | ||||
|        open and modifies them after they have been chown'ed to | ||||
|        root. */ | ||||
|     if (buildUser.enabled()) buildUser.kill(); | ||||
|     if (buildUser) buildUser->kill(); | ||||
| 
 | ||||
|     bool diskFull = false; | ||||
| 
 | ||||
|  | @ -1528,7 +1518,6 @@ void DerivationGoal::buildDone() | |||
|         /* Repeat the build if necessary. */ | ||||
|         if (curRound++ < nrRounds) { | ||||
|             outputLocks.unlock(); | ||||
|             buildUser.release(); | ||||
|             state = &DerivationGoal::tryToBuild; | ||||
|             worker.wakeUp(shared_from_this()); | ||||
|             return; | ||||
|  | @ -1545,7 +1534,6 @@ void DerivationGoal::buildDone() | |||
|         if (!hook) | ||||
|             printError(e.msg()); | ||||
|         outputLocks.unlock(); | ||||
|         buildUser.release(); | ||||
| 
 | ||||
|         BuildResult::Status st = BuildResult::MiscFailure; | ||||
| 
 | ||||
|  | @ -1567,9 +1555,6 @@ void DerivationGoal::buildDone() | |||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     /* Release the build user, if applicable. */ | ||||
|     buildUser.release(); | ||||
| 
 | ||||
|     done(BuildResult::Built); | ||||
| } | ||||
| 
 | ||||
|  | @ -1714,11 +1699,11 @@ void DerivationGoal::startBuilder() | |||
|     /* If `build-users-group' is not empty, then we have to build as
 | ||||
|        one of the members of that group. */ | ||||
|     if (settings.buildUsersGroup != "" && getuid() == 0) { | ||||
|         buildUser.acquire(); | ||||
|         buildUser = std::make_unique<UserLock>(); | ||||
| 
 | ||||
|         /* Make sure that no other processes are executing under this
 | ||||
|            uid. */ | ||||
|         buildUser.kill(); | ||||
|         buildUser->kill(); | ||||
|     } | ||||
| 
 | ||||
|     /* Create a temporary directory where the build will take
 | ||||
|  | @ -1831,7 +1816,7 @@ void DerivationGoal::startBuilder() | |||
|         if (mkdir(chrootRootDir.c_str(), 0750) == -1) | ||||
|             throw SysError(format("cannot create ‘%1%’") % chrootRootDir); | ||||
| 
 | ||||
|         if (buildUser.enabled() && chown(chrootRootDir.c_str(), 0, buildUser.getGID()) == -1) | ||||
|         if (buildUser && chown(chrootRootDir.c_str(), 0, buildUser->getGID()) == -1) | ||||
|             throw SysError(format("cannot change ownership of ‘%1%’") % chrootRootDir); | ||||
| 
 | ||||
|         /* Create a writable /tmp in the chroot.  Many builders need
 | ||||
|  | @ -1875,7 +1860,7 @@ void DerivationGoal::startBuilder() | |||
|         createDirs(chrootStoreDir); | ||||
|         chmod_(chrootStoreDir, 01775); | ||||
| 
 | ||||
|         if (buildUser.enabled() && chown(chrootStoreDir.c_str(), 0, buildUser.getGID()) == -1) | ||||
|         if (buildUser && chown(chrootStoreDir.c_str(), 0, buildUser->getGID()) == -1) | ||||
|             throw SysError(format("cannot change ownership of ‘%1%’") % chrootStoreDir); | ||||
| 
 | ||||
|         for (auto & i : inputPaths) { | ||||
|  | @ -2085,8 +2070,8 @@ void DerivationGoal::startBuilder() | |||
|         /* Set the UID/GID mapping of the builder's user namespace
 | ||||
|            such that the sandbox user maps to the build user, or to | ||||
|            the calling user (if build users are disabled). */ | ||||
|         uid_t hostUid = buildUser.enabled() ? buildUser.getUID() : getuid(); | ||||
|         uid_t hostGid = buildUser.enabled() ? buildUser.getGID() : getgid(); | ||||
|         uid_t hostUid = buildUser ? buildUser->getUID() : getuid(); | ||||
|         uid_t hostGid = buildUser ? buildUser->getGID() : getgid(); | ||||
| 
 | ||||
|         writeFile("/proc/" + std::to_string(pid) + "/uid_map", | ||||
|             (format("%d %d 1") % sandboxUid % hostUid).str()); | ||||
|  | @ -2104,7 +2089,7 @@ void DerivationGoal::startBuilder() | |||
|     } else | ||||
| #endif | ||||
|     { | ||||
|         options.allowVfork = !buildUser.enabled() && !drv->isBuiltin(); | ||||
|         options.allowVfork = !buildUser && !drv->isBuiltin(); | ||||
|         pid = startProcess([&]() { | ||||
|             runChild(); | ||||
|         }, options); | ||||
|  | @ -2208,8 +2193,8 @@ void DerivationGoal::initEnv() | |||
| 
 | ||||
| void DerivationGoal::chownToBuilder(const Path & path) | ||||
| { | ||||
|     if (!buildUser.enabled()) return; | ||||
|     if (chown(path.c_str(), buildUser.getUID(), buildUser.getGID()) == -1) | ||||
|     if (!buildUser) return; | ||||
|     if (chown(path.c_str(), buildUser->getUID(), buildUser->getGID()) == -1) | ||||
|         throw SysError(format("cannot change ownership of ‘%1%’") % path); | ||||
| } | ||||
| 
 | ||||
|  | @ -2497,22 +2482,22 @@ void DerivationGoal::runChild() | |||
|            descriptors except std*, so that's safe.  Also note that | ||||
|            setuid() when run as root sets the real, effective and | ||||
|            saved UIDs. */ | ||||
|         if (setUser && buildUser.enabled()) { | ||||
|         if (setUser && buildUser) { | ||||
|             /* Preserve supplementary groups of the build user, to allow
 | ||||
|                admins to specify groups such as "kvm".  */ | ||||
|             if (!buildUser.getSupplementaryGIDs().empty() && | ||||
|                 setgroups(buildUser.getSupplementaryGIDs().size(), | ||||
|                           buildUser.getSupplementaryGIDs().data()) == -1) | ||||
|             if (!buildUser->getSupplementaryGIDs().empty() && | ||||
|                 setgroups(buildUser->getSupplementaryGIDs().size(), | ||||
|                           buildUser->getSupplementaryGIDs().data()) == -1) | ||||
|                 throw SysError("cannot set supplementary groups of build user"); | ||||
| 
 | ||||
|             if (setgid(buildUser.getGID()) == -1 || | ||||
|                 getgid() != buildUser.getGID() || | ||||
|                 getegid() != buildUser.getGID()) | ||||
|             if (setgid(buildUser->getGID()) == -1 || | ||||
|                 getgid() != buildUser->getGID() || | ||||
|                 getegid() != buildUser->getGID()) | ||||
|                 throw SysError("setgid failed"); | ||||
| 
 | ||||
|             if (setuid(buildUser.getUID()) == -1 || | ||||
|                 getuid() != buildUser.getUID() || | ||||
|                 geteuid() != buildUser.getUID()) | ||||
|             if (setuid(buildUser->getUID()) == -1 || | ||||
|                 getuid() != buildUser->getUID() || | ||||
|                 geteuid() != buildUser->getUID()) | ||||
|                 throw SysError("setuid failed"); | ||||
|         } | ||||
| 
 | ||||
|  | @ -2752,7 +2737,7 @@ void DerivationGoal::registerOutputs() | |||
|            build.  Also, the output should be owned by the build | ||||
|            user. */ | ||||
|         if ((!S_ISLNK(st.st_mode) && (st.st_mode & (S_IWGRP | S_IWOTH))) || | ||||
|             (buildUser.enabled() && st.st_uid != buildUser.getUID())) | ||||
|             (buildUser && st.st_uid != buildUser->getUID())) | ||||
|             throw BuildError(format("suspicious ownership or permission on ‘%1%’; rejecting this build output") % path); | ||||
| #endif | ||||
| 
 | ||||
|  | @ -2764,7 +2749,7 @@ void DerivationGoal::registerOutputs() | |||
|             /* Canonicalise first.  This ensures that the path we're
 | ||||
|                rewriting doesn't contain a hard link to /etc/shadow or | ||||
|                something like that. */ | ||||
|             canonicalisePathMetaData(actualPath, buildUser.enabled() ? buildUser.getUID() : -1, inodesSeen); | ||||
|             canonicalisePathMetaData(actualPath, buildUser ? buildUser->getUID() : -1, inodesSeen); | ||||
| 
 | ||||
|             /* FIXME: this is in-memory. */ | ||||
|             StringSink sink; | ||||
|  | @ -2822,7 +2807,7 @@ void DerivationGoal::registerOutputs() | |||
|         /* Get rid of all weird permissions.  This also checks that
 | ||||
|            all files are owned by the build user, if applicable. */ | ||||
|         canonicalisePathMetaData(actualPath, | ||||
|             buildUser.enabled() && !rewritten ? buildUser.getUID() : -1, inodesSeen); | ||||
|             buildUser && !rewritten ? buildUser->getUID() : -1, inodesSeen); | ||||
| 
 | ||||
|         /* For this output path, find the references to other paths
 | ||||
|            contained in it.  Compute the SHA-256 NAR hash at the same | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue