From 7191e90f6f5b47b8b03d6e4e18afd2e4bbf90077 Mon Sep 17 00:00:00 2001 From: James Antill Date: Thu, 26 May 2022 16:25:05 -0400 Subject: [PATCH] Auto sync2gitlab import of zsh-5.5.1-9.el8.src.rpm --- .gitignore | 1 + ...h-5.5.1-CVE-2018-0502-CVE-2018-13259.patch | 148 ++ 0002-zsh-5.5.1-static-analysis.patch | 266 ++++ 0003-zsh-5.5.1-parse-error-exit-status.patch | 28 + 0004-zsh-5.5.1-CVE-2019-20044.patch | 1378 +++++++++++++++++ 0005-zsh-5.5.1-CVE-2021-45444.patch | 45 + EMPTY | 1 - dotzshrc | 34 + sources | 1 + zlogin.rhs | 8 + zlogout.rhs | 7 + zprofile.rhs | 22 + zsh.spec | 475 ++++++ zshenv.rhs | 14 + zshrc.rhs | 50 + 15 files changed, 2477 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 0001-zsh-5.5.1-CVE-2018-0502-CVE-2018-13259.patch create mode 100644 0002-zsh-5.5.1-static-analysis.patch create mode 100644 0003-zsh-5.5.1-parse-error-exit-status.patch create mode 100644 0004-zsh-5.5.1-CVE-2019-20044.patch create mode 100644 0005-zsh-5.5.1-CVE-2021-45444.patch delete mode 100644 EMPTY create mode 100644 dotzshrc create mode 100644 sources create mode 100644 zlogin.rhs create mode 100644 zlogout.rhs create mode 100644 zprofile.rhs create mode 100644 zsh.spec create mode 100644 zshenv.rhs create mode 100644 zshrc.rhs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..312f675 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/zsh-5.5.1.tar.xz diff --git a/0001-zsh-5.5.1-CVE-2018-0502-CVE-2018-13259.patch b/0001-zsh-5.5.1-CVE-2018-0502-CVE-2018-13259.patch new file mode 100644 index 0000000..0345288 --- /dev/null +++ b/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/0002-zsh-5.5.1-static-analysis.patch b/0002-zsh-5.5.1-static-analysis.patch new file mode 100644 index 0000000..4bec6e1 --- /dev/null +++ b/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/0003-zsh-5.5.1-parse-error-exit-status.patch b/0003-zsh-5.5.1-parse-error-exit-status.patch new file mode 100644 index 0000000..6605665 --- /dev/null +++ b/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/0004-zsh-5.5.1-CVE-2019-20044.patch b/0004-zsh-5.5.1-CVE-2019-20044.patch new file mode 100644 index 0000000..96bfcbb --- /dev/null +++ b/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/0005-zsh-5.5.1-CVE-2021-45444.patch b/0005-zsh-5.5.1-CVE-2021-45444.patch new file mode 100644 index 0000000..5de9e72 --- /dev/null +++ b/0005-zsh-5.5.1-CVE-2021-45444.patch @@ -0,0 +1,45 @@ +From 9ce87af4ced4e21258e6003f1fb65b05ca5a7d14 Mon Sep 17 00:00:00 2001 +From: Oliver Kiddle +Date: Wed, 15 Dec 2021 01:56:40 +0100 +Subject: [PATCH] security/41: Don't perform PROMPT_SUBST evaluation on %F/%K + arguments + +Mitigates CVE-2021-45444 + +Upstream-commit: c187154f47697cdbf822c2f9d714d570ed4a0fd1 +Signed-off-by: Kamil Dudka +--- + Src/prompt.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/Src/prompt.c b/Src/prompt.c +index 95da525..1368f8e 100644 +--- a/Src/prompt.c ++++ b/Src/prompt.c +@@ -244,6 +244,12 @@ parsecolorchar(int arg, int is_fg) + bv->fm += 2; /* skip over F{ */ + if ((ep = strchr(bv->fm, '}'))) { + char oc = *ep, *col, *coll; ++ int ops = opts[PROMPTSUBST], opb = opts[PROMPTBANG]; ++ int opp = opts[PROMPTPERCENT]; ++ ++ opts[PROMPTPERCENT] = 1; ++ opts[PROMPTSUBST] = opts[PROMPTBANG] = 0; ++ + *ep = '\0'; + /* expand the contents of the argument so you can use + * %v for example */ +@@ -252,6 +258,10 @@ parsecolorchar(int arg, int is_fg) + arg = match_colour((const char **)&coll, is_fg, 0); + free(col); + bv->fm = ep; ++ ++ opts[PROMPTSUBST] = ops; ++ opts[PROMPTBANG] = opb; ++ opts[PROMPTPERCENT] = opp; + } else { + arg = match_colour((const char **)&bv->fm, is_fg, 0); + if (*bv->fm != '}') +-- +2.34.1 + diff --git a/EMPTY b/EMPTY deleted file mode 100644 index 0519ecb..0000000 --- a/EMPTY +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/dotzshrc b/dotzshrc new file mode 100644 index 0000000..9935bec --- /dev/null +++ b/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 b/sources new file mode 100644 index 0000000..97eb4a1 --- /dev/null +++ b/sources @@ -0,0 +1 @@ +SHA512 (zsh-5.5.1.tar.xz) = 37156ff682f8ec337526c8f93a0a2031000bb0b4b2a59e04158aae3f0fb667f4d9898d6f2bae8abe3dd2839f760f2e335484e016564a4a59097273a1f64da0c2 diff --git a/zlogin.rhs b/zlogin.rhs new file mode 100644 index 0000000..5b7de4a --- /dev/null +++ b/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/zlogout.rhs b/zlogout.rhs new file mode 100644 index 0000000..3e78094 --- /dev/null +++ b/zlogout.rhs @@ -0,0 +1,7 @@ +# +# +# /etc/zlogout and ~/.zlogout are run when an interactive session ends +# +# + +clear diff --git a/zprofile.rhs b/zprofile.rhs new file mode 100644 index 0000000..03d316f --- /dev/null +++ b/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/zsh.spec b/zsh.spec new file mode 100644 index 0000000..bd37d4e --- /dev/null +++ b/zsh.spec @@ -0,0 +1,475 @@ +Summary: Powerful interactive shell +Name: zsh +Version: 5.5.1 +Release: 9%{?dist} +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 + +# do not perform PROMPT_SUBST evaluation on %F/%K arguments (CVE-2021-45444) +Patch5: 0005-zsh-5.5.1-CVE-2021-45444.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 Feb 22 2022 Kamil Dudka - 5.5.1-9 +- do not perform PROMPT_SUBST evaluation on %F/%K arguments (CVE-2021-45444) + +* Tue Mar 03 2020 Kamil Dudka - 5.5.1-8 +- improve printing of error messages introduced by the fix of CVE-2019-20044 + +* Mon Feb 24 2020 Kamil Dudka - 5.5.1-7 +- 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 diff --git a/zshenv.rhs b/zshenv.rhs new file mode 100644 index 0000000..a6614d3 --- /dev/null +++ b/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/zshrc.rhs b/zshrc.rhs new file mode 100644 index 0000000..5b3b92a --- /dev/null +++ b/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 +