su.cpp

00001 /* vi: ts=8 sts=4 sw=4
00002 *
00003 * $Id: su.cpp 397822 2005-03-15 15:11:58Z lunakl $
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 //change to sudo or su according to your preferences
00054 #define DEFAULT_SUPER_USER_COMMAND "sudo"
00055 
00056 SuProcess::SuProcess(const QCString &user, const QCString &command)
00057 {
00058     m_User = user;
00059     m_Command = command;
00060 
00061     KConfig* config = KGlobal::config();
00062     config->setGroup("super-user-command");
00063     superUserCommand = config->readEntry("super-user-command", DEFAULT_SUPER_USER_COMMAND);
00064     if ( superUserCommand != "sudo" && superUserCommand != "su" ) {
00065     kdWarning() << "unknown super user command" << endl;
00066     superUserCommand = "su";
00067     }
00068 }
00069 
00070 
00071 SuProcess::~SuProcess()
00072 {
00073 }
00074 
00075 int SuProcess::checkInstall(const char *password)
00076 {
00077     return exec(password, Install);
00078 }
00079 
00080 int SuProcess::checkNeedPassword()
00081 {
00082     return exec(0L, NeedPassword);
00083 }
00084 
00085 /*
00086 * Execute a command with su(1).
00087 */
00088 
00089 int SuProcess::exec(const char *password, int check)
00090 {
00091     if (check)
00092         setTerminal(true);
00093 
00094     // since user may change after constructor (due to setUser())
00095     // we need to override sudo with su for non-root here
00096     if (m_User != "root") {
00097         superUserCommand = "su";
00098     }
00099 
00100     QCStringList args;
00101     if (superUserCommand == "sudo") {
00102         args += "-u";
00103     }
00104 
00105     if ((m_Scheduler != SchedNormal) || (m_Priority > 50))
00106         args += "root";
00107     else
00108         args += m_User;
00109     
00110  
00111     if (superUserCommand == "su") {
00112         args += "-c";
00113     }
00114     args += QCString(__KDE_BINDIR) + "/kdesu_stub";
00115     args += "-";
00116 
00119     QCString command;
00120     if (superUserCommand == "sudo") {
00121         command = __PATH_SUDO;
00122     } else {
00123         command = __PATH_SU;
00124     }
00125  
00126     if (::access(command, X_OK) != 0)
00127     {
00129         command = QFile::encodeName( KGlobal::dirs()->findExe(superUserCommand.ascii()) );
00130         if (command.isEmpty())
00131             return check ? SuNotFound : -1;
00132     }
00133 
00134     // kdDebug(900) << k_lineinfo << "Call StubProcess::exec()" << endl;
00135     if (StubProcess::exec(command, args) < 0)
00136     {
00137         return check ? SuNotFound : -1;
00138     }
00139     // kdDebug(900) << k_lineinfo << "Done StubProcess::exec()" << endl;
00140 
00141     SuErrors ret = (SuErrors) ConverseSU(password);
00142     // kdDebug(900) << k_lineinfo << "Conversation returned " << ret << endl;
00143 
00144     if (ret == error) 
00145     {
00146         if (!check)
00147             kdError(900) << k_lineinfo << "Conversation with su failed\n";
00148         return ret;
00149     }
00150     if (check == NeedPassword)
00151     {
00152         if (ret == killme)
00153         {
00158             if ( superUserCommand == "sudo" ) {
00159             // sudo can not be killed, just return
00160             return ret;
00161         }
00162         if (kill(m_Pid, SIGKILL) < 0) {
00163             kdDebug() << k_funcinfo << "kill < 0" << endl;
00164         //FIXME SIGKILL doesn't work for sudo,
00165         //why is this different from su?
00166         ret=error;
00167         }
00168             else
00169             {
00170                 int iret = waitForChild();
00171                 if (iret < 0) ret=error;
00172                 else /* nothing */ {} ;
00173             }
00174         }
00175         return ret;
00176     }
00177 
00178     if (m_bErase && password) 
00179     {
00180         char *ptr = const_cast<char *>(password);
00181         const uint plen = strlen(password);
00182         for (unsigned i=0; i < plen; i++)
00183             ptr[i] = '\000';
00184     }
00185 
00186     if (ret == notauthorized)
00187     {
00188         kill(m_Pid, SIGKILL);
00189         if (superUserCommand != "sudo") {
00190             waitForChild();
00191         }
00192         return SuIncorrectPassword;
00193     }
00194 
00195     int iret = ConverseStub(check);
00196     if (iret < 0)
00197     {
00198         if (!check)
00199             kdError(900) << k_lineinfo << "Converstation with kdesu_stub failed\n";
00200         return iret;
00201     }
00202     else if (iret == 1)
00203     {
00204         kill(m_Pid, SIGKILL);
00205         waitForChild();
00206         return SuIncorrectPassword;
00207     }
00208 
00209     if (check == Install)
00210     {
00211         waitForChild();
00212         return 0;
00213     }
00214 
00215     iret = waitForChild();
00216     return iret;
00217 }
00218 
00219 /*
00220 * Conversation with su: feed the password.
00221 * Return values: -1 = error, 0 = ok, 1 = kill me, 2 not authorized
00222 */
00223 
00224 int SuProcess::ConverseSU(const char *password)
00225 {   
00226     enum { WaitForPrompt, CheckStar, HandleStub } state = WaitForPrompt;
00227     int colon;
00228     unsigned i, j;
00229     // kdDebug(900) << k_lineinfo << "ConverseSU starting." << endl;
00230 
00231     QCString line;
00232     while (true)
00233     {
00234         line = readLine(); 
00235         if (line.isNull())
00236             return ( state == HandleStub ? notauthorized : error);
00237         kdDebug(900) << k_lineinfo << "Read line <" << line << ">" << endl;
00238 
00239         switch (state) 
00240         {
00242             case WaitForPrompt:
00243             {
00244                 // In case no password is needed.
00245                 if (line == "kdesu_stub")
00246                 {
00247                     unreadLine(line);
00248                     return ok;
00249                 }
00250     
00251                 while(waitMS(m_Fd,100)>0)
00252                 {
00253                     // There is more output available, so the previous line
00254                     // couldn't have been a password prompt (the definition
00255                     // of prompt being that  there's a line of output followed 
00256                     // by a colon, and then the process waits).
00257                     QCString more = readLine();
00258                     if (more.isEmpty())
00259                         break;
00260     
00261                     line = more;
00262                     kdDebug(900) << k_lineinfo << "Read line <" << more << ">" << endl;
00263                 }
00264     
00265                 // Match "Password: " with the regex ^[^:]+:[\w]*$.
00266                 const uint len = line.length();
00267                 for (i=0,j=0,colon=0; i<len; i++) 
00268                 {
00269                     if (line[i] == ':') 
00270                     {
00271                         j = i; colon++;
00272                         continue;
00273                     }
00274                     if (!isspace(line[i]))
00275                         j++;
00276                 }
00277                 if ((colon == 1) && (line[j] == ':')) 
00278                 {
00279                     if (password == 0L)
00280                         return killme;
00281                     if (!checkPid(m_Pid))
00282                     {
00283                         kdError(900) << "su has exited while waiting for pwd." << endl;
00284                         return error;
00285                     }
00286                     if ((WaitSlave() == 0) && checkPid(m_Pid))
00287                     {
00288                         write(m_Fd, password, strlen(password));
00289                         write(m_Fd, "\n", 1);
00290                         state=CheckStar;
00291                     }
00292                     else
00293                     {
00294                         return error;
00295                     }
00296                 }
00297                 break;
00298             }
00300             case CheckStar:
00301             {
00302                 QCString s = line.stripWhiteSpace();
00303                 if (s.isEmpty()) 
00304                 {
00305                     state=HandleStub;
00306                     break;
00307                 }
00308                 const uint len = line.length();
00309                 for (i=0; i< len; i++)
00310                     {
00311                 if (s[i] != '*')
00312                     return error;
00313                 }
00314                 state=HandleStub;
00315                 break;
00316             }
00318             case HandleStub:
00319                 // Read till we get "kdesu_stub"
00320                 if (line == "kdesu_stub")
00321                 {
00322                     unreadLine(line);
00323                     return ok;
00324             } else if (superUserCommand == "sudo") {
00325                 // sudo gives a "sorry" line so reaches here
00326                 // with the wrong password
00327                 return notauthorized;
00328                 }
00329                 break;
00331         } // end switch
00332     } // end while (true)
00333     return ok;
00334 }
00335 
00336 void SuProcess::virtual_hook( int id, void* data )
00337 { StubProcess::virtual_hook( id, data ); }
00338 
00339 
KDE Home | KDE Accessibility Home | Description of Access Keys