1 /*
2 * bind.c : all ssh_bind functions
3 *
4 * This file is part of the SSH Library
5 *
6 * Copyright (c) 2004-2005 by Aris Adamantiadis
7 *
8 * The SSH Library is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or (at your
11 * option) any later version.
12 *
13 * The SSH Library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
16 * License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with the SSH Library; see the file COPYING. If not, write to
20 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
21 * MA 02111-1307, USA.
22 */
23
24
25 #include "config.h"
26
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <stdlib.h>
32
33 #include "libssh/priv.h"
34 #include "libssh/bind.h"
35 #include "libssh/libssh.h"
36 #include "libssh/server.h"
37 #include "libssh/pki.h"
38 #include "libssh/buffer.h"
39 #include "libssh/socket.h"
40 #include "libssh/session.h"
41
42 /**
43 * @addtogroup libssh_server
44 *
45 * @{
46 */
47
48
49 #ifdef _WIN32
50 #include <io.h>
51 #include <winsock2.h>
52 #include <ws2tcpip.h>
53
54 /*
55 * <wspiapi.h> is necessary for getaddrinfo before Windows XP, but it isn't
56 * available on some platforms like MinGW.
57 */
58 #ifdef HAVE_WSPIAPI_H
59 # include <wspiapi.h>
60 #endif
61
62 #define SOCKOPT_TYPE_ARG4 char
63
64 /*
65 * We need to provide hstrerror. Not we can't call the parameter h_errno
66 * because it's #defined
67 */
hstrerror(int h_errno_val)68 static char *hstrerror(int h_errno_val) {
69 static char text[50] = {0};
70
71 snprintf(text, sizeof(text), "getaddrino error %d\n", h_errno_val);
72
73 return text;
74 }
75 #else /* _WIN32 */
76
77 #include <sys/socket.h>
78 #include <netinet/in.h>
79 #include <netdb.h>
80 #define SOCKOPT_TYPE_ARG4 int
81
82 #endif /* _WIN32 */
83
bind_socket(ssh_bind sshbind,const char * hostname,int port)84 static socket_t bind_socket(ssh_bind sshbind, const char *hostname,
85 int port) {
86 char port_c[6];
87 struct addrinfo *ai;
88 struct addrinfo hints;
89 int opt = 1;
90 socket_t s;
91 int rc;
92
93 ZERO_STRUCT(hints);
94
95 hints.ai_flags = AI_PASSIVE;
96 hints.ai_socktype = SOCK_STREAM;
97
98 snprintf(port_c, 6, "%d", port);
99 rc = getaddrinfo(hostname, port_c, &hints, &ai);
100 if (rc != 0) {
101 ssh_set_error(sshbind,
102 SSH_FATAL,
103 "Resolving %s: %s", hostname, gai_strerror(rc));
104 return -1;
105 }
106
107 s = socket (ai->ai_family,
108 ai->ai_socktype,
109 ai->ai_protocol);
110 if (s == SSH_INVALID_SOCKET) {
111 ssh_set_error(sshbind, SSH_FATAL, "%s", strerror(errno));
112 freeaddrinfo (ai);
113 return -1;
114 }
115
116 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
117 (char *)&opt, sizeof(opt)) < 0) {
118 ssh_set_error(sshbind,
119 SSH_FATAL,
120 "Setting socket options failed: %s",
121 hstrerror(h_errno));
122 freeaddrinfo (ai);
123 close(s);
124 return -1;
125 }
126
127 if (bind(s, ai->ai_addr, ai->ai_addrlen) != 0) {
128 ssh_set_error(sshbind,
129 SSH_FATAL,
130 "Binding to %s:%d: %s",
131 hostname,
132 port,
133 strerror(errno));
134 freeaddrinfo (ai);
135 close(s);
136 return -1;
137 }
138
139 freeaddrinfo (ai);
140 return s;
141 }
142
ssh_bind_new(void)143 ssh_bind ssh_bind_new(void) {
144 ssh_bind ptr;
145
146 ptr = malloc(sizeof(struct ssh_bind_struct));
147 if (ptr == NULL) {
148 return NULL;
149 }
150 ZERO_STRUCTP(ptr);
151 ptr->bindfd = SSH_INVALID_SOCKET;
152 ptr->bindport= 22;
153 ptr->common.log_verbosity = 0;
154
155 return ptr;
156 }
157
ssh_bind_listen(ssh_bind sshbind)158 int ssh_bind_listen(ssh_bind sshbind) {
159 const char *host;
160 socket_t fd;
161 int rc;
162
163 if (ssh_init() < 0) {
164 ssh_set_error(sshbind, SSH_FATAL, "ssh_init() failed");
165 return -1;
166 }
167
168 if (sshbind->dsakey == NULL && sshbind->rsakey == NULL) {
169 ssh_set_error(sshbind, SSH_FATAL,
170 "DSA or RSA host key file must be set before listen()");
171 return SSH_ERROR;
172 }
173
174 if (sshbind->dsakey) {
175 rc = ssh_pki_import_privkey_file(sshbind->dsakey,
176 NULL,
177 NULL,
178 NULL,
179 &sshbind->dsa);
180 if (rc == SSH_ERROR) {
181 ssh_set_error(sshbind, SSH_FATAL,
182 "Failed to import private DSA host key");
183 return SSH_ERROR;
184 }
185
186 if (ssh_key_type(sshbind->dsa) != SSH_KEYTYPE_DSS) {
187 ssh_set_error(sshbind, SSH_FATAL,
188 "The DSA host key has the wrong type");
189 ssh_key_free(sshbind->dsa);
190 return SSH_ERROR;
191 }
192 }
193
194 if (sshbind->rsakey) {
195 rc = ssh_pki_import_privkey_file(sshbind->rsakey,
196 NULL,
197 NULL,
198 NULL,
199 &sshbind->rsa);
200 if (rc == SSH_ERROR) {
201 ssh_set_error(sshbind, SSH_FATAL,
202 "Failed to import private RSA host key");
203 return SSH_ERROR;
204 }
205
206 if (ssh_key_type(sshbind->rsa) != SSH_KEYTYPE_RSA &&
207 ssh_key_type(sshbind->rsa) != SSH_KEYTYPE_RSA1) {
208 ssh_set_error(sshbind, SSH_FATAL,
209 "The RSA host key has the wrong type");
210 ssh_key_free(sshbind->rsa);
211 return SSH_ERROR;
212 }
213 }
214
215 if (sshbind->bindfd == SSH_INVALID_SOCKET) {
216 host = sshbind->bindaddr;
217 if (host == NULL) {
218 host = "0.0.0.0";
219 }
220
221 fd = bind_socket(sshbind, host, sshbind->bindport);
222 if (fd == SSH_INVALID_SOCKET) {
223 ssh_key_free(sshbind->dsa);
224 ssh_key_free(sshbind->rsa);
225 return -1;
226 }
227 sshbind->bindfd = fd;
228
229 if (listen(fd, 10) < 0) {
230 ssh_set_error(sshbind, SSH_FATAL,
231 "Listening to socket %d: %s",
232 fd, strerror(errno));
233 close(fd);
234 ssh_key_free(sshbind->dsa);
235 ssh_key_free(sshbind->rsa);
236 return -1;
237 }
238 } else {
239 SSH_LOG(sshbind, SSH_LOG_INFO, "Using app-provided bind socket");
240 }
241 return 0;
242 }
243
ssh_bind_set_callbacks(ssh_bind sshbind,ssh_bind_callbacks callbacks,void * userdata)244 int ssh_bind_set_callbacks(ssh_bind sshbind, ssh_bind_callbacks callbacks,
245 void *userdata){
246 if (sshbind == NULL) {
247 return SSH_ERROR;
248 }
249 if (callbacks == NULL) {
250 ssh_set_error_invalid(sshbind);
251 return SSH_ERROR;
252 }
253 if(callbacks->size <= 0 || callbacks->size > 1024 * sizeof(void *)){
254 ssh_set_error(sshbind,SSH_FATAL,
255 "Invalid callback passed in (badly initialized)");
256 return SSH_ERROR;
257 }
258 sshbind->bind_callbacks = callbacks;
259 sshbind->bind_callbacks_userdata=userdata;
260 return 0;
261 }
262
263 /** @internal
264 * @brief callback being called by poll when an event happens
265 *
266 */
ssh_bind_poll_callback(ssh_poll_handle sshpoll,socket_t fd,int revents,void * user)267 static int ssh_bind_poll_callback(ssh_poll_handle sshpoll,
268 socket_t fd, int revents, void *user){
269 ssh_bind sshbind=(ssh_bind)user;
270 (void)sshpoll;
271 (void)fd;
272
273 if(revents & POLLIN){
274 /* new incoming connection */
275 if(ssh_callbacks_exists(sshbind->bind_callbacks,incoming_connection)){
276 sshbind->bind_callbacks->incoming_connection(sshbind,
277 sshbind->bind_callbacks_userdata);
278 }
279 }
280 return 0;
281 }
282
283 /** @internal
284 * @brief returns the current poll handle, or create it
285 * @param sshbind the ssh_bind object
286 * @returns a ssh_poll handle suitable for operation
287 */
ssh_bind_get_poll(ssh_bind sshbind)288 ssh_poll_handle ssh_bind_get_poll(ssh_bind sshbind){
289 if(sshbind->poll)
290 return sshbind->poll;
291 sshbind->poll=ssh_poll_new(sshbind->bindfd,POLLIN,
292 ssh_bind_poll_callback,sshbind);
293 return sshbind->poll;
294 }
295
ssh_bind_set_blocking(ssh_bind sshbind,int blocking)296 void ssh_bind_set_blocking(ssh_bind sshbind, int blocking) {
297 sshbind->blocking = blocking ? 1 : 0;
298 }
299
ssh_bind_get_fd(ssh_bind sshbind)300 socket_t ssh_bind_get_fd(ssh_bind sshbind) {
301 return sshbind->bindfd;
302 }
303
ssh_bind_set_fd(ssh_bind sshbind,socket_t fd)304 void ssh_bind_set_fd(ssh_bind sshbind, socket_t fd) {
305 sshbind->bindfd = fd;
306 }
307
ssh_bind_fd_toaccept(ssh_bind sshbind)308 void ssh_bind_fd_toaccept(ssh_bind sshbind) {
309 sshbind->toaccept = 1;
310 }
311
ssh_bind_free(ssh_bind sshbind)312 void ssh_bind_free(ssh_bind sshbind){
313 int i;
314
315 if (sshbind == NULL) {
316 return;
317 }
318
319 if (sshbind->bindfd >= 0) {
320 #ifdef _WIN32
321 closesocket(sshbind->bindfd);
322 #else
323 close(sshbind->bindfd);
324 #endif
325 }
326 sshbind->bindfd = SSH_INVALID_SOCKET;
327
328 /* options */
329 SAFE_FREE(sshbind->banner);
330 SAFE_FREE(sshbind->dsakey);
331 SAFE_FREE(sshbind->rsakey);
332 SAFE_FREE(sshbind->dsa);
333 SAFE_FREE(sshbind->rsa);
334 SAFE_FREE(sshbind->bindaddr);
335
336 for (i = 0; i < 10; i++) {
337 if (sshbind->wanted_methods[i]) {
338 SAFE_FREE(sshbind->wanted_methods[i]);
339 }
340 }
341
342 SAFE_FREE(sshbind);
343 }
344
ssh_bind_accept_fd(ssh_bind sshbind,ssh_session session,socket_t fd)345 int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd){
346 int i;
347
348 if (session == NULL){
349 ssh_set_error(sshbind, SSH_FATAL,"session is null");
350 return SSH_ERROR;
351 }
352
353 session->server = 1;
354 session->version = 2;
355
356 /* copy options */
357 for (i = 0; i < 10; ++i) {
358 if (sshbind->wanted_methods[i]) {
359 session->wanted_methods[i] = strdup(sshbind->wanted_methods[i]);
360 if (session->wanted_methods[i] == NULL) {
361 return SSH_ERROR;
362 }
363 }
364 }
365
366 if (sshbind->bindaddr == NULL)
367 session->bindaddr = NULL;
368 else {
369 SAFE_FREE(session->bindaddr);
370 session->bindaddr = strdup(sshbind->bindaddr);
371 if (session->bindaddr == NULL) {
372 return SSH_ERROR;
373 }
374 }
375
376 session->common.log_verbosity = sshbind->common.log_verbosity;
377
378 ssh_socket_free(session->socket);
379 session->socket = ssh_socket_new(session);
380 if (session->socket == NULL) {
381 /* perhaps it may be better to copy the error from session to sshbind */
382 ssh_set_error_oom(sshbind);
383 return SSH_ERROR;
384 }
385 ssh_socket_set_fd(session->socket, fd);
386 ssh_socket_get_poll_handle_out(session->socket);
387
388 if (sshbind->dsa) {
389 session->srv.dsa_key = ssh_key_dup(sshbind->dsa);
390 if (session->srv.dsa_key == NULL) {
391 ssh_set_error_oom(sshbind);
392 return SSH_ERROR;
393 }
394 }
395 if (sshbind->rsa) {
396 session->srv.rsa_key = ssh_key_dup(sshbind->rsa);
397 if (session->srv.rsa_key == NULL) {
398 ssh_set_error_oom(sshbind);
399 return SSH_ERROR;
400 }
401 }
402 return SSH_OK;
403 }
404
ssh_bind_accept(ssh_bind sshbind,ssh_session session)405 int ssh_bind_accept(ssh_bind sshbind, ssh_session session) {
406 socket_t fd = SSH_INVALID_SOCKET;
407 int rc;
408 if (sshbind->bindfd == SSH_INVALID_SOCKET) {
409 ssh_set_error(sshbind, SSH_FATAL,
410 "Can't accept new clients on a not bound socket.");
411 return SSH_ERROR;
412 }
413
414 if (session == NULL){
415 ssh_set_error(sshbind, SSH_FATAL,"session is null");
416 return SSH_ERROR;
417 }
418
419 fd = accept(sshbind->bindfd, NULL, NULL);
420 if (fd == SSH_INVALID_SOCKET) {
421 ssh_set_error(sshbind, SSH_FATAL,
422 "Accepting a new connection: %s",
423 strerror(errno));
424 return SSH_ERROR;
425 }
426 rc = ssh_bind_accept_fd(sshbind, session, fd);
427
428 if(rc == SSH_ERROR){
429 #ifdef _WIN32
430 closesocket(fd);
431 #else
432 close(fd);
433 #endif
434 if (session->socket)
435 ssh_socket_close(session->socket);
436 }
437 return rc;
438 }
439
440
441 /**
442 * @}
443 */
444