From afd34f2893a06a3aecf17e8e83b1df6ed2ae91a2 Mon Sep 17 00:00:00 2001 From: iroul Date: Fri, 4 Apr 2014 07:35:14 -0700 Subject: move to my github. --- src/protocol/CMakeLists.txt | 65 +++ src/protocol/fqterm_socket.cpp | 672 +++++++++++++++++++++++++++ src/protocol/fqterm_socket.h | 196 ++++++++ src/protocol/fqterm_ssh_socket.cpp | 352 ++++++++++++++ src/protocol/fqterm_ssh_socket.h | 131 ++++++ src/protocol/internal/crc32.cpp | 84 ++++ src/protocol/internal/crc32.h | 37 ++ src/protocol/internal/fqterm_serialization.h | 54 +++ src/protocol/internal/fqterm_ssh1_packet.cpp | 148 ++++++ src/protocol/internal/fqterm_ssh1_packet.h | 40 ++ src/protocol/internal/fqterm_ssh2_kex.cpp | 394 ++++++++++++++++ src/protocol/internal/fqterm_ssh2_kex.h | 105 +++++ src/protocol/internal/fqterm_ssh2_packet.cpp | 267 +++++++++++ src/protocol/internal/fqterm_ssh2_packet.h | 51 ++ src/protocol/internal/fqterm_ssh_auth.cpp | 223 +++++++++ src/protocol/internal/fqterm_ssh_auth.h | 112 +++++ src/protocol/internal/fqterm_ssh_buffer.cpp | 302 ++++++++++++ src/protocol/internal/fqterm_ssh_buffer.h | 83 ++++ src/protocol/internal/fqterm_ssh_channel.cpp | 439 +++++++++++++++++ src/protocol/internal/fqterm_ssh_channel.h | 130 ++++++ src/protocol/internal/fqterm_ssh_cipher.h | 58 +++ src/protocol/internal/fqterm_ssh_const.h | 146 ++++++ src/protocol/internal/fqterm_ssh_des.cpp | 180 +++++++ src/protocol/internal/fqterm_ssh_des.h | 74 +++ src/protocol/internal/fqterm_ssh_hash.h | 47 ++ src/protocol/internal/fqterm_ssh_kex.cpp | 210 +++++++++ src/protocol/internal/fqterm_ssh_kex.h | 100 ++++ src/protocol/internal/fqterm_ssh_mac.cpp | 53 +++ src/protocol/internal/fqterm_ssh_mac.h | 54 +++ src/protocol/internal/fqterm_ssh_md5.cpp | 36 ++ src/protocol/internal/fqterm_ssh_md5.h | 50 ++ src/protocol/internal/fqterm_ssh_packet.cpp | 242 ++++++++++ src/protocol/internal/fqterm_ssh_packet.h | 148 ++++++ src/protocol/internal/fqterm_ssh_pubkey.h | 37 ++ src/protocol/internal/fqterm_ssh_rsa.cpp | 67 +++ src/protocol/internal/fqterm_ssh_rsa.h | 41 ++ src/protocol/internal/fqterm_ssh_types.h | 43 ++ 37 files changed, 5471 insertions(+) create mode 100644 src/protocol/CMakeLists.txt create mode 100644 src/protocol/fqterm_socket.cpp create mode 100644 src/protocol/fqterm_socket.h create mode 100644 src/protocol/fqterm_ssh_socket.cpp create mode 100644 src/protocol/fqterm_ssh_socket.h create mode 100644 src/protocol/internal/crc32.cpp create mode 100644 src/protocol/internal/crc32.h create mode 100644 src/protocol/internal/fqterm_serialization.h create mode 100644 src/protocol/internal/fqterm_ssh1_packet.cpp create mode 100644 src/protocol/internal/fqterm_ssh1_packet.h create mode 100644 src/protocol/internal/fqterm_ssh2_kex.cpp create mode 100644 src/protocol/internal/fqterm_ssh2_kex.h create mode 100644 src/protocol/internal/fqterm_ssh2_packet.cpp create mode 100644 src/protocol/internal/fqterm_ssh2_packet.h create mode 100644 src/protocol/internal/fqterm_ssh_auth.cpp create mode 100644 src/protocol/internal/fqterm_ssh_auth.h create mode 100644 src/protocol/internal/fqterm_ssh_buffer.cpp create mode 100644 src/protocol/internal/fqterm_ssh_buffer.h create mode 100644 src/protocol/internal/fqterm_ssh_channel.cpp create mode 100644 src/protocol/internal/fqterm_ssh_channel.h create mode 100644 src/protocol/internal/fqterm_ssh_cipher.h create mode 100644 src/protocol/internal/fqterm_ssh_const.h create mode 100644 src/protocol/internal/fqterm_ssh_des.cpp create mode 100644 src/protocol/internal/fqterm_ssh_des.h create mode 100644 src/protocol/internal/fqterm_ssh_hash.h create mode 100644 src/protocol/internal/fqterm_ssh_kex.cpp create mode 100644 src/protocol/internal/fqterm_ssh_kex.h create mode 100644 src/protocol/internal/fqterm_ssh_mac.cpp create mode 100644 src/protocol/internal/fqterm_ssh_mac.h create mode 100644 src/protocol/internal/fqterm_ssh_md5.cpp create mode 100644 src/protocol/internal/fqterm_ssh_md5.h create mode 100644 src/protocol/internal/fqterm_ssh_packet.cpp create mode 100644 src/protocol/internal/fqterm_ssh_packet.h create mode 100644 src/protocol/internal/fqterm_ssh_pubkey.h create mode 100644 src/protocol/internal/fqterm_ssh_rsa.cpp create mode 100644 src/protocol/internal/fqterm_ssh_rsa.h create mode 100644 src/protocol/internal/fqterm_ssh_types.h (limited to 'src/protocol') diff --git a/src/protocol/CMakeLists.txt b/src/protocol/CMakeLists.txt new file mode 100644 index 0000000..c515590 --- /dev/null +++ b/src/protocol/CMakeLists.txt @@ -0,0 +1,65 @@ +set(export_SRCS + fqterm_socket.cpp + fqterm_socket.h + fqterm_ssh_socket.h + fqterm_ssh_socket.cpp +) + +set(internal_SRCS + internal/crc32.h + internal/fqterm_serialization.h + internal/fqterm_ssh_auth.h + internal/fqterm_ssh_buffer.h + internal/fqterm_ssh_cipher.h + internal/fqterm_ssh_const.h + internal/fqterm_ssh_des.h + internal/fqterm_ssh_hash.h + internal/fqterm_ssh_kex.h + internal/fqterm_ssh2_kex.h + internal/fqterm_ssh_mac.h + internal/fqterm_ssh_md5.h + internal/fqterm_ssh_packet.h + internal/fqterm_ssh1_packet.h + internal/fqterm_ssh2_packet.h + internal/fqterm_ssh_rsa.h + internal/fqterm_ssh_channel.h + internal/fqterm_ssh_types.h + internal/fqterm_ssh_pubkey.h + internal/crc32.cpp + internal/fqterm_ssh_auth.cpp + internal/fqterm_ssh_buffer.cpp + internal/fqterm_ssh_des.cpp + internal/fqterm_ssh_kex.cpp + internal/fqterm_ssh2_kex.cpp + internal/fqterm_ssh_mac.cpp + internal/fqterm_ssh_md5.cpp + internal/fqterm_ssh_packet.cpp + internal/fqterm_ssh1_packet.cpp + internal/fqterm_ssh2_packet.cpp + internal/fqterm_ssh_rsa.cpp + internal/fqterm_ssh_channel.cpp +) + +qt4_automoc( + ${export_SRCS} + ${internal_SRCS} +) + +include_directories( + ${QT_INCLUDE_DIR} + ${QT_QTCORE_INCLUDE_DIR} + ${QT_QTNETWORK_INCLUDE_DIR} + ${OPENSSL_INCLUDE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/internal + ${CMAKE_CURRENT_SOURCE_DIR}/../common + ${CMAKE_CURRENT_BINARY_DIR} +) + +add_library(fqterm_protocol + ${export_SRCS} + ${internal_SRCS} +) + +add_dependencies(fqterm_protocol + fqterm_common +) diff --git a/src/protocol/fqterm_socket.cpp b/src/protocol/fqterm_socket.cpp new file mode 100644 index 0000000..131044a --- /dev/null +++ b/src/protocol/fqterm_socket.cpp @@ -0,0 +1,672 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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 +#include + +#include +#include +#include + +#include "fqterm.h" +#include "fqterm_socket.h" +#include "fqterm_trace.h" + +#if !defined(Q_OS_BSD4) && !defined(_OS_FREEBSD_) \ + && !defined(Q_OS_MACX) && !defined(Q_OS_DARWIN) +#include +#endif + +namespace FQTerm { + +/* hack from wget/http.c */ +#define BASE64_LENGTH(len) (4 * (((len) + 2) / 3)) +static void base64_encode(const char *s, char *store, int length); +static char *basic_authentication_encode(const char *user, const char *passwd, + const char *header); +static int parse_http_status_line(const char *line, const char + **reason_phrase_ptr); + +const char wingate_enter = 'J' &0x1f; + +//============================================================================== +//FQTermSocketPrivate + +//============================================================================== +FQTermSocketPrivate::FQTermSocketPrivate(QObject *parent) + : QObject(parent) { + m_socket = new QTcpSocket(this); + + // proxy related + proxy_type = NOPROXY; + proxy_state = 0; + bauth = false; + + FQ_VERIFY(connect(m_socket, SIGNAL(connected()), this, SLOT(socketConnected()))); + FQ_VERIFY(connect(m_socket, SIGNAL(hostFound()), this, SIGNAL(hostFound()))); + FQ_VERIFY(connect(m_socket, SIGNAL(disconnected()), this, SIGNAL(connectionClosed()))); + + FQ_VERIFY(connect(m_socket, SIGNAL(readyRead()), this, SLOT(socketReadyRead()))); + FQ_VERIFY(connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SIGNAL(error(QAbstractSocket::SocketError)))); +} + +FQTermSocketPrivate::~FQTermSocketPrivate() { + delete m_socket; +} + +void FQTermSocketPrivate::setProxy(int nProxyType, bool bAuth, const QString &strProxyHost, + quint16 uProxyPort, const QString &strProxyUsr, + const QString &strProxyPwd) { + proxy_type = nProxyType; + if (proxy_type == NOPROXY) { + return ; + } + bauth = bAuth; + proxy_host = strProxyHost; + proxy_port = uProxyPort; + proxy_usr = strProxyUsr; + proxy_pwd = strProxyPwd; +} + +void FQTermSocketPrivate::socketConnected() { + QByteArray strPort; + QByteArray command(9, 0); + + char *proxyauth; + char *request; + int len = 0; + + switch (proxy_type) { + case NOPROXY: + // no proxy + emit connected(); + return ; + case WINGATE: + // Wingate Proxy + strPort.setNum(port); + writeBlock(host.toLocal8Bit()); + writeBlock(" "); + writeBlock(strPort); + writeBlock(&wingate_enter); // CTRL+J + emit socketState(TSHOSTCONNECTED); + return ; + case SOCKS4: + // Socks4 Proxy + command.resize(9); + command[0] = '\x04'; + command[1] = '\x01'; + memcpy(command.data() + 2, &addr_host.sin_port, 2); + memcpy(command.data() + 4, &addr_host.sin_addr, 4); + writeBlock(command); + proxy_state = 1; + emit socketState(TSPROXYCONNECTED); + return ; + case SOCKS5: + // Socks5 Proxy + if (bauth) { + command.resize(4); + command[0] = '\x05'; + command[1] = '\x02'; + command[2] = '\x02'; + command[3] = '\x00'; + writeBlock(command); + } + else { + command.resize(3); + command[0] = '\x05'; + command[1] = '\x01'; + command[2] = '\x00'; + writeBlock(command); + } + proxy_state = 1; + emit socketState(TSPROXYCONNECTED); + return ; + case HTTP: + proxyauth = NULL; + if (bauth) { + proxyauth = basic_authentication_encode(proxy_usr.toLocal8Bit(), + proxy_pwd.toLocal8Bit(), "Proxy-Authorization"); + } + + len = proxyauth != NULL ? strlen(proxyauth): 0; + + request = new char[host.length() + len + 81]; + + sprintf(request, "CONNECT %s:%u HTTP/1.0\r\n""%s\r\n", host.toLatin1().constData(), port, + proxyauth != NULL ? proxyauth : ""); + + + writeBlock(request); + delete [] request; + free(proxyauth); + proxy_state = 1; + emit socketState(TSPROXYCONNECTED); + return ; + default: + emit socketState(TSHOSTCONNECTED); + return ; + } +} + +void FQTermSocketPrivate::socketReadyRead() { + if (proxy_type == NOPROXY || proxy_state == 0) { + emit readyRead(); + } + + int nbytes, nread; + QByteArray from_socket; + // get the data size + nbytes = bytesAvailable(); + if (nbytes <= 0) { + return ; + } + + //resize input buffer + from_socket.resize(0); + + //read data from socket to from_socket + from_socket = readBlock(nbytes); + nread = from_socket.size(); + //do some checks + if (nread <= 0) { + FQ_TRACE("socket", 0) << "Failed to read socket: " << nread << " <= 0"; + return ; + } + if (nread > nbytes) { + FQ_TRACE("socket", 0) << "Overflow when reading socket: " + << " nread = " << nread + << " > nbytes = " << nbytes; + return ; + } + + // Socks4 Proxy Reply + if (proxy_type == SOCKS4 && proxy_state == 1) { + if (nread != 8) { + emit socketState(TSPROXYERROR); + return ; + } + + if (from_socket.at(1) == 90) { + emit connected(); + } else { + emit socketState(TSPROXYFAIL); + } + proxy_state = 0; + return ; + } + + // HTTP Proxy Reply + if (proxy_type == HTTP && proxy_state != 0) { + const char *error; + int statcode = parse_http_status_line(from_socket.data(), &error); + if (statcode >= 200 && statcode < 300) { + emit connected(); + } else { + emit socketState(TSPROXYFAIL); + } + proxy_state = 0; + return ; + } + + // Socks5 Proxy Reply + if (proxy_type == SOCKS5 && proxy_state != 0) { + socks5_reply(from_socket, nread); + return ; + } +} + +void FQTermSocketPrivate::flush() { + m_socket->flush(); +} + +///////////////////////////////////////////////////// +//originally version from qhostaddress.cpp, qt4.3.2, --dp +static bool isIp4(const QString& address) +{ + QStringList ipv4 = address.split(QLatin1String(".")); + if (ipv4.count() != 4) + return false; + + for (int i = 0; i < 4; ++i) { + bool ok = false; + uint byteValue = ipv4.at(i).toUInt(&ok); + if (!ok || byteValue > 255) + return false; + + } + + return true; +} +///////////////////////////////////////////////////////// + +void FQTermSocketPrivate::connectToHost(const QString &hostname, quint16 portnumber) { + host = hostname; + port = portnumber; + addr_host.sin_port = htons(portnumber); + + if (proxy_type == NOPROXY) { + m_socket->connectToHost(host, port); + } else { + //get ip addr from name + struct hostent *hostent; +#if defined(_OS_WIN32_) || defined(Q_OS_WIN32) + WSADATA wsd; + if (WSAStartup(0x202, &wsd) != 0) { + emit socketState(TSEINIWINSOCK); + WSACleanup(); + return ; + } +#endif + // QHostAddress ha(host); + //ipv4 address check -- dp + if(isIp4(host)) { + hostent = gethostbyaddr( host.toLocal8Bit(), host.length(), AF_INET ); + FQ_TRACE("ssh", 1) << "by addr"; + } else { + hostent = gethostbyname(host.toLocal8Bit()); + FQ_TRACE("ssh", 1) << "by name"; + } + + if (hostent == NULL) { + emit socketState(TSEGETHOSTBYNAME); + return ; + } + //now only ipv4 support + memcpy(&addr_host.sin_addr, hostent->h_addr, 4); + m_socket->connectToHost(proxy_host, proxy_port); + } + +} + +void FQTermSocketPrivate::close() { + m_socket->close(); +} + +QByteArray FQTermSocketPrivate::readBlock(unsigned long maxlen) { + return m_socket->read(maxlen); +} + +long FQTermSocketPrivate::writeBlock(const QByteArray &data) { + return m_socket->write(data); +} + +unsigned long FQTermSocketPrivate::bytesAvailable() { + return m_socket->bytesAvailable(); +} + +/*------------------------------------------------------------------------ + * connect command for socks5 + *------------------------------------------------------------------------ + */ +void FQTermSocketPrivate::socks5_connect() { + QByteArray command(10, 0); + command[0] = '\x05'; + command[1] = '\x01'; + command[2] = '\x00'; + command[3] = '\x01'; + memcpy(command.data() + 4, &addr_host.sin_addr, 4); + memcpy(command.data() + 8, &addr_host.sin_port, 2); + writeBlock(command); +} + +/*------------------------------------------------------------------------ + * authentation command for socks5 + *------------------------------------------------------------------------ + */ +void FQTermSocketPrivate::socks5_auth() { + int ulen = proxy_usr.length(); + int plen = proxy_pwd.length(); + QByteArray command(3+ulen + plen, 0); + sprintf((char*)command.data(), " %s %s", proxy_usr.toLocal8Bit().data(), + proxy_pwd.toLocal8Bit().data()); + command[0] = '\x01'; + command[1] = ulen; + command[2+ulen] = plen; + writeBlock(command); +} + +/*------------------------------------------------------------------------ + * reply from socks5 + *------------------------------------------------------------------------ + */ +void FQTermSocketPrivate::socks5_reply(const QByteArray &from_socket, int nread) { + if (proxy_state == 1) { // Socks5 Proxy Replay 1 + if (nread != 2) { + proxy_state = 0; + emit socketState(TSPROXYERROR); + return ; + } + switch (from_socket.at(1)) { + case '\x00': + // no authentation needed + socks5_connect(); + proxy_state = 3; + emit socketState(TSCONNECTVIAPROXY); + return ; + case '\x02': + //need user/password + socks5_auth(); + proxy_state = 2; + emit socketState(TSPROXYAUTH); + return ; + case '\xff': + proxy_state = 0; + emit socketState(TSPROXYFAIL); + return ; + default: + proxy_state = 0; + emit socketState(TSPROXYFAIL); + return ; + } + } else if (proxy_state == 2) { //Socks5 Proxy Replay 2 + if (nread != 2) { + proxy_state = 0; + emit socketState(TSPROXYERROR); + return ; + } + + + if (from_socket.at(1) != '\00') { + proxy_state = 0; + emit socketState(TSPROXYFAIL); + return ; + } + socks5_connect(); + proxy_state = 3; + emit socketState(TSCONNECTVIAPROXY); + return ; + } else if (proxy_state == 3) { //Socks5 Proxy Replay 3 + proxy_state = 0; + if (nread != 10) { + emit socketState(TSPROXYERROR); + return ; + } + if (from_socket.at(1) != '\00') { + emit socketState(TSPROXYFAIL); + return ; + } + emit connected(); + return ; + } else { + proxy_state = 0; + return ; + } +} + +/* hack from wget/http.c */ +/* How many bytes it will take to store LEN bytes in base64. */ +#define BASE64_LENGTH(len) (4 * (((len) + 2) / 3)) + +/* Encode the string S of length LENGTH to base64 format and place it + to STORE. STORE will be 0-terminated, and must point to a writable + buffer of at least 1+BASE64_LENGTH(length) bytes. */ +static void base64_encode(const char *s, char *store, int length) { + /* Conversion table. */ + static char tbl[64] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', + 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '+', '/' + }; + int i; + unsigned char *p = (unsigned char*)store; + + /* Transform the 3x8 bits to 4x6 bits, as required by base64. */ + for (i = 0; i < length; i += 3) { + *p++ = tbl[s[0] >> 2]; + *p++ = tbl[((s[0] &3) << 4) + (s[1] >> 4)]; + *p++ = tbl[((s[1] &0xf) << 2) + (s[2] >> 6)]; + *p++ = tbl[s[2] &0x3f]; + s += 3; + } + /* Pad the result if necessary... */ + if (i == length + 1) { + *(p - 1) = '='; + } else if (i == length + 2) { + *(p - 1) = *(p - 2) = '='; + } + /* ...and zero-terminate it. */ + *p = '\0'; +} + +/* Create the authentication header contents for the `Basic' scheme. + This is done by encoding the string `USER:PASS' in base64 and + prepending `HEADER: Basic ' to it. */ +static char *basic_authentication_encode(const char *user, const char *passwd, + const char *header) { + char *t1, *t2, *res; + int len1 = strlen(user) + 1+strlen(passwd); + int len2 = BASE64_LENGTH(len1); + + t1 = (char*)alloca(len1 + 1); + sprintf(t1, "%s:%s", user, passwd); + t2 = (char*)alloca(1+len2); + base64_encode(t1, t2, len1); + res = (char*)malloc(len2 + 11+strlen(header)); + sprintf(res, "%s: Basic %s\r\n", header, t2); + + return res; +} + +/* Parse the HTTP status line, which is of format: + + HTTP-Version SP Status-Code SP Reason-Phrase + + The function returns the status-code, or -1 if the status line is + malformed. The pointer to reason-phrase is returned in RP. */ +static int parse_http_status_line(const char *line, const char + **reason_phrase_ptr) { + /* (the variables must not be named `major' and `minor', because + that breaks compilation with SunOS4 cc.) */ + int mjr, mnr, statcode; + const char *p; + + *reason_phrase_ptr = NULL; + + /* The standard format of HTTP-Version is: `HTTP/X.Y', where X is + major version, and Y is minor version. */ + if (strncmp(line, "HTTP/", 5) != 0) { + return -1; + } + line += 5; + + /* Calculate major HTTP version. */ + p = line; + for (mjr = 0; QChar(*line).isDigit(); line++) { + mjr = 10 * mjr + (*line - '0'); + } + if (*line != '.' || p == line) { + return -1; + } + ++line; + + /* Calculate minor HTTP version. */ + p = line; + for (mnr = 0; QChar(*line).isDigit(); line++) { + mnr = 10 * mnr + (*line - '0'); + } + if (*line != ' ' || p == line) { + return -1; + } + /* Wget will accept only 1.0 and higher HTTP-versions. The value of + minor version can be safely ignored. */ + if (mjr < 1) { + return -1; + } + ++line; + + /* Calculate status code. */ + if (!(QChar(*line).isDigit() && QChar(line[1]).isDigit() && QChar(line[2]) + .isDigit())) { + return -1; + } + statcode = 100 *(*line - '0') + 10 *(line[1] - '0') + (line[2] - '0'); + + /* Set up the reason phrase pointer. */ + line += 3; + /* RFC2068 requires SPC here, but we allow the string to finish + here, in case no reason-phrase is present. */ + if (*line != ' ') { + if (! *line) { + *reason_phrase_ptr = line; + } else { + return -1; + } + } else { + *reason_phrase_ptr = line + 1; + } + + return statcode; +} + + +//============================================================================== +//FQTermTelnetSocket +//============================================================================== + +FQTermTelnetSocket::FQTermTelnetSocket() + : FQTermSocket() { + private_socket_ = new FQTermSocketPrivate(); + FQ_VERIFY(connect(private_socket_, SIGNAL(connected()), this, SIGNAL(connected()))); + FQ_VERIFY(connect(private_socket_, SIGNAL(hostFound()), this, SIGNAL(hostFound()))); + FQ_VERIFY(connect(private_socket_, SIGNAL(connectionClosed()), this, SIGNAL(connectionClosed()))); + FQ_VERIFY(connect(private_socket_, SIGNAL(delayedCloseFinished()), + this, SIGNAL(delayedCloseFinished()))); + FQ_VERIFY(connect(private_socket_, SIGNAL(readyRead()), this, SIGNAL(readyRead()))); + FQ_VERIFY(connect(private_socket_, SIGNAL(error(QAbstractSocket::SocketError)), this, SIGNAL(error(QAbstractSocket::SocketError)))); + FQ_VERIFY(connect(private_socket_, SIGNAL(socketState(int)), this, SIGNAL(socketState(int)))); +} + +FQTermTelnetSocket::~FQTermTelnetSocket() { + delete private_socket_; +} + +void FQTermTelnetSocket::flush() { + private_socket_->flush(); +} + +void FQTermTelnetSocket::setProxy(int nProxyType, bool bAuth, + const QString &strProxyHost, + quint16 uProxyPort, + const QString &strProxyUsr, + const QString &strProxyPwd) { + private_socket_->setProxy(nProxyType, bAuth, strProxyHost, uProxyPort, strProxyUsr, + strProxyPwd); +} + +void FQTermTelnetSocket::connectToHost(const QString &host, quint16 port) { + private_socket_->connectToHost(host, port); +} + +void FQTermTelnetSocket::close() { + private_socket_->close(); +} + +QByteArray FQTermTelnetSocket::readBlock(unsigned long maxlen) { + return private_socket_->readBlock(maxlen); +} + +long FQTermTelnetSocket::writeBlock(const QByteArray &data) { + return private_socket_->writeBlock(data); +} + +unsigned long FQTermTelnetSocket::bytesAvailable() { + return private_socket_->bytesAvailable(); +} + + +FQTermLocalSocket::FQTermLocalSocket( const QString& shell_bin ) : shell_bin_(shell_bin) +{ + shell_process_ = new QProcess(); + shell_process_->setProcessChannelMode(QProcess::MergedChannels); + FQ_VERIFY(connect(shell_process_, SIGNAL(started()), this, SIGNAL(connected()))); + FQ_VERIFY(connect(shell_process_, SIGNAL(stateChanged(QProcess::ProcessState)), this , SLOT(stateChanged(QProcess::ProcessState)))); + FQ_VERIFY(connect(shell_process_, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(finished(int, QProcess::ExitStatus)))); + //FQ_VERIFY(connect(shell_process_, SIGNAL(delayedCloseFinished()), this, SIGNAL(delayedCloseFinished()))); + FQ_VERIFY(connect(shell_process_, SIGNAL(readyRead()), this, SIGNAL(readyRead()))); + //TODO: Error + //FQ_VERIFY(connect(shell_process_, SIGNAL(error(QAbstractSocket::SocketError)), this, SIGNAL(error(QAbstractSocket::SocketError)))); + //FQ_VERIFY(connect(shell_process_, SIGNAL(socketState(int)), this, SIGNAL(socketState(int)))); +} + +FQTermLocalSocket::~FQTermLocalSocket() +{ + delete shell_process_; +} + +void FQTermLocalSocket::connectToHost( const QString &host, quint16 port ) +{ + shell_process_->start("E:/yxl/test.exe"/*C:/Windows/System32/cmd.exeshell_bin*/, QIODevice::ReadWrite | QIODevice::Unbuffered); +} + +void FQTermLocalSocket::close() +{ + shell_process_->close(); +} + +QByteArray FQTermLocalSocket::readBlock( unsigned long maxlen ) +{ + return shell_process_->read(maxlen); +} + +long FQTermLocalSocket::writeBlock( const QByteArray &data ) +{ + int count = shell_process_->write(data); + + //char c; +// shell_process_->getChar(&c); +// shell_process_->ungetChar(c); + if (bytesAvailable()) + { + emit readyRead(); + } + return count; +} + +unsigned long FQTermLocalSocket::bytesAvailable() +{ + return shell_process_->bytesAvailable(); +} + +void FQTermLocalSocket::finished( int exitCode, QProcess::ExitStatus exitStatus ) +{ + emit connectionClosed(); +} +void FQTermLocalSocket::stateChanged(QProcess::ProcessState newState) +{ + switch(newState) + { + case QProcess::NotRunning: + break; + case QProcess::Starting: + emit hostFound(); + shell_process_->write("dir\n"); + break; + case QProcess::Running: + break; + + } +} + + +} // namespace FQTerm + +#include "fqterm_socket.moc" diff --git a/src/protocol/fqterm_socket.h b/src/protocol/fqterm_socket.h new file mode 100644 index 0000000..2d6240b --- /dev/null +++ b/src/protocol/fqterm_socket.h @@ -0,0 +1,196 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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. * + ***************************************************************************/ + +#ifndef FQTERM_SOCKET_H +#define FQTERM_SOCKET_H + +// _OS_X_ not defined if i dont include it +#include + +// different +#if defined(Q_OS_WIN32) || defined(_OS_WIN32_) + +#include + +#elif defined(Q_OS_BSD4) || defined(_OS_FREEBSD_) \ + || defined(Q_OS_MACX) || defined(Q_OS_DARWIN) + +#include +#include +#include +#include + +#else + +#include +#include +#include +#include + +#endif + +#include +#include +#include +#include + +class QTcpSocket; +namespace FQTerm { +/* + * Socket with proxy support. + * + */ +class FQTermSocketPrivate: public QObject { + + Q_OBJECT; + public: + FQTermSocketPrivate(QObject *parent_ = 0); + + ~FQTermSocketPrivate(); + + void flush(); + void setProxy(int nProxyType, bool bAuth, const QString &strProxyHost, + quint16 uProxyPort, const QString &strProxyUsr, + const QString &strProxyPwd); + void connectToHost(const QString &hostname, quint16 portnumber); + void close(); + QByteArray readBlock(unsigned long maxlen); + long writeBlock(const QByteArray &data); + unsigned long bytesAvailable(); + + signals: + void connected(); + void hostFound(); + void connectionClosed(); + void delayedCloseFinished(); + void readyRead(); + void error(QAbstractSocket::SocketError); + void socketState(int); + + protected slots: + void socketConnected(); + void socketReadyRead(); + + protected: + // socks5 function + void socks5_connect(); + void socks5_auth(); + void socks5_reply(const QByteArray &, int); + + private: + // proxy + int proxy_type; + QString proxy_host; + QString proxy_usr; + quint16 proxy_port; + QString proxy_pwd; + QString host; + quint16 port; + int proxy_state; + bool bauth; + + struct sockaddr_in addr_host; + + QTcpSocket *m_socket; +}; + +// Virtual base class for FQTermTelnetSocket and FQTermSSHSocket +class FQTermSocket: public QObject { + + Q_OBJECT; + public: + FQTermSocket(QObject *parent = 0): QObject(parent) {} + + virtual ~FQTermSocket() {} + + virtual void flush() = 0; + virtual void setProxy(int nProxyType, bool bAuth, + const QString &strProxyHost, + quint16 uProxyPort, + const QString &strProxyUsr, + const QString &strProxyPwd) = 0; + virtual void connectToHost(const QString &host, quint16 port) = 0; + virtual void close() = 0; + virtual QByteArray readBlock(unsigned long maxlen) = 0; + virtual long writeBlock(const QByteArray &data) = 0; + virtual unsigned long bytesAvailable() = 0; + virtual bool readyForInput() {return true;} + virtual bool setTermSize(int col, int row) {return 0;} + signals: + void sshAuthOK(); + void connected(); + void hostFound(); + void connectionClosed(); + void delayedCloseFinished(); + void readyRead(); + void error(QAbstractSocket::SocketError); + void errorMessage(QString); + void socketState(int); + void requestUserPwd(QString *user, QString *pwd, bool *isOK); +}; + +class FQTermTelnetSocket: public FQTermSocket { + private: + FQTermSocketPrivate *private_socket_; + + public: + FQTermTelnetSocket(); + + ~FQTermTelnetSocket(); + + void flush(); + void setProxy(int nProxyType, bool bAuth, const QString &strProxyHost, + quint16 uProxyPort, const QString &strProxyUsr, + const QString &strProxyPwd); + void connectToHost(const QString &host, quint16 port); + void close(); + QByteArray readBlock(unsigned long maxlen); + long writeBlock(const QByteArray &data); + unsigned long bytesAvailable(); +}; + +class FQTermLocalSocket: public FQTermSocket { + Q_OBJECT; +private: + QString shell_bin_; + QProcess* shell_process_; +public: + FQTermLocalSocket(const QString& shell_bin); + ~FQTermLocalSocket(); + void flush(){} + void setProxy(int nProxyType, bool bAuth, const QString &strProxyHost, + quint16 uProxyPort, const QString &strProxyUsr, + const QString &strProxyPwd){} + void connectToHost(const QString &host, quint16 port); + void close(); + QByteArray readBlock(unsigned long maxlen); + long writeBlock(const QByteArray &data); + unsigned long bytesAvailable(); + +public slots: + void stateChanged(QProcess::ProcessState newState); + void finished (int exitCode, QProcess::ExitStatus exitStatus); +}; + + + +} // namespace FQTerm + +#endif // FQTERM_SOCKET_H diff --git a/src/protocol/fqterm_ssh_socket.cpp b/src/protocol/fqterm_ssh_socket.cpp new file mode 100644 index 0000000..4c1e77a --- /dev/null +++ b/src/protocol/fqterm_ssh_socket.cpp @@ -0,0 +1,352 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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 "fqterm_ssh_socket.h" +#include "fqterm_ssh_packet.h" +#include "fqterm_ssh1_packet.h" +#include "fqterm_ssh2_packet.h" +#include "fqterm_ssh_buffer.h" +#include "fqterm_ssh_rsa.h" +#include "fqterm_ssh_kex.h" +#include "fqterm_ssh2_kex.h" +#include "fqterm_ssh_const.h" +#include "fqterm_ssh_auth.h" +#include "fqterm_ssh_channel.h" +#include "fqterm_trace.h" +#include +namespace FQTerm { + +#define V1STR "SSH-1.5-FQTermSSH\n" +#define V2STR "SSH-2.0-FQTermSSH\n" +#define SSH_V1_C "SSH-1.5-FQTermSSH" +#define SSH_V2_C "SSH-2.0-FQTermSSH" + +FQTermSSHSocket::FQTermSSHSocket(int col, int row, const QString& termtype, const char *sshuser, const char *sshpasswd) + : termtype_(termtype) { + col_ = col; + row_ = row; + init_user_ = sshuser; + init_passwd_ = sshpasswd; + + private_socket_ = new FQTermSocketPrivate(); + + input_buffer_ = NULL; + output_buffer_ = NULL; + socket_buffer_ = NULL; + packet_receiver_ = NULL; + packet_sender_ = NULL; + key_exchanger_ = NULL; + authentication_ = NULL; + ssh_channel_ = NULL; + ssh_version_ = 1; + is_channel_ok_ = false; + auth_ok_emitted_ = false; + + FQ_VERIFY(connect(private_socket_, SIGNAL(hostFound()), this, SIGNAL(hostFound()))); + FQ_VERIFY(connect(private_socket_, SIGNAL(connected()), this, SIGNAL(connected()))); + FQ_VERIFY(connect(private_socket_, SIGNAL(connectionClosed()), this, SIGNAL(connectionClosed()))); + FQ_VERIFY(connect(private_socket_, SIGNAL(delayedCloseFinished()), this, SIGNAL(delayedCloseFinished()))); + FQ_VERIFY(connect(private_socket_, SIGNAL(error(QAbstractSocket::SocketError )), this, SIGNAL(error(QAbstractSocket::SocketError )))); + FQ_VERIFY(connect(private_socket_, SIGNAL(socketState(int)), this, SIGNAL(socketState(int)))); + + FQ_VERIFY(connect(private_socket_, SIGNAL(readyRead()), this, SLOT(socketReadyRead()))); +} + +FQTermSSHSocket::~FQTermSSHSocket() { + delete private_socket_; + delete input_buffer_; + delete output_buffer_; + delete socket_buffer_; + delete packet_receiver_; + delete packet_sender_; + delete key_exchanger_; + delete authentication_; + delete ssh_channel_; +} + +void FQTermSSHSocket::init(int ssh_version) { + // Actually we could reuse these buffers, sender/receivers, and etc. + // but in that case reset methods should be added to all these classes. + // Guys lazy as me won't do that. + + delete input_buffer_; + delete output_buffer_; + delete socket_buffer_; + delete packet_receiver_; + delete packet_sender_; + delete key_exchanger_; + delete authentication_; + delete ssh_channel_; + + is_channel_ok_ = false; + auth_ok_emitted_ = false; + + if (ssh_version == 1) { + input_buffer_ = new FQTermSSHBuffer(1024); + output_buffer_ = new FQTermSSHBuffer(1024); + socket_buffer_ = new FQTermSSHBuffer(1024); + packet_receiver_ = new FQTermSSH1PacketReceiver; + packet_sender_ = new FQTermSSH1PacketSender; + key_exchanger_ = new FQTermSSH1Kex(SSH_V1_C, server_name_.toAscii().constData()); + authentication_ = new FQTermSSH1PasswdAuth(init_user_, init_passwd_); + ssh_channel_ = new FQTermSSH1Channel; + + FQ_VERIFY(connect(packet_receiver_, SIGNAL(packetAvaliable(int)), this, SLOT(handlePacket(int)))); + FQ_VERIFY(connect(packet_receiver_, SIGNAL(packetError(QString)), this, SLOT(handleError(QString)))); + + FQ_VERIFY(connect(packet_sender_, SIGNAL(dataToWrite()), this, SLOT(writeData()))); + + FQ_VERIFY(connect(key_exchanger_, SIGNAL(kexOK()), this, SLOT(kexOK()))); + FQ_VERIFY(connect(key_exchanger_, SIGNAL(kexError(QString)), this, SLOT(handleError(QString)))); + FQ_VERIFY(connect(key_exchanger_, SIGNAL(reKex()), packet_receiver_, SLOT(resetEncryption()))); + FQ_VERIFY(connect(key_exchanger_, SIGNAL(reKex()), packet_sender_, SLOT(resetEncryption()))); + FQ_VERIFY(connect(key_exchanger_, SIGNAL(startEncryption(const u_char*)), packet_receiver_, SLOT(startEncryption(const u_char*)))); + FQ_VERIFY(connect(key_exchanger_, SIGNAL(startEncryption(const u_char*)), packet_sender_, SLOT(startEncryption(const u_char*)))); + + FQ_VERIFY(connect(authentication_, SIGNAL(requestUserPwd(QString *, QString *, bool *)), this, SIGNAL(requestUserPwd(QString *, QString *, bool *)))); + FQ_VERIFY(connect(authentication_, SIGNAL(authOK()), this, SLOT(authOK()))); + FQ_VERIFY(connect(authentication_, SIGNAL(authError(QString)), this, SLOT(handleError(QString)))); + + FQ_VERIFY(connect(ssh_channel_, SIGNAL(channelOK()), this, SLOT(channelOK()))); + FQ_VERIFY(connect(ssh_channel_, SIGNAL(channelReadyRead(const char *, int)), this, SLOT(channelReadyRead(const char *, int)))); + + key_exchanger_->initKex(packet_receiver_, packet_sender_); + } else { + input_buffer_ = new FQTermSSHBuffer(1024); + output_buffer_ = new FQTermSSHBuffer(1024); + socket_buffer_ = new FQTermSSHBuffer(1024); + packet_receiver_ = new FQTermSSH2PacketReceiver; + packet_sender_ = new FQTermSSH2PacketSender; + key_exchanger_ = new FQTermSSH2Kex(SSH_V2_C, server_name_.toAscii().constData()); + authentication_ = new FQTermSSH2PasswdAuth(init_user_, init_passwd_); + ssh_channel_ = new FQTermSSH2Channel; + + FQ_VERIFY(connect(packet_receiver_, SIGNAL(packetAvaliable(int)), this, SLOT(handlePacket(int)))); + FQ_VERIFY(connect(packet_receiver_, SIGNAL(packetError(QString)), this, SLOT(handleError(QString)))); + + FQ_VERIFY(connect(packet_sender_, SIGNAL(dataToWrite()), this, SLOT(writeData()))); + + FQ_VERIFY(connect(key_exchanger_, SIGNAL(kexOK()), this, SLOT(kexOK()))); + FQ_VERIFY(connect(key_exchanger_, SIGNAL(kexError(QString)), this, SLOT(handleError(QString)))); + FQ_VERIFY(connect(key_exchanger_, SIGNAL(reKex()), packet_receiver_, SLOT(resetEncryption()))); + FQ_VERIFY(connect(key_exchanger_, SIGNAL(reKex()), packet_sender_, SLOT(resetEncryption()))); + + FQ_VERIFY(connect(key_exchanger_, SIGNAL(startEncryption(const u_char*)), packet_receiver_, SLOT(startEncryption(const u_char*)))); + FQ_VERIFY(connect(key_exchanger_, SIGNAL(startEncryption(const u_char*)), packet_sender_, SLOT(startEncryption(const u_char*)))); + + FQ_VERIFY(connect(authentication_, SIGNAL(requestUserPwd(QString *, QString *, bool *)), this, SIGNAL(requestUserPwd(QString *, QString *, bool *)))); + FQ_VERIFY(connect(authentication_, SIGNAL(authOK()), this, SLOT(authOK()))); + FQ_VERIFY(connect(authentication_, SIGNAL(authError(QString)), this, SLOT(handleError(QString)))); + + FQ_VERIFY(connect(ssh_channel_, SIGNAL(channelOK()), this, SLOT(channelOK()))); + FQ_VERIFY(connect(ssh_channel_, SIGNAL(channelReadyRead(const char *, int)), this, SLOT(channelReadyRead(const char *, int)))); + FQ_VERIFY(connect(ssh_channel_, SIGNAL(channelError(QString)), this, SLOT(handleError(QString)))); + + key_exchanger_->initKex(packet_receiver_, packet_sender_); + } +} + +void FQTermSSHSocket::kexOK() { + FQ_TRACE("sshsocket", 3) << "Key exchange completed!"; + authentication_->initAuth(packet_receiver_, packet_sender_); +} + +void FQTermSSHSocket::authOK() { + FQ_TRACE("sshsocket", 3) << "Auth completed!"; + ssh_channel_->initChannel(packet_receiver_, packet_sender_, col_, row_, termtype_); +} + +void FQTermSSHSocket::channelOK() { + FQ_TRACE("sshsocket", 3) << "Channel established!"; + is_channel_ok_ = true; + //auth_ok_emitted_ = false; +} + +void FQTermSSHSocket::channelReadyRead(const char *data, int len) { + input_buffer_->putRawData(data, len); + emit readyRead(); +} + +unsigned long FQTermSSHSocket::socketWriteBlock(const char *data, unsigned long len) { + QByteArray to_write(data, len); + return private_socket_->writeBlock(to_write); +} + +void FQTermSSHSocket::socketReadyRead() { + if (!auth_ok_emitted_ && is_channel_ok_) { + auth_ok_emitted_ = true; + emit sshAuthOK(); + } + unsigned long size; + + switch (ssh_socket_state_) { + case BeforeSession: + { + QByteArray str; + int version; + size = private_socket_->bytesAvailable(); + str = private_socket_->readBlock(size); + + server_name_ = QString(str).trimmed(); // remove the newline + version = chooseVersion(str.data()); + + FQ_TRACE("sshsocket", 3) << "SSH server: " << server_name_; + FQ_TRACE("sshsocket", 3) << "SSH version chosen: " << version; + + if (version == 1) { + init(1); + ssh_version_ = 1; + socketWriteBlock(V1STR, strlen(V1STR)); + } else if (version == 2) { + init(2); + ssh_version_ = 2; + socketWriteBlock(V2STR, strlen(V2STR)); + } else { + handleError(tr("Unknown SSH version. " + "Check if you set the right server and port.")); + return ; + } + ssh_socket_state_ = SockSession; + private_socket_->flush(); + break; + } + case SockSession: + parsePacket(); + } +} + +void FQTermSSHSocket::parsePacket() { + unsigned long size; + QByteArray data; + size = private_socket_->bytesAvailable(); + data = private_socket_->readBlock(size); + + socket_buffer_->putRawData(data.data(), size); + packet_receiver_->parseData(socket_buffer_); +} + +int FQTermSSHSocket::chooseVersion(const QString &ver) { + QString verion = ver.mid(ver.indexOf("-") + 1); + verion = verion.left(verion.indexOf("-")); + + if (verion == "1.99" || verion == "1.3" || verion == "1.5") { + return 1; + } else if (verion == "2.0") { + return 2; + } else { + return - 1; + } +} + +void FQTermSSHSocket::connectToHost(const QString &host_name, quint16 port) { + ssh_socket_state_ = BeforeSession; + private_socket_->connectToHost(host_name, port); +} + +void FQTermSSHSocket::writeData() { + socketWriteBlock((const char*)packet_sender_->output_buffer_->data(), + packet_sender_->output_buffer_->len()); + private_socket_->flush(); +} + +void FQTermSSHSocket::handlePacket(int type) { + if (ssh_version_ == 1) { + switch (type) { + case SSH1_MSG_DISCONNECT: + char *reason; + reason = (char*)packet_receiver_->getString(); + FQ_TRACE("sshsocket", 1) << "Disconnect because: " << reason; + delete [] reason; + break; + default: + return ; + } + } else { + // ssh_version_ == 2; + switch (type) { + case SSH2_MSG_DISCONNECT: + char *reason; + packet_receiver_->consume(4); + reason = (char*)packet_receiver_->getString(); + FQ_TRACE("sshsocket", 1) << "Disconnect because: " << reason; + delete[] reason; + break; + default: + return ; + } + } +} + +unsigned long FQTermSSHSocket::bytesAvailable() { + return input_buffer_->len(); +} + +QByteArray FQTermSSHSocket::readBlock(unsigned long size) { + QByteArray data(size, 0); + input_buffer_->getRawData(data.data(), size); + return data; +} + +long FQTermSSHSocket::writeBlock(const QByteArray &data) { + if (!is_channel_ok_) return 0; + + unsigned long size = data.size(); + output_buffer_->putRawData(data.data(), size); + return size; +} + +void FQTermSSHSocket::flush() { + if (!is_channel_ok_) return; + + int size = output_buffer_->len(); + + ssh_channel_->sendData((const char *)output_buffer_->data(), size); + + output_buffer_->consume(size); +} + +void FQTermSSHSocket::close() { + private_socket_->close(); +} + +void FQTermSSHSocket::handleError(QString reason) { + close(); + + emit errorMessage(reason); + emit connectionClosed(); +} + +void FQTermSSHSocket::setProxy(int nProxyType, bool bAuth, + const QString &strProxyHost, + quint16 uProxyPort, + const QString &strProxyUsr, + const QString &strProxyPwd) { + private_socket_->setProxy(nProxyType, bAuth, strProxyHost, + uProxyPort, strProxyUsr, strProxyPwd); +} + +bool FQTermSSHSocket::setTermSize(int col, int row) { + if (ssh_channel_ && is_channel_ok_) + ssh_channel_->changeTermSize(col, row); + return true; +} +} // namespace FQTerm + +#include "fqterm_ssh_socket.moc" diff --git a/src/protocol/fqterm_ssh_socket.h b/src/protocol/fqterm_ssh_socket.h new file mode 100644 index 0000000..aa6fef4 --- /dev/null +++ b/src/protocol/fqterm_ssh_socket.h @@ -0,0 +1,131 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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. * + ***************************************************************************/ + +#ifndef FQTERM_SSH_SOCKET_H +#define FQTERM_SSH_SOCKET_H + +#include "fqterm_socket.h" + + +namespace FQTerm { + + +class FQTermSocketPrivate; + +class FQTermSSHPacketReceiver; + +class FQTermSSHPacketSender; + +class FQTermSSHBuffer; + +class FQTermSSHKex; + +class FQTermSSHAuth; + +class FQTermSSHChannel; + + +class FQTermSSHSocket: public FQTermSocket { + + Q_OBJECT; +private: + enum FQTermSSHSocketState { + + BeforeSession, SockSession + } ssh_socket_state_; + + FQTermSocketPrivate *private_socket_; + + + FQTermSSHBuffer *input_buffer_; + + FQTermSSHBuffer *output_buffer_; + + FQTermSSHBuffer *socket_buffer_; + + + FQTermSSHPacketReceiver *packet_receiver_; + + FQTermSSHPacketSender *packet_sender_; + + + FQTermSSHKex *key_exchanger_; + + FQTermSSHAuth *authentication_; + + FQTermSSHChannel *ssh_channel_; + + bool is_channel_ok_; + bool auth_ok_emitted_; + + QByteArray init_user_, init_passwd_; + + QString server_name_; + + int ssh_version_; + int col_; + int row_; + QString termtype_; + int chooseVersion(const QString &ver); + unsigned long socketWriteBlock(const char *data, unsigned long len); + void parsePacket(); + +private slots: + void handlePacket(int type); + void writeData(); + void kexOK(); + void authOK(); + void channelOK(); + void channelReadyRead(const char *data, int len); + void socketReadyRead(); + void handleError(QString); + +public: + FQTermSSHSocket(int col = 80, int row = 24, const QString& termtype = "vt100", const char *sshuser = NULL, const char *sshpasswd = NULL); + + ~FQTermSSHSocket(); + + + void setProxy(int nProxyType, //0-no proxy; 1-wingate; 2-sock4; 3-socks5 + bool bAuth, // if authentation needed + const QString &strProxyHost, quint16 uProxyPort, const QString &strProxyUsr, + const QString &strProxyPwd); + + void connectToHost(const QString &host_name, quint16 port); + + void init(int ssh_version); + + QByteArray readBlock(unsigned long size); + long writeBlock(const QByteArray &data); + + virtual bool readyForInput() {return is_channel_ok_;} + virtual bool setTermSize(int col, int row); + unsigned long bytesAvailable(); + + void flush(); + void close(); + +}; + +} // namespace FQTerm + + +#endif //FQTERM_SSH_SOCKET_H + diff --git a/src/protocol/internal/crc32.cpp b/src/protocol/internal/crc32.cpp new file mode 100644 index 0000000..2039ac8 --- /dev/null +++ b/src/protocol/internal/crc32.cpp @@ -0,0 +1,84 @@ +/* $OpenBSD: crc32.c,v 1.9 2003/02/12 21:39:50 markus Exp $ */ + +/* + * Copyright (c) 2003 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "crc32.h" + +namespace FQTerm { +static const u_int32_t crc32tab[] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 0x706af48fL, + 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, + 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, + 0xf3b97148L, 0x84be41deL, 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, + 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L, + 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, 0x35b5a8faL, 0x42b2986cL, + 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, + 0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, + 0xcfba9599L, 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, 0x01db7106L, + 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, + 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, + 0x91646c97L, 0xe6635c01L, 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, + 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, + 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L, + 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, + 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, + 0xbe0b1010L, 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L, + 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL, + 0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L, 0xe3630b12L, 0x94643b84L, + 0x0d6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, + 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, + 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, 0xd6d6a3e8L, 0xa1d1937eL, + 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, + 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, + 0x316e8eefL, 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, 0xb2bd0b28L, + 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, + 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, + 0x72076785L, 0x05005713L, 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, + 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, + 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 0x8f659effL, 0xf862ae69L, + 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, + 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, + 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, + 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, + 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL +}; + +u_int32_t ssh_crc32(const u_char *buf, u_int32_t size) { + u_int32_t i, crc; + + crc = 0; + for (i = 0; i < size; i++) { + crc = crc32tab[(crc ^ buf[i]) &0xff] ^ (crc >> 8); + } return crc; +} + +} // namespace FQTerm diff --git a/src/protocol/internal/crc32.h b/src/protocol/internal/crc32.h new file mode 100644 index 0000000..ca95b89 --- /dev/null +++ b/src/protocol/internal/crc32.h @@ -0,0 +1,37 @@ +/* $OpenBSD: crc32.h,v 1.14 2003/02/12 21:39:50 markus Exp $ */ + +/* + * Copyright (c) 2003 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SSH_CRC32_H +#define SSH_CRC32_H +#include "fqterm_ssh_types.h" + +namespace FQTerm { + +u_int32_t ssh_crc32(const u_char *, u_int32_t); + +} // namespace FQTerm + +#endif diff --git a/src/protocol/internal/fqterm_serialization.h b/src/protocol/internal/fqterm_serialization.h new file mode 100644 index 0000000..7a858c0 --- /dev/null +++ b/src/protocol/internal/fqterm_serialization.h @@ -0,0 +1,54 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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. * + ***************************************************************************/ + +#ifndef FQTERM_SERIALIZATION_H +#define FQTERM_SERIALIZATION_H + +#if defined(WIN32) +typedef unsigned __int16 u_int16_t; +#else +#include +#endif + +namespace FQTerm { + +inline u_int16_t ntohu16(const unsigned char *buf) { + return (buf[0] << 8) | buf[1]; +} + +inline u_int32_t ntohu32(const unsigned char *buf) { + return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; +} + +inline void htonu32(unsigned char *buf, u_int32_t number) { + buf[0] = (number >> 24) & 0xFF; + buf[1] = (number >> 16) & 0xFF; + buf[2] = (number >> 8) & 0xFF; + buf[3] = number & 0xFF; +} + +inline void htonu16(unsigned char *buf, u_int16_t number) { + buf[0] = (number >> 8 & 0xFF); + buf[1] = (number & 0xFF); +} + +} // namespace FQTerm + +#endif // FQTERM_SERIALIZATION_H diff --git a/src/protocol/internal/fqterm_ssh1_packet.cpp b/src/protocol/internal/fqterm_ssh1_packet.cpp new file mode 100644 index 0000000..ded7fe4 --- /dev/null +++ b/src/protocol/internal/fqterm_ssh1_packet.cpp @@ -0,0 +1,148 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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 "fqterm_trace.h" +#include "fqterm_ssh_buffer.h" +#include "fqterm_ssh1_packet.h" +#include "fqterm_ssh_des.h" + +#include "fqterm_serialization.h" +#include "crc32.h" + +namespace FQTerm { +//============================================================================== +//FQTermSSH1PacketSender +//============================================================================== +//============================================================================== +// +// SSH1 Packet Structure: +// -------------------------------------------------------------------------- +// | length | padding | type | data | crc32 | +// -------------------------------------------------------------------------- +// | uint32 | 1-7bytes | uchar | | 4bytes| +// -------------------------------------------------------------------------- +// encrypt = padding + type + data + crc32 +// length = type + data + crc32 +// +//============================================================================== + +void FQTermSSH1PacketSender::makePacket() { + int len, padding, i; + u_int32_t rand_val = 0; + + delete output_buffer_; + + len = buffer_->len() + 4; //CRC32 + padding = 8-(len % 8); + + output_buffer_ = new FQTermSSHBuffer(len + padding + 4); //pktlen and crc32 + output_buffer_->putInt(len); + + for (i = 0; i < padding; i++) { + if (i % 4 == 0) { + rand_val = rand(); // FIXME: rand() doesn't range from 0 to 2^32. + } + + output_buffer_->putByte(rand_val &0xff); + rand_val >>= 8; + } + + output_buffer_->putRawData((const char*)buffer_->data(), buffer_->len()); + output_buffer_->putInt(ssh_crc32(output_buffer_->data() + 4, output_buffer_->len() - 4)); + + if (is_encrypt_) { + cipher_->encrypt(output_buffer_->data() + 4, output_buffer_->data() + 4, output_buffer_->len() - 4); + } + +} + +//============================================================================== +//FQTermSSH1PacketReceiver +//============================================================================== +void FQTermSSH1PacketReceiver::parseData(FQTermSSHBuffer *input) { + u_int mycrc, gotcrc; + u_char *buf = NULL; + u_char *targetData = NULL; + u_char *sourceData = NULL; + + // Get the length of the packet. + while (input->len() > 0) { + if (input->len() < 4) { + FQ_TRACE("ssh1packet", 3) << "The packet is too small."; + return ; + } + buf = input->data(); + real_data_len_ = ntohu32(buf); + + if (real_data_len_ > SSH_BUFFER_MAX) { + emit packetError(tr("parseData: The packet is too big")); + return ; + } + + u_int total_len = (real_data_len_ + 8) &~7; + u_int padding_len = total_len - real_data_len_; + + real_data_len_ -= 5; + buffer_->clear(); + + // Get the data of the packet. + if (input->len() - 4 < (long)total_len) { + FQ_TRACE("ssh1packet", 3) << "The packet is too small"; + return ; + } + + real_data_len_ = input->getInt() - 5; + targetData = new u_char[total_len]; + sourceData = new u_char[total_len]; + memset(targetData, 0, total_len); + memset(sourceData, 0, total_len); + + input->getRawData((char*)sourceData, total_len); + if (is_decrypt_) { + cipher_->decrypt(sourceData, targetData, total_len); + } else { + memcpy(targetData, sourceData, total_len); + } + + buffer_->putRawData((char*)targetData, total_len); + + // Check the crc32. + buf = buffer_->data() + total_len - 4; + mycrc = ntohu32(buf); + gotcrc = ssh_crc32(buffer_->data(), total_len - 4); + + if (mycrc != gotcrc) { + emit packetError(tr("parseData: bad CRC32")); + break; + } + + // Drop the padding. + buffer_->consume(padding_len); + + packet_type_ = buffer_->getByte(); + + emit packetAvaliable(packet_type_); + + delete [] sourceData; + delete [] targetData; + } +} + +} // namespace FQTerm diff --git a/src/protocol/internal/fqterm_ssh1_packet.h b/src/protocol/internal/fqterm_ssh1_packet.h new file mode 100644 index 0000000..a535635 --- /dev/null +++ b/src/protocol/internal/fqterm_ssh1_packet.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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. * + ***************************************************************************/ + +#ifndef FQTERM_SSH1_PACKET_H +#define FQTERM_SSH1_PACKET_H + +#include "fqterm_ssh_packet.h" + +namespace FQTerm { + +class FQTermSSH1PacketSender: public FQTermSSHPacketSender { +protected: + virtual void makePacket(); +}; + +class FQTermSSH1PacketReceiver: public FQTermSSHPacketReceiver { +public: + virtual void parseData(FQTermSSHBuffer *input); +}; + +} // namespace FQTerm + +#endif // FQTERM_SSH1_PACKET diff --git a/src/protocol/internal/fqterm_ssh2_kex.cpp b/src/protocol/internal/fqterm_ssh2_kex.cpp new file mode 100644 index 0000000..acae08d --- /dev/null +++ b/src/protocol/internal/fqterm_ssh2_kex.cpp @@ -0,0 +1,394 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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 +#include +#include +#include +#include +#include +#include + +#include "fqterm_ssh2_kex.h" +#include "fqterm_ssh_md5.h" +#include "fqterm_trace.h" + +namespace FQTerm { + +static const int g = 2; +static const int q = 128; +static const unsigned char p[q]={ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, + 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, + 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +FQTermSSH2Kex::FQTermSSH2Kex(const char *V_C, const char *V_S) + : FQTermSSHKex(V_C, V_S) { + is_first_kex_ = true; + kex_state_ = FQTermSSH2Kex::BEFORE_KEXINIT; + + I_C_len_ = 0; + I_C_ = NULL; + I_S_len_ = 0; + I_S_ = NULL; + + bn_x_ = BN_new(); + bn_e_ = BN_new(); + bn_g_ = BN_new(); + bn_p_ = BN_new(); + ctx_ = BN_CTX_new(); + + bn_K_ = BN_new(); + bn_f_ = BN_new(); + + BN_set_word(bn_g_, g); + BN_bin2bn(p, q, bn_p_); + + session_id_ = NULL; +} + +FQTermSSH2Kex::~FQTermSSH2Kex() { + delete[] I_C_; + delete[] I_S_; + + BN_clear_free(bn_x_); + BN_clear_free(bn_e_); + BN_clear_free(bn_g_); + BN_clear_free(bn_p_); + BN_CTX_free(ctx_); + + BN_clear_free(bn_K_); + BN_clear_free(bn_f_); + + delete[] session_id_; +} + +void FQTermSSH2Kex::initKex(FQTermSSHPacketReceiver *packetReceiver, + FQTermSSHPacketSender *packetSender) { + packet_receiver_ = packetReceiver; + packet_sender_ = packetSender; + packet_receiver_->disconnect(this); + FQ_VERIFY(connect(packet_receiver_, SIGNAL(packetAvaliable(int)), + this, SLOT(handlePacket(int)))); + kex_state_ = FQTermSSH2Kex::BEFORE_KEXINIT; + emit reKex(); +} + +void FQTermSSH2Kex::handlePacket(int type) { + switch (kex_state_) { + case FQTermSSH2Kex::BEFORE_KEXINIT: + negotiateAlgorithms(); + exchangeKey(); + kex_state_ = FQTermSSH2Kex::WAIT_REPLY; + break; + case FQTermSSH2Kex::WAIT_REPLY: + if (verifyKey()) { + sendNewKeys(); + kex_state_ = FQTermSSH2Kex::SESSIONKEY_SENT; + } else { + emit kexError(tr("Key exchange failed!")); + } + break; + case FQTermSSH2Kex::SESSIONKEY_SENT: + if (changeKeyAlg()) { + kex_state_ = FQTermSSH2Kex::KEYEX_OK; + emit kexOK(); + } + break; + case FQTermSSH2Kex::KEYEX_OK: + // TODO: how about Key Re-Exchange (see RFC 4253, 9. Key Re-Exchange) + break; + } +} + +void FQTermSSH2Kex::negotiateAlgorithms() { + FQ_FUNC_TRACE("ssh2kex", 10); + + if (packet_receiver_->packetType() != SSH2_MSG_KEXINIT) { + emit kexError(tr("startKex: First packet is not SSH_MSG_KEXINIT")); + return ; + } + + // 0. Backup the payload of this server packet. + I_S_len_ = packet_receiver_->packetDataLen() + 1; // add 1 bytes for packet type. + delete[] I_S_; + I_S_ = new char[I_S_len_]; + I_S_[0] = SSH2_MSG_KEXINIT; + memcpy(I_S_ + 1, packet_receiver_->buffer_->data(), I_S_len_ - 1); + + // 1. Parse server kex init packet + packet_receiver_->getRawData((char*)cookie_, 16); + + std::vector name_lists; + for (int i = 0; i < 10; ++i) { + int name_lists_len = packet_receiver_->getInt(); + if (name_lists_len > 0) { + name_lists.resize(name_lists_len); + packet_receiver_->getRawData(&name_lists[0], name_lists_len); + FQ_TRACE("ssh2kex", 5) << "Algorithms: " << QString::fromAscii(&name_lists[0], name_lists_len); + } else { + FQ_TRACE("ssh2kex", 5) << "None Algorithms"; + } + } + + bool first_kex_packet_follows = packet_receiver_->getByte(); + FQ_TRACE("ssh2kex", 5) << "first_kex_packet_follows: " << first_kex_packet_follows; + + packet_receiver_->consume(4); + + // 2. compose a kex init packet. + packet_sender_->startPacket(SSH2_MSG_KEXINIT); + packet_sender_->putRawData((const char*)cookie_, 16); // FIXME: generate new cookie_; + packet_sender_->putString("diffie-hellman-group1-sha1"); + packet_sender_->putString("ssh-rsa"); + packet_sender_->putString("3des-cbc"); + packet_sender_->putString("3des-cbc"); + packet_sender_->putString("hmac-sha1"); + packet_sender_->putString("hmac-sha1"); + packet_sender_->putString("none"); + packet_sender_->putString("none"); + packet_sender_->putString(""); + packet_sender_->putString(""); + + packet_sender_->putByte(false); + packet_sender_->putInt(0); + + // 3. backup the payload of this client packet. + I_C_len_ = packet_sender_->buffer_->len(); + delete[] I_C_; + I_C_ = new char[I_C_len_]; + memcpy(I_C_, packet_sender_->buffer_->data(), I_C_len_); + + // 4. send packet to server + packet_sender_->write(); +} + +void FQTermSSH2Kex::exchangeKey() { + BN_rand(bn_x_, q, 0, -1); + BN_mod_exp(bn_e_, bn_g_, bn_x_, bn_p_, ctx_); + + packet_sender_->startPacket(SSH2_MSG_KEXDH_INIT); + packet_sender_->putBN2(bn_e_); + packet_sender_->write(); +} + +static RSA *CreateRSAContext(unsigned char *host_key, int len); + +bool FQTermSSH2Kex::verifyKey() { + if (packet_receiver_->packetType() != SSH2_MSG_KEXDH_REPLY) { + emit kexError(tr("Expect a SSH_MSG_KEXDH_REPLY packet")); + return false; + } + + // Extract data + + int K_S_len = -1; + unsigned char *K_S = (unsigned char *)packet_receiver_->getString(&K_S_len); + + packet_receiver_->getBN2(bn_f_); + + int s_len = -1; + unsigned char *s = (unsigned char *)packet_receiver_->getString(&s_len); + + BN_mod_exp(bn_K_, bn_f_, bn_x_, bn_p_, ctx_); + + FQTermSSHBuffer *buffer = packet_sender_->output_buffer_; + + buffer->clear(); + buffer->putString(V_C_); + buffer->putString(V_S_); + buffer->putString(I_C_, I_C_len_); + buffer->putString(I_S_, I_S_len_); + buffer->putString((char *)K_S, K_S_len); + buffer->putSSH2BN(bn_e_); + buffer->putSSH2BN(bn_f_); + buffer->putSSH2BN(bn_K_); + + SHA1(buffer->data(), buffer->len(), H_); + + // Start verify + unsigned char s_H[SHA_DIGEST_LENGTH]; + SHA1(H_, SHA_DIGEST_LENGTH, s_H); + + // Ignore the first 15 bytes of the signature of H sent from server: + // algorithm_name_length[4], algorithm_name[7]("ssh-rsa") and signature_length[4]. + RSA *rsactx = CreateRSAContext(K_S, K_S_len); + int sig_len = s_len - 15; + unsigned char *sig = s + 15; + int res = RSA_verify(NID_sha1, s_H, SHA_DIGEST_LENGTH, + sig, sig_len, rsactx); + + RSA_free(rsactx); + + delete K_S; + delete s; + + return res == 1; +} + +static RSA *CreateRSAContext(unsigned char *host_key, int len) { + FQTermSSHBuffer buffer(len); + + buffer.putRawData((char *)host_key, len); + + int algo_len = -1; + unsigned char *algo = (unsigned char *)buffer.getString(&algo_len); + + FQ_VERIFY(std::string("ssh-rsa") == std::string((char *)algo)); + + int e_len = -1; + unsigned char *e = (unsigned char *)buffer.getString(&e_len); + + int n_len = -1; + unsigned char *n = (unsigned char *)buffer.getString(&n_len); + + + RSA *rsa = RSA_new(); + rsa->e = BN_new(); + BN_bin2bn(e, e_len, rsa->e); + + rsa->n = BN_new(); + BN_bin2bn(n, n_len, rsa->n); + + delete[] algo; + delete[] e; + delete[] n; + + return rsa; +} + +void FQTermSSH2Kex::sendNewKeys(){ + packet_sender_->startPacket(SSH2_MSG_NEWKEYS); + packet_sender_->write(); +} + +bool FQTermSSH2Kex::changeKeyAlg() { + if (packet_receiver_->packetType() != SSH2_MSG_NEWKEYS) { + emit kexError(tr("Expect a SSH_MSG_NEWKEYS packet")); + return false; + } + + if (session_id_ == NULL) { + session_id_ = new unsigned char[SHA_DIGEST_LENGTH]; + memcpy(session_id_, H_, SHA_DIGEST_LENGTH); + } + + packet_sender_->setEncryptionType(SSH_CIPHER_3DES); + packet_receiver_->setEncryptionType(SSH_CIPHER_3DES); + + packet_sender_->setMacType(FQTERM_SSH_HMAC_SHA1); + packet_receiver_->setMacType(FQTERM_SSH_HMAC_SHA1); + + // From RFC 4253 section 7.2: + // Initial IV client to server: HASH(K || H || "A" || session_id) + // (Here K is encoded as mpint and "A" as byte and session_id as raw + // data. "A" means the single character A, ASCII 65). + // + // Initial IV server to client: HASH(K || H || "B" || session_id) + // + // Encryption key client to server: HASH(K || H || "C" || session_id) + // + // Encryption key server to client: HASH(K || H || "D" || session_id) + // + // Integrity key client to server: HASH(K || H || "E" || session_id) + // + // Integrity key server to client: HASH(K || H || "F" || session_id) + + int IV_c2s_len = packet_sender_->getIVSize(); + unsigned char *IV_c2s = computeKey(IV_c2s_len, 'A'); + + int IV_s2c_len = packet_receiver_->getIVSize(); + unsigned char *IV_s2c = computeKey(IV_s2c_len, 'B'); + + int key_c2s_len = packet_sender_->getKeySize(); + unsigned char *key_c2s = computeKey(key_c2s_len, 'C'); + + int key_s2c_len = packet_receiver_->getKeySize(); + unsigned char *key_s2c = computeKey(key_s2c_len, 'D'); + + int mac_key_c2s_len = packet_sender_->getMacKeySize(); + unsigned char *mac_key_c2s = computeKey(mac_key_c2s_len, 'E'); + + int mac_key_s2c_len = packet_receiver_->getMacKeySize(); + unsigned char *mac_key_s2c = computeKey(mac_key_s2c_len, 'F'); + + + packet_sender_->startEncryption(key_c2s, IV_c2s); + packet_sender_->startMac(mac_key_c2s); + + packet_receiver_->startEncryption(key_s2c, IV_s2c); + packet_receiver_->startMac(mac_key_s2c); + + + delete[] IV_c2s; + delete[] IV_s2c; + delete[] key_c2s; + delete[] key_s2c; + delete[] mac_key_c2s; + delete[] mac_key_s2c; + + return true; +} + +unsigned char *FQTermSSH2Kex::computeKey(int expected_len, char flag) { + unsigned char *key = new unsigned char[expected_len + SHA_DIGEST_LENGTH]; + + int len = 0; + SHA_CTX hash; + + FQTermSSHBuffer K(BN_num_bytes(bn_K_) + 5); + K.putSSH2BN(bn_K_); + + while (len < expected_len) { + SHA1_Init(&hash); + SHA1_Update(&hash, K.data(), K.len()); + SHA1_Update(&hash, H_, SHA_DIGEST_LENGTH); + + if (len == 0) { + SHA1_Update(&hash, &flag, 1); + SHA1_Update(&hash, session_id_, SHA_DIGEST_LENGTH); + } else { + SHA1_Update(&hash, key, len); + } + + SHA1_Final(key + len, &hash); + len += SHA_DIGEST_LENGTH; + } + + return key; +} + +} // namespace FQTerm + +#include "fqterm_ssh2_kex.moc" diff --git a/src/protocol/internal/fqterm_ssh2_kex.h b/src/protocol/internal/fqterm_ssh2_kex.h new file mode 100644 index 0000000..671e094 --- /dev/null +++ b/src/protocol/internal/fqterm_ssh2_kex.h @@ -0,0 +1,105 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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. * + ***************************************************************************/ + +#ifndef FQTERM_SSH2_KEX_H +#define FQTERM_SSH2_KEX_H + +#include + +#include "fqterm_ssh_kex.h" + +namespace FQTerm { + +class FQTermSSHPacketReceiver; +class FQTermSSHPacketSender; + +class FQTermSSH2Kex: public FQTermSSHKex { + Q_OBJECT; +private: + enum FQTermSSH2KexState { + BEFORE_KEXINIT, WAIT_REPLY, SESSIONKEY_SENT, KEYEX_OK + } kex_state_; + + /* + string V_C, the client's identification string (CR and LF + excluded) + string V_S, the server's identification string (CR and LF + excluded) + string I_C, the payload of the client's SSH_MSG_KEXINIT + string I_S, the payload of the server's SSH_MSG_KEXINIT + string K_S, the host key + mpint e, exchange value sent by the client + mpint f, exchange value sent by the server + mpint K, the shared secret + */ + + int I_C_len_; + char *I_C_; + int I_S_len_; + char *I_S_; + + BIGNUM *bn_x_; + BIGNUM *bn_e_; + BIGNUM *bn_g_; + BIGNUM *bn_p_; + BN_CTX *ctx_; + + BIGNUM *bn_K_; + BIGNUM *bn_f_; + + unsigned char H_[SHA_DIGEST_LENGTH]; + + unsigned char *session_id_; + + + + + bool is_first_kex_; + + FQTermSSHRSA *host_key_; + FQTermSSHRSA *server_key_; + + u_char cookie_[16]; + int server_flag_, ciphers_, auth_; +// u_char session_id_[16]; + u_char session_key_[32]; + + void negotiateAlgorithms(); + void exchangeKey(); + bool verifyKey(); + void sendNewKeys(); + bool changeKeyAlg(); + + unsigned char *computeKey(int len, char flag); + +public: + FQTermSSH2Kex(const char *V_C, const char *V_S); + ~FQTermSSH2Kex(); + + virtual void initKex(FQTermSSHPacketReceiver *packetReceiver, + FQTermSSHPacketSender *outputSender); + +public slots: + void handlePacket(int type); +}; + +} // namespace FQTerm + +#endif //FQTERM_SSH2_KEX_H diff --git a/src/protocol/internal/fqterm_ssh2_packet.cpp b/src/protocol/internal/fqterm_ssh2_packet.cpp new file mode 100644 index 0000000..d3094d3 --- /dev/null +++ b/src/protocol/internal/fqterm_ssh2_packet.cpp @@ -0,0 +1,267 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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 "fqterm_trace.h" +#include "fqterm_ssh_buffer.h" +#include "fqterm_ssh2_packet.h" +#include "fqterm_ssh_des.h" + +#include "fqterm_serialization.h" +#include "crc32.h" + +namespace FQTerm { +//============================================================================== +//FQTermSSH2PacketSender +//============================================================================== +// SSH2 Packet Structure: +// uint32 packet_length +// byte padding_length +// byte[n1] payload; n1 = packet_length - padding_length - 1 +// byte[n2] random padding; n2 = padding_length +// byte[m] mac (Message Authentication Code - MAC); m = mac_length +//============================================================================== + +void FQTermSSH2PacketSender::makePacket() { + FQ_TRACE("ssh2packet", 9) << "----------------------------Send " + << (is_encrypt_ ? "Encrypted": "plain") + << " Packet---->>>>>>>"; + + // 0. compress + if (is_compressed_) { + FQ_VERIFY(false); + } + + // 1. compute the padding length for padding. + int non_padding_len = 4 + 1 + buffer_->len(); + + int padding_block_len = 8; + if (is_encrypt_ && cipher_->blockSize() > padding_block_len) { + padding_block_len = cipher_->blockSize(); + } + + int padding_len = padding_block_len - (non_padding_len % padding_block_len); + if (padding_len < 4) { + padding_len += padding_block_len; + } + + // 2. renew the output buffer. + int total_len = non_padding_len + padding_len; + if (is_mac_) { + total_len += mac_->digestSize(); + } + + delete output_buffer_; + output_buffer_ = new FQTermSSHBuffer(total_len); + + // 3. Fill the output buffer. + int packet_len = 1 + buffer_->len() + padding_len; + + output_buffer_->putInt(packet_len); + output_buffer_->putByte(padding_len); + output_buffer_->putRawData((const char*)buffer_->data(), buffer_->len()); + + u_int32_t rand_val = 0; + for (int i = 0; i < padding_len; i++) { + if (i % 4 == 0) { + rand_val = rand(); // FIXME: rand() doesn't range from 0 to 2^32. + } + output_buffer_->putByte(rand_val & 0xff); + rand_val >>= 8; + } + + // 4. Add MAC on the entire unencrypted packet, + // including two length fields, 'payload' and 'random padding'. + if (is_mac_) { + const unsigned char *packet = output_buffer_->data(); + int len = output_buffer_->len(); + + FQTermSSHBuffer buffer(4 + len); + buffer.putInt(sequence_no_); + buffer.putRawData((const char *)packet, len); + + std::vector digest(mac_->digestSize()); + mac_->getDigest(buffer.data(), buffer.len(), &digest[0]); + + FQ_TRACE("ssh2packet", 9) << "Making packets..."; + FQ_TRACE("ssh2packet", 9) << "Append MAC with sequence_no_" << sequence_no_; + FQ_TRACE("ssh2packet", 9) << "Compute MAC with " + << buffer.len() << " bytes data:\n" + << dumpHexString << std::string((char *)buffer.data(), buffer.len()); + FQ_TRACE("ssh2packet", 9) << "MAC data " + << digest.size() << " bytes:\n" + << dumpHexString << std::string((const char *)&digest[0], digest.size()); + + output_buffer_->putRawData((const char *)&digest[0], digest.size()); + } + + if (is_compressed_) { + FQ_VERIFY(false); + } + + if (is_encrypt_) { + // as RFC 4253: + // When encryption is in effect, the packet length, padding + // length, payload, and padding fields of each packet MUST be encrypted + // with the given algorithm. + + u_char *data = output_buffer_->data(); + int len = output_buffer_->len() - mac_->digestSize(); + + FQ_TRACE("ssh2packet", 9) << "An packet (without MAC) to be encrypted:" + << len << " bytes:\n" + << dumpHexString << std::string((const char *)data, len); + + cipher_->encrypt(data, data, len); + + FQ_TRACE("ssh2packet", 9) << "An encrypted packet (without MAC) made:" + << len << " bytes:\n" + << dumpHexString << std::string((const char *)data, len); + } + + ++sequence_no_; +} + +void FQTermSSH2PacketSender::setEncryptionType(int cipherType) { + cipher_type_ = cipherType; + + delete cipher_; + cipher_ = NULL; + + switch (cipher_type_) { + case SSH_CIPHER_3DES: + cipher_ = new FQTermSSH2TripleDESCBC; + break; + } +} + + +//============================================================================== +//FQTermSSH2PacketReceiver +//============================================================================== +void FQTermSSH2PacketReceiver::parseData(FQTermSSHBuffer *input) { + FQ_TRACE("ssh2packet", 9) << "----------------------------Receive " + << (is_decrypt_ ? "Encrypted": "plain") + << " Packet----<<<<<<<"; + while (input->len() > 0) { + // 1. Check the ssh packet + if (input->len() < 16 + || (is_decrypt_ && input->len() < cipher_->blockSize()) + || input->len() < last_expected_input_length_ + ) { + FQ_TRACE("ssh2packet", 3) + << "Got an incomplete packet. Wait for more data."; + return ; + } + + if (last_expected_input_length_ == 0) { + if (is_decrypt_) { + // decrypte the first block to get the packet_length field. + cipher_->decrypt(input->data(), input->data(), cipher_->blockSize()); + } + } else { + // last_expected_input_length_ != 0 + // indicates an incomplete ssh2 packet received last time, + // the first block of data is already decrypted at that time, + // so it must not be decrypted again. + } + + int packet_len = ntohu32(input->data()); + + if (packet_len > SSH_BUFFER_MAX) { + emit packetError(tr("parseData: packet too big")); + return ; + } + + int expected_input_len = 4 + packet_len + (is_mac_ ? mac_->digestSize() : 0); + + if (input->len() < (long)expected_input_len) { + FQ_TRACE("ssh2packet", 3) + << "The packet is too small. Wait for more data."; + last_expected_input_length_ = expected_input_len; + return ; + } else { + last_expected_input_length_ = 0; + } + + // 2. decrypte data. + if (is_decrypt_) { + // decrypte blocks left. + unsigned char *tmp = input->data() + cipher_->blockSize(); + int left_len = expected_input_len - cipher_->blockSize() - mac_->digestSize(); + cipher_->decrypt(tmp, tmp, left_len); + } + + // 3. check MAC + if (is_mac_) { + int digest_len = mac_->digestSize(); + std::vector digest(digest_len); + mac_->getDigest(input->data(), expected_input_len - digest_len, &digest[0]); + + u_char *received_digest = input->data() + expected_input_len - digest_len; + + if (memcmp(&digest[0], received_digest, digest_len) == 0) { + FQ_TRACE("ssh2packet", 0) << "incorrect MAC."; + return ; + } + } + + // 4. get every field of the ssh packet. + packet_len = input->getInt(); + + std::vector data(packet_len); + + input->getRawData((char*)&data[0], packet_len); + if (is_mac_) { + input->consume(mac_->digestSize()); + } + + int padding_len = data[0]; + + real_data_len_ = packet_len - 1 - padding_len; + + buffer_->clear(); + buffer_->putRawData((char*)&data[0] + 1, real_data_len_); + + FQ_TRACE("ssh2packet", 9) << "Receive " << real_data_len_ << " bytes payload:\n" + << dumpHexString << std::string((char *)&data[0] + 1, real_data_len_); + + // 5. notify others a ssh packet is parsed successfully. + packet_type_ = buffer_->getByte(); + real_data_len_ -= 1; + emit packetAvaliable(packet_type_); + + ++sequence_no_; + } +} + +void FQTermSSH2PacketReceiver::setEncryptionType(int cipherType) { + cipher_type_ = cipherType; + + delete cipher_; + cipher_ = NULL; + + switch (cipher_type_) { + case SSH_CIPHER_3DES: + cipher_ = new FQTermSSH2TripleDESCBC; + break; + } +} + +} // namespace FQTerm diff --git a/src/protocol/internal/fqterm_ssh2_packet.h b/src/protocol/internal/fqterm_ssh2_packet.h new file mode 100644 index 0000000..c460984 --- /dev/null +++ b/src/protocol/internal/fqterm_ssh2_packet.h @@ -0,0 +1,51 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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. * + ***************************************************************************/ + +#ifndef FQTERM_SSH2_PACKET_H +#define FQTERM_SSH2_PACKET_H + +#include "fqterm_ssh_packet.h" + +namespace FQTerm { + +class FQTermSSH2PacketSender: public FQTermSSHPacketSender { + protected: + virtual void makePacket(); + + public: + virtual void setEncryptionType(int cipherType); +}; + +class FQTermSSH2PacketReceiver: public FQTermSSHPacketReceiver { + private: + // greater than 0 if last time an incomplete ssh2 packet received. + int last_expected_input_length_; + public: + FQTermSSH2PacketReceiver() + : last_expected_input_length_(0) { + } + + virtual void parseData(FQTermSSHBuffer *input); + virtual void setEncryptionType(int cipherType); +}; + +} // namespace FQTerm + +#endif // FQTERM_SSH2_PACKET diff --git a/src/protocol/internal/fqterm_ssh_auth.cpp b/src/protocol/internal/fqterm_ssh_auth.cpp new file mode 100644 index 0000000..d063d14 --- /dev/null +++ b/src/protocol/internal/fqterm_ssh_auth.cpp @@ -0,0 +1,223 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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 + +#include "fqterm_ssh_auth.h" +#include "fqterm_ssh_packet.h" +#include "fqterm_ssh_const.h" +#include "fqterm_trace.h" + +namespace FQTerm { + +//============================================================================== +//FQTermSSH1PasswdAuth +//============================================================================== + +FQTermSSH1PasswdAuth::FQTermSSH1PasswdAuth(const char *sshuser, + const char *sshpasswd) + : FQTermSSHPasswdAuth(sshuser, sshpasswd) { + is_tried_ = false; + ssh_pw_auth_state_ = FQTermSSH1PasswdAuth::BEFORE_AUTH; +} + +void FQTermSSH1PasswdAuth::initAuth(FQTermSSHPacketReceiver *packet, + FQTermSSHPacketSender *output) { + packet_receiver_ = packet; + packet_sender_ = output; + packet_receiver_->disconnect(this); + FQ_VERIFY(connect(packet_receiver_, SIGNAL(packetAvaliable(int)), + this, SLOT(handlePacket(int)))); + packet_sender_->startPacket(SSH1_CMSG_USER); + user_name_.clear(); + passwd_.clear(); + + if (!default_user_.isEmpty() && !default_passwd_.isEmpty()) { + user_name_ = default_user_; + passwd_ = default_passwd_; + } + + while (user_name_.isEmpty()) { + bool isOK = false; + emit requestUserPwd(&user_name_, &passwd_, &isOK); + // SSHLoginDialog login(&d_user, &d_passwd); + if (!isOK) { + emit authError(tr("UserCancel")); + return ; + } + } + + packet_sender_->putString(user_name_.toLatin1()); + packet_sender_->write(); + ssh_pw_auth_state_ = USER_SENT; + is_tried_ = false; +} + +void FQTermSSH1PasswdAuth::handlePacket(int type) { + switch (ssh_pw_auth_state_) { + case BEFORE_AUTH: + FQ_TRACE("sshauth", 0) << "Auth: We should not be here."; + break; + case USER_SENT: + if (type == SSH1_SMSG_SUCCESS) { + ssh_pw_auth_state_ = AUTH_OK; + emit authOK(); + break; + } + if (type != SSH1_SMSG_FAILURE) { + emit authError(tr("Strange response from server")); + break; + } + if (is_tried_) { + bool isOK = false; + emit requestUserPwd(&user_name_, &passwd_, &isOK); + // SSHLoginDialog login(&d_user, &d_passwd); + if (!isOK) { + emit authError(tr("User canceled")); + break; + } + is_tried_ = false; + } + packet_sender_->startPacket(SSH1_CMSG_AUTH_PASSWORD); + packet_sender_->putString(passwd_.toLatin1()); + packet_sender_->write(); + is_tried_ = true; + break; + case AUTH_OK: + break; + default: + return ; + } +} + +//============================================================================== +//FQTermSSH2PasswdAuth +//============================================================================== + +FQTermSSH2PasswdAuth::FQTermSSH2PasswdAuth(const char *sshuser, + const char *sshpasswd) + : FQTermSSHPasswdAuth(sshuser, sshpasswd) { + is_tried_ = false; + ssh_pw_auth_state_ = FQTermSSH2PasswdAuth::SERVICE_ACCEPTED; +} + +void FQTermSSH2PasswdAuth::initAuth(FQTermSSHPacketReceiver *packet, + FQTermSSHPacketSender *output) { + packet_receiver_ = packet; + packet_sender_ = output; + packet_receiver_->disconnect(this); + FQ_VERIFY(connect(packet_receiver_, SIGNAL(packetAvaliable(int)), + this, SLOT(handlePacket(int)))); + + packet_sender_->startPacket(SSH2_MSG_SERVICE_REQUEST); + packet_sender_->putString("ssh-userauth"); + packet_sender_->write(); + + ssh_pw_auth_state_ = FQTermSSH2PasswdAuth::SERVICE_ACCEPTED; +} + +void FQTermSSH2PasswdAuth::handlePacket(int type) { + switch (ssh_pw_auth_state_) { + case FQTermSSH2PasswdAuth::SERVICE_ACCEPTED: + sendUserPasswd(); + ssh_pw_auth_state_ = FQTermSSH2PasswdAuth::USER_PASSWD_SENT; + break; + case FQTermSSH2PasswdAuth::USER_PASSWD_SENT: + if (check()) { + FQ_TRACE("ssh2passwdauth", 3) << "Auth OK."; + + emit authOK(); + ssh_pw_auth_state_ = FQTermSSH2PasswdAuth::AUTH_OK; + } + break; + case FQTermSSH2PasswdAuth::AUTH_OK: + break; + default: + return ; + } +} + +bool FQTermSSH2PasswdAuth::check() { + switch(packet_receiver_->packetType()) { + case SSH2_MSG_USERAUTH_SUCCESS: + return true; + break; + case SSH2_MSG_USERAUTH_BANNER: + // TODO: just ignore banner messages. + break; + case SSH2_MSG_USERAUTH_FAILURE: + emit authError(tr("Authentication failed!")); + break; + default: + emit authError(tr("Unexpected packet")); + } + + return false; +} + +void FQTermSSH2PasswdAuth::sendUserPasswd() { + if (packet_receiver_->packetType() != SSH2_MSG_SERVICE_ACCEPT) { + emit authError(tr("Expect a SSH2_MSG_SERVICE_ACCEPT packet")); + return; + } + + u_char *service_name = (u_char *)packet_receiver_->getString(); + + if (std::string((char *)service_name) != std::string("ssh-userauth")) { + emit authError(tr("Error when sending username and password.")); + return; + } + + delete[] service_name; + + // byte SSH_MSG_USERAUTH_REQUEST + // string user name in ISO-10646 UTF-8 encoding [RFC3629] + // string service name in US-ASCII + // string method name in US-ASCII + // .... method specific fields + user_name_.clear(); + passwd_.clear(); + + if (!default_user_.isEmpty() && !default_passwd_.isEmpty()) { + user_name_ = default_user_; + passwd_ = default_passwd_; + } + + while (user_name_.isEmpty()) { + bool isOK = false; + emit requestUserPwd(&user_name_, &passwd_, &isOK); + if (!isOK) { + emit authError(tr("UserCancel")); + return ; + } + } + + packet_sender_->startPacket(SSH2_MSG_USERAUTH_REQUEST); + packet_sender_->putString(user_name_.toLatin1()); + packet_sender_->putString("ssh-connection"); + packet_sender_->putString("password"); + packet_sender_->putByte(false); + packet_sender_->putString(passwd_.toLatin1()); + packet_sender_->write(); +} + +} // namespace FQTerm + +#include diff --git a/src/protocol/internal/fqterm_ssh_auth.h b/src/protocol/internal/fqterm_ssh_auth.h new file mode 100644 index 0000000..6152a71 --- /dev/null +++ b/src/protocol/internal/fqterm_ssh_auth.h @@ -0,0 +1,112 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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. * + ***************************************************************************/ + +#ifndef FQTERM_SSH_AUTH_H +#define FQTERM_SSH_AUTH_H + +#include + +class QString; + +namespace FQTerm { + +class FQTermSSHPacketReceiver; +class FQTermSSHPacketSender; + +class FQTermSSHAuth: public QObject { + + Q_OBJECT; +protected: + QString user_name_; + FQTermSSHPacketReceiver *packet_receiver_; + FQTermSSHPacketSender *packet_sender_; + +public: + FQTermSSHAuth(const char *sshuser = NULL) + : user_name_(sshuser) { + } + + ~FQTermSSHAuth() {} + + virtual void initAuth(FQTermSSHPacketReceiver *packet, + FQTermSSHPacketSender *output) = 0; +public slots: + virtual void handlePacket(int type) = 0; + +signals: + void requestUserPwd(QString *user, QString *pwd, bool *isOK); + void authOK(); + void authError(QString); +}; + +class FQTermSSHPasswdAuth: public FQTermSSHAuth { + Q_OBJECT; +protected: + QString passwd_; + bool is_tried_; + + QString default_user_; + QString default_passwd_; + +public: + FQTermSSHPasswdAuth(const char *sshuser, const char *sshpasswd) + : FQTermSSHAuth(sshuser), + passwd_(sshpasswd), + default_user_(sshuser), + default_passwd_(sshpasswd) { + } +}; + +class FQTermSSH1PasswdAuth: public FQTermSSHPasswdAuth { + Q_OBJECT; +private: + enum FQTermSSH1PasswdAuthState { + BEFORE_AUTH, USER_SENT, PASS_SENT, AUTH_OK + } ssh_pw_auth_state_; + +public: + FQTermSSH1PasswdAuth(const char *sshuser, const char *sshpasswd); + +public slots: + void handlePacket(int type); + void initAuth(FQTermSSHPacketReceiver *packet, FQTermSSHPacketSender *output); +}; + +class FQTermSSH2PasswdAuth: public FQTermSSHPasswdAuth { + Q_OBJECT; +private: + enum FQTermSSH2PasswdAuthState { + SERVICE_ACCEPTED, USER_PASSWD_SENT, PASS_SENT, AUTH_OK + } ssh_pw_auth_state_; + + void sendUserPasswd(); + bool check(); + +public: + FQTermSSH2PasswdAuth(const char *sshuser, const char *sshpasswd); + +public slots: + void handlePacket(int type); + void initAuth(FQTermSSHPacketReceiver *packet, FQTermSSHPacketSender *output); +}; + +} // namespace FQTerm + +#endif // FQTERM_SSH_AUTH_H diff --git a/src/protocol/internal/fqterm_ssh_buffer.cpp b/src/protocol/internal/fqterm_ssh_buffer.cpp new file mode 100644 index 0000000..d95497a --- /dev/null +++ b/src/protocol/internal/fqterm_ssh_buffer.cpp @@ -0,0 +1,302 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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 +#include +#include + +#include "fqterm_trace.h" +#include "fqterm_ssh_buffer.h" +#include "fqterm_serialization.h" + +namespace FQTerm { +//============================================================================== +// FQTermSSHBuffer +//============================================================================== + +FQTermSSHBuffer::FQTermSSHBuffer(int size) { + alloc_size_ = size; + buffer_size_ = 0; + offset_ = 0; + buffer_ = new u_char[alloc_size_]; +} + +FQTermSSHBuffer::~FQTermSSHBuffer() { + delete [] buffer_; + offset_ = 0; + buffer_size_ = 0; + alloc_size_ = 0; +} + +void FQTermSSHBuffer::ensure(int len) { + if (len <= (alloc_size_ - (offset_ + buffer_size_))) { + return ; + } else { + alloc_size_ = buffer_size_ + len; + rebuffer(); + } +} + +void FQTermSSHBuffer::rebuffer() { + u_char *newBuffer; + newBuffer = new u_char[alloc_size_]; + memset(newBuffer, 0, alloc_size_); + memcpy(newBuffer, buffer_ + offset_, buffer_size_); + delete [] buffer_; + buffer_ = newBuffer; + offset_ = 0; +} + +void FQTermSSHBuffer::clear() { + memset(buffer_, 0, alloc_size_); + offset_ = 0; + buffer_size_ = 0; +} + +void FQTermSSHBuffer::consume(int len) { + if (len > buffer_size_) { + len = buffer_size_; + } + + offset_ += len; + buffer_size_ -= len; +} + +void FQTermSSHBuffer::putRawData(const char *data, int len) { + if (len < 0) { + FQ_TRACE("sshbuffer", 0) << "Write data error."; + } + + ensure(len); + memcpy((buffer_ + offset_ + buffer_size_), data, len); + buffer_size_ += len; +} + +void FQTermSSHBuffer::getRawData(char *data, int len) { + if (len <= buffer_size_ && len >= 0) { + memcpy(data, buffer_ + offset_, len); + consume(len); + } else { + FQ_TRACE("sshbuffer", 0) << "Read too many data: " << len << " bytes."; + } +} + +//============================================================================== +// Store an BIGNUM in the buffer with a 2-byte msb first bit count, followed by +// (bits+7)/8 bytes of binary data, msb first. +//============================================================================== + +void FQTermSSHBuffer::putSSH1BN(BIGNUM *bignum) { + int bits = BN_num_bits(bignum); + int bin_size = (bits + 7) / 8; + u_char *buf = new u_char[bin_size]; + int oi; + u_char msg[2]; + + // Get the value of in binary + oi = BN_bn2bin(bignum, buf); + if (oi != bin_size) { + FQ_TRACE("sshbuffer", 0) << "BN_bn2bin() failed: oi = " << oi + << " != bin_size." << bin_size; + } + + // Store the number of bits in the buffer in two bytes, msb first + htonu16(msg, bits); + putRawData((char*)msg, 2); + // Store the binary data. + putRawData((char*)buf, oi); + + memset(buf, 0, bin_size); + delete [] buf; +} + + + +void FQTermSSHBuffer::putSSH2BN(BIGNUM *bignum) { + // FIXME: support negative number and add error handling. + + FQ_VERIFY(!bignum->neg); // currently we don't support negative big number. + + if (BN_is_zero(bignum)) { + this->putInt(0); + } else { + u_int bytes = BN_num_bytes(bignum) + 1; + + FQ_VERIFY(bytes >= 2); // currently we don't support big numbers so small + + std::vector buf(bytes); + buf[0] = 0; + + int bin_len = BN_bn2bin(bignum, &buf[0] + 1); + + FQ_VERIFY(bin_len == (int)bytes - 1); + + u_int no_high_bit = (buf[1] & 0x80) ? 0 : 1; + + this->putInt(bytes - no_high_bit); + this->putRawData((const char *)&buf[0] + no_high_bit, bytes - no_high_bit); + } +} + +//============================================================================== +// Retrieves a BIGNUM from the buffer. +//============================================================================== + +void FQTermSSHBuffer::getSSH1BN(BIGNUM *bignum) { + int bits, bytes; + u_char buf[2]; + u_char *bin; + + // Get the number for bits. + getRawData((char*)buf, 2); + bits = ntohu16(buf); + // Compute the number of binary bytes that follow. + bytes = (bits + 7) / 8; + if (bytes > 8 *1024) { + FQ_TRACE("sshbuffer", 0) << "Can't handle BN of size " << bytes; + return ; + } + if (len() < bytes) { + FQ_TRACE("sshbuffer", 0) << "The input buffer is too small."; + return ; + } + bin = data(); + BN_bin2bn(bin, bytes, bignum); + consume(bytes); +} + +void FQTermSSHBuffer::getSSH2BN(BIGNUM *bignum) { + // FIXME: support negative numbers and error handling + int len; + unsigned char *hex_data = (unsigned char *)getString(&len); + + FQ_VERIFY(!(len > 0 && (hex_data[0] & 0x80))); // don't support negative numbers. + + FQ_VERIFY(len < 10240); // don't support so large numbers. + + BIGNUM *res = BN_bin2bn(hex_data, len, bignum); + + FQ_VERIFY(res != NULL); + + delete hex_data; +} + +u_short FQTermSSHBuffer::getWord() { + u_char buf[2]; + u_short data; + + getRawData((char*)buf, 2); + data = ntohu16(buf); + return data; +} + +void FQTermSSHBuffer::putWord(u_short data) { + u_char buf[2]; + + htonu16(buf, data); + putRawData((char*)buf, 2); +} + +u_int FQTermSSHBuffer::getInt() { + u_char buf[4]; + u_int data; + getRawData((char*)buf, 4); + data = ntohu32(buf); + return data; +} + +void FQTermSSHBuffer::putInt(u_int data) { + u_char buf[4]; + + htonu32(buf, data); + putRawData((char*)buf, 4); +} + +//============================================================================== +// Return a character from the buffer (0-255). +//============================================================================== + +u_char FQTermSSHBuffer::getByte() { + u_char ch; + + getRawData((char*) &ch, 1); + return ch; +} + +//============================================================================== +// Stores a character in the buffer. +//============================================================================== + +void FQTermSSHBuffer::putByte(int data) { + u_char ch = data; + + putRawData((char*) &ch, 1); +} + +//============================================================================== +// Stores an arbitrary binary string in the buffer. +//============================================================================== + +void FQTermSSHBuffer::putString(const char *str, int len) { + if (str == NULL) { + FQ_TRACE("sshbuffer", 0) << "Can't put a null pointer string."; + return ; + } + + if (len < 0) { + len = strlen(str); + } + + putInt(len); + putRawData(str, len); +} + +//============================================================================== +// Return an arbitrary binary string from the buffer. The string cannot be +// longer than 256k. The returned value points to memory allocated with new; +// It is the responsibility of the calling function to free the data. +//============================================================================== + +void *FQTermSSHBuffer::getString(int *length) { + u_char *data; + u_int len; + + // Get the length. + len = getInt(); + if ((long)len > buffer_size_) { + FQ_TRACE("sshbuffer", 0) + << "String length " << len + << " is greater than buffer size " << buffer_size_; + return 0; + } + // Allocate space for the string. Add one byte for a null character. + data = new u_char[len + 1]; + // Get the string. + getRawData((char*)data, len); + // Append a null character to make processing easier. + data[len] = 0; + if (length != NULL) { + *length = len; + } + // return the length of the string. + return data; +} + +} // namespace FQTerm diff --git a/src/protocol/internal/fqterm_ssh_buffer.h b/src/protocol/internal/fqterm_ssh_buffer.h new file mode 100644 index 0000000..e06872f --- /dev/null +++ b/src/protocol/internal/fqterm_ssh_buffer.h @@ -0,0 +1,83 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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. * + ***************************************************************************/ + +#ifndef FQTERM_SSH_BUFFER_H +#define FQTERM_SSH_BUFFER_H + +#include +#include + +#include "fqterm_ssh_types.h" + +namespace FQTerm { + +#define SSH_BUFFER_MAX 10000000 + +class FQTermSSHBuffer { + private: + u_char *buffer_; + int offset_; + int buffer_size_; + int alloc_size_; + + void ensure(int len); + void rebuffer(); + + public: + FQTermSSHBuffer(int size); + ~FQTermSSHBuffer(); + + u_char *data() const { + return buffer_ + offset_; + } + + int len() const { + return buffer_size_; + } + + void consume(int len); + + void clear(); + + void putRawData(const char *data, int len); + void getRawData(char *data, int len); + + void putSSH1BN(BIGNUM *bignum); + void getSSH1BN(BIGNUM *bignum); + + void putSSH2BN(BIGNUM *bignum); + void getSSH2BN(BIGNUM *bignum); + + void putInt(u_int data); + u_int getInt(); + + void putWord(u_short data); + u_short getWord(); + + void putByte(int data); + u_char getByte(); + + void putString(const char *str, int len = -1); + void *getString(int *length = NULL); +}; + +} // namespace FQTerm + +#endif //FQTERM_SSH_BUFFER diff --git a/src/protocol/internal/fqterm_ssh_channel.cpp b/src/protocol/internal/fqterm_ssh_channel.cpp new file mode 100644 index 0000000..38a755a --- /dev/null +++ b/src/protocol/internal/fqterm_ssh_channel.cpp @@ -0,0 +1,439 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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 "fqterm_ssh_channel.h" +#include "fqterm_ssh_const.h" +#include "fqterm_ssh_packet.h" +#include "fqterm_trace.h" +#include +namespace FQTerm { + +//============================================================================== +//FQTermSSH1Channel +//============================================================================== + +FQTermSSH1Channel::FQTermSSH1Channel() + : FQTermSSHChannel() { + service_state_ = FQTermSSH1Channel::BEGIN_SERVICE; +} + +void FQTermSSH1Channel::initChannel(FQTermSSHPacketReceiver *packet, + FQTermSSHPacketSender *output, + int col, int row, const QString& termtype) { + packet_receiver_ = packet; + packet_sender_ = output; + packet_receiver_->disconnect(this); + FQ_VERIFY(connect(packet_receiver_, SIGNAL(packetAvaliable(int)), this, SLOT(handlePacket(int)))); + packet_sender_->startPacket(SSH1_CMSG_REQUEST_PTY); + // pty request is of no use in BBS, but we do this + packet_sender_->putString(termtype.toLatin1()); + packet_sender_->putInt(row); // FIXME: hardcoded term size. + packet_sender_->putInt(col); + packet_sender_->putInt(0); + packet_sender_->putInt(0); + packet_sender_->putByte(0); + packet_sender_->write(); + service_state_ = FQTermSSH1Channel::REQPTY_SENT; + + is_closed_ = false; +} + +void FQTermSSH1Channel::changeTermSize(int col, int row) { + packet_sender_->startPacket(SSH1_CMSG_WINDOW_SIZE); + packet_sender_->putInt(row); + packet_sender_->putInt(col); + packet_sender_->putInt(0); + packet_sender_->putInt(0); + packet_sender_->write(); +} + +void FQTermSSH1Channel::sendData(const char *data, int len) { + packet_sender_->startPacket(SSH1_CMSG_STDIN_DATA); + packet_sender_->putInt(len); + packet_sender_->putRawData(data, len); + packet_sender_->write(); +} + +void FQTermSSH1Channel::closeConnection(const char *reason) { + packet_sender_->startPacket(SSH1_MSG_DISCONNECT); + packet_sender_->putString(reason); + packet_sender_->write(); + is_closed_ = true; +} + +void FQTermSSH1Channel::handlePacket(int type) { + switch (service_state_) { + case BEGIN_SERVICE: + FQ_TRACE("sshchannel", 0) << "Channel: We should not be here"; + break; + case REQPTY_SENT: + if (type != SSH1_SMSG_SUCCESS) { + emit channelError(tr("Server refused pty allocation!")); + } + packet_sender_->startPacket(SSH1_CMSG_EXEC_SHELL); + packet_sender_->write(); + emit channelOK(); + service_state_ = FQTermSSH1Channel::SERVICE_OK; + //emit msg to tell window we could process input. + break; + case SERVICE_OK: + switch (type) { + case SSH1_SMSG_STDOUT_DATA: + case SSH1_SMSG_STDERR_DATA: + { + const char *data = (const char *)packet_receiver_->buffer_->data() + 4; + int len = packet_receiver_->packetDataLen() - 4; + emit channelReadyRead(data, len); + } + break; + case SSH1_SMSG_X11_OPEN: + case SSH1_SMSG_AGENT_OPEN: + case SSH1_MSG_PORT_OPEN: + { + int i = packet_receiver_->getInt(); + packet_sender_->startPacket(SSH1_MSG_CHANNEL_OPEN_FAILURE); + packet_sender_->putInt(i); + packet_sender_->write(); + } + break; + case SSH1_SMSG_EXIT_STATUS: + packet_sender_->startPacket(SSH1_CMSG_EXIT_CONFIRMATION); + packet_sender_->write(); + closeConnection("end"); + is_closed_ = true; + break; + case SSH1_SMSG_SUCCESS: + case SSH1_SMSG_FAILURE: + break; + default: + FQ_TRACE("sshchannel", 0) << "Unimplemented message: " + << service_state_; + } + } +} + + +//============================================================================== +//FQTermSSH2Channel +//============================================================================== +u_int32_t FQTermSSH2Channel::generateChannelID() { + static u_int32_t id = 0; + return id++; +} + +FQTermSSH2Channel::FQTermSSH2Channel() + : FQTermSSHChannel(), + col_(80), + row_(24), + termtype_("vt100") { + channel_id_ = generateChannelID(); + server_channel_id_ = 0xCCCCCCCC; + local_window_size_ = 0; + channel_state_ = FQTermSSH2Channel::BEGIN_CHANNEL; +} + +void FQTermSSH2Channel::initChannel(FQTermSSHPacketReceiver *packet, + FQTermSSHPacketSender *output, + int col, int row, const QString& termtype) { + FQ_FUNC_TRACE("ssh2channel", 5); + col_ = col; + row_ = row; + termtype_ = termtype; + packet_receiver_ = packet; + packet_sender_ = output; + packet_receiver_->disconnect(this); + FQ_VERIFY(connect(packet_receiver_, SIGNAL(packetAvaliable(int)), this, SLOT(handlePacket(int)))); + + // byte SSH_MSG_CHANNEL_OPEN + // string channel type in US-ASCII only + // uint32 sender channel + // uint32 initial window size + // uint32 maximum packet size + // .... channel type specific data follows + + packet_sender_->startPacket(SSH2_MSG_CHANNEL_OPEN); + packet_sender_->putString("session"); + packet_sender_->putInt(channel_id_); + packet_sender_->putInt(MAX_LOCAL_WINDOW_SIZE); // TODO: what's the best window size? + packet_sender_->putInt(MAX_LOCAL_PACKET_SIZE); // TODO: what's the best maximum packet size? + packet_sender_->write(); + + server_window_size_ = 0; + server_max_packet_size_ = 0; + local_window_size_ = MAX_LOCAL_WINDOW_SIZE; + + is_closed_ = false; +} + +void FQTermSSH2Channel::changeTermSize(int col, int row) { + // byte SSH_MSG_CHANNEL_REQUEST + // uint32 recipient channel + // string "window-change" + // boolean FALSE + // uint32 terminal width, columns + // uint32 terminal height, rows + // uint32 terminal width, pixels + // uint32 terminal height, pixels + + packet_sender_->startPacket(SSH2_MSG_CHANNEL_REQUEST); + packet_sender_->putInt(server_channel_id_); + packet_sender_->putString("window-change"); + packet_sender_->putByte(false); + packet_sender_->putInt(col); + packet_sender_->putInt(row); + packet_sender_->putInt(640); // FIXME: hard-coded screen pixels. + packet_sender_->putInt(480); + packet_sender_->write(); +} + +void FQTermSSH2Channel::sendData(const char *data, int len) { + if (len > (int)server_window_size_ || len > (int)server_max_packet_size_) { + FQ_TRACE("ssh2channel", 3) << "Data length is greater than server's capacity: " + << "data length: " << len << ", " + << "server window: " << server_window_size_ << ", " + << "server packet max size: " << server_max_packet_size_; + return; + } + + // byte SSH_MSG_CHANNEL_DATA + // uint32 recipient channel + // string data + packet_sender_->startPacket(SSH2_MSG_CHANNEL_DATA); + packet_sender_->putInt(server_channel_id_); + packet_sender_->putString(data, len); + packet_sender_->write(); + + server_window_size_ -= len;; + + FQ_TRACE("ssh2channel", 5) << len + << " bytes data sent, server window size left: " + << server_window_size_;; +} + +void FQTermSSH2Channel::closeConnection(const char *reason) { + packet_sender_->startPacket(SSH1_MSG_DISCONNECT); + packet_sender_->putString(reason); + packet_sender_->write(); + is_closed_ = true; +} + +void FQTermSSH2Channel::requestPty() { + if (packet_receiver_->packetType() == SSH2_MSG_CHANNEL_OPEN_FAILURE) { + // TODO: Here the error reason in the packet is ignored. + emit channelError(tr("Server refuces to open a channel.")); + return; + } + + if (packet_receiver_->packetType() != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) { + emit channelError(tr("Server error when opening a channel.")); + return; + } + + FQ_TRACE("ssh2channel", 5) << "Channel open. Try to request a pty."; + + // byte SSH_MSG_CHANNEL_OPEN_CONFIRMATION + // uint32 recipient channel + // uint32 sender channel + // uint32 initial window size + // uint32 maximum packet size + // .... channel type specific data follows + packet_receiver_->consume(4); + server_channel_id_ = packet_receiver_->getInt(); + server_window_size_ = packet_receiver_->getInt(); + server_max_packet_size_ = packet_receiver_->getInt(); + + // byte SSH_MSG_CHANNEL_REQUEST + // uint32 recipient channel + // string "pty-req" + // boolean want_reply + // string TERM environment variable value (e.g., vt100) + // uint32 terminal width, characters (e.g., 80) + // uint32 terminal height, rows (e.g., 24) + // uint32 terminal width, pixels (e.g., 640) + // uint32 terminal height, pixels (e.g., 480) + // string encoded terminal modes + packet_sender_->startPacket(SSH2_MSG_CHANNEL_REQUEST); + packet_sender_->putInt(server_channel_id_); + packet_sender_->putString("pty-req"); + packet_sender_->putByte(true); + packet_sender_->putString(termtype_.toLatin1()); // TODO: hardcoded term type. + packet_sender_->putInt(col_); // FIXME: hardcoded screen parameters. + packet_sender_->putInt(row_); + packet_sender_->putInt(640); + packet_sender_->putInt(480); + packet_sender_->putString(""); // TODO: no modes sent. + packet_sender_->write(); +} + + +void FQTermSSH2Channel::requestShell() { + if (packet_receiver_->packetType() != SSH2_MSG_CHANNEL_SUCCESS) { + emit channelError(tr("Server refused pty allocation!")); + } + FQ_TRACE("ssh2channel", 5) << "Pty allocated. Now try to request a shell."; + // byte SSH_MSG_CHANNEL_REQUEST + // uint32 recipient channel + // string "shell" + // boolean want reply + packet_sender_->startPacket(SSH2_MSG_CHANNEL_REQUEST); + packet_sender_->putInt(server_channel_id_); + packet_sender_->putString("shell"); + packet_sender_->putByte(true); + packet_sender_->write(); +} + +void FQTermSSH2Channel::checkLocalWindowSize() { + if (local_window_size_ < MAX_LOCAL_WINDOW_SIZE/2) { + // byte SSH_MSG_CHANNEL_WINDOW_ADJUST + // uint32 recipient channel + // uint32 bytes to add + int inc = MAX_LOCAL_WINDOW_SIZE/2; + local_window_size_ += inc; + + packet_sender_->startPacket(SSH2_MSG_CHANNEL_WINDOW_ADJUST); + packet_sender_->putInt(server_channel_id_); + packet_sender_->putInt(inc); + packet_sender_->write(); + } +} + +void FQTermSSH2Channel::processChannelPacket() { + switch (packet_receiver_->packetType()) { + case SSH2_MSG_CHANNEL_WINDOW_ADJUST: + { + // byte SSH_MSG_CHANNEL_WINDOW_ADJUST + // uint32 recipient channel + // uint32 bytes to add + packet_receiver_->consume(4); // channel id is already checked. + int inc = packet_receiver_->getInt(); + server_window_size_ += inc; + FQ_TRACE("ssh2channel", 5) << "Server window size increased from " + << server_window_size_ - inc + << " to " << server_window_size_; + } + break; + case SSH2_MSG_CHANNEL_DATA: + { + // byte SSH_MSG_CHANNEL_DATA + // uint32 recipient channel + // string data + packet_receiver_->consume(4); + int len = packet_receiver_->getInt(); + const char *data = (const char *)packet_receiver_->buffer_->data(); + local_window_size_ -= len; + checkLocalWindowSize(); + emit channelReadyRead(data, len); + } + break; + case SSH2_MSG_CHANNEL_EXTENDED_DATA: + { + // byte SSH_MSG_CHANNEL_DATA + // uint32 recipient channel + // string data + packet_receiver_->consume(4); + int len = packet_receiver_->getInt(); + const char *data = (const char *)packet_receiver_->buffer_->data(); + local_window_size_ -= len; + checkLocalWindowSize(); + emit channelReadyRead(data, len); + } + break; + case SSH2_MSG_CHANNEL_EOF: + case SSH2_MSG_CHANNEL_CLOSE: + // byte SSH_MSG_CHANNEL_EOF + // uint32 recipient channel + // FIXME: this error would cause the connection closed, while only the channel need be closed in ssh2. + emit channelError(tr("Channel closed by the server.")); + break; + case SSH2_MSG_CHANNEL_REQUEST: + // byte SSH_MSG_CHANNEL_REQUEST + // uint32 recipient channel + // string "xon-xoff" + // boolean FALSE + // boolean client can do + + // TODO: just ignore this message currently. + break; + } +} + +void FQTermSSH2Channel::handlePacket(int type) { + // first check the channel id. + u_int32_t channel_id = ntohu32(packet_receiver_->buffer_->data()); + if (channel_id != channel_id_) { + return; + } + + switch (channel_state_) { + case FQTermSSH2Channel::BEGIN_CHANNEL: + requestPty(); + channel_state_ = FQTermSSH2Channel::REQUEST_PTY_SENT; + break; + case FQTermSSH2Channel::REQUEST_PTY_SENT: + requestShell(); + channel_state_ = FQTermSSH2Channel::REQUEST_SHELL_SENT; + break; + case FQTermSSH2Channel::REQUEST_SHELL_SENT: + switch (type) { + case SSH2_MSG_CHANNEL_REQUEST: + { + FQ_TRACE("ssh2channel", 8) << "SSH2_MSG_CHANNEL_REQUEST isn't supported, just send back a packet with SSH2_MSG_CHANNEL_SUCCESS if reply is needed."; + + packet_receiver_->consume(4); + u_int32_t len = ntohu32(packet_receiver_->buffer_->data()); + packet_receiver_->consume(4 + len); + bool replyNeeded = packet_receiver_->getByte(); + + if (replyNeeded) { + packet_sender_->startPacket(SSH2_MSG_CHANNEL_SUCCESS); + packet_sender_->putInt(server_channel_id_); + packet_sender_->write(); + } + } + break; + case SSH2_MSG_CHANNEL_WINDOW_ADJUST: + { + packet_receiver_->consume(4); + u_int32_t inc = packet_receiver_->getInt(); + server_window_size_ += inc; + } + break; + case SSH2_MSG_CHANNEL_SUCCESS: + emit channelOK(); + channel_state_ = FQTermSSH2Channel::CHANNEL_OK; + //emit a msg to tell window we could process input. + break; + case SSH2_MSG_CHANNEL_FAILURE: + emit channelError(tr("Can't open a shell.")); + break; + default: + emit channelError(tr("Unsupported packet.")); + break; + } + break; + case FQTermSSH2Channel::CHANNEL_OK: + processChannelPacket(); + break; + } +} + +} // namespace FQTerm + +#include "fqterm_ssh_channel.moc" diff --git a/src/protocol/internal/fqterm_ssh_channel.h b/src/protocol/internal/fqterm_ssh_channel.h new file mode 100644 index 0000000..9922528 --- /dev/null +++ b/src/protocol/internal/fqterm_ssh_channel.h @@ -0,0 +1,130 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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. * + ***************************************************************************/ + +#ifndef FQTERM_SSH_CHANNEL_H +#define FQTERM_SSH_CHANNEL_H + +#include "fqterm_ssh_types.h" + +#include + +namespace FQTerm { + +class FQTermSSHPacketReceiver; +class FQTermSSHPacketSender; + +class FQTermSSHChannel: public QObject { + Q_OBJECT; +protected: + bool is_closed_; + + FQTermSSHPacketReceiver *packet_receiver_; + FQTermSSHPacketSender *packet_sender_; + +public: + FQTermSSHChannel(){} + virtual ~FQTermSSHChannel(){} + virtual void initChannel(FQTermSSHPacketReceiver *packet, + FQTermSSHPacketSender *output, + int col, int row, const QString& termtype) = 0; + virtual void closeConnection(const char *reason) = 0; + + // TODO: it seems this function isn't used. + virtual void changeTermSize(int col, int row) = 0; + + virtual void sendData(const char *data, int len) = 0; + +public slots: + virtual void handlePacket(int type) = 0; +signals: + void channelOK(); + void channelReadyRead(const char *data, int len); + void channelError(QString); +}; + +class FQTermSSH1Channel: public FQTermSSHChannel { + Q_OBJECT; +private: + enum FQTermSSH1ChannelState { + BEGIN_SERVICE, REQPTY_SENT, + //REQCMD_SENT, + SERVICE_OK + } service_state_; + +public: + FQTermSSH1Channel(); + virtual void initChannel(FQTermSSHPacketReceiver *packet, FQTermSSHPacketSender *output, + int col, int row, const QString& termtype); + virtual void closeConnection(const char *reason); + virtual void changeTermSize(int col, int row); + virtual void sendData(const char *data, int len); + +public slots: + virtual void handlePacket(int type); +signals: + void channelReadyRead(const char *data, int len); +}; + + +class FQTermSSH2Channel: public FQTermSSHChannel { + Q_OBJECT; +private: + static u_int32_t generateChannelID(); + +private: + int col_; + int row_; + QString termtype_; + enum FQTermSSH2ChannelState { + BEGIN_CHANNEL, REQUEST_PTY_SENT, REQUEST_SHELL_SENT, CHANNEL_OK + } channel_state_; + + u_int32_t channel_id_; + + enum {MAX_LOCAL_WINDOW_SIZE = 0x100000, MAX_LOCAL_PACKET_SIZE = 0x4000}; + + u_int32_t local_window_size_; + + u_int32_t server_channel_id_; + u_int32_t server_window_size_; // connection packet window size; + u_int32_t server_max_packet_size_; // max size of each packet sent to server. + + void requestPty(); + void requestShell(); + void processChannelPacket(); + void checkLocalWindowSize(); + +public: + FQTermSSH2Channel(); + virtual void initChannel(FQTermSSHPacketReceiver *packet, FQTermSSHPacketSender *output, + int col, int row, const QString& termtype); + virtual void closeConnection(const char *reason); + virtual void changeTermSize(int col, int row); + virtual void sendData(const char *data, int len); + +public slots: + virtual void handlePacket(int type); +signals: + void channelReadyRead(const char *data, int len); +}; + +} // namespace FQTerm + +#endif // FQTERM_SSH_CHANNEL_H diff --git a/src/protocol/internal/fqterm_ssh_cipher.h b/src/protocol/internal/fqterm_ssh_cipher.h new file mode 100644 index 0000000..13bc9f3 --- /dev/null +++ b/src/protocol/internal/fqterm_ssh_cipher.h @@ -0,0 +1,58 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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. * + ***************************************************************************/ + +#ifndef FQTERM_SSH_CIPHER_H +#define FQTERM_SSH_CIPHER_H + +#include + +#include "fqterm_ssh_types.h" +#include "fqterm_ssh_const.h" + +namespace FQTerm { + +class FQTermSSHCipher { +protected: + const char *d_name; + +public: + FQTermSSHCipher() { + d_name = NULL; + } + + virtual ~FQTermSSHCipher(){} + + const char *name() const { + return d_name; + } + + virtual int blockSize() const = 0; + virtual int getKeySize() const = 0; + virtual int getIVSize() const = 0; + + virtual void setIV(const u_char *data) = 0; + virtual void setKey(const u_char *data) = 0; + virtual void encrypt(const u_char *source, u_char *dest, int len) = 0; + virtual void decrypt(const u_char *source, u_char *dest, int len) = 0; +}; + +} // namespace FQTerm + +#endif // FQTERM_SSH_CIPHER_H diff --git a/src/protocol/internal/fqterm_ssh_const.h b/src/protocol/internal/fqterm_ssh_const.h new file mode 100644 index 0000000..412760e --- /dev/null +++ b/src/protocol/internal/fqterm_ssh_const.h @@ -0,0 +1,146 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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. * + ***************************************************************************/ + +#ifndef FQTERMCONST_H +#define FQTERMCONST_H + +namespace FQTerm { + +#define SSH_CIPHER_SSH2 -3 +#define SSH_CIPHER_ILLEGAL -2 +#define SSH_CIPHER_NOT_SET -1 +#define SSH_CIPHER_NONE 0 +#define SSH_CIPHER_IDEA 1 +#define SSH_CIPHER_DES 2 +#define SSH_CIPHER_3DES 3 +#define SSH_CIPHER_BROKEN_TSS 4 +#define SSH_CIPHER_BROKEN_RC4 5 +#define SSH_CIPHER_BLOWFISH 6 +#define SSH_CIPHER_RESERVED 7 +#define SSH_CIPHER_MAX 31 + +#define SSH1_MSG_DISCONNECT 1 +#define SSH1_SMSG_PUBLIC_KEY 2 +#define SSH1_CMSG_SESSION_KEY 3 +#define SSH1_CMSG_USER 4 +#define SSH1_CMSG_AUTH_RSA 6 +#define SSH1_SMSG_AUTH_RSA_CHALLENGE 7 +#define SSH1_CMSG_AUTH_RSA_RESPONSE 8 +#define SSH1_CMSG_AUTH_PASSWORD 9 +#define SSH1_CMSG_REQUEST_PTY 10 +#define SSH1_CMSG_WINDOW_SIZE 11 +#define SSH1_CMSG_EXEC_SHELL 12 +#define SSH1_CMSG_EXEC_CMD 13 +#define SSH1_SMSG_SUCCESS 14 +#define SSH1_SMSG_FAILURE 15 +#define SSH1_CMSG_STDIN_DATA 16 +#define SSH1_SMSG_STDOUT_DATA 17 +#define SSH1_SMSG_STDERR_DATA 18 +#define SSH1_CMSG_EOF 19 +#define SSH1_SMSG_EXIT_STATUS 20 +#define SSH1_MSG_CHANNEL_OPEN_CONFIRMATION 21 +#define SSH1_MSG_CHANNEL_OPEN_FAILURE 22 +#define SSH1_MSG_CHANNEL_DATA 23 +#define SSH1_MSG_CHANNEL_CLOSE 24 +#define SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION 25 +#define SSH1_SMSG_X11_OPEN 27 +#define SSH1_CMSG_PORT_FORWARD_REQUEST 28 +#define SSH1_MSG_PORT_OPEN 29 +#define SSH1_CMSG_AGENT_REQUEST_FORWARDING 30 +#define SSH1_SMSG_AGENT_OPEN 31 +#define SSH1_MSG_IGNORE 32 +#define SSH1_CMSG_EXIT_CONFIRMATION 33 +#define SSH1_CMSG_X11_REQUEST_FORWARDING 34 +#define SSH1_CMSG_AUTH_RHOSTS_RSA 35 +#define SSH1_MSG_DEBUG 36 +#define SSH1_CMSG_REQUEST_COMPRESSION 37 +#define SSH1_CMSG_AUTH_TIS 39 +#define SSH1_SMSG_AUTH_TIS_CHALLENGE 40 +#define SSH1_CMSG_AUTH_TIS_RESPONSE 41 +#define SSH1_CMSG_AUTH_CCARD 70 +#define SSH1_SMSG_AUTH_CCARD_CHALLENGE 71 +#define SSH1_CMSG_AUTH_CCARD_RESPONSE 72 +#define SSH1_AUTH_TIS 5 +#define SSH1_PROTOFLAG_SCREEN_NUMBER 1 +#define SSH1_PROTOFLAGS_SUPPORTED 0 + + +#define SSH2_MSG_DISCONNECT 1 +#define SSH2_MSG_IGNORE 2 +#define SSH2_MSG_UNIMPLEMENTED 3 +#define SSH2_MSG_DEBUG 4 +#define SSH2_MSG_SERVICE_REQUEST 5 +#define SSH2_MSG_SERVICE_ACCEPT 6 +#define SSH2_MSG_KEXINIT 20 +#define SSH2_MSG_NEWKEYS 21 +#define SSH2_MSG_KEXDH_INIT 30 +#define SSH2_MSG_KEXDH_REPLY 31 +#define SSH2_MSG_KEX_DH_GEX_REQUEST_OLD 30 +#define SSH2_MSG_KEX_DH_GEX_GROUP 31 +#define SSH2_MSG_KEX_DH_GEX_INIT 32 +#define SSH2_MSG_KEX_DH_GEX_REPLY 33 +#define SSH2_MSG_KEX_DH_GEX_REQUEST 34 +#define SSH2_MSG_USERAUTH_REQUEST 50 +#define SSH2_MSG_USERAUTH_FAILURE 51 +#define SSH2_MSG_USERAUTH_SUCCESS 52 +#define SSH2_MSG_USERAUTH_BANNER 53 +#define SSH2_MSG_USERAUTH_PK_OK 60 +#define SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 60 +#define SSH2_MSG_USERAUTH_INFO_REQUEST 60 +#define SSH2_MSG_USERAUTH_INFO_RESPONSE 61 +#define SSH2_MSG_GLOBAL_REQUEST 80 +#define SSH2_MSG_REQUEST_SUCCESS 81 +#define SSH2_MSG_REQUEST_FAILURE 82 +#define SSH2_MSG_CHANNEL_OPEN 90 +#define SSH2_MSG_CHANNEL_OPEN_CONFIRMATION 91 +#define SSH2_MSG_CHANNEL_OPEN_FAILURE 92 +#define SSH2_MSG_CHANNEL_WINDOW_ADJUST 93 +#define SSH2_MSG_CHANNEL_DATA 94 +#define SSH2_MSG_CHANNEL_EXTENDED_DATA 95 +#define SSH2_MSG_CHANNEL_EOF 96 +#define SSH2_MSG_CHANNEL_CLOSE 97 +#define SSH2_MSG_CHANNEL_REQUEST 98 +#define SSH2_MSG_CHANNEL_SUCCESS 99 +#define SSH2_MSG_CHANNEL_FAILURE 100 +#define SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT 1 +#define SSH2_DISCONNECT_PROTOCOL_ERROR 2 +#define SSH2_DISCONNECT_KEY_EXCHANGE_FAILED 3 +#define SSH2_DISCONNECT_HOST_AUTHENTICATION_FAILED 4 +#define SSH2_DISCONNECT_RESERVED 4 +#define SSH2_DISCONNECT_MAC_ERROR 5 +#define SSH2_DISCONNECT_COMPRESSION_ERROR 6 +#define SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE 7 +#define SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED 8 +#define SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE 9 +#define SSH2_DISCONNECT_CONNECTION_LOST 10 +#define SSH2_DISCONNECT_BY_APPLICATION 11 +#define SSH2_DISCONNECT_TOO_MANY_CONNECTIONS 12 +#define SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER 13 +#define SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE 14 +#define SSH2_DISCONNECT_ILLEGAL_USER_NAME 15 +#define SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED 1 +#define SSH2_OPEN_CONNECT_FAILED 2 +#define SSH2_OPEN_UNKNOWN_CHANNEL_TYPE 3 +#define SSH2_OPEN_RESOURCE_SHORTAGE 4 +#define SSH2_EXTENDED_DATA_STDERR 1 + +} // namespace FQTerm + +#endif // FQTERMCONST_H diff --git a/src/protocol/internal/fqterm_ssh_des.cpp b/src/protocol/internal/fqterm_ssh_des.cpp new file mode 100644 index 0000000..603b6fe --- /dev/null +++ b/src/protocol/internal/fqterm_ssh_des.cpp @@ -0,0 +1,180 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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 +#include +#include +#include + +#include "fqterm_ssh_const.h" +#include "fqterm_ssh_des.h" +#include "fqterm_trace.h" + +namespace FQTerm { + +#define DES_ENCRYPT 1 +#define DES_DECRYPT 0 + +//============================================================================== +//FQTermSSH1DES3 +//============================================================================== + +FQTermSSH1DES3::FQTermSSH1DES3() { + d_name = "des3-cbc"; + + memset(d_IV1, 0, sizeof(d_IV1)); + memset(d_IV2, 0, sizeof(d_IV2)); + memset(d_IV3, 0, sizeof(d_IV3)); +} + +int FQTermSSH1DES3::blockSize() const { + return 8; +} + +void FQTermSSH1DES3::decrypt(const u_char *source, u_char *dest, int len) { + DES_ncbc_encrypt(source, dest, len, &d_key3, &d_IV3, DES_DECRYPT); + DES_ncbc_encrypt(dest, dest, len, &d_key2, &d_IV2, DES_ENCRYPT); + DES_ncbc_encrypt(dest, dest, len, &d_key1, &d_IV1, DES_DECRYPT); +} + +void FQTermSSH1DES3::encrypt(const u_char *source, u_char *dest, int len) { + DES_ncbc_encrypt(source, dest, len, &d_key1, &d_IV1, DES_ENCRYPT); + DES_ncbc_encrypt(dest, dest, len, &d_key2, &d_IV2, DES_DECRYPT); + DES_ncbc_encrypt(dest, dest, len, &d_key3, &d_IV3, DES_ENCRYPT); +} + +int FQTermSSH1DES3::getKeySize() const { + return 3*DES_KEY_SZ; +} + +int FQTermSSH1DES3::getIVSize() const { + return 0; +} + +void FQTermSSH1DES3::setIV(const u_char *data) { + memset(d_IV1, 0, sizeof(d_IV1)); + memset(d_IV2, 0, sizeof(d_IV2)); + memset(d_IV3, 0, sizeof(d_IV3)); +} + +void FQTermSSH1DES3::setKey(const u_char *data) { + DES_cblock key; + memset(key, 0, sizeof(key)); + memcpy(key, data, sizeof(key)); + DES_set_key(&key, &d_key1); + data += 8; + memset(key, 0, sizeof(key)); + memcpy(key, data, sizeof(key)); + DES_set_key(&key, &d_key2); + data += 8; + memset(key, 0, sizeof(key)); + memcpy(key, data, sizeof(key)); + DES_set_key(&key, &d_key3); +} + + +//================================================== +// TripleDES-CBC +//================================================== + +FQTermSSH2TripleDESCBC::FQTermSSH2TripleDESCBC() { + d_name = "des3-cbc"; + ctx_ = NULL; +} + +FQTermSSH2TripleDESCBC::~FQTermSSH2TripleDESCBC() { + if (ctx_ != NULL) { + EVP_CIPHER_CTX_cleanup(ctx_); + delete ctx_; + } +} + + +int FQTermSSH2TripleDESCBC::blockSize() const { + return 8; +} + +int FQTermSSH2TripleDESCBC::getKeySize() const { + return 24; +} + +int FQTermSSH2TripleDESCBC::getIVSize() const { + return 8; +} + +void FQTermSSH2TripleDESCBC::setIV(const u_char *data) { + memcpy(IV_, data, getIVSize()); +} + +void FQTermSSH2TripleDESCBC::setKey(const u_char *data) { + memcpy(key_, data, getKeySize()); +} + +void FQTermSSH2TripleDESCBC::encrypt(const u_char *source, u_char *dest, int len) { + FQ_TRACE("3DES_CBC", 9) << "Start encrypting"; + FQ_TRACE("3DES_CBC", 9) << "data len:" << len; + FQ_TRACE("3DES_CBC", 9) << "Source: \n" << dumpHexString << std::string((char *)source, len); + + int ret = 0; + if (ctx_ == NULL) { + // Lazy initialization. + ctx_ = new EVP_CIPHER_CTX; + EVP_CIPHER_CTX_init(ctx_); + ret = EVP_CipherInit(ctx_, EVP_des_ede3_cbc(), key_, IV_, 1); + FQ_VERIFY(ret == 1); + } + + ret = EVP_Cipher(ctx_, dest, source, len); + + FQ_VERIFY(ret == 1); + + FQ_TRACE("3DES_CBC", 9) << "Dest: \n" << dumpHexString << std::string((char *)dest, len); + + FQ_TRACE("3DES_CBC", 9) << "IV:\n" << dumpHexString << std::string((char *)IV_, getIVSize()); + FQ_TRACE("3DES_CBC", 9) << "key:\n" << dumpHexString << std::string((char *)key_, getKeySize()); + +} + +void FQTermSSH2TripleDESCBC::decrypt(const u_char *source, u_char *dest, int len) { + FQ_TRACE("3DES_CBC", 9) << "Start dencrypting"; + FQ_TRACE("3DES_CBC", 9) << "data len:" << len; + FQ_TRACE("3DES_CBC", 9) << "Source: \n" << dumpHexString << std::string((char *)source, len); + + int ret = 0; + if (ctx_ == NULL) { + // Lazy initialization. + ctx_ = new EVP_CIPHER_CTX; + EVP_CIPHER_CTX_init(ctx_); + ret = EVP_CipherInit(ctx_, EVP_des_ede3_cbc(), key_, IV_, 0); + FQ_VERIFY(ret == 1); + } + + ret = EVP_Cipher(ctx_, dest, source, len); + + FQ_VERIFY(ret == 1); + + FQ_TRACE("3DES_CBC", 9) << "Dest: \n" << dumpHexString << std::string((char *)dest, len); + + FQ_TRACE("3DES_CBC", 9) << "IV:\n" << dumpHexString << std::string((char *)IV_, getIVSize()); + FQ_TRACE("3DES_CBC", 9) << "key:\n" << dumpHexString << std::string((char *)key_, getKeySize()); +} + + +} // namespace FQTerm diff --git a/src/protocol/internal/fqterm_ssh_des.h b/src/protocol/internal/fqterm_ssh_des.h new file mode 100644 index 0000000..ba153f2 --- /dev/null +++ b/src/protocol/internal/fqterm_ssh_des.h @@ -0,0 +1,74 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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. * + ***************************************************************************/ + +#ifndef FQTERM_SSH_DES_H +#define FQTERM_SSH_DES_H + +#include +#include + +#include "fqterm_ssh_cipher.h" +#include "fqterm_ssh_types.h" + +namespace FQTerm { + +class FQTermSSH1DES3: public FQTermSSHCipher { + private: + DES_cblock d_IV1; + DES_cblock d_IV2; + DES_cblock d_IV3; + DES_key_schedule d_key1; + DES_key_schedule d_key2; + DES_key_schedule d_key3; + public: + FQTermSSH1DES3(); + + virtual int blockSize() const; + virtual int getKeySize() const; + virtual int getIVSize() const; + virtual void setIV(const u_char *data); + virtual void setKey(const u_char *data); + virtual void encrypt(const u_char *source, u_char *dest, int len); + virtual void decrypt(const u_char *source, u_char *dest, int len); +}; + +class FQTermSSH2TripleDESCBC: public FQTermSSHCipher { + private: + unsigned char IV_[8]; + unsigned char key_[24]; + + EVP_CIPHER_CTX *ctx_; + + public: + FQTermSSH2TripleDESCBC(); + ~FQTermSSH2TripleDESCBC(); + + virtual int blockSize() const; + virtual int getKeySize() const; + virtual int getIVSize() const; + virtual void setIV(const u_char *data); + virtual void setKey(const u_char *data); + virtual void encrypt(const u_char *source, u_char *dest, int len); + virtual void decrypt(const u_char *source, u_char *dest, int len); +}; + +} // namespace FQTerm + +#endif // FQTERM_SSH_DES_H diff --git a/src/protocol/internal/fqterm_ssh_hash.h b/src/protocol/internal/fqterm_ssh_hash.h new file mode 100644 index 0000000..b4679f4 --- /dev/null +++ b/src/protocol/internal/fqterm_ssh_hash.h @@ -0,0 +1,47 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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. * + ***************************************************************************/ + +#ifndef FQTERM_SSH_HASH_H +#define FQTERM_SSH_HASH_H + +#include "fqterm_ssh_types.h" + +namespace FQTerm { + +class FQTermSSHHash { +protected: + int d_digestLength; + int digestLength()const { + return d_digestLength; + } + +public: + FQTermSSHHash() { + d_digestLength = 0; + } + virtual ~FQTermSSHHash() {} + + virtual void update(u_char *data, int len) = 0; + virtual void final(u_char *data) = 0; +}; + +} // namespace FQTerm + +#endif //FQTERM_SSH_HASH_H diff --git a/src/protocol/internal/fqterm_ssh_kex.cpp b/src/protocol/internal/fqterm_ssh_kex.cpp new file mode 100644 index 0000000..d27bf14 --- /dev/null +++ b/src/protocol/internal/fqterm_ssh_kex.cpp @@ -0,0 +1,210 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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 "fqterm_ssh_kex.h" +#include "fqterm_ssh_md5.h" +#include "fqterm_trace.h" + +namespace FQTerm { + +FQTermSSHKex::FQTermSSHKex(const char *V_C, const char *V_S) { + V_C_ = new char[strlen(V_C) + 1]; + V_S_ = new char[strlen(V_S) + 1]; + + strcpy(V_C_, V_C); + strcpy(V_S_, V_S); +} + +FQTermSSHKex::~FQTermSSHKex() { + delete[] V_C_; + delete[] V_S_; +} + + +//============================================================================== +//FQTermSSH1Kex +//============================================================================== + +FQTermSSH1Kex::FQTermSSH1Kex(const char *V_C, const char *V_S) + : FQTermSSHKex(V_C, V_S) { + is_first_kex_ = true; + kex_state_ = FQTermSSH1Kex::BEFORE_PUBLICKEY; +} + +FQTermSSH1Kex::~FQTermSSH1Kex(){} + +void FQTermSSH1Kex::initKex(FQTermSSHPacketReceiver *packetReceiver, + FQTermSSHPacketSender *packetSender) { + packet_receiver_ = packetReceiver; + packet_sender_ = packetSender; + packet_receiver_->disconnect(this); + FQ_VERIFY(connect(packet_receiver_, SIGNAL(packetAvaliable(int)), + this, SLOT(handlePacket(int)))); + kex_state_ = FQTermSSH1Kex::BEFORE_PUBLICKEY; + emit reKex(); +} + +void FQTermSSH1Kex::handlePacket(int type) { + switch (kex_state_) { + case FQTermSSH1Kex::BEFORE_PUBLICKEY: + makeSessionKey(); + kex_state_ = FQTermSSH1Kex::SESSIONKEY_SENT; + break; + case FQTermSSH1Kex::SESSIONKEY_SENT: + if (type != SSH1_SMSG_SUCCESS) { + emit kexError(tr("Kex exchange failed!")); + break; + } + emit kexOK(); + kex_state_ = FQTermSSH1Kex::KEYEX_OK; + break; + case FQTermSSH1Kex::KEYEX_OK: + break; + } +} + +void FQTermSSH1Kex::makeSessionKey() { + int i; + BIGNUM *key; + u_int32_t rand_val; + int bits; + int rbits; + + if (packet_receiver_->packetType() != SSH1_SMSG_PUBLIC_KEY) { + emit kexError(tr("startKex: First packet is not public key")); + return ; + } + packet_receiver_->getRawData((char*)cookie_, 8); + + // Get the public key. + server_key_ = new FQTermSSHRSA; + bits = packet_receiver_->getInt(); + packet_receiver_->getBN(server_key_->d_rsa->e); + packet_receiver_->getBN(server_key_->d_rsa->n); + + rbits = BN_num_bits(server_key_->d_rsa->n); + if (bits != rbits) { + FQ_TRACE("sshkex", 0) << "Warning: Server lies about " + << "size of server public key: " + << "actual size: " << rbits + << " vs. anounced: " << bits; + FQ_TRACE("sshkex", 0) << "Warning: This may be due to " + << "an old implementation of ssh."; + } + + // Get the host key. + host_key_ = new FQTermSSHRSA; + bits = packet_receiver_->getInt(); + packet_receiver_->getBN(host_key_->d_rsa->e); + packet_receiver_->getBN(host_key_->d_rsa->n); + + rbits = BN_num_bits(host_key_->d_rsa->n); + if (bits != rbits) { + FQ_TRACE("sshkex", 0) << "Warning: Server lies about " + << "size of server public key: " + << "actual size: " << rbits + << " vs. anounced: " << bits; + FQ_TRACE("sshkex", 0) << "Warning: This may be due to " + << "an old implementation of ssh."; + } + + // Get protocol flags. + server_flag_ = packet_receiver_->getInt(); + ciphers_ = packet_receiver_->getInt(); + auth_ = packet_receiver_->getInt(); + + if ((ciphers_ &(1 << SSH_CIPHER_3DES)) == 0) { + FQ_VERIFY(false); // server do not support my cipher + } + + makeSessionId(); + + // Generate an encryption key for the session. The key is a 256 bit + // random number, interpreted as a 32-byte key, with the least + // significant 8 bits being the first byte of the key. + + for (i = 0; i < 32; i++) { + if (i % 4 == 0) { + rand_val = rand(); + } + + session_key_[i] = (rand_val &0xff); + rand_val >>= 8; + } + + key = BN_new(); + + BN_set_word(key, 0); + for (i = 0; i < 32; i++) { + BN_lshift(key, key, 8); + if (i < 16) { + BN_add_word(key, session_key_[i] ^ session_id_[i]); + } else { + BN_add_word(key, session_key_[i]); + } + } + + if (BN_cmp(server_key_->d_rsa->n, host_key_->d_rsa->n) < 0) { + server_key_->publicEncrypt(key, key); + host_key_->publicEncrypt(key, key); + } else { + host_key_->publicEncrypt(key, key); + server_key_->publicEncrypt(key, key); + } + + delete host_key_; + delete server_key_; + + packet_sender_->startPacket(SSH1_CMSG_SESSION_KEY); + packet_sender_->putByte(SSH_CIPHER_3DES); + packet_sender_->putRawData((const char*)cookie_, 8); + packet_sender_->putBN(key); + + BN_free(key); + + packet_sender_->putInt(1); + packet_sender_->write(); + + emit startEncryption(session_key_); +} + +void FQTermSSH1Kex::makeSessionId() { + u_char *p; + FQTermSSHMD5 *md5; + int servlen, hostlen; + + md5 = new FQTermSSHMD5; + servlen = BN_num_bytes(server_key_->d_rsa->n); + hostlen = BN_num_bytes(host_key_->d_rsa->n); + + p = new u_char[servlen + hostlen]; + + BN_bn2bin(host_key_->d_rsa->n, p); + BN_bn2bin(server_key_->d_rsa->n, p + hostlen); + md5->update(p, servlen + hostlen); + md5->update(cookie_, 8); + md5->final(session_id_); + delete md5; + delete [] p; +} + +} // namespace FQTerm + +#include "fqterm_ssh_kex.moc" diff --git a/src/protocol/internal/fqterm_ssh_kex.h b/src/protocol/internal/fqterm_ssh_kex.h new file mode 100644 index 0000000..b2f9c56 --- /dev/null +++ b/src/protocol/internal/fqterm_ssh_kex.h @@ -0,0 +1,100 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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. * + ***************************************************************************/ + +#ifndef FQTERM_SSH_KEX_H +#define FQTERM_SSH_KEX_H + +#include +#include + +#include "fqterm_ssh_types.h" +#include "fqterm_ssh_rsa.h" +#include "fqterm_ssh_packet.h" +#include "fqterm_ssh_const.h" + +namespace FQTerm { + +class FQTermSSHPacketReceiver; +class FQTermSSHPacketSender; + +class FQTermSSHKex: public QObject { + Q_OBJECT; +protected: + FQTermSSHPacketReceiver *packet_receiver_; + FQTermSSHPacketSender *packet_sender_; + + char *V_C_; + char *V_S_; +public: + // Init with + // string V_C, the client's identification string (CR and LF + // excluded) + // string V_S, the server's identification string (CR and LF + // excluded) + FQTermSSHKex(const char *V_C, const char *V_S); + virtual ~FQTermSSHKex(); + + virtual void initKex(FQTermSSHPacketReceiver *packetReceiver, + FQTermSSHPacketSender *outputSender) = 0; + +public slots: + virtual void handlePacket(int type) = 0; + +signals: + void kexOK(); + void reKex(); + void kexError(QString); + + void startEncryption(const u_char *sessionkey); + +}; + +class FQTermSSH1Kex: public FQTermSSHKex { + Q_OBJECT; +private: + enum FQTermSSH1KexState { + BEFORE_PUBLICKEY, SESSIONKEY_SENT, KEYEX_OK + } kex_state_; + + bool is_first_kex_; + + FQTermSSHRSA *host_key_; + FQTermSSHRSA *server_key_; + + u_char cookie_[8]; + int server_flag_, ciphers_, auth_; + u_char session_id_[16]; + u_char session_key_[32]; + + void makeSessionId(); + void makeSessionKey(); + +public: + FQTermSSH1Kex(const char *V_C, const char *V_S); + ~FQTermSSH1Kex(); + virtual void initKex(FQTermSSHPacketReceiver *packetReceiver, + FQTermSSHPacketSender *outputSender); + + virtual void handlePacket(int type); +}; + +} // namespace FQTerm + +#endif //FQTERM_SSH_KEX_H diff --git a/src/protocol/internal/fqterm_ssh_mac.cpp b/src/protocol/internal/fqterm_ssh_mac.cpp new file mode 100644 index 0000000..e2d345f --- /dev/null +++ b/src/protocol/internal/fqterm_ssh_mac.cpp @@ -0,0 +1,53 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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 +#include + +#include "fqterm_trace.h" +#include "fqterm_ssh_mac.h" +#include "fqterm_ssh_packet.h" + +namespace FQTerm { + +void FQTermSSHSHA1::setKey(const unsigned char *key) { + memcpy(key_, key, keySize()); +} + +int FQTermSSHSHA1::keySize() const { + return SHA_DIGEST_LENGTH; +} + +int FQTermSSHSHA1::digestSize() const { + return SHA_DIGEST_LENGTH; +} + +void FQTermSSHSHA1::getDigest(const unsigned char *data, int len, unsigned char *digest) const { + unsigned int tmp; + HMAC(EVP_sha1(), key_, SHA_DIGEST_LENGTH, data, len, digest, &tmp); + + FQ_TRACE("SHA1", 9) << "Key: \n" << dumpHexString << std::string((char *)key_, SHA_DIGEST_LENGTH); + FQ_TRACE("SHA1", 9) << "data len:" << len; + FQ_TRACE("SHA1", 9) << "data:\n" << dumpHexString << std::string((char *)data, len); + FQ_TRACE("SHA1", 9) << "digest len:" << tmp; + FQ_TRACE("SHA1", 9) << "digest:\n" << dumpHexString << std::string((char *)digest, tmp); +} + +} // namespace FQTerm diff --git a/src/protocol/internal/fqterm_ssh_mac.h b/src/protocol/internal/fqterm_ssh_mac.h new file mode 100644 index 0000000..545909c --- /dev/null +++ b/src/protocol/internal/fqterm_ssh_mac.h @@ -0,0 +1,54 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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. * + ***************************************************************************/ + +#ifndef FQTERM_SSH_MAC_H +#define FQTERM_SSH_MAC_H + +#include + +namespace FQTerm { + +class FQTermSSHMac { +public: + FQTermSSHMac() {} + virtual ~FQTermSSHMac() {} + virtual void setKey(const unsigned char *key) = 0; + virtual int keySize() const = 0; + virtual int digestSize() const = 0; + virtual void getDigest(const unsigned char *data, int len, unsigned char *digest) const = 0; +}; + +const int FQTERM_SSH_MAC_NONE = 0; +const int FQTERM_SSH_HMAC_SHA1 = 1; + +class FQTermSSHSHA1: public FQTermSSHMac { + unsigned char key_[SHA_DIGEST_LENGTH]; + +public: + virtual void setKey(const unsigned char *key); + virtual int keySize() const; + virtual int digestSize() const; + virtual void getDigest(const unsigned char *data, int len, unsigned char *digest) const; +}; + + +} // namespace FQTerm + +#endif // FQTERM_SSH_MAC_H diff --git a/src/protocol/internal/fqterm_ssh_md5.cpp b/src/protocol/internal/fqterm_ssh_md5.cpp new file mode 100644 index 0000000..10713e9 --- /dev/null +++ b/src/protocol/internal/fqterm_ssh_md5.cpp @@ -0,0 +1,36 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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 "fqterm_ssh_md5.h" + +namespace FQTerm { + +//============================================================================== +//FQTermSSHMD5 +//============================================================================== +void FQTermSSHMD5::final(u_char *digest) { + MD5_Final(digest, &d_md5); +} + +void FQTermSSHMD5::update(u_char *data, int len) { + MD5_Update(&d_md5, data, len); +} + +} // namespace FQTerm diff --git a/src/protocol/internal/fqterm_ssh_md5.h b/src/protocol/internal/fqterm_ssh_md5.h new file mode 100644 index 0000000..abb8127 --- /dev/null +++ b/src/protocol/internal/fqterm_ssh_md5.h @@ -0,0 +1,50 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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. * + ***************************************************************************/ + +#ifndef FQTERM_SSH_MD5_H +#define FQTERM_SSH_MD5_H + +#include + +#include + +#include "fqterm_ssh_types.h" +#include "fqterm_ssh_hash.h" + +namespace FQTerm { + +class FQTermSSHMD5: public FQTermSSHHash { +protected: + MD5_CTX d_md5; +public: + FQTermSSHMD5() + : FQTermSSHHash() { + d_digestLength = 16; + MD5_Init(&d_md5); + } + virtual ~FQTermSSHMD5() {} + + void update(u_char *data, int len); + void final(u_char *digest); +}; + +} // namespace FQTerm + +#endif //FQTERM_SSH_MD5_H diff --git a/src/protocol/internal/fqterm_ssh_packet.cpp b/src/protocol/internal/fqterm_ssh_packet.cpp new file mode 100644 index 0000000..9c4ff6c --- /dev/null +++ b/src/protocol/internal/fqterm_ssh_packet.cpp @@ -0,0 +1,242 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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 "fqterm_trace.h" +#include "fqterm_ssh_buffer.h" +#include "fqterm_ssh_packet.h" +#include "fqterm_ssh_des.h" + +#include "fqterm_serialization.h" +#include "crc32.h" + +namespace FQTerm { + +//============================================================================== +//FQTermSSHPacketSender +//============================================================================== + +FQTermSSHPacketSender::FQTermSSHPacketSender() { + buffer_ = new FQTermSSHBuffer(1024); + output_buffer_ = new FQTermSSHBuffer(1024); + + is_encrypt_ = false; + cipher_type_ = SSH_CIPHER_NONE; + cipher_ = NULL; + setEncryptionType(SSH_CIPHER_3DES); + + is_mac_ = false; + mac_type_ = FQTERM_SSH_MAC_NONE; + mac_ = NULL; + + is_compressed_ = false; + + sequence_no_ = 0; +} + +FQTermSSHPacketSender::~FQTermSSHPacketSender() { + delete buffer_; + delete output_buffer_; + if (is_encrypt_) { + delete cipher_; + } +} + +void FQTermSSHPacketSender::putRawData(const char *data, int len) { + buffer_->putRawData(data, len); +} + +void FQTermSSHPacketSender::putByte(int data) { + buffer_->putByte(data); +} + +void FQTermSSHPacketSender::putInt(u_int data) { + buffer_->putInt(data); +} + +void FQTermSSHPacketSender::putString(const char *string, int len) { + buffer_->putString(string, len); +} + +void FQTermSSHPacketSender::putBN(BIGNUM *bn) { + buffer_->putSSH1BN(bn); +} + +void FQTermSSHPacketSender::putBN2(BIGNUM *bn) { + buffer_->putSSH2BN(bn); +} + +void FQTermSSHPacketSender::startPacket(int pkt_type) { + buffer_->clear(); + buffer_->putByte(pkt_type); +} + +void FQTermSSHPacketSender::write() { + makePacket(); + emit dataToWrite(); +} + +void FQTermSSHPacketSender::setEncryptionType(int cipherType) { + cipher_type_ = cipherType; + + delete cipher_; + cipher_ = NULL; + + switch (cipher_type_) { + case SSH_CIPHER_3DES: + cipher_ = new FQTermSSH1DES3; + break; + } +} + +void FQTermSSHPacketSender::startEncryption(const u_char *key, const u_char *IV) { + is_encrypt_ = true; + cipher_->setIV(IV); + cipher_->setKey(key); +} + +void FQTermSSHPacketSender::resetEncryption() { + is_encrypt_ = false; +} + +void FQTermSSHPacketSender::setMacType(int macType) { + mac_type_ = macType; + + delete mac_; + mac_ = NULL; + + switch (mac_type_) { + case FQTERM_SSH_HMAC_SHA1: + mac_ = new FQTermSSHSHA1; + break; + } +} + +void FQTermSSHPacketSender::startMac(const u_char *key) { + is_mac_ = true; + mac_->setKey(key); +} + +void FQTermSSHPacketSender::resetMac() { + is_mac_ = false; +} + +//============================================================================== +//FQTermSSHPacketReceiver +//============================================================================== + +FQTermSSHPacketReceiver::FQTermSSHPacketReceiver() { + buffer_ = new FQTermSSHBuffer(1024); + + is_decrypt_ = false; + cipher_type_ = SSH_CIPHER_NONE; + cipher_ = NULL; + setEncryptionType(SSH_CIPHER_3DES); + + is_mac_ = false; + mac_type_ = FQTERM_SSH_MAC_NONE; + mac_ = NULL; + + is_compressed_ = false; + + sequence_no_ = 0; +} + +FQTermSSHPacketReceiver::~FQTermSSHPacketReceiver() { + delete buffer_; + if (is_decrypt_) { + delete cipher_; + } +} + +void FQTermSSHPacketReceiver::getRawData(char *data, int length) { + buffer_->getRawData(data, length); +} + +u_char FQTermSSHPacketReceiver::getByte() { + return buffer_->getByte(); +} + +u_int FQTermSSHPacketReceiver::getInt() { + return buffer_->getInt(); +} + +void *FQTermSSHPacketReceiver::getString(int *length) { + return buffer_->getString(length); +} + +void FQTermSSHPacketReceiver::getBN(BIGNUM *bignum) { + buffer_->getSSH1BN(bignum); +} + +void FQTermSSHPacketReceiver::getBN2(BIGNUM *bignum) { + buffer_->getSSH2BN(bignum); +} + +void FQTermSSHPacketReceiver::consume(int len) { + buffer_->consume(len); +} + +void FQTermSSHPacketReceiver::setEncryptionType(int cipherType) { + cipher_type_ = cipherType; + + delete cipher_; + cipher_ = NULL; + + switch (cipher_type_) { + case SSH_CIPHER_3DES: + cipher_ = new FQTermSSH1DES3; + break; + } +} + +void FQTermSSHPacketReceiver::startEncryption(const u_char *key, const u_char *IV) { + is_decrypt_ = true; + cipher_->setIV(IV); + cipher_->setKey(key); +} + +void FQTermSSHPacketReceiver::resetEncryption() { + is_decrypt_ = false; +} + +void FQTermSSHPacketReceiver::setMacType(int macType) { + mac_type_ = macType; + delete mac_; + mac_ = NULL; + + switch (mac_type_) { + case FQTERM_SSH_HMAC_SHA1: + mac_ = new FQTermSSHSHA1; + break; + } +} + +void FQTermSSHPacketReceiver::startMac(const u_char *key) { + is_mac_ = true; + mac_->setKey(key); +} + +void FQTermSSHPacketReceiver::resetMac() { + is_mac_ = false; +} + +} // namespace FQTerm + +#include "fqterm_ssh_packet.moc" diff --git a/src/protocol/internal/fqterm_ssh_packet.h b/src/protocol/internal/fqterm_ssh_packet.h new file mode 100644 index 0000000..ed5a488 --- /dev/null +++ b/src/protocol/internal/fqterm_ssh_packet.h @@ -0,0 +1,148 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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. * + ***************************************************************************/ + +#ifndef FQTERM_SSH_PACKET_H +#define FQTERM_SSH_PACKET_H + +#include + +#include + +#include "fqterm_ssh_types.h" +#include "fqterm_ssh_buffer.h" +#include "fqterm_ssh_mac.h" +#include "fqterm_ssh_cipher.h" +#include "fqterm_serialization.h" + +namespace FQTerm { + +class FQTermSSHPacketSender: public QObject { + Q_OBJECT; + public: + FQTermSSHBuffer *output_buffer_; + FQTermSSHBuffer *buffer_; + + FQTermSSHPacketSender(); + virtual ~FQTermSSHPacketSender(); + + void startPacket(int pkt_type); + void putByte(int data); + void putInt(u_int data); + void putString(const char *string, int len = -1); + void putRawData(const char *data, int length); + void putBN(BIGNUM *bignum); + void putBN2(BIGNUM *bignum); + void write(); + + int getIVSize() const { return cipher_->getIVSize();} + int getKeySize() const { return cipher_->getKeySize();} + int getMacKeySize() const { return mac_->keySize();} + + public slots: + virtual void setEncryptionType(int cipherType); + void startEncryption(const u_char *key, const u_char *IV = NULL); + void resetEncryption(); + + void setMacType(int macType); + void startMac(const u_char *sessionkey); + void resetMac(); + + void enableCompress(int enable) {is_compressed_ = enable;}; + + signals: + void dataToWrite(); + + protected: + bool is_encrypt_; + int cipher_type_; + FQTermSSHCipher *cipher_; + + bool is_mac_; + int mac_type_; + FQTermSSHMac *mac_; + + bool is_compressed_; + + u_int32_t sequence_no_; + + virtual void makePacket() = 0; +}; + +class FQTermSSHPacketReceiver: public QObject { + Q_OBJECT; + public: + FQTermSSHBuffer *buffer_; + + FQTermSSHPacketReceiver(); + virtual ~FQTermSSHPacketReceiver(); + + int packetType()const { + return packet_type_; + } + + u_char getByte(); + u_int getInt(); + void *getString(int *length = NULL); + void getRawData(char *data, int length); + void getBN(BIGNUM *bignum); + void getBN2(BIGNUM *bignum); + void consume(int len); + + virtual int packetDataLen() const { return real_data_len_;} + int getIVSize() const { return cipher_->getIVSize();} + int getKeySize() const { return cipher_->getKeySize();} + int getMacKeySize() const { return mac_->keySize();} + + virtual void parseData(FQTermSSHBuffer *input) = 0; + public slots: + virtual void setEncryptionType(int cipherType); + void startEncryption(const u_char *key, const u_char *IV = NULL); + void resetEncryption(); + + void setMacType(int macType); + void startMac(const u_char *sessionkey); + void resetMac(); + + void enableCompress(int enable) {is_compressed_ = enable;}; + + signals: + void packetAvaliable(int type); + void packetError(QString); + + protected: + bool is_decrypt_; + int cipher_type_; + FQTermSSHCipher *cipher_; + + bool is_mac_; + int mac_type_; + FQTermSSHMac *mac_; + + bool is_compressed_; + + int packet_type_; + int real_data_len_; + + u_int32_t sequence_no_; +}; + +} // namespace FQTerm + +#endif // FQTERM_SSH_PACKET diff --git a/src/protocol/internal/fqterm_ssh_pubkey.h b/src/protocol/internal/fqterm_ssh_pubkey.h new file mode 100644 index 0000000..0de957c --- /dev/null +++ b/src/protocol/internal/fqterm_ssh_pubkey.h @@ -0,0 +1,37 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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. * + ***************************************************************************/ + +#ifndef FQTERM_SSH_PUB_KEY_H +#define FQTERM_SSH_PUB_KEY_H + +#include + +namespace FQTerm { + +class FQTermSSHPubKey { +public: + FQTermSSHPubKey() {} + virtual ~FQTermSSHPubKey() {} + virtual void publicEncrypt(BIGNUM *, BIGNUM*) = 0; +}; + +} // namespace FQTerm + +#endif // FQTERM_SSH_PUB_KEY_H diff --git a/src/protocol/internal/fqterm_ssh_rsa.cpp b/src/protocol/internal/fqterm_ssh_rsa.cpp new file mode 100644 index 0000000..dd36898 --- /dev/null +++ b/src/protocol/internal/fqterm_ssh_rsa.cpp @@ -0,0 +1,67 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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 + +#include "fqterm_trace.h" +#include "fqterm_ssh_types.h" +#include "fqterm_ssh_rsa.h" + +namespace FQTerm { + +FQTermSSHRSA::FQTermSSHRSA() { + d_rsa = RSA_new(); + d_rsa->n = BN_new(); + d_rsa->e = BN_new(); +} + +FQTermSSHRSA::~FQTermSSHRSA() { + if (d_rsa != NULL) { + RSA_free(d_rsa); + } +} + +void FQTermSSHRSA::publicEncrypt(BIGNUM *out, BIGNUM *in) { + u_char *inbuf, *outbuf; + int len, ilen, olen; + + if (BN_num_bits(d_rsa->e) < 2 || !BN_is_odd(d_rsa->e)) { + FQ_VERIFY(false); // public_encrypt() exponent too small or not odd. + } + + olen = BN_num_bytes(d_rsa->n); + outbuf = new u_char[olen]; + + ilen = BN_num_bytes(in); + inbuf = new u_char[ilen]; + BN_bn2bin(in, inbuf); + + if ((len = RSA_public_encrypt(ilen, inbuf, outbuf, d_rsa, RSA_PKCS1_PADDING)) <= 0) { + //RSA_PKCS1_PADDING = 1 + FQ_VERIFY(false); // "rsa_public_encrypt() failed. + } + + BN_bin2bn(outbuf, len, out); + + delete [] outbuf; + delete [] inbuf; +} + +} // namespace FQTerm diff --git a/src/protocol/internal/fqterm_ssh_rsa.h b/src/protocol/internal/fqterm_ssh_rsa.h new file mode 100644 index 0000000..bfe89ce --- /dev/null +++ b/src/protocol/internal/fqterm_ssh_rsa.h @@ -0,0 +1,41 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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. * + ***************************************************************************/ + +#ifndef FQTERM_SSH_RSA_H +#define FQTERM_SSH_RSA_H + +#include + +#include "fqterm_ssh_pubkey.h" + +namespace FQTerm { + +class FQTermSSHRSA: public FQTermSSHPubKey { +public: + RSA *d_rsa; + FQTermSSHRSA(); + virtual ~FQTermSSHRSA(); + + virtual void publicEncrypt(BIGNUM *, BIGNUM*); +}; + +} // namespace FQTerm + +#endif // FQTERM_SSH_RSA_H diff --git a/src/protocol/internal/fqterm_ssh_types.h b/src/protocol/internal/fqterm_ssh_types.h new file mode 100644 index 0000000..c10bfac --- /dev/null +++ b/src/protocol/internal/fqterm_ssh_types.h @@ -0,0 +1,43 @@ +/*************************************************************************** + * fqterm, a terminal emulator for both BBS and *nix. * + * Copyright (C) 2008 fqterm development group. * + * * + * 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 2 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. * + ***************************************************************************/ + +#ifndef FQTERM_SSH_TYPES_H +#define FQTERM_SSH_TYPES_H + +#include +#if !defined(Q_OS_WIN32) && !defined(_OS_WIN32_) +#include +#else +typedef unsigned char u_char; +typedef unsigned short u_short; +typedef unsigned int u_int; +typedef unsigned long u_long; + +#ifdef QT3_SUPPORT +typedef Q_UINT32 u_int32_t; +typedef Q_UINT64 u_int64_t; +#else +typedef quint32 u_int32_t; +typedef quint64 u_int64_t; +#endif // QT3_SUPPORT + +#endif // Q_OS_WIN32 + +#endif // FQTERM_SSH_TYPES_H -- cgit v1.2.3