diff options
Diffstat (limited to 'proto/rip')
-rw-r--r-- | proto/rip/config.Y | 24 | ||||
-rw-r--r-- | proto/rip/packets.c | 77 | ||||
-rw-r--r-- | proto/rip/rip.c | 11 | ||||
-rw-r--r-- | proto/rip/rip.h | 2 |
4 files changed, 75 insertions, 39 deletions
diff --git a/proto/rip/config.Y b/proto/rip/config.Y index 61a2a101..b3ccdf39 100644 --- a/proto/rip/config.Y +++ b/proto/rip/config.Y @@ -104,15 +104,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); @@ -153,7 +167,7 @@ rip_auth: NONE { $$ = RIP_AUTH_NONE; } | PLAINTEXT { $$ = RIP_AUTH_PLAIN; } | CRYPTOGRAPHIC { $$ = RIP_AUTH_CRYPTO; } - | MD5 { $$ = RIP_AUTH_CRYPTO; } + | MD5 { $$ = RIP_AUTH_CRYPTO; } /* For backward compatibility */ ; rip_iface_opts: diff --git a/proto/rip/packets.c b/proto/rip/packets.c index f89bb178..9dc492b7 100644 --- a/proto/rip/packets.c +++ b/proto/rip/packets.c @@ -12,16 +12,14 @@ #undef LOCAL_DEBUG #include "rip.h" -#include "lib/md5.h" +#include "lib/mac.h" #define RIP_CMD_REQUEST 1 /* want info */ #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 @@ -74,7 +72,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 */ @@ -132,7 +130,7 @@ rip_put_block(struct rip_proto *p, byte *pos, struct rip_block *rte) } static inline void -rip_put_next_hop(struct rip_proto *p, byte *pos, struct rip_block *rte) +rip_put_next_hop(struct rip_proto *p UNUSED, byte *pos, struct rip_block *rte) { struct rip_block_ng *block = (void *) pos; block->prefix = ip6_hton(ipa_to_ip6(rte->next_hop)); @@ -221,16 +219,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 +244,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 md5_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 +298,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 +334,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 md5_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; @@ -632,7 +655,7 @@ rip_receive_response(struct rip_proto *p, struct rip_iface *ifa, struct rip_pack } static int -rip_rx_hook(sock *sk, int len) +rip_rx_hook(sock *sk, uint len) { struct rip_iface *ifa = sk->data; struct rip_proto *p = ifa->rip; diff --git a/proto/rip/rip.c b/proto/rip/rip.c index 74d472c9..d87a078c 100644 --- a/proto/rip/rip.c +++ b/proto/rip/rip.c @@ -575,7 +575,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 @@ -687,12 +687,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); @@ -1011,7 +1010,7 @@ rip_prepare_attrs(struct linpool *pool, ea_list *next, u8 metric, u16 tag) } static int -rip_import_control(struct proto *P, struct rte **rt, struct ea_list **attrs, struct linpool *pool) +rip_import_control(struct proto *P UNUSED, struct rte **rt, struct ea_list **attrs, struct linpool *pool) { /* Prepare attributes with initial values */ if ((*rt)->attrs->source != RTS_RIP) @@ -1145,7 +1144,7 @@ rip_reconfigure(struct proto *P, struct proto_config *CF) } static void -rip_get_route_info(rte *rte, byte *buf, ea_list *attrs) +rip_get_route_info(rte *rte, byte *buf, ea_list *attrs UNUSED) { buf += bsprintf(buf, " (%d/%d)", rte->pref, rte->u.rip.metric); diff --git a/proto/rip/rip.h b/proto/rip/rip.h index 7ec7e24d..03dc9138 100644 --- a/proto/rip/rip.h +++ b/proto/rip/rip.h @@ -34,7 +34,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 |