]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
RIP: Use message authentication interface
authorOndrej Zajicek (work) <santiago@crfreenet.org>
Wed, 26 Oct 2016 14:07:45 +0000 (16:07 +0200)
committerOndrej Zajicek (work) <santiago@crfreenet.org>
Wed, 2 Nov 2016 16:53:22 +0000 (17:53 +0100)
Based on former commit from Pavel Tvrdik

lib/mac.h
lib/string.h
nest/password.c
nest/password.h
proto/rip/config.Y
proto/rip/packets.c
proto/rip/rip.c
proto/rip/rip.h

index 9dba8f899296d55e153d6054312b991951318f01..5fc216fd7b2dc238bce4abe70f4809f9b0ee31d2 100644 (file)
--- a/lib/mac.h
+++ b/lib/mac.h
@@ -21,6 +21,7 @@
 #define ALG_SHA256             0x04
 #define ALG_SHA384             0x05
 #define ALG_SHA512             0x06
+#define ALG_HMAC               0x10
 #define ALG_HMAC_MD5           0x11
 #define ALG_HMAC_SHA1          0x12
 #define ALG_HMAC_SHA224                0x13
@@ -34,6 +35,9 @@
 #define HASH_STORAGE           sizeof(struct sha512_context)
 #define MAC_STORAGE            sizeof(struct hmac_context)
 
+/* This value is used by several IETF protocols for padding */
+#define HMAC_MAGIC             htonl(0x878FE1F3)
+
 /* Generic context used by hash functions */
 struct hash_context
 {
index bf0b7cb07c64c97971205e785a5cea718c44cb0b..75cb88ddc82e0c1dddbc579c8adc06dc2b25bf37 100644 (file)
@@ -39,6 +39,16 @@ xstrdup(const char *c)
   return z;
 }
 
+static inline void
+memset32(void *D, u32 val, uint n)
+{
+  u32 *dst = D;
+  uint i;
+
+  for (i = 0; i < n; i++)
+    dst[i] = val;
+}
+
 #define ROUTER_ID_64_LENGTH 23
 
 #endif
index d6e2087f5272e6277f19a471bffb09a642673161..e48137413f8e5d31e88f0158a4b99746b18910e3 100644 (file)
@@ -10,6 +10,7 @@
 #include "nest/bird.h"
 #include "nest/password.h"
 #include "lib/string.h"
+#include "lib/mac.h"
 
 struct password_item *last_password_item = NULL;
 
@@ -66,3 +67,17 @@ password_find_by_value(list *l, char *pass, uint size)
   return NULL;
 }
 
+uint
+max_mac_length(list *l)
+{
+  struct password_item *pi;
+  uint val = 0;
+
+  if (!l)
+    return 0;
+
+  WALK_LIST(pi, *l)
+    val = MAX(val, mac_type_length(pi->alg));
+
+  return val;
+}
index 7392389b61db31c7f1d96b6b5b2f0a0b1b4d44e5..f21483c4dd2c0e77a7bf5f3e2632654db48d7141 100644 (file)
@@ -34,4 +34,6 @@ static inline int password_verify(struct password_item *p1, char *p2, uint size)
   return !memcmp(buf, p2, size);
 }
 
+uint max_mac_length(list *l);
+
 #endif
index e15599e0e5ee44fe5b4fdb247808de6bce6735b0..4ec45c7ab01c9a7760da47dc3cd1e8510ba61f58 100644 (file)
@@ -98,15 +98,29 @@ rip_iface_start:
 
 rip_iface_finish:
 {
+  /* Default mode is broadcast for RIPv1, multicast for RIPv2 and RIPng */
+  if (!RIP_IFACE->mode)
+    RIP_IFACE->mode = (rip_cfg_is_v2() && (RIP_IFACE->version == RIP_V1)) ?
+      RIP_IM_BROADCAST : RIP_IM_MULTICAST;
+
   RIP_IFACE->passwords = get_passwords();
 
   if (!RIP_IFACE->auth_type != !RIP_IFACE->passwords)
     log(L_WARN "Authentication and password options should be used together");
 
-  /* Default mode is broadcast for RIPv1, multicast for RIPv2 and RIPng */
-  if (!RIP_IFACE->mode)
-    RIP_IFACE->mode = (rip_cfg_is_v2() && (RIP_IFACE->version == RIP_V1)) ?
-      RIP_IM_BROADCAST : RIP_IM_MULTICAST;
+  if (RIP_IFACE->passwords)
+  {
+    struct password_item *pass;
+    WALK_LIST(pass, *RIP_IFACE->passwords)
+    {
+      if (pass->alg && (RIP_IFACE->auth_type != RIP_AUTH_CRYPTO))
+       cf_error("Password algorithm option requires cryptographic authentication");
+
+      /* Set default crypto algorithm (MD5) */
+      if (!pass->alg && (RIP_IFACE->auth_type == RIP_AUTH_CRYPTO))
+       pass->alg = ALG_MD5;
+    }
+  }
 
   RIP_CFG->min_timeout_time = MIN_(RIP_CFG->min_timeout_time, RIP_IFACE->timeout_time);
   RIP_CFG->max_garbage_time = MAX_(RIP_CFG->max_garbage_time, RIP_IFACE->garbage_time);
index 381b477176b7e914436dcfd0f2a61b8ac9f357ac..468927e6e24d4f7a074435d76d1fd67bba819207 100644 (file)
@@ -10,7 +10,6 @@
  */
 
 #include "rip.h"
-#include "lib/md5.h"
 #include "lib/mac.h"
 
 
@@ -18,9 +17,7 @@
 #define RIP_CMD_RESPONSE       2       /* responding to request */
 
 #define RIP_BLOCK_LENGTH       20
-
 #define RIP_PASSWD_LENGTH      16
-#define RIP_MD5_LENGTH         16
 
 #define RIP_AF_IPV4            2
 #define RIP_AF_AUTH            0xffff
@@ -73,7 +70,7 @@ struct rip_auth_tail
 {
   u16 must_be_ffff;
   u16 must_be_0001;
-  byte auth_data[];
+  byte auth_data[0];
 };
 
 /* Internal representation of RTE block data */
@@ -221,16 +218,24 @@ rip_fill_authentication(struct rip_proto *p, struct rip_iface *ifa, struct rip_p
     auth->auth_type = htons(RIP_AUTH_CRYPTO);
     auth->packet_len = htons(*plen);
     auth->key_id = pass->id;
-    auth->auth_len = sizeof(struct rip_auth_tail) + RIP_MD5_LENGTH;
+    auth->auth_len = mac_type_length(pass->alg);
     auth->seq_num = ifa->csn_ready ? htonl(ifa->csn) : 0;
     auth->unused1 = 0;
     auth->unused2 = 0;
     ifa->csn_ready = 1;
 
+    if (pass->alg < ALG_HMAC)
+      auth->auth_len += sizeof(struct rip_auth_tail);
+
     /*
      * Note that RFC 4822 is unclear whether auth_len should cover whole
      * authentication trailer or just auth_data length.
      *
+     * FIXME: We should use just auth_data length by default. Currently we put
+     * the whole auth trailer length in keyed hash case to keep old behavior,
+     * but we put just auth_data length in the new HMAC case. Note that Quagga
+     * has config option for this.
+     *
      * Crypto sequence numbers are increased by sender in rip_update_csn().
      * First CSN should be zero, this is handled by csn_ready.
      */
@@ -238,14 +243,18 @@ rip_fill_authentication(struct rip_proto *p, struct rip_iface *ifa, struct rip_p
     struct rip_auth_tail *tail = (void *) ((byte *) pkt + *plen);
     tail->must_be_ffff = htons(0xffff);
     tail->must_be_0001 = htons(0x0001);
-    strncpy(tail->auth_data, pass->password, RIP_MD5_LENGTH);
 
-    *plen += sizeof(struct rip_auth_tail) + RIP_MD5_LENGTH;
+    uint auth_len = mac_type_length(pass->alg);
+    *plen += sizeof(struct rip_auth_tail) + auth_len;
 
-    struct hash_context ctx;
-    md5_init(&ctx);
-    md5_update(&ctx, (byte *) pkt, *plen);
-    memcpy(tail->auth_data, md5_final(&ctx), RIP_MD5_LENGTH);
+    /* Append key for keyed hash, append padding for HMAC (RFC 4822 2.5) */
+    if (pass->alg < ALG_HMAC)
+      strncpy(tail->auth_data, pass->password, auth_len);
+    else
+      memset32(tail->auth_data, HMAC_MAGIC, auth_len / 4);
+
+    mac_fill(pass->alg, pass->password, pass->length,
+            (byte *) pkt, *plen, tail->auth_data);
     return;
 
   default:
@@ -288,13 +297,25 @@ rip_check_authentication(struct rip_proto *p, struct rip_iface *ifa, struct rip_
       DROP("no suitable password found", auth->key_id);
 
     uint data_len = ntohs(auth->packet_len);
-    uint auth_len = sizeof(struct rip_auth_tail) + RIP_MD5_LENGTH;
+    uint auth_len = mac_type_length(pass->alg);
+    uint auth_len2 = sizeof(struct rip_auth_tail) + auth_len;
 
-    if (data_len + auth_len != *plen)
-      DROP("packet length mismatch", data_len);
+    /*
+     * Ideally, first check should be check for internal consistency:
+     *   (data_len + sizeof(struct rip_auth_tail) + auth->auth_len) != *plen
+     *
+     * Second one should check expected code length:
+     *   auth->auth_len != auth_len
+     *
+     * But as auth->auth_len has two interpretations, we simplify this
+     */
 
-    if ((auth->auth_len != RIP_MD5_LENGTH) && (auth->auth_len != auth_len))
-      DROP("authentication data length mismatch", auth->auth_len);
+    if (data_len + auth_len2 != *plen)
+      DROP("packet length mismatch", *plen);
+
+    /* Warning: two interpretations of auth_len field */
+    if ((auth->auth_len != auth_len) && (auth->auth_len != auth_len2))
+      DROP("wrong authentication length", auth->auth_len);
 
     struct rip_auth_tail *tail = (void *) ((byte *) pkt + data_len);
     if ((tail->must_be_ffff != htons(0xffff)) || (tail->must_be_0001 != htons(0x0001)))
@@ -312,17 +333,18 @@ rip_check_authentication(struct rip_proto *p, struct rip_iface *ifa, struct rip_
       return 0;
     }
 
-    char received[RIP_MD5_LENGTH];
-    memcpy(received, tail->auth_data, RIP_MD5_LENGTH);
-    strncpy(tail->auth_data, pass->password, RIP_MD5_LENGTH);
+    byte *auth_data = alloca(auth_len);
+    memcpy(auth_data, tail->auth_data, auth_len);
 
-    struct hash_context ctx;
-    md5_init(&ctx);
-    md5_update(&ctx, (byte *) pkt, *plen);
-    char *computed = md5_final(&ctx);
+    /* Append key for keyed hash, append padding for HMAC (RFC 4822 2.5) */
+    if (pass->alg < ALG_HMAC)
+      strncpy(tail->auth_data, pass->password, auth_len);
+    else
+      memset32(tail->auth_data, HMAC_MAGIC, auth_len / 4);
 
-    if (memcmp(received, computed, RIP_MD5_LENGTH))
-      DROP("wrong MD5 digest", pass->id);
+    if (!mac_verify(pass->alg, pass->password, pass->length,
+                   (byte *) pkt, *plen, auth_data))
+      DROP("wrong authentication code", pass->id);
 
     *plen = data_len;
     n->csn = rcv_csn;
index 37cfa9ac6ada8608055649a9363ed504e0f8af23..7b3800975d0d682bea771ff7b70be18024d4f5e0 100644 (file)
@@ -590,7 +590,7 @@ rip_iface_update_buffers(struct rip_iface *ifa)
   ifa->tx_plen = tbsize - headers;
 
   if (ifa->cf->auth_type == RIP_AUTH_CRYPTO)
-    ifa->tx_plen -= RIP_AUTH_TAIL_LENGTH;
+    ifa->tx_plen -= RIP_AUTH_TAIL_LENGTH + max_mac_length(ifa->cf->passwords);
 }
 
 static inline void
@@ -702,12 +702,11 @@ rip_reconfigure_iface(struct rip_proto *p, struct rip_iface *ifa, struct rip_ifa
 
   ifa->cf = new;
 
+  rip_iface_update_buffers(ifa);
+
   if (ifa->next_regular > (now + new->update_time))
     ifa->next_regular = now + (random() % new->update_time) + 1;
 
-  if ((new->tx_length != old->tx_length) || (new->rx_buffer != old->rx_buffer))
-    rip_iface_update_buffers(ifa);
-
   if (new->check_link != old->check_link)
     rip_iface_update_state(ifa);
 
index f245e61260d4792c9e16e662ee8bf4fd3bba4d59..b24d9536d20cee87baec85eebbead6339979ecef 100644 (file)
@@ -40,7 +40,7 @@
 #define RIP_NG_PORT            521     /* RIPng */
 
 #define RIP_MAX_PKT_LENGTH     532     /* 512 + IP4_HEADER_LENGTH */
-#define RIP_AUTH_TAIL_LENGTH   20      /* 4 + MD5 length */
+#define RIP_AUTH_TAIL_LENGTH   4       /* Without auth_data */
 
 #define RIP_DEFAULT_ECMP_LIMIT 16
 #define RIP_DEFAULT_INFINITY   16