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