Coverage Report

Created: 2026-08-13 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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