aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2016-08-20 15:58:34 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2016-08-20 15:58:34 +0200
commit7b25b1c5b2794a499c8ae99db75830a6d564561e (patch)
treec136ae68fd879d80277eebac0ef7686b749181df
parent869994cf4f9647fdfb519a1945f8582e71d3df3d (diff)
downloadbusybox-w32-7b25b1c5b2794a499c8ae99db75830a6d564561e.tar.gz
busybox-w32-7b25b1c5b2794a499c8ae99db75830a6d564561e.tar.bz2
busybox-w32-7b25b1c5b2794a499c8ae99db75830a6d564561e.zip
hush: do not leak script fds into NOEXEC children
We set all opened script fds to CLOEXEC, thus making then go away after fork+exec. Unfortunately, CLOFORK does not exist. NOEXEC children will still see those fds open. For one, "ls" applet is NOEXEC. Therefore running "ls -l /proc/self/fd" in a script from standalone shell shows this: lrwx------ 1 root root 64 Aug 20 15:17 0 -> /dev/pts/3 lrwx------ 1 root root 64 Aug 20 15:17 1 -> /dev/pts/3 lrwx------ 1 root root 64 Aug 20 15:17 2 -> /dev/pts/3 lr-x------ 1 root root 64 Aug 20 15:17 3 -> /path/to/top/level/script lr-x------ 1 root root 64 Aug 20 15:17 4 -> /path/to/sourced/SCRIPT1 ... with as many open fds as there are ". SCRIPTn" nest levels. Fix it by closing these fds after fork (only for NOEXEC children). Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--shell/hush.c78
1 files changed, 68 insertions, 10 deletions
diff --git a/shell/hush.c b/shell/hush.c
index 9b45b312f..6b0d85039 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -711,6 +711,13 @@ enum {
711}; 711};
712 712
713 713
714/* Can be extended to handle a FIXME in setup_redirects about not saving script fds */
715struct FILE_list {
716 struct FILE_list *next;
717 FILE *fp;
718};
719
720
714/* "Globals" within this file */ 721/* "Globals" within this file */
715/* Sorted roughly by size (smaller offsets == smaller code) */ 722/* Sorted roughly by size (smaller offsets == smaller code) */
716struct globals { 723struct globals {
@@ -802,6 +809,9 @@ struct globals {
802 unsigned handled_SIGCHLD; 809 unsigned handled_SIGCHLD;
803 smallint we_have_children; 810 smallint we_have_children;
804#endif 811#endif
812#if ENABLE_FEATURE_SH_STANDALONE
813 struct FILE_list *FILE_list;
814#endif
805 /* Which signals have non-DFL handler (even with no traps set)? 815 /* Which signals have non-DFL handler (even with no traps set)?
806 * Set at the start to: 816 * Set at the start to:
807 * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS) 817 * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS)
@@ -1252,6 +1262,54 @@ static void free_strings(char **strings)
1252} 1262}
1253 1263
1254 1264
1265/* Manipulating the list of open FILEs */
1266static FILE *remember_FILE(FILE *fp)
1267{
1268 if (fp) {
1269#if ENABLE_FEATURE_SH_STANDALONE
1270 struct FILE_list *n = xmalloc(sizeof(*n));
1271 n->fp = fp;
1272 n->next = G.FILE_list;
1273 G.FILE_list = n;
1274#endif
1275 close_on_exec_on(fileno(fp));
1276 }
1277 return fp;
1278}
1279#if ENABLE_FEATURE_SH_STANDALONE
1280static void close_all_FILE_list(void)
1281{
1282 struct FILE_list *fl = G.FILE_list;
1283 while (fl) {
1284 /* fclose would also free FILE object.
1285 * It is disastrous if we share memory with a vforked parent.
1286 * I'm not sure we never come here after vfork.
1287 * Therefore just close fd, nothing more.
1288 */
1289 /*fclose(fl->fp); - unsafe */
1290 close(fileno(fl->fp));
1291 fl = fl->next;
1292 }
1293}
1294static void fclose_and_forget(FILE *fp)
1295{
1296 struct FILE_list **pp = &G.FILE_list;
1297 while (*pp) {
1298 struct FILE_list *cur = *pp;
1299 if (cur->fp == fp) {
1300 *pp = cur->next;
1301 free(cur);
1302 break;
1303 }
1304 pp = &cur->next;
1305 }
1306 fclose(fp);
1307}
1308#else
1309# define fclose_and_forget(fp) fclose(fp);
1310#endif
1311
1312
1255/* Helpers for setting new $n and restoring them back 1313/* Helpers for setting new $n and restoring them back
1256 */ 1314 */
1257typedef struct save_arg_t { 1315typedef struct save_arg_t {
@@ -5968,8 +6026,7 @@ static FILE *generate_stream_from_string(const char *s, pid_t *pid_p)
5968 free(to_free); 6026 free(to_free);
5969# endif 6027# endif
5970 close(channel[1]); 6028 close(channel[1]);
5971 close_on_exec_on(channel[0]); 6029 return remember_FILE(xfdopen_for_read(channel[0]));
5972 return xfdopen_for_read(channel[0]);
5973} 6030}
5974 6031
5975/* Return code is exit status of the process that is run. */ 6032/* Return code is exit status of the process that is run. */
@@ -5998,7 +6055,7 @@ static int process_command_subs(o_string *dest, const char *s)
5998 } 6055 }
5999 6056
6000 debug_printf("done reading from `cmd` pipe, closing it\n"); 6057 debug_printf("done reading from `cmd` pipe, closing it\n");
6001 fclose(fp); 6058 fclose_and_forget(fp);
6002 /* We need to extract exitcode. Test case 6059 /* We need to extract exitcode. Test case
6003 * "true; echo `sleep 1; false` $?" 6060 * "true; echo `sleep 1; false` $?"
6004 * should print 1 */ 6061 * should print 1 */
@@ -6584,6 +6641,8 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
6584 if (a >= 0) { 6641 if (a >= 0) {
6585# if BB_MMU /* see above why on NOMMU it is not allowed */ 6642# if BB_MMU /* see above why on NOMMU it is not allowed */
6586 if (APPLET_IS_NOEXEC(a)) { 6643 if (APPLET_IS_NOEXEC(a)) {
6644 /* Do not leak open fds from opened script files etc */
6645 close_all_FILE_list();
6587 debug_printf_exec("running applet '%s'\n", argv[0]); 6646 debug_printf_exec("running applet '%s'\n", argv[0]);
6588 run_applet_no_and_exit(a, argv); 6647 run_applet_no_and_exit(a, argv);
6589 } 6648 }
@@ -8086,10 +8145,10 @@ int hush_main(int argc, char **argv)
8086 debug_printf("sourcing /etc/profile\n"); 8145 debug_printf("sourcing /etc/profile\n");
8087 input = fopen_for_read("/etc/profile"); 8146 input = fopen_for_read("/etc/profile");
8088 if (input != NULL) { 8147 if (input != NULL) {
8089 close_on_exec_on(fileno(input)); 8148 remember_FILE(input);
8090 install_special_sighandlers(); 8149 install_special_sighandlers();
8091 parse_and_run_file(input); 8150 parse_and_run_file(input);
8092 fclose(input); 8151 fclose_and_forget(input);
8093 } 8152 }
8094 /* bash: after sourcing /etc/profile, 8153 /* bash: after sourcing /etc/profile,
8095 * tries to source (in the given order): 8154 * tries to source (in the given order):
@@ -8111,11 +8170,11 @@ int hush_main(int argc, char **argv)
8111 G.global_argv++; 8170 G.global_argv++;
8112 debug_printf("running script '%s'\n", G.global_argv[0]); 8171 debug_printf("running script '%s'\n", G.global_argv[0]);
8113 input = xfopen_for_read(G.global_argv[0]); 8172 input = xfopen_for_read(G.global_argv[0]);
8114 close_on_exec_on(fileno(input)); 8173 remember_FILE(input);
8115 install_special_sighandlers(); 8174 install_special_sighandlers();
8116 parse_and_run_file(input); 8175 parse_and_run_file(input);
8117#if ENABLE_FEATURE_CLEAN_UP 8176#if ENABLE_FEATURE_CLEAN_UP
8118 fclose(input); 8177 fclose_and_forget(input);
8119#endif 8178#endif
8120 goto final_return; 8179 goto final_return;
8121 } 8180 }
@@ -8983,7 +9042,7 @@ static int FAST_FUNC builtin_source(char **argv)
8983 if (arg_path) 9042 if (arg_path)
8984 filename = arg_path; 9043 filename = arg_path;
8985 } 9044 }
8986 input = fopen_or_warn(filename, "r"); 9045 input = remember_FILE(fopen_or_warn(filename, "r"));
8987 free(arg_path); 9046 free(arg_path);
8988 if (!input) { 9047 if (!input) {
8989 /* bb_perror_msg("%s", *argv); - done by fopen_or_warn */ 9048 /* bb_perror_msg("%s", *argv); - done by fopen_or_warn */
@@ -8992,7 +9051,6 @@ static int FAST_FUNC builtin_source(char **argv)
8992 */ 9051 */
8993 return EXIT_FAILURE; 9052 return EXIT_FAILURE;
8994 } 9053 }
8995 close_on_exec_on(fileno(input));
8996 9054
8997#if ENABLE_HUSH_FUNCTIONS 9055#if ENABLE_HUSH_FUNCTIONS
8998 sv_flg = G.flag_return_in_progress; 9056 sv_flg = G.flag_return_in_progress;
@@ -9003,7 +9061,7 @@ static int FAST_FUNC builtin_source(char **argv)
9003 save_and_replace_G_args(&sv, argv); 9061 save_and_replace_G_args(&sv, argv);
9004 9062
9005 parse_and_run_file(input); 9063 parse_and_run_file(input);
9006 fclose(input); 9064 fclose_and_forget(input);
9007 9065
9008 if (argv[1]) 9066 if (argv[1])
9009 restore_G_args(&sv, argv); 9067 restore_G_args(&sv, argv);