ftp://ftp.smr.ru/pub/0/FreeBSD/releases/distfiles/xscreensaver-3.16.tar.gz
[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_name (struct passwd *p, struct group *g, char **message_ret)
79 {
80   int uid_errno = 0;
81   int gid_errno = 0;
82   uid_t uid = p->pw_uid;
83   gid_t gid = g->gr_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.
94    */
95   if (gid == (gid_t) -1) gid = (gid_t) -2;
96   if (uid == (uid_t) -1) uid = (uid_t) -2;
97
98   errno = 0;
99   if (setgid (gid) != 0)
100     gid_errno = errno ? errno : -1;
101
102   errno = 0;
103   if (setuid (uid) != 0)
104     uid_errno = errno ? errno : -1;
105
106   if (uid_errno == 0 && gid_errno == 0)
107     {
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);
112       if (message_ret)
113         *message_ret = buf;
114       return 0;
115     }
116   else
117     {
118       char buf [1024];
119       if (gid_errno)
120         {
121           sprintf (buf, "%s: couldn't set gid to %s (%ld)",
122                    blurb(),
123                    (g ? g->gr_name : "???"),
124                    (long) gid);
125           if (gid_errno == -1)
126             fprintf(stderr, "%s: unknown error\n", buf);
127           else
128             perror(buf);
129         }
130
131       if (uid_errno)
132         {
133           sprintf (buf, "%s: couldn't set uid to %s (%ld)",
134                    blurb(),
135                    (p ? p->pw_name : "???"),
136                    (long) uid);
137           if (uid_errno == -1)
138             fprintf(stderr, "%s: unknown error\n", buf);
139           else
140             perror(buf);
141         }
142
143       return -1;
144     }
145 }
146
147 static int
148 set_ids_by_number (uid_t uid, gid_t gid, char **message_ret)
149 {
150   struct passwd *p;
151   struct group *g;
152
153   errno = 0;
154   p = getpwuid (uid);
155   if (!p)
156     {
157       char buf [1024];
158       sprintf (buf, "%s: error looking up name of user %d", blurb(),
159                (long) uid);
160       if (errno)
161         perror (buf);
162       else
163         fprintf (stderr, "%s: unknown error.\n", buf);
164       return -1;
165     }
166
167   errno = 0;
168   g = getgrgid (gid);
169   if (!g)
170     {
171       char buf [1024];
172       sprintf (buf, "%s: error looking up name of group %d", blurb(),
173                (long) gid);
174       if (errno)
175         perror (buf);
176       else
177         fprintf (stderr, "%s: unknown error.\n", buf);
178       return -1;
179     }
180
181   return set_ids_by_name (p, g, message_ret);
182 }
183
184
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
189    lock-mode.)
190
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", 
194          and "USING XDM".
195  */
196 void
197 hack_uid (saver_info *si)
198 {
199
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.
202    */
203   {
204     uid_t euid = geteuid();
205     gid_t egid = getegid();
206     uid_t uid = getuid();
207     gid_t gid = getgid();
208
209     si->orig_uid = strdup (uid_gid_string (euid, egid));
210
211     if (uid != euid || gid != egid)
212       if (set_ids_by_number (uid, gid, &si->uid_message) != 0)
213         saver_exit (si, 1, 0);
214   }
215
216
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.)
220
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",
224          and "USING XDM".
225    */
226   if (getuid() == (uid_t) 0)
227     {
228       si->locking_disabled_p = True;
229       si->nolock_reason = "running as root";
230     }
231
232
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.)
239
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", 
243          and "USING XDM".
244    */
245   if (getuid() == (uid_t) 0)
246     {
247       struct passwd *p;
248
249       p = getpwnam ("nobody");
250       if (! p) p = getpwnam ("noaccess");
251       if (! p) p = getpwnam ("daemon");
252       if (! p)
253         {
254           fprintf (stderr,
255                    "%s: running as root, and couldn't find a safer uid.\n",
256                    blurb());
257           saver_exit(si, 1, 0);
258         }
259
260       if (set_ids_by_number (p->pw_uid, p->pw_gid, &si->uid_message) != 0)
261         saver_exit (si, -1, 0);
262     }
263
264
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.
271
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",
275          and "USING XDM".
276    */
277   {
278     uid_t uid = getuid ();              /* get it again */
279     struct passwd *p = getpwuid (uid);  /* get it again */
280
281     if (!p ||
282         uid == (uid_t)  0 ||
283         uid == (uid_t) -1 ||
284         uid == (uid_t) -2 ||
285         p->pw_uid == (uid_t)  0 ||
286         p->pw_uid == (uid_t) -1 ||
287         p->pw_uid == (uid_t) -2 ||
288         !p->pw_name ||
289         !*p->pw_name ||
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"))
299       {
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;
307       }
308   }
309 }