dropbear 2015.71
[tomato.git] / release / src / router / dropbear / dbutil.c
blobd87835b5b0ab5cb7d8a31ebc0733c66973582134
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:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
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.
25 * strlcat() is copyright as follows:
26 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
27 * All rights reserved.
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.
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. */
51 #include "config.h"
53 #ifdef __linux__
54 #define _GNU_SOURCE
55 /* To call clock_gettime() directly */
56 #include <sys/syscall.h>
57 #endif /* __linux */
59 #ifdef HAVE_MACH_MACH_TIME_H
60 #include <mach/mach_time.h>
61 #include <mach/mach.h>
62 #endif
64 #include "includes.h"
65 #include "dbutil.h"
66 #include "buffer.h"
67 #include "session.h"
68 #include "atomicio.h"
70 #define MAX_FMT 100
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);
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;
82 #ifdef DEBUG_TRACE
83 int debug_trace = 0;
84 #endif
86 #ifndef DISABLE_SYSLOG
87 void startsyslog() {
89 openlog(PROGNAME, LOG_PID, LOG_AUTHPRIV);
92 #endif /* DISABLE_SYSLOG */
94 /* the "format" string must be <= 100 characters */
95 void dropbear_close(const char* format, ...) {
97 va_list param;
99 va_start(param, format);
100 _dropbear_exit(EXIT_SUCCESS, format, param);
101 va_end(param);
105 void dropbear_exit(const char* format, ...) {
107 va_list param;
109 va_start(param, format);
110 _dropbear_exit(EXIT_FAILURE, format, param);
111 va_end(param);
114 static void generic_dropbear_exit(int exitcode, const char* format,
115 va_list param) {
117 char fmtbuf[300];
119 snprintf(fmtbuf, sizeof(fmtbuf), "Exited: %s", format);
121 _dropbear_log(LOG_INFO, fmtbuf, param);
123 exit(exitcode);
126 void fail_assert(const char* expr, const char* file, int line) {
127 dropbear_exit("Failed assertion (%s:%d): `%s'", file, line, expr);
130 static void generic_dropbear_log(int UNUSED(priority), const char* format,
131 va_list param) {
133 char printbuf[1024];
135 vsnprintf(printbuf, sizeof(printbuf), format, param);
137 fprintf(stderr, "%s\n", printbuf);
141 /* this is what can be called to write arbitrary log messages */
142 void dropbear_log(int priority, const char* format, ...) {
144 va_list param;
146 va_start(param, format);
147 _dropbear_log(priority, format, param);
148 va_end(param);
152 #ifdef DEBUG_TRACE
154 static double debug_start_time = -1;
156 void debug_start_net()
158 if (getenv("DROPBEAR_DEBUG_NET_TIMESTAMP"))
160 /* Timestamps start from first network activity */
161 struct timeval tv;
162 gettimeofday(&tv, NULL);
163 debug_start_time = tv.tv_sec + (tv.tv_usec / 1000000.0);
164 TRACE(("Resetting Dropbear TRACE timestamps"))
168 static double time_since_start()
170 double nowf;
171 struct timeval tv;
172 gettimeofday(&tv, NULL);
173 nowf = tv.tv_sec + (tv.tv_usec / 1000000.0);
174 if (debug_start_time < 0)
176 debug_start_time = nowf;
177 return 0;
179 return nowf - debug_start_time;
182 void dropbear_trace(const char* format, ...) {
183 va_list param;
185 if (!debug_trace) {
186 return;
189 va_start(param, format);
190 fprintf(stderr, "TRACE (%d) %f: ", getpid(), time_since_start());
191 vfprintf(stderr, format, param);
192 fprintf(stderr, "\n");
193 va_end(param);
196 void dropbear_trace2(const char* format, ...) {
197 static int trace_env = -1;
198 va_list param;
200 if (trace_env == -1) {
201 trace_env = getenv("DROPBEAR_TRACE2") ? 1 : 0;
204 if (!(debug_trace && trace_env)) {
205 return;
208 va_start(param, format);
209 fprintf(stderr, "TRACE2 (%d) %f: ", getpid(), time_since_start());
210 vfprintf(stderr, format, param);
211 fprintf(stderr, "\n");
212 va_end(param);
214 #endif /* DEBUG_TRACE */
216 /* Connect to a given unix socket. The socket is blocking */
217 #ifdef ENABLE_CONNECT_UNIX
218 int connect_unix(const char* path) {
219 struct sockaddr_un addr;
220 int fd = -1;
222 memset((void*)&addr, 0x0, sizeof(addr));
223 addr.sun_family = AF_UNIX;
224 strlcpy(addr.sun_path, path, sizeof(addr.sun_path));
225 fd = socket(PF_UNIX, SOCK_STREAM, 0);
226 if (fd < 0) {
227 TRACE(("Failed to open unix socket"))
228 return -1;
230 if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
231 TRACE(("Failed to connect to '%s' socket", path))
232 m_close(fd);
233 return -1;
235 return fd;
237 #endif
239 /* Sets up a pipe for a, returning three non-blocking file descriptors
240 * and the pid. exec_fn is the function that will actually execute the child process,
241 * it will be run after the child has fork()ed, and is passed exec_data.
242 * If ret_errfd == NULL then stderr will not be captured.
243 * ret_pid can be passed as NULL to discard the pid. */
244 int spawn_command(void(*exec_fn)(void *user_data), void *exec_data,
245 int *ret_writefd, int *ret_readfd, int *ret_errfd, pid_t *ret_pid) {
246 int infds[2];
247 int outfds[2];
248 int errfds[2];
249 pid_t pid;
251 const int FDIN = 0;
252 const int FDOUT = 1;
254 /* redirect stdin/stdout/stderr */
255 if (pipe(infds) != 0) {
256 return DROPBEAR_FAILURE;
258 if (pipe(outfds) != 0) {
259 return DROPBEAR_FAILURE;
261 if (ret_errfd && pipe(errfds) != 0) {
262 return DROPBEAR_FAILURE;
265 #ifdef USE_VFORK
266 pid = vfork();
267 #else
268 pid = fork();
269 #endif
271 if (pid < 0) {
272 return DROPBEAR_FAILURE;
275 if (!pid) {
276 /* child */
278 TRACE(("back to normal sigchld"))
279 /* Revert to normal sigchld handling */
280 if (signal(SIGCHLD, SIG_DFL) == SIG_ERR) {
281 dropbear_exit("signal() error");
284 /* redirect stdin/stdout */
286 if ((dup2(infds[FDIN], STDIN_FILENO) < 0) ||
287 (dup2(outfds[FDOUT], STDOUT_FILENO) < 0) ||
288 (ret_errfd && dup2(errfds[FDOUT], STDERR_FILENO) < 0)) {
289 TRACE(("leave noptycommand: error redirecting FDs"))
290 dropbear_exit("Child dup2() failure");
293 close(infds[FDOUT]);
294 close(infds[FDIN]);
295 close(outfds[FDIN]);
296 close(outfds[FDOUT]);
297 if (ret_errfd)
299 close(errfds[FDIN]);
300 close(errfds[FDOUT]);
303 exec_fn(exec_data);
304 /* not reached */
305 return DROPBEAR_FAILURE;
306 } else {
307 /* parent */
308 close(infds[FDIN]);
309 close(outfds[FDOUT]);
311 setnonblocking(outfds[FDIN]);
312 setnonblocking(infds[FDOUT]);
314 if (ret_errfd) {
315 close(errfds[FDOUT]);
316 setnonblocking(errfds[FDIN]);
319 if (ret_pid) {
320 *ret_pid = pid;
323 *ret_writefd = infds[FDOUT];
324 *ret_readfd = outfds[FDIN];
325 if (ret_errfd) {
326 *ret_errfd = errfds[FDIN];
328 return DROPBEAR_SUCCESS;
332 /* Runs a command with "sh -c". Will close FDs (except stdin/stdout/stderr) and
333 * re-enabled SIGPIPE. If cmd is NULL, will run a login shell.
335 void run_shell_command(const char* cmd, unsigned int maxfd, char* usershell) {
336 char * argv[4];
337 char * baseshell = NULL;
338 unsigned int i;
340 baseshell = basename(usershell);
342 if (cmd != NULL) {
343 argv[0] = baseshell;
344 } else {
345 /* a login shell should be "-bash" for "/bin/bash" etc */
346 int len = strlen(baseshell) + 2; /* 2 for "-" */
347 argv[0] = (char*)m_malloc(len);
348 snprintf(argv[0], len, "-%s", baseshell);
351 if (cmd != NULL) {
352 argv[1] = "-c";
353 argv[2] = (char*)cmd;
354 argv[3] = NULL;
355 } else {
356 /* construct a shell of the form "-bash" etc */
357 argv[1] = NULL;
360 /* Re-enable SIGPIPE for the executed process */
361 if (signal(SIGPIPE, SIG_DFL) == SIG_ERR) {
362 dropbear_exit("signal() error");
365 /* close file descriptors except stdin/stdout/stderr
366 * Need to be sure FDs are closed here to avoid reading files as root */
367 for (i = 3; i <= maxfd; i++) {
368 m_close(i);
371 execv(usershell, argv);
374 #ifdef DEBUG_TRACE
375 void printhex(const char * label, const unsigned char * buf, int len) {
377 int i;
379 fprintf(stderr, "%s\n", label);
380 for (i = 0; i < len; i++) {
381 fprintf(stderr, "%02x", buf[i]);
382 if (i % 16 == 15) {
383 fprintf(stderr, "\n");
385 else if (i % 2 == 1) {
386 fprintf(stderr, " ");
389 fprintf(stderr, "\n");
392 void printmpint(const char *label, mp_int *mp) {
393 buffer *buf = buf_new(1000);
394 buf_putmpint(buf, mp);
395 printhex(label, buf->data, buf->len);
396 buf_free(buf);
399 #endif
401 /* Strip all control characters from text (a null-terminated string), except
402 * for '\n', '\r' and '\t'.
403 * The result returned is a newly allocated string, this must be free()d after
404 * use */
405 char * stripcontrol(const char * text) {
407 char * ret;
408 int len, pos;
409 int i;
411 len = strlen(text);
412 ret = m_malloc(len+1);
414 pos = 0;
415 for (i = 0; i < len; i++) {
416 if ((text[i] <= '~' && text[i] >= ' ') /* normal printable range */
417 || text[i] == '\n' || text[i] == '\r' || text[i] == '\t') {
418 ret[pos] = text[i];
419 pos++;
422 ret[pos] = 0x0;
423 return ret;
427 /* reads the contents of filename into the buffer buf, from the current
428 * position, either to the end of the file, or the buffer being full.
429 * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
430 int buf_readfile(buffer* buf, const char* filename) {
432 int fd = -1;
433 int len;
434 int maxlen;
435 int ret = DROPBEAR_FAILURE;
437 fd = open(filename, O_RDONLY);
439 if (fd < 0) {
440 goto out;
443 do {
444 maxlen = buf->size - buf->pos;
445 len = read(fd, buf_getwriteptr(buf, maxlen), maxlen);
446 if (len < 0) {
447 if (errno == EINTR || errno == EAGAIN) {
448 continue;
450 goto out;
452 buf_incrwritepos(buf, len);
453 } while (len < maxlen && len > 0);
455 ret = DROPBEAR_SUCCESS;
457 out:
458 if (fd >= 0) {
459 m_close(fd);
461 return ret;
464 /* get a line from the file into buffer in the style expected for an
465 * authkeys file.
466 * Will return DROPBEAR_SUCCESS if data is read, or DROPBEAR_FAILURE on EOF.*/
467 /* Only used for ~/.ssh/known_hosts and ~/.ssh/authorized_keys */
468 #if defined(DROPBEAR_CLIENT) || defined(ENABLE_SVR_PUBKEY_AUTH)
469 int buf_getline(buffer * line, FILE * authfile) {
471 int c = EOF;
473 buf_setpos(line, 0);
474 buf_setlen(line, 0);
476 while (line->pos < line->size) {
478 c = fgetc(authfile); /*getc() is weird with some uClibc systems*/
479 if (c == EOF || c == '\n' || c == '\r') {
480 goto out;
483 buf_putbyte(line, (unsigned char)c);
486 TRACE(("leave getauthline: line too long"))
487 /* We return success, but the line length will be zeroed - ie we just
488 * ignore that line */
489 buf_setlen(line, 0);
491 out:
494 /* if we didn't read anything before EOF or error, exit */
495 if (c == EOF && line->pos == 0) {
496 return DROPBEAR_FAILURE;
497 } else {
498 buf_setpos(line, 0);
499 return DROPBEAR_SUCCESS;
503 #endif
505 /* make sure that the socket closes */
506 void m_close(int fd) {
507 int val;
509 if (fd == -1) {
510 return;
513 do {
514 val = close(fd);
515 } while (val < 0 && errno == EINTR);
517 if (val < 0 && errno != EBADF) {
518 /* Linux says EIO can happen */
519 dropbear_exit("Error closing fd %d, %s", fd, strerror(errno));
523 void * m_malloc(size_t size) {
525 void* ret;
527 if (size == 0) {
528 dropbear_exit("m_malloc failed");
530 ret = calloc(1, size);
531 if (ret == NULL) {
532 dropbear_exit("m_malloc failed");
534 return ret;
538 void * m_strdup(const char * str) {
539 char* ret;
541 ret = strdup(str);
542 if (ret == NULL) {
543 dropbear_exit("m_strdup failed");
545 return ret;
548 void * m_realloc(void* ptr, size_t size) {
550 void *ret;
552 if (size == 0) {
553 dropbear_exit("m_realloc failed");
555 ret = realloc(ptr, size);
556 if (ret == NULL) {
557 dropbear_exit("m_realloc failed");
559 return ret;
562 /* Clear the data, based on the method in David Wheeler's
563 * "Secure Programming for Linux and Unix HOWTO" */
564 /* Beware of calling this from within dbutil.c - things might get
565 * optimised away */
566 void m_burn(void *data, unsigned int len) {
567 volatile char *p = data;
569 if (data == NULL)
570 return;
571 while (len--) {
572 *p++ = 0x0;
577 void setnonblocking(int fd) {
579 TRACE(("setnonblocking: %d", fd))
581 if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
582 if (errno == ENODEV) {
583 /* Some devices (like /dev/null redirected in)
584 * can't be set to non-blocking */
585 TRACE(("ignoring ENODEV for setnonblocking"))
586 } else {
587 dropbear_exit("Couldn't set nonblocking");
590 TRACE(("leave setnonblocking"))
593 void disallow_core() {
594 struct rlimit lim;
595 lim.rlim_cur = lim.rlim_max = 0;
596 setrlimit(RLIMIT_CORE, &lim);
599 /* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE, with the result in *val */
600 int m_str_to_uint(const char* str, unsigned int *val) {
601 unsigned long l;
602 errno = 0;
603 l = strtoul(str, NULL, 10);
604 /* The c99 spec doesn't actually seem to define EINVAL, but most platforms
605 * I've looked at mention it in their manpage */
606 if ((l == 0 && errno == EINVAL)
607 || (l == ULONG_MAX && errno == ERANGE)
608 || (l > UINT_MAX)) {
609 return DROPBEAR_FAILURE;
610 } else {
611 *val = l;
612 return DROPBEAR_SUCCESS;
616 /* Returns malloced path. inpath beginning with '/' is returned as-is,
617 otherwise home directory is prepended */
618 char * expand_homedir_path(const char *inpath) {
619 struct passwd *pw = NULL;
620 if (inpath[0] != '/') {
621 pw = getpwuid(getuid());
622 if (pw && pw->pw_dir) {
623 int len = strlen(inpath) + strlen(pw->pw_dir) + 2;
624 char *buf = m_malloc(len);
625 snprintf(buf, len, "%s/%s", pw->pw_dir, inpath);
626 return buf;
630 /* Fallback */
631 return m_strdup(inpath);
634 int constant_time_memcmp(const void* a, const void *b, size_t n)
636 const char *xa = a, *xb = b;
637 uint8_t c = 0;
638 size_t i;
639 for (i = 0; i < n; i++)
641 c |= (xa[i] ^ xb[i]);
643 return c;
646 #if defined(__linux__) && defined(SYS_clock_gettime)
647 /* CLOCK_MONOTONIC_COARSE was added in Linux 2.6.32 but took a while to
648 reach userspace include headers */
649 #ifndef CLOCK_MONOTONIC_COARSE
650 #define CLOCK_MONOTONIC_COARSE 6
651 #endif
652 static clockid_t get_linux_clock_source() {
653 struct timespec ts;
654 if (syscall(SYS_clock_gettime, CLOCK_MONOTONIC_COARSE, &ts) == 0) {
655 return CLOCK_MONOTONIC_COARSE;
658 if (syscall(SYS_clock_gettime, CLOCK_MONOTONIC, &ts) == 0) {
659 return CLOCK_MONOTONIC;
661 return -1;
663 #endif
665 time_t monotonic_now() {
666 #if defined(__linux__) && defined(SYS_clock_gettime)
667 static clockid_t clock_source = -2;
669 if (clock_source == -2) {
670 /* First run, find out which one works.
671 -1 will fall back to time() */
672 clock_source = get_linux_clock_source();
675 if (clock_source >= 0) {
676 struct timespec ts;
677 if (syscall(SYS_clock_gettime, clock_source, &ts) != 0) {
678 /* Intermittent clock failures should not happen */
679 dropbear_exit("Clock broke");
681 return ts.tv_sec;
683 #endif /* linux clock_gettime */
685 #if defined(HAVE_MACH_ABSOLUTE_TIME)
686 /* OS X, see https://developer.apple.com/library/mac/qa/qa1398/_index.html */
687 static mach_timebase_info_data_t timebase_info;
688 if (timebase_info.denom == 0) {
689 mach_timebase_info(&timebase_info);
691 return mach_absolute_time() * timebase_info.numer / timebase_info.denom
692 / 1e9;
693 #endif /* osx mach_absolute_time */
695 /* Fallback for everything else - this will sometimes go backwards */
696 return time(NULL);