diff options
Diffstat (limited to 'tdm/backend')
40 files changed, 18277 insertions, 0 deletions
diff --git a/tdm/backend/CMakeLists.txt b/tdm/backend/CMakeLists.txt new file mode 100644 index 000000000..cd98b3a9c --- /dev/null +++ b/tdm/backend/CMakeLists.txt @@ -0,0 +1,48 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +# FIXME this is far from complete!!! + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR} + ${DBUS_TQT_INCLUDE_DIRS} +) + +link_directories( + ${DBUS_TQT_LIBRARY_DIRS} +) + +##### tdm (executable) ########################## + +add_custom_command( OUTPUT config.ci + COMMAND perl -w ${CMAKE_SOURCE_DIR}/tdm/confproc.pl ${CMAKE_SOURCE_DIR}/tdm/config.def config.ci + DEPENDS ${CMAKE_SOURCE_DIR}/tdm/confproc.pl ${CMAKE_SOURCE_DIR}/tdm/config.def ) + +set_property( SOURCE auth.c APPEND PROPERTY OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/config.ci ) + +if( WITH_XDMCP ) + set( XDMCP_LIBRARIES "Xdmcp" ) +else() + set( XDMCP_LIBRARIES "" ) +endif() + +tde_add_executable( tdm + SOURCES + access.c auth.c bootman.c choose.c client.c consolekit.c + ctrl.c daemon.c dm.c dpylist.c error.c genauth.c + inifile.c krb5auth.c mitauth.c netaddr.c policy.c + process.c protodpy.c reset.c resource.c rpcauth.c + server.c session.c sessreg.c socket.c streams.c + util.c xdmauth.c xdmcp.c + LINK X11 ${XAU_LIBRARIES} ${DBUS_TQT_LIBRARIES} ${CRYPT_LIBRARY} ${PAM_LIBRARY} ${XDMCP_LIBRARIES} + DESTINATION ${BIN_INSTALL_DIR} +) diff --git a/tdm/backend/Imakefile b/tdm/backend/Imakefile new file mode 100644 index 000000000..f3b4e0050 --- /dev/null +++ b/tdm/backend/Imakefile @@ -0,0 +1,203 @@ +/* well, we have no subdirs ... +#define PassCDebugFlags 'CDEBUGFLAGS=$(CDEBUGFLAGS)' +*/ + +#ifdef DEBUG +CDEBUGFLAGS := $(CDEBUGFLAGS) -g +#endif + +#ifndef BuildBoth +#define BuildBoth (defined(LinuxArchitecture) && !UseElfFormat) +#endif + +#ifndef LinuxShadowSuite +#define LinuxShadowSuite NO +#endif + +#if FSUseSyslog +LOG_DEFINES = -DUSE_SYSLOG +#endif + +#ifdef NoXDMCP +XDMCPLIB = +#else +XDMCP_DEFINES = -DXDMCP +#endif + +#if HasXdmAuth +XDMAUTH_DEFINES = -DHASXDMAUTH +XDMAUTHOBJS = xdmauth.o +XDMAUTHSRCS = xdmauth.c +#endif + +#if HasSecureRPC +RPC_DEFINES = -DSECURE_RPC +RPCOBJS = rpcauth.o +RPCSRCS = rpcauth.c +RPCLIB = -lrpcsvc +#endif + +#if HasKrbIV +#if NOAFS +KRBIV_DEFINES = KrbIVDefines -DNO_AFS +#else +KRBIV_DEFINES = KrbIVDefines +#endif +KRBIV_INCLUDES = KrbIVIncludes +KRBIVLIB = KrbIVLibraries +#endif + +#if HasKrb5 +KRB5_DEFINES = Krb5Defines +KRB5_INCLUDE = Krb5Includes +KRB5OBJS = krb5auth.o +KRB5SRCS = krb5auth.c +#endif + +/* This is correct for Linux and FreeBSD */ +#if HasPam +PAM_LIBRARIES = PamLibraries +PAM_DEFINES = -DUSE_PAM +#endif + +#if HasPam +#undef HasShadowPasswd +#define HasShadowPasswd NO +#undef HasLibCrypt +#define HasLibCrypt NO +#endif + +/* +#if HasBSDAuth +BSDAUTH_DEFINES = -DUSE_BSDAUTH +#endif +*/ + +#if SystemV4 || HasShadowPasswd + +#if !LinuxShadowSuite +PWD_DEFINES = -DUSESHADOW +#else +PWD_DEFINES = -DUSESHADOW -DSHADOWSUITE +#endif + +#if !defined(i386IscArchitecture) && !defined(i386ScoArchitecture) && !defined(LinuxArchitecture) && !defined(NTOArchitecture) && !defined(SGIArchitecture) +SYS_LIBRARIES3 = -lresolv +#endif +#if SystemV || defined(SequentArchitecture) +SYS_LIBRARIES1 = -lsec +#endif +#if defined(LinuxArchitecture) && (!UseElfFormat || LinuxShadowSuite) +SYS_LIBRARIES1 = -lshadow +#endif + +#endif + +#if defined(UltrixArchitecture) +SYS_LIBRARIES1 = -lauth +#endif + +#if (defined(AIXArchitecture) && (OSMajorVersion >= 3)) +SYS_LIBRARIES1 = -ls +#endif + +#if HasLibCrypt +#ifdef SpecialLibCrypt +CRYPT_LIBRARIES = SpecialLibCrypt +#else +CRYPT_LIBRARIES = -lcrypt +#if defined(LynxOSArchitecture) +CRYPT_DEFINES = -DHAS_CRYPT +#endif +#endif +#endif + +#if HasBSD44Sockets +SOCK_DEFINES = -DBSD44SOCKETS +#endif + +#if defined(i386Architecture) || defined(AmigaArchitecture) +FRAGILE_DEFINES = -DFRAGILE_DEV_MEM +#endif + +#ifdef RandomDefines +RANDOM_DEFINES = RandomDefines +#elif defined(OpenBSDArchitecture) +RANDOM_DEFINES = -DARC4_RANDOM +#elif defined(LinuxArchitecture) +RANDOM_DEFINES = -DDEV_RANDOM=\"/dev/urandom\" +#elif defined(NetBSDArchitecture) && \ + ((OSMajorVersion > 1) || \ + (OSMajorVersion == 1 && OSMinorVersion > 3)) +RANDOM_DEFINES = -DDEV_RANDOM=\"/dev/urandom\" +#endif + + +#if HasSetUserContext +USER_CONTEXT_DEFINES = -DHAS_SETUSERCONTEXT +# XXX - only FreeBSD has this in libutil +SYS_LIBRARIES1 = -lutil +#endif + +#if HasSetProcTitle +PROCTITLE_DEFINES = -DHAS_SETPROCTITLE +#endif + + SYS_LIBRARIES = $(SYS_LIBRARIES1) $(SYS_LIBRARIES2) $(SYS_LIBRARIES3) + + INCLUDES = $(KRB5_INCLUDE) + DEPLIBS = $(DEPXLIB) $(DEPXAUTHLIB) $(DEPXDMCPLIB) + LOCAL_LIBRARIES = $(XLIB) $(XAUTHLIB) \ + $(XDMCPLIB) $(RPCLIB) $(PAM_LIBRARIES) \ + $(CRYPT_LIBRARIES) $(KRBIVLIB) + + COMMSRCS = auth.c daemon.c server.c dpylist.c dm.c error.c \ + netaddr.c reset.c resource.c protodpy.c policy.c \ + session.c socket.c streams.c util.c xdmcp.c \ + process.c mitauth.c \ + genauth.c access.c choose.c consolekit.c \ + $(XDMAUTHSRCS) $(RPCSRCS) $(KRB5SRCS) + COMMOBJS = auth.o daemon.o server.o dpylist.o dm.o error.o \ + netaddr.o reset.o resource.o protodpy.o policy.o \ + session.o socket.o streams.o util.o xdmcp.o \ + process.o mitauth.o \ + genauth.o access.o choose.o consolekit.o \ + $(XDMAUTHOBJS) $(RPCOBJS) $(KRB5OBJS) + + SRCS1 = $(COMMSRCS) client.c + OBJS1 = $(COMMOBJS) client.o + +#if BuildBoth + SRCS2 = $(COMMSRCS) clientsh.c + OBJS2 = $(COMMOBJS) clientsh.o + + XDM_SHADOW = xdm-shadow +#endif + + PROGRAMS = xdm $(XDM_SHADOW) + + + OSMAJORVERSION = OSMajorVersion + OSMINORVERSION = OSMinorVersion + CONN_DEFINES = $(CONNECTION_FLAGS) + DEFINES = $(SIGNAL_DEFINES) $(LOG_DEFINES) \ + $(CRYPT_DEFINES)$(PWD_DEFINES) \ + $(BSDAUTH_DEFINES) $(PAM_DEFINES) $(USER_CONTEXT_DEFINES) \ + $(XDMAUTH_DEFINES) $(RPC_DEFINES) $(KRB5_DEFINES) \ + $(XDMCP_DEFINES) $(SOCK_DEFINES) $(CONN_DEFINES) \ + $(FRAGILE_DEFINES) $(RANDOM_DEFINES) $(PROCTITLE_DEFINES) \ + -DOSMAJORVERSION=$(OSMAJORVERSION) -DOSMINORVERSION=$(OSMINORVERSION) \ + -Dconst= + +ComplexProgramTarget_1(xdm,$(LOCAL_LIBRARIES),NullParameter) +#if BuildBoth +NormalProgramTarget(xdm-shadow,$(OBJS2),$(DEPLIBS),$(LOCAL_LIBRARIES),-lshadow) +InstallProgram(xdm-shadow,$(BINDIR)) +ObjectFromSpecialSource(clientsh,client,-DUSESHADOW) +#endif + +#if defined(FreeBSDArchitecture) && (OSMajorVersion < 2) +XCOMM only for daemon.c? it's used in some other places, too. +SpecialCObjectRule(daemon,$(ICONFIGFILES),-UCSRG_BASED) +#endif + diff --git a/tdm/backend/Makefile.am b/tdm/backend/Makefile.am new file mode 100644 index 000000000..6b5779d51 --- /dev/null +++ b/tdm/backend/Makefile.am @@ -0,0 +1,47 @@ +# forcibly remove thread-related defines & flags +AUTOMAKE_OPTIONS = foreign +CPPFLAGS = $(USER_INCLUDES) $(X_INCLUDES) $(KRB4_INCS) $(KRB5_INCS) $(DBUS_INCS) -I.. -I../.. +LDFLAGS = $(USER_LDFLAGS) $(X_LDFLAGS) $(X_RPATH) $(KRB4_RPATH) $(KRB5_RPATH) +LDADD = $(LIB_X11) -lXau $(LIBXDMCP) $(PASSWDLIBS) $(LIBSHADOW) $(LIBGEN) \ + $(LIB_LIBS) $(KRB4_LIBS) $(KRB5_LIBS) $(DBUS_LIBS) $(LIBSOCKET) $(LIBRESOLV) \ + $(LIBUCB) $(LIBUTIL) $(LIBPOSIX4) + +bin_PROGRAMS = tdm +tdm_SOURCES = \ + access.c \ + auth.c \ + bootman.c \ + choose.c \ + client.c \ + consolekit.c \ + ctrl.c \ + daemon.c \ + dm.c \ + dpylist.c \ + error.c \ + genauth.c \ + inifile.c \ + krb5auth.c \ + mitauth.c \ + netaddr.c \ + policy.c \ + process.c \ + protodpy.c \ + reset.c \ + resource.c \ + rpcauth.c \ + server.c \ + session.c \ + sessreg.c \ + socket.c \ + streams.c \ + util.c \ + xdmauth.c \ + xdmcp.c + +EXTRA_DIST = printf.c + +noinst_HEADERS = dm.h dm_socket.h dm_error.h dm_auth.h greet.h + +# for unsermake (automake is handled by SUBDIRS in ../) +tdm_COMPILE_FIRST = ../config.ci diff --git a/tdm/backend/access.c b/tdm/backend/access.c new file mode 100644 index 000000000..82736be57 --- /dev/null +++ b/tdm/backend/access.c @@ -0,0 +1,468 @@ +/* + +Copyright 1990, 1998 The Open Group +Copyright 2001,2004 Oswald Buddenhagen <[email protected]> +Copyright 2002 Sun Microsystems, Inc. All rights reserved. + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + * + * Access control for XDMCP - keep a database of allowable display addresses + * and (potentially) a list of hosts to send ForwardQuery packets to + */ + +#include <config.h> + +#ifdef XDMCP + +#include "dm.h" +#include "dm_error.h" +#include "dm_socket.h" + +#include <stdio.h> +#include <ctype.h> + +#include <netdb.h> +#if defined(IPv6) && defined(AF_INET6) +# include <arpa/inet.h> +#endif + +typedef struct { + short int type; + union { + char *aliasPattern; + char *hostPattern; + struct _displayAddress { + CARD16 connectionType; + ARRAY8 hostAddress; + } displayAddress; + } entry; +} HostEntry; + +typedef struct { + short int iface; + short int mcasts; + short int nmcasts; +} ListenEntry; + +typedef struct { + char *name; + short int hosts; + short int nhosts; +} AliasEntry; + +typedef struct { + short int entries; + short int nentries; + short int hosts; + short int nhosts; + short int flags; +} AclEntry; + +typedef struct { + HostEntry *hostList; + ListenEntry *listenList; + AliasEntry *aliasList; + AclEntry *acList; + short int nHosts, nListens, nAliases, nAcls; + CfgDep dep; +} AccArr; + +static AccArr accData[1]; + + +static ARRAY8 localAddress; + +ARRAY8Ptr +getLocalAddress( void ) +{ + static int haveLocalAddress; + + if (!haveLocalAddress) { +#if defined(IPv6) && defined(AF_INET6) + struct addrinfo *ai; + + if (getaddrinfo( localHostname(), NULL, NULL, &ai )) { + XdmcpAllocARRAY8( &localAddress, 4 ); + localAddress.data[0] = 127; + localAddress.data[1] = 0; + localAddress.data[2] = 0; + localAddress.data[3] = 1; + } else { + if (ai->ai_family == AF_INET) { + XdmcpAllocARRAY8( &localAddress, sizeof(struct in_addr) ); + memcpy( localAddress.data, + &((struct sockaddr_in *)ai->ai_addr)->sin_addr, + sizeof(struct in_addr) ); + } else /* if (ai->ai_family == AF_INET6) */ { + XdmcpAllocARRAY8( &localAddress, sizeof(struct in6_addr) ); + memcpy( localAddress.data, + &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr, + sizeof(struct in6_addr) ); + } + freeaddrinfo( ai ); +#else + struct hostent *hostent; + + if ((hostent = gethostbyname( localHostname() ))) { + XdmcpAllocARRAY8( &localAddress, hostent->h_length ); + memmove( localAddress.data, hostent->h_addr, hostent->h_length ); +#endif + haveLocalAddress = 1; + } + } + return &localAddress; +} + + +void +ScanAccessDatabase( int force ) +{ + struct _displayAddress *da; + char *cptr; + int nChars, i; + + Debug( "ScanAccessDatabase\n" ); + if (Setjmp( cnftalk.errjmp )) + return; /* may memleak */ + if (startConfig( GC_gXaccess, &accData->dep, force ) <= 0) + return; + if (accData->hostList) + free( accData->hostList ); + accData->nHosts = GRecvInt(); + accData->nListens = GRecvInt(); + accData->nAliases = GRecvInt(); + accData->nAcls = GRecvInt(); + nChars = GRecvInt(); + if (!(accData->hostList = (HostEntry *) + Malloc( accData->nHosts * sizeof(HostEntry) + + accData->nListens * sizeof(ListenEntry) + + accData->nAliases * sizeof(AliasEntry) + + accData->nAcls * sizeof(AclEntry) + + nChars ))) + { + CloseGetter(); + return; + } + accData->listenList = (ListenEntry *)(accData->hostList + accData->nHosts); + accData->aliasList = (AliasEntry *)(accData->listenList + accData->nListens); + accData->acList = (AclEntry *)(accData->aliasList + accData->nAliases); + cptr = (char *)(accData->acList + accData->nAcls); + for (i = 0; i < accData->nHosts; i++) { + switch ((accData->hostList[i].type = GRecvInt())) { + case HOST_ALIAS: + accData->hostList[i].entry.aliasPattern = cptr; + cptr += GRecvStrBuf( cptr ); + break; + case HOST_PATTERN: + accData->hostList[i].entry.hostPattern = cptr; + cptr += GRecvStrBuf( cptr ); + break; + case HOST_ADDRESS: + da = &accData->hostList[i].entry.displayAddress; + da->hostAddress.data = (unsigned char *)cptr; + cptr += (da->hostAddress.length = GRecvArrBuf( cptr )); + switch (GRecvInt()) + { +#ifdef AF_INET + case AF_INET: + da->connectionType = FamilyInternet; + break; +#endif +#if defined(IPv6) && defined(AF_INET6) + case AF_INET6: + da->connectionType = FamilyInternet6; + break; +#endif +#ifdef AF_DECnet + case AF_DECnet: + da->connectionType = FamilyDECnet; + break; +#endif +/*#ifdef AF_UNIX + case AF_UNIX: +#endif*/ + default: + da->connectionType = FamilyLocal; + break; + } + break; + case HOST_BROADCAST: + break; + default: + LogError( "Received unknown host type %d from config reader\n", accData->hostList[i].type ); + return; + } + } + for (i = 0; i < accData->nListens; i++) { + accData->listenList[i].iface = GRecvInt(); + accData->listenList[i].mcasts = GRecvInt(); + accData->listenList[i].nmcasts = GRecvInt(); + } + for (i = 0; i < accData->nAliases; i++) { + accData->aliasList[i].name = cptr; + cptr += GRecvStrBuf( cptr ); + accData->aliasList[i].hosts = GRecvInt(); + accData->aliasList[i].nhosts = GRecvInt(); + } + for (i = 0; i < accData->nAcls; i++) { + accData->acList[i].entries = GRecvInt(); + accData->acList[i].nentries = GRecvInt(); + accData->acList[i].hosts = GRecvInt(); + accData->acList[i].nhosts = GRecvInt(); + accData->acList[i].flags = GRecvInt(); + } +} + + +/* 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 + +static void +scanHostlist( int fh, int nh, + ARRAY8Ptr clientAddress, CARD16 connectionType, + ChooserFunc function, char *closure, + int broadcast, int *haveLocalhost ) +{ + HostEntry *h; + AliasEntry *a; + int na; + + for (h = accData->hostList + fh; nh; nh--, h++) { + switch (h->type) { + case HOST_ALIAS: + for (a = accData->aliasList, na = accData->nAliases; na; na--, a++) + if (patternMatch( a->name, h->entry.aliasPattern )) /* XXX originally swapped, no wildcards in alias name matching */ + scanHostlist( a->hosts, a->nhosts, + clientAddress, connectionType, + function, closure, broadcast, + haveLocalhost ); + break; + case HOST_ADDRESS: + if (XdmcpARRAY8Equal( getLocalAddress(), &h->entry.displayAddress.hostAddress )) + *haveLocalhost = 1; + else if (function) + (*function)( connectionType, &h->entry.displayAddress.hostAddress, closure ); + break; + case HOST_BROADCAST: + if (broadcast && function) + (*function)( FamilyBroadcast, 0, closure ); + break; + default: + break; + } + } +} + +static int +scanEntrylist( int fh, int nh, + ARRAY8Ptr clientAddress, CARD16 connectionType, + char **clientName ) +{ + HostEntry *h; + AliasEntry *a; + int na; + + for (h = accData->hostList + fh; nh; nh--, h++) { + switch (h->type) { + case HOST_ALIAS: + for (a = accData->aliasList, na = accData->nAliases; na; na--, a++) + if (patternMatch( a->name, h->entry.aliasPattern )) + if (scanEntrylist( a->hosts, a->nhosts, + clientAddress, connectionType, + clientName )) + return 1; + break; + case HOST_PATTERN: + if (!*clientName) + *clientName = NetworkAddressToHostname( connectionType, + clientAddress ); + if (patternMatch( *clientName, h->entry.hostPattern )) + return 1; + break; + case HOST_ADDRESS: + if (h->entry.displayAddress.connectionType == connectionType && + XdmcpARRAY8Equal( &h->entry.displayAddress.hostAddress, + clientAddress )) + return 1; + break; + default: + break; + } + } + return 0; +} + +static AclEntry * +matchAclEntry( ARRAY8Ptr clientAddress, CARD16 connectionType, int direct ) +{ + AclEntry *e, *re; + char *clientName = 0; + int ne; + + for (e = accData->acList, ne = accData->nAcls, re = 0; ne; ne--, e++) + if (!e->nhosts == direct) + if (scanEntrylist( e->entries, e->nentries, + clientAddress, connectionType, + &clientName )) + { + re = e; + break; + } + if (clientName) + free( clientName ); + return re; +} + +/* + * calls the given function for each valid indirect entry. Returns TRUE if + * the local host exists on any of the lists, else FALSE + */ +int +ForEachMatchingIndirectHost( ARRAY8Ptr clientAddress, + CARD16 connectionType, + ChooserFunc function, char *closure ) +{ + AclEntry *e; + int haveLocalhost = 0; + + e = matchAclEntry( clientAddress, connectionType, 0 ); + if (e && !(e->flags & a_notAllowed)) { + if (e->flags & a_useChooser) { + ARRAY8Ptr choice; + + choice = IndirectChoice( clientAddress, connectionType ); + if (!choice || XdmcpARRAY8Equal( getLocalAddress(), choice )) + haveLocalhost = 1; + else + (*function)( connectionType, choice, closure ); + } else + scanHostlist( e->hosts, e->nhosts, clientAddress, connectionType, + function, closure, FALSE, &haveLocalhost ); + } + return haveLocalhost; +} + +int +UseChooser( ARRAY8Ptr clientAddress, CARD16 connectionType ) +{ + AclEntry *e; + + e = matchAclEntry( clientAddress, connectionType, 0 ); + return e && !(e->flags & a_notAllowed) && (e->flags & a_useChooser) && + !IndirectChoice( clientAddress, connectionType ); +} + +void +ForEachChooserHost( ARRAY8Ptr clientAddress, CARD16 connectionType, + ChooserFunc function, char *closure ) +{ + AclEntry *e; + int haveLocalhost = 0; + + e = matchAclEntry( clientAddress, connectionType, 0 ); + if (e && !(e->flags & a_notAllowed) && (e->flags & a_useChooser)) + scanHostlist( e->hosts, e->nhosts, clientAddress, connectionType, + function, closure, TRUE, &haveLocalhost ); + if (haveLocalhost) + (*function)( connectionType, getLocalAddress(), closure ); +} + +/* + * returns TRUE if the given client is acceptable to the local host. The + * given display client is acceptable if it occurs without a host list. + */ +int +AcceptableDisplayAddress( ARRAY8Ptr clientAddress, CARD16 connectionType, + xdmOpCode type ) +{ + AclEntry *e; + + if (type == INDIRECT_QUERY) + return 1; + + e = matchAclEntry( clientAddress, connectionType, 1 ); + return e && !(e->flags & a_notAllowed) && + (type != BROADCAST_QUERY || !(e->flags & a_notBroadcast)); +} + +void +ForEachListenAddr( ListenFunc listenfunction, ListenFunc mcastfunction, + void **closure ) +{ + int i, j, ifc, mc, nmc; + + for (i = 0; i < accData->nListens; i++) { + ifc = accData->listenList[i].iface; + (*listenfunction)( ifc < 0 ? 0 : + &accData->hostList[ifc].entry.displayAddress.hostAddress, + closure ); + mc = accData->listenList[i].mcasts; + nmc = accData->listenList[i].nmcasts; + for (j = 0; j < nmc; j++, mc++) + (*mcastfunction)( &accData->hostList[mc].entry.displayAddress.hostAddress, + closure ); + } +} +#endif /* XDMCP */ diff --git a/tdm/backend/auth.c b/tdm/backend/auth.c new file mode 100644 index 000000000..bd183142c --- /dev/null +++ b/tdm/backend/auth.c @@ -0,0 +1,1238 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2000-2004 Oswald Buddenhagen <[email protected]> + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + * + * maintain the authorization generation daemon + */ + +#include "dm.h" +#include "dm_auth.h" +#include "dm_error.h" + +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> + +#include <sys/ioctl.h> + +#include "dm_socket.h" +#ifdef DNETCONN +# include <netdnet/dnetdb.h> +#endif + +#if (defined(_POSIX_SOURCE) && !defined(_AIX) && !defined(__QNX__)) || defined(__hpux) || defined(__svr4__) /* XXX */ +# define NEED_UTSNAME +# include <sys/utsname.h> +#endif + +#ifdef __GNU__ +# include <netdb.h> +# undef SIOCGIFCONF +#else /* __GNU__ */ +# include <net/if.h> +# ifdef __svr4__ +# include <netdb.h> +# include <sys/sockio.h> +# include <sys/stropts.h> +# endif +# ifdef __EMX__ +# define link rename +# define chown(a,b,c) +# include <io.h> +# endif +#endif /* __GNU__ */ + +struct AuthProtocol { + unsigned short name_length; + const char *name; + void (*InitAuth)( unsigned short len, const char *name ); + Xauth *(*GetAuth)( unsigned short len, const char *name ); +#ifdef XDMCP + void (*GetXdmcpAuth)( struct protoDisplay *pdpy, + unsigned short authorizationNameLen, + const char *authorizationName ); +#endif + int inited; +}; + +#ifdef XDMCP +# define xdmcpauth(arg) , arg +#else +# define xdmcpauth(arg) +#endif + +static struct AuthProtocol AuthProtocols[] = { +{ (unsigned short)18, "MIT-MAGIC-COOKIE-1", + MitInitAuth, MitGetAuth xdmcpauth(NULL), 0 +}, +#ifdef HASXDMAUTH +{ (unsigned short)19, "XDM-AUTHORIZATION-1", + XdmInitAuth, XdmGetAuth xdmcpauth(XdmGetXdmcpAuth), 0 +}, +#endif +#ifdef SECURE_RPC +{ (unsigned short)9, "SUN-DES-1", + SecureRPCInitAuth, SecureRPCGetAuth xdmcpauth(NULL), 0 +}, +#endif +#ifdef K5AUTH +{ (unsigned short)14, "MIT-KERBEROS-5", + Krb5InitAuth, Krb5GetAuth xdmcpauth(NULL), 0 +}, +#endif +}; + +static struct AuthProtocol * +findProtocol( unsigned short name_length, const char *name ) +{ + unsigned i; + + for (i = 0; i < as(AuthProtocols); i++) + if (AuthProtocols[i].name_length == name_length && + memcmp( AuthProtocols[i].name, name, name_length ) == 0) + { + return &AuthProtocols[i]; + } + return (struct AuthProtocol *)0; +} + +int +ValidAuthorization( unsigned short name_length, const char *name ) +{ + if (findProtocol( name_length, name )) + return TRUE; + return FALSE; +} + +static Xauth * +GenerateAuthorization( unsigned short name_length, const char *name ) +{ + struct AuthProtocol *a; + Xauth *auth = 0; + + Debug( "GenerateAuthorization %s\n", name ); + if ((a = findProtocol( name_length, name ))) { + if (!a->inited) { + (*a->InitAuth)( name_length, name ); + a->inited = TRUE; + } + auth = (*a->GetAuth)( name_length, name ); + if (auth) { + Debug( "got %p (%d %.*s) %02[*hhx\n", auth, + auth->name_length, auth->name_length, auth->name, + auth->data_length, auth->data ); + } else + Debug( "got (null)\n" ); + } else + Debug( "unknown authorization %s\n", name ); + return auth; +} + +#ifdef XDMCP + +void +SetProtoDisplayAuthorization( struct protoDisplay *pdpy, + unsigned short authorizationNameLen, + const char *authorizationName ) +{ + struct AuthProtocol *a; + Xauth *auth; + + a = findProtocol( authorizationNameLen, authorizationName ); + pdpy->xdmcpAuthorization = pdpy->fileAuthorization = 0; + if (a) { + if (!a->inited) { + (*a->InitAuth)( authorizationNameLen, authorizationName ); + a->inited = TRUE; + } + if (a->GetXdmcpAuth) { + (*a->GetXdmcpAuth)( pdpy, authorizationNameLen, authorizationName ); + auth = pdpy->xdmcpAuthorization; + } else { + auth = (*a->GetAuth)( authorizationNameLen, authorizationName ); + pdpy->fileAuthorization = auth; + pdpy->xdmcpAuthorization = 0; + } + if (auth) + Debug( "got %p (%d %.*s)\n", auth, + auth->name_length, auth->name_length, auth->name ); + else + Debug( "got (null)\n" ); + } +} + +#endif /* XDMCP */ + +void +CleanUpFileName( const char *src, char *dst, int len ) +{ + while (*src) { + if (--len <= 0) + break; + switch (*src & 0x7f) { + case '/': + *dst++ = '_'; + break; + case '-': + *dst++ = '.'; + break; + default: + *dst++ = (*src & 0x7f); + } + ++src; + } + *dst = '\0'; +} + + +static FILE * +fdOpenW( int fd ) +{ + FILE *f; + + if (fd >= 0) { + if ((f = fdopen( fd, "w" ))) + return f; + close( fd ); + } + return 0; +} + +static FILE * +mkTempFile( char *nambuf, int namelen ) +{ + FILE *f; + int r; + + for (r = 0; r < 100; r++) { + randomStr( nambuf + namelen ); + if ((f = fdOpenW( open( nambuf, O_WRONLY | O_CREAT | O_EXCL, 0600 ) ))) + return f; + if (errno != EEXIST) + break; + } + return 0; +} + +#define NAMELEN 255 + +static FILE * +MakeServerAuthFile( struct display *d ) +{ + FILE *f; + int i; + char cleanname[NAMELEN], nambuf[NAMELEN+128]; + + /* + * Some paranoid, but still not sufficient (DoS was still possible) + * checks used to be here. I removed all this stuff because + * a) authDir is supposed to be /var/run/xauth (=safe) or similar and + * b) even if it's not (say, /tmp), we create files safely (hopefully). + */ + if (mkdir( authDir, 0755 ) < 0 && errno != EEXIST) + return 0; + CleanUpFileName( d->name, cleanname, NAMELEN - 8 ); + i = sprintf( nambuf, "%s/A%s-", authDir, cleanname ); + if ((f = mkTempFile( nambuf, i ))) { + StrDup( &d->authFile, nambuf ); + return f; + } + return 0; +} + +int +SaveServerAuthorizations( struct display *d, Xauth **auths, int count ) +{ + FILE *auth_file; + int i; + + if (!d->authFile && d->clientAuthFile && *d->clientAuthFile) + StrDup( &d->authFile, d->clientAuthFile ); + if (d->authFile) { + if (!(auth_file = fdOpenW( creat( d->authFile, 0600 ) ))) { + LogError( "Cannot open X server authorization file %s\n", d->authFile ); + free( d->authFile ); + d->authFile = NULL; + return FALSE; + } + } else { + if (!(auth_file = MakeServerAuthFile( d ))) { + LogError( "Cannot create X server authorization file\n" ); + return FALSE; + } + } + Debug( "file: %s auth: %p\n", d->authFile, auths ); + for (i = 0; i < count; i++) { + /* + * User-based auths may not have data until + * a user logs in. In which case don't write + * to the auth file so xrdb and setup programs don't fail. + */ + if (auths[i]->data_length > 0) + if (!XauWriteAuth( auth_file, auths[i] ) || + fflush( auth_file ) == EOF) + { + fclose( auth_file ); + LogError( "Cannot write X server authorization file %s\n", + d->authFile ); + free( d->authFile ); + d->authFile = NULL; + return FALSE; + } + } + fclose( auth_file ); + return TRUE; +} + +void +SetLocalAuthorization( struct display *d ) +{ + Xauth *auth, **auths; + int i, j; + + if (d->authorizations) + { + for (i = 0; i < d->authNum; i++) + XauDisposeAuth( d->authorizations[i] ); + free( (char *)d->authorizations ); + d->authorizations = (Xauth **)NULL; + d->authNum = 0; + } + Debug( "SetLocalAuthorization %s, auths %[s\n", d->name, d->authNames ); + if (!d->authNames) + return; + for (i = 0; d->authNames[i]; i++) + ; + d->authNameNum = i; + if (d->authNameLens) + free( (char *)d->authNameLens ); + d->authNameLens = (unsigned short *)Malloc( d->authNameNum * sizeof(unsigned short) ); + if (!d->authNameLens) + return; + for (i = 0; i < d->authNameNum; i++) + d->authNameLens[i] = strlen( d->authNames[i] ); + auths = (Xauth **)Malloc( d->authNameNum * sizeof(Xauth *) ); + if (!auths) + return; + j = 0; + for (i = 0; i < d->authNameNum; i++) { + auth = GenerateAuthorization( d->authNameLens[i], d->authNames[i] ); + if (auth) + auths[j++] = auth; + } + if (SaveServerAuthorizations( d, auths, j )) { + d->authorizations = auths; + d->authNum = j; + } else { + for (i = 0; i < j; i++) + XauDisposeAuth( auths[i] ); + free( (char *)auths ); + } +} + +/* + * Set the authorization to use for xdm's initial connection + * to the X server. Cannot use user-based authorizations + * because no one has logged in yet, so we don't have any + * user credentials. + * Well, actually we could use SUN-DES-1 because we tell the server + * to allow root in. This is bogus and should be fixed. + */ +void +SetAuthorization( struct display *d ) +{ + register Xauth **auth = d->authorizations; + int i; + + for (i = 0; i < d->authNum; i++) { + if (auth[i]->name_length == 9 && + memcmp( auth[i]->name, "SUN-DES-1", 9 ) == 0) + continue; + if (auth[i]->name_length == 14 && + memcmp( auth[i]->name, "MIT-KERBEROS-5", 14 ) == 0) + continue; + XSetAuthorization( auth[i]->name, (int)auth[i]->name_length, + auth[i]->data, (int)auth[i]->data_length ); + } +} + +static int +openFiles( const char *name, char *new_name, FILE **oldp, FILE **newp ) +{ + strcat( strcpy( new_name, name ), "-n" ); + if (!(*newp = + fdOpenW( creat( new_name, 0600 ) ))) { + Debug( "can't open new file %s\n", new_name ); + return 0; + } + *oldp = fopen( name, "r" ); + Debug( "opens succeeded %s %s\n", name, new_name ); + return 1; +} + +struct addrList { + struct addrList *next; + unsigned short family, address_length, number_length; + char data[1]; +}; + +static struct addrList *addrs; + +static void +initAddrs( void ) +{ + addrs = 0; +} + +static void +doneAddrs( void ) +{ + struct addrList *a, *n; + for (a = addrs; a; a = n) { + n = a->next; + free( a ); + } +} + +static void +saveEntry( Xauth *auth ) +{ + struct addrList *new; + + if (!(new = Malloc( offsetof(struct addrList, data) + + auth->address_length + auth->number_length ))) + return; + new->address_length = auth->address_length; + new->number_length = auth->number_length; + memcpy( new->data, auth->address, (int)auth->address_length ); + memcpy( new->data + (int)auth->address_length, + auth->number, (int)auth->number_length ); + new->family = auth->family; + new->next = addrs; + addrs = new; +} + +static int +checkEntry( Xauth *auth ) +{ + struct addrList *a; + + for (a = addrs; a; a = a->next) + if (a->family == auth->family && + a->address_length == auth->address_length && + !memcmp( a->data, auth->address, auth->address_length ) && + a->number_length == auth->number_length && + !memcmp( a->data + a->address_length, + auth->number, auth->number_length )) + return 1; + return 0; +} + +static void +writeAuth( FILE *file, Xauth *auth, int *ok ) +{ + if (debugLevel & DEBUG_AUTH) /* normally too verbose */ + Debug( "writeAuth: doWrite = %d\n" + "family: %d\n" + "addr: %02[*:hhx\n" + "number: %02[*:hhx\n" + "name: %02[*:hhx\n" + "data: %02[*:hhx\n", + ok != 0, auth->family, + auth->address_length, auth->address, + auth->number_length, auth->number, + auth->name_length, auth->name, + auth->data_length, auth->data ); + if (ok && !XauWriteAuth( file, auth )) + *ok = FALSE; +} + +static void +writeAddr( int family, int addr_length, char *addr, + FILE *file, Xauth *auth, int *ok ) +{ + auth->family = (unsigned short)family; + auth->address_length = addr_length; + auth->address = addr; + Debug( "writeAddr: writing and saving an entry\n" ); + writeAuth( file, auth, ok ); + if (!checkEntry( auth )) + saveEntry( auth ); +} + +static void +DefineLocal( FILE *file, Xauth *auth, int *ok ) +{ +#if !defined(NEED_UTSNAME) || defined(__hpux) + char displayname[100]; +#endif +#ifdef NEED_UTSNAME + struct utsname name; +#endif + + /* stolen from xinit.c */ + +/* Make sure this produces the same string as _XGetHostname in lib/X/XlibInt.c. + * Otherwise, Xau will not be able to find your cookies in the Xauthority file. + * + * Note: POSIX says that the ``nodename'' member of utsname does _not_ have + * to have sufficient information for interfacing to the network, + * and so, you may be better off using gethostname (if it exists). + */ + +#ifdef NEED_UTSNAME + + /* hpux: + * Why not use gethostname()? Well, at least on my system, I've had to + * make an ugly kernel patch to get a name longer than 8 characters, and + * uname() lets me access to the whole string (it smashes release, you + * see), whereas gethostname() kindly truncates it for me. + */ + uname( &name ); + writeAddr( FamilyLocal, strlen( name.nodename ), name.nodename, + file, auth, ok ); +#endif + +#if !defined(NEED_UTSNAME) || defined(__hpux) + /* _AIX: + * In _AIX, _POSIX_SOURCE is defined, but uname gives only first + * field of hostname. Thus, we use gethostname instead. + */ + + /* + * For HP-UX, HP's Xlib expects a fully-qualified domain name, which + * is achieved by using gethostname(). For compatability, we must + * also still create the entry using uname() above. + */ + displayname[0] = 0; + if (!gethostname( displayname, sizeof(displayname) )) + displayname[sizeof(displayname) - 1] = 0; + +# ifdef NEED_UTSNAME + /* + * If gethostname and uname both returned the same name, + * do not write a duplicate entry. + */ + if (strcmp( displayname, name.nodename )) +# endif + writeAddr( FamilyLocal, strlen( displayname ), displayname, + file, auth, ok ); +#endif +} + +#ifdef SYSV_SIOCGIFCONF + +/* Deal with different SIOCGIFCONF ioctl semantics on SYSV, SVR4 */ + +int +ifioctl (int fd, int cmd, char *arg) +{ + struct strioctl ioc; + int ret; + + bzero( (char *)&ioc, sizeof(ioc) ); + ioc.ic_cmd = cmd; + ioc.ic_timout = 0; + if (cmd == SIOCGIFCONF) { + ioc.ic_len = ((struct ifconf *)arg)->ifc_len; + ioc.ic_dp = ((struct ifconf *)arg)->ifc_buf; + } else { + ioc.ic_len = sizeof(struct ifreq); + ioc.ic_dp = arg; + } + ret = ioctl( fd, I_STR, (char *)&ioc ); + if (ret >= 0 && cmd == SIOCGIFCONF) + ((struct ifconf *)arg)->ifc_len = ioc.ic_len; + return (ret); +} + +#endif /* SYSV_SIOCGIFCONF */ + +#ifdef HAVE_GETIFADDRS +# include <ifaddrs.h> + +static void +DefineSelf( FILE *file, Xauth *auth, int *ok ) +{ + struct ifaddrs *ifap, *ifr; + char *addr; + int family, len; + + if (getifaddrs( &ifap ) < 0) + return; + for (ifr = ifap; ifr; ifr = ifr->ifa_next) { + if (!ifr->ifa_addr) + continue; + family = ConvertAddr( (char *)(ifr->ifa_addr), &len, &addr ); + if (family == -1 || family == FamilyLocal) + continue; + /* + * don't write out 'localhost' entries, as + * they may conflict with other local entries. + * DefineLocal will always be called to add + * the local entry anyway, so this one can + * be tossed. + */ + if (family == FamilyInternet && + addr[0] == 127 && addr[1] == 0 && addr[2] == 0 && addr[3] == 1) + { + Debug( "Skipping localhost address\n" ); + continue; + } +# if defined(IPv6) && defined(AF_INET6) + if (family == FamilyInternet6) { + if (IN6_IS_ADDR_LOOPBACK( ((struct in6_addr *)addr) )) { + Debug( "Skipping IPv6 localhost address\n" ); + continue; + } + /* Also skip XDM-AUTHORIZATION-1 */ + if (auth->name_length == 19 && + !memcmp( auth->name, "XDM-AUTHORIZATION-1", 19 )) { + Debug( "Skipping IPv6 XDM-AUTHORIZATION-1\n" ); + continue; + } + } +# endif + writeAddr( family, len, addr, file, auth, ok ); + } + freeifaddrs( ifap ); +} +#else /* GETIFADDRS */ + +#if defined(STREAMSCONN) && !defined(SYSV_SIOCGIFCONF) && !defined(WINTCP) + +#include <tiuser.h> + +/* Define this host for access control. Find all the hosts the OS knows about + * for this fd and add them to the selfhosts list. + * TLI version, written without sufficient documentation. + */ +static void +DefineSelf( int fd, FILE *file, Xauth *auth, int *ok ) +{ + struct netbuf netb; + char addrret[1024]; /* easier than t_alloc */ + + netb.maxlen = sizeof(addrret); + netb.buf = addrret; + if (t_getname( fd, &netb, LOCALNAME ) == -1) + t_error( "t_getname" ); + /* what a kludge */ + writeAddr( FamilyInternet, 4, netb.buf+4, file, auth, ok ); +} + +#else + +#ifdef WINTCP /* NCR with Wollongong TCP */ + +#include <stropts.h> +#include <tiuser.h> + +#include <sys/stream.h> +#include <net/if.h> +#include <netinet/ip.h> +#include <netinet/ip_var.h> +#include <netinet/in.h> +#include <netinet/in_var.h> + +static void +DefineSelf( int fd, FILE *file, Xauth *auth, int *ok ) +{ + /* + * The Wollongong drivers used by NCR SVR4/MP-RAS don't understand the + * socket IO calls that most other drivers seem to like. Because of + * this, this routine must be special cased for NCR. Eventually, + * this will be cleared up. + */ + + struct ipb ifnet; + struct in_ifaddr ifaddr; + struct strioctl str; + unsigned char *addr; + int len, ipfd; + + if ((ipfd = open( "/dev/ip", O_RDWR, 0 )) < 0) { + LogError( "Trouble getting interface configuration\n" ); + return; + } + + /* Indicate that we want to start at the begining */ + ifnet.ib_next = (struct ipb *)1; + + while (ifnet.ib_next) { + str.ic_cmd = IPIOC_GETIPB; + str.ic_timout = 0; + str.ic_len = sizeof(struct ipb); + str.ic_dp = (char *)&ifnet; + + if (ioctl( ipfd, (int)I_STR, (char *)&str ) < 0) { + close( ipfd ); + LogError( "Trouble getting interface configuration\n" ); + return; + } + + ifaddr.ia_next = (struct in_ifaddr *)ifnet.if_addrlist; + str.ic_cmd = IPIOC_GETINADDR; + str.ic_timout = 0; + str.ic_len = sizeof(struct in_ifaddr); + str.ic_dp = (char *)&ifaddr; + + if (ioctl( ipfd, (int)I_STR, (char *)&str ) < 0) { + close( ipfd ); + LogError( "Trouble getting interface configuration\n" ); + return; + } + + /* + * Ignore the 127.0.0.1 entry. + */ + if (IA_SIN( &ifaddr )->sin_addr.s_addr == htonl( 0x7f000001 ) ) + continue; + + writeAddr( FamilyInternet, 4, (char *)&(IA_SIN( &ifaddr )->sin_addr), + file, auth, ok ); + + } + close( ipfd ); +} + +#else /* WINTCP */ + +/* Solaris provides an extended interface SIOCGLIFCONF. Other systems + * may have this as well, but the code has only been tested on Solaris + * so far, so we only enable it there. Other platforms may be added as + * needed. + * + * Test for Solaris commented out -- TSI @ UQV 2003.06.13 + */ +#ifdef SIOCGLIFCONF +/* #if defined(sun) */ +# define USE_SIOCGLIFCONF +/* #endif */ +#endif + +#if defined(SIOCGIFCONF) || defined (USE_SIOCGLIFCONF) + +#if !defined(SYSV_SIOCGIFCONF) || defined(USE_SIOCGLIFCONF) +# define ifioctl ioctl +#endif + +#ifdef USE_SIOCGLIFCONF +# define ifr_type struct lifreq +#else +# define ifr_type struct ifreq +#endif + +/* Handle variable length ifreq in BNR2 and later */ +#ifdef VARIABLE_IFREQ +# define ifr_size(p) (sizeof(struct ifreq) + \ + (p->ifr_addr.sa_len > sizeof(p->ifr_addr) ? \ + p->ifr_addr.sa_len - sizeof(p->ifr_addr) : 0)) +#else +# define ifr_size(p) (sizeof(ifr_type)) +#endif + +#ifdef USE_SIOCGLIFCONF +# define IFC_IOCTL_REQ SIOCGLIFCONF +# define IFC_REQ(ifc) ifc.lifc_req +# define IFC_LEN(ifc) ifc.lifc_len +# define IFR_ADDR(ifr) ifr->lifr_addr +# define IFR_NAME(ifr) ifr->lifr_name +#else +# define IFC_IOCTL_REQ SIOCGIFCONF +# define IFC_REQ(ifc) ifc.ifc_req +# define IFC_LEN(ifc) ifc.ifc_len +# define IFR_ADDR(ifr) ifr->ifr_addr +# define IFR_NAME(ifr) ifr->ifr_name +#endif + +/* Define this host for access control. Find all the hosts the OS knows about + * for this fd and add them to the selfhosts list. + */ +static void +DefineSelf( int fd, FILE *file, Xauth *auth, int *ok ) +{ + char buf[2048], *cp, *cplim; + int len; + char *addr; + int family; + ifr_type *ifr; +#ifdef USE_SIOCGLIFCONF + int n; + void * bufptr = buf; + size_t buflen = sizeof(buf); + struct lifconf ifc; +# ifdef SIOCGLIFNUM + struct lifnum ifn; +# endif +#else + struct ifconf ifc; +#endif + +#if defined(SIOCGLIFNUM) && defined(SIOCGLIFCONF) + ifn.lifn_family = AF_UNSPEC; + ifn.lifn_flags = 0; + if (ioctl( fd, (int)SIOCGLIFNUM, (char *)&ifn ) < 0) + LogError( "Failed getting interface count\n" ); + if (buflen < (ifn.lifn_count * sizeof(struct lifreq))) { + buflen = ifn.lifn_count * sizeof(struct lifreq); + bufptr = Malloc( buflen ); + } +#endif + +#ifdef USE_SIOCGLIFCONF + ifc.lifc_family = AF_UNSPEC; + ifc.lifc_flags = 0; + ifc.lifc_len = buflen; + ifc.lifc_buf = bufptr; +#else + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = buf; +#endif + if (ifioctl (fd, IFC_IOCTL_REQ, (char *)&ifc) < 0) { + LogError( "Trouble getting network interface configuration\n" ); +#if defined(SIOCGLIFNUM) && defined(SIOCGLIFCONF) + if (bufptr != buf) + free( bufptr ); +#endif + return; + } + + cplim = (char *)IFC_REQ( ifc ) + IFC_LEN( ifc ); + + for (cp = (char *)IFC_REQ( ifc ); cp < cplim; cp += ifr_size (ifr)) { + ifr = (ifr_type *) cp; +#ifdef DNETCONN + /* + * this is ugly but SIOCGIFCONF returns decnet addresses in + * a different form from other decnet calls + */ + if (IFR_ADDR( ifr ).sa_family == AF_DECnet) { + len = sizeof(struct dn_naddr); + addr = (char *)IFR_ADDR( ifr ).sa_data; + family = FamilyDECnet; + } else +#endif + { + family = ConvertAddr( (char *)&IFR_ADDR( ifr ), &len, &addr ); + if (family < 0) + continue; + + if (len == 0) { + Debug( "skipping zero length address\n" ); + continue; + } + /* + * don't write out 'localhost' entries, as + * they may conflict with other local entries. + * DefineLocal will always be called to add + * the local entry anyway, so this one can + * be tossed. + */ + if (family == FamilyInternet && + addr[0] == 127 && addr[1] == 0 && + addr[2] == 0 && addr[3] == 1) + { + Debug( "skipping localhost address\n" ); + continue; + } +#if defined(IPv6) && defined(AF_INET6) + if (family == FamilyInternet6) { + if (IN6_IS_ADDR_LOOPBACK( ((struct in6_addr *)addr) )) { + Debug( "Skipping IPv6 localhost address\n" ); + continue; + } + /* Also skip XDM-AUTHORIZATION-1 */ + if (auth->name_length == 19 && + !memcmp( auth->name, "XDM-AUTHORIZATION-1", 19 )) { + Debug( "Skipping IPv6 XDM-AUTHORIZATION-1\n" ); + continue; + } + } +#endif + } + Debug( "DefineSelf: write network address, length %d\n", len ); + writeAddr( family, len, addr, file, auth, ok ); + } +#if defined(SIOCGLIFNUM) && defined(SIOCGLIFCONF) + if (bufptr != buf) + free( bufptr ); +#endif +} + +#else /* SIOCGIFCONF */ + +/* Define this host for access control. Find all the hosts the OS knows about + * for this fd and add them to the selfhosts list. + */ +static void +DefineSelf( int fd, int file, int auth, int *ok ) +{ + int len; + caddr_t addr; + int family; + + struct utsname name; + register struct hostent *hp; + + union { + struct sockaddr sa; + struct sockaddr_in in; + } saddr; + + struct sockaddr_in *inetaddr; + + /* hpux: + * Why not use gethostname()? Well, at least on my system, I've had to + * make an ugly kernel patch to get a name longer than 8 characters, and + * uname() lets me access to the whole string (it smashes release, you + * see), whereas gethostname() kindly truncates it for me. + */ + uname( &name ); + if ((hp = gethostbyname( name.nodename ))) { + saddr.sa.sa_family = hp->h_addrtype; + inetaddr = (struct sockaddr_in *)(&(saddr.sa)); + memmove( (char *)&(inetaddr->sin_addr), (char *)hp->h_addr, + (int)hp->h_length ); + if ( (family = ConvertAddr( &(saddr.sa), &len, &addr )) >= 0) + writeAddr( FamilyInternet, sizeof(inetaddr->sin_addr), + (char *)(&inetaddr->sin_addr), file, auth, ok ); + } +} + +#endif /* SIOCGIFCONF else */ +#endif /* WINTCP else */ +#endif /* STREAMSCONN && !SYSV_SIOCGIFCONF else */ +#endif /* HAVE_GETIFADDRS */ + +static void +setAuthNumber( Xauth *auth, const char *name ) +{ + char *colon; + char *dot; + + Debug( "setAuthNumber %s\n", name ); + colon = strrchr( name, ':' ); + if (colon) { + ++colon; + dot = strchr( colon, '.' ); + if (dot) + auth->number_length = dot - colon; + else + auth->number_length = strlen( colon ); + if (!StrNDup( &auth->number, colon, auth->number_length )) + auth->number_length = 0; + Debug( "setAuthNumber: %s\n", auth->number ); + } +} + +static void +writeLocalAuth( FILE *file, Xauth *auth, const char *name, int *ok ) +{ +#if defined(STREAMSCONN) || !defined(HAVE_GETIFADDRS) + int fd; +#endif + + Debug( "writeLocalAuth: %s %.*s\n", name, auth->name_length, auth->name ); + setAuthNumber( auth, name ); +# ifdef STREAMSCONN + fd = t_open( "/dev/tcp", O_RDWR, 0 ); + t_bind( fd, NULL, NULL ); + DefineSelf( fd, file, auth, ok ); + t_unbind( fd ); + t_close( fd ); +# elif defined(HAVE_GETIFADDRS) + DefineSelf( file, auth, ok ); +# else +# ifdef TCPCONN +# if defined(IPv6) && defined(AF_INET6) + fd = socket( AF_INET6, SOCK_STREAM, 0 ); + if (fd < 0) +# endif + fd = socket( AF_INET, SOCK_STREAM, 0 ); + DefineSelf( fd, file, auth, ok ); + close( fd ); +# endif +# ifdef DNETCONN + fd = socket( AF_DECnet, SOCK_STREAM, 0 ); + DefineSelf( fd, file, auth, ok ); + close( fd ); +# endif +# endif /* HAVE_GETIFADDRS */ + DefineLocal( file, auth, ok ); +} + +#ifdef XDMCP + +/* + * Call ConvertAddr(), and if it returns an IPv4 localhost, convert it + * to a local display name. Meets the _XTransConvertAddress's localhost + * hack. + */ + +static int +ConvertAuthAddr( char *saddr, int *len, char **addr ) +{ + int ret = ConvertAddr( saddr, len, addr ); + if (ret == FamilyInternet && + ((struct in_addr *)*addr)->s_addr == htonl( 0x7F000001L )) + ret = FamilyLocal; + return ret; +} + +static void +writeRemoteAuth( FILE *file, Xauth *auth, XdmcpNetaddr peer, int peerlen, + const char *name, int *ok ) +{ + int family = FamilyLocal; + char *addr; + + Debug( "writeRemoteAuth: %s %.*s\n", name, auth->name_length, auth->name ); + if (!peer || peerlen < 2) + return; + setAuthNumber( auth, name ); + family = ConvertAuthAddr( peer, &peerlen, &addr ); + Debug( "writeRemoteAuth: family %d\n", family ); + if (family != FamilyLocal) { + Debug( "writeRemoteAuth: %d, %02[*:hhx\n", + family, peerlen, addr ); + writeAddr( family, peerlen, addr, file, auth, ok ); + } else + writeLocalAuth( file, auth, name, ok ); +} + +#endif /* XDMCP */ + +#define NBSIZE 1024 + +static void +startUserAuth( char *buf, char *nbuf, FILE **old, FILE **new ) +{ + const char *home; + int lockStatus; + + initAddrs(); + *new = 0; + if ((home = getEnv( userEnviron, "HOME" )) && strlen( home ) < NBSIZE - 12) { + sprintf( buf, "%s/.Xauthority", home ); + Debug( "XauLockAuth %s\n", buf ); + lockStatus = XauLockAuth( buf, 1, 2, 10 ); + Debug( "lock is %d\n", lockStatus ); + if (lockStatus == LOCK_SUCCESS) + if (!openFiles( buf, nbuf, old, new )) + XauUnlockAuth( buf ); + } + if (!*new) + LogWarn( "Can't update authorization file in home dir %s\n", home ); +} + +static int +endUserAuth( FILE *old, FILE *new, const char *nname, int ok ) +{ + Xauth *entry; + struct stat statb; + + if (old) { + if (fstat( fileno( old ), &statb ) != -1) + chmod( nname, (int)(statb.st_mode & 0777) ); + /*SUPPRESS 560*/ + while ((entry = XauReadAuth( old ))) { + if (!checkEntry( entry )) { + Debug( "writing an entry\n" ); + writeAuth( new, entry, &ok ); + } + XauDisposeAuth( entry ); + } + fclose( old ); + } + if (fclose( new ) == EOF) + ok = FALSE; + doneAddrs(); + return ok; +} + +static void +undoUserAuth( const char *name, const char *new_name ) +{ + LogWarn( "Can't save user authorization in home dir\n" ); + unlink( new_name ); + XauUnlockAuth( name ); +} + +static char * +moveUserAuth( const char *name, char *new_name, char *envname ) +{ + if (unlink( name )) + Debug( "unlink %s failed\n", name ); + if (link( new_name, name )) { + Debug( "link failed %s %s\n", new_name, name ); + LogError( "Can't move user authorization into place\n" ); + envname = new_name; + } else { + Debug( "new authorization moved into place\n" ); + unlink( new_name ); + } + XauUnlockAuth( name ); + return envname; +} + +void +SetUserAuthorization( struct display *d ) +{ + FILE *old, *new; + char *name; + char *envname; + Xauth **auths; + int i, ok; + int magicCookie; + int data_len; + char name_buf[NBSIZE], new_name[NBSIZE + 2]; + + Debug( "SetUserAuthorization\n" ); + auths = d->authorizations; + if (auths) { + startUserAuth( name_buf, new_name, &old, &new ); + if (new) { + envname = 0; + name = name_buf; + } else { + fallback: + if (strlen( d->userAuthDir ) >= NBSIZE - 13) + return; + /* + * Note, that we don't lock the auth file here, as it's + * temporary - we can assume, that we are the only ones + * knowing about this file anyway. + */ + i = sprintf( name_buf, "%s/.Xauth", d->userAuthDir ); + new = mkTempFile( name_buf, i ); + if (!new) { + LogError( "Can't create authorization file in %s\n", + d->userAuthDir ); + return; + } + name = 0; + envname = name_buf; + old = 0; + } + ok = TRUE; + Debug( "%d authorization protocols for %s\n", d->authNum, d->name ); + /* + * Write MIT-MAGIC-COOKIE-1 authorization first, so that + * R4 clients which only knew that, and used the first + * matching entry will continue to function + */ + magicCookie = -1; + for (i = 0; i < d->authNum; i++) { + if (auths[i]->name_length == 18 && + !memcmp( auths[i]->name, "MIT-MAGIC-COOKIE-1", 18 )) + { + magicCookie = i; + if ((d->displayType & d_location) == dLocal) + writeLocalAuth( new, auths[i], d->name, &ok ); +#ifdef XDMCP + else + writeRemoteAuth( new, auths[i], (XdmcpNetaddr)d->peer.data, + d->peer.length, d->name, &ok ); +#endif + break; + } + } + /* now write other authorizations */ + for (i = 0; i < d->authNum; i++) { + if (i != magicCookie) { + data_len = auths[i]->data_length; + /* client will just use default Kerberos cache, so don't + * even write cache info into the authority file. + */ + if (auths[i]->name_length == 14 && + !strncmp( auths[i]->name, "MIT-KERBEROS-5", 14 )) + auths[i]->data_length = 0; + if ((d->displayType & d_location) == dLocal) + writeLocalAuth( new, auths[i], d->name, &ok ); +#ifdef XDMCP + else + writeRemoteAuth( new, auths[i], (XdmcpNetaddr)d->peer.data, + d->peer.length, d->name, &ok ); +#endif + auths[i]->data_length = data_len; + } + } + if (!endUserAuth( old, new, new_name, ok )) { + if (!name) { + LogError( "Can't save user authorization\n" ); + return; + } + undoUserAuth( name, new_name ); + initAddrs(); + goto fallback; + } + if (name) + envname = moveUserAuth( name, new_name, envname ); + if (envname) { + userEnviron = setEnv( userEnviron, "XAUTHORITY", envname ); + systemEnviron = setEnv( systemEnviron, "XAUTHORITY", envname ); + } + /* a chown() used to be here, but this code runs as user anyway */ + } + Debug( "done SetUserAuthorization\n" ); +} + +void +RemoveUserAuthorization( struct display *d ) +{ + Xauth **auths; + FILE *old, *new; + int i; + char name[NBSIZE], new_name[NBSIZE + 2]; + + if (!(auths = d->authorizations)) + return; + Debug( "RemoveUserAuthorization\n" ); + startUserAuth( name, new_name, &old, &new ); + if (new) { + for (i = 0; i < d->authNum; i++) { + if ((d->displayType & d_location) == dLocal) + writeLocalAuth( new, auths[i], d->name, 0 ); +#ifdef XDMCP + else + writeRemoteAuth( new, auths[i], (XdmcpNetaddr)d->peer.data, + d->peer.length, d->name, 0 ); +#endif + } + if (endUserAuth( old, new, new_name, TRUE )) + (void)moveUserAuth( name, new_name, 0 ); + else + undoUserAuth( name, new_name ); + } + Debug( "done RemoveUserAuthorization\n" ); +} diff --git a/tdm/backend/bootman.c b/tdm/backend/bootman.c new file mode 100644 index 000000000..291164ea7 --- /dev/null +++ b/tdm/backend/bootman.c @@ -0,0 +1,277 @@ +/* + +Copyright 2005 Stephan Kulow <[email protected]> +Copyright 2005 Oswald Buddenhagen <[email protected]> + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + * + * Boot options + */ + +#include "dm.h" +#include "dm_error.h" + +#include <string.h> +#include <unistd.h> +#include <stdio.h> +#include <ctype.h> + +extern char **environ; + +static int +getNull( char ***opts ATTR_UNUSED, int *def ATTR_UNUSED, int *cur ATTR_UNUSED ) +{ + return BO_NOMAN; +} + +static int +setNull( const char *opt ATTR_UNUSED, SdRec *sdr ATTR_UNUSED ) +{ + return BO_NOMAN; +} + +static char * +match( char *obuf, int *blen, const char *key, int klen ) +{ + char *buf = obuf; + if (memcmp( buf, key, klen ) || !isspace( buf[klen] )) + return 0; + buf += klen + 1; + for (; isspace( *buf ); buf++); + if (!*buf) + return 0; + *blen -= buf - obuf; + return buf; +} + +#define GRUB_MENU "/boot/grub/menu.lst" + +static char *grub; + +static int +getGrub( char ***opts, int *def, int *cur ) +{ + FILE *f; + char *ptr, *linp; + int len; + char line[1000]; + + if (!grub && !(grub = locate( "grub-set-default" ))) + return BO_NOMAN; + + *def = 0; + *cur = -1; + *opts = initStrArr( 0 ); + + if (!(f = fopen( GRUB_MENU, "r" ))) + return errno == ENOENT ? BO_NOMAN : BO_IO; + while ((len = fGets( line, sizeof(line), f )) != -1) { + for (linp = line; isspace(*linp); linp++, len--); + if ((ptr = match( linp, &len, "default", 7 ))) + *def = atoi( ptr ); + else if ((ptr = match( linp, &len, "title", 5 ))) { + for (; isspace( ptr[len - 1] ); len--); + *opts = addStrArr( *opts, ptr, len ); + } + } + fclose( f ); + + return BO_OK; +} + +static int +setGrub( const char *opt, SdRec *sdr ) +{ + FILE *f; + char *ptr; + int len, i; + char line[1000]; + + if (!(f = fopen( GRUB_MENU, "r" ))) + return errno == ENOENT ? BO_NOMAN : BO_IO; + for (i = 0; (len = fGets( line, sizeof(line), f )) != -1; ) + if ((ptr = match( line, &len, "title", 5 ))) { + if (!strcmp( ptr, opt )) { + fclose( f ); + sdr->osindex = i; + sdr->bmstamp = mTime( GRUB_MENU ); + return BO_OK; + } + i++; + } + fclose( f ); + return BO_NOENT; +} + +static void +commitGrub( void ) +{ + char command[256]; + + if (sdRec.bmstamp != mTime( GRUB_MENU ) && + setGrub( sdRec.osname, &sdRec ) != BO_OK) + return; + + sprintf(command, "%s %d", grub, sdRec.osindex); + system(command); +} + +static char *lilo; + +static int +getLilo( char ***opts, int *def, int *cur ) +{ + FILE *f; + int cdef, pid, len, ret = BO_OK; + static const char *args[5] = { 0, "-w", "-v", "-q", 0 }; + char buf[256], next[256]; + + if (!lilo && !(lilo = locate( "lilo" ))) + return BO_NOMAN; + + args[0] = lilo; + if (!(f = pOpen( (char **)args, 'r', &pid ))) + return BO_IO; + *opts = 0; + next[0] = 0; + for (;;) { + if ((len = fGets( buf, sizeof(buf), f)) == -1) { + ret = BO_NOMAN; + goto out; + } + if (!memcmp( buf, "Images:", 7 )) + break; +#define Ldeflin " Default boot command line:" + if (!memcmp( buf, Ldeflin, strlen(Ldeflin) )) { + memcpy( next, buf + strlen(Ldeflin) + 2, len - strlen(Ldeflin) - 3 ); + next[len - strlen(Ldeflin) - 3] = 0; + } + } + cdef = *def = 0; + *cur = -1; + *opts = initStrArr( 0 ); + while ((len = fGets( buf, sizeof(buf), f)) != -1) + if (buf[0] == ' ' && buf[1] == ' ' && buf[2] != ' ') { + if (buf[len - 1] == '*') { + *def = cdef; + len--; + } + for (; buf[len - 1] == ' '; len--); + *opts = addStrArr( *opts, buf + 2, len - 2 ); + if (!strcmp( (*opts)[cdef], next )) + *cur = cdef; + cdef++; + } + out: + if (pClose( f, pid )) { + if (*opts) + freeStrArr( *opts ); + return BO_IO; + } + return ret; +} + +static int +setLilo( const char *opt, SdRec *sdr ATTR_UNUSED ) +{ + char **opts; + int def, cur, ret, i; + + if ((ret = getLilo( &opts, &def, &cur )) != BO_OK) + return ret; + if (!*opt) + opt = 0; + else { + for (i = 0; opts[i]; i++) + if (!strcmp( opts[i], opt )) + goto oke; + freeStrArr( opts ); + return BO_NOENT; + } + oke: + freeStrArr( opts ); + return BO_OK; +} + +static void +commitLilo( void ) +{ + static const char *args[5] = { 0, "-w", "-R", 0, 0 }; + + args[0] = lilo; + args[3] = sdRec.osname; + runAndWait( (char **)args, environ ); +} + +static struct { + int (*get)( char ***, int *, int * ); + int (*set)( const char *, SdRec * ); + void (*commit)( void ); +} bootOpts[] = { + { getNull, setNull, 0 }, + { getGrub, setGrub, commitGrub }, + { getLilo, setLilo, commitLilo }, +}; + +int +getBootOptions( char ***opts, int *def, int *cur ) +{ + return bootOpts[bootManager].get( opts, def, cur ); +} + +int +setBootOption( const char *opt, SdRec *sdr ) +{ + int ret; + + if (sdr->osname) { + free( sdr->osname ); + sdr->osname = 0; + } + if (opt) { + if ((ret = bootOpts[bootManager].set( opt, sdr )) != BO_OK) + return ret; + if (!StrDup( &sdr->osname, opt )) + return BO_IO; /* BO_NOMEM */ + } + return BO_OK; +} + +void +commitBootOption( void ) +{ + if (sdRec.osname) { + bootOpts[bootManager].commit(); +/* + free( sdRec.osname ); + sdRec.osname = 0; +*/ + } +} + diff --git a/tdm/backend/choose.c b/tdm/backend/choose.c new file mode 100644 index 000000000..ead841228 --- /dev/null +++ b/tdm/backend/choose.c @@ -0,0 +1,1051 @@ +/* + +Copyright 1990, 1998 The Open Group +Copyright 2002-2004 Oswald Buddenhagen <[email protected]> + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + * + * chooser backend + */ + +#include <config.h> + +#ifdef XDMCP + +#include "dm.h" +#include "dm_error.h" +#include "dm_socket.h" + +#include <ctype.h> + +#ifdef __svr4__ +# include <sys/sockio.h> +#endif +#include <arpa/inet.h> + +#include <sys/ioctl.h> +#ifdef STREAMSCONN +# ifdef WINTCP /* NCR with Wollongong TCP */ +# include <netinet/ip.h> +# endif +# include <stropts.h> +# include <tiuser.h> +# include <netconfig.h> +# include <netdir.h> +#endif + +#if !defined(__GNU__) && !defined(__hpux) /* XXX __hpux might be wrong */ +# include <net/if.h> +#endif + +#include <netdb.h> + +typedef struct _IndirectUsers { + struct _IndirectUsers *next; + ARRAY8 client; + CARD16 connectionType; +} IndirectUsersRec, *IndirectUsersPtr; + +static IndirectUsersPtr indirectUsers; + +int +RememberIndirectClient( ARRAY8Ptr clientAddress, CARD16 connectionType ) +{ + IndirectUsersPtr i; + + for (i = indirectUsers; i; i = i->next) + if (XdmcpARRAY8Equal( clientAddress, &i->client ) && + connectionType == i->connectionType) + return 1; + if (!(i = (IndirectUsersPtr)Malloc( sizeof(IndirectUsersRec) ))) + return 0; + if (!XdmcpCopyARRAY8( clientAddress, &i->client )) { + free( (char *)i ); + return 0; + } + i->connectionType = connectionType; + i->next = indirectUsers; + indirectUsers = i; + return 1; +} + +void +ForgetIndirectClient( ARRAY8Ptr clientAddress, CARD16 connectionType ) +{ + IndirectUsersPtr *i, ni; + + for (i = &indirectUsers; *i; i = &(*i)->next) + if (XdmcpARRAY8Equal( clientAddress, &(*i)->client ) && + connectionType == (*i)->connectionType) + { + ni = (*i)->next; + XdmcpDisposeARRAY8( &(*i)->client ); + free( (char *)(*i) ); + (*i) = ni; + break; + } +} + +int +IsIndirectClient( ARRAY8Ptr clientAddress, CARD16 connectionType ) +{ + IndirectUsersPtr i; + + for (i = indirectUsers; i; i = i->next) + if (XdmcpARRAY8Equal( clientAddress, &i->client ) && + connectionType == i->connectionType) + return 1; + return 0; +} + +typedef struct _Choices { + struct _Choices *next; + ARRAY8 client; + CARD16 connectionType; + ARRAY8 choice; + Time_t time; +} ChoiceRec, *ChoicePtr; + +static ChoicePtr choices; + +ARRAY8Ptr +IndirectChoice( ARRAY8Ptr clientAddress, CARD16 connectionType ) +{ + ChoicePtr c, next, prev; + + prev = 0; + for (c = choices; c; c = next) { + next = c->next; + Debug( "choice checking timeout: %ld >? %d\n", + (long)(now - c->time), choiceTimeout ); + if (now - c->time > (Time_t)choiceTimeout) { + Debug( "timeout choice %ld > %d\n", + (long)(now - c->time), choiceTimeout ); + if (prev) + prev->next = next; + else + choices = next; + XdmcpDisposeARRAY8( &c->client ); + XdmcpDisposeARRAY8( &c->choice ); + free( (char *)c ); + } else { + if (XdmcpARRAY8Equal( clientAddress, &c->client ) && + connectionType == c->connectionType) + return &c->choice; + prev = c; + } + } + return 0; +} + +int +RegisterIndirectChoice( ARRAY8Ptr clientAddress, CARD16 connectionType, + ARRAY8Ptr choice ) +{ + ChoicePtr c; + int insert; +#if 0 + int found = 0; +#endif + + Debug( "got indirect choice back\n" ); + for (c = choices; c; c = c->next) { + if (XdmcpARRAY8Equal( clientAddress, &c->client ) && + connectionType == c->connectionType) { +#if 0 + found = 1; +#endif + break; + } + } +#if 0 + if (!found) + return 0; +#endif + + insert = 0; + if (!c) { + insert = 1; + c = (ChoicePtr)Malloc( sizeof(ChoiceRec) ); + if (!c) + return 0; + c->connectionType = connectionType; + if (!XdmcpCopyARRAY8( clientAddress, &c->client )) { + free( (char *)c ); + return 0; + } + } else + XdmcpDisposeARRAY8( &c->choice ); + if (!XdmcpCopyARRAY8( choice, &c->choice )) { + XdmcpDisposeARRAY8( &c->client ); + free( (char *)c ); + return 0; + } + if (insert) { + c->next = choices; + choices = c; + } + c->time = now; + return 1; +} + +#if 0 +static void +RemoveIndirectChoice( ARRAY8Ptr clientAddress, CARD16 connectionType ) +{ + ChoicePtr c, prev; + + prev = 0; + for (c = choices; c; c = c->next) { + if (XdmcpARRAY8Equal( clientAddress, &c->client ) && + connectionType == c->connectionType) + { + if (prev) + prev->next = c->next; + else + choices = c->next; + XdmcpDisposeARRAY8( &c->client ); + XdmcpDisposeARRAY8( &c->choice ); + free( (char *)c ); + return; + } + prev = c; + } +} +#endif + + +/* ####################### */ + + +typedef struct _hostAddr { + struct _hostAddr *next; + struct sockaddr *addr; + int addrlen; + xdmOpCode type; +} HostAddr; + +static HostAddr *hostAddrdb; + +typedef struct _hostName { + struct _hostName *next; + unsigned willing:1, alive:1; + ARRAY8 hostname, status; + CARD16 connectionType; + ARRAY8 hostaddr; +} HostName; + +static HostName *hostNamedb; + +static XdmcpBuffer directBuffer, broadcastBuffer; +static XdmcpBuffer buffer; + +static int socketFD; +#if defined(IPv6) && defined(AF_INET6) +static int socket6FD; +#endif + + +static void +doPingHosts() +{ + HostAddr *hosts; + + for (hosts = hostAddrdb; hosts; hosts = hosts->next) +#if defined(IPv6) && defined(AF_INET6) + XdmcpFlush( hosts->addr->sa_family == AF_INET6 ? socket6FD : socketFD, +#else + XdmcpFlush( socketFD, +#endif + hosts->type == QUERY ? &directBuffer : &broadcastBuffer, + (XdmcpNetaddr)hosts->addr, hosts->addrlen ); +} + +static int +addHostname( ARRAY8Ptr hostname, ARRAY8Ptr status, + struct sockaddr *addr, int will ) +{ + HostName **names, *name; + ARRAY8 hostAddr; + CARD16 connectionType; + + switch (addr->sa_family) { + case AF_INET: + hostAddr.data = + (CARD8 *)& ((struct sockaddr_in *)addr)->sin_addr; + hostAddr.length = 4; + connectionType = FamilyInternet; + break; +#if defined(IPv6) && defined(AF_INET6) + case AF_INET6: + hostAddr.data = + (CARD8 *)&((struct sockaddr_in6 *)addr)->sin6_addr; + hostAddr.length = 16; + connectionType = FamilyInternet6; + break; +#endif + default: + hostAddr.data = (CARD8 *)""; + hostAddr.length = 0; + connectionType = FamilyLocal; + break; + } + for (names = &hostNamedb; *names; names = &(*names)->next) { + name = *names; + if (connectionType == name->connectionType && + XdmcpARRAY8Equal( &hostAddr, &name->hostaddr )) + { + if (XdmcpARRAY8Equal( status, &name->status )) + return 0; + XdmcpDisposeARRAY8( &name->status ); + XdmcpDisposeARRAY8( hostname ); + + GSendInt( G_Ch_ChangeHost ); + goto gotold; + } + } + if (!(name = (HostName *)Malloc( sizeof(*name) ))) + return 0; + if (hostname->length) { + switch (addr->sa_family) { + case AF_INET: +#if defined(IPv6) && defined(AF_INET6) + case AF_INET6: +#endif + { + struct hostent *hostent; + char *host; + + hostent = gethostbyaddr( (char *)hostAddr.data, + hostAddr.length, addr->sa_family ); + if (hostent) { + XdmcpDisposeARRAY8( hostname ); + host = hostent->h_name; + XdmcpAllocARRAY8( hostname, strlen( host ) ); + memmove( hostname->data, host, hostname->length ); + } + } + } + } + if (!XdmcpAllocARRAY8( &name->hostaddr, hostAddr.length )) { + free( (char *)name ); + return 0; + } + memmove( name->hostaddr.data, hostAddr.data, hostAddr.length ); + name->connectionType = connectionType; + name->hostname = *hostname; + + *names = name; + name->next = 0; + + GSendInt( G_Ch_AddHost ); + gotold: + name->alive = 1; + name->willing = will; + name->status = *status; + + GSendInt( (int)name ); /* just an id */ + GSendNStr( (char *)name->hostname.data, name->hostname.length ); + GSendNStr( (char *)name->status.data, name->status.length ); + GSendInt( will ); + + return 1; +} + +static void +disposeHostname( HostName *host ) +{ + XdmcpDisposeARRAY8( &host->hostname ); + XdmcpDisposeARRAY8( &host->hostaddr ); + XdmcpDisposeARRAY8( &host->status ); + free( (char *)host ); +} + +static void +emptyHostnames( void ) +{ + HostName *host, *nhost; + + for (host = hostNamedb; host; host = nhost) { + nhost = host->next; + disposeHostname( host ); + } + hostNamedb = 0; +} + +static void +receivePacket( int sfd ) +{ + XdmcpHeader header; + ARRAY8 authenticationName; + ARRAY8 hostname; + ARRAY8 status; + int saveHostname = 0; +#if defined(IPv6) && defined(AF_INET6) + struct sockaddr_storage addr; +#else + struct sockaddr addr; +#endif + int addrlen; + + addrlen = sizeof(addr); + if (!XdmcpFill( sfd, &buffer, (XdmcpNetaddr)&addr, &addrlen )) + return; + if (!XdmcpReadHeader( &buffer, &header )) + return; + if (header.version != XDM_PROTOCOL_VERSION) + return; + hostname.data = 0; + status.data = 0; + authenticationName.data = 0; + switch (header.opcode) { + case WILLING: + if (XdmcpReadARRAY8( &buffer, &authenticationName ) && + XdmcpReadARRAY8( &buffer, &hostname ) && + XdmcpReadARRAY8( &buffer, &status )) { + if (header.length == 6 + authenticationName.length + + hostname.length + status.length) { + if (addHostname( &hostname, &status, + (struct sockaddr *)&addr, 1 )) + saveHostname = 1; + } + } + XdmcpDisposeARRAY8( &authenticationName ); + break; + case UNWILLING: + if (XdmcpReadARRAY8( &buffer, &hostname ) && + XdmcpReadARRAY8( &buffer, &status )) { + if (header.length == 4 + hostname.length + status.length) { + if (addHostname( &hostname, &status, + (struct sockaddr *)&addr, 0 )) + saveHostname = 1; + } + } + break; + default: + break; + } + if (!saveHostname) { + XdmcpDisposeARRAY8( &hostname ); + XdmcpDisposeARRAY8( &status ); + } +} + +static void +addHostaddr( HostAddr **hosts, struct sockaddr *addr, int len, xdmOpCode type ) +{ + HostAddr *host; + + Debug( "adding host %[*hhu, type %d\n", len, addr, type ); + for (host = *hosts; host; host = host->next) + if (host->type == type && host->addr->sa_family == addr->sa_family) + switch (addr->sa_family) { + case AF_INET: + { + struct sockaddr_in *na = (struct sockaddr_in *)addr; + struct sockaddr_in *oa = (struct sockaddr_in *)host->addr; + if (na->sin_port == oa->sin_port && + na->sin_addr.s_addr == oa->sin_addr.s_addr) + return; + break; + } +#if defined(IPv6) && defined(AF_INET6) + case AF_INET6: + { + struct sockaddr_in6 *na = (struct sockaddr_in6 *)addr; + struct sockaddr_in6 *oa = (struct sockaddr_in6 *)host->addr; + if (na->sin6_port == oa->sin6_port && + !memcmp( &na->sin6_addr, &oa->sin6_addr, 16 )) + return; + break; + } +#endif + default: /* ... */ + break; + } + Debug( " not dupe\n" ); + if (!(host = (HostAddr *)Malloc( sizeof(*host) ))) + return; + if (!(host->addr = (struct sockaddr *)Malloc( len ))) { + free( (char *)host ); + return; + } + memcpy( (char *)host->addr, (char *)addr, len ); + host->addrlen = len; + host->type = type; + host->next = *hosts; + *hosts = host; +} + +static void +registerHostaddr( struct sockaddr *addr, int len, xdmOpCode type ) +{ + addHostaddr( &hostAddrdb, addr, len, type ); +} + +static void +emptyPingHosts( void ) +{ + HostAddr *host, *nhost; + + for (host = hostAddrdb; host; host = nhost) { + nhost = host->next; + free( host->addr ); + free( host ); + } + hostAddrdb = 0; +} + +/* Handle variable length ifreq in BNR2 and later */ +#ifdef VARIABLE_IFREQ +# define ifr_size(p) \ + (sizeof(struct ifreq) + \ + (p->ifr_addr.sa_len > sizeof (p->ifr_addr) ? \ + p->ifr_addr.sa_len - sizeof (p->ifr_addr) : 0)) +#else +# define ifr_size(p) (sizeof(struct ifreq)) +#endif + +#define IFC_REQ(ifc) ifc.ifc_req + +#ifndef SYSV_SIOCGIFCONF +# define ifioctl ioctl +#endif + +static void +registerBroadcastForPing( void ) +{ + struct sockaddr_in in_addr; + +#ifdef __GNU__ + in_addr.sin_addr.s_addr = htonl( 0xFFFFFFFF ); + in_addr.sin_port = htons( XDM_UDP_PORT ); + registerHostaddr( (struct sockaddr *)&in_addr, sizeof(in_addr), + BROADCAST_QUERY ); +#else /* __GNU__ */ + struct ifconf ifc; + register struct ifreq *ifr; + struct sockaddr broad_addr; + char buf[2048], *cp, *cplim; +# ifdef WINTCP /* NCR with Wollongong TCP */ + int ipfd; + struct ifconf *ifcp; + struct strioctl ioc; + int n; + + ifcp = (struct ifconf *)buf; + ifcp->ifc_buf = buf + 4; + ifcp->ifc_len = sizeof(buf) - 4; + + if ((ipfd = open( "/dev/ip", O_RDONLY )) < 0) { + t_error( "RegisterBroadcastForPing() t_open(/dev/ip) failed" ); + return; + } + + ioc.ic_cmd = IPIOC_GETIFCONF; + ioc.ic_timout = 60; + ioc.ic_len = sizeof(buf); + ioc.ic_dp = (char *)ifcp; + + if (ioctl( ipfd, (int)I_STR, (char *)&ioc ) < 0) { + perror( "RegisterBroadcastForPing() ioctl(I_STR(IPIOC_GETIFCONF)) failed" ); + close( ipfd ); + return; + } + + for (ifr = ifcp->ifc_req, n = ifcp->ifc_len / sizeof(struct ifreq); + --n >= 0; ifr++) +# else /* WINTCP */ + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = buf; + if (ifioctl(socketFD, (int)SIOCGIFCONF, (char *)&ifc) < 0) + return; + + cplim = (char *)IFC_REQ( ifc ) + ifc.ifc_len; + + for (cp = (char *)IFC_REQ( ifc ); cp < cplim; cp += ifr_size(ifr)) +# endif /* WINTCP */ + { +# ifndef WINTCP + ifr = (struct ifreq *)cp; +# endif + if (ifr->ifr_addr.sa_family != AF_INET) + continue; + + broad_addr = ifr->ifr_addr; + ((struct sockaddr_in *)&broad_addr)->sin_addr.s_addr = + htonl( INADDR_BROADCAST ); +# ifdef SIOCGIFBRDADDR + { + struct ifreq broad_req; + + broad_req = *ifr; +# ifdef WINTCP /* NCR with Wollongong TCP */ + ioc.ic_cmd = IPIOC_GETIFFLAGS; + ioc.ic_timout = 0; + ioc.ic_len = sizeof(broad_req); + ioc.ic_dp = (char *)&broad_req; + + if (ioctl (ipfd, I_STR, (char *) &ioc ) != -1 && +# else /* WINTCP */ + if (ifioctl( socketFD, SIOCGIFFLAGS, (char *)&broad_req ) != + -1 && +# endif /* WINTCP */ + (broad_req.ifr_flags & IFF_BROADCAST) && + (broad_req.ifr_flags & IFF_UP)) + { + broad_req = *ifr; +# ifdef WINTCP /* NCR with Wollongong TCP */ + ioc.ic_cmd = IPIOC_GETIFBRDADDR; + ioc.ic_timout = 0; + ioc.ic_len = sizeof(broad_req); + ioc.ic_dp = (char *)&broad_req; + + if (ioctl( ipfd, I_STR, (char *) &ioc) != -1 ) +# else /* WINTCP */ + if (ifioctl( socketFD, SIOCGIFBRDADDR, (char *)&broad_req ) + != -1) +# endif /* WINTCP */ + broad_addr = broad_req.ifr_addr; + else + continue; + } else + continue; + } +# endif + in_addr = *((struct sockaddr_in *)&broad_addr); + in_addr.sin_port = htons( XDM_UDP_PORT ); +# ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + in_addr.sin_len = sizeof(in_addr); +# endif + registerHostaddr( (struct sockaddr *)&in_addr, sizeof(in_addr), + BROADCAST_QUERY ); + } +#endif +} + +static int +makeSockAddrs( const char *name, HostAddr **hosts ) +{ +#if defined(IPv6) && defined(AF_INET6) + struct addrinfo *ai, *nai, hints; + bzero( &hints, sizeof(hints) ); + hints.ai_socktype = SOCK_DGRAM; + if (getaddrinfo( name, stringify( XDM_UDP_PORT ), &hints, &ai )) + return 0; + for (nai = ai; nai; nai = nai->ai_next) + if ((nai->ai_family == AF_INET) || (nai->ai_family == AF_INET6)) + addHostaddr( hosts, nai->ai_addr, nai->ai_addrlen, + (nai->ai_family == AF_INET ? + IN_MULTICAST( ((struct sockaddr_in *)nai->ai_addr)->sin_addr.s_addr ) : + IN6_IS_ADDR_MULTICAST( &((struct sockaddr_in6 *)nai->ai_addr)->sin6_addr )) ? + BROADCAST_QUERY : QUERY ); +#else + struct sockaddr_in in_addr; + /* Per RFC 1123, check first for IP address in dotted-decimal form */ + if ((in_addr.sin_addr.s_addr = inet_addr( name )) == (unsigned)-1) { + struct hostent *hostent; + if (!(hostent = gethostbyname( name )) || + hostent->h_addrtype != AF_INET) + return 0; + memcpy( &in_addr.sin_addr, hostent->h_addr, 4 ); + } + in_addr.sin_family = AF_INET; + in_addr.sin_port = htons( XDM_UDP_PORT ); +# ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + in_addr.sin_len = sizeof(in_addr); +# endif + addHostaddr( hosts, (struct sockaddr *)&in_addr, sizeof(in_addr), +# ifdef IN_MULTICAST + IN_MULTICAST( in_addr.sin_addr.s_addr ) ? + BROADCAST_QUERY : +# endif + QUERY ); +#endif + return 1; +} + +/* + * Register the address for this host. + * Called for interactively specified hosts. + * The special names "BROADCAST" and "*" look up all the broadcast + * addresses on the local host. + */ + +static int +registerForPing( const char *name ) +{ + Debug( "manual host registration: %s\n", name ); + if (!strcmp( name, "BROADCAST" ) || !strcmp( name, "*" )) + registerBroadcastForPing(); + else if (!makeSockAddrs( name, &hostAddrdb )) + return 0; + return 1; +} + +/*ARGSUSED*/ +static void +AddChooserHost( + CARD16 connectionType, + ARRAY8Ptr addr, + char *closure ATTR_UNUSED ) +{ + if (connectionType == FamilyBroadcast) { + registerBroadcastForPing(); + return; + } + Debug( "internal host registration: %[*hhu, family %hx\n", + addr->length, addr->data, connectionType ); + if (connectionType == FamilyInternet) { + struct sockaddr_in in_addr; + in_addr.sin_family = AF_INET; + memmove( &in_addr.sin_addr, addr->data, 4 ); + in_addr.sin_port = htons( XDM_UDP_PORT ); +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + in_addr.sin_len = sizeof(in_addr); +#endif + registerHostaddr( (struct sockaddr *)&in_addr, sizeof(in_addr), +#ifdef IN_MULTICAST + IN_MULTICAST( in_addr.sin_addr.s_addr ) ? + BROADCAST_QUERY : +#endif + QUERY ); + } +#if defined(IPv6) && defined(AF_INET6) + else if (connectionType == FamilyInternet6) { + struct sockaddr_in6 in6_addr; + in6_addr.sin6_family = AF_INET6; + memmove( &in6_addr.sin6_addr, addr->data, 16 ); + in6_addr.sin6_port = htons( XDM_UDP_PORT ); +#ifdef SIN6_LEN + in6_addr.sin6_len = sizeof(in6_addr); +#endif + registerHostaddr( (struct sockaddr *)&in6_addr, sizeof(in6_addr), + IN6_IS_ADDR_MULTICAST( &in6_addr.sin6_addr ) ? + BROADCAST_QUERY : QUERY ); + } +#endif +} + +static ARRAYofARRAY8 AuthenticationNames; + +#if 0 +static void RegisterAuthenticationName( char *name, int namelen ) +{ + ARRAY8Ptr authName; + if (!XdmcpReallocARRAYofARRAY8( &AuthenticationNames, + AuthenticationNames.length + 1 )) + return; + authName = &AuthenticationNames.data[AuthenticationNames.length - 1]; + if (!XdmcpAllocARRAY8( authName, namelen )) + return; + memmove( authName->data, name, namelen ); +} +#endif + +static int +initXDMCP() +{ + XdmcpHeader header; +#if 0 + int i; +#endif +#ifndef STREAMSCONN +#ifdef SO_BROADCAST + int soopts; +#endif +#endif + + header.version = XDM_PROTOCOL_VERSION; + header.length = 1; +#if 0 + for (i = 0; i < (int)AuthenticationNames.length; i++) + header.length += 2 + AuthenticationNames.data[i].length; +#endif + + header.opcode = (CARD16)BROADCAST_QUERY; + XdmcpWriteHeader( &broadcastBuffer, &header ); + XdmcpWriteARRAYofARRAY8( &broadcastBuffer, &AuthenticationNames ); + + header.opcode = (CARD16)QUERY; + XdmcpWriteHeader( &directBuffer, &header ); + XdmcpWriteARRAYofARRAY8( &directBuffer, &AuthenticationNames ); + +#if defined(STREAMSCONN) + if ((socketFD = t_open( "/dev/udp", O_RDWR, 0 )) < 0) + return 0; + + if (t_bind( socketFD, NULL, NULL ) < 0) { + t_close( socketFD ); + return 0; + } + + /* + * This part of the code looks contrived. It will actually fit in nicely + * when the CLTS part of Xtrans is implemented. + */ + { + struct netconfig *nconf; + + if ((nconf = getnetconfigent( "udp" )) == NULL) { + t_unbind( socketFD ); + t_close( socketFD ); + return 0; + } + + if (netdir_options( nconf, ND_SET_BROADCAST, socketFD, NULL )) { + freenetconfigent( nconf ); + t_unbind( socketFD ); + t_close( socketFD ); + return 0; + } + + freenetconfigent( nconf ); + } +#else + if ((socketFD = socket( AF_INET, SOCK_DGRAM, 0 )) < 0) + return 0; +#if defined(IPv6) && defined(AF_INET6) + socket6FD = socket( AF_INET6, SOCK_DGRAM, 0 ); +#endif +# ifdef SO_BROADCAST + soopts = 1; + if (setsockopt( socketFD, SOL_SOCKET, SO_BROADCAST, (char *)&soopts, + sizeof(soopts) ) < 0) + perror( "setsockopt" ); +# endif +#endif + + return 1; +} + +static void ATTR_NORETURN +chooseHost( int hid ) +{ + HostName *h; +#if defined(IPv6) && defined(AF_INET6) + char addr[64]; +#endif + + for (h = hostNamedb; h; h = h->next) + if ((int)h == hid) { + /* XXX error handling */ + GSet( &mstrtalk ); + if ((td->displayType & d_location) == dLocal) { + GSendInt( D_RemoteHost ); +#if defined(IPv6) && defined(AF_INET6) + switch (h->connectionType) { + case FamilyInternet6: + inet_ntop( AF_INET6, h->hostaddr.data, addr, sizeof(addr) ); + break; + default: /* FamilyInternet */ + inet_ntop( AF_INET, h->hostaddr.data, addr, sizeof(addr) ); + break; + } + GSendStr( addr ); +#else + GSendStr( inet_ntoa( *(struct in_addr *)h->hostaddr.data ) ); +#endif + CloseGreeter( FALSE ); + SessionExit( EX_REMOTE ); + } else { + GSendInt( D_ChooseHost ); + GSendArr( td->clientAddr.length, (char *)td->clientAddr.data ); + GSendInt( td->connectionType ); + GSendArr( h->hostaddr.length, (char *)h->hostaddr.data ); + CloseGreeter( FALSE ); + goto bout; + } + break; + } +/* LogError( "Internal error: chose unexisting host\n" ); */ + bout: + SessionExit( EX_NORMAL ); +} + +static void +directChooseHost( const char *name ) +{ + HostAddr *hosts = 0; + + if (!makeSockAddrs( name, &hosts )) + return; + GSendInt( G_Ch_Exit ); + /* XXX error handling */ + GSet( &mstrtalk ); + if ((td->displayType & d_location) == dLocal) { + GSendInt( D_RemoteHost ); + GSendStr( name ); + CloseGreeter( FALSE ); + SessionExit( EX_REMOTE ); + } else { + GSendInt( D_ChooseHost ); + GSendArr( td->clientAddr.length, (char *)td->clientAddr.data ); + GSendInt( td->connectionType ); + GSendArr( hosts->addrlen, (char *)hosts->addr ); + CloseGreeter( FALSE ); + SessionExit( EX_NORMAL ); + } +} + +#define PING_TRIES 3 + +int +DoChoose() +{ + HostName **hp, *h; + char *host, **hostp; + struct timeval *to, tnow, nextPing; + int pingTry, n, cmd; + FD_TYPE rfds; + static int xdmcpInited; + + OpenGreeter(); + GSendInt( G_Choose ); + switch (cmd = CtrlGreeterWait( TRUE )) { + case G_Ready: + break; + default: /* error */ + return cmd; + } + + if (!xdmcpInited) { + if (!initXDMCP()) + SessionExit( EX_UNMANAGE_DPY ); + xdmcpInited = 1; + } + if ((td->displayType & d_location) == dLocal) { + /* XXX the config reader should do the lookup already */ + for (hostp = td->chooserHosts; *hostp; hostp++) + if (!registerForPing( *hostp )) + LogError( "Unkown host %\"s specified for local chooser preload of display %s\n", *hostp, td->name ); + } else + ForEachChooserHost( &td->clientAddr, td->connectionType, + AddChooserHost, 0 ); + + GSendInt( 0 ); /* entering async mode signal */ + + reping: + for (h = hostNamedb; h; h = h->next) + h->alive = 0; + pingTry = 0; + goto pingen; + + for (;;) { + to = 0; + if (pingTry <= PING_TRIES) { + gettimeofday( &tnow, 0 ); + if (nextPing.tv_sec < tnow.tv_sec || + (nextPing.tv_sec == tnow.tv_sec && + nextPing.tv_usec < tnow.tv_usec)) { + if (pingTry < PING_TRIES) { + pingen: + pingTry++; + doPingHosts(); + gettimeofday( &tnow, 0 ); + nextPing = tnow; + nextPing.tv_sec++; + } else { + for (hp = &hostNamedb; *hp; ) + if (!(*hp)->alive) { + h = (*hp)->next; + disposeHostname( *hp ); + GSendInt( G_Ch_RemoveHost ); + GSendInt( (int)*hp ); /* just an id */ + *hp = h; + } else + hp = &(*hp)->next; + goto noto; + } + } + to = &tnow; + tnow.tv_sec = nextPing.tv_sec - tnow.tv_sec; + tnow.tv_usec = nextPing.tv_usec - tnow.tv_usec; + if (tnow.tv_usec < 0) { + tnow.tv_usec += 1000000; + tnow.tv_sec--; + } + } + noto: + FD_ZERO( &rfds ); + FD_SET( grtproc.pipe.rfd, &rfds ); + FD_SET( socketFD, &rfds ); +#if defined(IPv6) && defined(AF_INET6) + if (socket6FD >= 0) + FD_SET( socket6FD, &rfds ); +#endif + n = grtproc.pipe.rfd; + if (socketFD > n) + n = socketFD; +#if defined(IPv6) && defined(AF_INET6) + if (socket6FD > n) + n = socket6FD; +#endif + if (select( n + 1, &rfds, 0, 0, to ) > 0) { + if (FD_ISSET( grtproc.pipe.rfd, &rfds )) + switch (cmd = CtrlGreeterWait( FALSE )) { + case -1: + break; + case G_Ch_Refresh: + goto reping; + case G_Ch_RegisterHost: + host = GRecvStr(); + if (!registerForPing( host )) { + GSendInt( G_Ch_BadHost ); + GSendStr( host ); + } + free( host ); + goto reping; + case G_Ch_DirectChoice: + host = GRecvStr(); + directChooseHost( host ); + GSendInt( G_Ch_BadHost ); + GSendStr( host ); + free( host ); + break; + case G_Ready: + chooseHost( GRecvInt() ); + /* NOTREACHED */ + default: + emptyHostnames(); + emptyPingHosts(); + return cmd; + } + if (FD_ISSET( socketFD, &rfds )) + receivePacket( socketFD ); +#if defined(IPv6) && defined(AF_INET6) + if (socket6FD >= 0 && FD_ISSET( socket6FD, &rfds )) + receivePacket( socket6FD ); +#endif + } + } + +} + +#endif /* XDMCP */ diff --git a/tdm/backend/client.c b/tdm/backend/client.c new file mode 100644 index 000000000..0cf4e216b --- /dev/null +++ b/tdm/backend/client.c @@ -0,0 +1,1865 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2000-2004 Oswald Buddenhagen <[email protected]> + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + * + * user verification and session initiation. + */ + +#include "dm.h" +#include "dm_auth.h" +#include "dm_error.h" + +#include <sys/stat.h> +#include <pwd.h> +#include <grp.h> +#ifdef SECURE_RPC +# include <rpc/rpc.h> +# include <rpc/key_prot.h> +extern int key_setnet( struct key_netstarg *arg ); +#endif +#ifdef K5AUTH +# include <krb5/krb5.h> +#endif +#ifdef HAVE_SETUSERCONTEXT +# include <login_cap.h> +#endif +#ifdef USE_PAM +# ifdef HAVE_PAM_PAM_APPL_H +# include <pam/pam_appl.h> +# else +# include <security/pam_appl.h> +# endif +#elif defined(_AIX) /* USE_PAM */ +# include <login.h> +# include <usersec.h> +extern int loginrestrictions( const char *Name, const int Mode, const char *Tty, char **Msg ); +extern int loginfailed( const char *User, const char *Host, const char *Tty ); +extern int loginsuccess( const char *User, const char *Host, const char *Tty, char **Msg ); +#else /* USE_PAM || _AIX */ +# ifdef KERBEROS +# include <sys/param.h> +# include <krb.h> +# ifndef NO_AFS +# include <kafs.h> +# endif +# endif +/* for nologin */ +# include <sys/types.h> +# include <unistd.h> +/* for expiration */ +# include <time.h> +#endif /* USE_PAM || _AIX */ +#ifdef HAVE_SHADOW +# include <shadow.h> +#endif +#include <signal.h> + +#ifdef WITH_CONSOLE_KIT +#include "consolekit.h" +#endif + +#define AU_FAILED 0 +#define AU_SUCCESS 1 +#ifdef HAVE_LIBAUDIT +#include <libaudit.h> +#else +#define log_to_audit_system(l,h,d,s) do { ; } while (0) +#endif + +#ifdef WITH_CONSOLE_KIT +#include "consolekit.h" +#endif + +/* + * Session data, mostly what struct verify_info was for + */ +char *curuser; +char *curpass; +char *curtype; +char *newpass; +char **userEnviron; +char **systemEnviron; +static int curuid; +static int curgid; +int cursource; + +char *dmrcuser; +char *curdmrc; +char *newdmrc; + +static struct passwd *p; +#ifdef HAVE_SETUSERCONTEXT +# ifdef HAVE_LOGIN_GETCLASS +login_cap_t *lc; +# else +struct login_cap *lc; +# endif +#endif +#ifdef USE_PAM +static pam_handle_t *pamh; +#elif defined(_AIX) +static char tty[16], hostname[100]; +#else +# ifdef USESHADOW +static struct spwd *sp; +# endif +# ifdef KERBEROS +static char krbtkfile[MAXPATHLEN]; +# endif +#endif + +#define V_RET_AUTH \ + do { \ + PrepErrorGreet (); \ + GSendInt (V_AUTH); \ + return 0; \ + } while(0) + +#define V_RET_FAIL(m) \ + do { \ + PrepErrorGreet (); \ + GSendInt (V_MSG_ERR); \ + GSendStr (m); \ + GSendInt (V_FAIL); \ + return 0; \ + } while(0) + +#ifdef USE_PAM + +# ifdef PAM_MESSAGE_NONCONST +typedef struct pam_message pam_message_type; +typedef void *pam_gi_type; +# else +typedef const struct pam_message pam_message_type; +typedef const void *pam_gi_type; +# endif + +struct pam_data { + GConvFunc gconv; + int usecur; + int abort; +}; + +static int +PAM_conv( int num_msg, + pam_message_type **msg, + struct pam_response **resp, + void *appdata_ptr ) +{ + int count; + struct pam_response *reply; + struct pam_data *pd = (struct pam_data *)appdata_ptr; + + if (!(reply = Calloc( num_msg, sizeof(*reply) ))) + return PAM_CONV_ERR; + + ReInitErrorLog(); + Debug( "PAM_conv\n" ); + for (count = 0; count < num_msg; count++) + switch (msg[count]->msg_style) { + case PAM_TEXT_INFO: + Debug( " PAM_TEXT_INFO: %s\n", msg[count]->msg ); + PrepErrorGreet(); + GSendInt( V_MSG_INFO ); + GSendStr( msg[count]->msg ); + continue; + case PAM_ERROR_MSG: + Debug( " PAM_ERROR_MSG: %s\n", msg[count]->msg ); + PrepErrorGreet(); + GSendInt( V_MSG_ERR ); + GSendStr( msg[count]->msg ); + continue; + default: + /* could do better error handling here, but see below ... */ + if (pd->usecur) { + switch (msg[count]->msg_style) { + /* case PAM_PROMPT_ECHO_ON: cannot happen */ + case PAM_PROMPT_ECHO_OFF: + Debug( " PAM_PROMPT_ECHO_OFF (usecur): %s\n", msg[count]->msg ); + if (!curpass) + pd->gconv( GCONV_PASS, 0 ); + StrDup( &reply[count].resp, curpass ); + break; + default: + LogError( "Unknown PAM message style <%d>\n", msg[count]->msg_style ); + goto conv_err; + } + } else { + switch (msg[count]->msg_style) { + case PAM_PROMPT_ECHO_ON: + Debug( " PAM_PROMPT_ECHO_ON: %s\n", msg[count]->msg ); + reply[count].resp = pd->gconv( GCONV_NORMAL, msg[count]->msg ); + break; + case PAM_PROMPT_ECHO_OFF: + Debug( " PAM_PROMPT_ECHO_OFF: %s\n", msg[count]->msg ); + reply[count].resp = pd->gconv( GCONV_HIDDEN, msg[count]->msg ); + break; +#ifdef PAM_BINARY_PROMPT + case PAM_BINARY_PROMPT: + Debug( " PAM_BINARY_PROMPT\n" ); + reply[count].resp = pd->gconv( GCONV_BINARY, msg[count]->msg ); + break; +#endif + default: + LogError( "Unknown PAM message style <%d>\n", msg[count]->msg_style ); + goto conv_err; + } + } + if (!reply[count].resp) { + Debug( " PAM_conv aborted\n" ); + pd->abort = TRUE; + goto conv_err; + } + reply[count].resp_retcode = PAM_SUCCESS; /* unused in linux-pam */ + } + Debug( " PAM_conv success\n" ); + *resp = reply; + return PAM_SUCCESS; + + conv_err: + for (; count >= 0; count--) + if (reply[count].resp) + switch (msg[count]->msg_style) { + case PAM_PROMPT_ECHO_ON: + case PAM_PROMPT_ECHO_OFF: /* could wipe ... */ +#ifdef PAM_BINARY_PROMPT + case PAM_BINARY_PROMPT: /* ... that too ... */ +#endif + free( reply[count].resp ); + break; + } + free( reply ); + return PAM_CONV_ERR; +} + +static int +PAM_conv_null( int num_msg, + pam_message_type **msg, + struct pam_response **resp, + void *appdata_ptr ATTR_UNUSED ) +{ + int count; + struct pam_response *reply; + + if (!(reply = Calloc( num_msg, sizeof(*reply) ))) + return PAM_CONV_ERR; + + ReInitErrorLog(); + Debug( "PAM_conv_null\n" ); + for (count = 0; count < num_msg; count++) { + switch (msg[count]->msg_style) { + case PAM_TEXT_INFO: + Debug( " PAM_TEXT_INFO: %s\n", msg[count]->msg ); + continue; + case PAM_ERROR_MSG: + LogError( "PAM error message: %s\n", msg[count]->msg ); + continue; + default: + /* unknown */ + Debug( " PAM_<%d>\n", msg[count]->msg_style ); + free( reply ); + return PAM_CONV_ERR; + } + reply[count].resp_retcode = PAM_SUCCESS; /* unused in linux-pam */ + } + Debug( " PAM_conv_null success\n" ); + *resp = reply; + return PAM_SUCCESS; +} + +# ifdef PAM_FAIL_DELAY +static void +fail_delay( int retval ATTR_UNUSED, unsigned usec_delay ATTR_UNUSED, + void *appdata_ptr ATTR_UNUSED ) +{} +# endif + + /** + * log_to_audit_system: + * @login: Name of user + * @hostname: Name of host machine + * @tty: Name of display + * @success: 1 for success, 0 for failure + * + * Logs the success or failure of the login attempt with the linux kernel + * audit system. The intent is to capture failed events where the user + * fails authentication or otherwise is not permitted to login. There are + * many other places where pam could potentially fail and cause login to + * fail, but these are system failures rather than the signs of an account + * being hacked. + * + * Returns nothing. + */ + +#ifdef HAVE_LIBAUDIT +static void +log_to_audit_system (const char *loginname, + const char *hostname, + const char *tty, + int success) +{ + struct passwd *pw; + char buf[64]; + int audit_fd; + + audit_fd = audit_open(); + if (loginname) + pw = getpwnam(loginname); + else { + loginname = "unknown"; + pw = NULL; + } + Debug("log_to_audit %p %s\n", pw, loginname); + + if (pw) { + snprintf(buf, sizeof(buf), "uid=%d", pw->pw_uid); + audit_log_user_message(audit_fd, AUDIT_USER_LOGIN, + buf, hostname, NULL, tty, (int)success); + } else { + snprintf(buf, sizeof(buf), "acct=%s", loginname); + audit_log_user_message(audit_fd, AUDIT_USER_LOGIN, + buf, hostname, NULL, tty, (int)success); + } + close(audit_fd); +} +#endif + +static int +doPAMAuth( const char *psrv, struct pam_data *pdata ) +{ + pam_gi_type pitem; + struct pam_conv pconv; + int pretc; + + pdata->abort = FALSE; + pconv.conv = PAM_conv; + pconv.appdata_ptr = (void *)pdata; + Debug( " PAM service %s\n", psrv ); + if ((pretc = pam_start( psrv, curuser, &pconv, &pamh )) != PAM_SUCCESS) + goto pam_bail2; + if ((pretc = pam_set_item( pamh, PAM_TTY, td->name )) != PAM_SUCCESS) { + pam_bail: + pam_end( pamh, pretc ); + pamh = 0; + pam_bail2: + ReInitErrorLog(); + LogError( "PAM error: %s\n", pam_strerror( 0, pretc ) ); + V_RET_FAIL( 0 ); + } + if ((td->displayType & d_location) == dForeign) { + char *cp = strchr( td->name, ':' ); + *cp = 0; + pretc = pam_set_item( pamh, PAM_RHOST, td->name ); + *cp = ':'; + if (pretc != PAM_SUCCESS) + goto pam_bail; + } +# ifdef __sun__ /* Only Solaris <= 9, but checking it does not seem worth it. */ + else if (pam_set_item( pamh, PAM_RHOST, 0 ) != PAM_SUCCESS) + goto pam_bail; +# endif +# ifdef PAM_FAIL_DELAY + pam_set_item( pamh, PAM_FAIL_DELAY, (void *)fail_delay ); +# endif + ReInitErrorLog(); + + Debug( " pam_authenticate() ...\n" ); + pretc = pam_authenticate( pamh, + td->allowNullPasswd ? 0 : PAM_DISALLOW_NULL_AUTHTOK ); + ReInitErrorLog(); + Debug( " pam_authenticate() returned: %s\n", pam_strerror( pamh, pretc ) ); + if (pdata->abort) { + pam_end( pamh, PAM_SUCCESS ); + pamh = 0; + return 0; + } + if (!curuser) { + Debug( " asking PAM for user ...\n" ); + pam_get_item( pamh, PAM_USER, &pitem ); + ReInitErrorLog(); + StrDup( &curuser, (const char *)pitem ); + GSendInt( V_PUT_USER ); + GSendStr( curuser ); + } + if (pretc != PAM_SUCCESS) { + /* Log the failed login attempt */ + log_to_audit_system (curuser, td->remoteHost, td->name, AU_FAILED); + switch (pretc) { + case PAM_USER_UNKNOWN: + case PAM_AUTH_ERR: + case PAM_MAXTRIES: /* should handle this better ... */ + case PAM_AUTHINFO_UNAVAIL: /* returned for unknown users ... bogus */ + pam_end( pamh, pretc ); + pamh = 0; + V_RET_AUTH; + default: + pam_end( pamh, pretc ); + pamh = 0; + V_RET_FAIL( 0 ); + } + } + return 1; +} + +#endif /* USE_PAM */ + +static int +#if defined(USE_PAM) || defined(_AIX) +AccNoPass( const char *un ) +{ + struct passwd *pw = 0; +# ifdef HAVE_SHADOW /* (sic!) - not USESHADOW */ + struct spwd *spw; +# endif +#else +AccNoPass( const char *un, struct passwd *pw ) +{ +#endif + struct group *gr; + char **fp; + int hg; + + if (!*un) + return 0; + + if (cursource != PWSRC_MANUAL) + return 1; + + for (hg = 0, fp = td->noPassUsers; *fp; fp++) + if (**fp == '@') + hg = 1; + else if (!strcmp( un, *fp )) + return 1; + else if (!strcmp( "*", *fp )) { +#if defined(USE_PAM) || defined(_AIX) + if (!(pw = getpwnam( un ))) + return 0; + if (pw->pw_passwd[0] == '!' || pw->pw_passwd[0] == '*') + continue; +# ifdef HAVE_SHADOW /* (sic!) - not USESHADOW */ + if ((spw = getspnam( un )) && + (spw->sp_pwdp[0] == '!' || spw->sp_pwdp[0] == '*')) + continue; +# endif +#endif + if (pw->pw_uid) + return 1; + } + +#if defined(USE_PAM) || defined(_AIX) + if (hg && (pw || (pw = getpwnam( un )))) { +#else + if (hg) { +#endif + for (setgrent(); (gr = getgrent()); ) + for (fp = td->noPassUsers; *fp; fp++) + if (**fp == '@' && !strcmp( gr->gr_name, *fp + 1 )) { + if (pw->pw_gid == gr->gr_gid) { + endgrent(); + return 1; + } + for (; *gr->gr_mem; gr->gr_mem++) + if (!strcmp( un, *gr->gr_mem )) { + endgrent(); + return 1; + } + } + endgrent(); + } + + return 0; +} + +#if !defined(USE_PAM) && !defined(_AIX) && defined(HAVE_SETUSERCONTEXT) +# define LC_RET0 do { login_close(lc); return 0; } while(0) +#else +# define LC_RET0 return 0 +#endif + +int +Verify( GConvFunc gconv, int rootok ) +{ +#ifdef USE_PAM + const char *psrv; + struct pam_data pdata; + int pretc, pnopass; + char psrvb[64]; +#elif defined(_AIX) + char *msg, *curret; + int i, reenter; +#else + struct stat st; + const char *nolg; + char *buf; + int fd; +# ifdef HAVE_GETUSERSHELL + char *s; +# endif +# if defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || defined(USESHADOW) + int tim, expir, warntime, quietlog; +# endif +#endif + + Debug( "Verify ...\n" ); + +#ifdef USE_PAM + + pnopass = FALSE; + if (!strcmp( curtype, "classic" )) { + if (!gconv( GCONV_USER, 0 )) + return 0; + if (AccNoPass( curuser )) { + gconv( GCONV_PASS_ND, 0 ); + if (!*curpass) { + pnopass = TRUE; + sprintf( psrvb, "%.31s-np", PAMService ); + psrv = psrvb; + } else + psrv = PAMService; + } else + psrv = PAMService; + pdata.usecur = TRUE; + } else if (!strcmp( curtype, "pam" )) { + psrv = PAMService; + pdata.usecur = FALSE; + } else { + sprintf( psrvb, "%.31s-%.31s", PAMService, curtype ); + psrv = psrvb; + pdata.usecur = FALSE; + } + pdata.gconv = gconv; + if (!doPAMAuth( psrv, &pdata )) + return 0; + +#elif defined(_AIX) + + if ((td->displayType & d_location) == dForeign) { + char *tmpch; + strncpy( hostname, td->name, sizeof(hostname) - 1 ); + hostname[sizeof(hostname)-1] = '\0'; + if ((tmpch = strchr( hostname, ':' ))) + *tmpch = '\0'; + } else + hostname[0] = '\0'; + + /* tty names should only be 15 characters long */ +# if 0 + for (i = 0; i < 15 && td->name[i]; i++) { + if (td->name[i] == ':' || td->name[i] == '.') + tty[i] = '_'; + else + tty[i] = td->name[i]; + } + tty[i] = '\0'; +# else + memcpy( tty, "/dev/xdm/", 9 ); + for (i = 0; i < 6 && td->name[i]; i++) { + if (td->name[i] == ':' || td->name[i] == '.') + tty[9 + i] = '_'; + else + tty[9 + i] = td->name[i]; + } + tty[9 + i] = '\0'; +# endif + + if (!strcmp( curtype, "classic" )) { + if (!gconv( GCONV_USER, 0 )) + return 0; + if (AccNoPass( curuser )) { + gconv( GCONV_PASS_ND, 0 ); + if (!*curpass) { + Debug( "accepting despite empty password\n" ); + goto done; + } + } else + if (!gconv( GCONV_PASS, 0 )) + return 0; + enduserdb(); + msg = NULL; + if ((i = authenticate( curuser, curpass, &reenter, &msg ))) { + Debug( "authenticate() failed: %s\n", msg ); + if (msg) + free( msg ); + loginfailed( curuser, hostname, tty ); + if (i == ENOENT || i == ESAD) + V_RET_AUTH; + else + V_RET_FAIL( 0 ); + } + if (reenter) { + LogError( "authenticate() requests more data: %s\n", msg ); + free( msg ); + V_RET_FAIL( 0 ); + } + } else if (!strcmp( curtype, "generic" ) || !strcmp(curtype, "pam")) { + if (!gconv( GCONV_USER, 0 )) + return 0; + for (curret = 0;;) { + msg = NULL; + if ((i = authenticate( curuser, curret, &reenter, &msg ))) { + Debug( "authenticate() failed: %s\n", msg ); + if (msg) + free( msg ); + loginfailed( curuser, hostname, tty ); + if (i == ENOENT || i == ESAD) + V_RET_AUTH; + else + V_RET_FAIL( 0 ); + } + if (curret) + free( curret ); + if (!reenter) + break; + if (!(curret = gconv( GCONV_HIDDEN, msg ))) + return 0; + free( msg ); + } + } else { + LogError( "Unsupported authentication type %\"s requested\n", curtype ); + V_RET_FAIL( 0 ); + } + if (msg) { + PrepErrorGreet(); + GSendInt( V_MSG_INFO ); + GSendStr( msg ); + free( msg ); + } + + done: + +#else + + if (strcmp( curtype, "classic" )) { + LogError( "Unsupported authentication type %\"s requested\n", curtype ); + V_RET_FAIL( 0 ); + } + + if (!gconv( GCONV_USER, 0 )) + return 0; + + if (!(p = getpwnam( curuser ))) { + Debug( "getpwnam() failed.\n" ); + gconv( GCONV_PASS, 0 ); + V_RET_AUTH; + } + if (p->pw_passwd[0] == '!' || p->pw_passwd[0] == '*') { + Debug( "account is locked\n" ); + gconv( GCONV_PASS, 0 ); + V_RET_AUTH; + } + +# ifdef USESHADOW + if ((sp = getspnam( curuser ))) { + p->pw_passwd = sp->sp_pwdp; + if (p->pw_passwd[0] == '!' || p->pw_passwd[0] == '*') { + Debug( "account is locked\n" ); + gconv( GCONV_PASS, 0 ); + V_RET_AUTH; + } + } else + Debug( "getspnam() failed: %m. Are you root?\n" ); +# endif + + if (!*p->pw_passwd) { + if (!td->allowNullPasswd) { + Debug( "denying user with empty password\n" ); + gconv( GCONV_PASS, 0 ); + V_RET_AUTH; + } + goto nplogin; + } + + if (AccNoPass( curuser, p )) { + nplogin: + gconv( GCONV_PASS_ND, 0 ); + if (!*curpass) { + Debug( "accepting password-less login\n" ); + goto done; + } + } else + if (!gconv( GCONV_PASS, 0 )) + return 0; + +# ifdef KERBEROS + if (p->pw_uid) { + int ret; + char realm[REALM_SZ]; + + if (krb_get_lrealm( realm, 1 )) { + LogError( "Can't get KerberosIV realm.\n" ); + V_RET_FAIL( 0 ); + } + + sprintf( krbtkfile, "%s.%.*s", TKT_ROOT, MAXPATHLEN - strlen( TKT_ROOT ) - 2, td->name ); + krb_set_tkt_string( krbtkfile ); + unlink( krbtkfile ); + + ret = krb_verify_user( curuser, "", realm, curpass, 1, "rcmd" ); + if (ret == KSUCCESS) { + chown( krbtkfile, p->pw_uid, p->pw_gid ); + Debug( "KerberosIV verify succeeded\n" ); + goto done; + } else if (ret != KDC_PR_UNKNOWN && ret != SKDC_CANT) { + LogError( "KerberosIV verification failure %\"s for %s\n", + krb_get_err_text( ret ), curuser ); + krbtkfile[0] = '\0'; + V_RET_FAIL( 0 ); + } + Debug( "KerberosIV verify failed: %s\n", krb_get_err_text( ret ) ); + } + krbtkfile[0] = '\0'; +# endif /* KERBEROS */ + +# if defined(ultrix) || defined(__ultrix__) + if (authenticate_user( p, curpass, NULL ) < 0) +# elif defined(HAVE_CRYPT) + if (strcmp( crypt( curpass, p->pw_passwd ), p->pw_passwd )) +# else + if (strcmp( curpass, p->pw_passwd )) +# endif + { + Debug( "password verify failed\n" ); + V_RET_AUTH; + } + + done: + +#endif /* !defined(USE_PAM) && !defined(_AIX) */ + + Debug( "restrict %s ...\n", curuser ); + +#if defined(USE_PAM) || defined(_AIX) + if (!(p = getpwnam( curuser ))) { + LogError( "getpwnam(%s) failed.\n", curuser ); + V_RET_FAIL( 0 ); + } +#endif + if (!p->pw_uid) { + if (!rootok && !td->allowRootLogin) + V_RET_FAIL( "Root logins are not allowed" ); + /* Log the failed login attempt */ + log_to_audit_system (curuser, td->remoteHost, td->name, AU_FAILED); + return 1; /* don't deny root to log in */ + } + +#ifdef USE_PAM + + Debug( " pam_acct_mgmt() ...\n" ); + pretc = pam_acct_mgmt( pamh, 0 ); + ReInitErrorLog(); + Debug( " pam_acct_mgmt() returned: %s\n", pam_strerror( pamh, pretc ) ); + if (pretc == PAM_NEW_AUTHTOK_REQD) { + pdata.usecur = FALSE; + pdata.gconv = conv_interact; + /* pam will have output a message already, so no PrepErrorGreet () */ + if (gconv != conv_interact || pnopass) { + pam_end( pamh, PAM_SUCCESS ); + pamh = 0; + GSendInt( V_CHTOK_AUTH ); + /* this cannot auth the wrong user, as only classic auths get here */ + while (!doPAMAuth( PAMService, &pdata )) + if (pdata.abort) + return 0; + GSendInt( V_PRE_OK ); + } else + GSendInt( V_CHTOK ); + for (;;) { + Debug( " pam_chauthtok() ...\n" ); + pretc = pam_chauthtok( pamh, PAM_CHANGE_EXPIRED_AUTHTOK ); + ReInitErrorLog(); + Debug( " pam_chauthtok() returned: %s\n", pam_strerror( pamh, pretc ) ); + if (pdata.abort) { + pam_end( pamh, PAM_SUCCESS ); + pamh = 0; + return 0; + } + if (pretc == PAM_SUCCESS) + break; + /* Log the failed login attempt */ + log_to_audit_system (curuser, td->remoteHost, td->name, AU_FAILED); + /* effectively there is only PAM_AUTHTOK_ERR */ + GSendInt( V_FAIL ); + } + if (curpass) + free( curpass ); + curpass = newpass; + newpass = 0; + } else if (pretc != PAM_SUCCESS) { + pam_end( pamh, pretc ); + pamh = 0; + V_RET_AUTH; + } + +#elif defined(_AIX) /* USE_PAM */ + + msg = NULL; + if (loginrestrictions( curuser, + ((td->displayType & d_location) == dForeign) ? S_RLOGIN : S_LOGIN, + tty, &msg ) == -1) + { + Debug( "loginrestrictions() - %s\n", msg ? msg : "error" ); + loginfailed( curuser, hostname, tty ); + PrepErrorGreet(); + if (msg) { + GSendInt( V_MSG_ERR ); + GSendStr( msg ); + } + GSendInt( V_AUTH ); + return 0; + } + if (msg) + free( (void *)msg ); + +#endif /* USE_PAM || _AIX */ + +#ifndef _AIX + +# ifdef HAVE_SETUSERCONTEXT +# ifdef HAVE_LOGIN_GETCLASS + lc = login_getclass( p->pw_class ); +# else + lc = login_getpwclass( p ); +# endif + if (!lc) + V_RET_FAIL( 0 ); + + p->pw_shell = login_getcapstr( lc, "shell", p->pw_shell, p->pw_shell ); +# endif + +# ifndef USE_PAM + +/* restrict_expired */ +# if defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || defined(USESHADOW) + +# if !defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || (!defined(HAVE_SETUSERCONTEXT) && defined(USESHADOW)) + if (sp) +# endif + { + +# define DEFAULT_WARN (2L * 7L) /* Two weeks */ + + tim = time( NULL ) / 86400L; + +# ifdef HAVE_SETUSERCONTEXT + quietlog = login_getcapbool( lc, "hushlogin", 0 ); + warntime = login_getcaptime( lc, "warnexpire", + DEFAULT_WARN * 86400L, + DEFAULT_WARN * 86400L ) / 86400L; +# else + quietlog = 0; +# ifdef USESHADOW + warntime = sp->sp_warn != -1 ? sp->sp_warn : DEFAULT_WARN; +# else + warntime = DEFAULT_WARN; +# endif +# endif + +# ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE + if (p->pw_expire) { + expir = p->pw_expire / 86400L; +# else + if (sp->sp_expire != -1) { + expir = sp->sp_expire; +# endif + if (tim > expir) { + PrepErrorGreet(); + GSendInt( V_MSG_ERR ); + GSendStr( "Your account has expired;" + " please contact your system administrator" ); + /* Log the failed login attempt */ + log_to_audit_system (curuser, td->remoteHost, td->name, AU_FAILED); + GSendInt( V_FAIL ); + LC_RET0; + } else if (tim > (expir - warntime) && !quietlog) { + ASPrintf( &buf, + "Warning: your account will expire in %d day(s)", + expir - tim ); + if (buf) { + PrepErrorGreet(); + GSendInt( V_MSG_INFO ); + GSendStr( buf ); + free( buf ); + } + } + } + +# ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE + if (p->pw_change) { + expir = p->pw_change / 86400L; +# else + if (!sp->sp_lstchg) { + PrepErrorGreet(); + GSendInt( V_MSG_ERR ); + GSendStr( "You are required to change your password immediately" + " (root enforced)" ); + /* XXX todo password change */ + GSendInt( V_FAIL ); + LC_RET0; + } else if (sp->sp_max != -1) { + expir = sp->sp_lstchg + sp->sp_max; + if (sp->sp_inact != -1 && tim > expir + sp->sp_inact) { + PrepErrorGreet(); + GSendInt( V_MSG_ERR ); + GSendStr( "Your account has expired;" + " please contact your system administrator" ); + /* Log the failed login attempt */ + log_to_audit_system (curuser, td->remoteHost, td->name, AU_FAILED); + GSendInt( V_FAIL ); + LC_RET0; + } +# endif + if (tim > expir) { + PrepErrorGreet(); + GSendInt( V_MSG_ERR ); + GSendStr( "You are required to change your password immediately" + " (password aged)" ); + /* XXX todo password change */ + GSendInt( V_FAIL ); + LC_RET0; + } else if (tim > (expir - warntime) && !quietlog) { + ASPrintf( &buf, + "Warning: your password will expire in %d day(s)", + expir - tim ); + if (buf) { + PrepErrorGreet(); + GSendInt( V_MSG_INFO ); + GSendStr( buf ); + free( buf ); + } + } + } + + } + +# endif /* HAVE_STRUCT_PASSWD_PW_EXPIRE || USESHADOW */ + +/* restrict_nologin */ +# ifndef _PATH_NOLOGIN +# define _PATH_NOLOGIN "/etc/nologin" +# endif + + if (( +# ifdef HAVE_SETUSERCONTEXT + /* Do we ignore a nologin file? */ + !login_getcapbool( lc, "ignorenologin", 0 )) && + (!stat( (nolg = login_getcapstr( lc, "nologin", "", NULL )), &st ) || +# endif + !stat( (nolg = _PATH_NOLOGIN), &st ))) + { + PrepErrorGreet(); + GSendInt( V_MSG_ERR ); + if (st.st_size && (fd = open( nolg, O_RDONLY )) >= 0) { + if ((buf = Malloc( st.st_size + 1 ))) { + if (read( fd, buf, st.st_size ) == st.st_size) { + buf[st.st_size] = 0; + GSendStr( buf ); + free( buf ); + close( fd ); + GSendInt( V_FAIL ); + LC_RET0; + } + free( buf ); + } + close( fd ); + } + GSendStr( "Logins are not allowed at the moment.\nTry again later" ); + /* Log the failed login attempt */ + log_to_audit_system (curuser, td->remoteHost, td->name, AU_FAILED); + GSendInt( V_FAIL ); + LC_RET0; + } + +/* restrict_time */ +# if defined(HAVE_SETUSERCONTEXT) && defined(HAVE_AUTH_TIMEOK) + if (!auth_timeok( lc, time( NULL ) )) { + PrepErrorGreet(); + GSendInt( V_MSG_ERR ); + GSendStr( "You are not allowed to login at the moment" ); + /* Log the failed login attempt */ + log_to_audit_system (curuser, td->remoteHost, td->name, AU_FAILED); + GSendInt( V_FAIL ); + LC_RET0; + } +# endif + +# ifdef HAVE_GETUSERSHELL + for (;;) { + if (!(s = getusershell())) { + Debug( "shell not in /etc/shells\n" ); + endusershell(); + V_RET_FAIL( "Your login shell is not listed in /etc/shells" ); + /* Log the failed login attempt */ + log_to_audit_system (curuser, td->remoteHost, td->name, AU_FAILED); + } + if (!strcmp( s, p->pw_shell )) { + endusershell(); + break; + } + } +# endif + +# endif /* !USE_PAM */ + +/* restrict_nohome */ +# ifdef HAVE_SETUSERCONTEXT + if (login_getcapbool( lc, "requirehome", 0 )) { + struct stat st; + if (!*p->pw_dir || stat( p->pw_dir, &st ) || st.st_uid != p->pw_uid) { + PrepErrorGreet(); + GSendInt( V_MSG_ERR ); + GSendStr( "Home folder not available" ); + GSendInt( V_FAIL ); + LC_RET0; + } + } +# endif + +#endif /* !_AIX */ + + return 1; + +} + + +static const char *envvars[] = { + "TZ", /* SYSV and SVR4, but never hurts */ +#ifdef _AIX + "AUTHSTATE", /* for kerberos */ +#endif + NULL +}; + + +#if defined(USE_PAM) && defined(HAVE_INITGROUPS) +static int num_saved_gids; +static gid_t *saved_gids; + +static int +saveGids( void ) +{ + num_saved_gids = getgroups( 0, 0 ); + if (!(saved_gids = Malloc( sizeof(gid_t) * num_saved_gids ))) + return 0; + if (getgroups( num_saved_gids, saved_gids ) < 0) { + LogError( "saving groups failed: %m\n" ); + return 0; + } + return 1; +} + +static int +restoreGids( void ) +{ + if (setgroups( num_saved_gids, saved_gids ) < 0) { + LogError( "restoring groups failed: %m\n" ); + return 0; + } + if (setgid( p->pw_gid ) < 0) { + LogError( "restoring gid failed: %m\n" ); + return 0; + } + return 1; +} +#endif /* USE_PAM && HAVE_INITGROUPS */ + +static int +resetGids( void ) +{ +#ifdef HAVE_INITGROUPS + if (setgroups( 0, &p->pw_gid /* anything */ ) < 0) { + LogError( "restoring groups failed: %m\n" ); + return 0; + } +#endif + if (setgid( 0 ) < 0) { + LogError( "restoring gid failed: %m\n" ); + return 0; + } + return 1; +} + +static int +SetGid( const char *name, int gid ) +{ + if (setgid( gid ) < 0) { + LogError( "setgid(%d) (user %s) failed: %m\n", gid, name ); + return 0; + } +#ifdef HAVE_INITGROUPS + if (initgroups( name, gid ) < 0) { + LogError( "initgroups for %s failed: %m\n", name ); + setgid( 0 ); + return 0; + } +#endif /* QNX4 doesn't support multi-groups, no initgroups() */ + return 1; +} + +static int +SetUid( const char *name, int uid ) +{ + if (setuid( uid ) < 0) { + LogError( "setuid(%d) (user %s) failed: %m\n", uid, name ); + return 0; + } + return 1; +} + +static int +SetUser( const char *name, int uid, int gid ) +{ + if (SetGid( name, gid )) { + if (SetUid( name, uid )) + return 1; + resetGids(); + } + return 0; +} + +#if defined(SECURE_RPC) || defined(K5AUTH) +static void +NukeAuth( int len, const char *name ) +{ + int i; + + for (i = 0; i < td->authNum; i++) + if (td->authorizations[i]->name_length == len && + !memcmp( td->authorizations[i]->name, name, len )) + { + memcpy( &td->authorizations[i], &td->authorizations[i+1], + sizeof(td->authorizations[i]) * (--td->authNum - i) ); + break; + } +} +#endif + +static void +mergeSessionArgs( int cansave ) +{ + char *mfname; + const char *fname; + int i, needsave; + + mfname = 0; + fname = ".dmrc"; + if ((!curdmrc || newdmrc) && *dmrcDir) + if (StrApp( &mfname, dmrcDir, "/", curuser, fname, (char *)0 )) + fname = mfname; + needsave = 0; + if (!curdmrc) { + curdmrc = iniLoad( fname ); + if (!curdmrc) { + StrDup( &curdmrc, "[Desktop]\nSession=default\n" ); + needsave = 1; + } + } + if (newdmrc) { + curdmrc = iniMerge( curdmrc, newdmrc ); + needsave = 1; + } + if (needsave && cansave) + if (!iniSave( curdmrc, fname ) && errno == ENOENT && mfname) { + for (i = 0; mfname[i]; i++) + if (mfname[i] == '/') { + mfname[i] = 0; + mkdir( mfname, 0755 ); + mfname[i] = '/'; + } + iniSave( curdmrc, mfname ); + } + if (mfname) + free( mfname ); +} + +static int removeAuth; +#ifdef USE_PAM +static int removeSession; +static int removeCreds; +#endif + +#ifdef WITH_CONSOLE_KIT +int +StartClient( const char *ck_session_cookie ) +#else +int +StartClient() +#endif +{ + const char *home, *sessargs, *desksess; + char **env, *xma; + char **argv, *fname, *str; +#ifdef USE_PAM + char **pam_env; +# ifdef _AIX + char **saved_env; +# endif + struct pam_conv pconv; + int pretc; +#else +# ifdef _AIX + char *msg; + char **theenv; + extern char **newenv; /* from libs.a, this is set up by setpenv */ +# endif +#endif +#ifdef HAVE_SETUSERCONTEXT + extern char **environ; +#endif + char *failsafeArgv[2], *lname; + int i, pid, lfd; + + if (StrCmp( dmrcuser, curuser )) { + if (curdmrc) { free( curdmrc ); curdmrc = 0; } + if (dmrcuser) { free( dmrcuser ); dmrcuser = 0; } + } + +#if defined(USE_PAM) || defined(_AIX) + if (!(p = getpwnam( curuser ))) { + LogError( "getpwnam(%s) failed.\n", curuser ); + return 0; + } +#endif + +#ifndef USE_PAM +# ifdef _AIX + msg = NULL; + loginsuccess( curuser, hostname, tty, &msg ); + if (msg) { + Debug( "loginsuccess() - %s\n", msg ); + free( (void *)msg ); + } +# else /* _AIX */ +# if defined(KERBEROS) && !defined(NO_AFS) + if (krbtkfile[0] != '\0') { + if (k_hasafs()) { + if (k_setpag() == -1) + LogError( "setpag() for %s failed\n", curuser ); + if ((ret = k_afsklog( NULL, NULL )) != KSUCCESS) + LogError( "AFS Warning: %s\n", krb_get_err_text( ret ) ); + } + } +# endif /* KERBEROS && AFS */ +# endif /* _AIX */ +#endif /* !PAM */ + + curuid = p->pw_uid; + curgid = p->pw_gid; + + env = baseEnv( curuser ); + xma = 0; + if (td->ctrl.fpath && StrDup( &xma, td->ctrl.fpath )) { + if ((td->allowShutdown == SHUT_ALL || + (td->allowShutdown == SHUT_ROOT && !curuser)) && + StrApp( &xma, ",maysd", (char *)0 )) + { + if (td->allowNuke == SHUT_ALL || + (td->allowNuke == SHUT_ROOT && !curuser)) + StrApp( &xma, ",mayfn", (char *)0 ); + StrApp( &xma, td->defSdMode == SHUT_FORCENOW ? ",fn" : + td->defSdMode == SHUT_TRYNOW ? ",tn" : ",sched", + (char *)0 ); + } + if ((td->displayType & d_location) == dLocal && AnyReserveDisplays()) + StrApp( &xma, ",rsvd", (char *)0 ); + } else + StrDup( &xma, "true" ); + StrApp( &xma, ",method=", curtype, (char *)0 ); + if (td_setup) + StrApp( &xma, ",auto", (char *)0 ); + if (xma) { + env = setEnv( env, "XDM_MANAGED", xma ); + free( xma ); + } + if (td->autoLock && cursource == PWSRC_AUTOLOGIN) + env = setEnv( env, "DESKTOP_LOCKED", "true" ); + env = setEnv( env, "PATH", curuid ? td->userPath : td->systemPath ); + env = setEnv( env, "SHELL", p->pw_shell ); + env = setEnv( env, "HOME", p->pw_dir ); + if (cursource == PWSRC_AUTOLOGIN) + env = setEnv (env, "TDM_AUTOLOGIN", curuser); +#if !defined(USE_PAM) && !defined(_AIX) && defined(KERBEROS) + if (krbtkfile[0] != '\0') + env = setEnv( env, "KRBTKFILE", krbtkfile ); +#endif +#ifdef WITH_CONSOLE_KIT + if (ck_session_cookie != NULL) { + env = setEnv ( env, "XDG_SESSION_COOKIE", ck_session_cookie ); + } +#endif +#ifdef WITH_CONSOLE_KIT + if (ck_session_cookie != NULL) { + env = setEnv ( env, "XDG_SESSION_COOKIE", ck_session_cookie ); + } +#endif + userEnviron = inheritEnv( env, envvars ); + env = systemEnv( p->pw_name ); + systemEnviron = setEnv( env, "HOME", p->pw_dir ); + Debug( "user environment:\n%[|''>'\n's" + "system environment:\n%[|''>'\n's" + "end of environments\n", + userEnviron, + systemEnviron ); + + /* + * for user-based authorization schemes, + * add the user to the server's allowed "hosts" list. + */ + for (i = 0; i < td->authNum; i++) { +#ifdef SECURE_RPC + if (td->authorizations[i]->name_length == 9 && + !memcmp( td->authorizations[i]->name, "SUN-DES-1", 9 )) + { + XHostAddress addr; + char netname[MAXNETNAMELEN+1]; + char domainname[MAXNETNAMELEN+1]; + + getdomainname( domainname, sizeof(domainname) ); + user2netname( netname, curuid, domainname ); + addr.family = FamilyNetname; + addr.length = strlen( netname ); + addr.address = netname; + XAddHost( dpy, &addr ); + } +#endif +#ifdef K5AUTH + if (td->authorizations[i]->name_length == 14 && + !memcmp( td->authorizations[i]->name, "MIT-KERBEROS-5", 14 )) + { + /* Update server's auth file with user-specific info. + * Don't need to AddHost because X server will do that + * automatically when it reads the cache we are about + * to point it at. + */ + XauDisposeAuth( td->authorizations[i] ); + td->authorizations[i] = + Krb5GetAuthFor( 14, "MIT-KERBEROS-5", td->name ); + SaveServerAuthorizations( td, td->authorizations, td->authNum ); + } +#endif + } + + if (*dmrcDir) + mergeSessionArgs( TRUE ); + + Debug( "now starting the session\n" ); + +#ifdef USE_PAM + /* the greeter is gone by now ... */ + pconv.conv = PAM_conv_null; + pconv.appdata_ptr = 0; + if ((pretc = pam_set_item( pamh, PAM_CONV, &pconv )) != PAM_SUCCESS) { + ReInitErrorLog(); + LogError( "pam_set_item() for %s failed: %s\n", + curuser, pam_strerror( pamh, pretc ) ); + return 0; + } + ReInitErrorLog(); +#endif + +#ifdef USE_PAM + +# ifdef HAVE_SETUSERCONTEXT + if (setusercontext( lc, p, p->pw_uid, LOGIN_SETGROUP )) { + LogError( "setusercontext(groups) for %s failed: %m\n", + curuser ); + return 0; + } +# else + if (!SetGid( curuser, curgid )) + return 0; +# endif + +# ifdef _AIX + if (!(pam_env = initStrArr( 0 ))) { + resetGids(); + return 0; + } + saved_env = environ; + environ = pam_env; +# endif + removeCreds = 1; /* set it first - i don't trust PAM's rollback */ + pretc = pam_setcred( pamh, 0 ); + ReInitErrorLog(); +# ifdef _AIX + pam_env = environ; + environ = saved_env; +# endif +# ifdef HAVE_INITGROUPS + /* This seems to be a strange place for it, but do it: + - after the initial groups are set + - after pam_setcred might have set something, even in the error case + - before pam_setcred(DELETE_CRED) might need it + */ + if (!saveGids()) + return 0; +# endif + if (pretc != PAM_SUCCESS) { + LogError( "pam_setcred() for %s failed: %s\n", + curuser, pam_strerror( pamh, pretc ) ); + resetGids(); + return 0; + } + + removeSession = 1; /* set it first - same as above */ + pretc = pam_open_session( pamh, 0 ); + ReInitErrorLog(); + if (pretc != PAM_SUCCESS) { + LogError( "pam_open_session() for %s failed: %s\n", + curuser, pam_strerror( pamh, pretc ) ); + resetGids(); + return 0; + } + + /* we don't want sessreg and the startup/reset scripts run with user + credentials. unfortunately, we can reset only the gids. */ + resetGids(); + +# define D_LOGIN_SETGROUP LOGIN_SETGROUP +#else /* USE_PAM */ +# define D_LOGIN_SETGROUP 0 +#endif /* USE_PAM */ + + /* Login succeeded */ + log_to_audit_system (curuser, td->remoteHost, td->name, AU_SUCCESS); + + removeAuth = 1; + chownCtrl( &td->ctrl, curuid ); + endpwent(); +#if !defined(USE_PAM) && defined(USESHADOW) && !defined(_AIX) + endspent(); +#endif + ClearCloseOnFork( mstrtalk.pipe->wfd ); + switch (pid = Fork()) { + case 0: + + sessreg( td, getpid(), curuser, curuid ); + + if (source( systemEnviron, td->startup, td_setup )) { + LogError( "Cannot execute startup script %\"s\n", td->startup ); + exit( 1 ); + } + + if (Setjmp( mstrtalk.errjmp )) + exit( 1 ); + GSet( &mstrtalk ); + + setsid(); + Signal( SIGINT, SIG_DFL ); + + /* Memory leaks are ok here as we exec() soon. */ + +#if defined(USE_PAM) || !defined(_AIX) + +# ifdef USE_PAM + /* pass in environment variables set by libpam and modules it called */ +# ifndef _AIX + pam_env = pam_getenvlist( pamh ); + ReInitErrorLog(); +# endif + if (pam_env) + for (; *pam_env; pam_env++) + userEnviron = putEnv( *pam_env, userEnviron ); +# endif + +# ifdef HAVE_SETLOGIN + if (setlogin( curuser ) < 0) { + LogError( "setlogin for %s failed: %m\n", curuser ); + exit( 1 ); + } +# define D_LOGIN_SETLOGIN LOGIN_SETLOGIN +# else +# define D_LOGIN_SETLOGIN 0 +# endif + +# if defined(USE_PAM) && defined(HAVE_INITGROUPS) + if (!restoreGids()) + exit( 1 ); +# endif + +# ifndef HAVE_SETUSERCONTEXT + +# ifdef USE_PAM + if (!SetUid( curuser, curuid )) + exit( 1 ); +# else + if (!SetUser( curuser, curuid, curgid )) + exit( 1 ); +# endif + +# else /* !HAVE_SETUSERCONTEXT */ + + /* + * Destroy environment. + * We need to do this before setusercontext() because that may + * set or reset some environment variables. + */ + if (!(environ = initStrArr( 0 ))) + exit( 1 ); + + /* + * Set the user's credentials: uid, gid, groups, + * environment variables, resource limits, and umask. + */ + if (setusercontext( lc, p, p->pw_uid, + LOGIN_SETALL & ~(D_LOGIN_SETGROUP|D_LOGIN_SETLOGIN) ) < 0) + { + LogError( "setusercontext for %s failed: %m\n", curuser ); + exit( 1 ); + } + + for (i = 0; environ[i]; i++) + userEnviron = putEnv( environ[i], userEnviron ); + +# endif /* !HAVE_SETUSERCONTEXT */ + +#else /* PAM || !_AIX */ + /* + * Set the user's credentials: uid, gid, groups, + * audit classes, user limits, and umask. + */ + if (setpcred( curuser, NULL ) == -1) { + LogError( "setpcred for %s failed: %m\n", curuser ); + exit( 1 ); + } + + /* + * Set the users process environment. Store protected variables and + * obtain updated user environment list. This call will initialize + * global 'newenv'. + */ + if (setpenv( curuser, PENV_INIT | PENV_ARGV | PENV_NOEXEC, + userEnviron, NULL ) != 0) + { + LogError( "Can't set %s's process environment\n", curuser ); + exit( 1 ); + } + userEnviron = newenv; + +#endif /* _AIX */ + + /* + * for user-based authorization schemes, + * use the password to get the user's credentials. + */ +#ifdef SECURE_RPC + /* do like "keylogin" program */ + if (!curpass[0]) + LogInfo( "No password for NIS provided.\n" ); + else { + char netname[MAXNETNAMELEN+1], secretkey[HEXKEYBYTES+1]; + int nameret, keyret; + int len; + int key_set_ok = 0; + struct key_netstarg netst; + + nameret = getnetname( netname ); + Debug( "user netname: %s\n", netname ); + len = strlen( curpass ); + if (len > 8) + bzero( curpass + 8, len - 8 ); + keyret = getsecretkey( netname, secretkey, curpass ); + Debug( "getsecretkey returns %d, key length %d\n", + keyret, strlen( secretkey ) ); + netst.st_netname = netname; + memcpy( netst.st_priv_key, secretkey, HEXKEYBYTES ); + memset( netst.st_pub_key, 0, HEXKEYBYTES ); + if (key_setnet( &netst ) < 0) + Debug( "Could not set secret key.\n" ); + /* is there a key, and do we have the right password? */ + if (keyret == 1) { + if (*secretkey) { + keyret = key_setsecret( secretkey ); + Debug( "key_setsecret returns %d\n", keyret ); + if (keyret == -1) + LogError( "Failed to set NIS secret key\n" ); + else + key_set_ok = 1; + } else { + /* found a key, but couldn't interpret it */ + LogError( "Password incorrect for NIS principal %s\n", + nameret ? netname : curuser ); + } + } + if (!key_set_ok) + NukeAuth( 9, "SUN-DES-1" ); + bzero( secretkey, strlen( secretkey ) ); + } +#endif +#ifdef K5AUTH + /* do like "kinit" program */ + if (!curpass[0]) + LogInfo( "No password for Kerberos5 provided.\n" ); + else + if ((str = Krb5Init( curuser, curpass, td->name ))) + userEnviron = setEnv( userEnviron, "KRB5CCNAME", str ); + else + NukeAuth( 14, "MIT-KERBEROS-5" ); +#endif /* K5AUTH */ + if (td->autoReLogin) { + GSendInt( D_ReLogin ); + GSendStr( curuser ); + GSendStr( curpass ); + GSendStr( newdmrc ); + } + if (curpass) + bzero( curpass, strlen( curpass ) ); + SetUserAuthorization( td ); + home = getEnv( userEnviron, "HOME" ); + if (home) { + if (chdir( home ) < 0) { + LogError( "Cannot chdir to %s's home %s: %m, using /\n", + curuser, home ); + home = 0; + userEnviron = setEnv( userEnviron, "HOME", "/" ); + goto cdroot; + } + ASPrintf( &lname, td->clientLogFile, td->name ); + if ((lfd = creat( lname, 0600 )) < 0) { + LogWarn( "Cannot create session log file %s: %m\n", lname ); + free( lname ); + goto tmperr; + } + } else { + cdroot: + chdir( "/" ); + tmperr: + ASPrintf( &lname, "/tmp/xerr-%s-%s", curuser, td->name ); + unlink( lname ); + if ((lfd = open( lname, O_WRONLY|O_CREAT|O_EXCL, 0600 )) < 0) { + LogError( "Cannot create fallback session log file %s: %m\n", + lname ); + goto logerr; + } + } + dup2( lfd, 1 ); + dup2( lfd, 2 ); + close( lfd ); + logerr: + free( lname ); + if (!*dmrcDir) + mergeSessionArgs( home != 0 ); + if (!(desksess = iniEntry( curdmrc, "Desktop", "Session", 0 ))) + desksess = "failsafe"; /* only due to OOM */ + GSendInt( D_User ); + GSendInt( curuid ); + GSendStr( curuser ); + GSendStr( desksess ); + close( mstrtalk.pipe->wfd ); + userEnviron = setEnv( userEnviron, "DESKTOP_SESSION", desksess ); + for (i = 0; td->sessionsDirs[i]; i++) { + fname = 0; + if (StrApp( &fname, td->sessionsDirs[i], "/", desksess, ".desktop", (char *)0 )) { + if ((str = iniLoad( fname ))) { + if (!StrCmp( iniEntry( str, "Desktop Entry", "Hidden", 0 ), "true" ) || + !(sessargs = iniEntry( str, "Desktop Entry", "Exec", 0 ))) + sessargs = ""; + free( str ); + free( fname ); + goto gotit; + } + free( fname ); + } + } + if (!strcmp( desksess, "failsafe" ) || + !strcmp( desksess, "default" ) || + !strcmp( desksess, "custom" )) + sessargs = desksess; + else + sessargs = ""; + gotit: + if (!(argv = parseArgs( (char **)0, td->session )) || + !(argv = addStrArr( argv, sessargs, -1 ))) + exit( 1 ); + if (argv[0] && *argv[0]) { + Debug( "executing session %\"[s\n", argv ); + execute( argv, userEnviron ); + LogError( "Session %\"s execution failed: %m\n", argv[0] ); + } else + LogError( "Session has no command/arguments\n" ); + failsafeArgv[0] = td->failsafeClient; + failsafeArgv[1] = 0; + execute( failsafeArgv, userEnviron ); + LogError( "Failsafe client %\"s execution failed: %m\n", + failsafeArgv[0] ); + exit( 1 ); + case -1: + RegisterCloseOnFork( mstrtalk.pipe->wfd ); + LogError( "Forking session on %s failed: %m\n", td->name ); + return 0; + default: + RegisterCloseOnFork( mstrtalk.pipe->wfd ); + Debug( "StartSession, fork succeeded %d\n", pid ); + return pid; + } +} + +void +SessionExit( int status ) +{ + int pid; +#ifdef USE_PAM + int pretc; +#endif + + Signal( SIGTERM, SIG_IGN ); + + if (removeAuth) { + if (source( systemEnviron, td->reset, td_setup )) + LogError( "Cannot execute reset script %\"s\n", td->reset ); + sessreg( td, 0, 0, 0 ); + + switch ((pid = Fork())) { + case 0: +#if defined(USE_PAM) && defined(HAVE_INITGROUPS) + if (restoreGids() && SetUid( curuser, curuid )) +#else + if (SetUser( curuser, curuid, curgid )) +#endif + + { + RemoveUserAuthorization( td ); +#ifdef K5AUTH + Krb5Destroy( td->name ); +#endif /* K5AUTH */ +#if !defined(USE_PAM) && !defined(_AIX) +# ifdef KERBEROS + if (krbtkfile[0]) { + (void)dest_tkt(); +# ifndef NO_AFS + if (k_hasafs()) + (void)k_unlog(); +# endif + } +# endif +#endif /* !USE_PAM && !_AIX*/ + } + exit( 0 ); + case -1: + LogError( "Cannot clean up session: fork() failed: %m" ); + break; + default: + Wait4( pid ); + break; + } + } + +#ifdef USE_PAM + if (removeCreds) { +# ifdef HAVE_INITGROUPS + restoreGids(); +# endif + if (removeSession) + if ((pretc = pam_close_session( pamh, 0 )) != PAM_SUCCESS) + LogError( "pam_close_session() failed: %s\n", + pam_strerror( pamh, pretc ) ); + if ((pretc = pam_setcred( pamh, PAM_DELETE_CRED )) != PAM_SUCCESS) + LogError( "pam_setcred(DELETE_CRED) failed: %s\n", + pam_strerror( pamh, pretc ) ); + resetGids(); + } + if (pamh) { + pam_end( pamh, PAM_SUCCESS ); + ReInitErrorLog(); + } +#endif + + /* make sure the server gets reset after the session is over */ + if (td->serverPid >= 2) { + if (!td->terminateServer && td->resetSignal) + TerminateProcess( td->serverPid, td->resetSignal ); + } else + ResetServer( td ); + Debug( "display %s exiting with status %d\n", td->name, status ); + exit( status ); +} + +int +ReadDmrc() +{ + char *data, *fname = 0; + int len, pid, pfd[2], err; + + if (!dmrcuser || !dmrcuser[0] || !(p = getpwnam( dmrcuser ))) + return GE_NoUser; + + if (*dmrcDir) { + if (!StrApp( &fname, dmrcDir, "/", dmrcuser, ".dmrc", (char *)0 )) + return GE_Error; + if (!(curdmrc = iniLoad( fname ))) { + free( fname ); + return GE_Ok; + } + free( fname ); + return GE_NoFile; + } + + if (!StrApp( &fname, p->pw_dir, "/.dmrc", (char *)0 )) + return GE_Error; + if (pipe( pfd )) + return GE_Error; + if ((pid = Fork()) < 0) { + close( pfd[0] ); + close( pfd[1] ); + return GE_Error; + } + if (!pid) { + if (!SetUser( p->pw_name, p->pw_uid, p->pw_gid )) + exit( 0 ); + if (!(data = iniLoad( fname ))) { + static const int m1 = -1; + write( pfd[1], &m1, sizeof(int) ); + exit( 0 ); + } + len = strlen( data ); + write( pfd[1], &len, sizeof(int) ); + write( pfd[1], data, len + 1 ); + exit( 0 ); + } + close( pfd[1] ); + free( fname ); + err = GE_Error; + if (Reader( pfd[0], &len, sizeof(int) ) == sizeof(int)) { + if (len == -1) + err = GE_Denied; + else if ((curdmrc = Malloc( len + 1 ))) { + if (Reader( pfd[0], curdmrc, len + 1 ) == len + 1) + err = GE_Ok; + else { + free( curdmrc ); + curdmrc = 0; + } + } + } + close( pfd[0] ); + (void)Wait4( pid ); + return err; +} diff --git a/tdm/backend/consolekit.c b/tdm/backend/consolekit.c new file mode 100644 index 000000000..61d0b165e --- /dev/null +++ b/tdm/backend/consolekit.c @@ -0,0 +1,557 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + + Copyright (C) 2006-2007 William Jon McCann <[email protected]> + Copyright (C) 2007 Kevin Kofler <[email protected]> + + 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 Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#include "dm.h" +#include "dm_auth.h" +#include "dm_error.h" + +#include <stdlib.h> +#include <string.h> +#include <pwd.h> + +#define DBUS_API_SUBJECT_TO_CHANGE +#include <dbus/dbus.h> + +#include "consolekit.h" + + +#define CK_NAME "org.freedesktop.ConsoleKit" +#define CK_PATH "/org/freedesktop/ConsoleKit" +#define CK_INTERFACE "org.freedesktop.ConsoleKit" +#define CK_MANAGER_PATH "/org/freedesktop/ConsoleKit/Manager" +#define CK_MANAGER_INTERFACE "org.freedesktop.ConsoleKit.Manager" +#define CK_SESSION_INTERFACE "org.freedesktop.ConsoleKit.Session" + +static DBusConnection *private_connection = NULL; + +static void +add_param_int (DBusMessageIter *iter_struct, + const char *key, + int value) +{ + DBusMessageIter iter_struct_entry; + DBusMessageIter iter_var; + + dbus_message_iter_open_container (iter_struct, + DBUS_TYPE_STRUCT, + NULL, + &iter_struct_entry); + + dbus_message_iter_append_basic (&iter_struct_entry, + DBUS_TYPE_STRING, + &key); + + dbus_message_iter_open_container (&iter_struct_entry, + DBUS_TYPE_VARIANT, + DBUS_TYPE_INT32_AS_STRING, + &iter_var); + + dbus_message_iter_append_basic (&iter_var, + DBUS_TYPE_INT32, + &value); + + dbus_message_iter_close_container (&iter_struct_entry, + &iter_var); + + dbus_message_iter_close_container (iter_struct, &iter_struct_entry); +} + +static void +add_param_boolean (DBusMessageIter *iter_struct, + const char *key, + int value) +{ + DBusMessageIter iter_struct_entry; + DBusMessageIter iter_var; + + dbus_message_iter_open_container (iter_struct, + DBUS_TYPE_STRUCT, + NULL, + &iter_struct_entry); + + dbus_message_iter_append_basic (&iter_struct_entry, + DBUS_TYPE_STRING, + &key); + + dbus_message_iter_open_container (&iter_struct_entry, + DBUS_TYPE_VARIANT, + DBUS_TYPE_BOOLEAN_AS_STRING, + &iter_var); + + dbus_message_iter_append_basic (&iter_var, + DBUS_TYPE_BOOLEAN, + &value); + + dbus_message_iter_close_container (&iter_struct_entry, + &iter_var); + + dbus_message_iter_close_container (iter_struct, &iter_struct_entry); +} + +static void +add_param_string (DBusMessageIter *iter_struct, + const char *key, + const char *value) +{ + DBusMessageIter iter_struct_entry; + DBusMessageIter iter_var; + + dbus_message_iter_open_container (iter_struct, + DBUS_TYPE_STRUCT, + NULL, + &iter_struct_entry); + + dbus_message_iter_append_basic (&iter_struct_entry, + DBUS_TYPE_STRING, + &key); + + dbus_message_iter_open_container (&iter_struct_entry, + DBUS_TYPE_VARIANT, + DBUS_TYPE_STRING_AS_STRING, + &iter_var); + + dbus_message_iter_append_basic (&iter_var, + DBUS_TYPE_STRING, + &value); + + dbus_message_iter_close_container (&iter_struct_entry, + &iter_var); + + dbus_message_iter_close_container (iter_struct, &iter_struct_entry); +} + +static int +session_get_x11_display (DBusConnection *connection, + const char *ssid, + char **str) +{ + DBusError error; + DBusMessage *message; + DBusMessage *reply; + DBusMessageIter iter; + const char *value; + + if (str != NULL) { + *str = NULL; + } + + message = dbus_message_new_method_call (CK_NAME, + ssid, + CK_SESSION_INTERFACE, + "GetX11Display"); + if (message == NULL) { + Debug ("ConsoleKit: Couldn't allocate the D-Bus message"); + return FALSE; + } + + dbus_error_init (&error); + reply = dbus_connection_send_with_reply_and_block (connection, + message, + -1, &error); + if (dbus_error_is_set (&error)) { + Debug ("ConsoleKit: %s raised:\n %s\n\n", error.name, error.message); + reply = NULL; + } + + dbus_connection_flush (connection); + dbus_message_unref (message); + + if (reply == NULL) { + return FALSE; + } + + dbus_message_iter_init (reply, &iter); + dbus_message_iter_get_basic (&iter, &value); + if (str != NULL) { + *str = strdup (value); + } + dbus_message_unref (reply); + + return TRUE; +} + +static int +session_unlock (DBusConnection *connection, + const char *ssid) +{ + DBusError error; + DBusMessage *message; + DBusMessage *reply; + + Debug ("ConsoleKit: Unlocking session %s", ssid); + message = dbus_message_new_method_call (CK_NAME, + ssid, + CK_SESSION_INTERFACE, + "Unlock"); + if (message == NULL) { + Debug ("ConsoleKit: Couldn't allocate the D-Bus message"); + return FALSE; + } + + dbus_error_init (&error); + reply = dbus_connection_send_with_reply_and_block (connection, + message, + -1, &error); + dbus_message_unref (message); + dbus_message_unref (reply); + dbus_connection_flush (connection); + + if (dbus_error_is_set (&error)) { + Debug ("ConsoleKit: %s raised:\n %s\n\n", error.name, error.message); + return FALSE; + } + + return TRUE; +} + +/* from libhal */ +static char ** +get_path_array_from_iter (DBusMessageIter *iter, + int *num_elements) +{ + int count; + char **buffer; + + count = 0; + buffer = (char **)malloc (sizeof (char *) * 8); + + if (buffer == NULL) + goto oom; + + buffer[0] = NULL; + while (dbus_message_iter_get_arg_type (iter) == DBUS_TYPE_OBJECT_PATH) { + const char *value; + char *str; + + if ((count % 8) == 0 && count != 0) { + buffer = realloc (buffer, sizeof (char *) * (count + 8)); + if (buffer == NULL) + goto oom; + } + + dbus_message_iter_get_basic (iter, &value); + str = strdup (value); + if (str == NULL) + goto oom; + + buffer[count] = str; + + dbus_message_iter_next (iter); + count++; + } + + if ((count % 8) == 0) { + buffer = realloc (buffer, sizeof (char *) * (count + 1)); + if (buffer == NULL) + goto oom; + } + + buffer[count] = NULL; + if (num_elements != NULL) + *num_elements = count; + return buffer; + +oom: + LogWarn ("%s %d : error allocating memory\n", __FILE__, __LINE__); + return NULL; + +} + +static char ** +get_sessions_for_user (DBusConnection *connection, + const char *user, + const char *x11_display) +{ + DBusError error; + DBusMessage *message; + DBusMessage *reply; + DBusMessageIter iter; + DBusMessageIter iter_reply; + DBusMessageIter iter_array; + struct passwd *pwent; + char **sessions; + + sessions = NULL; + message = NULL; + reply = NULL; + + pwent = getpwnam (user); + + dbus_error_init (&error); + message = dbus_message_new_method_call (CK_NAME, + CK_MANAGER_PATH, + CK_MANAGER_INTERFACE, + "GetSessionsForUser"); + if (message == NULL) { + Debug ("ConsoleKit: Couldn't allocate the D-Bus message"); + goto out; + } + + dbus_message_iter_init_append (message, &iter); + dbus_message_iter_append_basic (&iter, + DBUS_TYPE_UINT32, + &pwent->pw_uid); + + dbus_error_init (&error); + reply = dbus_connection_send_with_reply_and_block (connection, + message, + -1, &error); + dbus_connection_flush (connection); + + if (dbus_error_is_set (&error)) { + Debug ("ConsoleKit: %s raised:\n %s\n\n", error.name, error.message); + goto out; + } + + if (reply == NULL) { + Debug ("ConsoleKit: No reply for GetSessionsForUser"); + goto out; + } + + dbus_message_iter_init (reply, &iter_reply); + if (dbus_message_iter_get_arg_type (&iter_reply) != DBUS_TYPE_ARRAY) { + Debug ("ConsoleKit: Wrong reply for GetSessionsForUser - expecting an array."); + goto out; + } + + dbus_message_iter_recurse (&iter_reply, &iter_array); + sessions = get_path_array_from_iter (&iter_array, NULL); + + out: + if (message != NULL) { + dbus_message_unref (message); + } + if (reply != NULL) { + dbus_message_unref (reply); + } + + return sessions; +} + +void +unlock_ck_session (const char *user, + const char *x11_display) +{ + DBusError error; + DBusConnection *connection; + char **sessions; + int i; + + Debug ("ConsoleKit: Unlocking session for %s on %s", user, x11_display); + + dbus_error_init (&error); + connection = dbus_bus_get (DBUS_BUS_SYSTEM, &error); + if (connection == NULL) { + Debug ("ConsoleKit: Failed to connect to the D-Bus daemon: %s", error.message); + dbus_error_free (&error); + return; + } + + sessions = get_sessions_for_user (connection, user, x11_display); + if (sessions == NULL || sessions[0] == NULL) { + Debug ("ConsoleKit: no sessions found"); + return; + } + + for (i = 0; sessions[i] != NULL; i++) { + char *ssid; + char *xdisplay; + + ssid = sessions[i]; + session_get_x11_display (connection, ssid, &xdisplay); + Debug ("ConsoleKit: session %s has DISPLAY %s", ssid, xdisplay); + + if (xdisplay != NULL + && x11_display != NULL + && strcmp (xdisplay, x11_display) == 0) { + int res; + + res = session_unlock (connection, ssid); + if (! res) { + LogError ("ConsoleKit: Unable to unlock %s", ssid); + } + } + + free (xdisplay); + } + + freeStrArr (sessions); +} + +char * +open_ck_session (struct passwd *pwent, + struct display *d) +{ + DBusConnection *connection; + DBusError error; + DBusMessage *message; + DBusMessage *reply; + DBusMessageIter iter; + DBusMessageIter iter_struct; + char *cookie; + + cookie = NULL; + + if (pwent == NULL) { + Debug ("ConsoleKit: NULL user passed as parameter"); + return NULL; + } + + Debug ("ConsoleKit: Opening session for %s", pwent->pw_name); + + dbus_error_init (&error); + connection = dbus_bus_get_private (DBUS_BUS_SYSTEM, &error); + private_connection = connection; + + if (connection == NULL) { + Debug ("ConsoleKit: Failed to connect to the D-Bus daemon: %s", error.message); + dbus_error_free (&error); + return NULL; + } + + dbus_connection_set_exit_on_disconnect (connection, FALSE); + /* FIXME: What to do about these? + dbus_connection_set_watch_functions( connection, + dbusAddWatch, + dbusRemoveWatch, + dbusToggleWatch, + data, 0 ); + dbus_connection_set_timeout_functions( connection, + dbusAddTimeout, + dbusRemoveTimeout, + dbusToggleTimeout, + data, 0 ); + dbus_connection_set_wakeup_main_function( connection, + dbusWakeupMain, + data, 0 ); */ + + dbus_error_init (&error); + message = dbus_message_new_method_call (CK_NAME, + CK_MANAGER_PATH, + CK_MANAGER_INTERFACE, + "OpenSessionWithParameters"); + if (message == NULL) { + Debug ("ConsoleKit: Couldn't allocate the D-Bus message"); + return NULL; + } + + dbus_message_iter_init_append (message, &iter); + dbus_message_iter_open_container (&iter, + DBUS_TYPE_ARRAY, + DBUS_STRUCT_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_VARIANT_AS_STRING + DBUS_STRUCT_END_CHAR_AS_STRING, + &iter_struct); + + add_param_int (&iter_struct, "user", pwent->pw_uid); + add_param_string (&iter_struct, "x11-display", d->name); + add_param_boolean (&iter_struct, "is-local", ((d->displayType & d_location) == dLocal)); +#ifdef XDMCP + if ((d->displayType & d_location) != dLocal) { + add_param_string (&iter_struct, "remote-host-name", d->remoteHost); + } +#endif + +#ifdef HAVE_VTS + if (d->serverVT > 0) { + char device[20]; + + /* FIXME: how does xorg construct this */ + sprintf(device, "/dev/tty%d", d->serverVT); + add_param_string (&iter_struct, "x11-display-device", device); + } +#endif + + dbus_message_iter_close_container (&iter, &iter_struct); + + reply = dbus_connection_send_with_reply_and_block (connection, + message, + -1, &error); + if (dbus_error_is_set (&error)) { + Debug ("ConsoleKit: %s raised:\n %s\n\n", error.name, error.message); + reply = NULL; + } + + dbus_connection_flush (connection); + + dbus_message_unref (message); + dbus_error_free (&error); + + if (reply != NULL) { + const char *value; + + dbus_message_iter_init (reply, &iter); + dbus_message_iter_get_basic (&iter, &value); + cookie = strdup (value); + dbus_message_unref (reply); + } + + return cookie; +} + +void +close_ck_session (const char *cookie) +{ + DBusError error; + DBusMessage *message; + DBusMessage *reply; + DBusMessageIter iter; + + if (cookie == NULL) { + return; + } + + if (private_connection == NULL) { + return; + } + + dbus_error_init (&error); + message = dbus_message_new_method_call (CK_NAME, + CK_MANAGER_PATH, + CK_MANAGER_INTERFACE, + "CloseSession"); + if (message == NULL) { + Debug ("ConsoleKit: Couldn't allocate the D-Bus message"); + return; + } + + dbus_message_iter_init_append (message, &iter); + dbus_message_iter_append_basic (&iter, + DBUS_TYPE_STRING, + &cookie); + + reply = dbus_connection_send_with_reply_and_block (private_connection, + message, + -1, &error); + if (dbus_error_is_set (&error)) { + Debug ("ConsoleKit: %s raised:\n %s\n\n", error.name, error.message); + reply = NULL; + } + + dbus_connection_flush (private_connection); + + dbus_message_unref (message); + dbus_error_free (&error); + + dbus_connection_close (private_connection); + private_connection = NULL; +} diff --git a/tdm/backend/consolekit.h b/tdm/backend/consolekit.h new file mode 100644 index 000000000..e385e3f91 --- /dev/null +++ b/tdm/backend/consolekit.h @@ -0,0 +1,36 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + + Copyright (C) 2006 William Jon McCann <[email protected]> + Copyright (C) 2007 Kevin Kofler <[email protected]> + + 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 Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + + +#ifndef __CONSOLE_KIT_H +#define __CONSOLE_KIT_H + +#include <pwd.h> + +struct display; + +char * open_ck_session (struct passwd *pwent, + struct display *display); +void close_ck_session (const char *cookie); +void unlock_ck_session (const char *user, + const char *x11_display); + +#endif /* __CONSOLE_KIT_H */ diff --git a/tdm/backend/ctrl.c b/tdm/backend/ctrl.c new file mode 100644 index 000000000..622c9370d --- /dev/null +++ b/tdm/backend/ctrl.c @@ -0,0 +1,1015 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2001-2005 Oswald Buddenhagen <[email protected]> + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + * + * display manager + */ + +#include "dm.h" +#include "dm_socket.h" +#include "dm_error.h" + +#include <string.h> +#include <signal.h> +#include <pwd.h> + +static void +acceptSock( CtrlRec *cr ) +{ + struct cmdsock *cs; + int fd; + + if ((fd = accept( cr->fd, 0, 0 )) < 0) { + bust: + LogError( "Error accepting command connection\n" ); + return; + } + if (!(cs = Malloc( sizeof(*cs) ))) { + close( fd ); + goto bust; + } + cs->sock.fd = fd; + cs->sock.buffer = 0; + cs->sock.buflen = 0; + cs->next = cr->css; + cr->css = cs; + fcntl( fd, F_SETFL, fcntl( fd, F_GETFL ) | O_NONBLOCK ); + RegisterCloseOnFork( fd ); + RegisterInput( fd ); +} + +static void +nukeSock( struct cmdsock *cs ) +{ + UnregisterInput( cs->sock.fd ); + CloseNClearCloseOnFork( cs->sock.fd ); + if (cs->sock.buffer) + free( cs->sock.buffer ); + free( cs ); +} + +#ifdef HONORS_SOCKET_PERMS +static CtrlRec ctrl = { 0, 0, -1, 0, 0, { -1, 0, 0 } }; +#else +static CtrlRec ctrl = { 0, 0, 0, -1, 0, 0, { -1, 0, 0 } }; + +static int mkTempDir( char *dir ) +{ + int i, l = strlen( dir ) - 6; + + for (i = 0; i < 100; i++) { + randomStr( dir + l ); + if (!mkdir( dir, 0700 )) + return True; + if (errno != EEXIST) + break; + } + return False; +} +#endif + +void +openCtrl( struct display *d ) +{ + CtrlRec *cr; + const char *dname; + char *sockdir; + struct sockaddr_un sa; + + if (!*fifoDir) + return; + if (d) { + cr = &d->ctrl, dname = d->name; + if (!memcmp( dname, "localhost:", 10 )) + dname += 9; + } else + cr = &ctrl, dname = 0; + if (cr->fifo.fd < 0) { + if (mkdir( fifoDir, 0755 )) { + if (errno != EEXIST) { + LogError( "mkdir %\"s failed; no control FiFos will be available\n", + fifoDir ); + return; + } + } else + chmod( fifoDir, 0755 ); /* override umask */ + StrApp( &cr->fpath, fifoDir, dname ? "/xdmctl-" : "/xdmctl", + dname, (char *)0 ); + if (cr->fpath) { + unlink( cr->fpath ); + if (mkfifo( cr->fpath, 0 ) < 0) + LogError( "Cannot create control FiFo %\"s\n", cr->fpath ); + else { + cr->gid = fifoGroup; + if (!d) + chown( cr->fpath, -1, fifoGroup ); + chmod( cr->fpath, 0620 ); + if ((cr->fifo.fd = open( cr->fpath, O_RDWR | O_NONBLOCK )) >= 0) { + RegisterCloseOnFork( cr->fifo.fd ); + RegisterInput( cr->fifo.fd ); + goto fifok; + } + unlink( cr->fpath ); + LogError( "Cannot open control FiFo %\"s\n", cr->fpath ); + } + free( cr->fpath ); + cr->fpath = 0; + } + } + fifok: + if (cr->fd < 0) { + /* fifoDir is created above already */ + sockdir = 0; + StrApp( &sockdir, fifoDir, dname ? "/dmctl-" : "/dmctl", + dname, (char *)0 ); + if (sockdir) { + StrApp( &cr->path, sockdir, "/socket", (char *)0 ); + if (cr->path) { + if (strlen( cr->path ) >= sizeof(sa.sun_path)) + LogError( "path %\"s too long; no control sockets will be available\n", + cr->path ); +#ifdef HONORS_SOCKET_PERMS + else if (mkdir( sockdir, 0700 ) && errno != EEXIST) + LogError( "mkdir %\"s failed; no control sockets will be available\n", + sockdir ); + else if (unlink( cr->path ) && errno != ENOENT) + LogError( "unlink %\"s failed: %m; control socket will not be available\n", + cr->path ); + else { +#else + else if (unlink( sockdir ) && errno != ENOENT) + LogError( "unlink %\"s failed: %m; control socket will not be available\n", + sockdir ); + else if (!StrApp( &cr->realdir, sockdir, "-XXXXXX", (char *)0)) + ; + else if (!mkTempDir( cr->realdir )) { + LogError( "mkdir %\"s failed: %m; control socket will not be available\n", + cr->realdir ); + free( cr->realdir ); + cr->realdir = 0; + } else if (symlink( cr->realdir, sockdir )) { + LogError( "symlink %\"s => %\"s failed: %m; control socket will not be available\n", + sockdir, cr->realdir ); + rmdir( cr->realdir ); + free( cr->realdir ); + cr->realdir = 0; + } else { + chown( sockdir, 0, d ? 0 : fifoGroup ); + chmod( sockdir, 0750 ); +#endif + if ((cr->fd = socket( PF_UNIX, SOCK_STREAM, 0 )) < 0) + LogError( "Cannot create control socket\n" ); + else { + sa.sun_family = AF_UNIX; + strcpy( sa.sun_path, cr->path ); + if (!bind( cr->fd, (struct sockaddr *)&sa, sizeof(sa) )) { + if (!listen( cr->fd, 5 )) { +#ifdef HONORS_SOCKET_PERMS + chmod( cr->path, 0660 ); + if (!d) + chown( cr->path, -1, fifoGroup ); + chmod( sockdir, 0755 ); +#else + chmod( cr->path, 0666 ); +#endif + RegisterCloseOnFork( cr->fd ); + RegisterInput( cr->fd ); + free( sockdir ); + return; + } + unlink( cr->path ); + LogError( "Cannot listen on control socket %\"s\n", + cr->path ); + } else + LogError( "Cannot bind control socket %\"s\n", + cr->path ); + close( cr->fd ); + cr->fd = -1; + } +#ifdef HONORS_SOCKET_PERMS + rmdir( sockdir ); +#else + unlink( sockdir ); + rmdir( cr->realdir ); + free( cr->realdir ); + cr->realdir = 0; +#endif + } + free( cr->path ); + cr->path = 0; + } + free( sockdir ); + } + } +} + +void +closeCtrl( struct display *d ) +{ + CtrlRec *cr = d ? &d->ctrl : &ctrl; + + if (cr->fd >= 0) { + UnregisterInput( cr->fd ); + CloseNClearCloseOnFork( cr->fd ); + cr->fd = -1; + unlink( cr->path ); + *strrchr( cr->path, '/' ) = 0; +#ifdef HONORS_SOCKET_PERMS + rmdir( cr->path ); +#else + unlink( cr->path ); + rmdir( cr->realdir ); + free( cr->realdir ); + cr->realdir = 0; +#endif + free( cr->path ); + cr->path = 0; + while (cr->css) { + struct cmdsock *cs = cr->css; + cr->css = cs->next; + nukeSock( cs ); + } + } + if (cr->fifo.fd >= 0) { + UnregisterInput( cr->fifo.fd ); + CloseNClearCloseOnFork( cr->fifo.fd ); + cr->fifo.fd = -1; + unlink( cr->fpath ); + free( cr->fpath ); + cr->fpath = 0; + if (cr->fifo.buffer) + free( cr->fifo.buffer ); + cr->fifo.buffer = 0; + cr->fifo.buflen = 0; + } +} + +void +chownCtrl( CtrlRec *cr, int uid ) +{ + if (cr->fpath) + chown( cr->fpath, uid, -1 ); + if (cr->path) +#ifdef HONORS_SOCKET_PERMS + chown( cr->path, uid, -1 ); +#else + chown( cr->realdir, uid, -1 ); +#endif +} + +void +updateCtrl( void ) +{ + unsigned ffl, slc; + + ffl = 0; + if (ctrl.path) + for (ffl = strlen( ctrl.path ), slc = 2; ;) + if (ctrl.path[--ffl] == '/') + if (!--slc) + break; + if (ffl != strlen( fifoDir ) || memcmp( fifoDir, ctrl.path, ffl ) || + ctrl.gid != fifoGroup) + { + closeCtrl( 0 ); + openCtrl( 0 ); + } +} + + +static void +fLog( struct display *d, int fd, const char *sts, const char *msg, ... ) +{ + char *fmsg, *otxt; + const char *what; + int olen; + va_list va; + + va_start( va, msg ); + VASPrintf( &fmsg, msg, va ); + va_end( va ); + if (!fmsg) + return; + if (fd >= 0) { + olen = ASPrintf( &otxt, "%s\t%\\s\n", sts, fmsg ); + if (otxt) { + Writer( fd, otxt, olen ); + free( otxt ); + } + what = "socket"; + } else + what = "FiFo"; + if (d) + Debug( "control %s for %s: %s - %s", what, d->name, sts, fmsg ); + else + Debug( "global control %s: %s - %s", what, sts, fmsg ); + free( fmsg ); +} + + +static char * +unQuote( const char *str ) +{ + char *ret, *adp; + + if (!(ret = Malloc( strlen( str ) + 1 ))) + return 0; + for (adp = ret; *str; str++, adp++) + if (*str == '\\') + switch (*++str) { + case 0: str--; /* fallthrough */ + case '\\': *adp = '\\'; break; + case 'n': *adp = '\n'; break; + case 't': *adp = '\t'; break; + default: *adp++ = '\\'; *adp = *str; break; + } + else + *adp = *str; + *adp = 0; + return ret; +} + +static void +str_cat_l( char **bp, const char *str, int max ) +{ + int dnl = StrNLen( str, max ); + memcpy( *bp, str, dnl ); + *bp += dnl; +} + +static void +str_cat( char **bp, const char *str ) +{ + int dnl = strlen( str ); + memcpy( *bp, str, dnl ); + *bp += dnl; +} + +static void +sd_cat( char **bp, SdRec *sdr ) +{ + if (sdr->how == SHUT_HALT) + str_cat( bp, "halt," ); + else + str_cat( bp, "reboot," ); + if (sdr->start == TO_INF) + str_cat( bp, "0," ); + else + *bp += sprintf( *bp, "%d,", sdr->start ); + if (sdr->timeout == TO_INF) + str_cat( bp, "-1," ); + else + *bp += sprintf( *bp, "%d,", sdr->timeout ); + if (sdr->force == SHUT_ASK) + str_cat( bp, "ask" ); + else if (sdr->force == SHUT_FORCE) + str_cat( bp, "force" ); + else if (sdr->force == SHUT_FORCEMY) + str_cat( bp, "forcemy" ); + else + str_cat( bp, "cancel" ); + *bp += sprintf( *bp, ",%d,%s", sdr->uid, sdr->osname ? sdr->osname : "-" ); +} + +static void +emitXSessC( struct display *di, struct display *d, void *ctx ) +{ + char *dname, *bp; + char cbuf[1024]; + + bp = cbuf; + *bp++ = '\t'; + dname = di->name; + if (!memcmp( dname, "localhost:", 10 )) + dname += 9; + str_cat_l( &bp, dname, sizeof(cbuf)/2 ); + *bp++ = ','; +#ifdef HAVE_VTS + if (di->serverVT) + bp += sprintf( bp, "vt%d", di->serverVT ); +#endif + *bp++ = ','; +#ifdef XDMCP + if (di->status == remoteLogin) { + *bp++ = ','; + str_cat_l( &bp, di->remoteHost, sizeof(cbuf)/3 ); + } else +#endif + { + if (di->userName) + str_cat_l( &bp, di->userName, sizeof(cbuf)/5 ); + *bp++ = ','; + if (di->sessName) + str_cat_l( &bp, di->sessName, sizeof(cbuf)/5 ); + } + *bp++ = ','; + if (di == d) + *bp++ = '*'; + if (di->userSess >= 0 && + (d ? (d->userSess != di->userSess && + (d->allowNuke == SHUT_NONE || + (d->allowNuke == SHUT_ROOT && d->userSess))) : + !fifoAllowNuke)) + *bp++ = '!'; + Writer( (int)ctx, cbuf, bp - cbuf ); +} + +static void +emitTTYSessC( STRUCTUTMP *ut, struct display *d, void *ctx ) +{ + struct passwd *pw; + char *bp; + int vt, l; + char cbuf[sizeof(ut->ut_line) + sizeof(ut->ut_user) + sizeof(ut->ut_host) + 16]; + char user[sizeof(ut->ut_user) + 1]; + +#ifndef BSD_UTMP + if (ut->ut_type != USER_PROCESS) + l = 0; + else +#endif + { + l = StrNLen( ut->ut_user, sizeof(ut->ut_user) ); + memcpy( user, ut->ut_user, l ); + } + user[l] = 0; + bp = cbuf; + *bp++ = '\t'; + str_cat_l( &bp, ut->ut_line, sizeof(ut->ut_line) ); + *bp++ = ','; + if (*ut->ut_host) { + *bp++ = '@'; + str_cat_l( &bp, ut->ut_host, sizeof(ut->ut_host) ); + } +#ifdef HAVE_VTS + else if ((vt = TTYtoVT( ut->ut_line ))) + bp += sprintf( bp, "vt%d", vt ); +#endif + *bp++ = ','; + str_cat( &bp, user ); + *bp++ = ','; + /* blank: session type unknown */ + *bp++ = ','; + /* blank: certainly not querying display */ + *bp++ = 't'; + if (*user && + (d ? ((d->allowNuke == SHUT_NONE || + (d->allowNuke == SHUT_ROOT && d->userSess)) && + (!(pw = getpwnam( user )) || d->userSess != (int)pw->pw_uid)) : + !fifoAllowNuke)) + *bp++ = '!'; + Writer( (int)ctx, cbuf, bp - cbuf ); +} + +static void +processCtrl( const char *string, int len, int fd, struct display *d ) +{ +#define Reply(t) Writer (fd, t, strlen (t)) + + struct display *di; + const char *word; + char **ar, **ap, *args, *bp; + SdRec sdr; + char cbuf[1024]; + + if (!(ar = initStrArr( 0 ))) + return; + for (word = string; ; string++, len--) + if (!len || *string == '\t') { + if (!(ar = addStrArr( ar, word, string - word ))) + return; + if (!len) + break; + word = string + 1; + } + word = fd >= 0 ? "socket" : "FiFo"; + if (d) + Debug( "control %s for %s received %'[s\n", word, d->name, ar ); + else + Debug( "global control %s received %'[s\n", word, ar ); + if (ar[0]) { + if (fd >= 0 && !strcmp( ar[0], "caps" )) { + if (ar[1]) + goto exce; + Reply( "ok\ttdm\tlist\t" ); + if (bootManager != BO_NONE) + Reply( "bootoptions\t" ); + if (d) { + if ((d->displayType & d_location) == dLocal) +#ifdef HAVE_VTS + Reply( "local\tactivate\t" ); +#else + Reply( "local\t" ); +#endif + if (d->allowShutdown != SHUT_NONE) { + if (d->allowShutdown == SHUT_ROOT && d->userSess) + Reply( "shutdown root\t" ); + else + Reply( "shutdown\t" ); + Reply( "shutdown ask\t" ); + if (d->allowNuke != SHUT_NONE) { + if (d->allowNuke == SHUT_ROOT && d->userSess) + Reply( "nuke root\t" ); + else + Reply( "nuke\t" ); + } + } + if ((d->displayType & d_location) == dLocal && + AnyReserveDisplays()) + Writer( fd, cbuf, sprintf( cbuf, "reserve %d\t", + idleReserveDisplays() ) ); + Reply( "lock\tsuicide\n" ); + } else { + if (fifoAllowShutdown) { + Reply( "shutdown\t" ); + if (fifoAllowNuke) + Reply( "nuke\t" ); + } + if (AnyReserveDisplays()) + Writer( fd, cbuf, sprintf( cbuf, "reserve %d\t", + idleReserveDisplays() ) ); +#ifdef HAVE_VTS + Reply( "login\tactivate\n" ); +#else + Reply( "login\n" ); +#endif + } + goto bust; + } else if (fd >= 0 && !strcmp( ar[0], "list" )) { + int flags = lstRemote | lstTTY; + if (ar[1]) { + if (!strcmp( ar[1], "all" )) + flags = lstRemote | lstPassive | lstTTY; + else if (!strcmp( ar[1], "alllocal" )) + flags = lstPassive | lstTTY; + else { + fLog( d, fd, "bad", "invalid list scope %\"s", ar[1] ); + goto bust; + } + if (ar[2]) + goto exce; + } + Reply( "ok" ); + ListSessions( flags, d, (void *)fd, emitXSessC, emitTTYSessC ); + Reply( "\n" ); + goto bust; + } else if (!strcmp( ar[0], "reserve" )) { + int lt = 60; /* XXX make default timeout configurable? */ + if (ar[1]) { + lt = strtol( ar[1], &bp, 10 ); + if (lt < 15 || *bp) { + fLog( d, fd, "bad", "invalid timeout %\"s", ar[1] ); + goto bust; + } + if (ar[2]) + goto exce; + } + if (d && (d->displayType & d_location) != dLocal) { + fLog( d, fd, "perm", "display is not local" ); + goto bust; + } + if (!StartReserveDisplay( lt )) { + fLog( d, fd, "noent", "no reserve display available" ); + goto bust; + } +#ifdef HAVE_VTS + } else if (!strcmp( ar[0], "activate" )) { + int vt; + if (!ar[1]) + goto miss; + if (ar[2]) + goto exce; + if (d && (d->displayType & d_location) != dLocal) { + fLog( d, fd, "perm", "display is not local" ); + goto bust; + } + if (ar[1][0] != 'v' || ar[1][1] != 't' || + (vt = atoi( ar[1] + 2 )) <= 0) + { + if (!(di = FindDisplayByName( ar[1] ))) { + fLog( d, fd, "noent", "display not found" ); + goto bust; + } + if ((di->displayType & d_location) != dLocal) { + fLog( d, fd, "inval", "target display is not local" ); + goto bust; + } + if (!di->serverVT) { + fLog( d, fd, "noent", "target display has no VT assigned" ); + goto bust; + } + vt = di->serverVT; + } + if (!activateVT( vt )) { + fLog( d, fd, "inval", "VT switch failed" ); + goto bust; + } +#endif + } else if (!strcmp( ar[0], "shutdown" )) { + ap = ar; + if (!*++ap) + goto miss; + sdr.force = SHUT_CANCEL; + sdr.osname = 0; + if (!strcmp( *ap, "status" )) { + if (fd < 0) + goto bust; + if (*++ap) + goto exce; + bp = cbuf; + *bp++ = 'o'; + *bp++ = 'k'; + if (sdRec.how) { + str_cat( &bp, "\tglobal," ); + sd_cat( &bp, &sdRec ); + } + if (d && d->hstent->sdRec.how) { + str_cat( &bp, "\tlocal," ); + sd_cat( &bp, &d->hstent->sdRec ); + } + *bp++ = '\n'; + Writer( fd, cbuf, bp - cbuf ); + goto bust; + } else if (!strcmp( *ap, "cancel" )) { + sdr.how = 0; + sdr.start = 0; + if (ap[1]) { + if (!d) + goto exce; + if (!strcmp( *++ap, "global" )) + sdr.start = TO_INF; + else if (strcmp( *ap, "local" )) { + fLog( d, fd, "bad", "invalid cancel scope %\"s", *ap ); + goto bust; + } + } + } else { + if (!strcmp( *ap, "reboot" )) + sdr.how = SHUT_REBOOT; + else if (!strcmp( *ap, "halt" )) + sdr.how = SHUT_HALT; + else { + fLog( d, fd, "bad", "invalid type %\"s", *ap ); + goto bust; + } + sdr.uid = -1; + if (!*++ap) + goto miss; + if (**ap == '=') { + switch (setBootOption( *ap + 1, &sdr )) { + case BO_NOMAN: + fLog( d, fd, "notsup", "boot options unavailable" ); + goto bust; + case BO_NOENT: + fLog( d, fd, "noent", "no such boot option" ); + goto bust; + case BO_IO: + fLog( d, fd, "io", "io error" ); + goto bust; + } + if (!*++ap) + goto miss; + } + sdr.start = strtol( *ap, &bp, 10 ); + if (bp != *ap && !*bp) { + if (**ap == '+') + sdr.start += now; + if (!*++ap) + goto miss; + sdr.timeout = strtol( *ap, &bp, 10 ); + if (bp == *ap || *bp) { + fLog( d, fd, "bad", "invalid timeout %\"s", ar[3] ); + goto bust; + } + if (**ap == '+') + sdr.timeout += sdr.start ? sdr.start : now; + if (sdr.timeout < 0) + sdr.timeout = TO_INF; + else { + if (!*++ap) + goto miss; + if (!strcmp( *ap, "force" )) + sdr.force = SHUT_FORCE; + else if (d && !strcmp( *ap, "forcemy" )) + sdr.force = SHUT_FORCEMY; + else if (strcmp( *ap, "cancel" )) { + fLog( d, fd, "bad", "invalid timeout action %\"s", + *ap ); + goto bust; + } + } + } else { + sdr.timeout = 0; + if (d && !strcmp( *ap, "ask" )) + sdr.force = SHUT_ASK; + else if (!strcmp( *ap, "forcenow" )) + sdr.force = SHUT_FORCE; + else if (!strcmp( *ap, "schedule" )) + sdr.timeout = TO_INF; + else if (strcmp( *ap, "trynow" )) { + fLog( d, fd, "bad", "invalid mode %\"s", *ap ); + goto bust; + } + } + } + if (*++ap) + goto exce; + if (d) { + sdr.uid = d->userSess >= 0 ? d->userSess : 0; + if (d->allowShutdown == SHUT_NONE || + (d->allowShutdown == SHUT_ROOT && sdr.uid && + sdr.force != SHUT_ASK)) + { + fLog( d, fd, "perm", "shutdown forbidden" ); + goto bust; + } + if (!sdr.how && !sdr.start) { + if (d->hstent->sdRec.osname) + free( d->hstent->sdRec.osname ); + d->hstent->sdRec = sdr; + } else { + if (sdRec.how && sdRec.force == SHUT_FORCE && + ((d->allowNuke == SHUT_NONE && sdRec.uid != sdr.uid) || + (d->allowNuke == SHUT_ROOT && sdr.uid))) + { + fLog( d, fd, "perm", "overriding forced shutdown forbidden" ); + goto bust; + } + if (sdr.force == SHUT_FORCE && + (d->allowNuke == SHUT_NONE || + (d->allowNuke == SHUT_ROOT && sdr.uid))) + { + fLog( d, fd, "perm", "forced shutdown forbidden" ); + goto bust; + } + if (!sdr.start) { + if (d->hstent->sdRec.osname) + free( d->hstent->sdRec.osname ); + d->hstent->sdRec = sdr; + } else { + if (!sdr.how) + cancelShutdown(); + else { + if (sdRec.osname) + free( sdRec.osname ); + sdRec = sdr; + } + } + } + } else { + if (!fifoAllowShutdown) { + fLog( d, fd, "perm", "shutdown forbidden" ); + goto bust; + } + if (sdRec.how && sdRec.force == SHUT_FORCE && + sdRec.uid != -1 && !fifoAllowNuke) + { + fLog( d, fd, "perm", "overriding forced shutdown forbidden" ); + goto bust; + } + if (!sdr.how) + cancelShutdown(); + else { + if (sdr.force != SHUT_CANCEL) { + if (!fifoAllowNuke) { + fLog( d, fd, "perm", "forced shutdown forbidden" ); + goto bust; + } + } else { + if (!sdr.start && !sdr.timeout && AnyActiveDisplays()) { + fLog( d, fd, "busy", "user sessions running" ); + goto bust; + } + } + sdr.uid = -1; + if (sdRec.osname) + free( sdRec.osname ); + sdRec = sdr; + } + } + } else if (fd >= 0 && !strcmp( ar[0], "listbootoptions" )) { + char **opts; + int def, cur, i, j; + + if (ar[1]) + goto exce; + switch (getBootOptions( &opts, &def, &cur )) { + case BO_NOMAN: + fLog( d, fd, "notsup", "boot options unavailable" ); + goto bust; + case BO_IO: + fLog( d, fd, "io", "io error" ); + goto bust; + } + Reply( "ok\t" ); + for (i = 0; opts[i]; i++) { + bp = cbuf; + if (i) + *bp++ = ' '; + for (j = 0; opts[i][j]; j++) + if (opts[i][j] == ' ') { + *bp++ = '\\'; + *bp++ = 's'; + } else + *bp++ = opts[i][j]; + Writer( fd, cbuf, bp - cbuf ); + } + freeStrArr( opts ); + Writer( fd, cbuf, sprintf( cbuf, "\t%d\t%d\n", def, cur ) ); + goto bust; + } else if (d) { + if (!strcmp( ar[0], "lock" )) { + if (ar[1]) + goto exce; + d->hstent->lock = 1; + } else if (!strcmp( ar[0], "unlock" )) { + if (ar[1]) + goto exce; + d->hstent->lock = 0; + } else if (!strcmp( ar[0], "suicide" )) { + if (ar[1]) + goto exce; + if (d->status == running && d->pid != -1) { + TerminateProcess( d->pid, SIGTERM ); + d->status = raiser; + } + } else { + fLog( d, fd, "nosys", "unknown command" ); + goto bust; + } + } else { + if (!strcmp( ar[0], "login" )) { + int nuke; + if (arrLen( ar ) < 5) { + miss: + fLog( d, fd, "bad", "missing argument(s)" ); + goto bust; + } + if (!(di = FindDisplayByName( ar[1] ))) { + fLog( d, fd, "noent", "display %s not found", ar[1] ); + goto bust; + } + if (ar[5]) { + if (!(args = unQuote( ar[5] ))) { + fLog( d, fd, "nomem", "out of memory" ); + goto bust; + } + if (ar[6]) { + free( args ); + exce: + fLog( d, fd, "bad", "excess argument(s)" ); + goto bust; + } + setNLogin( di, ar[3], ar[4], args, 2 ); + free( args ); + } else + setNLogin( di, ar[3], ar[4], 0, 2 ); + nuke = !strcmp( ar[2], "now" ); + switch (di->status) { + case running: + if (di->pid != -1 && (di->userSess < 0 || nuke)) { + TerminateProcess( di->pid, SIGTERM ); + di->status = raiser; + } + break; + case remoteLogin: + if (di->serverPid != -1 && nuke) + TerminateProcess( di->serverPid, di->termSignal ); + break; + case reserve: + di->status = notRunning; + break; + case textMode: +#ifndef HAVE_VTS + SwitchToX( di ); +#endif + break; + default: + break; + } + } else { + fLog( d, fd, "nosys", "unknown command" ); + goto bust; + } + } + if (fd >= 0) + Reply( "ok\n" ); + } + bust: + freeStrArr( ar ); +} + +static int +handleChan( struct display *d, struct bsock *cs, int fd, FD_TYPE *reads ) +{ + char *bufp, *nbuf, *obuf, *eol; + int len, bl, llen; + char buf[1024]; + + bl = cs->buflen; + obuf = cs->buffer; + if (bl <= 0 && FD_ISSET( cs->fd, reads )) { + FD_CLR( cs->fd, reads ); + bl = -bl; + memcpy( buf, obuf, bl ); + if ((len = Reader( cs->fd, buf + bl, sizeof(buf) - bl )) <= 0) + return -1; + bl += len; + bufp = buf; + } else { + len = 0; + bufp = obuf; + } + if (bl > 0) { + if ((eol = memchr( bufp, '\n', bl ))) { + llen = eol - bufp + 1; + bl -= llen; + if (bl) { + if (!(nbuf = Malloc( bl ))) + return -1; + memcpy( nbuf, bufp + llen, bl ); + } else + nbuf = 0; + cs->buffer = nbuf; + cs->buflen = bl; + processCtrl( bufp, llen - 1, fd, d ); + if (obuf) + free( obuf ); + return 1; + } else if (!len) { + if (fd >= 0) + cs->buflen = -bl; + else + fLog( d, -1, "bad", "unterminated command" ); + } + } + return 0; +} + +int +handleCtrl( FD_TYPE *reads, struct display *d ) +{ + CtrlRec *cr = d ? &d->ctrl : &ctrl; + struct cmdsock *cs, **csp; + + if (cr->fifo.fd >= 0) { + switch (handleChan( d, &cr->fifo, -1, reads )) { + case -1: + if (cr->fifo.buffer) + free( cr->fifo.buffer ); + cr->fifo.buflen = 0; + break; + case 1: + return 1; + default: + break; + } + } + if (cr->fd >= 0 && FD_ISSET( cr->fd, reads )) + acceptSock( cr ); + else { + for (csp = &cr->css; (cs = *csp); ) { + switch (handleChan( d, &cs->sock, cs->sock.fd, reads )) { + case -1: + *csp = cs->next; + nukeSock( cs ); + continue; + case 1: + return 1; + default: + break; + } + csp = &cs->next; + } + } + return 0; +} diff --git a/tdm/backend/daemon.c b/tdm/backend/daemon.c new file mode 100644 index 000000000..79d9e47ff --- /dev/null +++ b/tdm/backend/daemon.c @@ -0,0 +1,78 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2000,2001,2003 Oswald Buddenhagen <[email protected]> + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + */ + +#include "dm.h" +#include "dm_error.h" + +void +BecomeDaemon( void ) +{ + int pfd[2]; + + /* + * fork so that the process goes into the background automatically. Also + * has a nice side effect of having the child process get inherited by + * init (pid 1). + * Create a pipe and block on it, so the parent knows when the child is + * done with detaching. This eliminates the possibility that the child + * might get killed when the init script that's running xdm exits. + */ + + if (pipe( pfd )) + pfd[0] = pfd[1] = -1; /* so what ...? */ + switch (fork ()) { + case 0: + /* child */ + break; + case -1: + /* error */ + LogError( "Daemon fork failed: %m\n" ); + break; + + default: + /* parent */ + close( pfd[1] ); + read( pfd[0], &pfd[1] /* dummy */, 1 ); + exit( 0 ); + } + + /* don't use daemon() - it doesn't buy us anything but an additional fork */ + + setsid(); + + close( pfd[0] ); + close( pfd[1] ); /* tell parent that we're done with detaching */ + + chdir( "/" ); +} diff --git a/tdm/backend/dm.c b/tdm/backend/dm.c new file mode 100644 index 000000000..d372dd472 --- /dev/null +++ b/tdm/backend/dm.c @@ -0,0 +1,1669 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2000-2005 Oswald Buddenhagen <[email protected]> + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + * + * display manager + */ + +#include "dm.h" +#include "dm_auth.h" +#include "dm_error.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdarg.h> +#include <signal.h> +#include <sys/stat.h> + +#ifdef HAVE_VTS +# include <sys/ioctl.h> +# include <sys/vt.h> +#endif + +static void SigHandler( int n ); +static int ScanConfigs( int force ); +static void StartDisplays( void ); +#define XS_KEEP 0 +#define XS_RESTART 1 +#define XS_RETRY 2 +static void ExitDisplay( struct display *d, int endState, int serverCmd, int goodExit ); +static void rStopDisplay( struct display *d, int endState ); +static void MainLoop( void ); + +static int signalFds[2]; + +#if !defined(HAVE_SETPROCTITLE) && !defined(NOXDMTITLE) +static char *Title; +static int TitleLen; +#endif + +static int StorePid( void ); + +static int Stopping; +SdRec sdRec = { 0, 0, 0, TO_INF, TO_INF, 0, 0, 0 }; + +time_t now; + +char *prog, *progpath; + +int +main( int argc, char **argv ) +{ + int oldpid, oldumask, fd, noDaemonMode; + char *pt, *errorLogFile, **opts; + + /* make sure at least world write access is disabled */ + if (((oldumask = umask( 022 )) & 002) == 002) + (void)umask( oldumask ); + + /* give /dev/null as stdin */ + if ((fd = open( "/dev/null", O_RDONLY )) > 0) { + dup2( fd, 0 ); + close( fd ); + } + if (fcntl( 1, F_GETFD ) < 0) + dup2( 0, 1 ); + if (fcntl( 2, F_GETFD ) < 0) + dup2( 0, 2 ); + + if (argv[0][0] == '/') { + if (!StrDup( &progpath, argv[0] )) + Panic( "Out of memory" ); + } else +#ifdef __linux__ + { + /* note that this will resolve symlinks ... */ + int len; + char fullpath[PATH_MAX]; + if ((len = readlink( "/proc/self/exe", fullpath, sizeof(fullpath) )) < 0) + Panic( "Invoke with full path specification or mount /proc" ); + if (!StrNDup( &progpath, fullpath, len )) + Panic( "Out of memory" ); + } +#else +# if 0 + Panic( "Must be invoked with full path specification" ); +# else + { + char directory[PATH_MAX+1]; + if (!getcwd( directory, sizeof(directory) )) + Panic( "Can't find myself (getcwd failed)" ); + if (strchr( argv[0], '/' )) + StrApp( &progpath, directory, "/", argv[0], (char *)0 ); + else { + int len; + char *path, *name, *thenam, nambuf[PATH_MAX+1]; + char *pathe; + + if (!(path = getenv( "PATH" ))) + Panic( "Can't find myself (no PATH)" ); + len = strlen( argv[0] ); + name = nambuf + PATH_MAX - len; + memcpy( name, argv[0], len + 1 ); + *--name = '/'; + do { + if (!(pathe = strchr( path, ':' ))) + pathe = path + strlen( path ); + len = pathe - path; + if (!len || (len == 1 && *path == '.')) { + len = strlen( directory ); + path = directory; + } + thenam = name - len; + if (thenam >= nambuf) { + memcpy( thenam, path, len ); + if (!access( thenam, X_OK )) + goto found; + } + path = pathe; + } while (*path++ != '\0'); + Panic( "Can't find myself (not in PATH)" ); + found: + if (!StrDup( &progpath, thenam )) + Panic( "Out of memory" ); + } + } +# endif +#endif + prog = strrchr( progpath, '/' ) + 1; + +#if !defined(HAVE_SETPROCTITLE) && !defined(NOXDMTITLE) + Title = argv[0]; + TitleLen = (argv[argc - 1] + strlen( argv[argc - 1] )) - Title; +#endif + + /* + * Parse command line options + */ + noDaemonMode = getppid(); + errorLogFile = 0; + if (!(opts = Malloc( 2 * sizeof(char *) ))) + return 1; + opts[0] = (char *)""; + opts[1] = 0; + while (*++argv) { + if (**argv != '-') + break; + pt = *argv + 1; + if (*pt == '-') + pt++; + if (!strcmp( pt, "help" ) || !strcmp( pt, "h" )) { + printf( "Usage: %s [options] [tty]\n" +" -daemon\t - Daemonize even when started by init\n" +" -nodaemon\t - Don't daemonize even when started from command line\n" +" -config <file> - Use alternative master configuration file\n" +" -xrm <res>\t - Override frontend-specific resource\n" +" -error <file>\t - Use alternative log file\n" +" -debug <num>\t - Debug option bitfield:\n" +"\t\t\t0x1 - core log\n" +"\t\t\t0x2 - config reader log\n" +"\t\t\t0x4 - greeter log\n" +"\t\t\t0x8 - IPC log\n" +"\t\t\t0x10 - session sub-daemon post-fork delay\n" +"\t\t\t0x20 - config reader post-start delay\n" +"\t\t\t0x40 - greeter post-start delay\n" +"\t\t\t0x80 - don't use syslog\n" +"\t\t\t0x100 - core Xauth log\n" +"\t\t\t0x400 - valgrind config reader and greeter\n" +"\t\t\t0x800 - strace config reader and greeter\n" + , prog ); + exit( 0 ); + } else if (!strcmp( pt, "daemon" )) + noDaemonMode = 0; + else if (!strcmp( pt, "nodaemon" )) + noDaemonMode = 1; + else if (argv[1] && !strcmp( pt, "config" )) + StrDup( opts, *++argv ); + else if (argv[1] && !strcmp( pt, "xrm" )) + opts = addStrArr( opts, *++argv, -1 ); + else if (argv[1] && !strcmp( pt, "debug" )) + sscanf( *++argv, "%i", &debugLevel ); + else if (argv[1] && (!strcmp( pt, "error" ) || !strcmp( pt, "logfile" ))) + errorLogFile = *++argv; + else { + fprintf( stderr, "\"%s\" is an unknown option or is missing a parameter\n", *argv ); + exit( 1 ); + } + } + + /* + * Only allow root to run in non-debug mode to avoid problems + */ + if (!debugLevel && getuid()) { + fprintf( stderr, "Only root wants to run %s\n", prog ); + exit( 1 ); + } + + InitErrorLog( errorLogFile ); + + if (noDaemonMode != 1) + BecomeDaemon(); + + /* + * Step 1 - load configuration parameters + */ + if (!InitResources( opts ) || ScanConfigs( FALSE ) < 0) + LogPanic( "Config reader failed. Aborting ...\n" ); + + /* SUPPRESS 560 */ + if ((oldpid = StorePid())) { + if (oldpid == -1) + LogError( "Can't create/lock pid file %s\n", pidFile ); + else + LogError( "Can't lock pid file %s, another xdm is running (pid %d)\n", + pidFile, oldpid ); + exit( 1 ); + } + +#ifdef NEED_ENTROPY + AddOtherEntropy(); +#endif + + /* + * We used to clean up old authorization files here. As authDir is + * supposed to be /var/run/xauth or /tmp, we needn't to care for it. + */ + +#ifdef XDMCP + init_session_id(); +#else + Debug( "not compiled for XDMCP\n" ); +#endif + if (pipe( signalFds )) + LogPanic( "Unable to create signal notification pipe.\n" ); + RegisterInput( signalFds[0] ); + RegisterCloseOnFork( signalFds[0] ); + RegisterCloseOnFork( signalFds[1] ); + (void)Signal( SIGTERM, SigHandler ); + (void)Signal( SIGINT, SigHandler ); + (void)Signal( SIGHUP, SigHandler ); + (void)Signal( SIGCHLD, SigHandler ); + (void)Signal( SIGUSR1, SigHandler ); + + /* + * Step 2 - run a sub-daemon for each entry + */ +#ifdef XDMCP + UpdateListenSockets(); +#endif + openCtrl( 0 ); + MainLoop(); + closeCtrl( 0 ); + if (sdRec.how) { + commitBootOption(); + if (Fork() <= 0) { + char *cmd = sdRec.how == SHUT_HALT ? cmdHalt : cmdReboot; + execute( parseArgs( (char **)0, cmd ), (char **)0 ); + LogError( "Failed to execute shutdown command %\"s\n", cmd ); + exit( 1 ); + } else { + sigset_t mask; + sigemptyset( &mask ); + sigaddset( &mask, SIGCHLD ); + sigaddset( &mask, SIGHUP ); + sigsuspend( &mask ); + } + } + Debug( "nothing left to do, exiting\n" ); + return 0; +} + + +#ifdef HAVE_VTS +int +TTYtoVT( const char *tty ) +{ + return memcmp( tty, "tty", 3 ) ? 0 : atoi( tty + 3 ); +} + +int +activateVT( int vt ) +{ + int ret = 0; + int con = open( "/dev/console", O_RDONLY ); + if (con >= 0) { + if (!ioctl( con, VT_ACTIVATE, vt )) + ret = 1; + close( con ); + } + return ret; +} + + +static void +WakeDisplay( struct display *d ) +{ + if (d->status == textMode) + d->status = (d->displayType & d_lifetime) == dReserve ? reserve : notRunning; +} +#endif + +enum utState { UtDead, UtWait, UtActive }; + +struct utmps { + struct utmps *next; +#ifndef HAVE_VTS + struct display *d; +#endif + time_t time; + enum utState state; + int hadSess; +}; + +#define TIME_LOG 40 +#define TIME_RELOG 10 + +static struct utmps *utmpList; +static time_t utmpTimeout = TO_INF; + +static void +bombUtmp( void ) +{ + struct utmps *utp; + + while ((utp = utmpList)) { +#ifdef HAVE_VTS + ForEachDisplay( WakeDisplay ); +#else + utp->d->status = notRunning; +#endif + utmpList = utp->next; + free( utp ); + } +} + +static void +CheckUtmp( void ) +{ + static time_t modtim; + time_t nck; + time_t ends; + struct utmps *utp, **utpp; + struct stat st; +#ifdef BSD_UTMP + int fd; + struct utmp ut[1]; +#else + STRUCTUTMP *ut; +#endif + + if (!utmpList) + return; + if (stat( UTMP_FILE, &st )) { + LogError( UTMP_FILE " not found - cannot use console mode\n" ); + bombUtmp(); + return; + } + if (modtim != st.st_mtime) { + Debug( "rescanning " UTMP_FILE "\n" ); + for (utp = utmpList; utp; utp = utp->next) + utp->state = UtDead; +#ifdef BSD_UTMP + if ((fd = open( UTMP_FILE, O_RDONLY )) < 0) { + LogError( "Cannot open " UTMP_FILE " - cannot use console mode\n" ); + bombUtmp(); + return; + } + while (Reader( fd, ut, sizeof(ut[0]) ) == sizeof(ut[0])) +#else + SETUTENT(); + while ((ut = GETUTENT())) +#endif + { + for (utp = utmpList; utp; utp = utp->next) { +#ifdef HAVE_VTS + char **line; + for (line = consoleTTYs; *line; line++) + if (!strncmp( *line, ut->ut_line, sizeof(ut->ut_line) )) + goto hitlin; + continue; + hitlin: +#else + if (strncmp( utp->d->console, ut->ut_line, sizeof(ut->ut_line) )) + continue; +#endif +#ifdef BSD_UTMP + if (!*ut->ut_user) { +#else + if (ut->ut_type != USER_PROCESS) { +#endif +#ifdef HAVE_VTS + if (utp->state == UtActive) + break; +#endif + utp->state = UtWait; + } else { + utp->hadSess = 1; + utp->state = UtActive; + } + if (utp->time < ut->ut_time) /* theoretically superfluous */ + utp->time = ut->ut_time; + break; + } + } +#ifdef BSD_UTMP + close( fd ); +#else + ENDUTENT(); +#endif + modtim = st.st_mtime; + } + for (utpp = &utmpList; (utp = *utpp); ) { + if (utp->state != UtActive) { + if (utp->state == UtDead) /* shouldn't happen ... */ + utp->time = 0; + ends = utp->time + (utp->hadSess ? TIME_RELOG : TIME_LOG); + if (ends <= now) { +#ifdef HAVE_VTS + ForEachDisplay( WakeDisplay ); + Debug( "console login timed out\n" ); +#else + utp->d->status = notRunning; + Debug( "console login for %s at %s timed out\n", + utp->d->name, utp->d->console ); +#endif + *utpp = utp->next; + free( utp ); + continue; + } else + nck = ends; + } else + nck = TIME_RELOG + now; + if (nck < utmpTimeout) + utmpTimeout = nck; + utpp = &(*utpp)->next; + } +} + +static void +#ifdef HAVE_VTS +SwitchToTty( void ) +#else +SwitchToTty( struct display *d ) +#endif +{ + struct utmps *utp; +#ifdef HAVE_VTS + int vt; +#endif + + if (!(utp = Malloc( sizeof(*utp) ))) { +#ifdef HAVE_VTS + ForEachDisplay( WakeDisplay ); +#else + d->status = notRunning; +#endif + return; + } +#ifndef HAVE_VTS + d->status = textMode; + utp->d = d; +#endif + utp->time = now; + utp->hadSess = 0; + utp->next = utmpList; + utmpList = utp; + CheckUtmp(); + +#ifdef HAVE_VTS + if ((vt = TTYtoVT( *consoleTTYs ))) + activateVT( vt ); +#endif + + /* XXX output something useful here */ +} + +#ifdef HAVE_VTS +static void +StopToTTY( struct display *d ) +{ + if ((d->displayType & d_location) == dLocal) + switch (d->status) { + default: + rStopDisplay( d, DS_TEXTMODE | 0x100 ); + case reserve: + case textMode: + break; + } +} + +static void +CheckTTYMode( void ) +{ + struct display *d; + + for (d = displays; d; d = d->next) + if (d->status == zombie) + return; + + SwitchToTty(); +} + +#else + +void +SwitchToX( struct display *d ) +{ + struct utmps *utp, **utpp; + + for (utpp = &utmpList; (utp = *utpp); utpp = &(*utpp)->next) + if (utp->d == d) { + *utpp = utp->next; + free( utp ); + d->status = notRunning; + return; + } +} +#endif + +#ifdef XDMCP +static void +StartRemoteLogin( struct display *d ) +{ + char **argv; + int pid; + + Debug( "StartRemoteLogin for %s\n", d->name ); + /* HACK: omitting LoadDisplayResources( d ) here! */ + switch (pid = Fork()) { + case 0: + argv = PrepServerArgv( d, d->serverArgsRemote ); + if (!(argv = addStrArr( argv, "-once", 5 )) || + !(argv = addStrArr( argv, "-query", 6 )) || + !(argv = addStrArr( argv, d->remoteHost, -1 ))) + exit( 1 ); + Debug( "exec %\"[s\n", argv ); + (void)execv( argv[0], argv ); + LogError( "X server %\"s cannot be executed\n", argv[0] ); + + /* Let's try again with some standard paths */ + argv[0] = (char *)realloc(argv[0], strlen("/usr/X11R6/bin/X") + 1); + if (argv[0] != NULL) { + argv[0] = "/usr/X11R6/bin/X"; + Debug( "exec %\"[s\n", argv ); + (void)execv( argv[0], argv ); + LogError( "X server %\"s cannot be executed\n", argv[0] ); + + argv[0] = "/usr/bin/X"; /* Shorter than the previous file name */ + Debug( "exec %\"[s\n", argv ); + (void)execv( argv[0], argv ); + LogError( "X server %\"s cannot be executed\n", argv[0] ); + } + + exit( 1 ); + case -1: + LogError( "Forking X server for remote login failed: %m" ); + d->status = notRunning; + return; + default: + break; + } + Debug( "X server forked, pid %d\n", pid ); + d->serverPid = pid; + + d->status = remoteLogin; +} +#endif + + +static void +StopInactiveDisplay( struct display *d ) +{ + if (d->status != remoteLogin && d->userSess < 0) + StopDisplay( d ); +} + +static void +stoppen( int force ) +{ +#ifdef XDMCP + request_port = 0; + UpdateListenSockets(); +#endif + if (force) + ForEachDisplay( StopDisplay ); + else + ForEachDisplay( StopInactiveDisplay ); + Stopping = 1; +} + + +void +setNLogin( struct display *d, + const char *nuser, const char *npass, char *nargs, int rl ) +{ + struct disphist *he = d->hstent; + he->rLogin = + (ReStr( &he->nuser, nuser ) && + ReStr( &he->npass, npass ) && + ReStr( &he->nargs, nargs )) ? rl : 0; + Debug( "set next login for %s, level %d\n", nuser, rl ); +} + +static void +processDPipe( struct display *d ) +{ + char *user, *pass, *args; + int cmd; + GTalk dpytalk; +#ifdef XDMCP + int ct, len; + ARRAY8 ca, ha; +#endif + + dpytalk.pipe = &d->pipe; + if (Setjmp( dpytalk.errjmp )) { + StopDisplay( d ); + return; + } + GSet( &dpytalk ); + if (!GRecvCmd( &cmd )) { + /* process already exited */ + UnregisterInput( d->pipe.rfd ); + return; + } + switch (cmd) { + case D_User: + d->userSess = GRecvInt(); + d->userName = GRecvStr(); + d->sessName = GRecvStr(); + break; + case D_ReLogin: + user = GRecvStr(); + pass = GRecvStr(); + args = GRecvStr(); + setNLogin( d, user, pass, args, 1 ); + free( args ); + free( pass ); + free( user ); + break; +#ifdef XDMCP + case D_ChooseHost: + ca.data = (unsigned char *)GRecvArr( &len ); + ca.length = (CARD16)len; + ct = GRecvInt(); + ha.data = (unsigned char *)GRecvArr( &len ); + ha.length = (CARD16)len; + RegisterIndirectChoice( &ca, ct, &ha ); + XdmcpDisposeARRAY8( &ha ); + XdmcpDisposeARRAY8( &ca ); + break; + case D_RemoteHost: + if (d->remoteHost) + free( d->remoteHost ); + d->remoteHost = GRecvStr(); + break; +#endif + case D_XConnOk: + startingServer = 0; + break; + default: + LogError( "Internal error: unknown D_* command %d\n", cmd ); + StopDisplay( d ); + break; + } +} + +static void +emitXSessG( struct display *di, struct display *d, void *ctx ATTR_UNUSED ) +{ + GSendStr( di->name ); + GSendStr( "" ); +#ifdef HAVE_VTS + GSendInt( di->serverVT ); +#endif +#ifdef XDMCP + if (di->status == remoteLogin) { + GSendStr( "" ); + GSendStr( di->remoteHost ); + } else +#endif + { + GSendStr( di->userName ); + GSendStr( di->sessName ); + } + GSendInt( di == d ? isSelf : 0 ); +} + +static void +emitTTYSessG( STRUCTUTMP *ut, struct display *d ATTR_UNUSED, void *ctx ATTR_UNUSED ) +{ + GSendStrN( ut->ut_line, sizeof(ut->ut_line) ); + GSendStrN( ut->ut_host, sizeof(ut->ut_host) ); +#ifdef HAVE_VTS + GSendInt( TTYtoVT( ut->ut_line ) ); +#endif +#ifdef BSD_UTMP + GSendStrN( *ut->ut_user ? ut->ut_user : 0, sizeof(ut->ut_user) ); +#else + GSendStrN( ut->ut_type == USER_PROCESS ? ut->ut_user : 0, sizeof(ut->ut_user) ); +#endif + GSendStr( 0 ); /* session type unknown */ + GSendInt( isTTY ); +} + +static void +processGPipe( struct display *d ) +{ + char **opts, *option; + int cmd, ret, dflt, curr; + GTalk dpytalk; + + dpytalk.pipe = &d->gpipe; + if (Setjmp( dpytalk.errjmp )) { + StopDisplay( d ); + return; + } + GSet( &dpytalk ); + if (!GRecvCmd( &cmd )) { + /* process already exited */ + UnregisterInput( d->gpipe.rfd ); + return; + } + switch (cmd) { + case G_ListBootOpts: + ret = getBootOptions( &opts, &dflt, &curr ); + GSendInt( ret ); + if (ret == BO_OK) { + GSendArgv( opts ); + freeStrArr( opts ); + GSendInt( dflt ); + GSendInt( curr ); + } + break; + case G_Shutdown: + sdRec.how = GRecvInt(); + sdRec.start = GRecvInt(); + sdRec.timeout = GRecvInt(); + sdRec.force = GRecvInt(); + sdRec.uid = GRecvInt(); + option = GRecvStr(); + setBootOption( option, &sdRec ); + if (option) + free( option ); + break; + case G_QueryShutdown: + GSendInt( sdRec.how ); + GSendInt( sdRec.start ); + GSendInt( sdRec.timeout ); + GSendInt( sdRec.force ); + GSendInt( sdRec.uid ); + GSendStr( sdRec.osname ); + break; + case G_List: + ListSessions( GRecvInt(), d, 0, emitXSessG, emitTTYSessG ); + GSendInt( 0 ); + break; +#ifdef HAVE_VTS + case G_Activate: + activateVT( GRecvInt() ); + break; +#endif + case G_Console: +#ifdef HAVE_VTS + if (*consoleTTYs) { /* sanity check against greeter */ + ForEachDisplay( StopToTTY ); + CheckTTYMode(); + } +#else + if (*d->console) /* sanity check against greeter */ + rStopDisplay( d, DS_TEXTMODE ); +#endif + break; + default: + LogError( "Internal error: unknown G_* command %d\n", cmd ); + StopDisplay( d ); + break; + } +} + + +static int +ScanConfigs( int force ) +{ + int ret; + + if ((ret = LoadDMResources( force )) <= 0) + return ret; + ScanServers(); +#ifdef XDMCP + ScanAccessDatabase( force ); +#endif + return 1; +} + +static void +MarkDisplay( struct display *d ) +{ + d->stillThere = 0; +} + +static void +RescanConfigs( int force ) +{ + if (ScanConfigs( force ) > 0) { +#ifdef XDMCP + UpdateListenSockets(); +#endif + updateCtrl(); + } +} + +void +cancelShutdown( void ) +{ + sdRec.how = 0; + if (sdRec.osname) { + free( sdRec.osname ); + sdRec.osname = 0; + } + Stopping = 0; + RescanConfigs( TRUE ); +} + + +static void +ReapChildren( void ) +{ + int pid; + struct display *d; + waitType status; + + while ((pid = waitpid( -1, &status, WNOHANG )) > 0) + { + Debug( "manager wait returns pid %d sig %d core %d code %d\n", + pid, waitSig( status ), waitCore( status ), waitCode( status ) ); + /* SUPPRESS 560 */ + if ((d = FindDisplayByPid( pid ))) { + d->pid = -1; + UnregisterInput( d->pipe.rfd ); + GClosen (&d->pipe); + UnregisterInput( d->gpipe.rfd ); + GClosen (&d->gpipe); + closeCtrl( d ); + switch (waitVal( status )) { +#ifdef XDMCP + case EX_REMOTE: + Debug( "display exited with EX_REMOTE\n" ); + ExitDisplay( d, DS_REMOTE, 0, 0 ); + break; +#endif + case EX_NORMAL: + /* (any type of) session ended */ + Debug( "display exited with EX_NORMAL\n" ); + if ((d->displayType & d_lifetime) == dReserve) + ExitDisplay( d, DS_RESERVE, 0, 0 ); + else + ExitDisplay( d, DS_RESTART, XS_KEEP, TRUE ); + break; +#if 0 + case EX_REMANAGE_DPY: + /* user session ended */ + Debug( "display exited with EX_REMANAGE_DPY\n" ); + ExitDisplay( d, DS_RESTART, XS_KEEP, TRUE ); + break; +#endif + case EX_OPENFAILED_DPY: + /* WaitForServer() failed */ + LogError( "Display %s cannot be opened\n", d->name ); +#ifdef XDMCP + /* + * no display connection was ever made, tell the + * terminal that the open attempt failed + */ + if ((d->displayType & d_origin) == dFromXDMCP) + SendFailed( d, "cannot open display" ); +#endif + ExitDisplay( d, DS_RESTART, XS_RETRY, FALSE ); + break; + case waitCompose( SIGTERM,0,0 ): + /* killed before/during WaitForServer() + - local Xserver died + - display stopped (is zombie) + - "login now" and "suicide" pipe commands (is raiser) + */ + Debug( "display exited on SIGTERM\n" ); + ExitDisplay( d, DS_RESTART, XS_RETRY, FALSE ); + break; + case EX_AL_RESERVER_DPY: + /* - killed after WaitForServer() + - Xserver dead after remote session exit + */ + Debug( "display exited with EX_AL_RESERVER_DPY\n" ); + ExitDisplay( d, DS_RESTART, XS_RESTART, FALSE ); + break; + case EX_RESERVER_DPY: + /* induced by greeter: + - could not secure display + - requested by user + */ + Debug( "display exited with EX_RESERVER_DPY\n" ); + ExitDisplay( d, DS_RESTART, XS_RESTART, TRUE ); + break; + case EX_UNMANAGE_DPY: + /* some fatal error */ + Debug( "display exited with EX_UNMANAGE_DPY\n" ); + ExitDisplay( d, DS_REMOVE, 0, 0 ); + break; + default: + /* prolly crash */ + LogError( "Unknown session exit code %d (sig %d) from manager process\n", + waitCode( status ), waitSig( status ) ); + ExitDisplay( d, DS_REMOVE, 0, 0 ); + break; + } + } else if ((d = FindDisplayByServerPid( pid ))) { + d->serverPid = -1; + switch (d->status) { + case zombie: + Debug( "zombie X server for display %s reaped\n", d->name ); +#ifdef HAVE_VTS + if (d->serverVT && d->zstatus != DS_REMOTE) { + if (d->follower) { + d->follower->serverVT = d->serverVT; + d->follower = 0; + } else { + int con = open( "/dev/console", O_RDONLY ); + if (con >= 0) { + struct vt_stat vtstat; + ioctl( con, VT_GETSTATE, &vtstat ); + if (vtstat.v_active == d->serverVT) { + int vt = 1; + struct display *di; + for (di = displays; di; di = di->next) + if (di != d && di->serverVT) + vt = di->serverVT; + for (di = displays; di; di = di->next) + if (di != d && di->serverVT && + (di->userSess >= 0 || + di->status == remoteLogin)) + vt = di->serverVT; + ioctl( con, VT_ACTIVATE, vt ); + } + ioctl( con, VT_DISALLOCATE, d->serverVT ); + close( con ); + } + } + d->serverVT = 0; + } +#endif + rStopDisplay( d, d->zstatus ); + break; + case phoenix: + Debug( "phoenix X server arises, restarting display %s\n", + d->name ); + d->status = notRunning; + break; + case remoteLogin: + Debug( "remote login X server for display %s exited\n", + d->name ); + d->status = ((d->displayType & d_lifetime) == dReserve) ? + reserve : notRunning; + break; + case raiser: + LogError( "X server for display %s terminated unexpectedly\n", + d->name ); + /* don't kill again */ + break; + case running: + if (startingServer == d && d->serverStatus != ignore) { + if (d->serverStatus == starting && waitCode( status ) != 47) + LogError( "X server died during startup\n" ); + StartServerFailed(); + break; + } + LogError( "X server for display %s terminated unexpectedly\n", + d->name ); + if (d->pid != -1) { + Debug( "terminating session pid %d\n", d->pid ); + TerminateProcess( d->pid, SIGTERM ); + } + break; + case notRunning: + case textMode: + case reserve: + /* this cannot happen */ + Debug( "X server exited for passive (%d) session on display %s\n", + (int)d->status, d->name ); + break; + } + } else + Debug( "unknown child termination\n" ); + } +#ifdef NEED_ENTROPY + AddOtherEntropy(); +#endif +} + +static int +wouldShutdown( void ) +{ + struct display *d; + + if (sdRec.force != SHUT_CANCEL) { + if (sdRec.force == SHUT_FORCEMY) + for (d = displays; d; d = d->next) + if (d->status == remoteLogin || + (d->userSess >= 0 && d->userSess != sdRec.uid)) + return 0; + return 1; + } + return !AnyActiveDisplays(); +} + +FD_TYPE WellKnownSocketsMask; +int WellKnownSocketsMax; +int WellKnownSocketsCount; + +void +RegisterInput( int fd ) +{ + /* can be omited, as it is always called right after opening a socket + if (!FD_ISSET (fd, &WellKnownSocketsMask)) + */ + { + FD_SET( fd, &WellKnownSocketsMask ); + if (fd > WellKnownSocketsMax) + WellKnownSocketsMax = fd; + WellKnownSocketsCount++; + } +} + +void +UnregisterInput( int fd ) +{ + /* the check _is_ necessary, as some handles are unregistered before + the regular close sequence. + */ + if (FD_ISSET( fd, &WellKnownSocketsMask )) { + FD_CLR( fd, &WellKnownSocketsMask ); + WellKnownSocketsCount--; + } +} + +static void +SigHandler( int n ) +{ + int olderrno = errno; + char buf = (char)n; + /* Debug( "caught signal %d\n", n ); this hangs in syslog() */ + write( signalFds[1], &buf, 1 ); +#ifdef __EMX__ + (void)Signal( n, SigHandler ); +#endif + errno = olderrno; +} + +static void +MainLoop( void ) +{ + struct display *d; + struct timeval *tvp, tv; + time_t to; + int nready; + char buf; + FD_TYPE reads; + + Debug( "MainLoop\n" ); + time( &now ); + while ( +#ifdef XDMCP + AnyListenSockets() || +#endif + (Stopping ? AnyRunningDisplays() : AnyDisplaysLeft())) + { + if (!Stopping) + StartDisplays(); + to = TO_INF; + if (sdRec.how) { + if (sdRec.start != TO_INF && now < sdRec.start) { + /*if (sdRec.start < to)*/ + to = sdRec.start; + } else { + sdRec.start = TO_INF; + if (now >= sdRec.timeout) { + sdRec.timeout = TO_INF; + if (wouldShutdown()) + stoppen( TRUE ); + else + cancelShutdown(); + } else { + stoppen( FALSE ); + /*if (sdRec.timeout < to)*/ + to = sdRec.timeout; + } + } + } + if (serverTimeout < to) + to = serverTimeout; + if (utmpTimeout < to) + to = utmpTimeout; + if (to == TO_INF) + tvp = 0; + else { + to -= now; + if (to < 0) + to = 0; + tv.tv_sec = to; + tv.tv_usec = 0; + tvp = &tv; + } + reads = WellKnownSocketsMask; + nready = select( WellKnownSocketsMax + 1, &reads, 0, 0, tvp ); + Debug( "select returns %d\n", nready ); + time( &now ); +#ifdef NEED_ENTROPY + AddTimerEntropy(); +#endif + if (now >= serverTimeout) { + serverTimeout = TO_INF; + StartServerTimeout(); + } + if (now >= utmpTimeout) { + utmpTimeout = TO_INF; + CheckUtmp(); + } + if (nready > 0) { + /* + * we restart after the first handled fd, as + * a) it makes things simpler + * b) the probability that multiple fds trigger at once is + * ridiculously small. we handle it in the next iteration. + */ + /* XXX a cleaner solution would be a callback mechanism */ + if (FD_ISSET( signalFds[0], &reads )) { + if (read( signalFds[0], &buf, 1 ) != 1) + LogPanic( "Signal notification pipe broken.\n" ); + switch (buf) { + case SIGTERM: + case SIGINT: + Debug( "shutting down entire manager\n" ); + stoppen( TRUE ); + break; + case SIGHUP: + LogInfo( "Rescanning all config files\n" ); + ForEachDisplay( MarkDisplay ); + RescanConfigs( TRUE ); + break; + case SIGCHLD: + ReapChildren(); + if (!Stopping && autoRescan) + RescanConfigs( FALSE ); + break; + case SIGUSR1: + if (startingServer && + startingServer->serverStatus == starting) + StartServerSuccess(); + break; + } + continue; + } +#ifdef XDMCP + if (ProcessListenSockets( &reads )) + continue; +#endif /* XDMCP */ + if (handleCtrl( &reads, 0 )) + continue; + /* Must be last (because of the breaks)! */ + again: + for (d = displays; d; d = d->next) { + if (handleCtrl( &reads, d )) + goto again; + if (d->pipe.rfd >= 0 && FD_ISSET( d->pipe.rfd, &reads )) { + processDPipe( d ); + break; + } + if (d->gpipe.rfd >= 0 && FD_ISSET( d->gpipe.rfd, &reads )) { + processGPipe( d ); + break; + } + } + } + } +} + +static void +CheckDisplayStatus( struct display *d ) +{ + if ((d->displayType & d_origin) == dFromFile && !d->stillThere) + StopDisplay( d ); + else if ((d->displayType & d_lifetime) == dReserve && + d->status == running && d->userSess < 0 && !d->idleTimeout) + rStopDisplay( d, DS_RESERVE ); + else if (d->status == notRunning) + if (LoadDisplayResources( d ) < 0) { + LogError( "Unable to read configuration for display %s; " + "stopping it.\n", d->name ); + StopDisplay( d ); + return; + } +} + +static void +KickDisplay( struct display *d ) +{ + if (d->status == notRunning) + StartDisplay( d ); + if (d->serverStatus == awaiting && !startingServer) + StartServer( d ); +} + +#ifdef HAVE_VTS +static int active_vts; + +static int +GetBusyVTs( void ) +{ + struct vt_stat vtstat; + int con; + + if (active_vts == -1) { + vtstat.v_state = 0; + if ((con = open( "/dev/console", O_RDONLY )) >= 0) { + ioctl( con, VT_GETSTATE, &vtstat ); + close( con ); + } + active_vts = vtstat.v_state; + } + return active_vts; +} + +static void +AllocateVT( struct display *d ) +{ + struct display *cd; + int i, tvt, volun; + + if ((d->displayType & d_location) == dLocal && + d->status == notRunning && !d->serverVT && d->reqSrvVT >= 0) + { + if (d->reqSrvVT && d->reqSrvVT < 16) + d->serverVT = d->reqSrvVT; + else { + for (i = tvt = 0;;) { + if (serverVTs[i]) { + tvt = atoi( serverVTs[i++] ); + volun = 0; + if (tvt < 0) { + tvt = -tvt; + volun = 1; + } + if (!tvt || tvt >= 16) + continue; + } else { + if (++tvt >= 16) + break; + volun = 1; + } + for (cd = displays; cd; cd = cd->next) { + if (cd->reqSrvVT == tvt && /* protect from lusers */ + (cd->status != zombie || cd->zstatus != DS_REMOVE)) + goto next; + if (cd->serverVT == tvt) { + if (cd->status != zombie || cd->zstatus == DS_REMOTE) + goto next; + if (!cd->follower) { + d->serverVT = -1; + cd->follower = d; + return; + } + } + } + if (!volun || !((1 << tvt) & GetBusyVTs())) { + d->serverVT = tvt; + return; + } + next: ; + } + } + } +} +#endif + +static void +StartDisplays( void ) +{ + ForEachDisplay( CheckDisplayStatus ); + CloseGetter(); +#ifdef HAVE_VTS + active_vts = -1; + ForEachDisplayRev( AllocateVT ); +#endif + ForEachDisplay( KickDisplay ); +} + +void +StartDisplay( struct display *d ) +{ + if (Stopping) { + Debug( "stopping display %s because shutdown is scheduled\n", d->name ); + StopDisplay( d ); + return; + } + +#ifdef HAVE_VTS + if (d->serverVT < 0) + return; +#endif + + d->status = running; + if ((d->displayType & d_location) == dLocal) { + Debug( "StartDisplay %s\n", d->name ); + /* don't bother pinging local displays; we'll + * certainly notice when they exit + */ + d->pingInterval = 0; + if (d->authorize) { + SetLocalAuthorization( d ); + /* + * reset the server after writing the authorization information + * to make it read the file (for compatibility with old + * servers which read auth file only on reset instead of + * at first connection) + */ + if (d->serverPid != -1 && d->resetForAuth && d->resetSignal) + kill( d->serverPid, d->resetSignal ); + } + if (d->serverPid == -1) { + d->serverStatus = awaiting; + return; + } + } else { + Debug( "StartDisplay %s, try %d\n", d->name, d->startTries + 1 ); + /* this will only happen when using XDMCP */ + if (d->authorizations) + SaveServerAuthorizations( d, d->authorizations, d->authNum ); + } + StartDisplayP2( d ); +} + +void +StartDisplayP2( struct display *d ) +{ + char *cname, *cgname; + int pid; + + openCtrl( d ); + Debug( "forking session\n" ); + ASPrintf( &cname, "sub-daemon for display %s", d->name ); + ASPrintf( &cgname, "greeter for display %s", d->name ); + pid = GFork( &d->pipe, "master daemon", cname, + &d->gpipe, cgname ); + switch (pid) { + case 0: + SetTitle( d->name ); + if (debugLevel & DEBUG_WSESS) + sleep( 100 ); + mstrtalk.pipe = &d->pipe; + (void)Signal( SIGPIPE, SIG_IGN ); + SetAuthorization( d ); + WaitForServer( d ); + if ((d->displayType & d_location) == dLocal) { + GSet( &mstrtalk ); + GSendInt( D_XConnOk ); + } + ManageSession( d ); + /* NOTREACHED */ + case -1: + closeCtrl( d ); + d->status = notRunning; + break; + default: + Debug( "forked session, pid %d\n", pid ); + + /* (void) fcntl (d->pipe.rfd, F_SETFL, O_NONBLOCK); */ + /* (void) fcntl (d->gpipe.rfd, F_SETFL, O_NONBLOCK); */ + RegisterInput( d->pipe.rfd ); + RegisterInput( d->gpipe.rfd ); + + d->pid = pid; + d->hstent->lock = d->hstent->rLogin = d->hstent->goodExit = + d->hstent->sdRec.how = 0; + d->lastStart = now; + break; + } +} + +/* + * transition from running to zombie, textmode, reserve or deleted + */ + +static void +rStopDisplay( struct display *d, int endState ) +{ + Debug( "stopping display %s to state %d\n", d->name, endState ); + AbortStartServer( d ); + d->idleTimeout = 0; + if (d->serverPid != -1 || d->pid != -1) { + if (d->pid != -1) + TerminateProcess( d->pid, SIGTERM ); + if (d->serverPid != -1) + TerminateProcess( d->serverPid, d->termSignal ); + d->status = zombie; + d->zstatus = endState & 0xff; + Debug( " zombiefied\n" ); + } else if (endState == DS_TEXTMODE) { +#ifdef HAVE_VTS + d->status = textMode; + CheckTTYMode(); + } else if (endState == (DS_TEXTMODE | 0x100)) { + d->status = textMode; +#else + SwitchToTty( d ); +#endif + } else if (endState == DS_RESERVE) + d->status = reserve; +#ifdef XDMCP + else if (endState == DS_REMOTE) + StartRemoteLogin( d ); +#endif + else { +#ifndef HAVE_VTS + SwitchToX( d ); +#endif + RemoveDisplay( d ); + } +} + +void +StopDisplay( struct display *d ) +{ + rStopDisplay( d, DS_REMOVE ); +} + +static void +ExitDisplay( + struct display *d, + int endState, + int serverCmd, + int goodExit ) +{ + struct disphist *he; + + if (d->status == raiser) { + serverCmd = XS_KEEP; + goodExit = TRUE; + } + + Debug( "ExitDisplay %s, " + "endState = %d, serverCmd = %d, GoodExit = %d\n", + d->name, endState, serverCmd, goodExit ); + + d->userSess = -1; + if (d->userName) + free( d->userName ); + d->userName = 0; + if (d->sessName) + free( d->sessName ); + d->sessName = 0; + he = d->hstent; + he->lastExit = now; + he->goodExit = goodExit; + if (he->sdRec.how) { + if (he->sdRec.force == SHUT_ASK && + (AnyActiveDisplays() || d->allowShutdown == SHUT_ROOT)) + { + endState = DS_RESTART; + } else { + if (!sdRec.how || sdRec.force != SHUT_FORCE || + !((d->allowNuke == SHUT_NONE && sdRec.uid != he->sdRec.uid) || + (d->allowNuke == SHUT_ROOT && he->sdRec.uid))) + { + if (sdRec.osname) + free( sdRec.osname ); + sdRec = he->sdRec; + if (now < sdRec.timeout || wouldShutdown()) + endState = DS_REMOVE; + } else if (he->sdRec.osname) + free( he->sdRec.osname ); + he->sdRec.how = 0; + he->sdRec.osname = 0; + } + } + if (d->status == zombie) + rStopDisplay( d, d->zstatus ); + else { + if (Stopping) { + StopDisplay( d ); + return; + } + if (endState != DS_RESTART || + (d->displayType & d_origin) != dFromFile) + { + rStopDisplay( d, endState ); + } else { + if (serverCmd == XS_RETRY) { + if ((d->displayType & d_location) == dLocal) { + if (he->lastExit - d->lastStart < 120) { + LogError( "Unable to fire up local display %s;" + " disabling.\n", d->name ); + StopDisplay( d ); + return; + } + } else { + if (++d->startTries > d->startAttempts) { + LogError( "Disabling foreign display %s" + " (too many attempts)\n", d->name ); + StopDisplay( d ); + return; + } + } + } else + d->startTries = 0; + if (d->serverPid != -1 && + (serverCmd != XS_KEEP || d->terminateServer)) + { + Debug( "killing X server for %s\n", d->name ); + TerminateProcess( d->serverPid, d->termSignal ); + d->status = phoenix; + } else + d->status = notRunning; + } + } +} + + +static int pidFd; +static FILE *pidFilePtr; + +static int +StorePid( void ) +{ + int oldpid; + + if (pidFile[0] != '\0') { + pidFd = open( pidFile, O_RDWR ); + if (pidFd == -1 && errno == ENOENT) + pidFd = open( pidFile, O_RDWR|O_CREAT, 0666 ); + if (pidFd == -1 || !(pidFilePtr = fdopen( pidFd, "r+" ))) { + LogError( "process-id file %s cannot be opened\n", + pidFile ); + return -1; + } + if (fscanf( pidFilePtr, "%d\n", &oldpid ) != 1) + oldpid = -1; + fseek( pidFilePtr, 0l, 0 ); + if (lockPidFile) { +#ifdef F_SETLK +# ifndef SEEK_SET +# define SEEK_SET 0 +# endif + struct flock lock_data; + lock_data.l_type = F_WRLCK; + lock_data.l_whence = SEEK_SET; + lock_data.l_start = lock_data.l_len = 0; + if (fcntl( pidFd, F_SETLK, &lock_data ) == -1) { + if (errno == EAGAIN) + return oldpid; + else + return -1; + } +#else +# ifdef LOCK_EX + if (flock( pidFd, LOCK_EX|LOCK_NB ) == -1) { + if (errno == EWOULDBLOCK) + return oldpid; + else + return -1; + } +# else + if (lockf( pidFd, F_TLOCK, 0 ) == -1) { + if (errno == EACCES) + return oldpid; + else + return -1; + } +# endif +#endif + } + fprintf( pidFilePtr, "%ld\n", (long)getpid() ); + (void)fflush( pidFilePtr ); + RegisterCloseOnFork( pidFd ); + } + return 0; +} + +#if 0 +void +UnlockPidFile( void ) +{ + if (lockPidFile) +# ifdef F_SETLK + { + struct flock lock_data; + lock_data.l_type = F_UNLCK; + lock_data.l_whence = SEEK_SET; + lock_data.l_start = lock_data.l_len = 0; + (void)fcntl( pidFd, F_SETLK, &lock_data ); + } +# else +# ifdef F_ULOCK + lockf( pidFd, F_ULOCK, 0 ); +# else + flock( pidFd, LOCK_UN ); +# endif +# endif + close( pidFd ); + fclose( pidFilePtr ); +} +#endif + +void +SetTitle( const char *name ) +{ +#if !defined(HAVE_SETPROCTITLE) && !defined(NOXDMTITLE) + char *p; + int left; +#endif + + ASPrintf( &prog, "%s: %s", prog, name ); + ReInitErrorLog(); +#ifdef HAVE_SETPROCTITLE + setproctitle( "%s", name ); +#elif !defined(NOXDMTITLE) + p = Title; + left = TitleLen; + + *p++ = '-'; + --left; + while (*name && left > 0) { + *p++ = *name++; + --left; + } + while (left > 0) { + *p++ = '\0'; + --left; + } +#endif +} diff --git a/tdm/backend/dm.h b/tdm/backend/dm.h new file mode 100644 index 000000000..c05d4c865 --- /dev/null +++ b/tdm/backend/dm.h @@ -0,0 +1,630 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2000-2005 Oswald Buddenhagen <[email protected]> + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + * + * global xdm core declarations + */ + +#ifndef _DM_H_ +#define _DM_H_ 1 + +#define WITH_CONSOLE_KIT + +#include "greet.h" +#include <config.ci> + +#include <X11/X.h> /* FamilyInternet6 */ +#include <X11/Xos.h> +#include <X11/Xfuncs.h> +#include <X11/Xmd.h> +#include <X11/Xauth.h> +#include <X11/Intrinsic.h> + +#include <sys/param.h> +#ifdef HAVE_LIMITS_H +# include <limits.h> +#endif + +#include <time.h> +#define Time_t time_t + +#include <stdlib.h> +#include <errno.h> + +#ifdef XDMCP +# if defined(__osf__) +/* someone somewhere defines QUERY under Tru64 which confuses Xdmcp.h */ +# undef QUERY +# endif +# include <X11/Xdmcp.h> +#endif + +#ifndef PATH_MAX +# ifdef MAXPATHLEN +# define PATH_MAX MAXPATHLEN +# else +# define PATH_MAX 1024 +# endif +#endif + +#include <sys/wait.h> +#define waitCode(w) (WIFEXITED(w) ? WEXITSTATUS(w) : 0) +#define waitSig(w) (WIFSIGNALED(w) ? WTERMSIG(w) : 0) +#ifdef WCOREDUMP +# define waitCore(w) (WCOREDUMP(w)) +#else +# define waitCore(w) 0 /* not in POSIX. so what? */ +#endif +typedef int waitType; + +#define waitCompose(sig,core,code) ((sig) * 256 + (core) * 128 + (code)) +#define waitVal(w) waitCompose(waitSig(w), waitCore(w), waitCode(w)) +#define WaitCode(w) ((w) & 0x7f) +#define WaitCore(w) (((w) >> 7) & 1) +#define WaitSig(w) (((w) >> 8) & 0xff) + +#include <sys/time.h> +#define FD_TYPE fd_set + +#include <setjmp.h> +#if defined(__EMX__) || (defined(__NetBSD__) && defined(__sparc__)) /* XXX netbsd? */ +# define Setjmp(e) setjmp(e) +# define Longjmp(e,v) longjmp(e,v) +# define Jmp_buf jmp_buf +#else +# define Setjmp(e) sigsetjmp(e,1) +# define Longjmp(e,v) siglongjmp(e,v) +# define Jmp_buf sigjmp_buf +#endif + +#include <utmp.h> +#ifdef HAVE_UTMPX +# include <utmpx.h> +# define STRUCTUTMP struct utmpx +# define UTMPNAME utmpxname +# define SETUTENT setutxent +# define GETUTENT getutxent +# define PUTUTLINE pututxline +# define ENDUTENT endutxent +# define LASTLOG lastlogx +# define ut_time ut_tv.tv_sec +# define ll_time ll_tv.tv_sec +#else +# define STRUCTUTMP struct utmp +# define UTMPNAME utmpname +# define SETUTENT setutent +# define GETUTENT getutent +# define PUTUTLINE pututline +# define ENDUTENT endutent +# define LASTLOG lastlog +#endif +#ifndef HAVE_STRUCT_UTMP_UT_USER +# define ut_user ut_name +#endif +#ifndef WTMP_FILE +# ifdef _PATH_WTMPX +# define WTMP_FILE _PATH_WTMPX +# elif defined(_PATH_WTMP) +# define WTMP_FILE _PATH_WTMP +# else +# define WTMP_FILE "/usr/adm/wtmp" +# endif +#endif +#ifndef UTMP_FILE +# ifdef _PATH_UTMPX +# define UTMP_FILE _PATH_UTMPX +# elif defined(_PATH_UTMP) +# define UTMP_FILE _PATH_UTMP +# else +# define UTMP_FILE "/etc/utmp" +# endif +#endif + +#ifdef HAVE_NETCONFIG_H +# define STREAMSCONN +#else +# define UNIXCONN +# define TCPCONN +# ifdef FamilyInternet6 +# define IPv6 +# endif +# ifdef HAVE_NETDNET_DN_H +# define DNETCONN +# endif +#endif + +#if !defined(HAVE_ARC4RANDOM) && !defined(DEV_RANDOM) +# define NEED_ENTROPY +#endif + +typedef struct GPipe { + int wfd, rfd; + char *who; +} GPipe; + +typedef struct GTalk { + GPipe *pipe; + Jmp_buf errjmp; +} GTalk; + +typedef struct GProc { + GPipe pipe; + int pid; +} GProc; + +typedef enum displayStatus { notRunning = 0, running, zombie, phoenix, raiser, + textMode, reserve, remoteLogin } DisplayStatus; + +typedef enum serverStatus { ignore = 0, awaiting, starting, + terminated, killed, pausing } ServerStatus; + +typedef struct RcStr { + struct RcStr *next; + char *str; + int cnt; +} RcStr; + +typedef struct CfgDep { + RcStr *name; + long time; +} CfgDep; + +typedef struct CfgArr { + char *data; /* config value array; allocated */ + long *idx; /* config index array; alias */ + CfgDep dep; /* filestamp */ + int numCfgEnt; /* number of config entries */ +} CfgArr; + +struct bsock { + int fd; + int buflen; + char *buffer; +}; + +struct cmdsock { + struct cmdsock *next; + struct bsock sock; /* buffered fd of the socket */ +}; + +typedef struct { + struct cmdsock *css; /* open connections */ + + char *path; /* filename of the socket */ +#ifndef HONORS_SOCKET_PERMS + char *realdir; /* real dirname of the socket */ +#endif + int fd; /* fd of the socket */ + int gid; /* owner group of the socket */ + + char *fpath; /* filename of the fifo */ + struct bsock fifo; /* buffered fd of the fifo */ +} CtrlRec; + +struct display { + struct display *next; + struct disphist *hstent; /* display history entry */ + + /* basic display information */ + char *name; /* DISPLAY name -- also referenced in hstent */ + char *class2; /* display class (may be NULL) */ + int displayType; /* location/origin/lifetime */ + CfgArr cfg; /* config data array */ + + /* display state */ + DisplayStatus status; /* current status */ + int zstatus; /* substatus while zombie */ + int pid; /* process id of child */ + int serverPid; /* process id of server (-1 if none) */ +#ifdef HAVE_VTS + int serverVT; /* server VT (0 = none, -1 = pending) */ + struct display *follower; /* on exit, hand VT to this display */ +#endif + ServerStatus serverStatus; /* X server startup state */ + Time_t lastStart; /* time of last display start */ + int startTries; /* current start try */ + int stillThere; /* state during HUP processing */ + int userSess; /* -1=nobody, otherwise uid */ + char *userName; + char *sessName; + CtrlRec ctrl; /* command socket & fifo */ + GPipe pipe; /* comm master <-> slave */ + GPipe gpipe; /* comm master <-> greeter */ +#ifdef XDMCP + char *remoteHost; /* for X -query type remote login */ + /* XDMCP state */ + unsigned sessionID; /* ID of active session */ + ARRAY8 peer; /* display peer address */ + ARRAY8 from; /* XDMCP port of display */ + unsigned displayNumber; /* numerical part of name */ + int useChooser; /* Run the chooser for this display */ + ARRAY8 clientAddr; /* for chooser picking */ + unsigned connectionType; /* ... */ + int xdmcpFd; +#endif + + CONF_CORE_LOCAL_DEFS + + int idleTimeout; /* abort login after that time */ + + unsigned short *authNameLens; /* authorization protocol name lens */ + + /* information potentially derived from resources */ + int authNameNum; /* number of protocol names */ + Xauth **authorizations; /* authorization data */ + int authNum; /* number of authorizations */ + char *authFile; /* file to store authorization in */ +}; + +typedef struct { + unsigned how:2, /* 0=none 1=reboot 2=halt (SHUT_*) */ + force:2; + int uid; + int start; + int timeout; + char *osname; + time_t bmstamp; + int osindex; +} SdRec; + +struct disphist { + struct disphist *next; + char *name; + Time_t lastExit; /* time of last display exit */ + unsigned rLogin:2, /* 0=nothing 1=relogin 2=login */ + lock:1, /* screen locker running */ + goodExit:1; /* was the last exit "peaceful"? */ + SdRec sdRec; + char *nuser, *npass, *nargs; +}; + +#ifdef XDMCP + +#define PROTO_TIMEOUT (30 * 60) /* 30 minutes should be long enough */ + +struct protoDisplay { + struct protoDisplay *next; + XdmcpNetaddr address; /* UDP address */ + int addrlen; /* UDP address length */ + unsigned long date; /* creation date */ + CARD16 displayNumber; + CARD16 connectionType; + ARRAY8 connectionAddress; + CARD32 sessionID; + Xauth *fileAuthorization; + Xauth *xdmcpAuthorization; + ARRAY8 authenticationName; + ARRAY8 authenticationData; + XdmAuthKeyRec key; +}; +#endif /* XDMCP */ + +/* status code for RStopDisplay */ +#define DS_RESTART 0 +#define DS_TEXTMODE 1 +#define DS_RESERVE 2 +#define DS_REMOTE 3 +#define DS_REMOVE 4 + +/* command codes dpy process -> master process */ +#define D_User 1 +#define D_ReLogin 2 +#define D_ChooseHost 4 +#define D_RemoteHost 5 +#define D_XConnOk 6 + +extern int debugLevel; + +CONF_CORE_GLOBAL_DECLS + +/* in daemon.c */ +void BecomeDaemon( void ); + +/* in dm.c */ +extern char *prog, *progpath; +extern time_t now; +extern SdRec sdRec; +void StartDisplay( struct display *d ); +void StartDisplayP2( struct display *d ); +void StopDisplay( struct display *d ); +void SetTitle( const char *name ); +void SwitchToX( struct display *d ); +void setNLogin( struct display *d, + const char *nuser, const char *npass, char *nargs, + int rl ); +void cancelShutdown( void ); +int TTYtoVT( const char *tty ); +int activateVT( int vt ); + +/* in ctrl.c */ +void openCtrl( struct display *d ); +void closeCtrl( struct display *d ); +int handleCtrl( FD_TYPE *reads, struct display *d ); +void chownCtrl( CtrlRec *cr, int uid ); +void updateCtrl( void ); + +/* in dpylist.c */ +extern struct display *displays; /* that's ugly ... */ +int AnyDisplaysLeft( void ); +void ForEachDisplay( void (*f)( struct display * ) ); +#ifdef HAVE_VTS +void ForEachDisplayRev( void (*f)( struct display * ) ); +#endif +void RemoveDisplay( struct display *old ); +struct display + *FindDisplayByName( const char *name ), +#ifdef XDMCP + *FindDisplayBySessionID( CARD32 sessionID ), + *FindDisplayByAddress( XdmcpNetaddr addr, int addrlen, CARD16 displayNumber ), +#endif /* XDMCP */ + *FindDisplayByPid( int pid ), + *FindDisplayByServerPid( int serverPid ), + *NewDisplay( const char *name ); +int AnyActiveDisplays( void ); +int AnyRunningDisplays( void ); +int AnyReserveDisplays( void ); +int idleReserveDisplays( void ); +int AllLocalDisplaysLocked( struct display *dp ); +int StartReserveDisplay( int lt ); +void ReapReserveDisplays( void ); + +/* in reset.c */ +void pseudoReset( void ); + +/* in resource.c */ +char **FindCfgEnt( struct display *d, int id ); +int InitResources( char **argv ); +int LoadDMResources( int force ); +int LoadDisplayResources( struct display *d ); +void ScanServers( void ); +void CloseGetter( void ); +int startConfig( int what, CfgDep *dep, int force ); +RcStr *newStr( char *str ); +void delStr( RcStr *str ); +extern GTalk cnftalk; + +/* in session.c */ +extern struct display *td; +extern const char *td_setup; +char **baseEnv( const char *user ); +char **inheritEnv( char **env, const char **what ); +char **systemEnv( const char *user ); +int source( char **env, const char *file, const char *arg ); +void ManageSession( struct display *d ); + +extern GTalk mstrtalk, grttalk; +extern GProc grtproc; +void OpenGreeter( void ); +int CloseGreeter( int force ); +int CtrlGreeterWait( int wreply ); +void PrepErrorGreet( void ); +char *conv_interact( int what, const char *prompt ); + +/* process.c */ +typedef void (*SIGFUNC)( int ); +SIGFUNC Signal( int, SIGFUNC Handler ); + +void RegisterInput( int fd ); +void UnregisterInput( int fd ); +void RegisterCloseOnFork( int fd ); +void ClearCloseOnFork( int fd ); +void CloseNClearCloseOnFork( int fd ); +int Fork( void ); +int Wait4( int pid ); +void execute( char **argv, char **env ); +int runAndWait( char **args, char **env ); +FILE *pOpen( char **what, char m, int *pid ); +int pClose( FILE *f, int pid ); +char *locate( const char *exe ); +void TerminateProcess( int pid, int sig ); + +void GSet( GTalk *talk); /* call before GOpen! */ +int GFork( GPipe *pajp, const char *pname, char *cname, + GPipe *ogp, char *cgname ); +void GClosen( GPipe *pajp ); +int GOpen( GProc *proc, + char **argv, const char *what, char **env, char *cname, + GPipe *gp ); +int GClose( GProc *proc, GPipe *gp, int force ); + +void GSendInt( int val ); +int GRecvInt( void ); +int GRecvCmd( int *cmd ); +void GSendArr( int len, const char *data ); +char *GRecvArr( int *len ); +int GRecvStrBuf( char *buf ); +int GRecvArrBuf( char *buf ); +void GSendStr( const char *buf ); +void GSendNStr( const char *buf, int len ); /* exact len, buf != 0 */ +void GSendStrN( const char *buf, int len ); /* maximal len */ +char *GRecvStr( void ); +void GSendArgv( char **argv ); +void GSendStrArr( int len, char **data ); +char **GRecvStrArr( int *len ); +char **GRecvArgv( void ); + +/* client.c */ +#define GCONV_NORMAL 0 +#define GCONV_HIDDEN 1 +#define GCONV_USER 2 +#define GCONV_PASS 3 +#define GCONV_PASS_ND 4 +#define GCONV_BINARY 5 +typedef char *(*GConvFunc)( int what, const char *prompt ); +int Verify( GConvFunc gconv, int rootok ); +#ifdef WITH_CONSOLE_KIT +int StartClient( const char *ck_session_cookie ); +#else +int StartClient( void ); +#endif +void SessionExit( int status ) ATTR_NORETURN; +int ReadDmrc( void ); +extern char **userEnviron, **systemEnviron; +extern char *curuser, *curpass, *curtype, *newpass, + *dmrcuser, *curdmrc, *newdmrc; +extern int cursource; +#define PWSRC_MANUAL 0 +#define PWSRC_AUTOLOGIN 1 +#define PWSRC_RELOGIN 2 + +/* server.c */ +char **PrepServerArgv( struct display *d, const char *args ); +void StartServer( struct display *d ); +void AbortStartServer( struct display *d ); +void StartServerSuccess( void ); +void StartServerFailed( void ); +void StartServerTimeout( void ); +extern struct display *startingServer; +extern time_t serverTimeout; + +void WaitForServer( struct display *d ); +void ResetServer( struct display *d ); +int PingServer(struct display *d ); +extern Display *dpy; + +/* in util.c */ +void *Calloc( size_t nmemb, size_t size ); +void *Malloc( size_t size ); +void *Realloc( void *ptr, size_t size ); +void WipeStr( char *str ); +int StrCmp( const char *s1, const char *s2 ); +#ifdef HAVE_STRNLEN +# define StrNLen(s, m) strnlen(s, m) +#else +int StrNLen( const char *s, int max ); +#endif +int StrNDup( char **dst, const char *src, int len ); +int StrDup( char **dst, const char *src ); +int arrLen( char **arr ); +void freeStrArr( char **arr ); +char **initStrArr( char **arr ); +char **xCopyStrArr( int rn, char **arr ); +/* Note: the following functions free the old data even in case of failure */ +int ReStrN( char **dst, const char *src, int len ); +int ReStr( char **dst, const char *src ); +int StrApp( char **dst, ... ); +char **addStrArr( char **arr, const char *str, int len ); +char **parseArgs( char **argv, const char *string ); +/* End note */ +char **setEnv( char **e, const char *name, const char *value ); +char **putEnv( const char *string, char **env ); +const char *getEnv( char **e, const char *name ); +const char *localHostname( void ); +int Reader( int fd, void *buf, int len ); +int Writer( int fd, const void *buf, int len ); +int fGets( char *buf, int max, FILE *f ); +void randomStr( char *s ); +time_t mTime( const char *fn ); +void ListSessions( int flags, struct display *d, void *ctx, + void (*emitXSess)( struct display *, struct display *, void * ), + void (*emitTTYSess)( STRUCTUTMP *, struct display *, void * ) ); + +/* in inifile.c */ +char *iniLoad( const char *fname ); +int iniSave( const char *data, const char *fname ); +char *iniEntry( char *data, const char *section, const char *key, const char *value ); +char *iniMerge( char *data, const char *newdata ); + +/* in bootman.c */ +int getBootOptions( char ***opts, int *def, int *cur ); +int setBootOption( const char *opt, SdRec *sdr ); +void commitBootOption( void ); + +/* in netaddr.c */ +char *NetaddrAddress( char *netaddrp, int *lenp ); +char *NetaddrPort( char *netaddrp, int *lenp ); +int ConvertAddr( char *saddr, int *len, char **addr ); +int NetaddrFamily( char *netaddrp ); +int addressEqual( char *a1, int len1, char *a2, int len2 ); + +#ifdef XDMCP + +/* in xdmcp.c */ +char *NetworkAddressToHostname( CARD16 connectionType, ARRAY8Ptr connectionAddress ); +void SendFailed( struct display *d, const char *reason ); +void init_session_id( void ); + +/* in policy.c */ +struct sockaddr; +ARRAY8Ptr Accept( struct sockaddr *from, int fromlen, CARD16 displayNumber ); +ARRAY8Ptr ChooseAuthentication( ARRAYofARRAY8Ptr authenticationNames ); +int CheckAuthentication( struct protoDisplay *pdpy, ARRAY8Ptr displayID, ARRAY8Ptr name, ARRAY8Ptr data ); +int SelectAuthorizationTypeIndex( ARRAY8Ptr authenticationName, ARRAYofARRAY8Ptr authorizationNames ); +int SelectConnectionTypeIndex( ARRAY16Ptr connectionTypes, ARRAYofARRAY8Ptr connectionAddresses ); +int Willing( ARRAY8Ptr addr, CARD16 connectionType, ARRAY8Ptr authenticationName, ARRAY8Ptr status, xdmOpCode type ); + +/* in protodpy.c */ +void DisposeProtoDisplay( struct protoDisplay *pdpy ); + +struct protoDisplay *FindProtoDisplay( XdmcpNetaddr address, int addrlen, + CARD16 displayNumber ); +struct protoDisplay *NewProtoDisplay( XdmcpNetaddr address, int addrlen, + CARD16 displayNumber, + CARD16 connectionType, + ARRAY8Ptr connectionAddress, + CARD32 sessionID ); + +#define FamilyBroadcast 0xffff +typedef void (*ChooserFunc)( CARD16 connectionType, ARRAY8Ptr addr, char *closure ); +typedef void (*ListenFunc)( ARRAY8Ptr addr, void **closure ); + +/* in access.c */ +ARRAY8Ptr getLocalAddress( void ); +int AcceptableDisplayAddress( ARRAY8Ptr clientAddress, CARD16 connectionType, xdmOpCode type ); +int ForEachMatchingIndirectHost( ARRAY8Ptr clientAddress, CARD16 connectionType, ChooserFunc function, char *closure ); +void ScanAccessDatabase( int force ); +int UseChooser( ARRAY8Ptr clientAddress, CARD16 connectionType ); +void ForEachChooserHost( ARRAY8Ptr clientAddress, CARD16 connectionType, ChooserFunc function, char *closure ); +void ForEachListenAddr( ListenFunc listenfunction, ListenFunc mcastfcuntion, void **closure ); + +/* in choose.c */ +ARRAY8Ptr IndirectChoice( ARRAY8Ptr clientAddress, CARD16 connectionType ); +int IsIndirectClient( ARRAY8Ptr clientAddress, CARD16 connectionType ); +int RememberIndirectClient( ARRAY8Ptr clientAddress, CARD16 connectionType ); +void ForgetIndirectClient( ARRAY8Ptr clientAddress, CARD16 connectionType ); +int RegisterIndirectChoice( ARRAY8Ptr clientAddress, CARD16 connectionType, ARRAY8Ptr choice ); +int DoChoose( void ); + +/* socket.c or streams.c */ +void UpdateListenSockets( void ); +int AnyListenSockets( void ); +int ProcessListenSockets( FD_TYPE *reads ); + +/* in xdmcp.c */ +void ProcessRequestSocket( int fd ); + +#endif /* XDMCP */ + +/* in sessreg.c */ +void sessreg( struct display *d, int pid, const char *user, int uid ); + +#endif /* _DM_H_ */ diff --git a/tdm/backend/dm_auth.h b/tdm/backend/dm_auth.h new file mode 100644 index 000000000..28725ee8d --- /dev/null +++ b/tdm/backend/dm_auth.h @@ -0,0 +1,105 @@ +/************************************************************ + +Copyright 1998 by Thomas E. Dickey <[email protected]> + + All Rights Reserved + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name(s) of the above copyright +holders shall not be used in advertising or otherwise to promote the +sale, use or other dealings in this Software without prior written +authorization. + +********************************************************/ + +#ifndef _DM_AUTH_H_ +#define _DM_AUTH_H_ 1 + +#include "dm.h" + +void MitInitAuth( unsigned short name_len, const char *name ); +Xauth *MitGetAuth( unsigned short namelen, const char *name ); + +#ifdef HASXDMAUTH +void XdmInitAuth( unsigned short name_len, const char *name ); +Xauth *XdmGetAuth( unsigned short namelen, const char *name ); +# ifdef XDMCP +void XdmGetXdmcpAuth( struct protoDisplay *pdpy, + unsigned short authorizationNameLen, + const char *authorizationName ); +int XdmCheckAuthentication( struct protoDisplay *pdpy, + ARRAY8Ptr displayID, + ARRAY8Ptr authenticationName, + ARRAY8Ptr authenticationData ); +# else +# define XdmGetXdmcpAuth NULL +# endif +#endif + +#ifdef SECURE_RPC +void SecureRPCInitAuth( unsigned short name_len, const char *name ); +Xauth *SecureRPCGetAuth( unsigned short name_len, const char *name ); +#endif + +#ifdef K5AUTH +void Krb5InitAuth( unsigned short name_len, const char *name ); +Xauth *Krb5GetAuth( unsigned short name_len, const char *name ); + +Xauth *Krb5GetAuthFor( unsigned short name_len, const char *name, const char *dname ); +char *Krb5Init( const char *user, const char *passwd, const char *dname ); +void Krb5Destroy( const char *dname ); +#endif + +/* auth.c */ +int ValidAuthorization( unsigned short name_length, const char *name ); + + +#ifdef XDMCP + +void +SetProtoDisplayAuthorization( struct protoDisplay *pdpy, + unsigned short authorizationNameLen, + const char *authorizationName ); + +#endif /* XDMCP */ + +int SaveServerAuthorizations( struct display *d, Xauth **auths, int count ); +void CleanUpFileName( const char *src, char *dst, int len ); +void RemoveUserAuthorization( struct display *d ); +void SetAuthorization( struct display *d ); +void SetLocalAuthorization( struct display *d ); +void SetUserAuthorization( struct display *d ); + +/* genauth.c */ +int GenerateAuthData( char *auth, int len ); +#ifdef NEED_ENTROPY +void AddPreGetEntropy( void ); +void AddOtherEntropy( void ); +void AddTimerEntropy( void ); +#endif + +#ifdef HAVE_ARC4RANDOM +# define secureRandom() arc4random() +#else +int secureRandom( void ); +#endif + +#endif /* _DM_AUTH_H_ */ diff --git a/tdm/backend/dm_error.h b/tdm/backend/dm_error.h new file mode 100644 index 000000000..3570c18fc --- /dev/null +++ b/tdm/backend/dm_error.h @@ -0,0 +1,58 @@ +/************************************************************ + +Copyright 1998 by Thomas E. Dickey <[email protected]> + + All Rights Reserved + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name(s) of the above copyright +holders shall not be used in advertising or otherwise to promote the +sale, use or other dealings in this Software without prior written +authorization. + +********************************************************/ + + +#ifndef _DM_ERROR_H_ +#define _DM_ERROR_H_ 1 + +#include "greet.h" + +#include <stdarg.h> + +void GDebug( const char *fmt, ... ); +void Debug( const char *fmt, ... ); +void LogInfo( const char *fmt, ... ); +void LogWarn( const char *fmt, ... ); +void LogError( const char *fmt, ... ); +void LogPanic( const char *fmt, ... ) ATTR_NORETURN; +void LogOutOfMem( void ); +void Panic( const char *mesg ) ATTR_NORETURN; +void InitErrorLog( const char *errorLogFile ); +#ifdef USE_SYSLOG +void ReInitErrorLog( void ); +#else +# define ReInitErrorLog() while(0) +#endif +int ASPrintf( char **strp, const char *fmt, ... ); +int VASPrintf( char **strp, const char *fmt, va_list args ); + +#endif /* _DM_ERROR_H_ */ diff --git a/tdm/backend/dm_socket.h b/tdm/backend/dm_socket.h new file mode 100644 index 000000000..56a39fd0e --- /dev/null +++ b/tdm/backend/dm_socket.h @@ -0,0 +1,72 @@ +/************************************************************ + +Copyright 1998 by Thomas E. Dickey <[email protected]> +Copyright 2002-2004 Oswald Buddenhagen <[email protected]> + + All Rights Reserved + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name(s) of the above copyright +holders shall not be used in advertising or otherwise to promote the +sale, use or other dealings in this Software without prior written +authorization. + +********************************************************/ + +#ifndef _DM_SOCKET_H_ +#define _DM_SOCKET_H_ 1 + +#ifndef __Lynx__ +# include <sys/socket.h> +#else +# include <socket.h> +#endif + +#ifdef TCPCONN +# include <netinet/in.h> +#endif + +#ifdef UNIXCONN +# ifndef __Lynx__ +# include <sys/un.h> +# else +# include <un.h> +# endif +#endif + +#ifdef DNETCONN +# include <netdnet/dn.h> +#endif + +#if (defined(__svr4__) && !defined(__sun__)) && defined(SIOCGIFCONF) +# define SYSV_SIOCGIFCONF +int ifioctl( int fd, int cmd, char *arg ); +#else +# define ifioctl ioctl +#endif + +#ifdef BSD +# if (BSD >= 199103) +# define VARIABLE_IFREQ +# endif +#endif + +#endif /* _DM_SOCKET_H_ */ diff --git a/tdm/backend/dpylist.c b/tdm/backend/dpylist.c new file mode 100644 index 000000000..b512293f7 --- /dev/null +++ b/tdm/backend/dpylist.c @@ -0,0 +1,294 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2000-2005 Oswald Buddenhagen <[email protected]> + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + * + * a simple linked list of known displays + */ + +#include "dm.h" +#include "dm_error.h" + +struct display *displays; +static struct disphist *disphist; + +int +AnyDisplaysLeft( void ) +{ + return displays != (struct display *)0; +} + +int +AnyActiveDisplays( void ) +{ + struct display *d; + + for (d = displays; d; d = d->next) + if (d->status == remoteLogin || d->userSess >= 0) + return 1; + return 0; +} + +int +AnyRunningDisplays( void ) +{ + struct display *d; + + for (d = displays; d; d = d->next) + switch (d->status) { + case notRunning: + case textMode: + case reserve: + break; + default: + return 1; + } + return 0; +} + +int +AnyReserveDisplays( void ) +{ + struct display *d; + + for (d = displays; d; d = d->next) + if ((d->displayType & d_lifetime) == dReserve) + return 1; + return 0; +} + +int +idleReserveDisplays( void ) +{ + struct display *d; + int cnt = 0; + + for (d = displays; d; d = d->next) + if (d->status == reserve) + cnt++; + return cnt; +} + +int +StartReserveDisplay( int lt ) +{ + struct display *d, *rd; + + for (rd = 0, d = displays; d; d = d->next) + if (d->status == reserve) + rd = d; + if (rd) { + rd->idleTimeout = lt; + rd->status = notRunning; + return 1; + } + return 0; +} + +void +ForEachDisplay( void (*f)( struct display * ) ) +{ + struct display *d, *next; + + for (d = displays; d; d = next) { + next = d->next; + (*f)( d ); + } +} + +#ifdef HAVE_VTS +static void +_forEachDisplayRev( struct display *d, void (*f)( struct display * ) ) +{ + if (d) { + if (d->next) + _forEachDisplayRev( d->next, f ); + (*f)( d ); + } +} + +void +ForEachDisplayRev( void (*f)( struct display * ) ) +{ + _forEachDisplayRev( displays, f ); +} +#endif + +struct display * +FindDisplayByName( const char *name ) +{ + struct display *d; + + for (d = displays; d; d = d->next) + if (!strcmp( name, d->name )) + return d; + return 0; +} + +struct display * +FindDisplayByPid( int pid ) +{ + struct display *d; + + for (d = displays; d; d = d->next) + if (pid == d->pid) + return d; + return 0; +} + +struct display * +FindDisplayByServerPid( int serverPid ) +{ + struct display *d; + + for (d = displays; d; d = d->next) + if (serverPid == d->serverPid) + return d; + return 0; +} + +#ifdef XDMCP + +struct display * +FindDisplayBySessionID( CARD32 sessionID ) +{ + struct display *d; + + for (d = displays; d; d = d->next) + if (sessionID == d->sessionID) + return d; + return 0; +} + +struct display * +FindDisplayByAddress( XdmcpNetaddr addr, int addrlen, CARD16 displayNumber ) +{ + struct display *d; + + for (d = displays; d; d = d->next) + if ((d->displayType & d_origin) == dFromXDMCP && + d->displayNumber == displayNumber && + addressEqual( (XdmcpNetaddr)d->from.data, d->from.length, + addr, addrlen )) + return d; + return 0; +} + +#endif /* XDMCP */ + +#define IfFree(x) if (x) free( (char *)x ) + +void +RemoveDisplay( struct display *old ) +{ + struct display *d, **dp; + int i; + + for (dp = &displays; (d = *dp); dp = &(*dp)->next) { + if (d == old) { + Debug( "Removing display %s\n", d->name ); + *dp = d->next; + IfFree( d->class2 ); + IfFree( d->cfg.data ); + delStr( d->cfg.dep.name ); +#ifdef XDMCP + IfFree( d->remoteHost ); +#endif + if (d->authorizations) { + for (i = 0; i < d->authNum; i++) + XauDisposeAuth( d->authorizations[i] ); + free( (char *)d->authorizations ); + } + if (d->authFile) { + (void)unlink( d->authFile ); + free( d->authFile ); + } + IfFree( d->authNameLens ); +#ifdef XDMCP + XdmcpDisposeARRAY8( &d->peer ); + XdmcpDisposeARRAY8( &d->from ); + XdmcpDisposeARRAY8( &d->clientAddr ); +#endif + free( (char *)d ); + break; + } + } +} + +static struct disphist * +FindHist( const char *name ) +{ + struct disphist *hstent; + + for (hstent = disphist; hstent; hstent = hstent->next) + if (!strcmp( hstent->name, name )) + return hstent; + return 0; +} + +struct display * +NewDisplay( const char *name ) +{ + struct display *d; + struct disphist *hstent; + + if (!(hstent = FindHist( name ))) { + if (!(hstent = Calloc( 1, sizeof(*hstent) ))) + return 0; + if (!StrDup( &hstent->name, name )) { + free( hstent ); + return 0; + } + hstent->next = disphist; disphist = hstent; + } + + if (!(d = (struct display *)Calloc( 1, sizeof(*d) ))) + return 0; + d->next = displays; + d->hstent = hstent; + d->name = hstent->name; + /* initialize fields (others are 0) */ + d->pid = -1; + d->serverPid = -1; + d->ctrl.fd = -1; + d->ctrl.fifo.fd = -1; + d->pipe.rfd = -1; + d->pipe.wfd = -1; + d->gpipe.rfd = -1; + d->gpipe.wfd = -1; + d->userSess = -1; +#ifdef XDMCP + d->xdmcpFd = -1; +#endif + displays = d; + Debug( "created new display %s\n", d->name ); + return d; +} diff --git a/tdm/backend/error.c b/tdm/backend/error.c new file mode 100644 index 000000000..93ec40e70 --- /dev/null +++ b/tdm/backend/error.c @@ -0,0 +1,130 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2000-2004 Oswald Buddenhagen <[email protected]> + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + * + * Log display manager errors to a file as + * we generally do not have a terminal to talk to + * or use syslog if it exists + */ + +#include "dm.h" +#include "dm_error.h" + +#include <unistd.h> +#include <stdio.h> + +#define PRINT_QUOTES +#define PRINT_ARRAYS +#define LOG_DEBUG_MASK DEBUG_CORE +#define LOG_PANIC_EXIT 1 +#define NEED_ASPRINTF +#define STATIC +#include "printf.c" + +void +GDebug( const char *fmt, ... ) +{ + va_list args; + + if (debugLevel & DEBUG_HLPCON) { + va_start( args, fmt ); + Logger( DM_DEBUG, fmt, args ); + va_end( args ); + } +} + +void +Panic( const char *mesg ) +{ + int fd = open( "/dev/console", O_WRONLY ); + write( fd, "xdm panic: ", 11 ); + write( fd, mesg, strlen( mesg ) ); + write( fd, "\n", 1 ); +#ifdef USE_SYSLOG + ReInitErrorLog(); + syslog( LOG_ALERT, "%s", mesg ); +#endif + exit( 1 ); +} + +#ifdef USE_SYSLOG +void +ReInitErrorLog() +{ + if (!(debugLevel & DEBUG_NOSYSLOG)) + InitLog(); +} +#endif + +void +InitErrorLog( const char *errorLogFile ) +{ + int fd; + char buf[128]; + +#ifdef USE_SYSLOG + ReInitErrorLog(); +#endif + /* We do this independently of using syslog, as we cannot redirect + * the output of external programs to syslog. + */ + if (!errorLogFile || strcmp( errorLogFile, "-" )) { + if (!errorLogFile) { + sprintf( buf, "/var/log/%s.log", prog ); + errorLogFile = buf; + } + if ((fd = open( errorLogFile, O_CREAT | O_APPEND | O_WRONLY, 0666 )) < 0) + LogError( "Cannot open log file %s\n", errorLogFile ); + else { +#ifdef USE_SYSLOG +# ifdef USE_PAM +# define PAMLOG " PAM logs messages related to authentication to authpriv.*." +# else +# define PAMLOG +# endif +# define WARNMSG \ + "********************************************************************************\n" \ + "Note that your system uses syslog. All of tdm's internally generated messages\n" \ + "(i.e., not from libraries and external programs/scripts it uses) go to the\n" \ + "daemon.* syslog facility; check your syslog configuration to find out to which\n" \ + "file(s) it is logged." PAMLOG "\n" \ + "********************************************************************************\n\n" + if (!lseek( fd, 0, SEEK_END )) + write( fd, WARNMSG, sizeof(WARNMSG) - 1 ); +#endif + dup2( fd, 1 ); + close( fd ); + dup2( 1, 2 ); + } + } +} + diff --git a/tdm/backend/genauth.c b/tdm/backend/genauth.c new file mode 100644 index 000000000..6da95cce0 --- /dev/null +++ b/tdm/backend/genauth.c @@ -0,0 +1,500 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2003-2004 Oswald Buddenhagen <[email protected]> + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + */ + +#include "dm.h" +#include "dm_auth.h" +#include "dm_error.h" + +#ifdef NEED_ENTROPY + +# include <signal.h> + +/* ####################################################################### */ + +/* + * Copyright (c) 1995,1999 Theo de Raadt. All rights reserved. + * Copyright (c) 2001-2002 Damien Miller. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "dm_socket.h" + +#include <string.h> + +#ifndef INADDR_LOOPBACK +# define INADDR_LOOPBACK 0x7F000001U +#endif + +#ifndef offsetof +# define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +static int +getPrngdBytes( char *buf, int len, + unsigned short tcp_port, const char *socket_path ) +{ + int fd, addr_len, rval, errors; + char msg[2]; + struct sockaddr *addr; + struct sockaddr_in addr_in; + struct sockaddr_un addr_un; + int af; + SIGFUNC old_sigpipe; + + if (tcp_port) { + memset( &addr_in, 0, sizeof(addr_in) ); + af = addr_in.sin_family = AF_INET; + addr_in.sin_addr.s_addr = htonl( INADDR_LOOPBACK ); + addr_in.sin_port = htons( tcp_port ); + addr_len = sizeof(addr_in); + addr = (struct sockaddr *)&addr_in; + } else if (*socket_path) { + unsigned spl = strlen( socket_path ); + if (spl >= sizeof(addr_un.sun_path)) { + LogError( "get_random_prngd: " + "Random pool path is too long\n" ); + return -1; + } + af = addr_un.sun_family = AF_UNIX; + strncpy( addr_un.sun_path, socket_path, + sizeof(addr_un.sun_path) ); + addr_len = offsetof( struct sockaddr_un, sun_path ) + spl + 1; + addr = (struct sockaddr *)&addr_un; + } else + return -1; + + old_sigpipe = Signal( SIGPIPE, SIG_IGN ); + + errors = 0; + rval = -1; +reopen: + if ((fd = socket( af, SOCK_STREAM, 0 )) < 0) { + LogError( "Couldn't create socket: %m\n" ); + goto done; + } + + if (connect( fd, (struct sockaddr *)addr, addr_len )) { + if (af == AF_INET) + LogError( "Couldn't connect to PRNGD port %d: %m\n", + tcp_port ); + else + LogError( "Couldn't connect to PRNGD socket %\"s: %m\n", + socket_path ); + goto done; + } + + /* Send blocking read request to PRNGD */ + msg[0] = 0x02; + msg[1] = len; + + if (Writer( fd, msg, sizeof(msg) ) != sizeof(msg)) { + if (errno == EPIPE && errors < 10) { + close( fd ); + errors++; + goto reopen; + } + LogError( "Couldn't write to PRNGD socket: %m\n" ); + goto done; + } + + if (Reader( fd, buf, len ) != len) { + if (errno == EPIPE && errors < 10) { + close( fd ); + errors++; + goto reopen; + } + LogError( "Couldn't read from PRNGD socket: %m\n" ); + goto done; + } + + rval = 0; +done: + Signal( SIGPIPE, old_sigpipe ); + if (fd != -1) + close( fd ); + return rval; +} + +/* ####################################################################### */ + +/* + * Stolen from the Linux kernel. + * + * Copyright Theodore Ts'o, 1994, 1995, 1996, 1997, 1998, 1999. All + * rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +static unsigned epool[32], erotate, eadd_ptr; + +static void +add_entropy( unsigned const *in, int nwords ) +{ + static unsigned const twist_table[8] = { + 0, 0x3b6e20c8, 0x76dc4190, 0x4db26158, + 0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 }; + unsigned i, w; + int new_rotate; + + while (nwords--) { + w = *in++; + w = (w<<erotate | w>>(32-erotate)) & 0xffffffff; + i = eadd_ptr = (eadd_ptr - 1) & 31; + new_rotate = erotate + 14; + if (i) + new_rotate = erotate + 7; + erotate = new_rotate & 31; + w ^= epool[(i + 26) & 31]; + w ^= epool[(i + 20) & 31]; + w ^= epool[(i + 14) & 31]; + w ^= epool[(i + 7) & 31]; + w ^= epool[(i + 1) & 31]; + w ^= epool[i]; + epool[i] = (w >> 3) ^ twist_table[w & 7]; + } +} + +/* ####################################################################### */ + +/* + * This code implements something close to the MD5 message-digest + * algorithm. This code is based on code written by Colin Plumb + * in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + */ + +/* The four core functions - F1 is optimized somewhat */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1 (z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define pmd5_step(f, w, x, y, z, data, s) \ + (w += (f(x, y, z) + data) & 0xffffffff, w = w<<s | w>>(32-s), w += x) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. + */ +static void +pmd5_hash( unsigned *out, unsigned const in[16] ) +{ + unsigned a, b, c, d; + + a = out[0]; + b = out[1]; + c = out[2]; + d = out[3]; + + pmd5_step( F1, a, b, c, d, in[0] + 0xd76aa478, 7 ); + pmd5_step( F1, d, a, b, c, in[1] + 0xe8c7b756, 12 ); + pmd5_step( F1, c, d, a, b, in[2] + 0x242070db, 17 ); + pmd5_step( F1, b, c, d, a, in[3] + 0xc1bdceee, 22 ); + pmd5_step( F1, a, b, c, d, in[4] + 0xf57c0faf, 7 ); + pmd5_step( F1, d, a, b, c, in[5] + 0x4787c62a, 12 ); + pmd5_step( F1, c, d, a, b, in[6] + 0xa8304613, 17 ); + pmd5_step( F1, b, c, d, a, in[7] + 0xfd469501, 22 ); + pmd5_step( F1, a, b, c, d, in[8] + 0x698098d8, 7 ); + pmd5_step( F1, d, a, b, c, in[9] + 0x8b44f7af, 12 ); + pmd5_step( F1, c, d, a, b, in[10] + 0xffff5bb1, 17 ); + pmd5_step( F1, b, c, d, a, in[11] + 0x895cd7be, 22 ); + pmd5_step( F1, a, b, c, d, in[12] + 0x6b901122, 7 ); + pmd5_step( F1, d, a, b, c, in[13] + 0xfd987193, 12 ); + pmd5_step( F1, c, d, a, b, in[14] + 0xa679438e, 17 ); + pmd5_step( F1, b, c, d, a, in[15] + 0x49b40821, 22 ); + + pmd5_step( F2, a, b, c, d, in[1] + 0xf61e2562, 5 ); + pmd5_step( F2, d, a, b, c, in[6] + 0xc040b340, 9 ); + pmd5_step( F2, c, d, a, b, in[11] + 0x265e5a51, 14 ); + pmd5_step( F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20 ); + pmd5_step( F2, a, b, c, d, in[5] + 0xd62f105d, 5 ); + pmd5_step( F2, d, a, b, c, in[10] + 0x02441453, 9 ); + pmd5_step( F2, c, d, a, b, in[15] + 0xd8a1e681, 14 ); + pmd5_step( F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20 ); + pmd5_step( F2, a, b, c, d, in[9] + 0x21e1cde6, 5 ); + pmd5_step( F2, d, a, b, c, in[14] + 0xc33707d6, 9 ); + pmd5_step( F2, c, d, a, b, in[3] + 0xf4d50d87, 14 ); + pmd5_step( F2, b, c, d, a, in[8] + 0x455a14ed, 20 ); + pmd5_step( F2, a, b, c, d, in[13] + 0xa9e3e905, 5 ); + pmd5_step( F2, d, a, b, c, in[2] + 0xfcefa3f8, 9 ); + pmd5_step( F2, c, d, a, b, in[7] + 0x676f02d9, 14 ); + pmd5_step( F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20 ); + + pmd5_step( F3, a, b, c, d, in[5] + 0xfffa3942, 4 ); + pmd5_step( F3, d, a, b, c, in[8] + 0x8771f681, 11 ); + pmd5_step( F3, c, d, a, b, in[11] + 0x6d9d6122, 16 ); + pmd5_step( F3, b, c, d, a, in[14] + 0xfde5380c, 23 ); + pmd5_step( F3, a, b, c, d, in[1] + 0xa4beea44, 4 ); + pmd5_step( F3, d, a, b, c, in[4] + 0x4bdecfa9, 11 ); + pmd5_step( F3, c, d, a, b, in[7] + 0xf6bb4b60, 16 ); + pmd5_step( F3, b, c, d, a, in[10] + 0xbebfbc70, 23 ); + pmd5_step( F3, a, b, c, d, in[13] + 0x289b7ec6, 4 ); + pmd5_step( F3, d, a, b, c, in[0] + 0xeaa127fa, 11 ); + pmd5_step( F3, c, d, a, b, in[3] + 0xd4ef3085, 16 ); + pmd5_step( F3, b, c, d, a, in[6] + 0x04881d05, 23 ); + pmd5_step( F3, a, b, c, d, in[9] + 0xd9d4d039, 4 ); + pmd5_step( F3, d, a, b, c, in[12] + 0xe6db99e5, 11 ); + pmd5_step( F3, c, d, a, b, in[15] + 0x1fa27cf8, 16 ); + pmd5_step( F3, b, c, d, a, in[2] + 0xc4ac5665, 23 ); + + pmd5_step( F4, a, b, c, d, in[0] + 0xf4292244, 6 ); + pmd5_step( F4, d, a, b, c, in[7] + 0x432aff97, 10 ); + pmd5_step( F4, c, d, a, b, in[14] + 0xab9423a7, 15 ); + pmd5_step( F4, b, c, d, a, in[5] + 0xfc93a039, 21 ); + pmd5_step( F4, a, b, c, d, in[12] + 0x655b59c3, 6 ); + pmd5_step( F4, d, a, b, c, in[3] + 0x8f0ccc92, 10 ); + pmd5_step( F4, c, d, a, b, in[10] + 0xffeff47d, 15 ); + pmd5_step( F4, b, c, d, a, in[1] + 0x85845dd1, 21 ); + pmd5_step( F4, a, b, c, d, in[8] + 0x6fa87e4f, 6 ); + pmd5_step( F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10 ); + pmd5_step( F4, c, d, a, b, in[6] + 0xa3014314, 15 ); + pmd5_step( F4, b, c, d, a, in[13] + 0x4e0811a1, 21 ); + pmd5_step( F4, a, b, c, d, in[4] + 0xf7537e82, 6 ); + pmd5_step( F4, d, a, b, c, in[11] + 0xbd3af235, 10 ); + pmd5_step( F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15 ); + pmd5_step( F4, b, c, d, a, in[9] + 0xeb86d391, 21 ); + + out[0] += a; + out[1] += b; + out[2] += c; + out[3] += d; +} + +/* ####################################################################### */ + + +static int +sumFile( const char *name, int len, int whence, long offset ) +{ + int fd, i, cnt, readlen = 0; + unsigned char buf[0x1000]; + + if ((fd = open( name, O_RDONLY )) < 0) { + Debug( "cannot open entropy source %\"s: %m\n", name ); + return -1; + } + lseek( fd, offset, whence ); + while (readlen < len) { + if (!(cnt = read( fd, buf, sizeof(buf) ))) + break; + if (cnt < 0) { + close( fd ); + Debug( "cannot read entropy source %\"s: %m\n", name ); + return -1; + } + readlen += cnt; + if (sizeof(unsigned) == 4) + add_entropy( (unsigned *)buf, (cnt + 3) / 4 ); + else { + unsigned buf2[sizeof(buf) / 4]; + for (i = 0; i < cnt; i += 8) { + buf2[i / 4] = *(unsigned *)(buf + i) & 0xffffffff; + buf2[i / 4 + 1] = *(unsigned *)(buf + i) >> 32; + } + add_entropy( buf2, (cnt + 3) / 4 ); + } + } + close( fd ); + Debug( "read %d bytes from entropy source %\"s\n", readlen, name ); + return readlen; +} + +void +AddTimerEntropy( void ) +{ + struct timeval now; + gettimeofday( &now, 0 ); + add_entropy( (unsigned *)&now, sizeof(now)/sizeof(unsigned) ); +} + +#define BSIZ 0x10000 + +void +AddOtherEntropy( void ) +{ + AddTimerEntropy(); + /* XXX -- setup-specific ... use some common ones */ + sumFile( "/var/log/messages", 0x1000, SEEK_END, -0x1000 ); + sumFile( "/var/log/syslog", 0x1000, SEEK_END, -0x1000 ); + sumFile( "/var/log/debug", 0x1000, SEEK_END, -0x1000 ); + sumFile( "/var/log/kern.log", 0x1000, SEEK_END, -0x1000 ); + sumFile( "/var/log/daemon.log", 0x1000, SEEK_END, -0x1000 ); +/* root hardly ever has an own box ... maybe pick a random mailbox instead? eek ... + sumFile( "/var/spool/mail/root", 0x1000, SEEK_END, -0x1000 ); +*/ +} + +void +AddPreGetEntropy( void ) +{ + static long offset; + int readlen; + + AddTimerEntropy(); + if ((readlen = sumFile( randomFile, BSIZ, SEEK_SET, offset )) == BSIZ) { + offset += readlen; +#if defined(__i386__) || defined(amiga) + if (!strcmp( randomFile, "/dev/mem" )) { + if (offset == 0xa0000) /* skip 640kB-1MB ROM mappings */ + offset = 0x100000; + else if (offset == 0xf00000) /* skip 15-16MB memory hole */ + offset = 0x1000000; + } +#endif + return; + } else if (readlen >= 0 && offset) { + if ((offset = sumFile( randomFile, BSIZ, SEEK_SET, 0 )) == BSIZ) + return; + } + LogError( "Cannot read randomFile %\"s; " + "X cookies may be easily guessable\n", randomFile ); +} +#endif + +/* ONLY 8 or 16 bytes! */ +/* auth MUST be sizeof(unsigned)-aligned! */ +int +GenerateAuthData( char *auth, int len ) +{ +#ifdef HAVE_ARC4RANDOM + int i; + unsigned *rnd = (unsigned *)auth; + if (sizeof(unsigned) == 4) + for (i = 0; i < len; i += 4) + rnd[i / 4] = arc4random(); + else + for (i = 0; i < len; i += 8) + rnd[i / 8] = arc4random() | (arc4random() << 32); + return 1; +#else + int fd; + const char *rd = randomDevice; +# ifdef DEV_RANDOM + if (!*rd) + rd = DEV_RANDOM; +# else + if (*rd) { +# endif + if ((fd = open( rd, O_RDONLY )) >= 0) { + if (read( fd, auth, len ) == len) { + close( fd ); + return 1; + } + close( fd ); + LogError( "Cannot read randomDevice %\"s: %m\n", rd ); + } else + LogError( "Cannot open randomDevice %\"s: %m\n", rd ); +# ifdef DEV_RANDOM + return 0; +# else + } + + if (!getPrngdBytes( auth, len, prngdPort, prngdSocket )) + return 1; + + { + unsigned *rnd = (unsigned *)auth; + unsigned tmp[4] = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 }; + AddPreGetEntropy(); + pmd5_hash( tmp, epool ); + add_entropy( tmp, 1 ); + pmd5_hash( tmp, epool + 16 ); + add_entropy( tmp + 2, 1 ); + if (sizeof(unsigned) == 4) + memcpy( auth, tmp, len ); + else { + int i; + for (i = 0; i < len; i += 8) + rnd[i / 8] = tmp[i / 4] | (tmp[i / 4 + 1] << 32); + } + } + return 1; +# endif +#endif +} + +#ifndef HAVE_ARC4RANDOM +int +secureRandom( void ) +{ + int rslt; + GenerateAuthData( (char *)&rslt, sizeof(int) ); + return rslt & 0x7fffffff; +} +#endif
\ No newline at end of file diff --git a/tdm/backend/greet.h b/tdm/backend/greet.h new file mode 100644 index 000000000..985edc29c --- /dev/null +++ b/tdm/backend/greet.h @@ -0,0 +1,278 @@ +/* + +Copyright 2001-2005 Oswald Buddenhagen <[email protected]> + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + * + * interface to xdm's external greeter and config reader + */ + +#ifndef GREET_H +#define GREET_H + +#include <config.h> + +#define DEBUG_CORE 0x01 +#define DEBUG_CONFIG 0x02 +#define DEBUG_GREET 0x04 +#define DEBUG_HLPCON 0x08 +#define DEBUG_WSESS 0x10 +#define DEBUG_WCONFIG 0x20 +#define DEBUG_WGREET 0x40 +#define DEBUG_NOSYSLOG 0x80 +#define DEBUG_AUTH 0x100 +#define DEBUG_VALGRIND 0x400 +#define DEBUG_STRACE 0x800 + +#ifndef TRUE +# define TRUE 1 +# define FALSE 0 +#endif + +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) +# define ATTR_UNUSED __attribute__((unused)) +# define ATTR_NORETURN __attribute__((noreturn)) +# define ATTR_PRINTFLIKE(fmt,var) __attribute__((format(printf,fmt,var))) +#else +# define ATTR_UNUSED +# define ATTR_NORETURN +# define ATTR_PRINTFLIKE(fmt,var) +#endif + +#define as(ar) ((int)(sizeof(ar)/sizeof(ar[0]))) + +#define __stringify(x) #x +#define stringify(x) __stringify(x) + +/* + * Exit codes for fork()ed session process, greeter, and config reader + */ +#define EX_NORMAL 30 /* do whatever seems appropriate */ +#define EX_REMANAGE_DPY 31 /* force remanage; same as EX_NORMAL, but cannot return to reserve mode immediately */ +#define EX_UNMANAGE_DPY 32 /* force deletion */ +#define EX_RESERVER_DPY 33 /* force server termination */ +#define EX_AL_RESERVER_DPY 34 /* reserver; maybe, auto-(re-)login */ +#define EX_OPENFAILED_DPY 35 /* XOpenDisplay failed, retry */ +#define EX_RESERVE 37 /* put in reserve mode */ +#ifdef XDMCP +#define EX_REMOTE 38 /* start -query-ing X-server */ +#define EX_MAX EX_REMOTE +#else +#define EX_MAX EX_RESERVE +#endif + +/* + * Command codes core -> greeter + */ +#define G_Greet 1 /* get login; bidi */ +#define G_ErrorGreet 2 /* print failed auto-login */ +#ifdef XDMCP +#define G_Choose 3 /* run chooser; bidi */ +# define G_Ch_AddHost 301 +# define G_Ch_ChangeHost 302 +# define G_Ch_RemoveHost 303 +# define G_Ch_BadHost 304 +# define G_Ch_Exit 305 +#endif +#define G_SessMan 4 /* start "session manager" */ +#define G_ConfShutdown 5 /* confirm forced shutdown */ +#define G_GreetTimed 6 /* get login; timed login permitted */ + +#ifdef XDMCP +#define G_Ch_Refresh 10 /* XXX change */ +#define G_Ch_RegisterHost 11 /* str name XXX change */ +#define G_Ch_DirectChoice 12 /* str name XXX change */ +#endif + +/* + * Status/command codes greeter -> core + */ +#define G_Ready 0 /* nop */ +#define G_Cancel 1 /* abort login, etc. */ + +#define G_DGreet 2 /* get login */ +#ifdef XDMCP +#define G_DChoose 3 /* run chooser */ +#endif + +#define G_Shutdown 101 /* 5*int, string; async */ +# define SHUT_REBOOT 1 /* how */ +# define SHUT_HALT 2 +# define SHUT_CONSOLE -1 /* pseudo-code */ +# define SHUT_SCHEDULE 0 /* when; config only */ +# define SHUT_TRYNOW 1 +# define SHUT_FORCENOW 2 +# define SHUT_CANCEL 0 /* force */ +# define SHUT_FORCEMY 1 +# define SHUT_FORCE 2 +# define SHUT_ASK 3 +# define TO_INF 0x7fffffff +#define G_SessionExit 102 /* int code; async */ +#define G_GetCfg 103 /* int what; int sts, <variable> */ +#define G_SetupDpy 104 /* ; int <syncer> */ +#define G_ReadDmrc 105 /* str user; int sts - curdmrc */ +#define G_GetDmrc 106 /* str key; str value - curdmrc */ +/*#define G_ResetDmrc 107*/ /* ; async - newdmrc */ +#define G_PutDmrc 108 /* str key, str value; async - newdmrc */ +#define G_Verify 109 /* str type; ..., int V_ret */ +#define G_VerifyRootOK 110 /* str type; ..., int V_ret */ +#define G_List 111 /* int flags; ?*(str,str,[int,]str,str,int), int 0 */ +# define lstRemote 1 +# define lstPassive 2 +# define lstTTY 4 +# define isSelf 1 +# define isTTY 2 +#define G_QueryShutdown 112 /* ; 5*int; string */ +#define G_Activate 113 /* int vt; async */ +#define G_ListBootOpts 114 /* ; int sts, [argv opts, int dflt, int cur] */ +# define BO_OK 0 +# define BO_NOMAN -1 +# define BO_NOENT -2 +# define BO_IO -3 +#define G_Console 116 /* ; async */ +#define G_AutoLogin 117 /* ; async */ + +/* + * Command codes core -> config reader + */ +#define GC_Files 1 /* get file list */ +#define GC_GetConf 2 /* get a config group */ +# define GC_gGlobal 1 /* get global config array */ +#ifdef XDMCP +# define GC_gXaccess 3 /* get Xaccess equivalent */ +#endif +# define GC_gDisplay 4 /* get per-display config array */ + +/* + * Error code core -> greeter + */ +#define GE_Ok 0 +#define GE_NoFkt 1 /* no such function (only for extensions!) */ +#define GE_Error 2 /* internal error, like OOM */ +/* for config reading */ +#define GE_NoEnt 10 /* no such config entry */ +#define GE_BadType 11 /* unknown config entry type */ +/* for dmrc reading */ +#define GE_NoUser 20 /* no such user */ +#define GE_NoFile 21 /* no such file */ +#define GE_Denied 22 /* permission denied */ + +/* + * Log levels. + * Used independently in core, greeter & config reader. + */ +#define DM_DEBUG 0 +#define DM_INFO 1 +#define DM_WARN 2 +#define DM_ERR 3 +#define DM_PANIC 4 + +/* + * Status codes from Verify + */ +/* terminal status codes */ +#define V_OK 0 +#define V_FAIL 10 /* whatever, already reported with V_MSG_* */ +#define V_AUTH 11 /* authentication failed */ +/* non-terminal status codes */ +#define V_MSG_INFO 110 /* info message attached */ +#define V_MSG_ERR 111 /* error message attached (null for generic) */ +#define V_PUT_USER 112 /* user name attached; only with pam & no user send */ +#define V_CHTOK 113 /* password expired; change now */ +#define V_CHTOK_AUTH 114 /* password expired; change now, but authenticate first */ +#define V_PRE_OK 115 /* authentication succeeded, continue with password change */ +/* queries */ +#define V_GET_TEXT 200 /* str prompt, int echo, int ndelay; str return, int tag */ +# define V_IS_SECRET 1 +# define V_IS_USER 2 +# define V_IS_PASSWORD 4 +# define V_IS_OLDPASSWORD 8 +# define V_IS_NEWPASSWORD 16 +#define V_GET_BINARY 201 /* array prompt, int ndelay; array return */ + +/* + * Config/Runtime data keys + */ +#define C_WHO_MASK 0x00ff0000 /* Non-zero for proprietary extensions (see manufacturer table [to be written]) */ +#define C_TYPE_MASK 0x0f000000 /* Type of the value */ +# define C_TYPE_INT 0x00000000 /* Integer */ +# define C_TYPE_STR 0x01000000 /* String */ +# define C_TYPE_ARGV 0x02000000 /* 0-terminated Array of Strings */ +# define C_TYPE_ARR 0x03000000 /* Array (only when XDCMP is enabled) */ +#define C_PRIVATE 0xf0000000 /* Private, don't make it visible to interfaces! */ + +/* display variables */ +#define C_isLocal (C_TYPE_INT | 0x200) +#define C_hasConsole (C_TYPE_INT | 0x201) +#define C_isAuthorized (C_TYPE_INT | 0x202) + +/** + ** for struct display + **/ + +#define d_location 1 +#define dLocal 1 /* server runs on local host */ +#define dForeign 0 /* server runs on remote host */ + +#define d_lifetime 6 +#define dPermanent 4 /* display restarted when session exits */ +#define dReserve 2 /* display not restarted when session exits */ +#define dTransient 0 /* display removed when session exits */ + +#ifdef XDMCP +#define d_origin 8 +#else +#define d_origin 0 /* clever, huh? :) */ +#endif +#define dFromXDMCP 8 /* started with XDMCP */ +#define dFromFile 0 /* started via entry in servers file */ + +#ifdef XDMCP +/** + ** for xdmcp acls + **/ + +/* + * flags in acl entries + */ +#define a_notAllowed 1 /* both direct and indirect */ +#define a_notBroadcast 2 /* only direct */ +#define a_useChooser 2 /* only indirect */ + +/* + * type of host entries + */ +#define HOST_ALIAS 0 +#define HOST_ADDRESS 1 +#define HOST_PATTERN 2 +#define HOST_BROADCAST 3 + +#endif + +#endif /* GREET_H */ diff --git a/tdm/backend/inifile.c b/tdm/backend/inifile.c new file mode 100644 index 000000000..b5426de75 --- /dev/null +++ b/tdm/backend/inifile.c @@ -0,0 +1,255 @@ +/* + +Copyright 2003 Oswald Buddenhagen <[email protected]> + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + * + * load, save and manipulate ini-style config files + */ + +#include "dm.h" +#include "dm_error.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> + +char * +iniLoad( const char *fname ) +{ + char *data; + int fd, len; + struct stat st; + + if ((fd = open( fname, O_RDONLY | O_NONBLOCK )) < 0) { + Debug( "cannot open ini-file %\"s: %m", fname ); + return 0; + } + if (fstat( fd, &st ) || !S_ISREG( st.st_mode )) { + LogWarn( "Ini-file %\"s is no regular file\n", fname ); + close( fd ); + return 0; + } + if (st.st_size >= 0x10000) { + LogWarn( "Ini-file %\"s is too big\n", fname ); + close( fd ); + return 0; + } + len = st.st_size; + if (!(data = Malloc( len + 2 ))) { + close( fd ); + return 0; + } + if (read( fd, data, len ) != len) { + Debug( "cannot read ini-file %\"s: %m", fname ); + free( data ); + close( fd ); + return 0; + } + close( fd ); + if (data[len - 1] != '\n') + data[len++] = '\n'; + data[len] = 0; + return data; +} + +int +iniSave( const char *data, const char *fname ) +{ + int fd, cnt, len; + + if ((fd = open( fname, O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK, 0600 )) < 0) { + Debug( "cannot create ini-file %\"s: %m", fname ); + return 0; + } + len = strlen( data ); + if ((cnt = write( fd, data, len )) == len) { + close( fd ); + return 1; + } + if (cnt == -1) + Debug( "cannot write ini-file %\"s: %m", fname ); + else + Debug( "cannot write ini-file %\"s: partial write", fname ); + close( fd ); + return 0; +} + +#define apparr(d,s,n) do { memcpy (d, s, n); d += n; } while(0) +#define appbyte(d,b) *d++ = b + +char * +iniEntry( char *data, const char *section, const char *key, const char *value ) +{ + char *p = data, *secinsert = 0, *pastinsert = 0, *cb, *ce, *ndata; + const char *t; + int insect = 0, ll, sl, kl, vl, len, nlen; + + if (p) { + while (*p) { + for (; *p == ' ' || *p == '\t'; p++); + if (*p == '\n') { + p++; + continue; + } + if (*p == '[') { + for (t = section; *++p == *t; t++); + insect = !*t && *p == ']'; + } else if (insect) { + for (t = key; *p == *t; t++, p++); + for (; *p == ' ' || *p == '\t'; p++); + if (!*t && *p == '=') { + for (p++; *p == ' ' || *p == '\t'; p++); + cb = p; + while (*p++ != '\n'); + ce = p; + if (value) { + ll = sl = kl = 0; + len = (ce - data) + strlen( ce ); + goto insert; + } else { + for (ce--; ce != cb && (*(ce - 1) == ' ' || *(ce - 1) == '\t'); ce--); + if (!StrNDup( &p, cb, ce - cb )) + return 0; + return p; + } + } + } + for (; *p != '\n'; p++); + p++; + if (insect) + secinsert = p; + else + pastinsert = p; + } + } + if (!value) + return 0; + len = p - data; + if (secinsert) { + ce = cb = secinsert; + sl = ll = 0; + } else { + sl = strlen( section ) + 3; + if (pastinsert) { + ce = cb = pastinsert; + ll = 1; + } else { + ce = cb = data; + ll = 0; + } + } + kl = strlen( key ) + 1; + insert: + vl = strlen( value ); + nlen = len - (ce - cb) + ll + sl + kl + vl + 1; + if (!(p = ndata = Malloc( nlen + 1 ))) + return data; + apparr( p, data, cb - data ); + if (kl) { + if (sl) { + if (ll) + appbyte( p, '\n' ); + appbyte( p, '[' ); + apparr( p, section, sl - 3 ); + appbyte( p, ']' ); + appbyte( p, '\n' ); + } + apparr( p, key, kl - 1 ); + appbyte( p, '=' ); + } + apparr( p, value, vl ); + appbyte( p, '\n' ); + if (data) { + apparr( p, ce, len - (ce - data) ); + free( data ); + } + appbyte( p, 0 ); + return ndata; +} + +char * +iniMerge( char *data, const char *newdata ) +{ + const char *p, *cb, *ce; + char *section = 0, *key, *value; + + if (!newdata) + return data; + for (p = newdata;;) { + for (; *p == ' ' || *p == '\t'; p++); + if (!*p) + break; + if (*p == '\n') { + p++; + continue; + } + if (*p == '#') { + for (p++; *p != '\n'; p++) + if (!*p) + goto bail; + p++; + continue; + } + if (*p == '[') { + cb = ++p; + for (; *p != ']'; p++) + if (!*p || *p == '\n') /* missing ] */ + goto bail; + if (!ReStrN( §ion, cb, p - cb )) + break; + p++; + } else { + cb = p; + for (; *p != '='; p++) + if (!*p || *p == '\n') /* missing = */ + goto bail; + for (ce = p; ce != cb && (*(ce - 1) == ' ' || *(ce - 1) == '\t'); ce--); + if (!StrNDup( &key, cb, ce - cb )) + break; + for (p++; *p == ' ' || *p == '\t'; p++); + cb = p; + for (; *p && *p != '\n'; p++); + for (ce = p; ce != cb && (*(ce - 1) == ' ' || *(ce - 1) == '\t'); ce--); + if (!StrNDup( &value, cb, ce - cb )) + break; + if (section) + data = iniEntry( data, section, key, value ); + free( value ); + free( key ); + } + } + bail: + if (section) + free( section ); + return data; +} diff --git a/tdm/backend/krb5auth.c b/tdm/backend/krb5auth.c new file mode 100644 index 000000000..16b640a35 --- /dev/null +++ b/tdm/backend/krb5auth.c @@ -0,0 +1,248 @@ +/* + +Copyright 1994, 1998 The Open Group +Copyright 2003 Oswald Buddenhagen <[email protected]> + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Stephen Gildea, The Open Group + * + * generate Kerberos Version 5 authorization records + */ + +#include <config.h> + +#ifdef K5AUTH + +#include "dm.h" +#include "dm_auth.h" +#include "dm_error.h" + +#include <sys/types.h> +#include <sys/stat.h> + +#include <krb5/krb5.h> + +static krb5_context ctx; + +/*ARGSUSED*/ +void +Krb5InitAuth( unsigned short name_len ATTR_UNUSED, const char *name ATTR_UNUSED ) +{ + if (krb5_init_context( &ctx )) + LogError( "Error while initializing Krb5 context\n" ); +} + +/* + * Returns malloc'ed string that is the credentials cache name. + * name should be freed by caller. + */ +static char * +Krb5CCacheName( const char *dname ) +{ + char *name; + const char *tmpdir; + int dnl, nl; + + tmpdir = getenv( "TMPDIR" ); + if (!tmpdir) + tmpdir = "/tmp"; + dnl = strlen( dname ); + name = Malloc( strlen( tmpdir ) + dnl + 20 ); + if (!name) + return NULL; + nl = sprintf( name, "FILE:%s/K5C", tmpdir ); + CleanUpFileName( dname, name + nl, dnl + 1 ); + return name; +} + +Xauth * +Krb5GetAuthFor( unsigned short namelen, const char *name, const char *dname ) +{ + Xauth *new; + char *filename; + + if (!(new = (Xauth *)Malloc( sizeof(*new) ))) + return (Xauth *)0; + new->family = FamilyWild; + new->address_length = 0; + new->address = 0; + new->number_length = 0; + new->number = 0; + + if (dname) { + if (!(filename = Krb5CCacheName( dname ))) { + free( (char *)new ); + return (Xauth *)0; + } + new->data = 0; + if (!StrApp( &new->data, "UU:", filename, (char *)0 )) { + free( filename ); + free( (char *)new ); + return (Xauth *)0; + } + free( filename ); + new->data_length = strlen( new->data ); + } else { + new->data = NULL; + new->data_length = 0; + } + + if (!(new->name = (char *)Malloc( namelen ))) { + free( (char *)new->data ); + free( (char *)new ); + return (Xauth *)0; + } + memmove( new->name, name, namelen ); + new->name_length = namelen; + return new; +} + + +Xauth * +Krb5GetAuth( unsigned short namelen, const char *name ) +{ + return Krb5GetAuthFor( namelen, name, NULL ); +} + + +static krb5_error_code +Krb5DisplayCCache( const char *dname, krb5_ccache *ccache_return, char **name ) +{ + char *ccname; + krb5_error_code code; + + if (!(ccname = Krb5CCacheName( dname ))) + return ENOMEM; + Debug( "resolving Kerberos cache %s\n", ccname ); + if ((code = krb5_cc_resolve( ctx, ccname, ccache_return )) || !name) + free( ccname ); + else + *name = ccname; + return code; +} + +char * +Krb5Init( const char *user, const char *passwd, const char *dname ) +{ + krb5_error_code code; + krb5_get_init_creds_opt options; + krb5_principal me; + krb5_creds my_creds; + krb5_ccache ccache; + char *ccname; + + if (!ctx) + return 0; + + if ((code = krb5_parse_name( ctx, user, &me ))) { + LogError( "%s while parsing Krb5 user %\"s\n", + error_message( code ), user ); + return 0; + } + + krb5_get_init_creds_opt_init( &options ); + /*krb5_get_init_creds_opt_set_tkt_life (&options, 60*60*8);*/ /* 8 hours */ + + if ((code = krb5_get_init_creds_password( ctx, &my_creds, + me, /* principal */ + (char * /* for MIT */) passwd, + 0, /* prompter */ + 0, /* prompter ctx */ + 0, /* start time delta */ + 0, /* service */ + &options ))) + { + char *my_name = NULL; + int code2 = krb5_unparse_name( ctx, me, &my_name ); + if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) + LogError( "Password incorrect for Krb5 principal %\"s\n", + code2 ? user : my_name ); + else + LogError( "%s while getting initial Krb5 credentials for %\"s\n", + error_message( code ), code2 ? user : my_name ); + if (my_name) + free( my_name ); + goto err3; + } + + if ((code = Krb5DisplayCCache( dname, &ccache, &ccname ))) { + LogError( "%s while getting Krb5 ccache for %\"s\n", + error_message( code ), dname ); + goto err2; + } + + if ((code = krb5_cc_initialize( ctx, ccache, me ))) { + LogError( "%s while initializing Krb5 cache %\"s\n", + error_message( code ), ccname ); + goto err1; + } + + if ((code = krb5_cc_store_cred( ctx, ccache, &my_creds ))) { + LogError( "%s while storing Krb5 credentials to cache %\"s\n", + error_message( code ), ccname ); + err1: + krb5_cc_close( ctx, ccache ); + free( ccname ); + err2: + krb5_free_cred_contents( ctx, &my_creds ); + err3: + krb5_free_principal( ctx, me ); + return 0; + } + + krb5_cc_close( ctx, ccache ); + krb5_free_cred_contents( ctx, &my_creds ); + krb5_free_principal( ctx, me ); + return ccname; +} + +void +Krb5Destroy( const char *dname ) +{ + krb5_error_code code; + krb5_ccache ccache; + + if (!ctx) + return; + + if ((code = Krb5DisplayCCache( dname, &ccache, 0 ))) + LogError( "%s while getting Krb5 ccache to destroy\n", + error_message( code ) ); + else { + if ((code = krb5_cc_destroy( ctx, ccache ))) { + if (code == KRB5_FCC_NOFILE) + Debug( "no Kerberos ccache file found to destroy\n" ); + else + LogError( "%s while destroying Krb5 credentials cache\n", + error_message( code ) ); + } else + Debug( "kerberos ccache destroyed\n" ); + } +} + +#endif diff --git a/tdm/backend/mitauth.c b/tdm/backend/mitauth.c new file mode 100644 index 000000000..fd18d41df --- /dev/null +++ b/tdm/backend/mitauth.c @@ -0,0 +1,87 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2003 Oswald Buddenhagen <[email protected]> + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + * + * generate authorization keys + * for MIT-MAGIC-COOKIE-1 type authorization + */ + +#include "dm.h" +#include "dm_auth.h" + +#define AUTH_DATA_LEN 16 /* bytes of authorization data */ +static char auth_name[256]; + +void +MitInitAuth( unsigned short name_len, const char *name ) +{ + if (name_len > 256) + name_len = 256; + memmove( auth_name, name, name_len ); +} + +Xauth * +MitGetAuth( unsigned short namelen, const char *name ) +{ + Xauth *new; + new = (Xauth *)Malloc( sizeof(Xauth) ); + + if (!new) + return (Xauth *)0; + new->family = FamilyWild; + new->address_length = 0; + new->address = 0; + new->number_length = 0; + new->number = 0; + + new->data = (char *)Malloc( AUTH_DATA_LEN ); + if (!new->data) { + free( (char *)new ); + return (Xauth *)0; + } + new->name = (char *)Malloc( namelen ); + if (!new->name) { + free( (char *)new->data ); + free( (char *)new ); + return (Xauth *)0; + } + memmove( (char *)new->name, name, namelen ); + new->name_length = namelen; + if (!GenerateAuthData( new->data, AUTH_DATA_LEN )) { + free( (char *)new->name ); + free( (char *)new->data ); + free( (char *)new ); + return (Xauth *)0; + } + new->data_length = AUTH_DATA_LEN; + return new; +} diff --git a/tdm/backend/netaddr.c b/tdm/backend/netaddr.c new file mode 100644 index 000000000..349a53528 --- /dev/null +++ b/tdm/backend/netaddr.c @@ -0,0 +1,219 @@ +/* + +Copyright 1991, 1998 The Open Group +Copyright 2002 Sun Microsystems, Inc. All rights reserved. + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + * + * netaddr.c - Interpretation of XdmcpNetaddr object. + */ + +#include "dm.h" +#include "dm_socket.h" +#include "dm_error.h" + +/* given an char *, returns the socket protocol family used, + e.g., AF_INET */ + +int +NetaddrFamily( char *netaddrp ) +{ +#ifdef STREAMSCONN + short family = *(short *)netaddrp; + return family; +#else + return ((struct sockaddr *)netaddrp)->sa_family; +#endif +} + + +/* given an char *, returns a pointer to the TCP/UDP port used + and sets *lenp to the length of the address + or 0 if not using TCP or UDP. */ + +char * +NetaddrPort( char *netaddrp, int *lenp ) +{ +#ifdef STREAMSCONN + *lenp = 2; + return netaddrp+2; +#else + switch (NetaddrFamily( netaddrp )) + { + case AF_INET: + *lenp = 2; + return (char *)&(((struct sockaddr_in *)netaddrp)->sin_port); +#if defined(IPv6) && defined(AF_INET6) + case AF_INET6: + *lenp = 2; + return (char *)&(((struct sockaddr_in6 *)netaddrp)->sin6_port); +#endif + default: + *lenp = 0; + return NULL; + } +#endif +} + + +/* given an char *, returns a pointer to the network address + and sets *lenp to the length of the address */ + +char * +NetaddrAddress( char *netaddrp, int *lenp ) +{ +#ifdef STREAMSCONN + *lenp = 4; + return netaddrp+4; +#else + switch (NetaddrFamily( netaddrp )) { +#ifdef UNIXCONN + case AF_UNIX: + *lenp = strlen( ((struct sockaddr_un *)netaddrp)->sun_path ); + return (char *)(((struct sockaddr_un *)netaddrp)->sun_path); +#endif +#ifdef TCPCONN + case AF_INET: + *lenp = sizeof(struct in_addr); + return (char *)&(((struct sockaddr_in *)netaddrp)->sin_addr); +#if defined(IPv6) && defined(AF_INET6) + case AF_INET6: + { + struct in6_addr *a = &(((struct sockaddr_in6 *)netaddrp)->sin6_addr); + if (IN6_IS_ADDR_V4MAPPED( a )) { + *lenp = sizeof(struct in_addr); + return ((char *)&(a->s6_addr))+12; + } else { + *lenp = sizeof(struct in6_addr); + return (char *)&(a->s6_addr); + } + } +#endif +#endif +#ifdef DNETCONN + case AF_DECnet: + *lenp = sizeof(struct dn_naddr); + return (char *)&(((struct sockaddr_dn *)netaddrp)->sdn_add); +#endif +#ifdef AF_CHAOS + case AF_CHAOS: +#endif + default: + *lenp = 0; + return NULL; + } +#endif /* STREAMSCONN else */ +} + + +/* given an char *, sets *addr to the network address used and + sets *len to the number of bytes in addr. + Returns the X protocol family used, e.g., FamilyInternet */ + +int +ConvertAddr( char *saddr, int *len, char **addr ) +{ + int retval; + + if (len == NULL) + return -1; + *addr = NetaddrAddress( saddr, len ); +#ifdef STREAMSCONN + /* kludge */ + if (NetaddrFamily( saddr ) == 2) + retval = FamilyInternet; +#else + switch (NetaddrFamily( saddr )) { +#ifdef AF_UNSPEC + case AF_UNSPEC: + retval = FamilyLocal; + break; +#endif +#ifdef AF_UNIX +#ifndef __hpux + case AF_UNIX: + retval = FamilyLocal; + break; +#endif +#endif +#ifdef TCPCONN + case AF_INET: + retval = FamilyInternet; + break; +#if defined(IPv6) && defined(AF_INET6) + case AF_INET6: + if (*len == sizeof(struct in_addr)) + retval = FamilyInternet; + else + retval = FamilyInternet6; + break; +#endif +#endif +#ifdef DNETCONN + case AF_DECnet: + retval = FamilyDECnet; + break; +#endif +#ifdef AF_CHAOS + case AF_CHAOS: + retval = FamilyChaos; + break; +#endif + default: + retval = -1; + break; + } +#endif /* STREAMSCONN else */ + Debug( "ConvertAddr returning %d for family %d\n", retval, + NetaddrFamily( saddr ) ); + return retval; +} + +#ifdef XDMCP +int +addressEqual( char *a1, int len1, char *a2, int len2 ) +{ + int partlen1, partlen2; + char *part1, *part2; + + if (len1 != len2) + return FALSE; + if (NetaddrFamily( a1 ) != NetaddrFamily( a2 )) + return FALSE; + part1 = NetaddrPort( a1, &partlen1 ); + part2 = NetaddrPort( a2, &partlen2 ); + if (partlen1 != partlen2 || memcmp( part1, part2, partlen1 ) != 0) + return FALSE; + part1 = NetaddrAddress( a1, &partlen1 ); + part2 = NetaddrAddress( a2, &partlen2 ); + if (partlen1 != partlen2 || memcmp( part1, part2, partlen1 ) != 0) + return FALSE; + return TRUE; +} +#endif diff --git a/tdm/backend/policy.c b/tdm/backend/policy.c new file mode 100644 index 000000000..cabee7088 --- /dev/null +++ b/tdm/backend/policy.c @@ -0,0 +1,278 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2001 Oswald Buddenhagen <[email protected]> + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + * + * policy.c. Implement site-dependent policy for XDMCP connections + */ + +#include <config.h> + +#ifdef XDMCP + +#include "dm.h" +#include "dm_auth.h" +#include "dm_socket.h" + +static ARRAY8 noAuthentication = { (CARD16)0, (CARD8Ptr) 0 }; + +typedef struct _XdmAuth { + ARRAY8 authentication; + ARRAY8 authorization; +} XdmAuthRec, *XdmAuthPtr; + +static XdmAuthRec auth[] = { +#ifdef HASXDMAUTH +{ {(CARD16)20, (CARD8 *)"XDM-AUTHENTICATION-1"}, + {(CARD16)19, (CARD8 *)"XDM-AUTHORIZATION-1"}, +}, +#endif +{ {(CARD16)0, (CARD8 *)0}, + {(CARD16)0, (CARD8 *)0}, +} +}; + +#define NumAuth as(auth) + +ARRAY8Ptr +ChooseAuthentication( ARRAYofARRAY8Ptr authenticationNames ) +{ + int i, j; + + for (i = 0; i < (int)authenticationNames->length; i++) + for (j = 0; j < NumAuth; j++) + if (XdmcpARRAY8Equal( &authenticationNames->data[i], + &auth[j].authentication )) + return &authenticationNames->data[i]; + return &noAuthentication; +} + +int +CheckAuthentication( + struct protoDisplay *pdpy ATTR_UNUSED, + ARRAY8Ptr displayID ATTR_UNUSED, + ARRAY8Ptr name ATTR_UNUSED, + ARRAY8Ptr data ATTR_UNUSED ) +{ +#ifdef HASXDMAUTH + if (name->length && !memcmp( (char *)name->data, "XDM-AUTHENTICATION-1", 20 )) + return XdmCheckAuthentication( pdpy, displayID, name, data ); +#endif + return TRUE; +} + +int +SelectAuthorizationTypeIndex( ARRAY8Ptr authenticationName, + ARRAYofARRAY8Ptr authorizationNames ) +{ + int i, j; + + for (j = 0; j < NumAuth; j++) + if (XdmcpARRAY8Equal( authenticationName, + &auth[j].authentication )) + break; + if (j < NumAuth) + for (i = 0; i < (int)authorizationNames->length; i++) + if (XdmcpARRAY8Equal( &authorizationNames->data[i], + &auth[j].authorization )) + return i; + for (i = 0; i < (int)authorizationNames->length; i++) + if (ValidAuthorization( authorizationNames->data[i].length, + (char *)authorizationNames->data[i].data )) + return i; + return -1; +} + + +/*#define WILLING_INTERNAL*/ + +#ifdef WILLING_INTERNAL +/* Report the loadavg to chooser. Nice feature ... + * + * Wed Mar 10 1999 -- Steffen Hansen + */ +static void +Willing_msg( char *mbuf ) +{ +#ifdef __linux__ + int fd; + int numcpu; + const char *fail_msg = "Willing to manage"; + FILE *f; + float load[3]; + float mhz = 0.0; + char buf[1024]; + + fd = open( "/proc/loadavg", O_RDONLY ); + if (fd == -1) { + sprintf( mbuf, fail_msg ); + return; + } else if (read( fd, buf, 100 ) < 4) { + close( fd ); + sprintf( mbuf, fail_msg ); + return; + } + close( fd ); + + sscanf( buf, "%f %f %f", &load[0], &load[1], &load[2] ); + sprintf( mbuf, "Available (load: %0.2f, %0.2f, %0.2f)", + load[0], load[1], load[2] ); + + numcpu = 0; + + if (!(f = fopen( "/proc/cpuinfo", "r" ))) + return; + + while (fGets( buf, sizeof(buf), f ) != -1) { + float m; + if (sscanf( buf, "cpu MHz : %f", &m )) { + numcpu++; + mhz = m; + } + } + + fclose( f ); + + if (numcpu) { + if (numcpu > 1) + sprintf( buf, " %d*%0.0f MHz", numcpu, mhz ); + else + sprintf( buf, " %0.0f MHz", mhz ); + + strncat( mbuf, buf, 256 ); + + mbuf[255] = 0; + } +#elif HAVE_GETLOADAVG /* !__linux__ */ +#ifdef __GNUC__ +# warning This code is untested... +#endif + double load[3]; + getloadavg( load, 3 ); + sprintf( mbuf, "Available (load: %0.2f, %0.2f, %0.2f)", load[0], + load[1], load[2] ); +#else /* !__linux__ && !GETLOADAVG */ + strcpy( mbuf, "Willing to manage" ); +#endif +} +#endif + +/*ARGSUSED*/ +int +Willing( ARRAY8Ptr addr, CARD16 connectionType, + ARRAY8Ptr authenticationName ATTR_UNUSED, + ARRAY8Ptr status, xdmOpCode type ) +{ + int ret; + char statusBuf[256]; + static time_t lastscan; + + if (autoRescan && lastscan + 15 < now) { + lastscan = now; + ScanAccessDatabase( FALSE ); + } + ret = AcceptableDisplayAddress( addr, connectionType, type ); + if (!ret) + sprintf( statusBuf, "Display not authorized to connect" ); + else { + if (*willing) { + FILE *fd; + int len, ok = 0; + if ((fd = popen( willing, "r" ))) { + for (;;) { + if ((len = fGets( statusBuf, sizeof(statusBuf), fd )) != -1) { + if (len) { + ok = 1; + break; + } + } + if (feof( fd ) || errno != EINTR) + break; + } + pclose( fd ); + } + if (!ok) + sprintf( statusBuf, "Willing, but %.*s failed", + sizeof(statusBuf) - 21, willing ); + } else +#ifdef WILLING_INTERNAL + Willing_msg( statusBuf ); +#else + strcpy( statusBuf, "Willing to manage" ); +#endif + } + status->length = strlen( statusBuf ); + status->data = (CARD8Ptr) Malloc( status->length ); + if (!status->data) + status->length = 0; + else + memmove( status->data, statusBuf, status->length ); + return ret; +} + +/*ARGSUSED*/ +ARRAY8Ptr +Accept( struct sockaddr *from ATTR_UNUSED, int fromlen ATTR_UNUSED, + CARD16 displayNumber ATTR_UNUSED ) +{ + return 0; +} + +/*ARGSUSED*/ +int +SelectConnectionTypeIndex( ARRAY16Ptr connectionTypes, + ARRAYofARRAY8Ptr connectionAddresses ATTR_UNUSED ) +{ + int i; + + /* + * Select one supported connection type + */ + + for (i = 0; i < connectionTypes->length; i++) { + switch (connectionTypes->data[i]) { + case FamilyLocal: +#if defined(TCPCONN) + case FamilyInternet: +# if defined(IPv6) && defined(AF_INET6) + case FamilyInternet6: +# endif /* IPv6 */ +#endif /* TCPCONN */ +#if defined(DNETCONN) + case FamilyDECnet: +#endif /* DNETCONN */ + return i; + } + } /* for */ + return -1; +} + +#endif /* XDMCP */ diff --git a/tdm/backend/printf.c b/tdm/backend/printf.c new file mode 100644 index 000000000..d7220642b --- /dev/null +++ b/tdm/backend/printf.c @@ -0,0 +1,872 @@ +/* + +Copyright 2001,2002,2004 Oswald Buddenhagen <[email protected]> + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + * + * printf.c - working horse of error.c + */ + +/* + * NOTE: this file is meant to be included, not linked, + * so it can be used in the helper programs without much voodoo. + */ + +/* ########## printf core implementation with some extensions ########## */ +/* + * How to use the extensions: + * - put ' or " in the flags field to quote a string with this char and + * escape special characters (only available, if PRINT_QUOTES is defined) + * - put \\ in the flags field to quote special characters and leading and + * trailing spaces (only available, if PRINT_QUOTES is defined) + * - arrays (only available, if PRINT_ARRAYS is defined) + * - the array modifier [ comes after the maximal field width specifier + * - the array length can be specified literally, with the '*' modifier + * (in which case an argument is expected) or will be automatically + * determined (stop values are -1 for ints and 0 for strings) + * - these modifiers expect their argument to be an in-line string quoted + * with an arbitrary character: + * - (, ) -> array pre-/suf-fix; default "" + * - <, > -> element pre-/suf-fix; default "" + * - | -> element separator; default " " + * - these modifiers expect no argument: + * - : -> print '<number of elements>: ' before an array + * - , -> short for |',' + * - { -> short for ('{')' }'<' '|'' + * - the pointer to the array is the last argument to the format + * - the %m conversion from syslog() is supported + */ + +/************************************************************** + * Partially stolen from OpenSSH's OpenBSD compat directory. + * (C) Patrick Powell, Brandon Long, Thomas Roessler, + * Michael Elkins, Ben Lindstrom + **************************************************************/ + +#include <ctype.h> +#include <string.h> +#include <stdarg.h> + +/* format flags - Bits */ +#define DP_F_MINUS (1 << 0) +#define DP_F_PLUS (1 << 1) +#define DP_F_SPACE (1 << 2) +#define DP_F_NUM (1 << 3) +#define DP_F_ZERO (1 << 4) +#define DP_F_UPCASE (1 << 5) +#define DP_F_UNSIGNED (1 << 6) +#define DP_F_SQUOTE (1 << 7) +#define DP_F_DQUOTE (1 << 8) +#define DP_F_BACKSL (1 << 9) +#define DP_F_ARRAY (1 << 10) +#define DP_F_COLON (1 << 11) + +/* Conversion Flags */ +#define DP_C_INT 0 +#define DP_C_BYTE 1 +#define DP_C_SHORT 2 +#define DP_C_LONG 3 +#define DP_C_STR 10 + +typedef void (*OutCh)( void *bp, char c ); + + +static void +fmtint( OutCh dopr_outch, void *bp, + long value, int base, int min, int max, int flags ) +{ + const char *ctab; + unsigned long uvalue; + int signvalue = 0; + int place = 0; + int spadlen = 0; /* amount to space pad */ + int zpadlen = 0; /* amount to zero pad */ + char convert[20]; + + if (max < 0) + max = 0; + + uvalue = value; + + if (!(flags & DP_F_UNSIGNED)) { + if (value < 0) { + signvalue = '-'; + uvalue = -value; + } else if (flags & DP_F_PLUS) /* Do a sign (+/i) */ + signvalue = '+'; + else if (flags & DP_F_SPACE) + signvalue = ' '; + } + + ctab = (flags & DP_F_UPCASE) ? "0123456789ABCDEF" : "0123456789abcdef"; + do { + convert[place++] = ctab[uvalue % (unsigned)base]; + uvalue = uvalue / (unsigned)base; + } while (uvalue); + + zpadlen = max - place; + spadlen = min - (max > place ? max : place) - + (signvalue ? 1 : 0) - ((flags & DP_F_NUM) ? 2 : 0); + if (zpadlen < 0) + zpadlen = 0; + if (spadlen < 0) + spadlen = 0; + if (flags & DP_F_ZERO) { + zpadlen = zpadlen > spadlen ? zpadlen : spadlen; + spadlen = 0; + } + if (flags & DP_F_MINUS) + spadlen = -spadlen; /* Left Justifty */ + + + /* Spaces */ + while (spadlen > 0) { + dopr_outch( bp, ' ' ); + --spadlen; + } + + /* Sign */ + if (signvalue) + dopr_outch( bp, signvalue ); + + /* Prefix */ + if (flags & DP_F_NUM) { + dopr_outch( bp, '0' ); + dopr_outch( bp, 'x' ); + } + + /* Zeros */ + if (zpadlen > 0) + while (zpadlen > 0) { + dopr_outch( bp, '0' ); + --zpadlen; + } + + /* Digits */ + while (place > 0) + dopr_outch( bp, convert[--place] ); + + /* Left Justified spaces */ + while (spadlen < 0) { + dopr_outch( bp, ' ' ); + ++spadlen; + } +} + +typedef struct { + const char *str; + size_t len; +} str_t; + +static void +putstr( OutCh dopr_outch, void *bp, str_t *st ) +{ + size_t pt; + + for (pt = 0; pt < st->len; pt++) + dopr_outch( bp, st->str[pt] ); +} + +static str_t _null_parents = { "(null)", 6 }; +#ifdef PRINT_ARRAYS +static str_t _null_dparents = { "((null))", 8 }; +#endif +#if defined(PRINT_QUOTES) || defined(PRINT_ARRAYS) +static str_t _null_caps = { "NULL", 4 }; +#endif + +static void +fmtstr( OutCh dopr_outch, void *bp, + const char *value, int flags, int min, int max ) +{ + int padlen, strln, curcol; +#ifdef PRINT_QUOTES + int lastcol; +#endif + char ch; + + if (!value) { +#ifdef PRINT_QUOTES + if (flags & (DP_F_SQUOTE | DP_F_DQUOTE)) + putstr( dopr_outch, bp, &_null_caps ); + else +#endif + putstr( dopr_outch, bp, &_null_parents ); + return; + } + + for (strln = 0; (unsigned)strln < (unsigned)max && value[strln]; strln++); + padlen = min - strln; + if (padlen < 0) + padlen = 0; + if (flags & DP_F_MINUS) + padlen = -padlen; /* Left Justify */ + + for (; padlen > 0; padlen--) + dopr_outch( bp, ' ' ); +#ifdef PRINT_QUOTES +# if 0 /* gcc's flow analyzer is not the smartest ... */ + lastcol = 0; +# endif + if (flags & DP_F_SQUOTE) + dopr_outch( bp, '\'' ); + else if (flags & DP_F_DQUOTE) + dopr_outch( bp, '"'); + else if (flags & DP_F_BACKSL) + for (lastcol = strln; lastcol && value[lastcol - 1] == ' '; lastcol--); +#endif + for (curcol = 0; curcol < strln; curcol++) { + ch = value[curcol]; +#ifdef PRINT_QUOTES + if (flags & (DP_F_SQUOTE | DP_F_DQUOTE | DP_F_BACKSL)) { + switch (ch) { + case '\r': ch = 'r'; break; + case '\n': ch = 'n'; break; + case '\t': ch = 't'; break; + case '\a': ch = 'a'; break; + case '\b': ch = 'b'; break; + case '\v': ch = 'v'; break; + case '\f': ch = 'f'; break; + default: + if (ch < 32 || + ((unsigned char)ch >= 0x7f && (unsigned char)ch < 0xa0)) + { + dopr_outch( bp, '\\' ); + fmtint( dopr_outch, bp, (unsigned char)ch, 8, 3, 3, DP_F_ZERO ); + continue; + } else { + if ((ch == '\'' && (flags & DP_F_SQUOTE)) || + (ch == '"' && (flags & DP_F_DQUOTE) ) || + (ch == ' ' && (flags & DP_F_BACKSL) && + (!curcol || curcol >= lastcol)) || + ch == '\\') + dopr_outch( bp, '\\' ); + dopr_outch( bp, ch ); + continue; + } + } + dopr_outch( bp, '\\' ); + } +#endif + dopr_outch( bp, ch ); + } +#ifdef PRINT_QUOTES + if (flags & DP_F_SQUOTE) + dopr_outch( bp, '\'' ); + else if (flags & DP_F_DQUOTE) + dopr_outch( bp, '"' ); +#endif + for (; padlen < 0; padlen++) + dopr_outch( bp, ' ' ); +} + +static void +DoPr( OutCh dopr_outch, void *bp, const char *format, va_list args ) +{ + const char *strvalue; +#ifdef PRINT_ARRAYS + str_t arpr, arsf, arepr, aresf, aresp, *arp; + void *arptr; +#endif + unsigned long value; + int radix, min, max, flags, cflags, errn; +#ifdef PRINT_ARRAYS + int arlen; + unsigned aridx; + char sch; +#endif + char ch; +#define NCHR if (!(ch = *format++)) return + +#if 0 /* gcc's flow analyzer is not the smartest ... */ +# ifdef PRINT_ARRAYS + arlen = 0; +# endif + radix = 0; +#endif + errn = errno; + for (;;) { + for (;;) { + NCHR; + if (ch == '%') + break; + dopr_outch (bp, ch); + } + flags = cflags = min = 0; + max = -1; + for (;;) { + NCHR; + switch (ch) { + case '#': flags |= DP_F_NUM; continue; + case '-': flags |= DP_F_MINUS; continue; + case '+': flags |= DP_F_PLUS; continue; + case ' ': flags |= DP_F_SPACE; continue; + case '0': flags |= DP_F_ZERO; continue; +#ifdef PRINT_QUOTES + case '"': flags |= DP_F_DQUOTE; continue; + case '\'': flags |= DP_F_SQUOTE; continue; + case '\\': flags |= DP_F_BACKSL; continue; +#endif + } + break; + } + for (;;) { + if (isdigit( (unsigned char)ch )) { + min = 10 * min + (ch - '0'); + NCHR; + continue; + } else if (ch == '*') { + min = va_arg( args, int ); + NCHR; + } + break; + } + if (ch == '.') { + max = 0; + for (;;) { + NCHR; + if (isdigit( (unsigned char)ch )) { + max = 10 * max + (ch - '0'); + continue; + } else if (ch == '*') { + max = va_arg( args, int ); + NCHR; + } + break; + } + } +#ifdef PRINT_ARRAYS + if (ch == '[') { + flags |= DP_F_ARRAY; + arlen = -1; + arpr.len = arsf.len = arepr.len = aresf.len = 0; + aresp.len = 1, aresp.str = " "; + for (;;) { + NCHR; + if (isdigit( (unsigned char)ch )) { + arlen = 0; + for (;;) { + arlen += (ch - '0'); + NCHR; + if (!isdigit( (unsigned char)ch )) + break; + arlen *= 10; + } + } + switch (ch) { + case ':': flags |= DP_F_COLON; continue; + case '*': arlen = va_arg( args, int ); continue; + case '(': arp = &arpr; goto rar; + case ')': arp = &arsf; goto rar; + case '<': arp = &arepr; goto rar; + case '>': arp = &aresf; goto rar; + case '|': arp = &aresp; + rar: + NCHR; + sch = ch; + arp->str = format; + do { + NCHR; + } while (ch != sch); + arp->len = format - arp->str - 1; + continue; + case ',': + aresp.len = 1, aresp.str = ","; + continue; + case '{': + aresp.len = 0, arpr.len = arepr.len = 1, arsf.len = 2; + arpr.str = "{", arepr.str = " ", arsf.str = " }"; + continue; + } + break; + } + } +#endif + for (;;) { + switch (ch) { + case 'h': + cflags = DP_C_SHORT; + NCHR; + if (ch == 'h') { + cflags = DP_C_BYTE; + NCHR; + } + continue; + case 'l': + cflags = DP_C_LONG; + NCHR; + continue; + } + break; + } + switch (ch) { + case '%': + dopr_outch( bp, ch ); + break; + case 'm': + fmtstr( dopr_outch, bp, strerror( errn ), flags, min, max ); + break; + case 'c': + dopr_outch( bp, va_arg( args, int ) ); + break; + case 's': +#ifdef PRINT_ARRAYS + cflags = DP_C_STR; + goto printit; +#else + strvalue = va_arg( args, char * ); + fmtstr( dopr_outch, bp, strvalue, flags, min, max ); + break; +#endif + case 'u': + flags |= DP_F_UNSIGNED; + case 'd': + case 'i': + radix = 10; + goto printit; + case 'X': + flags |= DP_F_UPCASE; + case 'x': + flags |= DP_F_UNSIGNED; + radix = 16; + printit: +#ifdef PRINT_ARRAYS + if (flags & DP_F_ARRAY) { + if (!(arptr = va_arg( args, void * ))) + putstr( dopr_outch, bp, + arpr.len ? &_null_caps : &_null_dparents ); + else { + if (arlen == -1) { + arlen = 0; + switch (cflags) { + case DP_C_STR: while (((char **)arptr)[arlen]) arlen++; break; + case DP_C_BYTE: while (((unsigned char *)arptr)[arlen] != (unsigned char)-1) arlen++; break; + case DP_C_SHORT: while (((unsigned short int *)arptr)[arlen] != (unsigned short int)-1) arlen++; break; + case DP_C_LONG: while (((unsigned long int *)arptr)[arlen] != (unsigned long int)-1) arlen++; break; + default: while (((unsigned int *)arptr)[arlen] != (unsigned int)-1) arlen++; break; + } + } + if (flags & DP_F_COLON) { + fmtint( dopr_outch, bp, (long)arlen, 10, 0, -1, DP_F_UNSIGNED ); + dopr_outch( bp, ':' ); + dopr_outch( bp, ' ' ); + } + putstr( dopr_outch, bp, &arpr ); + for (aridx = 0; aridx < (unsigned)arlen; aridx++) { + if (aridx) + putstr( dopr_outch, bp, &aresp ); + putstr( dopr_outch, bp, &arepr ); + if (cflags == DP_C_STR) { + strvalue = ((char **)arptr)[aridx]; + fmtstr( dopr_outch, bp, strvalue, flags, min, max ); + } else { + if (flags & DP_F_UNSIGNED) { + switch (cflags) { + case DP_C_BYTE: value = ((unsigned char *)arptr)[aridx]; break; + case DP_C_SHORT: value = ((unsigned short int *)arptr)[aridx]; break; + case DP_C_LONG: value = ((unsigned long int *)arptr)[aridx]; break; + default: value = ((unsigned int *)arptr)[aridx]; break; + } + } else { + switch (cflags) { + case DP_C_BYTE: value = ((signed char *)arptr)[aridx]; break; + case DP_C_SHORT: value = ((short int *)arptr)[aridx]; break; + case DP_C_LONG: value = ((long int *)arptr)[aridx]; break; + default: value = ((int *)arptr)[aridx]; break; + } + } + fmtint( dopr_outch, bp, value, radix, min, max, flags ); + } + putstr( dopr_outch, bp, &aresf ); + } + putstr( dopr_outch, bp, &arsf ); + } + } else { + if (cflags == DP_C_STR) { + strvalue = va_arg( args, char * ); + fmtstr( dopr_outch, bp, strvalue, flags, min, max ); + } else { +#endif + if (flags & DP_F_UNSIGNED) { + switch (cflags) { + case DP_C_LONG: value = va_arg( args, unsigned long int ); break; + default: value = va_arg( args, unsigned int ); break; + } + } else { + switch (cflags) { + case DP_C_LONG: value = va_arg( args, long int ); break; + default: value = va_arg( args, int ); break; + } + } + fmtint( dopr_outch, bp, value, radix, min, max, flags ); +#ifdef PRINT_ARRAYS + } + } +#endif + break; + case 'p': + value = (long)va_arg( args, void * ); + fmtint( dopr_outch, bp, value, 16, sizeof(long) * 2 + 2, + max, flags | DP_F_UNSIGNED | DP_F_ZERO | DP_F_NUM ); + break; + } + } +} + +/* ########## end of printf core implementation ########## */ + + +/* + * Logging function for xdm and helper programs. + */ +#ifndef NO_LOGGER + +#include <stdio.h> +#include <time.h> + +#ifdef USE_SYSLOG +# include <syslog.h> +# ifdef LOG_NAME +# define InitLog() openlog(LOG_NAME, LOG_PID, LOG_DAEMON) +# else +# define InitLog() openlog(prog, LOG_PID, LOG_DAEMON) +# endif +static int lognums[] = { LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERR, LOG_CRIT }; +#else +# define InitLog() while(0) +#endif + +static const char *lognams[] = { "debug", "info", "warning", "error", "panic" }; + +static void +logTime( char *dbuf ) +{ + time_t tim; + (void)time( &tim ); + strftime( dbuf, 20, "%b %e %H:%M:%S", localtime( &tim ) ); +} + +#if defined(LOG_DEBUG_MASK) || defined(USE_SYSLOG) +STATIC int debugLevel; +#endif + +#define OOMSTR "Out of memory. Expect problems.\n" + +STATIC void +LogOutOfMem( void ) +{ + static time_t last; + time_t tnow; + + time( &tnow ); + if (last + 100 > tnow) { /* don't log bursts */ + last = tnow; + return; + } + last = tnow; +#ifdef USE_SYSLOG + if (!(debugLevel & DEBUG_NOSYSLOG)) + syslog( LOG_CRIT, OOMSTR ); + else +#endif + { + int el; + char dbuf[24], sbuf[128]; + logTime( dbuf ); + el = sprintf( sbuf, "%s " +#ifdef LOG_NAME + LOG_NAME "[%ld]: " OOMSTR, dbuf, +#else + "%s[%ld]: " OOMSTR, dbuf, prog, +#endif + (long)getpid() ); + write( 2, sbuf, el ); + } +} + +typedef struct { + char *buf; + int clen, blen, type; + char lmbuf[128]; +} OCLBuf; + +static void +OutChLFlush( OCLBuf *oclbp ) +{ + if (oclbp->clen) { +#ifdef USE_SYSLOG + if (!(debugLevel & DEBUG_NOSYSLOG)) + syslog( lognums[oclbp->type], "%.*s", oclbp->clen, oclbp->buf ); + else +#endif + { + oclbp->buf[oclbp->clen] = '\n'; + write( 2, oclbp->buf, oclbp->clen + 1 ); + } + oclbp->clen = 0; + } +} + +static void +OutChL( void *bp, char c ) +{ + OCLBuf *oclbp = (OCLBuf *)bp; + char *nbuf; + int nlen; + + if (c == '\n') + OutChLFlush( oclbp ); + else { + if (oclbp->clen >= oclbp->blen - 1) { + if (oclbp->buf == oclbp->lmbuf) { + OutChLFlush( oclbp ); + oclbp->buf = 0; + oclbp->blen = 0; + } + nlen = oclbp->blen * 3 / 2 + 128; + nbuf = Realloc( oclbp->buf, nlen ); + if (nbuf) { + oclbp->buf = nbuf; + oclbp->blen = nlen; + } else { + OutChLFlush( oclbp ); + oclbp->buf = oclbp->lmbuf; + oclbp->blen = sizeof(oclbp->lmbuf); + } + } +#ifdef USE_SYSLOG + if (!oclbp->clen && (debugLevel & DEBUG_NOSYSLOG)) { +#else + if (!oclbp->clen) { +#endif + char dbuf[24]; + logTime( dbuf ); + oclbp->clen = sprintf( oclbp->buf, "%s " +#ifdef LOG_NAME + LOG_NAME "[%ld] %s: ", dbuf, +#else + "%s[%ld] %s: ", dbuf, prog, +#endif + (long)getpid(), lognams[oclbp->type] ); + } + oclbp->buf[oclbp->clen++] = c; + } +} + +static void +Logger( int type, const char *fmt, va_list args ) +{ + OCLBuf oclb; + + oclb.buf = 0; + oclb.blen = oclb.clen = 0; + oclb.type = type; + DoPr( OutChL, &oclb, fmt, args ); + /* no flush, every message is supposed to be \n-terminated */ + if (oclb.buf && oclb.buf != oclb.lmbuf) + free( oclb.buf ); +} + +#ifdef LOG_DEBUG_MASK +STATIC void +Debug( const char *fmt, ... ) +{ + if (debugLevel & LOG_DEBUG_MASK) { + va_list args; + int olderrno = errno; + va_start( args, fmt ); + Logger( DM_DEBUG, fmt, args ); + va_end( args ); + errno = olderrno; + } +} +#endif + +#ifndef LOG_NO_INFO +STATIC void +LogInfo( const char *fmt, ... ) +{ + va_list args; + + va_start( args, fmt ); + Logger( DM_INFO, fmt, args ); + va_end( args ); +} +#endif + +#ifndef LOG_NO_WARN +STATIC void +LogWarn( const char *fmt, ... ) +{ + va_list args; + + va_start( args, fmt ); + Logger( DM_WARN, fmt, args ); + va_end( args ); +} +#endif + +#ifndef LOG_NO_ERROR +STATIC void +LogError( const char *fmt, ... ) +{ + va_list args; + + va_start( args, fmt ); + Logger( DM_ERR, fmt, args ); + va_end( args ); +} +#endif + +#ifdef LOG_PANIC_EXIT +STATIC void +LogPanic( const char *fmt, ... ) +{ + va_list args; + + va_start( args, fmt ); + Logger( DM_PANIC, fmt, args ); + va_end( args ); + exit( LOG_PANIC_EXIT ); +} +#endif + +#endif /* NO_LOGGER */ + +#ifdef NEED_FDPRINTF + +typedef struct { + char *buf; + int clen, blen, tlen; +} OCFBuf; + +static void +OutCh_OCF( void *bp, char c ) +{ + OCFBuf *ocfbp = (OCFBuf *)bp; + char *nbuf; + int nlen; + + ocfbp->tlen++; + if (ocfbp->clen >= ocfbp->blen) { + if (ocfbp->blen < 0) + return; + nlen = ocfbp->blen * 3 / 2 + 100; + nbuf = Realloc( ocfbp->buf, nlen ); + if (!nbuf) { + free( ocfbp->buf ); + ocfbp->blen = -1; + ocfbp->buf = 0; + ocfbp->clen = 0; + return; + } + ocfbp->blen = nlen; + ocfbp->buf = nbuf; + } + ocfbp->buf[ocfbp->clen++] = c; +} + +STATIC int +FdPrintf( int fd, const char *fmt, ... ) +{ + va_list args; + OCFBuf ocfb = { 0, 0, 0, -1 }; + + va_start( args, fmt ); + DoPr( OutCh_OCF, &ocfb, fmt, args ); + va_end( args ); + if (ocfb.buf) { + Debug( "FdPrintf %\".*s to %d\n", ocfb.clen, ocfb.buf, fd ); + (void)write( fd, ocfb.buf, ocfb.clen ); + free( ocfb.buf ); + } + return ocfb.tlen; +} + +#endif /* NEED_FDPRINTF */ + +#ifdef NEED_ASPRINTF + +typedef struct { + char *buf; + int clen, blen, tlen; +} OCABuf; + +static void +OutCh_OCA( void *bp, char c ) +{ + OCABuf *ocabp = (OCABuf *)bp; + char *nbuf; + int nlen; + + ocabp->tlen++; + if (ocabp->clen >= ocabp->blen) { + if (ocabp->blen < 0) + return; + nlen = ocabp->blen * 3 / 2 + 100; + nbuf = Realloc( ocabp->buf, nlen ); + if (!nbuf) { + free( ocabp->buf ); + ocabp->blen = -1; + ocabp->buf = 0; + ocabp->clen = 0; + return; + } + ocabp->blen = nlen; + ocabp->buf = nbuf; + } + ocabp->buf[ocabp->clen++] = c; +} + +STATIC int +VASPrintf( char **strp, const char *fmt, va_list args ) +{ + OCABuf ocab = { 0, 0, 0, -1 }; + + DoPr( OutCh_OCA, &ocab, fmt, args ); + OutCh_OCA( &ocab, 0 ); + *strp = Realloc( ocab.buf, ocab.clen ); + if (!*strp) + *strp = ocab.buf; + return ocab.tlen; +} + +STATIC int +ASPrintf( char **strp, const char *fmt, ... ) +{ + va_list args; + int len; + + va_start( args, fmt ); + len = VASPrintf( strp, fmt, args ); + va_end( args ); + return len; +} + +#endif /* NEED_ASPRINTF */ diff --git a/tdm/backend/process.c b/tdm/backend/process.c new file mode 100644 index 000000000..f9d34fe7f --- /dev/null +++ b/tdm/backend/process.c @@ -0,0 +1,762 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2001-2004 Oswald Buddenhagen <[email protected]> + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + * + * subdaemon and external process management and communication + */ + +#include "dm.h" +#include "dm_error.h" + +#include <ctype.h> +#include <stdio.h> +#include <stdarg.h> +#include <signal.h> +#include <unistd.h> +#ifdef _POSIX_PRIORITY_SCHEDULING +# include <sched.h> +#endif + +extern char **environ; + + +SIGFUNC Signal( int sig, SIGFUNC handler ) +{ +#ifndef __EMX__ + struct sigaction sigact, osigact; + sigact.sa_handler = handler; + sigemptyset( &sigact.sa_mask ); +# ifdef SA_RESTART + sigact.sa_flags = SA_RESTART; +# else + sigact.sa_flags = 0; +# endif + sigaction( sig, &sigact, &osigact ); + return osigact.sa_handler; +#else + return signal( sig, handler ); +#endif +} + + +void +TerminateProcess( int pid, int sig ) +{ + kill( pid, sig ); +#ifdef SIGCONT + kill( pid, SIGCONT ); +#endif +} + + +static FD_TYPE CloseMask; +static int max = -1; + +void +RegisterCloseOnFork( int fd ) +{ + FD_SET( fd, &CloseMask ); + if (fd > max) + max = fd; +} + +void +ClearCloseOnFork( int fd ) +{ + FD_CLR( fd, &CloseMask ); +} + +void +CloseNClearCloseOnFork( int fd ) +{ + close( fd ); + FD_CLR( fd, &CloseMask ); +} + +static void +CloseOnFork( void ) +{ + int fd; + + for (fd = 0; fd <= max; fd++) + if (FD_ISSET( fd, &CloseMask )) + close( fd ); + FD_ZERO( &CloseMask ); + max = -1; +} + +int +Fork() +{ + int pid; + + sigset_t ss, oss; + sigfillset( &ss ); + sigprocmask( SIG_SETMASK, &ss, &oss ); + + if (!(pid = fork())) { +#ifdef SIGCHLD + (void)Signal( SIGCHLD, SIG_DFL ); +#endif + (void)Signal( SIGTERM, SIG_DFL ); + (void)Signal( SIGINT, SIG_IGN ); /* for -nodaemon */ + (void)Signal( SIGPIPE, SIG_DFL ); + (void)Signal( SIGALRM, SIG_DFL ); + (void)Signal( SIGHUP, SIG_DFL ); + sigemptyset( &ss ); + sigprocmask( SIG_SETMASK, &ss, NULL ); + CloseOnFork(); + return 0; + } + + sigprocmask( SIG_SETMASK, &oss, 0 ); + + return pid; +} + +int +Wait4( int pid ) +{ + waitType result; + + while (waitpid( pid, &result, 0 ) < 0) + if (errno != EINTR) { + Debug( "Wait4(%d) failed: %m\n", pid ); + return 0; + } + return waitVal( result ); +} + + +void +execute( char **argv, char **env ) +{ + Debug( "execute: %[s ; %[s\n", argv, env ); + execve( argv[0], argv, env ); + /* + * In case this is a shell script which hasn't been + * made executable (or this is a SYSV box), do + * a reasonable thing + */ + if (errno != ENOENT) { + char **newargv; + FILE *f; + int nu; + char program[1024]; + + /* + * emulate BSD kernel behaviour -- read + * the first line; check if it starts + * with "#!", in which case it uses + * the rest of the line as the name of + * program to run. Else use "/bin/sh". + */ + if (!(f = fopen( argv[0], "r" ))) + return; + if (!fGets( program, sizeof(program), f )) { + fclose( f ); + return; + } + fclose( f ); + if (!strncmp( program, "#!", 2 )) + newargv = parseArgs( 0, program + 2 ); + else + newargv = addStrArr( 0, "/bin/sh", 7 ); + if (!newargv) + return; + nu = arrLen( newargv ); + if (!(argv = xCopyStrArr( nu, argv ))) + return; + memcpy( argv, newargv, sizeof(char *) * nu ); + Debug( "shell script execution: %[s\n", argv ); + execve( argv[0], argv, env ); + } +} + +int +runAndWait( char **args, char **env ) +{ + int pid, ret; + + switch (pid = Fork()) { + case 0: + execute( args, env ); + LogError( "Can't execute %\"s: %m\n", args[0] ); + exit( 127 ); + case -1: + LogError( "Can't fork to execute %\"s: %m\n", args[0] ); + return 1; + } + ret = Wait4( pid ); + return waitVal( ret ); +} + +FILE * +pOpen( char **what, char m, int *pid ) +{ + int dp[2]; + + if (pipe( dp )) + return 0; + switch ((*pid = Fork())) { + case 0: + if (m == 'r') + dup2( dp[1], 1 ); + else + dup2( dp[0], 0 ); + close( dp[0] ); + close( dp[1] ); + execute( what, environ ); + LogError( "Can't execute %\"s: %m\n", what[0] ); + exit( 127 ); + case -1: + close( dp[0] ); + close( dp[1] ); + LogError( "Can't fork to execute %\"s: %m\n", what[0] ); + return 0; + } + if (m == 'r') { + close( dp[1] ); + return fdopen( dp[0], "r" ); + } else { + close( dp[0] ); + return fdopen( dp[1], "w" ); + } +} + +int +pClose( FILE *f, int pid ) +{ + fclose( f ); + return Wait4( pid ); +} + +char * +locate( const char *exe ) +{ + int len; + char *path, *name, *thenam, nambuf[PATH_MAX+1]; + char *pathe; + + if (!(path = getenv( "PATH" ))) { + LogError( "Can't execute %'s: $PATH not set.\n", exe ); + return 0; + } + len = strlen( exe ); + name = nambuf + PATH_MAX - len; + memcpy( name, exe, len + 1 ); + *--name = '/'; + do { + if (!(pathe = strchr( path, ':' ))) + pathe = path + strlen( path ); + len = pathe - path; + if (len && !(len == 1 && *path == '.')) { + thenam = name - len; + if (thenam >= nambuf) { + memcpy( thenam, path, len ); + if (!access( thenam, X_OK )) { + StrDup( &name, thenam ); + return name; + } + } + } + path = pathe; + } while (*path++ != '\0'); + LogError( "Can't execute %'s: not in $PATH.\n", exe ); + return 0; +} + + +static GTalk *curtalk; + +void +GSet( GTalk *tlk ) +{ + curtalk = tlk; +} + +int +GFork( GPipe *pajp, const char *pname, char *cname, + GPipe *ogp, char *cgname ) +{ + int opipe[2], ipipe[2], ogpipe[2], igpipe[2], pid; + + if (pipe( opipe )) + goto badp1; + if (pipe( ipipe )) + goto badp2; + if (ogp) { + if (pipe( ogpipe )) + goto badp3; + if (pipe( igpipe )) { + close( ogpipe[0] ); + close( ogpipe[1] ); + badp3: + close( ipipe[0] ); + close( ipipe[1] ); + badp2: + close( opipe[0] ); + close( opipe[1] ); + badp1: + LogError( "Cannot start %s, pipe() failed", cname ); + if (cname) + free( cname ); + return -1; + } + } + RegisterCloseOnFork( opipe[1] ); + RegisterCloseOnFork( ipipe[0] ); + if (ogp) { + RegisterCloseOnFork( ogpipe[1] ); + RegisterCloseOnFork( igpipe[0] ); + } + switch (pid = Fork()) { + case -1: + close( opipe[0] ); + close( ipipe[1] ); + CloseNClearCloseOnFork( opipe[1] ); + CloseNClearCloseOnFork( ipipe[0] ); + if (ogp) { + close( ogpipe[0] ); + close( igpipe[1] ); + CloseNClearCloseOnFork( ogpipe[1] ); + CloseNClearCloseOnFork( igpipe[0] ); + } + LogError( "Cannot start %s, fork() failed\n", cname ); + if (cname) + free( cname ); + return -1; + case 0: + pajp->wfd = ipipe[1]; + RegisterCloseOnFork( ipipe[1] ); + pajp->rfd = opipe[0]; + RegisterCloseOnFork( opipe[0] ); + pajp->who = (char *)pname; + if (ogp) { + ogp->wfd = igpipe[1]; + RegisterCloseOnFork( igpipe[1] ); + ogp->rfd = ogpipe[0]; + RegisterCloseOnFork( ogpipe[0] ); + ogp->who = (char *)pname; + } + break; + default: + close( opipe[0] ); + close( ipipe[1] ); + pajp->rfd = ipipe[0]; + pajp->wfd = opipe[1]; + pajp->who = cname; + if (ogp) { + close( ogpipe[0] ); + close( igpipe[1] ); + ogp->rfd = igpipe[0]; + ogp->wfd = ogpipe[1]; + ogp->who = cgname; + } + break; + } + return pid; +} + +int +GOpen( GProc *proc, char **argv, const char *what, char **env, char *cname, + GPipe *gp ) +{ + char **margv; + int pip[2]; + char coninfo[32]; + +/* ### GSet (proc->pipe); */ + if (proc->pid) { + LogError( "%s already running\n", cname ); + if (cname) + free( cname ); + return -1; + } + if (!(margv = xCopyStrArr( 1, argv ))) { + if (cname) + free( cname ); + return -1; + } + if (!StrApp( margv, progpath, what, (char *)0 )) { + free( margv ); + if (cname) + free( cname ); + return -1; + } + if (pipe( pip )) { + LogError( "Cannot start %s, pipe() failed\n", cname ); + if (cname) + free( cname ); + goto fail; + } + if (gp) { + ClearCloseOnFork( gp->rfd ); + ClearCloseOnFork( gp->wfd ); + } + proc->pid = GFork( &proc->pipe, 0, cname, 0, 0 ); + if (proc->pid) { + close( pip[1] ); + if (gp) { + RegisterCloseOnFork( gp->rfd ); + RegisterCloseOnFork( gp->wfd ); + } + } + switch (proc->pid) { + case -1: + fail1: + close( pip[0] ); + fail: + free( margv[0] ); + free( margv ); + return -1; + case 0: + (void)Signal( SIGPIPE, SIG_IGN ); + close( pip[0] ); + fcntl( pip[1], F_SETFD, FD_CLOEXEC ); + if (gp) + sprintf( coninfo, "CONINFO=%d %d %d %d", + proc->pipe.rfd, proc->pipe.wfd, gp->rfd, gp->wfd ); + else + sprintf( coninfo, "CONINFO=%d %d", + proc->pipe.rfd, proc->pipe.wfd ); + env = putEnv( coninfo, env ); + if (debugLevel & DEBUG_VALGRIND) { + char **nmargv = xCopyStrArr( 1, margv ); + nmargv[0] = locate( "valgrind" ); + execute( nmargv, env ); + } else if (debugLevel & DEBUG_STRACE) { + char **nmargv = xCopyStrArr( 1, margv ); + nmargv[0] = locate( "strace" ); + execute( nmargv, env ); + } else + execute( margv, env ); + write( pip[1], "", 1 ); + exit( 1 ); + default: + (void)Signal( SIGPIPE, SIG_IGN ); + if (Reader( pip[0], coninfo, 1 )) { + Wait4( proc->pid ); + LogError( "Cannot execute %\"s (%s)\n", margv[0], cname ); + GClosen (&proc->pipe); + goto fail1; + } + close( pip[0] ); + Debug( "started %s (%\"s), pid %d\n", cname, margv[0], proc->pid ); + free( margv[0] ); + free( margv ); + GSendInt( debugLevel ); + return 0; + } +} + +static void +iGClosen( GPipe *pajp ) +{ + CloseNClearCloseOnFork( pajp->rfd ); + CloseNClearCloseOnFork( pajp->wfd ); + pajp->rfd = pajp->wfd = -1; +} + +void +GClosen (GPipe *pajp) +{ + iGClosen( pajp ); + if (pajp->who) + free( pajp->who ); + pajp->who = 0; +} + +int +GClose (GProc *proc, GPipe *gp, int force) +{ + int ret; + + if (!proc->pid) { + Debug( "whoops, GClose while helper not running\n" ); + return 0; + } + iGClosen( &proc->pipe ); + if (gp) + GClosen (gp); + if (force) + TerminateProcess( proc->pid, SIGTERM ); + ret = Wait4( proc->pid ); + proc->pid = 0; + if (WaitSig( ret ) ? WaitSig( ret ) != SIGTERM : + (WaitCode( ret ) < EX_NORMAL || WaitCode( ret ) > EX_MAX)) + LogError( "Abnormal termination of %s, code %d, signal %d\n", + proc->pipe.who, WaitCode( ret ), WaitSig( ret ) ); + Debug( "closed %s\n", proc->pipe.who ); + if (proc->pipe.who) + free( proc->pipe.who ); + proc->pipe.who = 0; + return ret; +} + +static void ATTR_NORETURN +GErr( void ) +{ + Longjmp( curtalk->errjmp, 1 ); +} + +static void +GRead( void *buf, int len ) +{ + if (Reader( curtalk->pipe->rfd, buf, len ) != len) { + LogError( "Cannot read from %s\n", curtalk->pipe->who ); + GErr(); + } +} + +static void +GWrite( const void *buf, int len ) +{ + if (Writer( curtalk->pipe->wfd, buf, len ) != len) { + LogError( "Cannot write to %s\n", curtalk->pipe->who ); + GErr(); + } +#ifdef _POSIX_PRIORITY_SCHEDULING + if ((debugLevel & DEBUG_HLPCON)) + sched_yield(); +#endif +} + +void +GSendInt( int val ) +{ + GDebug( "sending int %d (%#x) to %s\n", val, val, curtalk->pipe->who ); + GWrite( &val, sizeof(val) ); +} + +int +GRecvInt() +{ + int val; + + GDebug( "receiving int from %s ...\n", curtalk->pipe->who ); + GRead( &val, sizeof(val) ); + GDebug( " -> %d (%#x)\n", val, val ); + return val; +} + +int +GRecvCmd( int *cmd ) +{ + GDebug( "receiving command from %s ...\n", curtalk->pipe->who ); + if (Reader( curtalk->pipe->rfd, cmd, sizeof(*cmd) ) == sizeof(*cmd)) { + GDebug( " -> %d\n", *cmd ); + return 1; + } + GDebug( " -> no data\n" ); + return 0; +} + +void +GSendArr( int len, const char *data ) +{ + GDebug( "sending array[%d] %02[*{hhx to %s\n", + len, len, data, curtalk->pipe->who ); + GWrite( &len, sizeof(len) ); + GWrite( data, len ); +} + +static char * +iGRecvArr( int *rlen ) +{ + int len; + char *buf; + + GRead( &len, sizeof(len) ); + *rlen = len; + GDebug( " -> %d bytes\n", len ); + if (!len) + return (char *)0; + if (!(buf = Malloc( len ))) + GErr(); + GRead( buf, len ); + return buf; +} + +char * +GRecvArr( int *rlen ) +{ + char *buf; + + GDebug( "receiving array from %s ...\n", curtalk->pipe->who ); + buf = iGRecvArr( rlen ); + GDebug( " -> %02[*{hhx\n", *rlen, buf ); + return buf; +} + +static int +iGRecvArrBuf( char *buf ) +{ + int len; + + GRead( &len, sizeof(len) ); + GDebug( " -> %d bytes\n", len ); + if (len) + GRead( buf, len ); + return len; +} + +int +GRecvArrBuf( char *buf ) +{ + int len; + + GDebug( "receiving already allocated array from %s ...\n", + curtalk->pipe->who ); + len = iGRecvArrBuf( buf ); + GDebug( " -> %02[*{hhx\n", len, buf ); + return len; +} + +int +GRecvStrBuf( char *buf ) +{ + int len; + + GDebug( "receiving already allocated string from %s ...\n", + curtalk->pipe->who ); + len = iGRecvArrBuf( buf ); + GDebug( " -> %\".*s\n", len, buf ); + return len; +} + +void +GSendStr( const char *buf ) +{ + int len; + + GDebug( "sending string %\"s to %s\n", buf, curtalk->pipe->who ); + if (buf) { + len = strlen( buf ) + 1; + GWrite( &len, sizeof(len) ); + GWrite( buf, len ); + } else + GWrite( &buf, sizeof(int) ); +} + +void +GSendNStr( const char *buf, int len ) +{ + int tlen = len + 1; + GDebug( "sending string %\".*s to %s\n", len, buf, curtalk->pipe->who ); + GWrite( &tlen, sizeof(tlen) ); + GWrite( buf, len ); + GWrite( "", 1 ); +} + +void +GSendStrN( const char *buf, int len ) +{ + if (buf) + GSendNStr( buf, StrNLen( buf, len ) ); + else + GSendStr( buf ); +} + +char * +GRecvStr() +{ + int len; + char *buf; + + GDebug( "receiving string from %s ...\n", curtalk->pipe->who ); + buf = iGRecvArr( &len ); + GDebug( " -> %\".*s\n", len, buf ); + return buf; +} + +static void +iGSendStrArr( int num, char **data ) +{ + char **cdata; + + GWrite( &num, sizeof(num) ); + for (cdata = data; --num >= 0; cdata++) + GSendStr( *cdata ); +} + +/* +void +GSendStrArr (int num, char **data) +{ + GDebug( "sending string array[%d] to %s\n", num, curtalk->pipe->who ); + iGSendStrArr( num, data ); +} +*/ + +char ** +GRecvStrArr( int *rnum ) +{ + int num; + char **argv, **cargv; + + GDebug( "receiving string array from %s ...\n", curtalk->pipe->who ); + GRead( &num, sizeof(num) ); + GDebug( " -> %d strings\n", num ); + *rnum = num; + if (!num) + return (char **)0; + if (!(argv = Malloc( num * sizeof(char *) ))) + GErr(); + for (cargv = argv; --num >= 0; cargv++) + *cargv = GRecvStr(); + return argv; +} + +void +GSendArgv( char **argv ) +{ + int num; + + if (argv) { + for (num = 0; argv[num]; num++); + GDebug( "sending argv[%d] to %s ...\n", num, curtalk->pipe->who ); + iGSendStrArr( num + 1, argv ); + } else { + GDebug( "sending NULL argv to %s\n", curtalk->pipe->who ); + GWrite( &argv, sizeof(int) ); + } +} + +char ** +GRecvArgv() +{ + int num; + + return GRecvStrArr( &num ); +} + diff --git a/tdm/backend/protodpy.c b/tdm/backend/protodpy.c new file mode 100644 index 000000000..08c38fbd1 --- /dev/null +++ b/tdm/backend/protodpy.c @@ -0,0 +1,141 @@ +/* + +Copyright 1988, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + * + * manage a collection of proto-displays. These are displays for + * which sessionID's have been generated, but no session has been + * started. + */ + +#include <config.h> + +#ifdef XDMCP + +#include "dm.h" +#include "dm_error.h" + +static struct protoDisplay *protoDisplays; + +struct protoDisplay * +FindProtoDisplay( + XdmcpNetaddr address, + int addrlen, + CARD16 displayNumber ) +{ + struct protoDisplay *pdpy; + + Debug( "FindProtoDisplay\n" ); + for (pdpy = protoDisplays; pdpy; pdpy=pdpy->next) { + if (pdpy->displayNumber == displayNumber && + addressEqual( address, addrlen, pdpy->address, pdpy->addrlen )) + { + return pdpy; + } + } + return (struct protoDisplay *)0; +} + +static void +TimeoutProtoDisplays (void) +{ + struct protoDisplay *pdpy, *next; + + for (pdpy = protoDisplays; pdpy; pdpy = next) { + next = pdpy->next; + if (pdpy->date < (unsigned long)(now - PROTO_TIMEOUT)) + DisposeProtoDisplay( pdpy ); + } +} + +struct protoDisplay * +NewProtoDisplay( XdmcpNetaddr address, int addrlen, CARD16 displayNumber, + CARD16 connectionType, ARRAY8Ptr connectionAddress, + CARD32 sessionID ) +{ + struct protoDisplay *pdpy; + + Debug( "NewProtoDisplay\n" ); + TimeoutProtoDisplays (); + pdpy = (struct protoDisplay *)Malloc( sizeof(*pdpy) ); + if (!pdpy) + return NULL; + pdpy->address = (XdmcpNetaddr)Malloc( addrlen ); + if (!pdpy->address) { + free( (char *)pdpy ); + return NULL; + } + pdpy->addrlen = addrlen; + memmove( pdpy->address, address, addrlen ); + pdpy->displayNumber = displayNumber; + pdpy->connectionType = connectionType; + pdpy->date = now; + if (!XdmcpCopyARRAY8( connectionAddress, &pdpy->connectionAddress )) { + free( (char *)pdpy->address ); + free( (char *)pdpy ); + return NULL; + } + pdpy->sessionID = sessionID; + pdpy->fileAuthorization = (Xauth *)NULL; + pdpy->xdmcpAuthorization = (Xauth *)NULL; + pdpy->next = protoDisplays; + protoDisplays = pdpy; + return pdpy; +} + +void +DisposeProtoDisplay( pdpy ) + struct protoDisplay *pdpy; +{ + struct protoDisplay *p, *prev; + + prev = 0; + for (p = protoDisplays; p; p=p->next) { + if (p == pdpy) + break; + prev = p; + } + if (!p) + return; + if (prev) + prev->next = pdpy->next; + else + protoDisplays = pdpy->next; + bzero( &pdpy->key, sizeof(pdpy->key) ); + if (pdpy->fileAuthorization) + XauDisposeAuth( pdpy->fileAuthorization ); + if (pdpy->xdmcpAuthorization) + XauDisposeAuth( pdpy->xdmcpAuthorization ); + XdmcpDisposeARRAY8( &pdpy->connectionAddress ); + free( (char *)pdpy->address ); + free( (char *)pdpy ); +} + +#endif /* XDMCP */ diff --git a/tdm/backend/reset.c b/tdm/backend/reset.c new file mode 100644 index 000000000..2c2100870 --- /dev/null +++ b/tdm/backend/reset.c @@ -0,0 +1,111 @@ +/* + +Copyright 1988, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + * + * pseudoReset -- pretend to reset the server by killing all clients + * with windows. It will reset the server most of the time, unless + * a client remains connected with no windows. + */ + +#include "dm.h" +#include "dm_error.h" + +#include <X11/Xlib.h> + +#include <signal.h> + +/*ARGSUSED*/ +static int +ignoreErrors( Display *dspl ATTR_UNUSED, XErrorEvent *event ATTR_UNUSED ) +{ + Debug( "ignoring error\n" ); + return 0; +} + +/* + * this is mostly bogus -- but quite useful. I wish the protocol + * had some way of enumerating and identifying clients, that way + * this code wouldn't have to be this kludgy. + */ + +static void +killWindows( Window window ) +{ + Window root, parent, *children; + unsigned int child, nchildren = 0; + + while (XQueryTree( dpy, window, &root, &parent, &children, &nchildren ) + && nchildren > 0) + { + for (child = 0; child < nchildren; child++) { + Debug( "XKillClient %p\n", children[child] ); + XKillClient( dpy, children[child] ); + } + XFree( (char *)children ); + } +} + +static Jmp_buf resetJmp; + +/* ARGSUSED */ +static void +abortReset( int n ATTR_UNUSED ) +{ + Longjmp( resetJmp, 1 ); +} + +/* + * this display connection better not have any windows... + */ + +void +pseudoReset() +{ + int screen; + + if (Setjmp( resetJmp )) { + LogError( "pseudoReset timeout\n" ); + } else { + (void)Signal( SIGALRM, abortReset ); + (void)alarm( 30 ); + XSetErrorHandler( ignoreErrors ); + for (screen = 0; screen < ScreenCount (dpy); screen++) { + Debug( "pseudoReset screen %d\n", screen ); + killWindows( RootWindow( dpy, screen ) ); + } + Debug( "before XSync\n" ); + XSync( dpy, False ); + (void)alarm( 0 ); + } + Signal( SIGALRM, SIG_DFL ); + XSetErrorHandler( (XErrorHandler)0 ); + Debug( "pseudoReset done\n" ); +} diff --git a/tdm/backend/resource.c b/tdm/backend/resource.c new file mode 100644 index 000000000..f17db78ec --- /dev/null +++ b/tdm/backend/resource.c @@ -0,0 +1,486 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2000-2005 Oswald Buddenhagen <[email protected]> + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + * + * obtain configuration data + */ + +#include "dm.h" +#include "dm_error.h" + +#include <sys/stat.h> + + +static char **originalArgv; + +static GProc getter; +GTalk cnftalk; + +static void +OpenGetter() +{ + GSet( &cnftalk ); + if (!getter.pid) { + if (GOpen( &getter, + originalArgv, "_config", 0, strdup( "config reader" ), + 0 )) + LogPanic( "Cannot run config reader\n" ); + Debug( "getter now ready\n" ); + } +} + +void +CloseGetter() +{ + if (getter.pid) { + GSet( &cnftalk ); + (void)GClose (&getter, 0, 0); + Debug( "getter now closed\n" ); + } +} + +/* + * ref-counted, unique-instance strings + */ +static RcStr *strs; + +/* + * make a ref-counted string of the argument. the new string will + * have a ref-count of 1. the passed string pointer is no longer valid. + */ +RcStr * +newStr( char *str ) +{ + RcStr *cs; + + for (cs = strs; cs; cs = cs->next) + if (!strcmp( str, cs->str )) { + free( str ); + cs->cnt++; + return cs; + } + if (!(cs = Malloc( sizeof(*cs) ))) + return 0; + cs->cnt = 1; + cs->str = str; + cs->next = strs; + strs = cs; + return cs; +} + +/* + * decrement ref-count and delete string when count drops to 0. + */ +void +delStr( RcStr *str ) +{ + RcStr **cs; + + if (!str || --str->cnt) + return; + for (cs = &strs; *cs; cs = &((*cs)->next)) + if (str == *cs) { + *cs = (*cs)->next; + free( (*cs)->str ); + free( *cs ); + break; + } +} + + +typedef struct CfgFile { + RcStr *name; + int depidx; + long deptime; +} CfgFile; + +static int numCfgFiles; +static CfgFile *cfgFiles; + +static int cfgMapT[] = { + GC_gGlobal, + GC_gDisplay, +#ifdef XDMCP + GC_gXaccess, +#endif +}; +static int cfgMap[as(cfgMapT)]; + +static int +GetDeps() +{ + int ncf, i, dep, ret; + CfgFile *cf; + + OpenGetter(); + GSendInt( GC_Files ); + ncf = GRecvInt(); + if (!(cf = Malloc( ncf * sizeof(*cf) ))) { + CloseGetter(); + return 0; + } + for (i = 0; i < ncf; i++) { + cf[i].name = newStr( GRecvStr() ); + if ((dep = cf[i].depidx = GRecvInt()) != -1) + cf[i].deptime = mTime( cf[dep].name->str ); + } + if (cfgFiles) { + for (i = 0; i < numCfgFiles; i++) + delStr( cfgFiles[i].name ); + free( cfgFiles ); + } + ret = 1; + cfgFiles = cf; + numCfgFiles = ncf; + for (i = 0; i < as(cfgMapT); i++) { + GSendInt( cfgMapT[i] ); + if ((cfgMap[i] = GRecvInt()) < 0) { + LogError( "Config reader does not support config cathegory %#x\n", + cfgMapT[i] ); + ret = 0; + } + } + GSendInt( -1 ); + return ret; +} + +static int +checkDep( int idx ) +{ + int dep; + + if ((dep = cfgFiles[idx].depidx) == -1) + return 0; + if (checkDep( dep )) + return 1; + return mTime( cfgFiles[dep].name->str ) != cfgFiles[idx].deptime; +} + +static int +needsReScan( int what, CfgDep *dep ) +{ + int widx, idx; + long mt; + + for (widx = 0; cfgMapT[widx] != what; widx++); + idx = cfgMap[widx]; + if (checkDep( idx )) { + if (!GetDeps()) + return -1; + idx = cfgMap[widx]; + } + mt = mTime( cfgFiles[idx].name->str ); + if (dep->name != cfgFiles[idx].name) { + if (dep->name) + delStr( dep->name ); + dep->name = cfgFiles[idx].name; + dep->name->cnt++; + dep->time = mt; + return 1; + } else if (dep->time != mt) { + dep->time = mt; + return 1; + } else + return 0; +} + +int +startConfig( int what, CfgDep *dep, int force ) +{ + int ret; + + if ((ret = needsReScan( what, dep )) < 0 || (!ret && !force)) + return ret; + OpenGetter(); + GSendInt( GC_GetConf ); + GSendInt( what ); + GSendStr( dep->name->str ); + return 1; +} + +static void +LoadResources( CfgArr *conf ) +{ + char **vptr, **pptr, *cptr; + long *iptr, i, id, nu, j, nptr, nint, nchr; + + if (conf->data) + free( conf->data ); + conf->numCfgEnt = GRecvInt(); + nptr = GRecvInt(); + nint = GRecvInt(); + nchr = GRecvInt(); + if (!(conf->data = Malloc( conf->numCfgEnt * + (sizeof(long) + + sizeof(char *)) + + nptr * sizeof(char *) + + nint * sizeof(long) + + nchr ))) + { + CloseGetter(); + return; + } + vptr = (char **)conf->data; + pptr = vptr + conf->numCfgEnt; + conf->idx = (long *)(pptr + nptr); + iptr = conf->idx + conf->numCfgEnt; + cptr = (char *)(iptr + nint); + for (i = 0; i < conf->numCfgEnt; i++) { + id = GRecvInt(); + conf->idx[i] = id; + switch (id & C_TYPE_MASK) { + case C_TYPE_INT: + vptr[i] = (char *)((unsigned long)GRecvInt()); + break; + case C_TYPE_STR: + vptr[i] = cptr; + cptr += GRecvStrBuf( cptr ); + break; + case C_TYPE_ARGV: + nu = GRecvInt(); + vptr[i] = (char *)pptr; + for (j = 0; j < nu; j++) { + *pptr++ = cptr; + cptr += GRecvStrBuf( cptr ); + } + *pptr++ = (char *)0; + break; + default: + LogError( "Config reader supplied unknown data type in id %#x\n", + id ); + break; + } + } +} + +static void +ApplyResource( int id, char **src, char **dst ) +{ + switch (id & C_TYPE_MASK) { + case C_TYPE_INT: + *(int *)dst = *(long *)src; + break; + case C_TYPE_STR: + case C_TYPE_ARGV: + *dst = *src; + break; + } +} + + +#define boffset(f) XtOffsetOf(struct display, f) + +/* no global variables exported currently +struct globEnts { + int id; + char **off; +} globEnt[] = { +}; + */ + +/* no per-display variables exported currently +struct dpyEnts { + int id; + int off; +} dpyEnt[] = { +}; + */ + +CfgArr cfg; + +char ** +FindCfgEnt( struct display *d, int id ) +{ + int i; + +/* no global variables exported currently + for (i = 0; i < as(globEnt); i++) + if (globEnt[i].id == id) + return globEnt[i].off; + */ + for (i = 0; i < cfg.numCfgEnt; i++) + if (cfg.idx[i] == id) + return ((char **)cfg.data) + i; + if (d) { +/* no per-display variables exported currently + for (i = 0; i < as(dpyEnt); i++) + if (dpyEnt[i].id == id) + return (char **)(((char *)d) + dpyEnt[i].off); + */ + for (i = 0; i < d->cfg.numCfgEnt; i++) + if (d->cfg.idx[i] == id) + return ((char **)d->cfg.data) + i; + } + Debug( "unknown config entry %#x requested\n", id ); + return (char **)0; +} + + +CONF_CORE_GLOBAL_DEFS + +struct globVals { + int id; + char **off; +} globVal[] = { +CONF_CORE_GLOBALS +}; + +int +LoadDMResources( int force ) +{ + int i, ret; + char **ent; + + if (Setjmp( cnftalk.errjmp )) + return -1; /* may memleak, but we probably have to abort anyway */ + if ((ret = startConfig( GC_gGlobal, &cfg.dep, force )) <= 0) + return ret; + LoadResources( &cfg ); +/* Debug( "manager resources: %[*x\n", + cfg.numCfgEnt, ((char **)cfg.data) + cfg.numCfgEnt );*/ + ret = 1; + for (i = 0; i < as(globVal); i++) { + if (!(ent = FindCfgEnt( 0, globVal[i].id ))) + ret = -1; + else + ApplyResource( globVal[i].id, ent, globVal[i].off ); + } + if (ret < 0) + LogError( "Internal error: config reader supplied incomplete data\n" ); + return ret; +} + + +struct dpyVals { + int id; + int off; +} dpyVal[] = { +CONF_CORE_LOCALS +}; + +int +LoadDisplayResources( struct display *d ) +{ + int i, ret; + char **ent; + + if (Setjmp( cnftalk.errjmp )) + return -1; /* may memleak */ + if ((ret = startConfig( GC_gDisplay, &d->cfg.dep, FALSE )) <= 0) + return ret; + GSendStr( d->name ); + GSendStr( d->class2 ); + LoadResources( &d->cfg ); +/* Debug( "display(%s, %s) resources: %[*x\n", d->name, d->class2, + d->cfg.numCfgEnt, ((char **)d->cfg.data) + d->cfg.numCfgEnt );*/ + ret = 1; + for (i = 0; i < as(dpyVal); i++) { + if (!(ent = FindCfgEnt( d, dpyVal[i].id ))) + ret = -1; + else + ApplyResource( dpyVal[i].id, ent, + (char **)(((char *)d) + dpyVal[i].off) ); + } + if (ret < 0) + LogError( "Internal error: config reader supplied incomplete data\n" ); + return ret; +} + +int +InitResources( char **argv ) +{ + originalArgv = argv; + cnftalk.pipe = &getter.pipe; + if (Setjmp( cnftalk.errjmp )) + return 0; /* may memleak */ + return GetDeps(); +} + +static void +addServers( char **srv, int bType ) +{ + char *name, *class2; + const char *dtx, *cls; + struct display *d; + + for (; *srv; srv++) { + if ((cls = strchr( *srv, '_' ))) { + if (!StrNDup( &name, *srv, cls - *srv )) + return; + if (!StrDup( &class2, cls )) { + free( name ); + return; + } + } else { + if (!StrDup( &name, *srv )) + return; + class2 = 0; + } + if ((d = FindDisplayByName( name ))) { + if (d->class2) + free( d->class2 ); + dtx = "existing"; + } else { + if (!(d = NewDisplay( name ))) { + free( name ); + if (class2) + free( class2 ); + return; + } + dtx = "new"; + } + d->stillThere = 1; + d->class2 = class2; + d->displayType = (*name == ':' ? dLocal : dForeign) | bType; + if ((bType & d_lifetime) == dReserve) { + if (d->status == notRunning) + d->status = reserve; + } else { + if (d->status == reserve) + d->status = notRunning; + } + Debug( "found %s %s%s display: %s %s\n", dtx, + ((d->displayType & d_location) == dLocal) ? "local" : "foreign", + ((d->displayType & d_lifetime) == dReserve) ? " reserve" : "", + d->name, d->class2 ); + free( name ); + } +} + +void +ScanServers( void ) +{ + Debug( "ScanServers\n" ); + addServers( staticServers, dFromFile | dPermanent ); + addServers( reserveServers, dFromFile | dReserve ); +} + diff --git a/tdm/backend/rpcauth.c b/tdm/backend/rpcauth.c new file mode 100644 index 000000000..1186f72c2 --- /dev/null +++ b/tdm/backend/rpcauth.c @@ -0,0 +1,89 @@ +/* + +Copyright 1988, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + * + * generate SecureRPC authorization records + */ + +#include <config.h> + +#ifdef SECURE_RPC + +#include "dm.h" +#include "dm_auth.h" +#include "dm_error.h" + +#include <rpc/rpc.h> +#include <rpc/key_prot.h> + +/*ARGSUSED*/ +void +SecureRPCInitAuth( unsigned short name_len ATTR_UNUSED, + const char *name ATTR_UNUSED ) +{ +} + +Xauth * +SecureRPCGetAuth( unsigned short namelen, const char *name ) +{ + Xauth *new; + char key[MAXNETNAMELEN+1]; + + new = (Xauth *)Malloc( sizeof(*new) ); + if (!new) + return (Xauth *)0; + new->family = FamilyWild; + new->address_length = 0; + new->address = 0; + new->number_length = 0; + new->number = 0; + + getnetname( key ); + Debug( "system netname %s\n", key ); + new->data_length = strlen( key ); + new->data = (char *)Malloc( new->data_length ); + if (!new->data) { + free( (char *)new ); + return (Xauth *)0; + } + new->name = (char *)Malloc( namelen ); + if (!new->name) { + free( (char *)new->data ); + free( (char *)new ); + return (Xauth *)0; + } + memmove( new->name, name, namelen ); + new->name_length = namelen; + memmove( new->data, key, new->data_length ); + return new; +} + +#endif diff --git a/tdm/backend/server.c b/tdm/backend/server.c new file mode 100644 index 000000000..e78d8a66c --- /dev/null +++ b/tdm/backend/server.c @@ -0,0 +1,376 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2001,2003,2005 Oswald Buddenhagen <[email protected]> + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + * + * server.c - manage the X server + */ + +#include "dm.h" +#include "dm_error.h" +#include "dm_socket.h" + +#include <X11/Xlib.h> + +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> + + +struct display *startingServer; +time_t serverTimeout = TO_INF; + +char ** +PrepServerArgv( struct display *d, const char *args ) +{ + char **argv; +#ifdef HAVE_VTS + char vtstr[8]; +#endif + + if (!(argv = parseArgs( 0, d->serverCmd )) || + !(argv = parseArgs( argv, args )) || + !(argv = addStrArr( argv, d->name, -1 ))) + exit( 47 ); +#ifdef HAVE_VTS + if (d->serverVT && + !(argv = addStrArr( argv, vtstr, + sprintf( vtstr, "vt%d", d->serverVT ) ))) + exit( 47 ); +#endif + return argv; +} + +static void +StartServerOnce( void ) +{ + struct display *d = startingServer; + char **argv; + int pid; + + Debug( "StartServerOnce for %s, try %d\n", d->name, ++d->startTries ); + d->serverStatus = starting; + switch (pid = Fork()) { + case 0: + argv = PrepServerArgv( d, d->serverArgsLocal ); + if (d->authFile) { + if (!(argv = addStrArr( argv, "-auth", 5 )) || + !(argv = addStrArr( argv, d->authFile, -1 ))) + exit( 47 ); + } + Debug( "exec %\"[s\n", argv ); + /* + * give the server SIGUSR1 ignored, + * it will notice that and send SIGUSR1 + * when ready + */ + (void)Signal( SIGUSR1, SIG_IGN ); + (void)execv( argv[0], argv ); + LogError( "X server %\"s cannot be executed\n", argv[0] ); + + /* Let's try again with some standard paths */ + argv[0] = (char *)realloc(argv[0], strlen("/usr/X11R6/bin/X") + 1); + if (argv[0] != NULL) { + argv[0] = "/usr/X11R6/bin/X"; + Debug( "exec %\"[s\n", argv ); + (void)execv( argv[0], argv ); + LogError( "X server %\"s cannot be executed\n", argv[0] ); + + argv[0] = "/usr/bin/X"; /* Shorter than the previous file name */ + Debug( "exec %\"[s\n", argv ); + (void)execv( argv[0], argv ); + LogError( "X server %\"s cannot be executed\n", argv[0] ); + } + + exit( 47 ); + case -1: + LogError( "X server fork failed\n" ); + StartServerFailed(); + break; + default: + Debug( "X server forked, pid %d\n", pid ); + d->serverPid = pid; + serverTimeout = d->serverTimeout + now; + break; + } +} + +void +StartServer( struct display *d ) +{ + startingServer = d; + d->startTries = 0; + StartServerOnce(); +} + +void +AbortStartServer( struct display *d ) +{ + if (startingServer == d) + { + if (d->serverStatus != ignore) + { + d->serverStatus = ignore; + serverTimeout = TO_INF; + Debug( "aborting X server start\n" ); + } + startingServer = 0; + } +} + +void +StartServerSuccess() +{ + struct display *d = startingServer; + d->serverStatus = ignore; + serverTimeout = TO_INF; + Debug( "X server ready, starting session\n" ); + StartDisplayP2( d ); +} + +void +StartServerFailed() +{ + struct display *d = startingServer; + if (!d->serverAttempts || d->startTries < d->serverAttempts) { + d->serverStatus = pausing; + serverTimeout = d->openDelay + now; + } else { + d->serverStatus = ignore; + serverTimeout = TO_INF; + startingServer = 0; + LogError( "X server for display %s can't be started," + " session disabled\n", d->name ); + StopDisplay( d ); + } +} + +void +StartServerTimeout() +{ + struct display *d = startingServer; + switch (d->serverStatus) { + case ignore: + case awaiting: + break; /* cannot happen */ + case starting: + LogError( "X server startup timeout, terminating\n" ); + kill( d->serverPid, d->termSignal ); + d->serverStatus = d->termSignal == SIGKILL ? killed : terminated; + serverTimeout = d->serverTimeout + now; + break; + case terminated: + LogInfo( "X server termination timeout, killing\n" ); + kill( d->serverPid, SIGKILL ); + d->serverStatus = killed; + serverTimeout = 10 + now; + break; + case killed: + LogInfo( "X server is stuck in D state; leaving it alone\n" ); + StartServerFailed(); + break; + case pausing: + StartServerOnce(); + break; + } +} + + +Display *dpy; + +/* + * this code is complicated by some TCP failings. On + * many systems, the connect will occasionally hang forever, + * this trouble is avoided by setting up a timeout to Longjmp + * out of the connect (possibly leaving piles of garbage around + * inside Xlib) and give up, terminating the server. + */ + +static Jmp_buf openAbort; + +/* ARGSUSED */ +static void +abortOpen( int n ATTR_UNUSED ) +{ + Longjmp( openAbort, 1 ); +} + +#ifdef XDMCP + +#ifdef STREAMSCONN +# include <tiuser.h> +#endif + +static void +GetRemoteAddress( struct display *d, int fd ) +{ + char buf[512]; + int len = sizeof(buf); +#ifdef STREAMSCONN + struct netbuf netb; +#endif + + XdmcpDisposeARRAY8( &d->peer ); +#ifdef STREAMSCONN + netb.maxlen = sizeof(buf); + netb.buf = buf; + t_getname( fd, &netb, REMOTENAME ); + len = 8; + /* lucky for us, t_getname returns something that looks like a sockaddr */ +#else + getpeername( fd, (struct sockaddr *)buf, (void *)&len ); +#endif + if (len && XdmcpAllocARRAY8( &d->peer, len )) + memmove( (char *)d->peer.data, buf, len ); + Debug( "got remote address %s %d\n", d->name, d->peer.length ); +} + +#endif /* XDMCP */ + +static int +openErrorHandler( Display *dspl ATTR_UNUSED ) +{ + LogError( "IO Error in XOpenDisplay\n" ); + exit( EX_OPENFAILED_DPY ); + /*NOTREACHED*/ + return (0); +} + +void +WaitForServer( struct display *d ) +{ + volatile int i; + /* static int i; */ + + i = 0; + do { + (void)Signal( SIGALRM, abortOpen ); + (void)alarm( (unsigned)d->openTimeout ); + if (!Setjmp( openAbort )) { + Debug( "before XOpenDisplay(%s)\n", d->name ); + errno = 0; + (void)XSetIOErrorHandler( openErrorHandler ); + dpy = XOpenDisplay( d->name ); +#ifdef STREAMSCONN + { + /* For some reason, the next XOpenDisplay we do is + going to fail, so we might as well get that out + of the way. There is something broken here. */ + Display *bogusDpy = XOpenDisplay( d->name ); + Debug( "bogus XOpenDisplay %s\n", + bogusDpy ? "succeeded" : "failed" ); + if (bogusDpy) XCloseDisplay( bogusDpy ); /* just in case */ + } +#endif + (void)alarm( (unsigned)0 ); + (void)Signal( SIGALRM, SIG_DFL ); + (void)XSetIOErrorHandler( (int (*)( Display * )) 0 ); + Debug( "after XOpenDisplay(%s)\n", d->name ); + if (dpy) { +#ifdef XDMCP + if ((d->displayType & d_location) == dForeign) + GetRemoteAddress( d, ConnectionNumber( dpy ) ); +#endif + RegisterCloseOnFork( ConnectionNumber( dpy ) ); + return; + } + Debug( "OpenDisplay(%s) attempt %d failed: %m\n", d->name, i + 1 ); + sleep( (unsigned)d->openDelay ); + } else { + LogError( "Hung in XOpenDisplay(%s), aborting\n", d->name ); + (void)Signal( SIGALRM, SIG_DFL ); + break; + } + } while (++i < d->openRepeat); + LogError( "Cannot connect to %s, giving up\n", d->name ); + exit( EX_OPENFAILED_DPY ); +} + + +void +ResetServer( struct display *d ) +{ + if (dpy && (d->displayType & d_origin) != dFromXDMCP) + pseudoReset(); +} + + +static Jmp_buf pingTime; + +static void +PingLost( void ) +{ + Longjmp( pingTime, 1 ); +} + +/* ARGSUSED */ +static int +PingLostIOErr( Display *dspl ATTR_UNUSED ) +{ + PingLost(); + return 0; +} + +/* ARGSUSED */ +static void +PingLostSig( int n ATTR_UNUSED ) +{ + PingLost(); +} + +int +PingServer( struct display *d ) +{ + int (*oldError)( Display * ); + void (*oldSig)( int ); + int oldAlarm; + + oldError = XSetIOErrorHandler( PingLostIOErr ); + oldAlarm = alarm( 0 ); + oldSig = Signal( SIGALRM, PingLostSig ); + (void)alarm( d->pingTimeout * 60 ); + if (!Setjmp( pingTime )) { + Debug( "ping X server\n" ); + XSync( dpy, 0 ); + } else { + Debug( "X server dead\n" ); + (void)alarm( 0 ); + (void)Signal( SIGALRM, SIG_DFL ); + XSetIOErrorHandler( oldError ); + return 0; + } + (void)alarm( 0 ); + (void)Signal( SIGALRM, oldSig ); + (void)alarm( oldAlarm ); + Debug( "X server alive\n" ); + XSetIOErrorHandler( oldError ); + return 1; +} diff --git a/tdm/backend/session.c b/tdm/backend/session.c new file mode 100644 index 000000000..9a12ce312 --- /dev/null +++ b/tdm/backend/session.c @@ -0,0 +1,813 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2000-2004 Oswald Buddenhagen <[email protected]> + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + * + * subdaemon event loop, etc. + */ + +#include "dm.h" +#include "dm_error.h" + +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/cursorfont.h> + +#include <stdio.h> +#include <ctype.h> +#include <signal.h> + +#ifdef WITH_CONSOLE_KIT +#include "consolekit.h" +#endif + +struct display *td; +const char *td_setup = "auto"; + +static void DeleteXloginResources( void ); +static void LoadXloginResources( void ); +static void SetupDisplay( const char *arg ); + + +static Jmp_buf pingTime; + +/* ARGSUSED */ +static void +catchAlrm( int n ATTR_UNUSED ) +{ + Longjmp( pingTime, 1 ); +} + +static Jmp_buf tenaciousClient; + +/* ARGSUSED */ +static void +waitAbort( int n ATTR_UNUSED ) +{ + Longjmp( tenaciousClient, 1 ); +} + +static void +AbortClient( int pid ) +{ + int sig = SIGTERM; + volatile int i; + int retId; + + for (i = 0; i < 4; i++) { + if (kill( -pid, sig ) == -1) { + switch (errno) { + case EPERM: + LogError( "Can't kill client\n" ); + case EINVAL: + case ESRCH: + return; + } + } + if (!Setjmp( tenaciousClient )) { + (void)Signal( SIGALRM, waitAbort ); + (void)alarm( (unsigned)10 ); + retId = wait( (waitType *)0 ); + (void)alarm( (unsigned)0 ); + (void)Signal( SIGALRM, SIG_DFL ); + if (retId == pid) + break; + } else + (void)Signal( SIGALRM, SIG_DFL ); + sig = SIGKILL; + } +} + + +static char * +conv_auto( int what, const char *prompt ATTR_UNUSED ) +{ + switch (what) { + case GCONV_USER: + return curuser; + case GCONV_PASS: + case GCONV_PASS_ND: + return curpass; + default: + LogError( "Unknown authentication data type requested for autologin.\n" ); + return 0; + } +} + +static void +DoAutoLogon( void ) +{ + ReStr( &curuser, td->autoUser ); + ReStr( &curpass, td->autoPass ); + ReStr( &curtype, "classic" ); + cursource = PWSRC_AUTOLOGIN; +} + +static int +AutoLogon( Time_t tdiff ) +{ + Debug( "autoLogon, tdiff = %d, rLogin = %d, goodexit = %d, nuser = %s\n", + tdiff, td->hstent->rLogin, td->hstent->goodExit, td->hstent->nuser ); + if (td->hstent->rLogin == 2 || + (td->hstent->rLogin == 1 && + tdiff <= 0 && !td->hstent->goodExit && !td->hstent->lock)) + { + curuser = td->hstent->nuser; + td->hstent->nuser = 0; + curpass = td->hstent->npass; + td->hstent->npass = 0; + newdmrc = td->hstent->nargs; + td->hstent->nargs = 0; + ReStr( &curtype, "classic" ); + cursource = (td->hstent->rLogin == 1) ? PWSRC_RELOGIN : PWSRC_MANUAL; + return 1; + } else if (*td->autoUser && !td->autoDelay && + ((tdiff > 0 && ((td->displayType & d_lifetime) == dTransient || + !td->hstent->lastExit)) || + td->autoAgain)) + { + unsigned int lmask; + Window dummy1, dummy2; + int dummy3, dummy4, dummy5, dummy6; + XQueryPointer( dpy, DefaultRootWindow( dpy ), + &dummy1, &dummy2, &dummy3, &dummy4, &dummy5, &dummy6, + &lmask ); + if (lmask & ShiftMask) + return 0; + DoAutoLogon(); + return 1; + } + return 0; +} + + +static const struct { + int vcode, echo, ndelay; +} grqs[] = { + { V_GET_TEXT, TRUE, FALSE }, + { V_GET_TEXT, FALSE, FALSE }, + { V_GET_TEXT, TRUE, FALSE }, + { V_GET_TEXT, FALSE, FALSE }, + { V_GET_TEXT, FALSE, TRUE }, + { V_GET_BINARY, 0, 0 } +}; + +char * +conv_interact( int what, const char *prompt ) +{ + char *ret; + int tag; + + GSendInt( grqs[what].vcode ); + if (what == GCONV_BINARY) { + unsigned const char *up = (unsigned const char *)prompt; + int len = up[3] | (up[2] << 8) | (up[1] << 16) | (up[0] << 24); + GSendArr( len, prompt ); + GSendInt( FALSE ); /* ndelay */ + return GRecvArr( &len ); + } else { + GSendStr( prompt ); + GSendInt( grqs[what].echo ); + GSendInt( grqs[what].ndelay ); + ret = GRecvStr(); + if (ret) { + tag = GRecvInt(); + switch (what) { + case GCONV_USER: + /* assert(tag & V_IS_USER); */ + if (curuser) + free( curuser ); + curuser = ret; + break; + case GCONV_PASS: + case GCONV_PASS_ND: + /* assert(tag & V_IS_PASSWORD); */ + if (curpass) + free( curpass ); + curpass = ret; + break; + default: + if (tag & V_IS_USER) + ReStr( &curuser, ret ); + else if (tag & V_IS_PASSWORD) + ReStr( &curpass, ret ); + else if (tag & V_IS_NEWPASSWORD) + ReStr( &newpass, ret ); + else if (tag & V_IS_OLDPASSWORD) + ReStr( &ret, curpass ); + } + } + return ret; + } +} + +static int greeter; +GProc grtproc; +GTalk grttalk; + +GTalk mstrtalk; /* make static; see dm.c */ + +int +CtrlGreeterWait( int wreply ) +{ + int i, cmd, type, rootok; + char *name, *pass, **avptr; +#ifdef XDMCP + ARRAY8Ptr aptr; +#endif + + if (Setjmp( mstrtalk.errjmp )) { + CloseGreeter( TRUE ); + SessionExit( EX_UNMANAGE_DPY ); + } + + while (GRecvCmd( &cmd )) { + switch (cmd) + { + case G_Ready: + Debug( "G_Ready\n" ); + return 0; + case G_GetCfg: + /*Debug ("G_GetCfg\n");*/ + type = GRecvInt(); + /*Debug (" index %#x\n", type);*/ + if (type == C_isLocal) + i = (td->displayType & d_location) == dLocal; + else if (type == C_hasConsole) +#ifdef HAVE_VTS + i = *consoleTTYs != 0; +#else + i = td->console != 0; +#endif + else if (type == C_isAuthorized) + i = td->authorizations != 0; + else + goto normal; + GSendInt( GE_Ok ); + /*Debug (" -> bool %d\n", i);*/ + GSendInt( i ); + break; + normal: + if (!(avptr = FindCfgEnt( td, type ))) { + /*Debug (" -> not found\n");*/ + GSendInt( GE_NoEnt ); + break; + } + switch (type & C_TYPE_MASK) { + default: + /*Debug (" -> unknown type\n");*/ + GSendInt( GE_BadType ); + break; + case C_TYPE_INT: + case C_TYPE_STR: + case C_TYPE_ARGV: +#ifdef XDMCP + case C_TYPE_ARR: +#endif + GSendInt( GE_Ok ); + switch (type & C_TYPE_MASK) { + case C_TYPE_INT: + /*Debug (" -> int %#x (%d)\n", *(int *)avptr, *(int *)avptr);*/ + GSendInt( *(long *)avptr ); + break; + case C_TYPE_STR: + /*Debug (" -> string %\"s\n", *avptr);*/ + GSendStr( *avptr ); + break; + case C_TYPE_ARGV: + /*Debug (" -> sending argv %\"[{s\n", *(char ***)avptr);*/ + GSendArgv( *(char ***)avptr ); + break; +#ifdef XDMCP + case C_TYPE_ARR: + aptr = *(ARRAY8Ptr *)avptr; + /*Debug (" -> sending array %02[*:hhx\n", + aptr->length, aptr->data);*/ + GSendArr( aptr->length, (char *)aptr->data ); + break; +#endif + } + break; + } + break; + case G_ReadDmrc: + Debug( "G_ReadDmrc\n" ); + name = GRecvStr(); + Debug( " user %\"s\n", name ); + if (StrCmp( dmrcuser, name )) { + if (curdmrc) { free( curdmrc ); curdmrc = 0; } + if (dmrcuser) + free( dmrcuser ); + dmrcuser = name; + i = ReadDmrc(); + Debug( " -> status %d\n", i ); + GSendInt( i ); + Debug( " => %\"s\n", curdmrc ); + } else { + if (name) + free( name ); + Debug( " -> status " stringify( GE_Ok ) "\n" ); + GSendInt( GE_Ok ); + Debug( " => keeping old\n" ); + } + break; + case G_GetDmrc: + Debug( "G_GetDmrc\n" ); + name = GRecvStr(); + Debug( " key %\"s\n", name ); + pass = iniEntry( curdmrc, "Desktop", name, 0 ); + Debug( " -> %\"s\n", pass ); + GSendStr( pass ); + if (pass) + free( pass ); + free( name ); + break; +/* case G_ResetDmrc: + Debug ("G_ResetDmrc\n"); + if (newdmrc) { free (newdmrc); newdmrc = 0; } + break; */ + case G_PutDmrc: + Debug( "G_PutDmrc\n" ); + name = GRecvStr(); + Debug( " key %\"s\n", name ); + pass = GRecvStr(); + Debug( " value %\"s\n", pass ); + newdmrc = iniEntry( newdmrc, "Desktop", name, pass ); + free( pass ); + free( name ); + break; + case G_VerifyRootOK: + Debug( "G_VerifyRootOK\n" ); + rootok = TRUE; + goto doverify; + case G_Verify: + Debug( "G_Verify\n" ); + rootok = FALSE; + doverify: + if (curuser) { free( curuser ); curuser = 0; } + if (curpass) { free( curpass ); curpass = 0; } + if (curtype) free( curtype ); + curtype = GRecvStr(); + Debug( " type %\"s\n", curtype ); + cursource = PWSRC_MANUAL; + if (Verify( conv_interact, rootok )) { + Debug( " -> return success\n" ); + GSendInt( V_OK ); + } else + Debug( " -> failure returned\n" ); + break; + case G_AutoLogin: + Debug( "G_AutoLogin\n" ); + DoAutoLogon(); + if (Verify( conv_auto, FALSE )) { + Debug( " -> return success\n" ); + GSendInt( V_OK ); + } else + Debug( " -> failure returned\n" ); + break; + case G_SetupDpy: + Debug( "G_SetupDpy\n" ); + SetupDisplay( 0 ); + td_setup = 0; + GSendInt( 0 ); + break; + default: + return cmd; + } + if (!wreply) + return -1; + } + Debug( "lost connection to greeter\n" ); + return -2; +} + +void +OpenGreeter() +{ + char *name, **env; + static Time_t lastStart; + int cmd; + Cursor xcursor; + + GSet( &grttalk ); + if (greeter) + return; + if (time( 0 ) < lastStart + 10) /* XXX should use some readiness indicator instead */ + SessionExit( EX_UNMANAGE_DPY ); + greeter = 1; + ASPrintf( &name, "greeter for display %s", td->name ); + Debug( "starting %s\n", name ); + + /* Hourglass cursor */ + if ((xcursor = XCreateFontCursor( dpy, XC_watch ))) { + XDefineCursor( dpy, DefaultRootWindow( dpy ), xcursor ); + XFreeCursor( dpy, xcursor ); + } + XFlush( dpy ); + + /* Load system default Resources (if any) */ + LoadXloginResources(); + + grttalk.pipe = &grtproc.pipe; + env = systemEnv( (char *)0 ); + if (GOpen( &grtproc, (char **)0, "_greet", env, name, &td->gpipe )) + SessionExit( EX_UNMANAGE_DPY ); + freeStrArr( env ); + if ((cmd = CtrlGreeterWait( TRUE ))) { + if (cmd != -2) + LogError( "Received unknown or unexpected command %d from greeter\n", cmd ); + CloseGreeter( TRUE ); + SessionExit( EX_UNMANAGE_DPY ); + } + Debug( "%s ready\n", name ); + time( &lastStart ); +} + +int +CloseGreeter( int force ) +{ + int ret; + + if (!greeter) + return EX_NORMAL; + greeter = 0; + ret = GClose (&grtproc, 0, force); + Debug( "greeter for %s stopped\n", td->name ); + if (WaitCode( ret ) > EX_NORMAL && WaitCode( ret ) <= EX_MAX) { + Debug( "greeter-initiated session exit, code %d\n", WaitCode( ret ) ); + SessionExit( WaitCode( ret ) ); + } + return ret; +} + +void +PrepErrorGreet() +{ + if (!greeter) { + OpenGreeter(); + GSendInt( G_ErrorGreet ); + GSendStr( curuser ); + } +} + +static Jmp_buf idleTOJmp; + +/* ARGSUSED */ +static void +IdleTOJmp( int n ATTR_UNUSED ) +{ + Longjmp( idleTOJmp, 1 ); +} + + +static Jmp_buf abortSession; + +/* ARGSUSED */ +static void +catchTerm( int n ATTR_UNUSED ) +{ + Signal( SIGTERM, SIG_IGN ); + Longjmp( abortSession, EX_AL_RESERVER_DPY ); +} + +/* + * We need our own error handlers because we can't be sure what exit code Xlib + * will use, and our Xlib does exit(1) which matches EX_REMANAGE_DPY, which + * can cause a race condition leaving the display wedged. We need to use + * EX_RESERVER_DPY for IO errors, to ensure that the manager waits for the + * server to terminate. For other X errors, we should give up. + */ + +/*ARGSUSED*/ +static int +IOErrorHandler( Display *dspl ATTR_UNUSED ) +{ + LogError( "Fatal X server IO error: %m\n" ); + /* The only X interaction during the session are pings, and those + have an own IOErrorHandler -> not EX_AL_RESERVER_DPY */ + Longjmp( abortSession, EX_RESERVER_DPY ); + /*NOTREACHED*/ + return 0; +} + +/*ARGSUSED*/ +static int +ErrorHandler( Display *dspl ATTR_UNUSED, XErrorEvent *event ) +{ + LogError( "X error\n" ); + if (event->error_code == BadImplementation) + Longjmp( abortSession, EX_UNMANAGE_DPY ); + return 0; +} + +void +ManageSession( struct display *d ) +{ + int ex, cmd; + volatile int clientPid = 0; + volatile Time_t tdiff = 0; +#ifdef WITH_CONSOLE_KIT + char *ck_session_cookie; +#endif + + + td = d; + Debug( "ManageSession %s\n", d->name ); + if ((ex = Setjmp( abortSession ))) { + CloseGreeter( TRUE ); + if (clientPid) + AbortClient( clientPid ); + SessionExit( ex ); + /* NOTREACHED */ + } + (void)XSetIOErrorHandler( IOErrorHandler ); + (void)XSetErrorHandler( ErrorHandler ); + (void)Signal( SIGTERM, catchTerm ); + + (void)Signal( SIGHUP, SIG_IGN ); + + if (Setjmp( grttalk.errjmp )) + Longjmp( abortSession, EX_RESERVER_DPY ); /* EX_RETRY_ONCE */ + +#ifdef XDMCP + if (d->useChooser) + DoChoose(); + /* NOTREACHED */ +#endif + + if (d->hstent->sdRec.how) { + OpenGreeter(); + GSendInt( G_ConfShutdown ); + GSendInt( d->hstent->sdRec.how ); + GSendInt( d->hstent->sdRec.uid ); + GSendStr( d->hstent->sdRec.osname ); + if ((cmd = CtrlGreeterWait( TRUE )) != G_Ready) { + LogError( "Received unknown command %d from greeter\n", cmd ); + CloseGreeter( TRUE ); + } + goto regreet; + } + + tdiff = time( 0 ) - td->hstent->lastExit - td->openDelay; + if (AutoLogon( tdiff )) { + if (!Verify( conv_auto, FALSE )) + goto gcont; + if (greeter) + GSendInt( V_OK ); + } else { + regreet: + OpenGreeter(); + if (Setjmp( idleTOJmp )) { + CloseGreeter( TRUE ); + SessionExit( EX_NORMAL ); + } + Signal( SIGALRM, IdleTOJmp ); + alarm( td->idleTimeout ); +#ifdef XDMCP + if (((d->displayType & d_location) == dLocal) && + d->loginMode >= LOGIN_DEFAULT_REMOTE) + goto choose; +#endif + for (;;) { + Debug( "ManageSession, greeting, tdiff = %d\n", tdiff ); + GSendInt( (*td->autoUser && td->autoDelay && + (tdiff > 0 || td->autoAgain)) ? + G_GreetTimed : G_Greet ); + gcont: + cmd = CtrlGreeterWait( TRUE ); +#ifdef XDMCP + recmd: + if (cmd == G_DChoose) { + choose: + cmd = DoChoose(); + goto recmd; + } + if (cmd == G_DGreet) + continue; +#endif + alarm( 0 ); + if (cmd == G_Ready) + break; + if (cmd == -2) + CloseGreeter( FALSE ); + else { + LogError( "Received unknown command %d from greeter\n", cmd ); + CloseGreeter( TRUE ); + } + goto regreet; + } + } + + if (CloseGreeter( FALSE ) != EX_NORMAL) + goto regreet; + + DeleteXloginResources(); + + if (td_setup) + SetupDisplay( td_setup ); + +#ifdef WITH_CONSOLE_KIT + ck_session_cookie = open_ck_session (getpwnam(curuser), d); + if (!(clientPid = StartClient(ck_session_cookie))) { +#else + if (!(clientPid = StartClient())) { +#endif + LogError( "Client start failed\n" ); + SessionExit( EX_NORMAL ); /* XXX maybe EX_REMANAGE_DPY? -- enable in dm.c! */ + } + Debug( "client Started\n" ); + + /* + * Wait for session to end, + */ + for (;;) { + if (!Setjmp( pingTime )) { + (void)Signal( SIGALRM, catchAlrm ); + (void)alarm( d->pingInterval * 60 ); /* may be 0 */ + (void)Wait4( clientPid ); + (void)alarm( 0 ); + break; + } else { + (void)alarm( 0 ); + if (!PingServer( d )) + catchTerm( SIGTERM ); + } + } + +#ifdef WITH_CONSOLE_KIT + if (ck_session_cookie != NULL) { + close_ck_session (ck_session_cookie); + free (ck_session_cookie); + } +#endif + + /* + * Sometimes the Xsession somehow manages to exit before + * a server crash is noticed - so we sleep a bit and wait + * for being killed. + */ + if (!PingServer( d )) { + Debug( "X server dead upon session exit.\n" ); + if ((d->displayType & d_location) == dLocal) + sleep( 10 ); + SessionExit( EX_AL_RESERVER_DPY ); + } + SessionExit( EX_NORMAL ); /* XXX maybe EX_REMANAGE_DPY? -- enable in dm.c! */ +} + +static int xResLoaded; + +void +LoadXloginResources() +{ + char **args; + char **env; + + if (!xResLoaded && td->resources[0] && access( td->resources, 4 ) == 0) { + env = systemEnv( (char *)0 ); + if ((args = parseArgs( (char **)0, td->xrdb )) && + (args = addStrArr( args, td->resources, -1 ))) + { + Debug( "loading resource file: %s\n", td->resources ); + (void)runAndWait( args, env ); + freeStrArr( args ); + } + freeStrArr( env ); + xResLoaded = TRUE; + } +} + +void +SetupDisplay( const char *arg ) +{ + char **env; + + env = systemEnv( (char *)0 ); + (void)source( env, td->setup, arg ); + freeStrArr( env ); +} + +void +DeleteXloginResources() +{ + int i; + Atom prop; + + if (!xResLoaded) + return; + xResLoaded = FALSE; + prop = XInternAtom( dpy, "SCREEN_RESOURCES", True ); + XDeleteProperty( dpy, RootWindow( dpy, 0 ), XA_RESOURCE_MANAGER ); + if (prop) + for (i = ScreenCount(dpy); --i >= 0; ) + XDeleteProperty( dpy, RootWindow( dpy, i ), prop ); + XSync( dpy, 0 ); +} + + +int +source( char **env, const char *file, const char *arg ) +{ + char **args; + int ret; + + if (file && file[0]) { + Debug( "source %s\n", file ); + if (!(args = parseArgs( (char **)0, file ))) + return waitCompose( 0,0,3 ); + if (arg && !(args = addStrArr( args, arg, -1 ))) + return waitCompose( 0,0,3 ); + ret = runAndWait( args, env ); + freeStrArr( args ); + return ret; + } + return 0; +} + +char ** +inheritEnv( char **env, const char **what ) +{ + char *value; + + for (; *what; ++what) + if ((value = getenv( *what ))) + env = setEnv( env, *what, value ); + return env; +} + +char ** +baseEnv( const char *user ) +{ + char **env; + + env = 0; + +#ifdef _AIX + /* we need the tags SYSENVIRON: and USRENVIRON: in the call to setpenv() */ + env = setEnv( env, "SYSENVIRON:", 0 ); +#endif + + if (user) { + env = setEnv( env, "USER", user ); +#ifdef _AIX + env = setEnv( env, "LOGIN", user ); +#endif + env = setEnv( env, "LOGNAME", user ); + } + +#ifdef _AIX + env = setEnv( env, "USRENVIRON:", 0 ); +#endif + + env = inheritEnv( env, (const char **)exportList ); + + env = setEnv( env, "DISPLAY", + memcmp( td->name, "localhost:", 10 ) ? + td->name : td->name + 9 ); + + if (td->ctrl.path) + env = setEnv( env, "DM_CONTROL", fifoDir ); + + return env; +} + +char ** +systemEnv( const char *user ) +{ + char **env; + + env = baseEnv( user ); + if (td->authFile) + env = setEnv( env, "XAUTHORITY", td->authFile ); + env = setEnv( env, "PATH", td->systemPath ); + env = setEnv( env, "SHELL", td->systemShell ); + return env; +} diff --git a/tdm/backend/sessreg.c b/tdm/backend/sessreg.c new file mode 100644 index 000000000..b507f8141 --- /dev/null +++ b/tdm/backend/sessreg.c @@ -0,0 +1,307 @@ +/* + +Copyright 1990, 1998 The Open Group +Copyright 2005 Oswald Buddenhagen <[email protected]> + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from The Open Group. + +*/ + +/* + + Author: Keith Packard, MIT X Consortium + Lastlog support and dynamic utmp entry allocation + by Andreas Stolcke <[email protected]> + +*/ + +#define _FILE_OFFSET_BITS 64 +#include "dm.h" +#include "dm_error.h" + +#if defined(__svr4__) || defined(__Lynx__) || defined(__QNX__) || defined(__APPLE__) || defined(_SEQUENT_) /*|| defined(USE_PAM)*/ +# define NO_LASTLOG +#endif + +#ifndef NO_LASTLOG +# ifdef HAVE_LASTLOG_H +# include <lastlog.h> +# endif +# ifndef LLOG_FILE +# ifdef _PATH_LASTLOGX +# define LLOG_FILE _PATH_LASTLOGX +# elif defined(_PATH_LASTLOG) +# define LLOG_FILE _PATH_LASTLOG +# else +# define LLOG_FILE "/usr/adm/lastlog" +# endif +# endif +#endif + +#if !defined(__svr4__) && !defined(__QNX__) +# define SESSREG_HOST +#endif + +#ifdef BSD +# if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) +/* *BSD doesn't like a ':0' type entry in utmp */ +# define NO_UTMP +# endif +#endif + +#ifdef BSD_UTMP +# ifndef TTYS_FILE +# define TTYS_FILE "/etc/ttys" +# endif +#endif + +#ifdef _AIX +# define UTL_PFX "xdm/" +# define UTL_OFF strlen(UTL_PFX) +#else +# define UTL_OFF 0 +#endif + +#ifndef BSD_UTMP +static unsigned +crc32s( const unsigned char *str ) +{ + int b; + unsigned crc = 0xffffffff, by; + + for (; *str; str++) { + by = (crc & 255) ^ *str; + for (b = 0; b < 8; b++) + by = (by >> 1) ^ (-(by & 1) & 0xedb88320); + crc = (crc >> 8) ^ by; + } + return crc; +} +#endif + +void +sessreg( struct display *d, int pid, const char *user, int uid ) +{ + char *dot, *colon; + int left, clen; +#ifdef BSD_UTMP + FILE *ttys; + int utmp, slot, freeslot; + STRUCTUTMP entry; +#else + unsigned crc, i; +#endif + int wtmp, c; +#ifndef NO_LASTLOG + int llog; + struct LASTLOG ll; +#endif + STRUCTUTMP ut_ent; + + if (!d->useSessReg) + return; + + bzero( &ut_ent, sizeof(ut_ent) ); + + if (pid) { + strncpy( ut_ent.ut_user, user, sizeof(ut_ent.ut_user) ); +#ifndef BSD_UTMP + ut_ent.ut_pid = pid; + ut_ent.ut_type = USER_PROCESS; + } else { + ut_ent.ut_type = DEAD_PROCESS; +#endif + } + ut_ent.ut_time = time( 0 ); + + colon = strchr( d->name, ':' ); + clen = strlen( colon ); + if (clen > (int)(sizeof(ut_ent.ut_line) - UTL_OFF) - 2) + return; /* uhm, well ... */ + if (colon == d->name) { +#ifndef BSD_UTMP + strncpy( ut_ent.ut_id, d->name, sizeof(ut_ent.ut_id) ); +#endif + left = 0; + } else { +#ifdef SESSREG_HOST +# ifndef BSD_UTMP + if (pid) +# endif + { + if (colon - d->name > (int)sizeof(ut_ent.ut_host)) { + ut_ent.ut_host[0] = '~'; + memcpy( ut_ent.ut_host + 1, + colon - (sizeof(ut_ent.ut_host) - 1), + sizeof(ut_ent.ut_host) - 1 ); + } else + memcpy( ut_ent.ut_host, d->name, colon - d->name ); + } +#endif +#ifndef BSD_UTMP + crc = crc32s( d->name ); + ut_ent.ut_id[0] = crc % 26 + 'A'; + crc /= 26; + for (i = 1; i < sizeof(ut_ent.ut_id); i++) { + c = crc % 62; + crc /= 62; + ut_ent.ut_id[i] = c < 26 ? c + 'A' : + c < 52 ? c - 26 + 'a' : c - 52 + '0'; + } +#endif + left = sizeof(ut_ent.ut_line) - UTL_OFF - clen; + if (colon - d->name <= left) { + clen += colon - d->name; + colon = d->name; + left = 0; + } else { + dot = strchr( d->name, '.' ); + if (dot && dot - d->name < left) { + memcpy( ut_ent.ut_line + UTL_OFF, d->name, left - 1 ); + ut_ent.ut_line[UTL_OFF + left - 1] = '~'; + } else { + memcpy( ut_ent.ut_line + UTL_OFF, d->name, left/2 - 1 ); + ut_ent.ut_line[UTL_OFF + left/2 - 1] = '~'; + if (dot) { + memcpy( ut_ent.ut_line + UTL_OFF + left/2, + dot - (left - left/2 - 1), + left - left/2 - 1 ); + ut_ent.ut_line[UTL_OFF + left - 1] = '~'; + } else + memcpy( ut_ent.ut_line + UTL_OFF + left/2, + colon - (left - left/2), left - left/2 ); + } + } + } +#ifdef UTL_PFX + memcpy( ut_ent.ut_line, UTL_PFX, UTL_OFF ); +#endif + memcpy( ut_ent.ut_line + UTL_OFF + left, colon, clen ); + +#ifndef NO_UTMP +# ifdef BSD_UTMP + if ((utmp = open( UTMP_FILE, O_RDWR )) < 0) + Debug( "cannot open utmp file " UTMP_FILE ": %m\n" ); + else { + + slot = 1; + if (pid) { + if (!(ttys = fopen( TTYS_FILE, "r" ))) + LogWarn( "Cannot open tty file " TTYS_FILE ": %m\n" ); + else { + int column0 = 1; + while ((c = getc( ttys )) != EOF) + if (c == '\n') { + slot++; + column0 = 1; + } else + column0 = 0; + if (!column0) + slot++; + fclose( ttys ); + } + } + freeslot = -1; + lseek( utmp, slot * sizeof(entry), SEEK_SET ); + while (read( utmp, (char *)&entry, sizeof(entry) ) == sizeof(entry)) { + if (!strncmp( entry.ut_line, ut_ent.ut_line, + sizeof(entry.ut_line) )) +# ifdef SESSREG_HOST + if (!strncmp( entry.ut_host, ut_ent.ut_host, + sizeof(entry.ut_host) )) +# endif + goto found; + if (freeslot < 0 && *entry.ut_user == '\0') + freeslot = slot; + slot++; + } + if (!pid) { + Debug( "utmp entry for display %s vanished\n", d->name ); + goto skip; + } + if (freeslot >= 0) + slot = freeslot; + found: + +# ifdef SESSREG_HOST + if (!pid) + bzero( ut_ent.ut_host, sizeof(ut_ent.ut_host) ); +# endif + lseek( utmp, slot * sizeof(ut_ent), SEEK_SET ); + if (write( utmp, (char *)&ut_ent, sizeof(ut_ent) ) != sizeof(ut_ent)) + LogError( "Cannot write utmp file " UTMP_FILE ": %m\n" ); + skip: + close( utmp ); + } +# else + UTMPNAME( UTMP_FILE ); + SETUTENT(); + PUTUTLINE( &ut_ent ); + ENDUTENT(); +# endif +#endif + + if ((wtmp = open( WTMP_FILE, O_WRONLY|O_APPEND )) < 0) + Debug( "cannot open wtmp file " WTMP_FILE ": %m\n" ); + else { + if (write( wtmp, (char *)&ut_ent, sizeof(ut_ent) ) != sizeof(ut_ent)) + LogError( "Cannot write wtmp file " WTMP_FILE ": %m\n" ); + close( wtmp ); + } + +#ifndef NO_LASTLOG + if (pid) { + bzero( (char *)&ll, sizeof(ll) ); + ll.ll_time = ut_ent.ut_time; + memcpy( ll.ll_line, ut_ent.ut_line, sizeof(ll.ll_line) ); + memcpy( ll.ll_host, ut_ent.ut_host, sizeof(ll.ll_host) ); +# ifdef HAVE_UTMPX + updlastlogx( LLOG_FILE, uid, &ll ); +# else + if ((llog = open( LLOG_FILE, O_RDWR )) < 0) + Debug( "cannot open lastlog file " LLOG_FILE ": %m\n" ); + else { + lseek( llog, (off_t)uid * sizeof(ll), SEEK_SET ); + if (write( llog, (char *)&ll, sizeof(ll) ) != sizeof(ll)) + LogError( "Cannot write llog file " WTMP_FILE ": %m\n" ); + close( llog ); + } +# endif + } +#else + (void)uid; +#endif + +#ifdef UTL_PFX + { + char tmp[sizeof("/dev/") + sizeof(ut_ent.ut_line)]; + mkdir( "/dev/" UTL_PFX, 0755 ); + chmod( "/dev/" UTL_PFX, 0755 ); + sprintf( tmp, "/dev/%.*s", sizeof(ut_ent.ut_line), ut_ent.ut_line ); + if (pid) + close( creat( tmp, 0644 ) ); + else + unlink( tmp ); + } +#endif +} diff --git a/tdm/backend/socket.c b/tdm/backend/socket.c new file mode 100644 index 000000000..677a3d32f --- /dev/null +++ b/tdm/backend/socket.c @@ -0,0 +1,418 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2002 Sun Microsystems, Inc. All rights reserved. +Copyright 2002,2004 Oswald Buddenhagen <[email protected]> + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + * + * socket.c - Support for BSD sockets + */ + +#include "dm.h" + +#if defined(XDMCP) && !defined(STREAMSCONN) + +#include "dm_error.h" +#include "dm_socket.h" + +#include <netdb.h> +#include <arpa/inet.h> + +static int c_request_port; + +static int +CreateListeningSocket( struct sockaddr *sock_addr, int salen ) +{ + int fd; +#if defined(IPv6) && defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY) + int on = 0; +#endif + const char *addrstring = "unknown"; +#if defined(IPv6) && defined(AF_INET6) + char addrbuf[INET6_ADDRSTRLEN]; +#endif + + if (!request_port) + return -1; + + if (debugLevel & DEBUG_CORE) { +#if defined(IPv6) && defined(AF_INET6) + void *ipaddr; + if (sock_addr->sa_family == AF_INET6) + ipaddr = & ((struct sockaddr_in6 *)sock_addr)->sin6_addr; + else + ipaddr = & ((struct sockaddr_in *)sock_addr)->sin_addr; + addrstring = + inet_ntop( sock_addr->sa_family, ipaddr, addrbuf, sizeof(addrbuf) ); + +#else + addrstring = inet_ntoa( ((struct sockaddr_in *)sock_addr)->sin_addr ); +#endif + + Debug( "creating socket to listen on port %d of address %s\n", + request_port, addrstring ); + } + + if ((fd = socket( sock_addr->sa_family, SOCK_DGRAM, 0 )) == -1) { + LogError( "XDMCP socket creation failed, errno %d\n", errno ); + return -1; + } +#if defined(IPv6) && defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY) + setsockopt( fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on) ); +#endif + + if (bind( fd, sock_addr, salen ) == -1) { + LogError( "error %d binding socket address %d\n", errno, request_port ); + close( fd ); + return -1; + } + + RegisterCloseOnFork( fd ); + RegisterInput( fd ); + return fd; +} + +struct socklist { + struct socklist *next; + struct socklist *mcastgroups; + struct sockaddr *addr; + int salen; + int addrlen; + int fd; + int ref; /* referenced bit - see UpdateListenSockets */ +}; + +static struct socklist *listensocks; + +static void +DestroyListeningSocket( struct socklist *s ) +{ + struct socklist *g, *n; + + if (s->fd >= 0) { + CloseNClearCloseOnFork( s->fd ); + UnregisterInput( s->fd ); + s->fd = -1; + } + if (s->addr) { + free( s->addr ); + s->addr = NULL; + } + for (g = s->mcastgroups; g; g = n) { + n = g->next; + if (g->addr) + free( g->addr ); + free( g ); + } + s->mcastgroups = NULL; +} + +static struct socklist* +FindInList( struct socklist *list, ARRAY8Ptr addr ) +{ + struct socklist *s; + + for (s = list; s; s = s->next) { + if (s->addrlen == addr->length) { + char *addrdata; + + switch (s->addr->sa_family) { + case AF_INET: + addrdata = (char *) + &(((struct sockaddr_in *)s->addr)->sin_addr.s_addr); + break; +#if defined(IPv6) && defined(AF_INET6) + case AF_INET6: + addrdata = (char *) + &(((struct sockaddr_in6 *)s->addr)->sin6_addr.s6_addr); + break; +#endif + default: + /* Unrecognized address family */ + continue; + } + if (!memcmp( addrdata, addr->data, addr->length )) + return s; + } + } + return NULL; +} + +static struct socklist * +CreateSocklistEntry( ARRAY8Ptr addr ) +{ + struct socklist *s; + + if (!(s = Calloc( 1, sizeof(struct socklist) ))) + return NULL; + + if (addr->length == 4) { /* IPv4 */ + struct sockaddr_in *sin4; + sin4 = Calloc( 1, sizeof(struct sockaddr_in) ); +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + sin4->sin_len = sizeof(struct sockaddr_in); +#endif + s->addr = (struct sockaddr *)sin4; + s->salen = sizeof(struct sockaddr_in); + s->addrlen = sizeof(struct in_addr); + sin4->sin_family = AF_INET; + sin4->sin_port = htons( (short)request_port ); + memcpy( &sin4->sin_addr, addr->data, addr->length ); + } +#if defined(IPv6) && defined(AF_INET6) + else if (addr->length == 16) { /* IPv6 */ + struct sockaddr_in6 *sin6; + sin6 = Calloc( 1, sizeof(struct sockaddr_in6) ); +#ifdef SIN6_LEN + sin6->sin6_len = sizeof(struct sockaddr_in6); +#endif + s->addr = (struct sockaddr *)sin6; + s->salen = sizeof(struct sockaddr_in6); + s->addrlen = sizeof(struct in6_addr); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = htons( (short)request_port ); + memcpy( &sin6->sin6_addr, addr->data, addr->length ); + } +#endif + else { + /* Unknown address type */ + free( s ); + s = NULL; + } + return s; +} + +static void +UpdateListener( ARRAY8Ptr addr, void **closure ) +{ + struct socklist *s; + + *closure = NULL; + + if (addr == NULL) { + ARRAY8 tmpaddr; + struct in_addr in; +#if defined(IPv6) && defined(AF_INET6) + struct in6_addr in6 = in6addr_any; + tmpaddr.length = sizeof(in6); + tmpaddr.data = (CARD8Ptr) &in6; + UpdateListener( &tmpaddr, closure ); + if (*closure) + return; +#endif + in.s_addr = htonl( INADDR_ANY ); + tmpaddr.length = sizeof(in); + tmpaddr.data = (CARD8Ptr) ∈ + UpdateListener( &tmpaddr, closure ); + return; + } + + if (c_request_port == request_port && + (s = FindInList( listensocks, addr ))) + { + *closure = (void *)s; + s->ref = 1; + return; + } + + if (!(s = CreateSocklistEntry( addr ))) + return; + + if ((s->fd = CreateListeningSocket( s->addr, s->salen )) < 0) { + free( s->addr ); + free( s ); + return; + } + s->ref = 1; + s->next = listensocks; + listensocks = s; + *closure = (void *)s; +} + +#define JOIN_MCAST_GROUP 0 +#define LEAVE_MCAST_GROUP 1 + +static void +ChangeMcastMembership( struct socklist *s, struct socklist *g, int op ) +{ + int sockopt; + + switch (s->addr->sa_family) + { + case AF_INET: + { + struct ip_mreq mreq; + memcpy( &mreq.imr_multiaddr, + &((struct sockaddr_in *)g->addr)->sin_addr, + sizeof(struct in_addr) ); + memcpy( &mreq.imr_interface, + &((struct sockaddr_in *)s->addr)->sin_addr, + sizeof(struct in_addr) ); + if (op == JOIN_MCAST_GROUP) + sockopt = IP_ADD_MEMBERSHIP; + else + sockopt = IP_DROP_MEMBERSHIP; + if (setsockopt( s->fd, IPPROTO_IP, sockopt, + &mreq, sizeof(mreq) ) < 0) { + LogError( "XDMCP socket multicast %s to %s failed, errno %d\n", + (op == JOIN_MCAST_GROUP) ? "join" : "drop", + inet_ntoa( ((struct sockaddr_in *)g->addr)->sin_addr ), + errno ); + } else if (debugLevel & DEBUG_CORE) { + Debug( "XDMCP socket multicast %s to %s succeeded\n", + (op == JOIN_MCAST_GROUP) ? "join" : "drop", + inet_ntoa( ((struct sockaddr_in *)g->addr)->sin_addr ) ); + } + return; + } +#if defined(IPv6) && defined(AF_INET6) +# ifndef IPV6_JOIN_GROUP +# define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP +# endif +# ifndef IPV6_LEAVE_GROUP +# define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP +# endif + case AF_INET6: + { + struct ipv6_mreq mreq6; + memcpy( &mreq6.ipv6mr_multiaddr, + &((struct sockaddr_in6 *)g->addr)->sin6_addr, + sizeof(struct in6_addr) ); + mreq6.ipv6mr_interface = 0; /* TODO: fix this */ + if (op == JOIN_MCAST_GROUP) + sockopt = IPV6_JOIN_GROUP; + else + sockopt = IPV6_LEAVE_GROUP; + if (setsockopt( s->fd, IPPROTO_IPV6, sockopt, + &mreq6, sizeof(mreq6) ) < 0) + { + int saveerr = errno; + char addrbuf[INET6_ADDRSTRLEN]; + + inet_ntop( s->addr->sa_family, + &((struct sockaddr_in6 *)g->addr)->sin6_addr, + addrbuf, sizeof(addrbuf) ); + + LogError( "XDMCP socket multicast %s to %s failed, errno %d\n", + (op == JOIN_MCAST_GROUP) ? "join" : "drop", addrbuf, + saveerr ); + } else if (debugLevel & DEBUG_CORE) { + char addrbuf[INET6_ADDRSTRLEN]; + + inet_ntop( s->addr->sa_family, + &((struct sockaddr_in6 *)g->addr)->sin6_addr, + addrbuf, sizeof(addrbuf) ); + + Debug( "XDMCP socket multicast %s to %s succeeded\n", + (op == JOIN_MCAST_GROUP) ? "join" : "drop", addrbuf ); + } + return; + } +#endif + } +} + +static void +UpdateMcastGroup( ARRAY8Ptr addr, void **closure ) +{ + struct socklist *s = (struct socklist *)*closure; + struct socklist *g; + + if (!s) + return; + + /* Already in the group, mark & continue */ + if ((g = FindInList( s->mcastgroups, addr ))) { + g->ref = 1; + return; + } + + /* Need to join the group */ + if (!(g = CreateSocklistEntry( addr ))) + return; + + ChangeMcastMembership( s, g, JOIN_MCAST_GROUP ); + free( g ); +} + +/* Open or close listening sockets to match the current settings read in + from the access database. */ +void +UpdateListenSockets( void ) +{ + struct socklist *s, *g, **ls, **lg; + void *tmpPtr = NULL; + + /* Clear Ref bits - any not marked by UpdateCallback will be closed */ + for (s = listensocks; s; s = s->next) { + s->ref = 0; + for (g = s->mcastgroups; g; g = g->next) + g->ref = 0; + } + ForEachListenAddr( UpdateListener, UpdateMcastGroup, &tmpPtr ); + c_request_port = request_port; + for (ls = &listensocks; (s = *ls); ) + if (!s->ref) { + DestroyListeningSocket( s ); + *ls = s->next; + free( s ); + } else { + ls = &s->next; + for (lg = &s->mcastgroups; (g = *lg); ) + if (!g->ref) { + ChangeMcastMembership( s, g, LEAVE_MCAST_GROUP ); + *lg = g->next; + free( g ); + } else + lg = &g->next; + } +} + +int +AnyListenSockets( void ) +{ + return listensocks != NULL; +} + +int +ProcessListenSockets( FD_TYPE *reads ) +{ + struct socklist *s; + int ret = 0; + + for (s = listensocks; s; s = s->next) + if (FD_ISSET( s->fd, reads )) { + ProcessRequestSocket( s->fd ); + ret = 1; + } + return ret; +} + +#endif /* !STREAMSCONN && XDMCP */ diff --git a/tdm/backend/streams.c b/tdm/backend/streams.c new file mode 100644 index 000000000..f60fb955e --- /dev/null +++ b/tdm/backend/streams.c @@ -0,0 +1,127 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2002,2004 Oswald Buddenhagen <[email protected]> + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + * + * streams.c - Support for STREAMS + */ + +#include "dm.h" + +#if defined(XDMCP) && defined(STREAMSCONN) + +#include "dm_error.h" + +#include <fcntl.h> +#include <tiuser.h> +#include <netconfig.h> +#include <netdir.h> + +static int xdmcpFd = -1, c_request_port; + +void +UpdateListenSockets( void ) +{ + struct t_bind bind_addr; + struct netconfig *nconf; + struct nd_hostserv service; + struct nd_addrlist *servaddrs; + char bindbuf[15]; + int it; + + if (c_request_port == request_port) + return; + c_request_port = request_port; + + if (xdmcpFd != -1) { + CloseNClearCloseOnFork( xdmcpFd ); + UnregisterInput( xdmcpFd ); + xdmcpFd = -1; + } + + if (!request_port) + return; + + Debug( "creating UDP stream %d\n", request_port ); + + nconf = getnetconfigent( "udp" ); + if (!nconf) { + t_error( "getnetconfigent udp" ); + return; + } + + xdmcpFd = t_open( nconf->nc_device, O_RDWR, NULL ); + if (xdmcpFd == -1) { + LogError( "XDMCP stream creation failed\n" ); + t_error( "CreateWellKnownSockets(xdmcpFd): t_open failed" ); + return; + } + + service.h_host = HOST_SELF; + sprintf( bindbuf, "%d", request_port ); + service.h_serv = bindbuf; + netdir_getbyname( nconf, &service, &servaddrs ); + freenetconfigent( nconf ); + + bind_addr.qlen = 5; + bind_addr.addr.buf = servaddrs->n_addrs[0].buf; + bind_addr.addr.len = servaddrs->n_addrs[0].len; + bind_addr.addr.maxlen = servaddrs->n_addrs[0].len; + it = t_bind( xdmcpFd, &bind_addr, &bind_addr ); + netdir_free( (char *)servaddrs, ND_ADDRLIST ); + if (it < 0) { + LogError( "Error binding UDP port %d\n", request_port ); + t_error( "CreateWellKnownSockets(xdmcpFd): t_bind failed" ); + t_close( xdmcpFd ); + xdmcpFd = -1; + return; + } + RegisterCloseOnFork( xdmcpFd ); + RegisterInput( xdmcpFd ); +} + +int +AnyListenSockets( void ) +{ + return xdmcpFd != -1; +} + +int +ProcessRequestSockets( FD_TYPE *reads ) +{ + if (xdmcpFd >= 0 && FD_ISSET( xdmcpFd, reads )) { + ProcessRequestSocket( xdmcpFd ); + return 1; + } + return 0; +} + +#endif /* STREAMSCONN && XDMCP */ diff --git a/tdm/backend/util.c b/tdm/backend/util.c new file mode 100644 index 000000000..7dd58f031 --- /dev/null +++ b/tdm/backend/util.c @@ -0,0 +1,637 @@ +/* + +Copyright 1989, 1998 The Open Group +Copyright 2000-2005 Oswald Buddenhagen <[email protected]> + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + * + * various utility routines + */ + +#include "dm.h" +#include "dm_auth.h" +#include "dm_error.h" + +#include <string.h> +#include <unistd.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> + +#if 0 /*def USG; this was hpux once upon a time */ +# define NEED_UTSNAME +#endif + +#ifdef NEED_UTSNAME +# include <sys/utsname.h> +#endif + +void * +Calloc( size_t nmemb, size_t size ) +{ + void *ret; + + if (!(ret = calloc( nmemb, size ))) + LogOutOfMem(); + return ret; +} + +void * +Malloc( size_t size ) +{ + void *ret; + + if (!(ret = malloc( size ))) + LogOutOfMem(); + return ret; +} + +void * +Realloc( void *ptr, size_t size ) +{ + void *ret; + + if (!(ret = realloc( ptr, size )) && size) + LogOutOfMem(); + return ret; +} + +int +StrCmp( const char *s1, const char *s2 ) +{ + if (s1 == s2) + return 0; + if (!s1) + return -1; + if (!s2) + return 1; + return strcmp( s1, s2 ); +} + +void +WipeStr( char *str ) +{ + if (str) { + bzero( str, strlen( str ) ); + free( str ); + } +} + +#ifndef HAVE_STRNLEN +int +StrNLen( const char *s, int max ) +{ + unsigned l; + + for (l = 0; l < (unsigned)max && s[l]; l++); + return l; +} +#endif + +/* duplicate src; wipe & free old dst string */ +int +ReStrN( char **dst, const char *src, int len ) +{ + char *ndst = 0; + + if (src) { + if (len < 0) + len = strlen( src ); + if (*dst && !memcmp( *dst, src, len ) && !(*dst)[len]) + return 1; + if (!(ndst = Malloc( len + 1 ))) { + WipeStr( *dst ); + *dst = 0; + return 0; + } + memcpy( ndst, src, len ); + ndst[len] = 0; + } + WipeStr( *dst ); /* make an option, if we should become heavily used */ + *dst = ndst; + return 2; +} + +int +ReStr( char **dst, const char *src ) +{ + return ReStrN( dst, src, -1 ); +} + +/* duplicate src */ +int +StrNDup( char **dst, const char *src, int len ) +{ + if (src) { + if (len < 0) + len = strlen( src ); + if (!(*dst = Malloc( len + 1 ))) + return 0; + memcpy( *dst, src, len ); + (*dst)[len] = 0; + } else + *dst = 0; + return 1; +} + +int +StrDup( char **dst, const char *src ) +{ + return StrNDup( dst, src, -1 ); +} + +/* append any number of strings to dst */ +int +StrApp( char **dst, ... ) +{ + int len; + char *bk, *pt, *dp; + va_list va; + + len = 1; + if (*dst) + len += strlen( *dst ); + va_start( va, dst ); + for (;;) { + pt = va_arg( va, char * ); + if (!pt) + break; + len += strlen( pt ); + } + va_end( va ); + if (!(bk = Malloc( len ))) { + if (*dst) { + free( *dst ); + *dst = 0; + } + return 0; + } + dp = bk; + if (*dst) { + len = strlen( *dst ); + memcpy( dp, *dst, len ); + dp += len; + free( *dst ); + } + va_start( va, dst ); + for (;;) { + pt = va_arg( va, char * ); + if (!pt) + break; + len = strlen( pt ); + memcpy( dp, pt, len ); + dp += len; + } + va_end( va ); + *dp = '\0'; + *dst = bk; + return 1; +} + + +char ** +initStrArr( char **arr ) +{ + if (!arr && (arr = Malloc( sizeof(char *) ))) + arr[0] = 0; + return arr; +} + +int +arrLen( char **arr ) +{ + int nu = 0; + if (arr) + for (; arr[nu]; nu++); + return nu; +} + +static char ** +extStrArr( char **arr, char ***strp ) +{ + char **rarr; + int nu; + + nu = arrLen( arr ); + if ((rarr = Realloc( arr, sizeof(char *) * (nu + 2) ))) { + rarr[nu + 1] = 0; + *strp = rarr + nu; + return rarr; + } + freeStrArr( arr ); + return 0; +} + +char ** +addStrArr( char **arr, const char *str, int len ) +{ + char **strp; + + if ((arr = extStrArr( arr, &strp ))) { + if (StrNDup( strp, str, len )) + return arr; + freeStrArr( arr ); + } + return 0; +} + +char ** +xCopyStrArr( int rn, char **arr ) +{ + char **rarr; + int nu; + + nu = arrLen( arr ); + if ((rarr = Calloc( sizeof(char *), nu + rn + 1 ))) + memcpy( rarr + rn, arr, sizeof(char *) * nu ); + return rarr; +} + +void +freeStrArr( char **arr ) +{ + char **a; + + if (arr) { + for (a = arr; *a; a++) + free( *a ); + free( arr ); + } +} + + +char ** +parseArgs( char **argv, const char *string ) +{ + const char *word; + char **strp, *str; + int wlen; + + if (!(argv = initStrArr( argv ))) + return 0; + while (*string) { + if (isspace( *string )) { + string++; + continue; + } + word = string; + wlen = 0; + do { + if (*string == '\\') { + if (!*++string) + string--; + wlen++; + } else if (*string == '\'') { + while (*++string != '\'' && *string) + wlen++; + } else if (*string == '"') { + while (*++string != '"' && *string) { + if (*string == '\\') { + if (!*++string) + string--; + } + wlen++; + } + } else + wlen++; + } while (*++string && !isspace( *string )); + if (!(argv = extStrArr( argv, &strp ))) + return 0; + if (!(*strp = str = Malloc( wlen + 1 ))) { + freeStrArr( argv ); + return 0; + } + do { + if (*word == '\\') { + if (!*++word) + word--; + *str++ = *word; + } else if (*word == '\'') { + while (*++word != '\'' && *word) + *str++ = *word; + } else if (*word == '"') { + while (*++word != '"' && *word) { + if (*word == '\\') { + if (!*++word) + word--; + } + *str++ = *word; + } + } else + *str++ = *word; + } while (*++word && !isspace( *word )); + *str = 0; + } + return argv; +} + + +const char * +getEnv( char **e, const char *name ) +{ + if (e) { + int l = strlen( name ); + for (; *e; e++) + if (!memcmp( *e, name, l ) && (*e)[l] == '=') + return (*e) + l + 1; + } + return 0; +} + +char ** +setEnv( char **e, const char *name, const char *value ) +{ + char **new, **old; + char *newe; + int envsize; + int l; + +#ifdef _AIX + /* setpenv() depends on "SYSENVIRON:", not "SYSENVIRON:=" */ + if (!value) { + if (!StrDup( &newe, name )) + return e; + } else +#endif + { + newe = 0; + if (!StrApp( &newe, name, "=", value, (char *)0 )) + return e; + } + envsize = 0; + if (e) { + l = strlen( name ); + for (old = e; *old; old++) + if (!memcmp( *old, name, l ) && ((*old)[l] == '=' || !(*old)[l])) + { + free( *old ); + *old = newe; + return e; + } + envsize = old - e; + } + if (!(new = (char **) + Realloc( (char *)e, (unsigned)((envsize + 2) * sizeof(char *)) ))) + { + free( newe ); + return e; + } + new[envsize] = newe; + new[envsize + 1] = 0; + return new; +} + +char ** +putEnv( const char *string, char **env ) +{ + char *n; + char *b; + + if (!(b = strchr( string, '=' ))) + return NULL; + if (!StrNDup( &n, string, b - string )) + return NULL; + env = setEnv( env, n, b + 1 ); + free( n ); + return env; +} + +static int +GetHostname( char *buf, int maxlen ) +{ + int len; + +#ifdef NEED_UTSNAME + /* + * same host name crock as in server and xinit. + */ + struct utsname name; + + uname( &name ); + len = strlen( name.nodename ); + if (len >= maxlen) len = maxlen - 1; + memcpy( buf, name.nodename, len ); + buf[len] = '\0'; +#else + buf[0] = '\0'; + (void)gethostname( buf, maxlen ); + buf[maxlen - 1] = '\0'; + len = strlen( buf ); +#endif /* NEED_UTSNAME */ + return len; +} + +static char localHostbuf[256]; +static int gotLocalHostname; + +const char * +localHostname( void ) +{ + if (!gotLocalHostname) + { + GetHostname( localHostbuf, sizeof(localHostbuf) - 1 ); + gotLocalHostname = 1; + } + return localHostbuf; +} + +static int +AtomicIO( ssize_t (*f)( int, void *, size_t ), int fd, void *buf, int count ) +{ + int ret, rlen; + + for (rlen = 0; rlen < count; ) { + dord: + ret = f( fd, (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; +} + +int +Reader( int fd, void *buf, int count ) +{ + return AtomicIO( read, fd, buf, count ); +} + +int +Writer( int fd, const void *buf, int count ) +{ + return AtomicIO( (ssize_t(*)( int, void *, size_t ))write, + fd, (void *)buf, count ); +} + +int +fGets( char *buf, int max, FILE *f ) +{ + int len; + + if (!fgets( buf, max, f )) + return -1; + len = strlen( buf ); + if (len && buf[len - 1] == '\n') + buf[--len] = 0; + return len; +} + +time_t +mTime( const char *fn ) +{ + struct stat st; + + if (stat( fn, &st )) + return -1; + else + return st.st_mtime; +} + +void +randomStr( char *s ) +{ + static const char letters[] = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + unsigned i, rn = secureRandom(); + + for (i = 0; i < 6; i++) { + *s++ = letters[rn % 62]; + rn /= 62; + } + *s = 0; +} + +static int +StrNChrCnt( const char *s, int slen, char c ) +{ + int i, cnt; + + for (i = cnt = 0; i < slen && s[i]; i++) + if (s[i] == c) + cnt++; + return cnt; +} + +/* X -from ip6-addr does not work here, so i don't know whether this is needed. +#define IP6_MAGIC +*/ + +void +ListSessions( int flags, struct display *d, void *ctx, + void (*emitXSess)( struct display *, struct display *, void * ), + void (*emitTTYSess)( STRUCTUTMP *, struct display *, void * ) ) +{ + struct display *di; +#ifdef IP6_MAGIC + int le, dot; +#endif +#ifdef BSD_UTMP + int fd; + struct utmp ut[1]; +#else + STRUCTUTMP *ut; +#endif + + for (di = displays; di; di = di->next) + if (((flags & lstRemote) || (di->displayType & d_location) == dLocal) && + (di->status == remoteLogin || + ((flags & lstPassive) ? di->status == running : di->userSess >= 0))) + emitXSess( di, d, ctx ); + + if (!(flags & lstTTY)) + return; + +#ifdef BSD_UTMP + if ((fd = open( UTMP_FILE, O_RDONLY )) < 0) + return; + while (Reader( fd, ut, sizeof(ut[0]) ) == sizeof(ut[0])) { + if (*ut->ut_user) { /* no idea how to list passive TTYs on BSD */ +#else + SETUTENT(); + while ((ut = GETUTENT())) { + if (ut->ut_type == USER_PROCESS +# if 0 /* list passive TTYs at all? not too sensible, i think. */ + || ((flags & lstPassive) && ut->ut_type == LOGIN_PROCESS) +# endif + ) + { +#endif + if (*ut->ut_host) { /* from remote or x */ + if (!(flags & lstRemote)) + continue; + } else { + /* hack around broken konsole which does not set ut_host. */ + /* this check is probably linux-specific. */ + /* alternatively we could open the device and try VT_OPENQRY. */ + if (memcmp( ut->ut_line, "tty", 3 ) || + !isdigit( ut->ut_line[3] )) + continue; + } + if (StrNChrCnt( ut->ut_line, sizeof(ut->ut_line), ':' )) + continue; /* x login */ + switch (StrNChrCnt( ut->ut_host, sizeof(ut->ut_host), ':' )) { + case 1: /* x terminal */ + continue; + default: +#ifdef IP6_MAGIC + /* unknown - IPv6 makes things complicated */ + le = StrNLen( ut->ut_host, sizeof(ut->ut_host) ); + /* cut off screen number */ + for (dot = le; ut->ut_host[--dot] != ':'; ) + if (ut->ut_host[dot] == '.') { + le = dot; + break; + } + for (di = displays; di; di = di->next) + if (!memcmp( di->name, ut->ut_host, le ) && !di->name[le]) + goto cont; /* x terminal */ + break; + cont: + continue; + case 0: /* no x terminal */ +#endif + break; + } + emitTTYSess( ut, d, ctx ); + } + } +#ifdef BSD_UTMP + close( fd ); +#else + ENDUTENT(); +#endif +} + diff --git a/tdm/backend/xdmauth.c b/tdm/backend/xdmauth.c new file mode 100644 index 000000000..86257c651 --- /dev/null +++ b/tdm/backend/xdmauth.c @@ -0,0 +1,267 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2001,2003 Oswald Buddenhagen <[email protected]> + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + * + * generate authorization data for XDM-AUTHORIZATION-1 as per XDMCP spec + */ + +#include <config.h> + +#ifdef HASXDMAUTH + +#include "dm.h" +#include "dm_auth.h" +#include "dm_error.h" + +static char auth_name[256]; +static int auth_name_len; + +void +XdmInitAuth( unsigned short name_len, const char *name ) +{ + if (name_len > 256) + name_len = 256; + auth_name_len = name_len; + memmove( auth_name, name, name_len ); +} + +/* + * Generate authorization for XDM-AUTHORIZATION-1 + * + * When being used with XDMCP, 8 bytes are generated for the session key + * (sigma), as the random number (rho) is already shared between xdm and + * the server. Otherwise, we'll prepend a random number to pass in the file + * between xdm and the server (16 bytes total) + */ + +static Xauth * +XdmGetAuthHelper( unsigned short namelen, const char *name, int includeRho ) +{ + Xauth *new; + + if (!(new = (Xauth *)Malloc( sizeof(Xauth) ))) + return (Xauth *)0; + new->family = FamilyWild; + new->address_length = 0; + new->address = 0; + new->number_length = 0; + new->number = 0; + if (includeRho) + new->data_length = 16; + else + new->data_length = 8; + + new->data = (char *)Malloc( new->data_length ); + if (!new->data) { + free( (char *)new ); + return (Xauth *)0; + } + new->name = (char *)Malloc( namelen ); + if (!new->name) { + free( (char *)new->data ); + free( (char *)new ); + return (Xauth *)0; + } + memmove( (char *)new->name, name, namelen ); + new->name_length = namelen; + if (!GenerateAuthData( (char *)new->data, new->data_length )) { + free( (char *)new->name ); + free( (char *)new->data ); + free( (char *)new ); + return (Xauth *)0; + } + /* + * set the first byte of the session key to zero as it + * is a DES key and only uses 56 bits + */ + ((char *)new->data)[new->data_length - 8] = '\0'; + Debug( "local server auth %02[*hhx\n", new->data_length, new->data ); + return new; +} + +Xauth * +XdmGetAuth( unsigned short namelen, const char *name ) +{ + return XdmGetAuthHelper( namelen, name, TRUE ); +} + +#ifdef XDMCP + +void +XdmGetXdmcpAuth( struct protoDisplay *pdpy, + unsigned short authorizationNameLen, + const char *authorizationName ) +{ + Xauth *fileauth, *xdmcpauth; + + if (pdpy->fileAuthorization && pdpy->xdmcpAuthorization) + return; + xdmcpauth = XdmGetAuthHelper( authorizationNameLen, authorizationName, + FALSE ); + if (!xdmcpauth) + return; + fileauth = (Xauth *)Malloc( sizeof(Xauth) ); + if (!fileauth) { + XauDisposeAuth( xdmcpauth ); + return; + } + /* build the file auth from the XDMCP auth */ + *fileauth = *xdmcpauth; + fileauth->name = Malloc( xdmcpauth->name_length ); + fileauth->data = Malloc( 16 ); + fileauth->data_length = 16; + if (!fileauth->name || !fileauth->data) { + XauDisposeAuth( xdmcpauth ); + if (fileauth->name) + free( (char *)fileauth->name ); + if (fileauth->data) + free( (char *)fileauth->data ); + free( (char *)fileauth ); + return; + } + /* + * for the file authorization, prepend the random number (rho) + * which is simply the number we've been passing back and + * forth via XDMCP + */ + memmove( fileauth->name, xdmcpauth->name, xdmcpauth->name_length ); + memmove( fileauth->data, pdpy->authenticationData.data, 8 ); + memmove( fileauth->data + 8, xdmcpauth->data, 8 ); + Debug( "accept packet auth %02[*hhx\nauth file auth %02[*hhx\n", + xdmcpauth->data_length, xdmcpauth->data, + fileauth->data_length, fileauth->data ); + /* encrypt the session key for its trip back to the server */ + XdmcpWrap( (unsigned char *)xdmcpauth->data, (unsigned char *)&pdpy->key, + (unsigned char *)xdmcpauth->data, 8 ); + pdpy->fileAuthorization = fileauth; + pdpy->xdmcpAuthorization = xdmcpauth; +} + +#define atox(c) ('0' <= c && c <= '9' ? c - '0' : \ + 'a' <= c && c <= 'f' ? c - 'a' + 10 : \ + 'A' <= c && c <= 'F' ? c - 'A' + 10 : -1) + +static int +HexToBinary( char *key ) +{ + char *out, *in; + int top, bottom; + + in = key + 2; + out= key; + while (in[0] && in[1]) { + top = atox( in[0] ); + if (top == -1) + return 0; + bottom = atox( in[1] ); + if (bottom == -1) + return 0; + *out++ = (top << 4) | bottom; + in += 2; + } + if (in[0]) + return 0; + *out++ = '\0'; + return 1; +} + +/* + * Search the Keys file for the entry matching this display. This + * routine accepts either plain ascii strings for keys, or hex-encoded numbers + */ + +static int +XdmGetKey( struct protoDisplay *pdpy, ARRAY8Ptr displayID ) +{ + FILE *keys; + char line[1024], id[1024], key[1024]; + int keylen; + + Debug( "lookup key for %.*s\n", displayID->length, displayID->data ); + keys = fopen( keyFile, "r" ); + if (!keys) + return FALSE; + while (fgets( line, sizeof(line), keys )) { + if (line[0] == '#' || sscanf( line, "%s %s", id, key ) != 2) + continue; + bzero( line, sizeof(line) ); + Debug( "key entry for %\"s %d bytes\n", id, strlen( key ) ); + if (strlen( id ) == displayID->length && + !strncmp( id, (char *)displayID->data, displayID->length )) + { + if (!strncmp( key, "0x", 2 ) || !strncmp( key, "0X", 2 )) + if (!HexToBinary( key )) + break; + keylen = strlen( key ); + while (keylen < 7) + key[keylen++] = '\0'; + pdpy->key.data[0] = '\0'; + memmove( pdpy->key.data + 1, key, 7 ); + bzero( key, sizeof(key) ); + fclose( keys ); + return TRUE; + } + } + bzero( line, sizeof(line) ); + bzero( key, sizeof(key) ); + fclose( keys ); + return FALSE; +} + +/*ARGSUSED*/ +int +XdmCheckAuthentication( struct protoDisplay *pdpy, + ARRAY8Ptr displayID, + ARRAY8Ptr authenticationName ATTR_UNUSED, + ARRAY8Ptr authenticationData ) +{ + XdmAuthKeyPtr incoming; + + if (!XdmGetKey( pdpy, displayID )) + return FALSE; + if (authenticationData->length != 8) + return FALSE; + XdmcpUnwrap( authenticationData->data, (unsigned char *)&pdpy->key, + authenticationData->data, 8 ); + Debug( "request packet auth %02[*hhx\n", + authenticationData->length, authenticationData->data ); + if (!XdmcpCopyARRAY8( authenticationData, &pdpy->authenticationData )) + return FALSE; + incoming = (XdmAuthKeyPtr)authenticationData->data; + XdmcpIncrementKey( incoming ); + XdmcpWrap( authenticationData->data, (unsigned char *)&pdpy->key, + authenticationData->data, 8 ); + return TRUE; +} + +#endif /* XDMCP */ +#endif /* HASXDMAUTH (covering the entire file) */ diff --git a/tdm/backend/xdmcp.c b/tdm/backend/xdmcp.c new file mode 100644 index 000000000..6abaf5fc8 --- /dev/null +++ b/tdm/backend/xdmcp.c @@ -0,0 +1,1165 @@ +/* + +Copyright 1988, 1998 The Open Group +Copyright 2002 Sun Microsystems, Inc. All rights reserved. +Copyright 2001-2004 Oswald Buddenhagen <[email protected]> + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the copyright holder. + +*/ + +/* + * xdm - display manager daemon + * Author: Keith Packard, MIT X Consortium + * + * xdmcp.c - Support for XDMCP + */ + +#include <config.h> + +#ifdef XDMCP + +#include "dm.h" +#include "dm_error.h" +#include "dm_auth.h" +#include "dm_socket.h" + +#include <sys/types.h> +#include <ctype.h> + +#include <netdb.h> +#if defined(IPv6) && defined(AF_INET6) +# include <arpa/inet.h> +#endif + +/* + * Forward reference + */ +static void broadcast_respond( struct sockaddr *from, int fromlen, int length, int fd ); +static void forward_respond (struct sockaddr *from, int fromlen, int length, int fd); +static void manage( struct sockaddr *from, int fromlen, int length, int fd ); +static void query_respond( struct sockaddr *from, int fromlen, int length, int fd ); +static void request_respond( struct sockaddr *from, int fromlen, int length, int fd ); +static void send_accept( struct sockaddr *to, int tolen, CARD32 sessionID, ARRAY8Ptr authenticationName, ARRAY8Ptr authenticationData, ARRAY8Ptr authorizationName, ARRAY8Ptr authorizationData, int fd ); +static void send_alive( struct sockaddr *from, int fromlen, int length, int fd ); +static void send_decline( struct sockaddr *to, int tolen, ARRAY8Ptr authenticationName, ARRAY8Ptr authenticationData, ARRAY8Ptr status, int fd ); +static void send_failed( struct sockaddr *from, int fromlen, const char *name, CARD32 sessionID, const char *reason, int fd ); +static void send_refuse( struct sockaddr *from, int fromlen, CARD32 sessionID, int fd ); +static void send_unwilling( struct sockaddr *from, int fromlen, ARRAY8Ptr authenticationName, ARRAY8Ptr status, int fd ); +static void send_willing( struct sockaddr *from, int fromlen, ARRAY8Ptr authenticationName, ARRAY8Ptr status, int fd ); + + +static XdmcpBuffer buffer; + +static void +sendForward( CARD16 connectionType, ARRAY8Ptr address, char *closure ) +{ +#ifdef AF_INET + struct sockaddr_in in_addr; +#endif +#if defined(IPv6) && defined(AF_INET6) + struct sockaddr_in6 in6_addr; +#endif +#ifdef AF_DECnet +#endif + struct sockaddr *addr; + int addrlen; + + switch (connectionType) { +#ifdef AF_INET + case FamilyInternet: + addr = (struct sockaddr *)&in_addr; + bzero( (char *)&in_addr, sizeof(in_addr) ); +# ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + in_addr.sin_len = sizeof(in_addr); +# endif + in_addr.sin_family = AF_INET; + in_addr.sin_port = htons( (short)XDM_UDP_PORT ); + if (address->length != 4) + return; + memmove( (char *)&in_addr.sin_addr, address->data, address->length ); + addrlen = sizeof(struct sockaddr_in); + break; +#endif +#if defined(IPv6) && defined(AF_INET6) + case FamilyInternet6: + addr = (struct sockaddr *)&in6_addr; + bzero( (char *)&in6_addr, sizeof(in6_addr) ); +# ifdef SIN6_LEN + in6_addr.sin6_len = sizeof(in6_addr); +# endif + in6_addr.sin6_family = AF_INET6; + in6_addr.sin6_port = htons( (short)XDM_UDP_PORT ); + if (address->length != 16) + return; + memmove( (char *)&in6_addr.sin6_addr, address->data, address->length ); + addrlen = sizeof(struct sockaddr_in6); + break; +#endif +#ifdef AF_DECnet + case FamilyDECnet: +#endif + default: + return; + } + XdmcpFlush( (int)closure, &buffer, (XdmcpNetaddr)addr, addrlen ); + return; +} + +static void +ClientAddress( struct sockaddr *from, + ARRAY8Ptr addr, /* return */ + ARRAY8Ptr port, /* return */ + CARD16 *type ) /* return */ +{ + int length, family; + char *data; + + data = NetaddrPort( (XdmcpNetaddr)from, &length ); + XdmcpAllocARRAY8( port, length ); + memmove( port->data, data, length ); + port->length = length; + + family = ConvertAddr( (XdmcpNetaddr)from, &length, &data ); + XdmcpAllocARRAY8( addr, length ); + memmove( addr->data, data, length ); + addr->length = length; + + *type = family; +} + +static void +all_query_respond( struct sockaddr *from, int fromlen, + ARRAYofARRAY8Ptr authenticationNames, + xdmOpCode type, int fd ) +{ + ARRAY8Ptr authenticationName; + ARRAY8 status; + ARRAY8 addr; + CARD16 connectionType; + int family; + int length; + + family = ConvertAddr( (XdmcpNetaddr)from, &length, &(addr.data) ); + addr.length = length; /* convert int to short */ + Debug( "all_query_respond: conntype=%d, addr=%02[*:hhx\n", + family, addr.length, addr.data ); + if (family < 0) + return; + connectionType = family; + + if (type == INDIRECT_QUERY) + RememberIndirectClient( &addr, connectionType ); + else + ForgetIndirectClient( &addr, connectionType ); + + authenticationName = ChooseAuthentication( authenticationNames ); + if (Willing( &addr, connectionType, authenticationName, &status, type )) + send_willing( from, fromlen, authenticationName, &status, fd ); + else + if (type == QUERY) + send_unwilling( from, fromlen, authenticationName, &status, fd ); + XdmcpDisposeARRAY8( &status ); +} + +static void +indirect_respond( struct sockaddr *from, int fromlen, int length, int fd ) +{ + ARRAYofARRAY8 queryAuthenticationNames; + ARRAY8 clientAddress; + ARRAY8 clientPort; + CARD16 connectionType; + int expectedLen; + int i; + XdmcpHeader header; + int localHostAsWell; + + Debug( "<indirect> respond %d\n", length ); + if (!XdmcpReadARRAYofARRAY8( &buffer, &queryAuthenticationNames )) + return; + expectedLen = 1; + for (i = 0; i < (int)queryAuthenticationNames.length; i++) + expectedLen += 2 + queryAuthenticationNames.data[i].length; + if (length == expectedLen) { + ClientAddress( from, &clientAddress, &clientPort, &connectionType ); + /* + * set up the forward query packet + */ + header.version = XDM_PROTOCOL_VERSION; + header.opcode = (CARD16)FORWARD_QUERY; + header.length = 0; + header.length += 2 + clientAddress.length; + header.length += 2 + clientPort.length; + header.length += 1; + for (i = 0; i < (int)queryAuthenticationNames.length; i++) + header.length += 2 + queryAuthenticationNames.data[i].length; + XdmcpWriteHeader( &buffer, &header ); + XdmcpWriteARRAY8( &buffer, &clientAddress ); + XdmcpWriteARRAY8( &buffer, &clientPort ); + XdmcpWriteARRAYofARRAY8( &buffer, &queryAuthenticationNames ); + + localHostAsWell = + ForEachMatchingIndirectHost( &clientAddress, connectionType, + sendForward, (char *)fd ); + + XdmcpDisposeARRAY8( &clientAddress ); + XdmcpDisposeARRAY8( &clientPort ); + if (localHostAsWell) + all_query_respond( from, fromlen, &queryAuthenticationNames, + INDIRECT_QUERY, fd ); + } else + Debug( "<indirect> length error got %d expect %d\n", + length, expectedLen ); + XdmcpDisposeARRAYofARRAY8( &queryAuthenticationNames ); +} + +void +ProcessRequestSocket( int fd ) +{ + XdmcpHeader header; +#if defined(IPv6) && defined(AF_INET6) + struct sockaddr_storage addr; +#else + struct sockaddr addr; +#endif + int addrlen = sizeof(addr); + + Debug( "ProcessRequestSocket\n" ); + bzero( (char *)&addr, sizeof(addr) ); + if (!XdmcpFill( fd, &buffer, (XdmcpNetaddr)&addr, &addrlen )) { + Debug( "XdmcpFill failed\n" ); + return; + } + if (!XdmcpReadHeader( &buffer, &header )) { + Debug( "XdmcpReadHeader failed\n" ); + return; + } + if (header.version != XDM_PROTOCOL_VERSION) { + Debug( "XDMCP header version read was %d, expected %d\n", + header.version, XDM_PROTOCOL_VERSION ); + return; + } + Debug( "header: %d %d %d\n", header.version, header.opcode, header.length ); + switch (header.opcode) { + case BROADCAST_QUERY: + broadcast_respond( (struct sockaddr *)&addr, addrlen, header.length, fd ); + break; + case QUERY: + query_respond( (struct sockaddr *)&addr, addrlen, header.length, fd ); + break; + case INDIRECT_QUERY: + indirect_respond( (struct sockaddr *)&addr, addrlen, header.length, fd ); + break; + case FORWARD_QUERY: + forward_respond ((struct sockaddr *)&addr, addrlen, header.length, fd); + break; + case REQUEST: + request_respond( (struct sockaddr *)&addr, addrlen, header.length, fd ); + break; + case MANAGE: + manage( (struct sockaddr *)&addr, addrlen, header.length, fd ); + break; + case KEEPALIVE: + send_alive( (struct sockaddr *)&addr, addrlen, header.length, fd ); + break; + } +} + +/* + * respond to a request on the UDP socket. + */ + +static void +direct_query_respond( struct sockaddr *from, int fromlen, + int length, xdmOpCode type, int fd ) +{ + ARRAYofARRAY8 queryAuthenticationNames; + int expectedLen; + int i; + + if (!XdmcpReadARRAYofARRAY8( &buffer, &queryAuthenticationNames )) + return; + expectedLen = 1; + for (i = 0; i < (int)queryAuthenticationNames.length; i++) + expectedLen += 2 + queryAuthenticationNames.data[i].length; + if (length == expectedLen) + all_query_respond( from, fromlen, &queryAuthenticationNames, type, fd ); + XdmcpDisposeARRAYofARRAY8( &queryAuthenticationNames ); +} + +static void +query_respond( struct sockaddr *from, int fromlen, int length, int fd ) +{ + Debug( "<query> respond %d\n", length ); + direct_query_respond( from, fromlen, length, QUERY, fd ); +} + +static void +broadcast_respond( struct sockaddr *from, int fromlen, int length, int fd ) +{ + direct_query_respond( from, fromlen, length, BROADCAST_QUERY, fd ); +} + +/* computes an X display name */ + +static char * +NetworkAddressToName( CARD16 connectionType, ARRAY8Ptr connectionAddress, + struct sockaddr *originalAddress, CARD16 displayNumber ) +{ + switch (connectionType) { + case FamilyInternet: +#if defined(IPv6) && defined(AF_INET6) + case FamilyInternet6: +#endif + { + CARD8 *data; + struct hostent *hostent; + char *hostname = NULL; + char *name; + const char *localhost; + int multiHomed = 0; + int type; +#if defined(IPv6) && defined(AF_INET6) + struct addrinfo *ai = NULL, *nai, hints; + char dotted[INET6_ADDRSTRLEN]; + + if (connectionType == FamilyInternet6) + type = AF_INET6; + else +#endif + type = AF_INET; + + data = connectionAddress->data; + hostent = gethostbyaddr( (char *)data, + connectionAddress->length, type ); + if (hostent) { + if (sourceAddress) { +#if defined(IPv6) && defined(AF_INET6) + bzero( &hints, sizeof(hints) ); + hints.ai_flags = AI_CANONNAME; + if (!getaddrinfo( hostent->h_name, NULL, &hints, &ai )) { + hostname = ai->ai_canonname; + for (nai = ai->ai_next; nai; nai = nai->ai_next) + if (ai->ai_protocol == nai->ai_protocol && + memcmp( ai->ai_addr, nai->ai_addr, + ai->ai_addrlen )) + multiHomed = 1; + } +#else + hostent = gethostbyname( hostent->h_name ); + if (hostent && hostent->h_addrtype == AF_INET) { + multiHomed = hostent->h_addr_list[1] != NULL; + hostname = hostent->h_name; + } +#endif + } else + hostname = hostent->h_name; + } + + localhost = localHostname(); + + /* + * protect against bogus host names + */ + if (hostname && *hostname && *hostname != '.' && !multiHomed) { + if (!strcmp( localhost, hostname )) + ASPrintf( &name, "localhost:%d", displayNumber ); + else { + if (removeDomainname) { + char *remoteDot; + char *localDot; + + /* check for a common domain name. This + * could reduce names by recognising common + * super-domain names as well, but I don't think + * this is as useful, and will confuse more + * people + */ + if ((localDot = strchr( localhost, '.' )) && + (remoteDot = strchr( hostname, '.' ))) + { + /* smash the name in place; it won't + * be needed later. + */ + if (!strcmp( localDot+1, remoteDot+1 )) + *remoteDot = '\0'; + } + } + + ASPrintf( &name, "%s:%d", hostname, displayNumber ); + } + } else { +#if defined(IPv6) && defined(AF_INET6) + if (multiHomed) { + if (connectionType == FamilyInternet) { + data = (CARD8 *) + &((struct sockaddr_in *)originalAddress)->sin_addr; + } else { + data = (CARD8 *) + &((struct sockaddr_in6 *)originalAddress)->sin6_addr; + } + } + inet_ntop( type, data, dotted, sizeof(dotted) ); + ASPrintf( &name, "%s:%d", dotted, displayNumber ); +#else + if (multiHomed) + data = (CARD8 *) + &((struct sockaddr_in *)originalAddress)->sin_addr; + ASPrintf( &name, "%[4|'.'hhu:%d", data, displayNumber ); +#endif + } +#if defined(IPv6) && defined(AF_INET6) + if (ai) + freeaddrinfo( ai ); +#endif + return name; + } +#ifdef DNET + case FamilyDECnet: + return NULL; +#endif /* DNET */ + default: + return NULL; + } +} + +/*ARGSUSED*/ +static void +forward_respond ( struct sockaddr *from, int fromlen ATTR_UNUSED, + int length, int fd) +{ + ARRAY8 clientAddress; + ARRAY8 clientPort; + ARRAYofARRAY8 authenticationNames; + struct sockaddr *client; + int clientlen; + int expectedLen; + int i; + + Debug( "<forward> respond %d\n", length ); + clientAddress.length = 0; + clientAddress.data = 0; + clientPort.length = 0; + clientPort.data = 0; + authenticationNames.length = 0; + authenticationNames.data = 0; + if (XdmcpReadARRAY8( &buffer, &clientAddress ) && + XdmcpReadARRAY8( &buffer, &clientPort ) && + XdmcpReadARRAYofARRAY8( &buffer, &authenticationNames )) + { + expectedLen = 0; + expectedLen += 2 + clientAddress.length; + expectedLen += 2 + clientPort.length; + expectedLen += 1; /* authenticationNames */ + for (i = 0; i < (int)authenticationNames.length; i++) + expectedLen += 2 + authenticationNames.data[i].length; + if (length == expectedLen) { + int j; + + j = 0; + for (i = 0; i < (int)clientPort.length; i++) + j = j * 256 + clientPort.data[i]; + Debug( "<forward> client address (port %d) %[*hhu\n", j, + clientAddress.length, clientAddress.data ); + switch (from->sa_family) { +#ifdef AF_INET + case AF_INET: + { + struct sockaddr_in in_addr; + + if (clientAddress.length != 4 || clientPort.length != 2) + goto badAddress; + bzero( (char *)&in_addr, sizeof(in_addr) ); +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + in_addr.sin_len = sizeof(in_addr); +#endif + in_addr.sin_family = AF_INET; + memmove( &in_addr.sin_addr, clientAddress.data, 4 ); + memmove( (char *)&in_addr.sin_port, clientPort.data, 2 ); + client = (struct sockaddr *)&in_addr; + clientlen = sizeof(in_addr); + all_query_respond( client, clientlen, &authenticationNames, + FORWARD_QUERY, fd ); + } + break; +#endif +#if defined(IPv6) && defined(AF_INET6) + case AF_INET6: + { + struct sockaddr_in6 in6_addr; + + if (clientAddress.length != 16 || clientPort.length != 2) + goto badAddress; + bzero( (char *)&in6_addr, sizeof(in6_addr) ); +#ifdef SIN6_LEN + in6_addr.sin6_len = sizeof(in6_addr); +#endif + in6_addr.sin6_family = AF_INET6; + memmove( &in6_addr,clientAddress.data,clientAddress.length ); + memmove( (char *)&in6_addr.sin6_port, clientPort.data, 2 ); + client = (struct sockaddr *)&in6_addr; + clientlen = sizeof(in6_addr); + all_query_respond( client, clientlen, &authenticationNames, + FORWARD_QUERY, fd ); + } + break; +#endif +#ifdef AF_UNIX + case AF_UNIX: + { + struct sockaddr_un un_addr; + + if (clientAddress.length >= sizeof(un_addr.sun_path)) + goto badAddress; + bzero( (char *)&un_addr, sizeof(un_addr) ); + un_addr.sun_family = AF_UNIX; + memmove( un_addr.sun_path, clientAddress.data, clientAddress.length ); + un_addr.sun_path[clientAddress.length] = '\0'; + client = (struct sockaddr *)&un_addr; +#if defined(HAVE_STRUCT_SOCKADDR_IN_SIN_LEN) && !defined(__Lynx__) && defined(UNIXCONN) + un_addr.sun_len = strlen( un_addr.sun_path ); + clientlen = SUN_LEN( &un_addr ); +#else + clientlen = sizeof(un_addr); +#endif + all_query_respond( client, clientlen, &authenticationNames, + FORWARD_QUERY, fd ); + } + break; +#endif +#ifdef AF_CHAOS + case AF_CHAOS: + goto badAddress; +#endif +#ifdef AF_DECnet + case AF_DECnet: + goto badAddress; +#endif + } + } else + Debug( "<forward> length error got %d expect %d\n", length, expectedLen ); + } + badAddress: + XdmcpDisposeARRAY8( &clientAddress ); + XdmcpDisposeARRAY8( &clientPort ); + XdmcpDisposeARRAYofARRAY8( &authenticationNames ); +} + +static ARRAY8 Hostname; + +static void +send_willing( struct sockaddr *from, int fromlen, + ARRAY8Ptr authenticationName, ARRAY8Ptr status, int fd ) +{ + XdmcpHeader header; + + Debug( "send <willing> %.*s %.*s\n", authenticationName->length, + authenticationName->data, + status->length, + status->data ); + header.version = XDM_PROTOCOL_VERSION; + header.opcode = (CARD16)WILLING; + header.length = + 6 + authenticationName->length + Hostname.length + status->length; + XdmcpWriteHeader( &buffer, &header ); + XdmcpWriteARRAY8( &buffer, authenticationName ); + XdmcpWriteARRAY8( &buffer, &Hostname ); + XdmcpWriteARRAY8( &buffer, status ); + XdmcpFlush( fd, &buffer, (XdmcpNetaddr)from, fromlen ); +} + +static void +send_unwilling( struct sockaddr *from, int fromlen, + ARRAY8Ptr authenticationName, ARRAY8Ptr status, int fd ) +{ + XdmcpHeader header; + + Debug( "send <unwilling> %.*s %.*s\n", authenticationName->length, + authenticationName->data, + status->length, + status->data ); + header.version = XDM_PROTOCOL_VERSION; + header.opcode = (CARD16)UNWILLING; + header.length = 4 + Hostname.length + status->length; + XdmcpWriteHeader( &buffer, &header ); + XdmcpWriteARRAY8( &buffer, &Hostname ); + XdmcpWriteARRAY8( &buffer, status ); + XdmcpFlush( fd, &buffer, (XdmcpNetaddr)from, fromlen ); +} + +static unsigned long globalSessionID; + +#define NextSessionID() (++globalSessionID) + +void init_session_id( void ) +{ + /* Set randomly so we are unlikely to reuse id's from a previous + * incarnation so we don't say "Alive" to those displays. + * Start with low digits 0 to make debugging easier. + */ + globalSessionID = (time( (Time_t *)0 ) & 0x7fff) * 16000; + + Hostname.data = (char *)localHostname(); + Hostname.length = strlen( Hostname.data ); +} + +static ARRAY8 outOfMemory = { (CARD16)13, (CARD8Ptr)"Out of memory" }; +static ARRAY8 noValidAddr = { (CARD16)16, (CARD8Ptr)"No valid address" }; +static ARRAY8 noValidAuth = { (CARD16)22, (CARD8Ptr)"No valid authorization" }; +static ARRAY8 noAuthentic = { (CARD16)29, (CARD8Ptr)"XDM has no authentication key" }; + +static void +request_respond( struct sockaddr *from, int fromlen, int length, int fd ) +{ + CARD16 displayNumber; + ARRAY16 connectionTypes; + ARRAYofARRAY8 connectionAddresses; + ARRAY8 authenticationName; + ARRAY8 authenticationData; + ARRAYofARRAY8 authorizationNames; + ARRAY8 manufacturerDisplayID; + ARRAY8Ptr reason = 0; + int expectlen; + int i, j; + struct protoDisplay *pdpy; + ARRAY8 authorizationName, authorizationData; + ARRAY8Ptr connectionAddress; + + Debug( "<request> respond %d\n", length ); + connectionTypes.data = 0; + connectionAddresses.data = 0; + authenticationName.data = 0; + authenticationData.data = 0; + authorizationNames.data = 0; + authorizationName.length = 0; + authorizationData.length = 0; + manufacturerDisplayID.data = 0; + if (XdmcpReadCARD16( &buffer, &displayNumber ) && + XdmcpReadARRAY16( &buffer, &connectionTypes ) && + XdmcpReadARRAYofARRAY8( &buffer, &connectionAddresses ) && + XdmcpReadARRAY8( &buffer, &authenticationName ) && + XdmcpReadARRAY8( &buffer, &authenticationData ) && + XdmcpReadARRAYofARRAY8( &buffer, &authorizationNames ) && + XdmcpReadARRAY8( &buffer, &manufacturerDisplayID )) + { + expectlen = 0; + expectlen += 2; /* displayNumber */ + expectlen += 1 + 2 * connectionTypes.length; /* connectionTypes */ + expectlen += 1; /* connectionAddresses */ + for (i = 0; i < (int)connectionAddresses.length; i++) + expectlen += 2 + connectionAddresses.data[i].length; + expectlen += 2 + authenticationName.length; /* authenticationName */ + expectlen += 2 + authenticationData.length; /* authenticationData */ + expectlen += 1; /* authoriationNames */ + for (i = 0; i < (int)authorizationNames.length; i++) + expectlen += 2 + authorizationNames.data[i].length; + expectlen += 2 + manufacturerDisplayID.length; /* displayID */ + if (expectlen != length) { + Debug( "<request> length error got %d expect %d\n", + length, expectlen ); + goto abort; + } + if (connectionTypes.length == 0 || + connectionAddresses.length != connectionTypes.length) + { + reason = &noValidAddr; + pdpy = 0; + goto decline; + } + pdpy = FindProtoDisplay( (XdmcpNetaddr)from, fromlen, displayNumber ); + if (!pdpy) { + + /* Check this Display against the Manager's policy */ + reason = Accept( from, fromlen, displayNumber ); + if (reason) + goto decline; + + /* Check the Display's stream services against Manager's policy */ + i = SelectConnectionTypeIndex( &connectionTypes, + &connectionAddresses ); + if (i < 0) { + reason = &noValidAddr; + goto decline; + } + + /* The Manager considers this a new session */ + connectionAddress = &connectionAddresses.data[i]; + pdpy = NewProtoDisplay( (XdmcpNetaddr)from, fromlen, displayNumber, + connectionTypes.data[i], connectionAddress, + NextSessionID() ); + Debug( "NewProtoDisplay %p\n", pdpy ); + if (!pdpy) { + reason = &outOfMemory; + goto decline; + } + } + if (authorizationNames.length == 0) + j = 0; + else + j = SelectAuthorizationTypeIndex( &authenticationName, + &authorizationNames ); + if (j < 0) { + reason = &noValidAuth; + goto decline; + } + if (!CheckAuthentication( pdpy, + &manufacturerDisplayID, + &authenticationName, + &authenticationData )) + { + reason = &noAuthentic; + goto decline; + } + if (j < (int)authorizationNames.length) { + Xauth *auth; + SetProtoDisplayAuthorization( pdpy, + (unsigned short)authorizationNames.data[j].length, + (char *)authorizationNames.data[j].data ); + auth = pdpy->xdmcpAuthorization; + if (!auth) + auth = pdpy->fileAuthorization; + if (auth) { + authorizationName.length = auth->name_length; + authorizationName.data = (CARD8Ptr) auth->name; + authorizationData.length = auth->data_length; + authorizationData.data = (CARD8Ptr) auth->data; + } + } + if (pdpy) { + send_accept( from, fromlen, pdpy->sessionID, + &authenticationName, + &authenticationData, + &authorizationName, + &authorizationData, fd ); + } else { + decline: + send_decline( from, fromlen, &authenticationName, + &authenticationData, + reason, fd ); + if (pdpy) + DisposeProtoDisplay( pdpy ); + } + } + abort: + XdmcpDisposeARRAY16( &connectionTypes ); + XdmcpDisposeARRAYofARRAY8( &connectionAddresses ); + XdmcpDisposeARRAY8( &authenticationName ); + XdmcpDisposeARRAY8( &authenticationData ); + XdmcpDisposeARRAYofARRAY8( &authorizationNames ); + XdmcpDisposeARRAY8( &manufacturerDisplayID ); +} + +static void +send_accept( struct sockaddr *to, int tolen, CARD32 sessionID, + ARRAY8Ptr authenticationName, ARRAY8Ptr authenticationData, + ARRAY8Ptr authorizationName, ARRAY8Ptr authorizationData, + int fd ) +{ + XdmcpHeader header; + + Debug( "<accept> session ID %ld\n", (long)sessionID ); + header.version = XDM_PROTOCOL_VERSION; + header.opcode = (CARD16)ACCEPT; + header.length = 4; /* session ID */ + header.length += 2 + authenticationName->length; + header.length += 2 + authenticationData->length; + header.length += 2 + authorizationName->length; + header.length += 2 + authorizationData->length; + XdmcpWriteHeader( &buffer, &header ); + XdmcpWriteCARD32( &buffer, sessionID ); + XdmcpWriteARRAY8( &buffer, authenticationName ); + XdmcpWriteARRAY8( &buffer, authenticationData ); + XdmcpWriteARRAY8( &buffer, authorizationName ); + XdmcpWriteARRAY8( &buffer, authorizationData ); + XdmcpFlush( fd, &buffer, (XdmcpNetaddr)to, tolen ); +} + +static void +send_decline( struct sockaddr *to, int tolen, + ARRAY8Ptr authenticationName, ARRAY8Ptr authenticationData, + ARRAY8Ptr status, int fd ) +{ + XdmcpHeader header; + + Debug( "<decline> %.*s\n", status->length, status->data ); + header.version = XDM_PROTOCOL_VERSION; + header.opcode = (CARD16)DECLINE; + header.length = 0; + header.length += 2 + status->length; + header.length += 2 + authenticationName->length; + header.length += 2 + authenticationData->length; + XdmcpWriteHeader( &buffer, &header ); + XdmcpWriteARRAY8( &buffer, status ); + XdmcpWriteARRAY8( &buffer, authenticationName ); + XdmcpWriteARRAY8( &buffer, authenticationData ); + XdmcpFlush( fd, &buffer, (XdmcpNetaddr)to, tolen ); +} + +static void +manage( struct sockaddr *from, int fromlen, int length, int fd ) +{ + CARD32 sessionID; + CARD16 displayNumber; + ARRAY8 displayClass; + int expectlen; + struct protoDisplay *pdpy; + struct display *d; + char *name = NULL; + char *class2 = NULL; + XdmcpNetaddr from_save; + ARRAY8 clientAddress, clientPort; + CARD16 connectionType; + + Debug( "<manage> %d\n", length ); + displayClass.data = 0; + displayClass.length = 0; + if (XdmcpReadCARD32( &buffer, &sessionID ) && + XdmcpReadCARD16( &buffer, &displayNumber ) && + XdmcpReadARRAY8( &buffer, &displayClass )) + { + expectlen = 4 + /* session ID */ + 2 + /* displayNumber */ + 2 + displayClass.length; /* displayClass */ + if (expectlen != length) { + Debug( "<manage> length error got %d expect %d\n", length, expectlen ); + goto abort; + } + pdpy = FindProtoDisplay( (XdmcpNetaddr)from, fromlen, displayNumber ); + Debug( "<manage> session ID %ld, pdpy %p\n", (long)sessionID, pdpy ); + if (!pdpy || pdpy->sessionID != sessionID) { + /* + * We may have already started a session for this display + * but it hasn't seen the response in the form of an + * XOpenDisplay() yet. So check if it is in the list of active + * displays, and if so check that the session id's match. + * If all this is true, then we have a duplicate request that + * can be ignored. + */ + if (!pdpy && + (d = FindDisplayByAddress( (XdmcpNetaddr)from, fromlen, + displayNumber )) && + d->sessionID == sessionID) + { + Debug( "manage: got duplicate pkt, ignoring\n" ); + goto abort; + } + Debug( "session ID %ld refused\n", (long)sessionID ); + if (pdpy) + Debug( "existing session ID %ld\n", (long)pdpy->sessionID ); + send_refuse( from, fromlen, sessionID, fd ); + } else { + name = NetworkAddressToName( pdpy->connectionType, + &pdpy->connectionAddress, + from, + pdpy->displayNumber ); + if (!name) { + Debug( "could not compute display name\n" ); + send_failed( from, fromlen, "(no name)", sessionID, + "out of memory", fd ); + goto abort; + } + Debug( "computed display name: %s\n", name ); + if ((d = FindDisplayByName( name ))) { + Debug( "terminating active session for %s\n", d->name ); + StopDisplay( d ); + } + if (displayClass.length) { + if (!StrNDup( &class2, (char *)displayClass.data, + displayClass.length )) + { + send_failed( from, fromlen, name, sessionID, + "out of memory", fd ); + goto abort; + } + } + if (!(from_save = (XdmcpNetaddr)Malloc( fromlen ))) { + send_failed( from, fromlen, name, sessionID, + "out of memory", fd ); + goto abort; + } + memmove( from_save, from, fromlen ); + if (!(d = NewDisplay( name ))) { + free( (char *)from_save ); + send_failed( from, fromlen, name, sessionID, + "out of memory", fd ); + goto abort; + } + d->class2 = class2; + class2 = 0; + d->displayType = dForeign | dTransient | dFromXDMCP; + d->sessionID = pdpy->sessionID; + d->from.data = (unsigned char *)from_save; + d->from.length = fromlen; + d->displayNumber = pdpy->displayNumber; + ClientAddress( from, &clientAddress, &clientPort, + &connectionType ); + d->useChooser = 0; + d->xdmcpFd = fd; + if (IsIndirectClient( &clientAddress, connectionType )) { + Debug( "IsIndirectClient\n" ); + ForgetIndirectClient( &clientAddress, connectionType ); + if (UseChooser( &clientAddress, connectionType )) { + d->useChooser = 1; + Debug( "use chooser for %s\n", d->name ); + } + } + d->clientAddr = clientAddress; + d->connectionType = connectionType; + d->remoteHost = NetworkAddressToHostname (pdpy->connectionType, + &pdpy->connectionAddress); + + XdmcpDisposeARRAY8( &clientPort ); + if (pdpy->fileAuthorization) { + d->authorizations = (Xauth **)Malloc( sizeof(Xauth *) ); + if (!d->authorizations) { + free( (char *)from_save ); + free( (char *)d ); + send_failed( from, fromlen, name, sessionID, + "out of memory", fd ); + goto abort; + } + d->authorizations[0] = pdpy->fileAuthorization; + d->authNum = 1; + pdpy->fileAuthorization = 0; + } + DisposeProtoDisplay( pdpy ); + Debug( "starting display %s,%s\n", d->name, d->class2 ); + if (LoadDisplayResources( d ) < 0) { + LogError( "Unable to read configuration for display %s; " + "stopping it.\n", d->name ); + StopDisplay( d ); + } else + StartDisplay( d ); + CloseGetter(); + } + } +abort: + XdmcpDisposeARRAY8( &displayClass ); + if (name) + free( (char *)name ); + if (class2) + free( (char *)class2 ); +} + +void +SendFailed( struct display *d, const char *reason ) +{ + Debug( "display start failed, sending <failed>\n" ); + send_failed( (struct sockaddr *)(d->from.data), d->from.length, d->name, + d->sessionID, reason, d->xdmcpFd ); +} + +static void +send_failed( struct sockaddr *from, int fromlen, + const char *name, CARD32 sessionID, const char *reason, int fd ) +{ + char buf[360]; + XdmcpHeader header; + ARRAY8 status; + + sprintf( buf, "Session %ld failed for display %.260s: %s", + (long)sessionID, name, reason ); + Debug( "send_failed(%\"s)\n", buf ); + status.length = strlen( buf ); + status.data = (CARD8Ptr) buf; + header.version = XDM_PROTOCOL_VERSION; + header.opcode = (CARD16)FAILED; + header.length = 6 + status.length; + XdmcpWriteHeader( &buffer, &header ); + XdmcpWriteCARD32( &buffer, sessionID ); + XdmcpWriteARRAY8( &buffer, &status ); + XdmcpFlush( fd, &buffer, (XdmcpNetaddr)from, fromlen ); +} + +static void +send_refuse( struct sockaddr *from, int fromlen, CARD32 sessionID, int fd ) +{ + XdmcpHeader header; + + Debug( "send <refuse> %ld\n", (long)sessionID ); + header.version = XDM_PROTOCOL_VERSION; + header.opcode = (CARD16)REFUSE; + header.length = 4; + XdmcpWriteHeader( &buffer, &header ); + XdmcpWriteCARD32( &buffer, sessionID ); + XdmcpFlush( fd, &buffer, (XdmcpNetaddr)from, fromlen ); +} + +static void +send_alive( struct sockaddr *from, int fromlen, int length, int fd ) +{ + CARD32 sessionID; + CARD16 displayNumber; + struct display *d; + XdmcpHeader header; + CARD8 sendRunning; + CARD32 sendSessionID; + + Debug( "send <alive>\n" ); + if (XdmcpReadCARD16( &buffer, &displayNumber ) && + XdmcpReadCARD32( &buffer, &sessionID )) + { + if (length == 6) { + if (!(d = FindDisplayBySessionID( sessionID ))) + d = FindDisplayByAddress( (XdmcpNetaddr)from, fromlen, + displayNumber ); + sendRunning = 0; + sendSessionID = 0; + if (d && d->status == running) { + if (d->sessionID == sessionID) + sendRunning = 1; + sendSessionID = d->sessionID; + } + header.version = XDM_PROTOCOL_VERSION; + header.opcode = (CARD16)ALIVE; + header.length = 5; + Debug( "<alive>: %d %ld\n", sendRunning, (long)sendSessionID ); + XdmcpWriteHeader( &buffer, &header ); + XdmcpWriteCARD8( &buffer, sendRunning ); + XdmcpWriteCARD32( &buffer, sendSessionID ); + XdmcpFlush( fd, &buffer, (XdmcpNetaddr)from, fromlen ); + } + } +} + +char * +NetworkAddressToHostname( CARD16 connectionType, ARRAY8Ptr connectionAddress ) +{ + switch (connectionType) { + case FamilyInternet: +#if defined(IPv6) && defined(AF_INET6) + case FamilyInternet6: +#endif + { + struct hostent *he; + char *name, *lname; + char *myDot; + int af_type; +#if defined(IPv6) && defined(AF_INET6) + char dotted[INET6_ADDRSTRLEN]; + + if (connectionType == FamilyInternet6) + af_type = AF_INET6; + else +#endif + af_type = AF_INET; + + he = gethostbyaddr( (char *)connectionAddress->data, + connectionAddress->length, af_type ); + if (he) { +#if defined(IPv6) && defined(AF_INET6) + struct addrinfo *ai, *nai; + if (!getaddrinfo( he->h_name, NULL, NULL, &ai )) { + for (nai = ai; nai; nai = nai->ai_next) { + if (af_type == nai->ai_family && + !memcmp( nai->ai_family == AF_INET ? + (char *)&((struct sockaddr_in *)nai->ai_addr)->sin_addr : + (char *)&((struct sockaddr_in6 *)nai->ai_addr)->sin6_addr, + connectionAddress->data, + connectionAddress->length )) + { + freeaddrinfo( ai ); + goto oki; + } + } + freeaddrinfo( ai ); +#else + if ((he = gethostbyname( he->h_name )) && + he->h_addrtype == AF_INET) + { + int i; + for (i = 0; he->h_addr_list[i]; i++) + if (!memcmp( he->h_addr_list[i], + connectionAddress->data, 4 )) + goto oki; +#endif + LogError( "DNS spoof attempt or misconfigured resolver.\n" ); + } + goto gotnone; + oki: + if (StrDup( &name, he->h_name ) && + !strchr( name, '.' ) && + (myDot = strchr( localHostname(), '.' ))) + { + if (ASPrintf( &lname, "%s%s", name, myDot )) { +#if defined(IPv6) && defined(AF_INET6) + if (!getaddrinfo( lname, NULL, NULL, &ai )) { + for (nai = ai; nai; nai = nai->ai_next) { + if (af_type == nai->ai_family && + !memcmp( nai->ai_family == AF_INET ? + (char *)&((struct sockaddr_in *)nai->ai_addr)->sin_addr : + (char *)&((struct sockaddr_in6 *)nai->ai_addr)->sin6_addr, + connectionAddress->data, + connectionAddress->length )) + { + freeaddrinfo( ai ); + free( name ); + return lname; + } + } + freeaddrinfo( ai ); + } +#else + if ((he = gethostbyname( lname )) && + he->h_addrtype == AF_INET) + { + int i; + for (i = 0; he->h_addr_list[i]; i++) + if (!memcmp( he->h_addr_list[i], + connectionAddress->data, 4 )) + { + free( name ); + return lname; + } + } +#endif + free( lname ); + } + } + } else { + gotnone: + /* can't get name, so use emergency fallback */ +#if defined(IPv6) && defined(AF_INET6) + inet_ntop( af_type, connectionAddress->data, + dotted, sizeof(dotted) ); + StrDup( &name, dotted ); +#else + ASPrintf( &name, "%[4|'.'hhu", connectionAddress->data ); +#endif + LogWarn( "Cannot convert Internet address %s to host name\n", + name ); + } + return name; + } +#ifdef DNET + case FamilyDECnet: + break; +#endif /* DNET */ + default: + break; + } + return 0; +} + +#endif /* XDMCP */ + |