diff options
author | Damien Miller <djm@mindrot.org> | 2006-01-31 21:49:27 +1100 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2006-01-31 21:49:27 +1100 |
commit | 3eec6b73a2c446225fce546d61d83cfc695fbaa0 (patch) | |
tree | 425fe13ba7b751c6d9878eb592e2d6a014a468bd | |
parent | b5dd55cccc7096d3db59378bba44920183f34110 (diff) |
- djm@cvs.openbsd.org 2006/01/31 10:19:02
[misc.c misc.h scp.c sftp.c]
fix local arbitrary command execution vulnerability on local/local and
remote/remote copies (CVE-2006-0225, bz #1094), patch by
t8m AT centrum.cz, polished by dtucker@ and myself; ok markus@
-rw-r--r-- | ChangeLog | 7 | ||||
-rw-r--r-- | misc.c | 45 | ||||
-rw-r--r-- | misc.h | 8 | ||||
-rw-r--r-- | scp.c | 132 | ||||
-rw-r--r-- | sftp.c | 8 |
5 files changed, 145 insertions, 55 deletions
@@ -28,6 +28,11 @@ | |||
28 | remove an incorrect sentence; | 28 | remove an incorrect sentence; |
29 | reported by roumen petrov; | 29 | reported by roumen petrov; |
30 | ok djm markus | 30 | ok djm markus |
31 | - djm@cvs.openbsd.org 2006/01/31 10:19:02 | ||
32 | [misc.c misc.h scp.c sftp.c] | ||
33 | fix local arbitrary command execution vulnerability on local/local and | ||
34 | remote/remote copies (CVE-2006-0225, bz #1094), patch by | ||
35 | t8m AT centrum.cz, polished by dtucker@ and myself; ok markus@ | ||
31 | 36 | ||
32 | 20060129 | 37 | 20060129 |
33 | - (dtucker) [configure.ac opensshd.init.in] Bug #1144: Use /bin/sh for the | 38 | - (dtucker) [configure.ac opensshd.init.in] Bug #1144: Use /bin/sh for the |
@@ -3753,4 +3758,4 @@ | |||
3753 | - (djm) Trim deprecated options from INSTALL. Mention UsePAM | 3758 | - (djm) Trim deprecated options from INSTALL. Mention UsePAM |
3754 | - (djm) Fix quote handling in sftp; Patch from admorten AT umich.edu | 3759 | - (djm) Fix quote handling in sftp; Patch from admorten AT umich.edu |
3755 | 3760 | ||
3756 | $Id: ChangeLog,v 1.4100 2006/01/31 10:47:58 djm Exp $ | 3761 | $Id: ChangeLog,v 1.4101 2006/01/31 10:49:27 djm Exp $ |
@@ -24,7 +24,7 @@ | |||
24 | */ | 24 | */ |
25 | 25 | ||
26 | #include "includes.h" | 26 | #include "includes.h" |
27 | RCSID("$OpenBSD: misc.c,v 1.41 2006/01/05 23:43:53 djm Exp $"); | 27 | RCSID("$OpenBSD: misc.c,v 1.42 2006/01/31 10:19:02 djm Exp $"); |
28 | 28 | ||
29 | #ifdef SSH_TUN_OPENBSD | 29 | #ifdef SSH_TUN_OPENBSD |
30 | #include <net/if.h> | 30 | #include <net/if.h> |
@@ -391,12 +391,15 @@ void | |||
391 | addargs(arglist *args, char *fmt, ...) | 391 | addargs(arglist *args, char *fmt, ...) |
392 | { | 392 | { |
393 | va_list ap; | 393 | va_list ap; |
394 | char buf[1024]; | 394 | char *cp; |
395 | u_int nalloc; | 395 | u_int nalloc; |
396 | int r; | ||
396 | 397 | ||
397 | va_start(ap, fmt); | 398 | va_start(ap, fmt); |
398 | vsnprintf(buf, sizeof(buf), fmt, ap); | 399 | r = vasprintf(&cp, fmt, ap); |
399 | va_end(ap); | 400 | va_end(ap); |
401 | if (r == -1) | ||
402 | fatal("addargs: argument too long"); | ||
400 | 403 | ||
401 | nalloc = args->nalloc; | 404 | nalloc = args->nalloc; |
402 | if (args->list == NULL) { | 405 | if (args->list == NULL) { |
@@ -407,10 +410,44 @@ addargs(arglist *args, char *fmt, ...) | |||
407 | 410 | ||
408 | args->list = xrealloc(args->list, nalloc * sizeof(char *)); | 411 | args->list = xrealloc(args->list, nalloc * sizeof(char *)); |
409 | args->nalloc = nalloc; | 412 | args->nalloc = nalloc; |
410 | args->list[args->num++] = xstrdup(buf); | 413 | args->list[args->num++] = cp; |
411 | args->list[args->num] = NULL; | 414 | args->list[args->num] = NULL; |
412 | } | 415 | } |
413 | 416 | ||
417 | void | ||
418 | replacearg(arglist *args, u_int which, char *fmt, ...) | ||
419 | { | ||
420 | va_list ap; | ||
421 | char *cp; | ||
422 | int r; | ||
423 | |||
424 | va_start(ap, fmt); | ||
425 | r = vasprintf(&cp, fmt, ap); | ||
426 | va_end(ap); | ||
427 | if (r == -1) | ||
428 | fatal("replacearg: argument too long"); | ||
429 | |||
430 | if (which >= args->num) | ||
431 | fatal("replacearg: tried to replace invalid arg %d >= %d", | ||
432 | which, args->num); | ||
433 | xfree(args->list[which]); | ||
434 | args->list[which] = cp; | ||
435 | } | ||
436 | |||
437 | void | ||
438 | freeargs(arglist *args) | ||
439 | { | ||
440 | u_int i; | ||
441 | |||
442 | if (args->list != NULL) { | ||
443 | for (i = 0; i < args->num; i++) | ||
444 | xfree(args->list[i]); | ||
445 | xfree(args->list); | ||
446 | args->nalloc = args->num = 0; | ||
447 | args->list = NULL; | ||
448 | } | ||
449 | } | ||
450 | |||
414 | /* | 451 | /* |
415 | * Expands tildes in the file name. Returns data allocated by xmalloc. | 452 | * Expands tildes in the file name. Returns data allocated by xmalloc. |
416 | * Warning: this calls getpw*. | 453 | * Warning: this calls getpw*. |
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: misc.h,v 1.28 2005/12/08 18:34:11 reyk Exp $ */ | 1 | /* $OpenBSD: misc.h,v 1.29 2006/01/31 10:19:02 djm Exp $ */ |
2 | 2 | ||
3 | /* | 3 | /* |
4 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 4 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
@@ -38,7 +38,11 @@ struct arglist { | |||
38 | u_int num; | 38 | u_int num; |
39 | u_int nalloc; | 39 | u_int nalloc; |
40 | }; | 40 | }; |
41 | void addargs(arglist *, char *, ...) __attribute__((format(printf, 2, 3))); | 41 | void addargs(arglist *, char *, ...) |
42 | __attribute__((format(printf, 2, 3))); | ||
43 | void replacearg(arglist *, u_int, char *, ...) | ||
44 | __attribute__((format(printf, 3, 4))); | ||
45 | void freeargs(arglist *); | ||
42 | 46 | ||
43 | /* readpass.c */ | 47 | /* readpass.c */ |
44 | 48 | ||
@@ -71,7 +71,7 @@ | |||
71 | */ | 71 | */ |
72 | 72 | ||
73 | #include "includes.h" | 73 | #include "includes.h" |
74 | RCSID("$OpenBSD: scp.c,v 1.128 2005/12/06 22:38:27 reyk Exp $"); | 74 | RCSID("$OpenBSD: scp.c,v 1.129 2006/01/31 10:19:02 djm Exp $"); |
75 | 75 | ||
76 | #include "xmalloc.h" | 76 | #include "xmalloc.h" |
77 | #include "atomicio.h" | 77 | #include "atomicio.h" |
@@ -118,6 +118,48 @@ killchild(int signo) | |||
118 | exit(1); | 118 | exit(1); |
119 | } | 119 | } |
120 | 120 | ||
121 | static int | ||
122 | do_local_cmd(arglist *a) | ||
123 | { | ||
124 | u_int i; | ||
125 | int status; | ||
126 | pid_t pid; | ||
127 | |||
128 | if (a->num == 0) | ||
129 | fatal("do_local_cmd: no arguments"); | ||
130 | |||
131 | if (verbose_mode) { | ||
132 | fprintf(stderr, "Executing:"); | ||
133 | for (i = 0; i < a->num; i++) | ||
134 | fprintf(stderr, " %s", a->list[i]); | ||
135 | fprintf(stderr, "\n"); | ||
136 | } | ||
137 | if ((pid = fork()) == -1) | ||
138 | fatal("do_local_cmd: fork: %s", strerror(errno)); | ||
139 | |||
140 | if (pid == 0) { | ||
141 | execvp(a->list[0], a->list); | ||
142 | perror(a->list[0]); | ||
143 | exit(1); | ||
144 | } | ||
145 | |||
146 | do_cmd_pid = pid; | ||
147 | signal(SIGTERM, killchild); | ||
148 | signal(SIGINT, killchild); | ||
149 | signal(SIGHUP, killchild); | ||
150 | |||
151 | while (waitpid(pid, &status, 0) == -1) | ||
152 | if (errno != EINTR) | ||
153 | fatal("do_local_cmd: waitpid: %s", strerror(errno)); | ||
154 | |||
155 | do_cmd_pid = -1; | ||
156 | |||
157 | if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) | ||
158 | return (-1); | ||
159 | |||
160 | return (0); | ||
161 | } | ||
162 | |||
121 | /* | 163 | /* |
122 | * This function executes the given command as the specified user on the | 164 | * This function executes the given command as the specified user on the |
123 | * given host. This returns < 0 if execution fails, and >= 0 otherwise. This | 165 | * given host. This returns < 0 if execution fails, and >= 0 otherwise. This |
@@ -162,7 +204,7 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc) | |||
162 | close(pin[0]); | 204 | close(pin[0]); |
163 | close(pout[1]); | 205 | close(pout[1]); |
164 | 206 | ||
165 | args.list[0] = ssh_program; | 207 | replacearg(&args, 0, "%s", ssh_program); |
166 | if (remuser != NULL) | 208 | if (remuser != NULL) |
167 | addargs(&args, "-l%s", remuser); | 209 | addargs(&args, "-l%s", remuser); |
168 | addargs(&args, "%s", host); | 210 | addargs(&args, "%s", host); |
@@ -227,8 +269,9 @@ main(int argc, char **argv) | |||
227 | 269 | ||
228 | __progname = ssh_get_progname(argv[0]); | 270 | __progname = ssh_get_progname(argv[0]); |
229 | 271 | ||
272 | memset(&args, '\0', sizeof(args)); | ||
230 | args.list = NULL; | 273 | args.list = NULL; |
231 | addargs(&args, "ssh"); /* overwritten with ssh_program */ | 274 | addargs(&args, "%s", ssh_program); |
232 | addargs(&args, "-x"); | 275 | addargs(&args, "-x"); |
233 | addargs(&args, "-oForwardAgent no"); | 276 | addargs(&args, "-oForwardAgent no"); |
234 | addargs(&args, "-oPermitLocalCommand no"); | 277 | addargs(&args, "-oPermitLocalCommand no"); |
@@ -368,6 +411,10 @@ toremote(char *targ, int argc, char **argv) | |||
368 | { | 411 | { |
369 | int i, len; | 412 | int i, len; |
370 | char *bp, *host, *src, *suser, *thost, *tuser, *arg; | 413 | char *bp, *host, *src, *suser, *thost, *tuser, *arg; |
414 | arglist alist; | ||
415 | |||
416 | memset(&alist, '\0', sizeof(alist)); | ||
417 | alist.list = NULL; | ||
371 | 418 | ||
372 | *targ++ = 0; | 419 | *targ++ = 0; |
373 | if (*targ == 0) | 420 | if (*targ == 0) |
@@ -385,56 +432,48 @@ toremote(char *targ, int argc, char **argv) | |||
385 | tuser = NULL; | 432 | tuser = NULL; |
386 | } | 433 | } |
387 | 434 | ||
435 | if (tuser != NULL && !okname(tuser)) { | ||
436 | xfree(arg); | ||
437 | return; | ||
438 | } | ||
439 | |||
388 | for (i = 0; i < argc - 1; i++) { | 440 | for (i = 0; i < argc - 1; i++) { |
389 | src = colon(argv[i]); | 441 | src = colon(argv[i]); |
390 | if (src) { /* remote to remote */ | 442 | if (src) { /* remote to remote */ |
391 | static char *ssh_options = | 443 | freeargs(&alist); |
392 | "-x -o'ClearAllForwardings yes'"; | 444 | addargs(&alist, "%s", ssh_program); |
445 | if (verbose_mode) | ||
446 | addargs(&alist, "-v"); | ||
447 | addargs(&alist, "-x"); | ||
448 | addargs(&alist, "-oClearAllForwardings yes"); | ||
449 | addargs(&alist, "-n"); | ||
450 | |||
393 | *src++ = 0; | 451 | *src++ = 0; |
394 | if (*src == 0) | 452 | if (*src == 0) |
395 | src = "."; | 453 | src = "."; |
396 | host = strrchr(argv[i], '@'); | 454 | host = strrchr(argv[i], '@'); |
397 | len = strlen(ssh_program) + strlen(argv[i]) + | 455 | |
398 | strlen(src) + (tuser ? strlen(tuser) : 0) + | ||
399 | strlen(thost) + strlen(targ) + | ||
400 | strlen(ssh_options) + CMDNEEDS + 20; | ||
401 | bp = xmalloc(len); | ||
402 | if (host) { | 456 | if (host) { |
403 | *host++ = 0; | 457 | *host++ = 0; |
404 | host = cleanhostname(host); | 458 | host = cleanhostname(host); |
405 | suser = argv[i]; | 459 | suser = argv[i]; |
406 | if (*suser == '\0') | 460 | if (*suser == '\0') |
407 | suser = pwd->pw_name; | 461 | suser = pwd->pw_name; |
408 | else if (!okname(suser)) { | 462 | else if (!okname(suser)) |
409 | xfree(bp); | ||
410 | continue; | 463 | continue; |
411 | } | 464 | addargs(&alist, "-l"); |
412 | if (tuser && !okname(tuser)) { | 465 | addargs(&alist, "%s", suser); |
413 | xfree(bp); | ||
414 | continue; | ||
415 | } | ||
416 | snprintf(bp, len, | ||
417 | "%s%s %s -n " | ||
418 | "-l %s %s %s %s '%s%s%s:%s'", | ||
419 | ssh_program, verbose_mode ? " -v" : "", | ||
420 | ssh_options, suser, host, cmd, src, | ||
421 | tuser ? tuser : "", tuser ? "@" : "", | ||
422 | thost, targ); | ||
423 | } else { | 466 | } else { |
424 | host = cleanhostname(argv[i]); | 467 | host = cleanhostname(argv[i]); |
425 | snprintf(bp, len, | ||
426 | "exec %s%s %s -n %s " | ||
427 | "%s %s '%s%s%s:%s'", | ||
428 | ssh_program, verbose_mode ? " -v" : "", | ||
429 | ssh_options, host, cmd, src, | ||
430 | tuser ? tuser : "", tuser ? "@" : "", | ||
431 | thost, targ); | ||
432 | } | 468 | } |
433 | if (verbose_mode) | 469 | addargs(&alist, "%s", host); |
434 | fprintf(stderr, "Executing: %s\n", bp); | 470 | addargs(&alist, "%s", cmd); |
435 | if (system(bp) != 0) | 471 | addargs(&alist, "%s", src); |
472 | addargs(&alist, "%s%s%s:%s", | ||
473 | tuser ? tuser : "", tuser ? "@" : "", | ||
474 | thost, targ); | ||
475 | if (do_local_cmd(&alist) != 0) | ||
436 | errs = 1; | 476 | errs = 1; |
437 | (void) xfree(bp); | ||
438 | } else { /* local to remote */ | 477 | } else { /* local to remote */ |
439 | if (remin == -1) { | 478 | if (remin == -1) { |
440 | len = strlen(targ) + CMDNEEDS + 20; | 479 | len = strlen(targ) + CMDNEEDS + 20; |
@@ -458,20 +497,23 @@ tolocal(int argc, char **argv) | |||
458 | { | 497 | { |
459 | int i, len; | 498 | int i, len; |
460 | char *bp, *host, *src, *suser; | 499 | char *bp, *host, *src, *suser; |
500 | arglist alist; | ||
501 | |||
502 | memset(&alist, '\0', sizeof(alist)); | ||
503 | alist.list = NULL; | ||
461 | 504 | ||
462 | for (i = 0; i < argc - 1; i++) { | 505 | for (i = 0; i < argc - 1; i++) { |
463 | if (!(src = colon(argv[i]))) { /* Local to local. */ | 506 | if (!(src = colon(argv[i]))) { /* Local to local. */ |
464 | len = strlen(_PATH_CP) + strlen(argv[i]) + | 507 | freeargs(&alist); |
465 | strlen(argv[argc - 1]) + 20; | 508 | addargs(&alist, "%s", _PATH_CP); |
466 | bp = xmalloc(len); | 509 | if (iamrecursive) |
467 | (void) snprintf(bp, len, "exec %s%s%s %s %s", _PATH_CP, | 510 | addargs(&alist, "-r"); |
468 | iamrecursive ? " -r" : "", pflag ? " -p" : "", | 511 | if (pflag) |
469 | argv[i], argv[argc - 1]); | 512 | addargs(&alist, "-p"); |
470 | if (verbose_mode) | 513 | addargs(&alist, "%s", argv[i]); |
471 | fprintf(stderr, "Executing: %s\n", bp); | 514 | addargs(&alist, "%s", argv[argc-1]); |
472 | if (system(bp)) | 515 | if (do_local_cmd(&alist)) |
473 | ++errs; | 516 | ++errs; |
474 | (void) xfree(bp); | ||
475 | continue; | 517 | continue; |
476 | } | 518 | } |
477 | *src++ = 0; | 519 | *src++ = 0; |
@@ -16,7 +16,7 @@ | |||
16 | 16 | ||
17 | #include "includes.h" | 17 | #include "includes.h" |
18 | 18 | ||
19 | RCSID("$OpenBSD: sftp.c,v 1.69 2005/12/06 22:38:27 reyk Exp $"); | 19 | RCSID("$OpenBSD: sftp.c,v 1.70 2006/01/31 10:19:02 djm Exp $"); |
20 | 20 | ||
21 | #ifdef USE_LIBEDIT | 21 | #ifdef USE_LIBEDIT |
22 | #include <histedit.h> | 22 | #include <histedit.h> |
@@ -1453,8 +1453,9 @@ main(int argc, char **argv) | |||
1453 | sanitise_stdfd(); | 1453 | sanitise_stdfd(); |
1454 | 1454 | ||
1455 | __progname = ssh_get_progname(argv[0]); | 1455 | __progname = ssh_get_progname(argv[0]); |
1456 | memset(&args, '\0', sizeof(args)); | ||
1456 | args.list = NULL; | 1457 | args.list = NULL; |
1457 | addargs(&args, "ssh"); /* overwritten with ssh_program */ | 1458 | addargs(&args, ssh_program); |
1458 | addargs(&args, "-oForwardX11 no"); | 1459 | addargs(&args, "-oForwardX11 no"); |
1459 | addargs(&args, "-oForwardAgent no"); | 1460 | addargs(&args, "-oForwardAgent no"); |
1460 | addargs(&args, "-oPermitLocalCommand no"); | 1461 | addargs(&args, "-oPermitLocalCommand no"); |
@@ -1489,6 +1490,7 @@ main(int argc, char **argv) | |||
1489 | break; | 1490 | break; |
1490 | case 'S': | 1491 | case 'S': |
1491 | ssh_program = optarg; | 1492 | ssh_program = optarg; |
1493 | replacearg(&args, 0, "%s", ssh_program); | ||
1492 | break; | 1494 | break; |
1493 | case 'b': | 1495 | case 'b': |
1494 | if (batchmode) | 1496 | if (batchmode) |
@@ -1565,7 +1567,6 @@ main(int argc, char **argv) | |||
1565 | addargs(&args, "%s", host); | 1567 | addargs(&args, "%s", host); |
1566 | addargs(&args, "%s", (sftp_server != NULL ? | 1568 | addargs(&args, "%s", (sftp_server != NULL ? |
1567 | sftp_server : "sftp")); | 1569 | sftp_server : "sftp")); |
1568 | args.list[0] = ssh_program; | ||
1569 | 1570 | ||
1570 | if (!batchmode) | 1571 | if (!batchmode) |
1571 | fprintf(stderr, "Connecting to %s...\n", host); | 1572 | fprintf(stderr, "Connecting to %s...\n", host); |
@@ -1578,6 +1579,7 @@ main(int argc, char **argv) | |||
1578 | fprintf(stderr, "Attaching to %s...\n", sftp_direct); | 1579 | fprintf(stderr, "Attaching to %s...\n", sftp_direct); |
1579 | connect_to_server(sftp_direct, args.list, &in, &out); | 1580 | connect_to_server(sftp_direct, args.list, &in, &out); |
1580 | } | 1581 | } |
1582 | freeargs(&args); | ||
1581 | 1583 | ||
1582 | err = interactive_loop(in, out, file1, file2); | 1584 | err = interactive_loop(in, out, file1, file2); |
1583 | 1585 | ||