diff options
Diffstat (limited to 'kdesu/kdesud/kdesud.cpp')
-rw-r--r-- | kdesu/kdesud/kdesud.cpp | 415 |
1 files changed, 415 insertions, 0 deletions
diff --git a/kdesu/kdesud/kdesud.cpp b/kdesu/kdesud/kdesud.cpp new file mode 100644 index 000000000..d81b6a0c9 --- /dev/null +++ b/kdesu/kdesud/kdesud.cpp @@ -0,0 +1,415 @@ +/* vi: ts=8 sts=4 sw=4 + * + * This file is part of the KDE project, module kdesu. + * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org> + * + * + * kdesud.cpp: KDE su daemon. Offers "keep password" functionality to kde su. + * + * The socket $KDEHOME/socket-$(HOSTNAME)/kdesud_$(display) is used for communication with + * client programs. + * + * The protocol: Client initiates the connection. All commands and responses + * are terminated by a newline. + * + * Client Server Description + * ------ ------ ----------- + * + * PASS <pass> <timeout> OK Set password for commands in + * this session. Password is + * valid for <timeout> seconds. + * + * USER <user> OK Set the target user [required] + * + * EXEC <command> OK Execute command <command>. If + * NO <command> has been executed + * before (< timeout) no PASS + * command is needed. + * + * DEL <command> OK Delete password for command + * NO <command>. + * + * PING OK Ping the server (diagnostics). + */ + + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> +#include <string.h> +#include <stdarg.h> +#include <signal.h> +#include <pwd.h> +#include <errno.h> + +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/resource.h> +#include <sys/wait.h> +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> // Needed on some systems. +#endif + +#include <qptrvector.h> +#include <qfile.h> +#include <qregexp.h> + +#include <kinstance.h> +#include <kdebug.h> +#include <klocale.h> +#include <kcmdlineargs.h> +#include <kstandarddirs.h> +#include <kaboutdata.h> +#include <kdesu/client.h> +#include <kdesu/defaults.h> +#include <ksockaddr.h> + +#include "repo.h" +#include "handler.h" + +#include <X11/X.h> +#include <X11/Xlib.h> + +#ifndef SUN_LEN +#define SUN_LEN(ptr) ((kde_socklen_t) (((struct sockaddr_un *) 0)->sun_path) \ + + strlen ((ptr)->sun_path)) +#endif + +#define ERR strerror(errno) + +// Globals + +Repository *repo; +const char *Version = "1.01"; +QCString sock; +Display *x11Display; +int pipeOfDeath[2]; + + +void kdesud_cleanup() +{ + unlink(sock); +} + + +// Borrowed from kdebase/kaudio/kaudioserver.cpp + +extern "C" int xio_errhandler(Display *); + +int xio_errhandler(Display *) +{ + kdError(1205) << "Fatal IO error, exiting...\n"; + kdesud_cleanup(); + exit(1); + return 1; //silence compilers +} + +int initXconnection() +{ + x11Display = XOpenDisplay(NULL); + if (x11Display != 0L) + { + XSetIOErrorHandler(xio_errhandler); + XCreateSimpleWindow(x11Display, DefaultRootWindow(x11Display), + 0, 0, 1, 1, 0, + BlackPixelOfScreen(DefaultScreenOfDisplay(x11Display)), + BlackPixelOfScreen(DefaultScreenOfDisplay(x11Display))); + return XConnectionNumber(x11Display); + } else + { + kdWarning(1205) << "Can't connect to the X Server.\n"; + kdWarning(1205) << "Might not terminate at end of session.\n"; + return -1; + } +} + +extern "C" { + void signal_exit(int); + void sigchld_handler(int); +} + +void signal_exit(int sig) +{ + kdDebug(1205) << "Exiting on signal " << sig << "\n"; + kdesud_cleanup(); + exit(1); +} + +void sigchld_handler(int) +{ + char c = ' '; + write(pipeOfDeath[1], &c, 1); +} + +/** + * Creates an AF_UNIX socket in socket resource, mode 0600. + */ + +int create_socket() +{ + int sockfd; + ksocklen_t addrlen; + struct stat s; + + QCString display(getenv("DISPLAY")); + if (display.isEmpty()) + { + kdWarning(1205) << "$DISPLAY is not set\n"; + return -1; + } + + // strip the screen number from the display + display.replace(QRegExp("\\.[0-9]+$"), ""); + + sock = QFile::encodeName(locateLocal("socket", QString("kdesud_%1").arg(display))); + int stat_err=lstat(sock, &s); + if(!stat_err && S_ISLNK(s.st_mode)) { + kdWarning(1205) << "Someone is running a symlink attack on you\n"; + if(unlink(sock)) { + kdWarning(1205) << "Could not delete symlink\n"; + return -1; + } + } + + if (!access(sock, R_OK|W_OK)) + { + KDEsuClient client; + if (client.ping() == -1) + { + kdWarning(1205) << "stale socket exists\n"; + if (unlink(sock)) + { + kdWarning(1205) << "Could not delete stale socket\n"; + return -1; + } + } else + { + kdWarning(1205) << "kdesud is already running\n"; + return -1; + } + + } + + sockfd = socket(PF_UNIX, SOCK_STREAM, 0); + if (sockfd < 0) + { + kdError(1205) << "socket(): " << ERR << "\n"; + return -1; + } + + struct sockaddr_un addr; + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, sock, sizeof(addr.sun_path)-1); + addr.sun_path[sizeof(addr.sun_path)-1] = '\000'; + addrlen = SUN_LEN(&addr); + if (bind(sockfd, (struct sockaddr *)&addr, addrlen) < 0) + { + kdError(1205) << "bind(): " << ERR << "\n"; + return -1; + } + + struct linger lin; + lin.l_onoff = lin.l_linger = 0; + if (setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (char *) &lin, + sizeof(linger)) < 0) + { + kdError(1205) << "setsockopt(SO_LINGER): " << ERR << "\n"; + return -1; + } + + int opt = 1; + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, + sizeof(opt)) < 0) + { + kdError(1205) << "setsockopt(SO_REUSEADDR): " << ERR << "\n"; + return -1; + } + opt = 1; + if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt, + sizeof(opt)) < 0) + { + kdError(1205) << "setsockopt(SO_KEEPALIVE): " << ERR << "\n"; + return -1; + } + chmod(sock, 0600); + return sockfd; +} + + +/** + * Main program + */ + +int main(int argc, char *argv[]) +{ + KAboutData aboutData("kdesud", I18N_NOOP("KDE su daemon"), + Version, I18N_NOOP("Daemon used by kdesu"), + KAboutData::License_Artistic, + "Copyright (c) 1999,2000 Geert Jansen"); + aboutData.addAuthor("Geert Jansen", I18N_NOOP("Author"), + "jansen@kde.org", "http://www.stack.nl/~geertj/"); + KCmdLineArgs::init(argc, argv, &aboutData); + KInstance instance(&aboutData); + + // Set core dump size to 0 + struct rlimit rlim; + rlim.rlim_cur = rlim.rlim_max = 0; + if (setrlimit(RLIMIT_CORE, &rlim) < 0) + { + kdError(1205) << "setrlimit(): " << ERR << "\n"; + exit(1); + } + + // Create the Unix socket. + int sockfd = create_socket(); + if (sockfd < 0) + exit(1); + if (listen(sockfd, 1) < 0) + { + kdError(1205) << "listen(): " << ERR << "\n"; + kdesud_cleanup(); + exit(1); + } + int maxfd = sockfd; + + // Ok, we're accepting connections. Fork to the background. + pid_t pid = fork(); + if (pid == -1) + { + kdError(1205) << "fork():" << ERR << "\n"; + kdesud_cleanup(); + exit(1); + } + if (pid) + exit(0); + + // Make sure we exit when the display gets closed. + int x11Fd = initXconnection(); + maxfd = QMAX(maxfd, x11Fd); + + repo = new Repository; + QPtrVector<ConnectionHandler> handler; + handler.setAutoDelete(true); + + pipe(pipeOfDeath); + maxfd = QMAX(maxfd, pipeOfDeath[0]); + + // Signal handlers + struct sigaction sa; + sa.sa_handler = signal_exit; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sigaction(SIGHUP, &sa, 0L); + sigaction(SIGINT, &sa, 0L); + sigaction(SIGTERM, &sa, 0L); + sigaction(SIGQUIT, &sa, 0L); + + sa.sa_handler = sigchld_handler; + sa.sa_flags = SA_NOCLDSTOP; + sigaction(SIGCHLD, &sa, 0L); + sa.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &sa, 0L); + + // Main execution loop + + ksocklen_t addrlen; + struct sockaddr_un clientname; + + fd_set tmp_fds, active_fds; + FD_ZERO(&active_fds); + FD_SET(sockfd, &active_fds); + FD_SET(pipeOfDeath[0], &active_fds); + if (x11Fd != -1) + FD_SET(x11Fd, &active_fds); + + while (1) + { + tmp_fds = active_fds; + if(x11Display) + XFlush(x11Display); + if (select(maxfd+1, &tmp_fds, 0L, 0L, 0L) < 0) + { + if (errno == EINTR) continue; + + kdError(1205) << "select(): " << ERR << "\n"; + exit(1); + } + repo->expire(); + for (int i=0; i<=maxfd; i++) + { + if (!FD_ISSET(i, &tmp_fds)) + continue; + + if (i == pipeOfDeath[0]) + { + char buf[101]; + read(pipeOfDeath[0], buf, 100); + pid_t result; + do + { + int status; + result = waitpid((pid_t)-1, &status, WNOHANG); + if (result > 0) + { + for(int j=handler.size(); j--;) + { + if (handler[j] && (handler[j]->m_pid == result)) + { + handler[j]->m_exitCode = WEXITSTATUS(status); + handler[j]->m_hasExitCode = true; + handler[j]->sendExitCode(); + handler[j]->m_pid = 0; + break; + } + } + } + } + while(result > 0); + } + + if (i == x11Fd) + { + // Discard X events + XEvent event_return; + if (x11Display) + while(XPending(x11Display)) + XNextEvent(x11Display, &event_return); + continue; + } + + if (i == sockfd) + { + // Accept new connection + int fd; + addrlen = 64; + fd = accept(sockfd, (struct sockaddr *) &clientname, &addrlen); + if (fd < 0) + { + kdError(1205) << "accept():" << ERR << "\n"; + continue; + } + if (fd+1 > (int) handler.size()) + handler.resize(fd+1); + handler.insert(fd, new ConnectionHandler(fd)); + maxfd = QMAX(maxfd, fd); + FD_SET(fd, &active_fds); + continue; + } + + // handle alreay established connection + if (handler[i] && handler[i]->handle() < 0) + { + handler.remove(i); + FD_CLR(i, &active_fds); + } + } + } + kdWarning(1205) << "???\n"; +} + |