1
0
mirror of https://git.savannah.gnu.org/git/guix.git synced 2026-04-06 21:20:33 +02:00

gnu: openssh: Adapt for root-less guix store.

Fixes <https://issues.guix.gnu.org/78067>.

Previously sshd would use /gnu/store/…-openssh-…/var/empty as its
PRIVSEP_PATH.  However, when using the unprivileged daemon, that
directory would belong to guix-daemon:guix-daemon, leading to this
error:

  sshd[234]: fatal: /gnu/store/…-openssh-10.0p1/var/empty must be owned by root and not group or world-writable.

Fix that by switching to /var/empty.

* gnu/packages/patches/openssh-trust-guix-store-directory.patch
(openssh): Adjust to trust files in guix store owned by guix-daemon.
* gnu/packages/ssh.scm (openssh)[arguments]:  Remove ‘reset-/var/empty’
phase; change ‘install’ phase to not create PRIVSEP_PATH..  Append
ending slash when substituting STORE_DIRECTORY.

Change-Id: I3bd01f8b9d6406e3b886eea8f4b8c265a51cc72f
Reported-by: Zack Weinberg <zack@owlfolio.org>
Signed-off-by: Ludovic Courtès <ludo@gnu.org>
This commit is contained in:
Sergey Trofimov
2025-04-23 16:13:10 +02:00
committed by Ludovic Courtès
parent d9e0bb44c0
commit eab097c682
2 changed files with 51 additions and 30 deletions

View File

@@ -3,20 +3,20 @@ From: Alexey Abramov <levenson@mmer.org>
Date: Fri, 22 Apr 2022 11:32:15 +0200
Subject: [PATCH] Trust guix store directory
To be able to execute binaries defined in OpenSSH configuration, we
need to tell OpenSSH that we can trust Guix store objects. safe_path
procedure takes a canonical path and for each component, walking
upwards, checks ownership and permissions constrains which are: must
be owned by root, not writable by group or others.
To be able to execute binaries defined in OpenSSH configuration, we need to
tell OpenSSH that we can trust Guix store objects. safe_path procedure is
patched to assume files in Guix store to be safe. Additionally configuration
file placed in Guix store is assumed to be safe to load.
---
misc.c | 5 +++++
1 file changed, 5 insertions(+)
misc.c | 6 ++++++
readconf.c | 7 ++++---
2 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/misc.c b/misc.c
index 0134d69..7131d5e 100644
index dd0bd032a..6b866464c 100644
--- a/misc.c
+++ b/misc.c
@@ -2146,6 +2146,7 @@ int
@@ -2254,6 +2254,7 @@ int
safe_path(const char *name, struct stat *stp, const char *pw_dir,
uid_t uid, char *err, size_t errlen)
{
@@ -24,17 +24,42 @@ index 0134d69..7131d5e 100644
char buf[PATH_MAX], homedir[PATH_MAX];
char *cp;
int comparehome = 0;
@@ -2178,6 +2179,10 @@ safe_path(const char *name, struct stat *stp, const char *pw_dir,
}
strlcpy(buf, cp, sizeof(buf));
+ /* If we are past the Guix store then we can stop */
+ if (strcmp(guix_store, buf) == 0)
+ break;
@@ -2271,6 +2272,11 @@ safe_path(const char *name, struct stat *stp, const char *pw_dir,
snprintf(err, errlen, "%s is not a regular file", buf);
return -1;
}
+ // the file is trusted when it is located in guix store
+ if (strncmp(buf, guix_store, strlen(guix_store)) == 0) {
+ return 0;
+ }
+
if (stat(buf, &st) == -1 ||
(!platform_sys_dir_uid(st.st_uid) && st.st_uid != uid) ||
(st.st_mode & 022) != 0) {
if ((!platform_sys_dir_uid(stp->st_uid) && stp->st_uid != uid) ||
(stp->st_mode & 022) != 0) {
snprintf(err, errlen, "bad ownership or modes for file %s",
diff --git a/readconf.c b/readconf.c
index 7cbe7d2c2..40a5f1ace 100644
--- a/readconf.c
+++ b/readconf.c
@@ -2566,6 +2566,7 @@ read_config_file_depth(const char *filename, struct passwd *pw,
{
FILE *f;
char *line = NULL;
+ char errmsg[512];
size_t linesize = 0;
int linenum;
int bad_options = 0;
@@ -2581,9 +2582,9 @@ read_config_file_depth(const char *filename, struct passwd *pw,
if (fstat(fileno(f), &sb) == -1)
fatal("fstat %s: %s", filename, strerror(errno));
- if (((sb.st_uid != 0 && sb.st_uid != getuid()) ||
- (sb.st_mode & 022) != 0))
- fatal("Bad owner or permissions on %s", filename);
+ if (safe_path(filename, &sb, pw->pw_dir, pw->pw_uid, errmsg, sizeof(errmsg)) != 0) {
+ fatal(errmsg);
+ }
}
debug("Reading configuration data %.200s", filename);
--
2.34.0
2.49.0

View File

@@ -275,16 +275,11 @@ a server that supports the SSH-2 protocol.")
'()))
#:phases
#~(modify-phases %standard-phases
(add-after 'configure 'reset-/var/empty
(lambda _
(substitute* "Makefile"
(("PRIVSEP_PATH=/var/empty")
(string-append "PRIVSEP_PATH=" #$output "/var/empty")))))
(add-after 'configure 'set-store-location
(lambda _
(substitute* "misc.c"
(("@STORE_DIRECTORY@")
(string-append "\"" (%store-directory) "\"")))))
(string-append "\"" (%store-directory) "/\"")))))
(add-before 'check 'patch-tests
(lambda _
(substitute* "regress/test-exec.sh"
@@ -297,9 +292,10 @@ a server that supports the SSH-2 protocol.")
(string-append pre post)))))
(replace 'install
(lambda* (#:key (make-flags '()) #:allow-other-keys)
;; Install without host keys and system configuration files. This
;; will install /var/empty to the store, which is needed by the
;; system openssh-service-type.
;; Don't create /var/empty.
(substitute* "Makefile"
((".*MKDIR_P.*PRIVSEP_PATH.*") ""))
;; Install without host keys and system configuration files.
(apply invoke "make" "install-nosysconf" make-flags)
(with-directory-excursion "contrib"
(chmod "ssh-copy-id" #o555)