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 55static const char *progname; 56 57enum { ROOT_ONLY, CONSOLE_ONLY, ANYBODY }; 58 59/* KISS non locale / LANG parsing isspace version */ 60static int is_space(char c) 61{ 62 return c == ' ' || c == '\t' || c == '\n'; 63} 64 65static 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 83static 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 164static 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 192int 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 overridden 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