Commit e72f49b1 authored by Juliusz Chroboczek's avatar Juliusz Chroboczek

Implement HMAC authentication.

Known issues:

  - we create a neighbour entry before the first successful challenge;
  - we compute HMAC for each HMAC TLV rather than just once;
  - we only support sending one HMAC TLV;
  - we don't support key rotation.
Co-authored-by: default avatarClara Do <clarado_perso@yahoo.fr>
Co-authored-by: default avatarWeronika Kolodziejak <weronika.kolodziejak@gmail.com>
parent 3bc154c8
...@@ -11,11 +11,13 @@ LDLIBS = -lrt ...@@ -11,11 +11,13 @@ LDLIBS = -lrt
SRCS = babeld.c net.c kernel.c util.c interface.c source.c neighbour.c \ SRCS = babeld.c net.c kernel.c util.c interface.c source.c neighbour.c \
route.c xroute.c message.c resend.c configuration.c local.c \ route.c xroute.c message.c resend.c configuration.c local.c \
disambiguation.c rule.c rfc6234/sha224-256.c BLAKE2/ref/blake2s-ref.c disambiguation.c rule.c hmac.c \
rfc6234/sha224-256.c BLAKE2/ref/blake2s-ref.c
OBJS = babeld.o net.o kernel.o util.o interface.o source.o neighbour.o \ OBJS = babeld.o net.o kernel.o util.o interface.o source.o neighbour.o \
route.o xroute.o message.o resend.o configuration.o local.o \ route.o xroute.o message.o resend.o configuration.o local.o \
disambiguation.o rule.o rfc6234/sha224-256.o BLAKE2/ref/blake2s-ref.o disambiguation.o rule.o hmac.o \
rfc6234/sha224-256.o BLAKE2/ref/blake2s-ref.o
babeld: $(OBJS) babeld: $(OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) -o babeld $(OBJS) $(LDLIBS) $(CC) $(CFLAGS) $(LDFLAGS) -o babeld $(OBJS) $(LDLIBS)
......
...@@ -661,9 +661,10 @@ main(int argc, char **argv) ...@@ -661,9 +661,10 @@ main(int argc, char **argv)
} }
if(FD_ISSET(protocol_socket, &readfds)) { if(FD_ISSET(protocol_socket, &readfds)) {
unsigned char to[16];
rc = babel_recv(protocol_socket, rc = babel_recv(protocol_socket,
receive_buffer, receive_buffer_size, receive_buffer, receive_buffer_size,
(struct sockaddr*)&sin6, sizeof(sin6)); (struct sockaddr*)&sin6, sizeof(sin6), to);
if(rc < 0) { if(rc < 0) {
if(errno != EAGAIN && errno != EINTR) { if(errno != EAGAIN && errno != EINTR) {
perror("recv"); perror("recv");
...@@ -675,7 +676,7 @@ main(int argc, char **argv) ...@@ -675,7 +676,7 @@ main(int argc, char **argv)
continue; continue;
if(ifp->ifindex == sin6.sin6_scope_id) { if(ifp->ifindex == sin6.sin6_scope_id) {
parse_packet((unsigned char*)&sin6.sin6_addr, ifp, parse_packet((unsigned char*)&sin6.sin6_addr, ifp,
receive_buffer, rc); receive_buffer, rc, to);
VALGRIND_MAKE_MEM_UNDEFINED(receive_buffer, VALGRIND_MAKE_MEM_UNDEFINED(receive_buffer,
receive_buffer_size); receive_buffer_size);
break; break;
......
...@@ -39,6 +39,7 @@ THE SOFTWARE. ...@@ -39,6 +39,7 @@ THE SOFTWARE.
#include "interface.h" #include "interface.h"
#include "route.h" #include "route.h"
#include "kernel.h" #include "kernel.h"
#include "hmac.h"
#include "configuration.h" #include "configuration.h"
#include "rule.h" #include "rule.h"
...@@ -322,6 +323,35 @@ get_interface_type(int c, int *type_r, gnc_t gnc, void *closure) ...@@ -322,6 +323,35 @@ get_interface_type(int c, int *type_r, gnc_t gnc, void *closure)
return c; return c;
} }
static int
gethex(int c, unsigned char **value_r, int *len_r, gnc_t gnc, void *closure)
{
char *t;
unsigned char *value;
int len, rc;
c = getword(c, &t, gnc, closure);
if(c < -1)
return c;
len = strlen(t);
if(len % 2 != 0) {
free(t);
return -2;
}
value = malloc(len / 2);
if(value == NULL)
return -2;
rc = fromhex(value, t, len);
free(t);
if(rc < 0) {
free(value);
return -2;
}
*value_r = value;
*len_r = len / 2;
return c;
}
static void static void
free_filter(struct filter *f) free_filter(struct filter *f)
{ {
...@@ -647,6 +677,17 @@ parse_anonymous_ifconf(int c, gnc_t gnc, void *closure, ...@@ -647,6 +677,17 @@ parse_anonymous_ifconf(int c, gnc_t gnc, void *closure,
if(c < -1 || penalty <= 0 || penalty > 0xFFFF) if(c < -1 || penalty <= 0 || penalty > 0xFFFF)
goto error; goto error;
if_conf->max_rtt_penalty = penalty; if_conf->max_rtt_penalty = penalty;
} else if(strcmp(token, "hmac") == 0) {
char *key_id;
struct key *key;
c = getword(c, &key_id, gnc, closure);
if(c < -1) {
free(key_id);
goto error;
}
key = find_key(key_id);
if_conf->key = key;
free(key_id);
} else { } else {
goto error; goto error;
} }
...@@ -691,6 +732,65 @@ parse_ifconf(int c, gnc_t gnc, void *closure, ...@@ -691,6 +732,65 @@ parse_ifconf(int c, gnc_t gnc, void *closure,
return -2; return -2;
} }
static int
parse_key(int c, gnc_t gnc, void *closure, struct key **key_return)
{
char *token = NULL;
struct key *key;
key = calloc(1, sizeof(struct key));
if(key == NULL)
goto error;
while(1) {
c = skip_whitespace(c, gnc, closure);
if(c < 0 || c == '\n' || c == '#') {
c = skip_to_eol(c, gnc, closure);
break;
}
c = getword(c, &token, gnc, closure);
if(c < -1) {
goto error;
}
if(strcmp(token, "id") == 0) {
c = getword(c, &key->id, gnc, closure);
if(c < -1 || key->id == NULL) {
goto error;
}
} else if(strcmp(token, "type") == 0) {
char *auth_type;
c = getword(c, &auth_type, gnc, closure);
if(c < -1 || auth_type == NULL)
goto error;
if(strcmp(auth_type, "none") == 0) {
key->type = AUTH_TYPE_NONE;
} else if(strcmp(auth_type, "sha256") == 0) {
key->type = AUTH_TYPE_SHA256;
} else if(strcmp(auth_type, "blake2s") == 0) {
key->type = AUTH_TYPE_BLAKE2S;
} else {
key->type = 0;
free(auth_type);
goto error;
}
free(auth_type);
} else if(strcmp(token, "value") == 0) {
c = gethex(c, &key->value, &key->len, gnc, closure);
if(c < -1 || key->value == NULL)
goto error;
} else {
goto error;
}
free(token);
}
*key_return = key;
return c;
error:
free(token);
free(key);
return -2;
}
static void static void
add_filter(struct filter *filter, struct filter **filters) add_filter(struct filter *filter, struct filter **filters)
{ {
...@@ -736,6 +836,7 @@ merge_ifconf(struct interface_conf *dest, ...@@ -736,6 +836,7 @@ merge_ifconf(struct interface_conf *dest,
MERGE(rtt_min); MERGE(rtt_min);
MERGE(rtt_max); MERGE(rtt_max);
MERGE(max_rtt_penalty); MERGE(max_rtt_penalty);
MERGE(key);
#undef MERGE #undef MERGE
} }
...@@ -1100,6 +1201,32 @@ parse_config_line(int c, gnc_t gnc, void *closure, ...@@ -1100,6 +1201,32 @@ parse_config_line(int c, gnc_t gnc, void *closure,
if(c < -1 || !action_return) if(c < -1 || !action_return)
goto fail; goto fail;
reopen_logfile(); reopen_logfile();
} else if(strcmp(token, "key") == 0) {
struct key *key = NULL;
c = parse_key(c, gnc, closure, &key);
if(c < -1)
goto fail;
if(key->id == NULL)
goto fail;
switch(key->type) {
case AUTH_TYPE_SHA256:
if(key->len != 32) {
free(key);
goto fail;
}
break;
case AUTH_TYPE_BLAKE2S:
if(key->len != 16) {
free(key);
goto fail;
}
break;
default:
free(key);
goto fail;
}
add_key(key->id, key->type, key->len, key->value);
free(key);
} else { } else {
c = parse_option(c, gnc, closure, token); c = parse_option(c, gnc, closure, token);
if(c < -1) if(c < -1)
......
...@@ -29,6 +29,10 @@ THE SOFTWARE. ...@@ -29,6 +29,10 @@ THE SOFTWARE.
#define CONFIG_ACTION_UNMONITOR 4 #define CONFIG_ACTION_UNMONITOR 4
#define CONFIG_ACTION_NO 5 #define CONFIG_ACTION_NO 5
#define AUTH_TYPE_NONE 0
#define AUTH_TYPE_SHA256 1
#define AUTH_TYPE_BLAKE2S 2
struct filter_result { struct filter_result {
unsigned int add_metric; /* allow = 0, deny = INF, metric = <0..INF> */ unsigned int add_metric; /* allow = 0, deny = INF, metric = <0..INF> */
unsigned char *src_prefix; unsigned char *src_prefix;
......
/*
Copyright (c) 2018 by Clara Dô and Weronika Kolodziejak
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <netinet/in.h>
#include "rfc6234/sha.h"
#include "BLAKE2/ref/blake2.h"
#include "babeld.h"
#include "interface.h"
#include "neighbour.h"
#include "util.h"
#include "hmac.h"
#include "configuration.h"
#include "message.h"
struct key **keys = NULL;
int numkeys = 0, maxkeys = 0;
struct key *
find_key(const char *id)
{
int i;
for(i = 0; i < numkeys; i++) {
if(strcmp(keys[i]->id, id) == 0)
return retain_key(keys[i]);
}
return NULL;
}
struct key *
retain_key(struct key *key)
{
assert(key->ref_count < 0xffff);
key->ref_count++;
return key;
}
void
release_key(struct key *key)
{
assert(key->ref_count > 0);
key->ref_count--;
}
struct key *
add_key(char *id, int type, int len, unsigned char *value)
{
struct key *key;
assert(value != NULL && type != 0);
key = find_key(id);
if(key) {
key->type = type;
key->len = len;
key->value = value;
return key;
}
if(type == AUTH_TYPE_NONE)
return NULL;
if(numkeys >= maxkeys) {
struct key **new_keys;
int n = maxkeys < 1 ? 8 : 2 * maxkeys;
new_keys = realloc(keys, n * sizeof(struct key*));
if(new_keys == NULL)
return NULL;
maxkeys = n;
keys = new_keys;
}
key = calloc(1, sizeof(struct key));
if(key == NULL)
return NULL;
key->id = id;
key->type = type;
key->len = len;
key->value = value;
keys[numkeys++] = key;
return key;
}
static int
compute_hmac(const unsigned char *src, const unsigned char *dst,
const unsigned char *packet_header,
const unsigned char *body, int bodylen, struct key *key,
unsigned char *hmac_return)
{
unsigned char port[2];
int rc;
DO_HTONS(port, (unsigned short)protocol_port);
switch(key->type) {
case 1: {
SHA256Context inner, outer;
unsigned char ipad[64], ihash[32], opad[64];
if(key->len != 32)
return -1;
for(int i = 0; i < 32; i++)
ipad[i] = key->value[i] ^ 0x36;
for(int i = 32; i < 64; i++)
ipad[i] = 0x36;
rc = SHA256Reset(&inner);
if(rc < 0)
return -1;
rc = SHA256Input(&inner, ipad, 64);
if(rc < 0)
return -1;
rc = SHA256Input(&inner, dst, 16);
if(rc != 0)
return -1;
rc = SHA256Input(&inner, port, 2);
if(rc != 0)
return -1;
rc = SHA256Input(&inner, src, 16);
if(rc != 0)
return -1;
rc = SHA256Input(&inner, port, 2);
if(rc != 0)
return -1;
rc = SHA256Input(&inner, packet_header, 4);
if(rc != 0)
return -1;
rc = SHA256Input(&inner, body, bodylen);
if(rc != 0)
return -1;
rc = SHA256Result(&inner, ihash);
if(rc != 0)
return -1;
for(int i = 0; i < 32; i++)
opad[i] = ihash[i] ^ 0x5c;
for(int i = 32; i < 64; i++)
opad[i] = 0x5c;
rc = SHA256Reset(&outer);
if(rc != 0)
return -1;
rc = SHA256Input(&outer, opad, 64);
if(rc != 0)
return -1;
rc = SHA256Input(&outer, ihash, 32);
if(rc != 0)
return -1;
rc = SHA256Result(&outer, hmac_return);
if(rc < 0)
return -1;
return 32;
}
case 2: {
blake2s_state s;
if(key->len != 16)
return -1;
rc = blake2s_init_key(&s, 16, key->value, key->len);
if(rc < 0)
return -1;
rc = blake2s_update(&s, dst, 16);
if(rc < 0)
return -1;
rc = blake2s_update(&s, port, 2);
if(rc < 0)
return -1;
rc = blake2s_update(&s, src, 16);
if(rc < 0)
return -1;
rc = blake2s_update(&s, port, 2);
if(rc < 0)
return -1;
rc = blake2s_update(&s, packet_header, 4);
if(rc < 0)
return -1;
rc = blake2s_update(&s, body, bodylen);
if(rc < 0)
return -1;
rc = blake2s_final(&s, hmac_return, 16);
if(rc < 0)
return -1;
return 16;
}
default:
return -1;
}
}
int
add_hmac(struct buffered *buf, struct interface *ifp,
unsigned char *packet_header)
{
int hmaclen;
int i = buf->len;
unsigned char *dst = buf->sin6.sin6_addr.s6_addr;
unsigned char *src;
if(ifp->numll < 1) {
fprintf(stderr, "add_hmac: no link-local address.\n");
return -1;
}
src = ifp->ll[0];
if(buf->len + 2 + DIGEST_LEN > buf->size) {
fprintf(stderr, "Buffer overflow in add_hmac.\n");
return -1;
}
hmaclen = compute_hmac(src, dst, packet_header,
buf->buf, buf->len, ifp->key,
buf->buf + i + 2);
if(hmaclen < 0)
return -1;
buf->buf[i++] = MESSAGE_HMAC;
buf->buf[i++] = hmaclen;
i += hmaclen;
return i;
}
static int
compare_hmac(const unsigned char *src, const unsigned char *dst,
const unsigned char *packet, int bodylen,
const unsigned char *hmac, int hmaclen)
{
unsigned char true_hmac[DIGEST_LEN];
int true_hmaclen;
int i;
for(i = 0; i < numkeys; i++) {
true_hmaclen = compute_hmac(src, dst, packet,
packet + 4, bodylen, keys[i],
true_hmac);
if(true_hmaclen != hmaclen) {
debugf("Bad hmac length (%d != %d).\n", true_hmaclen, hmaclen);
return -1;
}
if(memcmp(true_hmac, hmac, hmaclen) == 0)
return 1;
}
return 0;
}
int
check_hmac(const unsigned char *packet, int packetlen, int bodylen,
const unsigned char *src, const unsigned char *dst)
{
int i = bodylen + 4;
int len;
debugf("check_hmac %s -> %s\n",
format_address(src), format_address(dst));
while(i < packetlen) {
if(i + 1 > packetlen) {
fprintf(stderr, "Received truncated message.\n");
break;
}
len = packet[i+1];
if(packet[i] == MESSAGE_HMAC) {
if(i + len > packetlen) {
fprintf(stderr, "Received truncated message.\n");
return -1;
}
if(compare_hmac(src, dst, packet, bodylen,
packet + i + 2 , len) == 1) {
return 1;
}
}
i += len + 2;
}
return 0;
}
/*
Copyright (c) 2018 by Clara Dô and Weronika Kolodziejak
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#define DIGEST_LEN 20
#define SHA1_BLOCK_SIZE 64
#define RIPEMD160_BLOCK_SIZE 64
struct key *find_key(const char *id);
struct key *retain_key(struct key *key);
void release_key(struct key *key);
struct key *add_key(char *id, int type, int len, unsigned char *value);
int add_hmac(struct buffered *buf, struct interface *ifp,
unsigned char *packet_header);
int check_hmac(const unsigned char *packet, int packetlen, int bodylen,
const unsigned char *src, const unsigned char *dst);
...@@ -42,6 +42,7 @@ THE SOFTWARE. ...@@ -42,6 +42,7 @@ THE SOFTWARE.
#include "configuration.h" #include "configuration.h"
#include "local.h" #include "local.h"
#include "xroute.h" #include "xroute.h"
#include "hmac.h"
struct interface *interfaces = NULL; struct interface *interfaces = NULL;
...@@ -474,6 +475,15 @@ interface_updown(struct interface *ifp, int up) ...@@ -474,6 +475,15 @@ interface_updown(struct interface *ifp, int up)
update_interface_metric(ifp); update_interface_metric(ifp);
rc = check_interface_ipv4(ifp); rc = check_interface_ipv4(ifp);
if(IF_CONF(ifp, key) != ifp->key) {
if(ifp->key != NULL)
release_key(ifp->key);
if(IF_CONF(ifp, key) != NULL)
ifp->key = retain_key(IF_CONF(ifp, key));
else
ifp->key = NULL;
}
debugf("Upped interface %s (cost=%d, channel=%d%s).\n", debugf("Upped interface %s (cost=%d, channel=%d%s).\n",
ifp->name, ifp->name,
ifp->cost, ifp->cost,
......
...@@ -35,6 +35,13 @@ struct buffered_update { ...@@ -35,6 +35,13 @@ struct buffered_update {
#define IF_TYPE_TUNNEL 3 #define IF_TYPE_TUNNEL 3
/* If you modify this structure, also modify the merge_ifconf function. */ /* If you modify this structure, also modify the merge_ifconf function. */
struct key {
char *id;
int type;
int len;
unsigned char *value;
unsigned short ref_count;
};
struct interface_conf { struct interface_conf {
char *ifname; char *ifname;
...@@ -53,6 +60,7 @@ struct interface_conf { ...@@ -53,6 +60,7 @@ struct interface_conf {
unsigned int rtt_min; unsigned int rtt_min;
unsigned int rtt_max; unsigned int rtt_max;
unsigned int max_rtt_penalty; unsigned int max_rtt_penalty;
struct key *key;
struct interface_conf *next; struct interface_conf *next;
}; };
...@@ -102,6 +110,8 @@ struct buffered { ...@@ -102,6 +110,8 @@ struct buffered {
int hello; int hello;
}; };
#define INDEX_LEN 8
struct interface { struct interface {
struct interface *next; struct interface *next;
struct interface_conf *conf; struct interface_conf *conf;
...@@ -131,6 +141,9 @@ struct interface { ...@@ -131,6 +141,9 @@ struct interface {
unsigned int rtt_min; unsigned int rtt_min;
unsigned int rtt_max; unsigned int rtt_max;
unsigned int max_rtt_penalty; unsigned int max_rtt_penalty;
struct key *key;
unsigned int pc;
unsigned char index[INDEX_LEN];
}; };
#define IF_CONF(_ifp, _field) \ #define IF_CONF(_ifp, _field) \
......
...@@ -27,6 +27,7 @@ THE SOFTWARE. ...@@ -27,6 +27,7 @@ THE SOFTWARE.
#include <sys/time.h> #include <sys/time.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <time.h>
#include "babeld.h" #include "babeld.h"
#include "util.h" #include "util.h"
...@@ -40,6 +41,7 @@ THE SOFTWARE. ...@@ -40,6 +41,7 @@ THE SOFTWARE.
#include "resend.h" #include "resend.h"
#include "message.h" #include "message.h"
#include "configuration.h" #include "configuration.h"
#include "hmac.h"
unsigned char packet_header[4] = {42, 2}; unsigned char packet_header[4] = {42, 2};
...@@ -248,7 +250,7 @@ parse_ihu_subtlv(const unsigned char *a, int alen, ...@@ -248,7 +250,7 @@ parse_ihu_subtlv(const unsigned char *a, int alen,
{ {
int type, len, i = 0; int type, len, i = 0;
int have_timestamp = 0; int have_timestamp = 0;
unsigned int timestamp1, timestamp2; unsigned int timestamp1 = 0, timestamp2 = 0;
while(i < alen) { while(i < alen) {
type = a[0]; type = a[0];
...@@ -293,9 +295,8 @@ parse_ihu_subtlv(const unsigned char *a, int alen, ...@@ -293,9 +295,8 @@ parse_ihu_subtlv(const unsigned char *a, int alen,
*timestamp1_return = timestamp1; *timestamp1_return = timestamp1;
*timestamp2_return = timestamp2; *timestamp2_return = timestamp2;
} }
if(have_timestamp_return) { if(have_timestamp_return)
*have_timestamp_return = have_timestamp; *have_timestamp_return = have_timestamp;
}
return 1; return 1;
} }
...@@ -439,9 +440,100 @@ network_address(int ae, const unsigned char *a, unsigned int len, ...@@ -439,9 +440,100 @@ network_address(int ae, const unsigned char *a, unsigned int len,
return network_prefix(ae, -1, 0, a, NULL, len, a_r); return network_prefix(ae, -1, 0, a, NULL, len, a_r);
} }
static int
preparse_packet(const unsigned char *packet, int bodylen,
struct neighbour *neigh, struct interface *ifp)
{
int i;
const unsigned char *message;
unsigned char type, len;
int challenge_success = 0;
int have_index = 0, index_len;
unsigned char pc[4], index[256];
i = 0;
while(i < bodylen) {
message = packet + 4 + i;
type = message[0];
if(type == MESSAGE_PAD1) {
i++;
continue;
}
if(i + 1 > bodylen) {
fprintf(stderr, "Received truncated message.\n");
break;
}
len = message[1];
if(i + len > bodylen) {
fprintf(stderr, "Received truncated message.\n");
break;
}
if(type == MESSAGE_CRYPTO_SEQNO) {
if(len < 4) {
fprintf(stderr, "Received truncated PC TLV.\n");
break;
}
if(len > 4 + 32) {
fprintf(stderr, "Overlong PC TLV.\n");
break;
}
debugf("Received PC from %s.\n",
format_address(neigh->address));
memcpy(pc, message + 2, 4);
index_len = len - 4;
memcpy(index, message + 6, len - 4);
have_index = 1;
} else if(type == MESSAGE_CHALLENGE_RESPONSE) {
debugf("Received challenge response from %s.\n",
format_address(neigh->address));
gettime(&now);
if(len == sizeof(neigh->nonce) &&
memcmp(neigh->nonce, message + 2, len) == 0 &&
timeval_compare(&now, &neigh->challenge_deadline) <= 0) {
challenge_success = 1;
} else {
debugf("Challenge failed.\n");
}
} else if(type == MESSAGE_CHALLENGE_REQUEST) {
unsigned char nonce[len];
debugf("Received challenge request from %s.\n",
format_address(neigh->address));
memcpy(nonce, message + 2, len);
send_challenge_reply(neigh, nonce, len);
}
i += len + 2;
}
if(!have_index) {
debugf("No PC in packet.\n");
return 0;
}
if(neigh->have_index && neigh->index_len == index_len &&
memcmp(index, neigh->index, index_len) == 0) {
if(memcmp(neigh->pc, pc, 4) < 0) {
memcpy(neigh->pc, pc, 4);
return 1;
} else {
debugf("Out of order PC.\n");
return 0;
}
} else if(challenge_success) {
neigh->index_len = index_len;
memcpy(neigh->index, index, index_len);
memcpy(neigh->pc, pc, 4);
neigh->have_index = 1;
return 1;
} else {
send_challenge_req(neigh);
return 0;
}
}
void void
parse_packet(const unsigned char *from, struct interface *ifp, parse_packet(const unsigned char *from, struct interface *ifp,
const unsigned char *packet, int packetlen) const unsigned char *packet, int packetlen,
const unsigned char *to)
{ {
int i; int i;
const unsigned char *message; const unsigned char *message;
...@@ -494,6 +586,19 @@ parse_packet(const unsigned char *from, struct interface *ifp, ...@@ -494,6 +586,19 @@ parse_packet(const unsigned char *from, struct interface *ifp,
return; return;
} }
if(ifp->key != NULL) {
if(check_hmac(packet, packetlen, bodylen, neigh->address,
to) != 1) {
fprintf(stderr, "Received wrong hmac.\n");
return;
}
if(preparse_packet(packet, bodylen, neigh, ifp) == 0) {
fprintf(stderr, "Received wrong PC or failed the challenge.\n");
return;
}
}
i = 0; i = 0;
while(i < bodylen) { while(i < bodylen) {
message = packet + 4 + i; message = packet + 4 + i;
...@@ -832,6 +937,10 @@ parse_packet(const unsigned char *from, struct interface *ifp, ...@@ -832,6 +937,10 @@ parse_packet(const unsigned char *from, struct interface *ifp,
format_eui64(message + 8), seqno); format_eui64(message + 8), seqno);
handle_request(neigh, prefix, plen, src_prefix, src_plen, handle_request(neigh, prefix, plen, src_prefix, src_plen,
message[6], seqno, message + 8); message[6], seqno, message + 8);
} else if(type == MESSAGE_CRYPTO_SEQNO ||
type == MESSAGE_CHALLENGE_REQUEST ||
type == MESSAGE_CHALLENGE_RESPONSE) {
/* We're dealing with these in preparse_packet. */
} else { } else {
debugf("Received unknown packet type %d from %s on %s.\n", debugf("Received unknown packet type %d from %s on %s.\n",
type, format_address(from), ifp->name); type, format_address(from), ifp->name);
...@@ -913,16 +1022,26 @@ void ...@@ -913,16 +1022,26 @@ void
flushbuf(struct buffered *buf, struct interface *ifp) flushbuf(struct buffered *buf, struct interface *ifp)
{ {
int rc; int rc;
int end = buf->len;
assert(buf->len <= buf->size); assert(buf->len <= buf->size);
if(buf->len > 0) { if(buf->len > 0) {
if(ifp->key != NULL && ifp->key->type != 0)
send_crypto_seqno(buf, ifp);
debugf(" (flushing %d buffered bytes)\n", buf->len); debugf(" (flushing %d buffered bytes)\n", buf->len);
DO_HTONS(packet_header + 2, buf->len); DO_HTONS(packet_header + 2, buf->len);
fill_rtt_message(buf, ifp); fill_rtt_message(buf, ifp);
if(ifp->key != NULL && ifp->key->type != 0) {
end = add_hmac(buf, ifp, packet_header);
if(end < 0) {
fprintf(stderr, "Couldn't add HMAC.\n");
return;
}
}
rc = babel_send(protocol_socket, rc = babel_send(protocol_socket,
packet_header, sizeof(packet_header), packet_header, sizeof(packet_header),
buf->buf, buf->len, buf->buf, end,
(struct sockaddr*)&buf->sin6, (struct sockaddr*)&buf->sin6,
sizeof(buf->sin6)); sizeof(buf->sin6));
if(rc < 0) if(rc < 0)
...@@ -962,6 +1081,8 @@ schedule_flush_now(struct buffered *buf) ...@@ -962,6 +1081,8 @@ schedule_flush_now(struct buffered *buf)
static void static void
ensure_space(struct buffered *buf, struct interface *ifp, int space) ensure_space(struct buffered *buf, struct interface *ifp, int space)
{ {
if(ifp->key != NULL)
space += MAX_HMAC_SPACE + 6 + NONCE_LEN;
if(buf->size - buf->len < space) if(buf->size - buf->len < space)
flushbuf(buf, ifp); flushbuf(buf, ifp);
} }
...@@ -969,7 +1090,9 @@ ensure_space(struct buffered *buf, struct interface *ifp, int space) ...@@ -969,7 +1090,9 @@ ensure_space(struct buffered *buf, struct interface *ifp, int space)
static void static void
start_message(struct buffered *buf, struct interface *ifp, int type, int len) start_message(struct buffered *buf, struct interface *ifp, int type, int len)
{ {
if(buf->size - buf->len < len + 2) int space =
ifp->key == NULL ? len + 2 : len + 2 + MAX_HMAC_SPACE + 6 + NONCE_LEN;
if(buf->size - buf->len < space)
flushbuf(buf, ifp); flushbuf(buf, ifp);
buf->buf[buf->len++] = type; buf->buf[buf->len++] = type;
buf->buf[buf->len++] = len; buf->buf[buf->len++] = len;
...@@ -1012,6 +1135,23 @@ accumulate_bytes(struct buffered *buf, ...@@ -1012,6 +1135,23 @@ accumulate_bytes(struct buffered *buf,
buf->len += len; buf->len += len;
} }
int
send_crypto_seqno(struct buffered *buf, struct interface *ifp)
{
if(ifp->pc == 0) {
int rc;
rc = read_random_bytes(ifp->index, INDEX_LEN);
if(rc < INDEX_LEN)
return -1;
}
start_message(buf, ifp, MESSAGE_CRYPTO_SEQNO, 4 + INDEX_LEN);
accumulate_int(buf, ifp->pc);
accumulate_bytes(buf, ifp->index, INDEX_LEN);
end_message(buf, MESSAGE_CRYPTO_SEQNO, 4 + INDEX_LEN);
ifp->pc++;
return 1;
}
void void
send_ack(struct neighbour *neigh, unsigned short nonce, unsigned short interval) send_ack(struct neighbour *neigh, unsigned short nonce, unsigned short interval)
{ {
...@@ -1024,6 +1164,36 @@ send_ack(struct neighbour *neigh, unsigned short nonce, unsigned short interval) ...@@ -1024,6 +1164,36 @@ send_ack(struct neighbour *neigh, unsigned short nonce, unsigned short interval)
schedule_flush_ms(&neigh->buf, roughly(interval * 6)); schedule_flush_ms(&neigh->buf, roughly(interval * 6));
} }
int
send_challenge_req(struct neighbour *neigh)
{
int rc;
debugf("Sending challenge request to %s on %s.\n",
format_address(neigh->address), neigh->ifp->name);
rc = read_random_bytes(neigh->nonce, NONCE_LEN);
if(rc < NONCE_LEN)
return -1;
start_message(&neigh->buf, neigh->ifp, MESSAGE_CHALLENGE_REQUEST, NONCE_LEN);
accumulate_bytes(&neigh->buf, neigh->nonce, NONCE_LEN);
end_message(&neigh->buf, MESSAGE_CHALLENGE_REQUEST, NONCE_LEN);
gettime(&now);
timeval_add_msec(&neigh->challenge_deadline, &now, 300);
schedule_flush_now(&neigh->buf);
return 1;
}
void
send_challenge_reply(struct neighbour *neigh, unsigned char *crypto_nonce,
int len)
{
debugf("Sending challenge reply to %s on %s.\n",
format_address(neigh->address), neigh->ifp->name);
start_message(&neigh->buf, neigh->ifp, MESSAGE_CHALLENGE_RESPONSE, len);
accumulate_bytes(&neigh->buf, crypto_nonce, len);
end_message(&neigh->buf, MESSAGE_CHALLENGE_RESPONSE, len);
schedule_flush_now(&neigh->buf);
}
static void static void
buffer_hello(struct buffered *buf, struct interface *ifp, buffer_hello(struct buffered *buf, struct interface *ifp,
unsigned short seqno, unsigned interval, int unicast) unsigned short seqno, unsigned interval, int unicast)
...@@ -1703,7 +1873,6 @@ send_ihu(struct neighbour *neigh, struct interface *ifp) ...@@ -1703,7 +1873,6 @@ send_ihu(struct neighbour *neigh, struct interface *ifp)
return; return;
} }
if(ifp && neigh->ifp != ifp) if(ifp && neigh->ifp != ifp)
return; return;
...@@ -1948,6 +2117,7 @@ send_multicast_multihop_request(struct interface *ifp, ...@@ -1948,6 +2117,7 @@ send_multicast_multihop_request(struct interface *ifp,
src_prefix, src_plen, src_prefix, src_plen,
seqno, id, hop_count); seqno, id, hop_count);
} }
} }
void void
......
...@@ -21,6 +21,7 @@ THE SOFTWARE. ...@@ -21,6 +21,7 @@ THE SOFTWARE.
*/ */
#define MAX_BUFFERED_UPDATES 200 #define MAX_BUFFERED_UPDATES 200
#define MAX_HMAC_SPACE 48
#define MESSAGE_PAD1 0 #define MESSAGE_PAD1 0
#define MESSAGE_PADN 1 #define MESSAGE_PADN 1
...@@ -34,6 +35,11 @@ THE SOFTWARE. ...@@ -34,6 +35,11 @@ THE SOFTWARE.
#define MESSAGE_REQUEST 9 #define MESSAGE_REQUEST 9
#define MESSAGE_MH_REQUEST 10 #define MESSAGE_MH_REQUEST 10
#define MESSAGE_CRYPTO_SEQNO 121
#define MESSAGE_HMAC 122
#define MESSAGE_CHALLENGE_REQUEST 123
#define MESSAGE_CHALLENGE_RESPONSE 124
/* Protocol extension through sub-TLVs. */ /* Protocol extension through sub-TLVs. */
#define SUBTLV_PAD1 0 #define SUBTLV_PAD1 0
#define SUBTLV_PADN 1 #define SUBTLV_PADN 1
...@@ -50,11 +56,15 @@ extern int split_horizon; ...@@ -50,11 +56,15 @@ extern int split_horizon;
extern unsigned char packet_header[4]; extern unsigned char packet_header[4];
void parse_packet(const unsigned char *from, struct interface *ifp, void parse_packet(const unsigned char *from, struct interface *ifp,
const unsigned char *packet, int packetlen); const unsigned char *packet, int packetlen,
const unsigned char *to);
void flushbuf(struct buffered *buf, struct interface *ifp); void flushbuf(struct buffered *buf, struct interface *ifp);
void flushupdates(struct interface *ifp); void flushupdates(struct interface *ifp);
int send_crypto_seqno(struct buffered *buf, struct interface *ifp);
void send_ack(struct neighbour *neigh, unsigned short nonce, void send_ack(struct neighbour *neigh, unsigned short nonce,
unsigned short interval); unsigned short interval);
int send_challenge_req(struct neighbour *neigh);
void send_challenge_reply(struct neighbour *neigh, unsigned char *crypto_nonce, int len);
void send_multicast_hello(struct interface *ifp, unsigned interval, int force); void send_multicast_hello(struct interface *ifp, unsigned interval, int force);
void send_unicast_hello(struct neighbour *neigh, unsigned interval, int force); void send_unicast_hello(struct neighbour *neigh, unsigned interval, int force);
void send_hello(struct interface *ifp); void send_hello(struct interface *ifp);
......
...@@ -34,6 +34,7 @@ THE SOFTWARE. ...@@ -34,6 +34,7 @@ THE SOFTWARE.
#include "interface.h" #include "interface.h"
#include "neighbour.h" #include "neighbour.h"
#include "source.h" #include "source.h"
#include "hmac.h"
#include "route.h" #include "route.h"
#include "message.h" #include "message.h"
#include "resend.h" #include "resend.h"
...@@ -105,7 +106,9 @@ find_neighbour(const unsigned char *address, struct interface *ifp) ...@@ -105,7 +106,9 @@ find_neighbour(const unsigned char *address, struct interface *ifp)
neigh->ihu_time = now; neigh->ihu_time = now;
neigh->hello.time = neigh->uhello.time = zero; neigh->hello.time = neigh->uhello.time = zero;
neigh->hello_rtt_receive_time = zero; neigh->hello_rtt_receive_time = zero;
neigh->echo_receive_time = zero;
neigh->rtt_time = zero; neigh->rtt_time = zero;
neigh->challenge_deadline = now;
neigh->ifp = ifp; neigh->ifp = ifp;
neigh->buf.buf = buf; neigh->buf.buf = buf;
neigh->buf.size = ifp->buf.size; neigh->buf.size = ifp->buf.size;
......
...@@ -27,6 +27,8 @@ struct hello_history { ...@@ -27,6 +27,8 @@ struct hello_history {
struct timeval time; struct timeval time;
}; };
#define NONCE_LEN 8
struct neighbour { struct neighbour {
struct neighbour *next; struct neighbour *next;
/* This is -1 when unknown, so don't make it unsigned */ /* This is -1 when unknown, so don't make it unsigned */
...@@ -42,8 +44,15 @@ struct neighbour { ...@@ -42,8 +44,15 @@ struct neighbour {
according to remote clock. */ according to remote clock. */
unsigned int hello_send_us; unsigned int hello_send_us;
struct timeval hello_rtt_receive_time; struct timeval hello_rtt_receive_time;
struct timeval echo_receive_time;
unsigned int rtt; unsigned int rtt;
struct timeval rtt_time; struct timeval rtt_time;
int have_index;
int index_len;
unsigned char pc[4];
unsigned char index[32];
unsigned char nonce[NONCE_LEN];
struct timeval challenge_deadline;
struct interface *ifp; struct interface *ifp;
struct buffered buf; struct buffered buf;
}; };
......
...@@ -29,6 +29,7 @@ THE SOFTWARE. ...@@ -29,6 +29,7 @@ THE SOFTWARE.
#include <sys/uio.h> #include <sys/uio.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/un.h> #include <sys/un.h>
#define __USE_GNU
#include <netinet/in.h> #include <netinet/in.h>
#include <netinet/in_systm.h> #include <netinet/in_systm.h>
#include <netinet/ip.h> #include <netinet/ip.h>
...@@ -84,6 +85,10 @@ babel_socket(int port) ...@@ -84,6 +85,10 @@ babel_socket(int port)
if(rc < 0) if(rc < 0)
perror("Couldn't set traffic class"); perror("Couldn't set traffic class");
rc = setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
if(rc < 0)
goto fail;
rc = fcntl(s, F_GETFL, 0); rc = fcntl(s, F_GETFL, 0);
if(rc < 0) if(rc < 0)
goto fail; goto fail;
...@@ -117,11 +122,15 @@ babel_socket(int port) ...@@ -117,11 +122,15 @@ babel_socket(int port)
} }
int int
babel_recv(int s, void *buf, int buflen, struct sockaddr *sin, int slen) babel_recv(int s, void *buf, int buflen, struct sockaddr *sin, int slen,
unsigned char *src_return)
{ {
struct iovec iovec; struct iovec iovec;
struct msghdr msg; struct msghdr msg;
int rc; unsigned char cmsgbuf[128];
struct cmsghdr *cmsg;
int rc, found;
unsigned char src[16] = {0};
memset(&msg, 0, sizeof(msg)); memset(&msg, 0, sizeof(msg));
iovec.iov_base = buf; iovec.iov_base = buf;
...@@ -130,8 +139,33 @@ babel_recv(int s, void *buf, int buflen, struct sockaddr *sin, int slen) ...@@ -130,8 +139,33 @@ babel_recv(int s, void *buf, int buflen, struct sockaddr *sin, int slen)
msg.msg_namelen = slen; msg.msg_namelen = slen;
msg.msg_iov = &iovec; msg.msg_iov = &iovec;
msg.msg_iovlen = 1; msg.msg_iovlen = 1;
msg.msg_control = cmsgbuf;
msg.msg_controllen = sizeof(cmsgbuf);
rc = recvmsg(s, &msg, 0); rc = recvmsg(s, &msg, 0);
if(rc < 0)
return rc;
found = 0;
memset(src, 0, 16);
cmsg = CMSG_FIRSTHDR(&msg);
while(cmsg != NULL) {
if(cmsg->cmsg_level == IPPROTO_IPV6 &&
cmsg->cmsg_type == IPV6_PKTINFO) {
struct in6_pktinfo *info =(struct in6_pktinfo*)CMSG_DATA(cmsg);
memcpy(src, info->ipi6_addr.s6_addr, 16);
found = 1;
break;
}
cmsg = CMSG_NXTHDR(&msg, cmsg);
}
if(!found) {
errno = EDESTADDRREQ;
return -1;
}
if(src_return != NULL)
memcpy(src_return, src, 16);
return rc; return rc;
} }
......
...@@ -21,7 +21,8 @@ THE SOFTWARE. ...@@ -21,7 +21,8 @@ THE SOFTWARE.
*/ */
int babel_socket(int port); int babel_socket(int port);
int babel_recv(int s, void *buf, int buflen, struct sockaddr *sin, int slen); int babel_recv(int s, void *buf, int buflen, struct sockaddr *sin, int slen,
unsigned char *src_return);
int babel_send(int s, int babel_send(int s,
const void *buf1, int buflen1, const void *buf2, int buflen2, const void *buf1, int buflen1, const void *buf2, int buflen2,
const struct sockaddr *sin, int slen); const struct sockaddr *sin, int slen);
......
...@@ -197,6 +197,38 @@ parse_thousands(const char *string) ...@@ -197,6 +197,38 @@ parse_thousands(const char *string)
return -1; return -1;
} }
int
h2i(char c)
{
if(c >= '0' && c <= '9')
return c - '0';
else if(c >= 'a' && c <= 'f')
return c - 'a' + 10;
else if(c >= 'A' && c <= 'F')
return c - 'A' + 10;
else
return -1;
}
int
fromhex(unsigned char *dest, char *src, int n)
{
int i;
if(n % 2 != 0)
return -1;
for(i = 0; i < n/2; i++) {
int a, b;
a = h2i(src[i*2]);
if(a < 0)
return -1;
b = h2i(src[i*2 + 1]);
if(b < 0)
return -1;
dest[i] = a*16 + b;
}
return n/2;
}
void void
do_debugf(int level, const char *format, ...) do_debugf(int level, const char *format, ...)
{ {
......
...@@ -79,6 +79,8 @@ void timeval_min(struct timeval *d, const struct timeval *s); ...@@ -79,6 +79,8 @@ void timeval_min(struct timeval *d, const struct timeval *s);
void timeval_min_sec(struct timeval *d, time_t secs); void timeval_min_sec(struct timeval *d, time_t secs);
int parse_nat(const char *string) ATTRIBUTE ((pure)); int parse_nat(const char *string) ATTRIBUTE ((pure));
int parse_thousands(const char *string) ATTRIBUTE ((pure)); int parse_thousands(const char *string) ATTRIBUTE ((pure));
int h2i(char c);
int fromhex(unsigned char *dest, char *src, int n);
void do_debugf(int level, const char *format, ...) void do_debugf(int level, const char *format, ...)
ATTRIBUTE ((format (printf, 2, 3))) COLD; ATTRIBUTE ((format (printf, 2, 3))) COLD;
int in_prefix(const unsigned char *restrict address, int in_prefix(const unsigned char *restrict address,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment