[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    KDE/kdebase/workspace/kdm
From:       Oswald Buddenhagen <ossi () kde ! org>
Date:       2010-04-13 13:58:48
Message-ID: 20100413140046.88C87AC897 () svn ! kde ! org
[Download RAW message or body]

SVN commit 1114416 by ossi:

fix CVE-2010-0436: local root hole relating to command sockets

the user owns the socket directory. if he prevented its deletion (e.g.,
by creating another file in it), he would subsequently get the chance to
inject a symlink to an arbitrary file whose permissions kdm would
conveniently change to 0666.

chmod()ing a directory to the user opens a can of worms, as he might
do all kinds of nasty things with it which would prevent us from
reclaiming it.

so instead rely on the system's ability to honor file ownership and
permissions of the socket's inode instead of its parent directory.

for systems where we cannot rely on this behavior (solaris), we create a
new randomly named socket directory for each session and symlink it from
the proper name. if the user tries to mess with us, he will pollute the
top-level socket directory but cause no real harm.

discovered by Sebastian Krahmer from the SUSE security team.



 M  +32 -0     ConfigureChecks.cmake  
 M  +69 -7     backend/ctrl.c  
 M  +3 -0      backend/dm.h  
 M  +3 -0      config-kdm.h.cmake  


--- trunk/KDE/kdebase/workspace/kdm/ConfigureChecks.cmake #1114415:1114416
@@ -80,6 +80,38 @@
 	define_library(nsl gethostbyname)
 endif (NOT have_gethostbyname)
 
+macro_push_required_vars()
+set(CMAKE_REQUIRED_LIBRARIES ${SOCKET_LIBRARIES})
+check_c_source_runs("
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+int main()
+{
+    int fd, fd2;
+    struct sockaddr_un sa;
+
+    if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
+        return 2;
+    sa.sun_family = AF_UNIX;
+    strcpy(sa.sun_path, \"testsock\");
+    unlink(sa.sun_path);
+    if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)))
+        return 2;
+    chmod(sa.sun_path, 0);
+    setuid(getuid() + 1000);
+    if ((fd2 = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
+        return 2;
+    connect(fd2, (struct sockaddr *)&sa, sizeof(sa));
+    return errno != EACCES;
+}
+" HONORS_SOCKET_PERMS)
+macro_pop_required_vars()
+
 # for genkdmconf; this is TODO
 #if (EXISTS /etc/ttys)
 #	set(BSD_INIT 1)
--- trunk/KDE/kdebase/workspace/kdm/backend/ctrl.c #1114415:1114416
@@ -79,8 +79,26 @@
 }
 
 
+#ifdef HONORS_SOCKET_PERMS
 static CtrlRec ctrl = { 0, 0, -1, 0 };
+#else
+static CtrlRec ctrl = { 0, 0, 0, -1, 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 )
 {
@@ -113,22 +131,50 @@
 				if (strlen( cr->path ) >= sizeof(sa.sun_path))
 					logError( "path %\"s too long; control socket will not be available\n",
 					          cr->path );
-				else if (mkdir( sockdir, 0755 ) && errno != EEXIST)
+#ifdef HONORS_SOCKET_PERMS
+				else if (mkdir( sockdir, 0700 ) && errno != EEXIST)
 					logError( "mkdir %\"s failed: %m; control socket will not 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 {
-					if (!d)
-						chown( sockdir, -1, fifoGroup );
+#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: %m\n" );
 					else {
-						unlink( cr->path );
 						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 );
@@ -143,6 +189,14 @@
 						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;
@@ -163,7 +217,14 @@
 		cr->fd = -1;
 		unlink( cr->path );
 		*strrchr( cr->path, '/' ) = 0;
+#ifdef HONORS_SOCKET_PERMS
 		rmdir( cr->path );
+#else
+		rmdir( cr->realdir );
+		free( cr->realdir );
+		cr->realdir = 0;
+		unlink( cr->path );
+#endif
 		free( cr->path );
 		cr->path = 0;
 		while (cr->css) {
@@ -178,10 +239,11 @@
 chownCtrl( CtrlRec *cr, int uid )
 {
 	if (cr->path) {
-		char *ptr = strrchr( cr->path, '/' );
-		*ptr = 0;
+#ifdef HONORS_SOCKET_PERMS
 		chown( cr->path, uid, -1 );
-		*ptr = '/';
+#else
+		chown( cr->realdir, uid, -1 );
+#endif
 	}
 }
 
--- trunk/KDE/kdebase/workspace/kdm/backend/dm.h #1114415:1114416
@@ -232,6 +232,9 @@
 	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 */
 } CtrlRec;
--- trunk/KDE/kdebase/workspace/kdm/config-kdm.h.cmake #1114415:1114416
@@ -129,6 +129,9 @@
 /* Define to 1 if the ck-connector library is found */
 #cmakedefine HAVE_CKCONNECTOR 1
 
+/* Define to 1 if OS honors permission bits on socket inodes */
+#cmakedefine HONORS_SOCKET_PERMS 1
+
 /* $PATH defaults set by KDM */
 #cmakedefine KDM_DEF_USER_PATH "${KDM_DEF_USER_PATH}"
 #cmakedefine KDM_DEF_SYSTEM_PATH "${KDM_DEF_SYSTEM_PATH}"
[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic