* [devel] [PATCH 1/6] Optimize the filling of the record fields
2020-10-27 11:33 [devel] [PATCH 0/6] sysklogd: implement checkum chains for log entries Alexey Gladkov
@ 2020-10-27 11:33 ` Alexey Gladkov
2020-10-27 11:33 ` [devel] [PATCH 2/6] syslogd: make logerror is printf-like Alexey Gladkov
` (5 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Alexey Gladkov @ 2020-10-27 11:33 UTC (permalink / raw)
To: ALT Devel discussion list
Signed-off-by: Alexey Gladkov <gladkov.alexey@gmail.com>
---
syslogd.c | 84 ++++++++++++++++++++++++++++---------------------------
1 file changed, 43 insertions(+), 41 deletions(-)
diff --git a/syslogd.c b/syslogd.c
index 0f2260d..55281bd 100644
--- a/syslogd.c
+++ b/syslogd.c
@@ -289,6 +289,16 @@ struct sourceinfo {
unsigned int flags;
} sinfo;
+enum record_fields_type {
+ RECORD_FIELD_TIME = 0,
+ RECORD_FIELD_SEP1,
+ RECORD_FIELD_HOST,
+ RECORD_FIELD_SEP2,
+ RECORD_FIELD_MSG,
+ RECORD_FIELD_EOL,
+ RECORD_FIELD_COUNTS,
+};
+
static int Debug; /* debug flag */
static int Compress = 1; /* compress repeated messages flag */
static char LocalHostName[MAXHOSTNAMELEN+1]; /* our hostname */
@@ -332,7 +342,7 @@ void printline(const struct sourceinfo* const, char *msg);
void logmsg(int pri, char *msg, const struct sourceinfo* const, int flags);
void fprintlog(register struct filed *f, char *from, int flags, char *msg);
void endtty(int);
-void wallmsg(register struct filed *f, struct iovec *iov, size_t iovsz);
+void wallmsg(register struct filed *f, struct iovec iov[RECORD_FIELD_COUNTS]);
void reapchild(int);
const char *cvtaddr(struct sockaddr_storage *f, int len);
const char *cvthname(struct sockaddr_storage *f, int len);
@@ -1443,10 +1453,16 @@ finish:
sigprocmask(SIG_UNBLOCK, &mask, NULL);
}
+static inline void set_record_field(struct iovec iov[RECORD_FIELD_COUNTS],
+ enum record_fields_type name, char *value, size_t len)
+{
+ iov[name].iov_base = value;
+ iov[name].iov_len = len == -1 ? strlen(value) : len;
+}
+
void fprintlog(struct filed *f, char *from, int flags, char *msg)
{
- struct iovec iov[6];
- register struct iovec *v = iov;
+ struct iovec iov[RECORD_FIELD_COUNTS];
char repbuf[80];
#ifdef SYSLOG_INET
register int l;
@@ -1458,31 +1474,20 @@ void fprintlog(struct filed *f, char *from, int flags, char *msg)
verbosef("Called fprintlog, ");
- v->iov_base = f->f_lasttime;
- v->iov_len = 15;
- v++;
- v->iov_base = " ";
- v->iov_len = 1;
- v++;
- v->iov_base = f->f_prevhost;
- v->iov_len = strlen(v->iov_base);
- v++;
- v->iov_base = " ";
- v->iov_len = 1;
- v++;
+ set_record_field(iov, RECORD_FIELD_TIME, f->f_lasttime, 15);
+ set_record_field(iov, RECORD_FIELD_SEP1, " ", 1);
+ set_record_field(iov, RECORD_FIELD_HOST, f->f_prevhost, -1);
+ set_record_field(iov, RECORD_FIELD_SEP2, " ", 1);
+
if (msg) {
- v->iov_base = msg;
- v->iov_len = strlen(msg);
+ set_record_field(iov, RECORD_FIELD_MSG, msg, -1);
} else if (f->f_prevcount > 1) {
(void) snprintf(repbuf, sizeof(repbuf), "last message repeated %d times",
f->f_prevcount);
- v->iov_base = repbuf;
- v->iov_len = strlen(repbuf);
+ set_record_field(iov, RECORD_FIELD_MSG, repbuf, -1);
} else {
- v->iov_base = f->f_prevline;
- v->iov_len = f->f_prevlen;
+ set_record_field(iov, RECORD_FIELD_MSG, f->f_prevline, f->f_prevlen);
}
- v++;
verbosef("logging to %s", TypeNames[f->f_type]);
@@ -1508,14 +1513,13 @@ void fprintlog(struct filed *f, char *from, int flags, char *msg)
(long)(INET_SUSPEND_TIME - fwd_suspend));
}
break;
-
/*
* The trick is to wait some time, then retry to get the
* address. If that fails retry x times and then give up.
*
* You'll run into this problem mostly if the name server you
* need for resolving the address is on the same machine, but
- * is started after syslogd.
+ * is started after syslogd.
*/
case F_FORW_UNKN:
verbosef(" %s\n", f->f_un.f_forw.f_hname);
@@ -1549,7 +1553,7 @@ void fprintlog(struct filed *f, char *from, int flags, char *msg)
break;
case F_FORW:
- /*
+ /*
* Don't send any message to a remote host if it
* already comes from one. (we don't care 'bout who
* sent the message, we don't send it anyway) -Joey
@@ -1562,7 +1566,7 @@ void fprintlog(struct filed *f, char *from, int flags, char *msg)
int i;
f->f_time = now;
(void) snprintf(line, sizeof(line), "<%d>%s", f->f_prevpri, \
- (char *) iov[4].iov_base);
+ (char *) iov[RECORD_FIELD_MSG].iov_base);
l = strlen(line);
if (l > MAXLINE)
l = MAXLINE;
@@ -1582,7 +1586,7 @@ void fprintlog(struct filed *f, char *from, int flags, char *msg)
break;
}
if (err != -1) {
- verbosef("INET sendto error: %d = %s.\n",
+ verbosef("INET sendto error: %d = %s.\n",
err, strerror(err));
f->f_type = F_FORW_SUSP;
errno = err;
@@ -1597,7 +1601,7 @@ void fprintlog(struct filed *f, char *from, int flags, char *msg)
#ifdef UNIXPC
if (1) {
#else
- if (flags & IGN_CONS) {
+ if (flags & IGN_CONS) {
#endif
verbosef(" (ignored).\n");
break;
@@ -1610,11 +1614,9 @@ void fprintlog(struct filed *f, char *from, int flags, char *msg)
f->f_time = now;
verbosef(" %s\n", f->f_un.f_fname);
if (f->f_type == F_TTY || f->f_type == F_CONSOLE) {
- v->iov_base = "\r\n";
- v->iov_len = 2;
+ set_record_field(iov, RECORD_FIELD_EOL, "\r\n", 2);
} else {
- v->iov_base = "\n";
- v->iov_len = 1;
+ set_record_field(iov, RECORD_FIELD_EOL, "\n", 1);
}
again:
/* f->f_file == -1 is an indicator that we couldn't
@@ -1622,7 +1624,7 @@ void fprintlog(struct filed *f, char *from, int flags, char *msg)
if (f->f_file == -1)
break;
- if (writev(f->f_file, iov, 6) < 0) {
+ if (writev(f->f_file, iov, RECORD_FIELD_COUNTS) < 0) {
int e = errno;
/* If a named pipe is full, just ignore it for now */
@@ -1669,9 +1671,8 @@ void fprintlog(struct filed *f, char *from, int flags, char *msg)
case F_WALL:
f->f_time = now;
verbosef("\n");
- v->iov_base = "\r\n";
- v->iov_len = 2;
- wallmsg(f, iov, 6);
+ set_record_field(iov, RECORD_FIELD_EOL, "\r\n", 2);
+ wallmsg(f, iov);
break;
} /* switch */
if (f->f_type != F_FORW_UNKN)
@@ -1693,7 +1694,7 @@ void endtty(int sig)
* world, or a list of approved users.
*/
-void wallmsg(struct filed *f, struct iovec *iov, size_t iovsz)
+void wallmsg(struct filed *f, struct iovec iov[RECORD_FIELD_COUNTS])
{
char p[sizeof (_PATH_DEV) + UNAMESZ];
register int i;
@@ -1722,7 +1723,7 @@ void wallmsg(struct filed *f, struct iovec *iov, size_t iovsz)
(void) snprintf(greetings, sizeof(greetings),
"\r\n\7Message from syslogd@%s at %.24s ...\r\n",
- (char *) iov[2].iov_base, ctime(&now));
+ (char *) iov[RECORD_FIELD_HOST].iov_base, ctime(&now));
len = strlen(greetings);
/* scan the user login file */
@@ -1768,9 +1769,10 @@ void wallmsg(struct filed *f, struct iovec *iov, size_t iovsz)
if (ttyf >= 0) {
struct stat statb;
- if (fstat(ttyf, &statb) == 0 &&
- (statb.st_mode & S_IWRITE))
- (void) writev(ttyf, iov, iovsz);
+ if (!fstat(ttyf, &statb) && (statb.st_mode & S_IWRITE)) {
+ if (writev(ttyf, iov, RECORD_FIELD_COUNTS) < 0)
+ errno = 0; /* ignore */
+ }
close(ttyf);
ttyf = -1;
}
--
2.25.4
^ permalink raw reply [flat|nested] 9+ messages in thread
* [devel] [PATCH 3/6] syslogd: Implement customization of log file records
2020-10-27 11:33 [devel] [PATCH 0/6] sysklogd: implement checkum chains for log entries 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 ` 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
` (3 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Alexey Gladkov @ 2020-10-27 11:33 UTC (permalink / raw)
To: ALT Devel discussion list
Signed-off-by: Alexey Gladkov <gladkov.alexey@gmail.com>
---
syslog.conf | 17 +++
syslogd.c | 303 ++++++++++++++++++++++++++++++++++++++++++++--------
2 files changed, 277 insertions(+), 43 deletions(-)
diff --git a/syslog.conf b/syslog.conf
index 801f35f..83fa433 100644
--- a/syslog.conf
+++ b/syslog.conf
@@ -1,4 +1,21 @@
# /etc/syslog.conf - Configuration file for syslogd(8)
+#
+# Specifies log format. The log format can contain placeholders for log message
+# elements:
+# %t - time of last occurrence.
+# %h - host from which this message.
+# %m - message text.
+# %% - '%' character.
+# \\ - '\' character.
+# \b - backspace character.
+# \t - horizontal tab character.
+# \f - form feed character.
+# \n - new line character.
+# \r - carriage return character.
+#
+# Default:
+#log_format: %t %h %m
+
#
# For info about the format of this file, see "man syslog.conf".
#
diff --git a/syslogd.c b/syslogd.c
index d4aa9e4..214c4e9 100644
--- a/syslogd.c
+++ b/syslogd.c
@@ -289,16 +289,38 @@ struct sourceinfo {
unsigned int flags;
} sinfo;
-enum record_fields_type {
- RECORD_FIELD_TIME = 0,
- RECORD_FIELD_SEP1,
- RECORD_FIELD_HOST,
- RECORD_FIELD_SEP2,
- RECORD_FIELD_MSG,
- RECORD_FIELD_EOL,
- RECORD_FIELD_COUNTS,
+enum log_format_type {
+ LOG_FORMAT_NONE = 0,
+ LOG_FORMAT_BOL,
+ LOG_FORMAT_TIME,
+ LOG_FORMAT_HOST,
+ LOG_FORMAT_MSG,
+ LOG_FORMAT_EOL,
+ LOG_FORMAT_COUNTS,
};
+#define LOG_FORMAT_REPEAT_MAX 5
+#define LOG_FORMAT_FIELDS_MAX LOG_FORMAT_COUNTS * LOG_FORMAT_REPEAT_MAX
+#define LOG_FORMAT_IOVEC_MAX LOG_FORMAT_FIELDS_MAX * 2 + 1
+
+struct log_format_field {
+ enum log_format_type f_type;
+ struct iovec *f_iov;
+};
+
+struct log_format {
+ char *line;
+
+ struct iovec *iov;
+ size_t iovec_nr;
+
+ struct log_format_field *fields;
+ size_t fields_nr;
+};
+
+static struct log_format log_fmt = { 0 };
+static long int iovec_max = 0;
+
static int Debug; /* debug flag */
static int Compress = 1; /* compress repeated messages flag */
static char LocalHostName[MAXHOSTNAMELEN+1]; /* our hostname */
@@ -307,6 +329,7 @@ static char *emptystring = "";
static int InetInuse = 0; /* non-zero if INET sockets are being used */
static int *finet = NULL; /* Internet datagram sockets */
static int Initialized = 0; /* set when we have initialized ourselves */
+static int LogFormatInitialized = 0; /* set when we have initialized log_format */
static int MarkInterval = 20 * 60; /* interval between marks in seconds */
#ifdef INET6
static int family = PF_UNSPEC; /* protocol family (IPv4, IPv6 or both) */
@@ -340,9 +363,15 @@ void untty(void);
void printchopped(const struct sourceinfo* const, char *msg, size_t len, int fd);
void printline(const struct sourceinfo* const, char *msg);
void logmsg(int pri, char *msg, const struct sourceinfo* const, int flags);
+char *get_record_field(struct log_format *log_fmt, enum log_format_type name)
+ SYSKLOGD_NONNULL((1));
+void clear_record_fields(struct log_format *log_fmt)
+ SYSKLOGD_NONNULL((1));
+void set_record_field(struct log_format *log_fmt, enum log_format_type name, char *value, ssize_t len)
+ SYSKLOGD_NONNULL((1));
void fprintlog(register struct filed *f, char *from, int flags, char *msg);
void endtty(int);
-void wallmsg(register struct filed *f, struct iovec iov[RECORD_FIELD_COUNTS]);
+void wallmsg(register struct filed *f, struct log_format *log_fmt);
void reapchild(int);
const char *cvtaddr(struct sockaddr_storage *f, int len);
const char *cvthname(struct sockaddr_storage *f, int len);
@@ -358,6 +387,9 @@ int decode(char *name, struct code *codetab);
static void verbosef(char *, ...)
SYSKLOGD_FORMAT((__printf__, 1, 2)) SYSKLOGD_NONNULL((1));
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 sighup_handler(int);
#ifdef SYSLOG_UNIXAF
@@ -648,6 +680,9 @@ int main(int argc, char **argv)
if (funix_dir && *funix_dir)
add_funix_dir(funix_dir);
+ if (parse_log_format(&log_fmt, "%t %h %m") < 0)
+ exit(1);
+
if ( !(Debug || NoFork) )
{
verbosef("Checking pidfile.\n");
@@ -1454,16 +1489,38 @@ finish:
sigprocmask(SIG_UNBLOCK, &mask, NULL);
}
-static inline void set_record_field(struct iovec iov[RECORD_FIELD_COUNTS],
- enum record_fields_type name, char *value, size_t len)
+char *get_record_field(struct log_format *log_fmt, enum log_format_type name)
{
- iov[name].iov_base = value;
- iov[name].iov_len = len == -1 ? strlen(value) : len;
+ for (int i = 0; i < LOG_FORMAT_FIELDS_MAX && log_fmt->fields[i].f_iov; i++) {
+ if (log_fmt->fields[i].f_type == name)
+ return log_fmt->fields[i].f_iov->iov_base;
+ }
+ return NULL;
+}
+
+void set_record_field(struct log_format *log_fmt,
+ enum log_format_type name, char *value, ssize_t len)
+{
+ size_t iov_len = len == -1 ? strlen(value) : len;
+
+ for (int i = 0; i < LOG_FORMAT_FIELDS_MAX && log_fmt->fields[i].f_iov; i++) {
+ if (log_fmt->fields[i].f_type == name) {
+ log_fmt->fields[i].f_iov->iov_base = value;
+ log_fmt->fields[i].f_iov->iov_len = iov_len;
+ }
+ }
+}
+
+void clear_record_fields(struct log_format *log_fmt)
+{
+ for (int i = 0; i < LOG_FORMAT_FIELDS_MAX && log_fmt->fields[i].f_iov; i++) {
+ log_fmt->fields[i].f_iov->iov_base = NULL;
+ log_fmt->fields[i].f_iov->iov_len = 0;
+ }
}
void fprintlog(struct filed *f, char *from, int flags, char *msg)
{
- struct iovec iov[RECORD_FIELD_COUNTS];
char repbuf[80];
#ifdef SYSLOG_INET
register int l;
@@ -1475,19 +1532,18 @@ void fprintlog(struct filed *f, char *from, int flags, char *msg)
verbosef("Called fprintlog, ");
- set_record_field(iov, RECORD_FIELD_TIME, f->f_lasttime, 15);
- set_record_field(iov, RECORD_FIELD_SEP1, " ", 1);
- set_record_field(iov, RECORD_FIELD_HOST, f->f_prevhost, -1);
- set_record_field(iov, RECORD_FIELD_SEP2, " ", 1);
+ clear_record_fields(&log_fmt);
+ set_record_field(&log_fmt, LOG_FORMAT_TIME, f->f_lasttime, 15);
+ set_record_field(&log_fmt, LOG_FORMAT_HOST, f->f_prevhost, -1);
if (msg) {
- set_record_field(iov, RECORD_FIELD_MSG, msg, -1);
+ set_record_field(&log_fmt, LOG_FORMAT_MSG, msg, -1);
} else if (f->f_prevcount > 1) {
(void) snprintf(repbuf, sizeof(repbuf), "last message repeated %d times",
f->f_prevcount);
- set_record_field(iov, RECORD_FIELD_MSG, repbuf, -1);
+ set_record_field(&log_fmt, LOG_FORMAT_MSG, repbuf, -1);
} else {
- set_record_field(iov, RECORD_FIELD_MSG, f->f_prevline, f->f_prevlen);
+ set_record_field(&log_fmt, LOG_FORMAT_MSG, f->f_prevline, f->f_prevlen);
}
verbosef("logging to %s", TypeNames[f->f_type]);
@@ -1567,7 +1623,7 @@ void fprintlog(struct filed *f, char *from, int flags, char *msg)
int i;
f->f_time = now;
(void) snprintf(line, sizeof(line), "<%d>%s", f->f_prevpri, \
- (char *) iov[RECORD_FIELD_MSG].iov_base);
+ (char *) log_fmt.iov[LOG_FORMAT_MSG].iov_base);
l = strlen(line);
if (l > MAXLINE)
l = MAXLINE;
@@ -1615,9 +1671,9 @@ void fprintlog(struct filed *f, char *from, int flags, char *msg)
f->f_time = now;
verbosef(" %s\n", f->f_un.f_fname);
if (f->f_type == F_TTY || f->f_type == F_CONSOLE) {
- set_record_field(iov, RECORD_FIELD_EOL, "\r\n", 2);
+ set_record_field(&log_fmt, LOG_FORMAT_EOL, "\r\n", 2);
} else {
- set_record_field(iov, RECORD_FIELD_EOL, "\n", 1);
+ set_record_field(&log_fmt, LOG_FORMAT_EOL, "\n", 1);
}
again:
/* f->f_file == -1 is an indicator that we couldn't
@@ -1625,7 +1681,7 @@ void fprintlog(struct filed *f, char *from, int flags, char *msg)
if (f->f_file == -1)
break;
- if (writev(f->f_file, iov, RECORD_FIELD_COUNTS) < 0) {
+ if (writev(f->f_file, log_fmt.iov, LOG_FORMAT_IOVEC_MAX) < 0) {
int e = errno;
/* If a named pipe is full, just ignore it for now */
@@ -1672,8 +1728,8 @@ void fprintlog(struct filed *f, char *from, int flags, char *msg)
case F_WALL:
f->f_time = now;
verbosef("\n");
- set_record_field(iov, RECORD_FIELD_EOL, "\r\n", 2);
- wallmsg(f, iov);
+ set_record_field(&log_fmt, LOG_FORMAT_EOL, "\r\n", 2);
+ wallmsg(f, &log_fmt);
break;
} /* switch */
if (f->f_type != F_FORW_UNKN)
@@ -1695,25 +1751,22 @@ void endtty(int sig)
* world, or a list of approved users.
*/
-void wallmsg(struct filed *f, struct iovec iov[RECORD_FIELD_COUNTS])
+void wallmsg(struct filed *f, struct log_format *log_fmt)
{
char p[sizeof (_PATH_DEV) + UNAMESZ];
register int i;
- int ttyf, len;
+ int ttyf;
static int reenter = 0;
struct utmp ut;
struct utmp *uptr;
char greetings[200];
- (void) &len;
-
if (reenter++)
return;
/* open the user login file */
setutent();
-
/*
* Might as well fork instead of using nonblocking I/O
* and doing notty().
@@ -1722,10 +1775,13 @@ void wallmsg(struct filed *f, struct iovec iov[RECORD_FIELD_COUNTS])
(void) signal(SIGTERM, SIG_DFL);
(void) alarm(0);
- (void) snprintf(greetings, sizeof(greetings),
- "\r\n\7Message from syslogd@%s at %.24s ...\r\n",
- (char *) iov[RECORD_FIELD_HOST].iov_base, ctime(&now));
- len = strlen(greetings);
+ if (f->f_type == F_WALL) {
+ snprintf(greetings, sizeof(greetings),
+ "\r\n\7Message from syslogd@%s at %.24s ...\r\n",
+ get_record_field(log_fmt, LOG_FORMAT_HOST), ctime(&now));
+
+ set_record_field(log_fmt, LOG_FORMAT_BOL, greetings, -1);
+ }
/* scan the user login file */
while ((uptr = getutent())) {
@@ -1757,11 +1813,6 @@ void wallmsg(struct filed *f, struct iovec iov[RECORD_FIELD_COUNTS])
strcpy(p, _PATH_DEV);
strncat(p, ut.ut_line, UNAMESZ);
- if (f->f_type == F_WALL) {
- iov[0].iov_base = greetings;
- iov[0].iov_len = len;
- iov[1].iov_len = 0;
- }
if (setjmp(ttybuf) == 0) {
(void) signal(SIGALRM, endtty);
(void) alarm(15);
@@ -1771,7 +1822,7 @@ void wallmsg(struct filed *f, struct iovec iov[RECORD_FIELD_COUNTS])
struct stat statb;
if (!fstat(ttyf, &statb) && (statb.st_mode & S_IWRITE)) {
- if (writev(ttyf, iov, RECORD_FIELD_COUNTS) < 0)
+ if (writev(ttyf, log_fmt->iov, LOG_FORMAT_IOVEC_MAX) < 0)
errno = 0; /* ignore */
}
close(ttyf);
@@ -1924,7 +1975,6 @@ void debug_switch(int sig)
signal(SIGUSR1, debug_switch);
}
-
/*
* Print syslogd errors some place.
*/
@@ -1949,6 +1999,11 @@ void logerror(const char *fmt, ...)
errno = 0; // ignore
}
+ if (!LogFormatInitialized) {
+ fputs(buf, stderr);
+ return;
+ }
+
memset(&source, '\0', sizeof(source));
source.flags = SINFO_ISINTERNAL;
@@ -2163,6 +2218,13 @@ void init(void)
*++p = '\0';
+
+ if (!strncmp("log_format:", cline, 11)) {
+ for (p = cline + 11; isspace(*p); ++p);
+ parse_log_format(&log_fmt, p);
+ continue;
+ }
+
allocate_log();
f = &Files[lognum++];
@@ -2590,6 +2652,161 @@ static void allocate_log(void)
return;
}
+int set_log_format_field(struct log_format *log_fmt, size_t i,
+ enum log_format_type t, char *s, size_t n)
+{
+ if (i >= iovec_max) {
+ logerror("Too many parts in the log_format string");
+ return -1;
+ }
+
+ if (t != LOG_FORMAT_NONE) {
+ if (log_fmt->fields_nr >= LOG_FORMAT_FIELDS_MAX) {
+ logerror("Too many placeholders in the log_format string");
+ return -1;
+ }
+
+ log_fmt->fields[log_fmt->fields_nr].f_type = t;
+ log_fmt->fields[log_fmt->fields_nr].f_iov = log_fmt->iov + i;
+ log_fmt->fields_nr++;
+ }
+
+ log_fmt->iov[i].iov_base = s;
+ log_fmt->iov[i].iov_len = n;
+ log_fmt->iovec_nr++;
+
+ return 0;
+}
+
+int parse_log_format(struct log_format *log_fmt, char *str)
+{
+ char *ptr, *start;
+ int i, special, field_nr;
+ struct log_format new_fmt = { 0 };
+
+ iovec_max = sysconf(_SC_IOV_MAX);
+
+ new_fmt.line = calloc(1, LINE_MAX);
+ if (!new_fmt.line) {
+ logerror("Cannot allocate log_format string");
+ goto error;
+ }
+
+ new_fmt.iov = calloc(LOG_FORMAT_IOVEC_MAX, sizeof(struct iovec));
+ if (!new_fmt.iov) {
+ logerror("Cannot allocate records array for log_format string");
+ goto error;
+ }
+
+ new_fmt.fields = calloc(LOG_FORMAT_FIELDS_MAX, sizeof(struct log_format_field));
+ if (!new_fmt.fields) {
+ logerror("Cannot allocate rules array for log_format string");
+ goto error;
+ }
+
+ ptr = str;
+ i = special = 0;
+
+ while (*ptr != '\0' && i < LINE_MAX) {
+ char c = *ptr++;
+
+ switch (c) {
+ case 'b': if (special) c = '\b'; break;
+ case 'f': if (special) c = '\f'; break;
+ case 'n': if (special) c = '\n'; break;
+ case 'r': if (special) c = '\r'; break;
+ case 't': if (special) c = '\t'; break;
+ case '\\':
+ if (!special) {
+ special = 1;
+ continue;
+ }
+ break;
+ }
+ new_fmt.line[i++] = c;
+ special = 0;
+ }
+
+ field_nr = 0;
+ special = 0;
+ i = 0;
+
+ if (set_log_format_field(&new_fmt, field_nr++, LOG_FORMAT_BOL, NULL, 0) < 0)
+ goto error;
+
+ start = ptr = new_fmt.line;
+
+ while (*ptr != '\0') {
+ enum log_format_type f_type;
+
+ if (special) {
+ switch (*ptr) {
+ 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 '%':
+ special = 0;
+ goto create_special;
+ default:
+ logerror("unexpected special: '%%%c'", *ptr);
+ goto error;
+ }
+ special = 0;
+ goto create_field;
+
+ } else if (*ptr == '%')
+ special = 1;
+next:
+ ptr++;
+ continue;
+create_field:
+ if ((ptr - start - 1) > 0 &&
+ set_log_format_field(&new_fmt, field_nr++,
+ LOG_FORMAT_NONE, start, (ptr - start - 1)) < 0)
+ goto error;
+
+ if (set_log_format_field(&new_fmt, field_nr++, f_type, NULL, 0) < 0)
+ goto error;
+
+ start = ptr + 1;
+ goto next;
+create_special:
+ if (set_log_format_field(&new_fmt, field_nr++,
+ LOG_FORMAT_NONE, start, (ptr - start - 1)) < 0)
+ goto error;
+
+ start = ptr;
+ goto next;
+ }
+
+ if (special) {
+ logerror("unexpected '%%' at the end of line");
+ goto error;
+ }
+
+ if (start != ptr &&
+ set_log_format_field(&new_fmt, field_nr++,
+ LOG_FORMAT_NONE, start, (ptr - start)) < 0)
+ goto error;
+
+ if (set_log_format_field(&new_fmt, field_nr++,
+ LOG_FORMAT_EOL, NULL, 0) < 0)
+ goto error;
+
+ log_fmt->line = new_fmt.line;
+ log_fmt->iov = new_fmt.iov;
+ log_fmt->fields = new_fmt.fields;
+
+ LogFormatInitialized = 1;
+
+ return 0;
+error:
+ free(new_fmt.line);
+ free(new_fmt.iov);
+ free(new_fmt.fields);
+
+ return -1;
+}
/*
* The following function is resposible for handling a SIGHUP signal. Since
--
2.25.4
^ permalink raw reply [flat|nested] 9+ messages in thread
* [devel] [PATCH 6/6] syslogd: implement checksum chains for log entries
2020-10-27 11:33 [devel] [PATCH 0/6] sysklogd: implement checkum chains for log entries Alexey Gladkov
` (4 preceding siblings ...)
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
2020-10-27 11:44 ` [devel] [PATCH 0/6] sysklogd: implement checkum " Alexey V. Vissarionov
6 siblings, 0 replies; 9+ messages in thread
From: Alexey Gladkov @ 2020-10-27 11:33 UTC (permalink / raw)
To: ALT Devel discussion list
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
^ permalink raw reply [flat|nested] 9+ messages in thread