[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