From 92ca844d7a0492ecc1464a9bf18eecd72ac6e907 Mon Sep 17 00:00:00 2001
From: Christian Breunig <christian@breunig.cc>
Date: Fri, 29 Dec 2023 20:54:29 +0100
Subject: login: T5875: restore home directory permissions when re-adding user
 account

After deleting a user account and working with a newly added account, we see
that after rebooting in the previously saved configuration, the user is
re-added but it's home directory might have an old UID set on the filesystem.

This is due to the fact that vyos config does not store UIDs. When adding a
user account to the system we now check if the home directory already exists
and adjust the ownership to the new UID.

(cherry picked from commit 3c990f49e2bf9347bd2cc478995baa995ee822fd)
---
 python/vyos/utils/file.py     | 23 ++++++++++++++++++-----
 src/conf_mode/system-login.py |  6 +++++-
 2 files changed, 23 insertions(+), 6 deletions(-)

diff --git a/python/vyos/utils/file.py b/python/vyos/utils/file.py
index 9f27a7fb9..0818f1b81 100644
--- a/python/vyos/utils/file.py
+++ b/python/vyos/utils/file.py
@@ -83,21 +83,34 @@ def read_json(fname, defaultonfailure=None):
             return defaultonfailure
         raise e
 
-def chown(path, user, group):
+def chown(path, user=None, group=None, recursive=False):
     """ change file/directory owner """
     from pwd import getpwnam
     from grp import getgrnam
 
-    if user is None or group is None:
+    if user is None and group is None:
         return False
 
     # path may also be an open file descriptor
     if not isinstance(path, int) and not os.path.exists(path):
         return False
 
-    uid = getpwnam(user).pw_uid
-    gid = getgrnam(group).gr_gid
-    os.chown(path, uid, gid)
+    # keep current value if not specified otherwise
+    uid = -1
+    gid = -1
+
+    if user:
+        uid = getpwnam(user).pw_uid
+    if group:
+        gid = getgrnam(group).gr_gid
+
+    if recursive:
+        for dirpath, dirnames, filenames in os.walk(path):
+            os.chown(dirpath, uid, gid)
+            for filename in filenames:
+                os.chown(os.path.join(dirpath, filename), uid, gid)
+    else:
+        os.chown(path, uid, gid)
     return True
 
 
diff --git a/src/conf_mode/system-login.py b/src/conf_mode/system-login.py
index cd85a5066..95021c8fd 100755
--- a/src/conf_mode/system-login.py
+++ b/src/conf_mode/system-login.py
@@ -29,6 +29,7 @@ from vyos.defaults import directories
 from vyos.template import render
 from vyos.template import is_ipv4
 from vyos.utils.dict import dict_search
+from vyos.utils.file import chown
 from vyos.utils.process import cmd
 from vyos.utils.process import call
 from vyos.utils.process import rc_cmd
@@ -334,13 +335,16 @@ def apply(login):
             command += f' --groups frr,frrvty,vyattacfg,sudo,adm,dip,disk {user}'
             try:
                 cmd(command)
-
                 # we should not rely on the value stored in
                 # user_config['home_directory'], as a crazy user will choose
                 # username root or any other system user which will fail.
                 #
                 # XXX: Should we deny using root at all?
                 home_dir = getpwnam(user).pw_dir
+                # T5875: ensure UID is properly set on home directory if user is re-added
+                if os.path.exists(home_dir):
+                    chown(home_dir, user=user, recursive=True)
+
                 render(f'{home_dir}/.ssh/authorized_keys', 'login/authorized_keys.j2',
                        user_config, permission=0o600,
                        formater=lambda _: _.replace("&quot;", '"'),
-- 
cgit v1.2.3