TDESudo – sudo frontend
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

448 lines
12 KiB

/***************************************************************************
tdesudo.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) 2011 by Timothy Pearson <kb9vqf@pearsoncomputing.net>
***************************************************************************/
/***************************************************************************
* *
* 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 "tdesudo.h"
#include <tqfile.h>
#include <tqdir.h>
#include <tqdatastream.h>
#include <tqstring.h>
#include <tdecmdlineargs.h>
#include <tdelocale.h>
#include <tdemessagebox.h>
#include <kpushbutton.h>
#include <kpassdlg.h>
#include <kstandarddirs.h>
#include <tdesu/kcookie.h>
#include <kdebug.h>
#include <kshell.h>
#include <tdetempfile.h>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <signal.h>
#include <sys/stat.h>
#include <sys/types.h>
TdeSudo::TdeSudo(TQWidget *parent, const char *name,const TQString& icon, const TQString& generic, bool withIgnoreButton)
: KPasswordDialog(KPasswordDialog::Password, false, false, icon, parent, name)
{
TDECmdLineArgs *args = TDECmdLineArgs::parsedArgs();
TQString defaultComment = i18n("<b>%1</b> needs administrative privileges. Please enter your password for verification.");
p=NULL;
bError=false;
m_pCookie = new KCookie;
// Set vars
bool newDcop = args->isSet("newdcop");
bool realtime = args->isSet("r");
bool priority = args->isSet("p");
bool showCommand = (!args->isSet("d"));
bool keepCurrentHome = true;
bool changeUID = true;
bool noExec = false;
keepPwd = (!args->isSet("n"));
emptyPwd = args->isSet("s");
TQString runas = args->getOption("u");
TQString cmd;
if (!(args->isSet("c") && !args->getOption("c").stripWhiteSpace().isEmpty()) &&
!(!args->isSet("c") && args->count()) &&
!args->isSet("s"))
{
KMessageBox::information(NULL, i18n("No command arguments supplied!\nUsage: tdesudo [-u <runas>] <command>\nTdeSudo will now exit..."));
noExec = true;
}
p = new TDEProcess;
p->clearArguments();
// Parsins args
/* Get the comment out of cli args */
TQByteArray commentBytes = args->getOption("comment");
TQTextCodec* tCodecConv = TQTextCodec::codecForLocale();
TQString comment = tCodecConv->toUnicode(commentBytes, commentBytes.size());
if (args->isSet("f"))
{
// If file is writeable, do not change uid
TQString filename = TQFile::decodeName(args->getOption("f"));
TQString file = filename;
if (!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);
}
if (fi.isWritable())
{
changeUID = false;
}
}
}
if (withIgnoreButton)
{
setButtonText(User1, i18n("&Ignore"));
}
// Apologies for the C code, taken from tdelibs/tdesu/tdesu_stub.c
// KControl and other places need to use the user's existing DCOP server
// For that we set DCOPSERVER. Create a file in /tmp and use iceauth to add magic cookies
// from the existing server and set ICEAUTHORITY to point to the file
if (!newDcop) {
dcopServer = m_pCookie->dcopServer();
TQCString dcopAuth = m_pCookie->dcopAuth();
TQCString iceAuth = m_pCookie->iceAuth();
FILE *fout;
char iceauthority[200];
char *host, *auth;
host = tqstrdup(dcopServer);
auth = tqstrdup(iceAuth);
int tempfile;
int oldumask = umask(077);
strcpy(iceauthority, "/tmp/iceauth.XXXXXXXXXX");
tempfile = mkstemp(iceauthority);
umask(oldumask);
if (tempfile == -1) {
kdError() << "error in tdesudo mkstemp" << endl;
exit(1);
} else {
// close(tempfile); //FIXME why does this make the connect() call later crash?
}
iceauthorityFile = iceauthority;
//FIXME we should change owner of iceauthority file, but don't have permissions
setenv("ICEAUTHORITY", iceauthorityFile, 1);
fout = popen("iceauth >/dev/null 2>&1", "w");
if (!fout) {
kdError() << "error in tdesudo running iceauth" << endl;
exit(1);
}
fprintf(fout, "add ICE \"\" %s %s\n", host, auth);
auth = tqstrdup(dcopAuth);
//auth = xstrsep(params[P_DCOP_AUTH].value);
fprintf(fout, "add DCOP \"\" %s %s\n", host, auth);
unsetenv("ICEAUTHORITY");
pclose(fout);
// Exporting the user's sycoca
kdeSycoca = TQFile::encodeName(locateLocal("cache", "tdesycoca"));
}
connect( p, TQT_SIGNAL(receivedStdout(TDEProcess*, char*, int)), this, TQT_SLOT(receivedOut(TDEProcess*, char*, int)) );
connect( p, TQT_SIGNAL(receivedStderr(TDEProcess*, char*, int)), this, TQT_SLOT(receivedOut(TDEProcess*, char*, int)) );
connect( p, TQT_SIGNAL(processExited (TDEProcess *)), this, TQT_SLOT(procExited(TDEProcess*)));
TQString xauthenv = TQString(getenv("HOME")) + "/.Xauthority";
p->setEnvironment("XAUTHORITY", xauthenv);
// 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.
KTempFile temp = KTempFile("/tmp/tdesudo-","-xauth");
m_tmpname = temp.name();
FILE *f;
char buf[1024];
TQCString disp = m_pCookie->display();
// command: xauth -q -f m_tmpname generate $DISPLAy . trusted timeout 60
TQString c = "/usr/bin/xauth -q -f " + m_tmpname + " generate "
+ TQString::fromLocal8Bit(disp) + " . trusted timeout 60";
blockSigChild(); // pclose uses waitpid()
if (!(f = popen(c, "r"))) {
kdWarning() << k_lineinfo << "Cannot run: " << c << "\n";
unblockSigChild();
return;
}
// non root users need to be able to read the xauth file.
// the xauth file is deleted when tdesudo exits. security?
TQFile tf(m_tmpname);
if (!runas.isEmpty() && runas != "root" && tf.exists())
chmod(m_tmpname.ascii(),0644);
QCStringList output;
while (fgets(buf, 1024, f) > 0)
output += buf;
if (pclose(f) < 0) {
kdError() << k_lineinfo << "Could not run xauth.\n";
unblockSigChild();
return;
}
unblockSigChild();
p->setEnvironment("DISPLAY", disp);
p->setEnvironment("XAUTHORITY", m_tmpname);
if (emptyPwd)
*p << "sudo" << "-k";
else
{
if (changeUID)
{
*p << "sudo";
if (!keepCurrentHome)
*p << "-H";
*p << "-S" << "-p" << "passprompt";
if (!runas.isEmpty())
*p << "-u" << runas;
}
if (!dcopServer.isEmpty())
*p << "DCOPSERVER=" + dcopServer;
if (!iceauthorityFile.isEmpty())
*p << "ICEAUTHORITY=" + iceauthorityFile;
if (!kdeSycoca.isEmpty())
*p << "TDESYCOCA=" + kdeSycoca;
if (realtime)
{
*p << "nice" << "-n" << "10";
addLine(i18n("Priority:"), i18n("realtime:") + TQChar(' ') + TQString("50/100"));
}
else if (priority)
{
TQString n = args->getOption("p");
int intn = atoi(n);
intn = (intn * 40 / 100) - (20 + 0.5);
TQString strn;
strn.sprintf("%d",intn);
*p << "nice" << "-n" << strn;
addLine(i18n("Priority:"), n + TQString("/100"));
}
*p << "--";
if (args->isSet("c"))
{
TQString command = args->getOption("c");
TQStringList commandSplit = KShell::splitArgs(command);
for (int i = 0; i < commandSplit.count(); i++)
{
TQString arg = validArg(commandSplit[i]);
*p << arg;
if (i == 0)
cmd += validArg(commandSplit[i]) + TQChar(' ');
else
cmd += TDEProcess::quote(validArg(commandSplit[i])) + TQChar(' ');
}
}
if (args->count())
{
for (int i = 0; i < args->count(); i++)
{
if ((!args->isSet("c")) && (i == 0))
{
TQStringList argsSplit = KShell::splitArgs(args->arg(i));
for (int j = 0; j < argsSplit.count(); j++)
{
*p << validArg(argsSplit[j]);
if (j == 0)
cmd += validArg(argsSplit[j]) + TQChar(' ');
else
cmd += TDEProcess::quote(validArg(argsSplit[j])) + TQChar(' ');
}
}
else
{
*p << validArg(args->arg(i));
cmd += validArg(args->arg(i)) + TQChar(' ');
}
}
}
if (showCommand && !cmd.isEmpty())
addLine(i18n("Command:"), cmd);
}
if (comment.isEmpty())
{
if (!generic.isEmpty())
setPrompt(defaultComment.arg(generic));
else
setPrompt(defaultComment.arg(cmd));
}
else
setPrompt(comment);
if (noExec)
exit(0);
else
p->start( TDEProcess::NotifyOnExit, TDEProcess::All );
}
TdeSudo::~TdeSudo()
{
}
void TdeSudo::receivedOut(TDEProcess*, char*buffer, int buflen)
{
char *pcTmp= new char[buflen+1];
strncpy(pcTmp,buffer,buflen);
pcTmp[buflen]='\0';
TQString strOut(pcTmp);
std::cout << strOut << std::endl;
static int badpass = 0;
if (strOut.find("Sorry, try again")!=-1)
{
badpass++;
if (badpass>2)
{
bError=true;
KMessageBox::error(this, i18n("Wrong password! Exiting..."));
kapp->quit();
}
}
if (strOut.find("command not found")!=-1)
{
bError=true;
KMessageBox::error(this, i18n("Command not found!"));
kapp->quit();
}
if (strOut.find("is not in the sudoers file")!=-1)
{
bError=true;
KMessageBox::error(this, i18n("Your username is unknown to sudo!"));
kapp->quit();
}
if (strOut.find("is not allowed to execute")!=-1)
{
bError=true;
KMessageBox::error(this, i18n("Your user is not allowed to run the specified command!"));
kapp->quit();
}
if (strOut.find("is not allowed to run sudo on")!=-1)
{
bError=true;
KMessageBox::error(this, i18n("Your user is not allowed to run sudo on this host!"));
kapp->quit();
}
if (strOut.find("may not run sudo on")!=-1)
{
bError=true;
KMessageBox::error(this, i18n("Your user is not allowed to run sudo on this host!"));
kapp->quit();
}
if ((strOut.find("passprompt")!=-1) || (strOut.find("PIN (CHV2)")!=-1))
{
this->clearPassword();
this->show();
}
}
void TdeSudo::procExited(TDEProcess*)
{
if (!keepPwd && unCleaned)
{
unCleaned = false;
p->clearArguments();
*p << "sudo" << "-k";
p->start( TDEProcess::NotifyOnExit, TDEProcess::All );
}
if (!newDcop && !iceauthorityFile.isEmpty())
if (!iceauthorityFile.isEmpty())
TQFile::remove(iceauthorityFile);
if (!bError) {
if (!m_tmpname.isEmpty())
TQFile::remove(m_tmpname);
kapp->quit();
}
}
void TdeSudo::slotOk()
{
TQString strTmp(password());
strTmp+="\n";
p->writeStdin(strTmp.ascii(),(int)strTmp.length());
this->hide();
}
void TdeSudo::slotUser1()
{
done(AsUser);
}
void TdeSudo::blockSigChild()
{
sigset_t sset;
sigemptyset(&sset);
sigaddset(&sset, SIGCHLD);
sigprocmask(SIG_BLOCK, &sset, 0L);
}
void TdeSudo::unblockSigChild()
{
sigset_t sset;
sigemptyset(&sset);
sigaddset(&sset, SIGCHLD);
sigprocmask(SIG_UNBLOCK, &sset, 0L);
}
TQString TdeSudo::validArg(TQString arg)
{
TQChar firstChar = arg.at(0);
TQChar 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;
}