summaryrefslogtreecommitdiff
path: root/src/setuid.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/setuid.c')
-rw-r--r--src/setuid.c247
1 files changed, 247 insertions, 0 deletions
diff --git a/src/setuid.c b/src/setuid.c
new file mode 100644
index 0000000..80e4659
--- /dev/null
+++ b/src/setuid.c
@@ -0,0 +1,247 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * setuid.c --- management of runtime privileges.
+ *
+ * xscreensaver, Copyright (c) 1993-1998 Jamie Zawinski <[email protected]>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#include "config.h"
+
+#ifndef EPERM
+#include <errno.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <pwd.h> /* for getpwnam() and struct passwd */
+#include <grp.h> /* for getgrgid() and struct group */
+#include <glib.h>
+
+#include "setuid.h"
+
+static char *
+uid_gid_string (uid_t uid,
+ gid_t gid)
+{
+ static char *buf;
+ struct passwd *p = NULL;
+ struct group *g = NULL;
+
+ p = getpwuid (uid);
+ g = getgrgid (gid);
+
+ buf = g_strdup_printf ("%s/%s (%ld/%ld)",
+ (p && p->pw_name ? p->pw_name : "???"),
+ (g && g->gr_name ? g->gr_name : "???"),
+ (long) uid, (long) gid);
+
+ return buf;
+}
+
+static gboolean
+set_ids_by_number (uid_t uid,
+ gid_t gid,
+ char **message_ret)
+{
+ int uid_errno = 0;
+ int gid_errno = 0;
+ int sgs_errno = 0;
+ struct passwd *p = getpwuid (uid);
+ struct group *g = getgrgid (gid);
+
+ if (message_ret)
+ *message_ret = NULL;
+
+ /* Rumor has it that some implementations of of setuid() do nothing
+ when called with -1; therefore, if the "nobody" user has a uid of
+ -1, then that would be Really Bad. Rumor further has it that such
+ systems really ought to be using -2 for "nobody", since that works.
+ So, if we get a uid (or gid, for good measure) of -1, switch to -2
+ instead. Note that this must be done after we've looked up the
+ user/group names with getpwuid(-1) and/or getgrgid(-1).
+ */
+ if (gid == (gid_t) -1) gid = (gid_t) -2;
+ if (uid == (uid_t) -1) uid = (uid_t) -2;
+
+ errno = 0;
+ if (setgroups (1, &gid) < 0)
+ sgs_errno = errno ? errno : -1;
+
+ errno = 0;
+ if (setgid (gid) != 0)
+ gid_errno = errno ? errno : -1;
+
+ errno = 0;
+ if (setuid (uid) != 0)
+ uid_errno = errno ? errno : -1;
+
+ if (uid_errno == 0 && gid_errno == 0 && sgs_errno == 0)
+ {
+ static char *reason;
+ reason = g_strdup_printf ("changed uid/gid to %s/%s (%ld/%ld).",
+ (p && p->pw_name ? p->pw_name : "???"),
+ (g && g->gr_name ? g->gr_name : "???"),
+ (long) uid, (long) gid);
+ if (message_ret)
+ *message_ret = g_strdup (reason);
+
+ g_free (reason);
+
+ return TRUE;
+ }
+ else
+ {
+ char *reason = NULL;
+
+ if (sgs_errno)
+ {
+ reason = g_strdup_printf ("couldn't setgroups to %s (%ld)",
+ (g && g->gr_name ? g->gr_name : "???"),
+ (long) gid);
+ if (sgs_errno == -1)
+ fprintf (stderr, "%s: unknown error\n", reason);
+ else
+ {
+ errno = sgs_errno;
+ perror (reason);
+ }
+ g_free (reason);
+ reason = NULL;
+ }
+
+ if (gid_errno)
+ {
+ reason = g_strdup_printf ("couldn't set gid to %s (%ld)",
+ (g && g->gr_name ? g->gr_name : "???"),
+ (long) gid);
+ if (gid_errno == -1)
+ fprintf (stderr, "%s: unknown error\n", reason);
+ else
+ {
+ errno = gid_errno;
+ perror (reason);
+ }
+ g_free (reason);
+ reason = NULL;
+ }
+
+ if (uid_errno)
+ {
+ reason = g_strdup_printf ("couldn't set uid to %s (%ld)",
+ (p && p->pw_name ? p->pw_name : "???"),
+ (long) uid);
+ if (uid_errno == -1)
+ fprintf (stderr, "%s: unknown error\n", reason);
+ else
+ {
+ errno = uid_errno;
+ perror (reason);
+ }
+ g_free (reason);
+ reason = NULL;
+ }
+ return FALSE;
+ }
+ return FALSE;
+}
+
+
+/* If we've been run as setuid or setgid to someone else (most likely root)
+ turn off the extra permissions so that random user-specified programs
+ don't get special privileges. (On some systems it is necessary to install
+ this program as setuid root in order to read the passwd file to implement
+ lock-mode.)
+
+ *** WARNING: DO NOT DISABLE ANY OF THE FOLLOWING CODE!
+ If you do so, you will open a security hole. See the sections
+ of the xscreensaver manual titled "LOCKING AND ROOT LOGINS",
+ and "USING XDM".
+*/
+
+/* Returns TRUE if OK to lock, FALSE otherwise */
+gboolean
+hack_uid (char **nolock_reason,
+ char **orig_uid,
+ char **uid_message)
+{
+ char *reason;
+ gboolean ret;
+
+ ret = TRUE;
+ reason = NULL;
+
+ if (nolock_reason != NULL)
+ {
+ *nolock_reason = NULL;
+ }
+ if (orig_uid != NULL)
+ {
+ *orig_uid = NULL;
+ }
+ if (uid_message != NULL)
+ {
+ *uid_message = NULL;
+ }
+
+ /* Discard privileges, and set the effective user/group ids to the
+ real user/group ids. That is, give up our "chmod +s" rights.
+ */
+ {
+ uid_t euid = geteuid ();
+ gid_t egid = getegid ();
+ uid_t uid = getuid ();
+ gid_t gid = getgid ();
+
+ if (orig_uid != NULL)
+ {
+ *orig_uid = uid_gid_string (euid, egid);
+ }
+
+ if (uid != euid || gid != egid)
+ {
+ if (! set_ids_by_number (uid, gid, uid_message))
+ {
+ reason = g_strdup ("unable to discard privileges.");
+
+ ret = FALSE;
+ goto out;
+ }
+ }
+ }
+
+
+ /* Locking can't work when running as root, because we have no way of
+ knowing what the user id of the logged in user is (so we don't know
+ whose password to prompt for.)
+
+ *** WARNING: DO NOT DISABLE THIS CODE!
+ If you do so, you will open a security hole. See the sections
+ of the xscreensaver manual titled "LOCKING AND ROOT LOGINS",
+ and "USING XDM".
+ */
+ if (getuid () == (uid_t) 0)
+ {
+ reason = g_strdup ("running as root");
+ ret = FALSE;
+ goto out;
+ }
+
+out:
+ if (nolock_reason != NULL)
+ {
+ *nolock_reason = g_strdup (reason);
+ }
+ g_free (reason);
+
+ return ret;
+}