1323565b775f0151508ababc37cc1351668ccb13
[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 #ifndef NO_SETUID /* whole file */
18
19 #include <X11/Xlib.h>           /* not used for much... */
20
21 /* This file doesn't need the Xt headers, so stub these types out... */
22 #undef XtPointer
23 #define XtAppContext void*
24 #define XrmDatabase  void*
25 #define XtIntervalId void*
26 #define XtPointer    void*
27 #define Widget       void*
28
29 #include "xscreensaver.h"
30
31 #ifndef EPERM
32 #include <errno.h>
33 #endif
34
35 #include <pwd.h>                /* for getpwnam() and struct passwd */
36 #include <grp.h>                /* for getgrgid() and struct group */
37
38
39 static const char *
40 uid_gid_string (uid_t uid, gid_t gid)
41 {
42   static char buf[255];
43   struct passwd *p = 0;
44   struct group *g = 0;
45   p = getpwuid (uid);
46   g = getgrgid (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);
51   return buf;
52 }
53
54
55 void
56 describe_uids (saver_info *si, FILE *out)
57 {
58   uid_t uid = getuid();
59   gid_t gid = getgid();
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));
64
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(),
69              si->orig_uid);
70
71   fprintf (out, "%s: running as %s", blurb(), s1);
72   if (uid != euid || gid != egid)
73     fprintf (out, "; effectively %s", s2);
74   fprintf(out, "\n");
75   free(s1);
76   free(s2);
77 }
78
79
80 static int
81 set_ids_by_name (struct passwd *p, struct group *g, char **message_ret)
82 {
83   int uid_errno = 0;
84   int gid_errno = 0;
85   uid_t uid = p->pw_uid;
86   gid_t gid = g->gr_gid;
87
88   if (message_ret)
89     *message_ret = 0;
90
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
96      instead.
97    */
98   if (gid == (gid_t) -1) gid = (gid_t) -2;
99   if (uid == (uid_t) -1) uid = (uid_t) -2;
100
101   errno = 0;
102   if (setgid (gid) != 0)
103     gid_errno = errno ? errno : -1;
104
105   errno = 0;
106   if (setuid (uid) != 0)
107     uid_errno = errno ? errno : -1;
108
109   if (uid_errno == 0 && gid_errno == 0)
110     {
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);
115       if (message_ret)
116         *message_ret = buf;
117       return 0;
118     }
119   else
120     {
121       char buf [1024];
122       if (gid_errno)
123         {
124           sprintf (buf, "%s: couldn't set gid to %s (%ld)",
125                    blurb(),
126                    (g ? g->gr_name : "???"),
127                    (long) gid);
128           if (gid_errno == -1)
129             fprintf(stderr, "%s: unknown error\n", buf);
130           else
131             perror(buf);
132         }
133
134       if (uid_errno)
135         {
136           sprintf (buf, "%s: couldn't set uid to %s (%ld)",
137                    blurb(),
138                    (p ? p->pw_name : "???"),
139                    (long) uid);
140           if (uid_errno == -1)
141             fprintf(stderr, "%s: unknown error\n", buf);
142           else
143             perror(buf);
144         }
145
146       return -1;
147     }
148 }
149
150 static int
151 set_ids_by_number (uid_t uid, gid_t gid, char **message_ret)
152 {
153   struct passwd *p;
154   struct group *g;
155
156   errno = 0;
157   p = getpwuid (uid);
158   if (!p)
159     {
160       char buf [1024];
161       sprintf (buf, "%s: error looking up name of user %d", blurb(),
162                (long) uid);
163       if (errno)
164         perror (buf);
165       else
166         fprintf (stderr, "%s: unknown error.\n", buf);
167       return -1;
168     }
169
170   errno = 0;
171   g = getgrgid (gid);
172   if (!g)
173     {
174       char buf [1024];
175       sprintf (buf, "%s: error looking up name of group %d", blurb(),
176                (long) gid);
177       if (errno)
178         perror (buf);
179       else
180         fprintf (stderr, "%s: unknown error.\n", buf);
181       return -1;
182     }
183
184   return set_ids_by_name (p, g, message_ret);
185 }
186
187
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
192    lock-mode.)
193
194      *** WARNING: DO NOT DISABLE ANY OF THE FOLLOWING CODE!
195          If you do so, you will open a security hole.  See the sections
196          of the xscreensaver manual titled "LOCKING AND ROOT LOGINS", 
197          and "USING XDM".
198  */
199 void
200 hack_uid (saver_info *si)
201 {
202
203   /* Discard privileges, and set the effective user/group ids to the
204      real user/group ids.  That is, give up our "chmod +s" rights.
205    */
206   {
207     uid_t euid = geteuid();
208     gid_t egid = getegid();
209     uid_t uid = getuid();
210     gid_t gid = getgid();
211
212     si->orig_uid = strdup (uid_gid_string (euid, egid));
213
214     if (uid != euid || gid != egid)
215       if (set_ids_by_number (uid, gid, &si->uid_message) != 0)
216         saver_exit (si, 1, 0);
217   }
218
219
220   /* Locking can't work when running as root, because we have no way of
221      knowing what the user id of the logged in user is (so we don't know
222      whose password to prompt for.)
223
224      *** WARNING: DO NOT DISABLE THIS CODE!
225          If you do so, you will open a security hole.  See the sections
226          of the xscreensaver manual titled "LOCKING AND ROOT LOGINS",
227          and "USING XDM".
228    */
229   if (getuid() == (uid_t) 0)
230     {
231       si->locking_disabled_p = True;
232       si->nolock_reason = "running as root";
233     }
234
235
236   /* If we're running as root, switch to a safer user.  This is above and
237      beyond the fact that we've disabling locking, above -- the theory is
238      that running graphics demos as root is just always a stupid thing
239      to do, since they have probably never been security reviewed and are
240      more likely to be buggy than just about any other kind of program.
241      (And that assumes non-malicious code.  There are also attacks here.)
242
243      *** WARNING: DO NOT DISABLE THIS CODE!
244          If you do so, you will open a security hole.  See the sections
245          of the xscreensaver manual titled "LOCKING AND ROOT LOGINS", 
246          and "USING XDM".
247    */
248   if (getuid() == (uid_t) 0)
249     {
250       struct passwd *p;
251
252       p = getpwnam ("nobody");
253       if (! p) p = getpwnam ("noaccess");
254       if (! p) p = getpwnam ("daemon");
255       if (! p)
256         {
257           fprintf (stderr,
258                    "%s: running as root, and couldn't find a safer uid.\n",
259                    blurb());
260           saver_exit(si, 1, 0);
261         }
262
263       if (set_ids_by_number (p->pw_uid, p->pw_gid, &si->uid_message) != 0)
264         saver_exit (si, -1, 0);
265     }
266
267
268   /* If there's anything even remotely funny looking about the passwd struct,
269      or if we're running as some other user from the list below (a
270      non-comprehensive selection of users known to be privileged in some way,
271      and not normal end-users) then disable locking.  If it was possible,
272      switching to "nobody" would be the thing to do, but only root itself has
273      the privs to do that.
274
275      *** WARNING: DO NOT DISABLE THIS CODE!
276          If you do so, you will open a security hole.  See the sections
277          of the xscreensaver manual titled "LOCKING AND ROOT LOGINS",
278          and "USING XDM".
279    */
280   {
281     uid_t uid = getuid ();              /* get it again */
282     struct passwd *p = getpwuid (uid);  /* get it again */
283
284     if (!p ||
285         uid == (uid_t)  0 ||
286         uid == (uid_t) -1 ||
287         uid == (uid_t) -2 ||
288         p->pw_uid == (uid_t)  0 ||
289         p->pw_uid == (uid_t) -1 ||
290         p->pw_uid == (uid_t) -2 ||
291         !p->pw_name ||
292         !*p->pw_name ||
293         !strcmp (p->pw_name, "root") ||
294         !strcmp (p->pw_name, "nobody") ||
295         !strcmp (p->pw_name, "noaccess") ||
296         !strcmp (p->pw_name, "operator") ||
297         !strcmp (p->pw_name, "daemon") ||
298         !strcmp (p->pw_name, "bin") ||
299         !strcmp (p->pw_name, "adm") ||
300         !strcmp (p->pw_name, "sys") ||
301         !strcmp (p->pw_name, "games"))
302       {
303         static char buf [1024];
304         sprintf (buf, "running as %s",
305                  (p && p->pw_name && *p->pw_name
306                   ? p->pw_name : "<unknown>"));
307         si->nolock_reason = buf;
308         si->locking_disabled_p = True;
309         si->dangerous_uid_p = True;
310       }
311   }
312 }
313
314 #else  /* !NO_SETUID */
315
316 void hack_uid (saver_info *si) { }
317 void describe_uids (saver_info *si, FILE *out) { }
318
319 #endif /* NO_SETUID */