Coverage Report

Created: 2023-09-25 06:08

/src/dropbear/src/dbutil.c
Line
Count
Source (jump to first uncovered line)
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
150
void dropbear_exit(const char* format, ...) {
106
107
150
  va_list param;
108
109
150
  va_start(param, format);
110
150
  _dropbear_exit(EXIT_FAILURE, format, param);
111
0
  va_end(param);
112
0
}
113
114
static void generic_dropbear_exit(int exitcode, const char* format, 
115
0
    va_list param) {
116
117
0
  char fmtbuf[300];
118
119
0
  snprintf(fmtbuf, sizeof(fmtbuf), "Exited: %s", format);
120
121
0
  _dropbear_log(LOG_INFO, fmtbuf, param);
122
123
0
#if DROPBEAR_FUZZ
124
0
    if (fuzz.do_jmp) {
125
0
        longjmp(fuzz.jmp, 1);
126
0
    }
127
0
#endif
128
129
0
  exit(exitcode);
130
0
}
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
150
void dropbear_log(int priority, const char* format, ...) {
149
150
150
  va_list param;
151
152
150
  va_start(param, format);
153
150
  _dropbear_log(priority, format, param);
154
150
  va_end(param);
155
150
}
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
0
  unsigned int i;
375
376
0
  baseshell = basename(usershell);
377
378
0
  if (cmd != NULL) {
379
0
    argv[0] = baseshell;
380
0
  } else {
381
    /* a login shell should be "-bash" for "/bin/bash" etc */
382
0
    int len = strlen(baseshell) + 2; /* 2 for "-" */
383
0
    argv[0] = (char*)m_malloc(len);
384
0
    snprintf(argv[0], len, "-%s", baseshell);
385
0
  }
386
387
0
  if (cmd != NULL) {
388
0
    argv[1] = "-c";
389
0
    argv[2] = (char*)cmd;
390
0
    argv[3] = NULL;
391
0
  } else {
392
    /* construct a shell of the form "-bash" etc */
393
0
    argv[1] = NULL;
394
0
  }
395
396
  /* Re-enable SIGPIPE for the executed process */
397
0
  if (signal(SIGPIPE, SIG_DFL) == SIG_ERR) {
398
0
    dropbear_exit("signal() error");
399
0
  }
400
401
  /* close file descriptors except stdin/stdout/stderr
402
   * Need to be sure FDs are closed here to avoid reading files as root */
403
0
  for (i = 3; i <= maxfd; i++) {
404
0
    m_close(i);
405
0
  }
406
407
0
  execv(usershell, argv);
408
0
}
409
410
#if DEBUG_TRACE
411
void printhex(const char * label, const unsigned char * buf, int len) {
412
  int i, j;
413
414
  fprintf(stderr, "%s\n", label);
415
  /* for each 16 byte line */
416
  for (j = 0; j < len; j += 16) {
417
    const int linelen = MIN(16, len - j);
418
419
    /* print hex digits */
420
    for (i = 0; i < 16; i++) {
421
      if (i < linelen) {
422
        fprintf(stderr, "%02x", buf[j+i]);
423
      } else {
424
        fprintf(stderr, "  ");
425
      }
426
      // separator between pairs
427
      if (i % 2 ==1) {
428
        fprintf(stderr, " ");
429
      }
430
    }
431
432
    /* print characters */
433
    fprintf(stderr, "  ");
434
    for (i = 0; i < linelen; i++) {
435
      char c = buf[j+i];
436
      if (!isprint(c)) {
437
        c = '.';
438
      }
439
      fputc(c, stderr);
440
    }
441
    fprintf(stderr, "\n");
442
  }
443
}
444
445
void printmpint(const char *label, const mp_int *mp) {
446
  buffer *buf = buf_new(1000);
447
  buf_putmpint(buf, mp);
448
  fprintf(stderr, "%d bits ", mp_count_bits(mp));
449
  printhex(label, buf->data, buf->len);
450
  buf_free(buf);
451
452
}
453
#endif
454
455
/* Strip all control characters from text (a null-terminated string), except
456
 * for '\n', '\r' and '\t'.
457
 * The result returned is a newly allocated string, this must be free()d after
458
 * use */
459
0
char * stripcontrol(const char * text) {
460
461
0
  char * ret;
462
0
  int len, pos;
463
0
  int i;
464
  
465
0
  len = strlen(text);
466
0
  ret = m_malloc(len+1);
467
468
0
  pos = 0;
469
0
  for (i = 0; i < len; i++) {
470
0
    if ((text[i] <= '~' && text[i] >= ' ') /* normal printable range */
471
0
        || text[i] == '\n' || text[i] == '\r' || text[i] == '\t') {
472
0
      ret[pos] = text[i];
473
0
      pos++;
474
0
    }
475
0
  }
476
0
  ret[pos] = 0x0;
477
0
  return ret;
478
0
}
479
      
480
481
/* reads the contents of filename into the buffer buf, from the current
482
 * position, either to the end of the file, or the buffer being full.
483
 * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
484
0
int buf_readfile(buffer* buf, const char* filename) {
485
486
0
  int fd = -1;
487
0
  int len;
488
0
  int maxlen;
489
0
  int ret = DROPBEAR_FAILURE;
490
491
0
  fd = open(filename, O_RDONLY);
492
493
0
  if (fd < 0) {
494
0
    goto out;
495
0
  }
496
  
497
0
  do {
498
0
    maxlen = buf->size - buf->pos;
499
0
    len = read(fd, buf_getwriteptr(buf, maxlen), maxlen);
500
0
    if (len < 0) {
501
0
      if (errno == EINTR || errno == EAGAIN) {
502
0
        continue;
503
0
      }
504
0
      goto out;
505
0
    }
506
0
    buf_incrwritepos(buf, len);
507
0
  } while (len < maxlen && len > 0);
508
509
0
  ret = DROPBEAR_SUCCESS;
510
511
0
out:
512
0
  if (fd >= 0) {
513
0
    m_close(fd);
514
0
  }
515
0
  return ret;
516
0
}
517
518
/* get a line from the file into buffer in the style expected for an
519
 * authkeys file.
520
 * Will return DROPBEAR_SUCCESS if data is read, or DROPBEAR_FAILURE on EOF.*/
521
/* Only used for ~/.ssh/known_hosts and ~/.ssh/authorized_keys */
522
#if DROPBEAR_CLIENT || DROPBEAR_SVR_PUBKEY_AUTH
523
0
int buf_getline(buffer * line, FILE * authfile) {
524
525
0
  int c = EOF;
526
527
0
  buf_setpos(line, 0);
528
0
  buf_setlen(line, 0);
529
530
0
  while (line->pos < line->size) {
531
532
0
    c = fgetc(authfile); /*getc() is weird with some uClibc systems*/
533
0
    if (c == EOF || c == '\n' || c == '\r') {
534
0
      goto out;
535
0
    }
536
537
0
    buf_putbyte(line, (unsigned char)c);
538
0
  }
539
540
0
  TRACE(("leave getauthline: line too long"))
541
  /* We return success, but the line length will be zeroed - ie we just
542
   * ignore that line */
543
0
  buf_setlen(line, 0);
544
545
0
out:
546
547
548
  /* if we didn't read anything before EOF or error, exit */
549
0
  if (c == EOF && line->pos == 0) {
550
0
    return DROPBEAR_FAILURE;
551
0
  } else {
552
0
    buf_setpos(line, 0);
553
0
    return DROPBEAR_SUCCESS;
554
0
  }
555
556
0
}  
557
#endif
558
559
/* make sure that the socket closes */
560
0
void m_close(int fd) {
561
0
  int val;
562
563
0
  if (fd < 0) {
564
0
    return;
565
0
  }
566
567
0
  do {
568
0
    val = close(fd);
569
0
  } while (val < 0 && errno == EINTR);
570
571
0
  if (val < 0 && errno != EBADF) {
572
    /* Linux says EIO can happen */
573
0
    dropbear_exit("Error closing fd %d, %s", fd, strerror(errno));
574
0
  }
575
0
}
576
  
577
0
void setnonblocking(int fd) {
578
579
0
  TRACE(("setnonblocking: %d", fd))
580
581
0
#if DROPBEAR_FUZZ
582
0
  if (fuzz.fuzzing) {
583
0
    return;
584
0
  }
585
0
#endif
586
587
0
  if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
588
0
    if (errno == ENODEV) {
589
      /* Some devices (like /dev/null redirected in)
590
       * can't be set to non-blocking */
591
0
      TRACE(("ignoring ENODEV for setnonblocking"))
592
0
    } else {
593
0
    {
594
0
      dropbear_exit("Couldn't set nonblocking");
595
0
    }
596
0
    }
597
0
  }
598
0
  TRACE(("leave setnonblocking"))
599
0
}
600
601
4
void disallow_core() {
602
4
  struct rlimit lim = {0};
603
4
  if (getrlimit(RLIMIT_CORE, &lim) < 0) {
604
0
    TRACE(("getrlimit(RLIMIT_CORE) failed"));
605
0
  }
606
4
  lim.rlim_cur = 0;
607
4
  if (setrlimit(RLIMIT_CORE, &lim) < 0) {
608
0
    TRACE(("setrlimit(RLIMIT_CORE) failed"));
609
0
  }
610
4
}
611
612
/* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE, with the result in *val */
613
0
int m_str_to_uint(const char* str, unsigned int *val) {
614
0
  unsigned long l;
615
0
  char *endp;
616
617
0
  l = strtoul(str, &endp, 10);
618
619
0
  if (endp == str || *endp != '\0') {
620
    /* parse error */
621
0
    return DROPBEAR_FAILURE;
622
0
  }
623
624
  /* The c99 spec doesn't actually seem to define EINVAL, but most platforms
625
   * I've looked at mention it in their manpage */
626
0
  if ((l == 0 && errno == EINVAL)
627
0
    || (l == ULONG_MAX && errno == ERANGE)
628
0
    || (l > UINT_MAX)) {
629
0
    return DROPBEAR_FAILURE;
630
0
  } else {
631
0
    *val = l;
632
0
    return DROPBEAR_SUCCESS;
633
0
  }
634
0
}
635
636
/* Returns malloced path. inpath beginning with '~/' expanded,
637
   otherwise returned as-is */
638
2
char * expand_homedir_path(const char *inpath) {
639
2
  struct passwd *pw = NULL;
640
2
  if (strncmp(inpath, "~/", 2) == 0) {
641
0
    char *homedir = getenv("HOME");
642
643
0
    if (!homedir) {
644
0
      pw = getpwuid(getuid());
645
0
      if (pw) {
646
0
        homedir = pw->pw_dir;
647
0
      }
648
0
    }
649
650
0
    if (homedir) {
651
0
      int len = strlen(inpath)-2 + strlen(homedir) + 2;
652
0
      char *buf = m_malloc(len);
653
0
      snprintf(buf, len, "%s/%s", homedir, inpath+2);
654
0
      return buf;
655
0
    }
656
0
  }
657
658
  /* Fallback */
659
2
  return m_strdup(inpath);
660
2
}
661
662
int constant_time_memcmp(const void* a, const void *b, size_t n)
663
0
{
664
0
  const char *xa = a, *xb = b;
665
0
  uint8_t c = 0;
666
0
  size_t i;
667
0
  for (i = 0; i < n; i++)
668
0
  {
669
0
    c |= (xa[i] ^ xb[i]);
670
0
  }
671
0
  return c;
672
0
}
673
674
/* higher-resolution monotonic timestamp, falls back to gettimeofday */
675
0
void gettime_wrapper(struct timespec *now) {
676
0
  struct timeval tv;
677
0
#if DROPBEAR_FUZZ
678
0
  if (fuzz.fuzzing) {
679
    /* time stands still when fuzzing */
680
0
    now->tv_sec = 5;
681
0
    now->tv_nsec = 0;
682
0
  }
683
0
#endif
684
685
0
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
686
  /* POSIX monotonic clock. Newer Linux, BSD, MacOSX >10.12 */
687
0
  if (clock_gettime(CLOCK_MONOTONIC, now) == 0) {
688
0
    return;
689
0
  }
690
0
#endif
691
692
0
#if defined(__linux__) && defined(SYS_clock_gettime)
693
0
  {
694
  /* Old linux toolchain - kernel might support it but not the build headers */
695
  /* Also glibc <2.17 requires -lrt which we neglect to add */
696
0
  static int linux_monotonic_failed = 0;
697
0
  if (!linux_monotonic_failed) {
698
    /* CLOCK_MONOTONIC isn't in some headers */
699
0
    int clock_source_monotonic = 1; 
700
0
    if (syscall(SYS_clock_gettime, clock_source_monotonic, now) == 0) {
701
0
      return;
702
0
    } else {
703
      /* Don't try again */
704
0
      linux_monotonic_failed = 1;
705
0
    }
706
0
  }
707
0
  }
708
0
#endif /* linux fallback clock_gettime */
709
710
#if defined(HAVE_MACH_ABSOLUTE_TIME)
711
  {
712
  /* OS X pre 10.12, see https://developer.apple.com/library/mac/qa/qa1398/_index.html */
713
  static mach_timebase_info_data_t timebase_info;
714
  uint64_t scaled_time;
715
  if (timebase_info.denom == 0) {
716
    mach_timebase_info(&timebase_info);
717
  }
718
  scaled_time = mach_absolute_time() * timebase_info.numer / timebase_info.denom;
719
  now->tv_sec = scaled_time / 1000000000;
720
  now->tv_nsec = scaled_time % 1000000000;
721
  }
722
#endif /* osx mach_absolute_time */
723
724
  /* Fallback for everything else - this will sometimes go backwards */
725
0
  gettimeofday(&tv, NULL);
726
0
  now->tv_sec = tv.tv_sec;
727
0
  now->tv_nsec = 1000*(long)tv.tv_usec;
728
0
}
729
730
/* second-resolution monotonic timestamp */
731
0
time_t monotonic_now() {
732
0
  struct timespec ts;
733
0
  gettime_wrapper(&ts);
734
0
  return ts.tv_sec;
735
0
}
736
737
0
void fsync_parent_dir(const char* fn) {
738
0
#ifdef HAVE_LIBGEN_H
739
0
  char *fn_dir = m_strdup(fn);
740
0
  char *dir = dirname(fn_dir);
741
0
  int dirfd = open(dir, O_RDONLY);
742
743
0
  if (dirfd != -1) {
744
0
    if (fsync(dirfd) != 0) {
745
0
      TRACE(("fsync of directory %s failed: %s", dir, strerror(errno)))
746
0
    }
747
0
    m_close(dirfd);
748
0
  } else {
749
0
    TRACE(("error opening directory %s for fsync: %s", dir, strerror(errno)))
750
0
  }
751
752
0
  m_free(fn_dir);
753
0
#endif
754
0
}
755
756
0
int fd_read_pending(int fd) {
757
0
  fd_set fds;
758
0
  struct timeval timeout;
759
760
0
  DROPBEAR_FD_ZERO(&fds);
761
0
  FD_SET(fd, &fds);
762
0
  while (1) {
763
0
    timeout.tv_sec = 0;
764
0
    timeout.tv_usec = 0;
765
0
    if (select(fd+1, &fds, NULL, NULL, &timeout) < 0) {
766
0
      if (errno == EINTR) {
767
0
        continue;
768
0
      }
769
0
      return 0;
770
0
    }
771
0
    return FD_ISSET(fd, &fds);
772
0
  }
773
0
}
774
775
0
int m_snprintf(char *str, size_t size, const char *format, ...) {
776
0
  va_list param;
777
0
  int ret;
778
779
0
  va_start(param, format);
780
0
  ret = vsnprintf(str, size, format, param);
781
0
  va_end(param);
782
0
  if (ret < 0) {
783
0
    dropbear_exit("snprintf failed");
784
0
  }
785
0
  return ret;
786
0
}