]> granicus.if.org Git - linux-pam/blob - modules/pam_namespace/pam_namespace.c
Relevant BUGIDs: rhbz #306901, rhbz #295151
[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 ((idata->flags & PAMNS_SELINUX_ENABLED) &&
593         (rc=form_context(polyptr, i_context, origcon, idata)) != PAM_SUCCESS) {
594             return rc;
595     }
596 #endif
597
598     rc = PAM_SESSION_ERR;
599     /*
600      * Set the name of the polyinstantiated instance dir based on the
601      * polyinstantiation method.
602      */
603     switch (polyptr->method) {
604         case USER:
605             if (asprintf(i_name, "%s", idata->user) < 0) {
606                 *i_name = NULL;
607                 goto fail;
608             }       
609             break;
610
611 #ifdef WITH_SELINUX
612         case LEVEL:
613         case CONTEXT:
614             if (selinux_trans_to_raw_context(*i_context, &rawcon) < 0) {
615                 pam_syslog(idata->pamh, LOG_ERR, "Error translating directory context");
616                 goto fail;
617             }                
618             if (asprintf(i_name, "%s_%s", rawcon, idata->user) < 0) {
619                 *i_name = NULL;
620                 goto fail;
621             }
622             break;
623
624 #endif /* WITH_SELINUX */
625
626         default:
627             if (idata->flags & PAMNS_DEBUG)
628                 pam_syslog(idata->pamh, LOG_ERR, "Unknown method");
629             goto fail;
630     }
631
632     if (idata->flags & PAMNS_DEBUG)
633         pam_syslog(idata->pamh, LOG_DEBUG, "poly_name %s", *i_name);
634
635     if ((idata->flags & PAMNS_GEN_HASH) || strlen(*i_name) > NAMESPACE_MAX_DIR_LEN) {
636         hash = md5hash(*i_name, idata);
637         if (hash == NULL) {
638             goto fail;
639         }
640         if (idata->flags & PAMNS_GEN_HASH) {
641             free(*i_name);
642             *i_name = hash;
643             hash = NULL;
644         } else {
645             char *newname;
646             if (asprintf(&newname, "%.*s_%s", NAMESPACE_MAX_DIR_LEN-1-strlen(hash),
647                 *i_name, hash) < 0) {
648                 goto fail;
649             }
650             free(*i_name);
651             *i_name = newname;
652         }
653     }
654     rc = PAM_SUCCESS;
655     
656 fail:
657     free(hash);
658 #ifdef WITH_SELINUX
659     freecon(rawcon);
660 #endif
661     if (rc != PAM_SUCCESS) {
662 #ifdef WITH_SELINUX
663         freecon(*i_context);
664         *i_context = NULL;
665         freecon(*origcon);
666         *origcon = NULL;
667 #endif
668         free(*i_name);
669         *i_name = NULL;
670     }
671     return rc;
672 }
673
674 static int check_inst_parent(char *ipath, struct instance_data *idata)
675 {
676         struct stat instpbuf;
677         char *inst_parent, *trailing_slash;
678         /*
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).
683          */
684         inst_parent = (char *) malloc(strlen(ipath)+1);
685         if (!inst_parent) {
686                 pam_syslog(idata->pamh, LOG_ERR, "Error allocating pathname string");
687                 return PAM_SESSION_ERR;
688         }
689
690         strcpy(inst_parent, ipath);
691         trailing_slash = strrchr(inst_parent, '/');
692         if (trailing_slash)
693                 *trailing_slash = '\0';
694
695         if (stat(inst_parent, &instpbuf) < 0) {
696                 pam_syslog(idata->pamh, LOG_ERR, "Error stating %s, %m", inst_parent);
697                 free(inst_parent);
698                 return PAM_SESSION_ERR;
699         }
700
701         /*
702          * Make sure we are dealing with a directory
703          */
704         if (!S_ISDIR(instpbuf.st_mode)) {
705                 pam_syslog(idata->pamh, LOG_ERR, "Instance parent %s is not a dir",
706                                 inst_parent);
707                 free(inst_parent);
708                 return PAM_SESSION_ERR;
709         }
710
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",
714                                         inst_parent);
715                         free(inst_parent);
716                         return PAM_SESSION_ERR;
717                 }
718         }
719         free(inst_parent);
720         return PAM_SUCCESS;
721 }
722
723 /*
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.
728 */
729 static int inst_init(const struct polydir_s *polyptr, char *ipath,
730            struct instance_data *idata)
731 {
732         pid_t rc, pid;
733         sighandler_t osighand = NULL;
734         int status;
735
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;
740                 goto out;
741         }
742
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;
749                         goto out;
750                 } else {
751                         pid = fork();
752                         if (pid == 0) {
753 #ifdef WITH_SELINUX
754                                 if (idata->flags & PAMNS_SELINUX_ENABLED) {
755                                         if (setexeccon(NULL) < 0)
756                                                 exit(1);
757                                 }
758 #endif
759                                 if (execl(NAMESPACE_INIT_SCRIPT, NAMESPACE_INIT_SCRIPT,
760                                                         polyptr->dir, ipath, (char *)NULL) < 0)
761                                         exit(1);
762                         } else if (pid > 0) {
763                                 while (((rc = waitpid(pid, &status, 0)) == (pid_t)-1) &&
764                                                 (errno == EINTR));
765                                 if (rc == (pid_t)-1) {
766                                         pam_syslog(idata->pamh, LOG_ERR, "waitpid failed- %m");
767                                         rc = PAM_SESSION_ERR;
768                                         goto out;
769                                 }
770                                 if (!WIFEXITED(status) || WIFSIGNALED(status) > 0) {
771                                         pam_syslog(idata->pamh, LOG_ERR,
772                                                         "Error initializing instance");
773                                         rc = PAM_SESSION_ERR;
774                                         goto out;
775                                 }
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;
780                                 goto out;
781                         }
782                 }
783         }
784         rc = PAM_SUCCESS;
785 out:
786    (void) signal(SIGCHLD, osighand);
787
788    return rc;
789 }
790
791 /*
792  * Create polyinstantiated instance directory (ipath).
793  */
794 #ifdef WITH_SELINUX
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)
798 #else
799 static int create_dirs(const struct polydir_s *polyptr, char *ipath,
800         struct instance_data *idata)
801 #endif
802 {
803         struct stat statbuf, newstatbuf;
804         int rc, fd;
805
806     /*
807      * stat the directory to polyinstantiate, so its owner-group-mode
808      * can be propagated to instance directory
809      */
810         rc = PAM_SUCCESS;
811     if (stat(polyptr->dir, &statbuf) < 0) {
812         pam_syslog(idata->pamh, LOG_ERR, "Error stating %s, %m",
813                 polyptr->dir);
814         return PAM_SESSION_ERR;
815     }
816
817     /*
818      * Make sure we are dealing with a directory
819      */
820     if (!S_ISDIR(statbuf.st_mode)) {
821         pam_syslog(idata->pamh, LOG_ERR, "poly dir %s is not a dir",
822                 polyptr->dir);
823         return PAM_SESSION_ERR;
824     }
825
826         /*
827          * Check to make sure instance parent is valid.
828          */
829         if (check_inst_parent(ipath, idata))
830                 return PAM_SESSION_ERR;
831
832     /*
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
836      * polyinstantiated.
837      */
838     if (mkdir(ipath, S_IRUSR) < 0) {
839         if (errno == EEXIST)
840             goto inst_init;
841         else {
842             pam_syslog(idata->pamh, LOG_ERR, "Error creating %s, %m",
843                         ipath);
844             return PAM_SESSION_ERR;
845         }
846     }
847
848     /* Open a descriptor to it to prevent races */
849     fd = open(ipath, O_DIRECTORY | O_RDONLY);
850     if (fd < 0) {
851         pam_syslog(idata->pamh, LOG_ERR, "Error opening %s, %m", ipath);
852         rmdir(ipath);
853         return PAM_SESSION_ERR;
854     }
855 #ifdef WITH_SELINUX
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 */
859         if (icontext) {
860             if (fsetfilecon(fd, icontext) < 0) {
861                 pam_syslog(idata->pamh, LOG_ERR,
862                         "Error setting context of %s to %s", ipath, icontext);
863                 close(fd);
864                 rmdir(ipath);
865                 return PAM_SESSION_ERR;
866             }
867         } else {
868             if (fsetfilecon(fd, ocontext) < 0) {
869                 pam_syslog(idata->pamh, LOG_ERR,
870                         "Error setting context of %s to %s", ipath, ocontext);
871                 close(fd);
872                 rmdir(ipath);
873                 return PAM_SESSION_ERR;
874             }
875         }
876     }
877 #endif
878     if (fstat(fd, &newstatbuf) < 0) {
879         pam_syslog(idata->pamh, LOG_ERR, "Error stating %s, %m",
880                 ipath);
881         rmdir(ipath);
882         return PAM_SESSION_ERR;
883     }
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",
889                         ipath);
890             close(fd);
891             rmdir(ipath);
892             return PAM_SESSION_ERR;
893         }
894     }
895     if (fchmod(fd, statbuf.st_mode & 07777) < 0) {
896         pam_syslog(idata->pamh, LOG_ERR, "Error changing mode for %s, %m",
897                         ipath);
898         close(fd);
899         rmdir(ipath);
900         return PAM_SESSION_ERR;
901     }
902     close(fd);
903
904     /*
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.
909      */
910
911 inst_init:
912         rc = inst_init(polyptr, ipath, idata);
913     return rc;
914 }
915
916
917 /*
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
922  * namespace.
923  */
924 static int ns_setup(const struct polydir_s *polyptr,
925         struct instance_data *idata)
926 {
927     int retval = 0;
928     char *inst_dir = NULL;
929     char *instname = NULL;
930     char *dir;
931 #ifdef WITH_SELINUX
932     security_context_t instcontext = NULL, origcontext = NULL;
933 #endif
934
935     if (idata->flags & PAMNS_DEBUG)
936         pam_syslog(idata->pamh, LOG_DEBUG,
937                "Set namespace for directory %s", polyptr->dir);
938
939     dir = strrchr(polyptr->dir, '/');
940     if (dir && strlen(dir) > 1)
941         dir++;
942
943     /*
944      * Obtain the name of instance pathname based on the
945      * polyinstantiation method and instance context returned by
946      * security policy.
947      */
948 #ifdef WITH_SELINUX
949     retval = poly_name(polyptr, &instname, &instcontext,
950                         &origcontext, idata);
951 #else
952     retval = poly_name(polyptr, &instname, idata);
953 #endif
954
955     if (retval) {
956         pam_syslog(idata->pamh, LOG_ERR, "Error getting instance name");
957         goto error_out;
958     } else {
959 #ifdef WITH_SELINUX
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);
964 #endif
965     }
966
967     if (asprintf(&inst_dir, "%s%s", polyptr->instance_prefix, instname) < 0)
968         goto error_out;
969
970     if (idata->flags & PAMNS_DEBUG)
971         pam_syslog(idata->pamh, LOG_DEBUG, "instance_dir %s",
972                 inst_dir);
973
974     /*
975      * Create instance directory with appropriate security
976      * contexts, owner, group and mode bits.
977      */
978 #ifdef WITH_SELINUX
979     retval = create_dirs(polyptr, inst_dir, instcontext,
980                          origcontext, idata);
981 #else
982     retval = create_dirs(polyptr, inst_dir, idata);
983 #endif
984
985     if (retval < 0) {
986         pam_syslog(idata->pamh, LOG_ERR, "Error creating instance dir");
987         goto error_out;
988     }
989
990     /*
991      * Bind mount instance directory on top of the polyinstantiated
992      * directory to provide an instance of polyinstantiated directory
993      * based on polyinstantiated method.
994      */
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);
998         goto error_out;
999     }
1000
1001     goto cleanup;
1002
1003     /*
1004      * various error exit points. Free allocated memory and set return
1005      * value to indicate a pam session error.
1006      */
1007 error_out:
1008     retval = PAM_SESSION_ERR;
1009
1010 cleanup:
1011     free(inst_dir);
1012     free(instname);
1013 #ifdef WITH_SELINUX
1014     freecon(instcontext);
1015     freecon(origcontext);
1016 #endif
1017     return retval;
1018 }
1019
1020
1021 /*
1022  * This function checks to see if the current working directory is
1023  * inside the directory passed in as the first argument.
1024  */
1025 static int cwd_in(char *dir, struct instance_data *idata)
1026 {
1027     int retval = 0;
1028     char cwd[PATH_MAX];
1029
1030     if (getcwd(cwd, PATH_MAX) == NULL) {
1031         pam_syslog(idata->pamh, LOG_ERR, "Can't get current dir, %m");
1032         return -1;
1033     }
1034
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);
1038         retval = 1;
1039     } else {
1040         if (idata->flags & PAMNS_DEBUG)
1041             pam_syslog(idata->pamh, LOG_DEBUG, "cwd is outside %s", dir);
1042     }
1043
1044     return retval;
1045 }
1046
1047
1048 /*
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.
1053  */
1054 static int setup_namespace(struct instance_data *idata, enum unmnt_op unmnt)
1055 {
1056     int retval = 0, need_poly = 0, changing_dir = 0;
1057     char *cptr, *fptr, poly_parent[PATH_MAX];
1058     struct polydir_s *pptr;
1059     uid_t req_uid;
1060     const void *ruser_name;
1061     struct passwd *pwd;
1062
1063     if (idata->flags & PAMNS_DEBUG)
1064         pam_syslog(idata->pamh, LOG_DEBUG, "Set up namespace for pid %d",
1065                 getpid());
1066
1067     retval = pam_get_item(idata->pamh, PAM_RUSER, &ruser_name);
1068     if (ruser_name == NULL || retval != PAM_SUCCESS) {
1069         retval = PAM_SUCCESS;
1070         req_uid = getuid();
1071     } else {
1072         pwd = pam_modutil_getpwnam(idata->pamh, ruser_name);
1073         if (pwd != NULL) {
1074             req_uid = pwd->pw_uid;
1075         } else {
1076             req_uid = getuid();
1077         }
1078     }
1079
1080     /*
1081      * Cycle through all polyinstantiated directory entries to see if
1082      * polyinstantiation is needed at all.
1083      */
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);
1091             } else {
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);
1096                 need_poly = 1;
1097                 break;
1098             }
1099             continue;
1100         } else {
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);
1105             need_poly = 1;
1106             break;
1107         }
1108     }
1109
1110     /*
1111      * If polyinstnatiation is needed, call the unshare system call to
1112      * disassociate from the parent namespace.
1113      */
1114     if (need_poly) {
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;
1119         }
1120     } else
1121         return PAM_SUCCESS;
1122
1123     /*
1124      * Again cycle through all polyinstantiated directories, this time,
1125      * call ns_setup to setup polyinstantiation for a particular entry.
1126      */
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)) {
1131                 continue;
1132             } else {
1133                 dir_unmnt = UNMNT_ONLY;
1134             }
1135         }
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);
1140
1141         if ((dir_unmnt == UNMNT_REMNT) || (dir_unmnt == UNMNT_ONLY)) {
1142                 /*
1143                  * Check to see if process current directory is in the
1144                  * bind mounted instance_parent directory that we are trying to
1145                  * umount
1146                  */
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");
1152
1153                     /*
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
1158                      */
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, "/");
1164                     else if (cptr)
1165                         *cptr = '\0';
1166                     if (chdir(poly_parent) < 0) {
1167                         pam_syslog(idata->pamh, LOG_ERR,
1168                                 "Can't chdir to %s, %m", poly_parent);
1169                     }
1170                 }
1171
1172                 if (umount(pptr->dir) < 0) {
1173                     int saved_errno = errno;
1174                     pam_syslog(idata->pamh, LOG_ERR, "Unmount of %s failed, %m",
1175                         pptr->dir);
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",
1180                                 pptr->dir);
1181         }
1182
1183         if (dir_unmnt != UNMNT_ONLY) {
1184                 retval = ns_setup(pptr, idata);
1185                 if (retval != PAM_SUCCESS)
1186                      break;
1187         }
1188     }
1189
1190     return retval;
1191 }
1192
1193
1194 /*
1195  * Orig namespace. This function is called from when closing a pam
1196  * session. If authorized, it unmounts instance directory.
1197  */
1198 static int orig_namespace(struct instance_data *idata)
1199 {
1200     struct polydir_s *pptr;
1201
1202     if (idata->flags & PAMNS_DEBUG)
1203         pam_syslog(idata->pamh, LOG_DEBUG, "orig namespace for pid %d",
1204                 getpid());
1205
1206     /*
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.
1211      */
1212     for (pptr = idata->polydirs_ptr; pptr; pptr = pptr->next) {
1213         if (ns_override(pptr, idata, idata->uid))
1214             continue;
1215         else {
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);
1220
1221             if (umount(pptr->dir) < 0) {
1222                 pam_syslog(idata->pamh, LOG_ERR, "Unmount of %s failed, %m",
1223                        pptr->dir);
1224                 return PAM_SESSION_ERR;
1225             } else if (idata->flags & PAMNS_DEBUG)
1226                 pam_syslog(idata->pamh, LOG_DEBUG, "Unmount of %s succeeded",
1227                         pptr->dir);
1228         }
1229     }
1230     return 0;
1231 }
1232
1233
1234 #ifdef WITH_SELINUX
1235 /*
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".
1243  */
1244 static int ctxt_based_inst_needed(void)
1245 {
1246     security_context_t scon = NULL;
1247     int rc = 0;
1248
1249     rc = getexeccon(&scon);
1250     if (rc < 0 || scon == NULL)
1251         return 0;
1252     else {
1253         freecon(scon);
1254         return 1;
1255     }
1256 }
1257 #endif
1258
1259
1260 /*
1261  * Entry point from pam_open_session call.
1262  */
1263 PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED,
1264                                    int argc, const char **argv)
1265 {
1266     int i, retval;
1267     struct instance_data idata;
1268     char *user_name;
1269     struct passwd *pwd;
1270     enum unmnt_op unmnt = NO_UNMNT;
1271
1272     /* init instance data */
1273     idata.flags = 0;
1274     idata.polydirs_ptr = NULL;
1275     idata.pamh = pamh;
1276 #ifdef WITH_SELINUX
1277     if (is_selinux_enabled())
1278         idata.flags |= PAMNS_SELINUX_ENABLED;
1279     if (ctxt_based_inst_needed()) 
1280         idata.flags |= PAMNS_CTXT_BASED_INST;
1281 #endif
1282
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)
1296             unmnt = UNMNT_ONLY;
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;
1302                 }
1303         }
1304     }
1305     if (idata.flags & PAMNS_DEBUG)
1306         pam_syslog(idata.pamh, LOG_DEBUG, "open_session - start");
1307
1308     /*
1309      * Lookup user and fill struct items
1310      */
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;
1315     }
1316
1317     pwd = pam_modutil_getpwnam(idata.pamh, user_name);
1318     if (!pwd) {
1319         pam_syslog(idata.pamh, LOG_ERR, "user unknown '%s'", user_name);
1320         return PAM_SESSION_ERR;
1321     }
1322
1323     /*
1324      * Add the user info to the instance data so we can refer to them later.
1325      */
1326     idata.user[0] = 0;
1327     strncat(idata.user, user_name, sizeof(idata.user) - 1);
1328     idata.uid = pwd->pw_uid;
1329
1330     /*
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.
1334      */
1335     retval = parse_config_file(&idata);
1336     if (retval != PAM_SUCCESS) {
1337         del_polydir_list(idata.polydirs_ptr);
1338         return PAM_SESSION_ERR;
1339     }
1340
1341     if (idata.polydirs_ptr) {
1342         retval = setup_namespace(&idata, unmnt);
1343         if (idata.flags & PAMNS_DEBUG) {
1344             if (retval)
1345                 pam_syslog(idata.pamh, LOG_DEBUG,
1346                         "namespace setup failed for pid %d", getpid());
1347             else
1348                 pam_syslog(idata.pamh, LOG_DEBUG,
1349                         "namespace setup ok for pid %d", getpid());
1350         }
1351     } else if (idata.flags & PAMNS_DEBUG)
1352         pam_syslog(idata.pamh, LOG_DEBUG, "Nothing to polyinstantiate");
1353
1354     del_polydir_list(idata.polydirs_ptr);
1355     return retval;
1356 }
1357
1358
1359 /*
1360  * Entry point from pam_close_session call.
1361  */
1362 PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags UNUSED,
1363                                     int argc, const char **argv)
1364 {
1365     int i, retval;
1366     struct instance_data idata;
1367     char *user_name;
1368     struct passwd *pwd;
1369
1370     /* init instance data */
1371     idata.flags = 0;
1372     idata.polydirs_ptr = NULL;
1373     idata.pamh = pamh;
1374 #ifdef WITH_SELINUX
1375     if (is_selinux_enabled())
1376         idata.flags |= PAMNS_SELINUX_ENABLED;
1377     if (ctxt_based_inst_needed())
1378         idata.flags |= PAMNS_CTXT_BASED_INST;
1379 #endif
1380
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;
1389     }
1390
1391     if (idata.flags & PAMNS_DEBUG)
1392         pam_syslog(idata.pamh, LOG_DEBUG, "close_session - start");
1393
1394     /*
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"
1402      * argument.
1403      */
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");
1407         return PAM_SUCCESS;
1408     }
1409
1410     /* 
1411      * Lookup user and fill struct items
1412      */
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;
1417     }
1418
1419     pwd = pam_modutil_getpwnam(idata.pamh, user_name);
1420     if (!pwd) {
1421         pam_syslog(idata.pamh, LOG_ERR, "user unknown '%s'", user_name);
1422         return PAM_SESSION_ERR;
1423     }
1424
1425     /*
1426      * Add the user info to the instance data so we can refer to them later.
1427      */
1428     idata.user[0] = 0;
1429     strncat(idata.user, user_name, sizeof(idata.user) - 1);
1430     idata.uid = pwd->pw_uid;
1431
1432     /*
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.
1436      */
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;
1441     }
1442
1443     if (idata.flags & PAMNS_DEBUG)
1444         pam_syslog(idata.pamh, LOG_DEBUG, "Resetting namespace for pid %d",
1445                 getpid());
1446
1447     retval = orig_namespace(&idata);
1448     if (idata.flags & PAMNS_DEBUG) {
1449         if (retval)
1450             pam_syslog(idata.pamh, LOG_DEBUG,
1451                 "resetting namespace failed for pid %d", getpid());
1452         else
1453             pam_syslog(idata.pamh, LOG_DEBUG,
1454                 "resetting namespace ok for pid %d", getpid());
1455     }
1456     del_polydir_list(idata.polydirs_ptr);
1457     return PAM_SUCCESS;
1458 }
1459
1460 #ifdef PAM_STATIC
1461
1462 /* static module data */
1463
1464 struct pam_module _pam_namespace_modstruct = {
1465      "pam_namespace",
1466      NULL,
1467      NULL,
1468      NULL,
1469      pam_sm_open_session,
1470      pam_sm_close_session,
1471      NULL
1472 };
1473 #endif