[systemd-commits] 6 commits - Makefile.am TODO configure.ac man/systemd.service.xml src/core src/journal src/remount-fs src/shared src/systemctl
Lennart Poettering
lennart at kemper.freedesktop.org
Tue Aug 14 09:43:04 PDT 2012
Makefile.am | 51 ++-
TODO | 3
configure.ac | 34 ++
man/systemd.service.xml | 43 ++
src/core/load-fragment-gperf.gperf.m4 | 2
src/core/mount.c | 2
src/core/service.c | 20 +
src/core/service.h | 3
src/core/socket.c | 2
src/core/swap.c | 2
src/journal/fsprg.c | 384 ++++++++++++++++++++++++
src/journal/fsprg.h | 64 ++++
src/journal/journal-def.h | 45 ++
src/journal/journal-file.c | 544 ++++++++++++++++++++++++++++++++--
src/journal/journal-file.h | 28 +
src/journal/journalctl.c | 195 +++++++++++-
src/journal/journald.c | 29 -
src/journal/sd-journal.c | 2
src/journal/test-journal-stream.c | 6
src/journal/test-journal.c | 7
src/remount-fs/remount-fs.c | 2
src/shared/conf-parser.c | 72 ++++
src/shared/conf-parser.h | 1
src/shared/exit-status.c | 16 -
src/shared/exit-status.h | 11
src/shared/hashmap.c | 15
src/shared/hashmap.h | 1
src/shared/set.c | 4
src/shared/set.h | 1
src/shared/util.c | 2
src/systemctl/systemctl.c | 2
31 files changed, 1482 insertions(+), 111 deletions(-)
New commits:
commit d046b20b11e78f4f52267850097e20f8221a6279
Author: Lennart Poettering <lennart at poettering.net>
Date: Tue Aug 14 18:42:26 2012 +0200
conf-parser: simplify a few things by using set_ensure_allocated() rather than set_new()
diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c
index 595bb51..f3e258a 100644
--- a/src/shared/conf-parser.c
+++ b/src/shared/conf-parser.c
@@ -959,7 +959,9 @@ int config_parse_set_status(
FOREACH_WORD(w, l, rvalue, state) {
int val;
- char *temp = strndup(w, l);
+ char *temp;
+
+ temp = strndup(w, l);
if (!temp)
return log_oom();
@@ -967,12 +969,12 @@ int config_parse_set_status(
if (r < 0) {
val = signal_from_string_try_harder(temp);
free(temp);
+
if (val > 0) {
- if (!status_set->signal) {
- status_set->signal = set_new(trivial_hash_func, trivial_compare_func);
- if (!status_set->signal)
- return log_oom();
- }
+ r = set_ensure_allocated(&status_set->signal, trivial_hash_func, trivial_compare_func);
+ if (r < 0)
+ return log_oom();
+
r = set_put(status_set->signal, INT_TO_PTR(val));
if (r < 0) {
log_error("[%s:%u] Unable to store: %s", filename, line, w);
@@ -984,14 +986,14 @@ int config_parse_set_status(
}
} else {
free(temp);
- if(val < 0 || val > 255)
+
+ if (val < 0 || val > 255)
log_warning("[%s:%u] Value %d is outside range 0-255, ignoring", filename, line, val);
else {
- if (!status_set->code) {
- status_set->code = set_new(trivial_hash_func, trivial_compare_func);
- if (!status_set->code)
- return log_oom();
- }
+ r = set_ensure_allocated(&status_set->code, trivial_hash_func, trivial_compare_func);
+ if (r < 0)
+ return log_oom();
+
r = set_put(status_set->code, INT_TO_PTR(val));
if (r < 0) {
log_error("[%s:%u] Unable to store: %s", filename, line, w);
commit abdf7993161a2762df6887fdb5a5f0f4f5da24cf
Author: Lennart Poettering <lennart at poettering.net>
Date: Tue Aug 14 18:37:45 2012 +0200
man: extend documentation for RestartPreventExitStatus= and SuccessExitStatus= a bit
diff --git a/TODO b/TODO
index 0a36aee..102a813 100644
--- a/TODO
+++ b/TODO
@@ -397,6 +397,7 @@ Features:
- resource control in systemd
- inhibiting
- testing with Harald's awesome test kit
+ - restart
* allow port=0 in .socket units
diff --git a/man/systemd.service.xml b/man/systemd.service.xml
index c4bd65e..72b67c6 100644
--- a/man/systemd.service.xml
+++ b/man/systemd.service.xml
@@ -580,17 +580,46 @@
</varlistentry>
<varlistentry>
- <term><varname>RestartPreventExitStatus=</varname></term>
- <listitem><para>Specify exit status list, which
- will prevent service from restart. Codes are
- separated by whitespace (e.g. "1 6 SIGKILL").</para></listitem>
+ <term><varname>SuccessExitStatus=</varname></term>
+ <listitem><para>Takes a list of exit
+ status definitions that when returned
+ by the main service process will be
+ considered successful termination, in
+ addition to the normal successful exit
+ code 0 and the signals SIGHUP, SIGINT,
+ SIGTERM and SIGPIPE. Exit status
+ definitions can either be numeric exit
+ codes or termination signal names, and
+ are are separated by spaces. Example:
+ "<literal>SuccessExitStatus=1 2 8
+ SIGKILL</literal>", ensures that exit
+ codes 1, 2, 8 and the termination
+ signal SIGKILL are considered clean
+ service
+ terminations.</para></listitem>
</varlistentry>
<varlistentry>
- <term><varname>SuccessExitStatus=</varname></term>
- <listitem><para>Specify exit status list, which
- will be considered as successful exit. Codes are
- separated by whitespace (e.g. "1 6 SIGKILL").</para></listitem>
+ <term><varname>RestartPreventExitStatus=</varname></term>
+ <listitem><para>Takes a list of exit
+ status definitions that when returned
+ by the main service process will
+ prevent automatic service restarts
+ regardless of the restart setting
+ configured with
+ <varname>Restart=</varname>. Exit
+ status definitions can either be
+ numeric exit codes or termination
+ signal names, and are separated by
+ spaces. Defaults to the empty list, so
+ that by default no exit status is
+ excluded from the configured restart
+ logic. Example:
+ "<literal>RestartPreventExitStatus=1 6
+ SIGABRT</literal>", ensures that exit
+ codes 1 and 6 and the termination signal
+ SIGABRT will not result in automatic
+ service restarting.</para></listitem>
</varlistentry>
<varlistentry>
commit 96342de68d0d6de71a062d984dafd2a0905ed9fe
Author: Lukas Nykryn <lnykryn at redhat.com>
Date: Mon Aug 13 13:58:01 2012 +0200
service: add options RestartPreventExitStatus and SuccessExitStatus
In some cases, like wrong configuration, restarting after error
does not help, so administrator can specify statuses by RestartPreventExitStatus
which will not cause restart of a service.
Sometimes you have non-standart exit status, so this can be specified
by SuccessfulExitStatus.
diff --git a/man/systemd.service.xml b/man/systemd.service.xml
index 1946d85..c4bd65e 100644
--- a/man/systemd.service.xml
+++ b/man/systemd.service.xml
@@ -580,6 +580,20 @@
</varlistentry>
<varlistentry>
+ <term><varname>RestartPreventExitStatus=</varname></term>
+ <listitem><para>Specify exit status list, which
+ will prevent service from restart. Codes are
+ separated by whitespace (e.g. "1 6 SIGKILL").</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>SuccessExitStatus=</varname></term>
+ <listitem><para>Specify exit status list, which
+ will be considered as successful exit. Codes are
+ separated by whitespace (e.g. "1 6 SIGKILL").</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>PermissionsStartOnly=</varname></term>
<listitem><para>Takes a boolean
argument. If true, the permission
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index e738213..84eea1c 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -158,6 +158,8 @@ Service.PermissionsStartOnly, config_parse_bool, 0,
Service.RootDirectoryStartOnly, config_parse_bool, 0, offsetof(Service, root_directory_start_only)
Service.RemainAfterExit, config_parse_bool, 0, offsetof(Service, remain_after_exit)
Service.GuessMainPID, config_parse_bool, 0, offsetof(Service, guess_main_pid)
+Service.RestartPreventExitStatus, config_parse_set_status, 0, offsetof(Service, restart_ignore_status)
+Service.SuccessExitStatus, config_parse_set_status, 0, offsetof(Service, success_status)
m4_ifdef(`HAVE_SYSV_COMPAT',
`Service.SysVStartPriority, config_parse_sysv_priority, 0, offsetof(Service, sysv_start_priority)',
`Service.SysVStartPriority, config_parse_warn_compat, 0, 0')
diff --git a/src/core/mount.c b/src/core/mount.c
index 83e51a7..fc981c7 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -1225,7 +1225,7 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
m->control_pid = 0;
- if (is_clean_exit(code, status))
+ if (is_clean_exit(code, status, NULL))
f = MOUNT_SUCCESS;
else if (code == CLD_EXITED)
f = MOUNT_FAILURE_EXIT_CODE;
diff --git a/src/core/service.c b/src/core/service.c
index e74da54..f540752 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -294,6 +294,16 @@ static void service_done(Unit *u) {
s->control_command = NULL;
s->main_command = NULL;
+ set_free(s->restart_ignore_status.code);
+ s->restart_ignore_status.code = NULL;
+ set_free(s->restart_ignore_status.signal);
+ s->restart_ignore_status.signal = NULL;
+
+ set_free(s->success_status.code);
+ s->success_status.code = NULL;
+ set_free(s->success_status.signal);
+ s->success_status.signal = NULL;
+
/* This will leak a process, but at least no memory or any of
* our resources */
service_unwatch_main_pid(s);
@@ -1902,7 +1912,12 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
(s->restart == SERVICE_RESTART_ON_SUCCESS && s->result == SERVICE_SUCCESS) ||
(s->restart == SERVICE_RESTART_ON_FAILURE && s->result != SERVICE_SUCCESS) ||
(s->restart == SERVICE_RESTART_ON_ABORT && (s->result == SERVICE_FAILURE_SIGNAL ||
- s->result == SERVICE_FAILURE_CORE_DUMP)))) {
+ s->result == SERVICE_FAILURE_CORE_DUMP))) &&
+ (s->result != SERVICE_FAILURE_EXIT_CODE ||
+ !set_contains(s->restart_ignore_status.code, INT_TO_PTR(s->main_exec_status.status))) &&
+ (s->result != SERVICE_FAILURE_SIGNAL ||
+ !set_contains(s->restart_ignore_status.signal, INT_TO_PTR(s->main_exec_status.status)))
+ ) {
r = unit_watch_timer(UNIT(s), s->restart_usec, &s->timer_watch);
if (r < 0)
@@ -2874,7 +2889,8 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
assert(s);
assert(pid >= 0);
- if (UNIT(s)->fragment_path ? is_clean_exit(code, status) : is_clean_exit_lsb(code, status))
+ if (UNIT(s)->fragment_path ? is_clean_exit(code, status, &s->success_status) :
+ is_clean_exit_lsb(code, status, &s->success_status))
f = SERVICE_SUCCESS;
else if (code == CLD_EXITED)
f = SERVICE_FAILURE_EXIT_CODE;
diff --git a/src/core/service.h b/src/core/service.h
index c78de79..2a4dc30 100644
--- a/src/core/service.h
+++ b/src/core/service.h
@@ -28,6 +28,7 @@ typedef struct Service Service;
#include "ratelimit.h"
#include "service.h"
#include "kill.h"
+#include "exit-status.h"
typedef enum ServiceState {
SERVICE_DEAD,
@@ -115,6 +116,8 @@ struct Service {
ServiceType type;
ServiceRestart restart;
+ ExitStatusSet restart_ignore_status;
+ ExitStatusSet success_status;
/* If set we'll read the main daemon PID from this file */
char *pid_file;
diff --git a/src/core/socket.c b/src/core/socket.c
index 837b166..cbbfb0c 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -1884,7 +1884,7 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) {
s->control_pid = 0;
- if (is_clean_exit(code, status))
+ if (is_clean_exit(code, status, NULL))
f = SOCKET_SUCCESS;
else if (code == CLD_EXITED)
f = SOCKET_FAILURE_EXIT_CODE;
diff --git a/src/core/swap.c b/src/core/swap.c
index 458e00e..cd4d9ab 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -917,7 +917,7 @@ static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) {
s->control_pid = 0;
- if (is_clean_exit(code, status))
+ if (is_clean_exit(code, status, NULL))
f = SWAP_SUCCESS;
else if (code == CLD_EXITED)
f = SWAP_FAILURE_EXIT_CODE;
diff --git a/src/remount-fs/remount-fs.c b/src/remount-fs/remount-fs.c
index 636c46f..b49d095 100644
--- a/src/remount-fs/remount-fs.c
+++ b/src/remount-fs/remount-fs.c
@@ -145,7 +145,7 @@ int main(int argc, char *argv[]) {
s = hashmap_remove(pids, UINT_TO_PTR(si.si_pid));
if (s) {
- if (!is_clean_exit(si.si_code, si.si_status)) {
+ if (!is_clean_exit(si.si_code, si.si_status, NULL)) {
if (si.si_code == CLD_EXITED)
log_error("/bin/mount for %s exited with exit status %i.", s, si.si_status);
else
diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c
index 1eccec5..595bb51 100644
--- a/src/shared/conf-parser.c
+++ b/src/shared/conf-parser.c
@@ -32,6 +32,8 @@
#include "log.h"
#include "utf8.h"
#include "path-util.h"
+#include "set.h"
+#include "exit-status.h"
int config_item_table_lookup(
void *table,
@@ -933,3 +935,71 @@ int config_parse_level(
*o = (*o & LOG_FACMASK) | x;
return 0;
}
+
+int config_parse_set_status(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ char *w;
+ size_t l;
+ char *state;
+ int r;
+ ExitStatusSet *status_set = data;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ FOREACH_WORD(w, l, rvalue, state) {
+ int val;
+ char *temp = strndup(w, l);
+ if (!temp)
+ return log_oom();
+
+ r = safe_atoi(temp, &val);
+ if (r < 0) {
+ val = signal_from_string_try_harder(temp);
+ free(temp);
+ if (val > 0) {
+ if (!status_set->signal) {
+ status_set->signal = set_new(trivial_hash_func, trivial_compare_func);
+ if (!status_set->signal)
+ return log_oom();
+ }
+ r = set_put(status_set->signal, INT_TO_PTR(val));
+ if (r < 0) {
+ log_error("[%s:%u] Unable to store: %s", filename, line, w);
+ return r;
+ }
+ } else {
+ log_error("[%s:%u] Failed to parse value: %s", filename, line, w);
+ return r;
+ }
+ } else {
+ free(temp);
+ if(val < 0 || val > 255)
+ log_warning("[%s:%u] Value %d is outside range 0-255, ignoring", filename, line, val);
+ else {
+ if (!status_set->code) {
+ status_set->code = set_new(trivial_hash_func, trivial_compare_func);
+ if (!status_set->code)
+ return log_oom();
+ }
+ r = set_put(status_set->code, INT_TO_PTR(val));
+ if (r < 0) {
+ log_error("[%s:%u] Unable to store: %s", filename, line, w);
+ return r;
+ }
+ }
+ }
+
+ }
+ return 0;
+}
diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h
index 4f94b3b..56ffc2f 100644
--- a/src/shared/conf-parser.h
+++ b/src/shared/conf-parser.h
@@ -105,6 +105,7 @@ int config_parse_nsec(const char *filename, unsigned line, const char *section,
int config_parse_mode(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_facility(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_level(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_set_status(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
#define DEFINE_CONFIG_PARSE_ENUM(function,name,type,msg) \
int function( \
diff --git a/src/shared/exit-status.c b/src/shared/exit-status.c
index 0dc82b2..45131f2 100644
--- a/src/shared/exit-status.c
+++ b/src/shared/exit-status.c
@@ -23,6 +23,8 @@
#include <sys/wait.h>
#include "exit-status.h"
+#include "set.h"
+#include "macro.h"
const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) {
@@ -158,10 +160,12 @@ const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) {
}
-bool is_clean_exit(int code, int status) {
+bool is_clean_exit(int code, int status, ExitStatusSet *success_status) {
if (code == CLD_EXITED)
- return status == 0;
+ return status == 0 ||
+ (success_status &&
+ set_contains(success_status->code, INT_TO_PTR(status)));
/* If a daemon does not implement handlers for some of the
* signals that's not considered an unclean shutdown */
@@ -170,14 +174,16 @@ bool is_clean_exit(int code, int status) {
status == SIGHUP ||
status == SIGINT ||
status == SIGTERM ||
- status == SIGPIPE;
+ status == SIGPIPE ||
+ (success_status &&
+ set_contains(success_status->signal, INT_TO_PTR(status)));
return false;
}
-bool is_clean_exit_lsb(int code, int status) {
+bool is_clean_exit_lsb(int code, int status, ExitStatusSet *success_status) {
- if (is_clean_exit(code, status))
+ if (is_clean_exit(code, status, success_status))
return true;
return
diff --git a/src/shared/exit-status.h b/src/shared/exit-status.h
index 3f42c15..d3b548f 100644
--- a/src/shared/exit-status.h
+++ b/src/shared/exit-status.h
@@ -22,7 +22,7 @@
***/
#include <stdbool.h>
-
+#include "set.h"
typedef enum ExitStatus {
/* EXIT_SUCCESS defined by libc */
/* EXIT_FAILURE defined by libc */
@@ -77,7 +77,12 @@ typedef enum ExitStatusLevel {
EXIT_STATUS_FULL = EXIT_STATUS_LSB
} ExitStatusLevel;
+typedef struct ExitStatusSet {
+ Set *code;
+ Set *signal;
+} ExitStatusSet;
+
const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level);
-bool is_clean_exit(int code, int status);
-bool is_clean_exit_lsb(int code, int status);
+bool is_clean_exit(int code, int status, ExitStatusSet *success_status);
+bool is_clean_exit_lsb(int code, int status, ExitStatusSet *success_status);
diff --git a/src/shared/hashmap.c b/src/shared/hashmap.c
index e2395d4..be37a36 100644
--- a/src/shared/hashmap.c
+++ b/src/shared/hashmap.c
@@ -378,6 +378,21 @@ void* hashmap_get(Hashmap *h, const void *key) {
return e->value;
}
+bool hashmap_contains(Hashmap *h, const void *key) {
+ unsigned hash;
+ struct hashmap_entry *e;
+
+ if (!h)
+ return false;
+
+ hash = h->hash_func(key) % NBUCKETS;
+
+ if (!(e = hash_scan(h, hash, key)))
+ return false;
+
+ return true;
+}
+
void* hashmap_remove(Hashmap *h, const void *key) {
struct hashmap_entry *e;
unsigned hash;
diff --git a/src/shared/hashmap.h b/src/shared/hashmap.h
index ee810f5..504f0b7 100644
--- a/src/shared/hashmap.h
+++ b/src/shared/hashmap.h
@@ -53,6 +53,7 @@ int hashmap_ensure_allocated(Hashmap **h, hash_func_t hash_func, compare_func_t
int hashmap_put(Hashmap *h, const void *key, void *value);
int hashmap_replace(Hashmap *h, const void *key, void *value);
void* hashmap_get(Hashmap *h, const void *key);
+bool hashmap_contains(Hashmap *h, const void *key);
void* hashmap_remove(Hashmap *h, const void *key);
void* hashmap_remove_value(Hashmap *h, const void *key, void *value);
int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value);
diff --git a/src/shared/set.c b/src/shared/set.c
index f5c7c37..4d56c4f 100644
--- a/src/shared/set.c
+++ b/src/shared/set.c
@@ -57,6 +57,10 @@ void *set_get(Set *s, void *value) {
return hashmap_get(MAKE_HASHMAP(s), value);
}
+bool set_contains(Set *s, void *value) {
+ return hashmap_contains(MAKE_HASHMAP(s), value);
+}
+
void *set_remove(Set *s, void *value) {
return hashmap_remove(MAKE_HASHMAP(s), value);
}
diff --git a/src/shared/set.h b/src/shared/set.h
index c7b6231..a6c1d76 100644
--- a/src/shared/set.h
+++ b/src/shared/set.h
@@ -40,6 +40,7 @@ int set_ensure_allocated(Set **s, hash_func_t hash_func, compare_func_t compare_
int set_put(Set *s, void *value);
int set_replace(Set *s, void *value);
void *set_get(Set *s, void *value);
+bool set_contains(Set *s, void *value);
void *set_remove(Set *s, void *value);
int set_remove_and_put(Set *s, void *old_value, void *new_value);
diff --git a/src/shared/util.c b/src/shared/util.c
index e615195..cbf44eb 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -4357,7 +4357,7 @@ void execute_directory(const char *directory, DIR *d, char *argv[]) {
}
if ((path = hashmap_remove(pids, UINT_TO_PTR(si.si_pid)))) {
- if (!is_clean_exit(si.si_code, si.si_status)) {
+ if (!is_clean_exit(si.si_code, si.si_status, NULL)) {
if (si.si_code == CLD_EXITED)
log_error("%s exited with exit status %i.", path, si.si_status);
else
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 13e0f91..2481849 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -2148,7 +2148,7 @@ static void print_status_info(UnitStatusInfo *i) {
printf("\t Process: %u %s=%s ", p->pid, p->name, strna(t));
free(t);
- good = is_clean_exit_lsb(p->code, p->status);
+ good = is_clean_exit_lsb(p->code, p->status, NULL);
if (!good) {
on = ansi_highlight_red(true);
off = ansi_highlight_red(false);
commit d98cc1f29fbf31ccc500d6e20c29b636b9af7e0f
Author: Lennart Poettering <lennart at poettering.net>
Date: Mon Aug 13 21:52:58 2012 +0200
journal: include tag object header in hmac
diff --git a/src/journal/journal-def.h b/src/journal/journal-def.h
index af22e17..82210bf 100644
--- a/src/journal/journal-def.h
+++ b/src/journal/journal-def.h
@@ -124,6 +124,7 @@ _packed_ struct EntryArrayObject {
_packed_ struct TagObject {
ObjectHeader object;
+ uint64_t seqnum;
uint8_t tag[TAG_LENGTH]; /* SHA-256 HMAC */
};
@@ -182,6 +183,8 @@ _packed_ struct Header {
/* Added in 187 */
le64_t n_data;
le64_t n_fields;
+ /* Added in 189 */
+ le64_t n_tags;
};
#define FSPRG_HEADER_SIGNATURE ((char[]) { 'K', 'S', 'H', 'H', 'R', 'H', 'L', 'P' })
diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c
index 7dd7256..9235e5f 100644
--- a/src/journal/journal-file.c
+++ b/src/journal/journal-file.c
@@ -475,7 +475,7 @@ int journal_file_move_to_object(JournalFile *f, int type, uint64_t offset, Objec
return 0;
}
-static uint64_t journal_file_seqnum(JournalFile *f, uint64_t *seqnum) {
+static uint64_t journal_file_entry_seqnum(JournalFile *f, uint64_t *seqnum) {
uint64_t r;
assert(f);
@@ -1050,7 +1050,7 @@ static int journal_file_append_entry_internal(
if (r < 0)
return r;
- o->entry.seqnum = htole64(journal_file_seqnum(f, seqnum));
+ o->entry.seqnum = htole64(journal_file_entry_seqnum(f, seqnum));
memcpy(o->entry.items, items, n_items * sizeof(EntryItem));
o->entry.realtime = htole64(ts->realtime);
o->entry.monotonic = htole64(ts->monotonic);
@@ -1905,6 +1905,17 @@ static void *fsprg_state(JournalFile *f) {
return (uint8_t*) f->fsprg_header + a;
}
+static uint64_t journal_file_tag_seqnum(JournalFile *f) {
+ uint64_t r;
+
+ assert(f);
+
+ r = le64toh(f->header->n_tags) + 1;
+ f->header->n_tags = htole64(r);
+
+ return r;
+}
+
int journal_file_append_tag(JournalFile *f) {
Object *o;
uint64_t p;
@@ -1926,6 +1937,14 @@ int journal_file_append_tag(JournalFile *f) {
if (r < 0)
return r;
+ o->tag.seqnum = htole64(journal_file_tag_seqnum(f));
+
+ /* Add the tag object itself, so that we can protect its
+ * header. This will exclude the actual hash value in it */
+ r = journal_file_hmac_put_object(f, OBJECT_TAG, p);
+ if (r < 0)
+ return r;
+
/* Get the HMAC tag and store it in the object */
memcpy(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH);
f->hmac_running = false;
@@ -2071,8 +2090,8 @@ static int journal_file_hmac_put_object(JournalFile *f, int type, uint64_t p) {
switch (o->object.type) {
case OBJECT_DATA:
- /* All but: entry_array_offset, n_entries are mutable */
- gcry_md_write(f->hmac, &o->data.hash, offsetof(DataObject, entry_array_offset) - offsetof(DataObject, hash));
+ /* All but: hash and payload are mutable */
+ gcry_md_write(f->hmac, &o->data.hash, sizeof(o->data.hash));
gcry_md_write(f->hmac, o->data.payload, le64toh(o->object.size) - offsetof(DataObject, payload));
break;
@@ -2088,10 +2107,9 @@ static int journal_file_hmac_put_object(JournalFile *f, int type, uint64_t p) {
break;
case OBJECT_TAG:
- /* All */
- gcry_md_write(f->hmac, o->tag.tag, le64toh(o->object.size) - offsetof(TagObject, tag));
+ /* All but the tag itself */
+ gcry_md_write(f->hmac, &o->tag.seqnum, sizeof(o->tag.seqnum));
break;
-
default:
return -EINVAL;
}
@@ -2322,7 +2340,8 @@ void journal_file_dump(JournalFile *f) {
break;
case OBJECT_TAG:
- printf("Type: OBJECT_TAG\n");
+ printf("Type: OBJECT_TAG %llu\n",
+ (unsigned long long) le64toh(o->tag.seqnum));
break;
}
commit b0af6f41ea67c97b8beb16fd1d63042379bbf103
Author: Lennart Poettering <lennart at poettering.net>
Date: Mon Aug 13 20:57:38 2012 +0200
journal: add all objects we add to HMAC
diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c
index 0e48893..7dd7256 100644
--- a/src/journal/journal-file.c
+++ b/src/journal/journal-file.c
@@ -68,12 +68,17 @@
(le64toh((h)->header_size) >= offsetof(Header, field) + sizeof((h)->field))
static int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime);
+static int journal_file_hmac_put_object(JournalFile *f, int type, uint64_t p);
void journal_file_close(JournalFile *f) {
int t;
assert(f);
+ /* Write the final tag */
+ if (f->authenticate)
+ journal_file_append_tag(f);
+
/* Sync everything to disk, before we mark the file offline */
for (t = 0; t < _WINDOW_MAX; t++)
if (f->windows[t].ptr)
@@ -831,6 +836,10 @@ static int journal_file_append_data(
if (r < 0)
return r;
+ r = journal_file_hmac_put_object(f, OBJECT_DATA, p);
+ if (r < 0)
+ return r;
+
/* The linking might have altered the window, so let's
* refresh our pointer */
r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
@@ -907,6 +916,10 @@ static int link_entry_into_array(JournalFile *f,
if (r < 0)
return r;
+ r = journal_file_hmac_put_object(f, OBJECT_ENTRY_ARRAY, q);
+ if (r < 0)
+ return r;
+
o->entry_array.items[i] = htole64(p);
if (ap == 0)
@@ -1044,6 +1057,10 @@ static int journal_file_append_entry_internal(
o->entry.xor_hash = htole64(xor_hash);
o->entry.boot_id = f->header->boot_id;
+ r = journal_file_hmac_put_object(f, OBJECT_ENTRY, np);
+ if (r < 0)
+ return r;
+
r = journal_file_link_entry(f, o, np);
if (r < 0)
return r;
@@ -1888,7 +1905,7 @@ static void *fsprg_state(JournalFile *f) {
return (uint8_t*) f->fsprg_header + a;
}
-static int journal_file_append_tag(JournalFile *f) {
+int journal_file_append_tag(JournalFile *f) {
Object *o;
uint64_t p;
int r;
@@ -2473,7 +2490,9 @@ int journal_file_open(
r = journal_file_verify_header(f);
if (r < 0)
goto fail;
+ }
+ if (!newly_created && f->writable) {
r = journal_file_load_fsprg(f);
if (r < 0)
goto fail;
diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h
index 25d9720..a16c8ff 100644
--- a/src/journal/journal-file.h
+++ b/src/journal/journal-file.h
@@ -164,3 +164,5 @@ int journal_file_get_cutoff_realtime_usec(JournalFile *f, usec_t *from, usec_t *
int journal_file_get_cutoff_monotonic_usec(JournalFile *f, sd_id128_t boot, usec_t *from, usec_t *to);
bool journal_file_rotate_suggested(JournalFile *f);
+
+int journal_file_append_tag(JournalFile *f);
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index 138bf09..b4874a7 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -84,7 +84,7 @@ static int help(void) {
" -D --directory=PATH Show journal files from directory\n"
" -p --priority=RANGE Show only messages within the specified priority range\n\n"
"Commands:\n"
- " --new-id128 Generate a new 128 Bit id\n"
+ " --new-id128 Generate a new 128 Bit ID\n"
" --header Show journal header information\n"
" --setup-keys Generate new FSPRG key pair\n",
program_invocation_short_name);
diff --git a/src/journal/test-journal.c b/src/journal/test-journal.c
index 7b1583c..8f01b4d 100644
--- a/src/journal/test-journal.c
+++ b/src/journal/test-journal.c
@@ -57,6 +57,7 @@ int main(int argc, char *argv[]) {
iovec.iov_len = strlen(test);
assert_se(journal_file_append_entry(f, &ts, &iovec, 1, NULL, NULL, NULL) == 0);
+ journal_file_append_tag(f);
journal_file_dump(f);
assert(journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, &o, &p) == 1);
commit 7560fffcd2531786b9c1ca657667a43e90331326
Author: Lennart Poettering <lennart at poettering.net>
Date: Mon Aug 13 20:31:10 2012 +0200
journald: initial version of FSPRG hookup
This adds forward-secure authentication of journal files. This patch
includes key generation as well as tagging of journal files,
Verification of journal files will be added in a later patch.
diff --git a/Makefile.am b/Makefile.am
index 9062dd6..837bc6c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2323,14 +2323,13 @@ systemd_journald_SOURCES = \
nodist_systemd_journald_SOURCES = \
src/journal/journald-gperf.c
-systemd_journald_CFLAGS =
-
systemd_journald_LDADD = \
libsystemd-label.la \
libsystemd-shared.la \
libsystemd-audit.la \
libsystemd-daemon.la \
- libsystemd-id128-internal.la
+ libsystemd-id128-internal.la \
+ libsystemd-journal-internal.la
if ENABLE_LOGIND
systemd_journald_LDADD += \
@@ -2342,18 +2341,6 @@ systemd_journald_LDADD += \
libsystemd-acl.la
endif
-if HAVE_XZ
-systemd_journald_SOURCES += \
- src/journal/compress.c
-
-systemd_journald_CFLAGS += \
- $(AM_CFLAGS) \
- $(XZ_CFLAGS)
-
-systemd_journald_LDADD += \
- $(XZ_LIBS)
-endif
-
systemd_cat_SOURCES = \
src/journal/cat.c
@@ -2364,6 +2351,9 @@ systemd_cat_LDADD = \
journalctl_SOURCES = \
src/journal/journalctl.c
+journalctl_CFLAGS = \
+ $(AM_CFLAGS)
+
journalctl_LDADD = \
libsystemd-shared.la \
libsystemd-journal-internal.la \
@@ -2425,26 +2415,49 @@ libsystemd_journal_la_LIBADD = \
libsystemd_journal_internal_la_SOURCES = \
$(libsystemd_journal_la_SOURCES)
+libsystemd_journal_internal_la_CFLAGS = \
+ $(AM_CFLAGS)
+
+libsystemd_journal_internal_la_LIBADD =
+
if HAVE_XZ
libsystemd_journal_la_SOURCES += \
src/journal/compress.c
libsystemd_journal_la_CFLAGS += \
- $(AM_CFLAGS) \
$(XZ_CFLAGS)
libsystemd_journal_la_LIBADD += \
$(XZ_LIBS)
-libsystemd_journal_internal_la_CFLAGS = \
- $(AM_CFLAGS)
+libsystemd_journal_internal_la_CFLAGS += \
$(XZ_CFLAGS)
-libsystemd_journal_internal_la_LIBADD = \
+libsystemd_journal_internal_la_LIBADD += \
$(XZ_LIBS)
endif
+if HAVE_GCRYPT
+libsystemd_journal_la_SOURCES += \
+ src/journal/fsprg.c \
+ src/journal/fsprg.h
+
+libsystemd_journal_la_CFLAGS += \
+ $(GCRYPT_CFLAGS) \
+ -Wno-pointer-arith
+
+libsystemd_journal_la_LIBADD += \
+ $(GCRYPT_LIBS)
+
+libsystemd_journal_internal_la_CFLAGS += \
+ $(GCRYPT_CFLAGS) \
+ -Wno-pointer-arith
+
+libsystemd_journal_internal_la_LIBADD += \
+ $(GCRYPT_LIBS)
+endif
+
# move lib from $(libdir) to $(rootlibdir) and update devel link, if needed
libsystemd-journal-install-hook:
if test "$(libdir)" != "$(rootlibdir)"; then \
diff --git a/TODO b/TODO
index 2467ea8..0a36aee 100644
--- a/TODO
+++ b/TODO
@@ -49,6 +49,8 @@ Bugfixes:
Features:
+* shutdown: don't read-only mount anything when running in container
+
* nspawn: --read-only is not applied recursively to submounts
* MountFlags=shared acts as MountFlags=slave right now.
diff --git a/configure.ac b/configure.ac
index 50176e1..c1e88da 100644
--- a/configure.ac
+++ b/configure.ac
@@ -302,6 +302,39 @@ AC_SUBST(ACL_LIBS)
AM_CONDITIONAL([HAVE_ACL], [test "x$have_acl" != xno])
# ------------------------------------------------------------------------------
+AC_ARG_ENABLE([],
+ AS_HELP_STRING([--disable-gcrypt],[Disable optional GCRYPT support]),
+ [case "${enableval}" in
+ yes) have_gcrypt=yes ;;
+ no) have_gcrypt=no ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --disable-gcrypt) ;;
+ esac],
+ [have_gcrypt=auto])
+
+if test "x${have_gcrypt}" != xno ; then
+ AM_PATH_LIBGCRYPT(
+ [1.4.5],
+ [have_gcrypt=yes],
+ [if test "x$have_gcrypt" = xyes ; then
+ AC_MSG_ERROR([*** GCRYPT headers not found.])
+ fi])
+
+ if test "x$have_gcrypt" = xyes ; then
+ GCRYPT_LIBS="$LIBGCRYPT_LIBS"
+ GCRYPT_CFLAGS="$LIBGCRYPT_CFLAGS"
+ AC_DEFINE(HAVE_GCRYPT, 1, [GCRYPT available])
+ else
+ have_gcrypt=no
+ fi
+else
+ GCRYPT_LIBS=
+ GCRYPT_CFLAGS=
+fi
+AC_SUBST(GCRYPT_LIBS)
+AC_SUBST(GCRYPT_CFLAGS)
+AM_CONDITIONAL([HAVE_GCRYPT], [test "x$have_gcrypt" != xno])
+
+# ------------------------------------------------------------------------------
AC_ARG_ENABLE([audit],
AS_HELP_STRING([--disable-audit],[Disable optional AUDIT support]),
[case "${enableval}" in
@@ -726,6 +759,7 @@ AC_MSG_RESULT([
SELinux: ${have_selinux}
XZ: ${have_xz}
ACL: ${have_acl}
+ GCRYPT: ${have_gcrypt}
binfmt: ${have_binfmt}
vconsole: ${have_vconsole}
readahead: ${have_readahead}
diff --git a/src/journal/fsprg.c b/src/journal/fsprg.c
new file mode 100644
index 0000000..34ce3be
--- /dev/null
+++ b/src/journal/fsprg.c
@@ -0,0 +1,384 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/*
+ * fsprg v0.1 - (seekable) forward-secure pseudorandom generator
+ * Copyright (C) 2012 B. Poettering
+ * Contact: fsprg at point-at-infinity.org
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <gcrypt.h>
+#include <string.h>
+#include <assert.h>
+
+#include "fsprg.h"
+
+#define ISVALID_SECPAR(secpar) (((secpar) % 16 == 0) && ((secpar) >= 16) && ((secpar) <= 16384))
+#define VALIDATE_SECPAR(secpar) assert(ISVALID_SECPAR(secpar));
+
+#define RND_HASH GCRY_MD_SHA256
+#define RND_GEN_P 0x01
+#define RND_GEN_Q 0x02
+#define RND_GEN_X 0x03
+
+/******************************************************************************/
+
+static void mpi_export(void *buf, size_t buflen, const gcry_mpi_t x) {
+ unsigned len;
+ size_t nwritten;
+
+ assert(gcry_mpi_cmp_ui(x, 0) >= 0);
+ len = (gcry_mpi_get_nbits(x) + 7) / 8;
+ assert(len <= buflen);
+ memset(buf, 0, buflen);
+ gcry_mpi_print(GCRYMPI_FMT_USG, buf + (buflen - len), len, &nwritten, x);
+ assert(nwritten == len);
+}
+
+static gcry_mpi_t mpi_import(const void *buf, size_t buflen) {
+ gcry_mpi_t h;
+ unsigned len;
+
+ gcry_mpi_scan(&h, GCRYMPI_FMT_USG, buf, buflen, NULL);
+ len = (gcry_mpi_get_nbits(h) + 7) / 8;
+ assert(len <= buflen);
+ assert(gcry_mpi_cmp_ui(h, 0) >= 0);
+
+ return h;
+}
+
+static void uint64_export(void *buf, size_t buflen, uint64_t x) {
+ assert(buflen == 8);
+ ((uint8_t*) buf)[0] = (x >> 56) & 0xff;
+ ((uint8_t*) buf)[1] = (x >> 48) & 0xff;
+ ((uint8_t*) buf)[2] = (x >> 40) & 0xff;
+ ((uint8_t*) buf)[3] = (x >> 32) & 0xff;
+ ((uint8_t*) buf)[4] = (x >> 24) & 0xff;
+ ((uint8_t*) buf)[5] = (x >> 16) & 0xff;
+ ((uint8_t*) buf)[6] = (x >> 8) & 0xff;
+ ((uint8_t*) buf)[7] = (x >> 0) & 0xff;
+}
+
+static uint64_t uint64_import(const void *buf, size_t buflen) {
+ assert(buflen == 8);
+ return
+ (uint64_t)(((uint8_t*) buf)[0]) << 56 |
+ (uint64_t)(((uint8_t*) buf)[1]) << 48 |
+ (uint64_t)(((uint8_t*) buf)[2]) << 40 |
+ (uint64_t)(((uint8_t*) buf)[3]) << 32 |
+ (uint64_t)(((uint8_t*) buf)[4]) << 24 |
+ (uint64_t)(((uint8_t*) buf)[5]) << 16 |
+ (uint64_t)(((uint8_t*) buf)[6]) << 8 |
+ (uint64_t)(((uint8_t*) buf)[7]) << 0;
+}
+
+/* deterministically generate from seed/idx a string of buflen pseudorandom bytes */
+static void det_randomize(void *buf, size_t buflen, const void *seed, size_t seedlen, uint32_t idx) {
+ gcry_md_hd_t hd, hd2;
+ size_t olen, cpylen;
+ uint32_t ctr;
+
+ olen = gcry_md_get_algo_dlen(RND_HASH);
+ gcry_md_open(&hd, RND_HASH, 0);
+ gcry_md_write(hd, seed, seedlen);
+ gcry_md_putc(hd, (idx >> 24) & 0xff);
+ gcry_md_putc(hd, (idx >> 16) & 0xff);
+ gcry_md_putc(hd, (idx >> 8) & 0xff);
+ gcry_md_putc(hd, (idx >> 0) & 0xff);
+
+ for (ctr = 0; buflen; ctr++) {
+ gcry_md_copy(&hd2, hd);
+ gcry_md_putc(hd2, (ctr >> 24) & 0xff);
+ gcry_md_putc(hd2, (ctr >> 16) & 0xff);
+ gcry_md_putc(hd2, (ctr >> 8) & 0xff);
+ gcry_md_putc(hd2, (ctr >> 0) & 0xff);
+ gcry_md_final(hd2);
+ cpylen = (buflen < olen) ? buflen : olen;
+ memcpy(buf, gcry_md_read(hd2, RND_HASH), cpylen);
+ gcry_md_close(hd2);
+ buf += cpylen;
+ buflen -= cpylen;
+ }
+ gcry_md_close(hd);
+}
+
+/* deterministically generate from seed/idx a prime of length `bits' that is 3 (mod 4) */
+static gcry_mpi_t genprime3mod4(int bits, const void *seed, size_t seedlen, uint32_t idx) {
+ size_t buflen = bits / 8;
+ uint8_t buf[buflen];
+ gcry_mpi_t p;
+
+ assert(bits % 8 == 0);
+ assert(buflen > 0);
+
+ det_randomize(buf, buflen, seed, seedlen, idx);
+ buf[0] |= 0xc0; /* set upper two bits, so that n=pq has maximum size */
+ buf[buflen - 1] |= 0x03; /* set lower two bits, to have result 3 (mod 4) */
+
+ p = mpi_import(buf, buflen);
+ while (gcry_prime_check(p, 0))
+ gcry_mpi_add_ui(p, p, 4);
+
+ return p;
+}
+
+/* deterministically generate from seed/idx a quadratic residue (mod n) */
+static gcry_mpi_t gensquare(const gcry_mpi_t n, const void *seed, size_t seedlen, uint32_t idx, unsigned secpar) {
+ size_t buflen = secpar / 8;
+ uint8_t buf[buflen];
+ gcry_mpi_t x;
+
+ det_randomize(buf, buflen, seed, seedlen, idx);
+ buf[0] &= 0x7f; /* clear upper bit, so that we have x < n */
+ x = mpi_import(buf, buflen);
+ assert(gcry_mpi_cmp(x, n) < 0);
+ gcry_mpi_mulm(x, x, x, n);
+ return x;
+}
+
+/* compute 2^m (mod phi(p)), for a prime p */
+static gcry_mpi_t twopowmodphi(uint64_t m, const gcry_mpi_t p) {
+ gcry_mpi_t phi, r;
+ int n;
+
+ phi = gcry_mpi_new(0);
+ gcry_mpi_sub_ui(phi, p, 1);
+
+ /* count number of used bits in m */
+ for (n = 0; ((uint64_t)1 << n) <= m; n++)
+ ;
+
+ r = gcry_mpi_new(0);
+ gcry_mpi_set_ui(r, 1);
+ while (n) { /* square and multiply algorithm for fast exponentiation */
+ n--;
+ gcry_mpi_mulm(r, r, r, phi);
+ if (m & ((uint64_t)1 << n)) {
+ gcry_mpi_add(r, r, r);
+ if (gcry_mpi_cmp(r, phi) >= 0)
+ gcry_mpi_sub(r, r, phi);
+ }
+ }
+
+ gcry_mpi_release(phi);
+ return r;
+}
+
+/* Decompose $x \in Z_n$ into $(xp,xq) \in Z_p \times Z_q$ using Chinese Remainder Theorem */
+static void CRT_decompose(gcry_mpi_t *xp, gcry_mpi_t *xq, const gcry_mpi_t x, const gcry_mpi_t p, const gcry_mpi_t q) {
+ *xp = gcry_mpi_new(0);
+ *xq = gcry_mpi_new(0);
+ gcry_mpi_mod(*xp, x, p);
+ gcry_mpi_mod(*xq, x, q);
+}
+
+/* Compose $(xp,xq) \in Z_p \times Z_q$ into $x \in Z_n$ using Chinese Remainder Theorem */
+static void CRT_compose(gcry_mpi_t *x, const gcry_mpi_t xp, const gcry_mpi_t xq, const gcry_mpi_t p, const gcry_mpi_t q) {
+ gcry_mpi_t a, u;
+
+ a = gcry_mpi_new(0);
+ u = gcry_mpi_new(0);
+ *x = gcry_mpi_new(0);
+ gcry_mpi_subm(a, xq, xp, q);
+ gcry_mpi_invm(u, p, q);
+ gcry_mpi_mulm(a, a, u, q); /* a = (xq - xp) / p (mod q) */
+ gcry_mpi_mul(*x, p, a);
+ gcry_mpi_add(*x, *x, xp); /* x = p * ((xq - xp) / p mod q) + xp */
+ gcry_mpi_release(a);
+ gcry_mpi_release(u);
+}
+
+static void initialize_libgcrypt(void) {
+ const char *p;
+ if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
+ return;
+
+ p = gcry_check_version("1.4.5");
+ assert(p);
+
+ /* Turn off "secmem". Clients which whish to make use of this
+ * feature should initialize the library manually */
+ gcry_control(GCRYCTL_DISABLE_SECMEM);
+ gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
+}
+
+/******************************************************************************/
+
+size_t FSPRG_mskinbytes(unsigned _secpar) {
+ VALIDATE_SECPAR(_secpar);
+ return 2 + 2 * (_secpar / 2) / 8; /* to store header,p,q */
+}
+
+size_t FSPRG_mpkinbytes(unsigned _secpar) {
+ VALIDATE_SECPAR(_secpar);
+ return 2 + _secpar / 8; /* to store header,n */
+}
+
+size_t FSPRG_stateinbytes(unsigned _secpar) {
+ VALIDATE_SECPAR(_secpar);
+ return 2 + 2 * _secpar / 8 + 8; /* to store header,n,x,epoch */
+}
+
+static void store_secpar(void *buf, uint16_t secpar) {
+ secpar = secpar / 16 - 1;
+ ((uint8_t*) buf)[0] = (secpar >> 8) & 0xff;
+ ((uint8_t*) buf)[1] = (secpar >> 0) & 0xff;
+}
+
+static uint16_t read_secpar(const void *buf) {
+ uint16_t secpar;
+ secpar =
+ (uint16_t)(((uint8_t*) buf)[0]) << 8 |
+ (uint16_t)(((uint8_t*) buf)[1]) << 0;
+ return 16 * (secpar + 1);
+}
+
+void FSPRG_GenMK(void *msk, void *mpk, const void *seed, size_t seedlen, unsigned _secpar) {
+ uint8_t iseed[FSPRG_RECOMMENDED_SEEDLEN];
+ gcry_mpi_t n, p, q;
+ uint16_t secpar;
+
+ VALIDATE_SECPAR(_secpar);
+ secpar = _secpar;
+
+ initialize_libgcrypt();
+
+ if (!seed) {
+ gcry_randomize(iseed, FSPRG_RECOMMENDED_SEEDLEN, GCRY_STRONG_RANDOM);
+ seed = iseed;
+ seedlen = FSPRG_RECOMMENDED_SEEDLEN;
+ }
+
+ p = genprime3mod4(secpar / 2, seed, seedlen, RND_GEN_P);
+ q = genprime3mod4(secpar / 2, seed, seedlen, RND_GEN_Q);
+
+ if (msk) {
+ store_secpar(msk + 0, secpar);
+ mpi_export(msk + 2 + 0 * (secpar / 2) / 8, (secpar / 2) / 8, p);
+ mpi_export(msk + 2 + 1 * (secpar / 2) / 8, (secpar / 2) / 8, q);
+ }
+
+ if (mpk) {
+ n = gcry_mpi_new(0);
+ gcry_mpi_mul(n, p, q);
+ assert(gcry_mpi_get_nbits(n) == secpar);
+
+ store_secpar(mpk + 0, secpar);
+ mpi_export(mpk + 2, secpar / 8, n);
+
+ gcry_mpi_release(n);
+ }
+
+ gcry_mpi_release(p);
+ gcry_mpi_release(q);
+}
+
+void FSPRG_GenState0(void *state, const void *mpk, const void *seed, size_t seedlen) {
+ gcry_mpi_t n, x;
+ uint16_t secpar;
+
+ initialize_libgcrypt();
+
+ secpar = read_secpar(mpk + 0);
+ n = mpi_import(mpk + 2, secpar / 8);
+ x = gensquare(n, seed, seedlen, RND_GEN_X, secpar);
+
+ memcpy(state, mpk, 2 + secpar / 8);
+ mpi_export(state + 2 + 1 * secpar / 8, secpar / 8, x);
+ memset(state + 2 + 2 * secpar / 8, 0, 8);
+
+ gcry_mpi_release(n);
+ gcry_mpi_release(x);
+}
+
+void FSPRG_Evolve(void *state) {
+ gcry_mpi_t n, x;
+ uint16_t secpar;
+ uint64_t epoch;
+
+ initialize_libgcrypt();
+
+ secpar = read_secpar(state + 0);
+ n = mpi_import(state + 2 + 0 * secpar / 8, secpar / 8);
+ x = mpi_import(state + 2 + 1 * secpar / 8, secpar / 8);
+ epoch = uint64_import(state + 2 + 2 * secpar / 8, 8);
+
+ gcry_mpi_mulm(x, x, x, n);
+ epoch++;
+
+ mpi_export(state + 2 + 1 * secpar / 8, secpar / 8, x);
+ uint64_export(state + 2 + 2 * secpar / 8, 8, epoch);
+
+ gcry_mpi_release(n);
+ gcry_mpi_release(x);
+}
+
+uint64_t FSPRG_GetEpoch(const void *state) {
+ uint16_t secpar;
+ secpar = read_secpar(state + 0);
+ return uint64_import(state + 2 + 2 * secpar / 8, 8);
+}
+
+void FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed, size_t seedlen) {
+ gcry_mpi_t p, q, n, x, xp, xq, kp, kq, xm;
+ uint16_t secpar;
+
+ initialize_libgcrypt();
+
+ secpar = read_secpar(msk + 0);
+ p = mpi_import(msk + 2 + 0 * (secpar / 2) / 8, (secpar / 2) / 8);
+ q = mpi_import(msk + 2 + 1 * (secpar / 2) / 8, (secpar / 2) / 8);
+
+ n = gcry_mpi_new(0);
+ gcry_mpi_mul(n, p, q);
+
+ x = gensquare(n, seed, seedlen, RND_GEN_X, secpar);
+ CRT_decompose(&xp, &xq, x, p, q); /* split (mod n) into (mod p) and (mod q) using CRT */
+
+ kp = twopowmodphi(epoch, p); /* compute 2^epoch (mod phi(p)) */
+ kq = twopowmodphi(epoch, q); /* compute 2^epoch (mod phi(q)) */
+
+ gcry_mpi_powm(xp, xp, kp, p); /* compute x^(2^epoch) (mod p) */
+ gcry_mpi_powm(xq, xq, kq, q); /* compute x^(2^epoch) (mod q) */
+
+ CRT_compose(&xm, xp, xq, p, q); /* combine (mod p) and (mod q) to (mod n) using CRT */
+
+ store_secpar(state + 0, secpar);
+ mpi_export(state + 2 + 0 * secpar / 8, secpar / 8, n);
+ mpi_export(state + 2 + 1 * secpar / 8, secpar / 8, xm);
+ uint64_export(state + 2 + 2 * secpar / 8, 8, epoch);
+
+ gcry_mpi_release(p);
+ gcry_mpi_release(q);
+ gcry_mpi_release(n);
+ gcry_mpi_release(x);
+ gcry_mpi_release(xp);
+ gcry_mpi_release(xq);
+ gcry_mpi_release(kp);
+ gcry_mpi_release(kq);
+ gcry_mpi_release(xm);
+}
+
+void FSPRG_GetKey(const void *state, void *key, size_t keylen, uint32_t idx) {
+ uint16_t secpar;
+
+ initialize_libgcrypt();
+
+ secpar = read_secpar(state + 0);
+ det_randomize(key, keylen, state + 2, 2 * secpar / 8 + 8, idx);
+}
diff --git a/src/journal/fsprg.h b/src/journal/fsprg.h
new file mode 100644
index 0000000..306ef18
--- /dev/null
+++ b/src/journal/fsprg.h
@@ -0,0 +1,64 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef __fsprgh__
+#define __fsprgh__
+
+/*
+ * fsprg v0.1 - (seekable) forward-secure pseudorandom generator
+ * Copyright (C) 2012 B. Poettering
+ * Contact: fsprg at point-at-infinity.org
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <sys/types.h>
+#include <inttypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define FSPRG_RECOMMENDED_SECPAR 1536
+#define FSPRG_RECOMMENDED_SEEDLEN (96/8)
+
+size_t FSPRG_mskinbytes(unsigned secpar);
+size_t FSPRG_mpkinbytes(unsigned secpar);
+size_t FSPRG_stateinbytes(unsigned secpar);
+
+/* Setup msk and mpk. Providing seed != NULL makes this algorithm deterministic. */
+void FSPRG_GenMK(void *msk, void *mpk, const void *seed, size_t seedlen, unsigned secpar);
+
+/* Initialize state deterministically in dependence on seed. */
+/* Note: in case one wants to run only one GenState0 per GenMK it is safe to use
+ the same seed for both GenMK and GenState0.
+*/
+void FSPRG_GenState0(void *state, const void *mpk, const void *seed, size_t seedlen);
+
+void FSPRG_Evolve(void *state);
+
+uint64_t FSPRG_GetEpoch(const void *state);
+
+/* Seek to any arbitrary state (by providing msk together with seed from GenState0). */
+void FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed, size_t seedlen);
+
+void FSPRG_GetKey(const void *state, void *key, size_t keylen, uint32_t idx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/journal/journal-def.h b/src/journal/journal-def.h
index 096dd8e..af22e17 100644
--- a/src/journal/journal-def.h
+++ b/src/journal/journal-def.h
@@ -37,11 +37,13 @@ typedef struct FieldObject FieldObject;
typedef struct EntryObject EntryObject;
typedef struct HashTableObject HashTableObject;
typedef struct EntryArrayObject EntryArrayObject;
-typedef struct SignatureObject SignatureObject;
+typedef struct TagObject TagObject;
typedef struct EntryItem EntryItem;
typedef struct HashItem HashItem;
+typedef struct FSPRGHeader FSPRGHeader;
+
/* Object types */
enum {
OBJECT_UNUSED,
@@ -51,7 +53,7 @@ enum {
OBJECT_DATA_HASH_TABLE,
OBJECT_FIELD_HASH_TABLE,
OBJECT_ENTRY_ARRAY,
- OBJECT_SIGNATURE,
+ OBJECT_TAG,
_OBJECT_TYPE_MAX
};
@@ -84,7 +86,6 @@ _packed_ struct FieldObject {
le64_t hash;
le64_t next_hash_offset;
le64_t head_data_offset;
- le64_t tail_data_offset;
uint8_t payload[];
};
@@ -119,12 +120,11 @@ _packed_ struct EntryArrayObject {
le64_t items[];
};
-#define SIGNATURE_LENGTH 160
+#define TAG_LENGTH (256/8)
-_packed_ struct SignatureObject {
+_packed_ struct TagObject {
ObjectHeader object;
- le64_t from;
- uint8_t signature[SIGNATURE_LENGTH];
+ uint8_t tag[TAG_LENGTH]; /* SHA-256 HMAC */
};
union Object {
@@ -134,7 +134,7 @@ union Object {
EntryObject entry;
HashTableObject hash_table;
EntryArrayObject entry_array;
- SignatureObject signature;
+ TagObject tag;
};
enum {
@@ -149,17 +149,19 @@ enum {
};
enum {
- HEADER_COMPATIBLE_SIGNED = 1
+ HEADER_COMPATIBLE_AUTHENTICATED = 1
};
+#define HEADER_SIGNATURE ((char[]) { 'L', 'P', 'K', 'S', 'H', 'H', 'R', 'H' })
+
_packed_ struct Header {
uint8_t signature[8]; /* "LPKSHHRH" */
- uint32_t compatible_flags;
- uint32_t incompatible_flags;
+ le32_t compatible_flags;
+ le32_t incompatible_flags;
uint8_t state;
uint8_t reserved[7];
sd_id128_t file_id;
- sd_id128_t machine_id; /* last writer */
+ sd_id128_t machine_id;
sd_id128_t boot_id; /* last writer */
sd_id128_t seqnum_id;
le64_t header_size;
@@ -181,3 +183,19 @@ _packed_ struct Header {
le64_t n_data;
le64_t n_fields;
};
+
+#define FSPRG_HEADER_SIGNATURE ((char[]) { 'K', 'S', 'H', 'H', 'R', 'H', 'L', 'P' })
+
+_packed_ struct FSPRGHeader {
+ uint8_t signature[8]; /* "KSHHRHLP" */
+ le32_t compatible_flags;
+ le32_t incompatible_flags;
+ sd_id128_t machine_id;
+ sd_id128_t boot_id; /* last writer */
+ le64_t header_size;
+ le64_t fsprg_start_usec;
+ le64_t fsprg_interval_usec;
+ le16_t secpar;
+ le16_t reserved[3];
+ le64_t state_size;
+};
diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c
index 718dc5d..0e48893 100644
--- a/src/journal/journal-file.c
+++ b/src/journal/journal-file.c
@@ -31,6 +31,7 @@
#include "journal-file.h"
#include "lookup3.h"
#include "compress.h"
+#include "fsprg.h"
#define DEFAULT_DATA_HASH_TABLE_SIZE (2047ULL*sizeof(HashItem))
#define DEFAULT_FIELD_HASH_TABLE_SIZE (333ULL*sizeof(HashItem))
@@ -66,13 +67,21 @@
#define JOURNAL_HEADER_CONTAINS(h, field) \
(le64toh((h)->header_size) >= offsetof(Header, field) + sizeof((h)->field))
-static const char signature[] = { 'L', 'P', 'K', 'S', 'H', 'H', 'R', 'H' };
+static int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime);
void journal_file_close(JournalFile *f) {
int t;
assert(f);
+ /* Sync everything to disk, before we mark the file offline */
+ for (t = 0; t < _WINDOW_MAX; t++)
+ if (f->windows[t].ptr)
+ munmap(f->windows[t].ptr, f->windows[t].size);
+
+ if (f->writable && f->fd >= 0)
+ fdatasync(f->fd);
+
if (f->header) {
/* Mark the file offline. Don't override the archived state if it already is set */
if (f->writable && f->header->state == STATE_ONLINE)
@@ -81,10 +90,6 @@ void journal_file_close(JournalFile *f) {
munmap(f->header, PAGE_ALIGN(sizeof(Header)));
}
- for (t = 0; t < _WINDOW_MAX; t++)
- if (f->windows[t].ptr)
- munmap(f->windows[t].ptr, f->windows[t].size);
-
if (f->fd >= 0)
close_nointr_nofail(f->fd);
@@ -94,6 +99,14 @@ void journal_file_close(JournalFile *f) {
free(f->compress_buffer);
#endif
+#ifdef HAVE_GCRYPT
+ if (f->fsprg_header)
+ munmap(f->fsprg_header, PAGE_ALIGN(f->fsprg_size));
+
+ if (f->hmac)
+ gcry_md_close(f->hmac);
+#endif
+
free(f);
}
@@ -105,9 +118,15 @@ static int journal_file_init_header(JournalFile *f, JournalFile *template) {
assert(f);
zero(h);
- memcpy(h.signature, signature, 8);
+ memcpy(h.signature, HEADER_SIGNATURE, 8);
h.header_size = htole64(ALIGN64(sizeof(h)));
+ h.incompatible_flags =
+ htole32(f->compress ? HEADER_INCOMPATIBLE_COMPRESSED : 0);
+
+ h.compatible_flags =
+ htole32(f->authenticate ? HEADER_COMPATIBLE_AUTHENTICATED : 0);
+
r = sd_id128_randomize(&h.file_id);
if (r < 0)
return r;
@@ -149,7 +168,9 @@ static int journal_file_refresh_header(JournalFile *f) {
f->header->state = STATE_ONLINE;
- __sync_synchronize();
+ /* Sync the online state to disk */
+ msync(f->header, PAGE_ALIGN(sizeof(Header)), MS_SYNC);
+ fdatasync(f->fd);
return 0;
}
@@ -157,17 +178,31 @@ static int journal_file_refresh_header(JournalFile *f) {
static int journal_file_verify_header(JournalFile *f) {
assert(f);
- if (memcmp(f->header, signature, 8))
+ if (memcmp(f->header->signature, HEADER_SIGNATURE, 8))
return -EBADMSG;
+ /* In both read and write mode we refuse to open files with
+ * incompatible flags we don't know */
#ifdef HAVE_XZ
- if ((le64toh(f->header->incompatible_flags) & ~HEADER_INCOMPATIBLE_COMPRESSED) != 0)
+ if ((le32toh(f->header->incompatible_flags) & ~HEADER_INCOMPATIBLE_COMPRESSED) != 0)
return -EPROTONOSUPPORT;
#else
if (f->header->incompatible_flags != 0)
return -EPROTONOSUPPORT;
#endif
+ /* When open for writing we refuse to open files with
+ * compatible flags, too */
+ if (f->writable) {
+#ifdef HAVE_GCRYPT
+ if ((le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_AUTHENTICATED) != 0)
+ return -EPROTONOSUPPORT;
+#else
+ if (f->header->compatible_flags != 0)
+ return -EPROTONOSUPPORT;
+#endif
+ }
+
/* The first addition was n_data, so check that we are at least this large */
if (le64toh(f->header->header_size) < HEADER_SIZE_MIN)
return -EBADMSG;
@@ -200,6 +235,9 @@ static int journal_file_verify_header(JournalFile *f) {
}
}
+ f->compress = !!(le32toh(f->header->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED);
+ f->authenticate = !!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_AUTHENTICATED);
+
return 0;
}
@@ -781,8 +819,6 @@ static int journal_file_append_data(
o->object.size = htole64(offsetof(Object, data.payload) + rsize);
o->object.flags |= OBJECT_COMPRESSED;
- f->header->incompatible_flags = htole32(le32toh(f->header->incompatible_flags) | HEADER_INCOMPATIBLE_COMPRESSED);
-
log_debug("Compressed data object %lu -> %lu", (unsigned long) size, (unsigned long) rsize);
}
}
@@ -1057,6 +1093,10 @@ int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const st
ts->monotonic < le64toh(f->header->tail_entry_monotonic))
return -EINVAL;
+ r = journal_file_maybe_append_tag(f, ts->realtime);
+ if (r < 0)
+ return r;
+
/* alloca() can't take 0, hence let's allocate at least one */
items = alloca(sizeof(EntryItem) * MAX(1, n_iovec));
@@ -1832,6 +1872,394 @@ int journal_file_move_to_entry_by_realtime_for_data(
ret, offset, NULL);
}
+static void *fsprg_state(JournalFile *f) {
+ uint64_t a, b;
+ assert(f);
+
+ if (!f->authenticate)
+ return NULL;
+
+ a = le64toh(f->fsprg_header->header_size);
+ b = le64toh(f->fsprg_header->state_size);
+
+ if (a + b > f->fsprg_size)
+ return NULL;
+
+ return (uint8_t*) f->fsprg_header + a;
+}
+
+static int journal_file_append_tag(JournalFile *f) {
+ Object *o;
+ uint64_t p;
+ int r;
+
+ assert(f);
+
+ if (!f->authenticate)
+ return 0;
+
+ if (!f->hmac_running)
+ return 0;
+
+ log_debug("Writing tag for epoch %llu\n", (unsigned long long) FSPRG_GetEpoch(fsprg_state(f)));
+
+ assert(f->hmac);
+
+ r = journal_file_append_object(f, OBJECT_TAG, sizeof(struct TagObject), &o, &p);
+ if (r < 0)
+ return r;
+
+ /* Get the HMAC tag and store it in the object */
+ memcpy(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH);
+ f->hmac_running = false;
+
+ return 0;
+}
+
+static int journal_file_hmac_start(JournalFile *f) {
+ uint8_t key[256 / 8]; /* Let's pass 256 bit from FSPRG to HMAC */
+
+ assert(f);
+
+ if (!f->authenticate)
+ return 0;
+
+ if (f->hmac_running)
+ return 0;
+
+ /* Prepare HMAC for next cycle */
+ gcry_md_reset(f->hmac);
+ FSPRG_GetKey(fsprg_state(f), key, sizeof(key), 0);
+ gcry_md_setkey(f->hmac, key, sizeof(key));
+
+ f->hmac_running = true;
+
+ return 0;
+}
+
+static int journal_file_get_epoch(JournalFile *f, uint64_t realtime, uint64_t *epoch) {
+ uint64_t t;
+
+ assert(f);
+ assert(epoch);
+ assert(f->authenticate);
+
+ if (le64toh(f->fsprg_header->fsprg_start_usec) == 0 ||
+ le64toh(f->fsprg_header->fsprg_interval_usec) == 0)
+ return -ENOTSUP;
+
+ if (realtime < le64toh(f->fsprg_header->fsprg_start_usec))
+ return -ESTALE;
+
+ t = realtime - le64toh(f->fsprg_header->fsprg_start_usec);
+ t = t / le64toh(f->fsprg_header->fsprg_interval_usec);
+
+ *epoch = t;
+ return 0;
+}
+
+static int journal_file_need_evolve(JournalFile *f, uint64_t realtime) {
+ uint64_t goal, epoch;
+ int r;
+ assert(f);
+
+ if (!f->authenticate)
+ return 0;
+
+ r = journal_file_get_epoch(f, realtime, &goal);
+ if (r < 0)
+ return r;
+
+ epoch = FSPRG_GetEpoch(fsprg_state(f));
+ if (epoch > goal)
+ return -ESTALE;
+
+ return epoch != goal;
+}
+
+static int journal_file_evolve(JournalFile *f, uint64_t realtime) {
+ uint64_t goal, epoch;
+ int r;
+
+ assert(f);
+
+ if (!f->authenticate)
+ return 0;
+
+ r = journal_file_get_epoch(f, realtime, &goal);
+ if (r < 0)
+ return r;
+
+ epoch = FSPRG_GetEpoch(fsprg_state(f));
+ if (epoch < goal)
+ log_debug("Evolving FSPRG key from epoch %llu to %llu.", (unsigned long long) epoch, (unsigned long long) goal);
+
+ for (;;) {
+ if (epoch > goal)
+ return -ESTALE;
+ if (epoch == goal)
+ return 0;
+
+ FSPRG_Evolve(fsprg_state(f));
+ epoch = FSPRG_GetEpoch(fsprg_state(f));
+ }
+}
+
+static int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime) {
+ int r;
+
+ assert(f);
+
+ if (!f->authenticate)
+ return 0;
+
+ r = journal_file_need_evolve(f, realtime);
+ if (r <= 0)
+ return 0;
+
+ r = journal_file_append_tag(f);
+ if (r < 0)
+ return r;
+
+ r = journal_file_evolve(f, realtime);
+ if (r < 0)
+ return r;
+
+ r = journal_file_hmac_start(f);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int journal_file_hmac_put_object(JournalFile *f, int type, uint64_t p) {
+ int r;
+ Object *o;
+
+ assert(f);
+
+ if (!f->authenticate)
+ return 0;
+
+ r = journal_file_hmac_start(f);
+ if (r < 0)
+ return r;
+
+ r = journal_file_move_to_object(f, type, p, &o);
+ if (r < 0)
+ return r;
+
+ gcry_md_write(f->hmac, o, offsetof(ObjectHeader, payload));
+
+ switch (o->object.type) {
+
+ case OBJECT_DATA:
+ /* All but: entry_array_offset, n_entries are mutable */
+ gcry_md_write(f->hmac, &o->data.hash, offsetof(DataObject, entry_array_offset) - offsetof(DataObject, hash));
+ gcry_md_write(f->hmac, o->data.payload, le64toh(o->object.size) - offsetof(DataObject, payload));
+ break;
+
+ case OBJECT_ENTRY:
+ /* All */
+ gcry_md_write(f->hmac, &o->entry.seqnum, le64toh(o->object.size) - offsetof(EntryObject, seqnum));
+ break;
+
+ case OBJECT_FIELD_HASH_TABLE:
+ case OBJECT_DATA_HASH_TABLE:
+ case OBJECT_ENTRY_ARRAY:
+ /* Nothing: everything is mutable */
+ break;
+
+ case OBJECT_TAG:
+ /* All */
+ gcry_md_write(f->hmac, o->tag.tag, le64toh(o->object.size) - offsetof(TagObject, tag));
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int journal_file_hmac_put_header(JournalFile *f) {
+ int r;
+
+ assert(f);
+
+ if (!f->authenticate)
+ return 0;
+
+ r = journal_file_hmac_start(f);
+ if (r < 0)
+ return r;
+
+ /* All but state+reserved, boot_id, arena_size,
+ * tail_object_offset, n_objects, n_entries, tail_seqnum,
+ * head_entry_realtime, tail_entry_realtime,
+ * tail_entry_monotonic, n_data, n_fields, header_tag */
+
+ gcry_md_write(f->hmac, f->header->signature, offsetof(Header, state) - offsetof(Header, signature));
+ gcry_md_write(f->hmac, &f->header->file_id, offsetof(Header, boot_id) - offsetof(Header, file_id));
+ gcry_md_write(f->hmac, &f->header->seqnum_id, offsetof(Header, arena_size) - offsetof(Header, seqnum_id));
+ gcry_md_write(f->hmac, &f->header->data_hash_table_offset, offsetof(Header, tail_object_offset) - offsetof(Header, data_hash_table_offset));
+ gcry_md_write(f->hmac, &f->header->head_seqnum, offsetof(Header, head_entry_realtime) - offsetof(Header, head_seqnum));
+
+ return 0;
+}
+
+static int journal_file_load_fsprg(JournalFile *f) {
+ int r, fd = -1;
+ char *p = NULL;
+ struct stat st;
+ FSPRGHeader *m = NULL;
+ sd_id128_t machine;
+
+ assert(f);
+
+ if (!f->authenticate)
+ return 0;
+
+ r = sd_id128_get_machine(&machine);
+ if (r < 0)
+ return r;
+
+ if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fsprg",
+ SD_ID128_FORMAT_VAL(machine)) < 0)
+ return -ENOMEM;
+
+ fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY, 0600);
+ if (fd < 0) {
+ log_error("Failed to open %s: %m", p);
+ r = -errno;
+ goto finish;
+ }
+
+ if (fstat(fd, &st) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ if (st.st_size < (off_t) sizeof(FSPRGHeader)) {
+ r = -ENODATA;
+ goto finish;
+ }
+
+ m = mmap(NULL, PAGE_ALIGN(sizeof(FSPRGHeader)), PROT_READ, MAP_SHARED, fd, 0);
+ if (m == MAP_FAILED) {
+ m = NULL;
+ r = -errno;
+ goto finish;
+ }
+
+ if (memcmp(m->signature, FSPRG_HEADER_SIGNATURE, 8) != 0) {
+ r = -EBADMSG;
+ goto finish;
+ }
+
+ if (m->incompatible_flags != 0) {
+ r = -EPROTONOSUPPORT;
+ goto finish;
+ }
+
+ if (le64toh(m->header_size) < sizeof(FSPRGHeader)) {
+ r = -EBADMSG;
+ goto finish;
+ }
+
+ if (le64toh(m->state_size) != FSPRG_stateinbytes(m->secpar)) {
+ r = -EBADMSG;
+ goto finish;
+ }
+
+ f->fsprg_size = le64toh(m->header_size) + le64toh(m->state_size);
+ if ((uint64_t) st.st_size < f->fsprg_size) {
+ r = -ENODATA;
+ goto finish;
+ }
+
+ if (!sd_id128_equal(machine, m->machine_id)) {
+ r = -EHOSTDOWN;
+ goto finish;
+ }
+
+ if (le64toh(m->fsprg_start_usec) <= 0 ||
+ le64toh(m->fsprg_interval_usec) <= 0) {
+ r = -EBADMSG;
+ goto finish;
+ }
+
+ f->fsprg_header = mmap(NULL, PAGE_ALIGN(f->fsprg_size), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+ if (f->fsprg_header == MAP_FAILED) {
+ f->fsprg_header = NULL;
+ r = -errno;
+ goto finish;
+ }
+
+ r = 0;
+
+finish:
+ if (m)
+ munmap(m, PAGE_ALIGN(sizeof(FSPRGHeader)));
+
+ if (fd >= 0)
+ close_nointr_nofail(fd);
+
+ free(p);
+ return r;
+}
+
+static int journal_file_setup_hmac(JournalFile *f) {
+ gcry_error_t e;
+
+ if (!f->authenticate)
+ return 0;
+
+ e = gcry_md_open(&f->hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
+ if (e != 0)
+ return -ENOTSUP;
+
+ return 0;
+}
+
+static int journal_file_append_first_tag(JournalFile *f) {
+ int r;
+ uint64_t p;
+
+ if (!f->authenticate)
+ return 0;
+
+ log_debug("Calculating first tag...");
+
+ r = journal_file_hmac_put_header(f);
+ if (r < 0)
+ return r;
+
+ p = le64toh(f->header->field_hash_table_offset);
+ if (p < offsetof(Object, hash_table.items))
+ return -EINVAL;
+ p -= offsetof(Object, hash_table.items);
+
+ r = journal_file_hmac_put_object(f, OBJECT_FIELD_HASH_TABLE, p);
+ if (r < 0)
+ return r;
+
+ p = le64toh(f->header->data_hash_table_offset);
+ if (p < offsetof(Object, hash_table.items))
+ return -EINVAL;
+ p -= offsetof(Object, hash_table.items);
+
+ r = journal_file_hmac_put_object(f, OBJECT_DATA_HASH_TABLE, p);
+ if (r < 0)
+ return r;
+
+ r = journal_file_append_tag(f);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
void journal_file_dump(JournalFile *f) {
Object *o;
int r;
@@ -1876,8 +2304,8 @@ void journal_file_dump(JournalFile *f) {
printf("Type: OBJECT_ENTRY_ARRAY\n");
break;
- case OBJECT_SIGNATURE:
- printf("Type: OBJECT_SIGNATURE\n");
+ case OBJECT_TAG:
+ printf("Type: OBJECT_TAG\n");
break;
}
@@ -1928,8 +2356,8 @@ void journal_file_print_header(JournalFile *f) {
f->header->state == STATE_OFFLINE ? "offline" :
f->header->state == STATE_ONLINE ? "online" :
f->header->state == STATE_ARCHIVED ? "archived" : "unknown",
- (f->header->compatible_flags & HEADER_COMPATIBLE_SIGNED) ? " SIGNED" : "",
- (f->header->compatible_flags & ~HEADER_COMPATIBLE_SIGNED) ? " ???" : "",
+ (f->header->compatible_flags & HEADER_COMPATIBLE_AUTHENTICATED) ? " AUTHENTICATED" : "",
+ (f->header->compatible_flags & ~HEADER_COMPATIBLE_AUTHENTICATED) ? " ???" : "",
(f->header->incompatible_flags & HEADER_INCOMPATIBLE_COMPRESSED) ? " COMPRESSED" : "",
(f->header->incompatible_flags & ~HEADER_INCOMPATIBLE_COMPRESSED) ? " ???" : "",
(unsigned long long) le64toh(f->header->header_size),
@@ -1961,6 +2389,8 @@ int journal_file_open(
const char *fname,
int flags,
mode_t mode,
+ bool compress,
+ bool authenticate,
JournalMetrics *metrics,
JournalFile *template,
JournalFile **ret) {
@@ -1983,13 +2413,13 @@ int journal_file_open(
return -ENOMEM;
f->fd = -1;
- f->flags = flags;
f->mode = mode;
- f->writable = (flags & O_ACCMODE) != O_RDONLY;
- f->prot = prot_from_flags(flags);
- if (template)
- f->compress = template->compress;
+ f->flags = flags;
+ f->prot = prot_from_flags(flags);
+ f->writable = (flags & O_ACCMODE) != O_RDONLY;
+ f->compress = compress;
+ f->authenticate = authenticate;
f->path = strdup(fname);
if (!f->path) {
@@ -2011,6 +2441,12 @@ int journal_file_open(
if (f->last_stat.st_size == 0 && f->writable) {
newly_created = true;
+ /* Try to load the FSPRG state, and if we can't, then
+ * just don't do authentication */
+ r = journal_file_load_fsprg(f);
+ if (r < 0)
+ f->authenticate = false;
+
r = journal_file_init_header(f, template);
if (r < 0)
goto fail;
@@ -2037,6 +2473,10 @@ int journal_file_open(
r = journal_file_verify_header(f);
if (r < 0)
goto fail;
+
+ r = journal_file_load_fsprg(f);
+ if (r < 0)
+ goto fail;
}
if (f->writable) {
@@ -2049,10 +2489,13 @@ int journal_file_open(
r = journal_file_refresh_header(f);
if (r < 0)
goto fail;
+
+ r = journal_file_setup_hmac(f);
+ if (r < 0)
+ goto fail;
}
if (newly_created) {
-
r = journal_file_setup_field_hash_table(f);
if (r < 0)
goto fail;
@@ -2060,6 +2503,10 @@ int journal_file_open(
r = journal_file_setup_data_hash_table(f);
if (r < 0)
goto fail;
+
+ r = journal_file_append_first_tag(f);
+ if (r < 0)
+ goto fail;
}
r = journal_file_map_field_hash_table(f);
@@ -2081,7 +2528,7 @@ fail:
return r;
}
-int journal_file_rotate(JournalFile **f) {
+int journal_file_rotate(JournalFile **f, bool compress, bool authenticate) {
char *p;
size_t l;
JournalFile *old_file, *new_file = NULL;
@@ -2120,7 +2567,7 @@ int journal_file_rotate(JournalFile **f) {
old_file->header->state = STATE_ARCHIVED;
- r = journal_file_open(old_file->path, old_file->flags, old_file->mode, NULL, old_file, &new_file);
+ r = journal_file_open(old_file->path, old_file->flags, old_file->mode, compress, authenticate, NULL, old_file, &new_file);
journal_file_close(old_file);
*f = new_file;
@@ -2131,6 +2578,8 @@ int journal_file_open_reliably(
const char *fname,
int flags,
mode_t mode,
+ bool compress,
+ bool authenticate,
JournalMetrics *metrics,
JournalFile *template,
JournalFile **ret) {
@@ -2139,7 +2588,7 @@ int journal_file_open_reliably(
size_t l;
char *p;
- r = journal_file_open(fname, flags, mode, metrics, template, ret);
+ r = journal_file_open(fname, flags, mode, compress, authenticate, metrics, template, ret);
if (r != -EBADMSG && /* corrupted */
r != -ENODATA && /* truncated */
r != -EHOSTDOWN && /* other machine */
@@ -2154,6 +2603,9 @@ int journal_file_open_reliably(
if (!(flags & O_CREAT))
return r;
+ if (!endswith(fname, ".journal"))
+ return r;
+
/* The file is corrupted. Rotate it away and try it again (but only once) */
l = strlen(fname);
@@ -2170,7 +2622,7 @@ int journal_file_open_reliably(
log_warning("File %s corrupted or uncleanly shut down, renaming and replacing.", fname);
- return journal_file_open(fname, flags, mode, metrics, template, ret);
+ return journal_file_open(fname, flags, mode, compress, authenticate, metrics, template, ret);
}
struct vacuum_info {
diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h
index eed49e0..25d9720 100644
--- a/src/journal/journal-file.h
+++ b/src/journal/journal-file.h
@@ -23,6 +23,10 @@
#include <inttypes.h>
+#ifdef HAVE_GCRYPT
+#include <gcrypt.h>
+#endif
+
#include <systemd/sd-id128.h>
#include "sparse-endian.h"
@@ -42,7 +46,7 @@ enum {
WINDOW_DATA_HASH_TABLE = OBJECT_DATA_HASH_TABLE,
WINDOW_FIELD_HASH_TABLE = OBJECT_FIELD_HASH_TABLE,
WINDOW_ENTRY_ARRAY = OBJECT_ENTRY_ARRAY,
- WINDOW_SIGNATURE = OBJECT_SIGNATURE,
+ WINDOW_TAG = OBJECT_TAG,
WINDOW_HEADER,
_WINDOW_MAX
};
@@ -59,9 +63,13 @@ typedef struct JournalFile {
char *path;
struct stat last_stat;
mode_t mode;
+
int flags;
int prot;
bool writable;
+ bool compress;
+ bool authenticate;
+
bool tail_entry_monotonic_valid;
Header *header;
@@ -74,12 +82,18 @@ typedef struct JournalFile {
JournalMetrics metrics;
- bool compress;
-
#ifdef HAVE_XZ
void *compress_buffer;
uint64_t compress_buffer_size;
#endif
+
+#ifdef HAVE_GCRYPT
+ gcry_md_hd_t hmac;
+ bool hmac_running;
+
+ FSPRGHeader *fsprg_header;
+ size_t fsprg_size;
+#endif
} JournalFile;
typedef enum direction {
@@ -91,6 +105,8 @@ int journal_file_open(
const char *fname,
int flags,
mode_t mode,
+ bool compress,
+ bool authenticate,
JournalMetrics *metrics,
JournalFile *template,
JournalFile **ret);
@@ -101,6 +117,8 @@ int journal_file_open_reliably(
const char *fname,
int flags,
mode_t mode,
+ bool compress,
+ bool authenticate,
JournalMetrics *metrics,
JournalFile *template,
JournalFile **ret);
@@ -134,7 +152,7 @@ int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint6
void journal_file_dump(JournalFile *f);
void journal_file_print_header(JournalFile *f);
-int journal_file_rotate(JournalFile **f);
+int journal_file_rotate(JournalFile **f, bool compress, bool authenticate);
int journal_directory_vacuum(const char *directory, uint64_t max_use, uint64_t min_free);
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index 62bb904..138bf09 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -41,6 +41,10 @@
#include "logs-show.h"
#include "strv.h"
#include "journal-internal.h"
+#include "fsprg.h"
+#include "journal-def.h"
+
+#define DEFAULT_FSPRG_INTERVAL_USEC (15*USEC_PER_MINUTE)
static OutputMode arg_output = OUTPUT_SHORT;
static bool arg_follow = false;
@@ -48,14 +52,19 @@ static bool arg_show_all = false;
static bool arg_no_pager = false;
static int arg_lines = -1;
static bool arg_no_tail = false;
-static bool arg_new_id128 = false;
-static bool arg_print_header = false;
static bool arg_quiet = false;
static bool arg_local = false;
static bool arg_this_boot = false;
static const char *arg_directory = NULL;
static int arg_priorities = 0xFF;
+static enum {
+ ACTION_SHOW,
+ ACTION_NEW_ID128,
+ ACTION_PRINT_HEADER,
+ ACTION_SETUP_KEYS
+} arg_action = ACTION_SHOW;
+
static int help(void) {
printf("%s [OPTIONS...] [MATCH]\n\n"
@@ -73,9 +82,11 @@ static int help(void) {
" -l --local Only local entries\n"
" -b --this-boot Show data only from current boot\n"
" -D --directory=PATH Show journal files from directory\n"
- " -p --priority=RANGE Show only messages within the specified priority range\n"
+ " -p --priority=RANGE Show only messages within the specified priority range\n\n"
+ "Commands:\n"
+ " --new-id128 Generate a new 128 Bit id\n"
" --header Show journal header information\n"
- " --new-id128 Generate a new 128 Bit id\n",
+ " --setup-keys Generate new FSPRG key pair\n",
program_invocation_short_name);
return 0;
@@ -88,7 +99,8 @@ static int parse_argv(int argc, char *argv[]) {
ARG_NO_PAGER,
ARG_NO_TAIL,
ARG_NEW_ID128,
- ARG_HEADER
+ ARG_HEADER,
+ ARG_SETUP_KEYS
};
static const struct option options[] = {
@@ -107,6 +119,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "directory", required_argument, NULL, 'D' },
{ "header", no_argument, NULL, ARG_HEADER },
{ "priority", no_argument, NULL, 'p' },
+ { "setup-keys",no_argument, NULL, ARG_SETUP_KEYS},
{ NULL, 0, NULL, 0 }
};
@@ -163,7 +176,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_NEW_ID128:
- arg_new_id128 = true;
+ arg_action = ACTION_NEW_ID128;
break;
case 'q':
@@ -183,7 +196,11 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_HEADER:
- arg_print_header = true;
+ arg_action = ACTION_PRINT_HEADER;
+ break;
+
+ case ARG_SETUP_KEYS:
+ arg_action = ACTION_SETUP_KEYS;
break;
case 'p': {
@@ -400,6 +417,161 @@ static int add_priorities(sd_journal *j) {
return 0;
}
+static int setup_keys(void) {
+#ifdef HAVE_GCRYPT
+ size_t mpk_size, seed_size, state_size, i;
+ uint8_t *mpk, *seed, *state;
+ ssize_t l;
+ int fd = -1, r;
+ sd_id128_t machine, boot;
+ char *p = NULL, *k = NULL;
+ struct FSPRGHeader h;
+ uint64_t n, interval;
+
+ r = sd_id128_get_machine(&machine);
+ if (r < 0) {
+ log_error("Failed to get machine ID: %s", strerror(-r));
+ return r;
+ }
+
+ r = sd_id128_get_boot(&boot);
+ if (r < 0) {
+ log_error("Failed to get boot ID: %s", strerror(-r));
+ return r;
+ }
+
+ if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fsprg",
+ SD_ID128_FORMAT_VAL(machine)) < 0)
+ return log_oom();
+
+ if (access(p, F_OK) >= 0) {
+ log_error("Evolving key file %s exists already.", p);
+ r = -EEXIST;
+ goto finish;
+ }
+
+ if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fsprg.tmp.XXXXXX",
+ SD_ID128_FORMAT_VAL(machine)) < 0) {
+ r = log_oom();
+ goto finish;
+ }
+
+ mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
+ mpk = alloca(mpk_size);
+
+ seed_size = FSPRG_RECOMMENDED_SEEDLEN;
+ seed = alloca(seed_size);
+
+ state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
+ state = alloca(state_size);
+
+ fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0) {
+ log_error("Failed to open /dev/random: %m");
+ r = -errno;
+ goto finish;
+ }
+
+ log_info("Generating seed...");
+ l = loop_read(fd, seed, seed_size, true);
+ if (l < 0 || (size_t) l != seed_size) {
+ log_error("Failed to read random seed: %s", strerror(EIO));
+ r = -EIO;
+ goto finish;
+ }
+
+ log_info("Generating key pair...");
+ FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
+
+ log_info("Generating evolving key...");
+ FSPRG_GenState0(state, mpk, seed, seed_size);
+
+ interval = DEFAULT_FSPRG_INTERVAL_USEC;
+ n = now(CLOCK_REALTIME);
+ n /= interval;
+
+ close_nointr_nofail(fd);
+ fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0) {
+ log_error("Failed to open %s: %m", k);
+ r = -errno;
+ goto finish;
+ }
+
+ zero(h);
+ memcpy(h.signature, "KSHHRHLP", 8);
+ h.machine_id = machine;
+ h.boot_id = boot;
+ h.header_size = htole64(sizeof(h));
+ h.fsprg_start_usec = htole64(n * interval);
+ h.fsprg_interval_usec = htole64(interval);
+ h.secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
+ h.state_size = htole64(state_size);
+
+ l = loop_write(fd, &h, sizeof(h), false);
+ if (l < 0 || (size_t) l != sizeof(h)) {
+ log_error("Failed to write header: %s", strerror(EIO));
+ r = -EIO;
+ goto finish;
+ }
+
+ l = loop_write(fd, state, state_size, false);
+ if (l < 0 || (size_t) l != state_size) {
+ log_error("Failed to write state: %s", strerror(EIO));
+ r = -EIO;
+ goto finish;
+ }
+
+ if (link(k, p) < 0) {
+ log_error("Failed to link file: %m");
+ r = -errno;
+ goto finish;
+ }
+
+ if (isatty(STDOUT_FILENO)) {
+ fprintf(stderr,
+ "\n"
+ "The new key pair has been generated. The evolving key has been written to the\n"
+ "following file. It will be used to protect local journal files. This file does\n"
+ "not need to be kept secret. It should not be used on multiple hosts.\n"
+ "\n"
+ "\t%s\n"
+ "\n"
+ "Please write down the following " ANSI_HIGHLIGHT_ON "secret" ANSI_HIGHLIGHT_OFF " seed value. It should not be stored\n"
+ "locally on disk, and may be used to verify journal files from this host.\n"
+ "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
+ fflush(stderr);
+ }
+ for (i = 0; i < seed_size; i++) {
+ if (i > 0 && i % 3 == 0)
+ putchar('-');
+ printf("%02x", ((uint8_t*) seed)[i]);
+ }
+
+ printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) interval);
+
+ if (isatty(STDOUT_FILENO))
+ fputs(ANSI_HIGHLIGHT_OFF "\n", stderr);
+
+ r = 0;
+
+finish:
+ if (fd >= 0)
+ close_nointr_nofail(fd);
+
+ if (k) {
+ unlink(k);
+ free(k);
+ }
+
+ free(p);
+
+ return r;
+#else
+ log_error("Forward-secure journal verification not available.");
+#endif
+}
+
int main(int argc, char *argv[]) {
int r;
sd_journal *j = NULL;
@@ -416,11 +588,16 @@ int main(int argc, char *argv[]) {
if (r <= 0)
goto finish;
- if (arg_new_id128) {
+ if (arg_action == ACTION_NEW_ID128) {
r = generate_new_id128();
goto finish;
}
+ if (arg_action == ACTION_SETUP_KEYS) {
+ r = setup_keys();
+ goto finish;
+ }
+
#ifdef HAVE_ACL
if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this message off.");
@@ -436,7 +613,7 @@ int main(int argc, char *argv[]) {
goto finish;
}
- if (arg_print_header) {
+ if (arg_action == ACTION_PRINT_HEADER) {
journal_print_header(j);
r = 0;
goto finish;
diff --git a/src/journal/journald.c b/src/journal/journald.c
index 7c89689..8c41d9b 100644
--- a/src/journal/journald.c
+++ b/src/journal/journald.c
@@ -281,7 +281,6 @@ static JournalFile* find_journal(Server *s, uid_t uid) {
char *p;
int r;
JournalFile *f;
- char ids[33];
sd_id128_t machine;
assert(s);
@@ -305,7 +304,8 @@ static JournalFile* find_journal(Server *s, uid_t uid) {
if (f)
return f;
- if (asprintf(&p, "/var/log/journal/%s/user-%lu.journal", sd_id128_to_string(machine, ids), (unsigned long) uid) < 0)
+ if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/user-%lu.journal",
+ SD_ID128_FORMAT_VAL(machine), (unsigned long) uid) < 0)
return s->system_journal;
while (hashmap_size(s->user_journals) >= USER_JOURNALS_MAX) {
@@ -315,7 +315,7 @@ static JournalFile* find_journal(Server *s, uid_t uid) {
journal_file_close(f);
}
- r = journal_file_open_reliably(p, O_RDWR|O_CREAT, 0640, &s->system_metrics, s->system_journal, &f);
+ r = journal_file_open_reliably(p, O_RDWR|O_CREAT, 0640, s->compress, false, &s->system_metrics, s->system_journal, &f);
free(p);
if (r < 0)
@@ -341,7 +341,7 @@ static void server_rotate(Server *s) {
log_info("Rotating...");
if (s->runtime_journal) {
- r = journal_file_rotate(&s->runtime_journal);
+ r = journal_file_rotate(&s->runtime_journal, s->compress, false);
if (r < 0)
if (s->runtime_journal)
log_error("Failed to rotate %s: %s", s->runtime_journal->path, strerror(-r));
@@ -352,7 +352,7 @@ static void server_rotate(Server *s) {
}
if (s->system_journal) {
- r = journal_file_rotate(&s->system_journal);
+ r = journal_file_rotate(&s->system_journal, s->compress, true);
if (r < 0)
if (s->system_journal)
log_error("Failed to rotate %s: %s", s->system_journal->path, strerror(-r));
@@ -364,7 +364,7 @@ static void server_rotate(Server *s) {
}
HASHMAP_FOREACH_KEY(f, k, s->user_journals, i) {
- r = journal_file_rotate(&f);
+ r = journal_file_rotate(&f, s->compress, false);
if (r < 0)
if (f->path)
log_error("Failed to rotate %s: %s", f->path, strerror(-r));
@@ -2006,14 +2006,12 @@ static int system_journal_open(Server *s) {
if (!fn)
return -ENOMEM;
- r = journal_file_open_reliably(fn, O_RDWR|O_CREAT, 0640, &s->system_metrics, NULL, &s->system_journal);
+ r = journal_file_open_reliably(fn, O_RDWR|O_CREAT, 0640, s->compress, true, &s->system_metrics, NULL, &s->system_journal);
free(fn);
- if (r >= 0) {
- s->system_journal->compress = s->compress;
-
+ if (r >= 0)
server_fix_perms(s, s->system_journal, 0);
- } else if (r < 0) {
+ else if (r < 0) {
if (r != -ENOENT && r != -EROFS)
log_warning("Failed to open system journal: %s", strerror(-r));
@@ -2035,7 +2033,7 @@ static int system_journal_open(Server *s) {
* if it already exists, so that we can flush
* it into the system journal */
- r = journal_file_open(fn, O_RDWR, 0640, &s->runtime_metrics, NULL, &s->runtime_journal);
+ r = journal_file_open(fn, O_RDWR, 0640, s->compress, false, &s->runtime_metrics, NULL, &s->runtime_journal);
free(fn);
if (r < 0) {
@@ -2051,7 +2049,7 @@ static int system_journal_open(Server *s) {
* it if necessary. */
(void) mkdir_parents(fn, 0755);
- r = journal_file_open_reliably(fn, O_RDWR|O_CREAT, 0640, &s->runtime_metrics, NULL, &s->runtime_journal);
+ r = journal_file_open_reliably(fn, O_RDWR|O_CREAT, 0640, s->compress, false, &s->runtime_metrics, NULL, &s->runtime_journal);
free(fn);
if (r < 0) {
@@ -2060,11 +2058,8 @@ static int system_journal_open(Server *s) {
}
}
- if (s->runtime_journal) {
- s->runtime_journal->compress = s->compress;
-
+ if (s->runtime_journal)
server_fix_perms(s, s->runtime_journal, 0);
- }
}
return r;
diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c
index 33686ed..359a7ca 100644
--- a/src/journal/sd-journal.c
+++ b/src/journal/sd-journal.c
@@ -1118,7 +1118,7 @@ static int add_file(sd_journal *j, const char *prefix, const char *filename) {
return 0;
}
- r = journal_file_open(path, O_RDONLY, 0, NULL, NULL, &f);
+ r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, NULL, &f);
free(path);
if (r < 0) {
diff --git a/src/journal/test-journal-stream.c b/src/journal/test-journal-stream.c
index fd448cb..0925995 100644
--- a/src/journal/test-journal-stream.c
+++ b/src/journal/test-journal-stream.c
@@ -79,9 +79,9 @@ int main(int argc, char *argv[]) {
assert_se(mkdtemp(t));
assert_se(chdir(t) >= 0);
- assert_se(journal_file_open("one.journal", O_RDWR|O_CREAT, 0666, NULL, NULL, &one) == 0);
- assert_se(journal_file_open("two.journal", O_RDWR|O_CREAT, 0666, NULL, NULL, &two) == 0);
- assert_se(journal_file_open("three.journal", O_RDWR|O_CREAT, 0666, NULL, NULL, &three) == 0);
+ assert_se(journal_file_open("one.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, &one) == 0);
+ assert_se(journal_file_open("two.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, &two) == 0);
+ assert_se(journal_file_open("three.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, &three) == 0);
for (i = 0; i < N_ENTRIES; i++) {
char *p, *q;
diff --git a/src/journal/test-journal.c b/src/journal/test-journal.c
index ca3b474..7b1583c 100644
--- a/src/journal/test-journal.c
+++ b/src/journal/test-journal.c
@@ -41,7 +41,7 @@ int main(int argc, char *argv[]) {
assert_se(mkdtemp(t));
assert_se(chdir(t) >= 0);
- assert_se(journal_file_open("test.journal", O_RDWR|O_CREAT, 0666, NULL, NULL, &f) == 0);
+ assert_se(journal_file_open("test.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, &f) == 0);
dual_timestamp_get(&ts);
@@ -109,8 +109,8 @@ int main(int argc, char *argv[]) {
assert(journal_file_move_to_entry_by_seqnum(f, 10, DIRECTION_DOWN, &o, NULL) == 0);
- journal_file_rotate(&f);
- journal_file_rotate(&f);
+ journal_file_rotate(&f, true, true);
+ journal_file_rotate(&f, true, true);
journal_file_close(f);
More information about the systemd-commits
mailing list