mirror of
https://github.com/owntracks/recorder.git
synced 2026-08-27 16:57:21 +00:00
NEW: experimental support for encrypted payloads
This commit is contained in:
@@ -27,6 +27,11 @@ ifeq ($(WITH_LUA),yes)
|
||||
OTR_OBJS += hooks.o
|
||||
endif
|
||||
|
||||
ifeq ($(WITH_ENCRYPT),yes)
|
||||
CFLAGS += -DWITH_ENCRYPT=1 $(SODIUM_CFLAGS)
|
||||
LIBS += $(SODIUM_LIBS)
|
||||
endif
|
||||
|
||||
ifeq ($(WITH_KILL),yes)
|
||||
CFLAGS += -DWITH_KILL=1
|
||||
endif
|
||||
|
||||
@@ -721,6 +721,24 @@ ocat --dump |
|
||||
|
||||
This named lmdb database is keyed on topic name (`owntracks/jane/phone`). If the topic of an incoming message is found in the database, the `tid` member in the JSON payload is replaced by the string value of this key.
|
||||
|
||||
#### `keys`
|
||||
|
||||
If the _recorder_ was built with encryption support (see below), this named database contains the secret decryption keys for users/device pairs. The LMDB key is the username followed by a dash followed by the device name, all lower case. For example, if user Jjolie with device iPhone needs a secret entered, the database key will be `jjolie-iphone`. This can be entered into the database as follows:
|
||||
|
||||
```bash
|
||||
echo "jjolie-iphone s3cr1t" | ocat --load=keys
|
||||
```
|
||||
|
||||
Beware: these secret keys are stored in plain text so the database must be protected!
|
||||
|
||||
## Encryption (*experimental!*)
|
||||
|
||||
If compiled with `WITH_ENCRYPT` support, the recorder will handle messages from OwnTracks devices which support payload encryption. Each user / device requires a secret key which is configured on the device and which must be configured on the Recorder host in order for the Recorder to be able to decrypt the payloads.
|
||||
|
||||
Upon successful decryption, the Recorder processes the original (device-transmitted) JSON and stores the result in plain (i.e. un-encrypted) form in the store.
|
||||
|
||||
|
||||
|
||||
## Prerequisites for building
|
||||
|
||||
You need a current version of the Mosquitto library (and you probably require the Mosquitto broker as well for OwnTracks). We strongly recommend installing Mosquitto either from [source](http://mosquitto.org/download/) or from a [binary package](http://mosquitto.org/download/), both of which are provided by the [Mosquitto project](http://mosquitto.org/). In particular, older or LTS OS versions profit from this.
|
||||
|
||||
@@ -17,6 +17,9 @@ WITH_PING ?= yes
|
||||
# Do you want support for removing data via the API? (Dangerous)
|
||||
WITH_KILL ?= no
|
||||
|
||||
# Do you want support for payload encryption with libsodium?
|
||||
WITH_ENCRYPT ?= no
|
||||
|
||||
# Do you want R_only support? (Probably not; this is for Hosted)
|
||||
# If you set this to `yes', WITH_LMDB will be set to yes
|
||||
WITH_RONLY ?= no
|
||||
@@ -24,6 +27,7 @@ WITH_RONLY ?= no
|
||||
# Do you require support for OwnTracks Greenwich firmware?
|
||||
WITH_GREENWICH ?= no
|
||||
|
||||
|
||||
# Where should the recorder store its data? This directory must
|
||||
# exist and be writeable by recorder (and readable by ocat)
|
||||
STORAGEDEFAULT = /var/spool/owntracks/recorder/store
|
||||
@@ -63,3 +67,6 @@ MORELIBS = # -lssl
|
||||
|
||||
LUA_CFLAGS = `pkg-config --cflags lua`
|
||||
LUA_LIBS = `pkg-config --libs lua`
|
||||
|
||||
SODIUM_CFLAGS = `pkg-config --cflags libsodium`
|
||||
SODIUM_LIBS = `pkg-config --libs libsodium`
|
||||
|
||||
@@ -97,6 +97,9 @@ void print_versioninfo()
|
||||
#ifdef WITH_HTTP
|
||||
printf("\tWITH_HTTP = yes\n");
|
||||
#endif
|
||||
#ifdef WITH_ENCRYPT
|
||||
printf("\tWITH_ENCRYPT = yes\n");
|
||||
#endif
|
||||
#ifdef WITH_PING
|
||||
printf("\tWITH_PING = yes\n");
|
||||
#endif
|
||||
|
||||
+124
@@ -50,6 +50,9 @@
|
||||
#ifdef WITH_LUA
|
||||
# include "hooks.h"
|
||||
#endif
|
||||
#if WITH_ENCRYPT
|
||||
# include <sodium.h>
|
||||
#endif
|
||||
|
||||
|
||||
#define SSL_VERIFY_PEER (1)
|
||||
@@ -549,6 +552,81 @@ void store_gwvalue(char *username, char *device, time_t tst, char *key, char *va
|
||||
}
|
||||
#endif /* GREENWICH */
|
||||
|
||||
#if WITH_ENCRYPT
|
||||
/*
|
||||
* Create a new mosquitto message structure, decrypt and populate new.
|
||||
* p64 contains the base64-encoded payload from the device. `username'
|
||||
* and `device' are needed to obtain the decryption key for this object.
|
||||
*/
|
||||
|
||||
struct mosquitto_message *decrypt(struct udata *ud, const struct mosquitto_message *m, char *p64, char *username, char *device)
|
||||
{
|
||||
struct mosquitto_message *msg;
|
||||
unsigned char key[crypto_secretbox_KEYBYTES];
|
||||
unsigned char *ciphertext, *cleartext;
|
||||
unsigned long ciphertext_len;
|
||||
int n, klen;
|
||||
UT_string *userdev;
|
||||
|
||||
|
||||
utstring_new(userdev);
|
||||
utstring_printf(userdev, "%s-%s", username, device);
|
||||
|
||||
memset(key, 0, sizeof(key));
|
||||
klen = gcache_get(ud->keydb, (char *)UB(userdev), (char *)key, sizeof(key));
|
||||
if (klen < 1) {
|
||||
olog(LOG_ERR, "no decryption key for %s in %s", UB(userdev), m->topic);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if ((msg = malloc(sizeof(struct mosquitto_message))) == NULL) {
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
n = strlen(p64); /* This is more than enough */
|
||||
|
||||
msg->mid = m->mid;
|
||||
msg->topic = m->topic;
|
||||
msg->qos = m->qos;
|
||||
msg->retain = m->retain;
|
||||
|
||||
if ((ciphertext = malloc(n)) == NULL) {
|
||||
free(msg);
|
||||
return (NULL);
|
||||
}
|
||||
ciphertext_len = base64_decode(p64, ciphertext);
|
||||
|
||||
fprintf(stderr, "START DECRYPT. clen==%lu\n", ciphertext_len);
|
||||
|
||||
if ((cleartext = calloc(n, sizeof(unsigned char))) == NULL) {
|
||||
free(ciphertext);
|
||||
free(msg);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (crypto_secretbox_open_easy(cleartext, // message
|
||||
ciphertext + crypto_secretbox_NONCEBYTES, // authtag + encrypted
|
||||
ciphertext_len - crypto_secretbox_NONCEBYTES, // len (auth+encr)
|
||||
ciphertext, // nonce
|
||||
key) != 0)
|
||||
{
|
||||
olog(LOG_ERR, "payload of %s cannot be decrypted; forged?", m->topic);
|
||||
free(ciphertext);
|
||||
free(cleartext);
|
||||
free(msg);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
printf("DECRYPTED: %s\n", (char *)cleartext);
|
||||
free(ciphertext);
|
||||
|
||||
msg->payload = (void *)cleartext;
|
||||
msg->payloadlen = strlen((char *)cleartext);
|
||||
|
||||
return (msg);
|
||||
}
|
||||
#endif /* ENCRYPT */
|
||||
|
||||
void on_message(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *m)
|
||||
{
|
||||
JsonNode *json, *j, *geo = NULL;
|
||||
@@ -565,6 +643,9 @@ void on_message(struct mosquitto *mosq, void *userdata, const struct mosquitto_m
|
||||
int pingping = FALSE, skipslash = 0;
|
||||
int r_ok = TRUE; /* True if recording enabled for a publish */
|
||||
payload_type _type;
|
||||
#ifdef WITH_ENCRYPT
|
||||
struct mosquitto_message *new_m;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* mosquitto_message->
|
||||
@@ -759,6 +840,9 @@ void on_message(struct mosquitto *mosq, void *userdata, const struct mosquitto_m
|
||||
else if (!strcmp(j->string_, "waypoint")) _type = T_WAYPOINT;
|
||||
else if (!strcmp(j->string_, "waypoints")) _type = T_WAYPOINTS;
|
||||
else if (!strcmp(j->string_, "dump")) _type = T_CONFIG;
|
||||
#if WITH_ENCRYPT
|
||||
else if (!strcmp(j->string_, "encrypted")) _type = T_ENCRYPTED;
|
||||
#endif /* WITH_ENCRYPT */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -799,6 +883,30 @@ void on_message(struct mosquitto *mosq, void *userdata, const struct mosquitto_m
|
||||
case T_TRANSITION:
|
||||
case T_LOCATION:
|
||||
break;
|
||||
#if WITH_ENCRYPT
|
||||
case T_ENCRYPTED:
|
||||
/*
|
||||
* Obtain the `data' element from JSON, and try and decrypt
|
||||
* that. If successful, we get a new mosquitto_message with
|
||||
* the decrypted message as payload, and invoke this function
|
||||
* again to do the heavy lifting.
|
||||
*/
|
||||
|
||||
if ((j = json_find_member(json, "data")) != NULL) {
|
||||
if (j->tag == JSON_STRING) {
|
||||
new_m = decrypt(ud, m, j->string_, UB(username), UB(device));
|
||||
if (new_m != NULL) {
|
||||
on_message(mosq, userdata, new_m);
|
||||
free(new_m->payload);
|
||||
free(new_m);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
olog(LOG_ERR, "no `data' in encrypted %s", m->topic);
|
||||
return;
|
||||
break;
|
||||
#endif /* WITH_ENCRYPT */
|
||||
default:
|
||||
if (r_ok) {
|
||||
putrec(ud, now, reltopic, username, device, bindump(m->payload, m->payloadlen));
|
||||
@@ -1399,6 +1507,13 @@ int main(int argc, char **argv)
|
||||
}
|
||||
gcache_close(gt);
|
||||
#endif /* !RONLY */
|
||||
#ifdef WITH_ENCRYPT
|
||||
if ((gt = gcache_open(path, "keys", FALSE)) == NULL) {
|
||||
fprintf(stderr, "Cannot lmdb-open `keys'\n");
|
||||
exit(2);
|
||||
}
|
||||
gcache_close(gt);
|
||||
#endif /* !ENCRYPT */
|
||||
#endif
|
||||
exit(0);
|
||||
}
|
||||
@@ -1488,6 +1603,9 @@ int main(int argc, char **argv)
|
||||
# ifdef WITH_RONLY
|
||||
ud->ronlydb = gcache_open(err, "ronlydb", FALSE);
|
||||
# endif
|
||||
# ifdef WITH_ENCRYPT
|
||||
ud->keydb = gcache_open(err, "keys", TRUE);
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if WITH_LUA && WITH_LMDB
|
||||
@@ -1503,6 +1621,12 @@ int main(int argc, char **argv)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if WITH_ENCRYPT
|
||||
if (sodium_init() == -1) {
|
||||
olog(LOG_ERR, "cannot initialize libsodium");
|
||||
}
|
||||
#endif
|
||||
|
||||
mosquitto_lib_init();
|
||||
|
||||
|
||||
|
||||
@@ -33,6 +33,9 @@ typedef enum {
|
||||
T_TRANSITION,
|
||||
T_WAYPOINT,
|
||||
T_WAYPOINTS,
|
||||
#if WITH_ENCRYPT
|
||||
T_ENCRYPTED,
|
||||
#endif
|
||||
} payload_type;
|
||||
|
||||
JsonNode *lister(char *username, char *device, time_t s_lo, time_t s_hi, int reverse);
|
||||
|
||||
Reference in New Issue
Block a user