/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- _channel
- setUser
- sshRead
- sshRead
- sshWrite
- sshWrite
- sshShutdown
- sshConnect
- sshConnect
- authKbdint
- authKbdint
- openChannel
- openChannel
- readChannel
- writeChannel
- closeChannel
- closeChannel
- dump
// ssh.cpp: HyperText Transport Protocol handler for Cygnal, for Gnash.
//
// Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
//
#include <boost/thread/mutex.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/shared_array.hpp>
#include <boost/scoped_array.hpp>
#include <boost/cstdint.hpp>
#include <boost/array.hpp>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string>
#include <vector>
#include <iostream>
#include <cstring>
#include <sstream>
#include <sys/types.h>
#include <sys/stat.h>
#include <algorithm>
#include <cstdlib> // getenv
#include "GnashSystemIOHeaders.h" // read()
#include "sshclient.h"
#include "amf.h"
#include "element.h"
#include "cque.h"
#include "log.h"
#include "network.h"
#include "utility.h"
#include "buffer.h"
#include "diskstream.h"
#include "cache.h"
extern "C" {
# include <libssh/libssh.h>
# include <libssh/sftp.h>
}
#if defined(_WIN32) || defined(WIN32)
# define __PRETTY_FUNCTION__ __FUNCDNAME__
# include <winsock2.h>
# include <direct.h>
#else
# include <unistd.h>
# include <sys/param.h>
#endif
using namespace gnash;
using namespace std;
static boost::mutex stl_mutex;
namespace gnash
{
SSHClient::SSHClient()
: _hostname("localhost"),
_need_server_auth(true),
_state(0),
_session(0),
_channel(0)
{
// GNASH_REPORT_FUNCTION;
// Set the default user name
setUser();
}
SSHClient::~SSHClient()
{
// GNASH_REPORT_FUNCTION;
sshShutdown();
}
void
SSHClient::setUser()
{
// GNASH_REPORT_FUNCTION;
string user = std::getenv("USER");
if (!user.empty()) {
_user = user;
}
}
// Read bytes from the already opened SSH connection
int
SSHClient::sshRead(amf::Buffer &buf)
{
GNASH_REPORT_FUNCTION;
return sshRead(buf.reference(), buf.size());
}
int
SSHClient::sshRead(boost::uint8_t *buf, size_t size)
{
GNASH_REPORT_FUNCTION;
int ret = channel_read(_channel, buf, size, 0);
if (ret < 0) {
log_error("SSH read error was: \"%s\"!", ssh_get_error(_session));
}
return ret;
}
// Write bytes to the already opened SSH connection
int
SSHClient::sshWrite(amf::Buffer &buf)
{
GNASH_REPORT_FUNCTION;
return sshWrite(buf.reference(), buf.allocated());
}
int
SSHClient::sshWrite(const boost::uint8_t *buf, size_t size)
{
GNASH_REPORT_FUNCTION;
int ret = channel_write(_channel, buf, size);
if (ret < 0) {
log_error("SSH write error was: \"%s\"!", ssh_get_error(_session));
}
return ret;
}
// Shutdown the Context for this connection
bool
SSHClient::sshShutdown()
{
// GNASH_REPORT_FUNCTION;
if (_session) {
ssh_disconnect(_session);
ssh_finalize();
}
free(_session);
_session = 0;
return true;
}
// sshConnect() is how the client connects to the server
bool
SSHClient::sshConnect(int fd)
{
return sshConnect(fd, _hostname);
}
bool
SSHClient::sshConnect(int /* fd */, std::string &hostname)
{
// GNASH_REPORT_FUNCTION;
char *password;
char *banner;
char *hexa;
// We always need a hostname to connect to
if (ssh_options_set(_session, SSH_OPTIONS_HOST, hostname.c_str()) < 0) {
log_error("Couldn't set hostname option");
return false;
}
// We always need a user name for the connection
if (_user.empty()) {
if (ssh_options_set(_session, SSH_OPTIONS_USER, _user.c_str()) < 0) {
log_error("Couldn't set user name option");
return false;
}
}
// Start a new session
_session = ssh_new();
if(ssh_connect(_session)){
log_error("Connection failed : %s\n", ssh_get_error(_session));
sshShutdown();
return false;
}
_state = ssh_is_server_known(_session);
unsigned char *hash = 0;
int hlen = ssh_get_pubkey_hash(_session, &hash);
if (hlen < 0) {
sshShutdown();
return false;
}
switch(_state){
case SSH_SERVER_KNOWN_OK: // ok
log_debug("SSH Server is currently known: %d", _state);
break;
case SSH_SERVER_KNOWN_CHANGED:
log_error("Host key for server changed : server's one is now: ");
ssh_print_hexa("Public key hash", hash, hlen);
free(hash);
log_error("For security reason, connection will be stopped");
sshShutdown();
return false;;
case SSH_SERVER_FOUND_OTHER:
log_error("The host key for this server was not found but an other type of key exists.");
log_error("An attacker might change the default server key to confuse your client"
"into thinking the key does not exist\n"
"We advise you to rerun the client with -d or -r for more safety.");
sshShutdown();
return false;;
case SSH_SERVER_NOT_KNOWN:
hexa = ssh_get_hexa(hash, hlen);
free(hash);
// FIXME: for now, accecpt all new keys, and update the
// $HOME/.ssh/know_hosts file.
#if 0
log_error("The server is unknown. Do you trust the host key ? (yes,no)");
log_error("Public key hash: %s", hexa);
free(hexa);
fgets(buf, sizeof(buf), stdin);
if(strncasecmp(buf, "yes", 3) != 0){
sshShutdown();
return false;
}
log_error("This new key will be written on disk for further usage. do you agree? (yes,no) ");
fgets(buf, sizeof(buf), stdin);
if(strncasecmp(buf, "yes", 3)==0){
if(ssh_write_knownhost(_session))
log_error("%s", ssh_get_error(_session));
}
#else
if(ssh_write_knownhost(_session)) {
log_error("%s", ssh_get_error(_session));
}
#endif
break;
case SSH_SERVER_ERROR:
free(hash);
log_error("%s", ssh_get_error(_session));
sshShutdown();
return false;
}
free(hash);
ssh_userauth_none(_session, NULL);
int auth = ssh_auth_list(_session);
// log_debug("auth: 0x%04x", auth);
log_debug("supported auth methods: ");
if (auth & SSH_AUTH_METHOD_PUBLICKEY) {
log_debug("\tpublickey");
}
if (auth & SSH_AUTH_METHOD_INTERACTIVE) {
log_debug("\tkeyboard-interactive");
}
/* no ? you should :) */
auth=ssh_userauth_autopubkey(_session, NULL);
if(auth == SSH_AUTH_ERROR){
log_debug("Authenticating with pubkey: %s",ssh_get_error(_session));
ssh_finalize();
return false;
}
banner = ssh_get_issue_banner(_session);
if(banner){
log_debug("%s", banner);
free(banner);
}
if(auth != SSH_AUTH_SUCCESS){
auth = authKbdint(_session);
if(auth == SSH_AUTH_ERROR){
log_error("authenticating with keyb-interactive: %s",
ssh_get_error(_session));
ssh_finalize();
return false;
}
}
if(auth != SSH_AUTH_SUCCESS){
password = getpass("Password: ");
if(ssh_userauth_password(_session, NULL, password) != SSH_AUTH_SUCCESS){
log_error("Authentication failed: %s",ssh_get_error(_session));
ssh_disconnect(_session);
ssh_finalize();
return false;
}
memset(password, 0, strlen(password));
}
ssh_log(_session, SSH_LOG_FUNCTIONS, "Authentication success");
#if 0
if(strstr(argv[0],"sftp")){
sftp = 1;
ssh_log(_session, SSH_LOG_FUNCTIONS, "Doing sftp instead");
}
if(!sftp){
if(!cmds[0])
shell(_session);
else
batch_shell(_session);
}
else
do_sftp(_session);
if(!sftp && !cmds[0])
do_cleanup(0);
#endif
return true;
}
int
SSHClient::authKbdint()
{
// GNASH_REPORT_FUNCTION;
return authKbdint(_session);
}
int
SSHClient::authKbdint(ssh_session session)
{
// GNASH_REPORT_FUNCTION;
int err = ssh_userauth_kbdint(session, NULL, NULL);
char *name,*instruction,*prompt,*ptr;
char buffer[128];
int i,n;
char echo;
while (err == SSH_AUTH_INFO){
name = const_cast<char *>(ssh_userauth_kbdint_getname(session));
instruction = const_cast<char *>(ssh_userauth_kbdint_getinstruction(session));
n=ssh_userauth_kbdint_getnprompts(session);
if(strlen(name)>0)
log_debug("%s", name);
if(strlen(instruction)>0)
log_debug("%s", instruction);
for(i=0; i<n; ++i){
prompt = const_cast<char *>(ssh_userauth_kbdint_getprompt(session, i, &echo));
if(echo){
log_debug("%s", prompt);
fgets(buffer,sizeof(buffer),stdin);
buffer[sizeof(buffer)-1]=0;
if((ptr=strchr(buffer,'\n')))
*ptr=0;
if (ssh_userauth_kbdint_setanswer(session, i, buffer) < 0) {
return SSH_AUTH_ERROR;
}
memset(buffer,0,strlen(buffer));
} else {
ptr=getpass(prompt);
if (ssh_userauth_kbdint_setanswer(session, i, ptr) < 0) {
return SSH_AUTH_ERROR;
}
}
}
err=ssh_userauth_kbdint(session, NULL, NULL);
}
return err;
}
// Channel operations
ssh_channel
SSHClient::openChannel()
{
// GNASH_REPORT_FUNCTION;
return openChannel(_session);
}
ssh_channel
SSHClient::openChannel(ssh_session session)
{
// GNASH_REPORT_FUNCTION;
if (session) {
_channel = channel_new(session);
if (_channel) {
if (channel_open_session(_channel) != SSH_OK) {
log_error("Can't open the SSH channel!");
}
} else {
log_error("Can't allocate memory for new SSH channel!");
}
}
return _channel;
}
int
SSHClient::readChannel(ssh_channel channel, amf::Buffer &buf)
{
// GNASH_REPORT_FUNCTION;
int ret = -1;
if (channel) {
ret = channel_read(channel, buf.reference(), buf.size(), 0);
} else {
log_error("Can't read from a non-existant channel!");
}
return ret;
}
int
SSHClient::writeChannel(ssh_channel channel, amf::Buffer &buf)
{
// GNASH_REPORT_FUNCTION;
int ret = -1;
if (channel) {
ret = channel_write(channel, buf.reference(), buf.size());
} else {
log_error("Can't write to a non-existant channel!");
}
return ret;
}
void
SSHClient::closeChannel()
{
// GNASH_REPORT_FUNCTION;
return closeChannel(_channel);
}
void
SSHClient::closeChannel(ssh_channel channel)
{
// GNASH_REPORT_FUNCTION;
if (channel) {
channel_close(channel);
// free(channel);
_channel = 0;
}
}
void
SSHClient::dump() {
// GNASH_REPORT_FUNCTION;
boost::mutex::scoped_lock lock(stl_mutex);
log_debug (_("==== The SSH header breaks down as follows: ===="));
ssh_version(0);
}
} // end of gnash namespace
// local Variables:
// mode: C++
// indent-tabs-mode: t
// End: