RIP: Use message authentication interface

Based on former commit from Pavel Tvrdik
This commit is contained in:
Ondrej Zajicek (work) 2016-10-26 16:07:45 +02:00
parent 64385aee0c
commit 390601f038
8 changed files with 100 additions and 34 deletions

View file

@ -21,6 +21,7 @@
#define ALG_SHA256 0x04 #define ALG_SHA256 0x04
#define ALG_SHA384 0x05 #define ALG_SHA384 0x05
#define ALG_SHA512 0x06 #define ALG_SHA512 0x06
#define ALG_HMAC 0x10
#define ALG_HMAC_MD5 0x11 #define ALG_HMAC_MD5 0x11
#define ALG_HMAC_SHA1 0x12 #define ALG_HMAC_SHA1 0x12
#define ALG_HMAC_SHA224 0x13 #define ALG_HMAC_SHA224 0x13
@ -34,6 +35,9 @@
#define HASH_STORAGE sizeof(struct sha512_context) #define HASH_STORAGE sizeof(struct sha512_context)
#define MAC_STORAGE sizeof(struct hmac_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 */ /* Generic context used by hash functions */
struct hash_context struct hash_context
{ {

View file

@ -39,6 +39,16 @@ xstrdup(const char *c)
return z; 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 #define ROUTER_ID_64_LENGTH 23
#endif #endif

View file

@ -10,6 +10,7 @@
#include "nest/bird.h" #include "nest/bird.h"
#include "nest/password.h" #include "nest/password.h"
#include "lib/string.h" #include "lib/string.h"
#include "lib/mac.h"
struct password_item *last_password_item = NULL; struct password_item *last_password_item = NULL;
@ -66,3 +67,17 @@ password_find_by_value(list *l, char *pass, uint size)
return NULL; 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;
}

View file

@ -34,4 +34,6 @@ static inline int password_verify(struct password_item *p1, char *p2, uint size)
return !memcmp(buf, p2, size); return !memcmp(buf, p2, size);
} }
uint max_mac_length(list *l);
#endif #endif

View file

@ -98,15 +98,29 @@ rip_iface_start:
rip_iface_finish: 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(); RIP_IFACE->passwords = get_passwords();
if (!RIP_IFACE->auth_type != !RIP_IFACE->passwords) if (!RIP_IFACE->auth_type != !RIP_IFACE->passwords)
log(L_WARN "Authentication and password options should be used together"); 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->passwords)
if (!RIP_IFACE->mode) {
RIP_IFACE->mode = (rip_cfg_is_v2() && (RIP_IFACE->version == RIP_V1)) ? struct password_item *pass;
RIP_IM_BROADCAST : RIP_IM_MULTICAST; 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->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); RIP_CFG->max_garbage_time = MAX_(RIP_CFG->max_garbage_time, RIP_IFACE->garbage_time);

View file

@ -10,7 +10,6 @@
*/ */
#include "rip.h" #include "rip.h"
#include "lib/md5.h"
#include "lib/mac.h" #include "lib/mac.h"
@ -18,9 +17,7 @@
#define RIP_CMD_RESPONSE 2 /* responding to request */ #define RIP_CMD_RESPONSE 2 /* responding to request */
#define RIP_BLOCK_LENGTH 20 #define RIP_BLOCK_LENGTH 20
#define RIP_PASSWD_LENGTH 16 #define RIP_PASSWD_LENGTH 16
#define RIP_MD5_LENGTH 16
#define RIP_AF_IPV4 2 #define RIP_AF_IPV4 2
#define RIP_AF_AUTH 0xffff #define RIP_AF_AUTH 0xffff
@ -73,7 +70,7 @@ struct rip_auth_tail
{ {
u16 must_be_ffff; u16 must_be_ffff;
u16 must_be_0001; u16 must_be_0001;
byte auth_data[]; byte auth_data[0];
}; };
/* Internal representation of RTE block data */ /* 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->auth_type = htons(RIP_AUTH_CRYPTO);
auth->packet_len = htons(*plen); auth->packet_len = htons(*plen);
auth->key_id = pass->id; 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->seq_num = ifa->csn_ready ? htonl(ifa->csn) : 0;
auth->unused1 = 0; auth->unused1 = 0;
auth->unused2 = 0; auth->unused2 = 0;
ifa->csn_ready = 1; 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 * Note that RFC 4822 is unclear whether auth_len should cover whole
* authentication trailer or just auth_data length. * 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(). * Crypto sequence numbers are increased by sender in rip_update_csn().
* First CSN should be zero, this is handled by csn_ready. * 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); struct rip_auth_tail *tail = (void *) ((byte *) pkt + *plen);
tail->must_be_ffff = htons(0xffff); tail->must_be_ffff = htons(0xffff);
tail->must_be_0001 = htons(0x0001); 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; /* Append key for keyed hash, append padding for HMAC (RFC 4822 2.5) */
md5_init(&ctx); if (pass->alg < ALG_HMAC)
md5_update(&ctx, (byte *) pkt, *plen); strncpy(tail->auth_data, pass->password, auth_len);
memcpy(tail->auth_data, md5_final(&ctx), RIP_MD5_LENGTH); 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; return;
default: 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); DROP("no suitable password found", auth->key_id);
uint data_len = ntohs(auth->packet_len); 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)) if (data_len + auth_len2 != *plen)
DROP("authentication data length mismatch", auth->auth_len); 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); struct rip_auth_tail *tail = (void *) ((byte *) pkt + data_len);
if ((tail->must_be_ffff != htons(0xffff)) || (tail->must_be_0001 != htons(0x0001))) 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; return 0;
} }
char received[RIP_MD5_LENGTH]; byte *auth_data = alloca(auth_len);
memcpy(received, tail->auth_data, RIP_MD5_LENGTH); memcpy(auth_data, tail->auth_data, auth_len);
strncpy(tail->auth_data, pass->password, RIP_MD5_LENGTH);
struct hash_context ctx; /* Append key for keyed hash, append padding for HMAC (RFC 4822 2.5) */
md5_init(&ctx); if (pass->alg < ALG_HMAC)
md5_update(&ctx, (byte *) pkt, *plen); strncpy(tail->auth_data, pass->password, auth_len);
char *computed = md5_final(&ctx); else
memset32(tail->auth_data, HMAC_MAGIC, auth_len / 4);
if (memcmp(received, computed, RIP_MD5_LENGTH)) if (!mac_verify(pass->alg, pass->password, pass->length,
DROP("wrong MD5 digest", pass->id); (byte *) pkt, *plen, auth_data))
DROP("wrong authentication code", pass->id);
*plen = data_len; *plen = data_len;
n->csn = rcv_csn; n->csn = rcv_csn;

View file

@ -590,7 +590,7 @@ rip_iface_update_buffers(struct rip_iface *ifa)
ifa->tx_plen = tbsize - headers; ifa->tx_plen = tbsize - headers;
if (ifa->cf->auth_type == RIP_AUTH_CRYPTO) 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 static inline void
@ -702,12 +702,11 @@ rip_reconfigure_iface(struct rip_proto *p, struct rip_iface *ifa, struct rip_ifa
ifa->cf = new; ifa->cf = new;
rip_iface_update_buffers(ifa);
if (ifa->next_regular > (now + new->update_time)) if (ifa->next_regular > (now + new->update_time))
ifa->next_regular = now + (random() % new->update_time) + 1; 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) if (new->check_link != old->check_link)
rip_iface_update_state(ifa); rip_iface_update_state(ifa);

View file

@ -40,7 +40,7 @@
#define RIP_NG_PORT 521 /* RIPng */ #define RIP_NG_PORT 521 /* RIPng */
#define RIP_MAX_PKT_LENGTH 532 /* 512 + IP4_HEADER_LENGTH */ #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_ECMP_LIMIT 16
#define RIP_DEFAULT_INFINITY 16 #define RIP_DEFAULT_INFINITY 16