Coverage Report

Created: 2026-08-13 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dropbear/src/dbutil.c
Line
Count
Source
1
/*
2
 * Dropbear - a SSH2 server
3
 * 
4
 * Copyright (c) 2002,2003 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
 * strlcat() is copyright as follows:
26
 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
27
 * All rights reserved.
28
 *
29
 * Redistribution and use in source and binary forms, with or without
30
 * modification, are permitted provided that the following conditions
31
 * are met:
32
 * 1. Redistributions of source code must retain the above copyright
33
 *    notice, this list of conditions and the following disclaimer.
34
 * 2. Redistributions in binary form must reproduce the above copyright
35
 *    notice, this list of conditions and the following disclaimer in the
36
 *    documentation and/or other materials provided with the distribution.
37
 * 3. The name of the author may not be used to endorse or promote products
38
 *    derived from this software without specific prior written permission.
39
 *
40
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
41
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
42
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
43
 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
44
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
45
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
46
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
47
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
48
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
49
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
50
51
#include "config.h"
52
53
#ifdef __linux__
54
#define _GNU_SOURCE
55
/* To call clock_gettime() directly */
56
#include <sys/syscall.h>
57
#endif /* __linux */
58
59
#ifdef HAVE_MACH_MACH_TIME_H
60
#include <mach/mach_time.h>
61
#include <mach/mach.h>
62
#endif
63
64
#include "includes.h"
65
#include "dbutil.h"
66
#include "buffer.h"
67
#include "session.h"
68
#include "atomicio.h"
69
70
#define MAX_FMT 100
71
72
static void generic_dropbear_exit(int exitcode, const char* format, 
73
    va_list param) ATTRIB_NORETURN;
74
static void generic_dropbear_log(int priority, const char* format, 
75
    va_list param);
76
77
void (*_dropbear_exit)(int exitcode, const char* format, va_list param) ATTRIB_NORETURN
78
            = generic_dropbear_exit;
79
void (*_dropbear_log)(int priority, const char* format, va_list param)
80
            = generic_dropbear_log;
81
82
#if DEBUG_TRACE
83
int debug_trace = 0;
84
#endif
85
86
#ifndef DISABLE_SYSLOG
87
0
void startsyslog(const char *ident) {
88
89
0
  openlog(ident, LOG_PID, LOG_AUTHPRIV);
90
91
0
}
92
#endif /* DISABLE_SYSLOG */
93
94
/* the "format" string must be <= 100 characters */
95
0
void dropbear_close(const char* format, ...) {
96
97
0
  va_list param;
98
99
0
  va_start(param, format);
100
0
  _dropbear_exit(EXIT_SUCCESS, format, param);
101
0
  va_end(param);
102
103
0
}
104
105
300
void dropbear_exit(const char* format, ...) {
106
107
300
  va_list param;
108
109
300
  va_start(param, format);
110
300
  _dropbear_exit(EXIT_FAILURE, format, param);
111
300
  va_end(param);
112
0
}
113
114
static void generic_dropbear_exit(int exitcode, const char* format, 
115
300
    va_list param) {
116
117
300
  char fmtbuf[300];
118
119
300
  snprintf(fmtbuf, sizeof(fmtbuf), "Exited: %s", format);
120
121
300
  _dropbear_log(LOG_INFO, fmtbuf, param);
122
123
300
#if DROPBEAR_FUZZ
124
300
    if (fuzz.do_jmp) {
125
300
        longjmp(fuzz.jmp, 1);
126
300
    }
127
0
#endif
128
129
0
  exit(exitcode);
130
300
}
131
132
0
void fail_assert(const char* expr, const char* file, int line) {
133
0
  dropbear_exit("Failed assertion (%s:%d): `%s'", file, line, expr);
134
0
}
135
136
static void generic_dropbear_log(int UNUSED(priority), const char* format, 
137
0
    va_list param) {
138
139
0
  char printbuf[1024];
140
141
0
  vsnprintf(printbuf, sizeof(printbuf), format, param);
142
143
0
  fprintf(stderr, "%s\n", printbuf);
144
145
0
}
146
147
/* this is what can be called to write arbitrary log messages */
148
1.58k
void dropbear_log(int priority, const char* format, ...) {
149
150
1.58k
  va_list param;
151
152
1.58k
  va_start(param, format);
153
1.58k
  _dropbear_log(priority, format, param);
154
1.58k
  va_end(param);
155
1.58k
}
156
157
158
#if DEBUG_TRACE 
159
160
static double debug_start_time = -1;
161
162
void debug_start_net()
163
{
164
  if (getenv("DROPBEAR_DEBUG_NET_TIMESTAMP"))
165
  {
166
    /* Timestamps start from first network activity */
167
    struct timeval tv;
168
    gettimeofday(&tv, NULL);
169
    debug_start_time = tv.tv_sec + (tv.tv_usec / 1000000.0);
170
    TRACE(("Resetting Dropbear TRACE timestamps"))
171
  }
172
}
173
174
static double time_since_start()
175
{
176
  double nowf;
177
  struct timeval tv;
178
  gettimeofday(&tv, NULL);
179
  nowf = tv.tv_sec + (tv.tv_usec / 1000000.0);
180
  if (debug_start_time < 0)
181
  {
182
    debug_start_time = nowf;
183
    return 0;
184
  }
185
  return nowf - debug_start_time;
186
}
187
188
static void dropbear_tracelevel(int level, const char *format, va_list param)
189
{
190
  if (debug_trace == 0 || debug_trace < level) {
191
    return;
192
  }
193
194
  fprintf(stderr, "TRACE%d (%d) %f: ", level, getpid(), time_since_start());
195
  vfprintf(stderr, format, param);
196
  fprintf(stderr, "\n");
197
}
198
#if (DEBUG_TRACE>=1)
199
void dropbear_trace1(const char* format, ...) {
200
  va_list param;
201
202
  va_start(param, format);
203
  dropbear_tracelevel(1, format, param);
204
  va_end(param);
205
}
206
#endif
207
#if (DEBUG_TRACE>=2)
208
void dropbear_trace2(const char* format, ...) {
209
  va_list param;
210
211
  va_start(param, format);
212
  dropbear_tracelevel(2, format, param);
213
  va_end(param);
214
}
215
#endif
216
#if (DEBUG_TRACE>=3)
217
void dropbear_trace3(const char* format, ...) {
218
  va_list param;
219
220
  va_start(param, format);
221
  dropbear_tracelevel(3, format, param);
222
  va_end(param);
223
}
224
#endif
225
#if (DEBUG_TRACE>=4)
226
void dropbear_trace4(const char* format, ...) {
227
  va_list param;
228
229
  va_start(param, format);
230
  dropbear_tracelevel(4, format, param);
231
  va_end(param);
232
}
233
#endif
234
#if (DEBUG_TRACE>=5)
235
void dropbear_trace5(const char* format, ...) {
236
  va_list param;
237
238
  va_start(param, format);
239
  dropbear_tracelevel(5, format, param);
240
  va_end(param);
241
}
242
#endif
243
#endif
244
245
246
/* Connect to a given unix socket. The socket is blocking */
247
#if ENABLE_CONNECT_UNIX
248
0
int connect_unix(const char* path) {
249
0
  struct sockaddr_un addr;
250
0
  int fd = -1;
251
252
0
  memset((void*)&addr, 0x0, sizeof(addr));
253
0
  addr.sun_family = AF_UNIX;
254
0
  strlcpy(addr.sun_path, path, sizeof(addr.sun_path));
255
0
  fd = socket(PF_UNIX, SOCK_STREAM, 0);
256
0
  if (fd < 0) {
257
0
    TRACE(("Failed to open unix socket"))
258
0
    return -1;
259
0
  }
260
0
  if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
261
0
    TRACE(("Failed to connect to '%s' socket", path))
262
0
    m_close(fd);
263
0
    return -1;
264
0
  }
265
0
  return fd;
266
0
}
267
#endif
268
269
/* Sets up a pipe for a, returning three non-blocking file descriptors
270
 * and the pid. exec_fn is the function that will actually execute the child process,
271
 * it will be run after the child has fork()ed, and is passed exec_data.
272
 * If ret_errfd == NULL then stderr will not be captured.
273
 * ret_pid can be passed as  NULL to discard the pid. */
274
int spawn_command(void(*exec_fn)(const void *user_data), const void *exec_data,
275
0
    int *ret_writefd, int *ret_readfd, int *ret_errfd, pid_t *ret_pid) {
276
0
  int infds[2];
277
0
  int outfds[2];
278
0
  int errfds[2];
279
0
  pid_t pid;
280
281
0
  const int FDIN = 0;
282
0
  const int FDOUT = 1;
283
284
0
#if DROPBEAR_FUZZ
285
0
  if (fuzz.fuzzing) {
286
0
    return fuzz_spawn_command(ret_writefd, ret_readfd, ret_errfd, ret_pid);
287
0
  }
288
0
#endif
289
290
  /* redirect stdin/stdout/stderr */
291
0
  if (pipe(infds) != 0) {
292
0
    return DROPBEAR_FAILURE;
293
0
  }
294
0
  if (pipe(outfds) != 0) {
295
0
    return DROPBEAR_FAILURE;
296
0
  }
297
0
  if (ret_errfd && pipe(errfds) != 0) {
298
0
    return DROPBEAR_FAILURE;
299
0
  }
300
301
#if DROPBEAR_VFORK
302
  pid = vfork();
303
#else
304
0
  pid = fork();
305
0
#endif
306
307
0
  if (pid < 0) {
308
0
    return DROPBEAR_FAILURE;
309
0
  }
310
311
0
  if (!pid) {
312
    /* child */
313
314
0
    TRACE(("back to normal sigchld"))
315
    /* Revert to normal sigchld handling */
316
0
    if (signal(SIGCHLD, SIG_DFL) == SIG_ERR) {
317
0
      dropbear_exit("signal() error");
318
0
    }
319
320
    /* redirect stdin/stdout */
321
322
0
    if ((dup2(infds[FDIN], STDIN_FILENO) < 0) ||
323
0
      (dup2(outfds[FDOUT], STDOUT_FILENO) < 0) ||
324
0
      (ret_errfd && dup2(errfds[FDOUT], STDERR_FILENO) < 0)) {
325
0
      TRACE(("leave noptycommand: error redirecting FDs"))
326
0
      dropbear_exit("Child dup2() failure");
327
0
    }
328
329
0
    close(infds[FDOUT]);
330
0
    close(infds[FDIN]);
331
0
    close(outfds[FDIN]);
332
0
    close(outfds[FDOUT]);
333
0
    if (ret_errfd)
334
0
    {
335
0
      close(errfds[FDIN]);
336
0
      close(errfds[FDOUT]);
337
0
    }
338
339
0
    exec_fn(exec_data);
340
    /* not reached */
341
0
    return DROPBEAR_FAILURE;
342
0
  } else {
343
    /* parent */
344
0
    close(infds[FDIN]);
345
0
    close(outfds[FDOUT]);
346
347
0
    setnonblocking(outfds[FDIN]);
348
0
    setnonblocking(infds[FDOUT]);
349
350
0
    if (ret_errfd) {
351
0
      close(errfds[FDOUT]);
352
0
      setnonblocking(errfds[FDIN]);
353
0
    }
354
355
0
    if (ret_pid) {
356
0
      *ret_pid = pid;
357
0
    }
358
359
0
    *ret_writefd = infds[FDOUT];
360
0
    *ret_readfd = outfds[FDIN];
361
0
    if (ret_errfd) {
362
0
      *ret_errfd = errfds[FDIN];
363
0
    }
364
0
    return DROPBEAR_SUCCESS;
365
0
  }
366
0
}
367
368
/* Runs a command with "sh -c". Will close FDs (except stdin/stdout/stderr) and
369
 * re-enabled SIGPIPE. If cmd is NULL, will run a login shell.
370
 */
371
0
void run_shell_command(const char* cmd, unsigned int maxfd, char* usershell) {
372
0
  char * argv[4];
373
0
  char * baseshell = NULL;
374
375
0
  baseshell = basename(usershell);
376
377
0
  if (cmd != NULL) {
378
0
    argv[0] = baseshell;
379
0
  } else {
380
    /* a login shell should be "-bash" for "/bin/bash" etc */
381
0
    int len = strlen(baseshell) + 2; /* 2 for "-" */
382
0
    argv[0] = (char*)m_malloc(len);
383
0
    snprintf(argv[0], len, "-%s", baseshell);
384
0
  }
385
386
0
  if (cmd != NULL) {
387
0
    argv[1] = "-c";
388
0
    argv[2] = (char*)cmd;
389
0
    argv[3] = NULL;
390
0
  } else {
391
    /* construct a shell of the form "-bash" etc */
392
0
    argv[1] = NULL;
393
0
  }
394
395
0
  run_command(usershell, argv, maxfd);
396
0
}
397
398
0
void run_command(const char* argv0, char** args, unsigned int maxfd) {
399
0
  unsigned int i;
400
401
  /* Re-enable SIGPIPE for the executed process */
402
0
  if (signal(SIGPIPE, SIG_DFL) == SIG_ERR) {
403
0
    dropbear_exit("signal() error");
404
0
  }
405
406
  /* close file descriptors except stdin/stdout/stderr
407
   * Need to be sure FDs are closed here to avoid reading files as root */
408
0
  for (i = 3; i <= maxfd; i++) {
409
0
    m_close(i);
410
0
  }
411
412
0
  execv(argv0, args);
413
0
}
414
415
#if DEBUG_TRACE
416
void printhex(const char * label, const unsigned char * buf, int len) {
417
  int i, j;
418
419
  fprintf(stderr, "%s\n", label);
420
  /* for each 16 byte line */
421
  for (j = 0; j < len; j += 16) {
422
    const int linelen = MIN(16, len - j);
423
424
    /* print hex digits */
425
    for (i = 0; i < 16; i++) {
426
      if (i < linelen) {
427
        fprintf(stderr, "%02x", buf[j+i]);
428
      } else {
429
        fprintf(stderr, "  ");
430
      }
431
      /* separator between pairs */
432
      if (i % 2 ==1) {
433
        fprintf(stderr, " ");
434
      }
435
    }
436
437
    /* print characters */
438
    fprintf(stderr, "  ");
439
    for (i = 0; i < linelen; i++) {
440
      char c = buf[j+i];
441
      if (!ascii_isprint(c)) {
442
        c = '.';
443
      }
444
      fputc(c, stderr);
445
    }
446
    fprintf(stderr, "\n");
447
  }
448
}
449
450
void printmpint(const char *label, const mp_int *mp) {
451
  buffer *buf = buf_new(1000);
452
  buf_putmpint(buf, mp);
453
  fprintf(stderr, "%d bits ", mp_count_bits(mp));
454
  printhex(label, buf->data, buf->len);
455
  buf_free(buf);
456
457
}
458
#endif
459
460
/* Strip all control characters from text (a null-terminated string), except
461
 * for '\n', '\r' and '\t'.
462
 * The result returned is a newly allocated string, this must be free()d after
463
 * use */
464
0
char * stripcontrol(const char * text) {
465
466
0
  char * ret;
467
0
  int len, pos;
468
0
  int i;
469
  
470
0
  len = strlen(text);
471
0
  ret = m_malloc(len+1);
472
473
0
  pos = 0;
474
0
  for (i = 0; i < len; i++) {
475
0
    if ((text[i] <= '~' && text[i] >= ' ') /* normal printable range */
476
0
        || text[i] == '\n' || text[i] == '\r' || text[i] == '\t') {
477
0
      ret[pos] = text[i];
478
0
      pos++;
479
0
    }
480
0
  }
481
0
  ret[pos] = 0x0;
482
0
  return ret;
483
0
}
484
      
485
486
/* reads the contents of filename into the buffer buf, from the current
487
 * position, either to the end of the file, or the buffer being full.
488
 * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
489
1.58k
int buf_readfile(buffer* buf, const char* filename) {
490
491
1.58k
  int fd = -1;
492
1.58k
  int len;
493
1.58k
  int maxlen;
494
1.58k
  int ret = DROPBEAR_FAILURE;
495
496
1.58k
  fd = open(filename, O_RDONLY);
497
498
1.58k
  if (fd < 0) {
499
773
    goto out;
500
773
  }
501
  
502
809
  do {
503
809
    maxlen = buf->size - buf->pos;
504
809
    len = read(fd, buf_getwriteptr(buf, maxlen), maxlen);
505
809
    if (len < 0) {
506
809
      if (errno == EINTR || errno == EAGAIN) {
507
0
        continue;
508
0
      }
509
809
      goto out;
510
809
    }
511
0
    buf_incrwritepos(buf, len);
512
0
  } while (len < maxlen && len > 0);
513
514
0
  ret = DROPBEAR_SUCCESS;
515
516
1.58k
out:
517
1.58k
  if (fd >= 0) {
518
809
    m_close(fd);
519
809
  }
520
1.58k
  return ret;
521
0
}
522
523
/* Get a line from the file into buffer in the style expected for an
524
 * authkeys file.
525
 * Will return DROPBEAR_SUCCESS if data is read, or DROPBEAR_FAILURE on EOF/error.*/
526
/* Returns failure if lines are longer than DROPBEAR_MAX_LINE_LENGTH.
527
 * A line that doesn't fit in the buffer returns DROPBEAR_SUCCESS with an empty buffer */
528
5.72k
int buf_getline(buffer * line, FILE * authfile) {
529
530
5.72k
  int c = EOF;
531
5.72k
  unsigned int pos;
532
5.72k
  int truncated = 0;
533
534
5.72k
  buf_setlen(line, 0);
535
536
153k
  for (pos = 0; pos < DROPBEAR_MAX_LINE_LENGTH; pos++) {
537
153k
    c = fgetc(authfile); /*getc() is weird with some uClibc systems*/
538
153k
    if (c == EOF || c == '\n' || c == '\r') {
539
5.72k
      break;
540
5.72k
    }
541
542
148k
    if (!truncated && line->len < line->size) {
543
68.4k
      buf_putbyte(line, (unsigned char)c);
544
79.8k
    } else {
545
      /* buffer is full, read to EOL and return an empty buffer */
546
79.8k
      truncated = 1;
547
79.8k
      buf_setlen(line, 0);
548
79.8k
    }
549
148k
  }
550
551
5.72k
  if (pos >= DROPBEAR_MAX_LINE_LENGTH) {
552
    /* Didn't reach end of line, the caller shouldn't continue since
553
     * it will still be mid-line */
554
0
    return DROPBEAR_FAILURE;
555
0
  }
556
557
5.72k
  if (c == EOF && line->pos == 0) {
558
    /* Reached EOF, no buffer data */
559
181
    return DROPBEAR_FAILURE;
560
181
  }
561
562
5.54k
  buf_setpos(line, 0);
563
5.54k
  return DROPBEAR_SUCCESS;
564
5.72k
}  
565
566
/* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
567
0
int buf_writefile(buffer * buf, const char * filename, int skip_exist) {
568
0
  int ret = DROPBEAR_FAILURE;
569
0
  int fd = -1;
570
571
0
  fd = open(filename, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW,
572
0
    S_IRUSR | S_IWUSR);
573
0
  if (fd < 0) {
574
    /* If generating keys on connection (skip_exist) it's OK to get EEXIST
575
    - we probably just lost a race with another connection to generate the key */
576
0
    if (skip_exist && errno == EEXIST) {
577
0
      ret = DROPBEAR_SUCCESS;
578
0
    } else {
579
0
      dropbear_log(LOG_ERR, "Couldn't create new file %s: %s",
580
0
        filename, strerror(errno));
581
0
    }
582
583
0
    goto out;
584
0
  }
585
586
  /* write the file now */
587
0
  while (buf->pos != buf->len) {
588
0
    int len = write(fd, buf_getptr(buf, buf->len - buf->pos),
589
0
        buf->len - buf->pos);
590
0
    if (len == -1 && errno == EINTR) {
591
0
      continue;
592
0
    }
593
0
    if (len <= 0) {
594
0
      dropbear_log(LOG_ERR, "Failed writing file %s: %s",
595
0
        filename, strerror(errno));
596
0
      goto out;
597
0
    }
598
0
    buf_incrpos(buf, len);
599
0
  }
600
601
0
  ret = DROPBEAR_SUCCESS;
602
603
0
out:
604
0
  if (fd >= 0) {
605
0
    if (fsync(fd) != 0) {
606
0
      dropbear_log(LOG_ERR, "fsync of %s failed: %s", filename, strerror(errno));
607
0
    }
608
0
    m_close(fd);
609
0
  }
610
0
  return ret;
611
0
}
612
613
614
/* make sure that the socket closes */
615
809
void m_close(int fd) {
616
809
  int val;
617
618
809
  if (fd < 0) {
619
0
    return;
620
0
  }
621
622
809
  do {
623
809
    val = close(fd);
624
809
  } while (val < 0 && errno == EINTR);
625
626
809
  if (val < 0 && errno != EBADF) {
627
    /* Linux says EIO can happen */
628
0
    dropbear_exit("Error closing fd %d, %s", fd, strerror(errno));
629
0
  }
630
809
}
631
632
0
void setnonblocking(int fd) {
633
634
0
  int fl = 0;
635
0
  TRACE(("setnonblocking: %d", fd))
636
637
0
#if DROPBEAR_FUZZ
638
0
  if (fuzz.fuzzing) {
639
0
    return;
640
0
  }
641
0
#endif
642
0
  fl = fcntl(fd, F_GETFL, 0);
643
0
  if (fl == -1) {
644
    /* F_GETFL shouldn't fail */
645
0
    dropbear_exit("Couldn't set nonblocking");
646
0
  }
647
648
0
  if (fcntl(fd, F_SETFL, fl | O_NONBLOCK) == -1) {
649
0
    if (errno == ENODEV) {
650
      /* Some devices (like /dev/null redirected in)
651
       * can't be set to non-blocking */
652
0
      TRACE(("ignoring ENODEV for setnonblocking"))
653
0
    } else {
654
0
      dropbear_exit("Couldn't set nonblocking");
655
0
    }
656
0
  }
657
0
  TRACE(("leave setnonblocking"))
658
0
}
659
660
1
void disallow_core() {
661
1
  struct rlimit lim = {0};
662
1
  if (getrlimit(RLIMIT_CORE, &lim) < 0) {
663
0
    TRACE(("getrlimit(RLIMIT_CORE) failed"));
664
0
  }
665
1
  lim.rlim_cur = 0;
666
1
  if (setrlimit(RLIMIT_CORE, &lim) < 0) {
667
0
    TRACE(("setrlimit(RLIMIT_CORE) failed"));
668
0
  }
669
1
}
670
671
/* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE, with the result in *val */
672
0
int m_str_to_uint(const char* str, unsigned int *val) {
673
0
  unsigned long l;
674
0
  char *endp;
675
676
0
  l = strtoul(str, &endp, 10);
677
678
0
  if (endp == str || *endp != '\0') {
679
    /* parse error */
680
0
    return DROPBEAR_FAILURE;
681
0
  }
682
683
  /* The c99 spec doesn't actually seem to define EINVAL, but most platforms
684
   * I've looked at mention it in their manpage */
685
0
  if ((l == 0 && errno == EINVAL)
686
0
    || (l == ULONG_MAX && errno == ERANGE)
687
0
    || (l > UINT_MAX)) {
688
0
    return DROPBEAR_FAILURE;
689
0
  } else {
690
0
    *val = l;
691
0
    return DROPBEAR_SUCCESS;
692
0
  }
693
0
}
694
695
/* Returns malloced path from inpath, possibly expanding '~/'
696
   into the specified home directory.*/
697
1.85k
char * expand_homedir_path_home(const char *inpath, const char *homedir) {
698
1.85k
  if (strncmp(inpath, "~/", 2) == 0 && homedir) {
699
274
    size_t len = strlen(inpath)-2 + strlen(homedir) + 2;
700
274
    char *buf = m_malloc(len);
701
274
    snprintf(buf, len, "%s/%s", homedir, inpath+2);
702
274
    return buf;
703
274
  }
704
  /* Fallback */
705
1.58k
  return m_strdup(inpath);
706
1.85k
}
707
708
/* Returns malloced path from inpath, possibly expanding '~/'
709
   into the current user's home directory.*/
710
1.85k
char * expand_homedir_path(const char *inpath) {
711
1.85k
  struct passwd *pw = NULL;
712
1.85k
  char *homedir = getenv("HOME");
713
714
1.85k
  if (!homedir) {
715
0
    pw = getpwuid(getuid());
716
0
    if (pw) {
717
0
      homedir = pw->pw_dir;
718
0
    }
719
0
  }
720
1.85k
  return expand_homedir_path_home(inpath, homedir);
721
1.85k
}
722
723
int constant_time_memcmp(const void* a, const void *b, size_t n)
724
0
{
725
0
  const char *xa = a, *xb = b;
726
0
  uint8_t c = 0;
727
0
  size_t i;
728
0
  for (i = 0; i < n; i++)
729
0
  {
730
0
    c |= (xa[i] ^ xb[i]);
731
0
  }
732
0
  return c;
733
0
}
734
735
/* higher-resolution monotonic timestamp, falls back to gettimeofday */
736
0
void gettime_wrapper(struct timespec *now) {
737
0
  struct timeval tv;
738
0
#if DROPBEAR_FUZZ
739
0
  if (fuzz.fuzzing) {
740
    /* time stands still when fuzzing */
741
0
    now->tv_sec = 5;
742
0
    now->tv_nsec = 0;
743
0
  }
744
0
#endif
745
746
0
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
747
  /* POSIX monotonic clock. Newer Linux, BSD, MacOSX >10.12 */
748
0
  if (clock_gettime(CLOCK_MONOTONIC, now) == 0) {
749
0
    return;
750
0
  }
751
0
#endif
752
753
0
#if defined(__linux__) && defined(SYS_clock_gettime)
754
0
  {
755
  /* Old linux toolchain - kernel might support it but not the build headers */
756
  /* Also glibc <2.17 requires -lrt which we neglect to add */
757
0
  static int linux_monotonic_failed = 0;
758
0
  if (!linux_monotonic_failed) {
759
    /* CLOCK_MONOTONIC isn't in some headers */
760
0
    int clock_source_monotonic = 1; 
761
0
    if (syscall(SYS_clock_gettime, clock_source_monotonic, now) == 0) {
762
0
      return;
763
0
    } else {
764
      /* Don't try again */
765
0
      linux_monotonic_failed = 1;
766
0
    }
767
0
  }
768
0
  }
769
0
#endif /* linux fallback clock_gettime */
770
771
#if defined(HAVE_MACH_ABSOLUTE_TIME)
772
  {
773
  /* OS X pre 10.12, see https://developer.apple.com/library/mac/qa/qa1398/_index.html */
774
  static mach_timebase_info_data_t timebase_info;
775
  uint64_t scaled_time;
776
  if (timebase_info.denom == 0) {
777
    mach_timebase_info(&timebase_info);
778
  }
779
  scaled_time = mach_absolute_time() * timebase_info.numer / timebase_info.denom;
780
  now->tv_sec = scaled_time / 1000000000;
781
  now->tv_nsec = scaled_time % 1000000000;
782
  }
783
#endif /* osx mach_absolute_time */
784
785
  /* Fallback for everything else - this will sometimes go backwards */
786
0
  gettimeofday(&tv, NULL);
787
0
  now->tv_sec = tv.tv_sec;
788
0
  now->tv_nsec = 1000*(long)tv.tv_usec;
789
0
}
790
791
/* second-resolution monotonic timestamp */
792
0
time_t monotonic_now() {
793
0
  struct timespec ts;
794
0
  gettime_wrapper(&ts);
795
0
  return ts.tv_sec;
796
0
}
797
798
0
void fsync_parent_dir(const char* fn) {
799
0
#ifdef HAVE_LIBGEN_H
800
0
  char *fn_dir = m_strdup(fn);
801
0
  char *dir = dirname(fn_dir);
802
0
  int dirfd = open(dir, O_RDONLY);
803
804
0
  if (dirfd != -1) {
805
0
    if (fsync(dirfd) != 0) {
806
0
      TRACE(("fsync of directory %s failed: %s", dir, strerror(errno)))
807
0
    }
808
0
    m_close(dirfd);
809
0
  } else {
810
0
    TRACE(("error opening directory %s for fsync: %s", dir, strerror(errno)))
811
0
  }
812
813
0
  m_free(fn_dir);
814
0
#endif
815
0
}
816
817
0
int fd_read_pending(int fd) {
818
0
  fd_set fds;
819
0
  struct timeval timeout;
820
821
0
  DROPBEAR_FD_ZERO(&fds);
822
0
  dropbear_fd_set(fd, &fds);
823
0
  while (1) {
824
0
    timeout.tv_sec = 0;
825
0
    timeout.tv_usec = 0;
826
0
    if (select(fd+1, &fds, NULL, NULL, &timeout) < 0) {
827
0
      if (errno == EINTR) {
828
0
        continue;
829
0
      }
830
0
      return 0;
831
0
    }
832
0
    return FD_ISSET(fd, &fds);
833
0
  }
834
0
}
835
836
/* FD_SET() wrapper with an overflow check */
837
0
void dropbear_fd_set(int fd, fd_set *set) {
838
0
  if (fd < 0) {
839
0
    return;
840
0
  }
841
0
  if (fd >= FD_SETSIZE) {
842
0
    dropbear_exit("fd limit %d", fd);
843
0
  }
844
0
  FD_SET(fd, set);
845
0
}
846
847
0
int m_snprintf(char *str, size_t size, const char *format, ...) {
848
0
  va_list param;
849
0
  int ret;
850
851
0
  va_start(param, format);
852
0
  ret = vsnprintf(str, size, format, param);
853
0
  va_end(param);
854
0
  if (ret < 0) {
855
0
    dropbear_exit("snprintf failed");
856
0
  }
857
0
  return ret;
858
0
}
859
860
/* Operates in-place turning dirty text (untrusted potentially containing control
861
 * characters) into clean text.
862
 * Only ascii (7 bit) characters are allowed.
863
 * Set allow_whitespace to allow \n and \t. */
864
0
void cleantext(char* dirtytext, int allow_whitespace) {
865
866
0
  unsigned int i, j;
867
0
  unsigned char c;
868
869
0
  j = 0;
870
0
  for (i = 0; dirtytext[i] != '\0'; i++) {
871
872
0
    c = (unsigned char)dirtytext[i];
873
    /* We can ignore '\r's */
874
0
    if ((c >= ' ' && c <= '~') || (allow_whitespace && (c == '\n' || c == '\t'))) {
875
0
      dirtytext[j] = c;
876
0
      j++;
877
0
    }
878
0
  }
879
  /* Null terminate */
880
0
  dirtytext[j] = '\0';
881
0
}