diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2014-06-27 12:24:39 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2014-06-27 12:24:39 +0200 |
commit | a6ae999b3b30ad522272325bac4c69b153150108 (patch) | |
tree | 0af61697af6661c9d45c28025f2404c522436278 | |
parent | 5f8daefb835687e428215f90d26fdf1f0206149d (diff) | |
download | busybox-w32-a6ae999b3b30ad522272325bac4c69b153150108.tar.gz busybox-w32-a6ae999b3b30ad522272325bac4c69b153150108.tar.bz2 busybox-w32-a6ae999b3b30ad522272325bac4c69b153150108.zip |
ftpd: escape chroot prior to re-executing ls helper
When we merely chdir to saved "real" root fd,
exec("proc/self/exe") works for static executables but not
for dynamic ones (they can't find their interpreter).
With this patch, we also *chroot* to real root.
As a bonus, this gives us proper usernames, timezone conversion
etc.
function old new delta
popen_ls 203 259 +56
ftpd_main 2362 2366 +4
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | networking/ftpd.c | 53 |
1 files changed, 21 insertions, 32 deletions
diff --git a/networking/ftpd.c b/networking/ftpd.c index e724734df..839a85d73 100644 --- a/networking/ftpd.c +++ b/networking/ftpd.c | |||
@@ -620,16 +620,12 @@ popen_ls(const char *opt) | |||
620 | const char *argv[5]; | 620 | const char *argv[5]; |
621 | struct fd_pair outfd; | 621 | struct fd_pair outfd; |
622 | pid_t pid; | 622 | pid_t pid; |
623 | 623 | #if !BB_MMU | |
624 | int cur_fd = xopen(".", O_RDONLY | O_DIRECTORY); | ||
625 | #endif | ||
624 | argv[0] = "ftpd"; | 626 | argv[0] = "ftpd"; |
625 | argv[1] = opt; /* "-l" or "-1" */ | 627 | argv[1] = opt; /* "-l" or "-1" */ |
626 | #if BB_MMU | ||
627 | argv[2] = "--"; | 628 | argv[2] = "--"; |
628 | #else | ||
629 | /* NOMMU ftpd ls helper chdirs to argv[2], | ||
630 | * preventing peer from seeing real root. */ | ||
631 | argv[2] = xrealloc_getcwd_or_warn(NULL); | ||
632 | #endif | ||
633 | argv[3] = G.ftp_arg; | 629 | argv[3] = G.ftp_arg; |
634 | argv[4] = NULL; | 630 | argv[4] = NULL; |
635 | 631 | ||
@@ -651,16 +647,6 @@ popen_ls(const char *opt) | |||
651 | pid = BB_MMU ? xfork() : xvfork(); | 647 | pid = BB_MMU ? xfork() : xvfork(); |
652 | if (pid == 0) { | 648 | if (pid == 0) { |
653 | /* child */ | 649 | /* child */ |
654 | #if !BB_MMU | ||
655 | /* On NOMMU, we want to execute a child - copy of ourself. | ||
656 | * In chroot we usually can't do it. Thus we chdir | ||
657 | * out of the chroot back to original root, | ||
658 | * and (see later below) execute bb_busybox_exec_path | ||
659 | * relative to current directory */ | ||
660 | if (fchdir(G.root_fd) != 0) | ||
661 | _exit(127); | ||
662 | /*close(G.root_fd); - close_on_exec_on() took care of this */ | ||
663 | #endif | ||
664 | /* NB: close _first_, then move fd! */ | 650 | /* NB: close _first_, then move fd! */ |
665 | close(outfd.rd); | 651 | close(outfd.rd); |
666 | xmove_fd(outfd.wr, STDOUT_FILENO); | 652 | xmove_fd(outfd.wr, STDOUT_FILENO); |
@@ -674,19 +660,25 @@ popen_ls(const char *opt) | |||
674 | /* memset(&G, 0, sizeof(G)); - ls_main does it */ | 660 | /* memset(&G, 0, sizeof(G)); - ls_main does it */ |
675 | exit(ls_main(ARRAY_SIZE(argv) - 1, (char**) argv)); | 661 | exit(ls_main(ARRAY_SIZE(argv) - 1, (char**) argv)); |
676 | #else | 662 | #else |
677 | /* + 1: we must use relative path here if in chroot. | 663 | /* On NOMMU, we want to execute a child - copy of ourself |
678 | * For example, execv("/proc/self/exe") will fail, since | 664 | * in order to unblock parent after vfork. |
679 | * it looks for "/proc/self/exe" _relative to chroot!_ */ | 665 | * In chroot we usually can't re-exec. Thus we escape |
680 | execv(bb_busybox_exec_path + 1, (char**) argv); | 666 | * out of the chroot back to original root. |
667 | */ | ||
668 | if (G.root_fd >= 0) { | ||
669 | if (fchdir(G.root_fd) != 0 || chroot(".") != 0) | ||
670 | _exit(127); | ||
671 | /*close(G.root_fd); - close_on_exec_on() took care of this */ | ||
672 | } | ||
673 | /* Child expects directory to list on fd #3 */ | ||
674 | xmove_fd(cur_fd, 3); | ||
675 | execv(bb_busybox_exec_path, (char**) argv); | ||
681 | _exit(127); | 676 | _exit(127); |
682 | #endif | 677 | #endif |
683 | } | 678 | } |
684 | 679 | ||
685 | /* parent */ | 680 | /* parent */ |
686 | close(outfd.wr); | 681 | close(outfd.wr); |
687 | #if !BB_MMU | ||
688 | free((char*)argv[2]); | ||
689 | #endif | ||
690 | return outfd.rd; | 682 | return outfd.rd; |
691 | } | 683 | } |
692 | 684 | ||
@@ -705,8 +697,6 @@ handle_dir_common(int opts) | |||
705 | if (!(opts & USE_CTRL_CONN) && !port_or_pasv_was_seen()) | 697 | if (!(opts & USE_CTRL_CONN) && !port_or_pasv_was_seen()) |
706 | return; /* port_or_pasv_was_seen emitted error response */ | 698 | return; /* port_or_pasv_was_seen emitted error response */ |
707 | 699 | ||
708 | /* -n prevents user/groupname display, | ||
709 | * which can be problematic in chroot */ | ||
710 | ls_fd = popen_ls((opts & LONG_LISTING) ? "-l" : "-1"); | 700 | ls_fd = popen_ls((opts & LONG_LISTING) ? "-l" : "-1"); |
711 | ls_fp = xfdopen_for_read(ls_fd); | 701 | ls_fp = xfdopen_for_read(ls_fd); |
712 | /* FIXME: filenames with embedded newlines are mishandled */ | 702 | /* FIXME: filenames with embedded newlines are mishandled */ |
@@ -1139,11 +1129,10 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv) | |||
1139 | opts = getopt32(argv, "l1vS" IF_FEATURE_FTP_WRITE("w") "t:T:", &G.timeout, &abs_timeout, &G.verbose, &verbose_S); | 1129 | opts = getopt32(argv, "l1vS" IF_FEATURE_FTP_WRITE("w") "t:T:", &G.timeout, &abs_timeout, &G.verbose, &verbose_S); |
1140 | if (opts & (OPT_l|OPT_1)) { | 1130 | if (opts & (OPT_l|OPT_1)) { |
1141 | /* Our secret backdoor to ls */ | 1131 | /* Our secret backdoor to ls */ |
1142 | /* TODO: pass -n? It prevents user/group resolution, which may not work in chroot anyway */ | ||
1143 | /* TODO: pass -A? It shows dot files */ | 1132 | /* TODO: pass -A? It shows dot files */ |
1144 | /* TODO: pass --group-directories-first? would be nice, but ls doesn't do that yet */ | 1133 | /* TODO: pass --group-directories-first? would be nice, but ls doesn't do that yet */ |
1145 | xchdir(argv[2]); | 1134 | if (fchdir(3) != 0) |
1146 | argv[2] = (char*)"--"; | 1135 | _exit(127); |
1147 | /* memset(&G, 0, sizeof(G)); - ls_main does it */ | 1136 | /* memset(&G, 0, sizeof(G)); - ls_main does it */ |
1148 | return ls_main(argc, argv); | 1137 | return ls_main(argc, argv); |
1149 | } | 1138 | } |
@@ -1185,9 +1174,9 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv) | |||
1185 | G.root_fd = xopen("/", O_RDONLY | O_DIRECTORY); | 1174 | G.root_fd = xopen("/", O_RDONLY | O_DIRECTORY); |
1186 | close_on_exec_on(G.root_fd); | 1175 | close_on_exec_on(G.root_fd); |
1187 | #endif | 1176 | #endif |
1188 | 1177 | argv += optind; | |
1189 | if (argv[optind]) { | 1178 | if (argv[0]) { |
1190 | xchroot(argv[optind]); | 1179 | xchroot(argv[0]); |
1191 | } | 1180 | } |
1192 | 1181 | ||
1193 | //umask(077); - admin can set umask before starting us | 1182 | //umask(077); - admin can set umask before starting us |