Blob Blame Raw
From 6003510f40dd64f8cd1c060b6fd5ca40d48d3e6d Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Thu, 23 Apr 2015 15:36:09 -0400
Subject: [PATCH 2/3] os: support new implicit local user access mode
 [CVE-2015-3164 2/3]

If the X server is started without a '-auth' argument, then
it gets started wide open to all local users on the system.

This isn't a great default access model, but changing it in
Xorg at this point would break backward compatibility.

Xwayland, on the other hand is new, and much more targeted
in scope.  It could, in theory, be changed to allow the much
more secure default of a "user who started X server can connect
clients to that server."

This commit paves the way for that change, by adding a mechanism
for DDXs to opt-in to that behavior.  They merely need to call

LocalAccessScopeUser()

in their init functions.

A subsequent commit will add that call for Xwayland.

Signed-off-by: Ray Strode <rstrode@redhat.com>
Reviewed-by: Daniel Stone <daniels@collabora.com>
Reviewed-by: Alan Coopersmith <alan.coopersmith@oracle.com>
---
 include/os.h |  17 ++++++++++
 os/access.c  | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 os/auth.c    |   8 ++---
 3 files changed, 130 insertions(+), 4 deletions(-)

diff --git a/include/os.h b/include/os.h
index 3e68c49..3c3954f 100644
--- a/include/os.h
+++ b/include/os.h
@@ -386,65 +386,82 @@ InvalidHost(sockaddrPtr /*saddr */ , int /*len */ , ClientPtr client);
 #define LCC_ZID_SET	(1 << 3)
 
 typedef struct {
     int fieldsSet;              /* Bit mask of fields set */
     int euid;                   /* Effective uid */
     int egid;                   /* Primary effective group id */
     int nSuppGids;              /* Number of supplementary group ids */
     int *pSuppGids;             /* Array of supplementary group ids */
     int pid;                    /* Process id */
     int zoneid;                 /* Only set on Solaris 10 & later */
 } LocalClientCredRec;
 
 extern _X_EXPORT int
 GetLocalClientCreds(ClientPtr, LocalClientCredRec **);
 extern _X_EXPORT void
 FreeLocalClientCreds(LocalClientCredRec *);
 
 extern _X_EXPORT int
 ChangeAccessControl(ClientPtr /*client */ , int /*fEnabled */ );
 
 extern _X_EXPORT int
 GetAccessControl(void);
 
 extern _X_EXPORT void
 AddLocalHosts(void);
 
 extern _X_EXPORT void
 ResetHosts(const char *display);
 
 extern _X_EXPORT void
+EnableLocalAccess(void);
+
+extern _X_EXPORT void
+DisableLocalAccess(void);
+
+extern _X_EXPORT void
 EnableLocalHost(void);
 
 extern _X_EXPORT void
 DisableLocalHost(void);
 
+#ifndef NO_LOCAL_CLIENT_CRED
+extern _X_EXPORT void
+EnableLocalUser(void);
+
+extern _X_EXPORT void
+DisableLocalUser(void);
+
+extern _X_EXPORT void
+LocalAccessScopeUser(void);
+#endif
+
 extern _X_EXPORT void
 AccessUsingXdmcp(void);
 
 extern _X_EXPORT void
 DefineSelf(int /*fd */ );
 
 #if XDMCP
 extern _X_EXPORT void
 AugmentSelf(void *from, int len);
 
 extern _X_EXPORT void
 RegisterAuthorizations(void);
 #endif
 
 extern _X_EXPORT void
 InitAuthorization(const char * /*filename */ );
 
 /* extern int LoadAuthorization(void); */
 
 extern _X_EXPORT int
 AuthorizationFromID(XID id,
                     unsigned short *name_lenp,
                     const char **namep,
                     unsigned short *data_lenp, char **datap);
 
 extern _X_EXPORT XID
 CheckAuthorization(unsigned int /*namelength */ ,
                    const char * /*name */ ,
                    unsigned int /*datalength */ ,
                    const char * /*data */ ,
diff --git a/os/access.c b/os/access.c
index 28f2d32..c9f8a1f 100644
--- a/os/access.c
+++ b/os/access.c
@@ -75,60 +75,64 @@ SOFTWARE.
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  */
 
 #ifdef HAVE_DIX_CONFIG_H
 #include <dix-config.h>
 #endif
 
 #ifdef WIN32
 #include <X11/Xwinsock.h>
 #endif
 
 #include <stdio.h>
 #include <stdlib.h>
 #define XSERV_t
 #define TRANS_SERVER
 #define TRANS_REOPEN
 #include <X11/Xtrans/Xtrans.h>
 #include <X11/Xauth.h>
 #include <X11/X.h>
 #include <X11/Xproto.h>
 #include "misc.h"
 #include "site.h"
 #include <errno.h>
 #include <sys/types.h>
 #ifndef WIN32
 #include <sys/socket.h>
 #include <sys/ioctl.h>
 #include <ctype.h>
 
+#ifndef NO_LOCAL_CLIENT_CRED
+#include <pwd.h>
+#endif
+
 #if defined(TCPCONN) || defined(STREAMSCONN)
 #include <netinet/in.h>
 #endif                          /* TCPCONN || STREAMSCONN */
 
 #ifdef HAVE_GETPEERUCRED
 #include <ucred.h>
 #ifdef sun
 #include <zone.h>
 #endif
 #endif
 
 #if defined(SVR4) ||  (defined(SYSV) && defined(__i386__)) || defined(__GNU__)
 #include <sys/utsname.h>
 #endif
 #if defined(SYSV) &&  defined(__i386__)
 #include <sys/stream.h>
 #endif
 #ifdef __GNU__
 #undef SIOCGIFCONF
 #include <netdb.h>
 #else                           /*!__GNU__ */
 #include <net/if.h>
 #endif /*__GNU__ */
 
 #ifdef SVR4
 #include <sys/sockio.h>
 #include <sys/stropts.h>
 #endif
 
 #include <netdb.h>
@@ -198,97 +202,202 @@ static Bool NewHost(int /*family */ ,
                     int /* addingLocalHosts */ );
 
 /* XFree86 bug #156: To keep track of which hosts were explicitly requested in
    /etc/X<display>.hosts, we've added a requested field to the HOST struct,
    and a LocalHostRequested variable.  These default to FALSE, but are set
    to TRUE in ResetHosts when reading in /etc/X<display>.hosts.  They are
    checked in DisableLocalHost(), which is called to disable the default
    local host entries when stronger authentication is turned on. */
 
 typedef struct _host {
     short family;
     short len;
     unsigned char *addr;
     struct _host *next;
     int requested;
 } HOST;
 
 #define MakeHost(h,l)	(h)=malloc(sizeof *(h)+(l));\
 			if (h) { \
 			   (h)->addr=(unsigned char *) ((h) + 1);\
 			   (h)->requested = FALSE; \
 			}
 #define FreeHost(h)	free(h)
 static HOST *selfhosts = NULL;
 static HOST *validhosts = NULL;
 static int AccessEnabled = DEFAULT_ACCESS_CONTROL;
 static int LocalHostEnabled = FALSE;
 static int LocalHostRequested = FALSE;
 static int UsingXdmcp = FALSE;
 
+static enum {
+    LOCAL_ACCESS_SCOPE_HOST = 0,
+#ifndef NO_LOCAL_CLIENT_CRED
+    LOCAL_ACCESS_SCOPE_USER,
+#endif
+} LocalAccessScope;
+
 /* FamilyServerInterpreted implementation */
 static Bool siAddrMatch(int family, void *addr, int len, HOST * host,
                         ClientPtr client);
 static int siCheckAddr(const char *addrString, int length);
 static void siTypesInitialize(void);
 
 /*
  * called when authorization is not enabled to add the
  * local host to the access list
  */
 
 void
+EnableLocalAccess(void)
+{
+    switch (LocalAccessScope) {
+        case LOCAL_ACCESS_SCOPE_HOST:
+            EnableLocalHost();
+            break;
+#ifndef NO_LOCAL_CLIENT_CRED
+        case LOCAL_ACCESS_SCOPE_USER:
+            EnableLocalUser();
+            break;
+#endif
+    }
+}
+
+void
 EnableLocalHost(void)
 {
     if (!UsingXdmcp) {
         LocalHostEnabled = TRUE;
         AddLocalHosts();
     }
 }
 
 /*
  * called when authorization is enabled to keep us secure
  */
 void
+DisableLocalAccess(void)
+{
+    switch (LocalAccessScope) {
+        case LOCAL_ACCESS_SCOPE_HOST:
+            DisableLocalHost();
+            break;
+#ifndef NO_LOCAL_CLIENT_CRED
+        case LOCAL_ACCESS_SCOPE_USER:
+            DisableLocalUser();
+            break;
+#endif
+    }
+}
+
+void
 DisableLocalHost(void)
 {
     HOST *self;
 
     if (!LocalHostRequested)    /* Fix for XFree86 bug #156 */
         LocalHostEnabled = FALSE;
     for (self = selfhosts; self; self = self->next) {
         if (!self->requested)   /* Fix for XFree86 bug #156 */
             (void) RemoveHost((ClientPtr) NULL, self->family, self->len,
                               (void *) self->addr);
     }
 }
 
+#ifndef NO_LOCAL_CLIENT_CRED
+static int GetLocalUserAddr(char **addr)
+{
+    static const char *type = "localuser";
+    static const char delimiter = '\0';
+    static const char *value;
+    struct passwd *pw;
+    int length = -1;
+
+    pw = getpwuid(getuid());
+
+    if (pw == NULL || pw->pw_name == NULL)
+        goto out;
+
+    value = pw->pw_name;
+
+    length = asprintf(addr, "%s%c%s", type, delimiter, value);
+
+    if (length == -1) {
+        goto out;
+    }
+
+    /* Trailing NUL */
+    length++;
+
+out:
+    return length;
+}
+
+void
+EnableLocalUser(void)
+{
+    char *addr = NULL;
+    int length = -1;
+
+    length = GetLocalUserAddr(&addr);
+
+    if (length == -1)
+        return;
+
+    NewHost(FamilyServerInterpreted, addr, length, TRUE);
+
+    free(addr);
+}
+
+void
+DisableLocalUser(void)
+{
+    char *addr = NULL;
+    int length = -1;
+
+    length = GetLocalUserAddr(&addr);
+
+    if (length == -1)
+        return;
+
+    RemoveHost(NULL, FamilyServerInterpreted, length, addr);
+
+    free(addr);
+}
+
+void
+LocalAccessScopeUser(void)
+{
+    LocalAccessScope = LOCAL_ACCESS_SCOPE_USER;
+}
+#endif
+
 /*
  * called at init time when XDMCP will be used; xdmcp always
  * adds local hosts manually when needed
  */
 
 void
 AccessUsingXdmcp(void)
 {
     UsingXdmcp = TRUE;
     LocalHostEnabled = FALSE;
 }
 
 #if  defined(SVR4) && !defined(sun)  && defined(SIOCGIFCONF) && !defined(USE_SIOCGLIFCONF)
 
 /* Deal with different SIOCGIFCONF ioctl semantics on these OSs */
 
 static int
 ifioctl(int fd, int cmd, char *arg)
 {
     struct strioctl ioc;
     int ret;
 
     memset((char *) &ioc, 0, 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 {
diff --git a/os/auth.c b/os/auth.c
index 5fcb538..7da6fc6 100644
--- a/os/auth.c
+++ b/os/auth.c
@@ -154,78 +154,78 @@ RegisterAuthorizations(void)
                                    (int) protocols[i].name_length);
 }
 #endif
 
 XID
 CheckAuthorization(unsigned int name_length,
                    const char *name,
                    unsigned int data_length,
                    const char *data, ClientPtr client, const char **reason)
 {                               /* failure message.  NULL for default msg */
     int i;
     struct stat buf;
     static time_t lastmod = 0;
     static Bool loaded = FALSE;
 
     if (!authorization_file || stat(authorization_file, &buf)) {
         if (lastmod != 0) {
             lastmod = 0;
             ShouldLoadAuth = TRUE;      /* stat lost, so force reload */
         }
     }
     else if (buf.st_mtime > lastmod) {
         lastmod = buf.st_mtime;
         ShouldLoadAuth = TRUE;
     }
     if (ShouldLoadAuth) {
         int loadauth = LoadAuthorization();
 
         /*
          * If the authorization file has at least one entry for this server,
-         * disable local host access. (loadauth > 0)
+         * disable local access. (loadauth > 0)
          *
          * If there are zero entries (either initially or when the
          * authorization file is later reloaded), or if a valid
-         * authorization file was never loaded, enable local host access.
+         * authorization file was never loaded, enable local access.
          * (loadauth == 0 || !loaded)
          *
          * If the authorization file was loaded initially (with valid
          * entries for this server), and reloading it later fails, don't
          * change anything. (loadauth == -1 && loaded)
          */
 
         if (loadauth > 0) {
-            DisableLocalHost(); /* got at least one */
+            DisableLocalAccess(); /* got at least one */
             loaded = TRUE;
         }
         else if (loadauth == 0 || !loaded)
-            EnableLocalHost();
+            EnableLocalAccess();
     }
     if (name_length) {
         for (i = 0; i < NUM_AUTHORIZATION; i++) {
             if (protocols[i].name_length == name_length &&
                 memcmp(protocols[i].name, name, (int) name_length) == 0) {
                 return (*protocols[i].Check) (data_length, data, client,
                                               reason);
             }
             *reason = "Protocol not supported by server\n";
         }
     }
     else
         *reason = "No protocol specified\n";
     return (XID) ~0L;
 }
 
 void
 ResetAuthorization(void)
 {
     int i;
 
     for (i = 0; i < NUM_AUTHORIZATION; i++)
         if (protocols[i].Reset)
             (*protocols[i].Reset) ();
     ShouldLoadAuth = TRUE;
 }
 
 int
 AuthorizationFromID(XID id,
                     unsigned short *name_lenp,
-- 
2.3.7