1 | /* vi: ts=8 sts=4 sw=4 |
2 | * |
3 | * This file is part of the KDE project, module kdesu. |
4 | * Copyright (C) 2000 Geert Jansen <jansen@kde.org> |
5 | * |
6 | * This is free software; you can use this library under the GNU Library |
7 | * General Public License, version 2. See the file "COPYING.LIB" for the |
8 | * exact licensing terms. |
9 | * |
10 | * ssh.cpp: Execute a program on a remote machine using ssh. |
11 | */ |
12 | |
13 | #include "ssh.h" |
14 | #include "kcookie.h" |
15 | |
16 | #include <config.h> |
17 | |
18 | #include <stdio.h> |
19 | #include <stdlib.h> |
20 | #include <unistd.h> |
21 | #include <fcntl.h> |
22 | #include <signal.h> |
23 | #include <errno.h> |
24 | #include <string.h> |
25 | #include <ctype.h> |
26 | #include <time.h> |
27 | |
28 | #include <sys/types.h> |
29 | #include <sys/stat.h> |
30 | |
31 | #include <QtCore/QBool> |
32 | |
33 | #include <kdebug.h> |
34 | #include <klocale.h> |
35 | #include <kstandarddirs.h> |
36 | |
37 | extern int kdesuDebugArea(); |
38 | |
39 | namespace KDESu { |
40 | |
41 | using namespace KDESuPrivate; |
42 | |
43 | class SshProcess::SshProcessPrivate |
44 | { |
45 | public: |
46 | SshProcessPrivate(const QByteArray &host) |
47 | : m_Host( host ) |
48 | , m_Stub( "kdesu_stub" ) |
49 | {} |
50 | QByteArray m_Prompt; |
51 | QByteArray m_Host; |
52 | QByteArray m_Error; |
53 | QByteArray m_Stub; |
54 | }; |
55 | |
56 | |
57 | SshProcess::SshProcess(const QByteArray &host, const QByteArray &user, const QByteArray &command) |
58 | : d( new SshProcessPrivate(host) ) |
59 | { |
60 | m_User = user; |
61 | m_Command = command; |
62 | srand(time(0L)); |
63 | } |
64 | |
65 | |
66 | SshProcess::~SshProcess() |
67 | { |
68 | delete d; |
69 | } |
70 | |
71 | void SshProcess::setHost(const QByteArray &host) |
72 | { |
73 | d->m_Host = host; |
74 | } |
75 | |
76 | |
77 | void SshProcess::setStub(const QByteArray &stub) |
78 | { |
79 | d->m_Stub = stub; |
80 | } |
81 | |
82 | |
83 | int SshProcess::checkInstall(const char *password) |
84 | { |
85 | return exec(password, 1); |
86 | } |
87 | |
88 | |
89 | int SshProcess::checkNeedPassword() |
90 | { |
91 | return exec(0L, 2); |
92 | } |
93 | |
94 | |
95 | int SshProcess::exec(const char *password, int check) |
96 | { |
97 | if (check) |
98 | setTerminal(true); |
99 | |
100 | QList<QByteArray> args; |
101 | args += "-l" ; args += m_User; |
102 | args += "-o" ; args += "StrictHostKeyChecking=no" ; |
103 | args += d->m_Host; args += d->m_Stub; |
104 | |
105 | if (StubProcess::exec("ssh" , args) < 0) |
106 | { |
107 | return check ? SshNotFound : -1; |
108 | } |
109 | |
110 | int ret = ConverseSsh(password, check); |
111 | if (ret < 0) |
112 | { |
113 | if (!check) |
114 | kError(kdesuDebugArea()) << k_lineinfo << "Conversation with ssh failed." ; |
115 | return ret; |
116 | } |
117 | if (check == 2) |
118 | { |
119 | if (ret == 1) |
120 | { |
121 | kill(m_Pid, SIGTERM); |
122 | waitForChild(); |
123 | } |
124 | return ret; |
125 | } |
126 | |
127 | if (m_bErase && password) |
128 | memset(const_cast<char *>(password), 0, qstrlen(password)); |
129 | |
130 | ret = ConverseStub(check); |
131 | if (ret < 0) |
132 | { |
133 | if (!check) |
134 | kError(kdesuDebugArea()) << k_lineinfo << "Conversation with kdesu_stub failed." ; |
135 | return ret; |
136 | } |
137 | else if (ret == 1) |
138 | { |
139 | kill(m_Pid, SIGTERM); |
140 | waitForChild(); |
141 | ret = SshIncorrectPassword; |
142 | } |
143 | |
144 | if (check == 1) |
145 | { |
146 | waitForChild(); |
147 | return 0; |
148 | } |
149 | |
150 | setExitString("Waiting for forwarded connections to terminate" ); |
151 | ret = waitForChild(); |
152 | return ret; |
153 | } |
154 | |
155 | QByteArray SshProcess::prompt() const |
156 | { |
157 | return d->m_Prompt; |
158 | } |
159 | |
160 | QByteArray SshProcess::error() const |
161 | { |
162 | return d->m_Error; |
163 | } |
164 | |
165 | |
166 | /* |
167 | * Conversation with ssh. |
168 | * If check is 0, this waits for either a "Password: " prompt, |
169 | * or the header of the stub. If a prompt is received, the password is |
170 | * written back. Used for running a command. |
171 | * If check is 1, operation is the same as 0 except that if a stub header is |
172 | * received, the stub is stopped with the "stop" command. This is used for |
173 | * checking a password. |
174 | * If check is 2, operation is the same as 1, except that no password is |
175 | * written. The prompt is saved to m_Prompt. Used for checking the need for |
176 | * a password. |
177 | */ |
178 | |
179 | int SshProcess::ConverseSsh(const char *password, int check) |
180 | { |
181 | unsigned i, j, colon; |
182 | |
183 | QByteArray line; |
184 | int state = 0; |
185 | |
186 | while (state < 2) |
187 | { |
188 | line = readLine(); |
189 | const uint len = line.length(); |
190 | if (line.isNull()) |
191 | return -1; |
192 | |
193 | switch (state) { |
194 | case 0: |
195 | // Check for "kdesu_stub" header. |
196 | if (line == "kdesu_stub" ) |
197 | { |
198 | unreadLine(line); |
199 | return 0; |
200 | } |
201 | |
202 | // Match "Password: " with the regex ^[^:]+:[\w]*$. |
203 | for (i=0,j=0,colon=0; i<len; ++i) |
204 | { |
205 | if (line[i] == ':') |
206 | { |
207 | j = i; colon++; |
208 | continue; |
209 | } |
210 | if (!isspace(line[i])) |
211 | j++; |
212 | } |
213 | if ((colon == 1) && (line[j] == ':')) |
214 | { |
215 | if (check == 2) |
216 | { |
217 | d->m_Prompt = line; |
218 | return SshNeedsPassword; |
219 | } |
220 | if (WaitSlave()) |
221 | return -1; |
222 | write(fd(), password, strlen(password)); |
223 | write(fd(), "\n" , 1); |
224 | state++; |
225 | break; |
226 | } |
227 | |
228 | // Warning/error message. |
229 | d->m_Error += line; d->m_Error += '\n'; |
230 | if (m_bTerminal) |
231 | fprintf(stderr, "ssh: %s\n" , line.constData()); |
232 | break; |
233 | |
234 | case 1: |
235 | if (line.isEmpty()) |
236 | { |
237 | state++; |
238 | break; |
239 | } |
240 | return -1; |
241 | } |
242 | } |
243 | return 0; |
244 | } |
245 | |
246 | |
247 | // Display redirection is handled by ssh natively. |
248 | QByteArray SshProcess::display() |
249 | { |
250 | return "no" ; |
251 | } |
252 | |
253 | |
254 | QByteArray SshProcess::displayAuth() |
255 | { |
256 | return "no" ; |
257 | } |
258 | |
259 | void SshProcess::virtual_hook( int id, void* data ) |
260 | { StubProcess::virtual_hook( id, data ); } |
261 | |
262 | } |
263 | |