Branch data Line data Source code
1 : : #pragma once
2 : :
3 : : #include <QDir>
4 : : #include <QFile>
5 : : #include <QFileInfo>
6 : : #include <QString>
7 : :
8 : : #ifdef Q_OS_UNIX
9 : : #include <sys/stat.h>
10 : : #include <unistd.h>
11 : : #endif
12 : :
13 : : namespace DatabaseSecurity {
14 : :
15 : 2158 : inline bool isOwnedByCurrentUser(const QFileInfo &info) {
16 : : #ifdef Q_OS_UNIX
17 : 2158 : return info.ownerId() == static_cast<uint>(::geteuid());
18 : : #else
19 : : Q_UNUSED(info)
20 : : return true;
21 : : #endif
22 : : }
23 : :
24 : 748 : inline bool isSharedSystemDirectory(const QString &path) {
25 : : #ifdef Q_OS_UNIX
26 [ + - + - : 748 : if (QDir::cleanPath(path) == QDir::rootPath())
- + ]
27 : 0 : return true;
28 : :
29 : 748 : struct stat metadata {};
30 [ + - ]: 748 : const QByteArray nativePath = QFile::encodeName(path);
31 [ + - - + ]: 1496 : if (::lstat(nativePath.constData(), &metadata) != 0 ||
32 [ - + ]: 748 : !S_ISDIR(metadata.st_mode))
33 : 0 : return true;
34 : :
35 : : // Sticky directories such as /tmp are deliberately shared trust domains.
36 : : // Even when MailJD happens to run as their owner (notably uid 0), changing
37 : : // their mode would damage the system and using them directly would expose
38 : : // the SQLite filename and sidecars to local races.
39 : 748 : return (metadata.st_mode & S_ISVTX) != 0;
40 : : #else
41 : : Q_UNUSED(path)
42 : : return false;
43 : : #endif
44 : 748 : }
45 : :
46 : 1418 : inline bool restrictOwnedRegularFile(const QString &path) {
47 [ + - ]: 1418 : const QFileInfo info(path);
48 [ + - + + : 2828 : if (info.isSymLink() || !info.exists() || !info.isFile() ||
+ - + - +
- + + +
+ ]
49 [ + - - + ]: 1410 : !isOwnedByCurrentUser(info)) {
50 : 8 : return false;
51 : : }
52 [ + - ]: 1410 : return QFile::setPermissions(
53 : 1410 : path, QFileDevice::ReadOwner | QFileDevice::WriteOwner);
54 : 1418 : }
55 : :
56 : 1484 : inline bool secureExistingSidecars(const QString &dbPath) {
57 : 5933 : for (const QString &suffix : {QStringLiteral("-wal"),
58 : 1484 : QStringLiteral("-shm"),
59 [ + + ]: 19289 : QStringLiteral("-journal")}) {
60 [ + - ]: 4450 : const QString sidecarPath = dbPath + suffix;
61 [ + - ]: 4450 : const QFileInfo sidecar(sidecarPath);
62 [ + - + + : 4450 : if (!sidecar.exists() && !sidecar.isSymLink())
+ - + - +
+ ]
63 : 4069 : continue;
64 [ + - + + ]: 381 : if (!restrictOwnedRegularFile(sidecarPath))
65 : 1 : return false;
66 [ + + + + : 14456 : }
+ + + + -
- ]
67 : 1483 : return true;
68 : : }
69 : :
70 : 774 : inline bool preparePath(const QString &dbPath) {
71 [ + + ]: 774 : if (dbPath == QLatin1String(":memory:"))
72 : 21 : return true;
73 : :
74 [ + - ]: 753 : const QFileInfo info(dbPath);
75 [ + - ]: 753 : const QString parentPath = info.absolutePath();
76 [ + - ]: 753 : QFileInfo parentInfo(parentPath);
77 [ + - + + : 753 : if (!parentInfo.exists() && !parentInfo.isSymLink()) {
+ - + - +
+ ]
78 [ + - + - : 4 : if (!QDir().mkpath(parentPath))
+ - ]
79 : 4 : return false;
80 [ # # ]: 0 : parentInfo.refresh();
81 : : }
82 [ + - + - : 1497 : if (parentInfo.isSymLink() || !parentInfo.isDir() ||
+ - ]
83 [ + + + - : 2245 : !isOwnedByCurrentUser(parentInfo) ||
+ - + + ]
84 [ + - + + ]: 748 : isSharedSystemDirectory(parentPath))
85 : 3 : return false;
86 [ + - ]: 746 : if (!QFile::setPermissions(
87 [ - + ]: 1492 : parentPath, QFileDevice::ReadOwner | QFileDevice::WriteOwner |
88 : : QFileDevice::ExeOwner))
89 : 0 : return false;
90 : :
91 : : // Reject sidecars planted while the directory was still too permissive.
92 [ + - + + ]: 746 : if (!secureExistingSidecars(dbPath))
93 : 1 : return false;
94 : :
95 [ + - + + : 745 : if (info.exists() || info.isSymLink())
+ - - + +
+ ]
96 [ + - ]: 299 : return restrictOwnedRegularFile(dbPath);
97 : :
98 [ + - ]: 446 : QFile file(dbPath);
99 [ + - ]: 446 : return file.open(QIODevice::WriteOnly | QIODevice::NewOnly,
100 : 446 : QFileDevice::ReadOwner | QFileDevice::WriteOwner);
101 : 753 : }
102 : :
103 : 759 : inline bool restrictExistingFile(const QString &dbPath) {
104 [ + + ]: 759 : if (dbPath == QLatin1String(":memory:"))
105 : 21 : return true;
106 : :
107 [ + - + - ]: 738 : return restrictOwnedRegularFile(dbPath) && secureExistingSidecars(dbPath);
108 : : }
109 : :
110 : : } // namespace DatabaseSecurity
|