I just a patch which extends pam_namespace to add unionfs support (via aufs). This makes
it possible to provide a private version of a directory that is writeable for the user.
Configuration is simple: all you need to do is add a “unionfs” option in namespace.conf.
In my test system the entry looks like this:
$HOME /private/home/ tmpfs:union,mntopts=size=128m root
With this line pam_namespace does the following:
when using tmpfs with the union option or a different method an instance directory is
created
when using the union option the bind mount is replaced with an aufs mount which adds the
instance directory as a branch on top of the polyinstantiated directory
Regards,
Wichert.
Index: pam-1.1.8/modules/pam_namespace/pam_namespace.c
===================================================================
--- pam-1.1.8.orig/modules/pam_namespace/pam_namespace.c 2014-06-22 20:27:45.731009280
+0200
+++ pam-1.1.8/modules/pam_namespace/pam_namespace.c 2014-06-22 20:27:56.331008975 +0200
@@ -230,6 +230,28 @@
return 0;
}
+/**
+ * Check if we can mount directly on top of the polydir_s.dir, or we
+ * need to go through polydir_s.rdir.
+ */
+static int can_mount_polydir_directly(const struct polydir_s* s) {
+ return s->method == TMPFS && !(s->flags & POLYDIR_UNION);
+}
+
+static int is_legal_aufs_path(char* path)
+{
+ /*
+ * From aufs(5):
+ * Comma, colon and the permission flags string (including ‘=’) in
+ * the path are not allowed.
+ */
+ return strchr(path, ',')==NULL &&
+ strchr(path, ',')==NULL &&
+ strstr(path, "=rw")==NULL &&
+ strstr(path, "=ro")==NULL &&
+ strstr(path, "=rr")==NULL;
+}
+
static int parse_method(char *method, struct polydir_s *poly,
struct instance_data *idata)
{
@@ -238,9 +260,9 @@
static const char *method_names[] = { "user", "context",
"level", "tmpdir",
"tmpfs", NULL };
static const char *flag_names[] = { "create", "noinit",
"iscript",
- "shared", "mntopts", NULL };
+ "shared", "mntopts", "union", NULL };
static const unsigned int flag_values[] = { POLYDIR_CREATE, POLYDIR_NOINIT,
- POLYDIR_ISCRIPT, POLYDIR_SHARED, POLYDIR_MNTOPTS };
+ POLYDIR_ISCRIPT, POLYDIR_SHARED, POLYDIR_MNTOPTS, POLYDIR_UNION };
int i;
char *flag;
@@ -294,6 +316,17 @@
return -1;
}
break;
+
+ case POLYDIR_UNION:
+ if (!is_legal_aufs_path(poly->dir)) {
+ pam_syslog(idata->pamh, LOG_CRIT, "Illegal polydir path for aufs");
+ return -1;
+ }
+ if (!is_legal_aufs_path(poly->rdir)) {
+ pam_syslog(idata->pamh, LOG_CRIT, "Illegal instance path for
aufs");
+ return -1;
+ }
+ break;
}
}
}
@@ -459,7 +492,7 @@
/*
* Ensure that all pathnames are absolute path names.
*/
- if ((poly->dir[0] != '/') || (poly->method != TMPFS &&
poly->instance_prefix[0] != '/')) {
+ if ((poly->dir[0] != '/') || (!can_mount_polydir_directly(poly) &&
poly->instance_prefix[0] != '/')) {
pam_syslog(idata->pamh, LOG_NOTICE, "Pathnames must start with
'/'");
goto skipping;
}
@@ -1453,6 +1486,7 @@
int newdir = 1;
char *inst_dir = NULL;
char *instname = NULL;
+ char *aufs_opts = NULL;
struct stat statbuf;
#ifdef WITH_SELINUX
security_context_t instcontext = NULL, origcontext = NULL;
@@ -1478,7 +1512,7 @@
close(retval);
}
- if (polyptr->method == TMPFS) {
+ if (polyptr->method == TMPFS && can_mount_polydir_directly(polyptr)) {
if (mount("tmpfs", polyptr->dir, "tmpfs", 0,
polyptr->mount_opts) < 0) {
pam_syslog(idata->pamh, LOG_ERR, "Error mounting tmpfs on %s, %m",
polyptr->dir);
@@ -1549,15 +1583,41 @@
goto error_out;
}
- /*
- * Bind mount instance directory on top of the polyinstantiated
- * directory to provide an instance of polyinstantiated directory
- * based on polyinstantiated method.
- */
- if (mount(inst_dir, polyptr->dir, NULL, MS_BIND, NULL) < 0) {
- pam_syslog(idata->pamh, LOG_ERR, "Error mounting %s on %s, %m",
- inst_dir, polyptr->dir);
- goto error_out;
+ if (polyptr->method == TMPFS) {
+ if (mount("tmpfs", inst_dir, "tmpfs", 0, polyptr->mount_opts)
< 0) {
+ pam_syslog(idata->pamh, LOG_ERR, "Error mounting tmpfs on %s, %m",
+ inst_dir);
+ return PAM_SESSION_ERR;
+ }
+ }
+
+ if (!(polyptr->flags & POLYDIR_UNION)) {
+ /*
+ * Bind mount instance directory on top of the polyinstantiated
+ * directory to provide an instance of polyinstantiated directory
+ * based on polyinstantiated method.
+ */
+ if (mount(inst_dir, polyptr->dir, NULL, MS_BIND, NULL) < 0) {
+ pam_syslog(idata->pamh, LOG_ERR, "Error mounting %s on %s, %m",
+ inst_dir, polyptr->dir);
+ goto error_out;
+ }
+ } else {
+ /*
+ * Use aufs to add the instance directory as writable branch of the
+ * polyinstantiated directory.
+ */
+ if (asprintf(&aufs_opts, "br=%s:%s", inst_dir, polyptr->dir) == -1) {
+ pam_syslog(idata->pamh, LOG_ERR, "Error aufs mounting %s on %s, %m",
+ inst_dir, polyptr->dir);
+ goto error_out;
+ }
+
+ if (mount(inst_dir, polyptr->dir, "aufs", 0, aufs_opts) < 0) {
+ pam_syslog(idata->pamh, LOG_ERR, "Error aufs mounting %s on %s, %m",
+ inst_dir, polyptr->dir);
+ goto error_out;
+ }
}
if (!(polyptr->flags & POLYDIR_NOINIT))
@@ -1573,6 +1633,7 @@
retval = PAM_SESSION_ERR;
cleanup:
+ free(aufs_opts);
free(inst_dir);
free(instname);
#ifdef WITH_SELINUX
Index: pam-1.1.8/modules/pam_namespace/pam_namespace.h
===================================================================
--- pam-1.1.8.orig/modules/pam_namespace/pam_namespace.h 2014-06-22 20:27:45.731009280
+0200
+++ pam-1.1.8/modules/pam_namespace/pam_namespace.h 2014-06-22 20:27:56.331008975 +0200
@@ -117,6 +117,7 @@
#define POLYDIR_SHARED 0x00000008 /* share context/level instances among users */
#define POLYDIR_ISCRIPT 0x00000010 /* non default init script */
#define POLYDIR_MNTOPTS 0x00000020 /* mount options for tmpfs mount */
+#define POLYDIR_UNION 0x00000040 /* aufs mount over polydir */
#define NAMESPACE_MAX_DIR_LEN 80