/* Read options from tdmrc 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 "tdm_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 *tdmrc = TDMCONF "/tdmrc"; static const char *tdmrc_dist = TDMCONF "/tdmdistrc"; 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", tdmrc_dist ); if (!readFile( &file, tdmrc_dist, "master configuration" )) { Debug( "reading config %s ...\n", tdmrc ); if (!readFile( &file, tdmrc, "master configuration" )) return; } else { tdmrc = tdmrc_dist; } 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", tdmrc, 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, tdmrc ); 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, tdmrc, 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", tdmrc, line ); } goto sktoeol; } for (; (s < file.eof) && (*s != '\n'); s++) if (*s == '=') goto haveeq; LogError( "Invalid entry (missing '=') at %s:%d\n", tdmrc, line ); continue; haveeq: for (ek = s - 1; ; ek--) { if (ek < sl) { LogError( "Invalid entry (empty key) at %s:%d\n", tdmrc, 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", tdmrc, 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, tdmrc, 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, tdmrc ); 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, tdmrc, 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 tdm 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) tdmrc_dist = tdmrc = *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( tdmrc ); 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; }