/************************************************************************** ** ** This file is part of Qt SDK** ** ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).* ** ** Contact: Nokia Corporation qt-info@nokia.com** ** ** No Commercial Usage ** ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the Technology Preview License Agreement accompanying ** this package. ** ** GNU Lesser General Public License Usage ** ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this file. ** Please review the following information to ensure the GNU Lesser General ** Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception version ** 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** If you are unsure which license is appropriate for your use, please contact ** (qt-info@nokia.com). ** **************************************************************************/ #include "adminauthorization.h" #include #include #include #include #include #include #include #ifdef Q_OS_LINUX #include #include #else #include #endif #include #include #include #include #define SU_COMMAND "/usr/bin/sudo" //#define SU_COMMAND "/bin/echo" class AdminAuthorization::Private { public: }; AdminAuthorization::AdminAuthorization() { } AdminAuthorization::~AdminAuthorization() { } bool AdminAuthorization::authorize() { return true; } static QString getPassword( QWidget* parent ) { if( QApplication::type() == QApplication::GuiClient ) { bool ok = false; const QString result = QInputDialog::getText( 0, QObject::tr( "Authorization required" ), QObject::tr( "Enter your password to authorize for sudo:" ), QLineEdit::Password, QString(), &ok ); return ok ? result : QString(); } else { std::cout << QObject::tr( "Authorization required" ).toStdString() << std::endl; std::cout << QObject::tr( "Enter your password to authorize for sudo:" ).toStdString() << std::endl; std::string password; std::cin >> password; return QString::fromStdString( password ); } } static void printError( QWidget* parent, const QString& value ) { if( QApplication::type() == QApplication::GuiClient ) { QMessageBox::critical( parent, QObject::tr( "Error acquiring admin rights" ), value, QMessageBox::Ok, QMessageBox::Ok ); } else { std::cout << value.toStdString() << std::endl; } } bool AdminAuthorization::execute( QWidget* parent, const QString& program, const QStringList& arguments ) { // as we cannot pipe the password to su in QProcess, we need to setup a pseudo-terminal for it int masterFD = -1; int slaveFD = -1; char ptsn[ PATH_MAX ]; if( ::openpty( &masterFD, &slaveFD, ptsn, 0, 0 ) ) return false; masterFD = ::posix_openpt( O_RDWR | O_NOCTTY ); if( masterFD < 0 ) return false; const QByteArray ttyName = ::ptsname( masterFD ); if( ::grantpt( masterFD ) ) { ::close( masterFD ); return false; } ::revoke( ttyName ); ::unlockpt( masterFD ); slaveFD = ::open( ttyName, O_RDWR | O_NOCTTY ); if( slaveFD < 0 ) { ::close( masterFD ); return false; } ::fcntl( masterFD, F_SETFD, FD_CLOEXEC ); ::fcntl( slaveFD, F_SETFD, FD_CLOEXEC ); int pipedData[ 2 ]; if ( pipe( pipedData ) != 0) return false; int flags = ::fcntl( pipedData[ 0 ], F_GETFD ); if ( flags != -1 ) ::fcntl( pipedData[ 0 ], F_SETFL, flags | O_NONBLOCK ); pid_t child = fork(); if( child < -1 ) { ::close( masterFD ); ::close( slaveFD ); ::close( pipedData[ 0 ]); ::close( pipedData[ 1 ]); return false; } // parent process else if( child > 0 ) { ::close( slaveFD ); //close writing end of pipe ::close( pipedData[1]); QRegExp re( QLatin1String( "[Pp]assword.*:" ) ); QByteArray errData; flags = ::fcntl( masterFD, F_GETFD ); // if ( flags != -1 ) // ::fcntl( masterFD, F_SETFL, flags | O_NONBLOCK ); int bytes = 0; int errBytes = 0; char buf[ 1024 ]; while( bytes >= 0 ) { int state; if( ::waitpid( child, &state, WNOHANG ) == -1 ) break; bytes = ::read( masterFD, buf, 1023 ); errBytes = ::read( pipedData[ 0 ], buf, 1023 ); if ( errBytes > 0 ) errData.append( buf, errBytes ); if( bytes > 0 ) { const QString line = QString::fromLatin1( buf, bytes ); if( re.indexIn( line ) != -1 ) { const QString password = getPassword( parent ); if ( password == QString() ) { QByteArray pwd = password.toLatin1(); for ( int i = 0; i < 3; ++i ) { ::write( masterFD, pwd.data(), pwd.length() ); ::write( masterFD, "\n", 1 ); } return false; } QByteArray pwd = password.toLatin1(); ::write( masterFD, pwd.data(), pwd.length() ); ::write( masterFD, "\n", 1 ); ::read( masterFD, buf, pwd.length() + 1 ); } } if( bytes == 0 ) ::usleep( 100000 ); } if ( !errData.isEmpty() ) { printError( parent, QString::fromLocal8Bit( errData.constData() ) ); return false; } int status; child = ::wait( &status ); const int exited = WIFEXITED( status ); const int exitStatus = WEXITSTATUS( status ); ::close( pipedData[1]); if( exited ) return exitStatus == 0; return false; } // child process else { ::close( pipedData[ 0 ] ); // Reset signal handlers for( int sig = 1; sig < NSIG; ++sig ) signal( sig, SIG_DFL ); signal( SIGHUP, SIG_IGN ); ::setsid(); ::ioctl( slaveFD, TIOCSCTTY, 1 ); int pgrp = ::getpid(); ::tcsetpgrp( slaveFD, pgrp ); ::dup2( slaveFD, 0 ); ::dup2( slaveFD, 1 ); ::dup2( pipedData[ 1 ], 2 ); // close all file descriptors struct rlimit rlp; getrlimit( RLIMIT_NOFILE, &rlp ); for( int i = 3; i < static_cast< int >( rlp.rlim_cur ); ++i ) ::close( i ); char** argp = (char**) ::malloc( arguments.count() + 4 * sizeof( char* ) ); QList< QByteArray > args; args.push_back( SU_COMMAND ); args.push_back( "-b" ); args.push_back( program.toLocal8Bit() ); for( QStringList::const_iterator it = arguments.begin(); it != arguments.end(); ++it ) args.push_back( it->toLocal8Bit() ); int i = 0; for( QList< QByteArray >::iterator it = args.begin(); it != args.end(); ++it, ++i ) argp[ i ] = it->data(); argp[ i ] = 0; ::unsetenv( "LANG" ); ::unsetenv( "LC_ALL" ); ::execv( SU_COMMAND, argp ); _exit( 0 ); return false; } } // has no guarantee to work, its implemented as discussed with Tim Jenssen and Niels Weber bool AdminAuthorization::hasAdminRights() { return getuid() == 0; }