strcpy(pent->instance_prefix, ent->instance_prefix);
pent->method = ent->method;
pent->num_uids = ent->num_uids;
+ pent->exclusive = ent->exclusive;
if (ent->num_uids) {
uid_t *pptr, *eptr;
}
}
+static void cleanup_data(pam_handle_t *pamh UNUSED , void *data, int err UNUSED)
+{
+ del_polydir_list(data);
+}
/*
* Called from parse_config_file, this function processes a single line
poly.uid = NULL;
poly.num_uids = 0;
+ poly.exclusive = 0;
/*
* skip the leading white space
instance_prefix = expanded;
}
- /*
- * Ensure that all pathnames are absolute path names.
- */
- if ((dir[0] != '/') || (instance_prefix[0] != '/')) {
- pam_syslog(idata->pamh, LOG_NOTICE,"Pathnames must start with '/'");
- goto skipping;
- }
- if (strstr(dir, "..") || strstr(instance_prefix, "..")) {
- pam_syslog(idata->pamh, LOG_NOTICE,"Pathnames must not contain '..'");
- goto skipping;
- }
-
/*
* Populate polyinstantiated directory structure with appropriate
* pathnames and the method with which to polyinstantiate.
if (strlen(dir) >= sizeof(poly.dir)
|| strlen(instance_prefix) >= sizeof(poly.instance_prefix)) {
pam_syslog(idata->pamh, LOG_NOTICE, "Pathnames too long");
+ goto skipping;
}
strcpy(poly.dir, dir);
strcpy(poly.instance_prefix, instance_prefix);
poly.method = NONE;
if (strcmp(method, "user") == 0)
poly.method = USER;
+
+ if (strcmp(method, "tmpdir") == 0) {
+ poly.method = TMPDIR;
+ if (sizeof(poly.instance_prefix) - strlen(poly.instance_prefix) < 7) {
+ pam_syslog(idata->pamh, LOG_NOTICE, "Pathnames too long");
+ goto skipping;
+ }
+ strcat(poly.instance_prefix, "XXXXXX");
+ }
+
+ if (strcmp(method, "tmpfs") == 0)
+ poly.method = TMPFS;
#ifdef WITH_SELINUX
if (strcmp(method, "level") == 0) {
#endif
- if ( poly.method == NONE) {
+ if (poly.method == NONE) {
pam_syslog(idata->pamh, LOG_NOTICE, "Illegal method");
goto skipping;
}
+ /*
+ * Ensure that all pathnames are absolute path names.
+ */
+ if ((dir[0] != '/') || (poly.method != TMPFS && instance_prefix[0] != '/')) {
+ pam_syslog(idata->pamh, LOG_NOTICE, "Pathnames must start with '/'");
+ goto skipping;
+ }
+ if (strstr(dir, "..") || strstr(instance_prefix, "..")) {
+ pam_syslog(idata->pamh, LOG_NOTICE, "Pathnames must not contain '..'");
+ goto skipping;
+ }
+
/*
* If the line in namespace.conf for a directory to polyinstantiate
* contains a list of override users (users for whom polyinstantiation
uid_t *uidptr;
const char *ustr, *sstr;
int count, i;
-
+
+ if (*uids == '~') {
+ poly.exclusive = 1;
+ uids++;
+ }
for (count = 0, ustr = sstr = uids; sstr; ustr = sstr + 1, count++)
sstr = strchr(ustr, ',');
* directory's list of override uids. If the uid is one of the override
* uids for the polyinstantiated directory, polyinstantiation is not
* performed for that user for that directory.
+ * If exclusive is set the returned values are opposite.
*/
static int ns_override(struct polydir_s *polyptr, struct instance_data *idata,
uid_t uid)
for (i = 0; i < polyptr->num_uids; i++) {
if (uid == polyptr->uid[i]) {
- return 1;
+ return !polyptr->exclusive;
}
}
- return 0;
+ return polyptr->exclusive;
}
/*
#endif /* WITH_SELINUX */
+ case TMPDIR:
+ case TMPFS:
+ if ((*i_name=strdup("")) == NULL)
+ goto fail;
+ return PAM_SUCCESS;
+
default:
if (idata->flags & PAMNS_DEBUG)
pam_syslog(idata->pamh, LOG_ERR, "Unknown method");
hash = NULL;
} else {
char *newname;
- if (asprintf(&newname, "%.*s_%s", NAMESPACE_MAX_DIR_LEN-1-strlen(hash),
+ if (asprintf(&newname, "%.*s_%s", NAMESPACE_MAX_DIR_LEN-1-(int)strlen(hash),
*i_name, hash) < 0) {
goto fail;
}
* execute it and pass directory to polyinstantiate and instance
* directory as arguments.
*/
-static int inst_init(const struct polydir_s *polyptr, char *ipath,
- struct instance_data *idata)
+static int inst_init(const struct polydir_s *polyptr, const char *ipath,
+ struct instance_data *idata, int newdir)
{
pid_t rc, pid;
sighandler_t osighand = NULL;
}
#endif
if (execl(NAMESPACE_INIT_SCRIPT, NAMESPACE_INIT_SCRIPT,
- polyptr->dir, ipath, (char *)NULL) < 0)
+ polyptr->dir, ipath, newdir?"1":"0", idata->user, (char *)NULL) < 0)
exit(1);
} else if (pid > 0) {
while (((rc = waitpid(pid, &status, 0)) == (pid_t)-1) &&
* Create polyinstantiated instance directory (ipath).
*/
#ifdef WITH_SELINUX
-static int create_dirs(const struct polydir_s *polyptr, char *ipath,
+static int create_dirs(struct polydir_s *polyptr, char *ipath,
security_context_t icontext, security_context_t ocontext,
struct instance_data *idata)
#else
-static int create_dirs(const struct polydir_s *polyptr, char *ipath,
+static int create_dirs(struct polydir_s *polyptr, char *ipath,
struct instance_data *idata)
#endif
{
struct stat statbuf, newstatbuf;
int rc, fd;
+ int newdir = 0;
/*
* stat the directory to polyinstantiate, so its owner-group-mode
* attributes to match that of the original directory that is being
* polyinstantiated.
*/
- if (mkdir(ipath, S_IRUSR) < 0) {
+
+ if (polyptr->method == TMPDIR) {
+ if (mkdtemp(polyptr->instance_prefix) == NULL) {
+ pam_syslog(idata->pamh, LOG_ERR, "Error creating temporary instance %s, %m",
+ polyptr->instance_prefix);
+ polyptr->method = NONE; /* do not clean up! */
+ return PAM_SESSION_ERR;
+ }
+ /* copy the actual directory name to ipath */
+ strcpy(ipath, polyptr->instance_prefix);
+ } else if (mkdir(ipath, S_IRUSR) < 0) {
if (errno == EEXIST)
goto inst_init;
else {
}
}
+ newdir = 1;
/* Open a descriptor to it to prevent races */
fd = open(ipath, O_DIRECTORY | O_RDONLY);
if (fd < 0) {
*/
inst_init:
- rc = inst_init(polyptr, ipath, idata);
+ rc = inst_init(polyptr, ipath, idata, newdir);
return rc;
}
* security attributes, and performs bind mount to setup the process
* namespace.
*/
-static int ns_setup(const struct polydir_s *polyptr,
+static int ns_setup(struct polydir_s *polyptr,
struct instance_data *idata)
{
int retval = 0;
char *inst_dir = NULL;
char *instname = NULL;
- char *dir;
#ifdef WITH_SELINUX
security_context_t instcontext = NULL, origcontext = NULL;
#endif
pam_syslog(idata->pamh, LOG_DEBUG,
"Set namespace for directory %s", polyptr->dir);
- dir = strrchr(polyptr->dir, '/');
- if (dir && strlen(dir) > 1)
- dir++;
+ if (polyptr->method == TMPFS) {
+ if (mount("tmpfs", polyptr->dir, "tmpfs", 0, NULL) < 0) {
+ pam_syslog(idata->pamh, LOG_ERR, "Error mounting tmpfs on %s, %m",
+ polyptr->dir);
+ return PAM_SESSION_ERR;
+ }
+ /* we must call inst_init after the mount in this case */
+ return inst_init(polyptr, "tmpfs", idata, 1);
+ }
/*
* Obtain the name of instance pathname based on the
return retval;
}
+static int cleanup_tmpdirs(struct instance_data *idata)
+{
+ struct polydir_s *pptr;
+ pid_t rc, pid;
+ sighandler_t osighand = NULL;
+ int status;
+
+ osighand = signal(SIGCHLD, SIG_DFL);
+ if (osighand == SIG_ERR) {
+ pam_syslog(idata->pamh, LOG_ERR, "Cannot set signal value");
+ rc = PAM_SESSION_ERR;
+ goto out;
+ }
+
+ for (pptr = idata->polydirs_ptr; pptr; pptr = pptr->next) {
+ if (pptr->method == TMPDIR && access(pptr->instance_prefix, F_OK) == 0) {
+ pid = fork();
+ if (pid == 0) {
+#ifdef WITH_SELINUX
+ if (idata->flags & PAMNS_SELINUX_ENABLED) {
+ if (setexeccon(NULL) < 0)
+ exit(1);
+ }
+#endif
+ if (execl("/bin/rm", "/bin/rm", "-rf", pptr->instance_prefix, (char *)NULL) < 0)
+ exit(1);
+ } else if (pid > 0) {
+ while (((rc = waitpid(pid, &status, 0)) == (pid_t)-1) &&
+ (errno == EINTR));
+ if (rc == (pid_t)-1) {
+ pam_syslog(idata->pamh, LOG_ERR, "waitpid failed- %m");
+ rc = PAM_SESSION_ERR;
+ goto out;
+ }
+ if (!WIFEXITED(status) || WIFSIGNALED(status) > 0) {
+ pam_syslog(idata->pamh, LOG_ERR,
+ "Error removing %s", pptr->instance_prefix);
+ }
+ } else if (pid < 0) {
+ pam_syslog(idata->pamh, LOG_ERR,
+ "Cannot fork to run namespace init script, %m");
+ rc = PAM_SESSION_ERR;
+ goto out;
+ }
+ }
+ }
+
+ rc = PAM_SUCCESS;
+out:
+ signal(SIGCHLD, osighand);
+ return rc;
+}
/*
* This function checks to see if polyinstantiation is needed for any
* disassociate from the parent namespace.
*/
if (need_poly) {
+ if (pam_set_data(idata->pamh, NAMESPACE_POLYDIR_DATA, idata->polydirs_ptr,
+ cleanup_data) != PAM_SUCCESS) {
+ pam_syslog(idata->pamh, LOG_ERR,
+ "Unable to set namespace data");
+ return PAM_SYSTEM_ERR;
+ }
if (unshare(CLONE_NEWNS) < 0) {
- pam_syslog(idata->pamh, LOG_ERR,
+ pam_set_data(idata->pamh, NAMESPACE_POLYDIR_DATA, NULL, NULL);
+ pam_syslog(idata->pamh, LOG_ERR,
"Unable to unshare from parent namespace, %m");
return PAM_SESSION_ERR;
}
- } else
+ } else {
+ del_polydir_list(idata->polydirs_ptr);
return PAM_SUCCESS;
+ }
/*
* Again cycle through all polyinstantiated directories, this time,
* umount
*/
if ((changing_dir = cwd_in(pptr->dir, idata)) < 0) {
- return PAM_SESSION_ERR;
+ retval = PAM_SESSION_ERR;
+ goto out;
} else if (changing_dir) {
if (idata->flags & PAMNS_DEBUG)
pam_syslog(idata->pamh, LOG_DEBUG, "changing cwd");
int saved_errno = errno;
pam_syslog(idata->pamh, LOG_ERR, "Unmount of %s failed, %m",
pptr->dir);
- if (saved_errno != EINVAL)
- return PAM_SESSION_ERR;
+ if (saved_errno != EINVAL) {
+ retval = PAM_SESSION_ERR;
+ goto out;
+ }
} else if (idata->flags & PAMNS_DEBUG)
pam_syslog(idata->pamh, LOG_DEBUG, "Umount succeeded %s",
pptr->dir);
break;
}
}
-
+out:
+ if (retval != PAM_SUCCESS)
+ cleanup_tmpdirs(idata);
return retval;
}
} else if (idata->flags & PAMNS_DEBUG)
pam_syslog(idata->pamh, LOG_DEBUG, "Unmount of %s succeeded",
pptr->dir);
- }
+ }
}
+
+ cleanup_tmpdirs(idata);
return 0;
}
} else if (idata.flags & PAMNS_DEBUG)
pam_syslog(idata.pamh, LOG_DEBUG, "Nothing to polyinstantiate");
- del_polydir_list(idata.polydirs_ptr);
+ if (retval != PAM_SUCCESS)
+ del_polydir_list(idata.polydirs_ptr);
return retval;
}
struct instance_data idata;
char *user_name;
struct passwd *pwd;
+ void *polyptr;
/* init instance data */
idata.flags = 0;
strncat(idata.user, user_name, sizeof(idata.user) - 1);
idata.uid = pwd->pw_uid;
- /*
- * Parse namespace configuration file which lists directories that
- * are polyinstantiated, directories where instance directories are
- * created and the method used for polyinstantiation.
- */
- retval = parse_config_file(&idata);
- if ((retval != PAM_SUCCESS) || !idata.polydirs_ptr) {
- del_polydir_list(idata.polydirs_ptr);
- return PAM_SESSION_ERR;
- }
+ retval = pam_get_data(idata.pamh, NAMESPACE_POLYDIR_DATA, (const void **)&polyptr);
+ if (retval != PAM_SUCCESS || polyptr == NULL)
+ /* nothing to reset */
+ return PAM_SUCCESS;
+
+ idata.polydirs_ptr = polyptr;
if (idata.flags & PAMNS_DEBUG)
pam_syslog(idata.pamh, LOG_DEBUG, "Resetting namespace for pid %d",
pam_syslog(idata.pamh, LOG_DEBUG,
"resetting namespace ok for pid %d", getpid());
}
- del_polydir_list(idata.polydirs_ptr);
+
+ pam_set_data(idata.pamh, NAMESPACE_POLYDIR_DATA, NULL, NULL);
+
return PAM_SUCCESS;
}