/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 | } |