diff options
Diffstat (limited to 'kdm/kfrontend/kdm_config.c')
-rw-r--r-- | kdm/kfrontend/kdm_config.c | 1470 |
1 files changed, 1470 insertions, 0 deletions
diff --git a/kdm/kfrontend/kdm_config.c b/kdm/kfrontend/kdm_config.c new file mode 100644 index 000000000..5d188e33d --- /dev/null +++ b/kdm/kfrontend/kdm_config.c @@ -0,0 +1,1470 @@ +/* + +Read options from kdmrc + +Copyright (C) 2001-2005 Oswald Buddenhagen <ossi@kde.org> + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#include <config.h> + +#include <stdio.h> +#include <unistd.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <ctype.h> +#include <errno.h> +#include <netdb.h> +#include <netinet/in.h> +#include <grp.h> +#ifdef _POSIX_PRIORITY_SCHEDULING +# include <sched.h> +#endif + +#include <X11/X.h> +#ifdef FamilyInternet6 +# define IPv6 +#endif + +#include <greet.h> +#include <config.ci> + +/* + * Section/Entry definition structs + */ + +typedef struct Ent { + const char *name; + int id; + void *ptr; + const char *def; +} Ent; + +typedef struct Sect { + const char *name; + Ent *ents; + int numents; +} Sect; + +/* + * Parsed ini file structs + */ + +typedef struct Entry { + struct Entry *next; + const char *val; + Ent *ent; + int vallen; + int line; +} Entry; + +typedef struct Section { + struct Section *next; + Entry *entries; + Sect *sect; + const char *name, *dname, *dhost, *dnum, *dclass; + int nlen, dlen, dhostl, dnuml, dclassl; +} Section; + + +/* + * Split up display-name/-class for fast comparison + */ +typedef struct DSpec { + const char *dhost, *dnum, *dclass; + int dhostl, dnuml, dclassl; +} DSpec; + + +/* + * Config value storage structures + */ + +typedef struct Value { + const char *ptr; + int len; +} Value; + +typedef struct Val { + Value val; + int id; +} Val; + +typedef struct ValArr { + Val *ents; + int nents, esiz, nchars, nptrs; +} ValArr; + + +static void *Malloc( size_t size ); +static void *Realloc( void *ptr, size_t size ); + +#define PRINT_QUOTES +#define LOG_NAME "kdm_config" +#define LOG_DEBUG_MASK DEBUG_CONFIG +#define LOG_PANIC_EXIT 1 +#define STATIC static +#include <printf.c> + + +static void * +Malloc( size_t size ) +{ + void *ret; + + if (!(ret = malloc( size ))) + LogOutOfMem(); + return ret; +} + +static void * +Realloc( void *ptr, size_t size ) +{ + void *ret; + + if (!(ret = realloc( ptr, size )) && size) + LogOutOfMem(); + return ret; +} + + +static void +MkDSpec( DSpec *spec, const char *dname, const char *dclass ) +{ + spec->dhost = dname; + for (spec->dhostl = 0; dname[spec->dhostl] != ':'; spec->dhostl++); + spec->dnum = dname + spec->dhostl + 1; + spec->dnuml = strlen( spec->dnum ); + spec->dclass = dclass; + spec->dclassl = strlen( dclass ); +} + + +static int rfd, wfd; + +static int +Reader( void *buf, int count ) +{ + int ret, rlen; + + for (rlen = 0; rlen < count; ) { + dord: + ret = read( rfd, (void *)((char *)buf + rlen), count - rlen ); + if (ret < 0) { + if (errno == EINTR) + goto dord; + if (errno == EAGAIN) + break; + return -1; + } + if (!ret) + break; + rlen += ret; + } + return rlen; +} + +static void +GRead( void *buf, int count ) +{ + if (Reader( buf, count ) != count) + LogPanic( "Can't read from core\n" ); +} + +static void +GWrite( const void *buf, int count ) +{ + if (write( wfd, buf, count ) != count) + LogPanic( "Can't write to core\n" ); +#ifdef _POSIX_PRIORITY_SCHEDULING + if ((debugLevel & DEBUG_HLPCON)) + sched_yield(); +#endif +} + +static void +GSendInt( int val ) +{ + GWrite( &val, sizeof(val) ); +} + +static void +GSendStr( const char *buf ) +{ + if (buf) { + int len = strlen( buf ) + 1; + GWrite( &len, sizeof(len) ); + GWrite( buf, len ); + } else + GWrite( &buf, sizeof(int)); +} + +static void +GSendNStr( const char *buf, int len ) +{ + int tlen = len + 1; + GWrite( &tlen, sizeof(tlen) ); + GWrite( buf, len ); + GWrite( "", 1 ); +} + +#ifdef XDMCP +static void +GSendArr( int len, const char *data ) +{ + GWrite( &len, sizeof(len) ); + GWrite( data, len ); +} +#endif + +static int +GRecvCmd( int *val ) +{ + if (Reader( val, sizeof(*val) ) != sizeof(*val)) + return 0; + return 1; +} + +static int +GRecvInt() +{ + int val; + + GRead( &val, sizeof(val) ); + return val; +} + +static char * +GRecvStr() +{ + int len; + char *buf; + + len = GRecvInt(); + if (!len) + return 0; + if (!(buf = malloc( len ))) + LogPanic( "No memory for read buffer" ); + GRead( buf, len ); + return buf; +} + + +/* #define WANT_CLOSE 1 */ + +typedef struct File { + char *buf, *eof, *cur; +#if defined(HAVE_MMAP) && defined(WANT_CLOSE) + int ismapped; +#endif +} File; + +static int +readFile( File *file, const char *fn, const char *what ) +{ + int fd; + off_t flen; + + if ((fd = open( fn, O_RDONLY )) < 0) { + LogInfo( "Cannot open %s file %s\n", what, fn ); + return 0; + } + + flen = lseek( fd, 0, SEEK_END ); +#ifdef HAVE_MMAP +# ifdef WANT_CLOSE + file->ismapped = 0; +# endif + file->buf = mmap( 0, flen + 1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0 ); +# ifdef WANT_CLOSE + if (file->buf) + file->ismapped = 1; + else +# else + if (!file->buf) +# endif +#endif + { + if (!(file->buf = Malloc( flen + 1 ))) { + close( fd ); + return 0; + } + lseek( fd, 0, SEEK_SET ); + if (read( fd, file->buf, flen ) != flen) { + free( file->buf ); + LogError( "Cannot read %s file %s\n", what, fn ); + close( fd ); + return 0; + } + } + file->eof = (file->cur = file->buf) + flen; + close( fd ); + return 1; +} + +#ifdef WANT_CLOSE +static void +freeBuf( File *file ) +{ +# ifdef HAVE_MMAP + if (file->ismapped) + munmap( file->buf, file->eof - file->buf + 1 ); + else +# endif + free( file->buf ); +} +#endif + +CONF_READ_VARS + +#define C_MTYPE_MASK 0x30000000 +# define C_PATH 0x10000000 /* C_TYPE_STR is a path spec */ +# define C_BOOL 0x10000000 /* C_TYPE_INT is a boolean */ +# define C_ENUM 0x20000000 /* C_TYPE_INT is an enum (option) */ +# define C_GRP 0x30000000 /* C_TYPE_INT is a group spec */ +#define C_INTERNAL 0x40000000 /* don't expose to core */ +#define C_CONFIG 0x80000000 /* process only for finding deps */ + +#ifdef XDMCP +static int +PrequestPort( Value *retval ) +{ + if (!VxdmcpEnable.ptr) { + retval->ptr = (char *)0; + return 1; + } + return 0; +} +#endif + +static Value + emptyStr = { "", 1 }, + nullValue = { 0, 0 }, + emptyArgv = { (char *)&nullValue, 0 }; + +static int +PnoPassUsers( Value *retval ) +{ + if (!VnoPassEnable.ptr) { + *retval = emptyArgv; + return 1; + } + return 0; +} + +static int +PautoLoginX( Value *retval ) +{ + if (!VautoLoginEnable.ptr) { + *retval = emptyStr; + return 1; + } + return 0; +} + +CONF_READ_ENTRIES + +static const char *kdmrc = KDMCONF "/kdmrc"; + +static Section *rootsec; + +static void +ReadConf() +{ + const char *nstr, *dstr, *cstr, *dhost, *dnum, *dclass; + char *s, *e, *st, *en, *ek, *sl, *pt; + Section *cursec; + Entry *curent; + Ent *ce; + int nlen, dlen, clen, dhostl, dnuml, dclassl; + int i, line, sectmoan, restl; + File file; + static int confread; + + if (confread) + return; + confread = 1; + + Debug( "reading config %s ...\n", kdmrc ); + if (!readFile( &file, kdmrc, "master configuration" )) + return; + + for (s = file.buf, line = 0, cursec = 0, sectmoan = 1; s < file.eof; s++) { + line++; + + while ((s < file.eof) && isspace( *s ) && (*s != '\n')) + s++; + + if ((s < file.eof) && ((*s == '\n') || (*s == '#'))) { + sktoeol: + while ((s < file.eof) && (*s != '\n')) + s++; + continue; + } + sl = s; + + if (*s == '[') { + sectmoan = 0; + while ((s < file.eof) && (*s != '\n')) + s++; + e = s - 1; + while ((e > sl) && isspace( *e )) + e--; + if (*e != ']') { + cursec = 0; + LogError( "Invalid section header at %s:%d\n", kdmrc, line ); + continue; + } + nstr = sl + 1; + nlen = e - nstr; + for (cursec = rootsec; cursec; cursec = cursec->next) + if (nlen == cursec->nlen && + !memcmp( nstr, cursec->name, nlen )) + { + LogInfo( "Multiple occurrences of section [%.*s] in %s. " + "Consider merging them.\n", nlen, nstr, kdmrc ); + goto secfnd; + } + if (nstr[0] == 'X' && nstr[1] == '-') { + cstr = nstr + nlen; + clen = 0; + while (++clen, *--cstr != '-'); + if (cstr == nstr + 1) + goto illsec; + dstr = nstr + 2; + dlen = nlen - clen - 2; + dhost = dstr; + dhostl = 0; + for (restl = dlen; restl; restl--) { + if (dhost[dhostl] == ':') { + dnum = dhost + dhostl + 1; + dnuml = 0; + for (restl--; restl; restl--) { + if (dnum[dnuml] == '_') { + dclass = dnum + dnuml + 1; + dclassl = restl; + goto gotall; + } + dnuml++; + } + goto gotnum; + } + dhostl++; + } + dnum = "*"; + dnuml = 1; + gotnum: + dclass = "*"; + dclassl = 1; + gotall: ; + } else { + if (nstr[0] == '-') + goto illsec; + dstr = 0; + dlen = 0; + dhost = 0; + dhostl = 0; + dnum = 0; + dnuml = 0; + dclass = 0; + dclassl = 0; + cstr = nstr; + clen = nlen; + } + for (i = 0; i < as(allSects); i++) + if ((int)strlen( allSects[i]->name ) == clen && + !memcmp( allSects[i]->name, cstr, clen )) + goto newsec; + illsec: + cursec = 0; + LogError( "Unrecognized section name [%.*s] at %s:%d\n", + nlen, nstr, kdmrc, line ); + continue; + newsec: + if (!(cursec = Malloc( sizeof(*cursec) ))) + return; + cursec->name = nstr; + cursec->nlen = nlen; + cursec->dname = dstr; + cursec->dlen = dlen; + cursec->dhost = dhost; + cursec->dhostl = dhostl; + cursec->dnum = dnum; + cursec->dnuml = dnuml; + cursec->dclass = dclass; + cursec->dclassl = dclassl; + cursec->sect = allSects[i]; + cursec->entries = 0; + cursec->next = rootsec; + rootsec = cursec; + /*Debug( "now in section [%.*s], dpy '%.*s', core '%.*s'\n", + nlen, nstr, dlen, dstr, clen, cstr );*/ + secfnd: + continue; + } + + if (!cursec) { + if (sectmoan) { + sectmoan = 0; + LogError( "Entry outside any section at %s:%d", kdmrc, line ); + } + goto sktoeol; + } + + for (; (s < file.eof) && (*s != '\n'); s++) + if (*s == '=') + goto haveeq; + LogError( "Invalid entry (missing '=') at %s:%d\n", kdmrc, line ); + continue; + + haveeq: + for (ek = s - 1; ; ek--) { + if (ek < sl) { + LogError( "Invalid entry (empty key) at %s:%d\n", kdmrc, line ); + goto sktoeol; + } + if (!isspace( *ek )) + break; + } + + s++; + while ((s < file.eof) && isspace( *s ) && (*s != '\n')) + s++; + for (pt = st = en = s; s < file.eof && *s != '\n'; s++) { + if (*s == '\\') { + s++; + if (s >= file.eof || *s == '\n') { + LogError( "Trailing backslash at %s:%d\n", kdmrc, line ); + break; + } + switch (*s) { + case 's': *pt++ = ' '; break; + case 't': *pt++ = '\t'; break; + case 'n': *pt++ = '\n'; break; + case 'r': *pt++ = '\r'; break; + case '\\': *pt++ = '\\'; break; + default: *pt++ = '\\'; *pt++ = *s; break; + } + en = pt; + } else { + *pt++ = *s; + if (*s != ' ' && *s != '\t') + en = pt; + } + } + + nstr = sl; + nlen = ek - sl + 1; + /*Debug( "read entry '%.*s'='%.*s'\n", nlen, nstr, en - st, st );*/ + for (i = 0; i < cursec->sect->numents; i++) { + ce = cursec->sect->ents + i; + if ((int)strlen( ce->name ) == nlen && + !memcmp( ce->name, nstr, nlen )) + goto keyok; + } + LogError( "Unrecognized key '%.*s' in section [%.*s] at %s:%d\n", + nlen, nstr, cursec->nlen, cursec->name, kdmrc, line ); + continue; + keyok: + for (curent = cursec->entries; curent; curent = curent->next) + if (ce == curent->ent) { + LogError( "Multiple occurrences of key '%s' in section [%.*s]" + " of %s\n", + ce->name, cursec->nlen, cursec->name, kdmrc ); + goto keyfnd; + } + if (!(curent = Malloc( sizeof(*curent) ))) + return; + curent->ent = ce; + curent->line = line; + curent->val = st; + curent->vallen = en - st; + curent->next = cursec->entries; + cursec->entries = curent; + keyfnd: + continue; + } +} + +static Entry * +FindGEnt( int id ) +{ + Section *cursec; + Entry *curent; + + for (cursec = rootsec; cursec; cursec = cursec->next) + if (!cursec->dname) + for (curent = cursec->entries; curent; curent = curent->next) + if (curent->ent->id == id) { + Debug( "line %d: %s = %'.*s\n", + curent->line, curent->ent->name, + curent->vallen, curent->val ); + return curent; + } + return 0; +} + +/* Display name match scoring: + * - class (any/exact) -> 0/1 + * - number (any/exact) -> 0/2 + * - host (any/nonempty/trail/exact) -> 0/4/8/12 + */ +static Entry * +FindDEnt( int id, DSpec *dspec ) +{ + Section *cursec, *bestsec; + Entry *curent, *bestent; + int score, bestscore; + + bestscore = -1, bestent = 0; + for (cursec = rootsec; cursec; cursec = cursec->next) + if (cursec->dname) { + score = 0; + if (cursec->dclassl != 1 || cursec->dclass[0] != '*') { + if (cursec->dclassl == dspec->dclassl && + !memcmp( cursec->dclass, dspec->dclass, dspec->dclassl )) + score = 1; + else + continue; + } + if (cursec->dnuml != 1 || cursec->dnum[0] != '*') { + if (cursec->dnuml == dspec->dnuml && + !memcmp( cursec->dnum, dspec->dnum, dspec->dnuml )) + score += 2; + else + continue; + } + if (cursec->dhostl != 1 || cursec->dhost[0] != '*') { + if (cursec->dhostl == 1 && cursec->dhost[0] == '+') { + if (dspec->dhostl) + score += 4; + else + continue; + } else if (cursec->dhost[0] == '.') { + if (cursec->dhostl < dspec->dhostl && + !memcmp( cursec->dhost, + dspec->dhost + dspec->dhostl - cursec->dhostl, + cursec->dhostl )) + score += 8; + else + continue; + } else { + if (cursec->dhostl == dspec->dhostl && + !memcmp( cursec->dhost, dspec->dhost, dspec->dhostl )) + score += 12; + else + continue; + } + } + if (score > bestscore) { + for (curent = cursec->entries; curent; curent = curent->next) + if (curent->ent->id == id) { + bestent = curent; + bestsec = cursec; + bestscore = score; + break; + } + } + } + if (bestent) + Debug( "line %d: %.*s:%.*s_%.*s/%s = %'.*s\n", bestent->line, + bestsec->dhostl, bestsec->dhost, + bestsec->dnuml, bestsec->dnum, + bestsec->dclassl, bestsec->dclass, + bestent->ent->name, bestent->vallen, bestent->val ); + return bestent; +} + +static const char * +CvtValue( Ent *et, Value *retval, int vallen, const char *val, char **eopts ) +{ + Value *ents; + int i, b, e, tlen, nents, esiz; + char buf[80]; + + switch (et->id & C_TYPE_MASK) { + case C_TYPE_INT: + for (i = 0; i < vallen && i < (int)sizeof(buf) - 1; i++) + buf[i] = tolower( val[i] ); + buf[i] = 0; + if ((et->id & C_MTYPE_MASK) == C_BOOL) { + if (!strcmp( buf, "true" ) || + !strcmp( buf, "on" ) || + !strcmp( buf, "yes" ) || + !strcmp( buf, "1" )) + retval->ptr = (char *)1; + else if (!strcmp( buf, "false" ) || + !strcmp( buf, "off" ) || + !strcmp( buf, "no" ) || + !strcmp( buf, "0" )) + retval->ptr = (char *)0; + else + return "boolean"; + return 0; + } else if ((et->id & C_MTYPE_MASK) == C_ENUM) { + for (i = 0; eopts[i]; i++) + if (!memcmp( eopts[i], val, vallen ) && !eopts[i][vallen]) { + retval->ptr = (char *)i; + return 0; + } + return "option"; + } else if ((et->id & C_MTYPE_MASK) == C_GRP) { + struct group *ge; + if ((ge = getgrnam( buf ))) { + retval->ptr = (char *)ge->gr_gid; + return 0; + } + } + retval->ptr = 0; + if (sscanf( buf, "%li", &retval->ptr ) != 1) + return "integer"; + return 0; + case C_TYPE_STR: + retval->ptr = val; + retval->len = vallen + 1; + if ((et->id & C_MTYPE_MASK) == C_PATH) + if (vallen && val[vallen-1] == '/') + retval->len--; + return 0; + case C_TYPE_ARGV: + if (!(ents = Malloc( sizeof(Value) * (esiz = 10) ))) + return 0; + for (nents = 0, tlen = 0, i = 0; ; i++) { + for (; i < vallen && isspace( val[i] ); i++); + for (b = i; i < vallen && val[i] != ','; i++); + if (b == i) + break; + for (e = i; e > b && isspace( val[e - 1] ); e--); + if (esiz < nents + 2) { + Value *entsn = Realloc( ents, + sizeof(Value) * (esiz = esiz * 2 + 1) ); + if (!nents) + break; + ents = entsn; + } + ents[nents].ptr = val + b; + ents[nents].len = e - b; + nents++; + tlen += e - b + 1; + } + ents[nents].ptr = 0; + retval->ptr = (char *)ents; + retval->len = tlen; + return 0; + default: + LogError( "Internal error: unknown value type in id %#x\n", et->id ); + return 0; + } +} + +static void +GetValue( Ent *et, DSpec *dspec, Value *retval, char **eopts ) +{ + Entry *ent; + const char *errs; + +/* Debug( "Getting value %#x\n", et->id );*/ + if (dspec) + ent = FindDEnt( et->id, dspec ); + else + ent = FindGEnt( et->id ); + if (ent) { + if (!(errs = CvtValue( et, retval, ent->vallen, ent->val, eopts ))) + return; + LogError( "Invalid %s value '%.*s' at %s:%d\n", + errs, ent->vallen, ent->val, kdmrc, ent->line ); + } + Debug( "default: %s = %'s\n", et->name, et->def ); + if ((errs = CvtValue( et, retval, strlen( et->def ), et->def, eopts ))) + LogError( "Internal error: invalid default %s value '%s' for key %s\n", + errs, et->def, et->name ); +} + +static int +AddValue( ValArr *va, int id, Value *val ) +{ + int nu; + +/* Debug( "Addig value %#x\n", id );*/ + if (va->nents == va->esiz) { + va->ents = Realloc( va->ents, sizeof(Val) * (va->esiz += 50) ); + if (!va->ents) + return 0; + } + va->ents[va->nents].id = id; + va->ents[va->nents].val = *val; + va->nents++; + switch (id & C_TYPE_MASK) { + case C_TYPE_INT: + break; + case C_TYPE_STR: + va->nchars += val->len; + break; + case C_TYPE_ARGV: + va->nchars += val->len; + for (nu = 0; ((Value *)val->ptr)[nu++].ptr; ); + va->nptrs += nu; + break; + } + return 1; +} + +static void +CopyValues( ValArr *va, Sect *sec, DSpec *dspec, int isconfig ) +{ + Value val; + int i; + + Debug( "getting values for section class [%s]\n", sec->name ); + for (i = 0; i < sec->numents; i++) { +/*Debug ("value %#x\n", sec->ents[i].id);*/ + if ((sec->ents[i].id & (int)C_CONFIG) != isconfig) + ; + else if (sec->ents[i].id & C_INTERNAL) { + GetValue( sec->ents + i, dspec, ((Value *)sec->ents[i].ptr), 0 ); + } else { + if (((sec->ents[i].id & C_MTYPE_MASK) == C_ENUM) || + !sec->ents[i].ptr || + !((int (*)( Value * ))sec->ents[i].ptr)(&val)) { + GetValue( sec->ents + i, dspec, &val, + (char **)sec->ents[i].ptr ); + } + if (!AddValue( va, sec->ents[i].id, &val )) + break; + } + } + return; +} + +static void +SendValues( ValArr *va ) +{ + Value *cst; + int i, nu; + + GSendInt( va->nents ); + GSendInt( va->nptrs ); + GSendInt( 0/*va->nints*/ ); + GSendInt( va->nchars ); + for (i = 0; i < va->nents; i++) { + GSendInt( va->ents[i].id & ~C_PRIVATE ); + switch (va->ents[i].id & C_TYPE_MASK) { + case C_TYPE_INT: + GSendInt( (int)va->ents[i].val.ptr ); + break; + case C_TYPE_STR: + GSendNStr( va->ents[i].val.ptr, va->ents[i].val.len - 1 ); + break; + case C_TYPE_ARGV: + cst = (Value *)va->ents[i].val.ptr; + for (nu = 0; cst[nu].ptr; nu++); + GSendInt( nu ); + for (; cst->ptr; cst++) + GSendNStr( cst->ptr, cst->len ); + break; + } + } +} + + +#ifdef XDMCP +static char * +ReadWord( File *file, int *len, int EOFatEOL ) +{ + char *wordp, *wordBuffer; + int quoted; + char c; + + rest: + wordp = wordBuffer = file->cur; + mloop: + quoted = 0; + qloop: + if (file->cur == file->eof) { + doeow: + if (wordp == wordBuffer) + return 0; + retw: + *wordp = '\0'; + *len = wordp - wordBuffer; + return wordBuffer; + } + c = *file->cur++; + switch (c) { + case '#': + if (quoted) + break; + do { + if (file->cur == file->eof) + goto doeow; + c = *file->cur++; + } while (c != '\n'); + case '\0': + case '\n': + if (EOFatEOL && !quoted) { + file->cur--; + goto doeow; + } + if (wordp != wordBuffer) { + file->cur--; + goto retw; + } + goto rest; + case ' ': + case '\t': + if (wordp != wordBuffer) + goto retw; + goto rest; + case '\\': + if (!quoted) { + quoted = 1; + goto qloop; + } + break; + } + *wordp++ = c; + goto mloop; +} + +#define ALIAS_CHARACTER '%' +#define EQUAL_CHARACTER '=' +#define NEGATE_CHARACTER '!' +#define CHOOSER_STRING "CHOOSER" +#define BROADCAST_STRING "BROADCAST" +#define NOBROADCAST_STRING "NOBROADCAST" +#define LISTEN_STRING "LISTEN" +#define WILDCARD_STRING "*" + +typedef struct hostEntry { + struct hostEntry *next; + int type; + union _hostOrAlias { + char *aliasPattern; + char *hostPattern; + struct _display { + int connectionType; + int hostAddrLen; + char *hostAddress; + } displayAddress; + } entry; +} HostEntry; + +typedef struct listenEntry { + struct listenEntry *next; + int iface; + int mcasts; + int nmcasts; +} ListenEntry; + +typedef struct aliasEntry { + struct aliasEntry *next; + char *name; + HostEntry **pHosts; + int hosts; + int nhosts; + int hasBad; +} AliasEntry; + +typedef struct aclEntry { + struct aclEntry *next; + HostEntry **pEntries; + int entries; + int nentries; + HostEntry **pHosts; + int hosts; + int nhosts; + int flags; +} AclEntry; + + +static int +HasGlobCharacters( char *s ) +{ + for (;;) + switch (*s++) { + case '?': + case '*': + return 1; + case '\0': + return 0; + } +} + +#define PARSE_ALL 0 +#define PARSE_NO_BCAST 1 +#define PARSE_NO_PAT 2 +#define PARSE_NO_ALIAS 4 + +static int +ParseHost( int *nHosts, HostEntry ***hostPtr, int *nChars, + char *hostOrAlias, int len, int parse ) +{ +#if defined(IPv6) && defined(AF_INET6) + struct addrinfo *ai; +#else + struct hostent *hostent; +#endif + void *addr; + int addr_type, addr_len; + + if (!(**hostPtr = (HostEntry *)Malloc( sizeof(HostEntry)))) + return 0; + if (!(parse & PARSE_NO_BCAST) && !strcmp( hostOrAlias, BROADCAST_STRING )) + { + (**hostPtr)->type = HOST_BROADCAST; + } + else if (!(parse & PARSE_NO_ALIAS) && *hostOrAlias == ALIAS_CHARACTER) + { + (**hostPtr)->type = HOST_ALIAS; + (**hostPtr)->entry.aliasPattern = hostOrAlias + 1; + *nChars += len; + } + else if (!(parse & PARSE_NO_PAT) && HasGlobCharacters( hostOrAlias )) + { + (**hostPtr)->type = HOST_PATTERN; + (**hostPtr)->entry.hostPattern = hostOrAlias; + *nChars += len + 1; + } + else + { + (**hostPtr)->type = HOST_ADDRESS; +#if defined(IPv6) && defined(AF_INET6) + if (getaddrinfo( hostOrAlias, NULL, NULL, &ai )) +#else + if (!(hostent = gethostbyname( hostOrAlias ))) +#endif + { + LogWarn( "XDMCP ACL: unresolved host %'s\n", hostOrAlias ); + free( (char *)(**hostPtr) ); + return 0; + } +#if defined(IPv6) && defined(AF_INET6) + addr_type = ai->ai_addr->sa_family; + if (ai->ai_family == AF_INET) { + addr = &((struct sockaddr_in *)ai->ai_addr)->sin_addr; + addr_len = sizeof(struct in_addr); + } else /*if (ai->ai_addr->sa_family == AF_INET6)*/ { + addr = &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr; + addr_len = sizeof(struct in6_addr); + } +#else + addr_type = hostent->h_addrtype; + addr = hostent->h_addr; + addr_len = hostent->h_length; +#endif + if (!((**hostPtr)->entry.displayAddress.hostAddress = + Malloc( addr_len ))) + { +#if defined(IPv6) && defined(AF_INET6) + freeaddrinfo( ai ); +#endif + free( (char *)(**hostPtr) ); + return 0; + } + memcpy( (**hostPtr)->entry.displayAddress.hostAddress, addr, addr_len ); + *nChars += addr_len; + (**hostPtr)->entry.displayAddress.hostAddrLen = addr_len; + (**hostPtr)->entry.displayAddress.connectionType = addr_type; +#if defined(IPv6) && defined(AF_INET6) + freeaddrinfo( ai ); +#endif + } + *hostPtr = &(**hostPtr)->next; + (*nHosts)++; + return 1; +} + +/* Returns non-0 if string is matched by pattern. Does case folding. */ +static int +patternMatch( const char *string, const char *pattern ) +{ + int p, s; + + if (!string) + string = ""; + + for (;;) { + s = *string++; + switch (p = *pattern++) { + case '*': + if (!*pattern) + return 1; + for (string--; *string; string++) + if (patternMatch( string, pattern )) + return 1; + return 0; + case '?': + if (s == '\0') + return 0; + break; + case '\0': + return s == '\0'; + case '\\': + p = *pattern++; + /* fall through */ + default: + if (tolower( p ) != tolower( s )) + return 0; + } + } +} + +#define MAX_DEPTH 32 + +#define CHECK_NOT 1 +#define CHECK_NO_PAT 2 + +static int +checkHostlist( HostEntry **hosts, int nh, AliasEntry *aliases, int na, + int depth, int flags ) +{ + HostEntry *h; + AliasEntry *a; + int hn, an, am; + + for (h = *hosts, hn = 0; hn < nh; hn++, h = h->next) + if (h->type == HOST_ALIAS) { + if (depth == MAX_DEPTH) { + LogError( "XDMCP ACL: alias recursion involving %%%s\n", + h->entry.aliasPattern ); + return 1; + } + for (a = aliases, an = 0, am = 0; an < na; an++, a = a->next) + if (patternMatch( a->name, h->entry.aliasPattern )) { + am = 1; + if ((flags & CHECK_NOT) && a->hasBad) { + LogError( "XDMCP ACL: alias %%%s with unresolved hosts " + "in denying rule\n", a->name ); + return 1; + } + if (checkHostlist( a->pHosts, a->nhosts, aliases, na, + depth + 1, flags )) + return 1; + } + if (!am) { + if (flags & CHECK_NOT) { + LogError( "XDMCP ACL: unresolved alias pattern %%%s " + "in denying rule\n", h->entry.aliasPattern ); + return 1; + } else + LogWarn( "XDMCP ACL: unresolved alias pattern %%%s\n", + h->entry.aliasPattern ); + } + } else if (h->type == HOST_PATTERN && (flags & CHECK_NO_PAT)) + LogWarn( "XDMCP ACL: wildcarded pattern %'s in host-only context\n", + h->entry.hostPattern ); + return 0; +} + +static void +ReadAccessFile( const char *fname ) +{ + HostEntry *hostList, **hostPtr = &hostList; + AliasEntry *aliasList, **aliasPtr = &aliasList; + AclEntry *acList, **acPtr = &acList, *acl; + ListenEntry *listenList, **listenPtr = &listenList; + char *displayOrAlias, *hostOrAlias; + File file; + int nHosts, nAliases, nAcls, nListens, nChars, error, bad; + int i, len; + + nHosts = nAliases = nAcls = nListens = nChars = error = 0; + if (!readFile( &file, fname, "XDMCP access control" )) + goto sendacl; + while ((displayOrAlias = ReadWord( &file, &len, FALSE ))) { + if (*displayOrAlias == ALIAS_CHARACTER) + { + if (!(*aliasPtr = (AliasEntry *)Malloc( sizeof(AliasEntry)))) { + error = 1; + break; + } + (*aliasPtr)->name = displayOrAlias + 1; + nChars += len; + (*aliasPtr)->hosts = nHosts; + (*aliasPtr)->pHosts = hostPtr; + (*aliasPtr)->nhosts = 0; + (*aliasPtr)->hasBad = 0; + while ((hostOrAlias = ReadWord( &file, &len, TRUE ))) { + if (ParseHost( &nHosts, &hostPtr, &nChars, hostOrAlias, len, + PARSE_NO_BCAST )) + (*aliasPtr)->nhosts++; + else + (*aliasPtr)->hasBad = 1; + } + aliasPtr = &(*aliasPtr)->next; + nAliases++; + } + else if (!strcmp( displayOrAlias, LISTEN_STRING )) + { + if (!(*listenPtr = (ListenEntry *)Malloc( sizeof(ListenEntry)))) { + error = 1; + break; + } + (*listenPtr)->iface = nHosts; + if (!(hostOrAlias = ReadWord( &file, &len, TRUE )) || + !strcmp( hostOrAlias, WILDCARD_STRING ) || + !ParseHost( &nHosts, &hostPtr, &nChars, hostOrAlias, len, + PARSE_NO_BCAST|PARSE_NO_PAT|PARSE_NO_ALIAS )) + { + (*listenPtr)->iface = -1; + } + (*listenPtr)->mcasts = nHosts; + (*listenPtr)->nmcasts = 0; + while ((hostOrAlias = ReadWord( &file, &len, TRUE ))) { + if (ParseHost( &nHosts, &hostPtr, &nChars, hostOrAlias, len, + PARSE_NO_BCAST|PARSE_NO_PAT|PARSE_NO_ALIAS )) + (*listenPtr)->nmcasts++; + } + listenPtr = &(*listenPtr)->next; + nListens++; + } + else + { + if (!(*acPtr = (AclEntry *)Malloc( sizeof(AclEntry)))) { + error = 1; + break; + } + (*acPtr)->flags = 0; + if (*displayOrAlias == NEGATE_CHARACTER) { + (*acPtr)->flags |= a_notAllowed; + displayOrAlias++; + } else if (*displayOrAlias == EQUAL_CHARACTER) + displayOrAlias++; + (*acPtr)->entries = nHosts; + (*acPtr)->pEntries = hostPtr; + (*acPtr)->nentries = 1; + if (!ParseHost( &nHosts, &hostPtr, &nChars, displayOrAlias, len, + PARSE_NO_BCAST )) + { + bad = 1; + if ((*acPtr)->flags & a_notAllowed) { + LogError( "XDMCP ACL: unresolved host in denying rule\n" ); + error = 1; + } + } else + bad = 0; + (*acPtr)->hosts = nHosts; + (*acPtr)->pHosts = hostPtr; + (*acPtr)->nhosts = 0; + while ((hostOrAlias = ReadWord( &file, &len, TRUE ))) { + if (!strcmp( hostOrAlias, CHOOSER_STRING )) + (*acPtr)->flags |= a_useChooser; + else if (!strcmp( hostOrAlias, NOBROADCAST_STRING )) + (*acPtr)->flags |= a_notBroadcast; + else { + if (ParseHost( &nHosts, &hostPtr, &nChars, + hostOrAlias, len, PARSE_NO_PAT )) + (*acPtr)->nhosts++; + } + } + if (!bad) { + acPtr = &(*acPtr)->next; + nAcls++; + } + } + } + + if (!nListens) { + if (!(*listenPtr = (ListenEntry *)Malloc( sizeof(ListenEntry)))) + error = 1; + else { + (*listenPtr)->iface = -1; + (*listenPtr)->mcasts = nHosts; + (*listenPtr)->nmcasts = 0; +#if defined(IPv6) && defined(AF_INET6) && defined(XDM_DEFAULT_MCAST_ADDR6) + if (ParseHost( &nHosts, &hostPtr, &nChars, + XDM_DEFAULT_MCAST_ADDR6, + sizeof(XDM_DEFAULT_MCAST_ADDR6)-1, + PARSE_ALL )) + (*listenPtr)->nmcasts++; +#endif + nListens++; + } + } + + for (acl = acList, i = 0; i < nAcls; i++, acl = acl->next) + if (checkHostlist( acl->pEntries, acl->nentries, aliasList, nAliases, + 0, (acl->flags & a_notAllowed) ? CHECK_NOT : 0 ) || + checkHostlist( acl->pHosts, acl->nhosts, aliasList, nAliases, + 0, CHECK_NO_PAT )) + error = 1; + + if (error) { + nHosts = nAliases = nAcls = nListens = nChars = 0; + sendacl: + LogError( "No XDMCP requests will be granted\n" ); + } + GSendInt( nHosts ); + GSendInt( nListens ); + GSendInt( nAliases ); + GSendInt( nAcls ); + GSendInt( nChars ); + for (i = 0; i < nHosts; i++, hostList = hostList->next) { + GSendInt( hostList->type ); + switch (hostList->type) { + case HOST_ALIAS: + GSendStr( hostList->entry.aliasPattern ); + break; + case HOST_PATTERN: + GSendStr( hostList->entry.hostPattern ); + break; + case HOST_ADDRESS: + GSendArr( hostList->entry.displayAddress.hostAddrLen, + hostList->entry.displayAddress.hostAddress ); + GSendInt( hostList->entry.displayAddress.connectionType ); + break; + } + } + for (i = 0; i < nListens; i++, listenList = listenList->next) { + GSendInt( listenList->iface ); + GSendInt( listenList->mcasts ); + GSendInt( listenList->nmcasts ); + } + for (i = 0; i < nAliases; i++, aliasList = aliasList->next) { + GSendStr( aliasList->name ); + GSendInt( aliasList->hosts ); + GSendInt( aliasList->nhosts ); + } + for (i = 0; i < nAcls; i++, acList = acList->next) { + GSendInt( acList->entries ); + GSendInt( acList->nentries ); + GSendInt( acList->hosts ); + GSendInt( acList->nhosts ); + GSendInt( acList->flags ); + } +} +#endif + + +int main( int argc ATTR_UNUSED, char **argv ) +{ + DSpec dspec; + ValArr va; + char *ci, *disp, *dcls, *cfgfile; + int what; + + if (!(ci = getenv( "CONINFO" ))) { + fprintf( stderr, "This program is part of kdm and should not be run manually.\n" ); + return 1; + } + if (sscanf( ci, "%d %d", &rfd, &wfd ) != 2) + return 1; + + InitLog(); + + if ((debugLevel = GRecvInt()) & DEBUG_WCONFIG) + sleep( 100 ); + +/* Debug ("parsing command line\n");*/ + if (**++argv) + kdmrc = *argv; +/* + while (*++argv) { + } +*/ + + for (;;) { +/* Debug ("Awaiting command ...\n");*/ + if (!GRecvCmd( &what )) + break; + switch (what) { + case GC_Files: +/* Debug ("GC_Files\n");*/ + ReadConf(); + CopyValues( 0, &secGeneral, 0, C_CONFIG ); +#ifdef XDMCP + CopyValues( 0, &secXdmcp, 0, C_CONFIG ); + GSendInt( 2 ); +#else + GSendInt( 1 ); +#endif + GSendStr( kdmrc ); + GSendInt( -1 ); +#ifdef XDMCP + GSendNStr( VXaccess.ptr, VXaccess.len - 1 ); + GSendInt( 0 ); +#endif + for (; (what = GRecvInt()) != -1; ) + switch (what) { + case GC_gGlobal: + case GC_gDisplay: + GSendInt( 0 ); + break; +#ifdef XDMCP + case GC_gXaccess: + GSendInt( 1 ); + break; +#endif + default: + GSendInt( -1 ); + break; + } + break; + case GC_GetConf: +/* Debug( "GC_GetConf\n" );*/ + memset( &va, 0, sizeof(va) ); + what = GRecvInt(); + cfgfile = GRecvStr(); + switch (what) { + case GC_gGlobal: +/* Debug( "GC_gGlobal\n" );*/ + Debug( "getting global config\n" ); + ReadConf(); + CopyValues( &va, &secGeneral, 0, 0 ); +#ifdef XDMCP + CopyValues( &va, &secXdmcp, 0, 0 ); +#endif + CopyValues( &va, &secShutdown, 0, 0 ); + SendValues( &va ); + break; + case GC_gDisplay: +/* Debug( "GC_gDisplay\n" );*/ + disp = GRecvStr(); +/* Debug( " Display %s\n", disp );*/ + dcls = GRecvStr(); +/* Debug( " Class %s\n", dcls );*/ + Debug( "getting config for display %s, class %s\n", disp, dcls ); + MkDSpec( &dspec, disp, dcls ? dcls : "" ); + ReadConf(); + CopyValues( &va, &sec_Core, &dspec, 0 ); + CopyValues( &va, &sec_Greeter, &dspec, 0 ); + free( disp ); + if (dcls) + free( dcls ); + SendValues( &va ); + break; +#ifdef XDMCP + case GC_gXaccess: + ReadAccessFile( cfgfile ); + break; +#endif + default: + Debug( "Unsupported config category %#x\n", what ); + } + free( cfgfile ); + break; + default: + Debug( "Unknown config command %#x\n", what ); + } + } + +/* Debug( "Config reader exiting ..." );*/ + return EX_NORMAL; +} |