diff options
-rw-r--r-- | Makefile | 12 | ||||
-rw-r--r-- | common.h | 13 | ||||
-rw-r--r-- | crypto.h | 89 | ||||
-rw-r--r-- | hbbpc.c | 21 | ||||
-rw-r--r-- | hbbpd.c | 33 |
5 files changed, 154 insertions, 14 deletions
@@ -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 @@ -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; +} @@ -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; @@ -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"); |