diff options
-rw-r--r-- | src/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/bind.c | 372 | ||||
-rw-r--r-- | src/server.c | 322 |
3 files changed, 373 insertions, 322 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 83aaf45a..48b05017 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -138,6 +138,7 @@ if (WITH_SERVER) set(libssh_SRCS ${libssh_SRCS} server.c + bind.c ) endif (WITH_SERVER) diff --git a/src/bind.c b/src/bind.c new file mode 100644 index 00000000..27ad6b55 --- /dev/null +++ b/src/bind.c @@ -0,0 +1,372 @@ +/* + * bind.c : all ssh_bind functions + * + * This file is part of the SSH Library + * + * Copyright (c) 2004-2005 by Aris Adamantiadis + * + * 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" + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "libssh/priv.h" +#include "libssh/libssh.h" +#include "libssh/server.h" +#include "libssh/keyfiles.h" +#include "libssh/buffer.h" +#include "libssh/socket.h" +#include "libssh/session.h" + +/** + * @addtogroup libssh_server + * + * @{ + */ + + +#ifdef _WIN32 + +#include <winsock2.h> +#define SOCKOPT_TYPE_ARG4 char + +/* We need to provide hstrerror. Not we can't call the parameter h_errno because it's #defined */ +static char *hstrerror(int h_errno_val) { + static char text[50] = {0}; + + snprintf(text, sizeof(text), "gethostbyname error %d\n", h_errno_val); + + return text; +} +#else /* _WIN32 */ + +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#define SOCKOPT_TYPE_ARG4 int + +#endif /* _WIN32 */ + +/* TODO FIXME: must use getaddrinfo */ +static socket_t bind_socket(ssh_bind sshbind, const char *hostname, + int port) { + struct sockaddr_in myaddr; + struct hostent *hp=NULL; + socket_t s; + int opt = 1; + + s = socket(PF_INET, SOCK_STREAM, 0); + if (s < 0) { + ssh_set_error(sshbind, SSH_FATAL, "%s", strerror(errno)); + return -1; + } + +#ifdef HAVE_GETHOSTBYNAME + hp = gethostbyname(hostname); +#endif + + if (hp == NULL) { + ssh_set_error(sshbind, SSH_FATAL, + "Resolving %s: %s", hostname, hstrerror(h_errno)); + close(s); + return -1; + } + + memset(&myaddr, 0, sizeof(myaddr)); + memcpy(&myaddr.sin_addr, hp->h_addr, hp->h_length); + myaddr.sin_family = hp->h_addrtype; + myaddr.sin_port = htons(port); + + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0) { + ssh_set_error(sshbind, SSH_FATAL, + "Setting socket options failed: %s", hstrerror(h_errno)); + close(s); + return -1; + } + + if (bind(s, (struct sockaddr *) &myaddr, sizeof(myaddr)) < 0) { + ssh_set_error(sshbind, SSH_FATAL, "Binding to %s:%d: %s", + hostname, + port, + strerror(errno)); + close(s); + return -1; + } + + return s; +} + +ssh_bind ssh_bind_new(void) { + ssh_bind ptr; + + ptr = malloc(sizeof(struct ssh_bind_struct)); + if (ptr == NULL) { + return NULL; + } + ZERO_STRUCTP(ptr); + ptr->bindfd = SSH_INVALID_SOCKET; + ptr->bindport= 22; + ptr->log_verbosity = 0; + + return ptr; +} + +int ssh_bind_listen(ssh_bind sshbind) { + const char *host; + socket_t fd; + + if (ssh_init() < 0) { + return -1; + } + + host = sshbind->bindaddr; + if (host == NULL) { + host = "0.0.0.0"; + } + + fd = bind_socket(sshbind, host, sshbind->bindport); + if (fd == SSH_INVALID_SOCKET) { + return -1; + } + sshbind->bindfd = fd; + + if (listen(fd, 10) < 0) { + ssh_set_error(sshbind, SSH_FATAL, + "Listening to socket %d: %s", + fd, strerror(errno)); + close(fd); + return -1; + } + + return 0; +} + +/** + * @brief set the bind callbacks for ssh_bind + * @code + * struct ssh_callbacks_struct cb = { + * .userdata = data, + * .auth_function = my_auth_function + * }; + * ssh_callbacks_init(&cb); + * ssh_set_callbacks(session, &cb); + * @endcode + * @param sshbind the ssh_bind structure to set + * @param callbacks a ssh_bind_callbacks instance already set up. Do + * use ssh_callbacks_init() to initialize it. + * @param userdata userdata to be used with each callback called + * within callbacks. + * @returns SSH_OK on success, + * SSH_ERROR on error. + */ + +int ssh_bind_set_callbacks(ssh_bind sshbind, ssh_bind_callbacks callbacks, + void *userdata){ + if (sshbind == NULL || callbacks == NULL) { + return SSH_ERROR; + } + if(callbacks->size <= 0 || callbacks->size > 1024 * sizeof(void *)){ + ssh_set_error(sshbind,SSH_FATAL, + "Invalid callback passed in (badly initialized)"); + return SSH_ERROR; + } + sshbind->bind_callbacks = callbacks; + sshbind->bind_callbacks_userdata=userdata; + return 0; +} + +/** @internal + * @brief callback being called by poll when an event happens + * + */ +static int ssh_bind_poll_callback(ssh_poll_handle sshpoll, + socket_t fd, int revents, void *user){ + ssh_bind sshbind=(ssh_bind)user; + (void)sshpoll; + (void)fd; + + if(revents & POLLIN){ + /* new incoming connection */ + if(ssh_callbacks_exists(sshbind->bind_callbacks,incoming_connection)){ + sshbind->bind_callbacks->incoming_connection(sshbind, + sshbind->bind_callbacks_userdata); + } + } + return 0; +} + +/** @internal + * @brief returns the current poll handle, or create it + * @param sshbind the ssh_bind object + * @returns a ssh_poll handle suitable for operation + */ +ssh_poll_handle ssh_bind_get_poll(ssh_bind sshbind){ + if(sshbind->poll) + return sshbind->poll; + sshbind->poll=ssh_poll_new(sshbind->bindfd,POLLIN, + ssh_bind_poll_callback,sshbind); + return sshbind->poll; +} + +void ssh_bind_set_blocking(ssh_bind sshbind, int blocking) { + sshbind->blocking = blocking ? 1 : 0; +} + +socket_t ssh_bind_get_fd(ssh_bind sshbind) { + return sshbind->bindfd; +} + +void ssh_bind_set_fd(ssh_bind sshbind, socket_t fd) { + sshbind->bindfd = fd; +} + +void ssh_bind_fd_toaccept(ssh_bind sshbind) { + sshbind->toaccept = 1; +} + +void ssh_bind_free(ssh_bind sshbind){ + int i; + + if (sshbind == NULL) { + return; + } + + if (sshbind->bindfd >= 0) { +#ifdef _WIN32 + closesocket(sshbind->bindfd); +#else + close(sshbind->bindfd); +#endif + } + sshbind->bindfd = SSH_INVALID_SOCKET; + + /* options */ + SAFE_FREE(sshbind->banner); + SAFE_FREE(sshbind->dsakey); + SAFE_FREE(sshbind->rsakey); + SAFE_FREE(sshbind->bindaddr); + + for (i = 0; i < 10; i++) { + if (sshbind->wanted_methods[i]) { + SAFE_FREE(sshbind->wanted_methods[i]); + } + } + + SAFE_FREE(sshbind); +} + + +int ssh_bind_accept(ssh_bind sshbind, ssh_session session) { + ssh_private_key dsa = NULL; + ssh_private_key rsa = NULL; + socket_t fd = SSH_INVALID_SOCKET; + int i; + + if (sshbind->bindfd == SSH_INVALID_SOCKET) { + ssh_set_error(sshbind, SSH_FATAL, + "Can't accept new clients on a not bound socket."); + return SSH_ERROR; + } + if(session == NULL){ + ssh_set_error(sshbind, SSH_FATAL,"session is null"); + return SSH_ERROR; + } + if (sshbind->dsakey == NULL && sshbind->rsakey == NULL) { + ssh_set_error(sshbind, SSH_FATAL, + "DSA or RSA host key file must be set before accept()"); + return SSH_ERROR; + } + + if (sshbind->dsakey) { + dsa = _privatekey_from_file(sshbind, sshbind->dsakey, SSH_KEYTYPE_DSS); + if (dsa == NULL) { + return SSH_ERROR; + } + } + + if (sshbind->rsakey) { + rsa = _privatekey_from_file(sshbind, sshbind->rsakey, SSH_KEYTYPE_RSA); + if (rsa == NULL) { + privatekey_free(dsa); + return SSH_ERROR; + } + } + + fd = accept(sshbind->bindfd, NULL, NULL); + if (fd == SSH_INVALID_SOCKET) { + ssh_set_error(sshbind, SSH_FATAL, + "Accepting a new connection: %s", + strerror(errno)); + privatekey_free(dsa); + privatekey_free(rsa); + return SSH_ERROR; + } + + session->server = 1; + session->version = 2; + + /* copy options */ + for (i = 0; i < 10; ++i) { + if (sshbind->wanted_methods[i]) { + session->wanted_methods[i] = strdup(sshbind->wanted_methods[i]); + if (session->wanted_methods[i] == NULL) { + privatekey_free(dsa); + privatekey_free(rsa); + return SSH_ERROR; + } + } + } + + if (sshbind->bindaddr == NULL) + session->bindaddr = NULL; + else { + SAFE_FREE(session->bindaddr); + session->bindaddr = strdup(sshbind->bindaddr); + if (session->bindaddr == NULL) { + privatekey_free(dsa); + privatekey_free(rsa); + return SSH_ERROR; + } + } + + session->log_verbosity = sshbind->log_verbosity; + + ssh_socket_free(session->socket); + session->socket = ssh_socket_new(session); + if (session->socket == NULL) { + privatekey_free(dsa); + privatekey_free(rsa); + return SSH_ERROR; + } + ssh_socket_set_fd(session->socket, fd); + ssh_socket_get_poll_handle_out(session->socket); + session->dsa_key = dsa; + session->rsa_key = rsa; + +return SSH_OK; +} + + +/** + * @} + */ diff --git a/src/server.c b/src/server.c index 10239773..55b677f6 100644 --- a/src/server.c +++ b/src/server.c @@ -43,8 +43,6 @@ #include "libssh/keys.h" #include "libssh/dh.h" #include "libssh/messages.h" -#include "libssh/misc.h" -#include "libssh/poll.h" #define set_status(session, status) do {\ if (session->callbacks && session->callbacks->connect_status_function) \ @@ -60,236 +58,6 @@ static int dh_handshake_server(ssh_session session); * @{ */ -#ifdef _WIN32 - -#include <winsock2.h> -#define SOCKOPT_TYPE_ARG4 char - -/* We need to provide hstrerror. Not we can't call the parameter h_errno because it's #defined */ -static char *hstrerror(int h_errno_val) { - static char text[50] = {0}; - - snprintf(text, sizeof(text), "gethostbyname error %d\n", h_errno_val); - - return text; -} -#else /* _WIN32 */ - -#include <sys/socket.h> -#include <netinet/in.h> -#include <netdb.h> -#define SOCKOPT_TYPE_ARG4 int - -#endif /* _WIN32 */ - -/* TODO FIXME: must use getaddrinfo */ -static socket_t bind_socket(ssh_bind sshbind, const char *hostname, - int port) { - struct sockaddr_in myaddr; - struct hostent *hp=NULL; - socket_t s; - int opt = 1; - - s = socket(PF_INET, SOCK_STREAM, 0); - if (s < 0) { - ssh_set_error(sshbind, SSH_FATAL, "%s", strerror(errno)); - return -1; - } - -#ifdef HAVE_GETHOSTBYNAME - hp = gethostbyname(hostname); -#endif - - if (hp == NULL) { - ssh_set_error(sshbind, SSH_FATAL, - "Resolving %s: %s", hostname, hstrerror(h_errno)); - close(s); - return -1; - } - - memset(&myaddr, 0, sizeof(myaddr)); - memcpy(&myaddr.sin_addr, hp->h_addr, hp->h_length); - myaddr.sin_family = hp->h_addrtype; - myaddr.sin_port = htons(port); - - if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0) { - ssh_set_error(sshbind, SSH_FATAL, - "Setting socket options failed: %s", hstrerror(h_errno)); - close(s); - return -1; - } - - if (bind(s, (struct sockaddr *) &myaddr, sizeof(myaddr)) < 0) { - ssh_set_error(sshbind, SSH_FATAL, "Binding to %s:%d: %s", - hostname, - port, - strerror(errno)); - close(s); - return -1; - } - - return s; -} - -ssh_bind ssh_bind_new(void) { - ssh_bind ptr; - - ptr = malloc(sizeof(struct ssh_bind_struct)); - if (ptr == NULL) { - return NULL; - } - ZERO_STRUCTP(ptr); - ptr->bindfd = SSH_INVALID_SOCKET; - ptr->bindport= 22; - ptr->log_verbosity = 0; - - return ptr; -} - -int ssh_bind_listen(ssh_bind sshbind) { - const char *host; - socket_t fd; - - if (ssh_init() < 0) { - return -1; - } - - host = sshbind->bindaddr; - if (host == NULL) { - host = "0.0.0.0"; - } - - fd = bind_socket(sshbind, host, sshbind->bindport); - if (fd == SSH_INVALID_SOCKET) { - return -1; - } - sshbind->bindfd = fd; - - if (listen(fd, 10) < 0) { - ssh_set_error(sshbind, SSH_FATAL, - "Listening to socket %d: %s", - fd, strerror(errno)); - close(fd); - return -1; - } - - return 0; -} - -/** - * @brief set the bind callbacks for ssh_bind - * @code - * struct ssh_callbacks_struct cb = { - * .userdata = data, - * .auth_function = my_auth_function - * }; - * ssh_callbacks_init(&cb); - * ssh_set_callbacks(session, &cb); - * @endcode - * @param sshbind the ssh_bind structure to set - * @param callbacks a ssh_bind_callbacks instance already set up. Do - * use ssh_callbacks_init() to initialize it. - * @param userdata userdata to be used with each callback called - * within callbacks. - * @returns SSH_OK on success, - * SSH_ERROR on error. - */ - -int ssh_bind_set_callbacks(ssh_bind sshbind, ssh_bind_callbacks callbacks, - void *userdata){ - if (sshbind == NULL || callbacks == NULL) { - return SSH_ERROR; - } - if(callbacks->size <= 0 || callbacks->size > 1024 * sizeof(void *)){ - ssh_set_error(sshbind,SSH_FATAL, - "Invalid callback passed in (badly initialized)"); - return SSH_ERROR; - } - sshbind->bind_callbacks = callbacks; - sshbind->bind_callbacks_userdata=userdata; - return 0; -} - -/** @internal - * @brief callback being called by poll when an event happens - * - */ -static int ssh_bind_poll_callback(ssh_poll_handle sshpoll, - socket_t fd, int revents, void *user){ - ssh_bind sshbind=(ssh_bind)user; - (void)sshpoll; - (void)fd; - - if(revents & POLLIN){ - /* new incoming connection */ - if(ssh_callbacks_exists(sshbind->bind_callbacks,incoming_connection)){ - sshbind->bind_callbacks->incoming_connection(sshbind, - sshbind->bind_callbacks_userdata); - } - } - return 0; -} - -/** @internal - * @brief returns the current poll handle, or create it - * @param sshbind the ssh_bind object - * @returns a ssh_poll handle suitable for operation - */ -ssh_poll_handle ssh_bind_get_poll(ssh_bind sshbind){ - if(sshbind->poll) - return sshbind->poll; - sshbind->poll=ssh_poll_new(sshbind->bindfd,POLLIN, - ssh_bind_poll_callback,sshbind); - return sshbind->poll; -} - -void ssh_bind_set_blocking(ssh_bind sshbind, int blocking) { - sshbind->blocking = blocking ? 1 : 0; -} - -socket_t ssh_bind_get_fd(ssh_bind sshbind) { - return sshbind->bindfd; -} - -void ssh_bind_set_fd(ssh_bind sshbind, socket_t fd) { - sshbind->bindfd = fd; -} - -void ssh_bind_fd_toaccept(ssh_bind sshbind) { - sshbind->toaccept = 1; -} - -void ssh_bind_free(ssh_bind sshbind){ - int i; - - if (sshbind == NULL) { - return; - } - - if (sshbind->bindfd >= 0) { -#ifdef _WIN32 - closesocket(sshbind->bindfd); -#else - close(sshbind->bindfd); -#endif - } - sshbind->bindfd = SSH_INVALID_SOCKET; - - /* options */ - SAFE_FREE(sshbind->banner); - SAFE_FREE(sshbind->dsakey); - SAFE_FREE(sshbind->rsakey); - SAFE_FREE(sshbind->bindaddr); - - for (i = 0; i < 10; i++) { - if (sshbind->wanted_methods[i]) { - SAFE_FREE(sshbind->wanted_methods[i]); - } - } - - SAFE_FREE(sshbind); -} - extern char *supported_methods[]; /** @internal * This functions sets the Key Exchange protocols to be accepted @@ -667,96 +435,6 @@ static int callback_receive_banner(const void *data, size_t len, void *user) { return ret; } -int ssh_bind_accept(ssh_bind sshbind, ssh_session session) { - ssh_private_key dsa = NULL; - ssh_private_key rsa = NULL; - socket_t fd = SSH_INVALID_SOCKET; - int i; - - if (sshbind->bindfd == SSH_INVALID_SOCKET) { - ssh_set_error(sshbind, SSH_FATAL, - "Can't accept new clients on a not bound socket."); - return SSH_ERROR; - } - if(session == NULL){ - ssh_set_error(sshbind, SSH_FATAL,"session is null"); - return SSH_ERROR; - } - if (sshbind->dsakey == NULL && sshbind->rsakey == NULL) { - ssh_set_error(sshbind, SSH_FATAL, - "DSA or RSA host key file must be set before accept()"); - return SSH_ERROR; - } - - if (sshbind->dsakey) { - dsa = _privatekey_from_file(sshbind, sshbind->dsakey, SSH_KEYTYPE_DSS); - if (dsa == NULL) { - return SSH_ERROR; - } - } - - if (sshbind->rsakey) { - rsa = _privatekey_from_file(sshbind, sshbind->rsakey, SSH_KEYTYPE_RSA); - if (rsa == NULL) { - privatekey_free(dsa); - return SSH_ERROR; - } - } - - fd = accept(sshbind->bindfd, NULL, NULL); - if (fd == SSH_INVALID_SOCKET) { - ssh_set_error(sshbind, SSH_FATAL, - "Accepting a new connection: %s", - strerror(errno)); - privatekey_free(dsa); - privatekey_free(rsa); - return SSH_ERROR; - } - - session->server = 1; - session->version = 2; - - /* copy options */ - for (i = 0; i < 10; ++i) { - if (sshbind->wanted_methods[i]) { - session->wanted_methods[i] = strdup(sshbind->wanted_methods[i]); - if (session->wanted_methods[i] == NULL) { - privatekey_free(dsa); - privatekey_free(rsa); - return SSH_ERROR; - } - } - } - - if (sshbind->bindaddr == NULL) - session->bindaddr = NULL; - else { - SAFE_FREE(session->bindaddr); - session->bindaddr = strdup(sshbind->bindaddr); - if (session->bindaddr == NULL) { - privatekey_free(dsa); - privatekey_free(rsa); - return SSH_ERROR; - } - } - - session->log_verbosity = sshbind->log_verbosity; - - ssh_socket_free(session->socket); - session->socket = ssh_socket_new(session); - if (session->socket == NULL) { - privatekey_free(dsa); - privatekey_free(rsa); - return SSH_ERROR; - } - ssh_socket_set_fd(session->socket, fd); - ssh_socket_get_poll_handle_out(session->socket); - session->dsa_key = dsa; - session->rsa_key = rsa; - -return SSH_OK; -} - /* Do the banner and key exchange */ int ssh_handle_key_exchange(ssh_session session) { int rc; |