Coverage Report

Created: 2025-11-10 06:56

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.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