#include "includes.h"
#include "ssh.h"
#include "xmalloc.h"
#include "loginrec.h"
#include "log.h"
#include "atomicio.h"
RCSID("$Id: loginrec.c,v 1.56 2004/04/08 06:16:06 dtucker Exp $");
#ifdef HAVE_UTIL_H
# include <util.h>
#endif
#ifdef HAVE_LIBUTIL_H
# include <libutil.h>
#endif
#if HAVE_UTMP_H
void set_utmp_time(struct logininfo *li, struct utmp *ut);
void construct_utmp(struct logininfo *li, struct utmp *ut);
#endif
#ifdef HAVE_UTMPX_H
void set_utmpx_time(struct logininfo *li, struct utmpx *ut);
void construct_utmpx(struct logininfo *li, struct utmpx *ut);
#endif
int utmp_write_entry(struct logininfo *li);
int utmpx_write_entry(struct logininfo *li);
int wtmp_write_entry(struct logininfo *li);
int wtmpx_write_entry(struct logininfo *li);
int lastlog_write_entry(struct logininfo *li);
int syslogin_write_entry(struct logininfo *li);
int getlast_entry(struct logininfo *li);
int lastlog_get_entry(struct logininfo *li);
int wtmp_get_entry(struct logininfo *li);
int wtmpx_get_entry(struct logininfo *li);
#define MIN_SIZEOF(s1,s2) ( sizeof(s1) < sizeof(s2) ? sizeof(s1) : sizeof(s2) )
int
login_login (struct logininfo *li)
{
li->type = LTYPE_LOGIN;
return login_write(li);
}
int
login_logout(struct logininfo *li)
{
li->type = LTYPE_LOGOUT;
return login_write(li);
}
unsigned int
login_get_lastlog_time(const int uid)
{
struct logininfo li;
if (login_get_lastlog(&li, uid))
return li.tv_sec;
else
return 0;
}
struct logininfo *
login_get_lastlog(struct logininfo *li, const int uid)
{
struct passwd *pw;
memset(li, '\0', sizeof(*li));
li->uid = uid;
pw = getpwuid(uid);
if (pw == NULL)
fatal("login_get_lastlog: Cannot find account for uid %i", uid);
strlcpy(li->username, pw->pw_name, sizeof(li->username));
if (getlast_entry(li))
return li;
else
return NULL;
}
struct
logininfo *login_alloc_entry(int pid, const char *username,
const char *hostname, const char *line)
{
struct logininfo *newli;
newli = (struct logininfo *) xmalloc (sizeof(*newli));
(void)login_init_entry(newli, pid, username, hostname, line);
return newli;
}
void
login_free_entry(struct logininfo *li)
{
xfree(li);
}
int
login_init_entry(struct logininfo *li, int pid, const char *username,
const char *hostname, const char *line)
{
struct passwd *pw;
memset(li, 0, sizeof(*li));
li->pid = pid;
if (line)
line_fullname(li->line, line, sizeof(li->line));
if (username) {
strlcpy(li->username, username, sizeof(li->username));
pw = getpwnam(li->username);
if (pw == NULL)
fatal("login_init_entry: Cannot find user \"%s\"", li->username);
li->uid = pw->pw_uid;
}
if (hostname)
strlcpy(li->hostname, hostname, sizeof(li->hostname));
return 1;
}
void
login_set_current_time(struct logininfo *li)
{
struct timeval tv;
gettimeofday(&tv, NULL);
li->tv_sec = tv.tv_sec;
li->tv_usec = tv.tv_usec;
}
void
login_set_addr(struct logininfo *li, const struct sockaddr *sa,
const unsigned int sa_size)
{
unsigned int bufsize = sa_size;
if (sizeof(li->hostaddr) < sa_size)
bufsize = sizeof(li->hostaddr);
memcpy((void *)&(li->hostaddr.sa), (const void *)sa, bufsize);
}
int
login_write (struct logininfo *li)
{
#ifndef HAVE_CYGWIN
if ((int)geteuid() != 0) {
logit("Attempt to write login records by non-root user (aborting)");
return 1;
}
#endif
login_set_current_time(li);
#ifdef USE_LOGIN
syslogin_write_entry(li);
#endif
#ifdef USE_LASTLOG
if (li->type == LTYPE_LOGIN) {
lastlog_write_entry(li);
}
#endif
#ifdef USE_UTMP
utmp_write_entry(li);
#endif
#ifdef USE_WTMP
wtmp_write_entry(li);
#endif
#ifdef USE_UTMPX
utmpx_write_entry(li);
#endif
#ifdef USE_WTMPX
wtmpx_write_entry(li);
#endif
return 0;
}
#ifdef LOGIN_NEEDS_UTMPX
int
login_utmp_only(struct logininfo *li)
{
li->type = LTYPE_LOGIN;
login_set_current_time(li);
# ifdef USE_UTMP
utmp_write_entry(li);
# endif
# ifdef USE_WTMP
wtmp_write_entry(li);
# endif
# ifdef USE_UTMPX
utmpx_write_entry(li);
# endif
# ifdef USE_WTMPX
wtmpx_write_entry(li);
# endif
return 0;
}
#endif
int
getlast_entry(struct logininfo *li)
{
#ifdef USE_LASTLOG
return(lastlog_get_entry(li));
#else
#ifdef DISABLE_LASTLOG
return 0;
# else
# if defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP))
return (wtmp_get_entry(li));
# else
# if defined(USE_WTMPX) && (defined(HAVE_TIME_IN_UTMPX) || defined(HAVE_TV_IN_UTMPX))
return (wtmpx_get_entry(li));
# else
return 0;
# endif
# endif
# endif
#endif
}
char *
line_fullname(char *dst, const char *src, int dstsize)
{
memset(dst, '\0', dstsize);
if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5))) {
strlcpy(dst, src, dstsize);
} else {
strlcpy(dst, "/dev/", dstsize);
strlcat(dst, src, dstsize);
}
return dst;
}
char *
line_stripname(char *dst, const char *src, int dstsize)
{
memset(dst, '\0', dstsize);
if (strncmp(src, "/dev/", 5) == 0)
strlcpy(dst, src + 5, dstsize);
else
strlcpy(dst, src, dstsize);
return dst;
}
char *
line_abbrevname(char *dst, const char *src, int dstsize)
{
size_t len;
memset(dst, '\0', dstsize);
if (strncmp(src, "/dev/", 5) == 0)
src += 5;
#ifdef WITH_ABBREV_NO_TTY
if (strncmp(src, "tty", 3) == 0)
src += 3;
#endif
len = strlen(src);
if (len > 0) {
if (((int)len - dstsize) > 0)
src += ((int)len - dstsize);
strncpy(dst, src, (size_t)dstsize);
}
return dst;
}
#if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN)
void
set_utmp_time(struct logininfo *li, struct utmp *ut)
{
# ifdef HAVE_TV_IN_UTMP
ut->ut_tv.tv_sec = li->tv_sec;
ut->ut_tv.tv_usec = li->tv_usec;
# else
# ifdef HAVE_TIME_IN_UTMP
ut->ut_time = li->tv_sec;
# endif
# endif
}
void
construct_utmp(struct logininfo *li,
struct utmp *ut)
{
# ifdef HAVE_ADDR_V6_IN_UTMP
struct sockaddr_in6 *sa6;
# endif
memset(ut, '\0', sizeof(*ut));
# ifdef HAVE_ID_IN_UTMP
line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id));
# endif
# ifdef HAVE_TYPE_IN_UTMP
switch (li->type) {
case LTYPE_LOGIN:
ut->ut_type = USER_PROCESS;
#ifdef _UNICOS
cray_set_tmpdir(ut);
#endif
break;
case LTYPE_LOGOUT:
ut->ut_type = DEAD_PROCESS;
#ifdef _UNICOS
cray_retain_utmp(ut, li->pid);
#endif
break;
}
# endif
set_utmp_time(li, ut);
line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line));
# ifdef HAVE_PID_IN_UTMP
ut->ut_pid = li->pid;
# endif
if (li->type == LTYPE_LOGOUT)
return;
strncpy(ut->ut_name, li->username, MIN_SIZEOF(ut->ut_name, li->username));
# ifdef HAVE_HOST_IN_UTMP
strncpy(ut->ut_host, li->hostname, MIN_SIZEOF(ut->ut_host, li->hostname));
# endif
# ifdef HAVE_ADDR_IN_UTMP
if (li->hostaddr.sa.sa_family == AF_INET)
ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
# endif
# ifdef HAVE_ADDR_V6_IN_UTMP
if (li->hostaddr.sa.sa_family == AF_INET6) {
sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa);
memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16);
if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
ut->ut_addr_v6[0] = ut->ut_addr_v6[3];
ut->ut_addr_v6[1] = 0;
ut->ut_addr_v6[2] = 0;
ut->ut_addr_v6[3] = 0;
}
}
# endif
}
#endif
#if defined(USE_UTMPX) || defined (USE_WTMPX)
void
set_utmpx_time(struct logininfo *li, struct utmpx *utx)
{
# ifdef HAVE_TV_IN_UTMPX
utx->ut_tv.tv_sec = li->tv_sec;
utx->ut_tv.tv_usec = li->tv_usec;
# else
# ifdef HAVE_TIME_IN_UTMPX
utx->ut_time = li->tv_sec;
# endif
# endif
}
void
construct_utmpx(struct logininfo *li, struct utmpx *utx)
{
# ifdef HAVE_ADDR_V6_IN_UTMP
struct sockaddr_in6 *sa6;
# endif
memset(utx, '\0', sizeof(*utx));
# ifdef HAVE_ID_IN_UTMPX
line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id));
# endif
switch (li->type) {
case LTYPE_LOGIN:
utx->ut_type = USER_PROCESS;
break;
case LTYPE_LOGOUT:
utx->ut_type = DEAD_PROCESS;
break;
}
line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line));
set_utmpx_time(li, utx);
utx->ut_pid = li->pid;
strncpy(utx->ut_name, li->username, MIN_SIZEOF(utx->ut_name, li->username));
if (li->type == LTYPE_LOGOUT)
return;
# ifdef HAVE_HOST_IN_UTMPX
strncpy(utx->ut_host, li->hostname, MIN_SIZEOF(utx->ut_host, li->hostname));
# endif
# ifdef HAVE_ADDR_IN_UTMPX
if (li->hostaddr.sa.sa_family == AF_INET)
utx->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
# endif
# ifdef HAVE_ADDR_V6_IN_UTMP
if (li->hostaddr.sa.sa_family == AF_INET6) {
sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa);
memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16);
if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
ut->ut_addr_v6[0] = ut->ut_addr_v6[3];
ut->ut_addr_v6[1] = 0;
ut->ut_addr_v6[2] = 0;
ut->ut_addr_v6[3] = 0;
}
}
# endif
# ifdef HAVE_SYSLEN_IN_UTMPX
utx->ut_syslen = MIN(strlen(li->hostname), sizeof(utx->ut_host));
# endif
}
#endif
#ifdef USE_UTMP
# if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \
defined(HAVE_PUTUTLINE)
# define UTMP_USE_LIBRARY
# endif
# ifdef UTMP_USE_LIBRARY
static int
utmp_write_library(struct logininfo *li, struct utmp *ut)
{
setutent();
pututline(ut);
# ifdef HAVE_ENDUTENT
endutent();
# endif
return 1;
}
# else
static int
utmp_write_direct(struct logininfo *li, struct utmp *ut)
{
struct utmp old_ut;
register int fd;
int tty;
#if defined(HAVE_GETTTYENT)
register struct ttyent *ty;
tty=0;
setttyent();
while ((struct ttyent *)0 != (ty = getttyent())) {
tty++;
if (!strncmp(ty->ty_name, ut->ut_line, sizeof(ut->ut_line)))
break;
}
endttyent();
if((struct ttyent *)0 == ty) {
logit("utmp_write_entry: tty not found");
return(1);
}
#else
tty = ttyslot();
#endif
if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) {
(void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) &&
(ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') &&
(strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) &&
(strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0)) {
(void)memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host));
}
(void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut))
logit("utmp_write_direct: error writing %s: %s",
UTMP_FILE, strerror(errno));
(void)close(fd);
return 1;
} else {
return 0;
}
}
# endif
static int
utmp_perform_login(struct logininfo *li)
{
struct utmp ut;
construct_utmp(li, &ut);
# ifdef UTMP_USE_LIBRARY
if (!utmp_write_library(li, &ut)) {
logit("utmp_perform_login: utmp_write_library() failed");
return 0;
}
# else
if (!utmp_write_direct(li, &ut)) {
logit("utmp_perform_login: utmp_write_direct() failed");
return 0;
}
# endif
return 1;
}
static int
utmp_perform_logout(struct logininfo *li)
{
struct utmp ut;
construct_utmp(li, &ut);
# ifdef UTMP_USE_LIBRARY
if (!utmp_write_library(li, &ut)) {
logit("utmp_perform_logout: utmp_write_library() failed");
return 0;
}
# else
if (!utmp_write_direct(li, &ut)) {
logit("utmp_perform_logout: utmp_write_direct() failed");
return 0;
}
# endif
return 1;
}
int
utmp_write_entry(struct logininfo *li)
{
switch(li->type) {
case LTYPE_LOGIN:
return utmp_perform_login(li);
case LTYPE_LOGOUT:
return utmp_perform_logout(li);
default:
logit("utmp_write_entry: invalid type field");
return 0;
}
}
#endif
#ifdef USE_UTMPX
# if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \
defined(HAVE_PUTUTXLINE)
# define UTMPX_USE_LIBRARY
# endif
# ifdef UTMPX_USE_LIBRARY
static int
utmpx_write_library(struct logininfo *li, struct utmpx *utx)
{
setutxent();
pututxline(utx);
# ifdef HAVE_ENDUTXENT
endutxent();
# endif
return 1;
}
# else
static int
utmpx_write_direct(struct logininfo *li, struct utmpx *utx)
{
logit("utmpx_write_direct: not implemented!");
return 0;
}
# endif
static int
utmpx_perform_login(struct logininfo *li)
{
struct utmpx utx;
construct_utmpx(li, &utx);
# ifdef UTMPX_USE_LIBRARY
if (!utmpx_write_library(li, &utx)) {
logit("utmpx_perform_login: utmp_write_library() failed");
return 0;
}
# else
if (!utmpx_write_direct(li, &ut)) {
logit("utmpx_perform_login: utmp_write_direct() failed");
return 0;
}
# endif
return 1;
}
static int
utmpx_perform_logout(struct logininfo *li)
{
struct utmpx utx;
construct_utmpx(li, &utx);
# ifdef HAVE_ID_IN_UTMPX
line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id));
# endif
# ifdef HAVE_TYPE_IN_UTMPX
utx.ut_type = DEAD_PROCESS;
# endif
# ifdef UTMPX_USE_LIBRARY
utmpx_write_library(li, &utx);
# else
utmpx_write_direct(li, &utx);
# endif
return 1;
}
int
utmpx_write_entry(struct logininfo *li)
{
switch(li->type) {
case LTYPE_LOGIN:
return utmpx_perform_login(li);
case LTYPE_LOGOUT:
return utmpx_perform_logout(li);
default:
logit("utmpx_write_entry: invalid type field");
return 0;
}
}
#endif
#ifdef USE_WTMP
static int
wtmp_write(struct logininfo *li, struct utmp *ut)
{
struct stat buf;
int fd, ret = 1;
if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
logit("wtmp_write: problem writing %s: %s",
WTMP_FILE, strerror(errno));
return 0;
}
if (fstat(fd, &buf) == 0)
if (atomicio(vwrite, fd, ut, sizeof(*ut)) != sizeof(*ut)) {
ftruncate(fd, buf.st_size);
logit("wtmp_write: problem writing %s: %s",
WTMP_FILE, strerror(errno));
ret = 0;
}
(void)close(fd);
return ret;
}
static int
wtmp_perform_login(struct logininfo *li)
{
struct utmp ut;
construct_utmp(li, &ut);
return wtmp_write(li, &ut);
}
static int
wtmp_perform_logout(struct logininfo *li)
{
struct utmp ut;
construct_utmp(li, &ut);
return wtmp_write(li, &ut);
}
int
wtmp_write_entry(struct logininfo *li)
{
switch(li->type) {
case LTYPE_LOGIN:
return wtmp_perform_login(li);
case LTYPE_LOGOUT:
return wtmp_perform_logout(li);
default:
logit("wtmp_write_entry: invalid type field");
return 0;
}
}
static int
wtmp_islogin(struct logininfo *li, struct utmp *ut)
{
if (strncmp(li->username, ut->ut_name,
MIN_SIZEOF(li->username, ut->ut_name)) == 0) {
# ifdef HAVE_TYPE_IN_UTMP
if (ut->ut_type & USER_PROCESS)
return 1;
# else
return 1;
# endif
}
return 0;
}
int
wtmp_get_entry(struct logininfo *li)
{
struct stat st;
struct utmp ut;
int fd, found=0;
li->tv_sec = li->tv_usec = 0;
if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) {
logit("wtmp_get_entry: problem opening %s: %s",
WTMP_FILE, strerror(errno));
return 0;
}
if (fstat(fd, &st) != 0) {
logit("wtmp_get_entry: couldn't stat %s: %s",
WTMP_FILE, strerror(errno));
close(fd);
return 0;
}
if (lseek(fd, -(off_t)sizeof(struct utmp), SEEK_END) == -1) {
close(fd);
return 0;
}
while (!found) {
if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) {
logit("wtmp_get_entry: read of %s failed: %s",
WTMP_FILE, strerror(errno));
close (fd);
return 0;
}
if ( wtmp_islogin(li, &ut) ) {
found = 1;
# ifdef HAVE_TIME_IN_UTMP
li->tv_sec = ut.ut_time;
# else
# if HAVE_TV_IN_UTMP
li->tv_sec = ut.ut_tv.tv_sec;
# endif
# endif
line_fullname(li->line, ut.ut_line,
MIN_SIZEOF(li->line, ut.ut_line));
# ifdef HAVE_HOST_IN_UTMP
strlcpy(li->hostname, ut.ut_host,
MIN_SIZEOF(li->hostname, ut.ut_host));
# endif
continue;
}
if (lseek(fd, -(off_t)(2 * sizeof(struct utmp)), SEEK_CUR) == -1) {
close (fd);
return 0;
}
}
close(fd);
return 1;
}
# endif
#ifdef USE_WTMPX
static int
wtmpx_write(struct logininfo *li, struct utmpx *utx)
{
#ifndef HAVE_UPDWTMPX
struct stat buf;
int fd, ret = 1;
if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
logit("wtmpx_write: problem opening %s: %s",
WTMPX_FILE, strerror(errno));
return 0;
}
if (fstat(fd, &buf) == 0)
if (atomicio(vwrite, fd, utx, sizeof(*utx)) != sizeof(*utx)) {
ftruncate(fd, buf.st_size);
logit("wtmpx_write: problem writing %s: %s",
WTMPX_FILE, strerror(errno));
ret = 0;
}
(void)close(fd);
return ret;
#else
updwtmpx(WTMPX_FILE, utx);
return 1;
#endif
}
static int
wtmpx_perform_login(struct logininfo *li)
{
struct utmpx utx;
construct_utmpx(li, &utx);
return wtmpx_write(li, &utx);
}
static int
wtmpx_perform_logout(struct logininfo *li)
{
struct utmpx utx;
construct_utmpx(li, &utx);
return wtmpx_write(li, &utx);
}
int
wtmpx_write_entry(struct logininfo *li)
{
switch(li->type) {
case LTYPE_LOGIN:
return wtmpx_perform_login(li);
case LTYPE_LOGOUT:
return wtmpx_perform_logout(li);
default:
logit("wtmpx_write_entry: invalid type field");
return 0;
}
}
static int
wtmpx_islogin(struct logininfo *li, struct utmpx *utx)
{
if ( strncmp(li->username, utx->ut_name,
MIN_SIZEOF(li->username, utx->ut_name)) == 0 ) {
# ifdef HAVE_TYPE_IN_UTMPX
if (utx->ut_type == USER_PROCESS)
return 1;
# else
return 1;
# endif
}
return 0;
}
int
wtmpx_get_entry(struct logininfo *li)
{
struct stat st;
struct utmpx utx;
int fd, found=0;
li->tv_sec = li->tv_usec = 0;
if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) {
logit("wtmpx_get_entry: problem opening %s: %s",
WTMPX_FILE, strerror(errno));
return 0;
}
if (fstat(fd, &st) != 0) {
logit("wtmpx_get_entry: couldn't stat %s: %s",
WTMPX_FILE, strerror(errno));
close(fd);
return 0;
}
if (lseek(fd, -(off_t)sizeof(struct utmpx), SEEK_END) == -1 ) {
close(fd);
return 0;
}
while (!found) {
if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) {
logit("wtmpx_get_entry: read of %s failed: %s",
WTMPX_FILE, strerror(errno));
close (fd);
return 0;
}
if ( wtmpx_islogin(li, &utx) ) {
found = 1;
# ifdef HAVE_TV_IN_UTMPX
li->tv_sec = utx.ut_tv.tv_sec;
# else
# ifdef HAVE_TIME_IN_UTMPX
li->tv_sec = utx.ut_time;
# endif
# endif
line_fullname(li->line, utx.ut_line, sizeof(li->line));
# ifdef HAVE_HOST_IN_UTMPX
strlcpy(li->hostname, utx.ut_host,
MIN_SIZEOF(li->hostname, utx.ut_host));
# endif
continue;
}
if (lseek(fd, -(off_t)(2 * sizeof(struct utmpx)), SEEK_CUR) == -1) {
close (fd);
return 0;
}
}
close(fd);
return 1;
}
#endif
#ifdef USE_LOGIN
static int
syslogin_perform_login(struct logininfo *li)
{
struct utmp *ut;
if (! (ut = (struct utmp *)malloc(sizeof(*ut)))) {
logit("syslogin_perform_login: couldn't malloc()");
return 0;
}
construct_utmp(li, ut);
login(ut);
free(ut);
return 1;
}
static int
syslogin_perform_logout(struct logininfo *li)
{
# ifdef HAVE_LOGOUT
char line[UT_LINESIZE];
(void)line_stripname(line, li->line, sizeof(line));
if (!logout(line)) {
logit("syslogin_perform_logout: logout() returned an error");
# ifdef HAVE_LOGWTMP
} else {
logwtmp(line, "", "");
# endif
}
# endif
return 1;
}
int
syslogin_write_entry(struct logininfo *li)
{
switch (li->type) {
case LTYPE_LOGIN:
return syslogin_perform_login(li);
case LTYPE_LOGOUT:
return syslogin_perform_logout(li);
default:
logit("syslogin_write_entry: Invalid type field");
return 0;
}
}
#endif
#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)
{
memset(last, '\0', sizeof(*last));
(void)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;
}
static int
lastlog_filetype(char *filename)
{
struct stat st;
if (stat(LASTLOG_FILE, &st) != 0) {
logit("lastlog_perform_login: Couldn't stat %s: %s", 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;
}
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:
snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s",
LASTLOG_FILE, li->username);
break;
default:
logit("lastlog_openseek: %.100s is not a file or directory!",
LASTLOG_FILE);
return 0;
}
*fd = open(lastlog_file, filemode, 0600);
if ( *fd < 0) {
debug("lastlog_openseek: Couldn't open %s: %s",
lastlog_file, strerror(errno));
return 0;
}
if (type == LL_FILE) {
offset = (off_t) ((long)li->uid * sizeof(struct lastlog));
if ( lseek(*fd, offset, SEEK_SET) != offset ) {
logit("lastlog_openseek: %s->lseek(): %s",
lastlog_file, strerror(errno));
return 0;
}
}
return 1;
}
static int
lastlog_perform_login(struct logininfo *li)
{
struct lastlog last;
int fd;
lastlog_construct(li, &last);
if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT))
return(0);
if (atomicio(vwrite, fd, &last, sizeof(last)) != sizeof(last)) {
close(fd);
logit("lastlog_write_filemode: Error writing to %s: %s",
LASTLOG_FILE, strerror(errno));
return 0;
}
close(fd);
return 1;
}
int
lastlog_write_entry(struct logininfo *li)
{
switch(li->type) {
case LTYPE_LOGIN:
return lastlog_perform_login(li);
default:
logit("lastlog_write_entry: Invalid type field");
return 0;
}
}
static void
lastlog_populate_entry(struct logininfo *li, struct lastlog *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;
}
int
lastlog_get_entry(struct logininfo *li)
{
struct lastlog last;
int fd, ret;
if (!lastlog_openseek(li, &fd, O_RDONLY))
return (0);
ret = atomicio(read, fd, &last, sizeof(last));
close(fd);
switch (ret) {
case 0:
memset(&last, '\0', sizeof(last));
case sizeof(last):
lastlog_populate_entry(li, &last);
return (1);
case -1:
error("%s: Error reading from %s: %s", __func__,
LASTLOG_FILE, strerror(errno));
return (0);
default:
error("%s: Error reading from %s: Expecting %d, got %d",
__func__, LASTLOG_FILE, sizeof(last), ret);
return (0);
}
return (0);
}
#endif