Added SSHv2 connections to retroshare-nogui.

* Using libssh in a seperate thread.
 * Binds to fixed port, and accepts connections from standard SSH clients.
 * Only an Echo Server at the moment: Interface to be decided yet.
 * Only accepts 1 connection, 2nd connection hangs at the moment.
 * A long way to go before it will be useful!



git-svn-id: http://svn.code.sf.net/p/retroshare/code/branches/v0.5-gxs-b1@5380 b45a01b8-16f6-495d-af2f-9b41ad6348cc
This commit is contained in:
drbob 2012-08-05 17:08:29 +00:00
parent f710dba2c6
commit b8729de06a
4 changed files with 653 additions and 0 deletions

View File

@ -2,6 +2,7 @@ TEMPLATE = app
TARGET = retroshare-nogui TARGET = retroshare-nogui
CONFIG += bitdht CONFIG += bitdht
#CONFIG += introserver #CONFIG += introserver
#CONFIG += sshserver
################################# Linux ########################################## ################################# Linux ##########################################
linux-* { linux-* {
@ -111,6 +112,26 @@ introserver {
DEFINES *= RS_INTRO_SERVER DEFINES *= RS_INTRO_SERVER
} }
sshserver {
# This Requires libssh-0.5.* to compile.
# Modify path below to point at it.
# Probably will only work on Linux for the moment.
#
# Use the following commend to generate a Server RSA Key.
# Key should be in current directory - when run/
# ssh-keygen -t rsa -f rs_ssh_host_rsa_key
#
# You can connect from a standard ssh, eg: ssh -p 7022 127.0.0.1
#
INCLUDEPATH += ../../../lib/libssh-0.5.2/include/
LIBS += ../../../lib/libssh-0.5.2/build/src/libssh.a
LIBS += ../../../lib/libssh-0.5.2/build/src/threads/libssh_threads.a
HEADERS += ssh/rssshd.h
SOURCES += ssh/rssshd.cc
DEFINES *= RS_SSH_SERVER
}

View File

@ -40,6 +40,10 @@
#include "introserver.h" #include "introserver.h"
#endif #endif
#ifdef RS_SSH_SERVER
#include "ssh/rssshd.h"
#endif
/* Basic instructions for running libretroshare as background thread. /* Basic instructions for running libretroshare as background thread.
* ******************************************************************* * * ******************************************************************* *
* This allows your program to communicate with authenticated peers. * This allows your program to communicate with authenticated peers.
@ -132,6 +136,13 @@ int main(int argc, char **argv)
return 1; return 1;
} }
#ifdef RS_SSH_SERVER
// Says it must be called before all the threads are launched! */
// NB: this port number is not currently used.
RsSshd *ssh = RsSshd::InitRsSshd(22, "rs_ssh_host_rsa_key");
ssh->adduser("anrsuser", "test");
#endif
/* Start-up libretroshare server threads */ /* Start-up libretroshare server threads */
rsServer -> StartupRetroShare(); rsServer -> StartupRetroShare();
@ -139,6 +150,10 @@ int main(int argc, char **argv)
RsIntroServer rsIS; RsIntroServer rsIS;
#endif #endif
#ifdef RS_SSH_SERVER
ssh->start();
#endif
/* pass control to the GUI */ /* pass control to the GUI */
while(1) while(1)
{ {

View File

@ -0,0 +1,491 @@
/* This is a sample implementation of a libssh based SSH server */
/*
Copyright 2003-2009 Aris Adamantiadis
This file is part of the SSH Library
You are free to copy this file, modify it in any way, consider it being public
domain. This does not apply to the rest of the library though, but it is
allowed to cut-and-paste working code from this file to any license of
program.
The goal is to show the API in action. It's not a reference on how terminal
clients must be made or how a client should react.
*/
/*****
* Heavily Modified by Robert Fernie 2012... for retroshare project!
*
*/
#include <libssh/callbacks.h>
#include "ssh/rssshd.h"
#include <iostream>
#define RSSSHD_STATE_NULL 0
#define RSSSHD_STATE_INIT_OK 1
RsSshd *rsSshd = NULL; // External Reference Variable.
// NB: This must be called EARLY before all the threads are launched.
RsSshd *RsSshd::InitRsSshd(uint16_t port, std::string rsakeyfile)
{
ssh_threads_set_callbacks(ssh_threads_get_pthread());
ssh_init();
rsSshd = new RsSshd(port);
if (rsSshd->init(rsakeyfile))
{
return rsSshd;
}
rsSshd = NULL;
return rsSshd;
}
RsSshd::RsSshd(uint16_t port)
:mSshMtx("sshMtx"), mPort(port), mChannel(0)
{
mState = RSSSHD_STATE_NULL;
mBindState = 0;
return;
}
int RsSshd::init(std::string pathrsakey)
{
mBind=ssh_bind_new();
mSession=ssh_new();
//ssh_bind_options_set(mBind, SSH_BIND_OPTIONS_DSAKEY, KEYS_FOLDER "ssh_host_dsa_key");
//ssh_bind_options_set(mBind, SSH_BIND_OPTIONS_RSAKEY, KEYS_FOLDER "ssh_host_rsa_key");
//ssh_bind_options_set(mBind, SSH_BIND_OPTIONS_BINDPORT_STR, arg);
ssh_bind_options_set(mBind, SSH_BIND_OPTIONS_BINDPORT_STR, "7022");
//ssh_bind_options_set(mBind, SSH_BIND_OPTIONS_DSAKEY, arg);
//ssh_bind_options_set(mBind, SSH_BIND_OPTIONS_HOSTKEY, arg);
ssh_bind_options_set(mBind, SSH_BIND_OPTIONS_RSAKEY, pathrsakey.c_str());
ssh_bind_options_set(mBind, SSH_BIND_OPTIONS_LOG_VERBOSITY_STR, "3");
mState = RSSSHD_STATE_INIT_OK;
mBindState = 0;
return 1;
}
void RsSshd::run()
{
/* main loop */
/* listen */
bool sshOk = true;
while(sshOk)
{
std::cerr << "RsSshd::run() starting sshd cycle";
std::cerr << std::endl;
if (listenConnect())
{
std::cerr << "RsSshd::run() success connect => setup mSession";
std::cerr << std::endl;
if (setupSession())
{
std::cerr << "RsSshd::run() setup mSession => interactive";
std::cerr << std::endl;
interactive();
}
else
{
std::cerr << "RsSshd::run() setup mSession failed";
std::cerr << std::endl;
}
}
cleanupSession();
sleep(5); // have a break;
}
}
int RsSshd::listenConnect()
{
std::cerr << "RsSshd::listenConnect()";
std::cerr << std::endl;
if (!mBindState)
{
if(ssh_bind_listen(mBind)<0)
{
printf("Error listening to socket: %s\n",ssh_get_error(mBind));
return 0;
}
mBindState = 1;
}
else
{
std::cerr << "RsSshd::listenConnect() Already Bound...";
std::cerr << std::endl;
}
int r=ssh_bind_accept(mBind,mSession);
if(r==SSH_ERROR)
{
printf("error accepting a connection : %s\n",ssh_get_error(mBind));
return 0;
}
if (ssh_handle_key_exchange(mSession))
{
printf("ssh_handle_key_exchange: %s\n", ssh_get_error(mSession));
return 0;
}
return 1;
}
int RsSshd::setupSession()
{
std::cerr << "RsSshd::listenConnect()";
std::cerr << std::endl;
if (authUser())
{
std::cerr << "RsSshd::listenConnect() authUser SUCCESS";
std::cerr << std::endl;
if (setupChannel())
{
std::cerr << "RsSshd::listenConnect() setupChannel SUCCESS";
std::cerr << std::endl;
if (setupShell())
{
std::cerr << "RsSshd::listenConnect() setupShell SUCCESS";
std::cerr << std::endl;
return 1;
}
}
}
std::cerr << "RsSshd::listenConnect() Failed";
std::cerr << std::endl;
return 0;
}
int RsSshd::interactive()
{
std::cerr << "RsSshd::interactive()";
std::cerr << std::endl;
doEcho();
return 1;
}
int RsSshd::authUser()
{
std::cerr << "RsSshd::authUser()";
std::cerr << std::endl;
ssh_message message;
int auth = 0;
do {
message=ssh_message_get(mSession);
if(!message)
break;
switch(ssh_message_type(message)){
case SSH_REQUEST_AUTH:
switch(ssh_message_subtype(message)){
case SSH_AUTH_METHOD_PASSWORD:
printf("User %s wants to auth with pass %s\n",
ssh_message_auth_user(message),
ssh_message_auth_password(message));
if(auth_password(ssh_message_auth_user(message),
ssh_message_auth_password(message))){
auth=1;
ssh_message_auth_reply_success(message,0);
break;
}
// not authenticated, send default message
case SSH_AUTH_METHOD_NONE:
default:
ssh_message_auth_set_methods(message,SSH_AUTH_METHOD_PASSWORD);
ssh_message_reply_default(message);
break;
}
break;
default:
ssh_message_reply_default(message);
}
ssh_message_free(message);
} while (!auth);
if(!auth){
printf("auth error: %s\n",ssh_get_error(mSession));
ssh_disconnect(mSession);
return 0;
}
std::cerr << "RsSshd::authuser() Success";
std::cerr << std::endl;
return 1;
}
int RsSshd::setupChannel()
{
std::cerr << "RsSshd::setupChannel()";
std::cerr << std::endl;
ssh_message message;
do {
message=ssh_message_get(mSession);
if(message){
switch(ssh_message_type(message)){
case SSH_REQUEST_CHANNEL_OPEN:
if(ssh_message_subtype(message)==SSH_CHANNEL_SESSION){
mChannel=ssh_message_channel_request_open_reply_accept(message);
break;
}
default:
ssh_message_reply_default(message);
}
ssh_message_free(message);
}
} while(message && !mChannel);
if(!mChannel){
printf("error : %s\n",ssh_get_error(mSession));
ssh_finalize();
return 0;
}
return 1;
}
int RsSshd::setupShell()
{
std::cerr << "RsSshd::setupShell()";
std::cerr << std::endl;
int shell = 0;
ssh_message message;
do {
message=ssh_message_get(mSession);
if(message && ssh_message_type(message)==SSH_REQUEST_CHANNEL &&
ssh_message_subtype(message)==SSH_CHANNEL_REQUEST_SHELL){
shell = 1;
ssh_message_channel_request_reply_success(message);
break;
}
if(!shell){
ssh_message_reply_default(message);
}
ssh_message_free(message);
} while (message && !shell);
if(!shell){
printf("error : %s\n",ssh_get_error(mSession));
return 0;
}
return 1;
}
int RsSshd::doEcho()
{
std::cerr << "RsSshd::doEcho()";
std::cerr << std::endl;
int i = 0;
char buf[2048];
do{
i=ssh_channel_read(mChannel,buf, 2048, 0);
if(i>0) {
ssh_channel_write(mChannel, buf, i);
ssh_channel_write(mChannel, buf, i);
if (write(1,buf,i) < 0) {
printf("error writing to buffer\n");
return 0;
}
}
} while (i>0);
std::cerr << "RsSshd::doEcho() Finished";
std::cerr << std::endl;
return 1;
}
int RsSshd::cleanupSession()
{
std::cerr << "RsSshd::cleanupSession()";
std::cerr << std::endl;
ssh_disconnect(mSession);
return 1;
}
int RsSshd::cleanupAll()
{
std::cerr << "RsSshd::cleanupAll()";
std::cerr << std::endl;
cleanupSession();
if (mBindState)
{
ssh_bind_free(mBind);
mBindState = 0;
}
ssh_finalize();
return 1;
}
/***********************************************************************************/
/* PASSWORDS */
/***********************************************************************************/
int RsSshd::auth_password(char *name, char *pwd)
{
#ifdef ALLOW_CLEARPWDS
if (auth_password_basic(name, pwd))
return 1;
#endif // ALLOW_CLEARPWDS
if (auth_password_hashed(name, pwd))
return 1;
return 0;
}
#define RSSSHD_MIN_PASSWORD 4
#define RSSSHD_MIN_USERNAME 3
#ifdef ALLOW_CLEARPWDS
int RsSshd::adduser(std::string username, std::string password)
{
if (password.length() < RSSSHD_MIN_PASSWORD)
{
std::cerr << "RsSshd::adduser() Password Too Short";
return 0;
}
if (username.length() < RSSSHD_MIN_USERNAME)
{
std::cerr << "RsSshd::adduser() Password Too Short";
return 0;
}
std::map<std::string, std::string>::iterator it;
it = mPasswords.find(username);
if (it != mPasswords.end())
{
std::cerr << "RsSshd::adduser() Warning username already exists";
}
mPasswords[username] = password;
return 1;
}
int RsSshd::auth_password_basic(char *name, char *pwd)
{
std::string username(name);
std::string password(pwd);
std::map<std::string, std::string>::iterator it;
it = mPasswords.find(username);
if (it == mPasswords.end())
{
std::cerr << "RsSshd::auth_password() Unknown username";
return 0;
}
if (it->second == password)
{
std::cerr << "RsSshd::auth_password() logged in " << username;
return 1;
}
std::cerr << "RsSshd::auth_password() Invalid pwd for " << username;
return 0;
}
#endif // ALLOW_CLEARPWDS
#define RSSSHD_HASH_PWD_LENGTH 40
int RsSshd::adduserpwdhash(std::string username, std::string hash)
{
if (hash.length() != RSSSHD_HASH_PWD_LENGTH)
{
std::cerr << "RsSshd::adduserpwdhash() Hash Wrong Length";
return 0;
}
if (username.length() < RSSSHD_MIN_USERNAME)
{
std::cerr << "RsSshd::adduserpwdhash() Username Too Short";
return 0;
}
std::map<std::string, std::string>::iterator it;
it = mPwdHashs.find(username);
if (it != mPwdHashs.end())
{
std::cerr << "RsSshd::adduser() Warning username already exists";
}
mPwdHashs[username] = hash;
return 1;
}
int RsSshd::auth_password_hashed(char *name, char *pwd)
{
std::cerr << "RsSshd::auth_password_hashed() Not Finished Yet!";
return 0;
std::string username(name);
std::string password(pwd);
std::map<std::string, std::string>::iterator it;
it = mPasswords.find(username);
if (it == mPasswords.end())
{
std::cerr << "RsSshd::auth_password_hashed() Unknown username";
return 0;
}
if (it->second == password)
{
std::cerr << "RsSshd::auth_password_hashed() logged in " << username;
return 1;
}
std::cerr << "RsSshd::auth_password_hashed() Invalid pwd for " << username;
return 0;
}

View File

@ -0,0 +1,126 @@
/* This is a sample implementation of a libssh based SSH server */
/*
Copyright 2003-2009 Aris Adamantiadis
This file is part of the SSH Library
You are free to copy this file, modify it in any way, consider it being public
domain. This does not apply to the rest of the library though, but it is
allowed to cut-and-paste working code from this file to any license of
program.
The goal is to show the API in action. It's not a reference on how terminal
clients must be made or how a client should react.
*/
/*****
* Heavily Modified by Robert Fernie 2012... for retroshare project!
*
*/
#ifndef RS_SSHD_INTERFACE_H
#define RS_SSHD_INTERFACE_H
#include <libssh/libssh.h>
#include <libssh/server.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
// From inside libretroshare.a
#include "util/rsthreads.h"
#include <string>
#include <map>
#ifndef KEYS_FOLDER
#ifdef _WIN32
#define KEYS_FOLDER
#else
#define KEYS_FOLDER "/etc/ssh/"
#endif
#endif
/******
*
* Minimal Options to start with
*
*/
#define ALLOW_CLEARPWDS 1
class RsSshd;
extern RsSshd *rsSshd;
class RsSshd: public RsThread
{
public:
// TODO: NB: THIS FN DOES NOT USE A "SLOW" HASH FUNCTION.
// THE FIRST HALF OF THE HASH STRING IS THE SALT
int adduserpwdhash(std::string username, std::string hash);
#ifdef ALLOW_CLEARPWDS
int adduser(std::string username, std::string password);
#endif // ALLOW_CLEARPWDS
virtual void run(); /* overloaded from RsThread => called once the thread is started */
// NB: This must be called EARLY before all the threads are launched.
static RsSshd *InitRsSshd(uint16_t port, std::string rsakeyfile);
private:
RsSshd(uint16_t port); /* private constructor => so can only create with */
int init(std::string pathrsakey);
// High level operations.
int listenConnect();
int setupSession();
int interactive();
// Lower Level Operations.
int authUser();
int setupChannel();
int setupShell();
int doEcho();
int cleanupSession();
int cleanupAll();
/* Password Checking */
int auth_password(char *name, char *pwd);
int auth_password_hashed(char *name, char *pwd);
#ifdef ALLOW_CLEARPWDS
int auth_password_basic(char *name, char *pwd);
#endif // ALLOW_CLEARPWDS
// DATA.
RsMutex mSshMtx;
uint32_t mState;
uint32_t mBindState;
uint16_t mPort;
ssh_session mSession;
ssh_bind mBind;
ssh_channel mChannel;
#ifdef ALLOW_CLEARPWDS
std::map<std::string, std::string> mPasswords;
#endif // ALLOW_CLEARPWDS
std::map<std::string, std::string> mPwdHashs;
};
#endif