Skip to content
Snippets Groups Projects
Commit 2210a3bd authored by Simon Levermann's avatar Simon Levermann Committed by Quanah Gibson-Mount
Browse files

ITS#8575 Implement argon2 password hashing as a module

This change implements argon2, which won the Password Hashing
Competition (https://password-hashing.net/) as a contrib-module in order
to provide a modern password hashing alternative in openldap. The
currently available password hashing algorithms are relatively old, and
modern hardware, especially GPUs can compute quite a few (ranging from
tens of thousands to millions) of hashes per second. Argon2 was designed
to withstand such attacks.

This implementation uses the default work factors used in the argon2
command line client, but the resulting hashes are stored in a way that
would allow retroactive changes to these values, or even exposing them
as configuration in the module.
parent 0daf8012
No related branches found
No related tags found
No related merge requests found
# $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
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: <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 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>.
---
/* pw-argon2.c - Password module for argon2 */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* 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
* <http://www.OpenLDAP.org/license.html>.
*/
#define _GNU_SOURCE
#include "portable.h"
#include "ac/string.h"
#include "lber_pvt.h"
#include "lutil.h"
#include <argon2.h>
#include <stdint.h>
#include <stdlib.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 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);
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment