head 1.1; access; symbols; locks; strict; comment @# @; 1.1 date 2026.09.25.14.20.39; author wiz; state Exp; branches; next ; commitid TmvT2kz6VnKzH0XG; desc @@ 1.1 log @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. @ text @$NetBSD$ 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 ); } @