Andrew Geissler | c3d88e4 | 2020-10-02 09:45:00 -0500 | [diff] [blame^] | 1 | From 951bdaad7a18cc0dc1036bba86b18b90874d39ff Mon Sep 17 00:00:00 2001 |
| 2 | From: Chet Ramey <chet.ramey@case.edu> |
| 3 | Date: Mon, 1 Jul 2019 09:03:53 -0400 |
| 4 | Subject: [PATCH] commit bash-20190628 snapshot |
| 5 | |
| 6 | An issue was discovered in disable_priv_mode in shell.c in GNU Bash through 5.0 patch 11. |
| 7 | By default, if Bash is run with its effective UID not equal to its real UID, |
| 8 | it will drop privileges by setting its effective UID to its real UID. |
| 9 | However, it does so incorrectly. On Linux and other systems that support "saved UID" functionality, |
| 10 | the saved UID is not dropped. An attacker with command execution in the shell can use "enable -f" for |
| 11 | runtime loading of a new builtin, which can be a shared object that calls setuid() and therefore |
| 12 | regains privileges. However, binaries running with an effective UID of 0 are unaffected. |
| 13 | |
| 14 | Get the patch from [1] to fix the issue. |
| 15 | |
| 16 | Upstream-Status: Inappropriate [the upstream thinks it doesn't increase the credibility of CVEs in general] |
| 17 | CVE: CVE-2019-18276 |
| 18 | |
| 19 | [1] https://git.savannah.gnu.org/cgit/bash.git/commit/?h=devel&id=951bdaa |
| 20 | |
| 21 | Signed-off-by: De Huo <De.Huo@windriver.com> |
| 22 | Signed-off-by: Kai Kang <kai.kang@windriver.com> |
| 23 | Signed-off-by: Mingli Yu <mingli.yu@windriver.com> |
| 24 | --- |
| 25 | MANIFEST | 2 ++ |
| 26 | bashline.c | 50 +------------------------------------------------- |
| 27 | builtins/help.def | 2 +- |
| 28 | config.h.in | 10 +++++++++- |
| 29 | configure.ac | 1 + |
| 30 | doc/bash.1 | 3 ++- |
| 31 | doc/bashref.texi | 3 ++- |
| 32 | lib/glob/glob.c | 5 ++++- |
| 33 | pathexp.c | 16 ++++++++++++++-- |
| 34 | shell.c | 8 ++++++++ |
| 35 | tests/glob.tests | 2 ++ |
| 36 | tests/glob6.sub | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| 37 | tests/glob7.sub | 11 +++++++++++ |
| 38 | 14 files changed, 122 insertions(+), 56 deletions(-) |
| 39 | create mode 100644 tests/glob6.sub |
| 40 | create mode 100644 tests/glob7.sub |
| 41 | |
| 42 | diff --git a/MANIFEST b/MANIFEST |
| 43 | index 03de221..f9ccad7 100644 |
| 44 | --- a/MANIFEST |
| 45 | +++ b/MANIFEST |
| 46 | @@ -1037,6 +1037,8 @@ tests/extglob3.tests f |
| 47 | tests/extglob3.right f |
| 48 | tests/extglob4.sub f |
| 49 | tests/extglob5.sub f |
| 50 | +tests/glob6.sub f |
| 51 | +tests/glob7.sub f |
| 52 | tests/func.tests f |
| 53 | tests/func.right f |
| 54 | tests/func1.sub f |
| 55 | diff --git a/bashline.c b/bashline.c |
| 56 | index 824ea9d..d86b47d 100644 |
| 57 | --- a/bashline.c |
| 58 | +++ b/bashline.c |
| 59 | @@ -3718,55 +3718,7 @@ static int |
| 60 | completion_glob_pattern (string) |
| 61 | char *string; |
| 62 | { |
| 63 | - register int c; |
| 64 | - char *send; |
| 65 | - int open; |
| 66 | - |
| 67 | - DECLARE_MBSTATE; |
| 68 | - |
| 69 | - open = 0; |
| 70 | - send = string + strlen (string); |
| 71 | - |
| 72 | - while (c = *string++) |
| 73 | - { |
| 74 | - switch (c) |
| 75 | - { |
| 76 | - case '?': |
| 77 | - case '*': |
| 78 | - return (1); |
| 79 | - |
| 80 | - case '[': |
| 81 | - open++; |
| 82 | - continue; |
| 83 | - |
| 84 | - case ']': |
| 85 | - if (open) |
| 86 | - return (1); |
| 87 | - continue; |
| 88 | - |
| 89 | - case '+': |
| 90 | - case '@': |
| 91 | - case '!': |
| 92 | - if (*string == '(') /*)*/ |
| 93 | - return (1); |
| 94 | - continue; |
| 95 | - |
| 96 | - case '\\': |
| 97 | - if (*string++ == 0) |
| 98 | - return (0); |
| 99 | - } |
| 100 | - |
| 101 | - /* Advance one fewer byte than an entire multibyte character to |
| 102 | - account for the auto-increment in the loop above. */ |
| 103 | -#ifdef HANDLE_MULTIBYTE |
| 104 | - string--; |
| 105 | - ADVANCE_CHAR_P (string, send - string); |
| 106 | - string++; |
| 107 | -#else |
| 108 | - ADVANCE_CHAR_P (string, send - string); |
| 109 | -#endif |
| 110 | - } |
| 111 | - return (0); |
| 112 | + return (glob_pattern_p (string) == 1); |
| 113 | } |
| 114 | |
| 115 | static char *globtext; |
| 116 | diff --git a/builtins/help.def b/builtins/help.def |
| 117 | index 006c4b5..92f9b38 100644 |
| 118 | --- a/builtins/help.def |
| 119 | +++ b/builtins/help.def |
| 120 | @@ -128,7 +128,7 @@ help_builtin (list) |
| 121 | |
| 122 | /* We should consider making `help bash' do something. */ |
| 123 | |
| 124 | - if (glob_pattern_p (list->word->word)) |
| 125 | + if (glob_pattern_p (list->word->word) == 1) |
| 126 | { |
| 127 | printf ("%s", ngettext ("Shell commands matching keyword `", "Shell commands matching keywords `", (list->next ? 2 : 1))); |
| 128 | print_word_list (list, ", "); |
| 129 | diff --git a/config.h.in b/config.h.in |
| 130 | index 8554aec..ad4b1e8 100644 |
| 131 | --- a/config.h.in |
| 132 | +++ b/config.h.in |
| 133 | @@ -1,6 +1,6 @@ |
| 134 | /* config.h -- Configuration file for bash. */ |
| 135 | |
| 136 | -/* Copyright (C) 1987-2009,2011-2012 Free Software Foundation, Inc. |
| 137 | +/* Copyright (C) 1987-2009,2011-2012,2013-2019 Free Software Foundation, Inc. |
| 138 | |
| 139 | This file is part of GNU Bash, the Bourne Again SHell. |
| 140 | |
| 141 | @@ -807,6 +807,14 @@ |
| 142 | #undef HAVE_SETREGID |
| 143 | #undef HAVE_DECL_SETREGID |
| 144 | |
| 145 | +/* Define if you have the setregid function. */ |
| 146 | +#undef HAVE_SETRESGID |
| 147 | +#undef HAVE_DECL_SETRESGID |
| 148 | + |
| 149 | +/* Define if you have the setresuid function. */ |
| 150 | +#undef HAVE_SETRESUID |
| 151 | +#undef HAVE_DECL_SETRESUID |
| 152 | + |
| 153 | /* Define if you have the setvbuf function. */ |
| 154 | #undef HAVE_SETVBUF |
| 155 | |
| 156 | diff --git a/configure.ac b/configure.ac |
| 157 | index 52b4cdb..549adef 100644 |
| 158 | --- a/configure.ac |
| 159 | +++ b/configure.ac |
| 160 | @@ -810,6 +810,7 @@ AC_CHECK_DECLS([confstr]) |
| 161 | AC_CHECK_DECLS([printf]) |
| 162 | AC_CHECK_DECLS([sbrk]) |
| 163 | AC_CHECK_DECLS([setregid]) |
| 164 | +AC_CHECK_DECLS[(setresuid, setresgid]) |
| 165 | AC_CHECK_DECLS([strcpy]) |
| 166 | AC_CHECK_DECLS([strsignal]) |
| 167 | |
| 168 | diff --git a/doc/bash.1 b/doc/bash.1 |
| 169 | index e6cd08d..9e58a0b 100644 |
| 170 | --- a/doc/bash.1 |
| 171 | +++ b/doc/bash.1 |
| 172 | @@ -4681,7 +4681,8 @@ above). |
| 173 | .PD |
| 174 | .SH "SIMPLE COMMAND EXPANSION" |
| 175 | When a simple command is executed, the shell performs the following |
| 176 | -expansions, assignments, and redirections, from left to right. |
| 177 | +expansions, assignments, and redirections, from left to right, in |
| 178 | +the following order. |
| 179 | .IP 1. |
| 180 | The words that the parser has marked as variable assignments (those |
| 181 | preceding the command name) and redirections are saved for later |
| 182 | diff --git a/doc/bashref.texi b/doc/bashref.texi |
| 183 | index d33cd57..3065126 100644 |
| 184 | --- a/doc/bashref.texi |
| 185 | +++ b/doc/bashref.texi |
| 186 | @@ -2964,7 +2964,8 @@ is not specified. If the file does not exist, it is created. |
| 187 | @cindex command expansion |
| 188 | |
| 189 | When a simple command is executed, the shell performs the following |
| 190 | -expansions, assignments, and redirections, from left to right. |
| 191 | +expansions, assignments, and redirections, from left to right, in |
| 192 | +the following order. |
| 193 | |
| 194 | @enumerate |
| 195 | @item |
| 196 | diff --git a/lib/glob/glob.c b/lib/glob/glob.c |
| 197 | index 398253b..2eaa33e 100644 |
| 198 | --- a/lib/glob/glob.c |
| 199 | +++ b/lib/glob/glob.c |
| 200 | @@ -607,6 +607,7 @@ glob_vector (pat, dir, flags) |
| 201 | register unsigned int i; |
| 202 | int mflags; /* Flags passed to strmatch (). */ |
| 203 | int pflags; /* flags passed to sh_makepath () */ |
| 204 | + int hasglob; /* return value from glob_pattern_p */ |
| 205 | int nalloca; |
| 206 | struct globval *firstmalloc, *tmplink; |
| 207 | char *convfn; |
| 208 | @@ -648,10 +649,12 @@ glob_vector (pat, dir, flags) |
| 209 | patlen = (pat && *pat) ? strlen (pat) : 0; |
| 210 | |
| 211 | /* If the filename pattern (PAT) does not contain any globbing characters, |
| 212 | + or contains a pattern with only backslash escapes (hasglob == 2), |
| 213 | we can dispense with reading the directory, and just see if there is |
| 214 | a filename `DIR/PAT'. If there is, and we can access it, just make the |
| 215 | vector to return and bail immediately. */ |
| 216 | - if (skip == 0 && glob_pattern_p (pat) == 0) |
| 217 | + hasglob = 0; |
| 218 | + if (skip == 0 && (hasglob = glob_pattern_p (pat)) == 0 || hasglob == 2) |
| 219 | { |
| 220 | int dirlen; |
| 221 | struct stat finfo; |
| 222 | diff --git a/pathexp.c b/pathexp.c |
| 223 | index c1bf2d8..e6c5392 100644 |
| 224 | --- a/pathexp.c |
| 225 | +++ b/pathexp.c |
| 226 | @@ -58,7 +58,10 @@ int extended_glob = EXTGLOB_DEFAULT; |
| 227 | /* Control enabling special handling of `**' */ |
| 228 | int glob_star = 0; |
| 229 | |
| 230 | -/* Return nonzero if STRING has any unquoted special globbing chars in it. */ |
| 231 | +/* Return nonzero if STRING has any unquoted special globbing chars in it. |
| 232 | + This is supposed to be called when pathname expansion is performed, so |
| 233 | + it implements the rules in Posix 2.13.3, specifically that an unquoted |
| 234 | + slash cannot appear in a bracket expression. */ |
| 235 | int |
| 236 | unquoted_glob_pattern_p (string) |
| 237 | register char *string; |
| 238 | @@ -85,10 +88,14 @@ unquoted_glob_pattern_p (string) |
| 239 | continue; |
| 240 | |
| 241 | case ']': |
| 242 | - if (open) |
| 243 | + if (open) /* XXX - if --open == 0? */ |
| 244 | return (1); |
| 245 | continue; |
| 246 | |
| 247 | + case '/': |
| 248 | + if (open) |
| 249 | + open = 0; |
| 250 | + |
| 251 | case '+': |
| 252 | case '@': |
| 253 | case '!': |
| 254 | @@ -106,6 +113,11 @@ unquoted_glob_pattern_p (string) |
| 255 | string++; |
| 256 | continue; |
| 257 | } |
| 258 | + else if (open && *string == '/') |
| 259 | + { |
| 260 | + string++; /* quoted slashes in bracket expressions are ok */ |
| 261 | + continue; |
| 262 | + } |
| 263 | else if (*string == 0) |
| 264 | return (0); |
| 265 | |
| 266 | diff --git a/shell.c b/shell.c |
| 267 | index a2b2a55..6adabc8 100644 |
| 268 | --- a/shell.c |
| 269 | +++ b/shell.c |
| 270 | @@ -1293,7 +1293,11 @@ disable_priv_mode () |
| 271 | { |
| 272 | int e; |
| 273 | |
| 274 | +#if HAVE_DECL_SETRESUID |
| 275 | + if (setresuid (current_user.uid, current_user.uid, current_user.uid) < 0) |
| 276 | +#else |
| 277 | if (setuid (current_user.uid) < 0) |
| 278 | +#endif |
| 279 | { |
| 280 | e = errno; |
| 281 | sys_error (_("cannot set uid to %d: effective uid %d"), current_user.uid, current_user.euid); |
| 282 | @@ -1302,7 +1306,11 @@ disable_priv_mode () |
| 283 | exit (e); |
| 284 | #endif |
| 285 | } |
| 286 | +#if HAVE_DECL_SETRESGID |
| 287 | + if (setresgid (current_user.gid, current_user.gid, current_user.gid) < 0) |
| 288 | +#else |
| 289 | if (setgid (current_user.gid) < 0) |
| 290 | +#endif |
| 291 | sys_error (_("cannot set gid to %d: effective gid %d"), current_user.gid, current_user.egid); |
| 292 | |
| 293 | current_user.euid = current_user.uid; |
| 294 | diff --git a/tests/glob.tests b/tests/glob.tests |
| 295 | index 01913bb..fb012f7 100644 |
| 296 | --- a/tests/glob.tests |
| 297 | +++ b/tests/glob.tests |
| 298 | @@ -12,6 +12,8 @@ ${THIS_SH} ./glob1.sub |
| 299 | ${THIS_SH} ./glob2.sub |
| 300 | ${THIS_SH} ./glob3.sub |
| 301 | ${THIS_SH} ./glob4.sub |
| 302 | +${THIS_SH} ./glob6.sub |
| 303 | +${THIS_SH} ./glob7.sub |
| 304 | |
| 305 | MYDIR=$PWD # save where we are |
| 306 | |
| 307 | diff --git a/tests/glob6.sub b/tests/glob6.sub |
| 308 | new file mode 100644 |
| 309 | index 0000000..b099811 |
| 310 | --- /dev/null |
| 311 | +++ b/tests/glob6.sub |
| 312 | @@ -0,0 +1,54 @@ |
| 313 | +# tests of the backslash-in-glob-patterns discussion on the austin-group ML |
| 314 | + |
| 315 | +: ${TMPDIR:=/var/tmp} |
| 316 | + |
| 317 | +ORIG=$PWD |
| 318 | +GLOBDIR=$TMPDIR/bash-glob-$$ |
| 319 | +mkdir $GLOBDIR && cd $GLOBDIR |
| 320 | + |
| 321 | +# does the pattern matcher allow backslashes as escape characters and remove |
| 322 | +# them as part of matching? |
| 323 | +touch abcdefg |
| 324 | +pat='ab\cd*' |
| 325 | +printf '<%s>\n' $pat |
| 326 | +pat='\.' |
| 327 | +printf '<%s>\n' $pat |
| 328 | +rm abcdefg |
| 329 | + |
| 330 | +# how about when escaping pattern characters? |
| 331 | +touch '*abc.c' |
| 332 | +a='\**.c' |
| 333 | +printf '%s\n' $a |
| 334 | +rm -f '*abc.c' |
| 335 | + |
| 336 | +# how about when making the distinction between readable and searchable path |
| 337 | +# components? |
| 338 | +mkdir -m a=x searchable |
| 339 | +mkdir -m a=r readable |
| 340 | + |
| 341 | +p='searchable/\.' |
| 342 | +printf "%s\n" $p |
| 343 | + |
| 344 | +p='searchable/\./.' |
| 345 | +printf "%s\n" $p |
| 346 | + |
| 347 | +p='readable/\.' |
| 348 | +printf "%s\n" $p |
| 349 | + |
| 350 | +p='readable/\./.' |
| 351 | +printf "%s\n" $p |
| 352 | + |
| 353 | +printf "%s\n" 'searchable/\.' |
| 354 | +printf "%s\n" 'readable/\.' |
| 355 | + |
| 356 | +echo */. |
| 357 | + |
| 358 | +p='*/\.' |
| 359 | +echo $p |
| 360 | + |
| 361 | +echo */'.' |
| 362 | + |
| 363 | +rmdir searchable readable |
| 364 | + |
| 365 | +cd $ORIG |
| 366 | +rmdir $GLOBDIR |
| 367 | diff --git a/tests/glob7.sub b/tests/glob7.sub |
| 368 | new file mode 100644 |
| 369 | index 0000000..0212b8e |
| 370 | --- /dev/null |
| 371 | +++ b/tests/glob7.sub |
| 372 | @@ -0,0 +1,11 @@ |
| 373 | +# according to Posix 2.13.3, a slash in a bracket expression renders that |
| 374 | +# bracket expression invalid |
| 375 | +shopt -s nullglob |
| 376 | + |
| 377 | +echo 1: [qwe/qwe] |
| 378 | +echo 2: [qwe/ |
| 379 | +echo 3: [qwe/] |
| 380 | + |
| 381 | +echo 4: [qwe\/qwe] |
| 382 | +echo 5: [qwe\/ |
| 383 | +echo 6: [qwe\/] |
| 384 | -- |
| 385 | 1.9.1 |
| 386 | |