diff options
Diffstat (limited to 'tdesu/su.cpp')
-rw-r--r-- | tdesu/su.cpp | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/tdesu/su.cpp b/tdesu/su.cpp new file mode 100644 index 000000000..0739c29c3 --- /dev/null +++ b/tdesu/su.cpp @@ -0,0 +1,342 @@ +/* vi: ts=8 sts=4 sw=4 +* +* $Id$ +* +* This file is part of the KDE project, module tdesu. +* Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org> +* +* Sudo support added by Jonathan Riddell <jriddell@ ubuntu.com> +* 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 <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <ctype.h> +#include <signal.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <tqglobal.h> +#include <tqcstring.h> +#include <tqfile.h> + +#include <kconfig.h> +#include <kdebug.h> +#include <klocale.h> +#include <kstandarddirs.h> + +#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; + + KConfig* config = KGlobal::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(__KDE_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(KGlobal::dirs()->findExe("su")); + command = TQFile::encodeName( KGlobal::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<char *>(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<len; i++) + { + if (line[i] == ':') + { + j = i; colon++; + continue; + } + if (!isspace(line[i])) + j++; + } + if ((colon == 1) && (line[j] == ':')) + { + if (password == 0L) + return killme; + if (!checkPid(m_Pid)) + { + kdError(900) << superUserCommand << " has exited while waiting for pwd." << endl; + return error; + } + if ((WaitSlave() == 0) && checkPid(m_Pid)) + { + write(m_Fd, password, strlen(password)); + write(m_Fd, "\n", 1); + state=CheckStar; + } + else + { + return error; + } + } + break; + } + ////////////////////////////////////////////////////////////////////////// + case CheckStar: + { + TQCString s = line.stripWhiteSpace(); + if (s.isEmpty()) + { + state=HandleStub; + break; + } + const uint len = line.length(); + for (i=0; i< len; i++) + { + if (s[i] != '*') + return error; + } + state=HandleStub; + break; + } + ////////////////////////////////////////////////////////////////////////// + case HandleStub: + // Read till we get "tdesu_stub" + if (line == "tdesu_stub") + { + unreadLine(line); + return ok; + } else if (superUserCommand == "sudo") { + // sudo gives a "sorry" line so reaches here + // with the wrong password + return notauthorized; + } + break; + ////////////////////////////////////////////////////////////////////////// + } // end switch + } // end while (true) + return ok; +} + +void SuProcess::virtual_hook( int id, void* data ) +{ StubProcess::virtual_hook( id, data ); } + + |