From 43c4d18c6a4e1a8b129114439718e26c12b49ca8 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 21 Feb 2007 15:45:32 +0000 Subject: [PATCH] * `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.) --- src/libstore/local-store.cc | 114 +++++++++++++++++++++++++++++++++-- src/libstore/local-store.hh | 2 + src/libstore/remote-store.cc | 6 ++ src/libstore/remote-store.hh | 2 + src/libstore/store-api.hh | 4 ++ src/libutil/util.cc | 10 +-- src/libutil/util.hh | 2 +- src/nix-store/nix-store.cc | 12 ++++ 8 files changed, 142 insertions(+), 10 deletions(-) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 991f28e8da..a7c9d58117 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -6,6 +6,7 @@ #include "pathlocks.hh" #include "aterm.hh" #include "derivations-ast.hh" +#include "worker-protocol.hh" #include "config.h" #include @@ -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(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; diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index cae9d5c536..76d14c3a69 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -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); diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index 2fb4dd9802..801df58ad9 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -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); diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh index 69fae2cd16..4c594b6066 100644 --- a/src/libstore/remote-store.hh +++ b/src/libstore/remote-store.hh @@ -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); diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index 6a1de616ca..1f2d60f11c 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -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 diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 7671c7c7e4..1576e1e8b0 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -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 diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 0ebf6f5a5f..3c4629957a 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -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); diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index 7b56de7e4d..61ae4cf4f9 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -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")