generic: replace kdesu with kdesudo

Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
Ivailo Monev 2016-05-15 02:06:56 +00:00
parent 31ce3fef82
commit 7419f793b0
42 changed files with 865 additions and 3205 deletions

View file

@ -54,6 +54,14 @@ set_package_properties(Perl PROPERTIES
PURPOSE "Needed for KIO fileshareset and KDEUI preparetips scripts"
)
find_package(Sudo)
set_package_properties(Sudo PROPERTIES
DESCRIPTION "Sudo allows a system administrator to delegate authority to give certain users"
URL "http://www.sudo.ws/"
TYPE RUNTIME
PURPOSE "Needed for kdesudo to operate"
)
find_package(ZLIB)
set_package_properties(ZLIB PROPERTIES
DESCRIPTION "Support for gzip compressed files and data streams"
@ -266,7 +274,7 @@ add_subdirectory( kdcraw )
add_subdirectory( kdeclarative )
add_subdirectory( kdecore )
add_subdirectory( kded )
add_subdirectory( kdesu )
add_subdirectory( kdesudo )
add_subdirectory( kdeui )
if(QT_QTWEBKIT_FOUND)
add_subdirectory( kdewebkit )

View file

@ -69,7 +69,7 @@ macro_bool_to_01(X11_Xrender_FOUND HAVE_XRENDER) # kio
check_symbol_exists(strtoll "stdlib.h" HAVE_STRTOLL) # kioslave
check_symbol_exists(S_ISSOCK "sys/stat.h" HAVE_S_ISSOCK) # config.h
check_symbol_exists(vsnprintf "stdio.h" HAVE_VSNPRINTF) # config.h
check_symbol_exists(getgrouplist "unistd.h;grp.h" HAVE_GETGROUPLIST)# kdecore/fakes.c
check_symbol_exists(getgrouplist "unistd.h;grp.h" HAVE_GETGROUPLIST)# kdecore/fakes.cpp
check_function_exists(backtrace HAVE_BACKTRACE) # kdecore, kio
check_function_exists(fdatasync HAVE_FDATASYNC) # kdecore, kate
@ -94,7 +94,7 @@ check_library_exists(socket connect "" HAVE_SOCKET_LIBRARY)
if (UNIX)
# for kdecore (kpty) & kdesu
# for kpty
check_include_files("sys/types.h;libutil.h" HAVE_LIBUTIL_H)
check_include_files(util.h HAVE_UTIL_H)
@ -182,19 +182,19 @@ if (UNIX)
endif (UNIX)
check_function_exists(getmntinfo HAVE_GETMNTINFO) # kdecore, kio
check_function_exists(initgroups HAVE_INITGROUPS) # kde3support/k3process, kdesu
check_function_exists(mkstemps HAVE_MKSTEMPS) # dcop, kdecore/fakes.c
check_function_exists(mkstemp HAVE_MKSTEMP) # kdecore/fakes.c
check_function_exists(mkdtemp HAVE_MKDTEMP) # kdecore/fakes.c
check_function_exists(strlcpy HAVE_STRLCPY) # kdecore/fakes.c
check_function_exists(strlcat HAVE_STRLCAT) # kdecore/fakes.c
check_function_exists(strcasestr HAVE_STRCASESTR) # kdecore/fakes.c
check_function_exists(initgroups HAVE_INITGROUPS) # kdecore/fakes.cpp
check_function_exists(mkstemps HAVE_MKSTEMPS) # dcop, kdecore/fakes.cpp
check_function_exists(mkstemp HAVE_MKSTEMP) # kdecore/fakes.cpp
check_function_exists(mkdtemp HAVE_MKDTEMP) # kdecore/fakes.cpp
check_function_exists(strlcpy HAVE_STRLCPY) # kdecore/fakes.cpp
check_function_exists(strlcat HAVE_STRLCAT) # kdecore/fakes.cpp
check_function_exists(strcasestr HAVE_STRCASESTR) # kdecore/fakes.cpp
check_symbol_exists(strcasestr string.h HAVE_STRCASESTR_PROTO)
check_function_exists(setenv HAVE_SETENV) # kdecore/fakes.c
check_function_exists(seteuid HAVE_SETEUID) # kdecore/fakes.c
check_function_exists(setenv HAVE_SETENV) # kdecore/fakes.cpp
check_function_exists(seteuid HAVE_SETEUID) # kdecore/fakes.cpp
check_function_exists(setmntent HAVE_SETMNTENT) # solid, kio, kdecore
check_function_exists(unsetenv HAVE_UNSETENV) # kdecore/fakes.c
check_function_exists(usleep HAVE_USLEEP) # kdecore/fakes.c, kdeui/qxembed
check_function_exists(unsetenv HAVE_UNSETENV) # kdecore/fakes.cpp
check_function_exists(usleep HAVE_USLEEP) # kdecore/fakes.cpp, kdeui/qxembed
# check for prototypes [for functions provided by kdefakes when not available]

View file

@ -78,7 +78,6 @@
# KDE4_KPRINTUTILS_LIBS - the kprintutils library and all depending libraries
# KDE4_KFILE_LIBS - the kfile library and all depending libraries
# KDE4_KDNSSD_LIBS - the kdnssd library and all depending libraries
# KDE4_KDESU_LIBS - the kdesu library and all depending libraries
# KDE4_KPTY_LIBS - the kpty library and all depending libraries
# KDE4_THREADWEAVER_LIBS - the threadweaver library and all depending libraries
# KDE4_SOLID_LIBS - the solid library and all depending libraries
@ -406,7 +405,6 @@ set(_kde_libraries
kdcraw
kdeclarative
kdecore
kdesu
kdeui
kdewebkit
kdnssd

View file

@ -419,14 +419,9 @@ install(
KWebView
KWebPluginFactory
KWebWallet
KDEsuClient
KPty
KPtyDevice
KPtyProcess
PtyProcess
SshProcess
StubProcess
SuProcess
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE
COMPONENT Devel
)

View file

@ -1 +0,0 @@
#include "../kdesu/client.h"

View file

@ -1 +0,0 @@
#include "../kdesu/process.h"

View file

@ -1 +0,0 @@
#include "../kdesu/ssh.h"

View file

@ -1 +0,0 @@
#include "../kdesu/stub.h"

View file

@ -1 +0,0 @@
#include "../kdesu/su.h"

View file

@ -61,8 +61,7 @@
1201 konqtree
1203 libkonq
1204 plasma
1205 kdesud
1206 kdesu
1205 kdesudo
1207 krunner
1208 kcontrol
1209 libplasma

View file

@ -1,80 +0,0 @@
project(kdesu)
include_directories(${KDE4_KPTY_INCLUDES})
check_function_exists(setpriority HAVE_SETPRIORITY)
check_function_exists(getpeereid HAVE_GETPEEREID)
set(CMAKE_EXTRA_INCLUDE_FILES sys/socket.h)
# defines HAVE_STRUCT_UCRED (bool) and STRUCT_UCRED (size, unused)
check_type_size("struct ucred" STRUCT_UCRED)
# reset CMAKE_EXTRA_INCLUDE_FILES
set(CMAKE_EXTRA_INCLUDE_FILES)
configure_file(
config-kdesu.h.cmake
${CMAKE_CURRENT_BINARY_DIR}/config-kdesu.h
)
find_package(Sudo)
set_package_properties(Sudo PROPERTIES
DESCRIPTION "Sudo allows a system administrator to delegate authority to give certain users"
URL "http://www.sudo.ws/"
PURPOSE "Needed for password caching in kdesu"
)
########### next target ###############
set(kdesu_LIB_SRCS
client.cpp
process.cpp
kcookie.cpp
su.cpp
ssh.cpp
stub.cpp
)
add_library(kdesu ${LIBRARY_TYPE} ${kdesu_LIB_SRCS})
target_link_libraries(kdesu PRIVATE ${KDE4_KPTY_LIBS})
target_link_libraries(kdesu PUBLIC ${QT_QTCORE_LIBRARY})
if(SUDO_FOUND)
add_definitions(-DKDESU_USE_SUDO_DEFAULT)
endif()
set_target_properties(kdesu PROPERTIES
VERSION ${GENERIC_LIB_VERSION}
SOVERSION ${GENERIC_LIB_SOVERSION}
)
install(
TARGETS kdesu
EXPORT kdelibsLibraryTargets
${INSTALL_TARGETS_DEFAULT_ARGS}
)
########### next target ###############
set(kdesu_stub_SRCS kdesu_stub.c )
add_executable(kdesu_stub ${kdesu_stub_SRCS})
install(TARGETS kdesu_stub DESTINATION ${LIBEXEC_INSTALL_DIR})
########### install files ###############
generate_export_header(kdesu)
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/kdesu_export.h
client.h
process.h
su.h
ssh.h
stub.h
defaults.h
DESTINATION ${INCLUDE_INSTALL_DIR}/kdesu
COMPONENT Devel
)

View file

@ -1,13 +0,0 @@
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.

View file

@ -1,438 +0,0 @@
/* vi: ts=8 sts=4 sw=4
*
* This file is part of the KDE project, module kdesu.
* Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
*
* 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 "client.h"
#include <config.h>
#include <config-kdesu.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 <qplatformdefs.h>
#include <QtCore/qglobal.h>
#include <QtCore/QFile>
#include <QtCore/QRegExp>
#include <kdebug.h>
#include <kstandarddirs.h>
#include <ktoolinvocation.h>
#include <kde_file.h>
extern int kdesuDebugArea();
namespace KDESu {
class KDEsuClient::KDEsuClientPrivate {
public:
KDEsuClientPrivate() : sockfd(-1) {}
QString daemon;
int sockfd;
QByteArray sock;
};
#ifndef SUN_LEN
#define SUN_LEN(ptr) ((QT_SOCKLEN_T) (((struct sockaddr_un *) 0)->sun_path) \
+ strlen ((ptr)->sun_path))
#endif
KDEsuClient::KDEsuClient()
:d(new KDEsuClientPrivate)
{
#ifdef Q_WS_X11
QString display = QString::fromLatin1(qgetenv("DISPLAY"));
if (display.isEmpty())
{
kWarning(kdesuDebugArea()) << "$DISPLAY is not set.";
return;
}
// strip the screen number from the display
display.remove(QRegExp("\\.[0-9]+$"));
#elif defined(Q_WS_QWS)
QByteArray display("QWS");
#else
QByteArray display("NODISPLAY");
#endif
d->sock = QFile::encodeName( KStandardDirs::locateLocal("socket",
QString("kdesud_").append(display)));
connect();
}
KDEsuClient::~KDEsuClient()
{
if (d->sockfd >= 0)
close(d->sockfd);
delete d;
}
int KDEsuClient::connect()
{
if (d->sockfd >= 0)
close(d->sockfd);
if (access(d->sock, R_OK|W_OK))
{
d->sockfd = -1;
return -1;
}
d->sockfd = socket(PF_UNIX, SOCK_STREAM, 0);
if (d->sockfd < 0)
{
kWarning(kdesuDebugArea()) << "socket():" << perror;
return -1;
}
struct sockaddr_un addr;
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, d->sock);
if (::connect(d->sockfd, (struct sockaddr *) &addr, SUN_LEN(&addr)) < 0)
{
kWarning(kdesuDebugArea()) << "connect():" << perror;
close(d->sockfd); d->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(d->sockfd, &euid, &egid) == 0)
{
if (euid != getuid())
{
kWarning(kdesuDebugArea()) << "socket not owned by me! socket uid =" << euid;
close(d->sockfd); d->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(d->sock, &s)!=0)
{
kWarning(kdesuDebugArea()) << "stat failed (" << d->sock << ")";
close(d->sockfd); d->sockfd = -1;
return -1;
}
if (s.st_uid != getuid())
{
kWarning(kdesuDebugArea()) << "socket not owned by me! socket uid =" << s.st_uid;
close(d->sockfd); d->sockfd = -1;
return -1;
}
if (!S_ISSOCK(s.st_mode))
{
kWarning(kdesuDebugArea()) << "socket is not a socket (" << d->sock << ")";
close(d->sockfd); d->sockfd = -1;
return -1;
}
# endif
#else
struct ucred cred;
QT_SOCKLEN_T siz = sizeof(cred);
// Security: if socket exists, we must own it
if (getsockopt(d->sockfd, SOL_SOCKET, SO_PEERCRED, &cred, &siz) == 0)
{
if (cred.uid != getuid())
{
kWarning(kdesuDebugArea()) << "socket not owned by me! socket uid =" << cred.uid;
close(d->sockfd); d->sockfd = -1;
return -1;
}
}
#endif
return 0;
}
QByteArray KDEsuClient::escape(const QByteArray &str)
{
QByteArray copy;
copy.reserve(str.size() + 4);
copy.append('"');
for (int i = 0; i < str.size(); i++) {
uchar c = str.at(i);
if (c < 32) {
copy.append('\\');
copy.append('^');
copy.append(c + '@');
} else {
if (c == '\\' || c == '"')
copy.append('\\');
copy.append(c);
}
}
copy.append('"');
return copy;
}
int KDEsuClient::command(const QByteArray &cmd, QByteArray *result)
{
if (d->sockfd < 0)
return -1;
if (send(d->sockfd, cmd, cmd.length(), 0) != (int) cmd.length())
return -1;
char buf[1024];
int nbytes = recv(d->sockfd, buf, 1023, 0);
if (nbytes <= 0)
{
kWarning(kdesuDebugArea()) << "no reply from daemon.";
return -1;
}
buf[nbytes] = '\000';
QByteArray 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)
{
QByteArray cmd = "PASS ";
cmd += escape(pass);
cmd += ' ';
cmd += QByteArray().setNum(timeout);
cmd += '\n';
return command(cmd);
}
int KDEsuClient::exec(const QByteArray &prog, const QByteArray &user, const QByteArray &options, const QList<QByteArray> &env)
{
QByteArray cmd;
cmd = "EXEC ";
cmd += escape(prog);
cmd += ' ';
cmd += escape(user);
if (!options.isEmpty() || !env.isEmpty())
{
cmd += ' ';
cmd += escape(options);
for (int i = 0; i < env.count(); ++i)
{
cmd += ' ';
cmd += escape(env.at(i));
}
}
cmd += '\n';
return command(cmd);
}
int KDEsuClient::setHost(const QByteArray &host)
{
QByteArray cmd = "HOST ";
cmd += escape(host);
cmd += '\n';
return command(cmd);
}
int KDEsuClient::setPriority(int prio)
{
QByteArray cmd;
cmd += "PRIO ";
cmd += QByteArray::number(prio);
cmd += '\n';
return command(cmd);
}
int KDEsuClient::setScheduler(int sched)
{
QByteArray cmd;
cmd += "SCHD ";
cmd += QByteArray::number(sched);
cmd += '\n';
return command(cmd);
}
int KDEsuClient::delCommand(const QByteArray &key, const QByteArray &user)
{
QByteArray cmd = "DEL ";
cmd += escape(key);
cmd += ' ';
cmd += escape(user);
cmd += '\n';
return command(cmd);
}
int KDEsuClient::setVar(const QByteArray &key, const QByteArray &value, int timeout,
const QByteArray &group)
{
QByteArray cmd = "SET ";
cmd += escape(key);
cmd += ' ';
cmd += escape(value);
cmd += ' ';
cmd += escape(group);
cmd += ' ';
cmd += QByteArray().setNum(timeout);
cmd += '\n';
return command(cmd);
}
QByteArray KDEsuClient::getVar(const QByteArray &key)
{
QByteArray cmd = "GET ";
cmd += escape(key);
cmd += '\n';
QByteArray reply;
command(cmd, &reply);
return reply;
}
QList<QByteArray> KDEsuClient::getKeys(const QByteArray &group)
{
QByteArray cmd = "GETK ";
cmd += escape(group);
cmd += '\n';
QByteArray reply;
command(cmd, &reply);
int index=0, pos;
QList<QByteArray> list;
if( !reply.isEmpty() )
{
// kDebug(kdesuDebugArea()) << "Found a matching entry:" << reply;
while (1)
{
pos = reply.indexOf( '\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 QByteArray &group)
{
QByteArray cmd = "CHKG ";
cmd += escape(group);
cmd += '\n';
if( command(cmd) == -1 )
return false;
return true;
}
int KDEsuClient::delVar(const QByteArray &key)
{
QByteArray cmd = "DELV ";
cmd += escape(key);
cmd += '\n';
return command(cmd);
}
int KDEsuClient::delGroup(const QByteArray &group)
{
QByteArray cmd = "DELG ";
cmd += escape(group);
cmd += '\n';
return command(cmd);
}
int KDEsuClient::delVars(const QByteArray &special_key)
{
QByteArray cmd = "DELS ";
cmd += escape(special_key);
cmd += '\n';
return command(cmd);
}
int KDEsuClient::ping()
{
return command("PING\n");
}
int KDEsuClient::exitCode()
{
QByteArray result;
if (command("EXIT\n", &result) != 0)
return -1;
return result.toInt();
}
int KDEsuClient::stopServer()
{
return command("STOP\n");
}
bool KDEsuClient::isServerSGID()
{
d->daemon = KStandardDirs::locate("exe", "kdesud");
if (d->daemon.isEmpty()) {
kWarning(kdesuDebugArea()) << "daemon not found.";
return false;
}
QT_STATBUF sbuf;
if (QT_STAT(QFile::encodeName(d->daemon), &sbuf) < 0)
{
kWarning(kdesuDebugArea()) << "stat():" << perror;
return false;
}
return (sbuf.st_mode & S_ISGID);
}
int KDEsuClient::startServer()
{
d->daemon = KStandardDirs::locate("exe", "kdesud");
if (d->daemon.isEmpty()) {
kWarning(kdesuDebugArea()) << "daemon not found.";
return -1;
}
if (!isServerSGID()) {
kWarning(kdesuDebugArea()) << "kdesud not setgid!";
}
// 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 = KToolInvocation::kdeinitExecWait(d->daemon);
connect();
return ret;
}
}

View file

@ -1,204 +0,0 @@
/* vi: ts=8 sts=4 sw=4
*
* This file is part of the KDE project, module kdesu.
* Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
*
* 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 <QtCore/qglobal.h>
#include <QtCore/QList>
#include <kdesu/kdesu_export.h>
#ifdef Q_OS_UNIX
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <QtCore/QByteArray>
namespace KDESu {
/** \class KDEsuClient client.h kdesu/client.h
* 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 QByteArray &command, const QByteArray &user, const QByteArray &options=0, const QList<QByteArray> &env=QList<QByteArray>());
/**
* 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 QByteArray &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 QByteArray &command, const QByteArray &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 QByteArray &key, const QByteArray &value, int timeout=0, const QByteArray &group=0);
/**
* Get a persistent variable.
* @param key The name of the variable.
* @return Its value.
*/
QByteArray getVar(const QByteArray &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.
*/
QList<QByteArray> getKeys(const QByteArray &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 QByteArray &group);
/**
* Delete a persistent variable.
* @param key The name of the variable.
* @return zero on success, -1 on failure.
*/
int delVar(const QByteArray &key);
/**
* Delete all persistent variables with the given key.
*
* A specicalized variant of delVar(QByteArray) 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 QByteArray &special_key);
/**
* Delete all persistent variables in a group.
*
* @param group the group name. See setVar.
* @return
*/
int delGroup(const QByteArray &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 command(const QByteArray &cmd, QByteArray *result=0L);
QByteArray escape(const QByteArray &str);
class KDEsuClientPrivate;
KDEsuClientPrivate* const d;
};
} //END namespace KDESu
#endif //Q_OS_UNIX
#endif //__KDE_su_Client_h_Included__

View file

@ -1,7 +0,0 @@
#cmakedefine HAVE_STRUCT_UCRED 1
#cmakedefine HAVE_SETPRIORITY 1
#cmakedefine HAVE_GETPEEREID 1

View file

@ -1,22 +0,0 @@
/* vi: ts=8 sts=4 sw=4
*
* This file is part of the KDE project, module kdesu.
* Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
*
* 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__
namespace KDESu {
const int defTimeout = 120*60;
const int defEchoMode = 0;
const bool defKeep = false;
}
#endif

View file

@ -1,107 +0,0 @@
/* vi: ts=8 sts=4 sw=4
*
* This file is part of the KDE project, module kdesu.
* Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
*
* 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 "kcookie.h"
#include <stdlib.h>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/qglobal.h>
#include <QtCore/qprocess.h>
#include <kdebug.h>
extern int kdesuDebugArea();
namespace KDESu {
namespace KDESuPrivate {
class KCookie::KCookiePrivate
{
public:
QByteArray m_Display;
#ifdef Q_WS_X11
QByteArray m_DisplayAuth;
#endif
};
KCookie::KCookie()
: d( new KCookiePrivate )
{
#ifdef Q_WS_X11
getXCookie();
#endif
}
KCookie::~KCookie()
{
delete d;
}
QByteArray KCookie::display() const
{
return d->m_Display;
}
#ifdef Q_WS_X11
QByteArray KCookie::displayAuth() const
{
return d->m_DisplayAuth;
}
#endif
void KCookie::getXCookie()
{
#ifdef Q_WS_X11
d->m_Display = qgetenv("DISPLAY");
#else
d->m_Display = qgetenv("QWS_DISPLAY");
#endif
if (d->m_Display.isEmpty())
{
kError(kdesuDebugArea()) << "$DISPLAY is not set.";
return;
}
#ifdef Q_WS_X11 // No need to mess with X Auth stuff
QByteArray disp = d->m_Display;
if (disp.startsWith("localhost:")) // krazy:exclude=strings
disp.remove(0, 9);
QProcess proc;
proc.start("xauth", QStringList() << "list" << disp);
if (!proc.waitForStarted())
{
kError(kdesuDebugArea()) << "Could not run xauth.";
return;
}
proc.waitForReadyRead(100);
QByteArray output = proc.readLine().simplified();
if (output.isEmpty())
{
kWarning(kdesuDebugArea()) << "No X authentication info set for display" << d->m_Display;
return;
}
QList<QByteArray> lst = output.split(' ');
if (lst.count() != 3)
{
kError(kdesuDebugArea()) << "parse error.";
return;
}
d->m_DisplayAuth = (lst[1] + ' ' + lst[2]);
proc.waitForFinished(100); // give QProcess a chance to clean up gracefully
#endif
}
}}

View file

@ -1,54 +0,0 @@
/* vi: ts=8 sts=4 sw=4
*
* This file is part of the KDE project, module kdesu
* Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
*
* 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 <QtCore/qbytearray.h>
namespace KDESu {
namespace KDESuPrivate {
/**
* Utility class to access the authentication tokens needed to run a KDE
* program (X11 cookies on X11, for instance).
* @internal
*/
class KCookie
{
public:
KCookie();
~KCookie();
/**
* Returns the X11 display.
*/
QByteArray display() const;
#ifdef Q_WS_X11
/**
* Returns the X11 magic cookie, if available.
*/
QByteArray displayAuth() const;
#endif
private:
void getXCookie();
class KCookiePrivate;
KCookiePrivate * const d;
};
}}
#endif // __KCookie_h_Included__

View file

@ -1,414 +0,0 @@
/* vi: ts=8 sts=4 sw=4
*
* 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.
*
* This file is part of the KDE project, module kdesu.
* Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*
* 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
* - 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 <config-kdesu.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 },
{ "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_COMMAND 3
#define P_PATH 4
#define P_XWIN_ONLY 5
#define P_USER 6
#define P_PRIORITY 7
#define P_SCHEDULER 8
#define P_APP_STARTUP_ID 9
#define P_LAST 10
/**
* 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 = xrealloc(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
static void dequote(char *buf)
{
char *in, *out;
for (in = buf, out = buf; *in; in++, out++) {
char c = *in;
if (c == '\\') {
c = *++in;
if (c == '/')
*out = '\\';
else
*out = c - '@';
} else {
*out = c;
}
}
*out = 0;
}
/**
* The main program
*/
int main()
{
char buf[BUFSIZE+1];
#ifndef QWS
char xauthority[200];
#endif
int i/*, res, sycoca*/;
pid_t pid;
struct passwd *pw;
const char* kdesu_lc_all;
xauthority[0] = '\0';
/* 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);
}
dequote(buf);
tmp = xstrdup( buf );
if( tmp[ 0 ] == '\0' ) /* terminator */
break;
putenv(tmp);
}
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);
/* New user won't be able to connect it anyway. */
unsetenv("DBUS_SESSION_BUS_ADDRESS");
/* Set scheduling/priority */
#if defined(POSIX1B_SCHEDULING) || defined(HAVE_SETPRIORITY)
double prio = (double) atoi(params[P_PRIORITY].value);
#endif
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) (prio * (max - min) / 100 + 0.5);
sched_setscheduler(0, SCHED_FIFO, &sched);
#else
printf("kdesu_stub: realtime scheduling not supported\n");
#endif
#ifdef HAVE_SETPRIORITY
} else {
int val = 20 - (int) (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);
FILE *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
}
/* Rebuild the sycoca and start kdeinit? */
if (strcmp(params[P_XWIN_ONLY].value, "no"))
{
system("kdeinit4 --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 xit = 1;
while (1)
{
int state;
int 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
if (*xauthority)
unlink(xauthority);
#endif
exit(xit);
} else
{
setsid();
/* Child: exec command. */
sprintf(buf, "%s", params[P_COMMAND].value);
dequote(buf);
execl("/bin/sh", "sh", "-c", buf, (void *)0);
perror("kdesu_stub: exec()");
_exit(1);
}
}

View file

@ -1,552 +0,0 @@
/* vi: ts=8 sts=4 sw=4
*
* This file is part of the KDE project, module kdesu.
* Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
*
* 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 "process.h"
#include "kcookie.h"
#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 <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/resource.h>
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h> // Needed on some systems.
#endif
#include <QtCore/qglobal.h>
#include <QtCore/QFile>
#include <ksharedconfig.h>
#include <kconfiggroup.h>
#include <kdebug.h>
#include <kstandarddirs.h>
#include <kde_file.h>
extern int kdesuDebugArea();
namespace KDESu {
using namespace KDESuPrivate;
/*
** Wait for @p ms miliseconds
** @param fd file descriptor
** @param ms time to wait in miliseconds
** @return
*/
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);
}
// XXX this function is nonsense:
// - for our child, we could use waitpid().
// - the configurability at this place it *complete* braindamage
/*
** Basic check for the existence of @p pid.
** Returns true iff @p pid is an extant process.
*/
bool PtyProcess::checkPid(pid_t pid)
{
KSharedConfig::Ptr config = KGlobal::config();
KConfigGroup cg(config, "super-user-command");
QString superUserCommand = cg.readEntry("super-user-command", "sudo");
//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)
{
kError(kdesuDebugArea()) << "waitpid():" << perror;
return Error;
}
if (ret == pid)
{
if (WIFEXITED(state))
return WEXITSTATUS(state);
return Killed;
}
return NotExited;
}
class PtyProcess::PtyProcessPrivate
{
public:
PtyProcessPrivate() : m_pPTY(0L) {}
~PtyProcessPrivate()
{
delete m_pPTY;
}
QList<QByteArray> env;
KPty *m_pPTY;
QByteArray m_Inbuf;
};
PtyProcess::PtyProcess()
:d(new PtyProcessPrivate)
{
m_bTerminal = false;
m_bErase = false;
}
int PtyProcess::init()
{
delete d->m_pPTY;
d->m_pPTY = new KPty();
if (!d->m_pPTY->open())
{
kError(kdesuDebugArea()) << "Failed to open PTY.";
return -1;
}
d->m_Inbuf.resize(0);
return 0;
}
PtyProcess::~PtyProcess()
{
delete d;
}
/** Set additional environment variables. */
void PtyProcess::setEnvironment( const QList<QByteArray> &env )
{
d->env = env;
}
int PtyProcess::fd() const
{
return d->m_pPTY ? d->m_pPTY->masterFd() : -1;
}
int PtyProcess::pid() const
{
return m_Pid;
}
/** Returns the additional environment variables set by setEnvironment() */
QList<QByteArray> PtyProcess::environment() const
{
return d->env;
}
QByteArray PtyProcess::readAll(bool block)
{
QByteArray ret;
if (!d->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 = d->m_Inbuf;
d->m_Inbuf.resize(0);
}
int flags = fcntl(fd(), F_GETFL);
if (flags < 0)
{
kError(kdesuDebugArea()) << "fcntl(F_GETFL):" << perror;
return ret;
}
int oflags = flags;
if (block)
flags &= ~O_NONBLOCK;
else
flags |= O_NONBLOCK;
if ((flags != oflags) && (fcntl(fd(), F_SETFL, flags) < 0))
{
// We get an error here when the child process has closed
// the file descriptor already.
return ret;
}
while (1)
{
ret.reserve(ret.size() + 0x8000);
int nbytes = read(fd(), ret.data() + ret.size(), 0x8000);
if (nbytes == -1)
{
if (errno == EINTR)
continue;
else break;
}
if (nbytes == 0)
break; // nothing available / eof
ret.resize(ret.size() + nbytes);
break;
}
return ret;
}
QByteArray PtyProcess::readLine(bool block)
{
d->m_Inbuf = readAll(block);
QByteArray ret;
if (!d->m_Inbuf.isEmpty())
{
int pos = d->m_Inbuf.indexOf('\n');
if (pos == -1)
{
// NOTE: this means we return something even if there in no full line!
ret = d->m_Inbuf;
d->m_Inbuf.resize(0);
} else
{
ret = d->m_Inbuf.left(pos);
d->m_Inbuf.remove(0, pos+1);
}
}
return ret;
}
void PtyProcess::writeLine(const QByteArray &line, bool addnl)
{
if (!line.isEmpty())
write(fd(), line, line.length());
if (addnl)
write(fd(), "\n", 1);
}
void PtyProcess::unreadLine(const QByteArray &line, bool addnl)
{
QByteArray tmp = line;
if (addnl)
tmp += '\n';
if (!tmp.isEmpty())
d->m_Inbuf.prepend(tmp);
}
void PtyProcess::setExitString(const QByteArray &exit)
{
m_Exit = exit;
}
/*
* Fork and execute the command. This returns in the parent.
*/
int PtyProcess::exec(const QByteArray &command, const QList<QByteArray> &args)
{
kDebug(kdesuDebugArea()) << "Running" << command;
if (init() < 0)
return -1;
if ((m_Pid = fork()) == -1)
{
kError(kdesuDebugArea()) << "fork():" << perror;
return -1;
}
// Parent
if (m_Pid)
{
d->m_pPTY->closeSlave();
return 0;
}
// Child
if (setupTTY() < 0)
_exit(1);
for (int i = 0; i < d->env.count(); ++i)
{
putenv(const_cast<char *>(d->env.at(i).constData()));
}
unsetenv("KDE_FULL_SESSION");
// for : Qt: Session management error
unsetenv("SESSION_MANAGER");
// QMutex::lock , deadlocks without that.
// <thiago> you cannot connect to the user's session bus from another UID
unsetenv("DBUS_SESSION_BUS_ADDRESS");
// set temporarily LC_ALL to C, for su (to be able to parse "Password:")
const QByteArray old_lc_all = qgetenv( "LC_ALL" );
if( !old_lc_all.isEmpty() )
qputenv( "KDESU_LC_ALL", old_lc_all );
else
unsetenv( "KDESU_LC_ALL" );
qputenv("LC_ALL", "C");
// From now on, terminal output goes through the tty.
QByteArray path;
if (command.contains('/')) {
path = command;
} else {
QString file = KStandardDirs::findExe(command);
if (file.isEmpty()) {
kError(kdesuDebugArea()) << command << "not found.";
_exit(1);
}
path = QFile::encodeName(file);
}
const char **argp = (const char **)malloc((args.count()+2)*sizeof(char *));
int i = 1;
argp[i] = path;
foreach (QByteArray it, args) {
argp[i] = it;
i++;
}
argp[i] = NULL;
execv(path, const_cast<char **>(argp));
free(argp);
kError(kdesuDebugArea()) << "execv(" << path << "):" << perror;
_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()
{
kDebug(kdesuDebugArea()) << "Child pid" << m_Pid;
struct termios tio;
while (1)
{
if (!checkPid(m_Pid))
{
kError(kdesuDebugArea()) << "process has exited while waiting for password.";
return -1;
}
if (!d->m_pPTY->tcGetAttr(&tio))
{
kError(kdesuDebugArea()) << "tcgetattr():" << perror;
return -1;
}
if (tio.c_lflag & ECHO)
{
kDebug(kdesuDebugArea()) << "Echo mode still on.";
usleep(10000);
continue;
}
break;
}
return 0;
}
int PtyProcess::enableLocalEcho(bool enable)
{
return d->m_pPTY->setEcho(enable) ? 0 : -1;
}
void PtyProcess::setTerminal(bool terminal)
{
m_bTerminal = terminal;
}
void PtyProcess::setErase(bool erase)
{
m_bErase = erase;
}
/*
* Copy output to stdout until the child process exits, 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()
{
fd_set fds;
FD_ZERO(&fds);
QByteArray remainder;
while (1)
{
FD_SET(fd(), &fds);
// specify timeout to make sure select() does not block, even if the
// process is dead / non-responsive. It does not matter if we abort too
// early. In that case 0 is returned, and we'll try again in the next
// iteration. (As long as we don't consitently time out in each iteration)
timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 100000;
int ret = select(fd()+1, &fds, 0L, 0L, &timeout);
if (ret == -1)
{
if (errno != EINTR)
{
kError(kdesuDebugArea()) << "select():" << perror;
return -1;
}
ret = 0;
}
if (ret)
{
forever {
QByteArray output = readAll(false);
if (output.isEmpty())
break;
if (m_bTerminal)
{
fwrite(output.constData(), output.size(), 1, stdout);
fflush(stdout);
}
if (!m_Exit.isEmpty())
{
// match exit string only at line starts
remainder += output;
while (remainder.length() >= m_Exit.length()) {
if (remainder.startsWith(m_Exit)) {
kill(m_Pid, SIGTERM);
remainder.remove(0, m_Exit.length());
}
int off = remainder.indexOf('\n');
if (off < 0)
break;
remainder.remove(0, off + 1);
}
}
}
}
ret = checkPidExited(m_Pid);
if (ret == Error)
{
if (errno == ECHILD) return 0;
else return 1;
}
else if (ret == Killed)
{
return 0;
}
else if (ret == NotExited)
{
// keep checking
}
else
{
return ret;
}
}
}
/*
* 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()
{
// Reset signal handlers
for (int sig = 1; sig < NSIG; sig++)
KDE_signal(sig, SIG_DFL);
KDE_signal(SIGHUP, SIG_IGN);
d->m_pPTY->setCTty();
// Connect stdin, stdout and stderr
int slave = d->m_pPTY->slaveFd();
dup2(slave, 0); dup2(slave, 1); dup2(slave, 2);
// Close all file handles
// XXX this caused problems in KProcess - not sure why anymore. -- ???
// Because it will close the start notification pipe. -- ossi
struct rlimit rlp;
getrlimit(RLIMIT_NOFILE, &rlp);
for (int i = 3; i < (int)rlp.rlim_cur; i++)
close(i);
// Disable OPOST processing. Otherwise, '\n' are (on Linux at least)
// translated to '\r\n'.
struct ::termios tio;
if (tcgetattr(0, &tio) < 0)
{
kError(kdesuDebugArea()) << "tcgetattr():" << perror;
return -1;
}
tio.c_oflag &= ~OPOST;
if (tcsetattr(0, TCSANOW, &tio) < 0)
{
kError(kdesuDebugArea()) << "tcsetattr():" << perror;
return -1;
}
return 0;
}
}

View file

@ -1,201 +0,0 @@
///////// XXX migrate it to kprocess /////////////////
/* vi: ts=8 sts=4 sw=4
*
* This file is part of the KDE project, module kdesu.
* Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
*
* 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 <QtCore/qbytearray.h>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QList>
#include <kdesu/kdesu_export.h>
#include <kpty.h>
namespace KDESu {
/** \class PtyProcess process.h kdesu/process.h
* 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.
* @return 0 on success, -1 on error. errno might give more information then.
*/
int exec(const QByteArray &command, const QList<QByteArray> &args);
/**
* Reads a line from the program's standard out. Depending on the @em block
* parameter, this call blocks until something was read.
* Note that in some situations this function will return less than a full
* line of output, but never more. Newline characters are stripped.
* @param block Block until a full line is read?
* @return The output string.
*/
QByteArray 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
* (else it will return an empty QByteArray)?
* @return The output.
*/
QByteArray 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 QByteArray &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 QByteArray &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 QByteArray &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);
/**
* Overwrites the password as soon as it is used. Relevant only to
* some subclasses.
*/
void setErase(bool erase);
/**
* Set additinal environment variables.
*/
void setEnvironment( const QList<QByteArray> &env );
/**
* Returns the filedescriptor of the process.
*/
int fd() const;
/**
* Returns the pid of the process.
*/
int pid() const;
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 (i.e. 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);
/** Error return values for checkPidExited() */
enum checkPidStatus { Error=-1, /**< No child */
NotExited=-2, /**< Child hasn't exited */
Killed=-3 /**< Child terminated by signal */
} ;
/**
** Check process exit status for process @p pid.
** If child @p pid has exited, return its exit status,
** (which may be zero).
** On error (no child, no exit), return -1.
** If child @p has not exited, return -2.
*/
static int checkPidExited(pid_t pid);
protected:
QList<QByteArray> environment() const;
bool m_bErase, /**< @see setErase() */
m_bTerminal; /**< Indicates running in a terminal, causes additional
newlines to be printed after output. Set to @c false
in constructor. @see setTerminal() */
int m_Pid; /**< PID of child process */
QByteArray m_Command, /**< Unused */
m_Exit; /**< String to scan for in output that indicates
child has exited. */
private:
int init();
int setupTTY();
private:
class PtyProcessPrivate;
PtyProcessPrivate* const d;
};
}
#endif

View file

@ -1,259 +0,0 @@
/* vi: ts=8 sts=4 sw=4
*
* 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 "ssh.h"
#include "kcookie.h"
#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 <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <QtCore/qglobal.h>
#include <kdebug.h>
#include <klocale.h>
#include <kstandarddirs.h>
extern int kdesuDebugArea();
namespace KDESu {
using namespace KDESuPrivate;
class SshProcess::SshProcessPrivate
{
public:
SshProcessPrivate(const QByteArray &host)
: m_Host( host )
, m_Stub( "kdesu_stub" )
{}
QByteArray m_Prompt;
QByteArray m_Host;
QByteArray m_Error;
QByteArray m_Stub;
};
SshProcess::SshProcess(const QByteArray &host, const QByteArray &user, const QByteArray &command)
: d( new SshProcessPrivate(host) )
{
m_User = user;
m_Command = command;
srand(time(0L));
}
SshProcess::~SshProcess()
{
delete d;
}
void SshProcess::setHost(const QByteArray &host)
{
d->m_Host = host;
}
void SshProcess::setStub(const QByteArray &stub)
{
d->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);
QList<QByteArray> args;
args += "-l"; args += m_User;
args += "-o"; args += "StrictHostKeyChecking=no";
args += d->m_Host; args += d->m_Stub;
if (StubProcess::exec("ssh", args) < 0)
{
return check ? SshNotFound : -1;
}
int ret = ConverseSsh(password, check);
if (ret < 0)
{
if (!check)
kError(kdesuDebugArea()) << "Conversation with ssh failed.";
return ret;
}
if (check == 2)
{
if (ret == 1)
{
kill(m_Pid, SIGTERM);
waitForChild();
}
return ret;
}
if (m_bErase && password)
memset(const_cast<char *>(password), 0, qstrlen(password));
ret = ConverseStub(check);
if (ret < 0)
{
if (!check)
kError(kdesuDebugArea()) << "Conversation with kdesu_stub failed.";
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;
}
QByteArray SshProcess::prompt() const
{
return d->m_Prompt;
}
QByteArray SshProcess::error() const
{
return d->m_Error;
}
/*
* 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;
QByteArray 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)
{
d->m_Prompt = line;
return SshNeedsPassword;
}
if (WaitSlave())
return -1;
write(fd(), password, strlen(password));
write(fd(), "\n", 1);
state++;
break;
}
// Warning/error message.
d->m_Error += line; d->m_Error += '\n';
if (m_bTerminal)
fprintf(stderr, "ssh: %s\n", line.constData());
break;
case 1:
if (line.isEmpty())
{
state++;
break;
}
return -1;
}
}
return 0;
}
// Display redirection is handled by ssh natively.
QByteArray SshProcess::display()
{
return "no";
}
QByteArray SshProcess::displayAuth()
{
return "no";
}
}

View file

@ -1,81 +0,0 @@
/* vi: ts=8 sts=4 sw=4
*
* 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 "stub.h"
#include <kdesu/kdesu_export.h>
namespace KDESu {
/** \class SshProcess ssh.h kdesu/ssh.h
* Executes a remote command, using ssh.
*/
class KDESU_EXPORT SshProcess: public StubProcess
{
public:
explicit SshProcess(const QByteArray &host = QByteArray(),
const QByteArray &user = QByteArray(),
const QByteArray &command = QByteArray());
~SshProcess();
enum Errors { SshNotFound=1, SshNeedsPassword, SshIncorrectPassword };
/**
* Sets the target host.
*/
void setHost(const QByteArray &host);
/**
* Sets the localtion of the remote stub.
*/
void setStub(const QByteArray &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);
QByteArray prompt() const;
QByteArray error() const;
protected:
virtual QByteArray display();
virtual QByteArray displayAuth();
private:
int ConverseSsh(const char *password, int check);
private:
class SshProcessPrivate;
SshProcessPrivate * const d;
};
}
#endif

View file

@ -1,222 +0,0 @@
/* vi: ts=8 sts=4 sw=4
*
* This file is part of the KDE project, module kdesu.
* Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
*
* 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 "stub.h"
#include "kcookie.h"
#include <config.h>
#include <stdlib.h>
#include <unistd.h>
#include <QtCore/qglobal.h>
#include <kdebug.h>
extern int kdesuDebugArea();
namespace KDESu {
using namespace KDESuPrivate;
StubProcess::StubProcess()
: d(0)
{
m_User = "root";
m_Scheduler = SchedNormal;
m_Priority = 50;
m_pCookie = new KCookie;
m_bXOnly = true;
}
StubProcess::~StubProcess()
{
delete m_pCookie;
}
void StubProcess::setCommand(const QByteArray &command)
{
m_Command = command;
}
void StubProcess::setUser(const QByteArray &user)
{
m_User = user;
}
void StubProcess::setXOnly(bool xonly)
{
m_bXOnly = xonly;
}
void StubProcess::setPriority(int prio)
{
if (prio > 100)
m_Priority = 100;
else if (prio < 0)
m_Priority = 0;
else
m_Priority = prio;
}
void StubProcess::setScheduler(int sched)
{
m_Scheduler = sched;
}
void StubProcess::writeString(const QByteArray &str)
{
QByteArray out;
out.reserve(str.size() + 8);
for (int i = 0; i < str.size(); i++) {
uchar c = str.at(i);
if (c < 32) {
out.append('\\');
out.append(c + '@');
} else if (c == '\\') {
out.append('\\');
out.append('/');
} else {
out.append(c);
}
}
writeLine(out);
}
/*
* Map pid_t to a signed integer type that makes sense for QByteArray;
* only the most common sizes 16 bit and 32 bit are special-cased.
*/
template<int T> struct PIDType { typedef pid_t PID_t; } ;
template<> struct PIDType<2> { typedef qint16 PID_t; } ;
template<> struct PIDType<4> { typedef qint32 PID_t; } ;
/*
* Conversation with kdesu_stub. This is how we pass the authentication
* tokens (X11) and other stuff to kdesu_stub.
* return values: -1 = error, 0 = ok, 1 = kill me
*/
int StubProcess::ConverseStub(int check)
{
QByteArray 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");
break;
}
}
while (1)
{
line = readLine();
if (line.isNull())
return -1;
if (line == "display") {
writeLine(display());
} else if (line == "display_auth") {
#ifdef Q_WS_X11
writeLine(displayAuth());
#else
writeLine("");
#endif
} else if (line == "command") {
writeString(m_Command);
} else if (line == "path") {
QByteArray path = qgetenv("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") {
QList<QByteArray> env = environment();
QByteArray tmp;
for(int i = 0; i < env.count(); ++i)
{
const char startup_env[] = "DESKTOP_STARTUP_ID=";
if (env.at(i).startsWith(startup_env))
tmp = env.at(i).mid(sizeof(startup_env) - 1);
}
if( tmp.isEmpty())
tmp = "0"; // krazy:exclude=doublequote_chars
writeLine(tmp);
} else if (line == "app_start_pid") { // obsolete
// Force the pid_t returned from getpid() into
// something QByteArray understands; avoids ambiguity
// between short and unsigned short in particular.
tmp.setNum((PIDType<sizeof(pid_t)>::PID_t)(getpid()));
writeLine(tmp);
} else if (line == "environment") { // additional env vars
QList<QByteArray> env = environment();
for (int i = 0; i < env.count(); ++i)
writeString(env.at(i));
writeLine( "" );
} else if (line == "end") {
return 0;
} else
{
kWarning(kdesuDebugArea()) << "Unknown request:" << line;
return 1;
}
}
return 0;
}
QByteArray StubProcess::display()
{
return m_pCookie->display();
}
#ifdef Q_WS_X11
QByteArray StubProcess::displayAuth()
{
return m_pCookie->displayAuth();
}
#endif
}

View file

@ -1,106 +0,0 @@
/* vi: ts=8 sts=4 sw=4
*
* This file is part of the KDE project, module kdesu.
* Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
*
* 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 <QtCore/qbytearray.h>
#include <QtCore/QList>
#include "process.h"
#include <kdesu/kdesu_export.h>
namespace KDESu {
namespace KDESuPrivate { class KCookie; }
/** \class StubProcess stub.h kdesu/stub.h
* Chat with kdesu_stub.
*
* StubProcess extends PtyProcess with functionality to chat with kdesu_stub.
*/
class KDESU_EXPORT StubProcess: public PtyProcess
{
public:
StubProcess();
~StubProcess();
/**
* Set the command.
*/
void setCommand(const QByteArray &command);
/**
* Set the target user.
*/
void setUser(const QByteArray &user);
/**
* Set to "X only mode": Sycoca is not built and kdeinit is not launched.
*/
void setXOnly(bool xonly);
/**
* 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 dependent 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);
protected:
/**
* Exchange all parameters with kdesu_stub.
*/
int ConverseStub(int check);
/**
* This virtual function can be overloaded when special behavior is
* desired. By default, it returns the value returned by KCookie.
*/
virtual QByteArray display();
#ifdef Q_WS_X11
/**
* See display.
*/
virtual QByteArray displayAuth();
#endif
bool m_bXOnly;
int m_Priority;
int m_Scheduler;
QByteArray m_Command;
QByteArray m_User;
KDESuPrivate::KCookie *m_pCookie;
private:
void writeString(const QByteArray &str);
private:
class StubProcessPrivate;
StubProcessPrivate * const d;
};
}
#endif // __Stub_h_Included__

View file

@ -1,329 +0,0 @@
/* vi: ts=8 sts=4 sw=4
*
* This file is part of the KDE project, module kdesu.
* Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
*
* Sudo support added by Jonathan Riddell <jriddell@ ubuntu.com>
* Copyright (C) 2005 Canonical Ltd // krazy:exclude=copyright (no email)
*
* 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 "su.h"
#include "kcookie.h"
#include <config.h>
#include <config-prefix.h> // for LIBEXEC_INSTALL_DIR
#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 <QtCore/QFile>
#include <kconfig.h>
#include <kconfiggroup.h>
#include <kdebug.h>
#include <klocale.h>
#include <kstandarddirs.h>
#include <kuser.h>
int kdesuDebugArea()
{
static int s_area = KDebug::registerArea("kdesu (kdelibs)");
return s_area;
}
#ifndef __PATH_SU
#define __PATH_SU "false"
#endif
#ifndef __PATH_SUDO
#define __PATH_SUDO "false"
#endif
#ifdef KDESU_USE_SUDO_DEFAULT
# define DEFAULT_SUPER_USER_COMMAND "sudo"
#else
# define DEFAULT_SUPER_USER_COMMAND "su"
#endif
namespace KDESu {
using namespace KDESuPrivate;
class SuProcess::SuProcessPrivate
{
public:
QString m_superUserCommand;
};
SuProcess::SuProcess(const QByteArray &user, const QByteArray &command)
: d( new SuProcessPrivate )
{
m_User = user;
m_Command = command;
KSharedConfig::Ptr config = KGlobal::config();
KConfigGroup group(config, "super-user-command");
d->m_superUserCommand = group.readEntry("super-user-command", DEFAULT_SUPER_USER_COMMAND);
if ( d->m_superUserCommand != "sudo" && d->m_superUserCommand != "su" ) {
kWarning() << "unknown super user command.";
d->m_superUserCommand = DEFAULT_SUPER_USER_COMMAND;
}
}
SuProcess::~SuProcess()
{
delete d;
}
QString SuProcess::superUserCommand()
{
return d->m_superUserCommand;
}
bool SuProcess::useUsersOwnPassword()
{
if (superUserCommand() == "sudo" && m_User == "root") {
return true;
}
KUser user;
return user.loginName() == m_User;
}
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") {
d->m_superUserCommand = "su";
}
QList<QByteArray> args;
if (d->m_superUserCommand == "sudo") {
args += "-u";
}
if ((m_Scheduler != SchedNormal) || (m_Priority > 50))
args += "root";
else
args += m_User;
if (d->m_superUserCommand == "su") {
args += "-c";
}
args += QByteArray(LIBEXEC_INSTALL_DIR) + "/kdesu_stub";
args += "-"; // krazy:exclude=doublequote_chars (QList, not QString)
QByteArray command;
if (d->m_superUserCommand == "sudo") {
command = __PATH_SUDO;
} else {
command = __PATH_SU;
}
if (::access(command, X_OK) != 0)
{
command = QFile::encodeName( KGlobal::dirs()->findExe(d->m_superUserCommand.toLatin1()) );
if (command.isEmpty())
return check ? SuNotFound : -1;
}
// kDebug(kdesuDebugArea()) << "Call StubProcess::exec()";
if (StubProcess::exec(command, args) < 0)
{
return check ? SuNotFound : -1;
}
// kDebug(kdesuDebugArea()) << "Done StubProcess::exec()";
SuErrors ret = (SuErrors) ConverseSU(password);
// kDebug(kdesuDebugArea()) << "Conversation returned" << ret;
if (ret == error)
{
if (!check)
kError(kdesuDebugArea()) << "Conversation with su failed.";
return ret;
}
if (check == NeedPassword)
{
if (ret == killme)
{
if (kill(m_Pid, SIGKILL) < 0) {
// SIGKILL doesn't work for sudo
if (kill(m_Pid, SIGINT) < 0) {
kDebug() << "kill < 0";
ret=error;
}
}
else
{
int iret = waitForChild();
if (iret < 0) ret=error;
else /* nothing */ {} ;
}
}
return ret;
}
if (m_bErase && password)
memset(const_cast<char *>(password), 0, qstrlen(password));
if (ret != ok)
{
kill(m_Pid, SIGKILL);
kill(m_Pid, SIGINT);
waitForChild();
return SuIncorrectPassword;
}
int iret = ConverseStub(check);
if (iret < 0)
{
if (!check)
kError(kdesuDebugArea()) << "Conversation with kdesu_stub failed.";
return iret;
}
else if (iret == 1)
{
kill(m_Pid, SIGKILL);
kill(m_Pid, SIGINT);
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;
// kDebug(kdesuDebugArea()) << "ConverseSU starting.";
QByteArray line;
while (true)
{
line = readLine();
// close your eyes for a sec and use that scroll button
if (line.isNull() || line == "Sorry, try again.")
return ( state == HandleStub ? notauthorized : error);
kDebug(kdesuDebugArea()) << "Read line" << line;
if (line == "kdesu_stub")
{
unreadLine(line);
return ok;
}
switch (state)
{
//////////////////////////////////////////////////////////////////////////
case WaitForPrompt:
{
if (waitMS(fd(),100)>0)
{
// There is more output available, so this 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).
continue;
}
// 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 (WaitSlave())
return error;
write(fd(), password, strlen(password));
write(fd(), "\n", 1);
state = CheckStar;
}
break;
}
//////////////////////////////////////////////////////////////////////////
case CheckStar:
{
QByteArray s = line.trimmed();
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:
break;
//////////////////////////////////////////////////////////////////////////
} // end switch
} // end while (true)
return ok;
}
}

View file

@ -1,74 +0,0 @@
/* vi: ts=8 sts=4 sw=4
*
* This file is part of the KDE project, module kdesu.
* Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
*
* 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 <kdesu/kdesu_export.h>
#include "stub.h"
namespace KDESu {
/** \class SuProcess su.h kdesu/su.h
* Executes a command under elevated privileges, using su.
*/
class KDESU_EXPORT SuProcess: public StubProcess
{
public:
explicit SuProcess(const QByteArray &user=0, const QByteArray &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();
/**
* Checks what the default super user command is, e.g. sudo, su, etc
* @return the default super user command
*/
QString superUserCommand();
/**
* Checks whether or not the user's password is being asked for or another
* user's password. Due to usage of systems such as sudo, even when attempting
* to switch to another user one may need to enter their own password.
*/
bool useUsersOwnPassword();
private:
enum SuErrors { error=-1, ok=0, killme=1, notauthorized=2 } ;
int ConverseSU(const char *password);
private:
class SuProcessPrivate;
SuProcessPrivate * const d;
};
}
#endif

20
kdesudo/CMakeLists.txt Normal file
View file

@ -0,0 +1,20 @@
check_include_files("sys/prctl.h" HAVE_SYS_PRCTL_H)
check_symbol_exists(PR_SET_DUMPABLE "sys/prctl.h" HAVE_PR_SET_DUMPABLE)
add_feature_info("prctl-dumpable" HAVE_PR_SET_DUMPABLE "Used to disallow ptracing")
configure_file(config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h)
# set(kt4_SRC app.cpp mainwindow.cpp core.cpp view.cpp viewmodel.cpp main.cpp)
set(KDESUDO_SRC
main.cpp
kdesudo.cpp
kcookie.cpp
)
add_executable(kdesudo ${KDESUDO_SRC})
target_link_libraries(kdesudo ${KDE4_KDEUI_LIBS})
install(
TARGETS kdesudo
${INSTALL_TARGETS_DEFAULT_ARGS}
)

2
kdesudo/Messages.sh Executable file
View file

@ -0,0 +1,2 @@
#! /usr/bin/env bash
$XGETTEXT `find . -name \*.cpp` -o $podir/kdesudo.pot

2
kdesudo/config.h.cmake Normal file
View file

@ -0,0 +1,2 @@
#cmakedefine01 HAVE_SYS_PRCTL_H
#cmakedefine01 HAVE_PR_SET_DUMPABLE

105
kdesudo/kcookie.cpp Normal file
View file

@ -0,0 +1,105 @@
/* vi: ts=8 sts=4 sw=4
*
* This file is part of the KDE project, module kdesu.
* Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
*
* 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 "kcookie.h"
#include <stdlib.h>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QBool>
#include <QtCore/Q_PID>
#include <kdebug.h>
namespace KDESu
{
namespace KDESuPrivate
{
class KCookie::KCookiePrivate
{
public:
QByteArray m_Display;
#ifdef Q_WS_X11
QByteArray m_DisplayAuth;
#endif
};
KCookie::KCookie()
: d(new KCookiePrivate)
{
#ifdef Q_WS_X11
getXCookie();
#endif
}
KCookie::~KCookie()
{
delete d;
}
QByteArray KCookie::display() const
{
return d->m_Display;
}
#ifdef Q_WS_X11
QByteArray KCookie::displayAuth() const
{
return d->m_DisplayAuth;
}
#endif
void KCookie::getXCookie()
{
#ifdef Q_WS_X11
d->m_Display = getenv("DISPLAY");
#else
d->m_Display = getenv("QWS_DISPLAY");
#endif
if (d->m_Display.isEmpty()) {
kError(900) << "$DISPLAY is not set.\n";
return;
}
#ifdef Q_WS_X11 // No need to mess with X Auth stuff
QByteArray disp = d->m_Display;
if (disp.startsWith("localhost:")) {
disp.remove(0, 9);
}
QProcess proc;
proc.start("xauth", QStringList() << "list" << disp);
if (!proc.waitForStarted()) {
kError(900) << "Could not run xauth.\n";
return;
}
proc.waitForReadyRead(100);
QByteArray output = proc.readLine().simplified();
if (output.isEmpty()) {
kWarning(900) << "No X authentication info set for display " <<
d->m_Display << endl; return;
}
QList<QByteArray> lst = output.split(' ');
if (lst.count() != 3) {
kError(900) << "parse error.\n";
return;
}
d->m_DisplayAuth = (lst[1] + ' ' + lst[2]);
proc.waitForFinished(100); // give QProcess a chance to clean up gracefully
#endif
}
}
}

57
kdesudo/kcookie.h Normal file
View file

@ -0,0 +1,57 @@
/* vi: ts=8 sts=4 sw=4
*
* This file is part of the KDE project, module kdesu
* Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
*
* 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 <QtCore/QByteRef>
namespace KDESu
{
namespace KDESuPrivate
{
/**
* Utility class to access the authentication tokens needed to run a KDE
* program (X11 cookies on X11, for instance).
* @internal
*/
class KCookie
{
public:
KCookie();
~KCookie();
/**
* Returns the X11 display.
*/
QByteArray display() const;
#ifdef Q_WS_X11
/**
* Returns the X11 magic cookie, if available.
*/
QByteArray displayAuth() const;
#endif
private:
void getXCookie();
class KCookiePrivate;
KCookiePrivate *const d;
};
}
}
#endif // __KCookie_h_Included__

387
kdesudo/kdesudo.cpp Normal file
View file

@ -0,0 +1,387 @@
/***************************************************************************
kdesudo.cpp - the implementation of the
admin granting sudo widget
-------------------
begin : Sam Feb 15 15:42:12 CET 2003
copyright : (C) 2003 by Robert Gruber
<rgruber@users.sourceforge.net>
(C) 2007 by Martin Böhm <martin.bohm@kubuntu.org>
Anthony Mercatante <tonio@kubuntu.org>
Canonical Ltd (Jonathan Riddell
<jriddell@ubuntu.com>)
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "kdesudo.h"
#include <QtCore/QDataStream>
#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QtCore/QProcess>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QTemporaryFile>
#include <QtCore/QTextCodec>
#include <KApplication>
#include <KCmdLineArgs>
#include <KDebug>
#include <KLocale>
#include <KMessageBox>
#include <KPasswordDialog>
#include <KPushButton>
#include <KShell>
#include <KStandardDirs>
#include <KWindowSystem>
#include <sys/stat.h>
#include <sys/types.h>
#include <cstdio>
#include <cstdlib>
#include <csignal>
KdeSudo::KdeSudo(const QString &icon, const QString &appname) :
QObject(),
m_process(0),
m_error(false),
m_pCookie(new KDESu::KDESuPrivate::KCookie)
{
KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
bool realtime = args->isSet("r");
bool priority = args->isSet("p");
bool showCommand = (!args->isSet("d"));
bool changeUID = true;
bool noExec = false;
QString runas = args->getOption("u");
QString cmd;
int winid = -1;
bool attach = args->isSet("attach");
m_dialog = new KPasswordDialog;
m_dialog->setDefaultButton(KDialog::Ok);
if (attach) {
winid = args->getOption("attach").toInt(&attach, 0);
KWindowSystem::setMainWindow(m_dialog, (WId)winid);
}
if (!args->isSet("c") && !args->count()) {
KMessageBox::information(0, i18n("No command arguments supplied!\n"
"Usage: kdesudo [-u <runas>] <command>\n"
"KdeSudo will now exit...")
);
noExec = true;
}
m_process = new QProcess;
/* load the icon */
m_dialog->setPixmap(icon);
// Parsins args
/* Get the comment out of cli args */
QByteArray commentBytes = args->getOption("comment").toUtf8();
QTextCodec *tCodecConv = QTextCodec::codecForLocale();
QString comment = tCodecConv->toUnicode(commentBytes, commentBytes.size());
if (args->isSet("f")) {
// If file is writeable, do not change uid
QString file = args->getOption("f");
if (!file.isEmpty()) {
if (file.at(0) != '/') {
KStandardDirs dirs;
file = dirs.findResource("config", file);
if (file.isEmpty()) {
kWarning(1206) << "Config file not found: " << file << "\n";
exit(1);
}
}
QFileInfo fi(file);
if (!fi.exists()) {
kWarning(1206) << "File does not exist: " << file << "\n";
exit(1);
}
if (fi.isWritable()) {
changeUID = false;
}
}
}
connect(m_process, SIGNAL(readyReadStandardOutput()),
this, SLOT(parseOutput()));
connect(m_process, SIGNAL(readyReadStandardError()),
this, SLOT(parseOutput()));
connect(m_process, SIGNAL(finished(int)),
this, SLOT(procExited(int)));
connect(m_dialog, SIGNAL(gotPassword(const QString & , bool)),
this, SLOT(pushPassword(const QString &)));
connect(m_dialog, SIGNAL(rejected()),
this, SLOT(slotCancel()));
// Generate the xauth cookie and put it in a tempfile
// set the environment variables to reflect that.
// Default cookie-timeout is 60 sec. .
// 'man xauth' for more info on xauth cookies.
QTemporaryFile *tmpFile = new QTemporaryFile("/tmp/kdesudo-XXXXXX-xauth");
tmpFile->open();
QString m_tmpName = tmpFile->fileName();
delete tmpFile;
QByteArray disp = m_pCookie->display();
// Create two processes, one for each xauth call
QProcess xauth_ext;
QProcess xauth_merge;
// This makes "xauth extract - $DISPLAY | xauth -f /tmp/kdesudo-... merge -"
xauth_ext.setStandardOutputProcess(&xauth_merge);
// Start the first
xauth_ext.start("xauth", QStringList() << "extract" << "-" << QString::fromLocal8Bit(disp), QIODevice::ReadOnly);
if (!xauth_ext.waitForStarted()) {
return;
}
// Start the second
xauth_merge.start("xauth", QStringList() << "-f" << m_tmpName << "merge" << "-", QIODevice::WriteOnly);
if (!xauth_merge.waitForStarted()) {
return;
}
// If they ended, close it all
if (!xauth_merge.waitForFinished()) {
return;
}
xauth_merge.close();
if (!xauth_ext.waitForFinished()) {
return;
}
xauth_ext.close();
// non root users need to be able to read the xauth file.
// the xauth file is deleted when kdesudo exits. security?
QFile tf;
tf.setFileName(m_tmpName);
if (!runas.isEmpty() && runas != "root" && tf.exists()) {
chmod(QFile::encodeName(m_tmpName), 0644);
}
QProcessEnvironment processEnv = QProcessEnvironment::systemEnvironment();
processEnv.insert("DISPLAY", disp);
processEnv.insert("XAUTHORITY", m_tmpName);
m_process->setProcessEnvironment(processEnv);
QStringList processArgs;
{
// Do not cache credentials to avoid security risks caused by the fact
// that kdesudo could be invoked from anyting inside the user session
// potentially in such a way that it uses the cached credentials of a
// previously kdesudo run in that same scope.
processArgs << "-k";
if (changeUID) {
processArgs << "-H" << "-S" << "-p" << "passprompt";
if (!runas.isEmpty()) {
processArgs << "-u" << runas;
}
processArgs << "--";
}
if (realtime) {
processArgs << "nice" << "-n" << "10";
m_dialog->addCommentLine(i18n("Priority:"), i18n("realtime:") +
QChar(' ') + QString("50/100"));
processArgs << "--";
} else if (priority) {
QString n = args->getOption("p");
int intn = atoi(n.toUtf8());
intn = (intn * 40 / 100) - (20 + 0.5);
QString strn;
strn.sprintf("%d", intn);
processArgs << "nice" << "-n" << strn;
m_dialog->addCommentLine(i18n("Priority:"), n + QString("/100"));
processArgs << "--";
}
if (args->isSet("c")) {
QString command = args->getOption("c");
cmd += command;
processArgs << "sh";
processArgs << "-c";
processArgs << command;
}
else if (args->count()) {
for (int i = 0; i < args->count(); i++) {
if ((!args->isSet("c")) && (i == 0)) {
QStringList argsSplit = KShell::splitArgs(args->arg(i));
for (int j = 0; j < argsSplit.count(); j++) {
processArgs << validArg(argsSplit[j]);
if (j == 0) {
cmd += validArg(argsSplit[j]) + QChar(' ');
} else {
cmd += KShell::quoteArg(validArg(argsSplit[j])) + QChar(' ');
}
}
} else {
processArgs << validArg(args->arg(i));
cmd += validArg(args->arg(i)) + QChar(' ');
}
}
}
// strcmd needs to be defined
if (showCommand && !cmd.isEmpty()) {
m_dialog->addCommentLine(i18n("Command:"), cmd);
}
}
if (comment.isEmpty()) {
QString defaultComment = "<b>%1</b> " + i18n("needs administrative privileges. ");
if (runas.isEmpty() || runas == "root") {
defaultComment += i18n("Please enter your password.");
} else {
defaultComment += i18n("Please enter password for <b>%1</b>.", runas);
}
if (!appname.isEmpty()) {
m_dialog->setPrompt(defaultComment.arg(appname));
} else {
m_dialog->setPrompt(defaultComment.arg(cmd));
}
} else {
m_dialog->setPrompt(comment);
}
m_process->setProcessChannelMode(QProcess::MergedChannels);
if (noExec) {
exit(0);
} else {
m_process->start("sudo", processArgs);
}
}
KdeSudo::~KdeSudo()
{
delete m_dialog;
}
void KdeSudo::error(const QString &msg)
{
m_error = true;
KMessageBox::error(0, msg);
KApplication::kApplication()->exit(1);
}
void KdeSudo::parseOutput()
{
QString strOut = m_process->readAllStandardOutput();
static int badpass = 0;
if (strOut.contains("try again")) {
badpass++;
if (badpass == 1) {
m_dialog->addCommentLine(i18n("<b>Warning: </b>"), i18n("<b>Incorrect password, please try again.</b>"));
m_dialog->show();
} else if (badpass == 2) {
m_dialog->show();
} else {
error(i18n("Wrong password! Exiting..."));
}
} else if (strOut.contains("command not found")) {
error(i18n("Command not found!"));
} else if (strOut.contains("is not in the sudoers file")) {
error(i18n("Your username is unknown to sudo!"));
} else if (strOut.contains("is not allowed to execute")) {
error(i18n("Your user is not allowed to run the specified command!"));
} else if (strOut.contains("is not allowed to run sudo on")) {
error(i18n("Your user is not allowed to run sudo on this host!"));
} else if (strOut.contains("may not run sudo on")) {
error(i18n("Your user is not allowed to run sudo on this host!"));
} else if ((strOut.contains("passprompt")) || (strOut.contains("PIN (CHV2)"))) {
m_dialog->setPassword(QString());
m_dialog->show();
} else {
fprintf(stdout, "%s", strOut.toLocal8Bit().constData());
}
}
void KdeSudo::procExited(int exitCode)
{
if (!m_error) {
if (!m_tmpName.isEmpty()) {
QFile::remove(m_tmpName);
}
}
KApplication::kApplication()->exit(exitCode);
}
void KdeSudo::pushPassword(const QString &pwd)
{
m_process->write(pwd.toLocal8Bit() + "\n");
}
void KdeSudo::slotCancel()
{
KApplication::kApplication()->exit(1);
}
void KdeSudo::slotUser1()
{
m_dialog->done(AsUser);
}
void KdeSudo::blockSigChild()
{
sigset_t sset;
sigemptyset(&sset);
sigaddset(&sset, SIGCHLD);
sigprocmask(SIG_BLOCK, &sset, 0L);
}
void KdeSudo::unblockSigChild()
{
sigset_t sset;
sigemptyset(&sset);
sigaddset(&sset, SIGCHLD);
sigprocmask(SIG_UNBLOCK, &sset, 0L);
}
QString KdeSudo::validArg(QString arg)
{
QChar firstChar = arg.at(0);
QChar lastChar = arg.at(arg.length() - 1);
if ((firstChar == '"' && lastChar == '"') || (firstChar == '\'' && lastChar == '\'')) {
arg = arg.remove(0, 1);
arg = arg.remove(arg.length() - 1, 1);
}
return arg;
}

94
kdesudo/kdesudo.h Normal file
View file

@ -0,0 +1,94 @@
/***************************************************************************
kdesudo.cpp - description
-------------------
begin : Sam Feb 15 15:42:12 CET 2003
copyright : (C) 2003 by Robert Gruber <rgruber@users.sourceforge.net>
(C) 2007 by Martin Böhm <martin.bohm@kubuntu.org>
Anthony Mercatante <tonio@kubuntu.org>
Canonical Ltd (Jonathan Riddell <jriddell@ubuntu.com>)
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef KDESUDO_H
#define KDESUDO_H
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <QtGui/QWidget>
#include <QtCore/QString>
#include <QProcess>
#include <KPasswordDialog>
#include <knewpassworddialog.h>
#include "kcookie.h"
/*
* KdeSudo is the base class of the project
*
* @version 3.1
*/
/* buffer is used when reading from the QProcess child */
#define BUFSIZE 1024
class KdeSudo : QObject
{
Q_OBJECT
public:
KdeSudo(const QString &icon = QString(), const QString &generic = QString());
~KdeSudo();
enum ResultCodes {
AsUser = 10
};
private slots:
/**
* This slot gets executed if sudo creates some output
* -- well, in theory it should. Even though the code
* seems to be doing what the API says, it doesn't
* yet do what we need.
**/
void parseOutput();
/**
* This slot gets exectuted when sudo exits
**/
void procExited(int exitCode);
/**
* This slot overrides the slot from KPasswordDialog
* @see KPasswordDialog
**/
void pushPassword(const QString &);
void slotCancel();
void slotUser1();
QString validArg(QString arg);
private:
void error(const QString &);
QProcess *m_process;
bool m_error;
bool useTerm;
bool noExec;
QString m_tmpName;
QString iceauthorityFile;
KDESu::KDESuPrivate::KCookie *m_pCookie;
void blockSigChild();
void unblockSigChild();
KPasswordDialog *m_dialog;
};
#endif // KDESUDO_H

160
kdesudo/main.cpp Normal file
View file

@ -0,0 +1,160 @@
/***************************************************************************
kdesudo.cpp - description
-------------------
begin : Sam Feb 15 15:42:12 CET 2003
copyright : (C) 2003 by Robert Gruber
<rgruber@users.sourceforge.net>
(C) 2007 by Martin Böhm <martin.bohm@kubuntu.org>
Anthony Mercatante <tonio@kubuntu.org>
Canonical Ltd (Jonathan Riddell
<jriddell@ubuntu.com>)
(C) 2009-2015 by Harald Sitter <sitter@kde.org>
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include <KCmdLineArgs>
#include <KAboutData>
#include <KLocale>
#include <KMessageBox>
#include <KDesktopFile>
#include <KIconLoader>
#include <KIconTheme>
#include <KGlobal>
#include <KStandardDirs>
#include <KProcess>
#include <KDebug>
#include <KApplication>
#include <config.h>
#if HAVE_SYS_PRCTL_H
#include <sys/prctl.h>
#endif
#include "kdesudo.h"
int main(int argc, char **argv)
{
// Disable ptrace to prevent arbitrary apps reading password out of memory.
#if HAVE_PR_SET_DUMPABLE
prctl(PR_SET_DUMPABLE, 0);
#endif
KAboutData about(
"kdesudo", 0, ki18n("KdeSudo"),
"3.4.2.3", ki18n("Sudo frontend for KDE"),
KAboutData::License_GPL,
ki18n("(C) 2007 - 2008 Anthony Mercatante"),
KLocalizedString(),
"https://code.launchpad.net/kdesudo/");
about.setBugAddress("https://launchpad.net/kdesudo/+filebug");
about.addAuthor(ki18n("Robert Gruber"), KLocalizedString(),
"rgruber@users.sourceforge.net", "http://test.com");
about.addAuthor(ki18n("Anthony Mercatante"), KLocalizedString(),
"tonio@ubuntu.com");
about.addAuthor(ki18n("Martin Böhm"), KLocalizedString(),
"martin.bohm@kubuntu.org");
about.addAuthor(ki18n("Jonathan Riddell"), KLocalizedString(),
"jriddell@ubuntu.com");
about.addAuthor(ki18n("Harald Sitter"), KLocalizedString(),
"apachelogger@ubuntu.com");
KCmdLineArgs::init(argc, argv, &about);
KCmdLineOptions options;
options.add("u <runas>", ki18n("sets a runas user"));
options.add("c <command>", ki18n("The command to execute"));
options.add("s", ki18n("Fake option for compatibility"));
options.add("i <icon name>", ki18n("Specify icon to use in the password"
" dialog"));
options.add("d", ki18n("Do not show the command to be run in the dialog"));
options.add("p <priority>", ki18n("Process priority, between 0 and 100,"
" 0 the lowest [50]"));
options.add("r", ki18n("Use realtime scheduling"));
options.add("f <file>", ki18n("Use target UID if <file> is not writeable"));
options.add("t", ki18n("Fake option for KDE's KdeSu compatibility"));
options.add("n", ki18n("Fake option for compatibility"));
options.add("nonewdcop", ki18n("Use existing DCOP server"));
options.add("comment <dialog text>", ki18n("The comment that should be "
"displayed in the dialog"));
options.add("noignorebutton", ki18n("Do not display « ignore » button"));
options.add("attach <winid>", ki18n("Makes the dialog transient for an X app specified by winid"));
options.add("desktop <desktop file>", ki18n("Manual override for "
"automatic desktop file detection"));
options.add("+command", ki18n("The command to execute"));
KCmdLineArgs::addCmdLineOptions(options);
KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
KApplication a;
QString executable, arg, command, icon;
QStringList executableList, commandlist;
KDesktopFile *desktopFile;
if (args->isSet("c")) {
executable = args->getOption("c");
}
if (args->count() && executable.isEmpty()) {
command = args->arg(0);
commandlist = command.split(" ");
executable = commandlist[0];
}
/* We have to make sure the executable is only the binary name */
executableList = executable.split(" ");
executable = executableList[0];
executableList = executable.split("/");
executable = executableList[executableList.count() - 1];
/* Kubuntu has a bug in it - this is a workaround for it */
KGlobal::dirs()->addResourceDir("apps", "/usr/share/applications/kde");
KGlobal::dirs()->addResourceDir("apps", "/usr/share/applications/kde4");
KGlobal::dirs()->addResourceDir("apps", "/usr/share/kde/services");
KGlobal::dirs()->addResourceDir("apps", "/usr/share/kde4/services");
KGlobal::dirs()->addResourceDir("apps", "/usr/share/applications");
KGlobal::dirs()->addResourceDir("apps", "/usr/share/applnk");
QString path = getenv("PATH");
QStringList pathList = path.split(":");
for (int i = 0; i < pathList.count(); i++) {
executable.remove(pathList[i]);
}
if (args->isSet("desktop")) {
desktopFile = new KDesktopFile(args->getOption("desktop"));
} else {
desktopFile = new KDesktopFile(executable + ".desktop");
}
/* icon parsing */
if (args->isSet("i")) {
icon = args->getOption("i");
} else {
QString iconName = desktopFile->readIcon();
KIconLoader *loader = KIconLoader::global();
icon = loader->iconPath(iconName, -1 * KIconLoader::StdSizes(
KIconLoader::SizeHuge), true);
}
/* generic name parsing */
QString name = desktopFile->readName();
a.setQuitOnLastWindowClosed(false);
KdeSudo kdesudo(icon, name);
return a.exec();
}

View file

@ -63,7 +63,7 @@ DEALINGS IN THE SOFTWARE.
static const char* const NET_STARTUP_MSG = "_NET_STARTUP_INFO";
static const char* const NET_STARTUP_WINDOW = "_NET_STARTUP_ID";
// DESKTOP_STARTUP_ID is used also in kinit/wrapper.c ,
// kdesu in both kdelibs and kdebase and who knows where else
// kdesudo in both kdelibs and kdebase and who knows where else
static const char* const NET_STARTUP_ENV = "DESKTOP_STARTUP_ID";
static bool auto_app_started_sending = true;

View file

@ -1420,15 +1420,15 @@ static void setupX()
XSetIOErrorHandler(kdeinit_xio_errhandler);
XSetErrorHandler(kdeinit_x_errhandler);
/*
Handle the tricky case of running via kdesu/su/sudo/etc. There the usual case
is that kdesu (etc.) creates a file with xauth information, sets XAUTHORITY,
Handle the tricky case of running via kdesudo/su/sudo/etc. There the usual case
is that kdesudo (etc.) creates a file with xauth information, sets XAUTHORITY,
runs the command and removes the xauth file after the command finishes. However,
dbus and kdeinit daemon currently don't clean up properly and keeping running.
Which means that running a KDE app via kdesu the second time talks to kdeinit
Which means that running a KDE app via kdesudo the second time talks to kdeinit
with obsolete xauth information, which makes it unable to connect to X or launch
any X11 applications.
Even fixing the cleanup probably wouldn't be sufficient, since it'd be possible to
launch one kdesu session, another one, exit the first one and the app from the second
launch one kdesudo session, another one, exit the first one and the app from the second
session would be using kdeinit from the first one.
So the trick here is to duplicate the xauth file to another file in KDE's tmp
location, make the file have a consistent name so that future sessions will use it

View file

@ -199,7 +199,7 @@ void KFileSharePropsPlugin::slotConfigureFileSharing()
if (d->m_configProc) return;
d->m_configProc = new QProcess(this);
if (!d->m_configProc->startDetached(KStandardDirs::findExe("kdesu"), QStringList() << "kcmshell4" << "fileshare"))
if (!d->m_configProc->startDetached(KStandardDirs::findExe("kdesudo"), QStringList() << "kcmshell4" << "fileshare"))
{
delete d->m_configProc;
d->m_configProc = 0;

View file

@ -440,8 +440,8 @@ QStringList KRun::processDesktopExec(const KService &_service, const KUrl::List&
2 << split(term) << "-e" << split(cmd)
3 << split(term) << "-e" << "sh" << "-c" << cmd
4 << "kdesu" << "-u" << user << "-c" << cmd
5 << "kdesu" << "-u" << user << "-c" << ("sh -c " + quote(cmd))
4 << "kdesudo" << "-u" << user << "-c" << cmd
5 << "kdesudo" << "-u" << user << "-c" << ("sh -c " + quote(cmd))
6 << split(term) << "-e" << "su" << user << "-c" << cmd
7 << split(term) << "-e" << "su" << user << "-c" << ("sh -c " + quote(cmd))
@ -484,7 +484,7 @@ QStringList KRun::processDesktopExec(const KService &_service, const KUrl::List&
result << "su";
}
else {
result << KStandardDirs::findExe("kdesu") << "-u";
result << KStandardDirs::findExe("kdesudo") << "-u";
}
result << _service.username() << "-c";

View file

@ -111,8 +111,8 @@ void KRunUnitTest::testProcessDesktopExec()
"/bin/sh -c 'echo $PWD '", // 1
"x-term -T ' - just_a_test' -e /bin/date -u", // 2
"x-term -T ' - just_a_test' -e /bin/sh -c 'echo $PWD '", // 3
/* kdesu */ " -u sprallo -c '/bin/date -u'", // 4
/* kdesu */ " -u sprallo -c '/bin/sh -c '\\''echo $PWD '\\'''", // 5
/* kdesudo */ " -u sprallo -c '/bin/date -u'", // 4
/* kdesudo */ " -u sprallo -c '/bin/sh -c '\\''echo $PWD '\\'''", // 5
"x-term -T ' - just_a_test' -e su sprallo -c '/bin/date -u'", // 6
"x-term -T ' - just_a_test' -e su sprallo -c '/bin/sh -c '\\''echo $PWD '\\'''", // 7
};
@ -131,9 +131,9 @@ void KRunUnitTest::testProcessDesktopExec()
int pt = ex+te*2+su*4;
QString exe;
if (pt == 4 || pt == 5) {
exe = KStandardDirs::findExe("kdesu");
exe = KStandardDirs::findExe("kdesudo");
if (exe.isEmpty()) {
qWarning() << "kdesu not found, skipping test";
qWarning() << "kdesudo not found, skipping test";
continue;
}
}

View file

@ -46,7 +46,7 @@
- Two Layout problems in runAsRoot:
* lblBusy doesn't show
* d->kcm/d->rootInfo doesn't get it right when the user
presses cancel in the kdesu dialog
presses cancel in the kdesudo dialog
- Resizing horizontally is contrained; minimum size is set somewhere.
It appears to be somehow derived from the module's size.

View file

@ -350,6 +350,7 @@ void KCMultiDialog::slotHelpClicked()
}
}
#warning this can make use of KToolInvocation::invokeHelp()
KUrl docUrl( KUrl( "help:/" ), docPath );
KToolInvocation::invokeBrowser( docUrl.url() );
}