diff --git a/src/basic/efivars.c b/src/basic/efivars.c index 0eeee1038ef..b64b83664a8 100644 --- a/src/basic/efivars.c +++ b/src/basic/efivars.c @@ -87,14 +87,21 @@ int efi_get_variable( } /* We want +1 for the read call, and +3 for the additional terminating bytes added below. */ + size_t file_size = (size_t) st.st_size, payload_size, read_size, alloc_size, read_limit; + if (!SUB_SAFE(&payload_size, file_size, sizeof(attr)) || + !ADD_SAFE(&read_size, payload_size, 1) || + !ADD_SAFE(&alloc_size, read_size, 3) || + !ADD_SAFE(&read_limit, file_size, 1)) + return log_debug_errno(SYNTHETIC_ERRNO(EOVERFLOW), "EFI variable '%s' size calculation overflow, refusing.", p); + free(buf); - buf = malloc((size_t) st.st_size - sizeof(attr) + CONST_MAX(1, 3)); + buf = malloc(alloc_size); if (!buf) return -ENOMEM; struct iovec iov[] = { { &attr, sizeof(attr) }, - { buf, (size_t) st.st_size - sizeof(attr) + 1 }, + { buf, read_size }, }; n = readv(fd, iov, 2); @@ -103,11 +110,11 @@ int efi_get_variable( return log_debug_errno(errno, "Reading from '%s' failed: %m", p); log_debug("Reading from '%s' failed with EINTR, retrying.", p); - } else if ((size_t) n == sizeof(attr) + st.st_size + 1) + } else if ((size_t) n == read_limit) /* We need to try again with a bigger buffer, the variable was apparently changed concurrently? */ log_debug("EFI variable '%s' larger than expected, retrying.", p); else { - assert((size_t) n < sizeof(attr) + st.st_size + 1); + assert((size_t) n < read_limit); break; } diff --git a/src/import/pull-oci.c b/src/import/pull-oci.c index 90dfee9ed4a..c2d9bfeddf4 100644 --- a/src/import/pull-oci.c +++ b/src/import/pull-oci.c @@ -366,7 +366,7 @@ static int oci_pull_redirect_manifest(OciPull *i, const OciIndexEntry *entry) { j->on_finished = oci_pull_job_on_finished_manifest; j->calc_checksum = true; - if (!iovec_memdup(&entry->digest, &j->checksum)) + if (!iovec_memdup(&entry->digest, &j->expected_checksum)) return -ENOMEM; j->description = strjoin("Image Manifest (", url, ")"); diff --git a/src/libsystemd-network/sd-ndisc-neighbor.c b/src/libsystemd-network/sd-ndisc-neighbor.c index 5ce1da06da3..a5de33db899 100644 --- a/src/libsystemd-network/sd-ndisc-neighbor.c +++ b/src/libsystemd-network/sd-ndisc-neighbor.c @@ -52,7 +52,9 @@ int ndisc_neighbor_parse(sd_ndisc *nd, sd_ndisc_neighbor *na) { /* Neighbor advertisement packets are neatly aligned to 64-bit boundaries, hence we can access them directly */ const struct nd_neighbor_advert *a = (const struct nd_neighbor_advert*) na->packet->raw_packet; assert(a->nd_na_type == ND_NEIGHBOR_ADVERT); - assert(a->nd_na_code == 0); + if (a->nd_na_code != 0) + return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), + "Received Neighbor Advertisement with non-zero code, ignoring datagram."); na->flags = a->nd_na_flags_reserved; /* the first 3 bits */ na->target_address = a->nd_na_target; diff --git a/src/libsystemd-network/sd-ndisc-redirect.c b/src/libsystemd-network/sd-ndisc-redirect.c index 4845adbd7bb..fcfefc5ca5b 100644 --- a/src/libsystemd-network/sd-ndisc-redirect.c +++ b/src/libsystemd-network/sd-ndisc-redirect.c @@ -52,7 +52,9 @@ int ndisc_redirect_parse(sd_ndisc *nd, sd_ndisc_redirect *rd) { const struct nd_redirect *a = (const struct nd_redirect*) rd->packet->raw_packet; assert(a->nd_rd_type == ND_REDIRECT); - assert(a->nd_rd_code == 0); + if (a->nd_rd_code != 0) + return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), + "Received Redirect message with non-zero code, ignoring datagram."); rd->target_address = a->nd_rd_target; rd->destination_address = a->nd_rd_dst; diff --git a/src/libsystemd-network/sd-ndisc-router.c b/src/libsystemd-network/sd-ndisc-router.c index 9d6181b3554..fcce89cc9b7 100644 --- a/src/libsystemd-network/sd-ndisc-router.c +++ b/src/libsystemd-network/sd-ndisc-router.c @@ -108,7 +108,9 @@ int ndisc_router_parse(sd_ndisc *nd, sd_ndisc_router *rt) { a = (const struct nd_router_advert*) rt->packet->raw_packet; assert(a->nd_ra_type == ND_ROUTER_ADVERT); - assert(a->nd_ra_code == 0); + if (a->nd_ra_code != 0) + return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), + "Received Router Advertisement with non-zero code, ignoring."); rt->hop_limit = a->nd_ra_curhoplimit; rt->flags = a->nd_ra_flags_reserved; /* the first 8 bits */ diff --git a/src/libsystemd-network/test-ndisc-rs.c b/src/libsystemd-network/test-ndisc-rs.c index 9c1e6f03a4c..86b7c07bdbf 100644 --- a/src/libsystemd-network/test-ndisc-rs.c +++ b/src/libsystemd-network/test-ndisc-rs.c @@ -15,6 +15,9 @@ #include "icmp6-test-util.h" #include "in-addr-util.h" #include "ndisc-internal.h" +#include "ndisc-neighbor-internal.h" +#include "ndisc-redirect-internal.h" +#include "ndisc-router-internal.h" #include "strv.h" #include "tests.h" @@ -22,6 +25,59 @@ static struct ether_addr mac_addr = { .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'} }; +static ICMP6Packet* make_packet(const void *raw, size_t size) { + ICMP6Packet *packet; + + assert(raw); + + packet = malloc0(offsetof(ICMP6Packet, raw_packet) + size); + ASSERT_NOT_NULL(packet); + + packet->n_ref = 1; + packet->raw_size = size; + memcpy(packet->raw_packet, raw, size); + + return packet; +} + +TEST(parse_rejects_nonzero_code) { + static const struct nd_router_advert router_advertisement = { + .nd_ra_type = ND_ROUTER_ADVERT, + .nd_ra_code = 1, + }; + static const struct nd_neighbor_advert neighbor_advertisement = { + .nd_na_type = ND_NEIGHBOR_ADVERT, + .nd_na_code = 1, + }; + static const struct nd_redirect redirect = { + .nd_rd_type = ND_REDIRECT, + .nd_rd_code = 1, + }; + + _cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = + make_packet(&router_advertisement, sizeof(router_advertisement)); + _cleanup_(sd_ndisc_router_unrefp) sd_ndisc_router *router = + ASSERT_PTR(ndisc_router_new(packet)); + + ASSERT_ERROR(ndisc_router_parse(/* nd= */ NULL, router), EBADMSG); + router = sd_ndisc_router_unref(router); + packet = icmp6_packet_unref(packet); + + packet = make_packet(&neighbor_advertisement, sizeof(neighbor_advertisement)); + _cleanup_(sd_ndisc_neighbor_unrefp) sd_ndisc_neighbor *neighbor = + ASSERT_PTR(ndisc_neighbor_new(packet)); + + ASSERT_ERROR(ndisc_neighbor_parse(/* nd= */ NULL, neighbor), EBADMSG); + neighbor = sd_ndisc_neighbor_unref(neighbor); + packet = icmp6_packet_unref(packet); + + packet = make_packet(&redirect, sizeof(redirect)); + _cleanup_(sd_ndisc_redirect_unrefp) sd_ndisc_redirect *rd = + ASSERT_PTR(ndisc_redirect_new(packet)); + + ASSERT_ERROR(ndisc_redirect_parse(/* nd= */ NULL, rd), EBADMSG); +} + static void router_dump(sd_ndisc_router *rt) { struct in6_addr addr; uint8_t hop_limit; diff --git a/src/nss-myhostname/nss-myhostname.c b/src/nss-myhostname/nss-myhostname.c index 83d968ff0b5..d902d305a7a 100644 --- a/src/nss-myhostname/nss-myhostname.c +++ b/src/nss-myhostname/nss-myhostname.c @@ -40,6 +40,7 @@ enum nss_status _nss_myhostname_gethostbyname4_r( const char *canonical = NULL; int n_addresses = 0; uint32_t local_address_ipv4 = 0; + bool ipv6_enabled; size_t l, idx, ms; char *r_name; @@ -96,7 +97,9 @@ enum nss_status _nss_myhostname_gethostbyname4_r( } l = strlen(canonical); - ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 1 + socket_ipv6_is_enabled()); + ipv6_enabled = n_addresses == 0 && socket_ipv6_is_enabled(); + ms = ALIGN(l+1) + + ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 1 + ipv6_enabled); if (buflen < ms) { UNPROTECT_ERRNO; *errnop = ERANGE; @@ -112,13 +115,14 @@ enum nss_status _nss_myhostname_gethostbyname4_r( assert(n_addresses >= 0); if (n_addresses == 0) { /* Second, fill in IPv6 tuple */ - if (socket_ipv6_is_enabled()) { + if (ipv6_enabled) { r_tuple = (struct gaih_addrtuple*) (buffer + idx); - r_tuple->next = r_tuple_prev; - r_tuple->name = r_name; - r_tuple->family = AF_INET6; + *r_tuple = (struct gaih_addrtuple) { + .next = r_tuple_prev, + .name = r_name, + .family = AF_INET6, + }; memcpy(r_tuple->addr, LOCALADDRESS_IPV6, FAMILY_ADDRESS_SIZE(AF_INET6)); - r_tuple->scopeid = 0; idx += ALIGN(sizeof(struct gaih_addrtuple)); r_tuple_prev = r_tuple; @@ -126,11 +130,12 @@ enum nss_status _nss_myhostname_gethostbyname4_r( /* Third, fill in IPv4 tuple */ r_tuple = (struct gaih_addrtuple*) (buffer + idx); - r_tuple->next = r_tuple_prev; - r_tuple->name = r_name; - r_tuple->family = AF_INET; - *(uint32_t*) r_tuple->addr = local_address_ipv4; - r_tuple->scopeid = 0; + *r_tuple = (struct gaih_addrtuple) { + .next = r_tuple_prev, + .name = r_name, + .family = AF_INET, + }; + memcpy(r_tuple->addr, &local_address_ipv4, FAMILY_ADDRESS_SIZE(AF_INET)); idx += ALIGN(sizeof(struct gaih_addrtuple)); r_tuple_prev = r_tuple; @@ -139,12 +144,18 @@ enum nss_status _nss_myhostname_gethostbyname4_r( /* Fourth, fill actual addresses in, but in backwards order */ for (int i = n_addresses; i > 0; i--) { struct local_address *a = addresses + i - 1; + uint32_t scopeid = 0; + + if (a->family == AF_INET6 && in6_addr_is_link_local(&a->address.in6)) + scopeid = a->ifindex; r_tuple = (struct gaih_addrtuple*) (buffer + idx); - r_tuple->next = r_tuple_prev; - r_tuple->name = r_name; - r_tuple->family = a->family; - r_tuple->scopeid = a->family == AF_INET6 && in6_addr_is_link_local(&a->address.in6) ? a->ifindex : 0; + *r_tuple = (struct gaih_addrtuple) { + .next = r_tuple_prev, + .name = r_name, + .family = a->family, + .scopeid = scopeid, + }; memcpy(r_tuple->addr, &a->address, FAMILY_ADDRESS_SIZE(a->family)); idx += ALIGN(sizeof(struct gaih_addrtuple)); @@ -180,6 +191,7 @@ static enum nss_status fill_in_hostent( int af, struct local_address *addresses, unsigned n_addresses, uint32_t local_address_ipv4, + bool ipv6_enabled, struct hostent *result, char *buffer, size_t buflen, int *errnop, int *h_errnop, @@ -212,8 +224,8 @@ static enum nss_status fill_in_hostent( (additional ? ALIGN(l_additional+1) : 0) + sizeof(char*) + (additional ? sizeof(char*) : 0) + - (c > 0 ? c : af == AF_INET ? 1 : socket_ipv6_is_enabled()) * ALIGN(alen) + - (c > 0 ? c+1 : af == AF_INET ? 2 : (unsigned) socket_ipv6_is_enabled() + 1) * sizeof(char*); + (c > 0 ? c : af == AF_INET ? 1 : ipv6_enabled) * ALIGN(alen) + + (c > 0 ? c+1 : af == AF_INET ? 2 : (unsigned) ipv6_enabled + 1) * sizeof(char*); if (buflen < ms) { UNPROTECT_ERRNO; @@ -263,7 +275,7 @@ static enum nss_status fill_in_hostent( } else if (af == AF_INET) { *(uint32_t*) r_addr = local_address_ipv4; assert_se(INC_SAFE(&idx, ALIGN(alen))); - } else if (socket_ipv6_is_enabled()) { + } else if (ipv6_enabled) { memcpy(r_addr, LOCALADDRESS_IPV6, FAMILY_ADDRESS_SIZE(AF_INET6)); assert_se(INC_SAFE(&idx, ALIGN(alen))); } @@ -279,7 +291,7 @@ static enum nss_status fill_in_hostent( ((char**) r_addr_list)[i] = NULL; assert_se(INC_SAFE(&idx, (c+1) * sizeof(char*))); - } else if (af == AF_INET || socket_ipv6_is_enabled()) { + } else if (af == AF_INET || ipv6_enabled) { ((char**) r_addr_list)[0] = r_addr; ((char**) r_addr_list)[1] = NULL; assert_se(INC_SAFE(&idx, 2 * sizeof(char*))); @@ -324,6 +336,7 @@ enum nss_status _nss_myhostname_gethostbyname3_r( const char *canonical, *additional = NULL; _cleanup_free_ char *hn = NULL; uint32_t local_address_ipv4 = 0; + bool ipv6_enabled; int n_addresses = 0; PROTECT_ERRNO; @@ -345,7 +358,8 @@ enum nss_status _nss_myhostname_gethostbyname3_r( return NSS_STATUS_UNAVAIL; } - if (af == AF_INET6 && !socket_ipv6_is_enabled()) + ipv6_enabled = af == AF_INET6 && socket_ipv6_is_enabled(); + if (af == AF_INET6 && !ipv6_enabled) goto not_found; if (is_localhost(name)) { @@ -397,6 +411,7 @@ enum nss_status _nss_myhostname_gethostbyname3_r( af, addresses, n_addresses, local_address_ipv4, + ipv6_enabled, host, buffer, buflen, errnop, h_errnop, @@ -423,6 +438,7 @@ enum nss_status _nss_myhostname_gethostbyaddr2_r( int n_addresses = 0; struct local_address *a; bool additional_from_hostname = false; + bool ipv6_enabled = false; unsigned n; PROTECT_ERRNO; @@ -461,7 +477,8 @@ enum nss_status _nss_myhostname_gethostbyaddr2_r( } else { assert(af == AF_INET6); - if (!socket_ipv6_is_enabled()) + ipv6_enabled = socket_ipv6_is_enabled(); + if (!ipv6_enabled) goto not_found; if (memcmp(addr, LOCALADDRESS_IPV6, FAMILY_ADDRESS_SIZE(AF_INET6)) == 0) { @@ -511,6 +528,7 @@ found: af, addresses, n_addresses, local_address_ipv4, + ipv6_enabled, host, buffer, buflen, errnop, h_errnop, diff --git a/src/shared/creds-util.c b/src/shared/creds-util.c index 58207d4d002..09bbf6bca89 100644 --- a/src/shared/creds-util.c +++ b/src/shared/creds-util.c @@ -1032,17 +1032,17 @@ int encrypt_credential_and_warn( return log_error_errno(r, "Failed to seal to TPM2: %m"); log_notice_errno(r, "TPM2 sealing didn't work, continuing without TPM2: %m"); + } else { + if (!iovec_memdup(&IOVEC_MAKE(tpm2_policy.buffer, tpm2_policy.size), &tpm2_policy_hash)) + return log_oom(); + + assert(n_blobs == 1); + tpm2_blob = TAKE_STRUCT(blobs[0]); + + assert(tpm2_blob.iov_len <= CREDENTIAL_FIELD_SIZE_MAX); + assert(tpm2_policy_hash.iov_len <= CREDENTIAL_FIELD_SIZE_MAX); + assert(tpm2_srk.iov_len <= CREDENTIAL_FIELD_SIZE_MAX); } - - if (!iovec_memdup(&IOVEC_MAKE(tpm2_policy.buffer, tpm2_policy.size), &tpm2_policy_hash)) - return log_oom(); - - assert(n_blobs == 1); - tpm2_blob = TAKE_STRUCT(blobs[0]); - - assert(tpm2_blob.iov_len <= CREDENTIAL_FIELD_SIZE_MAX); - assert(tpm2_policy_hash.iov_len <= CREDENTIAL_FIELD_SIZE_MAX); - assert(tpm2_srk.iov_len <= CREDENTIAL_FIELD_SIZE_MAX); } #endif diff --git a/src/test/test-nss-hosts.c b/src/test/test-nss-hosts.c index 20d151a4254..61f9b153f8c 100644 --- a/src/test/test-nss-hosts.c +++ b/src/test/test-nss-hosts.c @@ -15,6 +15,7 @@ #include "local-addresses.h" #include "log.h" #include "main-func.h" +#include "memory-util.h" #include "nss-test-util.h" #include "nss-util.h" #include "parse-util.h" @@ -67,6 +68,16 @@ static int print_gaih_addrtuples(const struct gaih_addrtuple *tuples) { return n; } +static void assert_myhostname_gaih_addrtuples(const struct gaih_addrtuple *tuples) { + assert(tuples); + + for (const struct gaih_addrtuple *it = tuples; it; it = it->next) + if (it->family == AF_INET) + assert_se(memeqzero( + (const uint8_t*) it->addr + FAMILY_ADDRESS_SIZE(AF_INET), + sizeof(it->addr) - FAMILY_ADDRESS_SIZE(AF_INET))); +} + static void print_struct_hostent(struct hostent *host, const char *canon) { log_info(" \"%s\"", host->h_name); STRV_FOREACH(s, host->h_aliases) @@ -112,6 +123,9 @@ static void test_gethostbyname4_r(void *handle, const char *module, const char * return; } + /* Poison the scratch buffer so uninitialized padding in NSS replies is observable. */ + memset(buffer, 0xa5, sizeof(buffer)); + status = f(name, &pat, buffer, sizeof buffer, &errno1, &errno2, &ttl); if (status == NSS_STATUS_SUCCESS) { log_info("%s(\"%s\") → status=%s%-20spat=buffer+0x%"PRIxPTR" errno=%d/%s h_errno=%d/%s ttl=%"PRIi32, @@ -122,6 +136,8 @@ static void test_gethostbyname4_r(void *handle, const char *module, const char * errno2, hstrerror(errno2), ttl); n = print_gaih_addrtuples(pat); + if (streq(module, "myhostname")) + assert_myhostname_gaih_addrtuples(pat); } else { log_info("%s(\"%s\") → status=%s%-20spat=0x%p errno=%d/%s h_errno=%d/%s", fname, name,