RabbitCommon v2.3.4
Loading...
Searching...
No Matches
adminauthorization_x11.cpp
1
35#include <QLoggingCategory>
36#include <QFile>
37#include <QCoreApplication>
38#include <QInputDialog>
39#include <QMessageBox>
40#include <QProcess>
41#include <QStandardPaths>
42
43#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
44#include <QRegExp>
45#else
46#include <QRegularExpression>
47#include <QRegularExpressionMatch>
48#endif
49
50#include <cstdlib>
51#include <sys/resource.h>
52#include <unistd.h>
53#include <fcntl.h>
54
55#ifdef Q_OS_LINUX
56#include <linux/limits.h>
57#include <pty.h>
58#else
59#include <util.h>
60#endif
61
62#include <sys/ioctl.h>
63#include <sys/types.h>
64#include <sys/wait.h>
65#include <cerrno>
66
67#include "adminauthorization_p.h"
68
69#define SU_COMMAND "/usr/bin/sudo"
70
71static Q_LOGGING_CATEGORY(log, "RabbitCommon.Admin.Rights")
72
73namespace RabbitCommon {
74
75bool execAdminFallback(const QString &program, const QStringList &arguments);
76const QList<QPair<QString, QStringList> > suFontends = {
77 {"pkexec", {"env", "DISPLAY=" + QString::fromLocal8Bit(qgetenv("DISPLAY")), "XAUTHORITY=" + QString::fromLocal8Bit(qgetenv("XAUTHORITY"))}}, // freedesktop
78 {"kdesu", {"-c"}},
79 {"gksu", {}}
80};
81
82bool CAdminAuthorization::hasAdminRights()
83{
84 return getuid() == 0;
85}
86
87bool CAdminAuthorization::executeAsAdmin(const QString &program, const QStringList &arguments)
88{
89 for(const auto &su : suFontends) {
90 auto command = QStandardPaths::findExecutable(su.first);
91 if(command.isEmpty()) continue;
92
93 auto args = su.second;
94 // if("pkexec" == su.first) {
95 // args << "env" << "DISPLAY=" + QString::fromLocal8Bit(qgetenv("DISPLAY"))
96 // << "XAUTHORITY=" + QString::fromLocal8Bit(qgetenv("XAUTHORITY"));
97 // }
98 args << program << arguments;
99 bool bRet = false;
100 if(GetDetached())
101 bRet = QProcess::startDetached(command, args);
102 else
103 bRet = QProcess::execute(command, args);
104 qDebug(log) << "exec" << bRet << command << args;
105 return bRet;
106 }
107
108 return execAdminFallback(program, arguments);
109}
110
111bool execAdminFallback(const QString &program, const QStringList &arguments)
112{
113 // as we cannot pipe the password to su in QProcess, we need to setup a pseudo-terminal for it
114 int masterFD = -1;
115 int slaveFD = -1;
116 char ptsn[ PATH_MAX ];
117
118 if (::openpty(&masterFD, &slaveFD, ptsn, nullptr, nullptr))
119 return false;
120
121 masterFD = ::posix_openpt(O_RDWR | O_NOCTTY);
122 if (masterFD < 0)
123 return false;
124
125 const QByteArray ttyName = ::ptsname(masterFD);
126
127 if (::grantpt(masterFD)) {
128 ::close(masterFD);
129 return false;
130 }
131
132 ::revoke(ttyName);
133 ::unlockpt(masterFD);
134
135 slaveFD = ::open(ttyName, O_RDWR | O_NOCTTY | O_CLOEXEC);
136 if (slaveFD < 0) {
137 ::close(masterFD);
138 return false;
139 }
140
141 ::fcntl(masterFD, F_SETFD, FD_CLOEXEC);
142 ::fcntl(slaveFD, F_SETFD, FD_CLOEXEC);
143 int pipedData[2];
144 if (pipe(pipedData) != 0)
145 return false;
146
147 int flags = ::fcntl(pipedData[0], F_GETFL);
148 if (flags != -1)
149 ::fcntl(pipedData[0], F_SETFL, flags | O_NONBLOCK);
150
151 flags = ::fcntl(masterFD, F_GETFL);
152 if (flags != -1)
153 ::fcntl(masterFD, F_SETFL, flags | O_NONBLOCK);
154
155 pid_t child = fork();
156
157 if (child < -1) {
158 ::close(masterFD);
159 ::close(slaveFD);
160 ::close(pipedData[0]);
161 ::close(pipedData[1]);
162 return false;
163 } else if (child > 0) { // parent process
164 ::close(slaveFD);
165 //close writing end of pipe
166 ::close(pipedData[1]);
167#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
168 QRegExp re{QLatin1String("[Pp]assword.*:")};
169#else
170 QRegularExpression re{QLatin1String("[Pp]assword.*:")};
171#endif
172 QByteArray data;
173 QByteArray errData;
174 int bytes = 0;
175 char buf[1024];
176 char errBuf[1024];
177 while (bytes >= 0) {
178 int state;
179 if (::waitpid(child, &state, WNOHANG) == -1)
180 break;
181
182 bytes = static_cast<int>(::read(masterFD, buf, 1023));
183 if (bytes == -1 && errno == EAGAIN)
184 bytes = 0;
185 else if (bytes > 0) {
186 if(!QByteArray(buf, bytes).simplified().isEmpty())
187 data.append(buf, bytes);
188 else
189 bytes = 0;
190 }
191 auto errBytes = static_cast<int>(::read(pipedData[0], errBuf, 1023));
192 if (errBytes > 0)
193 errData.append(errBuf, errBytes);
194
195 if (bytes > 0) {
196 const auto line = QString::fromLatin1(data);
197#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
198 if (re.indexIn(line) != -1)
199#else
200 QRegularExpressionMatch match = re.match(line);
201 if(match.hasMatch())
202#endif
203 {
204 if(!errData.isEmpty()) {
205 QMessageBox::critical(nullptr, QObject::tr("Critical"),
206 QString::fromLocal8Bit(errData));
207 errData.clear();
208 }
209
210 bool ok = false;
211 const auto password = QInputDialog::getText(
212 nullptr,
213 QObject::tr("AdminAuthorization",
214 "Enter Password"),
215 QObject::tr("AdminAuthorization",
216 "Enter your root password to run the program:"),
217 QLineEdit::Password,
218 QString(),
219 &ok,
220 Qt::WindowCloseButtonHint | Qt::WindowStaysOnTopHint);
221
222 if (!ok) {
223 const auto pwd = password.toLatin1();
224 for (int i = 0; i < 3; ++i) {
225 ::write(masterFD, pwd.data(),
226 static_cast<size_t>(pwd.length()));
227 ::write(masterFD, "\n", 1);
228 }
229 return false;
230 }
231 const auto pwd = password.toLatin1();
232 ::write(masterFD, pwd.data(), static_cast<size_t>(pwd.length()));
233 ::write(masterFD, "\n", 1);
234 ::read(masterFD, buf, static_cast<size_t>(pwd.length()) + 1);
235 }
236 }
237 if (bytes == 0)
238 ::usleep(100000);
239 }
240
241 if (!errData.isEmpty()) {
242 QMessageBox::critical(nullptr, QObject::tr("Critical"),
243 QString::fromLocal8Bit(errData));
244 return false;
245 }
246
247 int status;
248 ::wait(&status);
249 ::close(pipedData[1]);
250 return true;
251 } else {
252 ::close(pipedData[0]);
253 // Reset signal handlers
254 for (int sig = 1; sig < NSIG; ++sig)
255 signal(sig, SIG_DFL);
256 signal(SIGHUP, SIG_IGN);
257
258 ::setsid();
259
260 ::ioctl(slaveFD, TIOCSCTTY, 1);
261 int pgrp = ::getpid();
262 ::tcsetpgrp(slaveFD, pgrp);
263
264 ::dup2(slaveFD, 0);
265 ::dup2(slaveFD, 1);
266 ::dup2(pipedData[1], 2);
267
268 // close all file descriptors
269 struct rlimit rlp;
270 getrlimit(RLIMIT_NOFILE, &rlp);
271 for (int i = 3; i < static_cast<int>(rlp.rlim_cur); ++i)
272 ::close(i);
273
274 QList<QString> args;
275 QString command = QStandardPaths::findExecutable(SU_COMMAND);
276 args.push_back(command);
277 args.push_back("-b");
278 args.push_back("-p");
279 args.push_back("password:");
280 args.push_back(program.toLocal8Bit());
281 for (const auto &argument : arguments)
282 args.push_back(argument.toLocal8Bit());
283 QString szCmd;
284 for(auto &b: args)
285 {
286 szCmd += b + " ";
287 }
288 int nRet = ::system(szCmd.toStdString().c_str());
289 /*
290 int i = 0;
291 char **argp = reinterpret_cast<char **>(::malloc(static_cast<ulong>(args.count()) * sizeof(char *)));
292 for (auto &arg : args)
293 argp[i] = arg.data();
294 argp[i] = nullptr;
295
296 ::unsetenv("LANG");
297 ::unsetenv("LC_ALL");
298
299 ::execv(SU_COMMAND, argp);//*/
300
301 _exit(nRet);
302 return !nRet;
303 }
304}
305
306} // namespace RabbitCommon