* 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 "aterm.hh" | ||||
| #include "derivations-ast.hh" | ||||
| #include "worker-protocol.hh" | ||||
| #include "config.h" | ||||
|      | ||||
| #include <iostream> | ||||
|  | @ -743,8 +744,6 @@ void LocalStore::exportPath(const Path & path, bool sign, | |||
| 
 | ||||
|         writeInt(1, hashAndWriteSink); | ||||
|          | ||||
|         //printMsg(lvlError, format("HASH = %1%") % printHash(hash));
 | ||||
| 
 | ||||
|         Path tmpDir = createTempDir(); | ||||
|         AutoDelete delTmp(tmpDir); | ||||
|         Path hashFile = tmpDir + "/hash"; | ||||
|  | @ -759,8 +758,6 @@ void LocalStore::exportPath(const Path & path, bool sign, | |||
|         args.push_back(hashFile); | ||||
|         string signature = runProgram("openssl", true, args); | ||||
| 
 | ||||
|         //printMsg(lvlError, format("SIGNATURE = %1%") % signature);
 | ||||
| 
 | ||||
|         writeString(signature, hashAndWriteSink); | ||||
|          | ||||
|     } 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) | ||||
| { | ||||
|     bytesFreed = 0; | ||||
|  |  | |||
|  | @ -59,6 +59,8 @@ public: | |||
|     void exportPath(const Path & path, bool sign, | ||||
|         Sink & sink); | ||||
| 
 | ||||
|     Path importPath(bool requireSignature, Source & source); | ||||
|      | ||||
|     void buildDerivations(const PathSet & drvPaths); | ||||
| 
 | ||||
|     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) | ||||
| { | ||||
|     writeInt(wopBuildDerivations, to); | ||||
|  |  | |||
|  | @ -47,6 +47,8 @@ public: | |||
|     void exportPath(const Path & path, bool sign, | ||||
|         Sink & sink); | ||||
| 
 | ||||
|     Path importPath(bool requireSignature, Source & source); | ||||
|      | ||||
|     void buildDerivations(const PathSet & drvPaths); | ||||
| 
 | ||||
|     void ensurePath(const Path & path); | ||||
|  |  | |||
|  | @ -99,6 +99,10 @@ public: | |||
|     virtual void exportPath(const Path & path, bool sign, | ||||
|         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
 | ||||
|        they are already valid, this is a no-op.  Otherwise, validity | ||||
|        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; | ||||
|     Path tmpRoot = canonPath(getEnv("TMPDIR", "/tmp"), true); | ||||
|     return (format("%1%/nix-%2%-%3%") % tmpRoot % getpid() % counter++).str(); | ||||
|     Path tmpRoot2 = canonPath(tmpRoot.empty() ? getEnv("TMPDIR", "/tmp") : tmpRoot, true); | ||||
|     return (format("%1%/nix-%2%-%3%") % tmpRoot2 % getpid() % counter++).str(); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| Path createTempDir() | ||||
| Path createTempDir(const Path & tmpRoot) | ||||
| { | ||||
|     while (1) { | ||||
|         checkInterrupt(); | ||||
| 	Path tmpDir = tempName(); | ||||
| 	Path tmpDir = tempName(tmpRoot); | ||||
| 	if (mkdir(tmpDir.c_str(), 0777) == 0) { | ||||
| 	    /* Explicitly set the group of the directory.  This is to
 | ||||
| 	       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); | ||||
| 
 | ||||
| /* Create a temporary directory. */ | ||||
| Path createTempDir(); | ||||
| Path createTempDir(const Path & tmpRoot = ""); | ||||
| 
 | ||||
| /* Create a directory and all its parents, if necessary. */ | ||||
| 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. */ | ||||
| static void opInit(Strings opFlags, Strings opArgs) | ||||
| { | ||||
|  | @ -722,6 +732,8 @@ void run(Strings args) | |||
|             op = opRestore; | ||||
|         else if (arg == "--export") | ||||
|             op = opExport; | ||||
|         else if (arg == "--import") | ||||
|             op = opImport; | ||||
|         else if (arg == "--init") | ||||
|             op = opInit; | ||||
|         else if (arg == "--verify") | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue