Mercurial > dropbear
comparison svr-main.c @ 1861:2b3a8026a6ce
Add re-exec for server
This allows ASLR to re-randomize the address
space for every connection, preventing some
vulnerabilities from being exploitable by
repeated probing.
Overhead (memory and time) is yet to be confirmed.
At present this is only enabled on Linux. Other BSD platforms
with fexecve() would probably also work though have not been tested.
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Sun, 30 Jan 2022 10:14:56 +0800 |
parents | 6ea18ca8fc03 |
children | adfcdfb161a4 |
comparison
equal
deleted
inserted
replaced
1860:5001e9c5641f | 1861:2b3a8026a6ce |
---|---|
33 | 33 |
34 static size_t listensockets(int *sock, size_t sockcount, int *maxfd); | 34 static size_t listensockets(int *sock, size_t sockcount, int *maxfd); |
35 static void sigchld_handler(int dummy); | 35 static void sigchld_handler(int dummy); |
36 static void sigsegv_handler(int); | 36 static void sigsegv_handler(int); |
37 static void sigintterm_handler(int fish); | 37 static void sigintterm_handler(int fish); |
38 #if INETD_MODE | |
39 static void main_inetd(void); | 38 static void main_inetd(void); |
40 #endif | 39 static void main_noinetd(int argc, char ** argv); |
41 #if NON_INETD_MODE | |
42 static void main_noinetd(void); | |
43 #endif | |
44 static void commonsetup(void); | 40 static void commonsetup(void); |
45 | 41 |
46 #if defined(DBMULTI_dropbear) || !DROPBEAR_MULTI | 42 #if defined(DBMULTI_dropbear) || !DROPBEAR_MULTI |
47 #if defined(DBMULTI_dropbear) && DROPBEAR_MULTI | 43 #if defined(DBMULTI_dropbear) && DROPBEAR_MULTI |
48 int dropbear_main(int argc, char ** argv) | 44 int dropbear_main(int argc, char ** argv) |
53 _dropbear_exit = svr_dropbear_exit; | 49 _dropbear_exit = svr_dropbear_exit; |
54 _dropbear_log = svr_dropbear_log; | 50 _dropbear_log = svr_dropbear_log; |
55 | 51 |
56 disallow_core(); | 52 disallow_core(); |
57 | 53 |
54 if (argc < 1) { | |
55 dropbear_exit("Bad argc"); | |
56 } | |
57 | |
58 /* get commandline options */ | 58 /* get commandline options */ |
59 svr_getopts(argc, argv); | 59 svr_getopts(argc, argv); |
60 | 60 |
61 #if INETD_MODE | 61 #if INETD_MODE |
62 /* service program mode */ | 62 /* service program mode */ |
64 main_inetd(); | 64 main_inetd(); |
65 /* notreached */ | 65 /* notreached */ |
66 } | 66 } |
67 #endif | 67 #endif |
68 | 68 |
69 #if DROPBEAR_DO_REEXEC | |
70 if (svr_opts.reexec_child) { | |
71 #ifdef PR_SET_NAME | |
72 /* Fix the "Name:" in /proc/pid/status, otherwise it's | |
73 a FD number from fexecve. | |
74 Failure doesn't really matter, it's mostly aesthetic */ | |
75 prctl(PR_SET_NAME, basename(argv[0]), 0, 0); | |
76 #endif | |
77 main_inetd(); | |
78 /* notreached */ | |
79 } | |
80 #endif | |
81 | |
69 #if NON_INETD_MODE | 82 #if NON_INETD_MODE |
70 main_noinetd(); | 83 main_noinetd(argc, argv); |
71 /* notreached */ | 84 /* notreached */ |
72 #endif | 85 #endif |
73 | 86 |
74 dropbear_exit("Compiled without normal mode, can't run without -i\n"); | 87 dropbear_exit("Compiled without normal mode, can't run without -i\n"); |
75 return -1; | 88 return -1; |
76 } | 89 } |
77 #endif | 90 #endif |
78 | 91 |
79 #if INETD_MODE | 92 #if INETD_MODE || DROPBEAR_DO_REEXEC |
80 static void main_inetd() { | 93 static void main_inetd() { |
81 char *host, *port = NULL; | 94 char *host, *port = NULL; |
82 | 95 |
83 /* Set up handlers, syslog */ | 96 /* Set up handlers, syslog */ |
84 commonsetup(); | 97 commonsetup(); |
85 | 98 |
86 seedrandom(); | 99 seedrandom(); |
87 | 100 |
88 #if DEBUG_TRACE | 101 if (!svr_opts.reexec_child) { |
89 if (debug_trace) { | 102 /* In case our inetd was lax in logging source addresses */ |
90 /* -v output goes to stderr which would get sent over the inetd network socket */ | 103 get_socket_address(0, NULL, NULL, &host, &port, 0); |
91 dropbear_exit("Dropbear inetd mode is incompatible with debug -v"); | 104 dropbear_log(LOG_INFO, "Child connection from %s:%s", host, port); |
92 } | 105 m_free(host); |
93 #endif | 106 m_free(port); |
94 | 107 |
95 /* In case our inetd was lax in logging source addresses */ | 108 /* Don't check the return value - it may just fail since inetd has |
96 get_socket_address(0, NULL, NULL, &host, &port, 0); | 109 * already done setsid() after forking (xinetd on Darwin appears to do |
97 dropbear_log(LOG_INFO, "Child connection from %s:%s", host, port); | 110 * this */ |
98 m_free(host); | 111 setsid(); |
99 m_free(port); | 112 } |
100 | |
101 /* Don't check the return value - it may just fail since inetd has | |
102 * already done setsid() after forking (xinetd on Darwin appears to do | |
103 * this */ | |
104 setsid(); | |
105 | 113 |
106 /* Start service program | 114 /* Start service program |
107 * -1 is a dummy childpipe, just something we can close() without | 115 * -1 is a dummy childpipe, just something we can close() without |
108 * mattering. */ | 116 * mattering. */ |
109 svr_session(0, -1); | 117 svr_session(0, -1); |
111 /* notreached */ | 119 /* notreached */ |
112 } | 120 } |
113 #endif /* INETD_MODE */ | 121 #endif /* INETD_MODE */ |
114 | 122 |
115 #if NON_INETD_MODE | 123 #if NON_INETD_MODE |
116 static void main_noinetd() { | 124 static void main_noinetd(int argc, char ** argv) { |
117 fd_set fds; | 125 fd_set fds; |
118 unsigned int i, j; | 126 unsigned int i, j; |
119 int val; | 127 int val; |
120 int maxsock = -1; | 128 int maxsock = -1; |
121 int listensocks[MAX_LISTEN_ADDR]; | 129 int listensocks[MAX_LISTEN_ADDR]; |
122 size_t listensockcount = 0; | 130 size_t listensockcount = 0; |
123 FILE *pidfile = NULL; | 131 FILE *pidfile = NULL; |
132 int execfd = -1; | |
124 | 133 |
125 int childpipes[MAX_UNAUTH_CLIENTS]; | 134 int childpipes[MAX_UNAUTH_CLIENTS]; |
126 char * preauth_addrs[MAX_UNAUTH_CLIENTS]; | 135 char * preauth_addrs[MAX_UNAUTH_CLIENTS]; |
127 | 136 |
128 int childsock; | 137 int childsock; |
129 int childpipe[2]; | 138 int childpipe[2]; |
139 | |
140 (void)argc; | |
141 (void)argv; | |
130 | 142 |
131 /* Note: commonsetup() must happen before we daemon()ise. Otherwise | 143 /* Note: commonsetup() must happen before we daemon()ise. Otherwise |
132 daemon() will chdir("/"), and we won't be able to find local-dir | 144 daemon() will chdir("/"), and we won't be able to find local-dir |
133 hostkeys. */ | 145 hostkeys. */ |
134 commonsetup(); | 146 commonsetup(); |
136 /* sockets to identify pre-authenticated clients */ | 148 /* sockets to identify pre-authenticated clients */ |
137 for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) { | 149 for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) { |
138 childpipes[i] = -1; | 150 childpipes[i] = -1; |
139 } | 151 } |
140 memset(preauth_addrs, 0x0, sizeof(preauth_addrs)); | 152 memset(preauth_addrs, 0x0, sizeof(preauth_addrs)); |
141 | 153 |
142 /* Set up the listening sockets */ | 154 /* Set up the listening sockets */ |
143 listensockcount = listensockets(listensocks, MAX_LISTEN_ADDR, &maxsock); | 155 listensockcount = listensockets(listensocks, MAX_LISTEN_ADDR, &maxsock); |
144 if (listensockcount == 0) | 156 if (listensockcount == 0) |
145 { | 157 { |
146 dropbear_exit("No listening ports available."); | 158 dropbear_exit("No listening ports available."); |
147 } | 159 } |
148 | 160 |
149 for (i = 0; i < listensockcount; i++) { | 161 for (i = 0; i < listensockcount; i++) { |
150 FD_SET(listensocks[i], &fds); | 162 FD_SET(listensocks[i], &fds); |
151 } | 163 } |
164 | |
165 #if DROPBEAR_DO_REEXEC | |
166 execfd = open(argv[0], O_CLOEXEC|O_RDONLY); | |
167 if (execfd < 0) { | |
168 /* Just fallback to straight fork */ | |
169 TRACE(("Couldn't open own binary %s, disabling re-exec: %s", argv[0], strerror(errno))) | |
170 } | |
171 #endif | |
152 | 172 |
153 /* fork */ | 173 /* fork */ |
154 if (svr_opts.forkbg) { | 174 if (svr_opts.forkbg) { |
155 int closefds = 0; | 175 int closefds = 0; |
156 #if !DEBUG_TRACE | 176 #if !DEBUG_TRACE |
179 | 199 |
180 /* incoming connection select loop */ | 200 /* incoming connection select loop */ |
181 for(;;) { | 201 for(;;) { |
182 | 202 |
183 DROPBEAR_FD_ZERO(&fds); | 203 DROPBEAR_FD_ZERO(&fds); |
184 | 204 |
185 /* listening sockets */ | 205 /* listening sockets */ |
186 for (i = 0; i < listensockcount; i++) { | 206 for (i = 0; i < listensockcount; i++) { |
187 FD_SET(listensocks[i], &fds); | 207 FD_SET(listensocks[i], &fds); |
188 } | 208 } |
189 | 209 |
199 | 219 |
200 if (ses.exitflag) { | 220 if (ses.exitflag) { |
201 unlink(svr_opts.pidfile); | 221 unlink(svr_opts.pidfile); |
202 dropbear_exit("Terminated by signal"); | 222 dropbear_exit("Terminated by signal"); |
203 } | 223 } |
204 | 224 |
205 if (val == 0) { | 225 if (val == 0) { |
206 /* timeout reached - shouldn't happen. eh */ | 226 /* timeout reached - shouldn't happen. eh */ |
207 continue; | 227 continue; |
208 } | 228 } |
209 | 229 |
284 dropbear_log(LOG_WARNING, "Error forking: %s", strerror(errno)); | 304 dropbear_log(LOG_WARNING, "Error forking: %s", strerror(errno)); |
285 goto out; | 305 goto out; |
286 } | 306 } |
287 | 307 |
288 addrandom((void*)&fork_ret, sizeof(fork_ret)); | 308 addrandom((void*)&fork_ret, sizeof(fork_ret)); |
289 | 309 |
290 if (fork_ret > 0) { | 310 if (fork_ret > 0) { |
291 | 311 |
292 /* parent */ | 312 /* parent */ |
293 childpipes[conn_idx] = childpipe[0]; | 313 childpipes[conn_idx] = childpipe[0]; |
294 m_close(childpipe[1]); | 314 m_close(childpipe[1]); |
313 for (j = 0; j < listensockcount; j++) { | 333 for (j = 0; j < listensockcount; j++) { |
314 m_close(listensocks[j]); | 334 m_close(listensocks[j]); |
315 } | 335 } |
316 | 336 |
317 m_close(childpipe[0]); | 337 m_close(childpipe[0]); |
338 | |
339 if (execfd >= 0) { | |
340 #if DROPBEAR_DO_REEXEC | |
341 /* Add "-2" to the args and re-execute ourself */ | |
342 char **new_argv = m_malloc(sizeof(char*) * (argc+1)); | |
343 memcpy(new_argv, argv, sizeof(char*) * argc); | |
344 new_argv[argc] = "-2"; | |
345 | |
346 if ((dup2(childsock, STDIN_FILENO) < 0)) { | |
347 dropbear_exit("dup2 failed: %s", strerror(errno)); | |
348 } | |
349 m_close(childsock); | |
350 /* Re-execute ourself */ | |
351 fexecve(execfd, new_argv, environ); | |
352 /* Not reached on success */ | |
353 | |
354 /* Fall back on plain fork otherwise */ | |
355 TRACE(("fexecve failed, disabling re-exec: %s", strerror(errno))) | |
356 m_free(new_argv); | |
357 #endif /* DROPBEAR_DO_REEXEC */ | |
358 } | |
318 | 359 |
319 /* start the session */ | 360 /* start the session */ |
320 svr_session(childsock, childpipe[1]); | 361 svr_session(childsock, childpipe[1]); |
321 /* don't return */ | 362 /* don't return */ |
322 dropbear_assert(0); | 363 dropbear_assert(0); |