commit be690bc5eb12fd7280b1ebf73d7b82113db7f94c
parent cf5d2406695bca115148d801a5dac4cc9a3d14f0
Author: Remy Noulin <loader2x@gmail.com>
Date: Sat, 15 Jul 2023 09:38:20 +0200
stop using clock to avoid replay attacks, use a packet counter instead
this is because the clock jumps on some virtual machines in the cloud
heartbeat.c | 67 +++++++++++++++++++++++++++++++++++--------------------------
1 file changed, 39 insertions(+), 28 deletions(-)
Diffstat:
| M | heartbeat.c | | | 67 | +++++++++++++++++++++++++++++++++++++++---------------------------- |
1 file changed, 39 insertions(+), 28 deletions(-)
diff --git a/heartbeat.c b/heartbeat.c
@@ -52,6 +52,9 @@ change (event).
#define PUBLIC_KEY "public.bin"
#define LOGGER_PUBLIC_KEY "loggerPublic.bin"
+// counter filename for encryption to avoid replay attacks
+// using the clock fails (as in previous commit) because on some VMs, the clock jumps
+#define counterFilename "counter.bin"
#define defaultPeriod 1
// TODO #define defaultPeriod 120
@@ -594,6 +597,13 @@ void probe(char *cfgfile, char *secretFile, char *publicFile, char *loggerPublic
// send mail
// when agent is not logger, forward message
+ u64 counter = 0;
+ cleanCharP(counterfn) = expandHome(uHome home counterFilename);
+ if (isPath(counterfn)) {
+ // load counter value from disk
+ pError(bLReadFile(counterfn, &counter, sizeof(counter)));
+ }
+
keyst keys = init0Var;
// load keys
@@ -714,7 +724,6 @@ void probe(char *cfgfile, char *secretFile, char *publicFile, char *loggerPublic
// open log files and load agent public keys
int agentf[lenG(agents)];
u8 agentPublicKeys[lenG(agents)][sizeof(keys.remotePublicKey)];
- i64 drift[lenG(agents)]; // drift time, initialize after first packet from an agent
if (logger) {
arange(i, agentf) {
agentf[i] = open(agentdbs[i], O_APPEND|O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR);
@@ -737,6 +746,7 @@ void probe(char *cfgfile, char *secretFile, char *publicFile, char *loggerPublic
setG(d, "rebooted", FALSE);
setG(d, "lastBoot", startTime);
setG(d, "time", 0); // last incoming message time
+ setG(d, "count", 0); // packet counter from agent to avoid replay attacks
setG(d, "c", 0); // packet counter under a period
setG(d, "mono", 0); // current period start time
setG(d, "net", FALSE); // there is a network issue, when agent goes from down to alive and next mId is higher than last mId
@@ -814,7 +824,7 @@ void probe(char *cfgfile, char *secretFile, char *publicFile, char *loggerPublic
u32 info;
} msgt;
typ struct PACKED {
- u64 time;
+ u64 count;
msgt m;
} payloadt;
typ struct PACKED {
@@ -880,8 +890,9 @@ void probe(char *cfgfile, char *secretFile, char *publicFile, char *loggerPublic
randombytes_buf(keys.nonce, sizeof(keys.nonce));
memcpy(packet.nonce, keys.nonce, sizeof(keys.nonce));
payloadt payload;
- // set timestamp in encrypted message to avoid replay attacks
- payload.time = getCurrentUnixTime() /*- 3 */; // uncomment to introduce an error
+ // set counter in encrypted message to avoid replay attacks
+ payload.count = counter++;
+ pError0(writeFile(counterfn, &counter, sizeof(counter)));
payload.m = msg;
packet.len = selPublicEncrypt(packet.buf, sizeof(packet.buf), (u8*)&payload, sizeof(payload), &keys);
// send message
@@ -1025,7 +1036,8 @@ void probe(char *cfgfile, char *secretFile, char *publicFile, char *loggerPublic
int len = selPublicDecrypt((u8*)&payload, sizeof(payload), (u8*)&data.buf, data.len, &keys);
if (!len) {
- logE("failed to decrypt");
+ char *ip = inet_ntoa((client).sin_addr);
+ logE("failed to decrypt, ip %s", ip);
// drop packet
goto handleEventLoopSleep;
}
@@ -1033,17 +1045,14 @@ void probe(char *cfgfile, char *secretFile, char *publicFile, char *loggerPublic
// update state for this agent
cleanFinishSmallDictP(agent) = getG(completeCfg, rtSmallDictt, agents[data.id]);
- // packet is valid for 2 seconds
- u64 now = getCurrentUnixTime();
- if (u$(agent, "time") == 0) {
- // initialize drift time
- drift[data.id] = (i64)now - (i64)payload.time;
- }
-
- if (payload.time + drift[data.id] < now - 2 or payload.time + drift[data.id] > now + 2) {
- logW("Dropping packet. Wrong timestamp %"PRIu64" now %"PRIu64" diff %"PRIi64, payload.time, now, (i64)now - (i64)payload.time + drift[data.id]);
+ // packet is valid only if the counter is higher than the previous one
+ if (u$(agent, "count") and payload.count <= u$(agent, "count")) {
+ // packet counter is already initialized
+ char *ip = inet_ntoa((client).sin_addr);
+ logW("Dropping packet. Wrong remote packet count %"PRIu64" local %"PRIu64" diff %"PRIi64", ip %s", payload.count, u$(agent, "count"), (i64)u$(agent, "count") - (i64)payload.count, ip);
goto handleEventLoopSleep;
}
+ setG(agent, "count", payload.count);
// message is decrypted
msgt *m = &payload.m;
@@ -1053,7 +1062,7 @@ void probe(char *cfgfile, char *secretFile, char *publicFile, char *loggerPublic
bool saveEvent = no;
record.m = *m;
- record.time = now;
+ record.time = getCurrentUnixTime();
cleanAllocateSmallArray(mailMsg);
@@ -1081,12 +1090,12 @@ void probe(char *cfgfile, char *secretFile, char *publicFile, char *loggerPublic
}
}
bool loggerRunning = u$(agent, "time");
- setG(agent, "time", now);
+ setG(agent, "time", record.time);
char *newstate = m->messageId ? "alive" : "init";
if (m->messageId < u$(agent," // last message id, if next mId under last one, the agent rebootedmId")) {
saveEvent = yes;
setG(agent, "rebooted", TRUE);
- setG(agent, "lastBoot", now);
+ setG(agent, "lastBoot", record.time);
// send mail when agent rebooted
cleanCharP(s) = formatS("%s rebooted", agents[data.id]);
pushG(mailMsg, s);
@@ -1103,7 +1112,7 @@ void probe(char *cfgfile, char *secretFile, char *publicFile, char *loggerPublic
// in that case mails are not sent because loggerRunning is false
saveEvent = yes;
setG(agent, "net", TRUE);
- setG(agent, "lastNet", now);
+ setG(agent, "lastNet", record.time);
// send mail when agent has network issues
cleanCharP(s) = formatS("%s has network issues", agents[data.id]);
pushG(mailMsg, s);
@@ -1114,7 +1123,7 @@ void probe(char *cfgfile, char *secretFile, char *publicFile, char *loggerPublic
if (!eqG($(agent, "state"), newstate)) {
saveEvent = yes;
setG(agent, "state", newstate);
- setG(agent, "last", now);
+ setG(agent, "last", record.time);
}
cleanFinishSmallArrayP(probes) = getG(agent, rtSmallArrayt, "probes");
if (probes) {
@@ -1124,7 +1133,7 @@ void probe(char *cfgfile, char *secretFile, char *publicFile, char *loggerPublic
if (getG(p, rtBool, "state") != newstate) {
saveEvent = yes;
setG(p, "state", newstate);
- setG(p, "last", now);
+ setG(p, "last", record.time);
// send mail when service is down
char *state = newstate ? "down" : "up";
char *result = newstate ? "failed" : "ok";
@@ -1160,14 +1169,16 @@ void probe(char *cfgfile, char *secretFile, char *publicFile, char *loggerPublic
sendMail(mutt);
}
- // send to monitor
- //lv(completeCfg);
- cleanCharP(monitorData) = toStringG(completeCfg);
- if (sendto(sock, monitorData, lenG(monitorData)+1, 0, (const struct sockaddr *)&server, sizeof(server)) < 0) {
- logE("monitor send failed: %s", strerror(errno));
- close(sock);
- XFailure;
- }
+ // disable this, send to monitor only once for every period
+ // here it updates monitor for every packet received
+ /* // send to monitor */
+ /* //lv(completeCfg); */
+ /* cleanCharP(monitorData) = toStringG(completeCfg); */
+ /* if (sendto(sock, monitorData, lenG(monitorData)+1, 0, (const struct sockaddr *)&server, sizeof(server)) < 0) { */
+ /* logE("monitor send failed: %s", strerror(errno)); */
+ /* close(sock); */
+ /* XFailure; */
+ /* } */
}
}
else {