* When NIX_REMOTE is set to "slave", fork off nix-worker in slave

mode.  Presumably nix-worker would be setuid to the Nix store user.
  The worker performs all operations on the Nix store and database, so
  the caller can be completely unprivileged.

  This is already much more secure than the old setuid scheme, since
  the worker doesn't need to do Nix expression evaluation and so on.
  Most importantly, this means that it doesn't need to access any user
  files, with all resulting security risks; it only performs pure
  store operations.

  Once this works, it is easy to move to a daemon model that forks off
  a worker for connections established through a Unix domain socket.
  That would be even more secure.
This commit is contained in:
Eelco Dolstra 2006-11-30 19:54:43 +00:00
parent 40b3f64b55
commit 765bdfe542
5 changed files with 87 additions and 24 deletions

View file

@ -1,12 +1,64 @@
#include "serialise.hh"
#include "util.hh"
#include "remote-store.hh" #include "remote-store.hh"
#include <iostream>
#include <unistd.h>
namespace nix { namespace nix {
RemoteStore::RemoteStore() RemoteStore::RemoteStore()
{ {
throw Error("not implemented"); toChild.create();
fromChild.create();
/* Start the worker. */
string worker = "nix-worker";
child = fork();
switch (child) {
case -1:
throw SysError("unable to fork");
case 0:
try { /* child */
fromChild.readSide.close();
if (dup2(fromChild.writeSide, STDOUT_FILENO) == -1)
throw SysError("dupping write side");
toChild.writeSide.close();
if (dup2(toChild.readSide, STDIN_FILENO) == -1)
throw SysError("dupping read side");
execlp(worker.c_str(), worker.c_str(),
"-vvv", "--slave", NULL);
throw SysError(format("executing `%1%'") % worker);
} catch (std::exception & e) {
std::cerr << format("child error: %1%\n") % e.what();
}
quickExit(1);
}
fromChild.writeSide.close();
toChild.readSide.close();
from.fd = fromChild.readSide;
to.fd = toChild.writeSide;
/* Send the magic greeting, check for the reply. */
writeInt(0x6e697864, to);
unsigned int magic = readInt(from);
if (magic != 0x6478696e) throw Error("protocol mismatch");
} }

View file

@ -9,6 +9,12 @@
namespace nix { namespace nix {
class Pipe;
class Pid;
struct FdSink;
struct FdSource;
class RemoteStore : public StoreAPI class RemoteStore : public StoreAPI
{ {
public: public:
@ -44,6 +50,11 @@ public:
void ensurePath(const Path & storePath); void ensurePath(const Path & storePath);
private: private:
Pipe toChild;
Pipe fromChild;
FdSink to;
FdSource from;
Pid child;
}; };

View file

@ -91,6 +91,7 @@ Path makeFixedOutputPath(bool recursive,
#include "local-store.hh" #include "local-store.hh"
#include "serialise.hh"
#include "remote-store.hh" #include "remote-store.hh"

View file

@ -33,6 +33,11 @@ struct FdSink : Sink
{ {
int fd; int fd;
FdSink()
{
fd = 0;
}
FdSink(int fd) FdSink(int fd)
{ {
this->fd = fd; this->fd = fd;
@ -47,6 +52,11 @@ struct FdSource : Source
{ {
int fd; int fd;
FdSource()
{
fd = 0;
}
FdSource(int fd) FdSource(int fd)
{ {
this->fd = fd; this->fd = fd;

View file

@ -1,35 +1,21 @@
#include "shared.hh" #include "shared.hh"
#include "local-store.hh" #include "local-store.hh"
#include "util.hh" #include "util.hh"
#include "serialise.hh"
using namespace nix; using namespace nix;
/* !!! Mostly cut&pasted from util/archive.hh */ void processConnection(Source & from, Sink & to)
/* Use buffered reads. */
static unsigned int readInt(int fd)
{ {
unsigned char buf[8]; store = boost::shared_ptr<StoreAPI>(new LocalStore(true));
readFull(fd, buf, sizeof(buf));
if (buf[4] || buf[5] || buf[6] || buf[7])
throw Error("implementation cannot deal with > 32-bit integers");
return
buf[0] |
(buf[1] << 8) |
(buf[2] << 16) |
(buf[3] << 24);
}
unsigned int magic = readInt(from);
void processConnection(int fdFrom, int fdTo)
{
store = openStore();
unsigned int magic = readInt(fdFrom);
if (magic != 0x6e697864) throw Error("protocol mismatch"); if (magic != 0x6e697864) throw Error("protocol mismatch");
writeInt(0x6478696e, to);
debug("greeting exchanged");
} }
@ -43,8 +29,11 @@ void run(Strings args)
if (arg == "--slave") slave = true; if (arg == "--slave") slave = true;
} }
if (slave) if (slave) {
processConnection(STDIN_FILENO, STDOUT_FILENO); FdSource source(STDIN_FILENO);
FdSink sink(STDOUT_FILENO);
processConnection(source, sink);
}
else if (daemon) else if (daemon)
throw Error("daemon mode not implemented"); throw Error("daemon mode not implemented");