diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000000000000000000000000000000000..0a80fdce31f59c062e2abba28776e9521eddff30 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.gz filter=lfs diff=lfs merge=lfs -text diff --git a/.lfsconfig b/.lfsconfig new file mode 100644 index 0000000000000000000000000000000000000000..a446c3fd20e840a3e83b397dfb80fac12a40d174 --- /dev/null +++ b/.lfsconfig @@ -0,0 +1,2 @@ +[lfs] + url = https://artlfs.openeuler.openatom.cn/src-openEuler/unbound diff --git a/backport-CVE-2025-5994.patch b/backport-CVE-2025-5994.patch new file mode 100644 index 0000000000000000000000000000000000000000..240f2e29243529e3e1ed11cf0c8247f8a4af8bd5 --- /dev/null +++ b/backport-CVE-2025-5994.patch @@ -0,0 +1,262 @@ +diff --git a/edns-subnet/subnetmod.c b/edns-subnet/subnetmod.c +index ead720f3..c5e215b8 100644 +--- a/edns-subnet/subnetmod.c ++++ b/edns-subnet/subnetmod.c +@@ -51,6 +51,7 @@ + #include "services/cache/dns.h" + #include "util/module.h" + #include "util/regional.h" ++#include "util/fptr_wlist.h" + #include "util/storage/slabhash.h" + #include "util/config_file.h" + #include "util/data/msgreply.h" +@@ -155,7 +156,8 @@ int ecs_whitelist_check(struct query_info* qinfo, + + /* Cache by default, might be disabled after parsing EDNS option + * received from nameserver. */ +- if(!iter_stub_fwd_no_cache(qstate, &qstate->qinfo, NULL, NULL, NULL, 0)) { ++ if(!iter_stub_fwd_no_cache(qstate, &qstate->qinfo, NULL, NULL, NULL, 0) ++ && sq->ecs_client_in.subnet_validdata) { + qstate->no_cache_store = 0; + } + +@@ -522,6 +524,69 @@ common_prefix(uint8_t *a, uint8_t *b, uint8_t net) + return !memcmp(a, b, n) && ((net % 8) == 0 || a[n] == b[n]); + } + ++/** ++ * Create sub request that looks up the query. ++ * @param qstate: query state ++ * @param sq: subnet qstate ++ * @return false on failure. ++ */ ++static int ++generate_sub_request(struct module_qstate *qstate, struct subnet_qstate* sq) ++{ ++ struct module_qstate* subq = NULL; ++ uint16_t qflags = 0; /* OPCODE QUERY, no flags */ ++ int prime = 0; ++ int valrec = 0; ++ struct query_info qinf; ++ qinf.qname = qstate->qinfo.qname; ++ qinf.qname_len = qstate->qinfo.qname_len; ++ qinf.qtype = qstate->qinfo.qtype; ++ qinf.qclass = qstate->qinfo.qclass; ++ qinf.local_alias = NULL; ++ ++ qflags |= BIT_RD; ++ if((qstate->query_flags & BIT_CD)!=0) { ++ qflags |= BIT_CD; ++ valrec = 1; ++ } ++ ++ fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub)); ++ if(!(*qstate->env->attach_sub)(qstate, &qinf, qflags, prime, valrec, ++ &subq)) { ++ return 0; ++ } ++ if(subq) { ++ /* It is possible to access the subquery module state. */ ++ if(sq->ecs_client_in.subnet_source_mask == 0 && ++ edns_opt_list_find(qstate->edns_opts_front_in, ++ qstate->env->cfg->client_subnet_opcode)) { ++ subq->no_cache_store = 1; ++ } ++ } ++ return 1; ++} ++ ++/** ++ * Perform the query without subnet ++ * @param qstate: query state ++ * @param sq: subnet qstate ++ * @return module state ++ */ ++static enum module_ext_state ++generate_lookup_without_subnet(struct module_qstate *qstate, ++ struct subnet_qstate* sq) ++{ ++ verbose(VERB_ALGO, "subnetcache: make subquery to look up without subnet"); ++ if(!generate_sub_request(qstate, sq)) { ++ verbose(VERB_ALGO, "Could not generate sub query"); ++ qstate->return_rcode = LDNS_RCODE_FORMERR; ++ qstate->return_msg = NULL; ++ return module_finished; ++ } ++ sq->wait_subquery = 1; ++ return module_wait_subquery; ++} ++ + static enum module_ext_state + eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq) + { +@@ -557,14 +622,7 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq) + * is still useful to put it in the edns subnet cache for + * when a client explicitly asks for subnet specific answer. */ + verbose(VERB_QUERY, "subnetcache: Authority indicates no support"); +- if(!sq->started_no_cache_store) { +- lock_rw_wrlock(&sne->biglock); +- update_cache(qstate, id); +- lock_rw_unlock(&sne->biglock); +- } +- if (sq->subnet_downstream) +- cp_edns_bad_response(c_out, c_in); +- return module_finished; ++ return generate_lookup_without_subnet(qstate, sq); + } + + /* Purposefully there was no sent subnet, and there is consequently +@@ -589,14 +647,14 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq) + !common_prefix(s_out->subnet_addr, s_in->subnet_addr, + s_out->subnet_source_mask)) + { +- /* we can not accept, restart query without option */ ++ /* we can not accept, perform query without option */ + verbose(VERB_QUERY, "subnetcache: forged data"); + s_out->subnet_validdata = 0; + (void)edns_opt_list_remove(&qstate->edns_opts_back_out, + qstate->env->cfg->client_subnet_opcode); + sq->subnet_sent = 0; + sq->subnet_sent_no_subnet = 0; +- return module_restart_next; ++ return generate_lookup_without_subnet(qstate, sq); + } + + lock_rw_wrlock(&sne->biglock); +@@ -795,6 +853,9 @@ ecs_edns_back_parsed(struct module_qstate* qstate, int id, + } else if(sq->subnet_sent_no_subnet) { + /* The answer can be stored as scope 0, not in global cache. */ + qstate->no_cache_store = 1; ++ } else if(sq->subnet_sent) { ++ /* Need another query to be able to store in global cache. */ ++ qstate->no_cache_store = 1; + } + + return 1; +@@ -812,6 +873,32 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, + strmodulevent(event)); + log_query_info(VERB_QUERY, "subnetcache operate: query", &qstate->qinfo); + ++ if(sq && sq->wait_subquery_done) { ++ /* The subquery lookup returned. */ ++ if(sq->ecs_client_in.subnet_source_mask == 0 && ++ edns_opt_list_find(qstate->edns_opts_front_in, ++ qstate->env->cfg->client_subnet_opcode)) { ++ if(!sq->started_no_cache_store && ++ qstate->return_msg) { ++ lock_rw_wrlock(&sne->biglock); ++ update_cache(qstate, id); ++ lock_rw_unlock(&sne->biglock); ++ } ++ if (sq->subnet_downstream) ++ cp_edns_bad_response(&sq->ecs_client_out, ++ &sq->ecs_client_in); ++ /* It is a scope zero lookup, append edns subnet ++ * option to the querier. */ ++ subnet_ecs_opt_list_append(&sq->ecs_client_out, ++ &qstate->edns_opts_front_out, qstate, ++ qstate->region); ++ } ++ sq->wait_subquery_done = 0; ++ qstate->ext_state[id] = module_finished; ++ qstate->no_cache_store = sq->started_no_cache_store; ++ qstate->no_cache_lookup = sq->started_no_cache_lookup; ++ return; ++ } + if((event == module_event_new || event == module_event_pass) && + sq == NULL) { + struct edns_option* ecs_opt; +@@ -822,6 +909,8 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, + } + + sq = (struct subnet_qstate*)qstate->minfo[id]; ++ if(sq->wait_subquery) ++ return; /* Wait for that subquery to return */ + + if((ecs_opt = edns_opt_list_find( + qstate->edns_opts_front_in, +@@ -851,6 +940,14 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, + /* No clients are interested in result or we could not + * parse it, we don't do client subnet */ + sq->ecs_server_out.subnet_validdata = 0; ++ if(edns_opt_list_find(qstate->edns_opts_front_in, ++ qstate->env->cfg->client_subnet_opcode)) { ++ /* aggregated this deaggregated state */ ++ qstate->ext_state[id] = ++ generate_lookup_without_subnet( ++ qstate, sq); ++ return; ++ } + verbose(VERB_ALGO, "subnetcache: pass to next module"); + qstate->ext_state[id] = module_wait_module; + return; +@@ -891,6 +988,14 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, + } + lock_rw_unlock(&sne->biglock); + } ++ if(sq->ecs_client_in.subnet_source_mask == 0 && ++ edns_opt_list_find(qstate->edns_opts_front_in, ++ qstate->env->cfg->client_subnet_opcode)) { ++ /* client asked for resolution without edns subnet */ ++ qstate->ext_state[id] = generate_lookup_without_subnet( ++ qstate, sq); ++ return; ++ } + + sq->ecs_server_out.subnet_addr_fam = + sq->ecs_client_in.subnet_addr_fam; +@@ -927,6 +1032,8 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, + qstate->ext_state[id] = module_wait_module; + return; + } ++ if(sq && sq->wait_subquery) ++ return; /* Wait for that subquery to return */ + /* Query handed back by next module, we have a 'final' answer */ + if(sq && event == module_event_moddone) { + qstate->ext_state[id] = eval_response(qstate, id, sq); +@@ -975,10 +1082,27 @@ subnetmod_clear(struct module_qstate *ATTR_UNUSED(qstate), + } + + void +-subnetmod_inform_super(struct module_qstate *ATTR_UNUSED(qstate), +- int ATTR_UNUSED(id), struct module_qstate *ATTR_UNUSED(super)) ++subnetmod_inform_super(struct module_qstate *qstate, int id, ++ struct module_qstate *super) + { +- /* Not used */ ++ struct subnet_qstate* super_sq = ++ (struct subnet_qstate*)super->minfo[id]; ++ log_query_info(VERB_ALGO, "subnetcache inform_super: query", ++ &super->qinfo); ++ super_sq->wait_subquery = 0; ++ super_sq->wait_subquery_done = 1; ++ if(qstate->return_rcode != LDNS_RCODE_NOERROR || ++ !qstate->return_msg) { ++ super->return_msg = NULL; ++ super->return_rcode = LDNS_RCODE_SERVFAIL; ++ return; ++ } ++ super->return_rcode = LDNS_RCODE_NOERROR; ++ super->return_msg = dns_copy_msg(qstate->return_msg, super->region); ++ if(!super->return_msg) { ++ log_err("subnetcache: copy response, out of memory"); ++ super->return_rcode = LDNS_RCODE_SERVFAIL; ++ } + } + + size_t +diff --git a/edns-subnet/subnetmod.h b/edns-subnet/subnetmod.h +index 1ff8a23e..3893820f 100644 +--- a/edns-subnet/subnetmod.h ++++ b/edns-subnet/subnetmod.h +@@ -102,6 +102,10 @@ struct subnet_qstate { + int started_no_cache_store; + /** has the subnet module been started with no_cache_lookup? */ + int started_no_cache_lookup; ++ /** Wait for subquery that has been started for nonsubnet lookup. */ ++ int wait_subquery; ++ /** The subquery waited for is done. */ ++ int wait_subquery_done; + }; + + void subnet_data_delete(void* d, void* ATTR_UNUSED(arg)); diff --git a/unbound-1.22.0.tar.gz b/unbound-1.22.0.tar.gz index d9a7e9346dca4bf5bea9715d68617a6e76631529..7a4af9a9c7d632941013cccf44c8792ae1f6a273 100644 Binary files a/unbound-1.22.0.tar.gz and b/unbound-1.22.0.tar.gz differ diff --git a/unbound.spec b/unbound.spec index 404338ed05913fc210b03aad42f3a26eece956ce..c8b94bf2410d802e25ee488858defd928d580af3 100644 --- a/unbound.spec +++ b/unbound.spec @@ -1,8 +1,6 @@ -%{!?delete_la: %global delete_la find $RPM_BUILD_ROOT -type f -name "*.la" -delete} - Name: unbound Version: 1.22.0 -Release: 1 +Release: 2 Summary: Unbound is a validating, recursive, caching DNS resolver License: BSD-3-Clause Url: https://nlnetlabs.nl/projects/unbound/about/ @@ -19,11 +17,14 @@ Source10: root.anchor Source11: unbound.sysconfig Source12: unbound-anchor.timer Source13: unbound-anchor.service +Source20: unbound.sysusers Patch1: unbound-remove-buildin-key.patch Patch14: backport-check-before-use-daemon-shm_info.patch Patch15: backport-unbound-config.patch +Patch6001: backport-CVE-2025-5994.patch + BuildRequires: make flex swig pkgconfig systemd BuildRequires: libevent-devel expat-devel openssl-devel python3-devel BuildRequires: gcc byacc @@ -45,7 +46,7 @@ make custom builds or provide specific features to paying customers only. %package libs Summary: Libraries for %{name} -Requires(pre): shadow-utils +%{?sysusers_requires_compat} %description libs Libraries for %{name}. @@ -80,11 +81,7 @@ Requires: %{name}-libs = %{version}-%{release} %description -n python3-unbound The python3 module of unbound DNS resolver. -%package help -Summary: Man pages for unbound - -%description help -Package help includes includes man pages for unbound. +%package_help %prep @@ -141,6 +138,7 @@ install -p -m 0644 %{SOURCE9} $RPM_BUILD_ROOT%{_sysconfdir}/unbound install -p -m 0644 %{SOURCE11} $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/unbound install -p -m 0644 %{SOURCE12} $RPM_BUILD_ROOT%{_unitdir}/unbound-anchor.timer install -p -m 0644 %{SOURCE13} $RPM_BUILD_ROOT%{_unitdir}/unbound-anchor.service +install -p -D -m 0644 %{SOURCE20} %{buildroot}%{_sysusersdir}/%{name}.conf %delete_la @@ -154,10 +152,7 @@ install -p %{SOURCE8} $RPM_BUILD_ROOT%{_sysconfdir}/unbound/local.d/ echo ".so man8/unbound-control.8" > $RPM_BUILD_ROOT/%{_mandir}/man8/unbound-control-setup.8 %pre libs -getent group unbound >/dev/null || groupadd -r unbound -getent passwd unbound >/dev/null || \ -useradd -r -g unbound -d %{_sysconfdir}/unbound -s /sbin/nologin \ --c "Unbound DNS resolver" unbound +%sysusers_create_compat %{SOURCE20} %post %systemd_post unbound.service @@ -198,8 +193,8 @@ popd %files -%defattr(-,root,root) -%doc doc/CREDITS doc/FEATURES doc/README doc/LICENSE +%license doc/LICENSE +%doc doc/CREDITS doc/FEATURES doc/README %attr(0644,root,root) %{_tmpfilesdir}/unbound.conf %attr(0755,unbound,unbound) %dir %{_rundir}/%{name} %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/%{name}/unbound.conf @@ -222,7 +217,6 @@ popd %{_sbindir}/unbound-control-setup %files libs -%defattr(-,root,root) %if %{?openEuler:1}0 %dir %attr(0755,unbound,unbound) %{_sharedstatedir}/%{name} %attr(0644,unbound,unbound) %config %{_sharedstatedir}/%{name}/root.key @@ -231,14 +225,14 @@ popd %attr(0600,unbound,unbound) %config %{_sharedstatedir}/%{name}/root.key %endif %{_libdir}/libunbound.so.* +%{_sysusersdir}/%{name}.conf %files -n python3-unbound -%defattr(-,root,root) -%doc pythonmod/examples/* libunbound/python/examples/* pythonmod/LICENSE +%license pythonmod/LICENSE +%doc pythonmod/examples/* libunbound/python/examples/* %{python3_sitearch}/* %files devel -%defattr(-,root,root) %{_libdir}/libunbound.so %{_libdir}/pkgconfig/*.pc %{_includedir}/* @@ -256,10 +250,13 @@ popd %{_sbindir}/unbound-streamtcp %files help -%defattr(-,root,root) -%{_mandir}/man* +%{_mandir}/man?/* %changelog +* Sun Aug 03 2025 Funda Wang - 1.22.0-2 +- fix CVE-2025-5994 +- use systemd to create users + * Mon Mar 31 2025 gaihuiying - 1.22.0-1 - Type:requirement - CVE:NA diff --git a/unbound.sysusers b/unbound.sysusers new file mode 100644 index 0000000000000000000000000000000000000000..661468209fd20a3ee77ab496e339c1d839d31865 --- /dev/null +++ b/unbound.sysusers @@ -0,0 +1 @@ +u unbound - "Unbound DNS resolver" /var/lib/unbound /sbin/nologin