Coverage Report

Created: 2025-11-13 06:07

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
155
void dropbear_exit(const char* format, ...) {
106
107
155
  va_list param;
108
109
155
  va_start(param, format);
110
155
  _dropbear_exit(EXIT_FAILURE, format, param);
111
155
  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
155
void dropbear_log(int priority, const char* format, ...) {
149
150
155
  va_list param;
151
152
155
  va_start(param, format);
153
155
  _dropbear_log(priority, format, param);
154
155
  va_end(param);
155
155
}
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 (!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
0
int buf_readfile(buffer* buf, const char* filename) {
490
491
0
  int fd = -1;
492
0
  int len;
493
0
  int maxlen;
494
0
  int ret = DROPBEAR_FAILURE;
495
496
0
  fd = open(filename, O_RDONLY);
497
498
0
  if (fd < 0) {
499
0
    goto out;
500
0
  }
501
  
502
0
  do {
503
0
    maxlen = buf->size - buf->pos;
504
0
    len = read(fd, buf_getwriteptr(buf, maxlen), maxlen);
505
0
    if (len < 0) {
506
0
      if (errno == EINTR || errno == EAGAIN) {
507
0
        continue;
508
0
      }
509
0
      goto out;
510
0
    }
511
0
    buf_incrwritepos(buf, len);
512
0
  } while (len < maxlen && len > 0);
513
514
0
  ret = DROPBEAR_SUCCESS;
515
516
0
out:
517
0
  if (fd >= 0) {
518
0
    m_close(fd);
519
0
  }
520
0
  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.*/
526
/* Only used for ~/.ssh/known_hosts and ~/.ssh/authorized_keys */
527
#if DROPBEAR_CLIENT || DROPBEAR_SVR_PUBKEY_AUTH
528
0
int buf_getline(buffer * line, FILE * authfile) {
529
530
0
  int c = EOF;
531
532
0
  buf_setpos(line, 0);
533
0
  buf_setlen(line, 0);
534
535
0
  while (line->pos < line->size) {
536
537
0
    c = fgetc(authfile); /*getc() is weird with some uClibc systems*/
538
0
    if (c == EOF || c == '\n' || c == '\r') {
539
0
      goto out;
540
0
    }
541
542
0
    buf_putbyte(line, (unsigned char)c);
543
0
  }
544
545
0
  TRACE(("leave getauthline: line too long"))
546
  /* We return success, but the line length will be zeroed - ie we just
547
   * ignore that line */
548
0
  buf_setlen(line, 0);
549
550
0
out:
551
552
553
  /* if we didn't read anything before EOF or error, exit */
554
0
  if (c == EOF && line->pos == 0) {
555
0
    return DROPBEAR_FAILURE;
556
0
  } else {
557
0
    buf_setpos(line, 0);
558
0
    return DROPBEAR_SUCCESS;
559
0
  }
560
561
0
}  
562
#endif
563
564
/* make sure that the socket closes */
565
0
void m_close(int fd) {
566
0
  int val;
567
568
0
  if (fd < 0) {
569
0
    return;
570
0
  }
571
572
0
  do {
573
0
    val = close(fd);
574
0
  } while (val < 0 && errno == EINTR);
575
576
0
  if (val < 0 && errno != EBADF) {
577
    /* Linux says EIO can happen */
578
0
    dropbear_exit("Error closing fd %d, %s", fd, strerror(errno));
579
0
  }
580
0
}
581
582
0
void setnonblocking(int fd) {
583
584
0
  int fl = 0;
585
0
  TRACE(("setnonblocking: %d", fd))
586
587
0
#if DROPBEAR_FUZZ
588
0
  if (fuzz.fuzzing) {
589
0
    return;
590
0
  }
591
0
#endif
592
0
  fl = fcntl(fd, F_GETFL, 0);
593
0
  if (fl == -1) {
594
    /* F_GETFL shouldn't fail */
595
0
    dropbear_exit("Couldn't set nonblocking");
596
0
  }
597
598
0
  if (fcntl(fd, F_SETFL, fl | O_NONBLOCK) == -1) {
599
0
    if (errno == ENODEV) {
600
      /* Some devices (like /dev/null redirected in)
601
       * can't be set to non-blocking */
602
0
      TRACE(("ignoring ENODEV for setnonblocking"))
603
0
    } else {
604
0
      dropbear_exit("Couldn't set nonblocking");
605
0
    }
606
0
  }
607
0
  TRACE(("leave setnonblocking"))
608
0
}
609
610
4
void disallow_core() {
611
4
  struct rlimit lim = {0};
612
4
  if (getrlimit(RLIMIT_CORE, &lim) < 0) {
613
0
    TRACE(("getrlimit(RLIMIT_CORE) failed"));
614
0
  }
615
4
  lim.rlim_cur = 0;
616
4
  if (setrlimit(RLIMIT_CORE, &lim) < 0) {
617
0
    TRACE(("setrlimit(RLIMIT_CORE) failed"));
618
0
  }
619
4
}
620
621
/* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE, with the result in *val */
622
0
int m_str_to_uint(const char* str, unsigned int *val) {
623
0
  unsigned long l;
624
0
  char *endp;
625
626
0
  l = strtoul(str, &endp, 10);
627
628
0
  if (endp == str || *endp != '\0') {
629
    /* parse error */
630
0
    return DROPBEAR_FAILURE;
631
0
  }
632
633
  /* The c99 spec doesn't actually seem to define EINVAL, but most platforms
634
   * I've looked at mention it in their manpage */
635
0
  if ((l == 0 && errno == EINVAL)
636
0
    || (l == ULONG_MAX && errno == ERANGE)
637
0
    || (l > UINT_MAX)) {
638
0
    return DROPBEAR_FAILURE;
639
0
  } else {
640
0
    *val = l;
641
0
    return DROPBEAR_SUCCESS;
642
0
  }
643
0
}
644
645
/* Returns malloced path from inpath, possibly expanding '~/'
646
   into the specified home directory.*/
647
2
char * expand_homedir_path_home(const char *inpath, const char *homedir) {
648
2
  if (strncmp(inpath, "~/", 2) == 0 && homedir) {
649
0
    size_t len = strlen(inpath)-2 + strlen(homedir) + 2;
650
0
    char *buf = m_malloc(len);
651
0
    snprintf(buf, len, "%s/%s", homedir, inpath+2);
652
0
    return buf;
653
0
  }
654
  /* Fallback */
655
2
  return m_strdup(inpath);
656
2
}
657
658
/* Returns malloced path from inpath, possibly expanding '~/'
659
   into the current user's home directory.*/
660
2
char * expand_homedir_path(const char *inpath) {
661
2
  struct passwd *pw = NULL;
662
2
  char *homedir = getenv("HOME");
663
664
2
  if (!homedir) {
665
0
    pw = getpwuid(getuid());
666
0
    if (pw) {
667
0
      homedir = pw->pw_dir;
668
0
    }
669
0
  }
670
2
  return expand_homedir_path_home(inpath, homedir);
671
2
}
672
673
int constant_time_memcmp(const void* a, const void *b, size_t n)
674
2
{
675
2
  const char *xa = a, *xb = b;
676
2
  uint8_t c = 0;
677
2
  size_t i;
678
66
  for (i = 0; i < n; i++)
679
64
  {
680
64
    c |= (xa[i] ^ xb[i]);
681
64
  }
682
2
  return c;
683
2
}
684
685
/* higher-resolution monotonic timestamp, falls back to gettimeofday */
686
0
void gettime_wrapper(struct timespec *now) {
687
0
  struct timeval tv;
688
0
#if DROPBEAR_FUZZ
689
0
  if (fuzz.fuzzing) {
690
    /* time stands still when fuzzing */
691
0
    now->tv_sec = 5;
692
0
    now->tv_nsec = 0;
693
0
  }
694
0
#endif
695
696
0
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
697
  /* POSIX monotonic clock. Newer Linux, BSD, MacOSX >10.12 */
698
0
  if (clock_gettime(CLOCK_MONOTONIC, now) == 0) {
699
0
    return;
700
0
  }
701
0
#endif
702
703
0
#if defined(__linux__) && defined(SYS_clock_gettime)
704
0
  {
705
  /* Old linux toolchain - kernel might support it but not the build headers */
706
  /* Also glibc <2.17 requires -lrt which we neglect to add */
707
0
  static int linux_monotonic_failed = 0;
708
0
  if (!linux_monotonic_failed) {
709
    /* CLOCK_MONOTONIC isn't in some headers */
710
0
    int clock_source_monotonic = 1; 
711
0
    if (syscall(SYS_clock_gettime, clock_source_monotonic, now) == 0) {
712
0
      return;
713
0
    } else {
714
      /* Don't try again */
715
0
      linux_monotonic_failed = 1;
716
0
    }
717
0
  }
718
0
  }
719
0
#endif /* linux fallback clock_gettime */
720
721
#if defined(HAVE_MACH_ABSOLUTE_TIME)
722
  {
723
  /* OS X pre 10.12, see https://developer.apple.com/library/mac/qa/qa1398/_index.html */
724
  static mach_timebase_info_data_t timebase_info;
725
  uint64_t scaled_time;
726
  if (timebase_info.denom == 0) {
727
    mach_timebase_info(&timebase_info);
728
  }
729
  scaled_time = mach_absolute_time() * timebase_info.numer / timebase_info.denom;
730
  now->tv_sec = scaled_time / 1000000000;
731
  now->tv_nsec = scaled_time % 1000000000;
732
  }
733
#endif /* osx mach_absolute_time */
734
735
  /* Fallback for everything else - this will sometimes go backwards */
736
0
  gettimeofday(&tv, NULL);
737
0
  now->tv_sec = tv.tv_sec;
738
0
  now->tv_nsec = 1000*(long)tv.tv_usec;
739
0
}
740
741
/* second-resolution monotonic timestamp */
742
0
time_t monotonic_now() {
743
0
  struct timespec ts;
744
0
  gettime_wrapper(&ts);
745
0
  return ts.tv_sec;
746
0
}
747
748
0
void fsync_parent_dir(const char* fn) {
749
0
#ifdef HAVE_LIBGEN_H
750
0
  char *fn_dir = m_strdup(fn);
751
0
  char *dir = dirname(fn_dir);
752
0
  int dirfd = open(dir, O_RDONLY);
753
754
0
  if (dirfd != -1) {
755
0
    if (fsync(dirfd) != 0) {
756
0
      TRACE(("fsync of directory %s failed: %s", dir, strerror(errno)))
757
0
    }
758
0
    m_close(dirfd);
759
0
  } else {
760
0
    TRACE(("error opening directory %s for fsync: %s", dir, strerror(errno)))
761
0
  }
762
763
0
  m_free(fn_dir);
764
0
#endif
765
0
}
766
767
0
int fd_read_pending(int fd) {
768
0
  fd_set fds;
769
0
  struct timeval timeout;
770
771
0
  DROPBEAR_FD_ZERO(&fds);
772
0
  FD_SET(fd, &fds);
773
0
  while (1) {
774
0
    timeout.tv_sec = 0;
775
0
    timeout.tv_usec = 0;
776
0
    if (select(fd+1, &fds, NULL, NULL, &timeout) < 0) {
777
0
      if (errno == EINTR) {
778
0
        continue;
779
0
      }
780
0
      return 0;
781
0
    }
782
0
    return FD_ISSET(fd, &fds);
783
0
  }
784
0
}
785
786
0
int m_snprintf(char *str, size_t size, const char *format, ...) {
787
0
  va_list param;
788
0
  int ret;
789
790
0
  va_start(param, format);
791
0
  ret = vsnprintf(str, size, format, param);
792
0
  va_end(param);
793
0
  if (ret < 0) {
794
0
    dropbear_exit("snprintf failed");
795
0
  }
796
0
  return ret;
797
0
}