summaryrefslogtreecommitdiffstats
path: root/kdesu
diff options
context:
space:
mode:
Diffstat (limited to 'kdesu')
-rw-r--r--kdesu/Mainpage.dox21
-rw-r--r--kdesu/Makefile.am30
-rw-r--r--kdesu/README13
-rw-r--r--kdesu/client.cpp435
-rw-r--r--kdesu/client.h207
-rw-r--r--kdesu/configure.in.in93
-rw-r--r--kdesu/defaults.h20
-rw-r--r--kdesu/kcookie.cpp225
-rw-r--r--kdesu/kcookie.h90
-rw-r--r--kdesu/kdesu_pty.cpp302
-rw-r--r--kdesu/kdesu_pty.h71
-rw-r--r--kdesu/kdesu_stub.c432
-rw-r--r--kdesu/libkdesu.nmcheck10
-rw-r--r--kdesu/libkdesu_weak.nmcheck2
-rw-r--r--kdesu/process.cpp626
-rw-r--r--kdesu/process.h188
-rw-r--r--kdesu/ssh.cpp279
-rw-r--r--kdesu/ssh.h90
-rw-r--r--kdesu/stub.cpp184
-rw-r--r--kdesu/stub.h139
-rw-r--r--kdesu/su.cpp342
-rw-r--r--kdesu/su.h63
22 files changed, 3862 insertions, 0 deletions
diff --git a/kdesu/Mainpage.dox b/kdesu/Mainpage.dox
new file mode 100644
index 000000000..030ae3650
--- /dev/null
+++ b/kdesu/Mainpage.dox
@@ -0,0 +1,21 @@
+/** @mainpage Console-mode authentication
+
+libkdesu provides functionality for building GUI front ends for
+(password asking) console mode programs. For example, kdesu and
+kdessh use it to interface with su and ssh respectively.
+
+@authors
+Geert Jansen \<jansen@kde.org\>
+
+@maintainers
+Adriaan de Groot \<groot@kde.org\>
+
+@licenses
+@lgpl
+
+
+*/
+
+// DOXYGEN_REFERENCES = kdecore
+// DOXYGEN_SET_PROJECT_NAME = KDEsu
+// vim:ts=4:sw=4:expandtab:filetype=doxygen
diff --git a/kdesu/Makefile.am b/kdesu/Makefile.am
new file mode 100644
index 000000000..211d8d422
--- /dev/null
+++ b/kdesu/Makefile.am
@@ -0,0 +1,30 @@
+## Makefile.am for libkdesu
+
+INCLUDES = -I$(top_srcdir)/kio/ $(all_includes)
+
+lib_LTLIBRARIES = libkdesu.la
+libkdesu_la_SOURCES = client.cpp process.cpp kcookie.cpp su.cpp ssh.cpp stub.cpp kdesu_pty.cpp
+libkdesu_la_LDFLAGS = -version-info 6:0:2 -no-undefined $(all_libraries)
+libkdesu_la_LIBADD = $(LIB_KDECORE) $(LIBUTIL)
+libkdesu_la_NMCHECK = $(srcdir)/libkdesu.nmcheck
+libkdesu_la_NMCHECKWEAK = $(srcdir)/libkdesu_weak.nmcheck $(top_srcdir)/kdecore/libkdecore_weak.nmcheck \
+ $(top_srcdir)/dcop/libDCOP_weak.nmcheck $(top_srcdir)/kdecore/libqt-mt_weak.nmcheck
+
+kdesudir = $(includedir)/kdesu
+kdesu_HEADERS = defaults.h client.h process.h kdesu_pty.h kcookie.h su.h ssh.h stub.h
+
+install-data-hook:
+ $(mkinstalldirs) $(DESTDIR)$(kdesudir)
+ -rm -f $(DESTDIR)$(kdesudir)/pty.h
+ ln -s kdesu_pty.h $(DESTDIR)$(kdesudir)/pty.h
+
+uninstall-local:
+ -rm -f $(DESTDIR)$(kdesudir)/pty.h
+
+bin_PROGRAMS = kdesu_stub
+kdesu_stub_SOURCES = kdesu_stub.c
+kdesu_stub_LDFLAGS = $(all_libraries)
+kdesu_stub_LDADD = ../kdecore/libkdefakes.la
+
+include $(top_srcdir)/admin/Doxyfile.am
+
diff --git a/kdesu/README b/kdesu/README
new file mode 100644
index 000000000..0a6acd9d3
--- /dev/null
+++ b/kdesu/README
@@ -0,0 +1,13 @@
+Maintainer: Adriaan de Groot <groot@kde.org>
+Maintainer: kdesu was maintained by Alan Eldridge until his
+ death in 2003.
+
+README for libkdesu.
+
+Libkdesu provides functionality for building GUI front ends for
+(password asking) console mode programs. For example, kdesu and
+kdessh use it to interface with su and ssh respectively.
+
+libkdesu is Copyright (c) 1999,2000 Geert Jansen <jansen@kde.org>
+
+Distributed under the GNU Library General Public License, version 2.
diff --git a/kdesu/client.cpp b/kdesu/client.cpp
new file mode 100644
index 000000000..ae97f0af8
--- /dev/null
+++ b/kdesu/client.cpp
@@ -0,0 +1,435 @@
+/* vi: ts=8 sts=4 sw=4
+ *
+ * $Id$
+ *
+ * This file is part of the KDE project, module kdesu.
+ * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
+ *
+ * This is free software; you can use this library under the GNU Library
+ * General Public License, version 2. See the file "COPYING.LIB" for the
+ * exact licensing terms.
+ *
+ * client.cpp: A client for kdesud.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include <errno.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+
+#include <qglobal.h>
+#include <qcstring.h>
+#include <qfile.h>
+#include <qregexp.h>
+
+#include <kdebug.h>
+#include <kstandarddirs.h>
+#include <kapplication.h>
+#include <kde_file.h>
+
+#include "client.h"
+
+class KDEsuClient::KDEsuClientPrivate {
+public:
+ QString daemon;
+};
+
+#ifndef SUN_LEN
+#define SUN_LEN(ptr) ((socklen_t) (((struct sockaddr_un *) 0)->sun_path) \
+ + strlen ((ptr)->sun_path))
+#endif
+
+KDEsuClient::KDEsuClient()
+{
+ sockfd = -1;
+#ifdef Q_WS_X11
+ QCString display(getenv("DISPLAY"));
+ if (display.isEmpty())
+ {
+ kdWarning(900) << k_lineinfo << "$DISPLAY is not set\n";
+ return;
+ }
+
+ // strip the screen number from the display
+ display.replace(QRegExp("\\.[0-9]+$"), "");
+#else
+ QCString display("QWS");
+#endif
+
+ sock = QFile::encodeName(locateLocal("socket", QString("kdesud_%1").arg(display)));
+ d = new KDEsuClientPrivate;
+ connect();
+}
+
+
+KDEsuClient::~KDEsuClient()
+{
+ delete d;
+ if (sockfd >= 0)
+ close(sockfd);
+}
+
+int KDEsuClient::connect()
+{
+ if (sockfd >= 0)
+ close(sockfd);
+ if (access(sock, R_OK|W_OK))
+ {
+ sockfd = -1;
+ return -1;
+ }
+
+ sockfd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (sockfd < 0)
+ {
+ kdWarning(900) << k_lineinfo << "socket(): " << perror << "\n";
+ return -1;
+ }
+ struct sockaddr_un addr;
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, sock);
+
+ if (::connect(sockfd, (struct sockaddr *) &addr, SUN_LEN(&addr)) < 0)
+ {
+ kdWarning(900) << k_lineinfo << "connect():" << perror << endl;
+ close(sockfd); sockfd = -1;
+ return -1;
+ }
+
+#if !defined(SO_PEERCRED) || !defined(HAVE_STRUCT_UCRED)
+# if defined(HAVE_GETPEEREID)
+ uid_t euid;
+ gid_t egid;
+ // Security: if socket exists, we must own it
+ if (getpeereid(sockfd, &euid, &egid) == 0)
+ {
+ if (euid != getuid())
+ {
+ kdWarning(900) << "socket not owned by me! socket uid = " << euid << endl;
+ close(sockfd); sockfd = -1;
+ return -1;
+ }
+ }
+# else
+# ifdef __GNUC__
+# warning "Using sloppy security checks"
+# endif
+ // We check the owner of the socket after we have connected.
+ // If the socket was somehow not ours an attacker will be able
+ // to delete it after we connect but shouldn't be able to
+ // create a socket that is owned by us.
+ KDE_struct_stat s;
+ if (KDE_lstat(sock, &s)!=0)
+ {
+ kdWarning(900) << "stat failed (" << sock << ")" << endl;
+ close(sockfd); sockfd = -1;
+ return -1;
+ }
+ if (s.st_uid != getuid())
+ {
+ kdWarning(900) << "socket not owned by me! socket uid = " << s.st_uid << endl;
+ close(sockfd); sockfd = -1;
+ return -1;
+ }
+ if (!S_ISSOCK(s.st_mode))
+ {
+ kdWarning(900) << "socket is not a socket (" << sock << ")" << endl;
+ close(sockfd); sockfd = -1;
+ return -1;
+ }
+# endif
+#else
+ struct ucred cred;
+ socklen_t siz = sizeof(cred);
+
+ // Security: if socket exists, we must own it
+ if (getsockopt(sockfd, SOL_SOCKET, SO_PEERCRED, &cred, &siz) == 0)
+ {
+ if (cred.uid != getuid())
+ {
+ kdWarning(900) << "socket not owned by me! socket uid = " << cred.uid << endl;
+ close(sockfd); sockfd = -1;
+ return -1;
+ }
+ }
+#endif
+
+ return 0;
+}
+
+QCString KDEsuClient::escape(const QCString &str)
+{
+ QCString copy = str;
+ int n = 0;
+ while ((n = copy.find("\\", n)) != -1)
+ {
+ copy.insert(n, '\\');
+ n += 2;
+ }
+ n = 0;
+ while ((n = copy.find("\"", n)) != -1)
+ {
+ copy.insert(n, '\\');
+ n += 2;
+ }
+ copy.prepend("\"");
+ copy.append("\"");
+ return copy;
+}
+
+int KDEsuClient::command(const QCString &cmd, QCString *result)
+{
+ if (sockfd < 0)
+ return -1;
+
+ if (send(sockfd, cmd, cmd.length(), 0) != (int) cmd.length())
+ return -1;
+
+ char buf[1024];
+ int nbytes = recv(sockfd, buf, 1023, 0);
+ if (nbytes <= 0)
+ {
+ kdWarning(900) << k_lineinfo << "no reply from daemon\n";
+ return -1;
+ }
+ buf[nbytes] = '\000';
+
+ QCString reply = buf;
+ if (reply.left(2) != "OK")
+ return -1;
+
+ if (result)
+ *result = reply.mid(3, reply.length()-4);
+ return 0;
+}
+
+int KDEsuClient::setPass(const char *pass, int timeout)
+{
+ QCString cmd = "PASS ";
+ cmd += escape(pass);
+ cmd += " ";
+ cmd += QCString().setNum(timeout);
+ cmd += "\n";
+ return command(cmd);
+}
+
+int KDEsuClient::exec(const QCString &prog, const QCString &user, const QCString &options, const QCStringList &env)
+{
+ QCString cmd;
+ cmd = "EXEC ";
+ cmd += escape(prog);
+ cmd += " ";
+ cmd += escape(user);
+ if (!options.isEmpty() || !env.isEmpty())
+ {
+ cmd += " ";
+ cmd += escape(options);
+ for(QCStringList::ConstIterator it = env.begin();
+ it != env.end(); ++it)
+ {
+ cmd += " ";
+ cmd += escape(*it);
+ }
+ }
+ cmd += "\n";
+ return command(cmd);
+}
+
+int KDEsuClient::setHost(const QCString &host)
+{
+ QCString cmd = "HOST ";
+ cmd += escape(host);
+ cmd += "\n";
+ return command(cmd);
+}
+
+int KDEsuClient::setPriority(int prio)
+{
+ QCString cmd;
+ cmd.sprintf("PRIO %d\n", prio);
+ return command(cmd);
+}
+
+int KDEsuClient::setScheduler(int sched)
+{
+ QCString cmd;
+ cmd.sprintf("SCHD %d\n", sched);
+ return command(cmd);
+}
+
+int KDEsuClient::delCommand(const QCString &key, const QCString &user)
+{
+ QCString cmd = "DEL ";
+ cmd += escape(key);
+ cmd += " ";
+ cmd += escape(user);
+ cmd += "\n";
+ return command(cmd);
+}
+int KDEsuClient::setVar(const QCString &key, const QCString &value, int timeout,
+ const QCString &group)
+{
+ QCString cmd = "SET ";
+ cmd += escape(key);
+ cmd += " ";
+ cmd += escape(value);
+ cmd += " ";
+ cmd += escape(group);
+ cmd += " ";
+ cmd += QCString().setNum(timeout);
+ cmd += "\n";
+ return command(cmd);
+}
+
+QCString KDEsuClient::getVar(const QCString &key)
+{
+ QCString cmd = "GET ";
+ cmd += escape(key);
+ cmd += "\n";
+ QCString reply;
+ command(cmd, &reply);
+ return reply;
+}
+
+QValueList<QCString> KDEsuClient::getKeys(const QCString &group)
+{
+ QCString cmd = "GETK ";
+ cmd += escape(group);
+ cmd += "\n";
+ QCString reply;
+ command(cmd, &reply);
+ int index=0, pos;
+ QValueList<QCString> list;
+ if( !reply.isEmpty() )
+ {
+ // kdDebug(900) << "Found a matching entry: " << reply << endl;
+ while (1)
+ {
+ pos = reply.find( '\007', index );
+ if( pos == -1 )
+ {
+ if( index == 0 )
+ list.append( reply );
+ else
+ list.append( reply.mid(index) );
+ break;
+ }
+ else
+ {
+ list.append( reply.mid(index, pos-index) );
+ }
+ index = pos+1;
+ }
+ }
+ return list;
+}
+
+bool KDEsuClient::findGroup(const QCString &group)
+{
+ QCString cmd = "CHKG ";
+ cmd += escape(group);
+ cmd += "\n";
+ if( command(cmd) == -1 )
+ return false;
+ return true;
+}
+
+int KDEsuClient::delVar(const QCString &key)
+{
+ QCString cmd = "DELV ";
+ cmd += escape(key);
+ cmd += "\n";
+ return command(cmd);
+}
+
+int KDEsuClient::delGroup(const QCString &group)
+{
+ QCString cmd = "DELG ";
+ cmd += escape(group);
+ cmd += "\n";
+ return command(cmd);
+}
+
+int KDEsuClient::delVars(const QCString &special_key)
+{
+ QCString cmd = "DELS ";
+ cmd += escape(special_key);
+ cmd += "\n";
+ return command(cmd);
+}
+
+int KDEsuClient::ping()
+{
+ return command("PING\n");
+}
+
+int KDEsuClient::exitCode()
+{
+ QCString result;
+ if (command("EXIT\n", &result) != 0)
+ return -1;
+
+ return result.toLong();
+}
+
+int KDEsuClient::stopServer()
+{
+ return command("STOP\n");
+}
+
+static QString findDaemon()
+{
+ QString daemon = locate("bin", "kdesud");
+ if (daemon.isEmpty()) // if not in KDEDIRS, rely on PATH
+ daemon = KStandardDirs::findExe("kdesud");
+
+ if (daemon.isEmpty())
+ {
+ kdWarning(900) << k_lineinfo << "daemon not found\n";
+ }
+ return daemon;
+}
+
+bool KDEsuClient::isServerSGID()
+{
+ if (d->daemon.isEmpty())
+ d->daemon = findDaemon();
+ if (d->daemon.isEmpty())
+ return false;
+
+ KDE_struct_stat sbuf;
+ if (KDE_stat(QFile::encodeName(d->daemon), &sbuf) < 0)
+ {
+ kdWarning(900) << k_lineinfo << "stat(): " << perror << "\n";
+ return false;
+ }
+ return (sbuf.st_mode & S_ISGID);
+}
+
+int KDEsuClient::startServer()
+{
+ if (d->daemon.isEmpty())
+ d->daemon = findDaemon();
+ if (d->daemon.isEmpty())
+ return -1;
+
+ if (!isServerSGID()) {
+ kdWarning(900) << k_lineinfo << "kdesud not setgid!\n";
+ }
+
+ // kdesud only forks to the background after it is accepting
+ // connections.
+ // We start it via kdeinit to make sure that it doesn't inherit
+ // any fd's from the parent process.
+ int ret = kapp->kdeinitExecWait(d->daemon);
+ connect();
+ return ret;
+}
diff --git a/kdesu/client.h b/kdesu/client.h
new file mode 100644
index 000000000..00957b1ed
--- /dev/null
+++ b/kdesu/client.h
@@ -0,0 +1,207 @@
+/* vi: ts=8 sts=4 sw=4
+ *
+ * $Id$
+ *
+ * This file is part of the KDE project, module kdesu.
+ * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
+ *
+ * This is free software; you can use this library under the GNU Library
+ * General Public License, version 2. See the file "COPYING.LIB" for the
+ * exact licensing terms.
+ *
+ * client.h: client to access kdesud.
+ */
+
+#ifndef __KDE_su_Client_h_Included__
+#define __KDE_su_Client_h_Included__
+
+#include <qglobal.h>
+#include <kdelibs_export.h>
+
+#ifdef Q_OS_UNIX
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <qcstring.h>
+#include <qvaluelist.h>
+
+typedef QValueList<QCString> QCStringList;
+
+/**
+ * A client class to access kdesud, the KDE su daemon. Kdesud can assist in
+ * password caching in two ways:
+ *
+ * @li For high security passwords, like for su and ssh, it executes the
+ * password requesting command for you. It feeds the password to the
+ * command, without ever returning it to you, the user. The daemon should
+ * be installed setgid nogroup, in order to be able to act as an inaccessible,
+ * trusted 3rd party.
+ * See exec, setPass, delCommand.
+ *
+ * @li For lower security passwords, like web and ftp passwords, it can act
+ * as a persistent storage for string variables. These variables are
+ * returned to the user, and the daemon doesn't need to be setgid nogroup
+ * for this.
+ * See setVar, delVar, delGroup.
+ */
+
+class KDESU_EXPORT KDEsuClient {
+public:
+ KDEsuClient();
+ ~KDEsuClient();
+
+ /**
+ * Lets kdesud execute a command. If the daemon does not have a password
+ * for this command, this will fail and you need to call setPass().
+ *
+ * @param command The command to execute.
+ * @param user The user to run the command as.
+ * @param options Extra options.
+ * @param env Extra environment variables.
+ * @return Zero on success, -1 on failure.
+ */
+ int exec(const QCString &command, const QCString &user, const QCString &options=0, const QCStringList &env=QCStringList());
+
+ /**
+ * Wait for the last command to exit and return the exit code.
+ * @return Exit code of last command, -1 on failure.
+ */
+ int exitCode();
+
+ /**
+ * Set root's password, lasts one session.
+ *
+ * @param pass Root's password.
+ * @param timeout The time that a password will live.
+ * @return Zero on success, -1 on failure.
+ */
+ int setPass(const char *pass, int timeout);
+
+ /**
+ * Set the target host (optional).
+ */
+ int setHost(const QCString &host);
+
+ /**
+ * Set the desired priority (optional), see StubProcess.
+ */
+ int setPriority(int priority);
+
+ /**
+ * Set the desired scheduler (optional), see StubProcess.
+ */
+ int setScheduler(int scheduler);
+
+ /**
+ * Remove a password for a user/command.
+ * @param command The command.
+ * @param user The user.
+ * @return zero on success, -1 on an error
+ */
+ int delCommand(const QCString &command, const QCString &user);
+
+ /**
+ * Set a persistent variable.
+ * @param key The name of the variable.
+ * @param value Its value.
+ * @param timeout The timeout in seconds for this key. Zero means
+ * no timeout.
+ * @param group Make the key part of a group. See delGroup.
+ * @return zero on success, -1 on failure.
+ */
+ int setVar(const QCString &key, const QCString &value, int timeout=0, const QCString &group=0);
+
+ /**
+ * Get a persistent variable.
+ * @param key The name of the variable.
+ * @return Its value.
+ */
+ QCString getVar(const QCString &key);
+
+ /**
+ * Gets all the keys that are membes of the given group.
+ * @param group the group name of the variables.
+ * @return a list of the keys in the group.
+ */
+ QValueList<QCString> getKeys(const QCString &group);
+
+ /**
+ * Returns true if the specified group exists is
+ * cached.
+ *
+ * @param group the group key
+ * @return true if the group is found
+ */
+ bool findGroup(const QCString &group);
+
+ /**
+ * Delete a persistent variable.
+ * @param key The name of the variable.
+ * @return zero on success, -1 on failure.
+ */
+ int delVar(const QCString &key);
+
+ /**
+ * Delete all persistent variables with the given key.
+ *
+ * A specicalized variant of delVar(QCString) that removes all
+ * subsets of the cached varaibles given by @p key. In order for all
+ * cached variables related to this key to be deleted properly, the
+ * value given to the @p group argument when the setVar function
+ * was called, must be a subset of the argument given here and the key
+ *
+ * @note Simply supplying the group key here WILL not necessarily
+ * work. If you only have a group key, then use delGroup instead.
+ *
+ * @param special_key the name of the variable.
+ * @return zero on success, -1 on failure.
+ */
+ int delVars(const QCString &special_key);
+
+ /**
+ * Delete all persistent variables in a group.
+ *
+ * @param group the group name. See setVar.
+ * @return
+ */
+ int delGroup(const QCString &group);
+
+ /**
+ * Ping kdesud. This can be used for diagnostics.
+ * @return Zero on success, -1 on failure
+ */
+ int ping();
+
+ /**
+ * Stop the daemon.
+ */
+ int stopServer();
+
+ /**
+ * Try to start up kdesud
+ */
+ int startServer();
+
+ /**
+ * Returns true if the server is safe (installed setgid), false otherwise.
+ */
+ bool isServerSGID();
+
+private:
+ int connect();
+
+ int sockfd;
+ QCString sock;
+
+ int command(const QCString &cmd, QCString *result=0L);
+ QCString escape(const QCString &str);
+
+ class KDEsuClientPrivate;
+ KDEsuClientPrivate *d;
+};
+
+#endif //Q_OS_UNIX
+
+#endif //__KDE_su_Client_h_Included__
diff --git a/kdesu/configure.in.in b/kdesu/configure.in.in
new file mode 100644
index 000000000..4b4c6b78f
--- /dev/null
+++ b/kdesu/configure.in.in
@@ -0,0 +1,93 @@
+dnl Check for su
+AC_PATH_PROG(path_su, "su", "no")
+if test "$path_su" = "no"; then
+ AC_MSG_WARN(su was not found)
+else
+ AC_DEFINE_UNQUOTED(__PATH_SU, "$path_su", [path to su])
+fi
+
+dnl Check for sudo
+AC_PATH_PROG(path_sudo, "sudo", "no")
+if test "$path_sudo" = "no"; then
+ AC_MSG_WARN(sudo was not found)
+else
+ AC_DEFINE_UNQUOTED(__PATH_SUDO, "$path_sudo", [path to sudo])
+fi
+
+AC_ARG_WITH(sudo-kdesu-backend,
+ AC_HELP_STRING([--with-sudo-kdesu-backend],
+ [use sudo as backend for kdesu (default is su)]),
+[
+ if test x$withval = xyes; then
+ use_kdesu_backend="sudo"
+ else
+ use_kdesu_backend="su"
+ fi
+],
+ use_kdesu_backend="su"
+)
+
+if test x$use_kdesu_backend = xsudo -a x$path_sudo = xno; then
+ AC_MSG_ERROR(sudo was chosen as kdesu backend, but was not found in path.)
+fi
+
+AC_DEFINE_UNQUOTED(DEFAULT_SUPER_USER_COMMAND, "$use_kdesu_backend", [Use su or sudo])
+
+dnl Check for POSIX.1b scheduling
+AC_MSG_CHECKING([POSIX.1b scheduling])
+AC_TRY_LINK([
+ #include <sched.h>
+],
+[
+ sched_getscheduler(0);
+],
+have_rtsched="yes", have_rtsched="no")
+if test "$have_rtsched" = "yes"; then
+ AC_DEFINE(POSIX1B_SCHEDULING, 1, [Define if you have POSIX.1b scheduling])
+ AC_MSG_RESULT(yes)
+else
+ AC_MSG_RESULT(no)
+fi
+
+dnl Check for initgroups()
+AC_CHECK_FUNCS(initgroups)
+
+dnl openpty stuff
+AC_CHECK_HEADERS(libutil.h util.h pty.h)
+AC_CHECK_LIB(util, openpty, [AC_DEFINE_UNQUOTED(HAVE_OPENPTY, 1, [Define if you have openpty in -lutil])])
+AC_CHECK_FUNCS(openpty initgroups setgroups getgroups grantpt setpriority getpt unlockpt ptsname)
+
+AH_VERBATIM(_OPENPTY,
+[
+/*
+ * Steven Schultz <sms at to.gd-es.com> tells us :
+ * BSD/OS 4.2 doesn't have a prototype for openpty in its system header files
+ */
+#ifdef __bsdi__
+__BEGIN_DECLS
+int openpty(int *, int *, char *, struct termios *, struct winsize *);
+__END_DECLS
+#endif
+])
+
+dnl irix pty stuff
+AC_CHECK_FUNCS(_getpty)
+
+AC_MSG_CHECKING([for struct ucred])
+AC_CACHE_VAL(kde_cv_have_struct_ucred,
+[
+ AC_TRY_COMPILE(
+ [
+ #include <sys/socket.h>
+ ],
+ [
+ struct ucred cred;
+ ], kde_cv_have_struct_ucred=yes,
+ kde_cv_have_struct_ucred=no)
+])
+
+AC_MSG_RESULT($kde_cv_have_struct_ucred)
+if test "$kde_cv_have_struct_ucred" = yes; then
+ AC_DEFINE(HAVE_STRUCT_UCRED,1, [Define if struct ucred is present from sys/socket.h])
+fi
+AC_CHECK_FUNCS(getpeereid)
diff --git a/kdesu/defaults.h b/kdesu/defaults.h
new file mode 100644
index 000000000..7610985c2
--- /dev/null
+++ b/kdesu/defaults.h
@@ -0,0 +1,20 @@
+/* vi: ts=8 sts=4 sw=4
+ *
+ * $Id$
+ *
+ * This file is part of the KDE project, module kdesu.
+ * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
+ *
+ * This is free software; you can use this library under the GNU Library
+ * General Public License, version 2. See the file "COPYING.LIB" for the
+ * exact licensing terms.
+ */
+
+#ifndef __Defaults_h_included__
+#define __Defaults_h_included__
+
+const int defTimeout = 120*60;
+const int defEchoMode = 0;
+const int defKeep = false;
+
+#endif
diff --git a/kdesu/kcookie.cpp b/kdesu/kcookie.cpp
new file mode 100644
index 000000000..355dad82e
--- /dev/null
+++ b/kdesu/kcookie.cpp
@@ -0,0 +1,225 @@
+/* vi: ts=8 sts=4 sw=4
+ *
+ * $Id$
+ *
+ * This file is part of the KDE project, module kdesu.
+ * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
+ *
+ * This is free software; you can use this library under the GNU Library
+ * General Public License, version 2. See the file "COPYING.LIB" for the
+ * exact licensing terms.
+ *
+ * kcookie.cpp: KDE authentication cookies.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qglobal.h>
+#include <qfile.h>
+
+#include <dcopclient.h>
+
+#include <kdebug.h>
+#include <kprocess.h>
+#include "kcookie.h"
+
+
+KCookie::KCookie()
+{
+#ifdef Q_WS_X11
+ getXCookie();
+#endif
+ setDcopTransport("local");
+}
+
+void KCookie::setDcopTransport(const QCString &dcopTransport)
+{
+ m_dcopTransport = dcopTransport;
+ m_bHaveDCOPCookies = false;
+ m_bHaveICECookies = false;
+ m_DCOPSrv = "";
+ m_DCOPAuth = "";
+ m_ICEAuth = "";
+}
+
+QCStringList KCookie::split(const QCString &line, char ch)
+{
+ QCStringList result;
+
+ int i=0, pos;
+ while ((pos = line.find(ch, i)) != -1)
+ {
+ result += line.mid(i, pos-i);
+ i = pos+1;
+ }
+ if (i < (int) line.length())
+ result += line.mid(i);
+ return result;
+}
+
+void KCookie::blockSigChild()
+{
+ sigset_t sset;
+ sigemptyset(&sset);
+ sigaddset(&sset, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &sset, 0L);
+}
+
+void KCookie::unblockSigChild()
+{
+ sigset_t sset;
+ sigemptyset(&sset);
+ sigaddset(&sset, SIGCHLD);
+ sigprocmask(SIG_UNBLOCK, &sset, 0L);
+}
+
+void KCookie::getXCookie()
+{
+ char buf[1024];
+ FILE *f;
+
+#ifdef Q_WS_X11
+ m_Display = getenv("DISPLAY");
+#else
+ m_Display = getenv("QWS_DISPLAY");
+#endif
+ if (m_Display.isEmpty())
+ {
+ kdError(900) << k_lineinfo << "$DISPLAY is not set.\n";
+ return;
+ }
+#ifdef Q_WS_X11 // No need to mess with X Auth stuff
+ QCString disp = m_Display;
+ if (!memcmp(disp.data(), "localhost:", 10))
+ disp.remove(0, 9);
+
+ QString cmd = "xauth list "+KProcess::quote(disp);
+ blockSigChild(); // pclose uses waitpid()
+ if (!(f = popen(QFile::encodeName(cmd), "r")))
+ {
+ kdError(900) << k_lineinfo << "popen(): " << perror << "\n";
+ unblockSigChild();
+ return;
+ }
+ QCString output = fgets(buf, 1024, f);
+ if (pclose(f) < 0)
+ {
+ kdError(900) << k_lineinfo << "Could not run xauth.\n";
+ unblockSigChild();
+ return;
+ }
+ unblockSigChild();
+ output = output.simplifyWhiteSpace();
+ if (output.isEmpty())
+ {
+ kdWarning(900) << "No X authentication info set for display " <<
+ m_Display << endl; return;
+ }
+ QCStringList lst = split(output, ' ');
+ if (lst.count() != 3)
+ {
+ kdError(900) << k_lineinfo << "parse error.\n";
+ return;
+ }
+ m_DisplayAuth = (lst[1] + ' ' + lst[2]);
+#endif
+}
+
+void KCookie::getICECookie()
+{
+ FILE *f;
+ char buf[1024];
+
+ QCString dcopsrv = getenv("DCOPSERVER");
+ if (dcopsrv.isEmpty())
+ {
+ QCString dcopFile = DCOPClient::dcopServerFile();
+ if (!(f = fopen(dcopFile, "r")))
+ {
+ kdWarning(900) << k_lineinfo << "Cannot open " << dcopFile << ".\n";
+ return;
+ }
+ dcopsrv = fgets(buf, 1024, f);
+ dcopsrv = dcopsrv.stripWhiteSpace();
+ fclose(f);
+ }
+ QCStringList dcopServerList = split(dcopsrv, ',');
+ if (dcopServerList.isEmpty())
+ {
+ kdError(900) << k_lineinfo << "No DCOP servers found.\n";
+ return;
+ }
+
+ QCStringList::Iterator it;
+ for (it=dcopServerList.begin(); it != dcopServerList.end(); ++it)
+ {
+ if (strncmp((*it).data(), m_dcopTransport.data(), m_dcopTransport.length()) != 0)
+ continue;
+ m_DCOPSrv = *it;
+ QCString cmd = DCOPClient::iceauthPath()+" list netid="+QFile::encodeName(KProcess::quote(m_DCOPSrv));
+ blockSigChild();
+ if (!(f = popen(cmd, "r")))
+ {
+ kdError(900) << k_lineinfo << "popen(): " << perror << "\n";
+ unblockSigChild();
+ break;
+ }
+ QCStringList output;
+ while (fgets(buf, 1024, f))
+ output += buf;
+ if (pclose(f) < 0)
+ {
+ kdError(900) << k_lineinfo << "Could not run iceauth.\n";
+ unblockSigChild();
+ break;
+ }
+ unblockSigChild();
+ QCStringList::Iterator it2;
+ for (it2=output.begin(); it2!=output.end(); ++it2)
+ {
+ QCStringList lst = split((*it2).simplifyWhiteSpace(), ' ');
+ if (lst.count() != 5)
+ {
+ kdError(900) << "parse error.\n";
+ break;
+ }
+ if (lst[0] == "DCOP")
+ m_DCOPAuth = (lst[3] + ' ' + lst[4]);
+ else if (lst[0] == "ICE")
+ m_ICEAuth = (lst[3] + ' ' + lst[4]);
+ else
+ kdError(900) << k_lineinfo << "unknown protocol: " << lst[0] << "\n";
+ }
+ break;
+ }
+ m_bHaveDCOPCookies = true;
+ m_bHaveICECookies = true;
+}
+
+QCString KCookie::dcopServer()
+{
+ if (!m_bHaveDCOPCookies)
+ getICECookie();
+ return m_DCOPSrv;
+}
+
+QCString KCookie::dcopAuth()
+{
+ if (!m_bHaveDCOPCookies)
+ getICECookie();
+ return m_DCOPAuth;
+}
+
+QCString KCookie::iceAuth()
+{
+ if (!m_bHaveICECookies)
+ getICECookie();
+ return m_ICEAuth;
+}
diff --git a/kdesu/kcookie.h b/kdesu/kcookie.h
new file mode 100644
index 000000000..ed1cbf1d2
--- /dev/null
+++ b/kdesu/kcookie.h
@@ -0,0 +1,90 @@
+/* vi: ts=8 sts=4 sw=4
+ *
+ * $Id$
+ *
+ * This file is part of the KDE project, module kdesu
+ * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
+ *
+ * This is free software; you can use this library under the GNU Library
+ * General Public License, version 2. See the file "COPYING.LIB" for the
+ * exact licensing terms.
+ */
+
+#ifndef __KCookie_h_Included__
+#define __KCookie_h_Included__
+
+#include <qcstring.h>
+#include <qvaluelist.h>
+
+typedef QValueList<QCString> QCStringList;
+
+
+/**
+ * Utility class to access the authentication tokens needed to run a KDE
+ * program (X11 and DCOP cookies).
+ */
+
+class KDESU_EXPORT KCookie
+{
+public:
+ KCookie();
+
+ /**
+ * Returns the X11 display.
+ */
+ QCString display() { return m_Display; }
+
+#ifdef Q_WS_X11
+ /**
+ * Returns the X11 magic cookie, if available.
+ */
+ QCString displayAuth() { return m_DisplayAuth; }
+#endif
+
+ /**
+ * Select the DCOP transport to look for. Default: "local"
+ */
+ void setDcopTransport(const QCString &dcopTransport);
+
+ /**
+ * Returns the netid where the dcopserver is running
+ */
+ QCString dcopServer();
+
+ /**
+ * Returns a list of magic cookies for DCOP protocol authentication.
+ * The order is the same as in dcopServer().
+ */
+ QCString dcopAuth();
+
+ /**
+ * Returns a list of magic cookies for the ICE protocol.
+ */
+ QCString iceAuth();
+
+private:
+ void getXCookie();
+ void getICECookie();
+ QCStringList split(const QCString &line, char ch);
+
+ void blockSigChild();
+ void unblockSigChild();
+
+ bool m_bHaveDCOPCookies;
+ bool m_bHaveICECookies;
+
+ QCString m_Display;
+#ifdef Q_WS_X11
+ QCString m_DisplayAuth;
+#endif
+ QCString m_DCOPSrv;
+ QCString m_DCOPAuth;
+ QCString m_ICEAuth;
+ QCString m_dcopTransport;
+
+ class KCookiePrivate;
+ KCookiePrivate *d;
+};
+
+
+#endif // __KCookie_h_Included__
diff --git a/kdesu/kdesu_pty.cpp b/kdesu/kdesu_pty.cpp
new file mode 100644
index 000000000..3b0499f58
--- /dev/null
+++ b/kdesu/kdesu_pty.cpp
@@ -0,0 +1,302 @@
+/* vi: ts=8 sts=4 sw=4
+ *
+ * $Id$
+ *
+ * This file is part of the KDE project, module kdesu.
+ * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
+ *
+ * This file contains code from TEShell.C of the KDE konsole.
+ * Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
+ *
+ * This is free software; you can use this library under the GNU Library
+ * General Public License, version 2. See the file "COPYING.LIB" for the
+ * exact licensing terms.
+ *
+ * pty.cpp: Access to PTY's on different systems a la UNIX98.
+ */
+
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE /* Needed for getpt, ptsname in glibc 2.1.x systems */
+#endif
+
+#include <config.h>
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#if defined(__osf__) || defined(__CYGWIN__)
+#include <pty.h>
+#endif
+
+#include <qglobal.h>
+#include <qcstring.h>
+
+#include <kdebug.h>
+#include <kstandarddirs.h>
+#include "kdesu_pty.h"
+
+// stdlib.h is meant to declare the prototypes but doesn't :(
+#ifndef __THROW
+#define __THROW
+#endif
+
+#ifdef HAVE_GRANTPT
+extern "C" int grantpt(int fd) __THROW;
+#endif
+
+#ifdef HAVE_PTSNAME
+extern "C" char * ptsname(int fd) __THROW;
+#endif
+
+#ifdef HAVE_UNLOCKPT
+extern "C" int unlockpt(int fd) __THROW;
+#endif
+
+#ifdef HAVE__GETPTY
+extern "C" char *_getpty(int *, int, mode_t, int);
+#endif
+
+#ifdef HAVE_PTY_H
+ #include <pty.h>
+#endif
+
+#include <termios.h>
+
+#ifdef HAVE_LIBUTIL_H
+ #include <libutil.h>
+#elif defined(HAVE_UTIL_H)
+ #include <util.h>
+#endif
+
+PTY::PTY()
+{
+ ptyfd = -1;
+}
+
+PTY::~PTY()
+{
+ if (ptyfd >= 0)
+ close(ptyfd);
+}
+
+
+// Opens a pty master and returns its filedescriptor.
+
+int PTY::getpt()
+{
+
+#if defined(HAVE_GETPT) && defined(HAVE_PTSNAME)
+
+ // 1: UNIX98: preferred way
+ ptyfd = ::getpt();
+ ttyname = ::ptsname(ptyfd);
+ return ptyfd;
+
+#elif defined(HAVE_OPENPTY)
+ // 2: BSD interface
+ // More preferred than the linux hacks
+ char name[30];
+ int master_fd, slave_fd;
+ if (openpty(&master_fd, &slave_fd, name, 0L, 0L) != -1) {
+ ttyname = name;
+ name[5]='p';
+ ptyname = name;
+ close(slave_fd); // We don't need this yet // Yes, we do.
+ ptyfd = master_fd;
+ return ptyfd;
+ }
+ ptyfd = -1;
+ kdDebug(900) << k_lineinfo << "Opening pty failed.\n";
+ return -1;
+
+#elif defined(HAVE__GETPTY)
+ // 3: Irix interface
+ int master_fd;
+ ttyname = _getpty(&master_fd,O_RDWR,0600,0);
+ if (ttyname)
+ ptyfd = master_fd;
+ else{
+ ptyfd = -1;
+ kdDebug(900) << k_lineinfo << "Opening pty failed.error" << errno << '\n';
+ }
+ return ptyfd;
+
+#else
+
+ // 4: Open terminal device directly
+ // 4.1: Try /dev/ptmx first. (Linux w/ Unix98 PTYs, Solaris)
+
+ ptyfd = open("/dev/ptmx", O_RDWR);
+ if (ptyfd >= 0) {
+ ptyname = "/dev/ptmx";
+#ifdef HAVE_PTSNAME
+ ttyname = ::ptsname(ptyfd);
+ return ptyfd;
+#elif defined (TIOCGPTN)
+ int ptyno;
+ if (ioctl(ptyfd, TIOCGPTN, &ptyno) == 0) {
+ ttyname.sprintf("/dev/pts/%d", ptyno);
+ return ptyfd;
+ }
+#endif
+ close(ptyfd);
+ }
+
+ // 4.2: Try /dev/pty[p-e][0-f] (Linux w/o UNIX98 PTY's)
+
+ for (const char *c1 = "pqrstuvwxyzabcde"; *c1 != '\0'; c1++)
+ {
+ for (const char *c2 = "0123456789abcdef"; *c2 != '\0'; c2++)
+ {
+ ptyname.sprintf("/dev/pty%c%c", *c1, *c2);
+ ttyname.sprintf("/dev/tty%c%c", *c1, *c2);
+ if (access(ptyname, F_OK) < 0)
+ goto linux_out;
+ ptyfd = open(ptyname, O_RDWR);
+ if (ptyfd >= 0)
+ return ptyfd;
+ }
+ }
+linux_out:
+
+ // 4.3: Try /dev/pty%d (SCO, Unixware)
+
+ for (int i=0; i<256; i++)
+ {
+ ptyname.sprintf("/dev/ptyp%d", i);
+ ttyname.sprintf("/dev/ttyp%d", i);
+ if (access(ptyname, F_OK) < 0)
+ break;
+ ptyfd = open(ptyname, O_RDWR);
+ if (ptyfd >= 0)
+ return ptyfd;
+ }
+
+
+ // Other systems ??
+ ptyfd = -1;
+ kdDebug(900) << k_lineinfo << "Unknown system or all methods failed.\n";
+ return -1;
+
+#endif // HAVE_GETPT && HAVE_PTSNAME
+
+}
+
+
+int PTY::grantpt()
+{
+ if (ptyfd < 0)
+ return -1;
+
+#ifdef HAVE_GRANTPT
+
+ return ::grantpt(ptyfd);
+
+#elif defined(HAVE_OPENPTY)
+
+ // the BSD openpty() interface chowns the devices properly for us,
+ // no need to do this at all
+ return 0;
+
+#else
+
+ // konsole_grantpty only does /dev/pty??
+ if (ptyname.left(8) != "/dev/pty")
+ return 0;
+
+ // Use konsole_grantpty:
+ if (KStandardDirs::findExe("konsole_grantpty").isEmpty())
+ {
+ kdError(900) << k_lineinfo << "konsole_grantpty not found.\n";
+ return -1;
+ }
+
+ // As defined in konsole_grantpty.c
+ const int pty_fileno = 3;
+
+ pid_t pid;
+ if ((pid = fork()) == -1)
+ {
+ kdError(900) << k_lineinfo << "fork(): " << perror << "\n";
+ return -1;
+ }
+
+ if (pid)
+ {
+ // Parent: wait for child
+ int ret;
+ waitpid(pid, &ret, 0);
+ if (WIFEXITED(ret) && !WEXITSTATUS(ret))
+ return 0;
+ kdError(900) << k_lineinfo << "konsole_grantpty returned with error: "
+ << WEXITSTATUS(ret) << "\n";
+ return -1;
+ } else
+ {
+ // Child: exec konsole_grantpty
+ if (ptyfd != pty_fileno && dup2(ptyfd, pty_fileno) < 0)
+ _exit(1);
+ execlp("konsole_grantpty", "konsole_grantpty", "--grant", (void *)0);
+ kdError(900) << k_lineinfo << "exec(): " << perror << "\n";
+ _exit(1);
+ }
+
+ // shut up, gcc
+ return 0;
+
+#endif // HAVE_GRANTPT
+}
+
+
+/**
+ * Unlock the pty. This allows connections on the slave side.
+ */
+
+int PTY::unlockpt()
+{
+ if (ptyfd < 0)
+ return -1;
+
+#ifdef HAVE_UNLOCKPT
+
+ // (Linux w/ glibc 2.1, Solaris, ...)
+
+ return ::unlockpt(ptyfd);
+
+#elif defined(TIOCSPTLCK)
+
+ // Unlock pty (Linux w/ UNIX98 PTY's & glibc 2.0)
+ int flag = 0;
+ return ioctl(ptyfd, TIOCSPTLCK, &flag);
+
+#else
+
+ // Other systems (Linux w/o UNIX98 PTY's, ...)
+ return 0;
+
+#endif
+
+}
+
+
+/**
+ * Return the slave side name.
+ */
+
+QCString PTY::ptsname()
+{
+ if (ptyfd < 0)
+ return 0;
+
+ return ttyname;
+}
+
diff --git a/kdesu/kdesu_pty.h b/kdesu/kdesu_pty.h
new file mode 100644
index 000000000..c9eadceda
--- /dev/null
+++ b/kdesu/kdesu_pty.h
@@ -0,0 +1,71 @@
+/* vi: ts=8 sts=4 sw=4
+ *
+ * $Id$
+ *
+ * This file is part of the KDE project, module kdesu.
+ * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
+ *
+ * This is free software; you can use this library under the GNU Library
+ * General Public License, version 2. See the file "COPYING.LIB" for the
+ * exact licensing terms.
+ */
+
+
+/**
+ * PTY compatibility routines. This class tries to emulate a UNIX98 PTY API
+ * on various platforms.
+ */
+#ifndef __PTY_h_Included__
+#define __PTY_h_Included__
+
+#include <qcstring.h>
+
+#include <kdelibs_export.h>
+
+class KDESU_EXPORT PTY {
+
+public:
+ /**
+ * Construct a PTY object.
+ */
+ PTY();
+
+ /**
+ * Destructs the object. The PTY is closed if it is still open.
+ */
+ ~PTY();
+
+ /**
+ * Allocate a pty.
+ * @return A filedescriptor to the master side.
+ */
+ int getpt();
+
+ /**
+ * Grant access to the slave side.
+ * @return Zero if succesfull, < 0 otherwise.
+ */
+ int grantpt();
+
+ /**
+ * Unlock the slave side.
+ * @return Zero if successful, < 0 otherwise.
+ */
+ int unlockpt();
+
+ /**
+ * Get the slave name.
+ * @return The slave name.
+ */
+ QCString ptsname();
+
+private:
+
+ int ptyfd;
+ QCString ptyname, ttyname;
+
+ class PTYPrivate;
+ PTYPrivate *d;
+};
+
+#endif // __PTY_h_Included__
diff --git a/kdesu/kdesu_stub.c b/kdesu/kdesu_stub.c
new file mode 100644
index 000000000..8ef7f503e
--- /dev/null
+++ b/kdesu/kdesu_stub.c
@@ -0,0 +1,432 @@
+/* vi: ts=8 sts=4 sw=4
+ *
+ * $Id$
+ *
+ * This file is part of the KDE project, module kdesu.
+ * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
+ *
+ * kdesu_stub.c: KDE su executes this stub through su or ssh. This stub in turn
+ * executes the target program. Before that, startup parameters
+ * are sent through stdin.
+ *
+ *
+ * Available parameters:
+ *
+ * Parameter Description Format (csl = comma separated list)
+ *
+ * - kdesu_stub Header "ok" | "stop"
+ * - display X11 display string
+ * - display_auth X11 authentication "type cookie" pair
+ * - dcopserver KDE dcopserver csl of netids
+ * - dcop_auth DCOP authentication csl of "type cookie" pairs for DCOP
+ * - ice_auth ICE authentication csl of "type cookie" pairs for ICE
+ * - command Command to run string
+ * - path PATH env. var string
+ * - build_sycoca Rebuild sycoca? "yes" | "no"
+ * - user Target user string
+ * - priority Process priority 0 <= int <= 100
+ * - scheduler Process scheduler "fifo" | "normal"
+ * - app_startup_id DESKTOP_STARTUP_ID string
+ * - environment Additional envvars strings, last one is empty
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <pwd.h>
+#include <termios.h>
+#include <signal.h>
+
+#ifdef HAVE_INITGROUPS
+#include <grp.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#ifdef POSIX1B_SCHEDULING
+#include <sched.h>
+#endif
+
+/**
+ * Params sent by the peer.
+ */
+
+struct param_struct
+{
+ const char *name;
+ char *value;
+};
+
+struct param_struct params[] =
+{
+ { "kdesu_stub", 0L },
+ { "display", 0L },
+ { "display_auth", 0L },
+ { "dcopserver", 0L },
+ { "dcop_auth", 0L },
+ { "ice_auth", 0L },
+ { "command", 0L },
+ { "path", 0L },
+ { "xwindows_only", 0L },
+ { "user", 0L },
+ { "priority", 0L },
+ { "scheduler", 0L },
+/* obsoleted by app_startup_id { "app_start_pid", 0L } */
+ { "app_startup_id", 0L }
+};
+
+#define P_HEADER 0
+#define P_DISPLAY 1
+#define P_DISPLAY_AUTH 2
+#define P_DCOPSERVER 3
+#define P_DCOP_AUTH 4
+#define P_ICE_AUTH 5
+#define P_COMMAND 6
+#define P_PATH 7
+#define P_XWIN_ONLY 8
+#define P_USER 9
+#define P_PRIORITY 10
+#define P_SCHEDULER 11
+#define P_APP_STARTUP_ID 12
+#define P_LAST 13
+
+/* Prototypes */
+char *xmalloc(size_t);
+char *xrealloc(char *ptr, int size);
+int xsetenv(const char *name, const char *value);
+char *xstrdup(char *src);
+char **xstrsep(char *str);
+
+/**
+ * Safe malloc functions.
+ */
+char *xmalloc(size_t size)
+{
+ char *ptr = malloc(size);
+ if (ptr) return ptr;
+ perror("malloc()");
+ exit(1);
+}
+
+
+char *xrealloc(char *ptr, int size)
+{
+ ptr = realloc(ptr, size);
+ if (ptr) return ptr;
+ perror("realloc()");
+ exit(1);
+}
+
+
+/**
+ * Solaris does not have a setenv()...
+ */
+int xsetenv(const char *name, const char *value)
+{
+ char *s = malloc(strlen(name)+strlen(value)+2);
+ if (!s) return -1;
+ strcpy(s, name);
+ strcat(s, "=");
+ strcat(s, value);
+ return putenv(s); /* yes: no free()! */
+}
+
+/**
+ * Safe strdup and strip newline
+ */
+char *xstrdup(char *src)
+{
+ int len = strlen(src);
+ char *dst = xmalloc(len+1);
+ strcpy(dst, src);
+ if (dst[len-1] == '\n')
+ dst[len-1] = '\000';
+ return dst;
+}
+
+/**
+ * Split comma separated list.
+ */
+char **xstrsep(char *str)
+{
+ int i = 0, size = 10;
+ char **list = (char **) xmalloc(size * sizeof(char *));
+ char *ptr = str, *nptr;
+ while ((nptr = strchr(ptr, ',')) != 0L)
+ {
+ if (i > size-2)
+ list = realloc(list, (size *= 2) * sizeof(char *));
+ *nptr = '\000';
+ list[i++] = ptr;
+ ptr = nptr+1;
+ }
+ if (*ptr != '\000')
+ list[i++] = ptr;
+ list[i] = 0L;
+ return list;
+}
+
+#define BUFSIZE 8192
+
+/**
+ * The main program
+ */
+
+int main()
+{
+ char buf[BUFSIZE+1];
+#ifndef QWS
+ char xauthority[200];
+#endif
+ char iceauthority[200];
+ char **host, **auth;
+ int i/*, res, sycoca*/, prio;
+ pid_t pid;
+ FILE *fout;
+ struct passwd *pw;
+ const char* kdesu_lc_all;
+
+ /* Get startup parameters. */
+
+ for (i=0; i<P_LAST; i++)
+ {
+ printf("%s\n", params[i].name);
+ fflush(stdout);
+ if (fgets(buf, BUFSIZE, stdin) == 0L)
+ {
+ printf("end\n"); fflush(stdout);
+ perror("kdesu_stub: fgets()");
+ exit(1);
+ }
+ params[i].value = xstrdup(buf);
+ /* Installation check? */
+ if ((i == 0) && !strcmp(params[i].value, "stop"))
+ {
+ printf("end\n");
+ exit(0);
+ }
+ }
+ printf("environment\n");
+ fflush(stdout);
+ for(;;)
+ {
+ char* tmp;
+ if (fgets(buf, BUFSIZE, stdin) == 0L)
+ {
+ printf("end\n"); fflush(stdout);
+ perror("kdesu_stub: fgets()");
+ exit(1);
+ }
+ tmp = xstrdup( buf );
+ if( tmp[ 0 ] == '\0' ) /* terminator */
+ break;
+ putenv( xstrdup( buf ));
+ }
+
+ printf("end\n");
+ fflush(stdout);
+
+ xsetenv("PATH", params[P_PATH].value);
+ xsetenv("DESKTOP_STARTUP_ID", params[P_APP_STARTUP_ID].value);
+
+ kdesu_lc_all = getenv( "KDESU_LC_ALL" );
+ if( kdesu_lc_all != NULL )
+ xsetenv("LC_ALL",kdesu_lc_all);
+ else
+ unsetenv("LC_ALL");
+
+ /* Do we need to change uid? */
+
+ pw = getpwnam(params[P_USER].value);
+ if (pw == 0L)
+ {
+ printf("kdesu_stub: user %s does not exist!\n", params[P_USER].value);
+ exit(1);
+ }
+ xsetenv("HOME", pw->pw_dir);
+
+ /* Set scheduling/priority */
+
+ prio = atoi(params[P_PRIORITY].value);
+ if (!strcmp(params[P_SCHEDULER].value, "realtime"))
+ {
+#ifdef POSIX1B_SCHEDULING
+ struct sched_param sched;
+ int min = sched_get_priority_min(SCHED_FIFO);
+ int max = sched_get_priority_max(SCHED_FIFO);
+ sched.sched_priority = min + (int) (((double) prio) * (max - min) / 100 + 0.5);
+ sched_setscheduler(0, SCHED_FIFO, &sched);
+#else
+ printf("kdesu_stub: realtime scheduling not supported\n");
+#endif
+ } else
+ {
+#ifdef HAVE_SETPRIORITY
+ int val = 20 - (int) (((double) prio) * 40 / 100 + 0.5);
+ setpriority(PRIO_PROCESS, getpid(), val);
+#endif
+ }
+
+ /* Drop privileges (this is permanent) */
+
+ if (getuid() != pw->pw_uid)
+ {
+ if (setgid(pw->pw_gid) == -1)
+ {
+ perror("kdesu_stub: setgid()");
+ exit(1);
+ }
+#ifdef HAVE_INITGROUPS
+ if (initgroups(pw->pw_name, pw->pw_gid) == -1)
+ {
+ perror("kdesu_stub: initgroups()");
+ exit(1);
+ }
+#endif
+ if (setuid(pw->pw_uid) == -1)
+ {
+ perror("kdesu_stub: setuid()");
+ exit(1);
+ }
+ xsetenv("HOME", pw->pw_dir);
+ }
+
+ /* Handle display */
+
+ if (strcmp(params[P_DISPLAY].value, "no"))
+ {
+#ifndef QWS
+ xsetenv("DISPLAY", params[P_DISPLAY].value);
+ if (params[P_DISPLAY_AUTH].value[0])
+ {
+ int fd2;
+ /*
+ ** save umask and set to 077, so we create those files only
+ ** readable for root. (if someone else could read them, we
+ ** are in deep shit).
+ */
+ int oldumask = umask(077);
+ const char *disp = params[P_DISPLAY].value;
+ if (strncmp(disp, "localhost:", 10) == 0)
+ disp += 9;
+
+ strcpy(xauthority, "/tmp/xauth.XXXXXXXXXX");
+ fd2 = mkstemp(xauthority);
+ umask(oldumask);
+
+ if (fd2 == -1) {
+ perror("kdesu_stub: mkstemp()");
+ exit(1);
+ } else
+ close(fd2);
+ xsetenv("XAUTHORITY", xauthority);
+
+ fout = popen("xauth >/dev/null 2>&1","w");
+ if (fout == NULL)
+ {
+ perror("kdesu_stub: popen(xauth)");
+ exit(1);
+ }
+ fprintf(fout, "add %s %s\n", disp,
+ params[P_DISPLAY_AUTH].value);
+ pclose(fout);
+ }
+#else
+ xsetenv("DISPLAY", params[P_DISPLAY].value);
+#endif
+ }
+
+
+ /* Handle DCOP */
+
+ if (strcmp(params[P_DCOPSERVER].value, "no"))
+ {
+ xsetenv("DCOPSERVER", params[P_DCOPSERVER].value);
+ host = xstrsep(params[P_DCOPSERVER].value);
+ auth = xstrsep(params[P_ICE_AUTH].value);
+ if (host[0])
+ {
+ int fd;
+ int oldumask = umask(077);
+
+ strcpy(iceauthority, "/tmp/iceauth.XXXXXXXXXX");
+ fd = mkstemp(iceauthority);
+ umask(oldumask);
+ if (fd == -1) {
+ perror("kdesu_stub: mkstemp()");
+ exit(1);
+ } else
+ close(fd);
+ xsetenv("ICEAUTHORITY", iceauthority);
+
+ fout = popen("iceauth >/dev/null 2>&1", "w");
+ if (!fout) {
+ perror("kdesu_stub: popen iceauth");
+ exit(1);
+ }
+ for (i=0; host[i]; i++)
+ fprintf(fout, "add ICE \"\" %s %s\n", host[i], auth[i]);
+ auth = xstrsep(params[P_DCOP_AUTH].value);
+ for (i=0; host[i]; i++)
+ fprintf(fout, "add DCOP \"\" %s %s\n", host[i], auth[i]);
+ pclose(fout);
+ }
+ }
+
+ /* Rebuild the sycoca and start kdeinit? */
+
+ if (strcmp(params[P_XWIN_ONLY].value, "no"))
+ {
+ system("kdeinit --suicide");
+ }
+
+ /* Execute the command */
+
+ pid = fork();
+ if (pid == -1)
+ {
+ perror("kdesu_stub: fork()");
+ exit(1);
+ }
+ if (pid)
+ {
+ /* Parent: wait for child, delete tempfiles and return. */
+ int ret, state, xit = 1;
+ while (1)
+ {
+ ret = waitpid(pid, &state, 0);
+ if (ret == -1)
+ {
+ if (errno == EINTR)
+ continue;
+ if (errno != ECHILD)
+ perror("kdesu_stub: waitpid()");
+ break;
+ }
+ if (WIFEXITED(state))
+ xit = WEXITSTATUS(state);
+ }
+
+#ifndef QWS
+ unlink(xauthority);
+#endif
+ unlink(iceauthority);
+ exit(xit);
+ } else
+ {
+ setsid();
+ /* Child: exec command. */
+ sprintf(buf, "%s", params[P_COMMAND].value);
+ execl("/bin/sh", "sh", "-c", buf, (void *)0);
+ perror("kdesu_stub: exec()");
+ _exit(1);
+ }
+}
diff --git a/kdesu/libkdesu.nmcheck b/kdesu/libkdesu.nmcheck
new file mode 100644
index 000000000..11712f7d6
--- /dev/null
+++ b/kdesu/libkdesu.nmcheck
@@ -0,0 +1,10 @@
+# KDE namespace check file
+
+# kdesu classes
+SshProcess:*
+PTY::*
+SuProcess::*
+StubProcess::*
+PtyProcess::*
+KCookie::*
+KDEsuClient::*
diff --git a/kdesu/libkdesu_weak.nmcheck b/kdesu/libkdesu_weak.nmcheck
new file mode 100644
index 000000000..6fa8e9176
--- /dev/null
+++ b/kdesu/libkdesu_weak.nmcheck
@@ -0,0 +1,2 @@
+# KDE namespace check file
+
diff --git a/kdesu/process.cpp b/kdesu/process.cpp
new file mode 100644
index 000000000..bb09c309f
--- /dev/null
+++ b/kdesu/process.cpp
@@ -0,0 +1,626 @@
+/* vi: ts=8 sts=4 sw=4
+ *
+ * $Id$
+ *
+ * This file is part of the KDE project, module kdesu.
+ * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
+ *
+ * This file contains code from TEShell.C of the KDE konsole.
+ * Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
+ *
+ * This is free software; you can use this library under the GNU Library
+ * General Public License, version 2. See the file "COPYING.LIB" for the
+ * exact licensing terms.
+ *
+ * process.cpp: Functionality to build a front end to password asking
+ * terminal programs.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+#include <termios.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/ioctl.h>
+
+#if defined(__SVR4) && defined(sun)
+#include <stropts.h>
+#include <sys/stream.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h> // Needed on some systems.
+#endif
+
+#include <qglobal.h>
+#include <qcstring.h>
+#include <qfile.h>
+
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kstandarddirs.h>
+
+#include "process.h"
+#include "kdesu_pty.h"
+#include "kcookie.h"
+
+int PtyProcess::waitMS(int fd,int ms)
+{
+ struct timeval tv;
+ tv.tv_sec = 0;
+ tv.tv_usec = 1000*ms;
+
+ fd_set fds;
+ FD_ZERO(&fds);
+ FD_SET(fd,&fds);
+ return select(fd+1, &fds, 0L, 0L, &tv);
+}
+
+/*
+** Basic check for the existence of @p pid.
+** Returns true iff @p pid is an extant process.
+*/
+bool PtyProcess::checkPid(pid_t pid)
+{
+ KConfig* config = KGlobal::config();
+ config->setGroup("super-user-command");
+ QString superUserCommand = config->readEntry("super-user-command", DEFAULT_SUPER_USER_COMMAND);
+ //sudo does not accept signals from user so we except it
+ if (superUserCommand == "sudo") {
+ return true;
+ } else {
+ return kill(pid,0) == 0;
+ }
+}
+
+/*
+** Check process exit status for process @p pid.
+** On error (no child, no exit), return Error (-1).
+** If child @p pid has exited, return its exit status,
+** (which may be zero).
+** If child @p has not exited, return NotExited (-2).
+*/
+
+int PtyProcess::checkPidExited(pid_t pid)
+{
+ int state, ret;
+ ret = waitpid(pid, &state, WNOHANG);
+
+ if (ret < 0)
+ {
+ kdError(900) << k_lineinfo << "waitpid(): " << perror << "\n";
+ return Error;
+ }
+ if (ret == pid)
+ {
+ if (WIFEXITED(state))
+ return WEXITSTATUS(state);
+ return Killed;
+ }
+
+ return NotExited;
+}
+
+
+class PtyProcess::PtyProcessPrivate
+{
+public:
+ QCStringList env;
+};
+
+
+PtyProcess::PtyProcess()
+{
+ m_bTerminal = false;
+ m_bErase = false;
+ m_pPTY = 0L;
+ d = new PtyProcessPrivate;
+}
+
+
+int PtyProcess::init()
+{
+ delete m_pPTY;
+ m_pPTY = new PTY();
+ m_Fd = m_pPTY->getpt();
+ if (m_Fd < 0)
+ return -1;
+ if ((m_pPTY->grantpt() < 0) || (m_pPTY->unlockpt() < 0))
+ {
+ kdError(900) << k_lineinfo << "Master setup failed.\n";
+ m_Fd = -1;
+ return -1;
+ }
+ m_TTY = m_pPTY->ptsname();
+ m_Inbuf.resize(0);
+ return 0;
+}
+
+
+PtyProcess::~PtyProcess()
+{
+ delete m_pPTY;
+ delete d;
+}
+
+/** Set additinal environment variables. */
+void PtyProcess::setEnvironment( const QCStringList &env )
+{
+ d->env = env;
+}
+
+const QCStringList& PtyProcess::environment() const
+{
+ return d->env;
+}
+
+/*
+ * Read one line of input. The terminal is in canonical mode, so you always
+ * read a line at at time, but it's possible to receive multiple lines in
+ * one time.
+ */
+
+QCString PtyProcess::readLine(bool block)
+{
+ int pos;
+ QCString ret;
+
+ if (!m_Inbuf.isEmpty())
+ {
+ pos = m_Inbuf.find('\n');
+ if (pos == -1)
+ {
+ ret = m_Inbuf;
+ m_Inbuf.resize(0);
+ } else
+ {
+ ret = m_Inbuf.left(pos);
+ m_Inbuf = m_Inbuf.mid(pos+1);
+ }
+ return ret;
+ }
+
+ int flags = fcntl(m_Fd, F_GETFL);
+ if (flags < 0)
+ {
+ kdError(900) << k_lineinfo << "fcntl(F_GETFL): " << perror << "\n";
+ return ret;
+ }
+ int oflags = flags;
+ if (block)
+ flags &= ~O_NONBLOCK;
+ else
+ flags |= O_NONBLOCK;
+
+ if ((flags != oflags) && (fcntl(m_Fd, F_SETFL, flags) < 0))
+ {
+ // We get an error here when the child process has closed
+ // the file descriptor already.
+ return ret;
+ }
+
+ int nbytes;
+ char buf[256];
+ while (1)
+ {
+ nbytes = read(m_Fd, buf, 255);
+ if (nbytes == -1)
+ {
+ if (errno == EINTR)
+ continue;
+ else break;
+ }
+ if (nbytes == 0)
+ break; // eof
+
+ buf[nbytes] = '\000';
+ m_Inbuf += buf;
+
+ pos = m_Inbuf.find('\n');
+ if (pos == -1)
+ {
+ ret = m_Inbuf;
+ m_Inbuf.resize(0);
+ } else
+ {
+ ret = m_Inbuf.left(pos);
+ m_Inbuf = m_Inbuf.mid(pos+1);
+ }
+ break;
+ }
+
+ return ret;
+}
+
+QCString PtyProcess::readAll(bool block)
+{
+ QCString ret;
+
+ if (!m_Inbuf.isEmpty())
+ {
+ // if there is still something in the buffer, we need not block.
+ // we should still try to read any further output, from the fd, though.
+ block = false;
+ ret = m_Inbuf;
+ m_Inbuf.resize(0);
+ }
+
+ int flags = fcntl(m_Fd, F_GETFL);
+ if (flags < 0)
+ {
+ kdError(900) << k_lineinfo << "fcntl(F_GETFL): " << perror << "\n";
+ return ret;
+ }
+ int oflags = flags;
+ if (block)
+ flags &= ~O_NONBLOCK;
+ else
+ flags |= O_NONBLOCK;
+
+ if ((flags != oflags) && (fcntl(m_Fd, F_SETFL, flags) < 0))
+ {
+ // We get an error here when the child process has closed
+ // the file descriptor already.
+ return ret;
+ }
+
+ int nbytes;
+ char buf[256];
+ while (1)
+ {
+ nbytes = read(m_Fd, buf, 255);
+ if (nbytes == -1)
+ {
+ if (errno == EINTR)
+ continue;
+ else break;
+ }
+ if (nbytes == 0)
+ break; // eof
+
+ buf[nbytes] = '\000';
+ ret += buf;
+ break;
+ }
+
+ return ret;
+}
+
+
+void PtyProcess::writeLine(const QCString &line, bool addnl)
+{
+ if (!line.isEmpty())
+ write(m_Fd, line, line.length());
+ if (addnl)
+ write(m_Fd, "\n", 1);
+}
+
+
+void PtyProcess::unreadLine(const QCString &line, bool addnl)
+{
+ QCString tmp = line;
+ if (addnl)
+ tmp += '\n';
+ if (!tmp.isEmpty())
+ m_Inbuf.prepend(tmp);
+}
+
+/*
+ * Fork and execute the command. This returns in the parent.
+ */
+
+int PtyProcess::exec(const QCString &command, const QCStringList &args)
+{
+ kdDebug(900) << k_lineinfo << "Running `" << command << "'\n";
+
+ if (init() < 0)
+ return -1;
+
+ // Open the pty slave before forking. See SetupTTY()
+ int slave = open(m_TTY, O_RDWR);
+ if (slave < 0)
+ {
+ kdError(900) << k_lineinfo << "Could not open slave pty.\n";
+ return -1;
+ }
+
+ if ((m_Pid = fork()) == -1)
+ {
+ kdError(900) << k_lineinfo << "fork(): " << perror << "\n";
+ return -1;
+ }
+
+ // Parent
+ if (m_Pid)
+ {
+ close(slave);
+ return 0;
+ }
+
+ // Child
+ if (SetupTTY(slave) < 0)
+ _exit(1);
+
+ for(QCStringList::ConstIterator it = d->env.begin();
+ it != d->env.end(); it++)
+ {
+ putenv((*it).data());
+ }
+ unsetenv("KDE_FULL_SESSION");
+
+ // set temporarily LC_ALL to C, for su (to be able to parse "Password:")
+ const char* old_lc_all = getenv( "LC_ALL" );
+ if( old_lc_all != NULL )
+ setenv( "KDESU_LC_ALL", old_lc_all, 1 );
+ else
+ unsetenv( "KDESU_LC_ALL" );
+ setenv("LC_ALL", "C", 1);
+
+ // From now on, terminal output goes through the tty.
+
+ QCString path;
+ if (command.contains('/'))
+ path = command;
+ else
+ {
+ QString file = KStandardDirs::findExe(command);
+ if (file.isEmpty())
+ {
+ kdError(900) << k_lineinfo << command << " not found\n";
+ _exit(1);
+ }
+ path = QFile::encodeName(file);
+ }
+
+ const char **argp = (const char **)malloc((args.count()+2)*sizeof(char *));
+ int i = 0;
+ argp[i++] = path;
+ for (QCStringList::ConstIterator it=args.begin(); it!=args.end(); ++it)
+ argp[i++] = *it;
+
+ argp[i] = 0L;
+
+ execv(path, (char * const *)argp);
+ kdError(900) << k_lineinfo << "execv(\"" << path << "\"): " << perror << "\n";
+ _exit(1);
+ return -1; // Shut up compiler. Never reached.
+}
+
+
+/*
+ * Wait until the terminal is set into no echo mode. At least one su
+ * (RH6 w/ Linux-PAM patches) sets noecho mode AFTER writing the Password:
+ * prompt, using TCSAFLUSH. This flushes the terminal I/O queues, possibly
+ * taking the password with it. So we wait until no echo mode is set
+ * before writing the password.
+ * Note that this is done on the slave fd. While Linux allows tcgetattr() on
+ * the master side, Solaris doesn't.
+ */
+
+int PtyProcess::WaitSlave()
+{
+ int slave = open(m_TTY, O_RDWR);
+ if (slave < 0)
+ {
+ kdError(900) << k_lineinfo << "Could not open slave tty.\n";
+ return -1;
+ }
+
+ kdDebug(900) << k_lineinfo << "Child pid " << m_Pid << endl;
+
+ struct termios tio;
+ while (1)
+ {
+ if (!checkPid(m_Pid))
+ {
+ close(slave);
+ return -1;
+ }
+ if (tcgetattr(slave, &tio) < 0)
+ {
+ kdError(900) << k_lineinfo << "tcgetattr(): " << perror << "\n";
+ close(slave);
+ return -1;
+ }
+ if (tio.c_lflag & ECHO)
+ {
+ kdDebug(900) << k_lineinfo << "Echo mode still on.\n";
+ waitMS(slave,100);
+ continue;
+ }
+ break;
+ }
+ close(slave);
+ return 0;
+}
+
+
+int PtyProcess::enableLocalEcho(bool enable)
+{
+ int slave = open(m_TTY, O_RDWR);
+ if (slave < 0)
+ {
+ kdError(900) << k_lineinfo << "Could not open slave tty.\n";
+ return -1;
+ }
+ struct termios tio;
+ if (tcgetattr(slave, &tio) < 0)
+ {
+ kdError(900) << k_lineinfo << "tcgetattr(): " << perror << "\n";
+ close(slave); return -1;
+ }
+ if (enable)
+ tio.c_lflag |= ECHO;
+ else
+ tio.c_lflag &= ~ECHO;
+ if (tcsetattr(slave, TCSANOW, &tio) < 0)
+ {
+ kdError(900) << k_lineinfo << "tcsetattr(): " << perror << "\n";
+ close(slave); return -1;
+ }
+ close(slave);
+ return 0;
+}
+
+
+/*
+ * Copy output to stdout until the child process exists, or a line of output
+ * matches `m_Exit'.
+ * We have to use waitpid() to test for exit. Merely waiting for EOF on the
+ * pty does not work, because the target process may have children still
+ * attached to the terminal.
+ */
+
+int PtyProcess::waitForChild()
+{
+ int retval = 1;
+
+ fd_set fds;
+ FD_ZERO(&fds);
+
+ while (1)
+ {
+ FD_SET(m_Fd, &fds);
+ int ret = select(m_Fd+1, &fds, 0L, 0L, 0L);
+ if (ret == -1)
+ {
+ if (errno != EINTR)
+ {
+ kdError(900) << k_lineinfo << "select(): " << perror << "\n";
+ return -1;
+ }
+ ret = 0;
+ }
+
+ if (ret)
+ {
+ QCString output = readAll(false);
+ bool lineStart = true;
+ while (!output.isNull())
+ {
+ if (!m_Exit.isEmpty())
+ {
+ // match exit string only at line starts
+ int pos = output.find(m_Exit);
+ if ((pos >= 0) && ((pos == 0 && lineStart) || (output.at (pos - 1) == '\n')))
+ {
+ kill(m_Pid, SIGTERM);
+ }
+ }
+ if (m_bTerminal)
+ {
+ fputs(output, stdout);
+ fflush(stdout);
+ }
+ lineStart = output.at( output.length() - 1 ) == '\n';
+ output = readAll(false);
+ }
+ }
+
+ ret = checkPidExited(m_Pid);
+ if (ret == Error)
+ {
+ if (errno == ECHILD) retval = 0;
+ else retval = 1;
+ break;
+ }
+ else if (ret == Killed)
+ {
+ retval = 0;
+ break;
+ }
+ else if (ret == NotExited)
+ {
+ // keep checking
+ }
+ else
+ {
+ retval = ret;
+ break;
+ }
+ }
+ return retval;
+}
+
+/*
+ * SetupTTY: Creates a new session. The filedescriptor "fd" should be
+ * connected to the tty. It is closed after the tty is reopened to make it
+ * our controlling terminal. This way the tty is always opened at least once
+ * so we'll never get EIO when reading from it.
+ */
+
+int PtyProcess::SetupTTY(int fd)
+{
+ // Reset signal handlers
+ for (int sig = 1; sig < NSIG; sig++)
+ signal(sig, SIG_DFL);
+ signal(SIGHUP, SIG_IGN);
+
+ // Close all file handles
+ struct rlimit rlp;
+ getrlimit(RLIMIT_NOFILE, &rlp);
+ for (int i = 0; i < (int)rlp.rlim_cur; i++)
+ if (i != fd) close(i);
+
+ // Create a new session.
+ setsid();
+
+ // Open slave. This will make it our controlling terminal
+ int slave = open(m_TTY, O_RDWR);
+ if (slave < 0)
+ {
+ kdError(900) << k_lineinfo << "Could not open slave side: " << perror << "\n";
+ return -1;
+ }
+ close(fd);
+
+#if defined(__SVR4) && defined(sun)
+
+ // Solaris STREAMS environment.
+ // Push these modules to make the stream look like a terminal.
+ ioctl(slave, I_PUSH, "ptem");
+ ioctl(slave, I_PUSH, "ldterm");
+
+#endif
+
+#ifdef TIOCSCTTY
+ ioctl(slave, TIOCSCTTY, NULL);
+#endif
+
+ // Connect stdin, stdout and stderr
+ dup2(slave, 0); dup2(slave, 1); dup2(slave, 2);
+ if (slave > 2)
+ close(slave);
+
+ // Disable OPOST processing. Otherwise, '\n' are (on Linux at least)
+ // translated to '\r\n'.
+ struct termios tio;
+ if (tcgetattr(0, &tio) < 0)
+ {
+ kdError(900) << k_lineinfo << "tcgetattr(): " << perror << "\n";
+ return -1;
+ }
+ tio.c_oflag &= ~OPOST;
+ if (tcsetattr(0, TCSANOW, &tio) < 0)
+ {
+ kdError(900) << k_lineinfo << "tcsetattr(): " << perror << "\n";
+ return -1;
+ }
+
+ return 0;
+}
+
+void PtyProcess::virtual_hook( int, void* )
+{ /*BASE::virtual_hook( id, data );*/ }
diff --git a/kdesu/process.h b/kdesu/process.h
new file mode 100644
index 000000000..8a2004d92
--- /dev/null
+++ b/kdesu/process.h
@@ -0,0 +1,188 @@
+/* vi: ts=8 sts=4 sw=4
+ *
+ * $Id$
+ *
+ * This file is part of the KDE project, module kdesu.
+ * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
+ *
+ * This is free software; you can use this library under the GNU Library
+ * General Public License, version 2. See the file "COPYING.LIB" for the
+ * exact licensing terms.
+ */
+
+#ifndef __Process_h_Included__
+#define __Process_h_Included__
+
+#include <sys/types.h>
+
+#include <qcstring.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qvaluelist.h>
+
+#include <kdelibs_export.h>
+
+class PTY;
+typedef QValueList<QCString> QCStringList;
+
+/**
+ * Synchronous communication with tty programs.
+ *
+ * PtyProcess provides synchronous communication with tty based programs.
+ * The communications channel used is a pseudo tty (as opposed to a pipe)
+ * This means that programs which require a terminal will work.
+ */
+
+class KDESU_EXPORT PtyProcess
+{
+public:
+ PtyProcess();
+ virtual ~PtyProcess();
+
+ /**
+ * Forks off and execute a command. The command's standard in and output
+ * are connected to the pseudo tty. They are accessible with readLine
+ * and writeLine.
+ * @param command The command to execute.
+ * @param args The arguments to the command.
+ */
+ int exec(const QCString &command, const QCStringList &args);
+
+ /**
+ * Reads a line from the program's standard out. Depending on the @em block
+ * parameter, this call blocks until a single, full line is read.
+ * @param block Block until a full line is read?
+ * @return The output string.
+ */
+ QCString readLine(bool block=true);
+ /**
+ * Read all available output from the program's standard out.
+ * @param block If no output is in the buffer, should the function block
+ * @return The output.
+ */
+ QCString readAll(bool block=true);
+
+ /**
+ * Writes a line of text to the program's standard in.
+ * @param line The text to write.
+ * @param addNewline Adds a '\n' to the line.
+ */
+ void writeLine(const QCString &line, bool addNewline=true);
+
+ /**
+ * Puts back a line of input.
+ * @param line The line to put back.
+ * @param addNewline Adds a '\n' to the line.
+ */
+ void unreadLine(const QCString &line, bool addNewline=true);
+
+ /**
+ * Sets the exit string. If a line of program output matches this,
+ * waitForChild() will terminate the program and return.
+ */
+ void setExitString(const QCString &exit) { m_Exit = exit; }
+
+ /**
+ * Waits for the child to exit. See also setExitString.
+ */
+ int waitForChild();
+
+ /**
+ * Waits until the pty has cleared the ECHO flag. This is useful
+ * when programs write a password prompt before they disable ECHO.
+ * Disabling it might flush any input that was written.
+ */
+ int WaitSlave();
+
+ /**
+ * Enables/disables local echo on the pseudo tty.
+ */
+ int enableLocalEcho(bool enable=true);
+
+ /**
+ * Enables/disables terminal output. Relevant only to some subclasses.
+ */
+ void setTerminal(bool terminal) { m_bTerminal = terminal; }
+
+ /**
+ * Overwrites the password as soon as it is used. Relevant only to
+ * some subclasses.
+ */
+ void setErase(bool erase) { m_bErase = erase; }
+
+ /**
+ * Set additinal environment variables.
+ */
+ void setEnvironment( const QCStringList &env );
+
+ /**
+ * Returns the filedescriptor of the process.
+ */
+ int fd() {return m_Fd;}
+
+ /**
+ * Returns the pid of the process.
+ */
+ int pid() {return m_Pid;}
+
+public: /* static */
+ /*
+ ** This is a collection of static functions that can be
+ ** used for process control inside kdesu. I'd suggest
+ ** against using this publicly. There are probably
+ ** nicer Qt based ways to do what you want.
+ */
+
+ /**
+ ** Wait @p ms miliseconds (ie. 1/10th of a second is 100ms),
+ ** using @p fd as a filedescriptor to wait on. Returns
+ ** select(2)'s result, which is -1 on error, 0 on timeout,
+ ** or positive if there is data on one of the selected fd's.
+ **
+ ** @p ms must be in the range 0..999 (ie. the maximum wait
+ ** duration is 999ms, almost one second).
+ */
+ static int waitMS(int fd,int ms);
+
+
+ /**
+ ** Basic check for the existence of @p pid.
+ ** Returns true iff @p pid is an extant process,
+ ** (one you could kill - see man kill(2) for signal 0).
+ */
+ static bool checkPid(pid_t pid);
+
+ /**
+ ** Check process exit status for process @p pid.
+ ** On error (no child, no exit), return -1.
+ ** If child @p pid has exited, return its exit status,
+ ** (which may be zero).
+ ** If child @p has not exited, return -2.
+ */
+ enum checkPidStatus { Error=-1, NotExited=-2, Killed=-3 } ;
+ static int checkPidExited(pid_t pid);
+
+
+protected:
+ const QCStringList& environment() const;
+
+ bool m_bErase, m_bTerminal;
+ int m_Pid, m_Fd;
+ QCString m_Command, m_Exit;
+
+private:
+ int init();
+ int SetupTTY(int fd);
+
+ PTY *m_pPTY;
+ QCString m_Inbuf, m_TTY;
+
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ class PtyProcessPrivate;
+ PtyProcessPrivate *d;
+};
+
+
+#endif
diff --git a/kdesu/ssh.cpp b/kdesu/ssh.cpp
new file mode 100644
index 000000000..a325fb62e
--- /dev/null
+++ b/kdesu/ssh.cpp
@@ -0,0 +1,279 @@
+/* vi: ts=8 sts=4 sw=4
+*
+* $Id$
+*
+* This file is part of the KDE project, module kdesu.
+* Copyright (C) 2000 Geert Jansen <jansen@kde.org>
+*
+* This is free software; you can use this library under the GNU Library
+* General Public License, version 2. See the file "COPYING.LIB" for the
+* exact licensing terms.
+*
+* ssh.cpp: Execute a program on a remote machine using ssh.
+*/
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <signal.h>
+#include <time.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <qglobal.h>
+#include <qcstring.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+
+#include "ssh.h"
+#include "kcookie.h"
+
+
+SshProcess::SshProcess(const QCString &host, const QCString &user, const QCString &command)
+{
+ m_Host = host;
+ m_User = user;
+ m_Command = command;
+ m_Stub = "kdesu_stub";
+ srand(time(0L));
+}
+
+
+SshProcess::~SshProcess()
+{
+}
+
+
+void SshProcess::setStub(const QCString &stub)
+{
+ m_Stub = stub;
+}
+
+
+int SshProcess::checkInstall(const char *password)
+{
+ return exec(password, 1);
+}
+
+
+int SshProcess::checkNeedPassword()
+{
+ return exec(0L, 2);
+}
+
+
+int SshProcess::exec(const char *password, int check)
+{
+ if (check)
+ setTerminal(true);
+
+ QCStringList args;
+ args += "-l"; args += m_User;
+ args += "-o"; args += "StrictHostKeyChecking=no";
+ args += m_Host; args += m_Stub;
+
+ if (StubProcess::exec("ssh", args) < 0)
+ {
+ return check ? SshNotFound : -1;
+ }
+
+ int ret = ConverseSsh(password, check);
+ if (ret < 0)
+ {
+ if (!check)
+ kdError(900) << k_lineinfo << "Conversation with ssh failed\n";
+ return ret;
+ }
+ if (check == 2)
+ {
+ if (ret == 1)
+ {
+ kill(m_Pid, SIGTERM);
+ waitForChild();
+ }
+ return ret;
+ }
+
+ if (m_bErase && password)
+ {
+ char *ptr = const_cast<char *>(password);
+ const uint plen = strlen(password);
+ for (unsigned i=0; i < plen; i++)
+ ptr[i] = '\000';
+ }
+
+ ret = ConverseStub(check);
+ if (ret < 0)
+ {
+ if (!check)
+ kdError(900) << k_lineinfo << "Converstation with kdesu_stub failed\n";
+ return ret;
+ }
+ else if (ret == 1)
+ {
+ kill(m_Pid, SIGTERM);
+ waitForChild();
+ ret = SshIncorrectPassword;
+ }
+
+ if (check == 1)
+ {
+ waitForChild();
+ return 0;
+ }
+
+ setExitString("Waiting for forwarded connections to terminate");
+ ret = waitForChild();
+ return ret;
+}
+
+/*
+* Create a port forwarding for DCOP. For the remote port, we take a pseudo
+* random number between 10k and 50k. This is not ok, of course, but I see
+* no other way. There is, afaik, no security issue involved here. If the port
+* happens to be occupied, ssh will refuse to start.
+*
+* 14/SEP/2000: DCOP forwarding is not used anymore.
+*/
+
+QCString SshProcess::dcopForward()
+{
+ QCString result;
+
+ setDcopTransport("tcp");
+
+ QCString srv = StubProcess::dcopServer();
+ if (srv.isEmpty())
+ return result;
+
+ int i = srv.find('/');
+ if (i == -1)
+ return result;
+ if (srv.left(i) != "tcp")
+ return result;
+ int j = srv.find(':', ++i);
+ if (j == -1)
+ return result;
+ QCString host = srv.mid(i, j-i);
+ bool ok;
+ int port = srv.mid(++j).toInt(&ok);
+ if (!ok)
+ return result;
+
+ m_dcopPort = 10000 + (int) ((40000.0 * rand()) / (1.0 + RAND_MAX));
+ result.sprintf("%d:%s:%d", m_dcopPort, host.data(), port);
+ return result;
+}
+
+
+/*
+* Conversation with ssh.
+* If check is 0, this waits for either a "Password: " prompt,
+* or the header of the stub. If a prompt is received, the password is
+* written back. Used for running a command.
+* If check is 1, operation is the same as 0 except that if a stub header is
+* received, the stub is stopped with the "stop" command. This is used for
+* checking a password.
+* If check is 2, operation is the same as 1, except that no password is
+* written. The prompt is saved to m_Prompt. Used for checking the need for
+* a password.
+*/
+
+int SshProcess::ConverseSsh(const char *password, int check)
+{
+ unsigned i, j, colon;
+
+ QCString line;
+ int state = 0;
+
+ while (state < 2)
+ {
+ line = readLine();
+ const uint len = line.length();
+ if (line.isNull())
+ return -1;
+
+ switch (state) {
+ case 0:
+ // Check for "kdesu_stub" header.
+ if (line == "kdesu_stub")
+ {
+ unreadLine(line);
+ return 0;
+ }
+
+ // Match "Password: " with the regex ^[^:]+:[\w]*$.
+ for (i=0,j=0,colon=0; i<len; i++)
+ {
+ if (line[i] == ':')
+ {
+ j = i; colon++;
+ continue;
+ }
+ if (!isspace(line[i]))
+ j++;
+ }
+ if ((colon == 1) && (line[j] == ':'))
+ {
+ if (check == 2)
+ {
+ m_Prompt = line;
+ return SshNeedsPassword;
+ }
+ WaitSlave();
+ write(m_Fd, password, strlen(password));
+ write(m_Fd, "\n", 1);
+ state++;
+ break;
+ }
+
+ // Warning/error message.
+ m_Error += line; m_Error += "\n";
+ if (m_bTerminal)
+ fprintf(stderr, "ssh: %s\n", line.data());
+ break;
+
+ case 1:
+ if (line.isEmpty())
+ {
+ state++;
+ break;
+ }
+ return -1;
+ }
+ }
+ return 0;
+}
+
+
+// Display redirection is handled by ssh natively.
+QCString SshProcess::display()
+{
+ return "no";
+}
+
+
+QCString SshProcess::displayAuth()
+{
+ return "no";
+}
+
+
+// Return the remote end of the forwarded connection.
+QCString SshProcess::dcopServer()
+{
+ return QCString().sprintf("tcp/localhost:%d", m_dcopPort);
+}
+
+void SshProcess::virtual_hook( int id, void* data )
+{ StubProcess::virtual_hook( id, data ); }
diff --git a/kdesu/ssh.h b/kdesu/ssh.h
new file mode 100644
index 000000000..23308fd64
--- /dev/null
+++ b/kdesu/ssh.h
@@ -0,0 +1,90 @@
+/* vi: ts=8 sts=4 sw=4
+ *
+ * $Id$
+ *
+ * This file is part of the KDE project, module kdesu.
+ * Copyright (C) 2000 Geert Jansen <jansen@kde.org>
+ *
+ * This is free software; you can use this library under the GNU Library
+ * General Public License, version 2. See the file "COPYING.LIB" for the
+ * exact licensing terms.
+ */
+
+#ifndef __SSH_h_Included__
+#define __SSH_h_Included__
+
+#include <qcstring.h>
+
+#include "stub.h"
+
+#include <kdelibs_export.h>
+
+/**
+ * Executes a remote command, using ssh.
+ */
+
+class KDESU_EXPORT SshProcess: public StubProcess
+{
+public:
+ SshProcess(const QCString &host=0, const QCString &user=0, const QCString &command=0);
+ ~SshProcess();
+
+ enum Errors { SshNotFound=1, SshNeedsPassword, SshIncorrectPassword };
+
+ /**
+ * Sets the target host.
+ */
+ void setHost(const QCString &host) { m_Host = host; }
+
+ /**
+ * Sets the localtion of the remote stub.
+ */
+ void setStub(const QCString &stub);
+
+ /**
+ * Checks if the current user\@host needs a password.
+ * @return The prompt for the password if a password is required. A null
+ * string otherwise.
+ *
+ * @todo The return doc is so obviously wrong that the C code needs to be checked.
+ */
+ int checkNeedPassword();
+
+ /**
+ * Checks if the stub is installed and if the password is correct.
+ * @return Zero if everything is correct, nonzero otherwise.
+ */
+ int checkInstall(const char *password);
+
+ /**
+ * Executes the command.
+ */
+ int exec(const char *password, int check=0);
+
+ QCString prompt() { return m_Prompt; }
+ QCString error() { return m_Error; }
+
+protected:
+ virtual QCString display();
+ virtual QCString displayAuth();
+ virtual QCString dcopServer();
+
+private:
+ QCString dcopForward();
+ int ConverseSsh(const char *password, int check);
+
+ int m_dcopPort;
+ int m_dcopSrv;
+ QCString m_Prompt;
+ QCString m_Host;
+ QCString m_Error;
+ QCString m_Stub;
+
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ class SshProcessPrivate;
+ SshProcessPrivate *d;
+};
+
+#endif
diff --git a/kdesu/stub.cpp b/kdesu/stub.cpp
new file mode 100644
index 000000000..c0e968200
--- /dev/null
+++ b/kdesu/stub.cpp
@@ -0,0 +1,184 @@
+/* vi: ts=8 sts=4 sw=4
+ *
+ * $Id$
+ *
+ * This file is part of the KDE project, module kdesu.
+ * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
+ *
+ * This is free software; you can use this library under the GNU Library
+ * General Public License, version 2. See the file "COPYING.LIB" for the
+ * exact licensing terms.
+ *
+ * stub.cpp: Conversation with kdesu_stub.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <qglobal.h>
+#include <qcstring.h>
+#include <kdatastream.h>
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <dcopclient.h>
+
+#include "stub.h"
+#include "kcookie.h"
+
+
+StubProcess::StubProcess()
+{
+ m_User = "root";
+ m_Scheduler = SchedNormal;
+ m_Priority = 50;
+ m_pCookie = new KCookie;
+ m_bXOnly = true;
+ m_bDCOPForwarding = false;
+}
+
+
+StubProcess::~StubProcess()
+{
+ delete m_pCookie;
+}
+
+
+void StubProcess::setPriority(int prio)
+{
+ if (prio > 100)
+ m_Priority = 100;
+ else if (prio < 0)
+ m_Priority = 0;
+ else
+ m_Priority = prio;
+}
+
+
+QCString StubProcess::commaSeparatedList(QCStringList lst)
+{
+ if (lst.count() == 0)
+ return QCString("");
+
+ QCStringList::Iterator it = lst.begin();
+ QCString str = *it;
+ for (it++; it!=lst.end(); it++)
+ {
+ str += ',';
+ str += *it;
+ }
+ return str;
+}
+
+/*
+ * Conversation with kdesu_stub. This is how we pass the authentication
+ * tokens (X11, DCOP) and other stuff to kdesu_stub.
+ * return values: -1 = error, 0 = ok, 1 = kill me
+ */
+
+int StubProcess::ConverseStub(int check)
+{
+ QCString line, tmp;
+ while (1)
+ {
+ line = readLine();
+ if (line.isNull())
+ return -1;
+
+ if (line == "kdesu_stub")
+ {
+ // This makes parsing a lot easier.
+ enableLocalEcho(false);
+ if (check) writeLine("stop");
+ else writeLine("ok");
+ } else if (line == "display") {
+ writeLine(display());
+ } else if (line == "display_auth") {
+#ifdef Q_WS_X11
+ writeLine(displayAuth());
+#else
+ writeLine("");
+#endif
+ } else if (line == "dcopserver") {
+ if (m_bDCOPForwarding)
+ writeLine(dcopServer());
+ else
+ writeLine("no");
+ } else if (line == "dcop_auth") {
+ if (m_bDCOPForwarding)
+ writeLine(dcopAuth());
+ else
+ writeLine("no");
+ } else if (line == "ice_auth") {
+ if (m_bDCOPForwarding)
+ writeLine(iceAuth());
+ else
+ writeLine("no");
+ } else if (line == "command") {
+ writeLine(m_Command);
+ } else if (line == "path") {
+ QCString path = getenv("PATH");
+ if (!path.isEmpty() && path[0] == ':')
+ path = path.mid(1);
+ if (m_User == "root")
+ if (!path.isEmpty())
+ path = "/sbin:/bin:/usr/sbin:/usr/bin:" + path;
+ else
+ path = "/sbin:/bin:/usr/sbin:/usr/bin";
+ writeLine(path);
+ } else if (line == "user") {
+ writeLine(m_User);
+ } else if (line == "priority") {
+ tmp.setNum(m_Priority);
+ writeLine(tmp);
+ } else if (line == "scheduler") {
+ if (m_Scheduler == SchedRealtime) writeLine("realtime");
+ else writeLine("normal");
+ } else if (line == "xwindows_only") {
+ if (m_bXOnly) writeLine("no");
+ else writeLine("yes");
+ } else if (line == "app_startup_id") {
+ QCStringList env = environment();
+ QCString tmp;
+ for( QCStringList::ConstIterator it = env.begin();
+ it != env.end();
+ ++it )
+ {
+ if( (*it).find( "DESKTOP_STARTUP_ID=" ) == 0 )
+ tmp = (*it).mid( strlen( "DESKTOP_STARTUP_ID=" ));
+ }
+ if( tmp.isEmpty())
+ tmp = "0";
+ writeLine(tmp);
+ } else if (line == "app_start_pid") { // obsolete
+ tmp.setNum(getpid());
+ writeLine(tmp);
+ } else if (line == "environment") { // additional env vars
+ QCStringList env = environment();
+ for( QCStringList::ConstIterator it = env.begin();
+ it != env.end();
+ ++it )
+ writeLine( *it );
+ writeLine( "" );
+ } else if (line == "end") {
+ return 0;
+ } else
+ {
+ kdWarning(900) << k_lineinfo << "Unknown request: -->" << line
+ << "<--\n";
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+void StubProcess::notifyTaskbar(const QString &)
+{
+ kdWarning(900) << "Obsolete StubProcess::notifyTaskbar() called!" << endl;
+}
+
+void StubProcess::virtual_hook( int id, void* data )
+{ PtyProcess::virtual_hook( id, data ); }
diff --git a/kdesu/stub.h b/kdesu/stub.h
new file mode 100644
index 000000000..921db0e1c
--- /dev/null
+++ b/kdesu/stub.h
@@ -0,0 +1,139 @@
+/* vi: ts=8 sts=4 sw=4
+ *
+ * $Id$
+ *
+ * This file is part of the KDE project, module kdesu.
+ * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
+ *
+ * This is free software; you can use this library under the GNU Library
+ * General Public License, version 2. See the file "COPYING.LIB" for the
+ * exact licensing terms.
+ */
+
+#ifndef __Stub_h_Included__
+#define __Stub_h_Included__
+
+#include <qcstring.h>
+#include <qvaluelist.h>
+
+#include "process.h"
+#include "kcookie.h"
+
+#include <kdelibs_export.h>
+
+typedef QValueList<QCString> QCStringList;
+
+/**
+ * Chat with kdesu_stub.
+ *
+ * StubProcess extends PtyProcess with functionality to chat with kdesu_stub.
+ */
+
+class KDESU_EXPORT StubProcess: public PtyProcess
+{
+public:
+ StubProcess();
+ ~StubProcess();
+
+ /**
+ * Specify dcop transport
+ */
+ void setDcopTransport(const QCString &dcopTransport)
+ { m_pCookie->setDcopTransport(dcopTransport); }
+
+ /**
+ * Set the command.
+ */
+ void setCommand(const QCString &command) { m_Command = command; }
+
+ /**
+ * Set the target user.
+ */
+ void setUser(const QCString &user) { m_User = user; }
+
+ /**
+ * Set to "X only mode": Sycoca is not built and kdeinit is not launched.
+ */
+ void setXOnly(bool xonly) { m_bXOnly = xonly; }
+
+ /**
+ * Enable DCOP forwarding.
+ */
+ void setDCOPForwarding(bool dcopForwarding) { m_bDCOPForwarding = dcopForwarding; }
+
+ /**
+ * Set the priority of the process. The priority value must be between 0
+ * and 100, 0 being the lowest priority. This value is mapped to the
+ * scheduler and system dependant priority range of the OS.
+ */
+ void setPriority(int prio);
+
+ /**
+ * Different schedulers. SchedNormal is the normal Unix timesharing
+ * scheduler, while SchedRealtime is a POSIX.1b realtime scheduler.
+ */
+ enum Scheduler { SchedNormal, SchedRealtime };
+
+ /**
+ * Set the scheduler type.
+ */
+ void setScheduler(int sched) { m_Scheduler = sched; }
+
+protected:
+
+ /**
+ * Exchange all parameters with kdesu_stub.
+ */
+ int ConverseStub(int check);
+
+ /**
+ * Notify the taskbar that a new application has been started.
+ * @obsolete
+ */
+ // KDE4 remove
+ void notifyTaskbar(const QString &suffix);
+
+ /**
+ * This virtual function can be overloaded when special behavior is
+ * desired. By default, it returns the value returned by KCookie.
+ */
+ virtual QCString display() { return m_pCookie->display(); }
+#ifdef Q_WS_X11
+ /**
+ * See display.
+ */
+ virtual QCString displayAuth() { return m_pCookie->displayAuth(); }
+#endif
+ /**
+ * See display.
+ */
+ virtual QCString dcopServer() { return m_pCookie->dcopServer(); }
+ /**
+ * See display.
+ */
+ virtual QCString dcopAuth() { return m_pCookie->dcopAuth(); }
+ /**
+ * See display.
+ */
+ virtual QCString iceAuth() { return m_pCookie->iceAuth(); }
+
+ bool m_bXOnly;
+ bool m_bDCOPForwarding;
+ int m_Priority;
+ int m_Scheduler;
+ QCString m_dcopTransport;
+ QCString m_Command;
+ QCString m_User;
+ KCookie *m_pCookie;
+
+private:
+ QCString commaSeparatedList(QCStringList);
+
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ class StubProcessPrivate;
+ StubProcessPrivate *d;
+};
+
+#endif // __Stub_h_Included__
diff --git a/kdesu/su.cpp b/kdesu/su.cpp
new file mode 100644
index 000000000..344c0f34c
--- /dev/null
+++ b/kdesu/su.cpp
@@ -0,0 +1,342 @@
+/* vi: ts=8 sts=4 sw=4
+*
+* $Id$
+*
+* This file is part of the KDE project, module kdesu.
+* Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
+*
+* Sudo support added by Jonathan Riddell <jriddell@ ubuntu.com>
+* Copyright (C) 2005 Canonical Ltd
+*
+* This is free software; you can use this library under the GNU Library
+* General Public License, version 2. See the file "COPYING.LIB" for the
+* exact licensing terms.
+*
+* su.cpp: Execute a program as another user with "class SuProcess".
+*/
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <qglobal.h>
+#include <qcstring.h>
+#include <qfile.h>
+
+#include <kconfig.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+
+#include "su.h"
+#include "kcookie.h"
+
+
+#ifndef __PATH_SU
+#define __PATH_SU "false"
+#endif
+
+#ifndef __PATH_SUDO
+#define __PATH_SUDO "false"
+#endif
+
+SuProcess::SuProcess(const QCString &user, const QCString &command)
+{
+ m_User = user;
+ m_Command = command;
+
+ KConfig* config = KGlobal::config();
+ config->setGroup("super-user-command");
+ superUserCommand = config->readEntry("super-user-command", DEFAULT_SUPER_USER_COMMAND);
+ if ( superUserCommand != "sudo" && superUserCommand != "su" ) {
+ kdWarning() << "unknown super user command" << endl;
+ superUserCommand = "su";
+ }
+}
+
+
+SuProcess::~SuProcess()
+{
+}
+
+int SuProcess::checkInstall(const char *password)
+{
+ return exec(password, Install);
+}
+
+int SuProcess::checkNeedPassword()
+{
+ return exec(0L, NeedPassword);
+}
+
+/*
+* Execute a command with su(1).
+*/
+
+int SuProcess::exec(const char *password, int check)
+{
+ if (check)
+ setTerminal(true);
+
+ // since user may change after constructor (due to setUser())
+ // we need to override sudo with su for non-root here
+ if (m_User != "root") {
+ superUserCommand = "su";
+ }
+
+ QCStringList args;
+ if (superUserCommand == "sudo") {
+ args += "-u";
+ }
+
+#ifdef Q_OS_DARWIN
+ args += "-c";
+ args += "staff";
+#endif
+
+ if ((m_Scheduler != SchedNormal) || (m_Priority > 50))
+ args += "root";
+ else
+ args += m_User;
+
+ if (superUserCommand == "su") {
+ args += "-c";
+ }
+ args += QCString(__KDE_BINDIR) + "/kdesu_stub";
+#ifndef Q_OS_DARWIN
+ args += "-";
+#endif
+
+ /// QCString command = __PATH_SU;
+ /// if (::access(__PATH_SU, X_OK) != 0)
+ QCString command;
+ if (superUserCommand == "sudo") {
+ command = __PATH_SUDO;
+ } else {
+ command = __PATH_SU;
+ }
+
+ if (::access(command, X_OK) != 0)
+ {
+ /// command = QFile::encodeName(KGlobal::dirs()->findExe("su"));
+ command = QFile::encodeName( KGlobal::dirs()->findExe(superUserCommand.ascii()) );
+ if (command.isEmpty())
+ return check ? SuNotFound : -1;
+ }
+
+ // kdDebug(900) << k_lineinfo << "Call StubProcess::exec()" << endl;
+ if (StubProcess::exec(command, args) < 0)
+ {
+ return check ? SuNotFound : -1;
+ }
+ // kdDebug(900) << k_lineinfo << "Done StubProcess::exec()" << endl;
+
+ SuErrors ret = (SuErrors) ConverseSU(password);
+ // kdDebug(900) << k_lineinfo << "Conversation returned " << ret << endl;
+
+ if (ret == error)
+ {
+ if (!check)
+ kdError(900) << k_lineinfo << "Conversation with " << superUserCommand << " failed\n";
+ return ret;
+ }
+ if (check == NeedPassword)
+ {
+ if (ret == killme)
+ {
+ /// if (kill(m_Pid, SIGKILL) < 0)
+ /// {
+ /// ret=error;
+ /// }
+ if ( superUserCommand == "sudo" ) {
+ // sudo can not be killed, just return
+ return ret;
+ }
+ if (kill(m_Pid, SIGKILL) < 0) {
+ kdDebug() << k_funcinfo << "kill < 0" << endl;
+ //FIXME SIGKILL doesn't work for sudo,
+ //why is this different from su?
+ ret=error;
+ }
+ else
+ {
+ int iret = waitForChild();
+ if (iret < 0) ret=error;
+ else /* nothing */ {} ;
+ }
+ }
+ return ret;
+ }
+
+ if (m_bErase && password)
+ {
+ char *ptr = const_cast<char *>(password);
+ const uint plen = strlen(password);
+ for (unsigned i=0; i < plen; i++)
+ ptr[i] = '\000';
+ }
+
+ if (ret == notauthorized)
+ {
+ kill(m_Pid, SIGKILL);
+ if (superUserCommand != "sudo") {
+ waitForChild();
+ }
+ return SuIncorrectPassword;
+ }
+
+ int iret = ConverseStub(check);
+ if (iret < 0)
+ {
+ if (!check)
+ kdError(900) << k_lineinfo << "Converstation with kdesu_stub failed\n";
+ return iret;
+ }
+ else if (iret == 1)
+ {
+ kill(m_Pid, SIGKILL);
+ waitForChild();
+ return SuIncorrectPassword;
+ }
+
+ if (check == Install)
+ {
+ waitForChild();
+ return 0;
+ }
+
+ iret = waitForChild();
+ return iret;
+}
+
+/*
+* Conversation with su: feed the password.
+* Return values: -1 = error, 0 = ok, 1 = kill me, 2 not authorized
+*/
+
+int SuProcess::ConverseSU(const char *password)
+{
+ enum { WaitForPrompt, CheckStar, HandleStub } state = WaitForPrompt;
+ int colon;
+ unsigned i, j;
+ // kdDebug(900) << k_lineinfo << "ConverseSU starting." << endl;
+
+ QCString line;
+ while (true)
+ {
+ line = readLine();
+ if (line.isNull())
+ return ( state == HandleStub ? notauthorized : error);
+ kdDebug(900) << k_lineinfo << "Read line <" << line << ">" << endl;
+
+ switch (state)
+ {
+ //////////////////////////////////////////////////////////////////////////
+ case WaitForPrompt:
+ {
+ // In case no password is needed.
+ if (line == "kdesu_stub")
+ {
+ unreadLine(line);
+ return ok;
+ }
+
+ while(waitMS(m_Fd,100)>0)
+ {
+ // There is more output available, so the previous line
+ // couldn't have been a password prompt (the definition
+ // of prompt being that there's a line of output followed
+ // by a colon, and then the process waits).
+ QCString more = readLine();
+ if (more.isEmpty())
+ break;
+
+ line = more;
+ kdDebug(900) << k_lineinfo << "Read line <" << more << ">" << endl;
+ }
+
+ // Match "Password: " with the regex ^[^:]+:[\w]*$.
+ const uint len = line.length();
+ for (i=0,j=0,colon=0; i<len; i++)
+ {
+ if (line[i] == ':')
+ {
+ j = i; colon++;
+ continue;
+ }
+ if (!isspace(line[i]))
+ j++;
+ }
+ if ((colon == 1) && (line[j] == ':'))
+ {
+ if (password == 0L)
+ return killme;
+ if (!checkPid(m_Pid))
+ {
+ kdError(900) << superUserCommand << " has exited while waiting for pwd." << endl;
+ return error;
+ }
+ if ((WaitSlave() == 0) && checkPid(m_Pid))
+ {
+ write(m_Fd, password, strlen(password));
+ write(m_Fd, "\n", 1);
+ state=CheckStar;
+ }
+ else
+ {
+ return error;
+ }
+ }
+ break;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ case CheckStar:
+ {
+ QCString s = line.stripWhiteSpace();
+ if (s.isEmpty())
+ {
+ state=HandleStub;
+ break;
+ }
+ const uint len = line.length();
+ for (i=0; i< len; i++)
+ {
+ if (s[i] != '*')
+ return error;
+ }
+ state=HandleStub;
+ break;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ case HandleStub:
+ // Read till we get "kdesu_stub"
+ if (line == "kdesu_stub")
+ {
+ unreadLine(line);
+ return ok;
+ } else if (superUserCommand == "sudo") {
+ // sudo gives a "sorry" line so reaches here
+ // with the wrong password
+ return notauthorized;
+ }
+ break;
+ //////////////////////////////////////////////////////////////////////////
+ } // end switch
+ } // end while (true)
+ return ok;
+}
+
+void SuProcess::virtual_hook( int id, void* data )
+{ StubProcess::virtual_hook( id, data ); }
+
+
diff --git a/kdesu/su.h b/kdesu/su.h
new file mode 100644
index 000000000..c82e459eb
--- /dev/null
+++ b/kdesu/su.h
@@ -0,0 +1,63 @@
+/* vi: ts=8 sts=4 sw=4
+ *
+ * $Id$
+ *
+ * This file is part of the KDE project, module kdesu.
+ * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
+ *
+ * This is free software; you can use this library under the GNU Library
+ * General Public License, version 2. See the file "COPYING.LIB" for the
+ * exact licensing terms.
+ */
+
+#ifndef __SU_h_Included__
+#define __SU_h_Included__
+
+#include <qcstring.h>
+
+#include <kdelibs_export.h>
+
+#include "stub.h"
+
+/**
+ * Executes a command under elevated privileges, using su.
+ */
+
+class KDESU_EXPORT SuProcess: public StubProcess
+{
+public:
+ SuProcess(const QCString &user=0, const QCString &command=0);
+ ~SuProcess();
+
+ enum Errors { SuNotFound=1, SuNotAllowed, SuIncorrectPassword };
+
+ /**
+ * Executes the command. This will wait for the command to finish.
+ */
+ enum checkMode { NoCheck=0, Install=1, NeedPassword=2 } ;
+ int exec(const char *password, int check=NoCheck);
+
+ /**
+ * Checks if the stub is installed and the password is correct.
+ * @return Zero if everything is correct, nonzero otherwise.
+ */
+ int checkInstall(const char *password);
+
+ /**
+ * Checks if a password is needed.
+ */
+ int checkNeedPassword();
+
+private:
+ enum SuErrors { error=-1, ok=0, killme=1, notauthorized=2 } ;
+ int ConverseSU(const char *password);
+
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ class SuProcessPrivate;
+ SuProcessPrivate *d;
+ QString superUserCommand;
+};
+
+#endif