(root)/
Linux-PAM-1.5.3/
modules/
pam_succeed_if/
pam_succeed_if.c
       1  /******************************************************************************
       2   * A simple user-attribute based module for PAM.
       3   *
       4   * Copyright (c) 2003 Red Hat, Inc.
       5   * Written by Nalin Dahyabhai <nalin@redhat.com>
       6   *
       7   * Redistribution and use in source and binary forms, with or without
       8   * modification, are permitted provided that the following conditions
       9   * are met:
      10   * 1. Redistributions of source code must retain the above copyright
      11   *    notice, and the entire permission notice in its entirety,
      12   *    including the disclaimer of warranties.
      13   * 2. Redistributions in binary form must reproduce the above copyright
      14   *    notice, this list of conditions and the following disclaimer in the
      15   *    documentation and/or other materials provided with the distribution.
      16   * 3. The name of the author may not be used to endorse or promote
      17   *    products derived from this software without specific prior
      18   *    written permission.
      19   *
      20   * ALTERNATIVELY, this product may be distributed under the terms of
      21   * the GNU Public License, in which case the provisions of the GPL are
      22   * required INSTEAD OF the above restrictions.  (This clause is
      23   * necessary due to a potential bad interaction between the GPL and
      24   * the restrictions contained in a BSD-style copyright.)
      25   *
      26   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
      27   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
      28   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
      29   * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
      30   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
      31   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      32   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      33   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
      34   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
      35   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
      36   * OF THE POSSIBILITY OF SUCH DAMAGE.
      37   */
      38  
      39  #include "config.h"
      40  
      41  #include <sys/types.h>
      42  #include <errno.h>
      43  #include <fcntl.h>
      44  #include <fnmatch.h>
      45  #include <limits.h>
      46  #include <stdarg.h>
      47  #include <stdio.h>
      48  #include <stdlib.h>
      49  #include <string.h>
      50  #include <syslog.h>
      51  #include <unistd.h>
      52  #include <pwd.h>
      53  #include <grp.h>
      54  #include <netdb.h>
      55  
      56  #include <security/pam_modules.h>
      57  #include <security/pam_modutil.h>
      58  #include <security/pam_ext.h>
      59  
      60  /* Basically, run cmp(atol(left), atol(right)), returning PAM_SUCCESS if
      61   * the function returns non-zero, PAM_AUTH_ERR if it returns zero, and
      62   * PAM_SERVICE_ERR if the arguments can't be parsed as numbers. */
      63  static int
      64  evaluate_num(const pam_handle_t *pamh, const char *left,
      65  	     const char *right, int (*cmp)(long long, long long))
      66  {
      67  	long long l, r;
      68  	char *p;
      69  	int ret = PAM_SUCCESS;
      70  
      71  	errno = 0;
      72  	l = strtoll(left, &p, 0);
      73  	if ((p == NULL) || (*p != '\0') || errno) {
      74  		pam_syslog(pamh, LOG_INFO, "\"%s\" is not a number", left);
      75  		ret = PAM_SERVICE_ERR;
      76  	}
      77  
      78  	r = strtoll(right, &p, 0);
      79  	if ((p == NULL) || (*p != '\0') || errno) {
      80  		pam_syslog(pamh, LOG_INFO, "\"%s\" is not a number", right);
      81  		ret = PAM_SERVICE_ERR;
      82  	}
      83  
      84  	if (ret != PAM_SUCCESS) {
      85  		return ret;
      86  	}
      87  
      88  	return cmp(l, r) ? PAM_SUCCESS : PAM_AUTH_ERR;
      89  }
      90  
      91  /* Simple numeric comparison callbacks. */
      92  static int
      93  eq(long long i, long long j)
      94  {
      95  	return i == j;
      96  }
      97  static int
      98  ne(long long i, long long j)
      99  {
     100  	return i != j;
     101  }
     102  static int
     103  lt(long long i, long long j)
     104  {
     105  	return i < j;
     106  }
     107  static int
     108  le(long long i, long long j)
     109  {
     110  	return lt(i, j) || eq(i, j);
     111  }
     112  static int
     113  gt(long long i, long long j)
     114  {
     115  	return i > j;
     116  }
     117  static int
     118  ge(long long i, long long j)
     119  {
     120  	return gt(i, j) || eq(i, j);
     121  }
     122  
     123  /* Test for numeric equality. */
     124  static int
     125  evaluate_eqn(const pam_handle_t *pamh, const char *left, const char *right)
     126  {
     127  	return evaluate_num(pamh, left, right, eq);
     128  }
     129  /* Test for string equality. */
     130  static int
     131  evaluate_eqs(const char *left, const char *right)
     132  {
     133  	return (strcmp(left, right) == 0) ? PAM_SUCCESS : PAM_AUTH_ERR;
     134  }
     135  /* Test for numeric inequality. */
     136  static int
     137  evaluate_nen(const pam_handle_t *pamh, const char *left, const char *right)
     138  {
     139  	return evaluate_num(pamh, left, right, ne);
     140  }
     141  /* Test for string inequality. */
     142  static int
     143  evaluate_nes(const char *left, const char *right)
     144  {
     145  	return (strcmp(left, right) != 0) ? PAM_SUCCESS : PAM_AUTH_ERR;
     146  }
     147  /* Test for numeric less-than-ness(?) */
     148  static int
     149  evaluate_lt(const pam_handle_t *pamh, const char *left, const char *right)
     150  {
     151  	return evaluate_num(pamh, left, right, lt);
     152  }
     153  /* Test for numeric less-than-or-equal-ness(?) */
     154  static int
     155  evaluate_le(const pam_handle_t *pamh, const char *left, const char *right)
     156  {
     157  	return evaluate_num(pamh, left, right, le);
     158  }
     159  /* Test for numeric greater-than-ness(?) */
     160  static int
     161  evaluate_gt(const pam_handle_t *pamh, const char *left, const char *right)
     162  {
     163  	return evaluate_num(pamh, left, right, gt);
     164  }
     165  /* Test for numeric greater-than-or-equal-ness(?) */
     166  static int
     167  evaluate_ge(const pam_handle_t *pamh, const char *left, const char *right)
     168  {
     169  	return evaluate_num(pamh, left, right, ge);
     170  }
     171  /* Check for file glob match. */
     172  static int
     173  evaluate_glob(const char *left, const char *right)
     174  {
     175  	return (fnmatch(right, left, 0) == 0) ? PAM_SUCCESS : PAM_AUTH_ERR;
     176  }
     177  /* Check for file glob mismatch. */
     178  static int
     179  evaluate_noglob(const char *left, const char *right)
     180  {
     181  	return (fnmatch(right, left, 0) != 0) ? PAM_SUCCESS : PAM_AUTH_ERR;
     182  }
     183  /* Check for list match. */
     184  static int
     185  evaluate_inlist(const char *left, const char *right)
     186  {
     187  	char *p;
     188  	/* Don't care about left containing ':'. */
     189  	while ((p=strstr(right, left)) != NULL) {
     190  		if (p == right || *(p-1) == ':') { /* ':' is a list separator */
     191  			p += strlen(left);
     192  			if (*p == '\0' || *p == ':') {
     193  				return PAM_SUCCESS;
     194  			}
     195  		}
     196  		right = strchr(p, ':');
     197  		if (right == NULL)
     198  			break;
     199  		else
     200  			++right;
     201  	}
     202  	return PAM_AUTH_ERR;
     203  }
     204  /* Check for list mismatch. */
     205  static int
     206  evaluate_notinlist(const char *left, const char *right)
     207  {
     208  	return evaluate_inlist(left, right) != PAM_SUCCESS ? PAM_SUCCESS : PAM_AUTH_ERR;
     209  }
     210  /* Return PAM_SUCCESS if the user is in the group. */
     211  static int
     212  evaluate_ingroup(pam_handle_t *pamh, const char *user, const char *grouplist)
     213  {
     214  	char *ptr = NULL;
     215  	static const char delim[] = ":";
     216  	char const *grp = NULL;
     217  	char *group = strdup(grouplist);
     218  
     219  	if (group == NULL)
     220  		return PAM_BUF_ERR;
     221  
     222  	grp = strtok_r(group, delim, &ptr);
     223  	while(grp != NULL) {
     224  		if (pam_modutil_user_in_group_nam_nam(pamh, user, grp) == 1) {
     225  			free(group);
     226  			return PAM_SUCCESS;
     227  		}
     228  		grp = strtok_r(NULL, delim, &ptr);
     229  	}
     230  	free(group);
     231  	return PAM_AUTH_ERR;
     232  }
     233  /* Return PAM_SUCCESS if the user is NOT in the group. */
     234  static int
     235  evaluate_notingroup(pam_handle_t *pamh, const char *user, const char *grouplist)
     236  {
     237  	char *ptr = NULL;
     238  	static const char delim[] = ":";
     239  	char const *grp = NULL;
     240  	char *group = strdup(grouplist);
     241  
     242  	if (group == NULL)
     243  		return PAM_BUF_ERR;
     244  
     245  	grp = strtok_r(group, delim, &ptr);
     246  	while(grp != NULL) {
     247  		if (pam_modutil_user_in_group_nam_nam(pamh, user, grp) == 1) {
     248  			free(group);
     249  			return PAM_AUTH_ERR;
     250  		}
     251  		grp = strtok_r(NULL, delim, &ptr);
     252  	}
     253  	free(group);
     254  	return PAM_SUCCESS;
     255  }
     256  
     257  #ifdef HAVE_INNETGR
     258  # define SOMETIMES_UNUSED UNUSED
     259  #else
     260  # define SOMETIMES_UNUSED
     261  #endif
     262  
     263  /* Return PAM_SUCCESS if the (host,user) is in the netgroup. */
     264  static int
     265  evaluate_innetgr(const pam_handle_t* pamh SOMETIMES_UNUSED, const char *host, const char *user, const char *group)
     266  {
     267  #ifdef HAVE_INNETGR
     268  	if (innetgr(group, host, user, NULL) == 1)
     269  		return PAM_SUCCESS;
     270  #else
     271  	pam_syslog (pamh, LOG_ERR, "pam_succeed_if does not have netgroup support");
     272  #endif
     273  
     274  	return PAM_AUTH_ERR;
     275  }
     276  /* Return PAM_SUCCESS if the (host,user) is NOT in the netgroup. */
     277  static int
     278  evaluate_notinnetgr(const pam_handle_t* pamh SOMETIMES_UNUSED, const char *host, const char *user, const char *group)
     279  {
     280  #ifdef HAVE_INNETGR
     281  	if (innetgr(group, host, user, NULL) == 0)
     282  		return PAM_SUCCESS;
     283  #else
     284  	pam_syslog (pamh, LOG_ERR, "pam_succeed_if does not have netgroup support");
     285  #endif
     286  	return PAM_AUTH_ERR;
     287  }
     288  
     289  /* Match a triple. */
     290  static int
     291  evaluate(pam_handle_t *pamh, int debug,
     292  	 const char *left, const char *qual, const char *right,
     293  	 struct passwd **pwd, const char *user)
     294  {
     295  	char buf[LINE_MAX] = "";
     296  	const char *attribute = left;
     297  	/* Get information about the user if needed. */
     298  	if ((*pwd == NULL) &&
     299  	    ((strcasecmp(left, "uid") == 0) ||
     300  	     (strcasecmp(left, "gid") == 0) ||
     301  	     (strcasecmp(left, "shell") == 0) ||
     302  	     (strcasecmp(left, "home") == 0) ||
     303  	     (strcasecmp(left, "dir") == 0) ||
     304  	     (strcasecmp(left, "homedir") == 0))) {
     305  		*pwd = pam_modutil_getpwnam(pamh, user);
     306  		if (*pwd == NULL) {
     307  			return PAM_USER_UNKNOWN;
     308  		}
     309  	}
     310  	/* Figure out what we're evaluating here, and convert it to a string.*/
     311  	if ((strcasecmp(left, "login") == 0) ||
     312  	    (strcasecmp(left, "name") == 0) ||
     313  	    (strcasecmp(left, "user") == 0)) {
     314  		snprintf(buf, sizeof(buf), "%s", user);
     315  		left = buf;
     316  	} else if (strcasecmp(left, "uid") == 0) {
     317  		snprintf(buf, sizeof(buf), "%lu", (unsigned long) (*pwd)->pw_uid);
     318  		left = buf;
     319  	} else if (strcasecmp(left, "gid") == 0) {
     320  		snprintf(buf, sizeof(buf), "%lu", (unsigned long) (*pwd)->pw_gid);
     321  		left = buf;
     322  	} else if (strcasecmp(left, "shell") == 0) {
     323  		snprintf(buf, sizeof(buf), "%s", (*pwd)->pw_shell);
     324  		left = buf;
     325  	} else if ((strcasecmp(left, "home") == 0) ||
     326  	    (strcasecmp(left, "dir") == 0) ||
     327  	    (strcasecmp(left, "homedir") == 0)) {
     328  		snprintf(buf, sizeof(buf), "%s", (*pwd)->pw_dir);
     329  		left = buf;
     330  	} else if (strcasecmp(left, "service") == 0) {
     331  		const void *svc;
     332  		if (pam_get_item(pamh, PAM_SERVICE, &svc) != PAM_SUCCESS ||
     333  			svc == NULL)
     334  			svc = "";
     335  		snprintf(buf, sizeof(buf), "%s", (const char *)svc);
     336  		left = buf;
     337  	} else if (strcasecmp(left, "ruser") == 0) {
     338  		const void *ruser;
     339  		if (pam_get_item(pamh, PAM_RUSER, &ruser) != PAM_SUCCESS ||
     340  			ruser == NULL)
     341  			ruser = "";
     342  		snprintf(buf, sizeof(buf), "%s", (const char *)ruser);
     343  		left = buf;
     344  		user = buf;
     345  	} else if (strcasecmp(left, "rhost") == 0) {
     346  		const void *rhost;
     347  		if (pam_get_item(pamh, PAM_RHOST, &rhost) != PAM_SUCCESS ||
     348  			rhost == NULL)
     349  			rhost = "";
     350  		snprintf(buf, sizeof(buf), "%s", (const char *)rhost);
     351  		left = buf;
     352  	} else if (strcasecmp(left, "tty") == 0) {
     353  		const void *tty;
     354  		if (pam_get_item(pamh, PAM_TTY, &tty) != PAM_SUCCESS ||
     355  			tty == NULL)
     356  			tty = "";
     357  		snprintf(buf, sizeof(buf), "%s", (const char *)tty);
     358  		left = buf;
     359  	}
     360  	/* If we have no idea what's going on, return an error. */
     361  	if (left != buf) {
     362  		pam_syslog(pamh, LOG_ERR, "unknown attribute \"%s\"", left);
     363  		return PAM_SERVICE_ERR;
     364  	}
     365  	if (debug) {
     366  		pam_syslog(pamh, LOG_DEBUG, "'%s' resolves to '%s'",
     367  			   attribute, left);
     368  	}
     369  
     370  	/* Attribute value < some threshold. */
     371  	if ((strcasecmp(qual, "<") == 0) ||
     372  	    (strcasecmp(qual, "lt") == 0)) {
     373  		return evaluate_lt(pamh, left, right);
     374  	}
     375  	/* Attribute value <= some threshold. */
     376  	if ((strcasecmp(qual, "<=") == 0) ||
     377  	    (strcasecmp(qual, "le") == 0)) {
     378  		return evaluate_le(pamh, left, right);
     379  	}
     380  	/* Attribute value > some threshold. */
     381  	if ((strcasecmp(qual, ">") == 0) ||
     382  	    (strcasecmp(qual, "gt") == 0)) {
     383  		return evaluate_gt(pamh, left, right);
     384  	}
     385  	/* Attribute value >= some threshold. */
     386  	if ((strcasecmp(qual, ">=") == 0) ||
     387  	    (strcasecmp(qual, "ge") == 0)) {
     388  		return evaluate_ge(pamh, left, right);
     389  	}
     390  	/* Attribute value == some threshold. */
     391  	if (strcasecmp(qual, "eq") == 0) {
     392  		return evaluate_eqn(pamh, left, right);
     393  	}
     394  	/* Attribute value = some string. */
     395  	if (strcasecmp(qual, "=") == 0) {
     396  		return evaluate_eqs(left, right);
     397  	}
     398  	/* Attribute value != some threshold. */
     399  	if (strcasecmp(qual, "ne") == 0) {
     400  		return evaluate_nen(pamh, left, right);
     401  	}
     402  	/* Attribute value != some string. */
     403  	if (strcasecmp(qual, "!=") == 0) {
     404  		return evaluate_nes(left, right);
     405  	}
     406  	/* Attribute value matches some pattern. */
     407  	if ((strcasecmp(qual, "=~") == 0) ||
     408  	    (strcasecmp(qual, "glob") == 0)) {
     409  		return evaluate_glob(left, right);
     410  	}
     411  	if ((strcasecmp(qual, "!~") == 0) ||
     412  	    (strcasecmp(qual, "noglob") == 0)) {
     413  		return evaluate_noglob(left, right);
     414  	}
     415  	/* Attribute value matches item in list. */
     416  	if (strcasecmp(qual, "in") == 0) {
     417  		return evaluate_inlist(left, right);
     418  	}
     419  	if (strcasecmp(qual, "notin") == 0) {
     420  		return evaluate_notinlist(left, right);
     421  	}
     422  	/* User is in this group(s). */
     423  	if (strcasecmp(qual, "ingroup") == 0) {
     424  		return evaluate_ingroup(pamh, user, right);
     425  	}
     426  	/* User is not in this group(s). */
     427  	if (strcasecmp(qual, "notingroup") == 0) {
     428  		return evaluate_notingroup(pamh, user, right);
     429  	}
     430  	/* (Rhost, user) is in this netgroup. */
     431  	if (strcasecmp(qual, "innetgr") == 0) {
     432  		const void *rhost;
     433  		if (pam_get_item(pamh, PAM_RHOST, &rhost) != PAM_SUCCESS)
     434  			rhost = NULL;
     435  		return evaluate_innetgr(pamh, rhost, user, right);
     436  	}
     437  	/* (Rhost, user) is not in this group. */
     438  	if (strcasecmp(qual, "notinnetgr") == 0) {
     439  		const void *rhost;
     440  		if (pam_get_item(pamh, PAM_RHOST, &rhost) != PAM_SUCCESS)
     441  			rhost = NULL;
     442  		return evaluate_notinnetgr(pamh, rhost, user, right);
     443  	}
     444  	/* Fail closed. */
     445  	return PAM_SERVICE_ERR;
     446  }
     447  
     448  int
     449  pam_sm_authenticate (pam_handle_t *pamh, int flags UNUSED,
     450  		     int argc, const char **argv)
     451  {
     452  	const char *user;
     453  	struct passwd *pwd = NULL;
     454  	int ret, i, count, use_uid, debug;
     455  	const char *left, *right, *qual;
     456  	int quiet_fail, quiet_succ, audit;
     457  
     458  	quiet_fail = 0;
     459  	quiet_succ = 0;
     460  	audit = 0;
     461  	for (use_uid = 0, debug = 0, i = 0; i < argc; i++) {
     462  		if (strcmp(argv[i], "debug") == 0) {
     463  			debug++;
     464  		}
     465  		if (strcmp(argv[i], "use_uid") == 0) {
     466  			use_uid++;
     467  		}
     468  		if (strcmp(argv[i], "quiet") == 0) {
     469  			quiet_fail++;
     470  			quiet_succ++;
     471  		}
     472  		if (strcmp(argv[i], "quiet_fail") == 0) {
     473  			quiet_fail++;
     474  		}
     475  		if (strcmp(argv[i], "quiet_success") == 0) {
     476  			quiet_succ++;
     477  		}
     478  		if (strcmp(argv[i], "audit") == 0) {
     479  			audit++;
     480  		}
     481  	}
     482  
     483  	if (use_uid) {
     484  		/* Get information about the user. */
     485  		pwd = pam_modutil_getpwuid(pamh, getuid());
     486  		if (pwd == NULL) {
     487  			pam_syslog(pamh, LOG_ERR,
     488  				   "error retrieving information about user %lu",
     489  				   (unsigned long)getuid());
     490  			return PAM_USER_UNKNOWN;
     491  		}
     492  		user = pwd->pw_name;
     493  	} else {
     494  		/* Get the user's name. */
     495  		ret = pam_get_user(pamh, &user, NULL);
     496  		if (ret != PAM_SUCCESS) {
     497  			pam_syslog(pamh, LOG_NOTICE,
     498  				   "cannot determine user name: %s",
     499  				   pam_strerror(pamh, ret));
     500  			return ret;
     501  		}
     502  
     503  		/* Postpone requesting password data until it is needed */
     504  	}
     505  
     506  	/* Walk the argument list. */
     507  	count = 0;
     508  	left = qual = right = NULL;
     509  	for (i = 0; i < argc; i++) {
     510  		if (strcmp(argv[i], "debug") == 0) {
     511  			continue;
     512  		}
     513  		if (strcmp(argv[i], "use_uid") == 0) {
     514  			continue;
     515  		}
     516  		if (strcmp(argv[i], "quiet") == 0) {
     517  			continue;
     518  		}
     519  		if (strcmp(argv[i], "quiet_fail") == 0) {
     520  			continue;
     521  		}
     522  		if (strcmp(argv[i], "quiet_success") == 0) {
     523  			continue;
     524  		}
     525  		if (strcmp(argv[i], "audit") == 0) {
     526  			continue;
     527  		}
     528  		if (left == NULL) {
     529  			left = argv[i];
     530  			continue;
     531  		}
     532  		if (qual == NULL) {
     533  			qual = argv[i];
     534  			continue;
     535  		}
     536  		if (right == NULL) {
     537  			right = argv[i];
     538  			if (right == NULL)
     539  				continue;
     540  
     541  			count++;
     542  			ret = evaluate(pamh, debug,
     543  				       left, qual, right,
     544  				       &pwd, user);
     545  			if (ret == PAM_USER_UNKNOWN && audit)
     546  				pam_syslog(pamh, LOG_NOTICE,
     547  					   "error retrieving information about user %s",
     548  					   user);
     549  			if (ret != PAM_SUCCESS) {
     550  				if(!quiet_fail && ret != PAM_USER_UNKNOWN)
     551  					pam_syslog(pamh, LOG_INFO,
     552  						   "requirement \"%s %s %s\" "
     553  						   "not met by user \"%s\"",
     554  						   left, qual, right, user);
     555  				left = qual = right = NULL;
     556  				break;
     557  			}
     558  			else
     559  				if(!quiet_succ)
     560  					pam_syslog(pamh, LOG_INFO,
     561  						   "requirement \"%s %s %s\" "
     562  						   "was met by user \"%s\"",
     563  						   left, qual, right, user);
     564  			left = qual = right = NULL;
     565  			continue;
     566  		}
     567  	}
     568  
     569  	if (left || qual || right) {
     570  		ret = PAM_SERVICE_ERR;
     571  		pam_syslog(pamh, LOG_ERR,
     572  			"incomplete condition detected");
     573  	} else if (count == 0) {
     574  		pam_syslog(pamh, LOG_INFO,
     575  			"no condition detected; module succeeded");
     576  	}
     577  
     578  	return ret;
     579  }
     580  
     581  int
     582  pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED,
     583                 int argc UNUSED, const char **argv UNUSED)
     584  {
     585  	return PAM_IGNORE;
     586  }
     587  
     588  int
     589  pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
     590  {
     591  	return pam_sm_authenticate(pamh, flags, argc, argv);
     592  }
     593  
     594  int
     595  pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
     596  {
     597  	return pam_sm_authenticate(pamh, flags, argc, argv);
     598  }
     599  
     600  int
     601  pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
     602  {
     603  	return pam_sm_authenticate(pamh, flags, argc, argv);
     604  }
     605  
     606  int
     607  pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
     608  {
     609  	return pam_sm_authenticate(pamh, flags, argc, argv);
     610  }