Mercurial > dropbear
comparison svr-main.c @ 285:1b9e69c058d2
propagate from branch 'au.asn.ucc.matt.ltc.dropbear' (head 20dccfc09627970a312d77fb41dc2970b62689c3)
to branch 'au.asn.ucc.matt.dropbear' (head fdf4a7a3b97ae5046139915de7e40399cceb2c01)
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Wed, 08 Mar 2006 13:23:58 +0000 |
parents | 1f5ec029dfe8 |
children | 94ee16f5b8a8 |
comparison
equal
deleted
inserted
replaced
281:997e6f7dc01e | 285:1b9e69c058d2 |
---|---|
1 /* | |
2 * Dropbear - a SSH2 server | |
3 * | |
4 * Copyright (c) 2002,2003 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 | |
32 static size_t listensockets(int *sock, size_t sockcount, int *maxfd); | |
33 static void sigchld_handler(int dummy); | |
34 static void sigsegv_handler(int); | |
35 static void sigintterm_handler(int fish); | |
36 #ifdef INETD_MODE | |
37 static void main_inetd(); | |
38 #endif | |
39 #ifdef NON_INETD_MODE | |
40 static void main_noinetd(); | |
41 #endif | |
42 static void commonsetup(); | |
43 | |
44 #if defined(DBMULTI_dropbear) || !defined(DROPBEAR_MULTI) | |
45 #if defined(DBMULTI_dropbear) && defined(DROPBEAR_MULTI) | |
46 int dropbear_main(int argc, char ** argv) | |
47 #else | |
48 int main(int argc, char ** argv) | |
49 #endif | |
50 { | |
51 | |
52 | |
53 _dropbear_exit = svr_dropbear_exit; | |
54 _dropbear_log = svr_dropbear_log; | |
55 | |
56 /* get commandline options */ | |
57 svr_getopts(argc, argv); | |
58 | |
59 #ifdef INETD_MODE | |
60 /* service program mode */ | |
61 if (svr_opts.inetdmode) { | |
62 main_inetd(); | |
63 /* notreached */ | |
64 } | |
65 #endif | |
66 | |
67 #ifdef NON_INETD_MODE | |
68 main_noinetd(); | |
69 /* notreached */ | |
70 #endif | |
71 | |
72 dropbear_exit("Compiled without normal mode, can't run without -i\n"); | |
73 return -1; | |
74 } | |
75 #endif | |
76 | |
77 #ifdef INETD_MODE | |
78 static void main_inetd() { | |
79 | |
80 struct sockaddr_storage remoteaddr; | |
81 socklen_t remoteaddrlen; | |
82 char * addrstring = NULL; | |
83 | |
84 /* Set up handlers, syslog, seed random */ | |
85 commonsetup(); | |
86 | |
87 remoteaddrlen = sizeof(remoteaddr); | |
88 if (getpeername(0, (struct sockaddr*)&remoteaddr, &remoteaddrlen) < 0) { | |
89 dropbear_exit("Unable to getpeername: %s", strerror(errno)); | |
90 } | |
91 | |
92 /* In case our inetd was lax in logging source addresses */ | |
93 addrstring = getaddrstring(&remoteaddr, 1); | |
94 dropbear_log(LOG_INFO, "Child connection from %s", addrstring); | |
95 | |
96 /* Don't check the return value - it may just fail since inetd has | |
97 * already done setsid() after forking (xinetd on Darwin appears to do | |
98 * this */ | |
99 setsid(); | |
100 | |
101 /* Start service program | |
102 * -1 is a dummy childpipe, just something we can close() without | |
103 * mattering. */ | |
104 svr_session(0, -1, getaddrhostname(&remoteaddr), addrstring); | |
105 | |
106 /* notreached */ | |
107 } | |
108 #endif /* INETD_MODE */ | |
109 | |
110 #ifdef NON_INETD_MODE | |
111 void main_noinetd() { | |
112 fd_set fds; | |
113 struct timeval seltimeout; | |
114 unsigned int i, j; | |
115 int val; | |
116 int maxsock = -1; | |
117 int listensocks[MAX_LISTEN_ADDR]; | |
118 size_t listensockcount = 0; | |
119 FILE *pidfile = NULL; | |
120 | |
121 int childpipes[MAX_UNAUTH_CLIENTS]; | |
122 char * preauth_addrs[MAX_UNAUTH_CLIENTS]; | |
123 | |
124 int childsock; | |
125 int childpipe[2]; | |
126 | |
127 /* fork */ | |
128 if (svr_opts.forkbg) { | |
129 int closefds = 0; | |
130 #ifndef DEBUG_TRACE | |
131 if (!svr_opts.usingsyslog) { | |
132 closefds = 1; | |
133 } | |
134 #endif | |
135 if (daemon(0, closefds) < 0) { | |
136 dropbear_exit("Failed to daemonize: %s", strerror(errno)); | |
137 } | |
138 } | |
139 | |
140 commonsetup(); | |
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(DROPBEAR_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 /* XXX XXX ports */ | |
165 listensockcount = listensockets(listensocks, MAX_LISTEN_ADDR, &maxsock); | |
166 if (listensockcount == 0) | |
167 { | |
168 dropbear_exit("No listening ports available."); | |
169 } | |
170 | |
171 /* incoming connection select loop */ | |
172 for(;;) { | |
173 | |
174 FD_ZERO(&fds); | |
175 | |
176 seltimeout.tv_sec = 60; | |
177 seltimeout.tv_usec = 0; | |
178 | |
179 /* listening sockets */ | |
180 for (i = 0; i < listensockcount; i++) { | |
181 FD_SET(listensocks[i], &fds); | |
182 } | |
183 | |
184 /* pre-authentication clients */ | |
185 for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) { | |
186 if (childpipes[i] >= 0) { | |
187 FD_SET(childpipes[i], &fds); | |
188 maxsock = MAX(maxsock, childpipes[i]); | |
189 } | |
190 } | |
191 | |
192 val = select(maxsock+1, &fds, NULL, NULL, &seltimeout); | |
193 | |
194 if (exitflag) { | |
195 unlink(DROPBEAR_PIDFILE); | |
196 dropbear_exit("Terminated by signal"); | |
197 } | |
198 | |
199 if (val == 0) { | |
200 /* timeout reached */ | |
201 continue; | |
202 } | |
203 | |
204 if (val < 0) { | |
205 if (errno == EINTR) { | |
206 continue; | |
207 } | |
208 dropbear_exit("Listening socket error"); | |
209 } | |
210 | |
211 /* close fds which have been authed or closed - svr-auth.c handles | |
212 * closing the auth sockets on success */ | |
213 for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) { | |
214 if (childpipes[i] >= 0 && FD_ISSET(childpipes[i], &fds)) { | |
215 m_close(childpipes[i]); | |
216 childpipes[i] = -1; | |
217 m_free(preauth_addrs[i]); | |
218 } | |
219 } | |
220 | |
221 /* handle each socket which has something to say */ | |
222 for (i = 0; i < listensockcount; i++) { | |
223 | |
224 struct sockaddr_storage remoteaddr; | |
225 socklen_t remoteaddrlen = 0; | |
226 size_t num_unauthed_for_addr = 0; | |
227 size_t num_unauthed_total = 0; | |
228 char * remote_addr_str = NULL; | |
229 pid_t fork_ret = 0; | |
230 size_t conn_idx = 0; | |
231 | |
232 if (!FD_ISSET(listensocks[i], &fds)) | |
233 continue; | |
234 | |
235 remoteaddrlen = sizeof(remoteaddr); | |
236 childsock = accept(listensocks[i], | |
237 (struct sockaddr*)&remoteaddr, &remoteaddrlen); | |
238 | |
239 if (childsock < 0) { | |
240 /* accept failed */ | |
241 continue; | |
242 } | |
243 | |
244 /* Limit the number of unauthenticated connections per IP */ | |
245 remote_addr_str = getaddrstring(&remoteaddr, 0); | |
246 | |
247 num_unauthed_for_addr = 0; | |
248 num_unauthed_total = 0; | |
249 for (j = 0; j < MAX_UNAUTH_CLIENTS; j++) { | |
250 if (childpipes[j] >= 0) { | |
251 num_unauthed_total++; | |
252 if (strcmp(remote_addr_str, preauth_addrs[j]) == 0) { | |
253 num_unauthed_for_addr++; | |
254 } | |
255 } else { | |
256 /* a free slot */ | |
257 conn_idx = j; | |
258 } | |
259 } | |
260 | |
261 if (num_unauthed_total >= MAX_UNAUTH_CLIENTS | |
262 || num_unauthed_for_addr >= MAX_UNAUTH_PER_IP) { | |
263 goto out; | |
264 } | |
265 | |
266 if (pipe(childpipe) < 0) { | |
267 TRACE(("error creating child pipe")) | |
268 goto out; | |
269 } | |
270 | |
271 fork_ret = fork(); | |
272 if (fork_ret < 0) { | |
273 dropbear_log(LOG_WARNING, "error forking: %s", strerror(errno)); | |
274 goto out; | |
275 | |
276 } else if (fork_ret > 0) { | |
277 | |
278 /* parent */ | |
279 childpipes[conn_idx] = childpipe[0]; | |
280 m_close(childpipe[1]); | |
281 preauth_addrs[conn_idx] = remote_addr_str; | |
282 remote_addr_str = NULL; | |
283 | |
284 } else { | |
285 | |
286 /* child */ | |
287 char * addrstring = NULL; | |
288 #ifdef DEBUG_FORKGPROF | |
289 extern void _start(void), etext(void); | |
290 monstartup((u_long)&_start, (u_long)&etext); | |
291 #endif /* DEBUG_FORKGPROF */ | |
292 | |
293 m_free(remote_addr_str); | |
294 addrstring = getaddrstring(&remoteaddr, 1); | |
295 dropbear_log(LOG_INFO, "Child connection from %s", addrstring); | |
296 | |
297 if (setsid() < 0) { | |
298 dropbear_exit("setsid: %s", strerror(errno)); | |
299 } | |
300 | |
301 /* make sure we close sockets */ | |
302 for (i = 0; i < listensockcount; i++) { | |
303 m_close(listensocks[i]); | |
304 } | |
305 | |
306 m_close(childpipe[0]); | |
307 | |
308 /* start the session */ | |
309 svr_session(childsock, childpipe[1], | |
310 getaddrhostname(&remoteaddr), | |
311 addrstring); | |
312 /* don't return */ | |
313 dropbear_assert(0); | |
314 } | |
315 | |
316 out: | |
317 /* This section is important for the parent too */ | |
318 m_close(childsock); | |
319 if (remote_addr_str) { | |
320 m_free(remote_addr_str); | |
321 } | |
322 } | |
323 } /* for(;;) loop */ | |
324 | |
325 /* don't reach here */ | |
326 } | |
327 #endif /* NON_INETD_MODE */ | |
328 | |
329 | |
330 /* catch + reap zombie children */ | |
331 static void sigchld_handler(int UNUSED(unused)) { | |
332 struct sigaction sa_chld; | |
333 | |
334 while(waitpid(-1, NULL, WNOHANG) > 0); | |
335 | |
336 sa_chld.sa_handler = sigchld_handler; | |
337 sa_chld.sa_flags = SA_NOCLDSTOP; | |
338 if (sigaction(SIGCHLD, &sa_chld, NULL) < 0) { | |
339 dropbear_exit("signal() error"); | |
340 } | |
341 } | |
342 | |
343 /* catch any segvs */ | |
344 static void sigsegv_handler(int UNUSED(unused)) { | |
345 fprintf(stderr, "Aiee, segfault! You should probably report " | |
346 "this as a bug to the developer\n"); | |
347 exit(EXIT_FAILURE); | |
348 } | |
349 | |
350 /* catch ctrl-c or sigterm */ | |
351 static void sigintterm_handler(int UNUSED(unused)) { | |
352 | |
353 exitflag = 1; | |
354 } | |
355 | |
356 /* Things used by inetd and non-inetd modes */ | |
357 static void commonsetup() { | |
358 | |
359 struct sigaction sa_chld; | |
360 #ifndef DISABLE_SYSLOG | |
361 if (svr_opts.usingsyslog) { | |
362 startsyslog(); | |
363 } | |
364 #endif | |
365 | |
366 /* set up cleanup handler */ | |
367 if (signal(SIGINT, sigintterm_handler) == SIG_ERR || | |
368 #ifndef DEBUG_VALGRIND | |
369 signal(SIGTERM, sigintterm_handler) == SIG_ERR || | |
370 #endif | |
371 signal(SIGPIPE, SIG_IGN) == SIG_ERR) { | |
372 dropbear_exit("signal() error"); | |
373 } | |
374 | |
375 /* catch and reap zombie children */ | |
376 sa_chld.sa_handler = sigchld_handler; | |
377 sa_chld.sa_flags = SA_NOCLDSTOP; | |
378 if (sigaction(SIGCHLD, &sa_chld, NULL) < 0) { | |
379 dropbear_exit("signal() error"); | |
380 } | |
381 if (signal(SIGSEGV, sigsegv_handler) == SIG_ERR) { | |
382 dropbear_exit("signal() error"); | |
383 } | |
384 | |
385 /* Now we can setup the hostkeys - needs to be after logging is on, | |
386 * otherwise we might end up blatting error messages to the socket */ | |
387 loadhostkeys(); | |
388 | |
389 seedrandom(); | |
390 } | |
391 | |
392 /* Set up listening sockets for all the requested ports */ | |
393 static size_t listensockets(int *sock, size_t sockcount, int *maxfd) { | |
394 | |
395 unsigned int i; | |
396 char* errstring = NULL; | |
397 size_t sockpos = 0; | |
398 int nsock; | |
399 | |
400 TRACE(("listensockets: %d to try\n", svr_opts.portcount)) | |
401 | |
402 for (i = 0; i < svr_opts.portcount; i++) { | |
403 | |
404 TRACE(("listening on '%s'", svr_opts.ports[i])) | |
405 | |
406 nsock = dropbear_listen("", svr_opts.ports[i], &sock[sockpos], | |
407 sockcount - sockpos, | |
408 &errstring, maxfd); | |
409 | |
410 if (nsock < 0) { | |
411 dropbear_log(LOG_WARNING, "Failed listening on '%s': %s", | |
412 svr_opts.ports[i], errstring); | |
413 m_free(errstring); | |
414 continue; | |
415 } | |
416 | |
417 sockpos += nsock; | |
418 | |
419 } | |
420 return sockpos; | |
421 } |