Mercurial > dropbear
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 } |