Index: Linux-PAM-1.6.0/modules/pam_namespace/namespace.init
===================================================================
--- Linux-PAM-1.6.0.orig/modules/pam_namespace/namespace.init
+++ Linux-PAM-1.6.0/modules/pam_namespace/namespace.init
@@ -1,25 +1,43 @@
 #!/bin/sh
-# It receives polydir path as $1, the instance path as $2,
-# a flag whether the instance dir was newly created (0 - no, 1 - yes) in $3,
-# and user name in $4.
+# It receives as arguments:
+# - $1 polydir path (see WARNING below)
+# - $2 instance path (see WARNING below)
+# - $3 flag whether the instance dir was newly created (0 - no, 1 - yes)
+# - $4 user name
+# - $5 flag whether the polydir path ($1) is safe (0 - unsafe, 1 -safe)
+# - $6 flag whether the instance path ($2) is safe (0 - unsafe, 1 - safe)
+#
+# WARNING: This script is invoked with full root privileges. Accessing
+# the polydir ($1) and the instance ($2) directories in this context may be
+# extremely dangerous as those can be under user control. The flags $5 and $6
+# are provided to let you know if all the segments part of the path (except the
+# last one) are owned by root and are writable by root only. If the path does
+# not meet these criteria, you expose yourself to possible symlink attacks when
+# accessing these path.
+# However, even if the path components are safe, the content of the
+# directories may still be owned/writable by a user, so care must be taken!.
 #
 # The following section will copy the contents of /etc/skel if this is a
 # newly created home directory.
-if [ "$3" = 1 ]; then
-        # This line will fix the labeling on all newly created directories
-        [ -x /sbin/restorecon ] && /sbin/restorecon "$1"
-        user="$4"
-        passwd=$(getent passwd "$user")
-        homedir=$(echo "$passwd" | cut -f6 -d":")
-        if [ "$1" = "$homedir" ]; then
-                gid=$(echo "$passwd" | cut -f4 -d":")
-                cp -rT /etc/skel "$homedir"
-                chown -R "$user":"$gid" "$homedir"
-                mask=$(awk '/^UMASK/{gsub("#.*$", "", $2); print $2; exit}' /etc/login.defs)
-                mode=$(printf "%o" $((0777 & ~mask)))
-                chmod ${mode:-700} "$homedir"
-                [ -x /sbin/restorecon ] && /sbin/restorecon -R "$homedir"
-        fi
-fi
 
+# Executes only if the polydir path is safe
+if [ "$5" = 1 ]; then
+
+    if [ "$3" = 1 ]; then
+            # This line will fix the labeling on all newly created directories
+            [ -x /sbin/restorecon ] && /sbin/restorecon "$1"
+            user="$4"
+            passwd=$(getent passwd "$user")
+            homedir=$(echo "$passwd" | cut -f6 -d":")
+            if [ "$1" = "$homedir" ]; then
+                    gid=$(echo "$passwd" | cut -f4 -d":")
+                    cp -rT /etc/skel "$homedir"
+                    chown -R "$user":"$gid" "$homedir"
+                    mask=$(sed -E -n 's/^UMASK[[:space:]]+([^#[:space:]]+).*/\1/p' /etc/login.defs)
+                    mode=$(printf "%o" $((0777 & ~mask)))
+                    chmod ${mode:-700} "$homedir"
+                    [ -x /sbin/restorecon ] && /sbin/restorecon -R "$homedir"
+            fi
+    fi
+fi
 exit 0
Index: Linux-PAM-1.6.0/modules/pam_namespace/pam_namespace.c
===================================================================
--- Linux-PAM-1.6.0.orig/modules/pam_namespace/pam_namespace.c
+++ Linux-PAM-1.6.0/modules/pam_namespace/pam_namespace.c
@@ -1476,6 +1476,79 @@ static int check_inst_parent(int dfd, st
 }
 
 /*
+ * Check for a given absolute path that all segments except the last one are:
+ * 1. a directory owned by root and not writable by group or others
+ * 2. a symlink owned by root and referencing a directory respecting 1.
+ * Returns 0 if safe, -1 is unsafe.
+ * If the path is not accessible (does not exist, hidden under a mount...),
+ * returns -1 (unsafe).
+ */
+static int check_safe_path(const char *path, struct instance_data *idata)
+{
+    char *p = strdup(path);
+    char *d;
+    char *dir = p;
+    struct stat st;
+
+    if (p == NULL)
+        return -1;
+
+    /* Check path is absolute */
+    if (p[0] != '/')
+        goto error;
+
+    strip_trailing_slashes(p);
+
+    /* Last segment of the path may be owned by the user */
+    if ((d = strrchr(dir, '/')) != NULL)
+        *d = '\0';
+
+    while ((d=strrchr(dir, '/')) != NULL) {
+
+        /* Do not follow symlinks */
+        if (lstat(dir, &st) != 0)
+            goto error;
+
+        if (S_ISLNK(st.st_mode)) {
+            if (st.st_uid != 0) {
+                if (idata->flags & PAMNS_DEBUG)
+                    pam_syslog(idata->pamh, LOG_DEBUG,
+                            "Path deemed unsafe: Symlink %s should be owned by root", dir);
+                goto error;
+            }
+
+            /* Follow symlinks */
+            if (stat(dir, &st) != 0)
+                goto error;
+        }
+
+        if (!S_ISDIR(st.st_mode)) {
+                if (idata->flags & PAMNS_DEBUG)
+                    pam_syslog(idata->pamh, LOG_DEBUG,
+                        "Path deemed unsafe: %s is expected to be a directory", dir);
+            goto error;
+        }
+
+        if (st.st_uid != 0 ||
+            ((st.st_mode & (S_IWGRP|S_IWOTH)) && !(st.st_mode & S_ISVTX))) {
+                if (idata->flags & PAMNS_DEBUG)
+                    pam_syslog(idata->pamh, LOG_DEBUG,
+                        "Path deemed unsafe: %s should be owned by root, and not be writable by group or others", dir);
+            goto error;
+        }
+
+        *d = '\0';
+    }
+
+    free(p);
+    return 0;
+
+error:
+    free(p);
+    return -1;
+}
+
+/*
 * Check to see if there is a namespace initialization script in
 * the /etc/security directory. If such a script exists
 * execute it and pass directory to polyinstantiate and instance
@@ -1533,7 +1606,11 @@ static int inst_init(const struct polydi
 				close_fds_pre_exec(idata);
 
 				if (execle(init_script, init_script,
-					polyptr->dir, ipath, newdir?"1":"0", idata->user, NULL, envp) < 0)
+					polyptr->dir, ipath,
+		       		newdir ? "1":"0", idata->user,
+		       		(check_safe_path(polyptr->dir, idata) == -1) ? "0":"1",
+		       		(check_safe_path(ipath, idata) == -1) ? "0":"1",
+					NULL, envp) < 0)
 					_exit(1);
 			} else if (pid > 0) {
 				while (((rc = waitpid(pid, &status, 0)) == (pid_t)-1) &&
