Advertisement
Lekensteyn

xserver-wrapper.c

Jan 2nd, 2012
202
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 13.55 KB | None | 0 0
  1. /* xserver-wrapper.c - a simple wrapper for X servers that decides whether to
  2.  * let them be run.
  3.  *
  4.  * By Stephen Early
  5.  *
  6.  * Stephen Early: modified to use /etc/X11/X symlink with security level of
  7.  *                'Console' if /etc/X11/Xserver does not exist
  8.  * Mark W. Eichin: permit non-privileged -showconfig (6 May 1997)
  9.  * Mark W. Eichin: fix sense of error check for -showconfig (11 May 1997)
  10.  * Mark W. Eichin: drop privileges on alternate -config, even if we do pass the
  11.  *                 security check, to prevent using the error handling to read
  12.  *                 the first line of any protected file (19 Sep 1997)
  13.  * Erik Troan: prevent buffer overruns (25 Mar 1998)
  14.  * Topi Miettinen: plug file descriptor leak (26 Apr 1998)
  15.  * Branden Robinson: only fclose() if file was opened (3 May 1999)
  16.  * Colin Phipps: minor device number check should be < 64, not < 128, or we
  17.  *               catch serial terminals (26 Feb 2000)
  18.  * Branden Robinson: ensure sanity of X server socket directory (13 Jun 2000)
  19.  * Branden Robinson: make all paths #defines
  20.  *                   more helpful socket dir error messages (29 Jun 2000)
  21.  * Branden Robinson: bail out if the config file contains only the silly
  22.  *                   default X server name (XF86_NONE) (30 Jul 2000)
  23.  * Branden Robinson: increase verbosity when wrapper config not found
  24.  *                   (2 Oct 2000)
  25.  * Branden Robinson:
  26.  *   - new configuration file, Xwrapper.config, with different format
  27.  *     (name=value)
  28.  *   - now just exec's /etc/X11/X; whatever this symlink points
  29.  *     to will be used as the X server
  30.  *   - config file specifies allowed user types as before (root only,
  31.  *     console users, anyone)
  32.  *   - config file specifies nice value to use for server
  33.  *   (17 Nov 2000)
  34.  * Branden Robinson: now accepts hyphens in variable contents (24 Nov 2000)
  35.  * Branden Robinson: fix dumb errors left over from debugging (3 Dec 2000)
  36.  * Branden Robinson: let root start the server even if he isn't on a
  37.  *                    console, and the security level is console (11 Dec 2000)
  38.  * Branden Robinson: check out the X server symlink with readlink; abort if
  39.  *                   it's not a symlink, or if it points back to this wrapper
  40.  *                   (24 Feb 2001)
  41.  * Branden Robinson: whoops; readlink() doesn't null-terminate the target
  42.  *                   string (27 Feb 20001)
  43.  * Branden Robinson: add more info to "suspicious" error messages (16 Mar 2001)
  44.  * Branden Robinson: also allow unprivileged use of "-version" option
  45.  *                   (13 Jul 2001)
  46.  * Branden Robinson: check mode of DRI device directory, if it exists, and warn
  47.  *                   if it is weird (28 Aug 2001)
  48.  * Branden Robinson: skip lines in Xwrapper.config that don't match expected
  49.  *                   format (9 Dec 2001)
  50.  * Branden Robinson: fix logic that was supposed to also allow unprivileged use
  51.  *                   of "-version" option but which actually forbade both
  52.  *                   "-showconfig" and "-verbose"; also let unprivileged users
  53.  *                   specify "-help" option to get a usage message (26 Dec 2001)
  54.  * Branden Robinson: change nice() usage to fit SuSv2 semantics; see Debian Bug
  55.  *                   #140012 (2 Apr 2002)
  56.  * Branden Robinson: *sigh* Ben Collins changed our FROZEN C library back to
  57.  *                   pre-SuSv2 nice() semantics, so rewrote the nice() error
  58.  *                   handling; also correct limits on legal nice values from
  59.  *                   -20 <= x <= 20 to -20 <= x <= 19 (29 Apr 2002)
  60.  * Branden Robinson: make the nice() error handling switchable with a #define
  61.  *                   between SuSv2 semantics and old-style semantics
  62.  *                   (16 Oct 2002)
  63.  * Branden Robinson: stop using the GNU extension strnlen() to appease the
  64.  *                   Debian GNU/NetBSD geeks, who are using BSD's C library
  65.  *                   (16 Oct 2002)
  66.  * Branden Robinson: chdir() to the directory where the X server symlink is kept
  67.  *                   before executing its target, so that relative symlinks work
  68.  *                   (1 Aug 2003)
  69.  * Guillem Jover: add console detection support for GNU/kFreeBSD, and some
  70.  *                messages at build and run time to allow the user to know
  71.  *                what failed on unsupported systems
  72.  *                (30 Mar 2007)
  73.  * Brice Goglin: drop privileges on alternate config file given with
  74.  *               -xf86config (14 Jun 2007)
  75.  * Tollef Fog Heen: stop handling -config specifically, since newer
  76.  *                  Xorg does that check automatically and only allows
  77.  *                  non-root users to specify configuration files
  78.  *                  relative to /etc/X11 (10 Aug 2007)
  79.  * Loïc Minier: on Linux, also consider alternate tty devices (major 5 and
  80.  *              minor < 64) as consoles (24 Sep 2008)
  81.  * Julien Cristau: remove the nice_value option
  82.  * Julien Cristau: recognize /usr/bin/X as a path to this wrapper (6 Jun 2009)
  83.  * Julien Cristau: don't print an error message if Xwrapper.config doesn't exist
  84.  *                 (11 Aug 2009)
  85.  * Julien Cristau: allow unprivileged -showDefaultModulePath and
  86.  *                 -showDefaultLibPath options (11 Aug 2009)
  87.  * Julien Cristau: don't check the mode of the DRI device directory
  88.  *                 (11 Aug 2009)
  89.  *
  90.  * This is free software; you may redistribute it and/or modify
  91.  * it under the terms of the GNU General Public License as
  92.  * published by the Free Software Foundation; either version 2,
  93.  * or (at your option) any later version.
  94.  *
  95.  * This is distributed in the hope that it will be useful, but
  96.  * WITHOUT ANY WARRANTY; without even the implied warranty of
  97.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  98.  * GNU General Public License for more details.
  99.  *
  100.  * You should have received a copy of the GNU General Public License with
  101.  * the Debian operating system, in /usr/share/common-licenses/GPL;  if
  102.  * not, write to the Free Software Foundation, Inc., 59 Temple Place,
  103.  * Suite 330, Boston, MA 02111-1307 USA
  104.  *
  105.  */
  106.  
  107. #include <ctype.h>
  108. #include <errno.h>
  109. #include <fcntl.h>
  110. #include <stdio.h>
  111. #include <stdlib.h>
  112. #include <string.h>
  113. #include <unistd.h>
  114. #include <sys/stat.h>
  115. #include <sys/types.h>
  116.  
  117. #if defined(__linux__)
  118. #define TTY_MAJOR_DEV 4
  119. #define ALT_TTY_MAJOR_DEV 5
  120. #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
  121. #include <sys/consio.h>
  122. #endif
  123.  
  124. #define X_WRAPPER_CONFIG_FILE "/etc/X11/Xwrapper.config"
  125. #define X_SERVER_SYMLINK_DIR "/etc/X11"
  126. #define X_SERVER_SYMLINK "/etc/X11/X"
  127. #define X_SOCKET_DIR "/tmp/.X11-unix"
  128. #define X_SOCKET_DIR_MODE (S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
  129.  
  130. #ifndef FALSE
  131. #define FALSE 0
  132. #endif
  133. #ifndef TRUE
  134. #define TRUE 1
  135. #endif
  136.  
  137. typedef enum {
  138.   RootOnly,
  139.   Console,
  140.   Anybody
  141. } SecurityLevel;
  142.  
  143. static SecurityLevel
  144. getSecLevel(char *security)
  145. {
  146.   char *c;
  147.  
  148.   for (c = security; *c; c++) *c = toupper(*c);
  149.  
  150.   if (strncmp(security,"ROOTONLY",8) == 0) return RootOnly;
  151.   if (strncmp(security,"CONSOLE",7) == 0) return Console;
  152.   if (strncmp(security,"ANYBODY",7) == 0) return Anybody;
  153.   return RootOnly;
  154. }
  155.  
  156. static int
  157. onConsole()
  158. {
  159. #if defined(__linux__)
  160.   struct stat s;
  161.  
  162.   /* see if stdin is a virtual console device */
  163.   if (fstat(0, &s) != 0) {
  164.     (void) fprintf(stderr, "X: cannot stat stdin\n");
  165.     return FALSE;
  166.   }
  167.   if (S_ISCHR(s.st_mode) &&
  168.         ((((s.st_rdev >> 8) & 0xff) == TTY_MAJOR_DEV &&
  169.           (s.st_rdev & 0xff) < 64) ||
  170.         (((s.st_rdev >> 8) & 0xff) == ALT_TTY_MAJOR_DEV &&
  171.           (s.st_rdev & 0xff) < 64)
  172.         )) {
  173.     return TRUE;
  174.   }
  175. #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
  176.   int idx;
  177.  
  178.   if (ioctl(0, VT_GETINDEX, &idx) != -1)
  179.     return TRUE;
  180. #else
  181. #warning This program needs porting to your kernel.
  182.   (void) fprintf(stderr, "X: unable to determine if running on a console\n");
  183. #endif
  184.  
  185.   return FALSE;
  186. }
  187.  
  188. static int
  189. checkSecLevel(SecurityLevel level)
  190. {
  191.   switch (level) {
  192.   case RootOnly:
  193.     if (getuid() == 0) { /* real uid is root */
  194.       return TRUE;
  195.     } else {
  196.       return FALSE;
  197.     }
  198.     break;
  199.   case Console:
  200.     if (getuid() == 0) return TRUE; /* root */
  201.     return onConsole();
  202.     break;
  203.   case Anybody:
  204.     return TRUE;
  205.   }
  206.   return FALSE;
  207. }
  208.  
  209. int
  210. main(int argc, char **argv)
  211. {
  212.   FILE *cf;
  213.   struct stat statbuf;
  214.   char xserver[1025];
  215.   char line[1024];
  216.   char var[65];
  217.   char value[257];
  218.   int length;
  219.   int i;
  220.   char *val;
  221.   mode_t mask;
  222.   SecurityLevel level = RootOnly;
  223.  
  224.   /* attempt to use our config file */
  225.   cf = fopen(X_WRAPPER_CONFIG_FILE, "r");
  226.  
  227.   if (cf) {
  228.     /* parse it */
  229.  
  230.     val = fgets(line, 1024, cf);
  231.  
  232.     while (val != NULL) {
  233.       var[0] = '\0';
  234.       value[0] = '\0';
  235.       if (sscanf(line, " %64[A-Za-z0-9_] = %256[A-Za-z0-9_ -] ",
  236.                  var, value) > 0) {
  237.         /* truncate extra spaces at end of value */
  238.         length = strlen(value);
  239.         if (length > 256) {
  240.           length = 256;
  241.         }
  242.         for (i = (length - 1); (value[i] == ' '); i--) {
  243.           value[i] = '\0';
  244.         }
  245.         /* DEBUG (void) fprintf(stderr, "var: %s, value: %s.\n", var, value); */
  246.         if (strncasecmp(var, "allowed_users", 64) == 0) {
  247.           level = getSecLevel(value);
  248.           /* DEBUG (void) fprintf(stderr, "security level set to %d\n", level); */
  249.         }
  250.       }
  251.       val = fgets(line, 1024, cf);
  252.     }
  253.  
  254.     (void) fclose(cf);
  255.   } else {
  256.     /* DEBUG (void) fprintf(stderr, "X: unable to open wrapper config file %s\n",
  257.                    X_WRAPPER_CONFIG_FILE); */
  258.   }
  259.  
  260.   if (lstat(X_SERVER_SYMLINK, &statbuf)) {
  261.     (void) fprintf(stderr, "X: cannot stat %s (%s), aborting.\n",
  262.                    X_SERVER_SYMLINK, strerror(errno));
  263.     exit(1);
  264.   }
  265.  
  266.   i = readlink(X_SERVER_SYMLINK, xserver, 1024);
  267.  
  268.   if (i < 0) {
  269.     (void) fprintf(stderr, "X: cannot read %s symbolic link (%s), aborting.\n",
  270.                    X_SERVER_SYMLINK, strerror(errno));
  271.     exit(1);
  272.   }
  273.  
  274.   xserver[i] = '\0'; /* readlink() does not null-terminate the string */
  275.  
  276.   if ((strcmp(xserver, "/usr/bin/X11/X") == 0) ||
  277.       (strcmp(xserver, "/usr/X11R6/bin/X") == 0) ||
  278.       (strcmp(xserver, "/usr/bin/X") == 0)) {
  279.     (void) fprintf(stderr, "X: %s points back to X wrapper executable, "
  280.                    "aborting.\n", X_SERVER_SYMLINK);
  281.     exit(1);
  282.   }
  283.  
  284.   if (access(X_SERVER_SYMLINK, X_OK)) { /* access() uses real uid */
  285.     (void) fprintf(stderr, "%s is not executable\n", X_SERVER_SYMLINK);
  286.     exit(1);
  287.   }
  288.  
  289.   /* do we have permission to run the X server? */
  290.   if (checkSecLevel(level)) {
  291.     /* check for a sane server socket dir */
  292.     mask = umask(0);
  293.     /* some stupid kernels can't set the sticky bit during a mkdir() */
  294.     if (!(mkdir(X_SOCKET_DIR, X_SOCKET_DIR_MODE))) {
  295.       (void) chmod(X_SOCKET_DIR, X_SOCKET_DIR_MODE);
  296.     }
  297.     (void) umask(mask);
  298.  
  299.     /* do paranoid checks on the directory where the X server creates its socket */
  300.     if (lstat(X_SOCKET_DIR, &statbuf)) {
  301.       (void) fprintf(stderr, "X: cannot stat %s (%s), aborting.\n",
  302.                      X_SOCKET_DIR, strerror(errno));
  303.       exit(1);
  304.     }
  305.  
  306.     if ((statbuf.st_uid != 0) || (statbuf.st_gid != 0)) {
  307.       (void) fprintf(stderr, "X: %s has suspicious ownership (not root:root), "
  308.                      "aborting.\n", X_SOCKET_DIR);
  309.       exit(1);
  310.     }
  311.  
  312.     if (statbuf.st_mode != (S_IFDIR | X_SOCKET_DIR_MODE)) {
  313.       (void) fprintf(stderr, "X: %s has suspicious mode (not %o) or is not a "
  314.                      "directory, aborting.\n", X_SOCKET_DIR, X_SOCKET_DIR_MODE);
  315.       exit(1);
  316.     }
  317.  
  318.     for (i = 1; i < argc; i++) {
  319.       if (strlen(argv[i]) > 256) {
  320.         if (setuid(getuid())) {
  321.           perror("X unable to drop setuid privileges for suspiciously long "
  322.                  "argument");
  323.           exit(1);
  324.         }
  325.       }
  326.     }
  327.  
  328.     /* run the X server */
  329.     seteuid(0);
  330.  
  331.     /* DEBUG exit(0); */
  332.  
  333.     /*
  334.      * change to the directory where the X server symlink is so that a relative
  335.      * symlink will work and execute the X server
  336.      */
  337.     if (chdir(X_SERVER_SYMLINK_DIR)) {
  338.       (void) fprintf(stderr, "X: cannot chdir() to %s (%s), aborting.\n",
  339.                      X_SERVER_SYMLINK_DIR, strerror(errno));
  340.       exit(1);
  341.     }
  342.     (void) execv(xserver, argv);
  343.     (void) fprintf(stderr, "X: exec of %s failed\n", xserver);
  344.     exit(1);
  345.  
  346.   } else {
  347.       /* DEBUG fprintf(stderr, "argc = %d, argv[1] = \"%s\"\n", argc, argv[1]); */
  348.       /* DEBUG fprintf(stderr, "strcmp(argv[1], \"-showconfig\") = %d, strcmp(argv[1],
  349.         \"-version\" = %d\n", (strcmp(argv[1], "-showconfig")), (strcmp(argv[1],
  350.         "-version"))); */
  351.       if (argc == 2 && ( (strcmp(argv[1], "-help") == 0) ||
  352.                          (strcmp(argv[1], "-showconfig") == 0) ||
  353.                          (strcmp(argv[1], "-version") == 0) ||
  354.                          (strcmp(argv[1], "-showDefaultModulePath") == 0) ||
  355.                          (strcmp(argv[1], "-showDefaultLibPath") == 0) ) ) {
  356.           if (setuid(getuid())) {
  357.               perror("X unable to drop setuid privileges");
  358.               exit(1);
  359.           }
  360.           execv(xserver,argv);
  361.           (void) fprintf(stderr, "X: unprivileged exec of %s failed, "
  362.                          "aborting.\n", xserver);
  363.           exit(1);
  364.       } else {
  365.           (void) fprintf(stderr, "X: user not authorized to run the X "
  366.                          "server, aborting.\n");
  367.           exit(1);
  368.       }
  369.   }
  370.  
  371.   (void) fprintf(stderr, "X: Impossible!  Unreachable statement reached!\n");
  372.   exit(1);
  373. }
  374.  
  375. /*
  376.  * vim:set cindent et fo=tcroq sts=2 sw=2 tw=80:
  377.  */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement