summaryrefslogtreecommitdiff
path: root/proto/ospf/packet.c
diff options
context:
space:
mode:
authorOndrej Zajicek (work) <santiago@crfreenet.org>2018-04-25 15:50:57 +0200
committerOndrej Zajicek (work) <santiago@crfreenet.org>2018-04-25 15:54:53 +0200
commit4727d1db9d83a8f1025481cbcc06a7e4c8ec9f33 (patch)
tree8b0778f352da5135c6dc75d0389e0270202537de /proto/ospf/packet.c
parentf3a8cf050e6181e158dcde2fe885d7bf220eedc3 (diff)
OSPF: Support of authentication trailer for OSPFv3
Implement RFC 7166, crypthographic authentication for OSPFv3 analogous to authentication used for OSPFv2.
Diffstat (limited to 'proto/ospf/packet.c')
-rw-r--r--proto/ospf/packet.c212
1 files changed, 190 insertions, 22 deletions
diff --git a/proto/ospf/packet.c b/proto/ospf/packet.c
index 38d7a75f..db387661 100644
--- a/proto/ospf/packet.c
+++ b/proto/ospf/packet.c
@@ -29,23 +29,24 @@ ospf_pkt_fill_hdr(struct ospf_iface *ifa, void *buf, u8 h_type)
pkt->areaid = htonl(ifa->oa->areaid);
pkt->checksum = 0;
pkt->instance_id = ifa->instance_id;
- pkt->autype = ifa->autype;
+ pkt->autype = ospf_is_v2(p) ? ifa->autype : 0;
}
/* We assume OSPFv2 in ospf_pkt_finalize() */
static void
-ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt, uint *plen)
+ospf_pkt_finalize2(struct ospf_iface *ifa, struct ospf_packet *pkt, uint *plen)
{
+ struct ospf_proto *p = ifa->oa->po;
struct password_item *pass = NULL;
- union ospf_auth *auth = (void *) (pkt + 1);
-
- pkt->checksum = 0;
- pkt->autype = ifa->autype;
- bzero(auth, sizeof(union ospf_auth));
+ union ospf_auth2 *auth = (void *) (pkt + 1);
+ memset(auth, 0, sizeof(union ospf_auth2));
/* Compatibility note: auth may contain anything if autype is
none, but nonzero values do not work with Mikrotik OSPF */
+ pkt->checksum = 0;
+ pkt->autype = ifa->autype;
+
switch (ifa->autype)
{
case OSPF_AUTH_SIMPLE:
@@ -60,7 +61,7 @@ ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt, uint *plen)
case OSPF_AUTH_NONE:
{
void *body = (void *) (auth + 1);
- uint blen = *plen - sizeof(struct ospf_packet) - sizeof(union ospf_auth);
+ uint blen = *plen - sizeof(struct ospf_packet) - sizeof(union ospf_auth2);
pkt->checksum = ipsum_calculate(pkt, sizeof(struct ospf_packet), body, blen, NULL);
}
break;
@@ -69,7 +70,7 @@ ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt, uint *plen)
pass = password_find(ifa->passwords, 0);
if (!pass)
{
- log(L_ERR "No suitable password found for authentication");
+ log(L_ERR "%s: No suitable password found for authentication", p->p.name);
return;
}
@@ -105,8 +106,7 @@ ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt, uint *plen)
else
memset32(auth_tail, HMAC_MAGIC, auth_len / 4);
- mac_fill(pass->alg, pass->password, pass->length,
- (byte *) pkt, *plen, auth_tail);
+ mac_fill(pass->alg, pass->password, pass->length, (byte *) pkt, *plen, auth_tail);
break;
default:
@@ -114,13 +114,63 @@ ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt, uint *plen)
}
}
+/*
+ * Return an extra packet size that should be added to a final packet size
+ */
+static void
+ospf_pkt_finalize3(struct ospf_iface *ifa, struct ospf_packet *pkt, uint *plen, ip_addr src)
+{
+ struct ospf_proto *p = ifa->oa->po;
+ struct ospf_auth3 *auth = (void *) ((byte *) pkt + *plen);
+
+ pkt->checksum = 0;
+ pkt->autype = 0;
+
+ if (ifa->autype != OSPF_AUTH_CRYPT)
+ return;
+
+ struct password_item *pass = password_find(ifa->passwords, 0);
+ if (!pass)
+ {
+ log(L_ERR "%s: No suitable password found for authentication", p->p.name);
+ return;
+ }
+
+ /* FIXME: Ensure persistence */
+ p->csn64++;
+
+ uint mac_len = mac_type_length(pass->alg);
+ uint auth_len = sizeof(struct ospf_auth3) + mac_len;
+ *plen += auth_len;
+
+ ASSERT(*plen < ifa->sk->tbsize);
+
+ memset(auth, 0, sizeof(struct ospf_auth3));
+ auth->type = htons(OSPF3_AUTH_HMAC);
+ auth->length = htons(auth_len);
+ auth->reserved = 0;
+ auth->sa_id = htons(pass->id);
+ put_u64(&auth->csn, p->csn64);
+
+ /* Initialize with src IP address padded with HMAC_MAGIC */
+ put_ip6(auth->data, ipa_to_ip6(src));
+ memset32(auth->data + 16, HMAC_MAGIC, (mac_len - 16) / 4);
+
+ /* Attach OSPFv3 Cryptographic Protocol ID to the key */
+ uint pass_len = pass->length + 2;
+ byte *pass_key = alloca(pass_len);
+ memcpy(pass_key, pass->password, pass->length);
+ put_u16(pass_key + pass->length, OSPF3_CRYPTO_ID);
+
+ mac_fill(pass->alg, pass_key, pass_len, (byte *) pkt, *plen, auth->data);
+}
+
-/* We assume OSPFv2 in ospf_pkt_checkauth() */
static int
-ospf_pkt_checkauth(struct ospf_neighbor *n, struct ospf_iface *ifa, struct ospf_packet *pkt, uint len)
+ospf_pkt_checkauth2(struct ospf_neighbor *n, struct ospf_iface *ifa, struct ospf_packet *pkt, uint len)
{
struct ospf_proto *p = ifa->oa->po;
- union ospf_auth *auth = (void *) (pkt + 1);
+ union ospf_auth2 *auth = (void *) (pkt + 1);
struct password_item *pass = NULL;
const char *err_dsc = NULL;
uint err_val = 0;
@@ -200,6 +250,119 @@ drop:
return 0;
}
+static int
+ospf_pkt_checkauth3(struct ospf_neighbor *n, struct ospf_iface *ifa, struct ospf_packet *pkt, uint len, ip_addr src)
+{
+ struct ospf_proto *p = ifa->oa->po;
+ const char *err_dsc = NULL;
+ uint err_val = 0;
+
+ uint plen = ntohs(pkt->length);
+ uint opts, lls_present, auth_present;
+
+ /*
+ * When autentication is not enabled, ignore the trailer. This is different
+ * from OSPFv2, but it is necessary in order to support migration modes. Note
+ * that regular authenticated packets do not have valid checksum and will be
+ * dropped by OS on non-authenticated ifaces.
+ */
+ if (ifa->autype != OSPF_AUTH_CRYPT)
+ return 1;
+
+ switch(pkt->type)
+ {
+ case HELLO_P:
+ opts = ospf_hello3_options(pkt);
+ lls_present = opts & OPT_L_V3;
+ auth_present = opts & OPT_AT;
+ break;
+
+ case DBDES_P:
+ opts = ospf_dbdes3_options(pkt);
+ lls_present = opts & OPT_L_V3;
+ auth_present = opts & OPT_AT;
+ break;
+
+ default:
+ lls_present = 0;
+ auth_present = n->options & OPT_AT;
+ }
+
+ if (!auth_present)
+ DROP1("missing authentication trailer");
+
+ if (lls_present)
+ {
+ if ((plen + sizeof(struct ospf_lls)) > len)
+ DROP("packet length mismatch", len);
+
+ struct ospf_lls *lls = (void *) ((byte *) pkt + plen);
+ plen += ntohs(lls->length);
+ }
+
+ if ((plen + sizeof(struct ospf_auth3)) > len)
+ DROP("packet length mismatch", len);
+
+ struct ospf_auth3 *auth = (void *) ((byte *) pkt + plen);
+
+ uint rcv_auth_type = ntohs(auth->type);
+ if (rcv_auth_type != OSPF3_AUTH_HMAC)
+ DROP("authentication method mismatch", rcv_auth_type);
+
+ uint rcv_auth_len = ntohs(auth->length);
+ if (plen + rcv_auth_len > len)
+ DROP("packet length mismatch", len);
+
+ uint rcv_key_id = ntohs(auth->sa_id);
+ struct password_item *pass = password_find_by_id(ifa->passwords, rcv_key_id);
+ if (!pass)
+ DROP("no suitable password found", rcv_key_id);
+
+ uint mac_len = mac_type_length(pass->alg);
+ if (rcv_auth_len != (sizeof(struct ospf_auth3) + mac_len))
+ DROP("wrong authentication length", rcv_auth_len);
+
+ uint pt = pkt->type - 1;
+ u64 rcv_csn = get_u64(&auth->csn);
+ if (n && (rcv_csn <= n->csn64[pt]))
+ {
+ /* We want to report both new and old CSN */
+ LOG_PKT_AUTH("Authentication failed for nbr %R on %s - "
+ "lower sequence number (rcv %u, old %u)",
+ n->rid, ifa->ifname, (uint) rcv_csn, (uint) n->csn64[pt]);
+ return 0;
+ }
+
+ /* Save the received authentication data */
+ byte *auth_data = alloca(mac_len);
+ memcpy(auth_data, auth->data, mac_len);
+
+ /* Initialize with src IP address padded with HMAC_MAGIC */
+ put_ip6(auth->data, ipa_to_ip6(src));
+ memset32(auth->data + 16, HMAC_MAGIC, (mac_len - 16) / 4);
+
+ /* Attach OSPFv3 Cryptographic Protocol ID to the key */
+ uint pass_len = pass->length + 2;
+ byte *pass_key = alloca(pass_len);
+ memcpy(pass_key, pass->password, pass->length);
+ put_u16(pass_key + pass->length, OSPF3_CRYPTO_ID);
+
+ if (!mac_verify(pass->alg, pass_key, pass_len,
+ (byte *) pkt, plen + rcv_auth_len, auth_data))
+ DROP("wrong authentication code", pass->id);
+
+ if (n)
+ n->csn64[pt] = rcv_csn;
+
+ return 1;
+
+drop:
+ LOG_PKT_AUTH("Authentication failed for nbr %R on %s - %s (%u)",
+ (n ? n->rid : ntohl(pkt->routerid)), ifa->ifname, err_dsc, err_val);
+
+ return 0;
+}
+
/**
* ospf_rx_hook
* @sk: socket we received the packet.
@@ -298,12 +461,12 @@ ospf_rx_hook(sock *sk, uint len)
if (ospf_is_v2(p) && (pkt->autype != OSPF_AUTH_CRYPT))
{
- uint hlen = sizeof(struct ospf_packet) + sizeof(union ospf_auth);
+ uint hlen = sizeof(struct ospf_packet) + sizeof(union ospf_auth2);
uint blen = plen - hlen;
void *body = ((void *) pkt) + hlen;
if (!ipsum_verify(pkt, sizeof(struct ospf_packet), body, blen, NULL))
- DROP1("invalid checksum");
+ DROP("invalid checksum", ntohs(pkt->checksum));
}
/* Third, we resolve associated iface and handle vlinks. */
@@ -401,8 +564,14 @@ found:
return 1;
}
+ /* Check packet type here, ospf_pkt_checkauth3() expects valid values */
+ if (pkt->type < HELLO_P || pkt->type > LSACK_P)
+ DROP("invalid packet type", pkt->type);
+
/* ospf_pkt_checkauth() has its own error logging */
- if (ospf_is_v2(p) && !ospf_pkt_checkauth(n, ifa, pkt, len))
+ if ((ospf_is_v2(p) ?
+ !ospf_pkt_checkauth2(n, ifa, pkt, len) :
+ !ospf_pkt_checkauth3(n, ifa, pkt, len, sk->faddr)))
return 1;
switch (pkt->type)
@@ -426,9 +595,6 @@ found:
case LSACK_P:
ospf_receive_lsack(pkt, ifa, n);
break;
-
- default:
- DROP("invalid packet type", pkt->type);
};
return 1;
@@ -452,7 +618,7 @@ ospf_tx_hook(sock * sk)
void
ospf_err_hook(sock * sk, int err)
{
- struct ospf_iface *ifa= (struct ospf_iface *) (sk->data);
+ struct ospf_iface *ifa = (struct ospf_iface *) (sk->data);
struct ospf_proto *p = ifa->oa->po;
log(L_ERR "%s: Socket error on %s: %M", p->p.name, ifa->ifname, err);
}
@@ -472,7 +638,9 @@ ospf_send_to(struct ospf_iface *ifa, ip_addr dst)
uint plen = ntohs(pkt->length);
if (ospf_is_v2(ifa->oa->po))
- ospf_pkt_finalize(ifa, pkt, &plen);
+ ospf_pkt_finalize2(ifa, pkt, &plen);
+ else
+ ospf_pkt_finalize3(ifa, pkt, &plen, sk->saddr);
int done = sk_send_to(sk, plen, dst, 0);
if (!done)