RabbitCommon v2.3.3
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 = QProcess::startDetached(command, args);
100 qDebug(log) << "exec" << bRet << command << args;
101 return bRet;
102 }
103
104 return execAdminFallback(program, arguments);
105}
106
107bool execAdminFallback(const QString &program, const QStringList &arguments)
108{
109 // as we cannot pipe the password to su in QProcess, we need to setup a pseudo-terminal for it
110 int masterFD = -1;
111 int slaveFD = -1;
112 char ptsn[ PATH_MAX ];
113
114 if (::openpty(&masterFD, &slaveFD, ptsn, nullptr, nullptr))
115 return false;
116
117 masterFD = ::posix_openpt(O_RDWR | O_NOCTTY);
118 if (masterFD < 0)
119 return false;
120
121 const QByteArray ttyName = ::ptsname(masterFD);
122
123 if (::grantpt(masterFD)) {
124 ::close(masterFD);
125 return false;
126 }
127
128 ::revoke(ttyName);
129 ::unlockpt(masterFD);
130
131 slaveFD = ::open(ttyName, O_RDWR | O_NOCTTY | O_CLOEXEC);
132 if (slaveFD < 0) {
133 ::close(masterFD);
134 return false;
135 }
136
137 ::fcntl(masterFD, F_SETFD, FD_CLOEXEC);
138 ::fcntl(slaveFD, F_SETFD, FD_CLOEXEC);
139 int pipedData[2];
140 if (pipe(pipedData) != 0)
141 return false;
142
143 int flags = ::fcntl(pipedData[0], F_GETFL);
144 if (flags != -1)
145 ::fcntl(pipedData[0], F_SETFL, flags | O_NONBLOCK);
146
147 flags = ::fcntl(masterFD, F_GETFL);
148 if (flags != -1)
149 ::fcntl(masterFD, F_SETFL, flags | O_NONBLOCK);
150
151 pid_t child = fork();
152
153 if (child < -1) {
154 ::close(masterFD);
155 ::close(slaveFD);
156 ::close(pipedData[0]);
157 ::close(pipedData[1]);
158 return false;
159 } else if (child > 0) { // parent process
160 ::close(slaveFD);
161 //close writing end of pipe
162 ::close(pipedData[1]);
163#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
164 QRegExp re{QLatin1String("[Pp]assword.*:")};
165#else
166 QRegularExpression re{QLatin1String("[Pp]assword.*:")};
167#endif
168 QByteArray data;
169 QByteArray errData;
170 int bytes = 0;
171 char buf[1024];
172 char errBuf[1024];
173 while (bytes >= 0) {
174 int state;
175 if (::waitpid(child, &state, WNOHANG) == -1)
176 break;
177
178 bytes = static_cast<int>(::read(masterFD, buf, 1023));
179 if (bytes == -1 && errno == EAGAIN)
180 bytes = 0;
181 else if (bytes > 0) {
182 if(!QByteArray(buf, bytes).simplified().isEmpty())
183 data.append(buf, bytes);
184 else
185 bytes = 0;
186 }
187 auto errBytes = static_cast<int>(::read(pipedData[0], errBuf, 1023));
188 if (errBytes > 0)
189 errData.append(errBuf, errBytes);
190
191 if (bytes > 0) {
192 const auto line = QString::fromLatin1(data);
193#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
194 if (re.indexIn(line) != -1)
195#else
196 QRegularExpressionMatch match = re.match(line);
197 if(match.hasMatch())
198#endif
199 {
200 if(!errData.isEmpty()) {
201 QMessageBox::critical(nullptr, QObject::tr("Critical"),
202 QString::fromLocal8Bit(errData));
203 errData.clear();
204 }
205
206 bool ok = false;
207 const auto password = QInputDialog::getText(
208 nullptr,
209 QObject::tr("AdminAuthorization",
210 "Enter Password"),
211 QObject::tr("AdminAuthorization",
212 "Enter your root password to run the program:"),
213 QLineEdit::Password,
214 QString(),
215 &ok,
216 Qt::WindowCloseButtonHint | Qt::WindowStaysOnTopHint);
217
218 if (!ok) {
219 const auto pwd = password.toLatin1();
220 for (int i = 0; i < 3; ++i) {
221 ::write(masterFD, pwd.data(),
222 static_cast<size_t>(pwd.length()));
223 ::write(masterFD, "\n", 1);
224 }
225 return false;
226 }
227 const auto pwd = password.toLatin1();
228 ::write(masterFD, pwd.data(), static_cast<size_t>(pwd.length()));
229 ::write(masterFD, "\n", 1);
230 ::read(masterFD, buf, static_cast<size_t>(pwd.length()) + 1);
231 }
232 }
233 if (bytes == 0)
234 ::usleep(100000);
235 }
236
237 if (!errData.isEmpty()) {
238 QMessageBox::critical(nullptr, QObject::tr("Critical"),
239 QString::fromLocal8Bit(errData));
240 return false;
241 }
242
243 int status;
244 ::wait(&status);
245 ::close(pipedData[1]);
246 return true;
247 } else {
248 ::close(pipedData[0]);
249 // Reset signal handlers
250 for (int sig = 1; sig < NSIG; ++sig)
251 signal(sig, SIG_DFL);
252 signal(SIGHUP, SIG_IGN);
253
254 ::setsid();
255
256 ::ioctl(slaveFD, TIOCSCTTY, 1);
257 int pgrp = ::getpid();
258 ::tcsetpgrp(slaveFD, pgrp);
259
260 ::dup2(slaveFD, 0);
261 ::dup2(slaveFD, 1);
262 ::dup2(pipedData[1], 2);
263
264 // close all file descriptors
265 struct rlimit rlp;
266 getrlimit(RLIMIT_NOFILE, &rlp);
267 for (int i = 3; i < static_cast<int>(rlp.rlim_cur); ++i)
268 ::close(i);
269
270 QList<QString> args;
271 QString command = QStandardPaths::findExecutable(SU_COMMAND);
272 args.push_back(command);
273 args.push_back("-b");
274 args.push_back("-p");
275 args.push_back("password:");
276 args.push_back(program.toLocal8Bit());
277 for (const auto &argument : arguments)
278 args.push_back(argument.toLocal8Bit());
279 QString szCmd;
280 for(auto &b: args)
281 {
282 szCmd += b + " ";
283 }
284 int nRet = ::system(szCmd.toStdString().c_str());
285 /*
286 int i = 0;
287 char **argp = reinterpret_cast<char **>(::malloc(static_cast<ulong>(args.count()) * sizeof(char *)));
288 for (auto &arg : args)
289 argp[i] = arg.data();
290 argp[i] = nullptr;
291
292 ::unsetenv("LANG");
293 ::unsetenv("LC_ALL");
294
295 ::execv(SU_COMMAND, argp);//*/
296
297 _exit(nRet);
298 return !nRet;
299 }
300}
301
302} // namespace RabbitCommon