* nix-store --import': import an archive created by nix-store
				
					
				
			--export' into the Nix store, and optionally check the cryptographic signatures against /nix/etc/nix/signing-key.pub. (TODO: verify against a set of public keys.)
This commit is contained in:
		
							parent
							
								
									46e0919ced
								
							
						
					
					
						commit
						43c4d18c6a
					
				
					 8 changed files with 142 additions and 10 deletions
				
			
		|  | @ -6,6 +6,7 @@ | ||||||
| #include "pathlocks.hh" | #include "pathlocks.hh" | ||||||
| #include "aterm.hh" | #include "aterm.hh" | ||||||
| #include "derivations-ast.hh" | #include "derivations-ast.hh" | ||||||
|  | #include "worker-protocol.hh" | ||||||
| #include "config.h" | #include "config.h" | ||||||
|      |      | ||||||
| #include <iostream> | #include <iostream> | ||||||
|  | @ -743,8 +744,6 @@ void LocalStore::exportPath(const Path & path, bool sign, | ||||||
| 
 | 
 | ||||||
|         writeInt(1, hashAndWriteSink); |         writeInt(1, hashAndWriteSink); | ||||||
|          |          | ||||||
|         //printMsg(lvlError, format("HASH = %1%") % printHash(hash));
 |  | ||||||
| 
 |  | ||||||
|         Path tmpDir = createTempDir(); |         Path tmpDir = createTempDir(); | ||||||
|         AutoDelete delTmp(tmpDir); |         AutoDelete delTmp(tmpDir); | ||||||
|         Path hashFile = tmpDir + "/hash"; |         Path hashFile = tmpDir + "/hash"; | ||||||
|  | @ -759,8 +758,6 @@ void LocalStore::exportPath(const Path & path, bool sign, | ||||||
|         args.push_back(hashFile); |         args.push_back(hashFile); | ||||||
|         string signature = runProgram("openssl", true, args); |         string signature = runProgram("openssl", true, args); | ||||||
| 
 | 
 | ||||||
|         //printMsg(lvlError, format("SIGNATURE = %1%") % signature);
 |  | ||||||
| 
 |  | ||||||
|         writeString(signature, hashAndWriteSink); |         writeString(signature, hashAndWriteSink); | ||||||
|          |          | ||||||
|     } else |     } else | ||||||
|  | @ -768,6 +765,115 @@ void LocalStore::exportPath(const Path & path, bool sign, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | struct HashAndReadSource : Source | ||||||
|  | { | ||||||
|  |     Source & readSource; | ||||||
|  |     HashSink hashSink; | ||||||
|  |     bool hashing; | ||||||
|  |     HashAndReadSource(Source & readSource) : readSource(readSource), hashSink(htSHA256) | ||||||
|  |     { | ||||||
|  |         hashing = true; | ||||||
|  |     } | ||||||
|  |     virtual void operator () | ||||||
|  |         (unsigned char * data, unsigned int len) | ||||||
|  |     { | ||||||
|  |         readSource(data, len); | ||||||
|  |         if (hashing) hashSink(data, len); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | Path LocalStore::importPath(bool requireSignature, Source & source) | ||||||
|  | { | ||||||
|  |     HashAndReadSource hashAndReadSource(source); | ||||||
|  |      | ||||||
|  |     /* We don't yet know what store path this archive contains (the
 | ||||||
|  |        store path follows the archive data proper), and besides, we | ||||||
|  |        don't know yet whether the signature is valid. */ | ||||||
|  |     Path tmpDir = createTempDir(nixStore); | ||||||
|  |     AutoDelete delTmp(tmpDir); | ||||||
|  |     Path unpacked = tmpDir + "/unpacked"; | ||||||
|  | 
 | ||||||
|  |     restorePath(unpacked, hashAndReadSource); | ||||||
|  | 
 | ||||||
|  |     unsigned int magic = readInt(hashAndReadSource); | ||||||
|  |     if (magic != EXPORT_MAGIC) | ||||||
|  |         throw Error("Nix archive cannot be imported; wrong format"); | ||||||
|  | 
 | ||||||
|  |     Path dstPath = readStorePath(hashAndReadSource); | ||||||
|  | 
 | ||||||
|  |     PathSet references = readStorePaths(hashAndReadSource); | ||||||
|  | 
 | ||||||
|  |     Path deriver = readStorePath(hashAndReadSource); | ||||||
|  | 
 | ||||||
|  |     Hash hash = hashAndReadSource.hashSink.finish(); | ||||||
|  |     hashAndReadSource.hashing = false; | ||||||
|  | 
 | ||||||
|  |     bool haveSignature = readInt(hashAndReadSource) == 1; | ||||||
|  | 
 | ||||||
|  |     if (requireSignature && !haveSignature) | ||||||
|  |         throw Error("imported archive lacks a signature"); | ||||||
|  |      | ||||||
|  |     if (haveSignature) { | ||||||
|  |         string signature = readString(hashAndReadSource); | ||||||
|  | 
 | ||||||
|  |         Path sigFile = tmpDir + "/sig"; | ||||||
|  |         writeStringToFile(sigFile, signature); | ||||||
|  | 
 | ||||||
|  |         Strings args; | ||||||
|  |         args.push_back("rsautl"); | ||||||
|  |         args.push_back("-verify"); | ||||||
|  |         args.push_back("-inkey"); | ||||||
|  |         args.push_back(nixConfDir + "/signing-key.pub"); | ||||||
|  |         args.push_back("-pubin"); | ||||||
|  |         args.push_back("-in"); | ||||||
|  |         args.push_back(sigFile); | ||||||
|  |         string hash2 = runProgram("openssl", true, args); | ||||||
|  | 
 | ||||||
|  |         /* Note: runProgram() throws an exception if the signature is
 | ||||||
|  |            invalid. */ | ||||||
|  | 
 | ||||||
|  |         if (printHash(hash) != hash2) | ||||||
|  |             throw Error( | ||||||
|  |                 "signed hash doesn't match actual contents of imported " | ||||||
|  |                 "archive; archive could be corrupt, or someone is trying " | ||||||
|  |                 "to import a Trojan horse"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Do the actual import. */ | ||||||
|  | 
 | ||||||
|  |     /* !!! way too much code duplication with addTextToStore() etc. */ | ||||||
|  |     addTempRoot(dstPath); | ||||||
|  | 
 | ||||||
|  |     if (!isValidPath(dstPath)) { | ||||||
|  | 
 | ||||||
|  |         PathLocks outputLock(singleton<PathSet, Path>(dstPath)); | ||||||
|  | 
 | ||||||
|  |         if (!isValidPath(dstPath)) { | ||||||
|  | 
 | ||||||
|  |             if (pathExists(dstPath)) deletePathWrapped(dstPath); | ||||||
|  | 
 | ||||||
|  |             if (rename(unpacked.c_str(), dstPath.c_str()) == -1) | ||||||
|  |                 throw SysError(format("cannot move `%1%' to `%2%'") | ||||||
|  |                     % unpacked % dstPath); | ||||||
|  | 
 | ||||||
|  |             canonicalisePathMetaData(dstPath); | ||||||
|  |              | ||||||
|  |             Transaction txn(nixDB); | ||||||
|  |             /* !!! if we were clever, we could prevent the hashPath()
 | ||||||
|  |                here. */ | ||||||
|  |             registerValidPath(txn, dstPath, | ||||||
|  |                 hashPath(htSHA256, dstPath), references, ""); | ||||||
|  |             txn.commit(); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         outputLock.setDeletion(true); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     return dstPath; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| void deleteFromStore(const Path & _path, unsigned long long & bytesFreed) | void deleteFromStore(const Path & _path, unsigned long long & bytesFreed) | ||||||
| { | { | ||||||
|     bytesFreed = 0; |     bytesFreed = 0; | ||||||
|  |  | ||||||
|  | @ -59,6 +59,8 @@ public: | ||||||
|     void exportPath(const Path & path, bool sign, |     void exportPath(const Path & path, bool sign, | ||||||
|         Sink & sink); |         Sink & sink); | ||||||
| 
 | 
 | ||||||
|  |     Path importPath(bool requireSignature, Source & source); | ||||||
|  |      | ||||||
|     void buildDerivations(const PathSet & drvPaths); |     void buildDerivations(const PathSet & drvPaths); | ||||||
| 
 | 
 | ||||||
|     void ensurePath(const Path & path); |     void ensurePath(const Path & path); | ||||||
|  |  | ||||||
|  | @ -250,6 +250,12 @@ void RemoteStore::exportPath(const Path & path, bool sign, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | Path RemoteStore::importPath(bool requireSignature, Source & source) | ||||||
|  | { | ||||||
|  |     throw Error("not implemented"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| void RemoteStore::buildDerivations(const PathSet & drvPaths) | void RemoteStore::buildDerivations(const PathSet & drvPaths) | ||||||
| { | { | ||||||
|     writeInt(wopBuildDerivations, to); |     writeInt(wopBuildDerivations, to); | ||||||
|  |  | ||||||
|  | @ -47,6 +47,8 @@ public: | ||||||
|     void exportPath(const Path & path, bool sign, |     void exportPath(const Path & path, bool sign, | ||||||
|         Sink & sink); |         Sink & sink); | ||||||
| 
 | 
 | ||||||
|  |     Path importPath(bool requireSignature, Source & source); | ||||||
|  |      | ||||||
|     void buildDerivations(const PathSet & drvPaths); |     void buildDerivations(const PathSet & drvPaths); | ||||||
| 
 | 
 | ||||||
|     void ensurePath(const Path & path); |     void ensurePath(const Path & path); | ||||||
|  |  | ||||||
|  | @ -99,6 +99,10 @@ public: | ||||||
|     virtual void exportPath(const Path & path, bool sign, |     virtual void exportPath(const Path & path, bool sign, | ||||||
|         Sink & sink) = 0; |         Sink & sink) = 0; | ||||||
| 
 | 
 | ||||||
|  |     /* Import a NAR dump created by exportPath() into the Nix
 | ||||||
|  |        store. */ | ||||||
|  |     virtual Path importPath(bool requireSignature, Source & source) = 0; | ||||||
|  | 
 | ||||||
|     /* Ensure that the output paths of the derivation are valid.  If
 |     /* Ensure that the output paths of the derivation are valid.  If
 | ||||||
|        they are already valid, this is a no-op.  Otherwise, validity |        they are already valid, this is a no-op.  Otherwise, validity | ||||||
|        can be reached in two ways.  First, if the output paths have |        can be reached in two ways.  First, if the output paths have | ||||||
|  |  | ||||||
|  | @ -317,19 +317,19 @@ void makePathReadOnly(const Path & path) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| static Path tempName() | static Path tempName(const Path & tmpRoot) | ||||||
| { | { | ||||||
|     static int counter = 0; |     static int counter = 0; | ||||||
|     Path tmpRoot = canonPath(getEnv("TMPDIR", "/tmp"), true); |     Path tmpRoot2 = canonPath(tmpRoot.empty() ? getEnv("TMPDIR", "/tmp") : tmpRoot, true); | ||||||
|     return (format("%1%/nix-%2%-%3%") % tmpRoot % getpid() % counter++).str(); |     return (format("%1%/nix-%2%-%3%") % tmpRoot2 % getpid() % counter++).str(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Path createTempDir() | Path createTempDir(const Path & tmpRoot) | ||||||
| { | { | ||||||
|     while (1) { |     while (1) { | ||||||
|         checkInterrupt(); |         checkInterrupt(); | ||||||
| 	Path tmpDir = tempName(); | 	Path tmpDir = tempName(tmpRoot); | ||||||
| 	if (mkdir(tmpDir.c_str(), 0777) == 0) { | 	if (mkdir(tmpDir.c_str(), 0777) == 0) { | ||||||
| 	    /* Explicitly set the group of the directory.  This is to
 | 	    /* Explicitly set the group of the directory.  This is to
 | ||||||
| 	       work around around problems caused by BSD's group | 	       work around around problems caused by BSD's group | ||||||
|  |  | ||||||
|  | @ -70,7 +70,7 @@ void deletePath(const Path & path, unsigned long long & bytesFreed); | ||||||
| void makePathReadOnly(const Path & path); | void makePathReadOnly(const Path & path); | ||||||
| 
 | 
 | ||||||
| /* Create a temporary directory. */ | /* Create a temporary directory. */ | ||||||
| Path createTempDir(); | Path createTempDir(const Path & tmpRoot = ""); | ||||||
| 
 | 
 | ||||||
| /* Create a directory and all its parents, if necessary. */ | /* Create a directory and all its parents, if necessary. */ | ||||||
| void createDirs(const Path & path); | void createDirs(const Path & path); | ||||||
|  |  | ||||||
|  | @ -651,6 +651,16 @@ static void opExport(Strings opFlags, Strings opArgs) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | static void opImport(Strings opFlags, Strings opArgs) | ||||||
|  | { | ||||||
|  |     if (!opFlags.empty()) throw UsageError("unknown flag"); | ||||||
|  |     if (!opArgs.empty()) throw UsageError("no arguments expected"); | ||||||
|  |      | ||||||
|  |     FdSource source(STDIN_FILENO); | ||||||
|  |     cout << format("%1%\n") % store->importPath(false, source); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| /* Initialise the Nix databases. */ | /* Initialise the Nix databases. */ | ||||||
| static void opInit(Strings opFlags, Strings opArgs) | static void opInit(Strings opFlags, Strings opArgs) | ||||||
| { | { | ||||||
|  | @ -722,6 +732,8 @@ void run(Strings args) | ||||||
|             op = opRestore; |             op = opRestore; | ||||||
|         else if (arg == "--export") |         else if (arg == "--export") | ||||||
|             op = opExport; |             op = opExport; | ||||||
|  |         else if (arg == "--import") | ||||||
|  |             op = opImport; | ||||||
|         else if (arg == "--init") |         else if (arg == "--init") | ||||||
|             op = opInit; |             op = opInit; | ||||||
|         else if (arg == "--verify") |         else if (arg == "--verify") | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue