From 684f93e783e3e9d1c1833934bcfc25a16f6b7149 Mon Sep 17 00:00:00 2001 From: Lily Ballard Date: Tue, 29 Oct 2024 23:13:13 -0700 Subject: [PATCH] libstore: ignore broken symlinks in ssl-cert-file default Also tweak `pathAccessible` to ignore other relevant errors too. It was documented as ignoring permission errors but it was only ignoring `EPERM`, which comes from the darwin sandbox, and not ignoring `EACCESS` which is the real permission error. I figured it also makes sense to ignore `ELOOP`. Fixes: https://git.lix.systems/lix-project/lix/issues/560 Change-Id: Ibb849b68d07386eb80afb52b57f7d12b3a48a202 --- doc/manual/rl-next/ssl-cert-symlink.md | 12 ++++++++++ src/libstore/globals.cc | 2 +- src/libutil/file-system.cc | 32 ++++++++++++++++++++++---- src/libutil/file-system.hh | 11 +++++++-- 4 files changed, 49 insertions(+), 8 deletions(-) create mode 100644 doc/manual/rl-next/ssl-cert-symlink.md diff --git a/doc/manual/rl-next/ssl-cert-symlink.md b/doc/manual/rl-next/ssl-cert-symlink.md new file mode 100644 index 000000000..63bff61e1 --- /dev/null +++ b/doc/manual/rl-next/ssl-cert-symlink.md @@ -0,0 +1,12 @@ +--- +synopsis: Ignore broken `/etc/ssl/certs/ca-certificates.crt` symlink +issues: [fj#560] +cls: [2144] +category: Fixes +credits: lilyball +--- + +[`ssl-cert-file`](@docroot@/command-ref/conf-file.md#conf-ssl-cert-file) now checks its default +value for a broken symlink before using it. This fixes a problem on macOS where uninstalling +nix-darwin may leave behind a broken symlink at `/etc/ssl/certs/ca-certificates.crt` that was +stopping Lix from using the cert at `/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt`. diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index 9377ac936..cf72cfcbc 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -263,7 +263,7 @@ bool Settings::isWSL1() Path Settings::getDefaultSSLCertFile() { for (auto & fn : {"/etc/ssl/certs/ca-certificates.crt", "/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt"}) - if (pathAccessible(fn)) return fn; + if (pathAccessible(fn, true)) return fn; return ""; } diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index c4ffb1d0c..cc87375f8 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -214,6 +214,19 @@ struct stat lstat(const Path & path) return st; } +std::optional maybeStat(const Path & path) +{ + std::optional st{std::in_place}; + if (stat(path.c_str(), &*st)) + { + if (errno == ENOENT || errno == ENOTDIR) + st.reset(); + else + throw SysError("getting status of '%s'", path); + } + return st; +} + std::optional maybeLstat(const Path & path) { std::optional st{std::in_place}; @@ -232,14 +245,23 @@ bool pathExists(const Path & path) return maybeLstat(path).has_value(); } -bool pathAccessible(const Path & path) +bool pathAccessible(const Path & path, bool resolveSymlinks) { try { - return pathExists(path); + return resolveSymlinks ? maybeStat(path).has_value() : pathExists(path); } catch (SysError & e) { - // swallow EPERM - if (e.errNo == EPERM) return false; - throw; + switch (e.errNo) { + case EPERM: + // operation not permitted error, can occur in darwin sandbox + case EACCES: + // permission error + case ELOOP: + // path component is a looping symlink + // this seems like a reasonable condition to handle as well + return false; + default: + throw; + } } } diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 2547c63b5..08ec54632 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -123,6 +123,12 @@ bool isDirOrInDir(std::string_view path, std::string_view dir); struct stat stat(const Path & path); struct stat lstat(const Path & path); +/** + * `stat` the given path if it exists. + * @return std::nullopt if the path doesn't exist, or an optional containing the result of `stat` otherwise + */ +std::optional maybeStat(const Path & path); + /** * `lstat` the given path if it exists. * @return std::nullopt if the path doesn't exist, or an optional containing the result of `lstat` otherwise @@ -137,10 +143,11 @@ bool pathExists(const Path & path); /** * A version of pathExists that returns false on a permission error. * Useful for inferring default paths across directories that might not - * be readable. + * be readable. Optionally resolves symlinks to determine if the real + * path exists. * @return true iff the given path can be accessed and exists */ -bool pathAccessible(const Path & path); +bool pathAccessible(const Path & path, bool resolveSymlinks = false); /** * Read the contents (target) of a symbolic link. The result is not