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