diff --git a/contrib/slapd-modules/passwd/argon2/Makefile b/contrib/slapd-modules/passwd/argon2/Makefile new file mode 100644 index 0000000000..3e7aa2dd35 --- /dev/null +++ b/contrib/slapd-modules/passwd/argon2/Makefile @@ -0,0 +1,48 @@ +# $OpenLDAP$ + +LDAP_SRC = ../../../.. +LDAP_BUILD = ../../../.. +LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd +LDAP_LIB = $(LDAP_BUILD)/libraries/libldap_r/libldap_r.la \ + $(LDAP_BUILD)/libraries/liblber/liblber.la + +LIBTOOL = $(LDAP_BUILD)/libtool +CC = gcc +OPT = -g -O2 -Wall +#DEFS = -DSLAPD_ARGON2_DEBUG + +INCS = $(LDAP_INC) +LIBS = $(LDAP_LIB) -largon2 + +PROGRAMS = pw-argon2.la +LTVER = 0:0:0 + +#prefix=/usr/local +prefix=`grep -e "^prefix =" $(LDAP_BUILD)/Makefile | cut -d= -f2` + +exec_prefix=$(prefix) +ldap_subdir=/openldap + +libdir=$(exec_prefix)/lib +libexecdir=$(exec_prefix)/libexec +moduledir = $(libexecdir)$(ldap_subdir) + +.SUFFIXES: .c .o .lo + +.c.lo: + $(LIBTOOL) --mode=compile $(CC) $(OPT) $(DEFS) $(INCS) -c $< + +all: $(PROGRAMS) + +pw-argon2.la: pw-argon2.lo + $(LIBTOOL) --mode=link $(CC) $(OPT) -version-info $(LTVER) \ + -rpath $(moduledir) -module -o $@ $? $(LIBS) + +clean: + rm -rf *.o *.lo *.la .libs + +install: $(PROGRAMS) + mkdir -p $(DESTDIR)$(moduledir) + for p in $(PROGRAMS) ; do \ + $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \ + done diff --git a/contrib/slapd-modules/passwd/argon2/README b/contrib/slapd-modules/passwd/argon2/README new file mode 100644 index 0000000000..656df8f722 --- /dev/null +++ b/contrib/slapd-modules/passwd/argon2/README @@ -0,0 +1,109 @@ +Argon2 OpenLDAP support +---------------------- + +slapd-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 slapd-argon2.so + +3) Copy slapd-argon2.so somewhere permanent. + +4) Edit your slapd.conf (eg. /etc/ldap/slapd.conf), and add: + +moduleload ...path/to/slapd-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: + +$ 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 . + +Copyright 2017 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 +. + +--- diff --git a/contrib/slapd-modules/passwd/argon2/pw-argon2.c b/contrib/slapd-modules/passwd/argon2/pw-argon2.c new file mode 100644 index 0000000000..df0f715c1f --- /dev/null +++ b/contrib/slapd-modules/passwd/argon2/pw-argon2.c @@ -0,0 +1,117 @@ +/* pw-argon2.c - Password module for argon2 */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * Copyright 2017 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 + * . + */ + +#define _GNU_SOURCE + +#include "portable.h" +#include "ac/string.h" +#include "lber_pvt.h" +#include "lutil.h" + +#include +#include +#include + +/* + * 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 12 +#define SLAPD_ARGON2_PARALLELISM 1 +#define SLAPD_ARGON2_SALT_LENGTH 16 +#define SLAPD_ARGON2_HASH_LENGTH 32 + +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 iterations = SLAPD_ARGON2_ITERATIONS; + uint32_t memory = (1 << SLAPD_ARGON2_MEMORY); + uint32_t parallelism = SLAPD_ARGON2_PARALLELISM; + uint32_t salt_length = SLAPD_ARGON2_SALT_LENGTH; + uint32_t hash_length = SLAPD_ARGON2_HASH_LENGTH; + + size_t encoded_length = argon2_encodedlen(iterations, memory, parallelism, + salt_length, hash_length, Argon2_i); + + /* + * Gather random bytes for our salt + */ + struct berval salt; + salt.bv_len = salt_length; + salt.bv_val = ber_memalloc(salt.bv_len); + + int rc = lutil_entropy((unsigned char*)salt.bv_val, salt.bv_len); + + if(rc) { + ber_memfree(salt.bv_val); + return LUTIL_PASSWD_ERR; + } + + struct berval encoded; + encoded.bv_len = encoded_length; + encoded.bv_val = ber_memalloc(encoded.bv_len); + /* + * Do the actual heavy lifting + */ + rc = argon2i_hash_encoded(iterations, memory, parallelism, + passwd->bv_val, passwd->bv_len, salt.bv_val, salt_length, hash_length, + encoded.bv_val, encoded_length); + ber_memfree(salt.bv_val); + + if(rc) { + ber_memfree(encoded.bv_val); + return LUTIL_PASSWD_ERR; + } + + hash->bv_len = scheme->bv_len + encoded_length; + hash->bv_val = ber_memalloc(hash->bv_len); + + AC_MEMCPY(hash->bv_val, scheme->bv_val, scheme->bv_len); + AC_MEMCPY(hash->bv_val + scheme->bv_len, encoded.bv_val, encoded.bv_len); + + ber_memfree(encoded.bv_val); + + 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 = argon2i_verify(passwd->bv_val, cred->bv_val, cred->bv_len); + + if (rc) { + return LUTIL_PASSWD_ERR; + } + return LUTIL_PASSWD_OK; +} + +int init_module(int argc, char *argv[]) { + return lutil_passwd_add((struct berval *)&slapd_argon2_scheme, + slapd_argon2_verify, slapd_argon2_hash); +}