From nobody Thu Apr 2 11:20:39 2026 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4fmfY36FsZz6YGwR for ; Thu, 02 Apr 2026 11:20:39 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R12" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4fmfY34Bygz3HyM for ; Thu, 02 Apr 2026 11:20:39 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1775128839; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=JB6/KmXRLGo+PCVzgupngCL6WoXjZrPUaly7XG/ysoY=; b=jwKUl7EXmIapJpzUm0+e7oy+Hw3+NhejDS/y/SDCTKHOvk6MFh0+6ZU8BFJ0g9cGYwFrwQ FnjNtOxrJvQ8ypvkUv3wlZe2aPJADGGUZLNQMVmsV9t1LS2KbEhXc61wSd42Udjkzd37o7 2qWIW/GzaWvKpL6OX6FPuGXDBkBRmBfk1nByFBmDd7CpNQ29oEOT4V1OAebKSxwC7ZE6NS 206OdpPhs5Ak2yFv3ncP9qW3h10X88fNcepylXDEE5ZbAHybzJa2FFbhtGeBMxPTQTQkij CLmtnedXXSeD/56n6PPMmRCV2pPu1d+HGicfUTYcUcK07oftPb81VAKdYRcq/Q== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1775128839; a=rsa-sha256; cv=none; b=ogN7qOdIMgfOvf5d2ALaCOpUs/5ebAKmcxz6MWRG3ravaeDhDiZZudYa/BK7yTqnkvsFt3 01sV7JMqK3bGEMJkHe+9vHE7mxv+v+ggtKoqQ6N/ZhXLtmqQnI66wxV66mPlF9W+hIV1N6 LzQxVCMFEECixSLh+Oq4XeZ2SFwgeakl2WQna3eT7Lb332ph061LbknZCd792gZ/Am/Lco /x7v2Of+Z7RSPNyCcV/GgAqHtl916UPkSbytZsLMmaJ3QzWZngvkDPJmulDKG7pyvS8EOe blzADOBkhdZCbNTS6S81AKQCztx6QpNKzn3ddULwhPf+BTG1n66Q/jBq9Q5csQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1775128839; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=JB6/KmXRLGo+PCVzgupngCL6WoXjZrPUaly7XG/ysoY=; b=wPOZB2i95VDsC7uEUXU/1NEajnCE6XViPTOxAmva7G+XrAG10L/Az9C0qvey6xmZBcOjA+ 7ZbbEVP6RFHPxjHfzQGDj717F6Klaz7MfRirfVdp/7/vAzd0prJ5Pqs87G8D/tCs2tFtgM T3gBzPtjVrOvJhZR12snUFdSpLlZR0jl3J4NmidMMHE7OTrLMr1RrMlbEmR3HvTMSxSdMU RQ2VSLjOvg79hYd7YGGEkTnL8AfjVMiOgfTNaYiOkQ6TkhQ9j4wkA9bfp19s/IstLAKo5F uYta/zlTmW5/Q6Q/2E3b300WWSQbP3jylvtt/d9iOE+PYcV8izTlsC8utC+HiQ== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4fmfY33nqGz102x for ; Thu, 02 Apr 2026 11:20:39 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 1f610 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Thu, 02 Apr 2026 11:20:39 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Dag-Erling=?utf-8?Q? Sm=C3=B8rg?=rav Subject: git: ec60ca6010ca - stable/14 - tzcode: Limit TZ for setugid programs List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: des X-Git-Repository: src X-Git-Refname: refs/heads/stable/14 X-Git-Reftype: branch X-Git-Commit: ec60ca6010ca6d461a8f16c49828ceafb3b730f3 Auto-Submitted: auto-generated Date: Thu, 02 Apr 2026 11:20:39 +0000 Message-Id: <69ce5107.1f610.1491f27@gitrepo.freebsd.org> The branch stable/14 has been updated by des: URL: https://cgit.FreeBSD.org/src/commit/?id=ec60ca6010ca6d461a8f16c49828ceafb3b730f3 commit ec60ca6010ca6d461a8f16c49828ceafb3b730f3 Author: Dag-Erling Smørgrav AuthorDate: 2025-08-21 16:34:32 +0000 Commit: Dag-Erling Smørgrav CommitDate: 2026-04-02 11:16:00 +0000 tzcode: Limit TZ for setugid programs The zoneinfo parser can be told to read any file the program can access by setting TZ to either an absolute path, or a path relative to the zoneinfo directory. For setugid programs, we previously had a hack from OpenBSD which rejects values of TZ deemed unsafe, but that was rather arbitrary (anything containing a dot, for instance). Leverage openat() with AT_RESOLVE_BENEATH instead. For simplicity, move the TZ change detection code to after we've opened the file, and stat the file descriptor rather than the name. Reviewed by: jhb Differential Revision: https://reviews.freebsd.org/D52029 (cherry picked from commit b6ea2513f7769ea9d9e4d342777111add2c903b0) --- contrib/tzcode/localtime.c | 70 +++++++++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 25 deletions(-) diff --git a/contrib/tzcode/localtime.c b/contrib/tzcode/localtime.c index 6eabe0afe570..1a01db931cab 100644 --- a/contrib/tzcode/localtime.c +++ b/contrib/tzcode/localtime.c @@ -408,13 +408,13 @@ scrub_abbrs(struct state *sp) * 1 if the time zone has changed */ static int -tzfile_changed(const char *name) +tzfile_changed(const char *name, int fd) { static char old_name[PATH_MAX]; static struct stat old_sb; struct stat sb; - if (stat(name, &sb) != 0) + if (_fstat(fd, &sb) != 0) return -1; if (strcmp(name, old_name) != 0) { @@ -446,8 +446,10 @@ union input_buffer { + 4 * TZ_MAX_TIMES]; }; +#ifndef __FreeBSD__ /* TZDIR with a trailing '/' rather than a trailing '\0'. */ static char const tzdirslash[sizeof TZDIR] = TZDIR "/"; +#endif /* !__FreeBSD__ */ /* Local storage needed for 'tzloadbody'. */ union local_storage { @@ -460,6 +462,7 @@ union local_storage { struct state st; } u; +#ifndef __FreeBSD__ /* The name of the file to be opened. Ideally this would have no size limits, to support arbitrarily long Zone names. Limiting Zone names to 1024 bytes should suffice for practical use. @@ -467,6 +470,7 @@ union local_storage { file_analysis as that struct is allocated anyway, as the other union member. */ char fullname[max(sizeof(struct file_analysis), sizeof tzdirslash + 1024)]; +#endif /* !__FreeBSD__ */ }; /* Load tz data from the file named NAME into *SP. Read extended @@ -480,7 +484,11 @@ tzloadbody(char const *name, struct state *sp, bool doextend, register int fid; register int stored; register ssize_t nread; +#ifdef __FreeBSD__ + int serrno; +#else /* !__FreeBSD__ */ register bool doaccess; +#endif /* !__FreeBSD__ */ register union input_buffer *up = &lsp->u.u; register int tzheadsize = sizeof(struct tzhead); @@ -494,6 +502,7 @@ tzloadbody(char const *name, struct state *sp, bool doextend, if (name[0] == ':') ++name; +#ifndef __FreeBSD__ #ifdef SUPPRESS_TZDIR /* Do not prepend TZDIR. This is intended for specialized applications only, due to its security implications. */ @@ -502,9 +511,7 @@ tzloadbody(char const *name, struct state *sp, bool doextend, doaccess = name[0] == '/'; #endif if (!doaccess) { -#ifndef __FreeBSD__ char const *dot; -#endif /* !__FreeBSD__ */ if (sizeof lsp->fullname - sizeof tzdirslash <= strlen(name)) return ENAMETOOLONG; @@ -514,7 +521,6 @@ tzloadbody(char const *name, struct state *sp, bool doextend, memcpy(lsp->fullname, tzdirslash, sizeof tzdirslash); strcpy(lsp->fullname + sizeof tzdirslash, name); -#ifndef __FreeBSD__ /* Set doaccess if NAME contains a ".." file name component, as such a name could read a file outside the TZDIR virtual subtree. */ @@ -524,35 +530,49 @@ tzloadbody(char const *name, struct state *sp, bool doextend, doaccess = true; break; } -#endif /* !__FreeBSD__ */ name = lsp->fullname; } -#ifndef __FreeBSD__ if (doaccess && access(name, R_OK) != 0) return errno; -#endif /* !__FreeBSD__ */ -#ifdef DETECT_TZ_CHANGES - if (doextend) { - /* - * Detect if the timezone file has changed. Check - * 'doextend' to ignore TZDEFRULES; the tzfile_changed() - * function can only keep state for a single file. - */ - switch (tzfile_changed(name)) { - case -1: - return errno; - case 0: - return 0; - case 1: - break; - } - } -#endif /* DETECT_TZ_CHANGES */ +#else /* __FreeBSD__ */ + if (issetugid()) { + const char *relname = name; + if (strncmp(relname, TZDIR "/", strlen(TZDIR) + 1) == 0) + relname += strlen(TZDIR) + 1; + int dd = _open(TZDIR, O_DIRECTORY | O_RDONLY); + if (dd < 0) + return errno; + fid = _openat(dd, relname, O_RDONLY | O_BINARY, AT_RESOLVE_BENEATH); + serrno = errno; + _close(dd); + errno = serrno; + } else +#endif fid = _open(name, O_RDONLY | O_BINARY); if (fid < 0) return errno; +#ifdef DETECT_TZ_CHANGES + if (doextend) { + /* + * Detect if the timezone file has changed. Check 'doextend' to + * ignore TZDEFRULES; the tzfile_changed() function can only + * keep state for a single file. + */ + switch (tzfile_changed(name, fid)) { + case -1: + serrno = errno; + _close(fid); + return serrno; + case 0: + _close(fid); + return 0; + case 1: + break; + } + } +#endif /* DETECT_TZ_CHANGES */ nread = _read(fid, up->buf, sizeof up->buf); if (nread < tzheadsize) { int err = nread < 0 ? errno : EINVAL;