/* vi: ts=8 sts=4 sw=4 * * $Id$ * * This file is part of the KDE project, module tdesu. * Copyright (C) 1999,2000 Geert Jansen * * Sudo support added by Jonathan Riddell * Copyright (C) 2005 Canonical Ltd * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "su.h" #include "kcookie.h" #ifndef __PATH_SU #define __PATH_SU "false" #endif #ifndef __PATH_SUDO #define __PATH_SUDO "false" #endif SuProcess::SuProcess(const TQCString &user, const TQCString &command) { m_User = user; m_Command = command; TDEConfig* config = TDEGlobal::config(); config->setGroup("super-user-command"); superUserCommand = config->readEntry("super-user-command", DEFAULT_SUPER_USER_COMMAND); if ( superUserCommand != "sudo" && superUserCommand != "su" ) { kdWarning() << "unknown super user command" << endl; superUserCommand = "su"; } } SuProcess::~SuProcess() { } 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") { superUserCommand = "su"; } QCStringList args; if (superUserCommand == "sudo") { args += "-u"; } #ifdef Q_OS_DARWIN args += "-c"; args += "staff"; #endif if ((m_Scheduler != SchedNormal) || (m_Priority > 50)) args += "root"; else args += m_User; if (superUserCommand == "su") { args += "-c"; } args += TQCString(__TDE_BINDIR) + "/tdesu_stub"; #ifndef Q_OS_DARWIN args += "-"; #endif /// TQCString command = __PATH_SU; /// if (::access(__PATH_SU, X_OK) != 0) TQCString command; if (superUserCommand == "sudo") { command = __PATH_SUDO; } else { command = __PATH_SU; } if (::access(command, X_OK) != 0) { /// command = TQFile::encodeName(TDEGlobal::dirs()->findExe("su")); command = TQFile::encodeName( TDEGlobal::dirs()->findExe(superUserCommand.ascii()) ); if (command.isEmpty()) return check ? SuNotFound : -1; } // kdDebug(900) << k_lineinfo << "Call StubProcess::exec()" << endl; if (StubProcess::exec(command, args) < 0) { return check ? SuNotFound : -1; } // kdDebug(900) << k_lineinfo << "Done StubProcess::exec()" << endl; SuErrors ret = (SuErrors) ConverseSU(password); // kdDebug(900) << k_lineinfo << "Conversation returned " << ret << endl; if (ret == error) { if (!check) kdError(900) << k_lineinfo << "Conversation with " << superUserCommand << " failed\n"; return ret; } if (check == NeedPassword) { if (ret == killme) { /// if (kill(m_Pid, SIGKILL) < 0) /// { /// ret=error; /// } if ( superUserCommand == "sudo" ) { // sudo can not be killed, just return return ret; } if (kill(m_Pid, SIGKILL) < 0) { kdDebug() << k_funcinfo << "kill < 0" << endl; //FIXME SIGKILL doesn't work for sudo, //why is this different from su? ret=error; } else { int iret = waitForChild(); if (iret < 0) ret=error; else /* nothing */ {} ; } } return ret; } if (m_bErase && password) { char *ptr = const_cast(password); const uint plen = strlen(password); for (unsigned i=0; i < plen; i++) ptr[i] = '\000'; } if (ret == notauthorized) { kill(m_Pid, SIGKILL); if (superUserCommand != "sudo") { waitForChild(); } return SuIncorrectPassword; } int iret = ConverseStub(check); if (iret < 0) { if (!check) kdError(900) << k_lineinfo << "Converstation with tdesu_stub failed\n"; return iret; } else if (iret == 1) { kill(m_Pid, SIGKILL); 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; // kdDebug(900) << k_lineinfo << "ConverseSU starting." << endl; TQCString line; while (true) { line = readLine(); if (line.isNull()) return ( state == HandleStub ? notauthorized : error); kdDebug(900) << k_lineinfo << "Read line <" << line << ">" << endl; switch (state) { ////////////////////////////////////////////////////////////////////////// case WaitForPrompt: { // In case no password is needed. if (line == "tdesu_stub") { unreadLine(line); return ok; } while(waitMS(m_Fd,100)>0) { // There is more output available, so the previous 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). TQCString more = readLine(); if (more.isEmpty()) break; line = more; kdDebug(900) << k_lineinfo << "Read line <" << more << ">" << endl; } // Match "Password: " with the regex ^[^:]+:[\w]*$. const uint len = line.length(); for (i=0,j=0,colon=0; i