diff options
Diffstat (limited to 'proto/ospf/packet.c')
-rw-r--r-- | proto/ospf/packet.c | 217 |
1 files changed, 174 insertions, 43 deletions
diff --git a/proto/ospf/packet.c b/proto/ospf/packet.c index 4f5db66..9334e59 100644 --- a/proto/ospf/packet.c +++ b/proto/ospf/packet.c @@ -1,15 +1,17 @@ /* * BIRD -- OSPF * - * (c) 1999 - 2003 Ondrej Filip <feela@network.cz> + * (c) 1999--2004 Ondrej Filip <feela@network.cz> * * Can be freely distributed and used under the terms of the GNU GPL. */ #include "ospf.h" +#include "nest/password.h" +#include "lib/md5.h" void -fill_ospf_pkt_hdr(struct ospf_iface *ifa, void *buf, u8 h_type) +ospf_pkt_fill_hdr(struct ospf_iface *ifa, void *buf, u8 h_type) { struct ospf_packet *pkt; struct proto *p; @@ -28,45 +30,167 @@ fill_ospf_pkt_hdr(struct ospf_iface *ifa, void *buf, u8 h_type) pkt->checksum = 0; } +unsigned +ospf_pkt_maxsize(struct ospf_iface *ifa) +{ + return ifa->iface->mtu - SIZE_OF_IP_HEADER - + ((ifa->autype == OSPF_AUTH_CRYPT) ? OSPF_AUTH_CRYPT_SIZE : 0); +} + void -ospf_tx_authenticate(struct ospf_iface *ifa, struct ospf_packet *pkt) +ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt) { + struct proto_ospf *po = ifa->proto; + struct proto *p = &po->proto; + struct password_item *passwd = password_find (ifa->passwords); + void *tail; + struct MD5Context ctxt; + char password[OSPF_AUTH_CRYPT_SIZE]; + pkt->autype = htons(ifa->autype); - memcpy(pkt->authetication, ifa->aukey, 8); - return; + + switch(ifa->autype) + { + case OSPF_AUTH_NONE: + case OSPF_AUTH_SIMPLE: + pkt->checksum = ipsum_calculate(pkt, sizeof(struct ospf_packet) - 8, + (pkt + 1), + ntohs(pkt->length) - + sizeof(struct ospf_packet), NULL); + password_cpy(pkt->u.password, passwd->password, 8); + break; + case OSPF_AUTH_CRYPT: + if (!passwd) + { + log( L_ERR "No suitable password found for authentication" ); + return; + } + + pkt->checksum = 0; + + if (!ifa->csn) + ifa->csn = (u32) time(NULL); + + pkt->u.md5.keyid = passwd->id; + pkt->u.md5.len = OSPF_AUTH_CRYPT_SIZE; + pkt->u.md5.zero = 0; + pkt->u.md5.csn = htonl(ifa->csn++); + tail = ((void *)pkt) + ntohs(pkt->length); + MD5Init(&ctxt); + MD5Update(&ctxt, (char *) pkt, ntohs(pkt->length)); + password_cpy(password, passwd->password, OSPF_AUTH_CRYPT_SIZE); + MD5Update(&ctxt, password, OSPF_AUTH_CRYPT_SIZE); + MD5Final(tail, &ctxt); + + break; + default: + bug("Unknown authentication type"); + } } static int -ospf_rx_authenticate(struct ospf_iface *ifa, struct ospf_packet *pkt) +ospf_pkt_checkauth(struct ospf_neighbor *n, struct ospf_packet *pkt, int size) { int i; + struct ospf_iface *ifa = n->ifa; + struct proto_ospf *po = ifa->proto; + struct proto *p = &po->proto; + struct password_item *pass = NULL, *ptmp; + void *tail; + char md5sum[OSPF_AUTH_CRYPT_SIZE]; + char password[OSPF_AUTH_CRYPT_SIZE]; + struct MD5Context ctxt; + + if (pkt->autype != htons(ifa->autype)) - return 0; - if (ifa->autype == AU_NONE) - return 1; - if (ifa->autype == AU_SIMPLE) { - for (i = 0; i < 8; i++) - { - if (pkt->authetication[i] != ifa->aukey[i]) - return 0; - } - return 1; + OSPF_TRACE(D_PACKETS, "OSPF_auth: Method differs (%d)", ntohs(pkt->autype)); + return 0; } - return 0; -} -void -ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt) -{ - - ospf_tx_authenticate(ifa, pkt); + switch(ifa->autype) + { + case OSPF_AUTH_NONE: + return 1; + break; + case OSPF_AUTH_SIMPLE: + pass = password_find (ifa->passwords); + if(!pass) + { + OSPF_TRACE(D_PACKETS, "OSPF_auth: no password found"); + return 0; + } - /* Count checksum */ - pkt->checksum = ipsum_calculate(pkt, sizeof(struct ospf_packet) - 8, - (pkt + 1), - ntohs(pkt->length) - - sizeof(struct ospf_packet), NULL); + if (memcmp(pkt->u.password,pass, 8)) + { + OSPF_TRACE(D_PACKETS, "OSPF_auth: different passwords"); + return 0; + } + + else + return 1; + break; + case OSPF_AUTH_CRYPT: + if (pkt->u.md5.len != OSPF_AUTH_CRYPT_SIZE) + { + OSPF_TRACE(D_PACKETS, "OSPF_auth: wrong size of md5 digest"); + return 0; + } + if (ntohs(pkt->length) + OSPF_AUTH_CRYPT_SIZE != size) + { + OSPF_TRACE(D_PACKETS, "OSPF_auth: size mismatch"); + return 0; + } + + if (pkt->u.md5.zero) + { + OSPF_TRACE(D_PACKETS, "OSPF_auth: \"zero\" area is non-zero"); + return 0; + } + + tail = ((void *)pkt) + ntohs(pkt->length); + + WALK_LIST(ptmp, *(ifa->passwords)) + { + if (pkt->u.md5.keyid != pass->id) continue; + if ((pass->genfrom > now) || (pass->gento < now)) continue; + pass = ptmp; + break; + } + + if(!pass) + { + OSPF_TRACE(D_PACKETS, "OSPF_auth: no suitable md5 password found"); + return 0; + } + + if(n) + { + if(ntohs(pkt->u.md5.csn) <= n->csn) + { + OSPF_TRACE(D_PACKETS, "OSPF_auth: lower sequence number"); + return 0; + } + + n->csn = ntohs(pkt->u.md5.csn); + } + + MD5Init(&ctxt); + MD5Update(&ctxt, (char *) pkt, ntohs(pkt->length)); + password_cpy(password, pass->password, OSPF_AUTH_CRYPT_SIZE); + MD5Update(&ctxt, password, OSPF_AUTH_CRYPT_SIZE); + MD5Final(md5sum, &ctxt); + if (!memcmp(md5sum, tail, OSPF_AUTH_CRYPT_SIZE)) + { + OSPF_TRACE(D_PACKETS, "OSPF_auth: wrong md5 digest"); + return 0; + } + return 1; + break; + default: + OSPF_TRACE(D_PACKETS, "OSPF_auth: unknown auth type"); + return 0; + } } /** @@ -119,19 +243,13 @@ ospf_rx_hook(sock * sk, int size) return 1; } - if (!ipsum_verify(ps, 16, (void *) ps + sizeof(struct ospf_packet), - ntohs(ps->length) - sizeof(struct ospf_packet), NULL)) + if ((ifa->autype != OSPF_AUTH_CRYPT) && (!ipsum_verify(ps, 16, (void *) ps + sizeof(struct ospf_packet), + ntohs(ps->length) - sizeof(struct ospf_packet), NULL))) { log(L_ERR "%s%I - bad checksum", mesg, sk->faddr); return 1; } - if (!ospf_rx_authenticate(ifa, ps)) - { - log(L_ERR "%s%I - bad password", mesg, sk->faddr); - return 1; - } - if (ntohl(ps->areaid) != ifa->an) { log(L_ERR "%s%I - other area %ld", mesg, sk->faddr, ps->areaid); @@ -164,6 +282,12 @@ ospf_rx_hook(sock * sk, int size) return 1; } + if (!ospf_pkt_checkauth(n, ps, size)) + { + log(L_ERR "%s%I - authentification failed", mesg, sk->faddr); + return 1; + } + /* Dump packet pu8=(u8 *)(sk->rbuf+5*4); for(i=0;i<ntohs(ps->length);i+=4) @@ -226,26 +350,33 @@ ospf_err_hook(sock * sk, int err UNUSED) } void -ospf_send_to_agt(sock * sk, u16 len, struct ospf_iface *ifa, u8 state) +ospf_send_to_agt(sock * sk, struct ospf_iface *ifa, u8 state) { struct ospf_neighbor *n; WALK_LIST(NODE n, ifa->neigh_list) if (n->state >= state) - ospf_send_to(sk, len, n->ip); + ospf_send_to(sk, n->ip, ifa); } void -ospf_send_to_bdr(sock * sk, u16 len, struct ospf_iface *ifa) +ospf_send_to_bdr(sock * sk, struct ospf_iface *ifa) { if (ipa_compare(ifa->drip, ipa_from_u32(0)) != 0) - ospf_send_to(sk, len, ifa->drip); + ospf_send_to(sk, ifa->drip, ifa); if (ipa_compare(ifa->bdrip, ipa_from_u32(0)) != 0) - ospf_send_to(sk, len, ifa->bdrip); + ospf_send_to(sk, ifa->bdrip, ifa); } void -ospf_send_to(sock *sk, u16 len, ip_addr ip) +ospf_send_to(sock *sk, ip_addr ip, struct ospf_iface *ifa) { - sk_send_to(sk, len, ip, OSPF_PROTO); + struct ospf_packet *pkt = (struct ospf_packet *) sk->tbuf; + int len = ntohs(pkt->length) + (pkt->autype == OSPF_AUTH_CRYPT) ? OSPF_AUTH_CRYPT_SIZE : 0; + ospf_pkt_finalize(ifa, pkt); + + if (ipa_equal(ip, IPA_NONE)) + sk_send(sk, len); + else + sk_send_to(sk, len, ip, OSPF_PROTO); } |