* Move killUser() to libutil so that the setuid helper can use it.
This commit is contained in:
		
							parent
							
								
									79875c5e42
								
							
						
					
					
						commit
						6a8e60913a
					
				
					 3 changed files with 82 additions and 51 deletions
				
			
		|  | @ -352,6 +352,8 @@ public: | ||||||
| 
 | 
 | ||||||
|     uid_t getUID(); |     uid_t getUID(); | ||||||
|     uid_t getGID(); |     uid_t getGID(); | ||||||
|  | 
 | ||||||
|  |     bool enabled(); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -452,54 +454,27 @@ uid_t UserLock::getGID() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| static void killUser(uid_t uid) | bool UserLock::enabled() | ||||||
| { | { | ||||||
|     debug(format("killing all processes running under uid `%1%'") % uid); |     return uid != 0; | ||||||
|      |  | ||||||
|     assert(uid != rootUserId); /* just to be safe... */ |  | ||||||
| 
 |  | ||||||
|     /* The system call kill(-1, sig) sends the signal `sig' to all
 |  | ||||||
|        users to which the current process can send signals.  So we |  | ||||||
|        fork a process, switch to uid, and send a mass kill. */ |  | ||||||
| 
 |  | ||||||
|     Pid pid; |  | ||||||
|     pid = fork(); |  | ||||||
|     switch (pid) { |  | ||||||
| 
 |  | ||||||
|     case -1: |  | ||||||
|         throw SysError("unable to fork"); |  | ||||||
| 
 |  | ||||||
|     case 0: |  | ||||||
|         try { /* child */ |  | ||||||
| 
 |  | ||||||
|             if (setuid(uid) == -1) abort(); |  | ||||||
| 
 |  | ||||||
| 	    while (true) { |  | ||||||
| 		if (kill(-1, SIGKILL) == 0) break; |  | ||||||
| 		if (errno == ESRCH) break; /* no more processes */ |  | ||||||
| 		if (errno != EINTR) |  | ||||||
| 		    throw SysError(format("cannot kill processes for uid `%1%'") % uid); |  | ||||||
| 	    } |  | ||||||
|          |  | ||||||
|         } catch (std::exception & e) { |  | ||||||
|             std::cerr << format("killing processes beloging to uid `%1%': %1%\n") |  | ||||||
|                 % uid % e.what(); |  | ||||||
|             quickExit(1); |  | ||||||
|         } |  | ||||||
|         quickExit(0); |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     /* parent */ |  | ||||||
|     if (pid.wait(true) != 0) |  | ||||||
|         throw Error(format("cannot kill processes for uid `%1%'") % uid); |  | ||||||
| 
 |  | ||||||
|     /* !!! We should really do some check to make sure that there are
 |  | ||||||
|        no processes left running under `uid', but there is no portable |  | ||||||
|        way to do so (I think).  The most reliable way may be `ps -eo |  | ||||||
|        uid | grep -q $uid'. */ |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | static bool amPrivileged() | ||||||
|  | { | ||||||
|  |     return geteuid() == 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void killUserWrapped(uid_t uid) | ||||||
|  | { | ||||||
|  |     if (amPrivileged()) | ||||||
|  |         killUser(uid); | ||||||
|  |     else | ||||||
|  |         /* !!! TODO */ | ||||||
|  |         printMsg(lvlError, "must kill"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| //////////////////////////////////////////////////////////////////////
 | //////////////////////////////////////////////////////////////////////
 | ||||||
| 
 | 
 | ||||||
|  | @ -825,8 +800,8 @@ void DerivationGoal::buildDone() | ||||||
|        malicious user from leaving behind a process that keeps files |        malicious user from leaving behind a process that keeps files | ||||||
|        open and modifies them after they have been chown'ed to |        open and modifies them after they have been chown'ed to | ||||||
|        root. */ |        root. */ | ||||||
|     if (buildUser.getUID() != 0) |     if (buildUser.enabled()) | ||||||
|         killUser(buildUser.getUID()); |         killUserWrapped(buildUser.getUID()); | ||||||
| 
 | 
 | ||||||
|     /* Close the read side of the logger pipe. */ |     /* Close the read side of the logger pipe. */ | ||||||
|     logPipe.readSide.close(); |     logPipe.readSide.close(); | ||||||
|  | @ -1308,11 +1283,15 @@ void DerivationGoal::startBuilder() | ||||||
| 
 | 
 | ||||||
|         /* Make sure that no other processes are executing under this
 |         /* Make sure that no other processes are executing under this
 | ||||||
|            uid. */ |            uid. */ | ||||||
|         killUser(buildUser.getUID()); |         killUserWrapped(buildUser.getUID()); | ||||||
|          |          | ||||||
|         /* Change ownership of the temporary build directory.  !!! gid */ |         /* Change ownership of the temporary build directory, if we're
 | ||||||
|         if (chown(tmpDir.c_str(), buildUser.getUID(), (gid_t) -1) == -1) |            root.  If we're not root, then the setuid helper will do it | ||||||
|             throw SysError(format("cannot change ownership of `%1%'") % tmpDir); |            just before it starts the builder. */ | ||||||
|  |         if (amPrivileged()) { | ||||||
|  |             if (chown(tmpDir.c_str(), buildUser.getUID(), buildUser.getGID()) == -1) | ||||||
|  |                 throw SysError(format("cannot change ownership of `%1%'") % tmpDir); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         /* Check that the Nix store has the appropriate permissions,
 |         /* Check that the Nix store has the appropriate permissions,
 | ||||||
|            i.e., owned by root and mode 1775 (sticky bit on so that |            i.e., owned by root and mode 1775 (sticky bit on so that | ||||||
|  | @ -1325,7 +1304,7 @@ void DerivationGoal::startBuilder() | ||||||
|             ((st.st_mode & S_IRWXG) != S_IRWXG) || |             ((st.st_mode & S_IRWXG) != S_IRWXG) || | ||||||
|             (st.st_gid != buildUser.getGID())) |             (st.st_gid != buildUser.getGID())) | ||||||
|             throw Error(format( |             throw Error(format( | ||||||
|                 "builder does not have write permission to `%1%'; " |                 "builder does not have write permission to `%2%'; " | ||||||
|                 "try `chgrp %1% %2%; chmod 1775 %2%'") |                 "try `chgrp %1% %2%; chmod 1775 %2%'") | ||||||
|                 % buildUser.getGID() % nixStore); |                 % buildUser.getGID() % nixStore); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -707,6 +707,53 @@ void Pid::setSeparatePG(bool separatePG) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | void killUser(uid_t uid) | ||||||
|  | { | ||||||
|  |     debug(format("killing all processes running under uid `%1%'") % uid); | ||||||
|  | 
 | ||||||
|  |     assert(uid != 0); /* just to be safe... */ | ||||||
|  | 
 | ||||||
|  |     /* The system call kill(-1, sig) sends the signal `sig' to all
 | ||||||
|  |        users to which the current process can send signals.  So we | ||||||
|  |        fork a process, switch to uid, and send a mass kill. */ | ||||||
|  | 
 | ||||||
|  |     Pid pid; | ||||||
|  |     pid = fork(); | ||||||
|  |     switch (pid) { | ||||||
|  | 
 | ||||||
|  |     case -1: | ||||||
|  |         throw SysError("unable to fork"); | ||||||
|  | 
 | ||||||
|  |     case 0: | ||||||
|  |         try { /* child */ | ||||||
|  | 
 | ||||||
|  |             if (setuid(uid) == -1) abort(); | ||||||
|  | 
 | ||||||
|  | 	    while (true) { | ||||||
|  | 		if (kill(-1, SIGKILL) == 0) break; | ||||||
|  | 		if (errno == ESRCH) break; /* no more processes */ | ||||||
|  | 		if (errno != EINTR) | ||||||
|  | 		    throw SysError(format("cannot kill processes for uid `%1%'") % uid); | ||||||
|  | 	    } | ||||||
|  |          | ||||||
|  |         } catch (std::exception & e) { | ||||||
|  |             std::cerr << format("killing processes beloging to uid `%1%': %1%\n") | ||||||
|  |                 % uid % e.what(); | ||||||
|  |             quickExit(1); | ||||||
|  |         } | ||||||
|  |         quickExit(0); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     /* parent */ | ||||||
|  |     if (pid.wait(true) != 0) | ||||||
|  |         throw Error(format("cannot kill processes for uid `%1%'") % uid); | ||||||
|  | 
 | ||||||
|  |     /* !!! We should really do some check to make sure that there are
 | ||||||
|  |        no processes left running under `uid', but there is no portable | ||||||
|  |        way to do so (I think).  The most reliable way may be `ps -eo | ||||||
|  |        uid | grep -q $uid'. */ | ||||||
|  | } | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| //////////////////////////////////////////////////////////////////////
 | //////////////////////////////////////////////////////////////////////
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -224,6 +224,11 @@ public: | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | /* Kill all processes running under the specified uid by sending them
 | ||||||
|  |    a SIGKILL. */ | ||||||
|  | void killUser(uid_t uid); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| /* Run a program and return its stdout in a string (i.e., like the
 | /* Run a program and return its stdout in a string (i.e., like the
 | ||||||
|    shell backtick operator). */ |    shell backtick operator). */ | ||||||
| string runProgram(Path program); | string runProgram(Path program); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue