* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+/*
+ * The btmp logging code is derived from login.c from util-linux and is under
+ * the the following license:
+ *
+ * Copyright (c) 1980, 1987, 1988 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+
/**
** loginrec.c: platform-independent login recording and lastlog retrieval
**/
#include "includes.h"
-#include "ssh.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#ifdef HAVE_PATHS_H
+# include <paths.h>
+#endif
+#include <pwd.h>
+#include <stdarg.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
#include "xmalloc.h"
+#include "key.h"
+#include "hostfile.h"
+#include "ssh.h"
#include "loginrec.h"
#include "log.h"
#include "atomicio.h"
+#include "packet.h"
+#include "canohost.h"
+#include "auth.h"
+#include "buffer.h"
#ifdef HAVE_UTIL_H
# include <util.h>
# include <libutil.h>
#endif
-RCSID("$Id$");
-
/**
** prototypes for helper functions in this file
**/
int wtmp_get_entry(struct logininfo *li);
int wtmpx_get_entry(struct logininfo *li);
+extern Buffer loginmsg;
+
/* pick the shortest string */
#define MIN_SIZEOF(s1,s2) (sizeof(s1) < sizeof(s2) ? sizeof(s1) : sizeof(s2))
*/
pw = getpwuid(uid);
if (pw == NULL)
- fatal("login_get_lastlog: Cannot find account for uid %i", uid);
+ fatal("%s: Cannot find account for uid %i", __func__, uid);
/* No MIN_SIZEOF here - we absolutely *must not* truncate the
* username (XXX - so check for trunc!) */
strlcpy(li->username, username, sizeof(li->username));
pw = getpwnam(li->username);
if (pw == NULL) {
- fatal("login_init_entry: Cannot find user \"%s\"",
+ fatal("%s: Cannot find user \"%s\"", __func__,
li->username);
}
li->uid = pw->pw_uid;
return (1);
}
-/*
+/*
* login_set_current_time(struct logininfo *) - set the current time
*
* Set the current time in a logininfo structure. This function is
wtmpx_write_entry(li);
#endif
#ifdef CUSTOM_SYS_AUTH_RECORD_LOGIN
- if (li->type == LTYPE_LOGIN &&
- !sys_auth_record_login(li->username,li->hostname,li->line))
+ if (li->type == LTYPE_LOGIN &&
+ !sys_auth_record_login(li->username,li->hostname,li->line,
+ &loginmsg))
logit("Writing login record failed for %s", li->username);
+#endif
+#ifdef SSH_AUDIT_EVENTS
+ if (li->type == LTYPE_LOGIN)
+ audit_session_open(li->line);
+ else if (li->type == LTYPE_LOGOUT)
+ audit_session_close(li->line);
#endif
return (0);
}
* sure dst has enough space, if not just copy src (ugh)
*/
char *
-line_fullname(char *dst, const char *src, int dstsize)
+line_fullname(char *dst, const char *src, u_int dstsize)
{
memset(dst, '\0', dstsize);
if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5)))
return (dst);
}
-/*
+/*
* line_abbrevname(): Return the abbreviated (usually four-character)
* form of the line (Just use the last <dstsize> characters of the
* full name.)
}
# else /* UTMP_USE_LIBRARY */
-/*
+/*
* Write a utmp entry direct to the file
* This is a slightly modification of code in OpenBSD's login.c
*/
return (0);
}
if (ret != pos) {
- logit("%s: Couldn't seek to tty %d slot in %s",
+ logit("%s: Couldn't seek to tty %d slot in %s",
__func__, tty, UTMP_FILE);
return (0);
}
construct_utmp(li, &ut);
# ifdef UTMP_USE_LIBRARY
if (!utmp_write_library(li, &ut)) {
- logit("utmp_perform_login: utmp_write_library() failed");
+ logit("%s: utmp_write_library() failed", __func__);
return (0);
}
# else
if (!utmp_write_direct(li, &ut)) {
- logit("utmp_perform_login: utmp_write_direct() failed");
+ logit("%s: utmp_write_direct() failed", __func__);
return (0);
}
# endif
construct_utmp(li, &ut);
# ifdef UTMP_USE_LIBRARY
if (!utmp_write_library(li, &ut)) {
- logit("utmp_perform_logout: utmp_write_library() failed");
+ logit("%s: utmp_write_library() failed", __func__);
return (0);
}
# else
if (!utmp_write_direct(li, &ut)) {
- logit("utmp_perform_logout: utmp_write_direct() failed");
+ logit("%s: utmp_write_direct() failed", __func__);
return (0);
}
# endif
return (utmp_perform_logout(li));
default:
- logit("utmp_write_entry: invalid type field");
+ logit("%s: invalid type field", __func__);
return (0);
}
}
static int
utmpx_write_direct(struct logininfo *li, struct utmpx *utx)
{
- logit("utmpx_write_direct: not implemented!");
+ logit("%s: not implemented!", __func__);
return (0);
}
# endif /* UTMPX_USE_LIBRARY */
construct_utmpx(li, &utx);
# ifdef UTMPX_USE_LIBRARY
if (!utmpx_write_library(li, &utx)) {
- logit("utmpx_perform_login: utmp_write_library() failed");
+ logit("%s: utmp_write_library() failed", __func__);
return (0);
}
# else
if (!utmpx_write_direct(li, &ut)) {
- logit("utmpx_perform_login: utmp_write_direct() failed");
+ logit("%s: utmp_write_direct() failed", __func__);
return (0);
}
# endif
case LTYPE_LOGOUT:
return (utmpx_perform_logout(li));
default:
- logit("utmpx_write_entry: invalid type field");
+ logit("%s: invalid type field", __func__);
return (0);
}
}
#ifdef USE_WTMP
-/*
+/*
* Write a wtmp entry direct to the end of the file
* This is a slight modification of code in OpenBSD's logwtmp.c
*/
int fd, ret = 1;
if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
- logit("wtmp_write: problem writing %s: %s",
+ logit("%s: problem writing %s: %s", __func__,
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",
+ logit("%s: problem writing %s: %s", __func__,
WTMP_FILE, strerror(errno));
ret = 0;
}
case LTYPE_LOGOUT:
return (wtmp_perform_logout(li));
default:
- logit("wtmp_write_entry: invalid type field");
+ logit("%s: invalid type field", __func__);
return (0);
}
}
-/*
+/*
* Notes on fetching login data from wtmp/wtmpx
*
* Logouts are usually recorded with (amongst other things) a blank
li->tv_sec = li->tv_usec = 0;
if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) {
- logit("wtmp_get_entry: problem opening %s: %s",
+ logit("%s: problem opening %s: %s", __func__,
WTMP_FILE, strerror(errno));
return (0);
}
if (fstat(fd, &st) != 0) {
- logit("wtmp_get_entry: couldn't stat %s: %s",
+ logit("%s: couldn't stat %s: %s", __func__,
WTMP_FILE, strerror(errno));
close(fd);
return (0);
while (!found) {
if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) {
- logit("wtmp_get_entry: read of %s failed: %s",
+ logit("%s: read of %s failed: %s", __func__,
WTMP_FILE, strerror(errno));
close (fd);
return (0);
int fd, ret = 1;
if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
- logit("wtmpx_write: problem opening %s: %s",
+ logit("%s: problem opening %s: %s", __func__,
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",
+ logit("%s: problem writing %s: %s", __func__,
WTMPX_FILE, strerror(errno));
ret = 0;
}
case LTYPE_LOGOUT:
return (wtmpx_perform_logout(li));
default:
- logit("wtmpx_write_entry: invalid type field");
+ logit("%s: invalid type field", __func__);
return (0);
}
}
li->tv_sec = li->tv_usec = 0;
if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) {
- logit("wtmpx_get_entry: problem opening %s: %s",
+ logit("%s: problem opening %s: %s", __func__,
WTMPX_FILE, strerror(errno));
return (0);
}
if (fstat(fd, &st) != 0) {
- logit("wtmpx_get_entry: couldn't stat %s: %s",
+ logit("%s: couldn't stat %s: %s", __func__,
WTMPX_FILE, strerror(errno));
close(fd);
return (0);
while (!found) {
if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) {
- logit("wtmpx_get_entry: read of %s failed: %s",
+ logit("%s: read of %s failed: %s", __func__,
WTMPX_FILE, strerror(errno));
close (fd);
return (0);
}
/*
- * Logouts are recorded as a blank username on a particular
+ * Logouts are recorded as a blank username on a particular
* line. So, we just need to find the username in struct utmpx
*/
if (wtmpx_islogin(li, &utx)) {
{
struct utmp *ut;
- if ((ut = (struct utmp *)malloc(sizeof(*ut))) == NULL) {
- logit("syslogin_perform_login: couldn't malloc()");
- return (0);
- }
+ ut = xmalloc(sizeof(*ut));
construct_utmp(li, ut);
login(ut);
free(ut);
(void)line_stripname(line, li->line, sizeof(line));
if (!logout(line))
- logit("syslogin_perform_logout: logout() returned an error");
+ logit("%s: logout() returned an error", __func__);
# ifdef HAVE_LOGWTMP
else
logwtmp(line, "", "");
case LTYPE_LOGOUT:
return (syslogin_perform_logout(li));
default:
- logit("syslogin_write_entry: Invalid type field");
+ logit("%s: Invalid type field", __func__);
return (0);
}
}
**/
#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) {
- logit("lastlog_perform_login: Couldn't stat %s: %s",
+ logit("%s: Couldn't stat %s: %s", __func__,
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:
- logit("lastlog_openseek: %.100s is not a file or directory!",
+ } 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);
}
*fd = open(lastlog_file, filemode, 0600);
if (*fd < 0) {
- debug("lastlog_openseek: Couldn't open %s: %s",
+ debug("%s: Couldn't open %s: %s", __func__,
lastlog_file, strerror(errno));
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));
if (lseek(*fd, offset, SEEK_SET) != offset) {
- logit("lastlog_openseek: %s->lseek(): %s",
- lastlog_file, strerror(errno));
+ logit("%s: %s->lseek(): %s", __func__,
+ lastlog_file, strerror(errno));
return (0);
}
}
return (1);
}
+#endif /* !LASTLOG_WRITE_PUTUTXLINE || !HAVE_GETLASTLOGXBYNAME */
-static int
-lastlog_perform_login(struct logininfo *li)
+#ifdef LASTLOG_WRITE_PUTUTXLINE
+int
+lastlog_write_entry(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("lastlog_write_filemode: Error writing to %s: %s",
- LASTLOG_FILE, strerror(errno));
- return (0);
+ switch(li->type) {
+ case LTYPE_LOGIN:
+ return 1; /* lastlog written by pututxline */
+ default:
+ logit("lastlog_write_entry: Invalid type field");
+ return 0;
}
-
- close(fd);
- return (1);
}
-
+#else /* LASTLOG_WRITE_PUTUTXLINE */
int
lastlog_write_entry(struct logininfo *li)
{
+ struct lastlog last;
+ int fd;
+
switch(li->type) {
case LTYPE_LOGIN:
- return (lastlog_perform_login(li));
+ /* 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("lastlog_write_entry: Invalid type field");
+ 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)
{
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__,
return (0);
default:
error("%s: Error reading from %s: Expecting %d, got %d",
- __func__, LASTLOG_FILE, sizeof(last), ret);
+ __func__, LASTLOG_FILE, (int)sizeof(last), ret);
return (0);
}
/* NOTREACHED */
return (0);
}
+#endif /* HAVE_GETLASTLOGXBYNAME */
#endif /* USE_LASTLOG */
+
+#ifdef USE_BTMP
+ /*
+ * Logs failed login attempts in _PATH_BTMP if that exists.
+ * The most common login failure is to give password instead of username.
+ * So the _PATH_BTMP file checked for the correct permission, so that
+ * only root can read it.
+ */
+
+void
+record_failed_login(const char *username, const char *hostname,
+ const char *ttyn)
+{
+ int fd;
+ struct utmp ut;
+ struct sockaddr_storage from;
+ socklen_t fromlen = sizeof(from);
+ struct sockaddr_in *a4;
+ struct sockaddr_in6 *a6;
+ time_t t;
+ struct stat fst;
+
+ if (geteuid() != 0)
+ return;
+ if ((fd = open(_PATH_BTMP, O_WRONLY | O_APPEND)) < 0) {
+ debug("Unable to open the btmp file %s: %s", _PATH_BTMP,
+ strerror(errno));
+ return;
+ }
+ if (fstat(fd, &fst) < 0) {
+ logit("%s: fstat of %s failed: %s", __func__, _PATH_BTMP,
+ strerror(errno));
+ goto out;
+ }
+ if((fst.st_mode & (S_IRWXG | S_IRWXO)) || (fst.st_uid != 0)){
+ logit("Excess permission or bad ownership on file %s",
+ _PATH_BTMP);
+ goto out;
+ }
+
+ memset(&ut, 0, sizeof(ut));
+ /* strncpy because we don't necessarily want nul termination */
+ strncpy(ut.ut_user, username, sizeof(ut.ut_user));
+ strlcpy(ut.ut_line, "ssh:notty", sizeof(ut.ut_line));
+
+ time(&t);
+ ut.ut_time = t; /* ut_time is not always a time_t */
+ ut.ut_type = LOGIN_PROCESS;
+ ut.ut_pid = getpid();
+
+ /* strncpy because we don't necessarily want nul termination */
+ strncpy(ut.ut_host, hostname, sizeof(ut.ut_host));
+
+ if (packet_connection_is_on_socket() &&
+ getpeername(packet_get_connection_in(),
+ (struct sockaddr *)&from, &fromlen) == 0) {
+ ipv64_normalise_mapped(&from, &fromlen);
+ if (from.ss_family == AF_INET) {
+ a4 = (struct sockaddr_in *)&from;
+ memcpy(&ut.ut_addr, &(a4->sin_addr),
+ MIN_SIZEOF(ut.ut_addr, a4->sin_addr));
+ }
+#ifdef HAVE_ADDR_V6_IN_UTMP
+ if (from.ss_family == AF_INET6) {
+ a6 = (struct sockaddr_in6 *)&from;
+ memcpy(&ut.ut_addr_v6, &(a6->sin6_addr),
+ MIN_SIZEOF(ut.ut_addr_v6, a6->sin6_addr));
+ }
+#endif
+ }
+
+ if (atomicio(vwrite, fd, &ut, sizeof(ut)) != sizeof(ut))
+ error("Failed to write to %s: %s", _PATH_BTMP,
+ strerror(errno));
+
+out:
+ close(fd);
+}
+#endif /* USE_BTMP */