1 /* setuid.c --- management of runtime privileges.
2 * xscreensaver, Copyright (c) 1993-1998 Jamie Zawinski <jwz@jwz.org>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
17 #include <X11/Xlib.h> /* not used for much... */
19 /* This file doesn't need the Xt headers, so stub these types out... */
21 #define XtAppContext void*
22 #define XrmDatabase void*
23 #define XtIntervalId void*
24 #define XtPointer void*
27 #include "xscreensaver.h"
33 #include <pwd.h> /* for getpwnam() and struct passwd */
34 #include <grp.h> /* for getgrgid() and struct group */
37 uid_gid_string (uid_t uid, gid_t gid)
44 sprintf (buf, "%s/%s (%ld/%ld)",
45 (p && p->pw_name ? p->pw_name : "???"),
46 (g && g->gr_name ? g->gr_name : "???"),
47 (long) uid, (long) gid);
53 describe_uids (saver_info *si, FILE *out)
57 uid_t euid = geteuid();
58 gid_t egid = getegid();
59 char *s1 = strdup (uid_gid_string (uid, gid));
60 char *s2 = strdup (uid_gid_string (euid, egid));
62 if (si->orig_uid && *si->orig_uid &&
63 (!!strcmp (si->orig_uid, s1) ||
64 !!strcmp (si->orig_uid, s2)))
65 fprintf (out, "%s: initial effective uid/gid was %s\n", blurb(),
68 fprintf (out, "%s: running as %s", blurb(), s1);
69 if (uid != euid || gid != egid)
70 fprintf (out, "; effectively %s", s2);
78 set_ids_by_name (struct passwd *p, struct group *g, char **message_ret)
82 uid_t uid = p->pw_uid;
83 gid_t gid = g->gr_gid;
88 /* Rumor has it that some implementations of of setuid() do nothing
89 when called with -1; therefore, if the "nobody" user has a uid of
90 -1, then that would be Really Bad. Rumor further has it that such
91 systems really ought to be using -2 for "nobody", since that works.
92 So, if we get a uid (or gid, for good measure) of -1, switch to -2
95 if (gid == (gid_t) -1) gid = (gid_t) -2;
96 if (uid == (uid_t) -1) uid = (uid_t) -2;
99 if (setgid (gid) != 0)
100 gid_errno = errno ? errno : -1;
103 if (setuid (uid) != 0)
104 uid_errno = errno ? errno : -1;
106 if (uid_errno == 0 && gid_errno == 0)
108 static char buf [1024];
109 sprintf (buf, "changed uid/gid to %s/%s (%ld/%ld).",
110 p->pw_name, (g ? g->gr_name : "???"),
111 (long) uid, (long) gid);
121 sprintf (buf, "%s: couldn't set gid to %s (%ld)",
123 (g ? g->gr_name : "???"),
126 fprintf(stderr, "%s: unknown error\n", buf);
133 sprintf (buf, "%s: couldn't set uid to %s (%ld)",
135 (p ? p->pw_name : "???"),
138 fprintf(stderr, "%s: unknown error\n", buf);
148 set_ids_by_number (uid_t uid, gid_t gid, char **message_ret)
158 sprintf (buf, "%s: error looking up name of user %d", blurb(),
163 fprintf (stderr, "%s: unknown error.\n", buf);
172 sprintf (buf, "%s: error looking up name of group %d", blurb(),
177 fprintf (stderr, "%s: unknown error.\n", buf);
181 return set_ids_by_name (p, g, message_ret);
185 /* If we've been run as setuid or setgid to someone else (most likely root)
186 turn off the extra permissions so that random user-specified programs
187 don't get special privileges. (On some systems it is necessary to install
188 this program as setuid root in order to read the passwd file to implement
191 *** WARNING: DO NOT DISABLE ANY OF THE FOLLOWING CODE!
192 If you do so, you will open a security hole. See the sections
193 of the xscreensaver manual titled "LOCKING AND ROOT LOGINS",
197 hack_uid (saver_info *si)
200 /* Discard privileges, and set the effective user/group ids to the
201 real user/group ids. That is, give up our "chmod +s" rights.
204 uid_t euid = geteuid();
205 gid_t egid = getegid();
206 uid_t uid = getuid();
207 gid_t gid = getgid();
209 si->orig_uid = strdup (uid_gid_string (euid, egid));
211 if (uid != euid || gid != egid)
212 if (set_ids_by_number (uid, gid, &si->uid_message) != 0)
213 saver_exit (si, 1, 0);
217 /* Locking can't work when running as root, because we have no way of
218 knowing what the user id of the logged in user is (so we don't know
219 whose password to prompt for.)
221 *** WARNING: DO NOT DISABLE THIS CODE!
222 If you do so, you will open a security hole. See the sections
223 of the xscreensaver manual titled "LOCKING AND ROOT LOGINS",
226 if (getuid() == (uid_t) 0)
228 si->locking_disabled_p = True;
229 si->nolock_reason = "running as root";
233 /* If we're running as root, switch to a safer user. This is above and
234 beyond the fact that we've disabling locking, above -- the theory is
235 that running graphics demos as root is just always a stupid thing
236 to do, since they have probably never been security reviewed and are
237 more likely to be buggy than just about any other kind of program.
238 (And that assumes non-malicious code. There are also attacks here.)
240 *** WARNING: DO NOT DISABLE THIS CODE!
241 If you do so, you will open a security hole. See the sections
242 of the xscreensaver manual titled "LOCKING AND ROOT LOGINS",
245 if (getuid() == (uid_t) 0)
249 p = getpwnam ("nobody");
250 if (! p) p = getpwnam ("noaccess");
251 if (! p) p = getpwnam ("daemon");
255 "%s: running as root, and couldn't find a safer uid.\n",
257 saver_exit(si, 1, 0);
260 if (set_ids_by_number (p->pw_uid, p->pw_gid, &si->uid_message) != 0)
261 saver_exit (si, -1, 0);
265 /* If there's anything even remotely funny looking about the passwd struct,
266 or if we're running as some other user from the list below (a
267 non-comprehensive selection of users known to be privileged in some way,
268 and not normal end-users) then disable locking. If it was possible,
269 switching to "nobody" would be the thing to do, but only root itself has
270 the privs to do that.
272 *** WARNING: DO NOT DISABLE THIS CODE!
273 If you do so, you will open a security hole. See the sections
274 of the xscreensaver manual titled "LOCKING AND ROOT LOGINS",
278 uid_t uid = getuid (); /* get it again */
279 struct passwd *p = getpwuid (uid); /* get it again */
285 p->pw_uid == (uid_t) 0 ||
286 p->pw_uid == (uid_t) -1 ||
287 p->pw_uid == (uid_t) -2 ||
290 !strcmp (p->pw_name, "root") ||
291 !strcmp (p->pw_name, "nobody") ||
292 !strcmp (p->pw_name, "noaccess") ||
293 !strcmp (p->pw_name, "operator") ||
294 !strcmp (p->pw_name, "daemon") ||
295 !strcmp (p->pw_name, "bin") ||
296 !strcmp (p->pw_name, "adm") ||
297 !strcmp (p->pw_name, "sys") ||
298 !strcmp (p->pw_name, "games"))
300 static char buf [1024];
301 sprintf (buf, "running as %s",
302 (p && p->pw_name && *p->pw_name
303 ? p->pw_name : "<unknown>"));
304 si->nolock_reason = buf;
305 si->locking_disabled_p = True;
306 si->dangerous_uid_p = True;