From: Alexey Gladkov <legion@altlinux.ru> To: ALT Devel discussion list <devel@lists.altlinux.org> Subject: [devel] [PATCH 6/6] syslogd: implement checksum chains for log entries Date: Tue, 27 Oct 2020 12:33:51 +0100 Message-ID: <20201027113351.3373843-7-legion@altlinux.ru> (raw) In-Reply-To: <20201027113351.3373843-1-legion@altlinux.ru> 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
next prev parent reply other threads:[~2020-10-27 11:33 UTC|newest] Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top 2020-10-27 11:33 [devel] [PATCH 0/6] sysklogd: implement checkum " Alexey Gladkov 2020-10-27 11:33 ` [devel] [PATCH 1/6] Optimize the filling of the record fields Alexey Gladkov 2020-10-27 11:33 ` [devel] [PATCH 2/6] syslogd: make logerror is printf-like Alexey Gladkov 2020-10-27 11:33 ` [devel] [PATCH 3/6] syslogd: Implement customization of log file records Alexey Gladkov 2020-10-27 11:33 ` [devel] [PATCH 4/6] syslogd: no need to try to substitute a field in a record if it's not in the log_format Alexey Gladkov 2020-10-27 11:33 ` [devel] [PATCH 5/6] Makefile: use make variables instead of a hardcoded list Alexey Gladkov 2020-10-27 11:33 ` Alexey Gladkov [this message] 2020-10-27 11:44 ` [devel] [PATCH 0/6] sysklogd: implement checkum chains for log entries Alexey V. Vissarionov 2020-10-27 12:13 ` Alexey Gladkov
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=20201027113351.3373843-7-legion@altlinux.ru \ --to=legion@altlinux.ru \ --cc=devel@lists.altlinux.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
ALT Linux Team development discussions This inbox may be cloned and mirrored by anyone: git clone --mirror http://lore.altlinux.org/devel/0 devel/git/0.git # If you have public-inbox 1.1+ installed, you may # initialize and index your mirror using the following commands: public-inbox-init -V2 devel devel/ http://lore.altlinux.org/devel \ devel@altlinux.org devel@altlinux.ru devel@lists.altlinux.org devel@lists.altlinux.ru devel@linux.iplabs.ru mandrake-russian@linuxteam.iplabs.ru sisyphus@linuxteam.iplabs.ru public-inbox-index devel Example config snippet for mirrors. Newsgroup available over NNTP: nntp://lore.altlinux.org/org.altlinux.lists.devel AGPL code for this site: git clone https://public-inbox.org/public-inbox.git