(root)/
Linux-PAM-1.5.3/
modules/
pam_filter/
pam_filter.c
       1  /*
       2   * pam_filter module
       3   *
       4   * written by Andrew Morgan <morgan@transmeta.com> with much help from
       5   * Richard Stevens' UNIX Network Programming book.
       6   */
       7  
       8  #include "config.h"
       9  
      10  #include <stdlib.h>
      11  #include <syslog.h>
      12  #include <unistd.h>
      13  #include <fcntl.h>
      14  #include <string.h>
      15  
      16  #include <stdio.h>
      17  #include <sys/types.h>
      18  #include <sys/wait.h>
      19  #include <sys/time.h>
      20  #include <sys/file.h>
      21  #include <sys/stat.h>
      22  #include <sys/socket.h>
      23  #include <sys/ioctl.h>
      24  #include <termios.h>
      25  
      26  #include <signal.h>
      27  
      28  #include <security/pam_modules.h>
      29  #include <security/pam_ext.h>
      30  #include "pam_filter.h"
      31  
      32  /* ------ some tokens used for convenience throughout this file ------- */
      33  
      34  #define FILTER_DEBUG     01
      35  #define FILTER_RUN1      02
      36  #define FILTER_RUN2      04
      37  #define NEW_TERM        010
      38  #define NON_TERM        020
      39  
      40  /* -------------------------------------------------------------------- */
      41  
      42  /* log errors */
      43  
      44  #include <stdarg.h>
      45  
      46  #define DEV_PTMX "/dev/ptmx"
      47  
      48  static int
      49  master (void)
      50  {
      51      int fd;
      52  
      53      if ((fd = open(DEV_PTMX, O_RDWR)) >= 0) {
      54  	return fd;
      55      }
      56  
      57      return -1;
      58  }
      59  
      60  static int process_args(pam_handle_t *pamh
      61  			, int argc, const char **argv, const char *type
      62  			, char ***evp, const char **filtername)
      63  {
      64      int ctrl=0;
      65  
      66      while (argc-- > 0) {
      67  	if (strcmp("debug",*argv) == 0) {
      68  	    ctrl |= FILTER_DEBUG;
      69  	} else if (strcmp("new_term",*argv) == 0) {
      70  	    ctrl |= NEW_TERM;
      71  	} else if (strcmp("non_term",*argv) == 0) {
      72  	    ctrl |= NON_TERM;
      73  	} else if (strcmp("run1",*argv) == 0) {
      74  	    ctrl |= FILTER_RUN1;
      75  	    if (argc <= 0) {
      76  		pam_syslog(pamh, LOG_ERR, "no run filter supplied");
      77  	    } else
      78  		break;
      79  	} else if (strcmp("run2",*argv) == 0) {
      80  	    ctrl |= FILTER_RUN2;
      81  	    if (argc <= 0) {
      82  		pam_syslog(pamh, LOG_ERR, "no run filter supplied");
      83  	    } else
      84  		break;
      85  	} else {
      86  	    pam_syslog(pamh, LOG_ERR, "unrecognized option: %s", *argv);
      87  	}
      88  	++argv;                   /* step along list */
      89      }
      90  
      91      if (argc < 0) {
      92  	/* there was no reference to a filter */
      93  	*filtername = NULL;
      94  	*evp = NULL;
      95      } else {
      96  	char **levp;
      97  	const char *user = NULL;
      98  	const void *tmp;
      99  	int i,size, retval;
     100  
     101  	*filtername = *++argv;
     102  	if (ctrl & FILTER_DEBUG) {
     103  	    pam_syslog(pamh, LOG_DEBUG, "will run filter %s", *filtername);
     104  	}
     105  
     106  	levp = (char **) malloc(5*sizeof(char *));
     107  	if (levp == NULL) {
     108  	    pam_syslog(pamh, LOG_CRIT, "no memory for environment of filter");
     109  	    return -1;
     110  	}
     111  
     112  	/* the "ARGS" variable */
     113  
     114  #define ARGS_NAME      "ARGS="
     115  #define ARGS_OFFSET    (sizeof(ARGS_NAME) - 1)
     116  
     117  	size = sizeof(ARGS_NAME);
     118  
     119  	for (i=0; i<argc; ++i) {
     120  	    size += strlen(argv[i]) + (i != 0);
     121  	}
     122  
     123  	levp[0] = malloc(size);
     124  	if (levp[0] == NULL) {
     125  	    pam_syslog(pamh, LOG_CRIT, "no memory for filter arguments");
     126  	    free(levp);
     127  	    return -1;
     128  	}
     129  
     130  	strcpy(levp[0], ARGS_NAME);
     131  	size = ARGS_OFFSET;
     132  	for (i=0; i<argc; ++i) {
     133  	    if (i)
     134  		levp[0][size++] = ' ';
     135  	    strcpy(levp[0]+size, argv[i]);
     136  	    size += strlen(argv[i]);
     137  	}
     138  
     139  	/* the "SERVICE" variable */
     140  
     141  #define SERVICE_NAME      "SERVICE="
     142  #define SERVICE_OFFSET    (sizeof(SERVICE_NAME) - 1)
     143  
     144  	retval = pam_get_item(pamh, PAM_SERVICE, &tmp);
     145  	if (retval != PAM_SUCCESS || tmp == NULL) {
     146  	    pam_syslog(pamh, LOG_CRIT, "service name not found");
     147  	    if (levp) {
     148  		free(levp[0]);
     149  		free(levp);
     150  	    }
     151  	    return -1;
     152  	}
     153  	size = SERVICE_OFFSET+strlen(tmp);
     154  
     155  	levp[1] = (char *) malloc(size+1);
     156  	if (levp[1] == NULL) {
     157  	    pam_syslog(pamh, LOG_CRIT, "no memory for service name");
     158  	    if (levp) {
     159  		free(levp[0]);
     160  		free(levp);
     161  	    }
     162  	    return -1;
     163  	}
     164  
     165  	strcpy(levp[1], SERVICE_NAME);
     166  	strcpy(levp[1]+SERVICE_OFFSET, tmp);
     167  	levp[1][size] = '\0';                      /* <NUL> terminate */
     168  
     169  	/* the "USER" variable */
     170  
     171  #define USER_NAME      "USER="
     172  #define USER_OFFSET    (sizeof(USER_NAME) - 1)
     173  
     174  	if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS) {
     175  	    user = "<unknown>";
     176  	}
     177  	size = USER_OFFSET+strlen(user);
     178  
     179  	levp[2] = (char *) malloc(size+1);
     180  	if (levp[2] == NULL) {
     181  	    pam_syslog(pamh, LOG_CRIT, "no memory for user's name");
     182  	    if (levp) {
     183  		free(levp[1]);
     184  		free(levp[0]);
     185  		free(levp);
     186  	    }
     187  	    return -1;
     188  	}
     189  
     190  	strcpy(levp[2], USER_NAME);
     191  	strcpy(levp[2]+USER_OFFSET, user);
     192  	levp[2][size] = '\0';                      /* <NUL> terminate */
     193  
     194  	/* the "USER" variable */
     195  
     196  #define TYPE_NAME      "TYPE="
     197  #define TYPE_OFFSET    (sizeof(TYPE_NAME) - 1)
     198  
     199  	size = TYPE_OFFSET+strlen(type);
     200  
     201  	levp[3] = (char *) malloc(size+1);
     202  	if (levp[3] == NULL) {
     203  	    pam_syslog(pamh, LOG_CRIT, "no memory for type");
     204  	    if (levp) {
     205  		free(levp[2]);
     206  		free(levp[1]);
     207  		free(levp[0]);
     208  		free(levp);
     209  	    }
     210  	    return -1;
     211  	}
     212  
     213  	strcpy(levp[3], TYPE_NAME);
     214  	strcpy(levp[3]+TYPE_OFFSET, type);
     215  	levp[3][size] = '\0';                      /* <NUL> terminate */
     216  
     217  	levp[4] = NULL;	                     /* end list */
     218  
     219  	*evp = levp;
     220      }
     221  
     222      if ((ctrl & FILTER_DEBUG) && *filtername) {
     223  	char **e;
     224  
     225  	pam_syslog(pamh, LOG_DEBUG, "filter[%s]: %s", type, *filtername);
     226  	pam_syslog(pamh, LOG_DEBUG, "environment:");
     227  	for (e=*evp; e && *e; ++e) {
     228  	    pam_syslog(pamh, LOG_DEBUG, "  %s", *e);
     229  	}
     230      }
     231  
     232      return ctrl;
     233  }
     234  
     235  static void free_evp(char *evp[])
     236  {
     237      int i;
     238  
     239      if (evp)
     240  	for (i=0; i<4; ++i) {
     241  	    if (evp[i])
     242  		free(evp[i]);
     243  	}
     244      free(evp);
     245  }
     246  
     247  static int
     248  set_filter (pam_handle_t *pamh, int flags UNUSED, int ctrl,
     249  	    char * const evp[], const char *filtername)
     250  {
     251      int status=-1;
     252      char* terminal = NULL;
     253      struct termios stored_mode;           /* initial terminal mode settings */
     254      int fd[2], child=0, child2=0, aterminal;
     255  
     256      if (filtername == NULL || *filtername != '/') {
     257  	pam_syslog(pamh, LOG_ERR,
     258  		   "filtername not permitted; full pathname required");
     259  	return PAM_ABORT;
     260      }
     261  
     262      if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)) {
     263  	aterminal = 0;
     264      } else {
     265  	aterminal = 1;
     266      }
     267  
     268      if (aterminal) {
     269  
     270  	/* open the master pseudo terminal */
     271  
     272  	fd[0] = master();
     273  	if (fd[0] < 0) {
     274  	    pam_syslog(pamh, LOG_CRIT, "no master terminal");
     275  	    return PAM_AUTH_ERR;
     276  	}
     277  
     278  	/* set terminal into raw mode.. remember old mode so that we can
     279  	   revert to it after the child has quit. */
     280  
     281  	/* this is termios terminal handling... */
     282  
     283  	if ( tcgetattr(STDIN_FILENO, &stored_mode) < 0 ) {
     284  	    pam_syslog(pamh, LOG_CRIT, "couldn't copy terminal mode: %m");
     285  	    /* in trouble, so close down */
     286  	    close(fd[0]);
     287  	    return PAM_ABORT;
     288  	} else {
     289  	    struct termios t_mode = stored_mode;
     290  
     291  	    t_mode.c_iflag = 0;            /* no input control */
     292  	    t_mode.c_oflag &= ~OPOST;      /* no output post processing */
     293  
     294  	    /* no signals, canonical input, echoing, upper/lower output */
     295  #ifdef XCASE
     296  	    t_mode.c_lflag &= ~(XCASE);
     297  #endif
     298  	    t_mode.c_lflag &= ~(ISIG|ICANON|ECHO);
     299  	    t_mode.c_cflag &= ~(CSIZE|PARENB);  /* no parity */
     300  	    t_mode.c_cflag |= CS8;              /* 8 bit chars */
     301  
     302  	    t_mode.c_cc[VMIN] = 1; /* number of chars to satisfy a read */
     303  	    t_mode.c_cc[VTIME] = 0;          /* 0/10th second for chars */
     304  
     305  	    if ( tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_mode) < 0 ) {
     306  		pam_syslog(pamh, LOG_ERR,
     307  			   "couldn't put terminal in RAW mode: %m");
     308  		close(fd[0]);
     309  		return PAM_ABORT;
     310  	    }
     311  
     312  	    /*
     313  	     * NOTE: Unlike the stream socket case here the child
     314  	     * opens the slave terminal as fd[1] *after* the fork...
     315  	     */
     316  	}
     317      } else {
     318  
     319  	/*
     320  	 * not a terminal line so just open a stream socket fd[0-1]
     321  	 * both set...
     322  	 */
     323  
     324  	if ( socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0 ) {
     325  	    pam_syslog(pamh, LOG_ERR, "couldn't open a stream pipe: %m");
     326  	    return PAM_ABORT;
     327  	}
     328      }
     329  
     330      /* start child process */
     331  
     332      if ( (child = fork()) < 0 ) {
     333  
     334  	pam_syslog(pamh, LOG_ERR, "first fork failed: %m");
     335  	if (aterminal) {
     336  		(void) tcsetattr(STDIN_FILENO, TCSAFLUSH, &stored_mode);
     337  		close(fd[0]);
     338  	} else {
     339  		/* Socket pair */
     340  		close(fd[0]);
     341  		close(fd[1]);
     342  	}
     343  
     344  	return PAM_AUTH_ERR;
     345      }
     346  
     347      if ( child == 0 ) {                  /* child process *is* application */
     348  
     349  	if (aterminal) {
     350  
     351  	    /* close the controlling tty */
     352  
     353  #if defined(__hpux) && defined(O_NOCTTY)
     354  	    int t = open("/dev/tty", O_RDWR|O_NOCTTY);
     355  #else
     356  	    int t = open("/dev/tty",O_RDWR);
     357  	    if (t >= 0) {
     358  		(void) ioctl(t, TIOCNOTTY, NULL);
     359  		close(t);
     360  	    }
     361  #endif /* defined(__hpux) && defined(O_NOCTTY) */
     362  
     363  	    /* make this process it's own process leader */
     364  	    if (setsid() == -1) {
     365  		pam_syslog(pamh, LOG_ERR,
     366  			   "child cannot become new session: %m");
     367  		return PAM_ABORT;
     368  	    }
     369  
     370  	    /* grant slave terminal */
     371  	    if (grantpt (fd[0]) < 0) {
     372  		pam_syslog(pamh, LOG_ERR, "Cannot grant access to slave terminal");
     373  		return PAM_ABORT;
     374  	    }
     375  
     376  	    /* unlock slave terminal */
     377  	    if (unlockpt (fd[0]) < 0) {
     378  		pam_syslog(pamh, LOG_ERR, "Cannot unlock slave terminal");
     379  		return PAM_ABORT;
     380  	    }
     381  
     382  	    /* find slave's name */
     383  	    terminal = ptsname(fd[0]); /* returned value should not be freed */
     384  
     385  	    if (terminal == NULL) {
     386  		pam_syslog(pamh, LOG_ERR,
     387  			   "Cannot get the name of the slave terminal: %m");
     388  		return PAM_ABORT;
     389  	    }
     390  
     391  	    fd[1] = open(terminal, O_RDWR);
     392  	    close(fd[0]);      /* process is the child -- uses line fd[1] */
     393  
     394  	    if (fd[1] < 0) {
     395  		pam_syslog(pamh, LOG_ERR,
     396  			   "cannot open slave terminal: %s: %m", terminal);
     397  		return PAM_ABORT;
     398  	    }
     399  
     400  	    /* initialize the child's terminal to be the way the
     401  	       parent's was before we set it into RAW mode */
     402  
     403  	    if ( tcsetattr(fd[1], TCSANOW, &stored_mode) < 0 ) {
     404  		pam_syslog(pamh, LOG_ERR,
     405  			   "cannot set slave terminal mode: %s: %m", terminal);
     406  		close(fd[1]);
     407  		return PAM_ABORT;
     408  	    }
     409  	} else {
     410  
     411  	    /* nothing to do for a simple stream socket */
     412  
     413  	}
     414  
     415  	/* re-assign the stdin/out to fd[1] <- (talks to filter). */
     416  
     417  	if ( dup2(fd[1],STDIN_FILENO) != STDIN_FILENO ||
     418  	     dup2(fd[1],STDOUT_FILENO) != STDOUT_FILENO ||
     419  	     dup2(fd[1],STDERR_FILENO) != STDERR_FILENO )  {
     420  	    pam_syslog(pamh, LOG_ERR,
     421  		       "unable to re-assign STDIN/OUT/ERR: %m");
     422  	    close(fd[1]);
     423  	    return PAM_ABORT;
     424  	}
     425  
     426  	/* make sure that file descriptors survive 'exec's */
     427  
     428  	if ( fcntl(STDIN_FILENO, F_SETFD, 0) ||
     429  	     fcntl(STDOUT_FILENO,F_SETFD, 0) ||
     430  	     fcntl(STDERR_FILENO,F_SETFD, 0) ) {
     431  	    pam_syslog(pamh, LOG_ERR,
     432  		       "unable to re-assign STDIN/OUT/ERR: %m");
     433  	    return PAM_ABORT;
     434  	}
     435  
     436  	/* now the user input is read from the parent/filter: forget fd */
     437  
     438  	close(fd[1]);
     439  
     440  	/* the current process is now apparently working with filtered
     441  	   stdio/stdout/stderr --- success! */
     442  
     443  	return PAM_SUCCESS;
     444      }
     445  
     446      /* Clear out passwords... there is a security problem here in
     447       * that this process never executes pam_end.  Consequently, any
     448       * other sensitive data in this process is *not* explicitly
     449       * overwritten, before the process terminates */
     450  
     451      (void) pam_set_item(pamh, PAM_AUTHTOK, NULL);
     452      (void) pam_set_item(pamh, PAM_OLDAUTHTOK, NULL);
     453  
     454      /* fork a copy of process to run the actual filter executable */
     455  
     456      if ( (child2 = fork()) < 0 ) {
     457  
     458  	pam_syslog(pamh, LOG_ERR, "filter fork failed: %m");
     459  	child2 = 0;
     460  
     461      } else if ( child2 == 0 ) {              /* exec the child filter */
     462  
     463  	if ( dup2(fd[0],APPIN_FILENO) != APPIN_FILENO ||
     464  	     dup2(fd[0],APPOUT_FILENO) != APPOUT_FILENO ||
     465  	     dup2(fd[0],APPERR_FILENO) != APPERR_FILENO )  {
     466  	    pam_syslog(pamh, LOG_ERR,
     467  		       "unable to re-assign APPIN/OUT/ERR: %m");
     468  	    close(fd[0]);
     469  	    _exit(1);
     470  	}
     471  
     472  	/* make sure that file descriptors survive 'exec's */
     473  
     474  	if ( fcntl(APPIN_FILENO, F_SETFD, 0) == -1 ||
     475  	     fcntl(APPOUT_FILENO,F_SETFD, 0) == -1 ||
     476  	     fcntl(APPERR_FILENO,F_SETFD, 0) == -1 ) {
     477  	    pam_syslog(pamh, LOG_ERR,
     478  		       "unable to retain APPIN/OUT/ERR: %m");
     479  	    close(APPIN_FILENO);
     480  	    close(APPOUT_FILENO);
     481  	    close(APPERR_FILENO);
     482  	    _exit(1);
     483  	}
     484  
     485  	/* now the user input is read from the parent through filter */
     486  
     487  	execle(filtername, "<pam_filter>", NULL, evp);
     488  
     489  	/* getting to here is an error */
     490  
     491  	pam_syslog(pamh, LOG_ERR, "filter: %s: %m", filtername);
     492  	_exit(1);
     493  
     494      } else {           /* wait for either of the two children to exit */
     495  
     496  	while (child && child2) {    /* loop if there are two children */
     497  	    int lstatus=0;
     498  	    int chid;
     499  
     500  	    chid = wait(&lstatus);
     501  	    if (chid == child) {
     502  
     503  		if (WIFEXITED(lstatus)) {            /* exited ? */
     504  		    status = WEXITSTATUS(lstatus);
     505  		} else if (WIFSIGNALED(lstatus)) {   /* killed ? */
     506  		    status = -1;
     507  		} else
     508  		    continue;             /* just stopped etc.. */
     509  		child = 0;        /* the child has exited */
     510  
     511  	    } else if (chid == child2) {
     512  		/*
     513  		 * if the filter has exited. Let the child die
     514  		 * naturally below
     515  		 */
     516  		if (WIFEXITED(lstatus) || WIFSIGNALED(lstatus))
     517  		    child2 = 0;
     518  	    } else {
     519  
     520  		pam_syslog(pamh, LOG_ERR,
     521  			   "programming error <chid=%d,lstatus=%x> "
     522  			   "in file %s at line %d",
     523  			   chid, lstatus, __FILE__, __LINE__);
     524  		child = child2 = 0;
     525  		status = -1;
     526  
     527  	    }
     528  	}
     529      }
     530  
     531      close(fd[0]);
     532  
     533      /* if there is something running, wait for it to exit */
     534  
     535      while (child || child2) {
     536  	int lstatus=0;
     537  	int chid;
     538  
     539  	chid = wait(&lstatus);
     540  
     541  	if (child && chid == child) {
     542  
     543  	    if (WIFEXITED(lstatus)) {            /* exited ? */
     544  		status = WEXITSTATUS(lstatus);
     545  	    } else if (WIFSIGNALED(lstatus)) {   /* killed ? */
     546  		status = -1;
     547  	    } else
     548  		continue;             /* just stopped etc.. */
     549  	    child = 0;        /* the child has exited */
     550  
     551  	} else if (child2 && chid == child2) {
     552  
     553  	    if (WIFEXITED(lstatus) || WIFSIGNALED(lstatus))
     554  		child2 = 0;
     555  
     556  	} else {
     557  
     558  	    pam_syslog(pamh, LOG_ERR,
     559  		       "programming error <chid=%d,lstatus=%x> "
     560  		       "in file %s at line %d",
     561  		       chid, lstatus, __FILE__, __LINE__);
     562  	    child = child2 = 0;
     563  	    status = -1;
     564  
     565  	}
     566      }
     567  
     568      if (aterminal) {
     569  	/* reset to initial terminal mode */
     570  	    (void) tcsetattr(STDIN_FILENO, TCSANOW, &stored_mode);
     571      }
     572  
     573      if (ctrl & FILTER_DEBUG) {
     574  	pam_syslog(pamh, LOG_DEBUG, "parent process exited");      /* clock off */
     575      }
     576  
     577      /* quit the parent process, returning the child's exit status */
     578  
     579      exit(status);
     580      return status; /* never reached, to make gcc happy */
     581  }
     582  
     583  static int set_the_terminal(pam_handle_t *pamh)
     584  {
     585      const void *tty;
     586  
     587      if (pam_get_item(pamh, PAM_TTY, &tty) != PAM_SUCCESS
     588  	|| tty == NULL) {
     589  	tty = ttyname(STDIN_FILENO);
     590  	if (tty == NULL) {
     591  	    pam_syslog(pamh, LOG_ERR, "couldn't get the tty name");
     592  	    return PAM_ABORT;
     593  	}
     594  	if (pam_set_item(pamh, PAM_TTY, tty) != PAM_SUCCESS) {
     595  	    pam_syslog(pamh, LOG_ERR, "couldn't set tty name");
     596  	    return PAM_ABORT;
     597  	}
     598      }
     599      return PAM_SUCCESS;
     600  }
     601  
     602  static int need_a_filter(pam_handle_t *pamh
     603  			 , int flags, int argc, const char **argv
     604  			 , const char *name, int which_run)
     605  {
     606      int ctrl;
     607      char **evp;
     608      const char *filterfile;
     609      int retval;
     610  
     611      ctrl = process_args(pamh, argc, argv, name, &evp, &filterfile);
     612      if (ctrl == -1) {
     613  	return PAM_AUTHINFO_UNAVAIL;
     614      }
     615  
     616      /* set the tty to the old or the new one? */
     617  
     618      if (!(ctrl & NON_TERM) && !(ctrl & NEW_TERM)) {
     619  	retval = set_the_terminal(pamh);
     620  	if (retval != PAM_SUCCESS) {
     621  	    pam_syslog(pamh, LOG_ERR, "tried and failed to set PAM_TTY");
     622  	}
     623      } else {
     624  	retval = PAM_SUCCESS;  /* nothing to do which is always a success */
     625      }
     626  
     627      if (retval == PAM_SUCCESS && (ctrl & which_run)) {
     628  	retval = set_filter(pamh, flags, ctrl, evp, filterfile);
     629      }
     630  
     631      if (retval == PAM_SUCCESS
     632  	&& !(ctrl & NON_TERM) && (ctrl & NEW_TERM)) {
     633  	retval = set_the_terminal(pamh);
     634  	if (retval != PAM_SUCCESS) {
     635  	    pam_syslog(pamh, LOG_ERR,
     636  		       "tried and failed to set new terminal as PAM_TTY");
     637  	}
     638      }
     639  
     640      free_evp(evp);
     641  
     642      if (ctrl & FILTER_DEBUG) {
     643  	pam_syslog(pamh, LOG_DEBUG, "filter/%s, returning %d", name, retval);
     644  	pam_syslog(pamh, LOG_DEBUG, "[%s]", pam_strerror(pamh, retval));
     645      }
     646  
     647      return retval;
     648  }
     649  
     650  /* ----------------- public functions ---------------- */
     651  
     652  /*
     653   * here are the advertised access points ...
     654   */
     655  
     656  /* ------------------ authentication ----------------- */
     657  
     658  int pam_sm_authenticate(pam_handle_t *pamh,
     659  			int flags, int argc, const char **argv)
     660  {
     661      return need_a_filter(pamh, flags, argc, argv
     662  			 , "authenticate", FILTER_RUN1);
     663  }
     664  
     665  int pam_sm_setcred(pam_handle_t *pamh, int flags,
     666  		   int argc, const char **argv)
     667  {
     668      return need_a_filter(pamh, flags, argc, argv, "setcred", FILTER_RUN2);
     669  }
     670  
     671  /* --------------- account management ---------------- */
     672  
     673  int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc,
     674                       const char **argv)
     675  {
     676      return need_a_filter(pamh, flags, argc, argv
     677  			 , "setcred", FILTER_RUN1|FILTER_RUN2 );
     678  }
     679  
     680  /* --------------- session management ---------------- */
     681  
     682  int pam_sm_open_session(pam_handle_t *pamh, int flags,
     683  			int argc, const char **argv)
     684  {
     685      return need_a_filter(pamh, flags, argc, argv
     686  			 , "open_session", FILTER_RUN1);
     687  }
     688  
     689  int pam_sm_close_session(pam_handle_t *pamh, int flags,
     690                           int argc, const char **argv)
     691  {
     692      return need_a_filter(pamh, flags, argc, argv
     693  			 , "close_session", FILTER_RUN2);
     694  }
     695  
     696  /* --------- updating authentication tokens --------- */
     697  
     698  
     699  int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
     700  		     int argc, const char **argv)
     701  {
     702      int runN;
     703  
     704      if (flags & PAM_PRELIM_CHECK)
     705  	runN = FILTER_RUN1;
     706      else if (flags & PAM_UPDATE_AUTHTOK)
     707  	runN = FILTER_RUN2;
     708      else {
     709  	pam_syslog(pamh, LOG_ERR, "unknown flags for chauthtok (0x%X)", flags);
     710  	return PAM_TRY_AGAIN;
     711      }
     712  
     713      return need_a_filter(pamh, flags, argc, argv, "chauthtok", runN);
     714  }