* nix-setuid-helper: allow running programs under a different uid.
This commit is contained in:
		
							parent
							
								
									9f0efa6611
								
							
						
					
					
						commit
						751f6d2157
					
				
					 3 changed files with 140 additions and 11 deletions
				
			
		
							
								
								
									
										22
									
								
								src/libmain/setuid-common.hh
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/libmain/setuid-common.hh
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | ||||||
|  | /* Code shared between libmain and nix-setuid-helper. */ | ||||||
|  | 
 | ||||||
|  | extern char * * environ; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | namespace nix { | ||||||
|  |      | ||||||
|  | 
 | ||||||
|  | void setuidCleanup() | ||||||
|  | { | ||||||
|  |     /* Don't trust the environment. */ | ||||||
|  |     environ = 0; | ||||||
|  | 
 | ||||||
|  |     /* Make sure that file descriptors 0, 1, 2 are open. */ | ||||||
|  |     for (int fd = 0; fd <= 2; ++fd) { | ||||||
|  |         struct stat st; | ||||||
|  |         if (fstat(fd, &st) == -1) abort(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  |   | ||||||
|  | } | ||||||
|  | @ -14,7 +14,7 @@ | ||||||
| #include <aterm2.h> | #include <aterm2.h> | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| extern char * * environ; | #include "setuid-common.hh" | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| namespace nix { | namespace nix { | ||||||
|  | @ -218,20 +218,11 @@ static void setuidInit() | ||||||
|     uid_t nixUid = geteuid(); |     uid_t nixUid = geteuid(); | ||||||
|     gid_t nixGid = getegid(); |     gid_t nixGid = getegid(); | ||||||
|      |      | ||||||
|     fprintf(stderr, "<<< setuid mode >>>\n"); |     setuidCleanup(); | ||||||
| 
 |  | ||||||
|     /* Don't trust the environment. */ |  | ||||||
|     environ = 0; |  | ||||||
| 
 | 
 | ||||||
|     /* Don't trust the current directory. */ |     /* Don't trust the current directory. */ | ||||||
|     if (chdir("/") == -1) abort(); |     if (chdir("/") == -1) abort(); | ||||||
| 
 | 
 | ||||||
|     /* Make sure that file descriptors 0, 1, 2 are open. */ |  | ||||||
|     for (int fd = 0; fd <= 2; ++fd) { |  | ||||||
|         struct stat st; |  | ||||||
|         if (fstat(fd, &st) == -1) abort(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /* Set the real (and preferably also the save) uid/gid to the
 |     /* Set the real (and preferably also the save) uid/gid to the
 | ||||||
|        effective uid/gid.  This matters mostly when we're not using |        effective uid/gid.  This matters mostly when we're not using | ||||||
|        build-users (bad!), since some builders (like Perl) complain |        build-users (bad!), since some builders (like Perl) complain | ||||||
|  |  | ||||||
|  | @ -1,3 +1,119 @@ | ||||||
|  | #include <sys/types.h> | ||||||
|  | #include <sys/stat.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | 
 | ||||||
|  | #include <pwd.h> | ||||||
|  | #include <grp.h> | ||||||
|  | 
 | ||||||
|  | #include <iostream> | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | #include "util.hh" | ||||||
|  | 
 | ||||||
|  | #include "../libmain/setuid-common.hh" | ||||||
|  | 
 | ||||||
|  | using namespace nix; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static void secureChown(uid_t uidTarget, gid_t gidTarget, | ||||||
|  |     const Path & path) | ||||||
|  | { | ||||||
|  |     /* Recursively chown `path' to the specified uid and gid, but only
 | ||||||
|  |        if it is currently owned by the Nix account. */ | ||||||
|  |     /* !!! */ | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static void runBuilder(string userName, | ||||||
|  |     string program, int argc, char * * argv) | ||||||
|  | { | ||||||
|  |     struct passwd * pw = getpwnam(userName.c_str()); | ||||||
|  |     if (!pw) | ||||||
|  |         throw Error(format("the user `%1%' does not exist") % userName); | ||||||
|  | 
 | ||||||
|  |     gid_t gidBuilders = 1234; | ||||||
|  |      | ||||||
|  |     /* Chown the current directory, *if* it is owned by the Nix
 | ||||||
|  |        account.  The idea is that the current directory is the | ||||||
|  |        temporary build directory in /tmp or somewhere else, and we | ||||||
|  |        don't want to create that directory here. */ | ||||||
|  |     secureChown(pw->pw_uid, gidBuilders, "."); | ||||||
|  | 
 | ||||||
|  |     /* Set the real, effective and saved gid.  Must be done before
 | ||||||
|  |        setuid(), otherwise it won't set the real and saved gids. */ | ||||||
|  |     //setgid(gidBuilders);
 | ||||||
|  | 
 | ||||||
|  |     /* Set the real, effective and saved uid. */ | ||||||
|  |     setuid(pw->pw_uid); | ||||||
|  |     if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) | ||||||
|  |         throw Error("cannot setuid"); | ||||||
|  | 
 | ||||||
|  |     /* Execute the program. */ | ||||||
|  |     std::vector<const char *> args; | ||||||
|  |     args.push_back(program.c_str()); | ||||||
|  |     for (int i = 0; i < argc; ++i) | ||||||
|  |         args.push_back(argv[i]); | ||||||
|  |     args.push_back(0); | ||||||
|  |      | ||||||
|  |     if (execve(program.c_str(), (char * *) &args[0], 0) == -1) | ||||||
|  |         throw SysError(format("cannot execute `%1%'") % program); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static void run(int argc, char * * argv)  | ||||||
|  | { | ||||||
|  |     char * * oldEnviron = environ; | ||||||
|  |      | ||||||
|  |     setuidCleanup(); | ||||||
|  | 
 | ||||||
|  |     if (geteuid() != 0) | ||||||
|  |         throw Error("nix-setuid-wrapper must be setuid root"); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     /* Read the configuration file.  It should consist of two words:
 | ||||||
|  |         | ||||||
|  |        <nix-user-name> <nix-builders-group> | ||||||
|  | 
 | ||||||
|  |        The first is the privileged account under which the main Nix | ||||||
|  |        processes run (i.e., the supposed caller).  It should match our | ||||||
|  |        real uid.  The second is the Unix group to which the Nix | ||||||
|  |        builders belong (and nothing else!). */ | ||||||
|  |     /* !!! */ | ||||||
|  |      | ||||||
|  |      | ||||||
|  |     /* Make sure that we are called by the Nix account, not by someone
 | ||||||
|  |        else. */ | ||||||
|  |     // ...
 | ||||||
|  | 
 | ||||||
|  |     /* Perform the desired command. */ | ||||||
|  |     if (argc < 2) | ||||||
|  |         throw Error("invalid arguments"); | ||||||
|  | 
 | ||||||
|  |     string command(argv[1]); | ||||||
|  | 
 | ||||||
|  |     if (command == "run-builder") { | ||||||
|  |         /* Syntax: nix-setuid-helper run-builder <username> <program>
 | ||||||
|  |              <args...> */ | ||||||
|  |         if (argc < 4) throw Error("missing user name / program name"); | ||||||
|  |         runBuilder(argv[2], argv[3], argc - 4, argv + 4); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     else if (command == "fix-ownership") { | ||||||
|  |         /* Syntax: nix-setuid-helper <fix-ownership> <path> */ | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     else throw Error ("invalid command"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| int main(int argc, char * * argv) | int main(int argc, char * * argv) | ||||||
| { | { | ||||||
|  |     try { | ||||||
|  |         run(argc, argv); | ||||||
|  |     } catch (Error & e) { | ||||||
|  |         std::cerr << e.msg() << std::endl; | ||||||
|  |         return 1; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue