Add support for write-protected update on Linux

This commit is contained in:
Ilya Fedin 2021-04-27 16:16:20 +04:00 committed by John Preston
parent 4fae827f1e
commit 578833446d
3 changed files with 79 additions and 13 deletions

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <cstdio> #include <cstdio>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h>
#include <sys/sendfile.h> #include <sys/sendfile.h>
#include <cstdlib> #include <cstdlib>
#include <unistd.h> #include <unistd.h>
@ -88,7 +89,7 @@ void writeLog(const char *format, ...) {
va_end(args); va_end(args);
} }
bool copyFile(const char *from, const char *to) { bool copyFile(const char *from, const char *to, bool writeprotected) {
FILE *ffrom = fopen(from, "rb"), *fto = fopen(to, "wb"); FILE *ffrom = fopen(from, "rb"), *fto = fopen(to, "wb");
if (!ffrom) { if (!ffrom) {
if (fto) fclose(fto); if (fto) fclose(fto);
@ -134,7 +135,7 @@ bool copyFile(const char *from, const char *to) {
} }
//update to the same uid/gid //update to the same uid/gid
if (fchown(fileno(fto), fst.st_uid, fst.st_gid) != 0) { if (!writeprotected && fchown(fileno(fto), fst.st_uid, fst.st_gid) != 0) {
fclose(ffrom); fclose(ffrom);
fclose(fto); fclose(fto);
return false; return false;
@ -233,7 +234,7 @@ void delFolder() {
rmdir(delFolder.c_str()); rmdir(delFolder.c_str());
} }
bool update() { bool update(bool writeprotected) {
writeLog("Update started.."); writeLog("Update started..");
string updDir = workDir + "tupdates/temp", readyFilePath = workDir + "tupdates/temp/ready", tdataDir = workDir + "tupdates/temp/tdata"; string updDir = workDir + "tupdates/temp", readyFilePath = workDir + "tupdates/temp/ready", tdataDir = workDir + "tupdates/temp/tdata";
@ -346,7 +347,7 @@ bool update() {
writeLog("Copying file '%s' to '%s'..", fname.c_str(), tofname.c_str()); writeLog("Copying file '%s' to '%s'..", fname.c_str(), tofname.c_str());
int copyTries = 0, triesLimit = 30; int copyTries = 0, triesLimit = 30;
do { do {
if (!copyFile(fname.c_str(), tofname.c_str())) { if (!copyFile(fname.c_str(), tofname.c_str(), writeprotected)) {
++copyTries; ++copyTries;
usleep(100000); usleep(100000);
} else { } else {
@ -381,6 +382,7 @@ int main(int argc, char *argv[]) {
bool needupdate = true; bool needupdate = true;
bool autostart = false; bool autostart = false;
bool debug = false; bool debug = false;
bool writeprotected = false;
bool tosettings = false; bool tosettings = false;
bool startintray = false; bool startintray = false;
bool testmode = false; bool testmode = false;
@ -389,6 +391,8 @@ int main(int argc, char *argv[]) {
char *key = 0; char *key = 0;
char *workdir = 0; char *workdir = 0;
char *oldUsername = 0;
char *dbusAddress = 0;
for (int i = 1; i < argc; ++i) { for (int i = 1; i < argc; ++i) {
if (equal(argv[i], "-noupdate")) { if (equal(argv[i], "-noupdate")) {
needupdate = false; needupdate = false;
@ -408,12 +412,17 @@ int main(int argc, char *argv[]) {
customWorkingDir = true; customWorkingDir = true;
} else if (equal(argv[i], "-key") && ++i < argc) { } else if (equal(argv[i], "-key") && ++i < argc) {
key = argv[i]; key = argv[i];
} else if (equal(argv[i], "-writeprotected") && ++i < argc) {
writeprotected = true;
oldUsername = argv[i];
} else if (equal(argv[i], "-workpath") && ++i < argc) { } else if (equal(argv[i], "-workpath") && ++i < argc) {
workDir = workdir = argv[i]; workDir = workdir = argv[i];
} else if (equal(argv[i], "-exename") && ++i < argc) { } else if (equal(argv[i], "-exename") && ++i < argc) {
exeName = argv[i]; exeName = argv[i];
} else if (equal(argv[i], "-exepath") && ++i < argc) { } else if (equal(argv[i], "-exepath") && ++i < argc) {
exePath = argv[i]; exePath = argv[i];
} else if (equal(argv[i], "-dbus") && ++i < argc) {
dbusAddress = argv[i];
} }
} }
if (exeName.empty() || exeName.find('/') != string::npos) { if (exeName.empty() || exeName.find('/') != string::npos) {
@ -427,6 +436,7 @@ int main(int argc, char *argv[]) {
} }
if (needupdate) writeLog("Need to update!"); if (needupdate) writeLog("Need to update!");
if (autostart) writeLog("From autostart!"); if (autostart) writeLog("From autostart!");
if (writeprotected) writeLog("Write Protected folder!");
updaterName = CurrentExecutablePath(argc, argv); updaterName = CurrentExecutablePath(argc, argv);
writeLog("Updater binary full path is: %s", updaterName.c_str()); writeLog("Updater binary full path is: %s", updaterName.c_str());
@ -477,7 +487,7 @@ int main(int argc, char *argv[]) {
} else { } else {
writeLog("Passed workpath is '%s'", workDir.c_str()); writeLog("Passed workpath is '%s'", workDir.c_str());
} }
update(); update(writeprotected);
} }
} else { } else {
writeLog("Error: bad exe name!"); writeLog("Error: bad exe name!");
@ -494,6 +504,15 @@ int main(int argc, char *argv[]) {
// Force null-terminated .data() call result. // Force null-terminated .data() call result.
values.push_back(arg + char(0)); values.push_back(arg + char(0));
}; };
if (writeprotected) { // run un-elevated
push("pkexec");
push("--user");
push(oldUsername);
push("env");
push("DBUS_SESSION_BUS_ADDRESS=" + string(dbusAddress));
push("systemd-run"); // restore environment
push("--user");
}
push(path); push(path);
push("-noupdate"); push("-noupdate");
if (autostart) push("-autostart"); if (autostart) push("-autostart");
@ -523,10 +542,15 @@ int main(int argc, char *argv[]) {
writeLog("fork() failed!"); writeLog("fork() failed!");
return 1; return 1;
case 0: case 0:
execv(path, args.data()); execvp(args[0], args.data());
return 1; return 1;
} }
// pkexec needs an alive parent
if (writeprotected) {
waitpid(pid, nullptr, 0);
}
writeLog("Executed Telegram, closing log and quitting.."); writeLog("Executed Telegram, closing log and quitting..");
closeLog(); closeLog();

View file

@ -45,6 +45,10 @@ extern "C" {
#include <lzma.h> #include <lzma.h>
#endif // else of Q_OS_WIN && !DESKTOP_APP_USE_PACKAGED #endif // else of Q_OS_WIN && !DESKTOP_APP_USE_PACKAGED
#ifdef Q_OS_UNIX
#include <unistd.h>
#endif // Q_OS_UNIX
namespace Core { namespace Core {
namespace { namespace {
@ -1553,9 +1557,25 @@ bool checkReadyUpdate() {
return false; return false;
} }
#elif defined Q_OS_UNIX // Q_OS_MAC #elif defined Q_OS_UNIX // Q_OS_MAC
// if the files in the directory are owned by user, while the directory is not,
// update will still fail since it's not possible to remove files
if (unlink(QFile::encodeName(curUpdater).constData())) {
if (errno == EACCES) {
cSetWriteProtected(true);
return true;
} else {
ClearAll();
return false;
}
}
if (!linuxMoveFile(QFile::encodeName(updater.absoluteFilePath()).constData(), QFile::encodeName(curUpdater).constData())) { if (!linuxMoveFile(QFile::encodeName(updater.absoluteFilePath()).constData(), QFile::encodeName(curUpdater).constData())) {
ClearAll(); if (errno == EACCES) {
return false; cSetWriteProtected(true);
return true;
} else {
ClearAll();
return false;
}
} }
#endif // Q_OS_UNIX #endif // Q_OS_UNIX

View file

@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h>
#include <cstdlib> #include <cstdlib>
#include <unistd.h> #include <unistd.h>
#include <dirent.h> #include <dirent.h>
@ -75,12 +76,17 @@ bool Launcher::launchUpdater(UpdaterLaunch action) {
return false; return false;
} }
const auto binaryName = (action == UpdaterLaunch::JustRelaunch) const auto binaryPath = (action == UpdaterLaunch::JustRelaunch)
? cExeName() ? (cExeDir() + cExeName())
: QStringLiteral("Updater"); : (cWriteProtected()
? (cWorkingDir() + qsl("tupdates/temp/Updater"))
: (cExeDir() + qsl("Updater")));
auto argumentsList = Arguments(); auto argumentsList = Arguments();
argumentsList.push(QFile::encodeName(cExeDir() + binaryName)); if (action != UpdaterLaunch::JustRelaunch && cWriteProtected()) {
argumentsList.push("pkexec");
}
argumentsList.push(QFile::encodeName(binaryPath));
if (cLaunchMode() == LaunchModeAutoStart) { if (cLaunchMode() == LaunchModeAutoStart) {
argumentsList.push("-autostart"); argumentsList.push("-autostart");
@ -118,6 +124,16 @@ bool Launcher::launchUpdater(UpdaterLaunch action) {
if (customWorkingDir()) { if (customWorkingDir()) {
argumentsList.push("-workdir_custom"); argumentsList.push("-workdir_custom");
} }
if (cWriteProtected()) {
const auto currentUid = geteuid();
const auto pw = getpwuid(currentUid);
if (pw) {
argumentsList.push("-writeprotected");
argumentsList.push(pw->pw_name);
argumentsList.push("-dbus");
argumentsList.push(qgetenv("DBUS_SESSION_BUS_ADDRESS"));
}
}
} }
Logs::closeMain(); Logs::closeMain();
@ -128,8 +144,14 @@ bool Launcher::launchUpdater(UpdaterLaunch action) {
pid_t pid = fork(); pid_t pid = fork();
switch (pid) { switch (pid) {
case -1: return false; case -1: return false;
case 0: execv(args[0], args); return false; case 0: execvp(args[0], args); return false;
} }
// pkexec needs an alive parent
if (cWriteProtected()) {
waitpid(pid, nullptr, 0);
}
return true; return true;
} }