1 // ssh.cpp: HyperText Transport Protocol handler for Cygnal, for Gnash.
2 //
3 // Copyright (C) 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 //
19
20 #include <mutex>
21 #include <cstdint>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include <string>
26 #include <vector>
27 #include <iostream>
28 #include <cstring>
29 #include <sstream>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <algorithm>
33 #include <cstdlib> // getenv
34
35 #include "GnashSystemIOHeaders.h" // read()
36 #include "sshclient.h"
37 #include "amf.h"
38 #include "element.h"
39 #include "cque.h"
40 #include "log.h"
41 #include "network.h"
42 #include "utility.h"
43 #include "buffer.h"
44 #include "diskstream.h"
45 #include "cache.h"
46
47 extern "C" {
48 # include <libssh/libssh.h>
49 # include <libssh/sftp.h>
50 }
51
52 #if defined(_WIN32) || defined(WIN32)
53 # define __PRETTY_FUNCTION__ __FUNCDNAME__
54 # include <winsock2.h>
55 # include <direct.h>
56 #else
57 # include <unistd.h>
58 # include <sys/param.h>
59 #endif
60
61 using namespace gnash;
62 using namespace std;
63
64 static std::mutex stl_mutex;
65
66 namespace gnash
67 {
68
SSHClient()69 SSHClient::SSHClient()
70 : _hostname("localhost"),
71 _need_server_auth(true),
72 _state(0),
73 _session(0),
74 _channel(0)
75 {
76 // GNASH_REPORT_FUNCTION;
77
78 // Set the default user name
79 setUser();
80 }
81
~SSHClient()82 SSHClient::~SSHClient()
83 {
84 // GNASH_REPORT_FUNCTION;
85
86 sshShutdown();
87 }
88
89 void
setUser()90 SSHClient::setUser()
91 {
92 // GNASH_REPORT_FUNCTION;
93
94 string user = std::getenv("USER");
95 if (!user.empty()) {
96 _user = user;
97 }
98 }
99
100 // Read bytes from the already opened SSH connection
101 int
sshRead(cygnal::Buffer & buf)102 SSHClient::sshRead(cygnal::Buffer &buf)
103 {
104 GNASH_REPORT_FUNCTION;
105
106 return sshRead(buf.reference(), buf.size());
107 }
108
109 int
sshRead(std::uint8_t * buf,size_t size)110 SSHClient::sshRead(std::uint8_t *buf, size_t size)
111 {
112 GNASH_REPORT_FUNCTION;
113
114 int ret = channel_read(_channel, buf, size, 0);
115 if (ret < 0) {
116 log_error(_("SSH read error was: \"%s\"!"), ssh_get_error(_session));
117 }
118
119 return ret;
120 }
121
122 // Write bytes to the already opened SSH connection
123 int
sshWrite(cygnal::Buffer & buf)124 SSHClient::sshWrite(cygnal::Buffer &buf)
125 {
126 GNASH_REPORT_FUNCTION;
127
128 return sshWrite(buf.reference(), buf.allocated());
129 }
130
131 int
sshWrite(const std::uint8_t * buf,size_t size)132 SSHClient::sshWrite(const std::uint8_t *buf, size_t size)
133 {
134 GNASH_REPORT_FUNCTION;
135
136 int ret = channel_write(_channel, buf, size);
137 if (ret < 0) {
138 log_error(_("SSH write error was: \"%s\"!"), ssh_get_error(_session));
139 }
140 return ret;
141 }
142
143 // Shutdown the Context for this connection
144 bool
sshShutdown()145 SSHClient::sshShutdown()
146 {
147 // GNASH_REPORT_FUNCTION;
148
149 if (_session) {
150 ssh_disconnect(_session);
151 ssh_finalize();
152 }
153 free(_session);
154 _session = 0;
155
156 return true;
157 }
158
159 // sshConnect() is how the client connects to the server
160 bool
sshConnect(int fd)161 SSHClient::sshConnect(int fd)
162 {
163 return sshConnect(fd, _hostname);
164 }
165
166 bool
sshConnect(int,std::string & hostname)167 SSHClient::sshConnect(int /* fd */, std::string &hostname)
168 {
169 // GNASH_REPORT_FUNCTION;
170 char *password;
171 char *banner;
172 char *hexa;
173
174 // We always need a hostname to connect to
175 if (ssh_options_set(_session, SSH_OPTIONS_HOST, hostname.c_str()) < 0) {
176 log_error(_("Couldn't set hostname option"));
177 return false;
178 }
179
180 // We always need a user name for the connection
181 if (_user.empty()) {
182 if (ssh_options_set(_session, SSH_OPTIONS_USER, _user.c_str()) < 0) {
183 log_error(_("Couldn't set user name option"));
184 return false;
185 }
186 }
187
188 // Start a new session
189 _session = ssh_new();
190 if(ssh_connect(_session)){
191 log_error(_("Connection failed : %s\n"), ssh_get_error(_session));
192 sshShutdown();
193 return false;
194 }
195
196 _state = ssh_is_server_known(_session);
197
198 unsigned char *hash = 0;
199 int hlen = ssh_get_pubkey_hash(_session, &hash);
200 if (hlen < 0) {
201 sshShutdown();
202 return false;
203 }
204 switch(_state){
205 case SSH_SERVER_KNOWN_OK: // ok
206 log_debug(_("SSH Server is currently known: %d"), _state);
207 break;
208 case SSH_SERVER_KNOWN_CHANGED:
209 log_error(_("Host key for server changed : server's one is now: "));
210 ssh_print_hexa(_("Public key hash"), hash, hlen);
211 free(hash);
212 log_error(_("For security reason, connection will be stopped"));
213 sshShutdown();
214 return false;;
215 case SSH_SERVER_FOUND_OTHER:
216 log_error(_("The host key for this server was not found but an other type of key exists."));
217 log_error(_("An attacker might change the default server key to confuse your client"
218 " into thinking the key does not exist"
219 "We advise you to rerun the client with -d or -r for more safety."));
220 sshShutdown();
221 return false;;
222 case SSH_SERVER_NOT_KNOWN:
223 hexa = ssh_get_hexa(hash, hlen);
224 free(hash);
225 // FIXME: for now, accecpt all new keys, and update the
226 // $HOME/.ssh/know_hosts file.
227 #if 0
228 log_error(_("The server is unknown. Do you trust the host key ? (yes,no)"));
229 log_error(_("Public key hash: %s"), hexa);
230 free(hexa);
231 fgets(buf, sizeof(buf), stdin);
232 if(strncasecmp(buf, "yes", 3) != 0){
233 sshShutdown();
234 return false;
235 }
236 log_error(_("This new key will be written on disk for further usage. do you agree? (yes,no) "));
237 fgets(buf, sizeof(buf), stdin);
238 if(strncasecmp(buf, "yes", 3)==0){
239 if(ssh_write_knownhost(_session))
240 log_error(ssh_get_error(_session));
241 }
242 #else
243 if(ssh_write_knownhost(_session)) {
244 log_error(ssh_get_error(_session));
245 }
246 #endif
247 break;
248 case SSH_SERVER_ERROR:
249 free(hash);
250 log_error(ssh_get_error(_session));
251 sshShutdown();
252 return false;
253 }
254
255 free(hash);
256
257 ssh_userauth_none(_session, NULL);
258
259 int auth = ssh_auth_list(_session);
260
261 // log_debug("auth: 0x%04x", auth);
262 log_debug(_("supported auth methods: "));
263 if (auth & SSH_AUTH_METHOD_PUBLICKEY) {
264 log_debug(_("\tpublickey"));
265 }
266 if (auth & SSH_AUTH_METHOD_INTERACTIVE) {
267 log_debug(_("\tkeyboard-interactive"));
268 }
269
270 /* no ? you should :) */
271 auth=ssh_userauth_autopubkey(_session, NULL);
272 if(auth == SSH_AUTH_ERROR){
273 log_debug(_("Authenticating with pubkey: %s"), ssh_get_error(_session));
274 ssh_finalize();
275 return false;
276 }
277 banner = ssh_get_issue_banner(_session);
278 if(banner){
279 log_debug(banner);
280 free(banner);
281 }
282 if(auth != SSH_AUTH_SUCCESS){
283 auth = authKbdint(_session);
284 if(auth == SSH_AUTH_ERROR){
285 log_error(_("authenticating with keyb-interactive: %s"),
286 ssh_get_error(_session));
287 ssh_finalize();
288 return false;
289 }
290 }
291 if(auth != SSH_AUTH_SUCCESS){
292 password = getpass("Password: ");
293 if(ssh_userauth_password(_session, NULL, password) != SSH_AUTH_SUCCESS){
294 log_error(_("Authentication failed: %s"), ssh_get_error(_session));
295 ssh_disconnect(_session);
296 ssh_finalize();
297 return false;
298 }
299 memset(password, 0, strlen(password));
300 }
301 ssh_log(_session, SSH_LOG_FUNCTIONS, "Authentication success");
302
303 #if 0
304 if(strstr(argv[0],"sftp")){
305 sftp = 1;
306 ssh_log(_session, SSH_LOG_FUNCTIONS, "Doing sftp instead");
307 }
308 if(!sftp){
309 if(!cmds[0])
310 shell(_session);
311 else
312 batch_shell(_session);
313 }
314 else
315 do_sftp(_session);
316 if(!sftp && !cmds[0])
317 do_cleanup(0);
318 #endif
319
320 return true;
321 }
322
323 int
authKbdint()324 SSHClient::authKbdint()
325 {
326 // GNASH_REPORT_FUNCTION;
327 return authKbdint(_session);
328 }
329
330 int
authKbdint(ssh_session session)331 SSHClient::authKbdint(ssh_session session)
332 {
333 // GNASH_REPORT_FUNCTION;
334 int err = ssh_userauth_kbdint(session, NULL, NULL);
335 char *name,*instruction,*prompt,*ptr;
336 char buffer[128];
337 int i,n;
338 char echo;
339 while (err == SSH_AUTH_INFO){
340 name = const_cast<char *>(ssh_userauth_kbdint_getname(session));
341 instruction = const_cast<char *>(ssh_userauth_kbdint_getinstruction(session));
342 n=ssh_userauth_kbdint_getnprompts(session);
343 if(strlen(name)>0)
344 log_debug(name);
345 if(strlen(instruction)>0)
346 log_debug(instruction);
347 for(i=0; i<n; ++i){
348 prompt = const_cast<char *>(ssh_userauth_kbdint_getprompt(session, i, &echo));
349 if(echo){
350 log_debug(prompt);
351 fgets(buffer,sizeof(buffer),stdin);
352 buffer[sizeof(buffer)-1]=0;
353 if((ptr=strchr(buffer,'\n')))
354 *ptr=0;
355 if (ssh_userauth_kbdint_setanswer(session, i, buffer) < 0) {
356 return SSH_AUTH_ERROR;
357 }
358 memset(buffer,0,strlen(buffer));
359 } else {
360 ptr=getpass(prompt);
361 if (ssh_userauth_kbdint_setanswer(session, i, ptr) < 0) {
362 return SSH_AUTH_ERROR;
363 }
364 }
365 }
366 err=ssh_userauth_kbdint(session, NULL, NULL);
367 }
368
369 return err;
370 }
371
372 // Channel operations
373 ssh_channel
openChannel()374 SSHClient::openChannel()
375 {
376 // GNASH_REPORT_FUNCTION;
377 return openChannel(_session);
378 }
379
380
381 ssh_channel
openChannel(ssh_session session)382 SSHClient::openChannel(ssh_session session)
383 {
384 // GNASH_REPORT_FUNCTION;
385 if (session) {
386 _channel = channel_new(session);
387 if (_channel) {
388 if (channel_open_session(_channel) != SSH_OK) {
389 log_error(_("Can't open the SSH channel!"));
390 }
391 } else {
392 log_error(_("Can't allocate memory for new SSH channel!"));
393 }
394 }
395
396 return _channel;
397 }
398
399 int
readChannel(ssh_channel channel,cygnal::Buffer & buf)400 SSHClient::readChannel(ssh_channel channel, cygnal::Buffer &buf)
401 {
402 // GNASH_REPORT_FUNCTION;
403 int ret = -1;
404
405 if (channel) {
406 ret = channel_read(channel, buf.reference(), buf.size(), 0);
407 } else {
408 log_error(_("Can't read from a non-existent channel!"));
409 }
410
411 return ret;
412 }
413
414 int
writeChannel(ssh_channel channel,cygnal::Buffer & buf)415 SSHClient::writeChannel(ssh_channel channel, cygnal::Buffer &buf)
416 {
417 // GNASH_REPORT_FUNCTION;
418 int ret = -1;
419
420 if (channel) {
421 ret = channel_write(channel, buf.reference(), buf.size());
422 } else {
423 log_error(_("Can't write to a non-existent channel!"));
424 }
425
426 return ret;
427 }
428
429 void
closeChannel()430 SSHClient::closeChannel()
431 {
432 // GNASH_REPORT_FUNCTION;
433 return closeChannel(_channel);
434 }
435
436 void
closeChannel(ssh_channel channel)437 SSHClient::closeChannel(ssh_channel channel)
438 {
439 // GNASH_REPORT_FUNCTION;
440
441 if (channel) {
442 channel_close(channel);
443 // free(channel);
444 _channel = 0;
445 }
446 }
447
448
449 void
dump()450 SSHClient::dump() {
451 // GNASH_REPORT_FUNCTION;
452
453 std::lock_guard<std::mutex> lock(stl_mutex);
454
455 log_debug (_("==== The SSH header breaks down as follows: ===="));
456
457 ssh_version(0);
458 }
459
460 } // end of gnash namespace
461
462
463 // local Variables:
464 // mode: C++
465 // indent-tabs-mode: nil
466 // End:
467