mirror of
https://git.libssh.org/projects/libssh.git
synced 2026-02-04 12:20:42 +09:00
Compare commits
30 Commits
1b3c061aae
...
libssh-0.9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79900e5246 | ||
|
|
63b0399373 | ||
|
|
39665fd9c5 | ||
|
|
83f0be1f04 | ||
|
|
3bc5f88f77 | ||
|
|
466ca07626 | ||
|
|
b6e757d692 | ||
|
|
3f2375e948 | ||
|
|
4d06c2f283 | ||
|
|
0298bfbbf0 | ||
|
|
2399a9f8de | ||
|
|
79756c5c56 | ||
|
|
e8510043d2 | ||
|
|
1f7889f271 | ||
|
|
89efd56217 | ||
|
|
e3fca31c59 | ||
|
|
d71a7976dd | ||
|
|
8fe8d13e29 | ||
|
|
722f979790 | ||
|
|
2c60ef04d9 | ||
|
|
ec486d13db | ||
|
|
ebfe46f6ad | ||
|
|
3c0897b975 | ||
|
|
993e0df81e | ||
|
|
551188d99b | ||
|
|
cafafe8f5a | ||
|
|
c6c7856b51 | ||
|
|
ea71af9c22 | ||
|
|
bb98413fc1 | ||
|
|
2a8cd81e8f |
@@ -6,7 +6,7 @@ variables:
|
||||
MINGW_BUILD: buildenv-mingw
|
||||
DEBIAN_CROSS_BUILD: buildenv-debian-cross
|
||||
|
||||
# torture_auth fails on centos7 docker images, so we don't use -DCLIENT_TESTING=ON
|
||||
# pkd tests fail on CentOS7 docker images, so we don't use -DSERVER_TESTING=ON
|
||||
centos7/openssl_1.0.x/x86_64:
|
||||
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS7_BUILD
|
||||
script:
|
||||
@@ -14,7 +14,7 @@ centos7/openssl_1.0.x/x86_64:
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||
-DPICKY_DEVELOPER=ON
|
||||
-DWITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON
|
||||
-DUNIT_TESTING=ON .. &&
|
||||
-DUNIT_TESTING=ON -DCLIENT_TESTING=ON .. &&
|
||||
make -j$(nproc) && ctest --output-on-failure
|
||||
tags:
|
||||
- shared
|
||||
|
||||
@@ -10,7 +10,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules")
|
||||
include(DefineCMakeDefaults)
|
||||
include(DefineCompilerFlags)
|
||||
|
||||
project(libssh VERSION 0.8.90 LANGUAGES C)
|
||||
project(libssh VERSION 0.9.0 LANGUAGES C)
|
||||
|
||||
# global needed variable
|
||||
set(APPLICATION_NAME ${PROJECT_NAME})
|
||||
|
||||
68
ChangeLog
68
ChangeLog
@@ -1,7 +1,7 @@
|
||||
ChangeLog
|
||||
==========
|
||||
|
||||
version 0.9.0 (released 2019-02-xx)
|
||||
version 0.9.0 (released 2019-06-28)
|
||||
* Added support for AES-GCM
|
||||
* Added improved rekeying support
|
||||
* Added performance improvements
|
||||
@@ -11,10 +11,76 @@ version 0.9.0 (released 2019-02-xx)
|
||||
* Added support for Encrypt-then-MAC mode
|
||||
* Added support for parsing server side configuration file
|
||||
* Added support for ECDSA/Ed25519 certificates
|
||||
* Added FIPS 140-2 compatibility
|
||||
* Improved known_hosts parsing
|
||||
* Improved documentation
|
||||
* Improved OpenSSL API usage for KEX, DH, and signatures
|
||||
|
||||
version 0.8.7 (released 2019-02-25)
|
||||
* Fixed handling extension flags in the server implementation
|
||||
* Fixed exporting ed25519 private keys
|
||||
* Fixed corner cases for rsa-sha2 signatures
|
||||
* Fixed some issues with connector
|
||||
|
||||
version 0.8.6 (released 2018-12-24)
|
||||
* Fixed compilation issues with different OpenSSL versions
|
||||
* Fixed StrictHostKeyChecking in new knownhosts API
|
||||
* Fixed ssh_send_keepalive() with packet filter
|
||||
* Fixed possible crash with knownhosts options
|
||||
* Fixed issus with rekeying
|
||||
* Fixed strong ECDSA keys
|
||||
* Fixed some issues with rsa-sha2 extentions
|
||||
* Fixed access violation in ssh_init() (static linking)
|
||||
* Fixed ssh_channel_close() handling
|
||||
|
||||
version 0.8.5 (released 2018-10-29)
|
||||
* Added support to get known_hosts locations with ssh_options_get()
|
||||
* Fixed preferred algorithm for known hosts negotiations
|
||||
* Fixed KEX with some server implementations (e.g. Cisco)
|
||||
* Fixed issues with MSVC
|
||||
* Fixed keyboard-interactive auth in server mode
|
||||
(regression from CVE-2018-10933)
|
||||
* Fixed gssapi auth in server mode (regression from CVE-2018-10933)
|
||||
* Fixed socket fd handling with proxy command
|
||||
* Fixed a memory leak with OpenSSL
|
||||
|
||||
version 0.8.4 (released 2018-10-16)
|
||||
* Fixed CVE-2018-10933
|
||||
* Fixed building without globbing support
|
||||
* Fixed possible memory leaks
|
||||
* Avoid SIGPIPE on sockets
|
||||
|
||||
version 0.8.3 (released 2018-09-21)
|
||||
* Added support for rsa-sha2
|
||||
* Added support to parse private keys in openssh container format
|
||||
(other than ed25519)
|
||||
* Added support for diffie-hellman-group18-sha512 and
|
||||
diffie-hellman-group16-sha512
|
||||
* Added ssh_get_fingerprint_hash()
|
||||
* Added ssh_pki_export_privkey_base64()
|
||||
* Added support for Match keyword in config file
|
||||
* Improved performance and reduced memory footprint for sftp
|
||||
* Fixed ecdsa publickey auth
|
||||
* Fixed reading a closed channel
|
||||
* Added support to announce posix-rename@openssh.com and
|
||||
hardlink@openssh.com in the sftp server
|
||||
|
||||
version 0.8.2 (released 2018-08-30)
|
||||
* Added sha256 fingerprints for pubkeys
|
||||
* Improved compiler flag detection
|
||||
* Fixed race condition in reading sftp messages
|
||||
* Fixed doxygen generation and added modern style
|
||||
* Fixed library initialization on Windows
|
||||
* Fixed __bounded__ attribute detection
|
||||
* Fixed a bug in the options parser
|
||||
* Fixed documentation for new knwon_hosts API
|
||||
|
||||
version 0.8.1 (released 2018-08-13)
|
||||
* Fixed version number in the header
|
||||
* Fixed version number in pkg-config and cmake config
|
||||
* Fixed library initialization
|
||||
* Fixed attribute detection
|
||||
|
||||
version 0.8.0 (released 2018-08-10)
|
||||
* Removed support for deprecated SSHv1 protocol
|
||||
* Added new connector API for clients
|
||||
|
||||
@@ -197,6 +197,7 @@ static void sizechanged(void)
|
||||
static void select_loop(ssh_session session,ssh_channel channel)
|
||||
{
|
||||
ssh_connector connector_in, connector_out, connector_err;
|
||||
int rc;
|
||||
|
||||
ssh_event event = ssh_event_new();
|
||||
|
||||
@@ -222,7 +223,11 @@ static void select_loop(ssh_session session,ssh_channel channel)
|
||||
if (signal_delayed) {
|
||||
sizechanged();
|
||||
}
|
||||
ssh_event_dopoll(event, 60000);
|
||||
rc = ssh_event_dopoll(event, 60000);
|
||||
if (rc == SSH_ERROR) {
|
||||
fprintf(stderr, "Error in ssh_event_dopoll()\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
ssh_event_remove_connector(event, connector_in);
|
||||
ssh_event_remove_connector(event, connector_out);
|
||||
|
||||
@@ -78,8 +78,8 @@
|
||||
|
||||
/* libssh version */
|
||||
#define LIBSSH_VERSION_MAJOR 0
|
||||
#define LIBSSH_VERSION_MINOR 8
|
||||
#define LIBSSH_VERSION_MICRO 90
|
||||
#define LIBSSH_VERSION_MINOR 9
|
||||
#define LIBSSH_VERSION_MICRO 0
|
||||
|
||||
#define LIBSSH_VERSION_INT SSH_VERSION_INT(LIBSSH_VERSION_MAJOR, \
|
||||
LIBSSH_VERSION_MINOR, \
|
||||
|
||||
@@ -272,8 +272,6 @@ int ssh_auth_reply_success(ssh_session session, int partial);
|
||||
int ssh_send_banner(ssh_session session, int is_server);
|
||||
|
||||
/* connect.c */
|
||||
socket_t ssh_connect_host(ssh_session session, const char *host,const char
|
||||
*bind_addr, int port, long timeout, long usec);
|
||||
socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
|
||||
const char *bind_addr, int port);
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ static int ssh_userauth_request_service(ssh_session session) {
|
||||
int rc;
|
||||
|
||||
rc = ssh_service_request(session, "ssh-userauth");
|
||||
if (rc != SSH_OK) {
|
||||
if ((rc != SSH_OK) && (rc != SSH_AGAIN)) {
|
||||
SSH_LOG(SSH_LOG_WARN,
|
||||
"Failed to request \"ssh-userauth\" service");
|
||||
}
|
||||
|
||||
537
src/connect.c
537
src/connect.c
@@ -90,131 +90,55 @@
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef gai_strerror
|
||||
char WSAAPI *gai_strerrorA(int code) {
|
||||
static char buf[256];
|
||||
char WSAAPI *gai_strerrorA(int code)
|
||||
{
|
||||
static char buf[256];
|
||||
|
||||
snprintf(buf, sizeof(buf), "Undetermined error code (%d)", code);
|
||||
snprintf(buf, sizeof(buf), "Undetermined error code (%d)", code);
|
||||
|
||||
return buf;
|
||||
return buf;
|
||||
}
|
||||
#endif /* gai_strerror */
|
||||
#endif /* _WIN32 */
|
||||
|
||||
static int ssh_connect_socket_close(socket_t s){
|
||||
static int ssh_connect_socket_close(socket_t s)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return closesocket(s);
|
||||
return closesocket(s);
|
||||
#else
|
||||
return close(s);
|
||||
return close(s);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int getai(const char *host, int port, struct addrinfo **ai)
|
||||
{
|
||||
const char *service = NULL;
|
||||
struct addrinfo hints;
|
||||
char s_port[10];
|
||||
|
||||
static int getai(const char *host, int port, struct addrinfo **ai) {
|
||||
const char *service = NULL;
|
||||
struct addrinfo hints;
|
||||
char s_port[10];
|
||||
ZERO_STRUCT(hints);
|
||||
|
||||
ZERO_STRUCT(hints);
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
hints.ai_family = PF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
hints.ai_family = PF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
if (port == 0) {
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
} else {
|
||||
snprintf(s_port, sizeof(s_port), "%hu", (unsigned short)port);
|
||||
service = s_port;
|
||||
if (port == 0) {
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
} else {
|
||||
snprintf(s_port, sizeof(s_port), "%hu", (unsigned short)port);
|
||||
service = s_port;
|
||||
#ifdef AI_NUMERICSERV
|
||||
hints.ai_flags=AI_NUMERICSERV;
|
||||
hints.ai_flags = AI_NUMERICSERV;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (ssh_is_ipaddr(host)) {
|
||||
/* this is an IP address */
|
||||
SSH_LOG(SSH_LOG_PACKET,"host %s matches an IP address",host);
|
||||
hints.ai_flags |= AI_NUMERICHOST;
|
||||
}
|
||||
if (ssh_is_ipaddr(host)) {
|
||||
/* this is an IP address */
|
||||
SSH_LOG(SSH_LOG_PACKET, "host %s matches an IP address", host);
|
||||
hints.ai_flags |= AI_NUMERICHOST;
|
||||
}
|
||||
|
||||
return getaddrinfo(host, service, &hints, ai);
|
||||
}
|
||||
|
||||
static int ssh_connect_ai_timeout(ssh_session session, const char *host,
|
||||
int port, struct addrinfo *ai, long timeout, long usec, socket_t s) {
|
||||
int timeout_ms;
|
||||
ssh_pollfd_t fds;
|
||||
int rc = 0;
|
||||
int ret;
|
||||
socklen_t len = sizeof(rc);
|
||||
|
||||
/* I know we're losing some precision. But it's not like poll-like family
|
||||
* type of mechanisms are precise up to the microsecond.
|
||||
*/
|
||||
timeout_ms=timeout * 1000 + usec / 1000;
|
||||
|
||||
rc = ssh_socket_set_nonblocking(s);
|
||||
if (rc < 0) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Failed to set socket non-blocking for %s:%d", host, port);
|
||||
ssh_connect_socket_close(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
SSH_LOG(SSH_LOG_RARE, "Trying to connect to host: %s:%d with "
|
||||
"timeout %d ms", host, port, timeout_ms);
|
||||
|
||||
/* The return value is checked later */
|
||||
connect(s, ai->ai_addr, ai->ai_addrlen);
|
||||
freeaddrinfo(ai);
|
||||
|
||||
fds.fd=s;
|
||||
fds.revents=0;
|
||||
fds.events=POLLOUT;
|
||||
#ifdef _WIN32
|
||||
fds.events |= POLLWRNORM;
|
||||
#endif
|
||||
rc = ssh_poll(&fds,1,timeout_ms);
|
||||
|
||||
if (rc == 0) {
|
||||
/* timeout */
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Timeout while connecting to %s:%d", host, port);
|
||||
ssh_connect_socket_close(s);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"poll error: %s", strerror(errno));
|
||||
ssh_connect_socket_close(s);
|
||||
|
||||
return -1;
|
||||
}
|
||||
rc = -1;
|
||||
|
||||
/* Get connect(2) return code. Zero means no error */
|
||||
ret = getsockopt(s, SOL_SOCKET, SO_ERROR,(char *) &rc, &len);
|
||||
if (ret < 0 || rc != 0) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Connect to %s:%d failed: %s", host, port, strerror(rc));
|
||||
ssh_connect_socket_close(s);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* s is connected ? */
|
||||
SSH_LOG(SSH_LOG_PACKET, "Socket connected with timeout");
|
||||
ret = ssh_socket_set_blocking(s);
|
||||
if (ret < 0) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Failed to set socket as blocking connecting to %s:%d failed: %s",
|
||||
host, port, strerror(errno));
|
||||
ssh_connect_socket_close(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return s;
|
||||
return getaddrinfo(host, service, &hints, ai);
|
||||
}
|
||||
|
||||
static int set_tcp_nodelay(socket_t socket)
|
||||
@@ -228,99 +152,6 @@ static int set_tcp_nodelay(socket_t socket)
|
||||
sizeof(opt));
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Connect to an IPv4 or IPv6 host specified by its IP address or
|
||||
* hostname.
|
||||
*
|
||||
* @returns A file descriptor, < 0 on error.
|
||||
*/
|
||||
socket_t ssh_connect_host(ssh_session session, const char *host,
|
||||
const char *bind_addr, int port, long timeout, long usec) {
|
||||
socket_t s = -1;
|
||||
int rc;
|
||||
struct addrinfo *ai;
|
||||
struct addrinfo *itr;
|
||||
|
||||
rc = getai(host, port, &ai);
|
||||
if (rc != 0) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Failed to resolve hostname %s (%s)", host, gai_strerror(rc));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (itr = ai; itr != NULL; itr = itr->ai_next){
|
||||
/* create socket */
|
||||
s = socket(itr->ai_family, itr->ai_socktype, itr->ai_protocol);
|
||||
if (s < 0) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Socket create failed: %s", strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bind_addr) {
|
||||
struct addrinfo *bind_ai;
|
||||
struct addrinfo *bind_itr;
|
||||
|
||||
SSH_LOG(SSH_LOG_PACKET, "Resolving %s", bind_addr);
|
||||
|
||||
rc = getai(bind_addr, 0, &bind_ai);
|
||||
if (rc != 0) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Failed to resolve bind address %s (%s)",
|
||||
bind_addr,
|
||||
gai_strerror(rc));
|
||||
freeaddrinfo(ai);
|
||||
close(s);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (bind_itr = bind_ai; bind_itr != NULL; bind_itr = bind_itr->ai_next) {
|
||||
if (bind(s, bind_itr->ai_addr, bind_itr->ai_addrlen) < 0) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Binding local address: %s", strerror(errno));
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
freeaddrinfo(bind_ai);
|
||||
|
||||
/* Cannot bind to any local addresses */
|
||||
if (bind_itr == NULL) {
|
||||
ssh_connect_socket_close(s);
|
||||
s = -1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (timeout || usec) {
|
||||
socket_t ret = ssh_connect_ai_timeout(session, host, port, itr,
|
||||
timeout, usec, s);
|
||||
|
||||
freeaddrinfo(ai);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (connect(s, itr->ai_addr, itr->ai_addrlen) < 0) {
|
||||
ssh_set_error(session, SSH_FATAL, "Connect failed: %s", strerror(errno));
|
||||
ssh_connect_socket_close(s);
|
||||
s = -1;
|
||||
continue;
|
||||
} else {
|
||||
/* We are connected */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
freeaddrinfo(ai);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
@@ -331,102 +162,109 @@ socket_t ssh_connect_host(ssh_session session, const char *host,
|
||||
* @warning very ugly !!!
|
||||
*/
|
||||
socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
|
||||
const char *bind_addr, int port) {
|
||||
socket_t s = -1;
|
||||
int rc;
|
||||
struct addrinfo *ai;
|
||||
struct addrinfo *itr;
|
||||
const char *bind_addr, int port)
|
||||
{
|
||||
socket_t s = -1;
|
||||
int rc;
|
||||
struct addrinfo *ai = NULL;
|
||||
struct addrinfo *itr = NULL;
|
||||
|
||||
rc = getai(host, port, &ai);
|
||||
if (rc != 0) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Failed to resolve hostname %s (%s)", host, gai_strerror(rc));
|
||||
rc = getai(host, port, &ai);
|
||||
if (rc != 0) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Failed to resolve hostname %s (%s)",
|
||||
host, gai_strerror(rc));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (itr = ai; itr != NULL; itr = itr->ai_next){
|
||||
/* create socket */
|
||||
s = socket(itr->ai_family, itr->ai_socktype, itr->ai_protocol);
|
||||
if (s < 0) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Socket create failed: %s", strerror(errno));
|
||||
continue;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bind_addr) {
|
||||
struct addrinfo *bind_ai;
|
||||
struct addrinfo *bind_itr;
|
||||
|
||||
SSH_LOG(SSH_LOG_PACKET, "Resolving %s", bind_addr);
|
||||
|
||||
rc = getai(bind_addr, 0, &bind_ai);
|
||||
if (rc != 0) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Failed to resolve bind address %s (%s)",
|
||||
bind_addr,
|
||||
gai_strerror(rc));
|
||||
ssh_connect_socket_close(s);
|
||||
s=-1;
|
||||
break;
|
||||
}
|
||||
|
||||
for (bind_itr = bind_ai; bind_itr != NULL; bind_itr = bind_itr->ai_next) {
|
||||
if (bind(s, bind_itr->ai_addr, bind_itr->ai_addrlen) < 0) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Binding local address: %s", strerror(errno));
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
for (itr = ai; itr != NULL; itr = itr->ai_next) {
|
||||
/* create socket */
|
||||
s = socket(itr->ai_family, itr->ai_socktype, itr->ai_protocol);
|
||||
if (s < 0) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Socket create failed: %s", strerror(errno));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
freeaddrinfo(bind_ai);
|
||||
|
||||
/* Cannot bind to any local addresses */
|
||||
if (bind_itr == NULL) {
|
||||
ssh_connect_socket_close(s);
|
||||
s = -1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (bind_addr) {
|
||||
struct addrinfo *bind_ai;
|
||||
struct addrinfo *bind_itr;
|
||||
|
||||
rc = ssh_socket_set_nonblocking(s);
|
||||
if (rc < 0) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Failed to set socket non-blocking for %s:%d", host, port);
|
||||
ssh_connect_socket_close(s);
|
||||
s = -1;
|
||||
continue;
|
||||
}
|
||||
SSH_LOG(SSH_LOG_PACKET, "Resolving %s", bind_addr);
|
||||
|
||||
if (session->opts.nodelay) {
|
||||
/* For winsock, socket options are only effective before connect */
|
||||
rc = set_tcp_nodelay(s);
|
||||
rc = getai(bind_addr, 0, &bind_ai);
|
||||
if (rc != 0) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Failed to resolve bind address %s (%s)",
|
||||
bind_addr,
|
||||
gai_strerror(rc));
|
||||
ssh_connect_socket_close(s);
|
||||
s = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
for (bind_itr = bind_ai;
|
||||
bind_itr != NULL;
|
||||
bind_itr = bind_itr->ai_next)
|
||||
{
|
||||
if (bind(s, bind_itr->ai_addr, bind_itr->ai_addrlen) < 0) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Binding local address: %s", strerror(errno));
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
freeaddrinfo(bind_ai);
|
||||
|
||||
/* Cannot bind to any local addresses */
|
||||
if (bind_itr == NULL) {
|
||||
ssh_connect_socket_close(s);
|
||||
s = -1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
rc = ssh_socket_set_nonblocking(s);
|
||||
if (rc < 0) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Failed to set TCP_NODELAY on socket: %s", strerror(errno));
|
||||
"Failed to set socket non-blocking for %s:%d",
|
||||
host, port);
|
||||
ssh_connect_socket_close(s);
|
||||
s = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (session->opts.nodelay) {
|
||||
/* For winsock, socket options are only effective before connect */
|
||||
rc = set_tcp_nodelay(s);
|
||||
if (rc < 0) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Failed to set TCP_NODELAY on socket: %s",
|
||||
strerror(errno));
|
||||
ssh_connect_socket_close(s);
|
||||
s = -1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
rc = connect(s, itr->ai_addr, itr->ai_addrlen);
|
||||
if (rc == -1 && (errno != 0) && (errno != EINPROGRESS)) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Failed to connect: %s", strerror(errno));
|
||||
ssh_connect_socket_close(s);
|
||||
s = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
rc = connect(s, itr->ai_addr, itr->ai_addrlen);
|
||||
if (rc == -1 && (errno != 0) && (errno != EINPROGRESS)) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Failed to connect: %s", strerror(errno));
|
||||
ssh_connect_socket_close(s);
|
||||
s = -1;
|
||||
continue;
|
||||
}
|
||||
freeaddrinfo(ai);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
freeaddrinfo(ai);
|
||||
|
||||
return s;
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -435,11 +273,13 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
|
||||
* @{
|
||||
*/
|
||||
|
||||
static int ssh_select_cb (socket_t fd, int revents, void *userdata){
|
||||
fd_set *set = (fd_set *)userdata;
|
||||
if(revents & POLLIN)
|
||||
FD_SET(fd, set);
|
||||
return 0;
|
||||
static int ssh_select_cb (socket_t fd, int revents, void *userdata)
|
||||
{
|
||||
fd_set *set = (fd_set *)userdata;
|
||||
if (revents & POLLIN) {
|
||||
FD_SET(fd, set);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -473,73 +313,84 @@ static int ssh_select_cb (socket_t fd, int revents, void *userdata){
|
||||
* @see select(2)
|
||||
*/
|
||||
int ssh_select(ssh_channel *channels, ssh_channel *outchannels, socket_t maxfd,
|
||||
fd_set *readfds, struct timeval *timeout) {
|
||||
fd_set origfds;
|
||||
socket_t fd;
|
||||
size_t i, j;
|
||||
int rc;
|
||||
int base_tm, tm;
|
||||
struct ssh_timestamp ts;
|
||||
ssh_event event = ssh_event_new();
|
||||
int firstround=1;
|
||||
fd_set *readfds, struct timeval *timeout)
|
||||
{
|
||||
fd_set origfds;
|
||||
socket_t fd;
|
||||
size_t i, j;
|
||||
int rc;
|
||||
int base_tm, tm;
|
||||
struct ssh_timestamp ts;
|
||||
ssh_event event = ssh_event_new();
|
||||
int firstround = 1;
|
||||
|
||||
base_tm = tm=timeout->tv_sec * 1000 + timeout->tv_usec/1000;
|
||||
for (i=0 ; channels[i] != NULL; ++i){
|
||||
ssh_event_add_session(event, channels[i]->session);
|
||||
}
|
||||
|
||||
ZERO_STRUCT(origfds);
|
||||
FD_ZERO(&origfds);
|
||||
for (fd = 0; fd < maxfd ; fd++) {
|
||||
if (FD_ISSET(fd, readfds)) {
|
||||
ssh_event_add_fd(event, fd, POLLIN, ssh_select_cb, readfds);
|
||||
FD_SET(fd, &origfds);
|
||||
}
|
||||
}
|
||||
outchannels[0] = NULL;
|
||||
FD_ZERO(readfds);
|
||||
ssh_timestamp_init(&ts);
|
||||
do {
|
||||
/* Poll every channel */
|
||||
j = 0;
|
||||
for (i = 0; channels[i]; i++) {
|
||||
if(ssh_channel_poll(channels[i], 0) != 0) {
|
||||
outchannels[j] = channels[i];
|
||||
j++;
|
||||
} else if(ssh_channel_poll(channels[i], 1) != 0) {
|
||||
outchannels[j] = channels[i];
|
||||
j++;
|
||||
}
|
||||
base_tm = tm = (timeout->tv_sec * 1000) + (timeout->tv_usec / 1000);
|
||||
for (i = 0 ; channels[i] != NULL; ++i) {
|
||||
ssh_event_add_session(event, channels[i]->session);
|
||||
}
|
||||
outchannels[j] = NULL;
|
||||
if(j != 0)
|
||||
break;
|
||||
/* watch if a user socket was triggered */
|
||||
for (fd = 0; fd < maxfd; fd++) {
|
||||
|
||||
ZERO_STRUCT(origfds);
|
||||
FD_ZERO(&origfds);
|
||||
for (fd = 0; fd < maxfd ; fd++) {
|
||||
if (FD_ISSET(fd, readfds)) {
|
||||
goto out;
|
||||
ssh_event_add_fd(event, fd, POLLIN, ssh_select_cb, readfds);
|
||||
FD_SET(fd, &origfds);
|
||||
}
|
||||
}
|
||||
outchannels[0] = NULL;
|
||||
FD_ZERO(readfds);
|
||||
ssh_timestamp_init(&ts);
|
||||
do {
|
||||
/* Poll every channel */
|
||||
j = 0;
|
||||
for (i = 0; channels[i]; i++) {
|
||||
rc = ssh_channel_poll(channels[i], 0);
|
||||
if (rc != 0) {
|
||||
outchannels[j] = channels[i];
|
||||
j++;
|
||||
} else {
|
||||
rc = ssh_channel_poll(channels[i], 1);
|
||||
if (rc != 0) {
|
||||
outchannels[j] = channels[i];
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If the timeout is elapsed, we should go out */
|
||||
if(!firstround && ssh_timeout_elapsed(&ts, base_tm))
|
||||
goto out;
|
||||
/* since there's nothing, let's fire the polling */
|
||||
rc = ssh_event_dopoll(event,tm);
|
||||
if (rc == SSH_ERROR){
|
||||
goto out;
|
||||
}
|
||||
tm = ssh_timeout_update(&ts, base_tm);
|
||||
firstround=0;
|
||||
} while (1);
|
||||
outchannels[j] = NULL;
|
||||
if (j != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* watch if a user socket was triggered */
|
||||
for (fd = 0; fd < maxfd; fd++) {
|
||||
if (FD_ISSET(fd, readfds)) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* If the timeout is elapsed, we should go out */
|
||||
if (!firstround && ssh_timeout_elapsed(&ts, base_tm)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* since there's nothing, let's fire the polling */
|
||||
rc = ssh_event_dopoll(event,tm);
|
||||
if (rc == SSH_ERROR) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
tm = ssh_timeout_update(&ts, base_tm);
|
||||
firstround = 0;
|
||||
} while (1);
|
||||
out:
|
||||
for (fd = 0; fd < maxfd; fd++) {
|
||||
if (FD_ISSET(fd, &origfds)) {
|
||||
ssh_event_remove_fd(event, fd);
|
||||
for (fd = 0; fd < maxfd; fd++) {
|
||||
if (FD_ISSET(fd, &origfds)) {
|
||||
ssh_event_remove_fd(event, fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
ssh_event_free(event);
|
||||
return SSH_OK;
|
||||
ssh_event_free(event);
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
@@ -194,8 +194,13 @@ SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_group)
|
||||
if (rc == SSH_ERROR) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = ssh_dh_keypair_get_keys(session->next_crypto->dh_ctx,
|
||||
DH_CLIENT_KEYPAIR, NULL, &pubkey);
|
||||
if (rc != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_pack(session->out_buffer,
|
||||
"bB",
|
||||
SSH2_MSG_KEX_DH_GEX_INIT,
|
||||
|
||||
@@ -306,7 +306,7 @@ static char *ssh_session_get_host_port(ssh_session session)
|
||||
if (session->opts.host == NULL) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Can't verify server inn known hosts if the host we "
|
||||
"Can't verify server in known hosts if the host we "
|
||||
"should connect to has not been set");
|
||||
|
||||
return NULL;
|
||||
@@ -638,14 +638,15 @@ enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session)
|
||||
struct ssh_list *entry_list = NULL;
|
||||
struct ssh_iterator *it = NULL;
|
||||
char *host_port = NULL;
|
||||
bool ok;
|
||||
bool global_known_hosts_found = false;
|
||||
bool known_hosts_found = false;
|
||||
int rc;
|
||||
|
||||
if (session->opts.knownhosts == NULL) {
|
||||
if (ssh_options_apply(session) < 0) {
|
||||
ssh_set_error(session,
|
||||
SSH_REQUEST_DENIED,
|
||||
"Can't find a known_hosts file");
|
||||
"Cannot find a known_hosts file");
|
||||
|
||||
return SSH_KNOWN_HOSTS_NOT_FOUND;
|
||||
}
|
||||
@@ -653,23 +654,38 @@ enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session)
|
||||
|
||||
if (session->opts.knownhosts == NULL &&
|
||||
session->opts.global_knownhosts == NULL) {
|
||||
ssh_set_error(session,
|
||||
SSH_REQUEST_DENIED,
|
||||
"No path set for a known_hosts file");
|
||||
|
||||
return SSH_KNOWN_HOSTS_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (session->opts.knownhosts != NULL) {
|
||||
ok = ssh_file_readaccess_ok(session->opts.knownhosts);
|
||||
if (!ok) {
|
||||
return SSH_KNOWN_HOSTS_NOT_FOUND;
|
||||
known_hosts_found = ssh_file_readaccess_ok(session->opts.knownhosts);
|
||||
if (!known_hosts_found) {
|
||||
SSH_LOG(SSH_LOG_WARN, "Cannot access file %s",
|
||||
session->opts.knownhosts);
|
||||
}
|
||||
}
|
||||
|
||||
if (session->opts.global_knownhosts != NULL) {
|
||||
ok = ssh_file_readaccess_ok(session->opts.global_knownhosts);
|
||||
if (!ok) {
|
||||
return SSH_KNOWN_HOSTS_NOT_FOUND;
|
||||
global_known_hosts_found =
|
||||
ssh_file_readaccess_ok(session->opts.global_knownhosts);
|
||||
if (!global_known_hosts_found) {
|
||||
SSH_LOG(SSH_LOG_WARN, "Cannot access file %s",
|
||||
session->opts.global_knownhosts);
|
||||
}
|
||||
}
|
||||
|
||||
if ((!known_hosts_found) && (!global_known_hosts_found)) {
|
||||
ssh_set_error(session,
|
||||
SSH_REQUEST_DENIED,
|
||||
"Cannot find a known_hosts file");
|
||||
|
||||
return SSH_KNOWN_HOSTS_NOT_FOUND;
|
||||
}
|
||||
|
||||
host_port = ssh_session_get_host_port(session);
|
||||
if (host_port == NULL) {
|
||||
return SSH_KNOWN_HOSTS_ERROR;
|
||||
@@ -682,7 +698,7 @@ enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session)
|
||||
if (rc != 0) {
|
||||
SAFE_FREE(host_port);
|
||||
ssh_list_free(entry_list);
|
||||
return SSH_KNOWN_HOSTS_UNKNOWN;
|
||||
return SSH_KNOWN_HOSTS_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -693,7 +709,7 @@ enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session)
|
||||
SAFE_FREE(host_port);
|
||||
if (rc != 0) {
|
||||
ssh_list_free(entry_list);
|
||||
return SSH_KNOWN_HOSTS_UNKNOWN;
|
||||
return SSH_KNOWN_HOSTS_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1491,12 +1491,18 @@ SSH_PACKET_CALLBACK(ssh_packet_global_request){
|
||||
msg->type = SSH_REQUEST_GLOBAL;
|
||||
|
||||
if (strcmp(request, "tcpip-forward") == 0) {
|
||||
|
||||
/* According to RFC4254, the client SHOULD reject this message */
|
||||
if (session->client) {
|
||||
goto reply_with_failure;
|
||||
}
|
||||
|
||||
r = ssh_buffer_unpack(packet, "sd",
|
||||
&msg->global_request.bind_address,
|
||||
&msg->global_request.bind_port
|
||||
);
|
||||
if (r != SSH_OK){
|
||||
goto error;
|
||||
goto reply_with_failure;
|
||||
}
|
||||
msg->global_request.type = SSH_GLOBAL_REQUEST_TCPIP_FORWARD;
|
||||
msg->global_request.want_reply = want_reply;
|
||||
@@ -1516,11 +1522,17 @@ SSH_PACKET_CALLBACK(ssh_packet_global_request){
|
||||
return rc;
|
||||
}
|
||||
} else if (strcmp(request, "cancel-tcpip-forward") == 0) {
|
||||
|
||||
/* According to RFC4254, the client SHOULD reject this message */
|
||||
if (session->client) {
|
||||
goto reply_with_failure;
|
||||
}
|
||||
|
||||
r = ssh_buffer_unpack(packet, "sd",
|
||||
&msg->global_request.bind_address,
|
||||
&msg->global_request.bind_port);
|
||||
if (r != SSH_OK){
|
||||
goto error;
|
||||
goto reply_with_failure;
|
||||
}
|
||||
msg->global_request.type = SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD;
|
||||
msg->global_request.want_reply = want_reply;
|
||||
@@ -1546,18 +1558,41 @@ SSH_PACKET_CALLBACK(ssh_packet_global_request){
|
||||
ssh_message_global_request_reply_success(msg, 0);
|
||||
}
|
||||
} else {
|
||||
SSH_LOG(SSH_LOG_PROTOCOL, "UNKNOWN SSH_MSG_GLOBAL_REQUEST %s %d", request, want_reply);
|
||||
rc = SSH_PACKET_NOT_USED;
|
||||
SSH_LOG(SSH_LOG_PROTOCOL, "UNKNOWN SSH_MSG_GLOBAL_REQUEST %s, "
|
||||
"want_reply = %d", request, want_reply);
|
||||
goto reply_with_failure;
|
||||
}
|
||||
|
||||
SAFE_FREE(msg);
|
||||
SAFE_FREE(request);
|
||||
return rc;
|
||||
|
||||
reply_with_failure:
|
||||
/* Only report the failure if requested */
|
||||
if (want_reply) {
|
||||
r = ssh_buffer_add_u8(session->out_buffer,
|
||||
SSH2_MSG_REQUEST_FAILURE);
|
||||
if (r < 0) {
|
||||
ssh_set_error_oom(session);
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = ssh_packet_send(session);
|
||||
if (r != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
SSH_LOG(SSH_LOG_PACKET,
|
||||
"The requester doesn't want to know the request failed!");
|
||||
}
|
||||
|
||||
/* Consume the message to avoid sending UNIMPLEMENTED later */
|
||||
rc = SSH_PACKET_USED;
|
||||
error:
|
||||
SAFE_FREE(msg);
|
||||
SAFE_FREE(request);
|
||||
SSH_LOG(SSH_LOG_WARNING, "Invalid SSH_MSG_GLOBAL_REQUEST packet");
|
||||
return SSH_PACKET_NOT_USED;
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif /* WITH_SERVER */
|
||||
|
||||
75
src/packet.c
75
src/packet.c
@@ -1438,37 +1438,54 @@ void ssh_packet_set_default_callbacks(ssh_session session){
|
||||
* @brief dispatch the call of packet handlers callbacks for a received packet
|
||||
* @param type type of packet
|
||||
*/
|
||||
void ssh_packet_process(ssh_session session, uint8_t type){
|
||||
struct ssh_iterator *i;
|
||||
int r=SSH_PACKET_NOT_USED;
|
||||
ssh_packet_callbacks cb;
|
||||
void ssh_packet_process(ssh_session session, uint8_t type)
|
||||
{
|
||||
struct ssh_iterator *i = NULL;
|
||||
int rc = SSH_PACKET_NOT_USED;
|
||||
ssh_packet_callbacks cb;
|
||||
|
||||
SSH_LOG(SSH_LOG_PACKET, "Dispatching handler for packet type %d",type);
|
||||
if(session->packet_callbacks == NULL){
|
||||
SSH_LOG(SSH_LOG_RARE,"Packet callback is not initialized !");
|
||||
SSH_LOG(SSH_LOG_PACKET, "Dispatching handler for packet type %d", type);
|
||||
if (session->packet_callbacks == NULL) {
|
||||
SSH_LOG(SSH_LOG_RARE, "Packet callback is not initialized !");
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
i=ssh_list_get_iterator(session->packet_callbacks);
|
||||
while(i != NULL){
|
||||
cb=ssh_iterator_value(ssh_packet_callbacks,i);
|
||||
i=i->next;
|
||||
if(!cb)
|
||||
continue;
|
||||
if(cb->start > type)
|
||||
continue;
|
||||
if(cb->start + cb->n_callbacks <= type)
|
||||
continue;
|
||||
if(cb->callbacks[type - cb->start]==NULL)
|
||||
continue;
|
||||
r=cb->callbacks[type - cb->start](session,type,session->in_buffer,cb->user);
|
||||
if(r==SSH_PACKET_USED)
|
||||
break;
|
||||
}
|
||||
if(r==SSH_PACKET_NOT_USED){
|
||||
SSH_LOG(SSH_LOG_RARE,"Couldn't do anything with packet type %d",type);
|
||||
ssh_packet_send_unimplemented(session, session->recv_seq-1);
|
||||
}
|
||||
i = ssh_list_get_iterator(session->packet_callbacks);
|
||||
while (i != NULL) {
|
||||
cb = ssh_iterator_value(ssh_packet_callbacks, i);
|
||||
i = i->next;
|
||||
|
||||
if (!cb) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cb->start > type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cb->start + cb->n_callbacks <= type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cb->callbacks[type - cb->start] == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
rc = cb->callbacks[type - cb->start](session, type, session->in_buffer,
|
||||
cb->user);
|
||||
if (rc == SSH_PACKET_USED) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rc == SSH_PACKET_NOT_USED) {
|
||||
SSH_LOG(SSH_LOG_RARE, "Couldn't do anything with packet type %d", type);
|
||||
rc = ssh_packet_send_unimplemented(session, session->recv_seq - 1);
|
||||
if (rc != SSH_OK) {
|
||||
SSH_LOG(SSH_LOG_RARE, "Failed to send unimplemented: %s",
|
||||
ssh_get_error(session));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal
|
||||
|
||||
42
src/pki.c
42
src/pki.c
@@ -64,16 +64,22 @@
|
||||
#include "libssh/misc.h"
|
||||
#include "libssh/agent.h"
|
||||
|
||||
enum ssh_keytypes_e pki_privatekey_type_from_string(const char *privkey) {
|
||||
if (strncmp(privkey, DSA_HEADER_BEGIN, strlen(DSA_HEADER_BEGIN)) == 0) {
|
||||
enum ssh_keytypes_e pki_privatekey_type_from_string(const char *privkey)
|
||||
{
|
||||
char *start = NULL;
|
||||
|
||||
start = strstr(privkey, DSA_HEADER_BEGIN);
|
||||
if (start != NULL) {
|
||||
return SSH_KEYTYPE_DSS;
|
||||
}
|
||||
|
||||
if (strncmp(privkey, RSA_HEADER_BEGIN, strlen(RSA_HEADER_BEGIN)) == 0) {
|
||||
start = strstr(privkey, RSA_HEADER_BEGIN);
|
||||
if (start != NULL) {
|
||||
return SSH_KEYTYPE_RSA;
|
||||
}
|
||||
|
||||
if (strncmp(privkey, ECDSA_HEADER_BEGIN, strlen(ECDSA_HEADER_BEGIN)) == 0) {
|
||||
start = strstr(privkey, ECDSA_HEADER_BEGIN);
|
||||
if (start != 0) {
|
||||
/* We don't know what the curve is at this point, so we don't actually
|
||||
* know the type. We figure out the actual curve and fix things up in
|
||||
* pki_private_key_from_base64 */
|
||||
@@ -382,6 +388,19 @@ enum ssh_digest_e ssh_key_type_to_hash(ssh_session session,
|
||||
case SSH_KEYTYPE_DSS:
|
||||
return SSH_DIGEST_SHA1;
|
||||
case SSH_KEYTYPE_RSA_CERT01:
|
||||
/* If we are talking to an old OpenSSH version which does not support
|
||||
* SHA2 in certificates */
|
||||
if ((session->openssh > 0) &&
|
||||
(session->openssh < SSH_VERSION_INT(7, 2, 0)))
|
||||
{
|
||||
SSH_LOG(SSH_LOG_DEBUG,
|
||||
"We are talking to an old OpenSSH (%x); "
|
||||
"returning SSH_DIGEST_SHA1",
|
||||
session->openssh);
|
||||
|
||||
return SSH_DIGEST_SHA1;
|
||||
}
|
||||
FALL_THROUGH;
|
||||
case SSH_KEYTYPE_RSA:
|
||||
if (ssh_key_algorithm_allowed(session, "rsa-sha2-512") &&
|
||||
(session->extensions & SSH_EXT_SIG_RSA_SHA512)) {
|
||||
@@ -435,6 +454,21 @@ ssh_key_get_signature_algorithm(ssh_session session,
|
||||
{
|
||||
enum ssh_digest_e hash_type;
|
||||
|
||||
if (type == SSH_KEYTYPE_RSA_CERT01) {
|
||||
/* If we are talking to an old OpenSSH version which does not support
|
||||
* rsa-sha2-{256,512}-cert-v01@openssh.com */
|
||||
if ((session->openssh > 0) &&
|
||||
(session->openssh < SSH_VERSION_INT(7, 8, 0)))
|
||||
{
|
||||
SSH_LOG(SSH_LOG_DEBUG,
|
||||
"We are talking to an old OpenSSH (%x); "
|
||||
"using old cert format",
|
||||
session->openssh);
|
||||
|
||||
return "ssh-rsa-cert-v01@openssh.com";
|
||||
}
|
||||
}
|
||||
|
||||
hash_type = ssh_key_type_to_hash(session, type);
|
||||
|
||||
return ssh_key_signature_to_char(type, hash_type);
|
||||
|
||||
@@ -529,7 +529,7 @@ int pki_key_generate_rsa(ssh_key key, int parameter){
|
||||
|
||||
BN_free(e);
|
||||
|
||||
if (rc == -1 || key->rsa == NULL)
|
||||
if (rc <= 0 || key->rsa == NULL)
|
||||
return SSH_ERROR;
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
508
src/socket.c
508
src/socket.c
@@ -102,36 +102,37 @@ static ssize_t ssh_socket_unbuffered_write(ssh_socket s,
|
||||
* \internal
|
||||
* \brief inits the socket system (windows specific)
|
||||
*/
|
||||
int ssh_socket_init(void) {
|
||||
if (sockets_initialized == 0) {
|
||||
int ssh_socket_init(void)
|
||||
{
|
||||
if (sockets_initialized == 0) {
|
||||
#ifdef _WIN32
|
||||
struct WSAData wsaData;
|
||||
struct WSAData wsaData;
|
||||
|
||||
/* Initiates use of the Winsock DLL by a process. */
|
||||
if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) {
|
||||
return -1;
|
||||
/* Initiates use of the Winsock DLL by a process. */
|
||||
if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
ssh_poll_init();
|
||||
|
||||
sockets_initialized = 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
ssh_poll_init();
|
||||
|
||||
sockets_initialized = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Cleanup the socket system.
|
||||
*/
|
||||
void ssh_socket_cleanup(void) {
|
||||
if (sockets_initialized == 1) {
|
||||
ssh_poll_cleanup();
|
||||
void ssh_socket_cleanup(void)
|
||||
{
|
||||
if (sockets_initialized == 1) {
|
||||
ssh_poll_cleanup();
|
||||
#ifdef _WIN32
|
||||
WSACleanup();
|
||||
WSACleanup();
|
||||
#endif
|
||||
sockets_initialized = 0;
|
||||
}
|
||||
sockets_initialized = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -139,37 +140,38 @@ void ssh_socket_cleanup(void) {
|
||||
* \internal
|
||||
* \brief creates a new Socket object
|
||||
*/
|
||||
ssh_socket ssh_socket_new(ssh_session session) {
|
||||
ssh_socket s;
|
||||
ssh_socket ssh_socket_new(ssh_session session)
|
||||
{
|
||||
ssh_socket s;
|
||||
|
||||
s = calloc(1, sizeof(struct ssh_socket_struct));
|
||||
if (s == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
return NULL;
|
||||
}
|
||||
s->fd = SSH_INVALID_SOCKET;
|
||||
s->last_errno = -1;
|
||||
s->fd_is_socket = 1;
|
||||
s->session = session;
|
||||
s->in_buffer = ssh_buffer_new();
|
||||
if (s->in_buffer == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
SAFE_FREE(s);
|
||||
return NULL;
|
||||
}
|
||||
s->out_buffer=ssh_buffer_new();
|
||||
if (s->out_buffer == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
ssh_buffer_free(s->in_buffer);
|
||||
SAFE_FREE(s);
|
||||
return NULL;
|
||||
}
|
||||
s->read_wontblock = 0;
|
||||
s->write_wontblock = 0;
|
||||
s->data_except = 0;
|
||||
s->poll_handle = NULL;
|
||||
s->state=SSH_SOCKET_NONE;
|
||||
return s;
|
||||
s = calloc(1, sizeof(struct ssh_socket_struct));
|
||||
if (s == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
return NULL;
|
||||
}
|
||||
s->fd = SSH_INVALID_SOCKET;
|
||||
s->last_errno = -1;
|
||||
s->fd_is_socket = 1;
|
||||
s->session = session;
|
||||
s->in_buffer = ssh_buffer_new();
|
||||
if (s->in_buffer == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
SAFE_FREE(s);
|
||||
return NULL;
|
||||
}
|
||||
s->out_buffer=ssh_buffer_new();
|
||||
if (s->out_buffer == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
ssh_buffer_free(s->in_buffer);
|
||||
SAFE_FREE(s);
|
||||
return NULL;
|
||||
}
|
||||
s->read_wontblock = 0;
|
||||
s->write_wontblock = 0;
|
||||
s->data_except = 0;
|
||||
s->poll_handle = NULL;
|
||||
s->state=SSH_SOCKET_NONE;
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -177,17 +179,18 @@ ssh_socket ssh_socket_new(ssh_session session) {
|
||||
* @brief Reset the state of a socket so it looks brand-new
|
||||
* @param[in] s socket to rest
|
||||
*/
|
||||
void ssh_socket_reset(ssh_socket s){
|
||||
s->fd = SSH_INVALID_SOCKET;
|
||||
s->last_errno = -1;
|
||||
s->fd_is_socket = 1;
|
||||
ssh_buffer_reinit(s->in_buffer);
|
||||
ssh_buffer_reinit(s->out_buffer);
|
||||
s->read_wontblock = 0;
|
||||
s->write_wontblock = 0;
|
||||
s->data_except = 0;
|
||||
s->poll_handle = NULL;
|
||||
s->state=SSH_SOCKET_NONE;
|
||||
void ssh_socket_reset(ssh_socket s)
|
||||
{
|
||||
s->fd = SSH_INVALID_SOCKET;
|
||||
s->last_errno = -1;
|
||||
s->fd_is_socket = 1;
|
||||
ssh_buffer_reinit(s->in_buffer);
|
||||
ssh_buffer_reinit(s->out_buffer);
|
||||
s->read_wontblock = 0;
|
||||
s->write_wontblock = 0;
|
||||
s->data_except = 0;
|
||||
s->poll_handle = NULL;
|
||||
s->state=SSH_SOCKET_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -198,8 +201,9 @@ void ssh_socket_reset(ssh_socket s){
|
||||
* @param callbacks a ssh_socket_callback object reference.
|
||||
*/
|
||||
|
||||
void ssh_socket_set_callbacks(ssh_socket s, ssh_socket_callbacks callbacks){
|
||||
s->callbacks=callbacks;
|
||||
void ssh_socket_set_callbacks(ssh_socket s, ssh_socket_callbacks callbacks)
|
||||
{
|
||||
s->callbacks = callbacks;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -383,71 +387,73 @@ ssh_poll_handle ssh_socket_get_poll_handle(ssh_socket s)
|
||||
/** \internal
|
||||
* \brief Deletes a socket object
|
||||
*/
|
||||
void ssh_socket_free(ssh_socket s){
|
||||
if (s == NULL) {
|
||||
return;
|
||||
}
|
||||
ssh_socket_close(s);
|
||||
ssh_buffer_free(s->in_buffer);
|
||||
ssh_buffer_free(s->out_buffer);
|
||||
SAFE_FREE(s);
|
||||
void ssh_socket_free(ssh_socket s)
|
||||
{
|
||||
if (s == NULL) {
|
||||
return;
|
||||
}
|
||||
ssh_socket_close(s);
|
||||
ssh_buffer_free(s->in_buffer);
|
||||
ssh_buffer_free(s->out_buffer);
|
||||
SAFE_FREE(s);
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
int ssh_socket_unix(ssh_socket s, const char *path) {
|
||||
struct sockaddr_un sunaddr;
|
||||
socket_t fd;
|
||||
sunaddr.sun_family = AF_UNIX;
|
||||
snprintf(sunaddr.sun_path, sizeof(sunaddr.sun_path), "%s", path);
|
||||
int ssh_socket_unix(ssh_socket s, const char *path)
|
||||
{
|
||||
struct sockaddr_un sunaddr;
|
||||
socket_t fd;
|
||||
sunaddr.sun_family = AF_UNIX;
|
||||
snprintf(sunaddr.sun_path, sizeof(sunaddr.sun_path), "%s", path);
|
||||
|
||||
fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd == SSH_INVALID_SOCKET) {
|
||||
ssh_set_error(s->session, SSH_FATAL,
|
||||
"Error from socket(AF_UNIX, SOCK_STREAM, 0): %s",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd == SSH_INVALID_SOCKET) {
|
||||
ssh_set_error(s->session, SSH_FATAL,
|
||||
"Error from socket(AF_UNIX, SOCK_STREAM, 0): %s",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fcntl(fd, F_SETFD, 1) == -1) {
|
||||
ssh_set_error(s->session, SSH_FATAL,
|
||||
"Error from fcntl(fd, F_SETFD, 1): %s",
|
||||
strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
if (fcntl(fd, F_SETFD, 1) == -1) {
|
||||
ssh_set_error(s->session, SSH_FATAL,
|
||||
"Error from fcntl(fd, F_SETFD, 1): %s",
|
||||
strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (connect(fd, (struct sockaddr *) &sunaddr,
|
||||
sizeof(sunaddr)) < 0) {
|
||||
ssh_set_error(s->session, SSH_FATAL, "Error from connect(): %s",
|
||||
strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
ssh_socket_set_fd(s,fd);
|
||||
return 0;
|
||||
if (connect(fd, (struct sockaddr *) &sunaddr, sizeof(sunaddr)) < 0) {
|
||||
ssh_set_error(s->session, SSH_FATAL, "Error from connect(): %s",
|
||||
strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
ssh_socket_set_fd(s,fd);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/** \internal
|
||||
* \brief closes a socket
|
||||
*/
|
||||
void ssh_socket_close(ssh_socket s){
|
||||
if (ssh_socket_is_open(s)) {
|
||||
void ssh_socket_close(ssh_socket s)
|
||||
{
|
||||
if (ssh_socket_is_open(s)) {
|
||||
#ifdef _WIN32
|
||||
CLOSE_SOCKET(s->fd);
|
||||
s->last_errno = WSAGetLastError();
|
||||
CLOSE_SOCKET(s->fd);
|
||||
s->last_errno = WSAGetLastError();
|
||||
#else
|
||||
CLOSE_SOCKET(s->fd);
|
||||
s->last_errno = errno;
|
||||
CLOSE_SOCKET(s->fd);
|
||||
s->last_errno = errno;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if(s->poll_handle != NULL){
|
||||
ssh_poll_free(s->poll_handle);
|
||||
s->poll_handle=NULL;
|
||||
}
|
||||
if (s->poll_handle != NULL) {
|
||||
ssh_poll_free(s->poll_handle);
|
||||
s->poll_handle = NULL;
|
||||
}
|
||||
|
||||
s->state = SSH_SOCKET_CLOSED;
|
||||
s->state = SSH_SOCKET_CLOSED;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -458,7 +464,8 @@ void ssh_socket_close(ssh_socket s){
|
||||
* @warning this function updates boths the input and output
|
||||
* file descriptors
|
||||
*/
|
||||
void ssh_socket_set_fd(ssh_socket s, socket_t fd) {
|
||||
void ssh_socket_set_fd(ssh_socket s, socket_t fd)
|
||||
{
|
||||
s->fd = fd;
|
||||
|
||||
if (s->poll_handle) {
|
||||
@@ -479,14 +486,15 @@ void ssh_socket_set_fd(ssh_socket s, socket_t fd) {
|
||||
*/
|
||||
socket_t ssh_socket_get_fd(ssh_socket s)
|
||||
{
|
||||
return s->fd;
|
||||
return s->fd;
|
||||
}
|
||||
|
||||
/** \internal
|
||||
* \brief returns nonzero if the socket is open
|
||||
*/
|
||||
int ssh_socket_is_open(ssh_socket s) {
|
||||
return s->fd != SSH_INVALID_SOCKET;
|
||||
int ssh_socket_is_open(ssh_socket s)
|
||||
{
|
||||
return s->fd != SSH_INVALID_SOCKET;
|
||||
}
|
||||
|
||||
/** \internal
|
||||
@@ -564,29 +572,31 @@ static ssize_t ssh_socket_unbuffered_write(ssh_socket s,
|
||||
/** \internal
|
||||
* \brief returns nonzero if the current socket is in the fd_set
|
||||
*/
|
||||
int ssh_socket_fd_isset(ssh_socket s, fd_set *set) {
|
||||
if(s->fd == SSH_INVALID_SOCKET) {
|
||||
return 0;
|
||||
}
|
||||
return FD_ISSET(s->fd,set);
|
||||
int ssh_socket_fd_isset(ssh_socket s, fd_set *set)
|
||||
{
|
||||
if(s->fd == SSH_INVALID_SOCKET) {
|
||||
return 0;
|
||||
}
|
||||
return FD_ISSET(s->fd,set);
|
||||
}
|
||||
|
||||
/** \internal
|
||||
* \brief sets the current fd in a fd_set and updates the max_fd
|
||||
*/
|
||||
|
||||
void ssh_socket_fd_set(ssh_socket s, fd_set *set, socket_t *max_fd) {
|
||||
if (s->fd == SSH_INVALID_SOCKET) {
|
||||
return;
|
||||
}
|
||||
void ssh_socket_fd_set(ssh_socket s, fd_set *set, socket_t *max_fd)
|
||||
{
|
||||
if (s->fd == SSH_INVALID_SOCKET) {
|
||||
return;
|
||||
}
|
||||
|
||||
FD_SET(s->fd,set);
|
||||
FD_SET(s->fd,set);
|
||||
|
||||
if (s->fd >= 0 &&
|
||||
s->fd >= *max_fd &&
|
||||
s->fd != SSH_INVALID_SOCKET) {
|
||||
*max_fd = s->fd + 1;
|
||||
}
|
||||
if (s->fd >= 0 &&
|
||||
s->fd >= *max_fd &&
|
||||
s->fd != SSH_INVALID_SOCKET) {
|
||||
*max_fd = s->fd + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/** \internal
|
||||
@@ -594,16 +604,17 @@ void ssh_socket_fd_set(ssh_socket s, fd_set *set, socket_t *max_fd) {
|
||||
* \returns SSH_OK, or SSH_ERROR
|
||||
* \warning has no effect on socket before a flush
|
||||
*/
|
||||
int ssh_socket_write(ssh_socket s, const void *buffer, int len) {
|
||||
if(len > 0) {
|
||||
if (ssh_buffer_add_data(s->out_buffer, buffer, len) < 0) {
|
||||
ssh_set_error_oom(s->session);
|
||||
return SSH_ERROR;
|
||||
int ssh_socket_write(ssh_socket s, const void *buffer, int len)
|
||||
{
|
||||
if (len > 0) {
|
||||
if (ssh_buffer_add_data(s->out_buffer, buffer, len) < 0) {
|
||||
ssh_set_error_oom(s->session);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
ssh_socket_nonblocking_flush(s);
|
||||
}
|
||||
ssh_socket_nonblocking_flush(s);
|
||||
}
|
||||
|
||||
return SSH_OK;
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -685,24 +696,29 @@ int ssh_socket_nonblocking_flush(ssh_socket s)
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
void ssh_socket_set_write_wontblock(ssh_socket s) {
|
||||
s->write_wontblock = 1;
|
||||
void ssh_socket_set_write_wontblock(ssh_socket s)
|
||||
{
|
||||
s->write_wontblock = 1;
|
||||
}
|
||||
|
||||
void ssh_socket_set_read_wontblock(ssh_socket s) {
|
||||
s->read_wontblock = 1;
|
||||
void ssh_socket_set_read_wontblock(ssh_socket s)
|
||||
{
|
||||
s->read_wontblock = 1;
|
||||
}
|
||||
|
||||
void ssh_socket_set_except(ssh_socket s) {
|
||||
s->data_except = 1;
|
||||
void ssh_socket_set_except(ssh_socket s)
|
||||
{
|
||||
s->data_except = 1;
|
||||
}
|
||||
|
||||
int ssh_socket_data_available(ssh_socket s) {
|
||||
return s->read_wontblock;
|
||||
int ssh_socket_data_available(ssh_socket s)
|
||||
{
|
||||
return s->read_wontblock;
|
||||
}
|
||||
|
||||
int ssh_socket_data_writable(ssh_socket s) {
|
||||
return s->write_wontblock;
|
||||
int ssh_socket_data_writable(ssh_socket s)
|
||||
{
|
||||
return s->write_wontblock;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
@@ -710,60 +726,69 @@ int ssh_socket_data_writable(ssh_socket s) {
|
||||
* @param s the socket
|
||||
* @returns numbers of bytes buffered, or 0 if the socket isn't connected
|
||||
*/
|
||||
int ssh_socket_buffered_write_bytes(ssh_socket s){
|
||||
if(s==NULL || s->out_buffer == NULL)
|
||||
return 0;
|
||||
return ssh_buffer_get_len(s->out_buffer);
|
||||
int ssh_socket_buffered_write_bytes(ssh_socket s)
|
||||
{
|
||||
if (s==NULL || s->out_buffer == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ssh_buffer_get_len(s->out_buffer);
|
||||
}
|
||||
|
||||
|
||||
int ssh_socket_get_status(ssh_socket s) {
|
||||
int r = 0;
|
||||
int ssh_socket_get_status(ssh_socket s)
|
||||
{
|
||||
int r = 0;
|
||||
|
||||
if (ssh_buffer_get_len(s->in_buffer) > 0) {
|
||||
r |= SSH_READ_PENDING;
|
||||
}
|
||||
if (ssh_buffer_get_len(s->in_buffer) > 0) {
|
||||
r |= SSH_READ_PENDING;
|
||||
}
|
||||
|
||||
if (ssh_buffer_get_len(s->out_buffer) > 0) {
|
||||
r |= SSH_WRITE_PENDING;
|
||||
}
|
||||
if (ssh_buffer_get_len(s->out_buffer) > 0) {
|
||||
r |= SSH_WRITE_PENDING;
|
||||
}
|
||||
|
||||
if (s->data_except) {
|
||||
r |= SSH_CLOSED_ERROR;
|
||||
}
|
||||
if (s->data_except) {
|
||||
r |= SSH_CLOSED_ERROR;
|
||||
}
|
||||
|
||||
return r;
|
||||
return r;
|
||||
}
|
||||
|
||||
int ssh_socket_get_poll_flags(ssh_socket s) {
|
||||
int r = 0;
|
||||
if (s->poll_handle != NULL && (ssh_poll_get_events (s->poll_handle) & POLLIN) > 0) {
|
||||
r |= SSH_READ_PENDING;
|
||||
}
|
||||
if (s->poll_handle != NULL && (ssh_poll_get_events (s->poll_handle) & POLLOUT) > 0) {
|
||||
r |= SSH_WRITE_PENDING;
|
||||
}
|
||||
return r;
|
||||
int ssh_socket_get_poll_flags(ssh_socket s)
|
||||
{
|
||||
int r = 0;
|
||||
if (s->poll_handle != NULL && (ssh_poll_get_events (s->poll_handle) & POLLIN) > 0) {
|
||||
r |= SSH_READ_PENDING;
|
||||
}
|
||||
if (s->poll_handle != NULL && (ssh_poll_get_events (s->poll_handle) & POLLOUT) > 0) {
|
||||
r |= SSH_WRITE_PENDING;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
int ssh_socket_set_nonblocking(socket_t fd) {
|
||||
u_long nonblocking = 1;
|
||||
return ioctlsocket(fd, FIONBIO, &nonblocking);
|
||||
int ssh_socket_set_nonblocking(socket_t fd)
|
||||
{
|
||||
u_long nonblocking = 1;
|
||||
return ioctlsocket(fd, FIONBIO, &nonblocking);
|
||||
}
|
||||
|
||||
int ssh_socket_set_blocking(socket_t fd) {
|
||||
u_long nonblocking = 0;
|
||||
return ioctlsocket(fd, FIONBIO, &nonblocking);
|
||||
int ssh_socket_set_blocking(socket_t fd)
|
||||
{
|
||||
u_long nonblocking = 0;
|
||||
return ioctlsocket(fd, FIONBIO, &nonblocking);
|
||||
}
|
||||
|
||||
#else /* _WIN32 */
|
||||
int ssh_socket_set_nonblocking(socket_t fd) {
|
||||
return fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||
int ssh_socket_set_nonblocking(socket_t fd)
|
||||
{
|
||||
return fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||
}
|
||||
|
||||
int ssh_socket_set_blocking(socket_t fd) {
|
||||
return fcntl(fd, F_SETFL, 0);
|
||||
int ssh_socket_set_blocking(socket_t fd)
|
||||
{
|
||||
return fcntl(fd, F_SETFL, 0);
|
||||
}
|
||||
#endif /* _WIN32 */
|
||||
|
||||
@@ -782,21 +807,24 @@ int ssh_socket_set_blocking(socket_t fd) {
|
||||
* which is problematic for hosts having DNS fail-over.
|
||||
*/
|
||||
|
||||
int ssh_socket_connect(ssh_socket s, const char *host, int port, const char *bind_addr){
|
||||
socket_t fd;
|
||||
|
||||
if(s->state != SSH_SOCKET_NONE) {
|
||||
ssh_set_error(s->session, SSH_FATAL,
|
||||
"ssh_socket_connect called on socket not unconnected");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
fd=ssh_connect_host_nonblocking(s->session,host,bind_addr,port);
|
||||
SSH_LOG(SSH_LOG_PROTOCOL,"Nonblocking connection socket: %d",fd);
|
||||
if(fd == SSH_INVALID_SOCKET)
|
||||
return SSH_ERROR;
|
||||
ssh_socket_set_fd(s,fd);
|
||||
|
||||
return SSH_OK;
|
||||
int
|
||||
ssh_socket_connect(ssh_socket s, const char *host, int port, const char *bind_addr)
|
||||
{
|
||||
socket_t fd;
|
||||
|
||||
if (s->state != SSH_SOCKET_NONE) {
|
||||
ssh_set_error(s->session, SSH_FATAL,
|
||||
"ssh_socket_connect called on socket not unconnected");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
fd = ssh_connect_host_nonblocking(s->session, host, bind_addr, port);
|
||||
SSH_LOG(SSH_LOG_PROTOCOL, "Nonblocking connection socket: %d", fd);
|
||||
if (fd == SSH_INVALID_SOCKET) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
ssh_socket_set_fd(s,fd);
|
||||
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
@@ -807,16 +835,26 @@ int ssh_socket_connect(ssh_socket s, const char *host, int port, const char *bin
|
||||
* @param in input file descriptor
|
||||
* @param out output file descriptor
|
||||
*/
|
||||
void ssh_execute_command(const char *command, socket_t in, socket_t out){
|
||||
const char *args[]={"/bin/sh","-c",command,NULL};
|
||||
/* redirect in and out to stdin, stdout and stderr */
|
||||
dup2(in, 0);
|
||||
dup2(out,1);
|
||||
dup2(out,2);
|
||||
close(in);
|
||||
close(out);
|
||||
execv(args[0],(char * const *)args);
|
||||
exit(1);
|
||||
void
|
||||
ssh_execute_command(const char *command, socket_t in, socket_t out)
|
||||
{
|
||||
const char *args[] = {"/bin/sh", "-c", command, NULL};
|
||||
/* Prepare /dev/null socket for the stderr redirection */
|
||||
int devnull = open("/dev/null", O_WRONLY);
|
||||
if (devnull == -1) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to open stderr");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* redirect in and out to stdin, stdout */
|
||||
dup2(in, 0);
|
||||
dup2(out, 1);
|
||||
/* Ignore anything on the stderr */
|
||||
dup2(devnull, STDERR_FILENO);
|
||||
close(in);
|
||||
close(out);
|
||||
execv(args[0], (char * const *)args);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -829,37 +867,39 @@ void ssh_execute_command(const char *command, socket_t in, socket_t out){
|
||||
* @returns SSH_ERROR error while executing the command.
|
||||
*/
|
||||
|
||||
int ssh_socket_connect_proxycommand(ssh_socket s, const char *command){
|
||||
socket_t pair[2];
|
||||
int pid;
|
||||
int rc;
|
||||
int
|
||||
ssh_socket_connect_proxycommand(ssh_socket s, const char *command)
|
||||
{
|
||||
socket_t pair[2];
|
||||
int pid;
|
||||
int rc;
|
||||
|
||||
if (s->state != SSH_SOCKET_NONE) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
if (s->state != SSH_SOCKET_NONE) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
rc = socketpair(PF_UNIX, SOCK_STREAM, 0, pair);
|
||||
if (rc < 0) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
rc = socketpair(PF_UNIX, SOCK_STREAM, 0, pair);
|
||||
if (rc < 0) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
SSH_LOG(SSH_LOG_PROTOCOL,"Executing proxycommand '%s'",command);
|
||||
pid = fork();
|
||||
if(pid == 0){
|
||||
ssh_execute_command(command,pair[0],pair[0]);
|
||||
}
|
||||
close(pair[0]);
|
||||
SSH_LOG(SSH_LOG_PROTOCOL,"ProxyCommand connection pipe: [%d,%d]",pair[0],pair[1]);
|
||||
ssh_socket_set_fd(s, pair[1]);
|
||||
s->state=SSH_SOCKET_CONNECTED;
|
||||
s->fd_is_socket=0;
|
||||
/* POLLOUT is the event to wait for in a nonblocking connect */
|
||||
ssh_poll_set_events(ssh_socket_get_poll_handle(s), POLLIN | POLLOUT);
|
||||
if(s->callbacks && s->callbacks->connected) {
|
||||
s->callbacks->connected(SSH_SOCKET_CONNECTED_OK,0,s->callbacks->userdata);
|
||||
}
|
||||
SSH_LOG(SSH_LOG_PROTOCOL, "Executing proxycommand '%s'", command);
|
||||
pid = fork();
|
||||
if(pid == 0) {
|
||||
ssh_execute_command(command, pair[0], pair[0]);
|
||||
}
|
||||
close(pair[0]);
|
||||
SSH_LOG(SSH_LOG_PROTOCOL, "ProxyCommand connection pipe: [%d,%d]",pair[0],pair[1]);
|
||||
ssh_socket_set_fd(s, pair[1]);
|
||||
s->state=SSH_SOCKET_CONNECTED;
|
||||
s->fd_is_socket=0;
|
||||
/* POLLOUT is the event to wait for in a nonblocking connect */
|
||||
ssh_poll_set_events(ssh_socket_get_poll_handle(s), POLLIN | POLLOUT);
|
||||
if (s->callbacks && s->callbacks->connected) {
|
||||
s->callbacks->connected(SSH_SOCKET_CONNECTED_OK, 0, s->callbacks->userdata);
|
||||
}
|
||||
|
||||
return SSH_OK;
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
#endif /* _WIN32 */
|
||||
|
||||
@@ -67,8 +67,8 @@ if (CLIENT_TESTING)
|
||||
find_program(SSH_EXECUTABLE NAMES ssh)
|
||||
if (SSH_EXECUTABLE)
|
||||
execute_process(COMMAND ${SSH_EXECUTABLE} -V ERROR_VARIABLE OPENSSH_VERSION_STR)
|
||||
string(REGEX REPLACE "^OpenSSH_([0-9]).[0-9].*$" "\\1" OPENSSH_VERSION_MAJOR "${OPENSSH_VERSION_STR}")
|
||||
string(REGEX REPLACE "^OpenSSH_[0-9].([0-9]).*$" "\\1" OPENSSH_VERSION_MINOR "${OPENSSH_VERSION_STR}")
|
||||
string(REGEX REPLACE "^.*OpenSSH_([0-9]).[0-9].*$" "\\1" OPENSSH_VERSION_MAJOR "${OPENSSH_VERSION_STR}")
|
||||
string(REGEX REPLACE "^.*OpenSSH_[0-9].([0-9]).*$" "\\1" OPENSSH_VERSION_MINOR "${OPENSSH_VERSION_STR}")
|
||||
add_definitions(-DOPENSSH_VERSION_MAJOR=${OPENSSH_VERSION_MAJOR} -DOPENSSH_VERSION_MINOR=${OPENSSH_VERSION_MINOR})
|
||||
endif()
|
||||
|
||||
|
||||
@@ -14,7 +14,8 @@ set(LIBSSH_CLIENT_TESTS
|
||||
torture_knownhosts_verify
|
||||
torture_proxycommand
|
||||
torture_session
|
||||
torture_request_env)
|
||||
torture_request_env
|
||||
torture_client_global_requests)
|
||||
|
||||
if (DEFAULT_C_NO_DEPRECATION_FLAGS)
|
||||
set_source_files_properties(torture_knownhosts.c
|
||||
|
||||
152
tests/client/torture_client_global_requests.c
Normal file
152
tests/client/torture_client_global_requests.c
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* torture_client_global_requests.c - Tests for client global requests
|
||||
*
|
||||
* This file is part of the SSH Library
|
||||
*
|
||||
* Copyright (c) 2019 by Red Hat, Inc.
|
||||
*
|
||||
* Author: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
|
||||
*
|
||||
* The SSH Library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* The SSH Library 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 Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with the SSH Library; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
* MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#define LIBSSH_STATIC
|
||||
|
||||
#include "torture.h"
|
||||
#include "libssh/libssh.h"
|
||||
#include "libssh/priv.h"
|
||||
#include "libssh/session.h"
|
||||
#include "libssh/channels.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <pwd.h>
|
||||
|
||||
static int sshd_setup(void **state)
|
||||
{
|
||||
torture_setup_sshd_server(state, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sshd_teardown(void **state)
|
||||
{
|
||||
torture_teardown_sshd_server(state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int session_setup(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
int verbosity = torture_libssh_verbosity();
|
||||
struct passwd *pwd;
|
||||
bool b = false;
|
||||
int rc;
|
||||
|
||||
pwd = getpwnam("bob");
|
||||
assert_non_null(pwd);
|
||||
|
||||
rc = setuid(pwd->pw_uid);
|
||||
assert_return_code(rc, errno);
|
||||
|
||||
s->ssh.session = ssh_new();
|
||||
assert_non_null(s->ssh.session);
|
||||
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER);
|
||||
|
||||
/* Make sure no other configuration options from system will get used */
|
||||
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_PROCESS_CONFIG, &b);
|
||||
assert_ssh_return_code(s->ssh.session, rc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int session_teardown(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
|
||||
ssh_disconnect(s->ssh.session);
|
||||
ssh_free(s->ssh.session);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int authenticate(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
ssh_session session = s->ssh.session;
|
||||
int rc;
|
||||
|
||||
rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_BOB);
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
|
||||
rc = ssh_connect(session);
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
|
||||
rc = ssh_userauth_password(session, NULL, TORTURE_SSH_USER_BOB_PASSWORD);
|
||||
assert_int_equal(rc, SSH_AUTH_SUCCESS);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void torture_unknown_request(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
ssh_session session = s->ssh.session;
|
||||
ssh_channel channel;
|
||||
int rc;
|
||||
|
||||
rc = authenticate(state);
|
||||
assert_ssh_return_code(session, rc);
|
||||
|
||||
/* Request asking for reply */
|
||||
rc = ssh_global_request(session, "unknown-request-00@test.com", NULL, 1);
|
||||
assert_ssh_return_code_equal(session, rc, SSH_ERROR);
|
||||
|
||||
/* Request and don't ask for reply */
|
||||
rc = ssh_global_request(session, "another-bad-req-00@test.com", NULL, 0);
|
||||
assert_ssh_return_code(session, rc);
|
||||
|
||||
/* Open channel to make sure the session is still working */
|
||||
channel = ssh_channel_new(session);
|
||||
assert_non_null(channel);
|
||||
|
||||
rc = ssh_channel_open_session(channel);
|
||||
assert_ssh_return_code(session, rc);
|
||||
|
||||
ssh_channel_close(channel);
|
||||
}
|
||||
|
||||
int torture_run_tests(void)
|
||||
{
|
||||
int rc;
|
||||
struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test_setup_teardown(torture_unknown_request,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
};
|
||||
|
||||
ssh_init();
|
||||
torture_filter_tests(tests);
|
||||
rc = cmocka_run_group_tests(tests, sshd_setup, sshd_teardown);
|
||||
ssh_finalize();
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -115,6 +115,28 @@ static void torture_options_set_proxycommand_ssh(void **state)
|
||||
assert_int_equal(rc & O_RDWR, O_RDWR);
|
||||
}
|
||||
|
||||
static void torture_options_set_proxycommand_ssh_stderr(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
ssh_session session = s->ssh.session;
|
||||
const char *address = torture_server_address(AF_INET);
|
||||
char command[255] = {0};
|
||||
int rc;
|
||||
socket_t fd;
|
||||
|
||||
rc = snprintf(command, sizeof(command), "ssh -vvv -W [%%h]:%%p alice@%s", address);
|
||||
assert_true((size_t)rc < sizeof(command));
|
||||
|
||||
rc = ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, command);
|
||||
assert_int_equal(rc, 0);
|
||||
rc = ssh_connect(session);
|
||||
assert_ssh_return_code(session, rc);
|
||||
fd = ssh_get_fd(session);
|
||||
assert_true(fd != SSH_INVALID_SOCKET);
|
||||
rc = fcntl(fd, F_GETFL);
|
||||
assert_int_equal(rc & O_RDWR, O_RDWR);
|
||||
}
|
||||
|
||||
int torture_run_tests(void) {
|
||||
int rc;
|
||||
struct CMUnitTest tests[] = {
|
||||
@@ -127,6 +149,9 @@ int torture_run_tests(void) {
|
||||
cmocka_unit_test_setup_teardown(torture_options_set_proxycommand_ssh,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_options_set_proxycommand_ssh_stderr,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -261,7 +261,7 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
|
||||
f(client, ecdsa_256_diffie_hellman_group_exchange_sha256, kexcmd(GEX_SHA256), setup_ecdsa_256, teardown) \
|
||||
f(client, ecdsa_384_diffie_hellman_group_exchange_sha256, kexcmd(GEX_SHA256), setup_ecdsa_384, teardown) \
|
||||
f(client, ecdsa_521_diffie_hellman_group_exchange_sha256, kexcmd(GEX_SHA256), setup_ecdsa_521, teardown)
|
||||
#elif /* !defined(WITH_GEX) */
|
||||
#else /* !defined(WITH_GEX) */
|
||||
#define PKDTESTS_KEX_FIPS(f, client, kexcmd) \
|
||||
f(client, rsa_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_rsa, teardown) \
|
||||
f(client, rsa_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_rsa, teardown) \
|
||||
|
||||
@@ -473,6 +473,50 @@ static void torture_server_hostkey_mismatch(void **state)
|
||||
assert_int_equal(found, SSH_KNOWN_HOSTS_OK);
|
||||
}
|
||||
|
||||
static void torture_server_unknown_global_request(void **state)
|
||||
{
|
||||
struct test_server_st *tss = *state;
|
||||
struct torture_state *s = NULL;
|
||||
ssh_session session = NULL;
|
||||
ssh_channel channel;
|
||||
int rc;
|
||||
|
||||
assert_non_null(tss);
|
||||
|
||||
s = tss->state;
|
||||
assert_non_null(s);
|
||||
|
||||
session = s->ssh.session;
|
||||
assert_non_null(session);
|
||||
|
||||
rc = ssh_options_set(session, SSH_OPTIONS_USER, SSHD_DEFAULT_USER);
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
|
||||
rc = ssh_connect(session);
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
|
||||
/* Using the default password for the server */
|
||||
rc = ssh_userauth_password(session, NULL, SSHD_DEFAULT_PASSWORD);
|
||||
assert_int_equal(rc, SSH_AUTH_SUCCESS);
|
||||
|
||||
/* Request asking for reply */
|
||||
rc = ssh_global_request(session, "unknown-request-00@test.com", NULL, 1);
|
||||
assert_ssh_return_code_equal(session, rc, SSH_ERROR);
|
||||
|
||||
/* Request and don't ask for reply */
|
||||
rc = ssh_global_request(session, "another-bad-req-00@test.com", NULL, 0);
|
||||
assert_ssh_return_code(session, rc);
|
||||
|
||||
/* Open channel to make sure the session is still working */
|
||||
channel = ssh_channel_new(session);
|
||||
assert_non_null(channel);
|
||||
|
||||
rc = ssh_channel_open_session(channel);
|
||||
assert_ssh_return_code(session, rc);
|
||||
|
||||
ssh_channel_close(channel);
|
||||
}
|
||||
|
||||
int torture_run_tests(void) {
|
||||
int rc;
|
||||
struct CMUnitTest tests[] = {
|
||||
@@ -488,6 +532,9 @@ int torture_run_tests(void) {
|
||||
cmocka_unit_test_setup_teardown(torture_server_hostkey_mismatch,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_server_unknown_global_request,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
};
|
||||
|
||||
ssh_init();
|
||||
|
||||
@@ -381,6 +381,7 @@ static void torture_knownhosts_host_exists(void **state)
|
||||
|
||||
ssh_options_set(session, SSH_OPTIONS_HOST, "localhost");
|
||||
ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, knownhosts_file);
|
||||
|
||||
/* This makes sure the system's known_hosts are not used */
|
||||
ssh_options_set(session, SSH_OPTIONS_GLOBAL_KNOWNHOSTS, "/dev/null");
|
||||
|
||||
@@ -388,6 +389,14 @@ static void torture_knownhosts_host_exists(void **state)
|
||||
assert_int_equal(found, SSH_KNOWN_HOSTS_OK);
|
||||
assert_true(found == SSH_KNOWN_HOSTS_OK);
|
||||
|
||||
/* This makes sure the check will not fail when the system's known_hosts is
|
||||
* not accessible*/
|
||||
ssh_options_set(session, SSH_OPTIONS_GLOBAL_KNOWNHOSTS, "./unaccessible");
|
||||
|
||||
found = ssh_session_has_known_hosts_entry(session);
|
||||
assert_int_equal(found, SSH_KNOWN_HOSTS_OK);
|
||||
assert_true(found == SSH_KNOWN_HOSTS_OK);
|
||||
|
||||
ssh_options_set(session, SSH_OPTIONS_HOST, "wurstbrot");
|
||||
found = ssh_session_has_known_hosts_entry(session);
|
||||
assert_true(found == SSH_KNOWN_HOSTS_UNKNOWN);
|
||||
|
||||
@@ -168,6 +168,37 @@ static void torture_pki_dsa_import_privkey_base64(void **state)
|
||||
SSH_KEY_FREE(key);
|
||||
}
|
||||
|
||||
static void torture_pki_dsa_import_privkey_base64_comment(void **state)
|
||||
{
|
||||
int rc, file_str_len;
|
||||
ssh_key key = NULL;
|
||||
const char *passphrase = torture_get_testkey_passphrase();
|
||||
const char *comment_str = "#this is line-comment\n#this is another\n";
|
||||
const char *key_str = NULL;
|
||||
char *file_str = NULL;
|
||||
|
||||
(void) state; /* unused */
|
||||
|
||||
key_str = torture_get_testkey(SSH_KEYTYPE_DSS, 0);
|
||||
assert_non_null(key_str);
|
||||
|
||||
file_str_len = strlen(comment_str) + strlen(key_str) + 1;
|
||||
file_str = malloc(file_str_len);
|
||||
assert_non_null(file_str);
|
||||
rc = snprintf(file_str, file_str_len, "%s%s", comment_str, key_str);
|
||||
assert_int_equal(rc, file_str_len - 1);
|
||||
|
||||
rc = ssh_pki_import_privkey_base64(file_str,
|
||||
passphrase,
|
||||
NULL,
|
||||
NULL,
|
||||
&key);
|
||||
assert_true(rc == 0);
|
||||
|
||||
free(file_str);
|
||||
SSH_KEY_FREE(key);
|
||||
}
|
||||
|
||||
static int test_sign_verify_data(ssh_key key,
|
||||
enum ssh_digest_e hash_type,
|
||||
const unsigned char *input,
|
||||
@@ -833,6 +864,9 @@ int torture_run_tests(void)
|
||||
cmocka_unit_test_setup_teardown(torture_pki_dsa_import_privkey_base64,
|
||||
setup_dsa_key,
|
||||
teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_pki_dsa_import_privkey_base64_comment,
|
||||
setup_dsa_key,
|
||||
teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_pki_dsa_import_privkey_base64,
|
||||
setup_openssh_dsa_key,
|
||||
teardown),
|
||||
|
||||
@@ -241,6 +241,37 @@ static void torture_pki_ecdsa_import_privkey_base64(void **state)
|
||||
SSH_KEY_FREE(key);
|
||||
}
|
||||
|
||||
static void torture_pki_ecdsa_import_privkey_base64_comment(void **state)
|
||||
{
|
||||
int rc, file_str_len;
|
||||
const char *comment_str = "#this is line-comment\n#this is another\n";
|
||||
char *key_str = NULL, *file_str = NULL;
|
||||
ssh_key key = NULL;
|
||||
const char *passphrase = torture_get_testkey_passphrase();
|
||||
|
||||
(void) state; /* unused */
|
||||
|
||||
key_str = torture_pki_read_file(LIBSSH_ECDSA_TESTKEY);
|
||||
assert_non_null(key_str);
|
||||
|
||||
file_str_len = strlen(comment_str) + strlen(key_str) + 1;
|
||||
file_str = malloc(file_str_len);
|
||||
assert_non_null(file_str);
|
||||
rc = snprintf(file_str, file_str_len, "%s%s", comment_str, key_str);
|
||||
assert_int_equal(rc, file_str_len - 1);
|
||||
|
||||
rc = ssh_pki_import_privkey_base64(file_str, passphrase, NULL, NULL, &key);
|
||||
assert_true(rc == 0);
|
||||
assert_non_null(key);
|
||||
|
||||
rc = ssh_key_is_private(key);
|
||||
assert_true(rc == 1);
|
||||
|
||||
free(key_str);
|
||||
free(file_str);
|
||||
SSH_KEY_FREE(key);
|
||||
}
|
||||
|
||||
static void torture_pki_ecdsa_publickey_from_privatekey(void **state)
|
||||
{
|
||||
int rc;
|
||||
@@ -904,6 +935,15 @@ int torture_run_tests(void) {
|
||||
cmocka_unit_test_setup_teardown(torture_pki_ecdsa_import_privkey_base64,
|
||||
setup_ecdsa_key_521,
|
||||
teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_pki_ecdsa_import_privkey_base64_comment,
|
||||
setup_ecdsa_key_256,
|
||||
teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_pki_ecdsa_import_privkey_base64_comment,
|
||||
setup_ecdsa_key_384,
|
||||
teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_pki_ecdsa_import_privkey_base64_comment,
|
||||
setup_ecdsa_key_521,
|
||||
teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_pki_ecdsa_import_privkey_base64,
|
||||
setup_openssh_ecdsa_key_256,
|
||||
teardown),
|
||||
|
||||
@@ -213,6 +213,44 @@ static void torture_pki_rsa_import_privkey_base64(void **state)
|
||||
SSH_KEY_FREE(key);
|
||||
}
|
||||
|
||||
static void torture_pki_rsa_import_privkey_base64_comment(void **state)
|
||||
{
|
||||
int rc, file_str_len;
|
||||
const char *comment_str = "#this is line-comment\n#this is another\n";
|
||||
char *key_str = NULL, *file_str = NULL;
|
||||
ssh_key key = NULL;
|
||||
const char *passphrase = torture_get_testkey_passphrase();
|
||||
enum ssh_keytypes_e type;
|
||||
|
||||
(void) state; /* unused */
|
||||
|
||||
key_str = torture_pki_read_file(LIBSSH_RSA_TESTKEY);
|
||||
assert_non_null(key_str);
|
||||
|
||||
file_str_len = strlen(comment_str) + strlen(key_str) + 1;
|
||||
file_str = malloc(file_str_len);
|
||||
assert_non_null(file_str);
|
||||
rc = snprintf(file_str, file_str_len, "%s%s", comment_str, key_str);
|
||||
assert_int_equal(rc, file_str_len - 1);
|
||||
|
||||
rc = ssh_pki_import_privkey_base64(file_str, passphrase, NULL, NULL, &key);
|
||||
assert_true(rc == 0);
|
||||
assert_non_null(key);
|
||||
|
||||
type = ssh_key_type(key);
|
||||
assert_true(type == SSH_KEYTYPE_RSA);
|
||||
|
||||
rc = ssh_key_is_private(key);
|
||||
assert_true(rc == 1);
|
||||
|
||||
rc = ssh_key_is_public(key);
|
||||
assert_true(rc == 1);
|
||||
|
||||
free(key_str);
|
||||
free(file_str);
|
||||
SSH_KEY_FREE(key);
|
||||
}
|
||||
|
||||
static void torture_pki_rsa_publickey_from_privatekey(void **state)
|
||||
{
|
||||
int rc;
|
||||
@@ -468,21 +506,23 @@ static void torture_pki_rsa_generate_key(void **state)
|
||||
ssh_session session=ssh_new();
|
||||
(void) state;
|
||||
|
||||
rc = ssh_pki_generate(SSH_KEYTYPE_RSA, 1024, &key);
|
||||
assert_true(rc == SSH_OK);
|
||||
assert_non_null(key);
|
||||
rc = ssh_pki_export_privkey_to_pubkey(key, &pubkey);
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
assert_non_null(pubkey);
|
||||
sign = pki_do_sign(key, INPUT, sizeof(INPUT), SSH_DIGEST_SHA256);
|
||||
assert_non_null(sign);
|
||||
rc = pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT));
|
||||
assert_true(rc == SSH_OK);
|
||||
ssh_signature_free(sign);
|
||||
SSH_KEY_FREE(key);
|
||||
SSH_KEY_FREE(pubkey);
|
||||
key = NULL;
|
||||
pubkey = NULL;
|
||||
if (!ssh_fips_mode()) {
|
||||
rc = ssh_pki_generate(SSH_KEYTYPE_RSA, 1024, &key);
|
||||
assert_true(rc == SSH_OK);
|
||||
assert_non_null(key);
|
||||
rc = ssh_pki_export_privkey_to_pubkey(key, &pubkey);
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
assert_non_null(pubkey);
|
||||
sign = pki_do_sign(key, INPUT, sizeof(INPUT), SSH_DIGEST_SHA256);
|
||||
assert_non_null(sign);
|
||||
rc = pki_signature_verify(session, sign, pubkey, INPUT, sizeof(INPUT));
|
||||
assert_true(rc == SSH_OK);
|
||||
ssh_signature_free(sign);
|
||||
SSH_KEY_FREE(key);
|
||||
SSH_KEY_FREE(pubkey);
|
||||
key = NULL;
|
||||
pubkey = NULL;
|
||||
}
|
||||
|
||||
rc = ssh_pki_generate(SSH_KEYTYPE_RSA, 2048, &key);
|
||||
assert_true(rc == SSH_OK);
|
||||
@@ -877,6 +917,9 @@ int torture_run_tests(void) {
|
||||
cmocka_unit_test_setup_teardown(torture_pki_rsa_import_privkey_base64,
|
||||
setup_rsa_key,
|
||||
teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_pki_rsa_import_privkey_base64_comment,
|
||||
setup_rsa_key,
|
||||
teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_pki_rsa_import_privkey_base64,
|
||||
setup_openssh_rsa_key,
|
||||
teardown),
|
||||
|
||||
Reference in New Issue
Block a user