]> granicus.if.org Git - linux-pam/blob - modules/pam_namespace/pam_namespace.c
Relevant BUGIDs:
[linux-pam] / modules / pam_namespace / pam_namespace.c
1 /******************************************************************************
2  * A module for Linux-PAM that will set the default namespace after
3  * establishing a session via PAM.
4  *
5  * (C) Copyright IBM Corporation 2005
6  * (C) Copyright Red Hat 2006
7  * All Rights Reserved.
8  *
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>
12  *
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:
19  *
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
22  * Software.
23  *
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.
31  */
32
33 #include "pam_namespace.h"
34
35 /*
36  * Copies the contents of ent into pent
37  */
38 static int copy_ent(const struct polydir_s *ent, struct polydir_s *pent)
39 {
40         unsigned int i;
41
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;
46         if (ent->num_uids) {
47                 uid_t *pptr, *eptr;
48
49                 pent->uid = (uid_t *) malloc(ent->num_uids * sizeof(uid_t));
50                 if (!(pent->uid)) {
51                         return -1;
52                 }
53                 for (i = 0, pptr = pent->uid, eptr = ent->uid; i < ent->num_uids;
54                                 i++, eptr++, pptr++)
55                         *pptr = *eptr;
56         } else
57                 pent->uid = NULL;
58         return 0;
59 }
60
61 /*
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.
65  */
66 static int add_polydir_entry(struct instance_data *idata,
67         const struct polydir_s *ent)
68 {
69     struct polydir_s *pent;
70     int rc = 0;
71
72     /*
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
76      * directories.
77      */
78     pent = (struct polydir_s *) malloc(sizeof(struct polydir_s));
79         if (!pent) {
80                 rc = -1;
81                 goto out;
82         }
83     /* Make copy */
84         rc = copy_ent(ent,pent);
85         if(rc < 0)
86                 goto out_clean;
87
88     /* Now attach to linked list */
89     pent->next = NULL;
90     if (idata->polydirs_ptr == NULL)
91         idata->polydirs_ptr = pent;
92     else {
93         struct polydir_s *tail;
94
95         tail = idata->polydirs_ptr;
96         while (tail->next)
97             tail = tail->next;
98         tail->next = pent;
99     }
100     goto out;
101 out_clean:
102         free(pent);
103 out:
104         return rc;
105 }
106
107
108 /*
109  * Deletes all the entries in the linked list.
110  */
111 static void del_polydir_list(struct polydir_s *polydirs_ptr)
112 {
113         struct polydir_s *dptr = polydirs_ptr;
114
115         while (dptr) {
116                 struct polydir_s *tptr = dptr;
117                 dptr = dptr->next;
118                 free(tptr->uid);
119                 free(tptr);
120         }
121 }
122
123
124 /*
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.
131  */
132 static int process_line(char *line, const char *home,
133                         struct instance_data *idata)
134 {
135     const char *dir, *instance_prefix;
136     const char *method, *uids;
137     char *tptr;
138     struct polydir_s poly;
139     int retval = 0;
140
141     poly.uid = NULL;
142     poly.num_uids = 0;
143
144     /*
145      * skip the leading white space
146      */
147     while (*line && isspace(*line))
148         line++;
149
150     /*
151      * Rip off the comments
152      */
153     tptr = strchr(line,'#');
154     if (tptr)
155         *tptr = '\0';
156
157     /*
158      * Rip off the newline char
159      */
160     tptr = strchr(line,'\n');
161     if (tptr)
162         *tptr = '\0';
163
164     /*
165      * Anything left ?
166      */
167     if (line[0] == 0)
168         return 0;
169
170     /*
171      * Initialize and scan the five strings from the line from the
172      * namespace configuration file.
173      */
174     dir = strtok_r(line, " \t", &tptr);
175     if (dir == NULL) {
176         pam_syslog(idata->pamh, LOG_NOTICE, "Invalid line missing polydir");
177         goto skipping;
178     }
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");
182         goto skipping;
183     }
184     method = strtok_r(NULL, " \t", &tptr);
185     if (method == NULL) {
186         pam_syslog(idata->pamh, LOG_NOTICE, "Invalid line missing method");
187         goto skipping;
188     }
189
190     /*
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
194      * skip it.
195      */
196     uids = strtok_r(NULL, " \t", &tptr);
197
198     /*
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.
203      */
204     if (strcmp(dir, "$HOME") == 0) {
205         dir = home;
206     }
207
208     /*
209      * Expand $HOME and $USER in instance dir prefix
210      */
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);
214         *tptr = 0;
215         sprintf(expanded, "%s%s%s", instance_prefix, idata->user, tptr+5);
216         instance_prefix = expanded;
217     }
218     if ((tptr = strstr(instance_prefix, "$HOME")) != 0) {
219         char *expanded = alloca(strlen(home)+strlen(instance_prefix)-5+1);
220         *tptr = 0;
221         sprintf(expanded, "%s%s%s", instance_prefix, home, tptr+5);
222         instance_prefix = expanded;
223     }
224
225     /*
226      * Ensure that all pathnames are absolute path names.
227      */
228     if ((dir[0] != '/') || (instance_prefix[0] != '/')) {
229         pam_syslog(idata->pamh, LOG_NOTICE,"Pathnames must start with '/'");
230         goto skipping;
231     }
232     if (strstr(dir, "..") || strstr(instance_prefix, "..")) {
233         pam_syslog(idata->pamh, LOG_NOTICE,"Pathnames must not contain '..'");
234         goto skipping;
235     }
236
237     /*
238      * Populate polyinstantiated directory structure with appropriate
239      * pathnames and the method with which to polyinstantiate.
240      */
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");
244     }
245     strcpy(poly.dir, dir);
246     strcpy(poly.instance_prefix, instance_prefix);
247
248     poly.method = NONE;
249     if (strcmp(method, "user") == 0) 
250             poly.method = USER;
251
252 #ifdef WITH_SELINUX
253     if (strcmp(method, "level") == 0) {
254         if (idata->flags & PAMNS_CTXT_BASED_INST)
255             poly.method = LEVEL;
256         else
257             poly.method = USER;
258     }
259
260     if (strcmp(method, "context") == 0) {
261         if (idata->flags & PAMNS_CTXT_BASED_INST)
262             poly.method = CONTEXT;
263         else
264             poly.method = USER;
265     }
266
267 #endif
268
269     if ( poly.method == NONE) {
270         pam_syslog(idata->pamh, LOG_NOTICE, "Illegal method");
271         goto skipping;
272     }
273
274     /*
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.
279      */
280     if (uids) {
281         uid_t *uidptr;
282         const char *ustr, *sstr;
283         int count, i;
284
285         for (count = 0, ustr = sstr = uids; sstr; ustr = sstr + 1, count++)
286            sstr = strchr(ustr, ',');
287
288         poly.num_uids = count;
289         poly.uid = (uid_t *) malloc(count * sizeof (uid_t));
290         uidptr = poly.uid;
291         if (uidptr == NULL) {
292             pam_syslog(idata->pamh, LOG_NOTICE, "out of memory");
293             goto skipping;
294         }
295
296         ustr = uids;
297         for (i = 0; i < count; i++) {
298             struct passwd *pwd;
299
300             tptr = strchr(ustr, ',');
301             if (tptr)
302                 *tptr = '\0';
303
304             pwd = pam_modutil_getpwnam(idata->pamh, ustr);
305             if (pwd == NULL) {
306                 pam_syslog(idata->pamh, LOG_ERR, "Unknown user %s in configuration", ustr);
307                 poly.num_uids--;        
308             } else {
309                 *uidptr = pwd->pw_uid;
310                 uidptr++;
311             }
312             ustr = tptr + 1;
313         }
314     }
315
316     /*
317      * Add polyinstantiated directory structure to the linked list
318      * of all polyinstantiated directory structures.
319      */
320     if (add_polydir_entry(idata, &poly) < 0) {
321         pam_syslog(idata->pamh, LOG_ERR, "Allocation Error");
322         retval = PAM_SERVICE_ERR;
323     }
324     free(poly.uid);
325
326     goto out;
327
328 skipping:
329     if (idata->flags & PAMNS_IGN_CONFIG_ERR)
330         retval = 0;
331     else
332         retval = PAM_SERVICE_ERR;
333 out:
334     return retval;
335 }
336
337
338 /*
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
342  * one directory.
343  */
344 static int parse_config_file(struct instance_data *idata)
345 {
346     FILE *fil;
347     char *home;
348     struct passwd *cpwd;
349     char *line = NULL;
350     int retval;
351     size_t len = 0;
352
353     if (idata->flags & PAMNS_DEBUG)
354         pam_syslog(idata->pamh, LOG_DEBUG, "Parsing config file %s",
355                 PAM_NAMESPACE_CONFIG);
356
357     /*
358      * Extract the user's home directory to resolve $HOME entries
359      * in the namespace configuration file.
360      */
361     cpwd = pam_modutil_getpwnam(idata->pamh, idata->user);
362     if (!cpwd) {
363         pam_syslog(idata->pamh, LOG_ERR,
364                "Error getting home dir for '%s'", idata->user);
365         return PAM_SESSION_ERR;
366     }
367     home = strdupa(cpwd->pw_dir);
368
369     /*
370      * Open configuration file, read one line at a time and call
371      * process_line to process each line.
372      */
373     fil = fopen(PAM_NAMESPACE_CONFIG, "r");
374     if (fil == NULL) {
375         pam_syslog(idata->pamh, LOG_ERR, "Error opening config file");
376         return PAM_SERVICE_ERR;
377     }
378
379     /* Use unlocked IO */
380     __fsetlocking(fil, FSETLOCKING_BYCALLER);
381
382     /* loop reading the file */
383     while (getline(&line, &len, fil) > 0) {
384         retval = process_line(line, home, idata);
385         if (retval) {
386             pam_syslog(idata->pamh, LOG_ERR,
387                 "Error processing conf file line %s", line);
388             fclose(fil);
389             free(line);
390             return PAM_SERVICE_ERR;
391         }
392     }
393     fclose(fil);
394     free(line);
395
396     /* All done...just some debug stuff */
397     if (idata->flags & PAMNS_DEBUG) {
398         struct polydir_s *dptr = idata->polydirs_ptr;
399         uid_t *iptr;
400         uid_t i;
401
402         pam_syslog(idata->pamh, LOG_DEBUG,
403             dptr?"Configured poly dirs:":"No configured poly dirs");
404         while (dptr) {
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);
409             dptr = dptr->next;
410         }
411     }
412
413     return PAM_SUCCESS;
414 }
415
416
417 /*
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.
422  */
423 static int ns_override(struct polydir_s *polyptr, struct instance_data *idata,
424                 uid_t uid)
425 {
426     unsigned int i;
427
428     if (idata->flags & PAMNS_DEBUG)
429         pam_syslog(idata->pamh, LOG_DEBUG,
430                 "Checking for ns override in dir %s for uid %d",
431                 polyptr->dir, uid);
432
433     for (i = 0; i < polyptr->num_uids; i++) {
434         if (uid == polyptr->uid[i]) {
435             return 1;
436         }
437     }
438
439     return 0;
440 }
441
442 /*
443  * md5hash generates a hash of the passed in instance directory name.
444  */
445 static char *md5hash(const char *instname, struct instance_data *idata)
446 {
447     int i;
448     char *md5inst = NULL;
449     char *to;
450     unsigned char inst_digest[MD5_DIGEST_LENGTH];
451
452     /*
453      * Create MD5 hashes for instance pathname.
454      */
455
456     MD5((const unsigned char *)instname, strlen(instname), inst_digest);
457
458     if ((md5inst = malloc(MD5_DIGEST_LENGTH * 2 + 1)) == NULL) {
459         pam_syslog(idata->pamh, LOG_ERR, "Unable to allocate buffer");
460         return NULL;
461     }
462
463     to = md5inst;
464     for (i = 0; i < MD5_DIGEST_LENGTH; i++) {
465         snprintf(to, 3, "%02x", (unsigned int)inst_digest[i]);
466         to += 2;
467     }
468
469     return md5inst;
470 }
471
472 #ifdef WITH_SELINUX
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)
476 {
477         int rc = PAM_SUCCESS;
478         security_context_t scon = NULL;
479         security_class_t tclass;
480
481         /*
482          * Get the security context of the directory to polyinstantiate.
483          */
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;
489         }
490
491         if (polyptr->method == USER) return PAM_SUCCESS;
492
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;
498         }
499
500         /*
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.
505          */
506
507         if (polyptr->method == CONTEXT) {
508                 tclass = string_to_security_class("dir");
509
510                 if (security_compute_member(scon, *origcon, tclass,
511                                         i_context) < 0) {
512                         pam_syslog(idata->pamh, LOG_ERR,
513                                         "Error computing poly dir member context");
514                         freecon(scon);
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);
519                 freecon(scon);
520                 return PAM_SUCCESS;
521         }
522
523         /*
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.
527          */
528
529         if (polyptr->method == LEVEL) {
530                 context_t scontext = NULL;
531                 context_t fcontext = NULL;
532                 rc = PAM_SESSION_ERR;
533
534                 scontext = context_new(scon);
535                 if (! scontext) {
536                         pam_syslog(idata->pamh, LOG_ERR, "out of memory");
537                         goto fail;
538                 }
539                 fcontext = context_new(*origcon);
540                 if (! fcontext) {
541                         pam_syslog(idata->pamh, LOG_ERR, "out of memory");
542                         goto fail;
543                 }
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");
546                         goto fail;
547                 }
548                 *i_context=strdup(context_str(fcontext));
549                 if (! *i_context) {
550                         pam_syslog(idata->pamh, LOG_ERR, "out of memory");
551                         goto fail;
552                 }
553
554                 rc = PAM_SUCCESS;
555  fail:
556                 context_free(scontext);
557                 context_free(fcontext);
558                 freecon(scon);
559                 return rc;
560         }
561         /* Should never get here */
562         return PAM_SUCCESS;
563 }
564 #endif
565
566 /*
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
571  * directory.
572  */
573 #ifdef WITH_SELINUX
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)
577 #else
578 static int poly_name(const struct polydir_s *polyptr, char **i_name,
579         struct instance_data *idata)
580 #endif
581 {
582     int rc;
583     char *hash = NULL;
584 #ifdef WITH_SELINUX
585     security_context_t rawcon = NULL;
586 #endif
587
588     *i_name = NULL;
589 #ifdef WITH_SELINUX
590     *i_context = NULL;
591     *origcon = NULL;
592     if ((rc=form_context(polyptr, i_context, origcon, idata)) != PAM_SUCCESS) {
593         return rc;
594     }
595 #endif
596
597     rc = PAM_SESSION_ERR;
598     /*
599      * Set the name of the polyinstantiated instance dir based on the
600      * polyinstantiation method.
601      */
602     switch (polyptr->method) {
603         case USER:
604             if (asprintf(i_name, "%s", idata->user) < 0) {
605                 *i_name = NULL;
606                 goto fail;
607             }       
608             break;
609
610 #ifdef WITH_SELINUX
611         case LEVEL:
612         case CONTEXT:
613             if (selinux_trans_to_raw_context(*i_context, &rawcon) < 0) {
614                 pam_syslog(idata->pamh, LOG_ERR, "Error translating directory context");
615                 goto fail;
616             }                
617             if (asprintf(i_name, "%s_%s", rawcon, idata->user) < 0) {
618                 *i_name = NULL;
619                 goto fail;
620             }
621             break;
622
623 #endif /* WITH_SELINUX */
624
625         default:
626             if (idata->flags & PAMNS_DEBUG)
627                 pam_syslog(idata->pamh, LOG_ERR, "Unknown method");
628             goto fail;
629     }
630
631     if (idata->flags & PAMNS_DEBUG)
632         pam_syslog(idata->pamh, LOG_DEBUG, "poly_name %s", *i_name);
633
634     if ((idata->flags & PAMNS_GEN_HASH) || strlen(*i_name) > NAMESPACE_MAX_DIR_LEN) {
635         hash = md5hash(*i_name, idata);
636         if (hash == NULL) {
637             goto fail;
638         }
639         if (idata->flags & PAMNS_GEN_HASH) {
640             free(*i_name);
641             *i_name = hash;
642             hash = NULL;
643         } else {
644             char *newname;
645             if (asprintf(&newname, "%.*s_%s", NAMESPACE_MAX_DIR_LEN-1-strlen(hash),
646                 *i_name, hash) < 0) {
647                 goto fail;
648             }
649             free(*i_name);
650             *i_name = newname;
651         }
652     }
653     rc = PAM_SUCCESS;
654     
655 fail:
656     free(hash);
657 #ifdef WITH_SELINUX
658     freecon(rawcon);
659 #endif
660     if (rc != PAM_SUCCESS) {
661 #ifdef WITH_SELINUX
662         freecon(*i_context);
663         *i_context = NULL;
664         freecon(*origcon);
665         *origcon = NULL;
666 #endif
667         free(*i_name);
668         *i_name = NULL;
669     }
670     return rc;
671 }
672
673 static int check_inst_parent(char *ipath, struct instance_data *idata)
674 {
675         struct stat instpbuf;
676         char *inst_parent, *trailing_slash;
677         /*
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).
682          */
683         inst_parent = (char *) malloc(strlen(ipath)+1);
684         if (!inst_parent) {
685                 pam_syslog(idata->pamh, LOG_ERR, "Error allocating pathname string");
686                 return PAM_SESSION_ERR;
687         }
688
689         strcpy(inst_parent, ipath);
690         trailing_slash = strrchr(inst_parent, '/');
691         if (trailing_slash)
692                 *trailing_slash = '\0';
693
694         if (stat(inst_parent, &instpbuf) < 0) {
695                 pam_syslog(idata->pamh, LOG_ERR, "Error stating %s, %m", inst_parent);
696                 free(inst_parent);
697                 return PAM_SESSION_ERR;
698         }
699
700         /*
701          * Make sure we are dealing with a directory
702          */
703         if (!S_ISDIR(instpbuf.st_mode)) {
704                 pam_syslog(idata->pamh, LOG_ERR, "Instance parent %s is not a dir",
705                                 inst_parent);
706                 free(inst_parent);
707                 return PAM_SESSION_ERR;
708         }
709
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",
713                                         inst_parent);
714                         free(inst_parent);
715                         return PAM_SESSION_ERR;
716                 }
717         }
718         free(inst_parent);
719         return PAM_SUCCESS;
720 }
721
722 /*
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.
727 */
728 static int inst_init(const struct polydir_s *polyptr, char *ipath,
729            struct instance_data *idata)
730 {
731         pid_t rc, pid;
732         sighandler_t osighand = NULL;
733         int status;
734
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;
739                 goto out;
740         }
741
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;
748                         goto out;
749                 } else {
750                         pid = fork();
751                         if (pid == 0) {
752 #ifdef WITH_SELINUX
753                                 if (idata->flags & PAMNS_SELINUX_ENABLED) {
754                                         if (setexeccon(NULL) < 0)
755                                                 exit(1);
756                                 }
757 #endif
758                                 if (execl(NAMESPACE_INIT_SCRIPT, NAMESPACE_INIT_SCRIPT,
759                                                         polyptr->dir, ipath, (char *)NULL) < 0)
760                                         exit(1);
761                         } else if (pid > 0) {
762                                 while (((rc = waitpid(pid, &status, 0)) == (pid_t)-1) &&
763                                                 (errno == EINTR));
764                                 if (rc == (pid_t)-1) {
765                                         pam_syslog(idata->pamh, LOG_ERR, "waitpid failed- %m");
766                                         rc = PAM_SESSION_ERR;
767                                         goto out;
768                                 }
769                                 if (!WIFEXITED(status) || WIFSIGNALED(status) > 0) {
770                                         pam_syslog(idata->pamh, LOG_ERR,
771                                                         "Error initializing instance");
772                                         rc = PAM_SESSION_ERR;
773                                         goto out;
774                                 }
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;
779                                 goto out;
780                         }
781                 }
782         }
783         rc = PAM_SUCCESS;
784 out:
785    (void) signal(SIGCHLD, osighand);
786
787    return rc;
788 }
789
790 /*
791  * Create polyinstantiated instance directory (ipath).
792  */
793 #ifdef WITH_SELINUX
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)
797 #else
798 static int create_dirs(const struct polydir_s *polyptr, char *ipath,
799         struct instance_data *idata)
800 #endif
801 {
802         struct stat statbuf, newstatbuf;
803         int rc, fd;
804
805     /*
806      * stat the directory to polyinstantiate, so its owner-group-mode
807      * can be propagated to instance directory
808      */
809         rc = PAM_SUCCESS;
810     if (stat(polyptr->dir, &statbuf) < 0) {
811         pam_syslog(idata->pamh, LOG_ERR, "Error stating %s, %m",
812                 polyptr->dir);
813         return PAM_SESSION_ERR;
814     }
815
816     /*
817      * Make sure we are dealing with a directory
818      */
819     if (!S_ISDIR(statbuf.st_mode)) {
820         pam_syslog(idata->pamh, LOG_ERR, "poly dir %s is not a dir",
821                 polyptr->dir);
822         return PAM_SESSION_ERR;
823     }
824
825         /*
826          * Check to make sure instance parent is valid.
827          */
828         if (check_inst_parent(ipath, idata))
829                 return PAM_SESSION_ERR;
830
831     /*
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
835      * polyinstantiated.
836      */
837     if (mkdir(ipath, S_IRUSR) < 0) {
838         if (errno == EEXIST)
839             goto inst_init;
840         else {
841             pam_syslog(idata->pamh, LOG_ERR, "Error creating %s, %m",
842                         ipath);
843             return PAM_SESSION_ERR;
844         }
845     }
846
847     /* Open a descriptor to it to prevent races */
848     fd = open(ipath, O_DIRECTORY | O_RDONLY);
849     if (fd < 0) {
850         pam_syslog(idata->pamh, LOG_ERR, "Error opening %s, %m", ipath);
851         rmdir(ipath);
852         return PAM_SESSION_ERR;
853     }
854 #ifdef WITH_SELINUX
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 */
858         if (icontext) {
859             if (fsetfilecon(fd, icontext) < 0) {
860                 pam_syslog(idata->pamh, LOG_ERR,
861                         "Error setting context of %s to %s", ipath, icontext);
862                 close(fd);
863                 rmdir(ipath);
864                 return PAM_SESSION_ERR;
865             }
866         } else {
867             if (fsetfilecon(fd, ocontext) < 0) {
868                 pam_syslog(idata->pamh, LOG_ERR,
869                         "Error setting context of %s to %s", ipath, ocontext);
870                 close(fd);
871                 rmdir(ipath);
872                 return PAM_SESSION_ERR;
873             }
874         }
875     }
876 #endif
877     if (fstat(fd, &newstatbuf) < 0) {
878         pam_syslog(idata->pamh, LOG_ERR, "Error stating %s, %m",
879                 ipath);
880         rmdir(ipath);
881         return PAM_SESSION_ERR;
882     }
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",
888                         ipath);
889             close(fd);
890             rmdir(ipath);
891             return PAM_SESSION_ERR;
892         }
893     }
894     if (fchmod(fd, statbuf.st_mode & 07777) < 0) {
895         pam_syslog(idata->pamh, LOG_ERR, "Error changing mode for %s, %m",
896                         ipath);
897         close(fd);
898         rmdir(ipath);
899         return PAM_SESSION_ERR;
900     }
901     close(fd);
902
903     /*
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.
908      */
909
910 inst_init:
911         rc = inst_init(polyptr, ipath, idata);
912     return rc;
913 }
914
915
916 /*
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
921  * namespace.
922  */
923 static int ns_setup(const struct polydir_s *polyptr,
924         struct instance_data *idata)
925 {
926     int retval = 0;
927     char *inst_dir = NULL;
928     char *instname = NULL;
929     char *dir;
930 #ifdef WITH_SELINUX
931     security_context_t instcontext = NULL, origcontext = NULL;
932 #endif
933
934     if (idata->flags & PAMNS_DEBUG)
935         pam_syslog(idata->pamh, LOG_DEBUG,
936                "Set namespace for directory %s", polyptr->dir);
937
938     dir = strrchr(polyptr->dir, '/');
939     if (dir && strlen(dir) > 1)
940         dir++;
941
942     /*
943      * Obtain the name of instance pathname based on the
944      * polyinstantiation method and instance context returned by
945      * security policy.
946      */
947 #ifdef WITH_SELINUX
948     retval = poly_name(polyptr, &instname, &instcontext,
949                         &origcontext, idata);
950 #else
951     retval = poly_name(polyptr, &instname, idata);
952 #endif
953
954     if (retval) {
955         pam_syslog(idata->pamh, LOG_ERR, "Error getting instance name");
956         goto error_out;
957     } else {
958 #ifdef WITH_SELINUX
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);
963 #endif
964     }
965
966     if (asprintf(&inst_dir, "%s%s", polyptr->instance_prefix, instname) < 0)
967         goto error_out;
968
969     if (idata->flags & PAMNS_DEBUG)
970         pam_syslog(idata->pamh, LOG_DEBUG, "instance_dir %s",
971                 inst_dir);
972
973     /*
974      * Create instance directory with appropriate security
975      * contexts, owner, group and mode bits.
976      */
977 #ifdef WITH_SELINUX
978     retval = create_dirs(polyptr, inst_dir, instcontext,
979                          origcontext, idata);
980 #else
981     retval = create_dirs(polyptr, inst_dir, idata);
982 #endif
983
984     if (retval < 0) {
985         pam_syslog(idata->pamh, LOG_ERR, "Error creating instance dir");
986         goto error_out;
987     }
988
989     /*
990      * Bind mount instance directory on top of the polyinstantiated
991      * directory to provide an instance of polyinstantiated directory
992      * based on polyinstantiated method.
993      */
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);
997         goto error_out;
998     }
999
1000     goto cleanup;
1001
1002     /*
1003      * various error exit points. Free allocated memory and set return
1004      * value to indicate a pam session error.
1005      */
1006 error_out:
1007     retval = PAM_SESSION_ERR;
1008
1009 cleanup:
1010     free(inst_dir);
1011     free(instname);
1012 #ifdef WITH_SELINUX
1013     freecon(instcontext);
1014     freecon(origcontext);
1015 #endif
1016     return retval;
1017 }
1018
1019
1020 /*
1021  * This function checks to see if the current working directory is
1022  * inside the directory passed in as the first argument.
1023  */
1024 static int cwd_in(char *dir, struct instance_data *idata)
1025 {
1026     int retval = 0;
1027     char cwd[PATH_MAX];
1028
1029     if (getcwd(cwd, PATH_MAX) == NULL) {
1030         pam_syslog(idata->pamh, LOG_ERR, "Can't get current dir, %m");
1031         return -1;
1032     }
1033
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);
1037         retval = 1;
1038     } else {
1039         if (idata->flags & PAMNS_DEBUG)
1040             pam_syslog(idata->pamh, LOG_DEBUG, "cwd is outside %s", dir);
1041     }
1042
1043     return retval;
1044 }
1045
1046
1047 /*
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.
1052  */
1053 static int setup_namespace(struct instance_data *idata, enum unmnt_op unmnt)
1054 {
1055     int retval = 0, need_poly = 0, changing_dir = 0;
1056     char *cptr, *fptr, poly_parent[PATH_MAX];
1057     struct polydir_s *pptr;
1058     uid_t req_uid;
1059     const void *ruser_name;
1060     struct passwd *pwd;
1061
1062     if (idata->flags & PAMNS_DEBUG)
1063         pam_syslog(idata->pamh, LOG_DEBUG, "Set up namespace for pid %d",
1064                 getpid());
1065
1066     retval = pam_get_item(idata->pamh, PAM_RUSER, &ruser_name);
1067     if (ruser_name == NULL || retval != PAM_SUCCESS) {
1068         retval = PAM_SUCCESS;
1069         req_uid = getuid();
1070     } else {
1071         pwd = pam_modutil_getpwnam(idata->pamh, ruser_name);
1072         if (pwd != NULL) {
1073             req_uid = pwd->pw_uid;
1074         } else {
1075             req_uid = getuid();
1076         }
1077     }
1078
1079     /*
1080      * Cycle through all polyinstantiated directory entries to see if
1081      * polyinstantiation is needed at all.
1082      */
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);
1090             } else {
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);
1095                 need_poly = 1;
1096                 break;
1097             }
1098             continue;
1099         } else {
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);
1104             need_poly = 1;
1105             break;
1106         }
1107     }
1108
1109     /*
1110      * If polyinstnatiation is needed, call the unshare system call to
1111      * disassociate from the parent namespace.
1112      */
1113     if (need_poly) {
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;
1118         }
1119     } else
1120         return PAM_SUCCESS;
1121
1122     /*
1123      * Again cycle through all polyinstantiated directories, this time,
1124      * call ns_setup to setup polyinstantiation for a particular entry.
1125      */
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)) {
1130                 continue;
1131             } else {
1132                 dir_unmnt = UNMNT_ONLY;
1133             }
1134         }
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);
1139
1140         if ((dir_unmnt == UNMNT_REMNT) || (dir_unmnt == UNMNT_ONLY)) {
1141                 /*
1142                  * Check to see if process current directory is in the
1143                  * bind mounted instance_parent directory that we are trying to
1144                  * umount
1145                  */
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");
1151
1152                     /*
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
1157                      */
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, "/");
1163                     else if (cptr)
1164                         *cptr = '\0';
1165                     if (chdir(poly_parent) < 0) {
1166                         pam_syslog(idata->pamh, LOG_ERR,
1167                                 "Can't chdir to %s, %m", poly_parent);
1168                     }
1169                 }
1170
1171                 if (umount(pptr->dir) < 0) {
1172                     int saved_errno = errno;
1173                     pam_syslog(idata->pamh, LOG_ERR, "Unmount of %s failed, %m",
1174                         pptr->dir);
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",
1179                                 pptr->dir);
1180         }
1181
1182         if (dir_unmnt != UNMNT_ONLY) {
1183                 retval = ns_setup(pptr, idata);
1184                 if (retval != PAM_SUCCESS)
1185                      break;
1186         }
1187     }
1188
1189     return retval;
1190 }
1191
1192
1193 /*
1194  * Orig namespace. This function is called from when closing a pam
1195  * session. If authorized, it unmounts instance directory.
1196  */
1197 static int orig_namespace(struct instance_data *idata)
1198 {
1199     struct polydir_s *pptr;
1200
1201     if (idata->flags & PAMNS_DEBUG)
1202         pam_syslog(idata->pamh, LOG_DEBUG, "orig namespace for pid %d",
1203                 getpid());
1204
1205     /*
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.
1210      */
1211     for (pptr = idata->polydirs_ptr; pptr; pptr = pptr->next) {
1212         if (ns_override(pptr, idata, idata->uid))
1213             continue;
1214         else {
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);
1219
1220             if (umount(pptr->dir) < 0) {
1221                 pam_syslog(idata->pamh, LOG_ERR, "Unmount of %s failed, %m",
1222                        pptr->dir);
1223                 return PAM_SESSION_ERR;
1224             } else if (idata->flags & PAMNS_DEBUG)
1225                 pam_syslog(idata->pamh, LOG_DEBUG, "Unmount of %s succeeded",
1226                         pptr->dir);
1227         }
1228     }
1229     return 0;
1230 }
1231
1232
1233 #ifdef WITH_SELINUX
1234 /*
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".
1242  */
1243 static int ctxt_based_inst_needed(void)
1244 {
1245     security_context_t scon = NULL;
1246     int rc = 0;
1247
1248     rc = getexeccon(&scon);
1249     if (rc < 0 || scon == NULL)
1250         return 0;
1251     else {
1252         freecon(scon);
1253         return 1;
1254     }
1255 }
1256 #endif
1257
1258
1259 /*
1260  * Entry point from pam_open_session call.
1261  */
1262 PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED,
1263                                    int argc, const char **argv)
1264 {
1265     int i, retval;
1266     struct instance_data idata;
1267     char *user_name;
1268     struct passwd *pwd;
1269     enum unmnt_op unmnt = NO_UNMNT;
1270
1271     /* init instance data */
1272     idata.flags = 0;
1273     idata.polydirs_ptr = NULL;
1274     idata.pamh = pamh;
1275 #ifdef WITH_SELINUX
1276     if (is_selinux_enabled())
1277         idata.flags |= PAMNS_SELINUX_ENABLED;
1278     if (ctxt_based_inst_needed()) 
1279         idata.flags |= PAMNS_CTXT_BASED_INST;
1280 #endif
1281
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)
1295             unmnt = UNMNT_ONLY;
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;
1301                 }
1302         }
1303     }
1304     if (idata.flags & PAMNS_DEBUG)
1305         pam_syslog(idata.pamh, LOG_DEBUG, "open_session - start");
1306
1307     /*
1308      * Lookup user and fill struct items
1309      */
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;
1314     }
1315
1316     pwd = pam_modutil_getpwnam(idata.pamh, user_name);
1317     if (!pwd) {
1318         pam_syslog(idata.pamh, LOG_ERR, "user unknown '%s'", user_name);
1319         return PAM_SESSION_ERR;
1320     }
1321
1322     /*
1323      * Add the user info to the instance data so we can refer to them later.
1324      */
1325     idata.user[0] = 0;
1326     strncat(idata.user, user_name, sizeof(idata.user) - 1);
1327     idata.uid = pwd->pw_uid;
1328
1329     /*
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.
1333      */
1334     retval = parse_config_file(&idata);
1335     if (retval != PAM_SUCCESS) {
1336         del_polydir_list(idata.polydirs_ptr);
1337         return PAM_SESSION_ERR;
1338     }
1339
1340     if (idata.polydirs_ptr) {
1341         retval = setup_namespace(&idata, unmnt);
1342         if (idata.flags & PAMNS_DEBUG) {
1343             if (retval)
1344                 pam_syslog(idata.pamh, LOG_DEBUG,
1345                         "namespace setup failed for pid %d", getpid());
1346             else
1347                 pam_syslog(idata.pamh, LOG_DEBUG,
1348                         "namespace setup ok for pid %d", getpid());
1349         }
1350     } else if (idata.flags & PAMNS_DEBUG)
1351         pam_syslog(idata.pamh, LOG_DEBUG, "Nothing to polyinstantiate");
1352
1353     del_polydir_list(idata.polydirs_ptr);
1354     return retval;
1355 }
1356
1357
1358 /*
1359  * Entry point from pam_close_session call.
1360  */
1361 PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags UNUSED,
1362                                     int argc, const char **argv)
1363 {
1364     int i, retval;
1365     struct instance_data idata;
1366     char *user_name;
1367     struct passwd *pwd;
1368
1369     /* init instance data */
1370     idata.flags = 0;
1371     idata.polydirs_ptr = NULL;
1372     idata.pamh = pamh;
1373 #ifdef WITH_SELINUX
1374     if (is_selinux_enabled())
1375         idata.flags |= PAMNS_SELINUX_ENABLED;
1376     if (ctxt_based_inst_needed())
1377         idata.flags |= PAMNS_CTXT_BASED_INST;
1378 #endif
1379
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;
1388     }
1389
1390     if (idata.flags & PAMNS_DEBUG)
1391         pam_syslog(idata.pamh, LOG_DEBUG, "close_session - start");
1392
1393     /*
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"
1401      * argument.
1402      */
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");
1406         return PAM_SUCCESS;
1407     }
1408
1409     /* 
1410      * Lookup user and fill struct items
1411      */
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;
1416     }
1417
1418     pwd = pam_modutil_getpwnam(idata.pamh, user_name);
1419     if (!pwd) {
1420         pam_syslog(idata.pamh, LOG_ERR, "user unknown '%s'", user_name);
1421         return PAM_SESSION_ERR;
1422     }
1423
1424     /*
1425      * Add the user info to the instance data so we can refer to them later.
1426      */
1427     idata.user[0] = 0;
1428     strncat(idata.user, user_name, sizeof(idata.user) - 1);
1429     idata.uid = pwd->pw_uid;
1430
1431     /*
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.
1435      */
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;
1440     }
1441
1442     if (idata.flags & PAMNS_DEBUG)
1443         pam_syslog(idata.pamh, LOG_DEBUG, "Resetting namespace for pid %d",
1444                 getpid());
1445
1446     retval = orig_namespace(&idata);
1447     if (idata.flags & PAMNS_DEBUG) {
1448         if (retval)
1449             pam_syslog(idata.pamh, LOG_DEBUG,
1450                 "resetting namespace failed for pid %d", getpid());
1451         else
1452             pam_syslog(idata.pamh, LOG_DEBUG,
1453                 "resetting namespace ok for pid %d", getpid());
1454     }
1455     del_polydir_list(idata.polydirs_ptr);
1456     return PAM_SUCCESS;
1457 }
1458
1459 #ifdef PAM_STATIC
1460
1461 /* static module data */
1462
1463 struct pam_module _pam_namespace_modstruct = {
1464      "pam_namespace",
1465      NULL,
1466      NULL,
1467      NULL,
1468      pam_sm_open_session,
1469      pam_sm_close_session,
1470      NULL
1471 };
1472 #endif