]> 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 #if !(defined(linux))
34 #error THIS CODE IS KNOWN TO WORK ONLY ON LINUX !!!
35 #endif
36
37 #include "config.h"
38
39 #include <stdio.h>
40 #include <stdio_ext.h>
41 #include <unistd.h>
42 #include <string.h>
43 #include <ctype.h>
44 #include <stdlib.h>
45 #include <errno.h>
46 #include <syslog.h>
47 #include <dlfcn.h>
48 #include <stdarg.h>
49 #include <pwd.h>
50 #include <limits.h>
51 #include <sys/types.h>
52 #include <sys/stat.h>
53 #include <sys/resource.h>
54 #include <sys/mount.h>
55 #include <sys/wait.h>
56 #include <libgen.h>
57 #include <fcntl.h>
58 #include <sched.h>
59 #include "security/pam_modules.h"
60 #include "security/pam_modutil.h"
61 #include "security/pam_ext.h"
62 #include "md5.h"
63
64 #ifdef WITH_SELINUX
65 #include <selinux/selinux.h>
66 #endif
67
68 #ifndef CLONE_NEWNS
69 #define CLONE_NEWNS 0x00020000 /* Flag to create new namespace */
70 #endif
71
72 /*
73  * Module defines
74  */
75 #ifndef PAM_NAMESPACE_CONFIG
76 #define PAM_NAMESPACE_CONFIG "/etc/security/namespace.conf"
77 #endif
78
79 #ifndef NAMESPACE_INIT_SCRIPT
80 #define NAMESPACE_INIT_SCRIPT "/etc/security/namespace.init"
81 #endif
82
83 #define PAMNS_DEBUG           0x00000100 /* Running in debug mode */
84 #define PAMNS_SELINUX_ENABLED 0x00000400 /* SELinux is enabled */
85 #define PAMNS_CTXT_BASED_INST 0x00000800 /* Context based instance needed */
86 #define PAMNS_GEN_HASH        0x00002000 /* Generate md5 hash for inst names */
87 #define PAMNS_IGN_CONFIG_ERR  0x00004000 /* Ignore format error in conf file */
88 #define PAMNS_IGN_INST_PARENT_MODE  0x00008000 /* Ignore instance parent mode */
89
90 /*
91  * Polyinstantiation method options, based on user, security context
92  * or both
93  */
94 enum polymethod {
95     USER,
96     CONTEXT,
97     BOTH,
98 };
99
100 /*
101  * Depending on the application using this namespace module, we
102  * may need to unmount priviously bind mounted instance directory.
103  * Applications such as login and sshd, that establish a new 
104  * session unmount of instance directory is not needed. For applications
105  * such as su and newrole, that switch the identity, this module 
106  * has to unmount previous instance directory first and re-mount
107  * based on the new indentity. For other trusted applications that
108  * just want to undo polyinstantiation, only unmount of previous
109  * instance directory is needed.
110  */
111 enum unmnt_op {
112     NO_UNMNT,
113     UNMNT_REMNT,
114     UNMNT_ONLY,
115 };
116
117 /*
118  * Structure that holds information about a directory to polyinstantiate
119  */
120 struct polydir_s {
121     char dir[PATH_MAX];                 /* directory to polyinstantiate */
122     char instance_prefix[PATH_MAX];     /* prefix for instance dir path name */
123     enum polymethod method;             /* method used to polyinstantiate */
124     unsigned int num_uids;              /* number of override uids */
125     uid_t *uid;                         /* list of override uids */
126     struct polydir_s *next;             /* pointer to the next polydir entry */
127 };
128
129 struct instance_data {
130     pam_handle_t *pamh;         /* The pam handle for this instance */
131     struct polydir_s *polydirs_ptr; /* The linked list pointer */
132     char user[LOGIN_NAME_MAX];  /* User name */
133     uid_t uid;                  /* The uid of the user */
134     unsigned long flags;                /* Flags for debug, selinux etc */
135 };
136
137 /*
138  * Adds an entry for a polyinstantiated directory to the linked list of
139  * polyinstantiated directories. It is called from process_line() while
140  * parsing the namespace configuration file.
141  */
142 static int add_polydir_entry(struct instance_data *idata, 
143         const struct polydir_s *ent)
144 {
145     struct polydir_s *pent;
146     unsigned int i;
147
148     /*
149      * Allocate an entry to hold information about a directory to
150      * polyinstantiate, populate it with information from 2nd argument
151      * and add the entry to the linked list of polyinstantiated
152      * directories.
153      */
154     pent = (struct polydir_s *) malloc(sizeof(struct polydir_s));
155     if (!pent) 
156         return -1;
157
158     /* Make copy */
159     strcpy(pent->dir, ent->dir);
160     strcpy(pent->instance_prefix, ent->instance_prefix);
161     pent->method = ent->method;
162     pent->num_uids = ent->num_uids;
163     if (ent->num_uids) {
164         uid_t *pptr, *eptr;
165
166         pent->uid = (uid_t *) malloc(ent->num_uids * sizeof(uid_t));
167         if (!(pent->uid)) {
168             free(pent);
169             return -1;
170         }
171         for (i = 0, pptr = pent->uid, eptr = ent->uid; i < ent->num_uids;
172                  i++, eptr++, pptr++)
173              *pptr = *eptr;
174     } else 
175         pent->uid = NULL;
176
177     /* Now attach to linked list */
178     pent->next = NULL;
179     if (idata->polydirs_ptr == NULL)
180         idata->polydirs_ptr = pent;
181     else {
182         struct polydir_s *tail;
183
184         tail = idata->polydirs_ptr;
185         while (tail->next)
186             tail = tail->next;
187         tail->next = pent;
188     }
189
190     return 0;
191 }
192
193
194 /*
195  * Deletes all the entries in the linked list.
196  */
197 static void del_polydir_list(struct polydir_s *polydirs_ptr)
198 {
199         struct polydir_s *dptr = polydirs_ptr;
200
201         while (dptr) {
202                 struct polydir_s *tptr = dptr;
203                 dptr = dptr->next;
204                 free(tptr->uid);
205                 free(tptr);
206         }
207 }
208
209
210 /*
211  * Called from parse_config_file, this function processes a single line
212  * of the namespace configuration file. It skips over comments and incomplete
213  * or malformed lines. It processes a valid line with information on
214  * polyinstantiating a directory by populating appropriate fields of a
215  * polyinstatiated directory structure and then calling add_polydir_entry to
216  * add that entry to the linked list of polyinstantiated directories.
217  */
218 static int process_line(char *line, const char *home, 
219                         struct instance_data *idata)
220 {
221     const char *dir, *instance_prefix;
222     const char *method, *uids;
223     char *tptr;
224     struct polydir_s poly;
225     int retval = 0;
226
227     poly.uid = NULL;
228     poly.num_uids = 0;
229
230     /*
231      * skip the leading white space
232      */
233     while (*line && isspace(*line))
234         line++;
235
236     /*
237      * Rip off the comments
238      */
239     tptr = strchr(line,'#');
240     if (tptr)
241         *tptr = '\0';
242
243     /*
244      * Rip off the newline char
245      */
246     tptr = strchr(line,'\n');
247     if (tptr)
248         *tptr = '\0';
249
250     /*
251      * Anything left ?
252      */
253     if (line[0] == 0)
254         return 0;
255
256     /*
257      * Initialize and scan the five strings from the line from the
258      * namespace configuration file.
259      */
260     dir = strtok_r(line, " \t", &tptr);
261     if (dir == NULL) {
262         pam_syslog(idata->pamh, LOG_NOTICE, "Invalid line missing polydir");
263         goto skipping;
264     }
265     instance_prefix = strtok_r(NULL, " \t", &tptr);
266     if (instance_prefix == NULL) {
267         pam_syslog(idata->pamh, LOG_NOTICE, "Invalid line missing instance_prefix");
268         goto skipping;
269     }
270     method = strtok_r(NULL, " \t", &tptr);
271     if (method == NULL) {
272         pam_syslog(idata->pamh, LOG_NOTICE, "Invalid line missing method");
273         goto skipping;
274     }
275
276     /*
277      * Only the uids field is allowed to be blank, to indicate no
278      * override users for polyinstantiation of that directory. If
279      * any of the other fields are blank, the line is incomplete so
280      * skip it.
281      */
282     uids = strtok_r(NULL, " \t", &tptr);
283
284     /*
285      * If the directory being polyinstantiated is the home directory
286      * of the user who is establishing a session, we have to swap
287      * the "$HOME" string with the user's home directory that is
288      * passed in as an argument.
289      */
290     if (strcmp(dir, "$HOME") == 0) {
291         dir = home;
292     }
293
294     /*
295      * Expand $HOME and $USER in instance dir prefix
296      */
297     if ((tptr = strstr(instance_prefix, "$USER")) != 0) {
298         /* FIXME: should only support this if method is USER or BOTH */
299         char *expanded = alloca(strlen(idata->user) + strlen(instance_prefix)-5+1);
300         *tptr = 0;
301         sprintf(expanded, "%s%s%s", instance_prefix, idata->user, tptr+5);
302         instance_prefix = expanded;
303     }
304     if ((tptr = strstr(instance_prefix, "$HOME")) != 0) {
305         char *expanded = alloca(strlen(home)+strlen(instance_prefix)-5+1);
306         *tptr = 0;
307         sprintf(expanded, "%s%s%s", instance_prefix, home, tptr+5);
308         instance_prefix = expanded;
309     }
310
311     /*
312      * Ensure that all pathnames are absolute path names.
313      */
314     if ((dir[0] != '/') || (instance_prefix[0] != '/')) {
315         pam_syslog(idata->pamh, LOG_NOTICE,"Pathnames must start with '/'");
316         goto skipping;
317     }
318     if (strstr(dir, "..") || strstr(instance_prefix, "..")) {
319         pam_syslog(idata->pamh, LOG_NOTICE,"Pathnames must not contain '..'");
320         goto skipping;
321     }
322
323     /*
324      * Populate polyinstantiated directory structure with appropriate
325      * pathnames and the method with which to polyinstantiate.
326      */
327     if (strlen(dir) >= sizeof(poly.dir)
328         || strlen(instance_prefix) >= sizeof(poly.instance_prefix)) {
329         pam_syslog(idata->pamh, LOG_NOTICE, "Pathnames too long");
330     }
331     strcpy(poly.dir, dir);
332     strcpy(poly.instance_prefix, instance_prefix);
333     if (strcmp(method, "user") == 0)
334         poly.method = USER;
335 #ifdef WITH_SELINUX
336     else if (strcmp(method, "context") == 0) {
337         if (idata->flags & PAMNS_CTXT_BASED_INST)
338             poly.method = CONTEXT;
339         else
340             poly.method = USER;
341     } else if (strcmp(method, "both") == 0) {
342         if (idata->flags & PAMNS_CTXT_BASED_INST)
343             poly.method = BOTH;
344         else
345             poly.method = USER;
346     }
347
348 #endif
349     else {
350         pam_syslog(idata->pamh, LOG_NOTICE, "Illegal method");
351         goto skipping;
352     }
353
354     /*
355      * If the line in namespace.conf for a directory to polyinstantiate
356      * contains a list of override users (users for whom polyinstantiation
357      * is not performed), read the user ids, convert names into uids, and
358      * add to polyinstantiated directory structure.
359      */
360     if (uids) {
361         uid_t *uidptr;
362         const char *ustr, *sstr;
363         int count, i;
364
365         for (count = 0, ustr = sstr = uids; sstr; ustr = sstr + 1, count++)
366            sstr = strchr(ustr, ',');
367
368         poly.num_uids = count;
369         poly.uid = (uid_t *) malloc(count * sizeof (uid_t));
370         uidptr = poly.uid;
371         if (uidptr == NULL) {
372             pam_syslog(idata->pamh, LOG_NOTICE, "out of memory");
373             goto skipping;
374         }
375
376         ustr = uids;
377         for (i = 0; i < count; i++) {
378             struct passwd *pwd;
379
380             tptr = strchr(ustr, ',');
381             if (tptr)
382                 *tptr = '\0';
383
384             pwd = getpwnam(ustr);
385             *uidptr = pwd->pw_uid;
386             if (i < count - 1) {
387                 ustr = tptr + 1;
388                 uidptr++;
389             }
390         }
391     }
392
393     /*
394      * Add polyinstantiated directory structure to the linked list
395      * of all polyinstantiated directory structures.
396      */
397     if (add_polydir_entry(idata, &poly) < 0) {
398         pam_syslog(idata->pamh, LOG_ERR, "Allocation Error");
399         retval = PAM_SERVICE_ERR;
400     }
401     free(poly.uid);
402
403     goto out;
404
405 skipping:
406     if (idata->flags & PAMNS_IGN_CONFIG_ERR)
407         retval = 0;
408     else
409         retval = PAM_SERVICE_ERR;
410 out:
411     return retval;
412 }
413
414
415 /*
416  * Parses /etc/security/namespace.conf file to build a linked list of
417  * polyinstantiated directory structures of type polydir_s. Each entry
418  * in the linked list contains information needed to polyinstantiate
419  * one directory.
420  */
421 static int parse_config_file(struct instance_data *idata)
422 {
423     FILE *fil;
424     char *home;
425     struct passwd *cpwd;
426     char *line = NULL;
427     int retval;
428     size_t len = 0;
429
430     if (idata->flags & PAMNS_DEBUG)
431         pam_syslog(idata->pamh, LOG_DEBUG, "Parsing config file %s", 
432                 PAM_NAMESPACE_CONFIG);
433
434     /*
435      * Extract the user's home directory to resolve $HOME entries
436      * in the namespace configuration file.
437      */
438     cpwd = getpwnam(idata->user);
439     if (!cpwd) {
440         pam_syslog(idata->pamh, LOG_ERR,
441                "Error getting home dir for '%s'", idata->user);
442         return PAM_SESSION_ERR;
443     }
444     home = strdupa(cpwd->pw_dir);
445
446     /*
447      * Open configuration file, read one line at a time and call
448      * process_line to process each line.
449      */
450     fil = fopen(PAM_NAMESPACE_CONFIG, "r");
451     if (fil == NULL) {
452         pam_syslog(idata->pamh, LOG_ERR, "Error opening config file");
453         return PAM_SERVICE_ERR;
454     }
455
456     /* Use unlocked IO */
457     __fsetlocking(fil, FSETLOCKING_BYCALLER);
458
459     /* loop reading the file */
460     while (getline(&line, &len, fil) > 0) {
461         retval = process_line(line, home, idata);
462         if (retval) {
463             pam_syslog(idata->pamh, LOG_ERR,
464                 "Error processing conf file line %s", line);
465             fclose(fil);
466             free(line);
467             return PAM_SERVICE_ERR;
468         }
469     }
470     fclose(fil);
471     free(line);
472
473     /* All done...just some debug stuff */
474     if (idata->flags & PAMNS_DEBUG) {
475         struct polydir_s *dptr = idata->polydirs_ptr;
476         uid_t *iptr;
477         uid_t i;
478
479         pam_syslog(idata->pamh, LOG_DEBUG,
480             dptr?"Configured poly dirs:":"No configured poly dirs");
481         while (dptr) {
482             pam_syslog(idata->pamh, LOG_DEBUG, "dir='%s' iprefix='%s' meth=%d",
483                    dptr->dir, dptr->instance_prefix, dptr->method);
484             for (i = 0, iptr = dptr->uid; i < dptr->num_uids; i++, iptr++)
485                 pam_syslog(idata->pamh, LOG_DEBUG, "override user %d ", *iptr);
486             dptr = dptr->next;
487         }
488     }
489
490     return PAM_SUCCESS;
491 }
492
493
494 /*
495  * This funtion returns true if a given uid is present in the polyinstantiated
496  * directory's list of override uids. If the uid is one of the override
497  * uids for the polyinstantiated directory, polyinstantiation is not
498  * performed for that user for that directory.
499  */
500 static int ns_override(struct polydir_s *polyptr, struct instance_data *idata)
501 {
502     unsigned int i;
503
504     if (idata->flags & PAMNS_DEBUG)
505         pam_syslog(idata->pamh, LOG_DEBUG,
506                 "Checking for ns override in dir %s for uid %d",
507                 polyptr->dir, idata->uid);
508
509     for (i = 0; i < polyptr->num_uids; i++) {
510         if (idata->uid == polyptr->uid[i]) {
511             return 1;
512         }
513     }
514
515     return 0;
516 }
517
518
519 /*
520  * poly_name returns the name of the polyinstantiated instance directory
521  * based on the method used for polyinstantiation (user, context or both)
522  * In addition, the function also returns the security contexts of the
523  * original directory to polyinstantiate and the polyinstantiated instance
524  * directory.
525  */
526 #ifdef WITH_SELINUX
527 static int poly_name(const struct polydir_s *polyptr, char **i_name,
528         security_context_t *i_context, security_context_t *origcon,
529         struct instance_data *idata)
530 #else
531 static int poly_name(const struct polydir_s *polyptr, char **i_name, 
532         struct instance_data *idata)
533 #endif
534 {
535 #ifdef WITH_SELINUX
536     security_context_t scon = NULL;
537     security_class_t tclass;
538 #endif
539     int rc;
540
541 # ifdef WITH_SELINUX
542     /*
543      * Get the security context of the directory to polyinstantiate.
544      */
545     rc = getfilecon(polyptr->dir, origcon);
546     if (rc < 0 || *origcon == NULL) {
547        pam_syslog(idata->pamh, LOG_ERR,
548                 "Error getting poly dir context, %m");
549        return PAM_SESSION_ERR;
550     }
551
552     /*
553      * If polyinstantiating based on security context, get current
554      * process security context, get security class for directories,
555      * and ask the policy to provide security context of the
556      * polyinstantiated instance directory.
557      */
558     if ((polyptr->method == CONTEXT) || (polyptr->method == BOTH)) {
559         rc = getexeccon(&scon);
560         if (rc < 0 || scon == NULL) {
561             pam_syslog(idata->pamh, LOG_ERR, 
562                 "Error getting exec context, %m");
563             return PAM_SESSION_ERR;
564         }
565         tclass = string_to_security_class("dir");
566
567         if (security_compute_member(scon, *origcon, tclass,
568                                                 i_context) < 0) {
569             pam_syslog(idata->pamh, LOG_ERR,
570                        "Error computing poly dir member context");
571             freecon(scon);
572             return PAM_SESSION_ERR;
573         } else if (idata->flags & PAMNS_DEBUG)
574             pam_syslog(idata->pamh, LOG_DEBUG, 
575                     "member context returned by policy %s", *i_context);
576         freecon(scon);
577     }
578 #endif
579     rc = PAM_SUCCESS;
580
581     /*
582      * Set the name of the polyinstantiated instance dir based on the
583      * polyinstantiation method.
584      */
585     switch (polyptr->method) {
586         case USER:
587             if (asprintf(i_name, "%s", idata->user) < 0) {
588                 *i_name = NULL;
589                 rc = PAM_SESSION_ERR;
590             }
591             break;
592
593 #ifdef WITH_SELINUX
594         case CONTEXT:
595             if (asprintf(i_name, "%s", *i_context) < 0) {
596                 *i_name = NULL;
597                 rc = PAM_SESSION_ERR;
598             }
599             break;
600
601         case BOTH:
602             if (asprintf(i_name, "%s_%s", *i_context, idata->user) < 0) {
603                 *i_name = NULL;
604                 rc = PAM_SESSION_ERR;
605             }
606             break;
607 #endif /* WITH_SELINUX */
608
609         default:
610             if (idata->flags & PAMNS_DEBUG)
611                 pam_syslog(idata->pamh, LOG_ERR, "Unknown method");
612             rc = PAM_SESSION_ERR;
613     }
614
615     if ((idata->flags & PAMNS_DEBUG) && rc == PAM_SUCCESS)
616         pam_syslog(idata->pamh, LOG_DEBUG, "poly_name %s", *i_name);
617
618     return rc;
619 }
620
621
622 /*
623  * Create polyinstantiated instance directory (ipath).
624  */
625 #ifdef WITH_SELINUX
626 static int create_dirs(const struct polydir_s *polyptr, char *ipath, 
627         security_context_t icontext, security_context_t ocontext,
628         struct instance_data *idata)
629 #else
630 static int create_dirs(const struct polydir_s *polyptr, char *ipath,
631         struct instance_data *idata)
632 #endif
633 {
634     struct stat statbuf, newstatbuf, instpbuf;
635     int fd, status;
636     char *inst_parent, *trailing_slash;
637     pid_t rc, pid;
638     sighandler_t osighand = NULL;
639
640     /*
641      * stat the directory to polyinstantiate, so its owner-group-mode
642      * can be propagated to instance directory
643      */
644     if (stat(polyptr->dir, &statbuf) < 0) {
645         pam_syslog(idata->pamh, LOG_ERR, "Error stating %s, %m",
646                 polyptr->dir);
647         return PAM_SESSION_ERR;
648     }
649
650     /*
651      * Make sure we are dealing with a directory
652      */
653     if (!S_ISDIR(statbuf.st_mode)) {
654         pam_syslog(idata->pamh, LOG_ERR, "poly dir %s is not a dir",
655                 polyptr->dir);
656         return PAM_SESSION_ERR;
657     }
658
659     /*
660      * stat the instance parent path to make sure it exists
661      * and is a directory. Check that its mode is 000 (unless the
662      * admin explicitly instructs to ignore the instance parent
663      * mode by the "ignore_instance_parent_mode" argument).
664      */
665     inst_parent = (char *) malloc(strlen(ipath)+1);
666     if (!inst_parent) {
667         pam_syslog(idata->pamh, LOG_ERR, "Error allocating pathname string");
668         return PAM_SESSION_ERR;
669     }
670
671     strcpy(inst_parent, ipath);
672     trailing_slash = strrchr(inst_parent, '/');
673     if (trailing_slash)
674         *trailing_slash = '\0';
675
676     if (stat(inst_parent, &instpbuf) < 0) {
677         pam_syslog(idata->pamh, LOG_ERR, "Error stating %s, %m", inst_parent);
678         free(inst_parent);
679         return PAM_SESSION_ERR;
680     }
681
682     /*
683      * Make sure we are dealing with a directory
684      */
685     if (!S_ISDIR(instpbuf.st_mode)) {
686         pam_syslog(idata->pamh, LOG_ERR, "Instance parent %s is not a dir",
687                 inst_parent);
688         free(inst_parent);
689         return PAM_SESSION_ERR;
690     }
691
692     if ((idata->flags & PAMNS_IGN_INST_PARENT_MODE) == 0) {
693         if (instpbuf.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) {
694             pam_syslog(idata->pamh, LOG_ERR, "Mode of inst parent %s not 000",
695                     inst_parent);
696             free(inst_parent);
697             return PAM_SESSION_ERR;
698         }
699     }
700     free(inst_parent);
701
702     /*
703      * Create instance directory and set its security context to the context
704      * returned by the security policy. Set its mode and ownership
705      * attributes to match that of the original directory that is being
706      * polyinstantiated.
707      */
708     if (mkdir(ipath, S_IRUSR) < 0) {
709         if (errno == EEXIST)
710             goto inst_init;
711         else {
712             pam_syslog(idata->pamh, LOG_ERR, "Error creating %s, %m",
713                         ipath);
714             return PAM_SESSION_ERR;
715         }
716     }
717
718     /* Open a descriptor to it to prevent races */
719     fd = open(ipath, O_DIRECTORY | O_RDONLY);
720     if (fd < 0) {
721         pam_syslog(idata->pamh, LOG_ERR, "Error opening %s, %m", ipath);
722         rmdir(ipath);
723         return PAM_SESSION_ERR;
724     }
725 #ifdef WITH_SELINUX
726     /* If SE Linux is disabled, no need to label it */
727     if (idata->flags & PAMNS_SELINUX_ENABLED) {
728         /* If method is USER, icontext is NULL */
729         if (icontext) {
730             if (fsetfilecon(fd, icontext) < 0) {
731                 pam_syslog(idata->pamh, LOG_ERR, 
732                         "Error setting context of %s to %s", ipath, icontext);
733                 close(fd);
734                 rmdir(ipath);
735                 return PAM_SESSION_ERR;
736             }
737         } else {
738             if (fsetfilecon(fd, ocontext) < 0) {
739                 pam_syslog(idata->pamh, LOG_ERR,
740                         "Error setting context of %s to %s", ipath, ocontext);
741                 close(fd);
742                 rmdir(ipath);
743                 return PAM_SESSION_ERR;
744             }
745         }
746     }
747 #endif
748     if (fstat(fd, &newstatbuf) < 0) {
749         pam_syslog(idata->pamh, LOG_ERR, "Error stating %s, %m",
750                 ipath);
751         rmdir(ipath);
752         return PAM_SESSION_ERR;
753     }
754     if (newstatbuf.st_uid != statbuf.st_uid ||
755                          newstatbuf.st_gid != statbuf.st_gid) {
756         if (fchown(fd, statbuf.st_uid, statbuf.st_gid) < 0) {
757             pam_syslog(idata->pamh, LOG_ERR,
758                         "Error changing owner for %s, %m",
759                         ipath);
760             close(fd);
761             rmdir(ipath);
762             return PAM_SESSION_ERR;
763         }
764     }
765     if (fchmod(fd, statbuf.st_mode & 07777) < 0) {
766         pam_syslog(idata->pamh, LOG_ERR, "Error changing mode for %s, %m",
767                         ipath);
768         close(fd);
769         rmdir(ipath);
770         return PAM_SESSION_ERR;
771     }
772     close(fd);
773
774     /*
775      * Check to see if there is a namespace initialization script in
776      * the /etc/security directory. If such a script exists
777      * execute it and pass directory to polyinstantiate and instance
778      * directory as arguments.
779      */
780
781 inst_init:
782     osighand = signal(SIGCHLD, SIG_DFL);
783     if (osighand == SIG_ERR) {
784         pam_syslog(idata->pamh, LOG_ERR, "Cannot set signal value");
785         return PAM_SESSION_ERR;
786     }
787
788     if (access(NAMESPACE_INIT_SCRIPT, F_OK) == 0) {
789         if (access(NAMESPACE_INIT_SCRIPT, X_OK) < 0) {
790             if (idata->flags & PAMNS_DEBUG)
791                 pam_syslog(idata->pamh, LOG_ERR,
792                            "Namespace init script not executable");
793             (void) signal(SIGCHLD, osighand);
794             return PAM_SESSION_ERR;
795         } else {
796             pid = fork();
797             if (pid == 0) {
798 #ifdef WITH_SELINUX
799                 if (idata->flags & PAMNS_SELINUX_ENABLED) {
800                     if (setexeccon(NULL) < 0)
801                         exit(1);
802                 }
803 #endif
804                 if (execl(NAMESPACE_INIT_SCRIPT, NAMESPACE_INIT_SCRIPT,
805                           polyptr->dir, ipath, (char *)NULL) < 0)
806                     exit(1);
807             } else if (pid > 0) {
808                 while (((rc = waitpid(pid, &status, 0)) == (pid_t)-1) &&
809                        (errno == EINTR));
810                 if (rc == (pid_t)-1) {
811                     pam_syslog(idata->pamh, LOG_ERR, "waitpid failed- %m");
812                     (void) signal(SIGCHLD, osighand);
813                     return PAM_SESSION_ERR;
814                 }
815                 if (!WIFEXITED(status) || WIFSIGNALED(status) > 0) {
816                     pam_syslog(idata->pamh, LOG_ERR,
817                                "Error initializing instance");
818                     (void) signal(SIGCHLD, osighand);
819                     return PAM_SESSION_ERR;
820                 }
821             } else if (pid < 0) {
822                 pam_syslog(idata->pamh, LOG_ERR,
823                            "Cannot fork to run namespace init script, %m");
824                 (void) signal(SIGCHLD, osighand);
825                 return PAM_SESSION_ERR;
826             }
827         }
828     }
829
830     (void) signal(SIGCHLD, osighand);
831     return PAM_SUCCESS;
832 }
833
834
835 /*
836  * md5hash generates a hash of the passed in instance directory name.
837  */
838 static int md5hash(char **instname, struct instance_data *idata)
839 {
840     int i;
841     char *md5inst = NULL;
842     char *to;
843     unsigned char inst_digest[MD5_DIGEST_LENGTH];
844
845     /*
846      * Create MD5 hashes for instance pathname.
847      */
848
849     MD5((unsigned char *)*instname, strlen(*instname), inst_digest);
850
851     if ((md5inst = malloc(MD5_DIGEST_LENGTH * 2 + 1)) == NULL) {
852         pam_syslog(idata->pamh, LOG_ERR, "Unable to allocate buffer");
853         return PAM_SESSION_ERR;
854     }
855
856     to = md5inst;
857     for (i = 0; i < MD5_DIGEST_LENGTH; i++) {
858         snprintf(to, 3, "%02x", (unsigned int)inst_digest[i]);
859         to += 3;
860     }
861
862     free(*instname);
863     *instname = md5inst;
864
865     return PAM_SUCCESS;
866 }
867
868 /*
869  * This function performs the namespace setup for a particular directory
870  * that is being polyinstantiated. It creates an MD5 hash of instance 
871  * directory, calls create_dirs to create it with appropriate
872  * security attributes, and performs bind mount to setup the process
873  * namespace.
874  */
875 static int ns_setup(const struct polydir_s *polyptr,
876         struct instance_data *idata)
877 {
878     int retval = 0;
879     char *inst_dir = NULL;
880     char *instname = NULL;
881     char *dir;
882 #ifdef WITH_SELINUX
883     security_context_t instcontext = NULL, origcontext = NULL;
884 #endif
885
886     if (idata->flags & PAMNS_DEBUG)
887         pam_syslog(idata->pamh, LOG_DEBUG,
888                "Set namespace for directory %s", polyptr->dir);
889
890     dir = strrchr(polyptr->dir, '/');
891     if (dir && strlen(dir) > 1)
892         dir++;
893
894     /*
895      * Obtain the name of instance pathname based on the
896      * polyinstantiation method and instance context returned by
897      * security policy.
898      */
899 #ifdef WITH_SELINUX
900     retval = poly_name(polyptr, &instname, &instcontext,
901                         &origcontext, idata);
902 #else
903     retval = poly_name(polyptr, &instname, idata);
904 #endif
905
906     if (retval) {
907         pam_syslog(idata->pamh, LOG_ERR, "Error getting instance name");
908         goto error_out;
909     } else {
910 #ifdef WITH_SELINUX
911         if ((idata->flags & PAMNS_DEBUG) &&
912             (idata->flags & PAMNS_SELINUX_ENABLED))
913             pam_syslog(idata->pamh, LOG_DEBUG, "Inst ctxt %s Orig ctxt %s",
914                  instcontext, origcontext);
915 #endif
916     }
917
918     if (idata->flags & PAMNS_GEN_HASH) {
919         retval = md5hash(&instname, idata);
920         if (retval < 0) {
921             pam_syslog(idata->pamh, LOG_ERR, "Error generating md5 hash");
922             goto error_out;
923         }
924     }
925
926     if (asprintf(&inst_dir, "%s%s", polyptr->instance_prefix, instname) < 0)
927         goto error_out;
928
929     if (idata->flags & PAMNS_DEBUG)
930         pam_syslog(idata->pamh, LOG_DEBUG, "instance_dir %s",
931                 inst_dir);
932
933     /*
934      * Create instance directory with appropriate security
935      * contexts, owner, group and mode bits.
936      */
937 #ifdef WITH_SELINUX
938     retval = create_dirs(polyptr, inst_dir, instcontext,
939                          origcontext, idata);
940 #else
941     retval = create_dirs(polyptr, inst_dir, idata);
942 #endif
943
944     if (retval < 0) {
945         pam_syslog(idata->pamh, LOG_ERR, "Error creating instance dir");
946         goto error_out;
947     }
948
949     /*
950      * Bind mount instance directory on top of the polyinstantiated
951      * directory to provide an instance of polyinstantiated directory
952      * based on polyinstantiated method.
953      */
954     if (mount(inst_dir, polyptr->dir, NULL, MS_BIND, NULL) < 0) {
955         pam_syslog(idata->pamh, LOG_ERR, "Error mounting %s on %s, %m",
956                    inst_dir, polyptr->dir);
957         goto error_out;
958     }
959
960     goto cleanup;
961
962     /*
963      * various error exit points. Free allocated memory and set return
964      * value to indicate a pam session error.
965      */
966 error_out:
967     retval = PAM_SESSION_ERR;
968
969 cleanup:
970     free(inst_dir);
971     free(instname);
972 #ifdef WITH_SELINUX
973     freecon(instcontext);
974     freecon(origcontext);
975 #endif
976     return retval;
977 }
978
979
980 /*
981  * This function checks to see if the current working directory is
982  * inside the directory passed in as the first argument.
983  */
984 static int cwd_in(char *dir, struct instance_data *idata)
985 {
986     int retval = 0;
987     char cwd[PATH_MAX];
988
989     if (getcwd(cwd, PATH_MAX) == NULL) {
990         pam_syslog(idata->pamh, LOG_ERR, "Can't get current dir, %m");
991         return -1;
992     }
993
994     if (strncmp(cwd, dir, strlen(dir)) == 0) {
995         if (idata->flags & PAMNS_DEBUG)
996             pam_syslog(idata->pamh, LOG_DEBUG, "cwd is inside %s", dir);
997         retval = 1;
998     } else {
999         if (idata->flags & PAMNS_DEBUG)
1000             pam_syslog(idata->pamh, LOG_DEBUG, "cwd is outside %s", dir);
1001     }
1002
1003     return retval;
1004 }
1005
1006
1007 /*
1008  * This function checks to see if polyinstantiation is needed for any
1009  * of the directories listed in the configuration file. If needed,
1010  * cycles through all polyinstantiated directory entries and calls
1011  * ns_setup to setup polyinstantiation for each one of them.
1012  */
1013 static int setup_namespace(struct instance_data *idata, enum unmnt_op unmnt)
1014 {
1015     int retval = 0, need_poly = 0, changing_dir = 0;
1016     char *cptr, *fptr, poly_parent[PATH_MAX];
1017     struct polydir_s *pptr;
1018
1019     if (idata->flags & PAMNS_DEBUG)
1020         pam_syslog(idata->pamh, LOG_DEBUG, "Set up namespace for pid %d",
1021                 getpid());
1022
1023     /*
1024      * Cycle through all polyinstantiated directory entries to see if
1025      * polyinstantiation is needed at all.
1026      */
1027     for (pptr = idata->polydirs_ptr; pptr; pptr = pptr->next) {
1028         if (ns_override(pptr, idata)) {
1029             if (idata->flags & PAMNS_DEBUG)
1030                 pam_syslog(idata->pamh, LOG_DEBUG, 
1031                         "Overriding poly for user %d for dir %s",
1032                         idata->uid, pptr->dir);
1033             continue;
1034         } else {
1035             if (idata->flags & PAMNS_DEBUG)
1036                 pam_syslog(idata->pamh, LOG_DEBUG, 
1037                         "Need poly ns for user %d for dir %s",
1038                         idata->uid, pptr->dir);
1039             need_poly = 1;
1040             break;
1041         }
1042     }
1043
1044     /*
1045      * If polyinstnatiation is needed, call the unshare system call to
1046      * disassociate from the parent namespace.
1047      */
1048     if (need_poly) {
1049         if (unshare(CLONE_NEWNS) < 0) {
1050             pam_syslog(idata->pamh, LOG_ERR,
1051                 "Unable to unshare from parent namespace, %m");
1052             return PAM_SESSION_ERR;
1053         }
1054     } else
1055         return PAM_SUCCESS;
1056
1057     /*
1058      * Again cycle through all polyinstantiated directories, this time,
1059      * call ns_setup to setup polyinstantiation for a particular entry.
1060      */
1061     for (pptr = idata->polydirs_ptr; pptr; pptr = pptr->next) {
1062         if (ns_override(pptr, idata))
1063             continue;
1064         else {
1065             if (idata->flags & PAMNS_DEBUG)
1066                 pam_syslog(idata->pamh, LOG_DEBUG,
1067                         "Setting poly ns for user %d for dir %s",
1068                       idata->uid, pptr->dir);
1069
1070             if ((unmnt == UNMNT_REMNT) || (unmnt == UNMNT_ONLY)) {
1071                 /*
1072                  * Check to see if process current directory is in the
1073                  * bind mounted instance_parent directory that we are trying to
1074                  * umount
1075                  */
1076                 if ((changing_dir = cwd_in(pptr->dir, idata)) < 0) {
1077                     return PAM_SESSION_ERR;
1078                 } else if (changing_dir) {
1079                     if (idata->flags & PAMNS_DEBUG)
1080                         pam_syslog(idata->pamh, LOG_DEBUG, "changing cwd");
1081
1082                     /*
1083                      * Change current working directory to the parent of
1084                      * the mount point, that is parent of the orig
1085                      * directory where original contents of the polydir
1086                      * are available from
1087                      */
1088                     strcpy(poly_parent, pptr->dir);
1089                     fptr = strchr(poly_parent, '/');
1090                     cptr = strrchr(poly_parent, '/');
1091                     if (fptr && cptr && (fptr == cptr))
1092                         strcpy(poly_parent, "/");
1093                     else if (cptr)
1094                         *cptr = '\0';
1095                     if (chdir(poly_parent) < 0) {
1096                         pam_syslog(idata->pamh, LOG_ERR, 
1097                                 "Can't chdir to %s, %m", poly_parent);
1098                     }
1099                 }
1100
1101                 if (umount(pptr->dir) < 0) {
1102                     int saved_errno = errno;
1103                     pam_syslog(idata->pamh, LOG_ERR, "Unmount of %s failed, %m",
1104                         pptr->dir);
1105                     if (saved_errno != EINVAL)
1106                         return PAM_SESSION_ERR;
1107                 } else if (idata->flags & PAMNS_DEBUG)
1108                     pam_syslog(idata->pamh, LOG_DEBUG, "Umount succeeded %s",
1109                                 pptr->dir);
1110             }
1111
1112             if (unmnt != UNMNT_ONLY) {
1113                 retval = ns_setup(pptr, idata);
1114                 if (retval != PAM_SUCCESS) 
1115                      break;
1116             }
1117         }
1118     }
1119
1120     return retval;
1121 }
1122
1123
1124 /*
1125  * Orig namespace. This function is called from when closing a pam
1126  * session. If authorized, it unmounts instance directory.
1127  */
1128 static int orig_namespace(struct instance_data *idata)
1129 {
1130     struct polydir_s *pptr;
1131
1132     if (idata->flags & PAMNS_DEBUG)
1133         pam_syslog(idata->pamh, LOG_DEBUG, "orig namespace for pid %d", 
1134                 getpid());
1135
1136     /*
1137      * Cycle through all polyinstantiated directories from the namespace
1138      * configuration file to see if polyinstantiation was performed for
1139      * this user for each of the entry. If it was, try and unmount
1140      * appropriate polyinstantiated instance directories.
1141      */
1142     for (pptr = idata->polydirs_ptr; pptr; pptr = pptr->next) {
1143         if (ns_override(pptr, idata))
1144             continue;
1145         else {
1146             if (idata->flags & PAMNS_DEBUG)
1147                 pam_syslog(idata->pamh, LOG_DEBUG, 
1148                         "Unmounting instance dir for user %d & dir %s",
1149                        idata->uid, pptr->dir);
1150
1151             if (umount(pptr->dir) < 0) {
1152                 pam_syslog(idata->pamh, LOG_ERR, "Unmount of %s failed, %m",
1153                        pptr->dir);
1154                 return PAM_SESSION_ERR;
1155             } else if (idata->flags & PAMNS_DEBUG)
1156                 pam_syslog(idata->pamh, LOG_DEBUG, "Unmount of %s succeeded",
1157                         pptr->dir);
1158         }
1159     }
1160     return 0;
1161 }
1162
1163
1164 #ifdef WITH_SELINUX
1165 /*
1166  * This function checks if the calling program has requested context
1167  * change by calling setexeccon(). If context change is not requested
1168  * then it does not make sense to polyinstantiate based on context.
1169  * The return value from this function is used when selecting the 
1170  * polyinstantiation method. If context change is not requested then
1171  * the polyinstantiation method is set to USER, even if the configuration
1172  * file lists the method as "context" or "both".
1173  */
1174 static int ctxt_based_inst_needed(void)
1175 {
1176     security_context_t scon = NULL;
1177     int rc = 0;
1178
1179     rc = getexeccon(&scon);
1180     if (rc < 0 || scon == NULL)
1181         return 0;
1182     else {
1183         freecon(scon);
1184         return 1;
1185     }
1186 }
1187 #endif
1188
1189
1190 /*
1191  * Entry point from pam_open_session call.
1192  */
1193 PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED,
1194                                    int argc, const char **argv)
1195 {
1196     int i, retval;
1197     struct instance_data idata;
1198     char *user_name;
1199     struct passwd *pwd;
1200     enum unmnt_op unmnt = NO_UNMNT;
1201
1202     /* init instance data */
1203     idata.flags = 0;
1204     idata.polydirs_ptr = NULL;
1205     idata.pamh = pamh;
1206 #ifdef WITH_SELINUX
1207     if (is_selinux_enabled())
1208         idata.flags |= PAMNS_SELINUX_ENABLED;
1209     if (ctxt_based_inst_needed())
1210         idata.flags |= PAMNS_CTXT_BASED_INST;
1211 #endif
1212
1213     /* Parse arguments. */
1214     for (i = 0; i < argc; i++) {
1215         if (strcmp(argv[i], "debug") == 0)
1216             idata.flags |= PAMNS_DEBUG;
1217         if (strcmp(argv[i], "gen_hash") == 0)
1218             idata.flags |= PAMNS_GEN_HASH;
1219         if (strcmp(argv[i], "ignore_config_error") == 0)
1220             idata.flags |= PAMNS_IGN_CONFIG_ERR;
1221         if (strcmp(argv[i], "ignore_instance_parent_mode") == 0)
1222             idata.flags |= PAMNS_IGN_INST_PARENT_MODE;
1223         if (strcmp(argv[i], "unmnt_remnt") == 0)
1224             unmnt = UNMNT_REMNT;
1225         if (strcmp(argv[i], "unmnt_only") == 0)
1226             unmnt = UNMNT_ONLY;
1227         if (strcmp(argv[i], "require_selinux") == 0) {
1228                 if (~(idata.flags & PAMNS_SELINUX_ENABLED)) {
1229                         pam_syslog(idata.pamh, LOG_ERR, 
1230                     "selinux_required option given and selinux is disabled");
1231                         return PAM_SESSION_ERR;
1232                 }
1233         }
1234     }
1235     if (idata.flags & PAMNS_DEBUG)
1236         pam_syslog(idata.pamh, LOG_DEBUG, "open_session - start");
1237
1238     /* 
1239      * Lookup user and fill struct items
1240      */
1241     retval = pam_get_item(idata.pamh, PAM_USER, (void*) &user_name );
1242     if ( user_name == NULL || retval != PAM_SUCCESS ) {
1243         pam_syslog(idata.pamh, LOG_ERR, "Error recovering pam user name");
1244         return PAM_SESSION_ERR;
1245     }
1246
1247     pwd = getpwnam(user_name);
1248     if (!pwd) {
1249         pam_syslog(idata.pamh, LOG_ERR, "user unknown '%s'", user_name);
1250         return PAM_SESSION_ERR;
1251     }
1252
1253     /*
1254      * Add the user info to the instance data so we can refer to them later.
1255      */
1256     idata.user[0] = 0;
1257     strncat(idata.user, user_name, sizeof(idata.user));
1258     idata.uid = pwd->pw_uid;
1259
1260     /*
1261      * Parse namespace configuration file which lists directories to
1262      * polyinstantiate, directory where instance directories are to
1263      * be created and the method used for polyinstantiation.
1264      */
1265     retval = parse_config_file(&idata);
1266     if (retval != PAM_SUCCESS) {
1267         del_polydir_list(idata.polydirs_ptr);
1268         return PAM_SESSION_ERR;
1269     }
1270
1271     if (idata.polydirs_ptr) {
1272         retval = setup_namespace(&idata, unmnt);
1273         if (idata.flags & PAMNS_DEBUG) {
1274             if (retval)
1275                 pam_syslog(idata.pamh, LOG_DEBUG,
1276                         "namespace setup failed for pid %d", getpid());
1277             else
1278                 pam_syslog(idata.pamh, LOG_DEBUG,
1279                         "namespace setup ok for pid %d", getpid());
1280         }
1281     } else if (idata.flags & PAMNS_DEBUG)
1282         pam_syslog(idata.pamh, LOG_DEBUG, "Nothing to polyinstantiate");
1283
1284     del_polydir_list(idata.polydirs_ptr);
1285     return retval;
1286 }
1287
1288
1289 /*
1290  * Entry point from pam_close_session call.
1291  */
1292 PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags UNUSED,
1293                                     int argc, const char **argv)
1294 {
1295     int i, retval;
1296     struct instance_data idata;
1297     char *user_name;
1298     struct passwd *pwd;
1299
1300     /* init instance data */
1301     idata.flags = 0;
1302     idata.polydirs_ptr = NULL;
1303     idata.pamh = pamh;
1304 #ifdef WITH_SELINUX
1305     if (is_selinux_enabled())
1306         idata.flags |= PAMNS_SELINUX_ENABLED;
1307     if (ctxt_based_inst_needed())
1308         idata.flags |= PAMNS_CTXT_BASED_INST;
1309 #endif
1310
1311     /* Parse arguments. */
1312     for (i = 0; i < argc; i++) {
1313         if (strcmp(argv[i], "debug") == 0) 
1314             idata.flags |= PAMNS_DEBUG;
1315         if (strcmp(argv[i], "ignore_config_error") == 0)
1316             idata.flags |= PAMNS_IGN_CONFIG_ERR;
1317     }
1318
1319     if (idata.flags & PAMNS_DEBUG)
1320         pam_syslog(idata.pamh, LOG_DEBUG, "close_session - start");
1321
1322     /* 
1323      * Lookup user and fill struct items
1324      */
1325     retval = pam_get_item(idata.pamh, PAM_USER, (void*) &user_name );
1326     if ( user_name == NULL || retval != PAM_SUCCESS ) {
1327         pam_syslog(idata.pamh, LOG_ERR, "Error recovering pam user name");
1328         return PAM_SESSION_ERR;
1329     }
1330
1331     pwd = getpwnam(user_name);
1332     if (!pwd) {
1333         pam_syslog(idata.pamh, LOG_ERR, "user unknown '%s'", user_name);
1334         return PAM_SESSION_ERR;
1335     }
1336
1337     /*
1338      * Add the user info to the instance data so we can refer to them later.
1339      */
1340     idata.user[0] = 0;
1341     strncat(idata.user, user_name, sizeof(idata.user));
1342     idata.uid = pwd->pw_uid;
1343
1344     /*
1345      * Parse namespace configuration file which lists directories that
1346      * are polyinstantiated, directories where instance directories are
1347      * created and the method used for polyinstantiation.
1348      */
1349     retval = parse_config_file(&idata);
1350     if ((retval != PAM_SUCCESS) || !idata.polydirs_ptr) {
1351         del_polydir_list(idata.polydirs_ptr);
1352         return PAM_SESSION_ERR;
1353     }
1354
1355     if (idata.flags & PAMNS_DEBUG)
1356         pam_syslog(idata.pamh, LOG_DEBUG, "Resetting namespace for pid %d",
1357                 getpid());
1358
1359     retval = orig_namespace(&idata);
1360     if (idata.flags & PAMNS_DEBUG) {
1361         if (retval) 
1362             pam_syslog(idata.pamh, LOG_DEBUG,
1363                 "resetting namespace failed for pid %d", getpid());
1364         else 
1365             pam_syslog(idata.pamh, LOG_DEBUG, 
1366                 "resetting namespace ok for pid %d", getpid());
1367     }
1368     del_polydir_list(idata.polydirs_ptr);
1369     return PAM_SUCCESS;
1370 }
1371
1372 #ifdef PAM_STATIC
1373
1374 /* static module data */
1375
1376 struct pam_module _pam_namespace_modstruct = {
1377      "pam_namespace",
1378      NULL,
1379      NULL,
1380      NULL,
1381      pam_sm_open_session,
1382      pam_sm_close_session,
1383      NULL
1384 };
1385 #endif
1386