485 lines
12 KiB
Diff
485 lines
12 KiB
Diff
|
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
|
||
|
|