]> granicus.if.org Git - linux-pam/blob - modules/pam_env/pam_env.c
Relevant BUGIDs: 124923
[linux-pam] / modules / pam_env / pam_env.c
1 /* pam_mail module */
2
3 /*
4  * $Id$
5  *
6  * Written by Dave Kinchlea <kinch@kinch.ark.com> 1997/01/31
7  * Inspired by Andrew Morgan <morgan@parc.power.net, who also supplied the 
8  * template for this file (via pam_mail)
9  */
10
11 #ifndef DEFAULT_CONF_FILE
12 #define DEFAULT_CONF_FILE       "/etc/security/pam_env.conf"
13 #endif
14
15 #define DEFAULT_ETC_ENVFILE     "/etc/environment"
16 #define DEFAULT_READ_ENVFILE    1
17
18 #include <security/_pam_aconf.h>
19
20 #include <ctype.h>
21 #include <errno.h>
22 #include <pwd.h>
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <syslog.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31
32 /*
33  * here, we make a definition for the externally accessible function
34  * in this file (this definition is required for static a module
35  * but strongly encouraged generally) it is used to instruct the
36  * modules include file to define the function prototypes.
37  */
38
39 #define PAM_SM_AUTH         /* This is primarily a AUTH_SETCRED module */
40 #define PAM_SM_SESSION      /* But I like to be friendly */
41 #define PAM_SM_PASSWORD     /*        ""                 */
42 #define PAM_SM_ACCOUNT      /*        ""                 */
43
44 #include <security/pam_modules.h>
45 #include <security/_pam_macros.h>
46
47 /* This little structure makes it easier to keep variables together */
48
49 typedef struct var {
50   char *name;
51   char *value;
52   char *defval;
53   char *override;
54 } VAR;
55
56 #define BUF_SIZE 1024
57 #define MAX_ENV  8192
58
59 #define GOOD_LINE    0
60 #define BAD_LINE     100       /* This must be > the largest PAM_* error code */
61
62 #define DEFINE_VAR   101     
63 #define UNDEFINE_VAR 102
64 #define ILLEGAL_VAR  103
65
66 static int  _assemble_line(FILE *, char *, int);
67 static int  _parse_line(char *, VAR *);
68 static int  _check_var(pam_handle_t *, VAR *);           /* This is the real meat */
69 static void _clean_var(VAR *);        
70 static int  _expand_arg(pam_handle_t *, char **);
71 static const char * _pam_get_item_byname(pam_handle_t *, const char *);
72 static int  _define_var(pam_handle_t *, VAR *);
73 static int  _undefine_var(pam_handle_t *, VAR *);
74
75 /* This is a flag used to designate an empty string */
76 static char quote='Z';         
77
78 /* some syslogging */
79
80 static void _log_err(int err, const char *format, ...)
81 {
82     va_list args;
83
84     va_start(args, format);
85     openlog("PAM-env", LOG_CONS|LOG_PID, LOG_AUTH);
86     vsyslog(err, format, args);
87     va_end(args);
88     closelog();
89 }
90
91 /* argument parsing */
92
93 #define PAM_DEBUG_ARG       0x01
94 #define PAM_NEW_CONF_FILE   0x02
95 #define PAM_ENV_SILENT      0x04
96 #define PAM_NEW_ENV_FILE    0x10
97
98 static int _pam_parse(int flags, int argc, const char **argv, char **conffile,
99         char **envfile, int *readenv)
100 {
101     int ctrl=0;
102
103
104     /* step through arguments */
105     for (; argc-- > 0; ++argv) {
106
107         /* generic options */
108
109         if (!strcmp(*argv,"debug"))
110             ctrl |= PAM_DEBUG_ARG;
111         else if (!strncmp(*argv,"conffile=",9)) {
112             *conffile = x_strdup(9+*argv);
113             if (*conffile != NULL) {
114                 D(("new Configuration File: %s", *conffile));
115                 ctrl |= PAM_NEW_CONF_FILE;
116             } else {
117                 _log_err(LOG_CRIT,
118                          "Configuration file specification missing argument - ignored");
119             }
120         } else if (!strncmp(*argv,"envfile=",8)) {
121             *envfile = x_strdup(8+*argv);
122             if (*envfile != NULL) {
123                 D(("new Env File: %s", *envfile));
124                 ctrl |= PAM_NEW_ENV_FILE;
125             } else {
126                 _log_err(LOG_CRIT,
127                          "Env file specification missing argument - ignored");
128             }
129         } else if (!strncmp(*argv,"readenv=",8))
130             *readenv = atoi(8+*argv);
131         else
132             _log_err(LOG_ERR,"pam_parse: unknown option; %s",*argv);
133     }
134
135     return ctrl;
136 }
137
138 static int _parse_config_file(pam_handle_t *pamh, int ctrl, char **conffile)
139 {
140     int retval;
141     const char *file;
142     char buffer[BUF_SIZE];
143     FILE *conf;
144     VAR Var, *var=&Var;   
145
146     var->name=NULL; var->defval=NULL; var->override=NULL;
147     D(("Called."));
148
149     if (ctrl & PAM_NEW_CONF_FILE) {
150         file = *conffile;
151     } else {
152         file = DEFAULT_CONF_FILE;
153     }
154
155     D(("Config file name is: %s", file));
156
157     /* 
158      * Lets try to open the config file, parse it and process 
159      * any variables found.
160      */
161
162     if ((conf = fopen(file,"r")) == NULL) {
163       _log_err(LOG_ERR, "Unable to open config file: %s", 
164                strerror(errno));
165       return PAM_IGNORE;
166     }
167
168     /* _pam_assemble_line will provide a complete line from the config file, with all 
169      * comments removed and any escaped newlines fixed up
170      */
171
172     while (( retval = _assemble_line(conf, buffer, BUF_SIZE)) > 0) {
173       D(("Read line: %s", buffer));
174
175       if ((retval = _parse_line(buffer, var)) == GOOD_LINE) {
176         retval = _check_var(pamh, var);
177
178         if (DEFINE_VAR == retval) {
179           retval = _define_var(pamh, var);     
180
181         } else if (UNDEFINE_VAR == retval) {
182           retval = _undefine_var(pamh, var);   
183         } 
184       } 
185       if (PAM_SUCCESS != retval && ILLEGAL_VAR != retval 
186           && BAD_LINE != retval && PAM_BAD_ITEM != retval) break;
187       
188       _clean_var(var);    
189
190     }  /* while */
191     
192     (void) fclose(conf);
193
194     /* tidy up */
195     _clean_var(var);        /* We could have got here prematurely, this is safe though */
196     _pam_overwrite(*conffile);
197     _pam_drop(*conffile);
198     file = NULL;
199     D(("Exit."));
200     return (retval<0?PAM_ABORT:PAM_SUCCESS);
201 }
202
203 static int _parse_env_file(pam_handle_t *pamh, int ctrl, char **env_file)
204 {
205     int retval=PAM_SUCCESS, i, t;
206     const char *file;
207     char buffer[BUF_SIZE], *key, *mark;
208     FILE *conf;
209
210     if (ctrl & PAM_NEW_ENV_FILE)
211         file = *env_file;
212     else
213         file = DEFAULT_ETC_ENVFILE;
214
215     D(("Env file name is: %s", file));
216
217     if ((conf = fopen(file,"r")) == NULL) {
218       D(("Unable to open env file: %s", strerror(errno)));
219       return PAM_ABORT;
220     }
221
222     while (_assemble_line(conf, buffer, BUF_SIZE) > 0) {
223         D(("Read line: %s", buffer));
224         key = buffer;
225
226         /* skip leading white space */
227         key += strspn(key, " \n\t");
228
229         /* skip blanks lines and comments */
230         if (!key || key[0] == '#')
231             continue;
232
233         /* skip over "export " if present so we can be compat with
234            bash type declerations */
235         if (strncmp(key, "export ", (size_t) 7) == 0)
236             key += 7;
237
238         /* now find the end of value */
239         mark = key;
240         while(mark[0] != '\n' && mark[0] != '#' && mark[0] != '\0')
241             mark++;
242         if (mark[0] != '\0')
243             mark[0] = '\0';
244
245        /*
246         * sanity check, the key must be alpha-numeric
247         */
248
249         for ( i = 0 ; key[i] != '=' && key[i] != '\0' ; i++ )
250             if (!isalnum(key[i]) && key[i] != '_') {
251                 D(("key is not alpha numeric - '%s', ignoring", key));
252                 continue;
253             }
254
255         /* now we try to be smart about quotes around the value,
256            but not too smart, we can't get all fancy with escaped
257            values like bash */
258         if (key[i] == '=' && (key[++i] == '\"' || key[i] == '\'')) {
259             for ( t = i+1 ; key[t] != '\0' ; t++)
260                 if (key[t] != '\"' && key[t] != '\'')
261                     key[i++] = key[t];
262                 else if (key[t+1] != '\0')
263                     key[i++] = key[t];
264             key[i] = '\0';
265         }
266
267         /* set the env var, if it fails, we break out of the loop */
268         retval = pam_putenv(pamh, key);
269         if (retval != PAM_SUCCESS) {
270             D(("error setting env \"%s\"", key));
271             break;
272         }
273     }
274     
275     (void) fclose(conf);
276
277     /* tidy up */
278     _pam_overwrite(*env_file);
279     _pam_drop(*env_file);
280     file = NULL;
281     D(("Exit."));
282     return (retval<0?PAM_IGNORE:PAM_SUCCESS);
283 }
284
285 /*
286  * This is where we read a line of the PAM config file. The line may be
287  * preceeded by lines of comments and also extended with "\\\n"
288  */
289
290 static int _assemble_line(FILE *f, char *buffer, int buf_len)
291 {
292     char *p = buffer;
293     char *s, *os;
294     int used = 0;
295
296     /* loop broken with a 'break' when a non-'\\n' ended line is read */
297
298     D(("called."));
299     for (;;) {
300         if (used >= buf_len) {
301             /* Overflow */
302             D(("_assemble_line: overflow"));
303             return -1;
304         }
305         if (fgets(p, buf_len - used, f) == NULL) {
306             if (used) {
307                 /* Incomplete read */
308                 return -1;
309             } else {
310                 /* EOF */
311                 return 0;
312             }
313         }
314
315         /* skip leading spaces --- line may be blank */
316
317         s = p + strspn(p, " \n\t");
318         if (*s && (*s != '#')) {
319             os = s;
320
321             /*
322              * we are only interested in characters before the first '#'
323              * character
324              */
325
326             while (*s && *s != '#')
327                  ++s;
328             if (*s == '#') {
329                  *s = '\0';
330                  used += strlen(os);
331                  break;                /* the line has been read */
332             }
333
334             s = os;
335
336             /*
337              * Check for backslash by scanning back from the end of
338              * the entered line, the '\n' has been included since
339              * normally a line is terminated with this
340              * character. fgets() should only return one though!
341              */
342
343             s += strlen(s);
344             while (s > os && ((*--s == ' ') || (*s == '\t')
345                               || (*s == '\n')));
346
347             /* check if it ends with a backslash */
348             if (*s == '\\') {
349                 *s = '\0';              /* truncate the line here */
350                 used += strlen(os);
351                 p = s;                  /* there is more ... */
352             } else {
353                 /* End of the line! */
354                 used += strlen(os);
355                 break;                  /* this is the complete line */
356             }
357
358         } else {
359             /* Nothing in this line */
360             /* Don't move p         */
361         }
362     }
363
364     return used;
365 }
366
367 static int _parse_line(char *buffer, VAR *var)
368 {
369   /* 
370    * parse buffer into var, legal syntax is 
371    * VARIABLE [DEFAULT=[[string]] [OVERRIDE=[value]]
372    *
373    * Any other options defined make this a bad line, 
374    * error logged and no var set
375    */
376   
377   int length, quoteflg=0;
378   char *ptr, **valptr, *tmpptr; 
379   
380   D(("Called buffer = <%s>", buffer));
381
382   length = strcspn(buffer," \t\n");
383   
384   if ((var->name = malloc(length + 1)) == NULL) {
385     _log_err(LOG_ERR, "Couldn't malloc %d bytes", length+1);
386     return PAM_BUF_ERR;
387   }
388   
389   /* 
390    * The first thing on the line HAS to be the variable name, 
391    * it may be the only thing though.
392    */
393   strncpy(var->name, buffer, length);
394   var->name[length] = '\0';
395   D(("var->name = <%s>, length = %d", var->name, length));
396
397   /* 
398    * Now we check for arguments, we only support two kinds and ('cause I am lazy)
399    * each one can actually be listed any number of times
400    */
401   
402   ptr = buffer+length;
403   while ((length = strspn(ptr, " \t")) > 0) { 
404     ptr += length;                              /* remove leading whitespace */
405     D((ptr));
406     if (strncmp(ptr,"DEFAULT=",8) == 0) {
407       ptr+=8;
408       D(("Default arg found: <%s>", ptr));
409       valptr=&(var->defval);
410     } else if (strncmp(ptr, "OVERRIDE=", 9) == 0) {
411       ptr+=9;
412       D(("Override arg found: <%s>", ptr));
413       valptr=&(var->override);
414     } else {
415       D(("Unrecognized options: <%s> - ignoring line", ptr));
416       _log_err(LOG_ERR, "Unrecognized Option: %s - ignoring line", ptr);
417       return BAD_LINE;
418     }
419     
420     if ('"' != *ptr) {       /* Escaped quotes not supported */
421       length = strcspn(ptr, " \t\n");
422       tmpptr = ptr+length;
423     } else {
424       tmpptr = strchr(++ptr, '"');   
425       if (!tmpptr) {
426         D(("Unterminated quoted string: %s", ptr-1));
427         _log_err(LOG_ERR, "Unterminated quoted string: %s", ptr-1);
428         return BAD_LINE;
429       }
430       length = tmpptr - ptr;        
431       if (*++tmpptr && ' ' != *tmpptr && '\t' != *tmpptr && '\n' != *tmpptr) {
432         D(("Quotes must cover the entire string: <%s>", ptr));
433         _log_err(LOG_ERR, "Quotes must cover the entire string: <%s>", ptr);
434         return BAD_LINE;
435       }
436       quoteflg++;
437     }
438     if (length) {
439       if ((*valptr = malloc(length + 1)) == NULL) {
440         D(("Couldn't malloc %d bytes", length+1));
441         _log_err(LOG_ERR, "Couldn't malloc %d bytes", length+1);
442         return PAM_BUF_ERR;
443       }
444       (void)strncpy(*valptr,ptr,length);
445       (*valptr)[length]='\0';
446     } else if (quoteflg--) {
447       *valptr = &quote;      /* a quick hack to handle the empty string */
448     }
449     ptr = tmpptr;         /* Start the search where we stopped */
450   } /* while */
451   
452   /* 
453    * The line is parsed, all is well.
454    */
455   
456   D(("Exit."));
457   ptr = NULL; tmpptr = NULL; valptr = NULL;
458   return GOOD_LINE;
459 }
460
461 static int _check_var(pam_handle_t *pamh, VAR *var)
462 {
463   /* 
464    * Examine the variable and determine what action to take. 
465    * Returns DEFINE_VAR, UNDEFINE_VAR depending on action to take
466    * or a PAM_* error code if passed back from other routines
467    *
468    * if no DEFAULT provided, the empty string is assumed
469    * if no OVERRIDE provided, the empty string is assumed
470    * if DEFAULT=  and OVERRIDE evaluates to the empty string, 
471    *    this variable should be undefined
472    * if DEFAULT=""  and OVERRIDE evaluates to the empty string, 
473    *    this variable should be defined with no value
474    * if OVERRIDE=value   and value turns into the empty string, DEFAULT is used
475    *
476    * If DEFINE_VAR is to be returned, the correct value to define will
477    * be pointed to by var->value
478    */
479
480   int retval;
481
482   D(("Called."));
483
484   /*
485    * First thing to do is to expand any arguments, but only
486    * if they are not the special quote values (cause expand_arg
487    * changes memory).
488    */
489
490   if (var->defval && (&quote != var->defval) &&
491       ((retval = _expand_arg(pamh, &(var->defval))) != PAM_SUCCESS)) {
492       return retval;
493   }
494   if (var->override && (&quote != var->override) &&
495       ((retval = _expand_arg(pamh, &(var->override))) != PAM_SUCCESS)) {
496     return retval;
497   }
498
499   /* Now its easy */
500   
501   if (var->override && *(var->override) && &quote != var->override) { 
502     /* if there is a non-empty string in var->override, we use it */
503     D(("OVERRIDE variable <%s> being used: <%s>", var->name, var->override));
504     var->value = var->override;
505     retval = DEFINE_VAR;
506   } else {
507     
508     var->value = var->defval;
509     if (&quote == var->defval) {
510       /* 
511        * This means that the empty string was given for defval value 
512        * which indicates that a variable should be defined with no value
513        */
514       *var->defval = '\0';
515       D(("An empty variable: <%s>", var->name));
516       retval = DEFINE_VAR;
517     } else if (var->defval) {
518       D(("DEFAULT variable <%s> being used: <%s>", var->name, var->defval));
519       retval = DEFINE_VAR;
520     } else {
521       D(("UNDEFINE variable <%s>", var->name));
522       retval = UNDEFINE_VAR;
523     }
524   }
525
526   D(("Exit."));
527   return retval;
528 }
529
530 static int _expand_arg(pam_handle_t *pamh, char **value)
531 {
532   const char *orig=*value, *tmpptr=NULL;
533   char *ptr;       /* 
534                     * Sure would be nice to use tmpptr but it needs to be 
535                     * a constant so that the compiler will shut up when I
536                     * call pam_getenv and _pam_get_item_byname -- sigh
537                     */
538                       
539   char type, tmpval[BUF_SIZE]; /* No unexpanded variable can be bigger than BUF_SIZE */
540   char tmp[MAX_ENV];   /* I know this shouldn't be hard-coded but it's so 
541                         * much easier this way */
542
543   D(("Remember to initialize tmp!"));
544   tmp[0] = '\0';
545
546   /* 
547    * (possibly non-existent) environment variables can be used as values
548    * by prepending a "$" and wrapping in {} (ie: ${HOST}), can escape with "\"
549    * (possibly non-existent) PAM items can be used as values 
550    * by prepending a "@" and wrapping in {} (ie: @{PAM_RHOST}, can escape 
551    *
552    */
553   D(("Expanding <%s>",orig));
554   while (*orig) {     /* while there is some input to deal with */
555     if ('\\' == *orig) {
556       ++orig;
557       if ('$' != *orig && '@' != *orig) {
558         D(("Unrecognized escaped character: <%c> - ignoring", *orig));
559         _log_err(LOG_ERR, "Unrecognized escaped character: <%c> - ignoring", 
560                  *orig);
561       } else if ((strlen(tmp) + 1) < MAX_ENV) {
562         tmp[strlen(tmp)] = *orig++;        /* Note the increment */
563       } else {
564         /* is it really a good idea to try to log this? */
565         D(("Variable buffer overflow: <%s> + <%s>", tmp, tmpptr));
566         _log_err(LOG_ERR, "Variable buffer overflow: <%s> + <%s>", tmp, tmpptr);
567       }
568       continue;
569     } 
570     if ('$' == *orig || '@' == *orig) {
571       if ('{' != *(orig+1)) {
572         D(("Expandable variables must be wrapped in {} <%s> - ignoring", orig));
573         _log_err(LOG_ERR, "Expandable variables must be wrapped in {} <%s> - ignoring", 
574                  orig);
575         if ((strlen(tmp) + 1) < MAX_ENV) {
576           tmp[strlen(tmp)] = *orig++;        /* Note the increment */
577         }
578         continue;
579       } else {
580         D(("Expandable argument: <%s>", orig));
581         type = *orig;
582         orig+=2;     /* skip the ${ or @{ characters */
583         ptr = strchr(orig, '}');
584         if (ptr) { 
585           *ptr++ = '\0';
586         } else {
587           D(("Unterminated expandable variable: <%s>", orig-2));
588           _log_err(LOG_ERR, "Unterminated expandable variable: <%s>", orig-2);
589           return PAM_ABORT;
590         }
591         strncpy(tmpval, orig, (size_t) BUF_SIZE);
592         orig=ptr;
593         /* 
594          * so, we know we need to expand tmpval, it is either 
595          * an environment variable or a PAM_ITEM. type will tell us which
596          */
597         switch (type) {
598           
599         case '$':
600           D(("Expanding env var: <%s>",tmpval));
601           tmpptr = pam_getenv(pamh, tmpval);
602           D(("Expanded to <%s>", tmpptr));
603           break;
604           
605         case '@':
606           D(("Expanding pam item: <%s>",tmpval));
607           tmpptr = _pam_get_item_byname(pamh, tmpval);
608           D(("Expanded to <%s>", tmpptr));
609           break;
610
611         default:
612           D(("Impossible error, type == <%c>", type));
613           _log_err(LOG_CRIT, "Impossible error, type == <%c>", type);
614           return PAM_ABORT;
615         }         /* switch */
616         
617         if (tmpptr) {
618           if ((strlen(tmp) + strlen(tmpptr)) < MAX_ENV) {
619             strcat(tmp, tmpptr);
620           } else {
621             /* is it really a good idea to try to log this? */
622             D(("Variable buffer overflow: <%s> + <%s>", tmp, tmpptr));
623             _log_err(LOG_ERR, "Variable buffer overflow: <%s> + <%s>", tmp, tmpptr);
624           }
625         }
626       }           /* if ('{' != *orig++) */
627     } else {      /* if ( '$' == *orig || '@' == *orig) */
628       if ((strlen(tmp) + 1) < MAX_ENV) {
629         tmp[strlen(tmp)] = *orig++;        /* Note the increment */
630       } else {
631         /* is it really a good idea to try to log this? */
632         D(("Variable buffer overflow: <%s> + <%s>", tmp, tmpptr));
633         _log_err(LOG_ERR, "Variable buffer overflow: <%s> + <%s>", tmp, tmpptr);
634       }
635     }
636   }              /* for (;*orig;) */
637
638   if (strlen(tmp) > strlen(*value)) {
639     free(*value);
640     if ((*value = malloc(strlen(tmp) +1)) == NULL) {
641       D(("Couldn't malloc %d bytes for expanded var", strlen(tmp)+1));
642       _log_err(LOG_ERR,"Couldn't malloc %d bytes for expanded var",
643                strlen(tmp)+1);
644       return PAM_BUF_ERR;
645     }
646   }
647   strcpy(*value, tmp);
648   memset(tmp,'\0',sizeof(tmp));
649   D(("Exit."));
650
651   return PAM_SUCCESS;
652 }
653
654 static const char * _pam_get_item_byname(pam_handle_t *pamh, const char *name)
655 {
656   /* 
657    * This function just allows me to use names as given in the config
658    * file and translate them into the appropriate PAM_ITEM macro
659    */
660
661   int item;
662   const char *itemval;
663
664   D(("Called."));
665   if (strcmp(name, "PAM_USER") == 0) {
666     item = PAM_USER;
667   } else if (strcmp(name, "PAM_USER_PROMPT") == 0) {
668     item = PAM_USER_PROMPT;
669   } else if (strcmp(name, "PAM_TTY") == 0) {
670     item = PAM_TTY;
671   } else if (strcmp(name, "PAM_RUSER") == 0) {
672     item = PAM_RUSER;
673   } else if (strcmp(name, "PAM_RHOST") == 0) {
674     item = PAM_RHOST;
675   } else {
676     D(("Unknown PAM_ITEM: <%s>", name));
677     _log_err(LOG_ERR, "Unknown PAM_ITEM: <%s>", name);
678     return NULL;
679   }
680   
681   if (pam_get_item(pamh, item, (const void **)&itemval) != PAM_SUCCESS) {
682     D(("pam_get_item failed"));
683     return NULL;     /* let pam_get_item() log the error */
684   }
685   D(("Exit."));
686   return itemval;
687 }
688
689 static int _define_var(pam_handle_t *pamh, VAR *var)
690 {
691   /* We have a variable to define, this is a simple function */
692   
693   char *envvar;
694   int size, retval=PAM_SUCCESS;
695   
696   D(("Called."));
697   size = strlen(var->name)+strlen(var->value)+2;
698   if ((envvar = malloc(size)) == NULL) {
699     D(("Malloc fail, size = %d", size));
700     _log_err(LOG_ERR, "Malloc fail, size = %d", size);
701     return PAM_BUF_ERR;
702   }
703   (void) sprintf(envvar,"%s=%s",var->name,var->value);
704   retval = pam_putenv(pamh, envvar);
705   free(envvar); envvar=NULL;
706   D(("Exit."));
707   return retval;
708 }
709
710 static int _undefine_var(pam_handle_t *pamh, VAR *var)
711 {
712   /* We have a variable to undefine, this is a simple function */
713   
714   D(("Called and exit."));
715   return pam_putenv(pamh, var->name);
716 }
717
718 static void   _clean_var(VAR *var)
719 {
720     if (var->name) {
721       free(var->name); 
722     }
723     if (var->defval && (&quote != var->defval)) {
724       free(var->defval); 
725     }
726     if (var->override && (&quote != var->override)) {
727       free(var->override); 
728     }
729     var->name = NULL;
730     var->value = NULL;    /* never has memory specific to it */
731     var->defval = NULL;
732     var->override = NULL;
733     return;
734 }
735
736
737
738 /* --- authentication management functions (only) --- */
739
740 PAM_EXTERN
741 int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc,
742                         const char **argv)
743
744   return PAM_IGNORE;
745 }
746
747 PAM_EXTERN 
748 int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, 
749                    const char **argv)
750 {
751   int retval, ctrl, readenv=DEFAULT_READ_ENVFILE;
752   char *conf_file=NULL, *env_file=NULL;
753
754   /*
755    * this module sets environment variables read in from a file
756    */
757   
758   D(("Called."));
759   ctrl = _pam_parse(flags, argc, argv, &conf_file, &env_file, &readenv);
760
761   retval = _parse_config_file(pamh, ctrl, &conf_file);
762
763   if(readenv)
764     _parse_env_file(pamh, ctrl, &env_file);
765
766   /* indicate success or failure */
767   
768   D(("Exit."));
769   return retval;
770 }
771
772 PAM_EXTERN 
773 int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, 
774                      const char **argv)
775 {
776   _log_err(LOG_NOTICE, "pam_sm_acct_mgmt called inappropriatly");
777   return PAM_SERVICE_ERR;
778 }
779  
780 PAM_EXTERN
781 int pam_sm_open_session(pam_handle_t *pamh,int flags,int argc
782                         ,const char **argv)
783 {
784   int retval, ctrl, readenv=DEFAULT_READ_ENVFILE;
785   char *conf_file=NULL, *env_file=NULL;
786   
787   /*
788    * this module sets environment variables read in from a file
789    */
790   
791   D(("Called."));
792   ctrl = _pam_parse(flags, argc, argv, &conf_file, &env_file, &readenv);
793   
794   retval = _parse_config_file(pamh, ctrl, &conf_file);
795   
796   if(readenv)
797     _parse_env_file(pamh, ctrl, &env_file);
798
799   /* indicate success or failure */
800   
801   D(("Exit."));
802   return retval;
803 }
804
805 PAM_EXTERN
806 int pam_sm_close_session(pam_handle_t *pamh,int flags,int argc,
807                          const char **argv)
808 {
809   D(("Called and Exit"));
810   return PAM_SUCCESS;
811 }
812
813 PAM_EXTERN 
814 int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, 
815                      const char **argv)
816 {
817   _log_err(LOG_NOTICE, "pam_sm_chauthtok called inappropriatly");
818   return PAM_SERVICE_ERR;
819 }
820
821 #ifdef PAM_STATIC
822
823 /* static module data */
824
825 struct pam_module _pam_env_modstruct = {
826      "pam_env",
827      pam_sm_authenticate,
828      pam_sm_setcred,
829      pam_sm_acct_mgmt,
830      pam_sm_open_session,
831      pam_sm_close_session,
832      pam_sm_chauthtok,
833 };
834
835 #endif
836
837 /* end of module definition */