comparison svr-main.c @ 391:00fcf5045160

propagate from branch 'au.asn.ucc.matt.ltc.dropbear' (head c1db4398d56c56c6d06ae1e20c1e0d04dbb598ed) to branch 'au.asn.ucc.matt.dropbear' (head d26d5eb2837f46b56a33fb0e7573aa0201abd4d5)
author Matt Johnston <matt@ucc.asn.au>
date Thu, 11 Jan 2007 04:29:08 +0000
parents b66a00272a90
children 1afa503e33f5
comparison
equal deleted inserted replaced
390:d8e44bef7917 391:00fcf5045160
1 /*
2 * Dropbear - a SSH2 server
3 *
4 * Copyright (c) 2002-2006 Matt Johnston
5 * All rights reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE. */
24
25 #include "includes.h"
26 #include "dbutil.h"
27 #include "session.h"
28 #include "buffer.h"
29 #include "signkey.h"
30 #include "runopts.h"
31 #include "random.h"
32
33 static size_t listensockets(int *sock, size_t sockcount, int *maxfd);
34 static void sigchld_handler(int dummy);
35 static void sigsegv_handler(int);
36 static void sigintterm_handler(int fish);
37 #ifdef INETD_MODE
38 static void main_inetd();
39 #endif
40 #ifdef NON_INETD_MODE
41 static void main_noinetd();
42 #endif
43 static void commonsetup();
44
45 #if defined(DBMULTI_dropbear) || !defined(DROPBEAR_MULTI)
46 #if defined(DBMULTI_dropbear) && defined(DROPBEAR_MULTI)
47 int dropbear_main(int argc, char ** argv)
48 #else
49 int main(int argc, char ** argv)
50 #endif
51 {
52 _dropbear_exit = svr_dropbear_exit;
53 _dropbear_log = svr_dropbear_log;
54
55 /* get commandline options */
56 svr_getopts(argc, argv);
57
58 #ifdef INETD_MODE
59 /* service program mode */
60 if (svr_opts.inetdmode) {
61 main_inetd();
62 /* notreached */
63 }
64 #endif
65
66 #ifdef NON_INETD_MODE
67 main_noinetd();
68 /* notreached */
69 #endif
70
71 dropbear_exit("Compiled without normal mode, can't run without -i\n");
72 return -1;
73 }
74 #endif
75
76 #ifdef INETD_MODE
77 static void main_inetd() {
78
79 struct sockaddr_storage remoteaddr;
80 socklen_t remoteaddrlen;
81 char * addrstring = NULL;
82
83 /* Set up handlers, syslog, seed random */
84 commonsetup();
85
86 remoteaddrlen = sizeof(remoteaddr);
87 if (getpeername(0, (struct sockaddr*)&remoteaddr, &remoteaddrlen) < 0) {
88 dropbear_exit("Unable to getpeername: %s", strerror(errno));
89 }
90
91 /* In case our inetd was lax in logging source addresses */
92 addrstring = getaddrstring(&remoteaddr, 1);
93 dropbear_log(LOG_INFO, "Child connection from %s", addrstring);
94
95 /* Don't check the return value - it may just fail since inetd has
96 * already done setsid() after forking (xinetd on Darwin appears to do
97 * this */
98 setsid();
99
100 /* Start service program
101 * -1 is a dummy childpipe, just something we can close() without
102 * mattering. */
103 svr_session(0, -1, getaddrhostname(&remoteaddr), addrstring);
104
105 /* notreached */
106 }
107 #endif /* INETD_MODE */
108
109 #ifdef NON_INETD_MODE
110 void main_noinetd() {
111 fd_set fds;
112 struct timeval seltimeout;
113 unsigned int i, j;
114 int val;
115 int maxsock = -1;
116 int listensocks[MAX_LISTEN_ADDR];
117 size_t listensockcount = 0;
118 FILE *pidfile = NULL;
119
120 int childpipes[MAX_UNAUTH_CLIENTS];
121 char * preauth_addrs[MAX_UNAUTH_CLIENTS];
122
123 int childsock;
124 int childpipe[2];
125
126 // Note: commonsetup() must happen before we daemon()ise. Otherwise
127 // daemon() will chdir("/"), and we won't be able to find local-dir hostkeys.
128 commonsetup();
129
130 /* fork */
131 if (svr_opts.forkbg) {
132 int closefds = 0;
133 #ifndef DEBUG_TRACE
134 if (!svr_opts.usingsyslog) {
135 closefds = 1;
136 }
137 #endif
138 if (daemon(0, closefds) < 0) {
139 dropbear_exit("Failed to daemonize: %s", strerror(errno));
140 }
141 }
142
143 /* should be done after syslog is working */
144 if (svr_opts.forkbg) {
145 dropbear_log(LOG_INFO, "Running in background");
146 } else {
147 dropbear_log(LOG_INFO, "Not forking");
148 }
149
150 /* create a PID file so that we can be killed easily */
151 pidfile = fopen(svr_opts.pidfile, "w");
152 if (pidfile) {
153 fprintf(pidfile, "%d\n", getpid());
154 fclose(pidfile);
155 }
156
157 /* sockets to identify pre-authenticated clients */
158 for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) {
159 childpipes[i] = -1;
160 }
161 bzero(preauth_addrs, sizeof(preauth_addrs));
162
163 /* Set up the listening sockets */
164 listensockcount = listensockets(listensocks, MAX_LISTEN_ADDR, &maxsock);
165 if (listensockcount == 0)
166 {
167 dropbear_exit("No listening ports available.");
168 }
169
170 /* incoming connection select loop */
171 for(;;) {
172
173 FD_ZERO(&fds);
174
175 seltimeout.tv_sec = 60;
176 seltimeout.tv_usec = 0;
177
178 /* listening sockets */
179 for (i = 0; i < listensockcount; i++) {
180 FD_SET(listensocks[i], &fds);
181 }
182
183 /* pre-authentication clients */
184 for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) {
185 if (childpipes[i] >= 0) {
186 FD_SET(childpipes[i], &fds);
187 maxsock = MAX(maxsock, childpipes[i]);
188 }
189 }
190
191 val = select(maxsock+1, &fds, NULL, NULL, &seltimeout);
192
193 if (exitflag) {
194 unlink(svr_opts.pidfile);
195 dropbear_exit("Terminated by signal");
196 }
197
198 if (val == 0) {
199 /* timeout reached */
200 continue;
201 }
202
203 if (val < 0) {
204 if (errno == EINTR) {
205 continue;
206 }
207 dropbear_exit("Listening socket error");
208 }
209
210 /* close fds which have been authed or closed - svr-auth.c handles
211 * closing the auth sockets on success */
212 for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) {
213 if (childpipes[i] >= 0 && FD_ISSET(childpipes[i], &fds)) {
214 m_close(childpipes[i]);
215 childpipes[i] = -1;
216 m_free(preauth_addrs[i]);
217 }
218 }
219
220 /* handle each socket which has something to say */
221 for (i = 0; i < listensockcount; i++) {
222
223 struct sockaddr_storage remoteaddr;
224 socklen_t remoteaddrlen = 0;
225 size_t num_unauthed_for_addr = 0;
226 size_t num_unauthed_total = 0;
227 char * remote_addr_str = NULL;
228 pid_t fork_ret = 0;
229 size_t conn_idx = 0;
230
231 if (!FD_ISSET(listensocks[i], &fds))
232 continue;
233
234 remoteaddrlen = sizeof(remoteaddr);
235 childsock = accept(listensocks[i],
236 (struct sockaddr*)&remoteaddr, &remoteaddrlen);
237
238 if (childsock < 0) {
239 /* accept failed */
240 continue;
241 }
242
243 /* Limit the number of unauthenticated connections per IP */
244 remote_addr_str = getaddrstring(&remoteaddr, 0);
245
246 num_unauthed_for_addr = 0;
247 num_unauthed_total = 0;
248 for (j = 0; j < MAX_UNAUTH_CLIENTS; j++) {
249 if (childpipes[j] >= 0) {
250 num_unauthed_total++;
251 if (strcmp(remote_addr_str, preauth_addrs[j]) == 0) {
252 num_unauthed_for_addr++;
253 }
254 } else {
255 /* a free slot */
256 conn_idx = j;
257 }
258 }
259
260 if (num_unauthed_total >= MAX_UNAUTH_CLIENTS
261 || num_unauthed_for_addr >= MAX_UNAUTH_PER_IP) {
262 goto out;
263 }
264
265 if (pipe(childpipe) < 0) {
266 TRACE(("error creating child pipe"))
267 goto out;
268 }
269
270 fork_ret = fork();
271 if (fork_ret < 0) {
272 dropbear_log(LOG_WARNING, "error forking: %s", strerror(errno));
273 goto out;
274
275 } else if (fork_ret > 0) {
276
277 /* parent */
278 childpipes[conn_idx] = childpipe[0];
279 m_close(childpipe[1]);
280 preauth_addrs[conn_idx] = remote_addr_str;
281 remote_addr_str = NULL;
282
283 } else {
284
285 /* child */
286 char * addrstring = NULL;
287 #ifdef DEBUG_FORKGPROF
288 extern void _start(void), etext(void);
289 monstartup((u_long)&_start, (u_long)&etext);
290 #endif /* DEBUG_FORKGPROF */
291
292 m_free(remote_addr_str);
293 addrstring = getaddrstring(&remoteaddr, 1);
294 dropbear_log(LOG_INFO, "Child connection from %s", addrstring);
295
296 if (setsid() < 0) {
297 dropbear_exit("setsid: %s", strerror(errno));
298 }
299
300 /* make sure we close sockets */
301 for (i = 0; i < listensockcount; i++) {
302 m_close(listensocks[i]);
303 }
304
305 m_close(childpipe[0]);
306
307 /* start the session */
308 svr_session(childsock, childpipe[1],
309 getaddrhostname(&remoteaddr),
310 addrstring);
311 /* don't return */
312 dropbear_assert(0);
313 }
314
315 out:
316 /* This section is important for the parent too */
317 m_close(childsock);
318 if (remote_addr_str) {
319 m_free(remote_addr_str);
320 }
321 }
322 } /* for(;;) loop */
323
324 /* don't reach here */
325 }
326 #endif /* NON_INETD_MODE */
327
328
329 /* catch + reap zombie children */
330 static void sigchld_handler(int UNUSED(unused)) {
331 struct sigaction sa_chld;
332
333 while(waitpid(-1, NULL, WNOHANG) > 0);
334
335 sa_chld.sa_handler = sigchld_handler;
336 sa_chld.sa_flags = SA_NOCLDSTOP;
337 if (sigaction(SIGCHLD, &sa_chld, NULL) < 0) {
338 dropbear_exit("signal() error");
339 }
340 }
341
342 /* catch any segvs */
343 static void sigsegv_handler(int UNUSED(unused)) {
344 fprintf(stderr, "Aiee, segfault! You should probably report "
345 "this as a bug to the developer\n");
346 exit(EXIT_FAILURE);
347 }
348
349 /* catch ctrl-c or sigterm */
350 static void sigintterm_handler(int UNUSED(unused)) {
351
352 exitflag = 1;
353 }
354
355 /* Things used by inetd and non-inetd modes */
356 static void commonsetup() {
357
358 struct sigaction sa_chld;
359 #ifndef DISABLE_SYSLOG
360 if (svr_opts.usingsyslog) {
361 startsyslog();
362 }
363 #endif
364
365 /* set up cleanup handler */
366 if (signal(SIGINT, sigintterm_handler) == SIG_ERR ||
367 #ifndef DEBUG_VALGRIND
368 signal(SIGTERM, sigintterm_handler) == SIG_ERR ||
369 #endif
370 signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
371 dropbear_exit("signal() error");
372 }
373
374 /* catch and reap zombie children */
375 sa_chld.sa_handler = sigchld_handler;
376 sa_chld.sa_flags = SA_NOCLDSTOP;
377 if (sigaction(SIGCHLD, &sa_chld, NULL) < 0) {
378 dropbear_exit("signal() error");
379 }
380 if (signal(SIGSEGV, sigsegv_handler) == SIG_ERR) {
381 dropbear_exit("signal() error");
382 }
383
384 /* Now we can setup the hostkeys - needs to be after logging is on,
385 * otherwise we might end up blatting error messages to the socket */
386 loadhostkeys();
387
388 seedrandom();
389 }
390
391 /* Set up listening sockets for all the requested ports */
392 static size_t listensockets(int *sock, size_t sockcount, int *maxfd) {
393
394 unsigned int i;
395 char* errstring = NULL;
396 size_t sockpos = 0;
397 int nsock;
398
399 TRACE(("listensockets: %d to try\n", svr_opts.portcount))
400
401 for (i = 0; i < svr_opts.portcount; i++) {
402
403 TRACE(("listening on '%s'", svr_opts.ports[i]))
404
405 nsock = dropbear_listen("", svr_opts.ports[i], &sock[sockpos],
406 sockcount - sockpos,
407 &errstring, maxfd);
408
409 if (nsock < 0) {
410 dropbear_log(LOG_WARNING, "Failed listening on '%s': %s",
411 svr_opts.ports[i], errstring);
412 m_free(errstring);
413 continue;
414 }
415
416 sockpos += nsock;
417
418 }
419 return sockpos;
420 }