aboutsummaryrefslogtreecommitdiff
path: root/src/libutil
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2006-09-04 22:55:28 +0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2006-09-04 22:55:28 +0000
commitbafc1690fc4a2a2c3ff81ff1c1a677f208d3b1b7 (patch)
tree822877e73d7e4ffe4dd3ef3b1a1ff02dafaed186 /src/libutil
parente5a6c09b12a9a68ba604d4e95adf28482ae8fc8d (diff)
* Move setuid stuff to libutil.
* Install libexpr header files.
Diffstat (limited to 'src/libutil')
-rw-r--r--src/libutil/util.cc136
-rw-r--r--src/libutil/util.hh6
2 files changed, 139 insertions, 3 deletions
diff --git a/src/libutil/util.cc b/src/libutil/util.cc
index 44b39f8c6..eac124edb 100644
--- a/src/libutil/util.cc
+++ b/src/libutil/util.cc
@@ -850,4 +850,140 @@ bool string2Int(const string & s, int & n)
}
+//////////////////////////////////////////////////////////////////////
+
+
+static bool haveSwitched;
+static uid_t savedUid, nixUid;
+static gid_t savedGid, nixGid;
+
+
+#if HAVE_SETRESUID
+#define _setuid(uid) setresuid(uid, uid, savedUid)
+#define _setgid(gid) setresgid(gid, gid, savedGid)
+#else
+/* Only works properly when run by root. */
+#define _setuid(uid) setuid(uid)
+#define _setgid(gid) setgid(gid)
+#endif
+
+
+SwitchToOriginalUser::SwitchToOriginalUser()
+{
+#if SETUID_HACK && HAVE_SETRESUID
+ /* Temporarily switch the effective uid/gid back to the saved
+ uid/gid (which is the uid/gid of the user that executed the Nix
+ program; it's *not* the real uid/gid, since we changed that to
+ the Nix user in switchToNixUser()). */
+ if (haveSwitched) {
+ if (setuid(savedUid) == -1)
+ throw SysError(format("temporarily restoring uid to `%1%'") % savedUid);
+ if (setgid(savedGid) == -1)
+ throw SysError(format("temporarily restoring gid to `%1%'") % savedGid);
+ }
+#endif
+}
+
+
+SwitchToOriginalUser::~SwitchToOriginalUser()
+{
+#if SETUID_HACK && HAVE_SETRESUID
+ /* Switch the effective uid/gid back to the Nix user. */
+ if (haveSwitched) {
+ if (setuid(nixUid) == -1)
+ throw SysError(format("restoring uid to `%1%'") % nixUid);
+ if (setgid(nixGid) == -1)
+ throw SysError(format("restoring gid to `%1%'") % nixGid);
+ }
+#endif
+}
+
+
+void switchToNixUser()
+{
+#if SETUID_HACK
+
+ /* Don't do anything if this is not a setuid binary. */
+ if (getuid() == geteuid() && getgid() == getegid()) return;
+
+ /* Here we set the uid and gid to the Nix user and group,
+ respectively, IF the current (real) user is a member of the Nix
+ group. Otherwise we just drop all privileges. */
+
+ /* Lookup the Nix gid. */
+ struct group * gr = getgrnam(NIX_GROUP);
+ if (!gr) {
+ cerr << format("missing group `%1%'\n") % NIX_GROUP;
+ exit(1);
+ }
+
+ /* Get the supplementary group IDs for the current user. */
+ int maxGids = 512, nrGids;
+ gid_t gids[maxGids];
+ if ((nrGids = getgroups(maxGids, gids)) == -1) {
+ cerr << format("unable to query gids\n");
+ exit(1);
+ }
+
+ /* !!! Apparently it is unspecified whether getgroups() includes
+ the effective gid. In that case the following test is always
+ true *if* the program is installed setgid (which we do when we
+ have setresuid()). On Linux this doesn't appear to be the
+ case, but we should switch to the real gid before doing this
+ test, and then switch back to the saved gid. */
+
+ /* Check that the current user is a member of the Nix group. */
+ bool found = false;
+ for (int i = 0; i < nrGids; ++i)
+ if (gids[i] == gr->gr_gid) {
+ found = true;
+ break;
+ }
+
+ if (!found) {
+ /* Not in the Nix group - drop all root/Nix privileges. */
+ _setgid(getgid());
+ _setuid(getuid());
+ return;
+ }
+
+ savedUid = getuid();
+ savedGid = getgid();
+
+ /* Set the real, effective and saved gids to gr->gr_gid. Also
+ make very sure that this succeeded. We switch the gid first
+ because we cannot do it after we have dropped root uid. */
+ nixGid = gr->gr_gid;
+ if (_setgid(nixGid) != 0 || getgid() != nixGid || getegid() != nixGid) {
+ cerr << format("unable to set gid to `%1%'\n") % NIX_GROUP;
+ exit(1);
+ }
+
+ /* Lookup the Nix uid. */
+ struct passwd * pw = getpwnam(NIX_USER);
+ if (!pw) {
+ cerr << format("missing user `%1%'\n") % NIX_USER;
+ exit(1);
+ }
+
+ /* This will drop all root privileges, setting the real, effective
+ and saved uids to pw->pw_uid. Also make very sure that this
+ succeeded.*/
+ nixUid = pw->pw_uid;
+ if (_setuid(nixUid) != 0 || getuid() != nixUid || geteuid() != nixUid) {
+ cerr << format("unable to set uid to `%1%'\n") % NIX_USER;
+ exit(1);
+ }
+
+ /* !!! for setuid operation, we should: 1) wipe the environment;
+ 2) verify file descriptors 0, 1, 2; 3) etc.
+ See: http://www.daemon-systems.org/man/setuid.7.html
+ */
+
+ haveSwitched = true;
+
+#endif
+}
+
+
}
diff --git a/src/libutil/util.hh b/src/libutil/util.hh
index b4a61ae04..52ef2c6eb 100644
--- a/src/libutil/util.hh
+++ b/src/libutil/util.hh
@@ -246,15 +246,15 @@ string int2String(int n);
bool string2Int(const string & s, int & n);
-/* !!! HACK HACK HACK - this should be in shared.hh, but it's to
- facilitate a quick hack - will remove this eventually (famous last
- words). */
+/* Setuid support. */
struct SwitchToOriginalUser
{
SwitchToOriginalUser();
~SwitchToOriginalUser();
};
+void switchToNixUser();
+
}