|
|
|
/* 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/prctl.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 <tqptrvector.h>
|
|
|
|
#include <tqfile.h>
|
|
|
|
#include <tqregexp.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";
|
|
|
|
TQCString 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;
|
|
|
|
|
|
|
|
TQCString display(getenv("DISPLAY"));
|
|
|
|
if (display.isEmpty())
|
|
|
|
{
|
|
|
|
kdWarning(1205) << "$DISPLAY is not set\n";
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// strip the screen number from the display
|
|
|
|
display.tqreplace(TQRegExp("\\.[0-9]+$"), "");
|
|
|
|
|
|
|
|
sock = TQFile::encodeName(locateLocal("socket", TQString("kdesud_%1").arg(static_cast<const char *>(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[])
|
|
|
|
{
|
|
|
|
prctl(PR_SET_DUMPABLE, 0);
|
|
|
|
|
|
|
|
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;
|
|
|
|
TQPtrVector<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 = TQMAX(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";
|
|
|
|
}
|
|
|
|
|