From 1b8d886206ddb2f516f2448edac43f03054aca6c Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 22 May 2023 13:13:02 +0200 Subject: [PATCH] Do not rely on `getenv ("HOME")`'s path conversion In the very early code path where `dll_crt0_1 ()` calls `user_shared->initialize ()`, the Cygwin runtime calls `internal_pwsid ()` to initialize the user name in preparation for reading the `fstab` file. In case `db_home: env` is defined in `/etc/nsswitch.conf`, we need to look at the environment variable `HOME` and use it, if set. When all of this happens, though, the `pinfo_init ()` function has had no chance to run yet (and therefore, `environ_init ()`). At this stage, therefore, `getenv ()`'s `findenv_func ()` call still finds `getearly ()` and we get the _verbatim_ value of `HOME`. That is, the Windows form. But we need the "POSIX" form. To add insult to injury, later calls to `getpwuid (getuid ())` will receive a cached version of the home directory via `cygheap->pg.pwd_cache.win.find_user ()` thanks to the first `internal_pwsid ()` call caching the result via `add_user_from_cygserver ()`, read: we will never receive the converted `HOME` but always the Windows variant. So, contrary to the assumptions made in 27376c60a9 (Allow deriving the current user's home directory via the HOME variable, 2023-03-28), we cannot assume that `getenv ("HOME")` returned a "POSIX" path. This is a real problem. Even setting aside that common callers of `getpwuid ()` (such as OpenSSH) are unable to handle Windows paths in the `pw_dir` attribute, the Windows path never makes it back to the caller unscathed. The value returned from `fetch_home_env ()` is not actually used as-is. Instead, the `fetch_account_from_windows ()` method uses it to write a pseudo `/etc/passwd`-formatted line that is _then_ parsed via the `pwdgrp::parse_passwd ()` method which sees no problem with misinterpreting the colon after the drive letter as a field separator of that `/etc/passwd`-formatted line, and instead of a Windows path, we now have a mere drive letter. Let's detect when the `HOME` value is still in Windows format in `fetch_home_env ()`, and convert it in that case. For good measure, interpret this "Windows format" not only to include absolute paths with drive prefixes, but also UNC paths. Signed-off-by: Johannes Schindelin --- winsup/cygwin/uinfo.cc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/winsup/cygwin/uinfo.cc b/winsup/cygwin/uinfo.cc index 8716811b7e..f963841789 100644 --- a/winsup/cygwin/uinfo.cc +++ b/winsup/cygwin/uinfo.cc @@ -934,7 +934,13 @@ fetch_home_env (void) /* If `HOME` is set, prefer it */ const char *home = getenv ("HOME"); if (home) - return strdup (home); + { + /* In the very early code path of `user_info::initialize ()`, the value + of the environment variable `HOME` is still in its Windows form. */ + if (isdrive (home) || home[0] == '\\') + return (char *) cygwin_create_path (CCP_WIN_A_TO_POSIX, home); + return strdup (home); + } /* If `HOME` is unset, fall back to `HOMEDRIVE``HOMEPATH` (without a directory separator, as `HOMEPATH` starts with one). */