From kde-commits Tue Apr 13 13:58:48 2010 From: Oswald Buddenhagen Date: Tue, 13 Apr 2010 13:58:48 +0000 To: kde-commits Subject: KDE/kdebase/workspace/kdm Message-Id: <20100413140046.88C87AC897 () svn ! kde ! org> X-MARC-Message: https://marc.info/?l=kde-commits&m=127116712828181 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 +#include +#include +#include +#include +#include +#include +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}"