- (djm) [configure.ac loginrec.c] bz#1421: fix lastlog support for OSX.
   OSX provides a getlastlogxbyname function that automates the reading of
   a lastlog file. Also, the pututxline function will update lastlog so
   there is no need for loginrec.c to do it explicitly. Collapse some
   overly verbose code while I'm in there.
diff --git a/ChangeLog b/ChangeLog
index 8bec9d1..124924b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,11 @@
-20090212
+20090205
  - (djm) [sshpty.c] bz#1419: OSX uses cloning ptys that automagically
    set ownership and modes, so avoid explicitly setting them
+ - (djm) [configure.ac loginrec.c] bz#1421: fix lastlog support for OSX.
+   OSX provides a getlastlogxbyname function that automates the reading of
+   a lastlog file. Also, the pututxline function will update lastlog so
+   there is no need for loginrec.c to do it explicitly. Collapse some
+   overly verbose code while I'm in there.
 
 20090201
  - (dtucker) [defines.h sshconnect.c] INET6_ADDRSTRLEN is now needed in
@@ -5118,5 +5123,5 @@
    OpenServer 6 and add osr5bigcrypt support so when someone migrates
    passwords between UnixWare and OpenServer they will still work. OK dtucker@
 
-$Id: ChangeLog,v 1.5184 2009/02/12 01:19:20 djm Exp $
+$Id: ChangeLog,v 1.5185 2009/02/12 02:12:21 djm Exp $
 
diff --git a/configure.ac b/configure.ac
index 94589dd..242dea0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,4 +1,4 @@
-# $Id: configure.ac,v 1.413 2009/01/08 04:50:09 tim Exp $
+# $Id: configure.ac,v 1.414 2009/02/12 02:12:22 djm Exp $
 #
 # Copyright (c) 1999-2004 Damien Miller
 #
@@ -15,7 +15,7 @@
 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 AC_INIT(OpenSSH, Portable, openssh-unix-dev@mindrot.org)
-AC_REVISION($Revision: 1.413 $)
+AC_REVISION($Revision: 1.414 $)
 AC_CONFIG_SRCDIR([ssh.c])
 
 AC_CONFIG_HEADER(config.h)
@@ -477,6 +477,8 @@
 	AC_CHECK_DECL(AU_IPv4, [], 
 	    AC_DEFINE(AU_IPv4, 0, [System only supports IPv4 audit records])
 	    [#include <bsm/audit.h>]
+	AC_DEFINE(LASTLOG_WRITE_PUTUTXLINE, 1,
+	    [Define if pututxline updates lastlog too])
 	)
 	;;
 *-*-dragonfly*)
@@ -1508,6 +1510,8 @@
 dnl    Checks for utmpx functions
 AC_CHECK_FUNCS(endutxent getutxent getutxid getutxline pututxline )
 AC_CHECK_FUNCS(setutxent utmpxname)
+dnl    Checks for lastlog functions
+AC_CHECK_FUNCS(getlastlogxbyname)
 
 AC_CHECK_FUNC(daemon,
 	[AC_DEFINE(HAVE_DAEMON, 1, [Define if your libraries define daemon()])],
diff --git a/loginrec.c b/loginrec.c
index b411141..f4af067 100644
--- a/loginrec.c
+++ b/loginrec.c
@@ -1456,25 +1456,14 @@
  **/
 
 #ifdef USE_LASTLOG
-#define LL_FILE 1
-#define LL_DIR 2
-#define LL_OTHER 3
 
-static void
-lastlog_construct(struct logininfo *li, struct lastlog *last)
-{
-	/* clear the structure */
-	memset(last, '\0', sizeof(*last));
-
-	line_stripname(last->ll_line, li->line, sizeof(last->ll_line));
-	strlcpy(last->ll_host, li->hostname,
-		MIN_SIZEOF(last->ll_host, li->hostname));
-	last->ll_time = li->tv_sec;
-}
-
+#if !defined(LASTLOG_WRITE_PUTUTXLINE) || !defined(HAVE_GETLASTLOGXBYNAME)
+/* open the file (using filemode) and seek to the login entry */
 static int
-lastlog_filetype(char *filename)
+lastlog_openseek(struct logininfo *li, int *fd, int filemode)
 {
+	off_t offset;
+	char lastlog_file[1024];
 	struct stat st;
 
 	if (stat(LASTLOG_FILE, &st) != 0) {
@@ -1482,34 +1471,12 @@
 		    LASTLOG_FILE, strerror(errno));
 		return (0);
 	}
-	if (S_ISDIR(st.st_mode))
-		return (LL_DIR);
-	else if (S_ISREG(st.st_mode))
-		return (LL_FILE);
-	else
-		return (LL_OTHER);
-}
-
-
-/* open the file (using filemode) and seek to the login entry */
-static int
-lastlog_openseek(struct logininfo *li, int *fd, int filemode)
-{
-	off_t offset;
-	int type;
-	char lastlog_file[1024];
-
-	type = lastlog_filetype(LASTLOG_FILE);
-	switch (type) {
-	case LL_FILE:
-		strlcpy(lastlog_file, LASTLOG_FILE,
-		    sizeof(lastlog_file));
-		break;
-	case LL_DIR:
+	if (S_ISDIR(st.st_mode)) {
 		snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s",
 		    LASTLOG_FILE, li->username);
-		break;
-	default:
+	} else if (S_ISREG(st.st_mode)) {
+		strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file));
+	} else {
 		logit("%s: %.100s is not a file or directory!", __func__,
 		    LASTLOG_FILE);
 		return (0);
@@ -1522,7 +1489,7 @@
 		return (0);
 	}
 
-	if (type == LL_FILE) {
+	if (S_ISREG(st.st_mode)) {
 		/* find this uid's offset in the lastlog file */
 		offset = (off_t) ((long)li->uid * sizeof(struct lastlog));
 
@@ -1535,52 +1502,74 @@
 
 	return (1);
 }
+#endif /* !LASTLOG_WRITE_PUTUTXLINE || !HAVE_GETLASTLOGXBYNAME */
 
-static int
-lastlog_perform_login(struct logininfo *li)
-{
-	struct lastlog last;
-	int fd;
-
-	/* create our struct lastlog */
-	lastlog_construct(li, &last);
-
-	if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT))
-		return (0);
-
-	/* write the entry */
-	if (atomicio(vwrite, fd, &last, sizeof(last)) != sizeof(last)) {
-		close(fd);
-		logit("%s: Error writing to %s: %s", __func__,
-		    LASTLOG_FILE, strerror(errno));
-		return (0);
-	}
-
-	close(fd);
-	return (1);
-}
-
+#ifdef LASTLOG_WRITE_PUTUTXLINE
 int
 lastlog_write_entry(struct logininfo *li)
 {
 	switch(li->type) {
 	case LTYPE_LOGIN:
-		return (lastlog_perform_login(li));
+		return 1; /* lastlog written by pututxline */
+	default:
+		logit("lastlog_write_entry: Invalid type field");
+		return 0;
+	}
+}
+#else /* LASTLOG_WRITE_PUTUTXLINE */
+int
+lastlog_write_entry(struct logininfo *li)
+{
+	struct lastlog last;
+	int fd;
+
+	switch(li->type) {
+	case LTYPE_LOGIN:
+		/* create our struct lastlog */
+		memset(&last, '\0', sizeof(last));
+		line_stripname(last.ll_line, li->line, sizeof(last.ll_line));
+		strlcpy(last.ll_host, li->hostname,
+		    MIN_SIZEOF(last.ll_host, li->hostname));
+		last.ll_time = li->tv_sec;
+	
+		if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT))
+			return (0);
+	
+		/* write the entry */
+		if (atomicio(vwrite, fd, &last, sizeof(last)) != sizeof(last)) {
+			close(fd);
+			logit("%s: Error writing to %s: %s", __func__,
+			    LASTLOG_FILE, strerror(errno));
+			return (0);
+		}
+	
+		close(fd);
+		return (1);
 	default:
 		logit("%s: Invalid type field", __func__);
 		return (0);
 	}
 }
+#endif /* LASTLOG_WRITE_PUTUTXLINE */
 
-static void
-lastlog_populate_entry(struct logininfo *li, struct lastlog *last)
+#ifdef HAVE_GETLASTLOGXBYNAME
+int
+lastlog_get_entry(struct logininfo *li)
 {
-	line_fullname(li->line, last->ll_line, sizeof(li->line));
-	strlcpy(li->hostname, last->ll_host,
-	    MIN_SIZEOF(li->hostname, last->ll_host));
-	li->tv_sec = last->ll_time;
-}
+	struct lastlogx l, *ll;
 
+	if ((ll = getlastlogxbyname(li->username, &l)) == NULL) {
+		memset(&l, '\0', sizeof(l));
+		ll = &l;
+	}
+	line_fullname(li->line, ll->ll_line, sizeof(li->line));
+	strlcpy(li->hostname, ll->ll_host,
+		MIN_SIZEOF(li->hostname, ll->ll_host));
+	li->tv_sec = ll->ll_tv.tv_sec;
+	li->tv_usec = ll->ll_tv.tv_usec;
+	return (1);
+}
+#else /* HAVE_GETLASTLOGXBYNAME */
 int
 lastlog_get_entry(struct logininfo *li)
 {
@@ -1598,7 +1587,10 @@
 		memset(&last, '\0', sizeof(last));
 		/* FALLTHRU */
 	case sizeof(last):
-		lastlog_populate_entry(li, &last);
+		line_fullname(li->line, last.ll_line, sizeof(li->line));
+		strlcpy(li->hostname, last.ll_host,
+		    MIN_SIZEOF(li->hostname, last.ll_host));
+		li->tv_sec = last.ll_time;
 		return (1);
 	case -1:
 		error("%s: Error reading from %s: %s", __func__,
@@ -1613,6 +1605,7 @@
 	/* NOTREACHED */
 	return (0);
 }
+#endif /* HAVE_GETLASTLOGXBYNAME */
 #endif /* USE_LASTLOG */
 
 #ifdef USE_BTMP