You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
424 lines
12 KiB
C++
424 lines
12 KiB
C++
/*
|
|
*
|
|
* This file is part of the KDE project, module tdesu.
|
|
* Copyright (C) 1998 Pietro Iglio <iglio@fub.it>
|
|
* Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <pwd.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <sys/time.h>
|
|
#include <sys/resource.h>
|
|
#if defined(HAVE_SYS_WAIT_H)
|
|
#include <sys/wait.h>
|
|
#endif
|
|
|
|
#include <tqstring.h>
|
|
#include <tqfileinfo.h>
|
|
#include <tqglobal.h>
|
|
#include <tqfile.h>
|
|
#include <tqdir.h>
|
|
|
|
#include <dcopclient.h>
|
|
|
|
#include <kdebug.h>
|
|
#include <tdeglobal.h>
|
|
#include <tdeapplication.h>
|
|
#include <kstandarddirs.h>
|
|
#include <tdeconfig.h>
|
|
#include <tdelocale.h>
|
|
#include <tdeaboutdata.h>
|
|
#include <tdecmdlineargs.h>
|
|
#include <tdemessagebox.h>
|
|
#include <krun.h>
|
|
#include <kuser.h>
|
|
|
|
#include <tdesu/defaults.h>
|
|
#include <tdesu/su.h>
|
|
#include <tdesu/client.h>
|
|
|
|
#include "sudlg.h"
|
|
|
|
#define ERR strerror(errno)
|
|
|
|
TQCString command;
|
|
const char *Version = "1.0";
|
|
|
|
// NOTE: if you change the position of the -u switch, be sure to adjust it
|
|
// at the beginning of main()
|
|
static TDECmdLineOptions options[] = {
|
|
{ "+command", I18N_NOOP("Specifies the command to run"), 0 },
|
|
{ "c <command>", I18N_NOOP("Specifies the command to run"), "" },
|
|
{ "f <file>", I18N_NOOP("Run command under target uid if <file> is not writable"), "" },
|
|
{ "u <user>", I18N_NOOP("Specifies the target uid"), "root" },
|
|
{ "n", I18N_NOOP("Do not keep password"), 0 },
|
|
{ "s", I18N_NOOP("Stop the daemon (forgets all passwords)"), 0 },
|
|
{ "t", I18N_NOOP("Enable terminal output (no password keeping)"), 0 },
|
|
{ "p <prio>", I18N_NOOP("Set priority value: 0 <= prio <= 100, 0 is lowest"), "50" },
|
|
{ "r", I18N_NOOP("Use realtime scheduling"), 0 },
|
|
{ "nonewdcop", I18N_NOOP("Let command use existing dcopserver"), 0 },
|
|
{ "comment <comment>", I18N_NOOP("Ignored"), "" },
|
|
{ "ignorebutton", I18N_NOOP("Display the ignore button"), 0 },
|
|
{ "i <icon name>", I18N_NOOP("Specify icon to use in the password dialog"), 0},
|
|
{ "d", I18N_NOOP("Do not show the command to be run in the dialog"), 0},
|
|
TDECmdLineLastOption
|
|
};
|
|
|
|
|
|
TQCString dcopNetworkId()
|
|
{
|
|
TQCString result;
|
|
result.resize(1025);
|
|
TQFile file(DCOPClient::dcopServerFile());
|
|
if (!file.open(IO_ReadOnly))
|
|
return "";
|
|
int i = file.readLine(result.data(), 1024);
|
|
if (i <= 0)
|
|
return "";
|
|
result.data()[i-1] = '\0'; // strip newline
|
|
return result;
|
|
}
|
|
|
|
static int startApp();
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
// FIXME: this can be considered a poor man's solution, as it's not
|
|
// directly obvious to a gui user. :)
|
|
// anyway, i vote against removing it even when we have a proper gui
|
|
// implementation. -- ossi
|
|
const char *duser = ::getenv("ADMIN_ACCOUNT");
|
|
if (duser && duser[0])
|
|
options[3].def = duser;
|
|
|
|
TDEAboutData aboutData("tdesu", I18N_NOOP("TDE su"),
|
|
Version, I18N_NOOP("Runs a program with elevated privileges."),
|
|
TDEAboutData::License_Artistic,
|
|
"Copyright (c) 1998-2000 Geert Jansen, Pietro Iglio");
|
|
aboutData.addAuthor("Geert Jansen", I18N_NOOP("Maintainer"),
|
|
"jansen@kde.org", "http://www.stack.nl/~geertj/");
|
|
aboutData.addAuthor("Pietro Iglio", I18N_NOOP("Original author"),
|
|
"iglio@fub.it");
|
|
|
|
TDECmdLineArgs::init(argc, argv, &aboutData);
|
|
TDECmdLineArgs::addCmdLineOptions(options);
|
|
TDEApplication::disableAutoDcopRegistration();
|
|
// tdesu doesn't process SM events, so don't even connect to ksmserver
|
|
TQCString session_manager = getenv( "SESSION_MANAGER" );
|
|
unsetenv( "SESSION_MANAGER" );
|
|
TDEApplication app;
|
|
// but propagate it to the started app
|
|
if (session_manager.data())
|
|
{
|
|
setenv( "SESSION_MANAGER", session_manager.data(), 1 );
|
|
}
|
|
|
|
{
|
|
TDEStartupInfoId id;
|
|
id.initId( kapp->startupId());
|
|
id.setupStartupEnv(); // make DESKTOP_STARTUP_ID env. var. available again
|
|
}
|
|
|
|
int result = startApp();
|
|
|
|
if (result == 127)
|
|
{
|
|
KMessageBox::sorry(0, i18n("Command '%1' not found.").arg(static_cast<const char *>(command)));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static int startApp()
|
|
{
|
|
TDECmdLineArgs *args = TDECmdLineArgs::parsedArgs();
|
|
// Stop daemon and exit?
|
|
if (args->isSet("s"))
|
|
{
|
|
TDEsuClient client;
|
|
if (client.ping() == -1)
|
|
{
|
|
kdError(1206) << "Daemon not running -- nothing to stop\n";
|
|
exit(1);
|
|
}
|
|
if (client.stopServer() != -1)
|
|
{
|
|
kdDebug(1206) << "Daemon stopped\n";
|
|
exit(0);
|
|
}
|
|
kdError(1206) << "Could not stop daemon\n";
|
|
exit(1);
|
|
}
|
|
|
|
TQString icon;
|
|
if ( args->isSet("i"))
|
|
icon = args->getOption("i");
|
|
|
|
bool prompt = true;
|
|
if ( args->isSet("d"))
|
|
prompt = false;
|
|
|
|
// Get target uid
|
|
TQCString user = args->getOption("u");
|
|
TQCString auth_user = user;
|
|
struct passwd *pw = getpwnam(user);
|
|
if (pw == 0L)
|
|
{
|
|
kdError(1206) << "User " << user << " does not exist\n";
|
|
exit(1);
|
|
}
|
|
bool change_uid = (getuid() != pw->pw_uid);
|
|
|
|
// If file is writeable, do not change uid
|
|
TQString file = TQFile::decodeName(args->getOption("f"));
|
|
if (change_uid && !file.isEmpty())
|
|
{
|
|
if (file.at(0) != '/')
|
|
{
|
|
TDEStandardDirs dirs;
|
|
dirs.addKDEDefaults();
|
|
file = dirs.findResource("config", file);
|
|
if (file.isEmpty())
|
|
{
|
|
kdError(1206) << "Config file not found: " << file << "\n";
|
|
exit(1);
|
|
}
|
|
}
|
|
TQFileInfo fi(file);
|
|
if (!fi.exists())
|
|
{
|
|
kdError(1206) << "File does not exist: " << file << "\n";
|
|
exit(1);
|
|
}
|
|
change_uid = !fi.isWritable();
|
|
}
|
|
|
|
// Get priority/scheduler
|
|
TQCString tmp = args->getOption("p");
|
|
bool ok;
|
|
int priority = tmp.toInt(&ok);
|
|
if (!ok || (priority < 0) || (priority > 100))
|
|
{
|
|
TDECmdLineArgs::usage(i18n("Illegal priority: %1").arg(static_cast<const char *>(tmp)));
|
|
exit(1);
|
|
}
|
|
int scheduler = SuProcess::SchedNormal;
|
|
if (args->isSet("r"))
|
|
scheduler = SuProcess::SchedRealtime;
|
|
if ((priority > 50) || (scheduler != SuProcess::SchedNormal))
|
|
{
|
|
change_uid = true;
|
|
auth_user = "root";
|
|
}
|
|
|
|
// Get command
|
|
if (args->isSet("c")) {
|
|
command = args->getOption("c");
|
|
}
|
|
else {
|
|
if( args->count() ) {
|
|
command = args->arg(0);
|
|
}
|
|
}
|
|
if (command.stripWhiteSpace().isEmpty()) {
|
|
TDECmdLineArgs::usage(i18n("No command specified."));
|
|
exit(1);
|
|
}
|
|
for (int i= args->isSet("c") ? 0 : 1; i<args->count(); i++) {
|
|
TQString arg = TQFile::decodeName(args->arg(i));
|
|
KRun::shellQuote(arg);
|
|
command += " ";
|
|
command += TQFile::encodeName(arg);
|
|
}
|
|
|
|
// Don't change uid if we're don't need to.
|
|
if (!change_uid)
|
|
{
|
|
int result = system(command);
|
|
result = WEXITSTATUS(result);
|
|
return result;
|
|
}
|
|
|
|
// Check for daemon and start if necessary
|
|
bool just_started = false;
|
|
bool have_daemon = true;
|
|
TDEsuClient client;
|
|
if (!client.isServerSGID())
|
|
{
|
|
kdWarning(1206) << "Daemon not safe (not sgid), not using it.\n";
|
|
have_daemon = false;
|
|
}
|
|
else if (client.ping() == -1)
|
|
{
|
|
if (client.startServer() == -1)
|
|
{
|
|
kdWarning(1206) << "Could not start daemon, reduced functionality.\n";
|
|
have_daemon = false;
|
|
}
|
|
just_started = true;
|
|
}
|
|
|
|
// Try to exec the command with tdesud.
|
|
bool keep = !args->isSet("n") && have_daemon;
|
|
bool terminal = args->isSet("t");
|
|
bool new_dcop = args->isSet("newdcop");
|
|
bool withIgnoreButton = args->isSet("ignorebutton");
|
|
|
|
QCStringList env;
|
|
TQCString options;
|
|
env << ( "DESKTOP_STARTUP_ID=" + kapp->startupId());
|
|
|
|
if (pw->pw_uid)
|
|
{
|
|
// Only propagate TDEHOME for non-root users,
|
|
// root uses TDEROOTHOME
|
|
|
|
// Translate the TDEHOME of this user to the new user.
|
|
TQString kdeHome = TDEGlobal::dirs()->relativeLocation("home", TDEGlobal::dirs()->localtdedir());
|
|
if (kdeHome[0] != '/')
|
|
kdeHome.prepend("~/");
|
|
else
|
|
kdeHome=TQString::null; // Use default
|
|
|
|
env << ("TDEHOME="+ TQFile::encodeName(kdeHome));
|
|
}
|
|
|
|
KUser u;
|
|
env << (TQCString) ("TDESU_USER=" + u.loginName().local8Bit());
|
|
|
|
if (!new_dcop)
|
|
{
|
|
TQCString tdesycoca = "TDESYCOCA="+TQFile::encodeName(locateLocal("cache", "tdesycoca"));
|
|
env << tdesycoca;
|
|
|
|
options += "xf"; // X-only, dcop forwarding enabled.
|
|
}
|
|
|
|
if (keep && !terminal && !just_started)
|
|
{
|
|
client.setPriority(priority);
|
|
client.setScheduler(scheduler);
|
|
int result = client.exec(command, user, options, env);
|
|
if (result == 0)
|
|
{
|
|
result = client.exitCode();
|
|
return result;
|
|
}
|
|
}
|
|
|
|
// Set core dump size to 0 because we will have
|
|
// root's password in memory.
|
|
struct rlimit rlim;
|
|
rlim.rlim_cur = rlim.rlim_max = 0;
|
|
if (setrlimit(RLIMIT_CORE, &rlim))
|
|
{
|
|
kdError(1206) << "rlimit(): " << ERR << "\n";
|
|
exit(1);
|
|
}
|
|
|
|
// Read configuration
|
|
TDEConfig *config = TDEGlobal::config();
|
|
config->setGroup("Passwords");
|
|
int timeout = config->readNumEntry("Timeout", defTimeout);
|
|
|
|
// Check if we need a password
|
|
SuProcess proc;
|
|
proc.setUser(auth_user);
|
|
int needpw = proc.checkNeedPassword();
|
|
if (needpw < 0)
|
|
{
|
|
TQString err = i18n("Su returned with an error.\n");
|
|
KMessageBox::error(0L, err);
|
|
exit(1);
|
|
}
|
|
if (needpw == 0)
|
|
{
|
|
keep = 0;
|
|
kdDebug() << "Don't need password!!\n";
|
|
}
|
|
|
|
// Start the dialog
|
|
TQString password;
|
|
if (needpw)
|
|
{
|
|
TDEStartupInfoId id;
|
|
id.initId( kapp->startupId());
|
|
TDEStartupInfoData data;
|
|
data.setSilent( TDEStartupInfoData::Yes );
|
|
TDEStartupInfo::sendChange( id, data );
|
|
TDEsuDialog dlg(user, auth_user, keep && !terminal,icon, withIgnoreButton, timeout);
|
|
if (prompt)
|
|
dlg.addLine(i18n("Command:"), command);
|
|
if ((priority != 50) || (scheduler != SuProcess::SchedNormal))
|
|
{
|
|
TQString prio;
|
|
if (scheduler == SuProcess::SchedRealtime)
|
|
prio += i18n("realtime: ");
|
|
prio += TQString("%1/100").arg(priority);
|
|
if (prompt)
|
|
dlg.addLine(i18n("Priority:"), prio);
|
|
}
|
|
int ret = dlg.exec();
|
|
if (ret == TDEsuDialog::Rejected)
|
|
{
|
|
TDEStartupInfo::sendFinish( id );
|
|
exit(0);
|
|
}
|
|
if (ret == TDEsuDialog::AsUser)
|
|
change_uid = false;
|
|
password = dlg.password();
|
|
keep = dlg.keep();
|
|
TDEConfigGroup(config,"Passwords").writeEntry("Keep", keep);
|
|
data.setSilent( TDEStartupInfoData::No );
|
|
TDEStartupInfo::sendChange( id, data );
|
|
}
|
|
|
|
// Some events may need to be handled (like a button animation)
|
|
kapp->processEvents();
|
|
|
|
// Run command
|
|
if (!change_uid)
|
|
{
|
|
int result = system(command);
|
|
result = WEXITSTATUS(result);
|
|
return result;
|
|
}
|
|
else if (keep && have_daemon)
|
|
{
|
|
client.setPass(password.local8Bit(), timeout);
|
|
client.setPriority(priority);
|
|
client.setScheduler(scheduler);
|
|
int result = client.exec(command, user, options, env);
|
|
if (result == 0)
|
|
{
|
|
result = client.exitCode();
|
|
return result;
|
|
}
|
|
} else
|
|
{
|
|
SuProcess proc;
|
|
proc.setTerminal(terminal);
|
|
proc.setErase(true);
|
|
proc.setUser(user);
|
|
if (!new_dcop)
|
|
{
|
|
proc.setXOnly(true);
|
|
proc.setDCOPForwarding(true);
|
|
}
|
|
proc.setEnvironment(env);
|
|
proc.setPriority(priority);
|
|
proc.setScheduler(scheduler);
|
|
proc.setCommand(command);
|
|
int result = proc.exec(password.local8Bit());
|
|
return result;
|
|
}
|
|
return -1;
|
|
}
|
|
|