KDESu

kdesud.cpp
1 /* vi: ts=8 sts=4 sw=4
2 
3  This file is part of the KDE project, module kdesu.
4  SPDX-FileCopyrightText: 1999, 2000 Geert Jansen <[email protected]>
5 
6 
7  kdesud.cpp: KDE su daemon. Offers "keep password" functionality to kde su.
8 
9  The socket $KDEHOME/socket-$(HOSTNAME)/kdesud_$(display) is used for communication with
10  client programs.
11 
12  The protocol: Client initiates the connection. All commands and responses
13  are terminated by a newline.
14 
15  Client Server Description
16  ------ ------ -----------
17 
18  PASS <pass> <timeout> OK Set password for commands in
19  this session. Password is
20  valid for <timeout> seconds.
21 
22  USER <user> OK Set the target user [required]
23 
24  EXEC <command> OK Execute command <command>. If
25  NO <command> has been executed
26  before (< timeout) no PASS
27  command is needed.
28 
29  DEL <command> OK Delete password for command
30  NO <command>.
31 
32  PING OK Ping the server (diagnostics).
33 */
34 
35 #include "config-kdesud.h"
36 #include <config-kdesu.h>
37 #include <ksud_debug.h>
38 
39 #include <cerrno>
40 #include <ctype.h>
41 #include <fcntl.h>
42 #include <pwd.h>
43 #include <signal.h>
44 #include <stdarg.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49 
50 #include <sys/resource.h>
51 #include <sys/socket.h>
52 #include <sys/stat.h>
53 #include <sys/time.h>
54 #include <sys/types.h>
55 #include <sys/un.h>
56 #include <sys/wait.h>
57 #ifdef HAVE_SYS_SELECT_H
58 #include <sys/select.h> // Needed on some systems.
59 #endif
60 
61 #include <dirent.h>
62 
63 #if !HAVE_CLOSE_RANGE
64 #include <sys/syscall.h> // close_range syscall
65 #endif
66 
67 #include <QByteArray>
68 #include <QCommandLineParser>
69 #include <QFile>
70 #include <QRegularExpression>
71 #include <QStandardPaths>
72 #include <QVector>
73 
74 #include <KAboutData>
75 #include <KLocalizedString>
76 #include <client.h>
77 #include <defaults.h>
78 
79 #include "handler.h"
80 #include "repo.h"
81 
82 #if HAVE_X11
83 #include <X11/X.h>
84 #include <X11/Xlib.h>
85 #endif
86 
87 #ifndef SUN_LEN
88 #define SUN_LEN(ptr) ((socklen_t)(offsetof(struct sockaddr_un, sun_path) + strlen((ptr)->sun_path)))
89 #endif
90 
91 #define ERR strerror(errno)
92 
93 using namespace KDESu;
94 
95 static int closeExtraFds()
96 {
97 #if HAVE_CLOSE_RANGE
98  const int res = close_range(4, ~0U, 0);
99  if (res == 0) {
100  return 0;
101  }
102  // If ENOSYS, fallback to opendir/readdir/closedir below
103  if (errno != ENOSYS) {
104  return -1;
105  }
106 #elif defined(SYS_close_range)
107  const int res = syscall(SYS_close_range, 4, ~0U, 0);
108  if (res == 0) {
109  return 0;
110  }
111  // If ENOSYS, fallback to opendir/readdir/closedir below
112  if (errno != ENOSYS) {
113  return -1;
114  }
115 #endif
116 
117 #if !defined(__FreeBSD__) && !defined(__OpenBSD__) // /proc, /dev are Linux only
118  // close_range isn't available, fallback to iterarting over "/dev/fd/"
119  // and close the fd's manually
120  qCDebug(KSUD_LOG) << "close_range function/syscall isn't available, falling back to iterarting "
121  "over '/dev/fd' and closing the file descriptors manually.\n";
122 
123  std::unique_ptr<DIR, int (*)(DIR *)> dirPtr(opendir("/dev/fd"), closedir);
124  if (!dirPtr) {
125  return -1;
126  }
127 
128  int closeRes = 0;
129  const int dirFd = dirfd(dirPtr.get());
130  while (struct dirent *dirEnt = readdir(dirPtr.get())) {
131  const int currFd = std::atoi(dirEnt->d_name);
132  if (currFd > 3 && currFd != dirFd) {
133  closeRes = close(currFd);
134  if (closeRes == -1) {
135  break;
136  }
137  }
138  }
139  return closeRes;
140 #else
141  return -1;
142 #endif
143 }
144 
145 // Globals
146 
147 Repository *repo;
148 QString Version(QStringLiteral("1.01"));
149 QByteArray sock;
150 #if HAVE_X11
151 Display *x11Display;
152 #endif
153 int pipeOfDeath[2];
154 
155 void kdesud_cleanup()
156 {
157  unlink(sock.constData());
158 }
159 
160 // Borrowed from kdebase/kaudio/kaudioserver.cpp
161 
162 #if HAVE_X11
163 extern "C" int xio_errhandler(Display *);
164 
165 int xio_errhandler(Display *)
166 {
167  qCCritical(KSUD_LOG) << "Fatal IO error, exiting...\n";
168  kdesud_cleanup();
169  exit(1);
170  return 1; // silence compilers
171 }
172 
173 int initXconnection()
174 {
175  x11Display = XOpenDisplay(nullptr);
176  if (x11Display != nullptr) {
177  XSetIOErrorHandler(xio_errhandler);
178  /* clang-format off */
179  XCreateSimpleWindow(x11Display,
180  DefaultRootWindow(x11Display),
181  0, 0, 1, 1, 0,
182  BlackPixelOfScreen(DefaultScreenOfDisplay(x11Display)),
183  BlackPixelOfScreen(DefaultScreenOfDisplay(x11Display)));
184  /* clang-format on*/
185  return XConnectionNumber(x11Display);
186  } else {
187  qCWarning(KSUD_LOG) << "Can't connect to the X Server.\n";
188  qCWarning(KSUD_LOG) << "Might not terminate at end of session.\n";
189  return -1;
190  }
191 }
192 #endif
193 
194 extern "C" {
195 void signal_exit(int);
196 void sigchld_handler(int);
197 }
198 
199 void signal_exit(int sig)
200 {
201  qCDebug(KSUD_LOG) << "Exiting on signal " << sig << "\n";
202  kdesud_cleanup();
203  exit(1);
204 }
205 
206 void sigchld_handler(int)
207 {
208  char c = ' ';
209  write(pipeOfDeath[1], &c, 1);
210 }
211 
212 /**
213  * Creates an AF_UNIX socket in socket resource, mode 0600.
214  */
215 
216 int create_socket()
217 {
218  int sockfd;
219  socklen_t addrlen;
220  struct stat s;
221 
222  QString display = QString::fromLocal8Bit(qgetenv("DISPLAY"));
223  if (display.isEmpty()) {
224  qCWarning(KSUD_LOG) << "$DISPLAY is not set\n";
225  return -1;
226  }
227 
228  // strip the screen number from the display
229  display.remove(QRegularExpression(QStringLiteral("\\.[0-9]+$")));
230 
231  sock = QFile::encodeName(QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation) + QStringLiteral("/kdesud_%1").arg(display));
232  int stat_err = lstat(sock.constData(), &s);
233  if (!stat_err && S_ISLNK(s.st_mode)) {
234  qCWarning(KSUD_LOG) << "Someone is running a symlink attack on you\n";
235  if (unlink(sock.constData())) {
236  qCWarning(KSUD_LOG) << "Could not delete symlink\n";
237  return -1;
238  }
239  }
240 
241  if (!access(sock.constData(), R_OK | W_OK)) {
242  KDEsuClient client;
243  if (client.ping() == -1) {
244  qCWarning(KSUD_LOG) << "stale socket exists\n";
245  if (unlink(sock.constData())) {
246  qCWarning(KSUD_LOG) << "Could not delete stale socket\n";
247  return -1;
248  }
249  } else {
250  qCWarning(KSUD_LOG) << "kdesud is already running\n";
251  return -1;
252  }
253  }
254 
255  sockfd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
256  if (sockfd < 0) {
257  qCCritical(KSUD_LOG) << "socket(): " << ERR << "\n";
258  return -1;
259  }
260 
261  // Ensure socket closed on error
262  struct fd_ScopeGuard {
263  fd_ScopeGuard(int fd)
264  : _fd(fd)
265  {
266  }
267  ~fd_ScopeGuard()
268  {
269  if (_fd >= 0) {
270  close(_fd);
271  }
272  }
273  fd_ScopeGuard(const fd_ScopeGuard &) = delete;
274  fd_ScopeGuard &operator=(const fd_ScopeGuard &) = delete;
275  void reset()
276  {
277  _fd = -1;
278  }
279  int _fd;
280  } guard(sockfd);
281 
282  struct sockaddr_un addr;
283  addr.sun_family = AF_UNIX;
284  strncpy(addr.sun_path, sock.constData(), sizeof(addr.sun_path) - 1);
285  addr.sun_path[sizeof(addr.sun_path) - 1] = '\000';
286  addrlen = SUN_LEN(&addr);
287  if (bind(sockfd, (struct sockaddr *)&addr, addrlen) < 0) {
288  qCCritical(KSUD_LOG) << "bind(): " << ERR << "\n";
289  return -1;
290  }
291 
292  struct linger lin;
293  lin.l_onoff = lin.l_linger = 0;
294  if (setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (char *)&lin, sizeof(linger)) < 0) {
295  qCCritical(KSUD_LOG) << "setsockopt(SO_LINGER): " << ERR << "\n";
296  return -1;
297  }
298 
299  int opt = 1;
300  if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0) {
301  qCCritical(KSUD_LOG) << "setsockopt(SO_REUSEADDR): " << ERR << "\n";
302  return -1;
303  }
304  opt = 1;
305  if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (char *)&opt, sizeof(opt)) < 0) {
306  qCCritical(KSUD_LOG) << "setsockopt(SO_KEEPALIVE): " << ERR << "\n";
307  return -1;
308  }
309  chmod(sock.constData(), 0600);
310  guard.reset();
311  return sockfd;
312 }
313 
314 /**
315  * Main program
316  */
317 
318 int main(int argc, char *argv[])
319 {
320  QCoreApplication app(argc, argv);
321  KAboutData aboutData(QStringLiteral("kdesud") /* componentName */,
322  i18n("KDE su daemon"),
323  Version,
324  i18n("Daemon used by kdesu"),
326  i18n("Copyright (c) 1999,2000 Geert Jansen"));
327  aboutData.addAuthor(i18n("Geert Jansen"), i18n("Author"), QStringLiteral("[email protected]"), QStringLiteral("http://www.stack.nl/~geertj/"));
328 
330  QCommandLineParser parser;
331  aboutData.setupCommandLine(&parser);
332  parser.process(app);
333  aboutData.processCommandLine(&parser);
334 
335  // Set core dump size to 0
336  struct rlimit rlim;
337  rlim.rlim_cur = rlim.rlim_max = 0;
338  if (setrlimit(RLIMIT_CORE, &rlim) < 0) {
339  qCCritical(KSUD_LOG) << "setrlimit(): " << ERR << "\n";
340  exit(1);
341  }
342 
343  // Create the Unix socket.
344  int sockfd = create_socket();
345  if (sockfd < 0) {
346  exit(1);
347  }
348  if (listen(sockfd, 10) < 0) {
349  qCCritical(KSUD_LOG) << "listen(): " << ERR << "\n";
350  kdesud_cleanup();
351  exit(1);
352  }
353 
354  if (sockfd != 3) {
355  sockfd = dup3(sockfd, 3, O_CLOEXEC);
356  }
357  if (sockfd < 0) {
358  qCCritical(KSUD_LOG) << "Failed to set sockfd to fd 3" << ERR << "\n";
359  kdesud_cleanup();
360  exit(1);
361  }
362 
363  int maxfd = sockfd;
364 
365  if (closeExtraFds() < 0) {
366  qCCritical(KSUD_LOG) << "Failed to close file descriptors higher than 3, with error:" << ERR << "\n";
367  kdesud_cleanup();
368  exit(1);
369  }
370 
371  // Ok, we're accepting connections. Fork to the background.
372  pid_t pid = fork();
373  if (pid == -1) {
374  qCCritical(KSUD_LOG) << "fork():" << ERR << "\n";
375  kdesud_cleanup();
376  exit(1);
377  }
378  if (pid) {
379  _exit(0);
380  }
381 
382 #if HAVE_X11
383  // Make sure we exit when the display gets closed.
384  int x11Fd = initXconnection();
385  maxfd = qMax(maxfd, x11Fd);
386 #endif
387 
388  repo = new Repository;
390 
391  pipe2(pipeOfDeath, O_CLOEXEC);
392  maxfd = qMax(maxfd, pipeOfDeath[0]);
393 
394  // Signal handlers
395  struct sigaction sa;
396  sa.sa_handler = signal_exit;
397  sigemptyset(&sa.sa_mask);
398  sa.sa_flags = 0;
399  sigaction(SIGHUP, &sa, nullptr);
400  sigaction(SIGINT, &sa, nullptr);
401  sigaction(SIGTERM, &sa, nullptr);
402  sigaction(SIGQUIT, &sa, nullptr);
403 
404  sa.sa_handler = sigchld_handler;
405  sa.sa_flags = SA_NOCLDSTOP;
406  sigaction(SIGCHLD, &sa, nullptr);
407  sa.sa_handler = SIG_IGN;
408  sigaction(SIGPIPE, &sa, nullptr);
409 
410  // Main execution loop
411 
412  socklen_t addrlen;
413  struct sockaddr_un clientname;
414 
415  fd_set tmp_fds;
416  fd_set active_fds;
417  FD_ZERO(&active_fds);
418  FD_SET(sockfd, &active_fds);
419  FD_SET(pipeOfDeath[0], &active_fds);
420 #if HAVE_X11
421  if (x11Fd != -1) {
422  FD_SET(x11Fd, &active_fds);
423  }
424 #endif
425 
426  while (1) {
427  tmp_fds = active_fds;
428 #if HAVE_X11
429  if (x11Display) {
430  XFlush(x11Display);
431  }
432 #endif
433  if (select(maxfd + 1, &tmp_fds, nullptr, nullptr, nullptr) < 0) {
434  if (errno == EINTR) {
435  continue;
436  }
437 
438  qCCritical(KSUD_LOG) << "select(): " << ERR << "\n";
439  exit(1);
440  }
441  repo->expire();
442  for (int i = 0; i <= maxfd; i++) {
443  if (!FD_ISSET(i, &tmp_fds)) {
444  continue;
445  }
446 
447  if (i == pipeOfDeath[0]) {
448  char buf[101];
449  read(pipeOfDeath[0], buf, 100);
450  pid_t result;
451  do {
452  int status;
453  result = waitpid((pid_t)-1, &status, WNOHANG);
454  if (result > 0) {
455  for (int j = handler.size(); j--;) {
456  if (handler[j] && (handler[j]->m_pid == result)) {
457  handler[j]->m_exitCode = WEXITSTATUS(status);
458  handler[j]->m_hasExitCode = true;
459  handler[j]->sendExitCode();
460  handler[j]->m_pid = 0;
461  break;
462  }
463  }
464  }
465  } while (result > 0);
466  }
467 
468 #if HAVE_X11
469  if (i == x11Fd) {
470  // Discard X events
471  XEvent event_return;
472  if (x11Display) {
473  while (XPending(x11Display)) {
474  XNextEvent(x11Display, &event_return);
475  }
476  }
477  continue;
478  }
479 #endif
480 
481  if (i == sockfd) {
482  // Accept new connection
483  int fd;
484  addrlen = 64;
485  fd = accept(sockfd, (struct sockaddr *)&clientname, &addrlen);
486  if (fd < 0) {
487  qCCritical(KSUD_LOG) << "accept():" << ERR << "\n";
488  continue;
489  }
490  while (fd + 1 > (int)handler.size()) {
491  handler.append(nullptr);
492  }
493  delete handler[fd];
494  handler[fd] = new ConnectionHandler(fd);
495  maxfd = qMax(maxfd, fd);
496  FD_SET(fd, &active_fds);
497  fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
498  continue;
499  }
500 
501  // handle already established connection
502  if (handler[i] && handler[i]->handle() < 0) {
503  delete handler[i];
504  handler[i] = nullptr;
505  FD_CLR(i, &active_fds);
506  }
507  }
508  }
509  qCWarning(KSUD_LOG) << "???\n";
510 }
static void setApplicationData(const KAboutData &aboutData)
QByteArray encodeName(const QString &fileName)
void append(const T &value)
QString writableLocation(QStandardPaths::StandardLocation type)
int chmod(const QString &path, mode_t mode)
QTcpServer * listen(const QString &protocol, const QHostAddress &address=QHostAddress::Any, quint16 port=0, QObject *parent=nullptr)
KGuiItem reset()
QString i18n(const char *text, const TYPE &arg...)
int lstat(const QString &path, KDE_struct_stat *buf)
QString fromLocal8Bit(const char *str, int size)
void process(const QStringList &arguments)
NETWORKMANAGERQT_EXPORT NetworkManager::Status status()
QAction * close(const QObject *recvr, const char *slot, QObject *parent)
String repository.
Definition: repo.h:27
A ConnectionHandler handles a client.
Definition: handler.h:22
const char * constData() const const
int expire()
Remove data elements which are expired.
Definition: repo.cpp:151
QVariant read(const QByteArray &data, int versionOverride=0)
int size() const const
int access(const QString &path, int mode)
int stat(const QString &path, KDE_struct_stat *buf)
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Sun Jun 26 2022 03:48:58 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.