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