[devel] [PATCH 6/6] syslogd: implement checksum chains for log entries

Alexey Gladkov legion на altlinux.ru
Вт Окт 27 14:33:51 MSK 2020


This is a mechanism for detecting intrusion into log files.

Each time the server starts, it starts creating a checksum chain for
each log file. On restart, the chain starts with an empty checksum.

A mismatch between the checkum and the server restart is a possible
intrusion and the reason for investigation.

Each entry is accompanied by a checksum of this entry with the previous
checksum. So, to check the checksum of the current record, you need to
have a previous checksum.

Signed-off-by: Alexey Gladkov <gladkov.alexey на gmail.com>
---
 Makefile                         |  14 +-
 block/bswap.h                    | 217 +++++++++++++++++++++++++++++++
 block/sha256.c                   | 202 ++++++++++++++++++++++++++++
 block/sha256.h                   |  24 ++++
 hash.h                           |  66 ++++++++++
 syslogd.c                        |  41 ++++++
 tests/log-hashes/.gitignore      |   5 +
 tests/log-hashes/check           |  28 ++++
 tests/log-hashes/check-hashes.sh |  28 ++++
 9 files changed, 622 insertions(+), 3 deletions(-)
 create mode 100644 block/bswap.h
 create mode 100644 block/sha256.c
 create mode 100644 block/sha256.h
 create mode 100644 hash.h
 create mode 100644 tests/log-hashes/.gitignore
 create mode 100755 tests/log-hashes/check
 create mode 100755 tests/log-hashes/check-hashes.sh

diff --git a/Makefile b/Makefile
index b46969f..27c415b 100644
--- a/Makefile
+++ b/Makefile
@@ -53,6 +53,10 @@ MANDIR = $(prefix)/usr/share/man
 # file system standard.
 FSSTND = -DFSSTND
 
+# The following define determines whether the syslogd should create checksum
+# chains for log entries.
+USE_CHECKSUMS = -DUSE_CHECKSUMS
+
 # The following define establishes ownership for the man pages.
 # Avery tells me that there is a difference between Debian and
 # Slackware.  Rather than choose sides I am leaving it up to the user.
@@ -67,8 +71,8 @@ MAN_PERMS = 644
 # ballot below.
 SYSLOGD_PIDNAME = -DSYSLOGD_PIDNAME=\"syslogd.pid\"
 
-SYSLOGD_FLAGS= -DSYSLOG_INET -DSYSLOG_UNIXAF -DINET6 -DNO_SCCS ${FSSTND} \
-	${SYSLOGD_PIDNAME}
+SYSLOGD_FLAGS= -DSYSLOG_INET -DSYSLOG_UNIXAF -DINET6 -DNO_SCCS \
+	${USE_CHECKSUMS} ${FSSTND} ${SYSLOGD_PIDNAME}
 SYSLOG_FLAGS= -DALLOW_KERNEL_LOGGING
 KLOGD_FLAGS = ${FSSTND} ${KLOGD_START_DELAY}
 DEB =
@@ -80,6 +84,10 @@ TESTS = \
 	priority-exclamation \
 	named-pipes
 
+ifneq (${USE_CHECKSUMS},)
+TESTS += log-hashes
+endif
+
 all: syslogd klogd
 
 test:
@@ -98,7 +106,7 @@ test:
 
 install: install_man install_exec
 
-syslogd: syslogd.o pidfile.o
+syslogd: syslogd.o pidfile.o block/sha256.o
 	${CC} ${LDFLAGS} -o $@ $^ ${LIBS}
 
 klogd:	klogd.o syslog.o pidfile.o
diff --git a/block/bswap.h b/block/bswap.h
new file mode 100644
index 0000000..e4e2573
--- /dev/null
+++ b/block/bswap.h
@@ -0,0 +1,217 @@
+#ifndef COMPAT_BSWAP_H
+#define COMPAT_BSWAP_H
+
+/*
+ * Let's make sure we always have a sane definition for ntohl()/htonl().
+ * Some libraries define those as a function call, just to perform byte
+ * shifting, bringing significant overhead to what should be a simple
+ * operation.
+ */
+
+/*
+ * Default version that the compiler ought to optimize properly with
+ * constant values.
+ */
+static inline uint32_t default_swab32(uint32_t val)
+{
+	return (((val & 0xff000000) >> 24) |
+		((val & 0x00ff0000) >>  8) |
+		((val & 0x0000ff00) <<  8) |
+		((val & 0x000000ff) << 24));
+}
+
+static inline uint64_t default_bswap64(uint64_t val)
+{
+	return (((val & (uint64_t)0x00000000000000ffULL) << 56) |
+		((val & (uint64_t)0x000000000000ff00ULL) << 40) |
+		((val & (uint64_t)0x0000000000ff0000ULL) << 24) |
+		((val & (uint64_t)0x00000000ff000000ULL) <<  8) |
+		((val & (uint64_t)0x000000ff00000000ULL) >>  8) |
+		((val & (uint64_t)0x0000ff0000000000ULL) >> 24) |
+		((val & (uint64_t)0x00ff000000000000ULL) >> 40) |
+		((val & (uint64_t)0xff00000000000000ULL) >> 56));
+}
+
+#undef bswap32
+#undef bswap64
+
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+
+#define bswap32 git_bswap32
+static inline uint32_t git_bswap32(uint32_t x)
+{
+	uint32_t result;
+	if (__builtin_constant_p(x))
+		result = default_swab32(x);
+	else
+		__asm__("bswap %0" : "=r" (result) : "0" (x));
+	return result;
+}
+
+#define bswap64 git_bswap64
+#if defined(__x86_64__)
+static inline uint64_t git_bswap64(uint64_t x)
+{
+	uint64_t result;
+	if (__builtin_constant_p(x))
+		result = default_bswap64(x);
+	else
+		__asm__("bswap %q0" : "=r" (result) : "0" (x));
+	return result;
+}
+#else
+static inline uint64_t git_bswap64(uint64_t x)
+{
+	union { uint64_t i64; uint32_t i32[2]; } tmp, result;
+	if (__builtin_constant_p(x))
+		result.i64 = default_bswap64(x);
+	else {
+		tmp.i64 = x;
+		result.i32[0] = git_bswap32(tmp.i32[1]);
+		result.i32[1] = git_bswap32(tmp.i32[0]);
+	}
+	return result.i64;
+}
+#endif
+
+#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
+
+#include <stdlib.h>
+
+#define bswap32(x) _byteswap_ulong(x)
+#define bswap64(x) _byteswap_uint64(x)
+
+#endif
+
+#if defined(bswap32)
+
+#undef ntohl
+#undef htonl
+#define ntohl(x) bswap32(x)
+#define htonl(x) bswap32(x)
+
+#endif
+
+#if defined(bswap64)
+
+#undef ntohll
+#undef htonll
+#define ntohll(x) bswap64(x)
+#define htonll(x) bswap64(x)
+
+#else
+
+#undef ntohll
+#undef htonll
+
+#if defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && defined(__BIG_ENDIAN)
+
+# define GIT_BYTE_ORDER __BYTE_ORDER
+# define GIT_LITTLE_ENDIAN __LITTLE_ENDIAN
+# define GIT_BIG_ENDIAN __BIG_ENDIAN
+
+#elif defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN)
+
+# define GIT_BYTE_ORDER BYTE_ORDER
+# define GIT_LITTLE_ENDIAN LITTLE_ENDIAN
+# define GIT_BIG_ENDIAN BIG_ENDIAN
+
+#else
+
+# define GIT_BIG_ENDIAN 4321
+# define GIT_LITTLE_ENDIAN 1234
+
+# if defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)
+#  define GIT_BYTE_ORDER GIT_BIG_ENDIAN
+# elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)
+#  define GIT_BYTE_ORDER GIT_LITTLE_ENDIAN
+# elif defined(__THW_BIG_ENDIAN__) && !defined(__THW_LITTLE_ENDIAN__)
+#  define GIT_BYTE_ORDER GIT_BIG_ENDIAN
+# elif defined(__THW_LITTLE_ENDIAN__) && !defined(__THW_BIG_ENDIAN__)
+#  define GIT_BYTE_ORDER GIT_LITTLE_ENDIAN
+# else
+#  error "Cannot determine endianness"
+# endif
+
+#endif
+
+#if GIT_BYTE_ORDER == GIT_BIG_ENDIAN
+# define ntohll(n) (n)
+# define htonll(n) (n)
+#else
+# define ntohll(n) default_bswap64(n)
+# define htonll(n) default_bswap64(n)
+#endif
+
+#endif
+
+/*
+ * Performance might be improved if the CPU architecture is OK with
+ * unaligned 32-bit loads and a fast ntohl() is available.
+ * Otherwise fall back to byte loads and shifts which is portable,
+ * and is faster on architectures with memory alignment issues.
+ */
+
+#if !defined(NO_UNALIGNED_LOADS) && ( \
+    defined(__i386__) || defined(__x86_64__) || \
+    defined(_M_IX86) || defined(_M_X64) || \
+    defined(__ppc__) || defined(__ppc64__) || \
+    defined(__powerpc__) || defined(__powerpc64__) || \
+    defined(__s390__) || defined(__s390x__))
+
+#define get_be16(p)	ntohs(*(unsigned short *)(p))
+#define get_be32(p)	ntohl(*(unsigned int *)(p))
+#define get_be64(p)	ntohll(*(uint64_t *)(p))
+#define put_be32(p, v)	do { *(unsigned int *)(p) = htonl(v); } while (0)
+#define put_be64(p, v)	do { *(uint64_t *)(p) = htonll(v); } while (0)
+
+#else
+
+static inline uint16_t get_be16(const void *ptr)
+{
+	const unsigned char *p = ptr;
+	return	(uint16_t)p[0] << 8 |
+		(uint16_t)p[1] << 0;
+}
+
+static inline uint32_t get_be32(const void *ptr)
+{
+	const unsigned char *p = ptr;
+	return	(uint32_t)p[0] << 24 |
+		(uint32_t)p[1] << 16 |
+		(uint32_t)p[2] <<  8 |
+		(uint32_t)p[3] <<  0;
+}
+
+static inline uint64_t get_be64(const void *ptr)
+{
+	const unsigned char *p = ptr;
+	return	(uint64_t)get_be32(&p[0]) << 32 |
+		(uint64_t)get_be32(&p[4]) <<  0;
+}
+
+static inline void put_be32(void *ptr, uint32_t value)
+{
+	unsigned char *p = ptr;
+	p[0] = value >> 24;
+	p[1] = value >> 16;
+	p[2] = value >>  8;
+	p[3] = value >>  0;
+}
+
+static inline void put_be64(void *ptr, uint64_t value)
+{
+	unsigned char *p = ptr;
+	p[0] = value >> 56;
+	p[1] = value >> 48;
+	p[2] = value >> 40;
+	p[3] = value >> 32;
+	p[4] = value >> 24;
+	p[5] = value >> 16;
+	p[6] = value >>  8;
+	p[7] = value >>  0;
+}
+
+#endif
+
+#endif /* COMPAT_BSWAP_H */
diff --git a/block/sha256.c b/block/sha256.c
new file mode 100644
index 0000000..b1f02be
--- /dev/null
+++ b/block/sha256.c
@@ -0,0 +1,202 @@
+#include <arpa/inet.h>
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "./sha256.h"
+#include "./bswap.h"
+
+#undef RND
+#undef BLKSIZE
+
+#define BLKSIZE blk_SHA256_BLKSIZE
+
+void blk_SHA256_Init(blk_SHA256_CTX *ctx)
+{
+	ctx->offset = 0;
+	ctx->size = 0;
+	ctx->state[0] = 0x6a09e667ul;
+	ctx->state[1] = 0xbb67ae85ul;
+	ctx->state[2] = 0x3c6ef372ul;
+	ctx->state[3] = 0xa54ff53aul;
+	ctx->state[4] = 0x510e527ful;
+	ctx->state[5] = 0x9b05688cul;
+	ctx->state[6] = 0x1f83d9abul;
+	ctx->state[7] = 0x5be0cd19ul;
+}
+
+static inline uint32_t ror(uint32_t x, unsigned n)
+{
+	return (x >> n) | (x << (32 - n));
+}
+
+static inline uint32_t ch(uint32_t x, uint32_t y, uint32_t z)
+{
+	return z ^ (x & (y ^ z));
+}
+
+static inline uint32_t maj(uint32_t x, uint32_t y, uint32_t z)
+{
+	return ((x | y) & z) | (x & y);
+}
+
+static inline uint32_t sigma0(uint32_t x)
+{
+	return ror(x, 2) ^ ror(x, 13) ^ ror(x, 22);
+}
+
+static inline uint32_t sigma1(uint32_t x)
+{
+	return ror(x, 6) ^ ror(x, 11) ^ ror(x, 25);
+}
+
+static inline uint32_t gamma0(uint32_t x)
+{
+	return ror(x, 7) ^ ror(x, 18) ^ (x >> 3);
+}
+
+static inline uint32_t gamma1(uint32_t x)
+{
+	return ror(x, 17) ^ ror(x, 19) ^ (x >> 10);
+}
+
+static void blk_SHA256_Transform(blk_SHA256_CTX *ctx, const unsigned char *buf)
+{
+
+	uint32_t S[8], W[64], t0, t1;
+	int i;
+
+	/* copy state into S */
+	for (i = 0; i < 8; i++)
+		S[i] = ctx->state[i];
+
+	/* copy the state into 512-bits into W[0..15] */
+	for (i = 0; i < 16; i++, buf += sizeof(uint32_t))
+		W[i] = get_be32(buf);
+
+	/* fill W[16..63] */
+	for (i = 16; i < 64; i++)
+		W[i] = gamma1(W[i - 2]) + W[i - 7] + gamma0(W[i - 15]) + W[i - 16];
+
+#define RND(a,b,c,d,e,f,g,h,i,ki)                    \
+	t0 = h + sigma1(e) + ch(e, f, g) + ki + W[i];   \
+	t1 = sigma0(a) + maj(a, b, c);                  \
+	d += t0;                                        \
+	h  = t0 + t1;
+
+	RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],0,0x428a2f98);
+	RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],1,0x71374491);
+	RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],2,0xb5c0fbcf);
+	RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],3,0xe9b5dba5);
+	RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],4,0x3956c25b);
+	RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],5,0x59f111f1);
+	RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],6,0x923f82a4);
+	RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],7,0xab1c5ed5);
+	RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],8,0xd807aa98);
+	RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],9,0x12835b01);
+	RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],10,0x243185be);
+	RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],11,0x550c7dc3);
+	RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],12,0x72be5d74);
+	RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],13,0x80deb1fe);
+	RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],14,0x9bdc06a7);
+	RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],15,0xc19bf174);
+	RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],16,0xe49b69c1);
+	RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],17,0xefbe4786);
+	RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],18,0x0fc19dc6);
+	RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],19,0x240ca1cc);
+	RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],20,0x2de92c6f);
+	RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],21,0x4a7484aa);
+	RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],22,0x5cb0a9dc);
+	RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],23,0x76f988da);
+	RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],24,0x983e5152);
+	RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],25,0xa831c66d);
+	RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],26,0xb00327c8);
+	RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],27,0xbf597fc7);
+	RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],28,0xc6e00bf3);
+	RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],29,0xd5a79147);
+	RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],30,0x06ca6351);
+	RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],31,0x14292967);
+	RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],32,0x27b70a85);
+	RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],33,0x2e1b2138);
+	RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],34,0x4d2c6dfc);
+	RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],35,0x53380d13);
+	RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],36,0x650a7354);
+	RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],37,0x766a0abb);
+	RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],38,0x81c2c92e);
+	RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],39,0x92722c85);
+	RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],40,0xa2bfe8a1);
+	RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],41,0xa81a664b);
+	RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],42,0xc24b8b70);
+	RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],43,0xc76c51a3);
+	RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],44,0xd192e819);
+	RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],45,0xd6990624);
+	RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],46,0xf40e3585);
+	RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],47,0x106aa070);
+	RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],48,0x19a4c116);
+	RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],49,0x1e376c08);
+	RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],50,0x2748774c);
+	RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],51,0x34b0bcb5);
+	RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],52,0x391c0cb3);
+	RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],53,0x4ed8aa4a);
+	RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],54,0x5b9cca4f);
+	RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],55,0x682e6ff3);
+	RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],56,0x748f82ee);
+	RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],57,0x78a5636f);
+	RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],58,0x84c87814);
+	RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],59,0x8cc70208);
+	RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],60,0x90befffa);
+	RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],61,0xa4506ceb);
+	RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],62,0xbef9a3f7);
+	RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],63,0xc67178f2);
+
+	for (i = 0; i < 8; i++)
+		ctx->state[i] += S[i];
+}
+
+void blk_SHA256_Update(blk_SHA256_CTX *ctx, const void *data, size_t len)
+{
+	unsigned int len_buf = ctx->size & 63;
+
+	ctx->size += len;
+
+	/* Read the data into buf and process blocks as they get full */
+	if (len_buf) {
+		unsigned int left = 64 - len_buf;
+		if (len < left)
+			left = len;
+		memcpy(len_buf + ctx->buf, data, left);
+		len_buf = (len_buf + left) & 63;
+		len -= left;
+		data = ((const char *)data + left);
+		if (len_buf)
+			return;
+		blk_SHA256_Transform(ctx, ctx->buf);
+	}
+	while (len >= 64) {
+		blk_SHA256_Transform(ctx, data);
+		data = ((const char *)data + 64);
+		len -= 64;
+	}
+	if (len)
+		memcpy(ctx->buf, data, len);
+}
+
+void blk_SHA256_Final(unsigned char *digest, blk_SHA256_CTX *ctx)
+{
+	static const unsigned char pad[64] = { 0x80 };
+	unsigned int padlen[2];
+	int i;
+
+	/* Pad with a binary 1 (ie 0x80), then zeroes, then length */
+	padlen[0] = htonl((uint32_t)(ctx->size >> 29));
+	padlen[1] = htonl((uint32_t)(ctx->size << 3));
+
+	i = ctx->size & 63;
+	blk_SHA256_Update(ctx, pad, 1 + (63 & (55 - i)));
+	blk_SHA256_Update(ctx, padlen, 8);
+
+	/* copy output */
+	for (i = 0; i < 8; i++, digest += sizeof(uint32_t))
+		put_be32(digest, ctx->state[i]);
+}
diff --git a/block/sha256.h b/block/sha256.h
new file mode 100644
index 0000000..5099d64
--- /dev/null
+++ b/block/sha256.h
@@ -0,0 +1,24 @@
+#ifndef SHA256_BLOCK_SHA256_H
+#define SHA256_BLOCK_SHA256_H
+
+#define blk_SHA256_BLKSIZE 64
+
+struct blk_SHA256_CTX {
+	uint32_t state[8];
+	uint64_t size;
+	uint32_t offset;
+	uint8_t buf[blk_SHA256_BLKSIZE];
+};
+
+typedef struct blk_SHA256_CTX blk_SHA256_CTX;
+
+void blk_SHA256_Init(blk_SHA256_CTX *ctx);
+void blk_SHA256_Update(blk_SHA256_CTX *ctx, const void *data, size_t len);
+void blk_SHA256_Final(unsigned char *digest, blk_SHA256_CTX *ctx);
+
+#define platform_SHA256_CTX blk_SHA256_CTX
+#define platform_SHA256_Init blk_SHA256_Init
+#define platform_SHA256_Update blk_SHA256_Update
+#define platform_SHA256_Final blk_SHA256_Final
+
+#endif
diff --git a/hash.h b/hash.h
new file mode 100644
index 0000000..6fbed5c
--- /dev/null
+++ b/hash.h
@@ -0,0 +1,66 @@
+#ifndef SYSLOGD_HASH_H_
+#define SYSLOGD_HASH_H_
+
+#include "./block/sha256.h"
+
+/* A suitably aligned type for stack allocations of hash contexts. */
+union hash_ctx {
+	platform_SHA256_CTX sha256;
+};
+typedef union hash_ctx hash_ctx_t;
+
+#ifdef USE_CHECKSUMS
+
+/* The length in bytes and in hex digits (SHA-256 value). */
+#define HASH_RAWSZ 32
+#define HASH_HEXSZ (2 * HASH_RAWSZ)
+
+#define HASH_NAME   "sha256"
+#define HASH_NAMESZ sizeof(HASH_NAME) - 1
+
+#define EMPTY_HASH_LITERAL \
+	HASH_NAME ":e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
+
+static inline void hash_init(hash_ctx_t *ctx)
+{
+        platform_SHA256_Init(&ctx->sha256);
+}
+
+static inline void hash_update(hash_ctx_t *ctx, const void *data, size_t len)
+{
+        platform_SHA256_Update(&ctx->sha256, data, len);
+}
+
+static inline void hash_final(unsigned char *hash, hash_ctx_t *ctx)
+{
+        platform_SHA256_Final(hash, &ctx->sha256);
+}
+
+#else /* USE_CHECKSUMS */
+
+#define HASH_RAWSZ 0
+#define HASH_HEXSZ 0
+
+#define HASH_NAME   ""
+#define HASH_NAMESZ 0
+
+#define EMPTY_HASH_LITERAL ""
+
+
+static inline void hash_init(hash_ctx_t *ctx)
+{
+	return;
+}
+
+static inline void hash_update(hash_ctx_t *ctx, const void *in, size_t len)
+{
+	return;
+}
+
+static inline void hash_final(unsigned char *hash, hash_ctx_t *ctx)
+{
+	return;
+}
+
+#endif /* USE_CHECKSUMS */
+#endif
diff --git a/syslogd.c b/syslogd.c
index 4c3c85f..dac7c37 100644
--- a/syslogd.c
+++ b/syslogd.c
@@ -61,6 +61,7 @@
 #include "pidfile.h"
 #include "version.h"
 #include "attribute.h"
+#include "hash.h"
 
 #if defined(__linux__)
 #include <paths.h>
@@ -186,6 +187,8 @@ struct filed {
 	int	f_prevcount;			/* repetition cnt of prevline */
 	int	f_repeatcount;			/* number of "repeated" msgs */
 	int	f_flags;			/* store some additional flags */
+	/* hash of last logged message */
+	char    f_prevhash[HASH_NAMESZ + 1 + HASH_HEXSZ + 1];
 };
 
 /*
@@ -292,6 +295,7 @@ struct sourceinfo {
 enum log_format_type {
 	LOG_FORMAT_NONE = 0,
 	LOG_FORMAT_BOL,
+	LOG_FORMAT_HASH,
 	LOG_FORMAT_TIME,
 	LOG_FORMAT_HOST,
 	LOG_FORMAT_MSG,
@@ -391,6 +395,7 @@ static void allocate_log(void);
 int set_log_format_field(struct log_format *log_fmt, size_t i, enum log_format_type t, char *s, size_t n)
 	SYSKLOGD_NONNULL((1));
 int parse_log_format(struct log_format *log_fmt, char *s);
+void calculate_digest(struct filed *f, struct log_format *log_fmt);
 void sighup_handler(int);
 
 #ifdef SYSLOG_UNIXAF
@@ -1528,6 +1533,36 @@ void clear_record_fields(struct log_format *log_fmt)
 	}
 }
 
+void calculate_digest(struct filed *f, struct log_format *log_fmt)
+{
+	int i, n;
+	unsigned char digest[HASH_RAWSZ];
+	hash_ctx_t hash_ctx;
+
+	if (!(log_fmt->f_mask | (1 << LOG_FORMAT_HASH)))
+		return;
+
+	digest[0] = 0;
+
+	hash_init(&hash_ctx);
+	for (i = 0; i < LOG_FORMAT_IOVEC_MAX; i++)
+		hash_update(&hash_ctx, log_fmt->iov[i].iov_base, log_fmt->iov[i].iov_len);
+	hash_final(digest, &hash_ctx);
+
+	strncpy(f->f_prevhash, HASH_NAME, sizeof(f->f_prevhash));
+	n = HASH_NAMESZ;
+
+	strncpy(f->f_prevhash + n, ":", sizeof(f->f_prevhash) - n);
+	n += 1;
+
+	for (i = 0; i < HASH_RAWSZ; i++) {
+		snprintf(f->f_prevhash + n, sizeof(f->f_prevhash) - n, "%02x", digest[i]);
+		n += 2;
+	}
+	f->f_prevhash[n] = 0;
+	return;
+}
+
 void fprintlog(struct filed *f, char *from, int flags, char *msg)
 {
 	char repbuf[80];
@@ -1545,6 +1580,7 @@ void fprintlog(struct filed *f, char *from, int flags, char *msg)
 
 	set_record_field(&log_fmt, LOG_FORMAT_TIME, f->f_lasttime, 15);
 	set_record_field(&log_fmt, LOG_FORMAT_HOST, f->f_prevhost, -1);
+	set_record_field(&log_fmt, LOG_FORMAT_HASH, f->f_prevhash, -1);
 	if (msg) {
 		set_record_field(&log_fmt, LOG_FORMAT_MSG, msg, -1);
 	} else if (f->f_prevcount > 1) {
@@ -1690,6 +1726,8 @@ void fprintlog(struct filed *f, char *from, int flags, char *msg)
 		if (f->f_file == -1)
 			break;
 
+		calculate_digest(f, &log_fmt);
+
 		if (writev(f->f_file, log_fmt.iov, LOG_FORMAT_IOVEC_MAX) < 0) {
 			int e = errno;
 
@@ -1738,6 +1776,7 @@ void fprintlog(struct filed *f, char *from, int flags, char *msg)
 		f->f_time = now;
 		verbosef("\n");
 		set_record_field(&log_fmt, LOG_FORMAT_EOL, "\r\n", 2);
+		calculate_digest(f, &log_fmt);
 		wallmsg(f, &log_fmt);
 		break;
 	} /* switch */
@@ -2658,6 +2697,7 @@ static void allocate_log(void)
 	 */
 	++nlogs;
 	memset(&Files[nlogs], '\0', sizeof(struct filed));
+	strncpy(Files[nlogs].f_prevhash, EMPTY_HASH_LITERAL, sizeof(Files[nlogs].f_prevhash));
 	return;
 }
 
@@ -2754,6 +2794,7 @@ int parse_log_format(struct log_format *log_fmt, char *str)
 				case 't': f_type = LOG_FORMAT_TIME; break;
 				case 'h': f_type = LOG_FORMAT_HOST; break;
 				case 'm': f_type = LOG_FORMAT_MSG;  break;
+				case 'H': f_type = LOG_FORMAT_HASH; break;
 				case '%':
 					special = 0;
 					goto create_special;
diff --git a/tests/log-hashes/.gitignore b/tests/log-hashes/.gitignore
new file mode 100644
index 0000000..5d86449
--- /dev/null
+++ b/tests/log-hashes/.gitignore
@@ -0,0 +1,5 @@
+output
+log
+pipe
+syslog.conf
+*.log
diff --git a/tests/log-hashes/check b/tests/log-hashes/check
new file mode 100755
index 0000000..c8cfcbb
--- /dev/null
+++ b/tests/log-hashes/check
@@ -0,0 +1,28 @@
+#!/bin/sh -efu
+
+. "$TOPDIR/tests/common.sh"
+
+WORKDIR="$1"
+
+prepare
+
+cat > "$WORKDIR/syslog.conf" <<EOF
+log_format: %H %t %h %m
+*.*     $WORKDIR/output/everything.log
+*.info  $WORKDIR/syslog-mark.log
+EOF
+
+run_syslogd &
+sleep 3
+
+for f in $facilities; do
+	for p in $priorities; do
+		logger \
+			--socket "$WORKDIR/log" \
+			--socket-errors=on \
+			-p "$f.$p" "TEST $f.$p"
+	done
+done
+
+wait_mark
+"$WORKDIR/check-hashes.sh" "$WORKDIR/output/everything.log"
diff --git a/tests/log-hashes/check-hashes.sh b/tests/log-hashes/check-hashes.sh
new file mode 100755
index 0000000..c65c8de
--- /dev/null
+++ b/tests/log-hashes/check-hashes.sh
@@ -0,0 +1,28 @@
+#!/bin/sh -efu
+
+logfile="$1"
+
+empty_hash="sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
+
+lineno=1
+prevhash="$empty_hash"
+prevline=
+while read -r logline; do
+	loghash="${logline%% *}"
+	msg="${logline#$loghash }"
+
+	hash="sha256:$(printf '%s %s\n' "$prevhash" "$msg" |sha256sum)" ||:
+	hash="${hash%% *}"
+
+	if [ "$hash" != "$loghash" ]; then
+		printf >&2 'ERROR: hash chain broken at lineno=%d\n' "$lineno"
+		printf >&2 'expected hash: %s\n' "$hash"
+		printf >&2 '  logged hash: %s\n' "$loghash"
+		exit 1
+	fi
+
+	prevhash="$loghash"
+	prevline="$logline"
+
+	lineno=$(($lineno + 1))
+done < "$logfile"
-- 
2.25.4



Подробная информация о списке рассылки Devel