1 /******************************************************************************
2 * A module for Linux-PAM that will set the default namespace after
3 * establishing a session via PAM.
5 * (C) Copyright IBM Corporation 2005
6 * (C) Copyright Red Hat 2006
9 * Written by: Janak Desai <janak@us.ibm.com>
10 * With Revisions by: Steve Grubb <sgrubb@redhat.com>
11 * Derived from a namespace setup patch by Chad Sellers <cdselle@tycho.nsa.gov>
13 * Permission is hereby granted, free of charge, to any person obtaining a
14 * copy of this software and associated documentation files (the "Software"),
15 * to deal in the Software without restriction, including without limitation
16 * on the rights to use, copy, modify, merge, publish, distribute, sub
17 * license, and/or sell copies of the Software, and to permit persons to whom
18 * the Software is furnished to do so, subject to the following conditions:
20 * The above copyright notice and this permission notice (including the next
21 * paragraph) shall be included in all copies or substantial portions of the
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
27 * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30 * DEALINGS IN THE SOFTWARE.
33 #include "pam_namespace.h"
36 * Copies the contents of ent into pent
38 static int copy_ent(const struct polydir_s *ent, struct polydir_s *pent)
42 strcpy(pent->dir, ent->dir);
43 strcpy(pent->instance_prefix, ent->instance_prefix);
44 pent->method = ent->method;
45 pent->num_uids = ent->num_uids;
49 pent->uid = (uid_t *) malloc(ent->num_uids * sizeof(uid_t));
53 for (i = 0, pptr = pent->uid, eptr = ent->uid; i < ent->num_uids;
62 * Adds an entry for a polyinstantiated directory to the linked list of
63 * polyinstantiated directories. It is called from process_line() while
64 * parsing the namespace configuration file.
66 static int add_polydir_entry(struct instance_data *idata,
67 const struct polydir_s *ent)
69 struct polydir_s *pent;
73 * Allocate an entry to hold information about a directory to
74 * polyinstantiate, populate it with information from 2nd argument
75 * and add the entry to the linked list of polyinstantiated
78 pent = (struct polydir_s *) malloc(sizeof(struct polydir_s));
84 rc = copy_ent(ent,pent);
88 /* Now attach to linked list */
90 if (idata->polydirs_ptr == NULL)
91 idata->polydirs_ptr = pent;
93 struct polydir_s *tail;
95 tail = idata->polydirs_ptr;
109 * Deletes all the entries in the linked list.
111 static void del_polydir_list(struct polydir_s *polydirs_ptr)
113 struct polydir_s *dptr = polydirs_ptr;
116 struct polydir_s *tptr = dptr;
125 * Called from parse_config_file, this function processes a single line
126 * of the namespace configuration file. It skips over comments and incomplete
127 * or malformed lines. It processes a valid line with information on
128 * polyinstantiating a directory by populating appropriate fields of a
129 * polyinstatiated directory structure and then calling add_polydir_entry to
130 * add that entry to the linked list of polyinstantiated directories.
132 static int process_line(char *line, const char *home,
133 struct instance_data *idata)
135 const char *dir, *instance_prefix;
136 const char *method, *uids;
138 struct polydir_s poly;
145 * skip the leading white space
147 while (*line && isspace(*line))
151 * Rip off the comments
153 tptr = strchr(line,'#');
158 * Rip off the newline char
160 tptr = strchr(line,'\n');
171 * Initialize and scan the five strings from the line from the
172 * namespace configuration file.
174 dir = strtok_r(line, " \t", &tptr);
176 pam_syslog(idata->pamh, LOG_NOTICE, "Invalid line missing polydir");
179 instance_prefix = strtok_r(NULL, " \t", &tptr);
180 if (instance_prefix == NULL) {
181 pam_syslog(idata->pamh, LOG_NOTICE, "Invalid line missing instance_prefix");
184 method = strtok_r(NULL, " \t", &tptr);
185 if (method == NULL) {
186 pam_syslog(idata->pamh, LOG_NOTICE, "Invalid line missing method");
191 * Only the uids field is allowed to be blank, to indicate no
192 * override users for polyinstantiation of that directory. If
193 * any of the other fields are blank, the line is incomplete so
196 uids = strtok_r(NULL, " \t", &tptr);
199 * If the directory being polyinstantiated is the home directory
200 * of the user who is establishing a session, we have to swap
201 * the "$HOME" string with the user's home directory that is
202 * passed in as an argument.
204 if (strcmp(dir, "$HOME") == 0) {
209 * Expand $HOME and $USER in instance dir prefix
211 if ((tptr = strstr(instance_prefix, "$USER")) != 0) {
212 /* FIXME: should only support this if method is USER or BOTH */
213 char *expanded = alloca(strlen(idata->user) + strlen(instance_prefix)-5+1);
215 sprintf(expanded, "%s%s%s", instance_prefix, idata->user, tptr+5);
216 instance_prefix = expanded;
218 if ((tptr = strstr(instance_prefix, "$HOME")) != 0) {
219 char *expanded = alloca(strlen(home)+strlen(instance_prefix)-5+1);
221 sprintf(expanded, "%s%s%s", instance_prefix, home, tptr+5);
222 instance_prefix = expanded;
226 * Ensure that all pathnames are absolute path names.
228 if ((dir[0] != '/') || (instance_prefix[0] != '/')) {
229 pam_syslog(idata->pamh, LOG_NOTICE,"Pathnames must start with '/'");
232 if (strstr(dir, "..") || strstr(instance_prefix, "..")) {
233 pam_syslog(idata->pamh, LOG_NOTICE,"Pathnames must not contain '..'");
238 * Populate polyinstantiated directory structure with appropriate
239 * pathnames and the method with which to polyinstantiate.
241 if (strlen(dir) >= sizeof(poly.dir)
242 || strlen(instance_prefix) >= sizeof(poly.instance_prefix)) {
243 pam_syslog(idata->pamh, LOG_NOTICE, "Pathnames too long");
245 strcpy(poly.dir, dir);
246 strcpy(poly.instance_prefix, instance_prefix);
249 if (strcmp(method, "user") == 0)
253 if (strcmp(method, "level") == 0) {
254 if (idata->flags & PAMNS_CTXT_BASED_INST)
260 if (strcmp(method, "context") == 0) {
261 if (idata->flags & PAMNS_CTXT_BASED_INST)
262 poly.method = CONTEXT;
269 if ( poly.method == NONE) {
270 pam_syslog(idata->pamh, LOG_NOTICE, "Illegal method");
275 * If the line in namespace.conf for a directory to polyinstantiate
276 * contains a list of override users (users for whom polyinstantiation
277 * is not performed), read the user ids, convert names into uids, and
278 * add to polyinstantiated directory structure.
282 const char *ustr, *sstr;
285 for (count = 0, ustr = sstr = uids; sstr; ustr = sstr + 1, count++)
286 sstr = strchr(ustr, ',');
288 poly.num_uids = count;
289 poly.uid = (uid_t *) malloc(count * sizeof (uid_t));
291 if (uidptr == NULL) {
292 pam_syslog(idata->pamh, LOG_NOTICE, "out of memory");
297 for (i = 0; i < count; i++) {
300 tptr = strchr(ustr, ',');
304 pwd = pam_modutil_getpwnam(idata->pamh, ustr);
306 pam_syslog(idata->pamh, LOG_ERR, "Unknown user %s in configuration", ustr);
309 *uidptr = pwd->pw_uid;
317 * Add polyinstantiated directory structure to the linked list
318 * of all polyinstantiated directory structures.
320 if (add_polydir_entry(idata, &poly) < 0) {
321 pam_syslog(idata->pamh, LOG_ERR, "Allocation Error");
322 retval = PAM_SERVICE_ERR;
329 if (idata->flags & PAMNS_IGN_CONFIG_ERR)
332 retval = PAM_SERVICE_ERR;
339 * Parses /etc/security/namespace.conf file to build a linked list of
340 * polyinstantiated directory structures of type polydir_s. Each entry
341 * in the linked list contains information needed to polyinstantiate
344 static int parse_config_file(struct instance_data *idata)
353 if (idata->flags & PAMNS_DEBUG)
354 pam_syslog(idata->pamh, LOG_DEBUG, "Parsing config file %s",
355 PAM_NAMESPACE_CONFIG);
358 * Extract the user's home directory to resolve $HOME entries
359 * in the namespace configuration file.
361 cpwd = pam_modutil_getpwnam(idata->pamh, idata->user);
363 pam_syslog(idata->pamh, LOG_ERR,
364 "Error getting home dir for '%s'", idata->user);
365 return PAM_SESSION_ERR;
367 home = strdupa(cpwd->pw_dir);
370 * Open configuration file, read one line at a time and call
371 * process_line to process each line.
373 fil = fopen(PAM_NAMESPACE_CONFIG, "r");
375 pam_syslog(idata->pamh, LOG_ERR, "Error opening config file");
376 return PAM_SERVICE_ERR;
379 /* Use unlocked IO */
380 __fsetlocking(fil, FSETLOCKING_BYCALLER);
382 /* loop reading the file */
383 while (getline(&line, &len, fil) > 0) {
384 retval = process_line(line, home, idata);
386 pam_syslog(idata->pamh, LOG_ERR,
387 "Error processing conf file line %s", line);
390 return PAM_SERVICE_ERR;
396 /* All done...just some debug stuff */
397 if (idata->flags & PAMNS_DEBUG) {
398 struct polydir_s *dptr = idata->polydirs_ptr;
402 pam_syslog(idata->pamh, LOG_DEBUG,
403 dptr?"Configured poly dirs:":"No configured poly dirs");
405 pam_syslog(idata->pamh, LOG_DEBUG, "dir='%s' iprefix='%s' meth=%d",
406 dptr->dir, dptr->instance_prefix, dptr->method);
407 for (i = 0, iptr = dptr->uid; i < dptr->num_uids; i++, iptr++)
408 pam_syslog(idata->pamh, LOG_DEBUG, "override user %d ", *iptr);
418 * This funtion returns true if a given uid is present in the polyinstantiated
419 * directory's list of override uids. If the uid is one of the override
420 * uids for the polyinstantiated directory, polyinstantiation is not
421 * performed for that user for that directory.
423 static int ns_override(struct polydir_s *polyptr, struct instance_data *idata,
428 if (idata->flags & PAMNS_DEBUG)
429 pam_syslog(idata->pamh, LOG_DEBUG,
430 "Checking for ns override in dir %s for uid %d",
433 for (i = 0; i < polyptr->num_uids; i++) {
434 if (uid == polyptr->uid[i]) {
443 * md5hash generates a hash of the passed in instance directory name.
445 static char *md5hash(const char *instname, struct instance_data *idata)
448 char *md5inst = NULL;
450 unsigned char inst_digest[MD5_DIGEST_LENGTH];
453 * Create MD5 hashes for instance pathname.
456 MD5((const unsigned char *)instname, strlen(instname), inst_digest);
458 if ((md5inst = malloc(MD5_DIGEST_LENGTH * 2 + 1)) == NULL) {
459 pam_syslog(idata->pamh, LOG_ERR, "Unable to allocate buffer");
464 for (i = 0; i < MD5_DIGEST_LENGTH; i++) {
465 snprintf(to, 3, "%02x", (unsigned int)inst_digest[i]);
473 static int form_context(const struct polydir_s *polyptr,
474 security_context_t *i_context, security_context_t *origcon,
475 struct instance_data *idata)
477 int rc = PAM_SUCCESS;
478 security_context_t scon = NULL;
479 security_class_t tclass;
482 * Get the security context of the directory to polyinstantiate.
484 rc = getfilecon(polyptr->dir, origcon);
485 if (rc < 0 || *origcon == NULL) {
486 pam_syslog(idata->pamh, LOG_ERR,
487 "Error getting poly dir context, %m");
488 return PAM_SESSION_ERR;
491 if (polyptr->method == USER) return PAM_SUCCESS;
493 rc = getexeccon(&scon);
494 if (rc < 0 || scon == NULL) {
495 pam_syslog(idata->pamh, LOG_ERR,
496 "Error getting exec context, %m");
497 return PAM_SESSION_ERR;
501 * If polyinstantiating based on security context, get current
502 * process security context, get security class for directories,
503 * and ask the policy to provide security context of the
504 * polyinstantiated instance directory.
507 if (polyptr->method == CONTEXT) {
508 tclass = string_to_security_class("dir");
510 if (security_compute_member(scon, *origcon, tclass,
512 pam_syslog(idata->pamh, LOG_ERR,
513 "Error computing poly dir member context");
515 return PAM_SESSION_ERR;
516 } else if (idata->flags & PAMNS_DEBUG)
517 pam_syslog(idata->pamh, LOG_DEBUG,
518 "member context returned by policy %s", *i_context);
524 * If polyinstantiating based on security level, get current
525 * process security context, get security class for directories,
526 * and change the directories MLS Level to match process.
529 if (polyptr->method == LEVEL) {
530 context_t scontext = NULL;
531 context_t fcontext = NULL;
532 rc = PAM_SESSION_ERR;
534 scontext = context_new(scon);
536 pam_syslog(idata->pamh, LOG_ERR, "out of memory");
539 fcontext = context_new(*origcon);
541 pam_syslog(idata->pamh, LOG_ERR, "out of memory");
544 if (context_range_set(fcontext, context_range_get(scontext)) != 0) {
545 pam_syslog(idata->pamh, LOG_ERR, "Unable to set MLS Componant of context");
548 *i_context=strdup(context_str(fcontext));
550 pam_syslog(idata->pamh, LOG_ERR, "out of memory");
556 context_free(scontext);
557 context_free(fcontext);
561 /* Should never get here */
567 * poly_name returns the name of the polyinstantiated instance directory
568 * based on the method used for polyinstantiation (user, context or both)
569 * In addition, the function also returns the security contexts of the
570 * original directory to polyinstantiate and the polyinstantiated instance
574 static int poly_name(const struct polydir_s *polyptr, char **i_name,
575 security_context_t *i_context, security_context_t *origcon,
576 struct instance_data *idata)
578 static int poly_name(const struct polydir_s *polyptr, char **i_name,
579 struct instance_data *idata)
585 security_context_t rawcon = NULL;
592 if ((idata->flags & PAMNS_SELINUX_ENABLED) &&
593 (rc=form_context(polyptr, i_context, origcon, idata)) != PAM_SUCCESS) {
598 rc = PAM_SESSION_ERR;
600 * Set the name of the polyinstantiated instance dir based on the
601 * polyinstantiation method.
603 switch (polyptr->method) {
605 if (asprintf(i_name, "%s", idata->user) < 0) {
614 if (selinux_trans_to_raw_context(*i_context, &rawcon) < 0) {
615 pam_syslog(idata->pamh, LOG_ERR, "Error translating directory context");
618 if (asprintf(i_name, "%s_%s", rawcon, idata->user) < 0) {
624 #endif /* WITH_SELINUX */
627 if (idata->flags & PAMNS_DEBUG)
628 pam_syslog(idata->pamh, LOG_ERR, "Unknown method");
632 if (idata->flags & PAMNS_DEBUG)
633 pam_syslog(idata->pamh, LOG_DEBUG, "poly_name %s", *i_name);
635 if ((idata->flags & PAMNS_GEN_HASH) || strlen(*i_name) > NAMESPACE_MAX_DIR_LEN) {
636 hash = md5hash(*i_name, idata);
640 if (idata->flags & PAMNS_GEN_HASH) {
646 if (asprintf(&newname, "%.*s_%s", NAMESPACE_MAX_DIR_LEN-1-strlen(hash),
647 *i_name, hash) < 0) {
661 if (rc != PAM_SUCCESS) {
674 static int check_inst_parent(char *ipath, struct instance_data *idata)
676 struct stat instpbuf;
677 char *inst_parent, *trailing_slash;
679 * stat the instance parent path to make sure it exists
680 * and is a directory. Check that its mode is 000 (unless the
681 * admin explicitly instructs to ignore the instance parent
682 * mode by the "ignore_instance_parent_mode" argument).
684 inst_parent = (char *) malloc(strlen(ipath)+1);
686 pam_syslog(idata->pamh, LOG_ERR, "Error allocating pathname string");
687 return PAM_SESSION_ERR;
690 strcpy(inst_parent, ipath);
691 trailing_slash = strrchr(inst_parent, '/');
693 *trailing_slash = '\0';
695 if (stat(inst_parent, &instpbuf) < 0) {
696 pam_syslog(idata->pamh, LOG_ERR, "Error stating %s, %m", inst_parent);
698 return PAM_SESSION_ERR;
702 * Make sure we are dealing with a directory
704 if (!S_ISDIR(instpbuf.st_mode)) {
705 pam_syslog(idata->pamh, LOG_ERR, "Instance parent %s is not a dir",
708 return PAM_SESSION_ERR;
711 if ((idata->flags & PAMNS_IGN_INST_PARENT_MODE) == 0) {
712 if (instpbuf.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) {
713 pam_syslog(idata->pamh, LOG_ERR, "Mode of inst parent %s not 000",
716 return PAM_SESSION_ERR;
724 * Check to see if there is a namespace initialization script in
725 * the /etc/security directory. If such a script exists
726 * execute it and pass directory to polyinstantiate and instance
727 * directory as arguments.
729 static int inst_init(const struct polydir_s *polyptr, char *ipath,
730 struct instance_data *idata)
733 sighandler_t osighand = NULL;
736 osighand = signal(SIGCHLD, SIG_DFL);
737 if (osighand == SIG_ERR) {
738 pam_syslog(idata->pamh, LOG_ERR, "Cannot set signal value");
739 rc = PAM_SESSION_ERR;
743 if (access(NAMESPACE_INIT_SCRIPT, F_OK) == 0) {
744 if (access(NAMESPACE_INIT_SCRIPT, X_OK) < 0) {
745 if (idata->flags & PAMNS_DEBUG)
746 pam_syslog(idata->pamh, LOG_ERR,
747 "Namespace init script not executable");
748 rc = PAM_SESSION_ERR;
754 if (idata->flags & PAMNS_SELINUX_ENABLED) {
755 if (setexeccon(NULL) < 0)
759 if (execl(NAMESPACE_INIT_SCRIPT, NAMESPACE_INIT_SCRIPT,
760 polyptr->dir, ipath, (char *)NULL) < 0)
762 } else if (pid > 0) {
763 while (((rc = waitpid(pid, &status, 0)) == (pid_t)-1) &&
765 if (rc == (pid_t)-1) {
766 pam_syslog(idata->pamh, LOG_ERR, "waitpid failed- %m");
767 rc = PAM_SESSION_ERR;
770 if (!WIFEXITED(status) || WIFSIGNALED(status) > 0) {
771 pam_syslog(idata->pamh, LOG_ERR,
772 "Error initializing instance");
773 rc = PAM_SESSION_ERR;
776 } else if (pid < 0) {
777 pam_syslog(idata->pamh, LOG_ERR,
778 "Cannot fork to run namespace init script, %m");
779 rc = PAM_SESSION_ERR;
786 (void) signal(SIGCHLD, osighand);
792 * Create polyinstantiated instance directory (ipath).
795 static int create_dirs(const struct polydir_s *polyptr, char *ipath,
796 security_context_t icontext, security_context_t ocontext,
797 struct instance_data *idata)
799 static int create_dirs(const struct polydir_s *polyptr, char *ipath,
800 struct instance_data *idata)
803 struct stat statbuf, newstatbuf;
807 * stat the directory to polyinstantiate, so its owner-group-mode
808 * can be propagated to instance directory
811 if (stat(polyptr->dir, &statbuf) < 0) {
812 pam_syslog(idata->pamh, LOG_ERR, "Error stating %s, %m",
814 return PAM_SESSION_ERR;
818 * Make sure we are dealing with a directory
820 if (!S_ISDIR(statbuf.st_mode)) {
821 pam_syslog(idata->pamh, LOG_ERR, "poly dir %s is not a dir",
823 return PAM_SESSION_ERR;
827 * Check to make sure instance parent is valid.
829 if (check_inst_parent(ipath, idata))
830 return PAM_SESSION_ERR;
833 * Create instance directory and set its security context to the context
834 * returned by the security policy. Set its mode and ownership
835 * attributes to match that of the original directory that is being
838 if (mkdir(ipath, S_IRUSR) < 0) {
842 pam_syslog(idata->pamh, LOG_ERR, "Error creating %s, %m",
844 return PAM_SESSION_ERR;
848 /* Open a descriptor to it to prevent races */
849 fd = open(ipath, O_DIRECTORY | O_RDONLY);
851 pam_syslog(idata->pamh, LOG_ERR, "Error opening %s, %m", ipath);
853 return PAM_SESSION_ERR;
856 /* If SE Linux is disabled, no need to label it */
857 if (idata->flags & PAMNS_SELINUX_ENABLED) {
858 /* If method is USER, icontext is NULL */
860 if (fsetfilecon(fd, icontext) < 0) {
861 pam_syslog(idata->pamh, LOG_ERR,
862 "Error setting context of %s to %s", ipath, icontext);
865 return PAM_SESSION_ERR;
868 if (fsetfilecon(fd, ocontext) < 0) {
869 pam_syslog(idata->pamh, LOG_ERR,
870 "Error setting context of %s to %s", ipath, ocontext);
873 return PAM_SESSION_ERR;
878 if (fstat(fd, &newstatbuf) < 0) {
879 pam_syslog(idata->pamh, LOG_ERR, "Error stating %s, %m",
882 return PAM_SESSION_ERR;
884 if (newstatbuf.st_uid != statbuf.st_uid ||
885 newstatbuf.st_gid != statbuf.st_gid) {
886 if (fchown(fd, statbuf.st_uid, statbuf.st_gid) < 0) {
887 pam_syslog(idata->pamh, LOG_ERR,
888 "Error changing owner for %s, %m",
892 return PAM_SESSION_ERR;
895 if (fchmod(fd, statbuf.st_mode & 07777) < 0) {
896 pam_syslog(idata->pamh, LOG_ERR, "Error changing mode for %s, %m",
900 return PAM_SESSION_ERR;
905 * Check to see if there is a namespace initialization script in
906 * the /etc/security directory. If such a script exists
907 * execute it and pass directory to polyinstantiate and instance
908 * directory as arguments.
912 rc = inst_init(polyptr, ipath, idata);
918 * This function performs the namespace setup for a particular directory
919 * that is being polyinstantiated. It creates an MD5 hash of instance
920 * directory, calls create_dirs to create it with appropriate
921 * security attributes, and performs bind mount to setup the process
924 static int ns_setup(const struct polydir_s *polyptr,
925 struct instance_data *idata)
928 char *inst_dir = NULL;
929 char *instname = NULL;
932 security_context_t instcontext = NULL, origcontext = NULL;
935 if (idata->flags & PAMNS_DEBUG)
936 pam_syslog(idata->pamh, LOG_DEBUG,
937 "Set namespace for directory %s", polyptr->dir);
939 dir = strrchr(polyptr->dir, '/');
940 if (dir && strlen(dir) > 1)
944 * Obtain the name of instance pathname based on the
945 * polyinstantiation method and instance context returned by
949 retval = poly_name(polyptr, &instname, &instcontext,
950 &origcontext, idata);
952 retval = poly_name(polyptr, &instname, idata);
956 pam_syslog(idata->pamh, LOG_ERR, "Error getting instance name");
960 if ((idata->flags & PAMNS_DEBUG) &&
961 (idata->flags & PAMNS_SELINUX_ENABLED))
962 pam_syslog(idata->pamh, LOG_DEBUG, "Inst ctxt %s Orig ctxt %s",
963 instcontext, origcontext);
967 if (asprintf(&inst_dir, "%s%s", polyptr->instance_prefix, instname) < 0)
970 if (idata->flags & PAMNS_DEBUG)
971 pam_syslog(idata->pamh, LOG_DEBUG, "instance_dir %s",
975 * Create instance directory with appropriate security
976 * contexts, owner, group and mode bits.
979 retval = create_dirs(polyptr, inst_dir, instcontext,
982 retval = create_dirs(polyptr, inst_dir, idata);
986 pam_syslog(idata->pamh, LOG_ERR, "Error creating instance dir");
991 * Bind mount instance directory on top of the polyinstantiated
992 * directory to provide an instance of polyinstantiated directory
993 * based on polyinstantiated method.
995 if (mount(inst_dir, polyptr->dir, NULL, MS_BIND, NULL) < 0) {
996 pam_syslog(idata->pamh, LOG_ERR, "Error mounting %s on %s, %m",
997 inst_dir, polyptr->dir);
1004 * various error exit points. Free allocated memory and set return
1005 * value to indicate a pam session error.
1008 retval = PAM_SESSION_ERR;
1014 freecon(instcontext);
1015 freecon(origcontext);
1022 * This function checks to see if the current working directory is
1023 * inside the directory passed in as the first argument.
1025 static int cwd_in(char *dir, struct instance_data *idata)
1030 if (getcwd(cwd, PATH_MAX) == NULL) {
1031 pam_syslog(idata->pamh, LOG_ERR, "Can't get current dir, %m");
1035 if (strncmp(cwd, dir, strlen(dir)) == 0) {
1036 if (idata->flags & PAMNS_DEBUG)
1037 pam_syslog(idata->pamh, LOG_DEBUG, "cwd is inside %s", dir);
1040 if (idata->flags & PAMNS_DEBUG)
1041 pam_syslog(idata->pamh, LOG_DEBUG, "cwd is outside %s", dir);
1049 * This function checks to see if polyinstantiation is needed for any
1050 * of the directories listed in the configuration file. If needed,
1051 * cycles through all polyinstantiated directory entries and calls
1052 * ns_setup to setup polyinstantiation for each one of them.
1054 static int setup_namespace(struct instance_data *idata, enum unmnt_op unmnt)
1056 int retval = 0, need_poly = 0, changing_dir = 0;
1057 char *cptr, *fptr, poly_parent[PATH_MAX];
1058 struct polydir_s *pptr;
1060 const void *ruser_name;
1063 if (idata->flags & PAMNS_DEBUG)
1064 pam_syslog(idata->pamh, LOG_DEBUG, "Set up namespace for pid %d",
1067 retval = pam_get_item(idata->pamh, PAM_RUSER, &ruser_name);
1068 if (ruser_name == NULL || retval != PAM_SUCCESS) {
1069 retval = PAM_SUCCESS;
1072 pwd = pam_modutil_getpwnam(idata->pamh, ruser_name);
1074 req_uid = pwd->pw_uid;
1081 * Cycle through all polyinstantiated directory entries to see if
1082 * polyinstantiation is needed at all.
1084 for (pptr = idata->polydirs_ptr; pptr; pptr = pptr->next) {
1085 if (ns_override(pptr, idata, idata->uid)) {
1086 if (unmnt == NO_UNMNT || ns_override(pptr, idata, req_uid)) {
1087 if (idata->flags & PAMNS_DEBUG)
1088 pam_syslog(idata->pamh, LOG_DEBUG,
1089 "Overriding poly for user %d for dir %s",
1090 idata->uid, pptr->dir);
1092 if (idata->flags & PAMNS_DEBUG)
1093 pam_syslog(idata->pamh, LOG_DEBUG,
1094 "Need unmount ns for user %d for dir %s",
1095 idata->uid, pptr->dir);
1101 if (idata->flags & PAMNS_DEBUG)
1102 pam_syslog(idata->pamh, LOG_DEBUG,
1103 "Need poly ns for user %d for dir %s",
1104 idata->uid, pptr->dir);
1111 * If polyinstnatiation is needed, call the unshare system call to
1112 * disassociate from the parent namespace.
1115 if (unshare(CLONE_NEWNS) < 0) {
1116 pam_syslog(idata->pamh, LOG_ERR,
1117 "Unable to unshare from parent namespace, %m");
1118 return PAM_SESSION_ERR;
1124 * Again cycle through all polyinstantiated directories, this time,
1125 * call ns_setup to setup polyinstantiation for a particular entry.
1127 for (pptr = idata->polydirs_ptr; pptr; pptr = pptr->next) {
1128 enum unmnt_op dir_unmnt = unmnt;
1129 if (ns_override(pptr, idata, idata->uid)) {
1130 if (unmnt == NO_UNMNT || ns_override(pptr, idata, req_uid)) {
1133 dir_unmnt = UNMNT_ONLY;
1136 if (idata->flags & PAMNS_DEBUG)
1137 pam_syslog(idata->pamh, LOG_DEBUG,
1138 "Setting poly ns for user %d for dir %s",
1139 idata->uid, pptr->dir);
1141 if ((dir_unmnt == UNMNT_REMNT) || (dir_unmnt == UNMNT_ONLY)) {
1143 * Check to see if process current directory is in the
1144 * bind mounted instance_parent directory that we are trying to
1147 if ((changing_dir = cwd_in(pptr->dir, idata)) < 0) {
1148 return PAM_SESSION_ERR;
1149 } else if (changing_dir) {
1150 if (idata->flags & PAMNS_DEBUG)
1151 pam_syslog(idata->pamh, LOG_DEBUG, "changing cwd");
1154 * Change current working directory to the parent of
1155 * the mount point, that is parent of the orig
1156 * directory where original contents of the polydir
1157 * are available from
1159 strcpy(poly_parent, pptr->dir);
1160 fptr = strchr(poly_parent, '/');
1161 cptr = strrchr(poly_parent, '/');
1162 if (fptr && cptr && (fptr == cptr))
1163 strcpy(poly_parent, "/");
1166 if (chdir(poly_parent) < 0) {
1167 pam_syslog(idata->pamh, LOG_ERR,
1168 "Can't chdir to %s, %m", poly_parent);
1172 if (umount(pptr->dir) < 0) {
1173 int saved_errno = errno;
1174 pam_syslog(idata->pamh, LOG_ERR, "Unmount of %s failed, %m",
1176 if (saved_errno != EINVAL)
1177 return PAM_SESSION_ERR;
1178 } else if (idata->flags & PAMNS_DEBUG)
1179 pam_syslog(idata->pamh, LOG_DEBUG, "Umount succeeded %s",
1183 if (dir_unmnt != UNMNT_ONLY) {
1184 retval = ns_setup(pptr, idata);
1185 if (retval != PAM_SUCCESS)
1195 * Orig namespace. This function is called from when closing a pam
1196 * session. If authorized, it unmounts instance directory.
1198 static int orig_namespace(struct instance_data *idata)
1200 struct polydir_s *pptr;
1202 if (idata->flags & PAMNS_DEBUG)
1203 pam_syslog(idata->pamh, LOG_DEBUG, "orig namespace for pid %d",
1207 * Cycle through all polyinstantiated directories from the namespace
1208 * configuration file to see if polyinstantiation was performed for
1209 * this user for each of the entry. If it was, try and unmount
1210 * appropriate polyinstantiated instance directories.
1212 for (pptr = idata->polydirs_ptr; pptr; pptr = pptr->next) {
1213 if (ns_override(pptr, idata, idata->uid))
1216 if (idata->flags & PAMNS_DEBUG)
1217 pam_syslog(idata->pamh, LOG_DEBUG,
1218 "Unmounting instance dir for user %d & dir %s",
1219 idata->uid, pptr->dir);
1221 if (umount(pptr->dir) < 0) {
1222 pam_syslog(idata->pamh, LOG_ERR, "Unmount of %s failed, %m",
1224 return PAM_SESSION_ERR;
1225 } else if (idata->flags & PAMNS_DEBUG)
1226 pam_syslog(idata->pamh, LOG_DEBUG, "Unmount of %s succeeded",
1236 * This function checks if the calling program has requested context
1237 * change by calling setexeccon(). If context change is not requested
1238 * then it does not make sense to polyinstantiate based on context.
1239 * The return value from this function is used when selecting the
1240 * polyinstantiation method. If context change is not requested then
1241 * the polyinstantiation method is set to USER, even if the configuration
1242 * file lists the method as "context" or "both".
1244 static int ctxt_based_inst_needed(void)
1246 security_context_t scon = NULL;
1249 rc = getexeccon(&scon);
1250 if (rc < 0 || scon == NULL)
1261 * Entry point from pam_open_session call.
1263 PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED,
1264 int argc, const char **argv)
1267 struct instance_data idata;
1270 enum unmnt_op unmnt = NO_UNMNT;
1272 /* init instance data */
1274 idata.polydirs_ptr = NULL;
1277 if (is_selinux_enabled())
1278 idata.flags |= PAMNS_SELINUX_ENABLED;
1279 if (ctxt_based_inst_needed())
1280 idata.flags |= PAMNS_CTXT_BASED_INST;
1283 /* Parse arguments. */
1284 for (i = 0; i < argc; i++) {
1285 if (strcmp(argv[i], "debug") == 0)
1286 idata.flags |= PAMNS_DEBUG;
1287 if (strcmp(argv[i], "gen_hash") == 0)
1288 idata.flags |= PAMNS_GEN_HASH;
1289 if (strcmp(argv[i], "ignore_config_error") == 0)
1290 idata.flags |= PAMNS_IGN_CONFIG_ERR;
1291 if (strcmp(argv[i], "ignore_instance_parent_mode") == 0)
1292 idata.flags |= PAMNS_IGN_INST_PARENT_MODE;
1293 if (strcmp(argv[i], "unmnt_remnt") == 0)
1294 unmnt = UNMNT_REMNT;
1295 if (strcmp(argv[i], "unmnt_only") == 0)
1297 if (strcmp(argv[i], "require_selinux") == 0) {
1298 if (~(idata.flags & PAMNS_SELINUX_ENABLED)) {
1299 pam_syslog(idata.pamh, LOG_ERR,
1300 "selinux_required option given and selinux is disabled");
1301 return PAM_SESSION_ERR;
1305 if (idata.flags & PAMNS_DEBUG)
1306 pam_syslog(idata.pamh, LOG_DEBUG, "open_session - start");
1309 * Lookup user and fill struct items
1311 retval = pam_get_item(idata.pamh, PAM_USER, (void*) &user_name );
1312 if ( user_name == NULL || retval != PAM_SUCCESS ) {
1313 pam_syslog(idata.pamh, LOG_ERR, "Error recovering pam user name");
1314 return PAM_SESSION_ERR;
1317 pwd = pam_modutil_getpwnam(idata.pamh, user_name);
1319 pam_syslog(idata.pamh, LOG_ERR, "user unknown '%s'", user_name);
1320 return PAM_SESSION_ERR;
1324 * Add the user info to the instance data so we can refer to them later.
1327 strncat(idata.user, user_name, sizeof(idata.user) - 1);
1328 idata.uid = pwd->pw_uid;
1331 * Parse namespace configuration file which lists directories to
1332 * polyinstantiate, directory where instance directories are to
1333 * be created and the method used for polyinstantiation.
1335 retval = parse_config_file(&idata);
1336 if (retval != PAM_SUCCESS) {
1337 del_polydir_list(idata.polydirs_ptr);
1338 return PAM_SESSION_ERR;
1341 if (idata.polydirs_ptr) {
1342 retval = setup_namespace(&idata, unmnt);
1343 if (idata.flags & PAMNS_DEBUG) {
1345 pam_syslog(idata.pamh, LOG_DEBUG,
1346 "namespace setup failed for pid %d", getpid());
1348 pam_syslog(idata.pamh, LOG_DEBUG,
1349 "namespace setup ok for pid %d", getpid());
1351 } else if (idata.flags & PAMNS_DEBUG)
1352 pam_syslog(idata.pamh, LOG_DEBUG, "Nothing to polyinstantiate");
1354 del_polydir_list(idata.polydirs_ptr);
1360 * Entry point from pam_close_session call.
1362 PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags UNUSED,
1363 int argc, const char **argv)
1366 struct instance_data idata;
1370 /* init instance data */
1372 idata.polydirs_ptr = NULL;
1375 if (is_selinux_enabled())
1376 idata.flags |= PAMNS_SELINUX_ENABLED;
1377 if (ctxt_based_inst_needed())
1378 idata.flags |= PAMNS_CTXT_BASED_INST;
1381 /* Parse arguments. */
1382 for (i = 0; i < argc; i++) {
1383 if (strcmp(argv[i], "debug") == 0)
1384 idata.flags |= PAMNS_DEBUG;
1385 if (strcmp(argv[i], "ignore_config_error") == 0)
1386 idata.flags |= PAMNS_IGN_CONFIG_ERR;
1387 if (strcmp(argv[i], "no_unmount_on_close") == 0)
1388 idata.flags |= PAMNS_NO_UNMOUNT_ON_CLOSE;
1391 if (idata.flags & PAMNS_DEBUG)
1392 pam_syslog(idata.pamh, LOG_DEBUG, "close_session - start");
1395 * For certain trusted programs such as newrole, open session
1396 * is called from a child process while the parent perfoms
1397 * close session and pam end functions. For these commands
1398 * pam_close_session should not perform the unmount of the
1399 * polyinstantiatied directory because it will result in
1400 * undoing of parents polyinstantiatiaion. These commands
1401 * will invoke pam_namespace with the "no_unmount_on_close"
1404 if (idata.flags & PAMNS_NO_UNMOUNT_ON_CLOSE) {
1405 if (idata.flags & PAMNS_DEBUG)
1406 pam_syslog(idata.pamh, LOG_DEBUG, "close_session - sucessful");
1411 * Lookup user and fill struct items
1413 retval = pam_get_item(idata.pamh, PAM_USER, (void*) &user_name );
1414 if ( user_name == NULL || retval != PAM_SUCCESS ) {
1415 pam_syslog(idata.pamh, LOG_ERR, "Error recovering pam user name");
1416 return PAM_SESSION_ERR;
1419 pwd = pam_modutil_getpwnam(idata.pamh, user_name);
1421 pam_syslog(idata.pamh, LOG_ERR, "user unknown '%s'", user_name);
1422 return PAM_SESSION_ERR;
1426 * Add the user info to the instance data so we can refer to them later.
1429 strncat(idata.user, user_name, sizeof(idata.user) - 1);
1430 idata.uid = pwd->pw_uid;
1433 * Parse namespace configuration file which lists directories that
1434 * are polyinstantiated, directories where instance directories are
1435 * created and the method used for polyinstantiation.
1437 retval = parse_config_file(&idata);
1438 if ((retval != PAM_SUCCESS) || !idata.polydirs_ptr) {
1439 del_polydir_list(idata.polydirs_ptr);
1440 return PAM_SESSION_ERR;
1443 if (idata.flags & PAMNS_DEBUG)
1444 pam_syslog(idata.pamh, LOG_DEBUG, "Resetting namespace for pid %d",
1447 retval = orig_namespace(&idata);
1448 if (idata.flags & PAMNS_DEBUG) {
1450 pam_syslog(idata.pamh, LOG_DEBUG,
1451 "resetting namespace failed for pid %d", getpid());
1453 pam_syslog(idata.pamh, LOG_DEBUG,
1454 "resetting namespace ok for pid %d", getpid());
1456 del_polydir_list(idata.polydirs_ptr);
1462 /* static module data */
1464 struct pam_module _pam_namespace_modstruct = {
1469 pam_sm_open_session,
1470 pam_sm_close_session,