File pam_mkhomedir-Use-vendordir-when-defined.patch of Package pam

From c4a53492e1b7aebcf7a65a778d9e3a78f196d117 Mon Sep 17 00:00:00 2001
From: vlefebvre <valentin.lefebvre@suse.com>
Date: Tue, 15 Jul 2025 15:42:50 +0200
Subject: [PATCH] pam_mkhomedir: Use vendordir when defined

* When the configuration is split between /etc and vendordir,
  pam_mkhomedir now takes care of the distribution-provided
  skeleton directory.

* mkhomedir_helper gets a new optional argument: vendordir.
  If specified, the home directory will be populated by contents of
  the directory specified by skeldir argument, followed by contents
  of the directory specified by vendordir argument.

Signed-off-by: vlefebvre <valentin.lefebvre@suse.com>
---
 modules/pam_mkhomedir/mkhomedir_helper.8.xml | 12 ++-
 modules/pam_mkhomedir/mkhomedir_helper.c     | 91 +++++++++++++-------
 modules/pam_mkhomedir/pam_mkhomedir.8.xml    |  8 +-
 modules/pam_mkhomedir/pam_mkhomedir.c        | 14 ++-
 4 files changed, 88 insertions(+), 37 deletions(-)

diff --git a/modules/pam_mkhomedir/mkhomedir_helper.8.xml b/modules/pam_mkhomedir/mkhomedir_helper.8.xml
index 0f4c4b40..87b83a40 100644
--- a/modules/pam_mkhomedir/mkhomedir_helper.8.xml
+++ b/modules/pam_mkhomedir/mkhomedir_helper.8.xml
@@ -24,6 +24,9 @@
         <replaceable>path-to-skel</replaceable>
       <arg choice="opt" rep="norepeat">
         <replaceable>home_mode</replaceable>
+      <arg choice="opt" rep="norepeat">
+        <replaceable>path-to-vendor-skel</replaceable>
+      </arg>
       </arg>
       </arg>
       </arg>
@@ -48,6 +51,13 @@
       <replaceable>umask</replaceable>.
     </para>
 
+    <para>
+      <replaceable>path-to-vendor-skel</replaceable> doesn't have default
+      value. When set to a <emphasis>path</emphasis>, home directory will be
+      populated by contents of <replaceable>path-to-skel</replaceable> first,
+      and then by contents of <emphasis>path</emphasis>.
+    </para>
+
     <para>
       The helper is separated from the module to not require direct access from
       login SELinux domains to the contents of user home directories. The
@@ -77,4 +87,4 @@
       </para>
   </refsect1>
 
-</refentry>
\ No newline at end of file
+</refentry>
diff --git a/modules/pam_mkhomedir/mkhomedir_helper.c b/modules/pam_mkhomedir/mkhomedir_helper.c
index 0c05ee9c..1d741c5d 100644
--- a/modules/pam_mkhomedir/mkhomedir_helper.c
+++ b/modules/pam_mkhomedir/mkhomedir_helper.c
@@ -36,7 +36,7 @@ static unsigned long u_mask = 0022;
 static const char *skeldir = "/etc/skel";
 
 static int create_homedir(struct dir_spec *, const struct passwd *, mode_t,
-			  const char *, const char *);
+			  const char *, const char *, const char *);
 
 static int
 dir_spec_open(struct dir_spec *spec, const char *path)
@@ -88,7 +88,7 @@ dir_spec_close(struct dir_spec *spec)
 
 static int
 copy_entry(struct dir_spec *parent, const struct passwd *pwd, mode_t dir_mode,
-	   const char *source, struct dirent *dent)
+	   const char *source, struct dirent *dent, const char *vendordir)
 {
    char remark[BUFSIZ];
    int srcfd = -1, destfd = -1;
@@ -96,6 +96,7 @@ copy_entry(struct dir_spec *parent, const struct passwd *pwd, mode_t dir_mode,
    int retval = PAM_SESSION_ERR;
    struct stat st;
    char *newsource;
+   char *newvendordir = NULL;
 
    /* Determine what kind of file it is. */
    if ((newsource = pam_asprintf("%s/%s", source, dent->d_name)) == NULL)
@@ -114,8 +115,23 @@ copy_entry(struct dir_spec *parent, const struct passwd *pwd, mode_t dir_mode,
    /* If it's a directory, recurse. */
    if (S_ISDIR(st.st_mode))
    {
+      if (vendordir != NULL)
+      {
+         if ((newvendordir = pam_asprintf("%s/%s", vendordir, dent->d_name)) == NULL)
+         {
+            pam_syslog(NULL, LOG_CRIT, "asprintf failed for 'newvendordir'");
+            retval = PAM_BUF_ERR;
+            goto go_out;
+         }
+         if (lstat(newvendordir, &st) != 0)
+         {
+            free(newvendordir);
+            newvendordir = NULL;
+         }
+      }
+
       retval = create_homedir(parent, pwd, dir_mode & (~u_mask), newsource,
-			      dent->d_name);
+			      dent->d_name, newvendordir);
       goto go_out;
    }
 
@@ -259,19 +275,22 @@ copy_entry(struct dir_spec *parent, const struct passwd *pwd, mode_t dir_mode,
       close(destfd);
 
    free(newsource);
-
+   free(newvendordir);
    return retval;
 }
 
 /* Do the actual work of creating a home dir */
 static int
 create_homedir(struct dir_spec *parent, const struct passwd *pwd,
-	       mode_t dir_mode, const char *source, const char *dest)
+	       mode_t dir_mode, const char *source, const char *dest,
+	       const char *vendordir)
 {
    DIR *d = NULL;
    struct dirent *dent;
    struct dir_spec base;
    int retval = PAM_SESSION_ERR;
+   const char *sourcedirs[] = {source, vendordir};
+   unsigned int idx = 0;
 
    /* Create the new directory */
    if (mkdirat(parent->fd, dest, 0700))
@@ -289,32 +308,35 @@ create_homedir(struct dir_spec *parent, const struct passwd *pwd,
       goto go_out;
    }
 
-   /* See if we need to copy the skel dir over. */
-   if ((source == NULL) || (strlen(source) == 0))
-   {
-      retval = PAM_SUCCESS;
-      goto go_out;
-   }
-
-   /* Scan the directory */
-   d = opendir(source);
-   if (d == NULL)
-   {
-      pam_syslog(NULL, LOG_DEBUG, "unable to read directory %s: %m", source);
-      retval = PAM_PERM_DENIED;
-      goto go_out;
-   }
-
-   for (dent = readdir(d); dent != NULL; dent = readdir(d))
+   /* Scan source directories */
+   for (idx = 0; idx < PAM_ARRAY_SIZE(sourcedirs); idx++)
    {
-      /* Skip some files.. */
-      if (strcmp(dent->d_name,".") == 0 ||
-	  strcmp(dent->d_name,"..") == 0)
-	 continue;
+      /* See if we need to copy the source skel dir over. */
+      if ((sourcedirs[idx] == NULL) || strlen(sourcedirs[idx]) == 0)
+            continue;
+      d = opendir(sourcedirs[idx]);
+      if (d == NULL)
+      {
+         pam_syslog(NULL, LOG_DEBUG, "unable to read directory %s: %m",
+                    sourcedirs[idx]);
+         retval = PAM_PERM_DENIED;
+         goto go_out;
+      }
 
-      retval = copy_entry(&base, pwd, dir_mode, source, dent);
-      if (retval != PAM_SUCCESS)
-	 goto go_out;
+      for (dent = readdir(d); dent != NULL; dent = readdir(d))
+      {
+         /* Skip some files.. */
+         if (strcmp(dent->d_name,".") == 0 ||
+             strcmp(dent->d_name,"..") == 0)
+            continue;
+
+         retval = copy_entry(&base, pwd, dir_mode, sourcedirs[idx], dent,
+                             sourcedirs[idx] == vendordir ? NULL : vendordir);
+         if (retval != PAM_SUCCESS)
+            goto go_out;
+      }
+      closedir(d);
+      d = NULL;
    }
 
    retval = PAM_SUCCESS;
@@ -340,7 +362,8 @@ create_homedir(struct dir_spec *parent, const struct passwd *pwd,
 
 static int
 create_homedir_helper(const struct passwd *_pwd, mode_t home_mode,
-		      const char *_skeldir, const char *_homedir)
+		      const char *_skeldir, const char *_homedir,
+		      const char *_vendordir)
 {
    int retval = PAM_SESSION_ERR;
    struct dir_spec base;
@@ -357,7 +380,7 @@ create_homedir_helper(const struct passwd *_pwd, mode_t home_mode,
    }
    *cp = '/';
 
-   retval = create_homedir(&base, _pwd, home_mode, _skeldir, cp + 1);
+   retval = create_homedir(&base, _pwd, home_mode, _skeldir, cp + 1, _vendordir);
 
  go_out:
    dir_spec_close(&base);
@@ -399,6 +422,7 @@ main(int argc, char *argv[])
    struct stat st;
    char *eptr;
    unsigned long home_mode = 0;
+   const char *vendordir = NULL;
 
    if (argc < 2) {
 	fprintf(stderr, "Usage: %s <username> [<umask> [<skeldir> [<home_mode>]]]\n", argv[0]);
@@ -433,6 +457,9 @@ main(int argc, char *argv[])
        }
    }
 
+   if (argc >= 6)
+      vendordir = argv[5];
+
    if (home_mode == 0)
       home_mode = 0777 & ~u_mask;
 
@@ -449,5 +476,5 @@ main(int argc, char *argv[])
    if (make_parent_dirs(pwd->pw_dir, 0) != PAM_SUCCESS)
 	return PAM_PERM_DENIED;
 
-   return create_homedir_helper(pwd, home_mode, skeldir, pwd->pw_dir);
+   return create_homedir_helper(pwd, home_mode, skeldir, pwd->pw_dir, vendordir);
 }
diff --git a/modules/pam_mkhomedir/pam_mkhomedir.8.xml b/modules/pam_mkhomedir/pam_mkhomedir.8.xml
index ad957248..42f42c58 100644
--- a/modules/pam_mkhomedir/pam_mkhomedir.8.xml
+++ b/modules/pam_mkhomedir/pam_mkhomedir.8.xml
@@ -46,6 +46,12 @@
       <filename>/etc/skel/</filename>) is used to copy default files
       and also sets a umask for the creation.
     </para>
+
+    <para condition="with_vendordir">
+      <emphasis>%vendordir%/skel</emphasis> will also be used unless
+      <replaceable>skel</replaceable> option is specified.
+    </para>
+
     <para>
       The new users home directory will not be removed after logout
       of the user.
@@ -213,4 +219,4 @@
       pam_mkhomedir was written by Jason Gunthorpe &lt;jgg@debian.org&gt;.
     </para>
   </refsect1>
-</refentry>
\ No newline at end of file
+</refentry>
diff --git a/modules/pam_mkhomedir/pam_mkhomedir.c b/modules/pam_mkhomedir/pam_mkhomedir.c
index f090deee..5b08b6fd 100644
--- a/modules/pam_mkhomedir/pam_mkhomedir.c
+++ b/modules/pam_mkhomedir/pam_mkhomedir.c
@@ -60,6 +60,11 @@
 #define LOGIN_DEFS           "/etc/login.defs"
 #define UMASK_DEFAULT        "0022"
 
+#define SKELDIR "/etc/skel"
+#ifdef VENDORDIR
+#define VENDOR_SKELDIR (VENDORDIR "/skel")
+#endif
+
 struct options_t {
   int ctrl;
   const char *umask;
@@ -73,7 +78,7 @@ _pam_parse (const pam_handle_t *pamh, int flags, int argc, const char **argv,
 {
    opt->ctrl = 0;
    opt->umask = NULL;
-   opt->skeldir = "/etc/skel";
+   opt->skeldir = NULL;
 
    /* does the application require quiet? */
    if ((flags & PAM_SILENT) == PAM_SILENT)
@@ -154,7 +159,7 @@ create_homedir (pam_handle_t *pamh, options_t *opt,
    child = fork();
    if (child == 0) {
 	static char *envp[] = { NULL };
-	const char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL };
+	const char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
 
 	if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_PIPE_FD,
 					    PAM_MODUTIL_PIPE_FD,
@@ -165,8 +170,11 @@ create_homedir (pam_handle_t *pamh, options_t *opt,
 	args[0] = MKHOMEDIR_HELPER;
 	args[1] = user;
 	args[2] = opt->umask ? opt->umask : UMASK_DEFAULT;
-	args[3] = opt->skeldir;
+	args[3] = opt->skeldir ? opt->skeldir : SKELDIR;
 	args[4] = login_homemode;
+#ifdef VENDORDIR
+	args[5] = opt->skeldir ? NULL : VENDOR_SKELDIR;
+#endif
 
 	DIAG_PUSH_IGNORE_CAST_QUAL;
 	execve(MKHOMEDIR_HELPER, (char **)args, envp);
-- 
2.51.0

openSUSE Build Service is sponsored by