summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Huwald <jh@sotun.de>2012-05-21 07:11:41 (GMT)
committerJan Huwald <jh@sotun.de>2012-05-21 07:11:41 (GMT)
commit22670f19bd1f015bf3a0588f7d4d56b3a6415d3e (patch)
tree823fdf40d7bceb33a8eff8dd83d271521e44d8fb
parent608a3598f6eb5be4cb2305a4eb0b1b721d0617a6 (diff)
add optional assymetric payload encryption
This patch uses the NaCL library to en-/decrypt and authenticate the payload suitable private and public keys are found. It - adds crypto.h, containing all crypto logic (including the detection if the packet is to be en-/decrypted) - adds nacl dependency to Makefile (including a switch between internal and external nacl) It also - changes Makefile to build with -Werror -Wextra - builds warning-free
-rw-r--r--Makefile12
-rw-r--r--common.h13
-rw-r--r--crypto.h89
-rw-r--r--hbbpc.c21
-rw-r--r--hbbpd.c33
5 files changed, 154 insertions, 14 deletions
diff --git a/Makefile b/Makefile
index 1509435..87a65e8 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,13 @@
BIN=hbbpd hbbpc
+
+ifndef EXTERNAL_NACL
+CFLAGS += -Inacl/include/
+LDFLAGS += -Lnacl/lib/
+NACL_DEP = nacl/include
+endif
+
+CFLAGS +=-std=c99 -Wall -Wextra -fwhole-program -Os
+LDFLAGS +=-lnacl nacl/lib/randombytes.o
all: $(BIN)
@@ -6,6 +15,9 @@ all: $(BIN)
clean:
rm -f $(BIN) *~
+%: %.c common.h crypto.h $(NACL_DEP)
+ $(CC) -o $@ $< $(LDFLAGS) $(CFLAGS)
+
nacl/include:
mkdir nacl
wget http://hyperelliptic.org/nacl/nacl-20110221.tar.bz2 -O - | tar jxf - -C nacl
diff --git a/common.h b/common.h
index 5ce3c61..1a1bd5e 100644
--- a/common.h
+++ b/common.h
@@ -9,6 +9,10 @@
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
+#include <net/if.h>
+#include <unistd.h>
+
+typedef unsigned char byte;
/* the port users will be connecting to */
#define SERVERPORT 4950
@@ -25,4 +29,13 @@
exit(1); \
}
+#define E0P(Cmd, Msg) \
+ if ((Cmd) == NULL) { \
+ perror(Msg); \
+ exit(1); \
+ }
+
+#define IGN(Cmd) \
+ if (Cmd) {}
+
#endif // UDP_BROADCAST_COMMON
diff --git a/crypto.h b/crypto.h
new file mode 100644
index 0000000..729bbf5
--- /dev/null
+++ b/crypto.h
@@ -0,0 +1,89 @@
+#pragma once
+
+/* WARNING: none of these functions is reentrant because all use the
+ same scratch buffer */
+
+#include <fcntl.h>
+#include <strings.h>
+
+#include "nacl/crypto_box.h"
+
+#include "common.h"
+
+/* scratchpad for crypto operations */
+byte crypto_buf[MAXBUFLEN];
+
+char *key_path(char *task, char *type) {
+ char *base = getenv("HBBP_KEYDIR"),
+ *res = (char*) crypto_buf;
+ if (!base) base = "/etc/hbbp/keys";
+ if ((unsigned) snprintf(res, MAXBUFLEN, "%s/%s/%s", base, task, type)
+ > MAXBUFLEN)
+ return NULL;
+ return res;
+}
+
+int key_load(char *task, char *type, byte *key, int key_len) {
+ int fd = open(key_path(task, type), O_RDONLY);
+ if (fd == -1) /* no specific key? -> try default */
+ fd = open(key_path("default", type), O_RDONLY);
+ if (fd == -1) return 0;
+ if (read(fd, key, key_len) != key_len) {
+ fprintf(stderr, "invalid key %s\n", crypto_buf);
+ return 0;
+ }
+ ENP(close(fd), "close");
+ return 1;
+}
+
+/* return error msg or null on sucess */
+char* encipher(char *task, byte *data, int *len) {
+ byte key_priv[crypto_box_SECRETKEYBYTES],
+ key_pub [crypto_box_PUBLICKEYBYTES];
+ switch(key_load(task, "send.priv", key_priv, crypto_box_SECRETKEYBYTES)
+ + key_load(task, "recv.pub", key_pub, crypto_box_PUBLICKEYBYTES)) {
+ case 0: return NULL; /* no keys -> no encryption required */
+ case 1: return "missing key";
+ }
+
+ /* copy data to scratch buffer */
+ if ((*len + crypto_box_ZEROBYTES > MAXBUFLEN)
+ || (*len + crypto_box_ZEROBYTES + crypto_box_NONCEBYTES + strlen(task) + 1 > MAXBUFLEN))
+ return "payload to large (no space left for crypto)";
+ bzero (crypto_buf, crypto_box_ZEROBYTES);
+ memcpy(crypto_buf + crypto_box_ZEROBYTES, data, *len);
+
+ /* encrypt */
+ byte *nonce = data,
+ *cdata = data + crypto_box_NONCEBYTES;
+ memset(nonce, 42, crypto_box_NONCEBYTES); /* URGENT TODO: random nonce */
+ if (crypto_box(cdata, crypto_buf, *len + crypto_box_ZEROBYTES, nonce, key_pub, key_priv) != 0) return "oooh";
+ memmove(data + crypto_box_NONCEBYTES,
+ data + crypto_box_NONCEBYTES + crypto_box_BOXZEROBYTES,
+ *len + crypto_box_ZEROBYTES - crypto_box_BOXZEROBYTES);
+ *len += crypto_box_NONCEBYTES + crypto_box_ZEROBYTES - crypto_box_BOXZEROBYTES;
+ return NULL;
+}
+
+int decipher(char *task, byte **data, int *len) {
+ byte key_priv[crypto_box_SECRETKEYBYTES],
+ key_pub [crypto_box_PUBLICKEYBYTES];
+ switch(key_load(task, "recv.priv", key_priv, crypto_box_SECRETKEYBYTES)
+ + key_load(task, "send.pub", key_pub, crypto_box_PUBLICKEYBYTES)) {
+ case 0: return 1; /* no keys -> no decryption required */
+ case 1: return 0; /* one missing key -> error */
+ }
+
+ byte nonce[crypto_box_NONCEBYTES];
+ memcpy(nonce, *data, crypto_box_NONCEBYTES);
+ byte *cdata = *data + (crypto_box_NONCEBYTES - crypto_box_BOXZEROBYTES);
+ int cdata_len = *len - (crypto_box_NONCEBYTES - crypto_box_BOXZEROBYTES);
+ if (cdata_len < crypto_box_BOXZEROBYTES) return 0;
+ bzero(cdata, crypto_box_BOXZEROBYTES);
+ if (crypto_box_open(crypto_buf, cdata, cdata_len, nonce,
+ key_pub, key_priv) == -1) return 0;
+
+ *data = crypto_buf + crypto_box_ZEROBYTES;
+ *len = cdata_len - crypto_box_ZEROBYTES;
+ return 1;
+}
diff --git a/hbbpc.c b/hbbpc.c
index 66d6b58..e4687af 100644
--- a/hbbpc.c
+++ b/hbbpc.c
@@ -4,12 +4,12 @@
*/
#include "common.h"
+#include "crypto.h"
int main(int argc, char **argv)
{
int fd;
struct sockaddr_in6 addr;
- int broadcast = 1;
/* assemble packet, parse cmd line */
if (argc < 3 || argc > 4) {
@@ -20,13 +20,14 @@ int main(int argc, char **argv)
*task = argv[2],
*message = (argc == 4) ? argv[3] : "";
int task_len = strlen(task),
- total_len = task_len + 1;
+ msg_len = 0, total_len;
strcpy(buf, task);
buf[task_len] = 0;
+ /* TODO: recheck paragraph for off-by-one errors */
if (strcmp(message, "-") == 0) {
/* read payload from stdin */
int i;
- while ((MAXBUFLEN - total_len - 1 > 0)
+ while (((total_len = task_len + msg_len + 1) < MAXBUFLEN - 1)
&& ((i = read(0, &(buf[total_len]), MAXBUFLEN - total_len - 1)) > 0))
total_len += i;
ENP(i, "read(stdin)");
@@ -36,16 +37,24 @@ int main(int argc, char **argv)
}
}else{
/* use cmd line for payload */
- total_len += strlen(message);
- if (total_len <= MAXBUFLEN)
+ msg_len = strlen(message);
+ if ((total_len = task_len + msg_len + 1) < MAXBUFLEN - 1)
strcpy(buf + 1 + task_len, message);
}
- if (total_len > MAXBUFLEN) {
+ if ((total_len = task_len + msg_len + 1) >= MAXBUFLEN - 1) {
fprintf(stderr,"payload to long: max %d bytes, was %d\n",
MAXBUFLEN - 1, total_len);
exit(1);
}
+ /* (potentially) encrypt payload */
+ char *err_msg = encipher(task, (byte*) buf + task_len + 1, &msg_len);
+ if (err_msg != NULL) {
+ fprintf(stderr, "%s\n", err_msg);
+ exit(1);
+ }
+ total_len = task_len + msg_len + 1;
+
/* setup socket */
ENP((fd = socket(AF_INET6, SOCK_DGRAM, 0)), "socket");
addr.sin6_family = AF_INET6;
diff --git a/hbbpd.c b/hbbpd.c
index e24cf55..f47cd28 100644
--- a/hbbpd.c
+++ b/hbbpd.c
@@ -2,19 +2,23 @@
** listener.c -- a datagram sockets "server" demo
*/
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
#include "common.h"
+#include "crypto.h"
int main(int argc, char **argv, char **envp) {
int fd;
int numbytes;
struct sockaddr_storage their_addr;
- char buf[MAXBUFLEN];
+ byte buf[MAXBUFLEN];
socklen_t addr_len;
/* setup socket, parse cmd line */
{
struct sockaddr_in6 ba;
- struct ipv6_mreq ga;
if (argc > 2) {
fprintf(stderr, "usage: %s [interface]\n", argv[0]);
exit(1);
@@ -55,15 +59,28 @@ int main(int argc, char **argv, char **envp) {
addr_len = sizeof their_addr;
while ((numbytes = recvfrom(fd, buf, MAXBUFLEN-1 , 0,
(struct sockaddr *) &their_addr, &addr_len)) != -1) {
- /* decode packet & launch handler */
+ /* decode packet */
buf[numbytes] = '\0';
- char *task = buf,
- *cl_argv[2] = {task, NULL};
+ char *task = (char*) buf;
+ int task_len = strlen(task);
+ byte *payload = buf + task_len + 1;
+ int payload_len = numbytes - task_len - 1;
+
if (task[0] == '/' || strstr(task, "..")) {
fprintf(stderr, "payload tried directory traversal\n");
continue;
}
+ /* TODO: check if the task exists and is executable; save cpu
+ cycles trying to decode messages not intended for us */
+
+ /* decipher packet */
+ if (!decipher(task, &payload, &payload_len)) {
+ fprintf(stderr, "unable to decrypt payload\n");
+ continue;
+ }
+
+ /* launch handler */
int fd_payload[2];
ENP(pipe(fd_payload), "pipe");
@@ -76,6 +93,7 @@ int main(int argc, char **argv, char **envp) {
ENP(close(fd), "close");
/* run our task task */
+ char *cl_argv[2] = {task, NULL};
execve(task, cl_argv, envp);
perror("exec");
exit(1);
@@ -86,9 +104,8 @@ int main(int argc, char **argv, char **envp) {
/* write payload to child process; the return value of write is
intentionally ignored */
- int tasklen = strlen(task);
- if (tasklen < numbytes)
- write(fd_payload[1], buf + tasklen + 1, numbytes - tasklen - 1);
+ if (task_len < numbytes)
+ IGN(write(fd_payload[1], payload, payload_len))
ENP(close(fd_payload[1]), "close");
ENP(close(fd_payload[0]), "close");
ENP(wait(NULL), "wait");
contact: Jan Huwald // Impressum