From: Juergen Perlinger Date: Fri, 29 Dec 2017 09:53:55 +0000 (+0100) Subject: implement mask/prefix match on addresses for key lookup X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=97b9819a1ab7d3f7beb811c2db6b4dc4115ac90d;p=thirdparty%2Fntp.git implement mask/prefix match on addresses for key lookup bk: 5a4610b3scdgopmvRgyumDjRNTQKmg --- diff --git a/include/ntp_keyacc.h b/include/ntp_keyacc.h index aaed411c3..f497b62c5 100644 --- a/include/ntp_keyacc.h +++ b/include/ntp_keyacc.h @@ -8,14 +8,18 @@ typedef struct keyaccess KeyAccT; struct keyaccess { KeyAccT * next; sockaddr_u addr; - int subnetbits; + unsigned int subnetbits; }; extern KeyAccT* keyacc_new_push(KeyAccT *head, const sockaddr_u *addr, - int subnetbits); + unsigned int subnetbits); extern KeyAccT* keyacc_pop_free(KeyAccT *head); extern KeyAccT* keyacc_all_free(KeyAccT *head); extern int keyacc_contains(const KeyAccT *head, const sockaddr_u *addr, int res_on_empty_list); +/* public for testability: */ +extern int keyacc_amatch(const sockaddr_u *,const sockaddr_u *, + unsigned int mbits); + #endif /* NTP_KEYACC_H */ diff --git a/libntp/authkeys.c b/libntp/authkeys.c index ce9b82021..294e049a7 100644 --- a/libntp/authkeys.c +++ b/libntp/authkeys.c @@ -115,7 +115,7 @@ KeyAccT* keyacc_new_push( KeyAccT * head, const sockaddr_u * addr, - int subnetbits + unsigned int subnetbits ) { KeyAccT * node = emalloc(sizeof(KeyAccT)); @@ -168,7 +168,8 @@ keyacc_contains( { if (head) { do { - if (SOCK_EQ(&head->addr, addr)) + if (keyacc_amatch(&head->addr, addr, + head->subnetbits)) return TRUE; } while (NULL != (head = head->next)); return FALSE; @@ -177,6 +178,72 @@ keyacc_contains( } } +#if CHAR_BIT != 8 +# error "don't know how to handle bytes with that bit size" +#endif + +/* ----------------------------------------------------------------- */ +/* check two addresses for a match, taking a prefix length into account + * when doing the compare. + * + * The ISC lib contains a similar function with not entirely specified + * semantics, so it seemed somewhat cleaner to do this from scratch. + * + * Note: It *is* assumed that the addresses are stored in network byte + * order, that is, most significant byte first! + */ +int/*BOOL*/ +keyacc_amatch( + const sockaddr_u * a1, + const sockaddr_u * a2, + unsigned int mbits + ) +{ + const uint8_t * pm1; + const uint8_t * pm2; + uint8_t msk; + unsigned int len; + + if (AF(a1) != AF(a2)) + return FALSE; + + switch (AF(a1)) { + case AF_INET: + /* IPv4 is easy: clamp size, get byte pointers */ + if (mbits > sizeof(NSRCADR(a1)) * 8) + mbits = sizeof(NSRCADR(a1)) * 8; + pm1 = (const void*)&NSRCADR(a1); + pm2 = (const void*)&NSRCADR(a2); + break; + + case AF_INET6: + /* IPv6 is slightly different: Both scopes must match, + * too, before we even consider doing a match! + */ + if ( ! SCOPE_EQ(a1, a2)) + return FALSE; + if (mbits > sizeof(NSRCADR6(a1)) * 8) + mbits = sizeof(NSRCADR6(a1)) * 8; + pm1 = (const void*)&NSRCADR6(a1); + pm2 = (const void*)&NSRCADR6(a2); + break; + + default: + /* don't know how to compare that!?! */ + return FALSE; + } + + /* split bit length into byte length and partial byte mask */ + msk = 0xFFu ^ (0xFFu >> (mbits & 7)); + len = mbits >> 3; + + if (len && memcmp(pm1, pm2, len)) + return FALSE; + if (msk && ((pm1[len] ^ pm2[len]) & msk)) + return FALSE; + + return TRUE; +} /* * init_auth - initialize internal data @@ -319,6 +386,10 @@ auth_log2(size_t x) return (u_short)r; } +int/*BOOL*/ +ipaddr_match_masked(const sockaddr_u *,const sockaddr_u *, + unsigned int mbits); + static void authcache_flush_id( keyid_t id diff --git a/libntp/authreadkeys.c b/libntp/authreadkeys.c index f591d4304..bd98ab21a 100644 --- a/libntp/authreadkeys.c +++ b/libntp/authreadkeys.c @@ -303,7 +303,7 @@ authreadkeys( while (tp) { char *i; char *snp; /* subnet text pointer */ - int snbits; + unsigned int snbits; sockaddr_u addr; i = strchr(tp, (int)','); @@ -312,21 +312,18 @@ authreadkeys( } snp = strchr(tp, (int)'/'); if (snp) { - unsigned u; char *sp; *snp++ = '\0'; - snbits = -1; - u = 0; + snbits = 0; sp = snp; while (*sp != '\0') { if (!isdigit((unsigned char)*sp)) break; - if (u > 1000) + if (snbits > 1000) break; /* overflow */ - u = (u << 3) + (u << 1); - u += *sp++ - '0'; /* ascii dependent */ + snbits = 10 * snbits + (*sp++ - '0'); /* ascii dependent */ } if (*sp != '\0') { log_maybe(&nerr, @@ -335,23 +332,20 @@ authreadkeys( goto nextip; } } else { - snbits = -1; + snbits = UINT_MAX; } if (is_ip_address(tp, AF_UNSPEC, &addr)) { /* Make sure that snbits is valid for addr */ - if ( snbits == -1 - || (snbits >= 0 && - ( (IS_IPV4(&addr) && snbits <= 32) - || (IS_IPV6(&addr) && snbits <= 128)))) { - next->keyacclist = keyacc_new_push( - next->keyacclist, &addr, snbits); - } else { - - log_maybe(&nerr, - "authreadkeys: invalid IP address/subnet <%s/%s> for key %d", + if ((snbits < UINT_MAX) && + ( (IS_IPV4(&addr) && snbits > 32) || + (IS_IPV6(&addr) && snbits > 128))) { + log_maybe(NULL, + "authreadkeys: excessive subnet mask <%s/%s> for key %d", tp, snp, keyno); - } + } + next->keyacclist = keyacc_new_push( + next->keyacclist, &addr, snbits); } else { log_maybe(&nerr, "authreadkeys: invalid IP address <%s> for key %d", diff --git a/libntp/ssl_init.c b/libntp/ssl_init.c index 0279bfeb9..fa1f94978 100644 --- a/libntp/ssl_init.c +++ b/libntp/ssl_init.c @@ -148,8 +148,7 @@ keytype_from_text( if (NULL != pdigest_len) { #ifdef OPENSSL - const EVP_MD * md = EVP_get_digestbynid(key_type); - + md = EVP_get_digestbynid(key_type); digest_len = (md) ? EVP_MD_size(md) : 0; if (!md || digest_len <= 0) {