File indexing completed on 2023-09-24 04:11:27
0001 /* 0002 SPDX-FileCopyrightText: 2017 KDE Developers 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "katesecuretextbuffer_p.h" 0008 0009 #include "config.h" 0010 0011 #include <KAuth/HelperSupport> 0012 0013 #ifndef Q_OS_WIN 0014 #include <cerrno> 0015 #include <unistd.h> 0016 #endif 0017 0018 #include <QDir> 0019 #include <QFile> 0020 #include <QFileInfo> 0021 #include <QString> 0022 #include <QTemporaryFile> 0023 0024 KAUTH_HELPER_MAIN("org.kde.ktexteditor.katetextbuffer", SecureTextBuffer) 0025 0026 ActionReply SecureTextBuffer::savefile(const QVariantMap &args) 0027 { 0028 const QString sourceFile = args[QStringLiteral("sourceFile")].toString(); 0029 const QString targetFile = args[QStringLiteral("targetFile")].toString(); 0030 const QByteArray checksum = args[QStringLiteral("checksum")].toByteArray(); 0031 const uint ownerId = (uint)args[QStringLiteral("ownerId")].toInt(); 0032 const uint groupId = (uint)args[QStringLiteral("groupId")].toInt(); 0033 0034 if (saveFileInternal(sourceFile, targetFile, checksum, ownerId, groupId)) { 0035 return ActionReply::SuccessReply(); 0036 } 0037 0038 return ActionReply::HelperErrorReply(); 0039 } 0040 0041 bool SecureTextBuffer::saveFileInternal(const QString &sourceFile, 0042 const QString &targetFile, 0043 const QByteArray &checksum, 0044 const uint ownerId, 0045 const uint groupId) 0046 { 0047 // open source file for reading 0048 // if not possible, signal error 0049 QFile readFile(sourceFile); 0050 if (!readFile.open(QIODevice::ReadOnly)) { 0051 return false; 0052 } 0053 0054 // construct file info for target file 0055 // we need to know things like path/exists/permissions 0056 const QFileInfo targetFileInfo(targetFile); 0057 0058 // create temporary file in current directory to be able to later do an atomic rename 0059 // we need to pass full path, else QTemporaryFile uses the temporary directory 0060 // if not possible, signal error, this catches e.g. a non-existing target directory, too 0061 QTemporaryFile tempFile(targetFileInfo.absolutePath() + QLatin1String("/secureXXXXXX")); 0062 if (!tempFile.open()) { 0063 return false; 0064 } 0065 0066 // copy contents + do checksumming 0067 // if not possible, signal error 0068 QCryptographicHash cryptographicHash(checksumAlgorithm); 0069 const qint64 bufferLength = 4096; 0070 char buffer[bufferLength]; 0071 qint64 read = -1; 0072 while ((read = readFile.read(buffer, bufferLength)) > 0) { 0073 cryptographicHash.addData(buffer, read); 0074 if (tempFile.write(buffer, read) == -1) { 0075 return false; 0076 } 0077 } 0078 0079 // check that copying was successful and checksum matched 0080 // we need to flush the file, as QTemporaryFile keeps the handle open 0081 // and we later do things like renaming of the file! 0082 // if not possible, signal error 0083 if ((read == -1) || (cryptographicHash.result() != checksum) || !tempFile.flush()) { 0084 return false; 0085 } 0086 0087 // try to preserve the permissions 0088 if (!targetFileInfo.exists()) { 0089 // ensure new file is readable by anyone 0090 tempFile.setPermissions(tempFile.permissions() | QFile::Permission::ReadGroup | QFile::Permission::ReadOther); 0091 } else { 0092 // ensure the same file permissions 0093 tempFile.setPermissions(targetFileInfo.permissions()); 0094 0095 // ensure file has the same owner and group as before 0096 setOwner(tempFile.handle(), ownerId, groupId); 0097 } 0098 0099 // try to (atomic) rename temporary file to the target file 0100 if (moveFile(tempFile.fileName(), targetFileInfo.filePath())) { 0101 // temporary file was renamed, there is nothing to remove anymore 0102 tempFile.setAutoRemove(false); 0103 return true; 0104 } 0105 0106 // we failed 0107 // QTemporaryFile will handle cleanup 0108 return false; 0109 } 0110 0111 void SecureTextBuffer::setOwner(const int filedes, const uint ownerId, const uint groupId) 0112 { 0113 #ifndef Q_OS_WIN 0114 if (ownerId != (uint)-2 && groupId != (uint)-2) { 0115 const int result = fchown(filedes, ownerId, groupId); 0116 // set at least correct group if owner cannot be changed 0117 if (result != 0 && errno == EPERM) { 0118 fchown(filedes, getuid(), groupId); 0119 } 0120 } 0121 #else 0122 // no-op for windows 0123 #endif 0124 } 0125 0126 bool SecureTextBuffer::moveFile(const QString &sourceFile, const QString &targetFile) 0127 { 0128 #if !defined(Q_OS_WIN) && !defined(Q_OS_ANDROID) 0129 const int result = std::rename(QFile::encodeName(sourceFile).constData(), QFile::encodeName(targetFile).constData()); 0130 return (result == 0); 0131 #else 0132 // use racy fallback for windows 0133 QFile::remove(targetFile); 0134 return QFile::rename(sourceFile, targetFile); 0135 #endif 0136 } 0137 0138 #include "moc_katesecuretextbuffer_p.cpp"