diff --git a/.cvsignore b/.cvsignore index a9b9051..b3235be 100644 --- a/.cvsignore +++ b/.cvsignore @@ -2,3 +2,5 @@ dgamelaunch tags cscope.out error.log +y.tab.* +lex.yy.* diff --git a/Makefile b/Makefile index abe6483..090e35d 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ CC = gcc LDFLAGS = CFLAGS = -g3 $(optimize) -Wall $(DEFS) DEFS = -DVERSION=\"$(VERSION)\" -SRCS = virus.c ttyrec.c dgamelaunch.c io.c ttyplay.c stripgfx.c strlcpy.c strlcat.c +SRCS = virus.c ttyrec.c dgamelaunch.c io.c ttyplay.c stripgfx.c strlcpy.c strlcat.c y.tab.o lex.yy.o OBJS = $(SRCS:.c=.o) LIBS = -lncurses -lcrypt -lutil @@ -30,6 +30,15 @@ indent: indent -nut -ts2 *.c *.h rm -f *~ +lex.yy.c: config.l + flex $< + +y.tab.c: config.y + bison -d -y $< + +lex.yy.o: lex.yy.c +y.tab.o: y.tab.c + dist: clean indent rm -rf $(NAME)-$(VERSION) (cd .. && ln -sf $(CURDIR) $(NAME)-$(VERSION)) diff --git a/config.l b/config.l new file mode 100644 index 0000000..1ffcbb6 --- /dev/null +++ b/config.l @@ -0,0 +1,105 @@ +/* Lexical analyzer for dgamelaunch's configuration file. */ + +%option nounput +%option noyywrap + +%{ + +#include +#include + +#include "y.tab.h" +#include "dgamelaunch.h" + +unsigned int line = 1, col = 0; +unsigned int comment_begin_line, comment_begin_col; + +static void ccomment(void); + +#define YY_USER_ACTION col += yyleng; + +%} + +VALUE \".*\" +MALSTRING \"[^\"\n]*\n +WHITE [\t ]* +COMMENT ^#.* +LONGCOMMENT "/*" + +%% /* BEGIN RULES SECTION */ + +{VALUE} { + yytext[yyleng - 1] = '\0'; /* Kill the trailing quote */ + yytext++; /* Kill the leading quote */ + yylval.s = strdup(yytext); + return TYPE_VALUE; +} + +{MALSTRING} { + /* yytext already contains a newline, no need for one here */ + fprintf(stderr, "%s: unterminated string constant at line %d, start column %d: %s\n", config, line, col - yyleng + 1, yytext); +} + +{WHITE} { } +{COMMENT} { } +{LONGCOMMENT} { + comment_begin_line = line; + comment_begin_col = col - 1; + ccomment(); +} + +"=" { return '='; } +"shed_user" { return TYPE_SUSER; } +"shed_group" { return TYPE_SGROUP; } +"shed_uid" { return TYPE_SUID; } +"shed_gid" { return TYPE_SGID; } +"maxusers" { return TYPE_MAX; } + +"chroot_path" { return TYPE_PATH_CHROOT; } +"nethack" { return TYPE_PATH_NETHACK; } +"dglroot" { return TYPE_PATH_DGLDIR; } +"spooldir" { return TYPE_PATH_SPOOL; } +"banner" { return TYPE_PATH_BANNER; } +"rc_template" { return TYPE_PATH_CANNED; } + +\n { line++; col = 0; } + +. { + fprintf(stderr, "%s: unrecognized token \"%s\" at line %d, column %d\n", config, yytext, line, col); +} + +%% + +/* Ripped from ircd-hybrid/src/ircd_lexer.l */ +void ccomment(void) +{ + int c; + + while (1) + { + while ((c = input()) != '*' && c != EOF) + { + if (c == '\n') + { + col = 0; + ++line; + } + else + ++col; + } + + if (c == '*') + { + ++col; + while ((c = input()) == '*') ++col; + if (c == '/') { ++col; break; } + } + if (c == EOF) + { + fprintf(stderr, "%s: encountered end-of-file in comment starting on line %d, column %d\n", config, col, comment_begin_col); + exit(1); + + break; + } + } +} diff --git a/config.y b/config.y new file mode 100644 index 0000000..80f5ee7 --- /dev/null +++ b/config.y @@ -0,0 +1,171 @@ +%{ + +#include +#include +#include +#include +#include + +#include "dgamelaunch.h" + +extern int yylex(void); +extern void yyerror(const char*); +extern char *yytext; +extern unsigned int line, col; + +static const char* lookup_token (int t); + +%} + +%union { + char* s; + int kt; + unsigned long i; +} + +%token TYPE_SUSER TYPE_SGROUP TYPE_SGID TYPE_SUID TYPE_MAX +%token TYPE_PATH_NETHACK TYPE_PATH_DGLDIR TYPE_PATH_SPOOL +%token TYPE_PATH_BANNER TYPE_PATH_CANNED TYPE_PATH_CHROOT +%token TYPE_VALUE +%token TYPE_NUMBER +%type KeyType + +%% + +Configuration: KeyPairs + | { fprintf(stderr, "%s: no settings, proceeding with defaults\n", config); } + ; + +KeyPairs: KeyPairs KeyPair + | KeyPair + ; + +KeyPair: KeyType '=' TYPE_VALUE { + struct group* gr; + struct passwd* usr; + + if (!myconfig) + myconfig = calloc(1, sizeof(struct dg_config)); + + switch ($1) + { + case TYPE_SGROUP: + if ((gr = getgrnam($3)) != NULL) + myconfig->shed_gid = gr->gr_gid; + else + fprintf(stderr, "%s: no such group '%s'\n", config, $3); + + break; + case TYPE_SUSER: + if ((usr = getpwnam($3)) != NULL) + myconfig->shed_uid = usr->pw_uid; + else + fprintf(stderr, "%s: no such group '%s'\n", config, $3); + break; + + case TYPE_PATH_CHROOT: + if (myconfig->chroot) free(myconfig->chroot); + myconfig->chroot = strdup ($3); + break; + + case TYPE_PATH_NETHACK: + if (myconfig->nethack) free(myconfig->nethack); + myconfig->nethack = strdup ($3); + break; + + case TYPE_PATH_DGLDIR: + if (myconfig->dglroot) free(myconfig->dglroot); + myconfig->dglroot = strdup ($3); + break; + + case TYPE_PATH_BANNER: + if (myconfig->banner) free(myconfig->banner); + myconfig->banner = strdup($3); + break; + + case TYPE_PATH_CANNED: + if (myconfig->rcfile) free(myconfig->rcfile); + myconfig->rcfile = strdup($3); + break; + + case TYPE_PATH_SPOOL: + if (myconfig->spool) free (myconfig->spool); + myconfig->spool = strdup($3); + break; + + default: + fprintf(stderr, "%s: token %s does not take a string, bailing out\n", + config, lookup_token($1)); + exit(1); + + } + + free($3); +} + | KeyType '=' TYPE_NUMBER { + switch ($1) + { + case TYPE_SUID: + if (getpwuid($3) != NULL) + myconfig->shed_uid = $3; + else + fprintf(stderr, "%s: no such uid %lu\n", config, $3); + + break; + + case TYPE_SGID: + if (getgrgid($3) != NULL) + myconfig->shed_gid = $3; + else + fprintf(stderr, "%s: no such gid %lu\n", config, $3); + break; + + case TYPE_MAX: + myconfig->max = $3; + break; + + default: + fprintf(stderr, "%s: token %s does not take a number, bailing out\n", + config, lookup_token($1)); + exit(1); + } +}; + +KeyType : TYPE_SUSER { $$ = TYPE_SUSER; } + | TYPE_SGROUP { $$ = TYPE_SGROUP; } + | TYPE_SUID { $$ = TYPE_SUID; } + | TYPE_SGID { $$ = TYPE_SGID; } + | TYPE_MAX { $$ = TYPE_MAX; } + | TYPE_PATH_CHROOT { $$ = TYPE_PATH_CHROOT; } + | TYPE_PATH_NETHACK { $$ = TYPE_PATH_NETHACK; } + | TYPE_PATH_DGLDIR { $$ = TYPE_PATH_DGLDIR; } + | TYPE_PATH_SPOOL { $$ = TYPE_PATH_SPOOL; } + | TYPE_PATH_BANNER { $$ = TYPE_PATH_BANNER; } + | TYPE_PATH_CANNED { $$ = TYPE_PATH_CANNED; } + ; + +%% + +const char* lookup_token (int t) +{ + switch (t) + { + case TYPE_SUSER: return "shed_user"; + case TYPE_SGROUP: return "shed_group"; + case TYPE_SUID: return "shed_uid"; + case TYPE_SGID: return "shed_gid"; + case TYPE_MAX: return "maxusers"; + case TYPE_PATH_CHROOT: return "chroot_path"; + case TYPE_PATH_NETHACK: return "nethack"; + case TYPE_PATH_DGLDIR: return "dglroot"; + case TYPE_PATH_SPOOL: return "spooldir"; + case TYPE_PATH_BANNER: return "banner"; + case TYPE_PATH_CANNED: return "rc_template"; + default: abort(); + } +} + +void yyerror(char const* s) +{ + fprintf(stderr, "%s: couldn't parse \"%s\" at line %d, column %d: %s\n", config, yytext, line, col, s); +} diff --git a/dgamelaunch.c b/dgamelaunch.c index 897aa74..9ec3313 100644 --- a/dgamelaunch.c +++ b/dgamelaunch.c @@ -68,6 +68,7 @@ # define ARRAY_SIZE(x) sizeof(x) / sizeof(x[0]) #endif +#include #include #include #include @@ -82,6 +83,10 @@ #include #include +#include "y.tab.h" +extern FILE* yyin; +extern int yyparse (); + extern int vi_main (int argc, char **argv); extern int ttyplay_main (char *ttyfile, int mode, int rstripgfx); extern int ttyrec_main (char *); @@ -92,6 +97,21 @@ extern struct winsize win; /* global variables */ +struct dg_config *myconfig = NULL; +char* config = NULL; + +struct dg_config defconfig = { + "/var/lib/dgamelaunch/", + "/bin/nethack", + "/dgldir/", + "/dgl-banner", + "/dgl-default-rcfile", + "/var/mail/", + "games", "games", + 5, 60, /* games:games in Debian */ + 64000 +}; + int pid_game = 0; int loggedin = 0; char rcfilename[80]; @@ -103,9 +123,57 @@ struct dg_user **users = NULL; struct dg_user *me = NULL; struct dg_banner banner; +void +create_config () +{ + FILE *config_file = NULL; + + if (config) + { + if ((config_file = fopen(config, "r")) != NULL) + { + yyin = config_file; + yyparse(); + fclose(config_file); + free (config); + } + + /* Fill the rest with defaults */ + if (!myconfig->shed_user && myconfig->shed_uid == 0) + { + struct passwd *pw; + if ((pw = getpwnam(defconfig.shed_user))) + myconfig->shed_uid = pw->pw_uid; + else + myconfig->shed_uid = defconfig.shed_uid; + } + + if (!myconfig->shed_group && myconfig->shed_gid == 0) + { + struct group *gr; + if ((gr = getgrnam(defconfig.shed_group))) + myconfig->shed_gid = gr->gr_gid; + else + myconfig->shed_gid = defconfig.shed_gid; + } + + if (myconfig->max == 0) myconfig->max = defconfig.max; + if (!myconfig->chroot) myconfig->chroot = strdup(defconfig.chroot); + if (!myconfig->nethack) myconfig->nethack = strdup(defconfig.nethack); + if (!myconfig->dglroot) myconfig->dglroot = strdup(defconfig.dglroot); + if (!myconfig->rcfile) myconfig->rcfile = strdup(defconfig.rcfile); + if (!myconfig->spool) myconfig->spool = strdup(defconfig.spool); + } + else + { + myconfig = &defconfig; + } +} + /* ************************************************************* */ /* for ttyrec */ + void ttyrec_getmaster () { @@ -623,7 +691,10 @@ drawmenu () /* for retarded clients */ flood++; if (flood >= 20) + { + endwin(); graceful_exit (119); + } } /* ************************************************************* */ @@ -791,7 +862,10 @@ newuser () error = 1; if (strlen (buf) == 0) + { + free(me); return; + } } me->username = strdup (buf); @@ -802,6 +876,7 @@ newuser () if (!changepw ()) /* Calling changepw instead to prompt twice. */ { + free(me->username); free(me); me = NULL; return; @@ -818,7 +893,7 @@ newuser () "This is sent _nowhere_ but will be used if you ask the sysadmin for lost"); mvaddstr (7, 1, "password help. Please use a correct one. It only benefits you."); - mvaddstr (8, 1, "80 character max. No ':' characters."); + mvaddstr (8, 1, "80 character max. No ':' characters. Blank line aborts."); mvaddstr (10, 1, "=> "); refresh (); @@ -827,6 +902,15 @@ newuser () if (strchr (buf, ':') != NULL) graceful_exit (113); + if (buf && *buf == '\0') + { + free (me->username); + free (me->password); + free (me); + me = NULL; + return; + } + me->email = strdup (buf); me->env = calloc (1, 1); diff --git a/dgamelaunch.conf b/dgamelaunch.conf new file mode 100644 index 0000000..dfed64b --- /dev/null +++ b/dgamelaunch.conf @@ -0,0 +1,41 @@ +/* This is a sample dgamelaunch configuration file. Comments like this as + well as bash-style comments are allowed in this configuration file. Each + configuration option will be explained along with its default value. */ + +# shed_user: username to shed privileges to +shed_user = "games" + +# shed_group: group name to shed privileges to +shed_group = "games" + +# Alternatively, you may use the respective gids/uids. This is for Debian: +shed_uid = 5 +shed_gid = 60 + +# Note that shed_user and shed_group will always take precedence over +# shed_uid and shed_gid. + +# Max amount of registered users to allow. +maxusers = 64000 + +# Path to a prepared chroot jail. +chroot_path = "/var/lib/dgamelaunch/" + +# From inside the jail, the location of the nethack binary. +nethack = "/bin/nethack" + +# From inside the jail, dgamelaunch's working directory for rcfiles/ttyrec/etc +dglroot = "/dgldir/" + +# From inside the jail, where dgamelaunch should put mail - should match up with +# NetHack settings. +spooldir = "/var/mail/" + +# From inside the jail, location of a banner file that contains no more than +# 14 lines of 80-column width text. Any more will be truncated. +banner = "/dgl-banner" + +# From inside the jail, the default .nethackrc that is copied for new users. +rc_template = "/dgl-default-rcfile" + + diff --git a/dgamelaunch.h b/dgamelaunch.h index b1eac56..60a0b4b 100644 --- a/dgamelaunch.h +++ b/dgamelaunch.h @@ -34,6 +34,25 @@ struct dg_game time_t idle_time; }; +struct dg_config +{ + char* chroot; + char* nethack; + char* dglroot; + char* banner; + char* rcfile; + char* spool; + char* shed_user; + char* shed_group; + uid_t shed_uid; + gid_t shed_gid; + unsigned long max; +}; + +extern char* config; /* file path */ +extern struct dg_config *myconfig; +extern struct dg_config defconfig; + #define SHED_UID 5 /* the uid to shed privs to */ #define SHED_GID 60 /* the gid to shed privs to */ #define MAXUSERS 64000 /* solves some preallocation issues. */ @@ -49,6 +68,7 @@ struct dg_game #define LOC_BANNER "/dgl-banner" /* dgamelaunch.c function prototypes */ +extern void create_config (void); extern void ttyrec_getmaster (void); extern void gen_ttyrec_filename (void); extern void gen_inprogress_lock (pid_t pid);