From 3f87945f75686edc7115d3cc785dd0368c7daed1 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Mon, 29 Apr 2024 10:54:42 +0200 Subject: [PATCH] confd: map new users to existing home directories UNIX home directories are persistent across reboots, but user accounts are not. If multiple users exist in startup-config and one or more are removed, we must use the UID of their $HOME on the next reboot to ensure they do not lose their files. If a home directory exists and its UID is already in /etc/passwd we have triggered an unsupported use-case and must remove the home directory on disk before recreating it empty. This should not happen, but may occur on upgrades from a time before UIDs started at 1000 and instead shared the UID range with reserved system accounts. Signed-off-by: Joachim Wiberg --- src/confd/src/ietf-system.c | 62 ++++++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/src/confd/src/ietf-system.c b/src/confd/src/ietf-system.c index bacb6b8a..afe7e7e8 100644 --- a/src/confd/src/ietf-system.c +++ b/src/confd/src/ietf-system.c @@ -671,6 +671,35 @@ static char *sys_find_usable_shell(sr_session_ctx_t *sess, char *name) return shell; } +static uid_t sys_home_exists(char *user) +{ + char path[strlen(user) + 10]; + struct stat st; + + snprintf(path, sizeof(path), "/home/%s", user); + if (stat(path, &st)) + return 0; + + return st.st_uid; +} + +static int sys_uid_busy(char *user, uid_t uid) +{ + struct passwd *pw; + int rc = 0; + + setpwent(); + while ((pw = getpwent())) { + if (pw->pw_uid != uid) + continue; + if (strcmp(pw->pw_name, user)) + rc = -1; + } + endpwent(); + + return rc; +} + static int sys_del_user(char *user) { char *args[] = { @@ -692,11 +721,39 @@ static int sys_del_user(char *user) static int sys_add_new_user(sr_session_ctx_t *sess, char *name) { char *shell = sys_find_usable_shell(sess, name); - char *args[] = { + char uid_str[10]; + char *eargs[] = { + "adduser", "-d", "-s", shell, "-u", NULL, "-H", name, NULL + }; + char *nargs[] = { "adduser", "-d", "-s", shell, name, NULL }; + bool do_chown = false; + char **args; + uid_t uid; int err; + uid = sys_home_exists(name); + if (uid) { + if (sys_uid_busy(name, uid)) { + /* Exists but owned by someone else. */ + ERROR("Creating user %s, /home/%s existed but was owned by another uid (%d)", + name, name, uid); + ERROR("Cleaning up stale /home/%s", name); + systemf("rm -rf /home/%s", name); + args = nargs; + } else { + /* Not in passwd or owned by us already */ + do_chown = true; + + NOTE("Reusing uid %d and /home/%s for new user %s", uid, name, name); + snprintf(uid_str, sizeof(uid_str), "%d", uid); + eargs[5] = uid_str; + args = eargs; + } + } else + args = nargs; + /** * The Busybox implementation of 'adduser -d' sets the password * to "*", which prevents new users from logging in until Augeas @@ -708,7 +765,10 @@ static int sys_add_new_user(sr_session_ctx_t *sess, char *name) ERROR("Failed creating new user \"%s\"\n", name); return SR_ERR_SYS; } + NOTE("New user \"%s\" created\n", name); + if (do_chown) + systemf("chown -R %s:%s /home/%s", name, name, name); /* * OpenSSH in Infix has been set up to use /var/run/sshd/%s.keys