/src/dropbear/src/common-session.c
Line | Count | Source (jump to first uncovered line) |
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 | 0 | void common_session_init(int sock_in, int sock_out) { |
48 | 0 | time_t now; |
49 | |
|
50 | | #if DEBUG_TRACE |
51 | | debug_start_net(); |
52 | | #endif |
53 | |
|
54 | 0 | TRACE(("enter session_init")) |
55 | |
|
56 | 0 | ses.sock_in = sock_in; |
57 | 0 | ses.sock_out = sock_out; |
58 | 0 | ses.maxfd = MAX(sock_in, sock_out); |
59 | |
|
60 | 0 | if (sock_in >= 0) { |
61 | 0 | setnonblocking(sock_in); |
62 | 0 | } |
63 | 0 | if (sock_out >= 0) { |
64 | 0 | setnonblocking(sock_out); |
65 | 0 | } |
66 | |
|
67 | 0 | ses.socket_prio = DROPBEAR_PRIO_NORMAL; |
68 | | /* Sets it to lowdelay */ |
69 | 0 | 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 | 0 | now = monotonic_now(); |
85 | 0 | ses.connect_time = now; |
86 | 0 | ses.last_packet_time_keepalive_recv = now; |
87 | 0 | ses.last_packet_time_idle = now; |
88 | 0 | ses.last_packet_time_any_sent = 0; |
89 | 0 | ses.last_packet_time_keepalive_sent = 0; |
90 | | |
91 | 0 | #if DROPBEAR_FUZZ |
92 | 0 | 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 | 0 | ses.writepayload = buf_new(TRANS_MAX_PAYLOAD_LEN); |
105 | 0 | ses.transseq = 0; |
106 | |
|
107 | 0 | ses.readbuf = NULL; |
108 | 0 | ses.payload = NULL; |
109 | 0 | ses.recvseq = 0; |
110 | |
|
111 | 0 | initqueue(&ses.writequeue); |
112 | |
|
113 | 0 | ses.requirenext = SSH_MSG_KEXINIT; |
114 | 0 | ses.dataallowed = 1; /* we can send data until we actually |
115 | | send the SSH_MSG_KEXINIT */ |
116 | 0 | ses.ignorenext = 0; |
117 | 0 | ses.lastpacket = 0; |
118 | 0 | ses.reply_queue_head = NULL; |
119 | 0 | ses.reply_queue_tail = NULL; |
120 | | |
121 | | /* set all the algos to none */ |
122 | 0 | ses.keys = (struct key_context*)m_malloc(sizeof(struct key_context)); |
123 | 0 | ses.newkeys = NULL; |
124 | 0 | ses.keys->recv.algo_crypt = &dropbear_nocipher; |
125 | 0 | ses.keys->trans.algo_crypt = &dropbear_nocipher; |
126 | 0 | ses.keys->recv.crypt_mode = &dropbear_mode_none; |
127 | 0 | ses.keys->trans.crypt_mode = &dropbear_mode_none; |
128 | | |
129 | 0 | ses.keys->recv.algo_mac = &dropbear_nohash; |
130 | 0 | ses.keys->trans.algo_mac = &dropbear_nohash; |
131 | |
|
132 | 0 | ses.keys->algo_kex = NULL; |
133 | 0 | ses.keys->algo_hostkey = -1; |
134 | 0 | ses.keys->recv.algo_comp = DROPBEAR_COMP_NONE; |
135 | 0 | 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 | 0 | ses.session_id = NULL; |
144 | 0 | ses.kexhashbuf = NULL; |
145 | 0 | ses.transkexinit = NULL; |
146 | 0 | ses.dh_K = NULL; |
147 | 0 | ses.remoteident = NULL; |
148 | |
|
149 | 0 | ses.chantypes = NULL; |
150 | |
|
151 | 0 | ses.allowprivport = 0; |
152 | |
|
153 | | #if DROPBEAR_PLUGIN |
154 | | ses.plugin_session = NULL; |
155 | | #endif |
156 | |
|
157 | 0 | TRACE(("leave session_init")) |
158 | 0 | } |
159 | | |
160 | 0 | void session_loop(void(*loophandler)(void)) { |
161 | |
|
162 | 0 | fd_set readfd, writefd; |
163 | 0 | struct timeval timeout; |
164 | 0 | int val; |
165 | | |
166 | | /* main loop, select()s for all sockets in use */ |
167 | 0 | for(;;) { |
168 | 0 | const int writequeue_has_space = (ses.writequeue_len <= 2*TRANS_MAX_PAYLOAD_LEN); |
169 | |
|
170 | 0 | timeout.tv_sec = select_timeout(); |
171 | 0 | timeout.tv_usec = 0; |
172 | 0 | DROPBEAR_FD_ZERO(&writefd); |
173 | 0 | DROPBEAR_FD_ZERO(&readfd); |
174 | |
|
175 | 0 | 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 | 0 | #if DROPBEAR_FUZZ |
180 | 0 | 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 | 0 | setchannelfds(&readfd, &writefd, writequeue_has_space); |
188 | | |
189 | | /* Pending connections to test */ |
190 | 0 | 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 | 0 | if (ses.sock_in != -1 |
199 | 0 | && (ses.remoteident || isempty(&ses.writequeue)) |
200 | 0 | && writequeue_has_space) { |
201 | 0 | FD_SET(ses.sock_in, &readfd); |
202 | 0 | } |
203 | | |
204 | | /* Ordering is important, this test must occur after any other function |
205 | | might have queued packets (such as connection handlers) */ |
206 | 0 | if (ses.sock_out != -1 && !isempty(&ses.writequeue)) { |
207 | 0 | FD_SET(ses.sock_out, &writefd); |
208 | 0 | } |
209 | |
|
210 | 0 | val = select(ses.maxfd+1, &readfd, &writefd, NULL, &timeout); |
211 | |
|
212 | 0 | if (ses.exitflag) { |
213 | 0 | dropbear_exit("Terminated by signal"); |
214 | 0 | } |
215 | | |
216 | 0 | if (val < 0 && errno != EINTR) { |
217 | 0 | dropbear_exit("Error in select"); |
218 | 0 | } |
219 | | |
220 | 0 | 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 | 0 | DROPBEAR_FD_ZERO(&writefd); |
226 | 0 | DROPBEAR_FD_ZERO(&readfd); |
227 | 0 | } |
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 | 0 | ses.channel_signal_pending = 0; |
233 | 0 | 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 | 0 | checktimeouts(); |
242 | | |
243 | | /* process session socket's incoming data */ |
244 | 0 | if (ses.sock_in != -1) { |
245 | 0 | if (FD_ISSET(ses.sock_in, &readfd)) { |
246 | 0 | if (!ses.remoteident) { |
247 | | /* blocking read of the version string */ |
248 | 0 | read_session_identification(); |
249 | 0 | } else { |
250 | 0 | read_packet(); |
251 | 0 | } |
252 | 0 | } |
253 | | |
254 | | /* Process the decrypted packet. After this, the read buffer |
255 | | * will be ready for a new packet */ |
256 | 0 | if (ses.payload != NULL) { |
257 | 0 | process_packet(); |
258 | 0 | } |
259 | 0 | } |
260 | | |
261 | | /* if required, flush out any queued reply packets that |
262 | | were being held up during a KEX */ |
263 | 0 | maybe_flush_reply_queue(); |
264 | |
|
265 | 0 | handle_connect_fds(&writefd); |
266 | | |
267 | | /* loop handler prior to channelio, in case the server loophandler closes |
268 | | channels on process exit */ |
269 | 0 | loophandler(); |
270 | | |
271 | | /* process pipes etc for the channels, ses.dataallowed == 0 |
272 | | * during rekeying ) */ |
273 | 0 | channelio(&readfd, &writefd); |
274 | | |
275 | | /* process session socket's outgoing data */ |
276 | 0 | if (ses.sock_out != -1) { |
277 | 0 | if (!isempty(&ses.writequeue)) { |
278 | 0 | write_packet(); |
279 | 0 | } |
280 | 0 | } |
281 | |
|
282 | 0 | } /* for(;;) */ |
283 | | |
284 | | /* Not reached */ |
285 | 0 | } |
286 | | |
287 | 0 | static void cleanup_buf(buffer **buf) { |
288 | 0 | if (!*buf) { |
289 | 0 | return; |
290 | 0 | } |
291 | 0 | buf_burn_free(*buf); |
292 | 0 | *buf = NULL; |
293 | 0 | } |
294 | | |
295 | | /* clean up a session on exit */ |
296 | 219 | void session_cleanup() { |
297 | | |
298 | 219 | TRACE(("enter session_cleanup")) |
299 | | |
300 | | /* we can't cleanup if we don't know the session state */ |
301 | 219 | if (!ses.init_done) { |
302 | 219 | TRACE(("leave session_cleanup: !ses.init_done")) |
303 | 219 | return; |
304 | 219 | } |
305 | | |
306 | | /* BEWARE of changing order of functions here. */ |
307 | | |
308 | | /* Must be before extra_session_cleanup() */ |
309 | 0 | chancleanup(); |
310 | |
|
311 | 0 | if (ses.extra_session_cleanup) { |
312 | 0 | ses.extra_session_cleanup(); |
313 | 0 | } |
314 | | |
315 | | /* After these are freed most functions will fail */ |
316 | 0 | #if DROPBEAR_CLEANUP |
317 | | /* listeners call cleanup functions, this should occur before |
318 | | other session state is freed. */ |
319 | 0 | remove_all_listeners(); |
320 | |
|
321 | 0 | remove_connect_pending(); |
322 | |
|
323 | 0 | while (!isempty(&ses.writequeue)) { |
324 | 0 | buf_free(dequeue(&ses.writequeue)); |
325 | 0 | } |
326 | |
|
327 | 0 | 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 | 0 | m_free(ses.remoteident); |
344 | 0 | m_free(ses.authstate.pw_dir); |
345 | 0 | m_free(ses.authstate.pw_name); |
346 | 0 | m_free(ses.authstate.pw_shell); |
347 | 0 | m_free(ses.authstate.pw_passwd); |
348 | 0 | m_free(ses.authstate.username); |
349 | 0 | #endif |
350 | |
|
351 | 0 | cleanup_buf(&ses.session_id); |
352 | 0 | cleanup_buf(&ses.hash); |
353 | 0 | cleanup_buf(&ses.payload); |
354 | 0 | cleanup_buf(&ses.readbuf); |
355 | 0 | cleanup_buf(&ses.writepayload); |
356 | 0 | cleanup_buf(&ses.kexhashbuf); |
357 | 0 | cleanup_buf(&ses.transkexinit); |
358 | 0 | if (ses.dh_K) { |
359 | 0 | mp_clear(ses.dh_K); |
360 | 0 | } |
361 | 0 | m_free(ses.dh_K); |
362 | 0 | if (ses.dh_K_bytes) { |
363 | 0 | buf_burn_free(ses.dh_K_bytes); |
364 | 0 | } |
365 | |
|
366 | 0 | m_burn(ses.keys, sizeof(struct key_context)); |
367 | 0 | m_free(ses.keys); |
368 | |
|
369 | 0 | TRACE(("leave session_cleanup")) |
370 | 0 | } |
371 | | |
372 | 0 | void send_session_identification() { |
373 | 0 | buffer *writebuf = buf_new(strlen(LOCAL_IDENT "\r\n") + 1); |
374 | 0 | buf_putbytes(writebuf, (const unsigned char *) LOCAL_IDENT "\r\n", strlen(LOCAL_IDENT "\r\n")); |
375 | 0 | writebuf_enqueue(writebuf); |
376 | 0 | } |
377 | | |
378 | 0 | static void read_session_identification() { |
379 | | /* max length of 255 chars */ |
380 | 0 | char linebuf[256]; |
381 | 0 | int len = 0; |
382 | 0 | char done = 0; |
383 | 0 | 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 | 0 | for (i = IS_DROPBEAR_CLIENT ? 50 : 1; i > 0; i--) { |
389 | 0 | len = ident_readln(ses.sock_in, linebuf, sizeof(linebuf)); |
390 | |
|
391 | 0 | if (len < 0 && errno != EINTR) { |
392 | | /* It failed */ |
393 | 0 | break; |
394 | 0 | } |
395 | | |
396 | 0 | if (len >= 4 && memcmp(linebuf, "SSH-", 4) == 0) { |
397 | | /* start of line matches */ |
398 | 0 | done = 1; |
399 | 0 | break; |
400 | 0 | } |
401 | 0 | } |
402 | |
|
403 | 0 | if (!done) { |
404 | 0 | TRACE(("error reading remote ident: %s\n", strerror(errno))) |
405 | 0 | ses.remoteclosed(); |
406 | 0 | } else { |
407 | | /* linebuf is already null terminated */ |
408 | 0 | ses.remoteident = m_malloc(len); |
409 | 0 | memcpy(ses.remoteident, linebuf, len); |
410 | 0 | } |
411 | | |
412 | | /* Shall assume that 2.x will be backwards compatible. */ |
413 | 0 | if (strncmp(ses.remoteident, "SSH-2.", 6) != 0 |
414 | 0 | && strncmp(ses.remoteident, "SSH-1.99-", 9) != 0) { |
415 | 0 | dropbear_exit("Incompatible remote version '%s'", ses.remoteident); |
416 | 0 | } |
417 | |
|
418 | 0 | DEBUG1(("remoteident: %s", ses.remoteident)) |
419 | |
|
420 | 0 | } |
421 | | |
422 | | /* returns the length including null-terminating zero on success, |
423 | | * or -1 on failure */ |
424 | 0 | static int ident_readln(int fd, char* buf, int count) { |
425 | | |
426 | 0 | char in; |
427 | 0 | int pos = 0; |
428 | 0 | int num = 0; |
429 | 0 | fd_set fds; |
430 | 0 | struct timeval timeout; |
431 | |
|
432 | 0 | TRACE(("enter ident_readln")) |
433 | |
|
434 | 0 | if (count < 1) { |
435 | 0 | return -1; |
436 | 0 | } |
437 | | |
438 | 0 | DROPBEAR_FD_ZERO(&fds); |
439 | | |
440 | | /* select since it's a non-blocking fd */ |
441 | | |
442 | | /* leave space to null-terminate */ |
443 | 0 | while (pos < count-1) { |
444 | |
|
445 | 0 | FD_SET(fd, &fds); |
446 | |
|
447 | 0 | timeout.tv_sec = 1; |
448 | 0 | timeout.tv_usec = 0; |
449 | 0 | if (select(fd+1, &fds, NULL, NULL, &timeout) < 0) { |
450 | 0 | if (errno == EINTR) { |
451 | 0 | continue; |
452 | 0 | } |
453 | 0 | TRACE(("leave ident_readln: select error")) |
454 | 0 | return -1; |
455 | 0 | } |
456 | | |
457 | 0 | 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 | 0 | if (FD_ISSET(fd, &fds)) { |
463 | 0 | 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 | 0 | if (num < 0) { |
467 | | /* error */ |
468 | 0 | if (errno == EINTR) { |
469 | 0 | continue; /* not a real error */ |
470 | 0 | } |
471 | 0 | TRACE(("leave ident_readln: read error")) |
472 | 0 | return -1; |
473 | 0 | } |
474 | 0 | if (num == 0) { |
475 | | /* EOF */ |
476 | 0 | TRACE(("leave ident_readln: EOF")) |
477 | 0 | return -1; |
478 | 0 | } |
479 | | |
480 | 0 | #if DROPBEAR_FUZZ |
481 | 0 | fuzz_dump(&in, 1); |
482 | 0 | #endif |
483 | |
|
484 | 0 | if (in == '\n') { |
485 | | /* end of ident string */ |
486 | 0 | break; |
487 | 0 | } |
488 | | /* we don't want to include '\r's */ |
489 | 0 | if (in != '\r') { |
490 | 0 | buf[pos] = in; |
491 | 0 | pos++; |
492 | 0 | } |
493 | 0 | } |
494 | 0 | } |
495 | | |
496 | 0 | buf[pos] = '\0'; |
497 | 0 | TRACE(("leave ident_readln: return %d", pos+1)) |
498 | 0 | return pos+1; |
499 | 0 | } |
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 | 0 | static void checktimeouts() { |
546 | |
|
547 | 0 | time_t now; |
548 | 0 | now = monotonic_now(); |
549 | |
|
550 | 0 | 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 | 0 | if (ses.remoteident == NULL) { |
557 | 0 | return; |
558 | 0 | } |
559 | | |
560 | 0 | 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 | 0 | 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 | 0 | 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 | 0 | } |
596 | | |
597 | 0 | static void update_timeout(long limit, time_t now, time_t last_event, long * timeout) { |
598 | 0 | TRACE2(("update_timeout limit %ld, now %llu, last %llu, timeout %ld", |
599 | 0 | limit, |
600 | 0 | (unsigned long long)now, |
601 | 0 | (unsigned long long)last_event, *timeout)) |
602 | 0 | 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 | 0 | } |
607 | | |
608 | 0 | static long select_timeout() { |
609 | | /* determine the minimum timeout that might be required, so |
610 | | as to avoid waking when unneccessary */ |
611 | 0 | long timeout = KEX_REKEY_TIMEOUT; |
612 | 0 | time_t now = monotonic_now(); |
613 | |
|
614 | 0 | if (!ses.kexstate.sentkexinit) { |
615 | 0 | update_timeout(KEX_REKEY_TIMEOUT, now, ses.kexstate.lastkextime, &timeout); |
616 | 0 | } |
617 | 0 | if (ses.kexstate.needrekey) { |
618 | 0 | timeout = 0; |
619 | 0 | } |
620 | |
|
621 | 0 | 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 | 0 | 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 | 0 | update_timeout(opts.idle_timeout_secs, now, ses.last_packet_time_idle, |
633 | 0 | &timeout); |
634 | | |
635 | | /* clamp negative timeouts to zero - event has already triggered */ |
636 | 0 | return MAX(timeout, 0); |
637 | 0 | } |
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 | 0 | void update_channel_prio() { |
692 | 0 | enum dropbear_prio new_prio; |
693 | 0 | int any = 0; |
694 | 0 | unsigned int i; |
695 | |
|
696 | 0 | TRACE(("update_channel_prio")) |
697 | |
|
698 | 0 | if (ses.sock_out < 0) { |
699 | 0 | TRACE(("leave update_channel_prio: no socket")) |
700 | 0 | return; |
701 | 0 | } |
702 | | |
703 | 0 | new_prio = DROPBEAR_PRIO_NORMAL; |
704 | 0 | 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 | 0 | if (any == 0) { |
717 | | /* lowdelay during setup */ |
718 | 0 | TRACE(("update_channel_prio: not any")) |
719 | 0 | new_prio = DROPBEAR_PRIO_LOWDELAY; |
720 | 0 | } |
721 | |
|
722 | 0 | if (new_prio != ses.socket_prio) { |
723 | 0 | TRACE(("Dropbear priority transitioning %d -> %d", ses.socket_prio, new_prio)) |
724 | 0 | set_sock_priority(ses.sock_out, new_prio); |
725 | 0 | ses.socket_prio = new_prio; |
726 | 0 | } |
727 | 0 | } |
728 | | |