/***************************************************************************
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! \n Usage: tdesudo [-u <runas>] <command> \n TdeSudo 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 . ascii ( ) ) ;
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 . local8Bit ( ) , 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 , TQ_SIGNAL ( receivedStdout ( TDEProcess * , char * , int ) ) , this , TQ_SLOT ( receivedOut ( TDEProcess * , char * , int ) ) ) ;
connect ( p , TQ_SIGNAL ( receivedStderr ( TDEProcess * , char * , int ) ) , this , TQ_SLOT ( receivedOut ( TDEProcess * , char * , int ) ) ) ;
connect ( p , TQ_SIGNAL ( processExited ( TDEProcess * ) ) , this , TQ_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 . local8Bit ( ) , " 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 ) ! = NULL )
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 . ascii ( ) ) ;
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 . local8Bit ( ) < < 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 ;
}
# include "tdesudo.moc"