(root)/
Linux-PAM-1.5.3/
modules/
pam_unix/
support.c
       1  /*
       2   * Copyright information at end of file.
       3   */
       4  
       5  #include "config.h"
       6  
       7  #include <stdlib.h>
       8  #include <unistd.h>
       9  #include <stdarg.h>
      10  #include <stdio.h>
      11  #include <string.h>
      12  #include <malloc.h>
      13  #include <pwd.h>
      14  #include <shadow.h>
      15  #include <limits.h>
      16  #include <utmp.h>
      17  #include <errno.h>
      18  #include <signal.h>
      19  #include <ctype.h>
      20  #include <syslog.h>
      21  #include <sys/resource.h>
      22  #ifdef HAVE_NIS
      23  #include <rpcsvc/ypclnt.h>
      24  #endif
      25  
      26  #include <security/_pam_macros.h>
      27  #include <security/pam_modules.h>
      28  #include <security/pam_ext.h>
      29  #include <security/pam_modutil.h>
      30  
      31  #include "pam_cc_compat.h"
      32  #include "pam_inline.h"
      33  #include "support.h"
      34  #include "passverify.h"
      35  
      36  /* this is a front-end for module-application conversations */
      37  
      38  int _make_remark(pam_handle_t * pamh, unsigned long long ctrl,
      39  		    int type, const char *text)
      40  {
      41  	int retval = PAM_SUCCESS;
      42  
      43  	if (off(UNIX__QUIET, ctrl)) {
      44  		retval = pam_prompt(pamh, type, NULL, "%s", text);
      45  	}
      46  	return retval;
      47  }
      48  
      49  /*
      50   * set the control flags for the UNIX module.
      51   */
      52  
      53  unsigned long long _set_ctrl(pam_handle_t *pamh, int flags, int *remember,
      54  			     int *rounds, int *pass_min_len, int argc,
      55  			     const char **argv)
      56  {
      57  	unsigned long long ctrl;
      58  	char *val;
      59  	int j;
      60  
      61  	D(("called."));
      62  
      63  	ctrl = UNIX_DEFAULTS;	/* the default selection of options */
      64  
      65  	/* set some flags manually */
      66  
      67  	if (getuid() == 0 && !(flags & PAM_CHANGE_EXPIRED_AUTHTOK)) {
      68  		D(("IAMROOT"));
      69  		set(UNIX__IAMROOT, ctrl);
      70  	}
      71  	if (flags & PAM_UPDATE_AUTHTOK) {
      72  		D(("UPDATE_AUTHTOK"));
      73  		set(UNIX__UPDATE, ctrl);
      74  	}
      75  	if (flags & PAM_PRELIM_CHECK) {
      76  		D(("PRELIM_CHECK"));
      77  		set(UNIX__PRELIM, ctrl);
      78  	}
      79  	if (flags & PAM_SILENT) {
      80  		D(("SILENT"));
      81  		set(UNIX__QUIET, ctrl);
      82  	}
      83  
      84  	/* preset encryption method with value from /etc/login.defs */
      85  	val = pam_modutil_search_key(pamh, LOGIN_DEFS, "ENCRYPT_METHOD");
      86  	if (val) {
      87  	  for (j = 0; j < UNIX_CTRLS_; ++j) {
      88  	    if (unix_args[j].token && unix_args[j].is_hash_algo
      89  		&& !strncasecmp(val, unix_args[j].token, strlen(unix_args[j].token))) {
      90  	      break;
      91  	    }
      92  	  }
      93  	  if (j >= UNIX_CTRLS_) {
      94  	    pam_syslog(pamh, LOG_WARNING, "unrecognized ENCRYPT_METHOD value [%s]", val);
      95  	  } else {
      96  	    ctrl &= unix_args[j].mask;	/* for turning things off */
      97  	    ctrl |= unix_args[j].flag;	/* for turning things on  */
      98  	  }
      99  	  free (val);
     100  
     101  	  /* read number of rounds for crypt algo */
     102  	  if (rounds && (on(UNIX_SHA256_PASS, ctrl) || on(UNIX_SHA512_PASS, ctrl))) {
     103  	    val = pam_modutil_search_key(pamh, LOGIN_DEFS, "SHA_CRYPT_MAX_ROUNDS");
     104  
     105  	    if (val) {
     106  	      *rounds = strtol(val, NULL, 10);
     107  	      set(UNIX_ALGO_ROUNDS, ctrl);
     108  	      free (val);
     109  	    }
     110  	  }
     111  	}
     112  
     113  	/* now parse the arguments to this module */
     114  
     115  	for (; argc-- > 0; ++argv) {
     116  		const char *str = NULL;
     117  
     118  		D(("pam_unix arg: %s", *argv));
     119  
     120  		for (j = 0; j < UNIX_CTRLS_; ++j) {
     121  			if (unix_args[j].token
     122  			    && (str = pam_str_skip_prefix_len(*argv,
     123  							      unix_args[j].token,
     124  							      strlen(unix_args[j].token))) != NULL) {
     125  				break;
     126  			}
     127  		}
     128  
     129  		if (str == NULL) {
     130  			pam_syslog(pamh, LOG_ERR,
     131  			         "unrecognized option [%s]", *argv);
     132  		} else {
     133  			/* special cases */
     134  			if (j == UNIX_REMEMBER_PASSWD) {
     135  				if (remember == NULL) {
     136  					pam_syslog(pamh, LOG_ERR,
     137  					    "option remember not allowed for this module type");
     138  					continue;
     139  				}
     140  				*remember = strtol(str, NULL, 10);
     141  				if ((*remember == INT_MIN) || (*remember == INT_MAX))
     142  					*remember = -1;
     143  				if (*remember > 400)
     144  					*remember = 400;
     145  			} else if (j == UNIX_MIN_PASS_LEN) {
     146  				if (pass_min_len == NULL) {
     147  					pam_syslog(pamh, LOG_ERR,
     148  					    "option minlen not allowed for this module type");
     149  					continue;
     150  				}
     151  				*pass_min_len = atoi(str);
     152  			} else if (j == UNIX_ALGO_ROUNDS) {
     153  				if (rounds == NULL) {
     154  					pam_syslog(pamh, LOG_ERR,
     155  					    "option rounds not allowed for this module type");
     156  					continue;
     157  				}
     158  				*rounds = strtol(str, NULL, 10);
     159  			}
     160  
     161  			ctrl &= unix_args[j].mask;	/* for turning things off */
     162  			ctrl |= unix_args[j].flag;	/* for turning things on  */
     163  		}
     164  	}
     165  
     166  	if (UNIX_DES_CRYPT(ctrl)
     167  	    && pass_min_len && *pass_min_len > 8)
     168  	  {
     169  	    pam_syslog (pamh, LOG_NOTICE, "Password minlen reset to 8 characters");
     170  	    *pass_min_len = 8;
     171  	  }
     172  
     173  	if (flags & PAM_DISALLOW_NULL_AUTHTOK) {
     174  		D(("DISALLOW_NULL_AUTHTOK"));
     175  		set(UNIX__NONULL, ctrl);
     176  	}
     177  
     178  	/* Set default rounds for blowfish, gost-yescrypt and yescrypt */
     179  	if (off(UNIX_ALGO_ROUNDS, ctrl) && rounds != NULL) {
     180  		if (on(UNIX_BLOWFISH_PASS, ctrl) ||
     181  		    on(UNIX_GOST_YESCRYPT_PASS, ctrl) ||
     182  		    on(UNIX_YESCRYPT_PASS, ctrl)) {
     183  			*rounds = 5;
     184  			set(UNIX_ALGO_ROUNDS, ctrl);
     185  		}
     186  	}
     187  
     188  	/* Enforce sane "rounds" values */
     189  	if (on(UNIX_ALGO_ROUNDS, ctrl)) {
     190  		if (on(UNIX_GOST_YESCRYPT_PASS, ctrl) ||
     191  		    on(UNIX_YESCRYPT_PASS, ctrl)) {
     192  			if (*rounds < 3 || *rounds > 11)
     193  				*rounds = 5;
     194  		} else if (on(UNIX_BLOWFISH_PASS, ctrl)) {
     195  			if (*rounds < 4 || *rounds > 31)
     196  				*rounds = 5;
     197  		} else if (on(UNIX_SHA256_PASS, ctrl) || on(UNIX_SHA512_PASS, ctrl)) {
     198  			if ((*rounds < 1000) || (*rounds == INT_MAX)) {
     199  				/* don't care about bogus values */
     200  				*rounds = 0;
     201  				unset(UNIX_ALGO_ROUNDS, ctrl);
     202  			} else if (*rounds >= 10000000) {
     203  				*rounds = 9999999;
     204  			}
     205  		}
     206  	}
     207  
     208  	/* auditing is a more sensitive version of debug */
     209  
     210  	if (on(UNIX_AUDIT, ctrl)) {
     211  		set(UNIX_DEBUG, ctrl);
     212  	}
     213  	/* return the set of flags */
     214  
     215  	D(("done."));
     216  	return ctrl;
     217  }
     218  
     219  /* ************************************************************** *
     220   * Useful non-trivial functions                                   *
     221   * ************************************************************** */
     222  
     223    /*
     224     * the following is used to keep track of the number of times a user fails
     225     * to authenticate themself.
     226     */
     227  
     228  #define FAIL_PREFIX                   "-UN*X-FAIL-"
     229  #define UNIX_MAX_RETRIES              3
     230  
     231  struct _pam_failed_auth {
     232  	char *user;		/* user that's failed to be authenticated */
     233  	char *name;		/* attempt from user with name */
     234  	int uid;		/* uid of calling user */
     235  	int euid;		/* euid of calling process */
     236  	int count;		/* number of failures so far */
     237  };
     238  
     239  #ifndef PAM_DATA_REPLACE
     240  #error "Need to get an updated libpam 0.52 or better"
     241  #endif
     242  
     243  static void _cleanup_failures(pam_handle_t * pamh, void *fl, int err)
     244  {
     245  	int quiet;
     246  	const void *service = NULL;
     247  	const void *ruser = NULL;
     248  	const void *rhost = NULL;
     249  	const void *tty = NULL;
     250  	struct _pam_failed_auth *failure;
     251  
     252  	D(("called"));
     253  
     254  	quiet = err & PAM_DATA_SILENT;	/* should we log something? */
     255  	err &= PAM_DATA_REPLACE;	/* are we just replacing data? */
     256  	failure = (struct _pam_failed_auth *) fl;
     257  
     258  	if (failure != NULL) {
     259  
     260  		if (!quiet && !err) {	/* under advisement from Sun,may go away */
     261  
     262  			/* log the number of authentication failures */
     263  			if (failure->count > 1) {
     264  				(void) pam_get_item(pamh, PAM_SERVICE,
     265  						    &service);
     266  				(void) pam_get_item(pamh, PAM_RUSER,
     267  						    &ruser);
     268  				(void) pam_get_item(pamh, PAM_RHOST,
     269  						    &rhost);
     270  				(void) pam_get_item(pamh, PAM_TTY,
     271  						    &tty);
     272  				pam_syslog(pamh, LOG_NOTICE,
     273  				         "%d more authentication failure%s; "
     274  				         "logname=%s uid=%d euid=%d "
     275  				         "tty=%s ruser=%s rhost=%s "
     276  				         "%s%s",
     277  				         failure->count - 1, failure->count == 2 ? "" : "s",
     278  				         failure->name, failure->uid, failure->euid,
     279  				         tty ? (const char *)tty : "", ruser ? (const char *)ruser : "",
     280  				         rhost ? (const char *)rhost : "",
     281  				         (failure->user && failure->user[0] != '\0')
     282  				          ? " user=" : "", failure->user
     283  				);
     284  
     285  				if (failure->count > UNIX_MAX_RETRIES) {
     286  					pam_syslog(pamh, LOG_NOTICE,
     287  						 "service(%s) ignoring max retries; %d > %d",
     288  						 service == NULL ? "**unknown**" : (const char *)service,
     289  						 failure->count,
     290  						 UNIX_MAX_RETRIES);
     291  				}
     292  			}
     293  		}
     294  		_pam_delete(failure->user);	/* tidy up */
     295  		_pam_delete(failure->name);	/* tidy up */
     296  		free(failure);
     297  	}
     298  }
     299  
     300  /*
     301   * _unix_getpwnam() searches only /etc/passwd and NIS to find user information
     302   */
     303  static void _unix_cleanup(pam_handle_t *pamh UNUSED, void *data, int error_status UNUSED)
     304  {
     305  	free(data);
     306  }
     307  
     308  int _unix_getpwnam(pam_handle_t *pamh, const char *name,
     309  		   int files, int nis, struct passwd **ret)
     310  {
     311  	FILE *passwd;
     312  	char buf[16384];
     313  	int matched = 0, buflen;
     314  	char *slogin, *spasswd, *suid, *sgid, *sgecos, *shome, *sshell, *p;
     315  
     316  	memset(buf, 0, sizeof(buf));
     317  
     318  	if (!matched && files) {
     319  		int userlen = strlen(name);
     320  		passwd = fopen("/etc/passwd", "r");
     321  		if (passwd != NULL) {
     322  			while (fgets(buf, sizeof(buf), passwd) != NULL) {
     323  				if ((buf[userlen] == ':') &&
     324  				    (strncmp(name, buf, userlen) == 0)) {
     325  					p = buf + strlen(buf) - 1;
     326  					while (isspace(*p) && (p >= buf)) {
     327  						*p-- = '\0';
     328  					}
     329  					matched = 1;
     330  					break;
     331  				}
     332  			}
     333  			fclose(passwd);
     334  		}
     335  	}
     336  
     337  #if defined(HAVE_YP_GET_DEFAULT_DOMAIN) && defined (HAVE_YP_BIND) && defined (HAVE_YP_MATCH) && defined (HAVE_YP_UNBIND)
     338  	if (!matched && nis) {
     339  		char *userinfo = NULL, *domain = NULL;
     340  		int len = 0, i;
     341  		len = yp_get_default_domain(&domain);
     342  		if (len == YPERR_SUCCESS) {
     343  			len = yp_bind(domain);
     344  		}
     345  		if (len == YPERR_SUCCESS) {
     346  			i = yp_match(domain, "passwd.byname", name,
     347  				     strlen(name), &userinfo, &len);
     348  			yp_unbind(domain);
     349  			if ((i == YPERR_SUCCESS) && ((size_t)len < sizeof(buf))) {
     350  				strncpy(buf, userinfo, sizeof(buf) - 1);
     351  				buf[sizeof(buf) - 1] = '\0';
     352  				matched = 1;
     353  			}
     354  		}
     355  	}
     356  #else
     357  	/* we don't have NIS support, make compiler happy. */
     358  	(void) nis;
     359  #endif
     360  
     361  	if (matched && (ret != NULL)) {
     362  		*ret = NULL;
     363  
     364  		slogin = buf;
     365  
     366  		spasswd = strchr(slogin, ':');
     367  		if (spasswd == NULL) {
     368  			return matched;
     369  		}
     370  		*spasswd++ = '\0';
     371  
     372  		suid = strchr(spasswd, ':');
     373  		if (suid == NULL) {
     374  			return matched;
     375  		}
     376  		*suid++ = '\0';
     377  
     378  		sgid = strchr(suid, ':');
     379  		if (sgid == NULL) {
     380  			return matched;
     381  		}
     382  		*sgid++ = '\0';
     383  
     384  		sgecos = strchr(sgid, ':');
     385  		if (sgecos == NULL) {
     386  			return matched;
     387  		}
     388  		*sgecos++ = '\0';
     389  
     390  		shome = strchr(sgecos, ':');
     391  		if (shome == NULL) {
     392  			return matched;
     393  		}
     394  		*shome++ = '\0';
     395  
     396  		sshell = strchr(shome, ':');
     397  		if (sshell == NULL) {
     398  			return matched;
     399  		}
     400  		*sshell++ = '\0';
     401  
     402  		buflen = sizeof(struct passwd) +
     403  			 strlen(slogin) + 1 +
     404  			 strlen(spasswd) + 1 +
     405  			 strlen(sgecos) + 1 +
     406  			 strlen(shome) + 1 +
     407  			 strlen(sshell) + 1;
     408  		*ret = malloc(buflen);
     409  		if (*ret == NULL) {
     410  			return matched;
     411  		}
     412  		memset(*ret, '\0', buflen);
     413  
     414  		(*ret)->pw_uid = strtol(suid, &p, 10);
     415  		if ((strlen(suid) == 0) || (*p != '\0')) {
     416  			free(*ret);
     417  			*ret = NULL;
     418  			return matched;
     419  		}
     420  
     421  		(*ret)->pw_gid = strtol(sgid, &p, 10);
     422  		if ((strlen(sgid) == 0) || (*p != '\0')) {
     423  			free(*ret);
     424  			*ret = NULL;
     425  			return matched;
     426  		}
     427  
     428  		p = ((char*)(*ret)) + sizeof(struct passwd);
     429  		(*ret)->pw_name = strcpy(p, slogin);
     430  		p += strlen(p) + 1;
     431  		(*ret)->pw_passwd = strcpy(p, spasswd);
     432  		p += strlen(p) + 1;
     433  		(*ret)->pw_gecos = strcpy(p, sgecos);
     434  		p += strlen(p) + 1;
     435  		(*ret)->pw_dir = strcpy(p, shome);
     436  		p += strlen(p) + 1;
     437  		(*ret)->pw_shell = strcpy(p, sshell);
     438  
     439  		snprintf(buf, sizeof(buf), "_pam_unix_getpwnam_%s", name);
     440  
     441  		if (pam_set_data(pamh, buf,
     442  				 *ret, _unix_cleanup) != PAM_SUCCESS) {
     443  			free(*ret);
     444  			*ret = NULL;
     445  		}
     446  	}
     447  
     448  	return matched;
     449  }
     450  
     451  /*
     452   * _unix_comsefromsource() is a quick check to see if information about a given
     453   * user comes from a particular source (just files and nis for now)
     454   *
     455   */
     456  int _unix_comesfromsource(pam_handle_t *pamh,
     457  			  const char *name, int files, int nis)
     458  {
     459  	return _unix_getpwnam(pamh, name, files, nis, NULL);
     460  }
     461  
     462  /*
     463   * verify the password of a user
     464   */
     465  
     466  #include <sys/types.h>
     467  #include <sys/wait.h>
     468  
     469  static int _unix_run_helper_binary(pam_handle_t *pamh, const char *passwd,
     470  				   unsigned long long ctrl, const char *user)
     471  {
     472      int retval, child, fds[2];
     473      struct sigaction newsa, oldsa;
     474  
     475      D(("called."));
     476      /* create a pipe for the password */
     477      if (pipe(fds) != 0) {
     478  	D(("could not make pipe"));
     479  	return PAM_AUTH_ERR;
     480      }
     481  
     482      if (off(UNIX_NOREAP, ctrl)) {
     483  	/*
     484  	 * This code arranges that the demise of the child does not cause
     485  	 * the application to receive a signal it is not expecting - which
     486  	 * may kill the application or worse.
     487  	 *
     488  	 * The "noreap" module argument is provided so that the admin can
     489  	 * override this behavior.
     490  	 */
     491          memset(&newsa, '\0', sizeof(newsa));
     492  	newsa.sa_handler = SIG_DFL;
     493  	sigaction(SIGCHLD, &newsa, &oldsa);
     494      }
     495  
     496      /* fork */
     497      child = fork();
     498      if (child == 0) {
     499  	static char *envp[] = { NULL };
     500  	const char *args[] = { NULL, NULL, NULL, NULL };
     501  
     502  	/* XXX - should really tidy up PAM here too */
     503  
     504  	/* reopen stdin as pipe */
     505  	if (dup2(fds[0], STDIN_FILENO) != STDIN_FILENO) {
     506  		pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", "stdin");
     507  		_exit(PAM_AUTHINFO_UNAVAIL);
     508  	}
     509  
     510  	if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_IGNORE_FD,
     511  					    PAM_MODUTIL_PIPE_FD,
     512  					    PAM_MODUTIL_PIPE_FD) < 0) {
     513  		_exit(PAM_AUTHINFO_UNAVAIL);
     514  	}
     515  
     516  	if (geteuid() == 0) {
     517            /* must set the real uid to 0 so the helper will not error
     518  	     out if pam is called from setuid binary (su, sudo...) */
     519  	  if (setuid(0) == -1) {
     520               D(("setuid failed"));
     521  	     _exit(PAM_AUTHINFO_UNAVAIL);
     522            }
     523  	}
     524  
     525  	/* exec binary helper */
     526  	args[0] = CHKPWD_HELPER;
     527  	args[1] = user;
     528  	if (off(UNIX__NONULL, ctrl)) {	/* this means we've succeeded */
     529  	  args[2]="nullok";
     530  	} else {
     531  	  args[2]="nonull";
     532  	}
     533  
     534  	DIAG_PUSH_IGNORE_CAST_QUAL;
     535  	execve(CHKPWD_HELPER, (char *const *) args, envp);
     536  	DIAG_POP_IGNORE_CAST_QUAL;
     537  
     538  	/* should not get here: exit with error */
     539  	D(("helper binary is not available"));
     540  	_exit(PAM_AUTHINFO_UNAVAIL);
     541      } else if (child > 0) {
     542  	/* wait for child */
     543  	/* if the stored password is NULL */
     544          int rc=0;
     545  	if (passwd != NULL) {            /* send the password to the child */
     546  	    int len = strlen(passwd);
     547  
     548  	    if (len > PAM_MAX_RESP_SIZE)
     549  	      len = PAM_MAX_RESP_SIZE;
     550  	    if (write(fds[1], passwd, len) == -1 ||
     551  	        write(fds[1], "", 1) == -1) {
     552  	      pam_syslog (pamh, LOG_ERR, "Cannot send password to helper: %m");
     553  	      retval = PAM_AUTH_ERR;
     554  	    }
     555  	    passwd = NULL;
     556  	} else {                         /* blank password */
     557  	    if (write(fds[1], "", 1) == -1) {
     558  	      pam_syslog (pamh, LOG_ERR, "Cannot send password to helper: %m");
     559  	      retval = PAM_AUTH_ERR;
     560  	    }
     561  	}
     562  	close(fds[0]);       /* close here to avoid possible SIGPIPE above */
     563  	close(fds[1]);
     564  	/* wait for helper to complete: */
     565  	while ((rc=waitpid(child, &retval, 0)) < 0 && errno == EINTR);
     566  	if (rc<0) {
     567  	  pam_syslog(pamh, LOG_ERR, "unix_chkpwd waitpid returned %d: %m", rc);
     568  	  retval = PAM_AUTH_ERR;
     569  	} else if (!WIFEXITED(retval)) {
     570  	  pam_syslog(pamh, LOG_ERR, "unix_chkpwd abnormal exit: %d", retval);
     571  	  retval = PAM_AUTH_ERR;
     572  	} else {
     573  	  retval = WEXITSTATUS(retval);
     574  	}
     575      } else {
     576  	D(("fork failed"));
     577  	close(fds[0]);
     578  	close(fds[1]);
     579  	retval = PAM_AUTH_ERR;
     580      }
     581  
     582      if (off(UNIX_NOREAP, ctrl)) {
     583          sigaction(SIGCHLD, &oldsa, NULL);   /* restore old signal handler */
     584      }
     585  
     586      D(("returning %d", retval));
     587      return retval;
     588  }
     589  
     590  /*
     591   * _unix_blankpasswd() is a quick check for a blank password
     592   *
     593   * returns TRUE if user does not have a password
     594   * - to avoid prompting for one in such cases (CG)
     595   */
     596  
     597  int
     598  _unix_blankpasswd (pam_handle_t *pamh, unsigned long long ctrl, const char *name)
     599  {
     600  	struct passwd *pwd = NULL;
     601  	char *salt = NULL;
     602  	int daysleft;
     603  	int retval;
     604  	int blank = 0;
     605  	int execloop;
     606  	int nonexistent_check = 1;
     607  
     608  	D(("called"));
     609  
     610  	/*
     611  	 * This function does not have to be too smart if something goes
     612  	 * wrong, return FALSE and let this case to be treated somewhere
     613  	 * else (CG)
     614  	 */
     615  
     616  	if (on(UNIX_NULLRESETOK, ctrl)) {
     617  	    retval = _unix_verify_user(pamh, ctrl, name, &daysleft);
     618  	    if (retval == PAM_NEW_AUTHTOK_REQD) {
     619  	        /* password reset is enforced, allow authentication with empty password */
     620  	        pam_syslog(pamh, LOG_DEBUG, "user [%s] has expired blank password, enabling nullok", name);
     621  	        set(UNIX__NULLOK, ctrl);
     622  	    }
     623  	}
     624  
     625  	if (on(UNIX__NONULL, ctrl))
     626  		return 0;	/* will fail but don't let on yet */
     627  
     628  	/* UNIX passwords area */
     629  
     630  	/*
     631  	 * Execute this loop twice: one checking the password hash of an existing
     632  	 * user and another one for a non-existing user. This way the runtimes
     633  	 * are equal, making it more difficult to differentiate existing from
     634  	 * non-existing users.
     635  	 */
     636  	for (execloop = 0; execloop < 2; ++execloop) {
     637  		retval = get_pwd_hash(pamh, name, &pwd, &salt);
     638  
     639  		if (retval == PAM_UNIX_RUN_HELPER) {
     640  			if (_unix_run_helper_binary(pamh, NULL, ctrl, name) == PAM_SUCCESS)
     641  				blank = nonexistent_check;
     642  		} else if (retval == PAM_USER_UNKNOWN) {
     643  			name = "root";
     644  			nonexistent_check = 0;
     645  			continue;
     646  		} else if (salt != NULL) {
     647  			if (strlen(salt) == 0)
     648  				blank = nonexistent_check;
     649  		}
     650  		name = "pam_unix_non_existent:";
     651  		/* non-existent user check will not affect the blank value */
     652  	}
     653  
     654  	/* tidy up */
     655  	if (salt)
     656  		_pam_delete(salt);
     657  
     658  	return blank;
     659  }
     660  
     661  int _unix_verify_password(pam_handle_t * pamh, const char *name
     662  			  ,const char *p, unsigned long long ctrl)
     663  {
     664  	struct passwd *pwd = NULL;
     665  	char *salt = NULL;
     666  	char *data_name;
     667  	char pw[PAM_MAX_RESP_SIZE + 1];
     668  	int retval;
     669  
     670  
     671  	D(("called"));
     672  
     673  #ifdef HAVE_PAM_FAIL_DELAY
     674  	if (off(UNIX_NODELAY, ctrl)) {
     675  		D(("setting delay"));
     676  		(void) pam_fail_delay(pamh, 2000000);	/* 2 sec delay for on failure */
     677  	}
     678  #endif
     679  
     680  	/* locate the entry for this user */
     681  
     682  	D(("locating user's record"));
     683  
     684  	retval = get_pwd_hash(pamh, name, &pwd, &salt);
     685  
     686  	data_name = (char *) malloc(sizeof(FAIL_PREFIX) + strlen(name));
     687  	if (data_name == NULL) {
     688  		pam_syslog(pamh, LOG_CRIT, "no memory for data-name");
     689  	} else {
     690  		strcpy(data_name, FAIL_PREFIX);
     691  		strcpy(data_name + sizeof(FAIL_PREFIX) - 1, name);
     692  	}
     693  
     694  	if (p != NULL && strlen(p) > PAM_MAX_RESP_SIZE) {
     695  		memset(pw, 0, sizeof(pw));
     696  		p = strncpy(pw, p, sizeof(pw) - 1);
     697  	}
     698  
     699  	if (retval != PAM_SUCCESS) {
     700  		if (retval == PAM_UNIX_RUN_HELPER) {
     701  			D(("running helper binary"));
     702  			retval = _unix_run_helper_binary(pamh, p, ctrl, name);
     703  		} else {
     704  			D(("user's record unavailable"));
     705  			p = NULL;
     706  			if (on(UNIX_AUDIT, ctrl)) {
     707  				/* this might be a typo and the user has given a password
     708  				   instead of a username. Careful with this. */
     709  				pam_syslog(pamh, LOG_NOTICE,
     710  				         "check pass; user (%s) unknown", name);
     711  			} else {
     712  				name = NULL;
     713  				if (on(UNIX_DEBUG, ctrl) || pwd == NULL) {
     714  				    pam_syslog(pamh, LOG_NOTICE,
     715  				            "check pass; user unknown");
     716  				} else {
     717  				    /* don't log failure as another pam module can succeed */
     718  				    goto cleanup;
     719  				}
     720  			}
     721  		}
     722  	} else {
     723  		retval = verify_pwd_hash(pamh, p, salt, off(UNIX__NONULL, ctrl));
     724  	}
     725  
     726  	if (retval == PAM_SUCCESS) {
     727  		if (data_name)	/* reset failures */
     728  			pam_set_data(pamh, data_name, NULL, _cleanup_failures);
     729  	} else {
     730  		if (data_name != NULL) {
     731  			struct _pam_failed_auth *new = NULL;
     732  			const struct _pam_failed_auth *old = NULL;
     733  
     734  			/* get a failure recorder */
     735  
     736  			new = (struct _pam_failed_auth *)
     737  			    malloc(sizeof(struct _pam_failed_auth));
     738  
     739  			if (new != NULL) {
     740  
     741  			    const char *login_name;
     742  			    const void *void_old;
     743  
     744  
     745  			    login_name = pam_modutil_getlogin(pamh);
     746  			    if (login_name == NULL) {
     747  				login_name = "";
     748  			    }
     749  
     750  			        new->user = strdup(name ? name : "");
     751  				new->uid = getuid();
     752  				new->euid = geteuid();
     753  				new->name = strdup(login_name);
     754  
     755  				/* any previous failures for this user ? */
     756  				if (pam_get_data(pamh, data_name, &void_old)
     757  				    == PAM_SUCCESS)
     758  				        old = void_old;
     759  				else
     760  				        old = NULL;
     761  
     762  				if (old != NULL) {
     763  					new->count = old->count + 1;
     764  					if (new->count >= UNIX_MAX_RETRIES) {
     765  						retval = PAM_MAXTRIES;
     766  					}
     767  				} else {
     768  					const void *service=NULL;
     769  					const void *ruser=NULL;
     770  					const void *rhost=NULL;
     771  					const void *tty=NULL;
     772  
     773  					(void) pam_get_item(pamh, PAM_SERVICE,
     774  							    &service);
     775  					(void) pam_get_item(pamh, PAM_RUSER,
     776  							    &ruser);
     777  					(void) pam_get_item(pamh, PAM_RHOST,
     778  							    &rhost);
     779  					(void) pam_get_item(pamh, PAM_TTY,
     780  							    &tty);
     781  
     782  					pam_syslog(pamh, LOG_NOTICE,
     783  					         "authentication failure; "
     784  					         "logname=%s uid=%d euid=%d "
     785  					         "tty=%s ruser=%s rhost=%s "
     786  					         "%s%s",
     787  					         new->name, new->uid, new->euid,
     788  					         tty ? (const char *)tty : "",
     789  					         ruser ? (const char *)ruser : "",
     790  					         rhost ? (const char *)rhost : "",
     791  					         (new->user && new->user[0] != '\0')
     792  					          ? " user=" : "",
     793  					         new->user
     794  					);
     795  					new->count = 1;
     796  				}
     797  
     798  				pam_set_data(pamh, data_name, new, _cleanup_failures);
     799  
     800  			} else {
     801  				pam_syslog(pamh, LOG_CRIT,
     802  				         "no memory for failure recorder");
     803  			}
     804  		}
     805  	}
     806  
     807  cleanup:
     808  	pam_overwrite_array(pw); /* clear memory of the password */
     809  	if (data_name)
     810  		_pam_delete(data_name);
     811  	if (salt)
     812  		_pam_delete(salt);
     813  
     814  	D(("done [%d].", retval));
     815  
     816  	return retval;
     817  }
     818  
     819  int
     820  _unix_verify_user(pam_handle_t *pamh,
     821                    unsigned long long ctrl,
     822                    const char *name,
     823                    int *daysleft)
     824  {
     825      int retval;
     826      struct spwd *spent;
     827      struct passwd *pwent;
     828  
     829      retval = get_account_info(pamh, name, &pwent, &spent);
     830      if (retval == PAM_USER_UNKNOWN) {
     831          pam_syslog(pamh, LOG_ERR,
     832               "could not identify user (from getpwnam(%s))",
     833               name);
     834          return retval;
     835      }
     836  
     837      if (retval == PAM_SUCCESS && spent == NULL)
     838          return PAM_SUCCESS;
     839  
     840      if (retval == PAM_UNIX_RUN_HELPER) {
     841          retval = _unix_run_verify_binary(pamh, ctrl, name, daysleft);
     842          if (retval == PAM_AUTHINFO_UNAVAIL &&
     843              on(UNIX_BROKEN_SHADOW, ctrl))
     844              return PAM_SUCCESS;
     845      } else if (retval != PAM_SUCCESS) {
     846          if (on(UNIX_BROKEN_SHADOW,ctrl))
     847              return PAM_SUCCESS;
     848          else
     849              return retval;
     850      } else
     851          retval = check_shadow_expiry(pamh, spent, daysleft);
     852  
     853      return retval;
     854  }
     855  
     856  /* ****************************************************************** *
     857   * Copyright (c) Jan Rękorajski 1999.
     858   * Copyright (c) Andrew G. Morgan 1996-8.
     859   * Copyright (c) Alex O. Yuriev, 1996.
     860   * Copyright (c) Cristian Gafton 1996.
     861   * Copyright (c) Red Hat, Inc. 2007.
     862   *
     863   * Redistribution and use in source and binary forms, with or without
     864   * modification, are permitted provided that the following conditions
     865   * are met:
     866   * 1. Redistributions of source code must retain the above copyright
     867   *    notice, and the entire permission notice in its entirety,
     868   *    including the disclaimer of warranties.
     869   * 2. Redistributions in binary form must reproduce the above copyright
     870   *    notice, this list of conditions and the following disclaimer in the
     871   *    documentation and/or other materials provided with the distribution.
     872   * 3. The name of the author may not be used to endorse or promote
     873   *    products derived from this software without specific prior
     874   *    written permission.
     875   *
     876   * ALTERNATIVELY, this product may be distributed under the terms of
     877   * the GNU Public License, in which case the provisions of the GPL are
     878   * required INSTEAD OF the above restrictions.  (This clause is
     879   * necessary due to a potential bad interaction between the GPL and
     880   * the restrictions contained in a BSD-style copyright.)
     881   *
     882   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
     883   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     884   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     885   * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
     886   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     887   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     888   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     889   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
     890   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     891   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
     892   * OF THE POSSIBILITY OF SUCH DAMAGE.
     893   */