commit 01a500e92f5fbc1485e14d12e6ce55f8ac6151c5 Author: CentOS Sources Date: Mon May 24 04:12:02 2021 +0000 import zsh-5.5.1-6.el8_1.2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..08493c2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/zsh-5.5.1.tar.xz diff --git a/.zsh.metadata b/.zsh.metadata new file mode 100644 index 0000000..4899962 --- /dev/null +++ b/.zsh.metadata @@ -0,0 +1 @@ +98ea952bba9b8752635c75f54bcecef072d3036e SOURCES/zsh-5.5.1.tar.xz diff --git a/SOURCES/0001-zsh-5.5.1-CVE-2018-0502-CVE-2018-13259.patch b/SOURCES/0001-zsh-5.5.1-CVE-2018-0502-CVE-2018-13259.patch new file mode 100644 index 0000000..0345288 --- /dev/null +++ b/SOURCES/0001-zsh-5.5.1-CVE-2018-0502-CVE-2018-13259.patch @@ -0,0 +1,148 @@ +From ddb6c5b4c0ab9c6a7404112d367f0c7cc400ceec Mon Sep 17 00:00:00 2001 +From: Anthony Sottile +Date: Mon, 3 Sep 2018 14:39:25 +0000 +Subject: [PATCH] CVE-2018-0502, CVE-2018-13259: Fix two security issues in + shebang line parsing. + +See NEWS for more information. + +Patch by Anthony Sottile and Buck Evan. + +Upstream-commit: 1c4c7b6a4d17294df028322b70c53803a402233d +Signed-off-by: Kamil Dudka +--- + Etc/FAQ.yo | 2 +- + Src/exec.c | 36 ++++++++++++++++++++---------------- + Test/A05execution.ztst | 22 ++++++++++++++++++++++ + 3 files changed, 43 insertions(+), 17 deletions(-) + +diff --git a/Etc/FAQ.yo b/Etc/FAQ.yo +index 72ff7fa..8552fe7 100644 +--- a/Etc/FAQ.yo ++++ b/Etc/FAQ.yo +@@ -306,7 +306,7 @@ sect(On what machines will it run?) + + sect(What's the latest version?) + +- Zsh 5.5.1 is the latest production version. For details of all the ++ Zsh 5.6 is the latest production version. For details of all the + changes, see the NEWS file in the source distribution. + + A beta of the next version is sometimes available. Development of zsh is +diff --git a/Src/exec.c b/Src/exec.c +index 216057a..0908a1a 100644 +--- a/Src/exec.c ++++ b/Src/exec.c +@@ -453,7 +453,7 @@ execcursh(Estate state, int do_exec) + + /* execve after handling $_ and #! */ + +-#define POUNDBANGLIMIT 64 ++#define POUNDBANGLIMIT 128 + + /**/ + static int +@@ -494,18 +494,20 @@ zexecve(char *pth, char **argv, char **newenvp) + if ((fd = open(pth, O_RDONLY|O_NOCTTY)) >= 0) { + argv0 = *argv; + *argv = pth; +- execvebuf[0] = '\0'; ++ memset(execvebuf, '\0', POUNDBANGLIMIT + 1); + ct = read(fd, execvebuf, POUNDBANGLIMIT); + close(fd); + if (ct >= 0) { +- if (execvebuf[0] == '#') { +- if (execvebuf[1] == '!') { +- for (t0 = 0; t0 != ct; t0++) +- if (execvebuf[t0] == '\n') +- break; ++ if (ct >= 2 && execvebuf[0] == '#' && execvebuf[1] == '!') { ++ for (t0 = 0; t0 != ct; t0++) ++ if (execvebuf[t0] == '\n') ++ break; ++ if (t0 == ct) ++ zerr("%s: bad interpreter: %s: %e", pth, ++ execvebuf + 2, eno); ++ else { + while (inblank(execvebuf[t0])) + execvebuf[t0--] = '\0'; +- execvebuf[POUNDBANGLIMIT] = '\0'; + for (ptr = execvebuf + 2; *ptr && *ptr == ' '; ptr++); + for (ptr2 = ptr; *ptr && *ptr != ' '; ptr++); + if (eno == ENOENT) { +@@ -514,10 +516,16 @@ zexecve(char *pth, char **argv, char **newenvp) + *ptr = '\0'; + if (*ptr2 != '/' && + (pprog = pathprog(ptr2, NULL))) { +- argv[-2] = ptr2; +- argv[-1] = ptr + 1; +- winch_unblock(); +- execve(pprog, argv - 2, newenvp); ++ if (ptr == execvebuf + t0 + 1) { ++ argv[-1] = ptr2; ++ winch_unblock(); ++ execve(pprog, argv - 1, newenvp); ++ } else { ++ argv[-2] = ptr2; ++ argv[-1] = ptr + 1; ++ winch_unblock(); ++ execve(pprog, argv - 2, newenvp); ++ } + } + zerr("%s: bad interpreter: %s: %e", pth, ptr2, + eno); +@@ -532,10 +540,6 @@ zexecve(char *pth, char **argv, char **newenvp) + winch_unblock(); + execve(ptr2, argv - 1, newenvp); + } +- } else if (eno == ENOEXEC) { +- argv[-1] = "sh"; +- winch_unblock(); +- execve("/bin/sh", argv - 1, newenvp); + } + } else if (eno == ENOEXEC) { + for (t0 = 0; t0 != ct; t0++) +diff --git a/Test/A05execution.ztst b/Test/A05execution.ztst +index 0804691..fb39d05 100644 +--- a/Test/A05execution.ztst ++++ b/Test/A05execution.ztst +@@ -12,7 +12,14 @@ + + print '#!/bin/sh\necho This is dir2' >dir2/tstcmd + ++ print -n '#!sh\necho This is slashless' >tstcmd-slashless ++ print -n '#!echo foo\necho This is arg' >tstcmd-arg ++ print '#!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxnyyy' >tstcmd-interp-too-long ++ print '#!/bin/sh\necho should not execute; exit 1' >xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxn ++ + chmod 755 tstcmd dir1/tstcmd dir2/tstcmd ++ chmod 755 tstcmd-slashless tstcmd-arg tstcmd-interp-too-long ++ chmod 755 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxn + + %test + ./tstcmd +@@ -33,6 +40,21 @@ + 0:path (2) + >This is top + ++ PATH=/bin:${ZTST_testdir}/command.tmp/ tstcmd-slashless ++0:path (3) ++>This is slashless ++ ++ PATH=/bin:${ZTST_testdir}/command.tmp tstcmd-arg ++0:path (4) ++*>foo */command.tmp/tstcmd-arg ++ ++ path=(/bin ${ZTST_testdir}/command.tmp/) ++ tstcmd-interp-too-long 2>&1; echo "status $?" ++ path=($storepath) ++0:path (5) ++*>*tstcmd-interp-too-long: bad interpreter: x*xn: no such file or directory ++>status 127 ++ + functst() { print $# arguments:; print -l $*; } + functst "Eines Morgens" "als Gregor Samsa" + functst "" +-- +2.17.1 + diff --git a/SOURCES/0002-zsh-5.5.1-static-analysis.patch b/SOURCES/0002-zsh-5.5.1-static-analysis.patch new file mode 100644 index 0000000..4bec6e1 --- /dev/null +++ b/SOURCES/0002-zsh-5.5.1-static-analysis.patch @@ -0,0 +1,266 @@ +From bc943b78268ad633f79756639d4295f7b61dbedd Mon Sep 17 00:00:00 2001 +From: Kamil Dudka +Date: Wed, 7 Nov 2018 14:04:52 +0100 +Subject: [PATCH 1/5] 43791: File descriptor could be closed twice in clone + +Upstream-commit: a8cc017c74a916b690dc074c299faf4bd24b5af4 +Signed-off-by: Kamil Dudka + +Error: USE_AFTER_FREE (CWE-825): +zsh-5.5.1/Src/Modules/clone.c:71: closed_arg: "close(int)" closes "ttyfd". +zsh-5.5.1/Src/Modules/clone.c:99: double_close: Calling "close(int)" closes handle "ttyfd" which has already been closed. + 97| setsparam("TTY", ztrdup(ttystrname)); + 98| } + 99|-> close(ttyfd); +100| if (pid < 0) { +101| zerrnam(nam, "fork failed: %e", errno); +--- + Src/Modules/clone.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/Src/Modules/clone.c b/Src/Modules/clone.c +index 9304292..dfd8e8a 100644 +--- a/Src/Modules/clone.c ++++ b/Src/Modules/clone.c +@@ -96,7 +96,8 @@ bin_clone(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) + init_io(NULL); + setsparam("TTY", ztrdup(ttystrname)); + } +- close(ttyfd); ++ else ++ close(ttyfd); + if (pid < 0) { + zerrnam(nam, "fork failed: %e", errno); + return 1; +-- +2.17.2 + + +From 6096988f02635ed336a056e3670b63070400e6bc Mon Sep 17 00:00:00 2001 +From: Kamil Dudka +Date: Wed, 7 Nov 2018 14:04:53 +0100 +Subject: [PATCH 2/5] 43793: computil could overrun buffer + +Upstream-commit: 031afe420725e328e9d7742be69ef0bd81c62b9a +Signed-off-by: Kamil Dudka + +Error: BUFFER_SIZE (CWE-120): +zsh-5.5.1/Src/Zle/computil.c:564: overlapping_buffer: The source buffer "str->str + 2" potentially overlaps with the destination buffer "str->str", which results in undefined behavior for "strcpy". +zsh-5.5.1/Src/Zle/computil.c:564: remediation: Replace "strcpy(dest, src)" with "memmove(dest, src, strlen(src)+1)". +562| str->str = ztrdup(str->str); +563| if (hide[1] && str->str[0] == '-' && str->str[1] == '-') +564|-> strcpy(str->str, str->str + 2); +565| else if (str->str[0] == '-' || str->str[0] == '+') +566| strcpy(str->str, str->str + 1); + +Error: BUFFER_SIZE (CWE-120): +zsh-5.5.1/Src/Zle/computil.c:566: overlapping_buffer: The source buffer "str->str + 1" potentially overlaps with the destination buffer "str->str", which results in undefined behavior for "strcpy". +zsh-5.5.1/Src/Zle/computil.c:566: remediation: Replace "strcpy(dest, src)" with "memmove(dest, src, strlen(src)+1)". +564| strcpy(str->str, str->str + 2); +565| else if (str->str[0] == '-' || str->str[0] == '+') +566|-> strcpy(str->str, str->str + 1); +567| } +568| } +--- + Src/Zle/computil.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c +index 5526e0a..cb1c010 100644 +--- a/Src/Zle/computil.c ++++ b/Src/Zle/computil.c +@@ -561,9 +561,9 @@ cd_init(char *nam, char *hide, char *mlen, char *sep, + if (str->str == str->match) + str->str = ztrdup(str->str); + if (hide[1] && str->str[0] == '-' && str->str[1] == '-') +- strcpy(str->str, str->str + 2); ++ memmove(str->str, str->str + 2, strlen(str->str) - 1); + else if (str->str[0] == '-' || str->str[0] == '+') +- strcpy(str->str, str->str + 1); ++ memmove(str->str, str->str + 1, strlen(str->str)); + } + } + for (ap = args; *args && +-- +2.17.2 + + +From 29445bdf10714bd41d2124d3c31cc16c1f682854 Mon Sep 17 00:00:00 2001 +From: Kamil Dudka +Date: Wed, 7 Nov 2018 14:04:54 +0100 +Subject: [PATCH 3/5] 43723: file descriptor could leak on fork error + +Upstream-commit: d1095bdf744c190c7e8ff126ba02caea8f63880d +Signed-off-by: Kamil Dudka + +Error: RESOURCE_LEAK (CWE-772): +zsh-5.5.1/Src/exec.c:4680: open_fn: Returning handle opened by "open". +zsh-5.5.1/Src/exec.c:4680: var_assign: Assigning: "fd" = handle returned from "open(nam, 449, 384)". +zsh-5.5.1/Src/exec.c:4810: leaked_handle: Handle variable "fd" going out of scope leaks the handle. +4808| /* fork or open error */ +4809| child_unblock(); +4810|-> return nam; +4811| } else if (pid) { +4812| int os; +--- + Src/exec.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/Src/exec.c b/Src/exec.c +index 0908a1a..8045db2 100644 +--- a/Src/exec.c ++++ b/Src/exec.c +@@ -4722,7 +4722,8 @@ getoutputfile(char *cmd, char **eptr) + } + + if ((cmdoutpid = pid = zfork(NULL)) == -1) { +- /* fork or open error */ ++ /* fork error */ ++ close(fd); + child_unblock(); + return nam; + } else if (pid) { +-- +2.17.2 + + +From afb4192a75066f86ce7051a72c0feb7b80c0cdd8 Mon Sep 17 00:00:00 2001 +From: Kamil Dudka +Date: Wed, 7 Nov 2018 14:04:55 +0100 +Subject: [PATCH 4/5] 43789: possible use after free clearing up math func from + module + +Upstream-commit: e27175c7c8cdfeb4e28d4ff21eb51aa003d70a03 +Signed-off-by: Kamil Dudka + +Error: USE_AFTER_FREE (CWE-825): +zsh-5.5.1/Src/module.c:1390: freed_arg: "deletemathfunc" frees "f". +zsh-5.5.1/Src/module.c:1352:6: freed_arg: "zfree" frees parameter "f". +zsh-5.5.1/Src/mem.c:1888:5: freed_arg: "free" frees parameter "p". +zsh-5.5.1/Src/module.c:1394: deref_after_free: Dereferencing freed pointer "f". +1392| ret = 1; +1393| } else { +1394|-> f->flags &= ~MFF_ADDED; +1395| } +1396| } +--- + Src/module.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/Src/module.c b/Src/module.c +index 4ae7831..33d75eb 100644 +--- a/Src/module.c ++++ b/Src/module.c +@@ -1390,8 +1390,6 @@ setmathfuncs(char const *nam, MathFunc f, int size, int *e) + if (deletemathfunc(f)) { + zwarnnam(nam, "math function `%s' already deleted", f->name); + ret = 1; +- } else { +- f->flags &= ~MFF_ADDED; + } + } + f++; +-- +2.17.2 + + +From 4553645c00d9a2e81a79e2014b106f6590500287 Mon Sep 17 00:00:00 2001 +From: Kamil Dudka +Date: Wed, 7 Nov 2018 14:04:56 +0100 +Subject: [PATCH 5/5] 43790: failed mailstat could leak memory + +Upstream-commit: d50e204b0c4c10164a711bf640500e46987de9c3 +Signed-off-by: Kamil Dudka + +Error: RESOURCE_LEAK (CWE-772): +zsh-5.5.1/Src/utils.c:7406: alloc_fn: Storage is returned from allocation function "appstr". +zsh-5.5.1/Src/string.c:200:5: alloc_fn: Storage is returned from allocation function "realloc". +zsh-5.5.1/Src/string.c:200:5: identity_transfer: Passing "realloc(base, strlen(base) + strlen(append) + 1UL)" as argument 1 to function "strcat", which returns that argument. +zsh-5.5.1/Src/string.c:200:5: return_alloc_fn: Directly returning storage allocated by "strcat". +zsh-5.5.1/Src/utils.c:7406: var_assign: Assigning: "dir" = storage returned from "appstr(ztrdup(path), "/cur")". +zsh-5.5.1/Src/utils.c:7407: noescape: Resource "dir" is not freed or pointed-to in "stat". +zsh-5.5.1/Src/utils.c:7407: leaked_storage: Variable "dir" going out of scope leaks the storage it points to. +7405| /* See if cur/ is present */ +7406| dir = appstr(ztrdup(path), "/cur"); +7407|-> if (stat(dir, &st_tmp) || !S_ISDIR(st_tmp.st_mode)) return 0; +7408| st_ret.st_atime = st_tmp.st_atime; +7409| + +Error: RESOURCE_LEAK (CWE-772): +zsh-5.5.1/Src/utils.c:7412: alloc_fn: Storage is returned from allocation function "appstr". +zsh-5.5.1/Src/string.c:200:5: alloc_fn: Storage is returned from allocation function "realloc". +zsh-5.5.1/Src/string.c:200:5: identity_transfer: Passing "realloc(base, strlen(base) + strlen(append) + 1UL)" as argument 1 to function "strcat", which returns that argument. +zsh-5.5.1/Src/string.c:200:5: return_alloc_fn: Directly returning storage allocated by "strcat". +zsh-5.5.1/Src/utils.c:7412: var_assign: Assigning: "dir" = storage returned from "appstr(dir, "/tmp")". +zsh-5.5.1/Src/utils.c:7413: noescape: Resource "dir" is not freed or pointed-to in "stat". +zsh-5.5.1/Src/utils.c:7413: leaked_storage: Variable "dir" going out of scope leaks the storage it points to. +7411| dir[plen] = 0; +7412| dir = appstr(dir, "/tmp"); +7413|-> if (stat(dir, &st_tmp) || !S_ISDIR(st_tmp.st_mode)) return 0; +7414| st_ret.st_mtime = st_tmp.st_mtime; +7415| + +Error: RESOURCE_LEAK (CWE-772): +zsh-5.5.1/Src/utils.c:7418: alloc_fn: Storage is returned from allocation function "appstr". +zsh-5.5.1/Src/string.c:200:5: alloc_fn: Storage is returned from allocation function "realloc". +zsh-5.5.1/Src/string.c:200:5: identity_transfer: Passing "realloc(base, strlen(base) + strlen(append) + 1UL)" as argument 1 to function "strcat", which returns that argument. +zsh-5.5.1/Src/string.c:200:5: return_alloc_fn: Directly returning storage allocated by "strcat". +zsh-5.5.1/Src/utils.c:7418: var_assign: Assigning: "dir" = storage returned from "appstr(dir, "/new")". +zsh-5.5.1/Src/utils.c:7419: noescape: Resource "dir" is not freed or pointed-to in "stat". +zsh-5.5.1/Src/utils.c:7419: leaked_storage: Variable "dir" going out of scope leaks the storage it points to. +7417| dir[plen] = 0; +7418| dir = appstr(dir, "/new"); +7419|-> if (stat(dir, &st_tmp) || !S_ISDIR(st_tmp.st_mode)) return 0; +7420| st_ret.st_mtime = st_tmp.st_mtime; +7421| +--- + Src/utils.c | 16 +++++++++++++--- + 1 file changed, 13 insertions(+), 3 deletions(-) + +diff --git a/Src/utils.c b/Src/utils.c +index b418517..492babc 100644 +--- a/Src/utils.c ++++ b/Src/utils.c +@@ -7404,19 +7404,28 @@ mailstat(char *path, struct stat *st) + + /* See if cur/ is present */ + dir = appstr(ztrdup(path), "/cur"); +- if (stat(dir, &st_tmp) || !S_ISDIR(st_tmp.st_mode)) return 0; ++ if (stat(dir, &st_tmp) || !S_ISDIR(st_tmp.st_mode)) { ++ zsfree(dir); ++ return 0; ++ } + st_ret.st_atime = st_tmp.st_atime; + + /* See if tmp/ is present */ + dir[plen] = 0; + dir = appstr(dir, "/tmp"); +- if (stat(dir, &st_tmp) || !S_ISDIR(st_tmp.st_mode)) return 0; ++ if (stat(dir, &st_tmp) || !S_ISDIR(st_tmp.st_mode)) { ++ zsfree(dir); ++ return 0; ++ } + st_ret.st_mtime = st_tmp.st_mtime; + + /* And new/ */ + dir[plen] = 0; + dir = appstr(dir, "/new"); +- if (stat(dir, &st_tmp) || !S_ISDIR(st_tmp.st_mode)) return 0; ++ if (stat(dir, &st_tmp) || !S_ISDIR(st_tmp.st_mode)) { ++ zsfree(dir); ++ return 0; ++ } + st_ret.st_mtime = st_tmp.st_mtime; + + #if THERE_IS_EXACTLY_ONE_MAILDIR_IN_MAILPATH +@@ -7428,6 +7437,7 @@ mailstat(char *path, struct stat *st) + st_tmp.st_atime == st_new_last.st_atime && + st_tmp.st_mtime == st_new_last.st_mtime) { + *st = st_ret_last; ++ zsfree(dir); + return 0; + } + st_new_last = st_tmp; +-- +2.17.2 + diff --git a/SOURCES/0003-zsh-5.5.1-parse-error-exit-status.patch b/SOURCES/0003-zsh-5.5.1-parse-error-exit-status.patch new file mode 100644 index 0000000..6605665 --- /dev/null +++ b/SOURCES/0003-zsh-5.5.1-parse-error-exit-status.patch @@ -0,0 +1,28 @@ +From 878ebe3c74cee4b9702c9672b87ee56f057e1f02 Mon Sep 17 00:00:00 2001 +From: Peter Stephenson +Date: Thu, 29 Nov 2018 17:54:02 +0000 +Subject: [PATCH] 43854: Set tok to LEXERR on generic parse error. + +Needed by main loop which detects an error this way. + +Upstream-commit: ef20425381e83ebd5a10c2ab270a347018371162 +Signed-off-by: Kamil Dudka +--- + Src/lex.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/Src/lex.c b/Src/lex.c +index 44ad880..c29aaba 100644 +--- a/Src/lex.c ++++ b/Src/lex.c +@@ -1613,6 +1613,7 @@ parsestr(char **s) + zerr("parse error near `%c'", err); + else + zerr("parse error"); ++ tok = LEXERR; + } + } + return err; +-- +2.17.2 + diff --git a/SOURCES/0004-zsh-5.5.1-CVE-2019-20044.patch b/SOURCES/0004-zsh-5.5.1-CVE-2019-20044.patch new file mode 100644 index 0000000..96bfcbb --- /dev/null +++ b/SOURCES/0004-zsh-5.5.1-CVE-2019-20044.patch @@ -0,0 +1,1378 @@ +From e6dea148252c9d8cb3de0965f2e558ac13e12f06 Mon Sep 17 00:00:00 2001 +From: Daniel Shahaf +Date: Thu, 26 Dec 2019 11:49:45 +0000 +Subject: [PATCH 1/7] internal: Allow %L in zerrmsg() in non-debug builds, too. + +This will let error messages include long integers. + +Upstream-commit: 81185f4c6106d7ea2f7beaabbec7360c08e400d2 +Signed-off-by: Kamil Dudka +--- + Src/utils.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/Src/utils.c b/Src/utils.c +index 32f6008..2ddc596 100644 +--- a/Src/utils.c ++++ b/Src/utils.c +@@ -325,12 +325,10 @@ zerrmsg(FILE *file, const char *fmt, va_list ap) + nicezputs(s, file); + break; + } +-#ifdef DEBUG + case 'L': + lnum = va_arg(ap, long); + fprintf(file, "%ld", lnum); + break; +-#endif + case 'd': + num = va_arg(ap, int); + fprintf(file, "%d", num); +-- +2.21.1 + + +From 4907caaf15e5a054088e05534c5500679c15b105 Mon Sep 17 00:00:00 2001 +From: dana +Date: Thu, 26 Dec 2019 14:57:07 -0600 +Subject: [PATCH 2/7] unposted: zerrmsg(): Fix macro guard missed in previous + commit + +Upstream-commit: ed21a7b70068b4250a25dcdc5b7213a789b0d0ca +Signed-off-by: Kamil Dudka +--- + Src/utils.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/Src/utils.c b/Src/utils.c +index 2ddc596..4a1dcc4 100644 +--- a/Src/utils.c ++++ b/Src/utils.c +@@ -287,9 +287,7 @@ zerrmsg(FILE *file, const char *fmt, va_list ap) + { + const char *str; + int num; +-#ifdef DEBUG + long lnum; +-#endif + #ifdef HAVE_STRERROR_R + #define ERRBUFSIZE (80) + int olderrno; +-- +2.21.1 + + +From a6763e5de6eebc5994097fc4d778094e7254589c Mon Sep 17 00:00:00 2001 +From: Sam Foxman +Date: Sun, 22 Dec 2019 17:30:28 -0500 +Subject: [PATCH 3/7] Drop privileges securely + +Upstream-commit: 24e993db62cf146fb76ebcf677a4a7aa3766fc74 +Signed-off-by: Kamil Dudka +--- + Src/options.c | 146 +++++++++++++++++++++++++++++++++++++++++--------- + configure.ac | 4 +- + 2 files changed, 125 insertions(+), 25 deletions(-) + +diff --git a/Src/options.c b/Src/options.c +index 590652e..c9608af 100644 +--- a/Src/options.c ++++ b/Src/options.c +@@ -576,6 +576,7 @@ int + bin_setopt(char *nam, char **args, UNUSED(Options ops), int isun) + { + int action, optno, match = 0; ++ int retval = 0; + + /* With no arguments or options, display options. */ + if (!*args) { +@@ -603,18 +604,28 @@ bin_setopt(char *nam, char **args, UNUSED(Options ops), int isun) + inittyptab(); + return 1; + } +- if(!(optno = optlookup(*args))) ++ if(!(optno = optlookup(*args))) { + zwarnnam(nam, "no such option: %s", *args); +- else if(dosetopt(optno, action, 0, opts)) +- zwarnnam(nam, "can't change option: %s", *args); ++ retval = 1; ++ } else { ++ retval = !!dosetopt(optno, action, 0, opts); ++ if (retval) { ++ zwarnnam(nam, "can't change option: %s", *args); ++ } ++ } + break; + } else if(**args == 'm') { + match = 1; + } else { +- if (!(optno = optlookupc(**args))) ++ if (!(optno = optlookupc(**args))) { + zwarnnam(nam, "bad option: -%c", **args); +- else if(dosetopt(optno, action, 0, opts)) +- zwarnnam(nam, "can't change option: -%c", **args); ++ retval = 1; ++ } else { ++ retval = !!dosetopt(optno, action, 0, opts); ++ if (retval) { ++ zwarnnam(nam, "can't change option: -%c", **args); ++ } ++ } + } + } + args++; +@@ -624,10 +635,15 @@ bin_setopt(char *nam, char **args, UNUSED(Options ops), int isun) + if (!match) { + /* Not globbing the arguments -- arguments are simply option names. */ + while (*args) { +- if(!(optno = optlookup(*args++))) ++ if(!(optno = optlookup(*args++))) { + zwarnnam(nam, "no such option: %s", args[-1]); +- else if(dosetopt(optno, !isun, 0, opts)) +- zwarnnam(nam, "can't change option: %s", args[-1]); ++ retval = 1; ++ } else { ++ retval = !!dosetopt(optno, !isun, 0, opts); ++ if (retval) { ++ zwarnnam(nam, "can't change option: %s", args[-1]); ++ } ++ } + } + } else { + /* Globbing option (-m) set. */ +@@ -650,7 +666,8 @@ bin_setopt(char *nam, char **args, UNUSED(Options ops), int isun) + tokenize(s); + if (!(pprog = patcompile(s, PAT_HEAPDUP, NULL))) { + zwarnnam(nam, "bad pattern: %s", *args); +- continue; ++ retval = 1; ++ break; + } + /* Loop over expansions. */ + scanmatchtable(optiontab, pprog, 0, 0, OPT_ALIAS, +@@ -659,7 +676,7 @@ bin_setopt(char *nam, char **args, UNUSED(Options ops), int isun) + } + } + inittyptab(); +- return 0; ++ return retval; + } + + /* Identify an option name */ +@@ -768,20 +785,101 @@ dosetopt(int optno, int value, int force, char *new_opts) + return -1; + } else if(optno == PRIVILEGED && !value) { + /* unsetting PRIVILEGED causes the shell to make itself unprivileged */ +-#ifdef HAVE_SETUID +- setuid(getuid()); +- setgid(getgid()); +- if (setuid(getuid())) { +- zwarn("failed to change user ID: %e", errno); +- return -1; +- } else if (setgid(getgid())) { +- zwarn("failed to change group ID: %e", errno); +- return -1; +- } ++ ++ int skip_setuid = 0; ++ int skip_setgid = 0; ++ ++#if defined(HAVE_GETEGID) && defined(HAVE_SETGID) && defined(HAVE_GETUID) ++ int orig_egid = getegid(); ++#endif ++ ++#if defined(HAVE_GETEUID) && defined(HAVE_GETUID) ++ if (geteuid() == getuid()) { ++ skip_setuid = 1; ++ } ++#endif ++ ++#if defined(HAVE_GETEGID) && defined(HAVE_GETGID) ++ if (getegid() == getgid()) { ++ skip_setgid = 1; ++ } ++#endif ++ ++ if (!skip_setgid) { ++ int setgid_err; ++#ifdef HAVE_SETRESGID ++ setgid_err = setresgid(getgid(), getgid(), getgid()); ++#elif defined(HAVE_SETREGID) ++#if defined(HAVE_GETEGID) && defined(HAVE_SETGID) && defined(HAVE_GETUID) ++ setgid_err = setregid(getgid(), getgid()); ++#else ++ zwarnnam("unsetopt", ++ "PRIVILEGED: can't drop privileges; setregid available, but cannot check if saved gid changed"); ++ return -1; ++#endif ++#else ++ zwarnnam("unsetopt", "PRIVILEGED: can't drop privileges; setresgid and setregid not available"); ++ return -1; ++#endif ++ if (setgid_err) { ++ zwarnnam("unsetopt", "PRIVILEGED: can't drop privileges; failed to change group ID: %e", errno); ++ return -1; ++ } ++ } ++ ++ if (!skip_setuid) { ++#if defined(HAVE_GETEUID) && defined(HAVE_SETUID) ++ int orig_euid = geteuid(); ++#endif ++ int setuid_err; ++#if defined(HAVE_GETEUID) && defined(HAVE_INITGROUPS) && defined(HAVE_GETPWUID) ++ if (geteuid() == 0) { ++ struct passwd *pw = getpwuid(getuid()); ++ if (pw == NULL) { ++ zwarnnam("unsetopt", "can't drop privileges; failed to get user information for uid %d: %e", ++ getuid(), errno); ++ return -1; ++ } ++ if (initgroups(pw->pw_name, pw->pw_gid)) { ++ zwarnnam("unsetopt", "can't drop privileges; failed to set supplementary group list: %e", errno); ++ return -1; ++ } ++ } ++#endif ++ ++#ifdef HAVE_SETRESUID ++ setuid_err = setresuid(getuid(), getuid(), getuid()); ++#elif defined(HAVE_SETREUID) ++#if defined(HAVE_GETEUID) && defined(HAVE_SETUID) && defined(HAVE_GETUID) ++ setuid_err = setreuid(getuid(), getuid()); ++#else ++ zwarnnam("unsetopt", ++ "PRIVILEGED: can't drop privileges; setreuid available, but cannot check if saved uid changed"); ++ return -1; ++#endif + #else +- zwarn("setuid not available"); +- return -1; +-#endif /* not HAVE_SETUID */ ++ zwarnnam("unsetopt", "PRIVILEGED: can't drop privileges; setresuid and setreuid not available"); ++ return -1; ++#endif ++ if (setuid_err) { ++ zwarnnam("unsetopt", "PRIVILEGED: can't drop privileges; failed to change user ID: %e", errno); ++ return -1; ++ } ++#if defined(HAVE_GETEUID) && defined(HAVE_SETUID) && defined(HAVE_GETUID) ++ if (getuid() != 0 && !setuid(orig_euid)) { ++ zwarnnam("unsetopt", "PRIVILEGED: can't drop privileges; was able to restore the euid"); ++ return -1; ++ } ++#endif ++ } ++ ++#if defined(HAVE_GETEGID) && defined(HAVE_SETGID) && defined(HAVE_GETUID) ++ if (getuid() != 0 && !skip_setgid && !setgid(orig_egid)) { ++ zwarnnam("unsetopt", "PRIVILEGED: can't drop privileges; was able to restore the egid"); ++ return -1; ++ } ++#endif ++ + #ifdef JOB_CONTROL + } else if (!force && optno == MONITOR && value) { + if (new_opts[optno] == value) +diff --git a/configure.ac b/configure.ac +index d15a6cd..51cdf89 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1300,7 +1300,9 @@ AC_CHECK_FUNCS(strftime strptime mktime timelocal \ + inet_aton inet_pton inet_ntop \ + getlogin getpwent getpwnam getpwuid getgrgid getgrnam \ + initgroups nis_list \ +- setuid seteuid setreuid setresuid setsid \ ++ getuid setuid seteuid setreuid setresuid setsid \ ++ getgid setgid setegid setregid setresgid \ ++ geteuid getegid \ + memcpy memmove strstr strerror strtoul \ + getrlimit getrusage \ + setlocale \ +-- +2.21.1 + + +From 07ec3e46db743ff37fd2bfeafbb4a44ddc7b4aaa Mon Sep 17 00:00:00 2001 +From: Daniel Shahaf +Date: Thu, 26 Dec 2019 09:16:19 +0000 +Subject: [PATCH 4/7] Improve PRIVILEGED fixes +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +- Fix retval handling in bin_setopt() + +- Don't skip_setuid / skip_setgid. It's not our place to optimize away noops + (that might not even _be_ noops; they might change the saved uid…). + +- Remove HAVE_* guard checks around functions that are used unguarded elsewhere. + +- Use bsd-setres_id.c from OpenSSH to provide setresuid() / setresgid() + everywhere, and thus simplify the ifdef soup. Fix some preëxisting + bugs in the macro definitions of setuid() (do we still need that one?). + +- Fix zwarning() format codes for variadic arguments type safety + +- Restored a comment from HEAD + +- Fix failure modes around initgroups() + +- Compared privilege restoration code with OpenSSH's permanently_drop_uid() and + updated as needed + +- Add E01 PRIVILEGED sanity checks + +Upstream-commit: 8250c5c168f07549ed646e6848e6dda118271e23 +Signed-off-by: Kamil Dudka +--- + Src/openssh_bsd_setres_id.c | 129 +++++++++++++++++++++++++++++++ + Src/options.c | 148 ++++++++++++++++-------------------- + Src/zsh.mdd | 3 +- + Src/zsh_system.h | 94 ++++++++++++++++++----- + Test/E01options.ztst | 15 ++++ + configure.ac | 5 +- + 6 files changed, 292 insertions(+), 102 deletions(-) + create mode 100644 Src/openssh_bsd_setres_id.c + +diff --git a/Src/openssh_bsd_setres_id.c b/Src/openssh_bsd_setres_id.c +new file mode 100644 +index 0000000..65e91a4 +--- /dev/null ++++ b/Src/openssh_bsd_setres_id.c +@@ -0,0 +1,129 @@ ++/* ++ * Copyright (c) 2012 Darren Tucker (dtucker at zip com au). ++ * ++ * Permission to use, copy, modify, and distribute this software for any ++ * purpose with or without fee is hereby granted, provided that the above ++ * copyright notice and this permission notice appear in all copies. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ */ ++/* ++ * openssh_bsd_setres_id.c - setresuid() and setresgid() wrappers ++ * ++ * This file is part of zsh, the Z shell. ++ * ++ * It is based on the file openbsd-compat/bsd-setres_id.c in OpenSSH 7.9p1, ++ * which is subject to the copyright notice above. The zsh modifications are ++ * licensed as follows: ++ * ++ * Copyright (c) 2019 Daniel Shahaf ++ * All rights reserved. ++ * ++ * Permission is hereby granted, without written agreement and without ++ * license or royalty fees, to use, copy, modify, and distribute this ++ * software and to distribute modified versions of this software for any ++ * purpose, provided that the above copyright notice and the following ++ * two paragraphs appear in all copies of this software. ++ * ++ * In no event shall Daniel Shahaf or the Zsh Development Group be liable ++ * to any party for direct, indirect, special, incidental, or consequential ++ * damages arising out of the use of this software and its documentation, ++ * even if Daniel Shahaf and the Zsh Development Group have been advised of ++ * the possibility of such damage. ++ * ++ * Daniel Shahaf and the Zsh Development Group specifically disclaim any ++ * warranties, including, but not limited to, the implied warranties of ++ * merchantability and fitness for a particular purpose. The software ++ * provided hereunder is on an "as is" basis, and Daniel Shahaf and the ++ * Zsh Development Group have no obligation to provide maintenance, ++ * support, updates, enhancements, or modifications. ++ * ++ */ ++ ++ ++#include ++ ++#include ++#include ++#include ++ ++#include "zsh.mdh" ++ ++#if defined(ZSH_IMPLEMENT_SETRESGID) || defined(BROKEN_SETRESGID) ++int ++setresgid(gid_t rgid, gid_t egid, gid_t sgid) ++{ ++ int ret = 0, saved_errno; ++ ++ if (rgid != sgid) { ++ errno = ENOSYS; ++ return -1; ++ } ++#if defined(ZSH_HAVE_NATIVE_SETREGID) && !defined(BROKEN_SETREGID) ++ if (setregid(rgid, egid) < 0) { ++ saved_errno = errno; ++ zwarnnam("setregid", "to gid %L: %e", (long)rgid, errno); ++ errno = saved_errno; ++ ret = -1; ++ } ++#else ++ if (setegid(egid) < 0) { ++ saved_errno = errno; ++ zwarnnam("setegid", "to gid %L: %e", (long)(unsigned int)egid, errno); ++ errno = saved_errno; ++ ret = -1; ++ } ++ if (setgid(rgid) < 0) { ++ saved_errno = errno; ++ zwarnnam("setgid", "to gid %L: %e", (long)rgid, errno); ++ errno = saved_errno; ++ ret = -1; ++ } ++#endif ++ return ret; ++} ++#endif ++ ++#if defined(ZSH_IMPLEMENT_SETRESUID) || defined(BROKEN_SETRESUID) ++int ++setresuid(uid_t ruid, uid_t euid, uid_t suid) ++{ ++ int ret = 0, saved_errno; ++ ++ if (ruid != suid) { ++ errno = ENOSYS; ++ return -1; ++ } ++#if defined(ZSH_HAVE_NATIVE_SETREUID) && !defined(BROKEN_SETREUID) ++ if (setreuid(ruid, euid) < 0) { ++ saved_errno = errno; ++ zwarnnam("setreuid", "to uid %L: %e", (long)ruid, errno); ++ errno = saved_errno; ++ ret = -1; ++ } ++#else ++ ++# ifndef SETEUID_BREAKS_SETUID ++ if (seteuid(euid) < 0) { ++ saved_errno = errno; ++ zwarnnam("seteuid", "to uid %L: %e", (long)euid, errno); ++ errno = saved_errno; ++ ret = -1; ++ } ++# endif ++ if (setuid(ruid) < 0) { ++ saved_errno = errno; ++ zwarnnam("setuid", "to uid %L: %e", (long)ruid, errno); ++ errno = saved_errno; ++ ret = -1; ++ } ++#endif ++ return ret; ++} ++#endif +diff --git a/Src/options.c b/Src/options.c +index c9608af..deec560 100644 +--- a/Src/options.c ++++ b/Src/options.c +@@ -606,25 +606,21 @@ bin_setopt(char *nam, char **args, UNUSED(Options ops), int isun) + } + if(!(optno = optlookup(*args))) { + zwarnnam(nam, "no such option: %s", *args); +- retval = 1; +- } else { +- retval = !!dosetopt(optno, action, 0, opts); +- if (retval) { +- zwarnnam(nam, "can't change option: %s", *args); +- } ++ retval |= 1; ++ } else if (dosetopt(optno, action, 0, opts)) { ++ zwarnnam(nam, "can't change option: %s", *args); ++ retval |= 1; + } + break; + } else if(**args == 'm') { + match = 1; + } else { +- if (!(optno = optlookupc(**args))) { ++ if (!(optno = optlookupc(**args))) { + zwarnnam(nam, "bad option: -%c", **args); +- retval = 1; +- } else { +- retval = !!dosetopt(optno, action, 0, opts); +- if (retval) { +- zwarnnam(nam, "can't change option: -%c", **args); +- } ++ retval |= 1; ++ } else if (dosetopt(optno, action, 0, opts)) { ++ zwarnnam(nam, "can't change option: -%c", **args); ++ retval |= 1; + } + } + } +@@ -637,12 +633,10 @@ bin_setopt(char *nam, char **args, UNUSED(Options ops), int isun) + while (*args) { + if(!(optno = optlookup(*args++))) { + zwarnnam(nam, "no such option: %s", args[-1]); +- retval = 1; +- } else { +- retval = !!dosetopt(optno, !isun, 0, opts); +- if (retval) { +- zwarnnam(nam, "can't change option: %s", args[-1]); +- } ++ retval |= 1; ++ } else if (dosetopt(optno, !isun, 0, opts)) { ++ zwarnnam(nam, "can't change option: %s", args[-1]); ++ retval |= 1; + } + } + } else { +@@ -666,7 +660,7 @@ bin_setopt(char *nam, char **args, UNUSED(Options ops), int isun) + tokenize(s); + if (!(pprog = patcompile(s, PAT_HEAPDUP, NULL))) { + zwarnnam(nam, "bad pattern: %s", *args); +- retval = 1; ++ retval |= 1; + break; + } + /* Loop over expansions. */ +@@ -786,100 +780,92 @@ dosetopt(int optno, int value, int force, char *new_opts) + } else if(optno == PRIVILEGED && !value) { + /* unsetting PRIVILEGED causes the shell to make itself unprivileged */ + +- int skip_setuid = 0; +- int skip_setgid = 0; +- +-#if defined(HAVE_GETEGID) && defined(HAVE_SETGID) && defined(HAVE_GETUID) +- int orig_egid = getegid(); +-#endif ++ /* If set, return -1 so lastval will be non-zero. */ ++ int failed = 0; + +-#if defined(HAVE_GETEUID) && defined(HAVE_GETUID) +- if (geteuid() == getuid()) { +- skip_setuid = 1; +- } ++#ifdef HAVE_SETUID ++ const int orig_euid = geteuid(); + #endif ++ const int orig_egid = getegid(); + +-#if defined(HAVE_GETEGID) && defined(HAVE_GETGID) +- if (getegid() == getgid()) { +- skip_setgid = 1; +- } +-#endif +- +- if (!skip_setgid) { +- int setgid_err; +-#ifdef HAVE_SETRESGID +- setgid_err = setresgid(getgid(), getgid(), getgid()); +-#elif defined(HAVE_SETREGID) +-#if defined(HAVE_GETEGID) && defined(HAVE_SETGID) && defined(HAVE_GETUID) +- setgid_err = setregid(getgid(), getgid()); +-#else +- zwarnnam("unsetopt", +- "PRIVILEGED: can't drop privileges; setregid available, but cannot check if saved gid changed"); ++ /* ++ * Set the GID first as if we set the UID to non-privileged it ++ * might be impossible to restore the GID. ++ */ ++ { ++#ifndef HAVE_SETRESGID ++ zwarnnam("unsetopt", "PRIVILEGED: can't drop privileges; setresgid() and friends not available"); + return -1; +-#endif + #else +- zwarnnam("unsetopt", "PRIVILEGED: can't drop privileges; setresgid and setregid not available"); +- return -1; +-#endif ++ int setgid_err; ++ setgid_err = setresgid(getgid(), getgid(), getgid()); + if (setgid_err) { + zwarnnam("unsetopt", "PRIVILEGED: can't drop privileges; failed to change group ID: %e", errno); + return -1; + } ++#endif + } + +- if (!skip_setuid) { +-#if defined(HAVE_GETEUID) && defined(HAVE_SETUID) +- int orig_euid = geteuid(); +-#endif ++ /* Set the UID second. */ ++ { ++#ifndef HAVE_SETRESUID ++ zwarnnam("unsetopt", "PRIVILEGED: can't drop privileges; setresuid() and friends not available"); ++ return -1; ++#else + int setuid_err; +-#if defined(HAVE_GETEUID) && defined(HAVE_INITGROUPS) && defined(HAVE_GETPWUID) ++ ++# ifdef HAVE_INITGROUPS ++ /* Set the supplementary groups list. */ + if (geteuid() == 0) { + struct passwd *pw = getpwuid(getuid()); + if (pw == NULL) { +- zwarnnam("unsetopt", "can't drop privileges; failed to get user information for uid %d: %e", +- getuid(), errno); +- return -1; +- } +- if (initgroups(pw->pw_name, pw->pw_gid)) { ++ zwarnnam("unsetopt", "can't drop privileges; failed to get user information for uid %L: %e", ++ (long)getuid(), errno); ++ failed = 1; ++ } else if (initgroups(pw->pw_name, pw->pw_gid)) { + zwarnnam("unsetopt", "can't drop privileges; failed to set supplementary group list: %e", errno); + return -1; + } ++ } else if (getuid() != 0 && ++ (geteuid() != getuid() || orig_egid != getegid())) { ++ zwarnnam("unsetopt", "PRIVILEGED: supplementary group list not changed due to lack of permissions: EUID=%L", ++ (long)geteuid()); ++ failed = 1; + } +-#endif ++# else ++ /* initgroups() isn't in POSIX. If it's not available on the system, ++ * we silently skip it. */ ++# endif + +-#ifdef HAVE_SETRESUID + setuid_err = setresuid(getuid(), getuid(), getuid()); +-#elif defined(HAVE_SETREUID) +-#if defined(HAVE_GETEUID) && defined(HAVE_SETUID) && defined(HAVE_GETUID) +- setuid_err = setreuid(getuid(), getuid()); +-#else +- zwarnnam("unsetopt", +- "PRIVILEGED: can't drop privileges; setreuid available, but cannot check if saved uid changed"); +- return -1; +-#endif +-#else +- zwarnnam("unsetopt", "PRIVILEGED: can't drop privileges; setresuid and setreuid not available"); +- return -1; +-#endif + if (setuid_err) { + zwarnnam("unsetopt", "PRIVILEGED: can't drop privileges; failed to change user ID: %e", errno); + return -1; + } +-#if defined(HAVE_GETEUID) && defined(HAVE_SETUID) && defined(HAVE_GETUID) +- if (getuid() != 0 && !setuid(orig_euid)) { +- zwarnnam("unsetopt", "PRIVILEGED: can't drop privileges; was able to restore the euid"); +- return -1; +- } + #endif + } + +-#if defined(HAVE_GETEGID) && defined(HAVE_SETGID) && defined(HAVE_GETUID) +- if (getuid() != 0 && !skip_setgid && !setgid(orig_egid)) { ++#ifdef HAVE_SETGID ++ if (getuid() != 0 && orig_egid != getegid() && ++ (setgid(orig_egid) != -1 || setegid(orig_egid) != -1)) { + zwarnnam("unsetopt", "PRIVILEGED: can't drop privileges; was able to restore the egid"); + return -1; + } + #endif + ++#ifdef HAVE_SETUID ++ if (getuid() != 0 && orig_euid != geteuid() && ++ (setuid(orig_euid) != -1 || seteuid(orig_euid) != -1)) { ++ zwarnnam("unsetopt", "PRIVILEGED: can't drop privileges; was able to restore the euid"); ++ return -1; ++ } ++#endif ++ ++ if (failed) { ++ /* A warning message has been printed. */ ++ return -1; ++ } ++ + #ifdef JOB_CONTROL + } else if (!force && optno == MONITOR && value) { + if (new_opts[optno] == value) +diff --git a/Src/zsh.mdd b/Src/zsh.mdd +index 324435d..a2590dc 100644 +--- a/Src/zsh.mdd ++++ b/Src/zsh.mdd +@@ -13,7 +13,8 @@ objects="builtin.o compat.o cond.o context.o \ + exec.o glob.o hashtable.o hashnameddir.o \ + hist.o init.o input.o jobs.o lex.o linklist.o loop.o math.o \ + mem.o module.o options.o params.o parse.o pattern.o prompt.o signals.o \ +-signames.o sort.o string.o subst.o text.o utils.o watch.o" ++signames.o sort.o string.o subst.o text.o utils.o watch.o \ ++openssh_bsd_setres_id.o" + + headers="../config.h zsh_system.h zsh.h sigcount.h signals.h \ + prototypes.h hashtable.h ztype.h" +diff --git a/Src/zsh_system.h b/Src/zsh_system.h +index 5339b49..18fe09b 100644 +--- a/Src/zsh_system.h ++++ b/Src/zsh_system.h +@@ -456,30 +456,90 @@ struct timezone { + # define setpgrp setpgid + #endif + +-/* can we set the user/group id of a process */ ++/* compatibility wrappers */ + +-#ifndef HAVE_SETUID ++/* Our strategy is as follows: ++ * ++ * - Ensure that either setre[ug]id() or set{e,}[ug]id() is available. ++ * - If setres[ug]id() are missing, provide them in terms of either ++ * setre[ug]id() or set{e,}[ug]id(), whichever is available. ++ * - Provide replacement setre[ug]id() or set{e,}[ug]id() if they are not ++ * available natively. ++ * ++ * There isn't a circular dependency because, right off the bat, we check that ++ * there's an end condition, and #error out otherwise. ++ */ ++#if !defined(HAVE_SETREUID) && !(defined(HAVE_SETEUID) && defined(HAVE_SETUID)) ++ /* ++ * If you run into this error, you have two options: ++ * - Teach zsh how to do the equivalent of setreuid() on your system ++ * - Remove support for PRIVILEGED option, and then remove the #error. ++ */ ++# error "Don't know how to change UID" ++#endif ++#if !defined(HAVE_SETREGID) && !(defined(HAVE_SETEGID) && defined(HAVE_SETGID)) ++ /* See above comment. */ ++# error "Don't know how to change GID" ++#endif ++ ++/* Provide setresuid(). */ ++#ifndef HAVE_SETRESUID ++int setresuid(uid_t, uid_t, uid_t); ++# define HAVE_SETRESUID ++# define ZSH_IMPLEMENT_SETRESUID + # ifdef HAVE_SETREUID +-# define setuid(X) setreuid(X,X) +-# define setgid(X) setregid(X,X) +-# define HAVE_SETUID ++# define ZSH_HAVE_NATIVE_SETREUID + # endif + #endif + +-/* can we set the effective user/group id of a process */ ++/* Provide setresgid(). */ ++#ifndef HAVE_SETRESGID ++int setresgid(gid_t, gid_t, gid_t); ++# define HAVE_SETRESGID ++# define ZSH_IMPLEMENT_SETRESGID ++# ifdef HAVE_SETREGID ++# define ZSH_HAVE_NATIVE_SETREGID ++# endif ++#endif + ++/* Provide setreuid(). */ ++#ifndef HAVE_SETREUID ++# define setreuid(X, Y) setresuid((X), (Y), -1) ++# define HAVE_SETREUID ++#endif ++ ++/* Provide setregid(). */ ++#ifndef HAVE_SETREGID ++# define setregid(X, Y) setresgid((X), (Y), -1) ++# define HAVE_SETREGID ++#endif ++ ++/* Provide setuid(). */ ++/* ### TODO: Either remove this (this function has been standard since 1985), ++ * ### or rewrite this without multiply-evaluating the argument */ ++#ifndef HAVE_SETUID ++# define setuid(X) setreuid((X), (X)) ++# define HAVE_SETUID ++#endif ++ ++/* Provide setgid(). */ ++#ifndef HAVE_SETGID ++/* ### TODO: Either remove this (this function has been standard since 1985), ++ * ### or rewrite this without multiply-evaluating the argument */ ++# define setgid(X) setregid((X), (X)) ++# define HAVE_SETGID ++#endif ++ ++/* Provide seteuid(). */ + #ifndef HAVE_SETEUID +-# ifdef HAVE_SETREUID +-# define seteuid(X) setreuid(-1,X) +-# define setegid(X) setregid(-1,X) +-# define HAVE_SETEUID +-# else +-# ifdef HAVE_SETRESUID +-# define seteuid(X) setresuid(-1,X,-1) +-# define setegid(X) setresgid(-1,X,-1) +-# define HAVE_SETEUID +-# endif +-# endif ++# define seteuid(X) setreuid(-1, (X)) ++# define HAVE_SETEUID ++#endif ++ ++/* Provide setegid(). */ ++#ifndef HAVE_SETEGID ++# define setegid(X) setregid(-1, (X)) ++# define HAVE_SETEGID + #endif + + #ifdef HAVE_SYS_RESOURCE_H +diff --git a/Test/E01options.ztst b/Test/E01options.ztst +index 0f6bb34..c4c3d67 100644 +--- a/Test/E01options.ztst ++++ b/Test/E01options.ztst +@@ -1391,3 +1391,18 @@ F:Regression test for workers/41811 + ?(anon):4: `break' active at end of function scope + ?(anon):4: `break' active at end of function scope + ?(anon):4: `break' active at end of function scope ++ ++# There are further tests for PRIVILEGED in P01privileged.ztst. ++ if [[ -o privileged ]]; then ++ unsetopt privileged ++ fi ++ unsetopt privileged ++0:PRIVILEGED sanity check: unsetting is idempotent ++F:If this test fails at the first unsetopt, refer to P01privileged.ztst. ++ ++ if [[ -o privileged ]]; then ++ (( UID != EUID )) ++ else ++ (( UID == EUID )) ++ fi ++0:PRIVILEGED sanity check: default value is correct +diff --git a/configure.ac b/configure.ac +index 51cdf89..4e30ad1 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1300,9 +1300,8 @@ AC_CHECK_FUNCS(strftime strptime mktime timelocal \ + inet_aton inet_pton inet_ntop \ + getlogin getpwent getpwnam getpwuid getgrgid getgrnam \ + initgroups nis_list \ +- getuid setuid seteuid setreuid setresuid setsid \ +- getgid setgid setegid setregid setresgid \ +- geteuid getegid \ ++ setuid seteuid setreuid setresuid setsid \ ++ setgid setegid setregid setresgid \ + memcpy memmove strstr strerror strtoul \ + getrlimit getrusage \ + setlocale \ +-- +2.21.1 + + +From 6caecc2efb2fa4c708fd27858962ace55d957dce Mon Sep 17 00:00:00 2001 +From: dana +Date: Sun, 29 Dec 2019 02:41:11 +0000 +Subject: [PATCH 5/7] Improve PRIVILEGED fixes (again) + +* Pass RGID instead of passwd GID to initgroups() + +* Clean up #ifdefs, avoid unnecessary checks + +* Flatten conditions + +Upstream-commit: 26d02efa7a9b0a6b32e1a8bbc6aca6c544b94211 +Signed-off-by: Kamil Dudka +--- + Src/options.c | 92 ++++++++++++++++++++++++--------------------------- + 1 file changed, 43 insertions(+), 49 deletions(-) + +diff --git a/Src/options.c b/Src/options.c +index deec560..8599ed3 100644 +--- a/Src/options.c ++++ b/Src/options.c +@@ -780,91 +780,85 @@ dosetopt(int optno, int value, int force, char *new_opts) + } else if(optno == PRIVILEGED && !value) { + /* unsetting PRIVILEGED causes the shell to make itself unprivileged */ + ++/* For simplicity's sake, require both setresgid() and setresuid() up-front. */ ++#if !defined(HAVE_SETRESGID) ++ zwarnnam("unsetopt", "PRIVILEGED: can't drop privileges; setresgid() and friends not available"); ++ return -1; ++#elif !defined(HAVE_SETRESUID) ++ zwarnnam("unsetopt", "PRIVILEGED: can't drop privileges; setresuid() and friends not available"); ++ return -1; ++#else + /* If set, return -1 so lastval will be non-zero. */ + int failed = 0; +- +-#ifdef HAVE_SETUID + const int orig_euid = geteuid(); +-#endif + const int orig_egid = getegid(); + + /* + * Set the GID first as if we set the UID to non-privileged it + * might be impossible to restore the GID. + */ +- { +-#ifndef HAVE_SETRESGID +- zwarnnam("unsetopt", "PRIVILEGED: can't drop privileges; setresgid() and friends not available"); ++ if (setresgid(getgid(), getgid(), getgid())) { ++ zwarnnam("unsetopt", "PRIVILEGED: can't drop privileges; failed to change group ID: %e", errno); + return -1; +-#else +- int setgid_err; +- setgid_err = setresgid(getgid(), getgid(), getgid()); +- if (setgid_err) { +- zwarnnam("unsetopt", "PRIVILEGED: can't drop privileges; failed to change group ID: %e", errno); +- return -1; +- } +-#endif + } + +- /* Set the UID second. */ +- { +-#ifndef HAVE_SETRESUID +- zwarnnam("unsetopt", "PRIVILEGED: can't drop privileges; setresuid() and friends not available"); +- return -1; +-#else +- int setuid_err; +- + # ifdef HAVE_INITGROUPS +- /* Set the supplementary groups list. */ +- if (geteuid() == 0) { +- struct passwd *pw = getpwuid(getuid()); +- if (pw == NULL) { +- zwarnnam("unsetopt", "can't drop privileges; failed to get user information for uid %L: %e", +- (long)getuid(), errno); +- failed = 1; +- } else if (initgroups(pw->pw_name, pw->pw_gid)) { +- zwarnnam("unsetopt", "can't drop privileges; failed to set supplementary group list: %e", errno); +- return -1; +- } +- } else if (getuid() != 0 && +- (geteuid() != getuid() || orig_egid != getegid())) { +- zwarnnam("unsetopt", "PRIVILEGED: supplementary group list not changed due to lack of permissions: EUID=%L", +- (long)geteuid()); ++ /* Set the supplementary groups list. ++ * ++ * Note that on macOS, FreeBSD, and possibly some other platforms, ++ * initgroups() resets the EGID to its second argument (see setgroups(2) for ++ * details). This has the potential to leave the EGID in an unexpected ++ * state. However, it seems common in other projects that do this dance to ++ * simply re-use the same GID that's going to become the EGID anyway, in ++ * which case it doesn't matter. That's what we do here. It's therefore ++ * possible, in some probably uncommon cases, that the shell ends up not ++ * having the privileges of the RUID user's primary/passwd group. */ ++ if (geteuid() == 0) { ++ struct passwd *pw = getpwuid(getuid()); ++ if (pw == NULL) { ++ zwarnnam("unsetopt", "can't drop privileges; failed to get user information for uid %L: %e", ++ (long)getuid(), errno); + failed = 1; ++ /* This may behave strangely in the unlikely event that the same user ++ * name appears with multiple UIDs in the passwd database */ ++ } else if (initgroups(pw->pw_name, getgid())) { ++ zwarnnam("unsetopt", "can't drop privileges; failed to set supplementary group list: %e", errno); ++ return -1; + } ++ } else if (getuid() != 0 && ++ (geteuid() != getuid() || orig_egid != getegid())) { ++ zwarnnam("unsetopt", "PRIVILEGED: supplementary group list not changed due to lack of permissions: EUID=%L", ++ (long)geteuid()); ++ failed = 1; ++ } + # else +- /* initgroups() isn't in POSIX. If it's not available on the system, +- * we silently skip it. */ ++ /* initgroups() isn't in POSIX. If it's not available on the system, ++ * we silently skip it. */ + # endif + +- setuid_err = setresuid(getuid(), getuid(), getuid()); +- if (setuid_err) { +- zwarnnam("unsetopt", "PRIVILEGED: can't drop privileges; failed to change user ID: %e", errno); +- return -1; +- } +-#endif ++ /* Set the UID second. */ ++ if (setresuid(getuid(), getuid(), getuid())) { ++ zwarnnam("unsetopt", "PRIVILEGED: can't drop privileges; failed to change user ID: %e", errno); ++ return -1; + } + +-#ifdef HAVE_SETGID + if (getuid() != 0 && orig_egid != getegid() && + (setgid(orig_egid) != -1 || setegid(orig_egid) != -1)) { + zwarnnam("unsetopt", "PRIVILEGED: can't drop privileges; was able to restore the egid"); + return -1; + } +-#endif + +-#ifdef HAVE_SETUID + if (getuid() != 0 && orig_euid != geteuid() && + (setuid(orig_euid) != -1 || seteuid(orig_euid) != -1)) { + zwarnnam("unsetopt", "PRIVILEGED: can't drop privileges; was able to restore the euid"); + return -1; + } +-#endif + + if (failed) { + /* A warning message has been printed. */ + return -1; + } ++#endif /* HAVE_SETRESGID && HAVE_SETRESUID */ + + #ifdef JOB_CONTROL + } else if (!force && optno == MONITOR && value) { +-- +2.21.1 + + +From 7d224cc6d93933db596ecb4ec78ba5bf48ef2d32 Mon Sep 17 00:00:00 2001 +From: dana +Date: Sun, 29 Dec 2019 02:43:14 +0000 +Subject: [PATCH 6/7] Clean up error-message white space + +Upstream-commit: 4ce66857b71b40a0661df3780ff557f2b0f4cb13 +Signed-off-by: Kamil Dudka +--- + Src/options.c | 30 +++++++++++++++++++++--------- + 1 file changed, 21 insertions(+), 9 deletions(-) + +diff --git a/Src/options.c b/Src/options.c +index 8599ed3..5b972d4 100644 +--- a/Src/options.c ++++ b/Src/options.c +@@ -782,10 +782,12 @@ dosetopt(int optno, int value, int force, char *new_opts) + + /* For simplicity's sake, require both setresgid() and setresuid() up-front. */ + #if !defined(HAVE_SETRESGID) +- zwarnnam("unsetopt", "PRIVILEGED: can't drop privileges; setresgid() and friends not available"); ++ zwarnnam("unsetopt", ++ "PRIVILEGED: can't drop privileges; setresgid() and friends not available"); + return -1; + #elif !defined(HAVE_SETRESUID) +- zwarnnam("unsetopt", "PRIVILEGED: can't drop privileges; setresuid() and friends not available"); ++ zwarnnam("unsetopt", ++ "PRIVILEGED: can't drop privileges; setresuid() and friends not available"); + return -1; + #else + /* If set, return -1 so lastval will be non-zero. */ +@@ -798,7 +800,9 @@ dosetopt(int optno, int value, int force, char *new_opts) + * might be impossible to restore the GID. + */ + if (setresgid(getgid(), getgid(), getgid())) { +- zwarnnam("unsetopt", "PRIVILEGED: can't drop privileges; failed to change group ID: %e", errno); ++ zwarnnam("unsetopt", ++ "PRIVILEGED: can't drop privileges; failed to change group ID: %e", ++ errno); + return -1; + } + +@@ -816,18 +820,22 @@ dosetopt(int optno, int value, int force, char *new_opts) + if (geteuid() == 0) { + struct passwd *pw = getpwuid(getuid()); + if (pw == NULL) { +- zwarnnam("unsetopt", "can't drop privileges; failed to get user information for uid %L: %e", ++ zwarnnam("unsetopt", ++ "can't drop privileges; failed to get user information for uid %L: %e", + (long)getuid(), errno); + failed = 1; + /* This may behave strangely in the unlikely event that the same user + * name appears with multiple UIDs in the passwd database */ + } else if (initgroups(pw->pw_name, getgid())) { +- zwarnnam("unsetopt", "can't drop privileges; failed to set supplementary group list: %e", errno); ++ zwarnnam("unsetopt", ++ "can't drop privileges; failed to set supplementary group list: %e", ++ errno); + return -1; + } + } else if (getuid() != 0 && + (geteuid() != getuid() || orig_egid != getegid())) { +- zwarnnam("unsetopt", "PRIVILEGED: supplementary group list not changed due to lack of permissions: EUID=%L", ++ zwarnnam("unsetopt", ++ "PRIVILEGED: supplementary group list not changed due to lack of permissions: EUID=%L", + (long)geteuid()); + failed = 1; + } +@@ -838,19 +846,23 @@ dosetopt(int optno, int value, int force, char *new_opts) + + /* Set the UID second. */ + if (setresuid(getuid(), getuid(), getuid())) { +- zwarnnam("unsetopt", "PRIVILEGED: can't drop privileges; failed to change user ID: %e", errno); ++ zwarnnam("unsetopt", ++ "PRIVILEGED: can't drop privileges; failed to change user ID: %e", ++ errno); + return -1; + } + + if (getuid() != 0 && orig_egid != getegid() && + (setgid(orig_egid) != -1 || setegid(orig_egid) != -1)) { +- zwarnnam("unsetopt", "PRIVILEGED: can't drop privileges; was able to restore the egid"); ++ zwarnnam("unsetopt", ++ "PRIVILEGED: can't drop privileges; was able to restore the egid"); + return -1; + } + + if (getuid() != 0 && orig_euid != geteuid() && + (setuid(orig_euid) != -1 || seteuid(orig_euid) != -1)) { +- zwarnnam("unsetopt", "PRIVILEGED: can't drop privileges; was able to restore the euid"); ++ zwarnnam("unsetopt", ++ "PRIVILEGED: can't drop privileges; was able to restore the euid"); + return -1; + } + +-- +2.21.1 + + +From a86d3b201352f14bc17c9521baeb5eaa2ad60f1a Mon Sep 17 00:00:00 2001 +From: dana +Date: Sat, 28 Dec 2019 20:45:55 -0600 +Subject: [PATCH 7/7] Add unsetopt/PRIVILEGED tests + +Upstream-commit: b15bd4aa590db8087d1e8f2eb1af2874f5db814d +Signed-off-by: Kamil Dudka +--- + Test/E01options.ztst | 10 +- + Test/P01privileged.ztst | 197 ++++++++++++++++++++++++++++++++++++++++ + Test/README | 1 + + 3 files changed, 207 insertions(+), 1 deletion(-) + create mode 100644 Test/P01privileged.ztst + +diff --git a/Test/E01options.ztst b/Test/E01options.ztst +index c4c3d67..767879a 100644 +--- a/Test/E01options.ztst ++++ b/Test/E01options.ztst +@@ -74,7 +74,6 @@ + # HASH_LIST_ALL ) + # PRINT_EXIT_STATUS haven't worked out what this does yet, although + # Bart suggested a fix. +-# PRIVILEGED (similar to GLOBAL_RCS) + # RCS ( " " " " ) + # SH_OPTION_LETTERS even I found this too dull to set up a test for + # SINGLE_COMMAND kills shell +@@ -95,6 +94,15 @@ + + %test + ++ # setopt should move on to the next operation in the face of an error, but ++ # preserve the >0 return code ++ unsetopt aliases ++ setopt not_a_real_option aliases && return 2 ++ print -r - $options[aliases] ++0:setopt error handling ++?(eval):setopt:4: no such option: not_a_real_option ++>on ++ + alias echo='print foo' + unsetopt aliases + # use eval else aliases are all parsed at start +diff --git a/Test/P01privileged.ztst b/Test/P01privileged.ztst +new file mode 100644 +index 0000000..c54112b +--- /dev/null ++++ b/Test/P01privileged.ztst +@@ -0,0 +1,197 @@ ++# This file contains tests related to the PRIVILEGED option. In order to run, ++# it requires that the test process itself have super-user privileges (or that ++# one of the environment variables described below be set). This can be achieved ++# via, e.g., `sudo make check TESTNUM=P`. ++# ++# Optionally, the environment variables ZSH_TEST_UNPRIVILEGED_UID and/or ++# ZSH_TEST_UNPRIVILEGED_GID may be set to UID:EUID or GID:EGID pairs, where the ++# two IDs in each pair are different, non-0 IDs valid on the system being used ++# to run the tests. (The UIDs must both be non-0 to effectively test downgrading ++# of privileges, and they must be non-matching to test auto-enabling of ++# PRIVILEGED and to ensure that disabling PRIVILEGED correctly resets the saved ++# UID. Technically GID 0 is not special, but for simplicity's sake we apply the ++# same requirements here.) ++# ++# If either of the aforementioned environment variables is not set, the test ++# script will try to pick the first two >0 IDs from the passwd/group databases ++# on the current system. ++# ++# If either variable is set, the tests will run, but they will likely fail ++# without super-user privileges. ++ ++%prep ++ ++ # Mind your empty lines here. The logic in this %prep section is somewhat ++ # complex compared to most others; to avoid lots of nested/duplicated ++ # conditions we need to make sure that this all gets executed as a single ++ # function from which we can return early ++ [[ $EUID == 0 || -n $ZSH_TEST_UNPRIVILEGED_UID$ZSH_TEST_UNPRIVILEGED_GID ]] || { ++ ZTST_unimplemented='PRIVILEGED tests require super-user privileges (or env var)' ++ return 1 ++ } ++ (( $+commands[perl] )) || { # @todo Eliminate this dependency with a C wrapper? ++ ZTST_unimplemented='PRIVILEGED tests require Perl' ++ return 1 ++ } ++ grep -qE '#define HAVE_SETRES?UID' $ZTST_testdir/../config.h || { ++ ZTST_unimplemented='PRIVILEGED tests require setreuid()/setresuid()' ++ return 1 ++ } ++ # ++ ruid= euid= rgid= egid= ++ # ++ if [[ -n $ZSH_TEST_UNPRIVILEGED_UID ]]; then ++ ruid=${ZSH_TEST_UNPRIVILEGED_UID%%:*} ++ euid=${ZSH_TEST_UNPRIVILEGED_UID##*:} ++ else ++ print -ru$ZTST_fd 'Selecting unprivileged UID:EUID pair automatically' ++ local tmp=$( getent passwd 2> /dev/null || < /etc/passwd ) ++ # Note: Some awks require -v and its argument to be separate ++ ruid=$( awk -F: '$3 > 0 { print $3; exit; }' <<< $tmp ) ++ euid=$( awk -F: -v u=$ruid '$3 > u { print $3; exit; }' <<< $tmp ) ++ fi ++ # ++ if [[ -n $ZSH_TEST_UNPRIVILEGED_GID ]]; then ++ rgid=${ZSH_TEST_UNPRIVILEGED_GID%%:*} ++ egid=${ZSH_TEST_UNPRIVILEGED_GID##*:} ++ else ++ print -ru$ZTST_fd 'Selecting unprivileged GID:EGID pair automatically' ++ local tmp=$( getent group 2> /dev/null || < /etc/group ) ++ # Note: Some awks require -v and its argument to be separate ++ rgid=$( awk -F: '$3 > 0 { print $3; exit; }' <<< $tmp ) ++ egid=$( awk -F: -v g=$rgid '$3 > g { print $3; exit; }' <<< $tmp ) ++ fi ++ # ++ [[ $ruid/$euid == <1->/<1-> && $ruid != $euid ]] || ruid= euid= ++ [[ $rgid/$egid == <1->/<1-> && $rgid != $egid ]] || rgid= egid= ++ # ++ [[ -n $ruid && -n $euid ]] || { ++ ZTST_unimplemented='PRIVILEGED tests require unprivileged UID:EUID' ++ return 1 ++ } ++ [[ -n $rgid || -n $egid ]] || { ++ ZTST_unimplemented='PRIVILEGED tests require unprivileged GID:EGID' ++ return 1 ++ } ++ # ++ print -ru$ZTST_fd \ ++ "Using unprivileged UID $ruid, EUID $euid, GID $rgid, EGID $egid" ++ # ++ # Execute process with specified UID and EUID ++ # $1 => Real UID ++ # $2 => Effective UID ++ # $3 => Real GID ++ # $4 => Effective GID ++ # $5 ... => Command + args to execute (must NOT be a shell command string) ++ re_exec() { ++ perl -e ' ++ die("re_exec: not enough arguments") unless (@ARGV >= 5); ++ my ($ruid, $euid, $rgid, $egid, @cmd) = @ARGV; ++ foreach my $id ($ruid, $euid, $rgid, $egid) { ++ die("re_exec: invalid ID: $id") unless ($id =~ /^(-1|\d+)$/a); ++ } ++ $< = 0 + $ruid if ($ruid >= 0); ++ $> = 0 + $euid if ($euid >= 0); ++ $( = 0 + $rgid if ($rgid >= 0); ++ $) = 0 + $egid if ($egid >= 0); ++ exec(@cmd); ++ die("re_exec: exec failed: $!"); ++ ' -- "$@" ++ } ++ # ++ # Convenience wrapper for re_exec to call `zsh -c` ++ # -* ... => (optional) Command-line options to zsh ++ # $1 => Real UID ++ # $2 => Effective UID ++ # $3 => Real GID ++ # $4 => Effective GID ++ # $5 ... => zsh command string; multiple strings are joined by \n ++ re_zsh() { ++ local -a opts ++ while [[ $1 == -[A-Za-z-]* ]]; do ++ opts+=( $1 ) ++ shift ++ done ++ re_exec "$1" "$2" "$3" "$4" $ZTST_exe $opts -fc \ ++ "MODULE_PATH=${(q)MODULE_PATH}; ${(F)@[5,-1]}" ++ } ++ # ++ # Return one or more random unused UIDs ++ # $1 ... => Names of parameters to store UIDs in ++ get_unused_uid() { ++ while (( $# )); do ++ local i_=0 uid_= ++ until [[ -n $uid_ ]]; do ++ (( ++i_ > 99 )) && return 1 ++ uid_=$RANDOM ++ id $uid_ &> /dev/null || break ++ uid_= ++ done ++ : ${(P)1::=$uid_} ++ shift ++ done ++ } ++ ++%test ++ ++ re_zsh $ruid $ruid -1 -1 'echo $UID/$EUID $options[privileged]' ++ re_zsh $euid $euid -1 -1 'echo $UID/$EUID $options[privileged]' ++ re_zsh $ruid $euid -1 -1 'echo $UID/$EUID $options[privileged]' ++0q:PRIVILEGED automatically enabled when RUID != EUID ++>$ruid/$ruid off ++>$euid/$euid off ++>$ruid/$euid on ++ ++ re_zsh -1 -1 $rgid $rgid 'echo $GID/$EGID $options[privileged]' ++ re_zsh -1 -1 $egid $egid 'echo $GID/$EGID $options[privileged]' ++ re_zsh -1 -1 $rgid $egid 'echo $GID/$EGID $options[privileged]' ++0q:PRIVILEGED automatically enabled when RGID != EGID ++>$rgid/$rgid off ++>$egid/$egid off ++>$rgid/$egid on ++ ++ re_zsh $ruid $euid -1 -1 'unsetopt privileged; echo $UID/$EUID' ++0q:EUID set to RUID after disabling PRIVILEGED ++*?zsh:unsetopt:1: PRIVILEGED: supplementary group list not changed * ++*?zsh:unsetopt:1: can't change option: privileged ++>$ruid/$ruid ++ ++ re_zsh 0 $euid -1 -1 'unsetopt privileged && echo $UID/$EUID' ++0:RUID/EUID set to 0/0 when privileged after disabling PRIVILEGED ++>0/0 ++ ++ re_zsh $ruid $euid -1 -1 "unsetopt privileged; UID=$euid" || ++ re_zsh $ruid $euid -1 -1 "unsetopt privileged; EUID=$euid" ++1:not possible to regain EUID when unprivileged after disabling PRIVILEGED ++*?zsh:unsetopt:1: PRIVILEGED: supplementary group list not changed * ++*?zsh:unsetopt:1: can't change option: privileged ++*?zsh:1: failed to change user ID: * ++*?zsh:unsetopt:1: PRIVILEGED: supplementary group list not changed * ++*?zsh:unsetopt:1: can't change option: privileged ++*?zsh:1: failed to change effective user ID: * ++ ++ re_zsh -1 -1 $rgid $egid 'unsetopt privileged && echo $GID/$EGID' ++0q:EGID set to RGID after disabling PRIVILEGED ++>$rgid/$rgid ++ ++# This test also confirms that we can't revert to the original EUID's primary ++# GID, which initgroups() may reset the EGID to on some systems ++ re_zsh $ruid 0 $rgid 0 'unsetopt privileged; GID=0' || ++ re_zsh $ruid 0 $rgid 0 'unsetopt privileged; EGID=0' ++1:not possible to regain EGID when unprivileged after disabling PRIVILEGED ++*?zsh:1: failed to change group ID: * ++*?zsh:1: failed to change effective group ID: * ++ ++ local rruid ++ grep -qF '#define HAVE_INITGROUPS' $ZTST_testdir/../config.h || { ++ ZTST_skip='initgroups() not available' ++ return 1 ++ } ++ get_unused_uid rruid || { ++ ZTST_skip="Can't get unused UID" ++ return 1 ++ } ++ re_zsh $rruid 0 -1 -1 'unsetopt privileged' ++1:getpwuid() fails with non-existent RUID and 0 EUID ++*?zsh:unsetopt:1: can't drop privileges; failed to get user information * ++*?zsh:unsetopt:1: can't change option: privileged +diff --git a/Test/README b/Test/README +index d012277..726d68e 100644 +--- a/Test/README ++++ b/Test/README +@@ -6,6 +6,7 @@ scripts names: + C: shell commands with special syntax + D: substititution + E: options ++ P: privileged (needs super-user privileges) + V: modules + W: builtin interactive commands and constructs + X: line editing +-- +2.21.1 + diff --git a/SOURCES/dotzshrc b/SOURCES/dotzshrc new file mode 100644 index 0000000..9935bec --- /dev/null +++ b/SOURCES/dotzshrc @@ -0,0 +1,34 @@ +# +# .zshrc is sourced in interactive shells. +# It should contain commands to set up aliases, +# functions, options, key bindings, etc. +# + +autoload -U compinit +compinit + +#allow tab completion in the middle of a word +setopt COMPLETE_IN_WORD + +## keep background processes at full speed +#setopt NOBGNICE +## restart running processes on exit +#setopt HUP + +## history +#setopt APPEND_HISTORY +## for sharing history between zsh processes +#setopt INC_APPEND_HISTORY +#setopt SHARE_HISTORY + +## never ever beep ever +#setopt NO_BEEP + +## automatically decide when to page a list of completions +#LISTMAX=0 + +## disable mail checking +#MAILCHECK=0 + +# autoload -U colors +#colors diff --git a/SOURCES/zlogin.rhs b/SOURCES/zlogin.rhs new file mode 100644 index 0000000..5b7de4a --- /dev/null +++ b/SOURCES/zlogin.rhs @@ -0,0 +1,8 @@ +# +# /etc/zlogin and .zlogin are sourced in login shells. It should +# contain commands that should be executed only in +# login shells. It should be used to set the terminal +# type and run a series of external commands (fortune, +# msgs, from, etc). +# + diff --git a/SOURCES/zlogout.rhs b/SOURCES/zlogout.rhs new file mode 100644 index 0000000..3e78094 --- /dev/null +++ b/SOURCES/zlogout.rhs @@ -0,0 +1,7 @@ +# +# +# /etc/zlogout and ~/.zlogout are run when an interactive session ends +# +# + +clear diff --git a/SOURCES/zprofile.rhs b/SOURCES/zprofile.rhs new file mode 100644 index 0000000..03d316f --- /dev/null +++ b/SOURCES/zprofile.rhs @@ -0,0 +1,22 @@ +# +# /etc/zprofile and ~/.zprofile are run for login shells +# + +PATH="$PATH:$HOME/bin" +export PATH + +_src_etc_profile() +{ + # Make /etc/profile happier, and have possible ~/.zshenv options like + # NOMATCH ignored. + # + emulate -L ksh + + # source profile + if [ -f /etc/profile ]; then + source /etc/profile + fi +} +_src_etc_profile + +unset -f _src_etc_profile diff --git a/SOURCES/zshenv.rhs b/SOURCES/zshenv.rhs new file mode 100644 index 0000000..a6614d3 --- /dev/null +++ b/SOURCES/zshenv.rhs @@ -0,0 +1,14 @@ +# /etc/zsh/zshenv: system-wide .zshenv file for zsh(1). +# +# This file is sourced on all invocations of the shell. +# If the -f flag is present or if the NO_RCS option is +# set within this file, all other initialization files +# are skipped. +# +# This file should contain commands to set the command +# search path, plus other important environment variables. +# This file should not contain commands that produce +# output or assume the shell is attached to a tty. +# +# Global Order: zshenv, zprofile, zshrc, zlogin + diff --git a/SOURCES/zshrc.rhs b/SOURCES/zshrc.rhs new file mode 100644 index 0000000..5b3b92a --- /dev/null +++ b/SOURCES/zshrc.rhs @@ -0,0 +1,50 @@ +# +# /etc/zshrc is sourced in interactive shells. It +# should contain commands to set up aliases, functions, +# options, key bindings, etc. +# + +## shell functions +#setenv() { export $1=$2 } # csh compatibility + +# Set prompts +PROMPT='[%n@%m]%~%# ' # default prompt +#RPROMPT=' %~' # prompt for right side of screen + +# bindkey -v # vi key bindings +# bindkey -e # emacs key bindings +bindkey ' ' magic-space # also do history expansion on space + +# Provide pathmunge for /etc/profile.d scripts +pathmunge() +{ + if ! echo $PATH | /bin/grep -qE "(^|:)$1($|:)" ; then + if [ "$2" = "after" ] ; then + PATH=$PATH:$1 + else + PATH=$1:$PATH + fi + fi +} + +_src_etc_profile_d() +{ + # Make the *.sh things happier, and have possible ~/.zshenv options like + # NOMATCH ignored. + emulate -L ksh + + + # from bashrc, with zsh fixes + if [[ ! -o login ]]; then # We're not a login shell + for i in /etc/profile.d/*.sh; do + if [ -r "$i" ]; then + . $i + fi + done + unset i + fi +} +_src_etc_profile_d + +unset -f pathmunge _src_etc_profile_d + diff --git a/SPECS/zsh.spec b/SPECS/zsh.spec new file mode 100644 index 0000000..c9f75b3 --- /dev/null +++ b/SPECS/zsh.spec @@ -0,0 +1,469 @@ +Summary: Powerful interactive shell +Name: zsh +Version: 5.5.1 +Release: 6%{?dist}.2 +License: MIT +URL: http://zsh.sourceforge.net/ +Source0: https://downloads.sourceforge.net/%{name}/%{name}-%{version}.tar.xz +Source1: zlogin.rhs +Source2: zlogout.rhs +Source3: zprofile.rhs +Source4: zshrc.rhs +Source5: zshenv.rhs +Source6: dotzshrc + +# fix two security issues in shebang line parsing (CVE-2018-0502 CVE-2018-13259) +Patch1: 0001-zsh-5.5.1-CVE-2018-0502-CVE-2018-13259.patch + +# fix programming mistakes detected by static analysis (#1602743) +Patch2: 0002-zsh-5.5.1-static-analysis.patch + +# return non-zero exit status on nested parse error (#1654989) +Patch3: 0003-zsh-5.5.1-parse-error-exit-status.patch + +# drop privileges securely when unsetting PRIVILEGED option (CVE-2019-20044) +Patch4: 0004-zsh-5.5.1-CVE-2019-20044.patch + +BuildRequires: autoconf +BuildRequires: coreutils +BuildRequires: gawk +BuildRequires: gcc +BuildRequires: gdbm-devel +BuildRequires: libcap-devel +BuildRequires: ncurses-devel +BuildRequires: pcre-devel +BuildRequires: sed +BuildRequires: texi2html +BuildRequires: texinfo +Requires(post): info grep +Requires(preun): info +Requires(postun): coreutils grep + +# the hostname package is not available on RHEL-6 +%if 12 < 0%{?fedora} || 6 < 0%{?rhel} +BuildRequires: hostname +%else +# /bin and /usr/bin are separate directories on RHEL-6 +%define _bindir /bin +%endif + +Provides: /bin/zsh + +%description +The zsh shell is a command interpreter usable as an interactive login +shell and as a shell script command processor. Zsh resembles the ksh +shell (the Korn shell), but includes many enhancements. Zsh supports +command line editing, built-in spelling correction, programmable +command completion, shell functions (with autoloading), a history +mechanism, and more. + +%package html +Summary: Zsh shell manual in html format +BuildArch: noarch + +%description html +The zsh shell is a command interpreter usable as an interactive login +shell and as a shell script command processor. Zsh resembles the ksh +shell (the Korn shell), but includes many enhancements. Zsh supports +command line editing, built-in spelling correction, programmable +command completion, shell functions (with autoloading), a history +mechanism, and more. + +This package contains the Zsh manual in html format. + +%prep +%autosetup -p1 +autoreconf -fiv + +# enable parallel build +sed -e 's|^\.NOTPARALLEL|#.NOTPARALLEL|' -i 'Config/defs.mk.in' + +%build +# make build of run-time loadable modules work again (#1535422) +%undefine _strict_symbol_defs_build + +# make loading of module's dependencies work again (#1277996) +export LIBLDFLAGS='-z lazy' + +%configure \ + --enable-etcdir=%{_sysconfdir} \ + --with-tcsetpgrp \ + --enable-maildir-support \ + --enable-pcre + +# prevent the build from failing while running in parallel +make -C Src headers +make -C Src -f Makemod zsh{path,xmod}s.h version.h + +make %{?_smp_mflags} all html + +%check +# Run the testsuite +make check + +%install +%make_install install.info \ + fndir=%{_datadir}/%{name}/%{version}/functions \ + sitefndir=%{_datadir}/%{name}/site-functions \ + scriptdir=%{_datadir}/%{name}/%{version}/scripts \ + sitescriptdir=%{_datadir}/%{name}/scripts \ + runhelpdir=%{_datadir}/%{name}/%{version}/help + +rm -f $RPM_BUILD_ROOT%{_bindir}/zsh-%{version} +rm -f $RPM_BUILD_ROOT%{_infodir}/dir + +mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir} +for i in %{SOURCE1} %{SOURCE2} %{SOURCE3} %{SOURCE4} %{SOURCE5}; do + install -m 644 $i $RPM_BUILD_ROOT%{_sysconfdir}/"$(basename $i .rhs)" +done + +mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/skel +install -m 644 %{SOURCE6} $RPM_BUILD_ROOT%{_sysconfdir}/skel/.zshrc + +# This is just here to shut up rpmlint, and is very annoying. +# Note that we can't chmod everything as then rpmlint will complain about +# those without a she-bang line. +for i in checkmail harden run-help zcalc zkbd; do + sed -i -e 's!/usr/local/bin/zsh!%{_bindir}/zsh!' \ + $RPM_BUILD_ROOT%{_datadir}/zsh/%{version}/functions/$i + chmod +x $RPM_BUILD_ROOT%{_datadir}/zsh/%{version}/functions/$i +done + + +%post +if [ "$1" = 1 ]; then + if [ ! -f %{_sysconfdir}/shells ] ; then + echo "%{_bindir}/%{name}" > %{_sysconfdir}/shells + echo "/bin/%{name}" >> %{_sysconfdir}/shells + else + grep -q "^%{_bindir}/%{name}$" %{_sysconfdir}/shells || echo "%{_bindir}/%{name}" >> %{_sysconfdir}/shells + grep -q "^/bin/%{name}$" %{_sysconfdir}/shells || echo "/bin/%{name}" >> %{_sysconfdir}/shells + fi +fi + +if [ -f %{_infodir}/zsh.info.gz ]; then +# This is needed so that --excludedocs works. +/sbin/install-info %{_infodir}/zsh.info.gz %{_infodir}/dir \ + --entry="* zsh: (zsh). An enhanced bourne shell." +fi + + +%preun +if [ "$1" = 0 ] ; then + if [ -f %{_infodir}/zsh.info.gz ]; then + # This is needed so that --excludedocs works. + /sbin/install-info --delete %{_infodir}/zsh.info.gz %{_infodir}/dir \ + --entry="* zsh: (zsh). An enhanced bourne shell." + fi +fi + +%postun +if [ "$1" = 0 ] && [ -f %{_sysconfdir}/shells ] ; then + sed -i '\!^%{_bindir}/%{name}$!d' %{_sysconfdir}/shells + sed -i '\!^/bin/%{name}$!d' %{_sysconfdir}/shells +fi + + +%files +%doc README LICENCE Etc/BUGS Etc/CONTRIBUTORS Etc/FAQ FEATURES MACHINES +%doc NEWS Etc/zsh-development-guide Etc/completion-style-guide +%attr(755,root,root) %{_bindir}/zsh +%{_mandir}/*/* +%{_infodir}/* +%{_datadir}/zsh +%{_libdir}/zsh +%config(noreplace) %{_sysconfdir}/skel/.z* +%config(noreplace) %{_sysconfdir}/z* + +%files html +%doc Doc/*.html + +%changelog +* Tue Mar 03 2020 Kamil Dudka - 5.5.1-6.el8_1.2 +- improve printing of error messages introduced by the fix of CVE-2019-20044 + +* Mon Feb 24 2020 Kamil Dudka - 5.5.1-6.el8_1.1 +- drop privileges securely when unsetting PRIVILEGED option (CVE-2019-20044) + +* Mon Dec 17 2018 Kamil Dudka - 5.5.1-6 +- return non-zero exit status on nested parse error (#1654989) + +* Mon Nov 12 2018 Kamil Dudka - 5.5.1-5 +- fix programming mistakes detected by static analysis (#1602743) + +* Thu Oct 11 2018 Kamil Dudka - 5.5.1-4 +- fix two security issues in shebang line parsing (CVE-2018-0502 CVE-2018-13259) + +* Mon Jul 30 2018 Florian Weimer - 5.5.1-3 +- Rebuild with fixed binutils + +* Wed Jul 25 2018 Petr Kubat - 5.5.1-2 +- Rebuilt for gdbm + +* Tue Apr 17 2018 Kamil Dudka - 5.5.1-1 +- update to latest upstream release + +* Mon Apr 09 2018 Kamil Dudka - 5.5-1 +- update to latest upstream release, which fixes the following vulnerabilities: + CVE-2018-1100 - stack-based buffer overflow in utils.c:checkmailpath() + CVE-2018-1083 - stack-based buffer overflow in compctl.c:gen_matches_files() + CVE-2018-1071 - stack-based buffer overflow in exec.c:hashcmd() + +* Tue Mar 06 2018 Kamil Dudka - 5.4.2-7 +- avoid crash when copying empty hash table (CVE-2018-7549) +- avoid NULL dereference when using ${(PA)...} on an empty array (CVE-2018-7548) + +* Mon Feb 19 2018 Kamil Dudka - 5.4.2-6 +- add explicit BR for the gcc compiler + +* Fri Feb 09 2018 Fedora Release Engineering - 5.4.2-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Mon Jan 22 2018 Kamil Dudka - 5.4.2-4 +- make build of run-time loadable modules work again (#1535422) + +* Tue Jan 16 2018 Kamil Dudka - 5.4.2-3 +- rebuild against latest gdbm-devel (#1533176) + +* Wed Oct 04 2017 Kamil Dudka - 5.4.2-2 +- make the call depth limit configurable by $FUNCNEST (#1441092) + +* Mon Aug 28 2017 Kamil Dudka - 5.4.2-1 +- update to latest upstream release + +* Wed Aug 09 2017 Kamil Dudka - 5.4.1-1 +- update to latest upstream release + +* Tue Aug 01 2017 Kamil Dudka - 5.3.1-12 +- use %%make_install instead of %%makeinstall, which is deprecated +- modernize spec file (Group tag, %%clean, %%defattr) + +* Thu Jul 27 2017 Fedora Release Engineering - 5.3.1-11 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Fri Jul 07 2017 Kamil Dudka - 5.3.1-10 +- enable parallel build + +* Wed Jun 14 2017 Kamil Dudka - 5.3.1-9 +- fix unsafe use of a static buffer in history isearch (#1461483) + +* Thu Jun 08 2017 Kamil Dudka - 5.3.1-8 +- make the zsh-html subpackage noarch (#1459657) + +* Thu May 25 2017 Kamil Dudka - 5.3.1-7 +- drop unmaintained and undocumented zshprompt.pl script + +* Wed May 17 2017 Kamil Dudka - 5.3.1-6 +- drop workaround for broken terminals over serial port (#56353) + +* Thu May 11 2017 Kamil Dudka - 5.3.1-5 +- compile with -fconserve-stack to prevent stack overflow (#1441092) + +* Fri Mar 31 2017 Jason L Tibbitts III - 5.3.1-4 +- Add build deps on gdbm-devel and pcre-devel. Pass --enable-pcre to + configure. These should ensure that the pcre and gdbm modules are built. + (#1438009) + +* Sat Feb 11 2017 Fedora Release Engineering - 5.3.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Thu Dec 22 2016 Kamil Dudka - 5.3.1-2 +- do not require the hostname package when being built on RHEL-6 + +* Wed Dec 21 2016 Kamil Dudka - 5.3.1-1 +- Update to latest upstream release: Zsh 5.3.1 + +* Wed Dec 14 2016 Kamil Dudka - 5.3-2 +- drop zsh-4.3.6-8bit-prompts.patch which was superseeded by an upstream patch + (see http://www.zsh.org/mla/users/2007/msg00468.html for details) +- drop undocumented zsh-test-C02-dev_fd-mock.patch + +* Tue Dec 13 2016 Kamil Dudka - 5.3-1 +- apply patches automatically to ease maintenance +- Update to latest upstream release: Zsh 5.3 + +* Fri Feb 05 2016 Fedora Release Engineering - 5.2-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Wed Jan 27 2016 Kamil Dudka - 5.2-4 +- prevent zsh from crashing when printing the "out of memory" message (#1300958) + +* Thu Jan 07 2016 Jason L Tibbitts III - 5.2-3 +- Add patch to fix VCS_INFO_nbvsformats bug. + +* Fri Dec 25 2015 Adrien Vergé - 5.2-2 +- update zsh completion script for dnf to the latest upstream version + +* Thu Dec 03 2015 Kamil Dudka - 5.2-1 +- Update to latest upstream release: Zsh 5.2 + +* Thu Nov 05 2015 Kamil Dudka - 5.1.1-3 +- make loading of module's dependencies work again (#1277996) + +* Thu Oct 08 2015 Kamil Dudka - 5.1.1-2 +- fix crash in ksh mode with -n and $HOME (#1269883) + +* Mon Sep 14 2015 Kamil Dudka - 5.1.1-1 +- Update to latest upstream release: Zsh 5.1.1 + +* Mon Aug 31 2015 Kamil Dudka - 5.1-1 +- Update to latest upstream release: Zsh 5.1 +- remove outdated workarounds in %%check + +* Thu Jul 30 2015 Kamil Dudka - 5.0.8-6 +- fix handling of command substitution in math context + +* Wed Jul 22 2015 Kamil Dudka - 5.0.8-5 +- prevent infinite recursion in ihungetc() (#1245712) + +* Tue Jul 07 2015 Kamil Dudka - 5.0.8-4 +- backport completion for dnf (#1239337) + +* Thu Jul 02 2015 Kamil Dudka - 5.0.8-3 +- backport completion-related upstream fixes (#1238544) + +* Fri Jun 19 2015 Fedora Release Engineering - 5.0.8-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Wed Jun 03 2015 Kamil Dudka - 5.0.8-1 +- Update to latest upstream release: Zsh 5.0.8 + +* Fri May 22 2015 Kamil Dudka - 5.0.7-8 +- fix SIGSEGV of the syntax check in ksh emulation mode (#1222867) + +* Mon Apr 20 2015 Kamil Dudka - 5.0.7-7 +- fix SIGSEGV when handling heredocs and keyboard interrupt (#972624) +- queue signals when manipulating global state to avoid deadlock + +* Sun Jan 25 2015 Kamil Dudka - 5.0.7-6 +- use correct allocation function in the new 'cd' code (#1183238) + +* Fri Jan 23 2015 Kamil Dudka - 5.0.7-5 +- suppress a warning about closing an already closed file descriptor (#1184002) +- improve handling of NULL in the 'cd' built-in (#1183238) + +* Wed Nov 19 2014 Kamil Dudka - 5.0.7-4 +- update documentation of POSIX_JOBS in the zshoptions.1 man page (#1162198) + +* Tue Nov 18 2014 Kamil Dudka - 5.0.7-3 +- replace an incorrect comment in /etc/zshenv (#1164313) + +* Mon Nov 10 2014 Kamil Dudka - 5.0.7-2 +- make the wait built-in work for already exited processes (#1162198) + +* Wed Oct 08 2014 Dominic Hopf - 5.0.7-1 +- Update to latest upstream release: Zsh 5.0.7 + +* Thu Aug 28 2014 Dominic Hopf - 5.0.6-1 +- Update to latest upstream release: Zsh 5.0.6 + +* Mon Aug 18 2014 Fedora Release Engineering - 5.0.5-8 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild + +* Thu Jul 17 2014 Dominic Hopf - 5.0.5-7 +- apply upstream patch which fixes CPU load issue (RHBZ#1120424) + +* Wed Jul 09 2014 Adam Jackson 5.0.5-6 +- Fix missing 'fi' in %%post + +* Thu Jul 03 2014 Dominic Hopf - 5.0.5-5 +- improve handling of /etc/shells + +* Wed Jul 02 2014 Dominic Hopf - 5.0.5-4 +- fix FTBFS issue (RHBZ#1106713) +- remove individual _bindir setting; install to /usr/bin/ (RHBZ#1034060) +- require info package instead of /sbin/install-info binary + +* Sat Jun 07 2014 Fedora Release Engineering - 5.0.5-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Tue Apr 08 2014 Dominic Hopf - 5.0.5-1 +- Update to latest upstream release: Zsh 5.0.5 + +* Thu Jan 16 2014 James Antill - 5.0.2-8 +- Remove unneeded build require on tetex. + +* Sat Oct 26 2013 Dominic Hopf - 5.0.2-7 +- Require hostname package instead of /bin/hostname + +* Tue Oct 22 2013 Dominic Hopf - 5.0.2-6 +- remove systemd completion, it delivers it's own now (RHBZ#1022039) + +* Thu Aug 01 2013 Dominic Hopf - 5.0.2-5 +- update systemd completion (adds machinectl command) + +* Tue Jun 25 2013 Dominic Hopf - 5.0.2-4 +- up-to-date systemd completion (#949003) +- apply patch for building for aarch64 (#926864) + +* Mon Apr 15 2013 James Antill - 5.0.2-3 +- Fix the changelog dates. +- Fix the texi itemx bug. +- Resolves: bug#927863 + +* Fri Feb 15 2013 Fedora Release Engineering - 5.0.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild + +* Tue Jan 08 2013 Dominic Hopf - 5.0.2-1 +- Update to new upstream version: Zsh 5.0.2 + +* Wed Nov 21 2012 Dominic Hopf - 5.0.0-1 +- Update to new upstream version: Zsh 5.0.0 + +* Sun Jul 22 2012 Fedora Release Engineering - 4.3.17-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Sun Mar 04 2012 Dominic Hopf - 4.3.17-1 +- Update to new upstream version: Zsh 4.3.17 + +* Sat Jan 14 2012 Fedora Release Engineering - 4.3.15-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild + +* Sat Dec 24 2011 Dominic Hopf - 4.3.15-1 +- Update to new upstream version: Zsh 4.3.15 + +* Sat Dec 17 2011 Dominic Hopf - 4.3.14-2 +- change the License field to MIT (RHBZ#768548) + +* Sat Dec 10 2011 Dominic Hopf - 4.3.14-1 +- Update to new upstream version: Zsh 4.3.14 + +* Sat Dec 03 2011 Dominic Hopf - 4.3.13-1 +- Update to new upstream version: Zsh 4.3.13 + +* Sat Aug 13 2011 Dominic Hopf - 4.3.12-1 +- Update to new upstream version: Zsh 4.3.12 + +* Tue Feb 08 2011 Fedora Release Engineering - 4.3.11-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild + +* Thu Jan 20 2011 Christopher Ailon - 4.3.11-1 +- Rebase to upstream version 4.3.11 + +* Tue Dec 7 2010 Toshio Kuratomi - 4.3.10-6 +- Rebuild for FTBFS https://bugzilla.redhat.com/show_bug.cgi?id=631197 +- Remove deprecated PreReq, the packages aren't needed at runtime and they're + already in Requires(post,preun,etc): lines. + +* Mon Mar 22 2010 James Antill - 4.3.10-5 +- Add pathmunge to our /etc/zshrc, for profile.d compat. +- Resolves: bug#548960 + +* Fri Aug 7 2009 James Antill - 4.3.10-4 +- Allow --excludedocs command to work! +- Resolves: bug#515986 + +* Mon Jul 27 2009 Fedora Release Engineering - 4.3.10-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild + +* Mon Jul 20 2009 James Antill - 4.3.10-1 +- Import new upstream 4.3.10 + +* Wed Jun 10 2009 Karsten Hopp 4.3.9-4.1 +- skip D02glob test on s390, too + +* Mon Mar 2 2009 James Antill - 4.3.9-4 +- Remove D02glob testcase on ppc/ppc64, and hope noone cares + +* Wed Feb 25 2009 Fedora Release Engineering - 4.3.9-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild