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