su.cpp

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