summaryrefslogtreecommitdiffstats
path: root/tdesu/tdesud
diff options
context:
space:
mode:
Diffstat (limited to 'tdesu/tdesud')
-rw-r--r--tdesu/tdesud/CMakeLists.txt35
-rw-r--r--tdesu/tdesud/Makefile.am31
-rw-r--r--tdesu/tdesud/handler.cpp512
-rw-r--r--tdesu/tdesud/handler.h52
-rw-r--r--tdesu/tdesud/lexer.cpp134
-rw-r--r--tdesu/tdesud/lexer.h42
-rw-r--r--tdesu/tdesud/repo.cpp188
-rw-r--r--tdesu/tdesud/repo.h68
-rw-r--r--tdesu/tdesud/secure.cpp80
-rw-r--r--tdesu/tdesud/secure.h52
-rw-r--r--tdesu/tdesud/tdesud.cpp418
11 files changed, 1612 insertions, 0 deletions
diff --git a/tdesu/tdesud/CMakeLists.txt b/tdesu/tdesud/CMakeLists.txt
new file mode 100644
index 000000000..2696a8197
--- /dev/null
+++ b/tdesu/tdesud/CMakeLists.txt
@@ -0,0 +1,35 @@
+#################################################
+#
+# (C) 2010-2011 Serghei Amelian
+# serghei (DOT) amelian (AT) gmail.com
+#
+# Improvements and feedback are welcome
+#
+# This file is released under GPL >= 2
+#
+#################################################
+
+# FIXME there is used KDE_USE_FPIE
+
+include_directories(
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_BINARY_DIR}
+ ${TDE_INCLUDE_DIR}
+ ${TQT_INCLUDE_DIRS}
+)
+
+link_directories(
+ ${TQT_LIBRARY_DIRS}
+)
+
+
+##### tdesud (executable) #######################
+
+tde_add_executable( tdesud
+ SOURCES tdesud.cpp repo.cpp lexer.cpp handler.cpp secure.cpp
+ LINK tdesu-shared
+ DESTINATION ${BIN_INSTALL_DIR}
+)
+
+install( CODE "execute_process( COMMAND chown root:${nogroup} \$ENV{DESTDIR}${_destination}/tdesud )" )
+install( CODE "execute_process( COMMAND chmod 2755 \$ENV{DESTDIR}${_destination}/tdesud )" )
diff --git a/tdesu/tdesud/Makefile.am b/tdesu/tdesud/Makefile.am
new file mode 100644
index 000000000..140cbce06
--- /dev/null
+++ b/tdesu/tdesud/Makefile.am
@@ -0,0 +1,31 @@
+## Makefile.am for tdesud
+
+INCLUDES = $(all_includes)
+
+KDE_CXXFLAGS = $(KDE_USE_FPIE)
+
+bin_PROGRAMS = tdesud
+tdesud_SOURCES = tdesud.cpp repo.cpp lexer.cpp handler.cpp secure.cpp
+tdesud_LDFLAGS = $(KDE_USE_PIE) $(all_libraries) $(KDE_RPATH)
+tdesud_LDADD = $(LIB_KDECORE) -ltdesu $(LIBSOCKET)
+noinst_HEADERS = repo.h handler.h lexer.h secure.h
+
+## tdesud needs to be suid or sgid something
+install-data-local:
+ @echo "********************************************************"
+ @echo ""
+ @echo "For security reasons, tdesud is installed setgid nogroup."
+ @echo "Kdesud is the daemon that implements the password caching."
+ @echo ""
+ @echo "You should NOT use the password caching feature if tdesud is"
+ @echo "not installed setgid nogroup."
+ @echo ""
+ @echo "********************************************************"
+
+install-exec-hook:
+ @(chown root:@nogroup@ $(DESTDIR)$(bindir)/tdesud && chmod 2755 $(DESTDIR)$(bindir)/tdesud) \
+ || echo "Error: Could not install tdesud as setgid nogroup!!\n" \
+ "The password caching feature is disabled."
+
+messages:
+ $(XGETTEXT) $(tdesud_SOURCES) -o $(podir)/tdesud.pot
diff --git a/tdesu/tdesud/handler.cpp b/tdesu/tdesud/handler.cpp
new file mode 100644
index 000000000..2a744b217
--- /dev/null
+++ b/tdesu/tdesud/handler.cpp
@@ -0,0 +1,512 @@
+/*
+ * This file is part of the KDE project, module tdesu.
+ * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
+ *
+ * handler.cpp: A connection handler for tdesud.
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <tqcstring.h>
+
+#include <kdebug.h>
+#include <tdesu/su.h>
+#include <tdesu/ssh.h>
+
+#include "handler.h"
+#include "repo.h"
+#include "lexer.h"
+#include "secure.h"
+
+
+// Global repository
+extern Repository *repo;
+void tdesud_cleanup();
+
+ConnectionHandler::ConnectionHandler(int fd)
+ : SocketSecurity(fd), m_exitCode(0), m_hasExitCode(false), m_needExitCode(false), m_pid(0)
+{
+ m_Fd = fd;
+ m_Priority = 50;
+ m_Scheduler = SuProcess::SchedNormal;
+}
+
+ConnectionHandler::~ConnectionHandler()
+{
+ m_Buf.fill('x');
+ m_Pass.fill('x');
+ close(m_Fd);
+}
+
+/*
+ * Handle a connection: make sure we don't block
+ */
+
+int ConnectionHandler::handle()
+{
+ int ret, nbytes;
+
+ // Add max 100 bytes to connection buffer
+
+ char tmpbuf[100];
+ nbytes = recv(m_Fd, tmpbuf, 99, 0);
+
+ if (nbytes < 0)
+ {
+ if (errno == EINTR)
+ return 0;
+ // read error
+ return -1;
+ } else if (nbytes == 0)
+ {
+ // eof
+ return -1;
+ }
+ tmpbuf[nbytes] = '\000';
+
+ if (m_Buf.length()+nbytes > 1024)
+ {
+ kdWarning(1205) << "line too long";
+ return -1;
+ }
+
+ m_Buf.append(tmpbuf);
+ memset(tmpbuf, 'x', nbytes);
+
+ // Do we have a complete command yet?
+ int n;
+ TQCString newbuf;
+ while ((n = m_Buf.find('\n')) != -1)
+ {
+ newbuf = m_Buf.left(n+1);
+ m_Buf.fill('x', n+1);
+ m_Buf.remove(0, n+1);
+ ret = doCommand(newbuf);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+TQCString ConnectionHandler::makeKey(int _namespace, TQCString s1,
+ TQCString s2, TQCString s3)
+{
+ TQCString res;
+ res.setNum(_namespace);
+ res += "*";
+ res += s1 + "*" + s2 + "*" + s3;
+ return res;
+}
+
+void ConnectionHandler::sendExitCode()
+{
+ if (!m_needExitCode)
+ return;
+ TQCString buf;
+ buf.setNum(m_exitCode);
+ buf.prepend("OK ");
+ buf.append("\n");
+
+ send(m_Fd, buf.data(), buf.length(), 0);
+}
+
+void ConnectionHandler::respond(int ok, TQCString s)
+{
+ TQCString buf;
+
+ switch (ok) {
+ case Res_OK:
+ buf = "OK";
+ break;
+ case Res_NO:
+ default:
+ buf = "NO";
+ break;
+ }
+
+ if (!s.isEmpty())
+ {
+ buf += ' ';
+ buf += s;
+ }
+
+ buf += '\n';
+
+ send(m_Fd, buf.data(), buf.length(), 0);
+}
+
+/*
+ * Parse and do one command. On a parse error, return -1. This will
+ * close the socket in the main accept loop.
+ */
+
+int ConnectionHandler::doCommand(TQCString buf)
+{
+ if ((uid_t) peerUid() != getuid())
+ {
+ kdWarning(1205) << "Peer uid not equal to me\n";
+ kdWarning(1205) << "Peer: " << peerUid() << " Me: " << getuid() << endl;
+ return -1;
+ }
+
+ TQCString key, command, pass, name, user, value, env_check;
+ Data_entry data;
+
+ Lexer *l = new Lexer(buf);
+ int tok = l->lex();
+ switch (tok)
+ {
+ case Lexer::Tok_pass: // "PASS password:string timeout:int\n"
+ tok = l->lex();
+ if (tok != Lexer::Tok_str)
+ goto parse_error;
+ m_Pass.fill('x');
+ m_Pass = l->lval();
+ tok = l->lex();
+ if (tok != Lexer::Tok_num)
+ goto parse_error;
+ m_Timeout = l->lval().toInt();
+ if (l->lex() != '\n')
+ goto parse_error;
+ if (m_Pass.isNull())
+ m_Pass = "";
+ kdDebug(1205) << "Password set!\n";
+ respond(Res_OK);
+ break;
+
+ case Lexer::Tok_host: // "HOST host:string\n"
+ tok = l->lex();
+ if (tok != Lexer::Tok_str)
+ goto parse_error;
+ m_Host = l->lval();
+ if (l->lex() != '\n')
+ goto parse_error;
+ kdDebug(1205) << "Host set to " << m_Host << endl;
+ respond(Res_OK);
+ break;
+
+ case Lexer::Tok_prio: // "PRIO priority:int\n"
+ tok = l->lex();
+ if (tok != Lexer::Tok_num)
+ goto parse_error;
+ m_Priority = l->lval().toInt();
+ if (l->lex() != '\n')
+ goto parse_error;
+ kdDebug(1205) << "priority set to " << m_Priority << endl;
+ respond(Res_OK);
+ break;
+
+ case Lexer::Tok_sched: // "SCHD scheduler:int\n"
+ tok = l->lex();
+ if (tok != Lexer::Tok_num)
+ goto parse_error;
+ m_Scheduler = l->lval().toInt();
+ if (l->lex() != '\n')
+ goto parse_error;
+ kdDebug(1205) << "Scheduler set to " << m_Scheduler << endl;
+ respond(Res_OK);
+ break;
+
+ case Lexer::Tok_exec: // "EXEC command:string user:string [options:string (env:string)*]\n"
+ {
+ TQCString options;
+ QCStringList env;
+ tok = l->lex();
+ if (tok != Lexer::Tok_str)
+ goto parse_error;
+ command = l->lval();
+ tok = l->lex();
+ if (tok != Lexer::Tok_str)
+ goto parse_error;
+ user = l->lval();
+ tok = l->lex();
+ if (tok != '\n')
+ {
+ if (tok != Lexer::Tok_str)
+ goto parse_error;
+ options = l->lval();
+ tok = l->lex();
+ while (tok != '\n')
+ {
+ if (tok != Lexer::Tok_str)
+ goto parse_error;
+ TQCString env_str = l->lval();
+ env.append(env_str);
+ if (strncmp(env_str, "DESKTOP_STARTUP_ID=", strlen("DESKTOP_STARTUP_ID=")) != 0)
+ env_check += "*"+env_str;
+ tok = l->lex();
+ }
+ }
+
+ TQCString auth_user;
+ if ((m_Scheduler != SuProcess::SchedNormal) || (m_Priority > 50))
+ auth_user = "root";
+ else
+ auth_user = user;
+ key = makeKey(2, m_Host, auth_user, command);
+ // We only use the command if the environment is the same.
+ if (repo->find(key) == env_check)
+ {
+ key = makeKey(0, m_Host, auth_user, command);
+ pass = repo->find(key);
+ }
+ if (pass.isNull()) // isNull() means no password, isEmpty() can mean empty password
+ {
+ if (m_Pass.isNull())
+ {
+ respond(Res_NO);
+ break;
+ }
+ data.value = env_check;
+ data.timeout = m_Timeout;
+ key = makeKey(2, m_Host, auth_user, command);
+ repo->add(key, data);
+ data.value = m_Pass;
+ data.timeout = m_Timeout;
+ key = makeKey(0, m_Host, auth_user, command);
+ repo->add(key, data);
+ pass = m_Pass;
+ }
+
+ // Execute the command asynchronously
+ kdDebug(1205) << "Executing command: " << command << endl;
+ pid_t pid = fork();
+ if (pid < 0)
+ {
+ kdDebug(1205) << "fork(): " << strerror(errno) << endl;
+ respond(Res_NO);
+ break;
+ } else if (pid > 0)
+ {
+ m_pid = pid;
+ respond(Res_OK);
+ break;
+ }
+
+ // Ignore SIGCHLD because "class SuProcess" needs waitpid()
+ signal(SIGCHLD, SIG_DFL);
+
+ int ret;
+ if (m_Host.isEmpty())
+ {
+ SuProcess proc;
+ proc.setCommand(command);
+ proc.setUser(user);
+ if (options.contains('x'))
+ proc.setXOnly(true);
+ if (options.contains('f'))
+ proc.setDCOPForwarding(true);
+ proc.setPriority(m_Priority);
+ proc.setScheduler(m_Scheduler);
+ proc.setEnvironment(env);
+ ret = proc.exec(pass.data());
+ } else
+ {
+ SshProcess proc;
+ proc.setCommand(command);
+ proc.setUser(user);
+ proc.setHost(m_Host);
+ ret = proc.exec(pass.data());
+ }
+
+ kdDebug(1205) << "Command completed: " << command << endl;
+ _exit(ret);
+ }
+
+ case Lexer::Tok_delCmd: // "DEL command:string user:string\n"
+ tok = l->lex();
+ if (tok != Lexer::Tok_str)
+ goto parse_error;
+ command = l->lval();
+ tok = l->lex();
+ if (tok != Lexer::Tok_str)
+ goto parse_error;
+ user = l->lval();
+ if (l->lex() != '\n')
+ goto parse_error;
+ key = makeKey(0, m_Host, user, command);
+ if (repo->remove(key) < 0) {
+ kdDebug(1205) << "Unknown command: " << command << endl;
+ respond(Res_NO);
+ }
+ else {
+ kdDebug(1205) << "Deleted command: " << command << ", user = "
+ << user << endl;
+ respond(Res_OK);
+ }
+ break;
+
+ case Lexer::Tok_delVar: // "DELV name:string \n"
+ {
+ tok = l->lex();
+ if (tok != Lexer::Tok_str)
+ goto parse_error;
+ name = l->lval();
+ tok = l->lex();
+ if (tok != '\n')
+ goto parse_error;
+ key = makeKey(1, name);
+ if (repo->remove(key) < 0)
+ {
+ kdDebug(1205) << "Unknown name: " << name << endl;
+ respond(Res_NO);
+ }
+ else {
+ kdDebug(1205) << "Deleted name: " << name << endl;
+ respond(Res_OK);
+ }
+ break;
+ }
+
+ case Lexer::Tok_delGroup: // "DELG group:string\n"
+ tok = l->lex();
+ if (tok != Lexer::Tok_str)
+ goto parse_error;
+ name = l->lval();
+ if (repo->removeGroup(name) < 0)
+ {
+ kdDebug(1205) << "No keys found under group: " << name << endl;
+ respond(Res_NO);
+ }
+ else
+ {
+ kdDebug(1205) << "Removed all keys under group: " << name << endl;
+ respond(Res_OK);
+ }
+ break;
+
+ case Lexer::Tok_delSpecialKey: // "DELS special_key:string\n"
+ tok = l->lex();
+ if (tok != Lexer::Tok_str)
+ goto parse_error;
+ name = l->lval();
+ if (repo->removeSpecialKey(name) < 0)
+ respond(Res_NO);
+ else
+ respond(Res_OK);
+ break;
+
+ case Lexer::Tok_set: // "SET name:string value:string group:string timeout:int\n"
+ tok = l->lex();
+ if (tok != Lexer::Tok_str)
+ goto parse_error;
+ name = l->lval();
+ tok = l->lex();
+ if (tok != Lexer::Tok_str)
+ goto parse_error;
+ data.value = l->lval();
+ tok = l->lex();
+ if (tok != Lexer::Tok_str)
+ goto parse_error;
+ data.group = l->lval();
+ tok = l->lex();
+ if (tok != Lexer::Tok_num)
+ goto parse_error;
+ data.timeout = l->lval().toInt();
+ if (l->lex() != '\n')
+ goto parse_error;
+ key = makeKey(1, name);
+ repo->add(key, data);
+ kdDebug(1205) << "Stored key: " << key << endl;
+ respond(Res_OK);
+ break;
+
+ case Lexer::Tok_get: // "GET name:string\n"
+ tok = l->lex();
+ if (tok != Lexer::Tok_str)
+ goto parse_error;
+ name = l->lval();
+ if (l->lex() != '\n')
+ goto parse_error;
+ key = makeKey(1, name);
+ kdDebug(1205) << "Request for key: " << key << endl;
+ value = repo->find(key);
+ if (!value.isEmpty())
+ respond(Res_OK, value);
+ else
+ respond(Res_NO);
+ break;
+
+ case Lexer::Tok_getKeys: // "GETK groupname:string\n"
+ tok = l->lex();
+ if (tok != Lexer::Tok_str)
+ goto parse_error;
+ name = l->lval();
+ if (l->lex() != '\n')
+ goto parse_error;
+ kdDebug(1205) << "Request for group key: " << name << endl;
+ value = repo->findKeys(name);
+ if (!value.isEmpty())
+ respond(Res_OK, value);
+ else
+ respond(Res_NO);
+ break;
+
+ case Lexer::Tok_chkGroup: // "CHKG groupname:string\n"
+ tok = l->lex();
+ if (tok != Lexer::Tok_str)
+ goto parse_error;
+ name = l->lval();
+ if (l->lex() != '\n')
+ goto parse_error;
+ kdDebug(1205) << "Checking for group key: " << name << endl;
+ if ( repo->hasGroup( name ) < 0 )
+ respond(Res_NO);
+ else
+ respond(Res_OK);
+ break;
+
+ case Lexer::Tok_ping: // "PING\n"
+ tok = l->lex();
+ if (tok != '\n')
+ goto parse_error;
+ respond(Res_OK);
+ break;
+
+ case Lexer::Tok_exit: // "EXIT\n"
+ tok = l->lex();
+ if (tok != '\n')
+ goto parse_error;
+ m_needExitCode = true;
+ if (m_hasExitCode)
+ sendExitCode();
+ break;
+
+ case Lexer::Tok_stop: // "STOP\n"
+ tok = l->lex();
+ if (tok != '\n')
+ goto parse_error;
+ kdDebug(1205) << "Stopping by command" << endl;
+ respond(Res_OK);
+ tdesud_cleanup();
+ exit(0);
+
+ default:
+ kdWarning(1205) << "Unknown command: " << l->lval() << endl;
+ respond(Res_NO);
+ goto parse_error;
+ }
+
+ delete l;
+ return 0;
+
+parse_error:
+ kdWarning(1205) << "Parse error" << endl;
+ delete l;
+ return -1;
+}
+
+
+
diff --git a/tdesu/tdesud/handler.h b/tdesu/tdesud/handler.h
new file mode 100644
index 000000000..8728efe78
--- /dev/null
+++ b/tdesu/tdesud/handler.h
@@ -0,0 +1,52 @@
+/* vi: ts=8 sts=4 sw=4
+ *
+ * This file is part of the KDE project, module tdesu.
+ * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
+ */
+
+#ifndef __Handler_h_included__
+#define __Handler_h_included__
+
+#include <sys/types.h>
+
+#include <tqcstring.h>
+#include "secure.h"
+
+/**
+ * A ConnectionHandler handles a client. It is called from the main program
+ * loop whenever there is data to read from a corresponding socket.
+ * It keeps reading data until a newline is read. Then, a command is parsed
+ * and executed.
+ */
+
+class ConnectionHandler: public SocketSecurity
+{
+
+public:
+ ConnectionHandler(int fd);
+ ~ConnectionHandler();
+
+ /** Handle incoming data. */
+ int handle();
+
+ /* Send back exit code. */
+ void sendExitCode();
+
+private:
+ enum Results { Res_OK, Res_NO };
+
+ int doCommand(TQCString buf);
+ void respond(int ok, TQCString s=0);
+ TQCString makeKey(int namspace, TQCString s1, TQCString s2=0, TQCString s3=0);
+
+ int m_Fd, m_Timeout;
+ int m_Priority, m_Scheduler;
+ TQCString m_Buf, m_Pass, m_Host;
+public:
+ int m_exitCode;
+ bool m_hasExitCode;
+ bool m_needExitCode;
+ pid_t m_pid;
+};
+
+#endif
diff --git a/tdesu/tdesud/lexer.cpp b/tdesu/tdesud/lexer.cpp
new file mode 100644
index 000000000..f1932f07d
--- /dev/null
+++ b/tdesu/tdesud/lexer.cpp
@@ -0,0 +1,134 @@
+/* vi: ts=8 sts=4 sw=4
+ *
+ * This file is part of the KDE project, module tdesu.
+ * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
+ *
+ * lexer.cpp: A lexer for the tdesud protocol. See tdesud.cpp for a
+ * description of the protocol.
+ */
+
+#include <ctype.h>
+#include <tqcstring.h>
+#include "lexer.h"
+
+
+Lexer::Lexer(const TQCString &input)
+{
+ m_Input = input;
+ in = 0;
+}
+
+Lexer::~Lexer()
+{
+ // Erase buffers
+ m_Input.fill('x');
+ m_Output.fill('x');
+}
+
+TQCString &Lexer::lval()
+{
+ return m_Output;
+}
+
+/*
+ * lex() is the lexer. There is no end-of-input check here so that has to be
+ * done by the caller.
+ */
+
+int Lexer::lex()
+{
+ char c;
+
+ c = m_Input[in++];
+ m_Output.fill('x');
+ m_Output.resize(0);
+
+ while (1)
+ {
+ // newline?
+ if (c == '\n')
+ return '\n';
+
+ // No control characters
+ if (iscntrl(c))
+ return Tok_none;
+
+ if (isspace(c))
+ while (isspace(c = m_Input[in++]));
+
+ // number?
+ if (isdigit(c))
+ {
+ m_Output += c;
+ while (isdigit(c = m_Input[in++]))
+ m_Output += c;
+ in--;
+ return Tok_num;
+ }
+
+ // quoted string?
+ if (c == '"')
+ {
+ c = m_Input[in++];
+ while ((c != '"') && !iscntrl(c)) {
+ // handle escaped characters
+ if (c == '\\')
+ m_Output += m_Input[in++];
+ else
+ m_Output += c;
+ c = m_Input[in++];
+ }
+ if (c == '"')
+ return Tok_str;
+ return Tok_none;
+ }
+
+ // normal string
+ while (!isspace(c) && !iscntrl(c))
+ {
+ m_Output += c;
+ c = m_Input[in++];
+ }
+ in--;
+
+ // command?
+ if (m_Output.length() <= 4)
+ {
+ if (m_Output == "EXEC")
+ return Tok_exec;
+ if (m_Output == "PASS")
+ return Tok_pass;
+ if (m_Output == "DEL")
+ return Tok_delCmd;
+ if (m_Output == "PING")
+ return Tok_ping;
+ if (m_Output == "EXIT")
+ return Tok_exit;
+ if (m_Output == "STOP")
+ return Tok_stop;
+ if (m_Output == "SET")
+ return Tok_set;
+ if (m_Output == "GET")
+ return Tok_get;
+ if (m_Output == "HOST")
+ return Tok_host;
+ if (m_Output == "SCHD")
+ return Tok_sched;
+ if (m_Output == "PRIO")
+ return Tok_prio;
+ if (m_Output == "DELV")
+ return Tok_delVar;
+ if (m_Output == "DELG")
+ return Tok_delGroup;
+ if (m_Output == "DELS")
+ return Tok_delSpecialKey;
+ if (m_Output == "GETK")
+ return Tok_getKeys;
+ if (m_Output == "CHKG")
+ return Tok_chkGroup;
+ }
+
+ return Tok_str;
+ }
+}
+
diff --git a/tdesu/tdesud/lexer.h b/tdesu/tdesud/lexer.h
new file mode 100644
index 000000000..d8f529cd8
--- /dev/null
+++ b/tdesu/tdesud/lexer.h
@@ -0,0 +1,42 @@
+/* vi: ts=8 sts=4 sw=4
+ *
+ * This file is part of the KDE project, module tdesu.
+ * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
+ */
+
+#ifndef __Lexer_h_included__
+#define __Lexer_h_included__
+
+class TQCString;
+
+/**
+ * This is a lexer for the tdesud protocol.
+ */
+
+class Lexer {
+public:
+ Lexer(const TQCString &input);
+ ~Lexer();
+
+ /** Read next token. */
+ int lex();
+
+ /** Return the token's value. */
+ TQCString &lval();
+
+ enum Tokens {
+ Tok_none, Tok_exec=256, Tok_pass, Tok_delCmd,
+ Tok_ping, Tok_str, Tok_num , Tok_stop,
+ Tok_set, Tok_get, Tok_delVar, Tok_delGroup,
+ Tok_host, Tok_prio, Tok_sched, Tok_getKeys,
+ Tok_chkGroup, Tok_delSpecialKey, Tok_exit
+ };
+
+private:
+ TQCString m_Input;
+ TQCString m_Output;
+
+ int in;
+};
+
+#endif
diff --git a/tdesu/tdesud/repo.cpp b/tdesu/tdesud/repo.cpp
new file mode 100644
index 000000000..e923d6411
--- /dev/null
+++ b/tdesu/tdesud/repo.cpp
@@ -0,0 +1,188 @@
+/* vi: ts=8 sts=4 sw=4
+ *
+ * This file is part of the KDE project, module tdesu.
+ * Copyright (C) 1999,2000 Geert Jansen <g.t.jansen@stud.tue.nl>
+ */
+
+#include <time.h>
+#include <assert.h>
+
+#include <tqcstring.h>
+#include <tqmap.h>
+#include <tqvaluestack.h>
+#include <kdebug.h>
+
+#include "repo.h"
+
+
+Repository::Repository()
+{
+ head_time = (unsigned) -1;
+}
+
+
+Repository::~Repository()
+{}
+
+
+void Repository::add(const TQCString &key, Data_entry &data)
+{
+ RepoIterator it = repo.find(key);
+ if (it != repo.end())
+ remove(key);
+ if (data.timeout == 0)
+ data.timeout = (unsigned) -1;
+ else
+ data.timeout += time(0L);
+ head_time = QMIN(head_time, data.timeout);
+ repo.insert(key, data);
+}
+
+int Repository::remove(const TQCString &key)
+{
+ if( key.isEmpty() )
+ return -1;
+
+ RepoIterator it = repo.find(key);
+ if (it == repo.end())
+ return -1;
+ it.data().value.fill('x');
+ it.data().group.fill('x');
+ repo.remove(it);
+ return 0;
+}
+
+int Repository::removeSpecialKey(const TQCString &key)
+{
+ int found = -1;
+ if ( !key.isEmpty() )
+ {
+ TQValueStack<TQCString> rm_keys;
+ for (RepoCIterator it=repo.begin(); it!=repo.end(); ++it)
+ {
+ if ( key.find( static_cast<const char *>(it.data().group) ) == 0 &&
+ it.key().find( static_cast<const char *>(key) ) >= 0 )
+ {
+ rm_keys.push(it.key());
+ found = 0;
+ }
+ }
+ while (!rm_keys.isEmpty())
+ {
+ kdDebug(1205) << "Removed key: " << rm_keys.top() << endl;
+ remove(rm_keys.pop());
+ }
+ }
+ return found;
+}
+
+int Repository::removeGroup(const TQCString &group)
+{
+ int found = -1;
+ if ( !group.isEmpty() )
+ {
+ TQValueStack<TQCString> rm_keys;
+ for (RepoCIterator it=repo.begin(); it!=repo.end(); ++it)
+ {
+ if (it.data().group == group)
+ {
+ rm_keys.push(it.key());
+ found = 0;
+ }
+ }
+ while (!rm_keys.isEmpty())
+ {
+ kdDebug(1205) << "Removed key: " << rm_keys.top() << endl;
+ remove(rm_keys.pop());
+ }
+ }
+ return found;
+}
+
+int Repository::hasGroup(const TQCString &group) const
+{
+ if ( !group.isEmpty() )
+ {
+ RepoCIterator it;
+ for (it=repo.begin(); it!=repo.end(); ++it)
+ {
+ if (it.data().group == group)
+ return 0;
+ }
+ }
+ return -1;
+}
+
+TQCString Repository::findKeys(const TQCString &group, const char *sep ) const
+{
+ TQCString list="";
+ if( !group.isEmpty() )
+ {
+ kdDebug(1205) << "Looking for matching key with group key: " << group << endl;
+ int pos;
+ TQCString key;
+ RepoCIterator it;
+ for (it=repo.begin(); it!=repo.end(); ++it)
+ {
+ if (it.data().group == group)
+ {
+ key = it.key().copy();
+ kdDebug(1205) << "Matching key found: " << key << endl;
+ pos = key.findRev(sep);
+ key.truncate( pos );
+ key.remove(0, 2);
+ if (!list.isEmpty())
+ {
+ // Add the same keys only once please :)
+ if( !list.contains(static_cast<const char *>(key)) )
+ {
+ kdDebug(1205) << "Key added to list: " << key << endl;
+ list += '\007'; // I do not know
+ list.append(key);
+ }
+ }
+ else
+ list = key;
+ }
+ }
+ }
+ return list;
+}
+
+TQCString Repository::find(const TQCString &key) const
+{
+ if( key.isEmpty() )
+ return 0;
+
+ RepoCIterator it = repo.find(key);
+ if (it == repo.end())
+ return 0;
+ return it.data().value;
+}
+
+
+int Repository::expire()
+{
+ unsigned current = time(0L);
+ if (current < head_time)
+ return 0;
+
+ unsigned t;
+ TQValueStack<TQCString> keys;
+ head_time = (unsigned) -1;
+ RepoIterator it;
+ for (it=repo.begin(); it!=repo.end(); ++it)
+ {
+ t = it.data().timeout;
+ if (t <= current)
+ keys.push(it.key());
+ else
+ head_time = QMIN(head_time, t);
+ }
+
+ int n = keys.count();
+ while (!keys.isEmpty())
+ remove(keys.pop());
+ return n;
+}
+
diff --git a/tdesu/tdesud/repo.h b/tdesu/tdesud/repo.h
new file mode 100644
index 000000000..70027793f
--- /dev/null
+++ b/tdesu/tdesud/repo.h
@@ -0,0 +1,68 @@
+/* vi: ts=8 sts=4 sw=4
+ *
+ * This file is part of the KDE project, module tdesu.
+ * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
+ */
+
+#ifndef __Repo_h_included__
+#define __Repo_h_included__
+
+
+#include <tqmap.h>
+#include <tqcstring.h>
+
+
+/**
+ * Used internally.
+ */
+struct Data_entry
+{
+ TQCString value;
+ TQCString group;
+ unsigned int timeout;
+};
+
+
+/**
+ * String repository.
+ *
+ * This class implements a string repository with expiration.
+ */
+class Repository {
+public:
+ Repository();
+ ~Repository();
+
+ /** Remove data elements which are expired. */
+ int expire();
+
+ /** Add a data element */
+ void add(const TQCString& key, Data_entry& data);
+
+ /** Delete a data element. */
+ int remove(const TQCString& key);
+
+ /** Delete all data entries having the given group. */
+ int removeGroup(const TQCString& group);
+
+ /** Delete all data entries based on key. */
+ int removeSpecialKey(const TQCString& key );
+
+ /** Checks for the existence of the specified group. */
+ int hasGroup(const TQCString &group) const;
+
+ /** Return a data value. */
+ TQCString find(const TQCString& key) const;
+
+ /** Returns the key values for the given group. */
+ TQCString findKeys(const TQCString& group, const char *sep= "-") const;
+
+private:
+
+ TQMap<TQCString,Data_entry> repo;
+ typedef TQMap<TQCString,Data_entry>::Iterator RepoIterator;
+ typedef TQMap<TQCString,Data_entry>::ConstIterator RepoCIterator;
+ unsigned head_time;
+};
+
+#endif
diff --git a/tdesu/tdesud/secure.cpp b/tdesu/tdesud/secure.cpp
new file mode 100644
index 000000000..9b30ab4ee
--- /dev/null
+++ b/tdesu/tdesud/secure.cpp
@@ -0,0 +1,80 @@
+/* vi: ts=8 sts=4 sw=4
+ *
+ * This file is part of the KDE project, module tdesu.
+ * Copyright (C) 1999,2000 Geert Jansen <g.t.jansen@stud.tue.nl>
+ *
+ * secure.cpp: Peer credentials for a UNIX socket.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+
+#include <kdebug.h>
+#include <ksockaddr.h>
+#include "secure.h"
+
+
+/**
+ * Under Linux, Socket_security is supported.
+ */
+
+#if defined(SO_PEERCRED)
+
+SocketSecurity::SocketSecurity(int sockfd)
+{
+ ksocklen_t len = sizeof(struct ucred);
+ if (getsockopt(sockfd, SOL_SOCKET, SO_PEERCRED, &cred, &len) < 0) {
+ kdError() << "getsockopt(SO_PEERCRED) " << perror << endl;
+ return;
+ }
+
+ ok = true;
+}
+
+#else
+# if defined(HAVE_GETPEEREID)
+SocketSecurity::SocketSecurity(int sockfd)
+{
+ uid_t euid;
+ gid_t egid;
+ if (getpeereid(sockfd, &euid, &egid) == 0) {
+ cred.uid = euid;
+ cred.gid = egid;
+ cred.pid = -1;
+ ok = true;
+ }
+}
+
+# else
+
+
+/**
+ * The default version does nothing.
+ */
+
+SocketSecurity::SocketSecurity(int sockfd)
+{
+ static bool warned_him = FALSE;
+
+ if (!warned_him) {
+ kdWarning() << "Using void socket security. Please add support for your" << endl;
+ kdWarning() << "platform to tdesu/tdesud/secure.cpp" << endl;
+ warned_him = TRUE;
+ }
+
+ // This passes the test made in handler.cpp
+ cred.uid = getuid();
+ ok = true;
+}
+
+# endif
+#endif
diff --git a/tdesu/tdesud/secure.h b/tdesu/tdesud/secure.h
new file mode 100644
index 000000000..edf58d9c6
--- /dev/null
+++ b/tdesu/tdesud/secure.h
@@ -0,0 +1,52 @@
+/* vi: ts=8 sts=4 sw=4
+ *
+ * This file is part of the KDE project, module tdesu.
+ * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
+ */
+
+#ifndef __Secure_h_included__
+#define __Secure_h_included__
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#ifndef HAVE_STRUCT_UCRED
+
+// `struct ucred' is not defined in glibc 2.0.
+
+struct ucred {
+ pid_t pid;
+ uid_t uid;
+ gid_t gid;
+};
+
+#endif // HAVE_STRUCT_UCRED
+
+
+/**
+ * The Socket_security class autheticates the peer for you. It provides
+ * the process-id, user-id and group-id plus the MD5 sum of the connected
+ * binary.
+ */
+
+class SocketSecurity {
+public:
+ SocketSecurity(int fd);
+
+ /** Returns the peer's process-id. */
+ int peerPid() { if (!ok) return -1; return cred.pid; }
+
+ /** Returns the peer's user-id */
+ int peerUid() { if (!ok) return -1; return cred.uid; }
+
+ /** Returns the peer's group-id */
+ int peerGid() { if (!ok) return -1; return cred.gid; }
+
+private:
+ bool ok;
+ struct ucred cred;
+};
+
+#endif
diff --git a/tdesu/tdesud/tdesud.cpp b/tdesu/tdesud/tdesud.cpp
new file mode 100644
index 000000000..d369aaf9b
--- /dev/null
+++ b/tdesu/tdesud/tdesud.cpp
@@ -0,0 +1,418 @@
+/* vi: ts=8 sts=4 sw=4
+ *
+ * This file is part of the KDE project, module tdesu.
+ * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
+ *
+ *
+ * tdesud.cpp: KDE su daemon. Offers "keep password" functionality to kde su.
+ *
+ * The socket $KDEHOME/socket-$(HOSTNAME)/tdesud_$(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 <tdesu/client.h>
+#include <tdesu/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 tdesud_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";
+ tdesud_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";
+ tdesud_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.replace(TQRegExp("\\.[0-9]+$"), "");
+
+ sock = TQFile::encodeName(locateLocal("socket", TQString("tdesud_%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) << "tdesud 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("tdesud", I18N_NOOP("KDE su daemon"),
+ Version, I18N_NOOP("Daemon used by tdesu"),
+ 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";
+ tdesud_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";
+ tdesud_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";
+}
+