http://ftp.aanet.ru/pub/Linux/X11/apps/xscreensaver-2.31.tar.gz
[xscreensaver] / driver / setuid.c
1 /* setuid.c --- management of runtime priveleges.
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     fprintf (out, "%s: initial effective uid/gid was %s\n", blurb(),
67              si->orig_uid);
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 /* If we've been run as setuid or setgid to someone else (most likely root)
78    turn off the extra permissions so that random user-specified programs
79    don't get special privileges.  (On some systems it is necessary to install
80    this program as setuid root in order to read the passwd file to implement
81    lock-mode.)
82  */
83 void
84 hack_uid (saver_info *si)
85 {
86   si->orig_uid = strdup (uid_gid_string (geteuid(), getegid()));
87
88   setgid (getgid ());
89   setuid (getuid ());
90
91   /* If we're being run as root (as from xdm) then switch the user id
92      to something safe. */
93   if (getuid () == 0)
94     {
95       struct passwd *p = 0;
96       struct group *g = 0;
97       int uid_errno = 0;
98       int gid_errno = 0;
99
100       /* Locking can't work when running as root, because we have no way of
101          knowing what the user id of the logged in user is (so we don't know
102          whose password to prompt for.)
103        */
104       si->locking_disabled_p = True;
105       si->nolock_reason = "running as root";
106       p = getpwnam ("nobody");
107       if (! p) p = getpwnam ("noaccess");
108       if (! p) p = getpwnam ("daemon");
109       if (! p)
110         {
111           fprintf (stderr,
112                    "%s: running as root, and couldn't find a safer uid.\n",
113                    blurb());
114           saver_exit(si, 1, 0);
115         }
116
117       g = getgrgid (p->pw_gid);
118
119       /* Rumor has it that some implementations of of setuid() do nothing
120          when called with -1; therefore, if the "nobody" user has a uid of
121          -1, then that would be Really Bad.  Rumor further has it that such
122          systems really ought to be using -2 for "nobody", since that works.
123          So, if we get a uid (or gid, for good measure) of -1, switch to -2
124          instead.
125        */
126
127       if (p->pw_gid == -1) p->pw_gid = -2;
128       if (p->pw_uid == -1) p->pw_uid = -2;
129
130
131       /* Change the gid to be a safe one, then change the uid to be a safe
132          one (must do it in this order, because root privs vanish when uid
133          is changed, and after that, gid can't be changed.)
134        */
135       if (setgid (p->pw_gid) != 0)
136         gid_errno = errno ? errno : -1;
137       if (setuid (p->pw_uid) != 0)
138         uid_errno = errno ? errno : -1;
139
140       if (uid_errno == 0 && gid_errno == 0)
141         {
142           static char buf [1024];
143           sprintf (buf, "changed uid/gid to %s/%s (%ld/%ld).",
144                    p->pw_name, (g ? g->gr_name : "???"),
145                    (long) p->pw_uid, (long) p->pw_gid);
146           si->uid_message = buf;
147         }
148       else
149         {
150           char buf [1024];
151           if (gid_errno)
152             {
153               sprintf (buf, "%s: couldn't set gid to %s (%ld)",
154                        blurb(),
155                        (g ? g->gr_name : "???"),
156                        (long) p->pw_gid);
157               if (gid_errno == -1)
158                 fprintf(stderr, "%s: unknown error\n", buf);
159               else
160                 perror(buf);
161             }
162
163           if (uid_errno)
164             {
165               sprintf (buf, "%s: couldn't set uid to %s (%ld)",
166                        blurb(),
167                        (p ? p->pw_name : "???"),
168                        (long) p->pw_uid);
169               if (uid_errno == -1)
170                 fprintf(stderr, "%s: unknown error\n", buf);
171               else
172                 perror(buf);
173             }
174         }
175
176       if (uid_errno != 0)
177         {
178           /* We'd better exit rather than continue running as root.
179              But if we switched uid but not gid, continue running,
180              since that doesn't really matter.  (Right?)
181            */
182           saver_exit (si, -1, 0);
183         }
184     }
185 # ifndef NO_LOCKING
186  else   /* disable locking if already being run as "someone else" */
187    {
188      struct passwd *p = getpwuid (getuid ());
189      if (!p ||
190          !strcmp (p->pw_name, "root") ||
191          !strcmp (p->pw_name, "nobody") ||
192          !strcmp (p->pw_name, "noaccess") ||
193          !strcmp (p->pw_name, "operator") ||
194          !strcmp (p->pw_name, "daemon") ||
195          !strcmp (p->pw_name, "bin") ||
196          !strcmp (p->pw_name, "adm") ||
197          !strcmp (p->pw_name, "sys") ||
198          !strcmp (p->pw_name, "games"))
199        {
200          static char buf [1024];
201          sprintf (buf, "running as %s", p->pw_name);
202          si->nolock_reason = buf;
203          si->locking_disabled_p = True;
204        }
205    }
206 # endif /* !NO_LOCKING */
207 }
208
209 #else  /* !NO_SETUID */
210
211 void hack_uid (saver_info *si) { }
212 void hack_uid_warn (saver_info *si) { }
213 void describe_uids (saver_info *si) { }
214
215 #endif /* NO_SETUID */