KDEsu
su.cpp
Go to the documentation of this file. 00001 /* vi: ts=8 sts=4 sw=4
00002 *
00003 * $Id: su.cpp 592501 2006年10月04日 23:00:23Z mueller $
00004 *
00005 * This file is part of the KDE project, module kdesu.
00006 * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
00007 *
00008 * Sudo support added by Jonathan Riddell <jriddell@ ubuntu.com>
00009 * Copyright (C) 2005 Canonical Ltd
00010 *
00011 * This is free software; you can use this library under the GNU Library
00012 * General Public License, version 2. See the file "COPYING.LIB" for the
00013 * exact licensing terms.
00014 *
00015 * su.cpp: Execute a program as another user with "class SuProcess".
00016 */
00017
00018 #include <config.h>
00019
00020 #include <stdio.h>
00021 #include <stdlib.h>
00022 #include <unistd.h>
00023 #include <fcntl.h>
00024 #include <errno.h>
00025 #include <string.h>
00026 #include <ctype.h>
00027 #include <signal.h>
00028
00029 #include <sys/types.h>
00030 #include <sys/stat.h>
00031
00032 #include <qglobal.h>
00033 #include <qcstring.h>
00034 #include <qfile.h>
00035
00036 #include <kconfig.h>
00037 #include <kdebug.h>
00038 #include <klocale.h>
00039 #include <kstandarddirs.h>
00040
00041 #include "su.h"
00042 #include "kcookie.h"
00043
00044
00045 #ifndef __PATH_SU
00046 #define __PATH_SU "false"
00047 #endif
00048
00049 #ifndef __PATH_SUDO
00050 #define __PATH_SUDO "false"
00051 #endif
00052
00053 SuProcess::SuProcess(const QCString &user, const QCString &command)
00054 {
00055 m_User = user;
00056 m_Command = command;
00057
00058 KConfig* config = KGlobal::config();
00059 config->setGroup("super-user-command");
00060 superUserCommand = config->readEntry("super-user-command", DEFAULT_SUPER_USER_COMMAND);
00061 if ( superUserCommand != "sudo" && superUserCommand != "su" ) {
00062 kdWarning() << "unknown super user command" << endl;
00063 superUserCommand = "su";
00064 }
00065 }
00066
00067
00068 SuProcess::~SuProcess()
00069 {
00070 }
00071
00072 int SuProcess::checkInstall(const char *password)
00073 {
00074 return exec(password, Install);
00075 }
00076
00077 int SuProcess::checkNeedPassword()
00078 {
00079 return exec(0L, NeedPassword);
00080 }
00081
00082 /*
00083 * Execute a command with su(1).
00084 */
00085
00086 int SuProcess::exec(const char *password, int check)
00087 {
00088 if (check)
00089 setTerminal(true);
00090
00091 // since user may change after constructor (due to setUser())
00092 // we need to override sudo with su for non-root here
00093 if (m_User != "root") {
00094 superUserCommand = "su";
00095 }
00096
00097 QCStringList args;
00098 if (superUserCommand == "sudo") {
00099 args += "-u";
00100 }
00101
00102 #ifdef Q_OS_DARWIN
00103 args += "-c";
00104 args += "staff";
00105 #endif
00106
00107 if ((m_Scheduler != SchedNormal) || (m_Priority > 50))
00108 args += "root";
00109 else
00110 args += m_User;
00111
00112 if (superUserCommand == "su") {
00113 args += "-c";
00114 }
00115 args += QCString(__KDE_BINDIR) + "/kdesu_stub";
00116 #ifndef Q_OS_DARWIN
00117 args += "-";
00118 #endif
00119
00122 QCString command;
00123 if (superUserCommand == "sudo") {
00124 command = __PATH_SUDO;
00125 } else {
00126 command = __PATH_SU;
00127 }
00128
00129 if (::access(command, X_OK) != 0)
00130 {
00132 command = QFile::encodeName( KGlobal::dirs()->findExe(superUserCommand.ascii()) );
00133 if (command.isEmpty())
00134 return check ? SuNotFound : -1;
00135 }
00136
00137 // kdDebug(900) << k_lineinfo << "Call StubProcess::exec()" << endl;
00138 if (StubProcess::exec(command, args) < 0)
00139 {
00140 return check ? SuNotFound : -1;
00141 }
00142 // kdDebug(900) << k_lineinfo << "Done StubProcess::exec()" << endl;
00143
00144 SuErrors ret = (SuErrors) ConverseSU(password);
00145 // kdDebug(900) << k_lineinfo << "Conversation returned " << ret << endl;
00146
00147 if (ret == error)
00148 {
00149 if (!check)
00150 kdError(900) << k_lineinfo << "Conversation with " << superUserCommand << " failed\n";
00151 return ret;
00152 }
00153 if (check == NeedPassword)
00154 {
00155 if (ret == killme)
00156 {
00161 if ( superUserCommand == "sudo" ) {
00162 // sudo can not be killed, just return
00163 return ret;
00164 }
00165 if (kill(m_Pid, SIGKILL) < 0) {
00166 kdDebug() << k_funcinfo << "kill < 0" << endl;
00167 //FIXME SIGKILL doesn't work for sudo,
00168 //why is this different from su?
00169 ret=error;
00170 }
00171 else
00172 {
00173 int iret = waitForChild();
00174 if (iret < 0) ret=error;
00175 else /* nothing */ {} ;
00176 }
00177 }
00178 return ret;
00179 }
00180
00181 if (m_bErase && password)
00182 {
00183 char *ptr = const_cast<char *>(password);
00184 const uint plen = strlen(password);
00185 for (unsigned i=0; i < plen; i++)
00186 ptr[i] = '000円';
00187 }
00188
00189 if (ret == notauthorized)
00190 {
00191 kill(m_Pid, SIGKILL);
00192 if (superUserCommand != "sudo") {
00193 waitForChild();
00194 }
00195 return SuIncorrectPassword;
00196 }
00197
00198 int iret = ConverseStub(check);
00199 if (iret < 0)
00200 {
00201 if (!check)
00202 kdError(900) << k_lineinfo << "Converstation with kdesu_stub failed\n";
00203 return iret;
00204 }
00205 else if (iret == 1)
00206 {
00207 kill(m_Pid, SIGKILL);
00208 waitForChild();
00209 return SuIncorrectPassword;
00210 }
00211
00212 if (check == Install)
00213 {
00214 waitForChild();
00215 return 0;
00216 }
00217
00218 iret = waitForChild();
00219 return iret;
00220 }
00221
00222 /*
00223 * Conversation with su: feed the password.
00224 * Return values: -1 = error, 0 = ok, 1 = kill me, 2 not authorized
00225 */
00226
00227 int SuProcess::ConverseSU(const char *password)
00228 {
00229 enum { WaitForPrompt, CheckStar, HandleStub } state = WaitForPrompt;
00230 int colon;
00231 unsigned i, j;
00232 // kdDebug(900) << k_lineinfo << "ConverseSU starting." << endl;
00233
00234 QCString line;
00235 while (true)
00236 {
00237 line = readLine();
00238 if (line.isNull())
00239 return ( state == HandleStub ? notauthorized : error);
00240 kdDebug(900) << k_lineinfo << "Read line <" << line << ">" << endl;
00241
00242 switch (state)
00243 {
00245 case WaitForPrompt:
00246 {
00247 // In case no password is needed.
00248 if (line == "kdesu_stub")
00249 {
00250 unreadLine(line);
00251 return ok;
00252 }
00253
00254 while(waitMS(m_Fd,100)>0)
00255 {
00256 // There is more output available, so the previous line
00257 // couldn't have been a password prompt (the definition
00258 // of prompt being that there's a line of output followed
00259 // by a colon, and then the process waits).
00260 QCString more = readLine();
00261 if (more.isEmpty())
00262 break;
00263
00264 line = more;
00265 kdDebug(900) << k_lineinfo << "Read line <" << more << ">" << endl;
00266 }
00267
00268 // Match "Password: " with the regex ^[^:]+:[\w]*$.
00269 const uint len = line.length();
00270 for (i=0,j=0,colon=0; i<len; i++)
00271 {
00272 if (line[i] == ':')
00273 {
00274 j = i; colon++;
00275 continue;
00276 }
00277 if (!isspace(line[i]))
00278 j++;
00279 }
00280 if ((colon == 1) && (line[j] == ':'))
00281 {
00282 if (password == 0L)
00283 return killme;
00284 if (!checkPid(m_Pid))
00285 {
00286 kdError(900) << superUserCommand << " has exited while waiting for pwd." << endl;
00287 return error;
00288 }
00289 if ((WaitSlave() == 0) && checkPid(m_Pid))
00290 {
00291 write(m_Fd, password, strlen(password));
00292 write(m_Fd, "\n", 1);
00293 state=CheckStar;
00294 }
00295 else
00296 {
00297 return error;
00298 }
00299 }
00300 break;
00301 }
00303 case CheckStar:
00304 {
00305 QCString s = line.stripWhiteSpace();
00306 if (s.isEmpty())
00307 {
00308 state=HandleStub;
00309 break;
00310 }
00311 const uint len = line.length();
00312 for (i=0; i< len; i++)
00313 {
00314 if (s[i] != '*')
00315 return error;
00316 }
00317 state=HandleStub;
00318 break;
00319 }
00321 case HandleStub:
00322 // Read till we get "kdesu_stub"
00323 if (line == "kdesu_stub")
00324 {
00325 unreadLine(line);
00326 return ok;
00327 } else if (superUserCommand == "sudo") {
00328 // sudo gives a "sorry" line so reaches here
00329 // with the wrong password
00330 return notauthorized;
00331 }
00332 break;
00334 } // end switch
00335 } // end while (true)
00336 return ok;
00337 }
00338
00339 void SuProcess::virtual_hook( int id, void* data )
00340 { StubProcess::virtual_hook( id, data ); }
00341
00342