summaryrefslogtreecommitdiff
path: root/src/dbutil.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/dbutil.c')
-rw-r--r--src/dbutil.c786
1 files changed, 786 insertions, 0 deletions
diff --git a/src/dbutil.c b/src/dbutil.c
new file mode 100644
index 0000000..e8831c5
--- /dev/null
+++ b/src/dbutil.c
@@ -0,0 +1,786 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * Copyright (c) 2002,2003 Matt Johnston
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * strlcat() is copyright as follows:
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include "config.h"
+
+#ifdef __linux__
+#define _GNU_SOURCE
+/* To call clock_gettime() directly */
+#include <sys/syscall.h>
+#endif /* __linux */
+
+#ifdef HAVE_MACH_MACH_TIME_H
+#include <mach/mach_time.h>
+#include <mach/mach.h>
+#endif
+
+#include "includes.h"
+#include "dbutil.h"
+#include "buffer.h"
+#include "session.h"
+#include "atomicio.h"
+
+#define MAX_FMT 100
+
+static void generic_dropbear_exit(int exitcode, const char* format,
+ va_list param) ATTRIB_NORETURN;
+static void generic_dropbear_log(int priority, const char* format,
+ va_list param);
+
+void (*_dropbear_exit)(int exitcode, const char* format, va_list param) ATTRIB_NORETURN
+ = generic_dropbear_exit;
+void (*_dropbear_log)(int priority, const char* format, va_list param)
+ = generic_dropbear_log;
+
+#if DEBUG_TRACE
+int debug_trace = 0;
+#endif
+
+#ifndef DISABLE_SYSLOG
+void startsyslog(const char *ident) {
+
+ openlog(ident, LOG_PID, LOG_AUTHPRIV);
+
+}
+#endif /* DISABLE_SYSLOG */
+
+/* the "format" string must be <= 100 characters */
+void dropbear_close(const char* format, ...) {
+
+ va_list param;
+
+ va_start(param, format);
+ _dropbear_exit(EXIT_SUCCESS, format, param);
+ va_end(param);
+
+}
+
+void dropbear_exit(const char* format, ...) {
+
+ va_list param;
+
+ va_start(param, format);
+ _dropbear_exit(EXIT_FAILURE, format, param);
+ va_end(param);
+}
+
+static void generic_dropbear_exit(int exitcode, const char* format,
+ va_list param) {
+
+ char fmtbuf[300];
+
+ snprintf(fmtbuf, sizeof(fmtbuf), "Exited: %s", format);
+
+ _dropbear_log(LOG_INFO, fmtbuf, param);
+
+#if DROPBEAR_FUZZ
+ if (fuzz.do_jmp) {
+ longjmp(fuzz.jmp, 1);
+ }
+#endif
+
+ exit(exitcode);
+}
+
+void fail_assert(const char* expr, const char* file, int line) {
+ dropbear_exit("Failed assertion (%s:%d): `%s'", file, line, expr);
+}
+
+static void generic_dropbear_log(int UNUSED(priority), const char* format,
+ va_list param) {
+
+ char printbuf[1024];
+
+ vsnprintf(printbuf, sizeof(printbuf), format, param);
+
+ fprintf(stderr, "%s\n", printbuf);
+
+}
+
+/* this is what can be called to write arbitrary log messages */
+void dropbear_log(int priority, const char* format, ...) {
+
+ va_list param;
+
+ va_start(param, format);
+ _dropbear_log(priority, format, param);
+ va_end(param);
+}
+
+
+#if DEBUG_TRACE
+
+static double debug_start_time = -1;
+
+void debug_start_net()
+{
+ if (getenv("DROPBEAR_DEBUG_NET_TIMESTAMP"))
+ {
+ /* Timestamps start from first network activity */
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ debug_start_time = tv.tv_sec + (tv.tv_usec / 1000000.0);
+ TRACE(("Resetting Dropbear TRACE timestamps"))
+ }
+}
+
+static double time_since_start()
+{
+ double nowf;
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ nowf = tv.tv_sec + (tv.tv_usec / 1000000.0);
+ if (debug_start_time < 0)
+ {
+ debug_start_time = nowf;
+ return 0;
+ }
+ return nowf - debug_start_time;
+}
+
+static void dropbear_tracelevel(int level, const char *format, va_list param)
+{
+ if (debug_trace == 0 || debug_trace < level) {
+ return;
+ }
+
+ fprintf(stderr, "TRACE%d (%d) %f: ", level, getpid(), time_since_start());
+ vfprintf(stderr, format, param);
+ fprintf(stderr, "\n");
+}
+#if (DEBUG_TRACE>=1)
+void dropbear_trace1(const char* format, ...) {
+ va_list param;
+
+ va_start(param, format);
+ dropbear_tracelevel(1, format, param);
+ va_end(param);
+}
+#endif
+#if (DEBUG_TRACE>=2)
+void dropbear_trace2(const char* format, ...) {
+ va_list param;
+
+ va_start(param, format);
+ dropbear_tracelevel(2, format, param);
+ va_end(param);
+}
+#endif
+#if (DEBUG_TRACE>=3)
+void dropbear_trace3(const char* format, ...) {
+ va_list param;
+
+ va_start(param, format);
+ dropbear_tracelevel(3, format, param);
+ va_end(param);
+}
+#endif
+#if (DEBUG_TRACE>=4)
+void dropbear_trace4(const char* format, ...) {
+ va_list param;
+
+ va_start(param, format);
+ dropbear_tracelevel(4, format, param);
+ va_end(param);
+}
+#endif
+#if (DEBUG_TRACE>=5)
+void dropbear_trace5(const char* format, ...) {
+ va_list param;
+
+ va_start(param, format);
+ dropbear_tracelevel(5, format, param);
+ va_end(param);
+}
+#endif
+#endif
+
+
+/* Connect to a given unix socket. The socket is blocking */
+#if ENABLE_CONNECT_UNIX
+int connect_unix(const char* path) {
+ struct sockaddr_un addr;
+ int fd = -1;
+
+ memset((void*)&addr, 0x0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strlcpy(addr.sun_path, path, sizeof(addr.sun_path));
+ fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ TRACE(("Failed to open unix socket"))
+ return -1;
+ }
+ if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
+ TRACE(("Failed to connect to '%s' socket", path))
+ m_close(fd);
+ return -1;
+ }
+ return fd;
+}
+#endif
+
+/* Sets up a pipe for a, returning three non-blocking file descriptors
+ * and the pid. exec_fn is the function that will actually execute the child process,
+ * it will be run after the child has fork()ed, and is passed exec_data.
+ * If ret_errfd == NULL then stderr will not be captured.
+ * ret_pid can be passed as NULL to discard the pid. */
+int spawn_command(void(*exec_fn)(const void *user_data), const void *exec_data,
+ int *ret_writefd, int *ret_readfd, int *ret_errfd, pid_t *ret_pid) {
+ int infds[2];
+ int outfds[2];
+ int errfds[2];
+ pid_t pid;
+
+ const int FDIN = 0;
+ const int FDOUT = 1;
+
+#if DROPBEAR_FUZZ
+ if (fuzz.fuzzing) {
+ return fuzz_spawn_command(ret_writefd, ret_readfd, ret_errfd, ret_pid);
+ }
+#endif
+
+ /* redirect stdin/stdout/stderr */
+ if (pipe(infds) != 0) {
+ return DROPBEAR_FAILURE;
+ }
+ if (pipe(outfds) != 0) {
+ return DROPBEAR_FAILURE;
+ }
+ if (ret_errfd && pipe(errfds) != 0) {
+ return DROPBEAR_FAILURE;
+ }
+
+#if DROPBEAR_VFORK
+ pid = vfork();
+#else
+ pid = fork();
+#endif
+
+ if (pid < 0) {
+ return DROPBEAR_FAILURE;
+ }
+
+ if (!pid) {
+ /* child */
+
+ TRACE(("back to normal sigchld"))
+ /* Revert to normal sigchld handling */
+ if (signal(SIGCHLD, SIG_DFL) == SIG_ERR) {
+ dropbear_exit("signal() error");
+ }
+
+ /* redirect stdin/stdout */
+
+ if ((dup2(infds[FDIN], STDIN_FILENO) < 0) ||
+ (dup2(outfds[FDOUT], STDOUT_FILENO) < 0) ||
+ (ret_errfd && dup2(errfds[FDOUT], STDERR_FILENO) < 0)) {
+ TRACE(("leave noptycommand: error redirecting FDs"))
+ dropbear_exit("Child dup2() failure");
+ }
+
+ close(infds[FDOUT]);
+ close(infds[FDIN]);
+ close(outfds[FDIN]);
+ close(outfds[FDOUT]);
+ if (ret_errfd)
+ {
+ close(errfds[FDIN]);
+ close(errfds[FDOUT]);
+ }
+
+ exec_fn(exec_data);
+ /* not reached */
+ return DROPBEAR_FAILURE;
+ } else {
+ /* parent */
+ close(infds[FDIN]);
+ close(outfds[FDOUT]);
+
+ setnonblocking(outfds[FDIN]);
+ setnonblocking(infds[FDOUT]);
+
+ if (ret_errfd) {
+ close(errfds[FDOUT]);
+ setnonblocking(errfds[FDIN]);
+ }
+
+ if (ret_pid) {
+ *ret_pid = pid;
+ }
+
+ *ret_writefd = infds[FDOUT];
+ *ret_readfd = outfds[FDIN];
+ if (ret_errfd) {
+ *ret_errfd = errfds[FDIN];
+ }
+ return DROPBEAR_SUCCESS;
+ }
+}
+
+/* Runs a command with "sh -c". Will close FDs (except stdin/stdout/stderr) and
+ * re-enabled SIGPIPE. If cmd is NULL, will run a login shell.
+ */
+void run_shell_command(const char* cmd, unsigned int maxfd, char* usershell) {
+ char * argv[4];
+ char * baseshell = NULL;
+ unsigned int i;
+
+ baseshell = basename(usershell);
+
+ if (cmd != NULL) {
+ argv[0] = baseshell;
+ } else {
+ /* a login shell should be "-bash" for "/bin/bash" etc */
+ int len = strlen(baseshell) + 2; /* 2 for "-" */
+ argv[0] = (char*)m_malloc(len);
+ snprintf(argv[0], len, "-%s", baseshell);
+ }
+
+ if (cmd != NULL) {
+ argv[1] = "-c";
+ argv[2] = (char*)cmd;
+ argv[3] = NULL;
+ } else {
+ /* construct a shell of the form "-bash" etc */
+ argv[1] = NULL;
+ }
+
+ /* Re-enable SIGPIPE for the executed process */
+ if (signal(SIGPIPE, SIG_DFL) == SIG_ERR) {
+ dropbear_exit("signal() error");
+ }
+
+ /* close file descriptors except stdin/stdout/stderr
+ * Need to be sure FDs are closed here to avoid reading files as root */
+ for (i = 3; i <= maxfd; i++) {
+ m_close(i);
+ }
+
+ execv(usershell, argv);
+}
+
+#if DEBUG_TRACE
+void printhex(const char * label, const unsigned char * buf, int len) {
+ int i, j;
+
+ fprintf(stderr, "%s\n", label);
+ /* for each 16 byte line */
+ for (j = 0; j < len; j += 16) {
+ const int linelen = MIN(16, len - j);
+
+ /* print hex digits */
+ for (i = 0; i < 16; i++) {
+ if (i < linelen) {
+ fprintf(stderr, "%02x", buf[j+i]);
+ } else {
+ fprintf(stderr, " ");
+ }
+ // separator between pairs
+ if (i % 2 ==1) {
+ fprintf(stderr, " ");
+ }
+ }
+
+ /* print characters */
+ fprintf(stderr, " ");
+ for (i = 0; i < linelen; i++) {
+ char c = buf[j+i];
+ if (!isprint(c)) {
+ c = '.';
+ }
+ fputc(c, stderr);
+ }
+ fprintf(stderr, "\n");
+ }
+}
+
+void printmpint(const char *label, const mp_int *mp) {
+ buffer *buf = buf_new(1000);
+ buf_putmpint(buf, mp);
+ fprintf(stderr, "%d bits ", mp_count_bits(mp));
+ printhex(label, buf->data, buf->len);
+ buf_free(buf);
+
+}
+#endif
+
+/* Strip all control characters from text (a null-terminated string), except
+ * for '\n', '\r' and '\t'.
+ * The result returned is a newly allocated string, this must be free()d after
+ * use */
+char * stripcontrol(const char * text) {
+
+ char * ret;
+ int len, pos;
+ int i;
+
+ len = strlen(text);
+ ret = m_malloc(len+1);
+
+ pos = 0;
+ for (i = 0; i < len; i++) {
+ if ((text[i] <= '~' && text[i] >= ' ') /* normal printable range */
+ || text[i] == '\n' || text[i] == '\r' || text[i] == '\t') {
+ ret[pos] = text[i];
+ pos++;
+ }
+ }
+ ret[pos] = 0x0;
+ return ret;
+}
+
+
+/* reads the contents of filename into the buffer buf, from the current
+ * position, either to the end of the file, or the buffer being full.
+ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+int buf_readfile(buffer* buf, const char* filename) {
+
+ int fd = -1;
+ int len;
+ int maxlen;
+ int ret = DROPBEAR_FAILURE;
+
+ fd = open(filename, O_RDONLY);
+
+ if (fd < 0) {
+ goto out;
+ }
+
+ do {
+ maxlen = buf->size - buf->pos;
+ len = read(fd, buf_getwriteptr(buf, maxlen), maxlen);
+ if (len < 0) {
+ if (errno == EINTR || errno == EAGAIN) {
+ continue;
+ }
+ goto out;
+ }
+ buf_incrwritepos(buf, len);
+ } while (len < maxlen && len > 0);
+
+ ret = DROPBEAR_SUCCESS;
+
+out:
+ if (fd >= 0) {
+ m_close(fd);
+ }
+ return ret;
+}
+
+/* get a line from the file into buffer in the style expected for an
+ * authkeys file.
+ * Will return DROPBEAR_SUCCESS if data is read, or DROPBEAR_FAILURE on EOF.*/
+/* Only used for ~/.ssh/known_hosts and ~/.ssh/authorized_keys */
+#if DROPBEAR_CLIENT || DROPBEAR_SVR_PUBKEY_AUTH
+int buf_getline(buffer * line, FILE * authfile) {
+
+ int c = EOF;
+
+ buf_setpos(line, 0);
+ buf_setlen(line, 0);
+
+ while (line->pos < line->size) {
+
+ c = fgetc(authfile); /*getc() is weird with some uClibc systems*/
+ if (c == EOF || c == '\n' || c == '\r') {
+ goto out;
+ }
+
+ buf_putbyte(line, (unsigned char)c);
+ }
+
+ TRACE(("leave getauthline: line too long"))
+ /* We return success, but the line length will be zeroed - ie we just
+ * ignore that line */
+ buf_setlen(line, 0);
+
+out:
+
+
+ /* if we didn't read anything before EOF or error, exit */
+ if (c == EOF && line->pos == 0) {
+ return DROPBEAR_FAILURE;
+ } else {
+ buf_setpos(line, 0);
+ return DROPBEAR_SUCCESS;
+ }
+
+}
+#endif
+
+/* make sure that the socket closes */
+void m_close(int fd) {
+ int val;
+
+ if (fd < 0) {
+ return;
+ }
+
+ do {
+ val = close(fd);
+ } while (val < 0 && errno == EINTR);
+
+ if (val < 0 && errno != EBADF) {
+ /* Linux says EIO can happen */
+ dropbear_exit("Error closing fd %d, %s", fd, strerror(errno));
+ }
+}
+
+void setnonblocking(int fd) {
+
+ TRACE(("setnonblocking: %d", fd))
+
+#if DROPBEAR_FUZZ
+ if (fuzz.fuzzing) {
+ return;
+ }
+#endif
+
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
+ if (errno == ENODEV) {
+ /* Some devices (like /dev/null redirected in)
+ * can't be set to non-blocking */
+ TRACE(("ignoring ENODEV for setnonblocking"))
+ } else {
+ {
+ dropbear_exit("Couldn't set nonblocking");
+ }
+ }
+ }
+ TRACE(("leave setnonblocking"))
+}
+
+void disallow_core() {
+ struct rlimit lim = {0};
+ if (getrlimit(RLIMIT_CORE, &lim) < 0) {
+ TRACE(("getrlimit(RLIMIT_CORE) failed"));
+ }
+ lim.rlim_cur = 0;
+ if (setrlimit(RLIMIT_CORE, &lim) < 0) {
+ TRACE(("setrlimit(RLIMIT_CORE) failed"));
+ }
+}
+
+/* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE, with the result in *val */
+int m_str_to_uint(const char* str, unsigned int *val) {
+ unsigned long l;
+ char *endp;
+
+ l = strtoul(str, &endp, 10);
+
+ if (endp == str || *endp != '\0') {
+ /* parse error */
+ return DROPBEAR_FAILURE;
+ }
+
+ /* The c99 spec doesn't actually seem to define EINVAL, but most platforms
+ * I've looked at mention it in their manpage */
+ if ((l == 0 && errno == EINVAL)
+ || (l == ULONG_MAX && errno == ERANGE)
+ || (l > UINT_MAX)) {
+ return DROPBEAR_FAILURE;
+ } else {
+ *val = l;
+ return DROPBEAR_SUCCESS;
+ }
+}
+
+/* Returns malloced path. inpath beginning with '~/' expanded,
+ otherwise returned as-is */
+char * expand_homedir_path(const char *inpath) {
+ struct passwd *pw = NULL;
+ if (strncmp(inpath, "~/", 2) == 0) {
+ char *homedir = getenv("HOME");
+
+ if (!homedir) {
+ pw = getpwuid(getuid());
+ if (pw) {
+ homedir = pw->pw_dir;
+ }
+ }
+
+ if (homedir) {
+ int len = strlen(inpath)-2 + strlen(homedir) + 2;
+ char *buf = m_malloc(len);
+ snprintf(buf, len, "%s/%s", homedir, inpath+2);
+ return buf;
+ }
+ }
+
+ /* Fallback */
+ return m_strdup(inpath);
+}
+
+int constant_time_memcmp(const void* a, const void *b, size_t n)
+{
+ const char *xa = a, *xb = b;
+ uint8_t c = 0;
+ size_t i;
+ for (i = 0; i < n; i++)
+ {
+ c |= (xa[i] ^ xb[i]);
+ }
+ return c;
+}
+
+/* higher-resolution monotonic timestamp, falls back to gettimeofday */
+void gettime_wrapper(struct timespec *now) {
+ struct timeval tv;
+#if DROPBEAR_FUZZ
+ if (fuzz.fuzzing) {
+ /* time stands still when fuzzing */
+ now->tv_sec = 5;
+ now->tv_nsec = 0;
+ }
+#endif
+
+#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
+ /* POSIX monotonic clock. Newer Linux, BSD, MacOSX >10.12 */
+ if (clock_gettime(CLOCK_MONOTONIC, now) == 0) {
+ return;
+ }
+#endif
+
+#if defined(__linux__) && defined(SYS_clock_gettime)
+ {
+ /* Old linux toolchain - kernel might support it but not the build headers */
+ /* Also glibc <2.17 requires -lrt which we neglect to add */
+ static int linux_monotonic_failed = 0;
+ if (!linux_monotonic_failed) {
+ /* CLOCK_MONOTONIC isn't in some headers */
+ int clock_source_monotonic = 1;
+ if (syscall(SYS_clock_gettime, clock_source_monotonic, now) == 0) {
+ return;
+ } else {
+ /* Don't try again */
+ linux_monotonic_failed = 1;
+ }
+ }
+ }
+#endif /* linux fallback clock_gettime */
+
+#if defined(HAVE_MACH_ABSOLUTE_TIME)
+ {
+ /* OS X pre 10.12, see https://developer.apple.com/library/mac/qa/qa1398/_index.html */
+ static mach_timebase_info_data_t timebase_info;
+ uint64_t scaled_time;
+ if (timebase_info.denom == 0) {
+ mach_timebase_info(&timebase_info);
+ }
+ scaled_time = mach_absolute_time() * timebase_info.numer / timebase_info.denom;
+ now->tv_sec = scaled_time / 1000000000;
+ now->tv_nsec = scaled_time % 1000000000;
+ }
+#endif /* osx mach_absolute_time */
+
+ /* Fallback for everything else - this will sometimes go backwards */
+ gettimeofday(&tv, NULL);
+ now->tv_sec = tv.tv_sec;
+ now->tv_nsec = 1000*(long)tv.tv_usec;
+}
+
+/* second-resolution monotonic timestamp */
+time_t monotonic_now() {
+ struct timespec ts;
+ gettime_wrapper(&ts);
+ return ts.tv_sec;
+}
+
+void fsync_parent_dir(const char* fn) {
+#ifdef HAVE_LIBGEN_H
+ char *fn_dir = m_strdup(fn);
+ char *dir = dirname(fn_dir);
+ int dirfd = open(dir, O_RDONLY);
+
+ if (dirfd != -1) {
+ if (fsync(dirfd) != 0) {
+ TRACE(("fsync of directory %s failed: %s", dir, strerror(errno)))
+ }
+ m_close(dirfd);
+ } else {
+ TRACE(("error opening directory %s for fsync: %s", dir, strerror(errno)))
+ }
+
+ m_free(fn_dir);
+#endif
+}
+
+int fd_read_pending(int fd) {
+ fd_set fds;
+ struct timeval timeout;
+
+ DROPBEAR_FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+ while (1) {
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+ if (select(fd+1, &fds, NULL, NULL, &timeout) < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ return 0;
+ }
+ return FD_ISSET(fd, &fds);
+ }
+}
+
+int m_snprintf(char *str, size_t size, const char *format, ...) {
+ va_list param;
+ int ret;
+
+ va_start(param, format);
+ ret = vsnprintf(str, size, format, param);
+ va_end(param);
+ if (ret < 0) {
+ dropbear_exit("snprintf failed");
+ }
+ return ret;
+}