1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2 *
3 * setuid.c --- management of runtime privileges.
4 *
5 * xscreensaver, Copyright (c) 1993-1998 Jamie Zawinski <jwz@jwz.org>
6 *
7 * Permission to use, copy, modify, distribute, and sell this software and its
8 * documentation for any purpose is hereby granted without fee, provided that
9 * the above copyright notice appear in all copies and that both that
10 * copyright notice and this permission notice appear in supporting
11 * documentation. No representations are made about the suitability of this
12 * software for any purpose. It is provided "as is" without express or
13 * implied warranty.
14 */
15
16 #include "config.h"
17
18 #ifdef USE_SETRES
19 #define _GNU_SOURCE
20 #endif /* USE_SETRES */
21
22 #include <errno.h>
23
24 #include <stdio.h>
25 #include <string.h>
26 #include <sys/types.h>
27 #include <unistd.h>
28 #include <pwd.h> /* for getpwnam() and struct passwd */
29 #include <grp.h> /* for getgrgid() and struct group */
30
31 #include "setuid.h"
32
33 static char *
uid_gid_string(uid_t uid,gid_t gid)34 uid_gid_string (uid_t uid,
35 gid_t gid)
36 {
37 static char *buf;
38 struct passwd *p = NULL;
39 struct group *g = NULL;
40
41 p = getpwuid (uid);
42 g = getgrgid (gid);
43
44 buf = g_strdup_printf ("%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
49 return buf;
50 }
51
52 static gboolean
set_ids_by_number(uid_t uid,gid_t gid,char ** message_ret)53 set_ids_by_number (uid_t uid,
54 gid_t gid,
55 char **message_ret)
56 {
57 int uid_errno = 0;
58 int gid_errno = 0;
59 int sgs_errno = 0;
60 struct passwd *p = getpwuid (uid);
61 struct group *g = getgrgid (gid);
62
63 if (message_ret)
64 *message_ret = NULL;
65
66 /* Rumor has it that some implementations of of setuid() do nothing
67 when called with -1; therefore, if the "nobody" user has a uid of
68 -1, then that would be Really Bad. Rumor further has it that such
69 systems really ought to be using -2 for "nobody", since that works.
70 So, if we get a uid (or gid, for good measure) of -1, switch to -2
71 instead. Note that this must be done after we've looked up the
72 user/group names with getpwuid(-1) and/or getgrgid(-1).
73 */
74 if (gid == (gid_t) -1) gid = (gid_t) -2;
75 if (uid == (uid_t) -1) uid = (uid_t) -2;
76
77 #ifndef USE_SETRES
78 errno = 0;
79 if (setgroups (1, &gid) < 0)
80 sgs_errno = errno ? errno : -1;
81
82 errno = 0;
83 if (setgid (gid) != 0)
84 gid_errno = errno ? errno : -1;
85
86 errno = 0;
87 if (setuid (uid) != 0)
88 uid_errno = errno ? errno : -1;
89 #else /* !USE_SETRES */
90 errno = 0;
91 if (setresgid (gid, gid, gid) != 0)
92 gid_errno = errno ? errno : -1;
93
94 errno = 0;
95 if (setresuid (uid, uid, uid) != 0)
96 uid_errno = errno ? errno : -1;
97 #endif /* USE_SETRES */
98
99 if (uid_errno == 0 && gid_errno == 0 && sgs_errno == 0) {
100 static char *reason;
101 reason = g_strdup_printf ("changed uid/gid to %s/%s (%ld/%ld).",
102 (p && p->pw_name ? p->pw_name : "???"),
103 (g && g->gr_name ? g->gr_name : "???"),
104 (long) uid, (long) gid);
105 if (message_ret)
106 *message_ret = g_strdup (reason);
107
108 g_free (reason);
109
110 return TRUE;
111 } else {
112 char *reason = NULL;
113
114 if (sgs_errno) {
115 reason = g_strdup_printf ("couldn't setgroups to %s (%ld)",
116 (g && g->gr_name ? g->gr_name : "???"),
117 (long) gid);
118 if (sgs_errno == -1)
119 fprintf (stderr, "%s: unknown error\n", reason);
120 else {
121 errno = sgs_errno;
122 perror (reason);
123 }
124 g_free (reason);
125 reason = NULL;
126 }
127
128 if (gid_errno) {
129 reason = g_strdup_printf ("couldn't set gid to %s (%ld)",
130 (g && g->gr_name ? g->gr_name : "???"),
131 (long) gid);
132 if (gid_errno == -1)
133 fprintf (stderr, "%s: unknown error\n", reason);
134 else {
135 errno = gid_errno;
136 perror (reason);
137 }
138 g_free (reason);
139 reason = NULL;
140 }
141
142 if (uid_errno) {
143 reason = g_strdup_printf ("couldn't set uid to %s (%ld)",
144 (p && p->pw_name ? p->pw_name : "???"),
145 (long) uid);
146 if (uid_errno == -1)
147 fprintf (stderr, "%s: unknown error\n", reason);
148 else {
149 errno = uid_errno;
150 perror (reason);
151 }
152 g_free (reason);
153 reason = NULL;
154 }
155 return FALSE;
156 }
157 return FALSE;
158 }
159
160
161 /* If we've been run as setuid or setgid to someone else (most likely root)
162 turn off the extra permissions so that random user-specified programs
163 don't get special privileges. (On some systems it is necessary to install
164 this program as setuid root in order to read the passwd file to implement
165 lock-mode.)
166
167 *** WARNING: DO NOT DISABLE ANY OF THE FOLLOWING CODE!
168 If you do so, you will open a security hole. See the sections
169 of the xscreensaver manual titled "LOCKING AND ROOT LOGINS",
170 and "USING XDM".
171 */
172
173 /* Returns TRUE if OK to lock, FALSE otherwise */
174 gboolean
hack_uid(char ** nolock_reason,char ** orig_uid,char ** uid_message)175 hack_uid (char **nolock_reason,
176 char **orig_uid,
177 char **uid_message)
178 {
179 char *reason;
180 gboolean ret;
181
182 ret = TRUE;
183 reason = NULL;
184
185 if (nolock_reason != NULL) {
186 *nolock_reason = NULL;
187 }
188 if (orig_uid != NULL) {
189 *orig_uid = NULL;
190 }
191 if (uid_message != NULL) {
192 *uid_message = NULL;
193 }
194
195 /* Discard privileges, and set the effective user/group ids to the
196 real user/group ids. That is, give up our "chmod +s" rights.
197 */
198 {
199 uid_t euid = geteuid ();
200 gid_t egid = getegid ();
201 uid_t uid = getuid ();
202 gid_t gid = getgid ();
203
204 if (orig_uid != NULL) {
205 *orig_uid = uid_gid_string (euid, egid);
206 }
207
208 if (uid != euid || gid != egid) {
209 #ifndef USE_SETRES
210 if (! set_ids_by_number (uid, gid, uid_message)) {
211 #else /* !USE_SETRES */
212 if (! set_ids_by_number (euid == 0 ? uid : euid, egid == 0 ? gid : egid, uid_message)) {
213 #endif /* USE_SETRES */
214 reason = g_strdup ("unable to discard privileges.");
215
216 ret = FALSE;
217 goto out;
218 }
219 }
220 }
221
222
223 /* Locking can't work when running as root, because we have no way of
224 knowing what the user id of the logged in user is (so we don't know
225 whose password to prompt for.)
226
227 *** WARNING: DO NOT DISABLE THIS CODE!
228 If you do so, you will open a security hole. See the sections
229 of the xscreensaver manual titled "LOCKING AND ROOT LOGINS",
230 and "USING XDM".
231 */
232 if (getuid () == (uid_t) 0) {
233 reason = g_strdup ("running as root");
234 ret = FALSE;
235 goto out;
236 }
237
238 out:
239 if (nolock_reason != NULL) {
240 *nolock_reason = g_strdup (reason);
241 }
242 g_free (reason);
243
244 return ret;
245 }
246