Re: Xserver stack smashed -- wrapper

John Goerzen (jgoerzen@SOUTHWIND.NET)
Wed, 21 Jan 1998 12:01:27 -0600

A short time ago, there was some talk about various wrappers around
the X server, and I pointed out that Debian already has one better
than the example posted. Since then, I have received requests to post
Debian's wrapper source.

This comes from Debian's xfree86 source package. I do not know who
the original author was (I did not write this code), but a good guess
would be Mark W. Eichin <eichin@kitten.gen.ma.us>, Debian's X
maintainer.

I will include the code at the bottom of the message. First let me
include a sample of the config file that it looks for:

/etc/X11/Xserver:
-- cut --
/usr/X11R6/bin/XF86_S3V
Console

The first line in this file is the full pathname of the default X server.
The second line shows who is allowed to run the X server:
RootOnly
Console (anyone whose controlling tty is on the console)
Anybody
-- cut --

OK, here is the source to the wrapper itself. Just compile with gcc
-O2 or some such.

xserver-wrapper.c:
-- cut --
/* X.c - a simple wrapper for X servers that decides whether to let them be
* run.
*
* This program may be distributed under the terms of the GNU public license */

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>

#define VT_MAJOR_DEV 4

#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif

typedef enum {
RootOnly,
Console,
Anybody
} SecurityLevel;

static SecurityLevel getSecLevel(char *security)
{
char *c;

for (c=security; *c; c++) *c=toupper(*c);

if (strncmp(security,"ROOTONLY",8)==0) return RootOnly;
if (strncmp(security,"CONSOLE",7)==0) return Console;
if (strncmp(security,"ANYBODY",7)==0) return Anybody;
return RootOnly;
}

static int checkSecLevel(SecurityLevel level)
{
struct stat s;

switch (level) {
case RootOnly:
return FALSE;
break;
case Console:
if (fstat(0,&s)!=0) {
fprintf(stderr,"X: cannot stat stdin\n");
return FALSE;
}
if (S_ISCHR(s.st_mode) && ((s.st_rdev>>8)&0xff)==VT_MAJOR_DEV &&
(s.st_rdev&0xff)<128) {
return TRUE;
}
break;
case Anybody:
return TRUE;
}
return FALSE;
}

int main(int argc, char **argv)
{
FILE *cf;
char xserver[1024];
char security[80];
char *c;

if (!(cf=fopen("/etc/X11/Xserver","r"))) {
fprintf(stderr,"X: cannot find configuration file /etc/X11/Xserver\n"
"\nIf you want to run X clients locally you must install an\n"
"X server package (eg. xsvga, xs3, xp9000, etc.)\n"
"Installation of one of these packages should modify the\n"
"configuration file appropriately.\n");
exit(1);
}

if (!fgets(xserver,1024,cf)) {
fprintf(stderr,"X: cannot read name of X server from /etc/X11/Xserver\n");
exit(1);
}

/* Strip trailing newline from server name */
for (c=xserver; *c!=0; c++) if (*c=='\n') *c=0;

if (!fgets(security,80,cf)) {
fprintf(stderr,"X: cannot read security setting from /etc/X11/Xserver\n");
exit(1);
}

if (getuid()==0 || checkSecLevel(getSecLevel(security))) {
/* Run the X server */
int i;
for (i = 1; i < argc; i++) {
if (!strcmp(argv[i], "-config")) {
if (setuid(getuid())) {
perror("X couldn't drop setuid privileges for alternate config");
exit(1);
}
break;
}
}
execv(xserver,argv);
fprintf(stderr,"X: exec of %s failed\n",xserver);
exit(1);
} else {
if (argc == 2 && !strcmp(argv[1], "-showconfig")) {
if (setuid(getuid())) {
perror("X couldn't drop setuid privileges");
exit(1);
}
execv(xserver,argv);
fprintf(stderr,"X: unprivileged exec of %s failed\n",xserver);
exit(1);
} else {
fprintf(stderr,"X: you are not authorised to run the X server\n");
exit(1);
}
}

exit(1); /* We should never reach this */
}
-- cut --

--
John Goerzen
Southwind Internet Access, Inc,
Business e-mail: jgoerzen@southwind.net

Personal e-mail: jgoerzen@complete.org Wichita State University e-mail: jgoerzen@cs.twsu.edu Developer, Debian GNU/Linux <http://www.debian.org>