summaryrefslogtreecommitdiffstats
path: root/tdesu
diff options
context:
space:
mode:
Diffstat (limited to 'tdesu')
-rw-r--r--tdesu/AUTHORS2
-rw-r--r--tdesu/CMakeLists.txt53
-rw-r--r--tdesu/ChangeLog128
-rw-r--r--tdesu/FAQ46
-rw-r--r--tdesu/LICENSE.readme125
-rw-r--r--tdesu/Makefile.am1
-rw-r--r--tdesu/README34
-rw-r--r--tdesu/TODO3
-rw-r--r--tdesu/configure.in.in62
-rw-r--r--tdesu/tdesu/CMakeLists.txt30
-rw-r--r--tdesu/tdesu/Makefile.am15
-rw-r--r--tdesu/tdesu/sudlg.cpp103
-rw-r--r--tdesu/tdesu/sudlg.h32
-rw-r--r--tdesu/tdesu/tdesu.cpp431
-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
25 files changed, 2677 insertions, 0 deletions
diff --git a/tdesu/AUTHORS b/tdesu/AUTHORS
new file mode 100644
index 000000000..5cabd8892
--- /dev/null
+++ b/tdesu/AUTHORS
@@ -0,0 +1,2 @@
+Pietro Iglio <iglio@fub.it>
+Geert Jansen <g.t.jansen@stud.tue.nl>
diff --git a/tdesu/CMakeLists.txt b/tdesu/CMakeLists.txt
new file mode 100644
index 000000000..c1fef3ba8
--- /dev/null
+++ b/tdesu/CMakeLists.txt
@@ -0,0 +1,53 @@
+#################################################
+#
+# (C) 2010-2011 Serghei Amelian
+# serghei (DOT) amelian (AT) gmail.com
+#
+# Improvements and feedback are welcome
+#
+# This file is released under GPL >= 2
+#
+#################################################
+
+include( CheckCSourceCompiles )
+include( CheckFunctionExists )
+
+check_c_source_compiles( "
+ #define _GNU_SOURCE 1
+ #include <sys/socket.h>
+ int main(int argc, char *argv[]) { struct ucred red; return 0; } "
+ HAVE_STRUCT_UCRED )
+
+check_c_source_runs( "
+ #include <grp.h>
+ #include <sys/types.h>
+ int main() { struct group *grp = getgrnam(\"nogroup\"); if(grp) return 0; return 1; }"
+ HAVE_NOGROUP )
+
+if( HAVE_NOGROUP )
+ set( nogroup nogroup )
+else()
+ check_c_source_runs("
+ #include <grp.h>
+ #include <sys/types.h>
+ int main() { struct group *grp = getgrnam(\"nobody\"); if(grp) return 0; return 1; }"
+ HAVE_NOBODY )
+ if( HAVE_NOBODY )
+ set( nogroup nobody )
+ else()
+ set( nogroup 65534 )
+ endif()
+endif()
+
+check_function_exists( getpeereid HAVE_GETPEEREID )
+check_include_file( "sys/select.h" HAVE_SYS_SELECT_H )
+check_include_file( "sys/wait.h" HAVE_SYS_WAIT_H )
+
+if( WITH_SUDO_KDESU_BACKEND )
+ set( DEFAULT_SUPER_USER_COMMAND sudo CACHE INTERNAL "" FORCE )
+else()
+ set( DEFAULT_SUPER_USER_COMMAND su CACHE INTERNAL "" FORCE )
+endif()
+
+add_subdirectory( tdesu )
+add_subdirectory( tdesud )
diff --git a/tdesu/ChangeLog b/tdesu/ChangeLog
new file mode 100644
index 000000000..ad44b81a5
--- /dev/null
+++ b/tdesu/ChangeLog
@@ -0,0 +1,128 @@
+Wed, 26 Jan 00 Geert Jansen <g.t.jansen@stud.tue.nl>
+
+ * Build sycoca for target process.
+ * Use standard debug facilities.
+ * Use KCmdLineArgs for argument parsing.
+ * Dialog updated to use KDialogBase.
+
+Sat, 18 Dec 99 Geert Jansen <g.t.jansen@stud.tue.nl>
+
+ * Add DCOP authentication cookies.
+ * Fixed SMP install of tdesud.
+
+Sat, 23 Oct 99 Geert Jansen <g.t.jansen@stud.tue.nl>
+
+ * STL -> QTL
+ * Use Qt's standard debug facilities.
+
+Tue, 28 Sep 99 Geert Jansen <g.t.jansen@stud.tue.nl>
+
+ * Integrating tdesu into kdebase
+
+Sat, 25 Sep 99 Geert Jansen <g.t.jansen@stud.tue.nl>
+
+ * BIG change: su to other user than root. This required a different way
+ to pass X cookies and stuff to the target process. See
+ common/tdesu_stub.c for more info.
+ * Fixed a bug in tdesud/lexer.cpp
+
+Thu, 23 Sep 99 Geert Jansen <g.t.jansen@stud.tue.nl>
+
+ * Portability issues (root has a csh on FreeBSD ... is this just me?)
+ * The execution of processes is more efficient now: 2 processess instead
+ of 3 and 1 tempfile instead of 2. This way possible by setting the
+ XAUTHORITY env. var. to $HOME/.Xauthority instead of making a new one.
+ * Fixed a subtle bug in tdesud. tdesud catches SIGCHLD to handle child
+ exits but this is incompatible with "class RootProcess", which does a
+ waitpid() and depends on getting the exit code.
+ * Only connect to daemon when necessary.
+
+Wed, 22 Sep 99 Geert Jansen <g.t.jansen@stud.tue.nl>
+
+ * Changed WaitSlave() to open slave instead of master pty.
+ * Added French translation (Pierre Dorgueil).
+ * Updated German translation (Ludwig Nussel).
+ * If su has terminal output, feed it back to the user.
+
+Wed, 8 Sep 99 Geert Jansen <g.t.jansen@stud.tue.nl>
+
+ * If started as root, tdesu does not ask for a password anymore.
+
+Wed, 1 Sep 99 Geert Jansen <g.t.jansen@stud.tue.nl>
+
+ * Deferred instantiation of kapp in tdesu.cpp.
+ * Finished porting to KDE2.
+
+Sun, 29 Aug 99 Geert Jansen <g.t.jansen@stud.tue.nl>
+
+ * Fixed a bug that would let you run only one program with the daemon.
+ * Adapted debug.h macros.
+
+Tue, 24 Aug 99 Geert Jansen <g.t.jansen@stud.tue.nl>
+
+ * Version 0.97
+ * Fixed the RedHat problem! See the function WaitSlave() in
+ common/process.cpp.
+ * Fixed a few GUI bugs.
+ * Improved password security. Passwords are not kept until
+ the root program exits, they are deleted right away.
+
+Fri, 20 Aug 99 Geert Jansen <g.t.jansen@stud.tue.nl>
+
+ * Fixed the glibc 2.1 compile problem (struct ucred test).
+
+Tue, 17 Aug 99 Geert Jansen <g.t.jansen@stud.tue.nl>
+
+ * Fixed debug.h macros.
+ * Some more source cleanups.
+
+Mon, 16 Aug 99 Geert Jansen <g.t.jansen@stud.tue.nl>
+
+ * Added "nogroup" check.
+ * Updated the HTML documentation.
+ * Updated the FAQ
+
+Sun, 15 Aug 99 Geert Jansen <g.t.jansen@stud.tue.nl>
+
+ * KDE su now supports terminal mode apps! (-t switch, no password
+ keeping, output only)
+
+Sat, 14 Aug 99 Geert Jansen <g.t.jansen@stud.tue.nl>
+
+ * Version 0.94
+ * PTY handling is improved. It should work on more systems now.
+ (tested: Linux w & w/o UNIX98 PTY's w & w/o GLIBC 2.1, Solaris 7)
+ * Changed behaviour of "Keep Password" setting.
+ * Added -n option: don't offer the choice to keep password.
+ * Added -q, -d options: Quit the daemon and delete a key.
+ * Source cleanups.
+ * Various small bugfixes.
+ * Merged most of the KDE2 fixes from the CVS version.
+ * KDE su now waits for child programs to finish. Use 'command &' to
+ exit right away.
+
+Wed, 02 Jun 99 Geert Jansen <g.t.jansen@stud.tue.nl>
+
+ * Version 0.91
+ * The password is passed to su with a pty/tty pair. This should fix the
+ problems redhat users are experiencing.
+ * Some portability issues (a.o. AF_LOCAL -> AF_UNIX)
+
+Thu, 20 May 99 Geert Jansen <g.t.jansen@stud.tue.nl>
+
+ * I am finally able to release 0.9.
+
+Fri, 09 Apr 99 Geert Jansen <g.t.jansen@stud.tue.nl>
+
+ * Backport to KDE 1.1. Release as tdesu-0.9.tar.gz
+
+Mon, 22 Mar 99 Geert Jansen <g.t.jansen@stud.tue.nl>
+
+ * Major changes: password keeping, control module, UI changes
+ * Version 0.9
+
+Thu, 25 Feb 99 Geert Jansen <g.t.jansen@stud.tue.nl>
+
+ * Merge with Pietro Iglio's code.
+ * Version 0.4
+
diff --git a/tdesu/FAQ b/tdesu/FAQ
new file mode 100644
index 000000000..b7d1499c7
--- /dev/null
+++ b/tdesu/FAQ
@@ -0,0 +1,46 @@
+Q: On my SuSE system, KDE su does not compile. I get an error that some Qt
+ header files cannot be found.
+A: Install the package qtcompat.
+
+Q: Is KDE su safe?
+A: No program is 100% safe. However, KDE su is not setuid root and it
+ handles the password you enter with great care so it should be safe
+ enough.
+
+Q: How safe is password keeping?
+A: Enabling password keeping is less secure that disabling it. However, the
+ scheme tdesu uses to keep passwords prevents everyone (including you, the
+ user) from accessing them. Please see the HTML documentation for a full
+ description of this scheme.
+
+Q: Can I execute tty applications with tdesu?
+A: No. TTY application will probably never be supported. Use the Unix su for
+ those.
+ NOTE: As of version 0.94, tty _output_ _only_ is supported with the `-t'
+ switch. This disables password keeping, though.
+
+Q: What systems does KDE su support?
+A: Tested are:
+ * Linux 2.x (Redhat 6.x, Mandrake "Cooker", Debian potato, SuSE 6.1)
+ * Solaris 7 (intel)
+ * FreeBSD 3.2 (intel, w/ egcs 1.1.2)
+ It will probably work on more systems but I cannot test that.
+
+Q: Why doesn't it support every system that is out there.
+A: KDE su needs to setup a pty/tty pair for communicating with `su'. This is
+ because some `su' implementations refuse to read a password from stdin if
+ that is not a tty. Setting up a pty/tty pair is not completely portable.
+
+Q: A good debug tip?
+A: If tdesu doesn't fire up your application, use the '-t' switch.
+ This way, you'll get terminal output. Maybe there is something wrong with
+ the program you're trying to run.
+
+Q: I always get the warning: "Terminal output not available on non-terminal".
+A: Maybe you're not logged on from a terminal but probably you're using
+ UNIX98 pty's without glibc 2.1 (Linux). The glibc 2.0 ttyname() function
+ incorrectly reports that UNIX98 slave pty's are no tty's.
+
+Q: Why not use DCOP for the communications with the daemon?
+A: KDE su needs one instance of the daemon per host, instead of per desktop
+ session.
diff --git a/tdesu/LICENSE.readme b/tdesu/LICENSE.readme
new file mode 100644
index 000000000..234d3c1d8
--- /dev/null
+++ b/tdesu/LICENSE.readme
@@ -0,0 +1,125 @@
+tdesu - a KDE front end to su
+
+Copyright (c) 1998 by Pietro Iglio <iglio@fub.it>
+Copyright (c) 1999,2000 by Geert Jansen <jansen@kde.org>
+
+ The "Artistic License"
+
+ Preamble
+
+ The intent of this document is to state the conditions under which a
+ Package may be copied, such that the Copyright Holder maintains some
+ semblance of artistic control over the development of the package,
+ while giving the users of the package the right to use and
+ distribute the Package in a more-or-less customary fashion, plus the
+ right to make reasonable modifications.
+
+ Definitions:
+
+ * "Package" refers to the collection of files distributed by the
+ Copyright Holder, and derivatives of that collection of files
+ created through textual modification.
+
+ * "Standard Version" refers to such a Package if it has not been
+ modified, or has been modified in accordance with the wishes of
+ the Copyright Holder.
+
+ * "Copyright Holder" is whoever is named in the copyright or
+ copyrights for the package.
+
+ * "You" is you, if you're thinking about copying or distributing
+ this Package.
+
+ * "Reasonable copying fee" is whatever you can justify on the
+ basis of media cost, duplication charges, time of people
+ involved, and so on. (You will not be required to justify it to
+ the Copyright Holder, but only to the computing community at
+ large as a market that must bear the fee.)
+
+ * "Freely Available" means that no fee is charged for the item
+ itself, though there may be fees involved in handling the item.
+ It also means that recipients of the item may redistribute it
+ under the same conditions they received it.
+
+ 1. You may make and give away verbatim copies of the source form of
+ the Standard Version of this Package without restriction, provided
+ that you duplicate all of the original copyright notices and
+ associated disclaimers.
+
+ 2. You may apply bug fixes, portability fixes and other
+ modifications derived from the Public Domain or from the Copyright
+ Holder. A Package modified in such a way shall still be considered
+ the Standard Version.
+
+ 3. You may otherwise modify your copy of this Package in any way,
+ provided that you insert a prominent notice in each changed file
+ stating how and when you changed that file, and provided that you do
+ at least ONE of the following:
+
+ a) place your modifications in the Public Domain or
+ otherwise make them Freely Available, such as by posting
+ said modifications to Usenet or an equivalent medium, or
+ placing the modifications on a major archive site such as
+ ftp.uu.net, or by allowing the Copyright Holder to include
+ your modifications in the Standard Version of the Package.
+
+ b) use the modified Package only within your corporation
+ or organization.
+
+ c) rename any non-standard executables so the names do not
+ conflict with standard executables, which must also be
+ provided, and provide a separate manual page for each
+ non-standard executable that clearly documents how it
+ differs from the Standard Version.
+
+ d) make other distribution arrangements with the Copyright
+ Holder.
+
+ 4. You may distribute the programs of this Package in object code or
+ executable form, provided that you do at least ONE of the following:
+
+ a) distribute a Standard Version of the executables and
+ library files, together with instructions (in the manual
+ page or equivalent) on where to get the Standard Version.
+
+ b) accompany the distribution with the machine-readable
+ source of the Package with your modifications.
+
+ c) accompany any non-standard executables with their
+ corresponding Standard Version executables, giving the
+ non-standard executables non-standard names, and clearly
+ documenting the differences in manual pages (or
+ equivalent), together with instructions on where to get
+ the Standard Version.
+
+ d) make other distribution arrangements with the Copyright
+ Holder.
+
+ 5. You may charge a reasonable copying fee for any distribution of
+ this Package. You may charge any fee you choose for support of this
+ Package. You may not charge a fee for this Package itself. However,
+ you may distribute this Package in aggregate with other (possibly
+ commercial) programs as part of a larger (possibly commercial)
+ software distribution provided that you do not advertise this
+ Package as a product of your own.
+
+ 6. The scripts and library files supplied as input to or produced as
+ output from the programs of this Package do not automatically fall
+ under the copyright of this Package, but belong to whomever
+ generated them, and may be sold commercially, and may be aggregated
+ with this Package.
+
+ 7. C or perl subroutines supplied by you and linked into this
+ Package shall not be considered part of this Package.
+
+ 8. The name of the Copyright Holder may not be used to endorse or
+ promote products derived from this software without specific prior
+ written permission.
+
+ 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+ The End
+
+
diff --git a/tdesu/Makefile.am b/tdesu/Makefile.am
new file mode 100644
index 000000000..a07d47884
--- /dev/null
+++ b/tdesu/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = tdesu tdesud
diff --git a/tdesu/README b/tdesu/README
new file mode 100644
index 000000000..caa2e0d48
--- /dev/null
+++ b/tdesu/README
@@ -0,0 +1,34 @@
+
+ KDESU: A KDE front end for `su'.
+
+What is it?
+
+ KDE su is a graphical front end to the Unix `su' utility. It allows you
+ to run programs as another user by entering their password.
+ It is _not_ a setuid root program, it runs completely unprivileged.
+ The system's program `su' is used for acquiring privileges.
+
+Usage:
+
+ $ tdesu -h
+ $ tdesu -c konsole
+
+Please see the HTML documentation!
+
+Notes and Acknowledgements:
+
+ Credits go to Pietro Iglio. He was the original author of KDE su
+ (until version 0.3). I was writing a similar program when I
+ found out that KDE su already existed. We decided to merge our
+ projects and that I would continue with it.
+
+ If you find any bug of have a suggestion, feel free to contact me
+ at <kde@geeksrus.net>.
+
+
+License:
+
+ KDE su comes under the "Artistic License". See the file LICENSE.readme
+ for the exact terms.
+
+Alan Eldridge 2002/10/12 <kde@geeksrus.net>
diff --git a/tdesu/TODO b/tdesu/TODO
new file mode 100644
index 000000000..1be1aec4b
--- /dev/null
+++ b/tdesu/TODO
@@ -0,0 +1,3 @@
+KDE su TODO list.
+
+* Currently nothing
diff --git a/tdesu/configure.in.in b/tdesu/configure.in.in
new file mode 100644
index 000000000..512825786
--- /dev/null
+++ b/tdesu/configure.in.in
@@ -0,0 +1,62 @@
+dnl Check for "struct ucred"
+AC_MSG_CHECKING("struct ucred")
+AC_TRY_COMPILE(
+[
+ #define _GNU_SOURCE 1
+ #include <sys/socket.h>
+],
+[
+ struct ucred red;
+], have_ucred=yes
+ , have_ucred=no
+)
+if test "$have_ucred" = "yes"; then
+ AC_DEFINE(HAVE_STRUCT_UCRED, 1, [Define if you have the struct ucred])
+ AC_MSG_RESULT(yes)
+else
+ AC_MSG_RESULT(no)
+fi
+
+dnl Check for the group "nogroup" or "nobody" Use -2 otherwise.
+AC_MSG_CHECKING(nogroup)
+AC_TRY_RUN([
+ #include <grp.h>
+ #include <sys/types.h>
+
+ int main()
+ {
+ struct group *grp = getgrnam("nogroup");
+ if (grp) return 0;
+ return 1;
+ }
+], nogroup=nogroup,
+AC_TRY_RUN([
+ #include <grp.h>
+ #include <sys/types.h>
+
+ int main()
+ {
+ struct group *grp = getgrnam("nobody");
+ if (grp) return 0;
+ return 1;
+ }
+], nogroup=nobody,
+nogroup=65534, nogroup=65534), nogroup=65534)
+AC_MSG_RESULT($nogroup)
+AC_SUBST(nogroup)
+AC_CHECK_FUNCS(getpeereid)
+
+AC_ARG_WITH(sudo-tdesu-backend,
+ AC_HELP_STRING([--with-sudo-tdesu-backend],
+ [use sudo as backend for tdesu (default is su)]),
+[
+ if test x$withval = xyes; then
+ use_tdesu_backend="sudo"
+ else
+ use_tdesu_backend="su"
+ fi
+],
+ use_tdesu_backend="su"
+)
+
+AC_DEFINE_UNQUOTED(DEFAULT_SUPER_USER_COMMAND, "$use_tdesu_backend", [Use su or sudo])
diff --git a/tdesu/tdesu/CMakeLists.txt b/tdesu/tdesu/CMakeLists.txt
new file mode 100644
index 000000000..7eb878140
--- /dev/null
+++ b/tdesu/tdesu/CMakeLists.txt
@@ -0,0 +1,30 @@
+#################################################
+#
+# (C) 2010-2011 Serghei Amelian
+# serghei (DOT) amelian (AT) gmail.com
+#
+# Improvements and feedback are welcome
+#
+# This file is released under GPL >= 2
+#
+#################################################
+
+include_directories(
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_BINARY_DIR}
+ ${TDE_INCLUDE_DIR}
+ ${TQT_INCLUDE_DIRS}
+)
+
+link_directories(
+ ${TQT_LIBRARY_DIRS}
+)
+
+
+##### tdesu (executable) ########################
+
+tde_add_executable( tdesu AUTOMOC
+ SOURCES tdesu.cpp sudlg.cpp
+ LINK tdesu-shared kio-shared
+ DESTINATION ${BIN_INSTALL_DIR}
+)
diff --git a/tdesu/tdesu/Makefile.am b/tdesu/tdesu/Makefile.am
new file mode 100644
index 000000000..f322f0a6f
--- /dev/null
+++ b/tdesu/tdesu/Makefile.am
@@ -0,0 +1,15 @@
+## Makefile.am for tdesu by Geert Jansen
+
+INCLUDES= $(all_includes)
+
+## tdesu
+bin_PROGRAMS = tdesu
+tdesu_SOURCES = tdesu.cpp sudlg.cpp
+tdesu_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_QT) -lDCOP $(LIB_KDECORE) $(LIB_KDEUI) -ltdefx $(LIB_KIO) -lktexteditor
+tdesu_LDADD = $(LIB_KIO) -ltdesu
+tdesu_METASOURCES = AUTO
+noinst_HEADERS = sudlg.h
+
+## Messages
+messages:
+ $(XGETTEXT) $(tdesu_SOURCES) -o $(podir)/tdesu.pot
diff --git a/tdesu/tdesu/sudlg.cpp b/tdesu/tdesu/sudlg.cpp
new file mode 100644
index 000000000..cfda9168c
--- /dev/null
+++ b/tdesu/tdesu/sudlg.cpp
@@ -0,0 +1,103 @@
+/* vi: ts=8 sts=4 sw=4
+ *
+ * This file is part of the KDE project, module tdesu.
+ * Copyright (C) 2000 Geert Jansen <jansen@kde.org>
+ */
+
+#include <config.h>
+#include <tqstring.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+
+#include <tdesu/su.h>
+#include "sudlg.h"
+
+KDEsuDialog::KDEsuDialog(TQCString user, TQCString auth_user, bool enableKeep,const TQString& icon, bool withIgnoreButton)
+ : KPasswordDialog(Password, enableKeep, (withIgnoreButton ? User1:NoDefault), icon)
+{
+ KConfig* config = KGlobal::config();
+ config->setGroup("super-user-command");
+ TQString superUserCommand = config->readEntry("super-user-command", DEFAULT_SUPER_USER_COMMAND);
+ if ( superUserCommand != "sudo" && superUserCommand != "su" ) {
+ kdWarning() << "unknown super user command" << endl;
+ superUserCommand = "su";
+ }
+
+ m_User = auth_user;
+ setCaption(i18n("Run as %1").arg(static_cast<const char *>(user)));
+
+ TQString prompt;
+ if (superUserCommand == "sudo" && m_User == "root") {
+ prompt = i18n("Please enter your password." );
+ } else {
+ if (m_User == "root") {
+ prompt = i18n("The action you requested needs root privileges. "
+ "Please enter root's password below or click "
+ "Ignore to continue with your current privileges.");
+ } else {
+ prompt = i18n("The action you requested needs additional privileges. "
+ "Please enter the password for \"%1\" below or click "
+ "Ignore to continue with your current privileges.").arg(static_cast<const char *>(m_User));
+ }
+ }
+ setPrompt(prompt);
+
+ if( withIgnoreButton )
+ setButtonText(User1, i18n("&Ignore"));
+}
+
+
+KDEsuDialog::~KDEsuDialog()
+{
+}
+
+bool KDEsuDialog::checkPassword(const char *password)
+{
+ SuProcess proc;
+ proc.setUser(m_User);
+ int status = proc.checkInstall(password);
+ switch (status)
+ {
+ case -1:
+ KMessageBox::sorry(this, i18n("Conversation with su failed."));
+ done(Rejected);
+ return false;
+
+ case 0:
+ return true;
+
+ case SuProcess::SuNotFound:
+ KMessageBox::sorry(this,
+ i18n("The program 'su' is not found;\n"
+ "make sure your PATH is set correctly."));
+ done(Rejected);
+ return false;
+
+ case SuProcess::SuNotAllowed:
+ KMessageBox::sorry(this,
+ i18n("You are not allowed to use 'su';\n"
+ "on some systems, you need to be in a special "
+ "group (often: wheel) to use this program."));
+ done(Rejected);
+ return false;
+
+ case SuProcess::SuIncorrectPassword:
+ KMessageBox::sorry(this, i18n("Incorrect password; please try again."));
+ return false;
+
+ default:
+ KMessageBox::error(this, i18n("Internal error: illegal return from "
+ "SuProcess::checkInstall()"));
+ done(Rejected);
+ return false;
+ }
+}
+
+void KDEsuDialog::slotUser1()
+{
+ done(AsUser);
+}
+
+#include "sudlg.moc"
diff --git a/tdesu/tdesu/sudlg.h b/tdesu/tdesu/sudlg.h
new file mode 100644
index 000000000..64a283a0e
--- /dev/null
+++ b/tdesu/tdesu/sudlg.h
@@ -0,0 +1,32 @@
+/* vi: ts=8 sts=4 sw=4
+ *
+ * This file is part of the KDE project, module tdesu.
+ * Copyright (C) 2000 Geert Jansen <jansen@kde.org>
+ */
+
+#ifndef __SuDlg_h_Included__
+#define __SuDlg_h_Included__
+
+#include <kpassdlg.h>
+
+class KDEsuDialog
+ : public KPasswordDialog
+{
+ Q_OBJECT
+
+public:
+ KDEsuDialog(TQCString user, TQCString auth_user, bool enableKeep, const TQString& icon , bool withIgnoreButton=false);
+ ~KDEsuDialog();
+
+ enum ResultCodes { AsUser = 10 };
+
+protected:
+ bool checkPassword(const char *password);
+ void slotUser1();
+
+private:
+ TQCString m_User;
+};
+
+
+#endif // __SuDlg_h_Included__
diff --git a/tdesu/tdesu/tdesu.cpp b/tdesu/tdesu/tdesu.cpp
new file mode 100644
index 000000000..d6aeebc6f
--- /dev/null
+++ b/tdesu/tdesu/tdesu.cpp
@@ -0,0 +1,431 @@
+/* vi: ts=8 sts=4 sw=4
+ *
+ * This file is part of the KDE project, module tdesu.
+ * Copyright (C) 1998 Pietro Iglio <iglio@fub.it>
+ * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
+ */
+
+#include <config.h>
+
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <pwd.h>
+#include <stdlib.h>
+
+#include <sys/time.h>
+#include <sys/resource.h>
+#if defined(HAVE_SYS_WAIT_H)
+#include <sys/wait.h>
+#endif
+
+#include <tqstring.h>
+#include <tqfileinfo.h>
+#include <tqglobal.h>
+#include <tqfile.h>
+#include <tqdir.h>
+
+#include <dcopclient.h>
+
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kapplication.h>
+#include <kstandarddirs.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <kaboutdata.h>
+#include <kcmdlineargs.h>
+#include <kmessagebox.h>
+#include <krun.h>
+#include <kuser.h>
+
+#include <tdesu/defaults.h>
+#include <tdesu/su.h>
+#include <tdesu/client.h>
+
+#include "sudlg.h"
+
+#define ERR strerror(errno)
+
+TQCString command;
+const char *Version = "1.0";
+
+// NOTE: if you change the position of the -u switch, be sure to adjust it
+// at the beginning of main()
+static KCmdLineOptions options[] = {
+ { "+command", I18N_NOOP("Specifies the command to run"), 0 },
+ { "c <command>", I18N_NOOP("Specifies the command to run"), "" },
+ { "f <file>", I18N_NOOP("Run command under target uid if <file> is not writable"), "" },
+ { "u <user>", I18N_NOOP("Specifies the target uid"), "root" },
+ { "n", I18N_NOOP("Do not keep password"), 0 },
+ { "s", I18N_NOOP("Stop the daemon (forgets all passwords)"), 0 },
+ { "t", I18N_NOOP("Enable terminal output (no password keeping)"), 0 },
+ { "p <prio>", I18N_NOOP("Set priority value: 0 <= prio <= 100, 0 is lowest"), "50" },
+ { "r", I18N_NOOP("Use realtime scheduling"), 0 },
+ { "newdcop", I18N_NOOP("Create a new dcopserver instance (this can break your current TDE session)"), 0 },
+ { "noignorebutton", I18N_NOOP("Do not display ignore button"), 0 },
+ { "i <icon name>", I18N_NOOP("Specify icon to use in the password dialog"), 0},
+ { "d", I18N_NOOP("Do not show the command to be run in the dialog"), 0},
+ KCmdLineLastOption
+};
+
+
+TQCString dcopNetworkId()
+{
+ TQCString result;
+ result.resize(1025);
+ TQFile file(DCOPClient::dcopServerFile());
+ if (!file.open(IO_ReadOnly))
+ return "";
+ int i = file.readLine(result.data(), 1024);
+ if (i <= 0)
+ return "";
+ result.data()[i-1] = '\0'; // strip newline
+ return result;
+}
+
+static int startApp();
+
+int main(int argc, char *argv[])
+{
+ // FIXME: this can be considered a poor man's solution, as it's not
+ // directly obvious to a gui user. :)
+ // anyway, i vote against removing it even when we have a proper gui
+ // implementation. -- ossi
+ const char *duser = ::getenv("ADMIN_ACCOUNT");
+ if (duser && duser[0])
+ options[3].def = duser;
+
+ KAboutData aboutData("tdesu", I18N_NOOP("KDE su"),
+ Version, I18N_NOOP("Runs a program with elevated privileges."),
+ KAboutData::License_Artistic,
+ "Copyright (c) 1998-2000 Geert Jansen, Pietro Iglio");
+ aboutData.addAuthor("Geert Jansen", I18N_NOOP("Maintainer"),
+ "jansen@kde.org", "http://www.stack.nl/~geertj/");
+ aboutData.addAuthor("Pietro Iglio", I18N_NOOP("Original author"),
+ "iglio@fub.it");
+
+ KCmdLineArgs::init(argc, argv, &aboutData);
+ KCmdLineArgs::addCmdLineOptions(options);
+ KApplication::disableAutoDcopRegistration();
+ // tdesu doesn't process SM events, so don't even connect to ksmserver
+ TQCString session_manager = getenv( "SESSION_MANAGER" );
+ unsetenv( "SESSION_MANAGER" );
+ KApplication app;
+ // but propagate it to the started app
+ if (session_manager.data())
+ {
+ setenv( "SESSION_MANAGER", session_manager.data(), 1 );
+ }
+
+ {
+ KStartupInfoId id;
+ id.initId( kapp->startupId());
+ id.setupStartupEnv(); // make DESKTOP_STARTUP_ID env. var. available again
+ }
+
+ int result = startApp();
+
+ if (result == 127)
+ {
+ KMessageBox::sorry(0, i18n("Command '%1' not found.").arg(static_cast<const char *>(command)));
+ }
+
+ return result;
+}
+
+static int startApp()
+{
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+ // Stop daemon and exit?
+ if (args->isSet("s"))
+ {
+ KDEsuClient client;
+ if (client.ping() == -1)
+ {
+ kdError(1206) << "Daemon not running -- nothing to stop\n";
+ exit(1);
+ }
+ if (client.stopServer() != -1)
+ {
+ kdDebug(1206) << "Daemon stopped\n";
+ exit(0);
+ }
+ kdError(1206) << "Could not stop daemon\n";
+ exit(1);
+ }
+
+ TQString icon;
+ if ( args->isSet("i"))
+ icon = args->getOption("i");
+
+ bool prompt = true;
+ if ( args->isSet("d"))
+ prompt = false;
+
+ // Get target uid
+ TQCString user = args->getOption("u");
+ TQCString auth_user = user;
+ struct passwd *pw = getpwnam(user);
+ if (pw == 0L)
+ {
+ kdError(1206) << "User " << user << " does not exist\n";
+ exit(1);
+ }
+ bool change_uid = (getuid() != pw->pw_uid);
+
+ // If file is writeable, do not change uid
+ TQString file = TQFile::decodeName(args->getOption("f"));
+ if (change_uid && !file.isEmpty())
+ {
+ if (file.tqat(0) != '/')
+ {
+ KStandardDirs dirs;
+ dirs.addKDEDefaults();
+ file = dirs.findResource("config", file);
+ if (file.isEmpty())
+ {
+ kdError(1206) << "Config file not found: " << file << "\n";
+ exit(1);
+ }
+ }
+ TQFileInfo fi(file);
+ if (!fi.exists())
+ {
+ kdError(1206) << "File does not exist: " << file << "\n";
+ exit(1);
+ }
+ change_uid = !fi.isWritable();
+ }
+
+ // Get priority/scheduler
+ TQCString tmp = args->getOption("p");
+ bool ok;
+ int priority = tmp.toInt(&ok);
+ if (!ok || (priority < 0) || (priority > 100))
+ {
+ KCmdLineArgs::usage(i18n("Illegal priority: %1").arg(static_cast<const char *>(tmp)));
+ exit(1);
+ }
+ int scheduler = SuProcess::SchedNormal;
+ if (args->isSet("r"))
+ scheduler = SuProcess::SchedRealtime;
+ if ((priority > 50) || (scheduler != SuProcess::SchedNormal))
+ {
+ change_uid = true;
+ auth_user = "root";
+ }
+
+ // Get command
+ if (args->isSet("c"))
+ {
+ command = args->getOption("c");
+ for (int i=0; i<args->count(); i++)
+ {
+ TQString arg = TQFile::decodeName(args->arg(i));
+ KRun::shellQuote(arg);
+ command += " ";
+ command += TQFile::encodeName(arg);
+ }
+ }
+ else
+ {
+ if( args->count() == 0 )
+ {
+ KCmdLineArgs::usage(i18n("No command specified."));
+ exit(1);
+ }
+ command = args->arg(0);
+ for (int i=1; i<args->count(); i++)
+ {
+ TQString arg = TQFile::decodeName(args->arg(i));
+ KRun::shellQuote(arg);
+ command += " ";
+ command += TQFile::encodeName(arg);
+ }
+ }
+
+ // Don't change uid if we're don't need to.
+ if (!change_uid)
+ {
+ int result = system(command);
+ result = WEXITSTATUS(result);
+ return result;
+ }
+
+ // Check for daemon and start if necessary
+ bool just_started = false;
+ bool have_daemon = true;
+ KDEsuClient client;
+ if (!client.isServerSGID())
+ {
+ kdWarning(1206) << "Daemon not safe (not sgid), not using it.\n";
+ have_daemon = false;
+ }
+ else if (client.ping() == -1)
+ {
+ if (client.startServer() == -1)
+ {
+ kdWarning(1206) << "Could not start daemon, reduced functionality.\n";
+ have_daemon = false;
+ }
+ just_started = true;
+ }
+
+ // Try to exec the command with tdesud.
+ bool keep = !args->isSet("n") && have_daemon;
+ bool terminal = true;
+ bool new_dcop = args->isSet("newdcop");
+ bool withIgnoreButton = args->isSet("ignorebutton");
+
+ QCStringList env;
+ TQCString options;
+ env << ( "DESKTOP_STARTUP_ID=" + kapp->startupId());
+
+ if (pw->pw_uid)
+ {
+ // Only propagate KDEHOME for non-root users,
+ // root uses KDEROOTHOME
+
+ // Translate the KDEHOME of this user to the new user.
+ TQString kdeHome = KGlobal::dirs()->relativeLocation("home", KGlobal::dirs()->localkdedir());
+ if (kdeHome[0] != '/')
+ kdeHome.prepend("~/");
+ else
+ kdeHome=TQString::null; // Use default
+
+ env << ("KDEHOME="+ TQFile::encodeName(kdeHome));
+ }
+
+ KUser u;
+ env << (TQCString) ("KDESU_USER=" + u.loginName().local8Bit());
+
+ if (!new_dcop)
+ {
+ TQCString ksycoca = "KDESYCOCA="+TQFile::encodeName(locateLocal("cache", "ksycoca"));
+ env << ksycoca;
+
+ options += "xf"; // X-only, dcop forwarding enabled.
+ }
+
+ if (keep && !terminal && !just_started)
+ {
+ client.setPriority(priority);
+ client.setScheduler(scheduler);
+ int result = client.exec(command, user, options, env);
+ if (result == 0)
+ {
+ result = client.exitCode();
+ return result;
+ }
+ }
+
+ // Set core dump size to 0 because we will have
+ // root's password in memory.
+ struct rlimit rlim;
+ rlim.rlim_cur = rlim.rlim_max = 0;
+ if (setrlimit(RLIMIT_CORE, &rlim))
+ {
+ kdError(1206) << "rlimit(): " << ERR << "\n";
+ exit(1);
+ }
+
+ // Read configuration
+ KConfig *config = KGlobal::config();
+ config->setGroup("Passwords");
+ int timeout = config->readNumEntry("Timeout", defTimeout);
+
+ // Check if we need a password
+ SuProcess proc;
+ proc.setUser(auth_user);
+ int needpw = proc.checkNeedPassword();
+ if (needpw < 0)
+ {
+ TQString err = i18n("Su returned with an error.\n");
+ KMessageBox::error(0L, err);
+ exit(1);
+ }
+ if (needpw == 0)
+ {
+ keep = 0;
+ kdDebug() << "Don't need password!!\n";
+ }
+
+ // Start the dialog
+ TQCString password;
+ if (needpw)
+ {
+ KStartupInfoId id;
+ id.initId( kapp->startupId());
+ KStartupInfoData data;
+ data.setSilent( KStartupInfoData::Yes );
+ KStartupInfo::sendChange( id, data );
+ KDEsuDialog dlg(user, auth_user, keep && !terminal,icon, withIgnoreButton);
+ if (prompt)
+ dlg.addLine(i18n("Command:"), command);
+ if ((priority != 50) || (scheduler != SuProcess::SchedNormal))
+ {
+ TQString prio;
+ if (scheduler == SuProcess::SchedRealtime)
+ prio += i18n("realtime: ");
+ prio += TQString("%1/100").arg(priority);
+ if (prompt)
+ dlg.addLine(i18n("Priority:"), prio);
+ }
+ int ret = dlg.exec();
+ if (ret == KDEsuDialog::Rejected)
+ {
+ KStartupInfo::sendFinish( id );
+ exit(0);
+ }
+ if (ret == KDEsuDialog::AsUser)
+ change_uid = false;
+ password = dlg.password();
+ keep = dlg.keep();
+ KConfigGroup(config,"Passwords").writeEntry("Keep", keep);
+ data.setSilent( KStartupInfoData::No );
+ KStartupInfo::sendChange( id, data );
+ }
+
+ // Some events may need to be handled (like a button animation)
+ kapp->processEvents();
+
+ // Run command
+ if (!change_uid)
+ {
+ int result = system(command);
+ result = WEXITSTATUS(result);
+ return result;
+ }
+ else if (keep && have_daemon)
+ {
+ client.setPass(password, timeout);
+ client.setPriority(priority);
+ client.setScheduler(scheduler);
+ int result = client.exec(command, user, options, env);
+ if (result == 0)
+ {
+ result = client.exitCode();
+ return result;
+ }
+ } else
+ {
+ SuProcess proc;
+ proc.setTerminal(terminal);
+ proc.setErase(true);
+ proc.setUser(user);
+ if (!new_dcop)
+ {
+ proc.setXOnly(true);
+ proc.setDCOPForwarding(true);
+ }
+ proc.setEnvironment(env);
+ proc.setPriority(priority);
+ proc.setScheduler(scheduler);
+ proc.setCommand(command);
+ int result = proc.exec(password);
+ return result;
+ }
+ return -1;
+}
+
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";
+}
+