diff options
Diffstat (limited to '')
-rw-r--r-- | servers/slapd/pwmods/Makefile.in | 59 | ||||
-rw-r--r-- | servers/slapd/pwmods/README.argon2 | 109 | ||||
-rw-r--r-- | servers/slapd/pwmods/argon2.c | 240 |
3 files changed, 408 insertions, 0 deletions
diff --git a/servers/slapd/pwmods/Makefile.in b/servers/slapd/pwmods/Makefile.in new file mode 100644 index 0000000..e8d76b8 --- /dev/null +++ b/servers/slapd/pwmods/Makefile.in @@ -0,0 +1,59 @@ +# Makefile.in for overlays +# $OpenLDAP$ +## This work is part of OpenLDAP Software <http://www.openldap.org/>. +## +## Copyright 2003-2022 The OpenLDAP Foundation. +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted only as authorized by the OpenLDAP +## Public License. +## +## A copy of this license is available in the file LICENSE in the +## top-level directory of the distribution or, alternatively, at +## <http://www.OpenLDAP.org/license.html>. + +SRCS = argon2.c + +LTONLY_MOD = $(LTONLY_mod) +LDAP_INCDIR= ../../../include +LDAP_LIBDIR= ../../../libraries + +MOD_DEFS = -DSLAPD_IMPORT + +shared_LDAP_LIBS = $(LDAP_LIBLDAP_LA) $(LDAP_LIBLBER_LA) +NT_LINK_LIBS = -L.. -lslapd $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS) +UNIX_LINK_LIBS = $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS) + +LIBRARY = dummyvalue +PROGRAMS = @SLAPD_DYNAMIC_PWMODS@ + +XINCPATH = -I.. -I$(srcdir)/.. +XDEFS = $(MODULES_CPPFLAGS) + +dynamic: $(PROGRAMS) + +argon2.la : argon2.lo version.lo + $(LTLINK_MOD) -module -o $@ argon2.lo version.lo $(ARGON2_LIBS) $(LINK_LIBS) $(MODULES_LIBS) + +install-local: $(PROGRAMS) + @if test -n "$?" ; then \ + $(MKDIR) $(DESTDIR)$(moduledir); \ + $(LTINSTALL) $(INSTALLFLAGS) -m 755 $? $(DESTDIR)$(moduledir);\ + fi + +MKDEPFLAG = -l + +.SUFFIXES: .c .o .lo + +.c.lo: + $(LTCOMPILE_MOD) $< + +# Must fixup depends for non-libtool objects +depend-local: depend-common + @if test -n "$(OBJS)"; then \ + OBJ2=`echo $(OBJS) $(OBJDEP) | $(SED) -e 's/\.o//g'`; \ + SCR=''; for i in $$OBJ2; do SCR="$$SCR -e s/^$$i.lo:/$$i.o:/"; done; \ + mv Makefile Makefile.bak; $(SED) $$SCR Makefile.bak > Makefile && \ + $(RM) Makefile.bak; fi + diff --git a/servers/slapd/pwmods/README.argon2 b/servers/slapd/pwmods/README.argon2 new file mode 100644 index 0000000..595c50d --- /dev/null +++ b/servers/slapd/pwmods/README.argon2 @@ -0,0 +1,109 @@ +Argon2 OpenLDAP support +---------------------- + +argon2.c provides support for ARGON2 hashed passwords in OpenLDAP. For +instance, one could have the LDAP attribute: + +userPassword: {ARGON2}$argon2i$v=19$m=4096,t=3,p=1$c2FsdHNhbHQ$DKlexoEJUoZTmkAAC3SaMWk30El9/RvVhlqGo6afIng + +or: + +userPassword: {ARGON2}$argon2i$v=19$m=4096,t=3,p=1$c2FsdHNhbHRzYWx0$qOCkx9nMeFlaGOO4DUmPDgrlUbgMMuO9T1+vQCFuyzw + +Both hash the password "secret", the first using the salt "saltsalt", the second using the salt "saltsaltsalt" + +Building +-------- + +1) Customize the OPENLDAP variable in Makefile to point to the OpenLDAP +source root. + +For initial testing you might also want to edit DEFS to define +SLAPD_ARGON2_DEBUG, which enables logging to stderr (don't leave this on +in production, as it prints passwords in cleartext). + +2) Run 'make' to produce argon2.so + +3) Copy argon2.so somewhere permanent. + +4) Edit your slapd.conf (eg. /etc/ldap/slapd.conf), and add: + +moduleload ...path/to/argon2.so + +5) Restart slapd. + + +Configuring +----------- + +The {ARGON2} password scheme should now be recognised. + +You can also tell OpenLDAP to use one of this scheme when processing LDAP +Password Modify Extended Operations, thanks to the password-hash option in +slapd.conf: + +password-hash {ARGON2} + + +Testing +------- + +A quick way to test whether it's working is to customize the rootdn and +rootpw in slapd.conf, eg: + +rootdn "cn=admin,dc=example,dc=com" + +# This hashes the string 'secret', with a random salt +rootpw {ARGON2}$argon2i$v=19$m=4096,t=3,p=1$uJyf0UfB25SQTfX7oCyK2w$U45DJqEFwD0yFaLvTVyACHLvGMwzNGf19dvzPR8XvGc + + +Then to test, run something like: + +ldapsearch -b "dc=example,dc=com" -D "cn=admin,dc=example,dc=com" -x -w secret + + +-- Test hashes: + +Test hashes can be generated with argon2: +$ echo -n "secret" | argon2 "saltsalt" -e +$argon2i$v=19$m=4096,t=3,p=1$c2FsdHNhbHQ$DKlexoEJUoZTmkAAC3SaMWk30El9/RvVhlqGo6afIng + +$ echo -n "secret" | argon2 "saltsaltsalt" -e +$argon2i$v=19$m=4096,t=3,p=1$c2FsdHNhbHRzYWx0$qOCkx9nMeFlaGOO4DUmPDgrlUbgMMuO9T1+vQCFuyzw + +$ echo -n "secretsecret" | argon2 "saltsalt" -e +$argon2i$v=19$m=4096,t=3,p=1$c2FsdHNhbHQ$U0Pd/wEsssZ9bHezDA8oxHnWe01xftykEy+7ehM2vic + +$ echo -n "secretsecret" | argon2 "saltsaltsalt" -e +$argon2i$v=19$m=4096,t=3,p=1$c2FsdHNhbHRzYWx0$fkvoOwKgVtlX9ZDqcHFyyArBvqnAM0Igca8SScB4Jsc + + + +Alternatively we could modify an existing user's password with +ldappasswd, and then test binding as that user: + +$ ldappasswd -D "cn=admin,dc=example,dc=com" -x -W -S uid=jturner,ou=People,dc=example,dc=com +New password: secret +Re-enter new password: secret +Enter LDAP Password: <cn=admin's password> + +$ ldapsearch -b "dc=example,dc=com" -D "uid=jturner,ou=People,dc=example,dc=com" -x -w secret + + + +--- + +This work is part of OpenLDAP Software <http://www.openldap.org/>. + +Copyright 2017-2022 The OpenLDAP Foundation. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted only as authorized by the OpenLDAP +Public License. + +A copy of this license is available in the file LICENSE in the +top-level directory of the distribution or, alternatively, at +<http://www.OpenLDAP.org/license.html>. + +--- diff --git a/servers/slapd/pwmods/argon2.c b/servers/slapd/pwmods/argon2.c new file mode 100644 index 0000000..f3f7b4c --- /dev/null +++ b/servers/slapd/pwmods/argon2.c @@ -0,0 +1,240 @@ +/* argon2.c - Password module for argon2 */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2017-2022 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" +#ifdef SLAPD_PWMOD_PW_ARGON2 +#include "ac/string.h" +#include "lber_pvt.h" +#include "lutil.h" + +#include "slap.h" + +#include <stdint.h> +#include <stdlib.h> + +#ifdef HAVE_LIBARGON2 +#include <argon2.h> + +/* + * For now, we hardcode the default values from the argon2 command line tool + * (as of argon2 release 20161029) + */ +#define SLAPD_ARGON2_ITERATIONS 3 +#define SLAPD_ARGON2_MEMORY (1 << 12) +#define SLAPD_ARGON2_PARALLELISM 1 +#define SLAPD_ARGON2_SALT_LENGTH 16 +#define SLAPD_ARGON2_HASH_LENGTH 32 + +#else /* !HAVE_LIBARGON2 */ +#include <sodium.h> + +/* + * Or libsodium interactive settings + */ +#define SLAPD_ARGON2_ITERATIONS crypto_pwhash_argon2id_OPSLIMIT_INTERACTIVE +#define SLAPD_ARGON2_MEMORY (crypto_pwhash_argon2id_MEMLIMIT_INTERACTIVE / 1024) +#define SLAPD_ARGON2_PARALLELISM 1 +#define SLAPD_ARGON2_SALT_LENGTH crypto_pwhash_argon2id_SALTBYTES +#define SLAPD_ARGON2_HASH_LENGTH 32 + +#endif + +static unsigned long iterations = SLAPD_ARGON2_ITERATIONS; +static unsigned long memory = SLAPD_ARGON2_MEMORY; +static unsigned long parallelism = SLAPD_ARGON2_PARALLELISM; + +const struct berval slapd_argon2_scheme = BER_BVC("{ARGON2}"); + +static int +slapd_argon2_hash( + const struct berval *scheme, + const struct berval *passwd, + struct berval *hash, + const char **text ) +{ + + /* + * Duplicate these values here so future code which allows + * configuration has an easier time. + */ + uint32_t salt_length, hash_length; + char *p; + int rc = LUTIL_PASSWD_ERR; + +#ifdef HAVE_LIBARGON2 + struct berval salt; + size_t encoded_length; + + salt_length = SLAPD_ARGON2_SALT_LENGTH; + hash_length = SLAPD_ARGON2_HASH_LENGTH; + + encoded_length = argon2_encodedlen( iterations, memory, parallelism, + salt_length, hash_length, Argon2_id ); + + salt.bv_len = salt_length; + salt.bv_val = ber_memalloc( salt.bv_len ); + + if ( salt.bv_val == NULL ) { + return LUTIL_PASSWD_ERR; + } + + if ( lutil_entropy( (unsigned char*)salt.bv_val, salt.bv_len ) ) { + ber_memfree( salt.bv_val ); + return LUTIL_PASSWD_ERR; + } + + p = hash->bv_val = ber_memalloc( scheme->bv_len + encoded_length ); + if ( p == NULL ) { + ber_memfree( salt.bv_val ); + return LUTIL_PASSWD_ERR; + } + + AC_MEMCPY( p, scheme->bv_val, scheme->bv_len ); + p += scheme->bv_len; + + /* + * Do the actual heavy lifting + */ + if ( argon2i_hash_encoded( iterations, memory, parallelism, + passwd->bv_val, passwd->bv_len, + salt.bv_val, salt_length, hash_length, + p, encoded_length ) == 0 ) { + rc = LUTIL_PASSWD_OK; + } + hash->bv_len = scheme->bv_len + encoded_length; + ber_memfree( salt.bv_val ); + +#else /* !HAVE_LIBARGON2 */ + /* Not exposed by libsodium + salt_length = SLAPD_ARGON2_SALT_LENGTH; + hash_length = SLAPD_ARGON2_HASH_LENGTH; + */ + + p = hash->bv_val = ber_memalloc( scheme->bv_len + crypto_pwhash_STRBYTES ); + if ( p == NULL ) { + return LUTIL_PASSWD_ERR; + } + + AC_MEMCPY( hash->bv_val, scheme->bv_val, scheme->bv_len ); + p += scheme->bv_len; + + if ( crypto_pwhash_str_alg( p, passwd->bv_val, passwd->bv_len, + iterations, memory * 1024, + crypto_pwhash_ALG_ARGON2ID13 ) == 0 ) { + hash->bv_len = strlen( hash->bv_val ); + rc = LUTIL_PASSWD_OK; + } +#endif + + if ( rc ) { + ber_memfree( hash->bv_val ); + return LUTIL_PASSWD_ERR; + } + + return LUTIL_PASSWD_OK; +} + +static int +slapd_argon2_verify( + const struct berval *scheme, + const struct berval *passwd, + const struct berval *cred, + const char **text ) +{ + int rc = LUTIL_PASSWD_ERR; + +#ifdef HAVE_LIBARGON2 + if ( strncmp( passwd->bv_val, "$argon2i$", STRLENOF("$argon2i$") ) == 0 ) { + rc = argon2i_verify( passwd->bv_val, cred->bv_val, cred->bv_len ); + } else if ( strncmp( passwd->bv_val, "$argon2d$", STRLENOF("$argon2d$") ) == 0 ) { + rc = argon2d_verify( passwd->bv_val, cred->bv_val, cred->bv_len ); + } else if ( strncmp( passwd->bv_val, "$argon2id$", STRLENOF("$argon2id$") ) == 0 ) { + rc = argon2id_verify( passwd->bv_val, cred->bv_val, cred->bv_len ); + } +#else /* !HAVE_LIBARGON2 */ + rc = crypto_pwhash_str_verify( passwd->bv_val, cred->bv_val, cred->bv_len ); +#endif + + if ( rc ) { + return LUTIL_PASSWD_ERR; + } + return LUTIL_PASSWD_OK; +} + +int init_module( int argc, char *argv[] ) +{ + int i; + +#ifdef HAVE_LIBSODIUM + if ( sodium_init() == -1 ) { + return -1; + } +#endif + + for ( i=0; i < argc; i++ ) { + char *p; + unsigned long value; + + switch ( *argv[i] ) { + case 'm': + p = strchr( argv[i], '=' ); + if ( !p || lutil_atoulx( &value, p+1, 0 ) ) { + return -1; + } + memory = value; + break; + + case 't': + p = strchr( argv[i], '=' ); + if ( !p || lutil_atoulx( &value, p+1, 0 ) ) { + return -1; + } + iterations = value; + break; + + case 'p': + p = strchr( argv[i], '=' ); + if ( !p || lutil_atoulx( &value, p+1, 0 ) ) { + return -1; + } + parallelism = value; + break; + + default: + return -1; + } + } + +#ifndef HAVE_LIBARGON2 + /* At the moment, we can only use libargon2 to set parallelism for new + * hashes */ + if ( parallelism != SLAPD_ARGON2_PARALLELISM ) { + Debug( LDAP_DEBUG_ANY, "pw-argon2: " + "non-default parallelism only supported when linked with " + "libargon2, got p=%lu\n", + parallelism ); + + if ( (slapMode & SLAP_MODE) != SLAP_TOOL_MODE || + slapTool == SLAPPASSWD || slapTool == SLAPTEST ) { + return 1; + } + } +#endif + + return lutil_passwd_add( (struct berval *)&slapd_argon2_scheme, + slapd_argon2_verify, slapd_argon2_hash ); +} +#endif /* SLAPD_OVER_PW_ARGON2 */ |