Commit 7720e848 authored by David Kahles's avatar David Kahles

Forbid more syscalls which could modify the filesystem

Summary:
Forbid more syscalls. An malicious theme could create directories with the
password as name, or encode the password in chmod bits. Also, prevent
deleting anything, so a theme can't delete the users files.

Test Plan:
- Autotests run fine
- Started sceenlocker, unlocked, created a new session.
  Got no seccomp violations in dmesg and everything worked fine.
- Didn't test it with the nvidia driver

Reviewers: graesslin

Reviewed By: graesslin

Subscribers: plasma-devel

Tags: #plasma

Differential Revision: https://phabricator.kde.org/D8756
parent 0f9a10fa
......@@ -37,6 +37,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <fcntl.h>
#include <unistd.h>
#if !defined(SYS_open) || !defined(SYS_openat) || !defined(SYS_creat) || !defined(SYS_truncate) \
|| !defined(SYS_rename) || !defined(SYS_renameat) || !defined(SYS_renameat2) || !defined(SYS_mkdir) \
|| !defined(SYS_mkdirat) || !defined(SYS_rmdir) || !defined(SYS_link) || !defined(SYS_linkat) \
|| !defined(SYS_unlink) || !defined(SYS_unlinkat) || !defined(SYS_symlink) || !defined(SYS_symlinkat) \
|| !defined(SYS_mknod) || !defined(SYS_mknodat) || !defined(SYS_chmod) || !defined(SYS_fchmod) \
|| !defined(SYS_fchmodat)
#error "Some systemcalls are not available, though seccomp is available."
#endif
class SeccompTest : public QObject
{
Q_OBJECT
......@@ -46,13 +55,30 @@ private Q_SLOTS:
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:
QLatin1Literal existingFile = QLatin1Literal(KCHECKPASS_BIN);
QLatin1Literal createPath = QLatin1Literal(KCHECKPASS_BIN ".new");
QLatin1Literal existingDir = QLatin1Literal(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();
}
......@@ -67,7 +93,7 @@ void SeccompTest::testOpenFile()
if (KWin::GLPlatform::instance()->driver() == KWin::Driver_NVidia) {
QSKIP("Write protection not supported on NVIDIA");
}
QFile file(QStringLiteral(KCHECKPASS_BIN));
QFile file(existingFile);
QVERIFY(file.exists());
QVERIFY(!file.open(QIODevice::WriteOnly));
QVERIFY(!file.open(QIODevice::ReadWrite));
......@@ -76,14 +102,11 @@ void SeccompTest::testOpenFile()
void SeccompTest::testOpenFilePosix()
{
QVERIFY(open("/dev/null", O_RDONLY | O_CREAT, 0) == -1 && errno == EPERM);
QVERIFY(openat(AT_FDCWD, "/dev/null", O_RDONLY | O_CREAT, 0) == -1 && errno == EPERM);
#ifdef SYS_open
QVERIFY(syscall(SYS_open, "/dev/null", O_RDONLY | O_CREAT, 0) == -1 && errno == EPERM);
#endif
#ifdef SYS_openat
QVERIFY(syscall(SYS_openat, AT_FDCWD, "/dev/null", O_RDONLY | O_CREAT, 0) == -1 && errno == EPERM);
#endif
QVERIFY(open(createPathChar, O_RDONLY | O_CREAT, 0) == -1 && errno == EPERM);
QVERIFY(openat(AT_FDCWD, createPathChar, O_RDONLY | O_CREAT, 0) == -1 && errno == EPERM);
QVERIFY(syscall(SYS_open, createPathChar, O_RDONLY | O_CREAT, 0) == -1 && errno == EPERM);
QVERIFY(syscall(SYS_openat, AT_FDCWD, createPathChar, O_RDONLY | O_CREAT, 0) == -1 && errno == EPERM);
QVERIFY(syscall(SYS_creat, createPathChar, S_IRWXU) == -1 && errno == EPERM);
}
void SeccompTest::testWriteFilePosix()
......@@ -91,28 +114,90 @@ void SeccompTest::testWriteFilePosix()
if (KWin::GLPlatform::instance()->driver() == KWin::Driver_NVidia) {
QSKIP("Write protection not supported on NVIDIA");
}
QVERIFY(open("/dev/null", O_RDWR) == -1 && errno == EPERM);
QVERIFY(openat(AT_FDCWD, "/dev/null", O_RDWR) == -1 && errno == EPERM);
#ifdef SYS_open
QVERIFY(syscall(SYS_open, "/dev/null", O_RDWR) == -1 && errno == EPERM);
#endif
#ifdef SYS_openat
QVERIFY(syscall(SYS_openat, AT_FDCWD, "/dev/null", O_RDWR) == -1 && errno == EPERM);
#endif
QVERIFY(open(existingFileChar, O_RDWR) == -1 && errno == EPERM);
QVERIFY(openat(AT_FDCWD, existingFileChar, O_RDWR) == -1 && errno == EPERM);
QVERIFY(syscall(SYS_open, existingFileChar, O_RDWR) == -1 && errno == EPERM);
QVERIFY(syscall(SYS_openat, AT_FDCWD, existingFileChar, O_RDWR) == -1 && errno == EPERM);
}
void SeccompTest::testTruncate()
{
QVERIFY(!QFile::resize(existingFile, 0));
QVERIFY(syscall(SYS_truncate, existingFileChar, 0) == -1 && errno == EPERM);
}
void SeccompTest::testRename()
{
QVERIFY(!QFile::rename(existingFile, createPath));
QVERIFY(syscall(SYS_rename, existingFileChar, createPathChar) == -1 && errno == EPERM);
QVERIFY(syscall(SYS_renameat, AT_FDCWD, existingFileChar, AT_FDCWD, createPathChar) == -1 && errno == EPERM);
QVERIFY(syscall(SYS_renameat2, AT_FDCWD, existingFileChar, AT_FDCWD, createPathChar, 0) == -1 && errno == EPERM);
}
void SeccompTest::testMkdir()
{
QVERIFY(!QDir::current().mkdir(createPath));
QVERIFY(syscall(SYS_mkdir, createPathChar, S_IRWXU) == -1 && errno == EPERM);
QVERIFY(syscall(SYS_mkdirat, AT_FDCWD, createPathChar, S_IRWXU) == -1 && errno == EPERM);
}
void SeccompTest::testRmdir()
{
QVERIFY(!QDir::current().remove(existingDir));
QVERIFY(syscall(SYS_rmdir, existingDir.data()) == -1 && errno == EPERM);
}
void SeccompTest::testLinkUnlink()
{
QVERIFY(!QFile::remove(existingFile));
QVERIFY(syscall(SYS_link, existingFileChar, createPathChar) == -1 && errno == EPERM);
QVERIFY(syscall(SYS_linkat, AT_FDCWD, existingFileChar, AT_FDCWD, createPathChar, 0) == -1 && errno == EPERM);
QVERIFY(syscall(SYS_unlink, existingFileChar) == -1 && errno == EPERM);
QVERIFY(syscall(SYS_unlinkat, AT_FDCWD, existingFileChar, 0) == -1 && errno == EPERM);
}
void SeccompTest::testSymlink()
{
QVERIFY(!QFile::link(existingFile, createPath));
QVERIFY(syscall(SYS_symlink, existingFileChar, createPathChar) == -1 && errno == EPERM);
QVERIFY(syscall(SYS_symlinkat, existingFileChar, AT_FDCWD, createPathChar) == -1 && errno == EPERM);
}
void SeccompTest::testMknod()
{
QVERIFY(syscall(SYS_mknod, createPathChar, S_IRWXU, S_IFIFO) == -1 && errno == EPERM);
QVERIFY(syscall(SYS_mknodat, AT_FDCWD, createPathChar, S_IRWXU, S_IFIFO) == -1 && errno == EPERM);
}
void SeccompTest::testChmod()
{
QVERIFY(!QFile::setPermissions(existingFileChar, QFileDevice::ExeOwner));
QVERIFY(syscall(SYS_chmod, existingFileChar, S_IRWXU) == -1 && errno == EPERM);
QFile file(existingFile);
QVERIFY(file.open(QIODevice::ReadOnly));
QVERIFY(syscall(SYS_fchmod, file.handle(), S_IRWXU) == -1 && errno == EPERM);
file.close();
QVERIFY(syscall(SYS_fchmodat, AT_FDCWD, existingFileChar, S_IRWXU, 0) == -1 && errno == EPERM);
}
void SeccompTest::testStartProcess()
{
// QProcess fails already using pipe
QProcess p;
p.start(QStringLiteral(KCHECKPASS_BIN));
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(KCHECKPASS_BIN, "fakekcheckpass", (char*)0), -1);
QCOMPARE(execl(existingFileChar, "fakekcheckpass", (char*)0), -1);
QCOMPARE(errno, EPERM);
}
......
......@@ -76,12 +76,38 @@ void init()
// they should fail with EPERM error
if (writeSupported) {
seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(open), 1, SCMP_A1(SCMP_CMP_MASKED_EQ, O_WRONLY, O_WRONLY));
seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(openat), 1, SCMP_A2(SCMP_CMP_MASKED_EQ, O_WRONLY, O_WRONLY));
seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(open), 1, SCMP_A1(SCMP_CMP_MASKED_EQ, O_RDWR, O_RDWR));
seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(openat), 1, SCMP_A2(SCMP_CMP_MASKED_EQ, O_WRONLY, O_WRONLY));
seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(openat), 1, SCMP_A2(SCMP_CMP_MASKED_EQ, O_RDWR, O_RDWR));
}
seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(openat), 1, SCMP_A2(SCMP_CMP_MASKED_EQ, O_CREAT, O_CREAT));
seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(open), 1, SCMP_A1(SCMP_CMP_MASKED_EQ, O_CREAT, O_CREAT));
seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(open_by_handle_at), 1, SCMP_A2(SCMP_CMP_MASKED_EQ, O_WRONLY, O_WRONLY));
seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(open_by_handle_at), 1, SCMP_A2(SCMP_CMP_MASKED_EQ, O_RDWR, O_RDWR));
seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(open_by_handle_at), 1, SCMP_A2(SCMP_CMP_MASKED_EQ, O_CREAT, O_CREAT));
// Disallow everything which modifies the filesystem. An attacker could store the password as a directory name or encode it in chmod bits.
// Also prevent deleting anything, to prevent an attacker from deleting the users files.
seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(creat), 0);
seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(truncate), 0);
seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(rename), 0);
seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(renameat), 0);
seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(renameat2), 0);
seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(mkdir), 0);
seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(mkdirat), 0);
seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(rmdir), 0);
seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(link), 0);
seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(linkat), 0);
seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(unlink), 0);
seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(unlinkat), 0);
seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(symlink), 0);
seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(symlinkat), 0);
seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(mknod), 0);
seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(mknodat), 0);
seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(chmod), 0);
seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(fchmod), 0);
seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(fchmodat), 0);
// disallow going to a socket
seccomp_rule_add(context, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(socket), 0);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment