]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
login: actually honour $HOME for chdir()
authorLennart Poettering <lennart@poettering.net>
Thu, 9 Jan 2025 22:57:32 +0000 (23:57 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 13 Jan 2025 17:08:06 +0000 (18:08 +0100)
When login is almost done it chdir()'s into the user's home directory.
It currently uses pwd->pw_dir, i.e the home dir actually assigned to the
user.

OTOH it explicitly allows overriding HOME (see comment in
init_environ()), and explicitly imports PAM's env vars (which quite
possibly contain $HOME) into its env block.

This patch makes sure that HOME is *properly* overridable via the env
var and via PAM: it actually honours it for the chdir().

(Background: I am working on a PAM module that under some conditions
would like to redirect the home dir to some other dir, and while I can
nicely set $HOME from the PAM module, it has not as much as effect as I
wish, because whie the rest of userspace respects it login so far didn't
and leaves the user in the wrong dir).

include/logindefs.h
lib/logindefs.c
login-utils/login.c
login-utils/lslogins.c

index fa6aa2f07b4c72d6d94e191d1813ad533775ea2d..24732ca70d331869fdbe8f2b3cd5e76034ee0f4f 100644 (file)
@@ -30,6 +30,6 @@ extern const char *getlogindefs_str(const char *name, const char *dflt);
 extern void free_getlogindefs_data(void);
 extern int logindefs_setenv(const char *name, const char *conf, const char *dflt);
 extern int effective_access(const char *path, int mode);
-extern int get_hushlogin_status(struct passwd *pwd, int force_check);
+extern int get_hushlogin_status(struct passwd *pwd, const char *override_home, int force_check);
 
 #endif /* UTIL_LINUX_LOGINDEFS_H */
index 5f76ebd0f5b23ce61f5660ae91d4782b123eef4d..7b69599214566d6cba0ebb4b8773348044ec0fb2 100644 (file)
@@ -467,7 +467,7 @@ int effective_access(const char *path, int mode)
  * BSD setreuid().
  */
 
-int get_hushlogin_status(struct passwd *pwd, int force_check)
+int get_hushlogin_status(struct passwd *pwd, const char *override_home, int force_check)
 {
        const char *files[] = { _PATH_HUSHLOGINS, _PATH_HUSHLOGIN, NULL };
        const char *file;
@@ -520,7 +520,7 @@ int get_hushlogin_status(struct passwd *pwd, int force_check)
                if (strlen(pwd->pw_dir) + strlen(file) + 2 > sizeof(buf))
                        continue;
 
-               if (snprintf(buf, sizeof(buf), "%s/%s", pwd->pw_dir, file) < 0)
+               if (snprintf(buf, sizeof(buf), "%s/%s", override_home ?: pwd->pw_dir, file) < 0)
                        continue;
 
                if (force_check) {
index 1d7dd3a679107d3fd8fdddf2af96af170c4da2d7..321f9d6ce321bf5a04fd965cc6a38b718bee3083 100644 (file)
@@ -1493,7 +1493,7 @@ int main(int argc, char **argv)
                }
        }
 
-       cxt.quiet = get_hushlogin_status(pwd, 1) == 1 ? 1 : 0;
+       cxt.quiet = get_hushlogin_status(pwd, pam_getenv(cxt.pamh, "HOME"), 1) == 1 ? 1 : 0;
 
        /*
         * Open PAM session (after successful authentication and account check).
@@ -1543,8 +1543,9 @@ int main(int argc, char **argv)
        }
 
        /* wait until here to change directory! */
-       if (chdir(pwd->pw_dir) < 0) {
-               warn(_("%s: change directory failed"), pwd->pw_dir);
+       const char *home = getenv("HOME") ?: pwd->pw_dir;
+       if (chdir(home) < 0) {
+               warn(_("%s: change directory failed"), home);
 
                if (!getlogindefs_bool("DEFAULT_HOME", 1))
                        exit(0);
index 7c1ba689310f8e5f428e86d31d7baff0041a2abc..ebfdf6a79d6d606f27d8aa0ee2a8c9ef8ccce6ee 100644 (file)
@@ -904,7 +904,7 @@ static struct lslogins_user *get_user_info(struct lslogins_control *ctl, const c
                        }
                        break;
                case COL_HUSH_STATUS:
-                       user->hushed = get_hushlogin_status(pwd, 0);
+                       user->hushed = get_hushlogin_status(pwd, /* override_home= */ NULL, 0);
                        if (user->hushed == -1)
                                user->hushed = STATUS_UNKNOWN;
                        break;