|
|
7c7f29 |
From f0e03b5a51972a125fe78f448d1f68e288782d1e Mon Sep 17 00:00:00 2001
|
|
|
7c7f29 |
From: William Brown <firstyear@redhat.com>
|
|
|
7c7f29 |
Date: Thu, 21 Jul 2016 13:22:30 +1000
|
|
|
7c7f29 |
Subject: [PATCH 23/29] Ticket bz1358565 - clear and unsalted password types
|
|
|
7c7f29 |
are vulnerable to timing attack
|
|
|
7c7f29 |
|
|
|
7c7f29 |
Bug Description: Clear and unsalted password types were vulnerable to a timing
|
|
|
7c7f29 |
attack. This is due to the use of memcmp and strcmp in their comparison.
|
|
|
7c7f29 |
|
|
|
7c7f29 |
Fix Description: Add a constant time memcmp function, that does not shortcircuit.
|
|
|
7c7f29 |
Change all password comparison to use the constant time check. For the clear
|
|
|
7c7f29 |
scheme, alter the way we do the check to prevent length disclosure timing
|
|
|
7c7f29 |
attacks.
|
|
|
7c7f29 |
|
|
|
7c7f29 |
This resolves CVE-2016-5405
|
|
|
7c7f29 |
|
|
|
7c7f29 |
https://bugzilla.redhat.com/show_bug.cgi?id=1358565
|
|
|
7c7f29 |
|
|
|
7c7f29 |
https://access.redhat.com/security/cve/CVE-2016-5405
|
|
|
7c7f29 |
|
|
|
7c7f29 |
Author: wibrown
|
|
|
7c7f29 |
|
|
|
7c7f29 |
Review by: nhosoi (Thanks!)
|
|
|
7c7f29 |
|
|
|
7c7f29 |
(cherry picked from commit 9dcaa4a0c866d8696e0a2616ccf962af2833f0b8)
|
|
|
7c7f29 |
---
|
|
|
7c7f29 |
dirsrvtests/tests/suites/password/pwd_algo_test.py | 143 +++++++++++++++++++++
|
|
|
7c7f29 |
ldap/servers/plugins/pwdstorage/clear_pwd.c | 33 ++++-
|
|
|
7c7f29 |
ldap/servers/plugins/pwdstorage/crypt_pwd.c | 2 +-
|
|
|
7c7f29 |
ldap/servers/plugins/pwdstorage/md5_pwd.c | 2 +-
|
|
|
7c7f29 |
ldap/servers/plugins/pwdstorage/ns-mta-md5_pwd.c | 1 +
|
|
|
7c7f29 |
ldap/servers/plugins/pwdstorage/sha_pwd.c | 15 ++-
|
|
|
7c7f29 |
ldap/servers/plugins/pwdstorage/smd5_pwd.c | 2 +-
|
|
|
7c7f29 |
ldap/servers/slapd/ch_malloc.c | 22 ++++
|
|
|
7c7f29 |
ldap/servers/slapd/slapi-plugin.h | 16 +++
|
|
|
7c7f29 |
9 files changed, 226 insertions(+), 10 deletions(-)
|
|
|
7c7f29 |
create mode 100644 dirsrvtests/tests/suites/password/pwd_algo_test.py
|
|
|
7c7f29 |
|
|
|
7c7f29 |
diff --git a/dirsrvtests/tests/suites/password/pwd_algo_test.py b/dirsrvtests/tests/suites/password/pwd_algo_test.py
|
|
|
7c7f29 |
new file mode 100644
|
|
|
7c7f29 |
index 0000000..aa8cbf5
|
|
|
7c7f29 |
--- /dev/null
|
|
|
7c7f29 |
+++ b/dirsrvtests/tests/suites/password/pwd_algo_test.py
|
|
|
7c7f29 |
@@ -0,0 +1,143 @@
|
|
|
7c7f29 |
+import os
|
|
|
7c7f29 |
+import sys
|
|
|
7c7f29 |
+import time
|
|
|
7c7f29 |
+import ldap
|
|
|
7c7f29 |
+import logging
|
|
|
7c7f29 |
+import pytest
|
|
|
7c7f29 |
+from lib389 import DirSrv, Entry, tools, tasks
|
|
|
7c7f29 |
+from lib389.tools import DirSrvTools
|
|
|
7c7f29 |
+from lib389._constants import *
|
|
|
7c7f29 |
+from lib389.properties import *
|
|
|
7c7f29 |
+from lib389.tasks import *
|
|
|
7c7f29 |
+from lib389.utils import *
|
|
|
7c7f29 |
+
|
|
|
7c7f29 |
+DEBUGGING = True
|
|
|
7c7f29 |
+USER_DN = 'uid=user,ou=People,%s' % DEFAULT_SUFFIX
|
|
|
7c7f29 |
+
|
|
|
7c7f29 |
+if DEBUGGING:
|
|
|
7c7f29 |
+ logging.getLogger(__name__).setLevel(logging.DEBUG)
|
|
|
7c7f29 |
+else:
|
|
|
7c7f29 |
+ logging.getLogger(__name__).setLevel(logging.INFO)
|
|
|
7c7f29 |
+
|
|
|
7c7f29 |
+
|
|
|
7c7f29 |
+log = logging.getLogger(__name__)
|
|
|
7c7f29 |
+
|
|
|
7c7f29 |
+
|
|
|
7c7f29 |
+class TopologyStandalone(object):
|
|
|
7c7f29 |
+ """The DS Topology Class"""
|
|
|
7c7f29 |
+ def __init__(self, standalone):
|
|
|
7c7f29 |
+ """Init"""
|
|
|
7c7f29 |
+ standalone.open()
|
|
|
7c7f29 |
+ self.standalone = standalone
|
|
|
7c7f29 |
+
|
|
|
7c7f29 |
+
|
|
|
7c7f29 |
+@pytest.fixture(scope="module")
|
|
|
7c7f29 |
+def topology(request):
|
|
|
7c7f29 |
+ """Create DS Deployment"""
|
|
|
7c7f29 |
+
|
|
|
7c7f29 |
+ # Creating standalone instance ...
|
|
|
7c7f29 |
+ if DEBUGGING:
|
|
|
7c7f29 |
+ standalone = DirSrv(verbose=True)
|
|
|
7c7f29 |
+ else:
|
|
|
7c7f29 |
+ standalone = DirSrv(verbose=False)
|
|
|
7c7f29 |
+ args_instance[SER_HOST] = HOST_STANDALONE
|
|
|
7c7f29 |
+ args_instance[SER_PORT] = PORT_STANDALONE
|
|
|
7c7f29 |
+ args_instance[SER_SERVERID_PROP] = SERVERID_STANDALONE
|
|
|
7c7f29 |
+ args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX
|
|
|
7c7f29 |
+ args_standalone = args_instance.copy()
|
|
|
7c7f29 |
+ standalone.allocate(args_standalone)
|
|
|
7c7f29 |
+ instance_standalone = standalone.exists()
|
|
|
7c7f29 |
+ if instance_standalone:
|
|
|
7c7f29 |
+ standalone.delete()
|
|
|
7c7f29 |
+ standalone.create()
|
|
|
7c7f29 |
+ standalone.open()
|
|
|
7c7f29 |
+
|
|
|
7c7f29 |
+ def fin():
|
|
|
7c7f29 |
+ """If we are debugging just stop the instances, otherwise remove
|
|
|
7c7f29 |
+ them
|
|
|
7c7f29 |
+ """
|
|
|
7c7f29 |
+ if DEBUGGING:
|
|
|
7c7f29 |
+ standalone.stop()
|
|
|
7c7f29 |
+ else:
|
|
|
7c7f29 |
+ standalone.delete()
|
|
|
7c7f29 |
+
|
|
|
7c7f29 |
+ request.addfinalizer(fin)
|
|
|
7c7f29 |
+
|
|
|
7c7f29 |
+ # Clear out the tmp dir
|
|
|
7c7f29 |
+ standalone.clearTmpDir(__file__)
|
|
|
7c7f29 |
+
|
|
|
7c7f29 |
+ return TopologyStandalone(standalone)
|
|
|
7c7f29 |
+
|
|
|
7c7f29 |
+def _test_bind(inst, password):
|
|
|
7c7f29 |
+ result = True
|
|
|
7c7f29 |
+ userconn = ldap.initialize("ldap://%s:%s" % (HOST_STANDALONE, PORT_STANDALONE))
|
|
|
7c7f29 |
+ try:
|
|
|
7c7f29 |
+ userconn.simple_bind_s(USER_DN, password)
|
|
|
7c7f29 |
+ userconn.unbind_s()
|
|
|
7c7f29 |
+ except ldap.INVALID_CREDENTIALS:
|
|
|
7c7f29 |
+ result = False
|
|
|
7c7f29 |
+ return result
|
|
|
7c7f29 |
+
|
|
|
7c7f29 |
+def _test_algo(inst, algo_name):
|
|
|
7c7f29 |
+ inst.config.set('passwordStorageScheme', algo_name)
|
|
|
7c7f29 |
+
|
|
|
7c7f29 |
+ if DEBUGGING:
|
|
|
7c7f29 |
+ print('Testing %s', algo_name)
|
|
|
7c7f29 |
+
|
|
|
7c7f29 |
+ # Create the user with a password
|
|
|
7c7f29 |
+ inst.add_s(Entry((
|
|
|
7c7f29 |
+ USER_DN, {
|
|
|
7c7f29 |
+ 'objectClass': 'top account simplesecurityobject'.split(),
|
|
|
7c7f29 |
+ 'uid': 'user',
|
|
|
7c7f29 |
+ 'userpassword': 'Secret123'
|
|
|
7c7f29 |
+ })))
|
|
|
7c7f29 |
+
|
|
|
7c7f29 |
+ # Make sure when we read the userPassword field, it is the correct ALGO
|
|
|
7c7f29 |
+ pw_field = inst.search_s(USER_DN, ldap.SCOPE_BASE, '(objectClass=*)', ['userPassword'] )[0]
|
|
|
7c7f29 |
+
|
|
|
7c7f29 |
+ if DEBUGGING:
|
|
|
7c7f29 |
+ print(pw_field.getValue('userPassword'))
|
|
|
7c7f29 |
+
|
|
|
7c7f29 |
+ if algo_name != 'CLEAR':
|
|
|
7c7f29 |
+ assert(algo_name.lower() in pw_field.getValue('userPassword').lower())
|
|
|
7c7f29 |
+ # Now make sure a bind works
|
|
|
7c7f29 |
+ assert(_test_bind(inst, 'Secret123'))
|
|
|
7c7f29 |
+ # Bind with a wrong shorter password, should fail
|
|
|
7c7f29 |
+ assert(not _test_bind(inst, 'Wrong'))
|
|
|
7c7f29 |
+ # Bind with a wrong longer password, should fail
|
|
|
7c7f29 |
+ assert(not _test_bind(inst, 'This is even more wrong'))
|
|
|
7c7f29 |
+ # Bind with a wrong exact length password.
|
|
|
7c7f29 |
+ assert(not _test_bind(inst, 'Alsowrong'))
|
|
|
7c7f29 |
+ # Bind with a subset password, should fail
|
|
|
7c7f29 |
+ assert(not _test_bind(inst, 'Secret'))
|
|
|
7c7f29 |
+ if algo_name != 'CRYPT':
|
|
|
7c7f29 |
+ # Bind with a subset password that is 1 char shorter, to detect off by 1 in clear
|
|
|
7c7f29 |
+ assert(not _test_bind(inst, 'Secret12'))
|
|
|
7c7f29 |
+ # Bind with a superset password, should fail
|
|
|
7c7f29 |
+ assert(not _test_bind(inst, 'Secret123456'))
|
|
|
7c7f29 |
+ # Delete the user
|
|
|
7c7f29 |
+ inst.delete_s(USER_DN)
|
|
|
7c7f29 |
+ # done!
|
|
|
7c7f29 |
+
|
|
|
7c7f29 |
+def test_pwd_algo_test(topology):
|
|
|
7c7f29 |
+ """
|
|
|
7c7f29 |
+ Assert that all of our password algorithms correctly PASS and FAIL varying
|
|
|
7c7f29 |
+ password conditions.
|
|
|
7c7f29 |
+
|
|
|
7c7f29 |
+ """
|
|
|
7c7f29 |
+ if DEBUGGING:
|
|
|
7c7f29 |
+ # Add debugging steps(if any)...
|
|
|
7c7f29 |
+ pass
|
|
|
7c7f29 |
+
|
|
|
7c7f29 |
+ for algo in ('CLEAR', 'CRYPT', 'MD5', 'SHA', 'SHA256', 'SHA384', 'SHA512', 'SMD5', 'SSHA', 'SSHA256', 'SSHA384', 'SSHA512'):
|
|
|
7c7f29 |
+ _test_algo(topology.standalone, algo)
|
|
|
7c7f29 |
+
|
|
|
7c7f29 |
+ log.info('Test PASSED')
|
|
|
7c7f29 |
+
|
|
|
7c7f29 |
+
|
|
|
7c7f29 |
+if __name__ == '__main__':
|
|
|
7c7f29 |
+ # Run isolated
|
|
|
7c7f29 |
+ # -s for DEBUG mode
|
|
|
7c7f29 |
+ CURRENT_FILE = os.path.realpath(__file__)
|
|
|
7c7f29 |
+ pytest.main("-s %s" % CURRENT_FILE)
|
|
|
7c7f29 |
+
|
|
|
7c7f29 |
diff --git a/ldap/servers/plugins/pwdstorage/clear_pwd.c b/ldap/servers/plugins/pwdstorage/clear_pwd.c
|
|
|
7c7f29 |
index 84dac2a..2afe16e 100644
|
|
|
7c7f29 |
--- a/ldap/servers/plugins/pwdstorage/clear_pwd.c
|
|
|
7c7f29 |
+++ b/ldap/servers/plugins/pwdstorage/clear_pwd.c
|
|
|
7c7f29 |
@@ -25,7 +25,38 @@
|
|
|
7c7f29 |
int
|
|
|
7c7f29 |
clear_pw_cmp( const char *userpwd, const char *dbpwd )
|
|
|
7c7f29 |
{
|
|
|
7c7f29 |
- return( strcmp( userpwd, dbpwd ));
|
|
|
7c7f29 |
+ int result = 0;
|
|
|
7c7f29 |
+ int len = 0;
|
|
|
7c7f29 |
+ int len_user = strlen(userpwd);
|
|
|
7c7f29 |
+ int len_dbp = strlen(dbpwd);
|
|
|
7c7f29 |
+ if ( len_user != len_dbp ) {
|
|
|
7c7f29 |
+ result = 1;
|
|
|
7c7f29 |
+ }
|
|
|
7c7f29 |
+ /* We have to do this comparison ANYWAY else we have a length timing attack. */
|
|
|
7c7f29 |
+ if ( len_user >= len_dbp ) {
|
|
|
7c7f29 |
+ /*
|
|
|
7c7f29 |
+ * If they are the same length, result will be 0 here, and if we pass
|
|
|
7c7f29 |
+ * the check, we don't update result either. IE we pass.
|
|
|
7c7f29 |
+ * However, even if the first part of userpw matches dbpwd, but len !=, we
|
|
|
7c7f29 |
+ * have already failed anyawy. This prevents substring matching.
|
|
|
7c7f29 |
+ */
|
|
|
7c7f29 |
+ if (slapi_ct_memcmp(userpwd, dbpwd, len_dbp) != 0) {
|
|
|
7c7f29 |
+ result = 1;
|
|
|
7c7f29 |
+ }
|
|
|
7c7f29 |
+ } else {
|
|
|
7c7f29 |
+ /*
|
|
|
7c7f29 |
+ * If we stretched the userPassword, we'll allow a new timing attack, where
|
|
|
7c7f29 |
+ * if we see a delay on a short pw, we know we are stretching.
|
|
|
7c7f29 |
+ * when the delay goes away, it means we've found the length.
|
|
|
7c7f29 |
+ * Instead, because we don't want to use the short pw for comp, we just compare
|
|
|
7c7f29 |
+ * dbpwd to itself. We have already got result == 1 if we are here, so we are
|
|
|
7c7f29 |
+ * just trying to take up time!
|
|
|
7c7f29 |
+ */
|
|
|
7c7f29 |
+ if (slapi_ct_memcmp(dbpwd, dbpwd, len_dbp)) {
|
|
|
7c7f29 |
+ /* Do nothing, we have the if to fix a coverity check. */
|
|
|
7c7f29 |
+ }
|
|
|
7c7f29 |
+ }
|
|
|
7c7f29 |
+ return result;
|
|
|
7c7f29 |
}
|
|
|
7c7f29 |
|
|
|
7c7f29 |
char *
|
|
|
7c7f29 |
diff --git a/ldap/servers/plugins/pwdstorage/crypt_pwd.c b/ldap/servers/plugins/pwdstorage/crypt_pwd.c
|
|
|
7c7f29 |
index 29355a2..93b54b2 100644
|
|
|
7c7f29 |
--- a/ldap/servers/plugins/pwdstorage/crypt_pwd.c
|
|
|
7c7f29 |
+++ b/ldap/servers/plugins/pwdstorage/crypt_pwd.c
|
|
|
7c7f29 |
@@ -54,7 +54,7 @@ crypt_pw_cmp( const char *userpwd, const char *dbpwd )
|
|
|
7c7f29 |
/* we use salt (first 2 chars) of encoded password in call to crypt() */
|
|
|
7c7f29 |
cp = crypt( userpwd, dbpwd );
|
|
|
7c7f29 |
if (cp) {
|
|
|
7c7f29 |
- rc= strcmp( dbpwd, cp);
|
|
|
7c7f29 |
+ rc= slapi_ct_memcmp( dbpwd, cp, strlen(dbpwd));
|
|
|
7c7f29 |
} else {
|
|
|
7c7f29 |
rc = -1;
|
|
|
7c7f29 |
}
|
|
|
7c7f29 |
diff --git a/ldap/servers/plugins/pwdstorage/md5_pwd.c b/ldap/servers/plugins/pwdstorage/md5_pwd.c
|
|
|
7c7f29 |
index 0bc8f3c..181661a 100644
|
|
|
7c7f29 |
--- a/ldap/servers/plugins/pwdstorage/md5_pwd.c
|
|
|
7c7f29 |
+++ b/ldap/servers/plugins/pwdstorage/md5_pwd.c
|
|
|
7c7f29 |
@@ -57,7 +57,7 @@ md5_pw_cmp( const char *userpwd, const char *dbpwd )
|
|
|
7c7f29 |
bver = NSSBase64_EncodeItem(NULL, (char *)b2a_out, sizeof b2a_out, &binary_item);
|
|
|
7c7f29 |
/* bver points to b2a_out upon success */
|
|
|
7c7f29 |
if (bver) {
|
|
|
7c7f29 |
- rc = strcmp(bver,dbpwd);
|
|
|
7c7f29 |
+ rc = slapi_ct_memcmp(bver,dbpwd, strlen(dbpwd));
|
|
|
7c7f29 |
} else {
|
|
|
7c7f29 |
slapi_log_error(SLAPI_LOG_PLUGIN, MD5_SUBSYSTEM_NAME,
|
|
|
7c7f29 |
"Could not base64 encode hashed value for password compare");
|
|
|
7c7f29 |
diff --git a/ldap/servers/plugins/pwdstorage/ns-mta-md5_pwd.c b/ldap/servers/plugins/pwdstorage/ns-mta-md5_pwd.c
|
|
|
7c7f29 |
index 2fed61f..ae1f7b8 100644
|
|
|
7c7f29 |
--- a/ldap/servers/plugins/pwdstorage/ns-mta-md5_pwd.c
|
|
|
7c7f29 |
+++ b/ldap/servers/plugins/pwdstorage/ns-mta-md5_pwd.c
|
|
|
7c7f29 |
@@ -84,6 +84,7 @@ ns_mta_md5_pw_cmp(const char * clear, const char *mangled)
|
|
|
7c7f29 |
|
|
|
7c7f29 |
mta_hash[32] = mta_salt[32] = 0;
|
|
|
7c7f29 |
|
|
|
7c7f29 |
+ /* This is salted, so we don't need to change it for constant time */
|
|
|
7c7f29 |
return( strcmp(mta_hash,ns_mta_hash_alg(buffer,mta_salt,clear)));
|
|
|
7c7f29 |
}
|
|
|
7c7f29 |
|
|
|
7c7f29 |
diff --git a/ldap/servers/plugins/pwdstorage/sha_pwd.c b/ldap/servers/plugins/pwdstorage/sha_pwd.c
|
|
|
7c7f29 |
index 9594ac9..2e4973b 100644
|
|
|
7c7f29 |
--- a/ldap/servers/plugins/pwdstorage/sha_pwd.c
|
|
|
7c7f29 |
+++ b/ldap/servers/plugins/pwdstorage/sha_pwd.c
|
|
|
7c7f29 |
@@ -120,13 +120,16 @@ sha_pw_cmp (const char *userpwd, const char *dbpwd, unsigned int shaLen )
|
|
|
7c7f29 |
}
|
|
|
7c7f29 |
|
|
|
7c7f29 |
/* the proof is in the comparison... */
|
|
|
7c7f29 |
- result = ( hash_len >= shaLen ) ?
|
|
|
7c7f29 |
- ( memcmp( userhash, dbhash, shaLen ) ) : /* include salt */
|
|
|
7c7f29 |
- ( memcmp( userhash, dbhash + OLD_SALT_LENGTH,
|
|
|
7c7f29 |
- hash_len - OLD_SALT_LENGTH ) ); /* exclude salt */
|
|
|
7c7f29 |
+ if ( hash_len >= shaLen ) {
|
|
|
7c7f29 |
+ result = slapi_ct_memcmp( userhash, dbhash, shaLen );
|
|
|
7c7f29 |
+ } else {
|
|
|
7c7f29 |
+ result = slapi_ct_memcmp( userhash, dbhash + OLD_SALT_LENGTH, hash_len - OLD_SALT_LENGTH );
|
|
|
7c7f29 |
+ }
|
|
|
7c7f29 |
|
|
|
7c7f29 |
- loser:
|
|
|
7c7f29 |
- if ( dbhash && dbhash != quick_dbhash ) slapi_ch_free_string( &dbhash );
|
|
|
7c7f29 |
+loser:
|
|
|
7c7f29 |
+ if ( dbhash && dbhash != quick_dbhash ) {
|
|
|
7c7f29 |
+ slapi_ch_free_string( &dbhash );
|
|
|
7c7f29 |
+ }
|
|
|
7c7f29 |
return result;
|
|
|
7c7f29 |
}
|
|
|
7c7f29 |
|
|
|
7c7f29 |
diff --git a/ldap/servers/plugins/pwdstorage/smd5_pwd.c b/ldap/servers/plugins/pwdstorage/smd5_pwd.c
|
|
|
7c7f29 |
index f4c92f1..79c2846 100644
|
|
|
7c7f29 |
--- a/ldap/servers/plugins/pwdstorage/smd5_pwd.c
|
|
|
7c7f29 |
+++ b/ldap/servers/plugins/pwdstorage/smd5_pwd.c
|
|
|
7c7f29 |
@@ -80,7 +80,7 @@ smd5_pw_cmp( const char *userpwd, const char *dbpwd )
|
|
|
7c7f29 |
PK11_DestroyContext(ctx, 1);
|
|
|
7c7f29 |
|
|
|
7c7f29 |
/* Compare everything up to the salt. */
|
|
|
7c7f29 |
- rc = memcmp( userhash, dbhash, MD5_LENGTH );
|
|
|
7c7f29 |
+ rc = slapi_ct_memcmp( userhash, dbhash, MD5_LENGTH );
|
|
|
7c7f29 |
|
|
|
7c7f29 |
loser:
|
|
|
7c7f29 |
if ( dbhash && dbhash != quick_dbhash ) slapi_ch_free_string( (char **)&dbhash );
|
|
|
7c7f29 |
diff --git a/ldap/servers/slapd/ch_malloc.c b/ldap/servers/slapd/ch_malloc.c
|
|
|
7c7f29 |
index 10870df..a38268c 100644
|
|
|
7c7f29 |
--- a/ldap/servers/slapd/ch_malloc.c
|
|
|
7c7f29 |
+++ b/ldap/servers/slapd/ch_malloc.c
|
|
|
7c7f29 |
@@ -365,3 +365,25 @@ slapi_ch_smprintf(const char *fmt, ...)
|
|
|
7c7f29 |
return p;
|
|
|
7c7f29 |
}
|
|
|
7c7f29 |
#endif
|
|
|
7c7f29 |
+
|
|
|
7c7f29 |
+/* Constant time memcmp. Does not shortcircuit on failure! */
|
|
|
7c7f29 |
+/* This relies on p1 and p2 both being size at least n! */
|
|
|
7c7f29 |
+int
|
|
|
7c7f29 |
+slapi_ct_memcmp( const void *p1, const void *p2, size_t n)
|
|
|
7c7f29 |
+{
|
|
|
7c7f29 |
+ int result = 0;
|
|
|
7c7f29 |
+ const unsigned char *_p1 = (const unsigned char *)p1;
|
|
|
7c7f29 |
+ const unsigned char *_p2 = (const unsigned char *)p2;
|
|
|
7c7f29 |
+
|
|
|
7c7f29 |
+ if (_p1 == NULL || _p2 == NULL) {
|
|
|
7c7f29 |
+ return 2;
|
|
|
7c7f29 |
+ }
|
|
|
7c7f29 |
+
|
|
|
7c7f29 |
+ for (size_t i = 0; i < n; i++) {
|
|
|
7c7f29 |
+ if (_p1[i] ^ _p2[i]) {
|
|
|
7c7f29 |
+ result = 1;
|
|
|
7c7f29 |
+ }
|
|
|
7c7f29 |
+ }
|
|
|
7c7f29 |
+ return result;
|
|
|
7c7f29 |
+}
|
|
|
7c7f29 |
+
|
|
|
7c7f29 |
diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h
|
|
|
7c7f29 |
index a7e544a..165fb05 100644
|
|
|
7c7f29 |
--- a/ldap/servers/slapd/slapi-plugin.h
|
|
|
7c7f29 |
+++ b/ldap/servers/slapd/slapi-plugin.h
|
|
|
7c7f29 |
@@ -5825,6 +5825,22 @@ char * slapi_ch_smprintf(const char *fmt, ...)
|
|
|
7c7f29 |
#else
|
|
|
7c7f29 |
;
|
|
|
7c7f29 |
#endif
|
|
|
7c7f29 |
+/**
|
|
|
7c7f29 |
+ * slapi_ct_memcmp is a constant time memory comparison function. This is for
|
|
|
7c7f29 |
+ * use with password hashes and other locations which could lead to a timing
|
|
|
7c7f29 |
+ * attack due to early shortcut returns. This function *does not* shortcircuit
|
|
|
7c7f29 |
+ * during the comparison, always checking every byte regardless if it has already
|
|
|
7c7f29 |
+ * found that the memory does not match.
|
|
|
7c7f29 |
+ *
|
|
|
7c7f29 |
+ * WARNING! p1 and p2 must both reference content that is at least of size 'n'.
|
|
|
7c7f29 |
+ * Else this function may over-run (And will certainly fail).
|
|
|
7c7f29 |
+ *
|
|
|
7c7f29 |
+ * \param p1 pointer to first value to check.
|
|
|
7c7f29 |
+ * \param p2 pointer to second value to check.
|
|
|
7c7f29 |
+ * \param n length in bytes of the content of p1 AND p2.
|
|
|
7c7f29 |
+ * \return 0 on match. 1 on non-match. 2 on presence of NULL pointer in p1 or p2.
|
|
|
7c7f29 |
+ */
|
|
|
7c7f29 |
+int slapi_ct_memcmp( const void *p1, const void *p2, size_t n);
|
|
|
7c7f29 |
|
|
|
7c7f29 |
/*
|
|
|
7c7f29 |
* syntax plugin routines
|
|
|
7c7f29 |
--
|
|
|
7c7f29 |
2.4.11
|
|
|
7c7f29 |
|