KDEsu
ssh.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) 2000 Geert Jansen <jansen@kde.org> 00005 * 00006 * This is free software; you can use this library under the GNU Library 00007 * General Public License, version 2. See the file "COPYING.LIB" for the 00008 * exact licensing terms. 00009 * 00010 * ssh.cpp: Execute a program on a remote machine using ssh. 00011 */ 00012 00013 #include "ssh.h" 00014 #include "kcookie.h" 00015 00016 #include <config.h> 00017 00018 #include <stdio.h> 00019 #include <stdlib.h> 00020 #include <unistd.h> 00021 #include <fcntl.h> 00022 #include <signal.h> 00023 #include <errno.h> 00024 #include <string.h> 00025 #include <ctype.h> 00026 #include <time.h> 00027 00028 #include <sys/types.h> 00029 #include <sys/stat.h> 00030 00031 #include <QtCore/QBool> 00032 00033 #include <kdebug.h> 00034 #include <klocale.h> 00035 #include <kstandarddirs.h> 00036 00037 extern int kdesuDebugArea(); 00038 00039 namespace KDESu { 00040 00041 using namespace KDESuPrivate; 00042 00043 class SshProcess::SshProcessPrivate 00044 { 00045 public: 00046 SshProcessPrivate(const QByteArray &host) 00047 : m_Host( host ) 00048 , m_Stub( "kdesu_stub" ) 00049 {} 00050 QByteArray m_Prompt; 00051 QByteArray m_Host; 00052 QByteArray m_Error; 00053 QByteArray m_Stub; 00054 }; 00055 00056 00057 SshProcess::SshProcess(const QByteArray &host, const QByteArray &user, const QByteArray &command) 00058 : d( new SshProcessPrivate(host) ) 00059 { 00060 m_User = user; 00061 m_Command = command; 00062 srand(time(0L)); 00063 } 00064 00065 00066 SshProcess::~SshProcess() 00067 { 00068 delete d; 00069 } 00070 00071 void SshProcess::setHost(const QByteArray &host) 00072 { 00073 d->m_Host = host; 00074 } 00075 00076 00077 void SshProcess::setStub(const QByteArray &stub) 00078 { 00079 d->m_Stub = stub; 00080 } 00081 00082 00083 int SshProcess::checkInstall(const char *password) 00084 { 00085 return exec(password, 1); 00086 } 00087 00088 00089 int SshProcess::checkNeedPassword() 00090 { 00091 return exec(0L, 2); 00092 } 00093 00094 00095 int SshProcess::exec(const char *password, int check) 00096 { 00097 if (check) 00098 setTerminal(true); 00099 00100 QList<QByteArray> args; 00101 args += "-l"; args += m_User; 00102 args += "-o"; args += "StrictHostKeyChecking=no"; 00103 args += d->m_Host; args += d->m_Stub; 00104 00105 if (StubProcess::exec("ssh", args) < 0) 00106 { 00107 return check ? SshNotFound : -1; 00108 } 00109 00110 int ret = ConverseSsh(password, check); 00111 if (ret < 0) 00112 { 00113 if (!check) 00114 kError(kdesuDebugArea()) << k_lineinfo << "Conversation with ssh failed."; 00115 return ret; 00116 } 00117 if (check == 2) 00118 { 00119 if (ret == 1) 00120 { 00121 kill(m_Pid, SIGTERM); 00122 waitForChild(); 00123 } 00124 return ret; 00125 } 00126 00127 if (m_bErase && password) 00128 memset(const_cast<char *>(password), 0, qstrlen(password)); 00129 00130 ret = ConverseStub(check); 00131 if (ret < 0) 00132 { 00133 if (!check) 00134 kError(kdesuDebugArea()) << k_lineinfo << "Conversation with kdesu_stub failed."; 00135 return ret; 00136 } 00137 else if (ret == 1) 00138 { 00139 kill(m_Pid, SIGTERM); 00140 waitForChild(); 00141 ret = SshIncorrectPassword; 00142 } 00143 00144 if (check == 1) 00145 { 00146 waitForChild(); 00147 return 0; 00148 } 00149 00150 setExitString("Waiting for forwarded connections to terminate"); 00151 ret = waitForChild(); 00152 return ret; 00153 } 00154 00155 QByteArray SshProcess::prompt() const 00156 { 00157 return d->m_Prompt; 00158 } 00159 00160 QByteArray SshProcess::error() const 00161 { 00162 return d->m_Error; 00163 } 00164 00165 00166 /* 00167 * Conversation with ssh. 00168 * If check is 0, this waits for either a "Password: " prompt, 00169 * or the header of the stub. If a prompt is received, the password is 00170 * written back. Used for running a command. 00171 * If check is 1, operation is the same as 0 except that if a stub header is 00172 * received, the stub is stopped with the "stop" command. This is used for 00173 * checking a password. 00174 * If check is 2, operation is the same as 1, except that no password is 00175 * written. The prompt is saved to m_Prompt. Used for checking the need for 00176 * a password. 00177 */ 00178 00179 int SshProcess::ConverseSsh(const char *password, int check) 00180 { 00181 unsigned i, j, colon; 00182 00183 QByteArray line; 00184 int state = 0; 00185 00186 while (state < 2) 00187 { 00188 line = readLine(); 00189 const uint len = line.length(); 00190 if (line.isNull()) 00191 return -1; 00192 00193 switch (state) { 00194 case 0: 00195 // Check for "kdesu_stub" header. 00196 if (line == "kdesu_stub") 00197 { 00198 unreadLine(line); 00199 return 0; 00200 } 00201 00202 // Match "Password: " with the regex ^[^:]+:[\w]*$. 00203 for (i=0,j=0,colon=0; i<len; i++) 00204 { 00205 if (line[i] == ':') 00206 { 00207 j = i; colon++; 00208 continue; 00209 } 00210 if (!isspace(line[i])) 00211 j++; 00212 } 00213 if ((colon == 1) && (line[j] == ':')) 00214 { 00215 if (check == 2) 00216 { 00217 d->m_Prompt = line; 00218 return SshNeedsPassword; 00219 } 00220 if (WaitSlave()) 00221 return -1; 00222 write(fd(), password, strlen(password)); 00223 write(fd(), "\n", 1); 00224 state++; 00225 break; 00226 } 00227 00228 // Warning/error message. 00229 d->m_Error += line; d->m_Error += '\n'; 00230 if (m_bTerminal) 00231 fprintf(stderr, "ssh: %s\n", line.constData()); 00232 break; 00233 00234 case 1: 00235 if (line.isEmpty()) 00236 { 00237 state++; 00238 break; 00239 } 00240 return -1; 00241 } 00242 } 00243 return 0; 00244 } 00245 00246 00247 // Display redirection is handled by ssh natively. 00248 QByteArray SshProcess::display() 00249 { 00250 return "no"; 00251 } 00252 00253 00254 QByteArray SshProcess::displayAuth() 00255 { 00256 return "no"; 00257 } 00258 00259 void SshProcess::virtual_hook( int id, void* data ) 00260 { StubProcess::virtual_hook( id, data ); } 00261 00262 }
KDE 4.6 API Reference