35#include "config-kdesud.h"
36#include <config-kdesu.h>
37#include <ksud_debug.h>
50#include <sys/resource.h>
51#include <sys/socket.h>
57#ifdef HAVE_SYS_SELECT_H
58#include <sys/select.h>
64#include <sys/syscall.h>
68#include <QCommandLineParser>
71#include <QRegularExpression>
72#include <QStandardPaths>
75#include <KLocalizedString>
88#include <sys/procctl.h>
89#elif defined(__linux__)
94#define SUN_LEN(ptr) ((socklen_t)(offsetof(struct sockaddr_un, sun_path) + strlen((ptr)->sun_path)))
97#define ERR strerror(errno)
101static int closeExtraFds()
104 const int res = close_range(4, ~0U, 0);
109 if (errno != ENOSYS) {
112#elif defined(SYS_close_range)
113 const int res = syscall(SYS_close_range, 4, ~0U, 0);
118 if (errno != ENOSYS) {
123#if !defined(__FreeBSD__) && !defined(__OpenBSD__)
126 qCDebug(KSUD_LOG) <<
"close_range function/syscall isn't available, falling back to iterarting "
127 "over '/dev/fd' and closing the file descriptors manually.\n";
129 std::unique_ptr<DIR, int (*)(DIR *)> dirPtr(opendir(
"/dev/fd"), closedir);
135 const int dirFd = dirfd(dirPtr.get());
136 while (
struct dirent *dirEnt = readdir(dirPtr.get())) {
137 const int currFd = std::atoi(dirEnt->d_name);
138 if (currFd > 3 && currFd != dirFd) {
139 closeRes =
close(currFd);
140 if (closeRes == -1) {
154QString Version(QStringLiteral(
"1.01"));
169extern "C" int xio_errhandler(Display *);
171int xio_errhandler(Display *)
173 qCCritical(KSUD_LOG) <<
"Fatal IO error, exiting...\n";
181 x11Display = XOpenDisplay(
nullptr);
182 if (x11Display !=
nullptr) {
183 XSetIOErrorHandler(xio_errhandler);
185 XCreateSimpleWindow(x11Display,
186 DefaultRootWindow(x11Display),
188 BlackPixelOfScreen(DefaultScreenOfDisplay(x11Display)),
189 BlackPixelOfScreen(DefaultScreenOfDisplay(x11Display)));
191 return XConnectionNumber(x11Display);
193 qCWarning(KSUD_LOG) <<
"Can't connect to the X Server.\n";
194 qCWarning(KSUD_LOG) <<
"Might not terminate at end of session.\n";
201void signal_exit(
int);
202void sigchld_handler(
int);
205void signal_exit(
int sig)
207 qCDebug(KSUD_LOG) <<
"Exiting on signal " << sig <<
"\n";
212void sigchld_handler(
int)
215 write(pipeOfDeath[1], &c, 1);
229 if (display.isEmpty()) {
230 qCWarning(KSUD_LOG) <<
"$DISPLAY is not set\n";
238 int stat_err = lstat(sock.
constData(), &s);
239 if (!stat_err && S_ISLNK(s.st_mode)) {
240 qCWarning(KSUD_LOG) <<
"Someone is running a symlink attack on you\n";
242 qCWarning(KSUD_LOG) <<
"Could not delete symlink\n";
247 if (!access(sock.
constData(), R_OK | W_OK)) {
248 KDESu::Client client;
249 if (client.ping() == -1) {
250 qCWarning(KSUD_LOG) <<
"stale socket exists\n";
252 qCWarning(KSUD_LOG) <<
"Could not delete stale socket\n";
256 qCWarning(KSUD_LOG) <<
"kdesud is already running\n";
261 sockfd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
263 qCCritical(KSUD_LOG) <<
"socket(): " << ERR <<
"\n";
268 struct fd_ScopeGuard {
269 fd_ScopeGuard(
int fd)
279 fd_ScopeGuard(
const fd_ScopeGuard &) =
delete;
280 fd_ScopeGuard &operator=(
const fd_ScopeGuard &) =
delete;
288 struct sockaddr_un addr;
289 addr.sun_family = AF_UNIX;
290 strncpy(addr.sun_path, sock.
constData(),
sizeof(addr.sun_path) - 1);
291 addr.sun_path[
sizeof(addr.sun_path) - 1] =
'\000';
292 addrlen = SUN_LEN(&addr);
293 if (bind(sockfd, (
struct sockaddr *)&addr, addrlen) < 0) {
294 qCCritical(KSUD_LOG) <<
"bind(): " << ERR <<
"\n";
299 lin.l_onoff = lin.l_linger = 0;
300 if (setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (
char *)&lin,
sizeof(linger)) < 0) {
301 qCCritical(KSUD_LOG) <<
"setsockopt(SO_LINGER): " << ERR <<
"\n";
306 if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (
char *)&opt,
sizeof(opt)) < 0) {
307 qCCritical(KSUD_LOG) <<
"setsockopt(SO_REUSEADDR): " << ERR <<
"\n";
311 if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (
char *)&opt,
sizeof(opt)) < 0) {
312 qCCritical(KSUD_LOG) <<
"setsockopt(SO_KEEPALIVE): " << ERR <<
"\n";
320static bool prevent_tracing()
323#ifdef PR_SET_DUMPABLE
325 r = prctl(PR_SET_DUMPABLE, 0, 0, 0, 0);
326#elif defined(PROC_TRACE_CTL)
328 int disable = PROC_TRACE_CTL_DISABLE_EXEC;
329 r = procctl(P_PID, 0, PROC_TRACE_CTL, &disable);
331#warning Missing implementation for disabling traceability on this platform
341int main(
int argc,
char *argv[])
343 if (!prevent_tracing()) {
344 qCWarning(KSUD_LOG) <<
"failed to make process memory untraceable" << strerror(errno);
348 KAboutData aboutData(QStringLiteral(
"kdesud") ,
349 i18n(
"KDE su daemon"),
351 i18n(
"Daemon used by kdesu"),
353 i18n(
"Copyright (c) 1999,2000 Geert Jansen"));
354 aboutData.addAuthor(
i18n(
"Geert Jansen"),
i18n(
"Author"), QStringLiteral(
"jansen@kde.org"), QStringLiteral(
"http://www.stack.nl/~geertj/"));
358 aboutData.setupCommandLine(&parser);
360 aboutData.processCommandLine(&parser);
364 rlim.rlim_cur = rlim.rlim_max = 0;
365 if (setrlimit(RLIMIT_CORE, &rlim) < 0) {
366 qCCritical(KSUD_LOG) <<
"setrlimit(): " << ERR <<
"\n";
371 int sockfd = create_socket();
375 if (listen(sockfd, 10) < 0) {
376 qCCritical(KSUD_LOG) <<
"listen(): " << ERR <<
"\n";
382 sockfd = dup3(sockfd, 3, O_CLOEXEC);
385 qCCritical(KSUD_LOG) <<
"Failed to set sockfd to fd 3" << ERR <<
"\n";
392 if (closeExtraFds() < 0) {
393 qCCritical(KSUD_LOG) <<
"Failed to close file descriptors higher than 3, with error:" << ERR <<
"\n";
401 qCCritical(KSUD_LOG) <<
"fork():" << ERR <<
"\n";
411 int x11Fd = initXconnection();
412 maxfd = qMax(maxfd, x11Fd);
418 pipe2(pipeOfDeath, O_CLOEXEC);
419 maxfd = qMax(maxfd, pipeOfDeath[0]);
423 sa.sa_handler = signal_exit;
424 sigemptyset(&sa.sa_mask);
426 sigaction(SIGHUP, &sa,
nullptr);
427 sigaction(SIGINT, &sa,
nullptr);
428 sigaction(SIGTERM, &sa,
nullptr);
429 sigaction(SIGQUIT, &sa,
nullptr);
431 sa.sa_handler = sigchld_handler;
432 sa.sa_flags = SA_NOCLDSTOP;
433 sigaction(SIGCHLD, &sa,
nullptr);
434 sa.sa_handler = SIG_IGN;
435 sigaction(SIGPIPE, &sa,
nullptr);
440 struct sockaddr_un clientname;
444 FD_ZERO(&active_fds);
445 FD_SET(sockfd, &active_fds);
446 FD_SET(pipeOfDeath[0], &active_fds);
449 FD_SET(x11Fd, &active_fds);
454 tmp_fds = active_fds;
460 if (select(maxfd + 1, &tmp_fds,
nullptr,
nullptr,
nullptr) < 0) {
461 if (errno == EINTR) {
465 qCCritical(KSUD_LOG) <<
"select(): " << ERR <<
"\n";
469 for (
int i = 0; i <= maxfd; i++) {
470 if (!FD_ISSET(i, &tmp_fds)) {
474 if (i == pipeOfDeath[0]) {
476 read(pipeOfDeath[0], buf, 100);
480 result = waitpid((pid_t)-1, &
status, WNOHANG);
482 for (
int j = handler.
size(); j--;) {
483 if (handler[j] && (handler[j]->m_pid == result)) {
484 handler[j]->m_exitCode = WEXITSTATUS(
status);
485 handler[j]->m_hasExitCode =
true;
486 handler[j]->sendExitCode();
487 handler[j]->m_pid = 0;
492 }
while (result > 0);
500 while (XPending(x11Display)) {
501 XNextEvent(x11Display, &event_return);
512 fd = accept(sockfd, (
struct sockaddr *)&clientname, &addrlen);
514 qCCritical(KSUD_LOG) <<
"accept():" << ERR <<
"\n";
517 while (fd + 1 > (
int)handler.
size()) {
522 maxfd = qMax(maxfd, fd);
523 FD_SET(fd, &active_fds);
524 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
529 if (handler[i] && handler[i]->handle() < 0) {
531 handler[i] =
nullptr;
532 FD_CLR(i, &active_fds);
536 qCWarning(KSUD_LOG) <<
"???\n";
A ConnectionHandler handles a client.
static void setApplicationData(const KAboutData &aboutData)
int expire()
Remove data elements which are expired.
Q_SCRIPTABLE CaptureState status()
QString i18n(const char *text, const TYPE &arg...)
KIOCORE_EXPORT ChmodJob * chmod(const KFileItemList &lstItems, int permissions, int mask, const QString &newOwner, const QString &newGroup, bool recursive, JobFlags flags=DefaultFlags)
KIOCORE_EXPORT StatJob * stat(const QUrl &url, JobFlags flags=DefaultFlags)
QVariant read(const QByteArray &data, int versionOverride=0)
const QList< QKeySequence > & close()
const char * constData() const const
void process(const QCoreApplication &app)
QByteArray encodeName(const QString &fileName)
void append(QList< T > &&value)
qsizetype size() const const
QString writableLocation(StandardLocation type)
QString fromLocal8Bit(QByteArrayView str)