commit 4e2e1ea0908d7e6ad7f38ae04fdcdf2411f8b942 Author: Michal Sekletar Date: Wed Nov 27 18:07:32 2024 +0100 core/wide-area: fix for CVE-2024-52615 diff --git a/avahi-core/wide-area.c b/avahi-core/wide-area.c index 00a1505..06df7af 100644 --- a/avahi-core/wide-area.c +++ b/avahi-core/wide-area.c @@ -81,6 +81,10 @@ struct AvahiWideAreaLookup { AvahiAddress dns_server_used; + int fd; + AvahiWatch *watch; + AvahiProtocol proto; + AVAHI_LLIST_FIELDS(AvahiWideAreaLookup, lookups); AVAHI_LLIST_FIELDS(AvahiWideAreaLookup, by_key); }; @@ -88,9 +92,6 @@ struct AvahiWideAreaLookup { struct AvahiWideAreaLookupEngine { AvahiServer *server; - int fd_ipv4, fd_ipv6; - AvahiWatch *watch_ipv4, *watch_ipv6; - /* Cache */ AVAHI_LLIST_HEAD(AvahiWideAreaCacheEntry, cache); AvahiHashmap *cache_by_key; @@ -125,35 +126,67 @@ static AvahiWideAreaLookup* find_lookup(AvahiWideAreaLookupEngine *e, uint16_t i return l; } +static void socket_event(AVAHI_GCC_UNUSED AvahiWatch *w, int fd, AVAHI_GCC_UNUSED AvahiWatchEvent events, void *userdata); + static int send_to_dns_server(AvahiWideAreaLookup *l, AvahiDnsPacket *p) { + AvahiWideAreaLookupEngine *e; AvahiAddress *a; + AvahiServer *s; + AvahiWatch *w; + int r; assert(l); assert(p); - if (l->engine->n_dns_servers <= 0) + e = l->engine; + assert(e); + + s = e->server; + assert(s); + + if (e->n_dns_servers <= 0) return -1; - assert(l->engine->current_dns_server < l->engine->n_dns_servers); + assert(e->current_dns_server < e->n_dns_servers); - a = &l->engine->dns_servers[l->engine->current_dns_server]; + a = &e->dns_servers[e->current_dns_server]; l->dns_server_used = *a; - if (a->proto == AVAHI_PROTO_INET) { + if (l->fd >= 0) { + /* We are reusing lookup object and sending packet to another server so let's cleanup before we establish connection to new server. */ + s->poll_api->watch_free(l->watch); + l->watch = NULL; - if (l->engine->fd_ipv4 < 0) - return -1; - - return avahi_send_dns_packet_ipv4(l->engine->fd_ipv4, AVAHI_IF_UNSPEC, p, NULL, &a->data.ipv4, AVAHI_DNS_PORT); - - } else { - assert(a->proto == AVAHI_PROTO_INET6); - - if (l->engine->fd_ipv6 < 0) - return -1; - - return avahi_send_dns_packet_ipv6(l->engine->fd_ipv6, AVAHI_IF_UNSPEC, p, NULL, &a->data.ipv6, AVAHI_DNS_PORT); + close(l->fd); + l->fd = -EBADF; } + + assert(a->proto == AVAHI_PROTO_INET || a->proto == AVAHI_PROTO_INET6); + + if (a->proto == AVAHI_PROTO_INET) + r = s->config.use_ipv4 ? avahi_open_unicast_socket_ipv4() : -1; + else + r = s->config.use_ipv6 ? avahi_open_unicast_socket_ipv6() : -1; + + if (r < 0) { + avahi_log_error(__FILE__ ": Failed to create socket for wide area lookup"); + return -1; + } + + w = s->poll_api->watch_new(s->poll_api, r, AVAHI_WATCH_IN, socket_event, l); + if (!w) { + close(r); + avahi_log_error(__FILE__ ": Failed to create socket watch for wide area lookup"); + return -1; + } + + l->fd = r; + l->watch = w; + l->proto = a->proto; + + return a->proto == AVAHI_PROTO_INET ? + avahi_send_dns_packet_ipv4(l->fd, AVAHI_IF_UNSPEC, p, NULL, &a->data.ipv4, AVAHI_DNS_PORT): + avahi_send_dns_packet_ipv6(l->fd, AVAHI_IF_UNSPEC, p, NULL, &a->data.ipv6, AVAHI_DNS_PORT); } static void next_dns_server(AvahiWideAreaLookupEngine *e) { @@ -246,6 +279,9 @@ AvahiWideAreaLookup *avahi_wide_area_lookup_new( l->dead = 0; l->key = avahi_key_ref(key); l->cname_key = avahi_key_new_cname(l->key); + l->fd = -EBADF; + l->watch = NULL; + l->proto = AVAHI_PROTO_UNSPEC; l->callback = callback; l->userdata = userdata; @@ -314,6 +350,12 @@ static void lookup_destroy(AvahiWideAreaLookup *l) { if (l->cname_key) avahi_key_unref(l->cname_key); + if (l->watch) + l->engine->server->poll_api->watch_free(l->watch); + + if (l->fd >= 0) + close(l->fd); + avahi_free(l); } @@ -572,14 +614,20 @@ finish: } static void socket_event(AVAHI_GCC_UNUSED AvahiWatch *w, int fd, AVAHI_GCC_UNUSED AvahiWatchEvent events, void *userdata) { - AvahiWideAreaLookupEngine *e = userdata; + AvahiWideAreaLookup *l = userdata; + AvahiWideAreaLookupEngine *e = l->engine; AvahiDnsPacket *p = NULL; - if (fd == e->fd_ipv4) - p = avahi_recv_dns_packet_ipv4(e->fd_ipv4, NULL, NULL, NULL, NULL, NULL); + assert(l); + assert(e); + assert(l->fd == fd); + + if (l->proto == AVAHI_PROTO_INET) + p = avahi_recv_dns_packet_ipv4(l->fd, NULL, NULL, NULL, NULL, NULL); else { - assert(fd == e->fd_ipv6); - p = avahi_recv_dns_packet_ipv6(e->fd_ipv6, NULL, NULL, NULL, NULL, NULL); + assert(l->proto == AVAHI_PROTO_INET6); + + p = avahi_recv_dns_packet_ipv6(l->fd, NULL, NULL, NULL, NULL, NULL); } if (p) { @@ -598,32 +646,6 @@ AvahiWideAreaLookupEngine *avahi_wide_area_engine_new(AvahiServer *s) { e->server = s; e->cleanup_dead = 0; - /* Create sockets */ - e->fd_ipv4 = s->config.use_ipv4 ? avahi_open_unicast_socket_ipv4() : -1; - e->fd_ipv6 = s->config.use_ipv6 ? avahi_open_unicast_socket_ipv6() : -1; - - if (e->fd_ipv4 < 0 && e->fd_ipv6 < 0) { - avahi_log_error(__FILE__": Failed to create wide area sockets: %s", strerror(errno)); - - if (e->fd_ipv6 >= 0) - close(e->fd_ipv6); - - if (e->fd_ipv4 >= 0) - close(e->fd_ipv4); - - avahi_free(e); - return NULL; - } - - /* Create watches */ - - e->watch_ipv4 = e->watch_ipv6 = NULL; - - if (e->fd_ipv4 >= 0) - e->watch_ipv4 = s->poll_api->watch_new(e->server->poll_api, e->fd_ipv4, AVAHI_WATCH_IN, socket_event, e); - if (e->fd_ipv6 >= 0) - e->watch_ipv6 = s->poll_api->watch_new(e->server->poll_api, e->fd_ipv6, AVAHI_WATCH_IN, socket_event, e); - e->n_dns_servers = e->current_dns_server = 0; /* Initialize cache */ @@ -651,18 +673,6 @@ void avahi_wide_area_engine_free(AvahiWideAreaLookupEngine *e) { avahi_hashmap_free(e->lookups_by_id); avahi_hashmap_free(e->lookups_by_key); - if (e->watch_ipv4) - e->server->poll_api->watch_free(e->watch_ipv4); - - if (e->watch_ipv6) - e->server->poll_api->watch_free(e->watch_ipv6); - - if (e->fd_ipv6 >= 0) - close(e->fd_ipv6); - - if (e->fd_ipv4 >= 0) - close(e->fd_ipv4); - avahi_free(e); } @@ -680,7 +690,7 @@ void avahi_wide_area_set_servers(AvahiWideAreaLookupEngine *e, const AvahiAddres if (a) { for (e->n_dns_servers = 0; n > 0 && e->n_dns_servers < AVAHI_WIDE_AREA_SERVERS_MAX; a++, n--) - if ((a->proto == AVAHI_PROTO_INET && e->fd_ipv4 >= 0) || (a->proto == AVAHI_PROTO_INET6 && e->fd_ipv6 >= 0)) + if (a->proto == AVAHI_PROTO_INET || a->proto == AVAHI_PROTO_INET6) e->dns_servers[e->n_dns_servers++] = *a; } else { assert(n == 0);