restructure ash history buffer patch and make it a bit more secure
[busybox-power] / debian / patches / ash-history-buffer.patch
1 --- a/shell/ash.c
2 +++ b/shell/ash.c
3 @@ -182,6 +182,24 @@
4  //config:        This option recreates the prompt string from the environment
5  //config:        variable each time it is displayed.
6  //config:
7 +//config:config ASH_HIST_BUFFER
8 +//config:      bool "History buffer"
9 +//config:      default n
10 +//config:      depends on ASH && FEATURE_EDITING_SAVEHISTORY
11 +//config:      help
12 +//config:        Allows you to set a temporary location for .ash_history.
13 +//config:        History is saved to this custom location, and written out to
14 +//config:        its default location (~/.ash_history) upon shell exit.
15 +//config:        Useful to prevent wear on flash-based storage devices.
16 +//config:
17 +//config:config ASH_HIST_BUFFER_PATH
18 +//config:      string "History buffer location"
19 +//config:      default "/tmp"
20 +//config:      depends on ASH && ASH_HIST_BUFFER
21 +//config:      help
22 +//config:        Directory which will be used to save the shell history until
23 +//config:        ash is exited.
24 +//config:
25  
26  //usage:#define ash_trivial_usage NOUSAGE_STR
27  //usage:#define ash_full_usage ""
28 @@ -12810,6 +12828,14 @@ exitshell(void)
29         char *p;
30         int status;
31  
32 +#if ENABLE_ASH_HIST_BUFFER
33 +       const char *tmphistory = lookupvar("HISTFILE");
34 +       const char *storedhistory = lookupvar("STOREDHISTFILE");
35 +
36 +       if (storedhistory) /* is NULL when setting up the history buffer failed; check this before copying */
37 +               copy_file(tmphistory, storedhistory, FILEUTILS_FORCE | FILEUTILS_DEREFERENCE | FILEUTILS_PRESERVE_STATUS);
38 +#endif
39 +
40         status = exitstatus;
41         TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
42         if (setjmp(loc.loc)) {
43 @@ -13056,9 +13082,61 @@ int ash_main(int argc UNUSED_PARAM, char
44                 if (hp == NULL) {
45                         hp = lookupvar("HOME");
46                         if (hp != NULL) {
47 +#if ENABLE_ASH_HIST_BUFFER
48 +                               char *tmppath;
49 +                               char *tmphistory;
50 +                               char *storedhistory;
51 +                               const char *user = lookupvar("USER");
52 +
53 +                               tmppath = concat_path_file(CONFIG_ASH_HIST_BUFFER_PATH, user);
54 +                               tmphistory = concat_path_file(tmppath, ".ash_history");
55 +                               storedhistory = concat_path_file(hp, ".ash_history");
56 +
57 +                               if (access(tmphistory, R_OK | W_OK) == -1) {
58 +                                       if (access(CONFIG_ASH_HIST_BUFFER_PATH, R_OK == -1)) {
59 +                                               bb_simple_perror_msg("could not access history buffer path");
60 +                                               goto bail;
61 +                                       }
62 +                                       if (bb_make_directory(tmppath, 0700, FILEUTILS_RECUR)) {
63 +                                               /* bb_make_directory is noisy, no need for an additional error message here */
64 +                                               goto bail;
65 +                                       }
66 +                                       if (access(storedhistory, R_OK) == -1) {
67 +                                               creat(tmphistory, 0644);
68 +                                       } else {
69 +                                               copy_file(storedhistory, tmphistory, FILEUTILS_FORCE | FILEUTILS_DEREFERENCE | FILEUTILS_PRESERVE_STATUS);
70 +                                       }
71 +                               } else { /* (security) checks before reusing existing temporary history file */
72 +                                       struct stat stat_tmphistory;
73 +                                       lstat(tmphistory, &stat_tmphistory);
74 +                                       if (!S_ISREG(stat_tmphistory.st_mode)) {
75 +                                               errno = 0;
76 +                                               bb_simple_perror_msg("history buffer is not a regular file");
77 +                                               goto bail;
78 +                                       }
79 +                                       if (stat_tmphistory.st_uid != geteuid() || stat_tmphistory.st_gid != getegid()) {
80 +                                               errno = 0;
81 +                                               bb_simple_perror_msg("history buffer is not exclusive to the shell user");   
82 +                                               goto bail;
83 +                                       }
84 +                               }
85 +                               setvar("STOREDHISTFILE", storedhistory, 0);
86 +                               goto out;
87 +
88 + bail:
89 +                               errno = 0;
90 +                               bb_simple_perror_msg("shell history will not be saved");
91 +                               tmphistory = xasprintf("/dev/null");
92 + out:
93 +                               setvar("HISTFILE", tmphistory, 0);
94 +                               free(storedhistory);
95 +                               free(tmphistory);
96 +                               free(tmppath);
97 +#else
98                                 char *defhp = concat_path_file(hp, ".ash_history");
99                                 setvar("HISTFILE", defhp, 0);
100                                 free(defhp);
101 +#endif
102                         }
103                 }
104         }