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 ((rc=form_context(polyptr, i_context, origcon, idata)) != PAM_SUCCESS) {
597 rc = PAM_SESSION_ERR;
599 * Set the name of the polyinstantiated instance dir based on the
600 * polyinstantiation method.
602 switch (polyptr->method) {
604 if (asprintf(i_name, "%s", idata->user) < 0) {
613 if (selinux_trans_to_raw_context(*i_context, &rawcon) < 0) {
614 pam_syslog(idata->pamh, LOG_ERR, "Error translating directory context");
617 if (asprintf(i_name, "%s_%s", rawcon, idata->user) < 0) {
623 #endif /* WITH_SELINUX */
626 if (idata->flags & PAMNS_DEBUG)
627 pam_syslog(idata->pamh, LOG_ERR, "Unknown method");
631 if (idata->flags & PAMNS_DEBUG)
632 pam_syslog(idata->pamh, LOG_DEBUG, "poly_name %s", *i_name);
634 if ((idata->flags & PAMNS_GEN_HASH) || strlen(*i_name) > NAMESPACE_MAX_DIR_LEN) {
635 hash = md5hash(*i_name, idata);
639 if (idata->flags & PAMNS_GEN_HASH) {
645 if (asprintf(&newname, "%.*s_%s", NAMESPACE_MAX_DIR_LEN-1-strlen(hash),
646 *i_name, hash) < 0) {
660 if (rc != PAM_SUCCESS) {
673 static int check_inst_parent(char *ipath, struct instance_data *idata)
675 struct stat instpbuf;
676 char *inst_parent, *trailing_slash;
678 * stat the instance parent path to make sure it exists
679 * and is a directory. Check that its mode is 000 (unless the
680 * admin explicitly instructs to ignore the instance parent
681 * mode by the "ignore_instance_parent_mode" argument).
683 inst_parent = (char *) malloc(strlen(ipath)+1);
685 pam_syslog(idata->pamh, LOG_ERR, "Error allocating pathname string");
686 return PAM_SESSION_ERR;
689 strcpy(inst_parent, ipath);
690 trailing_slash = strrchr(inst_parent, '/');
692 *trailing_slash = '\0';
694 if (stat(inst_parent, &instpbuf) < 0) {
695 pam_syslog(idata->pamh, LOG_ERR, "Error stating %s, %m", inst_parent);
697 return PAM_SESSION_ERR;
701 * Make sure we are dealing with a directory
703 if (!S_ISDIR(instpbuf.st_mode)) {
704 pam_syslog(idata->pamh, LOG_ERR, "Instance parent %s is not a dir",
707 return PAM_SESSION_ERR;
710 if ((idata->flags & PAMNS_IGN_INST_PARENT_MODE) == 0) {
711 if (instpbuf.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) {
712 pam_syslog(idata->pamh, LOG_ERR, "Mode of inst parent %s not 000",
715 return PAM_SESSION_ERR;
723 * Check to see if there is a namespace initialization script in
724 * the /etc/security directory. If such a script exists
725 * execute it and pass directory to polyinstantiate and instance
726 * directory as arguments.
728 static int inst_init(const struct polydir_s *polyptr, char *ipath,
729 struct instance_data *idata)
732 sighandler_t osighand = NULL;
735 osighand = signal(SIGCHLD, SIG_DFL);
736 if (osighand == SIG_ERR) {
737 pam_syslog(idata->pamh, LOG_ERR, "Cannot set signal value");
738 rc = PAM_SESSION_ERR;
742 if (access(NAMESPACE_INIT_SCRIPT, F_OK) == 0) {
743 if (access(NAMESPACE_INIT_SCRIPT, X_OK) < 0) {
744 if (idata->flags & PAMNS_DEBUG)
745 pam_syslog(idata->pamh, LOG_ERR,
746 "Namespace init script not executable");
747 rc = PAM_SESSION_ERR;
753 if (idata->flags & PAMNS_SELINUX_ENABLED) {
754 if (setexeccon(NULL) < 0)
758 if (execl(NAMESPACE_INIT_SCRIPT, NAMESPACE_INIT_SCRIPT,
759 polyptr->dir, ipath, (char *)NULL) < 0)
761 } else if (pid > 0) {
762 while (((rc = waitpid(pid, &status, 0)) == (pid_t)-1) &&
764 if (rc == (pid_t)-1) {
765 pam_syslog(idata->pamh, LOG_ERR, "waitpid failed- %m");
766 rc = PAM_SESSION_ERR;
769 if (!WIFEXITED(status) || WIFSIGNALED(status) > 0) {
770 pam_syslog(idata->pamh, LOG_ERR,
771 "Error initializing instance");
772 rc = PAM_SESSION_ERR;
775 } else if (pid < 0) {
776 pam_syslog(idata->pamh, LOG_ERR,
777 "Cannot fork to run namespace init script, %m");
778 rc = PAM_SESSION_ERR;
785 (void) signal(SIGCHLD, osighand);
791 * Create polyinstantiated instance directory (ipath).
794 static int create_dirs(const struct polydir_s *polyptr, char *ipath,
795 security_context_t icontext, security_context_t ocontext,
796 struct instance_data *idata)
798 static int create_dirs(const struct polydir_s *polyptr, char *ipath,
799 struct instance_data *idata)
802 struct stat statbuf, newstatbuf;
806 * stat the directory to polyinstantiate, so its owner-group-mode
807 * can be propagated to instance directory
810 if (stat(polyptr->dir, &statbuf) < 0) {
811 pam_syslog(idata->pamh, LOG_ERR, "Error stating %s, %m",
813 return PAM_SESSION_ERR;
817 * Make sure we are dealing with a directory
819 if (!S_ISDIR(statbuf.st_mode)) {
820 pam_syslog(idata->pamh, LOG_ERR, "poly dir %s is not a dir",
822 return PAM_SESSION_ERR;
826 * Check to make sure instance parent is valid.
828 if (check_inst_parent(ipath, idata))
829 return PAM_SESSION_ERR;
832 * Create instance directory and set its security context to the context
833 * returned by the security policy. Set its mode and ownership
834 * attributes to match that of the original directory that is being
837 if (mkdir(ipath, S_IRUSR) < 0) {
841 pam_syslog(idata->pamh, LOG_ERR, "Error creating %s, %m",
843 return PAM_SESSION_ERR;
847 /* Open a descriptor to it to prevent races */
848 fd = open(ipath, O_DIRECTORY | O_RDONLY);
850 pam_syslog(idata->pamh, LOG_ERR, "Error opening %s, %m", ipath);
852 return PAM_SESSION_ERR;
855 /* If SE Linux is disabled, no need to label it */
856 if (idata->flags & PAMNS_SELINUX_ENABLED) {
857 /* If method is USER, icontext is NULL */
859 if (fsetfilecon(fd, icontext) < 0) {
860 pam_syslog(idata->pamh, LOG_ERR,
861 "Error setting context of %s to %s", ipath, icontext);
864 return PAM_SESSION_ERR;
867 if (fsetfilecon(fd, ocontext) < 0) {
868 pam_syslog(idata->pamh, LOG_ERR,
869 "Error setting context of %s to %s", ipath, ocontext);
872 return PAM_SESSION_ERR;
877 if (fstat(fd, &newstatbuf) < 0) {
878 pam_syslog(idata->pamh, LOG_ERR, "Error stating %s, %m",
881 return PAM_SESSION_ERR;
883 if (newstatbuf.st_uid != statbuf.st_uid ||
884 newstatbuf.st_gid != statbuf.st_gid) {
885 if (fchown(fd, statbuf.st_uid, statbuf.st_gid) < 0) {
886 pam_syslog(idata->pamh, LOG_ERR,
887 "Error changing owner for %s, %m",
891 return PAM_SESSION_ERR;
894 if (fchmod(fd, statbuf.st_mode & 07777) < 0) {
895 pam_syslog(idata->pamh, LOG_ERR, "Error changing mode for %s, %m",
899 return PAM_SESSION_ERR;
904 * Check to see if there is a namespace initialization script in
905 * the /etc/security directory. If such a script exists
906 * execute it and pass directory to polyinstantiate and instance
907 * directory as arguments.
911 rc = inst_init(polyptr, ipath, idata);
917 * This function performs the namespace setup for a particular directory
918 * that is being polyinstantiated. It creates an MD5 hash of instance
919 * directory, calls create_dirs to create it with appropriate
920 * security attributes, and performs bind mount to setup the process
923 static int ns_setup(const struct polydir_s *polyptr,
924 struct instance_data *idata)
927 char *inst_dir = NULL;
928 char *instname = NULL;
931 security_context_t instcontext = NULL, origcontext = NULL;
934 if (idata->flags & PAMNS_DEBUG)
935 pam_syslog(idata->pamh, LOG_DEBUG,
936 "Set namespace for directory %s", polyptr->dir);
938 dir = strrchr(polyptr->dir, '/');
939 if (dir && strlen(dir) > 1)
943 * Obtain the name of instance pathname based on the
944 * polyinstantiation method and instance context returned by
948 retval = poly_name(polyptr, &instname, &instcontext,
949 &origcontext, idata);
951 retval = poly_name(polyptr, &instname, idata);
955 pam_syslog(idata->pamh, LOG_ERR, "Error getting instance name");
959 if ((idata->flags & PAMNS_DEBUG) &&
960 (idata->flags & PAMNS_SELINUX_ENABLED))
961 pam_syslog(idata->pamh, LOG_DEBUG, "Inst ctxt %s Orig ctxt %s",
962 instcontext, origcontext);
966 if (asprintf(&inst_dir, "%s%s", polyptr->instance_prefix, instname) < 0)
969 if (idata->flags & PAMNS_DEBUG)
970 pam_syslog(idata->pamh, LOG_DEBUG, "instance_dir %s",
974 * Create instance directory with appropriate security
975 * contexts, owner, group and mode bits.
978 retval = create_dirs(polyptr, inst_dir, instcontext,
981 retval = create_dirs(polyptr, inst_dir, idata);
985 pam_syslog(idata->pamh, LOG_ERR, "Error creating instance dir");
990 * Bind mount instance directory on top of the polyinstantiated
991 * directory to provide an instance of polyinstantiated directory
992 * based on polyinstantiated method.
994 if (mount(inst_dir, polyptr->dir, NULL, MS_BIND, NULL) < 0) {
995 pam_syslog(idata->pamh, LOG_ERR, "Error mounting %s on %s, %m",
996 inst_dir, polyptr->dir);
1003 * various error exit points. Free allocated memory and set return
1004 * value to indicate a pam session error.
1007 retval = PAM_SESSION_ERR;
1013 freecon(instcontext);
1014 freecon(origcontext);
1021 * This function checks to see if the current working directory is
1022 * inside the directory passed in as the first argument.
1024 static int cwd_in(char *dir, struct instance_data *idata)
1029 if (getcwd(cwd, PATH_MAX) == NULL) {
1030 pam_syslog(idata->pamh, LOG_ERR, "Can't get current dir, %m");
1034 if (strncmp(cwd, dir, strlen(dir)) == 0) {
1035 if (idata->flags & PAMNS_DEBUG)
1036 pam_syslog(idata->pamh, LOG_DEBUG, "cwd is inside %s", dir);
1039 if (idata->flags & PAMNS_DEBUG)
1040 pam_syslog(idata->pamh, LOG_DEBUG, "cwd is outside %s", dir);
1048 * This function checks to see if polyinstantiation is needed for any
1049 * of the directories listed in the configuration file. If needed,
1050 * cycles through all polyinstantiated directory entries and calls
1051 * ns_setup to setup polyinstantiation for each one of them.
1053 static int setup_namespace(struct instance_data *idata, enum unmnt_op unmnt)
1055 int retval = 0, need_poly = 0, changing_dir = 0;
1056 char *cptr, *fptr, poly_parent[PATH_MAX];
1057 struct polydir_s *pptr;
1059 const void *ruser_name;
1062 if (idata->flags & PAMNS_DEBUG)
1063 pam_syslog(idata->pamh, LOG_DEBUG, "Set up namespace for pid %d",
1066 retval = pam_get_item(idata->pamh, PAM_RUSER, &ruser_name);
1067 if (ruser_name == NULL || retval != PAM_SUCCESS) {
1068 retval = PAM_SUCCESS;
1071 pwd = pam_modutil_getpwnam(idata->pamh, ruser_name);
1073 req_uid = pwd->pw_uid;
1080 * Cycle through all polyinstantiated directory entries to see if
1081 * polyinstantiation is needed at all.
1083 for (pptr = idata->polydirs_ptr; pptr; pptr = pptr->next) {
1084 if (ns_override(pptr, idata, idata->uid)) {
1085 if (unmnt == NO_UNMNT || ns_override(pptr, idata, req_uid)) {
1086 if (idata->flags & PAMNS_DEBUG)
1087 pam_syslog(idata->pamh, LOG_DEBUG,
1088 "Overriding poly for user %d for dir %s",
1089 idata->uid, pptr->dir);
1091 if (idata->flags & PAMNS_DEBUG)
1092 pam_syslog(idata->pamh, LOG_DEBUG,
1093 "Need unmount ns for user %d for dir %s",
1094 idata->uid, pptr->dir);
1100 if (idata->flags & PAMNS_DEBUG)
1101 pam_syslog(idata->pamh, LOG_DEBUG,
1102 "Need poly ns for user %d for dir %s",
1103 idata->uid, pptr->dir);
1110 * If polyinstnatiation is needed, call the unshare system call to
1111 * disassociate from the parent namespace.
1114 if (unshare(CLONE_NEWNS) < 0) {
1115 pam_syslog(idata->pamh, LOG_ERR,
1116 "Unable to unshare from parent namespace, %m");
1117 return PAM_SESSION_ERR;
1123 * Again cycle through all polyinstantiated directories, this time,
1124 * call ns_setup to setup polyinstantiation for a particular entry.
1126 for (pptr = idata->polydirs_ptr; pptr; pptr = pptr->next) {
1127 enum unmnt_op dir_unmnt = unmnt;
1128 if (ns_override(pptr, idata, idata->uid)) {
1129 if (unmnt == NO_UNMNT || ns_override(pptr, idata, req_uid)) {
1132 dir_unmnt = UNMNT_ONLY;
1135 if (idata->flags & PAMNS_DEBUG)
1136 pam_syslog(idata->pamh, LOG_DEBUG,
1137 "Setting poly ns for user %d for dir %s",
1138 idata->uid, pptr->dir);
1140 if ((dir_unmnt == UNMNT_REMNT) || (dir_unmnt == UNMNT_ONLY)) {
1142 * Check to see if process current directory is in the
1143 * bind mounted instance_parent directory that we are trying to
1146 if ((changing_dir = cwd_in(pptr->dir, idata)) < 0) {
1147 return PAM_SESSION_ERR;
1148 } else if (changing_dir) {
1149 if (idata->flags & PAMNS_DEBUG)
1150 pam_syslog(idata->pamh, LOG_DEBUG, "changing cwd");
1153 * Change current working directory to the parent of
1154 * the mount point, that is parent of the orig
1155 * directory where original contents of the polydir
1156 * are available from
1158 strcpy(poly_parent, pptr->dir);
1159 fptr = strchr(poly_parent, '/');
1160 cptr = strrchr(poly_parent, '/');
1161 if (fptr && cptr && (fptr == cptr))
1162 strcpy(poly_parent, "/");
1165 if (chdir(poly_parent) < 0) {
1166 pam_syslog(idata->pamh, LOG_ERR,
1167 "Can't chdir to %s, %m", poly_parent);
1171 if (umount(pptr->dir) < 0) {
1172 int saved_errno = errno;
1173 pam_syslog(idata->pamh, LOG_ERR, "Unmount of %s failed, %m",
1175 if (saved_errno != EINVAL)
1176 return PAM_SESSION_ERR;
1177 } else if (idata->flags & PAMNS_DEBUG)
1178 pam_syslog(idata->pamh, LOG_DEBUG, "Umount succeeded %s",
1182 if (dir_unmnt != UNMNT_ONLY) {
1183 retval = ns_setup(pptr, idata);
1184 if (retval != PAM_SUCCESS)
1194 * Orig namespace. This function is called from when closing a pam
1195 * session. If authorized, it unmounts instance directory.
1197 static int orig_namespace(struct instance_data *idata)
1199 struct polydir_s *pptr;
1201 if (idata->flags & PAMNS_DEBUG)
1202 pam_syslog(idata->pamh, LOG_DEBUG, "orig namespace for pid %d",
1206 * Cycle through all polyinstantiated directories from the namespace
1207 * configuration file to see if polyinstantiation was performed for
1208 * this user for each of the entry. If it was, try and unmount
1209 * appropriate polyinstantiated instance directories.
1211 for (pptr = idata->polydirs_ptr; pptr; pptr = pptr->next) {
1212 if (ns_override(pptr, idata, idata->uid))
1215 if (idata->flags & PAMNS_DEBUG)
1216 pam_syslog(idata->pamh, LOG_DEBUG,
1217 "Unmounting instance dir for user %d & dir %s",
1218 idata->uid, pptr->dir);
1220 if (umount(pptr->dir) < 0) {
1221 pam_syslog(idata->pamh, LOG_ERR, "Unmount of %s failed, %m",
1223 return PAM_SESSION_ERR;
1224 } else if (idata->flags & PAMNS_DEBUG)
1225 pam_syslog(idata->pamh, LOG_DEBUG, "Unmount of %s succeeded",
1235 * This function checks if the calling program has requested context
1236 * change by calling setexeccon(). If context change is not requested
1237 * then it does not make sense to polyinstantiate based on context.
1238 * The return value from this function is used when selecting the
1239 * polyinstantiation method. If context change is not requested then
1240 * the polyinstantiation method is set to USER, even if the configuration
1241 * file lists the method as "context" or "both".
1243 static int ctxt_based_inst_needed(void)
1245 security_context_t scon = NULL;
1248 rc = getexeccon(&scon);
1249 if (rc < 0 || scon == NULL)
1260 * Entry point from pam_open_session call.
1262 PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED,
1263 int argc, const char **argv)
1266 struct instance_data idata;
1269 enum unmnt_op unmnt = NO_UNMNT;
1271 /* init instance data */
1273 idata.polydirs_ptr = NULL;
1276 if (is_selinux_enabled())
1277 idata.flags |= PAMNS_SELINUX_ENABLED;
1278 if (ctxt_based_inst_needed())
1279 idata.flags |= PAMNS_CTXT_BASED_INST;
1282 /* Parse arguments. */
1283 for (i = 0; i < argc; i++) {
1284 if (strcmp(argv[i], "debug") == 0)
1285 idata.flags |= PAMNS_DEBUG;
1286 if (strcmp(argv[i], "gen_hash") == 0)
1287 idata.flags |= PAMNS_GEN_HASH;
1288 if (strcmp(argv[i], "ignore_config_error") == 0)
1289 idata.flags |= PAMNS_IGN_CONFIG_ERR;
1290 if (strcmp(argv[i], "ignore_instance_parent_mode") == 0)
1291 idata.flags |= PAMNS_IGN_INST_PARENT_MODE;
1292 if (strcmp(argv[i], "unmnt_remnt") == 0)
1293 unmnt = UNMNT_REMNT;
1294 if (strcmp(argv[i], "unmnt_only") == 0)
1296 if (strcmp(argv[i], "require_selinux") == 0) {
1297 if (~(idata.flags & PAMNS_SELINUX_ENABLED)) {
1298 pam_syslog(idata.pamh, LOG_ERR,
1299 "selinux_required option given and selinux is disabled");
1300 return PAM_SESSION_ERR;
1304 if (idata.flags & PAMNS_DEBUG)
1305 pam_syslog(idata.pamh, LOG_DEBUG, "open_session - start");
1308 * Lookup user and fill struct items
1310 retval = pam_get_item(idata.pamh, PAM_USER, (void*) &user_name );
1311 if ( user_name == NULL || retval != PAM_SUCCESS ) {
1312 pam_syslog(idata.pamh, LOG_ERR, "Error recovering pam user name");
1313 return PAM_SESSION_ERR;
1316 pwd = pam_modutil_getpwnam(idata.pamh, user_name);
1318 pam_syslog(idata.pamh, LOG_ERR, "user unknown '%s'", user_name);
1319 return PAM_SESSION_ERR;
1323 * Add the user info to the instance data so we can refer to them later.
1326 strncat(idata.user, user_name, sizeof(idata.user) - 1);
1327 idata.uid = pwd->pw_uid;
1330 * Parse namespace configuration file which lists directories to
1331 * polyinstantiate, directory where instance directories are to
1332 * be created and the method used for polyinstantiation.
1334 retval = parse_config_file(&idata);
1335 if (retval != PAM_SUCCESS) {
1336 del_polydir_list(idata.polydirs_ptr);
1337 return PAM_SESSION_ERR;
1340 if (idata.polydirs_ptr) {
1341 retval = setup_namespace(&idata, unmnt);
1342 if (idata.flags & PAMNS_DEBUG) {
1344 pam_syslog(idata.pamh, LOG_DEBUG,
1345 "namespace setup failed for pid %d", getpid());
1347 pam_syslog(idata.pamh, LOG_DEBUG,
1348 "namespace setup ok for pid %d", getpid());
1350 } else if (idata.flags & PAMNS_DEBUG)
1351 pam_syslog(idata.pamh, LOG_DEBUG, "Nothing to polyinstantiate");
1353 del_polydir_list(idata.polydirs_ptr);
1359 * Entry point from pam_close_session call.
1361 PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags UNUSED,
1362 int argc, const char **argv)
1365 struct instance_data idata;
1369 /* init instance data */
1371 idata.polydirs_ptr = NULL;
1374 if (is_selinux_enabled())
1375 idata.flags |= PAMNS_SELINUX_ENABLED;
1376 if (ctxt_based_inst_needed())
1377 idata.flags |= PAMNS_CTXT_BASED_INST;
1380 /* Parse arguments. */
1381 for (i = 0; i < argc; i++) {
1382 if (strcmp(argv[i], "debug") == 0)
1383 idata.flags |= PAMNS_DEBUG;
1384 if (strcmp(argv[i], "ignore_config_error") == 0)
1385 idata.flags |= PAMNS_IGN_CONFIG_ERR;
1386 if (strcmp(argv[i], "no_unmount_on_close") == 0)
1387 idata.flags |= PAMNS_NO_UNMOUNT_ON_CLOSE;
1390 if (idata.flags & PAMNS_DEBUG)
1391 pam_syslog(idata.pamh, LOG_DEBUG, "close_session - start");
1394 * For certain trusted programs such as newrole, open session
1395 * is called from a child process while the parent perfoms
1396 * close session and pam end functions. For these commands
1397 * pam_close_session should not perform the unmount of the
1398 * polyinstantiatied directory because it will result in
1399 * undoing of parents polyinstantiatiaion. These commands
1400 * will invoke pam_namespace with the "no_unmount_on_close"
1403 if (idata.flags & PAMNS_NO_UNMOUNT_ON_CLOSE) {
1404 if (idata.flags & PAMNS_DEBUG)
1405 pam_syslog(idata.pamh, LOG_DEBUG, "close_session - sucessful");
1410 * Lookup user and fill struct items
1412 retval = pam_get_item(idata.pamh, PAM_USER, (void*) &user_name );
1413 if ( user_name == NULL || retval != PAM_SUCCESS ) {
1414 pam_syslog(idata.pamh, LOG_ERR, "Error recovering pam user name");
1415 return PAM_SESSION_ERR;
1418 pwd = pam_modutil_getpwnam(idata.pamh, user_name);
1420 pam_syslog(idata.pamh, LOG_ERR, "user unknown '%s'", user_name);
1421 return PAM_SESSION_ERR;
1425 * Add the user info to the instance data so we can refer to them later.
1428 strncat(idata.user, user_name, sizeof(idata.user) - 1);
1429 idata.uid = pwd->pw_uid;
1432 * Parse namespace configuration file which lists directories that
1433 * are polyinstantiated, directories where instance directories are
1434 * created and the method used for polyinstantiation.
1436 retval = parse_config_file(&idata);
1437 if ((retval != PAM_SUCCESS) || !idata.polydirs_ptr) {
1438 del_polydir_list(idata.polydirs_ptr);
1439 return PAM_SESSION_ERR;
1442 if (idata.flags & PAMNS_DEBUG)
1443 pam_syslog(idata.pamh, LOG_DEBUG, "Resetting namespace for pid %d",
1446 retval = orig_namespace(&idata);
1447 if (idata.flags & PAMNS_DEBUG) {
1449 pam_syslog(idata.pamh, LOG_DEBUG,
1450 "resetting namespace failed for pid %d", getpid());
1452 pam_syslog(idata.pamh, LOG_DEBUG,
1453 "resetting namespace ok for pid %d", getpid());
1455 del_polydir_list(idata.polydirs_ptr);
1461 /* static module data */
1463 struct pam_module _pam_namespace_modstruct = {
1468 pam_sm_open_session,
1469 pam_sm_close_session,