CVE-2019-13229 (published 2019-07-04T12:15Z)

deepin-clone before 1.1.3 uses a fixed path /tmp/partclone.log in the Helper::getPartitionSizeInfo() function to write a log file as root, and follows symlinks there. An unprivileged user can prepare a symlink attack there to create or overwrite files in arbitrary file system locations. The content is not attacker controlled.

Severity: MEDIUM
CVSS v2 base score: 6.6 (AV:L/AC:L/Au:N/C:N/I:C/A:C)
CVSS v3 base score: 5.5, MEDIUM (CVSS:3.0/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N)

Weakness information:
cwe_id: CWE-59
cwe_name: Improper Link Resolution Before File Access ('Link Following')
description: The product attempts to access a file based on the filename, but it does not properly prevent that filename from identifying a link or shortcut that resolves to an unintended resource.
extended_description: 
            
            
         
         

Fix information:
repo_url: https://github.com/linuxdeepin/deepin-clone
commit_details: repo_url: https://github.com/linuxdeepin/deepin-clone
author: zccrs
author_date: 2019-05-31 15:38:16+08:00
msg: fix: Do not use the "/tmp" directory

https://github.com/linuxdeepin/deepin-clone/issues/16
https://bugzilla.opensuse.org/show_bug.cgi?id=1130388
repository: repo_url: https://github.com/linuxdeepin/deepin-clone
repo_name: linuxdeepin/deepin-clone
description: Disk and partition backup/restore tool.
repo_language: C++
owner: linuxdeepin
file_changes: filename: ddevicediskinfo.cpp
change_type: ModificationType.MODIFY
diff: @@ -303,9 +303,9 @@ bool DDeviceDiskInfoPrivate::openDataStream(int index)
 
         if (currentMode == DDiskInfo::Read) {
             const QString &executer = Helper::getPartcloneExecuter(part);
-            process->start(QStringLiteral("%1 -s %2 -o - -c -z %3 -L /tmp/partclone.log").arg(executer).arg(part.filePath()).arg(Global::bufferSize), QIODevice::ReadOnly);
+            process->start(QStringLiteral("%1 -s %2 -o - -c -z %3 -L /var/log/partclone.log").arg(executer).arg(part.filePath()).arg(Global::bufferSize), QIODevice::ReadOnly);
         } else {
-            process->start(QStringLiteral("partclone.restore -s - -o %2 -z %3 -L /tmp/partclone.log").arg(part.filePath()).arg(Global::bufferSize));
+            process->start(QStringLiteral("partclone.restore -s - -o %2 -z %3 -L /var/log/partclone.log").arg(part.filePath()).arg(Global::bufferSize));
         }
 
         break;

programming_language: C++
filename: helper.cpp
change_type: ModificationType.MODIFY
diff: @@ -100,6 +100,8 @@ int Helper::processExec(QProcess *process, const QString &command, int timeout,
     if (process->state() != QProcess::NotRunning) {
         dCDebug("The \"%s\" timeout, timeout: %d", qPrintable(command), timeout);
 
+        // QT Bug，某种情况下(未知) QProcess::state 返回的状态有误，导致进程已退出却未能正确获取到其当前状态
+        // 因此,额外通过系统文件判断进程是否还存在
         if (QFile::exists(QString("/proc/%1").arg(process->pid()))) {
             process->terminate();
             process->waitForFinished();
@@ -331,7 +333,7 @@ bool Helper::getPartitionSizeInfo(const QString &partDevice, qint64 *used, qint6
 
         return true;
     } else {
-        process.start(QString("%1 -s %2 -c -q -C -L /tmp/partclone.log").arg(getPartcloneExecuter(DDevicePartInfo(partDevice))).arg(partDevice));
+        process.start(QString("%1 -s %2 -c -q -C -L /var/log/partclone.log").arg(getPartcloneExecuter(DDevicePartInfo(partDevice))).arg(partDevice));
         process.setStandardOutputFile("/dev/null");
         process.setReadChannel(QProcess::StandardError);
         process.waitForStarted();
@@ -501,9 +503,9 @@ QString Helper::temporaryMountDevice(const QString &device, const QString &name,
         return mount_point;
 
     mount_point = "%1/.%2/mount/%3";
-    const QStringList &tmp_paths = QStandardPaths::standardLocations(QStandardPaths::TempLocation);
+    const QStringList &tmp_paths = QStandardPaths::standardLocations(QStandardPaths::RuntimeLocation);
 
-    mount_point = mount_point.arg(tmp_paths.isEmpty() ? "/tmp" : tmp_paths.first()).arg(qApp->applicationName()).arg(name);
+    mount_point = mount_point.arg(tmp_paths.isEmpty() ? "/run/user/0" : tmp_paths.first()).arg(qApp->applicationName()).arg(name);
 
     if (!QDir::current().mkpath(mount_point)) {
         dCError("mkpath \"%s\" failed", qPrintable(mount_point));

programming_language: C++
filename: helper.h
change_type: ModificationType.MODIFY
diff: @@ -99,6 +99,8 @@ public:
     static QString parseSerialUrl(const QString &urlString, QString *errorString = 0);
     static QString toSerialUrl(const QString &file);
 
+    static bool clearSymlink(const QString &path);
+
 signals:
     void newWarning(const QString &message);
     void newError(const QString &message);

programming_language: C++
filename: bootdoctor.cpp
change_type: ModificationType.MODIFY
diff: @@ -55,8 +55,7 @@ bool BootDoctor::fix(const QString &partDevice)
     }
 
     {
-        const QStringList &tmp_paths = QStandardPaths::standardLocations(QStandardPaths::TempLocation);
-        const QString tmp_dir = (tmp_paths.isEmpty() ? "/tmp" : tmp_paths.first()) + "/.deepin-clone";
+        const QString tmp_dir = "/var/cache/deepin-clone";
 
         if (!QDir::current().mkpath(tmp_dir)) {
             dCError("mkpath \"%s\" failed", qPrintable(tmp_dir));

programming_language: C++
filename: main.cpp
change_type: ModificationType.MODIFY
diff: @@ -102,36 +102,20 @@ int main(int argc, char *argv[])
         ConsoleAppender *consoleAppender = new ConsoleAppender;
         consoleAppender->setFormat(logFormat);
 
-        RollingFileAppender *rollingFileAppender = new RollingFileAppender("/tmp/.deepin-clone.log");
+        const QString log_file("/var/log/deepin-clone.log");
+
+        RollingFileAppender *rollingFileAppender = new RollingFileAppender(log_file);
         rollingFileAppender->setFormat(logFormat);
         rollingFileAppender->setLogFilesLimit(5);
         rollingFileAppender->setDatePattern(RollingFileAppender::DailyRollover);
 
-        logger->registerAppender(consoleAppender);
         logger->registerAppender(rollingFileAppender);
+        logger->registerAppender(consoleAppender);
 
         if (qEnvironmentVariableIsSet("PKEXEC_UID")) {
             const quint32 pkexec_uid = qgetenv("PKEXEC_UID").toUInt();
-            const QDir user_home(getpwuid(pkexec_uid)->pw_dir);
-
-            QFile pam_file(user_home.absoluteFilePath(".pam_environment"));
 
-            if (pam_file.open(QIODevice::ReadOnly)) {
-                while (!pam_file.atEnd()) {
-                    const QByteArray &line = pam_file.readLine().simplified();
-
-                    if (line.startsWith("QT_SCALE_FACTOR")) {
-                        const QByteArrayList &list = line.split('=');
-
-                        if (list.count() == 2) {
-                            qputenv("QT_SCALE_FACTOR", list.last());
-                            break;
-                        }
-                    }
-                }
-
-                pam_file.close();
-            }
+            DApplication::customQtThemeConfigPathByUserHome(getpwuid(pkexec_uid)->pw_dir);
         }
 
         DApplication::loadDXcbPlugin();

programming_language: C++