su.cpp
00001 /* vi: ts=8 sts=4 sw=4 00002 * 00003 * $Id$ 00004 * 00005 * This file is part of the KDE project, module tdesu. 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 <tqglobal.h> 00033 #include <tqcstring.h> 00034 #include <tqfile.h> 00035 00036 #include <tdeconfig.h> 00037 #include <kdebug.h> 00038 #include <tdelocale.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 TQCString &user, const TQCString &command) 00054 { 00055 m_User = user; 00056 m_Command = command; 00057 00058 TDEConfig* config = TDEGlobal::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 += TQCString(__TDE_BINDIR) + "/tdesu_stub"; 00116 #ifndef Q_OS_DARWIN 00117 args += "-"; 00118 #endif 00119 00122 TQCString 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 = TQFile::encodeName( TDEGlobal::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 tdesu_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 TQCString 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 == "tdesu_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 TQCString 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 TQCString 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 "tdesu_stub" 00323 if (line == "tdesu_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