diff options
84 files changed, 2068 insertions, 551 deletions
diff --git a/Makefile.inc1 b/Makefile.inc1 index d8853fef321b..861d368af838 100644 --- a/Makefile.inc1 +++ b/Makefile.inc1 @@ -1417,7 +1417,7 @@ _sysctl=sysctl .endif ITOOLS= [ awk cap_mkdb cat chflags chmod chown cmp cp \ - date echo egrep find grep id install ${_install-info} \ + date echo egrep find grep id install \ ln make mkdir mtree mv pwd_mkdb \ rm sed services_mkdb sh sort strip ${_sysctl} test time true uname wc @@ -1542,14 +1542,10 @@ distributeworld installworld stageworld: _installcheck_world .PHONY .endif # make(distributeworld) ${_+_}cd ${.CURDIR}; ${IMAKE} re${.TARGET:S/world$//}; \ ${IMAKEENV} rm -rf ${INSTALLTMP} -.if !make(packageworld) && ${MK_CAROOT} != "no" - @if which openssl>/dev/null; then \ - PATH=${TMPPATH:Q}:${PATH:Q} \ - LOCALBASE=${LOCALBASE:Q} \ - sh ${SRCTOP}/usr.sbin/certctl/certctl.sh ${CERTCTLFLAGS} rehash; \ - else \ - echo "No openssl on the host, not rehashing certificates target -- /etc/ssl may not be populated."; \ - fi +.if !make(packageworld) && ${MK_CAROOT} != "no" && ${MK_OPENSSL} != "no" + PATH=${TMPPATH:Q}:${PATH:Q} \ + LOCALBASE=${LOCALBASE:Q} \ + certctl ${CERTCTLFLAGS} rehash .endif .if make(distributeworld) .for dist in ${EXTRA_DISTRIBUTIONS} @@ -2713,6 +2709,17 @@ _basic_bootstrap_tools+=sbin/md5 _basic_bootstrap_tools+=usr.sbin/tzsetup .endif +# certctl is needed as an install tool. libcrypto is rather heavy, so we'll +# build that alongside it only for platforms that don't expose headers for +# OpenSSL, like macOS. +.if ${MK_CAROOT} != "no" && ${MK_OPENSSL} != "no" +.if ${.MAKE.OS} == "Darwin" +_bootstrap_libcrypto=secure/lib/libcrypto +${_bt}-usr.sbin/certctl: ${_bt}-secure/lib/libcrypto +.endif +_certctl=usr.sbin/certctl +.endif + .if defined(BOOTSTRAP_ALL_TOOLS) _other_bootstrap_tools+=${_basic_bootstrap_tools} .for _subdir _links in ${_basic_bootstrap_tools_multilink} @@ -2776,6 +2783,8 @@ bootstrap-tools: ${_bt}-links .PHONY ${_strfile} \ usr.bin/dtc \ ${_cat} \ + ${_bootstrap_libcrypto} \ + ${_certctl} \ ${_kbdcontrol} \ ${_elftoolchain_libs} \ ${_libkldelf} \ @@ -85,6 +85,16 @@ f1f230439fa4: and obj NFSv4 mounted, the total RPC count dropped from 5461286 to 945643, with a 20% drop in elapsed time. +c3fc0db3bc50 + The default value of the sysctl variable + net.inet.tcp.nolocaltimewait has changed from 1 to 0. This means + that FreeBSD does not skip the TIME_WAIT state anymore for + endpoints for which the remote address is local. The new sysctl + variable net.inet.tcp.msl_local can be used to control the time + these endpoints stay in the TIME_WAIT state. The sysctl variable + net.inet.tcp.nolocaltimewait is deprecated and intended to be + removed in FreeBSD 16. + cd240957d7ba Making a connection to INADDR_ANY (i.e., using INADDR_ANY as an alias for localhost) is now disabled by default. This functionality can be diff --git a/contrib/one-true-awk/FIXES b/contrib/one-true-awk/FIXES index b3bf38f0aa1c..b876b9ec5ec9 100644 --- a/contrib/one-true-awk/FIXES +++ b/contrib/one-true-awk/FIXES @@ -25,6 +25,14 @@ THIS SOFTWARE. This file lists all bug fixes, changes, etc., made since the second edition of the AWK book was published in September 2023. +Aug 04, 2025 + Fix incorrect divisor in rand() - it was returning + even random numbers only. Thanks to Ozan Yigit. + + Fix a syntax issue with /= that caused constants to + turn into variables [eg. 42 /= 7]. Thanks to Arnold + Robbins. + Jan 14, 2025 Fix incorrect error line number issues. unput has no business managing lineno. Thanks to Ozan Yigit. diff --git a/contrib/one-true-awk/main.c b/contrib/one-true-awk/main.c index 361c23e70861..b8053af34b05 100644 --- a/contrib/one-true-awk/main.c +++ b/contrib/one-true-awk/main.c @@ -22,7 +22,7 @@ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ****************************************************************/ -const char *version = "version 20250116"; +const char *version = "version 20250804"; #define DEBUG #include <stdio.h> diff --git a/contrib/one-true-awk/run.c b/contrib/one-true-awk/run.c index eaddfdecbdd3..9bc07a517372 100644 --- a/contrib/one-true-awk/run.c +++ b/contrib/one-true-awk/run.c @@ -1567,6 +1567,8 @@ Cell *assign(Node **a, int n) /* a[0] = a[1], a[0] += a[1], etc. */ xf *= yf; break; case DIVEQ: + if ((x->tval & CON) != 0) + FATAL("non-constant required for left side of /="); if (yf == 0) FATAL("division by zero in /="); xf /= yf; @@ -2188,7 +2190,7 @@ Cell *bltin(Node **a, int n) /* builtin functions. a[0] is type, a[1] is arg lis /* random() returns numbers in [0..2^31-1] * in order to get a number in [0, 1), divide it by 2^31 */ - u = (Awkfloat) random() / (0x7fffffffL + 0x1UL); + u = (Awkfloat) random() / RAND_MAX; break; case FSRAND: if (isrec(x)) /* no argument provided */ diff --git a/etc/mtree/BSD.tests.dist b/etc/mtree/BSD.tests.dist index 2c25d9386032..e6a013f010de 100644 --- a/etc/mtree/BSD.tests.dist +++ b/etc/mtree/BSD.tests.dist @@ -1255,6 +1255,8 @@ .. .. usr.sbin + certctl + .. chown .. ctladm diff --git a/krb5/Makefile b/krb5/Makefile index e9bbcae106c1..c49601990c4a 100644 --- a/krb5/Makefile +++ b/krb5/Makefile @@ -1,7 +1,7 @@ SUBDIR= util .WAIT \ include .WAIT \ lib .WAIT\ - plugins libdata libexec usr.bin usr.sbin + plugins libexec usr.bin usr.sbin # SUBDIR_PARALLEL= .include <bsd.subdir.mk> diff --git a/krb5/libdata/Makefile b/krb5/Makefile.pc index c9a2e8e9259f..693a4636d749 100644 --- a/krb5/libdata/Makefile +++ b/krb5/Makefile.pc @@ -2,26 +2,15 @@ # # SPDX-License-Identifier: BSD-2-Clause OR ISC -.include "../Makefile.inc" - -.PATH: ${KRB5_DIR}/build-tools - -PACKAGE=kerberos-lib - -PCFILES=gssrpc.pc \ - kadm-client.pc \ - kadm-server.pc \ - kdb.pc \ - krb5.pc \ - krb5-gssapi.pc \ - mit-krb5.pc \ - mit-krb5-gssapi.pc - -CLEANFILES+= ${PCFILES} .SUFFIXES: .pc .pc.in .pc.in.pc: + @if ! grep -q "^PACKAGE_VERSION='${KRB5_VERSION}'$$" ${KRB5_DIR}/configure; then \ + echo "KRB5_VERSION ${KRB5_VERSION} does not match the source:"; \ + grep "^PACKAGE_VERSION=" ${KRB5_DIR}/configure; \ + exit 1; \ + fi >&2 sed -e 's,@prefix@,/usr,g ; \ s,@exec_prefix@,$${prefix},g ; \ s,@libdir@,${LIBDIR},g ; \ @@ -33,12 +22,3 @@ CLEANFILES+= ${PCFILES} s,@DEFCKTNAME@,FILE:/var/krb5/user/%{euid}/client.keytab,g ; \ s,@COM_ERR_LIB@,-lcom_err,g ;' \ ${.IMPSRC} > ${.TARGET} - -all: ${PCFILES} - @if ! grep -q "^PACKAGE_VERSION='${KRB5_VERSION}'$$" ${KRB5_DIR}/configure; then \ - echo "KRB5_VERSION ${KRB5_VERSION} does not match the source:"; \ - grep "^PACKAGE_VERSION=" ${KRB5_DIR}/configure; \ - exit 1; \ - fi >&2 - -.include <bsd.lib.mk> diff --git a/krb5/include/autoconf.h b/krb5/include/autoconf.h index fe281d136954..19979b060f10 100644 --- a/krb5/include/autoconf.h +++ b/krb5/include/autoconf.h @@ -641,7 +641,7 @@ #define PACKAGE_NAME "Kerberos 5" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "Kerberos 5 1.21.1" +#define PACKAGE_STRING "Kerberos 5 1.22.0" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "krb5" @@ -650,7 +650,7 @@ #define PACKAGE_URL "" /* Define to the version of this package. */ -#define PACKAGE_VERSION "1.21.1" +#define PACKAGE_VERSION "1.22.0" /* Default PKCS11 module name */ #define PKCS11_MODNAME "opensc-pkcs11.so" diff --git a/krb5/lib/gssapi/Makefile b/krb5/lib/gssapi/Makefile index c3948c6ffe28..a434b29a2dfb 100644 --- a/krb5/lib/gssapi/Makefile +++ b/krb5/lib/gssapi/Makefile @@ -10,12 +10,15 @@ .include <src.opts.mk> .include "../Makefile.inc" +.include "${KRB5_SRCTOP}/Makefile.pc" LIB= gssapi_krb5 -# SHLIB_MAJOR= 2 -LDFLAGS=-Wl,--no-undefined -LIBADD= krb5 k5crypto com_err krb5profile krb5support +LDFLAGS= -Wl,--no-undefined +LIBADD= krb5 k5crypto com_err krb5profile krb5support VERSION_MAP= ${.CURDIR}/version.map +PCFILES= krb5-gssapi.pc \ + mit-krb5-gssapi.pc +CLEANFILES+= ${PCFILES} # This is a contcatonation of: # crypto/krb5/src/lib/gssapi/libgssapi_krb5.exports @@ -44,4 +47,8 @@ CFLAGS+=${DEFINES} \ .include <bsd.lib.mk> +all: ${PCFILES} + .SUFFIXES: .h .c .et + +.PATH: ${KRB5_DIR}/build-tools diff --git a/krb5/lib/kadm5clnt/Makefile b/krb5/lib/kadm5clnt/Makefile index 9b17644e86de..52a7187cf9bb 100644 --- a/krb5/lib/kadm5clnt/Makefile +++ b/krb5/lib/kadm5clnt/Makefile @@ -10,12 +10,14 @@ .include <src.opts.mk> .include "../Makefile.inc" +.include "${KRB5_SRCTOP}/Makefile.pc" LIB= kadm5clnt_mit -# SHLIB_MAJOR= 12 -LDFLAGS=-Wl,--no-undefined -LIBADD= krb5profile gssrpc gssapi_krb5 krb5 k5crypto krb5support com_err +LDFLAGS= -Wl,--no-undefined +LIBADD= krb5profile gssrpc gssapi_krb5 krb5 k5crypto krb5support com_err VERSION_MAP= ${.CURDIR}/version.map +PCFILES= kadm-client.pc +CLEANFILES+= ${PCFILES} SRCS= alt_prof.c \ chpass_util.c \ @@ -88,6 +90,9 @@ afterinstall: .include <bsd.lib.mk> +all: ${PCFILES} + .SUFFIXES: .h .c -.PATH: ${KRB5_DIR}/lib/kadm5 +.PATH: ${KRB5_DIR}/build-tools \ + ${KRB5_DIR}/lib/kadm5 diff --git a/krb5/lib/kadm5srv/Makefile b/krb5/lib/kadm5srv/Makefile index c3996af2225b..9eecd20ca822 100644 --- a/krb5/lib/kadm5srv/Makefile +++ b/krb5/lib/kadm5srv/Makefile @@ -10,12 +10,14 @@ .include <src.opts.mk> .include "../Makefile.inc" +.include "${KRB5_SRCTOP}/Makefile.pc" LIB= kadm5srv_mit -# SHLIB_MAJOR= 12 -LDFLAGS=-Wl,--no-undefined -LIBADD= krb5profile gssrpc gssapi_krb5 kdb5 krb5 k5crypto krb5support com_err +LDFLAGS= -Wl,--no-undefined +LIBADD= krb5profile gssrpc gssapi_krb5 kdb5 krb5 k5crypto krb5support com_err VERSION_MAP= ${.CURDIR}/version.map +PCFILES= kadm-server.pc +CLEANFILES+= ${PCFILES} SRCS= alt_prof.c \ chpass_util.c \ @@ -88,6 +90,9 @@ ${GEN_CHPASS_UTIL_STRINGS_C}: ${GEN_CHPASS_UTIL_STRINGS} .include <bsd.lib.mk> +all: ${PCFILES} + .SUFFIXES: .h .c .et -.PATH: ${KRB5_DIR}/lib/kadm5 +.PATH: ${KRB5_DIR}/build-tools \ + ${KRB5_DIR}/lib/kadm5 diff --git a/krb5/lib/kdb/Makefile b/krb5/lib/kdb/Makefile index d6fbc71b7584..80039ad83502 100644 --- a/krb5/lib/kdb/Makefile +++ b/krb5/lib/kdb/Makefile @@ -10,12 +10,14 @@ .include <src.opts.mk> .include "../Makefile.inc" +.include "${KRB5_SRCTOP}/Makefile.pc" LIB= kdb5 -# SHLIB_MAJOR= 10 -LDFLAGS=-Wl,--no-undefined -LIBADD= krb5profile gssrpc krb5 k5crypto com_err krb5support gssapi_krb5 +LDFLAGS= -Wl,--no-undefined +LIBADD= krb5profile gssrpc krb5 k5crypto com_err krb5support gssapi_krb5 VERSION_MAP= ${.CURDIR}/version.map +PCFILES= kdb.pc +CLEANFILES+= ${PCFILES} SRCS= decrypt_key.c \ encrypt_key.c \ @@ -60,6 +62,9 @@ ${ADB_ERR_C}: ${ADB_ERR} .include <bsd.lib.mk> +all: ${PCFILES} + .SUFFIXES: .h .c -.PATH: ${KRB5_DIR}/lib/kdb +.PATH: ${KRB5_DIR}/build-tools \ + ${KRB5_DIR}/lib/kdb diff --git a/krb5/lib/krb5/Makefile b/krb5/lib/krb5/Makefile index a1576fcc256f..b3587cf58c2b 100644 --- a/krb5/lib/krb5/Makefile +++ b/krb5/lib/krb5/Makefile @@ -10,12 +10,15 @@ .include <src.opts.mk> .include "../Makefile.inc" +.include "${KRB5_SRCTOP}/Makefile.pc" LIB= krb5 -LDFLAGS=-Wl,--no-undefined -LIBADD= krb5profile k5crypto com_err krb5support -# SHLIB_MAJOR= 3 +LDFLAGS= -Wl,--no-undefined +LIBADD= krb5profile k5crypto com_err krb5support VERSION_MAP= ${.CURDIR}/version.map +PCFILES= krb5.pc \ + mit-krb5.pc +CLEANFILES+= ${PCFILES} SRCS= krb5_libinit.c @@ -67,6 +70,8 @@ KDCPACKAGE= kerberos-kdc .include <bsd.lib.mk> +all: ${PCFILES} + .SUFFIXES: .et .man .man.5: @@ -75,5 +80,6 @@ KDCPACKAGE= kerberos-kdc .man.7: @cp ${.ALLSRC} ${.TARGET} -.PATH: ${KRB5_DIR}/lib/krb5 \ +.PATH: ${KRB5_DIR}/build-tools \ + ${KRB5_DIR}/lib/krb5 \ ${KRB5_DIR}/man diff --git a/krb5/lib/rpc/Makefile b/krb5/lib/rpc/Makefile index 6f09020261e1..7e37a5479132 100644 --- a/krb5/lib/rpc/Makefile +++ b/krb5/lib/rpc/Makefile @@ -10,12 +10,14 @@ .include <src.opts.mk> .include "../Makefile.inc" +.include "${KRB5_SRCTOP}/Makefile.pc" LIB= gssrpc -# SHLIB_MAJOR= 4 -LDFLAGS=-Wl,--no-undefined -LIBADD= gssapi_krb5 krb5 k5crypto com_err krb5support +LDFLAGS= -Wl,--no-undefined +LIBADD= gssapi_krb5 krb5 k5crypto com_err krb5support VERSION_MAP= ${.CURDIR}/version.map +PCFILES= gssrpc.pc +CLEANFILES+= ${PCFILES} SRCS= auth_gss.c \ auth_gssapi.c \ @@ -75,4 +77,7 @@ CFLAGS+=-I${KRB5_DIR}/lib/rpc \ .include <bsd.lib.mk> -.PATH: ${KRB5_DIR}/lib/rpc +all: ${PCFILES} + +.PATH: ${KRB5_DIR}/build-tools \ + ${KRB5_DIR}/lib/rpc diff --git a/krb5/util/build-tools/krb5-config.sh b/krb5/util/build-tools/krb5-config.sh index c0481f3417e1..b23fe0141345 100755 --- a/krb5/util/build-tools/krb5-config.sh +++ b/krb5/util/build-tools/krb5-config.sh @@ -26,7 +26,7 @@ # Configurable parameters set by autoconf # Disreagard the above. Edit this by hand in the bespoke FreeBSD build. -version_string="Kerberos 5 release 1.21.3" +version_string="Kerberos 5 release 1.22.0" prefix=/usr exec_prefix=${prefix} diff --git a/sbin/pfctl/pfctl_radix.c b/sbin/pfctl/pfctl_radix.c index 0fe9ca8813bb..398c5e998330 100644 --- a/sbin/pfctl/pfctl_radix.c +++ b/sbin/pfctl/pfctl_radix.c @@ -122,6 +122,9 @@ pfr_add_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, { int ret; + if (*nadd) + *nadd = 0; + ret = pfctl_table_add_addrs_h(pfh, tbl, addr, size, nadd, flags); if (ret) { errno = ret; diff --git a/secure/lib/libcrypto/Makefile b/secure/lib/libcrypto/Makefile index e7e491124241..7e2350fb33ea 100644 --- a/secure/lib/libcrypto/Makefile +++ b/secure/lib/libcrypto/Makefile @@ -1,7 +1,10 @@ SHLIBDIR?= /lib -.if !defined(LIBCRYPTO_WITHOUT_SUBDIRS) +.if !defined(LIBCRYPTO_WITHOUT_SUBDIRS) && !defined(BOOTSTRAPPING) SUBDIR= engines modules .endif +.ifdef BOOTSTRAPPING +CFLAGS+= -DOPENSSL_NO_SCTP +.endif .include <bsd.own.mk> .include <src.opts.mk> diff --git a/secure/lib/libcrypto/Makefile.common b/secure/lib/libcrypto/Makefile.common index afcc2a48660c..ad48e2b434cf 100644 --- a/secure/lib/libcrypto/Makefile.common +++ b/secure/lib/libcrypto/Makefile.common @@ -6,7 +6,7 @@ CFLAGS+= -DL_ENDIAN CFLAGS+= -DB_ENDIAN .endif -.ifndef WITHOUT_AUTO_ASM +.if !defined(WITHOUT_AUTO_ASM) && !defined(BOOTSTRAPPING) .if ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "amd64" || \ ${MACHINE_CPUARCH} == "arm" || ${MACHINE_CPUARCH} == "i386" ASM_${MACHINE_CPUARCH}= diff --git a/share/man/man4/ioat.4 b/share/man/man4/ioat.4 index deef466c0ae0..1c0e1dd49fd1 100644 --- a/share/man/man4/ioat.4 +++ b/share/man/man4/ioat.4 @@ -23,7 +23,7 @@ .\" SUCH DAMAGE. .\" .Dd May 3, 2016 -.Dt IOAT 4 +.Dt IOAT 4 amd64 .Os .Sh NAME .Nm I/OAT diff --git a/share/man/man4/iwx.4 b/share/man/man4/iwx.4 index 7cd54d61b920..295a5f318afa 100644 --- a/share/man/man4/iwx.4 +++ b/share/man/man4/iwx.4 @@ -18,7 +18,7 @@ .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .Dd May 2, 2025 -.Dt IWX 4 +.Dt IWX 4 amd64 .Os .Sh NAME .Nm iwx diff --git a/share/man/man4/man4.aarch64/armv8crypto.4 b/share/man/man4/man4.aarch64/armv8crypto.4 index 7b8704395daf..0f763adc5766 100644 --- a/share/man/man4/man4.aarch64/armv8crypto.4 +++ b/share/man/man4/man4.aarch64/armv8crypto.4 @@ -25,7 +25,7 @@ .\" SUCH DAMAGE. .\" .Dd July 29, 2020 -.Dt ARMV8CRYPTO 4 +.Dt ARMV8CRYPTO 4 aarch64 .Os .Sh NAME .Nm armv8crypto diff --git a/share/man/man4/man4.aarch64/enetc.4 b/share/man/man4/man4.aarch64/enetc.4 index 33f796347f96..e7cfcb7ebe0e 100644 --- a/share/man/man4/man4.aarch64/enetc.4 +++ b/share/man/man4/man4.aarch64/enetc.4 @@ -25,7 +25,7 @@ .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" .Dd June 11, 2021 -.Dt ENETC 4 +.Dt ENETC 4 aarch64 .Os .Sh NAME .Nm enetc diff --git a/share/man/man4/man4.aarch64/felix.4 b/share/man/man4/man4.aarch64/felix.4 index 15caef6d274f..b97f3c2168e8 100644 --- a/share/man/man4/man4.aarch64/felix.4 +++ b/share/man/man4/man4.aarch64/felix.4 @@ -26,7 +26,7 @@ .\" SUCH DAMAGE. .\" .Dd June 21, 2021 -.Dt FELIX 4 +.Dt FELIX 4 aarch64 .Os .Sh NAME .Nm felix diff --git a/share/man/man4/man4.aarch64/rk_gpio.4 b/share/man/man4/man4.aarch64/rk_gpio.4 index b5648662cf5e..b2767dd66dce 100644 --- a/share/man/man4/man4.aarch64/rk_gpio.4 +++ b/share/man/man4/man4.aarch64/rk_gpio.4 @@ -23,7 +23,7 @@ .\" SUCH DAMAGE. .\" .Dd Apr 26, 2018 -.Dt RK_GPIO 4 +.Dt RK_GPIO 4 aarch64 .Os .Sh NAME .Nm rk_gpio diff --git a/share/man/man4/man4.aarch64/rk_grf.4 b/share/man/man4/man4.aarch64/rk_grf.4 index 64ed468c1983..b01a93091ecb 100644 --- a/share/man/man4/man4.aarch64/rk_grf.4 +++ b/share/man/man4/man4.aarch64/rk_grf.4 @@ -23,7 +23,7 @@ .\" SUCH DAMAGE. .\" .Dd Apr 26, 2018 -.Dt RK_GRF 4 +.Dt RK_GRF 4 aarch64 .Os .Sh NAME .Nm rk_grf diff --git a/share/man/man4/man4.aarch64/rk_grf_gpio.4 b/share/man/man4/man4.aarch64/rk_grf_gpio.4 index 6a5ebbe19e3b..2bfbebce1b76 100644 --- a/share/man/man4/man4.aarch64/rk_grf_gpio.4 +++ b/share/man/man4/man4.aarch64/rk_grf_gpio.4 @@ -4,7 +4,7 @@ .\" SPDX-License-Identifier: BSD-2-Clause .\" .Dd March 18, 2025 -.Dt RK_GRF_GPIO 4 +.Dt RK_GRF_GPIO 4 aarch64 .Os .Sh NAME .Nm rk_grf_gpio diff --git a/share/man/man4/man4.aarch64/rk_i2c.4 b/share/man/man4/man4.aarch64/rk_i2c.4 index be1a0fab943e..363cdeac7f72 100644 --- a/share/man/man4/man4.aarch64/rk_i2c.4 +++ b/share/man/man4/man4.aarch64/rk_i2c.4 @@ -25,7 +25,7 @@ .\" SUCH DAMAGE. .\" .Dd June 14, 2018 -.Dt RK_I2C 4 +.Dt RK_I2C 4 aarch64 .Os .Sh NAME .Nm rk_i2c diff --git a/share/man/man4/man4.aarch64/rk_pinctrl.4 b/share/man/man4/man4.aarch64/rk_pinctrl.4 index 519b3e793cd1..2be5f363498d 100644 --- a/share/man/man4/man4.aarch64/rk_pinctrl.4 +++ b/share/man/man4/man4.aarch64/rk_pinctrl.4 @@ -23,7 +23,7 @@ .\" SUCH DAMAGE. .\" .Dd Apr 26, 2018 -.Dt RK_PINCTRL 4 +.Dt RK_PINCTRL 4 aarch64 .Os .Sh NAME .Nm rk_pinctrl diff --git a/share/man/man4/man4.arm/am335x_dmtpps.4 b/share/man/man4/man4.arm/am335x_dmtpps.4 index d565c65e2cf1..bec5ff7726a0 100644 --- a/share/man/man4/man4.arm/am335x_dmtpps.4 +++ b/share/man/man4/man4.arm/am335x_dmtpps.4 @@ -24,7 +24,7 @@ .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" .Dd August 12, 2015 -.Dt AM335X_DMTPPS 4 +.Dt AM335X_DMTPPS 4 arm .Os .Sh NAME .Nm am335x_dmtpps diff --git a/share/man/man4/man4.arm/aw_gpio.4 b/share/man/man4/man4.arm/aw_gpio.4 index 5cbc7562d9bd..ef9fc1fe2733 100644 --- a/share/man/man4/man4.arm/aw_gpio.4 +++ b/share/man/man4/man4.arm/aw_gpio.4 @@ -23,7 +23,7 @@ .\" SUCH DAMAGE. .\" .Dd October 8, 2024 -.Dt AW_GPIO 4 +.Dt AW_GPIO 4 arm .Os .Sh NAME .Nm aw_gpio diff --git a/share/man/man4/man4.arm/aw_mmc.4 b/share/man/man4/man4.arm/aw_mmc.4 index eb7fc9ce020a..e3f961fa5067 100644 --- a/share/man/man4/man4.arm/aw_mmc.4 +++ b/share/man/man4/man4.arm/aw_mmc.4 @@ -23,7 +23,7 @@ .\" SUCH DAMAGE. .\" .Dd Dec 25, 2017 -.Dt AW_MMC 4 +.Dt AW_MMC 4 arm .Os .Sh NAME .Nm aw_mmc diff --git a/share/man/man4/man4.arm/aw_rtc.4 b/share/man/man4/man4.arm/aw_rtc.4 index 1296cd41da68..87212d85116c 100644 --- a/share/man/man4/man4.arm/aw_rtc.4 +++ b/share/man/man4/man4.arm/aw_rtc.4 @@ -23,7 +23,7 @@ .\" SUCH DAMAGE. .\" .Dd December 10, 2024 -.Dt AW_RTC 4 +.Dt AW_RTC 4 arm .Os .Sh NAME .Nm aw_rtc diff --git a/share/man/man4/man4.arm/aw_sid.4 b/share/man/man4/man4.arm/aw_sid.4 index 5cd2f3d5e072..8b3691259f22 100644 --- a/share/man/man4/man4.arm/aw_sid.4 +++ b/share/man/man4/man4.arm/aw_sid.4 @@ -25,7 +25,7 @@ .\" SUCH DAMAGE. .\" .Dd October 8, 2024 -.Dt AW_SID 4 +.Dt AW_SID 4 arm .Os .Sh NAME .Nm aw_sid diff --git a/share/man/man4/man4.arm/aw_spi.4 b/share/man/man4/man4.arm/aw_spi.4 index f8985e1c16bb..d0566a45b54b 100644 --- a/share/man/man4/man4.arm/aw_spi.4 +++ b/share/man/man4/man4.arm/aw_spi.4 @@ -23,7 +23,7 @@ .\" SUCH DAMAGE. .\" .Dd May 17, 2018 -.Dt AW_SPI 4 +.Dt AW_SPI 4 arm .Os .Sh NAME .Nm aw_spi diff --git a/share/man/man4/man4.arm/aw_syscon.4 b/share/man/man4/man4.arm/aw_syscon.4 index e32f329e489a..97f01196a8a6 100644 --- a/share/man/man4/man4.arm/aw_syscon.4 +++ b/share/man/man4/man4.arm/aw_syscon.4 @@ -25,7 +25,7 @@ .\" SUCH DAMAGE. .\" .Dd November 11, 2024 -.Dt AW_SYSCON 4 +.Dt AW_SYSCON 4 arm .Os .Sh NAME .Nm aw_syscon diff --git a/share/man/man4/man4.arm/bcm283x_pwm.4 b/share/man/man4/man4.arm/bcm283x_pwm.4 index 1fb5a830ace7..71d7f0cc3cca 100644 --- a/share/man/man4/man4.arm/bcm283x_pwm.4 +++ b/share/man/man4/man4.arm/bcm283x_pwm.4 @@ -25,7 +25,7 @@ .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" .Dd September 10, 2018 -.Dt BCM283X_PWM 4 +.Dt BCM283X_PWM 4 arm .Os .Sh NAME .Nm bcm283x_pwm diff --git a/share/man/man4/man4.arm/devcfg.4 b/share/man/man4/man4.arm/devcfg.4 index ddf368a85f24..cbc205814c69 100644 --- a/share/man/man4/man4.arm/devcfg.4 +++ b/share/man/man4/man4.arm/devcfg.4 @@ -23,7 +23,7 @@ .\" SUCH DAMAGE. .\" .Dd February 28, 2013 -.Dt DEVCFG 4 +.Dt DEVCFG 4 arm .Os .Sh NAME .Nm devcfg diff --git a/share/man/man4/man4.arm/imx6_ahci.4 b/share/man/man4/man4.arm/imx6_ahci.4 index 9979cef50d79..50689e323db8 100644 --- a/share/man/man4/man4.arm/imx6_ahci.4 +++ b/share/man/man4/man4.arm/imx6_ahci.4 @@ -24,7 +24,7 @@ .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" .Dd July 7, 2018 -.Dt IMX6_AHCI 4 +.Dt IMX6_AHCI 4 arm .Os .Sh NAME .Nm imx6_ahci diff --git a/share/man/man4/man4.arm/imx6_snvs.4 b/share/man/man4/man4.arm/imx6_snvs.4 index b36c3ddd91c1..2c1db97b231c 100644 --- a/share/man/man4/man4.arm/imx6_snvs.4 +++ b/share/man/man4/man4.arm/imx6_snvs.4 @@ -24,7 +24,7 @@ .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" .Dd July 8, 2018 -.Dt IMX6_SNVS 4 +.Dt IMX6_SNVS 4 arm .Os .Sh NAME .Nm imx6_snvs diff --git a/share/man/man4/man4.arm/imx_spi.4 b/share/man/man4/man4.arm/imx_spi.4 index e7555ed20d94..54a5339e3276 100644 --- a/share/man/man4/man4.arm/imx_spi.4 +++ b/share/man/man4/man4.arm/imx_spi.4 @@ -24,7 +24,7 @@ .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" .Dd July 9, 2018 -.Dt IMX_SPI 4 +.Dt IMX_SPI 4 arm .Os .Sh NAME .Nm imx_spi diff --git a/share/man/man4/man4.arm/imx_wdog.4 b/share/man/man4/man4.arm/imx_wdog.4 index 4b993e1d066b..cb4d0e13865b 100644 --- a/share/man/man4/man4.arm/imx_wdog.4 +++ b/share/man/man4/man4.arm/imx_wdog.4 @@ -24,7 +24,7 @@ .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" .Dd July 7, 2018 -.Dt IMX_WDOG 4 +.Dt IMX_WDOG 4 arm .Os .Sh NAME .Nm imx_wdog diff --git a/share/man/man4/man4.arm/mge.4 b/share/man/man4/man4.arm/mge.4 index e949b36f4307..cba9327eadcf 100644 --- a/share/man/man4/man4.arm/mge.4 +++ b/share/man/man4/man4.arm/mge.4 @@ -24,7 +24,7 @@ .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" .Dd November 27, 2008 -.Dt MGE 4 +.Dt MGE 4 arm .Os .Sh NAME .Nm mge diff --git a/share/man/man4/man4.arm/ti_adc.4 b/share/man/man4/man4.arm/ti_adc.4 index d71547231e4c..fb59e1d3e57c 100644 --- a/share/man/man4/man4.arm/ti_adc.4 +++ b/share/man/man4/man4.arm/ti_adc.4 @@ -23,7 +23,7 @@ .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" .Dd June 1, 2014 -.Dt TI_ADC 4 +.Dt TI_ADC 4 arm .Os .Sh NAME .Nm ti_adc diff --git a/share/man/man4/man4.powerpc/abtn.4 b/share/man/man4/man4.powerpc/abtn.4 index 92d643d5cf32..7421d0a0b5a6 100644 --- a/share/man/man4/man4.powerpc/abtn.4 +++ b/share/man/man4/man4.powerpc/abtn.4 @@ -25,7 +25,7 @@ .\" POSSIBILITY OF SUCH DAMAGE. .\" .Dd October 16, 2011 -.Dt ABTN 4 +.Dt ABTN 4 powerpc .Os .Sh NAME .Nm abtn diff --git a/share/man/man4/man4.powerpc/adb.4 b/share/man/man4/man4.powerpc/adb.4 index a781787995ab..6041484b5e33 100644 --- a/share/man/man4/man4.powerpc/adb.4 +++ b/share/man/man4/man4.powerpc/adb.4 @@ -24,7 +24,7 @@ .\" POSSIBILITY OF SUCH DAMAGE. .\" .Dd December 3, 2009 -.Dt ADB 4 +.Dt ADB 4 powerpc .Os .Sh NAME .Nm adb diff --git a/share/man/man4/man4.powerpc/akbd.4 b/share/man/man4/man4.powerpc/akbd.4 index 44af08961122..3406f5a1aa76 100644 --- a/share/man/man4/man4.powerpc/akbd.4 +++ b/share/man/man4/man4.powerpc/akbd.4 @@ -24,7 +24,7 @@ .\" POSSIBILITY OF SUCH DAMAGE. .\" .Dd December 3, 2009 -.Dt AKBD 4 +.Dt AKBD 4 powerpc .Os .Sh NAME .Nm akbd diff --git a/share/man/man4/man4.powerpc/ams.4 b/share/man/man4/man4.powerpc/ams.4 index 21be3c098920..d7fa922e7307 100644 --- a/share/man/man4/man4.powerpc/ams.4 +++ b/share/man/man4/man4.powerpc/ams.4 @@ -24,7 +24,7 @@ .\" POSSIBILITY OF SUCH DAMAGE. .\" .Dd December 3, 2009 -.Dt AMS 4 +.Dt AMS 4 powerpc .Os .Sh NAME .Nm ams diff --git a/share/man/man4/man4.powerpc/cuda.4 b/share/man/man4/man4.powerpc/cuda.4 index 7171ebb42373..a52b9a447c9d 100644 --- a/share/man/man4/man4.powerpc/cuda.4 +++ b/share/man/man4/man4.powerpc/cuda.4 @@ -24,7 +24,7 @@ .\" POSSIBILITY OF SUCH DAMAGE. .\" .Dd December 3, 2009 -.Dt CUDA 4 +.Dt CUDA 4 powerpc .Os .Sh NAME .Nm cuda diff --git a/share/man/man4/man4.powerpc/dtsec.4 b/share/man/man4/man4.powerpc/dtsec.4 index 4a60dd0b8824..f18de90c4757 100644 --- a/share/man/man4/man4.powerpc/dtsec.4 +++ b/share/man/man4/man4.powerpc/dtsec.4 @@ -24,7 +24,7 @@ .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" .Dd October 31, 2017 -.Dt DTSEC 4 +.Dt DTSEC 4 powerpc .Os .Sh NAME .Nm dtsec diff --git a/share/man/man4/man4.powerpc/llan.4 b/share/man/man4/man4.powerpc/llan.4 index c32ddbca6a00..b78109cac626 100644 --- a/share/man/man4/man4.powerpc/llan.4 +++ b/share/man/man4/man4.powerpc/llan.4 @@ -24,7 +24,7 @@ .\" POSSIBILITY OF SUCH DAMAGE. .\" .Dd February 19, 2015 -.Dt LLAN 4 +.Dt LLAN 4 powerpc .Os .Sh NAME .Nm llan diff --git a/share/man/man4/man4.powerpc/pmu.4 b/share/man/man4/man4.powerpc/pmu.4 index 6eac20cfa6b7..4dfb31f175bd 100644 --- a/share/man/man4/man4.powerpc/pmu.4 +++ b/share/man/man4/man4.powerpc/pmu.4 @@ -24,7 +24,7 @@ .\" POSSIBILITY OF SUCH DAMAGE. .\" .Dd December 6, 2008 -.Dt PMU 4 +.Dt PMU 4 powerpc .Os .Sh NAME .Nm pmu diff --git a/share/man/man4/man4.powerpc/smu.4 b/share/man/man4/man4.powerpc/smu.4 index ef2654746e62..852a08abaa9e 100644 --- a/share/man/man4/man4.powerpc/smu.4 +++ b/share/man/man4/man4.powerpc/smu.4 @@ -24,7 +24,7 @@ .\" POSSIBILITY OF SUCH DAMAGE. .\" .Dd February 22, 2010 -.Dt SMU 4 +.Dt SMU 4 powerpc .Os .Sh NAME .Nm smu diff --git a/share/man/man4/man4.powerpc/snd_ai2s.4 b/share/man/man4/man4.powerpc/snd_ai2s.4 index 3880751e65c9..7a3cd9cb94af 100644 --- a/share/man/man4/man4.powerpc/snd_ai2s.4 +++ b/share/man/man4/man4.powerpc/snd_ai2s.4 @@ -24,7 +24,7 @@ .\" POSSIBILITY OF SUCH DAMAGE. .\" .Dd January 20, 2009 -.Dt SND_AI2S 4 +.Dt SND_AI2S 4 powerpc .Os .Sh NAME .Nm snd_ai2s diff --git a/share/man/man4/man4.powerpc/snd_davbus.4 b/share/man/man4/man4.powerpc/snd_davbus.4 index 6958ebd4b4b5..028225accf52 100644 --- a/share/man/man4/man4.powerpc/snd_davbus.4 +++ b/share/man/man4/man4.powerpc/snd_davbus.4 @@ -24,7 +24,7 @@ .\" POSSIBILITY OF SUCH DAMAGE. .\" .Dd January 20, 2009 -.Dt SND_DAVBUS 4 +.Dt SND_DAVBUS 4 powerpc .Os .Sh NAME .Nm snd_davbus diff --git a/share/man/man4/man4.powerpc/tsec.4 b/share/man/man4/man4.powerpc/tsec.4 index b3ccae648ab8..09510e329ff0 100644 --- a/share/man/man4/man4.powerpc/tsec.4 +++ b/share/man/man4/man4.powerpc/tsec.4 @@ -24,7 +24,7 @@ .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" .Dd February 20, 2015 -.Dt TSEC 4 +.Dt TSEC 4 powerpc .Os .Sh NAME .Nm tsec diff --git a/share/man/man4/nvdimm.4 b/share/man/man4/nvdimm.4 index 5b7dbe435c46..2bec51b42d72 100644 --- a/share/man/man4/nvdimm.4 +++ b/share/man/man4/nvdimm.4 @@ -26,7 +26,7 @@ .\" SUCH DAMAGE. .\" .Dd September 5, 2019 -.Dt NVDIMM 4 +.Dt NVDIMM 4 amd64 .Os .Sh NAME .Nm nvdimm diff --git a/share/man/man4/qlnxe.4 b/share/man/man4/qlnxe.4 index f545235ec1ff..70bad789add1 100644 --- a/share/man/man4/qlnxe.4 +++ b/share/man/man4/qlnxe.4 @@ -24,7 +24,7 @@ .\" SUCH DAMAGE. .\" .Dd May 9, 2017 -.Dt QLNXE 4 +.Dt QLNXE 4 amd64 .Os .Sh NAME .Nm qlnxe diff --git a/share/man/man4/qlxgb.4 b/share/man/man4/qlxgb.4 index 4bf8000d15da..cc97cd060a3f 100644 --- a/share/man/man4/qlxgb.4 +++ b/share/man/man4/qlxgb.4 @@ -24,7 +24,7 @@ .\" SUCH DAMAGE. .\" .Dd November 3, 2011 -.Dt QLXGB 4 +.Dt QLXGB 4 amd64 .Os .Sh NAME .Nm qlxgb diff --git a/share/man/man4/qlxgbe.4 b/share/man/man4/qlxgbe.4 index 486a5ec0f682..465e4fc018ad 100644 --- a/share/man/man4/qlxgbe.4 +++ b/share/man/man4/qlxgbe.4 @@ -24,7 +24,7 @@ .\" SUCH DAMAGE. .\" .Dd April 1, 2013 -.Dt QLXGBE 4 +.Dt QLXGBE 4 amd64 .Os .Sh NAME .Nm qlxgbe diff --git a/share/man/man4/qlxge.4 b/share/man/man4/qlxge.4 index 4723c56ff68b..14a1e1284fab 100644 --- a/share/man/man4/qlxge.4 +++ b/share/man/man4/qlxge.4 @@ -24,7 +24,7 @@ .\" SUCH DAMAGE. .\" .Dd June 21, 2013 -.Dt QLXGE 4 +.Dt QLXGE 4 amd64 .Os .Sh NAME .Nm qlxge diff --git a/share/man/man4/sfxge.4 b/share/man/man4/sfxge.4 index a9724074581e..ea35cf3e573c 100644 --- a/share/man/man4/sfxge.4 +++ b/share/man/man4/sfxge.4 @@ -27,7 +27,7 @@ .\" policies, either expressed or implied, of the FreeBSD Project. .\" .Dd February 22, 2015 -.Dt SFXGE 4 +.Dt SFXGE 4 amd64 .Os .Sh NAME .Nm sfxge diff --git a/share/man/man4/smartpqi.4 b/share/man/man4/smartpqi.4 index 5b7ea923e13e..f5fab85d13bd 100644 --- a/share/man/man4/smartpqi.4 +++ b/share/man/man4/smartpqi.4 @@ -25,7 +25,7 @@ .\" SUCH DAMAGE. .\" .Dd August 24, 2023 -.Dt SMARTPQI 4 +.Dt SMARTPQI 4 amd64 .Os .Sh NAME .Nm smartpqi diff --git a/share/man/man4/sume.4 b/share/man/man4/sume.4 index 219328a4f4c4..b36f924875e6 100644 --- a/share/man/man4/sume.4 +++ b/share/man/man4/sume.4 @@ -25,7 +25,7 @@ .\" POSSIBILITY OF SUCH DAMAGE. .\" .Dd August 30, 2020 -.Dt SUME 4 +.Dt SUME 4 amd64 .Os .Sh NAME .Nm sume diff --git a/share/man/man7/hier.7 b/share/man/man7/hier.7 index 1c69b911f53b..814f5b769be8 100644 --- a/share/man/man7/hier.7 +++ b/share/man/man7/hier.7 @@ -28,7 +28,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd October 10, 2024 +.Dd August 18, 2025 .Dt HIER 7 .Os .Sh NAME @@ -308,6 +308,21 @@ OpenSSH configuration files; see .Xr ssh 1 .It Pa ssl/ OpenSSL configuration files +.Pp +.Bl -tag -width "untrusted/" -compact +.It Pa cert.pem +System trust store in bundle form; see +.Xr certctl 8 . +.It Pa certs/ +System trust store in OpenSSL hashed-directory form; see +.Xr certctl 8 . +.It Pa openssl.cnf +OpenSSL configuration file; see +.Xr openssl.cnf 5 . +.It Pa untrusted/ +Explicitly distrusted certificates; see +.Xr certctl 8 . +.El .It Pa sysctl.conf kernel state defaults; see .Xr sysctl.conf 5 diff --git a/share/mk/bsd.endian.mk b/share/mk/bsd.endian.mk index ba662ffc7439..24da57954b5a 100644 --- a/share/mk/bsd.endian.mk +++ b/share/mk/bsd.endian.mk @@ -20,10 +20,17 @@ LOCALEDEF_ENDIAN= -b # # During bootstrapping on !FreeBSD OSes, we need to define some value. Short of # having an exhaustive list for all variants of Linux and MacOS we simply do not -# set TARGET_ENDIANNESS and poison the other variables. They should be unused -# during the bootstrap phases (apart from one place that's adequately protected -# in bsd.compiler.mk) where we're building the bootstrap tools. +# set TARGET_ENDIANNESS (on Linux) and poison the other variables. They should +# be unused during the bootstrap phases (apart from one place that's adequately +# protected in bsd.compiler.mk) where we're building the bootstrap tools. # +.if ${.MAKE.OS} == "Darwin" +# We do assume the endianness on macOS because Apple's modern hardware is all +# little-endian. This might need revisited in the far future, but for the time +# being Apple Silicon's reign of terror continues. We only set this one up +# because libcrypto is now built in bootstrap. +TARGET_ENDIANNESS= 1234 +.endif CAP_MKDB_ENDIAN= -B # Poisoned value, invalid flags for both cap_mkdb LOCALEDEF_ENDIAN= -B # and localedef. .endif diff --git a/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_ctldir.c b/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_ctldir.c index 3377db7952ef..a222c5de4a2a 100644 --- a/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_ctldir.c +++ b/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_ctldir.c @@ -674,6 +674,7 @@ zfsctl_root_readdir(struct vop_readdir_args *ap) zfs_uio_t uio; int *eofp = ap->a_eofflag; off_t dots_offset; + ssize_t orig_resid; int error; zfs_uio_init(&uio, ap->a_uio); @@ -688,16 +689,16 @@ zfsctl_root_readdir(struct vop_readdir_args *ap) * count to return is 0. */ if (zfs_uio_offset(&uio) == 3 * sizeof (entry)) { + if (eofp != NULL) + *eofp = 1; return (0); } + orig_resid = zfs_uio_resid(&uio); error = sfs_readdir_common(zfsvfs->z_root, ZFSCTL_INO_ROOT, ap, &uio, &dots_offset); - if (error != 0) { - if (error == ENAMETOOLONG) /* ran out of destination space */ - error = 0; - return (error); - } + if (error != 0) + goto err; if (zfs_uio_offset(&uio) != dots_offset) return (SET_ERROR(EINVAL)); @@ -710,8 +711,11 @@ zfsctl_root_readdir(struct vop_readdir_args *ap) entry.d_reclen = sizeof (entry); error = vfs_read_dirent(ap, &entry, zfs_uio_offset(&uio)); if (error != 0) { - if (error == ENAMETOOLONG) - error = 0; +err: + if (error == ENAMETOOLONG) { + error = orig_resid == zfs_uio_resid(&uio) ? + EINVAL : 0; + } return (SET_ERROR(error)); } if (eofp != NULL) @@ -1056,17 +1060,21 @@ zfsctl_snapdir_readdir(struct vop_readdir_args *ap) zfs_uio_t uio; int *eofp = ap->a_eofflag; off_t dots_offset; + ssize_t orig_resid; int error; zfs_uio_init(&uio, ap->a_uio); + orig_resid = zfs_uio_resid(&uio); ASSERT3S(vp->v_type, ==, VDIR); error = sfs_readdir_common(ZFSCTL_INO_ROOT, ZFSCTL_INO_SNAPDIR, ap, &uio, &dots_offset); if (error != 0) { - if (error == ENAMETOOLONG) /* ran out of destination space */ - error = 0; + if (error == ENAMETOOLONG) { /* ran out of destination space */ + error = orig_resid == zfs_uio_resid(&uio) ? + EINVAL : 0; + } return (error); } @@ -1084,9 +1092,13 @@ zfsctl_snapdir_readdir(struct vop_readdir_args *ap) dsl_pool_config_exit(dmu_objset_pool(zfsvfs->z_os), FTAG); if (error != 0) { if (error == ENOENT) { - if (eofp != NULL) - *eofp = 1; - error = 0; + if (orig_resid == zfs_uio_resid(&uio)) { + error = EINVAL; + } else { + error = 0; + if (eofp != NULL) + *eofp = 1; + } } zfs_exit(zfsvfs, FTAG); return (error); @@ -1099,8 +1111,10 @@ zfsctl_snapdir_readdir(struct vop_readdir_args *ap) entry.d_reclen = sizeof (entry); error = vfs_read_dirent(ap, &entry, zfs_uio_offset(&uio)); if (error != 0) { - if (error == ENAMETOOLONG) - error = 0; + if (error == ENAMETOOLONG) { + error = orig_resid == zfs_uio_resid(&uio) ? + EINVAL : 0; + } zfs_exit(zfsvfs, FTAG); return (SET_ERROR(error)); } diff --git a/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vnops_os.c b/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vnops_os.c index 64995f14ee5f..174141a5deab 100644 --- a/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vnops_os.c +++ b/sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vnops_os.c @@ -1695,6 +1695,7 @@ zfs_readdir(vnode_t *vp, zfs_uio_t *uio, cred_t *cr, int *eofp, objset_t *os; caddr_t outbuf; size_t bufsize; + ssize_t orig_resid; zap_cursor_t zc; zap_attribute_t *zap; uint_t bytes_wanted; @@ -1735,7 +1736,7 @@ zfs_readdir(vnode_t *vp, zfs_uio_t *uio, cred_t *cr, int *eofp, /* * Quit if directory has been removed (posix) */ - if ((*eofp = zp->z_unlinked) != 0) { + if ((*eofp = (zp->z_unlinked != 0)) != 0) { zfs_exit(zfsvfs, FTAG); return (0); } @@ -1743,6 +1744,7 @@ zfs_readdir(vnode_t *vp, zfs_uio_t *uio, cred_t *cr, int *eofp, error = 0; os = zfsvfs->z_os; offset = zfs_uio_offset(uio); + orig_resid = zfs_uio_resid(uio); prefetch = zp->z_zn_prefetch; zap = zap_attribute_long_alloc(); @@ -1922,7 +1924,7 @@ update: kmem_free(outbuf, bufsize); if (error == ENOENT) - error = 0; + error = orig_resid == zfs_uio_resid(uio) ? EINVAL : 0; ZFS_ACCESSTIME_STAMP(zfsvfs, zp); diff --git a/sys/dev/acpica/acpi_powerres.c b/sys/dev/acpica/acpi_powerres.c index 29d1690f1bdd..0a8b67a5fa84 100644 --- a/sys/dev/acpica/acpi_powerres.c +++ b/sys/dev/acpica/acpi_powerres.c @@ -76,6 +76,13 @@ struct acpi_powerconsumer { /* Device which is powered */ ACPI_HANDLE ac_consumer; int ac_state; + + struct { + bool prx_has; + size_t prx_count; + ACPI_HANDLE *prx_deps; + } ac_prx[ACPI_D_STATE_COUNT]; + TAILQ_ENTRY(acpi_powerconsumer) ac_link; TAILQ_HEAD(,acpi_powerreference) ac_references; }; @@ -96,9 +103,7 @@ static TAILQ_HEAD(acpi_powerconsumer_list, acpi_powerconsumer) ACPI_SERIAL_DECL(powerres, "ACPI power resources"); static ACPI_STATUS acpi_pwr_register_consumer(ACPI_HANDLE consumer); -#ifdef notyet static ACPI_STATUS acpi_pwr_deregister_consumer(ACPI_HANDLE consumer); -#endif /* notyet */ static ACPI_STATUS acpi_pwr_register_resource(ACPI_HANDLE res); #ifdef notyet static ACPI_STATUS acpi_pwr_deregister_resource(ACPI_HANDLE res); @@ -112,6 +117,8 @@ static struct acpi_powerresource *acpi_pwr_find_resource(ACPI_HANDLE res); static struct acpi_powerconsumer *acpi_pwr_find_consumer(ACPI_HANDLE consumer); +static ACPI_STATUS acpi_pwr_infer_state(struct acpi_powerconsumer *pc); +static ACPI_STATUS acpi_pwr_get_state_locked(ACPI_HANDLE consumer, int *state); /* * Register a power resource. @@ -222,6 +229,84 @@ acpi_pwr_deregister_resource(ACPI_HANDLE res) #endif /* notyet */ /* + * Evaluate the _PRx (power resources each D-state depends on). This also + * populates the acpi_powerresources queue with the power resources discovered + * during this step. + * + * ACPI 7.3.8 - 7.3.11 guarantee that _PRx will return the same data each + * time they are evaluated. + * + * If this function fails, acpi_pwr_deregister_consumer() must be called on the + * power consumer to free already allocated memory. + */ +static ACPI_STATUS +acpi_pwr_get_power_resources(ACPI_HANDLE consumer, struct acpi_powerconsumer *pc) +{ + ACPI_INTEGER status; + ACPI_STRING reslist_name; + ACPI_HANDLE reslist_handle; + ACPI_STRING reslist_names[] = {"_PR0", "_PR1", "_PR2", "_PR3"}; + ACPI_BUFFER reslist; + ACPI_OBJECT *reslist_object; + ACPI_OBJECT *dep; + ACPI_HANDLE *res; + + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + ACPI_SERIAL_ASSERT(powerres); + + MPASS(consumer != NULL); + + for (int state = ACPI_STATE_D0; state <= ACPI_STATE_D3_HOT; state++) { + pc->ac_prx[state].prx_has = false; + pc->ac_prx[state].prx_count = 0; + pc->ac_prx[state].prx_deps = NULL; + + reslist_name = reslist_names[state - ACPI_STATE_D0]; + if (ACPI_FAILURE(AcpiGetHandle(consumer, reslist_name, &reslist_handle))) + continue; + + reslist.Pointer = NULL; + reslist.Length = ACPI_ALLOCATE_BUFFER; + status = AcpiEvaluateObjectTyped(reslist_handle, NULL, NULL, &reslist, + ACPI_TYPE_PACKAGE); + if (ACPI_FAILURE(status) || reslist.Pointer == NULL) + /* + * ACPI_ALLOCATE_BUFFER entails everything will be freed on error + * by AcpiEvaluateObjectTyped. + */ + continue; + + reslist_object = (ACPI_OBJECT *)reslist.Pointer; + pc->ac_prx[state].prx_has = true; + pc->ac_prx[state].prx_count = reslist_object->Package.Count; + + if (reslist_object->Package.Count == 0) { + AcpiOsFree(reslist_object); + continue; + } + + pc->ac_prx[state].prx_deps = mallocarray(pc->ac_prx[state].prx_count, + sizeof(*pc->ac_prx[state].prx_deps), M_ACPIPWR, M_NOWAIT); + if (pc->ac_prx[state].prx_deps == NULL) { + AcpiOsFree(reslist_object); + return_ACPI_STATUS (AE_NO_MEMORY); + } + + for (size_t i = 0; i < reslist_object->Package.Count; i++) { + dep = &reslist_object->Package.Elements[i]; + res = dep->Reference.Handle; + pc->ac_prx[state].prx_deps[i] = res; + + /* It's fine to attempt to register the same resource twice. */ + acpi_pwr_register_resource(res); + } + AcpiOsFree(reslist_object); + } + + return_ACPI_STATUS (AE_OK); +} + +/* * Register a power consumer. * * It's OK to call this if we already know about the consumer. @@ -229,6 +314,7 @@ acpi_pwr_deregister_resource(ACPI_HANDLE res) static ACPI_STATUS acpi_pwr_register_consumer(ACPI_HANDLE consumer) { + ACPI_INTEGER status; struct acpi_powerconsumer *pc; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); @@ -239,14 +325,30 @@ acpi_pwr_register_consumer(ACPI_HANDLE consumer) return_ACPI_STATUS (AE_OK); /* Allocate a new power consumer */ - if ((pc = malloc(sizeof(*pc), M_ACPIPWR, M_NOWAIT)) == NULL) + if ((pc = malloc(sizeof(*pc), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) return_ACPI_STATUS (AE_NO_MEMORY); TAILQ_INSERT_HEAD(&acpi_powerconsumers, pc, ac_link); TAILQ_INIT(&pc->ac_references); pc->ac_consumer = consumer; - /* XXX we should try to find its current state */ - pc->ac_state = ACPI_STATE_UNKNOWN; + /* + * Get all its power resource dependencies, if it has _PRx. We do this now + * as an opportunity to populate the acpi_powerresources queue. + * + * If this fails, immediately deregister it. + */ + status = acpi_pwr_get_power_resources(consumer, pc); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, + "failed to get power resources for %s\n", + acpi_name(consumer))); + acpi_pwr_deregister_consumer(consumer); + return_ACPI_STATUS (status); + } + + /* Find its initial state. */ + if (ACPI_FAILURE(acpi_pwr_get_state_locked(consumer, &pc->ac_state))) + pc->ac_state = ACPI_STATE_UNKNOWN; ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "registered power consumer %s\n", acpi_name(consumer))); @@ -254,7 +356,6 @@ acpi_pwr_register_consumer(ACPI_HANDLE consumer) return_ACPI_STATUS (AE_OK); } -#ifdef notyet /* * Deregister a power consumer. * @@ -279,6 +380,9 @@ acpi_pwr_deregister_consumer(ACPI_HANDLE consumer) /* Pull the consumer off the list and free it */ TAILQ_REMOVE(&acpi_powerconsumers, pc, ac_link); + for (size_t i = 0; i < sizeof(pc->ac_prx) / sizeof(*pc->ac_prx); i++) + if (pc->ac_prx[i].prx_deps != NULL) + free(pc->ac_prx[i].prx_deps, M_ACPIPWR); free(pc, M_ACPIPWR); ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power consumer %s\n", @@ -286,10 +390,139 @@ acpi_pwr_deregister_consumer(ACPI_HANDLE consumer) return_ACPI_STATUS (AE_OK); } -#endif /* notyet */ /* - * Set a power consumer to a particular power state. + * The _PSC control method isn't required if it's possible to infer the D-state + * from the _PRx control methods. (See 7.3.6.) + * We can infer that a given D-state has been achieved when all the dependencies + * are in the ON state. + */ +static ACPI_STATUS +acpi_pwr_infer_state(struct acpi_powerconsumer *pc) +{ + ACPI_HANDLE *res; + uint32_t on; + bool all_on = false; + + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + ACPI_SERIAL_ASSERT(powerres); + + /* It is important we go from the hottest to the coldest state. */ + for ( + pc->ac_state = ACPI_STATE_D0; + pc->ac_state <= ACPI_STATE_D3_HOT && !all_on; + pc->ac_state++ + ) { + MPASS(pc->ac_state <= sizeof(pc->ac_prx) / sizeof(*pc->ac_prx)); + + if (!pc->ac_prx[pc->ac_state].prx_has) + continue; + + all_on = true; + + for (size_t i = 0; i < pc->ac_prx[pc->ac_state].prx_count; i++) { + res = pc->ac_prx[pc->ac_state].prx_deps[i]; + /* If failure, better to assume D-state is hotter than colder. */ + if (ACPI_FAILURE(acpi_GetInteger(res, "_STA", &on))) + continue; + if (on == 0) { + all_on = false; + break; + } + } + } + + MPASS(pc->ac_state != ACPI_STATE_D0); + + /* + * If none of the power resources required for the shallower D-states are + * on, then we can assume it is unpowered (i.e. D3cold). A device is not + * required to support D3cold however; in that case, _PR3 is not explicitly + * provided. Those devices should default to D3hot instead. + * + * See comments of first row of table 7.1 in ACPI spec. + */ + if (!all_on) + pc->ac_state = pc->ac_prx[ACPI_STATE_D3_HOT].prx_has ? + ACPI_STATE_D3_COLD : ACPI_STATE_D3_HOT; + else + pc->ac_state--; + + return_ACPI_STATUS (AE_OK); +} + +static ACPI_STATUS +acpi_pwr_get_state_locked(ACPI_HANDLE consumer, int *state) +{ + struct acpi_powerconsumer *pc; + ACPI_HANDLE method_handle; + ACPI_STATUS status; + ACPI_BUFFER result; + ACPI_OBJECT *object = NULL; + + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + ACPI_SERIAL_ASSERT(powerres); + + if (consumer == NULL) + return_ACPI_STATUS (AE_NOT_FOUND); + + if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { + if (ACPI_FAILURE(status = acpi_pwr_register_consumer(consumer))) + goto out; + if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) + panic("acpi added power consumer but can't find it"); + } + + status = AcpiGetHandle(consumer, "_PSC", &method_handle); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "no _PSC object - %s\n", + AcpiFormatException(status))); + status = acpi_pwr_infer_state(pc); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't infer D-state - %s\n", + AcpiFormatException(status))); + pc->ac_state = ACPI_STATE_UNKNOWN; + } + goto out; + } + + result.Pointer = NULL; + result.Length = ACPI_ALLOCATE_BUFFER; + status = AcpiEvaluateObjectTyped(method_handle, NULL, NULL, &result, ACPI_TYPE_INTEGER); + if (ACPI_FAILURE(status) || result.Pointer == NULL) { + ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to get state with _PSC - %s\n", + AcpiFormatException(status))); + pc->ac_state = ACPI_STATE_UNKNOWN; + goto out; + } + + object = (ACPI_OBJECT *)result.Pointer; + pc->ac_state = ACPI_STATE_D0 + object->Integer.Value; + status = AE_OK; + +out: + if (object != NULL) + AcpiOsFree(object); + *state = pc->ac_state; + return_ACPI_STATUS (status); +} + +/* + * Get a power consumer's D-state. + */ +ACPI_STATUS +acpi_pwr_get_state(ACPI_HANDLE consumer, int *state) +{ + ACPI_STATUS res; + + ACPI_SERIAL_BEGIN(powerres); + res = acpi_pwr_get_state_locked(consumer, state); + ACPI_SERIAL_END(powerres); + return (res); +} + +/* + * Set a power consumer to a particular D-state. */ ACPI_STATUS acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state) @@ -300,6 +533,7 @@ acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state) ACPI_OBJECT *reslist_object; ACPI_STATUS status; char *method_name, *reslist_name = NULL; + int new_state; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); @@ -501,8 +735,28 @@ acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state) } } - /* Transition was successful */ - pc->ac_state = state; + /* + * Make sure the transition succeeded. If getting new state failed, + * just assume the new state is what we wanted. This was the behaviour + * before we were checking D-states. + */ + if (ACPI_FAILURE(acpi_pwr_get_state_locked(consumer, &new_state))) { + printf("%s: failed to get new D-state\n", __func__); + pc->ac_state = state; + } else { + if (new_state != state) + printf("%s: new power state %s is not the one requested %s\n", + __func__, acpi_d_state_to_str(new_state), + acpi_d_state_to_str(state)); + pc->ac_state = new_state; + } + + /* + * We consider the transition successful even if the state we got doesn't + * reflect what we set it to. This is because we weren't previously + * checking the new state at all, so there might exist buggy platforms on + * which suspend would otherwise succeed if we failed here. + */ status = AE_OK; out: diff --git a/sys/dev/acpica/acpivar.h b/sys/dev/acpica/acpivar.h index 6887f080311d..7495a010432b 100644 --- a/sys/dev/acpica/acpivar.h +++ b/sys/dev/acpica/acpivar.h @@ -490,6 +490,7 @@ EVENTHANDLER_DECLARE(acpi_video_event, acpi_event_handler_t); /* Device power control. */ ACPI_STATUS acpi_pwr_wake_enable(ACPI_HANDLE consumer, int enable); +ACPI_STATUS acpi_pwr_get_state(ACPI_HANDLE consumer, int *state); ACPI_STATUS acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state); acpi_pwr_for_sleep_t acpi_device_pwr_for_sleep; int acpi_set_powerstate(device_t child, int state); diff --git a/sys/netinet/ip_icmp.c b/sys/netinet/ip_icmp.c index 543b846fbba5..fc0848b2c944 100644 --- a/sys/netinet/ip_icmp.c +++ b/sys/netinet/ip_icmp.c @@ -391,7 +391,6 @@ stdreply: icmpelen = max(8, min(V_icmp_quotelen, ntohs(oip->ip_len) - nip->ip_hl = 5; nip->ip_p = IPPROTO_ICMP; nip->ip_tos = 0; - nip->ip_off = 0; if (V_error_keeptags) m_tag_copy_chain(m, n, M_NOWAIT); @@ -872,6 +871,8 @@ match: mac_netinet_icmp_replyinplace(m); #endif ip->ip_src = t; + /* ip->ip_tos will be reflected. */ + ip->ip_off = htons(0); ip->ip_ttl = V_ip_defttl; if (optlen > 0) { diff --git a/sys/netlink/route/iface_drivers.c b/sys/netlink/route/iface_drivers.c index 4bf913d9c978..5f605b05f7b8 100644 --- a/sys/netlink/route/iface_drivers.c +++ b/sys/netlink/route/iface_drivers.c @@ -82,9 +82,12 @@ _nl_modify_ifp_generic(struct ifnet *ifp, struct nl_parsed_link *lattrs, } } - if ((lattrs->ifi_change & IFF_UP) && (lattrs->ifi_flags & IFF_UP) == 0) { - /* Request to down the interface */ - if_down(ifp); + if ((lattrs->ifi_change & IFF_UP) != 0 || lattrs->ifi_change == 0) { + /* Request to up or down the interface */ + if (lattrs->ifi_flags & IFF_UP) + if_up(ifp); + else + if_down(ifp); } if (lattrs->ifla_mtu > 0) { @@ -97,7 +100,8 @@ _nl_modify_ifp_generic(struct ifnet *ifp, struct nl_parsed_link *lattrs, } } - if (lattrs->ifi_change & IFF_PROMISC) { + if ((lattrs->ifi_change & IFF_PROMISC) != 0 || + lattrs->ifi_change == 0) { error = ifpromisc(ifp, lattrs->ifi_flags & IFF_PROMISC); if (error != 0) { nlmsg_report_err_msg(npt, "unable to set promisc"); diff --git a/sys/sys/param.h b/sys/sys/param.h index 915bfe1abfcd..fc2a78883f1e 100644 --- a/sys/sys/param.h +++ b/sys/sys/param.h @@ -74,7 +74,7 @@ * cannot include sys/param.h and should only be updated here. */ #undef __FreeBSD_version -#define __FreeBSD_version 1500062 +#define __FreeBSD_version 1500063 /* * __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD, diff --git a/tools/build/Makefile b/tools/build/Makefile index 83f589ce3864..3c4e07e3cfc2 100644 --- a/tools/build/Makefile +++ b/tools/build/Makefile @@ -495,6 +495,7 @@ INSTALLDIR_LIST= \ bin \ lib/geom \ usr/include/casper \ + usr/include/openssl \ usr/include/private/ucl \ usr/include/private/zstd \ usr/lib \ diff --git a/tools/build/mk/OptionalObsoleteFiles.inc b/tools/build/mk/OptionalObsoleteFiles.inc index b30604238855..dcd606feea1d 100644 --- a/tools/build/mk/OptionalObsoleteFiles.inc +++ b/tools/build/mk/OptionalObsoleteFiles.inc @@ -1709,6 +1709,8 @@ OLD_FILES+=usr/share/examples/hostapd/hostapd.conf OLD_FILES+=usr/share/examples/hostapd/hostapd.eap_user OLD_FILES+=usr/share/examples/hostapd/hostapd.wpa_psk OLD_FILES+=usr/share/examples/indent/indent.pro +OLD_FILES+=usr/share/examples/inotify/Makefile +OLD_FILES+=usr/share/examples/inotify/inotify.c OLD_FILES+=usr/share/examples/ipfilter/BASIC.NAT OLD_FILES+=usr/share/examples/ipfilter/BASIC_1.FW OLD_FILES+=usr/share/examples/ipfilter/BASIC_2.FW @@ -1921,6 +1923,7 @@ OLD_DIRS+=usr/share/examples/hast OLD_DIRS+=usr/share/examples/ibcs2 OLD_DIRS+=usr/share/examples/hostapd OLD_DIRS+=usr/share/examples/indent +OLD_DIRS+=usr/share/examples/inotify OLD_DIRS+=usr/share/examples/ipfilter OLD_DIRS+=usr/share/examples/ipfw OLD_DIRS+=usr/share/examples/jails diff --git a/usr.sbin/certctl/Makefile b/usr.sbin/certctl/Makefile index 88c024daf7e6..6900f0ce3b65 100644 --- a/usr.sbin/certctl/Makefile +++ b/usr.sbin/certctl/Makefile @@ -1,5 +1,14 @@ +.include <src.opts.mk> + PACKAGE= certctl -SCRIPTS=certctl.sh +PROG= certctl MAN= certctl.8 +LIBADD= crypto +HAS_TESTS= +SUBDIR.${MK_TESTS}= tests + +.ifdef BOOTSTRAPPING +CFLAGS+=-DBOOTSTRAPPING +.endif .include <bsd.prog.mk> diff --git a/usr.sbin/certctl/certctl.8 b/usr.sbin/certctl/certctl.8 index 7e49bb89e2ac..edf993e1361a 100644 --- a/usr.sbin/certctl/certctl.8 +++ b/usr.sbin/certctl/certctl.8 @@ -24,7 +24,7 @@ .\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd July 17, 2025 +.Dd August 18, 2025 .Dt CERTCTL 8 .Os .Sh NAME @@ -32,63 +32,85 @@ .Nd "tool for managing trusted and untrusted TLS certificates" .Sh SYNOPSIS .Nm -.Op Fl v +.Op Fl lv .Ic list .Nm -.Op Fl v +.Op Fl lv .Ic untrusted .Nm -.Op Fl cnUv +.Op Fl BnUv .Op Fl D Ar destdir .Op Fl M Ar metalog .Ic rehash .Nm -.Op Fl cnv -.Ic untrust Ar file +.Op Fl nv +.Ic untrust Ar .Nm -.Op Fl cnv -.Ic trust Ar file +.Op Fl nv +.Ic trust Ar .Sh DESCRIPTION The .Nm utility manages the list of TLS Certificate Authorities that are trusted by applications that use OpenSSL. .Pp -Flags: +The following options are available: .Bl -tag -width 4n -.It Fl c -Copy certificates instead of linking to them. +.It Fl B +Do not generate a bundle. +This option is only valid in conjunction with the +.Ic rehash +command. .It Fl D Ar destdir Specify the DESTDIR (overriding values from the environment). .It Fl d Ar distbase Specify the DISTBASE (overriding values from the environment). +.It Fl l +When listing installed (trusted or untrusted) certificates, show the +full path and distinguished name for each certificate. .It Fl M Ar metalog -Specify the path of the METALOG file (default: $DESTDIR/METALOG). +Specify the path of the METALOG file +.Po +default: +.Pa ${DESTDIR}/METALOG +.Pc . +This option is only valid in conjunction with the +.Ic rehash +command. .It Fl n -No-Op mode, do not actually perform any actions. +Dry-run mode. +Do not actually perform any actions except write the metalog. .It Fl v -Be verbose, print details about actions before performing them. +Verbose mode. +Print detailed information about each action taken. .It Fl U -Unprivileged mode, do not change the ownership of created links. -Do record the ownership in the METALOG file. +Unprivileged mode. +Do not attempt to set the ownership of created files. +This option is only valid in conjunction with the +.Fl M +option and the +.Ic rehash +command. .El .Pp Primary command functions: .Bl -tag -width untrusted .It Ic list -List all currently trusted certificate authorities. +List all currently trusted certificates. .It Ic untrusted List all currently untrusted certificates. .It Ic rehash -Rebuild the list of trusted certificate authorities by scanning all directories +Rebuild the list of trusted certificates by scanning all directories in .Ev TRUSTPATH and all untrusted certificates in .Ev UNTRUSTPATH . -A symbolic link to each trusted certificate is placed in +A copy of each trusted certificate is placed in .Ev CERTDESTDIR and each untrusted certificate in .Ev UNTRUSTDESTDIR . +In addition, a bundle containing the trusted certificates is placed in +.Ev BUNDLEFILE . .It Ic untrust Add the specified file to the untrusted list. .It Ic trust @@ -97,9 +119,13 @@ Remove the specified file from the untrusted list. .Sh ENVIRONMENT .Bl -tag -width UNTRUSTDESTDIR .It Ev DESTDIR -Alternate destination directory to operate on. +Absolute path to an alternate destination directory to operate on +instead of the file system root, e.g. +.Dq Li /tmp/install . .It Ev DISTBASE Additional path component to include when operating on certificate directories. +This must start with a slash, e.g. +.Dq Li /base . .It Ev LOCALBASE Location for local programs. Defaults to the value of the user.localbase sysctl which is usually @@ -107,32 +133,34 @@ Defaults to the value of the user.localbase sysctl which is usually .It Ev TRUSTPATH List of paths to search for trusted certificates. Default: -.Pa <DESTDIR><DISTBASE>/usr/share/certs/trusted -.Pa <DESTDIR><DISTBASE>/usr/local/share/certs -.Pa <DESTDIR><DISTBASE><LOCALBASE>/etc/ssl/certs +.Pa ${DESTDIR}${DISTBASE}/usr/share/certs/trusted +.Pa ${DESTDIR}${LOCALBASE}/share/certs/trusted +.Pa ${DESTDIR}${LOCALBASE}/share/certs .It Ev UNTRUSTPATH List of paths to search for untrusted certificates. Default: -.Pa <DESTDIR><DISTBASE>/usr/share/certs/untrusted -.Pa <DESTDIR><DISTBASE><LOCALBASE>/etc/ssl/untrusted -.Pa <DESTDIR><DISTBASE><LOCALBASE>/etc/ssl/blacklisted -.It Ev CERTDESTDIR +.Pa ${DESTDIR}${DISTBASE}/usr/share/certs/untrusted +.Pa ${DESTDIR}${LOCALBASE}/share/certs/untrusted +.It Ev TRUSTDESTDIR Destination directory for symbolic links to trusted certificates. Default: -.Pa <DESTDIR><DISTBASE>/etc/ssl/certs +.Pa ${DESTDIR}${DISTBASE}/etc/ssl/certs .It Ev UNTRUSTDESTDIR Destination directory for symbolic links to untrusted certificates. Default: -.Pa <DESTDIR><DISTBASE>/etc/ssl/untrusted -.It Ev EXTENSIONS -List of file extensions to read as certificate files. -Default: *.pem *.crt *.cer *.crl *.0 +.Pa ${DESTDIR}${DISTBASE}/etc/ssl/untrusted +.It Ev BUNDLE +File name of bundle to produce. .El .Sh SEE ALSO .Xr openssl 1 .Sh HISTORY .Nm first appeared in -.Fx 12.2 +.Fx 12.2 . .Sh AUTHORS -.An Allan Jude Aq Mt allanjude@freebsd.org +.An -nosplit +The original shell implementation was written by +.An Allan Jude Aq Mt allanjude@FreeBSD.org . +The current C implementation was written by +.An Dag-Erling Sm\(/orgrav Aq Mt des@FreeBSD.org . diff --git a/usr.sbin/certctl/certctl.c b/usr.sbin/certctl/certctl.c new file mode 100644 index 000000000000..ed7f05126ca7 --- /dev/null +++ b/usr.sbin/certctl/certctl.c @@ -0,0 +1,1114 @@ +/*- + * Copyright (c) 2023-2025 Dag-Erling Smørgrav <des@FreeBSD.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <sys/sysctl.h> +#include <sys/stat.h> +#include <sys/tree.h> + +#include <dirent.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <fts.h> +#include <paths.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <openssl/ssl.h> + +#define info(fmt, ...) \ + do { \ + if (verbose) \ + fprintf(stderr, fmt "\n", ##__VA_ARGS__); \ + } while (0) + +static char * +xasprintf(const char *fmt, ...) +{ + va_list ap; + char *str; + int ret; + + va_start(ap, fmt); + ret = vasprintf(&str, fmt, ap); + va_end(ap); + if (ret < 0 || str == NULL) + err(1, NULL); + return (str); +} + +static char * +xstrdup(const char *str) +{ + char *dup; + + if ((dup = strdup(str)) == NULL) + err(1, NULL); + return (dup); +} + +static void usage(void); + +static bool dryrun; +static bool longnames; +static bool nobundle; +static bool unprivileged; +static bool verbose; + +static const char *localbase; +static const char *destdir; +static const char *distbase; +static const char *metalog; + +static const char *uname = "root"; +static const char *gname = "wheel"; + +static const char *const default_trusted_paths[] = { + "/usr/share/certs/trusted", + "%L/share/certs/trusted", + "%L/share/certs", + NULL +}; +static char **trusted_paths; + +static const char *const default_untrusted_paths[] = { + "/usr/share/certs/untrusted", + "%L/share/certs/untrusted", + NULL +}; +static char **untrusted_paths; + +static char *trusted_dest; +static char *untrusted_dest; +static char *bundle_dest; + +#define SSL_PATH "/etc/ssl" +#define TRUSTED_DIR "certs" +#define TRUSTED_PATH SSL_PATH "/" TRUSTED_DIR +#define UNTRUSTED_DIR "untrusted" +#define UNTRUSTED_PATH SSL_PATH "/" UNTRUSTED_DIR +#define LEGACY_DIR "blacklisted" +#define LEGACY_PATH SSL_PATH "/" LEGACY_DIR +#define BUNDLE_FILE "cert.pem" +#define BUNDLE_PATH SSL_PATH "/" BUNDLE_FILE + +static FILE *mlf; + +/* + * Remove duplicate and trailing slashes from a path. + */ +static char * +normalize_path(const char *str) +{ + char *buf, *dst; + + if ((buf = malloc(strlen(str) + 1)) == NULL) + err(1, NULL); + for (dst = buf; *str != '\0'; dst++) { + if ((*dst = *str++) == '/') { + while (*str == '/') + str++; + if (*str == '\0') + break; + } + } + *dst = '\0'; + return (buf); +} + +/* + * Split a colon-separated list into a NULL-terminated array. + */ +static char ** +split_paths(const char *str) +{ + char **paths; + const char *p, *q; + unsigned int i, n; + + for (p = str, n = 1; *p; p++) { + if (*p == ':') + n++; + } + if ((paths = calloc(n + 1, sizeof(*paths))) == NULL) + err(1, NULL); + for (p = q = str, i = 0; i < n; i++, p = q + 1) { + q = strchrnul(p, ':'); + if ((paths[i] = strndup(p, q - p)) == NULL) + err(1, NULL); + } + return (paths); +} + +/* + * Expand %L into LOCALBASE and prefix DESTDIR and DISTBASE as needed. + */ +static char * +expand_path(const char *template) +{ + if (template[0] == '%' && template[1] == 'L') + return (xasprintf("%s%s%s", destdir, localbase, template + 2)); + return (xasprintf("%s%s%s", destdir, distbase, template)); +} + +/* + * Expand an array of paths. + */ +static char ** +expand_paths(const char *const *templates) +{ + char **paths; + unsigned int i, n; + + for (n = 0; templates[n] != NULL; n++) + continue; + if ((paths = calloc(n + 1, sizeof(*paths))) == NULL) + err(1, NULL); + for (i = 0; i < n; i++) + paths[i] = expand_path(templates[i]); + return (paths); +} + +/* + * If destdir is a prefix of path, returns a pointer to the rest of path, + * otherwise returns path. + * + * Note that this intentionally does not strip distbase from the path! + * Unlike destdir, distbase is expected to be included in the metalog. + */ +static const char * +unexpand_path(const char *path) +{ + const char *p = path; + const char *q = destdir; + + while (*p && *p == *q) { + p++; + q++; + } + return (*q == '\0' && *p == '/' ? p : path); +} + +/* + * X509 certificate in a rank-balanced tree. + */ +struct cert { + RB_ENTRY(cert) entry; + unsigned long hash; + char *name; + X509 *x509; + char *path; +}; + +static void +free_cert(struct cert *cert) +{ + free(cert->name); + X509_free(cert->x509); + free(cert->path); + free(cert); +} + +static int +certcmp(const struct cert *a, const struct cert *b) +{ + return (X509_cmp(a->x509, b->x509)); +} + +RB_HEAD(cert_tree, cert); +static struct cert_tree trusted = RB_INITIALIZER(&trusted); +static struct cert_tree untrusted = RB_INITIALIZER(&untrusted); +RB_GENERATE_STATIC(cert_tree, cert, entry, certcmp); + +static void +free_certs(struct cert_tree *tree) +{ + struct cert *cert, *tmp; + + RB_FOREACH_SAFE(cert, cert_tree, tree, tmp) { + RB_REMOVE(cert_tree, tree, cert); + free_cert(cert); + } +} + +static struct cert * +find_cert(struct cert_tree *haystack, X509 *x509) +{ + struct cert needle = { .x509 = x509 }; + + return (RB_FIND(cert_tree, haystack, &needle)); +} + +/* + * File containing a certificate in a rank-balanced tree sorted by + * certificate hash and disambiguating counter. This is needed because + * the certificate hash function is prone to collisions, necessitating a + * counter to distinguish certificates that hash to the same value. + */ +struct file { + RB_ENTRY(file) entry; + const struct cert *cert; + unsigned int c; +}; + +static int +filecmp(const struct file *a, const struct file *b) +{ + if (a->cert->hash > b->cert->hash) + return (1); + if (a->cert->hash < b->cert->hash) + return (-1); + return (a->c - b->c); +} + +RB_HEAD(file_tree, file); +RB_GENERATE_STATIC(file_tree, file, entry, filecmp); + +/* + * Lexicographical sort for scandir(). + */ +static int +lexisort(const struct dirent **d1, const struct dirent **d2) +{ + return (strcmp((*d1)->d_name, (*d2)->d_name)); +} + +/* + * Read certificate(s) from a single file and insert them into a tree. + * Ignore certificates that already exist in the tree. If exclude is not + * null, also ignore certificates that exist in exclude. + * + * Returns the number certificates added to the tree, or -1 on failure. + */ +static int +read_cert(const char *path, struct cert_tree *tree, struct cert_tree *exclude) +{ + FILE *f; + X509 *x509; + X509_NAME *name; + struct cert *cert; + unsigned long hash; + int len, ni, no; + + if ((f = fopen(path, "r")) == NULL) { + warn("%s", path); + return (-1); + } + for (ni = no = 0; + (x509 = PEM_read_X509(f, NULL, NULL, NULL)) != NULL; + ni++) { + hash = X509_subject_name_hash(x509); + if (exclude && find_cert(exclude, x509)) { + info("%08lx: excluded", hash); + X509_free(x509); + continue; + } + if (find_cert(tree, x509)) { + info("%08lx: duplicate", hash); + X509_free(x509); + continue; + } + if ((cert = calloc(1, sizeof(*cert))) == NULL) + err(1, NULL); + cert->x509 = x509; + name = X509_get_subject_name(x509); + cert->hash = X509_NAME_hash_ex(name, NULL, NULL, NULL); + len = X509_NAME_get_text_by_NID(name, NID_commonName, + NULL, 0); + if (len > 0) { + if ((cert->name = malloc(len + 1)) == NULL) + err(1, NULL); + X509_NAME_get_text_by_NID(name, NID_commonName, + cert->name, len + 1); + } else { + /* fallback for certificates without CN */ + cert->name = X509_NAME_oneline(name, NULL, 0); + } + cert->path = xstrdup(unexpand_path(path)); + if (RB_INSERT(cert_tree, tree, cert) != NULL) + errx(1, "unexpected duplicate"); + info("%08lx: %s", cert->hash, cert->name); + no++; + } + /* + * ni is the number of certificates we found in the file. + * no is the number of certificates that weren't already in our + * tree or on the exclusion list. + */ + if (ni == 0) + warnx("%s: no valid certificates found", path); + fclose(f); + return (no); +} + +/* + * Load all certificates found in the specified path into a tree, + * optionally excluding those that already exist in a different tree. + * + * Returns the number of certificates added to the tree, or -1 on failure. + */ +static int +read_certs(const char *path, struct cert_tree *tree, struct cert_tree *exclude) +{ + struct stat sb; + char *paths[] = { (char *)(uintptr_t)path, NULL }; + FTS *fts; + FTSENT *ent; + int fts_options = FTS_LOGICAL | FTS_NOCHDIR; + int ret, total = 0; + + if (stat(path, &sb) != 0) { + return (-1); + } else if (!S_ISDIR(sb.st_mode)) { + errno = ENOTDIR; + return (-1); + } + if ((fts = fts_open(paths, fts_options, NULL)) == NULL) + err(1, "fts_open()"); + while ((ent = fts_read(fts)) != NULL) { + if (ent->fts_info != FTS_F) { + if (ent->fts_info == FTS_ERR) + warnc(ent->fts_errno, "fts_read()"); + continue; + } + info("found %s", ent->fts_path); + ret = read_cert(ent->fts_path, tree, exclude); + if (ret > 0) + total += ret; + } + fts_close(fts); + return (total); +} + +/* + * Save the contents of a cert tree to disk. + * + * Returns 0 on success and -1 on failure. + */ +static int +write_certs(const char *dir, struct cert_tree *tree) +{ + struct file_tree files = RB_INITIALIZER(&files); + struct cert *cert; + struct file *file, *tmp; + struct dirent **dents, **ent; + char *path, *tmppath = NULL; + FILE *f; + mode_t mode = 0444; + int cmp, d, fd, ndents, ret = 0; + + /* + * Start by generating unambiguous file names for each certificate + * and storing them in lexicographical order + */ + RB_FOREACH(cert, cert_tree, tree) { + if ((file = calloc(1, sizeof(*file))) == NULL) + err(1, NULL); + file->cert = cert; + for (file->c = 0; file->c < INT_MAX; file->c++) + if (RB_INSERT(file_tree, &files, file) == NULL) + break; + if (file->c == INT_MAX) + errx(1, "unable to disambiguate %08lx", cert->hash); + free(cert->path); + cert->path = xasprintf("%08lx.%d", cert->hash, file->c); + } + /* + * Open and scan the directory. + */ + if ((d = open(dir, O_DIRECTORY | O_RDONLY)) < 0 || +#ifdef BOOTSTRAPPING + (ndents = scandir(dir, &dents, NULL, lexisort)) +#else + (ndents = fdscandir(d, &dents, NULL, lexisort)) +#endif + < 0) + err(1, "%s", dir); + /* + * Iterate over the directory listing and the certificate listing + * in parallel. If the directory listing gets ahead of the + * certificate listing, we need to write the current certificate + * and advance the certificate listing. If the certificate + * listing is ahead of the directory listing, we need to delete + * the current file and advance the directory listing. If they + * are neck and neck, we have a match and could in theory compare + * the two, but in practice it's faster to just replace the + * current file with the current certificate (and advance both). + */ + ent = dents; + file = RB_MIN(file_tree, &files); + for (;;) { + if (ent < dents + ndents) { + /* skip directories */ + if ((*ent)->d_type == DT_DIR) { + free(*ent++); + continue; + } + if (file != NULL) { + /* compare current dirent to current cert */ + path = file->cert->path; + cmp = strcmp((*ent)->d_name, path); + } else { + /* trailing files in directory */ + path = NULL; + cmp = -1; + } + } else { + if (file != NULL) { + /* trailing certificates */ + path = file->cert->path; + cmp = 1; + } else { + /* end of both lists */ + path = NULL; + break; + } + } + if (cmp < 0) { + /* a file on disk with no matching certificate */ + info("removing %s/%s", dir, (*ent)->d_name); + if (!dryrun) + (void)unlinkat(d, (*ent)->d_name, 0); + free(*ent++); + continue; + } + if (cmp == 0) { + /* a file on disk with a matching certificate */ + info("replacing %s/%s", dir, (*ent)->d_name); + if (dryrun) { + fd = open(_PATH_DEVNULL, O_WRONLY); + } else { + tmppath = xasprintf(".%s", path); + fd = openat(d, tmppath, + O_CREAT | O_WRONLY | O_TRUNC, mode); + if (!unprivileged && fd >= 0) + (void)fchmod(fd, mode); + } + free(*ent++); + } else { + /* a certificate with no matching file */ + info("writing %s/%s", dir, path); + if (dryrun) { + fd = open(_PATH_DEVNULL, O_WRONLY); + } else { + tmppath = xasprintf(".%s", path); + fd = openat(d, tmppath, + O_CREAT | O_WRONLY | O_EXCL, mode); + } + } + /* write the certificate */ + if (fd < 0 || + (f = fdopen(fd, "w")) == NULL || + !PEM_write_X509(f, file->cert->x509)) { + if (tmppath != NULL && fd >= 0) { + int serrno = errno; + (void)unlinkat(d, tmppath, 0); + errno = serrno; + } + err(1, "%s/%s", dir, tmppath ? tmppath : path); + } + /* rename temp file if applicable */ + if (tmppath != NULL) { + if (ret == 0 && renameat(d, tmppath, d, path) != 0) { + warn("%s/%s", dir, path); + ret = -1; + } + if (ret != 0) + (void)unlinkat(d, tmppath, 0); + free(tmppath); + tmppath = NULL; + } + fflush(f); + /* emit metalog */ + if (mlf != NULL) { + fprintf(mlf, ".%s/%s type=file " + "uname=%s gname=%s mode=%#o size=%ld\n", + unexpand_path(dir), path, + uname, gname, mode, ftell(f)); + } + fclose(f); + /* advance certificate listing */ + tmp = RB_NEXT(file_tree, &files, file); + RB_REMOVE(file_tree, &files, file); + free(file); + file = tmp; + } + free(dents); + close(d); + return (ret); +} + +/* + * Save all certs in a tree to a single file (bundle). + * + * Returns 0 on success and -1 on failure. + */ +static int +write_bundle(const char *dir, const char *file, struct cert_tree *tree) +{ + struct cert *cert; + char *tmpfile = NULL; + FILE *f; + int d, fd, ret = 0; + mode_t mode = 0444; + + if (dir != NULL) { + if ((d = open(dir, O_DIRECTORY | O_RDONLY)) < 0) + err(1, "%s", dir); + } else { + dir = "."; + d = AT_FDCWD; + } + info("writing %s/%s", dir, file); + if (dryrun) { + fd = open(_PATH_DEVNULL, O_WRONLY); + } else { + tmpfile = xasprintf(".%s", file); + fd = openat(d, tmpfile, O_WRONLY | O_CREAT | O_EXCL, mode); + } + if (fd < 0 || (f = fdopen(fd, "w")) == NULL) { + if (tmpfile != NULL && fd >= 0) { + int serrno = errno; + (void)unlinkat(d, tmpfile, 0); + errno = serrno; + } + err(1, "%s/%s", dir, tmpfile ? tmpfile : file); + } + RB_FOREACH(cert, cert_tree, tree) { + if (!PEM_write_X509(f, cert->x509)) { + warn("%s/%s", dir, tmpfile ? tmpfile : file); + ret = -1; + break; + } + } + if (tmpfile != NULL) { + if (ret == 0 && renameat(d, tmpfile, d, file) != 0) { + warn("%s/%s", dir, file); + ret = -1; + } + if (ret != 0) + (void)unlinkat(d, tmpfile, 0); + free(tmpfile); + } + if (ret == 0 && mlf != NULL) { + fprintf(mlf, + ".%s/%s type=file uname=%s gname=%s mode=%#o size=%ld\n", + unexpand_path(dir), file, uname, gname, mode, ftell(f)); + } + fclose(f); + if (d != AT_FDCWD) + close(d); + return (ret); +} + +/* + * Load trusted certificates. + * + * Returns the number of certificates loaded. + */ +static unsigned int +load_trusted(bool all, struct cert_tree *exclude) +{ + unsigned int i, n; + int ret; + + /* load external trusted certs */ + for (i = n = 0; all && trusted_paths[i] != NULL; i++) { + ret = read_certs(trusted_paths[i], &trusted, exclude); + if (ret > 0) + n += ret; + } + + /* load installed trusted certs */ + ret = read_certs(trusted_dest, &trusted, exclude); + if (ret > 0) + n += ret; + + info("%d trusted certificates found", n); + return (n); +} + +/* + * Load untrusted certificates. + * + * Returns the number of certificates loaded. + */ +static unsigned int +load_untrusted(bool all) +{ + char *path; + unsigned int i, n; + int ret; + + /* load external untrusted certs */ + for (i = n = 0; all && untrusted_paths[i] != NULL; i++) { + ret = read_certs(untrusted_paths[i], &untrusted, NULL); + if (ret > 0) + n += ret; + } + + /* load installed untrusted certs */ + ret = read_certs(untrusted_dest, &untrusted, NULL); + if (ret > 0) + n += ret; + + /* load legacy untrusted certs */ + path = expand_path(LEGACY_PATH); + ret = read_certs(path, &untrusted, NULL); + if (ret > 0) { + warnx("certificates found in legacy directory %s", + path); + n += ret; + } else if (ret == 0) { + warnx("legacy directory %s can safely be deleted", + path); + } + free(path); + + info("%d untrusted certificates found", n); + return (n); +} + +/* + * Save trusted certificates. + * + * Returns 0 on success and -1 on failure. + */ +static int +save_trusted(void) +{ + int ret; + + /* save untrusted certs */ + ret = write_certs(trusted_dest, &trusted); + return (ret); +} + +/* + * Save untrusted certificates. + * + * Returns 0 on success and -1 on failure. + */ +static int +save_untrusted(void) +{ + int ret; + + ret = write_certs(untrusted_dest, &untrusted); + return (ret); +} + +/* + * Save certificate bundle. + * + * Returns 0 on success and -1 on failure. + */ +static int +save_bundle(void) +{ + char *dir, *file, *sep; + int ret; + + if ((sep = strrchr(bundle_dest, '/')) == NULL) { + dir = NULL; + file = bundle_dest; + } else { + dir = xasprintf("%.*s", (int)(sep - bundle_dest), bundle_dest); + file = sep + 1; + } + ret = write_bundle(dir, file, &trusted); + free(dir); + return (ret); +} + +/* + * Save everything. + * + * Returns 0 on success and -1 on failure. + */ +static int +save_all(void) +{ + int ret = 0; + + ret |= save_untrusted(); + ret |= save_trusted(); + if (!nobundle) + ret |= save_bundle(); + return (ret); +} + +/* + * List the contents of a certificate tree. + */ +static void +list_certs(struct cert_tree *tree) +{ + struct cert *cert; + char *path, *name; + + RB_FOREACH(cert, cert_tree, tree) { + path = longnames ? NULL : strrchr(cert->path, '/'); + name = longnames ? NULL : strrchr(cert->name, '='); + printf("%s\t%s\n", path ? path + 1 : cert->path, + name ? name + 1 : cert->name); + } +} + +/* + * Load installed trusted certificates, then list them. + * + * Returns 0 on success and -1 on failure. + */ +static int +certctl_list(int argc, char **argv __unused) +{ + if (argc > 1) + usage(); + /* load trusted certificates */ + load_trusted(false, NULL); + /* list them */ + list_certs(&trusted); + free_certs(&trusted); + return (0); +} + +/* + * Load installed untrusted certificates, then list them. + * + * Returns 0 on success and -1 on failure. + */ +static int +certctl_untrusted(int argc, char **argv __unused) +{ + if (argc > 1) + usage(); + /* load untrusted certificates */ + load_untrusted(false); + /* list them */ + list_certs(&untrusted); + free_certs(&untrusted); + return (0); +} + +/* + * Load trusted and untrusted certificates from all sources, then + * regenerate both the hashed directories and the bundle. + * + * Returns 0 on success and -1 on failure. + */ +static int +certctl_rehash(int argc, char **argv __unused) +{ + int ret; + + if (argc > 1) + usage(); + + if (unprivileged && (mlf = fopen(metalog, "a")) == NULL) { + warn("%s", metalog); + return (-1); + } + + /* load untrusted certs first */ + load_untrusted(true); + + /* load trusted certs, excluding any that are already untrusted */ + load_trusted(true, &untrusted); + + /* save everything */ + ret = save_all(); + + /* clean up */ + free_certs(&untrusted); + free_certs(&trusted); + if (mlf != NULL) + fclose(mlf); + return (ret); +} + +/* + * Manually add one or more certificates to the list of trusted certificates. + * + * Returns 0 on success and -1 on failure. + */ +static int +certctl_trust(int argc, char **argv) +{ + struct cert_tree extra = RB_INITIALIZER(&extra); + struct cert *cert, *other, *tmp; + unsigned int n; + int i, ret; + + if (argc < 2) + usage(); + + /* load untrusted certs first */ + load_untrusted(true); + + /* load trusted certs, excluding any that are already untrusted */ + load_trusted(true, &untrusted); + + /* now load the additional trusted certificates */ + n = 0; + for (i = 1; i < argc; i++) { + ret = read_cert(argv[i], &extra, &trusted); + if (ret > 0) + n += ret; + } + if (n == 0) { + warnx("no new trusted certificates found"); + free_certs(&untrusted); + free_certs(&trusted); + free_certs(&extra); + return (0); + } + + /* + * For each new trusted cert, move it from the extra list to the + * trusted list, then check if a matching certificate exists on + * the untrusted list. If that is the case, warn the user, then + * remove the matching certificate from the untrusted list. + */ + RB_FOREACH_SAFE(cert, cert_tree, &extra, tmp) { + RB_REMOVE(cert_tree, &extra, cert); + RB_INSERT(cert_tree, &trusted, cert); + if ((other = RB_FIND(cert_tree, &untrusted, cert)) != NULL) { + warnx("%s was previously untrusted", cert->name); + RB_REMOVE(cert_tree, &untrusted, other); + free_cert(other); + } + } + + /* save everything */ + ret = save_all(); + + /* clean up */ + free_certs(&untrusted); + free_certs(&trusted); + return (ret); +} + +/* + * Manually add one or more certificates to the list of untrusted + * certificates. + * + * Returns 0 on success and -1 on failure. + */ +static int +certctl_untrust(int argc, char **argv) +{ + unsigned int n; + int i, ret; + + if (argc < 2) + usage(); + + /* load untrusted certs first */ + load_untrusted(true); + + /* now load the additional untrusted certificates */ + n = 0; + for (i = 1; i < argc; i++) { + ret = read_cert(argv[i], &untrusted, NULL); + if (ret > 0) + n += ret; + } + if (n == 0) { + warnx("no new untrusted certificates found"); + free_certs(&untrusted); + return (0); + } + + /* load trusted certs, excluding any that are already untrusted */ + load_trusted(true, &untrusted); + + /* save everything */ + ret = save_all(); + + /* clean up */ + free_certs(&untrusted); + free_certs(&trusted); + return (ret); +} + +static void +set_defaults(void) +{ + const char *value; + char *str; + size_t len; + + if (localbase == NULL && + (localbase = getenv("LOCALBASE")) == NULL) { + if ((str = malloc((len = PATH_MAX) + 1)) == NULL) + err(1, NULL); + while (sysctlbyname("user.localbase", str, &len, NULL, 0) < 0) { + if (errno != ENOMEM) + err(1, "sysctl(user.localbase)"); + if ((str = realloc(str, len + 1)) == NULL) + err(1, NULL); + } + str[len] = '\0'; + localbase = str; + } + + if (destdir == NULL && + (destdir = getenv("DESTDIR")) == NULL) + destdir = ""; + destdir = normalize_path(destdir); + + if (distbase == NULL && + (distbase = getenv("DISTBASE")) == NULL) + distbase = ""; + if (*distbase != '\0' && *distbase != '/') + errx(1, "DISTBASE=%s does not begin with a slash", distbase); + distbase = normalize_path(distbase); + + if (unprivileged && metalog == NULL && + (metalog = getenv("METALOG")) == NULL) + metalog = xasprintf("%s/METALOG", destdir); + + if (!verbose) { + if ((value = getenv("CERTCTL_VERBOSE")) != NULL) { + if (value[0] != '\0') { + verbose = true; + } + } + } + + if ((value = getenv("TRUSTPATH")) != NULL) + trusted_paths = split_paths(value); + else + trusted_paths = expand_paths(default_trusted_paths); + + if ((value = getenv("UNTRUSTPATH")) != NULL) + untrusted_paths = split_paths(value); + else + untrusted_paths = expand_paths(default_untrusted_paths); + + if ((value = getenv("TRUSTDESTDIR")) != NULL || + (value = getenv("CERTDESTDIR")) != NULL) + trusted_dest = xstrdup(value); + else + trusted_dest = expand_path(TRUSTED_PATH); + + if ((value = getenv("UNTRUSTDESTDIR")) != NULL) + untrusted_dest = xstrdup(value); + else + untrusted_dest = expand_path(UNTRUSTED_PATH); + + if ((value = getenv("BUNDLE")) != NULL) + bundle_dest = xstrdup(value); + else + bundle_dest = expand_path(BUNDLE_PATH); + + info("localbase:\t%s", localbase); + info("destdir:\t%s", destdir); + info("distbase:\t%s", distbase); + info("unprivileged:\t%s", unprivileged ? "true" : "false"); + info("verbose:\t%s", verbose ? "true" : "false"); +} + +typedef int (*main_t)(int, char **); + +static struct { + const char *name; + main_t func; +} commands[] = { + { "list", certctl_list }, + { "untrusted", certctl_untrusted }, + { "rehash", certctl_rehash }, + { "untrust", certctl_untrust }, + { "trust", certctl_trust }, + { 0 }, +}; + +static void +usage(void) +{ + fprintf(stderr, "usage: certctl [-lv] [-D destdir] [-d distbase] list\n" + " certctl [-lv] [-D destdir] [-d distbase] untrusted\n" + " certctl [-BnUv] [-D destdir] [-d distbase] [-M metalog] rehash\n" + " certctl [-nv] [-D destdir] [-d distbase] untrust <file>\n" + " certctl [-nv] [-D destdir] [-d distbase] trust <file>\n"); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + const char *command; + int opt; + + while ((opt = getopt(argc, argv, "BcD:d:g:lL:M:no:Uv")) != -1) + switch (opt) { + case 'B': + nobundle = true; + break; + case 'c': + /* ignored for compatibility */ + break; + case 'D': + destdir = optarg; + break; + case 'd': + distbase = optarg; + break; + case 'g': + gname = optarg; + break; + case 'l': + longnames = true; + break; + case 'L': + localbase = optarg; + break; + case 'M': + metalog = optarg; + break; + case 'n': + dryrun = true; + break; + case 'o': + uname = optarg; + break; + case 'U': + unprivileged = true; + break; + case 'v': + verbose = true; + break; + default: + usage(); + } + + argc -= optind; + argv += optind; + + if (argc < 1) + usage(); + + command = *argv; + + if ((nobundle || unprivileged || metalog != NULL) && + strcmp(command, "rehash") != 0) + usage(); + if (!unprivileged && metalog != NULL) { + warnx("-M may only be used in conjunction with -U"); + usage(); + } + + set_defaults(); + + for (unsigned i = 0; commands[i].name != NULL; i++) + if (strcmp(command, commands[i].name) == 0) + exit(!!commands[i].func(argc, argv)); + usage(); +} diff --git a/usr.sbin/certctl/certctl.sh b/usr.sbin/certctl/certctl.sh deleted file mode 100755 index 2bde651de126..000000000000 --- a/usr.sbin/certctl/certctl.sh +++ /dev/null @@ -1,366 +0,0 @@ -#!/bin/sh -#- -# SPDX-License-Identifier: BSD-2-Clause -# -# Copyright 2018 Allan Jude <allanjude@freebsd.org> -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted providing that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING -# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# - -set -u - -############################################################ CONFIGURATION - -: ${DESTDIR:=} -: ${DISTBASE:=} - -############################################################ GLOBALS - -SCRIPTNAME="${0##*/}" -LINK=-lrs -ERRORS=0 -NOOP=false -UNPRIV=false -VERBOSE=false - -############################################################ FUNCTIONS - -info() -{ - echo "${0##*/}: $@" >&2 -} - -verbose() -{ - if "${VERBOSE}" ; then - info "$@" - fi -} - -perform() -{ - if ! "${NOOP}" ; then - "$@" - fi -} - -cert_files_in() -{ - find -L "$@" -type f \( \ - -name '*.pem' -or \ - -name '*.crt' -or \ - -name '*.cer' \ - \) 2>/dev/null -} - -eolcvt() -{ - cat "$@" | tr -s '\r' '\n' -} - -do_hash() -{ - local hash - - if hash=$(openssl x509 -noout -subject_hash -in "$1") ; then - echo "$hash" - return 0 - else - info "Error: $1" - ERRORS=$((ERRORS + 1)) - return 1 - fi -} - -get_decimal() -{ - local checkdir hash decimal - - checkdir=$1 - hash=$2 - decimal=0 - - while [ -e "$checkdir/$hash.$decimal" ] ; do - decimal=$((decimal + 1)) - done - - echo ${decimal} - return 0 -} - -create_trusted() -{ - local hash certhash otherfile otherhash - local suffix - - hash=$(do_hash "$1") || return - certhash=$(openssl x509 -sha1 -in "$1" -noout -fingerprint) - for otherfile in $(find $UNTRUSTDESTDIR -name "$hash.*") ; do - otherhash=$(openssl x509 -sha1 -in "$otherfile" -noout -fingerprint) - if [ "$certhash" = "$otherhash" ] ; then - info "Skipping untrusted certificate $hash ($otherfile)" - return 0 - fi - done - for otherfile in $(find $CERTDESTDIR -name "$hash.*") ; do - otherhash=$(openssl x509 -sha1 -in "$otherfile" -noout -fingerprint) - if [ "$certhash" = "$otherhash" ] ; then - verbose "Skipping duplicate entry for certificate $hash" - return 0 - fi - done - suffix=$(get_decimal "$CERTDESTDIR" "$hash") - verbose "Adding $hash.$suffix to trust store" - perform install ${INSTALLFLAGS} -m 0444 ${LINK} \ - "$(realpath "$1")" "$CERTDESTDIR/$hash.$suffix" -} - -# Accepts either dot-hash form from `certctl list` or a path to a valid cert. -resolve_certname() -{ - local hash srcfile filename - local suffix - - # If it exists as a file, we'll try that; otherwise, we'll scan - if [ -e "$1" ] ; then - hash=$(do_hash "$1") || return - srcfile=$(realpath "$1") - suffix=$(get_decimal "$UNTRUSTDESTDIR" "$hash") - filename="$hash.$suffix" - echo "$srcfile" "$hash.$suffix" - elif [ -e "${CERTDESTDIR}/$1" ] ; then - srcfile=$(realpath "${CERTDESTDIR}/$1") - hash=$(echo "$1" | sed -Ee 's/\.([0-9])+$//') - suffix=$(get_decimal "$UNTRUSTDESTDIR" "$hash") - filename="$hash.$suffix" - echo "$srcfile" "$hash.$suffix" - fi -} - -create_untrusted() -{ - local srcfile filename - - set -- $(resolve_certname "$1") - srcfile=$1 - filename=$2 - - if [ -z "$srcfile" -o -z "$filename" ] ; then - return - fi - - verbose "Adding $filename to untrusted list" - perform install ${INSTALLFLAGS} -m 0444 ${LINK} \ - "$srcfile" "$UNTRUSTDESTDIR/$filename" -} - -do_scan() -{ - local CFUNC CSEARCH CPATH CFILE CERT SPLITDIR - local oldIFS="$IFS" - CFUNC="$1" - CSEARCH="$2" - - IFS=: - set -- $CSEARCH - IFS="$oldIFS" - for CFILE in $(cert_files_in "$@") ; do - verbose "Reading $CFILE" - case $(eolcvt "$CFILE" | egrep -c '^-+BEGIN CERTIFICATE-+$') in - 0) - ;; - 1) - "$CFUNC" "$CFILE" - ;; - *) - verbose "Multiple certificates found, splitting..." - SPLITDIR=$(mktemp -d) - eolcvt "$CFILE" | egrep '^(---|[0-9A-Za-z/+=]+$)' | \ - split -p '^-+BEGIN CERTIFICATE-+$' - "$SPLITDIR/x" - for CERT in $(find "$SPLITDIR" -type f) ; do - "$CFUNC" "$CERT" - done - rm -rf "$SPLITDIR" - ;; - esac - done -} - -do_list() -{ - local CFILE subject - - for CFILE in $(find "$@" \( -type f -or -type l \) -name '*.[0-9]') ; do - if [ ! -s "$CFILE" ] ; then - info "Unable to read $CFILE" - ERRORS=$((ERRORS + 1)) - continue - fi - subject= - if ! "$VERBOSE" ; then - subject=$(openssl x509 -noout -subject -nameopt multiline -in "$CFILE" | sed -n '/commonName/s/.*= //p') - fi - if [ -z "$subject" ] ; then - subject=$(openssl x509 -noout -subject -in "$CFILE") - fi - printf "%s\t%s\n" "${CFILE##*/}" "$subject" - done -} - -cmd_rehash() -{ - - if [ -e "$CERTDESTDIR" ] ; then - perform find "$CERTDESTDIR" \( -type f -or -type l \) -delete - else - perform install -d -m 0755 "$CERTDESTDIR" - fi - if [ -e "$UNTRUSTDESTDIR" ] ; then - perform find "$UNTRUSTDESTDIR" \( -type f -or -type l \) -delete - else - perform install -d -m 0755 "$UNTRUSTDESTDIR" - fi - - do_scan create_untrusted "$UNTRUSTPATH" - do_scan create_trusted "$TRUSTPATH" -} - -cmd_list() -{ - info "Listing Trusted Certificates:" - do_list "$CERTDESTDIR" -} - -cmd_untrust() -{ - local UTFILE - - shift # verb - perform install -d -m 0755 "$UNTRUSTDESTDIR" - for UTFILE in "$@"; do - info "Adding $UTFILE to untrusted list" - create_untrusted "$UTFILE" - done -} - -cmd_trust() -{ - local UTFILE untrustedhash certhash hash - - shift # verb - for UTFILE in "$@"; do - if [ -s "$UTFILE" ] ; then - hash=$(do_hash "$UTFILE") - certhash=$(openssl x509 -sha1 -in "$UTFILE" -noout -fingerprint) - for UNTRUSTEDFILE in $(find $UNTRUSTDESTDIR -name "$hash.*") ; do - untrustedhash=$(openssl x509 -sha1 -in "$UNTRUSTEDFILE" -noout -fingerprint) - if [ "$certhash" = "$untrustedhash" ] ; then - info "Removing $(basename "$UNTRUSTEDFILE") from untrusted list" - perform rm -f $UNTRUSTEDFILE - fi - done - elif [ -e "$UNTRUSTDESTDIR/$UTFILE" ] ; then - info "Removing $UTFILE from untrusted list" - perform rm -f "$UNTRUSTDESTDIR/$UTFILE" - else - info "Cannot find $UTFILE" - ERRORS=$((ERRORS + 1)) - fi - done -} - -cmd_untrusted() -{ - info "Listing Untrusted Certificates:" - do_list "$UNTRUSTDESTDIR" -} - -usage() -{ - exec >&2 - echo "Manage the TLS trusted certificates on the system" - echo " $SCRIPTNAME [-v] list" - echo " List trusted certificates" - echo " $SCRIPTNAME [-v] untrusted" - echo " List untrusted certificates" - echo " $SCRIPTNAME [-cnUv] [-D <destdir>] [-d <distbase>] [-M <metalog>] rehash" - echo " Rehash all trusted and untrusted certificates" - echo " $SCRIPTNAME [-cnv] untrust <file>" - echo " Add <file> to the list of untrusted certificates" - echo " $SCRIPTNAME [-cnv] trust <file>" - echo " Remove <file> from the list of untrusted certificates" - exit 64 -} - -############################################################ MAIN - -while getopts cD:d:M:nUv flag; do - case "$flag" in - c) LINK=-c ;; - D) DESTDIR=${OPTARG} ;; - d) DISTBASE=${OPTARG} ;; - M) METALOG=${OPTARG} ;; - n) NOOP=true ;; - U) UNPRIV=true ;; - v) VERBOSE=true ;; - esac -done -shift $((OPTIND - 1)) - -DESTDIR=${DESTDIR%/} - -if ! [ -z "${CERTCTL_VERBOSE:-}" ] ; then - VERBOSE=true -fi -: ${METALOG:=${DESTDIR}/METALOG} -INSTALLFLAGS= -if "$UNPRIV" ; then - INSTALLFLAGS="-U -M ${METALOG} -D ${DESTDIR:-/} -o root -g wheel" -fi -: ${LOCALBASE:=$(sysctl -n user.localbase)} -: ${TRUSTPATH:=${DESTDIR}${DISTBASE}/usr/share/certs/trusted:${DESTDIR}${LOCALBASE}/share/certs:${DESTDIR}${LOCALBASE}/etc/ssl/certs} -: ${UNTRUSTPATH:=${DESTDIR}${DISTBASE}/usr/share/certs/untrusted:${DESTDIR}${LOCALBASE}/etc/ssl/untrusted:${DESTDIR}${LOCALBASE}/etc/ssl/blacklisted} -: ${CERTDESTDIR:=${DESTDIR}${DISTBASE}/etc/ssl/certs} -: ${UNTRUSTDESTDIR:=${DESTDIR}${DISTBASE}/etc/ssl/untrusted} - -[ $# -gt 0 ] || usage -case "$1" in -list) cmd_list ;; -rehash) cmd_rehash ;; -blacklist) cmd_untrust "$@" ;; -untrust) cmd_untrust "$@" ;; -trust) cmd_trust "$@" ;; -unblacklist) cmd_trust "$@" ;; -untrusted) cmd_untrusted ;; -blacklisted) cmd_untrusted ;; -*) usage # NOTREACHED -esac - -retval=$? -if [ $ERRORS -gt 0 ] ; then - info "Encountered $ERRORS errors" -fi -exit $retval - -################################################################################ -# END -################################################################################ diff --git a/usr.sbin/certctl/tests/Makefile b/usr.sbin/certctl/tests/Makefile new file mode 100644 index 000000000000..da301c3ded03 --- /dev/null +++ b/usr.sbin/certctl/tests/Makefile @@ -0,0 +1,5 @@ +PACKAGE= tests +ATF_TESTS_SH= certctl_test +${PACKAGE}FILES+= certctl.subr + +.include <bsd.test.mk> diff --git a/usr.sbin/certctl/tests/certctl.subr b/usr.sbin/certctl/tests/certctl.subr new file mode 100644 index 000000000000..841cc1781e69 --- /dev/null +++ b/usr.sbin/certctl/tests/certctl.subr @@ -0,0 +1,44 @@ +# +# Copyright (c) 2025 Dag-Erling Smørgrav <des@FreeBSD.org> +# +# SPDX-License-Identifier: BSD-2-Clause +# + +# Generate a random name +rand_name() { + local length=${1:-32} + + jot -r -c -s '' ${length} A Z +} + +# Generate a subject for a given name +subject() { + local crtname=$1 + + echo "/CN=${crtname}/O=FreeBSD/OU=Test/" +} + +# Generate a key +gen_key() { + local keyname=$1 + + env -i PATH="${PATH}" OPENSSL_CONF=/dev/null \ + openssl genrsa -out ${keyname}.key +} + +# Generate a certificate for a given name, key, and serial number +gen_crt() { + local crtname=$1 + local keyname=${2:-${crtname}} + local serial=${3:-1} + + if ! [ -f "${keyname}".key ]; then + gen_key "${keyname}" + fi + env -i PATH="${PATH}" OPENSSL_CONF=/dev/null \ + openssl req -x509 -new \ + -subj="$(subject ${crtname})" \ + -set_serial ${serial} \ + -key ${keyname}.key \ + -out ${crtname}.crt +} diff --git a/usr.sbin/certctl/tests/certctl_test.sh b/usr.sbin/certctl/tests/certctl_test.sh new file mode 100644 index 000000000000..f60bac6ffbb3 --- /dev/null +++ b/usr.sbin/certctl/tests/certctl_test.sh @@ -0,0 +1,332 @@ +# +# Copyright (c) 2025 Dag-Erling Smørgrav <des@FreeBSD.org> +# +# SPDX-License-Identifier: BSD-2-Clause +# + +. $(atf_get_srcdir)/certctl.subr + +# Random sets of eight non-colliding names +set1() +{ + cat <<EOF +AVOYKJHSLFHWPVQMKBHENUAHJTEGMCCB 0ca83bbe +UYSYXKDNNJTYOQPBGIKQDHRJYZHTDPKK 0d9a6512 +LODHGFXMZYKGOKAYGWTMMYQJYHDATDDM 4e6219f5 +NBBTQHJLHKBFFFWJTHHSNKOQYMGLHLPW 5dd76abc +BJFAQZXZHYQLIDDPCAQFPDMNXICUXBXW ad68573d +IOKNTHVEVVIJMNMYAVILMEMQQWLVRESN b577803d +BHGMAJJGNJPIVMHMFCUTJLGFROJICEKN c98a6338 +HCRFQMGDQJALMLUQNXMPGLXFLLJRODJW f50c6379 +EOF +} + +set2() +{ + cat <<EOF +GOHKZTSKIPDSYNLMGYXGLROPTATELXIU 30789c88 +YOOTYHEGHZIYFXOBLNKENPSJUDGOPJJU 7fadbc13 +ETRINNYBGKIENAVGOKVJYFSSHFZIJZRH 8ed664af +DBFGMFFMRNLPQLQPOLXOEUVLCRXLRSWT 8f34355e +WFOPBQPLQFHDHZOUQFEIDGSYDUOTSNDQ ac0471df +HMNETZMGNIWRGXQCVZXVZGWSGFBRRDQC b32f1472 +SHFYBXDVAUACBFPPAIGDAQIAGYOYGMQE baca75fa +PCBGDNVPYCDGNRQSGRSLXFHYKXLAVLHW ddeeae01 +EOF +} + +set3() +{ + cat <<EOF +NJWIRLPWAIICVJBKXXHFHLCPAERZATRL 000aa2e5 +RJAENDPOCZQEVCPFUWOWDXPCSMYJPVYC 021b95a3 +PQUQDSWHBNVLBTNBGONYRLGZZVEFXVLO 071e8c50 +VZEXRKJUPZSFBDWBOLUZXOGLNTEAPCZM 3af7bb9b +ZXOWOXQTXNZMAMZIWVFDZDJEWOOAGAOH 48d5c7cc +KQSFQYVJMFTMADIHJIWGSQISWKSHRYQO 509f5ba1 +AIECYSLWZOIEPJWWUTWSQXCNCIHHZHYI 8cb0c503 +RFHWDJZEPOFLMPGXAHVEJFHCDODAPVEV 9ae4e049 +EOF +} + +# Random set of three colliding names +collhash=f2888ce3 +coll() +{ + cat <<EOF +EJFTZEOANQLOYPEHWWXBWEWEFVKHMSNA $collhash +LEMRWZAZLKZLPPSFLNLQZVGKKBEOFYWG $collhash +ZWUPHYWKKTVEFBJOLLPDAIKGRDFVXZID $collhash +EOF +} + +sortfile() { + for filename; do + sort "${filename}" >"${filename}"- + mv "${filename}"- "${filename}" + done +} + +certctl_setup() +{ + export DESTDIR="$PWD" + + # Create input directories + mkdir -p ${DESTDIR}${DISTBASE}/usr/share/certs/trusted + mkdir -p ${DESTDIR}${DISTBASE}/usr/share/certs/untrusted + mkdir -p ${DESTDIR}/usr/local/share/certs + + # Create output directories + mkdir -p ${DESTDIR}${DISTBASE}/etc/ssl/certs + mkdir -p ${DESTDIR}${DISTBASE}/etc/ssl/untrusted + + # Generate a random key + keyname="testkey" + gen_key ${keyname} + + # Generate certificates + :>metalog.expect + :>trusted.expect + :>untrusted.expect + metalog() { + echo ".${DISTBASE}$@ type=file" >>metalog.expect + } + trusted() { + local crtname=$1 + local filename=$2 + printf "%s\t%s\n" "${filename}" "${crtname}" >>trusted.expect + metalog "/etc/ssl/certs/${filename}" + } + untrusted() { + local crtname=$1 + local filename=$2 + printf "%s\t%s\n" "${filename}" "${crtname}" >>untrusted.expect + metalog "/etc/ssl/untrusted/${filename}" + } + set1 | while read crtname hash ; do + gen_crt ${crtname} ${keyname} + mv ${crtname}.crt ${DESTDIR}${DISTBASE}/usr/share/certs/trusted + trusted "${crtname}" "${hash}.0" + done + local c=0 + coll | while read crtname hash ; do + gen_crt ${crtname} ${keyname} + mv ${crtname}.crt ${DESTDIR}${DISTBASE}/usr/share/certs/trusted + trusted "${crtname}" "${hash}.${c}" + c=$((c+1)) + done + set2 | while read crtname hash ; do + gen_crt ${crtname} ${keyname} + openssl x509 -in ${crtname}.crt + rm ${crtname}.crt + trusted "${crtname}" "${hash}.0" + done >usr/local/share/certs/bundle.crt + set3 | while read crtname hash ; do + gen_crt ${crtname} ${keyname} + mv ${crtname}.crt ${DESTDIR}${DISTBASE}/usr/share/certs/untrusted + untrusted "${crtname}" "${hash}.0" + done + metalog "/etc/ssl/cert.pem" + unset -f untrusted + unset -f trusted + unset -f metalog + sortfile *.expect +} + +check_trusted() { + local crtname=$1 + local subject="$(subject ${crtname})" + local c=${2:-1} + + atf_check -e ignore -o match:"found: ${c}\$" \ + openssl storeutl -noout -subject "${subject}" \ + ${DESTDIR}${DISTBASE}/etc/ssl/certs + atf_check -e ignore -o not-match:"found: [1-9]" \ + openssl storeutl -noout -subject "${subject}" \ + ${DESTDIR}${DISTBASE}/etc/ssl/untrusted +} + +check_untrusted() { + local crtname=$1 + local subject="$(subject ${crtname})" + local c=${2:-1} + + atf_check -e ignore -o not-match:"found: [1-9]" \ + openssl storeutl -noout -subject "${subject}" \ + ${DESTDIR}/${DISTBASE}/etc/ssl/certs + atf_check -e ignore -o match:"found: ${c}\$" \ + openssl storeutl -noout -subject "${subject}" \ + ${DESTDIR}/${DISTBASE}/etc/ssl/untrusted +} + +check_in_bundle() { + local b=${DISTBASE}${DISTBASE+/} + local crtfile=$1 + local line + + line=$(tail +5 "${crtfile}" | head -1) + atf_check grep -q "${line}" ${DESTDIR}${DISTBASE}/etc/ssl/cert.pem +} + +check_not_in_bundle() { + local b=${DISTBASE}${DISTBASE+/} + local crtfile=$1 + local line + + line=$(tail +5 "${crtfile}" | head -1) + atf_check -s exit:1 grep -q "${line}" etc/ssl/cert.pem +} + +atf_test_case rehash +rehash_head() +{ + atf_set "descr" "Test the rehash command" +} +rehash_body() +{ + certctl_setup + atf_check certctl rehash + + # Verify non-colliding trusted certificates + (set1; set2) >trusted + while read crtname hash ; do + check_trusted "${crtname}" + done <trusted + + # Verify colliding trusted certificates + coll >coll + while read crtname hash ; do + check_trusted "${crtname}" $(wc -l <coll) + done <coll + + # Verify untrusted certificates + set3 >untrusted + while read crtname hash ; do + check_untrusted "${crtname}" + done <untrusted + + # Verify bundle + for f in etc/ssl/certs/*.? ; do + check_in_bundle "${f}" + done + for f in etc/ssl/untrusted/*.? ; do + check_not_in_bundle "${f}" + done +} + +atf_test_case list +list_head() +{ + atf_set "descr" "Test the list and untrusted commands" +} +list_body() +{ + certctl_setup + atf_check certctl rehash + + atf_check -o save:trusted.out certctl list + sortfile trusted.out + # the ordering of the colliding certificates is partly + # determined by fields that change every time we regenerate + # them, so ignore them in the diff + atf_check diff -u \ + --ignore-matching-lines $collhash \ + trusted.expect trusted.out + + atf_check -o save:untrusted.out certctl untrusted + sortfile untrusted.out + atf_check diff -u \ + untrusted.expect untrusted.out +} + +atf_test_case trust +trust_head() +{ + atf_set "descr" "Test the trust command" +} +trust_body() +{ + certctl_setup + atf_check certctl rehash + crtname=$(set3 | (read crtname hash ; echo ${crtname})) + crtfile=usr/share/certs/untrusted/${crtname}.crt + check_untrusted ${crtname} + check_not_in_bundle ${crtfile} + atf_check -e match:"was previously untrusted" \ + certctl trust ${crtfile} + check_trusted ${crtname} + check_in_bundle ${crtfile} +} + +atf_test_case untrust +untrust_head() +{ + atf_set "descr" "Test the untrust command" +} +untrust_body() +{ + certctl_setup + atf_check certctl rehash + crtname=$(set1 | (read crtname hash ; echo ${crtname})) + crtfile=usr/share/certs/trusted/${crtname}.crt + check_trusted "${crtname}" + check_in_bundle ${crtfile} + atf_check certctl untrust "${crtfile}" + check_untrusted "${crtname}" + check_not_in_bundle ${crtfile} +} + +atf_test_case metalog +metalog_head() +{ + atf_set "descr" "Verify the metalog" +} +metalog_body() +{ + export DISTBASE=/base + certctl_setup + + # certctl gets DESTDIR and DISTBASE from environment + rm -f metalog.orig + atf_check certctl -U -M metalog.orig rehash + sed -E 's/(type=file) .*/\1/' metalog.orig | sort >metalog.short + atf_check diff -u metalog.expect metalog.short + + # certctl gets DESTDIR and DISTBASE from command line + rm -f metalog.orig + atf_check env -uDESTDIR -uDISTBASE \ + certctl -D ${DESTDIR} -d ${DISTBASE} -U -M metalog.orig rehash + sed -E 's/(type=file) .*/\1/' metalog.orig | sort >metalog.short + atf_check diff -u metalog.expect metalog.short + + # as above, but intentionally add trailing slashes + rm -f metalog.orig + atf_check env -uDESTDIR -uDISTBASE \ + certctl -D ${DESTDIR}// -d ${DISTBASE}/ -U -M metalog.orig rehash + sed -E 's/(type=file) .*/\1/' metalog.orig | sort >metalog.short + atf_check diff -u metalog.expect metalog.short +} + +atf_test_case misc +misc_head() +{ + atf_set "descr" "Test miscellaneous edge cases" +} +misc_body() +{ + # certctl rejects DISTBASE that does not begin with a slash + atf_check -s exit:1 -e match:"begin with a slash" \ + certctl -d base -n rehash + atf_check -s exit:1 -e match:"begin with a slash" \ + env DISTBASE=base certctl -n rehash +} + +atf_init_test_cases() +{ + atf_add_test_case rehash + atf_add_test_case list + atf_add_test_case trust + atf_add_test_case untrust + atf_add_test_case metalog + atf_add_test_case misc +} diff --git a/usr.sbin/syslogd/syslogd.c b/usr.sbin/syslogd/syslogd.c index fe7427130b78..81bbbbe66be8 100644 --- a/usr.sbin/syslogd/syslogd.c +++ b/usr.sbin/syslogd/syslogd.c @@ -1830,15 +1830,14 @@ fprintlog_write(struct filed *f, struct iovlist *il, int flags) case EHOSTUNREACH: case EHOSTDOWN: case EADDRNOTAVAIL: + case EAGAIN: + case ECONNREFUSED: break; /* case EBADF: */ /* case EACCES: */ /* case ENOTSOCK: */ /* case EFAULT: */ /* case EMSGSIZE: */ - /* case EAGAIN: */ - /* case ENOBUFS: */ - /* case ECONNREFUSED: */ default: dprintf("removing entry: errno=%d\n", e); f->f_type = F_UNUSED; diff --git a/usr.sbin/unbound/setup/local-unbound-setup.sh b/usr.sbin/unbound/setup/local-unbound-setup.sh index d52534b46fa3..d57d74952fc7 100755 --- a/usr.sbin/unbound/setup/local-unbound-setup.sh +++ b/usr.sbin/unbound/setup/local-unbound-setup.sh @@ -259,7 +259,7 @@ gen_unbound_conf() { echo " pidfile: ${pidfile}" echo " auto-trust-anchor-file: ${anchor}" if [ "${use_tls}" = "yes" ] ; then - echo " tls-system-cert: yes" + echo " tls-cert-bundle: /etc/ssl/cert.pem" fi echo "" if [ -f "${forward_conf}" ] ; then |