[PATCH RFC 2/2] babel: Add HMAC support
Toke Høiland-Jørgensen
toke at toke.dk
Fri Jul 13 22:16:16 CEST 2018
This implements support for HMAC verification in Babel, as specified by
draft-do-babel-hmac-00.
Signed-off-by: Toke Høiland-Jørgensen <toke at toke.dk>
---
proto/babel/babel.c | 80 ++++++++++
proto/babel/babel.h | 58 +++++++-
proto/babel/config.Y | 34 ++++
proto/babel/packets.c | 375 ++++++++++++++++++++++++++++++++++++++++++++++++-
4 files changed, 533 insertions(+), 14 deletions(-)
diff --git a/proto/babel/babel.c b/proto/babel/babel.c
index afa482bb..6f00d381 100644
--- a/proto/babel/babel.c
+++ b/proto/babel/babel.c
@@ -36,6 +36,7 @@
*/
#include <stdlib.h>
+#include <sys/random.h>
#include "babel.h"
@@ -1377,6 +1378,79 @@ babel_handle_seqno_request(union babel_msg *m, struct babel_iface *ifa)
}
}
+void
+babel_hmac_send_challenge(struct babel_iface *ifa, struct babel_neighbor *n)
+{
+ struct babel_proto *p = ifa->proto;
+ union babel_msg msg = {};
+
+ if (n->hmac_next_challenge > current_time())
+ {
+ TRACE(D_PACKETS, "Not sending HMAC challenge to %I due to rate limiting");
+ return;
+ }
+
+ TRACE(D_PACKETS, "Sending HMAC challenge to %I on iface %s",
+ n->addr, ifa->ifname);
+
+ getrandom(n->hmac_nonce, BABEL_HMAC_NONCE_LEN, 0);
+ n->hmac_nonce_expiry = current_time() + BABEL_HMAC_CHALLENGE_TIMEOUT;
+ n->hmac_next_challenge = current_time() + BABEL_HMAC_CHALLENGE_INTERVAL;
+
+ msg.type = BABEL_TLV_CHALLENGE_REQ;
+ msg.challenge.nonce_len = BABEL_HMAC_NONCE_LEN;
+ msg.challenge.nonce = n->hmac_nonce;
+
+ babel_send_unicast(&msg, ifa, n->addr);
+}
+
+void
+babel_hmac_reset_index(struct babel_iface *ifa)
+{
+ getrandom(ifa->hmac_index, BABEL_HMAC_INDEX_LEN, 0);
+ ifa->hmac_pc = 1;
+}
+
+int
+babel_handle_hmac(union babel_msg *m, struct babel_iface *ifa)
+{
+ struct babel_proto *p = ifa->proto;
+ struct babel_msg_hmac *msg = &m->hmac;
+
+ TRACE(D_PACKETS, "Handling HMAC check on %s from %I", ifa->ifname, msg->sender);
+
+ if (msg->index_len > BABEL_HMAC_INDEX_LEN || !msg->pc_seen)
+ return 0;
+
+ struct babel_neighbor *n = babel_get_neighbor(ifa, msg->sender);
+
+ /* On successful challenge, update PC and index to current values */
+ if (msg->challenge_seen &&
+ n->hmac_nonce_expiry &&
+ n->hmac_nonce_expiry >= current_time() &&
+ !memcmp(msg->challenge, n->hmac_nonce, BABEL_HMAC_NONCE_LEN))
+ {
+ n->hmac_index_len = msg->index_len;
+ memcpy(n->hmac_index, msg->index, msg->index_len);
+ n->hmac_pc = msg->pc;
+ }
+
+ /* If index differs, send challenge */
+ if (n->hmac_index_len != msg->index_len ||
+ memcmp(n->hmac_index, msg->index, msg->index_len))
+ {
+ babel_hmac_send_challenge(ifa, n);
+ return 0;
+ }
+
+ /* Index matches; only accept if PC is greater than last */
+ if (n->hmac_pc >= msg->pc)
+ return 0;
+
+ n->hmac_pc = msg->pc;
+ return 1;
+}
+
/*
* Babel interfaces
@@ -1589,6 +1663,9 @@ babel_add_iface(struct babel_proto *p, struct iface *new, struct babel_iface_con
init_list(&ifa->neigh_list);
ifa->hello_seqno = 1;
+ if (ic->auth_type == BABEL_AUTH_HMAC)
+ babel_hmac_reset_index(ifa);
+
ifa->timer = tm_new_init(ifa->pool, babel_iface_timer, ifa, 0, 0);
init_list(&ifa->msg_queue);
@@ -1685,6 +1762,9 @@ babel_reconfigure_iface(struct babel_proto *p, struct babel_iface *ifa, struct b
ifa->next_hop_ip4 = ipa_nonzero(new->next_hop_ip4) ? new->next_hop_ip4 : addr4;
ifa->next_hop_ip6 = ipa_nonzero(new->next_hop_ip6) ? new->next_hop_ip6 : ifa->addr;
+ if (new->auth_type == BABEL_AUTH_HMAC && old->auth_type != BABEL_AUTH_HMAC)
+ babel_hmac_reset_index(ifa);
+
if (ipa_zero(ifa->next_hop_ip4) && p->ip4_channel)
log(L_WARN "%s: Missing IPv4 next hop address for %s", p->p.name, ifa->ifname);
diff --git a/proto/babel/babel.h b/proto/babel/babel.h
index 14765c60..9ca4d339 100644
--- a/proto/babel/babel.h
+++ b/proto/babel/babel.h
@@ -19,6 +19,7 @@
#include "nest/route.h"
#include "nest/protocol.h"
#include "nest/locks.h"
+#include "nest/password.h"
#include "lib/resource.h"
#include "lib/lists.h"
#include "lib/socket.h"
@@ -60,6 +61,13 @@
#define BABEL_OVERHEAD (IP6_HEADER_LENGTH+UDP_HEADER_LENGTH)
#define BABEL_MIN_MTU (512 + BABEL_OVERHEAD)
+#define BABEL_AUTH_NONE 0
+#define BABEL_AUTH_HMAC 1
+#define BABEL_HMAC_NONCE_LEN 10 /* 80 bit random nonce */
+#define BABEL_HMAC_INDEX_LEN 10 /* 80 bit indexes allowed */
+#define BABEL_HMAC_CHALLENGE_TIMEOUT (30 S_)
+#define BABEL_HMAC_CHALLENGE_INTERVAL (300 MS_)
+
enum babel_tlv_type {
BABEL_TLV_PAD1 = 0,
@@ -73,13 +81,10 @@ enum babel_tlv_type {
BABEL_TLV_UPDATE = 8,
BABEL_TLV_ROUTE_REQUEST = 9,
BABEL_TLV_SEQNO_REQUEST = 10,
- /* extensions - not implemented
- BABEL_TLV_TS_PC = 11,
- BABEL_TLV_HMAC = 12,
- BABEL_TLV_SS_UPDATE = 13,
- BABEL_TLV_SS_REQUEST = 14,
- BABEL_TLV_SS_SEQNO_REQUEST = 15,
- */
+ BABEL_TLV_CRYPTO_SEQNO = 121,
+ BABEL_TLV_HMAC = 122,
+ BABEL_TLV_CHALLENGE_REQ = 123,
+ BABEL_TLV_CHALLENGE_RESP = 124,
BABEL_TLV_MAX
};
@@ -137,6 +142,11 @@ struct babel_iface_config {
ip_addr next_hop_ip4;
ip_addr next_hop_ip6;
+
+ u8 auth_type; /* Authentication type (BABEL_AUTH_*) */
+ uint hmac_num_keys; /* Number of configured HMAC keys */
+ uint hmac_total_len; /* Total digest length for all configured keys */
+ list *passwords; /* Passwords for authentication */
};
struct babel_proto {
@@ -184,6 +194,9 @@ struct babel_iface {
u16 hello_seqno; /* To be increased on each hello */
+ u32 hmac_pc;
+ u8 hmac_index[BABEL_HMAC_INDEX_LEN];
+
btime next_hello;
btime next_regular;
btime next_triggered;
@@ -207,6 +220,14 @@ struct babel_neighbor {
u16 hello_map;
u16 next_hello_seqno;
uint last_hello_int;
+
+ u32 hmac_pc;
+ u8 hmac_index_len;
+ u8 hmac_index[BABEL_HMAC_INDEX_LEN];
+ u8 hmac_nonce[BABEL_HMAC_NONCE_LEN];
+ btime hmac_nonce_expiry;
+ btime hmac_next_challenge;
+
/* expiry timers */
btime hello_expiry;
btime ihu_expiry;
@@ -339,6 +360,23 @@ struct babel_msg_seqno_request {
ip_addr sender;
};
+struct babel_msg_challenge {
+ u8 type;
+ u8 nonce_len;
+ u8 *nonce;
+};
+
+struct babel_msg_hmac {
+ u8 type;
+ ip_addr sender;
+ u32 pc;
+ u8 pc_seen;
+ u8 index_len;
+ u8 *index;
+ u8 challenge_seen;
+ u8 challenge[BABEL_HMAC_NONCE_LEN];
+};
+
union babel_msg {
u8 type;
struct babel_msg_ack_req ack_req;
@@ -348,6 +386,8 @@ union babel_msg {
struct babel_msg_update update;
struct babel_msg_route_request route_request;
struct babel_msg_seqno_request seqno_request;
+ struct babel_msg_challenge challenge;
+ struct babel_msg_hmac hmac;
};
struct babel_msg_node {
@@ -367,6 +407,10 @@ void babel_handle_router_id(union babel_msg *msg, struct babel_iface *ifa);
void babel_handle_update(union babel_msg *msg, struct babel_iface *ifa);
void babel_handle_route_request(union babel_msg *msg, struct babel_iface *ifa);
void babel_handle_seqno_request(union babel_msg *msg, struct babel_iface *ifa);
+void babel_handle_hmac_challenge_req(union babel_msg *m, struct babel_iface *ifa);
+
+int babel_handle_hmac(union babel_msg *m, struct babel_iface *ifa);
+void babel_hmac_reset_index(struct babel_iface *ifa);
void babel_show_interfaces(struct proto *P, char *iff);
void babel_show_neighbors(struct proto *P, char *iff);
diff --git a/proto/babel/config.Y b/proto/babel/config.Y
index 3af79fd6..36608bf4 100644
--- a/proto/babel/config.Y
+++ b/proto/babel/config.Y
@@ -25,7 +25,7 @@ CF_DECLS
CF_KEYWORDS(BABEL, INTERFACE, METRIC, RXCOST, HELLO, UPDATE, INTERVAL, PORT,
TYPE, WIRED, WIRELESS, RX, TX, BUFFER, PRIORITY, LENGTH, CHECK, LINK,
NEXT, HOP, IPV4, IPV6, BABEL_METRIC, SHOW, INTERFACES, NEIGHBORS,
- ENTRIES, RANDOMIZE, ROUTER, ID)
+ ENTRIES, RANDOMIZE, ROUTER, ID, AUTHENTICATION, NONE, HMAC)
CF_GRAMMAR
@@ -91,6 +91,35 @@ babel_iface_finish:
BABEL_IFACE->ihu_interval = MIN_(BABEL_IFACE->hello_interval*BABEL_IHU_INTERVAL_FACTOR, BABEL_MAX_INTERVAL);
BABEL_CFG->hold_time = MAX_(BABEL_CFG->hold_time, BABEL_IFACE->update_interval*BABEL_HOLD_TIME_FACTOR);
+
+ BABEL_IFACE->passwords = get_passwords();
+
+ if (!BABEL_IFACE->auth_type != !BABEL_IFACE->passwords)
+ log(L_WARN "Authentication and password options should be used together");
+
+ if (BABEL_IFACE->passwords)
+ {
+ struct password_item *pass;
+ uint len = 0, i = 0;
+ WALK_LIST(pass, *BABEL_IFACE->passwords)
+ {
+ /* Set default crypto algorithm (HMAC-SHA256) */
+ if (!pass->alg && (BABEL_IFACE->auth_type == BABEL_AUTH_HMAC))
+ pass->alg = ALG_HMAC_SHA256;
+
+ if (!(pass->alg & ALG_HMAC))
+ cf_error("Only HMAC algorithms are supported");
+
+ if (BABEL_IFACE->auth_type != BABEL_AUTH_HMAC)
+ cf_error("Password setting requires HMAC authentication");
+
+ len += mac_type_length(pass->alg);
+ i++;
+ }
+ BABEL_IFACE->hmac_num_keys = i;
+ BABEL_IFACE->hmac_total_len = len;
+ }
+
};
@@ -109,6 +138,9 @@ babel_iface_item:
| CHECK LINK bool { BABEL_IFACE->check_link = $3; }
| NEXT HOP IPV4 ipa { BABEL_IFACE->next_hop_ip4 = $4; if (!ipa_is_ip4($4)) cf_error("Must be an IPv4 address"); }
| NEXT HOP IPV6 ipa { BABEL_IFACE->next_hop_ip6 = $4; if (!ipa_is_ip6($4)) cf_error("Must be an IPv6 address"); }
+ | AUTHENTICATION NONE { BABEL_IFACE->auth_type = BABEL_AUTH_NONE; }
+ | AUTHENTICATION HMAC { BABEL_IFACE->auth_type = BABEL_AUTH_HMAC; }
+ | password_list { }
;
babel_iface_opts:
diff --git a/proto/babel/packets.c b/proto/babel/packets.c
index 9c767874..a587be5c 100644
--- a/proto/babel/packets.c
+++ b/proto/babel/packets.c
@@ -11,6 +11,7 @@
*/
#include "babel.h"
+#include "lib/mac.h"
struct babel_pkt_header {
@@ -105,6 +106,26 @@ struct babel_tlv_seqno_request {
u8 addr[0];
} PACKED;
+struct babel_tlv_crypto_seqno {
+ u8 type;
+ u8 length;
+ u32 pc;
+ u8 index[0];
+} PACKED;
+
+struct babel_tlv_hmac {
+ u8 type;
+ u8 length;
+ u8 hmac[0];
+} PACKED;
+
+struct babel_tlv_challenge {
+ u8 type;
+ u8 length;
+ u8 nonce[0];
+} PACKED;
+
+
struct babel_subtlv_source_prefix {
u8 type;
u8 length;
@@ -135,6 +156,16 @@ struct babel_parse_state {
u8 def_ip4_prefix_seen; /* def_ip4_prefix is valid */
u8 current_tlv_endpos; /* End of self-terminating TLVs (offset from start) */
u8 sadr_enabled;
+ u8 is_unicast;
+ /* HMAC data */
+ u32 hmac_pc;
+ u8 hmac_pc_seen;
+ u8 hmac_index_len;
+ u8 *hmac_index;
+ u8 hmac_challenge_resp_seen;
+ u8 hmac_challenge_resp[BABEL_HMAC_NONCE_LEN];
+ u8 hmac_challenge_len;
+ u8 *hmac_challenge;
};
enum parse_result {
@@ -152,6 +183,13 @@ struct babel_write_state {
u8 def_ip6_pxlen;
};
+struct babel_hmac_pseudohdr {
+ u8 src_addr[16];
+ u16 src_port;
+ u8 dst_addr[16];
+ u16 dst_port;
+} PACKED;
+
#define DROP(DSC,VAL) do { err_dsc = DSC; err_val = VAL; goto drop; } while(0)
#define DROP1(DSC) do { err_dsc = DSC; goto drop; } while(0)
@@ -270,6 +308,9 @@ static int babel_read_update(struct babel_tlv *hdr, union babel_msg *msg, struct
static int babel_read_route_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
static int babel_read_seqno_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
static int babel_read_source_prefix(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
+static int babel_read_crypto_seqno(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
+static int babel_read_challenge_req(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
+static int babel_read_challenge_resp(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
static uint babel_write_ack(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
static uint babel_write_hello(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
@@ -277,6 +318,7 @@ static uint babel_write_ihu(struct babel_tlv *hdr, union babel_msg *msg, struct
static uint babel_write_update(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
static uint babel_write_route_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
static uint babel_write_seqno_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
+static uint babel_write_challenge(struct babel_tlv *hdr, union babel_msg *m, struct babel_write_state *state UNUSED, uint max_len);
static int babel_write_source_prefix(struct babel_tlv *hdr, net_addr *net, uint max_len);
struct babel_tlv_data {
@@ -341,6 +383,24 @@ static const struct babel_tlv_data tlv_data[BABEL_TLV_MAX] = {
babel_write_seqno_request,
babel_handle_seqno_request
},
+ [BABEL_TLV_CRYPTO_SEQNO] = {
+ sizeof(struct babel_tlv_crypto_seqno),
+ babel_read_crypto_seqno,
+ NULL,
+ NULL,
+ },
+ [BABEL_TLV_CHALLENGE_REQ] = {
+ sizeof(struct babel_tlv_challenge),
+ babel_read_challenge_req,
+ babel_write_challenge,
+ NULL,
+ },
+ [BABEL_TLV_CHALLENGE_RESP] = {
+ sizeof(struct babel_tlv_challenge),
+ babel_read_challenge_resp,
+ babel_write_challenge,
+ NULL,
+ },
};
static int
@@ -1107,6 +1167,73 @@ babel_write_source_prefix(struct babel_tlv *hdr, net_addr *n, uint max_len)
return len;
}
+static int
+babel_read_crypto_seqno(struct babel_tlv *hdr, union babel_msg *m UNUSED,
+ struct babel_parse_state *state)
+{
+ struct babel_tlv_crypto_seqno *tlv = (void *) hdr;
+
+ if (!state->hmac_pc_seen) {
+ state->hmac_pc_seen = 1;
+ state->hmac_pc = get_u32(&tlv->pc);
+ state->hmac_index_len = tlv->length - sizeof(state->hmac_pc);
+ state->hmac_index = tlv->index;
+ }
+
+ return PARSE_IGNORE;
+}
+
+static int
+babel_read_challenge_req(struct babel_tlv *hdr, union babel_msg *m UNUSED,
+ struct babel_parse_state *state)
+{
+ struct babel_tlv_challenge *tlv = (void *) hdr;
+
+ if (!state->is_unicast) {
+ DBG("Ignoring non-unicast challenge request from %I", state->saddr);
+ return PARSE_IGNORE;
+ }
+
+ if (!state->hmac_challenge_len) {
+ state->hmac_challenge_len = tlv->length;
+ state->hmac_challenge = tlv->nonce;
+ }
+
+ return PARSE_IGNORE;
+}
+
+static int
+babel_read_challenge_resp(struct babel_tlv *hdr, union babel_msg *m UNUSED,
+ struct babel_parse_state *state)
+{
+ struct babel_tlv_challenge *tlv = (void *) hdr;
+
+ if (tlv->length == BABEL_HMAC_NONCE_LEN && !state->hmac_challenge_resp_seen)
+ {
+ state->hmac_challenge_resp_seen = 1;
+ memcpy(state->hmac_challenge_resp, tlv->nonce, BABEL_HMAC_NONCE_LEN);
+ }
+
+ return PARSE_IGNORE;
+}
+
+static uint
+babel_write_challenge(struct babel_tlv *hdr, union babel_msg *m,
+ struct babel_write_state *state UNUSED, uint max_len)
+{
+ struct babel_tlv_challenge *tlv = (void *) hdr;
+ struct babel_msg_challenge *msg = &m->challenge;
+
+ uint len = sizeof(struct babel_tlv_challenge) + msg->nonce_len;
+
+ if (len > max_len)
+ return 0;
+
+ TLV_HDR(tlv, msg->type, len);
+ memcpy(tlv->nonce, msg->nonce, msg->nonce_len);
+
+ return len;
+}
static inline int
babel_read_subtlvs(struct babel_tlv *hdr,
@@ -1189,6 +1316,210 @@ babel_write_tlv(struct babel_tlv *hdr,
}
+static int
+babel_hmac_hash(struct password_item *pass,
+ struct babel_pkt_header *pkt,
+ ip_addr saddr, u16 sport,
+ ip_addr daddr, u16 dport,
+ byte *buf, uint *buf_len)
+{
+ struct mac_context ctx;
+ struct babel_hmac_pseudohdr phdr = {};
+ int pkt_len = get_u16(&pkt->length) + sizeof(struct babel_pkt_header);
+
+ put_ip6(phdr.src_addr, saddr);
+ put_u16(&phdr.src_port, sport);
+ put_ip6(phdr.dst_addr, daddr);
+ put_u16(&phdr.dst_port, dport);
+ DBG("HMAC pseudo-header: %I %d %I %d\n", saddr, sport, daddr, dport);
+
+ if (mac_type_length(pass->alg) > *buf_len)
+ return 1;
+
+ mac_init(&ctx, pass->alg, pass->password, pass->length);
+ mac_update(&ctx, (byte *)&phdr, sizeof(phdr));
+ mac_update(&ctx, (byte *)pkt, pkt_len);
+
+ *buf_len = mac_get_length(&ctx);
+ memcpy(buf, mac_final(&ctx), *buf_len);
+
+ mac_cleanup(&ctx);
+
+ return 0;
+}
+
+static int
+babel_hmac_check(struct babel_iface *ifa,
+ struct babel_pkt_header *pkt,
+ ip_addr saddr, u16 sport,
+ ip_addr daddr, u16 dport,
+ byte *start, uint len)
+{
+ struct babel_proto *p = ifa->proto;
+ struct password_item *pass;
+ struct babel_tlv *tlv;
+ struct babel_tlv_hmac *hmac;
+ btime now_ = current_real_time();
+ byte *end = start + len;
+
+ TRACE(D_PACKETS, "Checking packet HMAC signature");
+
+ if (len < sizeof(*tlv))
+ return 1;
+
+ WALK_LIST(pass, *ifa->cf->passwords)
+ {
+ byte mac_res[MAX_HASH_SIZE];
+ uint mac_len = MAX_HASH_SIZE;
+ int res = 0;
+
+ if (pass->accfrom > now_ || pass->accto < now_)
+ continue;
+
+ if (babel_hmac_hash(pass, pkt,
+ saddr, sport,
+ daddr, dport,
+ mac_res, &mac_len))
+ continue;
+
+ LOOP_TLVS(start, end, tlv)
+ {
+ if (tlv->type != BABEL_TLV_HMAC)
+ continue;
+
+ hmac = (void *)tlv;
+
+ res = (tlv->length == mac_len && !memcmp(hmac->hmac, mac_res, mac_len));
+ if (res)
+ break;
+
+ DBG("HMAC mismatch key id %d pos %d len %d/%d\n",
+ pass->id, (byte *)tlv - (byte *)pkt, mac_len, tlv->length);
+ }
+ LOOP_TLVS_END;
+
+ if (res) {
+ TRACE(D_PACKETS, "HMAC signature OK");
+ return 0;
+ }
+ }
+
+ TRACE(D_PACKETS, "No matching HMAC key found");
+ return 1;
+
+frame_err:
+ DBG("HMAC trailer TLV framing error\n");
+ return 1;
+}
+
+static int
+babel_hmac_check_state(struct babel_iface *ifa, struct babel_parse_state *state)
+{
+ struct babel_proto *p = ifa->proto;
+ union babel_msg msg = {};
+ int ret;
+
+ if (state->hmac_challenge_len)
+ {
+ union babel_msg resp = {};
+ TRACE(D_PACKETS, "Sending HMAC challenge response to %I", state->saddr);
+ resp.type = BABEL_TLV_CHALLENGE_RESP;
+ resp.challenge.nonce_len = state->hmac_challenge_len;
+ resp.challenge.nonce = state->hmac_challenge;
+ babel_send_unicast(&resp, ifa, state->saddr);
+ }
+
+ msg.type = BABEL_TLV_HMAC;
+ msg.hmac.sender = state->saddr;
+ msg.hmac.pc = state->hmac_pc;
+ msg.hmac.pc_seen = state->hmac_pc_seen;
+ msg.hmac.index_len = state->hmac_index_len;
+ msg.hmac.index = state->hmac_index;
+ msg.hmac.challenge_seen = state->hmac_challenge_resp_seen;
+ memcpy(msg.hmac.challenge,
+ state->hmac_challenge_resp,
+ BABEL_HMAC_NONCE_LEN);
+
+ ret = babel_handle_hmac(&msg, ifa);
+
+ return ret;
+}
+
+static int
+babel_hmac_add_pc(struct babel_iface *ifa, struct babel_tlv *tlv, int max_len)
+{
+ struct babel_proto *p = ifa->proto;
+
+ struct babel_tlv_crypto_seqno *msg = (void *)tlv;
+ int len = sizeof(*msg) + BABEL_HMAC_INDEX_LEN;
+
+ if (len > max_len) {
+ LOG_PKT("Warning: Insufficient space to add HMAC seqno TLV on iface %s: %d < %d",
+ ifa->ifname, max_len, len);
+ return 0;
+ }
+
+ msg->type = BABEL_TLV_CRYPTO_SEQNO;
+ msg->length = len - sizeof(struct babel_tlv);
+ put_u32(&msg->pc, ifa->hmac_pc++);
+ memcpy(msg->index, ifa->hmac_index, BABEL_HMAC_INDEX_LEN);
+
+ /* Reset index on overflow to 0 */
+ if (!ifa->hmac_pc)
+ babel_hmac_reset_index(ifa);
+
+ return len;
+}
+
+static int
+babel_hmac_sign(struct babel_iface *ifa, ip_addr dest)
+{
+ struct babel_proto *p = ifa->proto;
+ sock *sk = ifa->sk;
+
+ struct babel_pkt_header *hdr = (void *) sk->tbuf;
+ int len = get_u16(&hdr->length) + sizeof(struct babel_pkt_header);
+
+ byte *pos = (byte *)hdr + len;
+ byte *end = (byte *)hdr + ifa->tx_length;
+ struct babel_tlv *tlv = (void *)pos;
+ struct password_item *pass;
+ btime now_ = current_real_time();
+ int tot_len = 0, i = 0;
+
+ WALK_LIST(pass, *ifa->cf->passwords)
+ {
+ struct babel_tlv_hmac *msg = (void *)tlv;
+ uint buf_len = (uint) (end - (byte *)msg - sizeof(*msg));
+
+ if (pass->genfrom > now_ || pass->gento < now_)
+ continue;
+
+ if (babel_hmac_hash(pass, hdr,
+ sk->saddr, sk->fport,
+ dest, sk->dport,
+ msg->hmac, &buf_len))
+ {
+ LOG_PKT("Warning: Insufficient space for HMAC signatures on iface %s dest %I",
+ ifa->ifname, dest);
+ break;
+ }
+
+ msg->type = BABEL_TLV_HMAC;
+ msg->length = buf_len;
+
+ tlv = NEXT_TLV(tlv);
+ tot_len += buf_len + sizeof(*msg);
+ i++;
+ }
+
+ TRACE(D_PACKETS, "Added %d HMAC signatures (%d bytes) on ifa %s for dest %I",
+ i, tot_len, ifa->ifname, dest);
+
+ return tot_len;
+}
+
+
/*
* Packet RX/TX functions
*/
@@ -1200,6 +1531,9 @@ babel_send_to(struct babel_iface *ifa, ip_addr dest)
struct babel_pkt_header *hdr = (void *) sk->tbuf;
int len = get_u16(&hdr->length) + sizeof(struct babel_pkt_header);
+ if (ifa->cf->auth_type == BABEL_AUTH_HMAC)
+ len += babel_hmac_sign(ifa, dest);
+
DBG("Babel: Sending %d bytes to %I\n", len, dest);
return sk_send_to(sk, len, dest, 0);
}
@@ -1223,12 +1557,19 @@ babel_write_queue(struct babel_iface *ifa, list *queue)
{
struct babel_proto *p = ifa->proto;
struct babel_write_state state = { .next_hop_ip6 = ifa->addr };
+ int tx_length = ifa->tx_length;
if (EMPTY_LIST(*queue))
return 0;
+ if (ifa->cf->auth_type == BABEL_AUTH_HMAC)
+ tx_length -= (sizeof(struct babel_tlv_crypto_seqno) +
+ BABEL_HMAC_INDEX_LEN +
+ ifa->cf->hmac_total_len +
+ sizeof(struct babel_tlv_hmac) * ifa->cf->hmac_num_keys);
+
byte *pos = ifa->sk->tbuf;
- byte *end = pos + ifa->tx_length;
+ byte *end = pos + tx_length;
struct babel_pkt_header *pkt = (void *) pos;
pkt->magic = BABEL_MAGIC;
@@ -1252,6 +1593,9 @@ babel_write_queue(struct babel_iface *ifa, list *queue)
sl_free(p->msg_slab, msg);
}
+ if (ifa->cf->auth_type == BABEL_AUTH_HMAC)
+ pos += babel_hmac_add_pc(ifa, (struct babel_tlv *) pos, end-pos);
+
uint plen = pos - (byte *) pkt;
put_u16(&pkt->length, plen - sizeof(struct babel_pkt_header));
@@ -1329,10 +1673,13 @@ babel_enqueue(union babel_msg *msg, struct babel_iface *ifa)
/**
* babel_process_packet - process incoming data packet
+ * @ifa: Interface packet was received on.
* @pkt: Pointer to the packet data
* @len: Length of received packet
* @saddr: Address of packet sender
- * @ifa: Interface packet was received on.
+ * @sport: Packet source port
+ * @daddr: Destination address of packet
+ * @dport: Packet destination port
*
* This function is the main processing hook of incoming Babel packets. It
* checks that the packet header is well-formed, then processes the TLVs
@@ -1344,14 +1691,17 @@ babel_enqueue(union babel_msg *msg, struct babel_iface *ifa)
* order.
*/
static void
-babel_process_packet(struct babel_pkt_header *pkt, int len,
- ip_addr saddr, struct babel_iface *ifa)
+babel_process_packet(struct babel_iface *ifa,
+ struct babel_pkt_header *pkt, int len,
+ ip_addr saddr, u16 sport,
+ ip_addr daddr, u16 dport)
{
struct babel_proto *p = ifa->proto;
struct babel_tlv *tlv;
struct babel_msg_node *msg;
list msgs;
int res;
+ int hmac_ok = 1;
int plen = sizeof(struct babel_pkt_header) + get_u16(&pkt->length);
byte *end = (byte *)pkt + plen;
@@ -1362,6 +1712,7 @@ babel_process_packet(struct babel_pkt_header *pkt, int len,
.saddr = saddr,
.next_hop_ip6 = saddr,
.sadr_enabled = babel_sadr_enabled(p),
+ .is_unicast = !(ipa_classify(daddr) & IADDR_MULTICAST),
};
if ((pkt->magic != BABEL_MAGIC) || (pkt->version != BABEL_VERSION))
@@ -1381,6 +1732,11 @@ babel_process_packet(struct babel_pkt_header *pkt, int len,
TRACE(D_PACKETS, "Packet received from %I via %s",
saddr, ifa->iface->name);
+ /* First HMAC check - verify signature */
+ if (ifa->cf->auth_type == BABEL_AUTH_HMAC &&
+ babel_hmac_check(ifa, pkt, saddr, sport, daddr, dport, end, len-plen))
+ return;
+
init_list(&msgs);
/* First pass through the packet TLV by TLV, parsing each into internal data
@@ -1410,10 +1766,14 @@ babel_process_packet(struct babel_pkt_header *pkt, int len,
frame_err:
+ /* Second HMAC check - verify sequence counter state */
+ if (ifa->cf->auth_type == BABEL_AUTH_HMAC)
+ hmac_ok = babel_hmac_check_state(ifa, &state);
+
/* Parsing done, handle all parsed TLVs */
WALK_LIST_FIRST(msg, msgs)
{
- if (tlv_data[msg->msg.type].handle_tlv)
+ if (hmac_ok && tlv_data[msg->msg.type].handle_tlv)
tlv_data[msg->msg.type].handle_tlv(&msg->msg, ifa);
rem_node(NODE msg);
sl_free(p->msg_slab, msg);
@@ -1473,7 +1833,10 @@ babel_rx_hook(sock *sk, uint len)
if (sk->flags & SKF_TRUNCATED)
DROP("truncated", len);
- babel_process_packet((struct babel_pkt_header *) sk->rbuf, len, sk->faddr, ifa);
+ babel_process_packet(ifa,
+ (struct babel_pkt_header *) sk->rbuf, len,
+ sk->faddr, sk->fport,
+ sk->laddr, sk->dport);
return 1;
drop:
More information about the Bird-users
mailing list