NEW: experimental support for encrypted payloads

This commit is contained in:
Jan-Piet Mens
2016-01-22 22:42:30 +01:00
parent 321880a5f4
commit c08c18a036
7 changed files with 163 additions and 0 deletions
+5
View File
@@ -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
+18
View File
@@ -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.
+7
View File
@@ -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`
+3
View File
@@ -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
View File
@@ -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();
+3
View File
@@ -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);
+3
View File
@@ -35,6 +35,9 @@ struct udata {
# ifdef WITH_LMDB
struct gcache *luadb; /* lmdb named database 'luadb' */
# endif
# ifdef WITH_ENCRYPT
struct gcache *keydb; /* encryption keys */
# endif
#endif
char *label; /* Server label */
char *geokey; /* Google reverse-geo API key */