343dcf097dba0bb5b0b8a234492420a6ddeb6145
[xscreensaver] / driver / setuid.c
1 /* setuid.c --- management of runtime privileges.
2  * xscreensaver, Copyright (c) 1993-1998 Jamie Zawinski <jwz@jwz.org>
3  *
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 
10  * implied warranty.
11  */
12
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
16
17 #include <X11/Xlib.h>           /* not used for much... */
18
19 /* This file doesn't need the Xt headers, so stub these types out... */
20 #undef XtPointer
21 #define XtAppContext void*
22 #define XrmDatabase  void*
23 #define XtIntervalId void*
24 #define XtPointer    void*
25 #define Widget       void*
26
27 #include "xscreensaver.h"
28
29 #ifndef EPERM
30 #include <errno.h>
31 #endif
32
33 #include <pwd.h>                /* for getpwnam() and struct passwd */
34 #include <grp.h>                /* for getgrgid() and struct group */
35
36 static const char *
37 uid_gid_string (uid_t uid, gid_t gid)
38 {
39   static char buf[255];
40   struct passwd *p = 0;
41   struct group *g = 0;
42   p = getpwuid (uid);
43   g = getgrgid (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);
48   return buf;
49 }
50
51
52 void
53 describe_uids (saver_info *si, FILE *out)
54 {
55   uid_t uid = getuid();
56   gid_t gid = getgid();
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));
61
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(),
66              si->orig_uid);
67
68   fprintf (out, "%s: running as %s", blurb(), s1);
69   if (uid != euid || gid != egid)
70     fprintf (out, "; effectively %s", s2);
71   fprintf(out, "\n");
72   free(s1);
73   free(s2);
74 }
75
76
77 static int
78 set_ids_by_number (uid_t uid, gid_t gid, char **message_ret)
79 {
80   int uid_errno = 0;
81   int gid_errno = 0;
82   struct passwd *p = getpwuid (uid);
83   struct group  *g = getgrgid (gid);
84
85   if (message_ret)
86     *message_ret = 0;
87
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
93      instead.  Note that this must be done after we've looked up the
94      user/group names with getpwuid(-1) and/or getgrgid(-1).
95    */
96   if (gid == (gid_t) -1) gid = (gid_t) -2;
97   if (uid == (uid_t) -1) uid = (uid_t) -2;
98
99   errno = 0;
100   if (setgid (gid) != 0)
101     gid_errno = errno ? errno : -1;
102
103   errno = 0;
104   if (setuid (uid) != 0)
105     uid_errno = errno ? errno : -1;
106
107   if (uid_errno == 0 && gid_errno == 0)
108     {
109       static char buf [1024];
110       sprintf (buf, "changed uid/gid to %s/%s (%ld/%ld).",
111                (p && p->pw_name ? p->pw_name : "???"),
112                (g && g->gr_name ? g->gr_name : "???"),
113                (long) uid, (long) gid);
114       if (message_ret)
115         *message_ret = buf;
116       return 0;
117     }
118   else
119     {
120       char buf [1024];
121       if (gid_errno)
122         {
123           sprintf (buf, "%s: couldn't set gid to %s (%ld)",
124                    blurb(),
125                    (g && g->gr_name ? g->gr_name : "???"),
126                    (long) gid);
127           if (gid_errno == -1)
128             fprintf(stderr, "%s: unknown error\n", buf);
129           else
130             perror(buf);
131         }
132
133       if (uid_errno)
134         {
135           sprintf (buf, "%s: couldn't set uid to %s (%ld)",
136                    blurb(),
137                    (p && p->pw_name ? p->pw_name : "???"),
138                    (long) uid);
139           if (uid_errno == -1)
140             fprintf(stderr, "%s: unknown error\n", buf);
141           else
142             perror(buf);
143         }
144
145       return -1;
146     }
147 }
148
149
150 /* If we've been run as setuid or setgid to someone else (most likely root)
151    turn off the extra permissions so that random user-specified programs
152    don't get special privileges.  (On some systems it is necessary to install
153    this program as setuid root in order to read the passwd file to implement
154    lock-mode.)
155
156      *** WARNING: DO NOT DISABLE ANY OF THE FOLLOWING CODE!
157          If you do so, you will open a security hole.  See the sections
158          of the xscreensaver manual titled "LOCKING AND ROOT LOGINS", 
159          and "USING XDM".
160  */
161 void
162 hack_uid (saver_info *si)
163 {
164
165   /* Discard privileges, and set the effective user/group ids to the
166      real user/group ids.  That is, give up our "chmod +s" rights.
167    */
168   {
169     uid_t euid = geteuid();
170     gid_t egid = getegid();
171     uid_t uid = getuid();
172     gid_t gid = getgid();
173
174     si->orig_uid = strdup (uid_gid_string (euid, egid));
175
176     if (uid != euid || gid != egid)
177       if (set_ids_by_number (uid, gid, &si->uid_message) != 0)
178         saver_exit (si, 1, 0);
179   }
180
181
182   /* Locking can't work when running as root, because we have no way of
183      knowing what the user id of the logged in user is (so we don't know
184      whose password to prompt for.)
185
186      *** WARNING: DO NOT DISABLE THIS CODE!
187          If you do so, you will open a security hole.  See the sections
188          of the xscreensaver manual titled "LOCKING AND ROOT LOGINS",
189          and "USING XDM".
190    */
191   if (getuid() == (uid_t) 0)
192     {
193       si->locking_disabled_p = True;
194       si->nolock_reason = "running as root";
195     }
196
197
198   /* If we're running as root, switch to a safer user.  This is above and
199      beyond the fact that we've disabling locking, above -- the theory is
200      that running graphics demos as root is just always a stupid thing
201      to do, since they have probably never been security reviewed and are
202      more likely to be buggy than just about any other kind of program.
203      (And that assumes non-malicious code.  There are also attacks here.)
204
205      *** WARNING: DO NOT DISABLE THIS CODE!
206          If you do so, you will open a security hole.  See the sections
207          of the xscreensaver manual titled "LOCKING AND ROOT LOGINS", 
208          and "USING XDM".
209    */
210   if (getuid() == (uid_t) 0)
211     {
212       struct passwd *p;
213
214       p = getpwnam ("nobody");
215       if (! p) p = getpwnam ("noaccess");
216       if (! p) p = getpwnam ("daemon");
217       if (! p)
218         {
219           fprintf (stderr,
220                    "%s: running as root, and couldn't find a safer uid.\n",
221                    blurb());
222           saver_exit(si, 1, 0);
223         }
224
225       if (set_ids_by_number (p->pw_uid, p->pw_gid, &si->uid_message) != 0)
226         saver_exit (si, -1, 0);
227     }
228
229
230   /* If there's anything even remotely funny looking about the passwd struct,
231      or if we're running as some other user from the list below (a
232      non-comprehensive selection of users known to be privileged in some way,
233      and not normal end-users) then disable locking.  If it was possible,
234      switching to "nobody" would be the thing to do, but only root itself has
235      the privs to do that.
236
237      *** WARNING: DO NOT DISABLE THIS CODE!
238          If you do so, you will open a security hole.  See the sections
239          of the xscreensaver manual titled "LOCKING AND ROOT LOGINS",
240          and "USING XDM".
241    */
242   {
243     uid_t uid = getuid ();              /* get it again */
244     struct passwd *p = getpwuid (uid);  /* get it again */
245
246     if (!p ||
247         uid == (uid_t)  0 ||
248         uid == (uid_t) -1 ||
249         uid == (uid_t) -2 ||
250         p->pw_uid == (uid_t)  0 ||
251         p->pw_uid == (uid_t) -1 ||
252         p->pw_uid == (uid_t) -2 ||
253         !p->pw_name ||
254         !*p->pw_name ||
255         !strcmp (p->pw_name, "root") ||
256         !strcmp (p->pw_name, "nobody") ||
257         !strcmp (p->pw_name, "noaccess") ||
258         !strcmp (p->pw_name, "operator") ||
259         !strcmp (p->pw_name, "daemon") ||
260         !strcmp (p->pw_name, "bin") ||
261         !strcmp (p->pw_name, "adm") ||
262         !strcmp (p->pw_name, "sys") ||
263         !strcmp (p->pw_name, "games"))
264       {
265         static char buf [1024];
266         sprintf (buf, "running as %s",
267                  (p && p->pw_name && *p->pw_name
268                   ? p->pw_name : "<unknown>"));
269         si->nolock_reason = buf;
270         si->locking_disabled_p = True;
271         si->dangerous_uid_p = True;
272       }
273   }
274 }