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