/src/dropbear/src/common-session.c
Line | Count | Source |
1 | | /* |
2 | | * Dropbear - a SSH2 server |
3 | | * |
4 | | * Copyright (c) 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 "session.h" |
27 | | #include "dbutil.h" |
28 | | #include "packet.h" |
29 | | #include "algo.h" |
30 | | #include "buffer.h" |
31 | | #include "dss.h" |
32 | | #include "ssh.h" |
33 | | #include "dbrandom.h" |
34 | | #include "kex.h" |
35 | | #include "channel.h" |
36 | | #include "runopts.h" |
37 | | #include "netio.h" |
38 | | |
39 | | static void checktimeouts(void); |
40 | | static long select_timeout(void); |
41 | | static int ident_readln(int fd, char* buf, int count); |
42 | | static void read_session_identification(void); |
43 | | |
44 | | struct sshsession ses; /* GLOBAL */ |
45 | | |
46 | | /* called only at the start of a session, set up initial state */ |
47 | 3.30k | void common_session_init(int sock_in, int sock_out) { |
48 | 3.30k | time_t now; |
49 | | |
50 | | #if DEBUG_TRACE |
51 | | debug_start_net(); |
52 | | #endif |
53 | | |
54 | 3.30k | TRACE(("enter session_init")) |
55 | | |
56 | 3.30k | ses.sock_in = sock_in; |
57 | 3.30k | ses.sock_out = sock_out; |
58 | 3.30k | ses.maxfd = MAX(sock_in, sock_out); |
59 | | |
60 | 3.30k | if (sock_in >= 0) { |
61 | 3.30k | setnonblocking(sock_in); |
62 | 3.30k | } |
63 | 3.30k | if (sock_out >= 0) { |
64 | 3.30k | setnonblocking(sock_out); |
65 | 3.30k | } |
66 | | |
67 | 3.30k | ses.socket_prio = DROPBEAR_PRIO_NORMAL; |
68 | | /* Sets it to lowdelay */ |
69 | 3.30k | update_channel_prio(); |
70 | | |
71 | | #if !DROPBEAR_SVR_MULTIUSER |
72 | | /* A sanity check to prevent an accidental configuration option |
73 | | leaving multiuser systems exposed */ |
74 | | { |
75 | | int ret; |
76 | | errno = 0; |
77 | | ret = getgroups(0, NULL); |
78 | | if (!(ret == -1 && errno == ENOSYS)) { |
79 | | dropbear_exit("Non-multiuser Dropbear requires a non-multiuser kernel"); |
80 | | } |
81 | | } |
82 | | #endif |
83 | | |
84 | 3.30k | now = monotonic_now(); |
85 | 3.30k | ses.connect_time = now; |
86 | 3.30k | ses.last_packet_time_keepalive_recv = now; |
87 | 3.30k | ses.last_packet_time_idle = now; |
88 | 3.30k | ses.last_packet_time_any_sent = 0; |
89 | 3.30k | ses.last_packet_time_keepalive_sent = 0; |
90 | | |
91 | 3.30k | #if DROPBEAR_FUZZ |
92 | 3.30k | if (!fuzz.fuzzing) |
93 | 0 | #endif |
94 | 0 | { |
95 | 0 | if (pipe(ses.signal_pipe) < 0) { |
96 | 0 | dropbear_exit("Signal pipe failed"); |
97 | 0 | } |
98 | 0 | setnonblocking(ses.signal_pipe[0]); |
99 | 0 | setnonblocking(ses.signal_pipe[1]); |
100 | 0 | ses.maxfd = MAX(ses.maxfd, ses.signal_pipe[0]); |
101 | 0 | ses.maxfd = MAX(ses.maxfd, ses.signal_pipe[1]); |
102 | 0 | } |
103 | | |
104 | 3.30k | ses.writepayload = buf_new(TRANS_MAX_PAYLOAD_LEN); |
105 | 3.30k | ses.transseq = 0; |
106 | | |
107 | 3.30k | ses.readbuf = NULL; |
108 | 3.30k | ses.payload = NULL; |
109 | 3.30k | ses.recvseq = 0; |
110 | | |
111 | 3.30k | initqueue(&ses.writequeue); |
112 | | |
113 | 3.30k | ses.requirenext = SSH_MSG_KEXINIT; |
114 | 3.30k | ses.dataallowed = 1; /* we can send data until we actually |
115 | | send the SSH_MSG_KEXINIT */ |
116 | 3.30k | ses.ignorenext = 0; |
117 | 3.30k | ses.lastpacket = 0; |
118 | 3.30k | ses.reply_queue_head = NULL; |
119 | 3.30k | ses.reply_queue_tail = NULL; |
120 | 3.30k | ses.reply_queue_len = 0; |
121 | | |
122 | | /* set all the algos to none */ |
123 | 3.30k | ses.keys = (struct key_context*)m_malloc(sizeof(struct key_context)); |
124 | 3.30k | ses.newkeys = NULL; |
125 | 3.30k | ses.keys->recv.algo_crypt = &dropbear_nocipher; |
126 | 3.30k | ses.keys->trans.algo_crypt = &dropbear_nocipher; |
127 | 3.30k | ses.keys->recv.crypt_mode = &dropbear_mode_none; |
128 | 3.30k | ses.keys->trans.crypt_mode = &dropbear_mode_none; |
129 | | |
130 | 3.30k | ses.keys->recv.algo_mac = &dropbear_nohash; |
131 | 3.30k | ses.keys->trans.algo_mac = &dropbear_nohash; |
132 | | |
133 | 3.30k | ses.keys->algo_kex = NULL; |
134 | 3.30k | ses.keys->algo_hostkey = -1; |
135 | 3.30k | ses.keys->recv.algo_comp = DROPBEAR_COMP_NONE; |
136 | 3.30k | ses.keys->trans.algo_comp = DROPBEAR_COMP_NONE; |
137 | | |
138 | | #ifndef DISABLE_ZLIB |
139 | | ses.keys->recv.zstream = NULL; |
140 | | ses.keys->trans.zstream = NULL; |
141 | | #endif |
142 | | |
143 | | /* key exchange buffers */ |
144 | 3.30k | ses.session_id = NULL; |
145 | 3.30k | ses.kexhashbuf = NULL; |
146 | 3.30k | ses.transkexinit = NULL; |
147 | 3.30k | ses.dh_K = NULL; |
148 | 3.30k | ses.remoteident = NULL; |
149 | | |
150 | 3.30k | ses.chantypes = NULL; |
151 | | |
152 | 3.30k | ses.allowprivport = 0; |
153 | | |
154 | | #if DROPBEAR_PLUGIN |
155 | | ses.plugin_session = NULL; |
156 | | #endif |
157 | | |
158 | 3.30k | TRACE(("leave session_init")) |
159 | 3.30k | } |
160 | | |
161 | 3.30k | void session_loop(void(*loophandler)(void)) { |
162 | | |
163 | 3.30k | fd_set readfd, writefd; |
164 | 3.30k | struct timeval timeout; |
165 | 3.30k | int val; |
166 | | |
167 | | /* main loop, select()s for all sockets in use */ |
168 | 95.7k | for(;;) { |
169 | 95.7k | const int writequeue_has_space = (ses.writequeue_len <= 2*TRANS_MAX_PAYLOAD_LEN); |
170 | | |
171 | 95.7k | timeout.tv_sec = select_timeout(); |
172 | 95.7k | timeout.tv_usec = 0; |
173 | 95.7k | DROPBEAR_FD_ZERO(&writefd); |
174 | 95.7k | DROPBEAR_FD_ZERO(&readfd); |
175 | | |
176 | 95.7k | dropbear_assert(ses.payload == NULL); |
177 | | |
178 | | /* We get woken up when signal handlers write to this pipe. |
179 | | SIGCHLD in svr-chansession is the only one currently. */ |
180 | 95.7k | #if DROPBEAR_FUZZ |
181 | 95.7k | if (!fuzz.fuzzing) |
182 | 0 | #endif |
183 | 0 | { |
184 | 0 | dropbear_fd_set(ses.signal_pipe[0], &readfd); |
185 | 0 | } |
186 | | |
187 | | /* set up for channels which can be read/written */ |
188 | 95.7k | setchannelfds(&readfd, &writefd, writequeue_has_space); |
189 | | |
190 | | /* Pending connections to test */ |
191 | 95.7k | set_connect_fds(&writefd); |
192 | | |
193 | | /* We delay reading from the input socket during initial setup until |
194 | | after we have written out our initial KEXINIT packet (empty writequeue). |
195 | | This means our initial packet can be in-flight while we're doing a blocking |
196 | | read for the remote ident. |
197 | | We also avoid reading from the socket if the writequeue is full, that avoids |
198 | | replies backing up */ |
199 | 95.7k | if (ses.sock_in != -1 |
200 | 95.7k | && (ses.remoteident || isempty(&ses.writequeue)) |
201 | 85.8k | && writequeue_has_space) { |
202 | 85.8k | dropbear_fd_set(ses.sock_in, &readfd); |
203 | 85.8k | } |
204 | | |
205 | | /* Ordering is important, this test must occur after any other function |
206 | | might have queued packets (such as connection handlers) */ |
207 | 95.7k | if (ses.sock_out != -1 && !isempty(&ses.writequeue)) { |
208 | 9.92k | dropbear_fd_set(ses.sock_out, &writefd); |
209 | 9.92k | } |
210 | | |
211 | 95.7k | val = select(ses.maxfd+1, &readfd, &writefd, NULL, &timeout); |
212 | | |
213 | 95.7k | if (ses.exitflag) { |
214 | 0 | dropbear_exit("Terminated by signal"); |
215 | 0 | } |
216 | | |
217 | 95.7k | if (val < 0 && errno != EINTR) { |
218 | 0 | dropbear_exit("Error in select"); |
219 | 0 | } |
220 | | |
221 | 95.7k | if (val <= 0) { |
222 | | /* If we were interrupted or the select timed out, we still |
223 | | * want to iterate over channels etc for reading, to handle |
224 | | * server processes exiting etc. |
225 | | * We don't want to read/write FDs. */ |
226 | 3.40k | DROPBEAR_FD_ZERO(&writefd); |
227 | 3.40k | DROPBEAR_FD_ZERO(&readfd); |
228 | 3.40k | } |
229 | | |
230 | | /* We'll just empty out the pipe if required. We don't do |
231 | | any thing with the data, since the pipe's purpose is purely to |
232 | | wake up the select() above. */ |
233 | 95.7k | ses.channel_signal_pending = 0; |
234 | 95.7k | if (FD_ISSET(ses.signal_pipe[0], &readfd)) { |
235 | 0 | char x; |
236 | 0 | TRACE(("signal pipe set")) |
237 | 0 | while (read(ses.signal_pipe[0], &x, 1) > 0) {} |
238 | 0 | ses.channel_signal_pending = 1; |
239 | 0 | } |
240 | | |
241 | | /* check for auth timeout, rekeying required etc */ |
242 | 95.7k | checktimeouts(); |
243 | | |
244 | | /* process session socket's incoming data */ |
245 | 95.7k | if (ses.sock_in != -1) { |
246 | 95.7k | if (FD_ISSET(ses.sock_in, &readfd)) { |
247 | 82.6k | if (!ses.remoteident) { |
248 | | /* blocking read of the version string */ |
249 | 3.30k | read_session_identification(); |
250 | 79.3k | } else { |
251 | 79.3k | read_packet(); |
252 | 79.3k | } |
253 | 82.6k | } |
254 | | |
255 | | /* Process the decrypted packet. After this, the read buffer |
256 | | * will be ready for a new packet */ |
257 | 95.7k | if (ses.payload != NULL) { |
258 | 19.0k | process_packet(); |
259 | 19.0k | } |
260 | 95.7k | } |
261 | | |
262 | | /* if required, flush out any queued reply packets that |
263 | | were being held up during a KEX */ |
264 | 95.7k | maybe_flush_reply_queue(); |
265 | | |
266 | 95.7k | handle_connect_fds(&writefd); |
267 | | |
268 | | /* loop handler prior to channelio, in case the server loophandler closes |
269 | | channels on process exit */ |
270 | 95.7k | loophandler(); |
271 | | |
272 | | /* process pipes etc for the channels, ses.dataallowed == 0 |
273 | | * during rekeying ) */ |
274 | 95.7k | channelio(&readfd, &writefd); |
275 | | |
276 | | /* process session socket's outgoing data */ |
277 | 95.7k | if (ses.sock_out != -1) { |
278 | 92.4k | if (!isempty(&ses.writequeue)) { |
279 | 26.2k | write_packet(); |
280 | 26.2k | } |
281 | 92.4k | } |
282 | | |
283 | 95.7k | } /* for(;;) */ |
284 | | |
285 | | /* Not reached */ |
286 | 3.30k | } |
287 | | |
288 | 23.1k | static void cleanup_buf(buffer **buf) { |
289 | 23.1k | if (!*buf) { |
290 | 11.8k | return; |
291 | 11.8k | } |
292 | 11.3k | buf_burn_free(*buf); |
293 | 11.3k | *buf = NULL; |
294 | 11.3k | } |
295 | | |
296 | | /* clean up a session on exit */ |
297 | 3.30k | void session_cleanup() { |
298 | | |
299 | 3.30k | TRACE(("enter session_cleanup")) |
300 | | |
301 | | /* we can't cleanup if we don't know the session state */ |
302 | 3.30k | if (!ses.init_done) { |
303 | 0 | TRACE(("leave session_cleanup: !ses.init_done")) |
304 | 0 | return; |
305 | 0 | } |
306 | | |
307 | | /* BEWARE of changing order of functions here. */ |
308 | | |
309 | | /* Must be before extra_session_cleanup() */ |
310 | 3.30k | chancleanup(); |
311 | | |
312 | 3.30k | if (ses.extra_session_cleanup) { |
313 | 3.30k | ses.extra_session_cleanup(); |
314 | 3.30k | } |
315 | | |
316 | | /* After these are freed most functions will fail */ |
317 | 3.30k | #if DROPBEAR_CLEANUP |
318 | | /* listeners call cleanup functions, this should occur before |
319 | | other session state is freed. */ |
320 | 3.30k | remove_all_listeners(); |
321 | | |
322 | 3.30k | remove_connect_pending(); |
323 | | |
324 | 3.30k | while (!isempty(&ses.writequeue)) { |
325 | 0 | buf_free(dequeue(&ses.writequeue)); |
326 | 0 | } |
327 | | |
328 | 3.30k | m_free(ses.newkeys); |
329 | | #ifndef DISABLE_ZLIB |
330 | | if (ses.keys->recv.zstream != NULL) { |
331 | | if (inflateEnd(ses.keys->recv.zstream) == Z_STREAM_ERROR) { |
332 | | dropbear_exit("Crypto error"); |
333 | | } |
334 | | m_free(ses.keys->recv.zstream); |
335 | | } |
336 | | if (ses.keys->trans.zstream != NULL) { |
337 | | if (deflateEnd(ses.keys->trans.zstream) == Z_STREAM_ERROR) { |
338 | | dropbear_exit("Crypto error"); |
339 | | } |
340 | | m_free(ses.keys->trans.zstream); |
341 | | } |
342 | | #endif |
343 | | |
344 | 3.30k | m_free(ses.remoteident); |
345 | 3.30k | m_free(ses.authstate.pw_dir); |
346 | 3.30k | m_free(ses.authstate.pw_name); |
347 | 3.30k | m_free(ses.authstate.pw_shell); |
348 | 3.30k | m_free(ses.authstate.pw_passwd); |
349 | 3.30k | m_free(ses.authstate.username); |
350 | 3.30k | #endif |
351 | | |
352 | 3.30k | cleanup_buf(&ses.session_id); |
353 | 3.30k | cleanup_buf(&ses.hash); |
354 | 3.30k | cleanup_buf(&ses.payload); |
355 | 3.30k | cleanup_buf(&ses.readbuf); |
356 | 3.30k | cleanup_buf(&ses.writepayload); |
357 | 3.30k | cleanup_buf(&ses.kexhashbuf); |
358 | 3.30k | cleanup_buf(&ses.transkexinit); |
359 | 3.30k | if (ses.dh_K) { |
360 | 1.22k | mp_clear(ses.dh_K); |
361 | 1.22k | } |
362 | 3.30k | m_free(ses.dh_K); |
363 | 3.30k | if (ses.dh_K_bytes) { |
364 | 43 | buf_burn_free(ses.dh_K_bytes); |
365 | 43 | } |
366 | | |
367 | 3.30k | m_burn(ses.keys, sizeof(struct key_context)); |
368 | 3.30k | m_free(ses.keys); |
369 | | |
370 | 3.30k | TRACE(("leave session_cleanup")) |
371 | 3.30k | } |
372 | | |
373 | 3.30k | void send_session_identification() { |
374 | 3.30k | buffer *writebuf = buf_new(strlen(LOCAL_IDENT "\r\n") + 1); |
375 | 3.30k | buf_putbytes(writebuf, (const unsigned char *) LOCAL_IDENT "\r\n", strlen(LOCAL_IDENT "\r\n")); |
376 | 3.30k | writebuf_enqueue(writebuf); |
377 | 3.30k | } |
378 | | |
379 | 3.30k | static void read_session_identification() { |
380 | | /* max length of 255 chars */ |
381 | 3.30k | char linebuf[256]; |
382 | 3.30k | int len = 0; |
383 | 3.30k | char done = 0; |
384 | 3.30k | int i; |
385 | | |
386 | | /* Servers may send other lines of data before sending the |
387 | | * version string, client must be able to process such lines. |
388 | | * If they send more than 50 lines, something is wrong */ |
389 | 6.42k | for (i = IS_DROPBEAR_CLIENT ? 50 : 1; i > 0; i--) { |
390 | 6.36k | len = ident_readln(ses.sock_in, linebuf, sizeof(linebuf)); |
391 | | |
392 | 6.36k | if (len < 0 && errno != EINTR) { |
393 | | /* It failed */ |
394 | 92 | break; |
395 | 92 | } |
396 | | |
397 | 6.27k | if (len >= 4 && memcmp(linebuf, "SSH-", 4) == 0) { |
398 | | /* start of line matches */ |
399 | 3.16k | done = 1; |
400 | 3.16k | break; |
401 | 3.16k | } |
402 | 6.27k | } |
403 | | |
404 | 3.30k | if (!done) { |
405 | 147 | TRACE(("error reading remote ident: %s\n", strerror(errno))) |
406 | 147 | ses.remoteclosed(); |
407 | 3.16k | } else { |
408 | | /* linebuf is already null terminated */ |
409 | 3.16k | ses.remoteident = m_malloc(len); |
410 | 3.16k | memcpy(ses.remoteident, linebuf, len); |
411 | 3.16k | } |
412 | | |
413 | | /* Shall assume that 2.x will be backwards compatible. */ |
414 | 3.30k | if (strncmp(ses.remoteident, "SSH-2.", 6) != 0 |
415 | 62 | && strncmp(ses.remoteident, "SSH-1.99-", 9) != 0) { |
416 | 61 | dropbear_exit("Incompatible remote version '%s'", ses.remoteident); |
417 | 61 | } |
418 | | |
419 | 3.30k | DEBUG1(("remoteident: %s", ses.remoteident)) |
420 | | |
421 | 3.30k | } |
422 | | |
423 | | /* returns the length including null-terminating zero on success, |
424 | | * or -1 on failure */ |
425 | 6.36k | static int ident_readln(int fd, char* buf, int count) { |
426 | | |
427 | 6.36k | char in; |
428 | 6.36k | int pos = 0; |
429 | 6.36k | int num = 0; |
430 | 6.36k | fd_set fds; |
431 | 6.36k | struct timeval timeout; |
432 | | |
433 | 6.36k | TRACE(("enter ident_readln")) |
434 | | |
435 | 6.36k | if (count < 1) { |
436 | 0 | return -1; |
437 | 0 | } |
438 | | |
439 | 6.36k | DROPBEAR_FD_ZERO(&fds); |
440 | | |
441 | | /* select since it's a non-blocking fd */ |
442 | | |
443 | | /* leave space to null-terminate */ |
444 | 91.4k | while (pos < count-1) { |
445 | | |
446 | 91.3k | dropbear_fd_set(fd, &fds); |
447 | | |
448 | 91.3k | timeout.tv_sec = 1; |
449 | 91.3k | timeout.tv_usec = 0; |
450 | 91.3k | if (select(fd+1, &fds, NULL, NULL, &timeout) < 0) { |
451 | 214 | if (errno == EINTR) { |
452 | 214 | continue; |
453 | 214 | } |
454 | 0 | TRACE(("leave ident_readln: select error")) |
455 | 0 | return -1; |
456 | 214 | } |
457 | | |
458 | 91.1k | checktimeouts(); |
459 | | |
460 | | /* Have to go one byte at a time, since we don't want to read past |
461 | | * the end, and have to somehow shove bytes back into the normal |
462 | | * packet reader */ |
463 | 91.1k | if (FD_ISSET(fd, &fds)) { |
464 | 91.1k | num = read(fd, &in, 1); |
465 | | /* a "\n" is a newline, "\r" we want to read in and keep going |
466 | | * so that it won't be read as part of the next line */ |
467 | 91.1k | if (num < 0) { |
468 | | /* error */ |
469 | 144 | if (errno == EINTR) { |
470 | 130 | continue; /* not a real error */ |
471 | 130 | } |
472 | 14 | TRACE(("leave ident_readln: read error")) |
473 | 14 | return -1; |
474 | 144 | } |
475 | 91.0k | if (num == 0) { |
476 | | /* EOF */ |
477 | 2.78k | TRACE(("leave ident_readln: EOF")) |
478 | 2.78k | return -1; |
479 | 2.78k | } |
480 | | |
481 | 88.2k | #if DROPBEAR_FUZZ |
482 | 88.2k | fuzz_dump(&in, 1); |
483 | 88.2k | #endif |
484 | | |
485 | 88.2k | if (in == '\n') { |
486 | | /* end of ident string */ |
487 | 3.43k | break; |
488 | 3.43k | } |
489 | | /* we don't want to include '\r's */ |
490 | 84.7k | if (in != '\r') { |
491 | 82.2k | buf[pos] = in; |
492 | 82.2k | pos++; |
493 | 82.2k | } |
494 | 84.7k | } |
495 | 91.1k | } |
496 | | |
497 | 3.56k | buf[pos] = '\0'; |
498 | 3.56k | TRACE(("leave ident_readln: return %d", pos+1)) |
499 | 3.56k | return pos+1; |
500 | 6.36k | } |
501 | | |
502 | 0 | void ignore_recv_response() { |
503 | | /* Do nothing */ |
504 | 0 | TRACE(("Ignored msg_request_response")) |
505 | 0 | } |
506 | | |
507 | 0 | static void send_msg_keepalive(void) { |
508 | 0 | time_t old_time_idle = ses.last_packet_time_idle; |
509 | 0 | struct Channel *chan = get_any_ready_channel(); |
510 | |
|
511 | 0 | CHECKCLEARTOWRITE(); |
512 | |
|
513 | 0 | if (chan) { |
514 | | /* Channel requests are preferable, more implementations |
515 | | handle them than SSH_MSG_GLOBAL_REQUEST */ |
516 | 0 | TRACE(("keepalive channel request %d", chan->index)) |
517 | 0 | start_send_channel_request(chan, DROPBEAR_KEEPALIVE_STRING); |
518 | 0 | } else { |
519 | 0 | TRACE(("keepalive global request")) |
520 | | /* Some peers will reply with SSH_MSG_REQUEST_FAILURE, |
521 | | some will reply with SSH_MSG_UNIMPLEMENTED, some will exit. */ |
522 | 0 | buf_putbyte(ses.writepayload, SSH_MSG_GLOBAL_REQUEST); |
523 | 0 | buf_putstring(ses.writepayload, DROPBEAR_KEEPALIVE_STRING, |
524 | 0 | strlen(DROPBEAR_KEEPALIVE_STRING)); |
525 | 0 | } |
526 | 0 | buf_putbyte(ses.writepayload, 1); /* want_reply */ |
527 | 0 | encrypt_packet(); |
528 | |
|
529 | 0 | ses.last_packet_time_keepalive_sent = monotonic_now(); |
530 | | |
531 | | /* keepalives shouldn't update idle timeout, reset it back */ |
532 | 0 | ses.last_packet_time_idle = old_time_idle; |
533 | 0 | } |
534 | | |
535 | | /* Returns the difference in seconds, clamped to LONG_MAX */ |
536 | 0 | static long elapsed(time_t now, time_t prev) { |
537 | 0 | time_t del = now - prev; |
538 | 0 | if (del > LONG_MAX) { |
539 | 0 | return LONG_MAX; |
540 | 0 | } |
541 | 0 | return (long)del; |
542 | 0 | } |
543 | | |
544 | | /* Check all timeouts which are required. Currently these are the time for |
545 | | * user authentication, and the automatic rekeying. */ |
546 | 186k | static void checktimeouts() { |
547 | | |
548 | 186k | time_t now; |
549 | 186k | now = monotonic_now(); |
550 | | |
551 | 186k | if (IS_DROPBEAR_SERVER && ses.authstate.authdone != 1 |
552 | 0 | && elapsed(now, ses.connect_time) >= AUTH_TIMEOUT) { |
553 | 0 | dropbear_close("Timeout before auth"); |
554 | 0 | } |
555 | | |
556 | | /* we can't rekey if we haven't done remote ident exchange yet */ |
557 | 186k | if (ses.remoteident == NULL) { |
558 | 104k | return; |
559 | 104k | } |
560 | | |
561 | 82.3k | if (!ses.kexstate.sentkexinit |
562 | 0 | && (elapsed(now, ses.kexstate.lastkextime) >= KEX_REKEY_TIMEOUT |
563 | 0 | || ses.kexstate.datarecv+ses.kexstate.datatrans >= KEX_REKEY_DATA |
564 | 0 | || ses.kexstate.needrekey)) { |
565 | 0 | TRACE(("rekeying after timeout or max data reached")) |
566 | 0 | ses.kexstate.needrekey = 0; |
567 | 0 | send_msg_kexinit(); |
568 | 0 | } |
569 | | |
570 | 82.3k | if (opts.keepalive_secs > 0 && ses.authstate.authdone) { |
571 | | /* Avoid sending keepalives prior to auth - those are |
572 | | not valid pre-auth packet types */ |
573 | | |
574 | | /* Send keepalives if we've been idle */ |
575 | 0 | if (elapsed(now, ses.last_packet_time_any_sent) >= opts.keepalive_secs) { |
576 | 0 | send_msg_keepalive(); |
577 | 0 | } |
578 | | |
579 | | /* Also send an explicit keepalive message to trigger a response |
580 | | if the remote end hasn't sent us anything */ |
581 | 0 | if (elapsed(now, ses.last_packet_time_keepalive_recv) >= opts.keepalive_secs |
582 | 0 | && elapsed(now, ses.last_packet_time_keepalive_sent) >= opts.keepalive_secs) { |
583 | 0 | send_msg_keepalive(); |
584 | 0 | } |
585 | |
|
586 | 0 | if (elapsed(now, ses.last_packet_time_keepalive_recv) |
587 | 0 | >= opts.keepalive_secs * DEFAULT_KEEPALIVE_LIMIT) { |
588 | 0 | dropbear_exit("Keepalive timeout"); |
589 | 0 | } |
590 | 0 | } |
591 | | |
592 | 82.3k | if (opts.idle_timeout_secs > 0 |
593 | 0 | && elapsed(now, ses.last_packet_time_idle) >= opts.idle_timeout_secs) { |
594 | 0 | dropbear_close("Idle timeout"); |
595 | 0 | } |
596 | | |
597 | 82.3k | if (opts.max_duration_secs > 0 |
598 | 0 | && elapsed(now, ses.connect_time) >= opts.max_duration_secs) { |
599 | 0 | dropbear_close("Max duration reached"); |
600 | 0 | } |
601 | 82.3k | } |
602 | | |
603 | 191k | static void update_timeout(long limit, time_t now, time_t last_event, long * timeout) { |
604 | 191k | TRACE2(("update_timeout limit %ld, now %llu, last %llu, timeout %ld", |
605 | 191k | limit, |
606 | 191k | (unsigned long long)now, |
607 | 191k | (unsigned long long)last_event, *timeout)) |
608 | 191k | if (last_event > 0 && limit > 0) { |
609 | 0 | *timeout = MIN(*timeout, MAX(0, limit - elapsed(now, last_event))); |
610 | 0 | TRACE2(("new timeout %ld", *timeout)) |
611 | 0 | } |
612 | 191k | } |
613 | | |
614 | 95.7k | static long select_timeout() { |
615 | | /* determine the minimum timeout that might be required, so |
616 | | as to avoid waking when unneccessary */ |
617 | 95.7k | long timeout = KEX_REKEY_TIMEOUT; |
618 | 95.7k | time_t now = monotonic_now(); |
619 | | |
620 | 95.7k | if (!ses.kexstate.sentkexinit) { |
621 | 0 | update_timeout(KEX_REKEY_TIMEOUT, now, ses.kexstate.lastkextime, &timeout); |
622 | 0 | } |
623 | 95.7k | if (ses.kexstate.needrekey) { |
624 | 0 | timeout = 0; |
625 | 0 | } |
626 | | |
627 | 95.7k | if (ses.authstate.authdone != 1 && IS_DROPBEAR_SERVER) { |
628 | | /* AUTH_TIMEOUT is only relevant before authdone */ |
629 | 0 | update_timeout(AUTH_TIMEOUT, now, ses.connect_time, &timeout); |
630 | 0 | } |
631 | | |
632 | 95.7k | if (ses.authstate.authdone) { |
633 | 0 | update_timeout(opts.keepalive_secs, now, |
634 | 0 | MAX(ses.last_packet_time_keepalive_recv, ses.last_packet_time_keepalive_sent), |
635 | 0 | &timeout); |
636 | 0 | } |
637 | | |
638 | 95.7k | update_timeout(opts.idle_timeout_secs, now, ses.last_packet_time_idle, |
639 | 95.7k | &timeout); |
640 | | |
641 | 95.7k | update_timeout(opts.max_duration_secs, now, ses.connect_time, |
642 | 95.7k | &timeout); |
643 | | |
644 | | /* clamp negative timeouts to zero - event has already triggered */ |
645 | 95.7k | return MAX(timeout, 0); |
646 | 95.7k | } |
647 | | |
648 | 0 | const char* get_user_shell() { |
649 | | /* an empty shell should be interpreted as "/bin/sh" */ |
650 | 0 | if (ses.authstate.pw_shell[0] == '\0') { |
651 | 0 | return "/bin/sh"; |
652 | 0 | } else { |
653 | 0 | return ses.authstate.pw_shell; |
654 | 0 | } |
655 | 0 | } |
656 | 0 | void fill_passwd(const char* username) { |
657 | 0 | struct passwd *pw = NULL; |
658 | 0 | if (ses.authstate.pw_name) |
659 | 0 | m_free(ses.authstate.pw_name); |
660 | 0 | if (ses.authstate.pw_dir) |
661 | 0 | m_free(ses.authstate.pw_dir); |
662 | 0 | if (ses.authstate.pw_shell) |
663 | 0 | m_free(ses.authstate.pw_shell); |
664 | 0 | if (ses.authstate.pw_passwd) |
665 | 0 | m_free(ses.authstate.pw_passwd); |
666 | |
|
667 | 0 | pw = getpwnam(username); |
668 | 0 | if (!pw) { |
669 | 0 | return; |
670 | 0 | } |
671 | 0 | ses.authstate.pw_uid = pw->pw_uid; |
672 | 0 | ses.authstate.pw_gid = pw->pw_gid; |
673 | 0 | ses.authstate.pw_name = m_strdup(pw->pw_name); |
674 | 0 | ses.authstate.pw_dir = m_strdup(pw->pw_dir); |
675 | 0 | ses.authstate.pw_shell = m_strdup(pw->pw_shell); |
676 | 0 | { |
677 | 0 | char *passwd_crypt = pw->pw_passwd; |
678 | 0 | #ifdef HAVE_SHADOW_H |
679 | | /* "x" for the passwd crypt indicates shadow should be used */ |
680 | 0 | if (pw->pw_passwd && strcmp(pw->pw_passwd, "x") == 0) { |
681 | | /* get the shadow password */ |
682 | 0 | struct spwd *spasswd = getspnam(ses.authstate.pw_name); |
683 | 0 | if (spasswd && spasswd->sp_pwdp) { |
684 | 0 | passwd_crypt = spasswd->sp_pwdp; |
685 | 0 | } else { |
686 | | /* Fail if missing in /etc/shadow */ |
687 | 0 | passwd_crypt = "!!"; |
688 | 0 | } |
689 | 0 | } |
690 | 0 | #endif |
691 | 0 | if (!passwd_crypt) { |
692 | | /* android supposedly returns NULL */ |
693 | 0 | passwd_crypt = "!!"; |
694 | 0 | } |
695 | 0 | ses.authstate.pw_passwd = m_strdup(passwd_crypt); |
696 | 0 | } |
697 | 0 | } |
698 | | |
699 | | /* Called when channels are modified */ |
700 | 3.30k | void update_channel_prio() { |
701 | 3.30k | enum dropbear_prio new_prio; |
702 | 3.30k | int any = 0; |
703 | 3.30k | unsigned int i; |
704 | | |
705 | 3.30k | TRACE(("update_channel_prio")) |
706 | | |
707 | 3.30k | if (ses.sock_out < 0) { |
708 | 0 | TRACE(("leave update_channel_prio: no socket")) |
709 | 0 | return; |
710 | 0 | } |
711 | | |
712 | 3.30k | new_prio = DROPBEAR_PRIO_NORMAL; |
713 | 3.30k | for (i = 0; i < ses.chansize; i++) { |
714 | 0 | struct Channel *channel = ses.channels[i]; |
715 | 0 | if (!channel) { |
716 | 0 | continue; |
717 | 0 | } |
718 | 0 | any = 1; |
719 | 0 | if (channel->prio == DROPBEAR_PRIO_LOWDELAY) { |
720 | 0 | new_prio = DROPBEAR_PRIO_LOWDELAY; |
721 | 0 | break; |
722 | 0 | } |
723 | 0 | } |
724 | | |
725 | 3.30k | if (any == 0) { |
726 | | /* lowdelay during setup */ |
727 | 3.30k | TRACE(("update_channel_prio: not any")) |
728 | 3.30k | new_prio = DROPBEAR_PRIO_LOWDELAY; |
729 | 3.30k | } |
730 | | |
731 | 3.30k | if (new_prio != ses.socket_prio) { |
732 | 3.30k | TRACE(("Dropbear priority transitioning %d -> %d", ses.socket_prio, new_prio)) |
733 | 3.30k | set_sock_priority(ses.sock_out, new_prio); |
734 | 3.30k | ses.socket_prio = new_prio; |
735 | 3.30k | } |
736 | 3.30k | } |
737 | | |