(root)/
Linux-PAM-1.5.3/
modules/
pam_lastlog/
pam_lastlog.c
       1  /*
       2   * pam_lastlog module
       3   *
       4   * Written by Andrew Morgan <morgan@linux.kernel.org> 1996/3/11
       5   *
       6   * This module does the necessary work to display the last login
       7   * time+date for this user, it then updates this entry for the
       8   * present (login) service.
       9   */
      10  
      11  #include "config.h"
      12  
      13  #include <fcntl.h>
      14  #include <time.h>
      15  #include <errno.h>
      16  #ifdef HAVE_UTMP_H
      17  # include <utmp.h>
      18  #else
      19  # include <lastlog.h>
      20  #endif
      21  #include <pwd.h>
      22  #include <stdlib.h>
      23  #include <ctype.h>
      24  #include <stdarg.h>
      25  #include <stdio.h>
      26  #include <string.h>
      27  #include <sys/types.h>
      28  #include <sys/time.h>
      29  #include <sys/resource.h>
      30  #include <syslog.h>
      31  #include <unistd.h>
      32  
      33  #if defined(hpux) || defined(sunos) || defined(solaris)
      34  # ifndef _PATH_LASTLOG
      35  #  define _PATH_LASTLOG "/usr/adm/lastlog"
      36  # endif /* _PATH_LASTLOG */
      37  # ifndef UT_HOSTSIZE
      38  #  define UT_HOSTSIZE 16
      39  # endif /* UT_HOSTSIZE */
      40  # ifndef UT_LINESIZE
      41  #  define UT_LINESIZE 12
      42  # endif /* UT_LINESIZE */
      43  #endif
      44  #if defined(hpux)
      45  struct lastlog {
      46      time_t  ll_time;
      47      char    ll_line[UT_LINESIZE];
      48      char    ll_host[UT_HOSTSIZE];            /* same as in utmp */
      49  };
      50  #endif /* hpux */
      51  
      52  #ifndef _PATH_BTMP
      53  # define _PATH_BTMP "/var/log/btmp"
      54  #endif
      55  
      56  #ifndef PATH_LOGIN_DEFS
      57  # define PATH_LOGIN_DEFS "/etc/login.defs"
      58  #endif
      59  
      60  #define DEFAULT_HOST     ""  /* "[no.where]" */
      61  #define DEFAULT_TERM     ""  /* "tt???" */
      62  
      63  #define DEFAULT_INACTIVE_DAYS 90
      64  #define MAX_INACTIVE_DAYS 100000
      65  #define LOCK_RETRIES           3  /* number of file lock retries */
      66  #define LOCK_RETRY_DELAY       1  /* seconds to wait between lock attempts */
      67  
      68  #include <security/pam_modules.h>
      69  #include <security/_pam_macros.h>
      70  #include <security/pam_modutil.h>
      71  #include <security/pam_ext.h>
      72  #include "pam_inline.h"
      73  
      74  /* argument parsing */
      75  
      76  #define LASTLOG_DATE          01  /* display the date of the last login */
      77  #define LASTLOG_HOST          02  /* display the last host used (if set) */
      78  #define LASTLOG_LINE          04  /* display the last terminal used */
      79  #define LASTLOG_NEVER        010  /* display a welcome message for first login */
      80  #define LASTLOG_DEBUG        020  /* send info to syslog(3) */
      81  #define LASTLOG_QUIET        040  /* keep quiet about things */
      82  #define LASTLOG_WTMP        0100  /* log to wtmp as well as lastlog */
      83  #define LASTLOG_BTMP        0200  /* display failed login info from btmp */
      84  #define LASTLOG_UPDATE      0400  /* update the lastlog and wtmp files (default) */
      85  #define LASTLOG_UNLIMITED  01000  /* unlimited file size (ignore 'fsize' limit) */
      86  
      87  static int
      88  _pam_auth_parse(pam_handle_t *pamh, int flags, int argc, const char **argv,
      89      time_t *inactive)
      90  {
      91      int ctrl = 0;
      92  
      93      *inactive = DEFAULT_INACTIVE_DAYS;
      94  
      95      /* does the application require quiet? */
      96      if (flags & PAM_SILENT) {
      97  	ctrl |= LASTLOG_QUIET;
      98      }
      99  
     100      /* step through arguments */
     101      for (; argc-- > 0; ++argv) {
     102          const char *str;
     103          char *ep = NULL;
     104          long l;
     105  
     106  	if (!strcmp(*argv,"debug")) {
     107  	    ctrl |= LASTLOG_DEBUG;
     108  	} else if (!strcmp(*argv,"silent")) {
     109  	    ctrl |= LASTLOG_QUIET;
     110  	} else if ((str = pam_str_skip_prefix(*argv, "inactive=")) != NULL) {
     111              l = strtol(str, &ep, 10);
     112              if (ep != str && l > 0 && l < MAX_INACTIVE_DAYS)
     113                  *inactive = l;
     114              else {
     115                  pam_syslog(pamh, LOG_ERR, "bad option value: %s", *argv);
     116              }
     117  	} else {
     118  	    pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
     119  	}
     120      }
     121  
     122      D(("ctrl = %o", ctrl));
     123      return ctrl;
     124  }
     125  
     126  static int
     127  _pam_session_parse(pam_handle_t *pamh, int flags, int argc, const char **argv)
     128  {
     129      int ctrl=(LASTLOG_DATE|LASTLOG_HOST|LASTLOG_LINE|LASTLOG_WTMP|LASTLOG_UPDATE);
     130  
     131      /* step through arguments */
     132      for (; argc-- > 0; ++argv) {
     133  
     134  	/* generic options */
     135  
     136  	if (!strcmp(*argv,"debug")) {
     137  	    ctrl |= LASTLOG_DEBUG;
     138  	} else if (!strcmp(*argv,"nodate")) {
     139  	    ctrl &= ~LASTLOG_DATE;
     140  	} else if (!strcmp(*argv,"noterm")) {
     141  	    ctrl &= ~LASTLOG_LINE;
     142  	} else if (!strcmp(*argv,"nohost")) {
     143  	    ctrl &= ~LASTLOG_HOST;
     144  	} else if (!strcmp(*argv,"silent")) {
     145  	    ctrl |= LASTLOG_QUIET;
     146  	} else if (!strcmp(*argv,"never")) {
     147  	    ctrl |= LASTLOG_NEVER;
     148  	} else if (!strcmp(*argv,"nowtmp")) {
     149  	    ctrl &= ~LASTLOG_WTMP;
     150  	} else if (!strcmp(*argv,"noupdate")) {
     151  	    ctrl &= ~(LASTLOG_WTMP|LASTLOG_UPDATE);
     152  	} else if (!strcmp(*argv,"showfailed")) {
     153  	    ctrl |= LASTLOG_BTMP;
     154  	} else if (!strcmp(*argv,"unlimited")) {
     155  	    ctrl |= LASTLOG_UNLIMITED;
     156  	} else {
     157  	    pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
     158  	}
     159      }
     160  
     161      /* does the application require quiet? */
     162      if (flags & PAM_SILENT) {
     163  	ctrl |= LASTLOG_QUIET;
     164  	ctrl &= ~LASTLOG_BTMP;
     165      }
     166  
     167      D(("ctrl = %o", ctrl));
     168      return ctrl;
     169  }
     170  
     171  static const char *
     172  get_tty(pam_handle_t *pamh)
     173  {
     174      const void *void_terminal_line = NULL;
     175      const char *terminal_line;
     176      const char *str;
     177  
     178      if (pam_get_item(pamh, PAM_TTY, &void_terminal_line) != PAM_SUCCESS
     179  	|| void_terminal_line == NULL) {
     180  	terminal_line = DEFAULT_TERM;
     181      } else {
     182  	terminal_line = void_terminal_line;
     183      }
     184  
     185      /* strip leading "/dev/" from tty. */
     186      str = pam_str_skip_prefix(terminal_line, "/dev/");
     187      if (str != NULL)
     188  	terminal_line = str;
     189  
     190      D(("terminal = %s", terminal_line));
     191      return terminal_line;
     192  }
     193  
     194  #define MAX_UID_VALUE 0xFFFFFFFFUL
     195  
     196  static uid_t
     197  get_lastlog_uid_max(pam_handle_t *pamh)
     198  {
     199      uid_t uid_max = MAX_UID_VALUE;
     200      unsigned long ul;
     201      char *s, *ep;
     202  
     203      s = pam_modutil_search_key(pamh, PATH_LOGIN_DEFS, "LASTLOG_UID_MAX");
     204      if (s == NULL)
     205  	return uid_max;
     206  
     207      ep = s + strlen(s);
     208      while (ep > s && isspace(*(--ep))) {
     209  	*ep = '\0';
     210      }
     211      errno = 0;
     212      ul = strtoul(s, &ep, 10);
     213      if (!(ul >= MAX_UID_VALUE
     214  	|| (uid_t)ul >= MAX_UID_VALUE
     215  	|| (errno != 0 && ul == 0)
     216  	|| s == ep
     217  	|| *ep != '\0')) {
     218  	uid_max = (uid_t)ul;
     219      }
     220      free(s);
     221  
     222      return uid_max;
     223  }
     224  
     225  static int
     226  last_login_open(pam_handle_t *pamh, int announce, uid_t uid)
     227  {
     228      int last_fd;
     229  
     230      /* obtain the last login date and all the relevant info */
     231      last_fd = open(_PATH_LASTLOG, announce&LASTLOG_UPDATE ? O_RDWR : O_RDONLY);
     232      if (last_fd < 0) {
     233          if (errno == ENOENT && (announce & LASTLOG_UPDATE)) {
     234  	     last_fd = open(_PATH_LASTLOG, O_RDWR|O_CREAT,
     235                              S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
     236               if (last_fd < 0) {
     237  	          pam_syslog(pamh, LOG_ERR,
     238                               "unable to create %s: %m", _PATH_LASTLOG);
     239  		  D(("unable to create %s file", _PATH_LASTLOG));
     240  		  return -1;
     241  	     }
     242  	     pam_syslog(pamh, LOG_NOTICE,
     243  			"file %s created", _PATH_LASTLOG);
     244  	     D(("file %s created", _PATH_LASTLOG));
     245  	} else {
     246  	  pam_syslog(pamh, LOG_ERR, "unable to open %s: %m", _PATH_LASTLOG);
     247  	  D(("unable to open %s file", _PATH_LASTLOG));
     248  	  return -1;
     249  	}
     250      }
     251  
     252      if (lseek(last_fd, sizeof(struct lastlog) * (off_t) uid, SEEK_SET) < 0) {
     253  	pam_syslog(pamh, LOG_ERR, "failed to lseek %s: %m", _PATH_LASTLOG);
     254  	D(("unable to lseek %s file", _PATH_LASTLOG));
     255          close(last_fd);
     256  	return -1;
     257      }
     258  
     259      return last_fd;
     260  }
     261  
     262  
     263  static int
     264  last_login_read(pam_handle_t *pamh, int announce, int last_fd, uid_t uid, time_t *lltime)
     265  {
     266      struct flock last_lock;
     267      struct lastlog last_login;
     268      int lock_retries = LOCK_RETRIES;
     269      int retval = PAM_SUCCESS;
     270      char the_time[256];
     271      char *date = NULL;
     272      char *host = NULL;
     273      char *line = NULL;
     274  
     275      memset(&last_lock, 0, sizeof(last_lock));
     276      last_lock.l_type = F_RDLCK;
     277      last_lock.l_whence = SEEK_SET;
     278      last_lock.l_start = sizeof(last_login) * (off_t) uid;
     279      last_lock.l_len = sizeof(last_login);
     280  
     281      while (fcntl(last_fd, F_SETLK, &last_lock) < 0) {
     282          if (0 == --lock_retries) {
     283              /* read lock failed, proceed anyway to avoid possible DoS */
     284              D(("locking %s failed", _PATH_LASTLOG));
     285              pam_syslog(pamh, LOG_INFO,
     286                         "file %s is locked/read, proceeding anyway",
     287                         _PATH_LASTLOG);
     288              break;
     289          }
     290          D(("locking %s failed..(waiting a little)", _PATH_LASTLOG));
     291          pam_syslog(pamh, LOG_INFO,
     292                     "file %s is locked/read, retrying", _PATH_LASTLOG);
     293          sleep(LOCK_RETRY_DELAY);
     294      }
     295  
     296      if (pam_modutil_read(last_fd, (char *) &last_login,
     297  			 sizeof(last_login)) != sizeof(last_login)) {
     298          memset(&last_login, 0, sizeof(last_login));
     299      }
     300  
     301      last_lock.l_type = F_UNLCK;
     302      (void) fcntl(last_fd, F_SETLK, &last_lock);        /* unlock */
     303  
     304      *lltime = last_login.ll_time;
     305      if (!last_login.ll_time) {
     306          if (announce & LASTLOG_DEBUG) {
     307  	    pam_syslog(pamh, LOG_DEBUG,
     308  		       "first login for user with uid %lu",
     309  		       (unsigned long int)uid);
     310  	}
     311      }
     312  
     313      if (!(announce & LASTLOG_QUIET)) {
     314  
     315  	if (last_login.ll_time) {
     316  
     317  	    /* we want the date? */
     318  	    if (announce & LASTLOG_DATE) {
     319  	        struct tm *tm, tm_buf;
     320  		time_t ll_time;
     321  
     322  		ll_time = last_login.ll_time;
     323  		if ((tm = localtime_r (&ll_time, &tm_buf)) != NULL) {
     324  			strftime (the_time, sizeof (the_time),
     325  		        /* TRANSLATORS: "strftime options for date of last login" */
     326  				  _(" %a %b %e %H:%M:%S %Z %Y"), tm);
     327  			date = the_time;
     328  		}
     329  	    }
     330  
     331  	    /* we want & have the host? */
     332  	    if ((announce & LASTLOG_HOST)
     333  		&& (last_login.ll_host[0] != '\0')) {
     334  		/* TRANSLATORS: " from <host>" */
     335  		if (asprintf(&host, _(" from %.*s"), UT_HOSTSIZE,
     336  			     last_login.ll_host) < 0) {
     337  		    pam_syslog(pamh, LOG_CRIT, "out of memory");
     338  		    retval = PAM_BUF_ERR;
     339  		    goto cleanup;
     340  		}
     341  	    }
     342  
     343  	    /* we want and have the terminal? */
     344  	    if ((announce & LASTLOG_LINE)
     345  		&& (last_login.ll_line[0] != '\0')) {
     346  		/* TRANSLATORS: " on <terminal>" */
     347  		if (asprintf(&line, _(" on %.*s"), UT_LINESIZE,
     348  			     last_login.ll_line) < 0) {
     349  		    pam_syslog(pamh, LOG_CRIT, "out of memory");
     350  		    retval = PAM_BUF_ERR;
     351  		    goto cleanup;
     352  		}
     353  	    }
     354  
     355  	    if (date != NULL || host != NULL || line != NULL)
     356  		    /* TRANSLATORS: "Last login: <date> from <host> on <terminal>" */
     357  		    retval = pam_info(pamh, _("Last login:%s%s%s"),
     358  			      date ? date : "",
     359  			      host ? host : "",
     360  			      line ? line : "");
     361  	} else if (announce & LASTLOG_NEVER) {
     362  		D(("this is the first time this user has logged in"));
     363  		retval = pam_info(pamh, "%s", _("Welcome to your new account!"));
     364  	}
     365      }
     366  
     367      /* cleanup */
     368   cleanup:
     369      pam_overwrite_object(&last_login);
     370      pam_overwrite_string(date);
     371      pam_overwrite_string(host);
     372      _pam_drop(host);
     373      pam_overwrite_string(line);
     374      _pam_drop(line);
     375  
     376      return retval;
     377  }
     378  
     379  static int
     380  last_login_write(pam_handle_t *pamh, int announce, int last_fd,
     381  		 uid_t uid, const char *user)
     382  {
     383      static struct rlimit no_limit = {
     384  	RLIM_INFINITY,
     385  	RLIM_INFINITY
     386      };
     387      struct rlimit old_limit;
     388      int setrlimit_res;
     389      struct flock last_lock;
     390      struct lastlog last_login;
     391      int lock_retries = LOCK_RETRIES;
     392      time_t ll_time;
     393      const void *void_remote_host = NULL;
     394      const char *remote_host;
     395      const char *terminal_line;
     396      int retval = PAM_SUCCESS;
     397  
     398      /* rewind */
     399      if (lseek(last_fd, sizeof(last_login) * (off_t) uid, SEEK_SET) < 0) {
     400  	pam_syslog(pamh, LOG_ERR, "failed to lseek %s: %m", _PATH_LASTLOG);
     401  	return PAM_SERVICE_ERR;
     402      }
     403  
     404      memset(&last_login, 0, sizeof(last_login));
     405  
     406      /* set this login date */
     407      D(("set the most recent login time"));
     408      (void) time(&ll_time);    /* set the time */
     409      last_login.ll_time = ll_time;
     410  
     411      /* set the remote host */
     412      if (pam_get_item(pamh, PAM_RHOST, &void_remote_host) != PAM_SUCCESS
     413  	|| void_remote_host == NULL) {
     414  	remote_host = DEFAULT_HOST;
     415      } else {
     416  	remote_host = void_remote_host;
     417      }
     418  
     419      /* copy to last_login */
     420      strncat(last_login.ll_host, remote_host, sizeof(last_login.ll_host)-1);
     421  
     422      /* set the terminal line */
     423      terminal_line = get_tty(pamh);
     424  
     425      /* copy to last_login */
     426      strncat(last_login.ll_line, terminal_line, sizeof(last_login.ll_line)-1);
     427      terminal_line = NULL;
     428  
     429      D(("locking lastlog file"));
     430  
     431      /* now we try to lock this file-record exclusively; non-blocking */
     432      memset(&last_lock, 0, sizeof(last_lock));
     433      last_lock.l_type = F_WRLCK;
     434      last_lock.l_whence = SEEK_SET;
     435      last_lock.l_start = sizeof(last_login) * (off_t) uid;
     436      last_lock.l_len = sizeof(last_login);
     437  
     438      while (fcntl(last_fd, F_SETLK, &last_lock) < 0) {
     439  	if (0 == --lock_retries) {
     440  	    D(("locking %s failed", _PATH_LASTLOG));
     441  	    pam_syslog(pamh, LOG_ERR,
     442  		       "file %s is locked/write", _PATH_LASTLOG);
     443  	    return PAM_SERVICE_ERR;
     444  	}
     445  	D(("locking %s failed..(waiting a little)", _PATH_LASTLOG));
     446  	pam_syslog(pamh, LOG_INFO,
     447  		   "file %s is locked/write, retrying", _PATH_LASTLOG);
     448  	sleep(LOCK_RETRY_DELAY);
     449      }
     450  
     451      /*
     452       * Failing to set the 'fsize' limit is not a fatal error. We try to write
     453       * lastlog anyway, under the risk of dying due to a SIGXFSZ.
     454       */
     455      D(("setting limit for 'fsize'"));
     456  
     457      if ((announce & LASTLOG_UNLIMITED) == 0) {    /* don't set to unlimited */
     458  	setrlimit_res = -1;
     459      } else if (getrlimit(RLIMIT_FSIZE, &old_limit) == 0) {
     460  	if (old_limit.rlim_cur == RLIM_INFINITY) {    /* already unlimited */
     461  	    setrlimit_res = -1;
     462  	} else {
     463  	    setrlimit_res = setrlimit(RLIMIT_FSIZE, &no_limit);
     464  	    if (setrlimit_res != 0)
     465  		pam_syslog(pamh, LOG_WARNING, "Could not set limit for 'fsize': %m");
     466  	}
     467      } else {
     468  	setrlimit_res = -1;
     469  	if (errno == EINVAL) {
     470  	    pam_syslog(pamh, LOG_INFO, "Limit for 'fsize' not supported: %m");
     471  	} else {
     472  	    pam_syslog(pamh, LOG_WARNING, "Could not get limit for 'fsize': %m");
     473  	}
     474      }
     475  
     476      D(("writing to the lastlog file"));
     477      if (pam_modutil_write (last_fd, (char *) &last_login,
     478  			   sizeof (last_login)) != sizeof(last_login)) {
     479  	pam_syslog(pamh, LOG_ERR, "failed to write %s: %m", _PATH_LASTLOG);
     480  	retval = PAM_SERVICE_ERR;
     481      }
     482  
     483      /*
     484       * Failing to restore the 'fsize' limit is a fatal error.
     485       */
     486      D(("restoring limit for 'fsize'"));
     487      if (setrlimit_res == 0) {
     488  	setrlimit_res = setrlimit(RLIMIT_FSIZE, &old_limit);
     489  	if (setrlimit_res != 0) {
     490  	    pam_syslog(pamh, LOG_ERR, "Could not restore limit for 'fsize': %m");
     491  	    retval = PAM_SERVICE_ERR;
     492  	}
     493      }
     494  
     495      last_lock.l_type = F_UNLCK;
     496      (void) fcntl(last_fd, F_SETLK, &last_lock);        /* unlock */
     497      D(("unlocked"));
     498  
     499      if (announce & LASTLOG_WTMP) {
     500  	/* write wtmp entry for user */
     501  	logwtmp(last_login.ll_line, user, remote_host);
     502      }
     503  
     504      /* cleanup */
     505      pam_overwrite_object(&last_login);
     506  
     507      return retval;
     508  }
     509  
     510  static int
     511  last_login_date(pam_handle_t *pamh, int announce, uid_t uid, const char *user, time_t *lltime)
     512  {
     513      int retval;
     514      int last_fd;
     515  
     516      if (uid > get_lastlog_uid_max(pamh)) {
     517  	return PAM_SUCCESS;
     518      }
     519  
     520      /* obtain the last login date and all the relevant info */
     521      last_fd = last_login_open(pamh, announce, uid);
     522      if (last_fd < 0) {
     523          return PAM_SERVICE_ERR;
     524      }
     525  
     526      retval = last_login_read(pamh, announce, last_fd, uid, lltime);
     527      if (retval != PAM_SUCCESS)
     528        {
     529  	close(last_fd);
     530  	D(("error while reading lastlog file"));
     531  	return retval;
     532        }
     533  
     534      if (announce & LASTLOG_UPDATE) {
     535  	retval = last_login_write(pamh, announce, last_fd, uid, user);
     536      }
     537  
     538      close(last_fd);
     539      D(("all done with last login"));
     540  
     541      return retval;
     542  }
     543  
     544  static int
     545  last_login_failed(pam_handle_t *pamh, int announce, const char *user, time_t lltime)
     546  {
     547      int retval;
     548      int fd;
     549      struct utmp ut;
     550      struct utmp utuser;
     551      int failed = 0;
     552      char the_time[256];
     553      char *date = NULL;
     554      char *host = NULL;
     555      char *line = NULL;
     556  
     557      if (strlen(user) > UT_NAMESIZE) {
     558  	pam_syslog(pamh, LOG_WARNING, "username too long, output might be inaccurate");
     559      }
     560  
     561      /* obtain the failed login attempt records from btmp */
     562      fd = open(_PATH_BTMP, O_RDONLY);
     563      if (fd < 0) {
     564          int save_errno = errno;
     565  	pam_syslog(pamh, LOG_ERR, "unable to open %s: %m", _PATH_BTMP);
     566  	D(("unable to open %s file", _PATH_BTMP));
     567          if (save_errno == ENOENT)
     568  	  return PAM_SUCCESS;
     569  	else
     570  	  return PAM_SERVICE_ERR;
     571      }
     572  
     573      while ((retval=pam_modutil_read(fd, (void *)&ut,
     574  			 sizeof(ut))) == sizeof(ut)) {
     575  	if (ut.ut_tv.tv_sec >= lltime && strncmp(ut.ut_user, user, UT_NAMESIZE) == 0) {
     576  	    memcpy(&utuser, &ut, sizeof(utuser));
     577  	    failed++;
     578  	}
     579      }
     580  
     581      if (retval != 0)
     582  	pam_syslog(pamh, LOG_ERR, "corruption detected in %s", _PATH_BTMP);
     583      retval = PAM_SUCCESS;
     584  
     585      if (failed) {
     586  	/* we want the date? */
     587  	if (announce & LASTLOG_DATE) {
     588  	    struct tm *tm, tm_buf;
     589  	    time_t lf_time;
     590  
     591  	    lf_time = utuser.ut_tv.tv_sec;
     592  	    if ((tm = localtime_r (&lf_time, &tm_buf)) != NULL) {
     593  	        strftime (the_time, sizeof (the_time),
     594  	            /* TRANSLATORS: "strftime options for date of last login" */
     595  	                  _(" %a %b %e %H:%M:%S %Z %Y"), tm);
     596  	        date = the_time;
     597  	    }
     598  	}
     599  
     600  	/* we want & have the host? */
     601  	if ((announce & LASTLOG_HOST)
     602  		&& (utuser.ut_host[0] != '\0')) {
     603  	    /* TRANSLATORS: " from <host>" */
     604  	    if (asprintf(&host, _(" from %.*s"), UT_HOSTSIZE,
     605  		    utuser.ut_host) < 0) {
     606  		pam_syslog(pamh, LOG_CRIT, "out of memory");
     607  		retval = PAM_BUF_ERR;
     608  		goto cleanup;
     609  	    }
     610  	}
     611  
     612  	/* we want and have the terminal? */
     613  	if ((announce & LASTLOG_LINE)
     614  		&& (utuser.ut_line[0] != '\0')) {
     615  	    /* TRANSLATORS: " on <terminal>" */
     616  	    if (asprintf(&line, _(" on %.*s"), UT_LINESIZE,
     617  			utuser.ut_line) < 0) {
     618  		pam_syslog(pamh, LOG_CRIT, "out of memory");
     619  		retval = PAM_BUF_ERR;
     620  		goto cleanup;
     621  	    }
     622  	}
     623  
     624  	if (line != NULL || date != NULL || host != NULL) {
     625  	    /* TRANSLATORS: "Last failed login: <date> from <host> on <terminal>" */
     626  	    pam_info(pamh, _("Last failed login:%s%s%s"),
     627  			      date ? date : "",
     628  			      host ? host : "",
     629  			      line ? line : "");
     630  	}
     631  
     632  	_pam_drop(line);
     633  #if defined HAVE_DNGETTEXT && defined ENABLE_NLS
     634          retval = asprintf (&line, dngettext(PACKAGE,
     635  		"There was %d failed login attempt since the last successful login.",
     636  		"There were %d failed login attempts since the last successful login.",
     637  		failed),
     638  	    failed);
     639  #else
     640  	if (failed == 1)
     641  	    retval = asprintf(&line,
     642  		_("There was %d failed login attempt since the last successful login."),
     643  		failed);
     644  	else
     645  	    retval = asprintf(&line,
     646  		/* TRANSLATORS: only used if dngettext is not supported */
     647  		_("There were %d failed login attempts since the last successful login."),
     648  		failed);
     649  #endif
     650  	if (retval >= 0)
     651  		retval = pam_info(pamh, "%s", line);
     652  	else {
     653  		retval = PAM_BUF_ERR;
     654  		line = NULL;
     655  	}
     656      }
     657  
     658  cleanup:
     659      free(host);
     660      free(line);
     661      close(fd);
     662      D(("all done with btmp"));
     663  
     664      return retval;
     665  }
     666  
     667  /* --- authentication (locking out inactive users) functions --- */
     668  int
     669  pam_sm_authenticate(pam_handle_t *pamh, int flags,
     670  		    int argc, const char **argv)
     671  {
     672      int retval, ctrl;
     673      const char *user = NULL;
     674      const struct passwd *pwd;
     675      uid_t uid;
     676      time_t lltime = 0;
     677      time_t inactive_days = 0;
     678      int last_fd;
     679  
     680      /*
     681       * Lock out the user if he did not login recently enough.
     682       */
     683  
     684      ctrl = _pam_auth_parse(pamh, flags, argc, argv, &inactive_days);
     685  
     686      /* which user? */
     687  
     688      if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS) {
     689          pam_syslog(pamh, LOG_NOTICE, "cannot determine user name");
     690          return PAM_USER_UNKNOWN;
     691      }
     692  
     693      /* what uid? */
     694  
     695      pwd = pam_modutil_getpwnam (pamh, user);
     696      if (pwd == NULL) {
     697          pam_syslog(pamh, LOG_NOTICE, "user unknown");
     698  	return PAM_USER_UNKNOWN;
     699      }
     700      uid = pwd->pw_uid;
     701      pwd = NULL;                                         /* tidy up */
     702  
     703      if (uid == 0 || uid > get_lastlog_uid_max(pamh))
     704  	return PAM_SUCCESS;
     705  
     706      /* obtain the last login date and all the relevant info */
     707      last_fd = last_login_open(pamh, ctrl, uid);
     708      if (last_fd < 0) {
     709  	return PAM_IGNORE;
     710      }
     711  
     712      retval = last_login_read(pamh, ctrl|LASTLOG_QUIET, last_fd, uid, &lltime);
     713      close(last_fd);
     714  
     715      if (retval != PAM_SUCCESS) {
     716  	D(("error while reading lastlog file"));
     717  	return PAM_IGNORE;
     718      }
     719  
     720      if (lltime == 0) { /* user never logged in before */
     721          if (ctrl & LASTLOG_DEBUG)
     722              pam_syslog(pamh, LOG_DEBUG, "user never logged in - pass");
     723          return PAM_SUCCESS;
     724      }
     725  
     726      lltime = (time(NULL) - lltime) / (24*60*60);
     727  
     728      if (lltime > inactive_days) {
     729          pam_syslog(pamh, LOG_INFO, "user %s inactive for %ld days - denied",
     730  		   user, (long) lltime);
     731          return PAM_AUTH_ERR;
     732      }
     733  
     734      return PAM_SUCCESS;
     735  }
     736  
     737  int
     738  pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED,
     739  		    int argc UNUSED, const char **argv UNUSED)
     740  {
     741      return PAM_SUCCESS;
     742  }
     743  
     744  int
     745  pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
     746  		    int argc, const char **argv)
     747  {
     748      return pam_sm_authenticate(pamh, flags, argc, argv);
     749  }
     750  
     751  /* --- session management functions --- */
     752  
     753  int
     754  pam_sm_open_session(pam_handle_t *pamh, int flags,
     755  		    int argc, const char **argv)
     756  {
     757      int retval, ctrl;
     758      const void *user;
     759      const struct passwd *pwd;
     760      uid_t uid;
     761      time_t lltime = 0;
     762  
     763      /*
     764       * this module gets the uid of the PAM_USER. Uses it to display
     765       * last login info and then updates the lastlog for that user.
     766       */
     767  
     768      ctrl = _pam_session_parse(pamh, flags, argc, argv);
     769  
     770      /* which user? */
     771  
     772      retval = pam_get_item(pamh, PAM_USER, &user);
     773      if (retval != PAM_SUCCESS || user == NULL || *(const char *)user == '\0') {
     774  	pam_syslog(pamh, LOG_NOTICE, "user unknown");
     775  	return PAM_USER_UNKNOWN;
     776      }
     777  
     778      /* what uid? */
     779  
     780      pwd = pam_modutil_getpwnam (pamh, user);
     781      if (pwd == NULL) {
     782  	D(("couldn't identify user %s", user));
     783  	return PAM_USER_UNKNOWN;
     784      }
     785      uid = pwd->pw_uid;
     786      pwd = NULL;                                         /* tidy up */
     787  
     788      /* process the current login attempt (indicate last) */
     789  
     790      retval = last_login_date(pamh, ctrl, uid, user, &lltime);
     791  
     792      if ((ctrl & LASTLOG_BTMP) && retval == PAM_SUCCESS) {
     793  	    retval = last_login_failed(pamh, ctrl, user, lltime);
     794      }
     795  
     796      /* indicate success or failure */
     797  
     798      uid = -1;                                           /* forget this */
     799  
     800      return retval;
     801  }
     802  
     803  int
     804  pam_sm_close_session (pam_handle_t *pamh, int flags,
     805  		      int argc, const char **argv)
     806  {
     807      const char *terminal_line;
     808  
     809      if (!(_pam_session_parse(pamh, flags, argc, argv) & LASTLOG_WTMP))
     810  	return PAM_SUCCESS;
     811  
     812      terminal_line = get_tty(pamh);
     813  
     814      /* Wipe out utmp logout entry */
     815      logwtmp(terminal_line, "", "");
     816  
     817      return PAM_SUCCESS;
     818  }
     819  
     820  /* end of module definition */