From bd013b6f987c23c3b99b639ba7cdbc7b694a13f5 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 15 Feb 2012 01:31:56 +0100 Subject: On Linux, make the Nix store really read-only by using the immutable bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I was bitten one time too many by Python modifying the Nix store by creating *.pyc files when run as root. On Linux, we can prevent this by setting the immutable bit on files and directories (as in ‘chattr +i’). This isn't supported by all filesystems, so it's not an error if setting the bit fails. The immutable bit is cleared by the garbage collector before deleting a path. The only tricky aspect is in optimiseStore(), since it's forbidden to create hard links to an immutable file. Thus optimiseStore() temporarily clears the immutable bit before creating the link. --- src/libutil/Makefile.am | 4 +-- src/libutil/immutable.cc | 67 ++++++++++++++++++++++++++++++++++++++++++++++++ src/libutil/immutable.hh | 19 ++++++++++++++ src/libutil/util.cc | 3 +++ 4 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 src/libutil/immutable.cc create mode 100644 src/libutil/immutable.hh (limited to 'src/libutil') diff --git a/src/libutil/Makefile.am b/src/libutil/Makefile.am index 98f32633b894..a326060cfea9 100644 --- a/src/libutil/Makefile.am +++ b/src/libutil/Makefile.am @@ -1,12 +1,12 @@ pkglib_LTLIBRARIES = libutil.la libutil_la_SOURCES = util.cc hash.cc serialise.cc \ - archive.cc xml-writer.cc + archive.cc xml-writer.cc immutable.cc libutil_la_LIBADD = ../boost/format/libformat.la ${aterm_lib} ${sqlite_lib} pkginclude_HEADERS = util.hh hash.hh serialise.hh \ - archive.hh xml-writer.hh types.hh + archive.hh xml-writer.hh types.hh immutable.hh if !HAVE_OPENSSL libutil_la_SOURCES += \ diff --git a/src/libutil/immutable.cc b/src/libutil/immutable.cc new file mode 100644 index 000000000000..f72f85625486 --- /dev/null +++ b/src/libutil/immutable.cc @@ -0,0 +1,67 @@ +#include "config.h" + +#include "immutable.hh" +#include "util.hh" + +#include +#include +#include + +#if HAVE_LINUX_FS_H +#include +#include +#include +#endif + +namespace nix { + + +void changeMutable(const Path & path, bool mut) +{ +#if defined(FS_IOC_SETFLAGS) && defined(FS_IOC_GETFLAGS) && defined(FS_IMMUTABLE_FL) + + /* Don't even try if we're not root. One day we should support + the CAP_LINUX_IMMUTABLE capability. */ + if (getuid() != 0) return; + + /* The O_NOFOLLOW is important to prevent us from changing the + mutable bit on the target of a symlink (which would be a + security hole). */ + AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW); + if (fd == -1) { + if (errno == ELOOP) return; // it's a symlink + throw SysError(format("opening file `%1%'") % path); + } + + unsigned int flags = 0, old; + + /* Silently ignore errors getting/setting the immutable flag so + that we work correctly on filesystems that don't support it. */ + if (ioctl(fd, FS_IOC_GETFLAGS, &flags)) return; + + old = flags; + + if (mut) flags &= ~FS_IMMUTABLE_FL; + else flags |= FS_IMMUTABLE_FL; + + if (old == flags) return; + + if (ioctl(fd, FS_IOC_SETFLAGS, &flags)) return; + +#endif +} + + +void makeImmutable(const Path & path) +{ + changeMutable(path, false); +} + + +void makeMutable(const Path & path) +{ + changeMutable(path, true); +} + + +} diff --git a/src/libutil/immutable.hh b/src/libutil/immutable.hh new file mode 100644 index 000000000000..5a42a4610736 --- /dev/null +++ b/src/libutil/immutable.hh @@ -0,0 +1,19 @@ +#ifndef __IMMUTABLE_H +#define __IMMUTABLE_H + +#include + +namespace nix { + +/* Make the given path immutable, i.e., prevent it from being modified + in any way, even by root. This is a no-op on platforms that do not + support this, or if the calling user is not privileged. On Linux, + this is implemented by doing the equivalent of ‘chattr +i path’. */ +void makeImmutable(const Path & path); + +/* Make the given path mutable. */ +void makeMutable(const Path & path); + +} + +#endif /* !__IMMUTABLE_H */ diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 42e5519b48b0..31322f9c4894 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -12,6 +12,7 @@ #include #include "util.hh" +#include "immutable.hh" extern char * * environ; @@ -304,6 +305,8 @@ static void _deletePath(const Path & path, unsigned long long & bytesFreed, struct stat st = lstat(path); + if (S_ISDIR(st.st_mode) || S_ISREG(st.st_mode)) makeMutable(path); + if (!S_ISDIR(st.st_mode) && st.st_nlink == 1) { bytesFreed += st.st_size; blocksFreed += st.st_blocks; -- cgit 1.4.1