File indexing completed on 2025-02-02 14:11:33
0001 /* 0002 This file is part of the KDE project, module kdesu. 0003 SPDX-FileCopyrightText: 2000 Geert Jansen <jansen@kde.org> 0004 0005 SPDX-License-Identifier: GPL-2.0-only 0006 0007 ssh.cpp: Execute a program on a remote machine using ssh. 0008 */ 0009 0010 #include "sshprocess.h" 0011 0012 #include "kcookie_p.h" 0013 #include "stubprocess_p.h" 0014 #include <ksu_debug.h> 0015 0016 #include <signal.h> 0017 #include <time.h> 0018 #include <unistd.h> 0019 0020 extern int kdesuDebugArea(); 0021 0022 namespace KDESu 0023 { 0024 using namespace KDESuPrivate; 0025 0026 class SshProcessPrivate : public StubProcessPrivate 0027 { 0028 public: 0029 SshProcessPrivate(const QByteArray &host) 0030 : host(host) 0031 , stub("kdesu_stub") 0032 { 0033 } 0034 QByteArray prompt; 0035 QByteArray host; 0036 QByteArray error; 0037 QByteArray stub; 0038 }; 0039 0040 SshProcess::SshProcess(const QByteArray &host, const QByteArray &user, const QByteArray &command) 0041 : StubProcess(*new SshProcessPrivate(host)) 0042 { 0043 m_user = user; 0044 m_command = command; 0045 srand(time(nullptr)); 0046 } 0047 0048 SshProcess::~SshProcess() = default; 0049 0050 void SshProcess::setHost(const QByteArray &host) 0051 { 0052 Q_D(SshProcess); 0053 0054 d->host = host; 0055 } 0056 0057 void SshProcess::setStub(const QByteArray &stub) 0058 { 0059 Q_D(SshProcess); 0060 0061 d->stub = stub; 0062 } 0063 0064 int SshProcess::checkInstall(const char *password) 0065 { 0066 return exec(password, 1); 0067 } 0068 0069 int SshProcess::checkNeedPassword() 0070 { 0071 return exec(nullptr, 2); 0072 } 0073 0074 int SshProcess::exec(const char *password, int check) 0075 { 0076 Q_D(SshProcess); 0077 0078 if (check) { 0079 setTerminal(true); 0080 } 0081 0082 QList<QByteArray> args; 0083 args += "-l"; 0084 args += m_user; 0085 args += "-o"; 0086 args += "StrictHostKeyChecking=no"; 0087 args += d->host; 0088 args += d->stub; 0089 0090 if (StubProcess::exec("ssh", args) < 0) { 0091 return check ? SshNotFound : -1; 0092 } 0093 0094 int ret = converseSsh(password, check); 0095 if (ret < 0) { 0096 if (!check) { 0097 qCCritical(KSU_LOG) << "[" << __FILE__ << ":" << __LINE__ << "] " 0098 << "Conversation with ssh failed."; 0099 } 0100 return ret; 0101 } 0102 if (check == 2) { 0103 if (ret == 1) { 0104 kill(m_pid, SIGTERM); 0105 waitForChild(); 0106 } 0107 return ret; 0108 } 0109 0110 if (m_erase && password) { 0111 memset(const_cast<char *>(password), 0, qstrlen(password)); 0112 } 0113 0114 ret = converseStub(check); 0115 if (ret < 0) { 0116 if (!check) { 0117 qCCritical(KSU_LOG) << "[" << __FILE__ << ":" << __LINE__ << "] " 0118 << "Conversation with kdesu_stub failed."; 0119 } 0120 return ret; 0121 } else if (ret == 1) { 0122 kill(m_pid, SIGTERM); 0123 waitForChild(); 0124 ret = SshIncorrectPassword; 0125 } 0126 0127 if (check == 1) { 0128 waitForChild(); 0129 return 0; 0130 } 0131 0132 setExitString("Waiting for forwarded connections to terminate"); 0133 ret = waitForChild(); 0134 return ret; 0135 } 0136 0137 QByteArray SshProcess::prompt() const 0138 { 0139 Q_D(const SshProcess); 0140 0141 return d->prompt; 0142 } 0143 0144 QByteArray SshProcess::error() const 0145 { 0146 Q_D(const SshProcess); 0147 0148 return d->error; 0149 } 0150 0151 /* 0152 * Conversation with ssh. 0153 * If check is 0, this waits for either a "Password: " prompt, 0154 * or the header of the stub. If a prompt is received, the password is 0155 * written back. Used for running a command. 0156 * If check is 1, operation is the same as 0 except that if a stub header is 0157 * received, the stub is stopped with the "stop" command. This is used for 0158 * checking a password. 0159 * If check is 2, operation is the same as 1, except that no password is 0160 * written. The prompt is saved to prompt. Used for checking the need for 0161 * a password. 0162 */ 0163 int SshProcess::converseSsh(const char *password, int check) 0164 { 0165 Q_D(SshProcess); 0166 0167 unsigned i; 0168 unsigned j; 0169 unsigned colon; 0170 0171 QByteArray line; 0172 int state = 0; 0173 0174 while (state < 2) { 0175 line = readLine(); 0176 const uint len = line.length(); 0177 if (line.isNull()) { 0178 return -1; 0179 } 0180 0181 switch (state) { 0182 case 0: 0183 // Check for "kdesu_stub" header. 0184 if (line == "kdesu_stub") { 0185 unreadLine(line); 0186 return 0; 0187 } 0188 0189 // Match "Password: " with the regex ^[^:]+:[\w]*$. 0190 for (i = 0, j = 0, colon = 0; i < len; ++i) { 0191 if (line[i] == ':') { 0192 j = i; 0193 colon++; 0194 continue; 0195 } 0196 if (!isspace(line[i])) { 0197 j++; 0198 } 0199 } 0200 if ((colon == 1) && (line[j] == ':')) { 0201 if (check == 2) { 0202 d->prompt = line; 0203 return SshNeedsPassword; 0204 } 0205 if (waitSlave()) { 0206 return -1; 0207 } 0208 write(fd(), password, strlen(password)); 0209 write(fd(), "\n", 1); 0210 state++; 0211 break; 0212 } 0213 0214 // Warning/error message. 0215 d->error += line; 0216 d->error += '\n'; 0217 if (m_terminal) { 0218 fprintf(stderr, "ssh: %s\n", line.constData()); 0219 } 0220 break; 0221 0222 case 1: 0223 if (line.isEmpty()) { 0224 state++; 0225 break; 0226 } 0227 return -1; 0228 } 0229 } 0230 return 0; 0231 } 0232 0233 // Display redirection is handled by ssh natively. 0234 QByteArray SshProcess::display() 0235 { 0236 return "no"; 0237 } 0238 0239 QByteArray SshProcess::displayAuth() 0240 { 0241 return "no"; 0242 } 0243 0244 void SshProcess::virtual_hook(int id, void *data) 0245 { 0246 StubProcess::virtual_hook(id, data); 0247 } 0248 0249 } // namespace KDESu