Commit f53b9b32 authored by Fabian Vogt's avatar Fabian Vogt
Browse files

Drop seccomp sandboxing

Again and again the seccomp filter breaks the greeter, because of changes
in Qt, Mesa or other drivers.

On top of that, it doesn't even really provide security:
- It defaults to allowing syscalls (early on, open was not allowed, but openat
  was, making it useless)
- With the prop. nvidia driver or on wayland, creating and writing files is
  explicitly allowed
- The DBus session bus is open, allowing arbitrary commands to be run

This has a side effect: Without the sandbox, it's not necessary to have
a long-running kcheckpass anymore, so the authenticator is now always using
the "direct" mode.
parent 84a7e8d0
......@@ -112,15 +112,6 @@ add_feature_info("Unlock broken screenlocker"
option(PAM_REQUIRED "Require building with PAM" ON)
find_package(Seccomp)
set_package_properties(Seccomp PROPERTIES
TYPE
OPTIONAL
PURPOSE
"Used for putting the look'n'feel package in the greeter into a sandbox."
)
set(HAVE_SECCOMP ${Seccomp_FOUND})
include(ConfigureChecks.cmake)
configure_file(config-workspace.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-workspace.h)
......
#=============================================================================
# Copyright 2017 Martin Gräßlin <mgraesslin@kde.org>
#
# 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 copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the 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 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.
#=============================================================================
if(CMAKE_VERSION VERSION_LESS 2.8.12)
message(FATAL_ERROR "CMake 2.8.12 is required by FindSeccomp.cmake")
endif()
if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.12)
message(AUTHOR_WARNING "Your project should require at least CMake 2.8.12 to use FindSeccomp.cmake")
endif()
find_package(PkgConfig)
pkg_check_modules(PKG_Libseccomp QUIET libseccomp)
set(Seccomp_DEFINITIONS ${PKG_Libseccomp_CFLAGS_OTHER})
set(Seccomp_VERSION ${PKG_Libseccomp_VERSION})
find_path(Seccomp_INCLUDE_DIR
NAMES
seccomp.h
HINTS
${PKG_Libseccomp_INCLUDE_DIRS}
)
find_library(Seccomp_LIBRARY
NAMES
seccomp
HINTS
${PKG_Libseccomp_LIBRARY_DIRS}
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Seccomp
FOUND_VAR
Seccomp_FOUND
REQUIRED_VARS
Seccomp_LIBRARY
Seccomp_INCLUDE_DIR
VERSION_VAR
Seccomp_VERSION
)
if (Seccomp_FOUND AND NOT TARGET Seccomp::Seccomp)
add_library(Seccomp::Seccomp UNKNOWN IMPORTED)
set_target_properties(Seccomp::Seccomp PROPERTIES
IMPORTED_LOCATION "${Seccomp_LIBRARY}"
INTERFACE_COMPILE_OPTIONS "${Seccomp_DEFINITIONS}"
INTERFACE_INCLUDE_DIRECTORIES "${Seccomp_INCLUDE_DIR}"
)
endif()
mark_as_advanced(Seccomp_LIBRARY Seccomp_INCLUDE_DIR)
include(FeatureSummary)
set_package_properties(Seccomp PROPERTIES
URL "https://github.com/seccomp/libseccomp"
DESCRIPTION "The enhanced seccomp library."
)
......@@ -12,7 +12,6 @@
#cmakedefine01 HAVE_PR_SET_DUMPABLE
#cmakedefine01 HAVE_SYS_PROCCTL_H
#cmakedefine01 HAVE_PROC_TRACE_CTL
#cmakedefine01 HAVE_SECCOMP
#cmakedefine01 HAVE_SIGNALFD_H
#cmakedefine01 HAVE_EVENT_H
......@@ -12,13 +12,8 @@ set(kscreenlocker_greet_SRCS
greeterapp.cpp
main.cpp
noaccessnetworkaccessmanagerfactory.cpp
kwinglplatform.cpp
)
if(HAVE_SECCOMP)
set(kscreenlocker_greet_SRCS ${kscreenlocker_greet_SRCS} seccomp_filter.cpp)
endif()
ecm_qt_declare_logging_category(kscreenlocker_greet_SRCS
HEADER
kscreenlocker_greet_logging.h
......@@ -56,9 +51,6 @@ target_link_libraries(kscreenlocker_greet
KF5::WaylandClient
Wayland::Client
)
if(HAVE_SECCOMP)
target_link_libraries(kscreenlocker_greet Qt5::DBus Seccomp::Seccomp)
endif()
install(TARGETS kscreenlocker_greet DESTINATION ${KDE_INSTALL_LIBEXECDIR})
......
......@@ -27,20 +27,3 @@ add_executable(killTest killtest.cpp)
add_test(NAME kscreenlocker-killTest COMMAND killTest)
ecm_mark_as_test(killTest)
target_link_libraries(killTest Qt5::Test)
#######################################
# Seccomp Test
#######################################
if(HAVE_SECCOMP)
add_executable(seccompTest seccomp_test.cpp ../seccomp_filter.cpp ../kwinglplatform.cpp)
add_test(NAME kscreenlocker-seccompTest COMMAND seccompTest)
ecm_mark_as_test(seccompTest)
target_link_libraries(seccompTest
Qt5::Test
Qt5::Gui
Qt5::DBus
Qt5::Network
KF5::WindowSystem
Seccomp::Seccomp
)
endif()
/********************************************************************
KSld - the KDE Screenlocker Daemon
This file is part of the KDE project.
Copyright (C) 2017 Martin Gräßlin <mgraesslin@kde.org>
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 General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include <config-kscreenlocker.h>
#include "../seccomp_filter.h"
#include "../kwinglplatform.h"
#include <KWindowSystem>
#include <QtTest>
#include <QTemporaryFile>
#include <QProcess>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#ifdef __linux__
#include <sys/syscall.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
class SeccompTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void testCreateFile();
void testOpenFile();
void testOpenFilePosix();
void testWriteFilePosix();
void testRename();
void testTruncate();
void testMkdir();
void testRmdir();
void testLinkUnlink();
void testSymlink();
void testMknod();
void testChmod();
void testStartProcess();
void testNetworkAccess_data();
void testNetworkAccess();
private:
QLatin1String existingFile = QLatin1String(KCHECKPASS_BIN);
QLatin1String createPath = QLatin1String(KCHECKPASS_BIN ".new");
QLatin1String existingDir = QLatin1String(KCHECKPASS_BIN ".newDir");
const char* existingFileChar;
const char* createPathChar;
};
void SeccompTest::initTestCase()
{
existingFileChar = existingFile.data();
createPathChar = createPath.data();
QDir::current().mkdir(existingDir);
ScreenLocker::SecComp::init();
}
void SeccompTest::testCreateFile()
{
if (KWin::GLPlatform::instance()->isSoftwareEmulation() && KWindowSystem::isPlatformWayland()) {
QSKIP("File creation protection not supported with Mesa on Wayland");
}
QTemporaryFile file;
QVERIFY(!file.open());
}
void SeccompTest::testOpenFile()
{
if (KWin::GLPlatform::instance()->driver() == KWin::Driver_NVidia) {
QSKIP("Write protection not supported on NVIDIA");
}
QFile file(existingFile);
QVERIFY(file.exists());
QVERIFY(!file.open(QIODevice::WriteOnly));
QVERIFY(!file.open(QIODevice::ReadWrite));
QVERIFY(file.open(QIODevice::ReadOnly));
}
void SeccompTest::testOpenFilePosix()
{
QVERIFY(open(createPathChar, O_RDONLY | O_CREAT, 0) == -1 && errno == EPERM);
QVERIFY(openat(AT_FDCWD, createPathChar, O_RDONLY | O_CREAT, 0) == -1 && errno == EPERM);
#ifdef SYS_open
QVERIFY(syscall(SYS_open, createPathChar, O_RDONLY | O_CREAT, 0) == -1 && errno == EPERM);
#endif
#ifdef SYS_openat
QVERIFY(syscall(SYS_openat, AT_FDCWD, createPathChar, O_RDONLY | O_CREAT, 0) == -1 && errno == EPERM);
#endif
#ifdef SYS_creat
QVERIFY(syscall(SYS_creat, createPathChar, S_IRWXU) == -1 && errno == EPERM);
#endif
}
void SeccompTest::testWriteFilePosix()
{
if (KWin::GLPlatform::instance()->driver() == KWin::Driver_NVidia) {
QSKIP("Write protection not supported on NVIDIA");
}
QVERIFY(open(existingFileChar, O_RDWR) == -1 && errno == EPERM);
QVERIFY(openat(AT_FDCWD, existingFileChar, O_RDWR) == -1 && errno == EPERM);
#ifdef SYS_open
QVERIFY(syscall(SYS_open, existingFileChar, O_RDWR) == -1 && errno == EPERM);
#endif
#ifdef SYS_openat
QVERIFY(syscall(SYS_openat, AT_FDCWD, existingFileChar, O_RDWR) == -1 && errno == EPERM);
#endif
}
void SeccompTest::testTruncate()
{
QVERIFY(!QFile::resize(existingFile, 0));
#ifdef SYS_truncate
QVERIFY(syscall(SYS_truncate, existingFileChar, 0) == -1 && errno == EPERM);
#endif
}
void SeccompTest::testRename()
{
QVERIFY(!QFile::rename(existingFile, createPath));
#ifdef SYS_rename
QVERIFY(syscall(SYS_rename, existingFileChar, createPathChar) == -1 && errno == EPERM);
#endif
#ifdef SYS_renameat
QVERIFY(syscall(SYS_renameat, AT_FDCWD, existingFileChar, AT_FDCWD, createPathChar) == -1 && errno == EPERM);
#endif
#ifdef SYS_renameat2
QVERIFY(syscall(SYS_renameat2, AT_FDCWD, existingFileChar, AT_FDCWD, createPathChar, 0) == -1 && errno == EPERM);
#endif
}
void SeccompTest::testMkdir()
{
QVERIFY(!QDir::current().mkdir(createPath));
#ifdef SYS_mkdir
QVERIFY(syscall(SYS_mkdir, createPathChar, S_IRWXU) == -1 && errno == EPERM);
#endif
#ifdef SYS_mkdirat
QVERIFY(syscall(SYS_mkdirat, AT_FDCWD, createPathChar, S_IRWXU) == -1 && errno == EPERM);
#endif
}
void SeccompTest::testRmdir()
{
QVERIFY(!QDir::current().remove(existingDir));
#ifdef SYS_rmdir
QVERIFY(syscall(SYS_rmdir, existingDir.data()) == -1 && errno == EPERM);
#endif
}
void SeccompTest::testLinkUnlink()
{
QVERIFY(!QFile::remove(existingFile));
#ifdef SYS_link
QVERIFY(syscall(SYS_link, existingFileChar, createPathChar) == -1 && errno == EPERM);
#endif
#ifdef SYS_linkat
QVERIFY(syscall(SYS_linkat, AT_FDCWD, existingFileChar, AT_FDCWD, createPathChar, 0) == -1 && errno == EPERM);
#endif
#ifdef SYS_unlink
QVERIFY(syscall(SYS_unlink, existingFileChar) == -1 && errno == EPERM);
#endif
#ifdef SYS_unlinkat
QVERIFY(syscall(SYS_unlinkat, AT_FDCWD, existingFileChar, 0) == -1 && errno == EPERM);
#endif
}
void SeccompTest::testSymlink()
{
QVERIFY(!QFile::link(existingFile, createPath));
#ifdef SYS_symlink
QVERIFY(syscall(SYS_symlink, existingFileChar, createPathChar) == -1 && errno == EPERM);
#endif
#ifdef SYS_symlinkat
QVERIFY(syscall(SYS_symlinkat, existingFileChar, AT_FDCWD, createPathChar) == -1 && errno == EPERM);
#endif
}
void SeccompTest::testMknod()
{
#ifdef SYS_mknod
QVERIFY(syscall(SYS_mknod, createPathChar, S_IRWXU, S_IFIFO) == -1 && errno == EPERM);
#endif
#ifdef SYS_mknodat
QVERIFY(syscall(SYS_mknodat, AT_FDCWD, createPathChar, S_IRWXU, S_IFIFO) == -1 && errno == EPERM);
#endif
}
void SeccompTest::testChmod()
{
QVERIFY(!QFile::setPermissions(QLatin1String(existingFileChar), QFileDevice::ExeOwner));
#ifdef SYS_chmod
QVERIFY(syscall(SYS_chmod, existingFileChar, S_IRWXU) == -1 && errno == EPERM);
#endif
#ifdef SYS_fchmod
QFile file(existingFile);
QVERIFY(file.open(QIODevice::ReadOnly));
QVERIFY(syscall(SYS_fchmod, file.handle(), S_IRWXU) == -1 && errno == EPERM);
file.close();
#endif
#ifdef SYS_fchmodat
QVERIFY(syscall(SYS_fchmodat, AT_FDCWD, existingFileChar, S_IRWXU, 0) == -1 && errno == EPERM);
#endif
}
void SeccompTest::testStartProcess()
{
// QProcess fails already using pipe
QProcess p;
p.start(existingFile);
QVERIFY(!p.waitForStarted());
QCOMPARE(p.error(), QProcess::ProcessError::FailedToStart);
// using glibc fork succeeds as it uses clone
// we don't forbid clone as it's needed to start a new thread
// so only test that exec fails
QCOMPARE(execl(existingFileChar, "fakekcheckpass", (char*)nullptr), -1);
QCOMPARE(errno, EPERM);
}
void SeccompTest::testNetworkAccess_data()
{
QTest::addColumn<QString>("url");
// TODO: maybe resolve the IP addresses prior to installing seccomp?
QTest::newRow("domain") << QStringLiteral("https://www.kde.org");
QTest::newRow("ip4") << QStringLiteral("http://91.189.93.5");
// phabricator.kde.org
QTest::newRow("ip6") << QStringLiteral("http://[2a01:4f8:171:2687::4]/");
}
void SeccompTest::testNetworkAccess()
{
QNetworkAccessManager manager;
QFETCH(QString, url);
auto reply = manager.get(QNetworkRequest(QUrl(url)));
QVERIFY(reply);
QSignalSpy finishedSpy(reply, &QNetworkReply::finished);
QVERIFY(finishedSpy.isValid());
QVERIFY(finishedSpy.wait());
QVERIFY(reply->error() != QNetworkReply::NoError);
}
QTEST_MAIN(SeccompTest)
#include "seccomp_test.moc"
......@@ -74,11 +74,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#include <xcb/xcb.h>
#if HAVE_SECCOMP
#include <sys/stat.h>
#include <unistd.h>
#endif
// this is usable to fake a "screensaver" installation for testing
// *must* be "0" for every public commit!
#define TEST_SCREENSAVER 0
......@@ -162,14 +157,6 @@ UnlockApp::~UnlockApp()
Authenticator *UnlockApp::createAuthenticator()
{
#if HAVE_SECCOMP
struct stat buf;
stat(KCHECKPASS_BIN, &buf);
if (!(buf.st_mode & S_ISUID)) {
m_supportsSeccomp = true;
return new Authenticator(AuthenticationMode::Delayed, this);
}
#endif
return new Authenticator(AuthenticationMode::Direct, this);
}
......
......@@ -66,10 +66,6 @@ public:
void updateCanSuspend(bool set);
void updateCanHibernate(bool set);
bool supportsSeccomp() const {
return m_supportsSeccomp;
}
public Q_SLOTS:
void desktopResized();
......@@ -119,8 +115,6 @@ private:
KWayland::Client::PlasmaShell *m_plasmaShell = nullptr;
WallpaperIntegration *m_wallpaperIntegration;
LnFIntegration *m_lnfIntegration;
bool m_supportsSeccomp = false;
};
} // namespace
......
This diff is collapsed.
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2010 Fredrik Höglund <fredrik@kde.org>
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 General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#ifndef KWIN_GLPLATFORM_H
#define KWIN_GLPLATFORM_H
#include <QByteArray>
#include <QSet>
namespace KWin
{
// forward declare method
void cleanupGL();
inline qint64 kVersionNumber(qint64 major, qint64 minor, qint64 patch = 0)
{
return ((major & 0xffff) << 32) | ((minor & 0xffff) << 16) | (patch & 0xffff);
}
enum GLFeature {
/**
* Set when a texture bound to a pixmap uses the same storage as the pixmap,
* and thus doesn't need to be rebound when the contents of the pixmap
* has changed.
*/
LooseBinding,
/**
* Set if the driver supports the following extensions:
* - GL_ARB_shader_objects
* - GL_ARB_fragment_shader
* - GL_ARB_vertex_shader
* - GL_ARB_shading_language_100
*/
GLSL,
/**
* If set, assume the following:
* - No flow control or branches
* - No loops, unless the loops have a fixed iteration count and can be unrolled
* - No functions, unless they can be inlined
* - No indirect indexing of arrays
* - No support for gl_ClipVertex or gl_FrontFacing
* - No texture fetches in vertex shaders
* - Max 32 texture fetches in fragment shaders
* - Max 4 texture indirections
*/
LimitedGLSL,
/**
* Set when the driver supports GL_ARB_texture_non_power_of_two.
*/
TextureNPOT,
/**
* If set, the driver supports GL_ARB_texture_non_power_of_two with the
* GL_ARB_texture_rectangle limitations.
*
* This means no support for mipmap filters, and that only the following
* wrap modes are supported:
* - GL_CLAMP
* - GL_CLAMP_TO_EDGE
* - GL_CLAMP_TO_BORDER
*/
LimitedNPOT
};
enum Driver {
Driver_R100, // Technically "Radeon"
Driver_R200,
Driver_R300C,
Driver_R300G,
Driver_R600C,
Driver_R600G,
Driver_Nouveau,
Driver_Intel,
Driver_NVidia,
Driver_Catalyst,
Driver_Swrast,
Driver_Softpipe,
Driver_Llvmpipe,
Driver_VirtualBox,
Driver_VMware,
Driver_Qualcomm,
Driver_Unknown
};
enum ChipClass {
// Radeon
R100 = 0, // GL1.3 DX7 2000
R200, // GL1.4 DX8.1 SM 1.4 2001
R300, // GL2.0 DX9 SM 2.0 2002
R400, // GL2.0 DX9b SM 2.0b 2004
R500, // GL2.0 DX9c SM 3.0 2005
R600, // GL3.3 DX10 SM 4.0 2006
R700, // GL3.3 DX10.1 SM 4.1 2008
Evergreen, // GL4.0 CL1.0 DX11 SM 5.0 2009
NorthernIslands, // GL4.0 CL1.1 DX11 SM 5.0 2010
UnknownRadeon = 999,
// NVIDIA
NV10 = 1000, // GL1.2 DX7 1999
NV20, // GL1.3 DX8 SM 1.1 2001
NV30, // GL1.5 DX9a SM 2.0 2003
NV40, // GL2.1 DX9c SM 3.0 2004
G80, // GL3.3 DX10 SM 4.0 2006
GF100, // GL4.1 CL1.1 DX11 SM 5.0 2010
UnknownNVidia = 1999,
// Intel
I8XX = 2000, // GL1.3 DX7 2001
I915, // GL1.4/1.5 DX9/DX9c SM 2.0 2004
I965, // GL2.0/2.1 DX9/DX10 SM 3.0/4.0 2006
SandyBridge, // GL3.1 CL1.1 DX10.1 SM 4.0 2010
IvyBridge, // GL4.0 CL1.1 DX11 SM 5.0 2012
Haswell, // GL4.0 CL1.2 DX11.1 SM 5.0 2013
UnknownIntel = 2999,
// Qualcomm Adreno
// from https://en.wikipedia.org/wiki/Adreno
Adreno1XX = 3000, // GLES1.1
Adreno2XX, // GLES2.0 DX9c
Adreno3XX, // GLES3.0 CL1.1 DX11.1
Adreno4XX, // GLES3.1 CL1.2 DX11.2
Adreno5XX, // GLES3.1 CL2.0 DX11.2
UnknownAdreno = 3999,
UnknownChipClass = 99999
};