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 <jansen@kde.org>
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-kdesu.h>
36 #include <ksud_debug.h>
37 
38 #include <cerrno>
39 #include <ctype.h>
40 #include <fcntl.h>
41 #include <pwd.h>
42 #include <signal.h>
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48 
49 #include <sys/resource.h>
50 #include <sys/socket.h>
51 #include <sys/stat.h>
52 #include <sys/time.h>
53 #include <sys/types.h>
54 #include <sys/un.h>
55 #include <sys/wait.h>
56 #ifdef HAVE_SYS_SELECT_H
57 #include <sys/select.h> // Needed on some systems.
58 #endif
59 
60 #include <QByteArray>
61 #include <QCommandLineParser>
62 #include <QFile>
63 #include <QRegularExpression>
64 #include <QStandardPaths>
65 #include <QVector>
66 
67 #include <KAboutData>
68 #include <KLocalizedString>
69 #include <client.h>
70 #include <defaults.h>
71 
72 #include "handler.h"
73 #include "repo.h"
74 
75 #if HAVE_X11
76 #include <X11/X.h>
77 #include <X11/Xlib.h>
78 #endif
79 
80 #ifndef SUN_LEN
81 #define SUN_LEN(ptr) ((socklen_t)(offsetof(struct sockaddr_un, sun_path) + strlen((ptr)->sun_path)))
82 #endif
83 
84 #define ERR strerror(errno)
85 
86 using namespace KDESu;
87 
88 // Globals
89 
90 Repository *repo;
91 QString Version(QStringLiteral("1.01"));
92 QByteArray sock;
93 #if HAVE_X11
94 Display *x11Display;
95 #endif
96 int pipeOfDeath[2];
97 
kdesud_cleanup()98 void kdesud_cleanup()
99 {
100     unlink(sock.constData());
101 }
102 
103 // Borrowed from kdebase/kaudio/kaudioserver.cpp
104 
105 #if HAVE_X11
106 extern "C" int xio_errhandler(Display *);
107 
xio_errhandler(Display *)108 int xio_errhandler(Display *)
109 {
110     qCCritical(KSUD_LOG) << "Fatal IO error, exiting...\n";
111     kdesud_cleanup();
112     exit(1);
113     return 1; // silence compilers
114 }
115 
initXconnection()116 int initXconnection()
117 {
118     x11Display = XOpenDisplay(nullptr);
119     if (x11Display != nullptr) {
120         XSetIOErrorHandler(xio_errhandler);
121         /* clang-format off */
122         XCreateSimpleWindow(x11Display,
123                             DefaultRootWindow(x11Display),
124                             0, 0, 1, 1, 0,
125                             BlackPixelOfScreen(DefaultScreenOfDisplay(x11Display)),
126                             BlackPixelOfScreen(DefaultScreenOfDisplay(x11Display)));
127         /* clang-format on*/
128         return XConnectionNumber(x11Display);
129     } else {
130         qCWarning(KSUD_LOG) << "Can't connect to the X Server.\n";
131         qCWarning(KSUD_LOG) << "Might not terminate at end of session.\n";
132         return -1;
133     }
134 }
135 #endif
136 
137 extern "C" {
138 void signal_exit(int);
139 void sigchld_handler(int);
140 }
141 
signal_exit(int sig)142 void signal_exit(int sig)
143 {
144     qCDebug(KSUD_LOG) << "Exiting on signal " << sig << "\n";
145     kdesud_cleanup();
146     exit(1);
147 }
148 
sigchld_handler(int)149 void sigchld_handler(int)
150 {
151     char c = ' ';
152     write(pipeOfDeath[1], &c, 1);
153 }
154 
155 /**
156  * Creates an AF_UNIX socket in socket resource, mode 0600.
157  */
158 
create_socket()159 int create_socket()
160 {
161     int sockfd;
162     socklen_t addrlen;
163     struct stat s;
164 
165     QString display = QString::fromLocal8Bit(qgetenv("DISPLAY"));
166     if (display.isEmpty()) {
167         qCWarning(KSUD_LOG) << "$DISPLAY is not set\n";
168         return -1;
169     }
170 
171     // strip the screen number from the display
172     display.remove(QRegularExpression(QStringLiteral("\\.[0-9]+$")));
173 
174     sock = QFile::encodeName(QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation) + QStringLiteral("/kdesud_%1").arg(display));
175     int stat_err = lstat(sock.constData(), &s);
176     if (!stat_err && S_ISLNK(s.st_mode)) {
177         qCWarning(KSUD_LOG) << "Someone is running a symlink attack on you\n";
178         if (unlink(sock.constData())) {
179             qCWarning(KSUD_LOG) << "Could not delete symlink\n";
180             return -1;
181         }
182     }
183 
184     if (!access(sock.constData(), R_OK | W_OK)) {
185         KDEsuClient client;
186         if (client.ping() == -1) {
187             qCWarning(KSUD_LOG) << "stale socket exists\n";
188             if (unlink(sock.constData())) {
189                 qCWarning(KSUD_LOG) << "Could not delete stale socket\n";
190                 return -1;
191             }
192         } else {
193             qCWarning(KSUD_LOG) << "kdesud is already running\n";
194             return -1;
195         }
196     }
197 
198     sockfd = socket(PF_UNIX, SOCK_STREAM, 0);
199     if (sockfd < 0) {
200         qCCritical(KSUD_LOG) << "socket(): " << ERR << "\n";
201         return -1;
202     }
203 
204     // Ensure socket closed on error
205     struct fd_ScopeGuard {
206         fd_ScopeGuard(int fd)
207             : _fd(fd)
208         {
209         }
210         ~fd_ScopeGuard()
211         {
212             if (_fd >= 0) {
213                 close(_fd);
214             }
215         }
216         fd_ScopeGuard(const fd_ScopeGuard &) = delete;
217         fd_ScopeGuard &operator=(const fd_ScopeGuard &) = delete;
218         void reset()
219         {
220             _fd = -1;
221         }
222         int _fd;
223     } guard(sockfd);
224 
225     struct sockaddr_un addr;
226     addr.sun_family = AF_UNIX;
227     strncpy(addr.sun_path, sock.constData(), sizeof(addr.sun_path) - 1);
228     addr.sun_path[sizeof(addr.sun_path) - 1] = '\000';
229     addrlen = SUN_LEN(&addr);
230     if (bind(sockfd, (struct sockaddr *)&addr, addrlen) < 0) {
231         qCCritical(KSUD_LOG) << "bind(): " << ERR << "\n";
232         return -1;
233     }
234 
235     struct linger lin;
236     lin.l_onoff = lin.l_linger = 0;
237     if (setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (char *)&lin, sizeof(linger)) < 0) {
238         qCCritical(KSUD_LOG) << "setsockopt(SO_LINGER): " << ERR << "\n";
239         return -1;
240     }
241 
242     int opt = 1;
243     if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0) {
244         qCCritical(KSUD_LOG) << "setsockopt(SO_REUSEADDR): " << ERR << "\n";
245         return -1;
246     }
247     opt = 1;
248     if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (char *)&opt, sizeof(opt)) < 0) {
249         qCCritical(KSUD_LOG) << "setsockopt(SO_KEEPALIVE): " << ERR << "\n";
250         return -1;
251     }
252     chmod(sock.constData(), 0600);
253     guard.reset();
254     return sockfd;
255 }
256 
257 /**
258  * Main program
259  */
260 
main(int argc,char * argv[])261 int main(int argc, char *argv[])
262 {
263     QCoreApplication app(argc, argv);
264     KAboutData aboutData(QStringLiteral("kdesud") /* componentName */,
265                          i18n("KDE su daemon"),
266                          Version,
267                          i18n("Daemon used by kdesu"),
268                          KAboutLicense::Artistic,
269                          i18n("Copyright (c) 1999,2000 Geert Jansen"));
270     aboutData.addAuthor(i18n("Geert Jansen"), i18n("Author"), QStringLiteral("jansen@kde.org"), QStringLiteral("http://www.stack.nl/~geertj/"));
271 
272     KAboutData::setApplicationData(aboutData);
273     QCommandLineParser parser;
274     aboutData.setupCommandLine(&parser);
275     parser.process(app);
276     aboutData.processCommandLine(&parser);
277 
278     // Set core dump size to 0
279     struct rlimit rlim;
280     rlim.rlim_cur = rlim.rlim_max = 0;
281     if (setrlimit(RLIMIT_CORE, &rlim) < 0) {
282         qCCritical(KSUD_LOG) << "setrlimit(): " << ERR << "\n";
283         exit(1);
284     }
285 
286     // Create the Unix socket.
287     int sockfd = create_socket();
288     if (sockfd < 0) {
289         exit(1);
290     }
291     if (listen(sockfd, 10) < 0) {
292         qCCritical(KSUD_LOG) << "listen(): " << ERR << "\n";
293         kdesud_cleanup();
294         exit(1);
295     }
296     int maxfd = sockfd;
297 
298     // Ok, we're accepting connections. Fork to the background.
299     pid_t pid = fork();
300     if (pid == -1) {
301         qCCritical(KSUD_LOG) << "fork():" << ERR << "\n";
302         kdesud_cleanup();
303         exit(1);
304     }
305     if (pid) {
306         _exit(0);
307     }
308 
309 #if HAVE_X11
310     // Make sure we exit when the display gets closed.
311     int x11Fd = initXconnection();
312     maxfd = qMax(maxfd, x11Fd);
313 #endif
314 
315     repo = new Repository;
316     QVector<ConnectionHandler *> handler;
317 
318     pipe(pipeOfDeath);
319     maxfd = qMax(maxfd, pipeOfDeath[0]);
320 
321     // Signal handlers
322     struct sigaction sa;
323     sa.sa_handler = signal_exit;
324     sigemptyset(&sa.sa_mask);
325     sa.sa_flags = 0;
326     sigaction(SIGHUP, &sa, nullptr);
327     sigaction(SIGINT, &sa, nullptr);
328     sigaction(SIGTERM, &sa, nullptr);
329     sigaction(SIGQUIT, &sa, nullptr);
330 
331     sa.sa_handler = sigchld_handler;
332     sa.sa_flags = SA_NOCLDSTOP;
333     sigaction(SIGCHLD, &sa, nullptr);
334     sa.sa_handler = SIG_IGN;
335     sigaction(SIGPIPE, &sa, nullptr);
336 
337     // Main execution loop
338 
339     socklen_t addrlen;
340     struct sockaddr_un clientname;
341 
342     fd_set tmp_fds;
343     fd_set active_fds;
344     FD_ZERO(&active_fds);
345     FD_SET(sockfd, &active_fds);
346     FD_SET(pipeOfDeath[0], &active_fds);
347 #if HAVE_X11
348     if (x11Fd != -1) {
349         FD_SET(x11Fd, &active_fds);
350     }
351 #endif
352 
353     while (1) {
354         tmp_fds = active_fds;
355 #if HAVE_X11
356         if (x11Display) {
357             XFlush(x11Display);
358         }
359 #endif
360         if (select(maxfd + 1, &tmp_fds, nullptr, nullptr, nullptr) < 0) {
361             if (errno == EINTR) {
362                 continue;
363             }
364 
365             qCCritical(KSUD_LOG) << "select(): " << ERR << "\n";
366             exit(1);
367         }
368         repo->expire();
369         for (int i = 0; i <= maxfd; i++) {
370             if (!FD_ISSET(i, &tmp_fds)) {
371                 continue;
372             }
373 
374             if (i == pipeOfDeath[0]) {
375                 char buf[101];
376                 read(pipeOfDeath[0], buf, 100);
377                 pid_t result;
378                 do {
379                     int status;
380                     result = waitpid((pid_t)-1, &status, WNOHANG);
381                     if (result > 0) {
382                         for (int j = handler.size(); j--;) {
383                             if (handler[j] && (handler[j]->m_pid == result)) {
384                                 handler[j]->m_exitCode = WEXITSTATUS(status);
385                                 handler[j]->m_hasExitCode = true;
386                                 handler[j]->sendExitCode();
387                                 handler[j]->m_pid = 0;
388                                 break;
389                             }
390                         }
391                     }
392                 } while (result > 0);
393             }
394 
395 #if HAVE_X11
396             if (i == x11Fd) {
397                 // Discard X events
398                 XEvent event_return;
399                 if (x11Display) {
400                     while (XPending(x11Display)) {
401                         XNextEvent(x11Display, &event_return);
402                     }
403                 }
404                 continue;
405             }
406 #endif
407 
408             if (i == sockfd) {
409                 // Accept new connection
410                 int fd;
411                 addrlen = 64;
412                 fd = accept(sockfd, (struct sockaddr *)&clientname, &addrlen);
413                 if (fd < 0) {
414                     qCCritical(KSUD_LOG) << "accept():" << ERR << "\n";
415                     continue;
416                 }
417                 while (fd + 1 > (int)handler.size()) {
418                     handler.append(nullptr);
419                 }
420                 delete handler[fd];
421                 handler[fd] = new ConnectionHandler(fd);
422                 maxfd = qMax(maxfd, fd);
423                 FD_SET(fd, &active_fds);
424                 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
425                 continue;
426             }
427 
428             // handle already established connection
429             if (handler[i] && handler[i]->handle() < 0) {
430                 delete handler[i];
431                 handler[i] = nullptr;
432                 FD_CLR(i, &active_fds);
433             }
434         }
435     }
436     qCWarning(KSUD_LOG) << "???\n";
437 }
438