commit 06f762a2c6bed9bd942a92daa2cad841c0f5f93f
parent 7f965011f8e27aaafb8617b0d58b90173121da60
Author: sin <sin@2f30.org>
Date: Mon, 21 Mar 2016 11:08:59 +0000
add primitive challenge-response auth
server sends an encrypted random integer N and client decrypts it,
adds one and sends the encrypted result back.
Diffstat:
M | stun.c | | | 70 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ |
1 file changed, 64 insertions(+), 6 deletions(-)
diff --git a/stun.c b/stun.c
@@ -135,18 +135,33 @@ readtun(int fd, unsigned char *buf, int len)
}
void
-pack16(unsigned char *buf, int n)
+pack16(unsigned char *buf, uint16_t n)
{
buf[0] = n >> 8 & 0xff;
buf[1] = n & 0xff;
}
-int
+uint16_t
unpack16(unsigned char *buf)
{
return (buf[0] << 8) | buf[1];
}
+void
+pack32(unsigned char *buf, uint32_t n)
+{
+ buf[0] = n >> 24 & 0xff;
+ buf[1] = n >> 16 & 0xff;
+ buf[2] = n >> 8 & 0xff;
+ buf[3] = n & 0xff;
+}
+
+uint32_t
+unpack32(unsigned char *buf)
+{
+ return buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
+}
+
/* handles partial writes */
int
writeall(int fd, void *buf, int len)
@@ -209,6 +224,7 @@ int
readnet(int fd, unsigned char *buf, int len)
{
unsigned char decbuf[MTU + AES_BLOCK_SIZE + HDRLEN];
+ unsigned char encbuf[MTU + AES_BLOCK_SIZE + HDRLEN];
unsigned char hdr[HDRLEN];
int n, pktlen;
@@ -220,15 +236,44 @@ readnet(int fd, unsigned char *buf, int len)
warnx("bogus payload length: %d", pktlen);
return -1;
}
- n = readall(fd, buf, pktlen);
+ n = readall(fd, encbuf, pktlen);
if (n <= 0)
return n;
- pktlen = aesdec(&dec, decbuf, buf, pktlen);
+ pktlen = aesdec(&dec, decbuf, encbuf, pktlen);
memcpy(buf, decbuf, pktlen);
return pktlen;
}
int
+challenge(int netfd)
+{
+ unsigned char buf[4];
+ uint32_t n, reply;
+
+ pack32(buf, n = arc4random());
+ if (writenet(netfd, buf, sizeof(buf)) <= 0 ||
+ readnet(netfd, buf, sizeof(buf)) <= 0)
+ return -1;
+ reply = unpack32(buf);
+ return n + 1 == reply;
+}
+
+int
+response(int netfd)
+{
+ unsigned char buf[4];
+ uint32_t reply;
+
+ if (readnet(netfd, buf, sizeof(buf)) <= 0)
+ return -1;
+ reply = unpack32(buf);
+ pack32(buf, reply + 1);
+ if (writenet(netfd, buf, sizeof(buf)) <= 0)
+ return -1;
+ return 0;
+}
+
+int
loop(int netfd, int tunfd)
{
unsigned char buf[MTU + AES_BLOCK_SIZE + HDRLEN];
@@ -266,7 +311,9 @@ serversetup(int tunfd)
listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (listenfd < 0)
err(1, "socket");
+
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (int []){1}, sizeof(int));
+
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_addr.s_addr = htonl(INADDR_ANY);
@@ -274,17 +321,24 @@ serversetup(int tunfd)
ret = bind(listenfd, (struct sockaddr *)&local, sizeof(local));
if (ret < 0)
err(1, "bind");
+
ret = listen(listenfd, 5);
if (ret < 0)
err(1, "listen");
+
for (;;) {
netfd = accept(listenfd, (struct sockaddr *)&remote,
(socklen_t []){sizeof(remote)});
- if (ret < 0)
+ if (netfd < 0)
err(1, "accept");
+ setsockopt(netfd, IPPROTO_TCP, TCP_NODELAY, (int []){1}, sizeof(int));
if (debug)
printf("client connected\n");
- setsockopt(netfd, IPPROTO_TCP, TCP_NODELAY, (int []){1}, sizeof(int));
+ ret = challenge(netfd);
+ if (ret < 0) {
+ close(netfd);
+ continue;
+ }
loop(netfd, tunfd);
close(netfd);
}
@@ -298,6 +352,7 @@ clientsetup(int tunfd)
netfd = socket(AF_INET, SOCK_STREAM, 0);
if (netfd < 0)
err(1, "socket");
+
memset(&remote, 0, sizeof(remote));
remote.sin_family = AF_INET;
remote.sin_addr.s_addr = inet_addr(host);
@@ -306,6 +361,9 @@ clientsetup(int tunfd)
if (ret < 0)
err(1, "connect");
setsockopt(netfd, IPPROTO_TCP, TCP_NODELAY, (int []){1}, sizeof(int));
+ ret = response(netfd);
+ if (ret < 0)
+ errx(1, "failed to respond to challenge");
return loop(netfd, tunfd);
}