* First step towards setuid/setgid support: a setuid/setgid wrapper
program.
The Nix store cannot directly support setuid binaries for a number
of reasons:
- Builds are generally not performed as root (and they shouldn't
be), so the builder cannot chown/chmod executables to the right
setuid ownership.
- Unpacking a NAR archive containing a setuid binary would only work
when Nix is run as root.
- Worst of all, setuid binaries don't fit in the purely functional
model: if a security bug is discovered in a setuid binary, that
binary should be removed from the system to prevent users from
calling it. But we cannot garbage collect it unless all
references to it are gone, which might never happen. Of course,
we could just remove setuid permission, but that would also be
impure.
So the solution is to keep setuid-ness out of the Nix store.
Rather, for programs that we want to execute as setuid, we generate
wrapper programs (as root) that are setuid and do an execve() to
call the real, non-setuid program in the Nix store.
That's what setuid-wrapper does. It determines its own name (e.g.,
/var/setuid-wrappers/passwd), reads the name of the wrapped program
from <self>.real (e.g., /var/setuid-wrappers/passwd.real, which
might contain /nix/var/nix/profiles/system/bin/passwd), and executes
it. Thus, the non-setuid passwd in the Nix store would be executed
with the effective user set to root.
Setuid-wrapper also performs a few security checks to prevent it
from reading a fake <self>.real file through hard-linking tricks.
svn path=/nixos/trunk/; revision=7157
2006-11-28 14:36:27 +01:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <dirent.h>
|
2008-02-14 12:08:06 +01:00
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
/* Make sure assertions are not compiled out. */
|
|
|
|
#undef NDEBUG
|
* First step towards setuid/setgid support: a setuid/setgid wrapper
program.
The Nix store cannot directly support setuid binaries for a number
of reasons:
- Builds are generally not performed as root (and they shouldn't
be), so the builder cannot chown/chmod executables to the right
setuid ownership.
- Unpacking a NAR archive containing a setuid binary would only work
when Nix is run as root.
- Worst of all, setuid binaries don't fit in the purely functional
model: if a security bug is discovered in a setuid binary, that
binary should be removed from the system to prevent users from
calling it. But we cannot garbage collect it unless all
references to it are gone, which might never happen. Of course,
we could just remove setuid permission, but that would also be
impure.
So the solution is to keep setuid-ness out of the Nix store.
Rather, for programs that we want to execute as setuid, we generate
wrapper programs (as root) that are setuid and do an execve() to
call the real, non-setuid program in the Nix store.
That's what setuid-wrapper does. It determines its own name (e.g.,
/var/setuid-wrappers/passwd), reads the name of the wrapped program
from <self>.real (e.g., /var/setuid-wrappers/passwd.real, which
might contain /nix/var/nix/profiles/system/bin/passwd), and executes
it. Thus, the non-setuid passwd in the Nix store would be executed
with the effective user set to root.
Setuid-wrapper also performs a few security checks to prevent it
from reading a fake <self>.real file through hard-linking tricks.
svn path=/nixos/trunk/; revision=7157
2006-11-28 14:36:27 +01:00
|
|
|
|
|
|
|
extern char **environ;
|
|
|
|
|
2006-11-28 18:34:27 +01:00
|
|
|
static char * wrapperDir = WRAPPER_DIR;
|
* First step towards setuid/setgid support: a setuid/setgid wrapper
program.
The Nix store cannot directly support setuid binaries for a number
of reasons:
- Builds are generally not performed as root (and they shouldn't
be), so the builder cannot chown/chmod executables to the right
setuid ownership.
- Unpacking a NAR archive containing a setuid binary would only work
when Nix is run as root.
- Worst of all, setuid binaries don't fit in the purely functional
model: if a security bug is discovered in a setuid binary, that
binary should be removed from the system to prevent users from
calling it. But we cannot garbage collect it unless all
references to it are gone, which might never happen. Of course,
we could just remove setuid permission, but that would also be
impure.
So the solution is to keep setuid-ness out of the Nix store.
Rather, for programs that we want to execute as setuid, we generate
wrapper programs (as root) that are setuid and do an execve() to
call the real, non-setuid program in the Nix store.
That's what setuid-wrapper does. It determines its own name (e.g.,
/var/setuid-wrappers/passwd), reads the name of the wrapped program
from <self>.real (e.g., /var/setuid-wrappers/passwd.real, which
might contain /nix/var/nix/profiles/system/bin/passwd), and executes
it. Thus, the non-setuid passwd in the Nix store would be executed
with the effective user set to root.
Setuid-wrapper also performs a few security checks to prevent it
from reading a fake <self>.real file through hard-linking tricks.
svn path=/nixos/trunk/; revision=7157
2006-11-28 14:36:27 +01:00
|
|
|
|
|
|
|
int main(int argc, char * * argv)
|
|
|
|
{
|
|
|
|
char self[PATH_MAX];
|
2008-02-14 12:08:06 +01:00
|
|
|
|
* First step towards setuid/setgid support: a setuid/setgid wrapper
program.
The Nix store cannot directly support setuid binaries for a number
of reasons:
- Builds are generally not performed as root (and they shouldn't
be), so the builder cannot chown/chmod executables to the right
setuid ownership.
- Unpacking a NAR archive containing a setuid binary would only work
when Nix is run as root.
- Worst of all, setuid binaries don't fit in the purely functional
model: if a security bug is discovered in a setuid binary, that
binary should be removed from the system to prevent users from
calling it. But we cannot garbage collect it unless all
references to it are gone, which might never happen. Of course,
we could just remove setuid permission, but that would also be
impure.
So the solution is to keep setuid-ness out of the Nix store.
Rather, for programs that we want to execute as setuid, we generate
wrapper programs (as root) that are setuid and do an execve() to
call the real, non-setuid program in the Nix store.
That's what setuid-wrapper does. It determines its own name (e.g.,
/var/setuid-wrappers/passwd), reads the name of the wrapped program
from <self>.real (e.g., /var/setuid-wrappers/passwd.real, which
might contain /nix/var/nix/profiles/system/bin/passwd), and executes
it. Thus, the non-setuid passwd in the Nix store would be executed
with the effective user set to root.
Setuid-wrapper also performs a few security checks to prevent it
from reading a fake <self>.real file through hard-linking tricks.
svn path=/nixos/trunk/; revision=7157
2006-11-28 14:36:27 +01:00
|
|
|
int len = readlink("/proc/self/exe", self, sizeof(self) - 1);
|
2008-02-14 12:08:06 +01:00
|
|
|
assert (len > 0);
|
* First step towards setuid/setgid support: a setuid/setgid wrapper
program.
The Nix store cannot directly support setuid binaries for a number
of reasons:
- Builds are generally not performed as root (and they shouldn't
be), so the builder cannot chown/chmod executables to the right
setuid ownership.
- Unpacking a NAR archive containing a setuid binary would only work
when Nix is run as root.
- Worst of all, setuid binaries don't fit in the purely functional
model: if a security bug is discovered in a setuid binary, that
binary should be removed from the system to prevent users from
calling it. But we cannot garbage collect it unless all
references to it are gone, which might never happen. Of course,
we could just remove setuid permission, but that would also be
impure.
So the solution is to keep setuid-ness out of the Nix store.
Rather, for programs that we want to execute as setuid, we generate
wrapper programs (as root) that are setuid and do an execve() to
call the real, non-setuid program in the Nix store.
That's what setuid-wrapper does. It determines its own name (e.g.,
/var/setuid-wrappers/passwd), reads the name of the wrapped program
from <self>.real (e.g., /var/setuid-wrappers/passwd.real, which
might contain /nix/var/nix/profiles/system/bin/passwd), and executes
it. Thus, the non-setuid passwd in the Nix store would be executed
with the effective user set to root.
Setuid-wrapper also performs a few security checks to prevent it
from reading a fake <self>.real file through hard-linking tricks.
svn path=/nixos/trunk/; revision=7157
2006-11-28 14:36:27 +01:00
|
|
|
self[len] = 0;
|
|
|
|
|
|
|
|
/* Make sure that we are being executed from the right location,
|
|
|
|
i.e., `wrapperDir'. This is to prevent someone from
|
|
|
|
creating hard link `X' from some other location, along with a
|
|
|
|
false `X.real' file, to allow arbitrary programs from being
|
2008-02-14 12:08:06 +01:00
|
|
|
executed setuid. */
|
|
|
|
assert ((strncmp(self, wrapperDir, sizeof(wrapperDir)) == 0) &&
|
|
|
|
(self[strlen(wrapperDir)] == '/'));
|
* First step towards setuid/setgid support: a setuid/setgid wrapper
program.
The Nix store cannot directly support setuid binaries for a number
of reasons:
- Builds are generally not performed as root (and they shouldn't
be), so the builder cannot chown/chmod executables to the right
setuid ownership.
- Unpacking a NAR archive containing a setuid binary would only work
when Nix is run as root.
- Worst of all, setuid binaries don't fit in the purely functional
model: if a security bug is discovered in a setuid binary, that
binary should be removed from the system to prevent users from
calling it. But we cannot garbage collect it unless all
references to it are gone, which might never happen. Of course,
we could just remove setuid permission, but that would also be
impure.
So the solution is to keep setuid-ness out of the Nix store.
Rather, for programs that we want to execute as setuid, we generate
wrapper programs (as root) that are setuid and do an execve() to
call the real, non-setuid program in the Nix store.
That's what setuid-wrapper does. It determines its own name (e.g.,
/var/setuid-wrappers/passwd), reads the name of the wrapped program
from <self>.real (e.g., /var/setuid-wrappers/passwd.real, which
might contain /nix/var/nix/profiles/system/bin/passwd), and executes
it. Thus, the non-setuid passwd in the Nix store would be executed
with the effective user set to root.
Setuid-wrapper also performs a few security checks to prevent it
from reading a fake <self>.real file through hard-linking tricks.
svn path=/nixos/trunk/; revision=7157
2006-11-28 14:36:27 +01:00
|
|
|
|
|
|
|
/* Make *really* *really* sure that we were executed as `self',
|
|
|
|
and not, say, as some other setuid program. That is, our
|
|
|
|
effective uid/gid should match the uid/gid of `self'. */
|
|
|
|
//printf("%d %d\n", geteuid(), getegid());
|
|
|
|
|
|
|
|
struct stat st;
|
2008-02-14 12:44:54 +01:00
|
|
|
assert (lstat(self, &st) != -1);
|
* First step towards setuid/setgid support: a setuid/setgid wrapper
program.
The Nix store cannot directly support setuid binaries for a number
of reasons:
- Builds are generally not performed as root (and they shouldn't
be), so the builder cannot chown/chmod executables to the right
setuid ownership.
- Unpacking a NAR archive containing a setuid binary would only work
when Nix is run as root.
- Worst of all, setuid binaries don't fit in the purely functional
model: if a security bug is discovered in a setuid binary, that
binary should be removed from the system to prevent users from
calling it. But we cannot garbage collect it unless all
references to it are gone, which might never happen. Of course,
we could just remove setuid permission, but that would also be
impure.
So the solution is to keep setuid-ness out of the Nix store.
Rather, for programs that we want to execute as setuid, we generate
wrapper programs (as root) that are setuid and do an execve() to
call the real, non-setuid program in the Nix store.
That's what setuid-wrapper does. It determines its own name (e.g.,
/var/setuid-wrappers/passwd), reads the name of the wrapped program
from <self>.real (e.g., /var/setuid-wrappers/passwd.real, which
might contain /nix/var/nix/profiles/system/bin/passwd), and executes
it. Thus, the non-setuid passwd in the Nix store would be executed
with the effective user set to root.
Setuid-wrapper also performs a few security checks to prevent it
from reading a fake <self>.real file through hard-linking tricks.
svn path=/nixos/trunk/; revision=7157
2006-11-28 14:36:27 +01:00
|
|
|
|
|
|
|
//printf("%d %d\n", st.st_uid, st.st_gid);
|
|
|
|
|
2008-02-14 12:08:06 +01:00
|
|
|
assert ((st.st_mode & S_ISUID) == 0 ||
|
|
|
|
(st.st_uid == geteuid()));
|
* First step towards setuid/setgid support: a setuid/setgid wrapper
program.
The Nix store cannot directly support setuid binaries for a number
of reasons:
- Builds are generally not performed as root (and they shouldn't
be), so the builder cannot chown/chmod executables to the right
setuid ownership.
- Unpacking a NAR archive containing a setuid binary would only work
when Nix is run as root.
- Worst of all, setuid binaries don't fit in the purely functional
model: if a security bug is discovered in a setuid binary, that
binary should be removed from the system to prevent users from
calling it. But we cannot garbage collect it unless all
references to it are gone, which might never happen. Of course,
we could just remove setuid permission, but that would also be
impure.
So the solution is to keep setuid-ness out of the Nix store.
Rather, for programs that we want to execute as setuid, we generate
wrapper programs (as root) that are setuid and do an execve() to
call the real, non-setuid program in the Nix store.
That's what setuid-wrapper does. It determines its own name (e.g.,
/var/setuid-wrappers/passwd), reads the name of the wrapped program
from <self>.real (e.g., /var/setuid-wrappers/passwd.real, which
might contain /nix/var/nix/profiles/system/bin/passwd), and executes
it. Thus, the non-setuid passwd in the Nix store would be executed
with the effective user set to root.
Setuid-wrapper also performs a few security checks to prevent it
from reading a fake <self>.real file through hard-linking tricks.
svn path=/nixos/trunk/; revision=7157
2006-11-28 14:36:27 +01:00
|
|
|
|
2008-02-14 12:08:06 +01:00
|
|
|
assert ((st.st_mode & S_ISGID) == 0 ||
|
|
|
|
st.st_gid == getegid());
|
* First step towards setuid/setgid support: a setuid/setgid wrapper
program.
The Nix store cannot directly support setuid binaries for a number
of reasons:
- Builds are generally not performed as root (and they shouldn't
be), so the builder cannot chown/chmod executables to the right
setuid ownership.
- Unpacking a NAR archive containing a setuid binary would only work
when Nix is run as root.
- Worst of all, setuid binaries don't fit in the purely functional
model: if a security bug is discovered in a setuid binary, that
binary should be removed from the system to prevent users from
calling it. But we cannot garbage collect it unless all
references to it are gone, which might never happen. Of course,
we could just remove setuid permission, but that would also be
impure.
So the solution is to keep setuid-ness out of the Nix store.
Rather, for programs that we want to execute as setuid, we generate
wrapper programs (as root) that are setuid and do an execve() to
call the real, non-setuid program in the Nix store.
That's what setuid-wrapper does. It determines its own name (e.g.,
/var/setuid-wrappers/passwd), reads the name of the wrapped program
from <self>.real (e.g., /var/setuid-wrappers/passwd.real, which
might contain /nix/var/nix/profiles/system/bin/passwd), and executes
it. Thus, the non-setuid passwd in the Nix store would be executed
with the effective user set to root.
Setuid-wrapper also performs a few security checks to prevent it
from reading a fake <self>.real file through hard-linking tricks.
svn path=/nixos/trunk/; revision=7157
2006-11-28 14:36:27 +01:00
|
|
|
|
|
|
|
/* And, of course, we shouldn't be writable. */
|
2008-02-14 12:08:06 +01:00
|
|
|
assert (!(st.st_mode & (S_IWGRP | S_IWOTH)));
|
* First step towards setuid/setgid support: a setuid/setgid wrapper
program.
The Nix store cannot directly support setuid binaries for a number
of reasons:
- Builds are generally not performed as root (and they shouldn't
be), so the builder cannot chown/chmod executables to the right
setuid ownership.
- Unpacking a NAR archive containing a setuid binary would only work
when Nix is run as root.
- Worst of all, setuid binaries don't fit in the purely functional
model: if a security bug is discovered in a setuid binary, that
binary should be removed from the system to prevent users from
calling it. But we cannot garbage collect it unless all
references to it are gone, which might never happen. Of course,
we could just remove setuid permission, but that would also be
impure.
So the solution is to keep setuid-ness out of the Nix store.
Rather, for programs that we want to execute as setuid, we generate
wrapper programs (as root) that are setuid and do an execve() to
call the real, non-setuid program in the Nix store.
That's what setuid-wrapper does. It determines its own name (e.g.,
/var/setuid-wrappers/passwd), reads the name of the wrapped program
from <self>.real (e.g., /var/setuid-wrappers/passwd.real, which
might contain /nix/var/nix/profiles/system/bin/passwd), and executes
it. Thus, the non-setuid passwd in the Nix store would be executed
with the effective user set to root.
Setuid-wrapper also performs a few security checks to prevent it
from reading a fake <self>.real file through hard-linking tricks.
svn path=/nixos/trunk/; revision=7157
2006-11-28 14:36:27 +01:00
|
|
|
|
|
|
|
|
|
|
|
/* Read the path of the real (wrapped) program from <self>.real. */
|
|
|
|
char realFN[PATH_MAX + 10];
|
2008-02-14 12:08:06 +01:00
|
|
|
int realFNSize = snprintf (realFN, sizeof(realFN), "%s.real", self);
|
|
|
|
assert (realFNSize < sizeof(realFN));
|
* First step towards setuid/setgid support: a setuid/setgid wrapper
program.
The Nix store cannot directly support setuid binaries for a number
of reasons:
- Builds are generally not performed as root (and they shouldn't
be), so the builder cannot chown/chmod executables to the right
setuid ownership.
- Unpacking a NAR archive containing a setuid binary would only work
when Nix is run as root.
- Worst of all, setuid binaries don't fit in the purely functional
model: if a security bug is discovered in a setuid binary, that
binary should be removed from the system to prevent users from
calling it. But we cannot garbage collect it unless all
references to it are gone, which might never happen. Of course,
we could just remove setuid permission, but that would also be
impure.
So the solution is to keep setuid-ness out of the Nix store.
Rather, for programs that we want to execute as setuid, we generate
wrapper programs (as root) that are setuid and do an execve() to
call the real, non-setuid program in the Nix store.
That's what setuid-wrapper does. It determines its own name (e.g.,
/var/setuid-wrappers/passwd), reads the name of the wrapped program
from <self>.real (e.g., /var/setuid-wrappers/passwd.real, which
might contain /nix/var/nix/profiles/system/bin/passwd), and executes
it. Thus, the non-setuid passwd in the Nix store would be executed
with the effective user set to root.
Setuid-wrapper also performs a few security checks to prevent it
from reading a fake <self>.real file through hard-linking tricks.
svn path=/nixos/trunk/; revision=7157
2006-11-28 14:36:27 +01:00
|
|
|
|
|
|
|
int fdSelf = open(realFN, O_RDONLY);
|
2008-02-14 12:08:06 +01:00
|
|
|
assert (fdSelf != -1);
|
* First step towards setuid/setgid support: a setuid/setgid wrapper
program.
The Nix store cannot directly support setuid binaries for a number
of reasons:
- Builds are generally not performed as root (and they shouldn't
be), so the builder cannot chown/chmod executables to the right
setuid ownership.
- Unpacking a NAR archive containing a setuid binary would only work
when Nix is run as root.
- Worst of all, setuid binaries don't fit in the purely functional
model: if a security bug is discovered in a setuid binary, that
binary should be removed from the system to prevent users from
calling it. But we cannot garbage collect it unless all
references to it are gone, which might never happen. Of course,
we could just remove setuid permission, but that would also be
impure.
So the solution is to keep setuid-ness out of the Nix store.
Rather, for programs that we want to execute as setuid, we generate
wrapper programs (as root) that are setuid and do an execve() to
call the real, non-setuid program in the Nix store.
That's what setuid-wrapper does. It determines its own name (e.g.,
/var/setuid-wrappers/passwd), reads the name of the wrapped program
from <self>.real (e.g., /var/setuid-wrappers/passwd.real, which
might contain /nix/var/nix/profiles/system/bin/passwd), and executes
it. Thus, the non-setuid passwd in the Nix store would be executed
with the effective user set to root.
Setuid-wrapper also performs a few security checks to prevent it
from reading a fake <self>.real file through hard-linking tricks.
svn path=/nixos/trunk/; revision=7157
2006-11-28 14:36:27 +01:00
|
|
|
|
|
|
|
char real[PATH_MAX];
|
|
|
|
len = read(fdSelf, real, PATH_MAX);
|
2008-02-14 12:08:06 +01:00
|
|
|
assert (len != -1);
|
|
|
|
assert (len < sizeof (real));
|
* First step towards setuid/setgid support: a setuid/setgid wrapper
program.
The Nix store cannot directly support setuid binaries for a number
of reasons:
- Builds are generally not performed as root (and they shouldn't
be), so the builder cannot chown/chmod executables to the right
setuid ownership.
- Unpacking a NAR archive containing a setuid binary would only work
when Nix is run as root.
- Worst of all, setuid binaries don't fit in the purely functional
model: if a security bug is discovered in a setuid binary, that
binary should be removed from the system to prevent users from
calling it. But we cannot garbage collect it unless all
references to it are gone, which might never happen. Of course,
we could just remove setuid permission, but that would also be
impure.
So the solution is to keep setuid-ness out of the Nix store.
Rather, for programs that we want to execute as setuid, we generate
wrapper programs (as root) that are setuid and do an execve() to
call the real, non-setuid program in the Nix store.
That's what setuid-wrapper does. It determines its own name (e.g.,
/var/setuid-wrappers/passwd), reads the name of the wrapped program
from <self>.real (e.g., /var/setuid-wrappers/passwd.real, which
might contain /nix/var/nix/profiles/system/bin/passwd), and executes
it. Thus, the non-setuid passwd in the Nix store would be executed
with the effective user set to root.
Setuid-wrapper also performs a few security checks to prevent it
from reading a fake <self>.real file through hard-linking tricks.
svn path=/nixos/trunk/; revision=7157
2006-11-28 14:36:27 +01:00
|
|
|
real[len] = 0;
|
2008-02-14 11:56:14 +01:00
|
|
|
|
|
|
|
close(fdSelf);
|
* First step towards setuid/setgid support: a setuid/setgid wrapper
program.
The Nix store cannot directly support setuid binaries for a number
of reasons:
- Builds are generally not performed as root (and they shouldn't
be), so the builder cannot chown/chmod executables to the right
setuid ownership.
- Unpacking a NAR archive containing a setuid binary would only work
when Nix is run as root.
- Worst of all, setuid binaries don't fit in the purely functional
model: if a security bug is discovered in a setuid binary, that
binary should be removed from the system to prevent users from
calling it. But we cannot garbage collect it unless all
references to it are gone, which might never happen. Of course,
we could just remove setuid permission, but that would also be
impure.
So the solution is to keep setuid-ness out of the Nix store.
Rather, for programs that we want to execute as setuid, we generate
wrapper programs (as root) that are setuid and do an execve() to
call the real, non-setuid program in the Nix store.
That's what setuid-wrapper does. It determines its own name (e.g.,
/var/setuid-wrappers/passwd), reads the name of the wrapped program
from <self>.real (e.g., /var/setuid-wrappers/passwd.real, which
might contain /nix/var/nix/profiles/system/bin/passwd), and executes
it. Thus, the non-setuid passwd in the Nix store would be executed
with the effective user set to root.
Setuid-wrapper also performs a few security checks to prevent it
from reading a fake <self>.real file through hard-linking tricks.
svn path=/nixos/trunk/; revision=7157
2006-11-28 14:36:27 +01:00
|
|
|
|
|
|
|
//printf("real = %s, len = %d\n", real, len);
|
|
|
|
|
|
|
|
execve(real, argv, environ);
|
|
|
|
|
|
|
|
exit(1);
|
|
|
|
}
|