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