/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 | 565 | void dropbear_exit(const char* format, ...) { |
106 | | |
107 | 565 | va_list param; |
108 | | |
109 | 565 | va_start(param, format); |
110 | 565 | _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 | 565 | va_list param) { |
116 | | |
117 | 565 | char fmtbuf[300]; |
118 | | |
119 | 565 | snprintf(fmtbuf, sizeof(fmtbuf), "Exited: %s", format); |
120 | | |
121 | 565 | _dropbear_log(LOG_INFO, fmtbuf, param); |
122 | | |
123 | 565 | #if DROPBEAR_FUZZ |
124 | 565 | if (fuzz.do_jmp) { |
125 | 565 | longjmp(fuzz.jmp, 1); |
126 | 565 | } |
127 | 0 | #endif |
128 | | |
129 | 0 | exit(exitcode); |
130 | 565 | } |
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 | 136 | void dropbear_log(int priority, const char* format, ...) { |
149 | | |
150 | 136 | va_list param; |
151 | | |
152 | 136 | va_start(param, format); |
153 | 136 | _dropbear_log(priority, format, param); |
154 | 136 | va_end(param); |
155 | 136 | } |
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 | 1 | void disallow_core() { |
602 | 1 | struct rlimit lim = {0}; |
603 | 1 | if (getrlimit(RLIMIT_CORE, &lim) < 0) { |
604 | 0 | TRACE(("getrlimit(RLIMIT_CORE) failed")); |
605 | 0 | } |
606 | 1 | lim.rlim_cur = 0; |
607 | 1 | if (setrlimit(RLIMIT_CORE, &lim) < 0) { |
608 | 0 | TRACE(("setrlimit(RLIMIT_CORE) failed")); |
609 | 0 | } |
610 | 1 | } |
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 | 0 | char * expand_homedir_path(const char *inpath) { |
639 | 0 | struct passwd *pw = NULL; |
640 | 0 | 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 | 0 | return m_strdup(inpath); |
660 | 0 | } |
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 | } |