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 #ifndef NO_SETUID /* whole file */
19 #include <X11/Xlib.h> /* not used for much... */
21 /* This file doesn't need the Xt headers, so stub these types out... */
23 #define XtAppContext void*
24 #define XrmDatabase void*
25 #define XtIntervalId void*
26 #define XtPointer void*
29 #include "xscreensaver.h"
35 #include <pwd.h> /* for getpwnam() and struct passwd */
36 #include <grp.h> /* for getgrgid() and struct group */
40 uid_gid_string(uid_t uid, gid_t gid)
47 sprintf (buf, "%s/%s (%ld/%ld)",
48 (p && p->pw_name ? p->pw_name : "???"),
49 (g && g->gr_name ? g->gr_name : "???"),
50 (long) uid, (long) gid);
56 describe_uids (saver_info *si, FILE *out)
60 uid_t euid = geteuid();
61 gid_t egid = getegid();
62 char *s1 = strdup (uid_gid_string (uid, gid));
63 char *s2 = strdup (uid_gid_string (euid, egid));
65 if (si->orig_uid && *si->orig_uid &&
66 (!!strcmp (si->orig_uid, s1) ||
67 !!strcmp (si->orig_uid, s2)))
68 fprintf (out, "%s: initial effective uid/gid was %s\n", blurb(),
71 fprintf (out, "%s: running as %s", blurb(), s1);
72 if (uid != euid || gid != egid)
73 fprintf (out, "; effectively %s", s2);
81 set_ids_by_name (struct passwd *p, struct group *g, char **message_ret)
85 uid_t uid = p->pw_uid;
86 gid_t gid = g->gr_gid;
91 /* Rumor has it that some implementations of of setuid() do nothing
92 when called with -1; therefore, if the "nobody" user has a uid of
93 -1, then that would be Really Bad. Rumor further has it that such
94 systems really ought to be using -2 for "nobody", since that works.
95 So, if we get a uid (or gid, for good measure) of -1, switch to -2
98 if (gid == (gid_t) -1) gid = (gid_t) -2;
99 if (uid == (uid_t) -1) uid = (uid_t) -2;
102 if (setgid (gid) != 0)
103 gid_errno = errno ? errno : -1;
106 if (setuid (uid) != 0)
107 uid_errno = errno ? errno : -1;
109 if (uid_errno == 0 && gid_errno == 0)
111 static char buf [1024];
112 sprintf (buf, "changed uid/gid to %s/%s (%ld/%ld).",
113 p->pw_name, (g ? g->gr_name : "???"),
114 (long) uid, (long) gid);
124 sprintf (buf, "%s: couldn't set gid to %s (%ld)",
126 (g ? g->gr_name : "???"),
129 fprintf(stderr, "%s: unknown error\n", buf);
136 sprintf (buf, "%s: couldn't set uid to %s (%ld)",
138 (p ? p->pw_name : "???"),
141 fprintf(stderr, "%s: unknown error\n", buf);
151 set_ids_by_number (uid_t uid, gid_t gid, char **message_ret)
161 sprintf (buf, "%s: error looking up name of user %d", blurb(),
166 fprintf (stderr, "%s: unknown error.\n", buf);
175 sprintf (buf, "%s: error looking up name of group %d", blurb(),
180 fprintf (stderr, "%s: unknown error.\n", buf);
184 return set_ids_by_name (p, g, message_ret);
188 /* If we've been run as setuid or setgid to someone else (most likely root)
189 turn off the extra permissions so that random user-specified programs
190 don't get special privileges. (On some systems it is necessary to install
191 this program as setuid root in order to read the passwd file to implement
195 hack_uid (saver_info *si)
198 /* Discard privileges, and set the effective user/group ids to the
199 real user/group ids. That is, give up our "chmod +s" rights.
202 uid_t euid = geteuid();
203 gid_t egid = getegid();
204 uid_t uid = getuid();
205 gid_t gid = getgid();
207 si->orig_uid = strdup (uid_gid_string (euid, egid));
209 if (uid != euid || gid != egid)
210 if (set_ids_by_number (uid, gid, &si->uid_message) != 0)
211 saver_exit (si, 1, 0);
214 /* Locking can't work when running as root, because we have no way of
215 knowing what the user id of the logged in user is (so we don't know
216 whose password to prompt for.)
218 if (getuid() == (uid_t) 0)
220 si->locking_disabled_p = True;
221 si->nolock_reason = "running as root";
224 /* If we're running as root, switch to a safer user. This is above and
225 beyond the fact that we've disabling locking, above -- the theory is
226 that running graphics demos as root is just always a stupid thing
227 to do, since they have probably never been security reviewed and are
228 more likely to be buggy than just about any other kind of program.
230 if (getuid() == (uid_t) 0)
234 p = getpwnam ("nobody");
235 if (! p) p = getpwnam ("noaccess");
236 if (! p) p = getpwnam ("daemon");
240 "%s: running as root, and couldn't find a safer uid.\n",
242 saver_exit(si, 1, 0);
245 if (set_ids_by_number (p->pw_uid, p->pw_gid, &si->uid_message) != 0)
246 saver_exit (si, -1, 0);
250 /* If there's anything even remotely funny looking about the passwd struct,
251 or if we're running as some other user from the list below (a
252 non-comprehensive selection of users known to be privileged in some way,
253 and not normal end-users) then disable locking. If it was possible,
254 switching to "nobody" would be the thing to do, but only root itself has
255 the privs to do that.
258 uid_t uid = getuid (); /* get it again */
259 struct passwd *p = getpwuid (uid); /* get it again */
265 p->pw_uid == (uid_t) 0 ||
266 p->pw_uid == (uid_t) -1 ||
267 p->pw_uid == (uid_t) -2 ||
270 !strcmp (p->pw_name, "root") ||
271 !strcmp (p->pw_name, "nobody") ||
272 !strcmp (p->pw_name, "noaccess") ||
273 !strcmp (p->pw_name, "operator") ||
274 !strcmp (p->pw_name, "daemon") ||
275 !strcmp (p->pw_name, "bin") ||
276 !strcmp (p->pw_name, "adm") ||
277 !strcmp (p->pw_name, "sys") ||
278 !strcmp (p->pw_name, "games"))
280 static char buf [1024];
281 sprintf (buf, "running as %s",
282 (p && p->pw_name && *p->pw_name
283 ? p->pw_name : "<unknown>"));
284 si->nolock_reason = buf;
285 si->locking_disabled_p = True;
286 si->dangerous_uid_p = True;
291 #else /* !NO_SETUID */
293 void hack_uid (saver_info *si) { }
294 void describe_uids (saver_info *si, FILE *out) { }
296 #endif /* NO_SETUID */