pkgsrc-Changes archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
CVS commit: pkgsrc/www/thttpd
Module Name: pkgsrc
Committed By: wiz
Date: Fri Sep 25 14:20:40 UTC 2026
Modified Files:
pkgsrc/www/thttpd: Makefile PLIST distinfo
Added Files:
pkgsrc/www/thttpd/files: README.pkgsrc
pkgsrc/www/thttpd/patches: patch-CVE-2005-3124 patch-CVE-2007-0158
patch-CVE-2009-4491 patch-CVE-2012-5640 patch-Makefile.in
patch-cgi-src_Makefile.in patch-configure patch-extras_Makefile.in
patch-extras_htpasswd.c patch-index.html patch-libhttpd.c
patch-mmc.c patch-thttpd.c
Removed Files:
pkgsrc/www/thttpd: MESSAGE
pkgsrc/www/thttpd/patches: patch-aa patch-ab patch-ac patch-ad patch-af
patch-ag
Log Message:
thttpd: fix a couple CVEs
Rename patches, add some patches from Debian/FreeBSD,
move MESSAGE to README.pkgsrc.
Bump PKGREVISION.
>From Showta Ishizaki in PR 60761.
To generate a diff of this commit:
cvs rdiff -u -r1.2 -r0 pkgsrc/www/thttpd/MESSAGE
cvs rdiff -u -r1.54 -r1.55 pkgsrc/www/thttpd/Makefile
cvs rdiff -u -r1.6 -r1.7 pkgsrc/www/thttpd/PLIST
cvs rdiff -u -r1.21 -r1.22 pkgsrc/www/thttpd/distinfo
cvs rdiff -u -r0 -r1.1 pkgsrc/www/thttpd/files/README.pkgsrc
cvs rdiff -u -r0 -r1.1 pkgsrc/www/thttpd/patches/patch-CVE-2005-3124 \
pkgsrc/www/thttpd/patches/patch-CVE-2007-0158 \
pkgsrc/www/thttpd/patches/patch-CVE-2009-4491 \
pkgsrc/www/thttpd/patches/patch-CVE-2012-5640 \
pkgsrc/www/thttpd/patches/patch-Makefile.in \
pkgsrc/www/thttpd/patches/patch-cgi-src_Makefile.in \
pkgsrc/www/thttpd/patches/patch-configure \
pkgsrc/www/thttpd/patches/patch-extras_Makefile.in \
pkgsrc/www/thttpd/patches/patch-extras_htpasswd.c \
pkgsrc/www/thttpd/patches/patch-index.html \
pkgsrc/www/thttpd/patches/patch-libhttpd.c \
pkgsrc/www/thttpd/patches/patch-mmc.c \
pkgsrc/www/thttpd/patches/patch-thttpd.c
cvs rdiff -u -r1.6 -r0 pkgsrc/www/thttpd/patches/patch-aa
cvs rdiff -u -r1.4 -r0 pkgsrc/www/thttpd/patches/patch-ab \
pkgsrc/www/thttpd/patches/patch-ad pkgsrc/www/thttpd/patches/patch-af
cvs rdiff -u -r1.7 -r0 pkgsrc/www/thttpd/patches/patch-ac
cvs rdiff -u -r1.3 -r0 pkgsrc/www/thttpd/patches/patch-ag
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: pkgsrc/www/thttpd/Makefile
diff -u pkgsrc/www/thttpd/Makefile:1.54 pkgsrc/www/thttpd/Makefile:1.55
--- pkgsrc/www/thttpd/Makefile:1.54 Sun Mar 30 10:02:53 2025
+++ pkgsrc/www/thttpd/Makefile Fri Sep 25 14:20:39 2026
@@ -1,63 +1,92 @@
-# $NetBSD: Makefile,v 1.54 2025/03/30 10:02:53 nia Exp $
+# $NetBSD: Makefile,v 1.55 2026/09/25 14:20:39 wiz Exp $
-DISTNAME= thttpd-2.29
-CATEGORIES= www
-MASTER_SITES= https://www.acme.com/software/thttpd/
-DISTFILES= ${DISTNAME}${EXTRACT_SUFX} \
- ${NETBSD_LOGO}
-DIST_SUBDIR= ${PKGNAME_NOREV}
+DISTNAME= thttpd-2.29
+PKGREVISION= 1
+CATEGORIES= www
+MASTER_SITES= https://www.acme.com/software/thttpd/
+DIST_SUBDIR= ${PKGNAME_NOREV}
+DISTFILES= ${DISTNAME}${EXTRACT_SUFX} ${NETBSD_LOGO}
+EXTRACT_ONLY= ${DISTNAME}${EXTRACT_SUFX}
+NETBSD_LOGO= sitedrivenby.gif
+SITES.${NETBSD_LOGO}= http://www.NetBSD.org/images/logos/
MAINTAINER= pkgsrc-users%NetBSD.org@localhost
HOMEPAGE= https://www.acme.com/software/thttpd/
COMMENT= Tiny/turbo/throttling HTTP server
+LICENSE= 2-clause-bsd
-EXTRACT_ONLY= ${DISTNAME}${EXTRACT_SUFX}
+GNU_CONFIGURE= yes
-NETBSD_LOGO= sitedrivenby.gif
-SITES.${NETBSD_LOGO}= http://www.NetBSD.org/images/logos/
+# cgi-src/Makefile.in builds strerror.o by running make in the parent
+# directory ("cd .. ; $(MAKE) strerror.o"), and the top Makefile runs
+# "all: this subdirs", so with -j the parent's own build and the
+# subdirectory's can compile that object into the same file at once.
+MAKE_JOBS_SAFE= no
-GNU_CONFIGURE= yes
+.include "../../mk/bsd.prefs.mk"
-# Note: this pkg auto-detects IPv6.
-BUILD_DEFS+= IPV6_READY
-# thttpd syslogs at level LOG_DAEMON by default
-# change this to your preferred syslog level
+BUILD_DEFS+= IPV6_READY
+
+# thttpd syslogs at level LOG_DAEMON by default; change this to your
+# preferred syslog level.
THTTPD_LOG_FACILITY?= LOG_DAEMON
BUILD_DEFS+= THTTPD_LOG_FACILITY
+# glibc declares sigset(3) only under _XOPEN_SOURCE. configure finds the
+# symbol anyway (it links), so HAVE_SIGSET is defined and thttpd.c calls it
+# with no prototype in scope.
+CFLAGS.Linux+= -D_GNU_SOURCE
+
+# illumos declares crypt(3c) in <unistd.h> only under __EXTENSIONS__, and
+# thttpd includes no <crypt.h>.
+CFLAGS.SunOS+= -D__EXTENSIONS__
+
+CFLAGS.SCO_SV+= -DMAXPATHLEN=255 -DS_IFSOCK=0
+
EGDIR= ${PREFIX}/share/examples
+DOCDIR= ${PREFIX}/share/doc/thttpd
+
CONF_FILES= ${EGDIR}/thttpd.conf ${PKG_SYSCONFDIR}/thttpd.conf
RCD_SCRIPTS= thttpd
-INSTALLATION_DIRS= ${PKGMANDIR}/man1 share/thttpd ${EGDIR}
+INSTALLATION_DIRS= ${PKGMANDIR}/man1 share/thttpd ${EGDIR} ${DOCDIR}
SUBST_CLASSES+= paths
-SUBST_FILES.paths= thttpd.conf
-SUBST_VARS.paths= PREFIX
SUBST_STAGE.paths= pre-configure
+SUBST_FILES.paths= thttpd.conf README.pkgsrc
+SUBST_VARS.paths= PREFIX
-MAKE_JOBS_SAFE= no
-
-CFLAGS.SCO_SV+= -DMAXPATHLEN=255 -DS_IFSOCK=0
+# htpasswd(1) is a common name, so the package installs it as thtpasswd.
+# post-patch does the renaming: after the patches, which still name
+# extras/htpasswd.c, and before this substitution runs.
+SUBST_CLASSES+= thtpasswd
+SUBST_STAGE.thtpasswd= pre-configure
+SUBST_MESSAGE.thtpasswd= Renaming htpasswd to thtpasswd.
+SUBST_FILES.thtpasswd= thttpd.8 extras/Makefile.in extras/thtpasswd.c
+SUBST_SED.thtpasswd= -e 's,\.htpasswd,@DOTHTPASSWD@,g'
+SUBST_SED.thtpasswd+= -e 's,htpasswd,thtpasswd,g'
+SUBST_SED.thtpasswd+= -e 's,@DOTHTPASSWD@,.htpasswd,g'
+
+SUBST_CLASSES+= logfac
+SUBST_STAGE.logfac= pre-configure
+SUBST_MESSAGE.logfac= Setting the syslog facility.
+SUBST_FILES.logfac= config.h
+SUBST_SED.logfac= -e 's,LOG_DAEMON,${THTTPD_LOG_FACILITY},'
post-extract:
${CP} ${FILESDIR}/thttpd.conf ${WRKSRC}
+ ${CP} ${FILESDIR}/README.pkgsrc ${WRKSRC}
-pre-configure:
+post-patch:
${MV} ${WRKSRC}/extras/htpasswd.1 ${WRKSRC}/extras/thtpasswd.1
${MV} ${WRKSRC}/extras/htpasswd.c ${WRKSRC}/extras/thtpasswd.c
- for FILE in thttpd.8 extras/Makefile.in extras/thtpasswd.c; do \
- ${MV} -f ${WRKSRC}/$${FILE} ${WRKSRC}/$${FILE}.bak; \
- ${SED} -e "s,\.htpasswd,XXX,g;s,htpasswd,thtpasswd,g;s,XXX,.htpasswd,g" \
- ${WRKSRC}/$${FILE}.bak > ${WRKSRC}/$${FILE}; \
- done
- ${MV} -f ${WRKSRC}/config.h ${WRKSRC}/config.h.bak
- ${SED} -e "s/LOG_DAEMON/${THTTPD_LOG_FACILITY}/" ${WRKSRC}/config.h.bak \
- > ${WRKSRC}/config.h
post-install:
- ${INSTALL_DATA} ${WRKSRC}/thttpd.conf ${DESTDIR}${PREFIX}/share/examples
- ${INSTALL_DATA} ${WRKSRC}/index.html ${DESTDIR}${PREFIX}/share/thttpd
- ${INSTALL_DATA} ${DISTDIR}/${DIST_SUBDIR}/sitedrivenby.gif ${DESTDIR}${PREFIX}/share/thttpd
+ ${INSTALL_DATA} ${WRKSRC}/README.pkgsrc ${DESTDIR}${DOCDIR}
+ ${INSTALL_DATA} ${WRKSRC}/thttpd.conf ${DESTDIR}${EGDIR}
+ ${INSTALL_DATA} ${WRKSRC}/index.html \
+ ${DESTDIR}${PREFIX}/share/thttpd
+ ${INSTALL_DATA} ${DISTDIR}/${DIST_SUBDIR}/${NETBSD_LOGO} \
+ ${DESTDIR}${PREFIX}/share/thttpd
.include "../../mk/bsd.pkg.mk"
Index: pkgsrc/www/thttpd/PLIST
diff -u pkgsrc/www/thttpd/PLIST:1.6 pkgsrc/www/thttpd/PLIST:1.7
--- pkgsrc/www/thttpd/PLIST:1.6 Tue Mar 11 14:05:18 2014
+++ pkgsrc/www/thttpd/PLIST Fri Sep 25 14:20:39 2026
@@ -1,4 +1,4 @@
-@comment $NetBSD: PLIST,v 1.6 2014/03/11 14:05:18 jperkin Exp $
+@comment $NetBSD: PLIST,v 1.7 2026/09/25 14:20:39 wiz Exp $
libexec/cgi-bin/phf
libexec/cgi-bin/redirect
libexec/cgi-bin/ssi
@@ -12,6 +12,7 @@ sbin/makeweb
sbin/syslogtocern
sbin/thtpasswd
sbin/thttpd
+share/doc/thttpd/README.pkgsrc
share/examples/thttpd.conf
share/thttpd/index.html
share/thttpd/sitedrivenby.gif
Index: pkgsrc/www/thttpd/distinfo
diff -u pkgsrc/www/thttpd/distinfo:1.21 pkgsrc/www/thttpd/distinfo:1.22
--- pkgsrc/www/thttpd/distinfo:1.21 Tue Oct 26 11:31:10 2021
+++ pkgsrc/www/thttpd/distinfo Fri Sep 25 14:20:39 2026
@@ -1,4 +1,4 @@
-$NetBSD: distinfo,v 1.21 2021/10/26 11:31:10 nia Exp $
+$NetBSD: distinfo,v 1.22 2026/09/25 14:20:39 wiz Exp $
BLAKE2s (thttpd-2.29/sitedrivenby.gif) = 7eef9313c8279aadd94c91a4bbe0a682cff0b5647f76acf30cd414ea72b0df60
SHA512 (thttpd-2.29/sitedrivenby.gif) = fc80ec0f63a9a28cd8002ba4c1f922807faf8661c677f7935f8768b2739facf411e6a1e72c65038d72c68c1f7bf64d2bc583c1880dc85f77eca48f7baec38ae2
@@ -6,9 +6,16 @@ Size (thttpd-2.29/sitedrivenby.gif) = 85
BLAKE2s (thttpd-2.29/thttpd-2.29.tar.gz) = 023ee26357ec155995627c937e282ce5077dd1413f1f69f2f5927c883b2dca98
SHA512 (thttpd-2.29/thttpd-2.29.tar.gz) = e02ed5b889eb3c875d56503093777c542316165a8df2d83e539337fb0759b6d9728fd484123dd903af57b06c97fe339055b816cca64778b7369b94020bd61ab6
Size (thttpd-2.29/thttpd-2.29.tar.gz) = 133967 bytes
-SHA1 (patch-aa) = 0f739bebf1ade45a9b9819fc92d48eeb1004eb66
-SHA1 (patch-ab) = c6d6a20a00b4ceaf409b849982b8e963debf9530
-SHA1 (patch-ac) = f7fe1fed88b4cb33c9456d6fdc13e2bbc175ced6
-SHA1 (patch-ad) = 234127aaf4a3b5e6536bd08cc80f823800240fac
-SHA1 (patch-af) = 5160b635cdf9b3c997e93e039ef2764f77857018
-SHA1 (patch-ag) = 01410f8d293e2d4033f2a1119b21c05e21dfefbe
+SHA1 (patch-CVE-2005-3124) = af34d681b00af143ea86b696ae52b8b19fb619f7
+SHA1 (patch-CVE-2007-0158) = 49428634a39e4bda2f1d0a0a15cc62450c1259d1
+SHA1 (patch-CVE-2009-4491) = 37fa7858d14c7afff9d22ed1aa423b10cfe80516
+SHA1 (patch-CVE-2012-5640) = dda5e5a3e3b40e9d2997952f729e44d2d0ef58d1
+SHA1 (patch-Makefile.in) = 4c5b92df9e28d14ff83d00a4d951efa4595532ac
+SHA1 (patch-cgi-src_Makefile.in) = b80257756fcbb769b6540440b476f7548a5f117d
+SHA1 (patch-configure) = c3a09ec45abbfed2dced7e861333cf4376af2fbb
+SHA1 (patch-extras_Makefile.in) = c0c0df11f5ca945ef588d47a17906ad2d1c6c86c
+SHA1 (patch-extras_htpasswd.c) = 5cd23587d0a6d483462866c96fce5f2d31754556
+SHA1 (patch-index.html) = 2a7b22d91f8ef8cad7eecabdd8cb76fb44076b3e
+SHA1 (patch-libhttpd.c) = 89660ecceb2c4e3242c38a78f734db854d028f31
+SHA1 (patch-mmc.c) = 4052750ad1318f1d6776087f743a15ff609aed8c
+SHA1 (patch-thttpd.c) = 77e8de4f058ef27dc9463614e0c14295f0460fcb
Added files:
Index: pkgsrc/www/thttpd/files/README.pkgsrc
diff -u /dev/null pkgsrc/www/thttpd/files/README.pkgsrc:1.1
--- /dev/null Fri Sep 25 14:20:40 2026
+++ pkgsrc/www/thttpd/files/README.pkgsrc Fri Sep 25 14:20:39 2026
@@ -0,0 +1,16 @@
+makeweb and the web group
+=========================
+
+makeweb lets a user create a public_html directory under the web root.
+To do that it has to be set-group-id to the group that owns the web
+root; pkgsrc installs it without that bit, because making a program
+set-group-id is the administrator's decision, not the package's.
+
+If you want to use it:
+
+ chgrp <group> @PREFIX@/sbin/makeweb
+ chmod 2755 @PREFIX@/sbin/makeweb
+
+The group thttpd's own Makefile suggests is "www".
+
+If you do not use makeweb, leave it as it is.
Index: pkgsrc/www/thttpd/patches/patch-CVE-2005-3124
diff -u /dev/null pkgsrc/www/thttpd/patches/patch-CVE-2005-3124:1.1
--- /dev/null Fri Sep 25 14:20:40 2026
+++ pkgsrc/www/thttpd/patches/patch-CVE-2005-3124 Fri Sep 25 14:20:39 2026
@@ -0,0 +1,34 @@
+$NetBSD: patch-CVE-2005-3124,v 1.1 2026/09/25 14:20:39 wiz Exp $
+
+CVE-2005-3124 -- the script wrote to /tmp/stc1.$$, a name that can be
+worked out in advance, so anyone with write access to /tmp could put a
+symlink there and have the script write through it. Use mktemp(1) and
+remove the file on the way out. NVD says this was fixed before 2.23,
+but 2.29 still has the /tmp name, which is why the patch is still here.
+
+The line as written carried a stray second backtick, so the script it
+produced did not parse -- "sh -n" reports an unmatched backtick, and
+running it gives
+
+ syslogtocern: 34: Syntax error: "||" unexpected
+
+before it does anything. That has been so since the patch was added in
+2005, so the installed syslogtocern has never run. Nobody was exposed
+to the symlink attack by it, because a script that does not parse does
+not write anywhere; the tool was simply dead. With the backtick removed
+it parses, keeps the mktemp(1) protection, and converts a syslog file
+into CERN-format access_log and error_log as the manual page says.
+
+--- extras/syslogtocern.orig 2005-05-20 19:10:25.000000000 +0000
++++ extras/syslogtocern
+@@ -31,8 +31,8 @@
+ exit 1
+ fi
+
+-tmp1=/tmp/stc1.$$
+-rm -f $tmp1
++tmp1=`mktemp -t stc1.XXXXXX` || { echo "$0: Cannot create temporary file" >&2; exit 1; }
++trap " [ -f \"$tmp1\" ] && /bin/rm -f -- \"$tmp1\"" 0 1 2 3 13 15
+
+ # Gather up all the thttpd entries.
+ egrep -h ' thttpd\[' "$@" > $tmp1
Index: pkgsrc/www/thttpd/patches/patch-CVE-2007-0158
diff -u /dev/null pkgsrc/www/thttpd/patches/patch-CVE-2007-0158:1.1
--- /dev/null Fri Sep 25 14:20:40 2026
+++ pkgsrc/www/thttpd/patches/patch-CVE-2007-0158 Fri Sep 25 14:20:39 2026
@@ -0,0 +1,112 @@
+$NetBSD: patch-CVE-2007-0158,v 1.1 2026/09/25 14:20:39 wiz Exp $
+
+CVE-2007-0158 -- buffer underflow. Five places index with length-1
+without checking the length. Three are in expand_symlinks():
+
+ - with no_symlink_check (chroot mode) and a path of only slashes, the
+ trailing-slash trim drives checkedlen to 0 and then reads
+ checked[-1]; stat("/") always succeeds, so "/" reaches it;
+ - with an empty path, rest[restlen-1] reads before the buffer;
+ - with a zero-length symlink target in the served tree, readlink()
+ returns 0 and lnk[linklen-1] reads before the stack buffer.
+
+The fourth is in auth_check2(): fgets() returns non-NULL for a line that
+begins with a NUL byte, strlen() is then 0, and line[l-1] reads before
+the 500-byte stack buffer. The .htpasswd is user-written, so this is
+reached the same way CVE-2012-5640 is, by requesting a protected
+directory.
+
+The fifth is in thttpd.c: max_connects is fdwatch_get_nfiles() minus
+SPARE_FDS, with no lower bound, so a file descriptor limit of 10 leaves
+it at 0. connects[max_connects-1].next_free_connect is then written
+before the array; malloc(0) returns a pointer, so the allocation does
+not fail first. With that limit thttpd starts and then refuses every
+connection (measured: HTTP 200 normally, no answer at all under
+"ulimit -n 10"), which is what max_connects <= 0 looks like from
+outside. It now exits with a message instead.
+
+The four in libhttpd.c fire under AddressSanitizer on the routines
+extracted unchanged from 2.29, and are clean once guarded.
+
+What other packaging carries, for comparison:
+
+ site checkedlen restlen readlink auth_check2
+ FreeBSD ports yes yes no no
+ Debian (2.25b-11) no yes* no no
+ ACME 2.30 (unreleased) - - yes -
+
+ * Debian drops the trailing-slash trim entirely rather than guarding it.
+
+(FreeBSD and Debian also change the PATH_INFO trimming near
+origfilename[i-1]. That one is not a length-1 underflow -- the stock
+"i > 0" test already prevents it -- it is a behaviour change, and it is
+kept separate in patch-libhttpd.c.)
+
+ACME's 2.30 changelog lists "off-by-one illegal memory access in
+expand_symlinks()", which is the readlink() case.
+
+--- libhttpd.c.orig
++++ libhttpd.c
+@@ -1117,9 +1117,11 @@
+ /* Read it. */
+ while ( fgets( line, sizeof(line), fp ) != (char*) 0 )
+ {
+- /* Nuke newline. */
++ /* Nuke newline. A NUL byte in the file leaves strlen() at 0, and
++ ** line[l-1] would then read before the buffer.
++ */
+ l = strlen( line );
+- if ( line[l - 1] == '\n' )
++ if ( l > 0 && line[l - 1] == '\n' )
+ line[l - 1] = '\0';
+ /* Split into user and encrypted password. */
+ cryp = strchr( line, ':' );
+@@ -1486,7 +1488,7 @@
+ httpd_realloc_str( &checked, &maxchecked, checkedlen );
+ (void) strcpy( checked, path );
+ /* Trim trailing slashes. */
+- while ( checked[checkedlen - 1] == '/' )
++ while ( checkedlen > 0 && checked[checkedlen - 1] == '/' )
+ {
+ checked[checkedlen - 1] = '\0';
+ --checkedlen;
+@@ -1505,7 +1507,7 @@
+ restlen = strlen( path );
+ httpd_realloc_str( &rest, &maxrest, restlen );
+ (void) strcpy( rest, path );
+- if ( rest[restlen - 1] == '/' )
++ if ( restlen > 0 && rest[restlen - 1] == '/' )
+ rest[--restlen] = '\0'; /* trim trailing slash */
+ if ( ! tildemapped )
+ /* Remove any leading slashes. */
+@@ -1623,7 +1625,9 @@
+ return (char*) 0;
+ }
+ lnk[linklen] = '\0';
+- if ( lnk[linklen - 1] == '/' )
++ /* An empty symlink target makes readlink() return 0, and
++ ** lnk[linklen-1] then reads lnk[-1], underflowing the buffer. */
++ if ( linklen > 0 && lnk[linklen - 1] == '/' )
+ lnk[--linklen] = '\0'; /* trim trailing slash */
+
+ /* Insert the link contents in front of the rest of the filename. */
+--- thttpd.c.orig
++++ thttpd.c
+@@ -554,6 +554,17 @@
+ exit( 1 );
+ }
+ max_connects -= SPARE_FDS;
++ if ( max_connects <= 0 )
++ {
++ /* With a file descriptor limit this low there is no room for even one
++ ** connection, and connects[max_connects-1] below would write before
++ ** the array. malloc(0) returns a pointer, so this is not caught by
++ ** the allocation failing.
++ */
++ syslog( LOG_CRIT, "file descriptor limit is too low for any connection" );
++ (void) fprintf( stderr, "%s: file descriptor limit is too low for any connection\n", argv0 );
++ exit( 1 );
++ }
+
+ /* Chroot if requested. */
+ if ( do_chroot )
Index: pkgsrc/www/thttpd/patches/patch-CVE-2009-4491
diff -u /dev/null pkgsrc/www/thttpd/patches/patch-CVE-2009-4491:1.1
--- /dev/null Fri Sep 25 14:20:40 2026
+++ pkgsrc/www/thttpd/patches/patch-CVE-2009-4491 Fri Sep 25 14:20:39 2026
@@ -0,0 +1,332 @@
+$NetBSD: patch-CVE-2009-4491,v 1.1 2026/09/25 14:20:39 wiz Exp $
+
+CVE-2009-4491 -- log injection. thttpd writes the request line, the
+headers and values derived from them to the log exactly as the client
+sent them, so a request can place terminal escape sequences into the
+log. Control characters are written as \xHH instead.
+
+This is not only the access log. Every one of these logs a value the
+client controls, and none of them sanitised it:
+
+ make_log_entry() the access log and its syslog form: URL, Referer,
+ User-Agent, remote user
+ the "unparsable time" pair the raw If-Modified-Since and If-Range
+ header values
+ check_referrer() the Referer host, the URL and the Referer
+ thirteen more the URL or the expanded filename, in the "goes
+ outside the web tree", "tried to index a
+ directory", "tried to retrieve an auth file",
+ "isn't CGI", opendir, execve and spawn messages
+ thttpd.c the URL in the write-error message
+
+httpd_log_escape() is exported because thttpd.c needs it too. It hands
+back a pointer into a small ring of static buffers so that several
+values can be escaped in one syslog() call; thttpd is single-threaded,
+so that is safe.
+
+Not in ACME's 2.30 changelog, and not carried by FreeBSD ports, Debian,
+Fedora, Alpine, MacPorts or Void.
+
+--- libhttpd.c.orig
++++ libhttpd.c
+@@ -172,6 +172,7 @@
+ static int cgi( httpd_conn* hc );
+ static int really_start_request( httpd_conn* hc, struct timeval* nowP );
+ static void make_log_entry( httpd_conn* hc, struct timeval* nowP );
++static char* log_escape( char* dst, size_t dstsize, const char* src );
+ static int check_referrer( httpd_conn* hc );
+ static int really_check_referrer( httpd_conn* hc );
+ static int sockaddr_check( httpd_sockaddr* saP );
+@@ -1619,7 +1620,7 @@
+ ++nlinks;
+ if ( nlinks > MAX_LINKS )
+ {
+- syslog( LOG_ERR, "too many symlinks in %.80s", path );
++ syslog( LOG_ERR, "too many symlinks in %.80s", httpd_log_escape( path ) );
+ return (char*) 0;
+ }
+ lnk[linklen] = '\0';
+@@ -2173,7 +2174,8 @@
+ cp = &buf[18];
+ hc->if_modified_since = tdate_parse( cp );
+ if ( hc->if_modified_since == (time_t) -1 )
+- syslog( LOG_DEBUG, "unparsable time: %.80s", cp );
++ syslog( LOG_DEBUG, "unparsable time: %.80s",
++ httpd_log_escape( cp ) );
+ }
+ else if ( strncasecmp( buf, "Cookie:", 7 ) == 0 )
+ {
+@@ -2214,7 +2216,8 @@
+ cp = &buf[9];
+ hc->range_if = tdate_parse( cp );
+ if ( hc->range_if == (time_t) -1 )
+- syslog( LOG_DEBUG, "unparsable time: %.80s", cp );
++ syslog( LOG_DEBUG, "unparsable time: %.80s",
++ httpd_log_escape( cp ) );
+ }
+ else if ( strncasecmp( buf, "Content-Type:", 13 ) == 0 )
+ {
+@@ -2380,7 +2383,7 @@
+ {
+ syslog(
+ LOG_NOTICE, "%.80s URL \"%.80s\" goes outside the web tree",
+- httpd_ntoa( &hc->client_addr ), hc->encodedurl );
++ httpd_ntoa( &hc->client_addr ), httpd_log_escape( hc->encodedurl ) );
+ httpd_send_err(
+ hc, 403, err403title, "",
+ ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a file outside the permitted web server directory tree.\n" ),
+@@ -2734,7 +2737,7 @@
+ dirp = opendir( hc->expnfilename );
+ if ( dirp == (DIR*) 0 )
+ {
+- syslog( LOG_ERR, "opendir %.80s - %m", hc->expnfilename );
++ syslog( LOG_ERR, "opendir %.80s - %m", httpd_log_escape( hc->expnfilename ) );
+ httpd_send_err( hc, 404, err404title, "", err404form, hc->encodedurl );
+ return -1;
+ }
+@@ -2974,7 +2977,8 @@
+
+ /* Parent process. */
+ closedir( dirp );
+- syslog( LOG_DEBUG, "spawned indexing process %d for directory '%.200s'", r, hc->expnfilename );
++ syslog( LOG_DEBUG, "spawned indexing process %d for directory '%.200s'",
++ r, httpd_log_escape( hc->expnfilename ) );
+ #ifdef CGI_TIMELIMIT
+ /* Schedule a kill for the child process, in case it runs too long */
+ client_data.i = r;
+@@ -3563,7 +3567,7 @@
+ (void) execve( binary, argp, envp );
+
+ /* Something went wrong. */
+- syslog( LOG_ERR, "execve %.80s - %m", hc->expnfilename );
++ syslog( LOG_ERR, "execve %.80s - %m", httpd_log_escape( hc->expnfilename ) );
+ httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
+ httpd_write_response( hc );
+ _exit( 1 );
+@@ -3602,7 +3606,8 @@
+ }
+
+ /* Parent process. */
+- syslog( LOG_DEBUG, "spawned CGI process %d for file '%.200s'", r, hc->expnfilename );
++ syslog( LOG_DEBUG, "spawned CGI process %d for file '%.200s'",
++ r, httpd_log_escape( hc->expnfilename ) );
+ #ifdef CGI_TIMELIMIT
+ /* Schedule a kill for the child process, in case it runs too long */
+ client_data.i = r;
+@@ -3654,7 +3659,7 @@
+ syslog(
+ LOG_INFO,
+ "%.80s URL \"%.80s\" resolves to a non world-readable file",
+- httpd_ntoa( &hc->client_addr ), hc->encodedurl );
++ httpd_ntoa( &hc->client_addr ), httpd_log_escape( hc->encodedurl ) );
+ httpd_send_err(
+ hc, 403, err403title, "",
+ ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a file that is not world-readable.\n" ),
+@@ -3709,7 +3714,7 @@
+ syslog(
+ LOG_INFO,
+ "%.80s URL \"%.80s\" tried to index a directory with indexing disabled",
+- httpd_ntoa( &hc->client_addr ), hc->encodedurl );
++ httpd_ntoa( &hc->client_addr ), httpd_log_escape( hc->encodedurl ) );
+ httpd_send_err(
+ hc, 403, err403title, "",
+ ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a directory that has indexing disabled.\n" ),
+@@ -3729,7 +3734,7 @@
+ #else /* GENERATE_INDEXES */
+ syslog(
+ LOG_INFO, "%.80s URL \"%.80s\" tried to index a directory",
+- httpd_ntoa( &hc->client_addr ), hc->encodedurl );
++ httpd_ntoa( &hc->client_addr ), httpd_log_escape( hc->encodedurl ) );
+ httpd_send_err(
+ hc, 403, err403title, "",
+ ERROR_FORM( err403form, "The requested URL '%.80s' is a directory, and directory indexing is disabled on this server.\n" ),
+@@ -3757,7 +3762,7 @@
+ syslog(
+ LOG_INFO,
+ "%.80s URL \"%.80s\" resolves to a non-world-readable index file",
+- httpd_ntoa( &hc->client_addr ), hc->encodedurl );
++ httpd_ntoa( &hc->client_addr ), httpd_log_escape( hc->encodedurl ) );
+ httpd_send_err(
+ hc, 403, err403title, "",
+ ERROR_FORM( err403form, "The requested URL '%.80s' resolves to an index file that is not world-readable.\n" ),
+@@ -3786,7 +3791,7 @@
+ syslog(
+ LOG_NOTICE,
+ "%.80s URL \"%.80s\" tried to retrieve an auth file",
+- httpd_ntoa( &hc->client_addr ), hc->encodedurl );
++ httpd_ntoa( &hc->client_addr ), httpd_log_escape( hc->encodedurl ) );
+ httpd_send_err(
+ hc, 403, err403title, "",
+ ERROR_FORM( err403form, "The requested URL '%.80s' is an authorization file, retrieving it is not permitted.\n" ),
+@@ -3801,7 +3806,7 @@
+ syslog(
+ LOG_NOTICE,
+ "%.80s URL \"%.80s\" tried to retrieve an auth file",
+- httpd_ntoa( &hc->client_addr ), hc->encodedurl );
++ httpd_ntoa( &hc->client_addr ), httpd_log_escape( hc->encodedurl ) );
+ httpd_send_err(
+ hc, 403, err403title, "",
+ ERROR_FORM( err403form, "The requested URL '%.80s' is an authorization file, retrieving it is not permitted.\n" ),
+@@ -3828,7 +3833,7 @@
+ {
+ syslog(
+ LOG_NOTICE, "%.80s URL \"%.80s\" is executable but isn't CGI",
+- httpd_ntoa( &hc->client_addr ), hc->encodedurl );
++ httpd_ntoa( &hc->client_addr ), httpd_log_escape( hc->encodedurl ) );
+ httpd_send_err(
+ hc, 403, err403title, "",
+ ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a file which is marked executable but is not a CGI file; retrieving it is forbidden.\n" ),
+@@ -3839,7 +3844,7 @@
+ {
+ syslog(
+ LOG_INFO, "%.80s URL \"%.80s\" has pathinfo but isn't CGI",
+- httpd_ntoa( &hc->client_addr ), hc->encodedurl );
++ httpd_ntoa( &hc->client_addr ), httpd_log_escape( hc->encodedurl ) );
+ httpd_send_err(
+ hc, 403, err403title, "",
+ ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a file plus CGI-style pathinfo, but the file is not a valid CGI file.\n" ),
+@@ -3904,12 +3909,65 @@
+ }
+
+
++/* Copy src into dst, replacing control characters with \xHH. The request
++** line, the headers and anything derived from them reach the log exactly as
++** the client sent them, so without this a request can write terminal escape
++** sequences into the log.
++*/
++static char*
++log_escape( char* dst, size_t dstsize, const char* src )
++ {
++ static const char hex[] = "0123456789abcdef";
++ size_t i = 0;
++ unsigned char c;
++
++ if ( src == (const char*) 0 )
++ src = "";
++ for ( ; *src != '\0' && i + 4 < dstsize; ++src )
++ {
++ c = (unsigned char) *src;
++ if ( c < 0x20 || c == 0x7f )
++ {
++ dst[i++] = '\\';
++ dst[i++] = 'x';
++ dst[i++] = hex[c >> 4];
++ dst[i++] = hex[c & 0xf];
++ }
++ else
++ dst[i++] = c;
++ }
++ dst[i] = '\0';
++ return dst;
++ }
++
++
++/* The same, for use straight in a syslog() argument list. Returns a pointer
++** into a small ring of buffers so that more than one value can be escaped in
++** one call. thttpd is single-threaded, so this is safe.
++*/
++char*
++httpd_log_escape( const char* src )
++ {
++ static char bufs[4][1000];
++ static int next = 0;
++ char* dst;
++
++ dst = bufs[next];
++ next = ( next + 1 ) % 4;
++ return log_escape( dst, sizeof(bufs[0]), src );
++ }
++
++
+ static void
+ make_log_entry( httpd_conn* hc, struct timeval* nowP )
+ {
+ char* ru;
+ char url[305];
+ char bytes[40];
++ char eurl[305 * 4];
++ char eref[200 * 4 + 1];
++ char eua[200 * 4 + 1];
++ char eru[80 * 4 + 1];
+
+ if ( hc->hs->no_log )
+ return;
+@@ -3922,7 +3980,7 @@
+
+ /* Format remote user. */
+ if ( hc->remoteuser[0] != '\0' )
+- ru = hc->remoteuser;
++ ru = log_escape( eru, sizeof(eru), hc->remoteuser );
+ else
+ ru = "-";
+ /* If we're vhosting, prepend the hostname to the url. This is
+@@ -3937,6 +3995,9 @@
+ else
+ (void) my_snprintf( url, sizeof(url),
+ "%.200s", hc->encodedurl );
++ (void) log_escape( eurl, sizeof(eurl), url );
++ (void) log_escape( eref, sizeof(eref), hc->referrer );
++ (void) log_escape( eua, sizeof(eua), hc->useragent );
+ /* Format the bytes. */
+ if ( hc->bytes_sent >= 0 )
+ (void) my_snprintf(
+@@ -3985,8 +4046,8 @@
+ (void) fprintf( hc->hs->logfp,
+ "%.80s - %.80s [%s] \"%.80s %.300s %.80s\" %d %s \"%.200s\" \"%.200s\"\n",
+ httpd_ntoa( &hc->client_addr ), ru, date,
+- httpd_method_str( hc->method ), url, hc->protocol,
+- hc->status, bytes, hc->referrer, hc->useragent );
++ httpd_method_str( hc->method ), eurl, hc->protocol,
++ hc->status, bytes, eref, eua );
+ #ifdef FLUSH_LOG_EVERY_TIME
+ (void) fflush( hc->hs->logfp );
+ #endif
+@@ -3995,8 +4056,8 @@
+ syslog( LOG_INFO,
+ "%.80s - %.80s \"%.80s %.200s %.80s\" %d %s \"%.200s\" \"%.200s\"",
+ httpd_ntoa( &hc->client_addr ), ru,
+- httpd_method_str( hc->method ), url, hc->protocol,
+- hc->status, bytes, hc->referrer, hc->useragent );
++ httpd_method_str( hc->method ), eurl, hc->protocol,
++ hc->status, bytes, eref, eua );
+ }
+
+
+@@ -4023,7 +4084,9 @@
+ cp = "";
+ syslog(
+ LOG_INFO, "%.80s non-local referrer \"%.80s%.80s\" \"%.80s\"",
+- httpd_ntoa( &hc->client_addr ), cp, hc->encodedurl, hc->referrer );
++ httpd_ntoa( &hc->client_addr ), httpd_log_escape( cp ),
++ httpd_log_escape( hc->encodedurl ),
++ httpd_log_escape( hc->referrer ) );
+ httpd_send_err(
+ hc, 403, err403title, "",
+ ERROR_FORM( err403form, "You must supply a local referrer to get URL '%.80s' from this server.\n" ),
+--- libhttpd.h.orig
++++ libhttpd.h
+@@ -263,6 +263,12 @@
+ extern char* httpd_err408form;
+ extern char* httpd_err503title;
+ extern char* httpd_err503form;
++
++/* Escape control characters in a string for the log, so that a request
++** cannot write terminal escape sequences into it. Returns a pointer into
++** a small ring of static buffers.
++*/
++char* httpd_log_escape( const char* src );
+
+ /* Generate a string representation of a method number. */
+ char* httpd_method_str( int method );
+--- thttpd.c.orig
++++ thttpd.c
+@@ -1778,7 +1778,8 @@
+ ** And ECONNRESET isn't interesting either.
+ */
+ if ( errno != EPIPE && errno != EINVAL && errno != ECONNRESET )
+- syslog( LOG_ERR, "write - %m sending %.80s", hc->encodedurl );
++ syslog( LOG_ERR, "write - %m sending %.80s",
++ httpd_log_escape( hc->encodedurl ) );
+ clear_connection( c, tvP );
+ return;
+ }
Index: pkgsrc/www/thttpd/patches/patch-CVE-2012-5640
diff -u /dev/null pkgsrc/www/thttpd/patches/patch-CVE-2012-5640:1.1
--- /dev/null Fri Sep 25 14:20:40 2026
+++ pkgsrc/www/thttpd/patches/patch-CVE-2012-5640 Fri Sep 25 14:20:39 2026
@@ -0,0 +1,82 @@
+$NetBSD: patch-CVE-2012-5640,v 1.1 2026/09/25 14:20:39 wiz Exp $
+
+CVE-2012-5640 -- denial of service. crypt() returns NULL for a salt it
+does not recognise (glibc and illumos do; NetBSD and current Linux
+return "*0" instead). thttpd passes the result straight on in all three
+places it calls crypt(), and the salt comes from a user-written
+.htpasswd, so one malformed line is enough.
+
+In auth_check2() the result is handed to strcmp(), so a request for the
+protected directory crashes the server. Note that the fix must keep
+going to the send_authenticate() call below rather than returning
+straight away: this function documents -1 as "unauthorized" and every
+existing -1 is preceded by send_authenticate(), and the callers take -1
+to mean a response has already been written. Returning -1 without it
+would answer the request with nothing at all. Fedora and Alpine carry
+this fix in the shorter "return -1" form, which has that effect. A
+syslog line is added because an unusable salt is an administrator's
+mistake, not a wrong password.
+
+In extras/htpasswd.c the result is passed to fprintf("%s"), so writing a
+password with a salt crypt() dislikes dereferences NULL. Fedora and
+Alpine guard this one too.
+
+ACME lists a crypt() NULL check for the unreleased 2.30.
+
+--- libhttpd.c.orig
++++ libhttpd.c
+@@ -1030,6 +1030,7 @@
+ FILE* fp;
+ char line[500];
+ char* cryp;
++ char* cryp2;
+ static char* prevauthpath;
+ static size_t maxprevauthpath = 0;
+ static time_t prevmtime;
+@@ -1082,8 +1083,16 @@
+ sb.st_mtime == prevmtime &&
+ strcmp( authinfo, prevuser ) == 0 )
+ {
+- /* Yes. Check against the cached encrypted password. */
+- if ( strcmp( crypt( authpass, prevcryp ), prevcryp ) == 0 )
++ /* Yes. Check against the cached encrypted password. crypt() returns
++ ** NULL for a salt it does not understand, and the password file is
++ ** user-supplied, so a bad line must not be dereferenced. Treat it as
++ ** a mismatch so that the send_authenticate() below still runs: the
++ ** callers take -1 to mean a response has already been sent.
++ */
++ cryp = crypt( authpass, prevcryp );
++ if ( cryp == (char*) 0 )
++ syslog( LOG_ERR, "unusable password for %.80s in %.80s", authinfo, authpath );
++ if ( cryp != (char*) 0 && strcmp( cryp, prevcryp ) == 0 )
+ {
+ /* Ok! */
+ httpd_realloc_str(
+@@ -1131,8 +1140,11 @@
+ {
+ /* Yes. */
+ (void) fclose( fp );
+- /* So is the password right? */
+- if ( strcmp( crypt( authpass, cryp ), cryp ) == 0 )
++ /* So is the password right? As above, crypt() may return NULL. */
++ cryp2 = crypt( authpass, cryp );
++ if ( cryp2 == (char*) 0 )
++ syslog( LOG_ERR, "unusable password for %.80s in %.80s", line, authpath );
++ if ( cryp2 != (char*) 0 && strcmp( cryp2, cryp ) == 0 )
+ {
+ /* Ok! */
+ httpd_realloc_str(
+--- extras/htpasswd.c.orig
++++ extras/htpasswd.c
+@@ -131,6 +131,11 @@
+ (void) srandom( (int) time( (time_t*) 0 ) );
+ to64( &salt[0], random(), 2 );
+ cpw = crypt( pw, salt );
++ if ( cpw == (char*) 0 )
++ {
++ (void) fprintf( stderr, "htpasswd: crypt() could not hash the password\n" );
++ exit( 1 );
++ }
+ (void) fprintf( f, "%s:%s\n", user, cpw );
+ }
+
Index: pkgsrc/www/thttpd/patches/patch-Makefile.in
diff -u /dev/null pkgsrc/www/thttpd/patches/patch-Makefile.in:1.1
--- /dev/null Fri Sep 25 14:20:40 2026
+++ pkgsrc/www/thttpd/patches/patch-Makefile.in Fri Sep 25 14:20:39 2026
@@ -0,0 +1,59 @@
+$NetBSD: patch-Makefile.in,v 1.1 2026/09/25 14:20:39 wiz Exp $
+
+Install into the pkgsrc layout instead of the upstream defaults: the
+CGI programs go to libexec/cgi-bin and the sample web root to
+share/thttpd, rather than under $prefix/www.
+
+Use the BSD_INSTALL_* tools rather than "mkdir -p" and an install(1)
+run with a hardcoded owner of bin:bin, so that DESTDIR and the
+unprivileged build both work.
+
+CFLAGS is appended to rather than assigned, so that the flags pkgsrc
+passes in survive.
+
+--- Makefile.in.orig 2005-05-20 19:10:24.000000000 +0000
++++ Makefile.in
+@@ -34,7 +34,7 @@
+ # Pathname of directory to install the man page.
+ MANDIR = @mandir@
+ # Pathname of directory to install the CGI programs.
+-WEBDIR = $(prefix)/www
++WEBDIR = $(prefix)/share/thttpd
+
+ # CONFIGURE: The group that the web directory belongs to. This is so that
+ # the makeweb program can be installed set-group-id to that group, and make
+@@ -42,7 +42,7 @@
+ WEBGROUP = www
+
+ # CONFIGURE: Directory for CGI executables.
+-CGIBINDIR = $(WEBDIR)/cgi-bin
++CGIBINDIR = $(prefix)/libexec/cgi-bin
+
+ # You shouldn't need to edit anything below here.
+
+@@ -50,7 +50,7 @@
+ CCOPT = @V_CCOPT@
+ DEFS = @DEFS@
+ INCLS = -I.
+-CFLAGS = $(CCOPT) $(DEFS) $(INCLS)
++CFLAGS += $(CCOPT) $(DEFS) $(INCLS)
+ LDFLAGS = @LDFLAGS@
+ LIBS = @LIBS@
+ NETLIBS = @V_NETLIBS@
+@@ -109,12 +109,12 @@
+ install: installthis install-man installsubdirs
+
+ installthis:
+- -mkdir -p $(DESTDIR)$(BINDIR)
+- $(INSTALL) -m 555 -o bin -g bin thttpd $(DESTDIR)$(BINDIR)
++ $(BSD_INSTALL_PROGRAM_DIR) $(DESTDIR)$(BINDIR)
++ $(BSD_INSTALL_PROGRAM) thttpd $(DESTDIR)$(BINDIR)
+
+ install-man:
+- -mkdir -p $(DESTDIR)$(MANDIR)/man8
+- $(INSTALL) -m 444 -o bin -g bin thttpd.8 $(DESTDIR)$(MANDIR)/man8
++ $(BSD_INSTALL_DATA_DIR) $(DESTDIR)$(MANDIR)/man8
++ $(BSD_INSTALL_MAN) thttpd.8 $(DESTDIR)$(MANDIR)/man8
+
+ installsubdirs:
+ for i in $(SUBDIRS) ; do ( \
Index: pkgsrc/www/thttpd/patches/patch-cgi-src_Makefile.in
diff -u /dev/null pkgsrc/www/thttpd/patches/patch-cgi-src_Makefile.in:1.1
--- /dev/null Fri Sep 25 14:20:40 2026
+++ pkgsrc/www/thttpd/patches/patch-cgi-src_Makefile.in Fri Sep 25 14:20:40 2026
@@ -0,0 +1,46 @@
+$NetBSD: patch-cgi-src_Makefile.in,v 1.1 2026/09/25 14:20:40 wiz Exp $
+
+The same as patch-ab for the CGI programs: honour DESTDIR, install with
+the BSD_INSTALL_* tools instead of cp, and append to CFLAGS rather than
+overwrite it.
+
+--- cgi-src/Makefile.in.orig 2005-05-20 19:10:25.000000000 +0000
++++ cgi-src/Makefile.in
+@@ -34,7 +34,7 @@
+ CCOPT = @V_CCOPT@
+ DEFS = @DEFS@
+ INCLS = -I..
+-CFLAGS = $(CCOPT) $(DEFS) $(INCLS)
++CFLAGS += $(CCOPT) $(DEFS) $(INCLS)
+ LDFLAGS = @LDFLAGS@ @V_STATICFLAG@
+ LIBS = @LIBS@
+ NETLIBS = @V_NETLIBS@
+@@ -67,17 +67,17 @@
+ cd .. ; $(MAKE) $(MFLAGS) strerror.o
+
+ install: all
+- -mkdir -p $(CGIBINDIR)
+- rm -f $(CGIBINDIR)/redirect
+- cp redirect $(CGIBINDIR)/redirect
+- rm -f $(MANDIR)/man8/redirect.8
+- cp redirect.8 $(MANDIR)/man8/redirect.8
+- rm -f $(CGIBINDIR)/ssi
+- cp ssi $(CGIBINDIR)/ssi
+- rm -f $(MANDIR)/man8/ssi.8
+- cp ssi.8 $(MANDIR)/man8/ssi.8
+- rm -f $(CGIBINDIR)/phf
+- cp phf $(CGIBINDIR)/phf
++ -mkdir -p $(DESTDIR)$(CGIBINDIR)
++ rm -f $(DESTDIR)$(CGIBINDIR)/redirect
++ $(BSD_INSTALL_PROGRAM) redirect $(DESTDIR)$(CGIBINDIR)/redirect
++ rm -f $(DESTDIR)$(MANDIR)/man8/redirect.8
++ $(BSD_INSTALL_MAN) redirect.8 $(DESTDIR)$(MANDIR)/man8/redirect.8
++ rm -f $(DESTDIR)$(CGIBINDIR)/ssi
++ $(BSD_INSTALL_PROGRAM) ssi $(DESTDIR)$(CGIBINDIR)/ssi
++ rm -f $(DESTDIR)$(MANDIR)/man8/ssi.8
++ $(BSD_INSTALL_MAN) ssi.8 $(DESTDIR)$(MANDIR)/man8/ssi.8
++ rm -f $(DESTDIR)$(CGIBINDIR)/phf
++ $(BSD_INSTALL_PROGRAM) phf $(DESTDIR)$(CGIBINDIR)/phf
+
+ clean:
+ rm -f $(CLEANFILES)
Index: pkgsrc/www/thttpd/patches/patch-configure
diff -u /dev/null pkgsrc/www/thttpd/patches/patch-configure:1.1
--- /dev/null Fri Sep 25 14:20:40 2026
+++ pkgsrc/www/thttpd/patches/patch-configure Fri Sep 25 14:20:40 2026
@@ -0,0 +1,49 @@
+$NetBSD: patch-configure,v 1.1 2026/09/25 14:20:40 wiz Exp $
+
+Not a CVE: the configure script's test programs declare main() without a
+return type. C99 removed implicit int and current compilers reject it,
+so the very first test -- "checking whether the C compiler works" --
+fails and configure stops with "C compiler cannot create executables".
+gcc 14 and clang 16 and later are in that state; this was measured with
+clang 21 and it is what breaks the build on Debian 13.
+
+Fedora and MacPorts both carry this fix.
+
+--- configure.orig
++++ configure
+@@ -761,7 +761,7 @@
+ #line 762 "configure"
+ #include "confdefs.h"
+
+-main(){return(0);}
++int main(){return(0);}
+ EOF
+ if { (eval echo configure:767: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ ac_cv_prog_cc_works=yes
+@@ -880,7 +880,7 @@
+ echo $ac_n "(cached) $ac_c" 1>&6
+ else
+ ac_cv_lbl_static_flag=unknown
+- echo 'main() {}' > conftest.c
++ echo 'int main() {}' > conftest.c
+ if test "$GCC" != yes ; then
+ trial_flag="-Bstatic"
+ test=`$CC $trial_flag -o conftest conftest.c 2>&1`
+@@ -1588,7 +1588,7 @@
+ #include "confdefs.h"
+
+ int main() {
+-main()
++int main()
+ ; return 0; }
+ EOF
+ if { (eval echo configure:1595: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+@@ -2095,7 +2095,7 @@
+ #endif
+
+ int
+-main()
++int main()
+ {
+ char *data, *data2, *data3;
+ int i, pagesize;
Index: pkgsrc/www/thttpd/patches/patch-extras_Makefile.in
diff -u /dev/null pkgsrc/www/thttpd/patches/patch-extras_Makefile.in:1.1
--- /dev/null Fri Sep 25 14:20:40 2026
+++ pkgsrc/www/thttpd/patches/patch-extras_Makefile.in Fri Sep 25 14:20:40 2026
@@ -0,0 +1,51 @@
+$NetBSD: patch-extras_Makefile.in,v 1.1 2026/09/25 14:20:40 wiz Exp $
+
+The same two things as patch-aa, for the helper programs: honour
+DESTDIR, install with the BSD_INSTALL_* tools instead of cp, and append
+to CFLAGS rather than overwrite it.
+
+syslogtocern is a shell script, so it goes in with BSD_INSTALL_SCRIPT.
+
+The upstream rule also made makeweb set-group-id here; that is left to
+the administrator instead, which is what README.pkgsrc explains.
+
+--- extras/Makefile.in.orig 2005-05-20 19:10:25.000000000 +0000
++++ extras/Makefile.in
+@@ -35,7 +35,7 @@
+ CCOPT = @V_CCOPT@
+ DEFS = @DEFS@
+ INCLS = -I..
+-CFLAGS = $(CCOPT) $(DEFS) $(INCLS)
++CFLAGS += $(CCOPT) $(DEFS) $(INCLS)
+ STATICFLAG = @V_STATICFLAG@
+ LDFLAGS = @LDFLAGS@
+ LIBS = @LIBS@
+@@ -66,18 +66,16 @@
+
+
+ install: all
+- rm -f $(BINDIR)/makeweb $(BINDIR)/htpasswd $(BINDIR)/syslogtocern
+- cp makeweb $(BINDIR)/makeweb
+- chgrp $(WEBGROUP) $(BINDIR)/makeweb
+- chmod 2755 $(BINDIR)/makeweb
+- cp htpasswd $(BINDIR)/htpasswd
+- cp syslogtocern $(BINDIR)/syslogtocern
+- rm -f $(MANDIR)/man1/makeweb.1
+- cp makeweb.1 $(MANDIR)/man1/makeweb.1
+- rm -f $(MANDIR)/man1/htpasswd.1
+- cp htpasswd.1 $(MANDIR)/man1/htpasswd.1
+- rm -f $(MANDIR)/man8/syslogtocern.8
+- cp syslogtocern.8 $(MANDIR)/man8/syslogtocern.8
++ rm -f $(DESTDIR)$(BINDIR)/makeweb $(DESTDIR)$(BINDIR)/htpasswd $(DESTDIR)$(BINDIR)/syslogtocern
++ $(BSD_INSTALL_PROGRAM) makeweb $(DESTDIR)$(BINDIR)/makeweb
++ $(BSD_INSTALL_PROGRAM) htpasswd $(DESTDIR)$(BINDIR)/htpasswd
++ $(BSD_INSTALL_SCRIPT) syslogtocern $(DESTDIR)$(BINDIR)/syslogtocern
++ rm -f $(DESTDIR)$(MANDIR)/man1/makeweb.1
++ $(BSD_INSTALL_MAN) makeweb.1 $(DESTDIR)$(MANDIR)/man1/makeweb.1
++ rm -f $(DESTDIR)$(MANDIR)/man1/htpasswd.1
++ $(BSD_INSTALL_MAN) htpasswd.1 $(DESTDIR)$(MANDIR)/man1/htpasswd.1
++ rm -f $(DESTDIR)$(MANDIR)/man8/syslogtocern.8
++ $(BSD_INSTALL_MAN) syslogtocern.8 $(DESTDIR)$(MANDIR)/man8/syslogtocern.8
+
+ clean:
+ rm -f $(CLEANFILES)
Index: pkgsrc/www/thttpd/patches/patch-extras_htpasswd.c
diff -u /dev/null pkgsrc/www/thttpd/patches/patch-extras_htpasswd.c:1.1
--- /dev/null Fri Sep 25 14:20:40 2026
+++ pkgsrc/www/thttpd/patches/patch-extras_htpasswd.c Fri Sep 25 14:20:40 2026
@@ -0,0 +1,70 @@
+$NetBSD: patch-extras_htpasswd.c,v 1.1 2026/09/25 14:20:40 wiz Exp $
+
+Not a CVE, two things in the htpasswd utility.
+
+The line buffers are MAX_STRING_LEN (256) while a .htpasswd line is
+"user:hash". The hash of a modern crypt(3) runs to about a hundred
+characters, so a line written by any other tool does not fit, and
+my_getline() truncates at the limit without saying so; the remainder is
+then read as the next line. Reading an existing file in order to add a
+user to it therefore corrupted it. FreeBSD ports, Debian and Fedora all
+enlarge these buffers.
+
+fgets() is not checked when the password comes in on stdin, so pass is
+left uninitialised if it fails, and a leading NUL byte leaves strlen()
+at 0; either way pass[strlen(pass)-1] reads before the buffer. Both
+paths fire under AddressSanitizer on the routine extracted unchanged
+from 2.29 (empty stdin, and a line starting with a NUL byte). No other
+packaging fixes this one.
+
+--- extras/htpasswd.c.orig
++++ extras/htpasswd.c
+@@ -20,6 +20,12 @@
+ #define CR 13
+
+ #define MAX_STRING_LEN 256
++/* A .htpasswd line is "user:hash". The hash of a modern crypt(3) is around a
++** hundred characters, so a line does not fit in MAX_STRING_LEN. my_getline()
++** truncates silently at its limit and the remainder is then read as the next
++** line, so reading an existing file to update it corrupted it.
++*/
++#define MAX_LINE_LEN (MAX_STRING_LEN + 1 + 256)
+
+ int tfd;
+ char temp_template[] = "/tmp/htp.XXXXXX";
+@@ -112,8 +118,13 @@
+
+ if ( ! isatty( fileno( stdin ) ) )
+ {
+- (void) fgets( pass, sizeof(pass), stdin );
+- if ( pass[strlen(pass) - 1] == '\n' )
++ /* fgets() can fail, leaving pass uninitialised, and a NUL byte on
++ ** stdin leaves strlen() at 0; either way pass[strlen(pass)-1] would
++ ** read before the buffer.
++ */
++ if ( fgets( pass, sizeof(pass), stdin ) == (char*) 0 )
++ pass[0] = '\0';
++ if ( pass[0] != '\0' && pass[strlen(pass) - 1] == '\n' )
+ pass[strlen(pass) - 1] = '\0';
+ pw = pass;
+ }
+@@ -149,8 +160,8 @@
+ int main(int argc, char *argv[]) {
+ FILE *tfp,*f;
+ char user[MAX_STRING_LEN];
+- char line[MAX_STRING_LEN];
+- char l[MAX_STRING_LEN];
++ char line[MAX_LINE_LEN];
++ char l[MAX_LINE_LEN];
+ char w[MAX_STRING_LEN];
+ char command[MAX_STRING_LEN];
+ int found;
+@@ -188,7 +199,7 @@
+ user[sizeof(user)-1] = '\0';
+
+ found = 0;
+- while(!(my_getline(line,MAX_STRING_LEN,f))) {
++ while(!(my_getline(line,MAX_LINE_LEN,f))) {
+ if(found || (line[0] == '#') || (!line[0])) {
+ putline(tfp,line);
+ continue;
Index: pkgsrc/www/thttpd/patches/patch-index.html
diff -u /dev/null pkgsrc/www/thttpd/patches/patch-index.html:1.1
--- /dev/null Fri Sep 25 14:20:40 2026
+++ pkgsrc/www/thttpd/patches/patch-index.html Fri Sep 25 14:20:40 2026
@@ -0,0 +1,18 @@
+$NetBSD: patch-index.html,v 1.1 2026/09/25 14:20:40 wiz Exp $
+
+Add the NetBSD logo to the sample index page. The image it points at
+is sitedrivenby.gif, which the package fetches as a second distfile and
+installs beside this page in share/thttpd.
+
+--- index.html.orig 2005-05-13 20:20:35.000000000 +0000
++++ index.html
+@@ -9,6 +9,9 @@
+
+ <P>
+ Here's a link to the <A HREF="http://www.acme.com/software/thttpd/">thttpd web pages</A>.
++<A HREF="http://www.netbsd.org/">
++<IMG ALIGN=RIGHT SRC="sitedrivenby.gif" border=0 ALT="Site driven by NetBSD">
++</A>
+
+ </BODY>
+ </HTML>
Index: pkgsrc/www/thttpd/patches/patch-libhttpd.c
diff -u /dev/null pkgsrc/www/thttpd/patches/patch-libhttpd.c:1.1
--- /dev/null Fri Sep 25 14:20:40 2026
+++ pkgsrc/www/thttpd/patches/patch-libhttpd.c Fri Sep 25 14:20:40 2026
@@ -0,0 +1,44 @@
+$NetBSD: patch-libhttpd.c,v 1.1 2026/09/25 14:20:40 wiz Exp $
+
+Not a security fix, and not an underflow. The stock code
+
+ i = strlen( hc->origfilename ) - strlen( hc->pathinfo );
+ if ( i > 0 && strcmp( &hc->origfilename[i], hc->pathinfo ) == 0 )
+ hc->origfilename[i - 1] = '\0';
+
+already prevents the index from going negative: the "i > 0" test means
+origfilename[i-1] is only reached with i >= 1. I checked this after
+mistaking it for the same shape as the underflows in
+patch-CVE-2007-0158; it is not one.
+
+What it does change is behaviour. When PATH_INFO is the whole of
+origfilename (i == 0) the stock code leaves origfilename alone, so the
+pathinfo part is not removed from it as the comment above it says it
+should be. This makes that case set origfilename to the empty string.
+FreeBSD ports and Debian both carry the same change; Debian files it
+under "path-info" rather than as a security patch, which is the right
+reading.
+
+Included because the neighbouring CVE patches touch the same file and a
+reader will otherwise wonder why this one spot is left as it is. Drop
+this patch if the behaviour change is not wanted -- the CVE patches do
+not depend on it.
+
+--- libhttpd.c.orig
++++ libhttpd.c
+@@ -2351,8 +2351,13 @@
+ {
+ int i;
+ i = strlen( hc->origfilename ) - strlen( hc->pathinfo );
+- if ( i > 0 && strcmp( &hc->origfilename[i], hc->pathinfo ) == 0 )
+- hc->origfilename[i - 1] = '\0';
++ if ( i >= 0 && strcmp( &hc->origfilename[i], hc->pathinfo ) == 0 )
++ {
++ if ( i == 0 )
++ hc->origfilename[0] = '\0';
++ else
++ hc->origfilename[i - 1] = '\0';
++ }
+ }
+
+ /* If the expanded filename is an absolute path, check that it's still
Index: pkgsrc/www/thttpd/patches/patch-mmc.c
diff -u /dev/null pkgsrc/www/thttpd/patches/patch-mmc.c:1.1
--- /dev/null Fri Sep 25 14:20:40 2026
+++ pkgsrc/www/thttpd/patches/patch-mmc.c Fri Sep 25 14:20:40 2026
@@ -0,0 +1,27 @@
+$NetBSD: patch-mmc.c,v 1.1 2026/09/25 14:20:40 wiz Exp $
+
+Turn off mmap on NetBSD up to 1.5L (__NetBSD_Version__ 105120000) and
+include <sys/param.h> so that version is defined.
+
+Before UBC arrived in 1.5L, mmap and read were not kept in sync, so
+thttpd went on serving the old contents of a page after it had been
+edited until it was restarted. From PR pkg/25487, where Hauke Fath
+reported it on 1.5.4 and Michael Santos wrote this hunk.
+
+--- mmc.c.orig 2015-11-05 18:13:49.000000000 +0000
++++ mmc.c
+@@ -37,6 +37,14 @@
+ #include <fcntl.h>
+ #include <syslog.h>
+ #include <errno.h>
++
++#if defined(__NetBSD__)
++#include <sys/param.h>
++#endif
++
++#if (defined(__NetBSD__) && __NetBSD_Version__ <= 105120000)
++#undef HAVE_MMAP
++#endif
+
+ #ifdef HAVE_MMAP
+ #include <sys/mman.h>
Index: pkgsrc/www/thttpd/patches/patch-thttpd.c
diff -u /dev/null pkgsrc/www/thttpd/patches/patch-thttpd.c:1.1
--- /dev/null Fri Sep 25 14:20:40 2026
+++ pkgsrc/www/thttpd/patches/patch-thttpd.c Fri Sep 25 14:20:40 2026
@@ -0,0 +1,67 @@
+$NetBSD: patch-thttpd.c,v 1.1 2026/09/25 14:20:40 wiz Exp $
+
+Not a CVE: the access log is created world-readable. Both places that
+open it use fopen(logfile, "a"), so a log that does not exist yet is
+created 0666 & ~umask -- 0644 under the usual umask. Every line holds
+the request line, the Referer and the User-Agent, and on an
+authenticated directory the remote user name, so it should not be
+readable by every local user. This matters most after log rotation,
+where the file is gone and thttpd recreates it on SIGHUP.
+
+Created with 0640 instead. open() rather than a chmod() after fopen()
+so that there is no window in which the file exists with the wider
+mode, and so that a log the administrator already created keeps the
+mode they chose -- a chmod() would overwrite it on every start.
+
+FreeBSD ports and Fedora both fix this, but with a chmod() after the
+fopen(): FreeBSD to 0640, Fedora to 0600. Fedora additionally disables
+the fchown() below, which stops thttpd handing the log to the user it
+drops privileges to, so it cannot re-open the log afterwards. The
+fchown() is left alone here.
+
+--- thttpd.c.orig
++++ thttpd.c
+@@ -327,6 +327,25 @@
+ }
+
+
++/* Open the log file for appending. A log that has to be created is created
++** without the world-readable bit: every line holds the request, the Referer
++** and the User-Agent, and on an authenticated directory the remote user name.
++** A file that already exists keeps the mode the administrator gave it. This
++** uses open() rather than fopen() so that there is no window in which the
++** file exists with the wider mode.
++*/
++static FILE*
++open_logfile( char* lf )
++ {
++ int fd;
++
++ fd = open( lf, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP );
++ if ( fd < 0 )
++ return (FILE*) 0;
++ return fdopen( fd, "a" );
++ }
++
++
+ static void
+ re_open_logfile( void )
+ {
+@@ -339,7 +358,7 @@
+ if ( logfile != (char*) 0 && strcmp( logfile, "-" ) != 0 )
+ {
+ syslog( LOG_NOTICE, "re-opening logfile" );
+- logfp = fopen( logfile, "a" );
++ logfp = open_logfile( logfile );
+ if ( logfp == (FILE*) 0 )
+ {
+ syslog( LOG_CRIT, "re-opening %.80s - %m", logfile );
+@@ -428,7 +447,7 @@
+ logfp = stdout;
+ else
+ {
+- logfp = fopen( logfile, "a" );
++ logfp = open_logfile( logfile );
+ if ( logfp == (FILE*) 0 )
+ {
+ syslog( LOG_CRIT, "%.80s - %m", logfile );
Home |
Main Index |
Thread Index |
Old Index