ssh.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) 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 * ssh.cpp: Execute a program on a remote machine using ssh. 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 <signal.h> 00022 #include <errno.h> 00023 #include <string.h> 00024 #include <ctype.h> 00025 #include <signal.h> 00026 #include <time.h> 00027 00028 #include <sys/types.h> 00029 #include <sys/stat.h> 00030 00031 #include <tqglobal.h> 00032 #include <tqcstring.h> 00033 00034 #include <kdebug.h> 00035 #include <tdelocale.h> 00036 #include <kstandarddirs.h> 00037 00038 #include "ssh.h" 00039 #include "kcookie.h" 00040 00041 00042 SshProcess::SshProcess(const TQCString &host, const TQCString &user, const TQCString &command) 00043 { 00044 m_Host = host; 00045 m_User = user; 00046 m_Command = command; 00047 m_Stub = "tdesu_stub"; 00048 srand(time(0L)); 00049 } 00050 00051 00052 SshProcess::~SshProcess() 00053 { 00054 } 00055 00056 00057 void SshProcess::setStub(const TQCString &stub) 00058 { 00059 m_Stub = stub; 00060 } 00061 00062 00063 int SshProcess::checkInstall(const char *password) 00064 { 00065 return exec(password, 1); 00066 } 00067 00068 00069 int SshProcess::checkNeedPassword() 00070 { 00071 return exec(0L, 2); 00072 } 00073 00074 00075 int SshProcess::exec(const char *password, int check) 00076 { 00077 if (check) 00078 setTerminal(true); 00079 00080 QCStringList args; 00081 args += "-l"; args += m_User; 00082 args += "-o"; args += "StrictHostKeyChecking=no"; 00083 args += m_Host; args += m_Stub; 00084 00085 if (StubProcess::exec("ssh", args) < 0) 00086 { 00087 return check ? SshNotFound : -1; 00088 } 00089 00090 int ret = ConverseSsh(password, check); 00091 if (ret < 0) 00092 { 00093 if (!check) 00094 kdError(900) << k_lineinfo << "Conversation with ssh failed\n"; 00095 return ret; 00096 } 00097 if (check == 2) 00098 { 00099 if (ret == 1) 00100 { 00101 kill(m_Pid, SIGTERM); 00102 waitForChild(); 00103 } 00104 return ret; 00105 } 00106 00107 if (m_bErase && password) 00108 { 00109 char *ptr = const_cast<char *>(password); 00110 const uint plen = strlen(password); 00111 for (unsigned i=0; i < plen; i++) 00112 ptr[i] = '\000'; 00113 } 00114 00115 ret = ConverseStub(check); 00116 if (ret < 0) 00117 { 00118 if (!check) 00119 kdError(900) << k_lineinfo << "Converstation with tdesu_stub failed\n"; 00120 return ret; 00121 } 00122 else if (ret == 1) 00123 { 00124 kill(m_Pid, SIGTERM); 00125 waitForChild(); 00126 ret = SshIncorrectPassword; 00127 } 00128 00129 if (check == 1) 00130 { 00131 waitForChild(); 00132 return 0; 00133 } 00134 00135 setExitString("Waiting for forwarded connections to terminate"); 00136 ret = waitForChild(); 00137 return ret; 00138 } 00139 00140 /* 00141 * Create a port forwarding for DCOP. For the remote port, we take a pseudo 00142 * random number between 10k and 50k. This is not ok, of course, but I see 00143 * no other way. There is, afaik, no security issue involved here. If the port 00144 * happens to be occupied, ssh will refuse to start. 00145 * 00146 * 14/SEP/2000: DCOP forwarding is not used anymore. 00147 */ 00148 00149 TQCString SshProcess::dcopForward() 00150 { 00151 TQCString result; 00152 00153 setDcopTransport("tcp"); 00154 00155 TQCString srv = StubProcess::dcopServer(); 00156 if (srv.isEmpty()) 00157 return result; 00158 00159 int i = srv.find('/'); 00160 if (i == -1) 00161 return result; 00162 if (srv.left(i) != "tcp") 00163 return result; 00164 int j = srv.find(':', ++i); 00165 if (j == -1) 00166 return result; 00167 TQCString host = srv.mid(i, j-i); 00168 bool ok; 00169 int port = srv.mid(++j).toInt(&ok); 00170 if (!ok) 00171 return result; 00172 00173 m_dcopPort = 10000 + (int) ((40000.0 * rand()) / (1.0 + RAND_MAX)); 00174 result.sprintf("%d:%s:%d", m_dcopPort, host.data(), port); 00175 return result; 00176 } 00177 00178 00179 /* 00180 * Conversation with ssh. 00181 * If check is 0, this waits for either a "Password: " prompt, 00182 * or the header of the stub. If a prompt is received, the password is 00183 * written back. Used for running a command. 00184 * If check is 1, operation is the same as 0 except that if a stub header is 00185 * received, the stub is stopped with the "stop" command. This is used for 00186 * checking a password. 00187 * If check is 2, operation is the same as 1, except that no password is 00188 * written. The prompt is saved to m_Prompt. Used for checking the need for 00189 * a password. 00190 */ 00191 00192 int SshProcess::ConverseSsh(const char *password, int check) 00193 { 00194 unsigned i, j, colon; 00195 00196 TQCString line; 00197 int state = 0; 00198 00199 while (state < 2) 00200 { 00201 line = readLine(); 00202 const uint len = line.length(); 00203 if (line.isNull()) 00204 return -1; 00205 00206 switch (state) { 00207 case 0: 00208 // Check for "tdesu_stub" header. 00209 if (line == "tdesu_stub") 00210 { 00211 unreadLine(line); 00212 return 0; 00213 } 00214 00215 // Match "Password: " with the regex ^[^:]+:[\w]*$. 00216 for (i=0,j=0,colon=0; i<len; i++) 00217 { 00218 if (line[i] == ':') 00219 { 00220 j = i; colon++; 00221 continue; 00222 } 00223 if (!isspace(line[i])) 00224 j++; 00225 } 00226 if ((colon == 1) && (line[j] == ':')) 00227 { 00228 if (check == 2) 00229 { 00230 m_Prompt = line; 00231 return SshNeedsPassword; 00232 } 00233 WaitSlave(); 00234 write(m_Fd, password, strlen(password)); 00235 write(m_Fd, "\n", 1); 00236 state++; 00237 break; 00238 } 00239 00240 // Warning/error message. 00241 m_Error += line; m_Error += "\n"; 00242 if (m_bTerminal) 00243 fprintf(stderr, "ssh: %s\n", line.data()); 00244 break; 00245 00246 case 1: 00247 if (line.isEmpty()) 00248 { 00249 state++; 00250 break; 00251 } 00252 return -1; 00253 } 00254 } 00255 return 0; 00256 } 00257 00258 00259 // Display redirection is handled by ssh natively. 00260 TQCString SshProcess::display() 00261 { 00262 return "no"; 00263 } 00264 00265 00266 TQCString SshProcess::displayAuth() 00267 { 00268 return "no"; 00269 } 00270 00271 00272 // Return the remote end of the forwarded connection. 00273 TQCString SshProcess::dcopServer() 00274 { 00275 return TQCString().sprintf("tcp/localhost:%d", m_dcopPort); 00276 } 00277 00278 void SshProcess::virtual_hook( int id, void* data ) 00279 { StubProcess::virtual_hook( id, data ); }