1 /*
2 * Copyright © 2014 Red Hat, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 * Author: Hans de Goede <hdegoede@redhat.com>
24 */
25
26 #include "dix-config.h"
27 #include "xorg-config.h"
28
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <limits.h>
32 #include <stdint.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/ioctl.h>
37 #include <sys/stat.h>
38 #ifdef HAVE_SYS_SYSMACROS_H
39 #include <sys/sysmacros.h>
40 #endif
41 #include <sys/types.h>
42 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
43 #include <sys/consio.h>
44 #endif
45 #include <unistd.h>
46 #ifdef WITH_LIBDRM
47 #include <drm.h>
48 #include <xf86drm.h> /* For DRM_DEV_NAME */
49 #endif
50
51 #include "misc.h"
52
53 #define CONFIG_FILE SYSCONFDIR "/X11/Xwrapper.config"
54
55 static const char *progname;
56
57 enum { ROOT_ONLY, CONSOLE_ONLY, ANYBODY };
58
59 /* KISS non locale / LANG parsing isspace version */
is_space(char c)60 static int is_space(char c)
61 {
62 return c == ' ' || c == '\t' || c == '\n';
63 }
64
strip(char * s)65 static char *strip(char *s)
66 {
67 int i;
68
69 /* Strip leading whitespace */
70 while (s[0] && is_space(s[0]))
71 s++;
72
73 /* Strip trailing whitespace */
74 i = strlen(s) - 1;
75 while (i >= 0 && is_space(s[i])) {
76 s[i] = 0;
77 i--;
78 }
79
80 return s;
81 }
82
parse_config(int * allowed,int * needs_root_rights)83 static void parse_config(int *allowed, int *needs_root_rights)
84 {
85 FILE *f;
86 char buf[1024];
87 char *stripped, *equals, *key, *value;
88 int line = 0;
89
90 f = fopen(CONFIG_FILE, "r");
91 if (!f)
92 return;
93
94 while (fgets(buf, sizeof(buf), f)) {
95 line++;
96
97 /* Skip comments and empty lines */
98 stripped = strip(buf);
99 if (stripped[0] == '#' || stripped[0] == 0)
100 continue;
101
102 /* Split in a key + value pair */
103 equals = strchr(stripped, '=');
104 if (!equals) {
105 fprintf(stderr, "%s: Syntax error at %s line %d\n", progname,
106 CONFIG_FILE, line);
107 exit(1);
108 }
109 *equals = 0;
110 key = strip(stripped); /* To remove trailing whitespace from key */
111 value = strip(equals + 1); /* To remove leading whitespace from val */
112 if (!key[0]) {
113 fprintf(stderr, "%s: Missing key at %s line %d\n", progname,
114 CONFIG_FILE, line);
115 exit(1);
116 }
117 if (!value[0]) {
118 fprintf(stderr, "%s: Missing value at %s line %d\n", progname,
119 CONFIG_FILE, line);
120 exit(1);
121 }
122
123 /* And finally process */
124 if (strcmp(key, "allowed_users") == 0) {
125 if (strcmp(value, "rootonly") == 0)
126 *allowed = ROOT_ONLY;
127 else if (strcmp(value, "console") == 0)
128 *allowed = CONSOLE_ONLY;
129 else if (strcmp(value, "anybody") == 0)
130 *allowed = ANYBODY;
131 else {
132 fprintf(stderr,
133 "%s: Invalid value '%s' for 'allowed_users' at %s line %d\n",
134 progname, value, CONFIG_FILE, line);
135 exit(1);
136 }
137 }
138 else if (strcmp(key, "needs_root_rights") == 0) {
139 if (strcmp(value, "yes") == 0)
140 *needs_root_rights = 1;
141 else if (strcmp(value, "no") == 0)
142 *needs_root_rights = 0;
143 else if (strcmp(value, "auto") == 0)
144 *needs_root_rights = -1;
145 else {
146 fprintf(stderr,
147 "%s: Invalid value '%s' for 'needs_root_rights' at %s line %d\n",
148 progname, value, CONFIG_FILE, line);
149 exit(1);
150 }
151 }
152 else if (strcmp(key, "nice_value") == 0) {
153 /* Backward compatibility with older Debian Xwrapper, ignore */
154 }
155 else {
156 fprintf(stderr, "%s: Invalid key '%s' at %s line %d\n", key,
157 progname, CONFIG_FILE, line);
158 exit(1);
159 }
160 }
161 fclose(f);
162 }
163
on_console(int fd)164 static int on_console(int fd)
165 {
166 #if defined(__linux__)
167 struct stat st;
168 int r;
169
170 r = fstat(fd, &st);
171 if (r == 0 && S_ISCHR(st.st_mode) && major(st.st_rdev) == 4)
172 return 1;
173 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
174 int idx;
175
176 if (ioctl(fd, VT_GETINDEX, &idx) != -1)
177 return 1;
178 #else
179 #warning This program needs porting to your kernel.
180 static int seen;
181
182 if (!seen) {
183 fprintf(stderr, "%s: Unable to determine if running on a console\n",
184 progname);
185 seen = 1;
186 }
187 #endif
188
189 return 0;
190 }
191
main(int argc,char * argv[])192 int main(int argc, char *argv[])
193 {
194 #ifdef WITH_LIBDRM
195 struct drm_mode_card_res res;
196 #endif
197 char buf[PATH_MAX];
198 int i, r, fd;
199 int kms_cards = 0;
200 int total_cards = 0;
201 int allowed = CONSOLE_ONLY;
202 int needs_root_rights = -1;
203 char *const empty_envp[1] = { NULL, };
204
205 progname = argv[0];
206
207 parse_config(&allowed, &needs_root_rights);
208
209 /* For non root users check if they are allowed to run the X server */
210 if (getuid() != 0) {
211 switch (allowed) {
212 case ROOT_ONLY:
213 /* Already checked above */
214 fprintf(stderr, "%s: Only root is allowed to run the X server\n", argv[0]);
215 exit(1);
216 break;
217 case CONSOLE_ONLY:
218 /* Some of stdin / stdout / stderr maybe redirected to a file */
219 for (i = STDIN_FILENO; i <= STDERR_FILENO; i++) {
220 if (on_console(i))
221 break;
222 }
223 if (i > STDERR_FILENO) {
224 fprintf(stderr, "%s: Only console users are allowed to run the X server\n", argv[0]);
225 exit(1);
226 }
227 break;
228 case ANYBODY:
229 break;
230 }
231 }
232
233 #ifdef WITH_LIBDRM
234 /* Detect if we need root rights, except when overriden by the config */
235 if (needs_root_rights == -1) {
236 for (i = 0; i < 16; i++) {
237 snprintf(buf, sizeof(buf), DRM_DEV_NAME, DRM_DIR_NAME, i);
238 fd = open(buf, O_RDWR);
239 if (fd == -1)
240 continue;
241
242 total_cards++;
243
244 memset(&res, 0, sizeof(struct drm_mode_card_res));
245 r = ioctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res);
246 if (r == 0)
247 kms_cards++;
248
249 close(fd);
250 }
251 }
252 #endif
253
254 /* If we've found cards, and all cards support kms, drop root rights */
255 if (needs_root_rights == 0 || (total_cards && kms_cards == total_cards)) {
256 gid_t realgid = getgid();
257 uid_t realuid = getuid();
258
259 if (setresgid(-1, realgid, realgid) != 0) {
260 fprintf(stderr, "%s: Could not drop setgid privileges: %s\n",
261 progname, strerror(errno));
262 exit(1);
263 }
264 if (setresuid(-1, realuid, realuid) != 0) {
265 fprintf(stderr, "%s: Could not drop setuid privileges: %s\n",
266 progname, strerror(errno));
267 exit(1);
268 }
269 }
270
271 snprintf(buf, sizeof(buf), "%s/Xorg", SUID_WRAPPER_DIR);
272
273 /* Check if the server is executable by our real uid */
274 if (access(buf, X_OK) != 0) {
275 fprintf(stderr, "%s: Missing execute permissions for %s: %s\n",
276 progname, buf, strerror(errno));
277 exit(1);
278 }
279
280 argv[0] = buf;
281 if (getuid() == geteuid())
282 (void) execv(argv[0], argv);
283 else
284 (void) execve(argv[0], argv, empty_envp);
285 fprintf(stderr, "%s: Failed to execute %s: %s\n",
286 progname, buf, strerror(errno));
287 exit(1);
288 }
289