Browse Source

[mod_auth] implement and use safe_memclear, using memset_s or explicit_bzero if available

From: Loganaden Velvindron <logan@elandsys.com>

git-svn-id: svn://svn.lighttpd.net/lighttpd/branches/lighttpd-1.4.x@3045 152afb58-edef-0310-8abb-c4023f1b3aa9
svn/tags/lighttpd-1.4.38
Loganaden Velvindron 6 years ago
committed by Stefan Bühler
parent
commit
d7be04beb5
  1. 1
      NEWS
  2. 79
      SConstruct
  3. 13
      configure.ac
  4. 17
      src/CMakeLists.txt
  5. 5
      src/Makefile.am
  6. 2
      src/SConscript
  7. 2
      src/buffer.h
  8. 8
      src/config.h.cmake
  9. 303
      src/http_auth.c
  10. 50
      src/safe_memclear.c
  11. 9
      src/safe_memclear.h
  12. 2
      src/settings.h

1
NEWS

@ -8,6 +8,7 @@ NEWS
* [core] allocate at least 4k buffer for incoming data
* [core] fix search for header end if split across chunks (fixes #2670)
* [core] check configparserAlloc() result with force_assert
* [mod_auth] implement and use safe_memclear, using memset_s or explicit_bzero if available (thx loganaden)
- 1.4.37 - 2015-08-30
* [mod_proxy] remove debug log line from error log (fixes #2659)

79
SConstruct

@ -28,6 +28,52 @@ def checkTypes(autoconf, types):
if autoconf.CheckType(type, '#include <sys/types.h>'):
autoconf.env.Append(CPPFLAGS = [ '-DHAVE_' + p.sub('_', type.upper()) ])
def checkGmtOffInStructTm(context):
source = """
#include <time.h>
int main() {
struct tm a;
a.tm_gmtoff = 0;
return 0;
}
"""
context.Message('Checking for tm_gmtoff in struct tm...')
result = context.TryLink(source, '.c')
context.Result(result)
return result
def checkIPv6(context):
source = """
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main() {
struct sockaddr_in6 s; struct in6_addr t=in6addr_any; int i=AF_INET6; s; t.s6_addr[0] = 0;
return 0;
}
"""
context.Message('Checking for IPv6 support...')
result = context.TryLink(source, '.c')
context.Result(result)
return result
def checkWeakSymbols(context):
source = """
__attribute__((weak)) void __dummy(void *x) { }
int main() {
void *x;
__dummy(x);
}
"""
context.Message('Checking for weak symbol support...')
result = context.TryLink(source, '.c')
context.Result(result)
return result
def checkProgram(env, withname, progname):
withname = 'with_' + withname
binpath = None
@ -54,22 +100,6 @@ def checkProgram(env, withname, progname):
return binpath
def checkStructMember(context):
struct_member = """
#include <time.h>
int main() {
struct tm a;
a.tm_gmtoff = 0;
return 0;
}
"""
context.Message('Checking for tm_gmtoff in struct tm...')
result = context.TryLink(struct_member, '.c')
context.Result(result)
return result
VariantDir('sconsbuild/build', 'src', duplicate = 0)
VariantDir('sconsbuild/tests', 'tests', duplicate = 0)
@ -114,7 +144,11 @@ if env['CC'] == 'gcc':
# cache configure checks
if 1:
autoconf = Configure(env, custom_tests = {'CheckStructMember': checkStructMember })
autoconf = Configure(env, custom_tests = {
'CheckGmtOffInStructTm': checkGmtOffInStructTm,
'CheckIPv6': checkIPv6,
'CheckWeakSymbols': checkWeakSymbols,
})
if 'CFLAGS' in os.environ:
autoconf.env.Append(CCFLAGS = os.environ['CFLAGS'])
@ -175,7 +209,8 @@ if 1:
strdup strerror strstr strtol sendfile getopt socket \
gethostbyname poll epoll_ctl getrlimit chroot \
getuid select signal pathconf madvise prctl\
writev sigaction sendfile64 send_file kqueue port_create localtime_r posix_fadvise issetugid inet_pton'))
writev sigaction sendfile64 send_file kqueue port_create localtime_r posix_fadvise issetugid inet_pton \
memset_s explicit_bzero'))
checkTypes(autoconf, Split('pid_t size_t off_t'))
@ -242,9 +277,15 @@ if 1:
if autoconf.CheckType('struct sockaddr_storage', '#include <sys/socket.h>\n'):
autoconf.env.Append(CPPFLAGS = [ '-DHAVE_STRUCT_SOCKADDR_STORAGE' ])
if autoconf.CheckStructMember():
if autoconf.CheckGmtOffInStructTm():
autoconf.env.Append(CPPFLAGS = [ '-DHAVE_STRUCT_TM_GMTOFF' ])
if autoconf.CheckIPv6():
autoconf.env.Append(CPPFLAGS = [ '-DHAVE_IPV6' ])
if autoconf.CheckWeakSymbols():
autoconf.env.Append(CPPFLAGS = [ '-DHAVE_WEAK_SYMBOLS' ])
env = autoconf.Finish()
def TryLua(env, name):

13
configure.ac

@ -586,7 +586,18 @@ AC_CHECK_FUNCS([dup2 getcwd inet_ntoa inet_ntop memset mmap munmap strchr \
strdup strerror strstr strtol sendfile getopt socket lstat \
gethostbyname poll epoll_ctl getrlimit chroot \
getuid select signal pathconf madvise posix_fadvise posix_madvise \
writev sigaction sendfile64 send_file kqueue port_create localtime_r gmtime_r])
writev sigaction sendfile64 send_file kqueue port_create localtime_r gmtime_r \
memset_s explicit_bzero])
AC_MSG_CHECKING(if weak symbols are supported)
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
__attribute__((weak)) void __dummy(void *x) { }
void f(void *x) { __dummy(x); }
]])],
[
AC_MSG_RESULT(yes)
AC_DEFINE([HAVE_WEAK_SYMBOLS], [1], [weak symbols are supported])
],[AC_MSG_RESULT(no)])
AC_MSG_CHECKING(for Large File System support)
AC_ARG_ENABLE(lfs,

17
src/CMakeLists.txt

@ -105,6 +105,7 @@ else()
check_library_exists(crypt crypt "" HAVE_LIBCRYPT)
endif()
check_function_exists(crypt_r HAVE_CRYPT_R)
check_function_exists(crypt HAVE_CRYPT)
check_include_files(sys/inotify.h HAVE_SYS_INOTIFY_H)
if(HAVE_SYS_INOTIFY_H)
@ -119,7 +120,6 @@ check_type_size(long SIZEOF_LONG)
check_type_size(off_t SIZEOF_OFF_T)
check_function_exists(chroot HAVE_CHROOT)
check_function_exists(crypt HAVE_CRYPT)
check_function_exists(epoll_ctl HAVE_EPOLL_CTL)
check_function_exists(fork HAVE_FORK)
check_function_exists(getrlimit HAVE_GETRLIMIT)
@ -151,6 +151,10 @@ check_function_exists(strptime HAVE_STRPTIME)
check_function_exists(syslog HAVE_SYSLOG)
check_function_exists(writev HAVE_WRITEV)
check_function_exists(inet_aton HAVE_INET_ATON)
check_function_exists(issetugid HAVE_ISSETUGID)
check_function_exists(inet_pton HAVE_INET_PTON)
check_function_exists(memset_s HAVE_MEMSET_S)
check_function_exists(explicit_bzero HAVE_EXPLICIT_BZERO)
check_c_source_compiles("
#include <sys/types.h>
#include <sys/socket.h>
@ -160,8 +164,13 @@ check_c_source_compiles("
struct sockaddr_in6 s; struct in6_addr t=in6addr_any; int i=AF_INET6; s; t.s6_addr[0] = 0;
return 0;
}" HAVE_IPV6)
check_function_exists(issetugid HAVE_ISSETUGID)
check_function_exists(inet_pton HAVE_INET_PTON)
check_c_source_compiles("
__attribute__((weak)) void __dummy(void *x) { }
int main() {
void *x;
__dummy(x);
}
" HAVE_WEAK_SYMBOLS)
## refactor me
macro(XCONFIG _package _include_DIR _link_DIR _link_FLAGS _cflags)
@ -485,7 +494,7 @@ set(COMMON_SRC
network_write.c network_linux_sendfile.c
network_freebsd_sendfile.c
network_solaris_sendfilev.c network_openssl.c
status_counter.c
status_counter.c safe_memclear.c
)
if(WIN32)

5
src/Makefile.am

@ -75,7 +75,8 @@ common_src=buffer.c log.c \
network_write_mmap.c network_write_no_mmap.c \
network_freebsd_sendfile.c network_writev.c \
network_solaris_sendfilev.c network_openssl.c \
splaytree.c status_counter.c
splaytree.c status_counter.c \
safe_memclear.c
src = server.c response.c connections.c network.c \
configfile.c configparser.c request.c proc_open.c
@ -278,7 +279,7 @@ hdr = server.h buffer.h network.h log.h keyvalue.h \
mod_ssi.h mod_ssi_expr.h inet_ntop_cache.h \
configparser.h mod_ssi_exprparser.h \
sys-mmap.h sys-socket.h mod_cml.h mod_cml_funcs.h \
splaytree.h proc_open.h status_counter.h \
safe_memclear.h splaytree.h proc_open.h status_counter.h \
mod_magnet_cache.h \
version.h

2
src/SConscript

@ -28,7 +28,7 @@ common_src = Split("buffer.c log.c \
network_write.c network_linux_sendfile.c \
network_freebsd_sendfile.c \
network_solaris_sendfilev.c network_openssl.c \
status_counter.c \
status_counter.c safe_memclear.c \
")
src = Split("server.c response.c connections.c network.c \

2
src/buffer.h

@ -170,8 +170,6 @@ static inline void buffer_append_slash(buffer *b); /* append '/' no non-empty st
#define CONST_BUF_LEN(x) ((x) ? (x)->ptr : NULL), buffer_string_length(x)
#define UNUSED(x) ( (void)(x) )
void print_backtrace(FILE *file);
void log_failed_assert(const char *filename, unsigned int line, const char *msg) LI_NORETURN;
#define force_assert(x) do { if (!(x)) log_failed_assert(__FILE__, __LINE__, "assertion failed: " #x); } while(0)

8
src/config.h.cmake

@ -26,9 +26,8 @@
#cmakedefine HAVE_SYS_TIME_H
#cmakedefine HAVE_UNISTD_H
#cmakedefine HAVE_PTHREAD_H
#cmakedefine HAVE_INET_ATON
#cmakedefine HAVE_IPV6
#cmakedefine HAVE_ISSETUGID
#cmakedefine HAVE_WEAK_SYMBOLS
/* XATTR */
#cmakedefine HAVE_ATTR_ATTRIBUTES_H
@ -143,6 +142,11 @@
#cmakedefine HAVE_STRPTIME
#cmakedefine HAVE_SYSLOG
#cmakedefine HAVE_WRITEV
#cmakedefine HAVE_INET_ATON
#cmakedefine HAVE_ISSETUGID
#cmakedefine HAVE_INET_PTON
#cmakedefine HAVE_MEMSET_S
#cmakedefine HAVE_EXPLICIT_BZERO
/* libcrypt */
#cmakedefine HAVE_CRYPT_H

303
src/http_auth.c

@ -34,6 +34,8 @@
#include <openssl/sha.h>
#endif
#include "safe_memclear.h"
#define HASHLEN 16
#define HASHHEXLEN 32
typedef unsigned char HASH[HASHLEN];
@ -431,164 +433,165 @@ int http_auth_match_rules(server *srv, array *req, const char *username, const c
static void to64(char *s, unsigned long v, int n)
{
static const unsigned char itoa64[] = /* 0 ... 63 => ASCII - 64 */
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
static const unsigned char itoa64[] = /* 0 ... 63 => ASCII - 64 */
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
while (--n >= 0) {
*s++ = itoa64[v&0x3f];
v >>= 6;
}
while (--n >= 0) {
*s++ = itoa64[v&0x3f];
v >>= 6;
}
}
static void apr_md5_encode(const char *pw, const char *salt, char *result, size_t nbytes) {
/*
* Minimum size is 8 bytes for salt, plus 1 for the trailing NUL,
* plus 4 for the '$' separators, plus the password hash itself.
* Let's leave a goodly amount of leeway.
*/
char passwd[120], *p;
const char *sp, *ep;
unsigned char final[APR_MD5_DIGESTSIZE];
ssize_t sl, pl, i;
li_MD5_CTX ctx, ctx1;
unsigned long l;
/*
* Refine the salt first. It's possible we were given an already-hashed
* string as the salt argument, so extract the actual salt value from it
* if so. Otherwise just use the string up to the first '$' as the salt.
*/
sp = salt;
/*
* If it starts with the magic string, then skip that.
*/
if (!strncmp(sp, APR1_ID, strlen(APR1_ID))) {
sp += strlen(APR1_ID);
}
/*
* It stops at the first '$' or 8 chars, whichever comes first
*/
for (ep = sp; (*ep != '\0') && (*ep != '$') && (ep < (sp + 8)); ep++) {
continue;
}
/*
* Get the length of the true salt
*/
sl = ep - sp;
/*
* 'Time to make the doughnuts..'
*/
li_MD5_Init(&ctx);
/*
* The password first, since that is what is most unknown
*/
li_MD5_Update(&ctx, pw, strlen(pw));
/*
* Then our magic string
*/
li_MD5_Update(&ctx, APR1_ID, strlen(APR1_ID));
/*
* Then the raw salt
*/
li_MD5_Update(&ctx, sp, sl);
/*
* Then just as many characters of the MD5(pw, salt, pw)
*/
li_MD5_Init(&ctx1);
li_MD5_Update(&ctx1, pw, strlen(pw));
li_MD5_Update(&ctx1, sp, sl);
li_MD5_Update(&ctx1, pw, strlen(pw));
li_MD5_Final(final, &ctx1);
for (pl = strlen(pw); pl > 0; pl -= APR_MD5_DIGESTSIZE) {
li_MD5_Update(&ctx, final,
(pl > APR_MD5_DIGESTSIZE) ? APR_MD5_DIGESTSIZE : pl);
}
/*
* Don't leave anything around in vm they could use.
*/
memset(final, 0, sizeof(final));
/*
* Then something really weird...
*/
for (i = strlen(pw); i != 0; i >>= 1) {
if (i & 1) {
li_MD5_Update(&ctx, final, 1);
}
else {
li_MD5_Update(&ctx, pw, 1);
}
}
/*
* Now make the output string. We know our limitations, so we
* can use the string routines without bounds checking.
*/
strcpy(passwd, APR1_ID);
strncat(passwd, sp, sl);
strcat(passwd, "$");
li_MD5_Final(final, &ctx);
/*
* And now, just to make sure things don't run too fast..
* On a 60 Mhz Pentium this takes 34 msec, so you would
* need 30 seconds to build a 1000 entry dictionary...
*/
for (i = 0; i < 1000; i++) {
li_MD5_Init(&ctx1);
if (i & 1) {
li_MD5_Update(&ctx1, pw, strlen(pw));
}
else {
li_MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE);
}
if (i % 3) {
li_MD5_Update(&ctx1, sp, sl);
}
if (i % 7) {
li_MD5_Update(&ctx1, pw, strlen(pw));
}
if (i & 1) {
li_MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE);
}
else {
li_MD5_Update(&ctx1, pw, strlen(pw));
}
li_MD5_Final(final,&ctx1);
}
p = passwd + strlen(passwd);
l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p, l, 4); p += 4;
l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p, l, 4); p += 4;
l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p, l, 4); p += 4;
l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p, l, 4); p += 4;
l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p, l, 4); p += 4;
l = final[11] ; to64(p, l, 2); p += 2;
*p = '\0';
/*
* Don't leave anything around in vm they could use.
*/
memset(final, 0, sizeof(final));
/*
* Minimum size is 8 bytes for salt, plus 1 for the trailing NUL,
* plus 4 for the '$' separators, plus the password hash itself.
* Let's leave a goodly amount of leeway.
*/
char passwd[120], *p;
const char *sp, *ep;
unsigned char final[APR_MD5_DIGESTSIZE];
ssize_t sl, pl, i;
li_MD5_CTX ctx, ctx1;
unsigned long l;
/*
* Refine the salt first. It's possible we were given an already-hashed
* string as the salt argument, so extract the actual salt value from it
* if so. Otherwise just use the string up to the first '$' as the salt.
*/
sp = salt;
/*
* If it starts with the magic string, then skip that.
*/
if (!strncmp(sp, APR1_ID, strlen(APR1_ID))) {
sp += strlen(APR1_ID);
}
/*
* It stops at the first '$' or 8 chars, whichever comes first
*/
for (ep = sp; (*ep != '\0') && (*ep != '$') && (ep < (sp + 8)); ep++) {
continue;
}
/*
* Get the length of the true salt
*/
sl = ep - sp;
/*
* 'Time to make the doughnuts..'
*/
li_MD5_Init(&ctx);
/*
* The password first, since that is what is most unknown
*/
li_MD5_Update(&ctx, pw, strlen(pw));
/*
* Then our magic string
*/
li_MD5_Update(&ctx, APR1_ID, strlen(APR1_ID));
/*
* Then the raw salt
*/
li_MD5_Update(&ctx, sp, sl);
/*
* Then just as many characters of the MD5(pw, salt, pw)
*/
li_MD5_Init(&ctx1);
li_MD5_Update(&ctx1, pw, strlen(pw));
li_MD5_Update(&ctx1, sp, sl);
li_MD5_Update(&ctx1, pw, strlen(pw));
li_MD5_Final(final, &ctx1);
for (pl = strlen(pw); pl > 0; pl -= APR_MD5_DIGESTSIZE) {
li_MD5_Update(
&ctx, final,
(pl > APR_MD5_DIGESTSIZE) ? APR_MD5_DIGESTSIZE : pl);
}
/*
* Don't leave anything around in vm they could use.
*/
memset(final, 0, sizeof(final));
/*
* Then something really weird...
*/
for (i = strlen(pw); i != 0; i >>= 1) {
if (i & 1) {
li_MD5_Update(&ctx, final, 1);
}
else {
li_MD5_Update(&ctx, pw, 1);
}
}
/*
* Now make the output string. We know our limitations, so we
* can use the string routines without bounds checking.
*/
strcpy(passwd, APR1_ID);
strncat(passwd, sp, sl);
strcat(passwd, "$");
li_MD5_Final(final, &ctx);
/*
* And now, just to make sure things don't run too fast..
* On a 60 Mhz Pentium this takes 34 msec, so you would
* need 30 seconds to build a 1000 entry dictionary...
*/
for (i = 0; i < 1000; i++) {
li_MD5_Init(&ctx1);
if (i & 1) {
li_MD5_Update(&ctx1, pw, strlen(pw));
}
else {
li_MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE);
}
if (i % 3) {
li_MD5_Update(&ctx1, sp, sl);
}
if (i % 7) {
li_MD5_Update(&ctx1, pw, strlen(pw));
}
if (i & 1) {
li_MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE);
}
else {
li_MD5_Update(&ctx1, pw, strlen(pw));
}
li_MD5_Final(final,&ctx1);
}
p = passwd + strlen(passwd);
l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p, l, 4); p += 4;
l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p, l, 4); p += 4;
l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p, l, 4); p += 4;
l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p, l, 4); p += 4;
l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p, l, 4); p += 4;
l = final[11] ; to64(p, l, 2); p += 2;
*p = '\0';
/*
* Don't leave anything around in vm they could use.
*/
safe_memclear(final, sizeof(final));
/* FIXME
*/
#define apr_cpystrn strncpy
apr_cpystrn(result, passwd, nbytes - 1);
apr_cpystrn(result, passwd, nbytes - 1);
}
#ifdef USE_OPENSSL

50
src/safe_memclear.c

@ -0,0 +1,50 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "settings.h"
#include "safe_memclear.h"
#include <string.h>
#if !defined(HAVE_MEMSET_S) && !defined(HAVE_EXPLICIT_BZERO)
# if defined(HAVE_WEAK_SYMBOLS)
/* it seems weak functions are never inlined, even for static builds */
__attribute__((weak)) void __li_safe_memset_hook(void *buf, size_t len);
void __li_safe_memset_hook(void *buf, size_t len)
{
UNUSED(buf);
UNUSED(len);
}
# endif /* HAVE_WEAK_SYMBOLS */
static void* safe_memset(void *s, int c, size_t n)
{
if (n > 0) {
volatile unsigned volatile_zero = 0;
volatile unsigned char *vs = (volatile unsigned char*)s;
do {
memset(s, c, n);
} while (vs[volatile_zero] != (unsigned char)c);
# if defined(HAVE_WEAK_SYMBOLS)
__li_safe_memset_hook(s, n);
# endif /* HAVE_WEAK_SYMBOLS */
}
return s;
}
#endif /* !defined(HAVE_MEMSET_S) && !defined(HAVE_EXPLICIT_BZERO) */
void safe_memclear(void *s, size_t n) {
#if defined(HAVE_MEMSET_S)
memset_s(s, 0, n);
#elif defined(HAVE_EXPLICIT_BZERO)
explicit_bzero(s, n);
#else
safe_memset(s, 0, n);
#endif
}

9
src/safe_memclear.h

@ -0,0 +1,9 @@
#ifndef _SAFE_MEMCLEAR_H_
#define _SAFE_MEMCLEAR_H_
/* size_t */
#include <sys/types.h>
void safe_memclear(void *s, size_t n);
#endif

2
src/settings.h

@ -15,6 +15,8 @@
# define LI_NORETURN
#endif
#define UNUSED(x) ( (void)(x) )
#define BV(x) (1 << x)
#define INET_NTOP_CACHE_MAX 4

Loading…
Cancel
Save