KDESu

sshprocess.cpp
1 /*
2  This file is part of the KDE project, module kdesu.
3  SPDX-FileCopyrightText: 2000 Geert Jansen <[email protected]>
4 
5  SPDX-License-Identifier: GPL-2.0-only
6 
7  ssh.cpp: Execute a program on a remote machine using ssh.
8 */
9 
10 #include "sshprocess.h"
11 
12 #include "kcookie_p.h"
13 #include "stubprocess_p.h"
14 #include <ksu_debug.h>
15 
16 #include <signal.h>
17 #include <time.h>
18 #include <unistd.h>
19 
20 extern int kdesuDebugArea();
21 
22 namespace KDESu
23 {
24 using namespace KDESuPrivate;
25 
26 class SshProcessPrivate : public StubProcessPrivate
27 {
28 public:
29  SshProcessPrivate(const QByteArray &host)
30  : host(host)
31  , stub("kdesu_stub")
32  {
33  }
34  QByteArray prompt;
37  QByteArray stub;
38 };
39 
40 SshProcess::SshProcess(const QByteArray &host, const QByteArray &user, const QByteArray &command)
41  : StubProcess(*new SshProcessPrivate(host))
42 {
43  m_user = user;
44  m_command = command;
45  srand(time(nullptr));
46 }
47 
48 SshProcess::~SshProcess() = default;
49 
51 {
52  Q_D(SshProcess);
53 
54  d->host = host;
55 }
56 
57 void SshProcess::setStub(const QByteArray &stub)
58 {
59  Q_D(SshProcess);
60 
61  d->stub = stub;
62 }
63 
64 int SshProcess::checkInstall(const char *password)
65 {
66  return exec(password, 1);
67 }
68 
69 int SshProcess::checkNeedPassword()
70 {
71  return exec(nullptr, 2);
72 }
73 
74 int SshProcess::exec(const char *password, int check)
75 {
76  Q_D(SshProcess);
77 
78  if (check) {
79  setTerminal(true);
80  }
81 
82  QList<QByteArray> args;
83  args += "-l";
84  args += m_user;
85  args += "-o";
86  args += "StrictHostKeyChecking=no";
87  args += d->host;
88  args += d->stub;
89 
90  if (StubProcess::exec("ssh", args) < 0) {
91  return check ? SshNotFound : -1;
92  }
93 
94  int ret = converseSsh(password, check);
95  if (ret < 0) {
96  if (!check) {
97  qCCritical(KSU_LOG) << "[" << __FILE__ << ":" << __LINE__ << "] "
98  << "Conversation with ssh failed.";
99  }
100  return ret;
101  }
102  if (check == 2) {
103  if (ret == 1) {
104  kill(m_pid, SIGTERM);
105  waitForChild();
106  }
107  return ret;
108  }
109 
110  if (m_erase && password) {
111  memset(const_cast<char *>(password), 0, qstrlen(password));
112  }
113 
114  ret = converseStub(check);
115  if (ret < 0) {
116  if (!check) {
117  qCCritical(KSU_LOG) << "[" << __FILE__ << ":" << __LINE__ << "] "
118  << "Conversation with kdesu_stub failed.";
119  }
120  return ret;
121  } else if (ret == 1) {
122  kill(m_pid, SIGTERM);
123  waitForChild();
124  ret = SshIncorrectPassword;
125  }
126 
127  if (check == 1) {
128  waitForChild();
129  return 0;
130  }
131 
132  setExitString("Waiting for forwarded connections to terminate");
133  ret = waitForChild();
134  return ret;
135 }
136 
137 QByteArray SshProcess::prompt() const
138 {
139  Q_D(const SshProcess);
140 
141  return d->prompt;
142 }
143 
144 QByteArray SshProcess::error() const
145 {
146  Q_D(const SshProcess);
147 
148  return d->error;
149 }
150 
151 /*
152  * Conversation with ssh.
153  * If check is 0, this waits for either a "Password: " prompt,
154  * or the header of the stub. If a prompt is received, the password is
155  * written back. Used for running a command.
156  * If check is 1, operation is the same as 0 except that if a stub header is
157  * received, the stub is stopped with the "stop" command. This is used for
158  * checking a password.
159  * If check is 2, operation is the same as 1, except that no password is
160  * written. The prompt is saved to prompt. Used for checking the need for
161  * a password.
162  */
163 int SshProcess::converseSsh(const char *password, int check)
164 {
165  Q_D(SshProcess);
166 
167  unsigned i;
168  unsigned j;
169  unsigned colon;
170 
171  QByteArray line;
172  int state = 0;
173 
174  while (state < 2) {
175  line = readLine();
176  const uint len = line.length();
177  if (line.isNull()) {
178  return -1;
179  }
180 
181  switch (state) {
182  case 0:
183  // Check for "kdesu_stub" header.
184  if (line == "kdesu_stub") {
185  unreadLine(line);
186  return 0;
187  }
188 
189  // Match "Password: " with the regex ^[^:]+:[\w]*$.
190  for (i = 0, j = 0, colon = 0; i < len; ++i) {
191  if (line[i] == ':') {
192  j = i;
193  colon++;
194  continue;
195  }
196  if (!isspace(line[i])) {
197  j++;
198  }
199  }
200  if ((colon == 1) && (line[j] == ':')) {
201  if (check == 2) {
202  d->prompt = line;
203  return SshNeedsPassword;
204  }
205  if (waitSlave()) {
206  return -1;
207  }
208  write(fd(), password, strlen(password));
209  write(fd(), "\n", 1);
210  state++;
211  break;
212  }
213 
214  // Warning/error message.
215  d->error += line;
216  d->error += '\n';
217  if (m_terminal) {
218  fprintf(stderr, "ssh: %s\n", line.constData());
219  }
220  break;
221 
222  case 1:
223  if (line.isEmpty()) {
224  state++;
225  break;
226  }
227  return -1;
228  }
229  }
230  return 0;
231 }
232 
233 // Display redirection is handled by ssh natively.
234 QByteArray SshProcess::display()
235 {
236  return "no";
237 }
238 
239 QByteArray SshProcess::displayAuth()
240 {
241  return "no";
242 }
243 
244 void SshProcess::virtual_hook(int id, void *data)
245 {
246  StubProcess::virtual_hook(id, data);
247 }
248 
249 } // namespace KDESu
bool isNull() const const
void setHost(ScriptableExtension *host)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
ScriptableExtension * host() const
bool isEmpty() const const
const char * constData() const const
int length() const const
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Fri Sep 22 2023 04:04:04 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.