1 /*
2  * This file is part of RTRlib.
3  *
4  * This file is subject to the terms and conditions of the MIT license.
5  * See the file LICENSE in the top level directory for more details.
6  *
7  * Website: http://rtrlib.realmv6.org/
8  */
9 
10 #include <assert.h>
11 #include <stdbool.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <sys/time.h>
16 
17 #include <libssh/libssh.h>
18 
19 #include "rtrlib/lib/alloc_utils_private.h"
20 #include "rtrlib/lib/log_private.h"
21 #include "rtrlib/lib/utils_private.h"
22 #include "rtrlib/rtrlib_export_private.h"
23 #include "rtrlib/transport/ssh/ssh_transport_private.h"
24 #include "rtrlib/transport/transport_private.h"
25 
26 #define SSH_DBG(fmt, sock, ...) lrtr_dbg("SSH Transport(%s@%s:%u): " fmt, (sock)->config.username, (sock)->config.host, (sock)->config.port, ## __VA_ARGS__)
27 #define SSH_DBG1(a, sock) lrtr_dbg("SSH Transport(%s@%s:%u): " a, (sock)->config.username, (sock)->config.host, (sock)->config.port)
28 
29 struct tr_ssh_socket {
30     ssh_session session;
31     ssh_channel channel;
32     struct tr_ssh_config config;
33     char *ident;
34 };
35 
36 static int tr_ssh_open(void *tr_ssh_sock);
37 static void tr_ssh_close(void *tr_ssh_sock);
38 static void tr_ssh_free(struct tr_socket *tr_sock);
39 static int tr_ssh_recv(const void *tr_ssh_sock, void *buf, const size_t buf_len, const time_t timeout);
40 static int tr_ssh_send(const void *tr_ssh_sock, const void *pdu, const size_t len, const time_t timeout);
41 static int tr_ssh_recv_async(const struct tr_ssh_socket *tr_ssh_sock, void *buf, const size_t buf_len);
42 static const char *tr_ssh_ident(void *tr_ssh_sock);
43 
tr_ssh_open(void * socket)44 int tr_ssh_open(void *socket)
45 {
46     struct tr_ssh_socket *ssh_socket = socket;
47     const struct tr_ssh_config *config = &ssh_socket->config;
48 
49     assert(ssh_socket->channel == NULL);
50     assert(ssh_socket->session == NULL);
51 
52     if((ssh_socket->session = ssh_new()) == NULL) {
53         SSH_DBG1("tr_ssh_init: can't create ssh_session", ssh_socket);
54         goto error;
55     }
56 
57     const int verbosity = SSH_LOG_NOLOG;
58     ssh_options_set(ssh_socket->session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
59 
60     ssh_options_set(ssh_socket->session, SSH_OPTIONS_HOST, config->host);
61     ssh_options_set(ssh_socket->session, SSH_OPTIONS_PORT, &(config->port));
62     ssh_options_set(ssh_socket->session, SSH_OPTIONS_BINDADDR, config->bindaddr);
63     ssh_options_set(ssh_socket->session, SSH_OPTIONS_USER, config->username);
64 
65     if (config->server_hostkey_path != NULL)
66         ssh_options_set(ssh_socket->session, SSH_OPTIONS_KNOWNHOSTS, config->server_hostkey_path);
67 
68     if (config->client_privkey_path != NULL)
69         ssh_options_set(ssh_socket->session, SSH_OPTIONS_IDENTITY, config->client_privkey_path);
70 
71     if(ssh_connect(ssh_socket->session) == SSH_ERROR) {
72         SSH_DBG1("tr_ssh_init: opening SSH connection failed", ssh_socket);
73         goto error;
74     }
75 
76     //check server identity
77     if((config->server_hostkey_path != NULL) && (ssh_is_server_known(ssh_socket->session) != SSH_SERVER_KNOWN_OK)) {
78         SSH_DBG1("tr_ssh_init: Wrong hostkey", ssh_socket);
79         goto error;
80     }
81 
82 #if LIBSSH_VERSION_MAJOR > 0 || LIBSSH_VERSION_MINOR > 5
83     const int rtval = ssh_userauth_publickey_auto(ssh_socket->session, NULL, NULL);
84 #else /* else use libSSH version 0.5.0 */
85     const int rtval = ssh_userauth_autopubkey(ssh_socket->session, NULL);
86 #endif
87     if(rtval != SSH_AUTH_SUCCESS) {
88         SSH_DBG1("tr_ssh_init: Authentication failed", ssh_socket);
89         goto error;
90     }
91 
92     if((ssh_socket->channel = ssh_channel_new(ssh_socket->session)) == NULL)
93         goto error;
94 
95     if(ssh_channel_open_session(ssh_socket->channel) == SSH_ERROR)
96         goto error;
97 
98     if(ssh_channel_request_subsystem(ssh_socket->channel, "rpki-rtr") == SSH_ERROR) {
99         SSH_DBG1("tr_ssh_init: Error requesting subsystem rpki-rtr", ssh_socket);
100         goto error;
101     }
102     SSH_DBG1("Connection established", ssh_socket);
103 
104     return TR_SUCCESS;
105 
106 error:
107     tr_ssh_close(ssh_socket);
108     return TR_ERROR;
109 
110 }
111 
tr_ssh_close(void * tr_ssh_sock)112 void tr_ssh_close(void *tr_ssh_sock)
113 {
114     struct tr_ssh_socket *socket = tr_ssh_sock;
115 
116     if(socket->channel != NULL) {
117         if(ssh_channel_is_open(socket->channel))
118             ssh_channel_close(socket->channel);
119         ssh_channel_free(socket->channel);
120         socket->channel = NULL;
121     }
122     if(socket->session != NULL) {
123         ssh_disconnect(socket->session);
124         ssh_free(socket->session);
125         socket->session = NULL;
126     }
127     SSH_DBG1("Socket closed", socket);
128 }
129 
tr_ssh_free(struct tr_socket * tr_sock)130 void tr_ssh_free(struct tr_socket *tr_sock)
131 {
132     struct tr_ssh_socket *tr_ssh_sock = tr_sock->socket;
133     assert(tr_ssh_sock->channel == NULL);
134     assert(tr_ssh_sock->session == NULL);
135 
136     SSH_DBG1("Freeing socket", tr_ssh_sock);
137 
138     lrtr_free(tr_ssh_sock->config.host);
139     lrtr_free(tr_ssh_sock->config.bindaddr);
140     lrtr_free(tr_ssh_sock->config.username);
141     lrtr_free(tr_ssh_sock->config.client_privkey_path);
142     lrtr_free(tr_ssh_sock->config.server_hostkey_path);
143 
144     if (tr_ssh_sock->ident != NULL)
145         lrtr_free(tr_ssh_sock->ident);
146     lrtr_free(tr_ssh_sock);
147     tr_sock->socket = NULL;
148 }
149 
tr_ssh_recv_async(const struct tr_ssh_socket * tr_ssh_sock,void * buf,const size_t buf_len)150 int tr_ssh_recv_async(const struct tr_ssh_socket *tr_ssh_sock, void *buf, const size_t buf_len)
151 {
152     const int rtval = ssh_channel_read_nonblocking(tr_ssh_sock->channel, buf, buf_len, false);
153     if(rtval == 0) {
154         if(ssh_channel_is_eof(tr_ssh_sock->channel) != 0) {
155             SSH_DBG1("remote has sent EOF", tr_ssh_sock);
156             return TR_CLOSED;
157         } else {
158             return TR_WOULDBLOCK;
159         }
160     } else if(rtval == SSH_ERROR) {
161         SSH_DBG1("recv(..) error", tr_ssh_sock);
162         return TR_ERROR;
163     }
164     return rtval;
165 }
166 
tr_ssh_recv(const void * tr_ssh_sock,void * buf,const size_t buf_len,const time_t timeout)167 int tr_ssh_recv(const void* tr_ssh_sock, void* buf, const size_t buf_len, const time_t timeout){
168     ssh_channel rchans[2] = { ((struct tr_ssh_socket*) tr_ssh_sock)->channel, NULL };
169 
170     struct timeval timev = { 1, 0 };
171 
172     if(ssh_channel_select(rchans, NULL, NULL, &timev) == SSH_EINTR)
173         return TR_INTR;
174 
175     if(ssh_channel_is_eof(((struct tr_ssh_socket*) tr_ssh_sock)->channel) != 0)
176         return SSH_ERROR;
177 
178     if(rchans[0] == NULL)
179         return TR_WOULDBLOCK;
180 
181 
182     return tr_ssh_recv_async(tr_ssh_sock, buf, buf_len);
183 }
184 
tr_ssh_send(const void * tr_ssh_sock,const void * pdu,const size_t len,const time_t timeout)185 int tr_ssh_send(const void *tr_ssh_sock, const void *pdu, const size_t len, const time_t timeout __attribute__((unused)))
186 {
187     return ssh_channel_write(((struct tr_ssh_socket *) tr_ssh_sock)->channel, pdu, len);
188 }
189 
tr_ssh_ident(void * tr_ssh_sock)190 const char *tr_ssh_ident(void *tr_ssh_sock)
191 {
192     size_t len;
193     struct tr_ssh_socket *sock = tr_ssh_sock;
194 
195     assert(sock != NULL);
196 
197     if (sock->ident != NULL)
198         return sock->ident;
199 
200     len = strlen(sock->config.username) + 1 + strlen(sock->config.host) + 1 +
201           5 + 1;
202     sock->ident = lrtr_malloc(len);
203     if (sock->ident == NULL)
204         return NULL;
205     snprintf(sock->ident, len, "%s@%s:%u", sock->config.username,
206              sock->config.host, sock->config.port);
207     return sock->ident;
208 }
209 
tr_ssh_init(const struct tr_ssh_config * config,struct tr_socket * socket)210 RTRLIB_EXPORT int tr_ssh_init(const struct tr_ssh_config *config, struct tr_socket *socket)
211 {
212 
213     socket->close_fp = &tr_ssh_close;
214     socket->free_fp = &tr_ssh_free;
215     socket->open_fp = &tr_ssh_open;
216     socket->recv_fp = &tr_ssh_recv;
217     socket->send_fp = &tr_ssh_send;;
218     socket->ident_fp = &tr_ssh_ident;
219 
220     socket->socket = lrtr_malloc(sizeof(struct tr_ssh_socket));
221     struct tr_ssh_socket *ssh_socket = socket->socket;
222     ssh_socket->channel = NULL;
223     ssh_socket->session = NULL;
224     ssh_socket->config.host = lrtr_strdup(config->host);
225     ssh_socket->config.port = config->port;
226     ssh_socket->config.username = lrtr_strdup(config->username);
227 
228     if (config->bindaddr) {
229         ssh_socket->config.bindaddr = lrtr_strdup(config->bindaddr);
230     } else {
231         ssh_socket->config.bindaddr = NULL;
232     }
233 
234     if (config->bindaddr) {
235         ssh_socket->config.client_privkey_path =
236 		lrtr_strdup(config->client_privkey_path);
237     } else {
238         ssh_socket->config.client_privkey_path = NULL;
239     }
240 
241     if (config->bindaddr) {
242         ssh_socket->config.server_hostkey_path =
243 		lrtr_strdup(config->server_hostkey_path);
244     } else {
245         ssh_socket->config.server_hostkey_path = NULL;
246     }
247     ssh_socket->ident = NULL;;
248 
249     return TR_SUCCESS;
250 }
251